fix(interaction): restore retail loot placement and world-drop projection

This commit is contained in:
Erik 2026-07-27 00:03:15 +02:00
parent 4d095be286
commit 921712f412
24 changed files with 1581 additions and 34 deletions

View file

@ -423,6 +423,85 @@ public sealed class LiveEntityHydrationControllerTests
Assert.Equal(0, fixture.Resources.UnregisterCount);
}
[Fact]
public void PositionAfterInventoryOnlyCreate_ConstructsFirstSpatialProjection()
{
using var fixture = new Fixture(originKnown: true);
fixture.Controller.OnCreate(CelllessSpawn(
PositionSequence: 1,
parentGuid: null));
RuntimeEntityRecord canonical = fixture.Canonical;
Assert.False(fixture.Runtime.TryGetRecord(Guid, out _));
Assert.Equal(0, fixture.Resources.RegisterCount);
Assert.True(fixture.Runtime.TryApplyPosition(
PositionUpdate(sequence: 2, x: 14f),
isLocalPlayer: false,
forcePositionRotation: null,
currentLocalVelocity: null,
out _,
out _,
out _));
ulong authorityVersion = canonical.PositionAuthorityVersion;
Assert.True(fixture.Controller.RecoverCanonicalProjection(
canonical,
authorityVersion,
out LiveEntityRecord record));
Assert.NotNull(record.WorldEntity);
Assert.True(record.IsSpatiallyProjected);
Assert.True(record.InitialHydrationCompleted);
Assert.Equal(1, fixture.Resources.RegisterCount);
Assert.Equal(1, fixture.Ready.PublishCount);
Assert.Contains(
fixture.Materializer.Calls,
call => call.Purpose is LiveProjectionPurpose.SpatialRecovery);
Assert.True(fixture.Controller.RecoverCanonicalProjection(
canonical,
authorityVersion,
out LiveEntityRecord same));
Assert.Same(record, same);
Assert.Equal(1, fixture.Resources.RegisterCount);
Assert.Equal(1, fixture.Ready.PublishCount);
}
[Fact]
public void CanonicalProjectionRecovery_RejectsSupersededPositionAuthority()
{
using var fixture = new Fixture(originKnown: true);
fixture.Controller.OnCreate(CelllessSpawn(
PositionSequence: 1,
parentGuid: null));
RuntimeEntityRecord canonical = fixture.Canonical;
Assert.True(fixture.Runtime.TryApplyPosition(
PositionUpdate(sequence: 2, x: 14f),
isLocalPlayer: false,
forcePositionRotation: null,
currentLocalVelocity: null,
out _,
out _,
out _));
ulong staleAuthorityVersion = canonical.PositionAuthorityVersion;
Assert.True(fixture.Runtime.TryApplyPosition(
PositionUpdate(sequence: 3, x: 18f),
isLocalPlayer: false,
forcePositionRotation: null,
currentLocalVelocity: null,
out _,
out _,
out _));
Assert.False(fixture.Controller.RecoverCanonicalProjection(
canonical,
staleAuthorityVersion,
out _));
Assert.False(fixture.Runtime.TryGetRecord(Guid, out _));
Assert.Equal(0, fixture.Resources.RegisterCount);
Assert.Equal(0, fixture.Ready.PublishCount);
}
[Fact]
public void ParentEvent_IsRetainedByRelationshipOwnerAndAcceptedThroughHydrationGate()
{
@ -2350,4 +2429,19 @@ public sealed class LiveEntityHydrationControllerTests
PlacementId: parentGuid is null ? null : 1u,
Physics: physics);
}
private static WorldSession.EntityPositionUpdate PositionUpdate(
ushort sequence,
float x) =>
new(
Guid,
new CreateObject.ServerPosition(
Cell, x, 12f, 6f, 1f, 0f, 0f, 0f),
Velocity: null,
PlacementId: 0,
IsGrounded: true,
InstanceSequence: 1,
PositionSequence: sequence,
TeleportSequence: 0,
ForcePositionSequence: 0);
}