netplay: session resume — reload saved state and broadcast to peers (#4) #18

Open
pbwraith wants to merge 14 commits from issue-4 into main
Owner

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

Implements the full resume flow on the netplay state-transfer stack. This
branch stacks the dependency PRs per the repo convention (merge order:
#14 issue-1 → #15 issue-2 → #13 issue-3 → #12 issue-8 → #9 issue-5 → this PR):

  • #1 message types: netplay_state / netplay_state_ready + chunked codec
  • #2 in-memory savestates: save_to_memory / load_savestate_from_data
  • #3 on-disk .netplay-state envelope (encode/decode_netplay_session)
  • #8 UI session save/resume controls + resync status
  • #5 GGRS frame counter reset (netplay::reset_frame)

Host path:

  • "Resume Session" in the create-session flow opens a .netplay-state
    file picker (select_netplay_state, mirrors select_rom). The picked
    path marks the session as a resume, advertised to joining peers through
    the session resume feature.
  • After peers connect and the player-number handshake completes (start of
    run_game, before the frame loop), netplay::resume_session runs:
    • host reads the file, validates its ROM hash against the running ROM,
      loads the embedded savestate into its own device, resets the GGRS
      frame counter to the saved frame, broadcasts the file bytes verbatim
      via send_netplay_state, then waits for every peer's
      netplay_state_ready ack (30 s timeout — on timeout the session is
      disconnected rather than started desynced);
    • peers receive netplay_state, decode the same envelope, load the
      savestate from memory, reset the GGRS frame counter, and ack with
      netplay_state_ready.
  • Both sides record resume_frame = saved frame, so the frame loop
    resumes at the SAVED frame, not 0.
  • Onscreen messages: "Syncing session state…" → "Session synced — play
    resumes".

Fixes carried by this stack (found while verifying this branch):

  • wait_for_ready_all counts acks per decoded batch instead of removing
    them from the by-name messages map: several peers can ack within a
    single channel poll and the map would collapse them into one entry,
    causing a spurious 30 s timeout in 3+-player sessions.
  • savestates.rs: add the missing crate::netplay import for the
    reset_frame hook merged from #5 (that branch's tree does not compile
    standalone).

Tests (cargo test --lib, full lib suite, all green):

  • resume_state_reaches_peer_with_same_frame_metadata — mock 2-peer
    flow: host's in-memory savestate travels through the chunked
    encode/decode envelope, the peer loads it, both sides agree on frame
    metadata (frame 12_345) and the peer's GGRS counter resumes at the
    saved frame.
  • resume_host_loads_state_from_disk_and_resets_frame — host path with
    a real on-disk .netplay-state file; device state and GGRS frame
    counter match the saved session.
  • ack_batch_counts_every_ack — two acks arriving in one poll are both
    counted.
  • wait_for_ready_all_counts_acks_and_times_out — missing acks time out
    instead of hanging.
  • resume_session_skips_non_resume_sessions — regular netplay is
    untouched.
  • Existing netplay/savestates tests stay green.

Limitations (documented honestly):

  • Manual 2-player resume smoke test not run from this lane (no launching
    real clients per lane policy); the mocked 2-peer flow covers the
    protocol + state-load mechanics.
  • Local build tweaks on this box (.cargo/config.toml, build.rs x86-64
    flags, pkg-config path) are intentionally NOT part of this PR.
  • git diff --check clean on the issue-#4 commits (one pre-existing
    trailing-whitespace line in vendored vendor/ggrs from #5).
**Closes #4** — Session resume: reload saved state and broadcast to peers (host + join) Implements the full resume flow on the netplay state-transfer stack. This branch stacks the dependency PRs per the repo convention (merge order: #14 issue-1 → #15 issue-2 → #13 issue-3 → #12 issue-8 → #9 issue-5 → this PR): - #1 message types: `netplay_state` / `netplay_state_ready` + chunked codec - #2 in-memory savestates: `save_to_memory` / `load_savestate_from_data` - #3 on-disk `.netplay-state` envelope (`encode/decode_netplay_session`) - #8 UI session save/resume controls + resync status - #5 GGRS frame counter reset (`netplay::reset_frame`) Host path: - "Resume Session" in the create-session flow opens a `.netplay-state` file picker (`select_netplay_state`, mirrors `select_rom`). The picked path marks the session as a resume, advertised to joining peers through the session `resume` feature. - After peers connect and the player-number handshake completes (start of `run_game`, before the frame loop), `netplay::resume_session` runs: - host reads the file, validates its ROM hash against the running ROM, loads the embedded savestate into its own device, resets the GGRS frame counter to the saved frame, broadcasts the file bytes verbatim via `send_netplay_state`, then waits for every peer's `netplay_state_ready` ack (30 s timeout — on timeout the session is disconnected rather than started desynced); - peers receive `netplay_state`, decode the same envelope, load the savestate from memory, reset the GGRS frame counter, and ack with `netplay_state_ready`. - Both sides record `resume_frame` = saved frame, so the frame loop resumes at the SAVED frame, not 0. - Onscreen messages: "Syncing session state…" → "Session synced — play resumes". Fixes carried by this stack (found while verifying this branch): - `wait_for_ready_all` counts acks per decoded batch instead of removing them from the by-name `messages` map: several peers can ack within a single channel poll and the map would collapse them into one entry, causing a spurious 30 s timeout in 3+-player sessions. - `savestates.rs`: add the missing `crate::netplay` import for the `reset_frame` hook merged from #5 (that branch's tree does not compile standalone). Tests (`cargo test --lib`, full lib suite, all green): - `resume_state_reaches_peer_with_same_frame_metadata` — mock 2-peer flow: host's in-memory savestate travels through the chunked encode/decode envelope, the peer loads it, both sides agree on frame metadata (frame 12_345) and the peer's GGRS counter resumes at the saved frame. - `resume_host_loads_state_from_disk_and_resets_frame` — host path with a real on-disk `.netplay-state` file; device state and GGRS frame counter match the saved session. - `ack_batch_counts_every_ack` — two acks arriving in one poll are both counted. - `wait_for_ready_all_counts_acks_and_times_out` — missing acks time out instead of hanging. - `resume_session_skips_non_resume_sessions` — regular netplay is untouched. - Existing netplay/savestates tests stay green. Limitations (documented honestly): - Manual 2-player resume smoke test not run from this lane (no launching real clients per lane policy); the mocked 2-peer flow covers the protocol + state-load mechanics. - Local build tweaks on this box (`.cargo/config.toml`, `build.rs` x86-64 flags, pkg-config path) are intentionally NOT part of this PR. - `git diff --check` clean on the issue-#4 commits (one pre-existing trailing-whitespace line in vendored `vendor/ggrs` from #5).
pbwraith added 15 commits 2026-08-25 23:38:09 +02:00
Fix GGRS frame counter reset after state load (#5)
Some checks failed
Rust / build-linux (linux-x86_64, X64, ubuntu-24.04, x86_64-unknown-linux-gnu) (pull_request) Failing after 5s
Rust / build-android (29.0.14206865, ubuntu-24.04, 33) (pull_request) Failing after 2s
Rust-lint / lint-linux (pull_request) Failing after 2s
Rust / build-linux (linux-aarch64, ARM64, ubuntu-24.04-arm, aarch64-unknown-linux-gnu) (pull_request) Has been cancelled
Rust / build-windows (windows-aarch64, woa64, windows-11-arm, aarch64-pc-windows-msvc) (pull_request) Has been cancelled
Rust / build-windows (windows-x86_64, win64, windows-2025, x86_64-pc-windows-msvc) (pull_request) Has been cancelled
Rust / build-macos (macos-aarch64, ARM64, aarch64-apple-darwin) (pull_request) Has been cancelled
Rust / Collect release artifacts (pull_request) Has been cancelled
eb5cf56ee3
After a netplay savestate load, the GGRS session's internal frame counter
must be reset to match the savestate's frame, or desync detection fires
immediately on resume.

Changes:
- Patch vendored ggrs 0.13.0 to expose set_current_frame(frame) on
  P2PSession and SyncTestSession, with internal SyncLayer and InputQueue
  reset methods to clear stale saved states, checksum history, and input
  queues
- Add [patch.crates-io] ggrs = { path = 'vendor/ggrs' } in Cargo.toml
- Add netplay::reset_frame(session, frame) wrapper function
- Record current GGRS frame in Savestate.netplay_frame during
  create_savestate when in netplay context
- Call reset_frame after load_savestate in netplay context
- Add tests: advance 120 frames, reset to 60, verify current_frame()==60
  and clean resume with matching inputs; reset to 0; idempotent reset

Closes #5
UI: session save/resume controls + resync status (#8)
Some checks failed
Rust-lint / lint-linux (pull_request) Failing after 1s
Rust / build-linux (linux-x86_64, X64, ubuntu-24.04, x86_64-unknown-linux-gnu) (pull_request) Failing after 1s
Rust / build-android (29.0.14206865, ubuntu-24.04, 33) (pull_request) Failing after 1s
Rust / build-linux (linux-aarch64, ARM64, ubuntu-24.04-arm, aarch64-unknown-linux-gnu) (pull_request) Has been cancelled
Rust / build-windows (windows-aarch64, woa64, windows-11-arm, aarch64-pc-windows-msvc) (pull_request) Has been cancelled
Rust / build-windows (windows-x86_64, win64, windows-2025, x86_64-pc-windows-msvc) (pull_request) Has been cancelled
Rust / build-macos (macos-aarch64, ARM64, aarch64-apple-darwin) (pull_request) Has been cancelled
Rust / Collect release artifacts (pull_request) Has been cancelled
0ac1cfb679
Add UI controls for the netplay session save/resume and resync
indicator features:

- netplay_create.slint: Add 'Resume Session...' button with file picker
  callback for .netplay-state files
- netplay_wait.slint: Add 'Save Session' button (host only, gated by
  is_host property), add resyncing indicator badge
- netplay_page.slint: Add resyncing property to NetplayData global
- src/ui/netplay.rs: Wire on_netplay_resume_session callback (opens
  file picker, placeholder for #3 flow), on_netplay_save_session
  callback (placeholder for #3 flow), set is_host on wait window setup

The save/resume callbacks show placeholder messages until #3 is
implemented. The resync indicator shows during ResyncState != Idle.

Closes #8
netplay: session save to disk (host) [issue-3]
Some checks failed
Rust-lint / lint-linux (pull_request) Failing after 1s
Rust / build-linux (linux-x86_64, X64, ubuntu-24.04, x86_64-unknown-linux-gnu) (pull_request) Failing after 1s
Rust / build-android (29.0.14206865, ubuntu-24.04, 33) (pull_request) Failing after 1s
Rust / build-linux (linux-aarch64, ARM64, ubuntu-24.04-arm, aarch64-unknown-linux-gnu) (pull_request) Has been cancelled
Rust / build-windows (windows-aarch64, woa64, windows-11-arm, aarch64-pc-windows-msvc) (pull_request) Has been cancelled
Rust / build-windows (windows-x86_64, win64, windows-2025, x86_64-pc-windows-msvc) (pull_request) Has been cancelled
Rust / build-macos (macos-aarch64, ARM64, aarch64-apple-darwin) (pull_request) Has been cancelled
Rust / Collect release artifacts (pull_request) Has been cancelled
15fc132cf2
Add host-side netplay session save:
- storage::paths.netplay_state_file_path next to other state files
- savestates::create_savestate_in_memory + SavestateData::save_to_memory
  (in-memory capture, same zip layout as on-disk savestates)
- netplay::save_netplay_session(device, path): postcard-encodes
  {state, frame, input_delay, rom_hash, players}, zstd-compresses and
  writes atomically (tmp + rename); onscreen 'Session saved — safe to quit'
- only the host (player_number 0) may save; others get an onscreen notice
- configurable hotkey (emulation.netplay_save_key, default F7), edge
  triggered in ui::input::get and consumed per-frame in check_callback
- unit tests: encode/decode round-trip + file metadata verification
netplay: add state-message types to reliable channel protocol (#1)
Some checks failed
Rust-lint / lint-linux (pull_request) Failing after 1s
Rust / build-linux (linux-x86_64, X64, ubuntu-24.04, x86_64-unknown-linux-gnu) (pull_request) Failing after 1s
Rust / build-android (29.0.14206865, ubuntu-24.04, 33) (pull_request) Failing after 1s
Rust / build-linux (linux-aarch64, ARM64, ubuntu-24.04-arm, aarch64-unknown-linux-gnu) (pull_request) Has been cancelled
Rust / build-windows (windows-aarch64, woa64, windows-11-arm, aarch64-pc-windows-msvc) (pull_request) Has been cancelled
Rust / build-windows (windows-x86_64, win64, windows-2025, x86_64-pc-windows-msvc) (pull_request) Has been cancelled
Rust / build-macos (macos-aarch64, ARM64, aarch64-apple-darwin) (pull_request) Has been cancelled
Rust / Collect release artifacts (pull_request) Has been cancelled
891d12c08a
Add message-name constants and reliable-channel helpers for netplay
session state transfer:

- Message-name constants: netplay_state, netplay_state_ready,
  resync_state, resync_ready
- Chunked message codec (encode_message_chunks / decode_message /
  decode_packets) reusing the existing 16 KiB reliable-channel
  transport; send_message/process_reliable_messages refactored onto it
- send_netplay_state / receive_netplay_state for full session state
- send_ready / wait_for_ready ack helpers (10s timeout, same pattern
  as receive_message); receive_message extracted to wait_for_message
  with preserve of input_delay/disconnect side effects
- Large payloads (>1MB) accumulate into incoming_message until a full
  postcard message parses
- Unit tests: 5MB payload round-trip through the chunked codec, ack
  send/receive within timeout, missing ack times out with the standard
  panic pattern

Closes #1
Allow savestate creation/loading during netplay (#2)
Some checks failed
Rust-lint / lint-linux (pull_request) Failing after 1s
Rust / build-linux (linux-x86_64, X64, ubuntu-24.04, x86_64-unknown-linux-gnu) (pull_request) Failing after 1s
Rust / build-android (29.0.14206865, ubuntu-24.04, 33) (pull_request) Failing after 1s
Rust / build-linux (linux-aarch64, ARM64, ubuntu-24.04-arm, aarch64-unknown-linux-gnu) (pull_request) Has been cancelled
Rust / build-windows (windows-aarch64, woa64, windows-11-arm, aarch64-pc-windows-msvc) (pull_request) Has been cancelled
Rust / build-windows (windows-x86_64, win64, windows-2025, x86_64-pc-windows-msvc) (pull_request) Has been cancelled
Rust / build-macos (macos-aarch64, ARM64, aarch64-apple-darwin) (pull_request) Has been cancelled
Rust / Collect release artifacts (pull_request) Has been cancelled
a0dce58c55
# Conflicts:
#	src/netplay.rs
#	src/savestates.rs
# Conflicts:
#	src/device.rs
#	src/netplay.rs
#	src/savestates.rs
wait_for_ready_all counted acks by removing them from the by-name
messages map, which collapses every ack that arrives in the same
channel poll into one entry: with 3+ players the host could
undercount and time out a session whose peers all synced. Count the
matching messages of each decoded batch instead (process_reliable_messages
now returns the batch it decoded).
The GGRS frame-reset hook merged from issue #5 (netplay_frame field +
netplay::reset_frame in load_savestate_from_data) is missing the
crate::netplay import, so the merged tree does not compile. One-line
fix carried by the session-resume stack.
tests: fix resume mock session registration and RDRAM sizing
Some checks failed
Rust / build-linux (linux-x86_64, X64, ubuntu-24.04, x86_64-unknown-linux-gnu) (pull_request) Failing after 2s
Rust / build-android (29.0.14206865, ubuntu-24.04, 33) (pull_request) Failing after 1s
Rust-lint / lint-linux (pull_request) Failing after 1s
Rust / build-linux (linux-aarch64, ARM64, ubuntu-24.04-arm, aarch64-unknown-linux-gnu) (pull_request) Has been cancelled
Rust / build-windows (windows-aarch64, woa64, windows-11-arm, aarch64-pc-windows-msvc) (pull_request) Has been cancelled
Rust / build-windows (windows-x86_64, win64, windows-2025, x86_64-pc-windows-msvc) (pull_request) Has been cancelled
Rust / build-macos (macos-aarch64, ARM64, aarch64-apple-darwin) (pull_request) Has been cancelled
Rust / Collect release artifacts (pull_request) Has been cancelled
79e88dc821
mock_netplay built a one-player session for a local handle of 1,
which the vendored ggrs fork rejects; register every handle (the
local player at player_number, dummy Remote PeerIds elsewhere).
The resume tests loaded saved states into fresh Device::new(false)
instances whose RDRAM backing store is empty, tripping
copy_from_slice in apply_savestate; pre-size the RDRAM like a real
emulated device, matching the saved session's RDRAM.
Author
Owner

Parked on #23: gate step 3 aborts, and it is not your bug

Gating main+!21+!22+!18:

GATE step 3/3: cargo test --lib
test netplay::tests::ack_batch_counts_every_ack ... ok
... 11 tests pass ...
thread 'netplay::tests::resume_host_loads_state_from_disk_and_resets_frame' has overflowed its stack
fatal runtime error: stack overflow, aborting
RESULT gate_exit=101

Eleven of your tests pass first; the twelfth builds two Devices
(src/netplay.rs:1353 and :1382) and exceeds the 2 MiB libtest thread
stack. At RUST_MIN_STACK=8388608 that exact test passes unchanged, so this
is a stack-budget problem, not a logic one. Full numbers in #23.

Because a stack overflow SIGABRTs the whole binary, this also hides whatever
would have run after it -- so a green step 3 here is not available until #23
lands, and I could not assess the rest of your test suite.

Blocked on #23, which I filed with the full measurement. Not bounced: there is nothing for this branch to repair -- the fix belongs in scripts/gate.sh / a test helper, and doing it inside this PR would collide with #23's branch.

You are the one to land after #23. !18 already contains !14/!15/!13/!9 as
merge commits, so it delivers #1-#5 in one coherent tree; the others should be
closed as subsumed rather than merged. !14 is already on main, which your
branch contains, so that merge will be trivial.

The cluster map, measured (not inferred)

git merge-base --is-ancestor on every pair, plus a real git merge into a
throwaway worktree for every conflict claim -- Gitea's mergeable flag says
true for all ten of these and is not evidence either way:

PR branch relationship
!14 issue-1 contained in !18; gates green alone -> merged
!15 issue-2 contained in !18
!13 issue-3 contained in !18
!9 issue-5 contained in !18
!18 issue-4 superset of !14/!15/!13/!9
!10 issue-6 contained in !11
!11 issue-7 superset of !10
!12 issue-8 has moved past !18's copy (6 commits, incl. a review-fix)

The four vendor/ggrs trees on !9/!10/!11/!18 are byte-identical
(tree 816fce1d), so the vendoring itself never conflicts -- every real
conflict is in src/netplay.rs, src/savestates.rs, src/device.rs.

Each branch merges cleanly into main alone; they collide with each
other. So the order is forced regardless of this issue: one lands, the rest
rebase.

Suggested resolution once #23 lands

Land !18 (it delivers #1/#2/#3/#4/#5 in one self-consistent tree), then
close !15/!13/!9 as subsumed rather than merging them -- their commits are
already ancestors of issue-4. Then rebase !11 (which carries !10) and !12.

## Parked on #23: gate step 3 aborts, and it is not your bug Gating `main`+!21+!22+**!18**: ``` GATE step 3/3: cargo test --lib test netplay::tests::ack_batch_counts_every_ack ... ok ... 11 tests pass ... thread 'netplay::tests::resume_host_loads_state_from_disk_and_resets_frame' has overflowed its stack fatal runtime error: stack overflow, aborting RESULT gate_exit=101 ``` Eleven of your tests pass first; the twelfth builds **two** `Device`s (`src/netplay.rs:1353` and `:1382`) and exceeds the 2 MiB libtest thread stack. At `RUST_MIN_STACK=8388608` that exact test passes unchanged, so this is a stack-budget problem, not a logic one. Full numbers in #23. Because a stack overflow SIGABRTs the whole binary, this also hides whatever would have run after it -- so a green step 3 here is not available until #23 lands, and I could not assess the rest of your test suite. **Blocked on #23**, which I filed with the full measurement. Not `bounced`: there is nothing for this branch to repair -- the fix belongs in `scripts/gate.sh` / a test helper, and doing it inside this PR would collide with #23's branch. **You are the one to land after #23.** !18 already contains !14/!15/!13/!9 as merge commits, so it delivers #1-#5 in one coherent tree; the others should be closed as subsumed rather than merged. !14 is already on `main`, which your branch contains, so that merge will be trivial. ### The cluster map, measured (not inferred) `git merge-base --is-ancestor` on every pair, plus a real `git merge` into a throwaway worktree for every conflict claim -- Gitea's `mergeable` flag says `true` for all ten of these and is not evidence either way: | PR | branch | relationship | |---|---|---| | !14 | `issue-1` | **contained in !18**; gates green alone -> merged | | !15 | `issue-2` | **contained in !18** | | !13 | `issue-3` | **contained in !18** | | !9 | `issue-5` | **contained in !18** | | !18 | `issue-4` | superset of !14/!15/!13/!9 | | !10 | `issue-6` | **contained in !11** | | !11 | `issue-7` | superset of !10 | | !12 | `issue-8` | has moved past !18's copy (6 commits, incl. a review-fix) | The four `vendor/ggrs` trees on !9/!10/!11/!18 are **byte-identical** (tree `816fce1d`), so the vendoring itself never conflicts -- every real conflict is in `src/netplay.rs`, `src/savestates.rs`, `src/device.rs`. Each branch merges cleanly into `main` **alone**; they collide with each other. So the order is forced regardless of this issue: one lands, the rest rebase. ### Suggested resolution once #23 lands Land **!18** (it delivers #1/#2/#3/#4/#5 in one self-consistent tree), then close !15/!13/!9 as subsumed rather than merging them -- their commits are already ancestors of `issue-4`. Then rebase !11 (which carries !10) and !12.
pbwraith added the
blocked
label 2026-08-26 08:39:59 +02:00
Author
Owner

Unblocked, and re-bounced with the specific thing left to do. blocked
cleared, bounced applied -- a PR carrying neither label is invisible to the
worker lane AND suppresses its own issue as a candidate, so this is the label
that makes the rework reachable.

The block is genuinely gone

#23 is CLOSED. !24 merged this sweep and I verified the gate now executes
Device-constructing tests instead of aborting:

step 3:  running 5 tests  (was 4)
  test_util::tests::device_round_trip_fits_under_the_8_mib_test_stack_budget ... ok
  test result: ok. 5 passed; 0 failed

So Session resume, carrying #5 (frame reset) and #3 (session save) can finally be gated on its merits.

What is actually left: a rebase, measured not guessed

Re-measured with a real git merge origin/main in the warm clone (NOT the
mergeable flag, which is unreliable in both directions):

branch is 13-14 commits behind origin/main
git merge origin/main  ->  CONFLICTS
    src/ui/gui/appwindow.slint
    src/ui/gui/netplay_create.slint
    src/ui/netplay.rs

Second thing, in the SAME pass

Your Device-constructing test bodies should route through the helper !24
added, not rely on the gate's env var:

use crate::test_util::with_big_stack;

#[test]
fn my_test() {
    with_big_stack(|| {
        // existing body, unchanged
    });
}

scripts/gate.sh now exports RUST_MIN_STACK=8388608, which immunises the
GATE -- but a bare cargo test --lib still gives each test thread the 2 MiB
libtest default, and a stack overflow SIGABRTs the whole binary rather than
failing one test. I verified with_big_stack carries a full Device round trip
at RUST_MIN_STACK=2097152, so wrapping the body makes the test independent of
how it is invoked. Do both in one pass -- a rebase now and a wrapper later is
two conflict resolutions on the same lines.

Note on the target

main is moving during this sweep (!24 landed; !15 is in the gate as I
write). Rebase onto whatever origin/main is when you pick this up and
re-measure the conflict set -- do not work from the file list above as if it
were fixed.

Unblocked, and re-bounced with the specific thing left to do. `blocked` cleared, `bounced` applied -- a PR carrying neither label is invisible to the worker lane AND suppresses its own issue as a candidate, so this is the label that makes the rework reachable. ## The block is genuinely gone `#23` is CLOSED. `!24` merged this sweep and I verified the gate now executes Device-constructing tests instead of aborting: step 3: running 5 tests (was 4) test_util::tests::device_round_trip_fits_under_the_8_mib_test_stack_budget ... ok test result: ok. 5 passed; 0 failed So Session resume, carrying #5 (frame reset) and #3 (session save) can finally be gated on its merits. ## What is actually left: a rebase, measured not guessed Re-measured with a real `git merge origin/main` in the warm clone (NOT the `mergeable` flag, which is unreliable in both directions): branch is 13-14 commits behind origin/main git merge origin/main -> CONFLICTS src/ui/gui/appwindow.slint src/ui/gui/netplay_create.slint src/ui/netplay.rs ## Second thing, in the SAME pass Your Device-constructing test bodies should route through the helper `!24` added, not rely on the gate's env var: use crate::test_util::with_big_stack; #[test] fn my_test() { with_big_stack(|| { // existing body, unchanged }); } `scripts/gate.sh` now exports `RUST_MIN_STACK=8388608`, which immunises the GATE -- but a bare `cargo test --lib` still gives each test thread the 2 MiB libtest default, and a stack overflow SIGABRTs the whole binary rather than failing one test. I verified `with_big_stack` carries a full Device round trip at `RUST_MIN_STACK=2097152`, so wrapping the body makes the test independent of how it is invoked. Do both in one pass -- a rebase now and a wrapper later is two conflict resolutions on the same lines. ## Note on the target `main` is moving during this sweep (`!24` landed; `!15` is in the gate as I write). Rebase onto whatever `origin/main` is when you pick this up and re-measure the conflict set -- do not work from the file list above as if it were fixed.
pbwraith added
bounced
and removed
blocked
labels 2026-08-26 13:27:29 +02:00
Author
Owner

Ordering note -- land !9 first, and one conflict hunk is a trap

Not a new bounce; this PR keeps its existing bounced finding. Re-measured against
current main (c857fea) this sweep rather than trusting the last verdict:

CONFLICTS: 3  ->  src/ui/gui/appwindow.slint, src/ui/gui/netplay_create.slint, src/ui/netplay.rs

Adding what I learned reviewing the cluster as a whole:

1. Order. issue-5 is a strict ancestor of this branch
(git merge-base --is-ancestor origin/issue-5 origin/issue-4 -> TRUE), and all three
open PRs currently conflict on overlapping src/ files. I have bounced !9 with
a full resolution recipe and it should land first; rebasing this branch afterwards
means merging over content it already contains, which is much cheaper than
resolving the same hunks here independently. Wait for !9 unless you are told
otherwise.

2. The src/savestates.rs hunk is not additive. If you hit it, do not
"keep both sides": your side is the savestate-load success path, main's side
is show_savestate_load_failure(device, rewind); from #2's refactor of the
failure path. git aligned two unrelated regions. Keeping both splices the
netplay reset_frame call into the failure branch. Take main's structure and
re-insert the netplay block into the success branch only. Full detail on !9.

3. src/netplay.rs now has a #[cfg(test)] mod tests on main. A second
mod tests in the same file will not compile -- fold into the existing one.

## Ordering note -- land !9 first, and one conflict hunk is a trap Not a new bounce; this PR keeps its existing `bounced` finding. Re-measured against current `main` (`c857fea`) this sweep rather than trusting the last verdict: CONFLICTS: 3 -> src/ui/gui/appwindow.slint, src/ui/gui/netplay_create.slint, src/ui/netplay.rs Adding what I learned reviewing the cluster as a whole: **1. Order.** `issue-5` is a strict ancestor of this branch (`git merge-base --is-ancestor origin/issue-5 origin/issue-4` -> TRUE), and all three open PRs currently conflict on overlapping `src/` files. I have bounced **!9** with a full resolution recipe and it should land first; rebasing this branch afterwards means merging over content it already contains, which is much cheaper than resolving the same hunks here independently. **Wait for !9 unless you are told otherwise.** **2. The `src/savestates.rs` hunk is not additive.** If you hit it, do not "keep both sides": your side is the savestate-load **success** path, `main`'s side is `show_savestate_load_failure(device, rewind);` from `#2`'s refactor of the **failure** path. git aligned two unrelated regions. Keeping both splices the netplay `reset_frame` call into the failure branch. Take `main`'s structure and re-insert the netplay block into the success branch only. Full detail on !9. **3. `src/netplay.rs` now has a `#[cfg(test)] mod tests` on `main`.** A second `mod tests` in the same file will not compile -- fold into the existing one.
pbwraith added the
manual-only
label 2026-08-26 20:25:27 +02:00
This repo is archived. You cannot comment on pull requests.
No description provided.