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>
94 lines
3.3 KiB
C#
94 lines
3.3 KiB
C#
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");
|
|
}
|
|
}
|