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 <noreply@anthropic.com>
This commit is contained in:
parent
ce941de5cb
commit
3b5e099241
1 changed files with 94 additions and 0 deletions
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// AP-129 inspection (Campaign P P4 Opus review, 2026-07-30): measures how
|
||||
/// many EnvCells in the INSTALLED cell DAT actually carry a baked
|
||||
/// <c>RestrictionObj</c>. Decides the blast radius of the AP-71
|
||||
/// fail-closed gate: with <c>CanMoveInto</c> 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.
|
||||
/// </summary>
|
||||
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<uint>();
|
||||
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<uint>();
|
||||
var samples = new List<string>();
|
||||
|
||||
foreach (uint infoId in landblockInfoIds)
|
||||
{
|
||||
if (!dats.Cell.TryGet<DatReaderWriter.DBObjs.LandBlockInfo>(
|
||||
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<DatReaderWriter.DBObjs.EnvCell>(
|
||||
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");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue