using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using Robust.Shared.GameStates; using Robust.Shared.Log; using Robust.Shared.Physics.Components; using Robust.Shared.Players; 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 readonly Dictionary> _netComponents = new(EntityCapacity); 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 event Action? ComponentDeleted; public void InitializeComponents() { if (Initialized) throw new InvalidOperationException("Already initialized."); FillComponentDict(); _componentFactory.ComponentAdded += OnComponentAdded; _componentFactory.ComponentReferenceAdded += OnComponentReferenceAdded; } /// /// 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() { _netComponents.Clear(); _entCompIndex.Clear(); _deleteSet.Clear(); FillComponentDict(); } 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 private void OnComponentReferenceAdded(ComponentRegistration reg, CompIdx type) { AddComponentRefType(type); } /// public int Count() where T : Component { 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.Assert(metadata == null || metadata.Owner == uid); 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.Initialized }) comp.LifeInitialize(this, 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) transform.LifeStartup(this); // Init physics second if it exists. if (TryGetComponent(uid, out var phys) && phys.LifeStage == ComponentLifeStage.Initialized) { phys.LifeStartup(this); } // Do rest of components. foreach (var comp in comps) { if (comp is { LifeStage: ComponentLifeStage.Initialized }) comp.LifeStartup(this); } } public Component AddComponent(EntityUid uid, ushort netId) { var newComponent = (Component)_componentFactory.GetComponent(netId); newComponent.Owner = uid; AddComponent(uid, newComponent); return newComponent; } public T AddComponent(EntityUid uid) where T : Component, new() { var newComponent = _componentFactory.GetComponent(); newComponent.Owner = uid; AddComponent(uid, newComponent); return newComponent; } public readonly struct CompInitializeHandle : IDisposable where T : Component { private readonly IEntityManager _entMan; public readonly CompIdx CompType; public readonly T Comp; public CompInitializeHandle(IEntityManager entityManager, T comp, CompIdx compType) { _entMan = entityManager; Comp = comp; CompType = compType; } public void Dispose() { var metadata = _entMan.GetComponent(Comp.Owner); if (!metadata.EntityInitialized && !metadata.EntityInitializing) return; if (!Comp.Initialized) Comp.LifeInitialize(_entMan, CompType); if (metadata.EntityInitialized && !Comp.Running) Comp.LifeStartup(_entMan); } public static implicit operator T(CompInitializeHandle handle) { return handle.Comp; } } /// public CompInitializeHandle AddComponentUninitialized(EntityUid uid) where T : Component, new() { var reg = _componentFactory.GetRegistration(); var newComponent = (T)_componentFactory.GetComponent(reg); newComponent.Owner = uid; if (!uid.IsValid() || !EntityExists(uid)) throw new ArgumentException($"Entity {uid} is not valid.", nameof(uid)); if (newComponent == null) throw new ArgumentNullException(nameof(newComponent)); if (newComponent.Owner != uid) throw new InvalidOperationException("Component is not owned by entity."); AddComponentInternal(uid, newComponent, false, true); return new CompInitializeHandle(this, newComponent, reg.Idx); } /// public void AddComponent(EntityUid uid, T component, bool overwrite = false) where T : Component { if (!uid.IsValid() || !EntityExists(uid)) throw new ArgumentException($"Entity {uid} is not valid.", nameof(uid)); if (component == null) throw new ArgumentNullException(nameof(component)); if (component.Owner != uid) throw new InvalidOperationException("Component is not owned by entity."); AddComponentInternal(uid, component, overwrite, false); } private void AddComponentInternal(EntityUid uid, T component, bool overwrite, bool skipInit) where T : Component { // We can't use typeof(T) here in case T is just Component DebugTools.Assert(component is MetaDataComponent || GetComponent(uid).EntityLifeStage < EntityLifeStage.Terminating, $"Attempted to add a {component.GetType().Name} component to an entity ({ToPrettyString(uid)}) while it is terminating"); // get interface aliases for mapping var reg = _componentFactory.GetRegistration(component); // Check that there are no overlapping references. foreach (var type in reg.References) { var dict = _entTraitArray[type.Value]; if (!dict.TryGetValue(uid, out var duplicate)) continue; if (!overwrite && !duplicate.Deleted) throw new InvalidOperationException( $"Component reference type {component.GetType().Name} already occupied by {duplicate}"); RemoveComponentImmediate(duplicate, uid, false); } // add the component to the grid foreach (var type in reg.References) { _entTraitArray[type.Value].Add(uid, 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; if (!_netComponents.TryGetValue(uid, out var netSet)) { netSet = new Dictionary(NetComponentCapacity); _netComponents.Add(uid, netSet); } netSet.Add(netId, component); } else { component.Networked = false; } var eventArgs = new AddedComponentEventArgs(new ComponentEventArgs(component, uid), reg.Idx); ComponentAdded?.Invoke(eventArgs); _eventBus.OnComponentAdded(eventArgs); component.LifeAddToEntity(this, reg.Idx); if (skipInit) return; var metadata = GetComponent(uid); if (!metadata.EntityInitialized && !metadata.EntityInitializing) return; if (component.Networked) DirtyEntity(uid, metadata); component.LifeInitialize(this, reg.Idx); if (metadata.EntityInitialized) component.LifeStartup(this); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool RemoveComponent(EntityUid uid) { return RemoveComponent(uid, typeof(T)); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool RemoveComponent(EntityUid uid, Type type) { if (!TryGetComponent(uid, type, out var comp)) return false; RemoveComponentImmediate((Component)comp, uid, false); return true; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool RemoveComponent(EntityUid uid, ushort netId) { if (!TryGetComponent(uid, netId, out var comp)) return false; RemoveComponentImmediate((Component)comp, uid, false); return true; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveComponent(EntityUid uid, IComponent component) { RemoveComponent(uid, (Component)component); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveComponent(EntityUid uid, Component component) { RemoveComponentImmediate(component, uid, false); } /// [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((Component)comp, uid, false); return true; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool RemoveComponentDeferred(EntityUid uid, ushort netId) { if (!TryGetComponent(uid, netId, out var comp)) return false; RemoveComponentDeferred((Component)comp, uid, false); return true; } /// public void RemoveComponentDeferred(EntityUid owner, IComponent component) { RemoveComponentDeferred((Component)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) { foreach (var comp in InSafeOrder(_entCompIndex[uid])) { RemoveComponentImmediate(comp, uid, false); } } /// public void DisposeComponents(EntityUid uid) { foreach (var comp in InSafeOrder(_entCompIndex[uid])) { try { RemoveComponentImmediate(comp, uid, true); } catch (Exception) { _sawmill.Error($"Caught exception while trying to remove component {_componentFactory.GetComponentName(comp.GetType())} from entity '{ToPrettyString(uid)}'"); } } _entCompIndex.Remove(uid); _netComponents.Remove(uid); } private void RemoveComponentDeferred(Component component, EntityUid uid, bool terminating) { if (component == null) throw new ArgumentNullException(nameof(component)); if (component.Owner != uid) 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.Running) component.LifeShutdown(this); #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(Component component, EntityUid uid, bool terminating) { 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) component.LifeShutdown(this); if (component.LifeStage != ComponentLifeStage.PreAdd) component.LifeRemoveFromEntity(this); // 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 var eventArgs = new RemovedComponentEventArgs(new ComponentEventArgs(component, uid), terminating); ComponentRemoved?.Invoke(eventArgs); _eventBus.OnComponentRemoved(eventArgs); DeleteComponent(uid, component, terminating); } /// 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()}"); component.LifeShutdown(this); } if (component.LifeStage != ComponentLifeStage.PreAdd) component.LifeRemoveFromEntity(this); #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 var eventArgs = new RemovedComponentEventArgs(new ComponentEventArgs(component, uid), false); ComponentRemoved?.Invoke(eventArgs); _eventBus.OnComponentRemoved(eventArgs); DeleteComponent(uid, component, false); } _deleteSet.Clear(); } private void DeleteComponent(EntityUid entityUid, Component component, bool terminating) { var reg = _componentFactory.GetRegistration(component.GetType()); if (!terminating && reg.NetID != null && _netComponents.TryGetValue(entityUid, out var netSet)) { if (netSet.Count == 1) _netComponents.Remove(entityUid); else netSet.Remove(reg.NetID.Value); if (component.NetSyncEnabled) DirtyEntity(entityUid); } foreach (var refType in reg.References) { _entTraitArray[refType.Value].Remove(entityUid); } // TODO if terminating the entity, maybe defer this? // _entCompIndex.Remove(uid) gets called later on anyways. _entCompIndex.Remove(entityUid, component); ComponentDeleted?.Invoke(new DeletedComponentEventArgs(new ComponentEventArgs(component, entityUid), terminating)); } /// [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) { return _netComponents.TryGetValue(uid, out var netSet) && netSet.ContainsKey(netId); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasComponent(EntityUid? uid, ushort netId) { if (!uid.HasValue) { return false; } return _netComponents.TryGetValue(uid.Value, out var netSet) && netSet.ContainsKey(netId); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public T EnsureComponent(EntityUid uid) where T : Component, new() { if (TryGetComponent(uid, out var component)) { // Check for deferred component removal. if (component.LifeStage <= ComponentLifeStage.Running) return component; else RemoveComponent(uid, component); } return AddComponent(uid); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool EnsureComponent(EntityUid entity, out T component) where T : Component, new() { if (TryGetComponent(entity, out var comp)) { // Check for deferred component removal. if (comp.LifeStage <= ComponentLifeStage.Running) { component = comp; return true; } else RemoveComponent(entity, comp); } component = AddComponent(entity); return false; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public T GetComponent(EntityUid uid) { 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)(IComponent)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) { return _netComponents[uid][netId]; } /// [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)(IComponent)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) { if (_netComponents.TryGetValue(uid, out var netSet) && netSet.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) { if (!uid.HasValue) { component = default; return false; } if (_netComponents.TryGetValue(uid.Value, out var netSet) && netSet.TryGetValue(netId, out var comp)) { component = comp; return true; } component = default; return false; } public EntityQuery GetEntityQuery() where TComp1 : Component { 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 (Component 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 Component[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) { return new NetComponentEnumerable(_netComponents[uid]); } /// public NetComponentEnumerable? GetNetComponentsOrNull(EntityUid uid) { return _netComponents.TryGetValue(uid, out var data) ? new NetComponentEnumerable(data) : null; } #region Join Functions public AllEntityQueryEnumerator AllEntityQueryEnumerator() where TComp1 : Component { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; return new AllEntityQueryEnumerator(trait1); } public AllEntityQueryEnumerator AllEntityQueryEnumerator() where TComp1 : Component where TComp2 : Component { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; var trait2 = _entTraitArray[CompIdx.ArrayIndex()]; return new AllEntityQueryEnumerator(trait1, trait2); } public AllEntityQueryEnumerator AllEntityQueryEnumerator() where TComp1 : Component where TComp2 : Component where TComp3 : Component { 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 : Component where TComp2 : Component where TComp3 : Component where TComp4 : Component { 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 : Component { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; var metaTraits = _entTraitArray[CompIdx.ArrayIndex()]; return new EntityQueryEnumerator(trait1, metaTraits); } public EntityQueryEnumerator EntityQueryEnumerator() where TComp1 : Component where TComp2 : Component { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; var trait2 = _entTraitArray[CompIdx.ArrayIndex()]; var metaTraits = _entTraitArray[CompIdx.ArrayIndex()]; return new EntityQueryEnumerator(trait1, trait2, metaTraits); } public EntityQueryEnumerator EntityQueryEnumerator() where TComp1 : Component where TComp2 : Component where TComp3 : Component { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; var trait2 = _entTraitArray[CompIdx.ArrayIndex()]; var trait3 = _entTraitArray[CompIdx.ArrayIndex()]; var metaTraits = _entTraitArray[CompIdx.ArrayIndex()]; return new EntityQueryEnumerator(trait1, trait2, trait3, metaTraits); } public EntityQueryEnumerator EntityQueryEnumerator() where TComp1 : Component where TComp2 : Component where TComp3 : Component where TComp4 : Component { var trait1 = _entTraitArray[CompIdx.ArrayIndex()]; var trait2 = _entTraitArray[CompIdx.ArrayIndex()]; var trait3 = _entTraitArray[CompIdx.ArrayIndex()]; var trait4 = _entTraitArray[CompIdx.ArrayIndex()]; var metaTraits = _entTraitArray[CompIdx.ArrayIndex()]; return new EntityQueryEnumerator(trait1, trait2, trait3, trait4, metaTraits); } /// 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)(object)t1Comp; } } else { var metaComps = _entTraitArray[CompIdx.ArrayIndex()]; foreach (var t1Comp in comps!.Values) { if (t1Comp.Deleted || !metaComps.TryGetValue(t1Comp.Owner, out var metaComp)) continue; var meta = (MetaDataComponent)metaComp; if (meta.EntityPaused) continue; yield return (T)(object)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)(object)t1Comp, (TComp2)(object)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(t1Comp.Owner, out var metaComp)) continue; var meta = (MetaDataComponent)metaComp; if (meta.EntityPaused) continue; yield return ( (TComp1)(object)t1Comp, (TComp2)(object)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)(object)t1Comp, (TComp2)(object)t2Comp, (TComp3)(object)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(t1Comp.Owner, out var metaComp)) continue; var meta = (MetaDataComponent)metaComp; if (meta.EntityPaused) continue; yield return ( (TComp1)(object)t1Comp, (TComp2)(object)t2Comp, (TComp3)(object)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)(object)t1Comp, (TComp2)(object)t2Comp, (TComp3)(object)t3Comp, (TComp4)(object)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(t1Comp.Owner, out var metaComp)) continue; var meta = (MetaDataComponent)metaComp; if (meta.EntityPaused) continue; yield return ( (TComp1)(object)t1Comp, (TComp2)(object)t2Comp, (TComp3)(object)t3Comp, (TComp4)(object)t4Comp); } } } #endregion /// public IEnumerable GetAllComponents(Type type, bool includePaused = false) { var comps = _entTraitDict[type]; if (includePaused) { foreach (var comp in comps.Values) { if (comp.Deleted) continue; yield return comp; } } else { var metaQuery = GetEntityQuery(); foreach (var comp in comps.Values) { if (comp.Deleted || !metaQuery.TryGetComponent(comp.Owner, out var meta) || meta.EntityPaused) continue; yield return 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 ?? component.GetComponentState(); } 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, Component component) Current { get { var val = _dictEnum.Current; return (val.Key, val.Value); } } } public readonly struct EntityQuery where TComp1 : Component { private readonly Dictionary _traitDict; private readonly ISawmill _sawmill; public EntityQuery(Dictionary traitDict, ISawmill sawmill) { _traitDict = traitDict; _sawmill = sawmill; } [MethodImpl(MethodImplOptions.AggressiveInlining)] 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)] 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)] 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)] public bool HasComponent(EntityUid uid) { return _traitDict.TryGetValue(uid, out var comp) && !comp.Deleted; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Resolve(EntityUid uid, [NotNullWhen(true)] ref TComp1? component, bool logMissing = true) { if (component != null) { DebugTools.Assert(uid == component.Owner, "Specified Entity is not the component's Owner!"); 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)] public TComp1? CompOrNull(EntityUid uid) { if (TryGetComponent(uid, out var comp)) return comp; return null; } } #region Query /// /// Returns all matching unpaused components. /// public struct EntityQueryEnumerator : IDisposable where TComp1 : Component { private Dictionary.Enumerator _traitDict; private readonly Dictionary _metaDict; public EntityQueryEnumerator( Dictionary traitDict, Dictionary metaDict) { _traitDict = traitDict.GetEnumerator(); _metaDict = metaDict; } public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1) { while (true) { if (!_traitDict.MoveNext()) { uid = default; comp1 = null; return false; } var current = _traitDict.Current; if (current.Value.Deleted) { continue; } if (!_metaDict.TryGetValue(current.Key, out var metaComp) || ((MetaDataComponent)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 : Component where TComp2 : Component { private Dictionary.Enumerator _traitDict; private readonly Dictionary _traitDict2; private readonly Dictionary _metaDict; public EntityQueryEnumerator( Dictionary traitDict, Dictionary traitDict2, Dictionary metaDict) { _traitDict = traitDict.GetEnumerator(); _traitDict2 = traitDict2; _metaDict = metaDict; } public bool MoveNext(out EntityUid uid, [NotNullWhen(true)] out TComp1? comp1, [NotNullWhen(true)] out TComp2? comp2) { while (true) { if (!_traitDict.MoveNext()) { uid = default; comp1 = null; comp2 = null; return false; } var current = _traitDict.Current; if (current.Value.Deleted) { continue; } if (!_metaDict.TryGetValue(current.Key, out var metaComp) || ((MetaDataComponent)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 : Component where TComp2 : Component where TComp3 : Component { private Dictionary.Enumerator _traitDict; private readonly Dictionary _traitDict2; private readonly Dictionary _traitDict3; private readonly Dictionary _metaDict; public EntityQueryEnumerator( Dictionary traitDict, Dictionary traitDict2, Dictionary traitDict3, Dictionary metaDict) { _traitDict = traitDict.GetEnumerator(); _traitDict2 = traitDict2; _traitDict3 = traitDict3; _metaDict = metaDict; } 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 = null; comp2 = null; comp3 = null; return false; } var current = _traitDict.Current; if (current.Value.Deleted) { continue; } if (!_metaDict.TryGetValue(current.Key, out var metaComp) || ((MetaDataComponent)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 : Component where TComp2 : Component where TComp3 : Component where TComp4 : Component { private Dictionary.Enumerator _traitDict; private readonly Dictionary _traitDict2; private readonly Dictionary _traitDict3; private readonly Dictionary _traitDict4; private readonly Dictionary _metaDict; public EntityQueryEnumerator( Dictionary traitDict, Dictionary traitDict2, Dictionary traitDict3, Dictionary traitDict4, Dictionary metaDict) { _traitDict = traitDict.GetEnumerator(); _traitDict2 = traitDict2; _traitDict3 = traitDict3; _traitDict4 = traitDict4; _metaDict = metaDict; } 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 = null; comp2 = null; comp3 = null; comp4 = null; return false; } var current = _traitDict.Current; if (current.Value.Deleted) { continue; } if (!_metaDict.TryGetValue(current.Key, out var metaComp) || ((MetaDataComponent)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 : Component { 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 = null; 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 : Component where TComp2 : Component { 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 = null; comp2 = null; 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 : Component where TComp2 : Component where TComp3 : Component { 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 = null; comp2 = null; comp3 = null; 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 : Component where TComp2 : Component where TComp3 : Component where TComp4 : Component { 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 = null; comp2 = null; comp3 = null; comp4 = null; 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 }