refactor(world): extract live projection mechanics
Move appearance rebinding, collision construction, default-pose resolution, local shadow ownership, and exact leave-world presentation into focused owners. Preserve retail parent ordering with staged validation, committed recovery, recursive attached-subtree withdrawal, and retryable exact teardown across parent, pickup, position, unwield, and pose-loss edges. Co-Authored-By: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
c87bac31a0
commit
69a2ca0c6d
26 changed files with 4172 additions and 430 deletions
|
|
@ -136,12 +136,7 @@ public sealed class InboundPhysicsStateController
|
|||
return false;
|
||||
}
|
||||
|
||||
accepted = ApplyParent(
|
||||
child,
|
||||
update.ParentGuid,
|
||||
update.ParentLocation,
|
||||
update.PlacementId,
|
||||
update.ChildPositionSequence);
|
||||
accepted = ApplyPositionTimestampOnly(child, update.ChildPositionSequence);
|
||||
_snapshots[update.ChildGuid] = accepted;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -160,13 +155,34 @@ public sealed class InboundPhysicsStateController
|
|||
return false;
|
||||
}
|
||||
|
||||
accepted = ApplyPositionTimestampOnly(child, update.ChildPositionSequence);
|
||||
_snapshots[update.ChildGuid] = accepted;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryCommitParent(
|
||||
uint childGuid,
|
||||
uint parentGuid,
|
||||
uint parentLocation,
|
||||
uint placementId,
|
||||
ushort positionSequence,
|
||||
out WorldSession.EntitySpawn accepted)
|
||||
{
|
||||
if (!TryGet(childGuid, out PhysicsTimestampGate? gate, out WorldSession.EntitySpawn child)
|
||||
|| gate.PositionTimestamp != positionSequence
|
||||
|| child.PositionSequence != positionSequence)
|
||||
{
|
||||
accepted = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
accepted = ApplyParent(
|
||||
child,
|
||||
update.ParentGuid,
|
||||
update.ParentLocation,
|
||||
update.PlacementId,
|
||||
update.ChildPositionSequence);
|
||||
_snapshots[update.ChildGuid] = accepted;
|
||||
parentGuid,
|
||||
parentLocation,
|
||||
placementId,
|
||||
positionSequence);
|
||||
_snapshots[childGuid] = accepted;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -595,6 +611,25 @@ public sealed class InboundPhysicsStateController
|
|||
};
|
||||
}
|
||||
|
||||
private static WorldSession.EntitySpawn ApplyPositionTimestampOnly(
|
||||
WorldSession.EntitySpawn old,
|
||||
ushort positionSequence)
|
||||
{
|
||||
PhysicsSpawnData? physics = old.Physics;
|
||||
if (physics is { } desc)
|
||||
{
|
||||
physics = desc with
|
||||
{
|
||||
Timestamps = desc.Timestamps with { Position = positionSequence },
|
||||
};
|
||||
}
|
||||
return old with
|
||||
{
|
||||
PositionSequence = positionSequence,
|
||||
Physics = physics,
|
||||
};
|
||||
}
|
||||
|
||||
private static WorldSession.EntitySpawn ApplyParent(
|
||||
WorldSession.EntitySpawn old,
|
||||
uint parentGuid,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,175 @@
|
|||
using AcDream.App.Physics;
|
||||
using AcDream.App.Rendering.Vfx;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.Plugins;
|
||||
|
||||
namespace AcDream.App.World;
|
||||
|
||||
/// <summary>
|
||||
/// Owns retail <c>CPhysicsObj::leave_world</c>'s App projection tail for a
|
||||
/// still-live exact record. Component projection is removed before canonical
|
||||
/// runtime withdrawal, matching the shipped failure/retry boundary.
|
||||
/// </summary>
|
||||
internal sealed class LiveEntityProjectionWithdrawalController
|
||||
{
|
||||
private readonly LiveEntityRuntime _runtime;
|
||||
private readonly ProjectileController _projectiles;
|
||||
private readonly WorldGameState _worldState;
|
||||
private readonly WorldEvents _worldEvents;
|
||||
private readonly ShadowObjectRegistry _shadows;
|
||||
private readonly EntityEffectPoseRegistry _effectPoses;
|
||||
private readonly LocalPlayerShadowState _localPlayerShadow;
|
||||
|
||||
public LiveEntityProjectionWithdrawalController(
|
||||
LiveEntityRuntime runtime,
|
||||
ProjectileController projectiles,
|
||||
WorldGameState worldState,
|
||||
WorldEvents worldEvents,
|
||||
ShadowObjectRegistry shadows,
|
||||
EntityEffectPoseRegistry effectPoses,
|
||||
LocalPlayerShadowState localPlayerShadow)
|
||||
{
|
||||
_runtime = runtime ?? throw new ArgumentNullException(nameof(runtime));
|
||||
_projectiles = projectiles ?? throw new ArgumentNullException(nameof(projectiles));
|
||||
_worldState = worldState ?? throw new ArgumentNullException(nameof(worldState));
|
||||
_worldEvents = worldEvents ?? throw new ArgumentNullException(nameof(worldEvents));
|
||||
_shadows = shadows ?? throw new ArgumentNullException(nameof(shadows));
|
||||
_effectPoses = effectPoses ?? throw new ArgumentNullException(nameof(effectPoses));
|
||||
_localPlayerShadow = localPlayerShadow
|
||||
?? throw new ArgumentNullException(nameof(localPlayerShadow));
|
||||
}
|
||||
|
||||
public bool Withdraw(uint serverGuid, uint localPlayerGuid)
|
||||
{
|
||||
if (!_runtime.TryGetRecord(serverGuid, out LiveEntityRecord record)
|
||||
|| record.WorldEntity is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Withdraw(record, localPlayerGuid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Withdraws only the captured incarnation and projection operation. A
|
||||
/// leave-world observer may accept a fresh same-generation Position and
|
||||
/// reproject this record; that newer projection must survive the displaced
|
||||
/// outer operation just as a newer INSTANCE_TS must.
|
||||
/// </summary>
|
||||
public bool Withdraw(LiveEntityRecord record, uint localPlayerGuid)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(record);
|
||||
if (!_runtime.IsCurrentRecord(record) || record.WorldEntity is null)
|
||||
return false;
|
||||
|
||||
return Withdraw(
|
||||
record,
|
||||
record.PositionAuthorityVersion,
|
||||
record.ProjectionMutationVersion,
|
||||
localPlayerGuid);
|
||||
}
|
||||
|
||||
public bool Withdraw(
|
||||
LiveEntityRecord record,
|
||||
ulong positionAuthorityVersion,
|
||||
ulong projectionMutationVersion,
|
||||
uint localPlayerGuid)
|
||||
{
|
||||
ExactProjectionWithdrawalOutcome outcome = WithdrawExact(
|
||||
record,
|
||||
positionAuthorityVersion,
|
||||
projectionMutationVersion,
|
||||
localPlayerGuid);
|
||||
if (outcome.Failure is not null)
|
||||
throw outcome.Failure;
|
||||
return outcome.Disposition is ExactProjectionWithdrawalDisposition.Completed;
|
||||
}
|
||||
|
||||
public ExactProjectionWithdrawalOutcome WithdrawExact(
|
||||
LiveEntityRecord record,
|
||||
ulong positionAuthorityVersion,
|
||||
ulong projectionMutationVersion,
|
||||
uint localPlayerGuid)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(record);
|
||||
if (!_runtime.IsCurrentRecord(record)
|
||||
|| record.WorldEntity is null
|
||||
|| record.PositionAuthorityVersion != positionAuthorityVersion
|
||||
|| record.ProjectionMutationVersion != projectionMutationVersion)
|
||||
{
|
||||
return new(
|
||||
ExactProjectionWithdrawalDisposition.Superseded,
|
||||
Failure: null);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
LeaveWorld(record, localPlayerGuid);
|
||||
bool completed = _runtime.WithdrawLiveEntityProjection(
|
||||
record,
|
||||
positionAuthorityVersion,
|
||||
projectionMutationVersion);
|
||||
return new(
|
||||
completed
|
||||
? ExactProjectionWithdrawalDisposition.Completed
|
||||
: ExactProjectionWithdrawalDisposition.Superseded,
|
||||
Failure: null);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
bool current = _runtime.IsCurrentRecord(record);
|
||||
ExactProjectionWithdrawalDisposition disposition =
|
||||
current
|
||||
&& record.PositionAuthorityVersion == positionAuthorityVersion
|
||||
&& record.ProjectionMutationVersion == projectionMutationVersion
|
||||
&& record.IsSpatiallyProjected
|
||||
? ExactProjectionWithdrawalDisposition.Pending
|
||||
: current
|
||||
&& record.PositionAuthorityVersion == positionAuthorityVersion
|
||||
&& !record.IsSpatiallyProjected
|
||||
? ExactProjectionWithdrawalDisposition.Completed
|
||||
: ExactProjectionWithdrawalDisposition.Superseded;
|
||||
return new(disposition, error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes only the shorter world-facing component projection. This exact
|
||||
/// overload is also used by logical teardown after the active GUID table
|
||||
/// may already expose a replacement incarnation.
|
||||
/// </summary>
|
||||
public void LeaveWorld(LiveEntityRecord record, uint localPlayerGuid)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(record);
|
||||
if (record.WorldEntity is not { } entity)
|
||||
return;
|
||||
|
||||
bool retainedProjectileShadow = _projectiles.LeaveWorld(record);
|
||||
if (!retainedProjectileShadow)
|
||||
_shadows.Suspend(entity.Id);
|
||||
_worldState.RemoveById(entity.Id);
|
||||
_worldEvents.ForgetEntity(entity.Id);
|
||||
if (record.ServerGuid == localPlayerGuid
|
||||
&& (!_runtime.TryGetRecord(record.ServerGuid, out LiveEntityRecord current)
|
||||
|| ReferenceEquals(current, record)))
|
||||
{
|
||||
_localPlayerShadow.Clear();
|
||||
}
|
||||
|
||||
// Pose-attached effects keep logical ownership while cell-less. A
|
||||
// missing pose suppresses stale anchors; re-entry republishes the same
|
||||
// WorldEntity frames without replaying create-time resources.
|
||||
_effectPoses.Remove(entity.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ExactProjectionWithdrawalDisposition
|
||||
{
|
||||
Completed,
|
||||
Pending,
|
||||
Superseded,
|
||||
}
|
||||
|
||||
public readonly record struct ExactProjectionWithdrawalOutcome(
|
||||
ExactProjectionWithdrawalDisposition Disposition,
|
||||
Exception? Failure);
|
||||
|
|
@ -931,6 +931,38 @@ public sealed class LiveEntityRuntime
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exact-incarnation withdrawal used after App leave-world callbacks. A
|
||||
/// callback may accept a newer INSTANCE_TS for the same GUID; that newer
|
||||
/// projection must never be withdrawn by the displaced operation.
|
||||
/// </summary>
|
||||
public bool WithdrawLiveEntityProjection(LiveEntityRecord expectedRecord)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(expectedRecord);
|
||||
return WithdrawLiveEntityProjection(
|
||||
expectedRecord,
|
||||
expectedRecord.PositionAuthorityVersion,
|
||||
expectedRecord.ProjectionMutationVersion);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exact-operation withdrawal used across arbitrary App callbacks. Both
|
||||
/// the accepted Position authority and projection mutation token are
|
||||
/// pinned so a same-incarnation re-entry cannot be removed by an older
|
||||
/// parent/pickup/leave-world operation.
|
||||
/// </summary>
|
||||
internal bool WithdrawLiveEntityProjection(
|
||||
LiveEntityRecord expectedRecord,
|
||||
ulong expectedPositionAuthorityVersion,
|
||||
ulong expectedProjectionMutationVersion)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(expectedRecord);
|
||||
return IsCurrentRecord(expectedRecord)
|
||||
&& expectedRecord.PositionAuthorityVersion == expectedPositionAuthorityVersion
|
||||
&& expectedRecord.ProjectionMutationVersion == expectedProjectionMutationVersion
|
||||
&& WithdrawLiveEntityProjection(expectedRecord.ServerGuid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Withdraws a still-live projection whose authoritative rollback frame
|
||||
/// is outside every world cell. Logical owners remain registered.
|
||||
|
|
@ -1515,7 +1547,6 @@ public sealed class LiveEntityRuntime
|
|||
RefreshRecord(update.ChildGuid, accepted);
|
||||
if (_recordsByGuid.TryGetValue(update.ChildGuid, out LiveEntityRecord? record))
|
||||
record.AdvancePositionAuthority();
|
||||
ClearWorldCell(update.ChildGuid);
|
||||
}
|
||||
return applied;
|
||||
}
|
||||
|
|
@ -1528,11 +1559,43 @@ public sealed class LiveEntityRuntime
|
|||
RefreshRecord(update.ChildGuid, accepted);
|
||||
if (_recordsByGuid.TryGetValue(update.ChildGuid, out LiveEntityRecord? record))
|
||||
record.AdvancePositionAuthority();
|
||||
ClearWorldCell(update.ChildGuid);
|
||||
}
|
||||
return applied;
|
||||
}
|
||||
|
||||
internal bool CommitStagedParent(
|
||||
ParentAttachmentRelation relation,
|
||||
out WorldSession.EntitySpawn accepted)
|
||||
{
|
||||
bool committed = _inbound.TryCommitParent(
|
||||
relation.ChildGuid,
|
||||
relation.ParentGuid,
|
||||
relation.ParentLocation,
|
||||
relation.PlacementId,
|
||||
relation.ChildPositionSequence,
|
||||
out accepted);
|
||||
if (committed)
|
||||
RefreshRecord(relation.ChildGuid, accepted);
|
||||
return committed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commits retail <c>CPhysicsObj::set_parent</c>'s cell-less edge only
|
||||
/// after the parent accepted the child (PartArray + holding-location
|
||||
/// validation). The POSITION_TS is consumed before this step, matching
|
||||
/// retail; invalid parent relationships retain the old world projection.
|
||||
/// </summary>
|
||||
internal bool CommitAcceptedParentCellless(
|
||||
LiveEntityRecord record,
|
||||
ulong positionAuthorityVersion)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(record);
|
||||
if (!IsCurrentPositionAuthority(record, positionAuthorityVersion))
|
||||
return false;
|
||||
ClearWorldCell(record.ServerGuid);
|
||||
return IsCurrentPositionAuthority(record, positionAuthorityVersion);
|
||||
}
|
||||
|
||||
public bool TryApplyMotion(
|
||||
WorldSession.EntityMotionUpdate update,
|
||||
bool retainPayload,
|
||||
|
|
@ -2154,7 +2217,7 @@ public sealed class LiveEntityRuntime
|
|||
RefreshSpatialRuntimeIndexes(record);
|
||||
}
|
||||
|
||||
private bool IsCurrentRecord(LiveEntityRecord record) =>
|
||||
internal bool IsCurrentRecord(LiveEntityRecord record) =>
|
||||
_recordsByGuid.TryGetValue(record.ServerGuid, out LiveEntityRecord? current)
|
||||
&& ReferenceEquals(current, record);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,19 +6,20 @@ namespace AcDream.App.World;
|
|||
/// <summary>
|
||||
/// Update-thread state machine for parent relations that may arrive before
|
||||
/// either CreateObject. Unaccepted wire events remain in arrival order;
|
||||
/// rollback history contains only relations that passed the canonical retail
|
||||
/// timestamp gate.
|
||||
/// committed rollback history contains only relations that passed both the
|
||||
/// canonical retail timestamp gate and retail <c>PartArray::add_child</c>
|
||||
/// validation.
|
||||
/// </summary>
|
||||
public sealed class ParentAttachmentState
|
||||
{
|
||||
private readonly Dictionary<uint, Queue<ParentAttachmentRelation>> _unresolvedByChild = new();
|
||||
private readonly Dictionary<uint, ParentAttachmentRelation> _projectionByChild = new();
|
||||
private readonly Dictionary<uint, ParentAttachmentRelation> _stagedByChild = new();
|
||||
private readonly Dictionary<uint, ParentAttachmentRelation> _recoveryByChild = new();
|
||||
private readonly Dictionary<uint, ParentAttachmentRelation> _lastAcceptedByChild = new();
|
||||
|
||||
public void AcceptCreateObjectRelation(ParentAttachmentRelation relation)
|
||||
{
|
||||
_projectionByChild[relation.ChildGuid] = relation;
|
||||
_lastAcceptedByChild[relation.ChildGuid] = relation;
|
||||
_stagedByChild[relation.ChildGuid] = relation;
|
||||
}
|
||||
|
||||
public void Enqueue(ParentEvent.Parsed update)
|
||||
|
|
@ -50,6 +51,8 @@ public sealed class ParentAttachmentState
|
|||
Func<uint, ushort?> resolveInstance,
|
||||
Func<ParentEvent.Parsed, bool> accept)
|
||||
{
|
||||
if (_stagedByChild.ContainsKey(childGuid))
|
||||
return;
|
||||
if (!_unresolvedByChild.TryGetValue(childGuid, out Queue<ParentAttachmentRelation>? queue))
|
||||
return;
|
||||
int candidateCount = queue.Count;
|
||||
|
|
@ -95,8 +98,11 @@ public sealed class ParentAttachmentState
|
|||
if (!accept(update))
|
||||
continue;
|
||||
|
||||
_projectionByChild[childGuid] = relation;
|
||||
_lastAcceptedByChild[childGuid] = relation;
|
||||
_stagedByChild[childGuid] = relation with
|
||||
{
|
||||
WaitOwner = ParentAttachmentWaitOwner.Unknown,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
if (queue.Count == 0)
|
||||
|
|
@ -104,22 +110,109 @@ public sealed class ParentAttachmentState
|
|||
}
|
||||
|
||||
public bool TryGetProjection(uint childGuid, out ParentAttachmentRelation relation) =>
|
||||
_projectionByChild.TryGetValue(childGuid, out relation);
|
||||
_stagedByChild.TryGetValue(childGuid, out relation)
|
||||
|| _recoveryByChild.TryGetValue(childGuid, out relation);
|
||||
|
||||
public void MarkProjected(uint childGuid) => _projectionByChild.Remove(childGuid);
|
||||
public bool TryGetStagedProjection(
|
||||
uint childGuid,
|
||||
out ParentAttachmentRelation relation) =>
|
||||
_stagedByChild.TryGetValue(childGuid, out relation);
|
||||
|
||||
public bool TryGetRecoveryProjection(
|
||||
uint childGuid,
|
||||
out ParentAttachmentRelation relation) =>
|
||||
_recoveryByChild.TryGetValue(childGuid, out relation);
|
||||
|
||||
public void CopyPendingProjectionChildrenTo(List<uint> destination)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(destination);
|
||||
destination.Clear();
|
||||
foreach (uint childGuid in _stagedByChild.Keys)
|
||||
destination.Add(childGuid);
|
||||
foreach (uint childGuid in _recoveryByChild.Keys)
|
||||
{
|
||||
if (!_stagedByChild.ContainsKey(childGuid))
|
||||
destination.Add(childGuid);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCommitted(ParentAttachmentRelation relation) =>
|
||||
_lastAcceptedByChild.TryGetValue(
|
||||
relation.ChildGuid,
|
||||
out ParentAttachmentRelation committed)
|
||||
&& committed == relation;
|
||||
|
||||
public bool IsPending(
|
||||
ParentAttachmentRelation relation,
|
||||
ParentProjectionCandidateKind kind) =>
|
||||
(kind is ParentProjectionCandidateKind.Staged
|
||||
? _stagedByChild
|
||||
: _recoveryByChild).TryGetValue(
|
||||
relation.ChildGuid,
|
||||
out ParentAttachmentRelation pending)
|
||||
&& pending == relation;
|
||||
|
||||
public void MarkProjected(
|
||||
ParentAttachmentRelation relation,
|
||||
ParentProjectionCandidateKind kind)
|
||||
{
|
||||
Dictionary<uint, ParentAttachmentRelation> source =
|
||||
kind is ParentProjectionCandidateKind.Staged
|
||||
? _stagedByChild
|
||||
: _recoveryByChild;
|
||||
if (source.TryGetValue(
|
||||
relation.ChildGuid,
|
||||
out ParentAttachmentRelation pending)
|
||||
&& pending == relation)
|
||||
{
|
||||
source.Remove(relation.ChildGuid);
|
||||
}
|
||||
}
|
||||
|
||||
public bool CommitProjection(ParentAttachmentRelation relation)
|
||||
{
|
||||
if (!_stagedByChild.TryGetValue(
|
||||
relation.ChildGuid,
|
||||
out ParentAttachmentRelation staged)
|
||||
|| staged != relation)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_lastAcceptedByChild[relation.ChildGuid] = relation;
|
||||
_stagedByChild.Remove(relation.ChildGuid);
|
||||
_recoveryByChild[relation.ChildGuid] = relation;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void RejectProjection(ParentAttachmentRelation relation)
|
||||
{
|
||||
if (_stagedByChild.TryGetValue(
|
||||
relation.ChildGuid,
|
||||
out ParentAttachmentRelation staged)
|
||||
&& staged == relation)
|
||||
{
|
||||
_stagedByChild.Remove(relation.ChildGuid);
|
||||
}
|
||||
}
|
||||
|
||||
public bool RestoreLastAccepted(uint childGuid)
|
||||
{
|
||||
if (!_lastAcceptedByChild.TryGetValue(childGuid, out ParentAttachmentRelation relation))
|
||||
return false;
|
||||
_projectionByChild[childGuid] = relation;
|
||||
_recoveryByChild[childGuid] = relation;
|
||||
return true;
|
||||
}
|
||||
|
||||
public IReadOnlyList<uint> ChildrenWaitingForParent(uint parentGuid)
|
||||
{
|
||||
var result = new HashSet<uint>();
|
||||
foreach ((uint childGuid, ParentAttachmentRelation relation) in _projectionByChild)
|
||||
foreach ((uint childGuid, ParentAttachmentRelation relation) in _stagedByChild)
|
||||
{
|
||||
if (relation.ParentGuid == parentGuid)
|
||||
result.Add(childGuid);
|
||||
}
|
||||
|
||||
foreach ((uint childGuid, ParentAttachmentRelation relation) in _recoveryByChild)
|
||||
{
|
||||
if (relation.ParentGuid == parentGuid)
|
||||
result.Add(childGuid);
|
||||
|
|
@ -136,11 +229,13 @@ public sealed class ParentAttachmentState
|
|||
|
||||
public void RemoveObject(uint guid)
|
||||
{
|
||||
_projectionByChild.Remove(guid);
|
||||
_stagedByChild.Remove(guid);
|
||||
_recoveryByChild.Remove(guid);
|
||||
_lastAcceptedByChild.Remove(guid);
|
||||
_unresolvedByChild.Remove(guid);
|
||||
|
||||
RemoveParentReferences(_projectionByChild, guid);
|
||||
RemoveParentReferences(_stagedByChild, guid);
|
||||
RemoveParentReferences(_recoveryByChild, guid);
|
||||
RemoveParentReferences(_lastAcceptedByChild, guid);
|
||||
|
||||
uint[] children = _unresolvedByChild.Keys.ToArray();
|
||||
|
|
@ -165,9 +260,11 @@ public sealed class ParentAttachmentState
|
|||
FilterChildCandidates(
|
||||
guid,
|
||||
relation => relation.WaitOwner is ParentAttachmentWaitOwner.Parent);
|
||||
_projectionByChild.Remove(guid);
|
||||
_stagedByChild.Remove(guid);
|
||||
_recoveryByChild.Remove(guid);
|
||||
_lastAcceptedByChild.Remove(guid);
|
||||
RemoveParentReferences(_projectionByChild, guid);
|
||||
RemoveParentReferences(_stagedByChild, guid);
|
||||
RemoveParentReferences(_recoveryByChild, guid);
|
||||
RemoveParentReferences(_lastAcceptedByChild, guid);
|
||||
FilterParentCandidates(
|
||||
guid,
|
||||
|
|
@ -187,9 +284,11 @@ public sealed class ParentAttachmentState
|
|||
FilterChildCandidates(
|
||||
guid,
|
||||
relation => relation.WaitOwner is ParentAttachmentWaitOwner.Parent);
|
||||
_projectionByChild.Remove(guid);
|
||||
_stagedByChild.Remove(guid);
|
||||
_recoveryByChild.Remove(guid);
|
||||
_lastAcceptedByChild.Remove(guid);
|
||||
RemoveParentReferences(_projectionByChild, guid);
|
||||
RemoveParentReferences(_stagedByChild, guid);
|
||||
RemoveParentReferences(_recoveryByChild, guid);
|
||||
RemoveParentReferences(_lastAcceptedByChild, guid);
|
||||
FilterParentCandidates(
|
||||
guid,
|
||||
|
|
@ -204,13 +303,15 @@ public sealed class ParentAttachmentState
|
|||
/// </summary>
|
||||
public void EndChildProjection(uint childGuid)
|
||||
{
|
||||
_projectionByChild.Remove(childGuid);
|
||||
_stagedByChild.Remove(childGuid);
|
||||
_recoveryByChild.Remove(childGuid);
|
||||
_lastAcceptedByChild.Remove(childGuid);
|
||||
}
|
||||
|
||||
public void RemoveChild(uint childGuid)
|
||||
{
|
||||
_projectionByChild.Remove(childGuid);
|
||||
_stagedByChild.Remove(childGuid);
|
||||
_recoveryByChild.Remove(childGuid);
|
||||
_lastAcceptedByChild.Remove(childGuid);
|
||||
_unresolvedByChild.Remove(childGuid);
|
||||
}
|
||||
|
|
@ -218,7 +319,8 @@ public sealed class ParentAttachmentState
|
|||
public void Clear()
|
||||
{
|
||||
_unresolvedByChild.Clear();
|
||||
_projectionByChild.Clear();
|
||||
_stagedByChild.Clear();
|
||||
_recoveryByChild.Clear();
|
||||
_lastAcceptedByChild.Clear();
|
||||
}
|
||||
|
||||
|
|
@ -286,3 +388,9 @@ public enum ParentAttachmentWaitOwner
|
|||
Parent,
|
||||
Child,
|
||||
}
|
||||
|
||||
public enum ParentProjectionCandidateKind
|
||||
{
|
||||
Staged,
|
||||
Recovery,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue