using System.Collections.Immutable;
using AcDream.App.Physics;
using AcDream.App.Rendering;
using AcDream.Core.Net;
using AcDream.Core.Physics;
using AcDream.Core.Physics.Motion;
using AcDream.Core.World;
namespace AcDream.App.Physics;
///
/// Narrow reverse seam for collaborators constructed before the live network
/// owner. It carries no state or identity: every call resolves the one bound
/// update-thread owner, and use before bind fails immediately.
///
internal interface ILiveEntityMotionRuntimeBindings
{
(float Radius, float Height) GetSetupCylinder(uint serverGuid, WorldEntity entity);
/// TS-46 sibling of — see
/// LiveEntityMotionRuntimeController.GetSetupMoverShape.
(ImmutableArray Spheres, float Scale, float StepUpHeight, float StepDownHeight)
GetSetupMoverShape(uint serverGuid, WorldEntity entity);
bool RouteServerMoveTo(
MovementManager movement,
uint cellId,
WorldSession.EntityMotionUpdate update);
void StickToObjectFromWire(IPhysicsObjHost? host, uint targetGuid);
void ClearTargetForHiddenEntity(uint serverGuid);
IPhysicsObjHost? ResolvePhysicsHost(uint serverGuid);
}
internal sealed class DeferredLiveEntityMotionRuntimeBindings
: ILiveEntityMotionRuntimeBindings
{
private ILiveEntityMotionRuntimeBindings? _target;
public void Bind(ILiveEntityMotionRuntimeBindings target)
{
ArgumentNullException.ThrowIfNull(target);
if (_target is not null)
{
throw new InvalidOperationException(
"Live entity motion runtime bindings are already bound.");
}
_target = target;
}
public IDisposable BindOwned(ILiveEntityMotionRuntimeBindings target)
{
Bind(target);
return new Binding(this, target);
}
private void Unbind(ILiveEntityMotionRuntimeBindings expected)
{
if (ReferenceEquals(_target, expected))
_target = null;
}
private ILiveEntityMotionRuntimeBindings Target =>
_target ?? throw new InvalidOperationException(
"Live entity motion runtime bindings were used before binding.");
public (float Radius, float Height) GetSetupCylinder(
uint serverGuid,
WorldEntity entity) => Target.GetSetupCylinder(serverGuid, entity);
public (ImmutableArray Spheres, float Scale, float StepUpHeight, float StepDownHeight)
GetSetupMoverShape(uint serverGuid, WorldEntity entity) =>
Target.GetSetupMoverShape(serverGuid, entity);
public bool RouteServerMoveTo(
MovementManager movement,
uint cellId,
WorldSession.EntityMotionUpdate update) =>
Target.RouteServerMoveTo(movement, cellId, update);
public void StickToObjectFromWire(IPhysicsObjHost? host, uint targetGuid) =>
Target.StickToObjectFromWire(host, targetGuid);
public void ClearTargetForHiddenEntity(uint serverGuid) =>
Target.ClearTargetForHiddenEntity(serverGuid);
public IPhysicsObjHost? ResolvePhysicsHost(uint serverGuid) =>
Target.ResolvePhysicsHost(serverGuid);
private sealed class Binding : IDisposable
{
private DeferredLiveEntityMotionRuntimeBindings? _owner;
private readonly ILiveEntityMotionRuntimeBindings _expected;
public Binding(
DeferredLiveEntityMotionRuntimeBindings owner,
ILiveEntityMotionRuntimeBindings expected)
{
_owner = owner;
_expected = expected;
}
public void Dispose() =>
Interlocked.Exchange(ref _owner, null)?.Unbind(_expected);
}
}