namespace AcDream.App.World; /// /// Strongly typed view over animation components owned by a /// . It owns only reusable traversal scratch; /// canonical component identity remains in the runtime. /// public sealed class LiveEntityAnimationRuntimeView : IEnumerable> where TAnimation : class, ILiveEntityAnimationRuntime { private readonly Func _runtime; private readonly List> _iterationSnapshot = new(); public LiveEntityAnimationRuntimeView(Func runtime) => _runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); public int Count => _runtime()?.SpatialAnimationRuntimes.Count ?? 0; public IEnumerable Keys => _runtime()?.SpatialAnimationRuntimes.Keys ?? Array.Empty(); 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); } /// Copies only currently resident local IDs without boxing a /// dictionary-key enumerator. public void CopySpatialIdsTo(HashSet 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> IEnumerable>.GetEnumerator() => GetEnumerator(); System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); /// /// 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. /// public struct Enumerator : IEnumerator> { private readonly LiveEntityRuntime? _runtime; private readonly List> _snapshot; private int _index; internal Enumerator( LiveEntityRuntime? runtime, List> snapshot) { _runtime = runtime; _snapshot = snapshot; _index = -1; Current = default; } public KeyValuePair Current { get; private set; } object System.Collections.IEnumerator.Current => Current; public bool MoveNext() { while (++_index < _snapshot.Count) { KeyValuePair 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(); } } /// Storage-free typed view over remote-motion components. public sealed class LiveEntityRemoteMotionRuntimeView where TRemoteMotion : class, ILiveEntityRemoteMotionRuntime { private readonly Func _runtime; public LiveEntityRemoteMotionRuntimeView(Func 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; }