import {MutableSignal, mutableSignal} from "@/utils/mutable-signal"; import { Command, CommandRegistry, CommandResult, CommandRunnerContext, CommandSchema, createCommandRegistry, createCommandRunnerContext, parseCommandSchema, registerCommand } from "@/utils/command"; import type { GameModule } from './game-host'; export interface IGameContext = {} > { get value(): TState; produce(fn: (draft: TState) => void): void; produceAsync(fn: (draft: TState) => void): Promise; run(input: string): Promise>; runParsed(command: Command): Promise>; prompt(schema: CommandSchema | string, validator?: (command: Command) => string | null, currentPlayer?: string | null): Promise; addInterruption(promise: Promise): void; // test only _state: MutableSignal; _commands: CommandRunnerContext>; } export function createGameContext = {} >( commandRegistry: CommandRegistry>, initialState?: TState | (() => TState) ): IGameContext { const stateValue = typeof initialState === 'function' ? initialState() : initialState ?? {} as TState; const state = mutableSignal(stateValue); let commands: CommandRunnerContext> = null as any; const context: IGameContext = { get value(): TState { return state.value; }, produce(fn) { return state.produce(fn); }, produceAsync(fn) { return state.produceAsync(fn); }, run(input: string) { return commands.run(input); }, runParsed(command: Command) { return commands.runParsed(command); }, prompt(schema, validator, currentPlayer) { return commands.prompt(schema, validator, currentPlayer); }, addInterruption(promise) { state.addInterruption(promise); }, _state: state, _commands: commands, }; context._commands = commands = createCommandRunnerContext(commandRegistry, context); return context; } /** * so that we can do `import * as tictactoe from './tic-tac-toe.ts';\n\n createGameContextFromModule(tictactoe);` * @param module */ export function createGameContextFromModule = {} >( module: { registry: CommandRegistry>, createInitialState: () => TState }, ): IGameContext { return createGameContext(module.registry, module.createInitialState); } export function createGameCommandRegistry = {} >() { return createCommandRegistry>(); } export function registerGameCommand = {}, TResult = unknown>( registry: CommandRegistry>, schema: CommandSchema | string, run: (this: IGameContext, ...args: any[]) => Promise ) { registerCommand(registry, { schema: typeof schema === 'string' ? parseCommandSchema(schema) : schema, async run(this: CommandRunnerContext>, command: Command){ const params = command.params; return await run.call(this.context, ...params); }, }); } export { GameHost, createGameHost } from './game-host'; export type { GameHostStatus, GameModule } from './game-host'; export function createGameModule>( module: GameModule ): GameModule { return module; }