cc1eaece40
Главное: модлоадер физически не умел запускать больше одного мода. Родной
загрузчик игры запоминает тик-хук в статическом поле, которое перезаписывается
на каждой загрузке:
do = new ilxq(); // свой загрузчик на мод
int = method2; // статическое поле: помнит только ПОСЛЕДНИЙ мод
Из-за этого getRunnableList() вызывался у одного мода. Остальные грузились,
их onLoad отрабатывал, задачи висели в очереди - и их никто никогда не
разбирал. Молча.
Чинится без обфускации: в мосте одна очередь на всех и один drain. Какой бы
special.Main игра ни позвала, он выполняет работу всех модов. Владелец задачи
запоминается в ThreadLocal в момент onLoad, поэтому в логе видно, чей код
упал. Счетчик падений тоже переехал с записи в очереди на саму задачу: очередь
чистится каждый тик, и счетчик на ней обнулялся бы раньше, чем дошел до
предела.
API:
- Rsx.Player и Rsx.World: getName/getX/getY/getZ/getHealth/getMaxHealth/
isAlive/isDead и getTime/isClient. Раньше player() отдавал Object, и мод
вытаскивал имя из toString() обфусцированного класса.
- События: Rsx.on("tick"|"worldChange"|"playerJoin"|"playerLeave"|
"screenOpen"|"screenClose"). Заменяют polling.
- Rsx.everyTicks(n) - периодичность по тикам, не плывет и не двоит после лага.
- RsxMod.apiVersion() и onError(): мод заявляет версию API и сам решает, что
делать с падением. Без этого сломанный мод заливал лог.
- Задача, упавшая 5 раз подряд, отбрасывается целиком.
- Rsx.error() и именованные every/post - чтобы в логе было видно, чё это.
Профиль перегенерирован: в него попали члены Entity (posX/posY/posZ/isDead,
getName/getEntityId/isEntityAlive), EntityLivingBase (здоровье) и World
(isRemote/getWorldTime), которых раньше не было. Сгенерированы по SRG-именам
через make_profile.py, поэтому переживут очередную реобфускацию.
Тест-стенд переписан под три мода и воспроизводит обе особенности вендорного
загрузчика: новый classloader на jar и перезаписываемое статическое поле.
Контрольный прогон с отключённым drain роняет 10 проверок из 17, так что
проверки действительно завязаны на этот механизм.
Заодно: сломанный фейковый загрузчик делил один classloader на все моды,
и такой тест не мог поймать проблему с изоляцией.
141 lines
6.1 KiB
PowerShell
141 lines
6.1 KiB
PowerShell
# Builds everything the installer needs, into dist\:
|
|
#
|
|
# dist\rsx-bridge.jar the agent (-javaagent), Java 8
|
|
# dist\rsx-api.jar API a mod compiles against
|
|
# dist\rsx-launcher-patch.jar launcher patcher with ASM inside
|
|
# dist\mod-template\ ready-to-edit mod project
|
|
#
|
|
# Only a JDK is required: ASM is vendored under third_party\asm so the build
|
|
# works offline and the patcher jar is self-contained.
|
|
|
|
$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'
|
|
$Asm = @(Get-ChildItem (Join-Path $Repo 'third_party\asm') -Filter '*.jar' |
|
|
ForEach-Object { $_.FullName })
|
|
|
|
function Say($text) { Write-Host " $text" }
|
|
|
|
if (-not (Get-Command javac -ErrorAction SilentlyContinue)) {
|
|
Write-Host ' Нужен JDK (javac) в PATH.' -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
if ($Asm.Count -lt 2) {
|
|
Write-Host ' Не найдены jar-ки ASM в third_party\asm.' -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
New-Item -ItemType Directory -Force -Path $Dist, $Out | Out-Null
|
|
|
|
# ---------------------------------------------------------------- agent
|
|
$agentSrc = Join-Path $Repo 'bridge\src\main\java'
|
|
$agentOut = Join-Path $Out 'agent'
|
|
Remove-Item $agentOut -Recurse -Force -ErrorAction SilentlyContinue
|
|
New-Item -ItemType Directory -Force -Path $agentOut | Out-Null
|
|
$agentFiles = Get-ChildItem $agentSrc -Recurse -Filter '*.java' | ForEach-Object { $_.FullName }
|
|
& javac -encoding UTF-8 --release 8 -nowarn -d $agentOut $agentFiles
|
|
if ($LASTEXITCODE -ne 0) { Write-Host ' Агент не собрался.' -ForegroundColor Red; exit 1 }
|
|
Say "агент скомпилирован ($($agentFiles.Count) файлов)"
|
|
|
|
$manifest = Join-Path $Out 'agent-manifest.txt'
|
|
@"
|
|
Manifest-Version: 1.0
|
|
Premain-Class: rsx.RsxAgent
|
|
Can-Redefine-Classes: false
|
|
Can-Retransform-Classes: true
|
|
|
|
"@ | Set-Content -LiteralPath $manifest -Encoding ASCII
|
|
|
|
Push-Location $agentOut
|
|
& jar cfm (Join-Path $Dist 'rsx-bridge.jar') $manifest -C $agentOut .
|
|
Pop-Location
|
|
Say "rsx-bridge.jar ($((Get-Item (Join-Path $Dist 'rsx-bridge.jar')).Length) байт)"
|
|
|
|
# ---------------------------------------------------------------- api
|
|
$apiOut = Join-Path $Out 'api'
|
|
Remove-Item $apiOut -Recurse -Force -ErrorAction SilentlyContinue
|
|
New-Item -ItemType Directory -Force -Path $apiOut | Out-Null
|
|
$apiFiles = Get-ChildItem (Join-Path $Repo 'bridge\src\main\java\net') -Recurse -Filter '*.java' |
|
|
ForEach-Object { $_.FullName }
|
|
# the API delegates to the agent internals, so they have to be on the
|
|
# classpath; only net/ classes end up in the published jar
|
|
& javac -encoding UTF-8 --release 8 -nowarn -cp $agentOut -d $apiOut $apiFiles
|
|
if ($LASTEXITCODE -ne 0) { Write-Host ' API не собрался.' -ForegroundColor Red; exit 1 }
|
|
Push-Location $apiOut
|
|
& jar cf (Join-Path $Dist 'rsx-api.jar') .
|
|
Pop-Location
|
|
Say "rsx-api.jar ($((Get-Item (Join-Path $Dist 'rsx-api.jar')).Length) байт)"
|
|
|
|
# ---------------------------------------------------------------- patcher
|
|
$patchOut = Join-Path $Out 'patcher'
|
|
Remove-Item $patchOut -Recurse -Force -ErrorAction SilentlyContinue
|
|
New-Item -ItemType Directory -Force -Path $patchOut | Out-Null
|
|
$patchSrc = Join-Path $Repo 'launcher-patch\src'
|
|
# Choice needs JavaFX, which only the launcher ships; it is compiled separately
|
|
# below with those jars on the classpath
|
|
$patchFiles = Get-ChildItem $patchSrc -Recurse -Filter '*.java' |
|
|
Where-Object { $_.Name -ne 'Choice.java' } |
|
|
ForEach-Object { $_.FullName }
|
|
& javac -encoding UTF-8 --release 8 -nowarn -cp ($Asm -join ';') -d $patchOut $patchFiles
|
|
if ($LASTEXITCODE -ne 0) { Write-Host ' Патчер не собрался.' -ForegroundColor Red; exit 1 }
|
|
|
|
# Choice talks to JavaFX, so the launcher's own runtime provides those classes
|
|
$javafx = Join-Path $env:APPDATA 'kjstudio\launcher\runtime\javafx\lib'
|
|
if (Test-Path $javafx) {
|
|
$fx = @(Get-ChildItem $javafx -Filter '*.jar' | ForEach-Object { $_.FullName })
|
|
& javac -encoding UTF-8 --release 8 -nowarn -cp ($fx -join ';') -d $patchOut `
|
|
(Join-Path $patchSrc 'rsx\Choice.java')
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Host ' Choice не собрался: нужны JavaFX-jar-ки лаунчера.' -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
} else {
|
|
Say 'ВНИМАНИЕ: JavaFX лаунчера не найден, Choice собран вслепую'
|
|
}
|
|
|
|
# shade ASM so the jar runs on any machine
|
|
$shade = $patchOut
|
|
foreach ($jar in $Asm) {
|
|
Push-Location $shade
|
|
& jar xf $jar
|
|
Pop-Location
|
|
}
|
|
Remove-Item (Join-Path $shade 'META-INF\MANIFEST.MF') -Force -ErrorAction SilentlyContinue
|
|
Remove-Item (Join-Path $shade 'META-INF\versions') -Recurse -Force -ErrorAction SilentlyContinue
|
|
|
|
$patchManifest = Join-Path $Out 'patcher-manifest.txt'
|
|
@"
|
|
Manifest-Version: 1.0
|
|
Main-Class: LauncherPatch
|
|
|
|
"@ | Set-Content -LiteralPath $patchManifest -Encoding ASCII
|
|
Push-Location $shade
|
|
& jar cfm (Join-Path $Dist 'rsx-launcher-patch.jar') $patchManifest -C $shade .
|
|
Pop-Location
|
|
Say "rsx-launcher-patch.jar ($((Get-Item (Join-Path $Dist 'rsx-launcher-patch.jar')).Length) байт)"
|
|
|
|
# ---------------------------------------------------------------- mod template
|
|
$tpl = Join-Path $Dist 'mod-template'
|
|
Remove-Item $tpl -Recurse -Force -ErrorAction SilentlyContinue
|
|
New-Item -ItemType Directory -Force -Path "$tpl\libs", "$tpl\src\main\java\com\example" | Out-Null
|
|
Copy-Item (Join-Path $Repo 'mod-template\build.gradle') $tpl -Force
|
|
Copy-Item (Join-Path $Repo 'mod-template\settings.gradle') $tpl -Force
|
|
Copy-Item (Join-Path $Repo 'mod-template\src\main\java\com\example\ExampleMod.java') `
|
|
"$tpl\src\main\java\com\example\" -Force
|
|
Copy-Item (Join-Path $Dist 'rsx-api.jar') "$tpl\libs\" -Force
|
|
@'
|
|
@echo off
|
|
rem Builds this mod and drops the jar into the client's mods folder.
|
|
cd /d "%~dp0"
|
|
call gradle jar
|
|
if errorlevel 1 exit /b 1
|
|
echo.
|
|
echo Скопируй build\libs\example-mod.jar в working_dir\mods
|
|
'@ | Set-Content -LiteralPath (Join-Path $tpl 'build.cmd') -Encoding ASCII
|
|
Say 'mod-template готов'
|
|
|
|
Write-Host ''
|
|
Write-Host ' Готово:' -ForegroundColor Green
|
|
Get-ChildItem $Dist -File | ForEach-Object { Write-Host " $($_.Name)" } |