feat(runtime): publish dormant local physics ownership

This commit is contained in:
Erik 2026-08-01 10:01:30 +02:00
parent 442cb8f97b
commit 22651c823d
10 changed files with 1617 additions and 25 deletions

View file

@ -1100,6 +1100,74 @@ public class PlayerMovementControllerTests
public bool StopMotion(uint motion) => true;
}
[Fact]
public void PublicationLifecycleRequiresExplicitActivationAfterOwnershipCommit()
{
var candidate = PlayerMovementController.CreatePublicationCandidate(
new PhysicsEngine(),
PlayerMovementConstructionOptions.Fallback);
candidate.LocalEntityId = 0x70004001u;
candidate.StepUpHeight = 0.4f;
candidate.StepDownHeight = 0.4f;
candidate.ObjectScale = 1f;
candidate.PreparePositionForCommit(
new Vector3(1f, 2f, 3f),
0xA9B40021u,
new Vector3(1f, 2f, 3f));
candidate.SetBodyOrientation(Quaternion.Identity);
candidate.ApplyPhysicsState(PhysicsStateFlags.Gravity);
PhysicsBody body = candidate.PhysicsBody;
body.InWorld = false;
body.TransientState &= ~TransientStateFlags.Active;
Assert.False(body.InWorld);
candidate.SealPublicationCandidate();
Assert.True(candidate.IsSealedPublicationCandidate);
Assert.Throws<InvalidOperationException>(() => candidate.Update(
1f / 60f,
default));
Assert.Throws<InvalidOperationException>(() => candidate.TickHidden(
1f / 60f));
Assert.Throws<InvalidOperationException>(() => candidate.SetPosition(
Vector3.One,
0xA9B40021u,
Vector3.One));
Assert.Throws<InvalidOperationException>(() => candidate.BlipPosition(
Vector3.One,
0xA9B40021u,
Vector3.One));
Assert.Throws<InvalidOperationException>(() =>
candidate.CaptureMovementResult(mouseLookEvent: false));
Assert.Throws<InvalidOperationException>(() =>
candidate.NoteMovementSent(1f));
Assert.Throws<InvalidOperationException>(candidate.CommitPreparedPosition);
Assert.Throws<InvalidOperationException>(() =>
candidate.ApplyPhysicsState(PhysicsStateFlags.Frozen));
Assert.Throws<InvalidOperationException>(() => candidate.LocalEntityId = 2u);
Assert.Throws<InvalidOperationException>(() => _ = candidate.Movement);
var canonicalClock = new RetailObjectQuantumClock();
candidate.CommitRuntimeOwnership(canonicalClock);
Assert.Throws<InvalidOperationException>(() => candidate.Update(
1f / 60f,
default));
Assert.Throws<InvalidOperationException>(() =>
candidate.CaptureMovementResult(mouseLookEvent: false));
Assert.Throws<InvalidOperationException>(() => _ = candidate.PhysicsBody);
candidate.ActivateRuntimePublication();
Assert.Same(body, candidate.PhysicsBody);
_ = candidate.CaptureMovementResult(mouseLookEvent: false);
_ = candidate.Update(0f, default);
candidate.ApplyPhysicsState(PhysicsStateFlags.Gravity);
candidate.RetireRuntimePublication();
Assert.Throws<InvalidOperationException>(() => candidate.Update(
1f / 60f,
default));
}
[Fact]
public void Update_RunningJumpLandsOnFlatGround_ResidualVelocitySurvivesAndDecays_NotFrozen()
{