p2p-lockstep-kit-session 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -153,9 +153,10 @@ declare function buildGameStateSnapshot(state: State, connected?: boolean): Game
153
153
  declare class UINotificationAdapter implements IStateObserver {
154
154
  private stateRef;
155
155
  private uiObserver;
156
+ private getConnected;
156
157
  private lastNotificationTime;
157
158
  private notificationThrottleMs;
158
- constructor(stateRef: State, uiObserver: GameStateObserver);
159
+ constructor(stateRef: State, uiObserver: GameStateObserver, getConnected?: () => boolean);
159
160
  onStateChanged(): void;
160
161
  onHistoryChanged(): void;
161
162
  onGameReset(): void;
@@ -245,9 +245,10 @@ function buildGameStateSnapshot(state, connected = false) {
245
245
  };
246
246
  }
247
247
  var UINotificationAdapter = class {
248
- constructor(stateRef, uiObserver) {
248
+ constructor(stateRef, uiObserver, getConnected = () => false) {
249
249
  __publicField(this, "stateRef", stateRef);
250
250
  __publicField(this, "uiObserver", uiObserver);
251
+ __publicField(this, "getConnected", getConnected);
251
252
  __publicField(this, "lastNotificationTime", 0);
252
253
  __publicField(this, "notificationThrottleMs", 0);
253
254
  }
@@ -255,7 +256,7 @@ var UINotificationAdapter = class {
255
256
  const now = Date.now();
256
257
  if (this.lastNotificationTime + this.notificationThrottleMs > now) return;
257
258
  this.lastNotificationTime = now;
258
- const snapshot = buildGameStateSnapshot(this.stateRef);
259
+ const snapshot = buildGameStateSnapshot(this.stateRef, this.getConnected());
259
260
  this.uiObserver.notifyStateChange(snapshot);
260
261
  }
261
262
  onHistoryChanged() {
@@ -602,11 +603,11 @@ var NetClient = class {
602
603
  if (!message || typeof message !== "object" || !message.type) {
603
604
  return;
604
605
  }
605
- this.bus.emit(
606
- message.type,
607
- message,
608
- "remote"
609
- );
606
+ this.bus.dispatch({
607
+ ...message,
608
+ type: message.type,
609
+ from: "remote"
610
+ });
610
611
  });
611
612
  this.client.onStateChange((state) => {
612
613
  const wasConnected = this.isConnected;
@@ -774,14 +775,19 @@ var getNextStarter = (lastStarter) => {
774
775
  var start = (command) => {
775
776
  const state = getState();
776
777
  if (command.from === "local") {
777
- if (!state.canAction("local", "START")) {
778
+ if (!state.canAction("local", "START") || !state.canAction("remote", "REMOTE_START")) {
778
779
  console.warn("[Start] Cannot START from current state", {
779
- state: state.getState("local")
780
+ localState: state.getState("local"),
781
+ remoteState: state.getState("remote")
780
782
  });
781
783
  return;
782
784
  }
783
785
  const nextStarter = getNextStarter(state.getLastStart());
784
- state.dispatchStart(nextStarter);
786
+ const localTarget2 = nextStarter === "local" ? "turn" : "remote_turn";
787
+ const remoteTarget2 = nextStarter === "local" ? "remote_turn" : "turn";
788
+ state.setLastStart(nextStarter);
789
+ state.dispatch("local", "START", localTarget2);
790
+ state.dispatch("remote", "REMOTE_START", remoteTarget2);
785
791
  send({
786
792
  type: "START",
787
793
  payload: { starter: nextStarter === "local" ? "sender" : "receiver" }
@@ -793,14 +799,19 @@ var start = (command) => {
793
799
  console.warn("[Start] Invalid START message format", { payload: command });
794
800
  return;
795
801
  }
796
- if (!state.canAction("local", "REMOTE_START")) {
802
+ if (!state.canAction("local", "REMOTE_START") || !state.canAction("remote", "START")) {
797
803
  console.warn("[Start] Cannot START from current state", {
798
- state: state.getState("local")
804
+ localState: state.getState("local"),
805
+ remoteState: state.getState("remote")
799
806
  });
800
807
  return;
801
808
  }
802
- const starter = starterInfo === "sender" ? "local" : "remote";
803
- state.dispatchStart(starter);
809
+ const starter = starterInfo === "sender" ? "remote" : "local";
810
+ const localTarget = starter === "local" ? "turn" : "remote_turn";
811
+ const remoteTarget = starter === "local" ? "remote_turn" : "turn";
812
+ state.setLastStart(starter);
813
+ state.dispatch("local", "REMOTE_START", localTarget);
814
+ state.dispatch("remote", "START", remoteTarget);
804
815
  };
805
816
 
806
817
  // session/handlers/move.ts
@@ -1169,14 +1180,18 @@ var createSession = (networkClient, sid) => {
1169
1180
  const state = new State(null, null);
1170
1181
  const observer = new GameStateObserver();
1171
1182
  const net = createNetClient(networkClient, bus, null);
1172
- const uiAdapter = new UINotificationAdapter(state, observer);
1183
+ const uiAdapter = new UINotificationAdapter(
1184
+ state,
1185
+ observer,
1186
+ () => net.getIsConnected()
1187
+ );
1173
1188
  state.subscribeStateObserver(uiAdapter);
1174
1189
  initializeContext(state, bus, net, sid);
1175
1190
  registerHandlers(bus);
1176
1191
  const actions = new LocalActionsAPI(bus);
1177
1192
  net.onConnectionChange((isConnected) => {
1178
- bus.emit(isConnected ? "ONLINE" : "OFFLINE");
1179
1193
  observer.notifyConnectionChange(isConnected);
1194
+ observer.notifyStateChange(buildGameStateSnapshot(state, isConnected));
1180
1195
  });
1181
1196
  return {
1182
1197
  bus,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../session/commandBus.ts","../../session/state/fsm.ts","../../session/observer/index.ts","../../session/state/state.ts","../../session/net.ts","../../session/context.ts","../../session/handlers/ready.ts","../../session/handlers/start.ts","../../session/handlers/move.ts","../../session/handlers/request.ts","../../session/handlers/sync.ts","../../session/handlers/undo.ts","../../session/handlers/restart.ts","../../session/handlers/offLine.ts","../../session/handlers/busRegister.ts","../../session/actions.ts","../../session/index.ts"],"sourcesContent":["import { SessionMessage, SessionMessageType } from '../utils';\n\nexport type CommandOrigin = 'local' | 'remote';\nexport type BusMessageType = SessionMessageType | 'OFFLINE' | 'ONLINE' | 'GAME_OVER';\nexport type BusMessage = Omit<SessionMessage, 'type'> & { type: BusMessageType };\nexport type CommandListener = (message: BusMessage) => Promise<void> | void;\n\ntype HandlerMap = Partial<Record<BusMessageType, CommandListener>>;\n\nexport class CommandBus {\n private handlers: HandlerMap = {};\n private processingQueue: Promise<void> = Promise.resolve();\n\n public emit(type: BusMessageType, payload?: unknown, from: CommandOrigin = 'local'): void {\n this.dispatch({ type, payload, from });\n }\n\n public register(type: BusMessageType, handler: CommandListener): void {\n this.handlers[type] = handler;\n }\n\n public dispatch(message: BusMessage): void {\n this.processingQueue = this.processingQueue.then(async () => {\n const handler = this.handlers[message.type];\n if (handler) {\n try {\n await handler(message);\n } catch (err) {\n console.error(`[CommandBus] Error in ${message.type}:`, err);\n }\n }\n });\n }\n}\n\n","export type SessionState =\n | 'idle'\n | 'ready'\n | 'could_start'\n | 'turn'\n | 'remote_turn'\n | 'approving'\n | 'waiting_approval'\n | 'syncing'\n | 'offline';\n\nexport type SessionEvent =\n | 'REMOTE_READY'\n | 'READY'\n | 'START'\n | 'REMOTE_START'\n | 'MOVE'\n | 'REMOTE_MOVE'\n | 'UNDO'\n | 'REMOTE_UNDO'\n | 'RESTART'\n | 'REMOTE_RESTART'\n | 'APPROVE'\n | 'REJECT'\n | 'GAME_OVER'\n | 'REJOIN'\n | 'SYNC'\n | 'SYNC_COMPLETE'\n | 'OFFLINE'\n | 'ONLINE';\n\nexport type Transition = {\n from: SessionState;\n event: SessionEvent;\n to: SessionState;\n};\n\nconst transitions: Transition[] = [\n // Lobby readiness\n { from: 'idle', event: 'READY', to: 'ready' },\n { from: 'ready', event: 'READY', to: 'idle' },\n { from: 'idle', event: 'REMOTE_READY', to: 'could_start' },\n { from: 'could_start', event: 'REMOTE_READY', to: 'idle' },\n { from: 'ready', event: 'REJECT', to: 'idle' },\n { from: 'could_start', event: 'REJECT', to: 'idle' },\n\n // Match start / turn assignment\n { from: 'ready', event: 'REMOTE_START', to: 'turn' },\n { from: 'ready', event: 'REMOTE_START', to: 'remote_turn' },\n { from: 'could_start', event: 'START', to: 'turn' },\n { from: 'could_start', event: 'START', to: 'remote_turn' },\n\n // Turn swapping after moves\n { from: 'turn', event: 'MOVE', to: 'remote_turn' },\n { from: 'remote_turn', event: 'REMOTE_MOVE', to: 'turn' },\n { from: 'turn', event: 'REJECT', to: 'turn' },\n { from: 'remote_turn', event: 'REJECT', to: 'remote_turn' },\n\n // Requests initiated by local player (undo/restart)\n { from: 'turn', event: 'UNDO', to: 'waiting_approval' },\n { from: 'remote_turn', event: 'UNDO', to: 'waiting_approval' },\n { from: 'turn', event: 'RESTART', to: 'waiting_approval' },\n { from: 'remote_turn', event: 'RESTART', to: 'waiting_approval' },\n\n // Requests coming from remote (we need to approve)\n { from: 'turn', event: 'REMOTE_UNDO', to: 'approving' },\n { from: 'remote_turn', event: 'REMOTE_UNDO', to: 'approving' },\n { from: 'turn', event: 'REMOTE_RESTART', to: 'approving' },\n { from: 'remote_turn', event: 'REMOTE_RESTART', to: 'approving' },\n\n // Approval outcomes when we were waiting\n { from: 'waiting_approval', event: 'APPROVE', to: 'turn' },\n { from: 'waiting_approval', event: 'REJECT', to: 'turn' },\n { from: 'waiting_approval', event: 'REJECT', to: 'remote_turn' },\n\n // Approval outcomes when we were confirming\n { from: 'approving', event: 'APPROVE', to: 'remote_turn' },\n { from: 'approving', event: 'REJECT', to: 'remote_turn' },\n { from: 'approving', event: 'REJECT', to: 'turn' },\n\n // Game end resets back to lobby idle\n { from: 'turn', event: 'GAME_OVER', to: 'idle' },\n { from: 'remote_turn', event: 'GAME_OVER', to: 'idle' },\n\n // Rejoin/sync flows\n { from: 'turn', event: 'SYNC', to: 'syncing' },\n { from: 'remote_turn', event: 'SYNC', to: 'syncing' },\n { from: 'waiting_approval', event: 'SYNC', to: 'syncing' },\n { from: 'approving', event: 'SYNC', to: 'syncing' },\n { from: 'idle', event: 'SYNC', to: 'syncing' },\n { from: 'ready', event: 'SYNC', to: 'syncing' },\n { from: 'could_start', event: 'SYNC', to: 'syncing' },\n { from: 'syncing', event: 'SYNC_COMPLETE', to: 'turn' },\n { from: 'syncing', event: 'SYNC_COMPLETE', to: 'remote_turn' },\n\n // Connection state\n { from: 'idle', event: 'OFFLINE', to: 'offline' },\n { from: 'ready', event: 'OFFLINE', to: 'offline' },\n { from: 'could_start', event: 'OFFLINE', to: 'offline' },\n { from: 'turn', event: 'OFFLINE', to: 'offline' },\n { from: 'remote_turn', event: 'OFFLINE', to: 'offline' },\n { from: 'waiting_approval', event: 'OFFLINE', to: 'offline' },\n { from: 'approving', event: 'OFFLINE', to: 'offline' },\n { from: 'syncing', event: 'OFFLINE', to: 'offline' },\n { from: 'offline', event: 'ONLINE', to: 'syncing' },\n];\n// only receive 'to' from state. never receive from handlers.\nconst nextState = (\n state: SessionState,\n event: SessionEvent,\n to?: SessionState, // next turn or pending action\n): SessionState => {\n if (to) {\n if (\n !!transitions.find(\n (t) => t.from === state && t.event === event && t.to === to,\n )\n ) {\n return to;\n } else {\n return state;\n }\n } else {\n const hit = transitions.find((t) => t.from === state && t.event === event);\n return hit ? hit.to : state;\n }\n};\n\nconst hasNextState = (\n state: SessionState,\n action: SessionEvent,\n to?: SessionState,\n): boolean => {\n if (to) {\n return !!transitions.find(\n (t) => t.from === state && t.event === action && t.to === to,\n );\n }\n return !!transitions.find((t) => t.from === state && t.event === action);\n};\n\nexport class SessionFsm {\n private state: SessionState;\n\n constructor(state: SessionState = 'idle') {\n this.state = state;\n }\n\n public getState(): SessionState {\n return this.state;\n }\n\n public hasNextState(event: SessionEvent, to?: SessionState): boolean {\n return hasNextState(this.state, event, to);\n }\n\n public dispatch(action: SessionEvent, to?: SessionState) {\n this.state = nextState(this.state, action, to);\n }\n}\n","import type { PlayerLabel, TurnEntry, State } from '../state/state';\nimport type { SessionState } from '../state/fsm';\n\nexport interface GameStateSnapshot {\n localState: SessionState;\n remoteState: SessionState;\n turn: number;\n history: TurnEntry[];\n lastStart: PlayerLabel | null;\n pendingAction: 'undo' | 'restart' | null;\n connected: boolean;\n}\n\nexport interface GameEvent {\n type: 'READY' | 'START' | 'MOVE' | 'GAME_OVER' | 'UNDO' | 'RESTART' | 'OFFLINE' | 'ONLINE' | 'SYNC' | 'ERROR';\n payload?: any;\n from?: 'local' | 'remote';\n timestamp?: number;\n}\n\nexport interface IGameObserver {\n onStateChange(snapshot: GameStateSnapshot): void;\n onGameEvent(event: GameEvent): void;\n onConnectionChange?(connected: boolean): void;\n onError?(error: { message: string; context?: any }): void;\n}\n\nexport interface IStateObserver {\n onStateChanged?(): void;\n onHistoryChanged?(): void;\n onGameReset?(): void;\n}\n\nexport interface IGamePlugin {\n validateMove(move: unknown, gameState: GameState): ValidationResult;\n checkWin(gameState: GameState, history: TurnEntry[]): PlayerLabel | null;\n initialize?(): void;\n cleanup?(): void;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n reason?: string;\n}\n\nexport interface GameState {\n history: TurnEntry[];\n localState: 'turn' | 'remote_turn' | string;\n remoteState: 'turn' | 'remote_turn' | string;\n turn: number;\n lastStart: PlayerLabel | null;\n}\n\nexport class DefaultGamePlugin implements IGamePlugin {\n validateMove(): ValidationResult {\n return { valid: true };\n }\n checkWin(): PlayerLabel | null {\n return null;\n }\n}\n\nexport class StateObserverManager {\n private observers: Set<IStateObserver> = new Set();\n\n subscribe(observer: IStateObserver): void {\n this.observers.add(observer);\n }\n\n unsubscribe(observer: IStateObserver): void {\n this.observers.delete(observer);\n }\n\n notifyStateChanged(): void {\n for (const observer of this.observers) {\n try {\n observer.onStateChanged?.();\n } catch (err) {\n console.error('[StateObserver]', err);\n }\n }\n }\n\n notifyHistoryChanged(): void {\n for (const observer of this.observers) {\n try {\n observer.onHistoryChanged?.();\n } catch (err) {\n console.error('[StateObserver]', err);\n }\n }\n }\n\n notifyGameReset(): void {\n for (const observer of this.observers) {\n try {\n observer.onGameReset?.();\n } catch (err) {\n console.error('[StateObserver]', err);\n }\n }\n }\n}\n\nexport class GameStateObserver {\n private observers: Set<IGameObserver> = new Set();\n private currentSnapshot: GameStateSnapshot | null = null;\n\n subscribe(observer: IGameObserver): () => void {\n this.observers.add(observer);\n return () => {\n this.observers.delete(observer);\n };\n }\n\n unsubscribe(observer: IGameObserver): void {\n this.observers.delete(observer);\n }\n\n notifyStateChange(snapshot: GameStateSnapshot): void {\n this.currentSnapshot = snapshot;\n for (const observer of this.observers) {\n try {\n observer.onStateChange(snapshot);\n } catch (err) {\n console.error('[GameStateObserver]', err);\n }\n }\n }\n\n notifyGameEvent(event: GameEvent): void {\n event.timestamp = Date.now();\n for (const observer of this.observers) {\n try {\n observer.onGameEvent(event);\n } catch (err) {\n console.error('[GameStateObserver]', err);\n }\n }\n }\n\n notifyConnectionChange(connected: boolean): void {\n for (const observer of this.observers) {\n try {\n observer.onConnectionChange?.(connected);\n } catch (err) {\n console.error('[GameStateObserver]', err);\n }\n }\n }\n\n notifyError(error: { message: string; context?: any }): void {\n for (const observer of this.observers) {\n try {\n observer.onError?.(error);\n } catch (err) {\n console.error('[GameStateObserver]', err);\n }\n }\n }\n\n getSnapshot(): GameStateSnapshot | null {\n return this.currentSnapshot;\n }\n\n getObserverCount(): number {\n return this.observers.size;\n }\n}\n\nexport function buildGameStateSnapshot(state: State, connected: boolean = false): GameStateSnapshot {\n return {\n localState: state.getState('local'),\n remoteState: state.getState('remote'),\n turn: state.getTurnCount(),\n history: state.getHistory(),\n lastStart: state.getLastStart(),\n pendingAction: state.getPendingAction(),\n connected,\n };\n}\n\nexport class UINotificationAdapter implements IStateObserver {\n private lastNotificationTime = 0;\n private notificationThrottleMs = 0;\n\n constructor(private stateRef: State, private uiObserver: GameStateObserver) {}\n\n onStateChanged(): void {\n const now = Date.now();\n if (this.lastNotificationTime + this.notificationThrottleMs > now) return;\n this.lastNotificationTime = now;\n\n const snapshot = buildGameStateSnapshot(this.stateRef);\n this.uiObserver.notifyStateChange(snapshot);\n }\n\n onHistoryChanged(): void {}\n\n onGameReset(): void {}\n\n emitEvent(event: Omit<GameEvent, 'timestamp'>): void {\n this.uiObserver.notifyGameEvent(event as GameEvent);\n }\n}\n\n","import { SessionEvent, SessionFsm, SessionState } from './fsm';\nimport type {\n IGamePlugin,\n GameState,\n ValidationResult,\n IStateObserver,\n} from '../observer';\nimport { DefaultGamePlugin, StateObserverManager } from '../observer';\n\nexport type TurnEntry = {\n turn: number;\n player: 'local' | 'remote';\n move?: any;\n};\n\nexport type PlayerLabel = 'local' | 'remote';\n\nexport class State {\n // will update map when multi-players (>=3)\n private local = new SessionFsm('idle');\n private remote = new SessionFsm('idle');\n // for compare remote is same people or not\n private readonly localId: string | null = null;\n private remoteId: string | null = null;\n // store all actions\n private readonly history: TurnEntry[] = [];\n // pending some state\n private pendingAction: 'undo' | 'restart' | null = null;\n private pendingUndoCount: 1 | 2 | null = null;\n private resumeTurn: PlayerLabel | null = null;\n private lastStart: PlayerLabel | null = null;\n\n // Game plugin for rule validation and win checking\n private gamePlugin: IGamePlugin = new DefaultGamePlugin();\n\n // Internal state observer for UI notifications\n private stateObserverManager = new StateObserverManager();\n\n constructor(id: string | null, remoteId: string | null) {\n if (id) {\n this.localId = id;\n }\n if (remoteId) {\n this.remoteId = remoteId;\n }\n }\n\n /**\n * Register an internal observer (like plugin pattern)\n * Use this to connect State mutations to UI updates\n */\n public subscribeStateObserver(observer: IStateObserver): void {\n this.stateObserverManager.subscribe(observer);\n }\n\n // ...existing code...\n\n public getId(): string | null {\n return this.localId;\n }\n\n public getremoteId(): string | null {\n return this.remoteId;\n }\n\n public setremoteId(id: string) {\n this.remoteId = id;\n }\n\n public getState(player: PlayerLabel): SessionState {\n return this.getPlayerFsm(player).getState();\n }\n\n public getTurnCount(): number {\n return this.history.length + 1;\n }\n\n public getHistory(): TurnEntry[] {\n return this.history.slice();\n }\n\n public replaceHistory(entries: TurnEntry[]): void {\n this.clearHistory();\n entries.forEach((entry) => {\n this.pushHistory({\n turn: entry.turn,\n player: entry.player,\n move: entry.move,\n });\n });\n }\n\n public clearHistory(): void {\n this.history.splice(0, this.history.length);\n this.stateObserverManager.notifyHistoryChanged();\n }\n\n public pushHistory(entry: TurnEntry): void {\n this.history.push(entry);\n this.stateObserverManager.notifyHistoryChanged();\n }\n\n public popHistory(): TurnEntry | null {\n return this.history.pop() ?? null;\n }\n\n public canAction(\n player: PlayerLabel,\n action: SessionEvent,\n ): boolean {\n return this.getPlayerFsm(player).hasNextState(action);\n }\n\n /**\n * Dispatch an action and automatically determine target state if unique\n * Only use explicit 'to' parameter for ambiguous transitions (APPROVE, REJECT, etc.)\n *\n * For most actions (READY, MOVE, START, etc.), there's only one valid transition,\n * so we automatically find and apply it.\n */\n public dispatch(\n player: PlayerLabel,\n action: SessionEvent,\n to?: SessionState,\n ): void {\n this.getPlayerFsm(player).dispatch(action, to);\n // Notify all state observers after state change\n this.stateObserverManager.notifyStateChanged();\n }\n\n public setPendingAction(action: 'undo' | 'restart' | null) {\n this.pendingAction = action;\n }\n\n public getPendingAction() {\n return this.pendingAction;\n }\n\n public setPendingUndoCount(count: 1 | 2 | null) {\n this.pendingUndoCount = count;\n }\n\n public getPendingUndoCount(): 1 | 2 | null {\n return this.pendingUndoCount;\n }\n\n public setLastStart(player: PlayerLabel | null) {\n this.lastStart = player;\n }\n\n public getLastStart(): PlayerLabel | null {\n return this.lastStart;\n }\n\n public setResumeTurn(player: PlayerLabel | null) {\n this.resumeTurn = player;\n }\n\n public getResumeTurn(): PlayerLabel | null {\n return this.resumeTurn;\n }\n\n private getPlayerFsm(player: PlayerLabel): SessionFsm {\n return player === 'local' ? this.local : this.remote;\n }\n\n // ===== Helper Methods for Undo/Restart Request Handling =====\n\n /**\n * Save game state snapshot for undo/restart operations\n */\n private gameSnapshot: unknown = null;\n\n public saveGameSnapshot(snapshot: unknown): void {\n this.gameSnapshot = snapshot;\n }\n\n public getGameSnapshot(): unknown {\n return this.gameSnapshot;\n }\n\n public clearGameSnapshot(): void {\n this.gameSnapshot = null;\n }\n\n /**\n * Check if there's a pending action (undo/restart)\n */\n public hasPendingAction(): boolean {\n return this.pendingAction !== null;\n }\n\n /**\n * Clear all pending states (called after approval/rejection)\n */\n public clearPendingStates(): void {\n this.pendingAction = null;\n this.pendingUndoCount = null;\n this.resumeTurn = null;\n }\n\n /**\n * Initialize undo request with undo count and current turn holder\n */\n public initializeUndoRequest(undoCount: 1 | 2, resumeTurn: PlayerLabel): void {\n this.pendingAction = 'undo';\n this.pendingUndoCount = undoCount;\n this.resumeTurn = resumeTurn;\n }\n\n /**\n * Initialize restart request with resume turn\n */\n public initializeRestartRequest(resumeTurn: PlayerLabel): void {\n this.pendingAction = 'restart';\n this.resumeTurn = resumeTurn;\n }\n\n /**\n * Check if pending action is undo\n */\n public isPendingUndo(): boolean {\n return this.pendingAction === 'undo';\n }\n\n /**\n * Check if pending action is restart\n */\n public isPendingRestart(): boolean {\n return this.pendingAction === 'restart';\n }\n\n /**\n * Apply undo by popping history N times\n */\n public applyUndo(count: 1 | 2 = 1): void {\n for (let i = 0; i < count; i++) {\n this.popHistory();\n }\n }\n\n /**\n * Reset game state to initial (for restart)\n */\n public resetGame(): void {\n this.clearHistory();\n this.local = new SessionFsm('idle');\n this.remote = new SessionFsm('idle');\n this.lastStart = null;\n this.resumeTurn = null;\n this.stateObserverManager.notifyGameReset();\n }\n\n /**\n * Save start player for rejoin flow\n */\n public recordStartPlayer(player: PlayerLabel): void {\n this.lastStart = player;\n }\n\n /**\n * Get move to undo from history\n */\n public getLastMove(): TurnEntry | null {\n return this.history.length > 0 ? this.history[this.history.length - 1] : null;\n }\n\n // ===== Specialized FSM Dispatch Methods =====\n\n /**\n * Dispatch APPROVE action with automatic target state resolution\n * Multiple valid transitions exist - use state context to determine target\n */\n public dispatchApprove(): void {\n const localState = this.local.getState();\n if (localState === 'waiting_approval') {\n // Local was waiting, always go back to turn\n this.local.dispatch('APPROVE', 'turn');\n this.remote.dispatch('APPROVE', 'turn');\n } else if (localState === 'approving') {\n // Local was confirming, peer takes turn\n this.local.dispatch('APPROVE', 'remote_turn');\n this.remote.dispatch('APPROVE', 'remote_turn');\n }\n }\n\n /**\n * Dispatch REJECT action with automatic target state resolution\n * Multiple valid transitions exist - use resumeTurn to determine who continues\n */\n public dispatchReject(): void {\n const localState = this.local.getState();\n\n if (localState === 'waiting_approval' || localState === 'approving') {\n // resumeTurn tells us who should have turn after rejection\n const targetState = this.resumeTurn === 'local' ? 'turn' : 'remote_turn';\n this.local.dispatch('REJECT', targetState);\n this.remote.dispatch('REJECT', targetState);\n }\n }\n\n /**\n * Dispatch START action with automatic target state resolution\n * Determines who plays first based on starter parameter\n */\n public dispatchStart(firstPlayer: PlayerLabel): void {\n if (firstPlayer === 'local') {\n this.local.dispatch('START', 'turn');\n this.remote.dispatch('START', 'remote_turn');\n this.lastStart = 'local';\n } else {\n this.local.dispatch('START', 'remote_turn');\n this.remote.dispatch('START', 'turn');\n this.lastStart = 'remote';\n }\n }\n\n /**\n * Dispatch SYNC_COMPLETE with automatic target state resolution\n * Based on who should have the turn after sync\n */\n public dispatchSyncComplete(nextPlayer: PlayerLabel): void {\n if (nextPlayer === 'local') {\n this.local.dispatch('SYNC_COMPLETE', 'turn');\n this.remote.dispatch('SYNC_COMPLETE', 'remote_turn');\n } else {\n this.local.dispatch('SYNC_COMPLETE', 'remote_turn');\n this.remote.dispatch('SYNC_COMPLETE', 'turn');\n }\n this.resumeTurn = nextPlayer;\n }\n\n // ===== Game Plugin Integration (Proxy Pattern) =====\n\n /**\n * Set the game plugin for rule validation and win checking\n * @param plugin Implementation of IGamePlugin\n */\n public setGamePlugin(plugin: IGamePlugin): void {\n this.gamePlugin = plugin;\n if (plugin.initialize) {\n plugin.initialize();\n }\n }\n\n /**\n * Get current game plugin\n */\n public getGamePlugin(): IGamePlugin {\n return this.gamePlugin;\n }\n\n /**\n * Validate a move using the game plugin\n * Called by move handler to check if move is legal\n * @param move The move data to validate\n * @returns Validation result with reason if invalid\n */\n public validateMove(move: unknown): ValidationResult {\n const gameState = this.buildGameState();\n return this.gamePlugin.validateMove(move, gameState);\n }\n\n /**\n * Check if game has ended (someone won)\n * Called by move handler after move is applied\n * @returns Winner (local/remote) or null if game continues\n */\n public checkWin(): PlayerLabel | null {\n const gameState = this.buildGameState();\n return this.gamePlugin.checkWin(gameState, this.getHistory());\n }\n\n /**\n * Cleanup when game ends (for plugin to reset internal state)\n */\n public cleanupGame(): void {\n if (this.gamePlugin.cleanup) {\n this.gamePlugin.cleanup();\n }\n }\n\n /**\n * Build game state for plugin\n * @private\n */\n private buildGameState(): GameState {\n return {\n history: this.getHistory(),\n localState: this.getState('local'),\n remoteState: this.getState('remote'),\n turn: this.getTurnCount(),\n lastStart: this.getLastStart(),\n };\n }\n}\n","import type { NetworkClient } from 'p2p-lockstep-kit-network';\nimport type { SessionMessage } from '../utils';\nimport type { BusMessageType, CommandBus } from './commandBus';\n\n/**\n * Network client wrapper that bridges NetworkClient with CommandBus\n * Handles message encoding/decoding and connection state monitoring\n */\nexport class NetClient {\n private localPeerId: string | null;\n private remotePeerId: string | null;\n private isConnected: boolean = false;\n private connectionChangeListener: (isConnected: boolean) => void = () => {};\n private mediaStateListener: (active: boolean) => void = () => {};\n\n public constructor(\n private readonly client: NetworkClient,\n private readonly bus: CommandBus,\n peerId: string | null,\n ) {\n this.localPeerId = peerId ?? null;\n this.remotePeerId = null;\n\n // Forward incoming messages to the command bus\n this.client.onMessage((data) => {\n const message = data as Partial<SessionMessage> & { type?: string };\n if (!message || typeof message !== 'object' || !message.type) {\n return;\n }\n this.bus.emit(\n message.type as BusMessageType,\n message as SessionMessage,\n 'remote',\n );\n });\n\n // Monitor connection state and emit ONLINE/OFFLINE events\n this.client.onStateChange((state) => {\n const wasConnected = this.isConnected;\n this.isConnected = state === 'connected';\n\n // Notify listeners of connection state change\n this.connectionChangeListener(this.isConnected);\n\n // Only emit ONLINE/OFFLINE once when state changes\n if (this.isConnected && !wasConnected) {\n this.bus.emit('ONLINE', undefined, 'local');\n } else if (!this.isConnected && wasConnected) {\n this.bus.emit('OFFLINE', undefined, 'local');\n }\n });\n\n // Monitor remote media stream availability\n this.client.onRemoteStream((stream) => {\n const active =\n !!stream &&\n stream.getTracks().some((track) => track.readyState === 'live');\n this.mediaStateListener(active);\n });\n }\n\n /**\n * Send a message to the remote peer\n * Drops message if not connected and logs warning\n */\n public send(message: SessionMessage) {\n if (!this.isConnected) {\n console.warn(\n '[NetClient] Cannot send message: not connected',\n message.type,\n );\n return;\n }\n\n const enriched: SessionMessage = {\n ...message,\n from: message.from ?? this.localPeerId ?? '',\n };\n this.client.send(JSON.stringify(enriched));\n }\n\n /**\n * Update local and remote peer IDs\n */\n public setPeerIds(ids: { local?: string | null; remote?: string | null }) {\n if (ids.local !== undefined) {\n this.localPeerId = ids.local;\n }\n if (ids.remote !== undefined) {\n this.remotePeerId = ids.remote;\n }\n }\n\n /**\n * Get current peer IDs\n */\n public getPeerIds() {\n return { local: this.localPeerId, remote: this.remotePeerId };\n }\n\n /**\n * Check if currently connected to peer\n */\n public getIsConnected(): boolean {\n return this.isConnected;\n }\n\n /**\n * Monitor connection state changes\n * @param handler Called when connection state changes (true=connected, false=disconnected)\n */\n public onConnectionChange(handler: (isConnected: boolean) => void) {\n this.connectionChangeListener = handler;\n // Immediately call with current state if already have a state\n handler(this.isConnected);\n }\n\n /**\n * Monitor remote media stream state changes\n * @param handler Called when remote media becomes available or unavailable\n */\n public onMediaStateChange(handler: (active: boolean) => void) {\n this.mediaStateListener = handler;\n }\n}\n\nexport const createNetClient = (\n client: NetworkClient,\n bus: CommandBus,\n peerId: string | null,\n) => new NetClient(client, bus, peerId);\n","import type { State } from './state/state';\nimport type { CommandBus } from './commandBus';\nimport type { SessionMessage } from '../utils';\nimport { NetClient } from './net';\n\n/**\n * Global session context holder\n * Stores the current session's dependencies for handler access\n * Used by handlers to retrieve state, bus, and network client\n */\nclass SessionContext {\n private readonly state: State;\n private readonly bus: CommandBus;\n private readonly net: NetClient;\n private readonly sid?: string;\n\n constructor(state: State, bus: CommandBus, net: NetClient, sid?: string) {\n this.state = state;\n this.bus = bus;\n this.net = net;\n this.sid = sid;\n }\n\n getState() {\n return this.state;\n }\n\n getBus() {\n return this.bus;\n }\n\n getNet() {\n return this.net;\n }\n\n getSid() {\n return this.sid;\n }\n}\n\nlet instance: SessionContext | null = null;\n\n/**\n * Initialize the global session context\n * Must be called before handlers use context getters\n * @throws Error if context is accessed before initialization\n */\nexport const initializeContext = (\n state: State,\n bus: CommandBus,\n net: NetClient,\n sid?: string,\n) => {\n instance = new SessionContext(state, bus, net, sid);\n};\n\n/**\n * Reset the session context (for testing)\n * @internal Used by tests to clean up between test cases\n */\nexport const resetContext = () => {\n instance = null;\n};\n\n/**\n * Get the current session context\n * @throws Error if context has not been initialized\n * @internal Use getState(), getBus(), etc. instead\n */\nconst requireContext = () => {\n if (!instance) {\n throw new Error(\n '[SessionContext] Not initialized. Call initializeContext() first.',\n );\n }\n return instance;\n};\n\n/**\n * Get the current game state\n * @throws Error if context has not been initialized\n */\nexport const getState = () => requireContext().getState();\n\n/**\n * Get the command bus\n * @throws Error if context has not been initialized\n */\nexport const getBus = () => requireContext().getBus();\n\n/**\n * Get the network client\n * @throws Error if context has not been initialized\n * @internal Prefer using send() for sending messages\n */\nexport const getNet = () => requireContext().getNet();\n\n/**\n * Get the session ID\n * @throws Error if context has not been initialized\n */\nexport const getSid = () => requireContext().getSid();\n\n/**\n * Send a session message to the remote peer\n * @throws Error if context has not been initialized\n */\nexport const send = (message: SessionMessage) =>\n requireContext().getNet().send(message);\n\n","import type { CommandListener } from '../commandBus';\nimport { getBus, getSid, getState, send } from '../context';\nimport type { SessionMessage } from '../../utils';\n\n/**\n * Handle player ready status notification\n *\n * For most cases, there's only one valid FSM transition:\n * - READY: idle → ready (for initiator)\n * - REMOTE_READY: idle → could_start (for peer)\n *\n * The dispatch() method automatically finds the unique transition.\n */\nexport const ready: CommandListener = (command) => {\n const state = getState();\n const bus = getBus();\n const localSid = getSid();\n\n if (command.from === 'local') {\n // Local player initiating READY\n if (!state.canAction('local', 'READY')) {\n console.warn('[Ready] Cannot dispatch READY from current state', {\n state: state.getState('local'),\n });\n return;\n }\n\n state.dispatch('local', 'READY');\n state.dispatch('remote', 'REMOTE_READY');\n\n const message: SessionMessage = {\n type: 'READY',\n sid: localSid,\n };\n send(message);\n return;\n }\n\n // Remote player sent READY message\n const remoteSid = (command as any).sid;\n\n // Validate session ID\n if (!remoteSid || localSid !== remoteSid) {\n console.warn('[Ready] Session ID mismatch', {\n local: localSid,\n remote: remoteSid,\n });\n bus.emit('REJECT', { reason: 'sid-mismatch' }, 'local');\n return;\n }\n\n // Check if transition is valid\n if (!state.canAction('remote', 'READY')) {\n console.warn('[Ready] Cannot dispatch READY for remote peer', {\n state: state.getState('remote'),\n });\n return;\n }\n\n // Execute state transitions\n state.dispatch('remote', 'READY');\n state.dispatch('local', 'REMOTE_READY');\n};\n","import type { CommandListener } from '../commandBus';\nimport { getState, send } from '../context';\nimport type { PlayerLabel } from '../state/state';\n\n/**\n * Determine next starter (turn order rotation)\n * If no previous starter, randomly pick one\n * Otherwise, alternate between local and remote\n */\nconst getNextStarter = (lastStarter: PlayerLabel | null): PlayerLabel => {\n if (!lastStarter) {\n return Math.random() < 0.5 ? 'local' : 'remote';\n }\n return lastStarter === 'local' ? 'remote' : 'local';\n};\n\n/**\n * Handle game start request\n *\n * Determines who plays first and transitions both FSMs accordingly.\n * Use dispatchStart() for automatic turn assignment based on starter.\n */\nexport const start: CommandListener = (command) => {\n const state = getState();\n\n if (command.from === 'local') {\n // Local player initiating START\n if (!state.canAction('local', 'START')) {\n console.warn('[Start] Cannot START from current state', {\n state: state.getState('local'),\n });\n return;\n }\n\n const nextStarter = getNextStarter(state.getLastStart());\n\n // Use helper method for complex turn assignment\n state.dispatchStart(nextStarter);\n\n // Send message with starter info (encoded as 'sender'/'receiver')\n send({\n type: 'START',\n payload: { starter: nextStarter === 'local' ? 'sender' : 'receiver' },\n });\n return;\n }\n\n // Remote player sent START message\n const starterInfo = (command as any).payload?.starter;\n\n if (!starterInfo) {\n console.warn('[Start] Invalid START message format', { payload: command });\n return;\n }\n\n // Check if transition is valid\n if (!state.canAction('local', 'REMOTE_START')) {\n console.warn('[Start] Cannot START from current state', {\n state: state.getState('local'),\n });\n return;\n }\n\n // Decode starter: if sender started, local player (sender) is the starter\n const starter = starterInfo === 'sender' ? 'local' : 'remote';\n\n // Use helper method for complex turn assignment\n state.dispatchStart(starter);\n};\n","import type { CommandListener } from '../commandBus';\nimport { getBus, getState, send } from '../context';\nimport type { SessionMessage } from '../../utils';\n\n/**\n * Handle player move in game\n *\n * Flow:\n * 1. Validate move using game plugin (rule checking)\n * 2. Dispatch state transitions\n * 3. Record move in history\n * 4. Check win condition using game plugin\n * 5. Send to peer or emit GAME_OVER if won\n *\n * The game plugin is injected via state.setGamePlugin(), allowing\n * different games to provide their own rule validation and win logic.\n */\nexport const move: CommandListener = (command) => {\n const state = getState();\n const bus = getBus();\n const movePayload = command.payload;\n\n if (command.from === 'local') {\n // Local player making a move\n if (!state.canAction('local', 'MOVE')) {\n console.warn('[Move] Cannot MOVE from current state', {\n state: state.getState('local'),\n });\n return;\n }\n\n // ===== PROXY POINT 1: Validate move using game plugin =====\n const validation = state.validateMove(movePayload);\n if (!validation.valid) {\n console.warn('[Move] Move validation failed', {\n reason: validation.reason,\n move: movePayload,\n });\n // Could send REJECT to inform peer, but for now silently return\n return;\n }\n\n // Dispatch state transitions\n state.dispatch('local', 'MOVE');\n state.dispatch('remote', 'REMOTE_MOVE');\n\n // Record move in history\n const turn = state.getTurnCount();\n state.pushHistory({\n turn,\n player: 'local',\n move: movePayload,\n });\n\n // ===== PROXY POINT 2: Check win condition using game plugin =====\n const winner = state.checkWin();\n if (winner) {\n // Game ended - someone won\n console.log('[Move] Game over, winner:', winner);\n\n // Notify bus that game ended (for UI to display winner)\n bus.emit('GAME_OVER', { winner, turn }, 'local');\n\n // Send game over to peer\n send({\n type: 'GAME_OVER',\n payload: { winner, turn },\n });\n\n // Transition both FSMs back to idle (game ready to restart)\n state.dispatch('local', 'GAME_OVER');\n state.dispatch('remote', 'GAME_OVER');\n state.cleanupGame();\n return;\n }\n\n // Send move to peer\n const message: SessionMessage = {\n type: 'MOVE',\n turn,\n payload: movePayload,\n };\n send(message);\n return;\n }\n\n // Remote player made a move\n if (!state.canAction('remote', 'MOVE')) {\n console.warn('[Move] Cannot MOVE for remote player', {\n state: state.getState('remote'),\n });\n return;\n }\n\n // ===== PROXY POINT 1: Validate remote move =====\n const validation = state.validateMove(movePayload);\n if (!validation.valid) {\n console.warn('[Move] Remote move validation failed', {\n reason: validation.reason,\n move: movePayload,\n });\n return;\n }\n\n // Dispatch state transitions\n state.dispatch('remote', 'MOVE');\n state.dispatch('local', 'REMOTE_MOVE');\n\n // Record move in history\n const turn = state.getTurnCount();\n state.pushHistory({\n turn,\n player: 'remote',\n move: movePayload,\n });\n\n // ===== PROXY POINT 2: Check win condition =====\n const winner = state.checkWin();\n if (winner) {\n // Game ended - someone won\n console.log('[Move] Game over, winner:', winner);\n\n // Notify bus that game ended (for UI to display winner)\n bus.emit('GAME_OVER', { winner, turn }, 'local');\n\n // Transition both FSMs back to idle (game ready to restart)\n state.dispatch('local', 'GAME_OVER');\n state.dispatch('remote', 'GAME_OVER');\n state.cleanupGame();\n }\n};\n","import type { CommandListener } from '../commandBus';\nimport { getState, send } from '../context';\n\n/**\n * Handle approval/rejection of pending requests (undo/restart)\n *\n * When local player approves/rejects a pending request:\n * - Apply undo/restart state changes (clear history, pop moves)\n * - Use dispatchApprove/dispatchReject for complex state transitions\n * - Notify peer of decision\n *\n * When remote player approves/rejects local's request:\n * - Similar flow but opposite state transitions\n */\nexport const request: CommandListener = (command) => {\n if (command.type !== 'APPROVE' && command.type !== 'REJECT') {\n return;\n }\n\n const state = getState();\n const action = state.getPendingAction();\n\n // No pending action to respond to\n if (!action) {\n console.warn('[Request] No pending action', { commandType: command.type });\n return;\n }\n\n const payload = command.payload as { action?: string; reason?: string } | undefined;\n\n // Verify payload matches pending action\n if (payload?.action && payload.action !== action) {\n console.warn('[Request] Action mismatch', { pending: action, payload: payload.action });\n return;\n }\n\n if (command.from === 'local') {\n if (command.type === 'APPROVE') {\n // Local player approves the request\n if (!state.canAction('local', 'APPROVE')) {\n console.warn('[Request] Cannot APPROVE from current state');\n return;\n }\n\n // Use special method for complex APPROVE transition\n state.dispatchApprove();\n\n // Apply the approved action (undo or restart)\n if (action === 'undo') {\n state.applyUndo(state.getPendingUndoCount() ?? 1);\n } else if (action === 'restart') {\n state.resetGame();\n }\n\n send({ type: 'APPROVE', payload: { action } });\n state.clearPendingStates();\n return;\n }\n\n // REJECT from local\n if (!state.canAction('local', 'REJECT')) {\n console.warn('[Request] Cannot REJECT from current state');\n return;\n }\n\n // Use special method for complex REJECT transition\n state.dispatchReject();\n\n send({\n type: 'REJECT',\n payload: { action, reason: payload?.reason ?? 'rejected' },\n });\n state.clearPendingStates();\n return;\n }\n\n // Remote player responded to local's request\n if (command.type === 'APPROVE') {\n // Remote player approves\n if (!state.canAction('local', 'APPROVE')) {\n console.warn('[Request] Cannot APPROVE from current state (remote approved)');\n return;\n }\n\n // Use special method for complex APPROVE transition\n state.dispatchApprove();\n\n // Apply the approved action (undo or restart)\n if (action === 'undo') {\n state.applyUndo(state.getPendingUndoCount() ?? 1);\n } else if (action === 'restart') {\n state.resetGame();\n }\n\n state.clearPendingStates();\n return;\n }\n\n // REJECT from remote\n if (!state.canAction('local', 'REJECT')) {\n console.warn('[Request] Cannot REJECT from current state (remote rejected)');\n state.clearPendingStates();\n return;\n }\n\n // Use special method for complex REJECT transition\n state.dispatchReject();\n state.clearPendingStates();\n};\n","import type { CommandListener } from '../commandBus';\nimport { getState, send } from '../context';\nimport type { PlayerLabel } from '../state/state';\n\n/**\n * Handle game state synchronization after disconnect/reconnect\n *\n * SYNC_REQUEST: Initiator sends sync request, responder sends back complete game state\n * SYNC_STATE: Received game state, restore it, and transition both players to correct turn\n *\n * Synced data:\n * - history: All moves in order\n * - lastStart: Who started the last match (for turn rotation)\n * - turn: Current turn holder (to determine resume turn)\n * - resumeTurn: Who should have turn after sync (saved before disconnect)\n *\n * Flow:\n * 1. Local disconnects -> offline handler records resumeTurn\n * 2. Local reconnects -> online handler sends SYNC_REQUEST\n * 3. Remote responds with SYNC_STATE (history, lastStart, turn, resumeTurn)\n * 4. Local receives SYNC_STATE -> restores everything, calls dispatchSyncComplete\n * 5. Both FSMs now in correct 'turn'/'remote_turn' state\n */\nexport const sync: CommandListener = (command) => {\n const state = getState();\n\n if (command.type === 'SYNC_REQUEST') {\n if (command.from === 'local') {\n // Local player initiated sync (after reconnection)\n if (!state.canAction('local', 'SYNC')) {\n console.warn('[Sync] Cannot SYNC from current state');\n return;\n }\n\n // Both transition to syncing state\n state.dispatch('local', 'SYNC', 'syncing');\n state.dispatch('remote', 'SYNC', 'syncing');\n\n // Send request to peer (peer will respond with SYNC_STATE)\n send({ type: 'SYNC_REQUEST', from: '', payload: command.payload });\n return;\n }\n\n // Remote initiated sync - respond with complete game state\n const payload = {\n history: state.getHistory(),\n lastStart: state.getLastStart(),\n turn: state.getState('local') === 'turn' ? 'local' : 'remote',\n resumeTurn: state.getResumeTurn(), // Send back the saved resume turn\n };\n send({ type: 'SYNC_STATE', from: '', payload });\n return;\n }\n\n if (command.type !== 'SYNC_STATE') {\n return;\n }\n\n // Received complete game state from peer\n const payload = (command.payload as {\n history?: Array<{ turn: number; player: 'local' | 'remote'; move?: unknown }>;\n lastStart?: 'local' | 'remote' | null;\n turn?: 'local' | 'remote';\n resumeTurn?: 'local' | 'remote' | null;\n }) || {};\n\n // Restore game history from peer\n if (payload.history && payload.history.length > 0) {\n state.replaceHistory(payload.history);\n } else {\n state.clearHistory();\n }\n\n // Restore match start player (for turn rotation in next match)\n if (payload.lastStart) {\n state.setLastStart(payload.lastStart);\n } else {\n state.setLastStart(null);\n }\n\n // Determine who should have turn after sync\n // Priority: 1) resumeTurn from peer (what they saved on disconnect)\n // 2) turn from payload (whose turn it actually was)\n let nextPlayer: PlayerLabel;\n\n if (payload.resumeTurn) {\n // Use the saved resume turn if available\n nextPlayer = payload.resumeTurn;\n } else if (payload.turn) {\n // Otherwise use current turn from peer\n nextPlayer = payload.turn === 'local' ? 'local' : 'remote';\n } else {\n // Fallback to current state\n nextPlayer = state.getState('local') === 'turn' ? 'local' : 'remote';\n }\n\n console.log('[Sync] Restored game state', {\n historyLength: state.getHistory().length,\n lastStart: state.getLastStart(),\n nextTurnPlayer: nextPlayer,\n });\n\n // Use special method for complex SYNC_COMPLETE transition\n // This sets both local and remote FSM to correct 'turn'/'remote_turn' state\n state.dispatchSyncComplete(nextPlayer);\n};\n","import type { CommandListener } from '../commandBus';\nimport { getState, send } from '../context';\n\n/**\n * Handle undo request from local or remote player\n *\n * Validates undo is legal (enough history, no pending action, valid state)\n * and initiates undo request with peer approval flow.\n *\n * Determines undo count (1 or 2 moves) based on current turn holder.\n * Records pending undo state for approval/rejection handling by request handler.\n */\nexport const undo: CommandListener = (command) => {\n if (command.type !== 'UNDO') {\n return;\n }\n\n const state = getState();\n\n if (command.from === 'local') {\n // Validate no pending action\n if (state.hasPendingAction()) {\n return;\n }\n\n // Validate state transitions\n if (!state.canAction('local', 'UNDO')) {\n console.warn('[Undo] Cannot UNDO from current state');\n return;\n }\n\n // Determine undo count based on whose turn it is\n const localState = state.getState('local');\n const undoCount = localState === 'turn' ? 1 : 2;\n\n // Validate history is long enough\n if (state.getHistory().length < undoCount) {\n console.warn('[Undo] Not enough history to undo', { count: undoCount });\n return;\n }\n\n // Initialize pending undo state\n state.initializeUndoRequest(undoCount as 1 | 2, 'local');\n\n // Transition to approval waiting state\n state.dispatch('local', 'UNDO');\n state.dispatch('remote', 'REMOTE_UNDO');\n\n send({ type: 'UNDO', payload: { count: undoCount } });\n return;\n }\n\n // Remote player requested undo\n if (state.hasPendingAction()) {\n send({ type: 'REJECT', payload: { action: 'undo', reason: 'busy' } });\n return;\n }\n\n // Validate state transitions\n if (!state.canAction('local', 'REMOTE_UNDO')) {\n console.warn('[Undo] Cannot accept remote UNDO request');\n send({ type: 'REJECT', payload: { action: 'undo', reason: 'invalid_state' } });\n return;\n }\n\n // Extract undo count from payload\n const payload = command.payload as { count?: number } | undefined;\n const count = payload?.count === 2 ? 2 : 1;\n\n // Validate count value\n if (payload?.count && payload.count !== 1 && payload.count !== 2) {\n send({ type: 'REJECT', payload: { action: 'undo', reason: 'invalid' } });\n return;\n }\n\n // Validate history is long enough\n if (count === 2 && state.getHistory().length < 2) {\n send({ type: 'REJECT', payload: { action: 'undo', reason: 'no_history' } });\n return;\n }\n\n // Determine who will resume after undo\n const resumePlayer = state.getState('local') === 'turn' ? 'local' : 'remote';\n\n // Initialize pending undo state\n state.initializeUndoRequest(count as 1 | 2, resumePlayer);\n\n // Transition to approval waiting state (remote initiated, local approving)\n state.dispatch('local', 'REMOTE_UNDO');\n state.dispatch('remote', 'UNDO');\n};\n","import type { CommandListener } from '../commandBus';\nimport { getState, send } from '../context';\n\n/**\n * Handle restart request from local or remote player\n *\n * Validates restart is legal (no pending action, valid state)\n * and initiates restart request with peer approval flow.\n *\n * Restart clears all history but keeps player order for next match.\n */\nexport const restart: CommandListener = (command) => {\n if (command.type !== 'RESTART') {\n return;\n }\n\n const state = getState();\n\n if (command.from === 'local') {\n // Validate no pending action\n if (state.hasPendingAction()) {\n return;\n }\n\n // Validate state transitions\n if (!state.canAction('local', 'RESTART')) {\n console.warn('[Restart] Cannot RESTART from current state');\n return;\n }\n\n // Determine who will go first in next match\n const resumePlayer = state.getState('local') === 'turn' ? 'local' : 'remote';\n\n // Initialize pending restart state\n state.initializeRestartRequest(resumePlayer);\n\n // Transition to approval waiting state\n state.dispatch('local', 'RESTART');\n state.dispatch('remote', 'REMOTE_RESTART');\n\n send({ type: 'RESTART' });\n return;\n }\n\n // Remote player requested restart\n if (state.hasPendingAction()) {\n send({ type: 'REJECT', payload: { action: 'restart', reason: 'busy' } });\n return;\n }\n\n // Validate state transitions\n if (!state.canAction('local', 'REMOTE_RESTART')) {\n console.warn('[Restart] Cannot accept remote RESTART request');\n send({ type: 'REJECT', payload: { action: 'restart', reason: 'invalid_state' } });\n return;\n }\n\n // Determine who will go first in next match\n const resumePlayer = state.getState('local') === 'turn' ? 'local' : 'remote';\n\n // Initialize pending restart state\n state.initializeRestartRequest(resumePlayer);\n\n // Transition to approval waiting state (remote initiated, local approving)\n state.dispatch('local', 'REMOTE_RESTART');\n state.dispatch('remote', 'RESTART');\n};\n","import type { CommandListener } from '../commandBus';\nimport { getBus, getState } from '../context';\n\n/**\n * Handle connection state changes (OFFLINE/ONLINE)\n *\n * OFFLINE: Record current turn state before disconnecting\n * ONLINE: Trigger sync to restore game state after reconnect\n *\n * Flow:\n * 1. OFFLINE arrives -> save resumeTurn, transition to offline\n * 2. ONLINE arrives -> transition to syncing, emit SYNC_REQUEST\n * 3. sync handler takes over -> restore history and turn assignment\n */\nexport const offline: CommandListener = (command) => {\n if (command.type !== 'OFFLINE' && command.type !== 'ONLINE') {\n return;\n }\n\n const state = getState();\n const bus = getBus();\n\n if (command.type === 'OFFLINE') {\n // Peer disconnected - save current turn state for later recovery\n if (!state.canAction('remote', 'OFFLINE')) {\n console.warn('[Offline] Cannot transition to OFFLINE from current state');\n return;\n }\n\n // Record who had the turn before going offline\n const currentTurn = state.getState('local') === 'turn' ? 'local' : 'remote';\n state.setResumeTurn(currentTurn);\n\n // Transition to offline state\n state.dispatch('remote', 'OFFLINE', 'offline');\n return;\n }\n\n // ONLINE - peer reconnected\n if (!state.canAction('remote', 'ONLINE')) {\n console.warn('[Offline] Cannot transition to ONLINE from current state');\n return;\n }\n\n // Transition to syncing state\n state.dispatch('remote', 'ONLINE', 'syncing');\n\n // Trigger sync to restore game state\n // sync handler will:\n // 1. Send SYNC_REQUEST\n // 2. Receive SYNC_STATE from peer\n // 3. Restore history and correct turn assignment\n bus.emit('SYNC_REQUEST', undefined, 'local');\n};\n","import { CommandBus } from '../commandBus';\nimport { ready } from './ready';\nimport { start } from './start';\nimport { move } from './move';\nimport { request } from './request';\nimport { sync } from './sync';\nimport { undo } from './undo';\nimport { restart } from './restart';\nimport { offline } from './offLine';\n\nexport const registerHandlers = (bus: CommandBus) => {\n bus.register('READY', ready);\n bus.register('START', start);\n bus.register('MOVE', move);\n bus.register('UNDO', undo);\n bus.register('RESTART', restart);\n bus.register('SYNC_REQUEST', sync);\n bus.register('SYNC_STATE', sync);\n bus.register('OFFLINE', offline);\n bus.register('ONLINE', offline);\n bus.register('APPROVE', request);\n bus.register('REJECT', request);\n};\n\n\n","import type { CommandBus } from './commandBus';\n\nexport interface ISessionActions {\n ready(): void;\n start(): void;\n move(data: unknown): void;\n undo(): void;\n restart(): void;\n approve(): void;\n reject(): void;\n rejoin(sid: string): void;\n}\n\nexport class LocalActionsAPI implements ISessionActions {\n constructor(private bus: CommandBus) {}\n\n ready(): void {\n this.bus.emit('READY');\n }\n\n start(): void {\n this.bus.emit('START');\n }\n\n move(data: unknown): void {\n this.bus.emit('MOVE', data);\n }\n\n undo(): void {\n this.bus.emit('UNDO');\n }\n\n restart(): void {\n this.bus.emit('RESTART');\n }\n\n approve(): void {\n this.bus.emit('APPROVE');\n }\n\n reject(): void {\n this.bus.emit('REJECT');\n }\n\n rejoin(sid: string): void {\n this.bus.emit('REJOIN', { sid });\n }\n}\n\n","import { NetworkClient } from 'p2p-lockstep-kit-network';\nimport { CommandBus } from './commandBus';\nimport { State } from './state/state';\nimport { createNetClient } from './net';\nimport { initializeContext } from './context';\nimport { registerHandlers } from './handlers/busRegister.ts';\nimport { GameStateObserver, UINotificationAdapter } from './observer';\nimport { LocalActionsAPI } from './actions';\n\n/**\n * Create a new game session with state management and networking\n * @param sid Session ID for rejoining (optional)\n * @param networkClient Custom network client (optional, creates default if not provided)\n * @returns Session manager with bus, state, observer, net, and send method\n *\n * @example\n * const session = createSession();\n * // UI automatically updates when state changes - no manual observer calls needed!\n * session.observer.subscribe(myUIObserver);\n * session.bus.emit('READY', undefined, 'local');\n * await session.net.connect(remotePeerId);\n */\nexport const createSession = (networkClient: NetworkClient, sid?: string) => {\n const bus = new CommandBus();\n const state = new State(null, null);\n const observer = new GameStateObserver();\n const net = createNetClient(networkClient, bus, null);\n\n // Connect State mutations to UI updates via adapter (plugin pattern)\n // This is the ONLY place where UI notifications are triggered\n const uiAdapter = new UINotificationAdapter(state, observer);\n state.subscribeStateObserver(uiAdapter);\n\n initializeContext(state, bus, net, sid);\n registerHandlers(bus);\n\n const actions = new LocalActionsAPI(bus);\n\n net.onConnectionChange((isConnected) => {\n bus.emit(isConnected ? 'ONLINE' : 'OFFLINE');\n observer.notifyConnectionChange(isConnected);\n });\n\n return {\n bus,\n state,\n observer,\n net,\n actions,\n send: net.send.bind(net),\n };\n};\n\nexport * from './observer';\nexport type { ISessionActions } from './actions';\n"],"mappings":"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","names":["move","validation","turn","winner","payload","resumePlayer"]}
1
+ {"version":3,"sources":["../../session/commandBus.ts","../../session/state/fsm.ts","../../session/observer/index.ts","../../session/state/state.ts","../../session/net.ts","../../session/context.ts","../../session/handlers/ready.ts","../../session/handlers/start.ts","../../session/handlers/move.ts","../../session/handlers/request.ts","../../session/handlers/sync.ts","../../session/handlers/undo.ts","../../session/handlers/restart.ts","../../session/handlers/offLine.ts","../../session/handlers/busRegister.ts","../../session/actions.ts","../../session/index.ts"],"sourcesContent":["import { SessionMessage, SessionMessageType } from '../utils';\n\nexport type CommandOrigin = 'local' | 'remote';\nexport type BusMessageType = SessionMessageType | 'OFFLINE' | 'ONLINE' | 'GAME_OVER';\nexport type BusMessage = Omit<SessionMessage, 'type'> & { type: BusMessageType };\nexport type CommandListener = (message: BusMessage) => Promise<void> | void;\n\ntype HandlerMap = Partial<Record<BusMessageType, CommandListener>>;\n\nexport class CommandBus {\n private handlers: HandlerMap = {};\n private processingQueue: Promise<void> = Promise.resolve();\n\n public emit(type: BusMessageType, payload?: unknown, from: CommandOrigin = 'local'): void {\n this.dispatch({ type, payload, from });\n }\n\n public register(type: BusMessageType, handler: CommandListener): void {\n this.handlers[type] = handler;\n }\n\n public dispatch(message: BusMessage): void {\n this.processingQueue = this.processingQueue.then(async () => {\n const handler = this.handlers[message.type];\n if (handler) {\n try {\n await handler(message);\n } catch (err) {\n console.error(`[CommandBus] Error in ${message.type}:`, err);\n }\n }\n });\n }\n}\n\n","export type SessionState =\n | 'idle'\n | 'ready'\n | 'could_start'\n | 'turn'\n | 'remote_turn'\n | 'approving'\n | 'waiting_approval'\n | 'syncing'\n | 'offline';\n\nexport type SessionEvent =\n | 'REMOTE_READY'\n | 'READY'\n | 'START'\n | 'REMOTE_START'\n | 'MOVE'\n | 'REMOTE_MOVE'\n | 'UNDO'\n | 'REMOTE_UNDO'\n | 'RESTART'\n | 'REMOTE_RESTART'\n | 'APPROVE'\n | 'REJECT'\n | 'GAME_OVER'\n | 'REJOIN'\n | 'SYNC'\n | 'SYNC_COMPLETE'\n | 'OFFLINE'\n | 'ONLINE';\n\nexport type Transition = {\n from: SessionState;\n event: SessionEvent;\n to: SessionState;\n};\n\nconst transitions: Transition[] = [\n // Lobby readiness\n { from: 'idle', event: 'READY', to: 'ready' },\n { from: 'ready', event: 'READY', to: 'idle' },\n { from: 'idle', event: 'REMOTE_READY', to: 'could_start' },\n { from: 'could_start', event: 'REMOTE_READY', to: 'idle' },\n { from: 'ready', event: 'REJECT', to: 'idle' },\n { from: 'could_start', event: 'REJECT', to: 'idle' },\n\n // Match start / turn assignment\n { from: 'ready', event: 'REMOTE_START', to: 'turn' },\n { from: 'ready', event: 'REMOTE_START', to: 'remote_turn' },\n { from: 'could_start', event: 'START', to: 'turn' },\n { from: 'could_start', event: 'START', to: 'remote_turn' },\n\n // Turn swapping after moves\n { from: 'turn', event: 'MOVE', to: 'remote_turn' },\n { from: 'remote_turn', event: 'REMOTE_MOVE', to: 'turn' },\n { from: 'turn', event: 'REJECT', to: 'turn' },\n { from: 'remote_turn', event: 'REJECT', to: 'remote_turn' },\n\n // Requests initiated by local player (undo/restart)\n { from: 'turn', event: 'UNDO', to: 'waiting_approval' },\n { from: 'remote_turn', event: 'UNDO', to: 'waiting_approval' },\n { from: 'turn', event: 'RESTART', to: 'waiting_approval' },\n { from: 'remote_turn', event: 'RESTART', to: 'waiting_approval' },\n\n // Requests coming from remote (we need to approve)\n { from: 'turn', event: 'REMOTE_UNDO', to: 'approving' },\n { from: 'remote_turn', event: 'REMOTE_UNDO', to: 'approving' },\n { from: 'turn', event: 'REMOTE_RESTART', to: 'approving' },\n { from: 'remote_turn', event: 'REMOTE_RESTART', to: 'approving' },\n\n // Approval outcomes when we were waiting\n { from: 'waiting_approval', event: 'APPROVE', to: 'turn' },\n { from: 'waiting_approval', event: 'REJECT', to: 'turn' },\n { from: 'waiting_approval', event: 'REJECT', to: 'remote_turn' },\n\n // Approval outcomes when we were confirming\n { from: 'approving', event: 'APPROVE', to: 'remote_turn' },\n { from: 'approving', event: 'REJECT', to: 'remote_turn' },\n { from: 'approving', event: 'REJECT', to: 'turn' },\n\n // Game end resets back to lobby idle\n { from: 'turn', event: 'GAME_OVER', to: 'idle' },\n { from: 'remote_turn', event: 'GAME_OVER', to: 'idle' },\n\n // Rejoin/sync flows\n { from: 'turn', event: 'SYNC', to: 'syncing' },\n { from: 'remote_turn', event: 'SYNC', to: 'syncing' },\n { from: 'waiting_approval', event: 'SYNC', to: 'syncing' },\n { from: 'approving', event: 'SYNC', to: 'syncing' },\n { from: 'idle', event: 'SYNC', to: 'syncing' },\n { from: 'ready', event: 'SYNC', to: 'syncing' },\n { from: 'could_start', event: 'SYNC', to: 'syncing' },\n { from: 'syncing', event: 'SYNC_COMPLETE', to: 'turn' },\n { from: 'syncing', event: 'SYNC_COMPLETE', to: 'remote_turn' },\n\n // Connection state\n { from: 'idle', event: 'OFFLINE', to: 'offline' },\n { from: 'ready', event: 'OFFLINE', to: 'offline' },\n { from: 'could_start', event: 'OFFLINE', to: 'offline' },\n { from: 'turn', event: 'OFFLINE', to: 'offline' },\n { from: 'remote_turn', event: 'OFFLINE', to: 'offline' },\n { from: 'waiting_approval', event: 'OFFLINE', to: 'offline' },\n { from: 'approving', event: 'OFFLINE', to: 'offline' },\n { from: 'syncing', event: 'OFFLINE', to: 'offline' },\n { from: 'offline', event: 'ONLINE', to: 'syncing' },\n];\n// only receive 'to' from state. never receive from handlers.\nconst nextState = (\n state: SessionState,\n event: SessionEvent,\n to?: SessionState, // next turn or pending action\n): SessionState => {\n if (to) {\n if (\n !!transitions.find(\n (t) => t.from === state && t.event === event && t.to === to,\n )\n ) {\n return to;\n } else {\n return state;\n }\n } else {\n const hit = transitions.find((t) => t.from === state && t.event === event);\n return hit ? hit.to : state;\n }\n};\n\nconst hasNextState = (\n state: SessionState,\n action: SessionEvent,\n to?: SessionState,\n): boolean => {\n if (to) {\n return !!transitions.find(\n (t) => t.from === state && t.event === action && t.to === to,\n );\n }\n return !!transitions.find((t) => t.from === state && t.event === action);\n};\n\nexport class SessionFsm {\n private state: SessionState;\n\n constructor(state: SessionState = 'idle') {\n this.state = state;\n }\n\n public getState(): SessionState {\n return this.state;\n }\n\n public hasNextState(event: SessionEvent, to?: SessionState): boolean {\n return hasNextState(this.state, event, to);\n }\n\n public dispatch(action: SessionEvent, to?: SessionState) {\n this.state = nextState(this.state, action, to);\n }\n}\n","import type { PlayerLabel, TurnEntry, State } from '../state/state';\nimport type { SessionState } from '../state/fsm';\n\nexport interface GameStateSnapshot {\n localState: SessionState;\n remoteState: SessionState;\n turn: number;\n history: TurnEntry[];\n lastStart: PlayerLabel | null;\n pendingAction: 'undo' | 'restart' | null;\n connected: boolean;\n}\n\nexport interface GameEvent {\n type: 'READY' | 'START' | 'MOVE' | 'GAME_OVER' | 'UNDO' | 'RESTART' | 'OFFLINE' | 'ONLINE' | 'SYNC' | 'ERROR';\n payload?: any;\n from?: 'local' | 'remote';\n timestamp?: number;\n}\n\nexport interface IGameObserver {\n onStateChange(snapshot: GameStateSnapshot): void;\n onGameEvent(event: GameEvent): void;\n onConnectionChange?(connected: boolean): void;\n onError?(error: { message: string; context?: any }): void;\n}\n\nexport interface IStateObserver {\n onStateChanged?(): void;\n onHistoryChanged?(): void;\n onGameReset?(): void;\n}\n\nexport interface IGamePlugin {\n validateMove(move: unknown, gameState: GameState): ValidationResult;\n checkWin(gameState: GameState, history: TurnEntry[]): PlayerLabel | null;\n initialize?(): void;\n cleanup?(): void;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n reason?: string;\n}\n\nexport interface GameState {\n history: TurnEntry[];\n localState: 'turn' | 'remote_turn' | string;\n remoteState: 'turn' | 'remote_turn' | string;\n turn: number;\n lastStart: PlayerLabel | null;\n}\n\nexport class DefaultGamePlugin implements IGamePlugin {\n validateMove(): ValidationResult {\n return { valid: true };\n }\n checkWin(): PlayerLabel | null {\n return null;\n }\n}\n\nexport class StateObserverManager {\n private observers: Set<IStateObserver> = new Set();\n\n subscribe(observer: IStateObserver): void {\n this.observers.add(observer);\n }\n\n unsubscribe(observer: IStateObserver): void {\n this.observers.delete(observer);\n }\n\n notifyStateChanged(): void {\n for (const observer of this.observers) {\n try {\n observer.onStateChanged?.();\n } catch (err) {\n console.error('[StateObserver]', err);\n }\n }\n }\n\n notifyHistoryChanged(): void {\n for (const observer of this.observers) {\n try {\n observer.onHistoryChanged?.();\n } catch (err) {\n console.error('[StateObserver]', err);\n }\n }\n }\n\n notifyGameReset(): void {\n for (const observer of this.observers) {\n try {\n observer.onGameReset?.();\n } catch (err) {\n console.error('[StateObserver]', err);\n }\n }\n }\n}\n\nexport class GameStateObserver {\n private observers: Set<IGameObserver> = new Set();\n private currentSnapshot: GameStateSnapshot | null = null;\n\n subscribe(observer: IGameObserver): () => void {\n this.observers.add(observer);\n return () => {\n this.observers.delete(observer);\n };\n }\n\n unsubscribe(observer: IGameObserver): void {\n this.observers.delete(observer);\n }\n\n notifyStateChange(snapshot: GameStateSnapshot): void {\n this.currentSnapshot = snapshot;\n for (const observer of this.observers) {\n try {\n observer.onStateChange(snapshot);\n } catch (err) {\n console.error('[GameStateObserver]', err);\n }\n }\n }\n\n notifyGameEvent(event: GameEvent): void {\n event.timestamp = Date.now();\n for (const observer of this.observers) {\n try {\n observer.onGameEvent(event);\n } catch (err) {\n console.error('[GameStateObserver]', err);\n }\n }\n }\n\n notifyConnectionChange(connected: boolean): void {\n for (const observer of this.observers) {\n try {\n observer.onConnectionChange?.(connected);\n } catch (err) {\n console.error('[GameStateObserver]', err);\n }\n }\n }\n\n notifyError(error: { message: string; context?: any }): void {\n for (const observer of this.observers) {\n try {\n observer.onError?.(error);\n } catch (err) {\n console.error('[GameStateObserver]', err);\n }\n }\n }\n\n getSnapshot(): GameStateSnapshot | null {\n return this.currentSnapshot;\n }\n\n getObserverCount(): number {\n return this.observers.size;\n }\n}\n\nexport function buildGameStateSnapshot(state: State, connected: boolean = false): GameStateSnapshot {\n return {\n localState: state.getState('local'),\n remoteState: state.getState('remote'),\n turn: state.getTurnCount(),\n history: state.getHistory(),\n lastStart: state.getLastStart(),\n pendingAction: state.getPendingAction(),\n connected,\n };\n}\n\nexport class UINotificationAdapter implements IStateObserver {\n private lastNotificationTime = 0;\n private notificationThrottleMs = 0;\n\n constructor(\n private stateRef: State,\n private uiObserver: GameStateObserver,\n private getConnected: () => boolean = () => false,\n ) {}\n\n onStateChanged(): void {\n const now = Date.now();\n if (this.lastNotificationTime + this.notificationThrottleMs > now) return;\n this.lastNotificationTime = now;\n\n const snapshot = buildGameStateSnapshot(this.stateRef, this.getConnected());\n this.uiObserver.notifyStateChange(snapshot);\n }\n\n onHistoryChanged(): void {}\n\n onGameReset(): void {}\n\n emitEvent(event: Omit<GameEvent, 'timestamp'>): void {\n this.uiObserver.notifyGameEvent(event as GameEvent);\n }\n}\n","import { SessionEvent, SessionFsm, SessionState } from './fsm';\nimport type {\n IGamePlugin,\n GameState,\n ValidationResult,\n IStateObserver,\n} from '../observer';\nimport { DefaultGamePlugin, StateObserverManager } from '../observer';\n\nexport type TurnEntry = {\n turn: number;\n player: 'local' | 'remote';\n move?: any;\n};\n\nexport type PlayerLabel = 'local' | 'remote';\n\nexport class State {\n // will update map when multi-players (>=3)\n private local = new SessionFsm('idle');\n private remote = new SessionFsm('idle');\n // for compare remote is same people or not\n private readonly localId: string | null = null;\n private remoteId: string | null = null;\n // store all actions\n private readonly history: TurnEntry[] = [];\n // pending some state\n private pendingAction: 'undo' | 'restart' | null = null;\n private pendingUndoCount: 1 | 2 | null = null;\n private resumeTurn: PlayerLabel | null = null;\n private lastStart: PlayerLabel | null = null;\n\n // Game plugin for rule validation and win checking\n private gamePlugin: IGamePlugin = new DefaultGamePlugin();\n\n // Internal state observer for UI notifications\n private stateObserverManager = new StateObserverManager();\n\n constructor(id: string | null, remoteId: string | null) {\n if (id) {\n this.localId = id;\n }\n if (remoteId) {\n this.remoteId = remoteId;\n }\n }\n\n /**\n * Register an internal observer (like plugin pattern)\n * Use this to connect State mutations to UI updates\n */\n public subscribeStateObserver(observer: IStateObserver): void {\n this.stateObserverManager.subscribe(observer);\n }\n\n // ...existing code...\n\n public getId(): string | null {\n return this.localId;\n }\n\n public getremoteId(): string | null {\n return this.remoteId;\n }\n\n public setremoteId(id: string) {\n this.remoteId = id;\n }\n\n public getState(player: PlayerLabel): SessionState {\n return this.getPlayerFsm(player).getState();\n }\n\n public getTurnCount(): number {\n return this.history.length + 1;\n }\n\n public getHistory(): TurnEntry[] {\n return this.history.slice();\n }\n\n public replaceHistory(entries: TurnEntry[]): void {\n this.clearHistory();\n entries.forEach((entry) => {\n this.pushHistory({\n turn: entry.turn,\n player: entry.player,\n move: entry.move,\n });\n });\n }\n\n public clearHistory(): void {\n this.history.splice(0, this.history.length);\n this.stateObserverManager.notifyHistoryChanged();\n }\n\n public pushHistory(entry: TurnEntry): void {\n this.history.push(entry);\n this.stateObserverManager.notifyHistoryChanged();\n }\n\n public popHistory(): TurnEntry | null {\n return this.history.pop() ?? null;\n }\n\n public canAction(\n player: PlayerLabel,\n action: SessionEvent,\n ): boolean {\n return this.getPlayerFsm(player).hasNextState(action);\n }\n\n /**\n * Dispatch an action and automatically determine target state if unique\n * Only use explicit 'to' parameter for ambiguous transitions (APPROVE, REJECT, etc.)\n *\n * For most actions (READY, MOVE, START, etc.), there's only one valid transition,\n * so we automatically find and apply it.\n */\n public dispatch(\n player: PlayerLabel,\n action: SessionEvent,\n to?: SessionState,\n ): void {\n this.getPlayerFsm(player).dispatch(action, to);\n // Notify all state observers after state change\n this.stateObserverManager.notifyStateChanged();\n }\n\n public setPendingAction(action: 'undo' | 'restart' | null) {\n this.pendingAction = action;\n }\n\n public getPendingAction() {\n return this.pendingAction;\n }\n\n public setPendingUndoCount(count: 1 | 2 | null) {\n this.pendingUndoCount = count;\n }\n\n public getPendingUndoCount(): 1 | 2 | null {\n return this.pendingUndoCount;\n }\n\n public setLastStart(player: PlayerLabel | null) {\n this.lastStart = player;\n }\n\n public getLastStart(): PlayerLabel | null {\n return this.lastStart;\n }\n\n public setResumeTurn(player: PlayerLabel | null) {\n this.resumeTurn = player;\n }\n\n public getResumeTurn(): PlayerLabel | null {\n return this.resumeTurn;\n }\n\n private getPlayerFsm(player: PlayerLabel): SessionFsm {\n return player === 'local' ? this.local : this.remote;\n }\n\n // ===== Helper Methods for Undo/Restart Request Handling =====\n\n /**\n * Save game state snapshot for undo/restart operations\n */\n private gameSnapshot: unknown = null;\n\n public saveGameSnapshot(snapshot: unknown): void {\n this.gameSnapshot = snapshot;\n }\n\n public getGameSnapshot(): unknown {\n return this.gameSnapshot;\n }\n\n public clearGameSnapshot(): void {\n this.gameSnapshot = null;\n }\n\n /**\n * Check if there's a pending action (undo/restart)\n */\n public hasPendingAction(): boolean {\n return this.pendingAction !== null;\n }\n\n /**\n * Clear all pending states (called after approval/rejection)\n */\n public clearPendingStates(): void {\n this.pendingAction = null;\n this.pendingUndoCount = null;\n this.resumeTurn = null;\n }\n\n /**\n * Initialize undo request with undo count and current turn holder\n */\n public initializeUndoRequest(undoCount: 1 | 2, resumeTurn: PlayerLabel): void {\n this.pendingAction = 'undo';\n this.pendingUndoCount = undoCount;\n this.resumeTurn = resumeTurn;\n }\n\n /**\n * Initialize restart request with resume turn\n */\n public initializeRestartRequest(resumeTurn: PlayerLabel): void {\n this.pendingAction = 'restart';\n this.resumeTurn = resumeTurn;\n }\n\n /**\n * Check if pending action is undo\n */\n public isPendingUndo(): boolean {\n return this.pendingAction === 'undo';\n }\n\n /**\n * Check if pending action is restart\n */\n public isPendingRestart(): boolean {\n return this.pendingAction === 'restart';\n }\n\n /**\n * Apply undo by popping history N times\n */\n public applyUndo(count: 1 | 2 = 1): void {\n for (let i = 0; i < count; i++) {\n this.popHistory();\n }\n }\n\n /**\n * Reset game state to initial (for restart)\n */\n public resetGame(): void {\n this.clearHistory();\n this.local = new SessionFsm('idle');\n this.remote = new SessionFsm('idle');\n this.lastStart = null;\n this.resumeTurn = null;\n this.stateObserverManager.notifyGameReset();\n }\n\n /**\n * Save start player for rejoin flow\n */\n public recordStartPlayer(player: PlayerLabel): void {\n this.lastStart = player;\n }\n\n /**\n * Get move to undo from history\n */\n public getLastMove(): TurnEntry | null {\n return this.history.length > 0 ? this.history[this.history.length - 1] : null;\n }\n\n // ===== Specialized FSM Dispatch Methods =====\n\n /**\n * Dispatch APPROVE action with automatic target state resolution\n * Multiple valid transitions exist - use state context to determine target\n */\n public dispatchApprove(): void {\n const localState = this.local.getState();\n if (localState === 'waiting_approval') {\n // Local was waiting, always go back to turn\n this.local.dispatch('APPROVE', 'turn');\n this.remote.dispatch('APPROVE', 'turn');\n } else if (localState === 'approving') {\n // Local was confirming, peer takes turn\n this.local.dispatch('APPROVE', 'remote_turn');\n this.remote.dispatch('APPROVE', 'remote_turn');\n }\n }\n\n /**\n * Dispatch REJECT action with automatic target state resolution\n * Multiple valid transitions exist - use resumeTurn to determine who continues\n */\n public dispatchReject(): void {\n const localState = this.local.getState();\n\n if (localState === 'waiting_approval' || localState === 'approving') {\n // resumeTurn tells us who should have turn after rejection\n const targetState = this.resumeTurn === 'local' ? 'turn' : 'remote_turn';\n this.local.dispatch('REJECT', targetState);\n this.remote.dispatch('REJECT', targetState);\n }\n }\n\n /**\n * Dispatch START action with automatic target state resolution\n * Determines who plays first based on starter parameter\n */\n public dispatchStart(firstPlayer: PlayerLabel): void {\n if (firstPlayer === 'local') {\n this.local.dispatch('START', 'turn');\n this.remote.dispatch('START', 'remote_turn');\n this.lastStart = 'local';\n } else {\n this.local.dispatch('START', 'remote_turn');\n this.remote.dispatch('START', 'turn');\n this.lastStart = 'remote';\n }\n }\n\n /**\n * Dispatch SYNC_COMPLETE with automatic target state resolution\n * Based on who should have the turn after sync\n */\n public dispatchSyncComplete(nextPlayer: PlayerLabel): void {\n if (nextPlayer === 'local') {\n this.local.dispatch('SYNC_COMPLETE', 'turn');\n this.remote.dispatch('SYNC_COMPLETE', 'remote_turn');\n } else {\n this.local.dispatch('SYNC_COMPLETE', 'remote_turn');\n this.remote.dispatch('SYNC_COMPLETE', 'turn');\n }\n this.resumeTurn = nextPlayer;\n }\n\n // ===== Game Plugin Integration (Proxy Pattern) =====\n\n /**\n * Set the game plugin for rule validation and win checking\n * @param plugin Implementation of IGamePlugin\n */\n public setGamePlugin(plugin: IGamePlugin): void {\n this.gamePlugin = plugin;\n if (plugin.initialize) {\n plugin.initialize();\n }\n }\n\n /**\n * Get current game plugin\n */\n public getGamePlugin(): IGamePlugin {\n return this.gamePlugin;\n }\n\n /**\n * Validate a move using the game plugin\n * Called by move handler to check if move is legal\n * @param move The move data to validate\n * @returns Validation result with reason if invalid\n */\n public validateMove(move: unknown): ValidationResult {\n const gameState = this.buildGameState();\n return this.gamePlugin.validateMove(move, gameState);\n }\n\n /**\n * Check if game has ended (someone won)\n * Called by move handler after move is applied\n * @returns Winner (local/remote) or null if game continues\n */\n public checkWin(): PlayerLabel | null {\n const gameState = this.buildGameState();\n return this.gamePlugin.checkWin(gameState, this.getHistory());\n }\n\n /**\n * Cleanup when game ends (for plugin to reset internal state)\n */\n public cleanupGame(): void {\n if (this.gamePlugin.cleanup) {\n this.gamePlugin.cleanup();\n }\n }\n\n /**\n * Build game state for plugin\n * @private\n */\n private buildGameState(): GameState {\n return {\n history: this.getHistory(),\n localState: this.getState('local'),\n remoteState: this.getState('remote'),\n turn: this.getTurnCount(),\n lastStart: this.getLastStart(),\n };\n }\n}\n","import type { NetworkClient } from 'p2p-lockstep-kit-network';\nimport type { SessionMessage } from '../utils';\nimport type { BusMessageType, CommandBus } from './commandBus';\n\n/**\n * Network client wrapper that bridges NetworkClient with CommandBus\n * Handles message encoding/decoding and connection state monitoring\n */\nexport class NetClient {\n private localPeerId: string | null;\n private remotePeerId: string | null;\n private isConnected: boolean = false;\n private connectionChangeListener: (isConnected: boolean) => void = () => {};\n private mediaStateListener: (active: boolean) => void = () => {};\n\n public constructor(\n private readonly client: NetworkClient,\n private readonly bus: CommandBus,\n peerId: string | null,\n ) {\n this.localPeerId = peerId ?? null;\n this.remotePeerId = null;\n\n // Forward incoming messages to the command bus\n this.client.onMessage((data) => {\n const message = data as Partial<SessionMessage> & { type?: string };\n if (!message || typeof message !== 'object' || !message.type) {\n return;\n }\n this.bus.dispatch({\n ...(message as SessionMessage),\n type: message.type as BusMessageType,\n from: 'remote',\n });\n });\n\n // Monitor connection state and emit ONLINE/OFFLINE events\n this.client.onStateChange((state) => {\n const wasConnected = this.isConnected;\n this.isConnected = state === 'connected';\n\n // Notify listeners of connection state change\n this.connectionChangeListener(this.isConnected);\n\n // Only emit ONLINE/OFFLINE once when state changes\n if (this.isConnected && !wasConnected) {\n this.bus.emit('ONLINE', undefined, 'local');\n } else if (!this.isConnected && wasConnected) {\n this.bus.emit('OFFLINE', undefined, 'local');\n }\n });\n\n // Monitor remote media stream availability\n this.client.onRemoteStream((stream) => {\n const active =\n !!stream &&\n stream.getTracks().some((track) => track.readyState === 'live');\n this.mediaStateListener(active);\n });\n }\n\n /**\n * Send a message to the remote peer\n * Drops message if not connected and logs warning\n */\n public send(message: SessionMessage) {\n if (!this.isConnected) {\n console.warn(\n '[NetClient] Cannot send message: not connected',\n message.type,\n );\n return;\n }\n\n const enriched: SessionMessage = {\n ...message,\n from: message.from ?? this.localPeerId ?? '',\n };\n this.client.send(JSON.stringify(enriched));\n }\n\n /**\n * Update local and remote peer IDs\n */\n public setPeerIds(ids: { local?: string | null; remote?: string | null }) {\n if (ids.local !== undefined) {\n this.localPeerId = ids.local;\n }\n if (ids.remote !== undefined) {\n this.remotePeerId = ids.remote;\n }\n }\n\n /**\n * Get current peer IDs\n */\n public getPeerIds() {\n return { local: this.localPeerId, remote: this.remotePeerId };\n }\n\n /**\n * Check if currently connected to peer\n */\n public getIsConnected(): boolean {\n return this.isConnected;\n }\n\n /**\n * Monitor connection state changes\n * @param handler Called when connection state changes (true=connected, false=disconnected)\n */\n public onConnectionChange(handler: (isConnected: boolean) => void) {\n this.connectionChangeListener = handler;\n // Immediately call with current state if already have a state\n handler(this.isConnected);\n }\n\n /**\n * Monitor remote media stream state changes\n * @param handler Called when remote media becomes available or unavailable\n */\n public onMediaStateChange(handler: (active: boolean) => void) {\n this.mediaStateListener = handler;\n }\n}\n\nexport const createNetClient = (\n client: NetworkClient,\n bus: CommandBus,\n peerId: string | null,\n) => new NetClient(client, bus, peerId);\n","import type { State } from './state/state';\nimport type { CommandBus } from './commandBus';\nimport type { SessionMessage } from '../utils';\nimport { NetClient } from './net';\n\n/**\n * Global session context holder\n * Stores the current session's dependencies for handler access\n * Used by handlers to retrieve state, bus, and network client\n */\nclass SessionContext {\n private readonly state: State;\n private readonly bus: CommandBus;\n private readonly net: NetClient;\n private readonly sid?: string;\n\n constructor(state: State, bus: CommandBus, net: NetClient, sid?: string) {\n this.state = state;\n this.bus = bus;\n this.net = net;\n this.sid = sid;\n }\n\n getState() {\n return this.state;\n }\n\n getBus() {\n return this.bus;\n }\n\n getNet() {\n return this.net;\n }\n\n getSid() {\n return this.sid;\n }\n}\n\nlet instance: SessionContext | null = null;\n\n/**\n * Initialize the global session context\n * Must be called before handlers use context getters\n * @throws Error if context is accessed before initialization\n */\nexport const initializeContext = (\n state: State,\n bus: CommandBus,\n net: NetClient,\n sid?: string,\n) => {\n instance = new SessionContext(state, bus, net, sid);\n};\n\n/**\n * Reset the session context (for testing)\n * @internal Used by tests to clean up between test cases\n */\nexport const resetContext = () => {\n instance = null;\n};\n\n/**\n * Get the current session context\n * @throws Error if context has not been initialized\n * @internal Use getState(), getBus(), etc. instead\n */\nconst requireContext = () => {\n if (!instance) {\n throw new Error(\n '[SessionContext] Not initialized. Call initializeContext() first.',\n );\n }\n return instance;\n};\n\n/**\n * Get the current game state\n * @throws Error if context has not been initialized\n */\nexport const getState = () => requireContext().getState();\n\n/**\n * Get the command bus\n * @throws Error if context has not been initialized\n */\nexport const getBus = () => requireContext().getBus();\n\n/**\n * Get the network client\n * @throws Error if context has not been initialized\n * @internal Prefer using send() for sending messages\n */\nexport const getNet = () => requireContext().getNet();\n\n/**\n * Get the session ID\n * @throws Error if context has not been initialized\n */\nexport const getSid = () => requireContext().getSid();\n\n/**\n * Send a session message to the remote peer\n * @throws Error if context has not been initialized\n */\nexport const send = (message: SessionMessage) =>\n requireContext().getNet().send(message);\n\n","import type { CommandListener } from '../commandBus';\nimport { getBus, getSid, getState, send } from '../context';\nimport type { SessionMessage } from '../../utils';\n\n/**\n * Handle player ready status notification\n *\n * For most cases, there's only one valid FSM transition:\n * - READY: idle → ready (for initiator)\n * - REMOTE_READY: idle → could_start (for peer)\n *\n * The dispatch() method automatically finds the unique transition.\n */\nexport const ready: CommandListener = (command) => {\n const state = getState();\n const bus = getBus();\n const localSid = getSid();\n\n if (command.from === 'local') {\n // Local player initiating READY\n if (!state.canAction('local', 'READY')) {\n console.warn('[Ready] Cannot dispatch READY from current state', {\n state: state.getState('local'),\n });\n return;\n }\n\n state.dispatch('local', 'READY');\n state.dispatch('remote', 'REMOTE_READY');\n\n const message: SessionMessage = {\n type: 'READY',\n sid: localSid,\n };\n send(message);\n return;\n }\n\n // Remote player sent READY message\n const remoteSid = (command as any).sid;\n\n // Validate session ID\n if (!remoteSid || localSid !== remoteSid) {\n console.warn('[Ready] Session ID mismatch', {\n local: localSid,\n remote: remoteSid,\n });\n bus.emit('REJECT', { reason: 'sid-mismatch' }, 'local');\n return;\n }\n\n // Check if transition is valid\n if (!state.canAction('remote', 'READY')) {\n console.warn('[Ready] Cannot dispatch READY for remote peer', {\n state: state.getState('remote'),\n });\n return;\n }\n\n // Execute state transitions\n state.dispatch('remote', 'READY');\n state.dispatch('local', 'REMOTE_READY');\n};\n","import type { CommandListener } from '../commandBus';\nimport { getState, send } from '../context';\nimport type { PlayerLabel } from '../state/state';\n\n/**\n * Determine next starter (turn order rotation)\n * If no previous starter, randomly pick one\n * Otherwise, alternate between local and remote\n */\nconst getNextStarter = (lastStarter: PlayerLabel | null): PlayerLabel => {\n if (!lastStarter) {\n return Math.random() < 0.5 ? 'local' : 'remote';\n }\n return lastStarter === 'local' ? 'remote' : 'local';\n};\n\n/**\n * Handle game start request\n *\n * Determines who plays first and transitions both FSMs accordingly.\n */\nexport const start: CommandListener = (command) => {\n const state = getState();\n\n if (command.from === 'local') {\n // Local player initiating START\n if (\n !state.canAction('local', 'START') ||\n !state.canAction('remote', 'REMOTE_START')\n ) {\n console.warn('[Start] Cannot START from current state', {\n localState: state.getState('local'),\n remoteState: state.getState('remote'),\n });\n return;\n }\n\n const nextStarter = getNextStarter(state.getLastStart());\n const localTarget = nextStarter === 'local' ? 'turn' : 'remote_turn';\n const remoteTarget = nextStarter === 'local' ? 'remote_turn' : 'turn';\n\n state.setLastStart(nextStarter);\n state.dispatch('local', 'START', localTarget);\n state.dispatch('remote', 'REMOTE_START', remoteTarget);\n\n // Send message with starter info (encoded as 'sender'/'receiver')\n send({\n type: 'START',\n payload: { starter: nextStarter === 'local' ? 'sender' : 'receiver' },\n });\n return;\n }\n\n // Remote player sent START message\n const starterInfo = (command as any).payload?.starter;\n\n if (!starterInfo) {\n console.warn('[Start] Invalid START message format', { payload: command });\n return;\n }\n\n // Check if transition is valid\n if (\n !state.canAction('local', 'REMOTE_START') ||\n !state.canAction('remote', 'START')\n ) {\n console.warn('[Start] Cannot START from current state', {\n localState: state.getState('local'),\n remoteState: state.getState('remote'),\n });\n return;\n }\n\n // Decode from the receiver's perspective: the sender is the remote peer.\n const starter = starterInfo === 'sender' ? 'remote' : 'local';\n const localTarget = starter === 'local' ? 'turn' : 'remote_turn';\n const remoteTarget = starter === 'local' ? 'remote_turn' : 'turn';\n\n state.setLastStart(starter);\n state.dispatch('local', 'REMOTE_START', localTarget);\n state.dispatch('remote', 'START', remoteTarget);\n};\n","import type { CommandListener } from '../commandBus';\nimport { getBus, getState, send } from '../context';\nimport type { SessionMessage } from '../../utils';\n\n/**\n * Handle player move in game\n *\n * Flow:\n * 1. Validate move using game plugin (rule checking)\n * 2. Dispatch state transitions\n * 3. Record move in history\n * 4. Check win condition using game plugin\n * 5. Send to peer or emit GAME_OVER if won\n *\n * The game plugin is injected via state.setGamePlugin(), allowing\n * different games to provide their own rule validation and win logic.\n */\nexport const move: CommandListener = (command) => {\n const state = getState();\n const bus = getBus();\n const movePayload = command.payload;\n\n if (command.from === 'local') {\n // Local player making a move\n if (!state.canAction('local', 'MOVE')) {\n console.warn('[Move] Cannot MOVE from current state', {\n state: state.getState('local'),\n });\n return;\n }\n\n // ===== PROXY POINT 1: Validate move using game plugin =====\n const validation = state.validateMove(movePayload);\n if (!validation.valid) {\n console.warn('[Move] Move validation failed', {\n reason: validation.reason,\n move: movePayload,\n });\n // Could send REJECT to inform peer, but for now silently return\n return;\n }\n\n // Dispatch state transitions\n state.dispatch('local', 'MOVE');\n state.dispatch('remote', 'REMOTE_MOVE');\n\n // Record move in history\n const turn = state.getTurnCount();\n state.pushHistory({\n turn,\n player: 'local',\n move: movePayload,\n });\n\n // ===== PROXY POINT 2: Check win condition using game plugin =====\n const winner = state.checkWin();\n if (winner) {\n // Game ended - someone won\n console.log('[Move] Game over, winner:', winner);\n\n // Notify bus that game ended (for UI to display winner)\n bus.emit('GAME_OVER', { winner, turn }, 'local');\n\n // Send game over to peer\n send({\n type: 'GAME_OVER',\n payload: { winner, turn },\n });\n\n // Transition both FSMs back to idle (game ready to restart)\n state.dispatch('local', 'GAME_OVER');\n state.dispatch('remote', 'GAME_OVER');\n state.cleanupGame();\n return;\n }\n\n // Send move to peer\n const message: SessionMessage = {\n type: 'MOVE',\n turn,\n payload: movePayload,\n };\n send(message);\n return;\n }\n\n // Remote player made a move\n if (!state.canAction('remote', 'MOVE')) {\n console.warn('[Move] Cannot MOVE for remote player', {\n state: state.getState('remote'),\n });\n return;\n }\n\n // ===== PROXY POINT 1: Validate remote move =====\n const validation = state.validateMove(movePayload);\n if (!validation.valid) {\n console.warn('[Move] Remote move validation failed', {\n reason: validation.reason,\n move: movePayload,\n });\n return;\n }\n\n // Dispatch state transitions\n state.dispatch('remote', 'MOVE');\n state.dispatch('local', 'REMOTE_MOVE');\n\n // Record move in history\n const turn = state.getTurnCount();\n state.pushHistory({\n turn,\n player: 'remote',\n move: movePayload,\n });\n\n // ===== PROXY POINT 2: Check win condition =====\n const winner = state.checkWin();\n if (winner) {\n // Game ended - someone won\n console.log('[Move] Game over, winner:', winner);\n\n // Notify bus that game ended (for UI to display winner)\n bus.emit('GAME_OVER', { winner, turn }, 'local');\n\n // Transition both FSMs back to idle (game ready to restart)\n state.dispatch('local', 'GAME_OVER');\n state.dispatch('remote', 'GAME_OVER');\n state.cleanupGame();\n }\n};\n","import type { CommandListener } from '../commandBus';\nimport { getState, send } from '../context';\n\n/**\n * Handle approval/rejection of pending requests (undo/restart)\n *\n * When local player approves/rejects a pending request:\n * - Apply undo/restart state changes (clear history, pop moves)\n * - Use dispatchApprove/dispatchReject for complex state transitions\n * - Notify peer of decision\n *\n * When remote player approves/rejects local's request:\n * - Similar flow but opposite state transitions\n */\nexport const request: CommandListener = (command) => {\n if (command.type !== 'APPROVE' && command.type !== 'REJECT') {\n return;\n }\n\n const state = getState();\n const action = state.getPendingAction();\n\n // No pending action to respond to\n if (!action) {\n console.warn('[Request] No pending action', { commandType: command.type });\n return;\n }\n\n const payload = command.payload as { action?: string; reason?: string } | undefined;\n\n // Verify payload matches pending action\n if (payload?.action && payload.action !== action) {\n console.warn('[Request] Action mismatch', { pending: action, payload: payload.action });\n return;\n }\n\n if (command.from === 'local') {\n if (command.type === 'APPROVE') {\n // Local player approves the request\n if (!state.canAction('local', 'APPROVE')) {\n console.warn('[Request] Cannot APPROVE from current state');\n return;\n }\n\n // Use special method for complex APPROVE transition\n state.dispatchApprove();\n\n // Apply the approved action (undo or restart)\n if (action === 'undo') {\n state.applyUndo(state.getPendingUndoCount() ?? 1);\n } else if (action === 'restart') {\n state.resetGame();\n }\n\n send({ type: 'APPROVE', payload: { action } });\n state.clearPendingStates();\n return;\n }\n\n // REJECT from local\n if (!state.canAction('local', 'REJECT')) {\n console.warn('[Request] Cannot REJECT from current state');\n return;\n }\n\n // Use special method for complex REJECT transition\n state.dispatchReject();\n\n send({\n type: 'REJECT',\n payload: { action, reason: payload?.reason ?? 'rejected' },\n });\n state.clearPendingStates();\n return;\n }\n\n // Remote player responded to local's request\n if (command.type === 'APPROVE') {\n // Remote player approves\n if (!state.canAction('local', 'APPROVE')) {\n console.warn('[Request] Cannot APPROVE from current state (remote approved)');\n return;\n }\n\n // Use special method for complex APPROVE transition\n state.dispatchApprove();\n\n // Apply the approved action (undo or restart)\n if (action === 'undo') {\n state.applyUndo(state.getPendingUndoCount() ?? 1);\n } else if (action === 'restart') {\n state.resetGame();\n }\n\n state.clearPendingStates();\n return;\n }\n\n // REJECT from remote\n if (!state.canAction('local', 'REJECT')) {\n console.warn('[Request] Cannot REJECT from current state (remote rejected)');\n state.clearPendingStates();\n return;\n }\n\n // Use special method for complex REJECT transition\n state.dispatchReject();\n state.clearPendingStates();\n};\n","import type { CommandListener } from '../commandBus';\nimport { getState, send } from '../context';\nimport type { PlayerLabel } from '../state/state';\n\n/**\n * Handle game state synchronization after disconnect/reconnect\n *\n * SYNC_REQUEST: Initiator sends sync request, responder sends back complete game state\n * SYNC_STATE: Received game state, restore it, and transition both players to correct turn\n *\n * Synced data:\n * - history: All moves in order\n * - lastStart: Who started the last match (for turn rotation)\n * - turn: Current turn holder (to determine resume turn)\n * - resumeTurn: Who should have turn after sync (saved before disconnect)\n *\n * Flow:\n * 1. Local disconnects -> offline handler records resumeTurn\n * 2. Local reconnects -> online handler sends SYNC_REQUEST\n * 3. Remote responds with SYNC_STATE (history, lastStart, turn, resumeTurn)\n * 4. Local receives SYNC_STATE -> restores everything, calls dispatchSyncComplete\n * 5. Both FSMs now in correct 'turn'/'remote_turn' state\n */\nexport const sync: CommandListener = (command) => {\n const state = getState();\n\n if (command.type === 'SYNC_REQUEST') {\n if (command.from === 'local') {\n // Local player initiated sync (after reconnection)\n if (!state.canAction('local', 'SYNC')) {\n console.warn('[Sync] Cannot SYNC from current state');\n return;\n }\n\n // Both transition to syncing state\n state.dispatch('local', 'SYNC', 'syncing');\n state.dispatch('remote', 'SYNC', 'syncing');\n\n // Send request to peer (peer will respond with SYNC_STATE)\n send({ type: 'SYNC_REQUEST', from: '', payload: command.payload });\n return;\n }\n\n // Remote initiated sync - respond with complete game state\n const payload = {\n history: state.getHistory(),\n lastStart: state.getLastStart(),\n turn: state.getState('local') === 'turn' ? 'local' : 'remote',\n resumeTurn: state.getResumeTurn(), // Send back the saved resume turn\n };\n send({ type: 'SYNC_STATE', from: '', payload });\n return;\n }\n\n if (command.type !== 'SYNC_STATE') {\n return;\n }\n\n // Received complete game state from peer\n const payload = (command.payload as {\n history?: Array<{ turn: number; player: 'local' | 'remote'; move?: unknown }>;\n lastStart?: 'local' | 'remote' | null;\n turn?: 'local' | 'remote';\n resumeTurn?: 'local' | 'remote' | null;\n }) || {};\n\n // Restore game history from peer\n if (payload.history && payload.history.length > 0) {\n state.replaceHistory(payload.history);\n } else {\n state.clearHistory();\n }\n\n // Restore match start player (for turn rotation in next match)\n if (payload.lastStart) {\n state.setLastStart(payload.lastStart);\n } else {\n state.setLastStart(null);\n }\n\n // Determine who should have turn after sync\n // Priority: 1) resumeTurn from peer (what they saved on disconnect)\n // 2) turn from payload (whose turn it actually was)\n let nextPlayer: PlayerLabel;\n\n if (payload.resumeTurn) {\n // Use the saved resume turn if available\n nextPlayer = payload.resumeTurn;\n } else if (payload.turn) {\n // Otherwise use current turn from peer\n nextPlayer = payload.turn === 'local' ? 'local' : 'remote';\n } else {\n // Fallback to current state\n nextPlayer = state.getState('local') === 'turn' ? 'local' : 'remote';\n }\n\n console.log('[Sync] Restored game state', {\n historyLength: state.getHistory().length,\n lastStart: state.getLastStart(),\n nextTurnPlayer: nextPlayer,\n });\n\n // Use special method for complex SYNC_COMPLETE transition\n // This sets both local and remote FSM to correct 'turn'/'remote_turn' state\n state.dispatchSyncComplete(nextPlayer);\n};\n","import type { CommandListener } from '../commandBus';\nimport { getState, send } from '../context';\n\n/**\n * Handle undo request from local or remote player\n *\n * Validates undo is legal (enough history, no pending action, valid state)\n * and initiates undo request with peer approval flow.\n *\n * Determines undo count (1 or 2 moves) based on current turn holder.\n * Records pending undo state for approval/rejection handling by request handler.\n */\nexport const undo: CommandListener = (command) => {\n if (command.type !== 'UNDO') {\n return;\n }\n\n const state = getState();\n\n if (command.from === 'local') {\n // Validate no pending action\n if (state.hasPendingAction()) {\n return;\n }\n\n // Validate state transitions\n if (!state.canAction('local', 'UNDO')) {\n console.warn('[Undo] Cannot UNDO from current state');\n return;\n }\n\n // Determine undo count based on whose turn it is\n const localState = state.getState('local');\n const undoCount = localState === 'turn' ? 1 : 2;\n\n // Validate history is long enough\n if (state.getHistory().length < undoCount) {\n console.warn('[Undo] Not enough history to undo', { count: undoCount });\n return;\n }\n\n // Initialize pending undo state\n state.initializeUndoRequest(undoCount as 1 | 2, 'local');\n\n // Transition to approval waiting state\n state.dispatch('local', 'UNDO');\n state.dispatch('remote', 'REMOTE_UNDO');\n\n send({ type: 'UNDO', payload: { count: undoCount } });\n return;\n }\n\n // Remote player requested undo\n if (state.hasPendingAction()) {\n send({ type: 'REJECT', payload: { action: 'undo', reason: 'busy' } });\n return;\n }\n\n // Validate state transitions\n if (!state.canAction('local', 'REMOTE_UNDO')) {\n console.warn('[Undo] Cannot accept remote UNDO request');\n send({ type: 'REJECT', payload: { action: 'undo', reason: 'invalid_state' } });\n return;\n }\n\n // Extract undo count from payload\n const payload = command.payload as { count?: number } | undefined;\n const count = payload?.count === 2 ? 2 : 1;\n\n // Validate count value\n if (payload?.count && payload.count !== 1 && payload.count !== 2) {\n send({ type: 'REJECT', payload: { action: 'undo', reason: 'invalid' } });\n return;\n }\n\n // Validate history is long enough\n if (count === 2 && state.getHistory().length < 2) {\n send({ type: 'REJECT', payload: { action: 'undo', reason: 'no_history' } });\n return;\n }\n\n // Determine who will resume after undo\n const resumePlayer = state.getState('local') === 'turn' ? 'local' : 'remote';\n\n // Initialize pending undo state\n state.initializeUndoRequest(count as 1 | 2, resumePlayer);\n\n // Transition to approval waiting state (remote initiated, local approving)\n state.dispatch('local', 'REMOTE_UNDO');\n state.dispatch('remote', 'UNDO');\n};\n","import type { CommandListener } from '../commandBus';\nimport { getState, send } from '../context';\n\n/**\n * Handle restart request from local or remote player\n *\n * Validates restart is legal (no pending action, valid state)\n * and initiates restart request with peer approval flow.\n *\n * Restart clears all history but keeps player order for next match.\n */\nexport const restart: CommandListener = (command) => {\n if (command.type !== 'RESTART') {\n return;\n }\n\n const state = getState();\n\n if (command.from === 'local') {\n // Validate no pending action\n if (state.hasPendingAction()) {\n return;\n }\n\n // Validate state transitions\n if (!state.canAction('local', 'RESTART')) {\n console.warn('[Restart] Cannot RESTART from current state');\n return;\n }\n\n // Determine who will go first in next match\n const resumePlayer = state.getState('local') === 'turn' ? 'local' : 'remote';\n\n // Initialize pending restart state\n state.initializeRestartRequest(resumePlayer);\n\n // Transition to approval waiting state\n state.dispatch('local', 'RESTART');\n state.dispatch('remote', 'REMOTE_RESTART');\n\n send({ type: 'RESTART' });\n return;\n }\n\n // Remote player requested restart\n if (state.hasPendingAction()) {\n send({ type: 'REJECT', payload: { action: 'restart', reason: 'busy' } });\n return;\n }\n\n // Validate state transitions\n if (!state.canAction('local', 'REMOTE_RESTART')) {\n console.warn('[Restart] Cannot accept remote RESTART request');\n send({ type: 'REJECT', payload: { action: 'restart', reason: 'invalid_state' } });\n return;\n }\n\n // Determine who will go first in next match\n const resumePlayer = state.getState('local') === 'turn' ? 'local' : 'remote';\n\n // Initialize pending restart state\n state.initializeRestartRequest(resumePlayer);\n\n // Transition to approval waiting state (remote initiated, local approving)\n state.dispatch('local', 'REMOTE_RESTART');\n state.dispatch('remote', 'RESTART');\n};\n","import type { CommandListener } from '../commandBus';\nimport { getBus, getState } from '../context';\n\n/**\n * Handle connection state changes (OFFLINE/ONLINE)\n *\n * OFFLINE: Record current turn state before disconnecting\n * ONLINE: Trigger sync to restore game state after reconnect\n *\n * Flow:\n * 1. OFFLINE arrives -> save resumeTurn, transition to offline\n * 2. ONLINE arrives -> transition to syncing, emit SYNC_REQUEST\n * 3. sync handler takes over -> restore history and turn assignment\n */\nexport const offline: CommandListener = (command) => {\n if (command.type !== 'OFFLINE' && command.type !== 'ONLINE') {\n return;\n }\n\n const state = getState();\n const bus = getBus();\n\n if (command.type === 'OFFLINE') {\n // Peer disconnected - save current turn state for later recovery\n if (!state.canAction('remote', 'OFFLINE')) {\n console.warn('[Offline] Cannot transition to OFFLINE from current state');\n return;\n }\n\n // Record who had the turn before going offline\n const currentTurn = state.getState('local') === 'turn' ? 'local' : 'remote';\n state.setResumeTurn(currentTurn);\n\n // Transition to offline state\n state.dispatch('remote', 'OFFLINE', 'offline');\n return;\n }\n\n // ONLINE - peer reconnected\n if (!state.canAction('remote', 'ONLINE')) {\n console.warn('[Offline] Cannot transition to ONLINE from current state');\n return;\n }\n\n // Transition to syncing state\n state.dispatch('remote', 'ONLINE', 'syncing');\n\n // Trigger sync to restore game state\n // sync handler will:\n // 1. Send SYNC_REQUEST\n // 2. Receive SYNC_STATE from peer\n // 3. Restore history and correct turn assignment\n bus.emit('SYNC_REQUEST', undefined, 'local');\n};\n","import { CommandBus } from '../commandBus';\nimport { ready } from './ready';\nimport { start } from './start';\nimport { move } from './move';\nimport { request } from './request';\nimport { sync } from './sync';\nimport { undo } from './undo';\nimport { restart } from './restart';\nimport { offline } from './offLine';\n\nexport const registerHandlers = (bus: CommandBus) => {\n bus.register('READY', ready);\n bus.register('START', start);\n bus.register('MOVE', move);\n bus.register('UNDO', undo);\n bus.register('RESTART', restart);\n bus.register('SYNC_REQUEST', sync);\n bus.register('SYNC_STATE', sync);\n bus.register('OFFLINE', offline);\n bus.register('ONLINE', offline);\n bus.register('APPROVE', request);\n bus.register('REJECT', request);\n};\n\n\n","import type { CommandBus } from './commandBus';\n\nexport interface ISessionActions {\n ready(): void;\n start(): void;\n move(data: unknown): void;\n undo(): void;\n restart(): void;\n approve(): void;\n reject(): void;\n rejoin(sid: string): void;\n}\n\nexport class LocalActionsAPI implements ISessionActions {\n constructor(private bus: CommandBus) {}\n\n ready(): void {\n this.bus.emit('READY');\n }\n\n start(): void {\n this.bus.emit('START');\n }\n\n move(data: unknown): void {\n this.bus.emit('MOVE', data);\n }\n\n undo(): void {\n this.bus.emit('UNDO');\n }\n\n restart(): void {\n this.bus.emit('RESTART');\n }\n\n approve(): void {\n this.bus.emit('APPROVE');\n }\n\n reject(): void {\n this.bus.emit('REJECT');\n }\n\n rejoin(sid: string): void {\n this.bus.emit('REJOIN', { sid });\n }\n}\n\n","import { NetworkClient } from 'p2p-lockstep-kit-network';\nimport { CommandBus } from './commandBus';\nimport { State } from './state/state';\nimport { createNetClient } from './net';\nimport { initializeContext } from './context';\nimport { registerHandlers } from './handlers/busRegister.ts';\nimport {\n buildGameStateSnapshot,\n GameStateObserver,\n UINotificationAdapter,\n} from './observer';\nimport { LocalActionsAPI } from './actions';\n\n/**\n * Create a new game session with state management and networking\n * @param sid Session ID for rejoining (optional)\n * @param networkClient Custom network client (optional, creates default if not provided)\n * @returns Session manager with bus, state, observer, net, and send method\n *\n * @example\n * const session = createSession();\n * // UI automatically updates when state changes - no manual observer calls needed!\n * session.observer.subscribe(myUIObserver);\n * session.bus.emit('READY', undefined, 'local');\n * await session.net.connect(remotePeerId);\n */\nexport const createSession = (networkClient: NetworkClient, sid?: string) => {\n const bus = new CommandBus();\n const state = new State(null, null);\n const observer = new GameStateObserver();\n const net = createNetClient(networkClient, bus, null);\n\n // Connect State mutations to UI updates via adapter (plugin pattern)\n // This is the ONLY place where UI notifications are triggered\n const uiAdapter = new UINotificationAdapter(state, observer, () =>\n net.getIsConnected(),\n );\n state.subscribeStateObserver(uiAdapter);\n\n initializeContext(state, bus, net, sid);\n registerHandlers(bus);\n\n const actions = new LocalActionsAPI(bus);\n\n net.onConnectionChange((isConnected) => {\n observer.notifyConnectionChange(isConnected);\n observer.notifyStateChange(buildGameStateSnapshot(state, isConnected));\n });\n\n return {\n bus,\n state,\n observer,\n net,\n actions,\n send: net.send.bind(net),\n };\n};\n\nexport * from './observer';\nexport type { ISessionActions } from './actions';\n"],"mappings":"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","names":["move","localTarget","remoteTarget","validation","turn","winner","payload","resumePlayer"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "p2p-lockstep-kit-session",
3
3
  "description": "Game session and lockstep engine for the p2p lockstep kit.",
4
- "version": "0.1.3",
4
+ "version": "0.1.5",
5
5
  "license": "GPL-3.0-only",
6
6
  "type": "module",
7
7
  "main": "./dist/session/index.js",
@@ -15,12 +15,6 @@
15
15
  "import": "./dist/session/index.js"
16
16
  }
17
17
  },
18
- "scripts": {
19
- "dev": "tsup --watch",
20
- "build": "tsup",
21
- "typecheck": "tsc -p tsconfig.json --noEmit",
22
- "format": "prettier -w ."
23
- },
24
18
  "dependencies": {
25
19
  "p2p-lockstep-kit-network": "^0.1.1"
26
20
  },
@@ -28,5 +22,11 @@
28
22
  "prettier": "^3.3.1",
29
23
  "tsup": "^8.3.0",
30
24
  "typescript": "^5.3.3"
25
+ },
26
+ "scripts": {
27
+ "dev": "tsup --watch",
28
+ "build": "tsup",
29
+ "typecheck": "tsc -p tsconfig.json --noEmit",
30
+ "format": "prettier -w ."
31
31
  }
32
- }
32
+ }