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>
53 lines
1.8 KiB
Python
53 lines
1.8 KiB
Python
#!/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())
|