using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using JetBrains.Annotations; using Robust.Shared.GameStates; using Robust.Shared.Log; using Robust.Shared.Physics.Components; using Robust.Shared.Player; using Robust.Shared.Timing; using Robust.Shared.Utility; #if EXCEPTION_TOLERANCE using Robust.Shared.Exceptions; #endif namespace Robust.Shared.GameObjects { /// public partial class EntityManager { [IoC.Dependency] private readonly IComponentFactory _componentFactory = default!; #if EXCEPTION_TOLERANCE [IoC.Dependency] private readonly IRuntimeLog _runtimeLog = default!; #endif public IComponentFactory ComponentFactory => _componentFactory; private const int TypeCapacity = 32; private const int ComponentCollectionCapacity = 1024; private const int EntityCapacity = 1024; private const int NetComponentCapacity = 8; private static readonly ComponentState DefaultComponentState = new(); private readonly Dictionary> _entTraitDict = new(); private Dictionary[] _entTraitArray = Array.Empty>(); private readonly HashSet _deleteSet = new(TypeCapacity); private UniqueIndexHkm _entCompIndex = new(ComponentCollectionCapacity); /// public event Action? ComponentAdded; /// public event Action? ComponentRemoved; public void InitializeComponents() { if (Initialized) throw new InvalidOperationException("Already initialized."); FillComponentDict(); _componentFactory.ComponentAdded += OnComponentAdded; } /// /// Instantly clears all components from the manager. This will NOT shut them down gracefully. /// Any entities relying on existing components will be broken. /// public void ClearComponents() { _entCompIndex.Clear(); _deleteSet.Clear(); foreach (var dict in _entTraitDict.Values) { dict.Clear(); } } private void AddComponentRefType(CompIdx type) { var dict = new Dictionary(); _entTraitDict.Add(_componentFactory.IdxToType(type), dict); CompIdx.AssignArray(ref _entTraitArray, type, dict); } private void OnComponentAdded(ComponentRegistration obj) { AddComponentRefType(obj.Idx); } #region Component Management /// public int Count() where T : IComponent { var dict = _entTraitDict[typeof(T)]; return dict.Count; } /// public int Count(Type component) { var dict = _entTraitDict[component]; return dict.Count; } public void InitializeComponents(EntityUid uid, MetaDataComponent? metadata = null) { DebugTools.AssertOwner(uid, metadata); metadata ??= GetComponent(uid); DebugTools.Assert(metadata.EntityLifeStage == EntityLifeStage.PreInit); metadata.EntityLifeStage = EntityLifeStage.Initializing; // Initialize() can modify the collection of components. Copy them. FixedArray32 compsFixed = default; var comps = compsFixed.AsSpan; CopyComponentsInto(ref comps, uid); foreach (var comp in comps) { if (comp is { LifeStage: ComponentLifeStage.Added }) LifeInitialize(comp, CompIdx.Index(comp.GetType())); } #if DEBUG // Second integrity check in case of. foreach (var t in _entCompIndex[uid]) { if (!t.Deleted && !t.Initialized) { DebugTools.Assert( $"Component {t.GetType()} was not initialized at the end of {nameof(InitializeComponents)}."); } } #endif DebugTools.Assert(metadata.EntityLifeStage == EntityLifeStage.Initializing); metadata.EntityLifeStage = EntityLifeStage.Initialized; } public void StartComponents(EntityUid uid) { // Startup() can modify _components // This code can only handle additions to the list. Is there a better way? Probably not. FixedArray32 compsFixed = default; var comps = compsFixed.AsSpan; CopyComponentsInto(ref comps, uid); // TODO: please for the love of god remove these initialization order hacks. // Init transform first, we always have it. var transform = GetComponent(uid); if (transform.LifeStage == ComponentLifeStage.Initialized) LifeStartup(transform); // Init physics second if it exists. if (TryGetComponent(uid, out var phys) && phys.LifeStage == ComponentLifeStage.Initialized) { LifeStartup(phys); } // Do rest of components. foreach (var comp in comps) { if (comp is { LifeStage: ComponentLifeStage.Initialized }) LifeStartup(comp); } } public IComponent AddComponent(EntityUid uid, ushort netId, MetaDataComponent? meta = null) { var newComponent = _componentFactory.GetComponent(netId); AddComponent(uid, newComponent, metadata: meta); return newComponent; } public T AddComponent(EntityUid uid) where T : IComponent, new() { var newComponent = _componentFactory.GetComponent(); AddComponent(uid, newComponent); return newComponent; } public readonly struct CompInitializeHandle : IDisposable where T : IComponent { private readonly IEntityManager _entMan; private readonly EntityUid _owner; public readonly CompIdx CompType; public readonly T Comp; public CompInitializeHandle(IEntityManager entityManager, EntityUid owner, T comp, CompIdx compType) { _entMan = entityManager; _owner = owner; Comp = comp; CompType = compType; } public void Dispose() { var metadata = _entMan.GetComponent(_owner); if (!metadata.EntityInitialized && !metadata.EntityInitializing) return; if (!Comp.Initialized) ((EntityManager) _entMan).LifeInitialize(Comp, CompType); if (metadata.EntityInitialized && !Comp.Running) ((EntityManager) _entMan).LifeStartup(Comp); } public static implicit operator T(CompInitializeHandle handle) { return handle.Comp; } } /// [Obsolete] public CompInitializeHandle AddComponentUninitialized(EntityUid uid) where T : IComponent, new() { var reg = _componentFactory.GetRegistration(); var newComponent = (T)_componentFactory.GetComponent(reg); #pragma warning disable CS0618 // Type or member is obsolete newComponent.Owner = uid; #pragma warning restore CS0618 // Type or member is obsolete if (!uid.IsValid() || !EntityExists(uid)) throw new ArgumentException($"Entity {uid} is not valid.", nameof(uid)); AddComponentInternal(uid, newComponent, false, true); return new CompInitializeHandle(this, uid, newComponent, reg.Idx); } /// public void AddComponent(EntityUid uid, T component, bool overwrite = false, MetaDataComponent? metadata = null) where T : IComponent { if (!uid.IsValid() || !EntityExists(uid)) throw new ArgumentException($"Entity {uid} is not valid.", nameof(uid)); if (component == null) throw new ArgumentNullException(nameof(component)); #pragma warning disable CS0618 // Type or member is obsolete if (component.Owner == default) { component.Owner = uid; } else if (component.Owner != uid) { throw new InvalidOperationException("Component is not owned by entity."); } #pragma warning restore CS0618 // Type or member is obsolete AddComponentInternal(uid, component, overwrite, false, metadata); } private void AddComponentInternal(EntityUid uid, T component, bool overwrite, bool skipInit, MetaDataComponent? metadata = null) where T : IComponent { // get interface aliases for mapping var reg = _componentFactory.GetRegistration(component); AddComponentInternal(uid, component, reg, overwrite, skipInit, metadata); } private void AddComponentInternal(EntityUid uid, T component, ComponentRegistration reg, bool overwrite, bool skipInit, MetaDataComponent? metadata = null) where T : IComponent { // We can't use typeof(T) here in case T is just Component DebugTools.Assert(component is MetaDataComponent || (metadata ?? MetaQuery.GetComponent(uid)).EntityLifeStage < EntityLifeStage.Terminating, $"Attempted to add a {component.GetType().Name} component to an entity ({ToPrettyString(uid)}) while it is terminating"); // Check that there is no existing component. var type = reg.Idx; var dict = _entTraitArray[type.Value]; DebugTools.Assert(dict != null); // Code block to restrict access to ref comp. { ref var comp = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, uid, out var exists); if (exists) { if (!overwrite && !comp!.Deleted) { throw new InvalidOperationException( $"Component reference type {reg.Name} already occupied by {comp}"); } // This will invalidate the comp ref as it removes the key from the dictionary. // This is inefficient, but component overriding rarely ever happens. RemoveComponentImmediate(comp!, uid, false, metadata); dict.Add(uid, component); } else { comp = component; } } // actually ADD the component _entCompIndex.Add(uid, component); // add the component to the netId grid if (reg.NetID != null) { // the main comp grid keeps this in sync var netId = reg.NetID.Value; metadata ??= MetaQuery.GetComponentInternal(uid); metadata.NetComponents.Add(netId, component); } else { component.Networked = false; } var eventArgs = new AddedComponentEventArgs(new ComponentEventArgs(component, uid), reg); ComponentAdded?.Invoke(eventArgs); _eventBus.OnComponentAdded(eventArgs); LifeAddToEntity(component, reg.Idx); if (skipInit) return; metadata ??= MetaQuery.GetComponentInternal(uid); if (!metadata.EntityInitialized && !metadata.EntityInitializing) return; if (component.Networked) DirtyEntity(uid, metadata); LifeInitialize(component, reg.Idx); if (metadata.EntityInitialized) LifeStartup(component); if (metadata.EntityLifeStage >= EntityLifeStage.MapInitialized) EventBus.RaiseComponentEvent(component, MapInitEventInstance); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool RemoveComponent(EntityUid uid, MetaDataComponent? meta = null) { return RemoveComponent(uid, typeof(T), meta); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool RemoveComponent(EntityUid uid, Type type, MetaDataComponent? meta = null) { if (!TryGetComponent(uid, type, out var comp)) return false; RemoveComponentImmediate(comp, uid, false, meta); return true; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool RemoveComponent(EntityUid uid, ushort netId, MetaDataComponent? meta = null) { if (!MetaQuery.Resolve(uid, ref meta)) return false; if (!TryGetComponent(uid, netId, out var comp, meta)) return false; RemoveComponentImmediate(comp, uid, false, meta); return true; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveComponent(EntityUid uid, IComponent component, MetaDataComponent? meta = null) { RemoveComponentImmediate(component, uid, false, meta); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool RemoveComponentDeferred(EntityUid uid) { return RemoveComponentDeferred(uid, typeof(T)); } /// public bool RemoveComponentDeferred(EntityUid uid, Type type) { if (!TryGetComponent(uid, type, out var comp)) return false; RemoveComponentDeferred(comp, uid, false); return true; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool RemoveComponentDeferred(EntityUid uid, ushort netId, MetaDataComponent? meta = null) { if (!MetaQuery.Resolve(uid, ref meta)) return false; if (!TryGetComponent(uid, netId, out var comp, meta)) return false; RemoveComponentDeferred(comp, uid, false); return true; } /// public void RemoveComponentDeferred(EntityUid owner, IComponent component) { RemoveComponentDeferred(component, owner, false); } /// public void RemoveComponentDeferred(EntityUid owner, Component component) { RemoveComponentDeferred(component, owner, false); } private static IEnumerable InSafeOrder(IEnumerable comps, bool forCreation = false) { static int Sequence(IComponent x) => x switch { MetaDataComponent _ => 0, TransformComponent _ => 1, PhysicsComponent _ => 2, _ => int.MaxValue }; return forCreation ? comps.OrderBy(Sequence) : comps.OrderByDescending(Sequence); } /// public void RemoveComponents(EntityUid uid, MetaDataComponent? meta = null) { if (!MetaQuery.Resolve(uid, ref meta)) return; foreach (var comp in InSafeOrder(_entCompIndex[uid])) { RemoveComponentImmediate(comp, uid, false, meta); } } /// public void DisposeComponents(EntityUid uid, MetaDataComponent? meta = null) { if (!MetaQuery.Resolve(uid, ref meta)) return; foreach (var comp in InSafeOrder(_entCompIndex[uid])) { try { RemoveComponentImmediate(comp, uid, true, meta); } catch (Exception) { _sawmill.Error($"Caught exception while trying to remove component {_componentFactory.GetComponentName(comp.GetType())} from entity '{ToPrettyString(uid)}'"); } } _entCompIndex.Remove(uid); } private void RemoveComponentDeferred(IComponent component, EntityUid uid, bool terminating) { if (component == null) throw new ArgumentNullException(nameof(component)); #pragma warning disable CS0618 // Type or member is obsolete if (component.Owner != uid) #pragma warning restore CS0618 // Type or member is obsolete throw new InvalidOperationException("Component is not owned by entity."); if (component.Deleted) return; #if EXCEPTION_TOLERANCE try { #endif // these two components are required on all entities and cannot be removed normally. if (!terminating && component is TransformComponent or MetaDataComponent) { DebugTools.Assert("Tried to remove a protected component."); return; } if (!_deleteSet.Add(component)) { // already deferred deletion return; } if (component.LifeStage >= ComponentLifeStage.Initialized && component.LifeStage <= ComponentLifeStage.Running) LifeShutdown(component); #if EXCEPTION_TOLERANCE } catch (Exception e) { _sawmill.Error($"Caught exception while queuing deferred component removal. Entity={ToPrettyString(component.Owner)}, type={component.GetType()}"); _runtimeLog.LogException(e, nameof(RemoveComponentDeferred)); } #endif } private void RemoveComponentImmediate(IComponent component, EntityUid uid, bool terminating, MetaDataComponent? meta) { if (component.Deleted) { _sawmill.Warning($"Deleting an already deleted component. Entity: {ToPrettyString(uid)}, Component: {_componentFactory.GetComponentName(component.GetType())}."); return; } #if EXCEPTION_TOLERANCE try { #endif // these two components are required on all entities and cannot be removed. if (!terminating && component is TransformComponent or MetaDataComponent) { DebugTools.Assert("Tried to remove a protected component."); return; } if (component.Running) LifeShutdown(component); if (component.LifeStage != ComponentLifeStage.PreAdd) LifeRemoveFromEntity(component); // Sets delete #if EXCEPTION_TOLERANCE } catch (Exception e) { _sawmill.Error($"Caught exception during immediate component removal. Entity={ToPrettyString(component.Owner)}, type={component.GetType()}"); _runtimeLog.LogException(e, nameof(RemoveComponentImmediate)); } #endif DeleteComponent(uid, component, terminating, meta); } /// public void CullRemovedComponents() { foreach (var component in InSafeOrder(_deleteSet)) { if (component.Deleted) continue; var uid = component.Owner; #if EXCEPTION_TOLERANCE try { #endif // The component may have been restarted sometime after removal was deferred. if (component.Running) { // TODO add options to cancel deferred deletion? _sawmill.Warning($"Found a running component while culling deferred deletions, owner={ToPrettyString(uid)}, type={component.GetType()}"); LifeShutdown(component); } if (component.LifeStage != ComponentLifeStage.PreAdd) LifeRemoveFromEntity(component); #if EXCEPTION_TOLERANCE } catch (Exception e) { _sawmill.Error($"Caught exception while processing deferred component removal. Entity={ToPrettyString(component.Owner)}, type={component.GetType()}"); _runtimeLog.LogException(e, nameof(CullRemovedComponents)); } #endif DeleteComponent(uid, component, false); } _deleteSet.Clear(); } private void DeleteComponent(EntityUid entityUid, IComponent component, bool terminating, MetaDataComponent? metadata = null) { if (!MetaQuery.ResolveInternal(entityUid, ref metadata)) return; var eventArgs = new RemovedComponentEventArgs(new ComponentEventArgs(component, entityUid), false, metadata); ComponentRemoved?.Invoke(eventArgs); _eventBus.OnComponentRemoved(eventArgs); var reg = _componentFactory.GetRegistration(component); DebugTools.Assert(component.Networked == (reg.NetID != null)); if (!terminating && reg.NetID != null) { if (!metadata.NetComponents.Remove(reg.NetID.Value)) _sawmill.Error($"Entity {ToPrettyString(entityUid, metadata)} did not have {component.GetType().Name} in its networked component dictionary during component deletion."); if (component.NetSyncEnabled) { DirtyEntity(entityUid, metadata); metadata.LastComponentRemoved = _gameTiming.CurTick; } } _entTraitArray[reg.Idx.Value].Remove(entityUid); // TODO if terminating the entity, maybe defer this? // _entCompIndex.Remove(uid) gets called later on anyways. _entCompIndex.Remove(entityUid, component); DebugTools.Assert(_netMan.IsClient // Client side prediction can set LastComponentRemoved to some future tick, || metadata.EntityLastModifiedTick >= metadata.LastComponentRemoved); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasComponent(EntityUid uid) { var dict = _entTraitArray[CompIdx.ArrayIndex()]; DebugTools.Assert(dict != null, $"Unknown component: {typeof(T).Name}"); return dict.TryGetValue(uid, out var comp) && !comp.Deleted; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasComponent(EntityUid? uid) { return uid.HasValue && HasComponent(uid.Value); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasComponent(EntityUid uid, Type type) { var dict = _entTraitDict[type]; return dict.TryGetValue(uid, out var comp) && !comp.Deleted; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasComponent(EntityUid? uid, Type type) { if (!uid.HasValue) { return false; } var dict = _entTraitDict[type]; return dict.TryGetValue(uid.Value, out var comp) && !comp.Deleted; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasComponent(EntityUid uid, ushort netId, MetaDataComponent? meta = null) { if (!MetaQuery.Resolve(uid, ref meta)) return false; return meta.NetComponents.ContainsKey(netId); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasComponent(EntityUid? uid, ushort netId, MetaDataComponent? meta = null) { if (!uid.HasValue) { DebugTools.AssertNull(meta); return false; } return HasComponent(uid.Value, netId, meta); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public T EnsureComponent(EntityUid uid) where T : IComponent, new() { if (TryGetComponent(uid, out var component)) { // Check for deferred component removal. if (component.LifeStage <= ComponentLifeStage.Running) return component; RemoveComponent(uid, component); } return AddComponent(uid); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool EnsureComponent(ref Entity entity) where T : IComponent, new() { if (entity.Comp != null) { // Check for deferred component removal. if (entity.Comp.LifeStage <= ComponentLifeStage.Running) { DebugTools.AssertOwner(entity, entity.Comp); return true; } RemoveComponent(entity, entity.Comp); } entity.Comp = AddComponent(entity); return false; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool EnsureComponent(EntityUid entity, out T component) where T : IComponent, new() { if (TryGetComponent(entity, out var comp)) { // Check for deferred component removal. if (comp.LifeStage <= ComponentLifeStage.Running) { component = comp; return true; } RemoveComponent(entity, comp); } component = AddComponent(entity); return false; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public T GetComponent(EntityUid uid) where T : IComponent { var dict = _entTraitArray[CompIdx.ArrayIndex()]; DebugTools.Assert(dict != null, $"Unknown component: {typeof(T).Name}"); if (dict.TryGetValue(uid, out var comp)) { if (!comp.Deleted) { return (T)comp; } } throw new KeyNotFoundException($"Entity {uid} does not have a component of type {typeof(T)}"); } public IComponent GetComponent(EntityUid uid, CompIdx type) { var dict = _entTraitArray[type.Value]; if (dict.TryGetValue(uid, out var comp)) { if (!comp.Deleted) return comp; } throw new KeyNotFoundException($"Entity {uid} does not have a component of type {_componentFactory.IdxToType(type)}"); } /// public IComponent GetComponent(EntityUid uid, Type type) { // ReSharper disable once InvertIf var dict = _entTraitDict[type]; if (dict.TryGetValue(uid, out var comp)) { if (!comp.Deleted) { return comp; } } throw new KeyNotFoundException($"Entity {uid} does not have a component of type {type}"); } /// public IComponent GetComponent(EntityUid uid, ushort netId, MetaDataComponent? meta = null) { return (meta ?? MetaQuery.GetComponentInternal(uid)).NetComponents[netId]; } /// public IComponent GetComponentInternal(EntityUid uid, CompIdx type) { var dict = _entTraitArray[type.Value]; if (dict.TryGetValue(uid, out var comp)) { return comp; } throw new KeyNotFoundException($"Entity {uid} does not have a component of type {type}"); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetComponent(EntityUid uid, [NotNullWhen(true)] out T? component) { var dict = _entTraitArray[CompIdx.ArrayIndex()]; DebugTools.Assert(dict != null, $"Unknown component: {typeof(T).Name}"); if (dict.TryGetValue(uid, out var comp)) { if (!comp.Deleted) { component = (T)comp; return true; } } component = default; return false; } /// public bool TryGetComponent([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out T? component) { if (!uid.HasValue) { component = default!; return false; } if (TryGetComponent(uid.Value, typeof(T), out var comp)) { if (!comp.Deleted) { component = (T)comp; return true; } } component = default; return false; } /// public bool TryGetComponent(EntityUid uid, Type type, [NotNullWhen(true)] out IComponent? component) { var dict = _entTraitDict[type]; if (dict.TryGetValue(uid, out var comp)) { if (!comp.Deleted) { component = comp; return true; } } component = null; return false; } public bool TryGetComponent(EntityUid uid, CompIdx type, [NotNullWhen(true)] out IComponent? component) { var dict = _entTraitArray[type.Value]; if (dict.TryGetValue(uid, out var comp)) { if (!comp.Deleted) { component = comp; return true; } } component = null; return false; } /// public bool TryGetComponent([NotNullWhen(true)] EntityUid? uid, Type type, [NotNullWhen(true)] out IComponent? component) { if (!uid.HasValue) { component = null; return false; } var dict = _entTraitDict[type]; if (dict.TryGetValue(uid.Value, out var comp)) { if (!comp.Deleted) { component = comp; return true; } } component = null; return false; } /// public bool TryGetComponent(EntityUid uid, ushort netId, [MaybeNullWhen(false)] out IComponent component, MetaDataComponent? meta = null) { if (MetaQuery.TryGetComponentInternal(uid, out var metadata) && metadata.NetComponents.TryGetValue(netId, out var comp)) { component = comp; return true; } component = default; return false; } /// public bool TryGetComponent([NotNullWhen(true)] EntityUid? uid, ushort netId, [MaybeNullWhen(false)] out IComponent component, MetaDataComponent? meta = null) { if (!uid.HasValue) { DebugTools.AssertNull(meta); component = default; return false; } return TryGetComponent(uid.Value, netId, out component, meta); } public EntityQuery GetEntityQuery() where TComp1 : IComponent { var comps = _entTraitArray[CompIdx.ArrayIndex()]; DebugTools.Assert(comps != null, $"Unknown component: {typeof(TComp1).Name}"); return new EntityQuery(comps, _resolveSawmill); } public EntityQuery GetEntityQuery(Type type) { var comps = _entTraitArray[CompIdx.ArrayIndex(type)]; DebugTools.Assert(comps != null, $"Unknown component: {type.Name}"); return new EntityQuery(comps, _resolveSawmill); } /// public IEnumerable GetComponents(EntityUid uid) { // ReSharper disable once LoopCanBeConvertedToQuery foreach (var comp in _entCompIndex[uid].ToArray()) { if (comp.Deleted) continue; yield return comp; } } /// public int ComponentCount(EntityUid uid) { var comps = _entCompIndex[uid]; return comps.Count; } /// /// Copy the components for an entity into the given span, /// or re-allocate the span as an array if there's not enough space.ยบ /// private void CopyComponentsInto(ref Span comps, EntityUid uid) { var set = _entCompIndex[uid]; if (set.Count > comps.Length) { comps = new IComponent[set.Count]; } var i = 0; foreach (var c in set) { comps[i++] = c; } } /// public IEnumerable GetComponents(EntityUid uid) { var comps = _entCompIndex[uid].ToArray(); foreach (var comp in comps) { if (comp.Deleted || comp is not T tComp) continue; yield return tComp; } } /// public NetComponentEnumerable GetNetComponents(EntityUid uid, MetaDataComponent? meta = null) { meta ??= MetaQuery.GetComponentInternal(uid); return new NetComponentEnumerable(meta.NetComponents); } /// public NetComponentEnumerable? GetNetComponentsOrNull(EntityUid uid, MetaDataComponent? meta = null) { return MetaQuery.Resolve(uid, ref meta) ? new NetComponentEnumerable(meta.NetComponents) : null; } #region Join Functions public (EntityUid Uid, T Component)[] AllComponents() where T : IComponent { var query = AllEntityQueryEnumerator(); var comps = new (EntityUid Uid, T Component)[Count()]; var i = 0; while (query.MoveNext(out var uid, out var comp)) { comps[i] = (uid, comp); i++; } return comps; } public List<(EntityUid Uid, T Component)> AllComponentsList() where T : IComponent { var query = AllEntityQueryEnumerator(); var comps = new List<(EntityUid Uid, T Component)>(Count()); while (query.MoveNext(out var uid, out var comp)) { comps.Add((uid, comp)); } return comps; } public AllEntityQueryEnumerator AllEntityQueryEnumerator() where TComp1 : IComponent { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; return new AllEntityQueryEnumerator(trait1); } public AllEntityQueryEnumerator AllEntityQueryEnumerator() where TComp1 : IComponent where TComp2 : IComponent { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; var trait2 = _entTraitArray[CompIdx.ArrayIndex()]; return new AllEntityQueryEnumerator(trait1, trait2); } public AllEntityQueryEnumerator AllEntityQueryEnumerator() where TComp1 : IComponent where TComp2 : IComponent where TComp3 : IComponent { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; var trait2 = _entTraitArray[CompIdx.ArrayIndex()]; var trait3 = _entTraitArray[CompIdx.ArrayIndex()]; return new AllEntityQueryEnumerator(trait1, trait2, trait3); } public AllEntityQueryEnumerator AllEntityQueryEnumerator() where TComp1 : IComponent where TComp2 : IComponent where TComp3 : IComponent where TComp4 : IComponent { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; var trait2 = _entTraitArray[CompIdx.ArrayIndex()]; var trait3 = _entTraitArray[CompIdx.ArrayIndex()]; var trait4 = _entTraitArray[CompIdx.ArrayIndex()]; return new AllEntityQueryEnumerator(trait1, trait2, trait3, trait4); } public EntityQueryEnumerator EntityQueryEnumerator() where TComp1 : IComponent { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; return new EntityQueryEnumerator(trait1, MetaQuery); } public EntityQueryEnumerator EntityQueryEnumerator() where TComp1 : IComponent where TComp2 : IComponent { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; var trait2 = _entTraitArray[CompIdx.ArrayIndex()]; return new EntityQueryEnumerator(trait1, trait2, MetaQuery); } public EntityQueryEnumerator EntityQueryEnumerator() where TComp1 : IComponent where TComp2 : IComponent where TComp3 : IComponent { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; var trait2 = _entTraitArray[CompIdx.ArrayIndex()]; var trait3 = _entTraitArray[CompIdx.ArrayIndex()]; return new EntityQueryEnumerator(trait1, trait2, trait3, MetaQuery); } public EntityQueryEnumerator EntityQueryEnumerator() where TComp1 : IComponent where TComp2 : IComponent where TComp3 : IComponent where TComp4 : IComponent { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; var trait2 = _entTraitArray[CompIdx.ArrayIndex()]; var trait3 = _entTraitArray[CompIdx.ArrayIndex()]; var trait4 = _entTraitArray[CompIdx.ArrayIndex()]; return new EntityQueryEnumerator(trait1, trait2, trait3, trait4, MetaQuery); } /// public IEnumerable EntityQuery(bool includePaused = false) where T : IComponent { var comps = _entTraitArray[CompIdx.ArrayIndex()]; DebugTools.Assert(comps != null, $"Unknown component: {typeof(T).Name}"); if (includePaused) { foreach (var t1Comp in comps.Values) { if (t1Comp.Deleted) continue; yield return (T)t1Comp; } } else { foreach (var (uid, t1Comp) in comps) { if (t1Comp.Deleted || !MetaQuery.TryGetComponentInternal(uid, out var metaComp)) continue; if (metaComp.EntityPaused) continue; yield return (T)t1Comp; } } } /// public IEnumerable<(TComp1, TComp2)> EntityQuery(bool includePaused = false) where TComp1 : IComponent where TComp2 : IComponent { // this would prob be faster if trait1 was a list (or an array of structs hue). var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; var trait2 = _entTraitArray[CompIdx.ArrayIndex()]; // you really want trait1 to be the smaller set of components if (includePaused) { foreach (var (uid, t1Comp) in trait1) { if (!trait2.TryGetValue(uid, out var t2Comp) || t2Comp.Deleted) continue; yield return ( (TComp1) t1Comp, (TComp2) t2Comp); } } else { var metaComps = _entTraitArray[CompIdx.ArrayIndex()]; foreach (var (uid, t1Comp) in trait1) { // Check paused last because 90% of the time the component's likely not gonna be paused. if (!trait2.TryGetValue(uid, out var t2Comp) || t2Comp.Deleted) continue; if (t1Comp.Deleted || !metaComps.TryGetValue(uid, out var metaComp)) continue; var meta = (MetaDataComponent)metaComp; if (meta.EntityPaused) continue; yield return ( (TComp1) t1Comp, (TComp2) t2Comp); } } } /// public IEnumerable<(TComp1, TComp2, TComp3)> EntityQuery(bool includePaused = false) where TComp1 : IComponent where TComp2 : IComponent where TComp3 : IComponent { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; var trait2 = _entTraitArray[CompIdx.ArrayIndex()]; var trait3 = _entTraitArray[CompIdx.ArrayIndex()]; if (includePaused) { foreach (var (uid, t1Comp) in trait1) { if (!trait2.TryGetValue(uid, out var t2Comp) || t2Comp.Deleted) continue; if (!trait3.TryGetValue(uid, out var t3Comp) || t3Comp.Deleted) continue; yield return ( (TComp1) t1Comp, (TComp2) t2Comp, (TComp3) t3Comp); } } else { var metaComps = _entTraitArray[CompIdx.ArrayIndex()]; foreach (var (uid, t1Comp) in trait1) { // Check paused last because 90% of the time the component's likely not gonna be paused. if (!trait2.TryGetValue(uid, out var t2Comp) || t2Comp.Deleted) continue; if (!trait3.TryGetValue(uid, out var t3Comp) || t3Comp.Deleted) continue; if (t1Comp.Deleted || !metaComps.TryGetValue(uid, out var metaComp)) continue; var meta = (MetaDataComponent)metaComp; if (meta.EntityPaused) continue; yield return ( (TComp1) t1Comp, (TComp2) t2Comp, (TComp3) t3Comp); } } } /// public IEnumerable<(TComp1, TComp2, TComp3, TComp4)> EntityQuery( bool includePaused = false) where TComp1 : IComponent where TComp2 : IComponent where TComp3 : IComponent where TComp4 : IComponent { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; var trait2 = _entTraitArray[CompIdx.ArrayIndex()]; var trait3 = _entTraitArray[CompIdx.ArrayIndex()]; var trait4 = _entTraitArray[CompIdx.ArrayIndex()]; if (includePaused) { foreach (var (uid, t1Comp) in trait1) { if (!trait2.TryGetValue(uid, out var t2Comp) || t2Comp.Deleted) continue; if (!trait3.TryGetValue(uid, out var t3Comp) || t3Comp.Deleted) continue; if (!trait4.TryGetValue(uid, out var t4Comp) || t4Comp.Deleted) continue; yield return ( (TComp1) t1Comp, (TComp2) t2Comp, (TComp3) t3Comp, (TComp4) t4Comp); } } else { var metaComps = _entTraitArray[CompIdx.ArrayIndex()]; foreach (var (uid, t1Comp) in trait1) { // Check paused last because 90% of the time the component's likely not gonna be paused. if (!trait2.TryGetValue(uid, out var t2Comp) || t2Comp.Deleted) continue; if (!trait3.TryGetValue(uid, out var t3Comp) || t3Comp.Deleted) continue; if (!trait4.TryGetValue(uid, out var t4Comp) || t4Comp.Deleted) continue; if (t1Comp.Deleted || !metaComps.TryGetValue(uid, out var metaComp)) continue; var meta = (MetaDataComponent)metaComp; if (meta.EntityPaused) continue; yield return ( (TComp1) t1Comp, (TComp2) t2Comp, (TComp3) t3Comp, (TComp4) t4Comp); } } } #endregion /// public IEnumerable<(EntityUid Uid, IComponent Component)> GetAllComponents(Type type, bool includePaused = false) { var comps = _entTraitDict[type]; if (includePaused) { foreach (var (uid, comp) in comps) { if (comp.Deleted) continue; yield return (uid, comp); } } else { foreach (var (uid, comp) in comps) { if (comp.Deleted || !MetaQuery.TryGetComponent(uid, out var meta) || meta.EntityPaused) continue; yield return (uid, comp); } } } /// public ComponentState GetComponentState(IEventBus eventBus, IComponent component, ICommonSession? session, GameTick fromTick) { DebugTools.Assert(component.NetSyncEnabled, $"Attempting to get component state for an un-synced component: {component.GetType()}"); var getState = new ComponentGetState(session, fromTick); eventBus.RaiseComponentEvent(component, ref getState); return getState.State ?? DefaultComponentState; } public bool CanGetComponentState(IEventBus eventBus, IComponent component, ICommonSession player) { var attempt = new ComponentGetStateAttemptEvent(player); eventBus.RaiseComponentEvent(component, ref attempt); return !attempt.Cancelled; } #endregion [MethodImpl(MethodImplOptions.AggressiveInlining)] private void FillComponentDict() { _entTraitDict.Clear(); Array.Fill(_entTraitArray, null); foreach (var refType in _componentFactory.GetAllRefTypes()) { AddComponentRefType(refType); } } } public readonly struct NetComponentEnumerable { private readonly Dictionary _dictionary; public NetComponentEnumerable(Dictionary dictionary) => _dictionary = dictionary; public NetComponentEnumerator GetEnumerator() => new(_dictionary); } public struct NetComponentEnumerator { // DO NOT MAKE THIS READONLY private Dictionary.Enumerator _dictEnum; public NetComponentEnumerator(Dictionary dictionary) => _dictEnum = dictionary.GetEnumerator(); public bool MoveNext() => _dictEnum.MoveNext(); public (ushort netId, IComponent component) Current { get { var val = _dictEnum.Current; return (val.Key, val.Value); } } } public readonly struct EntityQuery where TComp1 : IComponent { private readonly Dictionary _traitDict; private readonly ISawmill _sawmill; public EntityQuery(Dictionary traitDict, ISawmill sawmill) { _traitDict = traitDict; _sawmill = sawmill; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public TComp1 GetComponent(EntityUid uid) { if (_traitDict.TryGetValue(uid, out var comp) && !comp.Deleted) return (TComp1) comp; throw new KeyNotFoundException($"Entity {uid} does not have a component of type {typeof(TComp1)}"); } [MethodImpl(MethodImplOptions.AggressiveInlining), Pure] public Entity Get(EntityUid uid) { if (_traitDict.TryGetValue(uid, out var comp) && !comp.Deleted) return new Entity(uid, (TComp1) comp); throw new KeyNotFoundException($"Entity {uid} does not have a component of type {typeof(TComp1)}"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public bool TryGetComponent([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out TComp1? component) { if (uid == null) { component = default; return false; } return TryGetComponent(uid.Value, out component); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public bool TryGetComponent(EntityUid uid, [NotNullWhen(true)] out TComp1? component) { if (_traitDict.TryGetValue(uid, out var comp) && !comp.Deleted) { component = (TComp1) comp; return true; } component = default; return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public bool HasComponent(EntityUid uid) { return _traitDict.TryGetValue(uid, out var comp) && !comp.Deleted; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public bool HasComponent(EntityUid? uid) { return uid != null && HasComponent(uid.Value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public bool Resolve(EntityUid uid, [NotNullWhen(true)] ref TComp1? component, bool logMissing = true) { if (component != null) { DebugTools.AssertOwner(uid, component); return true; } if (_traitDict.TryGetValue(uid, out var comp) && !comp.Deleted) { component = (TComp1)comp; return true; } if (logMissing) _sawmill.Error($"Can't resolve \"{typeof(TComp1)}\" on entity {uid}!\n{new StackTrace(1, true)}"); return false; } [MethodImpl(MethodImplOptions.AggressiveInlining), Pure] public bool Resolve(ref Entity entity, bool logMissing = true) { return Resolve(entity.Owner, ref entity.Comp, logMissing); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public TComp1? CompOrNull(EntityUid uid) { if (TryGetComponent(uid, out var comp)) return comp; return default; } #region Internal /// /// Elides the component.Deleted check of /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] internal TComp1 GetComponentInternal(EntityUid uid) { if (_traitDict.TryGetValue(uid, out var comp)) return (TComp1) comp; throw new KeyNotFoundException($"Entity {uid} does not have a component of type {typeof(TComp1)}"); } /// /// Elides the component.Deleted check of /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] internal bool TryGetComponentInternal([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out TComp1? component) { if (uid == null) { component = default; return false; } return TryGetComponentInternal(uid.Value, out component); } /// /// Elides the component.Deleted check of /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] internal bool TryGetComponentInternal(EntityUid uid, [NotNullWhen(true)] out TComp1? component) { if (_traitDict.TryGetValue(uid, out var comp)) { component = (TComp1) comp; return true; } component = default; return false; } /// /// Elides the component.Deleted check of /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] internal bool HasComponentInternal(EntityUid uid) { return _traitDict.TryGetValue(uid, out var comp) && !comp.Deleted; } /// /// Elides the component.Deleted check of /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] internal bool ResolveInternal(EntityUid uid, [NotNullWhen(true)] ref TComp1? component, bool logMissing = true) { if (component != null) { DebugTools.AssertOwner(uid, component); return true; } if (_traitDict.TryGetValue(uid, out var comp)) { component = (TComp1)comp; return true; } if (logMissing) _sawmill.Error($"Can't resolve \"{typeof(TComp1)}\" on entity {uid}!\n{new StackTrace(1, true)}"); return false; } /// /// Elides the component.Deleted check of /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] internal TComp1? CompOrNullInternal(EntityUid uid) { if (TryGetComponent(uid, out var comp)) return comp; return default; } #endregion } #region Query /// /// Returns all matching unpaused components. /// public struct EntityQueryEnumerator : IDisposable where TComp1 : IComponent { private Dictionary.Enumerator _traitDict; private readonly EntityQuery _metaQuery; public EntityQueryEnumerator( Dictionary traitDict, EntityQuery metaQuery) { _traitDict = traitDict.GetEnumerator(); _metaQuery = metaQuery; } public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1) { while (true) { if (!_traitDict.MoveNext()) { uid = default; comp1 = default; return false; } var current = _traitDict.Current; if (current.Value.Deleted) { continue; } if (!_metaQuery.TryGetComponentInternal(current.Key, out var metaComp) || metaComp.EntityPaused) { continue; } uid = current.Key; comp1 = (TComp1)current.Value; return true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext([NotNullWhen(true)] out TComp1? comp1) { return MoveNext(out _, out comp1); } public void Dispose() { _traitDict.Dispose(); } } /// /// Returns all matching unpaused components. /// public struct EntityQueryEnumerator : IDisposable where TComp1 : IComponent where TComp2 : IComponent { private Dictionary.Enumerator _traitDict; private readonly Dictionary _traitDict2; private readonly EntityQuery _metaQuery; public EntityQueryEnumerator( Dictionary traitDict, Dictionary traitDict2, EntityQuery metaQuery) { _traitDict = traitDict.GetEnumerator(); _traitDict2 = traitDict2; _metaQuery = metaQuery; } public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2) { while (true) { if (!_traitDict.MoveNext()) { uid = default; comp1 = default; comp2 = default; return false; } var current = _traitDict.Current; if (current.Value.Deleted) { continue; } if (!_metaQuery.TryGetComponentInternal(current.Key, out var metaComp) || metaComp.EntityPaused) { continue; } if (!_traitDict2.TryGetValue(current.Key, out var comp2Obj) || comp2Obj.Deleted) { continue; } uid = current.Key; comp1 = (TComp1)current.Value; comp2 = (TComp2)comp2Obj; return true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext([NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2) { return MoveNext(out _, out comp1, out comp2); } public void Dispose() { _traitDict.Dispose(); } } /// /// Returns all matching unpaused components. /// public struct EntityQueryEnumerator : IDisposable where TComp1 : IComponent where TComp2 : IComponent where TComp3 : IComponent { private Dictionary.Enumerator _traitDict; private readonly Dictionary _traitDict2; private readonly Dictionary _traitDict3; private readonly EntityQuery _metaQuery; public EntityQueryEnumerator( Dictionary traitDict, Dictionary traitDict2, Dictionary traitDict3, EntityQuery metaQuery) { _traitDict = traitDict.GetEnumerator(); _traitDict2 = traitDict2; _traitDict3 = traitDict3; _metaQuery = metaQuery; } public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2, [NotNullWhen(true)] out TComp3? comp3) { while (true) { if (!_traitDict.MoveNext()) { uid = default; comp1 = default; comp2 = default; comp3 = default; return false; } var current = _traitDict.Current; if (current.Value.Deleted) { continue; } if (!_metaQuery.TryGetComponentInternal(current.Key, out var metaComp) || metaComp.EntityPaused) { continue; } if (!_traitDict2.TryGetValue(current.Key, out var comp2Obj) || comp2Obj.Deleted) { continue; } if (!_traitDict3.TryGetValue(current.Key, out var comp3Obj) || comp3Obj.Deleted) { continue; } uid = current.Key; comp1 = (TComp1)current.Value; comp2 = (TComp2)comp2Obj; comp3 = (TComp3)comp3Obj; return true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext( [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2, [NotNullWhen(true)] out TComp3? comp3) { return MoveNext(out _, out comp1, out comp2, out comp3); } public void Dispose() { _traitDict.Dispose(); } } /// /// Returns all matching unpaused components. /// public struct EntityQueryEnumerator : IDisposable where TComp1 : IComponent where TComp2 : IComponent where TComp3 : IComponent where TComp4 : IComponent { private Dictionary.Enumerator _traitDict; private readonly Dictionary _traitDict2; private readonly Dictionary _traitDict3; private readonly Dictionary _traitDict4; private readonly EntityQuery _metaQuery; public EntityQueryEnumerator( Dictionary traitDict, Dictionary traitDict2, Dictionary traitDict3, Dictionary traitDict4, EntityQuery metaQuery) { _traitDict = traitDict.GetEnumerator(); _traitDict2 = traitDict2; _traitDict3 = traitDict3; _traitDict4 = traitDict4; _metaQuery = metaQuery; } public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2, [NotNullWhen(true)] out TComp3? comp3, [NotNullWhen(true)] out TComp4? comp4) { while (true) { if (!_traitDict.MoveNext()) { uid = default; comp1 = default; comp2 = default; comp3 = default; comp4 = default; return false; } var current = _traitDict.Current; if (current.Value.Deleted) { continue; } if (!_metaQuery.TryGetComponentInternal(current.Key, out var metaComp) || metaComp.EntityPaused) { continue; } if (!_traitDict2.TryGetValue(current.Key, out var comp2Obj) || comp2Obj.Deleted) { continue; } if (!_traitDict3.TryGetValue(current.Key, out var comp3Obj) || comp3Obj.Deleted) { continue; } if (!_traitDict4.TryGetValue(current.Key, out var comp4Obj) || comp4Obj.Deleted) { continue; } uid = current.Key; comp1 = (TComp1)current.Value; comp2 = (TComp2)comp2Obj; comp3 = (TComp3)comp3Obj; comp4 = (TComp4)comp4Obj; return true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext( [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2, [NotNullWhen(true)] out TComp3? comp3, [NotNullWhen(true)] out TComp4? comp4) { return MoveNext(out _, out comp1, out comp2, out comp3, out comp4); } public void Dispose() { _traitDict.Dispose(); } } #endregion #region All query /// /// Returns all matching components, paused or not. /// public struct AllEntityQueryEnumerator : IDisposable where TComp1 : IComponent { private Dictionary.Enumerator _traitDict; public AllEntityQueryEnumerator( Dictionary traitDict) { _traitDict = traitDict.GetEnumerator(); } public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1) { while (true) { if (!_traitDict.MoveNext()) { uid = default; comp1 = default; return false; } var current = _traitDict.Current; if (current.Value.Deleted) { continue; } uid = current.Key; comp1 = (TComp1)current.Value; return true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext([NotNullWhen(true)] out TComp1? comp1) { return MoveNext(out _, out comp1); } public void Dispose() { _traitDict.Dispose(); } } /// /// Returns all matching components, paused or not. /// public struct AllEntityQueryEnumerator : IDisposable where TComp1 : IComponent where TComp2 : IComponent { private Dictionary.Enumerator _traitDict; private readonly Dictionary _traitDict2; public AllEntityQueryEnumerator( Dictionary traitDict, Dictionary traitDict2) { _traitDict = traitDict.GetEnumerator(); _traitDict2 = traitDict2; } public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2) { while (true) { if (!_traitDict.MoveNext()) { uid = default; comp1 = default; comp2 = default; return false; } var current = _traitDict.Current; if (current.Value.Deleted) { continue; } if (!_traitDict2.TryGetValue(current.Key, out var comp2Obj) || comp2Obj.Deleted) { continue; } uid = current.Key; comp1 = (TComp1)current.Value; comp2 = (TComp2)comp2Obj; return true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext([NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2) { return MoveNext(out _, out comp1, out comp2); } public void Dispose() { _traitDict.Dispose(); } } /// /// Returns all matching components, paused or not. /// public struct AllEntityQueryEnumerator : IDisposable where TComp1 : IComponent where TComp2 : IComponent where TComp3 : IComponent { private Dictionary.Enumerator _traitDict; private readonly Dictionary _traitDict2; private readonly Dictionary _traitDict3; public AllEntityQueryEnumerator( Dictionary traitDict, Dictionary traitDict2, Dictionary traitDict3) { _traitDict = traitDict.GetEnumerator(); _traitDict2 = traitDict2; _traitDict3 = traitDict3; } public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2, [NotNullWhen(true)] out TComp3? comp3) { while (true) { if (!_traitDict.MoveNext()) { uid = default; comp1 = default; comp2 = default; comp3 = default; return false; } var current = _traitDict.Current; if (current.Value.Deleted) { continue; } if (!_traitDict2.TryGetValue(current.Key, out var comp2Obj) || comp2Obj.Deleted) { continue; } if (!_traitDict3.TryGetValue(current.Key, out var comp3Obj) || comp3Obj.Deleted) { continue; } uid = current.Key; comp1 = (TComp1)current.Value; comp2 = (TComp2)comp2Obj; comp3 = (TComp3)comp3Obj; return true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext( [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2, [NotNullWhen(true)] out TComp3? comp3) { return MoveNext(out _, out comp1, out comp2, out comp3); } public void Dispose() { _traitDict.Dispose(); } } /// /// Returns all matching components, paused or not. /// public struct AllEntityQueryEnumerator : IDisposable where TComp1 : IComponent where TComp2 : IComponent where TComp3 : IComponent where TComp4 : IComponent { private Dictionary.Enumerator _traitDict; private readonly Dictionary _traitDict2; private readonly Dictionary _traitDict3; private readonly Dictionary _traitDict4; public AllEntityQueryEnumerator( Dictionary traitDict, Dictionary traitDict2, Dictionary traitDict3, Dictionary traitDict4) { _traitDict = traitDict.GetEnumerator(); _traitDict2 = traitDict2; _traitDict3 = traitDict3; _traitDict4 = traitDict4; } public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2, [NotNullWhen(true)] out TComp3? comp3, [NotNullWhen(true)] out TComp4? comp4) { while (true) { if (!_traitDict.MoveNext()) { uid = default; comp1 = default; comp2 = default; comp3 = default; comp4 = default; return false; } var current = _traitDict.Current; if (current.Value.Deleted) { continue; } if (!_traitDict2.TryGetValue(current.Key, out var comp2Obj) || comp2Obj.Deleted) { continue; } if (!_traitDict3.TryGetValue(current.Key, out var comp3Obj) || comp3Obj.Deleted) { continue; } if (!_traitDict4.TryGetValue(current.Key, out var comp4Obj) || comp4Obj.Deleted) { continue; } uid = current.Key; comp1 = (TComp1)current.Value; comp2 = (TComp2)comp2Obj; comp3 = (TComp3)comp3Obj; comp4 = (TComp4)comp4Obj; return true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext( [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2, [NotNullWhen(true)] out TComp3? comp3, [NotNullWhen(true)] out TComp4? comp4) { return MoveNext(out _, out comp1, out comp2, out comp3, out comp4); } public void Dispose() { _traitDict.Dispose(); } } #endregion }