urt-rs / docs /research /QVM_SAVED_RESTORE_BATCH.md
nmarshall's picture
gameplay: integrate connect session lifecycle branch
2b1ac7e
|
Raw History Blame Contribute Delete
8.69 kB

Userinfo, numeric scanning and restore: RE-894–903

Ten separately committed tasks recover original QVM persistence behavior and compare it with Rust across504 paired source cases. File handles, file bytes, engine userinfo/cvars and initial caller scratch are supplied inputs; native writes/reads/closes are captured as intentions. This evidence does not establish a playable game or native engine/file acceptance.

Userinfo and numeric parsing

70503 strips an optional initial backslash, walks complete key/value pairs, compares keys case-insensitively through actual69386 and returns the first matching value. An empty matched value has a different pointer result from a miss. Non-null calls XOR selector23712 between two8192-byte buffers even on a miss; null arguments preserve the selector. Embedded NUL ends logical strings. The finite corpus covers incomplete pairs, duplicate/case-varied keys, empty values and null arguments; oversized/fatal and alias domains remain open.

Numeric38326 delegates to actual38355, which calls37159 for integers and36779 for floats. The recovered comma-template subset does not consume integer signs or leading whitespace. It writes0 for an unparsed field without incrementing the assignment count, then a literal mismatch can return-1 after earlier writes. Integer digit accumulation wraps32 bits. The float path uses f32 at each accumulation/scaling step, supports signs/decimal whitespace, and stops at lowercase e. Empty or unparsed final fields can still write0. The caller's untouched words are explicit inputs to both the Rust API and the independent oracle.

Uppercase exponent handling, non-ASCII numeric text and other scanf conversions remain outside the Rust scanner domain. Unsupported uppercase E is checked at the active numeric cursor, so a valid six-float prefix followed by newline/PLAYER is accepted without scanning the name. The source restore fixtures and a Rust regression verify that unconsumed name suffix. This is a bounded comma-template port, not a general scanner implementation.

Saved-position file flow

246746 gates on mode9, then fetches userinfo and extracts cl_guid before testing enable>=1, nonempty GUID and any nonzero position component at offsets3716/3720/3724. Angles alone at3728/3732/3736 do not trigger saving. It reads mapname with capacity64, formats positions/map/GUID.pos through actual70458/37914, and opens in mode1. GUID bytes pass into this path unchanged in this body. The supplied open return is ignored; a nonzero handle requests actual70386/37914 float/name formatting, then native write and close. The file payload has six comma-separated source float values, newline, and the logical name at client offset504. Native strncpy remains modeled inside the bounded formatter wrapper.

246554 uses the same userinfo/path gates and opens in mode0. A nonzero handle plus signed length>0 and unsigned length<1023 permits reading. Actual38326 must report exactly6 assignments before all six position/angle words are copied. Malformed/truncated input leaves the previous six words. Valid-length read paths close after scanning, including failures; invalid-length exits issue no close in this body. Native handle ownership and the engine open/close API remain unverified, so the port preserves these observed intentions rather than inferring cleanup semantics.

The load integration exposed a fixture byte-coherence gap for short userinfo values: nested handlers now transfer the returned value's exact bytes/NUL as well as changed words. The two original info-buffer domains are explicitly initialized for the fixture snapshot, providing the optimized strlen word lookahead around short values. Existing saved-write fixtures were rerun; the emitted file events stayed unchanged. These are analysis-memory fixes, not modifications to the game binary.

Session restore and caller scratch

247219 requests sessionN and scans nine integers. It ignores the scanner result, stores the first temporary into field3096, and leaves the four direct destination fields subject to partial parsing. It converts the next three temporaries using signed>0; the final boolean additionally requires original33715 team classification and flag1 in35000468. A malformed session can therefore depend on previous caller-stack words. Fixtures supply two explicit scratch snapshots at the original local offsets instead of assuming initialized memory. The Rust API requires that snapshot and stages its state on unsupported-domain errors. Actual stack provenance and a production compatibility/default policy remain open.

sessIgnoreN is read separately. Actual36489 finds commas and37159 parses signed integer prefixes. Valid indices0..63 set the corresponding existing table word to1; the table is not cleared. Duplicate entries remain harmless repeated stores, negative/out-of-range indices are skipped, and invalid/empty numeric tokens can select index0. The root then executes actual246554 saved-position loading, including the original userinfo, path, scanner and file-ownership branches. The144 compositions vary modes, team flag, complete/partial/empty sessions, prior ignore words and scratch snapshots. Real cvar storage and an actual map-reload round trip remain open.

Validation

Each task used focused cargo xtask test-group <name> before its commit, with deterministic double report regeneration, relevant source/Rust tests, scoped gameplay tests and storage/documentation/whitespace guards. Groups are qvm-info-lookup, rust-info-lookup, qvm-numeric-scan, rust-numeric-scan, qvm-position-save, rust-position-save, qvm-position-load, rust-position-load, qvm-session-restore and rust-session-restore. Shared byte/oracle changes also received focused writer/scanner/load regression checks.

Dependency-selection regression coverage includes all five new source-to-Rust report pairs, native_policy consumers, formatting dependencies and intermission classification used by restore. Every Python process uses the resolved Nix runtime declared by flake.nix, with explicit PYTHON for xtask and Git hooks. No original asset payload changed. After commit ten, cargo xtask test-full --since 6e01995b passed once: 1,753 Python tests and199 Rust tests (assets10, gameplay159, xtask30). All22 affected report producers regenerated twice deterministically. Storage policy, links across469 documentation files and whitespace checks passed with no generated-report drift. These notes were amended into task ten.

Follow-up work (expanded by RE-904–913)

  • Recover uppercase exponent behavior and remaining38355 conversions, non-ASCII parsing, oversized/fatal userinfo and pointer/buffer alias contracts.
  • Trace actual scratch provenance on malformed session restore; decide a documented production compatibility/default policy after validating native behavior.
  • Recover session clear246938 callers and initialization, metadata producers and ignore-table mutations; execute write/restore across a real map reload.
  • Integrate actual246746 and246963 together in249007 and full normal/series6939 flows; carry native file/cvar ownership and real command dispatch.
  • Connect saved-file IO intentions to a platform-neutral engine adapter and validate real file-open return/handle/close semantics on Linux/macOS.
  • Replace supplied spatial-query answers with recovered native broadphase, integrate bot rotation/native genetic behavior, and recover221662 map-list parsing.
  • Compose complete respawn/begin-intermission, event lifetime and ranking/scoreboard/network execution.
  • Validate actual Linux/macOS Rust/Bevy/Solari gameplay/rendering. Full-game completion remains open.

RE-904–913 composes initialization, full persistence, byte-fed round trips and the bounded connect caller. Its RE-914 list supersedes the historical follow-up list above.