namespace OECS;
///
/// A FIFO queue of instances with deferred execution.
///
/// Commands are executed in the order they were enqueued. If a command's
/// throws, the exception is caught and stored;
/// remaining commands continue to execute.
///
/// Commands enqueued during are processed in the
/// same drain cycle — the queue keeps draining until empty.
///
public class CommandQueue
{
private readonly List _commands = new();
private readonly List _errors = new();
///
/// Number of commands currently waiting in the queue.
///
public int Count => _commands.Count;
///
/// Exceptions thrown by commands during calls.
/// Accumulates across drain cycles. Call to reset.
///
public IReadOnlyList Errors => _errors;
///
/// Adds a command to the end of the queue.
/// Uses a constrained generic to avoid boxing struct commands.
///
public void Enqueue(T command) where T : struct, ICommand
{
// Boxing still occurs here because the heterogeneous queue stores
// ICommand references, but the constrained generic on the method
// avoids boxing at the call site.
_commands.Add(command);
}
///
/// Executes all queued commands in FIFO order against the given world.
///
/// Commands run inside a batching scope — structural mutations are
/// deferred and calls are
/// auto-tracked for modification until the drain completes.
/// Pending mutations are flushed after each command, so chained
/// commands see each other's changes within the same drain cycle.
///
/// The queue is fully drained — commands enqueued by other commands during
/// this call are also executed before the method returns.
///
public void ExecuteAll(World world)
{
world.BeginBatching();
int index = 0;
while (index < _commands.Count)
{
var command = _commands[index];
index++;
try
{
command.Execute(world);
}
catch (Exception ex)
{
_errors.Add(ex);
}
// Flush after each command so chained commands see mutations.
world.FlushPendingMutations();
}
_commands.Clear();
world.EndBatching();
}
///
/// Clears all queued commands without executing them.
///
public void Clear()
{
_commands.Clear();
}
///
/// Clears accumulated error history.
///
public void ClearErrors()
{
_errors.Clear();
}
}