fix(vfx): bind effects after canonical placement
C3c created graphical effect, projectile, and static-animation sidecars before Runtime finished the entity's first SetPosition. One-shot F754/F755 packets could be discarded, projectiles could adopt a cell-less body, and animated statics could compete for body ownership. Keep effects behind an exact-incarnation presentation barrier, retry projectile/static binding on the committed visibility edge, and keep effect cells synchronized with canonical rebuckets. User verified spell, recall, arrow, projectile, portal, and static presentation; 90 focused App tests and the Release build pass.
This commit is contained in:
parent
1fc529cdcb
commit
f24532adf3
11 changed files with 417 additions and 50 deletions
|
|
@ -107,7 +107,7 @@ public sealed class ProjectileControllerTests
|
|||
Assert.Null(record.AnimationRuntime);
|
||||
var remote =
|
||||
fixture.Live.GetOrCreateRemoteMotionRuntime(Guid);
|
||||
remote.Body.Position = entity.Position;
|
||||
remote.Body.SnapToCell(CellA, entity.Position, entity.Position);
|
||||
remote.Body.Orientation = entity.Rotation;
|
||||
Assert.True(fixture.Controller.TryBind(record, ProjectileSetup(), 1.0, 1, 1));
|
||||
Assert.Same(remote.Body, record.ProjectileRuntime!.Body);
|
||||
|
|
@ -1006,6 +1006,8 @@ public sealed class ProjectileControllerTests
|
|||
record.FinalPhysicsState = PhysicsStateFlags.ReportCollisions;
|
||||
var remote =
|
||||
fixture.Live.GetOrCreateRemoteMotionRuntime(Guid);
|
||||
WorldEntity entity = record.WorldEntity!;
|
||||
remote.Body.SnapToCell(CellA, entity.Position, entity.Position);
|
||||
|
||||
record.FinalPhysicsState = MissileState;
|
||||
Assert.True(fixture.Controller.ApplyAuthoritativeState(
|
||||
|
|
@ -1081,7 +1083,10 @@ public sealed class ProjectileControllerTests
|
|||
Assert.Equal(new Vector3(0f, 0f, 2f), remote.Body.Omega);
|
||||
remote.Body.set_velocity(new Vector3(8f, 0f, 0f));
|
||||
remote.Body.Omega = new Vector3(0f, 0f, 3f);
|
||||
remote.Body.Position = entity.Position;
|
||||
remote.Body.SnapToCell(
|
||||
startCell,
|
||||
entity.Position,
|
||||
new Vector3(191f, 10f, 50f));
|
||||
remote.Body.State = record.FinalPhysicsState;
|
||||
PhysicsBody body = remote.Body;
|
||||
|
||||
|
|
@ -1142,7 +1147,7 @@ public sealed class ProjectileControllerTests
|
|||
record.FinalPhysicsState = PhysicsStateFlags.ReportCollisions;
|
||||
var remote =
|
||||
fixture.Live.GetOrCreateRemoteMotionRuntime(Guid);
|
||||
remote.Body.Position = entity.Position;
|
||||
remote.Body.SnapToCell(CellA, entity.Position, entity.Position);
|
||||
remote.Body.Orientation = entity.Rotation;
|
||||
|
||||
Assert.True(float.IsNaN(record.Snapshot.Physics!.Value.Velocity!.Value.X));
|
||||
|
|
@ -1181,6 +1186,55 @@ public sealed class ProjectileControllerTests
|
|||
Assert.Equal(MissileState, remote.Body.State);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResidencePlacementVisibility_RetriesProjectileBindingAfterBodyFrameCommits()
|
||||
{
|
||||
var fixture = new Fixture();
|
||||
LiveEntityRecord record = fixture.Spawn(instance: 1);
|
||||
WorldEntity entity = record.WorldEntity!;
|
||||
var remote = fixture.Live.GetOrCreateRemoteMotionRuntime(Guid);
|
||||
|
||||
Assert.Null(record.ProjectileRuntime);
|
||||
Assert.True(fixture.Live.WithdrawLiveEntityProjection(Guid));
|
||||
|
||||
remote.Body.Orientation = Quaternion.Identity;
|
||||
remote.Body.set_velocity(new Vector3(10f, 0f, 0f));
|
||||
remote.Body.SnapToCell(CellA, entity.Position, entity.Position);
|
||||
|
||||
Assert.True(fixture.Live.RebucketLiveEntity(Guid, CellA));
|
||||
|
||||
Assert.NotNull(record.ProjectileRuntime);
|
||||
Assert.Same(remote.Body, record.ProjectileRuntime!.Body);
|
||||
Assert.True(record.ProjectileRuntime.Body.InWorld);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SharedBodyCell_IsCanonicalBeforePresentationCellIsProjected()
|
||||
{
|
||||
var fixture = new Fixture();
|
||||
LiveEntityRecord record = fixture.Spawn(instance: 1);
|
||||
WorldEntity entity = record.WorldEntity!;
|
||||
var remote = fixture.Live.GetOrCreateRemoteMotionRuntime(Guid);
|
||||
|
||||
Assert.True(fixture.Live.WithdrawLiveEntityProjection(Guid));
|
||||
record.CanonicalLandblockId = 0u;
|
||||
record.FullCellId = 0u;
|
||||
Assert.Equal(0u, record.FullCellId);
|
||||
remote.Body.Orientation = Quaternion.Identity;
|
||||
remote.Body.set_velocity(new Vector3(10f, 0f, 0f));
|
||||
remote.Body.SnapToCell(CellA, entity.Position, entity.Position);
|
||||
|
||||
Assert.True(fixture.Controller.TryBind(
|
||||
record,
|
||||
ProjectileSetup(),
|
||||
currentTime: 1.0,
|
||||
liveCenterX: 1,
|
||||
liveCenterY: 1));
|
||||
|
||||
Assert.Equal(CellA, record.FullCellId);
|
||||
Assert.Same(remote.Body, record.ProjectileRuntime!.Body);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SharedRemoteMotionCell_DelegatesToCanonicalLiveRecord()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue