diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 28072e75..07edb5e2 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,7 +2,7 @@ blank_issues_enabled: false contact_links: - name: Telegram url: https://t.me/customskinloader - about: Gugugu - - name: QQ + about: Community chat for CustomSkinLoader users. + - name: QQ Group url: https://jq.qq.com/?_wv=1027&k=vF16R5tg - about: Gugugu + about: Chinese community chat for CustomSkinLoader users. diff --git a/.github/scripts/build.ps1 b/.github/scripts/build.ps1 new file mode 100644 index 00000000..c5483c37 --- /dev/null +++ b/.github/scripts/build.ps1 @@ -0,0 +1,15 @@ +param( + [string] $GradleCommand = "./gradlew.bat" +) + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path +$RepositoryRoot = (Resolve-Path -LiteralPath (Join-Path $ScriptRoot "../..")).Path +Set-Location -LiteralPath $RepositoryRoot + +& $GradleCommand clean build --stacktrace +if ($LASTEXITCODE -ne 0) { + throw "Gradle build failed with exit code $LASTEXITCODE." +} diff --git a/.github/scripts/install-publish-clis.ps1 b/.github/scripts/install-publish-clis.ps1 new file mode 100644 index 00000000..f3e4edf6 --- /dev/null +++ b/.github/scripts/install-publish-clis.ps1 @@ -0,0 +1,51 @@ +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +$RunnerTemp = if ([string]::IsNullOrWhiteSpace($env:RUNNER_TEMP)) { + [System.IO.Path]::GetTempPath() +} else { + $env:RUNNER_TEMP +} + +function Add-ActionPath { + param([Parameter(Mandatory = $true)][string] $Path) + + $env:Path = "$Path;$env:Path" + if (-not [string]::IsNullOrWhiteSpace($env:GITHUB_PATH)) { + Add-Content -LiteralPath $env:GITHUB_PATH -Value $Path + } +} + +if (-not (Get-Command aws -ErrorAction SilentlyContinue)) { + $installer = Join-Path $RunnerTemp "AWSCLIV2.msi" + Invoke-WebRequest -Uri "https://awscli.amazonaws.com/AWSCLIV2.msi" -OutFile $installer + $install = Start-Process msiexec.exe -Wait -PassThru -ArgumentList "/i `"$installer`" /qn" + if ($install.ExitCode -ne 0) { + throw "AWS CLI installer failed with exit code $($install.ExitCode)." + } + + $awsCliDir = Join-Path $env:ProgramFiles "Amazon/AWSCLIV2" + $machinePath = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + $userPath = [System.Environment]::GetEnvironmentVariable("Path", "User") + $env:Path = "$awsCliDir;$machinePath;$userPath;$env:Path" + if (-not [string]::IsNullOrWhiteSpace($env:GITHUB_PATH)) { + Add-Content -LiteralPath $env:GITHUB_PATH -Value $awsCliDir + } +} + +if (-not (Get-Command coscli -ErrorAction SilentlyContinue)) { + $coscliDir = Join-Path $RunnerTemp "coscli" + New-Item -ItemType Directory -Force -Path $coscliDir | Out-Null + Invoke-WebRequest -Uri "https://github.com/tencentyun/coscli/releases/download/v1.0.8/coscli-v1.0.8-windows-amd64.exe" -OutFile (Join-Path $coscliDir "coscli.exe") + Add-ActionPath $coscliDir +} + +if (-not (Get-Command tccli -ErrorAction SilentlyContinue)) { + python -m pip install --user tccli + if ($LASTEXITCODE -ne 0) { + throw "tccli install failed with exit code $LASTEXITCODE." + } + + $pythonUserScripts = Join-Path (python -m site --user-base) "Scripts" + Add-ActionPath $pythonUserScripts +} diff --git a/.github/scripts/prepare-build-artifacts.ps1 b/.github/scripts/prepare-build-artifacts.ps1 new file mode 100644 index 00000000..9a0081c1 --- /dev/null +++ b/.github/scripts/prepare-build-artifacts.ps1 @@ -0,0 +1,35 @@ +param( + [string] $SourcePath = "Bootstrap/build/libs", + [string] $DestinationPath = "build/publish" +) + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path +$RepositoryRoot = (Resolve-Path -LiteralPath (Join-Path $ScriptRoot "../..")).Path +Set-Location -LiteralPath $RepositoryRoot + +function Resolve-RepoPath { + param([Parameter(Mandatory = $true)][string] $Path) + + if ([System.IO.Path]::IsPathRooted($Path)) { + return [System.IO.Path]::GetFullPath($Path) + } + + return [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($RepositoryRoot, $Path)) +} + +$source = Resolve-RepoPath $SourcePath +$destination = Resolve-RepoPath $DestinationPath + +New-Item -ItemType Directory -Force -Path $destination | Out-Null + +$jars = Get-ChildItem -LiteralPath $source -Filter "CustomSkinLoader_Universal-*.jar" -File | + Where-Object { $_.Name -notlike "*-sources.jar" } + +if (-not $jars) { + throw "No Universal jar found under $SourcePath." +} + +$jars | Copy-Item -Destination $destination -Force diff --git a/.github/scripts/prepare-publish-artifacts.ps1 b/.github/scripts/prepare-publish-artifacts.ps1 new file mode 100644 index 00000000..3197a4c2 --- /dev/null +++ b/.github/scripts/prepare-publish-artifacts.ps1 @@ -0,0 +1,25 @@ +param( + [string] $Channel = $env:PUBLISH_CHANNEL, + [string] $LatestJsonName = $env:LATEST_JSON_NAME, + [string] $DetailJsonName = $env:DETAIL_JSON_NAME +) + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +if ([string]::IsNullOrWhiteSpace($Channel)) { + $Channel = "Beta" +} + +$publishScript = Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Path) "publish-artifacts.ps1" +$arguments = @("-Channel", $Channel) + +if (-not [string]::IsNullOrWhiteSpace($LatestJsonName)) { + $arguments += @("-LatestJsonName", $LatestJsonName) +} + +if (-not [string]::IsNullOrWhiteSpace($DetailJsonName)) { + $arguments += @("-DetailJsonName", $DetailJsonName) +} + +& $publishScript @arguments diff --git a/.github/scripts/publish-artifacts.ps1 b/.github/scripts/publish-artifacts.ps1 new file mode 100644 index 00000000..a6596ffe --- /dev/null +++ b/.github/scripts/publish-artifacts.ps1 @@ -0,0 +1,427 @@ +param( + [ValidateSet("Beta", "Release")] + [string] $Channel = "Beta", + + [string] $LatestJsonName, + + [string] $DetailJsonName, + + [string] $ArtifactsRoot = "build/publish", + + [switch] $SkipObjectStorage +) + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path +$RepositoryRoot = (Resolve-Path -LiteralPath (Join-Path $ScriptRoot "..\..")).Path +Set-Location -LiteralPath $RepositoryRoot + +function Resolve-RepoPath { + param([Parameter(Mandatory = $true)][string] $Path) + + if ([System.IO.Path]::IsPathRooted($Path)) { + return [System.IO.Path]::GetFullPath($Path) + } + + return [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($RepositoryRoot, $Path)) +} + +function ConvertTo-ShortVersion { + param([Parameter(Mandatory = $true)][string] $Version) + + return $Version.Replace("SNAPSHOT-", "s") +} + +function Read-BuildProperties { + $properties = [ordered] @{} + foreach ($line in Get-Content -LiteralPath (Resolve-RepoPath "build.properties")) { + $trimmedLine = $line.Trim() + if ([string]::IsNullOrWhiteSpace($trimmedLine) -or $trimmedLine.StartsWith("#")) { + continue + } + + $separatorIndex = $trimmedLine.IndexOf("=") + if ($separatorIndex -lt 1) { + continue + } + + $name = $trimmedLine.Substring(0, $separatorIndex).Trim() + $value = $trimmedLine.Substring($separatorIndex + 1).Trim() + $properties[$name] = $value + } + + return $properties +} + +function Get-RequiredProperty { + param( + [Parameter(Mandatory = $true)][System.Collections.IDictionary] $Properties, + [Parameter(Mandatory = $true)][string] $Name + ) + + $value = $Properties[$Name] + if ([string]::IsNullOrWhiteSpace($value)) { + throw "Missing $Name in build.properties" + } + + return $value +} + +function Get-PropertyList { + param( + [Parameter(Mandatory = $true)][System.Collections.IDictionary] $Properties, + [Parameter(Mandatory = $true)][string] $Name + ) + + return (Get-RequiredProperty $Properties $Name) -split "," | + ForEach-Object { $_.Trim() } | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } +} + +function Get-PropertyListByNames { + param( + [Parameter(Mandatory = $true)][System.Collections.IDictionary] $Properties, + [Parameter(Mandatory = $true)][string[]] $Names + ) + + foreach ($name in $Names) { + if (-not [string]::IsNullOrWhiteSpace($Properties[$name])) { + return Get-PropertyList $Properties $name + } + } + + throw "Missing one of these properties in build.properties: $($Names -join ', ')" +} + +function Test-VersionAtLeast { + param( + [Parameter(Mandatory = $true)][string] $Version, + [Parameter(Mandatory = $true)][string] $MinimumVersion + ) + + return [version] $Version -ge [version] $MinimumVersion +} + +function Get-VersionFromJarName { + param([Parameter(Mandatory = $true)][string] $FileName) + + $prefix = "CustomSkinLoader_Universal-" + $suffix = ".jar" + if (-not $FileName.StartsWith($prefix) -or -not $FileName.EndsWith($suffix)) { + throw "Unexpected Universal jar name: $FileName" + } + + return $FileName.Substring($prefix.Length, $FileName.Length - $prefix.Length - $suffix.Length) +} + +function New-Directory { + param([Parameter(Mandatory = $true)][string] $Path) + + [System.IO.Directory]::CreateDirectory((Resolve-RepoPath $Path)) | Out-Null +} + +function Copy-PublishArtifacts { + param([Parameter(Mandatory = $true)][string] $Destination) + + New-Directory $Destination + + $destinationPath = Resolve-RepoPath $Destination + Get-ChildItem -LiteralPath $destinationPath -File -ErrorAction SilentlyContinue | Remove-Item -Force + + $jar = Get-ChildItem -LiteralPath (Resolve-RepoPath "Bootstrap/build/libs") -Filter "CustomSkinLoader_Universal-*.jar" -File -ErrorAction Stop | + Where-Object { $_.Name -notlike "*-sources.jar" } | + Sort-Object LastWriteTime -Descending | + Select-Object -First 1 + + if (-not $jar) { + throw "No Universal jar found under Bootstrap/build/libs." + } + + Copy-Item -LiteralPath $jar.FullName -Destination (Join-Path $destinationPath $jar.Name) -Force + return Get-Item -LiteralPath (Join-Path $destinationPath $jar.Name) +} + +function ConvertTo-JsonFile { + param( + [Parameter(Mandatory = $true)] $InputObject, + [Parameter(Mandatory = $true)][string] $Path + ) + + $json = $InputObject | ConvertTo-Json -Depth 10 + $absolutePath = Resolve-RepoPath $Path + [System.IO.Directory]::CreateDirectory([System.IO.Path]::GetDirectoryName($absolutePath)) | Out-Null + [System.IO.File]::WriteAllText($absolutePath, $json + [System.Environment]::NewLine, [System.Text.UTF8Encoding]::new($false)) +} + +function New-Metadata { + param( + [Parameter(Mandatory = $true)][string] $Version, + [Parameter(Mandatory = $true)][string] $OutputDirectory, + [Parameter(Mandatory = $true)][string] $LatestName, + [Parameter(Mandatory = $true)][string] $DetailName, + [Parameter(Mandatory = $true)][System.Collections.IDictionary] $BuildProperties + ) + + $publicBaseUrl = if ([string]::IsNullOrWhiteSpace($env:CLOUDFLARE_CDN_ROOT)) { "https://csl.3-3.dev/" } else { $env:CLOUDFLARE_CDN_ROOT.Trim() } + if (-not $publicBaseUrl.EndsWith("/")) { + $publicBaseUrl += "/" + } + + $outputPath = Resolve-RepoPath $OutputDirectory + $jars = Get-ChildItem -LiteralPath $outputPath -Filter "CustomSkinLoader_Universal-*.jar" -File | + Sort-Object Name + + if (-not $jars) { + throw "No publish jars found in $OutputDirectory." + } + + $latest = [ordered] @{ + version = ConvertTo-ShortVersion $Version + downloads = [ordered] @{} + launchermeta = [ordered] @{} + } + + foreach ($jar in $jars) { + $latest.downloads["Universal"] = "$publicBaseUrl`mods/$($jar.Name)" + } + + $minecraftMajorVersions = Get-PropertyList $BuildProperties "minecraft_major_versions" + $detailMap = [ordered] @{} + foreach ($minecraftVersion in $minecraftMajorVersions) { + $loaderMap = [ordered] @{} + $loaderMap["Forge"] = $latest.downloads["Universal"] + + if (Test-VersionAtLeast $minecraftVersion "1.14") { + $loaderMap["Fabric"] = $latest.downloads["Universal"] + $loaderMap["Quilt"] = $latest.downloads["Universal"] + } + + if (Test-VersionAtLeast $minecraftVersion "1.20") { + $loaderMap["NeoForge"] = $latest.downloads["Universal"] + } + + $detailMap[$minecraftVersion] = $loaderMap + } + + $detail = [ordered] @{ + version = $latest.version + timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() + details = $detailMap + } + + ConvertTo-JsonFile $latest (Join-Path $outputPath $LatestName) + ConvertTo-JsonFile $detail (Join-Path $outputPath $DetailName) +} + +function Write-GitHubOutputValue { + param( + [Parameter(Mandatory = $true)][string] $Name, + [Parameter(Mandatory = $true)][string] $Value + ) + + if ([string]::IsNullOrWhiteSpace($env:GITHUB_OUTPUT)) { + return + } + + $delimiter = "EOF_$([guid]::NewGuid().ToString('N'))" + $content = "$Name<<$delimiter" + [System.Environment]::NewLine + + $Value + [System.Environment]::NewLine + + $delimiter + [System.Environment]::NewLine + [System.IO.File]::AppendAllText($env:GITHUB_OUTPUT, $content, [System.Text.UTF8Encoding]::new($false)) +} + +function Write-PublishOutputs { + param( + [Parameter(Mandatory = $true)][string] $Version, + [Parameter(Mandatory = $true)][System.Collections.IDictionary] $BuildProperties + ) + + $shortVersion = ConvertTo-ShortVersion $Version + $edition = Get-RequiredProperty $BuildProperties "edition" + Write-GitHubOutputValue "edition" $edition + Write-GitHubOutputValue "version" $shortVersion + Write-GitHubOutputValue "mc_publish_name" "CustomSkinLoader_$edition`_$shortVersion" + Write-GitHubOutputValue "mc_publish_version" "$shortVersion-$edition" + Write-GitHubOutputValue "loaders" ((Get-PropertyListByNames $BuildProperties @("loaders", "mod_loaders")) -join [System.Environment]::NewLine) + Write-GitHubOutputValue "game_versions" ((Get-PropertyListByNames $BuildProperties @("game-versions", "minecraft_major_versions")) -join [System.Environment]::NewLine) + Write-GitHubOutputValue "java_versions" ((Get-PropertyListByNames $BuildProperties @("java", "java_full_versions")) -join [System.Environment]::NewLine) +} + +function Invoke-AwsCliUpload { + param( + [Parameter(Mandatory = $true)][string] $EndpointUrl, + [Parameter(Mandatory = $true)][string] $Bucket, + [Parameter(Mandatory = $true)][string] $AccessKeyId, + [Parameter(Mandatory = $true)][string] $SecretAccessKey, + [Parameter(Mandatory = $true)][string] $Directory + ) + + $aws = Get-Command aws -ErrorAction SilentlyContinue + if (-not $aws) { + Write-Warning "AWS CLI was not found. Skip S3-compatible upload." + return + } + + $oldAccessKey = $env:AWS_ACCESS_KEY_ID + $oldSecretKey = $env:AWS_SECRET_ACCESS_KEY + $oldRegion = $env:AWS_DEFAULT_REGION + + try { + $env:AWS_ACCESS_KEY_ID = $AccessKeyId + $env:AWS_SECRET_ACCESS_KEY = $SecretAccessKey + $env:AWS_DEFAULT_REGION = "auto" + + foreach ($file in Get-ChildItem -LiteralPath $Directory -File) { + $key = if ($file.Extension -eq ".jar") { "mods/$($file.Name)" } else { $file.Name } + & aws --endpoint-url $EndpointUrl s3 cp $file.FullName "s3://$Bucket/$key" --only-show-errors + } + } finally { + $env:AWS_ACCESS_KEY_ID = $oldAccessKey + $env:AWS_SECRET_ACCESS_KEY = $oldSecretKey + $env:AWS_DEFAULT_REGION = $oldRegion + } +} + +function Invoke-CosCliUpload { + param( + [Parameter(Mandatory = $true)][string] $Bucket, + [Parameter(Mandatory = $true)][string] $SecretId, + [Parameter(Mandatory = $true)][string] $SecretKey, + [Parameter(Mandatory = $true)][string] $Directory + ) + + $coscli = Get-Command coscli -ErrorAction SilentlyContinue + if (-not $coscli) { + Write-Warning "coscli was not found. Skip Tencent COS upload." + return + } + + $configPath = Join-Path $env:RUNNER_TEMP "coscli-customskinloader.yaml" + $cosRegion = if ([string]::IsNullOrWhiteSpace($env:COS_REGION)) { "ap-shanghai" } else { $env:COS_REGION.Trim() } + @" +cos: + base: + secretid: $SecretId + secretkey: $SecretKey + sessiontoken: "" + protocol: https + buckets: + - name: $Bucket + alias: $Bucket + region: $cosRegion + endpoint: cos.$cosRegion.myqcloud.com + ofs: false +"@ | Set-Content -LiteralPath $configPath -Encoding UTF8 + + foreach ($file in Get-ChildItem -LiteralPath $Directory -File) { + $key = if ($file.Extension -eq ".jar") { "mods/$($file.Name)" } else { $file.Name } + & coscli -c $configPath cp $file.FullName "cos://$Bucket/$key" + } +} + +function Get-PublishObjectKeys { + param([Parameter(Mandatory = $true)][string] $Directory) + + return Get-ChildItem -LiteralPath $Directory -File | + ForEach-Object { + if ($_.Extension -eq ".jar") { + "mods/$($_.Name)" + } else { + $_.Name + } + } +} + +function Invoke-TencentCdnRefresh { + param([Parameter(Mandatory = $true)][string[]] $Keys) + + $cdnRoot = if ([string]::IsNullOrWhiteSpace($env:TENCENT_CDN_ROOT)) { "https://csl.littleservice.cn/" } else { $env:TENCENT_CDN_ROOT.Trim() } + if (-not $cdnRoot.EndsWith("/")) { + $cdnRoot += "/" + } + + if ([string]::IsNullOrWhiteSpace($env:COS_SECRET_ID) -or [string]::IsNullOrWhiteSpace($env:COS_SECRET_KEY)) { + Write-Host "Tencent CDN credentials are incomplete. Skip CDN refresh." + return + } + + $tccli = Get-Command tccli -ErrorAction SilentlyContinue + if (-not $tccli) { + Write-Warning "tccli was not found. Skip Tencent CDN refresh." + return + } + + $oldSecretId = $env:TENCENTCLOUD_SECRET_ID + $oldSecretKey = $env:TENCENTCLOUD_SECRET_KEY + try { + $env:TENCENTCLOUD_SECRET_ID = $env:COS_SECRET_ID + $env:TENCENTCLOUD_SECRET_KEY = $env:COS_SECRET_KEY + $urlsJson = ConvertTo-Json -Compress -InputObject @($Keys | ForEach-Object { "$cdnRoot$_" }) + + & tccli cdn PurgeUrlsCache --Urls $urlsJson + if ($LASTEXITCODE -ne 0) { + throw "Tencent CDN purge failed." + } + + & tccli cdn PushUrlsCache --Urls $urlsJson + if ($LASTEXITCODE -ne 0) { + throw "Tencent CDN push failed." + } + } finally { + $env:TENCENTCLOUD_SECRET_ID = $oldSecretId + $env:TENCENTCLOUD_SECRET_KEY = $oldSecretKey + } +} + +if ([string]::IsNullOrWhiteSpace($LatestJsonName)) { + $LatestJsonName = switch ($Channel) { + "Beta" { "latest-beta.json" } + "Canary" { "latest-canary.json" } + "Release" { "latest.json" } + } +} + +if ([string]::IsNullOrWhiteSpace($DetailJsonName)) { + $DetailJsonName = switch ($Channel) { + "Beta" { "detail-beta.json" } + "Canary" { "detail-canary.json" } + "Release" { "detail.json" } + } +} + +New-Directory $ArtifactsRoot +$buildProperties = Read-BuildProperties +$artifacts = Copy-PublishArtifacts $ArtifactsRoot +$ArtifactsRoot = Resolve-RepoPath $ArtifactsRoot +$fullVersion = Get-VersionFromJarName $artifacts[0].Name +New-Metadata -Version $fullVersion -OutputDirectory $ArtifactsRoot -LatestName $LatestJsonName -DetailName $DetailJsonName -BuildProperties $buildProperties +Write-PublishOutputs -Version $fullVersion -BuildProperties $buildProperties + +Write-Host "Prepared publish artifacts:" +Get-ChildItem -LiteralPath $ArtifactsRoot -File | ForEach-Object { Write-Host " - $($_.Name)" } + +if ($SkipObjectStorage) { + Write-Host "SkipObjectStorage was set. Metadata generated locally only." + exit 0 +} + +if (-not [string]::IsNullOrWhiteSpace($env:R2_BASE_URL) -and + -not [string]::IsNullOrWhiteSpace($env:R2_BUCKET) -and + -not [string]::IsNullOrWhiteSpace($env:R2_SECRET_ID) -and + -not [string]::IsNullOrWhiteSpace($env:R2_SECRET_KEY)) { + Invoke-AwsCliUpload -EndpointUrl $env:R2_BASE_URL -Bucket $env:R2_BUCKET -AccessKeyId $env:R2_SECRET_ID -SecretAccessKey $env:R2_SECRET_KEY -Directory $ArtifactsRoot +} else { + Write-Host "R2 secrets are incomplete. Skip R2 upload." +} + +if (-not [string]::IsNullOrWhiteSpace($env:COS_BUCKET) -and + -not [string]::IsNullOrWhiteSpace($env:COS_SECRET_ID) -and + -not [string]::IsNullOrWhiteSpace($env:COS_SECRET_KEY)) { + Invoke-CosCliUpload -Bucket $env:COS_BUCKET -SecretId $env:COS_SECRET_ID -SecretKey $env:COS_SECRET_KEY -Directory $ArtifactsRoot +} else { + Write-Host "COS secrets are incomplete. Skip COS upload." +} + +Invoke-TencentCdnRefresh -Keys @(Get-PublishObjectKeys $ArtifactsRoot) diff --git a/.github/scripts/write-prerelease-body.ps1 b/.github/scripts/write-prerelease-body.ps1 new file mode 100644 index 00000000..29dc0ce3 --- /dev/null +++ b/.github/scripts/write-prerelease-body.ps1 @@ -0,0 +1,37 @@ +param( + [string] $BranchName = $env:GITHUB_REF_NAME, + [string] $BuildType = $env:BUILD_TYPE, + [string] $OutputPath = "build/publish/release-notes.md", + [string] $TimeZoneId = "China Standard Time" +) + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path +$RepositoryRoot = (Resolve-Path -LiteralPath (Join-Path $ScriptRoot "../..")).Path +Set-Location -LiteralPath $RepositoryRoot + +if ([string]::IsNullOrWhiteSpace($BranchName)) { + throw "BranchName is required." +} + +if ([string]::IsNullOrWhiteSpace($BuildType)) { + throw "BuildType is required." +} + +$absoluteOutputPath = if ([System.IO.Path]::IsPathRooted($OutputPath)) { + [System.IO.Path]::GetFullPath($OutputPath) +} else { + [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($RepositoryRoot, $OutputPath)) +} + +[System.IO.Directory]::CreateDirectory([System.IO.Path]::GetDirectoryName($absoluteOutputPath)) | Out-Null + +$buildTime = [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([DateTime]::UtcNow, $TimeZoneId).ToString("yyyy.MM.dd HH:mm") +@" +Build Time / 构建时间: $buildTime + +Synchronize $BranchName branch code updates, keeping only the latest $BuildType build. +自动同步 $BranchName 分支构建产物,仅保留最新 $BuildType 版本。 +"@ | Set-Content -LiteralPath $absoluteOutputPath -Encoding UTF8 diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml index d53ffe10..c60a8083 100644 --- a/.github/workflows/beta.yml +++ b/.github/workflows/beta.yml @@ -1,8 +1,10 @@ name: Beta + on: push: branches: - - '14-develop' + - 15-develop + workflow_dispatch: jobs: build-beta: @@ -10,5 +12,10 @@ jobs: uses: ./.github/workflows/common.yml with: type: Beta - publish-task: uploadBeta - secrets: inherit \ No newline at end of file + publish: true + prerelease-tag: Beta-Build + prerelease-name: CustomSkinLoader Beta Build + latest-json: latest-beta.json + detail-json: detail-beta.json + is-snapshot: true + secrets: inherit diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml deleted file mode 100644 index f2d8e607..00000000 --- a/.github/workflows/canary.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: Canary -on: - push: - branches: - - '15-develop' - -jobs: - build-canary: - name: Build Canary - uses: ./.github/workflows/common.yml - with: - type: Canary - publish-task: uploadCanary - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 08cc3825..618b97eb 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -1,215 +1,149 @@ name: Common + on: workflow_call: inputs: type: required: true type: string - publish-task: + publish: + required: false + type: boolean + default: false + release: + required: false + type: boolean + default: false + prerelease-tag: + required: false + type: string + default: "" + prerelease-name: required: false type: string + default: "" + latest-json: + required: false + type: string + default: latest-beta.json + detail-json: + required: false + type: string + default: detail-beta.json is-snapshot: required: false type: boolean default: true +permissions: + contents: write + jobs: - build-common: + build: name: Build - runs-on: ubuntu-latest - environment: Build + runs-on: windows-latest + defaults: + run: + shell: powershell steps: - - - name: Checkout + - name: Checkout uses: actions/checkout@v4 - - - name: Set up JDK + + - name: Set up JDK 25 uses: actions/setup-java@v4 with: - distribution: 'temurin' - java-version: '8' - cache: 'gradle' - - - name: Build + distribution: liberica + java-version: "25" + cache: gradle + + - name: Build + env: + IS_SNAPSHOT: ${{ inputs['is-snapshot'] }} + run: ./.github/scripts/build.ps1 + + - name: Install publish CLIs + if: ${{ inputs.publish || inputs.release }} + run: ./.github/scripts/install-publish-clis.ps1 + + - name: Prepare publish artifacts + id: publish-artifacts + if: ${{ inputs.publish || inputs.release }} env: - KEY_PASS: ${{ secrets.KEY_PASS }} - IS_SNAPSHOT: ${{ inputs.is-snapshot }} - run: | - echo Snapshot: $IS_SNAPSHOT - export GIT_COMMIT_DESC=$(git log --format=%B -n 1 $GITHUB_SHA) - ./gradlew clean build --stacktrace - - + IS_SNAPSHOT: ${{ inputs['is-snapshot'] }} + PUBLISH_CHANNEL: ${{ inputs.type }} + LATEST_JSON_NAME: ${{ inputs['latest-json'] }} + DETAIL_JSON_NAME: ${{ inputs['detail-json'] }} + R2_BASE_URL: ${{ secrets.R2_BASE_URL }} + R2_BUCKET: ${{ secrets.R2_BUCKET }} + R2_SECRET_ID: ${{ secrets.R2_SECRET_ID }} + R2_SECRET_KEY: ${{ secrets.R2_SECRET_KEY }} + COS_BUCKET: ${{ secrets.COS_BUCKET }} + COS_REGION: ${{ secrets.COS_REGION }} + COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }} + COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }} + TENCENT_CDN_ROOT: ${{ secrets.TENCENT_CDN_ROOT }} + CLOUDFLARE_CDN_ROOT: ${{ secrets.CLOUDFLARE_CDN_ROOT }} + run: ./.github/scripts/prepare-publish-artifacts.ps1 + + - name: Prepare build artifacts + if: ${{ !(inputs.publish || inputs.release) }} + run: ./.github/scripts/prepare-build-artifacts.ps1 + + - name: Upload workflow artifact uses: actions/upload-artifact@v4 - name: Publish to Github Artifact with: name: CustomSkinLoader-${{ inputs.type }}-${{ github.run_number }} - path: build/libs - - Upload: - if: inputs.publish-task - needs: build-common - continue-on-error: true - environment: Build - strategy: - matrix: - website: [CurseForge_Modrinth] - modtype: [Fabric,ForgeV1,ForgeV2,ForgeV3] - include: - - - website: ObjectStorage - modtype: All -# - -# website: GitHub-Release -# modtype: All - - - website: GitHub-PreRelease - modtype: All - name: ${{ matrix.website }} (${{ matrix.modtype}}) - runs-on: ubuntu-latest - steps: - - - name: Checkout Git Repository - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 - name: Download Artifact Zips - with: - path: build - - - name: Preparation Folder - run: | - rsync -avz "build/$(ls build|grep "CustomSkinLoader")/" build/libs/ - ls -lh build - - - if: ${{ matrix.modtype != 'All' }} - name: Checking and setting the environment - id: cslenv - run: | - output="$(ls build/libs | grep -v "sources")" - - case "${{ matrix.modtype }}" in - "ForgeV1") - modloader="Forge" - path=`echo "${{ matrix.modtype }}" | sed 's#V#/V#'` - ;; - "ForgeV2" | "ForgeV3") - modloader=$(cat <> $GITHUB_OUTPUT - echo "modloader="$modloader"" >> $GITHUB_OUTPUT - echo "filename=$(echo "$output"|grep $(echo ${{ matrix.modtype }}|sed "s#-##") )" >> $GITHUB_OUTPUT - echo "version=$(grep "version=" build.properties | grep -v "#" | sed "s/version=//")" >> $GITHUB_OUTPUT - echo "edition=$(echo ${{ matrix.modtype }} | sed "s/-//")" >> $GITHUB_OUTPUT - echo "minecraft_vers<> $GITHUB_OUTPUT - grep "dependencies=" "$path"/build.properties | grep -v "#" | awk -F '|' '{print $2}' | sed 's#\;\\##' | sed "s/,/\n/g" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - echo "java_vers<> $GITHUB_OUTPUT - grep "java_full_versions=" "$path"/build.properties | grep -v "#" | sed "s/java_full_versions=//" | sed "s/,/\n/g" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - - if: startsWith(matrix.website, 'GitHub-') - name: Settings more strings to Environment - id: cslenv-github - run: | - #展开 - echo "buildtime=$(TZ=Asia/Shanghai date "+%Y.%m.%d %H:%M")" >> $GITHUB_OUTPUT - echo "pathlist="$(find build/libs -type f|grep -v "sources"|grep -v "json")"" >> $GITHUB_OUTPUT - - - if: ${{ matrix.website == 'GitHub-PreRelease' && startsWith(github.ref, 'refs/heads') }} - name: "[GitHub Pre-Release] Delete" + path: build/publish/* + + - name: Write pre-release body + if: ${{ inputs.publish && inputs['prerelease-tag'] != '' }} + env: + BUILD_TYPE: ${{ inputs.type }} + run: ./.github/scripts/write-prerelease-body.ps1 + + - name: Delete previous pre-release assets + if: ${{ inputs.publish && inputs['prerelease-tag'] != '' }} uses: 8Mi-Tech/delete-release-assets-action@main with: github_token: ${{ secrets.GITHUB_TOKEN }} - tag: CI-Build + tag: ${{ inputs['prerelease-tag'] }} deleteOnlyFromDrafts: false - - - if: ${{ matrix.website == 'GitHub-PreRelease' && startsWith(github.ref, 'refs/heads') }} - name: 【GitHub Pre-Release】 Set CI-Build Text - run: | - cat > build/ci-build.md << 'EOF' - Build Time / 构建时间 : ${{ steps.cslenv-github.outputs.buildtime }} - 自动同步分支 ${{ github.ref_name }} 的代码,以保持最新 - Synchronize ${{ github.ref_name }} branch code updates, keeping only the latest version -
- [如何使用 / How to uses?](https://github.com/xfl03/MCCustomSkinLoader/wiki/How-to-Use) - EOF - - - if: ${{ matrix.website == 'GitHub-PreRelease' && startsWith(github.ref, 'refs/heads') }} - name: "[GitHub Pre-Release] Upload Tag" + + - name: Move pre-release tag + if: ${{ inputs.publish && inputs['prerelease-tag'] != '' }} uses: richardsimko/update-tag@v1 with: - tag_name: CI-Build + tag_name: ${{ inputs['prerelease-tag'] }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - if: ${{ matrix.website == 'GitHub-PreRelease' && startsWith(github.ref, 'refs/heads') }} - name: "[GitHub Pre-Release] Publish" + + - name: Publish GitHub pre-release + if: ${{ inputs.publish && inputs['prerelease-tag'] != '' }} uses: softprops/action-gh-release@v2 with: token: ${{ secrets.GITHUB_TOKEN }} - name: CustomSkinLoader CI-Build - tag_name: CI-Build + name: ${{ inputs['prerelease-name'] }} + tag_name: ${{ inputs['prerelease-tag'] }} prerelease: true - body_path: build/ci-build.md - files: build/libs/* - #files: "${{ steps.cslenv-github.outputs.pathlist }}" - - - if: ${{ matrix.website == 'GitHub-Release' && inputs.type == 'Release' }} - uses: softprops/action-gh-release@v1 - name: "[GitHub Release] Publish" + make_latest: false + body_path: build/publish/release-notes.md + overwrite_files: true + files: | + build/publish/*.jar + build/publish/*.json + + - name: Publish GitHub release assets + if: ${{ inputs.release }} + uses: softprops/action-gh-release@v2 with: token: ${{ secrets.GITHUB_TOKEN }} - name: CustomSkinLoader ${{ github.ref }} - tag_name: ${{ github.ref }} - #generate_release_notes: true - files: ${{ steps.cslenv-github.outputs.pathlist }} - - - if: ${{ matrix.website == 'ObjectStorage' }} - name: "[ObjectStorage] Force JDK 8" - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '8' - cache: 'gradle' - - - if: ${{ matrix.website == 'ObjectStorage' }} - name: "[ObjectStorage] Publish" - env: - COS_BUCKET: ${{ secrets.COS_BUCKET }} - COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }} - COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }} - R2_BASE_URL: ${{ secrets.R2_BASE_URL }} - R2_BUCKET: ${{ secrets.R2_BUCKET }} - R2_SECRET_ID: ${{ secrets.R2_SECRET_ID }} - R2_SECRET_KEY: ${{ secrets.R2_SECRET_KEY }} - IS_SNAPSHOT: ${{ inputs.is-snapshot }} - run: | - export GIT_COMMIT_DESC=$(git log --format=%B -n 1 $GITHUB_SHA) - ./gradlew ${{ inputs.publish-task }} --info --stacktrace - # ./gradlew publishGprPublicationToGitHubPackagesRepository - - - - if: ${{ matrix.website == 'CurseForge_Modrinth' && inputs.type == 'Release' }} - name: "[CurseForge & Modrinth] Publish" + overwrite_files: true + files: | + build/publish/*.jar + build/publish/*.json + + - name: Publish to CurseForge and Modrinth + if: ${{ inputs.release }} uses: Kir-Antipov/mc-publish@v3.3 with: modrinth-id: idMHQ4n2 @@ -220,19 +154,20 @@ jobs: curseforge-id: 286924 curseforge-token: ${{ secrets.CURSEFORGE_API_KEY }} - name: CustomSkinLoader_${{ steps.cslenv.outputs.edition }}_${{ steps.cslenv.outputs.version }} - version: ${{ steps.cslenv.outputs.version }}-${{ steps.cslenv.outputs.edition }} + name: ${{ steps.publish-artifacts.outputs.mc_publish_name }} + version: ${{ steps.publish-artifacts.outputs.mc_publish_version }} version-type: release - loaders: ${{ steps.cslenv.outputs.modloader }} + loaders: | + ${{ steps.publish-artifacts.outputs.loaders }} game-versions: | - ${{ steps.cslenv.outputs.minecraft_vers }} + ${{ steps.publish-artifacts.outputs.game_versions }} game-version-filter: releases java: | - ${{ steps.cslenv.outputs.java_vers }} + ${{ steps.publish-artifacts.outputs.java_versions }} retry-attempts: 2 retry-delay: 10000 fail-mode: fail files: | - build/libs/${{ steps.cslenv.outputs.filename }} + build/publish/*.jar diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index cd3a54e0..7579e3a7 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -1,17 +1,18 @@ name: Pull Request + on: pull_request: types: - opened - synchronize - reopened - branches: - - '14-develop' - - '15-develop' jobs: build-pull-request: name: Build Pull Request uses: ./.github/workflows/common.yml with: - type: PullRequest \ No newline at end of file + type: PullRequest + publish: false + release: false + is-snapshot: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 344833a6..61730928 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,5 @@ name: Release + on: release: types: [released] @@ -9,6 +10,8 @@ jobs: uses: ./.github/workflows/common.yml with: type: Release - publish-task: upload + release: true + latest-json: latest.json + detail-json: detail.json is-snapshot: false - secrets: inherit \ No newline at end of file + secrets: inherit diff --git a/.gitignore b/.gitignore index a8685dd6..0bc3901c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,36 +1,40 @@ -# Java -*.class - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ +.kotlin -# VSCode -.vscode/ +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ -# eclipse -bin/ -*/.classpath +### Eclipse ### +.apt_generated .classpath -*/.project +.factorypath .project -.settings/ -*.launch +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ -# gradle -.gradle/ -build/ -logs/ -run/ +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ -# IDEA -.idea/ -*.iml -*.ipr -*.iws -out/ +### VS Code ### +.vscode/ -#mac OS -.DS_Store +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/Bootstrap/Core/build.gradle b/Bootstrap/Core/build.gradle new file mode 100644 index 00000000..dfcb01d6 --- /dev/null +++ b/Bootstrap/Core/build.gradle @@ -0,0 +1,6 @@ + +dependencies { + implementation 'org.ow2.asm:asm:9.9.1' + implementation 'org.ow2.asm:asm-commons:9.9.1' + implementation 'org.ow2.asm:asm-tree:9.9.1' +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/BootstrapLogger.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/BootstrapLogger.java new file mode 100644 index 00000000..aff2bf23 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/BootstrapLogger.java @@ -0,0 +1,17 @@ +package customskinloader.bootstrap; + +import java.nio.file.Path; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public final class BootstrapLogger { + public static final Logger LOGGER = LogManager.getLogger("CustomSkinLoader Bootstrap"); + + private BootstrapLogger() { + } + + public static String formatPath(Path path) { + return path == null ? "" : path.toAbsolutePath().normalize().toString(); + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/installer/BytecodeRemapper.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/installer/BytecodeRemapper.java new file mode 100644 index 00000000..4e733463 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/installer/BytecodeRemapper.java @@ -0,0 +1,411 @@ +package customskinloader.bootstrap.installer; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import customskinloader.bootstrap.mapping.Mappings; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.Remapper; + +final class BytecodeRemapper extends Remapper { + private final Map classMappings = new HashMap<>(); + private final Map inverseClassMappings = new HashMap<>(); + private final Map fieldMappings = new HashMap<>(); + private final Map methodMappings = new HashMap<>(); + private final Map classInheritance = new HashMap<>(); + private final Set missingClassInheritance = new HashSet<>(); + + BytecodeRemapper(Mappings mappings) { + for (Mappings.ClassMapping classMapping : mappings.getClassMappings()) { + this.classMappings.put(classMapping.getSourceName(), classMapping.getTargetName()); + this.inverseClassMappings.put(classMapping.getTargetName(), classMapping.getSourceName()); + + for (Mappings.FieldMapping fieldMapping : classMapping.getFieldMappings()) { + this.fieldMappings.put(this.fieldKey(classMapping.getSourceName(), fieldMapping.getSourceName()), fieldMapping.getTargetName()); + } + + for (Mappings.MethodMapping methodMapping : classMapping.getMethodMappings()) { + this.methodMappings.put(this.methodKey(classMapping.getSourceName(), methodMapping.getSourceName(), methodMapping.getSourceDescriptor()), methodMapping.getTargetName()); + } + } + } + + public void addClassInheritance(byte[] classBytes) { + ClassReader classReader = new ClassReader(classBytes); + this.classInheritance.put(classReader.getClassName(), this.readClassInheritance(classReader, false)); + } + + public byte[] remapClass(byte[] classBytes) throws Exception { + ClassReader classReader = new ClassReader(classBytes); + ClassWriter classWriter = new ClassWriter(0); + ClassVisitor classRemapper = (ClassVisitor) findClassRemapper().getConstructor(ClassVisitor.class, Remapper.class).newInstance(classWriter, this); + classReader.accept(classRemapper, ClassReader.EXPAND_FRAMES); + return classWriter.toByteArray(); + } + + private static Class findClassRemapper() throws Exception { + try { + return Class.forName("org.objectweb.asm.commons.ClassRemapper"); + } catch (ClassNotFoundException e) { + return Class.forName("org.objectweb.asm.commons.RemappingClassAdapter"); + } + } + + public String toClassEntryName(byte[] classBytes) { + return new ClassReader(classBytes).getClassName() + ".class"; + } + + @Override + public String map(String internalName) { + String mappedName = this.classMappings.get(internalName); + return mappedName == null ? internalName : mappedName; + } + + @Override + public String mapFieldName(String owner, String name, String descriptor) { + String mappedName = this.fieldMappings.get(this.fieldKey(owner, name)); + if (mappedName != null) { + return mappedName; + } + + if (this.hasDeclaredField(owner, name, descriptor)) { + return name; + } + + mappedName = this.findInheritedFieldMapping(owner, name, descriptor, new HashSet<>()); + return mappedName == null ? name : mappedName; + } + + @Override + public String mapMethodName(String owner, String name, String descriptor) { + String mappedName = this.methodMappings.get(this.methodKey(owner, name, descriptor)); + if (mappedName != null || name.charAt(0) == '<') { + return mappedName == null ? name : mappedName; + } + + Integer declaredAccess = this.getDeclaredMethodAccess(owner, name, descriptor); + if (declaredAccess != null && this.blocksInheritedMethodMapping(declaredAccess)) { + return name; + } + + mappedName = this.findInheritedMethodMapping(owner, name, descriptor, new HashSet<>()); + return mappedName == null ? name : mappedName; + } + + private String findInheritedMethodMapping(String owner, String name, String descriptor, Set visitedOwners) { + if (owner == null || !visitedOwners.add(owner)) { + return null; + } + + ClassInheritance inheritance = this.resolveClassInheritance(owner); + if (inheritance == null) { + return null; + } + + String mappedName = this.findMethodMappingInHierarchy(inheritance.superName, name, descriptor, visitedOwners); + if (mappedName != null) { + return mappedName; + } + + for (String interfaceName : inheritance.interfaces) { + mappedName = this.findMethodMappingInHierarchy(interfaceName, name, descriptor, visitedOwners); + if (mappedName != null) { + return mappedName; + } + } + + return null; + } + + private String findInheritedFieldMapping(String owner, String name, String descriptor, Set visitedOwners) { + if (owner == null || !visitedOwners.add(owner)) { + return null; + } + + ClassInheritance inheritance = this.resolveClassInheritance(owner); + if (inheritance == null) { + return null; + } + + for (String interfaceName : inheritance.interfaces) { + String mappedName = this.findFieldMappingInHierarchy(interfaceName, name, descriptor, visitedOwners); + if (mappedName != null) { + return mappedName; + } + } + + return this.findFieldMappingInHierarchy(inheritance.superName, name, descriptor, visitedOwners); + } + + private String findFieldMappingInHierarchy(String owner, String name, String descriptor, Set visitedOwners) { + if (owner == null || !visitedOwners.add(owner)) { + return null; + } + + ClassInheritance inheritance = this.resolveClassInheritance(owner); + String mappedName = this.fieldMappings.get(this.fieldKey(owner, name)); + if (mappedName != null) { + return mappedName; + } + if (inheritance == null || inheritance.hasDeclaredField(name, descriptor)) { + return null; + } + + for (String interfaceName : inheritance.interfaces) { + mappedName = this.findFieldMappingInHierarchy(interfaceName, name, descriptor, visitedOwners); + if (mappedName != null) { + return mappedName; + } + } + + return this.findFieldMappingInHierarchy(inheritance.superName, name, descriptor, visitedOwners); + } + + private String findMethodMappingInHierarchy(String owner, String name, String descriptor, Set visitedOwners) { + if (owner == null || !visitedOwners.add(owner)) { + return null; + } + + ClassInheritance inheritance = this.resolveClassInheritance(owner); + Integer declaredAccess = inheritance == null ? null : inheritance.getDeclaredMethodAccess(name, descriptor); + String mappedName = this.methodMappings.get(this.methodKey(owner, name, descriptor)); + if (mappedName != null && (declaredAccess == null || !this.blocksInheritedMethodMapping(declaredAccess))) { + return mappedName; + } + if (inheritance == null) { + return null; + } + + mappedName = this.findMethodMappingInHierarchy(inheritance.superName, name, descriptor, visitedOwners); + if (mappedName != null) { + return mappedName; + } + + for (String interfaceName : inheritance.interfaces) { + mappedName = this.findMethodMappingInHierarchy(interfaceName, name, descriptor, visitedOwners); + if (mappedName != null) { + return mappedName; + } + } + + return null; + } + + private ClassInheritance resolveClassInheritance(String sourceName) { + ClassInheritance inheritance = this.classInheritance.get(sourceName); + if (inheritance != null || sourceName.startsWith("java/") || this.missingClassInheritance.contains(sourceName)) { + return inheritance; + } + + inheritance = this.loadClassInheritance(sourceName); + if (inheritance == null) { + this.missingClassInheritance.add(sourceName); + return null; + } + + this.classInheritance.put(sourceName, inheritance); + return inheritance; + } + + private ClassInheritance loadClassInheritance(String sourceName) { + String targetName = this.map(sourceName); + InputStream inputStream = this.getResourceAsStream(targetName + ".class"); + if (inputStream == null) { + return null; + } + + try { + try (InputStream classInputStream = inputStream) { + return this.readClassInheritance(new ClassReader(classInputStream), true); + } + } catch (IOException ignored) { + return null; + } + } + + private ClassInheritance readClassInheritance(final ClassReader classReader, final boolean unmapNames) { + final Set declaredFields = new HashSet<>(); + final Map declaredMethods = new HashMap<>(); + classReader.accept(new ClassVisitor(getASMVersion()) { + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + String normalizedDescriptor = unmapNames ? BytecodeRemapper.this.unmapDescriptor(descriptor) : descriptor; + declaredFields.add(BytecodeRemapper.this.fieldSignatureKey(name, normalizedDescriptor)); + return null; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + String normalizedDescriptor = unmapNames ? BytecodeRemapper.this.unmapDescriptor(descriptor) : descriptor; + declaredMethods.put(BytecodeRemapper.this.methodSignatureKey(name, normalizedDescriptor), access); + return null; + } + }, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + + return new ClassInheritance( + unmapNames ? this.unmapClassName(classReader.getSuperName()) : classReader.getSuperName(), + unmapNames ? this.unmapClassNames(classReader.getInterfaces()) : classReader.getInterfaces(), + declaredFields, + declaredMethods + ); + } + + private static int asmVersion = 0; + private static int getASMVersion() { + if (asmVersion != 0) { + return asmVersion; + } + + asmVersion = Opcodes.ASM4; + int versionIdx = 5; + while (true) { + try { + Field asmField = Opcodes.class.getField("ASM" + versionIdx); + asmVersion = (int) asmField.get(null); + versionIdx++; + } catch (NoSuchFieldException | IllegalAccessException e) { + break; + } + } + return asmVersion; + } + + private InputStream getResourceAsStream(String resourceName) { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if (contextClassLoader != null) { + InputStream inputStream = contextClassLoader.getResourceAsStream(resourceName); + if (inputStream != null) { + return inputStream; + } + } + + ClassLoader ownClassLoader = NestedJarRemapper.class.getClassLoader(); + if (ownClassLoader == null) { + return ClassLoader.getSystemResourceAsStream(resourceName); + } + + return ownClassLoader.getResourceAsStream(resourceName); + } + + private String[] unmapClassNames(String[] classNames) { + if (classNames == null || classNames.length == 0) { + return new String[0]; + } + + String[] unmappedClassNames = new String[classNames.length]; + for (int i = 0; i < classNames.length; i++) { + unmappedClassNames[i] = this.unmapClassName(classNames[i]); + } + return unmappedClassNames; + } + + private String unmapClassName(String className) { + String unmappedName = this.inverseClassMappings.get(className); + return unmappedName == null ? className : unmappedName; + } + + private String unmapDescriptor(String descriptor) { + StringBuilder remappedDescriptor = null; + int copyStart = 0; + + for (int i = 0; i < descriptor.length(); i++) { + if (descriptor.charAt(i) != 'L') { + continue; + } + + int classNameEnd = descriptor.indexOf(';', i); + if (classNameEnd < 0) { + return descriptor; + } + + String className = descriptor.substring(i + 1, classNameEnd); + String unmappedName = this.unmapClassName(className); + if (!className.equals(unmappedName)) { + if (remappedDescriptor == null) { + remappedDescriptor = new StringBuilder(descriptor.length()); + } + remappedDescriptor.append(descriptor, copyStart, i + 1); + remappedDescriptor.append(unmappedName); + copyStart = classNameEnd; + } + + i = classNameEnd; + } + + if (remappedDescriptor == null) { + return descriptor; + } + + remappedDescriptor.append(descriptor, copyStart, descriptor.length()); + return remappedDescriptor.toString(); + } + + private Integer getDeclaredMethodAccess(String owner, String name, String descriptor) { + ClassInheritance inheritance = this.resolveClassInheritance(owner); + return inheritance == null ? null : inheritance.getDeclaredMethodAccess(name, descriptor); + } + + private boolean hasDeclaredField(String owner, String name, String descriptor) { + ClassInheritance inheritance = this.resolveClassInheritance(owner); + return inheritance != null && inheritance.hasDeclaredField(name, descriptor); + } + + private boolean blocksInheritedMethodMapping(int access) { + return (access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC)) != 0; + } + + private String fieldKey(String owner, String name) { + return owner + '#' + name; + } + + private String methodKey(String owner, String name, String descriptor) { + return owner + '#' + name + descriptor; + } + + private String fieldSignatureKey(String name, String descriptor) { + return name + descriptor; + } + + private String methodSignatureKey(String name, String descriptor) { + return name + descriptor; + } + + private static final class ClassInheritance { + private final String superName; + private final List interfaces; + private final Set declaredFields; + private final Map declaredMethods; + + ClassInheritance(String superName, String[] interfaces, Set declaredFields, Map declaredMethods) { + this.superName = superName; + if (interfaces == null || interfaces.length == 0) { + this.interfaces = Collections.emptyList(); + } else { + this.interfaces = Collections.unmodifiableList(Arrays.asList(interfaces.clone())); + } + this.declaredFields = Collections.unmodifiableSet(new HashSet<>(declaredFields)); + this.declaredMethods = Collections.unmodifiableMap(new HashMap<>(declaredMethods)); + } + + boolean hasDeclaredField(String name, String descriptor) { + return this.declaredFields.contains(name + descriptor); + } + + Integer getDeclaredMethodAccess(String name, String descriptor) { + return this.declaredMethods.get(name + descriptor); + } + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/installer/CommonJarInstaller.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/installer/CommonJarInstaller.java new file mode 100644 index 00000000..506ef633 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/installer/CommonJarInstaller.java @@ -0,0 +1,46 @@ +package customskinloader.bootstrap.installer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +import customskinloader.bootstrap.BootstrapLogger; +import customskinloader.bootstrap.mapping.Parser; +import org.apache.logging.log4j.Logger; + +public final class CommonJarInstaller { + private static final String COMMON_JAR = "CustomSkinLoader-Common.jar"; + private static final String MAPPING_RESOURCE = "/customskinloader/mapping.xml"; + private static final Parser TSRG_PARSER = new Parser(); + private static final Logger LOGGER = BootstrapLogger.LOGGER; + + private CommonJarInstaller() { + } + + public static Path releaseCommonJar(Path gameDirectory, String mappingType) throws Exception { + InputStream mappingStream = CommonJarInstaller.class.getResourceAsStream(MAPPING_RESOURCE); + if (mappingStream == null) { + throw new IOException("Could not find bundled mapping resource " + MAPPING_RESOURCE); + } + + try (InputStream mappingInputStream = mappingStream) { + Path coreDirectory = gameDirectory.resolve("CustomSkinLoader").resolve("Core"); + Files.createDirectories(coreDirectory); + + Path jarPath = coreDirectory.resolve(COMMON_JAR); + LOGGER.info("Releasing CustomSkinLoader Common jar to " + BootstrapLogger.formatPath(jarPath)); + String resourceName = "/META-INF/jar-jar/" + COMMON_JAR; + InputStream jarStream = CommonJarInstaller.class.getResourceAsStream(resourceName); + if (jarStream == null) { + throw new IOException("Could not find bundled jar resource " + resourceName); + } + + try (InputStream jarInputStream = jarStream; OutputStream outputStream = Files.newOutputStream(jarPath)) { + new NestedJarRemapper(TSRG_PARSER.parse(mappingInputStream, mappingType)).remapJar(jarInputStream, outputStream); + } + return jarPath; + } + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/installer/NestedJarRemapper.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/installer/NestedJarRemapper.java new file mode 100644 index 00000000..89057f11 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/installer/NestedJarRemapper.java @@ -0,0 +1,144 @@ +package customskinloader.bootstrap.installer; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import customskinloader.bootstrap.BootstrapLogger; +import customskinloader.bootstrap.mapping.Mappings; +import org.apache.logging.log4j.Logger; + +final class NestedJarRemapper { + private static final Logger LOGGER = BootstrapLogger.LOGGER; + + private final BytecodeRemapper remapper; + + NestedJarRemapper(Mappings mappings) { + this.remapper = new BytecodeRemapper(mappings); + } + + public void remapJar(InputStream inputStream, OutputStream outputStream) throws Exception { + List entries = new ArrayList<>(); + Manifest manifest; + + try (JarInputStream jarInputStream = new JarInputStream(inputStream)) { + manifest = jarInputStream.getManifest(); + + JarEntry entry; + while ((entry = jarInputStream.getNextJarEntry()) != null) { + if (entry.isDirectory() || this.isSignatureEntry(entry.getName())) { + continue; + } + + byte[] entryBytes = this.readAllBytes(jarInputStream); + entries.add(new JarEntryData(entry.getName(), entry.getTime(), entryBytes)); + if (entry.getName().endsWith(".class")) { + this.remapper.addClassInheritance(entryBytes); + } + } + } + + LOGGER.debug("Read " + entries.size() + " entries from nested CustomSkinLoader Common jar"); + + try (JarOutputStream jarOutputStream = manifest == null + ? new JarOutputStream(outputStream) + : new JarOutputStream(outputStream, this.copyManifest(manifest))) { + for (JarEntryData entry : entries) { + byte[] entryBytes = entry.bytes; + if (entry.name.endsWith(".class")) { + entryBytes = this.remapper.remapClass(entryBytes); + this.writeEntry(jarOutputStream, this.toRemappedClassEntryName(entry.name, entryBytes), entry.time, entryBytes); + continue; + } + + this.writeEntry(jarOutputStream, entry.name, entry.time, entryBytes); + } + jarOutputStream.finish(); + } + + LOGGER.debug("Remapped " + entries.size() + " entries from nested CustomSkinLoader Common jar"); + } + + private Manifest copyManifest(Manifest manifest) { + Manifest copiedManifest = new Manifest(); + copiedManifest.getMainAttributes().putAll(manifest.getMainAttributes()); + + for (Map.Entry entry : manifest.getEntries().entrySet()) { + Attributes copiedAttributes = new Attributes(); + copiedAttributes.putAll(entry.getValue()); + copiedManifest.getEntries().put(entry.getKey(), copiedAttributes); + } + + return copiedManifest; + } + + private boolean isSignatureEntry(String entryName) { + String upperCaseEntryName = entryName.toUpperCase(); + return upperCaseEntryName.startsWith("META-INF/") + && (upperCaseEntryName.endsWith(".SF") || upperCaseEntryName.endsWith(".DSA") || upperCaseEntryName.endsWith(".RSA") || upperCaseEntryName.endsWith(".EC")); + } + + private String toRemappedClassEntryName(String sourceEntryName, byte[] remappedClassBytes) { + String classEntryName = this.remapper.toClassEntryName(remappedClassBytes); + String versionedPrefix = this.getMultiReleaseVersionedPrefix(sourceEntryName); + return versionedPrefix == null ? classEntryName : versionedPrefix + classEntryName; + } + + private String getMultiReleaseVersionedPrefix(String entryName) { + String prefix = "META-INF/versions/"; + if (!entryName.startsWith(prefix)) { + return null; + } + + int versionEnd = entryName.indexOf('/', prefix.length()); + if (versionEnd < 0) { + return null; + } + + return entryName.substring(0, versionEnd + 1); + } + + private byte[] readAllBytes(InputStream inputStream) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + int readBytes; + + while ((readBytes = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, readBytes); + } + + return outputStream.toByteArray(); + } + + private void writeEntry(JarOutputStream jarOutputStream, String entryName, long sourceEntryTime, byte[] entryBytes) throws IOException { + JarEntry jarEntry = new JarEntry(entryName); + if (sourceEntryTime >= 0L) { + jarEntry.setTime(sourceEntryTime); + } + + jarOutputStream.putNextEntry(jarEntry); + jarOutputStream.write(entryBytes); + jarOutputStream.closeEntry(); + } + + private static final class JarEntryData { + private final String name; + private final long time; + private final byte[] bytes; + + JarEntryData(String name, long time, byte[] bytes) { + this.name = name; + this.time = time; + this.bytes = bytes; + } + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/mapping/Loader.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/mapping/Loader.java new file mode 100644 index 00000000..2558ba78 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/mapping/Loader.java @@ -0,0 +1,31 @@ +package customskinloader.bootstrap.mapping; + +import java.io.IOException; +import java.io.InputStream; + +public final class Loader { + private static final String MAPPING_RESOURCE = "/customskinloader/mapping.xml"; + + private Loader() { + } + + public static Mappings load(ClassLoader classLoader, String mappingType) { + if (mappingType == null || mappingType.trim().isEmpty()) { + return Mappings.EMPTY; + } + + InputStream resourceStream = classLoader.getResourceAsStream(MAPPING_RESOURCE.substring(1)); + if (resourceStream == null) { + resourceStream = Loader.class.getResourceAsStream(MAPPING_RESOURCE); + } + if (resourceStream == null) { + throw new IllegalStateException("Could not find bundled mapping resource " + MAPPING_RESOURCE); + } + + try (InputStream inputStream = resourceStream) { + return new Parser().parse(inputStream, mappingType); + } catch (IOException exception) { + throw new IllegalStateException("Failed to parse bundled mapping resource " + MAPPING_RESOURCE, exception); + } + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/mapping/Mappings.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/mapping/Mappings.java new file mode 100644 index 00000000..44c2c062 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/mapping/Mappings.java @@ -0,0 +1,262 @@ +package customskinloader.bootstrap.mapping; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class Mappings { + public static final Mappings EMPTY = new Mappings(Collections.emptyList()); + + private static final Pattern CLASS_NAME_IN_DESCRIPTOR = Pattern.compile("L([^;]+);"); + + private final List classMappings; + private final Map classMappingsBySourceName; + private final Map classMappingsByTargetName; + private final Map fieldMappingsBySourceKey; + private final Map fieldMappingsByTargetKey; + private final Map methodMappingsBySourceKey; + private final Map methodMappingsByTargetKey; + + public Mappings(List classMappings) { + List copiedClassMappings = Collections.unmodifiableList(new ArrayList<>(classMappings)); + Map sourceIndex = new LinkedHashMap<>(); + Map targetIndex = new LinkedHashMap<>(); + Map sourceToTargetClassNames = new LinkedHashMap<>(); + Map targetToSourceClassNames = new LinkedHashMap<>(); + + for (ClassMapping classMapping : copiedClassMappings) { + String normalizedSourceName = normalizeClassName(classMapping.getSourceName()); + String normalizedTargetName = normalizeClassName(classMapping.getTargetName()); + sourceIndex.put(normalizedSourceName, classMapping); + targetIndex.put(normalizedTargetName, classMapping); + sourceToTargetClassNames.put(normalizedSourceName, normalizedTargetName); + targetToSourceClassNames.put(normalizedTargetName, normalizedSourceName); + } + + Map sourceFieldIndex = new LinkedHashMap<>(); + Map targetFieldIndex = new LinkedHashMap<>(); + Map sourceMethodIndex = new LinkedHashMap<>(); + Map targetMethodIndex = new LinkedHashMap<>(); + + for (ClassMapping classMapping : copiedClassMappings) { + String normalizedSourceOwner = normalizeClassName(classMapping.getSourceName()); + String normalizedTargetOwner = normalizeClassName(classMapping.getTargetName()); + + for (FieldMapping fieldMapping : classMapping.getFieldMappings()) { + sourceFieldIndex.put(fieldKey(normalizedSourceOwner, fieldMapping.getSourceName()), fieldMapping); + targetFieldIndex.put(fieldKey(normalizedTargetOwner, fieldMapping.getTargetName()), fieldMapping); + } + + for (MethodMapping methodMapping : classMapping.getMethodMappings()) { + sourceMethodIndex.put(methodKey(normalizedSourceOwner, methodMapping.getSourceName(), methodMapping.getSourceDescriptor()), methodMapping); + targetMethodIndex.put( + methodKey( + normalizedTargetOwner, + methodMapping.getTargetName(), + remapDescriptor(methodMapping.getSourceDescriptor(), sourceToTargetClassNames) + ), + methodMapping + ); + } + } + + this.classMappings = copiedClassMappings; + this.classMappingsBySourceName = Collections.unmodifiableMap(sourceIndex); + this.classMappingsByTargetName = Collections.unmodifiableMap(targetIndex); + this.fieldMappingsBySourceKey = Collections.unmodifiableMap(sourceFieldIndex); + this.fieldMappingsByTargetKey = Collections.unmodifiableMap(targetFieldIndex); + this.methodMappingsBySourceKey = Collections.unmodifiableMap(sourceMethodIndex); + this.methodMappingsByTargetKey = Collections.unmodifiableMap(targetMethodIndex); + } + + public List getClassMappings() { + return this.classMappings; + } + + public ClassMapping getClassMappingBySourceName(String sourceName) { + return this.classMappingsBySourceName.get(normalizeClassName(sourceName)); + } + + public ClassMapping getClassMappingByTargetName(String targetName) { + return this.classMappingsByTargetName.get(normalizeClassName(targetName)); + } + + public String remapClassName(String sourceName) { + ClassMapping classMapping = this.getClassMappingBySourceName(sourceName); + return classMapping == null ? normalizeClassName(sourceName) : normalizeClassName(classMapping.getTargetName()); + } + + public String unmapClassName(String targetName) { + ClassMapping classMapping = this.getClassMappingByTargetName(targetName); + return classMapping == null ? normalizeClassName(targetName) : normalizeClassName(classMapping.getSourceName()); + } + + public String remapFieldName(String sourceOwner, String sourceName) { + FieldMapping fieldMapping = this.fieldMappingsBySourceKey.get(fieldKey(normalizeClassName(sourceOwner), sourceName)); + return fieldMapping == null ? sourceName : fieldMapping.getTargetName(); + } + + public String unmapFieldName(String targetOwner, String targetName) { + FieldMapping fieldMapping = this.fieldMappingsByTargetKey.get(fieldKey(normalizeClassName(targetOwner), targetName)); + return fieldMapping == null ? targetName : fieldMapping.getSourceName(); + } + + public String remapMethodName(String sourceOwner, String sourceName, String sourceDescriptor) { + MethodMapping methodMapping = this.methodMappingsBySourceKey.get(methodKey(normalizeClassName(sourceOwner), sourceName, sourceDescriptor)); + return methodMapping == null ? sourceName : methodMapping.getTargetName(); + } + + public String unmapMethodName(String targetOwner, String targetName, String targetDescriptor) { + MethodMapping methodMapping = this.methodMappingsByTargetKey.get(methodKey(normalizeClassName(targetOwner), targetName, targetDescriptor)); + return methodMapping == null ? targetName : methodMapping.getSourceName(); + } + + public String remapMethodDescriptor(String sourceDescriptor) { + return remapDescriptor(sourceDescriptor, true); + } + + public String unmapMethodDescriptor(String targetDescriptor) { + return remapDescriptor(targetDescriptor, false); + } + + private String remapDescriptor(String descriptor, boolean sourceToTarget) { + if (descriptor == null || descriptor.isEmpty()) { + return descriptor; + } + + Matcher matcher = CLASS_NAME_IN_DESCRIPTOR.matcher(descriptor); + StringBuffer stringBuffer = new StringBuffer(); + while (matcher.find()) { + String className = matcher.group(1); + String mappedName = sourceToTarget ? this.remapClassName(className) : this.unmapClassName(className); + matcher.appendReplacement(stringBuffer, "L" + Matcher.quoteReplacement(mappedName) + ";"); + } + matcher.appendTail(stringBuffer); + return stringBuffer.toString(); + } + + private static String remapDescriptor(String descriptor, Map classNameMappings) { + if (descriptor == null || descriptor.isEmpty()) { + return descriptor; + } + + Matcher matcher = CLASS_NAME_IN_DESCRIPTOR.matcher(descriptor); + StringBuffer stringBuffer = new StringBuffer(); + while (matcher.find()) { + String className = matcher.group(1); + String mappedName = classNameMappings.get(className); + if (mappedName == null) { + mappedName = className; + } + matcher.appendReplacement(stringBuffer, "L" + Matcher.quoteReplacement(mappedName) + ";"); + } + matcher.appendTail(stringBuffer); + return stringBuffer.toString(); + } + + private static String normalizeClassName(String className) { + return className == null ? null : className.replace('.', '/'); + } + + private static String fieldKey(String owner, String name) { + return owner + '#' + name; + } + + private static String methodKey(String owner, String name, String descriptor) { + return owner + '#' + name + descriptor; + } + + public static final class ClassMapping { + private final String sourceName; + private final String targetName; + private final List fieldMappings = new ArrayList<>(); + private final List methodMappings = new ArrayList<>(); + + public ClassMapping(String sourceName, String targetName) { + this.sourceName = sourceName; + this.targetName = targetName; + } + + public String getSourceName() { + return this.sourceName; + } + + public String getTargetName() { + return this.targetName; + } + + public List getFieldMappings() { + return Collections.unmodifiableList(this.fieldMappings); + } + + public List getMethodMappings() { + return Collections.unmodifiableList(this.methodMappings); + } + + void addFieldMapping(FieldMapping fieldMapping) { + this.fieldMappings.add(fieldMapping); + } + + void addMethodMapping(MethodMapping methodMapping) { + this.methodMappings.add(methodMapping); + } + } + + public static final class FieldMapping { + private final ClassMapping owner; + private final String sourceName; + private final String targetName; + + public FieldMapping(ClassMapping owner, String sourceName, String targetName) { + this.owner = owner; + this.sourceName = sourceName; + this.targetName = targetName; + } + + public ClassMapping getOwner() { + return this.owner; + } + + public String getSourceName() { + return this.sourceName; + } + + public String getTargetName() { + return this.targetName; + } + } + + public static final class MethodMapping { + private final ClassMapping owner; + private final String sourceName; + private final String sourceDescriptor; + private final String targetName; + + public MethodMapping(ClassMapping owner, String sourceName, String sourceDescriptor, String targetName) { + this.owner = owner; + this.sourceName = sourceName; + this.sourceDescriptor = sourceDescriptor; + this.targetName = targetName; + } + + public ClassMapping getOwner() { + return this.owner; + } + + public String getSourceName() { + return this.sourceName; + } + + public String getSourceDescriptor() { + return this.sourceDescriptor; + } + + public String getTargetName() { + return this.targetName; + } + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/mapping/Parser.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/mapping/Parser.java new file mode 100644 index 00000000..d4931c56 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/mapping/Parser.java @@ -0,0 +1,96 @@ +package customskinloader.bootstrap.mapping; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import customskinloader.bootstrap.util.RangeMatcher; +import customskinloader.bootstrap.util.XmlUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public final class Parser { + public Mappings parse(InputStream inputStream, String mappingType) throws IOException { + InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); + Document document = XmlUtils.parseDocument(reader, "customskinloader mapping.xml"); + return this.buildMappings(document, mappingType, VersionMetadataReader.PROTOCOL_VERSION, VersionMetadataReader.WORLD_VERSION); + } + + private Mappings buildMappings(Document document, String mappingType, int protocolVersion, int worldVersion) { + List classMappings = new ArrayList<>(); + List classElements = XmlUtils.childElements(document.getDocumentElement(), "class"); + for (Element classElement : classElements) { + String canonicalClassName = XmlUtils.requiredAttribute(classElement, "name"); + String mappedClassName = this.selectMappedValue(XmlUtils.childElements(classElement, "name"), mappingType, protocolVersion, worldVersion); + if (mappedClassName == null) { + mappedClassName = canonicalClassName; + } + + Mappings.ClassMapping classMapping = new Mappings.ClassMapping(canonicalClassName, mappedClassName); + boolean includeClassMapping = !canonicalClassName.equals(mappedClassName); + + for (Element fieldElement : XmlUtils.childElements(classElement, "field")) { + String canonicalFieldName = XmlUtils.requiredAttribute(fieldElement, "name"); + String mappedFieldName = this.selectMappedValue(XmlUtils.childElements(fieldElement, "name"), mappingType, protocolVersion, worldVersion); + if (mappedFieldName == null || canonicalFieldName.equals(mappedFieldName)) { + continue; + } + + classMapping.addFieldMapping(new Mappings.FieldMapping(classMapping, canonicalFieldName, mappedFieldName)); + includeClassMapping = true; + } + + for (Element methodElement : XmlUtils.childElements(classElement, "method")) { + String canonicalMethodSignature = XmlUtils.requiredAttribute(methodElement, "name"); + int descriptorIndex = canonicalMethodSignature.indexOf('('); + if (descriptorIndex < 0) { + throw new IllegalArgumentException("Method mapping name must include a descriptor: " + canonicalMethodSignature); + } + + String canonicalMethodName = canonicalMethodSignature.substring(0, descriptorIndex); + String canonicalMethodDescriptor = canonicalMethodSignature.substring(descriptorIndex); + String mappedMethodName = this.selectMappedValue(XmlUtils.childElements(methodElement, "name"), mappingType, protocolVersion, worldVersion); + if (mappedMethodName == null || canonicalMethodName.equals(mappedMethodName)) { + continue; + } + + classMapping.addMethodMapping(new Mappings.MethodMapping(classMapping, canonicalMethodName, canonicalMethodDescriptor, mappedMethodName)); + includeClassMapping = true; + } + + if (includeClassMapping) { + classMappings.add(classMapping); + } + } + + return new Mappings(classMappings); + } + + private String selectMappedValue(List nameElements, String mappingType, int protocolVersion, int worldVersion) { + String selectedValue = null; + for (Element nameElement : nameElements) { + if (!mappingType.equals(nameElement.getAttribute("type"))) { + continue; + } + if (!RangeMatcher.matches(nameElement.getAttribute("version"), protocolVersion, worldVersion)) { + continue; + } + + String currentValue = nameElement.getTextContent() == null ? null : nameElement.getTextContent().trim(); + if (currentValue == null || currentValue.isEmpty()) { + continue; + } + + if (selectedValue != null && !selectedValue.equals(currentValue)) { + throw new IllegalArgumentException("Found multiple values for type \"" + mappingType + "\", protocol version " + protocolVersion + " and world version" + worldVersion + ": " + selectedValue + " vs " + currentValue); + } + + selectedValue = currentValue; + } + + return selectedValue; + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/mapping/VersionMetadataReader.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/mapping/VersionMetadataReader.java new file mode 100644 index 00000000..ad9030d0 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/mapping/VersionMetadataReader.java @@ -0,0 +1,112 @@ +package customskinloader.bootstrap.mapping; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.IntInsnNode; +import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.MethodNode; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +public final class VersionMetadataReader { + private static final String VERSION_JSON = "version.json"; + private static final String NETWORK_PROTOCOL_VERSION_FIELD = "NETWORK_PROTOCOL_VERSION"; + private static final String[] VERSION_KEYS = {"protocol_version", "world_version"}; + private static final int[] VERSIONS = {0, 0}; + + static { + initializeVersions(); + } + + public static final int PROTOCOL_VERSION = VERSIONS[0]; + public static final int WORLD_VERSION = VERSIONS[1]; + + private VersionMetadataReader() { + } + + private static void initializeVersions() { + InputStream resourceStream = ClassLoader.getSystemClassLoader().getResourceAsStream(VERSION_JSON); + if (resourceStream == null) { + readProtocolVersionFromRealmsSharedConstants(); + return; + } + + try (InputStream inputStream = resourceStream; InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) { + JsonElement rootElement = new JsonParser().parse(reader); + if (rootElement == null || !rootElement.isJsonObject()) { + return; + } + + JsonObject rootObject = rootElement.getAsJsonObject(); + for (int i = 0; i < VERSION_KEYS.length; i++) { + JsonElement versionElement = rootObject.get(VERSION_KEYS[i]); + if (versionElement != null && !versionElement.isJsonNull()) { + VERSIONS[i] = versionElement.getAsInt(); + } + } + } catch (Exception ignored) { + + } + } + + private static void readProtocolVersionFromRealmsSharedConstants() { + InputStream resourceStream = ClassLoader.getSystemClassLoader().getResourceAsStream("net/minecraft/realms/RealmsSharedConstants.class"); + if (resourceStream == null) { + return; + } + + ClassNode classNode = new ClassNode(); + try (InputStream inputStream = resourceStream) { + ClassReader classReader = new ClassReader(inputStream); + classReader.accept(classNode, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + } catch (IOException ignored) { + + } + + for (FieldNode fieldNode : classNode.fields) { + if (NETWORK_PROTOCOL_VERSION_FIELD.equals(fieldNode.name) && fieldNode.value instanceof Integer) { + VERSIONS[0] = (int) fieldNode.value; + return; + } + } + + for (MethodNode methodNode : classNode.methods) { + if (!"".equals(methodNode.name)) { + continue; + } + + for (AbstractInsnNode instruction : methodNode.instructions.toArray()) { + if (instruction.getOpcode() != Opcodes.PUTSTATIC) { + continue; + } + + FieldInsnNode fieldInsnNode = (FieldInsnNode) instruction; + if (classNode.name.equals(fieldInsnNode.owner) && NETWORK_PROTOCOL_VERSION_FIELD.equals(fieldInsnNode.name) && "I".equals(fieldInsnNode.desc)) { + instruction = instruction.getPrevious(); + int opcode = instruction.getOpcode(); + if (opcode >= Opcodes.ICONST_M1 && opcode <= Opcodes.ICONST_5) { + VERSIONS[0] = opcode - Opcodes.ICONST_0; + } else if (opcode >= Opcodes.BIPUSH && opcode <= Opcodes.SIPUSH) { + VERSIONS[0] = ((IntInsnNode) instruction).operand; + } else if (opcode == Opcodes.LDC) { + Object constant = ((LdcInsnNode) instruction).cst; + if (constant instanceof Integer) { + VERSIONS[0] = (int) constant; + } + } + break; + } + } + } + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/ClassTransformationContext.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/ClassTransformationContext.java new file mode 100644 index 00000000..9f245559 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/ClassTransformationContext.java @@ -0,0 +1,110 @@ +package customskinloader.bootstrap.transformer; + +import customskinloader.bootstrap.mapping.Mappings; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; + +public final class ClassTransformationContext { + private final String internalClassName; + private final ClassNode currentClassNode; + private final Mappings mappings; + + private ClassTransformationContext(String internalClassName, ClassNode currentClassNode, Mappings mappings) { + this.internalClassName = internalClassName; + this.currentClassNode = currentClassNode; + this.mappings = mappings == null ? Mappings.EMPTY : mappings; + } + + public static ClassTransformationContext create(String className, ClassNode classNode, Mappings mappings) { + if (classNode == null) { + throw new IllegalArgumentException("Class node must not be null"); + } + + return new ClassTransformationContext(normalizeInternalClassName(className, classNode), classNode, mappings); + } + + public String getInternalClassName() { + return this.internalClassName; + } + + public ClassNode getCurrentClassNode() { + return this.currentClassNode; + } + + public Mappings getMappings() { + return this.mappings; + } + + public String remapClassName(String sourceName) { + return this.mappings.remapClassName(sourceName); + } + + public String unmapClassName(String targetName) { + return this.mappings.unmapClassName(targetName); + } + + public String remapFieldName(String sourceOwner, String sourceName) { + return this.mappings.remapFieldName(sourceOwner, sourceName); + } + + public String unmapFieldName(String targetOwner, String targetName) { + return this.mappings.unmapFieldName(targetOwner, targetName); + } + + public String remapMethodName(String sourceOwner, String sourceName, String sourceDescriptor) { + return this.mappings.remapMethodName(sourceOwner, sourceName, sourceDescriptor); + } + + public String unmapMethodName(String targetOwner, String targetName, String targetDescriptor) { + return this.mappings.unmapMethodName(targetOwner, targetName, targetDescriptor); + } + + public String remapMethodDescriptor(String sourceDescriptor) { + return this.mappings.remapMethodDescriptor(sourceDescriptor); + } + + public String unmapMethodDescriptor(String targetDescriptor) { + return this.mappings.unmapMethodDescriptor(targetDescriptor); + } + + public boolean isTarget(String canonicalName) { + return this.getInternalClassName().equals(this.remapClassName(canonicalName)); + } + + public MethodNode findMethod(String owner, String name, String desc) { + String mappedDesc = this.remapMethodDescriptor(desc); + String mappedName = "".equals(name) ? name : this.remapMethodName(owner, name, desc); + for (MethodNode methodNode : this.getCurrentClassNode().methods) { + if (mappedName.equals(methodNode.name) && mappedDesc.equals(methodNode.desc)) { + return methodNode; + } + } + return null; + } + + public FieldNode findField(String owner, String name) { + String mappedName = this.remapFieldName(owner, name); + for (FieldNode fieldNode : this.getCurrentClassNode().fields) { + if (mappedName.equals(fieldNode.name)) { + return fieldNode; + } + } + return null; + } + + private static String normalizeInternalClassName(String className, ClassNode classNode) { + if (className != null && !className.trim().isEmpty()) { + return normalizeInternalClassName(className); + } + if (classNode == null || classNode.name == null || classNode.name.trim().isEmpty()) { + throw new IllegalArgumentException("Class name must be available from either the context or the ClassNode"); + } + + return normalizeInternalClassName(classNode.name); + } + + private static String normalizeInternalClassName(String className) { + return className.replace('.', '/'); + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/ClassTransformationReport.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/ClassTransformationReport.java new file mode 100644 index 00000000..eb23ba2a --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/ClassTransformationReport.java @@ -0,0 +1,46 @@ +package customskinloader.bootstrap.transformer; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.objectweb.asm.tree.ClassNode; + +public final class ClassTransformationReport { + private final ClassTransformationContext context; + private final List appliedTransformerNames; + private volatile byte[] transformedBytecode; + + public ClassTransformationReport(ClassTransformationContext context, List appliedTransformerNames) { + this.context = context; + this.appliedTransformerNames = Collections.unmodifiableList(new ArrayList<>(appliedTransformerNames)); + } + + public ClassTransformationContext getContext() { + return this.context; + } + + public byte[] getTransformedBytecode() { + if (this.transformedBytecode == null) { + if (this.getTransformedClassNode() == null) { + throw new IllegalStateException("Transformed bytecode is not available without a transformed ClassNode"); + } + + this.transformedBytecode = TransformerBootstrapSupport.toByteArray(this.getTransformedClassNode()); + } + + return this.transformedBytecode.clone(); + } + + public ClassNode getTransformedClassNode() { + return this.context.getCurrentClassNode(); + } + + public List getAppliedTransformerNames() { + return this.appliedTransformerNames; + } + + public boolean isModified() { + return !this.appliedTransformerNames.isEmpty(); + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/ClassTransformerRegistry.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/ClassTransformerRegistry.java new file mode 100644 index 00000000..aab3a608 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/ClassTransformerRegistry.java @@ -0,0 +1,51 @@ +package customskinloader.bootstrap.transformer; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import customskinloader.bootstrap.mapping.Mappings; + +public final class ClassTransformerRegistry { + private static final Comparator TRANSFORMER_ORDER = (left, right) -> { + // Higher-priority transformers run first so low-priority patches can react to earlier changes. + int priorityOrder = Integer.compare(right.getPriority(), left.getPriority()); + if (priorityOrder != 0) { + return priorityOrder; + } + + return left.getName().compareTo(right.getName()); + }; + + private final CopyOnWriteArrayList transformers = new CopyOnWriteArrayList<>(); + + public void register(TargetedClassTransformer transformer) { + this.transformers.add(transformer); + this.sortTransformers(); + } + + public List getTransformers() { + return Collections.unmodifiableList(new ArrayList<>(this.transformers)); + } + + public List getApplicableTransformers(String className, Mappings mappings) { + List applicableTransformers = new ArrayList<>(); + + for (TargetedClassTransformer transformer : this.transformers) { + if (transformer.supports(className, mappings)) { + applicableTransformers.add(transformer); + } + } + + return Collections.unmodifiableList(applicableTransformers); + } + + private void sortTransformers() { + List sortedTransformers = new ArrayList<>(this.transformers); + Collections.sort(sortedTransformers, TRANSFORMER_ORDER); + this.transformers.clear(); + this.transformers.addAll(sortedTransformers); + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/RuntimeClassTransformerEngine.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/RuntimeClassTransformerEngine.java new file mode 100644 index 00000000..12205d43 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/RuntimeClassTransformerEngine.java @@ -0,0 +1,46 @@ +package customskinloader.bootstrap.transformer; + +import java.util.ArrayList; +import java.util.List; + +import customskinloader.bootstrap.BootstrapLogger; +import org.apache.logging.log4j.Logger; + +public final class RuntimeClassTransformerEngine { + private static final Logger LOGGER = BootstrapLogger.LOGGER; + + private final ClassTransformerRegistry registry; + + public RuntimeClassTransformerEngine(ClassTransformerRegistry registry) { + this.registry = registry; + } + + public ClassTransformationReport transform(ClassTransformationContext context) { + List appliedTransformers = new ArrayList<>(); + + for (TargetedClassTransformer transformer : this.registry.getApplicableTransformers(context.getInternalClassName(), context.getMappings())) { + if (!this.applyTransformer(transformer, context)) { + LOGGER.debug("Skipped transformer " + transformer.getName() + " for " + context.getInternalClassName()); + continue; + } + appliedTransformers.add(transformer.getName()); + LOGGER.debug("Applied transformer " + transformer.getName() + " to " + context.getInternalClassName()); + } + + if (!appliedTransformers.isEmpty()) { + LOGGER.info("Transformed " + context.getInternalClassName() + " with " + appliedTransformers); + } + + return new ClassTransformationReport(context, appliedTransformers); + } + + private boolean applyTransformer(TargetedClassTransformer transformer, ClassTransformationContext context) { + try { + return transformer.transform(context); + } catch (Exception exception) { + String message = "Failed to transform " + context.getInternalClassName() + " with " + transformer.getName(); + LOGGER.error(message, exception); + throw new IllegalStateException(message, exception); + } + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/TargetedClassTransformer.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/TargetedClassTransformer.java new file mode 100644 index 00000000..9411b4be --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/TargetedClassTransformer.java @@ -0,0 +1,46 @@ +package customskinloader.bootstrap.transformer; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +public abstract class TargetedClassTransformer { + private final String name; + private final int priority; + private final Set targetClassNames; + + protected TargetedClassTransformer(String name, int priority, String... targetClassNames) { + this.name = name; + this.priority = priority; + this.targetClassNames = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(targetClassNames))); + } + + public final String getName() { + return this.name; + } + + public final int getPriority() { + return this.priority; + } + + public boolean supports(String className, customskinloader.bootstrap.mapping.Mappings mappings) { + if (this.targetClassNames.isEmpty()) { + return false; + } + + for (String targetClassName : this.targetClassNames) { + if (className.equals(mappings.remapClassName(targetClassName))) { + return true; + } + } + + return false; + } + + public Set getTargetClassNames() { + return this.targetClassNames; + } + + public abstract boolean transform(ClassTransformationContext context) throws Exception; +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/TransformerBootstrapSupport.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/TransformerBootstrapSupport.java new file mode 100644 index 00000000..2c5d2a94 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/TransformerBootstrapSupport.java @@ -0,0 +1,135 @@ +package customskinloader.bootstrap.transformer; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.ServiceLoader; +import java.util.Set; + +import customskinloader.bootstrap.BootstrapLogger; +import customskinloader.bootstrap.mapping.Loader; +import customskinloader.bootstrap.mapping.Mappings; +import org.apache.logging.log4j.Logger; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.tree.ClassNode; + +public final class TransformerBootstrapSupport { + private static final Field[] CLASS_NODE_FIELDS = ClassNode.class.getFields(); + private static final Logger LOGGER = BootstrapLogger.LOGGER; + + private final Object lock = new Object(); + private final Class serviceLoaderAnchor; + private final String mappingType; + private final ClassTransformerRegistry registry = new ClassTransformerRegistry(); + private final RuntimeClassTransformerEngine transformerEngine = new RuntimeClassTransformerEngine(this.registry); + + private volatile boolean transformersLoaded; + private volatile Mappings mappings; + + public TransformerBootstrapSupport(Class serviceLoaderAnchor, String mappingType) { + if (serviceLoaderAnchor == null) { + throw new IllegalArgumentException("Service loader anchor must not be null"); + } + + this.serviceLoaderAnchor = serviceLoaderAnchor; + this.mappingType = mappingType; + } + + public void ensureTransformersLoaded() { + if (this.transformersLoaded) { + return; + } + + synchronized (this.lock) { + if (this.transformersLoaded) { + return; + } + + // Discover platform-contributed targeted transformers lazily through the shared service contract. + ServiceLoader serviceLoader = ServiceLoader.load(TargetedClassTransformer.class, this.serviceLoaderAnchor.getClassLoader()); + int transformerCount = 0; + for (TargetedClassTransformer transformer : serviceLoader) { + this.registry.register(transformer); + transformerCount++; + LOGGER.debug("Registered transformer " + transformer.getName() + + " with priority " + transformer.getPriority() + + " and targets " + transformer.getTargetClassNames()); + } + + this.transformersLoaded = true; + LOGGER.info("Loaded " + transformerCount + " CustomSkinLoader bootstrap transformer(s) for " + this.mappingType + " mappings"); + } + } + + public Set collectTargetClassNames() { + this.ensureTransformersLoaded(); + Mappings loadedMappings = this.getMappings(); + + Set targetClassNames = new LinkedHashSet<>(); + for (TargetedClassTransformer transformer : this.registry.getTransformers()) { + for (String targetClassName : transformer.getTargetClassNames()) { + targetClassNames.add(loadedMappings.remapClassName(targetClassName)); + } + } + + LOGGER.info("Collected " + targetClassNames.size() + " CustomSkinLoader bootstrap target class(es) for " + this.mappingType + " mappings"); + return Collections.unmodifiableSet(targetClassNames); + } + + public boolean hasApplicableTransformers(String internalClassName) { + this.ensureTransformersLoaded(); + return !this.registry.getApplicableTransformers(internalClassName, this.getMappings()).isEmpty(); + } + + public ClassTransformationReport transformClassNode(String internalClassName, ClassNode inputClassNode) { + this.ensureTransformersLoaded(); + ClassTransformationContext context = ClassTransformationContext.create(internalClassName, inputClassNode, this.getMappings()); + return this.transformerEngine.transform(context); + } + + public Mappings getMappings() { + Mappings loadedMappings = this.mappings; + if (loadedMappings != null) { + return loadedMappings; + } + + synchronized (this.lock) { + loadedMappings = this.mappings; + if (loadedMappings == null) { + LOGGER.info("Loading CustomSkinLoader bootstrap mappings for " + this.mappingType); + loadedMappings = Loader.load(this.serviceLoaderAnchor.getClassLoader(), this.mappingType); + this.mappings = loadedMappings; + LOGGER.info("Loaded " + loadedMappings.getClassMappings().size() + " CustomSkinLoader bootstrap class mapping(s) for " + this.mappingType); + } + } + + return loadedMappings; + } + + public static byte[] toByteArray(ClassNode classNode) { + ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); + classNode.accept(classWriter); + return classWriter.toByteArray(); + } + + public static ClassNode toClassNode(byte[] classBytecode) { + ClassReader classReader = new ClassReader(classBytecode); + ClassNode classNode = new ClassNode(); + classReader.accept(classNode, 0); + return classNode; + } + + public static void copyClassNode(ClassNode source, ClassNode target) { + try { + for (Field field : CLASS_NODE_FIELDS) { + if (!Modifier.isStatic(field.getModifiers())) { + field.set(target, field.get(source)); + } + } + } catch (IllegalAccessException exception) { + throw new IllegalStateException("Failed to copy transformed ClassNode state", exception); + } + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/InterfacePatch.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/InterfacePatch.java new file mode 100644 index 00000000..aac4ee4e --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/InterfacePatch.java @@ -0,0 +1,47 @@ +package customskinloader.bootstrap.transformer.patch; + +import customskinloader.bootstrap.transformer.ClassTransformationContext; + +public final class InterfacePatch extends PatchSupport { + public InterfacePatch() { + super( + "customskinloader:interface-patch", 1000, + targets( + HTTP_TEXTURE_PROCESSOR, "[,553]", // 19w37a- (1.14.4-) + RESOURCE, "", + RESOURCE_MANAGER, "", + NATIVE_IMAGE, "[404,]", // 1.13.2+ + IDENTIFIER, "" + ) + ); + } + + @Override + public boolean transform(ClassTransformationContext context) { + boolean modified = false; + + if (context.isTarget(HTTP_TEXTURE_PROCESSOR)) { + modified |= this.requireModified("http-texture-processor.interface", this.addInterface(context.getCurrentClassNode(), FAKE_HTTP_TEXTURE_PROCESSOR)); + } + if (context.isTarget(RESOURCE)) { + boolean resourceModified = false; + resourceModified |= this.addInterface(context.getCurrentClassNode(), FAKE_I_RESOURCE_V1); + resourceModified |= this.addInterface(context.getCurrentClassNode(), FAKE_I_RESOURCE_V2); + modified |= this.requireModified("resource.interfaces", resourceModified); + } + if (context.isTarget(RESOURCE_MANAGER)) { + boolean resourceManagerModified = false; + resourceManagerModified |= this.addInterface(context.getCurrentClassNode(), FAKE_I_RESOURCE_MANAGER_V1); + resourceManagerModified |= this.addInterface(context.getCurrentClassNode(), FAKE_I_RESOURCE_MANAGER_V2); + modified |= this.requireModified("resource-manager.interfaces", resourceManagerModified); + } + if (context.isTarget(NATIVE_IMAGE)) { + modified |= this.requireModified("native-image.interface", this.addInterface(context.getCurrentClassNode(), FAKE_NATIVE_IMAGE)); + } + if (context.isTarget(IDENTIFIER)) { + modified |= this.requireModified("resource-location.patch", this.makeMethodPublicNonFinal(context.findMethod(IDENTIFIER, "", "(" + objectDesc(STRING) + objectDesc(STRING) + ")V"))); + } + + return modified; + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/PatchSupport.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/PatchSupport.java new file mode 100644 index 00000000..1a50c808 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/PatchSupport.java @@ -0,0 +1,263 @@ +package customskinloader.bootstrap.transformer.patch; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BooleanSupplier; + +import customskinloader.bootstrap.BootstrapLogger; +import customskinloader.bootstrap.mapping.VersionMetadataReader; +import customskinloader.bootstrap.transformer.TargetedClassTransformer; +import customskinloader.bootstrap.util.RangeMatcher; +import org.apache.logging.log4j.Logger; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.InnerClassNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.TypeInsnNode; + +abstract class PatchSupport extends TargetedClassTransformer implements Opcodes { + private static final String IGNORE_PATCH_FAILURE_PROPERTY = "customskinloader.ignorePatchFailure"; + private static final Logger LOGGER = BootstrapLogger.LOGGER; + + protected static final String BOOLEAN = "java/lang/Boolean"; + protected static final String OBJECT = "java/lang/Object"; + protected static final String STRING = "java/lang/String"; + protected static final String CALL_SITE = "java/lang/invoke/CallSite"; + protected static final String LAMBDA_METAFACTORY = "java/lang/invoke/LambdaMetafactory"; + protected static final String METHOD_HANDLE = "java/lang/invoke/MethodHandle"; + protected static final String METHOD_HANDLES_LOOKUP = "java/lang/invoke/MethodHandles$Lookup"; + protected static final String METHOD_TYPE = "java/lang/invoke/MethodType"; + protected static final String FILE = "java/io/File"; + protected static final String INPUT_STREAM = "java/io/InputStream"; + protected static final String PATH = "java/nio/file/Path"; + protected static final String RUNNABLE = "java/lang/Runnable"; + protected static final String MAP = "java/util/Map"; + protected static final String UUID = "java/util/UUID"; + protected static final String COMPLETABLE_FUTURE = "java/util/concurrent/CompletableFuture"; + protected static final String EXECUTOR = "java/util/concurrent/Executor"; + protected static final String EXECUTOR_SERVICE = "java/util/concurrent/ExecutorService"; + protected static final String FUTURE = "java/util/concurrent/Future"; + protected static final String FUNCTION = "java/util/function/Function"; + protected static final String SUPPLIER = "java/util/function/Supplier"; + protected static final String FUNCTION4 = "com/mojang/datafixers/util/Function4"; + + protected static final String NATIVE_IMAGE = "com/mojang/blaze3d/platform/NativeImage"; + protected static final String POSE_STACK = "com/mojang/blaze3d/vertex/PoseStack"; + + protected static final String MINECRAFT = "net/minecraft/client/Minecraft"; + protected static final String GUI_GRAPHICS = "net/minecraft/client/gui/GuiGraphics"; + protected static final String GUI_GRAPHICS_EXTRACTOR = "net/minecraft/client/gui/GuiGraphicsExtractor"; + protected static final String PLAYER_TAB_OVERLAY = "net/minecraft/client/gui/components/PlayerTabOverlay"; + protected static final String CLIENT_PACKET_LISTENER = "net/minecraft/client/multiplayer/ClientPacketListener"; + protected static final String HTTP_TEXTURE_PROCESSOR = "customskinloader/fake/itf/FakeHttpTextureProcessor"; // net/minecraft/client/renderer/HttpTextureProcessor + protected static final String MULTI_BUFFER_SOURCE = "net/minecraft/client/renderer/MultiBufferSource"; + protected static final String SUBMIT_NODE_COLLECTOR = "net/minecraft/client/renderer/SubmitNodeCollector"; + protected static final String MODEL_PART = "net/minecraft/client/model/geom/ModelPart"; + protected static final String ABSTRACT_CLIENT_PLAYER = "net/minecraft/client/player/AbstractClientPlayer"; + protected static final String CAPE_LAYER = "net/minecraft/client/renderer/entity/layers/CapeLayer"; + protected static final String AVATAR_RENDER_STATE = "net/minecraft/client/renderer/entity/state/AvatarRenderState"; + protected static final String PLAYER_RENDER_STATE = "net/minecraft/client/renderer/entity/state/PlayerRenderState"; + protected static final String PLAYER_RENDERER = "net/minecraft/client/renderer/entity/player/PlayerRenderer"; + protected static final String RENDER_TYPE = "net/minecraft/client/renderer/rendertype/RenderType"; + protected static final String RENDER_TYPES = "net/minecraft/client/renderer/rendertype/RenderTypes"; + protected static final String SKIN_MANAGER = "net/minecraft/client/resources/SkinManager"; + protected static final String SKIN_MANAGER_1 = "net/minecraft/client/resources/SkinManager$1"; + protected static final String SKIN_MANAGER_3 = "net/minecraft/client/resources/SkinManager$3"; + protected static final String SKIN_MANAGER_CACHE_KEY = "net/minecraft/client/resources/SkinManager$CacheKey"; + protected static final String SKIN_MANAGER_SKIN_TEXTURE_CALLBACK = "net/minecraft/client/resources/SkinManager$SkinTextureCallback"; + protected static final String SKIN_MANAGER_TEXTURE_CACHE = "net/minecraft/client/resources/SkinManager$TextureCache"; + protected static final String SKIN_MANAGER_TEXTURE_INFO = "net/minecraft/client/resources/SkinManager$TextureInfo"; + protected static final String SKIN_TEXTURE_DOWNLOADER = "net/minecraft/client/renderer/texture/SkinTextureDownloader"; + protected static final String TEXTURE_MANAGER = "net/minecraft/client/renderer/texture/TextureManager"; + protected static final String HTTP_TEXTURE = "net/minecraft/client/renderer/texture/HttpTexture"; + protected static final String CLIENT_ASSET_DOWNLOADED_TEXTURE = "net/minecraft/core/ClientAsset$DownloadedTexture"; + protected static final String IDENTIFIER = "net/minecraft/resources/Identifier"; + protected static final String SERVICES = "net/minecraft/server/Services"; + protected static final String RESOURCE = "net/minecraft/server/packs/resources/Resource"; + protected static final String RESOURCE_MANAGER = "net/minecraft/server/packs/resources/ResourceManager"; + protected static final String OBJECTIVE = "net/minecraft/world/scores/Objective"; + protected static final String SCOREBOARD = "net/minecraft/world/scores/Scoreboard"; + + protected static final String FAKE_HTTP_TEXTURE_PROCESSOR = "customskinloader/fake/itf/IFakeHttpTextureProcessor"; + protected static final String FAKE_I_RESOURCE_V1 = "customskinloader/fake/itf/IFakeIResource$V1"; + protected static final String FAKE_I_RESOURCE_V2 = "customskinloader/fake/itf/IFakeIResource$V2"; + protected static final String FAKE_I_RESOURCE_MANAGER_V1 = "customskinloader/fake/itf/IFakeIResourceManager$V1"; + protected static final String FAKE_I_RESOURCE_MANAGER_V2 = "customskinloader/fake/itf/IFakeIResourceManager$V2"; + protected static final String FAKE_NATIVE_IMAGE = "customskinloader/fake/itf/IFakeNativeImage"; + + protected static final String FAKE_SKIN_BUFFER = "customskinloader/fake/FakeSkinBuffer"; + protected static final String FAKE_SKIN_MANAGER = "customskinloader/fake/FakeSkinManager"; + protected static final String FAKE_CACHE_KEY = "customskinloader/fake/FakeSkinManager$FakeCacheKey"; + protected static final String FAKE_HTTP_TEXTURE_V1 = "customskinloader/fake/texture/FakeHttpTexture$V1"; + protected static final String FAKE_HTTP_TEXTURE_V2 = "customskinloader/fake/texture/FakeHttpTexture$V2"; + + protected static final String MINECRAFT_PROFILE_TEXTURE = "com/mojang/authlib/minecraft/MinecraftProfileTexture"; + protected static final String MINECRAFT_PROFILE_TEXTURE_TYPE = "com/mojang/authlib/minecraft/MinecraftProfileTexture$Type"; + protected static final String MINECRAFT_SESSION_SERVICE = "com/mojang/authlib/minecraft/MinecraftSessionService"; + protected static final String MINECRAFT_PROFILE_TEXTURES = "com/mojang/authlib/minecraft/MinecraftProfileTextures"; + protected static final String GAME_PROFILE = "com/mojang/authlib/GameProfile"; + protected static final String PROPERTY = "com/mojang/authlib/properties/Property"; + + protected static String objectDesc(String internalName) { + return "L" + internalName + ";"; + } + + private final int protocolVersion; + private final int worldVersion; + + protected PatchSupport(String name, int priority, Map targetClassNames) { + this(name, priority, VersionMetadataReader.PROTOCOL_VERSION, VersionMetadataReader.WORLD_VERSION, targetClassNames); + } + + private PatchSupport(String name, int priority, int protocolVersion, int worldVersion, Map targetClassNames) { + super(name, priority, filterTargetClassNames(targetClassNames, protocolVersion, worldVersion)); + this.protocolVersion = protocolVersion; + this.worldVersion = worldVersion; + } + + protected static Map targets(String... targetClassNameProtocolRanges) { + if (targetClassNameProtocolRanges.length % 2 != 0) { + throw new IllegalArgumentException("Target class names must be paired with protocol ranges"); + } + + Map targets = new LinkedHashMap<>(); + for (int index = 0; index < targetClassNameProtocolRanges.length; index += 2) { + targets.put(targetClassNameProtocolRanges[index], targetClassNameProtocolRanges[index + 1]); + } + return Collections.unmodifiableMap(targets); + } + + private static String[] filterTargetClassNames(Map targetClassNames, int protocolVersion, int worldVersion) { + List filteredTargetClassNames = new ArrayList<>(); + for (Map.Entry entry : targetClassNames.entrySet()) { + if (RangeMatcher.matches(entry.getValue(), protocolVersion, worldVersion)) { + filteredTargetClassNames.add(entry.getKey()); + } + } + return filteredTargetClassNames.toArray(new String[0]); + } + + protected boolean addInterface(ClassNode classNode, String interfaceName) { + if (!classNode.interfaces.contains(interfaceName)) { + classNode.interfaces.add(interfaceName); + } + return true; + } + + protected boolean makeClassPublicNonFinal(ClassNode classNode) { + boolean modified = false; + int access = makePublicNonFinal(classNode.access); + if (classNode.access != access) { + classNode.access = access; + modified = true; + } + modified |= this.makeInnerClassPublicNonFinal(classNode, classNode.name); + return modified; + } + + protected boolean makeInnerClassPublicNonFinal(ClassNode classNode, String innerName) { + boolean modified = false; + for (InnerClassNode innerClassNode : classNode.innerClasses) { + if (!innerName.equals(innerClassNode.name)) { + continue; + } + + innerClassNode.access = makePublicNonFinal(innerClassNode.access); + modified = true; + } + return modified; + } + + protected boolean makeMethodPublicNonFinal(MethodNode methodNode) { + if (methodNode == null) { + return false; + } + methodNode.access = makePublicNonFinal(methodNode.access); + return true; + } + + protected boolean makeFieldPublicNonFinal(FieldNode fieldNode) { + if (fieldNode == null) { + return false; + } + fieldNode.access = makePublicNonFinal(fieldNode.access); + return true; + } + + private static int makePublicNonFinal(int access) { + return (access & ~(ACC_PRIVATE | ACC_PROTECTED | ACC_FINAL)) | ACC_PUBLIC; + } + + protected boolean matches(String range) { + return RangeMatcher.matches(range, this.protocolVersion, this.worldVersion); + } + + protected boolean applyIfMatches(String range, String label, BooleanSupplier operation) { + if (!this.matches(range)) { + return false; + } + + return this.requireModified(label, operation.getAsBoolean()); + } + + protected boolean requireModified(String label, boolean modified) { + return this.requireModified(label, modified, true); + } + + protected boolean requireModified(String label, boolean modified, boolean required) { + if (modified) { + return true; + } + + String message = "Patch '" + this.getName() + ":" + label + "' matched protocol " + this.protocolVersion + + " but did not modify any bytecode. If you want to force CustomSkinLoader to ignore this patch failure, add -D" + + IGNORE_PATCH_FAILURE_PROPERTY + "=true to the JVM arguments."; + if ("false".equalsIgnoreCase(System.getProperty(IGNORE_PATCH_FAILURE_PROPERTY))) { + throw new IllegalStateException(message); + } + + if (!required && Boolean.getBoolean(IGNORE_PATCH_FAILURE_PROPERTY)) { + LOGGER.warn(message); + return false; + } + + throw new IllegalStateException(message); + } + + protected InsnList clone(InsnList source) { + InsnList copy = new InsnList(); + for (AbstractInsnNode abstractInsnNode : source) { + copy.add(abstractInsnNode.clone(null)); + } + return copy; + } + + protected AbstractInsnNode findPreviousTypeInstruction(AbstractInsnNode instruction, int opcode, String type) { + AbstractInsnNode current = instruction == null ? null : instruction.getPrevious(); + while (current != null) { + if (current.getOpcode() == opcode && current instanceof TypeInsnNode && type.equals(((TypeInsnNode) current).desc)) { + return current; + } + current = current.getPrevious(); + } + return null; + } + + protected AbstractInsnNode nextRealInstruction(AbstractInsnNode instruction) { + AbstractInsnNode current = instruction == null ? null : instruction.getNext(); + while (current != null && current.getOpcode() < 0) { + current = current.getNext(); + } + return current; + } + + protected static void methodNodeSet(MethodNode methodNode, AbstractInsnNode oldInstruction, AbstractInsnNode newInstruction) { + methodNode.instructions.set(oldInstruction, newInstruction); + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/RenderPatch.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/RenderPatch.java new file mode 100644 index 00000000..88cb42cd --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/RenderPatch.java @@ -0,0 +1,166 @@ +package customskinloader.bootstrap.transformer.patch; + +import customskinloader.bootstrap.transformer.ClassTransformationContext; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +public final class RenderPatch extends PatchSupport { + public RenderPatch() { + super( + "customskinloader:render-patch", 1000, + targets( + PLAYER_TAB_OVERLAY, "", + CAPE_LAYER, "[556,]", // 19w39a+ (1.15+) + PLAYER_RENDERER, "[556,767],[801,803],[0x40000001,0x400000CC]" // 19w39a ~ 1.21.1 (1.15 ~ 1.21.1) + ) + ); + } + + @Override + public boolean transform(ClassTransformationContext context) { + boolean modified = false; + + if (context.isTarget(PLAYER_TAB_OVERLAY)) { + modified |= this.requireModified("player-tab-overlay", this.patchPlayerTabOverlay(context)); + } + if (context.isTarget(CAPE_LAYER)) { + modified |= this.requireModified("cape-layer", this.patchCapeLayer(context), false); + } + if (context.isTarget(PLAYER_RENDERER)) { + modified |= this.requireModified("player-renderer", this.patchPlayerRenderer(context), false); + } + + return modified; + } + + private boolean patchPlayerTabOverlay(ClassTransformationContext context) { + boolean modified = false; + // 20w16a- (1.15.2-) + modified |= this.applyIfMatches("[,712]", "player-tab-overlay.render.v1", () -> replaceMinecraftLocalServerCheck( + context, context.findMethod(PLAYER_TAB_OVERLAY, "render", "(I" + objectDesc(SCOREBOARD) + objectDesc(OBJECTIVE) + ")V"), MINECRAFT, "isLocalServer" + )); + // 20w17a ~ 23w14a (1.16 ~ 1.19.4) + modified |= this.applyIfMatches("[713,762],[801,803],[0x40000001,0x40000082]", "player-tab-overlay.render.v2", () -> replaceMinecraftLocalServerCheck( + context, context.findMethod(PLAYER_TAB_OVERLAY, "render", "(" + objectDesc(POSE_STACK) + "I" + objectDesc(SCOREBOARD) + objectDesc(OBJECTIVE) + ")V"), MINECRAFT, "isLocalServer" + )); + // 23w16a ~ 26.1-snapshot-11 (1.20 ~ 1.21.11) + modified |= this.applyIfMatches("[763,774],[0x40000083,0x40000129]", "player-tab-overlay.render.v3", () -> replaceMinecraftLocalServerCheck( + context, context.findMethod(PLAYER_TAB_OVERLAY, "render", "(" + objectDesc(GUI_GRAPHICS) + "I" + objectDesc(SCOREBOARD) + objectDesc(OBJECTIVE) + ")V"), MINECRAFT, "isLocalServer" + )); + // 26.1-pre-1 ~ 26.2-snapshot-6 (26.1 ~ 26.1.2) + modified |= this.applyIfMatches("775,[0x4000012A,0x40000138]", "player-tab-overlay.extract-render-state.v1", () -> replaceMinecraftLocalServerCheck( + context, context.findMethod(PLAYER_TAB_OVERLAY, "extractRenderState", "(" + objectDesc(GUI_GRAPHICS_EXTRACTOR) + "I" + objectDesc(SCOREBOARD) + objectDesc(OBJECTIVE) + ")V"), MINECRAFT, "isLocalServer" + )); + // 26.2-snapshot-7+ (26.2+) + modified |= this.applyIfMatches("[776,800],[804,0x40000000],[0x40000139,]", "player-tab-overlay.extract-render-state.v2", () -> replaceMinecraftLocalServerCheck( + context, context.findMethod(PLAYER_TAB_OVERLAY, "extractRenderState", "(" + objectDesc(GUI_GRAPHICS_EXTRACTOR) + "I" + objectDesc(SCOREBOARD) + objectDesc(OBJECTIVE) + ")V"), CLIENT_PACKET_LISTENER, "onlineMode" + )); + return modified; + } + + private boolean replaceMinecraftLocalServerCheck(ClassTransformationContext context, MethodNode methodNode, String owner, String name) { + if (methodNode == null) { + return false; + } + + boolean modified = false; + String remappedOwner = context.remapClassName(owner); + String remappedName = context.remapMethodName(owner, name, "()Z"); + for (AbstractInsnNode instruction : methodNode.instructions.toArray()) { + if (!(instruction instanceof MethodInsnNode)) { + continue; + } + + MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; + if (!remappedOwner.equals(methodInsnNode.owner) || !remappedName.equals(methodInsnNode.name) || !"()Z".equals(methodInsnNode.desc)) { + continue; + } + + InsnList replacement = new InsnList(); + replacement.add(new InsnNode(POP)); + replacement.add(new InsnNode(ICONST_1)); + methodNode.instructions.insertBefore(instruction, replacement); + methodNode.instructions.remove(instruction); + modified = true; + } + + return modified; + } + + + private boolean patchCapeLayer(ClassTransformationContext context) { + boolean modified = false; + // 19w39a ~ 19w44a + modified |= this.applyIfMatches("[556,560]", "cape-layer.render.v1", () -> replaceEntitySolidWithTranslucent( + context, context.findMethod(CAPE_LAYER, "render", "(" + objectDesc(POSE_STACK) + objectDesc(MULTI_BUFFER_SOURCE) + "I" + objectDesc(ABSTRACT_CLIENT_PLAYER) + "FFFFFFF)V"), + RENDER_TYPE + )); + // 19w45a ~ 1.21.1 (1.15 ~ 1.21.1) + modified |= this.applyIfMatches("[561,767],[801,803],[0x40000001,0x400000CC]", "cape-layer.render.v2", () -> replaceEntitySolidWithTranslucent( + context, context.findMethod(CAPE_LAYER, "render", "(" + objectDesc(POSE_STACK) + objectDesc(MULTI_BUFFER_SOURCE) + "I" + objectDesc(ABSTRACT_CLIENT_PLAYER) + "FFFFFF)V"), + RENDER_TYPE + )); + // 24w33a ~ 1.21.8 (1.21.2 ~ 1.21.8) + modified |= this.applyIfMatches("[768,772],[0x400000CD,0x40000103]", "cape-layer.render.v3", () -> replaceEntitySolidWithTranslucent( + context, context.findMethod(CAPE_LAYER, "render", "(" + objectDesc(POSE_STACK) + objectDesc(MULTI_BUFFER_SOURCE) + "I" + objectDesc(PLAYER_RENDER_STATE) + "FF)V"), + RENDER_TYPE + )); + // 25w31a ~ 25w42a (1.21.9 ~ 1.21.10) + modified |= this.applyIfMatches("773,[0x40000104,0x40000112]", "cape-layer.submit.v1", () -> replaceEntitySolidWithTranslucent( + context, context.findMethod(CAPE_LAYER, "submit", "(" + objectDesc(POSE_STACK) + objectDesc(SUBMIT_NODE_COLLECTOR) + "I" + objectDesc(AVATAR_RENDER_STATE) + "FF)V"), + RENDER_TYPE + )); + // 25w43a+ (1.21.11+) + modified |= this.applyIfMatches("[774,800],[804,0x40000000],[0x40000113,]", "cape-layer.submit.v2", () -> replaceEntitySolidWithTranslucent( + context, context.findMethod(CAPE_LAYER, "submit", "(" + objectDesc(POSE_STACK) + objectDesc(SUBMIT_NODE_COLLECTOR) + "I" + objectDesc(AVATAR_RENDER_STATE) + "FF)V"), + RENDER_TYPES + )); + return modified; + } + + private boolean patchPlayerRenderer(ClassTransformationContext context) { + boolean modified = false; + // 19w39a ~ 19w44a + modified |= this.applyIfMatches("[556,560]", "player-renderer.render-hand.v1", () -> replaceEntitySolidWithTranslucent( + context, context.findMethod(PLAYER_RENDERER, "renderHand", "(" + objectDesc(POSE_STACK) + objectDesc(MULTI_BUFFER_SOURCE) + objectDesc(ABSTRACT_CLIENT_PLAYER) + objectDesc(MODEL_PART) + objectDesc(MODEL_PART) + ")V"), + RENDER_TYPE + )); + // 19w45a ~ 1.21.1 (1.15 ~ 1.21.1) + modified |= this.applyIfMatches("[561,767],[801,803],[0x40000001,0x400000CC]", "player-renderer.render-hand.v2", () -> replaceEntitySolidWithTranslucent( + context, context.findMethod(PLAYER_RENDERER, "renderHand", "(" + objectDesc(POSE_STACK) + objectDesc(MULTI_BUFFER_SOURCE) + "I" + objectDesc(ABSTRACT_CLIENT_PLAYER) + objectDesc(MODEL_PART) + objectDesc(MODEL_PART) + ")V"), + RENDER_TYPE + )); + return modified; + } + + private boolean replaceEntitySolidWithTranslucent(ClassTransformationContext context, MethodNode methodNode, String owner) { + if (methodNode == null) { + return false; + } + + boolean modified = false; + String remappedOwner = context.remapClassName(owner); + String desc = "(" + objectDesc(IDENTIFIER) + ")" + objectDesc(RENDER_TYPE); + String remappedDesc = context.remapMethodDescriptor(desc); + String originalName = context.remapMethodName(owner, "entitySolid", desc); + String replacementName = context.remapMethodName(owner, "entityTranslucent", desc); + for (AbstractInsnNode instruction : methodNode.instructions.toArray()) { + if (!(instruction instanceof MethodInsnNode)) { + continue; + } + + MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; + if (!remappedOwner.equals(methodInsnNode.owner) || !originalName.equals(methodInsnNode.name) || !remappedDesc.equals(methodInsnNode.desc)) { + continue; + } + + methodNode.instructions.set(instruction, new MethodInsnNode(INVOKESTATIC, remappedOwner, replacementName, remappedDesc, false)); + modified = true; + } + + return modified; + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/SkinManagerPatch.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/SkinManagerPatch.java new file mode 100644 index 00000000..1ad10b61 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/SkinManagerPatch.java @@ -0,0 +1,523 @@ +package customskinloader.bootstrap.transformer.patch; + +import java.util.function.Predicate; + +import customskinloader.bootstrap.transformer.ClassTransformationContext; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.InvokeDynamicInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.TypeInsnNode; +import org.objectweb.asm.tree.VarInsnNode; + +public final class SkinManagerPatch extends PatchSupport { + public SkinManagerPatch() { + super( + "customskinloader:skin-manager-patch", 1000, + targets( + SKIN_MANAGER, "", + SKIN_MANAGER_1, "[764,800],[804,0x40000000],[0x40000090,]", // 23w31a+ (1.20.2+) + SKIN_MANAGER_3, "[,340]", // 1.12.2- + SKIN_MANAGER_CACHE_KEY, "[764,800],[804,0x40000000],[0x40000090,]", // 23w31a+ (1.20.2+) + SKIN_MANAGER_TEXTURE_CACHE, "[764,800],[804,0x40000000],[0x40000090,]" // 23w31a+ (1.20.2+) + ) + ); + } + + @Override + public boolean transform(ClassTransformationContext context) { + boolean modified = false; + + if (context.isTarget(SKIN_MANAGER)) { + // 23w31a+ (1.20.2+) + modified |= this.applyIfMatches("[764,800],[804,0x40000000],[0x40000090,]", "skin-manager.cache-key-inner-class", () -> makeInnerClassPublicNonFinal(context.getCurrentClassNode(), context.remapClassName(SKIN_MANAGER_CACHE_KEY))); + modified |= this.requireModified("skin-manager", this.patchSkinManager(context)); + } + if (context.isTarget(SKIN_MANAGER_1)) { + modified |= this.requireModified("skin-manager-1", this.patchSkinManagerLoader(context)); + } + if (context.isTarget(SKIN_MANAGER_3)) { + MethodNode getUserProfile = context.findMethod(SKIN_MANAGER_3, "run", "()V"); + modified |= this.requireModified("skin-manager-3", getUserProfile != null && this.replaceGetTexturesWithFakeUserProfile(getUserProfile)); + } + if (context.isTarget(SKIN_MANAGER_CACHE_KEY)) { + modified |= this.requireModified("skin-manager-cache-key", this.patchSkinManagerCacheKeyClass(context)); + } + if (context.isTarget(SKIN_MANAGER_TEXTURE_CACHE)) { + modified |= this.requireModified("skin-manager-texture-cache", this.patchSkinManagerTextureCache(context)); + } + + return modified; + } + + private boolean patchSkinManager(ClassTransformationContext context) { + boolean modified = false; + modified |= this.injectSkinManagerConstructors(context); + // 1.20.1- + modified |= this.applyIfMatches("[,763],[801,803],[0x40000001,0x4000008E]", "skin-manager.v1", () -> patchLegacySkinManager(context)); + // 23w42a+ (1.20.3+) + modified |= this.applyIfMatches("[765,800],[804,0x40000000],[0x4000009D,]", "skin-manager.v2", () -> patchSkinManagerCacheKey(context)); + return modified; + } + + private boolean patchSkinManagerCacheKeyClass(ClassTransformationContext context) { + boolean modified = this.makeClassPublicNonFinal(context.getCurrentClassNode()); + modified |= this.makeMethodPublicNonFinal(context.findMethod(SKIN_MANAGER_CACHE_KEY, "", "(" + objectDesc(UUID) + objectDesc(PROPERTY) + ")V")); + return modified; + } + + private boolean injectSkinManagerConstructors(ClassTransformationContext context) { + boolean modified = false; + // 1.20.1- + modified |= this.applyIfMatches("[,763],[801,803],[0x40000001,0x4000008E]", "skin-manager..v1", () -> injectSetSkinCacheDir( + context, "(" + objectDesc(TEXTURE_MANAGER) + objectDesc(FILE) + objectDesc(MINECRAFT_SESSION_SERVICE) + ")V", 2, objectDesc(FILE) + )); + // 23w31a ~ 24w45a (1.20.2 ~ 1.21.3) + modified |= this.applyIfMatches("[764,768],[0x40000090,0x400000DD]", "skin-manager..v2", () -> injectSetSkinCacheDir( + context, "(" + objectDesc(TEXTURE_MANAGER) + objectDesc(PATH) + objectDesc(MINECRAFT_SESSION_SERVICE) + objectDesc(EXECUTOR) + ")V", 2, objectDesc(PATH) + )); + // 24w46a ~ 25w33a (1.21.4 ~ 1.21.8) + modified |= this.applyIfMatches("[769,772],[0x400000DE,0x40000106]", "skin-manager..v3", () -> injectSetSkinCacheDir( + context, "(" + objectDesc(PATH) + objectDesc(MINECRAFT_SESSION_SERVICE) + objectDesc(EXECUTOR) + ")V", 1, objectDesc(PATH) + )); + // 25w34a ~ 25w34b + modified |= this.applyIfMatches("[0x40000107,0x40000108]", "skin-manager..v4", () -> injectSetSkinCacheDir( + context, "(" + objectDesc(PATH) + objectDesc(SERVICES) + objectDesc(EXECUTOR) + ")V", 1, objectDesc(PATH) + )); + // 25w35a+ (1.21.9+) + modified |= this.applyIfMatches("[773,800],[804,0x40000000],[0x40000109,]", "skin-manager..v5", () -> injectSetSkinCacheDir( + context, "(" + objectDesc(PATH) + objectDesc(SERVICES) + objectDesc(SKIN_TEXTURE_DOWNLOADER) + objectDesc(EXECUTOR) + ")V", 1, objectDesc(PATH) + )); + return modified; + } + + private boolean injectSetSkinCacheDir(ClassTransformationContext context, String constructorDesc, int localIndex, String argumentDesc) { + MethodNode methodNode = context.findMethod(SKIN_MANAGER, "", constructorDesc); + if (methodNode == null) { + return false; + } + + boolean modified = false; + String remappedArgumentDesc = context.remapMethodDescriptor("(" + argumentDesc + ")V"); + for (AbstractInsnNode instruction : methodNode.instructions.toArray()) { + if (instruction.getOpcode() != RETURN) { + continue; + } + + InsnList injection = new InsnList(); + injection.add(new VarInsnNode(ALOAD, localIndex)); + injection.add(new MethodInsnNode(INVOKESTATIC, FAKE_SKIN_MANAGER, "setSkinCacheDir", remappedArgumentDesc, false)); + methodNode.instructions.insertBefore(instruction, injection); + modified = true; + } + return modified; + } + + private boolean patchLegacySkinManager(ClassTransformationContext context) { + boolean modified = false; + modified |= this.requireModified("skin-manager.v1.register-texture", this.patchLegacySkinManagerRegisterTexture(context)); + modified |= this.requireModified("skin-manager.v1.register-skins", this.patchLegacySkinManagerRegisterSkins(context)); + modified |= this.requireModified("skin-manager.v1.get-insecure-skin-information", this.patchLegacySkinManagerGetInsecureSkinInformation(context)); + // 1.13.2 ~ 1.20.1 + modified |= this.applyIfMatches("[404,763],[801,803],[0x40000001,0x4000008E]", "skin-manager.v1.lambda-register-skins-4", () -> patchLegacySkinManagerGetUserProfile(context)); + return modified; + } + + private boolean patchLegacySkinManagerRegisterTexture(ClassTransformationContext context) { + MethodNode registerTexture = context.findMethod(SKIN_MANAGER, "registerTexture", "(" + objectDesc(MINECRAFT_PROFILE_TEXTURE) + objectDesc(MINECRAFT_PROFILE_TEXTURE_TYPE) + objectDesc(SKIN_MANAGER_SKIN_TEXTURE_CALLBACK) + ")" + objectDesc(IDENTIFIER)); + if (registerTexture == null) { + return false; + } + + InsnList insnList = new InsnList(); + insnList.add(new VarInsnNode(ALOAD, 2)); + return this.patchCreateThreadDownloadImageData(context, registerTexture, insnList); + } + + private boolean patchLegacySkinManagerRegisterSkins(ClassTransformationContext context) { + MethodNode registerSkins = context.findMethod(SKIN_MANAGER, "registerSkins", "(" + objectDesc(GAME_PROFILE) + objectDesc(SKIN_MANAGER_SKIN_TEXTURE_CALLBACK) + "Z)V"); + if (registerSkins == null) { + return false; + } + + boolean modified = false; + // 19w37a- (1.14.4-) + modified |= this.applyIfMatches("[,553]", "skin-manager.register-skins.v1", () -> replaceExecutorProfileLoadAtInvocation(registerSkins, EXECUTOR_SERVICE, "submit", "(" + objectDesc(RUNNABLE) + ")" + objectDesc(FUTURE), true)); + // 19w38a ~ 1.18-exp7 (1.15 ~ 1.17.1) + modified |= this.applyIfMatches("[554,756],[801,803],[0x40000001,0x4000002F]+[2205,2831]", "skin-manager.register-skins.v2", () -> replaceExecutorProfileLoadAtInvocation(registerSkins, EXECUTOR, "execute", "(" + objectDesc(RUNNABLE) + ")V", false)); + // 21w37a ~ 1.20.1 (1.18 ~ 1.20.1) + modified |= this.applyIfMatches("[757,763],[0x40000029,0x4000008E]+[2834,3465]", "skin-manager.register-skins.v3", () -> replaceExecutorProfileLoadAtInvocation(registerSkins, EXECUTOR_SERVICE, "execute", "(" + objectDesc(RUNNABLE) + ")V", false)); + return modified; + } + + private boolean patchLegacySkinManagerGetInsecureSkinInformation(ClassTransformationContext context) { + MethodNode loadSkinFromCache = context.findMethod(SKIN_MANAGER, "getInsecureSkinInformation", "(" + objectDesc(GAME_PROFILE) + ")" + objectDesc(MAP)); + if (loadSkinFromCache == null) { + return false; + } + + return this.replaceLoadSkinFromCacheMethod(loadSkinFromCache); + } + + private boolean patchLegacySkinManagerGetUserProfile(ClassTransformationContext context) { + MethodNode getUserProfile = context.findMethod(SKIN_MANAGER, "lambda$registerSkins$4", "(" + objectDesc(GAME_PROFILE) + "Z" + objectDesc(SKIN_MANAGER_SKIN_TEXTURE_CALLBACK) + ")V"); + if (getUserProfile == null) { + return false; + } + + return this.replaceGetTexturesWithFakeUserProfile(getUserProfile); + } + + private boolean patchSkinManagerCacheKey(ClassTransformationContext context) { + boolean modified = false; + // 23w42a ~ 25w33a (1.20.3 ~ 1.21.8) + modified |= this.applyIfMatches("[765,772],[0x4000009D,0x40000106]", "skin-manager.cache-key.v1", () -> { + MethodNode getOrLoad = context.findMethod(SKIN_MANAGER, "getOrLoad", "(" + objectDesc(GAME_PROFILE) + ")" + objectDesc(COMPLETABLE_FUTURE)); + return getOrLoad != null && redirectCacheKeyConstruction(context, getOrLoad); + }); + // 25w34a+ (1.21.9+) + modified |= this.applyIfMatches("[773,800],[804,0x40000000],[0x40000107,]", "skin-manager.cache-key.v2", () -> { + MethodNode get = context.findMethod(SKIN_MANAGER, "get", "(" + objectDesc(GAME_PROFILE) + ")" + objectDesc(COMPLETABLE_FUTURE)); + return get != null && redirectCacheKeyConstruction(context, get); + }); + return modified; + } + + private boolean patchSkinManagerLoader(ClassTransformationContext context) { + boolean modified = false; + MethodNode load = context.findMethod(SKIN_MANAGER_1, "load", "(" + objectDesc(SKIN_MANAGER_CACHE_KEY) + ")" + objectDesc(COMPLETABLE_FUTURE)); + modified |= this.requireModified("skin-manager-1.executor", load != null && this.replaceCompletableFutureExecutor(load)); + + modified |= this.patchSkinManagerLoaderLambda(context); + + return modified; + } + + private boolean patchSkinManagerLoaderLambda(ClassTransformationContext context) { + boolean modified = false; + // 23w31a ~ 23w41a (1.20.2) + modified |= this.applyIfMatches("764,[0x40000090,0x4000009C]", "skin-manager-1.lambda-load-0.v1", () -> { + MethodNode oldLambda = context.findMethod(SKIN_MANAGER_1, "lambda$load$0", "(" + objectDesc(MINECRAFT_SESSION_SERVICE) + objectDesc(GAME_PROFILE) + ")" + objectDesc(SKIN_MANAGER_TEXTURE_INFO)); + return oldLambda != null && replaceGetTexturesWithFakeSkinCache(oldLambda); + }); + // 23w42a ~ 25w33a (1.20.3 ~ 1.21.8) + modified |= this.applyIfMatches("[765,772],[0x4000009D,0x40000106]", "skin-manager-1.lambda-load-0.v2", () -> { + MethodNode lambdaWithSession = context.findMethod(SKIN_MANAGER_1, "lambda$load$0", "(" + objectDesc(SKIN_MANAGER_CACHE_KEY) + objectDesc(MINECRAFT_SESSION_SERVICE) + ")" + objectDesc(MINECRAFT_PROFILE_TEXTURES)); + return lambdaWithSession != null && replaceUnpackTexturesWithFakeSkinCache(context, lambdaWithSession); + }); + // 25w34a+ (1.21.9+) + modified |= this.applyIfMatches("[773,800],[804,0x40000000],[0x40000107,]", "skin-manager-1.lambda-load-0.v3", () -> { + MethodNode lambdaWithServices = context.findMethod(SKIN_MANAGER_1, "lambda$load$0", "(" + objectDesc(SKIN_MANAGER_CACHE_KEY) + objectDesc(SERVICES) + ")" + objectDesc(MINECRAFT_PROFILE_TEXTURES)); + return lambdaWithServices != null && replaceUnpackTexturesWithFakeSkinCache(context, lambdaWithServices); + }); + return modified; + } + + private boolean patchSkinManagerTextureCache(ClassTransformationContext context) { + MethodNode registerTexture = context.findMethod(SKIN_MANAGER_TEXTURE_CACHE, "registerTexture", "(" + objectDesc(MINECRAFT_PROFILE_TEXTURE) + ")" + objectDesc(COMPLETABLE_FUTURE)); + if (registerTexture == null) { + return false; + } + + String fieldName = context.remapFieldName(SKIN_MANAGER_TEXTURE_CACHE, "type"); + InsnList loadType = new InsnList(); + loadType.add(new VarInsnNode(ALOAD, 0)); + loadType.add(new FieldInsnNode(GETFIELD, context.remapClassName(SKIN_MANAGER_TEXTURE_CACHE), fieldName, objectDesc(MINECRAFT_PROFILE_TEXTURE_TYPE))); + + boolean modified = false; + // 23w31a ~ 24w45a (1.20.2 ~ 1.21.3) + modified |= this.applyIfMatches("[764,768],[0x40000090,0x400000DD]", "skin-manager-texture-cache.http-texture", () -> redirectHttpTextureConstructorToFake( + context, registerTexture, context.remapMethodDescriptor("(" + objectDesc(FILE) + objectDesc(STRING) + objectDesc(IDENTIFIER) + "Z" + objectDesc(RUNNABLE) + ")V"), + context.remapMethodDescriptor("(" + objectDesc(FILE) + objectDesc(STRING) + objectDesc(IDENTIFIER) + "Z" + objectDesc(RUNNABLE) + objectDesc(MINECRAFT_PROFILE_TEXTURE) + objectDesc(MINECRAFT_PROFILE_TEXTURE_TYPE) + ")V"), + loadType + )); + // 24w46a ~ 25w34b (1.21.4 ~ 1.21.8) + modified |= this.applyIfMatches("[769,772],[0x400000DE,0x40000108]", "skin-manager-texture-cache.skin-texture-downloader.v1", () -> redirectSkinTextureDownloaderToFake( + context, registerTexture, INVOKESTATIC + )); + // 25w35a+ (1.21.9+) + modified |= this.applyIfMatches("[773,800],[804,0x40000000],[0x40000109,]", "skin-manager-texture-cache.skin-texture-downloader.v2", () -> redirectSkinTextureDownloaderToFake( + context, registerTexture, INVOKEVIRTUAL + )); + return modified; + } + + + private boolean patchCreateThreadDownloadImageData(ClassTransformationContext context, MethodNode methodNode, InsnList loadTextureType) { + boolean modified = false; + // 19w37a- (1.14.4-) + modified |= this.applyIfMatches("[,553]", "skin-manager.http-texture.v1", () -> redirectHttpTextureConstructorToFake( + context, methodNode, context.remapMethodDescriptor("(" + objectDesc(FILE) + objectDesc(STRING) + objectDesc(IDENTIFIER) + objectDesc(HTTP_TEXTURE_PROCESSOR) + ")V"), + context.remapMethodDescriptor("(" + objectDesc(FILE) + objectDesc(STRING) + objectDesc(IDENTIFIER) + objectDesc(HTTP_TEXTURE_PROCESSOR) + objectDesc(MINECRAFT_PROFILE_TEXTURE) + objectDesc(MINECRAFT_PROFILE_TEXTURE_TYPE) + ")V"), + loadTextureType + )); + // 19w38a ~ 1.20.1 (1.15 ~ 1.20.1) + modified |= this.applyIfMatches("[554,763],[801,803],[0x40000001,0x4000008E]", "skin-manager.http-texture.v2", () -> redirectHttpTextureConstructorToFake( + context, methodNode, context.remapMethodDescriptor("(" + objectDesc(FILE) + objectDesc(STRING) + objectDesc(IDENTIFIER) + "Z" + objectDesc(RUNNABLE) + ")V"), + context.remapMethodDescriptor("(" + objectDesc(FILE) + objectDesc(STRING) + objectDesc(IDENTIFIER) + "Z" + objectDesc(RUNNABLE) + objectDesc(MINECRAFT_PROFILE_TEXTURE) + objectDesc(MINECRAFT_PROFILE_TEXTURE_TYPE) + ")V"), + loadTextureType + )); + return modified; + } + + private boolean redirectHttpTextureConstructorToFake(ClassTransformationContext context, MethodNode methodNode, String originalDesc, String replacementDesc, InsnList loadTextureType) { + if (methodNode == null) { + return false; + } + + boolean modified = false; + String originalOwner = context.remapClassName(HTTP_TEXTURE); + String replacementOwner = context.remapClassName(FAKE_HTTP_TEXTURE_V1); + for (AbstractInsnNode instruction : methodNode.instructions.toArray()) { + if (!(instruction instanceof MethodInsnNode)) { + continue; + } + + MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; + if (methodInsnNode.getOpcode() != INVOKESPECIAL || !originalOwner.equals(methodInsnNode.owner) || !"".equals(methodInsnNode.name) || !originalDesc.equals(methodInsnNode.desc)) { + continue; + } + + AbstractInsnNode newNode = this.findPreviousTypeInstruction(methodInsnNode, NEW, originalOwner); + if (!(newNode instanceof TypeInsnNode)) { + continue; + } + + ((TypeInsnNode) newNode).desc = replacementOwner; + InsnList injection = new InsnList(); + injection.add(new VarInsnNode(ALOAD, 1)); + injection.add(this.clone(loadTextureType)); + methodNode.instructions.insertBefore(methodInsnNode, injection); + methodInsnNode.owner = replacementOwner; + methodInsnNode.desc = replacementDesc; + modified = true; + } + return modified; + } + + private boolean redirectSkinTextureDownloaderToFake(ClassTransformationContext context, MethodNode methodNode, int opcode) { + if (methodNode == null) { + return false; + } + + boolean modified = false; + String owner = context.remapClassName(SKIN_TEXTURE_DOWNLOADER); + String desc = context.remapMethodDescriptor("(" + objectDesc(IDENTIFIER) + objectDesc(PATH) + objectDesc(STRING) + "Z)" + objectDesc(COMPLETABLE_FUTURE)); + String name = context.remapMethodName(SKIN_TEXTURE_DOWNLOADER, "downloadAndRegisterSkin", "(" + objectDesc(IDENTIFIER) + objectDesc(PATH) + objectDesc(STRING) + "Z)" + objectDesc(COMPLETABLE_FUTURE)); + String replacementDesc = context.remapMethodDescriptor("(" + objectDesc(FUNCTION4) + objectDesc(IDENTIFIER) + objectDesc(PATH) + objectDesc(STRING) + "Z" + objectDesc(MINECRAFT_PROFILE_TEXTURE) + ")" + objectDesc(COMPLETABLE_FUTURE)); + String functionDesc = context.remapMethodDescriptor("(" + objectDesc(IDENTIFIER) + objectDesc(PATH) + objectDesc(STRING) + objectDesc(BOOLEAN) + ")" + objectDesc(COMPLETABLE_FUTURE)); + for (AbstractInsnNode instruction : methodNode.instructions.toArray()) { + if (!(instruction instanceof MethodInsnNode)) { + continue; + } + + MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; + if (methodInsnNode.getOpcode() != opcode || !owner.equals(methodInsnNode.owner) || !name.equals(methodInsnNode.name) || !desc.equals(methodInsnNode.desc)) { + continue; + } + + InsnList injection = new InsnList(); + if (opcode == INVOKESTATIC) { + if (!replaceSkinTextureDownloaderReceiver(methodNode, methodInsnNode, owner, name, desc, "", functionDesc, insnNode -> insnNode.getOpcode() == ASTORE, H_INVOKESTATIC)) { + continue; + } + } else { + String skinManagerOwner = context.remapClassName(SKIN_MANAGER); + String fieldName = context.remapFieldName(SKIN_MANAGER, "skinTextureDownloader"); + if (!replaceSkinTextureDownloaderReceiver(methodNode, methodInsnNode, owner, name, desc, objectDesc(owner), functionDesc, insnNode -> { + if (insnNode.getOpcode() == GETFIELD) { + FieldInsnNode fieldInsnNode = (FieldInsnNode) insnNode; + return skinManagerOwner.equals(fieldInsnNode.owner) && fieldName.equals(fieldInsnNode.name) && objectDesc(owner).equals(fieldInsnNode.desc); + } + return false; + }, H_INVOKEVIRTUAL)) { + continue; + } + } + injection.add(new VarInsnNode(ALOAD, 1)); + methodNode.instructions.insertBefore(methodInsnNode, injection); + methodNodeSet(methodNode, methodInsnNode, new MethodInsnNode(INVOKESTATIC, FAKE_HTTP_TEXTURE_V2, "downloadAndRegisterSkin", replacementDesc, false)); + modified = true; + } + return modified; + } + + private boolean replaceSkinTextureDownloaderReceiver(MethodNode methodNode, MethodInsnNode invoke, String owner, String name, String desc, String ownerDesc, String functionDesc, Predicate predicate, int handleTag) { + AbstractInsnNode current = invoke.getPrevious(); + while (current != null) { + if (predicate.test(current)) { + methodNode.instructions.insert(current, new InvokeDynamicInsnNode( + "apply", "(" + ownerDesc + ")" + objectDesc(FUNCTION4), + new Handle(H_INVOKESTATIC, LAMBDA_METAFACTORY, "metafactory", "(" + objectDesc(METHOD_HANDLES_LOOKUP) + objectDesc(STRING) + objectDesc(METHOD_TYPE) + objectDesc(METHOD_TYPE) + objectDesc(METHOD_HANDLE) + objectDesc(METHOD_TYPE) + ")" + objectDesc(CALL_SITE), false), + Type.getType("(" + objectDesc(OBJECT) + objectDesc(OBJECT) + objectDesc(OBJECT) + objectDesc(OBJECT) + ")" + objectDesc(OBJECT)), + new Handle(handleTag, owner, name, desc, false), + Type.getType(functionDesc) + )); + return true; + } + current = current.getPrevious(); + } + return false; + } + + private boolean replaceExecutorProfileLoadAtInvocation(MethodNode methodNode, String owner, String name, String desc, boolean pushNullReturn) { + boolean modified = false; + for (AbstractInsnNode instruction : methodNode.instructions.toArray()) { + if (!(instruction instanceof MethodInsnNode)) { + continue; + } + + MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; + if (!owner.equals(methodInsnNode.owner) || !name.equals(methodInsnNode.name) || !desc.equals(methodInsnNode.desc)) { + continue; + } + + InsnList replacement = new InsnList(); + replacement.add(new InsnNode(SWAP)); + replacement.add(new InsnNode(POP)); + replacement.add(new MethodInsnNode(INVOKESTATIC, FAKE_SKIN_MANAGER, "loadProfileTextures", "(" + objectDesc(RUNNABLE) + ")V", false)); + if (pushNullReturn) { + replacement.add(new InsnNode(ACONST_NULL)); + } + methodNode.instructions.insertBefore(instruction, replacement); + methodNode.instructions.remove(instruction); + modified = true; + } + return modified; + } + + private boolean replaceLoadSkinFromCacheMethod(MethodNode methodNode) { + methodNode.instructions.clear(); + methodNode.tryCatchBlocks.clear(); + methodNode.instructions.add(new VarInsnNode(ALOAD, 1)); + methodNode.instructions.add(new MethodInsnNode(INVOKESTATIC, FAKE_SKIN_MANAGER, "loadSkinFromCache", "(" + objectDesc(GAME_PROFILE) + ")" + objectDesc(MAP), false)); + methodNode.instructions.add(new InsnNode(ARETURN)); + methodNode.maxLocals = Math.max(methodNode.maxLocals, 2); + return true; + } + + private boolean replaceGetTexturesWithFakeUserProfile(MethodNode methodNode) { + boolean modified = false; + for (AbstractInsnNode instruction : methodNode.instructions.toArray()) { + if (!(instruction instanceof MethodInsnNode)) { + continue; + } + + MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; + if (!MINECRAFT_SESSION_SERVICE.equals(methodInsnNode.owner) || !"getTextures".equals(methodInsnNode.name) || !("(" + objectDesc(GAME_PROFILE) + "Z)" + objectDesc(MAP)).equals(methodInsnNode.desc)) { + continue; + } + + methodNode.instructions.set(instruction, new MethodInsnNode(INVOKESTATIC, FAKE_SKIN_MANAGER, "getUserProfile", "(" + objectDesc(MINECRAFT_SESSION_SERVICE) + objectDesc(GAME_PROFILE) + "Z)" + objectDesc(MAP), false)); + modified = true; + } + return modified; + } + + private boolean redirectCacheKeyConstruction(ClassTransformationContext context, MethodNode methodNode) { + boolean modified = false; + String owner = context.remapClassName(SKIN_MANAGER_CACHE_KEY); + String desc = context.remapMethodDescriptor("(" + objectDesc(UUID) + objectDesc(PROPERTY) + ")V"); + String replacementDesc = context.remapMethodDescriptor("(" + objectDesc(UUID) + objectDesc(PROPERTY) + objectDesc(GAME_PROFILE) + ")" + objectDesc(SKIN_MANAGER_CACHE_KEY)); + + for (AbstractInsnNode instruction : methodNode.instructions.toArray()) { + if (!(instruction instanceof MethodInsnNode)) { + continue; + } + + MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; + if (!owner.equals(methodInsnNode.owner) || !"".equals(methodInsnNode.name) || !desc.equals(methodInsnNode.desc)) { + continue; + } + + AbstractInsnNode newNode = this.findPreviousTypeInstruction(methodInsnNode, NEW, owner); + AbstractInsnNode dupNode = this.nextRealInstruction(newNode); + if (newNode == null || dupNode == null || dupNode.getOpcode() != DUP) { + continue; + } + + methodNode.instructions.remove(newNode); + methodNode.instructions.remove(dupNode); + + InsnList replacement = new InsnList(); + replacement.add(new VarInsnNode(ALOAD, 1)); + replacement.add(new MethodInsnNode(INVOKESTATIC, FAKE_CACHE_KEY, "createFakeCacheKey", replacementDesc, false)); + methodNode.instructions.insertBefore(methodInsnNode, replacement); + methodNode.instructions.remove(methodInsnNode); + modified = true; + } + + return modified; + } + + private boolean replaceCompletableFutureExecutor(MethodNode methodNode) { + boolean modified = false; + for (AbstractInsnNode instruction : methodNode.instructions.toArray()) { + if (!(instruction instanceof MethodInsnNode)) { + continue; + } + + MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; + if (!COMPLETABLE_FUTURE.equals(methodInsnNode.owner)) { + continue; + } + if (("supplyAsync".equals(methodInsnNode.name) && ("(" + objectDesc(SUPPLIER) + objectDesc(EXECUTOR) + ")" + objectDesc(COMPLETABLE_FUTURE)).equals(methodInsnNode.desc)) + || ("thenComposeAsync".equals(methodInsnNode.name) && ("(" + objectDesc(FUNCTION) + objectDesc(EXECUTOR) + ")" + objectDesc(COMPLETABLE_FUTURE)).equals(methodInsnNode.desc))) { + InsnList replacement = new InsnList(); + replacement.add(new MethodInsnNode(INVOKESTATIC, FAKE_SKIN_MANAGER, "loadProfileTextures", "(" + objectDesc(EXECUTOR) + ")" + objectDesc(EXECUTOR), false)); + methodNode.instructions.insertBefore(instruction, replacement); + modified = true; + } + } + return modified; + } + + private boolean replaceGetTexturesWithFakeSkinCache(MethodNode methodNode) { + boolean modified = false; + for (AbstractInsnNode instruction : methodNode.instructions.toArray()) { + if (!(instruction instanceof MethodInsnNode)) { + continue; + } + + MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; + if (!MINECRAFT_SESSION_SERVICE.equals(methodInsnNode.owner) || !"getTextures".equals(methodInsnNode.name) || !("(" + objectDesc(GAME_PROFILE) + "Z)" + objectDesc(MAP)).equals(methodInsnNode.desc)) { + continue; + } + + methodNode.instructions.set(instruction, new MethodInsnNode(INVOKESTATIC, FAKE_SKIN_MANAGER, "loadSkinFromCache", "(" + objectDesc(MINECRAFT_SESSION_SERVICE) + objectDesc(GAME_PROFILE) + "Z)" + objectDesc(MAP), false)); + modified = true; + } + return modified; + } + + private boolean replaceUnpackTexturesWithFakeSkinCache(ClassTransformationContext context, MethodNode methodNode) { + boolean modified = false; + String desc = context.remapMethodDescriptor("(" + objectDesc(MINECRAFT_SESSION_SERVICE) + objectDesc(PROPERTY) + objectDesc(SKIN_MANAGER_CACHE_KEY) + ")" + objectDesc(OBJECT)); + String returnType = context.remapClassName(MINECRAFT_PROFILE_TEXTURES); + + for (AbstractInsnNode instruction : methodNode.instructions.toArray()) { + if (!(instruction instanceof MethodInsnNode)) { + continue; + } + + MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; + if (!MINECRAFT_SESSION_SERVICE.equals(methodInsnNode.owner) || !"unpackTextures".equals(methodInsnNode.name) || !("(" + objectDesc(PROPERTY) + ")" + objectDesc(MINECRAFT_PROFILE_TEXTURES)).equals(methodInsnNode.desc)) { + continue; + } + + InsnList replacement = new InsnList(); + replacement.add(new VarInsnNode(ALOAD, 0)); + replacement.add(new MethodInsnNode(INVOKESTATIC, FAKE_SKIN_MANAGER, "loadSkinFromCache", desc, false)); + replacement.add(new TypeInsnNode(CHECKCAST, returnType)); + methodNode.instructions.insertBefore(instruction, replacement); + methodNode.instructions.remove(instruction); + modified = true; + } + return modified; + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/SkinTexturePatch.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/SkinTexturePatch.java new file mode 100644 index 00000000..b9b78d29 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/transformer/patch/SkinTexturePatch.java @@ -0,0 +1,177 @@ +package customskinloader.bootstrap.transformer.patch; + +import customskinloader.bootstrap.transformer.ClassTransformationContext; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.InvokeDynamicInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.VarInsnNode; + +public final class SkinTexturePatch extends PatchSupport { + public SkinTexturePatch() { + super( + "customskinloader:skin-texture-patch", 1000, + targets( + HTTP_TEXTURE, "[,768],[801,803],[0x40000001,0x400000DD]", // 24w45a- (1.21.3-) + SKIN_TEXTURE_DOWNLOADER, "[769,800],[804,0x40000000],[0x400000DE,]" // 24w46a+ (1.21.4+) + ) + ); + } + + @Override + public boolean transform(ClassTransformationContext context) { + boolean modified = false; + + if (context.isTarget(HTTP_TEXTURE)) { + // 24w45a- (1.21.3-) + modified |= this.applyIfMatches("[,768],[801,803],[0x40000001,0x400000DD]", "http-texture.uploaded", () -> this.makeFieldPublicNonFinal(context.findField(HTTP_TEXTURE, "uploaded"))); + // 19w38a ~ 24w45a (1.15 ~ 1.21.3) + modified |= this.applyIfMatches("[554,768],[801,803],[0x40000001,0x400000DD]", "http-texture.patch", () -> this.patchHttpTexture(context)); + } + if (context.isTarget(SKIN_TEXTURE_DOWNLOADER)) { + modified |= this.requireModified("skin-texture-downloader.patch", this.patchSkinTextureDownloader(context)); + } + + return modified; + } + + private boolean patchHttpTexture(ClassTransformationContext context) { + return this.replaceHttpTextureProcessLegacySkin( + context, context.findMethod(HTTP_TEXTURE, "load", "(" + objectDesc(INPUT_STREAM) + ")" + objectDesc(NATIVE_IMAGE)) + ); + } + + private boolean patchSkinTextureDownloader(ClassTransformationContext context) { + boolean modified = false; + modified |= this.requireModified("skin-texture-downloader.then-compose", this.replaceThenComposeTextureFunction( + context, context.findMethod(SKIN_TEXTURE_DOWNLOADER, "downloadAndRegisterSkin", "(" + objectDesc(IDENTIFIER) + objectDesc(PATH) + objectDesc(STRING) + "Z)" + objectDesc(COMPLETABLE_FUTURE)) + )); + // 24w46a+ (1.21.4+) + modified |= this.applyIfMatches("[769,800],[804,0x40000000],[0x400000DE,]", "skin-texture-downloader.process-legacy-skin", () -> replaceSkinTextureDownloaderProcessLegacySkin(context)); + return modified; + } + + private boolean replaceThenComposeTextureFunction(ClassTransformationContext context, MethodNode methodNode) { + if (methodNode == null) { + return false; + } + + boolean modified = false; + int locationLocal = (methodNode.access & ACC_STATIC) == 0 ? 1 : 0; + int booleanLocal = locationLocal + 3; + String remappedHookDesc = context.remapMethodDescriptor("(" + objectDesc(FUNCTION) + objectDesc(IDENTIFIER) + "Z)" + objectDesc(FUNCTION)); + for (AbstractInsnNode instruction : methodNode.instructions.toArray()) { + if (!(instruction instanceof MethodInsnNode)) { + continue; + } + + MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; + if (!COMPLETABLE_FUTURE.equals(methodInsnNode.owner) || !"thenCompose".equals(methodInsnNode.name) || !("(" + objectDesc(FUNCTION) + ")" + objectDesc(COMPLETABLE_FUTURE)).equals(methodInsnNode.desc)) { + continue; + } + + InsnList injection = new InsnList(); + injection.add(new VarInsnNode(ALOAD, locationLocal)); + injection.add(new VarInsnNode(ILOAD, booleanLocal)); + injection.add(new MethodInsnNode(INVOKESTATIC, FAKE_HTTP_TEXTURE_V2, "createTexture", remappedHookDesc, false)); + methodNode.instructions.insertBefore(instruction, injection); + modified = true; + } + return modified; + } + + private boolean replaceSkinTextureDownloaderProcessLegacySkin(ClassTransformationContext context) { + boolean modified = false; + // 24w46a ~ 25w37a (1.21.4 ~ 1.21.8) + modified |= this.applyIfMatches("[769,772],[0x400000DE,0x4000010C]", "skin-texture-downloader.process-legacy-skin.string", () -> replaceSkinTextureDownloaderProcessLegacySkin( + context, context.findMethod(SKIN_TEXTURE_DOWNLOADER, "lambda$downloadAndRegisterSkin$0", "(" + objectDesc(PATH) + objectDesc(STRING) + "Z)" + objectDesc(NATIVE_IMAGE)) + )); + // 1.21.9-pre1+ (1.21.9+) + modified |= this.applyIfMatches("[773,800],[804,0x40000000],[0x4000010D,]", "skin-texture-downloader.process-legacy-skin.downloaded-texture", () -> replaceSkinTextureDownloaderProcessLegacySkin( + context, context.findMethod(SKIN_TEXTURE_DOWNLOADER, "lambda$downloadAndRegisterSkin$0", "(" + objectDesc(PATH) + objectDesc(CLIENT_ASSET_DOWNLOADED_TEXTURE) + "Z)" + objectDesc(NATIVE_IMAGE)) + )); + return modified; + } + + private boolean replaceSkinTextureDownloaderProcessLegacySkin(ClassTransformationContext context, MethodNode methodNode) { + if (methodNode == null) { + return false; + } + + boolean modified = false; + String owner = context.remapClassName(SKIN_TEXTURE_DOWNLOADER); + String originalDesc = context.remapMethodDescriptor("(" + objectDesc(NATIVE_IMAGE) + objectDesc(STRING) + ")" + objectDesc(NATIVE_IMAGE)); + String originalName = context.remapMethodName(SKIN_TEXTURE_DOWNLOADER, "processLegacySkin", "(" + objectDesc(NATIVE_IMAGE) + objectDesc(STRING) + ")" + objectDesc(NATIVE_IMAGE)); + for (AbstractInsnNode instruction : methodNode.instructions.toArray()) { + if (!(instruction instanceof MethodInsnNode)) { + continue; + } + + MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; + if (methodInsnNode.getOpcode() != INVOKESTATIC || !owner.equals(methodInsnNode.owner) || !originalName.equals(methodInsnNode.name) || !originalDesc.equals(methodInsnNode.desc)) { + continue; + } + + methodNodeSet(methodNode, instruction, new MethodInsnNode(INVOKESTATIC, FAKE_SKIN_BUFFER, "processLegacySkin", originalDesc, false)); + modified = true; + } + return modified; + } + + private boolean replaceHttpTextureProcessLegacySkin(ClassTransformationContext context, MethodNode load) { + if (load == null) { + return false; + } + + boolean modified = false; + String owner = context.remapClassName(HTTP_TEXTURE); + String originalName = context.remapMethodName(HTTP_TEXTURE, "processLegacySkin", "(" + objectDesc(NATIVE_IMAGE) + ")" + objectDesc(NATIVE_IMAGE)); + String imageDesc = context.remapMethodDescriptor("(" + objectDesc(NATIVE_IMAGE) + ")" + objectDesc(NATIVE_IMAGE)); + String replacementDesc = context.remapMethodDescriptor("(" + objectDesc(NATIVE_IMAGE) + objectDesc(RUNNABLE) + objectDesc(FUNCTION) + ")" + objectDesc(NATIVE_IMAGE)); + String processTaskField = context.remapFieldName(HTTP_TEXTURE, "onDownloaded"); + + for (AbstractInsnNode instruction : load.instructions.toArray()) { + if (!(instruction instanceof MethodInsnNode)) { + continue; + } + + MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; + if (!owner.equals(methodInsnNode.owner) || !originalName.equals(methodInsnNode.name) || !imageDesc.equals(methodInsnNode.desc)) { + continue; + } + + boolean staticProcessLegacySkin = methodInsnNode.getOpcode() == INVOKESTATIC; + InsnList injection = new InsnList(); + int implementationHandleTag; + String functionFactoryDesc; + if (staticProcessLegacySkin) { + injection.add(new VarInsnNode(ALOAD, 0)); + injection.add(new FieldInsnNode(GETFIELD, owner, processTaskField, objectDesc(RUNNABLE))); + implementationHandleTag = H_INVOKESTATIC; + functionFactoryDesc = "()" + objectDesc(FUNCTION); + } else { + injection.add(new InsnNode(SWAP)); + injection.add(new FieldInsnNode(GETFIELD, owner, processTaskField, objectDesc(RUNNABLE))); + injection.add(new VarInsnNode(ALOAD, 0)); + implementationHandleTag = H_INVOKEVIRTUAL; + functionFactoryDesc = "(" + objectDesc(owner) + ")" + objectDesc(FUNCTION); + } + injection.add(new InvokeDynamicInsnNode( + "apply", functionFactoryDesc, + new Handle(H_INVOKESTATIC, LAMBDA_METAFACTORY, "metafactory", "(" + objectDesc(METHOD_HANDLES_LOOKUP) + objectDesc(STRING) + objectDesc(METHOD_TYPE) + objectDesc(METHOD_TYPE) + objectDesc(METHOD_HANDLE) + objectDesc(METHOD_TYPE) + ")" + objectDesc(CALL_SITE), false), + Type.getType("(" + objectDesc(OBJECT) + ")" + objectDesc(OBJECT)), + new Handle(implementationHandleTag, owner, originalName, imageDesc, false), + Type.getType(imageDesc) + )); + load.instructions.insertBefore(instruction, injection); + methodNodeSet(load, instruction, new MethodInsnNode(INVOKESTATIC, FAKE_SKIN_BUFFER, "processLegacySkin", replacementDesc, false)); + modified = true; + } + return modified; + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/util/ModLocatorUtils.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/util/ModLocatorUtils.java new file mode 100644 index 00000000..72ca7bb0 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/util/ModLocatorUtils.java @@ -0,0 +1,43 @@ +package customskinloader.bootstrap.util; + +import java.lang.reflect.Field; +import java.nio.file.Path; + +import customskinloader.bootstrap.BootstrapLogger; +import customskinloader.bootstrap.installer.CommonJarInstaller; +import org.apache.logging.log4j.Logger; + +/** + * Shared setup for Forge-style mods-folder locators that expose the released jar as a synthetic mod. + */ +public final class ModLocatorUtils { + public static final String LOCATOR_NAME = "customskinloader-locator"; + private static final Logger LOGGER = BootstrapLogger.LOGGER; + + private ModLocatorUtils() { + } + + public static void initialize(Object locator, Class locatorType, Path gameDirectory, String mappingType) { + try { + Path runtimeDirectory = resolveRuntimeDirectory(gameDirectory); + LOGGER.info("Initializing " + LOCATOR_NAME + " for " + mappingType + " mappings in " + BootstrapLogger.formatPath(gameDirectory)); + setModFolder(locator, locatorType, runtimeDirectory); + Path commonJar = CommonJarInstaller.releaseCommonJar(gameDirectory, mappingType); + LOGGER.info("Initialized " + LOCATOR_NAME + " with mod folder " + BootstrapLogger.formatPath(runtimeDirectory) + + " and Common jar " + BootstrapLogger.formatPath(commonJar)); + } catch (Exception exception) { + LOGGER.error("Failed to prepare CustomSkinLoader runtime artifacts for discovery", exception); + throw new IllegalStateException("Failed to prepare CustomSkinLoader runtime artifacts for discovery", exception); + } + } + + private static Path resolveRuntimeDirectory(Path gameDirectory) { + return gameDirectory.resolve("CustomSkinLoader").resolve("Core"); + } + + private static void setModFolder(Object locator, Class locatorType, Path modFolder) throws NoSuchFieldException, IllegalAccessException { + Field field = locatorType.getDeclaredField("modFolder"); + field.setAccessible(true); + field.set(locator, modFolder); + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/util/RangeMatcher.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/util/RangeMatcher.java new file mode 100644 index 00000000..2371e6c0 --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/util/RangeMatcher.java @@ -0,0 +1,99 @@ +package customskinloader.bootstrap.util; + +import java.util.ArrayList; +import java.util.List; + +/** + * Shared matcher for protocol and ordinal range expressions. + */ +public final class RangeMatcher { + private RangeMatcher() { + } + + public static boolean matches(String expression, int protocolVersion, int worldVersion) { + if (expression == null || expression.trim().isEmpty()) { + return true; + } + + String protocolRange = ""; + String worldRange = ""; + int index = expression.indexOf("+"); + if (index >= 0) { + protocolRange = expression.substring(0, index); + worldRange = expression.substring(index + 1); + } else { + protocolRange = expression; + } + return matches(protocolRange, protocolVersion) && matches(worldRange, worldVersion); + } + + private static boolean matches(String expression, int value) { + if (expression == null || expression.trim().isEmpty()) { + return true; + } + + for (String token : splitTokens(expression)) { + String trimmedToken = token.trim(); + if (trimmedToken.isEmpty()) { + continue; + } + + if (trimmedToken.startsWith("[") && trimmedToken.endsWith("]")) { + int commaIndex = trimmedToken.indexOf(','); + if (commaIndex < 0) { + throw new IllegalArgumentException("Invalid range token: " + trimmedToken); + } + + String leftPart = trimmedToken.substring(1, commaIndex).trim(); + String rightPart = trimmedToken.substring(commaIndex + 1, trimmedToken.length() - 1).trim(); + int lowerBound = leftPart.isEmpty() ? 0 : parseNumber(leftPart); + int upperBound = rightPart.isEmpty() ? Integer.MAX_VALUE : parseNumber(rightPart); + if (value >= lowerBound && value <= upperBound) { + return true; + } + continue; + } + + if (value == parseNumber(trimmedToken)) { + return true; + } + } + + return false; + } + + private static List splitTokens(String expression) { + List tokens = new ArrayList<>(); + StringBuilder currentToken = new StringBuilder(); + int bracketDepth = 0; + + for (int index = 0; index < expression.length(); index++) { + char currentChar = expression.charAt(index); + if (currentChar == '[') { + bracketDepth++; + } else if (currentChar == ']') { + bracketDepth--; + } + + if (currentChar == ',' && bracketDepth == 0) { + tokens.add(currentToken.toString()); + currentToken.setLength(0); + continue; + } + + currentToken.append(currentChar); + } + + tokens.add(currentToken.toString()); + return tokens; + } + + private static int parseNumber(String token) { + String trimmedToken = token.trim(); + if (trimmedToken.startsWith("0x")) { + return Integer.parseInt(trimmedToken.substring(2), 16); + } + + return Integer.parseInt(trimmedToken, 10); + } +} diff --git a/Bootstrap/Core/src/main/java/customskinloader/bootstrap/util/XmlUtils.java b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/util/XmlUtils.java new file mode 100644 index 00000000..d117d25b --- /dev/null +++ b/Bootstrap/Core/src/main/java/customskinloader/bootstrap/util/XmlUtils.java @@ -0,0 +1,75 @@ +package customskinloader.bootstrap.util; + +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * Small DOM helpers shared by mapping and patch XML parsers. + */ +public final class XmlUtils { + private XmlUtils() { + } + + public static Document parseDocument(Reader reader, String documentDescription) throws IOException { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + factory.setExpandEntityReferences(false); + factory.setIgnoringComments(true); + + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(new InputSource(reader)); + document.getDocumentElement().normalize(); + return document; + } catch (ParserConfigurationException exception) { + throw new IOException("Failed to configure XML parser for " + documentDescription, exception); + } catch (SAXException exception) { + throw new IOException("Failed to parse " + documentDescription, exception); + } + } + + public static String requiredAttribute(Element element, String attributeName) { + String value = element.getAttribute(attributeName); + if (value.trim().isEmpty()) { + throw new IllegalArgumentException("Missing required attribute '" + attributeName + "' on <" + element.getTagName() + ">"); + } + return value.trim(); + } + + public static List childElements(Element parentElement, String requiredTagName) { + if (parentElement == null) { + return Collections.emptyList(); + } + + List elements = new ArrayList<>(); + NodeList childNodes = parentElement.getChildNodes(); + for (int index = 0; index < childNodes.getLength(); index++) { + Node currentNode = childNodes.item(index); + if (currentNode.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + Element currentElement = (Element) currentNode; + if (requiredTagName == null || requiredTagName.equals(currentElement.getTagName())) { + elements.add(currentElement); + } + } + return elements; + } +} diff --git a/Common/src/main/resources/LICENSE b/Bootstrap/Core/src/main/resources/LICENSE similarity index 100% rename from Common/src/main/resources/LICENSE rename to Bootstrap/Core/src/main/resources/LICENSE diff --git a/Bootstrap/Core/src/main/resources/META-INF/services/customskinloader.bootstrap.transformer.TargetedClassTransformer b/Bootstrap/Core/src/main/resources/META-INF/services/customskinloader.bootstrap.transformer.TargetedClassTransformer new file mode 100644 index 00000000..0dff459e --- /dev/null +++ b/Bootstrap/Core/src/main/resources/META-INF/services/customskinloader.bootstrap.transformer.TargetedClassTransformer @@ -0,0 +1,4 @@ +customskinloader.bootstrap.transformer.patch.InterfacePatch +customskinloader.bootstrap.transformer.patch.RenderPatch +customskinloader.bootstrap.transformer.patch.SkinManagerPatch +customskinloader.bootstrap.transformer.patch.SkinTexturePatch diff --git a/Bootstrap/Core/src/main/resources/customskinloader/mapping.xml b/Bootstrap/Core/src/main/resources/customskinloader/mapping.xml new file mode 100644 index 00000000..38391b7f --- /dev/null +++ b/Bootstrap/Core/src/main/resources/customskinloader/mapping.xml @@ -0,0 +1,457 @@ + + + + net/minecraft/client/renderer/texture/NativeImage + net/minecraft/class_1011 + + func_195703_a + m_85054_ + method_4317 + + + func_195699_a + m_85025_ + method_4304 + + + func_195715_a + m_84997_ + method_4326 + + + func_195714_b + m_85084_ + method_4323 + + + func_195709_a + m_84985_ + method_4315 + + + func_195702_a + m_84982_ + method_4307 + + + func_195713_a + m_85058_ + method_4309 + + + func_195700_a + m_84988_ + method_4305 + + + + com/mojang/blaze3d/matrix/MatrixStack + net/minecraft/class_4587 + + + net/minecraft/client/renderer/IImageBuffer + net/minecraft/class_760 + + func_152634_a + method_3238 + + + func_195786_a + method_3237 + + + func_78432_a + + + + + func_110527_b + func_199027_b + m_6679_ + method_14482 + + + + + m_215507_ + method_14482 + + + + + func_110536_a + func_199002_a + m_142591_ + method_14486 + + + + + m_213713_ + method_14486 + + + + + method_61940 + + + method_61941 + + + + net/minecraft/class_310 + + field_71412_D + f_91069_ + field_1697 + + + func_147104_D + m_91089_ + method_1558 + + + func_71410_x + m_91087_ + method_1551 + + + func_110442_L + func_195551_G + m_91098_ + method_1478 + + + func_152342_ad + m_91109_ + method_1582 + + + func_110434_K + m_91097_ + method_1531 + + + func_71387_A + m_91090_ + method_1542 + + + + net/minecraft/class_332 + + + + net/minecraft/client/gui/GuiPlayerTabOverlay + net/minecraft/class_355 + + + func_175249_a + method_1919 + + + func_238523_a_ + m_94544_ + method_1919 + + + m_280406_ + method_1919 + + + + net/minecraft/client/renderer/model/ModelRenderer + net/minecraft/class_630 + + + net/minecraft/client/network/NetHandlerPlayClient + net/minecraft/client/network/play/ClientPlayNetHandler + net/minecraft/class_634 + + + + net/minecraft/class_642 + + field_78845_b + f_105363_ + field_3761 + + + + net/minecraft/client/entity/AbstractClientPlayer + net/minecraft/client/entity/player/AbstractClientPlayerEntity + net/minecraft/class_742 + + + net/minecraft/client/renderer/IRenderTypeBuffer + net/minecraft/class_4597 + + + net/minecraft/class_11659 + + + net/minecraft/class_972 + + func_225628_a_ + m_6494_ + method_4177 + + + method_4177 + + + method_4177 + + + method_4177 + + + + net/minecraft/client/renderer/entity/PlayerRenderer + net/minecraft/class_1007 + + func_229145_a_ + m_117775_ + method_23205 + + + method_23205 + + + + net/minecraft/class_10055 + + + net/minecraft/class_10055 + + + net/minecraft/client/renderer/RenderType + net/minecraft/class_1921 + net/minecraft/client/renderer/RenderType + + func_228634_a_ + m_110446_ + method_23572 + + + func_228644_e_ + m_110473_ + method_23580 + + + + net/minecraft/class_12249 + + method_75982 + + + method_76000 + + + + net/minecraft/class_1044 + + + net/minecraft/client/renderer/ThreadDownloadImageData + net/minecraft/client/renderer/texture/ThreadDownloadImageData + net/minecraft/client/renderer/texture/DownloadingTexture + net/minecraft/class_1046 + + field_229155_i_ + f_117997_ + field_20843 + + + field_110559_g + f_117999_ + field_5215 + + + func_229159_a_ + m_118018_ + method_22795 + + + func_229163_c_ + m_118032_ + method_22798 + + + func_147641_a + + + + net/minecraft/class_10537 + + + net/minecraft/class_1049 + + method_65809 + + + + net/minecraft/class_10538 + + method_65861 + + + method_65866 + + + method_65866 + + + method_65863 + + + + net/minecraft/class_10539 + + + net/minecraft/class_1060 + + func_229263_a_ + m_118495_ + method_4616 + + + method_65876 + + + + net/minecraft/class_1071 + + field_62487 + + + method_52863 + + + func_152788_a + m_118815_ + method_4654 + + + m_293351_ + method_52863 + + + func_210275_a + func_229293_a_ + m_118821_ + method_4653 + + + func_152790_a + m_118817_ + method_4652 + + + func_152792_a + m_118825_ + method_4656 + + + func_152789_a + m_118828_ + method_4651 + + + + net/minecraft/class_1071$1 + + m_293645_ + method_52867 + + + m_304063_ + method_54647 + + + method_54647 + + + method_52868 + + + + net/minecraft/class_1071$class_8686 + + + net/minecraft/client/resources/SkinManager$SkinAvailableCallback + net/minecraft/client/resources/SkinManager$ISkinAvailableCallback + net/minecraft/class_1071$class_1072 + + + net/minecraft/class_1071$class_8687 + + f_291290_ + field_45641 + + + m_294542_ + method_52873 + + + + net/minecraft/class_1071$class_8688 + + + net/minecraft/client/resources/data/TextureMetadataSection + net/minecraft/class_1084 + + + net/minecraft/class_12079$class_12080 + + + net/minecraft/util/ResourceLocation + net/minecraft/resources/ResourceLocation + net/minecraft/class_2960 + net/minecraft/resources/ResourceLocation + + + net/minecraft/class_7497 + + + net/minecraft/client/resources/IResource + net/minecraft/resources/IResource + net/minecraft/class_3298 + + func_199027_b + m_6679_ + m_215507_ + method_14482 + + + + net/minecraft/client/resources/IResourceManager + net/minecraft/resources/IResourceManager + net/minecraft/class_3300 + + method_14486 + + + func_199002_a + m_142591_ + method_14486 + + + + net/minecraft/util/StringUtils + net/minecraft/class_3544 + + func_76338_a + m_14406_ + method_15440 + + + + net/minecraft/scoreboard/ScoreObjective + net/minecraft/class_266 + + + net/minecraft/scoreboard/Scoreboard + net/minecraft/class_269 + + diff --git a/Bootstrap/FabricV1/build.gradle b/Bootstrap/FabricV1/build.gradle new file mode 100644 index 00000000..8e32a9b1 --- /dev/null +++ b/Bootstrap/FabricV1/build.gradle @@ -0,0 +1,4 @@ + +manifestLibraries { + add 'https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.fabricmc.fabric-loader/0.19.2.json' +} diff --git a/Bootstrap/FabricV1/src/main/java/customskinloader/bootstrap/fabric/v1/MixinConfigPlugin.java b/Bootstrap/FabricV1/src/main/java/customskinloader/bootstrap/fabric/v1/MixinConfigPlugin.java new file mode 100644 index 00000000..6ac82770 --- /dev/null +++ b/Bootstrap/FabricV1/src/main/java/customskinloader/bootstrap/fabric/v1/MixinConfigPlugin.java @@ -0,0 +1,47 @@ +package customskinloader.bootstrap.fabric.v1; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +public final class MixinConfigPlugin implements IMixinConfigPlugin { + @Override + public void onLoad(String mixinPackage) { + TransformerBootstrap.initialize(); + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + return true; + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + // This is the last stable point before postInitialise/validation, so reflected target injection is still visible downstream. + TransformerBootstrap.applyPendingMixinTargets(this); + } + + @Override + public List getMixins() { + return Collections.emptyList(); + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + TransformerBootstrap.transformTargetClass(targetClassName, targetClass); + } +} diff --git a/Bootstrap/FabricV1/src/main/java/customskinloader/bootstrap/fabric/v1/TransformerBootstrap.java b/Bootstrap/FabricV1/src/main/java/customskinloader/bootstrap/fabric/v1/TransformerBootstrap.java new file mode 100644 index 00000000..05c57b03 --- /dev/null +++ b/Bootstrap/FabricV1/src/main/java/customskinloader/bootstrap/fabric/v1/TransformerBootstrap.java @@ -0,0 +1,186 @@ +package customskinloader.bootstrap.fabric.v1; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import customskinloader.bootstrap.BootstrapLogger; +import customskinloader.bootstrap.installer.CommonJarInstaller; +import customskinloader.bootstrap.transformer.ClassTransformationReport; +import customskinloader.bootstrap.transformer.TransformerBootstrapSupport; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.impl.launch.FabricLauncherBase; +import org.apache.logging.log4j.Logger; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfig; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; +import org.spongepowered.asm.mixin.transformer.ClassInfo; +import org.spongepowered.asm.mixin.transformer.Config; + +final class TransformerBootstrap { + private static final Logger LOGGER = BootstrapLogger.LOGGER; + private static final TransformerBootstrapSupport SUPPORT = new TransformerBootstrapSupport(TransformerBootstrap.class, "intermediary"); + private static final Object LOCK = new Object(); + private static final List PENDING_MIXIN_TARGETS = new ArrayList(); + private static final String DUMMY_MIXIN = "customskinloader.bootstrap.fabric.v1.mixin.DummyMixin"; + + private TransformerBootstrap() { + } + + public static void initialize() { + LOGGER.info("Initializing CustomSkinLoader Bootstrap for Fabric"); + releaseRuntimeArtifacts(); + int targetCount = 0; + for (String targetClassName : SUPPORT.collectTargetClassNames()) { + registerMixinTarget(DUMMY_MIXIN, targetClassName.replace('/', '.')); + targetCount++; + } + LOGGER.info("Initialized CustomSkinLoader Bootstrap for Fabric with " + targetCount + " reflected Mixin target(s)"); + } + + private static void releaseRuntimeArtifacts() { + try { + java.nio.file.Path commonJar = CommonJarInstaller.releaseCommonJar(FabricLoader.getInstance().getGameDir(), "intermediary"); + FabricLauncherBase.getLauncher().addToClassPath(commonJar, "customskinloader."); + LOGGER.info("Added CustomSkinLoader Common jar to Fabric class path: " + BootstrapLogger.formatPath(commonJar)); + } catch (Exception exception) { + LOGGER.error("Failed to prepare CustomSkinLoader runtime artifacts for Fabric", exception); + throw new IllegalStateException("Failed to prepare CustomSkinLoader runtime artifacts for Fabric", exception); + } + } + + public static void registerMixinTarget(String mixinClassName, String targetClassName) { + synchronized (LOCK) { + PENDING_MIXIN_TARGETS.add(new PendingMixinTarget(mixinClassName, targetClassName)); + LOGGER.debug("Queued Fabric Mixin target " + targetClassName + " for " + mixinClassName); + } + } + + public static void applyPendingMixinTargets(IMixinConfigPlugin plugin) { + synchronized (LOCK) { + if (PENDING_MIXIN_TARGETS.isEmpty()) { + return; + } + + try { + IMixinConfig mixinConfig = findOwningMixinConfig(plugin); + int appliedTargetCount = 0; + Iterator iterator = PENDING_MIXIN_TARGETS.iterator(); + while (iterator.hasNext()) { + PendingMixinTarget pendingTarget = iterator.next(); + applyMixinTarget(mixinConfig, pendingTarget.mixinClassName, pendingTarget.targetClassName); + iterator.remove(); + appliedTargetCount++; + } + LOGGER.info("Applied " + appliedTargetCount + " reflected Fabric Mixin target(s)"); + } catch (ReflectiveOperationException exception) { + LOGGER.error("Failed to append reflected targets to the current Fabric mixin config", exception); + throw new IllegalStateException("Failed to append reflected targets to the current Fabric mixin config", exception); + } + } + } + + public static void transformTargetClass(String targetClassName, ClassNode targetClass) { + ClassTransformationReport report = SUPPORT.transformClassNode(targetClassName, targetClass); + ClassNode transformedNode = report.getTransformedClassNode(); + if (transformedNode != targetClass) { + TransformerBootstrapSupport.copyClassNode(transformedNode, targetClass); + } + if (report.isModified()) { + LOGGER.info("Transformed Fabric target " + targetClassName + " with " + report.getAppliedTransformerNames()); + } + } + + private static IMixinConfig findOwningMixinConfig(IMixinConfigPlugin plugin) throws ReflectiveOperationException { + Field allConfigsField = Config.class.getDeclaredField("allConfigs"); + allConfigsField.setAccessible(true); + + for (Object configHandle : ((Map) allConfigsField.get(null)).values()) { + IMixinConfig mixinConfig = ((Config) configHandle).getConfig(); + if (mixinConfig.getPlugin() == plugin) { + return mixinConfig; + } + } + + throw new IllegalStateException("Could not find the owning MixinConfig for " + plugin.getClass().getName()); + } + + @SuppressWarnings("unchecked") + private static void applyMixinTarget(IMixinConfig mixinConfig, String mixinClassName, String targetClassName) throws ReflectiveOperationException { + IMixinInfo mixinInfo = findMixinInfo(mixinConfig, mixinClassName); + String targetBinaryName = targetClassName.replace('/', '.'); + String targetInternalName = targetClassName.replace('.', '/'); + + List targetClassNames = (List) getField(mixinInfo, "targetClassNames"); + if (targetClassNames.contains(targetInternalName)) { + return; + } + + ClassInfo targetClassInfo = ClassInfo.forName(targetInternalName); + if (targetClassInfo == null) { + throw new IllegalStateException("Could not resolve target ClassInfo for " + targetInternalName); + } + + Method addMixin = ClassInfo.class.getDeclaredMethod("addMixin", Class.forName("org.spongepowered.asm.mixin.transformer.MixinInfo")); + addMixin.setAccessible(true); + addMixin.invoke(targetClassInfo, mixinInfo); + + List targetClasses = (List) getField(mixinInfo, "targetClasses"); + targetClasses.add(targetClassInfo); + targetClassNames.add(targetInternalName); + + Map> mixinMapping = (Map>) getField(mixinConfig, "mixinMapping"); + List mappedMixins = mixinMapping.computeIfAbsent(targetBinaryName, k -> new ArrayList()); + if (!mappedMixins.contains(mixinInfo)) { + mappedMixins.add(mixinInfo); + } + + ((Set) getField(mixinConfig, "unhandledTargets")).add(targetBinaryName); + } + + @SuppressWarnings("unchecked") + private static IMixinInfo findMixinInfo(IMixinConfig mixinConfig, String mixinClassName) throws ReflectiveOperationException { + for (IMixinInfo mixinInfo : (List) getField(mixinConfig, "mixins")) { + String currentMixinName = mixinInfo.getClassName(); + if (mixinClassName.equals(currentMixinName)) { + return mixinInfo; + } + } + + throw new IllegalStateException("Could not find mixin " + mixinClassName + " in the current config"); + } + + private static Object getField(Object instance, String fieldName) throws ReflectiveOperationException { + Field field = findField(instance.getClass(), fieldName); + field.setAccessible(true); + return field.get(instance); + } + + private static Field findField(Class type, String fieldName) throws NoSuchFieldException { + Class currentType = type; + while (currentType != null) { + try { + return currentType.getDeclaredField(fieldName); + } catch (NoSuchFieldException exception) { + currentType = currentType.getSuperclass(); + } + } + + throw new NoSuchFieldException(fieldName); + } + + private static final class PendingMixinTarget { + private final String mixinClassName; + private final String targetClassName; + + private PendingMixinTarget(String mixinClassName, String targetClassName) { + this.mixinClassName = mixinClassName; + this.targetClassName = targetClassName; + } + } +} diff --git a/Bootstrap/FabricV1/src/main/java/customskinloader/bootstrap/fabric/v1/mixin/DummyMixin.java b/Bootstrap/FabricV1/src/main/java/customskinloader/bootstrap/fabric/v1/mixin/DummyMixin.java new file mode 100644 index 00000000..b9f7193e --- /dev/null +++ b/Bootstrap/FabricV1/src/main/java/customskinloader/bootstrap/fabric/v1/mixin/DummyMixin.java @@ -0,0 +1,9 @@ +package customskinloader.bootstrap.fabric.v1.mixin; + +import org.spongepowered.asm.mixin.Mixin; + +// This placeholder mixin exists solely to provide a stable MixinInfo that we can retarget reflectively. +@Mixin(targets = "net.minecraft.client.main.Main") +public abstract class DummyMixin { + +} diff --git a/Bootstrap/FabricV1/src/main/resources/customskinloader.bootstrap.fabric.v1.mixins.json b/Bootstrap/FabricV1/src/main/resources/customskinloader.bootstrap.fabric.v1.mixins.json new file mode 100644 index 00000000..4e4c547d --- /dev/null +++ b/Bootstrap/FabricV1/src/main/resources/customskinloader.bootstrap.fabric.v1.mixins.json @@ -0,0 +1,12 @@ +{ + "required": true, + "package": "customskinloader.bootstrap.fabric.v1.mixin", + "plugin": "customskinloader.bootstrap.fabric.v1.MixinConfigPlugin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "DummyMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/Universal/src/main/resources/fabric.mod.json b/Bootstrap/FabricV1/src/main/resources/fabric.mod.json similarity index 69% rename from Universal/src/main/resources/fabric.mod.json rename to Bootstrap/FabricV1/src/main/resources/fabric.mod.json index ea1a3e96..b3908f5a 100644 --- a/Universal/src/main/resources/fabric.mod.json +++ b/Bootstrap/FabricV1/src/main/resources/fabric.mod.json @@ -1,9 +1,9 @@ { "schemaVersion": 1, - "id": "customskinloader", + "id": "customskinloader-bootstrap", "version": "${modFullVersion}", - "name": "CustomSkinLoader", + "name": "CustomSkinLoader Bootstrap", "description": "Custom Skin Loader for Minecraft", "authors": ["xfl03", "JLChnToZ", "ZekerZhayard"], "contact": { @@ -12,14 +12,9 @@ "sources": "https://github.com/xfl03/MCCustomSkinLoader" }, "license": "GPL-3.0-only", - + "environment": "client", "mixins": [ - "mixins.customskinloader.json" - ], - "accessWidener": "customskinloader.accesswidener", - - "depends": { - "minecraft": ">1.21.11" - } + "customskinloader.bootstrap.fabric.v1.mixins.json" + ] } diff --git a/Bootstrap/ForgeV1/build.gradle b/Bootstrap/ForgeV1/build.gradle new file mode 100644 index 00000000..d80379ad --- /dev/null +++ b/Bootstrap/ForgeV1/build.gradle @@ -0,0 +1,4 @@ + +manifestLibraries { + add 'https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.minecraftforge/14.23.5.2864.json' +} diff --git a/Bootstrap/ForgeV1/src/main/java/customskinloader/bootstrap/forge/v1/CallHook.java b/Bootstrap/ForgeV1/src/main/java/customskinloader/bootstrap/forge/v1/CallHook.java new file mode 100644 index 00000000..f776d869 --- /dev/null +++ b/Bootstrap/ForgeV1/src/main/java/customskinloader/bootstrap/forge/v1/CallHook.java @@ -0,0 +1,17 @@ +package customskinloader.bootstrap.forge.v1; + +import java.util.Map; + +import net.minecraftforge.fml.relauncher.IFMLCallHook; + +public class CallHook implements IFMLCallHook { + @Override + public void injectData(Map data) { + TransformerBootstrap.injectData(data); + } + + @Override + public Void call() { + return null; + } +} diff --git a/Bootstrap/ForgeV1/src/main/java/customskinloader/bootstrap/forge/v1/ClassTransformerAdapter.java b/Bootstrap/ForgeV1/src/main/java/customskinloader/bootstrap/forge/v1/ClassTransformerAdapter.java new file mode 100644 index 00000000..baaaa9bc --- /dev/null +++ b/Bootstrap/ForgeV1/src/main/java/customskinloader/bootstrap/forge/v1/ClassTransformerAdapter.java @@ -0,0 +1,15 @@ +package customskinloader.bootstrap.forge.v1; + +import net.minecraft.launchwrapper.IClassTransformer; + +public final class ClassTransformerAdapter implements IClassTransformer { + @Override + public byte[] transform(String name, String transformedName, byte[] basicClass) { + if (basicClass == null) { + return null; + } + + String className = transformedName != null && !transformedName.isEmpty() ? transformedName : name; + return TransformerBootstrap.transform(className.replace('.', '/'), basicClass); + } +} diff --git a/Forge/V1/src/main/java/customskinloader/forge/ForgePlugin.java b/Bootstrap/ForgeV1/src/main/java/customskinloader/bootstrap/forge/v1/LoadingPlugin.java similarity index 51% rename from Forge/V1/src/main/java/customskinloader/forge/ForgePlugin.java rename to Bootstrap/ForgeV1/src/main/java/customskinloader/bootstrap/forge/v1/LoadingPlugin.java index 47049216..2d95844b 100644 --- a/Forge/V1/src/main/java/customskinloader/forge/ForgePlugin.java +++ b/Bootstrap/ForgeV1/src/main/java/customskinloader/bootstrap/forge/v1/LoadingPlugin.java @@ -1,16 +1,16 @@ -package customskinloader.forge; - -import java.util.Map; +package customskinloader.bootstrap.forge.v1; import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; -@IFMLLoadingPlugin.Name("CustomSkinLoader") -@IFMLLoadingPlugin.SortingIndex(-10) -public class ForgePlugin implements IFMLLoadingPlugin { +import java.util.Map; +@IFMLLoadingPlugin.Name("CustomSkinLoaderBootstrap") +@IFMLLoadingPlugin.SortingIndex(1010) +@IFMLLoadingPlugin.TransformerExclusions("customskinloader.bootstrap.") +public final class LoadingPlugin implements IFMLLoadingPlugin { @Override public String[] getASMTransformerClass() { - return new String[]{"customskinloader.forge.loader.LaunchWrapper"};//LaunchWrapper + return new String[] { "customskinloader.bootstrap.forge.v1.ClassTransformerAdapter" }; } @Override @@ -20,17 +20,16 @@ public String getModContainerClass() { @Override public String getSetupClass() { - return null; + return "customskinloader.bootstrap.forge.v1.CallHook"; } @Override public void injectData(Map data) { - TransformerManager.isDevelopmentEnvironment = !(boolean) data.get("runtimeDeobfuscationEnabled"); + } @Override public String getAccessTransformerClass() { return null; } - } diff --git a/Bootstrap/ForgeV1/src/main/java/customskinloader/bootstrap/forge/v1/TransformerBootstrap.java b/Bootstrap/ForgeV1/src/main/java/customskinloader/bootstrap/forge/v1/TransformerBootstrap.java new file mode 100644 index 00000000..a42fdd1e --- /dev/null +++ b/Bootstrap/ForgeV1/src/main/java/customskinloader/bootstrap/forge/v1/TransformerBootstrap.java @@ -0,0 +1,68 @@ +package customskinloader.bootstrap.forge.v1; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; + +import customskinloader.bootstrap.BootstrapLogger; +import customskinloader.bootstrap.installer.CommonJarInstaller; +import customskinloader.bootstrap.transformer.ClassTransformationReport; +import customskinloader.bootstrap.transformer.TransformerBootstrapSupport; +import net.minecraft.launchwrapper.LaunchClassLoader; +import org.apache.logging.log4j.Logger; +import org.objectweb.asm.tree.ClassNode; + +final class TransformerBootstrap { + private static final Logger LOGGER = BootstrapLogger.LOGGER; + private static final TransformerBootstrapSupport SUPPORT = new TransformerBootstrapSupport(TransformerBootstrap.class, "srg"); + + private TransformerBootstrap() { + } + + public static void injectData(Map data) { + try { + Path runtimeDirectory = resolveRuntimeDirectory(data); + LOGGER.info("Initializing CustomSkinLoader Bootstrap for Forge v1 in " + BootstrapLogger.formatPath(runtimeDirectory)); + Path commonJar = CommonJarInstaller.releaseCommonJar(runtimeDirectory, "srg"); + resolveLaunchClassLoader(data).addURL(commonJar.toUri().toURL()); + LOGGER.info("Added CustomSkinLoader Common jar to LaunchClassLoader: " + BootstrapLogger.formatPath(commonJar)); + } catch (Exception exception) { + LOGGER.error("Failed to prepare CustomSkinLoader runtime jars", exception); + throw new IllegalStateException("Failed to prepare CustomSkinLoader runtime jars", exception); + } + } + + public static byte[] transform(String className, byte[] classBytecode) { + SUPPORT.ensureTransformersLoaded(); + + if (!SUPPORT.hasApplicableTransformers(className)) { + return classBytecode; + } + + ClassNode classNode = TransformerBootstrapSupport.toClassNode(classBytecode); + ClassTransformationReport report = SUPPORT.transformClassNode(className, classNode); + if (report.isModified()) { + LOGGER.info("Transformed Forge v1 target " + className + " with " + report.getAppliedTransformerNames()); + } + return report.isModified() ? report.getTransformedBytecode() : classBytecode; + } + + private static Path resolveRuntimeDirectory(Map data) { + Object mcLocation = data.get("mcLocation"); + if (mcLocation instanceof File) { + return ((File) mcLocation).toPath(); + } + + return Paths.get("."); + } + + private static LaunchClassLoader resolveLaunchClassLoader(Map data) { + Object classLoader = data.get("classLoader"); + if (classLoader instanceof LaunchClassLoader) { + return (LaunchClassLoader) classLoader; + } + + throw new IllegalStateException("Forge did not provide a LaunchClassLoader in injectData"); + } +} diff --git a/Bootstrap/ForgeV2/build.gradle b/Bootstrap/ForgeV2/build.gradle new file mode 100644 index 00000000..4db5c26c --- /dev/null +++ b/Bootstrap/ForgeV2/build.gradle @@ -0,0 +1,4 @@ + +manifestLibraries { + add 'https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.minecraftforge/36.2.42.json' +} diff --git a/Bootstrap/ForgeV2/src/main/java/customskinloader/bootstrap/forge/v2/BridgeModLocator.java b/Bootstrap/ForgeV2/src/main/java/customskinloader/bootstrap/forge/v2/BridgeModLocator.java new file mode 100644 index 00000000..05a3f373 --- /dev/null +++ b/Bootstrap/ForgeV2/src/main/java/customskinloader/bootstrap/forge/v2/BridgeModLocator.java @@ -0,0 +1,17 @@ +package customskinloader.bootstrap.forge.v2; + +import net.minecraftforge.fml.loading.FMLPaths; +import net.minecraftforge.fml.loading.moddiscovery.ModsFolderLocator; + +import customskinloader.bootstrap.util.ModLocatorUtils; + +public final class BridgeModLocator extends ModsFolderLocator { + public BridgeModLocator() { + ModLocatorUtils.initialize(this, ModsFolderLocator.class, FMLPaths.GAMEDIR.get(), "srg"); + } + + @Override + public String name() { + return ModLocatorUtils.LOCATOR_NAME; + } +} diff --git a/Bootstrap/ForgeV2/src/main/java/customskinloader/bootstrap/forge/v2/TransformationService.java b/Bootstrap/ForgeV2/src/main/java/customskinloader/bootstrap/forge/v2/TransformationService.java new file mode 100644 index 00000000..631528e5 --- /dev/null +++ b/Bootstrap/ForgeV2/src/main/java/customskinloader/bootstrap/forge/v2/TransformationService.java @@ -0,0 +1,61 @@ +package customskinloader.bootstrap.forge.v2; + +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Set; + +import cpw.mods.modlauncher.api.IEnvironment; +import cpw.mods.modlauncher.api.ITransformationService; +import cpw.mods.modlauncher.api.ITransformer; +import customskinloader.bootstrap.BootstrapLogger; +import net.minecraftforge.fml.loading.ModDirTransformerDiscoverer; +import org.apache.logging.log4j.Logger; + +public final class TransformationService implements ITransformationService { + private static final Logger LOGGER = BootstrapLogger.LOGGER; + + @Override + public String name() { + return "customskinloader-bootstrap"; + } + + @Override + public void initialize(IEnvironment environment) { + LOGGER.info("Initializing CustomSkinLoader Bootstrap transformation service"); + try { + List extraLocators = ModDirTransformerDiscoverer.getExtraLocators(); + Path servicePath = Paths.get(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).toAbsolutePath().normalize(); + if (!extraLocators.contains(servicePath)) { + extraLocators.add(servicePath); + LOGGER.info("Registered CustomSkinLoader Bootstrap service path as Forge extra locator: " + BootstrapLogger.formatPath(servicePath)); + } + } catch (NoSuchMethodError e) { // 1.13.2-25.0.216 ~ 1.14.4-28.1.64 + Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); + LOGGER.info("Using context ClassLoader fallback for legacy Forge transformation discovery"); + } catch (NoClassDefFoundError ignored) { // NeoForge + LOGGER.debug("Skipping Forge ModDirTransformerDiscoverer setup on NeoForge"); + } catch (URISyntaxException e) { + LOGGER.error("Unable to resolve CustomSkinLoader bootstrap service path", e); + throw new IllegalStateException("Unable to resolve CustomSkinLoader bootstrap service path", e); + } + } + + @Override + public void beginScanning(IEnvironment environment) { + + } + + @Override + public void onLoad(IEnvironment environment, Set otherServices) { + LOGGER.info("Loaded CustomSkinLoader Bootstrap transformation service with " + otherServices.size() + " other service(s)"); + } + + @Override + public List transformers() { + List transformers = TransformerBootstrap.buildTransformers(); + LOGGER.info("Provided " + transformers.size() + " CustomSkinLoader ModLauncher transformer(s)"); + return transformers; + } +} diff --git a/Bootstrap/ForgeV2/src/main/java/customskinloader/bootstrap/forge/v2/TransformerBootstrap.java b/Bootstrap/ForgeV2/src/main/java/customskinloader/bootstrap/forge/v2/TransformerBootstrap.java new file mode 100644 index 00000000..e3fd63ed --- /dev/null +++ b/Bootstrap/ForgeV2/src/main/java/customskinloader/bootstrap/forge/v2/TransformerBootstrap.java @@ -0,0 +1,85 @@ +package customskinloader.bootstrap.forge.v2; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import cpw.mods.modlauncher.Launcher; +import cpw.mods.modlauncher.api.ITransformer; +import cpw.mods.modlauncher.api.ITransformerVotingContext; +import cpw.mods.modlauncher.api.TransformerVoteResult; +import customskinloader.bootstrap.BootstrapLogger; +import customskinloader.bootstrap.transformer.ClassTransformationReport; +import customskinloader.bootstrap.transformer.TransformerBootstrapSupport; +import org.apache.logging.log4j.Logger; +import org.objectweb.asm.tree.ClassNode; + +final class TransformerBootstrap { + private static final Logger LOGGER = BootstrapLogger.LOGGER; + private static final TransformerBootstrapSupport SUPPORT = new TransformerBootstrapSupport(TransformerBootstrap.class, + Launcher.INSTANCE.environment().findLaunchHandler("forgeclient") + .map(handle -> handle.getClass().getName().startsWith("net.neoforged.")).orElse(false) ? "official" : "srg"); + + private TransformerBootstrap() { + } + + public static List buildTransformers() { + List transformers = new ArrayList<>(); + for (String targetClassName : SUPPORT.collectTargetClassNames()) { + transformers.add(new ModLauncherClassTransformer(targetClassName)); + LOGGER.debug("Created ModLauncher transformer for " + targetClassName); + } + + return Collections.unmodifiableList(transformers); + } + + private static ClassNode transformClassNode(String internalClassName, ClassNode inputClassNode) { + ClassTransformationReport report = SUPPORT.transformClassNode(internalClassName, inputClassNode); + if (!report.isModified() || report.getTransformedClassNode() == null) { + return inputClassNode; + } + + LOGGER.info("Transformed ModLauncher target " + internalClassName + " with " + report.getAppliedTransformerNames()); + return report.getTransformedClassNode(); + } + + private static final class ModLauncherClassTransformer implements ITransformer { + private final String targetClassName; + private final Set targets; + + private ModLauncherClassTransformer(String targetClassName) { + this.targetClassName = targetClassName; + + Set transformerTargets = new LinkedHashSet<>(); + transformerTargets.add(ITransformer.Target.targetClass(targetClassName)); + this.targets = Collections.unmodifiableSet(transformerTargets); + } + + @Override + public ClassNode transform(ClassNode input, ITransformerVotingContext context) { + return transformClassNode(this.targetClassName, input); + } + + @Override + public TransformerVoteResult castVote(ITransformerVotingContext context) { + return TransformerVoteResult.YES; + } + + @Override + public Set targets() { + return this.targets; + } + + // NeoForge + public cpw.mods.modlauncher.api.TargetType getTargetType() { + return cpw.mods.modlauncher.api.TargetType.CLASS; + } + + @Override + public String[] labels() { + return new String[]{"customskinloader:" + this.targetClassName.replace('/', '.')}; + } + } +} diff --git a/Bootstrap/ForgeV2/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService b/Bootstrap/ForgeV2/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService new file mode 100644 index 00000000..db4e046c --- /dev/null +++ b/Bootstrap/ForgeV2/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService @@ -0,0 +1 @@ +customskinloader.bootstrap.forge.v2.TransformationService diff --git a/Bootstrap/ForgeV2/src/main/resources/META-INF/services/net.minecraftforge.fml.loading.moddiscovery.IModLocator b/Bootstrap/ForgeV2/src/main/resources/META-INF/services/net.minecraftforge.fml.loading.moddiscovery.IModLocator new file mode 100644 index 00000000..b6684d4f --- /dev/null +++ b/Bootstrap/ForgeV2/src/main/resources/META-INF/services/net.minecraftforge.fml.loading.moddiscovery.IModLocator @@ -0,0 +1 @@ +customskinloader.bootstrap.forge.v2.BridgeModLocator diff --git a/Bootstrap/ForgeV2/src/main/resources/META-INF/services/net.minecraftforge.forgespi.locating.IModLocator b/Bootstrap/ForgeV2/src/main/resources/META-INF/services/net.minecraftforge.forgespi.locating.IModLocator new file mode 100644 index 00000000..b6684d4f --- /dev/null +++ b/Bootstrap/ForgeV2/src/main/resources/META-INF/services/net.minecraftforge.forgespi.locating.IModLocator @@ -0,0 +1 @@ +customskinloader.bootstrap.forge.v2.BridgeModLocator diff --git a/Bootstrap/NeoForgeV1/build.gradle b/Bootstrap/NeoForgeV1/build.gradle new file mode 100644 index 00000000..cb3b7b07 --- /dev/null +++ b/Bootstrap/NeoForgeV1/build.gradle @@ -0,0 +1,4 @@ + +manifestLibraries { + add 'https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.neoforged/20.5.21-beta.json' +} diff --git a/Bootstrap/NeoForgeV1/src/main/java/customskinloader/bootstrap/neoforge/v1/BridgeModLocator.java b/Bootstrap/NeoForgeV1/src/main/java/customskinloader/bootstrap/neoforge/v1/BridgeModLocator.java new file mode 100644 index 00000000..6dbdcebf --- /dev/null +++ b/Bootstrap/NeoForgeV1/src/main/java/customskinloader/bootstrap/neoforge/v1/BridgeModLocator.java @@ -0,0 +1,15 @@ +package customskinloader.bootstrap.neoforge.v1; + +import customskinloader.bootstrap.util.ModLocatorUtils; +import net.neoforged.fml.loading.FMLPaths; +import net.neoforged.fml.loading.moddiscovery.ModsFolderLocator; + +public final class BridgeModLocator extends ModsFolderLocator { + public BridgeModLocator() { + ModLocatorUtils.initialize(this, ModsFolderLocator.class, FMLPaths.GAMEDIR.get(), "official"); + } + + public String name() { + return ModLocatorUtils.LOCATOR_NAME; + } +} diff --git a/Bootstrap/NeoForgeV1/src/main/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModLocator b/Bootstrap/NeoForgeV1/src/main/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModLocator new file mode 100644 index 00000000..4e0bb1fa --- /dev/null +++ b/Bootstrap/NeoForgeV1/src/main/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModLocator @@ -0,0 +1 @@ +customskinloader.bootstrap.neoforge.v1.BridgeModLocator diff --git a/Bootstrap/NeoForgeV2/build.gradle b/Bootstrap/NeoForgeV2/build.gradle new file mode 100644 index 00000000..119c20c8 --- /dev/null +++ b/Bootstrap/NeoForgeV2/build.gradle @@ -0,0 +1,4 @@ + +manifestLibraries { + add 'https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.neoforged/26.1.2.59-beta.json' +} diff --git a/Bootstrap/NeoForgeV2/src/main/java/customskinloader/bootstrap/neoforge/v2/BridgeModLocator.java b/Bootstrap/NeoForgeV2/src/main/java/customskinloader/bootstrap/neoforge/v2/BridgeModLocator.java new file mode 100644 index 00000000..7020cd70 --- /dev/null +++ b/Bootstrap/NeoForgeV2/src/main/java/customskinloader/bootstrap/neoforge/v2/BridgeModLocator.java @@ -0,0 +1,15 @@ +package customskinloader.bootstrap.neoforge.v2; + +import customskinloader.bootstrap.util.ModLocatorUtils; +import net.neoforged.fml.loading.FMLPaths; +import net.neoforged.fml.loading.moddiscovery.locators.ModsFolderLocator; + +public final class BridgeModLocator extends ModsFolderLocator { + public BridgeModLocator() { + ModLocatorUtils.initialize(this, ModsFolderLocator.class, FMLPaths.GAMEDIR.get(), "official"); + } + + public String name() { + return ModLocatorUtils.LOCATOR_NAME; + } +} diff --git a/Bootstrap/NeoForgeV2/src/main/java/customskinloader/bootstrap/neoforge/v2/ClassProcessorProviderImpl.java b/Bootstrap/NeoForgeV2/src/main/java/customskinloader/bootstrap/neoforge/v2/ClassProcessorProviderImpl.java new file mode 100644 index 00000000..a5f4e6db --- /dev/null +++ b/Bootstrap/NeoForgeV2/src/main/java/customskinloader/bootstrap/neoforge/v2/ClassProcessorProviderImpl.java @@ -0,0 +1,16 @@ +package customskinloader.bootstrap.neoforge.v2; + +import customskinloader.bootstrap.BootstrapLogger; +import net.neoforged.neoforgespi.transformation.ClassProcessorProvider; +import org.apache.logging.log4j.Logger; + +public final class ClassProcessorProviderImpl implements ClassProcessorProvider { + private static final Logger LOGGER = BootstrapLogger.LOGGER; + + @Override + public void createProcessors(Context context, Collector collector) { + // Always register the bridge processor so late transformer registrations can still be observed. + LOGGER.info("Registering CustomSkinLoader Bootstrap NeoForge class processor"); + collector.add(TransformerBootstrap.createProcessor()); + } +} diff --git a/Bootstrap/NeoForgeV2/src/main/java/customskinloader/bootstrap/neoforge/v2/TransformerBootstrap.java b/Bootstrap/NeoForgeV2/src/main/java/customskinloader/bootstrap/neoforge/v2/TransformerBootstrap.java new file mode 100644 index 00000000..c885f8f0 --- /dev/null +++ b/Bootstrap/NeoForgeV2/src/main/java/customskinloader/bootstrap/neoforge/v2/TransformerBootstrap.java @@ -0,0 +1,72 @@ +package customskinloader.bootstrap.neoforge.v2; + +import java.util.Collections; +import java.util.Set; + +import customskinloader.bootstrap.BootstrapLogger; +import customskinloader.bootstrap.transformer.ClassTransformationReport; +import customskinloader.bootstrap.transformer.TransformerBootstrapSupport; +import net.neoforged.neoforgespi.transformation.ClassProcessor; +import net.neoforged.neoforgespi.transformation.ClassProcessorIds; +import net.neoforged.neoforgespi.transformation.ProcessorName; +import org.apache.logging.log4j.Logger; +import org.objectweb.asm.tree.ClassNode; + +final class TransformerBootstrap { + private static final Logger LOGGER = BootstrapLogger.LOGGER; + private static final TransformerBootstrapSupport SUPPORT = new TransformerBootstrapSupport(TransformerBootstrap.class, "official"); + private static final ProcessorName PROCESSOR_NAME = new ProcessorName("customskinloader", "bootstrap"); + + private TransformerBootstrap() { + } + + public static ClassProcessor createProcessor() { + SUPPORT.ensureTransformersLoaded(); + LOGGER.info("Created CustomSkinLoader Bootstrap NeoForge class processor"); + return new BootstrapClassProcessor(); + } + + private static ClassTransformationReport transformClassNode(String internalClassName, ClassNode inputClassNode) { + return SUPPORT.transformClassNode(internalClassName, inputClassNode); + } + + private static final class BootstrapClassProcessor implements ClassProcessor { + @Override + public ProcessorName name() { + return PROCESSOR_NAME; + } + + @Override + public Set runsAfter() { + // Run after the default simple-processor group so the shared framework sees post-Mixin bytecode. + return Collections.singleton(ClassProcessorIds.SIMPLE_PROCESSORS_GROUP); + } + + @Override + public boolean handlesClass(SelectionContext context) { + // The shared framework currently targets concrete classes only, so we skip synthetic empty definitions. + return !context.empty() && SUPPORT.hasApplicableTransformers(context.type().getInternalName()); + } + + @Override + public ComputeFlags processClass(TransformationContext context) { + ClassTransformationReport report = transformClassNode(context.type().getInternalName(), context.node()); + if (!report.isModified()) { + return ComputeFlags.NO_REWRITE; + } + + TransformerBootstrapSupport.copyClassNode(report.getTransformedClassNode(), context.node()); + for (String transformerName : report.getAppliedTransformerNames()) { + context.audit("customskinloader.bootstrap", transformerName); + } + + LOGGER.info("Transformed NeoForge target " + context.type().getInternalName() + " with " + report.getAppliedTransformerNames()); + return ComputeFlags.COMPUTE_FRAMES; + } + + @Override + public OrderingHint orderingHint() { + return OrderingHint.LATE; + } + } +} diff --git a/Bootstrap/NeoForgeV2/src/main/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModFileCandidateLocator b/Bootstrap/NeoForgeV2/src/main/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModFileCandidateLocator new file mode 100644 index 00000000..41826edd --- /dev/null +++ b/Bootstrap/NeoForgeV2/src/main/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModFileCandidateLocator @@ -0,0 +1 @@ +customskinloader.bootstrap.neoforge.v2.BridgeModLocator diff --git a/Bootstrap/NeoForgeV2/src/main/resources/META-INF/services/net.neoforged.neoforgespi.transformation.ClassProcessorProvider b/Bootstrap/NeoForgeV2/src/main/resources/META-INF/services/net.neoforged.neoforgespi.transformation.ClassProcessorProvider new file mode 100644 index 00000000..1b85321a --- /dev/null +++ b/Bootstrap/NeoForgeV2/src/main/resources/META-INF/services/net.neoforged.neoforgespi.transformation.ClassProcessorProvider @@ -0,0 +1 @@ +customskinloader.bootstrap.neoforge.v2.ClassProcessorProviderImpl diff --git a/Bootstrap/build.gradle b/Bootstrap/build.gradle new file mode 100644 index 00000000..36eac1d7 --- /dev/null +++ b/Bootstrap/build.gradle @@ -0,0 +1,48 @@ + +def bootstrapCoreProject = project(':Bootstrap:Core') +def commonJarTask = project(':Common').tasks.named('jar') + +subprojects { + dependencies { + compileOnly project(':Dummy:Bootstrap') + if (project != bootstrapCoreProject) compileOnly bootstrapCoreProject + } +} + +jar { + archiveBaseName = rootProject.ext.releaseArchiveBaseName + archiveVersion = rootProject.ext.modFullVersion + + manifest { + attributes( + "Automatic-Module-Name" : "customskinloader.bootstrap", + "Specification-Title" : "CustomSkinLoader", + "Specification-Vendor" : "xfl03", + "Specification-Version" : "1", + "Implementation-Title" : "CustomSkinLoader", + "Implementation-Version" : rootProject.ext.modFullVersion, + "Implementation-Vendor" : "xfl03", + "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + "FMLCorePlugin" : "customskinloader.bootstrap.forge.v1.LoadingPlugin" + ) + } + + // Build child module outputs before bundling them into the Bootstrap artifact. + dependsOn subprojects.collect { bundledProject -> + bundledProject.tasks.named('classes') + } + dependsOn commonJarTask + + // Package all leaf child module outputs into the Bootstrap jar. + from({ + subprojects.collect { bundledProject -> + bundledProject.sourceSets.main.output + } + }) + + into('META-INF/jar-jar') { + // Bundle the runtime payload with Bridge so Bridge owns releasing the Universal jar. + from(commonJarTask.flatMap { it.archiveFile }) + rename { 'CustomSkinLoader-Common.jar' } + } +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0f7356cb..1f476d0a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,157 +1,103 @@ -# Development and Contribution - -## Building -1. Run `./gradlew setupDecompWorkspace clean build` command. -1. The built jars are all in `${rootprojectDir}/build/libs` folder. - -*NOTICE: The jar file with specific Minecraft version is for the vanilla edition without Forge and Fabric. The json files are launcher profiles which only work when building with our CI server.* - -## Running and Testing -For now, CustomSkinLoader is unable to run under self development environment, so it needs to add to another development environment as a library. -There are the available versions in different environments below: - -| | Forge | Fabric | -|:-----------------------:|:-----------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------:| -| Runtime Environment | forge-1.8-11.14.0.1237 ~ 1.13.2-25.0.22
forge-1.13.2-25.0.42 ~ latest | fabric-loader-0.4.3+build.134 ~ latest
Minecraft 18w43b ~ latest
*fabric-api is not required* | -| Development Environment | ForgeGradle-2.1-SNAPSHOT ~ latest
forge-1.8-11.14.3.1503 ~ 1.12.2-14.23.5.2860
forge-1.13.2-25.0.198 ~ latest | fabric-loom-(?) ~ latest
fabric-loader-0.12.0 ~ latest
Minecraft 18w49a ~ latest
*fabric-api is not required* | - -### Preliminary steps for testing local builds -1. Create a new empty minecraft development environment. -1. Add below contents to `build.gradle`: - ```gradle - repositories { - maven { - url = "file:/${projectDir}/local-repo" - } - } - ``` -1. Create these folders in the new project directory, then copy the built jar and source jar into it: - ``` - Forge 1.8 ~ 1.16.5: ./local-repo/mods/CustomSkinLoader_ForgeV1/${version} - Forge 1.17.1 ~ 1.20.4 / NeoForge 1.20.1: ./local-repo/mods/CustomSkinLoader_ForgeV2/${version} - Forge 1.20.6 ~ latest / NeoForge 1.20.2 ~ latest: ./local-repo/mods/CustomSkinLoader_ForgeV3/${version} - Fabric: ./local-repo/mods/CustomSkinLoader_Fabric/${version} - ``` - *`${version}` should be repalced with something like `15.0-SNAPSHOT-00` manually* -1. Create a pom file in the same folder: - - Forge 1.8 ~ 1.16.5: `CustomSkinLoader_ForgeV1-${version}.pom` - ```xml - - - 4.0.0 - mods - CustomSkinLoader_ForgeV1 - - ${version} - - ``` - - Forge 1.17.1 ~ 1.20.4 / NeoForge 1.20.1: `CustomSkinLoader_ForgeV2-${version}.pom` - ```xml - - - 4.0.0 - mods - CustomSkinLoader_ForgeV2 - - ${version} - - ``` - - Forge 1.20.6 ~ latest / NeoForge 1.20.2 ~ latest: `CustomSkinLoader_ForgeV3-${version}.pom` - ```xml - - - 4.0.0 - mods - CustomSkinLoader_ForgeV3 - - ${version} - - ``` - - Fabric: `CustomSkinLoader_Fabric-${version}.pom` - ```xml - - - 4.0.0 - mods - CustomSkinLoader_Fabric - - ${version} - - ``` - -### For ForgeGradle 2.x ( forge-1.8-11.14.3.1503 ~ forge-1.12.2-14.23.5.2847 ) -1. Add below contents to `build.gradle`: - ```gradle - dependencies { - deobfCompile "mods:CustomSkinLoader_ForgeLegacy:15.0-SNAPSHOT-00" - } - - minecraft { - clientRunArgs += ["--tweakClass", "customskinloader.forge.ForgeDevTweaker", "--username", ""] - } - ``` -1. Run `./gradlew setupDecompWorkspace` command. -1. Then you can debug the mod in IDE or through `./gradlew runClient` command. - -### For ForgeGradle 3.x ~ latest ( forge-1.12.2-14.23.5.2851 ~ latest ) -1. Add below contents to `build.gradle`: - ```gradle - dependencies { - implementation fg.deobf("mods:CustomSkinLoader_ForgeLegacy:15.0-SNAPSHOT-00") // Only required for MinecraftForge 1.8 ~ 1.16.5 - implementation fg.deobf("mods:CustomSkinLoader_ForgeActive:15.0-SNAPSHOT-00") // Only required for MinecraftForge 1.17.1 ~ latest - } - - minecraft { - runs { - client { - args += ["--username", ""] - args += ["--tweakClass", "customskinloader.forge.ForgeDevTweaker"] // Only required for MinecraftForge 1.12.2 - } - } - } - ``` -1. Setup the development environment and run the game as usual. - -### For fabric-loom ( fabric-loader-0.12.0 ~ latest ) -1. Add below contents to `build.gradle`: - ```gradle - dependencies { - modImplementation "mods:CustomSkinLoader_Fabric:15.0-SNAPSHOT-00" - } - - loom.runs.client.programArgs "--username", "" - ``` -1. Setup the development environment and run the game as usual. - -## Depend on release builds -1. Check the latest version in https://littlesk.in/csl-latest . -1. Add below contents to `build.gradle`: - ```gradle - // Before Gradle 5.x - repositories { - ivy { - url = "https://csl.littleservice.cn/" - layout "pattern", { - artifact "[organisation]/[artifact]-[revision](-[classifier])(.[ext])" - } - } - } - - // After Gradle 6.x - repositories { - ivy { - url = "https://csl.littleservice.cn/" - metadataSources { - artifact() - } - patternLayout { - artifact "[organisation]/[artifact]-[revision](-[classifier])(.[ext])" - } - } - } - ``` -1. Follow the same steps in **Running and Testing**. - -## Developing -- CustomSkinLoader is based on forge-1.12.2-14.23.5.2768 currently, including Fabric edition. We use custom reobfuscation mappings such as [Fabric.tsrg](Fabric/Fabric.tsrg) and [mixin.tsrg](Fabric/mixin.tsrg) to generate different editions jar and Mixin reference jsons. -- Do not add other required mods. +# Contributing to CustomSkinLoader Universal + +Thanks for helping improve CustomSkinLoader. This branch is the Universal generation: one installable Bootstrap jar carries the shared Common runtime, remaps it for the active Minecraft mapping namespace, and applies loader-specific patches at launch time. A successful Java compile is useful, but changes that touch runtime loading, mappings, or transformers also need in-game verification. + +## Requirements + +- Git. +- A Java 25 JDK. Set `JAVA_HOME` if Java 25 is not the default `java` on your `PATH`. +- The Gradle wrapper from this repository. +- Local Minecraft test instances for the loaders and versions affected by your change. + +The build uses Java 25 tooling, while project sources are compiled for Java 8 compatibility unless a module explicitly provides multi-release sources such as `Common/src/main/java21`. + +## Build + +Use the wrapper instead of a system Gradle installation. + +Windows: + +```powershell +.\gradlew.bat clean build --stacktrace +``` + +Linux/macOS: + +```bash +./gradlew clean build --stacktrace +``` + +The user-facing mod artifact is: + +```text +Bootstrap/build/libs/CustomSkinLoader_Universal-.jar +``` + +`Common/build/libs/Common-.jar` is bundled into the Universal jar as `META-INF/jar-jar/CustomSkinLoader-Common.jar`. Do not publish or ask users to install the Common jar directly. + +## Project Layout + +- `Common`: shared CustomSkinLoader runtime, configuration, skin API loaders, cache, logging, and texture utilities. +- `Common/src/main/java21`: Java 21+ multi-release overrides for runtime behavior that benefits from newer JVM APIs. +- `Bootstrap`: assembles the installable Universal jar. +- `Bootstrap/Core`: runtime installer, remapper, mapping reader, transformer registry, and shared patch logic. +- `Bootstrap/FabricV1`: Fabric and Quilt-compatible bootstrap integration. +- `Bootstrap/ForgeV1`: legacy Forge coremod bootstrap. +- `Bootstrap/ForgeV2`: Forge ModLauncher bootstrap. +- `Bootstrap/NeoForgeV1` and `Bootstrap/NeoForgeV2`: NeoForge discovery and transformation bootstrap. +- `Dummy/Bootstrap` and `Dummy/Common`: compile-time stubs for Minecraft and loader APIs. Keep these minimal; they are not runtime implementations. +- `buildSrc`: local Gradle plugin code, including manifest library resolution from launcher metadata. +- `.github/workflows` and `.github/scripts`: CI, release, metadata, and publishing automation. + +## Development Guidelines + +- Keep changes scoped to the loader, mapping range, API loader, or Common behavior being modified. +- Preserve compatibility across the supported Minecraft and loader matrix unless the change is intentionally narrowing support. +- Prefer small, explicit compatibility branches over broad reflection or version checks when the affected API surface is known. +- Treat `Bootstrap/Core/src/main/resources/customskinloader/mapping.xml` and transformer patches as runtime-critical code. Document why a mapping range or target class changed. +- Do not edit generated Gradle output under `build/`, `.gradle/`, IDE metadata, or files generated by a local Minecraft test run. +- Keep `Dummy` classes limited to the members needed for compilation. Runtime behavior belongs in `Common` or `Bootstrap`, not in stubs. +- Update `README.md`, issue templates, or release metadata when a user-visible feature, supported loader, supported version, or install path changes. +- Follow the existing Java style in nearby files. The codebase intentionally avoids adding large abstractions where a focused compatibility patch is clearer. + +## Testing Checklist + +Before opening a pull request, run the relevant parts of this checklist: + +1. Run `.\gradlew.bat clean build --stacktrace` on Windows, or `./gradlew clean build --stacktrace` on Linux/macOS. +2. Install only `Bootstrap/build/libs/CustomSkinLoader_Universal-.jar` into a clean Minecraft instance. +3. If you changed `Common`, confirm the runtime payload is regenerated under `.minecraft/CustomSkinLoader/Core/CustomSkinLoader-Common.jar`. +4. If you changed `Bootstrap`, mappings, service descriptors, or transformer patches, test at least one Forge or NeoForge instance and one Fabric or Quilt-compatible instance. +5. If you changed a version range or mapping entry, test the oldest and newest Minecraft versions covered by that range. +6. Check `.minecraft/CustomSkinLoader/CustomSkinLoader.log` for loader, remapper, transformer, and skin loading errors. +7. For skin API, cache, or network changes, test a normal online profile and a cache/local-profile fallback path when possible. + +There is currently no standalone test suite that replaces in-game compatibility testing. + +## Pull Requests + +Please include: + +- A short summary of the behavior change. +- The Minecraft versions and loaders you tested. +- Any relevant `CustomSkinLoader.log` snippets for bootstrap, remapping, or skin loading issues. +- Screenshots or reproduction steps for visible texture/rendering fixes. +- Notes about compatibility risk when changing mappings, ASM patches, service descriptors, or build packaging. + +Keep pull requests focused. If a cleanup is unrelated to the bug or feature, send it separately so compatibility regressions are easier to review. + +## CI and Publishing + +Pull requests run the shared GitHub Actions build on `windows-latest` with PowerShell and Java 25. This is a CI choice; local builds should remain portable across Windows, Linux, and macOS. + +Publishing jobs generate the Universal jar plus metadata files from `build.properties`. Beta builds publish moving beta metadata, release builds publish release metadata and upload to distribution platforms. Object-storage upload is optional and is skipped when the required secrets are not configured. + +To generate publish metadata locally without uploading, first build the project, then run: + +```powershell +.\.github\scripts\publish-artifacts.ps1 -Channel Beta -LatestJsonName latest-beta.json -DetailJsonName detail-beta.json -SkipObjectStorage +``` + +## License + +By contributing, you agree that your contribution will be distributed under the repository license. Source code is licensed under GPL-3.0-only; see `LICENSE`. diff --git a/Common/CustomSkinLoader.jks b/Common/CustomSkinLoader.jks deleted file mode 100644 index 942569b6..00000000 Binary files a/Common/CustomSkinLoader.jks and /dev/null differ diff --git a/Common/build.gradle b/Common/build.gradle index e7056bf8..c5e43e11 100644 --- a/Common/build.gradle +++ b/Common/build.gradle @@ -1,5 +1,27 @@ -// This subproject is only to provide dependency for other subprojects. dependencies { - implementation project(':Dummy') + compileOnly project(':Dummy:Common') +} + +sourceSets { + java21 { + java.srcDir('src/main/java21') + compileClasspath += sourceSets.main.output + } +} + +tasks.named(sourceSets.java21.compileJavaTaskName, JavaCompile) { + dependsOn tasks.named('compileJava') +} + +jar { + dependsOn tasks.named(sourceSets.java21.classesTaskName) + + manifest { + attributes('Multi-Release': 'true') + } + + into('META-INF/versions/21') { + from(sourceSets.java21.output) + } } diff --git a/Common/build.properties b/Common/build.properties deleted file mode 100644 index 2c5a53ad..00000000 --- a/Common/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -forge_mc_version=1.12.2 -forge_version=14.23.5.2768 -mappings_version=stable_39 -# this subproject is only to provide dependency for other subprojects. -is_real_project=false diff --git a/Common/src/main/java/customskinloader/CustomSkinLoader.java b/Common/src/main/java/customskinloader/CustomSkinLoader.java index 97906c04..5c14fcfd 100644 --- a/Common/src/main/java/customskinloader/CustomSkinLoader.java +++ b/Common/src/main/java/customskinloader/CustomSkinLoader.java @@ -1,15 +1,10 @@ package customskinloader; import java.io.File; +import java.util.LinkedList; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; import com.google.common.collect.ImmutableMap; import com.google.gson.Gson; @@ -18,16 +13,16 @@ import com.mojang.authlib.minecraft.MinecraftProfileTexture; import customskinloader.config.Config; import customskinloader.config.SkinSiteProfile; +import customskinloader.loader.GameProfileLoader; import customskinloader.loader.ProfileLoader; import customskinloader.log.LogManager; import customskinloader.log.Logger; -import customskinloader.profile.DynamicSkullManager; import customskinloader.profile.ModelManager0; import customskinloader.profile.ProfileCache; import customskinloader.profile.UserProfile; -import customskinloader.utils.LIFOBlockingQueue; import customskinloader.utils.MinecraftUtil; import customskinloader.utils.TextureUtil; +import customskinloader.utils.ThreadPoolFactory; /** * Custom skin loader mod for Minecraft. @@ -50,24 +45,9 @@ public class CustomSkinLoader { public static final Config config = initConfig(); private static final ProfileCache profileCache = new ProfileCache(); - private static final DynamicSkullManager dynamicSkullManager = new DynamicSkullManager(); - public static final ExecutorService THREAD_POOL = new ThreadPoolExecutor(config.threadPoolSize, config.threadPoolSize, 1L, TimeUnit.MINUTES, new LIFOBlockingQueue<>(new LinkedBlockingDeque<>())); - - //Correct thread name in thread pool - private static final ThreadFactory defaultFactory = Executors.defaultThreadFactory(); - private static final ThreadFactory customFactory = r -> { - Thread t = defaultFactory.newThread(r); - if (r instanceof Thread) { - t.setName(((Thread) r).getName()); - } - return t; - }; - //Thread pool will discard oldest task when queue reaches 333 tasks - private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor( - config.threadPoolSize, config.threadPoolSize, 1L, TimeUnit.MINUTES, - new LinkedBlockingQueue<>(333), customFactory, new ThreadPoolExecutor.DiscardOldestPolicy() - ); + public static final ExecutorService THREAD_POOL = ThreadPoolFactory.create(config.threadPoolSize, false); + public static final ExecutorService PROFILE_THREAD_POOL = ThreadPoolFactory.create(config.loadlist.size() * config.threadPoolSize, true); public static void loadProfileTextures(Runnable runnable) { THREAD_POOL.execute(runnable); @@ -77,14 +57,15 @@ public static void loadProfileTextures(Runnable runnable) { public static UserProfile loadProfile(GameProfile gameProfile) { String username = TextureUtil.AuthlibField.GAME_PROFILE_NAME.get(gameProfile); String credential = MinecraftUtil.getCredential(gameProfile); + + String tempName = Thread.currentThread().getName(); + Thread.currentThread().setName(username + " <" + TextureUtil.AuthlibField.GAME_PROFILE_ID.get(gameProfile) + ">"); // Change Thread Name + // Fix: http://hopper.minecraft.net/crashes/minecraft/MCX-2773713 - if (username == null) { - logger.warning("Could not load profile: username is null."); - return new UserProfile(); + if (username == null || username.isEmpty() || username.equals(" ")) { + return ModelManager0.toUserProfile(GameProfileLoader.getTextures(TextureUtil.AuthlibField.GAME_PROFILE_PROPERTIES.get(gameProfile))); } - String tempName = Thread.currentThread().getName(); - Thread.currentThread().setName(username); // Change Thread Name UserProfile profile; if (profileCache.isReady(credential)) { logger.info("Cached profile will be used."); @@ -111,16 +92,19 @@ public static UserProfile loadProfile0(GameProfile gameProfile, boolean isSkull) String credential = MinecraftUtil.getCredential(gameProfile); profileCache.setLoading(credential, true); + long time = System.currentTimeMillis(); logger.info("Loading " + username + "'s profile."); if (config.loadlist == null || config.loadlist.isEmpty()) { logger.info("LoadList is Empty."); return null; } + int size = config.loadlist.size(); + LinkedList> profileGetters = new LinkedList<>(); UserProfile profile0 = new UserProfile(); - for (int i = 0; i < config.loadlist.size(); i++) { + for (int i = 0; i < size; i++) { SkinSiteProfile ssp = config.loadlist.get(i); - logger.info((i + 1) + "/" + config.loadlist.size() + " Try to load profile from '" + ssp.name + "'."); + logger.info((i + 1) + "/" + size + " Try to load profile from '" + ssp.name + "'."); if (ssp.type == null) { logger.info("The type of '" + ssp.name + "' is null."); continue; @@ -130,17 +114,27 @@ public static UserProfile loadProfile0(GameProfile gameProfile, boolean isSkull) logger.info("Type '" + ssp.type + "' is not defined."); continue; } - UserProfile profile = null; - try { - profile = loader.loadProfile(ssp, gameProfile); - } catch (Exception e) { - logger.warning("Exception occurs while loading."); - logger.warning(e); - if (e.getCause() != null) { - logger.warning("Caused By:"); - logger.warning(e.getCause()); + profileGetters.add(CompletableFuture.supplyAsync(() -> { + String tempName = Thread.currentThread().getName(); + Thread.currentThread().setName(username + " <" + TextureUtil.AuthlibField.GAME_PROFILE_ID.get(gameProfile) + "> (" + ssp.name + ")"); // Change Thread Name + UserProfile profile = null; + try { + profile = loader.loadProfile(ssp, gameProfile); + } catch (Exception e) { + logger.warning("Exception occurs while loading."); + logger.warning(e); + if (e.getCause() != null) { + logger.warning("Caused By:"); + logger.warning(e.getCause()); + } } - } + Thread.currentThread().setName(tempName); + return profile; + }, PROFILE_THREAD_POOL)); + } + + for (CompletableFuture profileGetter : profileGetters) { + UserProfile profile = profileGetter.join(); if (profile == null) { continue; } @@ -155,8 +149,9 @@ public static UserProfile loadProfile0(GameProfile gameProfile, boolean isSkull) break; } } + if (!profile0.isEmpty()) { - logger.info(username + "'s profile loaded."); + logger.info(username + "'s profile loaded. (" + (System.currentTimeMillis() - time) + "ms)"); if (!config.enableCape) { profile0.capeUrl = null; } @@ -165,7 +160,7 @@ public static UserProfile loadProfile0(GameProfile gameProfile, boolean isSkull) logger.info(profile0.toString(profileCache.getExpiry(credential))); return profile0; } - logger.info(username + "'s profile not found in load list."); + logger.info(username + "'s profile not found in load list. (" + (System.currentTimeMillis() - time) + "ms)"); if (config.enableLocalProfileCache) { UserProfile profile = profileCache.getLocalProfile(credential); @@ -192,7 +187,7 @@ public static Map loadPro //CustomSkinLoader needs username to load standard skin, if username not exist, only textures in NBT can be used //Authlib 3.11.50 makes empty username to " " if (username == null || username.isEmpty() || username.equals(" ") || credential == null) { - return dynamicSkullManager.getTexture(gameProfile); + return GameProfileLoader.getTextures(TextureUtil.AuthlibField.GAME_PROFILE_PROPERTIES.get(gameProfile)); } if (config.forceUpdateSkull ? profileCache.isReady(credential) : profileCache.isExist(credential)) { UserProfile profile = profileCache.getProfile(credential); @@ -209,7 +204,7 @@ public static Map loadPro if (config.forceUpdateSkull) { new Thread(loadThread).start(); } else { - threadPool.execute(loadThread); + THREAD_POOL.execute(loadThread); } } return INCOMPLETED; diff --git a/Common/src/main/java/customskinloader/config/Config.java b/Common/src/main/java/customskinloader/config/Config.java index bf3efceb..a9348bbd 100644 --- a/Common/src/main/java/customskinloader/config/Config.java +++ b/Common/src/main/java/customskinloader/config/Config.java @@ -25,7 +25,6 @@ public class Config { public List loadlist; //Function - public boolean enableDynamicSkull = true; public boolean enableTransparentSkin = true; public boolean forceLoadAllTextures = true; public boolean enableCape = true; @@ -77,7 +76,7 @@ public static Config loadConfig0() { config.loadExtraList(); config.updateLoadlist(); config.initLocalFolder(); - config.threadPoolSize = Math.max(config.threadPoolSize, 1); + config.threadPoolSize = Math.max(config.threadPoolSize, 2); if (config.enableCacheAutoClean && !config.enableLocalProfileCache) { try { FileUtils.deleteDirectory(HttpRequestUtil.CACHE_DIR); diff --git a/Common/src/main/java/customskinloader/fake/FakeCapeBuffer.java b/Common/src/main/java/customskinloader/fake/FakeCapeBuffer.java index 31d85bca..5aa1549e 100644 --- a/Common/src/main/java/customskinloader/fake/FakeCapeBuffer.java +++ b/Common/src/main/java/customskinloader/fake/FakeCapeBuffer.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.io.InputStream; -import java.util.concurrent.CompletableFuture; import java.util.function.BiPredicate; import java.util.function.Predicate; @@ -11,12 +10,10 @@ import customskinloader.fake.texture.FakeImage; import net.minecraft.client.Minecraft; import net.minecraft.resources.Identifier; -import net.minecraft.util.ResourceLocation; public class FakeCapeBuffer extends FakeSkinBuffer { - private static final CompletableFuture TEXTURE_ELYTRA_V2 = CompletableFuture.supplyAsync(() -> (Object) new ResourceLocation("minecraft", "textures/entity/equipment/wings/elytra.png")); - private static final CompletableFuture TEXTURE_ELYTRA_V3 = CompletableFuture.supplyAsync(() -> (Object) Identifier.fromNamespaceAndPath("minecraft", "textures/entity/equipment/wings/elytra.png")).exceptionally(t -> TEXTURE_ELYTRA_V2.join()); - private static final CompletableFuture TEXTURE_ELYTRA_V1 = CompletableFuture.supplyAsync(() -> (Object) new ResourceLocation("minecraft", "textures/entity/elytra.png")).exceptionally(t -> TEXTURE_ELYTRA_V3.join()); + private static final Identifier TEXTURE_ELYTRA_V1 = new Identifier("minecraft", "textures/entity/elytra.png"); + private static final Identifier TEXTURE_ELYTRA_V2 = new Identifier("minecraft", "textures/entity/equipment/wings/elytra.png"); private static int loadedGlobal = 0; private static FakeImage elytraImage; @@ -24,9 +21,9 @@ public class FakeCapeBuffer extends FakeSkinBuffer { private static FakeImage loadElytra(FakeImage originalImage) { loadedGlobal++; try { - Object resourceManager = FakeInterfaceManager.Minecraft_getResourceManager(Minecraft.getMinecraft()); - InputStream is = FakeInterfaceManager.IResource_getInputStream(FakeInterfaceManager.IResourceManager_getResource(resourceManager, TEXTURE_ELYTRA_V1.join()) - .orElseGet(() -> FakeInterfaceManager.IResourceManager_getResource(resourceManager, TEXTURE_ELYTRA_V2.join()).orElse(null))); + Object resourceManager = Minecraft.getInstance().getResourceManager(); + InputStream is = FakeInterfaceManager.IResource_getInputStream(FakeInterfaceManager.IResourceManager_getResource(resourceManager, TEXTURE_ELYTRA_V1) + .orElseGet(() -> FakeInterfaceManager.IResourceManager_getResource(resourceManager, TEXTURE_ELYTRA_V2).orElse(null))); if (is != null) { FakeImage image = originalImage.createImage(is); if (image.getWidth() % 64 != 0 || image.getHeight() % 32 != 0) { // wtf? diff --git a/Common/src/main/java/customskinloader/fake/FakeClientPlayer.java b/Common/src/main/java/customskinloader/fake/FakeClientPlayer.java deleted file mode 100644 index f4ff77b5..00000000 --- a/Common/src/main/java/customskinloader/fake/FakeClientPlayer.java +++ /dev/null @@ -1,98 +0,0 @@ -package customskinloader.fake; - -import java.util.Map; -import java.util.UUID; - -import com.google.common.collect.Maps; -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.minecraft.MinecraftProfileTexture; -import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type; -import customskinloader.CustomSkinLoader; -import customskinloader.utils.MinecraftUtil; -import net.minecraft.client.renderer.ThreadDownloadImageData; -import net.minecraft.client.renderer.texture.ITextureObject; -import net.minecraft.client.renderer.texture.SimpleTexture; -import net.minecraft.client.renderer.texture.TextureManager; -import net.minecraft.client.resources.DefaultPlayerSkin; -import net.minecraft.client.resources.SkinManager; -import net.minecraft.client.resources.SkinManager$SkinAvailableCallback; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.StringUtils; - -public class FakeClientPlayer { - /** - * Invoked from {@link SkinManager#loadSkin(MinecraftProfileTexture, MinecraftProfileTexture.Type, SkinManager$SkinAvailableCallback)} - */ - public static ThreadDownloadImageData putCache(ThreadDownloadImageData threaddownloadimagedata, SkinManager$SkinAvailableCallback skinAvailableCallback, ResourceLocation resourcelocation) { - if (skinAvailableCallback instanceof FakeClientPlayer.LegacyBuffer) {//Cache for client player - textureCache.put(resourcelocation, threaddownloadimagedata); - } - return threaddownloadimagedata; - } - - //For Legacy Skin - public static ThreadDownloadImageData getDownloadImageSkin(ResourceLocation resourceLocationIn, String username) { - //CustomSkinLoader.logger.debug("FakeClientPlayer/getDownloadImageSkin "+username); - TextureManager textman = MinecraftUtil.getTextureManager(); - ITextureObject ito = textman.getTexture(resourceLocationIn); - - if (ito == null || !(ito instanceof ThreadDownloadImageData)) { - //if Legacy Skin for username not loaded yet - SkinManager skinman = MinecraftUtil.getSkinManager(); - UUID offlineUUID = getOfflineUUID(username); - GameProfile offlineProfile = new GameProfile(offlineUUID, username); - - //Load Default Skin - ResourceLocation defaultSkin = DefaultPlayerSkin.getDefaultSkin(offlineUUID); - ITextureObject defaultSkinObj = new SimpleTexture(defaultSkin); - textman.loadTexture(resourceLocationIn, defaultSkinObj); - - //Load Skin from SkinManager - skinman.loadProfileTextures(offlineProfile, new LegacyBuffer(resourceLocationIn), false); - } - - if (ito instanceof ThreadDownloadImageData) - return (ThreadDownloadImageData) ito; - else - return null; - } - - public static ResourceLocation getLocationSkin(String username) { - //CustomSkinLoader.logger.debug("FakeClientPlayer/getLocationSkin "+username); - return new ResourceLocation("skins/legacy-" + StringUtils.stripControlCodes(username)); - } - - public static UUID getOfflineUUID(String username) { - return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes()); - } - - public static Map textureCache = Maps.newHashMap(); - - public static class LegacyBuffer implements SkinManager$SkinAvailableCallback { - ResourceLocation resourceLocationIn; - boolean loaded = false; - - public LegacyBuffer(ResourceLocation resourceLocationIn) { - CustomSkinLoader.logger.debug("Loading Legacy Texture (" + resourceLocationIn + ")"); - this.resourceLocationIn = resourceLocationIn; - } - - @Override - public void skinAvailable(Type typeIn, ResourceLocation location, MinecraftProfileTexture profileTexture) { - if (typeIn != Type.SKIN || loaded) - return; - - TextureManager textman = MinecraftUtil.getTextureManager(); - ITextureObject ito = textman.getTexture(location); - if (ito == null) - ito = textureCache.get(location); - if (ito == null) - return; - - loaded = true; - textman.loadTexture(resourceLocationIn, ito); - CustomSkinLoader.logger.debug("Legacy Texture (" + resourceLocationIn + ") Loaded as " + - ito.toString() + " (" + location + ")"); - } - } -} diff --git a/Common/src/main/java/customskinloader/fake/FakeMinecraftProfileTexture.java b/Common/src/main/java/customskinloader/fake/FakeMinecraftProfileTexture.java index dfdee5f6..82242140 100644 --- a/Common/src/main/java/customskinloader/fake/FakeMinecraftProfileTexture.java +++ b/Common/src/main/java/customskinloader/fake/FakeMinecraftProfileTexture.java @@ -2,12 +2,10 @@ import java.io.File; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import com.mojang.authlib.minecraft.MinecraftProfileTexture; -import customskinloader.CustomSkinLoader; import customskinloader.utils.HttpTextureUtil; public class FakeMinecraftProfileTexture extends MinecraftProfileTexture { @@ -15,7 +13,7 @@ public class FakeMinecraftProfileTexture extends MinecraftProfileTexture { private final HttpTextureUtil.HttpTextureInfo info; private final Map metadata; - private final CountDownLatch latch = new CountDownLatch(1); + private final CompletableFuture model = new CompletableFuture<>(); public FakeMinecraftProfileTexture(String url, Map metadata) { super(url, metadata); @@ -40,12 +38,8 @@ public String getMetadata(String key, boolean lock) { if (model != null) { return model; } else { - try { - if (lock && this.latch.await(60L * CustomSkinLoader.config.loadlist.size(), TimeUnit.SECONDS)) { - return this.getMetadata(key, false); - } - } catch (InterruptedException e) { - throw new RuntimeException(e); + if (lock && this.model.isDone()) { + return this.getMetadata(key, false); } } } @@ -56,7 +50,7 @@ public void setModel(String model) { if (this.metadata != null) { MODEL_CACHE.put(this.getHash(), model); this.metadata.put("model", model); - this.latch.countDown(); + this.model.complete(null); } } diff --git a/Common/src/main/java/customskinloader/fake/FakeSkinBuffer.java b/Common/src/main/java/customskinloader/fake/FakeSkinBuffer.java index cf99dac8..2c0fd886 100644 --- a/Common/src/main/java/customskinloader/fake/FakeSkinBuffer.java +++ b/Common/src/main/java/customskinloader/fake/FakeSkinBuffer.java @@ -6,17 +6,15 @@ import java.util.function.Function; import java.util.function.Predicate; +import com.mojang.blaze3d.platform.NativeImage; import customskinloader.CustomSkinLoader; -import customskinloader.fake.itf.FakeInterfaceManager; +import customskinloader.fake.itf.FakeHttpTextureProcessor; import customskinloader.fake.texture.FakeBufferedImage; import customskinloader.fake.texture.FakeImage; import customskinloader.fake.texture.FakeNativeImage; -import net.minecraft.client.renderer.IImageBuffer; -import net.minecraft.client.renderer.ThreadDownloadImageData; -import net.minecraft.client.renderer.texture.NativeImage; -import net.minecraft.client.renderer.texture.SkinTextureDownloader; +import net.minecraft.client.renderer.texture.HttpTexture; -public class FakeSkinBuffer implements IImageBuffer { +public class FakeSkinBuffer implements FakeHttpTextureProcessor { FakeImage image = null; /** @@ -29,9 +27,7 @@ public static NativeImage processLegacySkin(NativeImage image, String url) { FakeImage img = parseUserSkin0(new FakeNativeImage(image)); if (img instanceof FakeNativeImage) { - image = ((FakeNativeImage) img).getImage(); - FakeInterfaceManager.NativeImage_setFakeImage(image, img); - return image; + return ((FakeNativeImage) img).getImage(); } CustomSkinLoader.logger.warning("Failed to parseUserSkin(downloadAndRegisterSkin)."); @@ -40,11 +36,11 @@ public static NativeImage processLegacySkin(NativeImage image, String url) { /** * 19w38a ~ 24w45a - * Invoked from {@link ThreadDownloadImageData#loadTexture(InputStream)} + * Invoked from {@link HttpTexture#loadTexture(InputStream)} */ public static NativeImage processLegacySkin(NativeImage image, Runnable processTask, Function processLegacySkin) { - if (processTask instanceof IImageBuffer) { - return ((IImageBuffer) processTask).func_195786_a(image); + if (processTask instanceof FakeHttpTextureProcessor) { + return ((FakeHttpTextureProcessor) processTask).process(image); } else if (processLegacySkin != null) { return processLegacySkin.apply(image); } else { @@ -53,7 +49,8 @@ public static NativeImage processLegacySkin(NativeImage image, Runnable processT } //parseUserSkin for 1.13+ - public NativeImage func_195786_a(NativeImage image) { + @Override + public NativeImage process(NativeImage image) { if (image == null) return null; @@ -66,7 +63,8 @@ public NativeImage func_195786_a(NativeImage image) { } //parseUserSkin for 1.12.2- - public BufferedImage parseUserSkin(BufferedImage image) { + @Override + public BufferedImage process(BufferedImage image) { if (image == null) return null; @@ -125,7 +123,6 @@ public static FakeImage parseUserSkin0(FakeImage image) { setAreaDueToConfig(image, 16 * ratio, 48 * ratio, 32 * ratio, 64 * ratio);//Left Leg - 1 setAreaTransparent(image, 0 * ratio, 48 * ratio, 16 * ratio, 64 * ratio);//Left Leg - 2 - image.setRatio(ratio); return image; } @@ -146,7 +143,7 @@ public String judgeType() { public static String judgeType0(FakeImage image) { if (image == null) return "default"; - int ratio = image.getRatio(); + int ratio = image.getWidth() / 64; int bgColor = image.getRGBA(63 * ratio, 20 * ratio); /* * If background is transparent, all the pixels in ((54, 20), (55, 31)) areas is transparent, @@ -211,7 +208,8 @@ private static void setAreaDueToConfig(FakeImage image, int x0, int y0, int x1, setAreaOpaque(image, x0, y0, x1, y1); } - public void skinAvailable() { + @Override + public void onTextureDownloaded() { //A callback when skin loaded, nothing to do } diff --git a/Common/src/main/java/customskinloader/fake/FakeSkinManager.java b/Common/src/main/java/customskinloader/fake/FakeSkinManager.java index e4676e9c..3ea5c8a1 100644 --- a/Common/src/main/java/customskinloader/fake/FakeSkinManager.java +++ b/Common/src/main/java/customskinloader/fake/FakeSkinManager.java @@ -8,25 +8,20 @@ import java.util.concurrent.Executor; import java.util.function.Supplier; -import com.google.common.collect.ImmutableList; import com.mojang.authlib.GameProfile; import com.mojang.authlib.SignatureState; import com.mojang.authlib.minecraft.MinecraftProfileTexture; import com.mojang.authlib.minecraft.MinecraftProfileTextures; import com.mojang.authlib.minecraft.MinecraftSessionService; import com.mojang.authlib.properties.Property; +import com.mojang.blaze3d.platform.NativeImage; import customskinloader.CustomSkinLoader; -import customskinloader.fake.itf.FakeInterfaceManager; +import customskinloader.fake.itf.FakeHttpTextureProcessor; import customskinloader.profile.ModelManager0; import customskinloader.utils.HttpTextureUtil; -import net.minecraft.client.renderer.IImageBuffer; -import net.minecraft.client.renderer.texture.NativeImage; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.client.resources.SkinManager; -import net.minecraft.client.resources.SkinManager$1; import net.minecraft.client.resources.SkinManager$CacheKey; -import net.minecraft.client.resources.SkinManager$SkinAvailableCallback; -import net.minecraft.client.resources.SkinManager$TextureCache; import net.minecraft.server.Services; public class FakeSkinManager { @@ -54,39 +49,7 @@ public static void setSkinCacheDir(Path skinCacheDirectory) { /** * 1.20.1- - * Invoked from {@link SkinManager#loadSkin(MinecraftProfileTexture, MinecraftProfileTexture.Type, SkinManager$SkinAvailableCallback)} - * - * 23w31a+ - * Invoked from {@link SkinManager$TextureCache#registerTexture(MinecraftProfileTexture)} - */ - public static Object[] createThreadDownloadImageData(ImmutableList list, MinecraftProfileTexture profileTexture, MinecraftProfileTexture.Type textureType) { - Object[] params = list.toArray(); - if (profileTexture instanceof FakeMinecraftProfileTexture) { - FakeMinecraftProfileTexture fakeProfileTexture = (FakeMinecraftProfileTexture) profileTexture; - File cacheFile = fakeProfileTexture.getCacheFile(); - if (params.length == 4) { - if (params[3] instanceof Boolean) { // 24w46a+ - if ((Boolean) params[3]) { - FakeInterfaceManager.ResourceLocation_setTexture(params[0], fakeProfileTexture); - } - params[1] = cacheFile.toPath(); - } else { // 19w37a- - params[0] = cacheFile; - params[3] = new BaseBuffer((Runnable) params[3], textureType, fakeProfileTexture); - } - } else if (params.length == 5) { // 19w38a ~ 24w45a - params[0] = cacheFile; - params[3] = true; - params[4] = new BaseBuffer((Runnable) params[4], textureType, fakeProfileTexture); - } - } - return params; - } - - - /** - * 1.20.1- - * Invoked from {@link SkinManager#loadProfileTextures(GameProfile, SkinManager$SkinAvailableCallback, boolean)} + * Invoked from {@link SkinManager#registerSkins(GameProfile, SkinManager$SkinTextureCallback, boolean)} */ public static void loadProfileTextures(Runnable runnable) { CustomSkinLoader.loadProfileTextures(runnable); @@ -102,7 +65,7 @@ public static Executor loadProfileTextures(Executor executor) { /** * 1.20.1- - * Invoked from {@link SkinManager#func_210275_a(GameProfile, boolean, SkinManager$SkinAvailableCallback)} + * Invoked from {@link SkinManager#lambda$registerSkins$4(GameProfile, boolean, SkinManager$SkinTextureCallback)} */ public static Map getUserProfile(MinecraftSessionService sessionService, GameProfile profile, boolean requireSecure) { return ModelManager0.fromUserProfile(CustomSkinLoader.loadProfile(profile)); @@ -110,20 +73,7 @@ public static Map getUser /** * 1.20.1- - * Invoked from {@link SkinManager#func_210276_a(Map, SkinManager$SkinAvailableCallback)} - */ - public static void loadElytraTexture(SkinManager skinManager, Map map, SkinManager$SkinAvailableCallback skinAvailableCallback) { - for (int i = 2; i < MinecraftProfileTexture.Type.values().length; i++) { - MinecraftProfileTexture.Type type = MinecraftProfileTexture.Type.values()[i]; - if (map.containsKey(type)) { - skinManager.loadSkin(map.get(type), type, skinAvailableCallback); - } - } - } - - /** - * 1.20.1- - * Invoked from {@link SkinManager#loadSkinFromCache(GameProfile)} + * Invoked from {@link SkinManager#getInsecureSkinInformation(GameProfile)} */ public static Map loadSkinFromCache(GameProfile profile) { return CustomSkinLoader.loadProfileFromCache(profile); @@ -151,8 +101,8 @@ public static Object loadSkinFromCache(MinecraftSessionService sessionService, P return sessionService.unpackTextures(property); } - public static class BaseBuffer implements IImageBuffer { - private IImageBuffer buffer; + public static class BaseBuffer implements FakeHttpTextureProcessor { + private FakeHttpTextureProcessor buffer; private final Runnable callback; private final FakeMinecraftProfileTexture texture; @@ -168,19 +118,19 @@ public BaseBuffer(Runnable callback, MinecraftProfileTexture.Type type, FakeMine } @Override - public NativeImage func_195786_a(NativeImage image) { - return this.buffer instanceof FakeSkinBuffer ? this.buffer.func_195786_a(image) : image; + public NativeImage process(NativeImage image) { + return this.buffer instanceof FakeSkinBuffer ? this.buffer.process(image) : image; } @Override - public BufferedImage parseUserSkin(BufferedImage image) { - return this.buffer instanceof FakeSkinBuffer ? this.buffer.parseUserSkin(image) : image; + public BufferedImage process(BufferedImage image) { + return this.buffer instanceof FakeSkinBuffer ? this.buffer.process(image) : image; } @Override - public void skinAvailable() { + public void onTextureDownloaded() { if (this.buffer != null) { - this.buffer.skinAvailable(); + this.buffer.onTextureDownloaded(); if (this.buffer instanceof FakeSkinBuffer) { judgeType(this.texture, () -> ((FakeSkinBuffer) this.buffer).judgeType()); } diff --git a/Common/src/main/java/customskinloader/fake/itf/FakeHttpTextureProcessor.java b/Common/src/main/java/customskinloader/fake/itf/FakeHttpTextureProcessor.java new file mode 100644 index 00000000..e7499c55 --- /dev/null +++ b/Common/src/main/java/customskinloader/fake/itf/FakeHttpTextureProcessor.java @@ -0,0 +1,11 @@ +package customskinloader.fake.itf; + +import java.awt.image.BufferedImage; + +import com.mojang.blaze3d.platform.NativeImage; + +public interface FakeHttpTextureProcessor extends IFakeHttpTextureProcessor { + BufferedImage process(BufferedImage image); + NativeImage process(NativeImage image); + void onTextureDownloaded(); +} diff --git a/Common/src/main/java/customskinloader/fake/itf/FakeInterfaceManager.java b/Common/src/main/java/customskinloader/fake/itf/FakeInterfaceManager.java index 56ec3df4..343a3086 100644 --- a/Common/src/main/java/customskinloader/fake/itf/FakeInterfaceManager.java +++ b/Common/src/main/java/customskinloader/fake/itf/FakeInterfaceManager.java @@ -1,51 +1,33 @@ package customskinloader.fake.itf; +import java.awt.image.BufferedImage; import java.io.InputStream; import java.util.Optional; -import customskinloader.fake.FakeMinecraftProfileTexture; -import customskinloader.fake.texture.FakeNativeImage; -import net.minecraft.client.resources.IResource; -import net.minecraft.client.resources.IResourceManager; -import net.minecraft.util.ResourceLocation; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.Resource; public class FakeInterfaceManager { public static InputStream IResource_getInputStream(Object resource) { return ((IFakeIResource.V2) resource).open(); } - public static Optional IResourceManager_getResource(Object resourceManager, Object location) { - if (resourceManager instanceof IFakeIResourceManager.V2) { - return ((IFakeIResourceManager.V2) resourceManager).getResource((IFakeResourceLocation) location); - } - return ((IFakeIResourceManager.V1) resourceManager).getResource((ResourceLocation) location); - } - - public static IResourceManager Minecraft_getResourceManager(Object minecraft) { - return (IResourceManager) ((IFakeMinecraft) minecraft).func_195551_G(); - } - - public static int NativeImage_getPixel(Object nativeImage, int x, int y) { - return ((IFakeNativeImage) nativeImage).getPixel(x, y); - } - - public static void NativeImage_setPixel(Object nativeImage, int x, int y, int color) { - ((IFakeNativeImage) nativeImage).setPixel(x, y, color); + public static Optional IResourceManager_getResource(Object resourceManager, Object location) { + return ((IFakeIResourceManager.V2) resourceManager).getResource((Identifier) location); } - public static FakeNativeImage NativeImage_getFakeImage(Object nativeImage) { - return ((IFakeNativeImage) nativeImage).getFakeImage(); - } - - public static void NativeImage_setFakeImage(Object nativeImage, Object fakeImage) { - ((IFakeNativeImage) nativeImage).setFakeImage((FakeNativeImage) fakeImage); - } - - public static FakeMinecraftProfileTexture ResourceLocation_getTexture(Object location) { - return ((IFakeResourceLocation) location).getTexture(); + public static int NativeImage_getPixel(Object image, int x, int y) { + if (image instanceof IFakeNativeImage) { + return ((IFakeNativeImage) image).getPixel(x, y); + } + return ((BufferedImage) image).getRGB(x, y); } - public static void ResourceLocation_setTexture(Object location, FakeMinecraftProfileTexture texture) { - ((IFakeResourceLocation) location).setTexture(texture); + public static void NativeImage_setPixel(Object image, int x, int y, int color) { + if (image instanceof IFakeNativeImage) { + ((IFakeNativeImage) image).setPixel(x, y, color); + } else { + ((BufferedImage) image).setRGB(x, y, color); + } } } diff --git a/Common/src/main/java/customskinloader/fake/itf/IFakeHttpTextureProcessor.java b/Common/src/main/java/customskinloader/fake/itf/IFakeHttpTextureProcessor.java new file mode 100644 index 00000000..3c6e5780 --- /dev/null +++ b/Common/src/main/java/customskinloader/fake/itf/IFakeHttpTextureProcessor.java @@ -0,0 +1,8 @@ +package customskinloader.fake.itf; + +public interface IFakeHttpTextureProcessor extends Runnable { + @Override + default void run() { + ((FakeHttpTextureProcessor) this).onTextureDownloaded(); + } +} diff --git a/Common/src/main/java/customskinloader/fake/itf/IFakeIImageBuffer.java b/Common/src/main/java/customskinloader/fake/itf/IFakeIImageBuffer.java deleted file mode 100644 index 92f85c48..00000000 --- a/Common/src/main/java/customskinloader/fake/itf/IFakeIImageBuffer.java +++ /dev/null @@ -1,10 +0,0 @@ -package customskinloader.fake.itf; - -import net.minecraft.client.renderer.IImageBuffer; - -public interface IFakeIImageBuffer extends Runnable { - @Override - default void run() { - ((IImageBuffer) this).skinAvailable(); - } -} diff --git a/Common/src/main/java/customskinloader/fake/itf/IFakeIResource.java b/Common/src/main/java/customskinloader/fake/itf/IFakeIResource.java index 9d359572..16f8d290 100644 --- a/Common/src/main/java/customskinloader/fake/itf/IFakeIResource.java +++ b/Common/src/main/java/customskinloader/fake/itf/IFakeIResource.java @@ -2,21 +2,19 @@ import java.io.InputStream; -import net.minecraft.client.resources.IResource; +import net.minecraft.server.packs.resources.Resource; -/** {@link IResource} is no longer an interface since 22w14a */ +/** {@link Resource} is no longer an interface since 22w14a */ public interface IFakeIResource { - // 1.13.2 ~ 22w13a + // 22w13a- (1.18.2-) interface V1 { - default InputStream func_199027_b() { - return ((IResource) this).getInputStream(); - } + InputStream getInputStream(); } - // 22w14a+ + // 22w14a+ (1.19+) interface V2 { default InputStream open() { - return ((IFakeIResource.V1) this).func_199027_b(); + return ((IFakeIResource.V1) this).getInputStream(); } } } diff --git a/Common/src/main/java/customskinloader/fake/itf/IFakeIResourceManager.java b/Common/src/main/java/customskinloader/fake/itf/IFakeIResourceManager.java index a39f1d46..11268ede 100644 --- a/Common/src/main/java/customskinloader/fake/itf/IFakeIResourceManager.java +++ b/Common/src/main/java/customskinloader/fake/itf/IFakeIResourceManager.java @@ -2,25 +2,19 @@ import java.util.Optional; -import net.minecraft.client.resources.IResourceManager; -import net.minecraft.resources.IResource; -import net.minecraft.util.ResourceLocation; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.Resource; public interface IFakeIResourceManager { interface V1 { - // 1.13.2 ~ 22w13a - default IResource func_199002_a(ResourceLocation location) { - return (IResource) ((IResourceManager) this).getResource(location); - } - - // 22w14a ~ 25w44a - default Optional getResource(ResourceLocation location) { - return Optional.ofNullable(this.func_199002_a(location)); - } + // 22w13a- + Resource getResource(Identifier location); } interface V2 { - // 25w45a+ - Optional getResource(IFakeResourceLocation location); + // 22w14a+ + default Optional getResource(Identifier location) { + return Optional.ofNullable(((IFakeIResourceManager.V1) this).getResource(location)); + } } } diff --git a/Common/src/main/java/customskinloader/fake/itf/IFakeMinecraft.java b/Common/src/main/java/customskinloader/fake/itf/IFakeMinecraft.java deleted file mode 100644 index 841a4822..00000000 --- a/Common/src/main/java/customskinloader/fake/itf/IFakeMinecraft.java +++ /dev/null @@ -1,11 +0,0 @@ -package customskinloader.fake.itf; - -import net.minecraft.client.Minecraft; -import net.minecraft.resources.IResourceManager; - -public interface IFakeMinecraft { - // 1.13.2+ - default IResourceManager func_195551_G() { - return (IResourceManager) ((Minecraft) this).getResourceManager(); - } -} diff --git a/Common/src/main/java/customskinloader/fake/itf/IFakeNativeImage.java b/Common/src/main/java/customskinloader/fake/itf/IFakeNativeImage.java index 82d6f35f..3fbb90f4 100644 --- a/Common/src/main/java/customskinloader/fake/itf/IFakeNativeImage.java +++ b/Common/src/main/java/customskinloader/fake/itf/IFakeNativeImage.java @@ -1,18 +1,13 @@ package customskinloader.fake.itf; -import customskinloader.fake.texture.FakeNativeImage; -import net.minecraft.client.renderer.texture.NativeImage; +import com.mojang.blaze3d.platform.NativeImage; public interface IFakeNativeImage { default int getPixel(int x, int y) { - return ((NativeImage) this).func_195709_a(x, y); + return ((NativeImage) this).getPixelRGBA(x, y); } default void setPixel(int x, int y, int color) { - ((NativeImage) this).func_195700_a(x, y, color); + ((NativeImage) this).setPixelRGBA(x, y, color); } - - FakeNativeImage getFakeImage(); - - void setFakeImage(FakeNativeImage fakeImage); } diff --git a/Common/src/main/java/customskinloader/fake/itf/IFakeResourceLocation.java b/Common/src/main/java/customskinloader/fake/itf/IFakeResourceLocation.java deleted file mode 100644 index 8739bb62..00000000 --- a/Common/src/main/java/customskinloader/fake/itf/IFakeResourceLocation.java +++ /dev/null @@ -1,9 +0,0 @@ -package customskinloader.fake.itf; - -import customskinloader.fake.FakeMinecraftProfileTexture; - -public interface IFakeResourceLocation { - FakeMinecraftProfileTexture getTexture(); - - void setTexture(FakeMinecraftProfileTexture texture); -} diff --git a/Common/src/main/java/customskinloader/fake/texture/FakeBufferedImage.java b/Common/src/main/java/customskinloader/fake/texture/FakeBufferedImage.java index e0531f7c..0e96498b 100644 --- a/Common/src/main/java/customskinloader/fake/texture/FakeBufferedImage.java +++ b/Common/src/main/java/customskinloader/fake/texture/FakeBufferedImage.java @@ -10,7 +10,6 @@ public class FakeBufferedImage implements FakeImage { private BufferedImage image; private Graphics graphics; - private int ratio; public FakeBufferedImage(int width, int height) { this(new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)); @@ -34,16 +33,6 @@ public FakeImage createImage(InputStream is) throws IOException { return new FakeBufferedImage(ImageIO.read(is)); } - @Override - public int getRatio() { - return this.ratio; - } - - @Override - public void setRatio(int ratio) { - this.ratio = ratio; - } - public int getWidth() { return image.getWidth(); } diff --git a/Common/src/main/java/customskinloader/fake/texture/FakeHttpTexture.java b/Common/src/main/java/customskinloader/fake/texture/FakeHttpTexture.java new file mode 100644 index 00000000..602cca79 --- /dev/null +++ b/Common/src/main/java/customskinloader/fake/texture/FakeHttpTexture.java @@ -0,0 +1,109 @@ +package customskinloader.fake.texture; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.nio.file.Path; +import java.util.AbstractMap; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; + +import com.mojang.authlib.minecraft.MinecraftProfileTexture; +import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.datafixers.util.Function4; +import customskinloader.fake.FakeCapeBuffer; +import customskinloader.fake.FakeMinecraftProfileTexture; +import customskinloader.fake.FakeSkinBuffer; +import customskinloader.fake.FakeSkinManager; +import customskinloader.fake.itf.FakeHttpTextureProcessor; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.HttpTexture; +import net.minecraft.client.renderer.texture.SimpleTexture; +import net.minecraft.client.renderer.texture.TextureContents; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.ResourceManager; + +public class FakeHttpTexture { + public static class V1 extends HttpTexture { + // 19w37a- + public V1(File cacheFileIn, String imageUrlIn, Identifier textureResourceLocation, FakeHttpTextureProcessor processor, MinecraftProfileTexture texture, MinecraftProfileTexture.Type textureType) { + super(setCacheFile(cacheFileIn, texture), imageUrlIn, textureResourceLocation, (FakeHttpTextureProcessor) setProcessor(processor, texture, textureType)); + } + + // 19w38a ~ 24w45a + public V1(File cacheFileIn, String imageUrlIn, Identifier textureResourceLocation, boolean isSkin, Runnable processor, MinecraftProfileTexture texture, MinecraftProfileTexture.Type textureType) { + super(setCacheFile(cacheFileIn, texture), imageUrlIn, textureResourceLocation, texture instanceof FakeMinecraftProfileTexture || isSkin, setProcessor(processor, texture, textureType)); + } + + @Override + public void upload(BufferedImage bufferedImageIn) { + // TextureID won't be regenerated when changing resource packs before 1.12.2 + this.uploaded = false; + super.upload(bufferedImageIn); + } + + private static File setCacheFile(File cacheFile, MinecraftProfileTexture texture) { + return texture instanceof FakeMinecraftProfileTexture ? ((FakeMinecraftProfileTexture) texture).getCacheFile() : cacheFile; + } + + private static Runnable setProcessor(Runnable processor, MinecraftProfileTexture texture, MinecraftProfileTexture.Type textureType) { + return texture instanceof FakeMinecraftProfileTexture ? new FakeSkinManager.BaseBuffer(processor, textureType, (FakeMinecraftProfileTexture) texture) : processor; + } + } + + public static class V2 extends SimpleTexture { + private static final String DOMAIN = "customskinloader:"; + + private FakeCapeBuffer buffer; + private FakeNativeImage image; + + public static Function> createTexture(Function> function, Identifier location, boolean isSkin) { + return isSkin ? _image -> { + if (location.toString().startsWith(DOMAIN)) { + return CompletableFuture.completedFuture(new AbstractMap.SimpleEntry<>(_image, function.apply(_image))); + } + return function.apply(_image); + } : _image -> function.apply(_image).thenApply(_location -> { + Minecraft.getInstance().getTextureManager().registerAndLoad(location, new V2(location, copyImage(new FakeNativeImage(_image)))); + return _location; + }); + } + + public V2(Identifier location, FakeNativeImage image) { + super(location); + this.buffer = new FakeCapeBuffer(); + this.image = image; + } + + @Override + public TextureContents loadContents(ResourceManager resourceManager) { + this.image = (FakeNativeImage) this.buffer.parseUserSkin(this.image); + return new TextureContents(copyImage(this.image).getImage(), null); + } + + /** + * Protects cached NativeImage instances from external interference (e.g. accidental close operations). All lifecycle management is handled internally by this cache. + */ + private static FakeNativeImage copyImage(FakeNativeImage from) { + FakeNativeImage local = (FakeNativeImage) from.createImage(from.getWidth(), from.getHeight()); + local.copyImageData(from); + return local; + } + + // 24w46a+ + public static CompletableFuture downloadAndRegisterSkin( + Function4>>> downloadAndRegisterSkin, Identifier location, Path cacheFile, String imageUrl, boolean isSkin, MinecraftProfileTexture texture) { + if (isSkin && texture instanceof FakeMinecraftProfileTexture) { + location = new Identifier(DOMAIN, location.toString().split(":")[1]); + return downloadAndRegisterSkin.apply(location, setCacheFile(cacheFile, texture), imageUrl, isSkin).thenApply(entry -> { + FakeSkinManager.BaseBuffer.judgeType((FakeMinecraftProfileTexture) texture, () -> FakeSkinBuffer.judgeType0(new FakeNativeImage(entry.getKey()))); + return entry.getValue(); + }).thenCompose(f -> f); + } + return downloadAndRegisterSkin.apply(location, setCacheFile(cacheFile, texture), imageUrl, isSkin); + } + + private static Path setCacheFile(Path cacheFile, MinecraftProfileTexture texture) { + return texture instanceof FakeMinecraftProfileTexture ? ((FakeMinecraftProfileTexture) texture).getCacheFile().toPath() : cacheFile; + } + } +} diff --git a/Common/src/main/java/customskinloader/fake/texture/FakeImage.java b/Common/src/main/java/customskinloader/fake/texture/FakeImage.java index 01992676..7614865c 100644 --- a/Common/src/main/java/customskinloader/fake/texture/FakeImage.java +++ b/Common/src/main/java/customskinloader/fake/texture/FakeImage.java @@ -8,10 +8,6 @@ public interface FakeImage { FakeImage createImage(InputStream is) throws IOException; - int getRatio(); - - void setRatio(int ratio); - int getWidth(); int getHeight(); diff --git a/Common/src/main/java/customskinloader/fake/texture/FakeNativeImage.java b/Common/src/main/java/customskinloader/fake/texture/FakeNativeImage.java index 496baa65..c491a2e5 100644 --- a/Common/src/main/java/customskinloader/fake/texture/FakeNativeImage.java +++ b/Common/src/main/java/customskinloader/fake/texture/FakeNativeImage.java @@ -3,11 +3,10 @@ import java.io.InputStream; import customskinloader.fake.itf.FakeInterfaceManager; -import net.minecraft.client.renderer.texture.NativeImage; +import com.mojang.blaze3d.platform.NativeImage; public class FakeNativeImage implements FakeImage { private NativeImage image; - private int ratio; public FakeNativeImage(int width, int height) { this(new NativeImage(width, height, true)); @@ -26,25 +25,15 @@ public FakeImage createImage(int width, int height) { } public FakeImage createImage(InputStream is) { - return new FakeNativeImage(NativeImage.func_195713_a(is)); - } - - @Override - public int getRatio() { - return this.ratio; - } - - @Override - public void setRatio(int ratio) { - this.ratio = ratio; + return new FakeNativeImage(NativeImage.read(is)); } public int getWidth() { - return image.func_195702_a(); + return image.getWidth(); } public int getHeight() { - return image.func_195714_b(); + return image.getHeight(); } public int getRGBA(int x, int y) { @@ -57,15 +46,15 @@ public void setRGBA(int x, int y, int rgba) { public void copyImageData(FakeImage image) { if (!(image instanceof FakeNativeImage)) return; - this.image.func_195703_a(((FakeNativeImage) image).getImage()); + this.image.copyFrom(((FakeNativeImage) image).getImage()); } public void fillArea(int x0, int y0, int width, int height) { - image.func_195715_a(x0, y0, width, height, 0); + image.fillRect(x0, y0, width, height, 0); } public void copyArea(int x0, int y0, int dx, int dy, int width, int height, boolean reversex, boolean reversey) { - image.func_195699_a(x0, y0, dx, dy, width, height, reversex, reversey); + image.copyRect(x0, y0, dx, dy, width, height, reversex, reversey); } public void close() { diff --git a/Common/src/main/java/customskinloader/fake/texture/FakeThreadDownloadImageData.java b/Common/src/main/java/customskinloader/fake/texture/FakeThreadDownloadImageData.java deleted file mode 100644 index a2701732..00000000 --- a/Common/src/main/java/customskinloader/fake/texture/FakeThreadDownloadImageData.java +++ /dev/null @@ -1,75 +0,0 @@ -package customskinloader.fake.texture; - -import java.util.concurrent.CompletableFuture; -import java.util.function.Function; - -import customskinloader.fake.FakeCapeBuffer; -import customskinloader.fake.FakeMinecraftProfileTexture; -import customskinloader.fake.FakeSkinBuffer; -import customskinloader.fake.FakeSkinManager; -import customskinloader.fake.itf.FakeInterfaceManager; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.texture.NativeImage; -import net.minecraft.client.renderer.texture.SimpleTexture; -import net.minecraft.client.renderer.texture.TextureContents; -import net.minecraft.client.resources.IResourceManager; -import net.minecraft.resources.Identifier; -import net.minecraft.util.ResourceLocation; - -// 24w46a+ -public class FakeThreadDownloadImageData extends SimpleTexture { - private FakeCapeBuffer buffer; - private FakeNativeImage image; - - public static Function> createTextureV1(Function> function, ResourceLocation location, boolean bl) { - return createTexture(function, _image -> _location -> { - Minecraft.getMinecraft().getTextureManager().registerAndLoad(location, new FakeThreadDownloadImageData(location, copyImage(new FakeNativeImage(_image)))); - return _location; - }, location, bl); - } - - public static Function> createTextureV2(Function> function, Identifier identifier, boolean bl) { - return createTexture(function, _image -> _location -> { - Minecraft.getMinecraft().getTextureManager().registerAndLoad(identifier, new FakeThreadDownloadImageData(identifier, copyImage(new FakeNativeImage(_image)), null)); - return _location; - }, identifier, bl); - } - - public static Function> createTexture(Function> function, Function> cape, Object location, boolean bl) { - return bl ? _image -> { - FakeMinecraftProfileTexture texture = FakeInterfaceManager.ResourceLocation_getTexture(location); - FakeNativeImage image = FakeInterfaceManager.NativeImage_getFakeImage(_image); - if (texture != null && image != null) { - FakeSkinManager.BaseBuffer.judgeType(texture, () -> FakeSkinBuffer.judgeType0(image)); - } - return function.apply(_image); - } : _image -> function.apply(_image).thenApply(cape.apply(_image)); - } - - public FakeThreadDownloadImageData(ResourceLocation location, FakeNativeImage image) { - super(location); - this.buffer = new FakeCapeBuffer(); - this.image = image; - } - - public FakeThreadDownloadImageData(Identifier identifier, FakeNativeImage image, Object o) { - super(identifier); - this.buffer = new FakeCapeBuffer(); - this.image = image; - } - - @Override - public TextureContents loadContents(IResourceManager resourceManager) { - this.image = (FakeNativeImage) this.buffer.parseUserSkin(this.image); - return new TextureContents(copyImage(this.image).getImage(), null); - } - - /** - * Protects cached NativeImage instances from external interference (e.g. accidental close operations). All lifecycle management is handled internally by this cache. - */ - private static FakeNativeImage copyImage(FakeNativeImage from) { - FakeNativeImage local = (FakeNativeImage) from.createImage(from.getWidth(), from.getHeight()); - local.copyImageData(from); - return local; - } -} diff --git a/Common/src/main/java/customskinloader/loader/GameProfileLoader.java b/Common/src/main/java/customskinloader/loader/GameProfileLoader.java index 1577dcf7..234a35c5 100644 --- a/Common/src/main/java/customskinloader/loader/GameProfileLoader.java +++ b/Common/src/main/java/customskinloader/loader/GameProfileLoader.java @@ -61,7 +61,7 @@ public void updateSkinSiteProfile(SkinSiteProfile ssp) { @Override public UserProfile loadProfile(SkinSiteProfile ssp, GameProfile gameProfile) throws Exception { - Map map = getTextures(Iterables.getFirst(TextureUtil.AuthlibField.GAME_PROFILE_PROPERTIES.get(gameProfile).get("textures"), null)); + Map map = getTextures(TextureUtil.AuthlibField.GAME_PROFILE_PROPERTIES.get(gameProfile)); if (!map.isEmpty()) { CustomSkinLoader.logger.info("Default profile will be used."); return ModelManager0.toUserProfile(map); @@ -86,7 +86,8 @@ public void init(SkinSiteProfile ssp) { } public static final Gson GSON = new GsonBuilder().registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).registerTypeAdapter(PropertyMap.class, new PropertyMap.Serializer()).create(); - public static Map getTextures(Property textureProperty) { + public static Map getTextures(PropertyMap map) { + Property textureProperty = Iterables.getFirst(map.get("textures"), null); if (textureProperty == null) { return Maps.newHashMap(); } diff --git a/Common/src/main/java/customskinloader/loader/MojangAPILoader.java b/Common/src/main/java/customskinloader/loader/MojangAPILoader.java index bac7897c..16d3e9dd 100644 --- a/Common/src/main/java/customskinloader/loader/MojangAPILoader.java +++ b/Common/src/main/java/customskinloader/loader/MojangAPILoader.java @@ -88,7 +88,7 @@ public UserProfile loadProfile(SkinSiteProfile ssp, GameProfile gameProfile) { } PropertyMap propertyMap = TextureUtil.AuthlibField.MINECRAFT_PROFILE_PROPERTIES_RESPONSE_PROPERTIES.get(fillProfile(ssp.sessionRoot, newProfile)); if (propertyMap != null) { - Map map = GameProfileLoader.getTextures(Iterables.getFirst(propertyMap.get("textures"), null)); + Map map = GameProfileLoader.getTextures(propertyMap); if (!map.isEmpty()) { return ModelManager0.toUserProfile(map); } diff --git a/Common/src/main/java/customskinloader/loader/jsonapi/ElyByAPI.java b/Common/src/main/java/customskinloader/loader/jsonapi/ElyByAPI.java index 4945de12..3c7945cb 100644 --- a/Common/src/main/java/customskinloader/loader/jsonapi/ElyByAPI.java +++ b/Common/src/main/java/customskinloader/loader/jsonapi/ElyByAPI.java @@ -56,9 +56,30 @@ public String getRoot() { } } + public static class GlitchlessGames extends JsonAPILoader.DefaultProfile { + public GlitchlessGames(JsonAPILoader loader) { + super(loader); + } + + @Override + public String getName() { + return "GlitchlessGames"; + } + + @Override + public int getPriority() { + return 700; + } + + @Override + public String getRoot() { + return "https://games.glitchless.ru/api/minecraft/users/profiles/textures/?nickname="; + } + } + @Override public List getDefaultProfiles(JsonAPILoader loader) { - return Lists.newArrayList(new ElyBy(loader), new TLauncher(loader)); + return Lists.newArrayList(new ElyBy(loader), new TLauncher(loader), new GlitchlessGames(loader)); } @Override diff --git a/Common/src/main/java/customskinloader/loader/jsonapi/GlitchlessAPI.java b/Common/src/main/java/customskinloader/loader/jsonapi/GlitchlessAPI.java deleted file mode 100644 index 3a2c78fa..00000000 --- a/Common/src/main/java/customskinloader/loader/jsonapi/GlitchlessAPI.java +++ /dev/null @@ -1,64 +0,0 @@ -package customskinloader.loader.jsonapi; - -import java.util.List; -import java.util.Map; - -import com.google.common.collect.Lists; -import com.google.gson.Gson; -import com.mojang.authlib.minecraft.MinecraftProfileTexture; -import customskinloader.loader.JsonAPILoader; -import customskinloader.plugin.ICustomSkinLoaderPlugin; -import customskinloader.profile.ModelManager0; -import customskinloader.profile.UserProfile; - -public class GlitchlessAPI implements JsonAPILoader.IJsonAPI { - - public static class GlitchlessGames extends JsonAPILoader.DefaultProfile { - public GlitchlessGames(JsonAPILoader loader) { - super(loader); - } - - @Override - public String getName() { - return "GlitchlessGames"; - } - - @Override - public int getPriority() { - return 700; - } - - @Override - public String getRoot() { - return "https://games.glitchless.ru/api/minecraft/users/profiles/textures/?nickname="; - } - } - - @Override - public List getDefaultProfiles(JsonAPILoader loader) { - return Lists.newArrayList(new GlitchlessGames(loader)); - } - - @Override - public String toJsonUrl(String root, String username) { - return root + username; - } - - @Override - public UserProfile toUserProfile(String root, String json, boolean local) { - GlitchlessApiResponse result = new Gson().fromJson(json, GlitchlessApiResponse.class); - if (!result.textures.containsKey(MinecraftProfileTexture.Type.SKIN)) - return null; - - return ModelManager0.toUserProfile(result.textures); - } - - @Override - public String getName() { - return "GlitchlessAPI"; - } - - public static class GlitchlessApiResponse { - protected Map textures; - } -} diff --git a/Common/src/main/java/customskinloader/loader/jsonapi/UniSkinAPI.java b/Common/src/main/java/customskinloader/loader/jsonapi/UniSkinAPI.java index 576d3a1b..d08aa920 100644 --- a/Common/src/main/java/customskinloader/loader/jsonapi/UniSkinAPI.java +++ b/Common/src/main/java/customskinloader/loader/jsonapi/UniSkinAPI.java @@ -14,7 +14,7 @@ public class UniSkinAPI implements JsonAPILoader.IJsonAPI { - public static class SkinMe extends JsonAPILoader.DefaultProfile { + /*public static class SkinMe extends JsonAPILoader.DefaultProfile { public SkinMe(JsonAPILoader loader) { super(loader); } @@ -33,14 +33,14 @@ public int getPriority() { public String getRoot() { return "http://www.skinme.cc/uniskin/"; } - } + }*/ private static final String TEXTURES = "textures/"; private static final String SUFFIX = ".json"; @Override public List getDefaultProfiles(JsonAPILoader loader) { - return Lists.newArrayList(new SkinMe(loader)); + return Lists.newArrayList(/*new SkinMe(loader)*/); } @Override diff --git a/Common/src/main/java/customskinloader/loader/jsonapi/WynntilsAPI.java b/Common/src/main/java/customskinloader/loader/jsonapi/WynntilsAPI.java index 95855559..50d38045 100644 --- a/Common/src/main/java/customskinloader/loader/jsonapi/WynntilsAPI.java +++ b/Common/src/main/java/customskinloader/loader/jsonapi/WynntilsAPI.java @@ -21,7 +21,7 @@ */ public class WynntilsAPI implements JsonAPILoader.IJsonAPI { - public static class Wynntils extends JsonAPILoader.DefaultProfile { + /*public static class Wynntils extends JsonAPILoader.DefaultProfile { public Wynntils(JsonAPILoader loader) { super(loader); } @@ -40,11 +40,11 @@ public int getPriority() { public String getRoot() { return "https://athena.wynntils.com/user/getInfo"; } - } + }*/ @Override public List getDefaultProfiles(JsonAPILoader loader) { - return Lists.newArrayList(new Wynntils(loader)); + return Lists.newArrayList(/*new Wynntils(loader)*/); } @Override diff --git a/Common/src/main/java/customskinloader/log/Logger.java b/Common/src/main/java/customskinloader/log/Logger.java index 8ffc924a..92a2c4ff 100644 --- a/Common/src/main/java/customskinloader/log/Logger.java +++ b/Common/src/main/java/customskinloader/log/Logger.java @@ -1,7 +1,11 @@ package customskinloader.log; -import javax.annotation.Nullable; -import java.io.*; +import java.io.BufferedWriter; +import java.io.File; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.text.SimpleDateFormat; @@ -97,7 +101,7 @@ public Logger(File logFile, String loggerName) { * * @since 14.16 */ - public Logger(@Nullable Writer writer, String loggerName) { + public Logger(Writer writer, String loggerName) { this.loggerName = loggerName; this.writer = writer; } diff --git a/Common/src/main/java/customskinloader/mod/forge/ForgeMod.java b/Common/src/main/java/customskinloader/mod/forge/ForgeMod.java new file mode 100644 index 00000000..4cdbf484 --- /dev/null +++ b/Common/src/main/java/customskinloader/mod/forge/ForgeMod.java @@ -0,0 +1,41 @@ +package customskinloader.mod.forge; + +import net.minecraftforge.fml.ExtensionPoint; +import net.minecraftforge.fml.IExtensionPoint; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.common.Mod; +import org.apache.commons.lang3.tuple.Pair; + +/** + * Forge Mod Container + */ +@Mod( + value = "customskinloader", + modid = "customskinloader", + name = "CustomSkinLoader", + version = "@MOD_FULL_VERSION@", + clientSideOnly = true, + acceptedMinecraftVersions = "[1.8,1.13)", + acceptableRemoteVersions = "*" +) +public class ForgeMod { + public ForgeMod() { + try { + this.setExtensionPoint(); + } catch (Throwable ignored) { + // before forge-1.13.2-25.0.103 + } + } + + // Make sure the mod being absent on the other network side does not cause the client to display the server as incompatible after forge-1.13.2-25.0.107. + private void setExtensionPoint() { + try { + // 1.13.2 ~ 1.16.5 + ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.DISPLAYTEST, () -> Pair.of(() -> "customskinloader", (remote, isServer) -> isServer)); + } catch (Throwable ignored) { + // 1.17.1+ + ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class, () -> (Object) new IExtensionPoint.DisplayTest(() -> "customskinloader", (remote, isServer) -> isServer)); + } + } +} + diff --git a/Universal/src/main/java/customskinloader/forge/NeoForgeMod.java b/Common/src/main/java/customskinloader/mod/neoforge/NeoForgeMod.java similarity index 70% rename from Universal/src/main/java/customskinloader/forge/NeoForgeMod.java rename to Common/src/main/java/customskinloader/mod/neoforge/NeoForgeMod.java index 86c7e54f..eb45fe9d 100644 --- a/Universal/src/main/java/customskinloader/forge/NeoForgeMod.java +++ b/Common/src/main/java/customskinloader/mod/neoforge/NeoForgeMod.java @@ -1,4 +1,4 @@ -package customskinloader.forge; +package customskinloader.mod.neoforge; import net.neoforged.fml.common.Mod; diff --git a/Common/src/main/java/customskinloader/plugin/PluginLoader.java b/Common/src/main/java/customskinloader/plugin/PluginLoader.java index fc6e203f..f651719a 100644 --- a/Common/src/main/java/customskinloader/plugin/PluginLoader.java +++ b/Common/src/main/java/customskinloader/plugin/PluginLoader.java @@ -25,7 +25,6 @@ public class PluginLoader { new JsonAPILoader(new CustomSkinAPIPlus()), new JsonAPILoader(new UniSkinAPI()), new JsonAPILoader(new ElyByAPI()), - new JsonAPILoader(new GlitchlessAPI()), new JsonAPILoader(new MinecraftCapesAPI()), new JsonAPILoader(new WynntilsAPI()), }; diff --git a/Common/src/main/java/customskinloader/profile/DynamicSkullManager.java b/Common/src/main/java/customskinloader/profile/DynamicSkullManager.java deleted file mode 100644 index b8818e0e..00000000 --- a/Common/src/main/java/customskinloader/profile/DynamicSkullManager.java +++ /dev/null @@ -1,159 +0,0 @@ -package customskinloader.profile; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.minecraft.MinecraftProfileTexture; -import com.mojang.authlib.properties.Property; -import com.mojang.authlib.properties.PropertyMap; -import com.mojang.util.UUIDTypeAdapter; -import customskinloader.CustomSkinLoader; -import customskinloader.utils.HttpRequestUtil; -import customskinloader.utils.HttpTextureUtil; -import customskinloader.utils.HttpUtil0; -import customskinloader.utils.TextureUtil; -import org.apache.commons.codec.Charsets; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang3.StringUtils; - -public class DynamicSkullManager { - public static class SkullTexture { - public Map textures; - public String index;//Index File - public ArrayList skins; - public int interval; - public boolean fromZero; - //For program - public long startTime; - public int period; - } - - private Map dynamicTextures = new ConcurrentHashMap<>(); - private Map> staticTextures = new ConcurrentHashMap<>(); - private List loadingList = Lists.newArrayList(); - - private void parseGameProfile(GameProfile profile) { - Property textureProperty = Iterables.getFirst(TextureUtil.AuthlibField.GAME_PROFILE_PROPERTIES.get(profile).get("textures"), null); - if (textureProperty == null) { - staticTextures.put(profile, Maps.newHashMap()); - return; - } - String value = TextureUtil.AuthlibField.PROPERTY_VALUE.get(textureProperty); - if (StringUtils.isBlank(value)) { - staticTextures.put(profile, Maps.newHashMap()); - return; - } - @SuppressWarnings("deprecation") String json = new String(Base64.decodeBase64(value), Charsets.UTF_8); - Gson gson = new GsonBuilder().registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).create(); - SkullTexture result = gson.fromJson(json, SkullTexture.class); - if (result == null) { - staticTextures.put(profile, Maps.newHashMap()); - return; - } - staticTextures.put(profile, (result.textures == null || !result.textures.containsKey(MinecraftProfileTexture.Type.SKIN)) ? Maps.newHashMap() : parseTextures(result.textures)); - - if (StringUtils.isNotEmpty(result.index)) { - File indexFile = new File(CustomSkinLoader.DATA_DIR, result.index); - try { - String index = FileUtils.readFileToString(indexFile, "UTF-8"); - if (StringUtils.isNotEmpty(index)) { - String[] skins = CustomSkinLoader.GSON.fromJson(index, String[].class); - if (skins != null && skins.length != 0) - result.skins = Lists.newArrayList(skins); - } - } catch (Exception e) { - CustomSkinLoader.logger.warning("Exception occurs while parsing index file: " + e.toString()); - } - } - - if (!CustomSkinLoader.config.enableDynamicSkull || result.skins == null || result.skins.isEmpty()) - return; - - CustomSkinLoader.logger.info("Try to load Dynamic Skull: " + json); - - for (int i = 0; i < result.skins.size(); i++) {//check and cache skins - String skin = result.skins.get(i); - if (HttpUtil0.isLocal(skin)) { - //Local Skin - File skinFile = new File(CustomSkinLoader.DATA_DIR, skin); - if (skinFile.isFile() && skinFile.length() > 0) { - //Skin found - String fakeUrl = HttpTextureUtil.getLocalFakeUrl(skin); - result.skins.set(i, fakeUrl); - } else { - //Could not find skin - result.skins.remove(i--); - } - } else { - //Online Skin - HttpRequestUtil.HttpResponce responce = HttpRequestUtil.makeHttpRequest(new HttpRequestUtil.HttpRequest(skin).setCacheFile(HttpTextureUtil.getCacheFile(FilenameUtils.getBaseName(skin))).setCacheTime(0).setLoadContent(false)); - if (!responce.success) { - //Could not load skin - result.skins.remove(i--); - } - } - } - - if (result.skins.isEmpty()) {//Nothing loaded - CustomSkinLoader.logger.info("Failed: Nothing loaded."); - return; - } - result.interval = Math.max(result.interval, 50); - if (result.fromZero) - result.startTime = System.currentTimeMillis(); - result.period = result.interval * result.skins.size(); - CustomSkinLoader.logger.info("Successfully loaded Dynamic Skull: " + new Gson().toJson(result)); - dynamicTextures.put(profile, result); - staticTextures.remove(profile); - } - - //Support local skin for skull - public Map parseTextures(Map textures) { - MinecraftProfileTexture skin = textures.get(MinecraftProfileTexture.Type.SKIN); - String skinUrl = skin.getUrl(); - if (!HttpUtil0.isLocal(skinUrl)) - // Wrap MinecraftProfileTexture to FakeMinecraftProfileTexture - return ModelManager0.fromUserProfile(ModelManager0.toUserProfile(textures)); - File skinFile = new File(CustomSkinLoader.DATA_DIR, skinUrl); - if (!skinFile.isFile()) - return Maps.newHashMap(); - textures.put(MinecraftProfileTexture.Type.SKIN, ModelManager0.getProfileTexture(HttpTextureUtil.getLocalFakeUrl(skinUrl), null)); - return textures; - } - - public Map getTexture(final GameProfile profile) { - if (staticTextures.get(profile) != null) - return staticTextures.get(profile); - if (loadingList.contains(profile)) - return CustomSkinLoader.INCOMPLETED; - if (dynamicTextures.containsKey(profile)) { - SkullTexture texture = dynamicTextures.get(profile); - long time = System.currentTimeMillis() - texture.startTime; - int index = (int) Math.floor((time % texture.period) / texture.interval); - Map map = Maps.newHashMap(); - map.put(MinecraftProfileTexture.Type.SKIN, ModelManager0.getProfileTexture(texture.skins.get(index), null)); - return map; - } - - loadingList.add(profile); - Thread loadThread = new Thread(() -> { - parseGameProfile(profile);//Load in thread - loadingList.remove(profile); - }); - loadThread.setName("Skull " + profile.hashCode()); - loadThread.start(); - return CustomSkinLoader.INCOMPLETED; - } -} diff --git a/Common/src/main/java/customskinloader/utils/HttpRequestUtil.java b/Common/src/main/java/customskinloader/utils/HttpRequestUtil.java index 0c305b4f..84519a54 100644 --- a/Common/src/main/java/customskinloader/utils/HttpRequestUtil.java +++ b/Common/src/main/java/customskinloader/utils/HttpRequestUtil.java @@ -142,8 +142,8 @@ public static HttpResponce makeHttpRequest(HttpRequest request, int redirectTime CustomSkinLoader.logger.debug("Encoded URL: " + url); } HttpURLConnection c = (HttpURLConnection) (new URL(url)).openConnection(); - c.setReadTimeout(1000 * 10); - c.setConnectTimeout(1000 * 10); + c.setReadTimeout(1000 * 5); + c.setConnectTimeout(1000 * 5); c.setDoInput(true); c.setUseCaches(false); c.setInstanceFollowRedirects(true); diff --git a/Common/src/main/java/customskinloader/utils/JavaUtil.java b/Common/src/main/java/customskinloader/utils/JavaUtil.java deleted file mode 100644 index 5360fe5d..00000000 --- a/Common/src/main/java/customskinloader/utils/JavaUtil.java +++ /dev/null @@ -1,33 +0,0 @@ -package customskinloader.utils; - -import java.io.File; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.LinkedList; - -public class JavaUtil { - public static URL[] getClasspath() { - if (JavaUtil.class.getClassLoader() instanceof URLClassLoader) { - //Java 8- - //Use URLClassLoader Directly - URLClassLoader ucl = (URLClassLoader) JavaUtil.class.getClassLoader(); - return ucl.getURLs(); - } else { - //Java 9+ - //It uses APPClassLoader instead of URLClassLoader - //Get Classpath from properties and parse it to URL array - String classpath = System.getProperty("java.class.path"); - String[] elements = classpath.split(File.pathSeparator); - if (elements.length == 0) - return new URL[0]; - LinkedList urls = new LinkedList(); - for (String ele : elements) { - try { - urls.add(new File(ele).toURI().toURL()); - } catch (Exception ignored) { - } - } - return urls.toArray(new URL[0]); - } - } -} diff --git a/Common/src/main/java/customskinloader/utils/MinecraftUtil.java b/Common/src/main/java/customskinloader/utils/MinecraftUtil.java index dfee05f8..96ae4d5b 100644 --- a/Common/src/main/java/customskinloader/utils/MinecraftUtil.java +++ b/Common/src/main/java/customskinloader/utils/MinecraftUtil.java @@ -21,15 +21,15 @@ */ public class MinecraftUtil { public static File getMinecraftDataDir() { - return Minecraft.getMinecraft().gameDir; + return Minecraft.getInstance().gameDirectory; } public static TextureManager getTextureManager() { - return Minecraft.getMinecraft().getTextureManager(); + return Minecraft.getInstance().getTextureManager(); } public static SkinManager getSkinManager() { - return Minecraft.getMinecraft().getSkinManager(); + return Minecraft.getInstance().getSkinManager(); } private static String minecraftMainVersion = null; @@ -69,10 +69,10 @@ public static String getMinecraftMainVersion() { // (domain|ip)(:port) public static String getServerAddress() { - ServerData data = Minecraft.getMinecraft().getCurrentServerData(); + ServerData data = Minecraft.getInstance().getCurrentServer(); if (data == null)//Single Player return null; - return data.serverIP; + return data.ip; } // ip:port @@ -85,6 +85,6 @@ public static boolean isLanServer() { } public static String getCredential(GameProfile profile) { - return profile == null ? null : String.format("%s-%s", TextureUtil.AuthlibField.GAME_PROFILE_NAME.get(profile), TextureUtil.AuthlibField.GAME_PROFILE_ID.get(profile)); + return profile == null ? null : profile.toString(); } } diff --git a/Common/src/main/java/customskinloader/utils/ThreadPoolFactory.java b/Common/src/main/java/customskinloader/utils/ThreadPoolFactory.java new file mode 100644 index 00000000..d09d20a8 --- /dev/null +++ b/Common/src/main/java/customskinloader/utils/ThreadPoolFactory.java @@ -0,0 +1,19 @@ +package customskinloader.utils; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public final class ThreadPoolFactory { + private ThreadPoolFactory() { + } + + public static ExecutorService create(int poolSize, boolean fifo) { + poolSize = Math.max(1, poolSize); + ThreadPoolExecutor executor = new ThreadPoolExecutor(poolSize, Integer.MAX_VALUE, 1L, TimeUnit.MINUTES, fifo ? new LinkedBlockingQueue<>(poolSize) : new LIFOBlockingQueue<>(new LinkedBlockingDeque<>(poolSize))); + executor.allowCoreThreadTimeOut(true); + return executor; + } +} diff --git a/Common/src/main/java/net/minecraft/client/renderer/IImageBuffer.java b/Common/src/main/java/net/minecraft/client/renderer/IImageBuffer.java deleted file mode 100644 index 7d3860a6..00000000 --- a/Common/src/main/java/net/minecraft/client/renderer/IImageBuffer.java +++ /dev/null @@ -1,14 +0,0 @@ -package net.minecraft.client.renderer; - -import java.awt.image.BufferedImage; - -import customskinloader.fake.itf.IFakeIImageBuffer; -import net.minecraft.client.renderer.texture.NativeImage; - -public interface IImageBuffer extends IFakeIImageBuffer { - BufferedImage parseUserSkin(BufferedImage image); - - NativeImage func_195786_a(NativeImage image); - - void skinAvailable(); -} diff --git a/Common/src/main/java21/customskinloader/utils/ThreadPoolFactory.java b/Common/src/main/java21/customskinloader/utils/ThreadPoolFactory.java new file mode 100644 index 00000000..e9c0e1af --- /dev/null +++ b/Common/src/main/java21/customskinloader/utils/ThreadPoolFactory.java @@ -0,0 +1,13 @@ +package customskinloader.utils; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public final class ThreadPoolFactory { + private ThreadPoolFactory() { + } + + public static ExecutorService create(int poolSize, boolean fifo) { + return Executors.newVirtualThreadPerTaskExecutor(); + } +} diff --git a/Common/src/main/resources/BuildInfo.json b/Common/src/main/resources/BuildInfo.json deleted file mode 100644 index ad60517f..00000000 --- a/Common/src/main/resources/BuildInfo.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "CustomSkinLoader", - "version": "${modVersion}", - "fullVersion": "${modFullVersion}", - "dependencies": { - "${dependencies}" - }, - "gitVersion": "${gitVersion}", - "buildUrl": "${buildUrl}", - "buildTime": "${releaseTime}" -} diff --git a/Universal/src/main/resources/META-INF/mods.toml b/Common/src/main/resources/META-INF/mods.toml similarity index 94% rename from Universal/src/main/resources/META-INF/mods.toml rename to Common/src/main/resources/META-INF/mods.toml index 21051d74..a8bc5efc 100644 --- a/Universal/src/main/resources/META-INF/mods.toml +++ b/Common/src/main/resources/META-INF/mods.toml @@ -3,6 +3,7 @@ loaderVersion="[0,)" # The javafml version number for neoforge is 1 license="GPL-3.0-only" issueTrackerURL="https://github.com/xfl03/MCCustomSkinLoader/issues" clientSideOnly=true # added by forge + [[mods]] modId="customskinloader" version="${modFullVersion}" @@ -13,13 +14,15 @@ Custom Skin Loader for Minecraft ''' displayURL="https://github.com/xfl03/MCCustomSkinLoader" authors="xfl03, JLChnToZ, ZekerZhayard" + [[dependencies.customskinloader]] modId="forge" mandatory=false type="optional" # added by neoforge 1.20.4+ -versionRange="[50,)" +versionRange="[25,)" ordering="NONE" side="CLIENT" + [[dependencies.customskinloader]] modId="neoforge" mandatory=false @@ -27,9 +30,10 @@ type="required" # added by neoforge 1.20.4+ versionRange="[20.2,)" ordering="NONE" side="CLIENT" + [[dependencies.customskinloader]] modId="minecraft" mandatory=true -versionRange="[1.20.2,)" +versionRange="[1.13.2,)" ordering="NONE" side="CLIENT" diff --git a/Forge/V2/src/main/resources/META-INF/mods.toml b/Common/src/main/resources/META-INF/neoforge.mods.toml similarity index 70% rename from Forge/V2/src/main/resources/META-INF/mods.toml rename to Common/src/main/resources/META-INF/neoforge.mods.toml index 9b9165cd..58f4520a 100644 --- a/Forge/V2/src/main/resources/META-INF/mods.toml +++ b/Common/src/main/resources/META-INF/neoforge.mods.toml @@ -1,25 +1,27 @@ modLoader="javafml" -loaderVersion="[37,)" +loaderVersion="[2,)" license="GPL-3.0-only" issueTrackerURL="https://github.com/xfl03/MCCustomSkinLoader/issues" + [[mods]] modId="customskinloader" version="${modFullVersion}" displayName="CustomSkinLoader" +authors="xfl03, JLChnToZ, ZekerZhayard" +displayTest="IGNORE_ALL_VERSION" description=''' Custom Skin Loader for Minecraft ''' -displayURL="https://github.com/xfl03/MCCustomSkinLoader" -authors="xfl03, JLChnToZ, ZekerZhayard" + [[dependencies.customskinloader]] -modId="forge" -mandatory=true -versionRange="[37,)" +modId="neoforge" +type="required" +versionRange="[20.5,)" ordering="NONE" side="CLIENT" [[dependencies.customskinloader]] modId="minecraft" -mandatory=true -versionRange="[1.17.1,)" +type="required" +versionRange="[1.20.5,)" ordering="NONE" -side="CLIENT" +side="BOTH" diff --git a/Common/src/main/resources/mcmod.info b/Common/src/main/resources/mcmod.info new file mode 100644 index 00000000..b3ed3615 --- /dev/null +++ b/Common/src/main/resources/mcmod.info @@ -0,0 +1,10 @@ +[ + { + "modid": "customskinloader", + "name": "CustomSkinLoader", + "description": "Custom Skin Loader for Minecraft", + "version": "${modFullVersion}", + "url": "https://github.com/xfl03/MCCustomSkinLoader", + "authorList": ["xfl03", "JLChnToZ", "ZekerZhayard"] + } +] diff --git a/Forge/V1/src/main/resources/pack.mcmeta b/Common/src/main/resources/pack.mcmeta similarity index 75% rename from Forge/V1/src/main/resources/pack.mcmeta rename to Common/src/main/resources/pack.mcmeta index b19ba5f1..8b588f68 100644 --- a/Forge/V1/src/main/resources/pack.mcmeta +++ b/Common/src/main/resources/pack.mcmeta @@ -1,6 +1,6 @@ { "pack": { "description": "CustomSkinLoader resources", - "pack_format": 4 + "pack_format": 1 } } diff --git a/Dummy/Bootstrap/src/main/java/cpw/mods/modlauncher/api/TargetType.java b/Dummy/Bootstrap/src/main/java/cpw/mods/modlauncher/api/TargetType.java new file mode 100644 index 00000000..9d519d0f --- /dev/null +++ b/Dummy/Bootstrap/src/main/java/cpw/mods/modlauncher/api/TargetType.java @@ -0,0 +1,5 @@ +package cpw.mods.modlauncher.api; + +public class TargetType { + public static TargetType CLASS; +} diff --git a/Dummy/Common/src/main/java/com/mojang/blaze3d/platform/NativeImage.java b/Dummy/Common/src/main/java/com/mojang/blaze3d/platform/NativeImage.java new file mode 100644 index 00000000..fb5ddfe6 --- /dev/null +++ b/Dummy/Common/src/main/java/com/mojang/blaze3d/platform/NativeImage.java @@ -0,0 +1,63 @@ +package com.mojang.blaze3d.platform; + +import java.io.InputStream; + +/** + * Empty stack for NativeImage + */ +public class NativeImage { + public static NativeImage read(InputStream inputStreamIn) { + return null; + } + + public NativeImage(int p_i48122_1_, int p_i48122_2_, boolean p_i48122_3_) { + } + + /** + * getWidth + */ + public int getWidth() { + return 0; + } + + /** + * getHeight + */ + public int getHeight() { + return 0; + } + + /** + * copyImage image + */ + public void copyFrom(NativeImage p_195703_1_) { + } + + /** + * fillAreaRGBA x0 y0 weight height rgba + */ + public void fillRect(int p_195715_1_, int p_195715_2_, int p_195715_3_, int p_195715_4_, int p_195715_5_) { + } + + /** + * copyAreaRGBA x0 y0 dx dy weight height reversex reversey + */ + public void copyRect(int p_195699_1_, int p_195699_2_, int p_195699_3_, int p_195699_4_, int p_195699_5_, int p_195699_6_, boolean p_195699_7_, boolean p_195699_8_) { + } + + /** + * getPixelRGBA x y + */ + public int getPixelRGBA(int p_195709_1_, int p_195709_2_) { + return 0; + } + + /** + * setPixelRGBA x y rgba + */ + public void setPixelRGBA(int p_195700_1_, int p_195700_2_, int p_195700_3_) { + } + + public void close() { + } +} diff --git a/Dummy/Common/src/main/java/customskinloader/fake/itf/FakeHttpTextureProcessor.java b/Dummy/Common/src/main/java/customskinloader/fake/itf/FakeHttpTextureProcessor.java new file mode 100644 index 00000000..08d4056b --- /dev/null +++ b/Dummy/Common/src/main/java/customskinloader/fake/itf/FakeHttpTextureProcessor.java @@ -0,0 +1,4 @@ +package customskinloader.fake.itf; + +public interface FakeHttpTextureProcessor { +} diff --git a/Dummy/src/main/java/net/minecraft/client/Minecraft.java b/Dummy/Common/src/main/java/net/minecraft/client/Minecraft.java similarity index 57% rename from Dummy/src/main/java/net/minecraft/client/Minecraft.java rename to Dummy/Common/src/main/java/net/minecraft/client/Minecraft.java index 24d16b14..7409fca5 100644 --- a/Dummy/src/main/java/net/minecraft/client/Minecraft.java +++ b/Dummy/Common/src/main/java/net/minecraft/client/Minecraft.java @@ -4,14 +4,14 @@ import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.renderer.texture.TextureManager; -import net.minecraft.client.resources.IResourceManager; +import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.client.resources.SkinManager; public class Minecraft { - public File gameDir; - public static Minecraft getMinecraft() { return null; } - public ServerData getCurrentServerData() { return null; } - public IResourceManager getResourceManager() { return null; } + public File gameDirectory; + public static Minecraft getInstance() { return null; } + public ServerData getCurrentServer() { return null; } + public ResourceManager getResourceManager() { return null; } public SkinManager getSkinManager() { return null; } public TextureManager getTextureManager() { return null; } } diff --git a/Dummy/src/main/java/net/minecraft/client/multiplayer/ServerData.java b/Dummy/Common/src/main/java/net/minecraft/client/multiplayer/ServerData.java similarity index 71% rename from Dummy/src/main/java/net/minecraft/client/multiplayer/ServerData.java rename to Dummy/Common/src/main/java/net/minecraft/client/multiplayer/ServerData.java index 7975be06..c999a6be 100644 --- a/Dummy/src/main/java/net/minecraft/client/multiplayer/ServerData.java +++ b/Dummy/Common/src/main/java/net/minecraft/client/multiplayer/ServerData.java @@ -1,5 +1,5 @@ package net.minecraft.client.multiplayer; public class ServerData { - public String serverIP; + public String ip; } diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/texture/AbstractTexture.java b/Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/AbstractTexture.java similarity index 100% rename from Dummy/src/main/java/net/minecraft/client/renderer/texture/AbstractTexture.java rename to Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/AbstractTexture.java diff --git a/Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/HttpTexture.java b/Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/HttpTexture.java new file mode 100644 index 00000000..706d7d6b --- /dev/null +++ b/Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/HttpTexture.java @@ -0,0 +1,23 @@ +package net.minecraft.client.renderer.texture; + +import java.awt.image.BufferedImage; +import java.io.File; + +import customskinloader.fake.itf.FakeHttpTextureProcessor; +import net.minecraft.resources.Identifier; + +public class HttpTexture { + public boolean uploaded; + + public HttpTexture(File cacheFileIn, String imageUrlIn, Identifier textureResourceLocation, FakeHttpTextureProcessor imageBufferIn) { + + } + + public HttpTexture(File cacheFileIn, String imageUrlIn, Identifier textureResourceLocation, boolean isLegacy, Runnable processTask) { + + } + + public void upload(BufferedImage bufferedImageIn) { + + } +} diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/texture/ReloadableTexture.java b/Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/ReloadableTexture.java similarity index 100% rename from Dummy/src/main/java/net/minecraft/client/renderer/texture/ReloadableTexture.java rename to Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/ReloadableTexture.java diff --git a/Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/SimpleTexture.java b/Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/SimpleTexture.java new file mode 100644 index 00000000..8ad8f5c2 --- /dev/null +++ b/Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/SimpleTexture.java @@ -0,0 +1,9 @@ +package net.minecraft.client.renderer.texture; + +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.ResourceManager; + +public class SimpleTexture extends ReloadableTexture { + public SimpleTexture(Identifier location) {} + public TextureContents loadContents(ResourceManager resourceManager) { return null; } +} diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/texture/TextureContents.java b/Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/TextureContents.java similarity index 56% rename from Dummy/src/main/java/net/minecraft/client/renderer/texture/TextureContents.java rename to Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/TextureContents.java index 36835d6f..610e1393 100644 --- a/Dummy/src/main/java/net/minecraft/client/renderer/texture/TextureContents.java +++ b/Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/TextureContents.java @@ -1,6 +1,7 @@ package net.minecraft.client.renderer.texture; -import net.minecraft.client.resources.data.TextureMetadataSection; +import com.mojang.blaze3d.platform.NativeImage; +import net.minecraft.client.resources.metadata.texture.TextureMetadataSection; public class TextureContents { public TextureContents(NativeImage image, TextureMetadataSection metadata) {} diff --git a/Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/TextureManager.java b/Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/TextureManager.java new file mode 100644 index 00000000..89fb9eaa --- /dev/null +++ b/Dummy/Common/src/main/java/net/minecraft/client/renderer/texture/TextureManager.java @@ -0,0 +1,7 @@ +package net.minecraft.client.renderer.texture; + +import net.minecraft.resources.Identifier; + +public class TextureManager { + public void registerAndLoad(Identifier resourceLocation, ReloadableTexture reloadableTexture) {} +} diff --git a/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$CacheKey.java b/Dummy/Common/src/main/java/net/minecraft/client/resources/SkinManager$CacheKey.java similarity index 100% rename from Dummy/src/main/java/net/minecraft/client/resources/SkinManager$CacheKey.java rename to Dummy/Common/src/main/java/net/minecraft/client/resources/SkinManager$CacheKey.java diff --git a/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$TextureInfo.java b/Dummy/Common/src/main/java/net/minecraft/client/resources/SkinManager.java similarity index 53% rename from Dummy/src/main/java/net/minecraft/client/resources/SkinManager$TextureInfo.java rename to Dummy/Common/src/main/java/net/minecraft/client/resources/SkinManager.java index 0aee0a5b..464afeca 100644 --- a/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$TextureInfo.java +++ b/Dummy/Common/src/main/java/net/minecraft/client/resources/SkinManager.java @@ -1,5 +1,5 @@ package net.minecraft.client.resources; -public class SkinManager$TextureInfo { +public class SkinManager { } diff --git a/Dummy/Common/src/main/java/net/minecraft/client/resources/metadata/texture/TextureMetadataSection.java b/Dummy/Common/src/main/java/net/minecraft/client/resources/metadata/texture/TextureMetadataSection.java new file mode 100644 index 00000000..44477a9d --- /dev/null +++ b/Dummy/Common/src/main/java/net/minecraft/client/resources/metadata/texture/TextureMetadataSection.java @@ -0,0 +1,5 @@ +package net.minecraft.client.resources.metadata.texture; + +public class TextureMetadataSection { + +} diff --git a/Dummy/Common/src/main/java/net/minecraft/resources/Identifier.java b/Dummy/Common/src/main/java/net/minecraft/resources/Identifier.java new file mode 100644 index 00000000..9d08c0cb --- /dev/null +++ b/Dummy/Common/src/main/java/net/minecraft/resources/Identifier.java @@ -0,0 +1,5 @@ +package net.minecraft.resources; + +public class Identifier { + public Identifier(String domain, String path) {} +} diff --git a/Dummy/src/main/java/net/minecraft/server/Services.java b/Dummy/Common/src/main/java/net/minecraft/server/Services.java similarity index 100% rename from Dummy/src/main/java/net/minecraft/server/Services.java rename to Dummy/Common/src/main/java/net/minecraft/server/Services.java diff --git a/Dummy/Common/src/main/java/net/minecraft/server/packs/resources/Resource.java b/Dummy/Common/src/main/java/net/minecraft/server/packs/resources/Resource.java new file mode 100644 index 00000000..ddf64339 --- /dev/null +++ b/Dummy/Common/src/main/java/net/minecraft/server/packs/resources/Resource.java @@ -0,0 +1,7 @@ +package net.minecraft.server.packs.resources; + +import java.io.InputStream; + +public class Resource { + public InputStream getInputStream() { return null; } +} diff --git a/Dummy/Common/src/main/java/net/minecraft/server/packs/resources/ResourceManager.java b/Dummy/Common/src/main/java/net/minecraft/server/packs/resources/ResourceManager.java new file mode 100644 index 00000000..9c6f4977 --- /dev/null +++ b/Dummy/Common/src/main/java/net/minecraft/server/packs/resources/ResourceManager.java @@ -0,0 +1,5 @@ +package net.minecraft.server.packs.resources; + +public interface ResourceManager { + +} diff --git a/Dummy/Common/src/main/java/net/minecraft/util/StringUtil.java b/Dummy/Common/src/main/java/net/minecraft/util/StringUtil.java new file mode 100644 index 00000000..46bbed41 --- /dev/null +++ b/Dummy/Common/src/main/java/net/minecraft/util/StringUtil.java @@ -0,0 +1,5 @@ +package net.minecraft.util; + +public class StringUtil { + public static String stripColor(String string) { return null; } +} diff --git a/Forge/Common/src/main/java/net/minecraftforge/fml/ExtensionPoint.java b/Dummy/Common/src/main/java/net/minecraftforge/fml/ExtensionPoint.java similarity index 100% rename from Forge/Common/src/main/java/net/minecraftforge/fml/ExtensionPoint.java rename to Dummy/Common/src/main/java/net/minecraftforge/fml/ExtensionPoint.java diff --git a/Forge/Common/src/main/java/net/minecraftforge/fml/IExtensionPoint.java b/Dummy/Common/src/main/java/net/minecraftforge/fml/IExtensionPoint.java similarity index 100% rename from Forge/Common/src/main/java/net/minecraftforge/fml/IExtensionPoint.java rename to Dummy/Common/src/main/java/net/minecraftforge/fml/IExtensionPoint.java diff --git a/Forge/Common/src/main/java/net/minecraftforge/fml/ModLoadingContext.java b/Dummy/Common/src/main/java/net/minecraftforge/fml/ModLoadingContext.java similarity index 71% rename from Forge/Common/src/main/java/net/minecraftforge/fml/ModLoadingContext.java rename to Dummy/Common/src/main/java/net/minecraftforge/fml/ModLoadingContext.java index e405eaa5..e38d2215 100644 --- a/Forge/Common/src/main/java/net/minecraftforge/fml/ModLoadingContext.java +++ b/Dummy/Common/src/main/java/net/minecraftforge/fml/ModLoadingContext.java @@ -11,7 +11,7 @@ public void registerExtensionPoint(ExtensionPoint point, Supplier exte } - public void registerExtensionPoint(Class> point, Supplier extension) { + public void registerExtensionPoint(Class point, Supplier extension) { } } diff --git a/Forge/Common/src/main/java/net/minecraftforge/fml/common/Mod.java b/Dummy/Common/src/main/java/net/minecraftforge/fml/common/Mod.java similarity index 83% rename from Forge/Common/src/main/java/net/minecraftforge/fml/common/Mod.java rename to Dummy/Common/src/main/java/net/minecraftforge/fml/common/Mod.java index 1b1bf67b..00e0cb27 100644 --- a/Forge/Common/src/main/java/net/minecraftforge/fml/common/Mod.java +++ b/Dummy/Common/src/main/java/net/minecraftforge/fml/common/Mod.java @@ -23,12 +23,4 @@ String acceptedMinecraftVersions() default ""; String acceptableRemoteVersions() default ""; - - String certificateFingerprint() default ""; - - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.METHOD) - @interface EventHandler { - } - } diff --git a/Forge/Common/src/main/java/net/neoforged/fml/common/Mod.java b/Dummy/Common/src/main/java/net/neoforged/fml/common/Mod.java similarity index 100% rename from Forge/Common/src/main/java/net/neoforged/fml/common/Mod.java rename to Dummy/Common/src/main/java/net/neoforged/fml/common/Mod.java diff --git a/Dummy/Dummy.srg b/Dummy/Dummy.srg deleted file mode 100644 index e69de29b..00000000 diff --git a/Dummy/build.gradle b/Dummy/build.gradle deleted file mode 100644 index 825c4a80..00000000 --- a/Dummy/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -// This subproject is only to provide dependency for other subprojects. - -reobf { - jar { - mappings = file("Dummy.srg") - } -} diff --git a/Dummy/build.properties b/Dummy/build.properties deleted file mode 100644 index 2c5a53ad..00000000 --- a/Dummy/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -forge_mc_version=1.12.2 -forge_version=14.23.5.2768 -mappings_version=stable_39 -# this subproject is only to provide dependency for other subprojects. -is_real_project=false diff --git a/Dummy/src/main/java/com/google/errorprone/annotations/CanIgnoreReturnValue.java b/Dummy/src/main/java/com/google/errorprone/annotations/CanIgnoreReturnValue.java deleted file mode 100644 index 5c542b7b..00000000 --- a/Dummy/src/main/java/com/google/errorprone/annotations/CanIgnoreReturnValue.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.google.errorprone.annotations; - -public @interface CanIgnoreReturnValue { - -} diff --git a/Dummy/src/main/java/com/mojang/authlib/SignatureState.java b/Dummy/src/main/java/com/mojang/authlib/SignatureState.java deleted file mode 100644 index 1809bc72..00000000 --- a/Dummy/src/main/java/com/mojang/authlib/SignatureState.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.mojang.authlib; - -public enum SignatureState { - SIGNED -} diff --git a/Dummy/src/main/java/com/mojang/authlib/minecraft/MinecraftProfileTextures.java b/Dummy/src/main/java/com/mojang/authlib/minecraft/MinecraftProfileTextures.java deleted file mode 100644 index b9c3e222..00000000 --- a/Dummy/src/main/java/com/mojang/authlib/minecraft/MinecraftProfileTextures.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.mojang.authlib.minecraft; - -import com.mojang.authlib.SignatureState; - -public class MinecraftProfileTextures { - public MinecraftProfileTextures(MinecraftProfileTexture skin, MinecraftProfileTexture cape, MinecraftProfileTexture elytra, SignatureState signatureState) {} - public MinecraftProfileTexture skin() { return null; } -} diff --git a/Dummy/src/main/java/com/mojang/authlib/minecraft/MinecraftSessionService.java b/Dummy/src/main/java/com/mojang/authlib/minecraft/MinecraftSessionService.java deleted file mode 100644 index 02e12995..00000000 --- a/Dummy/src/main/java/com/mojang/authlib/minecraft/MinecraftSessionService.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.mojang.authlib.minecraft; - -import com.mojang.authlib.properties.Property; - -public interface MinecraftSessionService { - MinecraftProfileTextures unpackTextures(Property packedTextures); -} diff --git a/Dummy/src/main/java/customskinloader/dummy/FakeBiMap.java b/Dummy/src/main/java/customskinloader/dummy/FakeBiMap.java deleted file mode 100644 index 86064782..00000000 --- a/Dummy/src/main/java/customskinloader/dummy/FakeBiMap.java +++ /dev/null @@ -1,89 +0,0 @@ -package customskinloader.dummy; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import com.google.common.collect.BiMap; -import com.google.common.collect.Sets; - -public class FakeBiMap implements BiMap { - private final Map map = new HashMap(); - - public static BiMap create(BiMap map) { - return new FakeBiMap<>(); - } - - private FakeBiMap() { - } - - @Override - public int size() { - return this.map.size(); - } - - @Override - public boolean isEmpty() { - return this.map.isEmpty(); - } - - @Override - public boolean containsKey(Object key) { - return this.map.containsKey(key); - } - - @Override - public boolean containsValue(Object value) { - return this.map.containsValue(value); - } - - @Override - public V get(Object key) { - return this.map.get(key); - } - - @Override - public V put(K k, V v) { - return this.map.put(k, v); - } - - @Override - public V remove(Object key) { - return this.map.remove(key); - } - - @Override - public V forcePut(K k, V v) { - return this.map.put(k, v); - } - - @Override - public void putAll(Map map) { - this.map.putAll(map); - } - - @Override - public void clear() { - this.map.clear(); - } - - @Override - public Set keySet() { - return this.map.keySet(); - } - - @Override - public Set values() { - return Sets.newHashSet(this.map.values()); - } - - @Override - public Set> entrySet() { - return this.map.entrySet(); - } - - @Override - public BiMap inverse() { - throw new UnsupportedOperationException(); - } -} diff --git a/Dummy/src/main/java/net/minecraft/client/gui/GuiPlayerTabOverlay.java b/Dummy/src/main/java/net/minecraft/client/gui/GuiPlayerTabOverlay.java deleted file mode 100644 index 3c18df2b..00000000 --- a/Dummy/src/main/java/net/minecraft/client/gui/GuiPlayerTabOverlay.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.minecraft.client.gui; - -public class GuiPlayerTabOverlay { - -} diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/RenderType.java b/Dummy/src/main/java/net/minecraft/client/renderer/RenderType.java deleted file mode 100644 index aa941466..00000000 --- a/Dummy/src/main/java/net/minecraft/client/renderer/RenderType.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.minecraft.client.renderer; - -import net.minecraft.util.ResourceLocation; - -public class RenderType { - // getEntityTranslucent - public static RenderType func_228644_e_(ResourceLocation locationIn) { - return null; - } -} diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/ThreadDownloadImageData.java b/Dummy/src/main/java/net/minecraft/client/renderer/ThreadDownloadImageData.java deleted file mode 100644 index e3b461b7..00000000 --- a/Dummy/src/main/java/net/minecraft/client/renderer/ThreadDownloadImageData.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.minecraft.client.renderer; - -import java.io.InputStream; - -import net.minecraft.client.renderer.texture.ITextureObject; -import net.minecraft.client.renderer.texture.NativeImage; - -public class ThreadDownloadImageData implements ITextureObject { - public NativeImage loadTexture(InputStream stream) { return null; } -} diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/entity/RenderPlayer.java b/Dummy/src/main/java/net/minecraft/client/renderer/entity/RenderPlayer.java deleted file mode 100644 index e828cef5..00000000 --- a/Dummy/src/main/java/net/minecraft/client/renderer/entity/RenderPlayer.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.minecraft.client.renderer.entity; - -public class RenderPlayer { - -} diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/entity/layers/LayerCape.java b/Dummy/src/main/java/net/minecraft/client/renderer/entity/layers/LayerCape.java deleted file mode 100644 index f3e8c9a6..00000000 --- a/Dummy/src/main/java/net/minecraft/client/renderer/entity/layers/LayerCape.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.minecraft.client.renderer.entity.layers; - -public class LayerCape { - -} diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/rendertype/RenderType.java b/Dummy/src/main/java/net/minecraft/client/renderer/rendertype/RenderType.java deleted file mode 100644 index 930ef466..00000000 --- a/Dummy/src/main/java/net/minecraft/client/renderer/rendertype/RenderType.java +++ /dev/null @@ -1,4 +0,0 @@ -package net.minecraft.client.renderer.rendertype; - -public class RenderType { -} diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/rendertype/RenderTypes.java b/Dummy/src/main/java/net/minecraft/client/renderer/rendertype/RenderTypes.java deleted file mode 100644 index e6a97bf7..00000000 --- a/Dummy/src/main/java/net/minecraft/client/renderer/rendertype/RenderTypes.java +++ /dev/null @@ -1,9 +0,0 @@ -package net.minecraft.client.renderer.rendertype; - -import net.minecraft.resources.Identifier; - -public class RenderTypes { - public static RenderType entityTranslucent(Identifier identifier) { - return null; - } -} diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/texture/ITextureObject.java b/Dummy/src/main/java/net/minecraft/client/renderer/texture/ITextureObject.java deleted file mode 100644 index 146d766c..00000000 --- a/Dummy/src/main/java/net/minecraft/client/renderer/texture/ITextureObject.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.minecraft.client.renderer.texture; - -public interface ITextureObject { - -} diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/texture/NativeImage.java b/Dummy/src/main/java/net/minecraft/client/renderer/texture/NativeImage.java deleted file mode 100644 index 54457fab..00000000 --- a/Dummy/src/main/java/net/minecraft/client/renderer/texture/NativeImage.java +++ /dev/null @@ -1,63 +0,0 @@ -package net.minecraft.client.renderer.texture; - -import java.io.InputStream; - -/** - * Empty stack for NativeImage - */ -public class NativeImage { - public static NativeImage func_195713_a(InputStream inputStreamIn) { - return null; - } - - public NativeImage(int p_i48122_1_, int p_i48122_2_, boolean p_i48122_3_) { - } - - /** - * getWidth - */ - public int func_195702_a() { - return 0; - } - - /** - * getHeight - */ - public int func_195714_b() { - return 0; - } - - /** - * copyImage image - */ - public void func_195703_a(NativeImage p_195703_1_) { - } - - /** - * fillAreaRGBA x0 y0 weight height rgba - */ - public void func_195715_a(int p_195715_1_, int p_195715_2_, int p_195715_3_, int p_195715_4_, int p_195715_5_) { - } - - /** - * copyAreaRGBA x0 y0 dx dy weight height reversex reversey - */ - public void func_195699_a(int p_195699_1_, int p_195699_2_, int p_195699_3_, int p_195699_4_, int p_195699_5_, int p_195699_6_, boolean p_195699_7_, boolean p_195699_8_) { - } - - /** - * getPixelRGBA x y - */ - public int func_195709_a(int p_195709_1_, int p_195709_2_) { - return 0; - } - - /** - * setPixelRGBA x y rgba - */ - public void func_195700_a(int p_195700_1_, int p_195700_2_, int p_195700_3_) { - } - - public void close() { - } -} diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/texture/SimpleTexture.java b/Dummy/src/main/java/net/minecraft/client/renderer/texture/SimpleTexture.java deleted file mode 100644 index d2c86a4e..00000000 --- a/Dummy/src/main/java/net/minecraft/client/renderer/texture/SimpleTexture.java +++ /dev/null @@ -1,11 +0,0 @@ -package net.minecraft.client.renderer.texture; - -import net.minecraft.client.resources.IResourceManager; -import net.minecraft.resources.Identifier; -import net.minecraft.util.ResourceLocation; - -public class SimpleTexture extends ReloadableTexture implements ITextureObject { - public SimpleTexture(ResourceLocation location) {} - public SimpleTexture(Identifier identifier) {} - public TextureContents loadContents(IResourceManager resourceManager) { return null; } -} diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/texture/SkinTextureDownloader.java b/Dummy/src/main/java/net/minecraft/client/renderer/texture/SkinTextureDownloader.java deleted file mode 100644 index 11dd19eb..00000000 --- a/Dummy/src/main/java/net/minecraft/client/renderer/texture/SkinTextureDownloader.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.minecraft.client.renderer.texture; - -import java.nio.file.Path; -import java.util.concurrent.CompletableFuture; - -import net.minecraft.util.ResourceLocation; - -public class SkinTextureDownloader { - public static CompletableFuture downloadAndRegisterSkin(ResourceLocation location, Path path, String string, boolean b) { return null; } - public static NativeImage processLegacySkin(NativeImage nativeImage, String url) { return null; } - public static NativeImage lambda$downloadAndRegisterSkin$0(Path path, String url, boolean b) { return null; } -} diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/texture/Texture.java b/Dummy/src/main/java/net/minecraft/client/renderer/texture/Texture.java deleted file mode 100644 index 19baa195..00000000 --- a/Dummy/src/main/java/net/minecraft/client/renderer/texture/Texture.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.minecraft.client.renderer.texture; - -// exclude -public interface Texture { -} diff --git a/Dummy/src/main/java/net/minecraft/client/renderer/texture/TextureManager.java b/Dummy/src/main/java/net/minecraft/client/renderer/texture/TextureManager.java deleted file mode 100644 index b04037f9..00000000 --- a/Dummy/src/main/java/net/minecraft/client/renderer/texture/TextureManager.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.minecraft.client.renderer.texture; - -import net.minecraft.resources.Identifier; -import net.minecraft.util.ResourceLocation; - -public class TextureManager { - public ITextureObject getTexture(ResourceLocation location) { return null; } - public void loadTexture(ResourceLocation location, AbstractTexture texture) {} - public boolean loadTexture(ResourceLocation location, ITextureObject texture) { return false; } - public void registerAndLoad(ResourceLocation resourceLocation, ReloadableTexture reloadableTexture) {} - public void registerAndLoad(Identifier identifier, ReloadableTexture reloadableTexture) {} -} diff --git a/Dummy/src/main/java/net/minecraft/client/resources/DefaultPlayerSkin.java b/Dummy/src/main/java/net/minecraft/client/resources/DefaultPlayerSkin.java deleted file mode 100644 index 081e50b8..00000000 --- a/Dummy/src/main/java/net/minecraft/client/resources/DefaultPlayerSkin.java +++ /dev/null @@ -1,9 +0,0 @@ -package net.minecraft.client.resources; - -import java.util.UUID; - -import net.minecraft.util.ResourceLocation; - -public class DefaultPlayerSkin { - public static ResourceLocation getDefaultSkin(UUID uuid) { return null; } -} diff --git a/Dummy/src/main/java/net/minecraft/client/resources/IResource.java b/Dummy/src/main/java/net/minecraft/client/resources/IResource.java deleted file mode 100644 index 0f0239a0..00000000 --- a/Dummy/src/main/java/net/minecraft/client/resources/IResource.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.minecraft.client.resources; - -import java.io.InputStream; - -public interface IResource { - InputStream getInputStream(); -} diff --git a/Dummy/src/main/java/net/minecraft/client/resources/IResourceManager.java b/Dummy/src/main/java/net/minecraft/client/resources/IResourceManager.java deleted file mode 100644 index f095f46f..00000000 --- a/Dummy/src/main/java/net/minecraft/client/resources/IResourceManager.java +++ /dev/null @@ -1,11 +0,0 @@ -package net.minecraft.client.resources; - -import java.util.Optional; - -import net.minecraft.resources.Identifier; -import net.minecraft.util.ResourceLocation; - -public interface IResourceManager { - IResource getResource(ResourceLocation location); - Optional getResource(Identifier Identifier); -} diff --git a/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$1.java b/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$1.java deleted file mode 100644 index 0e1971b5..00000000 --- a/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$1.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.minecraft.client.resources; - -import java.util.concurrent.CompletableFuture; - -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.minecraft.MinecraftProfileTextures; -import com.mojang.authlib.minecraft.MinecraftSessionService; -import net.minecraft.server.Services; - -public class SkinManager$1 { - public CompletableFuture load(SkinManager$CacheKey cacheKey) { return null; } - public static SkinManager$TextureInfo lambda$load$0(MinecraftSessionService sessionService, GameProfile profile) { return null; } - public static MinecraftProfileTextures lambda$load$0(SkinManager$CacheKey cacheKey, MinecraftSessionService sessionService) { return null; } - public static MinecraftProfileTextures lambda$load$0(SkinManager$CacheKey cacheKey, Services services) { return null; } -} diff --git a/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$ISkinAvailableCallback.java b/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$ISkinAvailableCallback.java deleted file mode 100644 index 641af6a2..00000000 --- a/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$ISkinAvailableCallback.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.minecraft.client.resources; - -import com.mojang.authlib.minecraft.MinecraftProfileTexture; -import net.minecraft.util.ResourceLocation; - -public interface SkinManager$ISkinAvailableCallback { - void onSkinTextureAvailable(MinecraftProfileTexture.Type typeIn, ResourceLocation location, MinecraftProfileTexture profileTexture); -} diff --git a/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$SkinAvailableCallback.java b/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$SkinAvailableCallback.java deleted file mode 100644 index bebaea23..00000000 --- a/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$SkinAvailableCallback.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.minecraft.client.resources; - -import com.mojang.authlib.minecraft.MinecraftProfileTexture; -import net.minecraft.util.ResourceLocation; - -// put into mod jar to prevent https://github.com/cpw/modlauncher/issues/39 -public interface SkinManager$SkinAvailableCallback { - default void skinAvailable(MinecraftProfileTexture.Type typeIn, ResourceLocation location, MinecraftProfileTexture profileTexture) { - this.onSkinTextureAvailable(typeIn, location, profileTexture); - } - - default void onSkinTextureAvailable(MinecraftProfileTexture.Type typeIn, ResourceLocation location, MinecraftProfileTexture profileTexture) { - ((SkinManager$ISkinAvailableCallback) this).onSkinTextureAvailable(typeIn, location, profileTexture); - } -} diff --git a/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$TextureCache.java b/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$TextureCache.java deleted file mode 100644 index 36118455..00000000 --- a/Dummy/src/main/java/net/minecraft/client/resources/SkinManager$TextureCache.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.minecraft.client.resources; - -import java.util.concurrent.CompletableFuture; - -import com.mojang.authlib.minecraft.MinecraftProfileTexture; - -public class SkinManager$TextureCache { - private MinecraftProfileTexture.Type type; - public CompletableFuture registerTexture(MinecraftProfileTexture texture) { return null; } -} diff --git a/Dummy/src/main/java/net/minecraft/client/resources/SkinManager.java b/Dummy/src/main/java/net/minecraft/client/resources/SkinManager.java deleted file mode 100644 index e3fb7c2a..00000000 --- a/Dummy/src/main/java/net/minecraft/client/resources/SkinManager.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.minecraft.client.resources; - -import java.io.File; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.minecraft.MinecraftProfileTexture; -import com.mojang.authlib.minecraft.MinecraftSessionService; -import net.minecraft.client.renderer.texture.TextureManager; -import net.minecraft.util.ResourceLocation; - -public class SkinManager { - public SkinManager(TextureManager textureManagerInstance, File skinCacheDirectory, MinecraftSessionService sessionService) { } - public ResourceLocation loadSkin(MinecraftProfileTexture profileTexture, MinecraftProfileTexture.Type textureType, SkinManager$SkinAvailableCallback skinAvailableCallback) { return null; } - public void loadProfileTextures(GameProfile profile, SkinManager$SkinAvailableCallback skinAvailableCallback, boolean requireSecure) { } - public Map loadSkinFromCache(GameProfile profile) { return null; } - public CompletableFuture get(GameProfile profile) { return null; } - public CompletableFuture getOrLoad(GameProfile profile) { return null; } - public void /* lambda$loadProfileTextures$1 */ func_210275_a(GameProfile profile, boolean b, SkinManager$SkinAvailableCallback callback) {} - public void /* lambda$null$0 */ func_210276_a(Map map, SkinManager$SkinAvailableCallback callback) {} -} diff --git a/Dummy/src/main/java/net/minecraft/client/resources/data/TextureMetadataSection.java b/Dummy/src/main/java/net/minecraft/client/resources/data/TextureMetadataSection.java deleted file mode 100644 index 2a60b17f..00000000 --- a/Dummy/src/main/java/net/minecraft/client/resources/data/TextureMetadataSection.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.minecraft.client.resources.data; - -public class TextureMetadataSection { - -} diff --git a/Dummy/src/main/java/net/minecraft/resources/IResource.java b/Dummy/src/main/java/net/minecraft/resources/IResource.java deleted file mode 100644 index 7c833ead..00000000 --- a/Dummy/src/main/java/net/minecraft/resources/IResource.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.minecraft.resources; - -// exclude -public interface IResource { - -} diff --git a/Dummy/src/main/java/net/minecraft/resources/IResourceManager.java b/Dummy/src/main/java/net/minecraft/resources/IResourceManager.java deleted file mode 100644 index 89755cf6..00000000 --- a/Dummy/src/main/java/net/minecraft/resources/IResourceManager.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.minecraft.resources; - -// exclude -public interface IResourceManager { - -} diff --git a/Dummy/src/main/java/net/minecraft/resources/Identifier.java b/Dummy/src/main/java/net/minecraft/resources/Identifier.java deleted file mode 100644 index b665fe12..00000000 --- a/Dummy/src/main/java/net/minecraft/resources/Identifier.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.minecraft.resources; - -public class Identifier { - public static Identifier fromNamespaceAndPath(String namespace, String path) { - return null; - } -} diff --git a/Dummy/src/main/java/net/minecraft/util/ResourceLocation.java b/Dummy/src/main/java/net/minecraft/util/ResourceLocation.java deleted file mode 100644 index 7d40c6f8..00000000 --- a/Dummy/src/main/java/net/minecraft/util/ResourceLocation.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.minecraft.util; - -public class ResourceLocation { - public ResourceLocation(String path) {} - public ResourceLocation(String domain, String path) {} -} diff --git a/Dummy/src/main/java/net/minecraft/util/StringUtils.java b/Dummy/src/main/java/net/minecraft/util/StringUtils.java deleted file mode 100644 index 6db0f733..00000000 --- a/Dummy/src/main/java/net/minecraft/util/StringUtils.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.minecraft.util; - -public class StringUtils { - public static String stripControlCodes(String string) { return null; } -} diff --git a/Fabric/Fabric.tsrg b/Fabric/Fabric.tsrg deleted file mode 100644 index 12757b9e..00000000 --- a/Fabric/Fabric.tsrg +++ /dev/null @@ -1,80 +0,0 @@ -customskinloader/fake/itf/IFakeIResource$V1 customskinloader/fake/itf/IFakeIResource$V1 - func_199027_b ()Ljava/io/InputStream; method_14482 -customskinloader/fake/itf/IFakeIResource$V2 customskinloader/fake/itf/IFakeIResource$V2 - open ()Ljava/io/InputStream; method_14482 -customskinloader/fake/itf/IFakeIResourceManager$V1 customskinloader/fake/itf/IFakeIResourceManager$V1 - func_199002_a (Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/resources/IResource; method_14486 - getResource (Lnet/minecraft/util/ResourceLocation;)Ljava/util/Optional; method_14486 -customskinloader/fake/itf/IFakeMinecraft customskinloader/fake/itf/IFakeMinecraft - func_195551_G ()Lnet/minecraft/resources/IResourceManager; method_1478 -customskinloader/fake/itf/IFakeNativeImage customskinloader/fake/itf/IFakeNativeImage - getPixel (II)I method_61940 - setPixel (III)V method_61941 -net/minecraft/client/Minecraft net/minecraft/class_310 - gameDir field_1697 - getCurrentServerData ()Lnet/minecraft/client/multiplayer/ServerData; method_1558 - getMinecraft ()Lnet/minecraft/client/Minecraft; method_1551 - getResourceManager ()Lnet/minecraft/client/resources/IResourceManager; method_1478 - getSkinManager ()Lnet/minecraft/client/resources/SkinManager; method_1582 - getTextureManager ()Lnet/minecraft/client/renderer/texture/TextureManager; method_1531 -net/minecraft/client/gui/GuiPlayerTabOverlay net/minecraft/class_355 -net/minecraft/client/multiplayer/ServerData net/minecraft/class_642 - serverIP field_3761 -net/minecraft/client/renderer/RenderType net/minecraft/class_1921 - func_228644_e_ (Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/RenderType; method_23580 -net/minecraft/client/renderer/IImageBuffer net/minecraft/class_760 - func_195786_a (Lnet/minecraft/client/renderer/texture/NativeImage;)Lnet/minecraft/client/renderer/texture/NativeImage; method_3237 - parseUserSkin (Ljava/awt/image/BufferedImage;)Ljava/awt/image/BufferedImage; func_78432_a - skinAvailable ()V method_3238 -net/minecraft/client/renderer/ThreadDownloadImageData net/minecraft/class_1046 -net/minecraft/client/renderer/entity/RenderPlayer net/minecraft/class_1007 -net/minecraft/client/renderer/entity/layers/LayerCape net/minecraft/class_972 -net/minecraft/client/renderer/rendertype/RenderType net/minecraft/class_1921 -net/minecraft/client/renderer/rendertype/RenderTypes net/minecraft/class_12249 - entityTranslucent (Lnet/minecraft/resources/Identifier;)Lnet/minecraft/client/renderer/rendertype/RenderType; method_76000 -net/minecraft/client/renderer/texture/AbstractTexture net/minecraft/class_1044 -net/minecraft/client/renderer/texture/ITextureObject net/minecraft/class_1062 -net/minecraft/client/renderer/texture/NativeImage net/minecraft/class_1011 - func_195699_a (IIIIIIZZ)V method_4304 - func_195700_a (III)V method_4305 - func_195702_a ()I method_4307 - func_195703_a (Lnet/minecraft/client/renderer/texture/NativeImage;)V method_4317 - func_195709_a (II)I method_4315 - func_195713_a (Ljava/io/InputStream;)Lnet/minecraft/client/renderer/texture/NativeImage; method_4309 - func_195714_b ()I method_4323 - func_195715_a (IIIII)V method_4326 -net/minecraft/client/renderer/texture/ReloadableTexture net/minecraft/class_10537 -net/minecraft/client/renderer/texture/SkinTextureDownloader net/minecraft/class_10538 -net/minecraft/client/renderer/texture/SimpleTexture net/minecraft/class_1049 - loadContents (Lnet/minecraft/client/resources/IResourceManager;)Lnet/minecraft/client/renderer/texture/TextureContents; method_65809 -net/minecraft/client/renderer/texture/TextureContents net/minecraft/class_10539 -net/minecraft/client/renderer/texture/TextureManager net/minecraft/class_1060 - getTexture (Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/texture/ITextureObject; method_4619 - loadTexture (Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/client/renderer/texture/AbstractTexture;)V method_4616 - loadTexture (Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/client/renderer/texture/ITextureObject;)Z method_4616 - registerAndLoad (Lnet/minecraft/resources/Identifier;Lnet/minecraft/client/renderer/texture/ReloadableTexture;)V method_65876 - registerAndLoad (Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/client/renderer/texture/ReloadableTexture;)V method_65876 -net/minecraft/client/resources/DefaultPlayerSkin net/minecraft/class_1068 - getDefaultSkin (Ljava/util/UUID;)Lnet/minecraft/util/ResourceLocation; method_4648 -net/minecraft/client/resources/IResource net/minecraft/class_3298 - getInputStream ()Ljava/io/InputStream; method_14482 -net/minecraft/client/resources/IResourceManager net/minecraft/class_3300 - getResource (Lnet/minecraft/resources/Identifier;)Ljava/util/Optional; method_14486 - getResource (Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/resources/IResource; method_14486 -net/minecraft/client/resources/SkinManager net/minecraft/class_1071 - loadProfileTextures (Lcom/mojang/authlib/GameProfile;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;Z)V method_4652 - loadSkin (Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;)Lnet/minecraft/util/ResourceLocation; method_4656 -net/minecraft/client/resources/SkinManager$1 net/minecraft/class_1071$1 -net/minecraft/client/resources/SkinManager$CacheKey net/minecraft/class_1071$class_8686 -net/minecraft/client/resources/SkinManager$SkinAvailableCallback net/minecraft/class_1071$class_1072 - skinAvailable (Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/util/ResourceLocation;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;)V onSkinTextureAvailable -net/minecraft/client/resources/SkinManager$TextureCache net/minecraft/class_1071$class_8687 -net/minecraft/client/resources/data/TextureMetadataSection net/minecraft/class_1084 -net/minecraft/resources/IResource net/minecraft/class_3298 -net/minecraft/resources/IResourceManager net/minecraft/class_3300 -net/minecraft/resources/Identifier net/minecraft/class_2960 - fromNamespaceAndPath (Ljava/lang/String;Ljava/lang/String;)Lnet/minecraft/resources/Identifier; method_60655 -net/minecraft/server/Services net/minecraft/class_7497 -net/minecraft/util/ResourceLocation net/minecraft/class_2960 -net/minecraft/util/StringUtils net/minecraft/class_3544 - stripControlCodes (Ljava/lang/String;)Ljava/lang/String; method_15440 diff --git a/Fabric/build.gradle b/Fabric/build.gradle deleted file mode 100644 index 19112a77..00000000 --- a/Fabric/build.gradle +++ /dev/null @@ -1,41 +0,0 @@ - -jar { - exclude 'net/minecraft/client/resources/**' -} -sourceJar { - exclude 'net/**' -} - -repositories { - maven { - name = "fabric" - url = "https://maven.fabricmc.net/" - } - maven { - name = "sponge" - url = "https://repo.spongepowered.org/repository/maven-public/" - } -} - -dependencies { - implementation project(':Dummy') - // This is the minimum version number that the mod should dependent in the development environment. - implementation "net.fabricmc:fabric-loader:0.12.0" -} - -import customskinloader.gradle.util.RemapUtil -import customskinloader.gradle.util.SourceUtil - -apply plugin: 'org.spongepowered.mixin' - -apply from: rootProject.file("buildSrc/patch.gradle") -patchMixin() - -mixin { - add sourceSets.main, "mixins.customskinloader.refmap.json" - reobfSrgFile = "build/mixin.srg" - reobfNotchSrgFile = "build/mixin.srg" -} - -SourceUtil.addDependencies project, project(":Common"), project(":Vanilla/Common") -RemapUtil.remapSources project diff --git a/Fabric/build.properties b/Fabric/build.properties deleted file mode 100644 index 81109c5e..00000000 --- a/Fabric/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -dependencies=Fabric|1.14,1.14.1,1.14.2,1.14.3,1.14.4,1.15,1.15.1,1.15.2,1.16,1.16.1,1.16.2,1.16.3,1.16.4,1.16.5,1.17,1.17.1,1.18,1.18.1,1.18.2,1.19,1.19.1,1.19.2,1.19.3,1.19.4,1.20,1.20.1,1.20.2,1.20.3,1.20.4,1.20.5,1.20.6,1.21,1.21.1,1.21.2,1.21.3,1.21.4,1.21.5,1.21.6,1.21.7,1.21.8,1.21.9,1.21.10,1.21.11;\ - Quilt|1.14,1.14.1,1.14.2,1.14.3,1.14.4,1.15,1.15.1,1.15.2,1.16,1.16.1,1.16.2,1.16.3,1.16.4,1.16.5,1.17,1.17.1,1.18,1.18.1,1.18.2,1.19,1.19.1,1.19.2,1.19.3,1.19.4,1.20,1.20.1,1.20.2,1.20.3,1.20.4,1.20.5,1.20.6,1.21,1.21.1,1.21.2,1.21.3,1.21.4,1.21.5,1.21.6,1.21.7,1.21.8,1.21.9,1.21.10,1.21.11 -java_full_versions=8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 -#forge gradle needs forge version -forge_mc_version=1.12.2 -forge_version=14.23.5.2768 -mappings_version=stable_39 -srg_notch_map=Fabric.tsrg diff --git a/Fabric/mixin.tsrg b/Fabric/mixin.tsrg deleted file mode 100644 index 2cfe570d..00000000 --- a/Fabric/mixin.tsrg +++ /dev/null @@ -1,67 +0,0 @@ -com/mojang/blaze3d/matrix/MatrixStack net/minecraft/class_4587 -net/minecraft/client/Minecraft net/minecraft/class_310 - isIntegratedServerRunning ()Z method_1542 -net/minecraft/client/entity/AbstractClientPlayer net/minecraft/class_742 -net/minecraft/client/gui/Gui net/minecraft/class_332 -net/minecraft/client/gui/GuiGraphicsExtractor net/minecraft/client/gui/GuiGraphicsExtractor -net/minecraft/client/gui/GuiPlayerTabOverlay net/minecraft/class_355 - renderPlayerlist (ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V method_1919 - renderPlayerlist (Lcom/mojang/blaze3d/matrix/MatrixStack;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V method_1919 - renderPlayerlist (Lnet/minecraft/client/gui/Gui;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V method_1919 - renderPlayerlist (Lnet/minecraft/client/gui/GuiGraphicsExtractor;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V extractRenderState -net/minecraft/client/model/ModelRenderer net/minecraft/class_630 -net/minecraft/client/renderer/IImageBuffer net/minecraft/class_760 -net/minecraft/client/renderer/IRenderTypeBuffer net/minecraft/class_4597 -net/minecraft/client/renderer/RenderType net/minecraft/class_1921 - getEntitySolid (Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/RenderType; method_23572 -net/minecraft/client/renderer/SubmitNodeCollector net/minecraft/class_11659 -net/minecraft/client/renderer/ThreadDownloadImageData net/minecraft/class_1046 - processTask field_20843 - loadTexture (Ljava/io/InputStream;)Lnet/minecraft/client/renderer/texture/NativeImage; method_22795 - processLegacySkin (Lnet/minecraft/client/renderer/texture/NativeImage;)Lnet/minecraft/client/renderer/texture/NativeImage; method_22798 -net/minecraft/client/renderer/entity/RenderPlayer net/minecraft/class_1007 - renderItem (Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;ILnet/minecraft/client/entity/AbstractClientPlayer;Lnet/minecraft/client/model/ModelRenderer;Lnet/minecraft/client/model/ModelRenderer;)V method_23205 - renderItem (Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;Lnet/minecraft/client/entity/AbstractClientPlayer;Lnet/minecraft/client/model/ModelRenderer;Lnet/minecraft/client/model/ModelRenderer;)V method_23205 -net/minecraft/client/renderer/entity/layers/LayerCape net/minecraft/class_972 - doRenderLayer (Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;ILnet/minecraft/client/entity/AbstractClientPlayer;FFFFFF)V method_4177 - doRenderLayer (Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;ILnet/minecraft/client/entity/AbstractClientPlayer;FFFFFFF)V method_4177 - doRenderLayer (Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;ILnet/minecraft/client/renderer/entity/state/PlayerRenderState;FF)V method_4177 - doRenderLayer (Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;ILnet/minecraft/client/renderer/entity/state/AvatarRenderState;FF)V method_4177 -net/minecraft/client/renderer/entity/state/AvatarRenderState net/minecraft/class_10055 -net/minecraft/client/renderer/entity/state/PlayerRenderState net/minecraft/class_10055 -net/minecraft/client/renderer/rendertype/RenderType net/minecraft/class_1921 -net/minecraft/client/renderer/rendertype/RenderTypes net/minecraft/class_12249 - entitySolid (Lnet/minecraft/resources/Identifier;)Lnet/minecraft/client/renderer/rendertype/RenderType; method_75982 -net/minecraft/client/renderer/texture/NativeImage net/minecraft/class_1011 -net/minecraft/client/renderer/texture/SkinTextureDownloader net/minecraft/class_10538 - downloadAndRegisterSkin (Lnet/minecraft/resources/Identifier;Ljava/nio/file/Path;Ljava/lang/String;Z)Ljava/util/concurrent/CompletableFuture; method_65861 - downloadAndRegisterSkin (Lnet/minecraft/util/ResourceLocation;Ljava/nio/file/Path;Ljava/lang/String;Z)Ljava/util/concurrent/CompletableFuture; method_65861 - lambda$downloadAndRegisterSkin$0 (Ljava/nio/file/Path;Ljava/lang/String;Z)Lnet/minecraft/client/renderer/texture/NativeImage; method_65866 - lambda$downloadAndRegisterSkin$0 (Ljava/nio/file/Path;Lnet/minecraft/core/ClientAsset$DownloadedTexture;Z)Lnet/minecraft/client/renderer/texture/NativeImage; method_65866 - processLegacySkin (Lnet/minecraft/client/renderer/texture/NativeImage;Ljava/lang/String;)Lnet/minecraft/client/renderer/texture/NativeImage; method_65863 -net/minecraft/client/renderer/texture/TextureManager net/minecraft/class_1060 -net/minecraft/client/resources/SkinManager net/minecraft/class_1071 - func_210275_a (Lcom/mojang/authlib/GameProfile;ZLnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)V method_4653 - func_210276_a (Ljava/util/Map;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)V method_4655 - get (Lcom/mojang/authlib/GameProfile;)Ljava/util/concurrent/CompletableFuture; method_52863_ - getOrLoad (Lcom/mojang/authlib/GameProfile;)Ljava/util/concurrent/CompletableFuture; method_52863 - loadProfileTextures (Lcom/mojang/authlib/GameProfile;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;Z)V method_4652 - loadSkin (Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)Lnet/minecraft/util/ResourceLocation; method_4651 - loadSkinFromCache (Lcom/mojang/authlib/GameProfile;)Ljava/util/Map; method_4654 -net/minecraft/client/resources/SkinManager$1 net/minecraft/class_1071$1 - lambda$load$0 (Lcom/mojang/authlib/minecraft/MinecraftSessionService;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/client/resources/SkinManager$TextureInfo; method_52867 - lambda$load$0 (Lnet/minecraft/client/resources/SkinManager$CacheKey;Lcom/mojang/authlib/minecraft/MinecraftSessionService;)Lcom/mojang/authlib/minecraft/MinecraftProfileTextures; method_54647 - lambda$load$0 (Lnet/minecraft/client/resources/SkinManager$CacheKey;Lnet/minecraft/server/Services;)Lcom/mojang/authlib/minecraft/MinecraftProfileTextures; method_54647 - load (Lnet/minecraft/client/resources/SkinManager$CacheKey;)Ljava/util/concurrent/CompletableFuture; method_52868 -net/minecraft/client/resources/SkinManager$CacheKey net/minecraft/class_1071$class_8686 -net/minecraft/client/resources/SkinManager$SkinAvailableCallback net/minecraft/class_1071$class_1072 -net/minecraft/client/resources/SkinManager$TextureCache net/minecraft/class_1071$class_8687 - type field_45641 - registerTexture (Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;)Ljava/util/concurrent/CompletableFuture; method_52873 -net/minecraft/client/resources/SkinManager$TextureInfo net/minecraft/class_1071$class_8688 -net/minecraft/core/ClientAsset$DownloadedTexture net/minecraft/class_12079$class_12080 -net/minecraft/resources/Identifier net/minecraft/class_2960 -net/minecraft/scoreboard/ScoreObjective net/minecraft/class_266 -net/minecraft/scoreboard/Scoreboard net/minecraft/class_269 -net/minecraft/server/Services net/minecraft/class_7497 -net/minecraft/util/ResourceLocation net/minecraft/class_2960 diff --git a/Fabric/src/main/java/customskinloader/fabric/DevEnvRemapper.java b/Fabric/src/main/java/customskinloader/fabric/DevEnvRemapper.java deleted file mode 100644 index 4131e9d7..00000000 --- a/Fabric/src/main/java/customskinloader/fabric/DevEnvRemapper.java +++ /dev/null @@ -1,123 +0,0 @@ -package customskinloader.fabric; - -import java.lang.reflect.Field; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import com.google.common.collect.Lists; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.impl.FabricLoaderImpl; -import net.fabricmc.loader.impl.game.patch.GameTransformer; -import org.apache.commons.io.IOUtils; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Type; -import org.objectweb.asm.commons.ClassRemapper; -import org.objectweb.asm.commons.SimpleRemapper; -import org.objectweb.asm.tree.ClassNode; - -public class DevEnvRemapper extends SimpleRemapper { - // key: correct method owner names, - // value: - // left: fake method owner names, - // right: other classes which contain fake methods. - private static Map, List>> remappedClasses = new HashMap<>(); - - static { - remappedClasses.put( - "net.minecraft.class_310", - new AbstractMap.SimpleEntry<>( - Lists.newArrayList("customskinloader.fake.itf.IFakeMinecraft"), - Lists.newArrayList("customskinloader.fake.itf.FakeInterfaceManager") - ) - ); - remappedClasses.put( - "net.minecraft.class_1011", - new AbstractMap.SimpleEntry<>( - Lists.newArrayList("customskinloader.fake.itf.IFakeNativeImage"), - Lists.newArrayList("customskinloader.fake.itf.FakeInterfaceManager") - ) - ); - remappedClasses.put( - "net.minecraft.class_3298", - new AbstractMap.SimpleEntry<>( - Lists.newArrayList("customskinloader.fake.itf.IFakeIResource$V1", "customskinloader.fake.itf.IFakeIResource$V2"), - Lists.newArrayList("customskinloader.fake.itf.FakeInterfaceManager") - ) - ); - remappedClasses.put( - "net.minecraft.class_3300", - new AbstractMap.SimpleEntry<>( - Lists.newArrayList("customskinloader.fake.itf.IFakeIResourceManager$V1", "customskinloader.fake.itf.IFakeIResourceManager$V2"), - Lists.newArrayList("customskinloader.fake.itf.FakeInterfaceManager") - ) - ); - } - - @SuppressWarnings("unchecked") - public static void initRemapper() { - try { - if (FabricLoader.getInstance().isDevelopmentEnvironment()) { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - Field patchedClassesField = GameTransformer.class.getDeclaredField("patchedClasses"); - patchedClassesField.setAccessible(true); - Map patchedClasses = (Map) patchedClassesField.get(FabricLoaderImpl.INSTANCE.getGameProvider().getEntrypointTransformer()); - - for (Map.Entry, List>> entry : remappedClasses.entrySet()) { - List targetClasses = new ArrayList<>(entry.getValue().getKey()); - targetClasses.addAll(entry.getValue().getValue()); - for (String clazz : targetClasses) { - byte[] classBytes = patchedClasses.get(clazz); - if (classBytes == null) { - classBytes = IOUtils.toByteArray(Objects.requireNonNull(cl.getResourceAsStream(clazz.replace(".", "/") + ".class"))); - } - - patchedClasses.put(clazz, remapClass(entry.getKey(), classBytes)); - } - } - } - } catch (Throwable t) { - MixinConfigPlugin.logger.warning(t); - } - } - - // Remap all method names with specific method owner name. - public static byte[] remapClass(String owner, byte[] bytes) { - ClassNode cn = new ClassNode(); - new ClassReader(bytes).accept(new ClassRemapper(cn, new DevEnvRemapper(owner)), ClassReader.EXPAND_FRAMES); - - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); - cn.accept(cw); - return cw.toByteArray(); - } - - private final String owner; - private final SimpleRemapper remapper = new SimpleRemapper(new HashMap<>()) { - @Override - public String map(String key) { - return FabricLoader.getInstance().getMappingResolver().unmapClassName("intermediary", Type.getType("L" + key + ";").getClassName()).replace(".", "/"); - } - }; - - public DevEnvRemapper(String owner) { - super(new HashMap<>()); - this.owner = owner; - } - - @Override - public String mapMethodName(String owner, String name, String desc) { - // Method desc should be unmapped in the development environment. - desc = this.remapper.mapDesc(desc); - - String s = this.isFakeOwner(owner) ? FabricLoader.getInstance().getMappingResolver().mapMethodName("intermediary", this.owner, name, desc) : name; - return s == null ? name : s; - } - - private boolean isFakeOwner(String owner) { - return remappedClasses.get(this.owner).getKey().contains(owner.replace("/", ".")); - } -} diff --git a/Fabric/src/main/java/customskinloader/fabric/MixinConfigPlugin.java b/Fabric/src/main/java/customskinloader/fabric/MixinConfigPlugin.java deleted file mode 100644 index d84b3288..00000000 --- a/Fabric/src/main/java/customskinloader/fabric/MixinConfigPlugin.java +++ /dev/null @@ -1,11 +0,0 @@ -package customskinloader.fabric; - -public class MixinConfigPlugin extends customskinloader.mixin.core.MixinConfigPlugin { - @Override - public void onLoad(String mixinPackage) { - super.onLoad(mixinPackage); - - // This mod will remap extra classes when in the development environment. - DevEnvRemapper.initRemapper(); - } -} diff --git a/Fabric/src/main/resources/customskinloader.accesswidener b/Fabric/src/main/resources/customskinloader.accesswidener deleted file mode 100644 index bcd66a55..00000000 --- a/Fabric/src/main/resources/customskinloader.accesswidener +++ /dev/null @@ -1,5 +0,0 @@ -accessWidener v2 intermediary -extendable class net/minecraft/class_1011 # 24w46a+ -extendable class net/minecraft/class_1071$class_8686 # 23w31a+ -accessible method net/minecraft/class_1071$class_8686 (Ljava/util/UUID;Lcom/mojang/authlib/properties/Property;)V # 23w42a+ -accessible method net/minecraft/class_2960 (Ljava/lang/String;Ljava/lang/String;)V # 24w18a+ diff --git a/Fabric/src/main/resources/fabric.mod.json b/Fabric/src/main/resources/fabric.mod.json deleted file mode 100644 index dc021952..00000000 --- a/Fabric/src/main/resources/fabric.mod.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "schemaVersion": 1, - "id": "customskinloader", - "version": "${modFullVersion}", - - "name": "CustomSkinLoader", - "description": "Custom Skin Loader for Minecraft", - "authors": ["xfl03", "JLChnToZ", "ZekerZhayard"], - "contact": { - "homepage": "https://github.com/xfl03/MCCustomSkinLoader", - "issues": "https://github.com/xfl03/MCCustomSkinLoader/issues", - "sources": "https://github.com/xfl03/MCCustomSkinLoader" - }, - "license": "GPL-3.0-only", - - "environment": "client", - "mixins": [ - "mixins.customskinloader.json" - ], - "accessWidener": "customskinloader.accesswidener", - - "depends": { - "minecraft": "<=1.21.11" - } -} diff --git a/Fabric/src/main/resources/mixins.customskinloader.json b/Fabric/src/main/resources/mixins.customskinloader.json deleted file mode 100644 index 1da3ea7e..00000000 --- a/Fabric/src/main/resources/mixins.customskinloader.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "compatibilityLevel": "JAVA_8", - "mixinPriority": 1010, - "package": "customskinloader.mixin", - "client": [ - "MixinGuiPlayerTabOverlay", - "MixinIImageBuffer", - "MixinIResource", - "MixinIResourceManager$V1", - "MixinLayerCape$V1", - "MixinLayerCape$V2", - "MixinMinecraft", - "MixinNativeImage", - "MixinRenderPlayer", - "MixinResourceLocation$V1", - "MixinResourceLocation$V2", - "MixinSkinManager$1", - "MixinSkinManager$TextureCache", - "MixinSkinManager$V1", - "MixinSkinManager$V2", - "MixinSkinManager$V3", - "MixinSkinTextureDownloader$V1", - "MixinSkinTextureDownloader$V2", - "MixinSkinTextureDownloader$V3", - "MixinThreadDownloadImageData$V1", - "MixinThreadDownloadImageData$V2" - ], - "refmap": "mixins.customskinloader.refmap.json", - "plugin": "customskinloader.fabric.MixinConfigPlugin", - "injectors": { - "defaultRequire": 1 - } -} diff --git a/Forge/Common/build.gradle b/Forge/Common/build.gradle deleted file mode 100644 index e7056bf8..00000000 --- a/Forge/Common/build.gradle +++ /dev/null @@ -1,5 +0,0 @@ -// This subproject is only to provide dependency for other subprojects. - -dependencies { - implementation project(':Dummy') -} diff --git a/Forge/Common/build.properties b/Forge/Common/build.properties deleted file mode 100644 index 2c5a53ad..00000000 --- a/Forge/Common/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -forge_mc_version=1.12.2 -forge_version=14.23.5.2768 -mappings_version=stable_39 -# this subproject is only to provide dependency for other subprojects. -is_real_project=false diff --git a/Forge/Common/src/main/java/cpw/mods/cl/JarModuleFinder.java b/Forge/Common/src/main/java/cpw/mods/cl/JarModuleFinder.java deleted file mode 100644 index bf383a0c..00000000 --- a/Forge/Common/src/main/java/cpw/mods/cl/JarModuleFinder.java +++ /dev/null @@ -1,15 +0,0 @@ -package cpw.mods.cl; - -import java.lang.module.ModuleFinder; - -import cpw.mods.jarhandling.SecureJar; - -public class JarModuleFinder implements ModuleFinder { - public static JarModuleFinder of(SecureJar... jars) { - return null; - } - - public static class JarModuleReference { - - } -} diff --git a/Forge/Common/src/main/java/cpw/mods/jarhandling/SecureJar.java b/Forge/Common/src/main/java/cpw/mods/jarhandling/SecureJar.java deleted file mode 100644 index d561f2bd..00000000 --- a/Forge/Common/src/main/java/cpw/mods/jarhandling/SecureJar.java +++ /dev/null @@ -1,9 +0,0 @@ -package cpw.mods.jarhandling; - -import java.nio.file.Path; - -public interface SecureJar { - static SecureJar from(final Path... paths) { - return null; - } -} diff --git a/Forge/Common/src/main/java/java/lang/ModuleLayer.java b/Forge/Common/src/main/java/java/lang/ModuleLayer.java deleted file mode 100644 index adeb4599..00000000 --- a/Forge/Common/src/main/java/java/lang/ModuleLayer.java +++ /dev/null @@ -1,13 +0,0 @@ -package java.lang; - -import java.lang.module.Configuration; - -public class ModuleLayer { - public static ModuleLayer boot() { - return null; - } - - public Configuration configuration() { - return null; - } -} diff --git a/Forge/Common/src/main/java/java/lang/module/Configuration.java b/Forge/Common/src/main/java/java/lang/module/Configuration.java deleted file mode 100644 index 04e1a19f..00000000 --- a/Forge/Common/src/main/java/java/lang/module/Configuration.java +++ /dev/null @@ -1,19 +0,0 @@ -package java.lang.module; - -import java.util.Collection; -import java.util.List; -import java.util.Optional; - -public class Configuration { - public static Configuration resolve(ModuleFinder before, List parents, ModuleFinder after, Collection roots) { - return null; - } - - public static Configuration empty() { - return null; - } - - public Optional findModule(String name) { - return null; - } -} diff --git a/Forge/Common/src/main/java/java/lang/module/ModuleFinder.java b/Forge/Common/src/main/java/java/lang/module/ModuleFinder.java deleted file mode 100644 index 33d461db..00000000 --- a/Forge/Common/src/main/java/java/lang/module/ModuleFinder.java +++ /dev/null @@ -1,5 +0,0 @@ -package java.lang.module; - -public interface ModuleFinder { - -} diff --git a/Forge/Common/src/main/java/java/lang/module/ModuleReference.java b/Forge/Common/src/main/java/java/lang/module/ModuleReference.java deleted file mode 100644 index 8eeb1cc1..00000000 --- a/Forge/Common/src/main/java/java/lang/module/ModuleReference.java +++ /dev/null @@ -1,5 +0,0 @@ -package java.lang.module; - -public class ModuleReference { - -} diff --git a/Forge/Common/src/main/java/java/lang/module/ResolvedModule.java b/Forge/Common/src/main/java/java/lang/module/ResolvedModule.java deleted file mode 100644 index f4d91c8c..00000000 --- a/Forge/Common/src/main/java/java/lang/module/ResolvedModule.java +++ /dev/null @@ -1,7 +0,0 @@ -package java.lang.module; - -public class ResolvedModule { - public ModuleReference reference() { - return null; - } -} diff --git a/Forge/Common/src/main/java/net/minecraftforge/fml/common/asm/transformers/deobf/FMLDeobfuscatingRemapper.java b/Forge/Common/src/main/java/net/minecraftforge/fml/common/asm/transformers/deobf/FMLDeobfuscatingRemapper.java deleted file mode 100644 index 4cd5ec71..00000000 --- a/Forge/Common/src/main/java/net/minecraftforge/fml/common/asm/transformers/deobf/FMLDeobfuscatingRemapper.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.minecraftforge.fml.common.asm.transformers.deobf; - -import org.objectweb.asm.commons.Remapper; - -public class FMLDeobfuscatingRemapper extends Remapper { - public static FMLDeobfuscatingRemapper INSTANCE; -} diff --git a/Forge/Common/src/main/java/net/minecraftforge/fml/relauncher/CoreModManager.java b/Forge/Common/src/main/java/net/minecraftforge/fml/relauncher/CoreModManager.java deleted file mode 100644 index 919b628f..00000000 --- a/Forge/Common/src/main/java/net/minecraftforge/fml/relauncher/CoreModManager.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.minecraftforge.fml.relauncher; - -import java.util.List; - -public class CoreModManager { - public static List getIgnoredMods() { return null; } -} diff --git a/Forge/Common/src/main/java/net/minecraftforge/fml/relauncher/FMLLaunchHandler.java b/Forge/Common/src/main/java/net/minecraftforge/fml/relauncher/FMLLaunchHandler.java deleted file mode 100644 index f469e6f8..00000000 --- a/Forge/Common/src/main/java/net/minecraftforge/fml/relauncher/FMLLaunchHandler.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.minecraftforge.fml.relauncher; - -public class FMLLaunchHandler { - public static Side side() { return null; } -} diff --git a/Forge/Common/src/main/java/net/minecraftforge/fml/relauncher/IFMLLoadingPlugin.java b/Forge/Common/src/main/java/net/minecraftforge/fml/relauncher/IFMLLoadingPlugin.java deleted file mode 100644 index 529d68e3..00000000 --- a/Forge/Common/src/main/java/net/minecraftforge/fml/relauncher/IFMLLoadingPlugin.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.minecraftforge.fml.relauncher; - -import java.util.Map; - -public interface IFMLLoadingPlugin { - String[] getASMTransformerClass(); - String getModContainerClass(); - String getSetupClass(); - void injectData(Map data); - String getAccessTransformerClass(); - - @interface Name { - String value(); - } - - @interface SortingIndex { - int value(); - } -} diff --git a/Forge/Common/src/main/java/net/minecraftforge/fml/relauncher/Side.java b/Forge/Common/src/main/java/net/minecraftforge/fml/relauncher/Side.java deleted file mode 100644 index a97d1c4c..00000000 --- a/Forge/Common/src/main/java/net/minecraftforge/fml/relauncher/Side.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.minecraftforge.fml.relauncher; - -public enum Side { - -} diff --git a/Forge/V1/build.gradle b/Forge/V1/build.gradle deleted file mode 100644 index f428770e..00000000 --- a/Forge/V1/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ - -jar { - manifest { - attributes([ - "TweakClass": "customskinloader.forge.ForgeTweaker", - "TweakOrder": -10 - ]) - } -} -sourceJar { - exclude 'net/**' -} - -tasks.signJar { - // To prevent modlauncher from checking signer information about Minecraft classes in CustomSkinLoader jar after forge-1.16.3-34.1.27. - exclude "net/minecraft/client/**" -} - -dependencies { - implementation project(':Dummy') - implementation project(":Forge/Common") -} - -minecraft { - replaceIn 'ForgeMod.java' -} - -import customskinloader.gradle.util.SourceUtil - -SourceUtil.addDependencies project, project(":Common") diff --git a/Forge/V1/build.properties b/Forge/V1/build.properties deleted file mode 100644 index 3a0c9821..00000000 --- a/Forge/V1/build.properties +++ /dev/null @@ -1,6 +0,0 @@ -dependencies=Forge|1.8,1.8.8,1.8.9,1.9,1.9.4,1.10,1.10.2,1.11,1.11.2,1.12,1.12.1,1.12.2,1.13.2,1.14.2,1.14.3,1.14.4,1.15,1.15.1,1.15.2,1.16.1,1.16.2,1.16.3,1.16.4,1.16.5 -java_full_versions=8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 -#forge gradle needs forge version -forge_mc_version=1.12.2 -forge_version=14.23.5.2768 -mappings_version=stable_39 diff --git a/Forge/V1/src/main/java/customskinloader/forge/ForgeDevTweaker.java b/Forge/V1/src/main/java/customskinloader/forge/ForgeDevTweaker.java deleted file mode 100644 index 7092f7e3..00000000 --- a/Forge/V1/src/main/java/customskinloader/forge/ForgeDevTweaker.java +++ /dev/null @@ -1,35 +0,0 @@ -package customskinloader.forge; - -import java.io.File; -import java.util.List; - -import net.minecraft.launchwrapper.ITweaker; -import net.minecraft.launchwrapper.Launch; -import net.minecraft.launchwrapper.LaunchClassLoader; - -/** - * The tweaker for development environment. - */ -public class ForgeDevTweaker implements ITweaker { - @Override - public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { - - } - - @Override - @SuppressWarnings("unchecked") - public void injectIntoClassLoader(LaunchClassLoader classLoader) { - // ForgeTweaker should be added after FMLTweaker being initialized. - ((List) Launch.blackboard.get("TweakClasses")).add(ForgeTweaker.class.getName()); - } - - @Override - public String getLaunchTarget() { - return ""; - } - - @Override - public String[] getLaunchArguments() { - return new String[0]; - } -} diff --git a/Forge/V1/src/main/java/customskinloader/forge/ForgeMod.java b/Forge/V1/src/main/java/customskinloader/forge/ForgeMod.java deleted file mode 100644 index 68bbc5f9..00000000 --- a/Forge/V1/src/main/java/customskinloader/forge/ForgeMod.java +++ /dev/null @@ -1,33 +0,0 @@ -package customskinloader.forge; - -import net.minecraftforge.fml.ExtensionPoint; -import net.minecraftforge.fml.ModLoadingContext; -import net.minecraftforge.fml.common.Mod; -import org.apache.commons.lang3.tuple.Pair; - -/** - * Forge Mod Container - */ -@Mod( - value = "customskinloader", - modid = "customskinloader", - name = "CustomSkinLoader", - version = "@MOD_FULL_VERSION@", - clientSideOnly = true, - acceptedMinecraftVersions = "[1.8,1.13)", - acceptableRemoteVersions = "*" -)//1.13- -public class ForgeMod { - public ForgeMod() { - try { - this.setExtensionPoint(); - } catch (Throwable ignored) { - // before forge-1.13.2-25.0.103 - } - } - - // Make sure the mod being absent on the other network side does not cause the client to display the server as incompatible after forge-1.13.2-25.0.107. - private void setExtensionPoint() { - ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.DISPLAYTEST, () -> Pair.of(() -> "customskinloader", (remote, isServer) -> isServer)); - } -} diff --git a/Forge/V1/src/main/java/customskinloader/forge/ForgeTweaker.java b/Forge/V1/src/main/java/customskinloader/forge/ForgeTweaker.java deleted file mode 100644 index 6f052b10..00000000 --- a/Forge/V1/src/main/java/customskinloader/forge/ForgeTweaker.java +++ /dev/null @@ -1,56 +0,0 @@ -package customskinloader.forge; - -import java.io.File; -import java.lang.reflect.Field; -import java.util.List; -import java.util.Set; - -import customskinloader.log.LogManager; -import customskinloader.log.Logger; -import net.minecraft.launchwrapper.ITweaker; -import net.minecraft.launchwrapper.Launch; -import net.minecraft.launchwrapper.LaunchClassLoader; - -public class ForgeTweaker implements ITweaker { - public static Logger logger = LogManager.getLogger("Forge"); - - private final static String FML_PLATFORM_INITIALIZER = "customskinloader.forge.platform.IFMLPlatform$FMLPlatformInitializer"; - - @SuppressWarnings("unchecked") - public ForgeTweaker() throws Exception { - // FML is loaded by LaunchClassLoader but we are in AppClassLoader. - if (!this.getClass().getClassLoader().equals(Launch.classLoader)) { - Field field = LaunchClassLoader.class.getDeclaredField("classLoaderExceptions"); - field.setAccessible(true); - - String exclusionName = this.getClass().getName().substring(0, this.getClass().getName().lastIndexOf(".")); - ((Set) field.get(Launch.classLoader)).remove(exclusionName); - Launch.classLoader.addClassLoaderExclusion(Logger.class.getName()); - Launch.classLoader.addTransformerExclusion(exclusionName); - Launch.classLoader.loadClass(FML_PLATFORM_INITIALIZER).getMethod("initFMLPlatform").invoke(null); - } - } - - @Override - public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { - if (gameDir == null) { - gameDir = new File("."); - } - LogManager.setLogFile(gameDir.toPath().resolve("CustomSkinLoader/CustomSkinLoader.log")); - } - - @Override - public void injectIntoClassLoader(LaunchClassLoader classLoader) { - - } - - @Override - public String getLaunchTarget() { - return ""; - } - - @Override - public String[] getLaunchArguments() { - return new String[0]; - } -} diff --git a/Forge/V1/src/main/java/customskinloader/forge/TransformerManager.java b/Forge/V1/src/main/java/customskinloader/forge/TransformerManager.java deleted file mode 100644 index 669c8006..00000000 --- a/Forge/V1/src/main/java/customskinloader/forge/TransformerManager.java +++ /dev/null @@ -1,190 +0,0 @@ -package customskinloader.forge; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Supplier; - -import customskinloader.forge.platform.IFMLPlatform; -import org.objectweb.asm.commons.Remapper; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodNode; - -public class TransformerManager { - - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface TransformTarget { - String className(); - - String methodNameSrg() default ""; - - String[] methodNames() default {}; - - String desc() default ""; - } - - public interface IClassTransformer { - ClassNode transform(ClassNode cn); - } - - public interface IMethodTransformer { - MethodNode transform(ClassNode cn, MethodNode mn); - } - - public static boolean isDevelopmentEnvironment = false; - /** If true, match {@link TransformTarget#methodNames} if {@link TransformTarget#methodNameSrg} fails to match. */ - public static boolean useDeobfName = false; - private final static Remapper remapper = IFMLPlatform.FMLPlatformInitializer.getPlatform().getRemapper(); - - public static String mapClassName(String name) { - return remapper.mapType(name); - } - - public static String mapFieldName(String owner, String name, String desc) { - return remapper.mapFieldName(owner, name, desc); - } - - public static String mapFieldDesc(String desc) { - return remapper.mapDesc(desc); - } - - public static String mapMethodName(String owner, String name, String desc) { - return remapper.mapMethodName(owner, name, desc); - } - - public static String mapMethodDesc(String desc) { - return remapper.mapMethodDesc(desc); - } - - public static boolean checkClassName(String name, String deobfName) { - if (isDevelopmentEnvironment) { - return name.equals(deobfName); - } else { - return mapClassName(name).equals(deobfName); - } - } - - public static boolean checkFieldName(String owner, String name, String desc, String srgName) { - if (isDevelopmentEnvironment) { - return mapFieldName(owner, srgName, desc).equals(name); - } else { - return mapFieldName(owner, name, desc).equals(srgName); - } - } - - public static boolean checkFieldDesc(String desc, String deobfDesc) { - if (isDevelopmentEnvironment) { - return desc.equals(deobfDesc); - } else { - return mapFieldDesc(desc).equals(deobfDesc); - } - } - - public static boolean checkMethodName(String owner, String name, String desc, String srgName) { - if (isDevelopmentEnvironment) { - return mapMethodName(owner, srgName, desc).equals(name); - } else { - return mapMethodName(owner, name, desc).equals(srgName); - } - } - - public static boolean checkMethodDesc(String desc, String deobfDesc) { - if (isDevelopmentEnvironment) { - return desc.equals(deobfDesc); - } else { - return mapMethodDesc(desc).equals(deobfDesc); - } - } - - public Map classMap = new HashMap<>(); - public Map, IMethodTransformer>> srgMap = new HashMap<>(); - public Map> map = new HashMap<>(); - - public TransformerManager(IClassTransformer[] classTransformers, IMethodTransformer[] methodTransformers) { - for (IClassTransformer t : classTransformers) { - TransformTarget tt = this.getTransformTarget(t.getClass()); - if (tt != null) { - addClassTransformer(tt.className(), t); - } - } - for (IMethodTransformer t : methodTransformers) { - TransformTarget tt = this.getTransformTarget(t.getClass()); - if (tt != null) { - addMethodTransformer(tt, tt.className(), t); - } - } - } - - private TransformTarget getTransformTarget(Class cl) { - ForgeTweaker.logger.info("[CSL DEBUG] REGISTERING TRANSFORMER %s", cl.getName()); - if (!cl.isAnnotationPresent(TransformTarget.class)) { - ForgeTweaker.logger.info("[CSL DEBUG] ERROR occurs while parsing Annotation"); - return null; - } - return cl.getAnnotation(TransformTarget.class); - } - - private void addClassTransformer(String className, IClassTransformer transformer) { - if (!classMap.containsKey(className)) { - classMap.put(className, transformer); - ForgeTweaker.logger.info("[CSL DEBUG] REGISTERING CLASS %s", className); - } - } - - private void addMethodTransformer(TransformTarget target, String className, IMethodTransformer transformer) { - if (!srgMap.containsKey(className)) { - srgMap.put(className, new HashMap<>()); - } - if (!target.methodNameSrg().equals("")) { - // FMLDeobfRemapper has not been setup when this line being reached. - Supplier mappedMethod = () -> (isDevelopmentEnvironment ? mapMethodName(className.replace(".", "/"), target.methodNameSrg(), target.desc()) : target.methodNameSrg()) + target.desc(); - srgMap.get(className).put(mappedMethod, transformer); - ForgeTweaker.logger.info("[CSL DEBUG] REGISTERING SRG METHOD %s::%s", className, target.methodNameSrg() + target.desc()); - } - if (!map.containsKey(className)) - map.put(className, new HashMap<>()); - for (String methodName : target.methodNames()) { - map.get(className).put(methodName + target.desc(), transformer); - ForgeTweaker.logger.info("[CSL DEBUG] REGISTERING METHOD %s::%s", className, methodName + target.desc()); - } - } - - public ClassNode transform(ClassNode classNode, String className) { - IClassTransformer transformer = classMap.get(className); - if (transformer != null) { - try { - classNode = transformer.transform(classNode); - ForgeTweaker.logger.info("[CSL DEBUG] Successfully transformed class %s", className); - } catch (Exception e) { - ForgeTweaker.logger.warning("[CSL DEBUG] An error happened when transforming class %s", className); - ForgeTweaker.logger.warning(e); - } - } - return classNode; - } - - public MethodNode transform(ClassNode classNode, MethodNode methodNode, String className, String methodName, String methodDesc) { - String methodTarget = methodName + methodDesc; - Map, IMethodTransformer> transSrgMap = srgMap.get(className); - IMethodTransformer transformer = transSrgMap == null ? null : transSrgMap.get(transSrgMap.keySet().stream().filter(s -> s.get().equals(methodTarget)).findFirst().orElse(null)); - - Map transMap = map.get(className); - if (useDeobfName && transMap != null && transformer == null) { - transformer = transMap.get(methodTarget); - } - if (transformer != null) { - try { - methodNode = transformer.transform(classNode, methodNode); - ForgeTweaker.logger.info("[CSL DEBUG] Successfully transformed method %s in class %s", methodName, className); - } catch (Exception e) { - ForgeTweaker.logger.warning("[CSL DEBUG] An error happened when transforming method %s in class %s", methodTarget, className); - ForgeTweaker.logger.warning(e); - } - } - return methodNode; - } -} diff --git a/Forge/V1/src/main/java/customskinloader/forge/loader/LaunchWrapper.java b/Forge/V1/src/main/java/customskinloader/forge/loader/LaunchWrapper.java deleted file mode 100644 index 994d9854..00000000 --- a/Forge/V1/src/main/java/customskinloader/forge/loader/LaunchWrapper.java +++ /dev/null @@ -1,70 +0,0 @@ -package customskinloader.forge.loader; - -import java.util.ArrayList; - -import customskinloader.forge.TransformerManager; -import customskinloader.forge.transformer.FakeInterfacesTransformer; -import customskinloader.forge.transformer.PlayerTabTransformer; -import customskinloader.forge.transformer.SkinManagerTransformer; -import customskinloader.forge.transformer.SpectatorMenuTransformer; -import net.minecraft.launchwrapper.IClassTransformer; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodNode; - -//LaunchWrapper for 1.13- -public class LaunchWrapper implements IClassTransformer { - private static final TransformerManager.IClassTransformer[] CLASS_TRANSFORMERS = { - new FakeInterfacesTransformer.MinecraftTransformer(), - new FakeInterfacesTransformer.IImageBufferTransformer(), - new FakeInterfacesTransformer.ClientIResourceTransformer(), - new FakeInterfacesTransformer.ClientIResourceManagerTransformer(), - new FakeInterfacesTransformer.IResourceTransformer(), - new FakeInterfacesTransformer.IResourceManagerTransformer() - }; - private static final TransformerManager.IMethodTransformer[] TRANFORMERS = { - new SkinManagerTransformer.InitTransformer(), - new SkinManagerTransformer.LoadSkinTransformer(), - new SkinManagerTransformer.LoadProfileTexturesTransformer(), - new SkinManagerTransformer.LoadSkinFromCacheTransformer(), - new SkinManagerTransformer.Run0Transformer(), - new SkinManagerTransformer.Run1Transformer(), - new PlayerTabTransformer.ScoreObjectiveTransformer(), - new SpectatorMenuTransformer.PlayerMenuObjectTransformer() - }; - private TransformerManager transformerManager = new TransformerManager(CLASS_TRANSFORMERS, TRANFORMERS); - - //From: https://github.com/RecursiveG/UniSkinMod/blob/1.9.4/src/main/java/org/devinprogress/uniskinmod/coremod/BaseAsmTransformer.java#L83-L114 - public byte[] transform(String obfClassName, String className, byte[] bytes) { - if (!transformerManager.classMap.containsKey(className) && !transformerManager.map.containsKey(className)) return bytes;//Check if should be transformed - - //Parse bytes to ClassNode - ClassNode cn = new ClassNode(); - if (bytes != null && bytes.length > 0) { - ClassReader cr = new ClassReader(bytes); - cr.accept(cn, 0); - } else { - cn.name = className.replace(".", "/"); - cn.version = Opcodes.V1_8; - cn.superName = "java/lang/Object"; - } - - //Transform ClassNode - cn = transformerManager.transform(cn, className); - ArrayList methods = new ArrayList<>(); - for (MethodNode mn : cn.methods) { - String mappedMethodName = TransformerManager.isDevelopmentEnvironment ? mn.name : TransformerManager.mapMethodName(cn.name, mn.name, mn.desc); - String mappedMethodDesc = TransformerManager.isDevelopmentEnvironment ? mn.desc : TransformerManager.mapMethodDesc(mn.desc); - methods.add(transformerManager.transform(cn, mn, className, mappedMethodName, mappedMethodDesc)); - } - cn.methods.clear(); - cn.methods.addAll(methods); - - //Parse Class Node to bytes - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); - cn.accept(cw); - return cw.toByteArray(); - } -} diff --git a/Forge/V1/src/main/java/customskinloader/forge/platform/DefaultFMLPlatform.java b/Forge/V1/src/main/java/customskinloader/forge/platform/DefaultFMLPlatform.java deleted file mode 100644 index 4659b0dd..00000000 --- a/Forge/V1/src/main/java/customskinloader/forge/platform/DefaultFMLPlatform.java +++ /dev/null @@ -1,103 +0,0 @@ -package customskinloader.forge.platform; - -import java.io.File; -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.util.List; -import java.util.Set; -import java.util.function.Supplier; - -import com.google.common.base.Strings; -import customskinloader.forge.ForgePlugin; -import net.minecraft.launchwrapper.ITweaker; -import net.minecraftforge.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper; -import net.minecraftforge.fml.relauncher.CoreModManager; -import net.minecraftforge.fml.relauncher.FMLLaunchHandler; -import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; -import org.objectweb.asm.commons.Remapper; - -public class DefaultFMLPlatform implements IFMLPlatform { - private final static String FML_PLUGIN_WRAPPER = "net.minecraftforge.fml.relauncher.CoreModManager$FMLPluginWrapper"; - - @Override - public Result init(Set otherPlatforms) { - return otherPlatforms.size() == 0 ? Result.ACCEPT : Result.REJECT; - } - - @Override - public String getSide() { - return FMLLaunchHandler.side().name(); - } - - @Override - @SuppressWarnings("unchecked") - public List getIgnoredMods() { - try { - return (List) CoreModManager.class.getMethod("getLoadedCoremods").invoke(null); // 1.8 - } catch (Exception e) { - return CoreModManager.getIgnoredMods(); // 1.8.8~1.12.2 - } - } - - @Override - public String getName() { - Annotation name = this.getNameAnnotation(); - if (name != null && !Strings.isNullOrEmpty(this.getNameValue(name))) { - return this.getNameValue(name); - } - return this.getFMLLoadingPluginClass().getSimpleName(); - } - - @Override - public int getSortingIndex() { - Annotation index = this.getSortingIndexAnnotation(); - return index != null ? this.getSortingIndexValue(index) : 0; - } - - @Override - public ITweaker createFMLPluginWrapper(String name, File location, int sortIndex) throws Exception { - Constructor constructor = Class.forName(FML_PLUGIN_WRAPPER).getDeclaredConstructor(String.class, IFMLLoadingPlugin.class, File.class, int.class, String[].class); - constructor.setAccessible(true); - return (ITweaker) constructor.newInstance(name, this.getFMLLoadingPluginClass().newInstance(), location, sortIndex, new String[0]); - } - - @Override - @SuppressWarnings("unchecked") - public void addLoadPlugins(ITweaker tweaker) throws Exception { - Field field = CoreModManager.class.getDeclaredField("loadPlugins"); - field.setAccessible(true); - ((List) field.get(null)).add(tweaker); - } - - @Override - public Remapper getRemapper() { - // DO NOT replace with lambda or replace call with method body. - return new Supplier() { - @Override - public Remapper get() { - return FMLDeobfuscatingRemapper.INSTANCE; - } - }.get(); - } - - protected Class getFMLLoadingPluginClass() { - return ForgePlugin.class; - } - - protected Annotation getNameAnnotation() { - return this.getFMLLoadingPluginClass().getAnnotation(IFMLLoadingPlugin.Name.class); - } - - protected String getNameValue(Annotation annotationIn) { - return ((IFMLLoadingPlugin.Name) annotationIn).value(); - } - - protected Annotation getSortingIndexAnnotation() { - return this.getFMLLoadingPluginClass().getAnnotation(IFMLLoadingPlugin.SortingIndex.class); - } - - protected int getSortingIndexValue(Annotation annotationIn) { - return ((IFMLLoadingPlugin.SortingIndex) annotationIn).value(); - } -} diff --git a/Forge/V1/src/main/java/customskinloader/forge/platform/IFMLPlatform.java b/Forge/V1/src/main/java/customskinloader/forge/platform/IFMLPlatform.java deleted file mode 100644 index 3f9a42ab..00000000 --- a/Forge/V1/src/main/java/customskinloader/forge/platform/IFMLPlatform.java +++ /dev/null @@ -1,110 +0,0 @@ -package customskinloader.forge.platform; - -import java.io.File; -import java.net.URL; -import java.security.CodeSource; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.ServiceLoader; -import java.util.Set; - -import customskinloader.forge.ForgeTweaker; -import net.minecraft.launchwrapper.ITweaker; -import net.minecraft.launchwrapper.Launch; -import org.objectweb.asm.commons.Remapper; - -public interface IFMLPlatform { - enum Result { - /** - * The platform container will be used. - */ - ACCEPT, - - /** - * The platform container won't be used. - */ - REJECT - } - - class FMLPlatformInitializer { - private static IFMLPlatform platform; - - @SuppressWarnings("unchecked") - public static void initFMLPlatform() throws Exception { - Set platforms = new HashSet<>(); - List throwables = new ArrayList<>(); - - for (Iterator iterator = ServiceLoader.load(IFMLPlatform.class).iterator(); iterator.hasNext(); ) { - try { - platforms.add(iterator.next()); - } catch (Throwable t) { - throwables.add(t); - } - } - - for (IFMLPlatform platform0 : platforms) { - Set otherPlatforms = new HashSet<>(platforms); - otherPlatforms.remove(platform0); - if (platform0.init(otherPlatforms).equals(Result.ACCEPT)) { - if (platform == null) { - platform = platform0; - } else { - throw new RuntimeException("Duplicated platforms! (" + platform.getClass().getName() + ", " + platform0 + ")"); - } - } - } - if (platform == null) { - for (int i = 0, len = throwables.size(); i < len; i++) { - Throwable throwable = throwables.get(i); - ForgeTweaker.logger.warning("Platform - %s :", i); - ForgeTweaker.logger.warning(throwable); - } - throw new RuntimeException("No available platform!"); - } - - // CustomSkinLoader is a client side mod - if (!platform.getSide().equals("CLIENT")) { - return; - } - - CodeSource codeSource = FMLPlatformInitializer.class.getProtectionDomain().getCodeSource(); - if (codeSource != null) { - URL location = codeSource.getLocation(); - File file = new File(location.toURI()); - if (file.isFile()) { - // This forces forge to reexamine the jar file for FML mods. - platform.getIgnoredMods().remove(file.getName()); - } - - ITweaker tweaker = platform.createFMLPluginWrapper(platform.getName(), file, platform.getSortingIndex()); - platform.addLoadPlugins(tweaker); - ((List) Launch.blackboard.get("Tweaks")).add(tweaker); - } else { - ForgeTweaker.logger.warning("No CodeSource, if this is not a development environment we might run into problems!"); - ForgeTweaker.logger.warning(FMLPlatformInitializer.class.getProtectionDomain().toString()); - } - } - - public static IFMLPlatform getPlatform() { - return platform; - } - } - - Result init(Set otherPlatforms); - - String getSide(); - - List getIgnoredMods(); - - String getName(); - - int getSortingIndex(); - - ITweaker createFMLPluginWrapper(String name, File location, int sortIndex) throws Exception; - - void addLoadPlugins(ITweaker tweaker) throws Exception; - - Remapper getRemapper(); -} diff --git a/Forge/V1/src/main/java/customskinloader/forge/transformer/FakeInterfacesTransformer.java b/Forge/V1/src/main/java/customskinloader/forge/transformer/FakeInterfacesTransformer.java deleted file mode 100644 index ac17c787..00000000 --- a/Forge/V1/src/main/java/customskinloader/forge/transformer/FakeInterfacesTransformer.java +++ /dev/null @@ -1,78 +0,0 @@ -package customskinloader.forge.transformer; - -import customskinloader.forge.TransformerManager; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; - -public class FakeInterfacesTransformer { - public static class FakeInterfaceTransformer implements TransformerManager.IClassTransformer { - @Override - public ClassNode transform(ClassNode cn) { - if (cn.access == 0) { - cn.access = Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT; - } - return cn; - } - } - - @TransformerManager.TransformTarget( - className = "net.minecraft.client.Minecraft" - ) - public static class MinecraftTransformer implements TransformerManager.IClassTransformer { - @Override - public ClassNode transform(ClassNode cn) { - cn.interfaces.add("customskinloader/fake/itf/IFakeMinecraft"); - return cn; - } - } - - @TransformerManager.TransformTarget( - className = "net.minecraft.client.renderer.IImageBuffer" - ) - public static class IImageBufferTransformer implements TransformerManager.IClassTransformer { - @Override - public ClassNode transform(ClassNode cn) { - cn.interfaces.add("customskinloader/fake/itf/IFakeIImageBuffer"); - return cn; - } - } - - @TransformerManager.TransformTarget( - className = "net.minecraft.client.resources.IResource" - ) - public static class ClientIResourceTransformer implements TransformerManager.IClassTransformer { - @Override - public ClassNode transform(ClassNode cn) { - cn.interfaces.add("customskinloader/fake/itf/IFakeIResource$V1"); - cn.interfaces.add("customskinloader/fake/itf/IFakeIResource$V2"); - cn.interfaces.add("net/minecraft/resources/IResource"); - return cn; - } - } - - @TransformerManager.TransformTarget( - className = "net.minecraft.client.resources.IResourceManager" - ) - public static class ClientIResourceManagerTransformer implements TransformerManager.IClassTransformer { - @Override - public ClassNode transform(ClassNode cn) { - cn.interfaces.add("customskinloader/fake/itf/IFakeIResourceManager$V1"); - cn.interfaces.add("net/minecraft/resources/IResourceManager"); - return cn; - } - } - - @TransformerManager.TransformTarget( - className = "net.minecraft.resources.IResource" - ) - public static class IResourceTransformer extends FakeInterfacesTransformer.FakeInterfaceTransformer { - - } - - @TransformerManager.TransformTarget( - className = "net.minecraft.resources.IResourceManager" - ) - public static class IResourceManagerTransformer extends FakeInterfacesTransformer.FakeInterfaceTransformer { - - } -} diff --git a/Forge/V1/src/main/java/customskinloader/forge/transformer/PlayerTabTransformer.java b/Forge/V1/src/main/java/customskinloader/forge/transformer/PlayerTabTransformer.java deleted file mode 100644 index 1c1c8fdd..00000000 --- a/Forge/V1/src/main/java/customskinloader/forge/transformer/PlayerTabTransformer.java +++ /dev/null @@ -1,35 +0,0 @@ -package customskinloader.forge.transformer; - -import java.util.ListIterator; - -import customskinloader.forge.TransformerManager; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.InsnNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; - -public class PlayerTabTransformer { - @TransformerManager.TransformTarget(className = "net.minecraft.client.gui.GuiPlayerTabOverlay", - methodNameSrg = "func_175249_a", - methodNames = "renderPlayerlist", - desc = "(ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V") - public static class ScoreObjectiveTransformer implements TransformerManager.IMethodTransformer { - //From: http://git.oschina.net/AsteriskTeam/TabIconHackForge/blob/master/src/main/java/kengxxiao/tabiconhack/coremod/TabIconHackForgeClassTransformer.java#L30-L43 - @Override - public MethodNode transform(ClassNode cn, MethodNode mn) { - for (ListIterator iterator = mn.instructions.iterator(); iterator.hasNext();) { - AbstractInsnNode node = iterator.next(); - if (node.getOpcode() == Opcodes.INVOKEVIRTUAL) { - MethodInsnNode min = (MethodInsnNode) node; - if (TransformerManager.checkClassName(min.owner, "net/minecraft/client/Minecraft") && TransformerManager.checkMethodName(min.owner, min.name, min.desc, "func_71387_A") && TransformerManager.checkMethodDesc(min.desc, "()Z")) { - mn.instructions.insertBefore(node, new InsnNode(Opcodes.POP)); - mn.instructions.set(node, new InsnNode(Opcodes.ICONST_1)); - } - } - } - return mn; - } - } -} diff --git a/Forge/V1/src/main/java/customskinloader/forge/transformer/SkinManagerTransformer.java b/Forge/V1/src/main/java/customskinloader/forge/transformer/SkinManagerTransformer.java deleted file mode 100644 index 5d21a214..00000000 --- a/Forge/V1/src/main/java/customskinloader/forge/transformer/SkinManagerTransformer.java +++ /dev/null @@ -1,213 +0,0 @@ -package customskinloader.forge.transformer; - -import customskinloader.forge.TransformerManager; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldInsnNode; -import org.objectweb.asm.tree.FieldNode; -import org.objectweb.asm.tree.FrameNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.InsnNode; -import org.objectweb.asm.tree.IntInsnNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.TypeInsnNode; -import org.objectweb.asm.tree.VarInsnNode; - -public class SkinManagerTransformer { - @TransformerManager.TransformTarget( - className = "net.minecraft.client.resources.SkinManager", - methodNameSrg = "", - desc = "(Lnet/minecraft/client/renderer/texture/TextureManager;Ljava/io/File;Lcom/mojang/authlib/minecraft/MinecraftSessionService;)V" - ) - public static class InitTransformer implements TransformerManager.IMethodTransformer { - @Override - public MethodNode transform(ClassNode cn, MethodNode mn) { - for (AbstractInsnNode ain : mn.instructions.toArray()) { - if (ain.getOpcode() == Opcodes.RETURN) { - InsnList il = new InsnList(); - il.add(new VarInsnNode(Opcodes.ALOAD, 2)); - il.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "setSkinCacheDir", "(Ljava/io/File;)V", false)); - mn.instructions.insertBefore(ain, il); - } - } - return mn; - } - } - - @TransformerManager.TransformTarget( - className = "net.minecraft.client.resources.SkinManager", - methodNameSrg = "func_152789_a", - methodNames = "loadSkin", - desc = "(Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)Lnet/minecraft/util/ResourceLocation;" - ) - public static class LoadSkinTransformer implements TransformerManager.IMethodTransformer { - @Override - public MethodNode transform(ClassNode cn, MethodNode mn) { - for (AbstractInsnNode ain : mn.instructions.toArray()) { - if (ain.getOpcode() == Opcodes.INVOKESPECIAL) { - MethodInsnNode min = (MethodInsnNode) ain; - if (TransformerManager.checkClassName(min.owner, "net/minecraft/client/renderer/ThreadDownloadImageData") && TransformerManager.checkMethodName(min.owner, min.name, min.desc, "") && TransformerManager.checkMethodDesc(min.desc, "(Ljava/io/File;Ljava/lang/String;Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/client/renderer/IImageBuffer;)V")) { - InsnList il0 = new InsnList(); - Type[] args = Type.getType(min.desc).getArgumentTypes(); - StringBuilder sb = new StringBuilder("("); - for (int i = 0, len = args.length; i < len; i++) { - sb.append("Ljava/lang/Object;"); - } - il0.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/google/common/collect/ImmutableList", "of", sb.append(")Lcom/google/common/collect/ImmutableList;").toString(), false)); - il0.add(new VarInsnNode(Opcodes.ALOAD, 1)); - il0.add(new VarInsnNode(Opcodes.ALOAD, 2)); - - il0.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "createThreadDownloadImageData", "(Lcom/google/common/collect/ImmutableList;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;)[Ljava/lang/Object;", false)); - for (int i = 0, len = args.length; i < len; i++) { - il0.add(new InsnNode(Opcodes.DUP)); - il0.add(new IntInsnNode(Opcodes.BIPUSH, i)); - il0.add(new InsnNode(Opcodes.AALOAD)); - il0.add(new TypeInsnNode(Opcodes.CHECKCAST, args[i].getInternalName())); - il0.add(new InsnNode(Opcodes.SWAP)); - } - il0.add(new InsnNode(Opcodes.POP)); - - mn.instructions.insertBefore(min, il0); - - // This is only for 1.12.2- - InsnList il1 = new InsnList(); - il1.add(new VarInsnNode(Opcodes.ALOAD, 3)); - il1.add(new VarInsnNode(Opcodes.ALOAD, 4)); - il1.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeClientPlayer", "putCache", "(Lnet/minecraft/client/renderer/ThreadDownloadImageData;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/ThreadDownloadImageData;", false)); - - mn.instructions.insert(min, il1); - } - } - } - return mn; - } - } - - @TransformerManager.TransformTarget( - className = "net.minecraft.client.resources.SkinManager", - methodNameSrg = "func_152790_a", - methodNames = "loadProfileTextures", - desc = "(Lcom/mojang/authlib/GameProfile;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;Z)V" - ) - public static class LoadProfileTexturesTransformer implements TransformerManager.IMethodTransformer { - @Override - public MethodNode transform(ClassNode cn, MethodNode mn) { - for (AbstractInsnNode ain : mn.instructions.toArray()) { - if (ain.getOpcode() == Opcodes.INVOKEINTERFACE) { - MethodInsnNode min = (MethodInsnNode) ain; - if (TransformerManager.checkClassName(min.owner, "java/util/concurrent/ExecutorService") && TransformerManager.checkMethodName(min.owner, min.name, min.desc, "submit") && TransformerManager.checkMethodDesc(min.desc, "(Ljava/lang/Runnable;)Ljava/util/concurrent/Future;")) { - mn.instructions.set(min, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "loadProfileTextures", "(Ljava/lang/Runnable;)V", false)); - } - } - } - return mn; - } - } - - @TransformerManager.TransformTarget( - className = "net.minecraft.client.resources.SkinManager", - methodNameSrg = "func_152788_a", - methodNames = "loadSkinFromCache", - desc = "(Lcom/mojang/authlib/GameProfile;)Ljava/util/Map;" - ) - public static class LoadSkinFromCacheTransformer implements TransformerManager.IMethodTransformer { - @Override - public MethodNode transform(ClassNode cn, MethodNode mn) { - InsnList il = new InsnList(); - il.add(new VarInsnNode(Opcodes.ALOAD, 1)); - il.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "loadSkinFromCache", "(Lcom/mojang/authlib/GameProfile;)Ljava/util/Map;", false)); - il.add(new InsnNode(Opcodes.ARETURN)); - il.add(new FrameNode(Opcodes.F_SAME, 0, null, 0, null)); - mn.instructions.insert(il); - return mn; - } - } - - @TransformerManager.TransformTarget( - className = "net.minecraft.client.resources.SkinManager$3", - methodNameSrg = "run", - methodNames = "run", - desc = "()V" - ) - public static class Run0Transformer implements TransformerManager.IMethodTransformer { - @Override - public MethodNode transform(ClassNode cn, MethodNode mn) { - for (AbstractInsnNode ain : mn.instructions.toArray()) { - if (ain.getOpcode() == Opcodes.INVOKEINTERFACE) { - MethodInsnNode min = (MethodInsnNode) ain; - if (TransformerManager.checkClassName(min.owner, "com/mojang/authlib/minecraft/MinecraftSessionService") && TransformerManager.checkMethodName(min.owner, min.name, min.desc, "getTextures") && TransformerManager.checkMethodDesc(min.desc, "(Lcom/mojang/authlib/GameProfile;Z)Ljava/util/Map;")) { - mn.instructions.set(min, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "getUserProfile", "(Lcom/mojang/authlib/minecraft/MinecraftSessionService;Lcom/mojang/authlib/GameProfile;Z)Ljava/util/Map;", false)); - break; - } - } - } - return mn; - } - } - - @TransformerManager.TransformTarget( - className = "net.minecraft.client.resources.SkinManager$3$1", - methodNameSrg = "run", - methodNames = "run", - desc = "()V" - ) - public static class Run1Transformer implements TransformerManager.IMethodTransformer { - @Override - public MethodNode transform(ClassNode cn, MethodNode mn) { - String val$skinAvailableCallback = null; - String this$0 = null; - - String val$map = null; - String this$1 = null; - - for (FieldNode fn : cn.fields) { - if (val$map == null && TransformerManager.checkFieldDesc(fn.desc, "Ljava/util/Map;")) { - val$map = TransformerManager.mapFieldName(cn.name, fn.name, fn.desc); - } else if (this$1 == null && TransformerManager.checkFieldDesc(fn.desc, "Lnet/minecraft/client/resources/SkinManager$3;")) { - this$1 = TransformerManager.mapFieldName(cn.name, fn.name, fn.desc); - } - } - - for (AbstractInsnNode ain : mn.instructions.toArray()) { - if (ain.getOpcode() == Opcodes.GETFIELD) { - FieldInsnNode fin = (FieldInsnNode) ain; - if (TransformerManager.checkClassName(fin.owner, "net/minecraft/client/resources/SkinManager$3")) { - if (TransformerManager.checkFieldDesc(fin.desc, "Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;")) { - val$skinAvailableCallback = TransformerManager.mapFieldName(fin.owner, fin.name, fin.desc); - } else if (TransformerManager.checkFieldDesc(fin.desc, "Lnet/minecraft/client/resources/SkinManager;")) { - this$0 = TransformerManager.mapFieldName(fin.owner, fin.name, fin.desc); - } - } - } - } - - if (val$skinAvailableCallback != null && this$0 != null && val$map != null && this$1 != null) { - AbstractInsnNode frame = null; - for (AbstractInsnNode ain : mn.instructions.toArray()) { - if (ain instanceof FrameNode) { - frame = ain; - } - if (ain.getOpcode() == Opcodes.RETURN) { - InsnList il = new InsnList(); - il.add(new VarInsnNode(Opcodes.ALOAD, 0)); - il.add(new FieldInsnNode(Opcodes.GETFIELD, "net/minecraft/client/resources/SkinManager$3$1", this$1, "Lnet/minecraft/client/resources/SkinManager$3;")); - il.add(new FieldInsnNode(Opcodes.GETFIELD, "net/minecraft/client/resources/SkinManager$3", this$0, "Lnet/minecraft/client/resources/SkinManager;")); - il.add(new VarInsnNode(Opcodes.ALOAD, 0)); - il.add(new FieldInsnNode(Opcodes.GETFIELD, "net/minecraft/client/resources/SkinManager$3$1", val$map, "Ljava/util/Map;")); - il.add(new VarInsnNode(Opcodes.ALOAD, 0)); - il.add(new FieldInsnNode(Opcodes.GETFIELD, "net/minecraft/client/resources/SkinManager$3$1", this$1, "Lnet/minecraft/client/resources/SkinManager$3;")); - il.add(new FieldInsnNode(Opcodes.GETFIELD, "net/minecraft/client/resources/SkinManager$3", val$skinAvailableCallback, "Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;")); - il.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "loadElytraTexture", "(Lnet/minecraft/client/resources/SkinManager;Ljava/util/Map;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)V", false)); - mn.instructions.insertBefore(ain, il); - } - } - mn.instructions.set(frame, new FrameNode(Opcodes.F_SAME, 0, null, 0, null)); - } - - return mn; - } - } -} diff --git a/Forge/V1/src/main/java/customskinloader/forge/transformer/SpectatorMenuTransformer.java b/Forge/V1/src/main/java/customskinloader/forge/transformer/SpectatorMenuTransformer.java deleted file mode 100644 index 0b9ebefb..00000000 --- a/Forge/V1/src/main/java/customskinloader/forge/transformer/SpectatorMenuTransformer.java +++ /dev/null @@ -1,53 +0,0 @@ -package customskinloader.forge.transformer; - -import java.util.ListIterator; - -import customskinloader.forge.TransformerManager; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; - -/** - * Transformer of Spectator Menu for 1.13- - */ -public class SpectatorMenuTransformer { - @TransformerManager.TransformTarget(className = "net.minecraft.client.gui.spectator.PlayerMenuObject", - methodNameSrg = "", - desc = "(Lcom/mojang/authlib/GameProfile;)V") - public static class PlayerMenuObjectTransformer implements TransformerManager.IMethodTransformer { - - @Override - public MethodNode transform(ClassNode cn, MethodNode mn) { - InsnList il = mn.instructions; - ListIterator li = il.iterator(); - - boolean flag = false; - while (li.hasNext()) { - AbstractInsnNode ain = li.next(); - if (ain.getOpcode() != Opcodes.INVOKESTATIC) - continue; - - if (!flag) { - //First InvokeStatic - il.set(ain, new MethodInsnNode(Opcodes.INVOKESTATIC, - "customskinloader/fake/FakeClientPlayer", - "getLocationSkin", - "(Ljava/lang/String;)Lnet/minecraft/util/ResourceLocation;", false)); - flag = true; - } else { - //Second InvokeStatic - il.set(ain, new MethodInsnNode(Opcodes.INVOKESTATIC, - "customskinloader/fake/FakeClientPlayer", - "getDownloadImageSkin", - "(Lnet/minecraft/util/ResourceLocation;Ljava/lang/String;)Lnet/minecraft/client/renderer/ThreadDownloadImageData;", false)); - break; - } - } - return mn; - } - - } -} diff --git a/Forge/V1/src/main/java/net/minecraft/client/resources/IResource.java b/Forge/V1/src/main/java/net/minecraft/client/resources/IResource.java deleted file mode 100644 index 3c49e058..00000000 --- a/Forge/V1/src/main/java/net/minecraft/client/resources/IResource.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.minecraft.client.resources; - -import java.io.InputStream; - -// this class shouldn't be created by ASM because of modlauncher issue. -public interface IResource { - InputStream getInputStream(); -} diff --git a/Forge/V1/src/main/java/net/minecraft/client/resources/IResourceManager.java b/Forge/V1/src/main/java/net/minecraft/client/resources/IResourceManager.java deleted file mode 100644 index 98b0fb41..00000000 --- a/Forge/V1/src/main/java/net/minecraft/client/resources/IResourceManager.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.minecraft.client.resources; - -import net.minecraft.util.ResourceLocation; - -// this class shouldn't be created by ASM because of modlauncher issue. -public interface IResourceManager { - IResource getResource(ResourceLocation location); -} diff --git a/Forge/V1/src/main/java/net/minecraft/client/resources/SkinManager$SkinAvailableCallback.java b/Forge/V1/src/main/java/net/minecraft/client/resources/SkinManager$SkinAvailableCallback.java deleted file mode 100644 index bebaea23..00000000 --- a/Forge/V1/src/main/java/net/minecraft/client/resources/SkinManager$SkinAvailableCallback.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.minecraft.client.resources; - -import com.mojang.authlib.minecraft.MinecraftProfileTexture; -import net.minecraft.util.ResourceLocation; - -// put into mod jar to prevent https://github.com/cpw/modlauncher/issues/39 -public interface SkinManager$SkinAvailableCallback { - default void skinAvailable(MinecraftProfileTexture.Type typeIn, ResourceLocation location, MinecraftProfileTexture profileTexture) { - this.onSkinTextureAvailable(typeIn, location, profileTexture); - } - - default void onSkinTextureAvailable(MinecraftProfileTexture.Type typeIn, ResourceLocation location, MinecraftProfileTexture profileTexture) { - ((SkinManager$ISkinAvailableCallback) this).onSkinTextureAvailable(typeIn, location, profileTexture); - } -} diff --git a/Forge/V1/src/main/resources/META-INF/coremods.json b/Forge/V1/src/main/resources/META-INF/coremods.json deleted file mode 100644 index de3761fd..00000000 --- a/Forge/V1/src/main/resources/META-INF/coremods.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "transformers": "transformers.js" -} diff --git a/Forge/V1/src/main/resources/META-INF/mods.toml b/Forge/V1/src/main/resources/META-INF/mods.toml deleted file mode 100644 index e153a7c2..00000000 --- a/Forge/V1/src/main/resources/META-INF/mods.toml +++ /dev/null @@ -1,25 +0,0 @@ -modLoader="javafml" -loaderVersion="[25,37)" -license="GPL-3.0-only" -issueTrackerURL="https://github.com/xfl03/MCCustomSkinLoader/issues" -[[mods]] -modId="customskinloader" -version="${modFullVersion}" -displayName="CustomSkinLoader" -description=''' -Custom Skin Loader for Minecraft -''' -displayURL="https://github.com/xfl03/MCCustomSkinLoader" -authors="xfl03, JLChnToZ, ZekerZhayard" -[[dependencies.customskinloader]] -modId="forge" -mandatory=true -versionRange="[25.0.9,25.0.22],[25.0.42,37.0.0)" # incompatible with CoreMods-0.3.0 -ordering="NONE" -side="CLIENT" -[[dependencies.customskinloader]] -modId="minecraft" -mandatory=true -versionRange="[1.13.2,1.17.0)" -ordering="NONE" -side="CLIENT" diff --git a/Forge/V1/src/main/resources/META-INF/services/customskinloader.forge.platform.IFMLPlatform b/Forge/V1/src/main/resources/META-INF/services/customskinloader.forge.platform.IFMLPlatform deleted file mode 100644 index c2bff95b..00000000 --- a/Forge/V1/src/main/resources/META-INF/services/customskinloader.forge.platform.IFMLPlatform +++ /dev/null @@ -1 +0,0 @@ -customskinloader.forge.platform.DefaultFMLPlatform diff --git a/Forge/V1/src/main/resources/mcmod.info b/Forge/V1/src/main/resources/mcmod.info deleted file mode 100644 index 0d53039c..00000000 --- a/Forge/V1/src/main/resources/mcmod.info +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "modid": "customskinloader", - "name": "CustomSkinLoader", - "description": "Custom Skin Loader for Minecraft", - "version": "${modFullVersion}", - "url": "https://github.com/xfl03/MCCustomSkinLoader", - "authorList": ["xfl03", "JLChnToZ", "ZekerZhayard"] - } -] diff --git a/Forge/V1/src/main/resources/transformers.js b/Forge/V1/src/main/resources/transformers.js deleted file mode 100644 index a6b9255f..00000000 --- a/Forge/V1/src/main/resources/transformers.js +++ /dev/null @@ -1,295 +0,0 @@ -var ASMAPI = getJavaType('net.minecraftforge.coremod.api.ASMAPI'); -var Handle = getJavaType('org.objectweb.asm.Handle'); -var Opcodes = getJavaType('org.objectweb.asm.Opcodes'); -var Type = getJavaType('org.objectweb.asm.Type'); -var FieldInsnNode = getJavaType('org.objectweb.asm.tree.FieldInsnNode'); -var InsnNode = getJavaType('org.objectweb.asm.tree.InsnNode'); -var IntInsnNode = getJavaType('org.objectweb.asm.tree.IntInsnNode'); -var InvokeDynamicInsnNode = getJavaType('org.objectweb.asm.tree.InvokeDynamicInsnNode'); -var MethodInsnNode = getJavaType('org.objectweb.asm.tree.MethodInsnNode'); -var TypeInsnNode = getJavaType('org.objectweb.asm.tree.TypeInsnNode'); -var VarInsnNode = getJavaType('org.objectweb.asm.tree.VarInsnNode'); - -function getJavaType(name) { - try { - return Java.type(name); - } catch (ignored) { - // forge-1.13.2-25.0.23 ~ 1.13.2-25.0.41 - return null; - } -} - -// Compare method and field names, doesn't support forge-1.13.2-25.0.194 and earlier version. -function checkName(name, srgName) { - return name.equals(mapName(srgName)); -} - -// De-obfuscate method and field names. -function mapName(srgName) { - try { - if (srgName.startsWith("field_")) return ASMAPI.mapField(srgName); - if (srgName.startsWith("func_")) return ASMAPI.mapMethod(srgName); - } catch (ignored) { - // Before forge-1.13.2-25.0.194 - } - return srgName; -} - -function initializeCoreMod() { - return { - 'SkinManagerTransformer': { - 'target': { - 'type': 'CLASS', - 'name': 'net/minecraft/client/resources/SkinManager' - }, - 'transformer': function (cn) { - cn.methods.forEach(function (mn) { - if (checkName(mn.name, "") && mn.desc.equals("(Lnet/minecraft/client/renderer/texture/TextureManager;Ljava/io/File;Lcom/mojang/authlib/minecraft/MinecraftSessionService;)V")) { - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.RETURN) { - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 2)); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "setSkinCacheDir", "(Ljava/io/File;)V", false)); - } - } - } else if (checkName(mn.name, "func_152789_a") - && (mn.desc.equals("(Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)Lnet/minecraft/util/ResourceLocation;") // 1.13.2- - || mn.desc.equals("(Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/client/resources/SkinManager$ISkinAvailableCallback;)Lnet/minecraft/util/ResourceLocation;"))) { // 1.14.2+ - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKESPECIAL - && (node.owner.equals("net/minecraft/client/renderer/texture/ThreadDownloadImageData") // 1.13.2- - || node.owner.equals("net/minecraft/client/renderer/texture/DownloadingTexture")) // 1.14.2+ - && checkName(node.name, "") - && (node.desc.equals("(Ljava/io/File;Ljava/lang/String;Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/client/renderer/IImageBuffer;)V") // 1.14.4- - || node.desc.equals("(Ljava/io/File;Ljava/lang/String;Lnet/minecraft/util/ResourceLocation;ZLjava/lang/Runnable;)V"))) { // 1.15+ - var args = Type.getType(node.desc).getArgumentTypes(); - var s = "("; - for (var i = 0; i < args.length; i++) { - s = s + "Ljava/lang/Object;"; - } - if (node.desc.contains("Z")) { - mn.instructions.insertBefore(node, new InsnNode(Opcodes.SWAP)); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false)); - mn.instructions.insertBefore(node, new InsnNode(Opcodes.SWAP)); - } - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "com/google/common/collect/ImmutableList", "of", s + ")Lcom/google/common/collect/ImmutableList;", false)); - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 1)); - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 2)); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "createThreadDownloadImageData", "(Lcom/google/common/collect/ImmutableList;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;)[Ljava/lang/Object;", false)); - for (var i = 0; i < args.length; i++) { - mn.instructions.insertBefore(node, new InsnNode(Opcodes.DUP)); - mn.instructions.insertBefore(node, new IntInsnNode(Opcodes.BIPUSH, i)); - mn.instructions.insertBefore(node, new InsnNode(Opcodes.AALOAD)); - if (args[i].getInternalName().equals("Z")) { - mn.instructions.insertBefore(node, new TypeInsnNode(Opcodes.CHECKCAST, "java/lang/Boolean")); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false)); - } else { - mn.instructions.insertBefore(node, new TypeInsnNode(Opcodes.CHECKCAST, args[i].getInternalName())); - } - mn.instructions.insertBefore(node, new InsnNode(Opcodes.SWAP)); - } - mn.instructions.insertBefore(node, new InsnNode(Opcodes.POP)); - } - } - } else if (checkName(mn.name, "func_152790_a") - && (mn.desc.equals("(Lcom/mojang/authlib/GameProfile;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;Z)V") // 1.13.2- - || mn.desc.equals("(Lcom/mojang/authlib/GameProfile;Lnet/minecraft/client/resources/SkinManager$ISkinAvailableCallback;Z)V"))) { // 1.14.2+ - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKEINTERFACE - && ((node.owner.equals("java/util/concurrent/ExecutorService") && checkName(node.name, "submit") && node.desc.equals("(Ljava/lang/Runnable;)Ljava/util/concurrent/Future;")) // 1.14.4- - || (node.owner.equals("java/util/concurrent/Executor") && checkName(node.name, "execute") && node.desc.equals("(Ljava/lang/Runnable;)V")))) { // 1.15+ - if (node.desc.endsWith("V")) { - mn.instructions.insert(node, new InsnNode(Opcodes.POP)); - } - mn.instructions.set(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "loadProfileTextures", "(Ljava/lang/Runnable;)V", false)); - } - } - } else if (((checkName(mn.name, "func_210275_a") || checkName(mn.name, "lambda$loadProfileTextures$1")) // 1.14.4- - || (checkName(mn.name, "func_229293_a_") || checkName(mn.name, "lambda$loadProfileTextures$4"))) // 1.15+ - && (mn.desc.equals("(Lcom/mojang/authlib/GameProfile;ZLnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)V") // 1.13.2- - || mn.desc.equals("(Lcom/mojang/authlib/GameProfile;ZLnet/minecraft/client/resources/SkinManager$ISkinAvailableCallback;)V"))) { // 1.14.2+ - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKEINTERFACE && node.owner.equals("com/mojang/authlib/minecraft/MinecraftSessionService") && checkName(node.name, "getTextures") && node.desc.equals("(Lcom/mojang/authlib/GameProfile;Z)Ljava/util/Map;")) { - mn.instructions.set(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "getUserProfile", "(Lcom/mojang/authlib/minecraft/MinecraftSessionService;Lcom/mojang/authlib/GameProfile;Z)Ljava/util/Map;", false)); - break; - } - } - } else if ((checkName(mn.name, "func_210276_a") || checkName(mn.name, "lambda$null$0")) - && (mn.desc.equals("(Ljava/util/Map;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)V") // 1.13.2- - || mn.desc.equals("(Ljava/util/Map;Lnet/minecraft/client/resources/SkinManager$ISkinAvailableCallback;)V"))) { // 1.14.2 ~ 1.14.4 - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.RETURN) { - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 0)); - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 1)); - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 2)); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "loadElytraTexture", "(Lnet/minecraft/client/resources/SkinManager;" + mn.desc.substring(1), false)); - } - } - } else if ((checkName(mn.name, "func_229297_b_") || checkName(mn.name, "lambda$null$2")) && mn.desc.equals("(Ljava/util/Map;Lnet/minecraft/client/resources/SkinManager$ISkinAvailableCallback;)V")) { // 1.15+ - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKESTATIC && node.owner.equals("com/google/common/collect/ImmutableList") && checkName(node.name, "of") && node.desc.equals("(Ljava/lang/Object;Ljava/lang/Object;)Lcom/google/common/collect/ImmutableList;")) { - mn.instructions.insertBefore(node, new InsnNode(Opcodes.POP2)); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "com/mojang/authlib/minecraft/MinecraftProfileTexture$Type", "values", "()[Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;")) - mn.instructions.set(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "com/google/common/collect/ImmutableList", "copyOf", "([Ljava/lang/Object;)Lcom/google/common/collect/ImmutableList;")) - } - } - } else if (checkName(mn.name, "func_152788_a") && mn.desc.equals("(Lcom/mojang/authlib/GameProfile;)Ljava/util/Map;")) { - var first = mn.instructions.getFirst(); - mn.instructions.insertBefore(first, new VarInsnNode(Opcodes.ALOAD, 1)); - mn.instructions.insertBefore(first, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "loadSkinFromCache", "(Lcom/mojang/authlib/GameProfile;)Ljava/util/Map;", false)); - mn.instructions.insertBefore(first, new InsnNode(Opcodes.ARETURN)); - } - }); - return cn; - } - }, - 'PlayerTabTransformer': { - 'target': { - 'type': 'CLASS', - 'names': function (target) { - return ['net/minecraft/client/gui/GuiPlayerTabOverlay', 'net/minecraft/client/gui/overlay/PlayerTabOverlayGui']; - } - }, - 'transformer': function (cn) { - cn.methods.forEach(function (mn) { - if ((checkName(mn.name, "func_175249_a") && mn.desc.equals("(ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V")) // 1.15.2- - || (checkName(mn.name, "func_238523_a_") && mn.desc.equals("(Lcom/mojang/blaze3d/matrix/MatrixStack;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V"))) { // 1.16.1+ - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKEVIRTUAL && node.owner.equals("net/minecraft/client/Minecraft") && checkName(node.name, "func_71387_A") && node.desc.equals("()Z")) { - mn.instructions.insertBefore(node, new InsnNode(Opcodes.POP)); - mn.instructions.set(node, new InsnNode(Opcodes.ICONST_1)); - } - } - } - }); - return cn; - } - }, - 'IImageBufferTransformer': { - 'target': { - 'type': 'CLASS', - 'name': 'net/minecraft/client/renderer/IImageBuffer' - }, - 'transformer': function (cn) { - if (!cn.interfaces.contains("customskinloader/fake/itf/IFakeIImageBuffer")) { - cn.interfaces.add("customskinloader/fake/itf/IFakeIImageBuffer"); - } - return cn; - } - }, - 'NativeImageTransformer': { - 'target': { - 'type': 'CLASS', - 'name': 'net/minecraft/client/renderer/texture/NativeImage' - }, - 'transformer': function (cn) { - if (!cn.interfaces.contains("customskinloader/fake/itf/IFakeNativeImage")) { - cn.interfaces.add("customskinloader/fake/itf/IFakeNativeImage"); - } - return cn; - } - }, - - // For 1.14+ - 'MinecraftTransformer': { - 'target': { - 'type': 'CLASS', - 'name': 'net/minecraft/client/Minecraft' - }, - 'transformer': function (cn) { - cn.interfaces.add("customskinloader/fake/itf/IFakeMinecraft"); - return cn; - } - }, - 'IResourceTransformer': { - 'target': { - 'type': 'CLASS', - 'name': 'net/minecraft/resources/IResource' - }, - 'transformer': function (cn) { - cn.interfaces.add("customskinloader/fake/itf/IFakeIResource$V1"); - cn.interfaces.add("customskinloader/fake/itf/IFakeIResource$V2"); - cn.interfaces.add("net/minecraft/client/resources/IResource"); - return cn; - } - }, - 'IResourceManagerTransformer': { - 'target': { - 'type': 'CLASS', - 'name': 'net/minecraft/resources/IResourceManager' - }, - 'transformer': function (cn) { - cn.interfaces.add("customskinloader/fake/itf/IFakeIResourceManager$V1"); - cn.interfaces.add("net/minecraft/client/resources/IResourceManager"); - return cn; - } - }, - 'ISkinAvailableCallbackTransformer': { - 'target': { - 'type': 'CLASS', - 'name': 'net/minecraft/client/resources/SkinManager$ISkinAvailableCallback' - }, - 'transformer': function (cn) { - cn.interfaces.add("net/minecraft/client/resources/SkinManager$SkinAvailableCallback"); - return cn; - } - }, - - // For 1.15+ - 'DownloadingTextureTransformer': { - 'target': { - 'type': 'CLASS', - 'name': 'net/minecraft/client/renderer/texture/DownloadingTexture' - }, - 'transformer': function (cn) { - cn.methods.forEach(function (mn) { - if (checkName(mn.name, "func_229159_a_") && mn.desc.equals("(Ljava/io/InputStream;)Lnet/minecraft/client/renderer/texture/NativeImage;")) { - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKESTATIC && node.owner.equals("net/minecraft/client/renderer/texture/DownloadingTexture") && checkName(node.name, "func_229163_c_") && node.desc.equals("(Lnet/minecraft/client/renderer/texture/NativeImage;)Lnet/minecraft/client/renderer/texture/NativeImage;")) { - // FakeSkinBuffer.processLegacySkin(image, this.processTask, DownloadingTexture::processLegacySkin); - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 0)); - mn.instructions.insertBefore(node, new FieldInsnNode(Opcodes.GETFIELD, "net/minecraft/client/renderer/texture/DownloadingTexture", mapName("field_229155_i_"), "Ljava/lang/Runnable;")); - mn.instructions.insertBefore(node, new InvokeDynamicInsnNode("apply", "()Ljava/util/function/Function;", - /* bsm */ new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), - /* bsmArgs */ Type.getType("(Ljava/lang/Object;)Ljava/lang/Object;"), - /* bsmArgs */ new Handle(Opcodes.H_INVOKESTATIC, "net/minecraft/client/renderer/texture/DownloadingTexture", mapName("func_229163_c_"), "(Lnet/minecraft/client/renderer/texture/NativeImage;)Lnet/minecraft/client/renderer/texture/NativeImage;", false), - /* bsmArgs */ Type.getType("(Lnet/minecraft/client/renderer/texture/NativeImage;)Lnet/minecraft/client/renderer/texture/NativeImage;"))); - iterator.set(new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinBuffer", "processLegacySkin", "(Lnet/minecraft/client/renderer/texture/NativeImage;Ljava/lang/Runnable;Ljava/util/function/Function;)Lnet/minecraft/client/renderer/texture/NativeImage;", false)); - } - } - } - }); - return cn; - } - }, - 'RenderPlayer_LayerCapeTransformer': { - 'target': { - 'type': 'CLASS', - 'names': function (target) { - return ['net/minecraft/client/renderer/entity/PlayerRenderer', 'net/minecraft/client/renderer/entity/layers/CapeLayer']; - } - }, - 'transformer': function (cn) { - cn.methods.forEach(function (mn) { - if ((checkName(mn.name, "func_229145_a_") && mn.desc.equals("(Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;ILnet/minecraft/client/entity/player/AbstractClientPlayerEntity;Lnet/minecraft/client/renderer/model/ModelRenderer;Lnet/minecraft/client/renderer/model/ModelRenderer;)V")) // PlayerRenderer.renderItem - || (checkName(mn.name, "func_225628_a_") && mn.desc.equals("(Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;ILnet/minecraft/client/entity/player/AbstractClientPlayerEntity;FFFFFF)V"))) { // CapeLayer.render - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKESTATIC && node.owner.equals("net/minecraft/client/renderer/RenderType") && checkName(node.name, "func_228634_a_") && node.desc.equals("(Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/RenderType;")) { // RenderType.getEntitySolid - node.name = mapName("func_228644_e_"); // RenderType.getEntityTranslucent - } - } - } - }); - return cn; - } - } - }; -} diff --git a/Forge/V2/Forge.tsrg b/Forge/V2/Forge.tsrg deleted file mode 100644 index 5bb36856..00000000 --- a/Forge/V2/Forge.tsrg +++ /dev/null @@ -1,80 +0,0 @@ -customskinloader/fake/itf/IFakeIResource$V1 customskinloader/fake/itf/IFakeIResource$V1 - func_199027_b ()Ljava/io/InputStream; m_6679_ -customskinloader/fake/itf/IFakeIResource$V2 customskinloader/fake/itf/IFakeIResource$V2 - open ()Ljava/io/InputStream; m_215507_ -customskinloader/fake/itf/IFakeIResourceManager$V1 customskinloader/fake/itf/IFakeIResourceManager$V1 - func_199002_a (Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/resources/IResource; m_142591_ - getResource (Lnet/minecraft/util/ResourceLocation;)Ljava/util/Optional; m_213713_ -customskinloader/fake/itf/IFakeMinecraft customskinloader/fake/itf/IFakeMinecraft - func_195551_G ()Lnet/minecraft/resources/IResourceManager; m_91098_ -customskinloader/fake/itf/IFakeNativeImage customskinloader/fake/itf/IFakeNativeImage - getPixel (II)I getPixel - setPixel (III)V setPixel -net/minecraft/client/Minecraft net/minecraft/client/Minecraft - gameDir f_91069_ - getCurrentServerData ()Lnet/minecraft/client/multiplayer/ServerData; m_91089_ - getMinecraft ()Lnet/minecraft/client/Minecraft; m_91087_ - getResourceManager ()Lnet/minecraft/client/resources/IResourceManager; m_91098_ - getSkinManager ()Lnet/minecraft/client/resources/SkinManager; m_91109_ - getTextureManager ()Lnet/minecraft/client/renderer/texture/TextureManager; m_91097_ -net/minecraft/client/gui/GuiPlayerTabOverlay net/minecraft/client/gui/components/PlayerTabOverlay -net/minecraft/client/multiplayer/ServerData net/minecraft/client/multiplayer/ServerData - serverIP f_105363_ -net/minecraft/client/renderer/RenderType net/minecraft/client/renderer/RenderType - func_228644_e_ (Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/RenderType; m_110473_ -net/minecraft/client/renderer/IImageBuffer customskinloader/fake/IFakeImageBuffer - func_195786_a (Lnet/minecraft/client/renderer/texture/NativeImage;)Lnet/minecraft/client/renderer/texture/NativeImage; func_195786_a - parseUserSkin (Ljava/awt/image/BufferedImage;)Ljava/awt/image/BufferedImage; parseUserSkin - skinAvailable ()V skinAvailable -net/minecraft/client/renderer/ThreadDownloadImageData net/minecraft/client/renderer/texture/HttpTexture -net/minecraft/client/renderer/entity/RenderPlayer net/minecraft/client/renderer/entity/player/PlayerRenderer -net/minecraft/client/renderer/entity/layers/LayerCape net/minecraft/client/renderer/entity/layers/CapeLayer -net/minecraft/client/renderer/rendertype/RenderType net/minecraft/client/renderer/rendertype/RenderType -net/minecraft/client/renderer/rendertype/RenderTypes net/minecraft/client/renderer/rendertype/RenderTypes - entityTranslucent (Lnet/minecraft/resources/Identifier;)Lnet/minecraft/client/renderer/rendertype/RenderType; entityTranslucent -net/minecraft/client/renderer/texture/AbstractTexture net/minecraft/client/renderer/texture/AbstractTexture -net/minecraft/client/renderer/texture/ITextureObject net/minecraft/client/renderer/texture/ITextureObject -net/minecraft/client/renderer/texture/NativeImage com/mojang/blaze3d/platform/NativeImage - func_195699_a (IIIIIIZZ)V m_85025_ - func_195700_a (III)V m_84988_ - func_195702_a ()I m_84982_ - func_195703_a (Lnet/minecraft/client/renderer/texture/NativeImage;)V m_85054_ - func_195709_a (II)I m_84985_ - func_195713_a (Ljava/io/InputStream;)Lnet/minecraft/client/renderer/texture/NativeImage; m_85058_ - func_195714_b ()I m_85084_ - func_195715_a (IIIII)V m_84997_ -net/minecraft/client/renderer/texture/ReloadableTexture net/minecraft/client/renderer/texture/ReloadableTexture -net/minecraft/client/renderer/texture/SkinTextureDownloader net/minecraft/client/renderer/texture/SkinTextureDownloader -net/minecraft/client/renderer/texture/SimpleTexture net/minecraft/client/renderer/texture/SimpleTexture - loadContents (Lnet/minecraft/client/resources/IResourceManager;)Lnet/minecraft/client/renderer/texture/TextureContents; loadContents -net/minecraft/client/renderer/texture/TextureContents net/minecraft/client/renderer/texture/TextureContents -net/minecraft/client/renderer/texture/TextureManager net/minecraft/client/renderer/texture/TextureManager - getTexture (Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/texture/ITextureObject; func_110581_b - loadTexture (Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/client/renderer/texture/AbstractTexture;)V m_118495_ - loadTexture (Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/client/renderer/texture/ITextureObject;)Z func_110579_a - registerAndLoad (Lnet/minecraft/resources/Identifier;Lnet/minecraft/client/renderer/texture/ReloadableTexture;)V registerAndLoad - registerAndLoad (Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/client/renderer/texture/ReloadableTexture;)V registerAndLoad -net/minecraft/client/resources/DefaultPlayerSkin net/minecraft/client/resources/DefaultPlayerSkin - getDefaultSkin (Ljava/util/UUID;)Lnet/minecraft/util/ResourceLocation; m_118627_ -net/minecraft/client/resources/IResource net/minecraft/server/packs/resources/Resource - getInputStream ()Ljava/io/InputStream; m_6679_ -net/minecraft/client/resources/IResourceManager net/minecraft/server/packs/resources/ResourceManager - getResource (Lnet/minecraft/resources/Identifier;)Ljava/util/Optional; getResource - getResource (Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/resources/IResource; m_142591_ -net/minecraft/client/resources/SkinManager net/minecraft/client/resources/SkinManager - loadProfileTextures (Lcom/mojang/authlib/GameProfile;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;Z)V m_118817_ - loadSkin (Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;)Lnet/minecraft/util/ResourceLocation; m_118825_ -net/minecraft/client/resources/SkinManager$1 net/minecraft/client/resources/SkinManager$1 -net/minecraft/client/resources/SkinManager$CacheKey net/minecraft/client/resources/SkinManager$CacheKey -net/minecraft/client/resources/SkinManager$SkinAvailableCallback net/minecraft/client/resources/SkinManager$SkinTextureCallback - skinAvailable (Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/util/ResourceLocation;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;)V m_118856_ -net/minecraft/client/resources/SkinManager$TextureCache net/minecraft/client/resources/SkinManager$TextureCache -net/minecraft/client/resources/data/TextureMetadataSection net/minecraft/client/resources/metadata/texture/TextureMetadataSection -net/minecraft/resources/IResource net/minecraft/server/packs/resources/Resource -net/minecraft/resources/IResourceManager net/minecraft/server/packs/resources/ResourceManager -net/minecraft/resources/Identifier net/minecraft/resources/Identifier - fromNamespaceAndPath (Ljava/lang/String;Ljava/lang/String;)Lnet/minecraft/resources/Identifier; fromNamespaceAndPath -net/minecraft/server/Services net/minecraft/server/Services -net/minecraft/util/ResourceLocation net/minecraft/resources/ResourceLocation -net/minecraft/util/StringUtils net/minecraft/util/StringUtil - stripControlCodes (Ljava/lang/String;)Ljava/lang/String; m_14406_ diff --git a/Forge/V2/build.gradle b/Forge/V2/build.gradle deleted file mode 100644 index fec2c337..00000000 --- a/Forge/V2/build.gradle +++ /dev/null @@ -1,25 +0,0 @@ - -jar { - manifest { - attributes([ - "Automatic-Module-Name" : "customskinloader" - ]) - } - - exclude 'net/minecraft/client/resources/**' -} -sourceJar { - exclude 'com/**' - exclude 'net/**' -} - -dependencies { - implementation project(':Dummy') - implementation project(":Forge/Common") -} - -import customskinloader.gradle.util.RemapUtil -import customskinloader.gradle.util.SourceUtil - -RemapUtil.remapSources project -SourceUtil.addDependencies project, project(":Common") diff --git a/Forge/V2/build.properties b/Forge/V2/build.properties deleted file mode 100644 index e2d58bf9..00000000 --- a/Forge/V2/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -dependencies=Forge|1.17.1,1.18,1.18.1,1.18.2,1.19,1.19.1,1.19.2,1.19.3,1.19.4,1.20,1.20.1,1.20.2,1.20.3,1.20.4;\ - NeoForge|1.20.1 -java_full_versions=16,17,18,19,20,21,22 -#forge gradle needs forge version -forge_mc_version=1.12.2 -forge_version=14.23.5.2768 -mappings_version=stable_39 -srg_notch_map=Forge.tsrg diff --git a/Forge/V2/src/main/java/customskinloader/forge/ForgeMod.java b/Forge/V2/src/main/java/customskinloader/forge/ForgeMod.java deleted file mode 100644 index e30bd785..00000000 --- a/Forge/V2/src/main/java/customskinloader/forge/ForgeMod.java +++ /dev/null @@ -1,16 +0,0 @@ -package customskinloader.forge; - -import net.minecraftforge.fml.IExtensionPoint; -import net.minecraftforge.fml.ModLoadingContext; -import net.minecraftforge.fml.common.Mod; - -@Mod("customskinloader") -public class ForgeMod { - public ForgeMod() { - this.setExtensionPoint(); - } - - private void setExtensionPoint() { - ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class, () -> new IExtensionPoint.DisplayTest(() -> "customskinloader", (remote, isServer) -> isServer)); - } -} diff --git a/Forge/V2/src/main/resources/META-INF/accesstransformer.cfg b/Forge/V2/src/main/resources/META-INF/accesstransformer.cfg deleted file mode 100644 index b329ae55..00000000 --- a/Forge/V2/src/main/resources/META-INF/accesstransformer.cfg +++ /dev/null @@ -1,2 +0,0 @@ -public-f net.minecraft.client.resources.SkinManager$CacheKey # 23w31a+ -public net.minecraft.client.resources.SkinManager$CacheKey (Ljava/util/UUID;Lcom/mojang/authlib/properties/Property;)V # 23w42a+ diff --git a/Forge/V2/src/main/resources/META-INF/coremods.json b/Forge/V2/src/main/resources/META-INF/coremods.json deleted file mode 100644 index de3761fd..00000000 --- a/Forge/V2/src/main/resources/META-INF/coremods.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "transformers": "transformers.js" -} diff --git a/Forge/V2/src/main/resources/pack.mcmeta b/Forge/V2/src/main/resources/pack.mcmeta deleted file mode 100644 index 94737498..00000000 --- a/Forge/V2/src/main/resources/pack.mcmeta +++ /dev/null @@ -1,6 +0,0 @@ -{ - "pack": { - "description": "CustomSkinLoader resources", - "pack_format": 7 - } -} diff --git a/Forge/V2/src/main/resources/transformers.js b/Forge/V2/src/main/resources/transformers.js deleted file mode 100644 index 46bdadd9..00000000 --- a/Forge/V2/src/main/resources/transformers.js +++ /dev/null @@ -1,332 +0,0 @@ -var ASMAPI = getJavaType('net.minecraftforge.coremod.api.ASMAPI'); -var Handle = getJavaType('org.objectweb.asm.Handle'); -var Opcodes = getJavaType('org.objectweb.asm.Opcodes'); -var Type = getJavaType('org.objectweb.asm.Type'); -var FieldInsnNode = getJavaType('org.objectweb.asm.tree.FieldInsnNode'); -var InsnNode = getJavaType('org.objectweb.asm.tree.InsnNode'); -var IntInsnNode = getJavaType('org.objectweb.asm.tree.IntInsnNode'); -var InvokeDynamicInsnNode = getJavaType('org.objectweb.asm.tree.InvokeDynamicInsnNode'); -var MethodInsnNode = getJavaType('org.objectweb.asm.tree.MethodInsnNode'); -var TypeInsnNode = getJavaType('org.objectweb.asm.tree.TypeInsnNode'); -var VarInsnNode = getJavaType('org.objectweb.asm.tree.VarInsnNode'); - -function getJavaType(name) { - return Java.type(name); -} - -// Compare method and field names. -function checkName(name, srgName) { - return name.equals(mapName(srgName)); -} - -// De-obfuscate method and field names. -function mapName(srgName) { - if (srgName.startsWith("f_")) return ASMAPI.mapField(srgName); - if (srgName.startsWith("m_")) return ASMAPI.mapMethod(srgName); - return srgName; -} - -function initializeCoreMod() { - return { - 'SkinManagerTransformer': { - 'target': { - 'type': 'CLASS', - 'name': 'net/minecraft/client/resources/SkinManager' - }, - 'transformer': function (cn) { - cn.methods.forEach(function (mn) { - if (checkName(mn.name, "") && mn.desc.equals("(Lnet/minecraft/client/renderer/texture/TextureManager;Ljava/io/File;Lcom/mojang/authlib/minecraft/MinecraftSessionService;)V")) { // 1.20.1- - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.RETURN) { - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 2)); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "setSkinCacheDir", "(Ljava/io/File;)V", false)); - } - } - } else if (checkName(mn.name, "") && mn.desc.equals("(Lnet/minecraft/client/renderer/texture/TextureManager;Ljava/nio/file/Path;Lcom/mojang/authlib/minecraft/MinecraftSessionService;Ljava/util/concurrent/Executor;)V")) { // 1.20.2+ - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.RETURN) { - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 2)); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "setSkinCacheDir", "(Ljava/nio/file/Path;)V", false)); - } - } - } else if (checkName(mn.name, "m_118828_") && mn.desc.equals("(Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/client/resources/SkinManager$SkinTextureCallback;)Lnet/minecraft/resources/ResourceLocation;")) { // 1.20.1- - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKESPECIAL && (node.owner.equals("net/minecraft/client/renderer/texture/HttpTexture") && checkName(node.name, "") && node.desc.equals("(Ljava/io/File;Ljava/lang/String;Lnet/minecraft/resources/ResourceLocation;ZLjava/lang/Runnable;)V"))) { - var s = "("; - var args = Type.getType(node.desc).getArgumentTypes(); - for (var i = 0; i < args.length; i++) { - s = s + "Ljava/lang/Object;"; - } - mn.instructions.insertBefore(node, new InsnNode(Opcodes.SWAP)); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false)); - mn.instructions.insertBefore(node, new InsnNode(Opcodes.SWAP)); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "com/google/common/collect/ImmutableList", "of", s + ")Lcom/google/common/collect/ImmutableList;", false)); - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 1)); - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 2)); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "createThreadDownloadImageData", "(Lcom/google/common/collect/ImmutableList;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;)[Ljava/lang/Object;", false)); - for (var i = 0; i < args.length; i++) { - mn.instructions.insertBefore(node, new InsnNode(Opcodes.DUP)); - mn.instructions.insertBefore(node, new IntInsnNode(Opcodes.BIPUSH, i)); - mn.instructions.insertBefore(node, new InsnNode(Opcodes.AALOAD)); - if (args[i].getInternalName().equals("Z")) { - mn.instructions.insertBefore(node, new TypeInsnNode(Opcodes.CHECKCAST, "java/lang/Boolean")); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false)); - } else { - mn.instructions.insertBefore(node, new TypeInsnNode(Opcodes.CHECKCAST, args[i].getInternalName())); - } - mn.instructions.insertBefore(node, new InsnNode(Opcodes.SWAP)); - } - mn.instructions.insertBefore(node, new InsnNode(Opcodes.POP)); - } - } - } else if (checkName(mn.name, "m_118817_") && mn.desc.equals("(Lcom/mojang/authlib/GameProfile;Lnet/minecraft/client/resources/SkinManager$SkinTextureCallback;Z)V")) { // 1.20.1- - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKEINTERFACE - && (node.owner.equals("java/util/concurrent/Executor") // 1.17.1 - || node.owner.equals("java/util/concurrent/ExecutorService")) // 1.18+ - && checkName(node.name, "execute") && node.desc.equals("(Ljava/lang/Runnable;)V")) { - mn.instructions.insert(node, new InsnNode(Opcodes.POP)); - mn.instructions.set(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "loadProfileTextures", "(Ljava/lang/Runnable;)V", false)); - } - } - } else if (checkName(mn.name, "m_118821_") && mn.desc.equals("(Lcom/mojang/authlib/GameProfile;ZLnet/minecraft/client/resources/SkinManager$SkinTextureCallback;)V")) { // 1.20.1- - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKEINTERFACE && node.owner.equals("com/mojang/authlib/minecraft/MinecraftSessionService") && checkName(node.name, "getTextures") && node.desc.equals("(Lcom/mojang/authlib/GameProfile;Z)Ljava/util/Map;")) { - mn.instructions.set(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "getUserProfile", "(Lcom/mojang/authlib/minecraft/MinecraftSessionService;Lcom/mojang/authlib/GameProfile;Z)Ljava/util/Map;", false)); - break; - } - } - } else if (checkName(mn.name, "m_174849_") && mn.desc.equals("(Ljava/util/Map;Lnet/minecraft/client/resources/SkinManager$SkinTextureCallback;)V")) { // 1.20.1- - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKESTATIC && node.owner.equals("com/google/common/collect/ImmutableList") && checkName(node.name, "of") && node.desc.equals("(Ljava/lang/Object;Ljava/lang/Object;)Lcom/google/common/collect/ImmutableList;")) { - mn.instructions.insertBefore(node, new InsnNode(Opcodes.POP2)); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "com/mojang/authlib/minecraft/MinecraftProfileTexture$Type", "values", "()[Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;")); - mn.instructions.set(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "com/google/common/collect/ImmutableList", "copyOf", "([Ljava/lang/Object;)Lcom/google/common/collect/ImmutableList;")); - } - } - } else if (checkName(mn.name, "m_118815_") && mn.desc.equals("(Lcom/mojang/authlib/GameProfile;)Ljava/util/Map;")) { // 1.20.1- - var first = mn.instructions.getFirst(); - mn.instructions.insertBefore(first, new VarInsnNode(Opcodes.ALOAD, 1)); - mn.instructions.insertBefore(first, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "loadSkinFromCache", "(Lcom/mojang/authlib/GameProfile;)Ljava/util/Map;", false)); - mn.instructions.insertBefore(first, new InsnNode(Opcodes.ARETURN)); - } else if (checkName(mn.name, "m_293351_") && mn.desc.equals("(Lcom/mojang/authlib/GameProfile;)Ljava/util/concurrent/CompletableFuture;")) { // 1.20.2+ - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.NEW && node.desc.equals("net/minecraft/client/resources/SkinManager$CacheKey")) { - iterator.remove(); - iterator.next(); // DUP - iterator.remove(); - } else if (node.getOpcode() === Opcodes.INVOKESPECIAL && node.owner.equals("net/minecraft/client/resources/SkinManager$CacheKey") && checkName(node.name, "") && node.desc.equals("(Ljava/util/UUID;Lcom/mojang/authlib/properties/Property;)V")) { - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 1)); - mn.instructions.set(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager$FakeCacheKey", "createFakeCacheKey", "(Ljava/util/UUID;Lcom/mojang/authlib/properties/Property;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/client/resources/SkinManager$CacheKey;", false)); - } - } - } - }); - return cn; - } - }, - 'SkinManager$1Transformer': { // 1.20.2+ - 'target': { - 'type': 'CLASS', - 'name': 'net/minecraft/client/resources/SkinManager$1' - }, - 'transformer': function (cn) { - cn.methods.forEach(function (mn) { - if (checkName(mn.name, "load") && mn.desc.equals("(Lnet/minecraft/client/resources/SkinManager$CacheKey;)Ljava/util/concurrent/CompletableFuture;")) { - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if ((node.getOpcode() === Opcodes.INVOKESTATIC - || node.getOpcode() === Opcodes.INVOKEVIRTUAL) - && node.owner.equals("java/util/concurrent/CompletableFuture") - && (checkName(node.name, "supplyAsync") - || checkName(node.name, "thenComposeAsync")) - && (node.desc.equals("(Ljava/util/function/Supplier;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;") - || node.desc.equals("(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))) { - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "loadProfileTextures", "(Ljava/util/concurrent/Executor;)Ljava/util/concurrent/Executor;", false)); - } - } - } else if (checkName(mn.name, "m_293645_") && mn.desc.equals("(Lcom/mojang/authlib/minecraft/MinecraftSessionService;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/client/resources/SkinManager$TextureInfo;")) { // 1.20.2 - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKEINTERFACE && node.owner.equals("com/mojang/authlib/minecraft/MinecraftSessionService") && checkName(node.name, "getTextures") && node.desc.equals("(Lcom/mojang/authlib/GameProfile;Z)Ljava/util/Map;")) { - mn.instructions.set(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "loadSkinFromCache", "(Lcom/mojang/authlib/minecraft/MinecraftSessionService;Lcom/mojang/authlib/GameProfile;Z)Ljava/util/Map;", false)); - } - } - } else if (checkName(mn.name, "m_304063_") && mn.desc.equals("(Lnet/minecraft/client/resources/SkinManager$CacheKey;Lcom/mojang/authlib/minecraft/MinecraftSessionService;)Lcom/mojang/authlib/minecraft/MinecraftProfileTextures;")) { // 1.20.3+ - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKEINTERFACE && node.owner.equals("com/mojang/authlib/minecraft/MinecraftSessionService") && checkName(node.name, "unpackTextures") && node.desc.equals("(Lcom/mojang/authlib/properties/Property;)Lcom/mojang/authlib/minecraft/MinecraftProfileTextures;")) { - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 0)); - mn.instructions.insert(node, new TypeInsnNode(Opcodes.CHECKCAST, "com/mojang/authlib/minecraft/MinecraftProfileTextures")); - mn.instructions.set(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "loadSkinFromCache", "(Lcom/mojang/authlib/minecraft/MinecraftSessionService;Lcom/mojang/authlib/properties/Property;Lnet/minecraft/client/resources/SkinManager$CacheKey;)Ljava/lang/Object;", false)); - } - } - } - }); - return cn; - } - }, - 'IResourceTransformer': { - 'target': { - 'type': 'CLASS', - 'name': 'net/minecraft/server/packs/resources/Resource' - }, - 'transformer': function (cn) { - cn.interfaces.add("customskinloader/fake/itf/IFakeIResource$V1"); - cn.interfaces.add("customskinloader/fake/itf/IFakeIResource$V2"); - return cn; - } - }, - 'IResourceManagerTransformer': { - 'target': { - 'type': 'CLASS', - 'name': 'net/minecraft/server/packs/resources/ResourceManager' - }, - 'transformer': function (cn) { - cn.interfaces.add("customskinloader/fake/itf/IFakeIResourceManager$V1"); - return cn; - } - }, - 'MinecraftTransformer': { - 'target': { - 'type': 'CLASS', - 'name': 'net/minecraft/client/Minecraft' - }, - 'transformer': function (cn) { - cn.interfaces.add("customskinloader/fake/itf/IFakeMinecraft"); - return cn; - } - }, - 'NativeImageTransformer': { - 'target': { - 'type': 'CLASS', - 'name': 'com/mojang/blaze3d/platform/NativeImage' - }, - 'transformer': function (cn) { - if (!cn.interfaces.contains("customskinloader/fake/itf/IFakeNativeImage")) { - cn.interfaces.add("customskinloader/fake/itf/IFakeNativeImage"); - } - return cn; - } - }, - 'PlayerTabOverlayTransformer': { - 'target': { - 'type': 'CLASS', - 'name': 'net/minecraft/client/gui/components/PlayerTabOverlay' - }, - 'transformer': function (cn) { - cn.methods.forEach(function (mn) { - if ((checkName(mn.name, "m_94544_") && mn.desc.equals("(Lcom/mojang/blaze3d/vertex/PoseStack;ILnet/minecraft/world/scores/Scoreboard;Lnet/minecraft/world/scores/Objective;)V")) // 1.19.4- - || (checkName(mn.name, "m_280406_") && mn.desc.equals("(Lnet/minecraft/client/gui/GuiGraphics;ILnet/minecraft/world/scores/Scoreboard;Lnet/minecraft/world/scores/Objective;)V"))) { // 1.20+ - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKEVIRTUAL && node.owner.equals("net/minecraft/client/Minecraft") && checkName(node.name, "m_91090_") && node.desc.equals("()Z")) { - mn.instructions.insertBefore(node, new InsnNode(Opcodes.POP)); - mn.instructions.set(node, new InsnNode(Opcodes.ICONST_1)); - } - } - } - }); - return cn; - } - }, - 'RenderPlayer_LayerCapeTransformer': { - 'target': { - 'type': 'CLASS', - 'names': function (target) { - return ['net/minecraft/client/renderer/entity/player/PlayerRenderer', 'net/minecraft/client/renderer/entity/layers/CapeLayer']; - } - }, - 'transformer': function (cn) { - cn.methods.forEach(function (mn) { - if ((checkName(mn.name, "m_117775_") && mn.desc.equals("(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/client/player/AbstractClientPlayer;Lnet/minecraft/client/model/geom/ModelPart;Lnet/minecraft/client/model/geom/ModelPart;)V")) // PlayerRenderer.renderHand - || (checkName(mn.name, "m_6494_") && mn.desc.equals("(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/client/player/AbstractClientPlayer;FFFFFF)V"))) { // CapeLayer.render - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKESTATIC && node.owner.equals("net/minecraft/client/renderer/RenderType") && checkName(node.name, "m_110446_") && node.desc.equals("(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/renderer/RenderType;")) { // RenderType.entitySolid - node.name = mapName("m_110473_"); // RenderType.entityTranslucent - } - } - } - }); - return cn; - } - }, - - 'SkinManager$TextureCacheTransformer': { // 1.20.2+ - 'target': { - 'type': 'METHOD', - 'class': 'net/minecraft/client/resources/SkinManager$TextureCache', - 'methodName': 'm_294542_', - 'methodDesc': '(Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;)Ljava/util/concurrent/CompletableFuture;' - }, - 'transformer': function (mn) { - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if (node.getOpcode() === Opcodes.INVOKESPECIAL && (node.owner.equals("net/minecraft/client/renderer/texture/HttpTexture") && checkName(node.name, "") && node.desc.equals("(Ljava/io/File;Ljava/lang/String;Lnet/minecraft/resources/ResourceLocation;ZLjava/lang/Runnable;)V"))) { - var s = "("; - var args = Type.getType(node.desc).getArgumentTypes(); - for (var i = 0; i < args.length; i++) { - s = s + "Ljava/lang/Object;"; - } - mn.instructions.insertBefore(node, new InsnNode(Opcodes.SWAP)); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false)); - mn.instructions.insertBefore(node, new InsnNode(Opcodes.SWAP)); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "com/google/common/collect/ImmutableList", "of", s + ")Lcom/google/common/collect/ImmutableList;", false)); - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 1)); - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 0)); - mn.instructions.insertBefore(node, new FieldInsnNode(Opcodes.GETFIELD, "net/minecraft/client/resources/SkinManager$TextureCache", mapName("f_291290_"), "Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;")); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinManager", "createThreadDownloadImageData", "(Lcom/google/common/collect/ImmutableList;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;)[Ljava/lang/Object;", false)); - for (var i = 0; i < args.length; i++) { - mn.instructions.insertBefore(node, new InsnNode(Opcodes.DUP)); - mn.instructions.insertBefore(node, new IntInsnNode(Opcodes.BIPUSH, i)); - mn.instructions.insertBefore(node, new InsnNode(Opcodes.AALOAD)); - if (args[i].getInternalName().equals("Z")) { - mn.instructions.insertBefore(node, new TypeInsnNode(Opcodes.CHECKCAST, "java/lang/Boolean")); - mn.instructions.insertBefore(node, new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false)); - } else { - mn.instructions.insertBefore(node, new TypeInsnNode(Opcodes.CHECKCAST, args[i].getInternalName())); - } - mn.instructions.insertBefore(node, new InsnNode(Opcodes.SWAP)); - } - mn.instructions.insertBefore(node, new InsnNode(Opcodes.POP)); - } - } - return mn; - } - }, - 'HttpTextureTransformer': { - 'target': { - 'type': 'METHOD', - 'class': 'net/minecraft/client/renderer/texture/HttpTexture', - 'methodName': 'm_118018_', - 'methodDesc': '(Ljava/io/InputStream;)Lcom/mojang/blaze3d/platform/NativeImage;' - }, - 'transformer': function (mn) { - for (var iterator = mn.instructions.iterator(); iterator.hasNext();) { - var node = iterator.next(); - if ((node.getOpcode() === Opcodes.INVOKEVIRTUAL || node.getOpcode() === Opcodes.INVOKESPECIAL) && node.owner.equals("net/minecraft/client/renderer/texture/HttpTexture") && checkName(node.name, "m_118032_") && node.desc.equals("(Lcom/mojang/blaze3d/platform/NativeImage;)Lcom/mojang/blaze3d/platform/NativeImage;")) { - // FakeSkinBuffer.processLegacySkin(image, this.onDownloaded, this::processLegacySkin); - mn.instructions.insertBefore(node, new InsnNode(Opcodes.SWAP)); - mn.instructions.insertBefore(node, new FieldInsnNode(Opcodes.GETFIELD, "net/minecraft/client/renderer/texture/HttpTexture", mapName("f_117997_"), "Ljava/lang/Runnable;")); - mn.instructions.insertBefore(node, new VarInsnNode(Opcodes.ALOAD, 0)); - mn.instructions.insertBefore(node, new InvokeDynamicInsnNode("apply", "(Lnet/minecraft/client/renderer/texture/HttpTexture;)Ljava/util/function/Function;", - /* bsm */ new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), - /* bsmArgs */ Type.getType("(Ljava/lang/Object;)Ljava/lang/Object;"), - /* bsmArgs */ new Handle(Opcodes.H_INVOKEVIRTUAL, "net/minecraft/client/renderer/texture/HttpTexture", mapName("m_118032_"), "(Lcom/mojang/blaze3d/platform/NativeImage;)Lcom/mojang/blaze3d/platform/NativeImage;", false), - /* bsmArgs */ Type.getType("(Lcom/mojang/blaze3d/platform/NativeImage;)Lcom/mojang/blaze3d/platform/NativeImage;"))); - iterator.set(new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/fake/FakeSkinBuffer", "processLegacySkin", "(Lcom/mojang/blaze3d/platform/NativeImage;Ljava/lang/Runnable;Ljava/util/function/Function;)Lcom/mojang/blaze3d/platform/NativeImage;", false)); - } - } - return mn; - } - }, - }; -} diff --git a/README.md b/README.md index 012999b4..bbd23318 100644 --- a/README.md +++ b/README.md @@ -1,131 +1,168 @@ # CustomSkinLoader + [![Version](https://img.shields.io/github/v/release/xfl03/MCCustomSkinLoader?label=&logo=V&labelColor=E1F5FE&color=5D87BF&style=for-the-badge)](https://github.com/xfl03/MCCustomSkinLoader/tags) [![CurseForge](https://cf.way2muchnoise.eu/short_CustomSkinLoader.svg?badge_style=for_the_badge)](https://www.curseforge.com/minecraft/mc-mods/customskinloader) [![Modrinth](https://img.shields.io/modrinth/dt/idMHQ4n2?label=&logo=Modrinth&labelColor=white&color=00AF5C&style=for-the-badge)](https://modrinth.com/mod/customskinloader) -[![License](https://img.shields.io/github/license/xfl03/MCCustomSkinLoader?label=&logo=c&style=for-the-badge&color=A8B9CC&labelColor=455A64)](https://github.com/xfl03/MCCustomSkinLoader/blob/14-develop/LICENSE) +[![License](https://img.shields.io/github/license/xfl03/MCCustomSkinLoader?label=&logo=c&style=for-the-badge&color=A8B9CC&labelColor=455A64)](https://github.com/xfl03/MCCustomSkinLoader/blob/master/LICENSE) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/xfl03/MCCustomSkinLoader/beta.yml?style=for-the-badge&label=&logo=Gradle&labelColor=388E3C)](https://github.com/xfl03/MCCustomSkinLoader/actions) [![Star](https://img.shields.io/github/stars/xfl03/MCCustomSkinLoader?label=&logo=GitHub&labelColor=black&color=FAFAFA&style=for-the-badge)](https://github.com/xfl03/MCCustomSkinLoader/stargazers) [![Minecraft Version](https://img.shields.io/badge/Minecraft-1.21%20|%201.20%20|%201.19%20|%201.18%20|%201.17%20|%201.16%20|%201.15%20|%201.14%20|%201.13%20|%201.12%20|%201.11%20|%201.10%20|%201.9%20|%201.8-green?style=for-the-badge&labelColor=388E3C&color=8BC34A)](https://github.com/xfl03/MCCustomSkinLoader) -## What's this? -Custom Skin Loader mod for Minecraft. -It's a mod which can load skins and capes from any online source or from your local. - -## Download +## What's this? + +CustomSkinLoader is a Minecraft mod that loads skins, capes, and elytra textures from online skin APIs or local files. + +This branch is the Universal generation of MCCustomSkinLoader. It replaces the old edition-specific jars with one bootstrap artifact. The bootstrap jar prepares a runtime `CustomSkinLoader-Common.jar`, remaps it for the active Minecraft mapping namespace, and applies the required loader-specific class patches at runtime. + +## Download + ### Release Build + +- [GitHub Releases](https://github.com/xfl03/MCCustomSkinLoader/releases) - [CurseForge](https://www.curseforge.com/minecraft/mc-mods/customskinloader) - [Modrinth](https://modrinth.com/mod/customskinloader) ### Develop Build -- [GitHub Action](https://github.com/xfl03/MCCustomSkinLoader/actions) + +- [GitHub Actions](https://github.com/xfl03/MCCustomSkinLoader/actions) - [33 Kit(Chinese/中文)](https://3-3.dev/csl-download) ## Contact Us + - [Telegram @customskinloader](https://t.me/customskinloader) - [QQ Group(Chinese/中文) 651287593](https://jq.qq.com/?_wv=1027&k=vF16R5tg) -## Feature -### Support Plenty of Skin Load API and Customizable Skin Load List +## Supported Loaders + +- Forge Legacy and Forge ModLauncher +- NeoForge +- Fabric +- Quilt-compatible Fabric Loader environments + +## Feature + +### Universal Runtime + +One user-facing jar is used across supported loaders. The generated installable artifact is: + +```text +Bootstrap/build/libs/CustomSkinLoader_Universal-.jar +``` + +`Common/build/libs/Common-.jar` is the nested runtime payload and should not be installed directly. + +### Support Plenty of Skin Load APIs and Customizable Skin Load List + Supported skin loading APIs: -- [MojangAPI](http://wiki.vg/Mojang_API) -- [CustomSkinAPI](https://github.com/xfl03/CustomSkinLoaderAPI/tree/master/CustomSkinAPI) -- CustomSkinAPIPlus (Test Only) + +- [MojangAPI](https://minecraft.wiki/w/Mojang_API) +- [CustomSkinAPI](https://github.com/xfl03/CustomSkinLoaderAPI/tree/master/CustomSkinAPI) +- CustomSkinAPIPlus - [UniSkinAPI](https://github.com/RecursiveG/UniSkinServer/tree/master/doc) -- [ElyByAPI](https://docs.ely.by/en/api.html) -- Legacy +- [ElyByAPI](https://docs.ely.by/en/api.html) +- Legacy + +Supported special skin sites and profiles: -Supported special skin sites: -- **ANY SITE** implements skin loading API above - [Glitchless](https://games.glitchless.ru/games/minecraft/) - [MinecraftCapes](https://minecraftcapes.net/) -- [Wynntils](https://wynntils.com/) - -You can use this feature to customize your skin load list so as to load your skins from any skin server you want. -If you are the owner of skin server, you can use CustomSkinLoader to load skins from your server if one of the APIs has been actualized by your server. - -### HD Skins Support -Even though you don't have OptiFine and MCPatcher, CustomSkinLoader can still load and render HD skins. -You can easily to get a better view of skins. - -### Skull Support -Fixed skull loading bug, you can apply any skin to your skull now. -Dynamic skull supported. - -### Profile Cache -- Decrease the frenquency of using the network. -- Meanwhile, you can still load profiles when network is unavailable.(*) - -*Only when it is opened in configratulation. - -### Local Skin -Load skins without a skin server. -Furthermore, by using this function you can preview your skins in game and even change the default skin and model. -You can load local skins by using any API (excluding MojangAPI). -*While using default configratulation, just put your skins into `.minecraft/CustomSkinLoader/LocalSkin/(skin|cape)s/{USERNAME}.png`. - -### Extra List -A json file generated by skin servers which supports this feature. -To add a server to your load list, users just need to put the file into `.minecraft/CustomSkinLoader/ExtraList` . - -### Transparent Skin Support -The problem of incorrectly rendering textures has been fixed. - -### Spectator Menu Fixed -By using this mod, you can see correct avatar of players in Spectator Menu rather than steve and alex. - -## Default Load List +- [OptiFineCape](https://optifine.net/home) +- [Cloaks+](https://cloaksplus.com/) +- [Cosmetica](https://cosmetica.cc/) +- [Wynntils](https://wynntils.com/) compatible profile support + +You can customize the skin load list and load textures from any compatible skin server. Skin server owners can also use ExtraList files to help users add their server. + +### HD Skins and Capes Support + +CustomSkinLoader can load and process HD skins and capes. OptiFine cape textures are converted to the standard cape format when needed. + +### Profile Cache + +- Decreases repeated network requests. +- Allows cached profiles to load when the network is unavailable. + +### Local Skin + +Load skins without a skin server. With the default Legacy paths, place textures under: + +```text +.minecraft/CustomSkinLoader/LocalSkin/skins/.png +.minecraft/CustomSkinLoader/LocalSkin/capes/.png +.minecraft/CustomSkinLoader/LocalSkin/elytras/.png +``` + +### Extra List + +Skin servers can provide ExtraList JSON files. Users can put them into `.minecraft/CustomSkinLoader/ExtraList` to add servers to the load list. + +### Transparent Texture Support + +The render patches are designed to keep skin and cape transparency correct on supported versions. + +## Build + +The project should compile on Windows, Linux, and macOS with a suitable Java 25 installation. + +Windows: + +```powershell +.\gradlew.bat clean build --stacktrace +``` + +Linux/macOS: + +```bash +./gradlew clean build --stacktrace +``` + +GitHub Actions intentionally runs on Windows with PowerShell, but local project builds are not Windows-only. + +## Default Load List + - [Mojang](https://minecraft.wiki/w/Mojang_API) (MojangAPI) -- [LittleSkin](https://littleskin.cn/) (CustomSkinAPI) +- [LittleSkin](https://littleskin.cn/) (CustomSkinAPI) - [BlessingSkin](http://skin.prinzeugen.net/) (CustomSkinAPI) -- [ElyBy](http://docs.ely.by/) (ElyByAPI) -- SkinMe (UniSkinAPI) +- [ElyBy](https://docs.ely.by/en/api.html) (ElyByAPI) - [TLauncher](https://tlauncher.org/) (ElyByAPI) -- [Glitchless](https://games.glitchless.ru/games/minecraft/) +- [Glitchless](https://games.glitchless.ru/games/minecraft/) (ElyBy-compatible profile) - LocalSkin (Legacy) - [MinecraftCapes](https://minecraftcapes.net/) - [OptiFineCape](https://optifine.net/home) -- [Wynntils](https://wynntils.com/) - [Cloaks+](https://cloaksplus.com/) -- [LabyMod](https://www.labymod.net/en) - [Cosmetica](https://cosmetica.cc/) -If you want to apply to add other skin server to default list, please go to [issue](https://github.com/JLChnToZ/MCCustomSkinLoader/issues). - -## To Skin Server Owner -CustomSkinLoader is designed for loading from any server, which makes the mod complex. -It's not a good idea to refer to CustomSkinLoader's source code to develop your own skin mod. -It's recommended to use CustomSkinLoader for your server directly. -Furthermore, you can add your server to 'Default Load List'. -You can also use 'ExtraList' which makes it easier for users to add your server into load list. +If you want to add another skin server to the default list, please open an [issue](https://github.com/xfl03/MCCustomSkinLoader/issues). + +## To Skin Server Owner + +CustomSkinLoader is designed for loading textures from many server implementations. It is usually better to support one of CustomSkinLoader's APIs than to copy this mod's internal implementation. If you maintain a skin server, you can provide an ExtraList file or request inclusion in the default load list. ## Development and Contribution -See [CONTRIBUTING.md](CONTRIBUTING.md) -## Copyright & LICENSE +See [CONTRIBUTING.md](CONTRIBUTING.md). + +## Copyright & LICENSE + ### Major Contributor -- 2013-2014 Jeremy Lam([JLChnToZ](https://github.com/JLChnToZ)) -- 2014-2024 Alexander Xia([xfl03](https://github.com/xfl03)) -- 2020-2024 [ZekerZhayard](https://github.com/ZekerZhayard) - -### Binary File -You could not modify binary file. -Feel free to use and share this mod and unmodified file in anyway like modpack. -When using in modpack, you must put 'CustomSkinLoader' in mod list. -You could not repost this mod to any website without permission. -You could not earn money with this mod excluding modpack. - -### Source Code -#### Package 'customskinloader' -Including some codes from + +- 2013-2014 Jeremy Lam ([JLChnToZ](https://github.com/JLChnToZ)) +- 2014-2024 Alexander Xia ([xfl03](https://github.com/xfl03)) +- 2020-2026 [ZekerZhayard](https://github.com/ZekerZhayard) + +### Binary File + +You may use and share the unmodified official binary in modpacks. When used in a modpack, CustomSkinLoader must be listed in the mod list. Do not repost the mod to other websites without permission. + +### Source Code + +The source code is licensed under GPL-3.0-only. See [LICENSE](LICENSE). + +Package `customskinloader` includes code or ideas from: + - [AsteriskTeam/TabIconHackForge](https://gitee.com/AsteriskTeam/TabIconHackForge) (GPLv3) -- [RecursiveG/UniSkinMod](https://github.com/RecursiveG/UniSkinMod) (GPLv3) -- [NekoCaffeine/Alchemy](https://github.com/NekoCaffeine/Alchemy) (GPLv3) +- [RecursiveG/UniSkinMod](https://github.com/RecursiveG/UniSkinMod) (GPLv3) +- [NekoCaffeine/Alchemy](https://github.com/NekoCaffeine/Alchemy) (GPLv3) -``` -This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. -This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -``` -GPLv3: http://www.gnu.org/licenses/gpl.html - -You should change the name of the package to avoid others' misunderstanding. +If you redistribute a modified build, change the package name and clearly mark the build as modified to avoid confusion with official CustomSkinLoader releases. diff --git a/Universal/Universal.tsrg b/Universal/Universal.tsrg deleted file mode 100644 index 201f2a19..00000000 --- a/Universal/Universal.tsrg +++ /dev/null @@ -1,80 +0,0 @@ -customskinloader/fake/itf/IFakeIResource$V1 customskinloader/fake/itf/IFakeIResource$V1 - func_199027_b ()Ljava/io/InputStream; getInputStream -customskinloader/fake/itf/IFakeIResource$V2 customskinloader/fake/itf/IFakeIResource$V2 - open ()Ljava/io/InputStream; open -customskinloader/fake/itf/IFakeIResourceManager$V1 customskinloader/fake/itf/IFakeIResourceManager$V1 - func_199002_a (Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/resources/IResource; getResource - getResource (Lnet/minecraft/util/ResourceLocation;)Ljava/util/Optional; getResource -customskinloader/fake/itf/IFakeMinecraft customskinloader/fake/itf/IFakeMinecraft - func_195551_G ()Lnet/minecraft/resources/IResourceManager; getResourceManager -customskinloader/fake/itf/IFakeNativeImage customskinloader/fake/itf/IFakeNativeImage - getPixel (II)I getPixel - setPixel (III)V setPixel -net/minecraft/client/Minecraft net/minecraft/client/Minecraft - gameDir gameDirectory - getCurrentServerData ()Lnet/minecraft/client/multiplayer/ServerData; getCurrentServer - getMinecraft ()Lnet/minecraft/client/Minecraft; getInstance - getResourceManager ()Lnet/minecraft/client/resources/IResourceManager; getResourceManager - getSkinManager ()Lnet/minecraft/client/resources/SkinManager; getSkinManager - getTextureManager ()Lnet/minecraft/client/renderer/texture/TextureManager; getTextureManager -net/minecraft/client/gui/GuiPlayerTabOverlay net/minecraft/client/gui/components/PlayerTabOverlay -net/minecraft/client/multiplayer/ServerData net/minecraft/client/multiplayer/ServerData - serverIP ip -net/minecraft/client/renderer/RenderType net/minecraft/client/renderer/RenderType - func_228644_e_ (Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/RenderType; entityTranslucent -net/minecraft/client/renderer/IImageBuffer customskinloader/fake/IFakeImageBuffer - func_195786_a (Lnet/minecraft/client/renderer/texture/NativeImage;)Lnet/minecraft/client/renderer/texture/NativeImage; process - parseUserSkin (Ljava/awt/image/BufferedImage;)Ljava/awt/image/BufferedImage; process - skinAvailable ()V onTextureDownloaded -net/minecraft/client/renderer/ThreadDownloadImageData net/minecraft/client/renderer/texture/HttpTexture -net/minecraft/client/renderer/entity/RenderPlayer net/minecraft/client/renderer/entity/player/PlayerRenderer -net/minecraft/client/renderer/entity/layers/LayerCape net/minecraft/client/renderer/entity/layers/CapeLayer -net/minecraft/client/renderer/rendertype/RenderType net/minecraft/client/renderer/rendertype/RenderType -net/minecraft/client/renderer/rendertype/RenderTypes net/minecraft/client/renderer/rendertype/RenderTypes - entityTranslucent (Lnet/minecraft/resources/Identifier;)Lnet/minecraft/client/renderer/rendertype/RenderType; entityTranslucent -net/minecraft/client/renderer/texture/AbstractTexture net/minecraft/client/renderer/texture/AbstractTexture -net/minecraft/client/renderer/texture/ITextureObject net/minecraft/client/renderer/texture/ITextureObject -net/minecraft/client/renderer/texture/NativeImage com/mojang/blaze3d/platform/NativeImage - func_195699_a (IIIIIIZZ)V copyRect - func_195700_a (III)V setPixelRGBA - func_195702_a ()I getWidth - func_195703_a (Lnet/minecraft/client/renderer/texture/NativeImage;)V copyFrom - func_195709_a (II)I getPixelRGBA - func_195713_a (Ljava/io/InputStream;)Lnet/minecraft/client/renderer/texture/NativeImage; read - func_195714_b ()I getHeight - func_195715_a (IIIII)V fillRect -net/minecraft/client/renderer/texture/ReloadableTexture net/minecraft/client/renderer/texture/ReloadableTexture -net/minecraft/client/renderer/texture/SkinTextureDownloader net/minecraft/client/renderer/texture/SkinTextureDownloader -net/minecraft/client/renderer/texture/SimpleTexture net/minecraft/client/renderer/texture/SimpleTexture - loadContents (Lnet/minecraft/client/resources/IResourceManager;)Lnet/minecraft/client/renderer/texture/TextureContents; loadContents -net/minecraft/client/renderer/texture/TextureContents net/minecraft/client/renderer/texture/TextureContents -net/minecraft/client/renderer/texture/TextureManager net/minecraft/client/renderer/texture/TextureManager - getTexture (Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/texture/ITextureObject; getTexture - loadTexture (Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/client/renderer/texture/AbstractTexture;)V register - loadTexture (Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/client/renderer/texture/ITextureObject;)Z register - registerAndLoad (Lnet/minecraft/resources/Identifier;Lnet/minecraft/client/renderer/texture/ReloadableTexture;)V registerAndLoad - registerAndLoad (Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/client/renderer/texture/ReloadableTexture;)V registerAndLoad -net/minecraft/client/resources/DefaultPlayerSkin net/minecraft/client/resources/DefaultPlayerSkin - getDefaultSkin (Ljava/util/UUID;)Lnet/minecraft/util/ResourceLocation; getDefaultSkin -net/minecraft/client/resources/IResource net/minecraft/server/packs/resources/Resource - getInputStream ()Ljava/io/InputStream; getInputStream -net/minecraft/client/resources/IResourceManager net/minecraft/server/packs/resources/ResourceManager - getResource (Lnet/minecraft/resources/Identifier;)Ljava/util/Optional; getResource - getResource (Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/resources/IResource; getResource -net/minecraft/client/resources/SkinManager net/minecraft/client/resources/SkinManager - loadProfileTextures (Lcom/mojang/authlib/GameProfile;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;Z)V registerSkins - loadSkin (Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;)Lnet/minecraft/util/ResourceLocation; registerTexture -net/minecraft/client/resources/SkinManager$1 net/minecraft/client/resources/SkinManager$1 -net/minecraft/client/resources/SkinManager$CacheKey net/minecraft/client/resources/SkinManager$CacheKey -net/minecraft/client/resources/SkinManager$SkinAvailableCallback net/minecraft/client/resources/SkinManager$SkinTextureCallback - skinAvailable (Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/util/ResourceLocation;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;)V onSkinTextureAvailable -net/minecraft/client/resources/SkinManager$TextureCache net/minecraft/client/resources/SkinManager$TextureCache -net/minecraft/client/resources/data/TextureMetadataSection net/minecraft/client/resources/metadata/texture/TextureMetadataSection -net/minecraft/resources/IResource net/minecraft/server/packs/resources/Resource -net/minecraft/resources/IResourceManager net/minecraft/server/packs/resources/ResourceManager -net/minecraft/resources/Identifier net/minecraft/resources/Identifier - fromNamespaceAndPath (Ljava/lang/String;Ljava/lang/String;)Lnet/minecraft/resources/Identifier; fromNamespaceAndPath -net/minecraft/server/Services net/minecraft/server/Services -net/minecraft/util/ResourceLocation net/minecraft/resources/ResourceLocation -net/minecraft/util/StringUtils net/minecraft/util/StringUtil - stripControlCodes (Ljava/lang/String;)Ljava/lang/String; stripColor diff --git a/Universal/build.gradle b/Universal/build.gradle deleted file mode 100644 index 221c88de..00000000 --- a/Universal/build.gradle +++ /dev/null @@ -1,38 +0,0 @@ - -jar { - manifest { - attributes([ - "Automatic-Module-Name" : "customskinloader", - "MixinConfigs": "mixins.customskinloader.json" - ]) - } - - exclude 'net/minecraft/client/resources/**' -} -sourceJar { - exclude 'com/**' - exclude 'net/**' -} - -dependencies { - implementation project(':Dummy') - implementation project(":Forge/Common") -} - -import customskinloader.gradle.util.RemapUtil -import customskinloader.gradle.util.SourceUtil - -apply plugin: 'org.spongepowered.mixin' - -apply from: rootProject.file("buildSrc/patch.gradle") -patchMixin() - -mixin { - add sourceSets.main, "mixins.customskinloader.refmap.json" - reobfSrgFile = "build/mixin.srg" - reobfNotchSrgFile = "build/mixin.srg" -} - -SourceUtil.addDependencies project, project(":Common"), project(":Vanilla/Common") -RemapUtil.remapSources project - diff --git a/Universal/build.properties b/Universal/build.properties deleted file mode 100644 index bf0f8732..00000000 --- a/Universal/build.properties +++ /dev/null @@ -1,10 +0,0 @@ -dependencies=Fabric|26.1;\ - Forge|1.20.6,1.21,1.21.1,1.21.3,1.21.4,1.21.5,1.21.6,1.21.7,1.21.8,1.21.9,1.21.10,1.21.11,26.1;\ - NeoForge|1.20.2,1.20.3,1.20.4,1.20.5,1.20.6,1.21,1.21.1,1.21.2,1.21.3,1.21.4,1.21.5,1.21.6,1.21.7,1.21.8,1.21.9,1.21.10,1.21.11,26.1;\ - Quilt|26.1 -java_full_versions=17,18,19,20,21,22 -#forge gradle needs forge version -forge_mc_version=1.12.2 -forge_version=14.23.5.2768 -mappings_version=stable_39 -srg_notch_map=Universal.tsrg diff --git a/Universal/mixin.tsrg b/Universal/mixin.tsrg deleted file mode 100644 index d737192c..00000000 --- a/Universal/mixin.tsrg +++ /dev/null @@ -1,67 +0,0 @@ -com/mojang/blaze3d/matrix/MatrixStack com/mojang/blaze3d/vertex/PoseStack -net/minecraft/client/Minecraft net/minecraft/client/Minecraft - isIntegratedServerRunning ()Z isLocalServer -net/minecraft/client/entity/AbstractClientPlayer net/minecraft/client/player/AbstractClientPlayer -net/minecraft/client/gui/Gui net/minecraft/client/gui/GuiGraphics -net/minecraft/client/gui/GuiGraphicsExtractor net/minecraft/client/gui/GuiGraphicsExtractor -net/minecraft/client/gui/GuiPlayerTabOverlay net/minecraft/client/gui/components/PlayerTabOverlay - renderPlayerlist (ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V render - renderPlayerlist (Lcom/mojang/blaze3d/matrix/MatrixStack;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V render - renderPlayerlist (Lnet/minecraft/client/gui/Gui;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V render - renderPlayerlist (Lnet/minecraft/client/gui/GuiGraphicsExtractor;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V extractRenderState -net/minecraft/client/model/ModelRenderer net/minecraft/client/model/geom/ModelPart -net/minecraft/client/renderer/IImageBuffer net/minecraft/client/renderer/HttpTextureProcessor -net/minecraft/client/renderer/IRenderTypeBuffer net/minecraft/client/renderer/MultiBufferSource -net/minecraft/client/renderer/RenderType net/minecraft/client/renderer/RenderType - getEntitySolid (Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/RenderType; entitySolid -net/minecraft/client/renderer/SubmitNodeCollector net/minecraft/client/renderer/SubmitNodeCollector -net/minecraft/client/renderer/ThreadDownloadImageData net/minecraft/client/renderer/texture/HttpTexture - processTask onDownloaded - loadTexture (Ljava/io/InputStream;)Lnet/minecraft/client/renderer/texture/NativeImage; load - processLegacySkin (Lnet/minecraft/client/renderer/texture/NativeImage;)Lnet/minecraft/client/renderer/texture/NativeImage; processLegacySkin -net/minecraft/client/renderer/entity/RenderPlayer net/minecraft/client/renderer/entity/player/PlayerRenderer - renderItem (Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;ILnet/minecraft/client/entity/AbstractClientPlayer;Lnet/minecraft/client/model/ModelRenderer;Lnet/minecraft/client/model/ModelRenderer;)V renderHand - renderItem (Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;Lnet/minecraft/client/entity/AbstractClientPlayer;Lnet/minecraft/client/model/ModelRenderer;Lnet/minecraft/client/model/ModelRenderer;)V renderHand -net/minecraft/client/renderer/entity/layers/LayerCape net/minecraft/client/renderer/entity/layers/CapeLayer - doRenderLayer (Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;ILnet/minecraft/client/entity/AbstractClientPlayer;FFFFFF)V render - doRenderLayer (Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;ILnet/minecraft/client/entity/AbstractClientPlayer;FFFFFFF)V render - doRenderLayer (Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;ILnet/minecraft/client/renderer/entity/state/PlayerRenderState;FF)V render - doRenderLayer (Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;ILnet/minecraft/client/renderer/entity/state/AvatarRenderState;FF)V submit -net/minecraft/client/renderer/entity/state/AvatarRenderState net/minecraft/client/renderer/entity/state/AvatarRenderState -net/minecraft/client/renderer/entity/state/PlayerRenderState net/minecraft/client/renderer/entity/state/PlayerRenderState -net/minecraft/client/renderer/rendertype/RenderType net/minecraft/client/renderer/rendertype/RenderType -net/minecraft/client/renderer/rendertype/RenderTypes net/minecraft/client/renderer/rendertype/RenderTypes - entitySolid (Lnet/minecraft/resources/Identifier;)Lnet/minecraft/client/renderer/rendertype/RenderType; entitySolid -net/minecraft/client/renderer/texture/NativeImage com/mojang/blaze3d/platform/NativeImage -net/minecraft/client/renderer/texture/SkinTextureDownloader net/minecraft/client/renderer/texture/SkinTextureDownloader - downloadAndRegisterSkin (Lnet/minecraft/resources/Identifier;Ljava/nio/file/Path;Ljava/lang/String;Z)Ljava/util/concurrent/CompletableFuture; downloadAndRegisterSkin - downloadAndRegisterSkin (Lnet/minecraft/util/ResourceLocation;Ljava/nio/file/Path;Ljava/lang/String;Z)Ljava/util/concurrent/CompletableFuture; downloadAndRegisterSkin - lambda$downloadAndRegisterSkin$0 (Ljava/nio/file/Path;Ljava/lang/String;Z)Lnet/minecraft/client/renderer/texture/NativeImage; lambda$downloadAndRegisterSkin$0 - lambda$downloadAndRegisterSkin$0 (Ljava/nio/file/Path;Lnet/minecraft/core/ClientAsset$DownloadedTexture;Z)Lnet/minecraft/client/renderer/texture/NativeImage; lambda$downloadAndRegisterSkin$0 - processLegacySkin (Lnet/minecraft/client/renderer/texture/NativeImage;Ljava/lang/String;)Lnet/minecraft/client/renderer/texture/NativeImage; processLegacySkin -net/minecraft/client/renderer/texture/TextureManager net/minecraft/client/renderer/texture/TextureManager -net/minecraft/client/resources/SkinManager net/minecraft/client/resources/SkinManager - func_210275_a (Lcom/mojang/authlib/GameProfile;ZLnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)V lambda$registerSkins$4 - func_210276_a (Ljava/util/Map;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)V lambda$registerSkins$2 - get (Lcom/mojang/authlib/GameProfile;)Ljava/util/concurrent/CompletableFuture; get - getOrLoad (Lcom/mojang/authlib/GameProfile;)Ljava/util/concurrent/CompletableFuture; getOrLoad - loadProfileTextures (Lcom/mojang/authlib/GameProfile;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;Z)V registerSkins - loadSkin (Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)Lnet/minecraft/util/ResourceLocation; registerTexture - loadSkinFromCache (Lcom/mojang/authlib/GameProfile;)Ljava/util/Map; getInsecureSkinInformation -net/minecraft/client/resources/SkinManager$1 net/minecraft/client/resources/SkinManager$1 - lambda$load$0 (Lcom/mojang/authlib/minecraft/MinecraftSessionService;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/client/resources/SkinManager$TextureInfo; lambda$load$0 - lambda$load$0 (Lnet/minecraft/client/resources/SkinManager$CacheKey;Lcom/mojang/authlib/minecraft/MinecraftSessionService;)Lcom/mojang/authlib/minecraft/MinecraftProfileTextures; lambda$load$0 - lambda$load$0 (Lnet/minecraft/client/resources/SkinManager$CacheKey;Lnet/minecraft/server/Services;)Lcom/mojang/authlib/minecraft/MinecraftProfileTextures; lambda$load$0 - load (Lnet/minecraft/client/resources/SkinManager$CacheKey;)Ljava/util/concurrent/CompletableFuture; load -net/minecraft/client/resources/SkinManager$CacheKey net/minecraft/client/resources/SkinManager$CacheKey -net/minecraft/client/resources/SkinManager$SkinAvailableCallback net/minecraft/client/resources/SkinManager$SkinTextureCallback -net/minecraft/client/resources/SkinManager$TextureCache net/minecraft/client/resources/SkinManager$TextureCache - type type - registerTexture (Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;)Ljava/util/concurrent/CompletableFuture; registerTexture -net/minecraft/client/resources/SkinManager$TextureInfo net/minecraft/client/resources/SkinManager$TextureInfo -net/minecraft/core/ClientAsset$DownloadedTexture net/minecraft/core/ClientAsset$DownloadedTexture -net/minecraft/resources/Identifier net/minecraft/resources/Identifier -net/minecraft/scoreboard/ScoreObjective net/minecraft/world/scores/Objective -net/minecraft/scoreboard/Scoreboard net/minecraft/world/scores/Scoreboard -net/minecraft/server/Services net/minecraft/server/Services -net/minecraft/util/ResourceLocation net/minecraft/resources/ResourceLocation diff --git a/Universal/src/main/java/customskinloader/forge/ForgeMod.java b/Universal/src/main/java/customskinloader/forge/ForgeMod.java deleted file mode 100644 index e30bd785..00000000 --- a/Universal/src/main/java/customskinloader/forge/ForgeMod.java +++ /dev/null @@ -1,16 +0,0 @@ -package customskinloader.forge; - -import net.minecraftforge.fml.IExtensionPoint; -import net.minecraftforge.fml.ModLoadingContext; -import net.minecraftforge.fml.common.Mod; - -@Mod("customskinloader") -public class ForgeMod { - public ForgeMod() { - this.setExtensionPoint(); - } - - private void setExtensionPoint() { - ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class, () -> new IExtensionPoint.DisplayTest(() -> "customskinloader", (remote, isServer) -> isServer)); - } -} diff --git a/Universal/src/main/java/customskinloader/forge/MixinConfigPlugin.java b/Universal/src/main/java/customskinloader/forge/MixinConfigPlugin.java deleted file mode 100644 index 242a35bd..00000000 --- a/Universal/src/main/java/customskinloader/forge/MixinConfigPlugin.java +++ /dev/null @@ -1,121 +0,0 @@ -package customskinloader.forge; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.module.Configuration; -import java.lang.module.ModuleReference; -import java.lang.module.ResolvedModule; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Supplier; - -import com.google.common.collect.ImmutableList; -import cpw.mods.cl.JarModuleFinder; -import cpw.mods.jarhandling.SecureJar; - -public class MixinConfigPlugin extends customskinloader.mixin.core.MixinConfigPlugin { - private final static MethodHandles.Lookup IMPL_LOOKUP = ((Supplier) () -> { - try { - Class unsafeClass = Class.forName("sun.misc.Unsafe"); - Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe"); - theUnsafeField.setAccessible(true); - Object theUnsafe = theUnsafeField.get(null); - - Method getObjectMethod = unsafeClass.getMethod("getObject", Object.class, long.class); - Method staticFieldBaseMethod = unsafeClass.getMethod("staticFieldBase", Field.class); - Method staticFieldOffsetMethod = unsafeClass.getMethod("staticFieldOffset", Field.class); - - Field implLookupField = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); - return (MethodHandles.Lookup) getObjectMethod.invoke(theUnsafe, staticFieldBaseMethod.invoke(theUnsafe, implLookupField), staticFieldOffsetMethod.invoke(theUnsafe, implLookupField)); - } catch (Throwable t) { - throw new RuntimeException(t); - } - }).get(); - - static { - try { - fixMixinModifyArgs(); - } catch (Throwable t) { - throw new RuntimeException(t); - } - } - - // Dynamically creates and configures a fake module to ensure the package (org.spongepowered.asm.synthetic.args) is correctly loaded. - @SuppressWarnings("unchecked") - private static void fixMixinModifyArgs() throws Throwable { - ClassLoader loader = MixinConfigPlugin.class.getClassLoader(); - Map handles = findPackageLookup(loader.getClass()); - if (handles != null && handles.get("packageLookup") != null) { - Map packageToOurModules = (Map) handles.get("packageLookup").invokeWithArguments(loader); - String packageName = "org.spongepowered.asm.synthetic.args"; - if (packageToOurModules.get(packageName) == null) { - Path moduleRoot = Paths.get("./CustomSkinLoader/FakeModule"); - Path classPath = Files.createDirectories(moduleRoot.resolve(packageName.replace('.', '/'))); - Path classFile = classPath.resolve("package-info.class"); - if (!Files.exists(classFile)) { - Files.createFile(classFile); - } - - Configuration config = Configuration.resolve(JarModuleFinder.of(SecureJar.from(moduleRoot)), ImmutableList.of(ModuleLayer.boot().configuration()), JarModuleFinder.of(), ImmutableList.of("FakeModule")); - ResolvedModule module = config.findModule("FakeModule").orElse(null); - packageToOurModules.put(packageName, module); - - Object configuration = handles.get("configuration").invokeWithArguments(loader); - Map nameToModule = new HashMap<>((Map) handles.get("nameToModuleGetter").invokeWithArguments(configuration)); - nameToModule.put("FakeModule", module); - handles.get("nameToModuleSetter").invokeWithArguments(configuration, nameToModule); - - MethodHandle resolvedRootsGetter = handles.get("resolvedRoots"); - if (resolvedRootsGetter != null) { - ((Map) resolvedRootsGetter.invokeWithArguments(loader)).put("FakeModule", module.reference()); - } - } - } - } - - private static Map findPackageLookup(Class cl) throws Throwable { - if (!ClassLoader.class.equals(cl)) { - Map map = new HashMap<>(); - Field[] fields = cl.getDeclaredFields(); - for (Field field : fields) { - Type type = field.getGenericType(); - if (type instanceof ParameterizedType) { - ParameterizedType paramType = (ParameterizedType) type; - if (Map.class.equals(paramType.getRawType())) { - Class classJarModuleReference = findClass("cpw.mods.cl.JarModuleFinder$JarModuleReference"); - if (Arrays.equals(new Class[] { String.class, ResolvedModule.class }, paramType.getActualTypeArguments())) { - map.put("packageLookup", IMPL_LOOKUP.findGetter(cl, field.getName(), field.getType())); // Forge & NeoForge - } else if (Arrays.equals(new Class[] { String.class, classJarModuleReference }, paramType.getActualTypeArguments())) { - map.put("resolvedRoots", IMPL_LOOKUP.findGetter(cl, field.getName(), field.getType())); // NeoForge - } - } - } else if (Configuration.class.equals(field.getType())) { - map.put("configuration", IMPL_LOOKUP.findGetter(cl, field.getName(), field.getType())); // NeoForge - map.put("nameToModuleGetter", IMPL_LOOKUP.findGetter(field.getType(), "nameToModule", Map.class)); // NeoForge - map.put("nameToModuleSetter", IMPL_LOOKUP.findSetter(field.getType(), "nameToModule", Map.class)); // NeoForge - } - } - if (!map.isEmpty()) { - return map; - } - return findPackageLookup(cl.getSuperclass()); - } - return null; - } - - private static Class findClass(String name) { - try { - return Class.forName(name); - } catch (Throwable t) { - return null; - } - } -} diff --git a/Universal/src/main/resources/META-INF/accesstransformer.cfg b/Universal/src/main/resources/META-INF/accesstransformer.cfg deleted file mode 100644 index 5ff808e2..00000000 --- a/Universal/src/main/resources/META-INF/accesstransformer.cfg +++ /dev/null @@ -1,4 +0,0 @@ -public-f com.mojang.blaze3d.platform.NativeImage # 24w46a+ -public-f net.minecraft.client.resources.SkinManager$CacheKey # 23w42a+ -public net.minecraft.client.resources.SkinManager$CacheKey (Ljava/util/UUID;Lcom/mojang/authlib/properties/Property;)V # 23w42a+ -public net.minecraft.resources.ResourceLocation (Ljava/lang/String;Ljava/lang/String;)V # 24w18a+ diff --git a/Universal/src/main/resources/META-INF/neoforge.mods.toml b/Universal/src/main/resources/META-INF/neoforge.mods.toml deleted file mode 100644 index f8ef3e88..00000000 --- a/Universal/src/main/resources/META-INF/neoforge.mods.toml +++ /dev/null @@ -1,37 +0,0 @@ -modLoader="javafml" -loaderVersion="[2,)" -license="GPL-3.0-only" -issueTrackerURL="https://github.com/xfl03/MCCustomSkinLoader/issues" -[[mods]] -modId="customskinloader" -version="${modFullVersion}" -displayName="CustomSkinLoader" -authors="xfl03, JLChnToZ, ZekerZhayard" -displayTest="IGNORE_ALL_VERSION" -description=''' -Custom Skin Loader for Minecraft -''' - -# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded. -[[mixins]] -config="mixins.customskinloader.json" - -# The [[accessTransformers]] block allows you to declare where your AT file is. -# If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg -#[[accessTransformers]] -#file="META-INF/accesstransformer.cfg" - -# The coremods config file path is not configurable and is always loaded from META-INF/coremods.json - -[[dependencies.customskinloader]] -modId="neoforge" -type="required" -versionRange="[20.5,)" -ordering="NONE" -side="CLIENT" -[[dependencies.customskinloader]] -modId="minecraft" -type="required" -versionRange="[1.20.5,)" -ordering="NONE" -side="BOTH" diff --git a/Universal/src/main/resources/customskinloader.accesswidener b/Universal/src/main/resources/customskinloader.accesswidener deleted file mode 100644 index 43ec9315..00000000 --- a/Universal/src/main/resources/customskinloader.accesswidener +++ /dev/null @@ -1,4 +0,0 @@ -accessWidener v2 official -extendable class com/mojang/blaze3d/platform/NativeImage # 24w46a+ -extendable class net/minecraft/client/resources/SkinManager$CacheKey # 23w31a+ -accessible method net/minecraft/client/resources/SkinManager$CacheKey (Ljava/util/UUID;Lcom/mojang/authlib/properties/Property;)V # 23w42a+ diff --git a/Universal/src/main/resources/mixins.customskinloader.json b/Universal/src/main/resources/mixins.customskinloader.json deleted file mode 100644 index 19e1e107..00000000 --- a/Universal/src/main/resources/mixins.customskinloader.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "compatibilityLevel": "JAVA_8", - "mixinPriority": 1010, - "package": "customskinloader.mixin", - "client": [ - "MixinGuiPlayerTabOverlay", - "MixinIResource", - "MixinIResourceManager$V1", - "MixinIResourceManager$V2", - "MixinLayerCape$V1", - "MixinLayerCape$V2", - "MixinMinecraft", - "MixinNativeImage", - "MixinRenderPlayer", - "MixinResourceLocation$V1", - "MixinResourceLocation$V2", - "MixinSkinManager$1", - "MixinSkinManager$TextureCache", - "MixinSkinManager$V1", - "MixinSkinManager$V3", - "MixinSkinTextureDownloader$V1", - "MixinSkinTextureDownloader$V2", - "MixinSkinTextureDownloader$V3", - "MixinThreadDownloadImageData$V2" - ], - "refmap": "mixins.customskinloader.refmap.json", - "plugin": "customskinloader.forge.MixinConfigPlugin", - "injectors": { - "defaultRequire": 1 - } -} diff --git a/Universal/src/main/resources/pack.mcmeta b/Universal/src/main/resources/pack.mcmeta deleted file mode 100644 index 1ec83b3b..00000000 --- a/Universal/src/main/resources/pack.mcmeta +++ /dev/null @@ -1,6 +0,0 @@ -{ - "pack": { - "description": "CustomSkinLoader resources", - "pack_format": 18 - } -} diff --git a/Vanilla/Common/build.gradle b/Vanilla/Common/build.gradle deleted file mode 100644 index 2afe3d42..00000000 --- a/Vanilla/Common/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -// This subproject is only to provide dependency for other subprojects. - -dependencies { - implementation project(':Common') - implementation project(':Dummy') - implementation 'org.spongepowered:mixin:0.8.5' -} - -compileJava { - // Disable Mixin AP - // Since 0.8.4, Mixin no longer provide the jar exclude AP, we should disable them manually - options.compilerArgs << "-proc:none" -} diff --git a/Vanilla/Common/build.properties b/Vanilla/Common/build.properties deleted file mode 100644 index 2c5a53ad..00000000 --- a/Vanilla/Common/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -forge_mc_version=1.12.2 -forge_version=14.23.5.2768 -mappings_version=stable_39 -# this subproject is only to provide dependency for other subprojects. -is_real_project=false diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinGuiPlayerTabOverlay.java b/Vanilla/Common/src/main/java/customskinloader/mixin/MixinGuiPlayerTabOverlay.java deleted file mode 100644 index 7a2605f8..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinGuiPlayerTabOverlay.java +++ /dev/null @@ -1,27 +0,0 @@ -package customskinloader.mixin; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiPlayerTabOverlay; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -@Mixin(GuiPlayerTabOverlay.class) -@SuppressWarnings("target") -public abstract class MixinGuiPlayerTabOverlay { - @Redirect( - method = { - "Lnet/minecraft/client/gui/GuiPlayerTabOverlay;renderPlayerlist(ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V", // 20w16a- - "Lnet/minecraft/client/gui/GuiPlayerTabOverlay;renderPlayerlist(Lcom/mojang/blaze3d/matrix/MatrixStack;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V", // 20w17a ~ 23w14a - "Lnet/minecraft/client/gui/GuiPlayerTabOverlay;renderPlayerlist(Lnet/minecraft/client/gui/Gui;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V", // 23w16a ~ 26.1-snapshot-11 - "Lnet/minecraft/client/gui/GuiPlayerTabOverlay;renderPlayerlist(Lnet/minecraft/client/gui/GuiGraphicsExtractor;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreObjective;)V" // 26.1-pre-1+ - }, - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/Minecraft;isIntegratedServerRunning()Z" - ) - ) - private boolean redirect_renderPlayerlist(Minecraft mc) { - return true; - } -} diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinIImageBuffer.java b/Vanilla/Common/src/main/java/customskinloader/mixin/MixinIImageBuffer.java deleted file mode 100644 index 1e700879..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinIImageBuffer.java +++ /dev/null @@ -1,10 +0,0 @@ -package customskinloader.mixin; - -import customskinloader.fake.itf.IFakeIImageBuffer; -import net.minecraft.client.renderer.IImageBuffer; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(IImageBuffer.class) -public interface MixinIImageBuffer extends IFakeIImageBuffer { - -} diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinIResource.java b/Vanilla/Common/src/main/java/customskinloader/mixin/MixinIResource.java deleted file mode 100644 index 0ba8998d..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinIResource.java +++ /dev/null @@ -1,10 +0,0 @@ -package customskinloader.mixin; - -import customskinloader.fake.itf.IFakeIResource; -import net.minecraft.client.resources.IResource; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(IResource.class) -public interface MixinIResource extends IFakeIResource.V1, IFakeIResource.V2 { - -} diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinIResourceManager.java b/Vanilla/Common/src/main/java/customskinloader/mixin/MixinIResourceManager.java deleted file mode 100644 index 5ed706ea..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinIResourceManager.java +++ /dev/null @@ -1,25 +0,0 @@ -package customskinloader.mixin; - -import java.util.Optional; - -import customskinloader.fake.itf.IFakeIResourceManager; -import customskinloader.fake.itf.IFakeResourceLocation; -import net.minecraft.client.resources.IResourceManager; -import net.minecraft.resources.Identifier; -import org.spongepowered.asm.mixin.Mixin; - -public abstract class MixinIResourceManager { - @Mixin(IResourceManager.class) - public interface V1 extends IFakeIResourceManager.V1 { - - } - - // 25w45a+ - @Mixin(IResourceManager.class) - public interface V2 extends IFakeIResourceManager.V2 { - @Override - default Optional getResource(IFakeResourceLocation location) { - return ((IResourceManager) this).getResource((Identifier) location); - } - } -} diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinLayerCape.java b/Vanilla/Common/src/main/java/customskinloader/mixin/MixinLayerCape.java deleted file mode 100644 index 3dad02a8..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinLayerCape.java +++ /dev/null @@ -1,56 +0,0 @@ -package customskinloader.mixin; - -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.entity.layers.LayerCape; -import net.minecraft.client.renderer.rendertype.RenderTypes; -import net.minecraft.resources.Identifier; -import net.minecraft.util.ResourceLocation; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -@SuppressWarnings("target") -public abstract class MixinLayerCape { - // 19w39a ~ 25w42a - @Mixin( - value = LayerCape.class, - priority = 990 - ) - public abstract static class V1 { - @Redirect( - method = { - "Lnet/minecraft/client/renderer/entity/layers/LayerCape;doRenderLayer(Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;ILnet/minecraft/client/entity/AbstractClientPlayer;FFFFFFF)V", // 19w39a ~ 19w44a - "Lnet/minecraft/client/renderer/entity/layers/LayerCape;doRenderLayer(Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;ILnet/minecraft/client/entity/AbstractClientPlayer;FFFFFF)V", // 19w45a ~ 1.21.1 - "Lnet/minecraft/client/renderer/entity/layers/LayerCape;doRenderLayer(Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;ILnet/minecraft/client/renderer/entity/state/PlayerRenderState;FF)V", // 24w33a ~ 1.21.8 - "Lnet/minecraft/client/renderer/entity/layers/LayerCape;doRenderLayer(Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;ILnet/minecraft/client/renderer/entity/state/AvatarRenderState;FF)V" // 25w31a ~ 25w42a - }, - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/renderer/RenderType;getEntitySolid(Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/RenderType;" - ), - require = 0 - ) - private RenderType redirect_doRenderLayer(ResourceLocation locationIn) { - return RenderType.func_228644_e_(locationIn); - } - } - - // 25w43a+ - @Mixin( - value = LayerCape.class, - priority = 990 - ) - public abstract static class V2 { - @Redirect( - method = "Lnet/minecraft/client/renderer/entity/layers/LayerCape;doRenderLayer(Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;ILnet/minecraft/client/renderer/entity/state/AvatarRenderState;FF)V", // 25w43a+ - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;entitySolid(Lnet/minecraft/resources/Identifier;)Lnet/minecraft/client/renderer/rendertype/RenderType;" - ), - require = 0 - ) - private net.minecraft.client.renderer.rendertype.RenderType redirect_doRenderLayer(Identifier identifier) { - return RenderTypes.entityTranslucent(identifier); - } - } -} diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinMinecraft.java b/Vanilla/Common/src/main/java/customskinloader/mixin/MixinMinecraft.java deleted file mode 100644 index a44a7a22..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinMinecraft.java +++ /dev/null @@ -1,10 +0,0 @@ -package customskinloader.mixin; - -import customskinloader.fake.itf.IFakeMinecraft; -import net.minecraft.client.Minecraft; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(Minecraft.class) -public abstract class MixinMinecraft implements IFakeMinecraft { - -} diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinNativeImage.java b/Vanilla/Common/src/main/java/customskinloader/mixin/MixinNativeImage.java deleted file mode 100644 index 9a44683b..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinNativeImage.java +++ /dev/null @@ -1,21 +0,0 @@ -package customskinloader.mixin; - -import customskinloader.fake.itf.IFakeNativeImage; -import customskinloader.fake.texture.FakeNativeImage; -import net.minecraft.client.renderer.texture.NativeImage; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(NativeImage.class) -public abstract class MixinNativeImage implements IFakeNativeImage { - private FakeNativeImage fakeImage; - - @Override - public FakeNativeImage getFakeImage() { - return this.fakeImage; - } - - @Override - public void setFakeImage(FakeNativeImage fakeImage) { - this.fakeImage = fakeImage; - } -} diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinRenderPlayer.java b/Vanilla/Common/src/main/java/customskinloader/mixin/MixinRenderPlayer.java deleted file mode 100644 index 0d3c3785..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinRenderPlayer.java +++ /dev/null @@ -1,30 +0,0 @@ -package customskinloader.mixin; - -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.entity.RenderPlayer; -import net.minecraft.util.ResourceLocation; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -@Mixin( - value = RenderPlayer.class, - priority = 990 -) -@SuppressWarnings("target") -public abstract class MixinRenderPlayer { - @Redirect( - method = { - "Lnet/minecraft/client/renderer/entity/RenderPlayer;renderItem(Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;Lnet/minecraft/client/entity/AbstractClientPlayer;Lnet/minecraft/client/model/ModelRenderer;Lnet/minecraft/client/model/ModelRenderer;)V", // 19w39a ~ 19w44a - "Lnet/minecraft/client/renderer/entity/RenderPlayer;renderItem(Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;ILnet/minecraft/client/entity/AbstractClientPlayer;Lnet/minecraft/client/model/ModelRenderer;Lnet/minecraft/client/model/ModelRenderer;)V" // 19w45a ~ 1.21.1 - }, - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/renderer/RenderType;getEntitySolid(Lnet/minecraft/util/ResourceLocation;)Lnet/minecraft/client/renderer/RenderType;" - ), - require = 0 - ) - private RenderType redirect_renderItem(ResourceLocation locationIn) { - return RenderType.func_228644_e_(locationIn); - } -} diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinResourceLocation.java b/Vanilla/Common/src/main/java/customskinloader/mixin/MixinResourceLocation.java deleted file mode 100644 index 30f6f57e..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinResourceLocation.java +++ /dev/null @@ -1,39 +0,0 @@ -package customskinloader.mixin; - -import customskinloader.fake.FakeMinecraftProfileTexture; -import customskinloader.fake.itf.IFakeResourceLocation; -import net.minecraft.resources.Identifier; -import net.minecraft.util.ResourceLocation; -import org.spongepowered.asm.mixin.Mixin; - -public abstract class MixinResourceLocation { - @Mixin(ResourceLocation.class) - public abstract static class V1 implements IFakeResourceLocation { - private FakeMinecraftProfileTexture texture; - - @Override - public FakeMinecraftProfileTexture getTexture() { - return this.texture; - } - - @Override - public void setTexture(FakeMinecraftProfileTexture texture) { - this.texture = texture; - } - } - - @Mixin(Identifier.class) - public abstract static class V2 implements IFakeResourceLocation { - private FakeMinecraftProfileTexture texture; - - @Override - public FakeMinecraftProfileTexture getTexture() { - return this.texture; - } - - @Override - public void setTexture(FakeMinecraftProfileTexture texture) { - this.texture = texture; - } - } -} diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinSkinManager$1.java b/Vanilla/Common/src/main/java/customskinloader/mixin/MixinSkinManager$1.java deleted file mode 100644 index 5e4d557b..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinSkinManager$1.java +++ /dev/null @@ -1,85 +0,0 @@ -package customskinloader.mixin; - -import java.util.Map; -import java.util.concurrent.Executor; - -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.minecraft.MinecraftProfileTexture; -import com.mojang.authlib.minecraft.MinecraftSessionService; -import com.mojang.authlib.properties.Property; -import customskinloader.fake.FakeSkinManager; -import net.minecraft.client.resources.SkinManager$1; -import net.minecraft.client.resources.SkinManager$CacheKey; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Coerce; -import org.spongepowered.asm.mixin.injection.Group; -import org.spongepowered.asm.mixin.injection.ModifyArg; -import org.spongepowered.asm.mixin.injection.Redirect; - -@Mixin(SkinManager$1.class) -public abstract class MixinSkinManager$1 { - @ModifyArg( - method = "Lnet/minecraft/client/resources/SkinManager$1;load(Lnet/minecraft/client/resources/SkinManager$CacheKey;)Ljava/util/concurrent/CompletableFuture;", - at = @At( - value = "INVOKE", - target = "Ljava/util/concurrent/CompletableFuture;supplyAsync(Ljava/util/function/Supplier;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;", - remap = false - ) - ) - private Executor modifyArg_load_0(Executor executor) { - return FakeSkinManager.loadProfileTextures(executor); - } - - @ModifyArg( - method = "Lnet/minecraft/client/resources/SkinManager$1;load(Lnet/minecraft/client/resources/SkinManager$CacheKey;)Ljava/util/concurrent/CompletableFuture;", - at = @At( - value = "INVOKE", - target = "Ljava/util/concurrent/CompletableFuture;thenComposeAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;", - remap = false - ) - ) - private Executor modifyArg_load_1(Executor executor) { - return FakeSkinManager.loadProfileTextures(executor); - } - - // 23w31a ~ 23w41a - @Group( - name = "lambda$load$0", - min = 1, - max = 2 - ) - @Redirect( - method = "Lnet/minecraft/client/resources/SkinManager$1;lambda$load$0(Lcom/mojang/authlib/minecraft/MinecraftSessionService;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/client/resources/SkinManager$TextureInfo;", - at = @At( - value = "INVOKE", - target = "Lcom/mojang/authlib/minecraft/MinecraftSessionService;getTextures(Lcom/mojang/authlib/GameProfile;Z)Ljava/util/Map;", - remap = false - ) - ) - private static Map redirect_lambda$load$0(MinecraftSessionService sessionService, GameProfile profile, boolean requireSecure) { - return FakeSkinManager.loadSkinFromCache(sessionService, profile, requireSecure); - } - - // 23w42a+ - @Coerce - @Group( - name = "lambda$load$0", - min = 1, - max = 2 - ) - @Redirect( - method = { - "Lnet/minecraft/client/resources/SkinManager$1;lambda$load$0(Lnet/minecraft/client/resources/SkinManager$CacheKey;Lcom/mojang/authlib/minecraft/MinecraftSessionService;)Lcom/mojang/authlib/minecraft/MinecraftProfileTextures;", // 23w42a ~ 25w33a - "Lnet/minecraft/client/resources/SkinManager$1;lambda$load$0(Lnet/minecraft/client/resources/SkinManager$CacheKey;Lnet/minecraft/server/Services;)Lcom/mojang/authlib/minecraft/MinecraftProfileTextures;" // 25w34a+ - }, - at = @At( - value = "INVOKE", - target = "Lcom/mojang/authlib/minecraft/MinecraftSessionService;unpackTextures(Lcom/mojang/authlib/properties/Property;)Lcom/mojang/authlib/minecraft/MinecraftProfileTextures;", - remap = false - ) - ) - private static Object redirect_lambda$load$0(MinecraftSessionService sessionService, Property property, SkinManager$CacheKey cacheKey, @Coerce Object service) { - return FakeSkinManager.loadSkinFromCache(sessionService, property, cacheKey); - } -} diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinSkinManager$TextureCache.java b/Vanilla/Common/src/main/java/customskinloader/mixin/MixinSkinManager$TextureCache.java deleted file mode 100644 index 9c99e3c8..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinSkinManager$TextureCache.java +++ /dev/null @@ -1,81 +0,0 @@ -package customskinloader.mixin; - -import com.google.common.collect.ImmutableList; -import com.mojang.authlib.minecraft.MinecraftProfileTexture; -import customskinloader.fake.FakeSkinManager; -import net.minecraft.client.resources.SkinManager$TextureCache; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Group; -import org.spongepowered.asm.mixin.injection.ModifyArgs; -import org.spongepowered.asm.mixin.injection.invoke.arg.Args; - -@Mixin(SkinManager$TextureCache.class) -public abstract class MixinSkinManager$TextureCache { - @Shadow - private MinecraftProfileTexture.Type type; - - // 23w31a ~ 24w45a - @Group( - name = "modifyArgs_registerTexture", - min = 1 - ) - @ModifyArgs( - method = "Lnet/minecraft/client/resources/SkinManager$TextureCache;registerTexture(Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;)Ljava/util/concurrent/CompletableFuture;", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/renderer/ThreadDownloadImageData;(Ljava/io/File;Ljava/lang/String;Lnet/minecraft/util/ResourceLocation;ZLjava/lang/Runnable;)V" - ) - ) - private void modifyArgs_registerTexture_0(Args args, MinecraftProfileTexture profileTexture) { - Object[] argsArr = new Object[args.size()]; - for (int i = 0; i < argsArr.length; i++) { - argsArr[i] = args.get(i); - } - argsArr = FakeSkinManager.createThreadDownloadImageData(ImmutableList.copyOf(argsArr), profileTexture, this.type); - args.setAll(argsArr); - } - - // 24w46a ~ 25w45a - @Group( - name = "modifyArgs_registerTexture", - min = 1 - ) - @ModifyArgs( - method = "Lnet/minecraft/client/resources/SkinManager$TextureCache;registerTexture(Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;)Ljava/util/concurrent/CompletableFuture;", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/renderer/texture/SkinTextureDownloader;downloadAndRegisterSkin(Lnet/minecraft/util/ResourceLocation;Ljava/nio/file/Path;Ljava/lang/String;Z)Ljava/util/concurrent/CompletableFuture;" - ) - ) - private void modifyArgs_registerTexture_1(Args args, MinecraftProfileTexture profileTexture) { - Object[] argsArr = new Object[args.size()]; - for (int i = 0; i < argsArr.length; i++) { - argsArr[i] = args.get(i); - } - argsArr = FakeSkinManager.createThreadDownloadImageData(ImmutableList.copyOf(argsArr), profileTexture, this.type); - args.setAll(argsArr); - } - - // 25w46a+ - @Group( - name = "modifyArgs_registerTexture", - min = 1 - ) - @ModifyArgs( - method = "Lnet/minecraft/client/resources/SkinManager$TextureCache;registerTexture(Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;)Ljava/util/concurrent/CompletableFuture;", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/renderer/texture/SkinTextureDownloader;downloadAndRegisterSkin(Lnet/minecraft/resources/Identifier;Ljava/nio/file/Path;Ljava/lang/String;Z)Ljava/util/concurrent/CompletableFuture;" - ) - ) - private void modifyArgs_registerTexture_2(Args args, MinecraftProfileTexture profileTexture) { - Object[] argsArr = new Object[args.size()]; - for (int i = 0; i < argsArr.length; i++) { - argsArr[i] = args.get(i); - } - argsArr = FakeSkinManager.createThreadDownloadImageData(ImmutableList.copyOf(argsArr), profileTexture, this.type); - args.setAll(argsArr); - } -} diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinSkinManager.java b/Vanilla/Common/src/main/java/customskinloader/mixin/MixinSkinManager.java deleted file mode 100644 index 9e9e8a01..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinSkinManager.java +++ /dev/null @@ -1,258 +0,0 @@ -package customskinloader.mixin; - -import java.io.File; -import java.nio.file.Path; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; - -import com.google.common.collect.ImmutableList; -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.minecraft.MinecraftProfileTexture; -import com.mojang.authlib.minecraft.MinecraftSessionService; -import com.mojang.authlib.properties.Property; -import customskinloader.fake.FakeSkinManager; -import net.minecraft.client.resources.SkinManager; -import net.minecraft.client.resources.SkinManager$CacheKey; -import net.minecraft.client.resources.SkinManager$SkinAvailableCallback; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Coerce; -import org.spongepowered.asm.mixin.injection.Group; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyArgs; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.invoke.arg.Args; - -@SuppressWarnings("target") -public abstract class MixinSkinManager { - // 18w43b+ - @Mixin(SkinManager.class) - public abstract static class V1 { - // 18w43b ~ 1.20.1 - @Group( - name = "inject_init", - min = 1 - ) - @Inject( - method = "Lnet/minecraft/client/resources/SkinManager;(Lnet/minecraft/client/renderer/texture/TextureManager;Ljava/io/File;Lcom/mojang/authlib/minecraft/MinecraftSessionService;)V", - at = @At("RETURN") - ) - private void inject_init(@Coerce Object textureManager, File skinCacheDirectory, @Coerce Object service, CallbackInfo callbackInfo) { - FakeSkinManager.setSkinCacheDir(skinCacheDirectory); - } - - // 23w31a ~ 24w45a - @Group( - name = "inject_init", - min = 1 - ) - @Inject( - method = "Lnet/minecraft/client/resources/SkinManager;(Lnet/minecraft/client/renderer/texture/TextureManager;Ljava/nio/file/Path;Lcom/mojang/authlib/minecraft/MinecraftSessionService;Ljava/util/concurrent/Executor;)V", - at = @At("RETURN") - ) - private void inject_init(@Coerce Object textureManager, Path path, @Coerce Object service, @Coerce Object executor, CallbackInfo callbackInfo) { - FakeSkinManager.setSkinCacheDir(path); - } - - // 24w46a ~ 25w34b - @Group( - name = "inject_init", - min = 1 - ) - @Inject( - method = { - "Lnet/minecraft/client/resources/SkinManager;(Ljava/nio/file/Path;Lcom/mojang/authlib/minecraft/MinecraftSessionService;Ljava/util/concurrent/Executor;)V", // 24w46a ~ 25w33a - "Lnet/minecraft/client/resources/SkinManager;(Ljava/nio/file/Path;Lnet/minecraft/server/Services;Ljava/util/concurrent/Executor;)V" // 25w34a ~ 25w34b - }, - at = @At("RETURN") - ) - private void inject_init(Path path, @Coerce Object service, @Coerce Object executor, CallbackInfo callbackInfo) { - FakeSkinManager.setSkinCacheDir(path); - } - - // 25w35a+ - @Group( - name = "inject_init", - min = 1 - ) - @Inject( - method = "Lnet/minecraft/client/resources/SkinManager;(Ljava/nio/file/Path;Lnet/minecraft/server/Services;Lnet/minecraft/client/renderer/texture/SkinTextureDownloader;Ljava/util/concurrent/Executor;)V", - at = @At("RETURN") - ) - private void inject_init(Path path, @Coerce Object service, @Coerce Object downloader, @Coerce Object executor, CallbackInfo callbackInfo) { - FakeSkinManager.setSkinCacheDir(path); - } - } - - // 18w43b ~ 1.20.1 - @Mixin(SkinManager.class) - public abstract static class V2 { - // 18w43b ~ 19w37a - @Group( - name = "modifyArgs_loadSkin", - min = 1 - ) - @ModifyArgs( - method = "Lnet/minecraft/client/resources/SkinManager;loadSkin(Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)Lnet/minecraft/util/ResourceLocation;", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/renderer/ThreadDownloadImageData;(Ljava/io/File;Ljava/lang/String;Lnet/minecraft/util/ResourceLocation;Lnet/minecraft/client/renderer/IImageBuffer;)V" - ) - ) - private void modifyArgs_loadSkin_0(Args args, MinecraftProfileTexture profileTexture, MinecraftProfileTexture.Type textureType, SkinManager$SkinAvailableCallback skinAvailableCallback) { - Object[] argsArr = new Object[args.size()]; - for (int i = 0; i < argsArr.length; i++) { - argsArr[i] = args.get(i); - } - argsArr = FakeSkinManager.createThreadDownloadImageData(ImmutableList.copyOf(argsArr), profileTexture, textureType); - args.setAll(argsArr); - } - - // 19w38a ~ 1.20.1 - @Group( - name = "modifyArgs_loadSkin", - min = 1 - ) - @ModifyArgs( - method = "Lnet/minecraft/client/resources/SkinManager;loadSkin(Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)Lnet/minecraft/util/ResourceLocation;", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/renderer/ThreadDownloadImageData;(Ljava/io/File;Ljava/lang/String;Lnet/minecraft/util/ResourceLocation;ZLjava/lang/Runnable;)V" - ) - ) - private void modifyArgs_loadSkin_1(Args args, MinecraftProfileTexture profileTexture, MinecraftProfileTexture.Type textureType, SkinManager$SkinAvailableCallback skinAvailableCallback) { - this.modifyArgs_loadSkin_0(args, profileTexture, textureType, skinAvailableCallback); - } - - // 18w43b ~ 19w37a - @Group( - name = "redirect_loadProfileTextures", - min = 1 - ) - @Redirect( - method = "Lnet/minecraft/client/resources/SkinManager;loadProfileTextures(Lcom/mojang/authlib/GameProfile;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;Z)V", - at = @At( - value = "INVOKE", - target = "Ljava/util/concurrent/ExecutorService;submit(Ljava/lang/Runnable;)Ljava/util/concurrent/Future;", - remap = false - ) - ) - private Future redirect_loadProfileTextures_0(ExecutorService executor, Runnable task) { - FakeSkinManager.loadProfileTextures(task); - return null; - } - - // 19w38a ~ 1.18-exp7 - @Group( - name = "redirect_loadProfileTextures", - min = 1 - ) - @Redirect( - method = "Lnet/minecraft/client/resources/SkinManager;loadProfileTextures(Lcom/mojang/authlib/GameProfile;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;Z)V", - at = @At( - value = "INVOKE", - target = "Ljava/util/concurrent/Executor;execute(Ljava/lang/Runnable;)V", - remap = false - ) - ) - private void redirect_loadProfileTextures_1(Executor executor, Runnable task) { - FakeSkinManager.loadProfileTextures(task); - } - - // 21w37a ~ 1.20.1 - @Group( - name = "redirect_loadProfileTextures", - min = 1 - ) - @Redirect( - method = "Lnet/minecraft/client/resources/SkinManager;loadProfileTextures(Lcom/mojang/authlib/GameProfile;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;Z)V", - at = @At( - value = "INVOKE", - target = "Ljava/util/concurrent/ExecutorService;execute(Ljava/lang/Runnable;)V", - remap = false - ) - ) - private void redirect_loadProfileTextures_2(ExecutorService executor, Runnable task) { - FakeSkinManager.loadProfileTextures(task); - } - - @Inject( - method = "Lnet/minecraft/client/resources/SkinManager;loadSkinFromCache(Lcom/mojang/authlib/GameProfile;)Ljava/util/Map;", - at = @At("HEAD"), - cancellable = true - ) - private void inject_loadSkinFromCache(GameProfile profile, CallbackInfoReturnable> callbackInfoReturnable) { - callbackInfoReturnable.setReturnValue(FakeSkinManager.loadSkinFromCache(profile)); - } - - @Redirect( - method = "Lnet/minecraft/client/resources/SkinManager;func_210275_a(Lcom/mojang/authlib/GameProfile;ZLnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)V", - at = @At( - value = "INVOKE", - target = "Lcom/mojang/authlib/minecraft/MinecraftSessionService;getTextures(Lcom/mojang/authlib/GameProfile;Z)Ljava/util/Map;", - remap = false - ) - ) - private Map redirect_func_210275_a(MinecraftSessionService sessionService, GameProfile profile, boolean requireSecure) { - return FakeSkinManager.getUserProfile(sessionService, profile, requireSecure); - } - - // 18w43b ~ 19w37a - @Group( - name = "loadElytraTexture", - min = 1 - ) - @Inject( - method = "Lnet/minecraft/client/resources/SkinManager;func_210276_a(Ljava/util/Map;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)V", - at = @At( - value = "INVOKE", - target = "Ljava/util/Map;containsKey(Ljava/lang/Object;)Z", - ordinal = 0, - remap = false - ) - ) - private void inject_func_210276_a(Map map, SkinManager$SkinAvailableCallback skinAvailableCallback, CallbackInfo callbackInfo) { - FakeSkinManager.loadElytraTexture((SkinManager) (Object) this, map, skinAvailableCallback); - } - - // 19w38a ~ 1.20.1 - @Group( - name = "loadElytraTexture", - min = 1 - ) - @Redirect( - method = "Lnet/minecraft/client/resources/SkinManager;func_210276_a(Ljava/util/Map;Lnet/minecraft/client/resources/SkinManager$SkinAvailableCallback;)V", - at = @At( - value = "INVOKE", - target = "Lcom/google/common/collect/ImmutableList;of(Ljava/lang/Object;Ljava/lang/Object;)Lcom/google/common/collect/ImmutableList;", - remap = false - ) - ) - private ImmutableList redirect_func_229297_b_(Object e1, Object e2) { - return ImmutableList.copyOf(MinecraftProfileTexture.Type.values()); - } - } - - // 23w42a+ - @Mixin(SkinManager.class) - public abstract static class V3 { - @Redirect( - method = { - "Lnet/minecraft/client/resources/SkinManager;getOrLoad(Lcom/mojang/authlib/GameProfile;)Ljava/util/concurrent/CompletableFuture;", // 23w42a ~ 25w33a - "Lnet/minecraft/client/resources/SkinManager;get(Lcom/mojang/authlib/GameProfile;)Ljava/util/concurrent/CompletableFuture;" // 25w34a+ - }, - at = @At( - value = "NEW", - target = "(Ljava/util/UUID;Lcom/mojang/authlib/properties/Property;)Lnet/minecraft/client/resources/SkinManager$CacheKey;" - ) - ) - private SkinManager$CacheKey redirect_getOrLoad(UUID uuid, Property property, GameProfile profile) { - return FakeSkinManager.FakeCacheKey.createFakeCacheKey(uuid, property, profile); - } - } -} diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinSkinTextureDownloader.java b/Vanilla/Common/src/main/java/customskinloader/mixin/MixinSkinTextureDownloader.java deleted file mode 100644 index a93a4633..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinSkinTextureDownloader.java +++ /dev/null @@ -1,68 +0,0 @@ -package customskinloader.mixin; - -import java.nio.file.Path; - -import customskinloader.fake.FakeSkinBuffer; -import customskinloader.fake.texture.FakeThreadDownloadImageData; -import net.minecraft.client.renderer.texture.NativeImage; -import net.minecraft.client.renderer.texture.SkinTextureDownloader; -import net.minecraft.resources.Identifier; -import net.minecraft.util.ResourceLocation; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyArgs; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.invoke.arg.Args; - -@SuppressWarnings("target") -public abstract class MixinSkinTextureDownloader { - // 24w46a ~ 25w44a - @Mixin(SkinTextureDownloader.class) - public abstract static class V1 { - @ModifyArgs( - method = "Lnet/minecraft/client/renderer/texture/SkinTextureDownloader;downloadAndRegisterSkin(Lnet/minecraft/util/ResourceLocation;Ljava/nio/file/Path;Ljava/lang/String;Z)Ljava/util/concurrent/CompletableFuture;", - at = @At( - value = "INVOKE", - target = "Ljava/util/concurrent/CompletableFuture;thenCompose(Ljava/util/function/Function;)Ljava/util/concurrent/CompletableFuture;", - remap = false - ) - ) - private static void modifyArg_downloadAndRegisterSkin(Args args, ResourceLocation location, Path path, String url, boolean bl) { - args.set(0, FakeThreadDownloadImageData.createTextureV1(args.get(0), location, bl)); - } - } - - // 24w46a+ - @Mixin(SkinTextureDownloader.class) - public abstract static class V2 { - @Redirect( - method = { - "Lnet/minecraft/client/renderer/texture/SkinTextureDownloader;lambda$downloadAndRegisterSkin$0(Ljava/nio/file/Path;Ljava/lang/String;Z)Lnet/minecraft/client/renderer/texture/NativeImage;", // 24w46a ~ 25w37a - "Lnet/minecraft/client/renderer/texture/SkinTextureDownloader;lambda$downloadAndRegisterSkin$0(Ljava/nio/file/Path;Lnet/minecraft/core/ClientAsset$DownloadedTexture;Z)Lnet/minecraft/client/renderer/texture/NativeImage;" // 1.21.9-pre1+ - }, - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/renderer/texture/SkinTextureDownloader;processLegacySkin(Lnet/minecraft/client/renderer/texture/NativeImage;Ljava/lang/String;)Lnet/minecraft/client/renderer/texture/NativeImage;" - ) - ) - private static NativeImage redirect_lambda$downloadAndRegisterSkin$0(NativeImage image, String url) { - return FakeSkinBuffer.processLegacySkin(image, url); - } - } - - // 25w45a+ - @Mixin(SkinTextureDownloader.class) - public abstract static class V3 { - @ModifyArgs( - method = "Lnet/minecraft/client/renderer/texture/SkinTextureDownloader;downloadAndRegisterSkin(Lnet/minecraft/resources/Identifier;Ljava/nio/file/Path;Ljava/lang/String;Z)Ljava/util/concurrent/CompletableFuture;", - at = @At( - value = "INVOKE", - target = "Ljava/util/concurrent/CompletableFuture;thenCompose(Ljava/util/function/Function;)Ljava/util/concurrent/CompletableFuture;", - remap = false - ) - ) - private static void modifyArg_downloadAndRegisterSkin(Args args, Identifier identifier, Path path, String url, boolean bl) { - args.set(0, FakeThreadDownloadImageData.createTextureV2(args.get(0), identifier, bl)); - } - } -} diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinThreadDownloadImageData.java b/Vanilla/Common/src/main/java/customskinloader/mixin/MixinThreadDownloadImageData.java deleted file mode 100644 index d2246c62..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/MixinThreadDownloadImageData.java +++ /dev/null @@ -1,57 +0,0 @@ -package customskinloader.mixin; - -import customskinloader.fake.FakeSkinBuffer; -import net.minecraft.client.renderer.ThreadDownloadImageData; -import net.minecraft.client.renderer.texture.NativeImage; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -@SuppressWarnings("target") -public abstract class MixinThreadDownloadImageData { - @Mixin(ThreadDownloadImageData.class) // 19w38a ~ 1.17-rc1 - public abstract static class V1 { - @Final - @Shadow - private Runnable processTask; - - @Shadow - private static NativeImage processLegacySkin(NativeImage nativeImageIn) { - return null; - } - - @Redirect( - method = "Lnet/minecraft/client/renderer/ThreadDownloadImageData;loadTexture(Ljava/io/InputStream;)Lnet/minecraft/client/renderer/texture/NativeImage;", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/renderer/ThreadDownloadImageData;processLegacySkin(Lnet/minecraft/client/renderer/texture/NativeImage;)Lnet/minecraft/client/renderer/texture/NativeImage;" - ) - ) - private NativeImage redirect_loadTexture(NativeImage image) { - return FakeSkinBuffer.processLegacySkin(image, this.processTask, MixinThreadDownloadImageData.V1::processLegacySkin); - } - } - - @Mixin(ThreadDownloadImageData.class) // 1.17-rc2 ~ 24w45a - public abstract static class V2 { - @Final - @Shadow - private Runnable processTask; - - @Shadow - abstract NativeImage processLegacySkin(NativeImage nativeImageIn); - - @Redirect( - method = "Lnet/minecraft/client/renderer/ThreadDownloadImageData;loadTexture(Ljava/io/InputStream;)Lnet/minecraft/client/renderer/texture/NativeImage;", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/client/renderer/ThreadDownloadImageData;processLegacySkin(Lnet/minecraft/client/renderer/texture/NativeImage;)Lnet/minecraft/client/renderer/texture/NativeImage;" - ) - ) - private NativeImage redirect_loadTexture(ThreadDownloadImageData _this, NativeImage image) { - return FakeSkinBuffer.processLegacySkin(image, this.processTask, this::processLegacySkin); - } - } -} diff --git a/Vanilla/Common/src/main/java/customskinloader/mixin/core/MixinConfigPlugin.java b/Vanilla/Common/src/main/java/customskinloader/mixin/core/MixinConfigPlugin.java deleted file mode 100644 index 4b7a5eb9..00000000 --- a/Vanilla/Common/src/main/java/customskinloader/mixin/core/MixinConfigPlugin.java +++ /dev/null @@ -1,116 +0,0 @@ -package customskinloader.mixin.core; - -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; -import java.nio.file.Paths; -import java.util.List; -import java.util.Set; - -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import customskinloader.log.LogManager; -import customskinloader.log.Logger; -import org.objectweb.asm.tree.ClassNode; -import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; -import org.spongepowered.asm.mixin.extensibility.IMixinInfo; - -public class MixinConfigPlugin implements IMixinConfigPlugin { - public static Logger logger = LogManager.getLogger("Core"); - private long world_version; - private long protocol_version; - - @Override - public void onLoad(String mixinPackage) { - LogManager.setLogFile(Paths.get("./CustomSkinLoader/CustomSkinLoader.log")); - URL versionJson = ClassLoader.getSystemClassLoader().getResource("version.json"); - if (versionJson != null) { - logger.info("\"version.json\": " + versionJson); - try ( - InputStream is = versionJson.openStream(); - InputStreamReader isr = new InputStreamReader(is) - ) { - JsonObject object = new JsonParser().parse(isr).getAsJsonObject(); - String name = object.get("name").getAsString(); - this.world_version = object.get("world_version").getAsLong(); - this.protocol_version = object.get("protocol_version").getAsLong(); - logger.info("MinecraftVersion: {name='" + name + "', world_version='" + this.world_version + "', protocol_version='" + this.protocol_version + "'}"); - } catch (Throwable t) { - logger.warning("An exception occurred when reading \"version.json\"!"); - logger.warning(t); - } - } else { - logger.warning("Can't read \"version.json\"! Ignore this message if the version you start is earlier than 18w47b."); - } - } - - @Override - public String getRefMapperConfig() { - return null; - } - - @Override - public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { - boolean result = true; - if (mixinClassName.endsWith(".MixinIImageBuffer")) { - result = (this.world_version >= 0 && this.world_version <= 2204) && (this.protocol_version >= 0 && this.protocol_version <= 553); // 18w43b ~ 19w37a - } else if (mixinClassName.endsWith(".MixinLayerCape$V1")) { - result = (this.world_version >= 2210 && this.world_version <= 4654) && ((this.protocol_version >= 558 && this.protocol_version <= 773) || (this.protocol_version >= 801 && this.protocol_version <= 803) || (this.protocol_version >= 0x40000001 && this.protocol_version <= 0x40000112)); // 19w41a ~ 25w42a - } else if (mixinClassName.endsWith(".MixinLayerCape$V2")) { - result = this.world_version >= 4655 && ((this.protocol_version >= 774 && this.protocol_version < 801) || (this.protocol_version > 803 && this.protocol_version < 0x40000001) || this.protocol_version >= 0x40000113); // 25w43a+ - } else if (mixinClassName.endsWith(".MixinRenderPlayer")) { - result = (this.world_version >= 2210 && this.world_version <= 3955) && ((this.protocol_version >= 558 && this.protocol_version <= 767) || (this.protocol_version >= 801 && this.protocol_version <= 803) || (this.protocol_version >= 0x40000001 && this.protocol_version <= 0x400000CC)); // 19w41a ~ 1.21.1 - } else if (mixinClassName.endsWith(".MixinSkinManager$V1")) { - result = this.world_version >= 0 && this.protocol_version >= 0; // 18w43b+ - } else if (mixinClassName.endsWith(".MixinSkinManager$V2")) { - result = (this.world_version >= 0 && this.world_version <= 3465) && ((this.protocol_version >= 0 && this.protocol_version <= 763) || (this.protocol_version >= 801 && this.protocol_version <= 803) || (this.protocol_version >= 0x40000001 && this.protocol_version <= 0x4000008E)); // 18w43b ~ 1.20.1 - } else if (mixinClassName.endsWith(".MixinSkinManager$V3")) { - result = this.world_version >= 3684 && ((this.protocol_version >= 765 && this.protocol_version < 801) || (this.protocol_version > 803 && this.protocol_version < 0x40000001) || this.protocol_version >= 0x4000009D); // 23w42a+ - } else if (mixinClassName.endsWith(".MixinSkinManager$1") || mixinClassName.endsWith(".MixinSkinManager$TextureCache")) { - result = this.world_version >= 3567 && ((this.protocol_version >= 764 && this.protocol_version < 801) || (this.protocol_version > 803 && this.protocol_version < 0x40000001) || this.protocol_version >= 0x40000090); // 23w31a+ - } else if (mixinClassName.endsWith(".MixinSkinTextureDownloader$V1") || mixinClassName.endsWith(".MixinResourceLocation$V1")) { - result = (this.world_version >= 4178 && this.world_version <= 4659) && ((this.protocol_version >= 769 && this.protocol_version <= 773) || (this.protocol_version >= 0x400000DE && this.protocol_version <= 0x40000114)); // 24w46a ~ 25w44a - } else if (mixinClassName.endsWith(".MixinSkinTextureDownloader$V2")) { - result = this.world_version >= 4178 && ((this.protocol_version >= 769 && this.protocol_version < 801) || (this.protocol_version > 803 && this.protocol_version < 0x40000001) || this.protocol_version >= 0x400000DE); // 24w46a+ - } else if (mixinClassName.endsWith(".MixinSkinTextureDownloader$V3") || mixinClassName.endsWith(".MixinIResourceManager$V2") || mixinClassName.endsWith(".MixinResourceLocation$V2")) { - result = this.world_version >= 4660 && ((this.protocol_version >= 774 && this.protocol_version < 801) || (this.protocol_version > 803 && this.protocol_version < 0x40000001) || this.protocol_version >= 0x40000115); // 25w45a+ - } else if (mixinClassName.endsWith(".MixinThreadDownloadImageData$V1")) { - result = (this.world_version >= 2205 && this.world_version <= 2722) && ((this.protocol_version >= 554 && this.protocol_version <= 754) || (this.protocol_version >= 801 && this.protocol_version <= 803) || (this.protocol_version >= 0x40000001 && this.protocol_version <= 0x40000022)); // 19w38a ~ 1.17-rc1 - } else if (mixinClassName.endsWith(".MixinThreadDownloadImageData$V2")) { - result = (this.world_version >= 2723 && this.world_version <= 4177) && ((this.protocol_version >= 755 && this.protocol_version <= 768) || (this.protocol_version >= 0x40000023 && this.protocol_version <= 0x400000DD)); // 1.17-rc2 ~ 24w45a - } - logger.info("target: " + targetClassName + ", mixin: " + mixinClassName + ", result: " + result); - return result; - } - - @Override - public void acceptTargets(Set myTargets, Set otherTargets) { - - } - - @Override - public List getMixins() { - return null; - } - - @Override - public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - - } - - @Override - public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - - } - - // To be compatible with 0.7.11 - public void preApply(String targetClassName, org.spongepowered.asm.lib.tree.ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - - } - - // To be compatible with 0.7.11 - public void postApply(String targetClassName, org.spongepowered.asm.lib.tree.ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - - } -} - diff --git a/build.gradle b/build.gradle index 413df4c3..7721c402 100644 --- a/build.gradle +++ b/build.gradle @@ -1,142 +1,119 @@ -apply plugin: 'base' -apply plugin: 'customskinloader' - -ext.configFile = file "build.properties" -configFile.withReader { - def prop = new Properties() - prop.load(it) - project.ext.config = new ConfigSlurper().parse prop -} +import org.apache.tools.ant.filters.ReplaceTokens +import org.gradle.api.attributes.java.TargetJvmVersion + +def buildPropertiesFile = rootProject.file('build.properties') -wrapper { - distributionType = Wrapper.DistributionType.ALL - gradleVersion = "4.9" +if (!buildPropertiesFile) { + throw new GradleException('Missing build.properties') } -import customskinloader.gradle.util.RemapUtil -import customskinloader.gradle.util.VersionUtil +def buildProperties = new Properties() +buildPropertiesFile.withInputStream { buildProperties.load(it) } -subprojects { - apply plugin: 'net.minecraftforge.gradle.forge' +def requireBuildProperty = { String propertyName -> + def propertyValue = buildProperties.getProperty(propertyName)?.trim() + if (!propertyValue) { + throw new GradleException("Missing ${propertyName} in ${buildPropertiesFile}") + } + propertyValue +} - ext.configFile = file "build.properties" - configFile.withReader { - def prop = new Properties() - prop.load(it) - project.ext.config = new ConfigSlurper().parse prop +def isTruthy = { String value -> + if (value == null) { + return true } - version = VersionUtil.getCSLVersion(rootProject) - group = "customskinloader" - ext.shortVersion = VersionUtil.getShortVersion(rootProject) + value.trim().toLowerCase(Locale.ROOT) == 'true' +} - sourceCompatibility = targetCompatibility = 1.8 - compileJava { - sourceCompatibility = targetCompatibility = 1.8 - } +def releaseName = requireBuildProperty('name') +def releaseGroup = requireBuildProperty('group') +def releaseVersion = requireBuildProperty('version') +def releaseBuildNumber = System.getenv('GITHUB_RUN_NUMBER')?.trim() ?: '0' +def releaseIsSnapshot = isTruthy(System.getenv('IS_SNAPSHOT')) +def releaseFullVersion = (releaseIsSnapshot ? "${releaseVersion}-SNAPSHOT-${releaseBuildNumber}" : releaseVersion).toString() + +group = releaseGroup +version = releaseFullVersion + +ext { + modName = releaseName + modVersion = releaseVersion + modBuildNumber = releaseBuildNumber + modFullVersion = releaseFullVersion + releaseArchiveBaseName = "${releaseName}_Universal".toString() +} - minecraft { - version = config.forge_mc_version + "-" + config.forge_version - runDir = "run" - mappings = config.mappings_version +def macroValues = { + [ + modVersion : rootProject.ext.modVersion.toString(), + modFullVersion : rootProject.ext.modFullVersion.toString(), + modBuildNumber : rootProject.ext.modBuildNumber.toString() + ] +} - replace '@MOD_VERSION@', rootProject.ext.config.version - replace '@MOD_FULL_VERSION@', project.version - replace '@MOD_BUILD_NUMBER@', VersionUtil.getBuildNum() - replaceIn 'CustomSkinLoader.java' +def javaMacroTokens = { + def values = macroValues() + [ + MOD_VERSION : values.modVersion, + MOD_FULL_VERSION : values.modFullVersion, + MOD_BUILD_NUMBER : values.modBuildNumber + ] +} - makeObfSourceJar = false - } +def resourceMacroTokens = { + macroValues() +} - if (project.name != "Dummy") { - configurations { - configureEach { - exclude module: "forgeSrc" - } - } +subprojects { + apply plugin: 'java' + apply plugin: 'customskinloader.manifest-libraries' + + manifestLibraries { + // Share Bootstrap's dependency set with all packaged child modules. + add 'https://raw.githubusercontent.com/PrismLauncher/meta-launcher/refs/heads/master/net.minecraft/26.1.2.json' } - jar { - manifest { - attributes([ - "Specification-Title" : "CustomSkinLoader", - "Specification-Vendor" : "xfl03", - "Specification-Version" : "1", - "Implementation-Title" : "CustomSkinLoader", - "Implementation-Version" : "${version}", - "Implementation-Vendor" : "xfl03", - "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") - ]) - } + repositories { + mavenCentral() } - // For real subprojects - if (!config.is_real_project || Boolean.parseBoolean(config.is_real_project.toString())) { - archivesBaseName = "CustomSkinLoader_${-> VersionUtil.getEdition(project)}" - - processResources { - filesNotMatching(["**/*.js", "**/accesstransformer.cfg", "customskinloader.accesswidener", "mixins.*.json"]) { - expand([ - modVersion : rootProject.ext.config.version, - modFullVersion : project.version, - dependencies : VersionUtil.getDependencies(project.ext.config.dependencies.toString()), - gitVersion : System.getenv("CIRCLE_SHA1") ?: System.getenv("GITHUB_SHA"), - buildUrl : System.getenv("CIRCLE_BUILD_URL") ?: "https://github.com/xfl03/MCCustomSkinLoader/actions/runs/" + System.getenv("GITHUB_RUN_ID"), - releaseTime : new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") - ]) - } - } + def generatedJavaDir = layout.buildDirectory.dir('generated/sources/java-macros/main/java').get().asFile - jar { - exclude "com/**" - exclude "net/minecraft/client/renderer/RenderType.class" - exclude "net/minecraft/client/renderer/texture/**" - exclude "net/minecraft/resources/**" - } + def generateJavaMacros = tasks.register('generateJavaMacros', Sync) { + inputs.properties(javaMacroTokens()) - task genSrgMap { - doLast { - if (!config.srg_notch_map.isEmpty()) { - // convert mappings - RemapUtil.tsrg2srg file(config.srg_notch_map), file("build/reobf.srg") - RemapUtil.tsrg2srg file("mixin.tsrg"), file("build/mixin.srg") - } - } + from('src/main/java') { + include '**/*.java' + filteringCharset = 'UTF-8' + filter(ReplaceTokens, tokens: javaMacroTokens()) } - deobfCompileDummyTask.dependsOn genSrgMap - - reobf { - jar { - if (!config.srg_notch_map.isEmpty()) { - mappings = file("build/reobf.srg") - } - } + + into(generatedJavaDir) + } + + configurations.configureEach { configuration -> + if (configuration.canBeResolved) { + configuration.attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 25) } + } - task signJar(type: SignJar, dependsOn: reobfJar) { - onlyIf { System.getenv("KEY_PASS") != null && System.getenv("KEY_PASS") != "" } - doLast { System.out.println("Jar will be signed.") } + java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } - inputFile = jar.archivePath - outputFile = jar.archivePath + compileJava { + dependsOn(generateJavaMacros) + source = generatedJavaDir + } - keyStore = "${rootDir}/Common/CustomSkinLoader.jks" - alias = 'CustomSkinLoader' - storePass = System.getenv("KEY_PASS") - keyPass = System.getenv("KEY_PASS") - } - build.dependsOn signJar - - task afterBuild { - doLast { - //copyBuildFile - copy { - from "build/libs" - into "${rootDir}/build/libs" - include "**/*.jar" - } - } + processResources { + inputs.properties(resourceMacroTokens()) + filteringCharset = 'UTF-8' + + filesMatching(['**/*.json', '**/*.toml', '**/mcmod.info']) { + filter(ReplaceTokens, beginToken: '${', endToken: '}', tokens: resourceMacroTokens()) } - build.finalizedBy afterBuild } } diff --git a/build.properties b/build.properties index 17da25b7..562521e6 100644 --- a/build.properties +++ b/build.properties @@ -1,3 +1,9 @@ name=CustomSkinLoader group=customskinloader -version=14.28 +version=15.0 +edition=Universal +minecraft_major_versions=1.8,1.9,1.10,1.11,1.12,1.13,1.14,1.15,1.16,1.17,1.18,1.19,1.20,1.21,26.1 + +loaders=fabric,forge,neoforge,quilt +game-versions=1.8,1.8.8,1.8.9,1.9,1.9.4,1.10,1.10.2,1.11,1.11.2,1.12,1.12.1,1.12.2,1.13.2,1.14,1.14.1,1.14.2,1.14.3,1.14.4,1.15,1.15.1,1.15.2,1.16,1.16.1,1.16.2,1.16.3,1.16.4,1.16.5,1.17,1.17.1,1.18,1.18.1,1.18.2,1.19,1.19.1,1.19.2,1.19.3,1.19.4,1.20,1.20.1,1.20.2,1.20.3,1.20.4,1.20.5,1.20.6,1.21,1.21.1,1.21.2,1.21.3,1.21.4,1.21.5,1.21.6,1.21.7,1.21.8,1.21.9,1.21.10,1.21.11,26.1,26.1.1,26.1.2 +java=8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,25 diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 1dd35058..64526944 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -1,33 +1,25 @@ plugins { - id("java") + id 'java-gradle-plugin' } -group = "customskinloader" -version = "1.0.0" - repositories { mavenCentral() - maven { - url = "https://plugins.gradle.org/m2/" - } - maven { - url = "https://maven.minecraftforge.net/" - } - maven { - url = "https://repo.spongepowered.org/repository/maven-public/" - } +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } dependencies { - implementation("com.qcloud:cos_api:5.6.98") - implementation("com.tencentcloudapi:tencentcloud-sdk-java-cdn:3.1.559") - implementation("com.google.code.gson:gson:2.9.0") - implementation("net.minecraftforge.gradle:ForgeGradle:2.3.+") - implementation("gradle.plugin.com.matthewprenger:CurseGradle:1.4.0") - implementation("gradle.plugin.com.modrinth.minotaur:Minotaur:1.2.1") - implementation("org.spongepowered:mixingradle:0.6-SNAPSHOT") - implementation("software.amazon.awssdk:s3:2.21.10") + implementation localGroovy() } -apply from: file("patch.gradle") -patchMercury() +gradlePlugin { + plugins { + manifestLibraries { + id = 'customskinloader.manifest-libraries' + implementationClass = 'customskinloader.gradle.ManifestLibrariesPlugin' + } + } +} diff --git a/buildSrc/patch.gradle b/buildSrc/patch.gradle deleted file mode 100644 index 238d3008..00000000 --- a/buildSrc/patch.gradle +++ /dev/null @@ -1,149 +0,0 @@ - -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath "org.ow2.asm:asm-debug-all:5.2" - } -} - -import org.objectweb.asm.ClassReader -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.Opcodes -import org.objectweb.asm.tree.AbstractInsnNode -import org.objectweb.asm.tree.ClassNode -import org.objectweb.asm.tree.InsnList -import org.objectweb.asm.tree.MethodInsnNode -import org.objectweb.asm.tree.MethodNode - -import java.util.zip.ZipEntry -import java.util.zip.ZipFile -import java.util.zip.ZipOutputStream - - -configurations { - patchClass -} - -void patch(Object dependency, Closure fileFilter, List> filters) { - project.dependencies { - patchClass dependency - } - - project.afterEvaluate { - project.configurations.patchClass.findAll { fileFilter.call(it) }.each { File jarFile -> - def outputJar = file("${project.gradle.gradleUserHomeDir}/caches/customskinloader/libs/${jarFile.name}") - outputJar.parentFile.mkdirs() - - new ZipOutputStream(new FileOutputStream(outputJar)).withCloseable { zos -> - new ZipFile(jarFile).withCloseable { zf -> - zf.entries().each { ZipEntry entry -> - if (entry.name.startsWith('META-INF/') && (entry.name.endsWith('.RSA') || entry.name.endsWith('.SF'))) { - return - } - - def applicableFilters = filters.findAll { it.classFilter.call(entry.name) } - if (applicableFilters) { - def cn = new ClassNode() - new ClassReader(zf.getInputStream(entry).bytes).accept(cn, 0) - - applicableFilters.each { filter -> - cn.methods.findAll { filter.methodFilter.call(it) }.each { method -> - method.instructions.toArray().findAll { filter.nodeFilter.call(it) }.each { filter.methodPatcher.call(method.instructions, it) } - } - } - - def cw = new ClassWriter(ClassWriter.COMPUTE_MAXS) - cn.accept(cw) - zos.putNextEntry(new ZipEntry(entry.name)) - zos.write(cw.toByteArray()) - zos.closeEntry() - } else { - zos.putNextEntry(new ZipEntry(entry.name)) - zos.write(zf.getInputStream(entry).bytes) - zos.closeEntry() - } - } - } - } - - project.dependencies { - compile files(outputJar) - compile dependency - } - } - } -} - -// Mercury assumes that all classes containing '$' are inner classes and ignores classes that contain '$' natively. -void patchMercury() { - this.patch( - "org.cadixdev:mercury:0.1.0", - { File f -> - f.name == "mercury-0.1.0.jar" - }, - [ - [ - classFilter: { String name -> - name == "org/cadixdev/mercury/remapper/RemapperVisitor.class" - }, - methodFilter: { MethodNode mn -> - mn.name == "remapType" && mn.desc == "(Lorg/eclipse/jdt/core/dom/SimpleName;Lorg/eclipse/jdt/core/dom/ITypeBinding;)V" - }, - nodeFilter: { AbstractInsnNode node -> - node.opcode == Opcodes.ASTORE && node.var == 4 - }, - methodPatcher: { InsnList list, AbstractInsnNode node -> - list.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/gradle/util/RemapUtil", "fixClassName", "(Ljava/lang/String;)Ljava/lang/String;", false)) - } - ] - ] - ) -} - -void patchMixin() { - // Mixin uses incorrect method for field descriptors to get the signature. - this.patch( - "org.spongepowered:mixin:0.8.5", - { File f -> - f.name == "mixin-0.8.5.jar" - }, - [ - [ - classFilter: { String name -> - name == "org/spongepowered/tools/obfuscation/mirror/TypeHandleASM.class" - }, - methodFilter: { MethodNode mn -> - mn.name == "findField" && mn.desc == "(Ljava/lang/String;Ljava/lang/String;Z)Lorg/spongepowered/tools/obfuscation/mirror/FieldHandle;" - }, - nodeFilter: { AbstractInsnNode node -> - node.opcode == Opcodes.INVOKESTATIC && node.owner == "org/spongepowered/tools/obfuscation/mirror/TypeUtils" && node.name == "getJavaSignature" && node.desc == "(Ljava/lang/String;)Ljava/lang/String;" - }, - methodPatcher: { InsnList list, AbstractInsnNode node -> - list.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "org/objectweb/asm/Type", "getType", "(Ljava/lang/String;)Lorg/objectweb/asm/Type;", false)) - list.set(node, new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "org/objectweb/asm/Type", "getClassName", "()Ljava/lang/String;", false)) - } - ], - [ - classFilter: { String name -> - name == "org/spongepowered/tools/obfuscation/mapping/common/MappingProvider.class" - }, - methodFilter: { MethodNode mn -> - mn.name == "" && mn.desc == "(Ljavax/annotation/processing/Messager;Ljavax/annotation/processing/Filer;)V" - }, - nodeFilter: { AbstractInsnNode node -> - node.opcode == Opcodes.PUTFIELD && node.owner == "org/spongepowered/tools/obfuscation/mapping/common/MappingProvider" && node.desc == "Lcom/google/common/collect/BiMap;" - }, - methodPatcher: { InsnList list, AbstractInsnNode node -> - list.insertBefore(node, new MethodInsnNode(Opcodes.INVOKESTATIC, "customskinloader/dummy/FakeBiMap", "create", "(Lcom/google/common/collect/BiMap;)Lcom/google/common/collect/BiMap;", false)) - } - ] - ] - ) -} - -ext { - patchMixin = this.&patchMixin - patchMercury = this.&patchMercury -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/BuildPlugin.java b/buildSrc/src/main/java/customskinloader/gradle/BuildPlugin.java deleted file mode 100644 index 4a775e29..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/BuildPlugin.java +++ /dev/null @@ -1,20 +0,0 @@ -package customskinloader.gradle; - -import customskinloader.gradle.task.UploadBetaTask; -import customskinloader.gradle.task.UploadCanaryTask; -import customskinloader.gradle.task.UploadReleaseTask; -import org.gradle.api.Plugin; -import org.gradle.api.Project; - -public class BuildPlugin implements Plugin { - @Override - public void apply(Project project) { - System.out.printf("Apply project '%s'", project.getName()); - project.getTasks().create("upload", UploadReleaseTask.class, - task -> task.rootProject = project.getRootProject()); - project.getTasks().create("uploadBeta", UploadBetaTask.class, - task -> task.rootProject = project.getRootProject()); - project.getTasks().create("uploadCanary", UploadCanaryTask.class, - task -> task.rootProject = project.getRootProject()); - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/ManifestLibrariesExtension.java b/buildSrc/src/main/java/customskinloader/gradle/ManifestLibrariesExtension.java new file mode 100644 index 00000000..c8639843 --- /dev/null +++ b/buildSrc/src/main/java/customskinloader/gradle/ManifestLibrariesExtension.java @@ -0,0 +1,295 @@ +package customskinloader.gradle; + +import groovy.json.JsonSlurper; +import org.gradle.api.GradleException; +import org.gradle.api.Project; + +import javax.inject.Inject; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +public class ManifestLibrariesExtension { + private static final int TIMEOUT_MILLIS = 15_000; + + private final Project project; + + @Inject + public ManifestLibrariesExtension(Project project) { + this.project = project; + } + + public ManifestLibrarySet resolve(String manifestUrl) { + Set repositoryUrls = new LinkedHashSet<>(); + Set dependencyNotations = new LinkedHashSet<>(); + + Map manifest = asObject(readJson(manifestUrl), "manifest root"); + Object librariesValue = manifest.get("libraries"); + if (librariesValue == null) { + return new ManifestLibrarySet(repositoryUrls, dependencyNotations); + } + + List libraries = asList(librariesValue, "libraries"); + for (Object libraryValue : libraries) { + Map library = asObject(libraryValue, "library"); + if (!isAllowed(library)) { + continue; + } + + if (library.get("downloads") != null) { + addDownloadsLibrary(library, repositoryUrls, dependencyNotations); + } else { + addUrlLibrary(library, repositoryUrls, dependencyNotations); + } + } + + return new ManifestLibrarySet(repositoryUrls, dependencyNotations); + } + + public void add(String manifestUrl) { + add(resolve(manifestUrl)); + } + + public void add(ManifestLibrarySet libraries) { + libraries.addTo(project); + } + + public void add(ManifestLibrarySet libraries, Project target) { + libraries.addTo(target); + } + + public void add(ManifestLibrarySet libraries, Iterable targets) { + for (Object target : targets) { + if (!(target instanceof Project)) { + throw new GradleException("Expected manifest library target to be a Project, got " + target); + } + libraries.addTo((Project) target); + } + } + + private Object readJson(String url) { + try { + URLConnection connection = new URI(url).toURL().openConnection(); + connection.setRequestProperty("User-Agent", project.getRootProject().getName() + "/" + project.getRootProject().getVersion()); + connection.setConnectTimeout(TIMEOUT_MILLIS); + connection.setReadTimeout(TIMEOUT_MILLIS); + + try (InputStream input = connection.getInputStream(); + InputStreamReader reader = new InputStreamReader(input, StandardCharsets.UTF_8)) { + return new JsonSlurper().parse(reader); + } + } catch (Exception exception) { + throw new GradleException("Failed to read manifest libraries from " + url, exception); + } + } + + private static void addUrlLibrary(Map library, Set repositoryUrls, Set dependencyNotations) { + String name = asString(library.get("name")); + String url = asString(library.get("url")); + + if (name != null && !name.isEmpty()) { + dependencyNotations.add(name); + } + if (url != null && !url.isEmpty()) { + repositoryUrls.add(trimTrailingSlashes(url)); + } + } + + private static void addDownloadsLibrary(Map library, Set repositoryUrls, Set dependencyNotations) { + String name = asString(library.get("name")); + String artifactUrl = artifactUrl(library); + if (name == null || name.isEmpty() || artifactUrl == null || artifactUrl.isEmpty()) { + return; + } + + DerivedArtifact artifact = deriveArtifact(name, artifactUrl); + repositoryUrls.add(artifact.repositoryUrl); + dependencyNotations.add(artifact.dependencyNotation); + } + + @SuppressWarnings("unchecked") + private static String artifactUrl(Map library) { + Object downloads = library.get("downloads"); + if (!(downloads instanceof Map)) { + return null; + } + + Object artifact = ((Map) downloads).get("artifact"); + if (!(artifact instanceof Map)) { + return null; + } + + return asString(((Map) artifact).get("url")); + } + + private static DerivedArtifact deriveArtifact(String name, String artifactUrl) { + String[] nameParts = name.split(":"); + if (nameParts.length < 3) { + throw new GradleException("Expected dependency name to be group:artifact:version, got " + name); + } + + String group = nameParts[0]; + String[] organizationPath = group.split("\\."); + URI uri = URI.create(artifactUrl); + String[] path = trimLeadingSlashes(uri.getPath()).split("/"); + int organizationStart = indexOf(path, organizationPath); + if (organizationStart < 0) { + throw new GradleException("Could not find organization path " + group.replace('.', '/') + " in " + artifactUrl); + } + + int artifactIndex = organizationStart + organizationPath.length; + int versionIndex = artifactIndex + 1; + int fileIndex = versionIndex + 1; + if (path.length != fileIndex + 1) { + throw new GradleException("Expected Maven artifact path in " + artifactUrl); + } + + String artifact = path[artifactIndex]; + String version = path[versionIndex]; + String fileName = path[fileIndex]; + String baseName = stripExtension(fileName); + String prefix = artifact + "-" + version; + if (!baseName.equals(prefix) && !baseName.startsWith(prefix + "-")) { + throw new GradleException("Expected " + fileName + " to start with " + prefix); + } + + String repositoryUrl = uri.getScheme() + "://" + uri.getAuthority() + pathPrefix(path, organizationStart); + String dependencyNotation = group + ":" + artifact + ":" + version; + if (!baseName.equals(prefix)) { + dependencyNotation += ":" + baseName.substring(prefix.length() + 1); + } + + return new DerivedArtifact(trimTrailingSlashes(repositoryUrl), dependencyNotation); + } + + private static String stripExtension(String fileName) { + int extensionStart = fileName.lastIndexOf('.'); + return extensionStart < 0 ? fileName : fileName.substring(0, extensionStart); + } + + private static int indexOf(String[] values, String[] sequence) { + for (int i = 0; i <= values.length - sequence.length; i++) { + boolean matches = true; + for (int j = 0; j < sequence.length; j++) { + if (!values[i + j].equals(sequence[j])) { + matches = false; + break; + } + } + if (matches) { + return i; + } + } + return -1; + } + + private static String pathPrefix(String[] path, int endExclusive) { + StringBuilder prefix = new StringBuilder(); + for (int i = 0; i < endExclusive; i++) { + prefix.append('/').append(path[i]); + } + return prefix.toString(); + } + + private static boolean isAllowed(Map library) { + Object rulesValue = library.get("rules"); + if (rulesValue == null) { + return true; + } + + boolean allowed = false; + for (Object ruleValue : asList(rulesValue, "rules")) { + Map rule = asObject(ruleValue, "library rule"); + if (matchesOs(rule.get("os"))) { + allowed = "allow".equals(asString(rule.get("action"))); + } + } + return allowed; + } + + private static boolean matchesOs(Object osValue) { + if (osValue == null) { + return true; + } + + Map os = asObject(osValue, "rule os"); + String name = asString(os.get("name")); + String arch = asString(os.get("arch")); + String version = asString(os.get("version")); + + return (name == null || name.equals(currentOs())) + && (arch == null || arch.equals(System.getProperty("os.arch"))) + && (version == null || System.getProperty("os.version").matches(version)); + } + + private static String currentOs() { + String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT); + if (osName.contains("win")) { + return "windows"; + } + if (osName.contains("mac") || osName.contains("darwin")) { + return "osx"; + } + if (osName.contains("linux") || osName.contains("nix")) { + return "linux"; + } + return osName; + } + + @SuppressWarnings("unchecked") + private static Map asObject(Object value, String name) { + if (value instanceof Map) { + return (Map) value; + } + throw new GradleException("Expected " + name + " to be a JSON object"); + } + + @SuppressWarnings("unchecked") + private static List asList(Object value, String name) { + if (value instanceof List) { + return (List) value; + } + throw new GradleException("Expected " + name + " to be a JSON array"); + } + + private static String asString(Object value) { + return value == null ? null : String.valueOf(value); + } + + private static String trimLeadingSlashes(String value) { + if (value == null) { + return ""; + } + + int index = 0; + while (index < value.length() && value.charAt(index) == '/') { + index++; + } + return value.substring(index); + } + + private static String trimTrailingSlashes(String value) { + int end = value.length(); + while (end > 0 && value.charAt(end - 1) == '/') { + end--; + } + return value.substring(0, end); + } + + private static final class DerivedArtifact { + final String repositoryUrl; + final String dependencyNotation; + + DerivedArtifact(String repositoryUrl, String dependencyNotation) { + this.repositoryUrl = repositoryUrl; + this.dependencyNotation = dependencyNotation; + } + } +} diff --git a/buildSrc/src/main/java/customskinloader/gradle/ManifestLibrariesPlugin.java b/buildSrc/src/main/java/customskinloader/gradle/ManifestLibrariesPlugin.java new file mode 100644 index 00000000..cb7b2151 --- /dev/null +++ b/buildSrc/src/main/java/customskinloader/gradle/ManifestLibrariesPlugin.java @@ -0,0 +1,11 @@ +package customskinloader.gradle; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +public final class ManifestLibrariesPlugin implements Plugin { + @Override + public void apply(Project project) { + project.getExtensions().create("manifestLibraries", ManifestLibrariesExtension.class, project); + } +} diff --git a/buildSrc/src/main/java/customskinloader/gradle/ManifestLibrarySet.java b/buildSrc/src/main/java/customskinloader/gradle/ManifestLibrarySet.java new file mode 100644 index 00000000..1ef80b45 --- /dev/null +++ b/buildSrc/src/main/java/customskinloader/gradle/ManifestLibrarySet.java @@ -0,0 +1,44 @@ +package customskinloader.gradle; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.ExternalModuleDependency; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +public final class ManifestLibrarySet { + private final Set repositoryUrls; + private final Set dependencyNotations; + + ManifestLibrarySet(Set repositoryUrls, Set dependencyNotations) { + this.repositoryUrls = Collections.unmodifiableSet(new LinkedHashSet<>(repositoryUrls)); + this.dependencyNotations = Collections.unmodifiableSet(new LinkedHashSet<>(dependencyNotations)); + } + + public void addTo(Project project) { + addTo(project, "compileOnly"); + } + + public void addTo(Project project, String configuration) { + for (String repositoryUrl : repositoryUrls) { + addRepository(project, repositoryUrl); + } + + for (String notation : dependencyNotations) { + ExternalModuleDependency dependency = (ExternalModuleDependency) project.getDependencies().create(notation); + dependency.setTransitive(false); + project.getDependencies().add(configuration, dependency); + } + } + + private static void addRepository(Project project, String url) { + project.getRepositories().maven(repository -> { + repository.setUrl(project.uri(url)); + repository.metadataSources(sources -> { + sources.mavenPom(); + sources.artifact(); + }); + }); + } +} diff --git a/buildSrc/src/main/java/customskinloader/gradle/entity/CslDetail.java b/buildSrc/src/main/java/customskinloader/gradle/entity/CslDetail.java deleted file mode 100644 index 5508810f..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/entity/CslDetail.java +++ /dev/null @@ -1,34 +0,0 @@ -package customskinloader.gradle.entity; - -import org.gradle.util.VersionNumber; - -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.stream.Collectors; - -public class CslDetail { - public String version; - public long timestamp; - - Map> details = new LinkedHashMap<>(); - - public CslDetail(String version) { - this.version = version; - this.timestamp = System.currentTimeMillis(); - } - - public void addDetail(String mcMajorVersion, String edition, String url) { - String e = edition - .replace("ForgeLegacy", "Forge") - .replace("ForgeActive", "Forge"); - details.computeIfAbsent(mcMajorVersion, (key) -> new LinkedHashMap<>()) - .put(e, url); - } - - public void sortDetails() { - details = details.entrySet().stream() - .sorted(Comparator.comparing(a -> VersionNumber.parse(a.getKey()))) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (x, y) -> y, LinkedHashMap::new)); - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/entity/CslLatest.java b/buildSrc/src/main/java/customskinloader/gradle/entity/CslLatest.java deleted file mode 100644 index 11a64da1..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/entity/CslLatest.java +++ /dev/null @@ -1,20 +0,0 @@ -package customskinloader.gradle.entity; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - -public class CslLatest { - public String version; - public Map downloads = new LinkedHashMap<>(); - public Map launchermeta = new LinkedHashMap<>(); - - public CslLatest(String version) { - this.version = version; - } - - public String getUrl(String edition) { - return Optional.ofNullable(downloads.get(edition)) - .orElseGet(() -> launchermeta.get(edition)); - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/storage/CosStorage.java b/buildSrc/src/main/java/customskinloader/gradle/storage/CosStorage.java deleted file mode 100644 index 6a002f1b..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/storage/CosStorage.java +++ /dev/null @@ -1,51 +0,0 @@ -package customskinloader.gradle.storage; - -import com.qcloud.cos.COSClient; -import com.qcloud.cos.ClientConfig; -import com.qcloud.cos.auth.BasicCOSCredentials; -import com.qcloud.cos.auth.COSCredentials; -import com.qcloud.cos.model.PutObjectRequest; -import com.qcloud.cos.region.Region; -import customskinloader.gradle.util.CdnUtil; - -import java.nio.file.Path; - -/** - * Tencent Cloud COS - */ -public class CosStorage implements Storage { - private static final String BUCKET_NAME = System.getenv("COS_BUCKET"); - private COSClient cosClient = null; - - private COSClient getCosClient() { - if (cosClient != null) { - return cosClient; - } - - synchronized (this) { - if (cosClient != null) { - return cosClient; - } - - COSCredentials cred = new BasicCOSCredentials( - System.getenv("COS_SECRET_ID"), - System.getenv("COS_SECRET_KEY") - ); - ClientConfig clientConfig = new ClientConfig(new Region("ap-shanghai")); - cosClient = new COSClient(cred, clientConfig); - - return cosClient; - } - } - - @Override - public void put(String key, Path file) { - PutObjectRequest putObjectRequest = new PutObjectRequest(BUCKET_NAME, key, file.toFile()); - getCosClient().putObject(putObjectRequest); - } - - @Override - public String getPublicBaseUrl() { - return CdnUtil.TENCENT_CDN_ROOT; - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/storage/R2Storage.java b/buildSrc/src/main/java/customskinloader/gradle/storage/R2Storage.java deleted file mode 100644 index 398b4433..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/storage/R2Storage.java +++ /dev/null @@ -1,44 +0,0 @@ -package customskinloader.gradle.storage; - -import customskinloader.gradle.util.CdnUtil; -import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; -import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.s3.S3Client; - -import javax.annotation.Nonnull; -import java.net.URI; - -/** - * CloudFlare R2 - */ -public class R2Storage extends S3Storage { - @Nonnull - @Override - protected S3Client initClient() { - try { - return S3Client.builder() - .endpointOverride(new URI(System.getenv("R2_BASE_URL"))) - .credentialsProvider( - StaticCredentialsProvider.create( - AwsBasicCredentials.create( - System.getenv("R2_SECRET_ID"), - System.getenv("R2_SECRET_KEY")))) - .region(Region.of("auto")) - .build(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Nonnull - @Override - protected String getBucket() { - return System.getenv("R2_BUCKET"); - } - - @Override - public String getPublicBaseUrl() { - return CdnUtil.CLOUDFLARE_CDN_ROOT; - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/storage/S3Storage.java b/buildSrc/src/main/java/customskinloader/gradle/storage/S3Storage.java deleted file mode 100644 index b0b42d23..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/storage/S3Storage.java +++ /dev/null @@ -1,50 +0,0 @@ -package customskinloader.gradle.storage; - -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.PutObjectRequest; - -import javax.annotation.Nonnull; -import java.nio.file.Path; - -/** - * Amazon Web Service Simple Storage Service - * There is also plenty of S3 compatible API. - */ -public abstract class S3Storage implements Storage { - private S3Client client; - - /** - * The S3 client will be initialized only once. - * @return S3 client - */ - protected abstract @Nonnull S3Client initClient(); - - private @Nonnull S3Client getClient() { - if (client != null) { - return client; - } - synchronized (this) { - if (client != null) { - return client; - } - client = initClient(); - return client; - } - } - - /** - * Get bucket for object. - * @return bucket - */ - protected abstract @Nonnull String getBucket(); - - @Override - public void put(String key, Path file) { - getClient().putObject( - PutObjectRequest.builder() - .bucket(getBucket()) - .key(key).build(), - file - ); - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/storage/Storage.java b/buildSrc/src/main/java/customskinloader/gradle/storage/Storage.java deleted file mode 100644 index f6fef383..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/storage/Storage.java +++ /dev/null @@ -1,14 +0,0 @@ -package customskinloader.gradle.storage; - -import java.nio.file.Path; - -public interface Storage { - /** - * Put file to object storage service. - * @param key key - * @param file file - */ - void put(String key, Path file); - - String getPublicBaseUrl(); -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/storage/StorageService.java b/buildSrc/src/main/java/customskinloader/gradle/storage/StorageService.java deleted file mode 100644 index 88ff1f03..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/storage/StorageService.java +++ /dev/null @@ -1,61 +0,0 @@ -package customskinloader.gradle.storage; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; - -public class StorageService { - private static final List storages = new ArrayList<>(); - - static { - storages.add(new CosStorage()); - storages.add(new R2Storage()); - } - - /** - * Put file to all storage. - * - * @param key key - * @param file file - */ - public static void put(String key, Path file) { - storages.forEach(it -> it.put(key, file)); - } - - /** - * Put file to all storage. - * - * @param key key - * @param file file - */ - public static void put(String key, File file) { - put(key, file.toPath()); - } - - private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); - - /** - * Put object to all storage. - * - * @param key key - * @param obj obj - */ - public static void put(String key, Object obj) throws IOException { - for (Storage it : storages) { - Path file = Paths.get("build/libs/", key); - Files.write(file, - gson.toJson(obj).replace(BASE_URL, it.getPublicBaseUrl()).getBytes(StandardCharsets.UTF_8)); - it.put(key, file); - } - } - - public static final String BASE_URL = "{BASE_URL}"; -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/task/SourceSetsSetupTask.java b/buildSrc/src/main/java/customskinloader/gradle/task/SourceSetsSetupTask.java deleted file mode 100644 index 02e562d7..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/task/SourceSetsSetupTask.java +++ /dev/null @@ -1,36 +0,0 @@ -package customskinloader.gradle.task; - -import java.util.HashSet; -import java.util.Set; - -import org.gradle.api.DefaultTask; -import org.gradle.api.Project; -import org.gradle.api.plugins.JavaPluginConvention; -import org.gradle.api.tasks.SourceSet; -import org.gradle.api.tasks.TaskAction; - -/** - * We need to add sourceSets at compile time to avoid IntelliJ IDEA from removing duplicated source roots and to avoid Eclipse add source dirs twice. - * The reason of that we don't just dependent other subprojects is to let Mixin AP work correctly. - */ -public class SourceSetsSetupTask extends DefaultTask { - public Project project; - - public Set otherProjects = new HashSet<>(); - - @TaskAction - public void setup() { - project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().getByName("main", sourceSet -> - this.otherProjects.stream() - .filter(other -> other.getPlugins().hasPlugin("java")) - .forEach(other -> { - SourceSet otherMain = other.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().getByName("main"); - otherMain.getJava().getSrcDirs().forEach(targetDir -> - sourceSet.getJava().srcDir(targetDir) - ); - otherMain.getResources().getSrcDirs().forEach(targetDir -> - sourceSet.getResources().srcDir(targetDir) - ); - })); - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/task/UploadBaseTask.java b/buildSrc/src/main/java/customskinloader/gradle/task/UploadBaseTask.java deleted file mode 100644 index c482d7cf..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/task/UploadBaseTask.java +++ /dev/null @@ -1,88 +0,0 @@ -package customskinloader.gradle.task; - -import com.tencentcloudapi.common.exception.TencentCloudSDKException; -import customskinloader.gradle.entity.CslDetail; -import customskinloader.gradle.entity.CslLatest; -import customskinloader.gradle.storage.StorageService; -import customskinloader.gradle.util.CdnUtil; -import customskinloader.gradle.util.ConfigUtil; -import customskinloader.gradle.util.StorageUtil; -import customskinloader.gradle.util.VersionUtil; -import org.gradle.api.DefaultTask; -import org.gradle.api.Project; - -import java.io.File; -import java.io.IOException; - -public abstract class UploadBaseTask extends DefaultTask { - public Project rootProject; - - private CslLatest uploadArtifacts(String filename) throws IOException { - String shortVersion = VersionUtil.getShortVersion(rootProject); - File dir = rootProject.file("build/libs"); - if (!dir.isDirectory()) { - return null; - } - - String cslversion = shortVersion.replace(".", ""); - CslLatest latest = new CslLatest(shortVersion); - - File[] files = dir.listFiles(); - if (files == null) { - return null; - } - for (File file : files) { - if (file.getName().endsWith("-sources.jar")) { - //Don't upload sources jar to cos - continue; - } - String key = StorageUtil.getKey(file.getName()); - if (key == null) { - continue; - } - StorageService.put(key, file); - String mcversion = VersionUtil.getMcVersion(file.getName()); - System.out.printf("csl-%s-%s\t%s%n", - mcversion.replace(".", "").toLowerCase(), - cslversion, CdnUtil.CLOUDFLARE_CDN_ROOT + key); - - if (key.startsWith("mods/") && key.endsWith(".jar") && !key.endsWith("-sources.jar")) { - latest.downloads.put(mcversion, StorageService.BASE_URL + key); - } - } - - StorageService.put(filename, latest); - return latest; - } - - private void uploadDetail(CslLatest latest, String filename) throws IOException { - CslDetail detail = new CslDetail(latest.version); - - rootProject.getAllprojects().stream() - .filter(it -> !"false".equals(ConfigUtil.getConfigString(it, "is_real_project"))) - .forEach(project -> { - String edition = VersionUtil.getEdition(project); - String url = latest.getUrl(edition); - VersionUtil.parseDependencies(ConfigUtil.getConfigString(project, "dependencies")) - .forEach((loader, version) -> VersionUtil.getMcMajorVersions(version) - .forEach(mcMajorVersion -> detail.addDetail(mcMajorVersion, loader, url))); - }); - - detail.sortDetails(); - StorageService.put(filename, detail); - } - - protected void uploadBase(String latestJsonName, String detailJsonName) throws IOException, TencentCloudSDKException { - System.out.printf("latestJsonName: %s, detailJsonName: %s\n", latestJsonName, detailJsonName); - if (System.getenv("COS_SECRET_KEY") == null) { - System.out.println("COS_SECRET_KEY not found."); - return; - } - CslLatest latest = uploadArtifacts(latestJsonName); - if (latest == null) { - return; - } - uploadDetail(latest, detailJsonName); - CdnUtil.updateCdn(latestJsonName, detailJsonName); - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/task/UploadBetaTask.java b/buildSrc/src/main/java/customskinloader/gradle/task/UploadBetaTask.java deleted file mode 100644 index c49efc68..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/task/UploadBetaTask.java +++ /dev/null @@ -1,13 +0,0 @@ -package customskinloader.gradle.task; - -import com.tencentcloudapi.common.exception.TencentCloudSDKException; -import org.gradle.api.tasks.TaskAction; - -import java.io.IOException; - -public class UploadBetaTask extends UploadBaseTask { - @TaskAction - public void uploadBeta() throws IOException, TencentCloudSDKException { - uploadBase("latest-beta.json", "detail-beta.json"); - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/task/UploadCanaryTask.java b/buildSrc/src/main/java/customskinloader/gradle/task/UploadCanaryTask.java deleted file mode 100644 index 80bb9b81..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/task/UploadCanaryTask.java +++ /dev/null @@ -1,13 +0,0 @@ -package customskinloader.gradle.task; - -import com.tencentcloudapi.common.exception.TencentCloudSDKException; -import org.gradle.api.tasks.TaskAction; - -import java.io.IOException; - -public class UploadCanaryTask extends UploadBaseTask { - @TaskAction - public void uploadCanary() throws IOException, TencentCloudSDKException { - uploadBase("latest-canary.json","detail-canary.json"); - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/task/UploadReleaseTask.java b/buildSrc/src/main/java/customskinloader/gradle/task/UploadReleaseTask.java deleted file mode 100644 index 4686aa50..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/task/UploadReleaseTask.java +++ /dev/null @@ -1,13 +0,0 @@ -package customskinloader.gradle.task; - -import com.tencentcloudapi.common.exception.TencentCloudSDKException; -import org.gradle.api.tasks.TaskAction; - -import java.io.IOException; - -public class UploadReleaseTask extends UploadBaseTask{ - @TaskAction - public void upload() throws IOException, TencentCloudSDKException { - uploadBase("latest.json","detail.json"); - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/util/CdnUtil.java b/buildSrc/src/main/java/customskinloader/gradle/util/CdnUtil.java deleted file mode 100644 index 3f720860..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/util/CdnUtil.java +++ /dev/null @@ -1,50 +0,0 @@ -package customskinloader.gradle.util; - -import com.tencentcloudapi.cdn.v20180606.CdnClient; -import com.tencentcloudapi.cdn.v20180606.models.PurgeUrlsCacheRequest; -import com.tencentcloudapi.cdn.v20180606.models.PushUrlsCacheRequest; -import com.tencentcloudapi.common.Credential; -import com.tencentcloudapi.common.exception.TencentCloudSDKException; - -import java.util.Arrays; - -public class CdnUtil { - public static final String TENCENT_CDN_ROOT = "https://csl.littleservice.cn/"; - public static final String CLOUDFLARE_CDN_ROOT = "https://csl.3-3.dev/"; - private static CdnClient cdnClient; - - private static CdnClient getCdnClient() { - //Check if CdnClient has been created - if (cdnClient != null) { - return cdnClient; - } - - //Create CdnClient - Credential credential = new Credential( - System.getenv("COS_SECRET_ID"), - System.getenv("COS_SECRET_KEY") - ); - cdnClient = new CdnClient(credential, ""); - - return cdnClient; - } - - /** - * CDN has cache. When updating file, we should update CDN cache. - * @param paths what to update - * @throws TencentCloudSDKException when Tencent Cloud API has exception - */ - public static void updateCdn(String... paths) throws TencentCloudSDKException { - String[] urls = Arrays.stream(paths).map(it -> TENCENT_CDN_ROOT + it).toArray(String[]::new); - - //Purge CDN cache - PurgeUrlsCacheRequest purgeReq = new PurgeUrlsCacheRequest(); - purgeReq.setUrls(urls); - getCdnClient().PurgeUrlsCache(purgeReq); - - //Push to CDN cache - PushUrlsCacheRequest pushReq = new PushUrlsCacheRequest(); - pushReq.setUrls(urls); - getCdnClient().PushUrlsCache(pushReq); - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/util/ConfigUtil.java b/buildSrc/src/main/java/customskinloader/gradle/util/ConfigUtil.java deleted file mode 100644 index 12b003a0..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/util/ConfigUtil.java +++ /dev/null @@ -1,33 +0,0 @@ -package customskinloader.gradle.util; - -import groovy.util.ConfigObject; -import org.gradle.api.Project; - -public class ConfigUtil { - public static ConfigObject getConfig(Project project) { - if (!project.getExtensions().getExtraProperties().has("config")) { - System.out.printf("Config not found in '%s'", project.getName()); - return new ConfigObject(); - } - Object o = project.getExtensions().getExtraProperties().get("config"); - if (!(o instanceof ConfigObject)) { - System.out.printf("Config in '%s' is not ConfigObject", project.getName()); - return new ConfigObject(); - } - return (ConfigObject) o; - } - - public static String getConfigString(Project project, String key) { - ConfigObject config = getConfig(project); - Object o = config.get(key); - if (o == null) { - //System.out.printf("Config '%s' not found in '%s'", key, project.getName()); - return null; - } - if (!(o instanceof String)) { - System.out.printf("Config '%s' in '%s' is not String", key, project.getName()); - return null; - } - return (String) config.getProperty(key); - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/util/RemapUtil.java b/buildSrc/src/main/java/customskinloader/gradle/util/RemapUtil.java deleted file mode 100644 index 5775f84f..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/util/RemapUtil.java +++ /dev/null @@ -1,214 +0,0 @@ -package customskinloader.gradle.util; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.io.Writer; -import java.lang.reflect.Field; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import com.google.common.collect.Sets; -import net.minecraftforge.srg2source.ast.RangeExtractor; -import net.minecraftforge.srg2source.util.io.InputSupplier; -import org.apache.commons.io.FileUtils; -import org.cadixdev.bombe.type.ArrayType; -import org.cadixdev.bombe.type.FieldType; -import org.cadixdev.bombe.type.ObjectType; -import org.cadixdev.lorenz.MappingSet; -import org.cadixdev.lorenz.impl.MappingSetImpl; -import org.cadixdev.lorenz.io.srg.SrgReader; -import org.cadixdev.lorenz.io.srg.SrgWriter; -import org.cadixdev.lorenz.io.srg.tsrg.TSrgReader; -import org.cadixdev.lorenz.model.ClassMapping; -import org.cadixdev.lorenz.model.FieldMapping; -import org.cadixdev.lorenz.model.MethodMapping; -import org.cadixdev.mercury.Mercury; -import org.cadixdev.mercury.remapper.MercuryRemapper; -import org.gradle.api.Project; - -/** - * Gradle Utils for Converting Mappings - * Designed for Converting Mappings. - * Converting Mappings: tsrg->srg - */ -public class RemapUtil { - public static void tsrg2srg(File tsrg, File srg) throws IOException { - if (!tsrg.exists()) return; - if (!srg.getParentFile().exists()) srg.getParentFile().mkdirs(); - - MappingSet set = new MappingSetImplWithoutInnerClass(); - - try ( - Reader reader = Files.newBufferedReader(tsrg.toPath(), StandardCharsets.UTF_8); - Writer writer = Files.newBufferedWriter(srg.toPath(), StandardCharsets.UTF_8) - ) { - new TSrgReader(reader).read(set); - new SrgWriterWithoutFilter(writer).write(set); - } - srg.deleteOnExit(); - } - - public static void remapSources(Project project) { - Optional.ofNullable(project.getTasks().findByName("extractRangemapReplacedMain")).ifPresent(task -> task.doFirst(task0 -> { - try { - // Because of https://github.com/MinecraftForge/ForgeGradle/blob/62f37569f3afc044489e7606d2eb4c2509a85fb8/build.gradle#L152-L245, - // ForgeGradle modified Eclipse JDT and Mercury will call related methods, - // so here must use some hacks. - RangeExtractor instance = new RangeExtractor(); - instance.setInput(new InputSupplier() { - @Override public String getRoot(String resource) { return null; } - @Override public InputStream getInput(String relPath) { try { return Files.newInputStream(Paths.get(relPath)); } catch (IOException e) { throw new RuntimeException(e); } } - @Override public List gatherAll(String endFilter) { return null; } - @Override public void close() { } - }); - - Field instanceField = RangeExtractor.class.getDeclaredField("INSTANCE"); - instanceField.setAccessible(true); - instanceField.set(null, instance); - - // We use Mercury to remap source jar because srg2source have a few issues. - Mercury mercury = new Mercury(); - - Set set = Sets.newHashSet(project.getConfigurations().getByName("compileClasspath").getFiles()); - set.addAll(project.getConfigurations().getByName("forgeGradleMc").getFiles()); - set.addAll(project.getConfigurations().getByName("forgeGradleMcDeps").getFiles()); - for (File dependencies : set) { - mercury.getClassPath().add(dependencies.toPath()); - } - - mercury.getProcessors().add(MercuryRemapper.create(new SrgReader(Files.newBufferedReader(project.file("build/reobf.srg").toPath(), StandardCharsets.UTF_8)).read(MappingSet.create()))); - - File source = project.file("build/sources/main/java"), dest = project.file("build/sources/main/java_tmp"); - if (source.renameTo(dest)) { - mercury.rewrite(dest.toPath(), source.toPath()); - FileUtils.deleteDirectory(dest); - } - - instanceField.set(null, null); - } catch (Exception e) { - throw new RuntimeException(e); - } - })); - } - - /** - * Invoked from {@link org.cadixdev.mercury.remapper.RemapperVisitor#remapType(org.eclipse.jdt.core.dom.SimpleName, org.eclipse.jdt.core.dom.ITypeBinding)} - */ - public static String fixClassName(String name) { - char[] chars = name.toCharArray(); - for (int i = 0; i < chars.length - 1; i++) { - if (chars[i] == '.' && chars[i + 1] >= '0' && chars[i + 1] <= '9') { - chars[i] = '$'; - } - } - return new String(chars); - } - - public static class MappingSetImplWithoutInnerClass extends MappingSetImpl { - @Override - public Optional> getClassMapping(String obfuscatedName) { - return this.getTopLevelClassMapping(obfuscatedName); - } - - @Override - public Optional> computeClassMapping(final String obfuscatedName) { - return this.getTopLevelClassMapping(obfuscatedName); - } - - @Override - public ClassMapping getOrCreateClassMapping(final String obfuscatedName) { - return this.getOrCreateTopLevelClassMapping(obfuscatedName); - } - - @Override - public FieldType deobfuscate(final FieldType type) { - if (type instanceof ArrayType) { - final ArrayType arr = (ArrayType) type; - final FieldType component = this.deobfuscate(arr.getComponent()); - return component == arr.getComponent() ? arr : new ArrayType(arr.getDimCount(), component); - } else if (type instanceof ObjectType) { - final ObjectType obj = (ObjectType) type; - - ClassMapping currentClass = this.getClassMapping(obj.getClassName()).orElse(null); - if (currentClass == null) { - return type; - } - - return new ObjectType(currentClass.getFullDeobfuscatedName()); - } - return type; - } - } - - public static class SrgWriterWithoutFilter extends SrgWriter { - private final List classes = new ArrayList<>(); - private final List fields = new ArrayList<>(); - private final List methods = new ArrayList<>(); - - public SrgWriterWithoutFilter(Writer writer) { - super(writer); - } - - @Override - public void write(final MappingSet mappings) { - // Write class mappings - mappings.getTopLevelClassMappings().stream() - .sorted(this.getConfig().getClassMappingComparator()) - .forEach(this::writeClassMapping); - - // Write everything to the print writer - this.classes.forEach(this.writer::println); - this.fields.forEach(this.writer::println); - this.methods.forEach(this.writer::println); - - // Clear out the lists, to ensure that mappings aren't written twice (or more) - this.classes.clear(); - this.fields.clear(); - this.methods.clear(); - } - - @Override - protected void writeClassMapping(ClassMapping mapping) { - // Write class mappings without checking - try { - this.classes.add(String.format("CL: %s %s", mapping.getFullObfuscatedName(), mapping.getFullDeobfuscatedName())); - } catch (Exception e) { - throw new RuntimeException(e); - } - - // Write inner class mappings - mapping.getInnerClassMappings().stream() - .sorted(this.getConfig().getClassMappingComparator()) - .forEach(this::writeClassMapping); - - // Write field mappings - mapping.getFieldsByName().values().stream() - .sorted(this.getConfig().getFieldMappingComparator()) - .forEach(this::writeFieldMapping); - - // Write method mappings - mapping.getMethodMappings().stream() - .sorted(this.getConfig().getMethodMappingComparator()) - .forEach(this::writeMethodMapping); - } - - @Override - protected void writeFieldMapping(final FieldMapping mapping) { - this.fields.add(String.format("FD: %s %s", mapping.getFullObfuscatedName(), mapping.getFullDeobfuscatedName())); - } - - @Override - protected void writeMethodMapping(final MethodMapping mapping) { - this.methods.add(String.format("MD: %s %s %s %s", - mapping.getFullObfuscatedName(), mapping.getObfuscatedDescriptor(), - mapping.getFullDeobfuscatedName(), mapping.getDeobfuscatedDescriptor())); - } - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/util/SourceUtil.java b/buildSrc/src/main/java/customskinloader/gradle/util/SourceUtil.java deleted file mode 100644 index bb5dcff4..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/util/SourceUtil.java +++ /dev/null @@ -1,32 +0,0 @@ -package customskinloader.gradle.util; - -import java.util.Collections; -import java.util.Optional; - -import customskinloader.gradle.task.SourceSetsSetupTask; -import org.gradle.api.Project; -import org.gradle.api.artifacts.Dependency; -import org.gradle.api.artifacts.ModuleDependency; - -/** - * If you want to add dependent project sources for annotation processors in current project, - * use `SourceUtil.addDependencies project, project(":xxx")` instead of `dependencies { implementation project(":xxx") }` - */ -public class SourceUtil { - public static void addDependencies(Project project, Project... otherProjects) { - if (project.getTasks().findByName("setupSourceSets") == null) { - project.getTasks().create("setupSourceSets", SourceSetsSetupTask.class, task -> { - task.project = project; - Collections.addAll(task.otherProjects, otherProjects); - Optional.ofNullable(project.getTasks().findByName("deobfCompileDummyTask")).ifPresent(task0 -> task0.dependsOn(task)); - }); - } - - for (Project otherProject : otherProjects) { - Dependency dependency = project.getDependencies().add("implementation", otherProject); - if (dependency instanceof ModuleDependency) { - ((ModuleDependency) dependency).setTransitive(false); - } - } - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/util/StorageUtil.java b/buildSrc/src/main/java/customskinloader/gradle/util/StorageUtil.java deleted file mode 100644 index 31ad52b3..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/util/StorageUtil.java +++ /dev/null @@ -1,19 +0,0 @@ -package customskinloader.gradle.util; - -public class StorageUtil { - public static String getKey(String filename) { - String name = filename.substring(0, filename.lastIndexOf('.')); - if (name.indexOf('-') == -1) { - return null; - } - if (filename.endsWith(".jar")) { - if (filename.contains("Fabric") || filename.contains("Forge")) { - return String.format( - "mods/%s", - filename - ); - } - } - return filename; - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/util/TaskUtil.java b/buildSrc/src/main/java/customskinloader/gradle/util/TaskUtil.java deleted file mode 100644 index 835b3c69..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/util/TaskUtil.java +++ /dev/null @@ -1,20 +0,0 @@ -package customskinloader.gradle.util; - -import org.gradle.api.Action; -import org.gradle.api.Project; -import org.gradle.api.Task; - -public class TaskUtil { - public static void withTask(Project project, String taskName, Action action) { - Task task = project.getTasks().findByName(taskName); - if (task == null) { - project.getTasks().whenTaskAdded(task0 -> { - if (taskName.equals(task0.getName())) { - action.execute(task0); - } - }); - } else { - action.execute(task); - } - } -} diff --git a/buildSrc/src/main/java/customskinloader/gradle/util/VersionUtil.java b/buildSrc/src/main/java/customskinloader/gradle/util/VersionUtil.java deleted file mode 100644 index dcfb5d6e..00000000 --- a/buildSrc/src/main/java/customskinloader/gradle/util/VersionUtil.java +++ /dev/null @@ -1,82 +0,0 @@ -package customskinloader.gradle.util; - -import org.gradle.api.Project; -import org.gradle.util.VersionNumber; - -import java.util.*; -import java.util.stream.Collectors; - -public class VersionUtil { - public static boolean isRelease(Project rootProject) { - String s = System.getenv("IS_SNAPSHOT"); - // When we cannot find environment variable, it means we are in development, so it is not a release - if (s == null) { - return false; - } - return s.equals("0") || s.equals("false"); - } - - public static boolean isSnapshot(Project rootProject) { - return !isRelease(rootProject); - } - - public static String getBuildNum() { - if (System.getenv("GITHUB_RUN_NUMBER") != null) { - return System.getenv("GITHUB_RUN_NUMBER"); - } - if (System.getenv("CIRCLE_BUILD_NUM") != null) { - return System.getenv("CIRCLE_BUILD_NUM"); - } - return "00"; - } - - //Example: 14.10a-SNAPSHOT-33 - public static String getCSLVersion(Project rootProject) { - - return ConfigUtil.getConfigString(rootProject, "version") + - (isRelease(rootProject) ? "" : ("-SNAPSHOT-" + getBuildNum())); - } - - //Example: 14.10a-s33 - public static String getShortVersion(Project rootProject) { - return getCSLVersion(rootProject).replace("SNAPSHOT-", "s"); - } - - public static String getMcVersion(String filename) { - return filename.substring(filename.indexOf('_') + 1, filename.indexOf('-')); - } - - public static Collection getMcMajorVersions(String version) { - if (version == null) { - return Collections.emptyList(); - } - return Arrays.stream(version.split(",")) - .map(VersionNumber::parse) - .map(it -> String.format("%s.%s", it.getMajor(), it.getMinor())) - .collect(Collectors.toSet()); - } - - public static String getEdition(Project project) { - return project.getName().replace("/", ""); - } - - public static Map parseDependencies(String dependencies) { - Map map = new LinkedHashMap<>(); - if (dependencies != null) { - String[] loaderVersions = dependencies.split(";"); - for (String loader : loaderVersions) { - String[] mcVersions = loader.split("\\|"); - map.put(mcVersions[0], mcVersions[1]); - } - } - return map; - } - - public static String getDependencies(String dependencies) { - StringBuilder sb = new StringBuilder(); - for (Map.Entry dependency : parseDependencies(dependencies).entrySet()) { - sb.append("\"").append(dependency.getKey()).append("\": \"").append(dependency.getValue()).append("\",\n "); - } - return sb.substring(1, sb.length() - 7); - } -} diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/customskinloader.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/customskinloader.properties deleted file mode 100644 index 92dab805..00000000 --- a/buildSrc/src/main/resources/META-INF/gradle-plugins/customskinloader.properties +++ /dev/null @@ -1 +0,0 @@ -implementation-class=customskinloader.gradle.BuildPlugin diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 0d4a9516..b1b8ef56 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7dc503f1..df6a6ad7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,9 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip +networkTimeout=10000 +retries=0 +retryBackOffMs=500 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d5..b9bb139f 100755 --- a/gradlew +++ b/gradlew @@ -1,78 +1,128 @@ -#!/usr/bin/env sh +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -81,92 +131,118 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" fi +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f9553162..24c62d56 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,84 +1,82 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +if exist "%JAVA_EXE%" goto execute -goto fail +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal +@rem Execute Gradle +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/settings.gradle b/settings.gradle index fba167ed..61481fea 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,21 +1,14 @@ -rootProject.name = "MCCustomSkinLoader" - -//Base Project -include "Common" -include "Dummy" - -//Vanilla Base Project -include "Vanilla/Common" - -//Forge Base Project -include "Forge/Common" -//Forge Edition for [1.8,1.16.5] -include "Forge/V1" -//Forge Edition for [1.17,1.20.4] -include "Forge/V2" - -//Fabric Edition for [26.1,), Forge Edition for [1.20.6,) and NeoForge Edition for [1.20.2,) -include "Universal" - -//Fabric Edition for [1.14,1.21.11] -include "Fabric" +rootProject.name = 'CustomSkinLoader-Universal' + +include( + 'Bootstrap', + 'Bootstrap:Core', + 'Bootstrap:FabricV1', + 'Bootstrap:ForgeV1', + 'Bootstrap:ForgeV2', + 'Bootstrap:NeoForgeV1', + 'Bootstrap:NeoForgeV2', + 'Common', + 'Dummy:Bootstrap', + 'Dummy:Common' +)