feat: integrate erqiwang rooms and use spec-compliant Web compilation

This commit is contained in:
2026-09-07 22:36:24 +08:00
parent 30a1b4114c
commit a7ec8aa6a7
49 changed files with 11603 additions and 1183 deletions
@@ -1,3 +1,5 @@
import {createErqiwangGame} from '../../games/erqiwang/erqiwang-game.ts';
import {ErqiwangRoomView} from '../../games/erqiwang/ErqiwangRoomView.ts';
import { _decorator, assetManager, Component, Director, Label, Node, Prefab, ProgressBar, SceneAsset,
UITransform, Widget, director, instantiate, isValid } from 'cc';
import { resolveRuntimeConfig, type RuntimeConfig } from '../../framework/config/runtime-config.ts';
@@ -150,7 +152,9 @@ export class PlatformStartup extends Component {
/** 退出只重建账号会话;配置、原生桥和已加载资源由常驻组件保留。 */
private async startSession(firstStart:boolean):Promise<void>{
const identity=required(this.config,'config').identity;
this.game=createTemplateGame(identity.gameid);
const gameKey=required(this.getComponent(SubgameAssets),'SubgameAssets').gameKey;
if(gameKey!=='erqiwang'&&gameKey!=='Game_Surface_3')throw Error('Unsupported subgame: '+gameKey);
this.game=gameKey==='erqiwang'?createErqiwangGame(identity.gameid):createTemplateGame(identity.gameid);
this.runtime=new PlatformRuntime({gameEntry:this.game.entry,
roomHistory:new OwnedRoomHistory(window.localStorage,identity.gameid,identity.agentid),
resolveRuntimeConfig:async()=>required(this.config,'config'),
@@ -359,7 +363,7 @@ export class PlatformStartup extends Component {
await this.flow.enter(required(assets.roomScene,'subgame room scene').name,()=>{
if(required(this.runtime,'runtime').state.room.kind!=='inside')return;
const root=instantiate(required(assets.room,'subgame room prefab'));root.parent=this.canvas();root.setPosition(0,0,0);
const view=required(root.getComponent(TemplateRoomView),'TemplateRoomView');
const view=assets.gameKey==='erqiwang'?required(root.getComponent(ErqiwangRoomView),'ErqiwangRoomView'):required(root.getComponent(TemplateRoomView),'TemplateRoomView');
root.on('room-action-error',(error:unknown)=>this.tip(error instanceof Error?error.message:String(error)));
root.on('room-action-timeout',()=>this.tip('房间操作超时,请重试'));
this.createStatus();required(this.game,'template game').mount(view);this.events.push({kind:'scene',data:'Room'});
@@ -3,6 +3,7 @@ const {ccclass,property}=_decorator;
/** App composition root: each game assigns its own pre-room page, room view and scene. */
@ccclass('SubgameAssets')
export class SubgameAssets extends Component {
@property gameKey:string='Game_Surface_3';
@property(Prefab) createRoom:Prefab|null=null;
@property(Prefab) room:Prefab|null=null;
@property(SceneAsset) roomScene:SceneAsset|null=null;
@@ -1,3 +1,4 @@
import type {Roomtype} from '../../framework/sdk/contracts/roomtype.ts';
import {_decorator,Button,Component,instantiate,Label,Node,Prefab,ScrollView,Sprite,SpriteFrame,UITransform} from 'cc';
import type {VipCode,VipRoom} from '../../framework/core/types/vip-room.ts';
import {sameVipCode} from '../../framework/core/types/vip-room.ts';
@@ -15,8 +16,8 @@ export class VipRoomListView extends Component {
@property([SpriteFrame]) deskFrames:SpriteFrame[]=[];
private rooms:readonly VipRoom[]=[];
private selected:VipCode|null=null;
private describe:(roomtype:readonly unknown[])=>string=()=>{throw Error('VIP room description provider is not configured');};
configure(describe:(roomtype:readonly unknown[])=>string):void {this.describe=describe;this.showRooms();}
private describe:(roomtype: Roomtype)=>string=()=>{throw Error('VIP room description provider is not configured');};
configure(describe:(roomtype: Roomtype)=>string):void {this.describe=describe;this.showRooms();}
get selectedCode():VipCode|null {return this.selected;}
render(rooms:readonly VipRoom[],codes:readonly VipCode[]):void {
this.rooms=orderedVipRooms(rooms,codes);
@@ -1,3 +1,4 @@
import type {Roomtype} from '../../framework/sdk/contracts/roomtype.ts';
import { assetManager, BlockInputEvents, Button, EditBox, ImageAsset, Label, Mask, Node, Prefab, ScrollView, Sprite, SpriteFrame, Texture2D, UITransform, Widget, instantiate, isValid } from 'cc';
import type { RuntimeConfig } from '../../framework/config/runtime-config.ts';
import { selectLobbyModel, shouldShowNotice, type LobbyModel, type LobbyPolicy } from '../../framework/presentation/lobby-model.ts';
@@ -67,10 +68,10 @@ export class LobbyPanel {
private readonly onAction:(action:LobbyAction)=>void,
private readonly claimTask:(taskid:number|string)=>void,
private readonly nativeCall:(name:string,data:Record<string,unknown>)=>void,
private readonly vip?:{request:(codes:readonly VipCode[])=>void;join:(code:VipCode)=>void;describe:(roomtype:readonly unknown[])=>string},
private readonly vip?:{request:(codes:readonly VipCode[])=>void;join:(code:VipCode)=>void;describe:(roomtype: Roomtype)=>string},
private readonly invite?:{query:()=>void;bind:(code:string)=>void;paste:()=>Promise<string>},
private readonly extras?:{rank:()=>void;redeem:(cardno:string)=>void;paste:()=>Promise<string>;vipRank?:(limit:number)=>void;joinOwned?:(code:number)=>void;loading?:(show:boolean)=>void;
room?:{createPrefab:Prefab;join(code:number):void;create(roomtype:readonly unknown[]):void;busy():boolean;close():void}}){
room?:{createPrefab:Prefab;join(code:number):void;create(roomtype: Roomtype):void;busy():boolean;close():void}}){
this.requestLoading=new RequestLoading(show=>need(this.extras?.loading,'request Loading UI')(show));
this.model=selectLobbyModel(state);
if(assets.switches.length!==2)throw Error('LobbyAssets requires 2 switch frames');
@@ -479,7 +480,7 @@ export class LobbyPanel {
const text=window.localStorage.getItem('tsgame_roomtype_'+this.config.identity.gameid+this.config.identity.agentid);
let previous:unknown=null;
if(text!==null&&text!==''){
const saved=JSON.parse(text);if(!saved||!Array.isArray(saved.data))throw Error('Invalid saved roomtype');previous=saved.data;
const saved=JSON.parse(text);if(!saved||!(typeof saved.data==='string'||Array.isArray(saved.data)))throw Error('Invalid saved roomtype');previous=saved.data;
}
const current=()=>this.modal===modal&&this.roomPage===page&&this.model.canAct;
page.open({previousRoomtype:previous,submit:roomtype=>{if(current())commands.create(roomtype);},cancel:()=>{if(this.modal===modal)this.close();},reportError:text=>{if(current())this.tip(text);}});