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