Complete room UI and protocol integration, move game definitions and resources behind bundle entries, publish authoritative version XML, and document single-game builds. Include all current resource changes and experiment artifacts.
40 lines
3.1 KiB
TypeScript
40 lines
3.1 KiB
TypeScript
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<GameEntry,'key'|'route'>){
|
|
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;}};
|
|
}
|