Session resume — reload state and broadcast to peers (host+join) #4

Closed
opened 2026-08-25 06:03:45 +02:00 by pbwraith · 1 comment
Owner

Repo: gopher64-netplay (fork of https://github.com/gopher64/gopher64)
Context: Full scope doc at upstream analysis — see commit history. Netplay uses GGRS over WebRTC (matchbox_socket), reliable channel carries NetplayMessage { name, data } in 16KB chunks. Savestates serialize full Device via postcard + compress.

Goal: Host loads a .netplay-state file, then broadcasts the state to joining peers so everyone resumes at the saved frame.

Background:

  • Downstream of issues #1 (message types) and #2 (in-memory state)
  • Lobby protocol in src/ui/netplay.rs currently has no resume concept; session start sends cartridge saves only (ui/storage.rs load_saves)

Acceptance:

  • Host path:
    • Add "Resume Session" action in the create-session flow (loads .netplay-state file; file picker like select_rom)
    • After peers connect and player-number handshake completes, host sends full state via send_netplay_state (message name from #1) BEFORE advance_frame starts
    • Host waits for all peers' netplay_state_ready ack (timeout 30s; on timeout, disconnect that peer)
  • Join path:
    • Client receives netplay_state, loads via load_savestate_from_data
    • Client sends netplay_state_ready ack
  • After both sides load: GGRS frame counter must start at the SAVED frame, not 0 (see issue #5 for the frame-reset work — coordinate the interface with that change; if frame reset is not yet landed, start at captured frame using whatever mechanism #5 provides, else document the gap)
  • Onscreen messages: "Syncing session state…", "Session synced — play resumes"
  • Tests: mock 2-peer flow — host saves session, peer receives and loads, both report same frame metadata.

Verification: cargo test; manual 2-player resume smoke test.

**Repo:** gopher64-netplay (fork of https://github.com/gopher64/gopher64) **Context:** Full scope doc at upstream analysis — see commit history. Netplay uses GGRS over WebRTC (matchbox_socket), reliable channel carries `NetplayMessage { name, data }` in 16KB chunks. Savestates serialize full Device via postcard + compress. **Goal:** Host loads a `.netplay-state` file, then broadcasts the state to joining peers so everyone resumes at the saved frame. **Background:** - Downstream of issues #1 (message types) and #2 (in-memory state) - Lobby protocol in `src/ui/netplay.rs` currently has no resume concept; session start sends cartridge saves only (ui/storage.rs `load_saves`) **Acceptance:** - Host path: - Add "Resume Session" action in the create-session flow (loads `.netplay-state` file; file picker like select_rom) - After peers connect and player-number handshake completes, host sends full state via `send_netplay_state` (message name from #1) BEFORE `advance_frame` starts - Host waits for all peers' `netplay_state_ready` ack (timeout 30s; on timeout, disconnect that peer) - Join path: - Client receives `netplay_state`, loads via `load_savestate_from_data` - Client sends `netplay_state_ready` ack - After both sides load: GGRS frame counter must start at the SAVED frame, not 0 (see issue #5 for the frame-reset work — coordinate the interface with that change; if frame reset is not yet landed, start at captured frame using whatever mechanism #5 provides, else document the gap) - Onscreen messages: "Syncing session state…", "Session synced — play resumes" - Tests: mock 2-peer flow — host saves session, peer receives and loads, both report same frame metadata. **Verification:** `cargo test`; manual 2-player resume smoke test.
pbwraith added the
netplay
p1
rust
labels 2026-08-25 06:04:34 +02:00
pbwraith added the
in-progress
label 2026-08-25 10:54:10 +02:00
pbwraith added
in-review
and removed
in-progress
labels 2026-08-25 23:38:33 +02:00
Author
Owner

Implementing lane 1 — PR ready for manual review.

PR: #18 — #18 (base main, head issue-4, state open, mergeable)

Branch commits (issue-#4 work, on the stacked dependency branch):

  • 9097b45 — session resume: reload saved state and broadcast to peers (#4)
  • 1bb409f — resume both sides at the saved GGRS frame (issue #5 reset_frame)
  • 92a4781 — count same-batch peer acks in session-resume handshake (fixes 3+ player ack undercount: the by-name messages map collapses same-poll acks into one entry)
  • 79127f9 — savestates: import netplay for reset_frame call (compile fix for #5's merged code)
  • 79e88dc — tests: fix resume mock session registration and RDRAM sizing

Acceptance coverage:

  • Host path: "Resume Session" action in create-session flow (.netplay-state file picker mirroring select_rom); resume advertised to joiners via session resume feature; after the player-number handshake, host loads its own state, resets the GGRS frame counter, broadcasts via netplay_state, waits for every peer's netplay_state_ready ack (30s timeout → session abandoned, never started desynced).
  • Join path: peer receives netplay_state over the reliable channel, loads via load_savestate_from_data, acks netplay_state_ready.
  • Frame counter: both sides reset to the SAVED frame via issue #5's reset_frame (vendored ggrs, PR #9's patch — merged into this branch).
  • Onscreen messages: "Syncing session state…" → "Session synced — play resumes".
  • Stacked dependency branches per repo convention (merge order #14 → #15 → #13 → #12 → #9 → this PR).

Tests (cargo test --lib on the local worktree, full lib suite): 18 passed / 0 failed

  • resume_state_reaches_peer_with_same_frame_metadata — mock 2-peer flow: host's in-memory savestate travels the chunked envelope, peer loads it, both agree on frame 12_345 and the peer's GGRS counter resumes there
  • resume_host_loads_state_from_disk_and_resets_frame — host path with real on-disk .netplay-state; device state + frame counter match the saved session
  • ack_batch_counts_every_ack — two acks in one poll both counted
  • wait_for_ready_all_counts_acks_and_times_out — timeout path
  • resume_session_skips_non_resume_sessions — regular netplay untouched
  • All existing netplay/savestates tests stay green

cargo build --bin gopher64 green; git diff --check clean on the issue-#4 commits.

Notes for the reviewer:

  • This WSL box builds with local-only tweaks (.cargo/config.toml, build.rs, clang-18/pkg-config/fcroot env, RUST_MIN_STACK=8388608 for test threads, -lexpat link arg) — none are part of this PR.
  • The cdylib (Android .so) target cannot link on this box (non-PIC static fontconfig/expat); bin + tests are the practical gate here.
  • Manual 2-player resume smoke test not run by the lane (no live clients per lane policy); the mocked 2-peer flow covers the protocol + state-load mechanics. One trailing-whitespace line in vendored vendor/ggrs/examples/README.md (from #5's branch) is the only diff --check nit in the stack.
Implementing lane 1 — PR ready for manual review. **PR:** #18 — https://gitea.185.214.135.234.nip.io/pbwraith/gopher64-netplay/pulls/18 (base `main`, head `issue-4`, state open, mergeable) **Branch commits (issue-#4 work, on the stacked dependency branch):** - `9097b45` — session resume: reload saved state and broadcast to peers (#4) - `1bb409f` — resume both sides at the saved GGRS frame (issue #5 reset_frame) - `92a4781` — count same-batch peer acks in session-resume handshake (fixes 3+ player ack undercount: the by-name messages map collapses same-poll acks into one entry) - `79127f9` — savestates: import netplay for reset_frame call (compile fix for #5's merged code) - `79e88dc` — tests: fix resume mock session registration and RDRAM sizing **Acceptance coverage:** - Host path: "Resume Session" action in create-session flow (`.netplay-state` file picker mirroring select_rom); resume advertised to joiners via session `resume` feature; after the player-number handshake, host loads its own state, resets the GGRS frame counter, broadcasts via `netplay_state`, waits for every peer's `netplay_state_ready` ack (30s timeout → session abandoned, never started desynced). - Join path: peer receives `netplay_state` over the reliable channel, loads via `load_savestate_from_data`, acks `netplay_state_ready`. - Frame counter: both sides reset to the SAVED frame via issue #5's `reset_frame` (vendored ggrs, PR #9's patch — merged into this branch). - Onscreen messages: "Syncing session state…" → "Session synced — play resumes". - Stacked dependency branches per repo convention (merge order #14 → #15 → #13 → #12 → #9 → this PR). **Tests** (`cargo test --lib` on the local worktree, full lib suite): 18 passed / 0 failed - `resume_state_reaches_peer_with_same_frame_metadata` — mock 2-peer flow: host's in-memory savestate travels the chunked envelope, peer loads it, both agree on frame 12_345 and the peer's GGRS counter resumes there - `resume_host_loads_state_from_disk_and_resets_frame` — host path with real on-disk `.netplay-state`; device state + frame counter match the saved session - `ack_batch_counts_every_ack` — two acks in one poll both counted - `wait_for_ready_all_counts_acks_and_times_out` — timeout path - `resume_session_skips_non_resume_sessions` — regular netplay untouched - All existing netplay/savestates tests stay green `cargo build --bin gopher64` green; `git diff --check` clean on the issue-#4 commits. Notes for the reviewer: - This WSL box builds with local-only tweaks (`.cargo/config.toml`, `build.rs`, clang-18/pkg-config/fcroot env, `RUST_MIN_STACK=8388608` for test threads, `-lexpat` link arg) — none are part of this PR. - The `cdylib` (Android .so) target cannot link on this box (non-PIC static fontconfig/expat); bin + tests are the practical gate here. - Manual 2-player resume smoke test not run by the lane (no live clients per lane policy); the mocked 2-peer flow covers the protocol + state-load mechanics. One trailing-whitespace line in vendored `vendor/ggrs/examples/README.md` (from #5's branch) is the only `diff --check` nit in the stack.
pbwraith added the
manual-only
label 2026-08-26 20:25:38 +02:00
This repo is archived. You cannot comment on issues.
No Milestone
No project
No Assignees
1 Participants
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: pbwraith/gopher64-netplay#4
No description provided.