fix(physics): keep remote placement and targeting in one world frame
CreateObject positions are landblock-local, but Runtime first-entry previously submitted remotes with a zero world offset. Runtime now owns the accepted local-player world-frame center and converts remote placements before SetPosition. The local physics host also publishes body.Position rather than CellPosition's landblock-local origin, so TargetManager no longer directs monsters toward a phantom player position. User gate: monster/static placement, chase, and attacks accepted outside Tusker Barracks.
This commit is contained in:
parent
01f4791e95
commit
670f307c84
7 changed files with 140 additions and 12 deletions
|
|
@ -448,6 +448,7 @@ public sealed class RuntimePhysicsState : IDisposable
|
|||
private int _collisionMutationThreadId;
|
||||
private long _nextCollisionPreparationSequence;
|
||||
private ulong _collisionWorldAuthority = 1UL;
|
||||
private uint _worldFrameCenterLandblockId;
|
||||
private readonly List<Action<RuntimeCollisionGenerationCommitted>>
|
||||
_collisionGenerationCommittedObservers = new();
|
||||
private bool _disposed;
|
||||
|
|
@ -524,6 +525,52 @@ public sealed class RuntimePhysicsState : IDisposable
|
|||
internal double PlacementSimulationTime(double fallback) =>
|
||||
_gameClock?.SimulationTimeSeconds ?? fallback;
|
||||
|
||||
/// <summary>
|
||||
/// Establishes the landblock that maps to world-frame XY (0,0). The
|
||||
/// local-player Create initializes it before remote first-entry work;
|
||||
/// only an accepted teleport moves it afterward. Ordinary walking across
|
||||
/// a landblock boundary does not rebase the streamed world.
|
||||
/// </summary>
|
||||
internal void ObserveLocalWorldFrame(
|
||||
uint fullCellId,
|
||||
bool teleportAdvanced)
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
if (fullCellId == 0u)
|
||||
return;
|
||||
if (_worldFrameCenterLandblockId == 0u || teleportAdvanced)
|
||||
{
|
||||
_worldFrameCenterLandblockId =
|
||||
(fullCellId & 0xFFFF0000u) | 0xFFFFu;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a retail landblock-local network frame into the Runtime's
|
||||
/// current world frame without consulting presentation or waiting for
|
||||
/// the destination collision package.
|
||||
/// </summary>
|
||||
internal bool TryGetWorldFrameOffset(
|
||||
uint fullCellId,
|
||||
out float worldOffsetX,
|
||||
out float worldOffsetY)
|
||||
{
|
||||
if (_worldFrameCenterLandblockId == 0u || fullCellId == 0u)
|
||||
{
|
||||
worldOffsetX = 0f;
|
||||
worldOffsetY = 0f;
|
||||
return false;
|
||||
}
|
||||
|
||||
int centerX = (int)((_worldFrameCenterLandblockId >> 24) & 0xFFu);
|
||||
int centerY = (int)((_worldFrameCenterLandblockId >> 16) & 0xFFu);
|
||||
int landblockX = (int)((fullCellId >> 24) & 0xFFu);
|
||||
int landblockY = (int)((fullCellId >> 16) & 0xFFu);
|
||||
worldOffsetX = (landblockX - centerX) * 192f;
|
||||
worldOffsetY = (landblockY - centerY) * 192f;
|
||||
return true;
|
||||
}
|
||||
|
||||
public RuntimePhysicsOwnershipSnapshot CaptureOwnership()
|
||||
{
|
||||
RuntimeSetPositionOwnershipSnapshot setPosition =
|
||||
|
|
@ -1315,6 +1362,7 @@ public sealed class RuntimePhysicsState : IDisposable
|
|||
_collisionAdmissions.Clear();
|
||||
SetPosition.ResetSession();
|
||||
CollisionReports.ResetSession();
|
||||
_worldFrameCenterLandblockId = 0u;
|
||||
AdvanceCollisionWorldAuthority();
|
||||
Volatile.Write(ref _collisionMutationThreadId, 0);
|
||||
}
|
||||
|
|
@ -2007,6 +2055,7 @@ public sealed class RuntimePhysicsState : IDisposable
|
|||
_collisionPrefixMutations.Clear();
|
||||
_collisionAdmissions.Clear();
|
||||
_collisionGenerations.Clear();
|
||||
_worldFrameCenterLandblockId = 0u;
|
||||
CellCommitted = null;
|
||||
_collisionGenerationCommittedObservers.Clear();
|
||||
_disposed = true;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue