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 88b90802a7 Агент ищет свой дом, а не доверяет месту jar
Путь к агенту в конфиге лаунчера переписывает сам лаунчер, и однажды он
потерял целый сегмент пути: осталось ...\rsx-mod\rsx-bridge.jar вместо
...\rsx-mod\bridge\rsx-bridge.jar. JVM не смог загрузить агент, и игра падала с
error2 при входе в моды.

Две правки:
- агент кладём в корень модлоадера, а не в bridge\: тогда его каталог и есть
  дом, и mods\, libs\ и config\ находятся без угадывания;
- в env пишем слеши вперёд. Лаунчер переписывает конфиг своим properties-писателем
  и портит именно обратные слеши, а у прямого слеша экранировать нечего.

Заодно premain теперь поднимается вверх, пока не найдёт каталог с mods\ или
libs\, вместо того чтобы считать родителем jar единственно верным ответом.

И ещё: лаунчер вместе с env потерял gamePath1, установщик теперь проверяет и
восстанавливает его.

Заодно выяснилось, что апдейтер всё же чинит app.jar ("Final repair: app.jar") -
наш фильтр не сработал, потому что маркер с базовым хешем к тому моменту не
совпадал. Сейчас это лечится автоматически, но в логах видно.
2026-10-17 09:30:09 +03:00

240 lines
10 KiB
PowerShell

# Installs the modloader: copies the runtime files, patches the launcher and
# wires the agent into the launcher config.
#
# install.cmd normal install
# install.cmd --uninstall put everything back
# install.cmd --home <path> install the runtime somewhere else
# install.cmd --game <path> point at a specific game installation
$ErrorActionPreference = 'Stop'
$Repo = Split-Path -Parent $PSScriptRoot
if (-not $Repo) { $Repo = $PSCommandPath | Split-Path -Parent | Split-Path -Parent }
$LauncherHome = Join-Path $env:APPDATA 'kjstudio\launcher'
$Config = Join-Path $LauncherHome 'launcher_config.properties'
$AppJar = Join-Path $LauncherHome 'app.jar'
$Marker = Join-Path $LauncherHome '.rsx-patched'
$Dist = Join-Path $Repo 'dist'
$ModHome = Join-Path $env:LOCALAPPDATA 'rsx-mod'
$GamePath = $null
$Uninstall = $false
for ($i = 0; $i -lt $args.Count; $i++) {
switch ($args[$i]) {
'--uninstall' { $Uninstall = $true }
'--home' { $ModHome = $args[++$i] }
'--game' { $GamePath = $args[++$i] }
default { }
}
}
function Say($text, $color) {
if ($color) { Write-Host " $text" -ForegroundColor $color } else { Write-Host " $text" }
}
function Die($text) { Say $text Red; exit 1 }
# --------------------------------------------------------------- launcher must be closed
function Get-LauncherRunning {
Get-Process -ErrorAction SilentlyContinue | Where-Object {
$_.MainWindowTitle -like '*Koptojop*' -or
($_.Path -and $_.Path -like "$LauncherHome*")
}
}
$running = Get-LauncherRunning
if ($running) {
Die 'Лаунчер запущен - закрой его (крестик) и запусти установщик снова.'
}
# --------------------------------------------------------------- java and patcher
$FindPatcher = Join-Path $Dist 'rsx-launcher-patch.jar'
function Find-Java {
$candidates = @(
(Join-Path $LauncherHome 'runtime\java\bin\java.exe')
)
foreach ($c in $candidates) {
if (Test-Path $c) { return $c }
}
$cmd = Get-Command java -ErrorAction SilentlyContinue
if ($cmd) { return $cmd.Source }
return $null
}
# --------------------------------------------------------------- read the config safely
# Handing the config to java.util.Properties is the only safe way to read it: it
# is a properties file, where a backslash escapes the next character.
function Read-GamePath($java, $path, $patcher) {
if (-not (Test-Path $path)) { return $null }
$prev = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
$raw = & $java -jar $patcher getprop $path gamePath1 2>$null
$ErrorActionPreference = $prev
if ($raw) {
$value = ($raw | Select-Object -First 1) -replace '^\s+', ''
if ($value) { return $value }
}
return $null
}
if (-not (Test-Path $Config)) {
Die "Не найден конфиг лаунчера: $Config"
}
$game = Read-GamePath (Find-Java) $Config $FindPatcher
if ($GamePath) { $game = $GamePath }
# --------------------------------------------------------------- uninstall
if ($Uninstall) {
Say 'Удаляю модлоадер...'
$backup = "$AppJar.orig"
if (Test-Path $backup) {
Copy-Item $backup $AppJar -Force
Say 'app.jar восстановлен из резервной копии'
} else {
Say 'резервной копии нет, лаунчер не трогаю'
}
$ErrorActionPreference = 'Continue'
& $java -jar $FindPatcher setenv $Config '' 2>&1 |
ForEach-Object { Say $_ }
$ErrorActionPreference = 'Stop'
Say 'env убран из конфига лаунчера'
Remove-Item $Marker -Force -ErrorAction SilentlyContinue
Say 'маркер удалён'
# the updater has to go back to stock, otherwise it keeps skipping app.jar
# downloads for a launcher that is no longer patched
$updaterExe = Join-Path $env:APPDATA 'kjstudio\updater\updater.exe'
$updaterBackup = "$updaterExe.orig"
if (Test-Path $updaterBackup) {
Copy-Item $updaterBackup $updaterExe -Force
Remove-Item $updaterBackup -Force
Say 'updater.exe восстановлен'
}
Remove-Item (Join-Path $env:APPDATA 'kjstudio\updater\rsx-launcher-patch.jar') -Force -ErrorAction SilentlyContinue
Say 'Готово. Лаунчер снова запускается как обычно.' Green
exit 0
}
# --------------------------------------------------------------- checks
if (-not (Test-Path $Dist)) { Die "Нет каталога dist: $Dist (собери через build.cmd)" }
foreach ($f in 'rsx-bridge.jar', 'rsx-launcher-patch.jar', 'rsx-api.jar') {
if (-not (Test-Path (Join-Path $Dist $f))) { Die "Нет dist\$f - запусти build.cmd" }
}
$java = Find-Java
if (-not $java) { Die 'Не найден java (нужен лаунчерный runtime или java в PATH)' }
# a clean base to patch: re-patching an already patched jar is a no-op
if (Test-Path "$AppJar.orig") {
Copy-Item "$AppJar.orig" $AppJar -Force
Say 'восстановлен оригинальный app.jar перед патчем'
}
Say "Лаунчер: $LauncherHome"
Say "Игра: $(if ($game) { $game } else { 'не указан в gamePath1' })"
Say "Модлоадер: $ModHome"
Say ''
# --------------------------------------------------------------- copy the runtime
New-Item -ItemType Directory -Force -Path (Join-Path $ModHome 'profiles') | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $ModHome 'mods') | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $ModHome 'libs') | Out-Null
# The agent sits in the home itself, not in a subfolder: then its own directory
# IS the home, and mods\, libs\ and config\ resolve without any guessing.
Copy-Item (Join-Path $Dist 'rsx-bridge.jar') $ModHome -Force
Say 'скопирован rsx-bridge.jar'
$existingProfile = Join-Path $ModHome 'profiles\latest.rsxp'
$repoProfile = Join-Path $Repo 'profiles\latest.rsxp'
$oldProfile = 'G:\rsx-mod\bridge\profiles\latest.rsxp'
if (Test-Path $existingProfile) {
Say 'профиль уже на месте, оставляю как есть'
} elseif (Test-Path $repoProfile) {
# the profile that ships with this package: the mapping for the vendor build
Copy-Item $repoProfile (Split-Path $existingProfile) -Force
Say 'установлен профиль сборки игры из пакета (latest.rsxp)'
} elseif (Test-Path $oldProfile) {
Copy-Item $oldProfile (Split-Path $existingProfile) -Force
Say 'перенесён профиль сборки игры (latest.rsxp)'
} else {
Say 'профиля нет - моды грузиться не будут, нужен latest.rspx для этой сборки', Yellow
}
# --------------------------------------------------------------- patch the launcher
Say ''
Say 'Патчу лаунчер...'
$patcher = Join-Path $Dist 'rsx-launcher-patch.jar'
$meta = "wa`nALLATORIxDEMO"
# native stderr must not be treated as a PowerShell error here
$prev = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
$out = & $java -jar $patcher patch $AppJar $ModHome $meta 2>&1
$code = $LASTEXITCODE
$ErrorActionPreference = $prev
$out | ForEach-Object { Say $_ }
if ($code -ne 0) {
Die "Патч не удался (код $code). Если лаунчер запущен с правами администратора - закрой его и повтори."
}
# the patcher writes .rsx-patched itself: it needs baseSha256 (the hash of the
# untouched jar) so the updater hook can tell our patch from a real update
if (-not (Test-Path -LiteralPath $Marker)) {
Set-Content -LiteralPath $Marker -Value 'baseSha256=unknown' -Encoding ASCII
Say 'маркер .rsx-patched создан'
} else {
Say 'маркер .rsx-patched обновлён патчером'
}
# --------------------------------------------------------------- wire the agent
# Forward slashes on purpose. The launcher rewrites this config through a
# properties writer that mangles Windows paths - it dropped a whole path segment
# from ours once - and a forward slash has nothing to escape. The JVM takes this
# form happily on Windows.
$agentArg = '-javaagent:"' + ($ModHome -replace '\\', '/') + '/rsx-bridge.jar"'
$ErrorActionPreference = 'Continue'
$out = & $java -jar $patcher setenv $Config $agentArg 2>&1
$code = $LASTEXITCODE
$ErrorActionPreference = $prev
$out | ForEach-Object { Say $_ }
if ($code -ne 0) {
Die "Не удалось записать env в конфиг лаунчера (код $code)"
}
Say ''
Say 'Проверяю, что путь читается как надо...'
$ErrorActionPreference = 'Continue'
$check = & $java -jar $patcher showenv $Config 2>&1
$ErrorActionPreference = $prev
$check | ForEach-Object { Say $_ }
# --------------------------------------------------------------- patch the updater
# The stock updater re-downloads app.jar, which would wipe the patch. We hook its
# embedded jar so it keeps our app.jar and re-patches after a real update.
$UpdaterHome = Join-Path $env:APPDATA 'kjstudio\updater'
$UpdaterExe = Join-Path $UpdaterHome 'updater.exe'
Say ''
Say 'Патчу апдейтер...'
if (-not (Test-Path -LiteralPath $UpdaterExe)) {
Say " не нашёл $UpdaterExe - пропускаю (лаунчер сам предложит обновление)"
} else {
Copy-Item -LiteralPath $patcher -Destination (Join-Path $UpdaterHome 'rsx-launcher-patch.jar') -Force
$ErrorActionPreference = 'Continue'
$out = & $java -jar $patcher patchupdater $UpdaterExe $ModHome 2>&1
$code = $LASTEXITCODE
$ErrorActionPreference = $prev
$out | ForEach-Object { Say $_ }
if ($code -ne 0) {
Die "Не удалось пропатчить updater.exe (код $code). Запускай лаунчер через start-launcher.cmd"
}
if (-not (Test-Path -LiteralPath (Join-Path $UpdaterHome 'updater.exe.orig'))) {
Die 'Резервная копия updater.exe не создана - откатываю изменение'
}
}
Say ''
Say 'Готово.' Green
Say ''
Say 'Дальше:'
Say ' 1. запускай лаунчер обычным ярлыком - обновления больше не сотрут патч'
Say ' 2. жми «Играть» - появится выбор «С модами / Ванилла»'
Say " 3. моды клади в $ModHome\mods"
Say ' 4. если лаунчер всё же предложит обновление - согласись, патч применится сам'