Форк клиента собирается из исходников, модзагрузчик встроен в игру
Проект начинается с двух вещей: клиент, который мы действительно собрали, и загрузчик, который является его частью, а не чем-то рядом. Форк - prepare-game.ps1 пересобирает клиент целиком: ванильный jar, SRG и MCP, SpecialSource, FernFlower, патчи, автофиксеры, javac. Идемпотентен. - Свой код игры лежит в src-overlay, потому что src пересоздаётся при каждой сборке и всё написанное вручную там погибает. - 116 патчей в patches/decompiler-fixes.txt. Каждый сверялся с оригинальным байткодом через javap: компиляция не ловит ошибки декомпиляции. - struct_diff.py сравнивает исходный байткод с нашей сборкой по классам, интерфейсам, суперклассам, методам и полям, и отдельно проверяет, что пропавшие классы действительно никому не нужны. Загрузчик - Собирается и тестируется без игры: build.ps1 не использует classpath вообще, поэтому SelfTest из 128 проверок проходит без запущенного форка. - Клиентский API не содержит ни одного игрового типа, реализацию даёт игра. - Политика: мод меняет то, что клиент показывает, но никогда то, что истинно в мире. Мир, предметы, игроки и сеть принадлежат серверу. - Capability приведены в соответствие с этой политикой: осталось шесть, и за каждым стоит реальная проверка. Прежние WORLD, PLAYER, NETWORK, TEXT, INPUT и COMMAND выдавались, но ничего не проверяли. - Команды модов имеют область CLIENT, SERVER или BOTH. Серверные не выполняются на клиенте никогда. - Свой jar загрузчика лежит в classpath игры и сборки форка. Инструменты - add_patch.py, чтобы каждое исправление переживало передекомпиляцию - find_self_comparisons.py, find_remove_index_bugs.py, find_callers.py - stage_run.py и fetch_missing.py для запуска клиента
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
# Builds a mod jar from source.
|
||||
#
|
||||
# build-mod.ps1 [-Source <dir>] [-Output <jar>]
|
||||
#
|
||||
# Mods are ordinary jars with a descriptor at META-INF/rsxmod.properties. There
|
||||
# is no annotation processor and no gradle: javac against the loader jar, then
|
||||
# jar, then done.
|
||||
|
||||
param(
|
||||
[string]$Source = 'G:\RustNotMC\rsx-mods\rsxm-demo',
|
||||
[string]$Output = ''
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
if (-not $Output) { $Output = Join-Path $Source 'dist' }
|
||||
$Loader = 'G:\RustNotMC\rsx-client\dist\rsx-loader.jar'
|
||||
if (-not (Test-Path $Loader)) { throw "loader jar missing: $Loader" }
|
||||
|
||||
$Name = Split-Path -Leaf $Source
|
||||
$build = Join-Path $Source 'build'
|
||||
$classes = Join-Path $build 'classes'
|
||||
$jarPath = Join-Path $Output "$Name.jar"
|
||||
|
||||
Remove-Item $classes -Recurse -Force -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Force -Path $classes | Out-Null
|
||||
|
||||
$mainSrc = Join-Path $Source 'src\main\java'
|
||||
$mainRes = Join-Path $Source 'resources'
|
||||
|
||||
$mainFiles = @(Get-ChildItem $mainSrc -Recurse -Filter '*.java' | ForEach-Object { $_.FullName })
|
||||
Write-Host " sources: $($mainFiles.Count)"
|
||||
|
||||
& javac -encoding UTF-8 -nowarn -proc:none --release 17 -cp $Loader -d $classes @mainFiles
|
||||
if ($LASTEXITCODE -ne 0) { throw "javac failed" }
|
||||
|
||||
if (Test-Path $mainRes) { Copy-Item -Recurse -Force (Join-Path $mainRes '*') $classes }
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $Output | Out-Null
|
||||
Remove-Item $jarPath -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# a mod is a library, never an executable, so there is no main class here
|
||||
Push-Location $classes
|
||||
& jar --create --file $jarPath -C $classes .
|
||||
$jarExit = $LASTEXITCODE
|
||||
Pop-Location
|
||||
if ($jarExit -ne 0) { throw "jar failed with exit code $jarExit" }
|
||||
|
||||
Write-Host ''
|
||||
Write-Host " $jarPath ($((Get-Item $jarPath).Length) bytes)"
|
||||
exit 0
|
||||
@@ -0,0 +1,66 @@
|
||||
# Builds the loader into dist\.
|
||||
#
|
||||
# Only a JDK is required. No Gradle, no network: the loader is compiled with
|
||||
# javac exactly the way the client will run it, with --release 17, because the
|
||||
# game ships its own Java 8 runtime and anything newer simply will not load.
|
||||
#
|
||||
# dist\rsx-loader.jar the loader and the mod API
|
||||
#
|
||||
# NOTE: this file is ASCII on purpose. Windows PowerShell 5.1 reads a .ps1 that
|
||||
# has no byte order mark using the system ANSI code page, which mangles UTF-8
|
||||
# and makes the parser choke on non-ASCII text. Keep script messages English.
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$Repo = Split-Path -Parent $PSScriptRoot
|
||||
if (-not $Repo) { $Repo = $PSCommandPath | Split-Path -Parent | Split-Path -Parent }
|
||||
$Dist = Join-Path $Repo 'dist'
|
||||
$Out = Join-Path $Repo 'build'
|
||||
|
||||
function Say($text) { Write-Host " $text" -ForegroundColor Cyan }
|
||||
|
||||
if (-not (Get-Command javac -ErrorAction SilentlyContinue)) {
|
||||
Write-Host ' A JDK (javac) is required.' -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $Dist, $Out | Out-Null
|
||||
|
||||
# ---------------------------------------------------------------- loader
|
||||
$mainOut = Join-Path $Out 'classes'
|
||||
Remove-Item $mainOut -Recurse -Force -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Force -Path $mainOut | Out-Null
|
||||
|
||||
$mainSrc = Join-Path $Repo 'src\main\java'
|
||||
$mainFiles = @(Get-ChildItem $mainSrc -Recurse -Filter '*.java' | ForEach-Object { $_.FullName })
|
||||
if ($mainFiles.Count -eq 0) {
|
||||
Write-Host ' No sources found.' -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
& javac -encoding UTF-8 --release 17 -Xlint:-options -nowarn -d $mainOut $mainFiles
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host ' Loader failed to compile.' -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
Say "loader compiled ($($mainFiles.Count) files)"
|
||||
|
||||
$manifest = Join-Path $Out 'manifest.txt'
|
||||
@"
|
||||
Manifest-Version: 1.0
|
||||
Implementation-Title: RustNotMC loader
|
||||
Implementation-Version: 0.1.0
|
||||
"@ | Set-Content -LiteralPath $manifest -Encoding ASCII
|
||||
|
||||
$jar = Join-Path $Dist 'rsx-loader.jar'
|
||||
if (Test-Path -LiteralPath $jar) { Remove-Item -LiteralPath $jar -Force }
|
||||
& jar cfm $jar $manifest -C $mainOut .
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host ' Could not package the jar.' -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
Say "rsx-loader.jar ($((Get-Item $jar).Length) bytes)"
|
||||
|
||||
Write-Host ''
|
||||
Write-Host ' Done:' -ForegroundColor Green
|
||||
Write-Host " $jar"
|
||||
@@ -0,0 +1,63 @@
|
||||
# Launches the compiled fork as an ordinary client: no agent, no injection.
|
||||
#
|
||||
# run-game.ps1 [-Run <dir>] [-Java <exe>] [-ExtraArgs <string[]>]
|
||||
#
|
||||
# Stage first if run\classpath.txt is missing:
|
||||
# python tools\stage_run.py <run> ..\..\game-fork\build\classes <meta>
|
||||
|
||||
param(
|
||||
[string]$Run = 'G:\RustNotMC\run',
|
||||
[string]$Java = '',
|
||||
[string[]]$ExtraArgs = @(),
|
||||
[int]$Width = 1280,
|
||||
[int]$Height = 720
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$root = 'G:\RustNotMC'
|
||||
|
||||
if (-not (Test-Path (Join-Path $Run 'classpath.txt'))) {
|
||||
throw "no classpath.txt in $Run - run stage_run.py first"
|
||||
}
|
||||
|
||||
if (-not $Java) {
|
||||
$Java = Get-ChildItem "$env:APPDATA\AstralRinthApp\meta\java_versions" `
|
||||
-Directory | Where-Object { $_.Name -like 'zulu17*' } | ForEach-Object {
|
||||
Get-ChildItem $_.FullName -Recurse -Filter 'java.exe' -ErrorAction SilentlyContinue |
|
||||
Select-Object -First 1 } | Select-Object -First 1 -ExpandProperty FullName
|
||||
}
|
||||
if (-not $Java) { throw 'no Java 17 found' }
|
||||
|
||||
$assets = "$env:APPDATA\AstralRinthApp\meta\assets"
|
||||
$cp = (Get-Content (Join-Path $Run 'classpath.txt') -Raw).Trim()
|
||||
$natives = Join-Path $Run 'natives'
|
||||
|
||||
# built as strings first: mixing '+' into an array literal folds the whole
|
||||
# list into one element, because the comma binds tighter than the concatenation
|
||||
$javaArgs = @()
|
||||
$javaArgs += "-Djava.library.path=$natives"
|
||||
$javaArgs += "-Dorg.lwjgl.librarypath=$natives"
|
||||
$javaArgs += '-Dfile.encoding=UTF-8'
|
||||
$javaArgs += '-cp'
|
||||
$javaArgs += $cp
|
||||
$javaArgs += 'net.minecraft.client.main.Main'
|
||||
# singleplayer-only session: the client never validates this against Mojang
|
||||
$javaArgs += '--username'; $javaArgs += 'Player'
|
||||
$javaArgs += '--version'; $javaArgs += '1.12.2'
|
||||
$javaArgs += '--gameDir'; $javaArgs += $Run
|
||||
$javaArgs += '--assetsDir'; $javaArgs += $assets
|
||||
$javaArgs += '--assetIndex'; $javaArgs += '1.12'
|
||||
$javaArgs += '--uuid'; $javaArgs += '00000000000000000000000000000000'
|
||||
$javaArgs += '--accessToken'; $javaArgs += '0'
|
||||
$javaArgs += '--userType'; $javaArgs += 'legacy'
|
||||
$javaArgs += '--versionType'; $javaArgs += 'release'
|
||||
$javaArgs += '--width'; $javaArgs += "$Width"
|
||||
$javaArgs += '--height'; $javaArgs += "$Height"
|
||||
$javaArgs += $ExtraArgs
|
||||
|
||||
Write-Host "java : $Java"
|
||||
Write-Host "game : $Run"
|
||||
Write-Host "assets : $assets"
|
||||
Write-Host "args : $($javaArgs.Count)"
|
||||
& $Java @javaArgs
|
||||
exit $LASTEXITCODE
|
||||
@@ -0,0 +1,43 @@
|
||||
# Compiles the loader plus the self test and runs it.
|
||||
#
|
||||
# ASCII only, for the same reason as build.ps1: PowerShell 5.1 misreads a .ps1
|
||||
# that has no byte order mark.
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$Repo = Split-Path -Parent $PSScriptRoot
|
||||
if (-not $Repo) { $Repo = $PSCommandPath | Split-Path -Parent | Split-Path -Parent }
|
||||
$Out = Join-Path $Repo 'build'
|
||||
|
||||
function Say($text) { Write-Host " $text" -ForegroundColor Cyan }
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $Out | Out-Null
|
||||
|
||||
$mainOut = Join-Path $Out 'classes'
|
||||
Remove-Item $mainOut -Recurse -Force -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Force -Path $mainOut | Out-Null
|
||||
|
||||
$mainFiles = @(Get-ChildItem (Join-Path $Repo 'src\main\java') -Recurse -Filter '*.java' |
|
||||
ForEach-Object { $_.FullName })
|
||||
& javac -encoding UTF-8 --release 17 -Xlint:-options -nowarn -d $mainOut $mainFiles
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host ' Main failed.' -ForegroundColor Red; exit 1 }
|
||||
|
||||
$testOut = Join-Path $Out 'test-classes'
|
||||
Remove-Item $testOut -Recurse -Force -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Force -Path $testOut | Out-Null
|
||||
|
||||
$testFiles = @(Get-ChildItem (Join-Path $Repo 'src\test\java') -Recurse -Filter '*.java' |
|
||||
ForEach-Object { $_.FullName })
|
||||
& javac -encoding UTF-8 --release 17 -Xlint:-options -nowarn -cp $mainOut -d $testOut $testFiles
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host ' Test failed to compile.' -ForegroundColor Red; exit 1 }
|
||||
Say "compiled $($mainFiles.Count) main + $($testFiles.Count) test files"
|
||||
|
||||
& java -cp "$mainOut;$testOut" net.rustnotmc.test.SelfTest (Join-Path $Out 'selftest')
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host ''
|
||||
Write-Host ' SELFTEST FAILED' -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
Write-Host ' Self test passed.' -ForegroundColor Green
|
||||
Reference in New Issue
Block a user