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;
}