refactor(runtime): own teleport destination correlation
This commit is contained in:
parent
38b3773cb9
commit
6a063a27d4
23 changed files with 1209 additions and 487 deletions
|
|
@ -5,12 +5,12 @@ using AcDream.App.Physics;
|
|||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Update;
|
||||
using AcDream.App.World;
|
||||
using AcDream.Core.Net;
|
||||
using AcDream.Core.Net.Messages;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.Rendering;
|
||||
using AcDream.Core.World;
|
||||
using AcDream.Runtime;
|
||||
using AcDream.Runtime.World;
|
||||
|
||||
namespace AcDream.App.Streaming;
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ internal interface ILocalPlayerTeleportNetworkSink
|
|||
void OnTeleportStarted(uint sequence);
|
||||
|
||||
void OfferDestination(
|
||||
WorldSession.EntityPositionUpdate update,
|
||||
RuntimeTeleportDestination destination,
|
||||
bool teleportTimestampAdvanced);
|
||||
|
||||
void ResetSession();
|
||||
|
|
@ -56,9 +56,9 @@ internal sealed class DeferredLocalPlayerTeleportNetworkSink
|
|||
public void OnTeleportStarted(uint sequence) => Required().OnTeleportStarted(sequence);
|
||||
|
||||
public void OfferDestination(
|
||||
WorldSession.EntityPositionUpdate update,
|
||||
RuntimeTeleportDestination destination,
|
||||
bool teleportTimestampAdvanced) =>
|
||||
Required().OfferDestination(update, teleportTimestampAdvanced);
|
||||
Required().OfferDestination(destination, teleportTimestampAdvanced);
|
||||
|
||||
public void ResetSession() => Required().ResetSession();
|
||||
|
||||
|
|
@ -372,9 +372,10 @@ internal sealed class LocalPlayerTeleportPresentation
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Single update-thread owner of the local F751/Position-correlated portal
|
||||
/// lifetime: activation, destination aim, readiness hold, Place, viewport
|
||||
/// transitions, LoginComplete, and session/reset symmetry.
|
||||
/// Graphical-host orchestrator for Runtime's local F751/Position-correlated
|
||||
/// portal lifetime. Runtime owns sequence/destination/reveal authority; this
|
||||
/// update-thread adapter owns portal viewport activation, render-space aim,
|
||||
/// readiness preparation, placement callbacks, and LoginComplete presentation.
|
||||
/// </summary>
|
||||
internal sealed class LocalPlayerTeleportController
|
||||
: ILocalPlayerTeleportFramePhase,
|
||||
|
|
@ -382,7 +383,7 @@ internal sealed class LocalPlayerTeleportController
|
|||
AcDream.App.Interaction.ISelectionViewPlaneSource,
|
||||
IDisposable
|
||||
{
|
||||
private readonly TeleportTransitCoordinator<WorldSession.EntityPositionUpdate> _transit = new();
|
||||
private readonly RuntimeWorldTransitState _transit;
|
||||
private readonly ILocalPlayerTeleportAuthority _authority;
|
||||
private readonly ILocalPlayerTeleportInputLifetime _input;
|
||||
private readonly ILocalPlayerTeleportModeOperations _mode;
|
||||
|
|
@ -395,8 +396,7 @@ internal sealed class LocalPlayerTeleportController
|
|||
private Vector3 _pendingPosition;
|
||||
private uint _pendingCell;
|
||||
private Quaternion _pendingRotation = Quaternion.Identity;
|
||||
private bool _hasAcceptedDestination;
|
||||
private WorldSession.EntityPositionUpdate _acceptedDestination;
|
||||
private long _pendingRevealGeneration;
|
||||
private float _holdSeconds;
|
||||
private long _lifetimeGeneration;
|
||||
private bool _disposed;
|
||||
|
|
@ -406,6 +406,7 @@ internal sealed class LocalPlayerTeleportController
|
|||
ILocalPlayerTeleportInputLifetime input,
|
||||
ILocalPlayerTeleportModeOperations mode,
|
||||
ILocalPlayerTeleportStreamingOperations streaming,
|
||||
RuntimeWorldTransitState transit,
|
||||
WorldRevealCoordinator worldReveal,
|
||||
ILocalPlayerTeleportPlacement placement,
|
||||
ILocalPlayerTeleportSession session,
|
||||
|
|
@ -415,22 +416,24 @@ internal sealed class LocalPlayerTeleportController
|
|||
_input = input ?? throw new ArgumentNullException(nameof(input));
|
||||
_mode = mode ?? throw new ArgumentNullException(nameof(mode));
|
||||
_streaming = streaming ?? throw new ArgumentNullException(nameof(streaming));
|
||||
_transit = transit ?? throw new ArgumentNullException(nameof(transit));
|
||||
_worldReveal = worldReveal ?? throw new ArgumentNullException(nameof(worldReveal));
|
||||
_placement = placement ?? throw new ArgumentNullException(nameof(placement));
|
||||
_session = session ?? throw new ArgumentNullException(nameof(session));
|
||||
_presentation = presentation ?? throw new ArgumentNullException(nameof(presentation));
|
||||
}
|
||||
|
||||
public bool IsActive => _transit.IsActive;
|
||||
public bool IsActive => _transit.IsTeleportActive;
|
||||
public bool IsPortalViewportVisible => _presentation.IsPortalViewportVisible;
|
||||
public uint ActiveDestinationCell => _transit.IsActive ? _pendingCell : 0u;
|
||||
public uint ActiveDestinationCell =>
|
||||
_transit.IsTeleportActive ? _pendingCell : 0u;
|
||||
|
||||
public void OnTeleportStarted(uint sequence)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ushort teleportSequence = (ushort)sequence;
|
||||
if (!_authority.IsFreshStart(teleportSequence)
|
||||
|| !_transit.CanBegin(teleportSequence))
|
||||
|| !_transit.CanQueueTeleportStart(teleportSequence))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -444,24 +447,21 @@ internal sealed class LocalPlayerTeleportController
|
|||
if (_lifetimeGeneration != resetGeneration)
|
||||
return;
|
||||
|
||||
_transit.QueueStart(teleportSequence);
|
||||
if (!_transit.TryQueueTeleportStart(teleportSequence))
|
||||
return;
|
||||
TryActivatePendingPresentation();
|
||||
Console.WriteLine($"live: teleport queued (seq={sequence})");
|
||||
}
|
||||
|
||||
public void OfferDestination(
|
||||
WorldSession.EntityPositionUpdate update,
|
||||
RuntimeTeleportDestination destination,
|
||||
bool teleportTimestampAdvanced)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (_transit.OfferDestination(
|
||||
update.TeleportSequence,
|
||||
teleportTimestampAdvanced,
|
||||
update,
|
||||
out WorldSession.EntityPositionUpdate destination))
|
||||
{
|
||||
AcceptDestination(destination);
|
||||
}
|
||||
_transit.OfferTeleportDestination(
|
||||
destination,
|
||||
teleportTimestampAdvanced);
|
||||
TryAimAcceptedDestination();
|
||||
}
|
||||
|
||||
public void Tick(float deltaSeconds)
|
||||
|
|
@ -469,11 +469,11 @@ internal sealed class LocalPlayerTeleportController
|
|||
ThrowIfDisposed();
|
||||
TryActivatePendingPresentation();
|
||||
TryAimAcceptedDestination();
|
||||
if (!_transit.IsActive)
|
||||
if (!_transit.IsTeleportActive)
|
||||
return;
|
||||
|
||||
long generation = _lifetimeGeneration;
|
||||
ushort sequence = _transit.Sequence;
|
||||
ushort sequence = _transit.ActiveTeleportSequence;
|
||||
if (!_mode.TryEnterPortalSpace()
|
||||
|| !IsCurrentLifetime(generation, sequence)
|
||||
|| _mode.Controller is null)
|
||||
|
|
@ -506,13 +506,23 @@ internal sealed class LocalPlayerTeleportController
|
|||
switch (teleportEvent)
|
||||
{
|
||||
case TeleportAnimEvent.Place:
|
||||
if (!_worldReveal.CanPlacePortalDestination(
|
||||
_pendingRevealGeneration,
|
||||
sequence,
|
||||
_pendingCell))
|
||||
{
|
||||
return;
|
||||
}
|
||||
_placement.Place(
|
||||
_pendingPosition,
|
||||
_pendingCell,
|
||||
_pendingRotation);
|
||||
if (!IsCurrentLifetime(generation, sequence))
|
||||
return;
|
||||
_worldReveal.ObserveMaterialized();
|
||||
_worldReveal.ObserveMaterialized(
|
||||
_pendingRevealGeneration,
|
||||
sequence,
|
||||
_pendingCell);
|
||||
if (!IsCurrentLifetime(generation, sequence))
|
||||
return;
|
||||
break;
|
||||
|
|
@ -571,43 +581,43 @@ internal sealed class LocalPlayerTeleportController
|
|||
|
||||
private void TryActivatePendingPresentation()
|
||||
{
|
||||
if (!_transit.HasPendingStart)
|
||||
if (!_transit.HasPendingTeleportStart)
|
||||
return;
|
||||
|
||||
long generation = _lifetimeGeneration;
|
||||
if (!_mode.TryEnterPortalSpace()
|
||||
|| _lifetimeGeneration != generation
|
||||
|| !_transit.HasPendingStart)
|
||||
|| !_transit.HasPendingTeleportStart)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_holdSeconds = 0f;
|
||||
_presentation.Begin(_mode.Projection);
|
||||
if (_lifetimeGeneration != generation || !_transit.HasPendingStart)
|
||||
if (_lifetimeGeneration != generation
|
||||
|| !_transit.HasPendingTeleportStart)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasBufferedDestination = _transit.Activate(out var destination);
|
||||
if (hasBufferedDestination)
|
||||
AcceptDestination(destination);
|
||||
Console.WriteLine(
|
||||
$"live: teleport presentation started (seq={_transit.Sequence})");
|
||||
}
|
||||
|
||||
private void AcceptDestination(WorldSession.EntityPositionUpdate destination)
|
||||
{
|
||||
_acceptedDestination = destination;
|
||||
_hasAcceptedDestination = true;
|
||||
if (!_transit.ActivateQueuedTeleport())
|
||||
return;
|
||||
TryAimAcceptedDestination();
|
||||
Console.WriteLine(
|
||||
$"live: teleport presentation started "
|
||||
+ $"(seq={_transit.ActiveTeleportSequence})");
|
||||
}
|
||||
|
||||
private void TryAimAcceptedDestination()
|
||||
{
|
||||
if (!_hasAcceptedDestination || !_transit.IsActive)
|
||||
if (!_transit.TryGetAcceptedTeleportDestination(
|
||||
out RuntimeTeleportDestination destination))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long generation = _lifetimeGeneration;
|
||||
ushort sequence = _transit.Sequence;
|
||||
ushort sequence = _transit.ActiveTeleportSequence;
|
||||
PlayerMovementController? controller = _mode.Controller;
|
||||
if (controller is null)
|
||||
{
|
||||
|
|
@ -622,26 +632,19 @@ internal sealed class LocalPlayerTeleportController
|
|||
return;
|
||||
}
|
||||
|
||||
WorldSession.EntityPositionUpdate destination = _acceptedDestination;
|
||||
if (!AimDestination(destination, controller, generation, sequence))
|
||||
return;
|
||||
|
||||
if (IsCurrentLifetime(generation, sequence))
|
||||
{
|
||||
_hasAcceptedDestination = false;
|
||||
_acceptedDestination = default;
|
||||
}
|
||||
}
|
||||
|
||||
private bool AimDestination(
|
||||
WorldSession.EntityPositionUpdate update,
|
||||
RuntimeTeleportDestination destination,
|
||||
PlayerMovementController controller,
|
||||
long generation,
|
||||
ushort sequence)
|
||||
{
|
||||
var position = update.Position;
|
||||
int landblockX = (int)((position.LandblockId >> 24) & 0xFFu);
|
||||
int landblockY = (int)((position.LandblockId >> 16) & 0xFFu);
|
||||
Position position = destination.Position;
|
||||
int landblockX = (int)((position.ObjCellId >> 24) & 0xFFu);
|
||||
int landblockY = (int)((position.ObjCellId >> 16) & 0xFFu);
|
||||
uint streamingOriginLandblockId = StreamingRegion.EncodeLandblockId(
|
||||
_streaming.CenterX,
|
||||
_streaming.CenterY);
|
||||
|
|
@ -649,14 +652,11 @@ internal sealed class LocalPlayerTeleportController
|
|||
(landblockX - _streaming.CenterX) * 192f,
|
||||
(landblockY - _streaming.CenterY) * 192f,
|
||||
0f);
|
||||
var translated = new Vector3(
|
||||
position.PositionX,
|
||||
position.PositionY,
|
||||
position.PositionZ) + origin;
|
||||
Vector3 translated = position.Frame.Origin + origin;
|
||||
|
||||
TeleportLandblockTransition transition = TeleportLandblockTransition.Classify(
|
||||
controller.CellId,
|
||||
position.LandblockId,
|
||||
position.ObjCellId,
|
||||
streamingOriginLandblockId);
|
||||
int oldX = (int)((transition.SourceLandblockId >> 24) & 0xFFu);
|
||||
int oldY = (int)((transition.SourceLandblockId >> 16) & 0xFFu);
|
||||
|
|
@ -669,7 +669,14 @@ internal sealed class LocalPlayerTeleportController
|
|||
// Retail SmartBox enters blocking_for_cells before old-world object,
|
||||
// physics, landscape, and ambient owners may advance again. Begin the
|
||||
// reveal generation before a recenter can start detaching that world.
|
||||
_worldReveal.Begin(RuntimePortalKind.Portal, position.LandblockId);
|
||||
if (!_worldReveal.TryBeginPortal(
|
||||
sequence,
|
||||
position.ObjCellId,
|
||||
out long revealGeneration))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_pendingRevealGeneration = revealGeneration;
|
||||
if (!IsCurrentLifetime(generation, sequence))
|
||||
return false;
|
||||
|
||||
|
|
@ -677,7 +684,7 @@ internal sealed class LocalPlayerTeleportController
|
|||
if (transition.ChangesStreamingCenter)
|
||||
{
|
||||
bool isSealedDungeon = _streaming.IsSealedDungeon(
|
||||
position.LandblockId);
|
||||
position.ObjCellId);
|
||||
if (!IsCurrentLifetime(generation, sequence))
|
||||
return false;
|
||||
|
||||
|
|
@ -688,28 +695,24 @@ internal sealed class LocalPlayerTeleportController
|
|||
if (!IsCurrentLifetime(generation, sequence))
|
||||
return false;
|
||||
worldPosition = new Vector3(
|
||||
position.PositionX,
|
||||
position.PositionY,
|
||||
position.PositionZ);
|
||||
position.Frame.Origin.X,
|
||||
position.Frame.Origin.Y,
|
||||
position.Frame.Origin.Z);
|
||||
}
|
||||
else
|
||||
{
|
||||
worldPosition = translated;
|
||||
}
|
||||
|
||||
_pendingRotation = new Quaternion(
|
||||
position.RotationX,
|
||||
position.RotationY,
|
||||
position.RotationZ,
|
||||
position.RotationW);
|
||||
_pendingRotation = position.Frame.Orientation;
|
||||
_pendingPosition = worldPosition;
|
||||
_pendingCell = position.LandblockId;
|
||||
_pendingCell = position.ObjCellId;
|
||||
_holdSeconds = 0f;
|
||||
PhysicsDiagnostics.LogTeleport(
|
||||
"AIM",
|
||||
position.LandblockId,
|
||||
$"seq={update.TeleportSequence} lb={landblockX},{landblockY} "
|
||||
+ $"indoor={((position.LandblockId & 0xFFFFu) >= 0x0100u)} "
|
||||
position.ObjCellId,
|
||||
$"seq={destination.TeleportSequence} lb={landblockX},{landblockY} "
|
||||
+ $"indoor={((position.ObjCellId & 0xFFFFu) >= 0x0100u)} "
|
||||
+ $"playerCross={transition.CrossesLandblock} "
|
||||
+ $"centerChange={transition.ChangesStreamingCenter}");
|
||||
return true;
|
||||
|
|
@ -718,20 +721,11 @@ internal sealed class LocalPlayerTeleportController
|
|||
private long ResetTransit(bool clearSession)
|
||||
{
|
||||
long generation = checked(++_lifetimeGeneration);
|
||||
if (clearSession)
|
||||
{
|
||||
_transit.ClearSession();
|
||||
}
|
||||
else
|
||||
{
|
||||
_transit.EndActive();
|
||||
}
|
||||
|
||||
_pendingPosition = default;
|
||||
_pendingCell = 0u;
|
||||
_pendingRotation = Quaternion.Identity;
|
||||
_hasAcceptedDestination = false;
|
||||
_acceptedDestination = default;
|
||||
_pendingRevealGeneration = 0;
|
||||
_holdSeconds = 0f;
|
||||
|
||||
_streaming.ResetRecenter(clearSession);
|
||||
|
|
@ -741,7 +735,10 @@ internal sealed class LocalPlayerTeleportController
|
|||
if (clearSession)
|
||||
_worldReveal.ResetSession();
|
||||
else
|
||||
{
|
||||
_transit.EndTeleport();
|
||||
_worldReveal.Cancel();
|
||||
}
|
||||
if (_lifetimeGeneration != generation)
|
||||
return generation;
|
||||
|
||||
|
|
@ -754,8 +751,8 @@ internal sealed class LocalPlayerTeleportController
|
|||
|
||||
private bool IsCurrentLifetime(long generation, ushort sequence) =>
|
||||
_lifetimeGeneration == generation
|
||||
&& _transit.IsActive
|
||||
&& _transit.Sequence == sequence;
|
||||
&& _transit.IsTeleportActive
|
||||
&& _transit.ActiveTeleportSequence == sequence;
|
||||
|
||||
private void ThrowIfDisposed() =>
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue