`launcher.cli update` downloaded the release and switched to it without asking whether the game ran. The new version's continue-update then failed in its rebuild with "the game is running", which left the new launcher with the old build until Rebuild the Game. The window's App.start_update refuses first; the command now does the same before its update check, and ends with exit code 1 and "error: the game is running: close it first, the update rebuilds it".
Not reproduced before the change: found by reading the code. Checks that passed: tests/launcher_test.py (217 tests, a new one in which a running game stops `update` before its check and its download, which fails before the change) and tools/check_content.py --staged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The worker, the thread that reads the build's output and the window all change a job while the window draws it. A
step could be read with a new state and the value before it, the output's lines could change while they were
copied, and the change counter was incremented without a lock although the window learns from it that the job
ended. Every change and the counter now take the job's lock, and the job screen and the command line copy the steps
under it.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The window already locked data/launcher.lock, but a second one closed without a word, the command line did not take
the lock, and nothing kept two jobs apart. The lock is now launcher/locks.py: the window, every command but check and
every job that changes files hold it, the one refused says who holds it, and a job names what it changes so that a
second one on the same thing fails before its first step.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
An update whose lift ran again drops the compile step's record before the
compile runs, so a compile that then stopped left state.json without it,
build.built_version said '' and the launcher's next start showed the
first-run screen and asked for the dump. build.installed now also counts a
folder that holds the exe of the build before and game files a rebuild can
start from: the window opens on home, where Rebuild the Game reads "Not
Finished" and Play says which build it starts, and an update's rebuild and
the command line's go on with the game. Begin Installation into a folder
whose game files are already unpacked builds on from them instead of
reading the dump again.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
NX2navi-Setup.exe installs the launcher into %LOCALAPPDATA%\Programs\NX2navi without administrator rights, with
shortcuts and an Apps entry. The launcher finds the player's .nup, downloads the build tools itself (the MSYS2 UCRT64
packages toolchain.lock.json pins, checked by sha256 and unpacked without pacman, and zbic from PyPI) and runs
tools/make_game.py, whose steps it shows with their progress. Its window follows the game's options menu: colours
sampled from it, the menu's filigree, the white glowing cursor, the Triforce in the title band and as the programs'
icon, and all text in the game's Cetra Display, decoded from the player's own dump. Mouse, keyboard and an XInput
pad drive it.
Updates come from GitHub releases (NX2navi-update.json): the new version is downloaded, checked and staged beside
the running one, the stub restarts into it and falls back to the previous version when it fails to start, and the
game is rebuilt without the dump. make_game.py now keys its exefs and romfs steps on the game files alone, has
--rebuild and --events, compiles only the files whose source, headers, flags or compiler changed, and replaces
ocarina.exe only once the new one links. tools/make_launcher.py builds the release on Windows or Linux.
Tested: tests/launcher_test.py (19 tests), the real toolchain download and install, and under Wine 11 the setup,
the stub, the window on the bundled Windows Python, an update from a local feed by clicking Updates, the rollback
of a version that fails to start, and the downloaded clang compiling and linking a program against the game's
libraries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged onto the current port: make_game.py keeps the launcher's step keys, cached link and in-place swap, and its
own runtime manifest (windres), AMD's FidelityFX library and the shader preparation, which becomes a step of its own
("shaders") that the launcher shows in its install, rebuild and update jobs. make_launcher.py ships the shader tools
and, with --ffx, AMD's library.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>