feat(runtime): own deferred set-position residence

This commit is contained in:
Erik 2026-07-31 22:32:49 +02:00
parent e84a388e6f
commit 4c02ac4259
18 changed files with 5557 additions and 34 deletions

View file

@ -16,6 +16,7 @@ public sealed class ParentAttachmentState
private readonly Dictionary<uint, ParentAttachmentRelation> _stagedByChild = new();
private readonly Dictionary<uint, ParentAttachmentRelation> _recoveryByChild = new();
private readonly Dictionary<uint, ParentAttachmentRelation> _lastAcceptedByChild = new();
private readonly Dictionary<ParentIncarnation, List<uint>> _committedChildrenByParent = new();
public int UnresolvedRelationCount =>
_unresolvedByChild.Values.Sum(queue => queue.Count);
@ -148,6 +149,9 @@ public sealed class ParentAttachmentState
out ParentAttachmentRelation committed)
&& committed == relation;
public bool HasCommittedParent(uint childGuid) =>
_lastAcceptedByChild.ContainsKey(childGuid);
public bool IsPending(
ParentAttachmentRelation relation,
ParentProjectionCandidateKind kind) =>
@ -184,7 +188,20 @@ public sealed class ParentAttachmentState
{
return false;
}
RemoveCommittedChild(relation.ChildGuid);
_lastAcceptedByChild[relation.ChildGuid] = relation;
var parent = new ParentIncarnation(
relation.ParentGuid,
relation.ParentInstanceSequence);
if (!_committedChildrenByParent.TryGetValue(
parent,
out List<uint>? children))
{
children = [];
_committedChildrenByParent.Add(parent, children);
}
children.Add(relation.ChildGuid);
_stagedByChild.Remove(relation.ChildGuid);
_recoveryByChild[relation.ChildGuid] = relation;
return true;
@ -233,16 +250,34 @@ public sealed class ParentAttachmentState
return result.ToArray();
}
/// <summary>
/// Returns only the exact direct children currently committed to one
/// parent incarnation. Lost-cell destruction follows retail's live
/// CHILDLIST and must not capture staged, unresolved, or future-generation
/// relations that merely reuse the same parent GUID.
/// </summary>
public IReadOnlyList<uint> ChildrenAttachedToParent(
uint parentGuid,
ushort parentInstanceSequence)
{
var parent = new ParentIncarnation(parentGuid, parentInstanceSequence);
return _committedChildrenByParent.TryGetValue(
parent,
out List<uint>? children)
? children
: Array.Empty<uint>();
}
public void RemoveObject(uint guid)
{
_stagedByChild.Remove(guid);
_recoveryByChild.Remove(guid);
_lastAcceptedByChild.Remove(guid);
RemoveCommittedChild(guid);
_unresolvedByChild.Remove(guid);
RemoveParentReferences(_stagedByChild, guid);
RemoveParentReferences(_recoveryByChild, guid);
RemoveParentReferences(_lastAcceptedByChild, guid);
RemoveCommittedParentReferences(guid);
uint[] children = _unresolvedByChild.Keys.ToArray();
for (int i = 0; i < children.Length; i++)
@ -268,10 +303,10 @@ public sealed class ParentAttachmentState
relation => relation.WaitOwner is ParentAttachmentWaitOwner.Parent);
_stagedByChild.Remove(guid);
_recoveryByChild.Remove(guid);
_lastAcceptedByChild.Remove(guid);
RemoveCommittedChild(guid);
RemoveParentReferences(_stagedByChild, guid);
RemoveParentReferences(_recoveryByChild, guid);
RemoveParentReferences(_lastAcceptedByChild, guid);
RemoveCommittedParentReferences(guid);
FilterParentCandidates(
guid,
relation => relation.ParentInstanceSequence == replacementGeneration
@ -292,10 +327,10 @@ public sealed class ParentAttachmentState
relation => relation.WaitOwner is ParentAttachmentWaitOwner.Parent);
_stagedByChild.Remove(guid);
_recoveryByChild.Remove(guid);
_lastAcceptedByChild.Remove(guid);
RemoveCommittedChild(guid);
RemoveParentReferences(_stagedByChild, guid);
RemoveParentReferences(_recoveryByChild, guid);
RemoveParentReferences(_lastAcceptedByChild, guid);
RemoveCommittedParentReferences(guid);
FilterParentCandidates(
guid,
relation => PhysicsTimestampGate.IsNewer(
@ -311,14 +346,14 @@ public sealed class ParentAttachmentState
{
_stagedByChild.Remove(childGuid);
_recoveryByChild.Remove(childGuid);
_lastAcceptedByChild.Remove(childGuid);
RemoveCommittedChild(childGuid);
}
public void RemoveChild(uint childGuid)
{
_stagedByChild.Remove(childGuid);
_recoveryByChild.Remove(childGuid);
_lastAcceptedByChild.Remove(childGuid);
RemoveCommittedChild(childGuid);
_unresolvedByChild.Remove(childGuid);
}
@ -328,6 +363,50 @@ public sealed class ParentAttachmentState
_stagedByChild.Clear();
_recoveryByChild.Clear();
_lastAcceptedByChild.Clear();
foreach (List<uint> children in _committedChildrenByParent.Values)
children.Clear();
_committedChildrenByParent.Clear();
}
private void RemoveCommittedChild(uint childGuid)
{
if (!_lastAcceptedByChild.Remove(
childGuid,
out ParentAttachmentRelation relation))
{
return;
}
var parent = new ParentIncarnation(
relation.ParentGuid,
relation.ParentInstanceSequence);
if (!_committedChildrenByParent.TryGetValue(
parent,
out List<uint>? children))
{
return;
}
int childIndex = children.IndexOf(childGuid);
if (childIndex >= 0)
{
int lastIndex = children.Count - 1;
children[childIndex] = children[lastIndex];
children.RemoveAt(lastIndex);
}
if (children.Count == 0)
_committedChildrenByParent.Remove(parent);
}
private void RemoveCommittedParentReferences(uint parentGuid)
{
uint[] children = _lastAcceptedByChild
.Where(pair => pair.Value.ParentGuid == parentGuid)
.Select(pair => pair.Key)
.ToArray();
for (int i = 0; i < children.Length; i++)
RemoveCommittedChild(children[i]);
}
private static void RemoveParentReferences(
@ -377,6 +456,10 @@ public sealed class ParentAttachmentState
else
_unresolvedByChild[childGuid] = retained;
}
private readonly record struct ParentIncarnation(
uint ServerGuid,
ushort InstanceSequence);
}
public readonly record struct ParentAttachmentRelation(