refactor(camera): extract the update frame
Move fly/chase publication, combat target tracking, and local player projection behind typed runtime owners. Preserve the inbound-created projection/reconcile barrier while removing GameWindow callbacks and duplicate shadow helpers.
This commit is contained in:
parent
eeb0f6b45c
commit
947c61d2d7
19 changed files with 988 additions and 356 deletions
|
|
@ -1,47 +1,77 @@
|
|||
using AcDream.Core.World;
|
||||
using AcDream.App.Physics;
|
||||
using AcDream.App.World;
|
||||
|
||||
namespace AcDream.App.Input;
|
||||
|
||||
internal interface ILocalPlayerProjectionRuntime
|
||||
{
|
||||
WorldEntity? ResolveEntity();
|
||||
int LiveCenterX { get; }
|
||||
int LiveCenterY { get; }
|
||||
void SyncShadow(WorldEntity entity, uint cellId);
|
||||
void Rebucket(uint serverGuid, uint landblockId);
|
||||
bool IsCurrentVisibleProjection(WorldEntity entity);
|
||||
void SuspendShadow(WorldEntity entity);
|
||||
}
|
||||
|
||||
internal sealed class LiveLocalPlayerProjectionRuntime
|
||||
: ILocalPlayerProjectionRuntime
|
||||
{
|
||||
private readonly LiveEntityRuntime _liveEntities;
|
||||
private readonly ILocalPlayerIdentitySource _identity;
|
||||
private readonly LiveWorldOriginState _origin;
|
||||
private readonly LocalPlayerShadowSynchronizer _shadow;
|
||||
|
||||
public LiveLocalPlayerProjectionRuntime(
|
||||
LiveEntityRuntime liveEntities,
|
||||
ILocalPlayerIdentitySource identity,
|
||||
LiveWorldOriginState origin,
|
||||
LocalPlayerShadowSynchronizer shadow)
|
||||
{
|
||||
_liveEntities = liveEntities ?? throw new ArgumentNullException(nameof(liveEntities));
|
||||
_identity = identity ?? throw new ArgumentNullException(nameof(identity));
|
||||
_origin = origin ?? throw new ArgumentNullException(nameof(origin));
|
||||
_shadow = shadow ?? throw new ArgumentNullException(nameof(shadow));
|
||||
}
|
||||
|
||||
public WorldEntity? ResolveEntity() =>
|
||||
_liveEntities.TryGetWorldEntity(_identity.ServerGuid, out var entity)
|
||||
? entity
|
||||
: null;
|
||||
|
||||
public int LiveCenterX => _origin.CenterX;
|
||||
public int LiveCenterY => _origin.CenterY;
|
||||
|
||||
public void SyncShadow(WorldEntity entity, uint cellId) =>
|
||||
_shadow.Sync(entity, cellId);
|
||||
|
||||
public void Rebucket(uint serverGuid, uint landblockId) =>
|
||||
_liveEntities.RebucketLiveEntity(serverGuid, landblockId);
|
||||
|
||||
public bool IsCurrentVisibleProjection(WorldEntity entity) =>
|
||||
_shadow.IsCurrentVisibleProjection(entity);
|
||||
|
||||
public void SuspendShadow(WorldEntity entity) => _shadow.Suspend(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projects the canonical local physics body into world rendering and spatial
|
||||
/// buckets without advancing it.
|
||||
/// </summary>
|
||||
public sealed class LocalPlayerProjectionController
|
||||
{
|
||||
private readonly Func<WorldEntity?> _resolveEntity;
|
||||
private readonly Func<int> _liveCenterX;
|
||||
private readonly Func<int> _liveCenterY;
|
||||
private readonly Action<WorldEntity, uint> _syncShadow;
|
||||
private readonly Action<uint, uint> _rebucket;
|
||||
private readonly Func<WorldEntity, bool> _isCurrentVisibleProjection;
|
||||
private readonly Action<WorldEntity> _suspendShadow;
|
||||
private readonly ILocalPlayerProjectionRuntime _runtime;
|
||||
|
||||
public LocalPlayerProjectionController(
|
||||
Func<WorldEntity?> resolveEntity,
|
||||
Func<int> liveCenterX,
|
||||
Func<int> liveCenterY,
|
||||
Action<WorldEntity, uint> syncShadow,
|
||||
Action<uint, uint> rebucket,
|
||||
Func<WorldEntity, bool> isCurrentVisibleProjection,
|
||||
Action<WorldEntity> suspendShadow)
|
||||
{
|
||||
_resolveEntity = resolveEntity ?? throw new ArgumentNullException(nameof(resolveEntity));
|
||||
_liveCenterX = liveCenterX ?? throw new ArgumentNullException(nameof(liveCenterX));
|
||||
_liveCenterY = liveCenterY ?? throw new ArgumentNullException(nameof(liveCenterY));
|
||||
_syncShadow = syncShadow ?? throw new ArgumentNullException(nameof(syncShadow));
|
||||
_rebucket = rebucket ?? throw new ArgumentNullException(nameof(rebucket));
|
||||
_isCurrentVisibleProjection = isCurrentVisibleProjection
|
||||
?? throw new ArgumentNullException(nameof(isCurrentVisibleProjection));
|
||||
_suspendShadow = suspendShadow
|
||||
?? throw new ArgumentNullException(nameof(suspendShadow));
|
||||
}
|
||||
internal LocalPlayerProjectionController(ILocalPlayerProjectionRuntime runtime) =>
|
||||
_runtime = runtime ?? throw new ArgumentNullException(nameof(runtime));
|
||||
|
||||
public void Project(
|
||||
PlayerMovementController controller,
|
||||
MovementResult movement,
|
||||
bool hidden)
|
||||
{
|
||||
WorldEntity? entity = _resolveEntity();
|
||||
WorldEntity? entity = _runtime.ResolveEntity();
|
||||
if (entity is null)
|
||||
return;
|
||||
|
||||
|
|
@ -62,8 +92,8 @@ public sealed class LocalPlayerProjectionController
|
|||
else
|
||||
{
|
||||
System.Numerics.Vector3 position = controller.Position;
|
||||
int landblockX = _liveCenterX() + (int)Math.Floor(position.X / 192f);
|
||||
int landblockY = _liveCenterY() + (int)Math.Floor(position.Y / 192f);
|
||||
int landblockX = _runtime.LiveCenterX + (int)Math.Floor(position.X / 192f);
|
||||
int landblockY = _runtime.LiveCenterY + (int)Math.Floor(position.Y / 192f);
|
||||
currentLandblock = (uint)((landblockX << 24) | (landblockY << 16) | 0xFFFF);
|
||||
}
|
||||
|
||||
|
|
@ -76,15 +106,15 @@ public sealed class LocalPlayerProjectionController
|
|||
// membership, then replaces collision rows only while the object still
|
||||
// owns a loaded cell. Rebucket is a synchronous callback boundary: it
|
||||
// can move this incarnation to pending or replace the GUID entirely.
|
||||
_rebucket(entity.ServerGuid, currentLandblock);
|
||||
_runtime.Rebucket(entity.ServerGuid, currentLandblock);
|
||||
if (hidden
|
||||
|| movement.CellId == 0
|
||||
|| !_isCurrentVisibleProjection(entity))
|
||||
|| !_runtime.IsCurrentVisibleProjection(entity))
|
||||
{
|
||||
_suspendShadow(entity);
|
||||
_runtime.SuspendShadow(entity);
|
||||
return;
|
||||
}
|
||||
|
||||
_syncShadow(entity, movement.CellId);
|
||||
_runtime.SyncShadow(entity, movement.CellId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue