refactor(physics #145): Slice 1 — rename Frame->CellFrame to avoid DatReaderWriter.Types.Frame collision
The new value type collided with DatReaderWriter.Types.Frame (used in physics-adjacent code like ShadowShapeBuilder), which the structural fix (per-file using-aliases across 6 files) would have re-incurred in every later physics slice. Renamed the TYPE to CellFrame; the Position.Frame MEMBER keeps retail's name. Restored the 5 alias-only files to their pre-Slice-1 state; synced spec + plan. Core 1522 passed / 0 failed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
438bb681a5
commit
c980763322
8 changed files with 51 additions and 47 deletions
|
|
@ -30,7 +30,7 @@ dotnet test tests/AcDream.Core.Net.Tests/AcDream.Core.Net.Tests.csproj
|
|||
|
||||
| File | Responsibility | Slices |
|
||||
|---|---|---|
|
||||
| `src/AcDream.Core/Physics/Position.cs` (create) | `Frame` + `Position` value types | 1 |
|
||||
| `src/AcDream.Core/Physics/Position.cs` (create) | `CellFrame` + `Position` value types (`CellFrame` = retail `Frame`, renamed to dodge `DatReaderWriter.Types.Frame`) | 1 |
|
||||
| `src/AcDream.Core/Physics/LandDefs.cs` (modify) | add `GetBlockOffset` | 1 |
|
||||
| `src/AcDream.Core/Physics/PhysicsBody.cs` (modify) | carry `Position`; computed `WorldPosition`; `AdjustToOutside` on placement; integrate-then-rewrap | 2,4 |
|
||||
| `src/AcDream.Core/Physics/ResolveResult.cs` (modify) | carry the canonicalized `Position` | 2,3 |
|
||||
|
|
@ -57,30 +57,32 @@ dotnet test tests/AcDream.Core.Net.Tests/AcDream.Core.Net.Tests.csproj
|
|||
|
||||
- [ ] **Step 1: Create the value types**
|
||||
|
||||
`src/AcDream.Core/Physics/Position.cs`:
|
||||
`src/AcDream.Core/Physics/Position.cs` (the value type is named `CellFrame`, NOT `Frame` — `Frame`
|
||||
collides with `DatReaderWriter.Types.Frame` used in physics-adjacent code; the `Position.Frame` *member*
|
||||
keeps retail's name):
|
||||
```csharp
|
||||
using System.Numerics;
|
||||
|
||||
namespace AcDream.Core.Physics;
|
||||
|
||||
/// <summary>
|
||||
/// Retail <c>Frame</c> (acclient.h:30647). <see cref="Origin"/> is LOCAL to the
|
||||
/// owning cell: outdoor → X/Y ∈ [0,192) within the landblock, Z = height;
|
||||
/// indoor → the EnvCell-relative placement. <see cref="Orientation"/> replaces
|
||||
/// retail's <c>m_fl2gv</c> 3×3 local→global matrix (equivalent rotation).
|
||||
/// Retail <c>Frame</c> (acclient.h:30647) — named <c>CellFrame</c> here to avoid
|
||||
/// colliding with <c>DatReaderWriter.Types.Frame</c>. <see cref="Origin"/> is LOCAL
|
||||
/// to the owning cell: outdoor → X/Y ∈ [0,192), Z = height; indoor → EnvCell-relative.
|
||||
/// <see cref="Orientation"/> replaces retail's <c>m_fl2gv</c> 3×3 matrix.
|
||||
/// </summary>
|
||||
public readonly record struct Frame(Vector3 Origin, Quaternion Orientation);
|
||||
public readonly record struct CellFrame(Vector3 Origin, Quaternion Orientation);
|
||||
|
||||
/// <summary>
|
||||
/// Retail <c>Position</c> (acclient.h:30658): the (which-cell, where-inside) pair.
|
||||
/// One type for indoor and outdoor. The full 32-bit cell id (high 16 = landblock
|
||||
/// prefix, low 16 = cell index) plus the cell-local <see cref="Frame"/>. Neither
|
||||
/// half is ever reconstructed from a streaming center — see #145 design spec.
|
||||
/// Full 32-bit cell id + the cell-local <see cref="CellFrame"/>. Never reconstructed
|
||||
/// from a streaming center — see #145 design spec. The <c>Frame</c> member keeps
|
||||
/// retail's name.
|
||||
/// </summary>
|
||||
public readonly record struct Position(uint ObjCellId, Frame Frame)
|
||||
public readonly record struct Position(uint ObjCellId, CellFrame Frame)
|
||||
{
|
||||
public Position(uint objCellId, Vector3 origin, Quaternion orientation)
|
||||
: this(objCellId, new Frame(origin, orientation)) { }
|
||||
: this(objCellId, new CellFrame(origin, orientation)) { }
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -59,16 +59,19 @@ boundary crossing.
|
|||
### 3.1 New value types (`AcDream.Core.Physics`)
|
||||
|
||||
```csharp
|
||||
public readonly record struct Frame(Vector3 Origin, Quaternion Orientation);
|
||||
// Type named CellFrame (retail "Frame") to avoid colliding with
|
||||
// DatReaderWriter.Types.Frame, which is used in physics-adjacent code.
|
||||
public readonly record struct CellFrame(Vector3 Origin, Quaternion Orientation);
|
||||
// Origin is LOCAL to ObjCellId's cell:
|
||||
// outdoor → Origin.X/Y ∈ [0,192) within the landblock; Origin.Z = height.
|
||||
// indoor → Origin is the EnvCell-relative placement the BSP path already uses.
|
||||
|
||||
public readonly record struct Position(uint ObjCellId, Frame Frame);
|
||||
public readonly record struct Position(uint ObjCellId, CellFrame Frame); // member keeps retail's name
|
||||
```
|
||||
|
||||
One `Position` type covers indoor and outdoor (retail uniformity). `PhysicsBody.Position` becomes a
|
||||
`Position` and is **the source of truth.**
|
||||
`Position` and is **the source of truth.** (The value type is `CellFrame`; the `Position.Frame` member
|
||||
keeps retail's name. `Frame` collided with `DatReaderWriter.Types.Frame` — renamed during Slice 1.)
|
||||
|
||||
### 3.2 The seam: `_liveCenter` is render-only
|
||||
|
||||
|
|
|
|||
|
|
@ -3,21 +3,25 @@ using System.Numerics;
|
|||
namespace AcDream.Core.Physics;
|
||||
|
||||
/// <summary>
|
||||
/// Retail <c>Frame</c> (acclient.h:30647). <see cref="Origin"/> is LOCAL to the
|
||||
/// owning cell: outdoor → X/Y ∈ [0,192) within the landblock, Z = height;
|
||||
/// indoor → the EnvCell-relative placement. <see cref="Orientation"/> replaces
|
||||
/// retail's <c>m_fl2gv</c> 3×3 local→global matrix (equivalent rotation).
|
||||
/// Retail <c>Frame</c> (acclient.h:30647) — named <c>CellFrame</c> here to avoid
|
||||
/// colliding with <c>DatReaderWriter.Types.Frame</c> (the dat placement/animation
|
||||
/// frame, used in physics-adjacent code such as <c>ShadowShapeBuilder</c>).
|
||||
/// <see cref="Origin"/> is LOCAL to the owning cell: outdoor → X/Y ∈ [0,192) within
|
||||
/// the landblock, Z = height; indoor → the EnvCell-relative placement.
|
||||
/// <see cref="Orientation"/> replaces retail's <c>m_fl2gv</c> 3×3 local→global
|
||||
/// matrix (equivalent rotation).
|
||||
/// </summary>
|
||||
public readonly record struct Frame(Vector3 Origin, Quaternion Orientation);
|
||||
public readonly record struct CellFrame(Vector3 Origin, Quaternion Orientation);
|
||||
|
||||
/// <summary>
|
||||
/// Retail <c>Position</c> (acclient.h:30658): the (which-cell, where-inside) pair.
|
||||
/// One type for indoor and outdoor. The full 32-bit cell id (high 16 = landblock
|
||||
/// prefix, low 16 = cell index) plus the cell-local <see cref="Frame"/>. Neither
|
||||
/// half is ever reconstructed from a streaming center — see #145 design spec.
|
||||
/// prefix, low 16 = cell index) plus the cell-local <see cref="CellFrame"/>. Neither
|
||||
/// half is ever reconstructed from a streaming center — see #145 design spec. The
|
||||
/// <c>Frame</c> member keeps retail's name.
|
||||
/// </summary>
|
||||
public readonly record struct Position(uint ObjCellId, Frame Frame)
|
||||
public readonly record struct Position(uint ObjCellId, CellFrame Frame)
|
||||
{
|
||||
public Position(uint objCellId, Vector3 origin, Quaternion orientation)
|
||||
: this(objCellId, new Frame(origin, orientation)) { }
|
||||
: this(objCellId, new CellFrame(origin, orientation)) { }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ using System.Numerics;
|
|||
using DatReaderWriter.DBObjs;
|
||||
using DatReaderWriter.Enums;
|
||||
using DatReaderWriter.Types;
|
||||
using DatFrame = DatReaderWriter.Types.Frame;
|
||||
|
||||
namespace AcDream.Core.Physics;
|
||||
|
||||
|
|
@ -89,9 +88,9 @@ public static class ShadowShapeBuilder
|
|||
uint gfxId = (uint)setup.Parts[i];
|
||||
if (!hasPhysicsBsp(gfxId)) continue;
|
||||
|
||||
DatFrame partFrame = placementFrame is not null && i < placementFrame.Frames.Count
|
||||
Frame partFrame = placementFrame is not null && i < placementFrame.Frames.Count
|
||||
? placementFrame.Frames[i]
|
||||
: new DatFrame { Origin = Vector3.Zero, Orientation = Quaternion.Identity };
|
||||
: new Frame { Origin = Vector3.Zero, Orientation = Quaternion.Identity };
|
||||
|
||||
// BSP radius default; caller substitutes the real BoundingSphere.Radius
|
||||
// at registration time when available. Loose-but-safe broadphase value.
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ using Xunit;
|
|||
// Alias the DatReaderWriter enum so it doesn't clash with
|
||||
// AcDream.Core.Physics.MotionCommand (which is a static class of uint constants).
|
||||
using DRWMotionCommand = DatReaderWriter.Enums.MotionCommand;
|
||||
using DatFrame = DatReaderWriter.Types.Frame;
|
||||
|
||||
namespace AcDream.Core.Tests.Physics;
|
||||
|
||||
|
|
@ -66,7 +65,7 @@ file static class Fixtures
|
|||
{
|
||||
var pf = new AnimationFrame((uint)numParts);
|
||||
for (int p = 0; p < numParts; p++)
|
||||
pf.Frames.Add(new DatFrame { Origin = origin, Orientation = orientation });
|
||||
pf.Frames.Add(new Frame { Origin = origin, Orientation = orientation });
|
||||
anim.PartFrames.Add(pf);
|
||||
}
|
||||
return anim;
|
||||
|
|
@ -87,8 +86,8 @@ file static class Fixtures
|
|||
var pf1 = new AnimationFrame((uint)numParts);
|
||||
for (int p = 0; p < numParts; p++)
|
||||
{
|
||||
pf0.Frames.Add(new DatFrame { Origin = fromOrigin, Orientation = fromRot });
|
||||
pf1.Frames.Add(new DatFrame { Origin = toOrigin, Orientation = toRot });
|
||||
pf0.Frames.Add(new Frame { Origin = fromOrigin, Orientation = fromRot });
|
||||
pf1.Frames.Add(new Frame { Origin = toOrigin, Orientation = toRot });
|
||||
}
|
||||
anim.PartFrames.Add(pf0);
|
||||
anim.PartFrames.Add(pf1);
|
||||
|
|
@ -397,7 +396,7 @@ public sealed class AnimationSequencerTests
|
|||
{
|
||||
var pf = new AnimationFrame(1);
|
||||
float y = 10f - 5f * f; // 10, 5, 0
|
||||
pf.Frames.Add(new DatFrame { Origin = new Vector3(0, y, 0), Orientation = Quaternion.Identity });
|
||||
pf.Frames.Add(new Frame { Origin = new Vector3(0, y, 0), Orientation = Quaternion.Identity });
|
||||
linkAnim.PartFrames.Add(pf);
|
||||
}
|
||||
|
||||
|
|
@ -594,7 +593,7 @@ public sealed class AnimationSequencerTests
|
|||
for (int f = 0; f < 4; f++)
|
||||
{
|
||||
var pf = new AnimationFrame(1);
|
||||
pf.Frames.Add(new DatFrame { Origin = new Vector3(0, 0, f), Orientation = Quaternion.Identity });
|
||||
pf.Frames.Add(new Frame { Origin = new Vector3(0, 0, f), Orientation = Quaternion.Identity });
|
||||
anim.PartFrames.Add(pf);
|
||||
}
|
||||
|
||||
|
|
@ -1007,7 +1006,7 @@ public sealed class AnimationSequencerTests
|
|||
anim.Flags = AnimationFlags.PosFrames;
|
||||
for (int f = 0; f < 4; f++)
|
||||
{
|
||||
anim.PosFrames.Add(new DatFrame
|
||||
anim.PosFrames.Add(new Frame
|
||||
{
|
||||
Origin = new Vector3(1f, 0f, 0f),
|
||||
Orientation = Quaternion.Identity,
|
||||
|
|
@ -1154,7 +1153,7 @@ public sealed class AnimationSequencerTests
|
|||
for (int f = 0; f < 10; f++)
|
||||
{
|
||||
var pf = new AnimationFrame(1);
|
||||
pf.Frames.Add(new DatFrame { Origin = new Vector3(0, 0, f), Orientation = Quaternion.Identity });
|
||||
pf.Frames.Add(new Frame { Origin = new Vector3(0, 0, f), Orientation = Quaternion.Identity });
|
||||
anim.PartFrames.Add(pf);
|
||||
}
|
||||
|
||||
|
|
@ -1205,7 +1204,7 @@ public sealed class AnimationSequencerTests
|
|||
for (int f = 0; f < 10; f++)
|
||||
{
|
||||
var pf = new AnimationFrame(1);
|
||||
pf.Frames.Add(new DatFrame { Origin = new Vector3(0, 0, f), Orientation = Quaternion.Identity });
|
||||
pf.Frames.Add(new Frame { Origin = new Vector3(0, 0, f), Orientation = Quaternion.Identity });
|
||||
anim.PartFrames.Add(pf);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ using DatReaderWriter.DBObjs;
|
|||
using DatReaderWriter.Enums;
|
||||
using DatReaderWriter.Types;
|
||||
using Xunit;
|
||||
using DatFrame = DatReaderWriter.Types.Frame;
|
||||
|
||||
namespace AcDream.Core.Tests.Physics;
|
||||
|
||||
|
|
@ -38,9 +37,9 @@ public class ShadowShapeBuilderTests
|
|||
{
|
||||
Frames =
|
||||
{
|
||||
new DatFrame { Origin = Vector3.Zero, Orientation = Quaternion.Identity },
|
||||
new DatFrame { Origin = Vector3.Zero, Orientation = Quaternion.Identity },
|
||||
new DatFrame { Origin = Vector3.Zero, Orientation = Quaternion.Identity }
|
||||
new Frame { Origin = Vector3.Zero, Orientation = Quaternion.Identity },
|
||||
new Frame { Origin = Vector3.Zero, Orientation = Quaternion.Identity },
|
||||
new Frame { Origin = Vector3.Zero, Orientation = Quaternion.Identity }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ using AcDream.Core.World;
|
|||
using DatReaderWriter.Types;
|
||||
using Xunit;
|
||||
using DatPhysicsScript = DatReaderWriter.DBObjs.PhysicsScript;
|
||||
using DatFrame = DatReaderWriter.Types.Frame;
|
||||
|
||||
namespace AcDream.Core.Tests.Rendering.Vfx;
|
||||
|
||||
|
|
@ -141,7 +140,7 @@ public sealed class EntityScriptActivatorTests
|
|||
var hookSink = new ParticleHookSink(system);
|
||||
|
||||
// Hook offset = (1, 0, 0) in entity-local frame.
|
||||
var hookOffset = new DatFrame
|
||||
var hookOffset = new Frame
|
||||
{
|
||||
Origin = new Vector3(1f, 0f, 0f),
|
||||
Orientation = Quaternion.Identity,
|
||||
|
|
@ -195,7 +194,7 @@ public sealed class EntityScriptActivatorTests
|
|||
var system = new ParticleSystem(registry);
|
||||
var hookSink = new ParticleHookSink(system);
|
||||
|
||||
var script = BuildScript((0.0, new CreateParticleHook { EmitterInfoId = 100u, Offset = new DatFrame() }));
|
||||
var script = BuildScript((0.0, new CreateParticleHook { EmitterInfoId = 100u, Offset = new Frame() }));
|
||||
var table = new Dictionary<uint, DatPhysicsScript> { [0xAAu] = script };
|
||||
var runner = new PhysicsScriptRunner(
|
||||
id => table.TryGetValue(id, out var s) ? s : null,
|
||||
|
|
@ -258,7 +257,7 @@ public sealed class EntityScriptActivatorTests
|
|||
var system = new ParticleSystem(registry);
|
||||
var hookSink = new ParticleHookSink(system);
|
||||
|
||||
var hookOffset = new DatFrame { Origin = new Vector3(1f, 0, 0), Orientation = Quaternion.Identity };
|
||||
var hookOffset = new Frame { Origin = new Vector3(1f, 0, 0), Orientation = Quaternion.Identity };
|
||||
var script = BuildScript(
|
||||
(0.0, new CreateParticleHook { EmitterInfoId = 100u, Offset = hookOffset, PartIndex = 1 }));
|
||||
var table = new Dictionary<uint, DatPhysicsScript> { [0xAAu] = script };
|
||||
|
|
@ -298,7 +297,7 @@ public sealed class EntityScriptActivatorTests
|
|||
var system = new ParticleSystem(registry);
|
||||
var hookSink = new ParticleHookSink(system);
|
||||
|
||||
var script = BuildScript((0.0, new CreateParticleHook { EmitterInfoId = 100u, Offset = new DatFrame() }));
|
||||
var script = BuildScript((0.0, new CreateParticleHook { EmitterInfoId = 100u, Offset = new Frame() }));
|
||||
var table = new Dictionary<uint, DatPhysicsScript> { [0xAAu] = script };
|
||||
var runner = new PhysicsScriptRunner(
|
||||
id => table.TryGetValue(id, out var s) ? s : null,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ using DatReaderWriter.DBObjs;
|
|||
using DatReaderWriter.Types;
|
||||
using Xunit;
|
||||
using DatPhysicsScript = DatReaderWriter.DBObjs.PhysicsScript;
|
||||
using DatFrame = DatReaderWriter.Types.Frame;
|
||||
|
||||
namespace AcDream.Core.Tests.Streaming;
|
||||
|
||||
|
|
@ -44,7 +43,7 @@ public sealed class GpuWorldStateActivatorTests
|
|||
script.ScriptData.Add(new PhysicsScriptData
|
||||
{
|
||||
StartTime = 0.0,
|
||||
Hook = new CreateParticleHook { EmitterInfoId = 100u, Offset = new DatFrame() },
|
||||
Hook = new CreateParticleHook { EmitterInfoId = 100u, Offset = new Frame() },
|
||||
});
|
||||
var table = new Dictionary<uint, DatPhysicsScript> { [scriptId] = script };
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue