public Task <EngineTickResult> Tick(EngineTickInput input) { if (_state != States.Idle) { throw new InvalidOperationException($"RuntimeEngine cannot Tick() while while in state `{_state}`"); } _state = States.Updating; var tick = new Task <EngineTickResult>(() => { // Clear the "RemovedEntities" list from last frame. EntityService.CommitRemoved(); // TODO: This is probably not deterministic. Update order is not guaranteeed for multithreaded bursts // Also this many loops is nasty. foreach (var updateBurst in SystemUpdateScheduler.UpdateBursts) { foreach (var sys in updateBurst.Systems) { foreach (var commandHandler in sys.CommandHandlers) { foreach (var command in input.Commands) { if (commandHandler.CanHandle(command)) { commandHandler.OnCommand(command); } } } } } // Update all systems foreach (var updateBurst in SystemUpdateScheduler.UpdateBursts) { // Lock the entity access gate if this is a multi-threaded burst. AddEntityAccessGate.IsLocked = updateBurst.Systems.Count > 1; // TODO: This still won't be enough to guarantee entity ID consistency. // across different machines, since entity tick is also multithreaded. Parallel.ForEach(updateBurst.Systems, system => { if (system.HasTick) { ((Triggers.IOnTick)system.System).OnTick(); } if (system.HasEntityTick) { Parallel.ForEach(system.Entities, entity => { ((Triggers.IOnEntityTick)system.System).OnTick(entity); }); } }); } // Add any entities from this frame to the system manager if (EntityService.AddedEntities.Count > 0) { SystemManager.AddEntities(EntityService.AddedEntities); } // Process the removal from systems of any entities that were removed in this frame. if (EntityService.RemovedEntities.Count > 0) { SystemManager.RemoveEntities(EntityService.RemovedEntities); } // Gather all the events that were added during this update step var events = EventQueue.DequeueAll(); _state = States.AwaitingSync; return(new EngineTickResult(events)); }); return(tick); }