From 3b5e0992414acf87ae2c3382127bee4eceee6c38 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 30 Jul 2026 11:01:10 +0200 Subject: [PATCH] test(physics): P4 review - RestrictionObj prevalence inspection over the installed cell DAT 103,766 of 729,888 EnvCells (14%, 1,293 landblocks - the entire housing estate, 0x70xxxxxx GUIDs) carry a baked RestrictionObj. The AP-71 gate as wired (CanMoveInto unmodeled, fail-closed) would therefore lock every housing interior for everyone; retail's CanMoveInto (0x0058da40) is fail-OPEN for unowned houses and for a null RestrictionDB. Fix directed back to the P4 implementer. Co-Authored-By: Claude Opus 5 --- ...RestrictionObjPrevalenceInspectionTests.cs | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 tests/AcDream.Core.Tests/Physics/RestrictionObjPrevalenceInspectionTests.cs diff --git a/tests/AcDream.Core.Tests/Physics/RestrictionObjPrevalenceInspectionTests.cs b/tests/AcDream.Core.Tests/Physics/RestrictionObjPrevalenceInspectionTests.cs new file mode 100644 index 00000000..dbd8b71d --- /dev/null +++ b/tests/AcDream.Core.Tests/Physics/RestrictionObjPrevalenceInspectionTests.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using AcDream.Core.Tests.Conformance; +using DatReaderWriter; +using DatReaderWriter.Options; +using Xunit; +using Xunit.Abstractions; + +namespace AcDream.Core.Tests.Physics; + +/// +/// AP-129 inspection (Campaign P P4 Opus review, 2026-07-30): measures how +/// many EnvCells in the INSTALLED cell DAT actually carry a baked +/// RestrictionObj. Decides the blast radius of the AP-71 +/// fail-closed gate: with CanMoveInto unmodeled (AP-129), every +/// restricted cell refuses entry for every non-bypass player — harmless +/// if the flag is rare/authored-for-housing-only, a live lockout if it is +/// broadly baked into ordinary interiors. Inspection only; skips cleanly +/// when the installed DATs are absent (CI), following the +/// DoorSetupGfxObjInspectionTests pattern. +/// +public sealed class RestrictionObjPrevalenceInspectionTests +{ + private readonly ITestOutputHelper _output; + + public RestrictionObjPrevalenceInspectionTests(ITestOutputHelper output) + => _output = output; + + [Fact] + public void CountRestrictionObjCells_InstalledCellDat() + { + string? datDir = ConformanceDats.ResolveDatDir(); + if (datDir is null) + return; // installed dats absent (CI) — skip cleanly + + using var dats = new DatCollection(datDir, DatAccessType.Read); + + // Bake-tool enumeration shape (BakeRunner.EnumerateEnvCellIds): + // LandBlockInfo (xxxxFFFE) NumCells ranges name every EnvCell id. + var landblockInfoIds = new List(); + foreach (var file in dats.Cell.Tree) + { + if ((file.Id & 0xFFFFu) == 0xFFFEu) + landblockInfoIds.Add(file.Id); + } + + int totalCells = 0; + int restricted = 0; + var restrictedLandblocks = new HashSet(); + var samples = new List(); + + foreach (uint infoId in landblockInfoIds) + { + if (!dats.Cell.TryGet( + infoId, out var info) + || info is null + || info.NumCells == 0) + { + continue; + } + + uint firstCellId = (infoId & 0xFFFF_0000u) | 0x0100u; + for (uint offset = 0; offset < info.NumCells; offset++) + { + uint cellId = firstCellId + offset; + if (!dats.Cell.TryGet( + cellId, out var cell) + || cell is null) + { + continue; + } + + totalCells++; + if (cell.RestrictionObj != 0) + { + restricted++; + restrictedLandblocks.Add(cellId & 0xFFFF_0000u); + if (samples.Count < 40) + { + samples.Add( + $"cell 0x{cellId:X8} restrictionObj 0x{cell.RestrictionObj:X8}"); + } + } + } + } + + _output.WriteLine( + $"EnvCells scanned: {totalCells}; with RestrictionObj != 0: " + + $"{restricted}; distinct landblocks: {restrictedLandblocks.Count}"); + foreach (string s in samples) + _output.WriteLine(s); + + Assert.True(totalCells > 0, "installed cell DAT enumerated no EnvCells"); + } +}