# Builds the forked client from the verified vanilla jar. # # vanilla client -> MCP names -> source -> patches -> classes # # ASCII only: PowerShell 5.1 reads a .ps1 without a byte order mark using the # system ANSI code page, which mangles UTF-8 and breaks the parser. # # Everything here is repeatable. Run it any time; nothing is edited by hand # except game-fork\patches\decompiler-fixes.txt, which is re-applied at the end. $ErrorActionPreference = 'Stop' $Fork = Split-Path -Parent $PSScriptRoot if (-not $Fork) { $Fork = $PSCommandPath | Split-Path -Parent | Split-Path -Parent } $Root = Split-Path -Parent $Fork $Client = Join-Path $Root 'rsx-client' $VanillaJar = 'G:\rsx-map\vanilla\client-1.12.2.jar' $Astral = Join-Path $env:APPDATA 'AstralRinthApp\meta' $Fernflower = 'G:\Paper-Dev\work\BuildData\bin\fernflower.jar' $SpecialSource = 'G:\Paper-Dev\work\BuildData\bin\SpecialSource.jar' $McpCs = 'G:\rsx-map\vanilla\mcp-stable' $Maps = Join-Path $Fork 'mappings' $Classes = Join-Path $Fork 'classes' $Src = Join-Path $Fork 'src' $Out = Join-Path $Fork 'build\classes' $Temp = Join-Path $env:TEMP 'opencode' function Say($text) { Write-Host " $text" -ForegroundColor Cyan } function Need($path, $what) { if (-not (Test-Path -LiteralPath $path)) { throw "missing $what at $path" } } New-Item -ItemType Directory -Force -Path $Maps, $Temp | Out-Null Need $VanillaJar 'the verified vanilla client jar' Need $Fernflower 'fernflower' Need $SpecialSource 'SpecialSource' Need $McpCs 'the MCP name CSVs' # 1. vanilla SRG $srgZip = Join-Path $Maps 'mcp-1.12.2-srg.zip' $srgDir = Join-Path $Maps 'mcp-srg-vanilla' if (-not (Test-Path (Join-Path $srgDir 'joined.srg'))) { Say 'downloading the vanilla 1.12.2 SRG' Invoke-WebRequest -Uri 'https://maven.minecraftforge.net/de/oceanlabs/mcp/mcp/1.12.2/mcp-1.12.2-srg.zip' ` -OutFile $srgZip -TimeoutSec 180 -UseBasicParsing Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::ExtractToDirectory($srgZip, $srgDir) } # 2. merge SRG with the MCP CSVs into a single obf -> MCP SRG $srg = Join-Path $Maps 'obf-to-mcp.srg' Say 'merging SRG with the MCP CSVs' & python (Join-Path $Client 'tools\merge_srg_mcp.py') ` (Join-Path $srgDir 'joined.srg') $McpCs $srg if ($LASTEXITCODE -ne 0) { throw 'srg merge failed' } # 3. remap the vanilla client $named = Join-Path $Maps 'client-mcp-named.jar' Say 'remapping the vanilla client to MCP names' if (Test-Path $named) { Remove-Item $named -Force } & java -Xmx4g -jar $SpecialSource -m $srg -i $VanillaJar -o $named if (-not (Test-Path $named)) { throw 'remap produced nothing' } Say " client-mcp-named.jar ($((Get-Item $named).Length) bytes)" # 4. unpack for the decompiler Remove-Item $Classes -Recurse -Force -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force -Path $Classes | Out-Null Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::ExtractToDirectory($named, $Classes) Say " unpacked $((Get-ChildItem $Classes -Recurse -Filter '*.class' -File).Count) classes" # 5. decompile # -dgs=1 decode generic signatures # -rbr=1 drop bridge methods (otherwise every generic method appears twice # and the compiler rejects the erasure clash) # -rsy=1 drop synthetic members Remove-Item $Src -Recurse -Force -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force -Path $Src | Out-Null Say 'decompiling (this is the slow part)' $log = Join-Path $Temp 'fernflower.log' & cmd /c "java -Xmx4g -jar `"$Fernflower`" -dgs=1 -rbr=1 -rsy=1 `"$Classes`" `"$Src`" > `"$log`" 2>&1" Say " $((Get-ChildItem $Src -Recurse -Filter '*.java' -File).Count) source files" # 5b. copy in our own game-side code # # Everything above this line is regenerated from the vanilla jar and is wiped on # every run. Our own classes under net/rustnotmc are not generated and must live # outside src, or a rebuild deletes them and leaves the patches that call them # pointing at nothing. They go in before the patches so a hand fix may edit them # too. $Overlay = Join-Path $Fork 'src-overlay' if (Test-Path $Overlay) { $copied = 0 foreach ($f in (Get-ChildItem $Overlay -Recurse -Filter '*.java' -File)) { $rel = $f.FullName.Substring($Overlay.Length).TrimStart('\') $target = Join-Path $Src $rel $dir = Split-Path -Parent $target if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Force -Path $dir | Out-Null } Copy-Item -LiteralPath $f.FullName -Destination $target -Force $copied++ } Say " overlay: $copied source file(s) from src-overlay" } # 6. re-apply the hand fixes Say 'applying hand fixes' & python (Join-Path $Client 'tools\apply_patches.py') $Src (Join-Path $Fork 'patches\decompiler-fixes.txt') if ($LASTEXITCODE -ne 0) { Write-Host ' WARNING: some patches did not apply' -ForegroundColor Yellow } # 6b. the systematic rewrites, which would be hundreds of patch entries one by one Say 'applying systematic rules' & python (Join-Path $Client 'tools\fix_decompiler_rules.py') $Src # 7. compile, let the automated fixers converge, compile again. # # The fixers need javac's diagnostics as their input, and fixing one error can # reveal the next, so this runs to a fixed point rather than once. It stops as # soon as a round fails to reduce the count, which is the signal that the # remaining errors need a human. $argf = Join-Path $Temp 'fork-build.txt' $errf = Join-Path $Temp 'fork-build-errors.txt' $good = New-Object System.Collections.Generic.List[string] foreach ($j in (Get-ChildItem (Join-Path $Astral 'libraries') -Recurse -Filter '*.jar' -File)) { if ($j.Length -le 100) { continue } $ok = $false try { $z = [System.IO.Compression.ZipFile]::OpenRead($j.FullName); $z.Dispose(); $ok = $true } catch { $ok = $false } if ($ok) { $good.Add(($j.FullName -replace '\\', '/')) } } # The fork calls into the mod loader, so the loader has to be visible to javac # exactly as it is at runtime. The protocol and the account code are compiled # against too: the client speaks the server's protocol, not the vanilla one. $Loader = Join-Path $Client 'dist\rsx-loader.jar' $Protocol = Join-Path $Root 'rsx-protocol\dist\rsx-protocol.jar' $Auth = Join-Path $Root 'rsx-auth\dist\rsx-auth.jar' $cp = @($good + $(if (Test-Path $Loader) { $Loader -replace '\\', '/' } else { $null }) ` + $(if (Test-Path $Protocol) { $Protocol -replace '\\', '/' } else { $null }) ` + $(if (Test-Path $Auth) { $Auth -replace '\\', '/' } else { $null })) -join ';' $sb = New-Object System.Text.StringBuilder foreach ($a in @('-nowarn', '-proc:none', '-encoding', 'UTF-8', '--release', '17', '-d', ($Out -replace '\\', '/'))) { [void]$sb.AppendLine($a) } [void]$sb.AppendLine('-cp'); [void]$sb.AppendLine('"' + $cp + '"') Get-ChildItem $Src -Recurse -Filter '*.java' | ForEach-Object { [void]$sb.AppendLine(($_.FullName -replace '\\', '/')) } Set-Content -LiteralPath $argf -Value $sb.ToString() -Encoding ASCII $javac = (Get-Command javac).Source function Invoke-Compile { Remove-Item $Out -Recurse -Force -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force -Path $Out | Out-Null & cmd /c "`"$javac`" @`"$argf`" -Xmaxerrs 10000 > `"$errf`" 2>&1" return (Get-Content -LiteralPath $errf | Select-String -Pattern ': error:').Count } $count = Invoke-Compile Say "compiled, $count errors" $previous = [int]::MaxValue for ($round = 1; $round -le 8 -and $count -gt 0; $round++) { if ($count -ge $previous) { Say "no further progress, stopping"; break } $previous = $count Say "fix round $round" & python (Join-Path $Client 'tools\fix_stale_locals.py') $errf --apply | Out-Null & python (Join-Path $Client 'tools\fix_generic_casts.py') $errf --apply | Out-Null & python (Join-Path $Client 'tools\fix_decompiler_rules.py') $Src | Out-Null $count = Invoke-Compile Say " -> $count errors" } $errors = Get-Content -LiteralPath $errf | Select-String -Pattern ': error:' $files = $errors | ForEach-Object { if ($_.Line -match '^([A-Z]:.*?\.java):\d+:') { $matches[1] } } | Sort-Object -Unique Write-Host '' if ($errors.Count -eq 0) { Write-Host ' FORK COMPILES CLEAN' -ForegroundColor Green Write-Host " $((Get-ChildItem $Out -Recurse -Filter '*.class' -File).Count) classes" } else { Write-Host " $((Get-ChildItem $Out -Recurse -Filter '*.class' -File).Count) classes, $($errors.Count) errors in $($files.Count) files" -ForegroundColor Yellow $errors | ForEach-Object { if ($_.Line -match '^([A-Z]:.*?\.java):\d+:') { $matches[1] } } | Group-Object | Sort-Object Count -Descending | Select-Object -First 15 | ForEach-Object { Write-Host (" {0,4} {1}" -f $_.Count, ($_.Name.Replace(($Src + '\'), ''))) -ForegroundColor Gray } }