|
Download docs/research/QVM_USERCMD.md from nmarshall/urt-rs: direct link, hf CLI and curl.
- Browser
- Download file 7.73 kB
-
https://huggingface.co/nmarshall/urt-rs/resolve/main/docs/research/QVM_USERCMD.md
- Command line
-
hf download hf://nmarshall/urt-rs/docs/research/QVM_USERCMD.md
-
curl -L -o QVM_USERCMD.md https://huggingface.co/nmarshall/urt-rs/resolve/main/docs/research/QVM_USERCMD.md
7.73 kB
| # Native usercmd retrieval and game-QVM time prefix | |
| RE-405–407 / F-163–165. [qvm-usercmd.json](qvm-usercmd.json) retains verified Linux ELF64 retrieval graphs/trap cases and shipped qagame instruction evidence. Reproduce with `python3 scripts/analyze_qvm_usercmd.py`; verify with `python3 scripts/test_qvm_usercmd.py`. | |
| This extends [server acceptance and selector 7](USERCMD_ACCEPTANCE.md). Original executables, native callees and the complete QVM never execute. VM_ArgPtr results and cross-address-space mapping are supplied. Native retrieval instructions copy bytes, which are explicitly mapped into the synthetic QVM word memory; this is a bounded composition, not a running engine/VM bridge. Calls in QVM entry/wrapper slices are recorded. The time prefix stops before remaining simulation. | |
| ## QCMD-001 — Trap 36, index guards and native record copy (RE-405) | |
| The file-backed SV_GameSystemCalls switch slot 36 resolves to dedicated `0x40b7c8` / client-engine `0x473738`. The report verifies the slot's little-endian pointer against the source file and retains its file offset and case instructions. This case first converts argument slot 2 through VM_ArgPtr, then treats slot 1's low 32 bits as a signed client index. | |
| An index below zero or at least the signed sv_maxclients integer reaches `Com_Error(1, format, index)`. The analysis stops at that call and does not assume normal continuation; the native graph's syntactic fallthrough is not an error-recovery contract. Pointer conversion occurs before this guard even on the invalid-index paths. Translation validity, pointer masking/bounds and actual error unwinding remain open. | |
| Valid paths read the client-array base from svs+0x10, multiply the sign-extended index by **0x2df40**, and copy three 64-bit words from client +0x2081c/+0x20824/+0x2082c to the converted destination. The trap case sets return EAX to zero before its common epilogue jump. The standalone `SV_GetUsercmd` performs the same signed guards, explicit stride multiplication and 24-byte copy; it is not called by the inlined trap case. | |
| This independently confirms the earlier arithmetic **candidate stride as an explicit access stride** in these two native paths. Actual array allocation, valid slot lifetime and maximum-client policy still require recovery. | |
| 112 probes total (28 standalone and 28 trap cases per engine) cover indices -1/0/1/7/63/64/0x7fffffff against maxima 0/1/8/64. Independent tests verify guards, conversion requests, invalid destination retention, exact 24-byte copies, canaries and valid trap return zero. No Com_Error or VM_ArgPtr callee executes. | |
| ## QCMD-002 — Selector 7 wrapper and command placement (RE-406) | |
| The shipped qagame entry dispatch reads jump-table word at VM address 3564 and selects instruction 65 for selector 7. The case loads incoming local 48, forwards it in argument slot 8 and calls helper **155718**, then returns zero. This matches the native selector/client-index boundary recovered previously; the local/table mapping is checked against the source QVM and existing ABI report. | |
| Helper 155718 computes `entity = 34091368 + 880*index`, loads its client pointer from entity +520, and records trap target **-37** (trap 36) with `(index, client_pointer+472)`. Thus the native 24-byte stored usercmd is fetched into the game client record at +472. Entity indexing and pointer validity are supplied, not checked by this wrapper slice. | |
| After retrieval, the helper writes global time word 34992532 into client +4044. It calls helper **154089** with the entity pointer only when `(entity[+424] & 8) == 0` and global word **34084868 == 0**. Bit-8 and global-gate producer semantics remain open; names such as bot/synchronous-client mode require independent confirmation. Retrieval and callback-time storage happen even when those gates suppress the think call. | |
| 108 wrapper compositions (54 per engine) cover indices 0/1/63, flag words 0/8/9, gate words 0/1/-1 and time words 1000/0xffffffff. Native copy results become six QVM words under explicitly supplied address mapping. Independent checks verify trap arguments, every copied word, metadata time and exact think-call gating. Native client storage and QVM game-client storage have different bases/layouts; they must remain separate in a Rust port. | |
| ## QCMD-003 — Game-side time clamp, elapsed gate and alignment (RE-407) | |
| Helper 154089 begins with a 436-byte QVM frame. The analyzed prefix (154090 through boundary 154210) loads entity +520 and requires client[+468] ==2; other sampled states jump to the return tail at 155716 without this prefix's time mutations. | |
| For state 2, observed operations are sequential: | |
| 1. Write game time 34992532 to client +6172. | |
| 2. Preserve the raw command time from client +472 at client +6176. | |
| 3. Clamp command time downward when it exceeds signed32(game_time+200), then upward when it is below signed32(game_time-1000). | |
| 4. Compute signed32(command_time-client[+0]) as elapsed time. | |
| 5. If elapsed <1 and client[+3108] !=2, return after the preceding time stores/clamp. | |
| 6. Cap elapsed at 200. This modifies local 328, independently of the command timestamp. | |
| 7. If client[+712] is nonzero, set command time to signed32(trunc_toward_zero(signed32(command_time+7)/8)*8). | |
| The eight-unit adjustment occurs **after** elapsed calculation and its cap; this prefix does not recompute elapsed or reapply the clamp after rounding. For ordinary positive times this rounds upward to a multiple of eight. Negative/boundary inputs require the recovered signed/truncating expression rather than a generic ceiling formula. Client +0 is provisionally the player-state command time; the +3108 mode and +712 alignment flag still need producer/consumer confirmation. | |
| 147 probes cover three rejecting states, 128 ordinary combinations of command time/prior time/mode/alignment, four negative-time rounding fixtures, and twelve signed-boundary combinations near 0x7fffffff/0x80000000. Independent modular arithmetic predicts command time, raw/observed time stores, elapsed and early return. Overflow in the game-time bounds can produce unusual sequential-clamp behavior in synthetic boundary fixtures. These are modeled instruction results, not evidence of legitimate clock wrapping or runtime parity. | |
| The evaluator adds an optional explicit stop-target boundary, allowing state guards and the continuing prefix to stop before the remaining helper. Existing callers keep their original behavior/output. A focused test proves that an unsupported boundary instruction is not executed and still fails when the optional stop is absent. | |
| ## Validation and remaining work | |
| Eight tests verify native signed guards/pointer ordering/copy retention, file-backed trap slot and explicit stride, selector/table/argument mapping, wrapper copy/metadata/gates, game-side signed clamps/elapsed/mode/alignment, paired results, source identities and optional stop behavior. | |
| RE-404 remains open for complete callback/producer/allocation/runtime work. RE-408 must confirm the global/record flag meanings, recover command generation and all timestamp/elapsed consumers, continue helper154089 into movement/simulation, characterize selector7 trap/VM mutation effects and address translation, audit other trap36 callers, compose packets through native acceptance/retrieval/game-side processing and validate real traffic/all-platform behavior. Full entity/player/snapshot codecs and Rust/Bevy/Solari implementation remain open. | |
| Follow-up: [think gates](QVM_THINK_GATES.md) continues the timestamp prefix through special routing, command suppression, inactivity decisions and mode-word selection. Gravity/speed, complete movement inputs, action effects and simulation/runtime validation remain open. | |