fix(interaction): restore retail loot placement and world-drop projection
This commit is contained in:
parent
4d095be286
commit
921712f412
24 changed files with 1581 additions and 34 deletions
|
|
@ -27,7 +27,7 @@ internal readonly record struct AcceptedStateNetworkUpdate(
|
|||
|
||||
internal readonly record struct AcceptedPositionNetworkUpdate(
|
||||
WorldSession.EntityPositionUpdate Update,
|
||||
LiveEntityRecord Record,
|
||||
RuntimeEntityRecord Canonical,
|
||||
WorldSession.EntitySpawn Spawn,
|
||||
AcceptedPhysicsTimestamps Timestamps,
|
||||
PositionTimestampDisposition TimestampDisposition,
|
||||
|
|
@ -173,22 +173,22 @@ internal sealed class LiveEntityInboundAuthorityGate
|
|||
return false;
|
||||
}
|
||||
|
||||
LiveEntityRecord? record = null;
|
||||
RuntimeEntityRecord? canonical = null;
|
||||
ulong positionAuthorityVersion = 0;
|
||||
ulong velocityAuthorityVersion = 0;
|
||||
if (disposition is not PositionTimestampDisposition.Rejected)
|
||||
{
|
||||
if (!_liveEntities.TryGetRecord(update.Guid, out record))
|
||||
if (!_liveEntities.TryGetCanonical(update.Guid, out canonical))
|
||||
return false;
|
||||
positionAuthorityVersion = record.PositionAuthorityVersion;
|
||||
velocityAuthorityVersion = record.VelocityAuthorityVersion;
|
||||
positionAuthorityVersion = canonical.PositionAuthorityVersion;
|
||||
velocityAuthorityVersion = canonical.VelocityAuthorityVersion;
|
||||
}
|
||||
|
||||
_publishTimestamps(update.Guid, timestamps);
|
||||
if (disposition is PositionTimestampDisposition.Rejected)
|
||||
return false;
|
||||
if (!_liveEntities.IsCurrentPositionAuthority(
|
||||
record!,
|
||||
canonical!,
|
||||
positionAuthorityVersion))
|
||||
{
|
||||
return false;
|
||||
|
|
@ -196,7 +196,7 @@ internal sealed class LiveEntityInboundAuthorityGate
|
|||
|
||||
accepted = new AcceptedPositionNetworkUpdate(
|
||||
update,
|
||||
record!,
|
||||
canonical!,
|
||||
spawn,
|
||||
timestamps,
|
||||
disposition,
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ internal sealed class LiveEntityNetworkUpdateController
|
|||
private readonly ILiveWorldSessionSource _session;
|
||||
private readonly LiveEntityInboundAuthorityGate _authorityGate;
|
||||
private readonly IMovementTruthDiagnosticSink _movementTruthDiagnostics;
|
||||
private readonly InventoryWorldDropProjectionController?
|
||||
_worldDropProjection;
|
||||
|
||||
private PlayerMovementController? _playerController => _playerControllerSource.Controller;
|
||||
private EntityPhysicsHost? _playerHost => _playerHostSource.Host;
|
||||
|
|
@ -100,7 +102,8 @@ internal sealed class LiveEntityNetworkUpdateController
|
|||
IPhysicsScriptTimeSource gameTime,
|
||||
ILiveWorldSessionSource session,
|
||||
Action<uint, AcceptedPhysicsTimestamps> publishTimestamps,
|
||||
IMovementTruthDiagnosticSink movementTruthDiagnostics)
|
||||
IMovementTruthDiagnosticSink movementTruthDiagnostics,
|
||||
InventoryWorldDropProjectionController? worldDropProjection = null)
|
||||
{
|
||||
_liveEntities = liveEntities ?? throw new ArgumentNullException(nameof(liveEntities));
|
||||
_objects = objects ?? throw new ArgumentNullException(nameof(objects));
|
||||
|
|
@ -135,6 +138,7 @@ internal sealed class LiveEntityNetworkUpdateController
|
|||
publishTimestamps);
|
||||
_movementTruthDiagnostics = movementTruthDiagnostics
|
||||
?? throw new ArgumentNullException(nameof(movementTruthDiagnostics));
|
||||
_worldDropProjection = worldDropProjection;
|
||||
}
|
||||
|
||||
internal void ResetSessionState() => _authorityGate.ResetSessionState();
|
||||
|
|
@ -936,6 +940,11 @@ internal sealed class LiveEntityNetworkUpdateController
|
|||
|
||||
public void OnPosition(AcDream.Core.Net.WorldSession.EntityPositionUpdate update)
|
||||
{
|
||||
if (_worldDropProjection?.TryRecoverUnknownPosition(update) == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool payloadIsValid = _projectileController?.CanAcceptPositionPayload(
|
||||
update.Guid,
|
||||
update.Position,
|
||||
|
|
@ -957,11 +966,22 @@ internal sealed class LiveEntityNetworkUpdateController
|
|||
var timestampDisposition = accepted.TimestampDisposition;
|
||||
var acceptedSpawn = accepted.Spawn;
|
||||
var timestamps = accepted.Timestamps;
|
||||
LiveEntityRecord acceptedPositionRecord = accepted.Record;
|
||||
RuntimeEntityRecord acceptedPositionCanonical = accepted.Canonical;
|
||||
ulong acceptedPositionAuthorityVersion =
|
||||
accepted.PositionAuthorityVersion;
|
||||
ulong acceptedPositionVelocityAuthorityVersion =
|
||||
accepted.VelocityAuthorityVersion;
|
||||
if (!_liveEntities.TryGetProjection(
|
||||
acceptedPositionCanonical,
|
||||
out LiveEntityRecord acceptedPositionRecord)
|
||||
&& !_liveEntityHydration.RecoverCanonicalProjection(
|
||||
acceptedPositionCanonical,
|
||||
acceptedPositionAuthorityVersion,
|
||||
out acceptedPositionRecord))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool IsCurrentPositionOwner(
|
||||
AcDream.Core.World.WorldEntity? expectedEntity = null) =>
|
||||
_liveEntities.IsCurrentPositionAuthority(
|
||||
|
|
@ -1013,7 +1033,12 @@ internal sealed class LiveEntityNetworkUpdateController
|
|||
_playerController)))
|
||||
return;
|
||||
|
||||
if (!_liveEntities.ContainsWorldEntity(update.Guid))
|
||||
// A leave-world transition deliberately retains WorldEntity as the
|
||||
// logical/render-resource owner while IsSpatiallyProjected is false.
|
||||
// A fresh retail Position is the re-entry edge; testing only for the
|
||||
// retained object reference leaves dropped inventory permanently
|
||||
// invisible after InventoryPutObjectIn3D.
|
||||
if (RequiresSpatialProjectionRecovery(acceptedPositionRecord))
|
||||
{
|
||||
if (!IsCurrentPositionOwner())
|
||||
return;
|
||||
|
|
@ -1771,6 +1796,13 @@ internal sealed class LiveEntityNetworkUpdateController
|
|||
}
|
||||
}
|
||||
|
||||
internal static bool RequiresSpatialProjectionRecovery(
|
||||
LiveEntityRecord record)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(record);
|
||||
return record.WorldEntity is null || !record.IsSpatiallyProjected;
|
||||
}
|
||||
|
||||
// Retail teleport transit: the 7-state TAS drives portal/view-plane presentation, holds the
|
||||
// player in PortalSpace until the destination is resident (TeleportWorldReady), then
|
||||
// fires Place (materialize) and FireLoginComplete (regain control + ack the server).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue