WIP: feat(coop): add consent-based player presence and isolated save playtests #355
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!355
Loading…
Reference in a new issue
No description provided.
Delete branch "mooki/Longshot:coop-playtest-r9"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
Friends should be able to see one another, agree on what their room shares, and try co-op without risking their usual saves. This is the whole experimental playtest line above #68, kept together so testers can use one matching build.
Work in progress. The tested head is
2f01e1b4bdb6e1629c54a998f0403519fe4e5c74(r12), Linux source05329d46. The pull request's branch is still namedcoop-playtest-r9; that name is historical. Build and report the commit, not the branch suffix.The base is unchanged from r10: pull request 68 at
4a9b96060ad71a3b48e05ac0269fc7e0e1a4a626, on maincf605c85bbaeaaa78176b933b3bc1a3366e13a78. The 80 line commits and three r10 fixture/documentation repairs remain, followed by the three commits below. Main is still three commits later at0caf06184b391a5259d3741bb006ef5826eb6417: documentation, Windows lift workers and button prompts. This is not current main. The text-field parent remains #51.What is measured on r12: the whole game-free suite and all six short two-game Linux proofs, each passing at its first attempt with both complete logs checked for unexpected body removal or recreation. Linux builds and runs; the Windows executable and renderer cross-build and link. Not run on r12: Windows, two machines or a 30-minute session. Earlier LAN, soak and native Windows results retain their actual commits below, including the failed r10 continuity result and a separate open Windows body-disappearance report. This build cannot start a fight.
What changed
Pull request 68 already supplies the relay, sessions, signed progress, backups and the Co-op tab. Its work is a dependency, not new work to review twice. Above that pinned parent, the same pieces remain integrated in this order:
host/coop_state.c,host/coop_proto.c.host/coop_menu.c,gpu/vk/vk_menu.cpp.host/coop_gameplay.h,host/oot_coop.c.host/oot_coop_players.c,nx2/coop_players_assets.py.host/coop_profile.c,host/nn_fs.c,gpu/vk/vk_menu.cpp.gpu/vk/vk_menu.cpp.host/coop_save.c,host/coop_save_runtime.c,host/coop_profile.c.host/oot_coop_hits.c.host/oot_coop_hits.c.host/coop.c,host/oot_coop_players.c.host/oot_coop_players.c,nx2/coop_players_assets.py.host/oot_coop_players.c,gpu/vk/vk_menu.cpp.host/oot_coop_warp.c.host/coop_chat.c,host/coop_chat_runtime.c,gpu/vk/vk_menu.cpp.host/oot_coop_kit.c.host/coop_duel.c,host/coop_duel_menu.c.host/oot_coop.c,host/coop_menu.c.coop: stats:line now names reliable retries and events given up. Deliberate cancellation contributes to given-up counts; they are not a packet-loss percentage.host/coop.c.tests/test_coop_save_copy.py.host/coop_profile.c,tests/test_coop_export_path.py.No shared enemy/boss simulation, playable PvP, arbitrary travel, save-owner picker, progress return from a copy, shared clock, invite service or mod-loading interface is included. Models and animations are generated from each player's own game; none is distributed with the source or sent over the network.
Earlier r10 replay (
1ed75e61): at that head the co-op core was byte-identical to r9: protocol, signed consent, progress/presence/download separation, save profiles and transfers, body adapters, chat, travel, kits and trace queues have no new behavior. Shared files retain the newer base's input toggles, audio/Intel menu rows, sampled game state, filesystem handling andlogs//cache/layout. The observer hooks are composed with the base's full pre-clamp damage-policy argument; the actual game-code translation was regenerated and both products rebuilt, rather than relabelling an r9 binary.The three fixture/documentation commits that completed r10 were:
7f4dcaf4,tests/test_menu_feature_rules.py: provide the newer menu parser's Plus/Minus exclusions, keeping every rule-registry and saved-key assertion.44c6f99a,tests/test_menu_text_field.py: combine the real pad and text-release callback declarations and retain the correct input-polling sections beside chat, without losing text, repeat, toggle or restart checks.1ed75e61,tests/test_oot_coop_hits.py: use the base's three-argument damage-policy interface and add overkill checks for both native update paths, retaining the original request, scaled committed life and transmitted result checks.Those three fixture commits changed no production code. The two presence fixes below do; no permission, filesystem refusal or test assertion was removed to obtain the results.
Three commits after r10:
2be4e92335a722468631d19d7fac52e12f970d5e,fix(coop): preserve presence across unchanged roster heartbeats: an identical periodic roster is no longer published as a changed room view. A change of view could briefly close otherwise valid presentation permission before its consumer caught up. Real member, session and rule changes still revoke old permission; no grace period or consent bypass was added.15a68a5ebaafc7638e6dfa7fb6e6de969c8a16e0,docs(coop): list the line's switches in the switch reference: adds the 25 missing direct switch entries and completes automatic-acceptance documentation, with defaults, who sets them, and test/development scope. It changes onlydocs/development.mdand the README's Teleport note.2f01e1b4bdb6e1629c54a998f0403519fe4e5c74,fix(coop): apply joined views after the native engine starts: a joined room view stays pending until it has actually been applied to an existing native engine. The next ordinary session update can consume it without waiting for another roster publication.Why both fixes are needed: the r10 30-minute session at
1ed75e61contained an unrequested hide/delete/recreate in one game despite the room remaining accepted. Investigation reproduced the unchanged-roster publication race in both r9 and r10; it is consistent with that interruption, although the original log did not record the exact cross-thread version pair. The intermediate head15a68a5e, containing only the first fix and the documentation, then failed its first two-game proof before signed-room readiness and was never pushed. It exposed a second fault: the initial view could be marked handled before the native engine existed, and the old repeated heartbeat had accidentally retried it. The second fix retains that view until applied, while keeping the unchanged-heartbeat repair.Two game-free regressions cover the distinct boundaries:
tests/test_coop_roster_presence.pyforces the publication/consumption race and preserves revocation for real changes;tests/test_coop_join_publication.pydrives a real join, signed identity/charter exchange and explicit acceptance with engine creation and callback registration in different orders. Both pass with GCC and Clang on the corrected code. The join test compiles and fails on the intermediate head with both compilers. The first race test began with an existing engine and accepted room, so its success alone had not established startup joining.Flags and settings
Defaults below are the unchanged co-op defaults of r12, not a recommendation to enable every feature. Signed defaults initialize a maker's draft; an absent permission is still off. New terms require fresh acceptance. All co-op remains opt-in, and all participants need the same compatible build. The Untested on column describes this r12 head; the separately labelled r9/r10 LAN, soak and Windows evidence below does not turn those into r12 results.
The inherited connection settings are
coop/HOS_COOP(off),coop_host/HOS_COOP_HOST(off),coop_server/HOS_COOP_SERVER(unset; relay port 24680),coop_port/HOS_COOP_PORT(0, a free client port), andcoop_name/HOS_COOP_NAME(a generated player name). Make/Join enables the session for that run. Only the maker enables hosting; Join never hosts. No router or firewall is changed automatically.The inherited ordinary-save sharing defaults are Shared for
coop_progress,coop_items,coop_songsandcoop_quest; Pool forcoop_keys; Own forcoop_rupeesandcoop_ammo. They apply only after ordinary consent and a completed backup. A native temporary copy never shares this progress, even if its room displays Shared. For a first playtest, use disposable save roots and Own categories.The seven category environment variables below are inputs to the maker's signed room rules, not separate permission to write a peer's game. With Shared progress, Items/Songs/Quest are normalized to Shared; with Own progress, Keys is normalized to Own. An unset or empty category override uses the saved key, then the stated fallback. Changing the accepted rules needs fresh consent. Test-only switches are not recommended player settings, and the two debug Teleport overrides already exist in the base; Go to friend merely adds a refusal when they are set. These rows document source behavior, not new runtime coverage.
HOS_COOP_PROGRESS,HOS_COOP_ITEMS,HOS_COOP_SONGS,HOS_COOP_QUESTsharedorownfor progress, items, songs and quest items respectively; a nonempty environment value overrides the correspondingcoop_*key in the maker's draftsharedfor eachHOS_COOP_KEYSpoolorownfor dungeon small keys, overcoop_keyspool, normalized toownwhen progress is OwnHOS_COOP_RUPEES,HOS_COOP_AMMOpoolorownfor rupees and ammunition respectively, overcoop_rupeesandcoop_ammoownfor bothHOS_COOP_AUTOcreateorjoinrequests a room at startup while co-op is enabled, then recreates or waits for that room after relay loss; it is not acceptanceoff: no automatic requestHOS_COOP_ROOMHOS_COOP_ACCEPT=alwaysalways: manual acceptanceHOS_COOP_DROPHOS_COOP_DEMOHOS_COOP_PROBEHOS_COOP_TEST_SEThash=value@frame, up to 8 entries; these writes can become saved progressHOS_OOT_TELEPORT_EVENTHOS_OOT_TELEPORT_STARTER0omits Link as its starter; other values retain him. Go to friend refuses any nonempty override0or1makes Go to friend refuse; no network-selected starter or new permissioncoop_room_mode/ Room rules: Modecoop_presence_permissions/ Friend informationcoop_players/HOS_COOP_PLAYERSHOS_COOP_PLAYERS_ANIMHOS_COOP_PLAYERS_ACTOR,HOS_COOP_PLAYERS_TESTcoop_names/ Friend namescoop_labels/ Friend labelsHOS_COOP_PROFILE_TICKETcoop_saveactions: status, identity, offer, accept, cancelcoop_warp/ Go to friendcoop_social_permissions/ Room chatkey_chatcoop_notices/ Noticescoop_hit_events/ Native hit observationsHOS_COOP_HITS_TRACEHOS_COOP_ARENA_FILEcoop_loadout_version,coop_loadout_presetcoop_loadout_age,coop_loadout_bits_0coop_loadout_bits_1through_7coop_loadout_magic,coop_loadout_seeds,coop_loadout_arrows,coop_loadout_bombs,coop_loadout_bombchus,coop_loadout_nuts,coop_loadout_sticks,coop_loadout_magic_startcoop_loadout_bottles,coop_loadout_bottle_0through_4coop_combat_mode,coop_damage_modelcoop_pvp_damage,coop_pvp_items,coop_pvp_grace,coop_pvp_shieldcoop_duel_hearts,coop_duel_rounds,coop_duel_healing,coop_duel_revivecoop_arena,coop_arena_hazards,coop_arena_timeHOS_COOP_TRACERX_VK_MENU_SCRIPTwithCHATNo new public build flag or command-line launch flag is required. The existing
--userselects a separate test save root. Do not putHOS_COOP*switches in the shareablelongshot_env.txt: they are deliberately ignored there. The detailed reference isdocs/coop.md; commit15a68a5e, included here, now supplies the missing named switch entries indocs/development.mdand the README Teleport warning. Internal callback/option numbers are not user settings.Newer-base startup differences, not new co-op permissions: ordinary file logging is now
logs/longshot_log.txt, with rotated logs in the same directory. Runtime caches are incache/, includinglongshot_vk_pipelines*and generatedcache/prompts/files. Terminal or redirected launches keep their actual output stream instead of necessarily creating that log file. On Windows Intel, keepintel_shader_compiles=0(one compiler thread at a time) and leaveRX_VK_NO_INTEL_SERIALunset; do not disable that safety default to start a test. The inherited hold-to-toggle option stays off and Mono output stays Auto by default. The updated tester guide is #355 (comment).Measurements
These are functional results, not a performance comparison. The base below is the pinned ancestry, not current main. No main-versus-r12 timing pair was run; older off-cost or frame-duration observations do not measure this corrected head.
main:
cf605c85bb(2026-11-14)this PR: coop-playtest-r9 @
2f01e1b4bdHardware: NVIDIA RTX 4090, driver 615.78.08; Core i9-13900K, 96 GB RAM; Linux 7.2.15-1-omarchy x86-64.
Scene: child play in Kokiri Forest and Link's house; requested output and captures 1280x720, frame generation off, cap 60; windowless. The renderer reports an initial 1920x1080 offscreen target. No swapchain/display claim.
Method: six separately started two-game sessions on the exact r12 build, with a local relay and disposable saves; every proof passed its first attempt. Each requires the original consent/save/pose/observer checks, inspected new captures, both normal exits and a scan of both complete stopped-game logs, including
coop: puppet:andcoop: players:, for unexpected body hiding, deletion or recreation. Other compilation was kept out; this is not a performance experiment.How to reproduce: build commit
2f01e1b4bdb6e1629c54a998f0403519fe4e5c74from each player's own supported game and use separate save roots and matching peers. Check signed terms/stale acceptance, body consent and Leave, reciprocal child steps, native Copy/autosave/Return, an explicitly approved checkpoint transfer with a genuinely partial cancellation, and five visible sword swings with closed health/activation records. Account for every body creation and removal against the requested action, not only pictures at the endpoints. The short human guide uses menu consent, not the control-API transfer procedure.cf605c85b2f01e1b4No performance/off-cost result exists for r12.
The suite's exact final line:
It ran once with strict prerequisites. All 54 line-added-or-modified drivers pass, including both new presence/join regressions. The three failures are still the pinned main's:
tests/test_framegen_resources.py: its extracted fixture lacks the Intel driver-guard type and shader/compute declarations.tests/test_module_ids.py: its source-extraction marker no longer matches the function it seeks.tests/test_pad_layout.py,PadLayoutTests.test_presets: its extracted fixture lacksHoldToggle.That is one additional pass over intermediate
15a68a5e, two over r101ed75e61, with no new failure or skip; it is not an all-green suite. The translation failure from the older r9 base no longer occurs. The separately recorded GCC-onlytest_menu_keys.pydeclaration failure remains earlier focused-check evidence; the full suite uses Clang for that driver and passes it.All 12 games exited 0. Both complete logs for every proof pass the lifecycle review; a generic “map change or a load” reason is not accepted as an explanation without the corresponding requested action. All six runs have complete kernel coverage without reported driver faults. Six present intervals above one second occur only in S08's initial normal generations: four during entry, two near native Copy/restart; neither temporary generation has one. Their precise cause was not isolated. These short successes do not establish 30-minute continuity.
Earlier evidence keeps its head: the r10 short-proof round at
1ed75e61retained a failed first S03 picture-framing attempt, then passed with the same input/camera route while other compilation was excluded. That history is still in #355 (comment); it is not an r12 failure or an r12 speed result. The intermediate15a68a5efailure at signed joining and the failed r10 long session remain failures, not renamed passes.Earlier export-path repair, still present: the r8 failure came from Windows separators reaching Linux filesystem calls. The r9 fix normalizes the profile/export boundary and rejects parent components; its earlier 92-outcome compiler matrix and 5 Windows-target syntax units remain r9 evidence. The exact r12 whole suite passes the regression and the new S08 run proves actual transfer without weakening owner selection, signed descriptions, recipient consent or path refusals. R8/r10 focused matrices and syntax checks are not described as rerun on r12.
Testing
Tested: the exact r12 Linux executable and renderer build and run, source
05329d46. All seven locally generated child/adult actor, animation and shared-table files are present and agree between the two builds. All six short proofs above pass, including post-shutdown scans of both complete logs; child standing/walking, native Copy/autosave/Return, signed transfer/partial cancellation and the bounded complete health-observer interval have new r12 evidence. The whole r12 suite has 580 passes and the three named inherited failures. Both race and join regressions pass with GCC and Clang; the join regression fails on intermediate15a68a5ebefore the second fix. The switch-reference commit15a68a5epassed its seven selected documentation/source-reader checks before and after, not a separate r12 runtime test.Built for Windows at this head, not run: the exact r12 Windows x86-64 executable and Vulkan renderer cross-built and linked on Linux with llvm-mingw 20261104 / Clang 23.1.5, release/PGO settings, exit 0, with no compiler/linker failure or source workaround. Validated unchanged generated game code from r10 was reused; the full products contain the corrected host code and
v3.0.0-rc.3-179-g2f01e1b4b (source 05329d46). The production warning-suppression flags remain, so this is not warning-clean evidence. Static dependency checks and embedded version checks are not Windows execution, visible-menu proof or Windows UTF-8 runtime-path coverage. No r12 Windows process has run in this evidence.Earlier heads only:
0ec9d0b0: Windows hosted through the game and Linux joined; signed acceptance, reciprocal child bodies/walking, chat and Leave worked, with normal exits and unchanged disposable saves. Linux hosting in the reverse direction was blocked by its firewall, not a successful reverse-direction game test. This was short play on normal disposable saves, not native temporary copies, shared progress, transfer or kits: #355 (comment).0ec9d0b0: one Linux PC, game-hosted loopback traffic. Connection, observed bodies/names/chat and unchanged normal saves held, but the presence readings missed the predeclared sampling cadence. It remains qualified, not an unqualified pass: #355 (comment).1ed75e61: the completed restart met its sampling schedule but failed body continuity: one A-side hide/delete/recreate while the room remained accepted. The first attempt's earlier undetermined stop and the openly revised swap criterion remain in its record. Normal exits and unchanged saves do not turn it into a pass, and neither new fix retroactively repairs that result: #355 (comment).1ed75e61: Windows 11 Pro 26300, Core i9-12900K, 64 GB RAM, RTX 3080 10 GB / NVIDIA 621.14. The tester reports a native build and suite of 556 passed, 6 failed, 35 skipped, against main0caf0618with 521 passed, 6 failed, 34 skipped. The same six failures aretest_pad_layout.py,test_release.py(OpenSSL path),test_tested_configurations.py(line endings),test_oot_state_tap.py(local control connection),test_module_ids.pyandtest_framegen_resources.py; this is not an all-green Windows suite. Two windowed games on that one PC joined a separate loopback relay with automatic acceptance, made backups and left both saves unchanged. Adult bodies were reported as standing-model fallbacks because generated animation assets were missing, so this is not adult walking-animation proof. The repeated body disappearance remains open below. No native Copy/Return, real NTFS junction-refusal case, game-hosted relay, firewall prompt or two-PC session was tested in that report: #355 (comment).1ed75e61: the cross-built r10 folder reached the title and quit on Intel Arc / driver 32.0.101.9175 with co-op off, on-demand shaders, pipeline-cache load/save disabled and native float atomic add forced. Its nonfatalprograms (10 failed)counter stays recorded. It is not an r12 start or completed preparation.Not tested: on r12, any Windows runtime or execution of its changed C/C++ fixtures, a two-machine session, game-hosted LAN/internet/NAT gameplay, a 30-minute session or longer play, and windowed presentation. A new r12 long session is planned separately; no result is claimed here. Also not settled: AMD/Steam Deck, physical input, more than two live games, active-room performance/off-cost or continuous frame-fault coverage. The six r12 proofs use a separate local relay. Generated adult animation/full gear, live arrows/health/place, safe Go to friend arrival, copy-only see-and-talk presence, full chat focus/mute/notices behavior and usable practice kits need their own coverage. S08 tests old-generation refusal, not same-room signed-term changes during a live transfer. Downloads are stored, not loaded or imported. The bounded native health trace does not prove a hit, shield block, every health writer or playable PvP. Broader ordinary progress/chest/boss behavior is not re-proven by these six cases.
Known open problem, found by the Windows tester at
1ed75e61: with two games sharing one GPU over its video-memory allowance, one game fell to 1 to 8 frames a second, and at each such collapse it hid and later re-made the other player's body while the peer loggedin no map: seven times in about two and a half minutes, none in the stretches at 37 to 52 frames a second (#355 (comment)). This is the play check working as written: a game whose play counter has not ticked for 2.5 seconds counts as not being played, removes the bodies it shows, and asks for them again only 2 seconds after it recovers. It is too quick for a real hitch (a starved GPU, a long shader compile). The two fixes in this head do not change that rule; what to change is still to be decided.Needs other testers:
1ed75e61(T-QF6F).Screenshots
These eight pictures were taken on
1ed75e61(r10) and keep their original attachment links and captions. Freshly inspected r12 captures showed the same illustrated states in all six proofs, as recorded by the new proof review; room fingerprints, idle poses and foliage phases naturally differ. They remain r10 illustrations, not relabelled r12 captures. R12's own signed replies, file/mount checks and complete lifecycle logs establish the new results; stills alone do not prove transfer, save isolation or uninterrupted body lifetime.Risk and rollback
This is a large change across save handling, permissions, networking, input, generated presentation and the host/renderer interface. A permission or path mistake could affect a save; incorrect body lifetime could leave a wrong actor; stale input could turn a chat key into a game action. Network loss or the host leaving can stop the room. An invited holder of the secret can lie about cosmetic poses or chat; this is not an anti-cheat system. A verified checkpoint hash proves its bytes, not that an untrusted save is safe for the game's parser.
Use a separate
--userroot from the first launch, containing only a copy of your own saves, and keep the normal installation closed. Native Temporary save adds another isolated working copy. Return switches back to that test root without importing progress. An ordinary restart after a crash selects the normal root for that launch, so never omit--userwhen resuming this test. Copies stay for recovery and are not automatically pruned.Leave the room or set
coop=0/HOS_COOP=0to stop further sharing;coop_host=0/HOS_COOP_HOST=0disables hosting andcoop_players=0/HOS_COOP_PLAYERS=0disables local bodies. Revoke the signed presence bits to withdraw disclosure; merely hiding labels is not revocation. Turning co-op off or reverting source cannot undo progress already merged into an ordinary save. Do not copy a test save back into the usual save folder. To stop using the line, keep its test folder separate and return to the unchanged normal installation.Known wording limits remain: the human-readable signed-rule log is abbreviated; pending PvP log prose can mention progress even though the apply gate stays closed; a body-retirement reason can call Leave a map change/load; the copy reminder can be shortened at 1280x720. Read the actual signed Rules page and acceptance state, not those shortened diagnostic sentences, when deciding what to accept.
The other player's body being hidden and re-made when one game stalls for more than 2.5 seconds, found on Windows at
1ed75e61, is a known open problem of the play check itself. These roster and start-up fixes do not repair it, and the short Linux passes did not exercise it.Reviewer notes
Start with the consent and separate save-domain boundaries, then native Copy/export and body lifetime. The later presentation, travel, chat and practice pieces deliberately do not reuse download consent as permission to affect a game. The native damage/contact connection is absent rather than replaced by guessed remote damage.
Review question: how would you like a line of this size cut for review now that the line is on the current pull request 68 head: signed rules/consent and ownership first, then bodies, save isolation/transfer, and the optional social/travel/practice pieces, or another boundary? This is one WIP tracking pull request, not a request to merge every piece at once. The existing core-versus-module discussion remains at #42; no module interface is built here.
The corrected r12 head has passed the new six-proof round. Next are its own continuous 30-minute session and two-PC/Windows runtime checks, neither claimed here. The r9 qualified soak, failed r10 soak, r9 LAN session and native r10 Windows report retain their original commits and outcomes. Main remains three commits beyond the pinned base; the current-main checkbox and review-splitting question stay open.
Issues
See #68 for the co-op base and earlier proof discussion.
See #51 for its shared text-field dependency.
See #42 for the unresolved core/module direction.
Checklist
For everyone
type(scope): description).main. Replayed onto pull request 68 at4a9b9606/ maincf605c85b, but main has since advanced three commits to0caf06184.python -I -B tests/run_all.pyrun and passing (any failure that also fails onmainis named above). The exact r12 run has 580 passes and the three named failures of its pinned main base, not an all-green result.2f01e1b4: all changed files are text, and the body assets are generated locally from each player's own game rather than committed.third_party/untouched (a new folder with aPROVENANCE.mdinstead). This line does not changethird_party/; its inherited base only adds unmodified Monocypher 4.0.2 with its licence and provenance, and all 12 recorded part hashes match. That version is to be updated to 4.0.3 in the base, since the library's own download page lists 4.0.2 among versions with a possible timing leak in public-key operations.2f01e1b4: the additions are GPL-3.0-or-later contributions or compatible material with preserved notices, and the native bindings contain interoperability facts rather than decompiled game bodies.If applicable
Assisted-by:/Co-Authored-By:trailers.15a68a5e, included in this head, adds all 25 missing named entries and completesHOS_COOP_ACCEPT=always, with the README Teleport warning.MKWVK_API_VERSIONbumped (gpu/vk/vk_api.h'sMkwVkHostorMkwVkRendererchanged).mainand passing here. The export-path regression is fail-before on r8, its actual feature-bearing baseline; plain main has no export layer to exercise.tests/that reads changed source updated in this PR, not weakened.2f01e1b4, the native Linux executable/renderer build, link and run; the Windows executable/renderer cross-build and link with llvm-mingw, exit 0, from a build path containing a space and accented character. Nothing from this head has run on Windows; Windows runtime, changed-fixture execution and UTF-8 runtime-path behavior remain untested.New on this forge; not opened on GitHub.
The fixture's helper was named accept. On Windows the host's network header brings in winsock2.h, which declares accept(), so the Windows checks could not compile the fixture ("conflicting types for 'accept'") and the event tests never ran there. The helper is named grant. Tested: all eight C fixtures this branch adds or changes compiled for the Windows target (clang --target=x86_64-w64-mingw32, -Wall -Wextra, syntax only): this fixture had 15 errors before and has none now; the four other co-op fixtures show none (three fixtures that take their sources or defines from their Python drivers cannot be judged by that pass). Linux, GCC 16.2.1 and Clang 23.1.3: tests/test_coop_events.py passes as before. Not tested: run on Windows (the project's Windows job does that). Assisted-by: Claude Fable 5.2 Co-Authored-By: Claude Fable 5.2 <[email protected]>Two-player co-op playtest: r12
This is an experimental test, not a finished multiplayer release. Use commit
2f01e1b4bdb6e1629c54a998f0403519fe4e5c74(r12) on both PCs, Linux source05329d46. The pull request's branch is still namedcoop-playtest-r9for historical reasons; pin the commit, not the branch suffix. All six short proofs passed on this r12 head with two games on one Linux PC. Windows runtime, two-machine and 30-minute results already posted on the pull request belong to earlier heads, not r12. No Windows runtime, two-machine session or 30-minute result is claimed for this head.Plan for 10 to 15 minutes of play after building and initial shader preparation. Loading and preparation are not part of that estimate. Start with child saves. One tester hosts on Linux, the other joins on Windows; the Windows/network part is what this test is meant to find out.
What each tester needs
Obtain the playtest branch and pin it:
Follow that checkout's
CONTRIBUTING.md, Development setup and Building. Windows uses Python and llvm-mingw, withLONGSHOT_MINGWpointing to it and the documented Builder prerequisites. Linux needs the documented compiler, headers and Python dependencies; its packaged build instructions use Ubuntu 22.04 or the provided container route. Do not update an ordinary playing installation to test this branch.From the prepared source checkout, build into a new game folder using your own dump, for example:
Replace
your-game.nupwith your own file. This is the normal full build, including locally generated co-op bodies. Keep its executable and renderer together; do not mix products from different commits. The reference instructions are https://git.magicbean.dev/mooki/Longshot/src/branch/coop-playtest-r9/CONTRIBUTING.md and https://git.magicbean.dev/mooki/Longshot/src/branch/coop-playtest-r9/docs/coop.md.Keep the normal save out of the test
coop-playtest-user. Copy only your ownsavedirectory, retaining its numbered subfolders, into it. Do not copy someone else's user folder or thecoop-keysdirectory. Each player must have a separate test root and identity. A new disposable game is also fine, but is not a quick substitute for a prepared checkpoint.--user "./coop-playtest-user". A source build normally haslongshot.exeorlongshotdirectly in its output folder. Packaged builds put the real executable inbin; the outer launcher does not forward--user.fs: mountedlines and the chosen save before accepting a room. They must refer to this test root, never the usual save directory. If they do not, stop without accepting.coop-sessions, and Return to normal saves returns to this test root, not the original installation. Normal and copy acceptance do not carry across those restarts.For a source-built game, run from its new game folder:
For a packaged game use
bin\longshot.exeorbin/longshotinstead. Keep this explicit--userargument on every manual restart, especially after a crash. An ordinary restart does not automatically resume an interrupted native temporary copy. Do not import the test's progress back into the usual save folder.Windows Copy/Return still needs a real game window. Do not use
HOS_HEADLESS=1for this route. A restart creates a new Windows process; the first process ending is not proof of a completed Return. Wait for the replacement window and check its newfs: mountedlines before continuing. Never create or copy a restart ticket by hand.R12 keeps runtime program/pipeline/preparation caches under
cache/, Vulkan pipeline files ascache/longshot_vk_pipelines*, and generated prompt files undercache/prompts/. Use a new writable r12 game folder; do not copy Linux driver caches into Windows or move files out of a running r9 folder. Initial preparation is still needed for the target PC. The game's shipped data andlongshot_options.inistay at their documented locations.Settings before starting
Edit only the test game's
longshot_options.ini, with that game closed. Set these on both PCs:Here category value 0 means Own. Presence value 1 permits child bodies only; it does not enable fighting, gear, health, place or arrows. Chat is enabled here for an explicitly agreed trial, not because it was already proven on r12.
coop=0prevents automatic connection; Make or Join turns it on for the current run. The maker's signed rules, not the joiner's local draft, govern the room.The Linux host additionally sets
coop_host=1; the Windows joiner setscoop_host=0. Remove any inherited co-op environment overrides, automatic joining/acceptance, diagnostic feeds or loss simulation from the test launch. Do not useHOS_COOP_ACCEPT=always.HOS_COOP*values written inlongshot_env.txtare ignored; these settings belong in the options file or the process's own environment.On Windows Intel Arc, keep the newer base's
intel_shader_compiles=0, which admits one compiler thread at a time. LeaveRX_VK_NO_INTEL_SERIALunset; do not use it to bypass that safety default. The inherited hold-to-toggle controls default to off and Mono output to Auto; neither needs changing for this route. If a follow-up asks for diagnostic commands, usescripts/longshot_ctl.pyfrom this exact r12 checkout: it now reports sampled game state, which can remain old at the title, during a pause or while loading. An advancing reply counter alone does not establish fresh play state.Host and join using the game itself
No separate relay program is needed. The firewall example below is retained from the previous head
0ec9d0b0; it does not claim a two-machine r12 session. A new executable path on Windows can require a fresh firewall decision for the intended private network.hosting on this PCand printshosting: players on this network can try .... The host's own client connects over loopback.127.0.0.1on the second PC. Choose Join a room and enter the secret shared privately by the host.Connecting: no answer from the relay, and the host sits alone in its room; nothing else is wrong. The host's log can print more than one address: use the home-network one. Tried between a Linux PC and a Windows 11 PC: #355 (comment)The 10-to-15-minute route
Use menu labels, not a remembered row number: rows appear, disappear and scroll with room state. Insert opens the menu, and the Co-op tab is at the end. Follow the displayed keyboard/controller prompts.
fs: mountedlines showing the new copy root. A failed copy must not be bypassed by accepting on the ordinary save.If anything unexpected changes the save, the game crashes, or either player cannot tell which save domain is active, stop that session and preserve the logs. Do not delete the copy or try to repair it by importing another save.
What is not part of this short test
coop_warp=1; only Kokiri Forest and Hyrule Field are supported. Safe actual arrivals are not proven by the six proofs. Do not return to the real save just to make the row available.What to send back
Send a short result from each tester: exact commit/source line, OS, GPU/driver, CPU/RAM, host or joiner, LAN or internet, duration, which route steps worked/failed/not tried, and the first unexpected action. Say whether the normal save stayed unchanged and how you checked. A picture or short video of both views is useful; mask the room secret and unrelated private information.
The documented default game log is now
logs/longshot_log.txtin the game folder. The four preceding runs arelogs/longshot_log.1.txtthroughlogs/longshot_log.4.txt. Copy/Return restart the process, so retain the rotated files too. A game started from a terminal prints there instead; an explicitly redirected launch keeps its chosen output file and may leavelogs/empty. Retain that actual output, rather than assuming a missing default file means nothing happened. Preserve output from every restarted process. The README documents these names at https://git.magicbean.dev/mooki/Longshot/src/branch/coop-playtest-r9/README.md.Keep the complete relevant logs, plus these lines around the failure:
vk: deviceidentifying the GPU and driver.fs: mountedlines before Copy, on the temporary copy and after Return. Redact personal path prefixes when posting, but keep enough to distinguish the save domains.coop: hosting/hosting: players on this network can try ...,coop: connecting to the relay,the relay answered,created,joined room, and anyno room:refusal. Share network addresses privately when needed, not in a public screenshot.coop: sync:acceptance and changed-offer lines;coop: players:creation, hiding/deletion or asset/matching-build refusals.coop: stats:line, especiallyreliable retries,events given up,outbox fullandround trip. Given-up events include intentional cancellations, so do not label that total packet loss.coop: relay lost; rejoining,is away,is back,left room, and the final shutdown or error lines.coop: kit:only if a separate kit test was explicitly agreed. Chat text is not logged: describe a failed message without expecting to find its contents in the log.HOS_COOP_TRACE=1can add packet and body details when a focused follow-up asks for them; it also logs relay addresses and can make a large log. It is not needed for the first attempt. Do not send saves, identity keys, room secrets, game files, generated assets or a whole user folder. Return the report to the WIP line or #68 while that remains the collection point.One continuous 30-minute two-game session on this branch's build (
0ec9d0b0), two windowless games on one Linux PC (RTX 4090, NVIDIA 615.78.08), hosted by the game itself, one acceptance each, then standing, short walks towards and away from each other, a chat line every five minutes and idle stretches.What held for the whole 1,803 seconds:
What the run did not meet: the plan, written before the run, wanted each game's presence read every five seconds. The reader managed 265 paired readings at a median of 6.6 s, with gaps up to 18.9 s while pictures were being taken. That is the test's own shortfall, not something the games did (their frame records kept advancing through those gaps), but it means presence was not observed at the planned rate. By the plan's rule the soak is therefore not an unqualified pass, and it was not run a second time.
Not covered by this run: two machines, Windows, a map change, items or shared progress, the save copy and return (shown separately in the six proofs), adult bodies and equipment. It is an ordinary functional run, not a speed or smoothness measurement. The traffic was on one PC over the loopback address.
Two machines: a Linux PC and a Windows 11 PC on a home network. Until now every proof here ran two games on one PC over the loopback address. This is the first session across a real network and across operating systems, on head
0ec9d0b0on both.0ec9d0b00ec9d0b0, cross-builtSettings: Co-op mode, every progress category per player, child bodies on, chat on; nothing shared, no fighting, no transfer. The game's own hosting on UDP 24680, home-network addresses. Joining was scripted with a private room secret; consent and Leave went through the real menu rows on each machine.
Windows hosts, Linux joins: works
1a52-512e-5836-45fd, same rulesLast counters of each game:
The Linux game logged a round trip of 1 to 4 ms to the Windows host over the session.
Linux hosts, Windows joins: blocked by the host PC's firewall, not by the game
The Linux game hosted and sat in its room alone; the Windows game sent its join packets to the Linux PC's home-network address and showed "Connecting: no answer from the relay; trying again in 15 s". The Linux PC's firewall logged those exact packets as blocked (incoming UDP 24680). Three attempts, the same each time; no firewall was changed for this test. So a host has to allow incoming UDP 24680 from the other player's PC. On Windows the first hosting brings up Windows' own firewall prompt, and allowing it for private networks was enough here.
One thing a player cannot tell from that message is which side is at fault; a hint about the host's firewall in that text would save a search.
What this does not cover
0ec9d0b0241ed75e61b5Updated to
1ed75e61b5bc083247950a262b1a48230f44909a(r10), sourcec058db8c, replayed onto pull request 68's current head4a9b9606: #68. The branch namecoop-playtest-r9is historical; both testers should pin the new commit. Its base is maincf605c85b, not today's0caf06184, which is three commits later.All 80 line commits remain, and the co-op core is unchanged. Three separate fixture fixes follow the replay:
test_menu_feature_rules.py: supply the newer parser's Plus/Minus exclusions.test_menu_text_field.py: combine pad/text callbacks and retain the correct input sections beside chat.test_oot_coop_hits.py: use the newer damage-policy interface and check overkill on both native update paths.The new head was rebuilt from a fresh game-code translation. The whole suite reports 578 passed, 3 failed, 16 skipped; the failures are the pinned main's
test_framegen_resources.py,test_module_ids.pyandtest_pad_layout.py. All 26 added drivers pass within the 52 added-or-modified drivers, all of which pass. No check was weakened or removed.All six two-game proofs pass on r10: signed terms and fresh consent; bodies appearing and leaving; reciprocal child standing/walking; native save copy, autosave and Return; signed checkpoint transfer and partial cancellation; five visible sword swings with complete health records and no committed player loss through that observed path. The description now has eight pictures from this build.
The first S03 attempt remains a failed visual proof: other compilation was running, and the same 72-frame walks lasted 1.85 and 2.03 seconds instead of about 1.20 seconds, moving the bodies outside the cameras. With other compilation kept out, the unchanged route passed; no game code, input length or camera was changed, and this is not a controlled performance comparison.
The r10 Windows build links and has reached the title, but has not shown Windows gameplay. The two-machine LAN result and the qualified 30-minute session remain evidence for the previous head
0ec9d0b0only: #355 (comment) and #355 (comment). No result from the new r10 30-minute session is claimed. This stays WIP; fighting remains disabled, and the updated guide names the new commit,logs/andcache/locations and Windows startup limits.R10: thirty minutes completed, but body continuity failed
Tested commit
1ed75e61b5bc083247950a262b1a48230f44909a, sourcec058db8c, with two windowless games on one Linux PC, RTX 4090 and NVIDIA 615.78.08. The game hosted the room over loopback. Each player accepted once, then took short walks, stood idle and sent the seven planned chat lines. Other compilation was kept out.The completed session is not a pass. Around 18 minutes 14 to 19 seconds, game A logged hiding and deleting B's body, followed by creating it again. B has no corresponding body removal. The room stayed connected and accepted, and no load, travel or Leave was requested.
A's native log says:
It then reports the child body shown again. Both changes fall within one five-second native-clock bracket; the exact missing duration is unknown. Nearby fresh readings still show the same place with no event or loading. The surrounding pictures show both bodies before and after, not the missing instant. The reason for that brief change is not established. The live check missed the actual hide line; reviewing the whole log caught it, so normal exits are not being counted as a pass.
Both attempts remain on record
The first attempt stopped at minute 4 when its combined same-process/zero-swap check failed. The failing value was not saved before the check, so it cannot be recovered. The logs show one native start and one socket generation per game, with no evidence of a process change. Its cause remains undetermined.
I chose the single restart on that undetermined stop, not on a proven test-only failure. Before restarting, I also changed the zero-swap criterion openly: this PC already held about 12 GiB of other programs' pages in compressed zram, with swappiness 150. Swap became a recorded observation rather than a stop by itself, and memory growth became resident plus swapped memory, keeping the predeclared 512 MiB final-growth limit. Process identity remained a separate hard check. All measurements were written before checking them, with the PC's memory and paging counters alongside them.
The restarted games happened to report zero swapped memory in all 31 samples each. That does not explain the first attempt's lost value. No third attempt was made.
Results beside the previous head
The earlier result remains #355 (comment).
0ec9d0b01ed75e61The first r10 attempt separately had four gaps above one second during startup/entry, none in its reached accepted interval. It must not be confused with the completed restart's zero gaps. The different memory values are observations from individual runs, not a controlled claim that r10 is faster or uses less memory.
The five-second schedule is now met, including during walking and pictures. Its local-state and room readings cannot, by themselves, prove the remote body exists between them; the native lifetime records remain necessary.
Not covered: two PCs, Windows, internet traffic, physical input, map changes, items or shared progress, save copy/Return, transfers, adult equipment or fighting. Copy/Return belongs to the separate six-proof result. This remains an ordinary functional test, not a speed, smoothness or general leak-free claim.
The cause of that one hide and recreate is being looked for in the code that decides a game has stopped being played; the result follows here.
Windows results at r10
1ed75e61(source as checked out, not a cross-build), for the "Windows" lines under Needs other testers. One PC, so no two-PC session from here.Hardware: Windows 11 Pro 26300, i9-12900K, 64 GB, RTX 3080 10 GB (NVIDIA 621.14). Toolchain: llvm-mingw from the Longshot Builder's tools, Python 3.12.
1. The game-free suite, run natively on Windows (
python -I -B tests/run_all.py, 24 at once): 556 passed, 6 failed, 35 skipped. Main0caf0618on the same PC, same run: 521 passed, 6 failed, 34 skipped. The 6 failures are the same tests on both, so this line adds none, and the 35 extra tests it brings all pass on Windows, including the co-op fixtures that compile and run here:test_pad_layout.pyHoldToggleundeclared in the fixture since #137 (#331 fixes it)test_release.pyopensslnot on the PATH (#144 has run_all find Git's); passes with it on the PATHtest_tested_configurations.py\r\nfrom the CLI's ASCII pipe on Windowstest_oot_state_tap.pyno game listens on longshot-oot-test-<n> (Invalid argument)test_module_ids.py,test_framegen_resources.py0caf0618on Windows too; not looked into2. A native Windows build:
nx2/lift.py --standalone --writeandnx2/build.py --standaloneat1ed75e61, exit 0 in 610 s, no build error.3. Two games on one Windows PC, each in a window that never took focus, with their own game folder (B a full copy of A's) and their own user folder. Each user folder had a copy of one played save (adult Link, Desert Colossus). Relay:
scripts/coop_relay.py --host 127.0.0.1 --port 0, loopback only, so Windows Firewall's prompt and the game's own hosting were not tested. A:HOS_COOP_AUTO=create,coop_presence_permissions=3in itslongshot_options.ini; B:HOS_COOP_AUTO=join; bothHOS_COOP_PLAYERS=1,HOS_COOP_ACCEPT=always, the same 18-character secret. The script loaded both saves, walked B forward 90 frames, then A, waited, quit B, then quit A.room RSY… created (member 1), Bjoined room RSY… as member 2. Relay:WinA joined as member 1,WinB joined as member 2, thenWinB (member 2) left (1/8)andWinA (member 1) left (0/8). A loggedWinB (member 2) left the room, and both loggedleft room (the game is closing).room accepted (HOS_COOP_ACCEPT=always); the save is copied first, then madecoop-backup\first-20261114-0846xxZbefore applying anything.the flag table reads as the game's getter does (28 flags checked). A kept its signing key underuserA\coop-keys.save\before and after: identical for A and for B. The user folders gained onlycoop-backup,coop-keys(A) andtemp.PouchPlayer_Adultat the other's position (member 2 is shown as PouchPlayer_Adult at -4.01 100.13 0.06 (made in 104 ms)). A person watching both windows says each could see the other's Link. B was attacked by the Colossus enemies, which A doesn't show (expected: only players are shared), but A did show B being knocked back by the hits.thrown away 0,reliable retries 0,events given up 0, round trip 1 ms, on both. NoCRASH, no device loss; both exited onquit.players: locally built animation assets missing or incomplete→ standing models, in this dev build (no Builder asset step); so no walk animation was checked.One thing to look at: the other body was shown and hidden repeatedly while both games stayed in Desert Colossus and in play (no
control: statechange at those points). In about 2.5 minutes, A logged 7 pairs ofmember 2 is shown as PouchPlayer_Adult .../member 2 is hidden (this game stopped being played: a map change or a load). B loggedmember 1 is hidden (in no map)in step with them, made 7 times. So each game seems to decide every so often that it isn't being played, and tells the other "no map". Each body was re-made each time (34-1285 ms). I couldn't tie it to a cause from the logs. It might be the enemy hits and knockback (B was being attacked), or the windows being clicked by the person watching, since the log shows the foreground switching between them. But there's no line that says so.Not done here: native Copy/Return (Temporary save) and the junction refusal on a real NTFS junction in the game. Both need the Co-op tab's two presses, which I can drive only by timed menu keys, so I'd do that as a separate run if it's still wanted. Also not done: hosting through the game, the firewall prompt, more than two players, and two PCs. Shots stay on this PC (game imagery), so none are attached.
Thank you, this is exactly what the Windows lines were missing: the suite natively on Windows with the line's fixtures compiling and running there, a native build, and two games sharing a room with saves unchanged.
On the body being shown and hidden repeatedly: that needs explaining, and it is not what the guide or the proofs lead anyone to expect.
member 1 is hidden (in no map)in step with A, so game A itself reported that it was not in play, seven times in two and a half minutes. Two games in windows on one PC, one of them always in the background, and one player being hit, are both things the Linux proofs never had.The code that makes that decision is being read against your description now. Three things from your run would narrow it, if the logs are still there:
member 2 is hiddenline, and the same stretch of B's log.Copy/Return and the junction refusal on Windows are still wanted, but they can wait for the next head so they are not spent on a commit that is about to be replaced. Your suite and build results stand for
1ed75e61as posted.The logs are still here. Short version: every hide came during a frame-rate collapse, which two games sharing one 10 GB RTX 3080 caused. They were over the driver's video-memory allowance. Window switching doesn't line up.
3. Frame rate. A's
vi: ... fps over the last 5 slines against its hides (log line numbers):There's no hide in A's stretches at 37-52 fps. B ran at 17-60 fps. Its own single
member 1 is hidden (this game stopped being played ...)(line 2662) came as it fell from 16.4 to 9.1 fps. Itsmember 1 is hidden (in no map)lines are A's reports arriving. Around A's first hide, the capture path was stalling too:vk: a capture's wait for write-back 24013 timed outrepeated,nvn: GPU waits in 5 s: 33 sync waits (1447 ms).So my reading: A's game thread went seconds between frames, the "being played" check took that for a load, and B was told "in no map". It's an artefact of this test setup, two games on one GPU over its memory budget. But it would also show for a real player whose game stalls for a second or two (a slow PC, a shader compile, a hitch), so the check may want a longer grace before it reports a load.
1. The stretch around the first hide. The above is A's, with the renderer's own lines left out. Untrimmed, A from 4 lines before to 4 after:
B at the same time logged
member 1 is hidden (in no map)next tovi: ... 47.4 fps.2. Window switches and hits. Window switches don't line up: A's foreground changes are at lines 1156, 1750, 1910, 1979, 2824 and 2938, and only the last is near a hide (2951, 13 lines later). B was being hit in that stretch, but the log has no line for a hit (heart protection was on), so I can't say which hits were when.
I'll do Copy/Return and the junction refusal on your next head. For the bodies I'll run it with one game on the RTX 3080 and the other on the iGPU, so neither runs out of video memory.
Follow-up on the repeated show and hide, after reading the code against your description.
What decides it. A game stops counting as played, removes the bodies it shows and tells the others it is in no map, when one of these fails: a save is loaded, the player object is valid and in the world, no load or transition flag is set, and the game's own play-time counter has ticked normally within the last 2.5 seconds. A change of that counter other than a small step forward is taken for another save having been loaded. Separately, a game that has not managed to read its own player's pose for 2.5 seconds also reports itself as not in the world. After a removal there is a 2-second wait before the body is asked for again. Being hit has no branch of its own in any of this, and neither does window focus: the game is always told it is in focus, and losing focus only resets input.
What that suggests, not established: anything that holds one game still for a moment could trip the counter rule or the 2.5-second rule. On Windows, pressing on a window's title bar or border blocks that window's thread until the button is released, and a covered window can be held back by the compositor. That would fit clicking between two windows; it is a candidate, not a finding. The fix that is being proven now is for a different fault and does not touch these rules.
For comparison: in the two-PC session on the earlier head, the Windows game ran in its own real window for about three minutes and each game's complete log has exactly one show and one hide (the Leave). That was one game per PC, never covered or clicked.
If you run it again,
HOS_COOP_TRACE=1on both games makes the reason visible. The lines to look for around the first hide and the recovery:and whether each hide follows a click on a title bar or a switch of the front window. A minute with nobody touching either window, then a minute of deliberate switching, would separate the two.
Here, the same is being tried on Linux by stopping one game for a fraction of a second, one second and three seconds with the trace on; the result follows.
That fits the 2.5-second rule: in my comment above (frame rates against the hides), game A ran at 1.2-8 fps over 5-6.5 s windows at every hide, with present intervals almost all over 40 ms (34 of 42 in the window before the first hide), on one RTX 3080 shared by both games over the driver's video-memory allowance. So a counter stalled for over 2.5 s explains all seven, with no window clicks needed. A real player with a hitch that long would see the same, so a longer grace or a frame-count-based rule might be worth a look. I'll redo the bodies with one game on the iGPU so neither starves.
That settles it better than anything here could: every hide sits in a stretch at 1 to 8 frames a second, none in the stretches at 37 to 52, and the window switching does not line up. Thank you for going back through the logs.
So this is the rule working as written on a game that was starved, and you are right that the rule is too quick for a real hitch: a slow PC, a long shader compile or a memory squeeze would make the other player vanish, and after a removal the body is not asked for again for two seconds. It is being tried here directly, by stopping one game for 0.3, 1 and 3 seconds with the trace on, to see which lengths remove a body and by which of the two rules (the 2.5-second one, or a jump of the play counter being taken for a load). What to do about it (a longer grace before a body is removed, or judging by frames instead of seconds as you suggest, and not treating a jump after a stall as another save) will be proposed here with that result. It is not in the head that is about to be pushed: that one,
2f01e1b4, fixes the race from the 30-minute session and has passed the six proofs.One game on the iGPU so neither starves is a good next run; the bodies with walking animation would need the animation assets built, which your development build did not have.
Updated to
2f01e1b4bdb6e1629c54a998f0403519fe4e5c74(r12), Linux source05329d46. The fork branch still has its historical namecoop-playtest-r9. The base remains pull request 68 at4a9b9606, on maincf605c85b, three commits behind0caf06184.The failed 30-minute session on r10 led to two presence fixes and the completed switch reference:
2be4e9233: an unchanged roster heartbeat no longer looks like a changed room view that briefly withdraws permission to show the other player.15a68a5eb: the missing switch-reference entries, defaults and test/development limits are documented.2f01e1b4b: a joined room view stays pending until the native engine exists and actually receives it.The intermediate head
15a68a5efailed its first two-game proof before signed-room readiness and was never pushed. Removing the repeated heartbeat exposed a second fault: the first room view had been marked handled before the native engine existed, and the old heartbeat had been retrying it by accident.Both new regressions pass with GCC and Clang; the real join-to-accepted-room test fails on that intermediate head. On r12 the whole suite reports 580 passed, 3 failed, 16 skipped: the failures remain the pinned main's
test_framegen_resources.py,test_module_ids.pyandtest_pad_layout.py.All six short two-game proofs pass at the first attempt on r12: signed terms/consent, body consent and Leave, reciprocal child poses, native Copy/autosave/Return, signed transfer/partial cancellation, and the bounded five-swing health-observer test. Both complete logs were also scanned in every proof for unexpected body hiding, deletion or recreation; none occurred. The eight existing pictures remain labelled r10 illustrations; fresh r12 captures showed the same views.
Linux builds and runs. The exact r12 Windows executable and renderer cross-build and link, but have not run on Windows; no r12 two-machine or 30-minute result is claimed. The qualified 30-minute session and LAN result remain at
0ec9d0b0; the failed later 30-minute session and native Windows tester's results remain at1ed75e61. The repeated show and hide the Windows tester found is a separate matter and is not repaired here: the tester's logs show every hide during a frame-rate collapse (#355 (comment)), which is the play check's 2.5-second rule working as written and being too quick for a real hitch. What to change there is still to be decided.Testers: pin
2f01e1b4; results already posted for1ed75e61stand for that commit. This stays WIP and fighting remains disabled.The freeze experiment promised above, on head
2f01e1b4(two windowless games on one Linux PC, RTX 4090, both accepted, standing in sight of each other, trace on). One game's whole process was stopped and released again three times, with more than 42 seconds of untouched play between; nothing else was changed and no input was sent.In the 3-second case there are two separate reactions, as the code read suggested:
not heardshortly before A was released: it had received nothing from A for 3 seconds.game paused(its play counter had not ticked for more than 2.5 seconds), removed B's body, loggedgame runningone counter step later, and asked for the body again.in no mapline oranother save was loaded. So on this PC a clean 3-second stop shows asnot heardon the peer; the Windows report'sin no mapis the same check seen from a game that kept sending while starved.What this means for a player: a hitch of up to a second does nothing. One of about 2.5 to 3 seconds or more makes the other player disappear on both sides and come back roughly two seconds after the game recovers. Nothing is lost and no consent has to be given again, but it looks like the other person left.
Possible changes, to be decided, none made: keep a body through a short not-in-play reading and remove it only if the reading lasts (for example ten seconds), or freeze it in place with a mark instead of removing it; ask for a removed body again at once when play resumes rather than after the 2-second wait; judge liveness by frames as well as seconds, so a game that is slow but still running is not taken for one that stopped. Each of these touches when another player's body may be shown, so it needs the same care as the consent rules.
One observation session, not a proof with a pass or fail; exits 0 on both games; the stop was applied to the game process only.
R12: thirty minutes of body continuity passed, but the save-byte rule failed
Tested commit
2f01e1b4bdb6e1629c54a998f0403519fe4e5c74, source05329d46, with two windowless games on one Linux PC, RTX 4090 and NVIDIA 615.78.08. The game hosted the room over loopback. Each player accepted once, then stood, took short walks and sent the seven planned chat lines.The accepted session lasted 1,800.129 seconds with no disconnect, rejoin or unexpected body removal. It is nevertheless not an overall pass: the rule written beforehand required every normal-save file to remain byte-identical, and each disposable test copy's
option.savchanged.What passed
There were four game-present intervals longer than one second during startup, two per game. None occurred during the accepted interval or shutdown. Those startup intervals remain part of the result; this was not a speed or smoothness measurement.
What failed
Each normal save tree contained 14 files. Only
option.savchanged, by 99 bytes on each side. All 13 other files per game and all files in the original test-save source remained byte-identical. These were disposable copies, not the ordinary playing installation.The retained file times are 05:00:00.732 and 05:00:01.053 local, on 14 November 2026, around minute 14 of the accepted session. Both games were idle then, well before the planned final Leave. Both files kept the same size and layout; one changed value equals the timestamp of 05:00:00. The log does not identify the writer, and some changed values have not been given meanings.
So the byte check genuinely failed, but the evidence does not yet establish whether this was expected native bookkeeping or a multiplayer defect. It does not establish corruption. A clock-related update is a possibility, not a demonstrated cause. The failed requirement is not being changed afterward to ignore the options file, and no second r12 soak was run.
Beside the earlier sessions
0ec9d0b01ed75e612f01e1b4option.savchanged on each sideThe earlier r10 first attempt stopped at minute 4 on an undetermined same-process/zero-swap check whose failing value was not recorded. I chose its single restart on that undetermined stop, not a proven test-only trigger. Before restarting, I changed zero-swap to recorded swap with resident-plus-swap growth because this PC already used compressed zram at swappiness 150. R12 used that openly amended memory rule from the outset, without a restart. The earlier records remain:
#355 (comment)
#355 (comment)
Not covered by this session: Windows, two PCs, internet traffic, other GPUs, physical input, map changes, save copy/Return, transfers, progress-changing actions, adult equipment, fighting or deliberately stalled games. In particular, this does not resolve the separate Windows starvation and 2.5-second play-clock problem described at #355 (comment). The line stays WIP.
Whether a single game with co-op off writes the same file at the same clock time is being checked next; the answer follows here.
Answer to the open point of the 30-minute session above (#355 (comment)).
The options-save change is the game's own daily bookkeeping, not something that requires co-op.
I ran one game at a time on Linux/RTX 4090, using disposable copies of the same checkpoint and the existing guest-clock override. The PC's clock and time zone were not changed. Each successful test had loaded the save and reached quiet play more than three minutes before the boundary, then received no gameplay input.
option.savresult2f01e1b40caf061840caf06184The native code uses a day boundary at 05:00 local, not every full hour. It saves a rollover timestamp, buffered event and position history, and their four counters. The relevant native functions are unchanged between these builds.
That also accounts for the original 99 bytes: the stationary tests have one new position record; the soak has two, accounting for the extra 14 changed bytes. All eight new event records have the same non-time contents in main, r12-off and both soak games. It was not merely one harmless timestamp that the byte check noticed.
All three tests exited normally; every other save file and the original checkpoint stayed unchanged, with no driver faults. One earlier attempt never passed startup preparation and is retained as unexercised, not a negative result.
The original soak still failed its predeclared byte-invariance rule. For future sessions, keep the whole-file hashes and compare these identified bookkeeping fields separately, checking the rollover time, counters and appended records while requiring every other byte to stay unchanged. Do not ignore
option.sav. No game code or soak criterion was changed here. Windows and other clock boundaries remain untested.So for the session on
2f01e1b4: what it was run for held (30 minutes with both bodies continuously present, no retries, saves' progress untouched), and the one rule it failed was the test's own assumption that an idle game never writes its options save. The session crossed 05:00; the two earlier ones did not.View command line instructions
Manual merge helper
Use this merge commit message when completing the merge manually.
Checkout
From your project repository, check out a new branch and test the changes.