import '../diagnostics/runtime_diagnostics.dart'; import '../lifecycle/runtime_serial_queue.dart'; import '../lifecycle/runtime_session.dart'; import '../models/game_diff.dart'; import '../models/runtime_event.dart'; import '../scripting/script_engine.dart'; import 'runtime_event_gate.dart'; class RuntimeEventDispatcher { RuntimeEventDispatcher({ required RuntimeSession session, required ScriptEngine scriptEngine, required bool Function(String id) isScopeAlive, bool Function(String id, int epoch)? isNodeEpochAlive, required void Function(GameDiff diff) applyDiff, RuntimeDiagnostics? diagnostics, void Function(Object error)? onError, }) : _scriptEngine = scriptEngine, _applyDiff = applyDiff, _diagnostics = diagnostics, _onError = onError, _gate = RuntimeEventGate( session: session, isScopeAlive: isScopeAlive, isNodeEpochAlive: isNodeEpochAlive, ) { _queue = RuntimeSerialQueue( shouldContinue: () => !_disposed && session.isActive, onItem: _dispatch, ); } final ScriptEngine _scriptEngine; final void Function(GameDiff diff) _applyDiff; final RuntimeDiagnostics? _diagnostics; final void Function(Object error)? _onError; final RuntimeEventGate _gate; late final RuntimeSerialQueue _queue; bool _disposed = false; int get pendingEventCount => _queue.pendingCount; void enqueue(RuntimeEvent event) { if (_disposed || !_gate.session.isActive) { return; } _queue.enqueue(_gate.attachSession(event)); } void dispose() { _disposed = true; _queue.dispose(); } void _dispatch(RuntimeEvent event) { if (!_gate.accepts(event)) { return; } try { final diff = _scriptEngine.dispatchEvent(event); if (_disposed || !_gate.session.isActive || !_gate.accepts(event)) { return; } _applyDiff(diff); } catch (error) { _diagnostics?.record( type: RuntimeDiagnosticType.luaEventError, message: 'Lua event dispatch failed', error: error, context: { 'eventType': event.type, if (event.target != null) 'target': event.target, if (event.handler != null) 'handler': event.handler, }, ); _onError?.call(error); } } }