Allow savestate creation/loading during netplay (#2) #15
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#15
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "issue-2"
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?
Closes #2
Allows savestate creation/loading while a netplay session is active, fully
in-memory (no disk I/O).
Implementation
SavestateData::save_to_memory() -> Vec<u8>: serializes device + saves +rdp/ra state into the same compressed multi-entry format used for on-disk
savestates (postcard + zip), without touching the disk.
SavestateData::load_from_memory(&[u8]) -> Option<SavestateData>: reverseof the above.
create_savestate_in_memory(device) -> Option<SavestateData>: clones thedevice exactly like
create_savestate(reusing theDEVICE_CLONE/SAVES_CLONEstatics), returns the data instead of writing a file. Bothstatics are held for the whole capture so concurrent savestate callers get
a consistent snapshot.
load_savestate_from_data(device, &SavestateData) -> bool: thestd::mem::swapapply-block fromload_savestatewas refactored into ashared
apply_savestate()used by both the disk path and the in-memorypath.
process_savestatesroutes explicit save/load requests to the in-memorypath while
device.netplay.is_some(), storing the latest netplay snapshotin
Savestate.last_netplay_state(#[serde(skip)]). The automatic rewindpool stays offline-only (unchanged).
src/device/vi.rs:process_savestatesis now invoked during netplaytoo; the function itself keeps all offline behavior identical.
src/ui/video.rs: rdp state capture/restore/idle/onscreen are safe no-opswhile the parallel-RDP backend is not initialized, so in-memory state ops
work headless (tests) and before/after UI init.
Tests (cargo test, all passing)
netplay_in_memory_savestate_round_trip_restores_device: create ->save_to_memory -> load_from_memory -> apply with an active (mocked) netplay
session; verifies cpu regs, rdram bytes and vi regs round-trip.
netplay_process_savestates_uses_in_memory_snapshot: explicit save/loadrequests during netplay route through
process_savestatesto thein-memory snapshot and never touch the disk.
offline_explicit_save_stays_on_disk_path_...: offline explicit savestill writes the state file to disk and never populates the in-memory
slot (offline behavior unchanged).
Environment note (worker box only, not part of this diff): the local
build uses
-march=x86-64and no LTO because the WSL worker box lacks AVX2and rust-lld cannot consume gcc LTO sections; tests are run with
RUST_MIN_STACKraised for the postcard Device (de)serialization depth.Not 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: this PR's own test is the one that aborts the gate
Gating
main+!21+!22+!14+!15 in a warm clone on the review box:This is not a defect in your test.
size_of::<Device>()is 307,968 bytes,Device::new()builds it as a stack temporary before boxing it(
src/device.rs:345) in debug builds, andlibtestgives each test thread2 MiB. Measured:
RUST_MIN_STACK=4MiBoverflows,8MiBpasses -- the same8 MiB the repo already link-args onto Windows binaries. The identical test
passes unchanged with a bigger stack.
The same cause takes down !18 via a different test, and step 3 has never run a
real test on
main(it reports0 passed), which is why this surfaced now.Blocked on #23, which I filed with the full measurement. Not
bounced: there is nothing for this branch to repair -- the fix belongs inscripts/gate.sh/ a test helper, and doing it inside this PR would collide with #23's branch.The cluster map, measured (not inferred)
git merge-base --is-ancestoron every pair, plus a realgit mergeinto athrowaway worktree for every conflict claim -- Gitea's
mergeableflag saystruefor all ten of these and is not evidence either way:issue-1issue-2issue-3issue-5issue-4issue-6issue-7issue-8The four
vendor/ggrstrees on !9/!10/!11/!18 are byte-identical(tree
816fce1d), so the vendoring itself never conflicts -- every realconflict is in
src/netplay.rs,src/savestates.rs,src/device.rs.Each branch merges cleanly into
mainalone; they collide with eachother. 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.Merged. Gated against the MERGE RESULT on the post-!24 main.
First run discarded -- stale base
My first attempt at this gate warned
test_util/with_big_stack absenton themerge result, which was impossible if
!24was really onmain. Cause: Imerged
!24through the Gitea API, so the WINDOWS checkout that~/gate64fetches from still had
origin/main = ff5b4f3(pre-!24). Re-fetched(
ff5b4f3 -> d6569a1,src/test_util.rsPRESENT) and re-ran. Recording itbecause a stale base does not conflict and does not error -- it just quietly
gates a tree nobody proposed.
Gate
5 -> 8, all three new ones named in the output.
This is the PR that proves #23 is actually fixed
savestates::tests::netplay_in_memory_savestate_round_trip_restores_deviceisthe exact test #23 named as SIGABRTing the whole binary. It now runs and
passes. That is better evidence than !24's own synthetic tripwire, because it
is the real workload rather than a test written to demonstrate the problem.
One residual, filed as #25 rather than held
These tests pass because
scripts/gate.shexportsRUST_MIN_STACK=8388608--they do NOT route through
test_util::with_big_stack. So onmain, a barecargo test --libnow aborts:and takes the four not-yet-reported tests down with it. Merging anyway because
the project's documented gate command is
./scripts/gate.sh, that gate isgreen, and this unblocks the netplay stack -- but it is a real trap for anyone
who types
cargo test, so it is filed with the measurement and a three-linefix rather than left to be rediscovered.