Restore the named-retail object update order across local, remote, static, projectile, animation, shadow, teleport, and effect lifetimes. Separate authoritative root commits from spatial rebucketing, preserve per-owner hook/FIFO ordering, and remove update-path allocations with exact lifecycle and residency gates. Add deterministic conformance, adversarial lifetime, GUID-reuse, pending-cell, quaternion, timestamp, and allocation coverage. Release build is warning-free and all 6,446 tests pass with five intentional skips; retail, architecture, and adversarial reviews are clean. Co-authored-by: OpenAI Codex <codex@openai.com>
165 lines
5.7 KiB
C#
165 lines
5.7 KiB
C#
namespace AcDream.App.World;
|
|
|
|
/// <summary>
|
|
/// Strongly typed view over animation components owned by a
|
|
/// <see cref="LiveEntityRuntime"/>. It owns only reusable traversal scratch;
|
|
/// canonical component identity remains in the runtime.
|
|
/// </summary>
|
|
public sealed class LiveEntityAnimationRuntimeView<TAnimation>
|
|
: IEnumerable<KeyValuePair<uint, TAnimation>>
|
|
where TAnimation : class, ILiveEntityAnimationRuntime
|
|
{
|
|
private readonly Func<LiveEntityRuntime?> _runtime;
|
|
private readonly List<KeyValuePair<uint, TAnimation>> _iterationSnapshot = new();
|
|
|
|
public LiveEntityAnimationRuntimeView(Func<LiveEntityRuntime?> runtime) =>
|
|
_runtime = runtime ?? throw new ArgumentNullException(nameof(runtime));
|
|
|
|
public int Count => _runtime()?.SpatialAnimationRuntimes.Count ?? 0;
|
|
public IEnumerable<uint> Keys =>
|
|
_runtime()?.SpatialAnimationRuntimes.Keys ?? Array.Empty<uint>();
|
|
|
|
public TAnimation this[uint localEntityId]
|
|
{
|
|
set
|
|
{
|
|
LiveEntityRuntime runtime = _runtime()
|
|
?? throw new InvalidOperationException("Live entity runtime is not initialized.");
|
|
if (!runtime.TryGetServerGuid(localEntityId, out uint guid))
|
|
throw new InvalidOperationException($"No live entity owns local id 0x{localEntityId:X8}.");
|
|
runtime.SetAnimationRuntime(guid, value);
|
|
}
|
|
}
|
|
|
|
public bool TryGetValue(uint localEntityId, out TAnimation animation)
|
|
{
|
|
if (_runtime()?.TryGetAnimationRuntime(localEntityId, out var found) == true
|
|
&& found is TAnimation typed)
|
|
{
|
|
animation = typed;
|
|
return true;
|
|
}
|
|
|
|
animation = null!;
|
|
return false;
|
|
}
|
|
|
|
public bool Remove(uint localEntityId)
|
|
{
|
|
LiveEntityRuntime? runtime = _runtime();
|
|
return runtime is not null
|
|
&& runtime.TryGetServerGuid(localEntityId, out uint guid)
|
|
&& runtime.ClearAnimationRuntime(guid);
|
|
}
|
|
|
|
/// <summary>Copies only currently resident local IDs without boxing a
|
|
/// dictionary-key enumerator.</summary>
|
|
public void CopySpatialIdsTo(HashSet<uint> destination)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(destination);
|
|
LiveEntityRuntime? runtime = _runtime();
|
|
if (runtime is null)
|
|
destination.Clear();
|
|
else
|
|
runtime.CopySpatialAnimationLocalIdsTo(destination);
|
|
}
|
|
|
|
public Enumerator GetEnumerator()
|
|
{
|
|
LiveEntityRuntime? runtime = _runtime();
|
|
if (runtime is null)
|
|
_iterationSnapshot.Clear();
|
|
else
|
|
runtime.CopySpatialAnimationRuntimesTo(_iterationSnapshot);
|
|
|
|
return new Enumerator(runtime, _iterationSnapshot);
|
|
}
|
|
|
|
IEnumerator<KeyValuePair<uint, TAnimation>> IEnumerable<KeyValuePair<uint, TAnimation>>.GetEnumerator() =>
|
|
GetEnumerator();
|
|
|
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
|
|
|
|
/// <summary>
|
|
/// Struct enumerator over a reusable frame snapshot. A runtime can rebucket
|
|
/// or delete another entity from inside animation/physics callbacks without
|
|
/// invalidating this traversal. Each entry is identity-checked immediately
|
|
/// before it is yielded, so GUID reuse cannot advance the displaced owner.
|
|
/// </summary>
|
|
public struct Enumerator : IEnumerator<KeyValuePair<uint, TAnimation>>
|
|
{
|
|
private readonly LiveEntityRuntime? _runtime;
|
|
private readonly List<KeyValuePair<uint, TAnimation>> _snapshot;
|
|
private int _index;
|
|
|
|
internal Enumerator(
|
|
LiveEntityRuntime? runtime,
|
|
List<KeyValuePair<uint, TAnimation>> snapshot)
|
|
{
|
|
_runtime = runtime;
|
|
_snapshot = snapshot;
|
|
_index = -1;
|
|
Current = default;
|
|
}
|
|
|
|
public KeyValuePair<uint, TAnimation> Current { get; private set; }
|
|
object System.Collections.IEnumerator.Current => Current;
|
|
|
|
public bool MoveNext()
|
|
{
|
|
while (++_index < _snapshot.Count)
|
|
{
|
|
KeyValuePair<uint, TAnimation> pair = _snapshot[_index];
|
|
if (_runtime?.SpatialAnimationRuntimes.TryGetValue(
|
|
pair.Key,
|
|
out ILiveEntityAnimationRuntime? indexed) == true
|
|
&& ReferenceEquals(indexed, pair.Value))
|
|
{
|
|
Current = pair;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Current = default;
|
|
return false;
|
|
}
|
|
|
|
public void Dispose() { }
|
|
|
|
void System.Collections.IEnumerator.Reset() =>
|
|
throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
/// <summary>Storage-free typed view over remote-motion components.</summary>
|
|
public sealed class LiveEntityRemoteMotionRuntimeView<TRemoteMotion>
|
|
where TRemoteMotion : class, ILiveEntityRemoteMotionRuntime
|
|
{
|
|
private readonly Func<LiveEntityRuntime?> _runtime;
|
|
|
|
public LiveEntityRemoteMotionRuntimeView(Func<LiveEntityRuntime?> runtime) =>
|
|
_runtime = runtime ?? throw new ArgumentNullException(nameof(runtime));
|
|
|
|
public TRemoteMotion this[uint serverGuid]
|
|
{
|
|
set => (_runtime()
|
|
?? throw new InvalidOperationException("Live entity runtime is not initialized."))
|
|
.SetRemoteMotionRuntime(serverGuid, value);
|
|
}
|
|
|
|
public bool TryGetValue(uint serverGuid, out TRemoteMotion motion)
|
|
{
|
|
if (_runtime()?.TryGetRemoteMotionRuntime(serverGuid, out var found) == true
|
|
&& found is TRemoteMotion typed)
|
|
{
|
|
motion = typed;
|
|
return true;
|
|
}
|
|
|
|
motion = null!;
|
|
return false;
|
|
}
|
|
|
|
public bool Remove(uint serverGuid) =>
|
|
_runtime()?.ClearRemoteMotionRuntime(serverGuid) == true;
|
|
}
|