# 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 install the runtime somewhere else # install.cmd --game 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. если лаунчер всё же предложит обновление - согласись, патч применится сам'