Gameplay Differential Capture¶
scripts/frida/gameplay_diff_capture.js captures deterministic gameplay ticks into a
JSONL event stream designed for robust incremental writes.
If you are starting from only a fresh capture artifact, follow
docs/frida/differential-playbook.md first.
Primary output:
C:\share\frida\gameplay_diff_capture.json
Attach:
Optional sidecar for unattended recordings:
Just shortcut (Windows VM):
Capture format¶
The capture file is newline-delimited JSON rows:
{"event":"capture_meta","capture":{...}}exactly once at start{"event":"tick","tick":{...}}once per captured gameplay tickcapture_meta.capture_format_versionis required and must match the current loader version (4).
uv run crimson original ... commands load this stream and normalize it to the
typed CaptureFile schema in Python (msgspec).
Notes:
- The file is streamed incrementally and flushed on each write.
- Console output is filtered by default to high-signal lifecycle/errors only.
- Before detaching from a live Frida session, call
crimsonCaptureStop("manual_stop")in the REPL and wait for thecapture_shutdownlog line. - Loader behavior is strict: truncated trailing JSON rows are rejected.
- Loader behavior is strict: only captures with the current
capture_format_versionare accepted. - Loader accepts only stream rows (
capture_meta+tick), not legacy monolithic JSON captures. - Current checkpoints include direct kill count, perk snapshot
(
pending_count/choices_dirty/choices/player_nonzero_counts), and per-player bonus timers in checkpoint player rows. - Entity
samplespayloads are strictly typed (creatures,projectiles,secondary_projectiles,bonuses): schema/script drift should be fixed in instrumentation and re-captured, not handled via parser fallbacks. - Creature sample/lifecycle payloads include AI lineage context
(
ai_mode,link_index,orbit_angle,orbit_radius,ai7_timer_ms) to diagnose spawn/link timer drift without replay-side guesswork. creature_update_microevent heads provide slot-level movement internals (creature_update_windowpre/post snapshots +angle_approachcall traces) and are enabled in default captures.- No top-level raw event stream is written; diagnostics stay in per-tick aggregates.
- Float precision contract: capture script emits memory-sourced float values as
tagged float32 bit tokens (
"f32:XXXXXXXX"). Tooling decodes these tokens at load time and treats decoded float32 values as authoritative. - Input metadata contract:
input_approxrows include per-playermove_modeandaim_schemesampled from config globals. Legacy captures may containnullfor these fields; use CLI--aim-scheme-player <PLAYER=SCHEME>overrides when replay synthesis needs known config values.
Convert to checkpoints + replay¶
uv run crimson original convert-capture \
artifacts/frida/share/gameplay_diff_capture.json \
analysis/frida/gameplay_diff_capture.checkpoints.json.gz
This also writes analysis/frida/gameplay_diff_capture.crdemo.gz by default
(override with --replay).
Verify capture directly against rewrite sim¶
Divergence report¶
uv run crimson original divergence-report \
artifacts/frida/share/gameplay_diff_capture.json \
--float-abs-tol 2e-3 \
--window 24 \
--lead-lookback 1024 \
--run-summary
Use --run-summary-short for a shorter narrative.
First-divergence bisect¶
uv run crimson original bisect-divergence \
artifacts/frida/share/gameplay_diff_capture.json \
--window-before 12 \
--window-after 6 \
--json-out
Focus tick trace¶
uv run crimson original focus-trace \
artifacts/frida/share/gameplay_diff_capture.json \
--tick 3453 \
--near-miss-threshold 0.35 \
--json-out
Creature trajectory trace¶
uv run crimson original creature-trajectory \
artifacts/frida/share/gameplay_diff_capture.json \
--creature-index 120 \
--json-out
Defaults¶
Without extra env vars, the script captures full per-tick detail:
before/aftersnapshots every captured tick- samples for
creatures,projectiles,secondary_projectiles,bonuses - unlimited head budgets by default (
-1limits) - RNG per-draw stream rows (
value/state_before/state_after/branch_id), caller diagnostics, mirror tracking, outside-tick carry - blood-splatter effect diagnostics (
effect_spawn_blood_splatter) with per-tick caller and RNG-draw attribution - perk-apply diagnostics and input query/key snapshots
- creature movement micro telemetry (
creature_update_window+angle_approach) with per-kind per-tick head cap (128) and no slot/window filtering
Optional env knobs¶
Creature micro hooks are not controlled via env knobs anymore; they are always captured by default for differential parity sessions.
CRIMSON_FRIDA_STATES=6,9,10CRIMSON_FRIDA_ALL_STATES=1CRIMSON_FRIDA_CONSOLE_ALL_EVENTS=1CRIMSON_FRIDA_CONSOLE_EVENTS=start,ready,capture_shutdown,error,hook_error,hook_skip,tickless_eventCRIMSON_FRIDA_TICK_DETAILS_EVERY=30CRIMSON_FRIDA_CREATURE_SAMPLE_LIMIT=24CRIMSON_FRIDA_PROJECTILE_SAMPLE_LIMIT=32CRIMSON_FRIDA_SECONDARY_PROJECTILE_SAMPLE_LIMIT=32CRIMSON_FRIDA_BONUS_SAMPLE_LIMIT=12CRIMSON_FRIDA_MAX_HEAD=-1CRIMSON_FRIDA_MAX_EVENTS_PER_TICK=-1CRIMSON_FRIDA_INPUT_HOOKS=0CRIMSON_FRIDA_RNG_HOOKS=0CRIMSON_FRIDA_EFFECTS=0CRIMSON_FRIDA_SPAWNS=0CRIMSON_FRIDA_CREATURE_SPAWN_HOOK=0CRIMSON_FRIDA_CREATURE_DEATH_HOOK=0CRIMSON_FRIDA_BONUS_SPAWN_HOOK=0CRIMSON_FRIDA_RNG_ROLL_LOG=0CRIMSON_FRIDA_MAX_RNG_ROLL_LOG_EVENTS=-1CRIMSON_FRIDA_RNG_HEAD=-1CRIMSON_FRIDA_RNG_CALLERS=-1CRIMSON_FRIDA_RNG_OUTSIDE_TICK_HEAD=-1CRIMSON_FRIDA_RNG_STATE_MIRROR=0CRIMSON_FRIDA_INCLUDE_BT=1CRIMSON_FRIDA_INCLUDE_CALLER=0
Capture loading in Python accepts .json and .json.gz only, with JSONL
row-stream capture rows as the canonical format.