namespace OECS; /// /// The central container for all ECS state. /// /// Manages entity lifecycle, component storage, and provides the primary /// API for adding, removing, and querying components. /// public class World : IDisposable { private readonly EntityAllocator _allocator; private readonly ComponentStore _components; private readonly CommandQueue _commands; private readonly RelationshipIndex _relationships; public World() { _allocator = new EntityAllocator(); _components = new ComponentStore(); _commands = new CommandQueue(); _relationships = new RelationshipIndex(); } // ── Entity Management ──────────────────────────────────────────── /// /// Creates a new entity and returns its handle. /// public Entity CreateEntity() { return _allocator.Allocate(); } /// /// Destroys an entity, removing all its components and recycling its ID. /// /// Cascade behavior: /// - All relationships where this entity is the source are removed. /// - All relationships where this entity is the target are removed /// from the source entities. /// public void DestroyEntity(Entity entity) { if (!_allocator.IsAlive(entity)) return; // Singleton entity is never destroyed. if (entity.Id == 1) return; // Remove all relationships where this entity is the target. foreach (var (relType, sources) in _relationships.GetIncomingRelationships(entity)) { foreach (var source in sources) { _relationships.OnRemoved(relType, source, entity); _components.Remove(source, relType); } } // Remove all relationships where this entity is the source. _relationships.RemoveAllSourcesForEntity(entity); _components.RemoveAll(entity); _allocator.Free(entity); } /// /// Returns true if the entity is currently alive (not destroyed). /// public bool IsAlive(Entity entity) { return _allocator.IsAlive(entity); } // ── Component Management ───────────────────────────────────────── /// /// Adds a component to the entity. Replaces the existing component if /// the entity already has one of type . /// /// If implements , /// the reverse index is updated automatically. /// public void AddComponent(Entity entity, T component) where T : struct { ThrowIfNotAlive(entity); // If replacing an existing relationship, remove the old index entry first. if (component is IRelationship newRel) { if (_components.TryGet(entity, out var old)) { var oldRel = (IRelationship)(object)old; _relationships.OnRemoved(typeof(T), entity, oldRel.Target); } } _components.Add(entity, component); // Update relationship index. if (component is IRelationship rel) { _relationships.OnAdded(typeof(T), entity, rel.Target); } } /// /// Removes the component of type from the entity. /// No-op if the entity does not have the component. /// /// If implements , /// the reverse index is updated automatically. /// public void RemoveComponent(Entity entity) where T : struct { // Update relationship index before removal. if (typeof(IRelationship).IsAssignableFrom(typeof(T))) { if (_components.TryGet(entity, out var existing)) { var rel = (IRelationship)(object)existing; _relationships.OnRemoved(typeof(T), entity, rel.Target); } } _components.Remove(entity); } /// /// Returns a reference to the component of type /// for the given entity. Throws if the entity does not have the component. /// public ref T GetComponent(Entity entity) where T : struct { return ref _components.Get(entity); } /// /// Returns true if the entity has a component of type . /// public bool HasComponent(Entity entity) where T : struct { return _components.Has(entity); } // ── Relationships ──────────────────────────────────────────────── /// /// Returns all source entities that have a relationship of type /// pointing to the given target entity. /// public IReadOnlyCollection GetSources(Entity target) where T : struct, IRelationship { return _relationships.GetSources(target); } // ── Commands ────────────────────────────────────────────────────── /// /// The command queue for deferred, serializable commands. /// Systems enqueue commands via world.Commands.Enqueue(...). /// public CommandQueue Commands => _commands; /// /// Manually drains the command queue, executing all pending commands. /// Called automatically by after each system /// and after the full tick. /// public void ExecuteCommands() { _commands.ExecuteAll(this); } // ── Queries ────────────────────────────────────────────────────── /// /// Returns a new for constructing queries. /// public QueryBuilder Query() => new(); /// /// Iterates all entities matching the query, providing ref access to /// one component type. /// public void ForEach( QueryDescriptor query, ForEachAction action) where T1 : struct { QueryExecutor.ForEach(_components, query, action); } /// /// Iterates all entities matching the query, providing ref access to /// two component types. /// public void ForEach( QueryDescriptor query, ForEachAction action) where T1 : struct where T2 : struct { QueryExecutor.ForEach(_components, query, action); } /// /// Iterates all entities matching the query, providing ref access to /// three component types. /// public void ForEach( QueryDescriptor query, ForEachAction action) where T1 : struct where T2 : struct where T3 : struct { QueryExecutor.ForEach(_components, query, action); } /// /// Iterates all entities matching the query, providing ref access to /// four component types. /// public void ForEach( QueryDescriptor query, ForEachAction action) where T1 : struct where T2 : struct where T3 : struct where T4 : struct { QueryExecutor.ForEach(_components, query, action); } /// /// Iterates all entities matching the query, providing ref access to /// five component types. /// public void ForEach( QueryDescriptor query, ForEachAction action) where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct { QueryExecutor.ForEach(_components, query, action); } /// /// Iterates all entities matching the query, providing ref access to /// six component types. /// public void ForEach( QueryDescriptor query, ForEachAction action) where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct where T6 : struct { QueryExecutor.ForEach(_components, query, action); } // ── Internal Access ─────────────────────────────────────────────── /// /// The component store. Exposed internally for query execution and /// system infrastructure. /// internal ComponentStore Components => _components; // ── Helpers ─────────────────────────────────────────────────────── private void ThrowIfNotAlive(Entity entity) { if (!_allocator.IsAlive(entity)) throw new InvalidOperationException($"Entity {entity} is not alive."); } // ── Cleanup ─────────────────────────────────────────────────────── public void Dispose() { // Future: dispose R3 subscriptions, etc. } }