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
|
|
@ -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