refactor(runtime): close simulation ownership
Move remote-motion construction, CreateObject vector initialization, final simulation-component retirement, and the combined J5 ownership ledger into Runtime. Delete App compatibility views and moved-state reconstruction while preserving the existing graphical projection and retail update order.
This commit is contained in:
parent
c30a3efeb0
commit
cdee7a4b49
57 changed files with 1013 additions and 410 deletions
|
|
@ -223,33 +223,27 @@ public sealed class LiveEntityRecord
|
|||
public PhysicsBody? PhysicsBody
|
||||
{
|
||||
get => Canonical.PhysicsBody;
|
||||
internal set => _directory.SetPhysicsBody(Canonical, value);
|
||||
}
|
||||
internal bool PhysicsBodyAcquisitionInProgress
|
||||
{
|
||||
get => Canonical.PhysicsBodyAcquisitionInProgress;
|
||||
set => _directory.SetPhysicsBodyAcquisitionInProgress(Canonical, value);
|
||||
}
|
||||
public ILiveEntityAnimationRuntime? AnimationRuntime { get; internal set; }
|
||||
public ILiveEntityRemoteMotionRuntime? RemoteMotionRuntime
|
||||
public IRuntimeRemoteMotion? RemoteMotionRuntime
|
||||
{
|
||||
get => Canonical.RemoteMotion;
|
||||
internal set => _directory.SetRemoteMotion(Canonical, value);
|
||||
}
|
||||
internal bool RemoteMotionBindingInProgress
|
||||
{
|
||||
get => Canonical.RemoteMotionBindingInProgress;
|
||||
set => _directory.SetRemoteMotionBindingInProgress(Canonical, value);
|
||||
}
|
||||
public AcDream.Core.Physics.Motion.IPhysicsObjHost? PhysicsHost
|
||||
{
|
||||
get => Canonical.PhysicsHost;
|
||||
internal set => _directory.SetPhysicsHost(Canonical, value);
|
||||
}
|
||||
internal bool RequiresRemotePlacementRuntime
|
||||
{
|
||||
get => Canonical.RequiresRemotePlacementRuntime;
|
||||
set => _directory.SetRequiresRemotePlacementRuntime(Canonical, value);
|
||||
}
|
||||
public IRuntimeProjectile? ProjectileRuntime => Canonical.Projectile;
|
||||
public ILiveEntityEffectProfile? EffectProfile { get; internal set; }
|
||||
|
|
@ -1389,7 +1383,7 @@ public sealed class LiveEntityRuntime : ILiveEntityRadarSource
|
|||
ProjectionIsCurrent);
|
||||
}
|
||||
|
||||
public void SetRemoteMotionRuntime(uint serverGuid, ILiveEntityRemoteMotionRuntime runtime)
|
||||
internal void SetRemoteMotionRuntime(uint serverGuid, IRuntimeRemoteMotion runtime)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(runtime);
|
||||
if (!_projections.TryGetCurrent(serverGuid, out LiveEntityRecord? record))
|
||||
|
|
@ -1442,7 +1436,7 @@ public sealed class LiveEntityRuntime : ILiveEntityRadarSource
|
|||
|
||||
public bool TryGetRemoteMotionRuntime(
|
||||
uint serverGuid,
|
||||
out ILiveEntityRemoteMotionRuntime runtime)
|
||||
out IRuntimeRemoteMotion runtime)
|
||||
{
|
||||
if (_projections.TryGetCurrent(
|
||||
serverGuid,
|
||||
|
|
@ -1457,7 +1451,7 @@ public sealed class LiveEntityRuntime : ILiveEntityRadarSource
|
|||
return false;
|
||||
}
|
||||
|
||||
public bool ClearRemoteMotionRuntime(uint serverGuid)
|
||||
internal bool ClearRemoteMotionRuntime(uint serverGuid)
|
||||
{
|
||||
if (!_projections.TryGetCurrent(serverGuid, out LiveEntityRecord? record)
|
||||
|| record.RemoteMotionRuntime is null)
|
||||
|
|
@ -1468,6 +1462,26 @@ public sealed class LiveEntityRuntime : ILiveEntityRadarSource
|
|||
return true;
|
||||
}
|
||||
|
||||
internal RemoteMotion GetOrCreateRemoteMotionRuntime(uint serverGuid)
|
||||
{
|
||||
if (!_projections.TryGetCurrent(
|
||||
serverGuid,
|
||||
out LiveEntityRecord? record))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Cannot acquire remote motion before live entity 0x{serverGuid:X8} exists.");
|
||||
}
|
||||
|
||||
bool ProjectionIsCurrent() =>
|
||||
_projections.TryGetCurrent(
|
||||
serverGuid,
|
||||
out LiveEntityRecord? current)
|
||||
&& ReferenceEquals(current, record);
|
||||
return _physics.GetOrCreateRemoteMotion(
|
||||
record.Canonical,
|
||||
ProjectionIsCurrent);
|
||||
}
|
||||
|
||||
internal IRuntimeProjectile BindProjectileRuntime(
|
||||
uint serverGuid,
|
||||
PhysicsBody body,
|
||||
|
|
@ -1874,27 +1888,6 @@ public sealed class LiveEntityRuntime : ILiveEntityRadarSource
|
|||
destination.Add(key.LocalEntityId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Host side of retail <c>CPhysicsObj::set_active(1)</c> for a movement
|
||||
/// manager owned by this exact incarnation. Static is a required no-op.
|
||||
/// </summary>
|
||||
internal bool TryActivateOrdinaryObject(
|
||||
uint serverGuid,
|
||||
ILiveEntityRemoteMotionRuntime runtime)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(runtime);
|
||||
if (!_projections.TryGetCurrent(serverGuid, out LiveEntityRecord? record)
|
||||
|| !ReferenceEquals(record.RemoteMotionRuntime, runtime)
|
||||
|| (record.FinalPhysicsState & PhysicsStateFlags.Static) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
record.ObjectClock.Activate();
|
||||
runtime.Body.TransientState |= TransientStateFlags.Active;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commits retail <c>CPhysicsObj::set_velocity</c> (0x005113F0) to the
|
||||
/// canonical body for this exact incarnation. The body and the retained
|
||||
|
|
@ -1941,41 +1934,19 @@ public sealed class LiveEntityRuntime : ILiveEntityRadarSource
|
|||
{
|
||||
ArgumentNullException.ThrowIfNull(record);
|
||||
ArgumentNullException.ThrowIfNull(body);
|
||||
if (!IsFinite(velocity)
|
||||
|| (angularVelocity is { } omega && !IsFinite(omega))
|
||||
|| !double.IsFinite(currentTime)
|
||||
|| !_projections.TryGetCurrent(
|
||||
bool ProjectionIsCurrent() =>
|
||||
_projections.TryGetCurrent(
|
||||
record.ServerGuid,
|
||||
out LiveEntityRecord? current)
|
||||
|| !ReferenceEquals(current, record)
|
||||
|| !ReferenceEquals(current.PhysicsBody, body))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wasBodyActive =
|
||||
(body.TransientState & TransientStateFlags.Active) != 0;
|
||||
body.set_velocity(velocity);
|
||||
if ((current.FinalPhysicsState & PhysicsStateFlags.Static) != 0)
|
||||
{
|
||||
// PhysicsBody models set_velocity's leaf write and therefore sets
|
||||
// Active unconditionally. Retail CPhysicsObj::set_active(1) is a
|
||||
// no-op for Static, so restore the exact prior bit here.
|
||||
if (!wasBodyActive)
|
||||
body.TransientState &= ~TransientStateFlags.Active;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool clockReactivated = current.ObjectClock.Activate();
|
||||
// PhysicsBody.LastUpdateTime remains only the compatibility clock
|
||||
// for older absolute-time helpers; the record clock is canonical.
|
||||
if (clockReactivated || !wasBodyActive)
|
||||
body.LastUpdateTime = currentTime;
|
||||
}
|
||||
|
||||
if (angularVelocity is { } acceptedOmega)
|
||||
body.Omega = acceptedOmega;
|
||||
return true;
|
||||
&& ReferenceEquals(current, record)
|
||||
&& ReferenceEquals(current.PhysicsBody, body);
|
||||
return _physics.TryCommitAuthoritativeVector(
|
||||
record.Canonical,
|
||||
body,
|
||||
velocity,
|
||||
angularVelocity,
|
||||
currentTime,
|
||||
ProjectionIsCurrent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -2007,7 +1978,7 @@ public sealed class LiveEntityRuntime : ILiveEntityRadarSource
|
|||
|
||||
internal bool IsCurrentSpatialRemoteMotion(
|
||||
LiveEntityRecord record,
|
||||
ILiveEntityRemoteMotionRuntime runtime) =>
|
||||
IRuntimeRemoteMotion runtime) =>
|
||||
IsCurrentRecord(record)
|
||||
&& _physics.IsSpatialRemote(record.Canonical, runtime)
|
||||
&& HasSpatialRuntimeProjection(record);
|
||||
|
|
@ -2546,11 +2517,6 @@ public sealed class LiveEntityRuntime : ILiveEntityRadarSource
|
|||
body.TransientState &= ~TransientStateFlags.Active;
|
||||
}
|
||||
|
||||
private static bool IsFinite(Vector3 value) =>
|
||||
float.IsFinite(value.X)
|
||||
&& float.IsFinite(value.Y)
|
||||
&& float.IsFinite(value.Z);
|
||||
|
||||
private void PublishProjectionVisibilityChanged(LiveEntityRecord record, bool visible)
|
||||
{
|
||||
Delegate[] subscribers = ProjectionVisibilityChanged?.GetInvocationList()
|
||||
|
|
@ -2728,16 +2694,11 @@ public sealed class LiveEntityRuntime : ILiveEntityRadarSource
|
|||
}
|
||||
|
||||
if (record.WorldEntity is not null)
|
||||
{
|
||||
_ = RequireProjectionKey(record);
|
||||
_directory.ReleaseLocalId(record.Canonical);
|
||||
}
|
||||
_entityObjects.CompleteProjectionRetirement(
|
||||
record.Canonical);
|
||||
|
||||
record.AnimationRuntime = null;
|
||||
record.RemoteMotionRuntime = null;
|
||||
record.PhysicsHost = null;
|
||||
record.RequiresRemotePlacementRuntime = false;
|
||||
record.PhysicsBody = null;
|
||||
record.EffectProfile = null;
|
||||
record.IsSpatiallyProjected = false;
|
||||
record.IsSpatiallyVisible = false;
|
||||
|
|
|
|||
|
|
@ -133,36 +133,3 @@ public sealed class LiveEntityAnimationRuntimeView<TAnimation>
|
|||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Storage-free typed view over remote-motion components.</summary>
|
||||
public sealed class LiveEntityRemoteMotionRuntimeView<TRemoteMotion>
|
||||
where TRemoteMotion : class, ILiveEntityRemoteMotionRuntime
|
||||
{
|
||||
private readonly ILiveEntityRuntimeSource _runtime;
|
||||
|
||||
internal LiveEntityRemoteMotionRuntimeView(ILiveEntityRuntimeSource runtime) =>
|
||||
_runtime = runtime ?? throw new ArgumentNullException(nameof(runtime));
|
||||
|
||||
public TRemoteMotion this[uint serverGuid]
|
||||
{
|
||||
set => (_runtime.Current
|
||||
?? throw new InvalidOperationException("Live entity runtime is not initialized."))
|
||||
.SetRemoteMotionRuntime(serverGuid, value);
|
||||
}
|
||||
|
||||
public bool TryGetValue(uint serverGuid, out TRemoteMotion motion)
|
||||
{
|
||||
if (_runtime.Current?.TryGetRemoteMotionRuntime(serverGuid, out var found) == true
|
||||
&& found is TRemoteMotion typed)
|
||||
{
|
||||
motion = typed;
|
||||
return true;
|
||||
}
|
||||
|
||||
motion = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Remove(uint serverGuid) =>
|
||||
_runtime.Current?.ClearRemoteMotionRuntime(serverGuid) == true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue