Desync auto-resync: client receives and loads full state (#7) #11
No reviewers
Labels
No Label
blocked
bounced
in-progress
in-review
manual-only
needs-guidance
netplay
p1
p2
p3
rust
No Milestone
No project
No Assignees
1 Participants
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: pbwraith/gopher64-netplay#11
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "issue-7"
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?
Desync auto-resync — client receives and loads state (#7)
Problem
The #6 implementation (host-side resync) only swapped the device state on the client side, missing rdp_state and ra_state. The frame number was not encoded in the message, so the client used
session.current_frame()as a fallback.Solution
Update the client-side
process_resync_loadto:reset_framewith the host's frameChanges
create_savestate_in_memory: Now includesrdp_stateandra_statein the compressed buffer (was only device + saves)handle_desync: Prepends the host's frame number as 4-byte big-endian i32 to theresync_statemessage dataprocess_resync_load:savestates::load_savestateexactly: includes vru, cheats, transferpaks, cart.rom, PIF channel reconnection, audio frequency updateui::video::load_stateand RA state viaretroachievements::load_statereset_framewith the host's frame (not the client's current frame)Dependencies
reset_frameand patched ggrsset_current_frameissue-6and extends the client-side loading. PR includes #5 and #6 changes.Tests
3 new tests:
test_resync_frame_encoding— frame number encoding/decoding round-trip through NetplayMessage serializationtest_resync_frame_encoding_zero— edge case: frame 0test_resync_frame_encoding_negative— edge case: negative frameCloses #7
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 #5Not merged this sweep, and not bounced — this PR has not been found faulty.
Two things block a verdict, both recorded in gopher64-netplay#16:
cargo checkfails on aclean
origin/mainbefore compiling any of this repo's code — the CPU ispre-AVX2 and
.cargo/config.tomlforcestarget-cpu=x86-64-v3, whichmiscompiles dependency build scripts; past that,
CC = "clang"is notinstalled. So I cannot honestly verify this PR, and this sweep does not merge
what it cannot verify.
src/netplay.rs. Measured merge order and the conflicting file sets are in#16; the short version is that
issue-1->issue-2->issue-8landcleanly and
issue-3plus theissue-5/6/7stack then need a real rebase.No action needed from you until #16 is resolved. Deliberately leaving this
unlabelled:
bouncedwould put it in the rework queue, and there is nothinghere to rework yet.
Parked on #23 (and you now need a rebase)
!11 is the one to carry this work forward -- it contains !10.
Two things to fix in ONE pass, not two
1. You now conflict with
main. !14 (issue-1, state-message types)merged a few minutes ago. Re-measured against the new
main(c5609be) witha real
git mergein a throwaway worktree -- not themergeableflag, whichstill reads
true:Both branches merged cleanly before !14 landed, so this is a fresh, legitimate
rebase need and not something you got wrong.
2. A rebase alone will not make the gate green.
cargo test --lib(gatestep 3) currently aborts the entire test binary with a stack overflow on any
test that constructs a
Device-- measured on !15 and !18, root-caused andfiled as #23. Your branches touch
src/device.rsandsrc/savestates.rs,so this very likely reaches you too.
I did not gate !10/!11 -- I stopped once the conflict was established,
because a gate against a tree I would have had to hand-resolve proves nothing
about your branch. So treat point 2 as "expected, unverified for these two",
not as a measured failure of your code.
Waiting for #23 before reworking means one rebase against a
mainthat canactually run tests, instead of a rebase now and another after #23 lands.
Relationship between these two PRs
git merge-base --is-ancestor origin/issue-6 origin/issue-7-> true.!11 already contains !10's commit
48598f4. Rework !11 and close !10 assubsumed; do not repair both.
Unblocked, and re-bounced with the specific thing left to do.
blockedcleared,
bouncedapplied -- a PR carrying neither label is invisible to theworker lane AND suppresses its own issue as a candidate, so this is the label
that makes the rework reachable.
The block is genuinely gone
#23is CLOSED.!24merged this sweep and I verified the gate now executesDevice-constructing tests instead of aborting:
So Desync auto-resync (host + client halves) can finally be gated on its merits.
What is actually left: a rebase, measured not guessed
Re-measured with a real
git merge origin/mainin the warm clone (NOT themergeableflag, which is unreliable in both directions):!10(issue-6) is closed as subsumed into this branch (--is-ancestor-> TRUE), so this PR is now the single carrier for that work.Second thing, in the SAME pass
Your Device-constructing test bodies should route through the helper
!24added, not rely on the gate's env var:
scripts/gate.shnow exportsRUST_MIN_STACK=8388608, which immunises theGATE -- but a bare
cargo test --libstill gives each test thread the 2 MiBlibtest default, and a stack overflow SIGABRTs the whole binary rather than
failing one test. I verified
with_big_stackcarries a full Device round tripat
RUST_MIN_STACK=2097152, so wrapping the body makes the test independent ofhow 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
mainis moving during this sweep (!24landed;!15is in the gate as Iwrite). Rebase onto whatever
origin/mainis when you pick this up andre-measure the conflict set -- do not work from the file list above as if it
were fixed.
Ordering note -- land !9 first, and one conflict hunk is a trap
Not a new bounce; this PR keeps its existing
bouncedfinding. Re-measured againstcurrent
main(c857fea) this sweep rather than trusting the last verdict:Adding what I learned reviewing the cluster as a whole:
1. Order.
issue-5is a strict ancestor of this branch(
git merge-base --is-ancestor origin/issue-5 origin/issue-7-> TRUE), and all threeopen PRs currently conflict on overlapping
src/files. I have bounced !9 witha 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.rshunk is not additive. If you hit it, do not"keep both sides": your side is the savestate-load success path,
main's sideis
show_savestate_load_failure(device, rewind);from#2's refactor of thefailure path. git aligned two unrelated regions. Keeping both splices the
netplay
reset_framecall into the failure branch. Takemain's structure andre-insert the netplay block into the success branch only. Full detail on !9.
3.
src/netplay.rsnow has a#[cfg(test)] mod testsonmain. A secondmod testsin the same file will not compile -- fold into the existing one.