feat(runtime): own deferred set-position residence
This commit is contained in:
parent
e84a388e6f
commit
4c02ac4259
18 changed files with 5557 additions and 34 deletions
167
tests/AcDream.Core.Tests/Physics/ShadowSetPositionCommitTests.cs
Normal file
167
tests/AcDream.Core.Tests/Physics/ShadowSetPositionCommitTests.cs
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
using System.Collections.Immutable;
|
||||
using System.Numerics;
|
||||
using AcDream.Core.Physics;
|
||||
|
||||
namespace AcDream.Core.Tests.Physics;
|
||||
|
||||
public sealed class ShadowSetPositionCommitTests
|
||||
{
|
||||
private const uint Landblock = 0xA9B40000u;
|
||||
private const uint Cell1 = Landblock | 0x0001u;
|
||||
private const uint Cell9 = Landblock | 0x0009u;
|
||||
|
||||
[Fact]
|
||||
public void NoneRefreshesFrameWithoutChangingExactMembership()
|
||||
{
|
||||
var registry = RegisteredSingle();
|
||||
ulong version = registry.GetOwnerVersion(1u);
|
||||
var moved = new Vector3(14f, 12f, 50f);
|
||||
|
||||
registry.CommitSetPosition(
|
||||
1u, moved, Quaternion.Identity, Cell1, 0f, 0f,
|
||||
PhysicsShadowCommitAction.None, []);
|
||||
|
||||
ShadowEntry entry = Assert.Single(registry.GetObjectsInCell(Cell1));
|
||||
Assert.Equal(moved, entry.Position);
|
||||
Assert.Empty(registry.GetObjectsInCell(Cell9));
|
||||
Assert.Equal(version + 1UL, registry.GetOwnerVersion(1u));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReplaceDeduplicatesExactCellsAndPreserveRetainsThem()
|
||||
{
|
||||
var registry = RegisteredSingle();
|
||||
var replaced = new Vector3(36f, 12f, 50f);
|
||||
registry.CommitSetPosition(
|
||||
1u, replaced, Quaternion.Identity, Cell9, 0f, 0f,
|
||||
PhysicsShadowCommitAction.Replace,
|
||||
ImmutableArray.Create(Cell9, Cell9, 0u));
|
||||
|
||||
Assert.Empty(registry.GetObjectsInCell(Cell1));
|
||||
Assert.Equal(replaced,
|
||||
Assert.Single(registry.GetObjectsInCell(Cell9)).Position);
|
||||
|
||||
var preserved = new Vector3(38f, 12f, 50f);
|
||||
registry.CommitSetPosition(
|
||||
1u, preserved, Quaternion.Identity, Cell9, 0f, 0f,
|
||||
PhysicsShadowCommitAction.Preserve, []);
|
||||
Assert.Equal(preserved,
|
||||
Assert.Single(registry.GetObjectsInCell(Cell9)).Position);
|
||||
|
||||
var emptyReplace = new Vector3(40f, 12f, 50f);
|
||||
registry.CommitSetPosition(
|
||||
1u, emptyReplace, Quaternion.Identity, Cell9, 0f, 0f,
|
||||
PhysicsShadowCommitAction.Replace, []);
|
||||
Assert.Equal(emptyReplace,
|
||||
Assert.Single(registry.GetObjectsInCell(Cell9)).Position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RecalculateUsesCanonicalFloodInsteadOfRetainedCells()
|
||||
{
|
||||
var registry = RegisteredSingle();
|
||||
var moved = new Vector3(36f, 12f, 50f);
|
||||
|
||||
registry.CommitSetPosition(
|
||||
1u, moved, Quaternion.Identity, Cell9, 0f, 0f,
|
||||
PhysicsShadowCommitAction.Recalculate, []);
|
||||
|
||||
Assert.Empty(registry.GetObjectsInCell(Cell1));
|
||||
Assert.Equal(moved,
|
||||
Assert.Single(registry.GetObjectsInCell(Cell9)).Position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SuspendedPreserveRestoresExactRowsAndConsumesReceipt()
|
||||
{
|
||||
var registry = RegisteredSingle();
|
||||
Assert.True(registry.Suspend(1u));
|
||||
Assert.Equal(0, registry.TotalRegistered);
|
||||
Assert.Equal(1, registry.SuspendedRegistrationCount);
|
||||
var moved = new Vector3(15f, 12f, 50f);
|
||||
|
||||
registry.CommitSetPosition(
|
||||
1u, moved, Quaternion.Identity, Cell1, 0f, 0f,
|
||||
PhysicsShadowCommitAction.Preserve, []);
|
||||
|
||||
Assert.Equal(1, registry.TotalRegistered);
|
||||
Assert.Equal(0, registry.SuspendedRegistrationCount);
|
||||
Assert.Equal(moved,
|
||||
Assert.Single(registry.GetObjectsInCell(Cell1)).Position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SuspendedReceiptCopiesAcrossCollisionRootAndClearsOnTeardown()
|
||||
{
|
||||
var source = RegisteredSingle();
|
||||
Assert.True(source.Suspend(1u));
|
||||
var destination = new ShadowObjectRegistry();
|
||||
|
||||
Assert.False(destination.RefreshRetainedOwnerFrom(
|
||||
source, 1u, Landblock, out ulong version));
|
||||
Assert.Equal(source.GetOwnerVersion(1u), version);
|
||||
Assert.Equal(1, destination.RetainedRegistrationCount);
|
||||
Assert.Equal(1, destination.SuspendedRegistrationCount);
|
||||
|
||||
destination.CommitSetPosition(
|
||||
1u, new Vector3(16f, 12f, 50f), Quaternion.Identity,
|
||||
Cell1, 0f, 0f, PhysicsShadowCommitAction.None, []);
|
||||
Assert.Single(destination.GetObjectsInCell(Cell1));
|
||||
destination.Deregister(1u);
|
||||
Assert.Equal(0, destination.RetainedRegistrationCount);
|
||||
Assert.Equal(0, destination.SuspendedRegistrationCount);
|
||||
destination.Clear();
|
||||
Assert.Equal(0, destination.TotalRegistered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultiPartNoneRefreshesEachPartFromRootTransform()
|
||||
{
|
||||
var registry = new ShadowObjectRegistry();
|
||||
IReadOnlyList<ShadowShape> shapes =
|
||||
[
|
||||
Shape(0x01000001u, new Vector3(1f, 0f, 0f)),
|
||||
Shape(0x01000002u, new Vector3(0f, 1f, 0f)),
|
||||
];
|
||||
registry.RegisterMultiPart(
|
||||
2u, new Vector3(12f, 12f, 50f), Quaternion.Identity,
|
||||
shapes, 0u, EntityCollisionFlags.None, 0f, 0f, Landblock,
|
||||
Cell1);
|
||||
Quaternion rotation = Quaternion.CreateFromAxisAngle(
|
||||
Vector3.UnitZ, MathF.PI / 2f);
|
||||
var root = new Vector3(14f, 12f, 50f);
|
||||
|
||||
registry.CommitSetPosition(
|
||||
2u, root, rotation, Cell1, 0f, 0f,
|
||||
PhysicsShadowCommitAction.None, []);
|
||||
|
||||
ShadowEntry[] entries = registry.GetObjectsInCell(Cell1)
|
||||
.Where(entry => entry.EntityId == 2u)
|
||||
.OrderBy(entry => entry.GfxObjId)
|
||||
.ToArray();
|
||||
Assert.Equal(2, entries.Length);
|
||||
Assert.Equal(root + Vector3.Transform(shapes[0].LocalPosition, rotation),
|
||||
entries[0].Position);
|
||||
Assert.Equal(root + Vector3.Transform(shapes[1].LocalPosition, rotation),
|
||||
entries[1].Position);
|
||||
}
|
||||
|
||||
private static ShadowObjectRegistry RegisteredSingle()
|
||||
{
|
||||
var registry = new ShadowObjectRegistry();
|
||||
registry.Register(
|
||||
1u, 0x01000001u, new Vector3(12f, 12f, 50f),
|
||||
Quaternion.Identity, 1f, 0f, 0f, Landblock,
|
||||
seedCellId: Cell1, isStatic: false);
|
||||
return registry;
|
||||
}
|
||||
|
||||
private static ShadowShape Shape(uint gfxObjId, Vector3 local) => new(
|
||||
gfxObjId,
|
||||
local,
|
||||
Quaternion.Identity,
|
||||
1f,
|
||||
ShadowCollisionType.BSP,
|
||||
0.25f,
|
||||
0f);
|
||||
}
|
||||
|
|
@ -264,6 +264,87 @@ public sealed class ParentAttachmentStateTests
|
|||
Assert.Equal((ushort)6, projection.ChildPositionSequence);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CommittedChildrenAppendInCommitOrder()
|
||||
{
|
||||
const uint parentGuid = 0x70000200u;
|
||||
var relations = new ParentAttachmentState();
|
||||
|
||||
Commit(relations, Relation(parentGuid, 0x70000201u, parentInstance: 9));
|
||||
Commit(relations, Relation(parentGuid, 0x70000202u, parentInstance: 9));
|
||||
IReadOnlyList<uint> committed = relations.ChildrenAttachedToParent(
|
||||
parentGuid,
|
||||
9);
|
||||
Commit(relations, Relation(parentGuid, 0x70000203u, parentInstance: 9));
|
||||
|
||||
Assert.Same(
|
||||
committed,
|
||||
relations.ChildrenAttachedToParent(parentGuid, 9));
|
||||
Assert.Equal(
|
||||
[0x70000201u, 0x70000202u, 0x70000203u],
|
||||
committed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EndingChildProjectionSwapRemovesCommittedChild()
|
||||
{
|
||||
const uint parentGuid = 0x70000210u;
|
||||
var relations = new ParentAttachmentState();
|
||||
Commit(relations, Relation(parentGuid, 0x70000211u, parentInstance: 9));
|
||||
Commit(relations, Relation(parentGuid, 0x70000212u, parentInstance: 9));
|
||||
Commit(relations, Relation(parentGuid, 0x70000213u, parentInstance: 9));
|
||||
|
||||
relations.EndChildProjection(0x70000211u);
|
||||
|
||||
Assert.Equal(
|
||||
[0x70000213u, 0x70000212u],
|
||||
relations.ChildrenAttachedToParent(parentGuid, 9));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReparentSwapRemovesOldEntryAndAppendsNewEntry()
|
||||
{
|
||||
const uint oldParentGuid = 0x70000220u;
|
||||
const uint newParentGuid = 0x70000221u;
|
||||
const uint childGuid = 0x70000222u;
|
||||
var relations = new ParentAttachmentState();
|
||||
Commit(relations, Relation(oldParentGuid, childGuid, parentInstance: 9));
|
||||
Commit(relations, Relation(oldParentGuid, 0x70000223u, parentInstance: 9));
|
||||
Commit(relations, Relation(oldParentGuid, 0x70000224u, parentInstance: 9));
|
||||
Commit(relations, Relation(newParentGuid, 0x70000225u, parentInstance: 4));
|
||||
|
||||
Commit(relations, Relation(newParentGuid, childGuid, parentInstance: 4));
|
||||
|
||||
Assert.Equal(
|
||||
[0x70000224u, 0x70000223u],
|
||||
relations.ChildrenAttachedToParent(oldParentGuid, 9));
|
||||
Assert.Equal(
|
||||
[0x70000225u, childGuid],
|
||||
relations.ChildrenAttachedToParent(newParentGuid, 4));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CommittedChildrenAreIsolatedByParentGuidAndIncarnation()
|
||||
{
|
||||
const uint parentGuid = 0x70000230u;
|
||||
const uint otherParentGuid = 0x70000231u;
|
||||
var relations = new ParentAttachmentState();
|
||||
Commit(relations, Relation(parentGuid, 0x70000232u, parentInstance: 9));
|
||||
Commit(relations, Relation(parentGuid, 0x70000233u, parentInstance: 10));
|
||||
Commit(relations, Relation(otherParentGuid, 0x70000234u, parentInstance: 9));
|
||||
|
||||
Assert.Equal(
|
||||
[0x70000232u],
|
||||
relations.ChildrenAttachedToParent(parentGuid, 9));
|
||||
Assert.Equal(
|
||||
[0x70000233u],
|
||||
relations.ChildrenAttachedToParent(parentGuid, 10));
|
||||
Assert.Equal(
|
||||
[0x70000234u],
|
||||
relations.ChildrenAttachedToParent(otherParentGuid, 9));
|
||||
Assert.Empty(relations.ChildrenAttachedToParent(otherParentGuid, 10));
|
||||
}
|
||||
|
||||
private static void Resolve(
|
||||
ParentAttachmentState relations,
|
||||
InboundPhysicsStateController inbound,
|
||||
|
|
@ -290,6 +371,20 @@ public sealed class ParentAttachmentStateTests
|
|||
out accepted)
|
||||
&& relations.CommitProjection(relation);
|
||||
|
||||
private static void Commit(
|
||||
ParentAttachmentState relations,
|
||||
ParentAttachmentRelation relation)
|
||||
{
|
||||
relations.AcceptCreateObjectRelation(relation);
|
||||
Assert.True(relations.CommitProjection(relation));
|
||||
}
|
||||
|
||||
private static ParentAttachmentRelation Relation(
|
||||
uint parentGuid,
|
||||
uint childGuid,
|
||||
ushort parentInstance) =>
|
||||
new(parentGuid, childGuid, 1, 2, parentInstance, 1);
|
||||
|
||||
private static WorldSession.EntitySpawn Spawn(
|
||||
uint guid,
|
||||
ushort instance,
|
||||
|
|
|
|||
2449
tests/AcDream.Runtime.Tests/Physics/RuntimeSetPositionStateTests.cs
Normal file
2449
tests/AcDream.Runtime.Tests/Physics/RuntimeSetPositionStateTests.cs
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue