Update the desync auto-resync client-side state loading to achieve full
parity with disk savestates:
- Include rdp_state and ra_state in create_savestate_in_memory (was
only device + saves)
- Encode host's frame number as 4-byte big-endian i32 prefix in the
resync_state message data so the client knows which frame to reset to
(was using session.current_frame() as fallback)
- Update process_resync_load to extract the frame from the message and
load all four state sections: device, saves, rdp_state, ra_state
- Add full device state swap in process_resync_load: includes vru,
cheats, transferpaks, cart.rom, PIF channel reconnection, and audio
frequency update — matching savestates::load_savestate exactly
- Restore RDP state via ui::video::load_state and RA state via
retroachievements::load_state
- Add tests: frame encoding/decoding round-trip, zero frame, negative
frame edge cases
Based on issue-6 branch (includes #5 frame reset and #6 host-side
resync).
Closes#7
When GGRS fires DesyncDetected, the host (player_number==0) snapshots
the current emulator state, sends it to all peers via the reliable
channel as a 'resync_state' message, and pauses the frame loop until
all peers acknowledge with 'resync_ready'. Non-host peers ignore the
desync event and wait for the host's state.
Changes:
- Add ResyncState enum (Idle, HostWaiting, ClientLoading) to Netplay
- In poll_clients DesyncDetected handler: host creates in-memory
savestate, sends resync_state message, enters HostWaiting state
- Add check_resync_messages: non-host receives resync_state and enters
ClientLoading; host receives resync_ready acks and resets frame when
all peers ack
- Add process_resync_load: non-host decompresses and loads received
state, calls reset_frame, sends resync_ready ack, resumes
- Add resyncing state check in advance_frame: pauses frame loop during
resync (like disconnected path), with 5s timeout -> disconnect
- Add create_savestate_in_memory: serializes Device via postcard +
compress without file I/O
- Call process_resync_load from vi.rs update_screen before normal frame
processing
- Add tests: ResyncState default/transitions, message serialization,
timeout logic, ack tracking
Based on issue-5 branch (includes GGRS frame reset from #5).
Closes#6
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