using Ghost.Core; using Ghost.Entities.Components; using Ghost.Entities.Query; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Ghost.Entities; public readonly struct EntityManager : IDisposable { private readonly List _entities; private readonly Queue _freeEntitySlots; private readonly World _world; public readonly int EntityCount => _entities.Count; public readonly ReadOnlySpan Entities => CollectionsMarshal.AsSpan(_entities); internal EntityManager(World world, int initialCapacity) { _entities = new(initialCapacity); _freeEntitySlots = new(initialCapacity); _world = world; } /// /// Adds a new to the world. /// /// The created . public readonly Entity CreateEntity() { Entity entity; if (_freeEntitySlots.TryDequeue(out var id)) { entity = _entities[id]; } else { id = _entities.Count; entity = new Entity(id, 0); _entities.Add(entity); } return entity; } internal readonly void AddEntityInternal(Entity entity) { _entities.Add(entity); } /// /// Removes the specified from the world. /// /// public readonly void RemoveEntity(ref Entity entity) { if (entity.ID >= _entities.Count || _entities[entity.ID].Generation != entity.Generation) { return; } _world.ComponentStorage.Remove(entity); var slot = _entities[entity.ID]; slot.IncrementGeneration(); _entities[entity.ID] = slot; _freeEntitySlots.Enqueue(entity.ID); entity = Entity.Invalid; } /// /// Checks if the given is valid and belongs to this . /// /// The entity to check. /// True if the entity is valid and belongs to this world; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool HasEntity(Entity entity) { if (!entity.IsValid || entity.ID >= _entities.Count) { return false; } return _entities[entity.ID].Generation == entity.Generation; } public readonly void AddComponent(Entity entity, IComponentData component, Type type) { var typeHandle = TypeHandle.Get(type); ref var pool = ref CollectionsMarshal.GetValueRefOrAddDefault(_world.ComponentStorage.ComponentPools, typeHandle, out var exists); if (!exists) { var poolType = typeof(ComponentPool<>).MakeGenericType(type); pool = (IComponentPool)(Activator.CreateInstance(poolType) ?? throw new InvalidOperationException($"Failed to create component pool for type {type}.")); } pool!.Add(entity, component); _world.ComponentStorage.GetOrCreateMask(typeHandle).SetBit(entity.ID); } /// /// Adds a component of type to the given . /// /// The type of the component to set. /// The entity for which the component is to be add. /// The component value to add. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly void AddComponent(Entity entity, T component) where T : unmanaged, IComponentData { _world.ComponentStorage.GetOrCreateComponentPool().Add(entity, component); _world.ComponentStorage.GetOrCreateMask(TypeHandle.Get()).SetBit(entity.ID); } /// /// Removes a component of type from the given . /// /// The type of the component to remove. /// The entity for which the component is to be remove. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool RemoveComponent(Entity entity) where T : unmanaged, IComponentData { if (!_world.ComponentStorage.TryGetPool(out var pool) || !pool.Has(entity)) { return false; } if (!pool.Remove(entity)) { return false; } _world.ComponentStorage.GetOrCreateMask(TypeHandle.Get()).ClearBit(entity.ID); return true; } /// /// Sets a component of the specified type for the given . /// /// The entity for which the component is to be set. /// The component value to set. /// The type of the component to set. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly void SetComponent(Entity entity, IComponentData component, Type type) { var typeHandle = TypeHandle.Get(type); if (!_world.ComponentStorage.TryGetPool(typeHandle, out var pool)) { return; } if (!pool.Has(entity)) { return; } pool.Set(entity, component); } /// /// Sets a component of type for the given . /// /// The type of the component to set. /// The entity for which the component is to be set. /// The component value to set. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly void SetComponent(Entity entity, in T component) where T : unmanaged, IComponentData { _world.ComponentStorage.GetOrCreateComponentPool().Set(entity, in component); } /// /// Checks if the given has a component of the specified type. /// /// The entity to check. /// The handle of the component type. /// True if the entity has the component; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool HasComponent(Entity entity, TypeHandle typeHandle) { return _world.ComponentStorage.TryGetMask(typeHandle, out var bitSet) && bitSet.IsSet(entity.ID); } /// /// Retrieves a reference to a component of type associated with the given . /// /// The type of the component to retrieve. /// The entity whose component is to be retrieved. /// A to the component, or a null reference if the component does not exist. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Ref GetComponent(Entity entity) where T : unmanaged, IComponentData { if (_world.ComponentStorage.TryGetPool(out var pool) && pool.Has(entity)) { return new Ref(ref pool.GetRef(entity)); } else { return new Ref(ref Unsafe.NullRef(), false); } } /// /// Retrieves all components associated with the specified entity. /// /// This method iterates through all available component pools to find components associated /// with the given entity. It is designed to lazily yield components, making it efficient for scenarios where only /// a subset of components may be needed. /// The entity for which components are to be retrieved. /// An enumerable collection of components associated with the specified entity. If the entity has no components, /// the collection will be empty. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly IEnumerable GetComponents(Entity entity) { foreach (var pool in _world.ComponentStorage.ComponentPools.Values) { if (pool.Has(entity)) { yield return pool.Get(entity); } } } /// /// Retrieves an enumerable collection of raw pointers to the components associated with the specified entity. /// /// This method provides direct access to the memory locations of components, bypassing type /// safety. Use with caution, as improper handling of raw pointers can lead to undefined behavior or memory /// corruption. Ensure that the entity is valid and exists within the current world context before calling this /// method. /// The entity whose components are to be retrieved. /// An enumerable collection of representing the memory addresses of the components associated /// with the specified entity. The collection will be empty if the entity has no associated components. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly IEnumerable<(TypeHandle, IntPtr)> GetComponentsUnsafe(Entity entity) { foreach (var (typeHandle, pool) in _world.ComponentStorage.ComponentPools) { if (pool.Has(entity)) { yield return (typeHandle, pool.GetUnsafe(entity)); } } } /// /// Adds a script of type to the given . /// /// The type of the script to add. /// The entity to which the script is to be added. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly void AddScript(Entity entity) where T : ScriptComponent, new() { _world.ComponentStorage.ScriptComponentPool.Add(entity, new T()); } /// /// Adds a script of the specified type to the given . /// /// The entity to which the script is to be added. /// The type of the script to add. /// Thrown if the specified type does not inherit from . /// Thrown if the script instance could not be created. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly void AddScript(Entity entity, Type type) { if (!typeof(ScriptComponent).IsAssignableFrom(type)) { throw new ArgumentException($"Type {type} must inherit from ScriptComponent.", nameof(type)); } var instance = (ScriptComponent?)Activator.CreateInstance(type) ?? throw new InvalidOperationException($"Failed to create instance of {type}."); _world.ComponentStorage.ScriptComponentPool.Add(entity, instance); } /// /// Removes a script of type from the given . /// /// The type of the script to remove. /// The entity from which the script is to be removed. /// True if the script was successfully removed; otherwise, false. public readonly bool RemoveScript(Entity entity) where T : ScriptComponent { if (!_world.ComponentStorage.ScriptComponentPool.Remove(entity)) { return false; } return true; } /// /// Removes a script at the specified index from the given . /// /// The entity from which the script is to be removed. /// The index of the script to remove. /// True if the script was successfully removed; otherwise, false. public readonly bool RemoveScriptAt(Entity entity, int index) { if (!_world.ComponentStorage.ScriptComponentPool.RemoveAt(entity, index)) { return false; } return true; } /// /// Retrieves the first script of type associated with the given . /// /// The type of the script to retrieve. /// The entity whose script is to be retrieved. /// The script of type , or null if no such script exists. public readonly T? GetScript(Entity entity) where T : ScriptComponent { return (T?)_world.ComponentStorage.ScriptComponentPool.GetAll(entity)? .FirstOrDefault(script => script is T tScript); } /// /// Retrieves all scripts of type associated with the given . /// /// The type of the scripts to retrieve. /// The entity whose scripts are to be retrieved. /// An enumerable of scripts of type . public readonly IEnumerable GetScripts(Entity entity) where T : ScriptComponent { return (IEnumerable?)_world.ComponentStorage.ScriptComponentPool.GetAll(entity)?.Where(script => script is T tScript) ?? Enumerable.Empty(); } public readonly void Dispose() { _entities.Clear(); _freeEntitySlots.Clear(); } }