acdream/tests/AcDream.Core.Tests/Conformance/ConformanceDats.cs
Erik 1662da8731 test(p0): threshold-trace golden wiring + PVS scaffold + P1-entry checklist
P0 Tasks 6 (autonomous half) + 7. FindCellList_DoorwayThreshold_MatchesRetailTrace
asserts acdream's pick == each captured retail pick; skips until the capture
fixture lands. PvsConformanceTests scaffolds the render visible-set golden
(skipped; filled in P4). ConformanceDats.FixturesDir resolves fixtures from the
source tree (issue98 pattern). Notes record: existing retail traces are
collision-only (no membership) so the strict P1 gate needs the one live capture;
plus the P1 re-scope finding (Stage-1 membership already on this branch).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:29:30 +02:00

87 lines
3.9 KiB
C#

using System;
using System.IO;
using System.Numerics;
using AcDream.Core.Physics;
using AcDream.Core.World.Cells;
using DatReaderWriter;
using DatReaderWriter.Options;
using DatEnvCell = DatReaderWriter.DBObjs.EnvCell;
using DatEnvironment = DatReaderWriter.DBObjs.Environment;
using Env = System.Environment;
namespace AcDream.Core.Tests.Conformance;
/// <summary>
/// P0 conformance apparatus — headless load of the real Holtburg dats.
/// Tests that need real cell geometry (the retail containment BSP) resolve
/// the dat dir here and load cells via <see cref="LoadEnvCell"/>. Returns
/// null dat dir when the dats are absent (CI) so callers can skip cleanly,
/// matching DoorBugTrajectoryReplayTests.ResolveDatDir.
///
/// Mirrors the proven dat-read pattern at
/// DoorBugTrajectoryReplayTests.cs:184-219 + EnvCell.FromDat (EnvCell.cs:42-76).
/// </summary>
public static class ConformanceDats
{
private const uint EnvironmentFilePrefix = 0x0D000000u; // dat namespace for Environment files
/// <summary>The Holtburg landblock these fixtures live in.</summary>
public const uint HoltburgLandblock = 0xA9B40000u;
/// <summary>Resolve the client dat directory, or null if unavailable (skip the test).</summary>
public static string? ResolveDatDir()
{
var fromEnv = Env.GetEnvironmentVariable("ACDREAM_DAT_DIR");
if (!string.IsNullOrWhiteSpace(fromEnv) && Directory.Exists(fromEnv))
return fromEnv;
var def = Path.Combine(
Env.GetFolderPath(Env.SpecialFolder.UserProfile),
"Documents", "Asheron's Call");
return Directory.Exists(def) ? def : null;
}
/// <summary>
/// The conformance fixtures dir in the SOURCE tree (mirrors
/// CellarUpTrajectoryReplayTests.FixtureDir — fixtures are read from source,
/// not copied to the output dir).
/// </summary>
public static string FixturesDir =>
Path.Combine(SolutionRoot(), "tests", "AcDream.Core.Tests", "Conformance", "Fixtures");
private static string SolutionRoot()
{
var dir = AppContext.BaseDirectory;
while (!string.IsNullOrEmpty(dir))
{
if (File.Exists(Path.Combine(dir, "AcDream.slnx")))
return dir;
dir = Path.GetDirectoryName(dir);
}
throw new InvalidOperationException(
"Could not locate AcDream.slnx from " + AppContext.BaseDirectory);
}
/// <summary>The physics-verbatim cell→world transform (no +2cm render lift).</summary>
public static Matrix4x4 WorldTransform(DatEnvCell datCell) =>
Matrix4x4.CreateFromQuaternion(datCell.Position.Orientation) *
Matrix4x4.CreateTranslation(datCell.Position.Origin);
/// <summary>
/// Load one EnvCell from the dats with its REAL containment BSP, and register
/// it into <paramref name="cache"/> as a CellPhysics. Returns the high-level
/// EnvCell (PointInCell) so a single load serves both membership predicates.
/// </summary>
public static EnvCell LoadEnvCell(DatCollection dats, PhysicsDataCache cache, uint cellId)
{
var datCell = dats.Get<DatEnvCell>(cellId)
?? throw new InvalidOperationException($"EnvCell 0x{cellId:X8} not found in dats");
var environment = dats.Get<DatEnvironment>(EnvironmentFilePrefix | datCell.EnvironmentId)
?? throw new InvalidOperationException($"Environment 0x{datCell.EnvironmentId:X8} not found");
if (!environment.Cells.TryGetValue(datCell.CellStructure, out var cellStruct) || cellStruct is null)
throw new InvalidOperationException($"CellStruct {datCell.CellStructure} missing from environment");
var world = WorldTransform(datCell);
cache.CacheCellStruct(cellId, datCell, cellStruct, world); // physics CellPhysics (real CellBSP)
return EnvCell.FromDat(cellId, datCell, cellStruct, world); // render/containment EnvCell (real ContainmentBsp)
}
}