/** * Player-scoped remote command override/observe system. */ import { noopLogger, type AviationLogger } from '../logger'; import type { RemoteCommand, RemoteCommandEvent } from '../specs/types.nitro'; import type { PlaybackEngine } from '../specs/PlaybackEngine.nitro'; export type RemoteCommandHandler = (event: RemoteCommandEvent) => void; type RemoteCommandEventGuard = () => boolean; interface RemoteCommandHub { wire(engine: PlaybackEngine, isCurrentEvent?: RemoteCommandEventGuard): void; applyPendingOverrides(engine: PlaybackEngine): void; override( command: RemoteCommand, handler: RemoteCommandHandler, engine?: PlaybackEngine ): void; clearOverride(command: RemoteCommand, engine?: PlaybackEngine): void; observe(command: RemoteCommand, callback: RemoteCommandHandler): () => void; reset(): void; } export function createRemoteCommandHub( log: AviationLogger = noopLogger ): RemoteCommandHub { const commandObservers = new Map>(); const commandOverrides = new Map(); return { wire( engine: PlaybackEngine, isCurrentEvent: RemoteCommandEventGuard = () => true ): void { engine.onRemoteCommand((event: RemoteCommandEvent) => { if (!isCurrentEvent()) return; log.verbose('commands', 'remote command received', { command: event.command, }); const override = commandOverrides.get(event.command); if (override) override(event); const observers = commandObservers.get(event.command); if (observers) observers.forEach((cb) => cb(event)); }); }, applyPendingOverrides(engine: PlaybackEngine): void { for (const cmd of commandOverrides.keys()) { engine.overrideRemoteCommand(cmd); } }, override(command, handler, engine): void { log.debug('commands', 'override registered', { command }); commandOverrides.set(command, handler); engine?.overrideRemoteCommand(command); }, clearOverride(command, engine): void { log.debug('commands', 'override cleared', { command }); commandOverrides.delete(command); engine?.clearRemoteCommandOverride(command); }, observe(command, callback): () => void { let observers = commandObservers.get(command); if (!observers) { observers = new Set(); commandObservers.set(command, observers); } observers.add(callback); return () => { observers!.delete(callback); }; }, reset(): void { commandObservers.clear(); commandOverrides.clear(); }, }; }