refactor(runtime): own projectile simulation
Move projectile component identity, prediction invalidation, spatial worksets, authoritative corrections, and the retail physics step into AcDream.Runtime. Keep App as the DAT-shape and presentation adapter so ACE outcomes and visible behavior remain unchanged.
This commit is contained in:
parent
fce8e7b18e
commit
2aee33569f
19 changed files with 1255 additions and 335 deletions
|
|
@ -86,6 +86,201 @@ public sealed class RuntimePhysicsStateTests
|
|||
Assert.Equal(0, lifetime.Physics.SpatialRootCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanonicalRecordAndPhysicsOwnerOwnProjectileComponentAndWorkset()
|
||||
{
|
||||
using var lifetime = new RuntimeEntityObjectLifetime();
|
||||
RuntimeEntityRecord record =
|
||||
lifetime.Entities.AddActive(Spawn(0x70000031u, 1));
|
||||
lifetime.Entities.SetFullCell(record, 0x01010001u, 0x0101FFFFu);
|
||||
lifetime.Entities.SetFinalPhysicsState(
|
||||
record,
|
||||
PhysicsStateFlags.ReportCollisions
|
||||
| PhysicsStateFlags.Missile
|
||||
| PhysicsStateFlags.AlignPath
|
||||
| PhysicsStateFlags.PathClipped);
|
||||
var body = new PhysicsBody
|
||||
{
|
||||
Position = new Vector3(10f, 20f, 5f),
|
||||
Orientation = Quaternion.Identity,
|
||||
InWorld = true,
|
||||
TransientState = TransientStateFlags.Active,
|
||||
};
|
||||
body.SnapToCell(
|
||||
record.FullCellId,
|
||||
body.Position,
|
||||
body.Position);
|
||||
lifetime.Entities.SetPhysicsBody(record, body);
|
||||
var sphere = new ProjectileCollisionSphere(
|
||||
new Vector3(0.1f, 0f, 0f),
|
||||
0.25f);
|
||||
|
||||
IRuntimeProjectile projectile = lifetime.Physics.BindProjectile(
|
||||
record,
|
||||
body,
|
||||
sphere);
|
||||
lifetime.Physics.AcknowledgeSpatialProjection(
|
||||
record,
|
||||
spatial: true);
|
||||
|
||||
Assert.Same(projectile, record.Projectile);
|
||||
Assert.Same(body, projectile.Body);
|
||||
Assert.Equal(sphere, projectile.CollisionSphere);
|
||||
Assert.True(
|
||||
lifetime.Physics.IsSpatialProjectile(record, projectile));
|
||||
Assert.Equal(1, lifetime.Physics.SpatialProjectileCount);
|
||||
Assert.Equal(
|
||||
1,
|
||||
lifetime.Physics.CaptureOwnership().SpatialProjectileCount);
|
||||
|
||||
Assert.Same(
|
||||
projectile,
|
||||
lifetime.Physics.BindProjectile(record, body, sphere));
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
lifetime.Physics.BindProjectile(
|
||||
record,
|
||||
new PhysicsBody(),
|
||||
sphere));
|
||||
|
||||
lifetime.Physics.RemoveSpatialProjection(record);
|
||||
Assert.Equal(0, lifetime.Physics.SpatialProjectileCount);
|
||||
Assert.Same(projectile, record.Projectile);
|
||||
|
||||
Assert.True(lifetime.Physics.ClearProjectile(record));
|
||||
Assert.Null(record.Projectile);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectileAuthoritativeMutationInvalidatesSplitPrediction()
|
||||
{
|
||||
using var lifetime = new RuntimeEntityObjectLifetime();
|
||||
RuntimeEntityRecord record =
|
||||
lifetime.Entities.AddActive(Spawn(0x70000032u, 1));
|
||||
lifetime.Entities.SetFullCell(record, 0x01010001u, 0x0101FFFFu);
|
||||
lifetime.Entities.SetFinalPhysicsState(
|
||||
record,
|
||||
PhysicsStateFlags.ReportCollisions
|
||||
| PhysicsStateFlags.Missile
|
||||
| PhysicsStateFlags.AlignPath
|
||||
| PhysicsStateFlags.PathClipped);
|
||||
var body = new PhysicsBody
|
||||
{
|
||||
Position = new Vector3(10f, 20f, 5f),
|
||||
Orientation = Quaternion.Identity,
|
||||
InWorld = true,
|
||||
TransientState = TransientStateFlags.Active,
|
||||
};
|
||||
body.set_velocity(new Vector3(10f, 0f, 0f));
|
||||
body.SnapToCell(
|
||||
record.FullCellId,
|
||||
body.Position,
|
||||
body.Position);
|
||||
lifetime.Entities.SetPhysicsBody(record, body);
|
||||
IRuntimeProjectile projectile = lifetime.Physics.BindProjectile(
|
||||
record,
|
||||
body,
|
||||
new ProjectileCollisionSphere(Vector3.Zero, 0.25f));
|
||||
lifetime.Physics.AcknowledgeSpatialProjection(
|
||||
record,
|
||||
spatial: true);
|
||||
var updater =
|
||||
new RuntimeProjectilePhysicsUpdater(lifetime.Physics);
|
||||
|
||||
Assert.True(updater.TryBegin(
|
||||
record,
|
||||
quantum: 0.05f,
|
||||
record.ObjectClockEpoch,
|
||||
externalOwnerValid: null,
|
||||
out RuntimeProjectilePhysicsCommit commit));
|
||||
|
||||
lifetime.Entities.AdvanceVectorAuthority(record);
|
||||
Vector3 correction = new(7f, 8f, 9f);
|
||||
Assert.True(updater.ApplyAuthoritativeVector(
|
||||
record,
|
||||
record.VectorAuthorityVersion,
|
||||
record.VelocityAuthorityVersion,
|
||||
correction,
|
||||
Vector3.UnitZ,
|
||||
currentTime: 1.0));
|
||||
|
||||
Assert.True(
|
||||
projectile.PredictionAuthorityVersion
|
||||
> commit.PredictionAuthorityVersion);
|
||||
Assert.False(updater.Complete(
|
||||
commit,
|
||||
liveCenterX: 1,
|
||||
liveCenterY: 1,
|
||||
_ => true));
|
||||
Assert.Equal(correction, body.Velocity);
|
||||
Assert.Equal(Vector3.UnitZ, body.Omega);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectileQuantumPublishesOneImmutableRuntimeFrame()
|
||||
{
|
||||
using var lifetime = new RuntimeEntityObjectLifetime();
|
||||
RuntimeEntityRecord record =
|
||||
lifetime.Entities.AddActive(Spawn(0x70000033u, 1));
|
||||
lifetime.Entities.SetFullCell(record, 0x01010001u, 0x0101FFFFu);
|
||||
lifetime.Entities.SetFinalPhysicsState(
|
||||
record,
|
||||
PhysicsStateFlags.ReportCollisions
|
||||
| PhysicsStateFlags.Missile
|
||||
| PhysicsStateFlags.PathClipped);
|
||||
var body = new PhysicsBody
|
||||
{
|
||||
Position = new Vector3(10f, 20f, 5f),
|
||||
Orientation = Quaternion.Identity,
|
||||
InWorld = true,
|
||||
TransientState = TransientStateFlags.Active,
|
||||
};
|
||||
body.SnapToCell(
|
||||
record.FullCellId,
|
||||
body.Position,
|
||||
body.Position);
|
||||
lifetime.Entities.SetPhysicsBody(record, body);
|
||||
lifetime.Physics.BindProjectile(
|
||||
record,
|
||||
body,
|
||||
new ProjectileCollisionSphere(Vector3.Zero, 0.25f));
|
||||
lifetime.Physics.AcknowledgeSpatialProjection(
|
||||
record,
|
||||
spatial: true);
|
||||
var updater =
|
||||
new RuntimeProjectilePhysicsUpdater(lifetime.Physics);
|
||||
|
||||
Assert.True(updater.TryBegin(
|
||||
record,
|
||||
quantum: 0.05f,
|
||||
record.ObjectClockEpoch,
|
||||
externalOwnerValid: null,
|
||||
out RuntimeProjectilePhysicsCommit commit));
|
||||
int acknowledgements = 0;
|
||||
RuntimePhysicsFrameSnapshot published = default;
|
||||
|
||||
Assert.True(updater.Complete(
|
||||
commit,
|
||||
liveCenterX: 1,
|
||||
liveCenterY: 1,
|
||||
snapshot =>
|
||||
{
|
||||
published = snapshot;
|
||||
acknowledgements++;
|
||||
return true;
|
||||
}));
|
||||
|
||||
Assert.Equal(1, acknowledgements);
|
||||
Assert.Equal(body.Position, published.Position);
|
||||
Assert.Equal(body.Orientation, published.Orientation);
|
||||
Assert.Equal(record.FullCellId, published.FullCellId);
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
updater.Complete(
|
||||
commit,
|
||||
liveCenterX: 1,
|
||||
liveCenterY: 1,
|
||||
_ => true));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EqualLocalKeysInConcurrentRuntimesDoNotShareWorksets()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue