refactor(world): extract live appearance and parenting
Move ObjDesc, Parent, Pickup, child-ready, and retained projection recovery into LiveEntityHydrationController while preserving retail timestamp and leave-world semantics. Pin ready publication to exact projection authority and restore attached descendant chains synchronously.
This commit is contained in:
parent
40352d5a7a
commit
fe5514967c
9 changed files with 1098 additions and 199 deletions
|
|
@ -433,6 +433,134 @@ public sealed class EquippedChildProjectionWithdrawalTests
|
|||
Assert.Equal(LiveEntityProjectionKind.Attached, child.ProjectionKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpawnParentWithoutAnimationFrame_UsesRetailPlacementZero()
|
||||
{
|
||||
using var fixture = new ControllerFixture((_, _, _) =>
|
||||
new ExactProjectionWithdrawalOutcome(
|
||||
ExactProjectionWithdrawalDisposition.Completed,
|
||||
Failure: null));
|
||||
LiveEntityRecord parent = fixture.Spawn(0x70000270u, generation: 1);
|
||||
WorldSession.EntitySpawn childSpawn = ControllerFixture.SpawnData(
|
||||
0x70000271u,
|
||||
generation: 1) with
|
||||
{
|
||||
Position = null,
|
||||
ParentGuid = parent.ServerGuid,
|
||||
ParentLocation = 0,
|
||||
PlacementId = null,
|
||||
PositionSequence = 0,
|
||||
};
|
||||
LiveEntityRecord child = fixture.Live.RegisterLiveEntity(childSpawn).Record!;
|
||||
|
||||
fixture.Controller.OnSpawn(childSpawn);
|
||||
|
||||
var expected = new ParentAttachmentRelation(
|
||||
parent.ServerGuid,
|
||||
child.ServerGuid,
|
||||
ParentLocation: 0,
|
||||
PlacementId: 0,
|
||||
ParentInstanceSequence: 0,
|
||||
ChildPositionSequence: 0);
|
||||
Assert.True(fixture.Live.ParentAttachments.IsCommitted(expected));
|
||||
fixture.Poses.Publish(parent.WorldEntity!, Array.Empty<Matrix4x4>());
|
||||
fixture.Controller.OnPosePublished(parent.ServerGuid);
|
||||
Assert.Equal(LiveEntityProjectionKind.Attached, child.ProjectionKind);
|
||||
Assert.NotNull(child.WorldEntity);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ObjDesc_ReprojectsAttachedChildWithoutWorldPositionOrIdentityChange()
|
||||
{
|
||||
ControllerFixture? fixture = null;
|
||||
fixture = new ControllerFixture((record, positionVersion, projectionVersion) =>
|
||||
{
|
||||
bool completed = fixture!.Live.WithdrawLiveEntityProjection(
|
||||
record,
|
||||
positionVersion,
|
||||
projectionVersion);
|
||||
return new(
|
||||
completed
|
||||
? ExactProjectionWithdrawalDisposition.Completed
|
||||
: ExactProjectionWithdrawalDisposition.Superseded,
|
||||
Failure: null);
|
||||
});
|
||||
using (fixture)
|
||||
{
|
||||
LiveEntityRecord parent = fixture.Spawn(0x70000272u, generation: 1);
|
||||
WorldSession.EntitySpawn childSpawn = ControllerFixture.SpawnData(
|
||||
0x70000273u,
|
||||
generation: 1) with
|
||||
{
|
||||
Position = null,
|
||||
ParentGuid = parent.ServerGuid,
|
||||
ParentLocation = 0,
|
||||
PlacementId = 0,
|
||||
PositionSequence = 0,
|
||||
};
|
||||
LiveEntityRecord child = fixture.Live.RegisterLiveEntity(childSpawn).Record!;
|
||||
fixture.Controller.OnSpawn(childSpawn);
|
||||
fixture.Poses.Publish(parent.WorldEntity!, Array.Empty<Matrix4x4>());
|
||||
fixture.Controller.OnPosePublished(parent.ServerGuid);
|
||||
WorldEntity entity = child.WorldEntity!;
|
||||
Assert.Null(entity.PaletteOverride);
|
||||
WorldSession.EntitySpawn grandchildSpawn = ControllerFixture.SpawnData(
|
||||
0x70000274u,
|
||||
generation: 1) with
|
||||
{
|
||||
Position = null,
|
||||
ParentGuid = child.ServerGuid,
|
||||
ParentLocation = 0,
|
||||
PlacementId = 0,
|
||||
PositionSequence = 0,
|
||||
};
|
||||
LiveEntityRecord grandchild = fixture.Live.RegisterLiveEntity(
|
||||
grandchildSpawn).Record!;
|
||||
fixture.Controller.OnSpawn(grandchildSpawn);
|
||||
WorldEntity grandchildEntity = grandchild.WorldEntity!;
|
||||
Assert.Equal(
|
||||
LiveEntityProjectionKind.Attached,
|
||||
grandchild.ProjectionKind);
|
||||
|
||||
Assert.True(fixture.Live.TryApplyObjDesc(
|
||||
new ObjDescEvent.Parsed(
|
||||
child.ServerGuid,
|
||||
new CreateObject.ModelData(
|
||||
BasePaletteId: 0x04000022u,
|
||||
[new CreateObject.SubPaletteSwap(
|
||||
0x0F000033u,
|
||||
Offset: 4,
|
||||
Length: 8)],
|
||||
Array.Empty<CreateObject.TextureChange>(),
|
||||
Array.Empty<CreateObject.AnimPartChange>()),
|
||||
InstanceSequence: 1,
|
||||
ObjDescSequence: 1),
|
||||
out _));
|
||||
Assert.Null(child.Snapshot.Position);
|
||||
|
||||
Assert.True(fixture.Controller.TryApplyAttachedAppearance(
|
||||
child,
|
||||
child.ObjDescAuthorityVersion));
|
||||
|
||||
Assert.Same(entity, child.WorldEntity);
|
||||
Assert.True(child.IsSpatiallyProjected);
|
||||
Assert.Equal(LiveEntityProjectionKind.Attached, child.ProjectionKind);
|
||||
Assert.Equal((uint)0x04000022u, child.Snapshot.BasePaletteId);
|
||||
Assert.NotNull(entity.PaletteOverride);
|
||||
Assert.Equal(0x04000022u, entity.PaletteOverride!.BasePaletteId);
|
||||
PaletteOverride.SubPaletteRange range =
|
||||
Assert.Single(entity.PaletteOverride.SubPalettes);
|
||||
Assert.Equal(0x0F000033u, range.SubPaletteId);
|
||||
Assert.Equal((byte)4, range.Offset);
|
||||
Assert.Equal((byte)8, range.Length);
|
||||
Assert.Same(grandchildEntity, grandchild.WorldEntity);
|
||||
Assert.True(grandchild.IsSpatiallyProjected);
|
||||
Assert.Equal(
|
||||
LiveEntityProjectionKind.Attached,
|
||||
grandchild.ProjectionKind);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChildWithoutSetup_StillCommitsLogicalParenting()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue