test(p0): retail-trace golden captured — membership criterion divergence pinned (P0 GATE MET)
P0 Task 6 complete. Captured live retail membership at the 0031<->0170<->0171 doorway via cdb on CPhysicsObj::change_cell (symbol-driven; offsets verified by discover-types.cdb; PDB MATCH). 22 transitions, clean monotonic sequence, NO ping-pong (retail is correct-by-construction). Golden: Conformance/Fixtures/find-cell-list-threshold.log. ROOT-CAUSE FINDING (the central P1 work): retail transitions membership at the PORTAL CROSSING (CEnvCell::find_transit_cells @ 0x52c820 pc:309968 — sphere crosses the doorway polygon plane), while acdream's FindCellList re-picks by POINT-IN-CELL containment at the foot. Retail commits room 0171 while the foot is STILL inside vestibule 0170's BSP (in_0171=0); acdream lags. ALL 22 transitions diverge for this one criterion mismatch — not a per-cell hysteresis or a building-entry-only split. This is master-plan §0 'hysteresis gap' confirmed against the real client. FindCellList_DoorwayThreshold_DivergesFromRetail_PendingP1 (documents-the-bug, GREEN) + ThresholdDivergenceDiagnosticTests (per-transition containment print) pin it; both flip when P1 ports the directed portal crossing. Conformance 59 pass / 1 skip / 0 fail; full Core 1308 pass / 5 fail (baseline) / 1 skip — no new failures. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1662da8731
commit
bb4dead0ae
7 changed files with 253 additions and 59 deletions
53
tools/cdb/decode_fcl_capture.py
Normal file
53
tools/cdb/decode_fcl_capture.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env python
|
||||
"""Decode a find-cell-list-capture.log into the golden fixture format.
|
||||
|
||||
The cdb capture (tools/cdb/find-cell-list-capture.cdb) writes player world
|
||||
origin as raw IEEE-754 hex:
|
||||
[fcl] seed=0xHHHHHHHH px=0xHHHHHHHH py=0xHHHHHHHH pz=0xHHHHHHHH picked=0xHHHHHHHH
|
||||
This rewrites px/py/pz to decimals (RetailTrace.ParseFindCellList format):
|
||||
[fcl] seed=0xHHHHHHHH px=<f> py=<f> pz=<f> picked=0xHHHHHHHH
|
||||
|
||||
Usage: py tools/cdb/decode_fcl_capture.py <capture.log> <out.log>
|
||||
"""
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
|
||||
LINE = re.compile(
|
||||
r"\[fcl\]\s+seed=0x([0-9a-fA-F]+)\s+px=0x([0-9a-fA-F]+)\s+py=0x([0-9a-fA-F]+)"
|
||||
r"\s+pz=0x([0-9a-fA-F]+)\s+picked=0x([0-9a-fA-F]+)")
|
||||
|
||||
|
||||
def hex_to_float(h: str) -> float:
|
||||
return struct.unpack("<f", struct.pack("<I", int(h, 16)))[0]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
src, dst = sys.argv[1], sys.argv[2]
|
||||
out = []
|
||||
seen = set()
|
||||
with open(src, "r", encoding="utf-8", errors="ignore") as f:
|
||||
for line in f:
|
||||
m = LINE.search(line)
|
||||
if not m:
|
||||
continue
|
||||
seed, px, py, pz, picked = m.groups()
|
||||
# Dedupe identical (seed, picked, rounded-pos) rows to keep the golden tight.
|
||||
x, y, z = hex_to_float(px), hex_to_float(py), hex_to_float(pz)
|
||||
key = (seed.lower(), picked.lower(), round(x, 2), round(y, 2), round(z, 2))
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
out.append(
|
||||
f"[fcl] seed=0x{int(seed,16):08X} "
|
||||
f"px={x:.4f} py={y:.4f} pz={z:.4f} "
|
||||
f"picked=0x{int(picked,16):08X}")
|
||||
with open(dst, "w", encoding="utf-8") as f:
|
||||
f.write("\n".join(out) + "\n")
|
||||
print("\n".join(out))
|
||||
print(f"\n{len(out)} unique picks -> {dst}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
17
tools/cdb/discover-types.cdb
Normal file
17
tools/cdb/discover-types.cdb
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
$$ Safe type-layout discovery (no breakpoint -> cannot freeze retail).
|
||||
$$ Confirms the @@c++ field paths the capture script depends on, then detaches.
|
||||
.sympath C:\Users\erikn\source\repos\acdream\refs
|
||||
.symopt+ 0x40
|
||||
.reload /f acclient.exe
|
||||
.echo ===PHYSOBJ===
|
||||
dt acclient!CPhysicsObj m_position cell
|
||||
.echo ===POSITION===
|
||||
dt acclient!Position
|
||||
.echo ===FRAME===
|
||||
dt acclient!Frame
|
||||
.echo ===VECTOR3===
|
||||
dt acclient!AC1Legacy::Vector3
|
||||
.echo ===OBJCELL===
|
||||
dt acclient!CObjCell pos
|
||||
.echo ===DONE===
|
||||
qd
|
||||
|
|
@ -1,42 +1,34 @@
|
|||
$$ ============================================================================
|
||||
$$ find-cell-list-capture.cdb — P0 conformance retail golden capture
|
||||
$$ ----------------------------------------------------------------------------
|
||||
$$ Captures retail's ACCEPTED membership decision at the cottage doorway so we
|
||||
$$ can pin acdream's CellTransit.FindCellList against it (P0 Task 6, the P1 gate).
|
||||
$$
|
||||
$$ Emits one line per accepted cell change in the golden format the parser reads
|
||||
$$ (tests/AcDream.Core.Tests/Conformance/RetailTrace.cs):
|
||||
$$ [fcl] seed=0xHHHHHHHH px=0x<hex> py=0x<hex> pz=0x<hex> picked=0xHHHHHHHH
|
||||
$$ where seed = the cell the player was in (old), picked = the cell committed (new),
|
||||
$$ and px/py/pz = the player world origin (raw IEEE-754 hex; decode offline with
|
||||
$$ tools/cdb/decode_retail_hex.py, then write decimals into the golden fixture).
|
||||
$$
|
||||
$$ Target: CPhysicsObj::change_cell @ 0x00513390 (pc:281192) — commit-on-diff.
|
||||
$$ It fires ONLY on an accepted cell transition, so the doorway crossing yields a
|
||||
$$ short, clean sequence (e.g. 0031 -> 0170 -> 0171), not a per-tick flood.
|
||||
$$ Captures retail's ACCEPTED membership transitions at a building doorway so we
|
||||
$$ can pin acdream's CellTransit.FindCellList against it (P0 Task 6, P1 gate).
|
||||
$$
|
||||
$$ Target: CPhysicsObj::change_cell @ 0x00513390 (pc:281192) — fires ONLY on an
|
||||
$$ accepted cell transition (the doorway crossings) -> short clean sequence.
|
||||
$$ thiscall: this (CPhysicsObj*) = @ecx ; arg new_cell (CObjCell*) = poi(@esp+4)
|
||||
$$
|
||||
$$ OFFSETS BELOW ARE PLACEHOLDERS — VERIFY LIVE (see README). At the first break:
|
||||
$$ dt acclient!CPhysicsObj @ecx $$ find m_position (Frame/Position) + cell
|
||||
$$ dt acclient!CObjCell poi(@esp+4) $$ find cell_id offset
|
||||
$$ dt acclient!Position <m_position> $$ find origin (x,y,z) float offsets
|
||||
$$ then edit CELLID_OFF / CELLPTR_OFF / POS_OFF and re-run.
|
||||
$$ Offsets VERIFIED live via tools/cdb/discover-types.cdb against the matched PDB:
|
||||
$$ CPhysicsObj.m_position @ +0x48 ; .cell @ +0x90
|
||||
$$ Position.objcell_id @ +0x04 ; .frame @ +0x08 ; Frame.m_fOrigin @ +0x34
|
||||
$$ CObjCell.pos @ +0x54 -> a cell's id = cell + 0x54 + 0x04 = cell + 0x58
|
||||
$$ player origin x/y/z = @ecx + 0x48 + 0x08 + 0x34 = @ecx + 0x84 (x), +0x88, +0x8c
|
||||
$$
|
||||
$$ seed = this->cell->pos.objcell_id (the cell BEFORE this change; null-guarded)
|
||||
$$ picked = new_cell->pos.objcell_id (the cell committed)
|
||||
$$ px/py/pz = player world origin as raw IEEE-754 hex (decode: decode_retail_hex.py)
|
||||
$$
|
||||
$$ Emits the golden format read by RetailTrace.ParseFindCellList:
|
||||
$$ [fcl] seed=0x<oldcell> px=0x<hex> py=0x<hex> pz=0x<hex> picked=0x<newcell>
|
||||
$$ ============================================================================
|
||||
|
||||
.logopen C:\Users\erikn\source\repos\acdream\find-cell-list-capture.log
|
||||
.logopen C:\Users\erikn\source\repos\acdream\.claude\worktrees\thirsty-goldberg-51bb9b\find-cell-list-capture.log
|
||||
.sympath C:\Users\erikn\source\repos\acdream\refs
|
||||
.symopt+ 0x40
|
||||
.reload /f acclient.exe
|
||||
|
||||
$$ ---- EDIT THESE after the dt dumps (hex byte offsets) ----------------------
|
||||
r $t4 = 0x18 $$ CELLID_OFF : CObjCell.cell_id (acclient.h:30938 — verify)
|
||||
r $t5 = 0x08 $$ CELLPTR_OFF : CPhysicsObj.cell (current cell ptr — verify)
|
||||
r $t6 = 0x1C $$ POS_OFF : CPhysicsObj.m_position.origin.x (verify; y=+4, z=+8)
|
||||
$$ ---------------------------------------------------------------------------
|
||||
|
||||
r $t0 = 0
|
||||
bp acclient!CPhysicsObj::change_cell "r $t0 = @$t0 + 1; r $t1 = poi(@ecx+@$t5); r $t2 = poi(@esp+4); .printf /D \"[fcl] seed=0x%08x px=0x%08x py=0x%08x pz=0x%08x picked=0x%08x\\n\", poi(@$t1+@$t4), poi(@ecx+@$t6), poi(@ecx+@$t6+4), poi(@ecx+@$t6+8), poi(@$t2+@$t4); .if (@$t0 >= 400) { .printf /D \"=== DETACH after %d cell changes ===\\n\", @$t0; qd } .else { gc }"
|
||||
bp acclient!CPhysicsObj::change_cell "r $t0 = @$t0 + 1; r $t1 = 0; .if (poi(@ecx+0x90) != 0) { r $t1 = poi(poi(@ecx+0x90)+0x58) }; .printf /D \"[fcl] seed=0x%08x px=0x%08x py=0x%08x pz=0x%08x picked=0x%08x\\n\", @$t1, poi(@ecx+0x84), poi(@ecx+0x88), poi(@ecx+0x8c), poi(poi(@esp+4)+0x58); .if (@$t0 >= 16) { .printf /D \"=== DETACH after %d cell changes ===\\n\", @$t0; qd } .else { gc }"
|
||||
|
||||
.printf \"find-cell-list capture armed (change_cell). Walk SLOWLY in/out of the cottage doorway now.\\n\"
|
||||
.printf \"find-cell-list capture armed (change_cell). Walk SLOWLY in/out of the doorway now.\\n\"
|
||||
g
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue