oecs-sharp/src/OECS/World.cs

194 lines
6.4 KiB
C#
Raw Normal View History

namespace OECS;
/// <summary>
/// The central container for all ECS state.
///
/// Manages entity lifecycle, component storage, and provides the primary
/// API for adding, removing, and querying components.
/// </summary>
public class World : IDisposable
{
private readonly EntityAllocator _allocator;
private readonly ComponentStore _components;
public World()
{
_allocator = new EntityAllocator();
_components = new ComponentStore();
}
// ── Entity Management ────────────────────────────────────────────
/// <summary>
/// Creates a new entity and returns its handle.
/// </summary>
public Entity CreateEntity()
{
return _allocator.Allocate();
}
/// <summary>
/// Destroys an entity, removing all its components and recycling its ID.
/// </summary>
public void DestroyEntity(Entity entity)
{
if (!_allocator.IsAlive(entity))
return;
// Singleton entity is never destroyed.
if (entity.Id == 1)
return;
_components.RemoveAll(entity);
_allocator.Free(entity);
}
/// <summary>
/// Returns true if the entity is currently alive (not destroyed).
/// </summary>
public bool IsAlive(Entity entity)
{
return _allocator.IsAlive(entity);
}
// ── Component Management ─────────────────────────────────────────
/// <summary>
/// Adds a component to the entity. Replaces the existing component if
/// the entity already has one of type <typeparamref name="T"/>.
/// </summary>
public void AddComponent<T>(Entity entity, T component) where T : struct
{
ThrowIfNotAlive(entity);
_components.Add(entity, component);
}
/// <summary>
/// Removes the component of type <typeparamref name="T"/> from the entity.
/// No-op if the entity does not have the component.
/// </summary>
public void RemoveComponent<T>(Entity entity) where T : struct
{
_components.Remove<T>(entity);
}
/// <summary>
/// Returns a reference to the component of type <typeparamref name="T"/>
/// for the given entity. Throws if the entity does not have the component.
/// </summary>
public ref T GetComponent<T>(Entity entity) where T : struct
{
return ref _components.Get<T>(entity);
}
/// <summary>
/// Returns true if the entity has a component of type <typeparamref name="T"/>.
/// </summary>
public bool HasComponent<T>(Entity entity) where T : struct
{
return _components.Has<T>(entity);
}
// ── Queries ──────────────────────────────────────────────────────
/// <summary>
/// Returns a new <see cref="QueryBuilder"/> for constructing queries.
/// </summary>
public QueryBuilder Query() => new();
/// <summary>
/// Iterates all entities matching the query, providing ref access to
/// one component type.
/// </summary>
public void ForEach<T1>(
QueryDescriptor query,
ForEachAction<T1> action)
where T1 : struct
{
QueryExecutor.ForEach(_components, query, action);
}
/// <summary>
/// Iterates all entities matching the query, providing ref access to
/// two component types.
/// </summary>
public void ForEach<T1, T2>(
QueryDescriptor query,
ForEachAction<T1, T2> action)
where T1 : struct where T2 : struct
{
QueryExecutor.ForEach(_components, query, action);
}
/// <summary>
/// Iterates all entities matching the query, providing ref access to
/// three component types.
/// </summary>
public void ForEach<T1, T2, T3>(
QueryDescriptor query,
ForEachAction<T1, T2, T3> action)
where T1 : struct where T2 : struct where T3 : struct
{
QueryExecutor.ForEach(_components, query, action);
}
/// <summary>
/// Iterates all entities matching the query, providing ref access to
/// four component types.
/// </summary>
public void ForEach<T1, T2, T3, T4>(
QueryDescriptor query,
ForEachAction<T1, T2, T3, T4> action)
where T1 : struct where T2 : struct where T3 : struct where T4 : struct
{
QueryExecutor.ForEach(_components, query, action);
}
/// <summary>
/// Iterates all entities matching the query, providing ref access to
/// five component types.
/// </summary>
public void ForEach<T1, T2, T3, T4, T5>(
QueryDescriptor query,
ForEachAction<T1, T2, T3, T4, T5> action)
where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct
{
QueryExecutor.ForEach(_components, query, action);
}
/// <summary>
/// Iterates all entities matching the query, providing ref access to
/// six component types.
/// </summary>
public void ForEach<T1, T2, T3, T4, T5, T6>(
QueryDescriptor query,
ForEachAction<T1, T2, T3, T4, T5, T6> 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 ───────────────────────────────────────────────
/// <summary>
/// The component store. Exposed internally for query execution and
/// system infrastructure.
/// </summary>
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.
}
}