feat(runtime): commit dormant SetPosition activation

This commit is contained in:
Erik 2026-08-01 14:25:02 +02:00
parent 99f867f053
commit 5785a07b3e
13 changed files with 4674 additions and 111 deletions

View file

@ -10,6 +10,284 @@ public sealed class ShadowSetPositionCommitTests
private const uint Cell1 = Landblock | 0x0001u;
private const uint Cell9 = Landblock | 0x0009u;
[Fact]
public void PreparedAuthoredMoveKeepsOldRowsUntilAtomicApply()
{
var registry = RegisteredSingle();
var moved = new Vector3(36f, 12f, 50f);
Assert.True(registry.TryPrepareSetPosition(
1u,
moved,
Quaternion.Identity,
Cell9,
0f,
0f,
PhysicsShadowCommitAction.Replace,
[Cell9],
provenShapeless: false,
suspendOwner: false,
out var prepared));
Assert.Equal(new Vector3(12f, 12f, 50f),
Assert.Single(registry.GetObjectsInCell(Cell1)).Position);
Assert.Empty(registry.GetObjectsInCell(Cell9));
Assert.True(registry.TryApplySetPosition(prepared!, out var receipt));
Assert.True(receipt.Mutated);
Assert.Empty(registry.GetObjectsInCell(Cell1));
Assert.Equal(moved,
Assert.Single(registry.GetObjectsInCell(Cell9)).Position);
}
[Fact]
public void PreparedCrossPrefixDispatchesMembershipThenMutationOnce()
{
const uint otherCell = 0xA9B50001u;
var registry = RegisteredSingle();
var callbacks = new List<string>();
registry.OwnerPrefixMembershipChanged += (owner, prefix) =>
callbacks.Add($"prefix:{owner:X8}:{prefix:X8}");
registry.OwnerMutated += (owner, version) =>
callbacks.Add($"owner:{owner:X8}:{version}");
Assert.True(registry.TryPrepareSetPosition(
1u,
new Vector3(204f, 12f, 50f),
Quaternion.Identity,
otherCell,
192f,
0f,
PhysicsShadowCommitAction.Replace,
[otherCell],
provenShapeless: false,
suspendOwner: false,
out var prepared));
Assert.True(registry.TryApplySetPosition(prepared!, out var receipt));
Assert.Empty(callbacks);
registry.DispatchSetPositionCommit(receipt);
registry.DispatchSetPositionCommit(receipt);
Assert.Equal(
[
"prefix:00000001:A9B40000",
"prefix:00000001:A9B50000",
$"owner:00000001:{receipt.OwnerVersion}",
], callbacks);
}
[Fact]
public void TwoOwnerReceiptsDispatchExactlyOnceInReverseOrder()
{
const uint otherCell = 0xA9B50001u;
var registry = RegisteredSingle();
registry.Register(
2u, 0x01000002u, new Vector3(13f, 12f, 50f),
Quaternion.Identity, 1f, 0f, 0f, Landblock,
seedCellId: Cell1, isStatic: false);
var owners = new List<uint>();
registry.OwnerMutated += (owner, _) => owners.Add(owner);
Assert.True(registry.TryPrepareSetPosition(
1u, new Vector3(204f, 12f, 50f), Quaternion.Identity,
otherCell, 192f, 0f, PhysicsShadowCommitAction.Replace,
[otherCell], provenShapeless: false, suspendOwner: false,
out var first));
Assert.True(registry.TryApplySetPosition(first!, out var firstReceipt));
Assert.True(registry.TryPrepareSetPosition(
2u, new Vector3(205f, 12f, 50f), Quaternion.Identity,
otherCell, 192f, 0f, PhysicsShadowCommitAction.Replace,
[otherCell], provenShapeless: false, suspendOwner: false,
out var second));
Assert.True(registry.TryApplySetPosition(second!, out var secondReceipt));
registry.DispatchSetPositionCommit(secondReceipt);
registry.DispatchSetPositionCommit(firstReceipt);
registry.DispatchSetPositionCommit(secondReceipt);
registry.DispatchSetPositionCommit(firstReceipt);
Assert.Equal([2u, 1u], owners);
Assert.Equal(0, registry.PendingSetPositionDispatchCount);
}
[Fact]
public void PrefixObserverMutationCannotRegressFinalOwnerVersion()
{
const uint otherCell = 0xA9B50001u;
var registry = RegisteredSingle();
var versions = new List<ulong>();
var prefixes = new List<uint>();
bool mutated = false;
registry.OwnerPrefixMembershipChanged += (owner, prefix) =>
{
prefixes.Add(prefix);
if (mutated)
return;
mutated = true;
registry.UpdatePhysicsState(
owner,
(uint)PhysicsStateFlags.Hidden);
};
registry.OwnerMutated += (_, version) => versions.Add(version);
Assert.True(registry.TryPrepareSetPosition(
1u, new Vector3(204f, 12f, 50f), Quaternion.Identity,
otherCell, 192f, 0f, PhysicsShadowCommitAction.Replace,
[otherCell], provenShapeless: false, suspendOwner: false,
out var prepared));
Assert.True(registry.TryApplySetPosition(prepared!, out var receipt));
registry.DispatchSetPositionCommit(receipt);
Assert.Single(versions);
Assert.Equal(registry.GetOwnerVersion(1u), versions[0]);
Assert.Equal([Landblock], prefixes);
}
[Fact]
public void PreparedShapelessRequiresExplicitDispositionAndCreatesNoRows()
{
var registry = new ShadowObjectRegistry();
Assert.False(registry.TryPrepareSetPosition(
77u,
Vector3.One,
Quaternion.Identity,
Cell1,
0f,
0f,
PhysicsShadowCommitAction.Replace,
[Cell1],
provenShapeless: false,
suspendOwner: false,
out _));
Assert.True(registry.TryPrepareSetPosition(
77u,
Vector3.One,
Quaternion.Identity,
Cell1,
0f,
0f,
PhysicsShadowCommitAction.Replace,
[Cell1],
provenShapeless: true,
suspendOwner: false,
out var prepared));
Assert.True(registry.TryApplySetPosition(prepared!, out var receipt));
Assert.False(receipt.Mutated);
Assert.Empty(registry.GetObjectsInCell(Cell1));
Assert.False(registry.TryApplySetPosition(prepared!, out _));
}
[Fact]
public void ClearInvalidatesPreparedUnappliedShapelessCommit()
{
var registry = new ShadowObjectRegistry();
Assert.True(registry.TryPrepareSetPosition(
77u,
Vector3.One,
Quaternion.Identity,
Cell1,
0f,
0f,
PhysicsShadowCommitAction.Replace,
[Cell1],
provenShapeless: true,
suspendOwner: false,
out var prepared));
registry.Clear();
Assert.False(registry.TryApplySetPosition(prepared!, out _));
Assert.Equal(0, registry.PendingSetPositionDispatchCount);
Assert.Empty(registry.GetObjectsInCell(Cell1));
}
[Fact]
public void PreparedCommitRejectsStaleGlobalRevisionWithoutMutation()
{
var registry = RegisteredSingle();
Assert.True(registry.TryPrepareSetPosition(
1u, new Vector3(36f, 12f, 50f), Quaternion.Identity,
Cell9, 0f, 0f, PhysicsShadowCommitAction.Replace, [Cell9],
provenShapeless: false, suspendOwner: false, out var prepared));
registry.Register(
2u, 0x01000002u, new Vector3(13f, 12f, 50f),
Quaternion.Identity, 1f, 0f, 0f, Landblock,
seedCellId: Cell1, isStatic: false);
Vector3 before = registry.GetObjectsInCell(Cell1)
.Single(entry => entry.EntityId == 1u).Position;
Assert.False(registry.TryApplySetPosition(prepared!, out _));
Assert.Equal(before, registry.GetObjectsInCell(Cell1)
.Single(entry => entry.EntityId == 1u).Position);
Assert.Empty(registry.GetObjectsInCell(Cell9));
}
[Fact]
public void PreparedCommitRejectsStaleOwnerVersionWithoutMutation()
{
var registry = RegisteredSingle();
Assert.True(registry.TryPrepareSetPosition(
1u, new Vector3(36f, 12f, 50f), Quaternion.Identity,
Cell9, 0f, 0f, PhysicsShadowCommitAction.Replace, [Cell9],
provenShapeless: false, suspendOwner: false, out var prepared));
registry.UpdatePhysicsState(1u, (uint)PhysicsStateFlags.Hidden);
ShadowEntry before = Assert.Single(registry.GetObjectsInCell(Cell1));
Assert.False(registry.TryApplySetPosition(prepared!, out _));
Assert.Equal(before, Assert.Single(registry.GetObjectsInCell(Cell1)));
Assert.Empty(registry.GetObjectsInCell(Cell9));
}
[Fact]
public void PreparedApplyTailAllocatesZeroManagedBytes()
{
// Warm the generic owner-value and dictionary replacement paths.
var warm = RegisteredSingle();
Assert.True(warm.TryPrepareSetPosition(
1u, new Vector3(36f, 12f, 50f), Quaternion.Identity,
Cell9, 0f, 0f, PhysicsShadowCommitAction.Replace, [Cell9],
provenShapeless: false, suspendOwner: false, out var warmPrepared));
Assert.True(warm.TryApplySetPosition(warmPrepared!, out _));
var registry = RegisteredSingle();
Assert.True(registry.TryPrepareSetPosition(
1u, new Vector3(36f, 12f, 50f), Quaternion.Identity,
Cell9, 0f, 0f, PhysicsShadowCommitAction.Replace, [Cell9],
provenShapeless: false, suspendOwner: false, out var prepared));
long before = GC.GetAllocatedBytesForCurrentThread();
bool applied = registry.TryApplySetPosition(prepared!, out _);
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
Assert.True(applied);
Assert.Equal(0, allocated);
}
[Fact]
public void DeferredDispatchRejectsReceiptAfterInterveningOwnerMutation()
{
const uint otherCell = 0xA9B50001u;
var registry = RegisteredSingle();
var versions = new List<ulong>();
var prefixes = new List<uint>();
registry.OwnerMutated += (_, version) => versions.Add(version);
registry.OwnerPrefixMembershipChanged += (_, prefix) =>
prefixes.Add(prefix);
Assert.True(registry.TryPrepareSetPosition(
1u, new Vector3(204f, 12f, 50f), Quaternion.Identity,
otherCell, 192f, 0f, PhysicsShadowCommitAction.Replace,
[otherCell], provenShapeless: false, suspendOwner: false,
out var prepared));
Assert.True(registry.TryApplySetPosition(prepared!, out var receipt));
registry.UpdatePhysicsState(1u, (uint)PhysicsStateFlags.Hidden);
ulong current = registry.GetOwnerVersion(1u);
registry.DispatchSetPositionCommit(receipt);
Assert.Equal([current], versions);
Assert.Empty(prefixes);
}
[Fact]
public void NoneRefreshesFrameWithoutChangingExactMembership()
{