import type {GameRoomProjection,GameRoomProjectionSource} from '../../framework/sdk/contracts/game-room-projection.ts'; import type {GameEntry,GameHost,GameModule,PlatformGameSnapshot} from '../../framework/sdk/contracts/index.ts'; import {TemplateRoomRules} from './template-room-rules.ts'; import type {PlatformToGameEvent} from '../../framework/sdk/contracts/platform-events.ts'; import {requireRoomCapabilities,type RoomCapabilities} from '../../framework/sdk/contracts/index.ts'; export interface TemplateRoomActions {prepare():void;exit():void;} export interface TemplateRoomPresentation {render(snapshot:PlatformGameSnapshot,actions:TemplateRoomActions,projection?:GameRoomProjection|null):void;close():void;} /** Template hooks are intentionally empty, matching 02_SubGame_Input.js. No gameplay is inferred. */ export function createTemplateGame(gameId:string|number,identity:Pick){ let active:TemplateModule|null=null; class TemplateModule implements GameModule { private capabilities:RoomCapabilities|null=null; readonly roomProjection:GameRoomProjectionSource|null=null; private unsubscribeProjection:(()=>void)|null=null; private unsubscribe:(()=>void)|null=null; private view:TemplateRoomPresentation|null=null; restored:unknown=null; attach(host:GameHost):void{ if(this.capabilities||active)throw Error('Template module already attached'); this.capabilities=requireRoomCapabilities(host);active=this;this.unsubscribe=this.capabilities.room.subscribe(()=>this.render()); this.unsubscribeProjection=this.roomProjection?.subscribe(()=>this.render())??null; } mount(view:TemplateRoomPresentation):void{this.view?.close();this.view=view;this.render();} private requireCapabilities():RoomCapabilities{if(!this.capabilities)throw Error('Template room session disposed');return this.capabilities;} private render():void{this.view?.render(this.requireCapabilities().room.getSnapshot(),{prepare:()=>this.requireCapabilities().room.prepare(),exit:()=>this.requireCapabilities().room.requestExit()},this.roomProjection?.getSnapshot()??null);} handlePlatformEvent(event:PlatformToGameEvent):void{ if(event.type==='room.dissolution-confirmed')throw Error('模板游戏解散结算界面尚未接入'); this.render(); } handleGameMessage():void{/* Original template _ReceiveData hook is empty. */} restore(deskinfo:unknown):void{this.requireCapabilities();this.restored=deskinfo;} dispose():void{this.unsubscribeProjection?.();this.unsubscribeProjection=null;this.unsubscribe?.();this.unsubscribe=null;this.view?.close();this.view=null;this.capabilities=null;this.restored=null;if(active===this)active=null;} } const rules=new TemplateRoomRules(); const entry:GameEntry={key:identity.key,gameId,route:identity.route,resolveSeatCount:roomtype=>rules.seatCount(roomtype),createModule:()=>new TemplateModule()}; return {entry,mount(view:TemplateRoomPresentation){if(!active)throw Error('Template room not attached');active.mount(view);}, get roomProjection(){return active?.roomProjection??null;}, get restoredSnapshot():unknown{return active?.restored;}}; }