This repository has been archived on 2026-10-17. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
SashegDev 419b2d0a6b HUD: моды рисуют через собственный API игры
Хук в отрисовку компаса. Выбор не случайный: виджет рисуется только пока виден
HUD-сло�� игры, поэтому панель бесплатно наследует и видимость, и масштаб, и
состояние горячей клавиши, которая HUD прячет. Дублировать логику клавиш не
пришлось.

Бридж зашивает ASM и регистрирует ClassFileTransformer, который дописывает вызов
rsx.HudHook.render перед возвратом метода. Профиль диктует цель, обфусцированных
имён в коде нет. Трансформер отдаёт контекст как Object: так один дескриптор
работает при любом переименовании класса контекста.

Привязка к API отрисовки трёхуровневая, и каждая ступень умеет отвалиться
отдельно: точное совпадение по дескриптору, затем по имени и числу аргументов
(класс текстуры грузится позже, чем привязка), затем по runtime-классу
билдера, который мы держим в руках. Не нашлось - пишем в лог один раз и гасим
только эту возможность.

Текст рисуется ванильной текстурой textures/font/ascii.png: у игры нет
однострочного текстового API, а для цифр и меток атласа хватает. Своим TTF
конвейером мы не занимались.

Стенд учит подделки: батч, билдер, команду, реестр текстур, масштаб, синглтон
экрана и виджет-компас. Проверяет, что панель рисует плашку и глифы с верными
координатами и что замолкает, когда игра прячет HUD. С отключённым хуком падают
пять проверок из двадцати пяти.

Профиль: hook.hud, экран HUD, батч, реестр текстур, масштаб и сетевые методы
NetHandlerPlayClient. У каждой строки - рецепт поиска после реобфускации.
2026-10-17 03:29:00 +03:00

191 lines
8.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 -cp ($Asm -join ';') -d $agentOut $agentFiles
if ($LASTEXITCODE -ne 0) { Write-Host ' Агент не собрался.' -ForegroundColor Red; exit 1 }
Say "агент скомпилирован ($($agentFiles.Count) файлов)"
# The agent rewrites bytecode at runtime to give mods the game's own HUD, so it
# carries ASM inside rather than making every install provide it.
foreach ($jar in $Asm) {
Push-Location $agentOut
& jar xf $jar
Pop-Location
}
Get-ChildItem (Join-Path $agentOut 'META-INF') -Recurse -ErrorAction SilentlyContinue |
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item (Join-Path $agentOut 'module-info.class') -Force -ErrorAction SilentlyContinue
Say 'ASM зашит в агент'
$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 the mod and drops the jar into the client's mods folder.
rem Only a JDK is needed: no Gradle, no network, nothing but javac and jar.
setlocal
cd /d "%~dp0"
if not exist "build\classes" mkdir "build\classes"
if not exist "build\mods" mkdir "build\mods"
set ENTRY=com.example.ExampleMod
rem where the modloader looks for mods; override with set MODS_DIR=...
if "%MODS_DIR%"=="" set MODS_DIR=%LOCALAPPDATA%\rsx-mod\mods
echo [1/3] compiling...
dir /s /b src\main\java\*.java > build\sources.txt
javac -encoding UTF-8 --release 8 -nowarn -cp libs\rsx-api.jar -d build\classes @build\sources.txt
if errorlevel 1 (
echo.
echo Сборка не удалась.
exit /b 1
)
echo [2/3] packaging example-mod.jar...
if exist "build\mods\example-mod.jar" del "build\mods\example-mod.jar"
jar cfm "build\mods\example-mod.jar" manifest.txt -C build/classes .
if errorlevel 1 exit /b 1
echo [3/3] installing...
if not exist "%MODS_DIR%" (
echo.
echo Папка модов не найдена: %MODS_DIR%
echo Скопируй build\mods\example-mod.jar в неё сам.
exit /b 0
)
copy /y "build\mods\example-mod.jar" "%MODS_DIR%\" >nul
echo.
echo Готово: build\mods\example-mod.jar скопирован в %MODS_DIR%
'@ | Set-Content -LiteralPath (Join-Path $tpl 'build.cmd') -Encoding ASCII
# the manifest carries the entry point the bridge looks for
$entry = (Select-String -LiteralPath "$tpl\src\main\java\com\example\ExampleMod.java" `
-Pattern '^package\s+([\w.]+)' | ForEach-Object { $_.Matches[0].Groups[1].Value })
"Manifest-Version: 1.0`r`nRsx-Mod-Entrypoint: $entry.ExampleMod`r`n`r`n" |
Set-Content -LiteralPath (Join-Path $tpl 'manifest.txt') -Encoding ASCII
Say 'mod-template готов'
Write-Host ''
Write-Host ' Готово:' -ForegroundColor Green
Get-ChildItem $Dist -File | ForEach-Object { Write-Host " $($_.Name)" }