Skip to content

fix(builder): Linux Play leaves the game its hookshot_log.txt again - #2279

Merged
hckrman101 merged 1 commit into
NotRexed:mainfrom
mooki000:builder/play-keeps-log
Nov 11, 2026
Merged

hckrman101 merged 1 commit into
NotRexed:mainfrom
mooki000:builder/play-keeps-log

Conversation

@mooki000

@mooki000 mooki000 commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Since #2024, a Linux player who starts the game with Play (in the Builder after a build, or in the launcher that the Hookshot menu entry and hookshot in the game folder open) gets no new hookshot_log.txt: the game writes its log into #2024's capture instead, which keeps the first 64 KiB in hookshot_startup.log once the game has closed and throws the rest away. The log every issue asks for is missing, or an older run's log is still there and gets sent in its place.

Fixes #2330.

Why it happened

play-diagnostics.sh runs the game as "$1" 2>&1 >/dev/null | head -c 65536 ..., so the game's stderr is a pipe. The game opens hookshot_log.txt only when its stderr is nowhere to log (host/hos_main.c: GetFileType(err) == FILE_TYPE_UNKNOWN), and the POSIX shim answers FILE_TYPE_PIPE for a pipe (host/posix/win32_posix.c GetFileType): to the game, a pipe means someone reads its output, as with ./bin/hookshot | tee. Before #2024, Play gave the game /dev/null, which counts as nowhere, so it wrote its log.

What changed

  • builder/linux/hookshot_builder.py play(): the game's stderr is one end of a socket.socketpair(), and the script reads the other end on its stdin. The shim counts a socket as nowhere to log (it says so for the journal's socket a desktop launcher gives), so the game writes hookshot_log.txt and keeps hookshot_log.1.txt to .4.txt, as before fix(builder): retain bounded Linux Play startup diagnostics #2024.
  • builder/linux/play-diagnostics.sh: reads the socket instead of a pipe. What reaches it is what the game prints before its log opens. On Linux the display start comes first, so display: SDL_Init: ...: no window and the loader's errors land there, along with the udmabuf warning the game already mirrors there. hookshot_startup.log keeps that, the 64 KiB cap and the exit status, as fix(builder): retain bounded Linux Play startup diagnostics #2024 made it. The game's stdin is now /dev/null instead of the Builder's. If the script cannot make its temporary folder, the game gets /dev/null for stderr, as before fix(builder): retain bounded Linux Play startup diagnostics #2024.
  • Nothing changes in the game, so this works with game folders already built. Hookshot.AppImage --play (hookshot-launch) never went through the script and is unchanged.

Why a socket and not a change in the game (a marker that makes it log even with a launcher's pipe): every game folder would need a new build, and a folder built before the change, started from a newer Builder, would still have no log. The script also cannot know in advance whether the game will fail early, so it cannot pick between the two set-ups.

How it was tested

  • tests/test_linux_play_diagnostics.py keeps fix(builder): retain bounded Linux Play startup diagnostics #2024's checks and adds one. Play starts a small game compiled with the shim's own GetFileType (cut from host/posix/win32_posix.c). It prints a line, opens hookshot_log.txt only when GetFileType(stderr) is FILE_TYPE_UNKNOWN as host/hos_main.c does (the test checks that this condition is still there), and logs 16,384 lines, more than 64 KiB. On main the check fails (no hookshot_log.txt). With this change the whole log is in hookshot_log.txt, and hookshot_startup.log holds the first line and the exit status. It was also run with dash 0.5.12 as the shell (Debian's and Ubuntu's /bin/sh): it passes, and fails on main the same way.

  • python -I -B tests/run_all.py on this branch as opened (77a9112, on main 49f75dc): 362 passed, 2 failed, 10 skipped. The 2 failures also fail on main on this machine, for reasons outside this change: tests/test_updater_asset_digest.py (its fixture needs the .NET 8 SDK, and only .NET 10 is installed) and tests/test_wayland_libdecor_hint.py (a clang 23 warning under -Werror).

  • The real game: a build of main (bdfe09c, with the same log set-up as main today) on an RTX 4090 with NVIDIA 615.78.08 on Arch Linux. The folder was laid out as the Builder lays it out (bin/hookshot), and each start went through the Builder's own play(), imported from hookshot_builder.py of each revision. The runs were headless and muted, and the game was ended with SIGTERM after 30 s (as closing it would):

    Play of game's stderr hookshot_log.txt hookshot_startup.log
    before fix(builder): retain bounded Linux Play startup diagnostics #2024 (2009c5f) /dev/null written: 71,757 bytes, 643 lines (none)
    main a pipe not written: the 71,757-byte log of the run before stays 65,569 bytes: the log's first 65,536 bytes (from the version line, cut mid-line after the vk: 600 presents report) and Hookshot exited with status 143
    this branch a socket, then the game's log file written: 77,184 bytes, 682 lines, to vi: 1037 frames presented; the run before's log moved to hookshot_log.1.txt 33 bytes: Hookshot exited with status 143
  • A start that fails before the log opens (no display reachable, so SDL's video start fails in main before the log is set up): main and this branch leave the same 175-byte hookshot_startup.log (display: SDL_Init: wayland,x11 not available: no window and Hookshot exited with status 1), and no hookshot_log.txt. What fix(builder): retain bounded Linux Play startup diagnostics #2024 added for this case is kept.

  • A start that fails after the log has opened: an empty image.bin, which play() still starts. The game logs its first lines, then stops with image.bin: 0 bytes, the lifted code expects 0x6020000: lift and image do not match, exit 1. With main's Play these three lines and the exit status are in hookshot_startup.log, and no hookshot_log.txt is written. With this change they are in hookshot_log.txt (274 bytes, the message is its last line), and hookshot_startup.log holds only Hookshot exited with status 1.

Not tested

  • The Builder's window itself (a click on Play). The test and the runs call the same play() the button calls.
  • The AppImage. The script is carried as before (no new file), and socket is part of every CPython, including the AppImage's python-build-standalone 3.12 (the version these runs used).
  • A start with a window on a desktop session: the runs were headless. The failure run covers the part before the window.
  • Distributions other than Arch. dash was only a stand-in shell for the test.

#1958 (profiles) changes two lines of the same test file. Whichever merges second needs a small rebase there.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com

Since NotRexed#2024 the Builder's Play (and the launcher's, which the menu entry
opens) ran the game with a pipe on stderr. The game takes a pipe for a
reader (host/posix/win32_posix.c GetFileType: FILE_TYPE_PIPE) and then
opens no hookshot_log.txt: a player who pressed Play had no log, only the
first 64 KiB of it in hookshot_startup.log once the game had closed.

Play now gives the game one end of a socket for stderr and the script
reads the other. The game takes a socket for nowhere to log, as it takes
the journal's under a desktop launcher, and writes its hookshot_log.txt
as before NotRexed#2024; hookshot_startup.log keeps what it prints before that
log opens (an SDL or loader failure) and its exit status.

The test runs Play with a game that decides where to log by the shim's
own GetFileType; it fails on main (no hookshot_log.txt).

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mooki000

Copy link
Copy Markdown
Contributor Author

Thanks for the careful read. About the workflows: GitHub now starts them for my pushes without an approval (it did for #2380). Main's game-free tests fail until #2380 is in, so I'll rebase this onto main right after that merge and push; the Linux AppImage, Windows builder and game-free runs should then show here on their own.

@hckrman101
hckrman101 merged commit 1e02fdb into NotRexed:main Nov 11, 2026
3 checks passed
LNDF added a commit to LNDF/Hookshot that referenced this pull request Nov 11, 2026
Conflicts resolved:
- MKWVK_API_VERSION 18: upstream's version 17 is prompts_live (NotRexed#2366); the
  data folder's MkwVkHost.data_dir follows it as version 18.
- host/hos_update.c: "Skip this version" stays in the data folder; upstream's
  call order (NotRexed#2283, the check after the saves' place is known) is kept, with
  hos_data_init having chosen that place first. tests/test_linux_update_skip.py
  builds the start with hos_data.c instead of the removed saves_dir.
- host/prompts.c: upstream's live prompts and mod size tables (NotRexed#2366, NotRexed#2270),
  with the generated files (and the new rsizetable.mod.zs) in cache/prompts;
  made_dir makes cache/ too.
- builder/linux/play-diagnostics.sh: upstream's socket stderr (NotRexed#2279), with
  hookshot_startup.log in logs/.
- The program cache moved to vk_progcache.cpp (NotRexed#2323): its path is cache/.
- hookshot_driver_faults.txt (NotRexed#1823) goes to cache/ and is moved there.
- env_on/env_value_off (NotRexed#2042), env_override (vk_menu.cpp), the prepare
  watchdog (vk_main.cpp), audio and docs merged with the data folder paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linux: starting the game with Play leaves no new hookshot_log.txt

1 participant