feat: integrate platform room entry, UI migration and erqiwang documentation
This commit is contained in:
@@ -1,61 +1,69 @@
|
|||||||
# AGENTS.md
|
# AGENTS.md
|
||||||
|
|
||||||
|
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## 第一准则:服务器零改动
|
||||||
|
|
||||||
|
**任何前端开发都必须完全遵循前后端数据包的协议与数据结构,做到服务器零改动。** 这是本仓库不可逾越的最高准则,优先于一切其它考量:
|
||||||
|
|
||||||
|
- 协议信封、`route`/`rpc` 命名、字段名与类型、`roomtype` 配置编码、`deskinfo` 快照结构等,**必须与现有协议逐字节对齐**(依据 `docs/protocol/`),不得为前端方便而改变格式或新增/重命名字段。
|
||||||
|
- 遇到协议层有疑问时,**先读 `docs/protocol/` 对应章节核对,不要臆测**;宁可前端多做适配,也绝不要求服务器配合改动。
|
||||||
|
- 新的 Cocos 前端(`YouleNexus`)的目标,就是在不改服务器的前提下复刻该协议契约、实现联调兼容。
|
||||||
|
|
||||||
|
## 第二准则:数据源权威、唯一,下游不兜底
|
||||||
|
|
||||||
|
**每一类数据只能有一个权威且唯一的来源(single source of truth);下游消费方不得猜测、修补、兜底。**
|
||||||
|
|
||||||
|
- **唯一来源**:同一份配置/数据(如服务器地址、渠道身份、协议常量)只在一个地方定义,其它地方一律引用,不得各处复制或并存第二份。
|
||||||
|
- **下游零兜底**:消费方拿到的数据缺失或非法时,**直接显式报错/抛出,把问题暴露到来源**,不得用默认值「猜一个」、不得静默 `?? 兜底值`、不得 try/catch 后塞个 fallback 蒙混。错误要早暴露、可定位,而不是被下游悄悄掩盖。
|
||||||
|
- **配置即契约**:来源(如 `profiles.ts` 的服务器地址)必须把该提供的字段显式提供齐全;缺了就是配置错误,应在来源处修正,而不是让下游补。
|
||||||
|
- 例外只有一种:来源本身明确定义了「可选 + 缺省语义」,且该缺省**写在来源处**(而非散落在各下游)。
|
||||||
|
|
||||||
|
> 这条与「服务器零改动」并列:前者保边界契约不变,后者保内部数据流可信、单源、可追溯。新增/修改任何带默认值、回退、容错分支的代码前,先问:这是不是在替上游兜底?若是,改为在来源处修正 + 在下游暴露。
|
||||||
|
|
||||||
|
## 仓库总览
|
||||||
|
|
||||||
|
这是友乐(Youle)棋牌游戏平台的前端工作区,核心是「**一套前端模板 + 多个子游戏**」的架构,并正在迁移到 Cocos Creator。主要目录:
|
||||||
|
|
||||||
|
- **`projects/`** —— 旧版 HTML5 前端。`projects/Game_Surface_3` 是**前端模板(平台外壳)**,也是新前端 UI 迁移的**原工程(迁移源)**;其余目录都是**基于该模板扩展出来的子游戏**,平台层几乎不动,只改子游戏层 + 美术数据。分层结构与关键文件见 `projects/AGENTS.md`。
|
||||||
|
- **`docs/protocol/`** —— 前端模板框架与服务器收发包的**协议规范**(权威文档,8 篇)。改动任何网络层代码前**必须先读对应章节**,服务器不可改,前端必须严格对齐协议。
|
||||||
|
- **`cocoscreator_projects/YouleNexus/`** —— 新的 Cocos Creator 3.8+ 前端工程,目标是按 `docs/protocol` 复刻协议契约,做到服务器零改动即可联调。
|
||||||
|
|
||||||
|
### 服务器文档与二七王服务端
|
||||||
|
|
||||||
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
|
- **`server/development-guide/`**:服务器通用开发文档。查服务端框架、子游戏接入、通信协议和开发约束时,从 [README](server/development-guide/README.md) 开始,按任务阅读对应章节。
|
||||||
|
- **`server/games/erqiwang/`**:二七王当前改动后的服务器代码。核对实际行为时读取这里的实现,不以 `projects/erqiwang` 的旧前端推断当前服务器规则。
|
||||||
|
- 二七王规则设计位于 [design.md](server/games/erqiwang/docs/design/design.md),玩法协议位于 [packet_protocol.md](server/games/erqiwang/docs/protocol/packet_protocol.md)。前端接入时同时核对设计、协议与服务端实现;发现差异应明确记录,不把设计要求冒充已实现行为。
|
||||||
|
- `roomtype` 由目标子游戏定义,可能是数组或字符串;平台通用流程不可擅自转换类型。二七王实际解析以 `class.config.js` 为入口,局数与扣卡检查 `class.export.js`;位索引只在协议文档中维护,不在本文件重复定义。
|
||||||
|
- 查阅服务器源码用于前端适配,不代表授权修改服务器代码;前端开发继续遵守“服务器零改动”。
|
||||||
|
|
||||||
## 第一准则:服务器零改动
|
### 原工程 `Game_Surface_3` → `YouleNexus` 的 UI 迁移
|
||||||
|
|
||||||
**任何前端开发都必须完全遵循前后端数据包的协议与数据结构,做到服务器零改动。** 这是本仓库不可逾越的最高准则,优先于一切其它考量:
|
`YouleNexus` 的 UI 资源(`assets/framework/ui/prefabs/`)从**原工程 `projects/Game_Surface_3`** 逐层复刻。数据契约与流程规范:
|
||||||
|
|
||||||
- 协议信封、`route`/`rpc` 命名、字段名与类型、`roomtype` 配置数组、`deskinfo` 快照结构等,**必须与现有协议逐字节对齐**(依据 `docs/protocol/`),不得为前端方便而改变格式或新增/重命名字段。
|
- 数据模型(权威):`docs/superpowers/specs/原工程数据模型.md`;迁移流程:`docs/superpowers/specs/UI-手工迁移规范.md`(9 步 funplay MCP + 8 项验证清单)。
|
||||||
- 遇到协议层有疑问时,**先读 `docs/protocol/` 对应章节核对,不要臆测**;宁可前端多做适配,也绝不要求服务器配合改动。
|
- 已完成的参照真相源:`assets/framework/ui/prefabs/Login_Layer.prefab`(Layer 2)。
|
||||||
- 新的 Cocos 前端(`YouleNexus`)的目标,就是在不改服务器的前提下复刻该协议契约、实现联调兼容。
|
|
||||||
|
> ⚠️ **索引与类型(易踩坑,实测修正)**:源数据索引**直接按 ID**——`gameabc_Object.json` 的 `ObjectList[ObjectID]`、`gameabc_Image.json` 的 `ImageFileList[ImageFileID]`(**不是** `ID - 1`,旧文档的 off-by-one 是错的);组件类型看 `ObjectType`(`2`=Sprite、`4`=Text),**不是** XML 的 `SpiritType`(那是另一套枚举)。
|
||||||
## 第二准则:数据源权威、唯一,下游不兜底
|
|
||||||
|
## 网络协议(`docs/protocol/`,新旧前端共同契约)
|
||||||
**每一类数据只能有一个权威且唯一的来源(single source of truth);下游消费方不得猜测、修补、兜底。**
|
|
||||||
|
`docs/protocol/` 是权威文档,改任何网络层代码前先读对应章节,**不要凭本文件的转述臆测**。
|
||||||
- **唯一来源**:同一份配置/数据(如服务器地址、渠道身份、协议常量)只在一个地方定义,其它地方一律引用,不得各处复制或并存第二份。
|
|
||||||
- **下游零兜底**:消费方拿到的数据缺失或非法时,**直接显式报错/抛出,把问题暴露到来源**,不得用默认值「猜一个」、不得静默 `?? 兜底值`、不得 try/catch 后塞个 fallback 蒙混。错误要早暴露、可定位,而不是被下游悄悄掩盖。
|
**两个不可妥协的约束:** ① `roomtype` 房间配置编码的类型与结构必须与目标子游戏**逐字节一致**(服务器据此解析规则);② `deskinfo` 对局快照须可序列化/反序列化以支持 `isbattle==1` 时 `Game_Modify.Reconnect(deskinfo)` 断线重连。
|
||||||
- **配置即契约**:来源(如 `profiles.ts` 的服务器地址)必须把该提供的字段显式提供齐全;缺了就是配置错误,应在来源处修正,而不是让下游补。
|
|
||||||
- 例外只有一种:来源本身明确定义了「可选 + 缺省语义」,且该缺省**写在来源处**(而非散落在各下游)。
|
## 客户端侧适配约束:远程配置 / 原生数据接口 / 原生↔H5 桥接
|
||||||
|
|
||||||
> 这条与「服务器零改动」并列:前者保边界契约不变,后者保内部数据流可信、单源、可追溯。新增/修改任何带默认值、回退、容错分支的代码前,先问:这是不是在替上游兜底?若是,改为在来源处修正 + 在下游暴露。
|
继「服务器零改动」之后的**第二类不可妥协约束**:除 WebSocket 协议外,前端还通过「远程配置文件」(`gameserver` → `urlserver`)与「原生接口」(`window.settings` 同步取值、WVJB 异步桥)获取大量数据。新 Cocos 前端必须与原项目**逐字一致**地复刻这些读取/互调/注册方式(接口名、数据格式、回调约定、URL 构造),让原生侧与配置服务**零改动**即可对接。
|
||||||
|
|
||||||
## 仓库总览
|
具体机制、handler 清单与源码位置见 `native-bridge-contract` 技能(`.Codex/skills/native-bridge-contract/SKILL.md`)——改这部分前先读它核对,不要臆测接口名或数据格式。
|
||||||
|
|
||||||
这是友乐(Youle)棋牌游戏平台的前端工作区,核心是「**一套前端模板 + 多个子游戏**」的架构,并正在迁移到 Cocos Creator。三个顶层目录:
|
## Cocos Creator 新前端:优先使用 funplay-cocos-mcp
|
||||||
|
|
||||||
- **`projects/`** —— 旧版 HTML5 前端。`projects/Game_Surface_3` 是**前端模板(平台外壳)**,也是新前端 UI 迁移的**原工程(迁移源)**;其余目录都是**基于该模板扩展出来的子游戏**,平台层几乎不动,只改子游戏层 + 美术数据。分层结构与关键文件见 `projects/AGENTS.md`。
|
新工程 `cocoscreator_projects/YouleNexus`。**任何涉及该工程的编辑器操作**(场景、节点、组件、prefab、资源、动画、预览/截图等),都必须走 `mcp__funplay_cocos__*` 这组 MCP 工具驱动编辑器。
|
||||||
- **`docs/protocol/`** —— 前端模板框架与服务器收发包的**协议规范**(权威文档,8 篇)。改动任何网络层代码前**必须先读对应章节**,服务器不可改,前端必须严格对齐协议。
|
|
||||||
- **`cocoscreator_projects/YouleNexus/`** —— 新的 Cocos Creator 3.8+ 前端工程,目标是按 `docs/protocol` 复刻协议契约,做到服务器零改动即可联调。
|
**绝不允许手改** `.scene` / `.prefab` / `.anim` / `.meta`——它们是 UUID 引用的序列化资源,任何文本编辑都会破坏引用结构,导致「资源导入失败」且只能人工恢复。**MCP 连不上时也不例外**:应提示用户去编辑器启动服务,而不是退而求其次去手改文件。
|
||||||
|
|
||||||
### 原工程 `Game_Surface_3` → `YouleNexus` 的 UI 迁移
|
具体工具用法不写在这里——MCP server 连上后会自注入完整说明(工具清单、action 速查、批量操作),以其为准,**正常操作不需要先读任何仓库文档**。
|
||||||
|
|
||||||
`YouleNexus` 的 UI 资源(`assets/framework/ui/prefabs/`)从**原工程 `projects/Game_Surface_3`** 逐层复刻。数据契约与流程规范:
|
`cocos-mcp` 技能(`.Codex/skills/cocos-mcp/SKILL.md`)只记录 MCP 自己给不了的部分:前置条件、探活、排障、已知坑。**工具正常时不必读它;一旦 `cocos_*` 工具没出现、调用报连接失败、或行为与预期不符,就去读。**
|
||||||
|
|
||||||
- 数据模型(权威):`docs/superpowers/specs/原工程数据模型.md`;迁移流程:`docs/superpowers/specs/UI-手工迁移规范.md`(9 步 funplay MCP + 8 项验证清单)。
|
|
||||||
- 已完成的参照真相源:`assets/framework/ui/prefabs/Login_Layer.prefab`(Layer 2)。
|
|
||||||
|
|
||||||
> ⚠️ **索引与类型(易踩坑,实测修正)**:源数据索引**直接按 ID**——`gameabc_Object.json` 的 `ObjectList[ObjectID]`、`gameabc_Image.json` 的 `ImageFileList[ImageFileID]`(**不是** `ID - 1`,旧文档的 off-by-one 是错的);组件类型看 `ObjectType`(`2`=Sprite、`4`=Text),**不是** XML 的 `SpiritType`(那是另一套枚举)。
|
|
||||||
|
|
||||||
## 网络协议(`docs/protocol/`,新旧前端共同契约)
|
|
||||||
|
|
||||||
`docs/protocol/` 是权威文档,改任何网络层代码前先读对应章节,**不要凭本文件的转述臆测**。
|
|
||||||
|
|
||||||
**两个不可妥协的约束:** ① `roomtype` 房间配置数组结构必须与目标子游戏**逐字节一致**(服务器据此解析规则);② `deskinfo` 对局快照须可序列化/反序列化以支持 `isbattle==1` 时 `Game_Modify.Reconnect(deskinfo)` 断线重连。
|
|
||||||
|
|
||||||
## 客户端侧适配约束:远程配置 / 原生数据接口 / 原生↔H5 桥接
|
|
||||||
|
|
||||||
继「服务器零改动」之后的**第二类不可妥协约束**:除 WebSocket 协议外,前端还通过「远程配置文件」(`gameserver` → `urlserver`)与「原生接口」(`window.settings` 同步取值、WVJB 异步桥)获取大量数据。新 Cocos 前端必须与原项目**逐字一致**地复刻这些读取/互调/注册方式(接口名、数据格式、回调约定、URL 构造),让原生侧与配置服务**零改动**即可对接。
|
|
||||||
|
|
||||||
具体机制、handler 清单与源码位置见 `native-bridge-contract` 技能(`.Codex/skills/native-bridge-contract/SKILL.md`)——改这部分前先读它核对,不要臆测接口名或数据格式。
|
|
||||||
|
|
||||||
## Cocos Creator 新前端:优先使用 funplay-cocos-mcp
|
|
||||||
|
|
||||||
新工程 `cocoscreator_projects/YouleNexus`。**任何涉及该工程的编辑器操作**(场景、节点、组件、prefab、资源、动画、预览/截图等),都必须走 `mcp__funplay_cocos__*` 这组 MCP 工具驱动编辑器。
|
|
||||||
|
|
||||||
**绝不允许手改** `.scene` / `.prefab` / `.anim` / `.meta`——它们是 UUID 引用的序列化资源,任何文本编辑都会破坏引用结构,导致「资源导入失败」且只能人工恢复。**MCP 连不上时也不例外**:应提示用户去编辑器启动服务,而不是退而求其次去手改文件。
|
|
||||||
|
|
||||||
具体工具用法不写在这里——MCP server 连上后会自注入完整说明(工具清单、action 速查、批量操作),以其为准,**正常操作不需要先读任何仓库文档**。
|
|
||||||
|
|
||||||
`cocos-mcp` 技能(`.Codex/skills/cocos-mcp/SKILL.md`)只记录 MCP 自己给不了的部分:前置条件、探活、排障、已知坑。**工具正常时不必读它;一旦 `cocos_*` 工具没出现、调用报连接失败、或行为与预期不符,就去读。**
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
type ClipboardHost=
|
||||||
|
|{kind:'settings';read:()=>unknown}
|
||||||
|
|{kind:'bridge';call:(name:'gamepastetext',data:string,callback:(value:unknown)=>void)=>void}
|
||||||
|
|{kind:'browser';read:()=>Promise<string>};
|
||||||
|
|
||||||
|
/** 05_Func: settings takes no arguments; WVJB takes "" and returns through its callback. */
|
||||||
|
export async function readClipboardText(host:ClipboardHost):Promise<string>{
|
||||||
|
const value=host.kind==='bridge'?await new Promise<unknown>((resolve,reject)=>{
|
||||||
|
const timer=setTimeout(()=>reject(Error('读取剪贴板超时,请手动输入')),10000);
|
||||||
|
try{host.call('gamepastetext','',value=>{clearTimeout(timer);resolve(value);});}
|
||||||
|
catch(error){clearTimeout(timer);reject(error);}
|
||||||
|
}):await host.read();
|
||||||
|
if(typeof value!=='string')throw Error('Invalid clipboard text');
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function readInviteClipboard(host:ClipboardHost):Promise<string>{
|
||||||
|
return (await readClipboardText(host)).replace(/[^0-9]/g,'');
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "4aeb3de8-f982-4246-95be-7dca058bc99d",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
import {requireRecord} from '../../protocol/contracts/validation.ts';
|
||||||
|
/** Logic.readRoomList/addRoomToList/removeRoomFromList. This is owner-return history, not recently played rooms. */
|
||||||
|
export class OwnedRoomHistory {
|
||||||
|
private readonly key:string;
|
||||||
|
constructor(private readonly storage:Pick<Storage,'getItem'|'setItem'>,gameid:string|number,agentid:string|number){this.key='tsgame_roomlist_'+gameid+agentid;}
|
||||||
|
read():readonly number[]{
|
||||||
|
const text=this.storage.getItem(this.key);
|
||||||
|
if(!text){this.save([]);return [];}
|
||||||
|
const raw=requireRecord(JSON.parse(text),this.key);
|
||||||
|
if(!Array.isArray(raw.data)||raw.data.some(n=>typeof n!=='number'||!Number.isSafeInteger(n)||n<=0))throw Error('Invalid '+this.key);
|
||||||
|
return Object.freeze([...raw.data]);
|
||||||
|
}
|
||||||
|
private code(value:unknown):number {
|
||||||
|
if(!(typeof value==='string'&&/^[0-9]+$/.test(value)||typeof value==='number'))throw Error('Invalid room history roomcode');
|
||||||
|
const code=Number(value);if(!Number.isSafeInteger(code)||code<=0)throw Error('Invalid room history roomcode');return code;
|
||||||
|
}
|
||||||
|
private save(data:readonly number[]):void{this.storage.setItem(this.key,JSON.stringify({data}));}
|
||||||
|
joined(raw:Readonly<Record<string,unknown>>):void{
|
||||||
|
if(Number(raw.state)===99||Number(raw.state)===0&&Number(raw.isowner)===1){const code=this.code(raw.roomcode);this.save(this.read().filter(n=>n!==code));}
|
||||||
|
}
|
||||||
|
exited(raw:Readonly<Record<string,unknown>>,infinite:number):void{
|
||||||
|
if(Number(raw.isowner)!==1||infinite!==0)return;
|
||||||
|
const code=this.code(raw.roomcode),list=this.read();if(!list.includes(code))this.save([code,...list]);
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "ca074aba-afc9-428f-93ea-199d30dba6ef",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
import type { ChannelIdentity } from '../identity.ts';
|
import type { ChannelIdentity } from '../identity.ts';
|
||||||
|
|
||||||
/** Legacy GameData launchWaitTime (04_Data.js): timer units are milliseconds. */
|
/** Legacy GameData launchWaitTime (04_Data.js): timer units are milliseconds. */
|
||||||
export const DEFAULT_LAUNCH_WAIT_TIME_MS = 10;
|
export const DEFAULT_LAUNCH_WAIT_TIME_MS = 10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建期默认渠道身份(旧 version.js 等价物)。
|
* 构建期默认渠道身份(旧 version.js 等价物)。
|
||||||
* gameid 每子游戏固定写死;agentid/channelid/marketid/version 为默认,运行期由 query/native/profile 覆盖。
|
* gameid 每子游戏固定写死;agentid/channelid/marketid/version 为默认,运行期由 query/native/profile 覆盖。
|
||||||
*/
|
*/
|
||||||
export const BUILD_IDENTITY: ChannelIdentity = {
|
export const BUILD_IDENTITY: ChannelIdentity = {
|
||||||
// This game's entry in config_test/update_jsonv2.txt belongs to this agent/channel.
|
// This game's entry in config_test/update_jsonv2.txt belongs to this agent/channel.
|
||||||
agentid: '1B2h0ccl205c390Y28m1Ajdplkuu4wgy',
|
agentid: '1B2h0ccl205c390Y28m1Ajdplkuu4wgy',
|
||||||
channelid: 'aouv0LotK0pYyQ0Pdrx0CsdcaezfzrcG',
|
channelid: 'aouv0LotK0pYyQ0Pdrx0CsdcaezfzrcG',
|
||||||
gameid: 'J9r4022t80pxlb01zOK76DN1WD4QewFl',
|
// agentid: 'veRa0qrBf0df2K1G4de2tgfmVxB2jxpv',
|
||||||
marketid: 4,
|
// channelid: 'FtJf073aa0d6rI1xD8J1Y42fINTm0ziK',
|
||||||
version: 1, // 数字 versionCode(联调时改成本地服要求的版本号)
|
gameid: 'J9r4022t80pxlb01zOK76DN1WD4QewFl',
|
||||||
};
|
marketid: 4,
|
||||||
|
version: 1, // 数字 versionCode(联调时改成本地服要求的版本号)
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,133 +1,181 @@
|
|||||||
import type { RuntimeConfig } from '../config/runtime-config.ts';
|
import type { RuntimeConfig } from '../config/runtime-config.ts';
|
||||||
import { buildFirstSliceEnvelope } from '../protocol/first-slice-routes.ts';
|
import { buildFirstSliceEnvelope } from '../protocol/first-slice-routes.ts';
|
||||||
import type { OutboundEnvelope } from '../core/types/envelope.ts';
|
import type { OutboundEnvelope } from '../core/types/envelope.ts';
|
||||||
import {
|
import {
|
||||||
buildExitRoomRequest,
|
buildCreateRoomRequest,
|
||||||
buildJoinRoomRequest,
|
type CreateRoomEnvironment,
|
||||||
buildPrepareRequest,
|
buildExitRoomRequest,
|
||||||
type JoinRoomEnvironment,
|
buildJoinRoomRequest,
|
||||||
type RoomIdentity,
|
buildPrepareRequest,
|
||||||
} from '../protocol/contracts/index.ts';
|
type JoinRoomEnvironment,
|
||||||
import { PlatformStore } from './stores/platform-store.ts';
|
type RoomIdentity,
|
||||||
import type { InsideRoomState, PlatformState } from './stores/platform-types.ts';
|
} from '../protocol/contracts/index.ts';
|
||||||
|
import { cloneAndFreezeRoomtype, PlatformStore } from './stores/platform-store.ts';
|
||||||
/** The complete-envelope send capability used by platform command adapters. */
|
import type { InsideRoomState, PlatformState } from './stores/platform-types.ts';
|
||||||
export interface OutboundEnvelopeSendPort {
|
|
||||||
send(envelope: OutboundEnvelope): void;
|
/** The complete-envelope send capability used by platform command adapters. */
|
||||||
}
|
export interface OutboundEnvelopeSendPort {
|
||||||
|
send(envelope: OutboundEnvelope): void;
|
||||||
export interface PlatformCommandsOptions {
|
}
|
||||||
readonly runtimeConfig: RuntimeConfig;
|
|
||||||
readonly store: PlatformStore;
|
export interface PlatformCommandsOptions {
|
||||||
readonly send: OutboundEnvelopeSendPort;
|
readonly runtimeConfig: RuntimeConfig;
|
||||||
}
|
readonly store: PlatformStore;
|
||||||
|
readonly send: OutboundEnvelopeSendPort;
|
||||||
export interface JoinRoomCommand extends JoinRoomEnvironment {
|
}
|
||||||
readonly roomcode: string;
|
|
||||||
}
|
export interface JoinRoomCommand extends JoinRoomEnvironment {
|
||||||
|
readonly roomcode: string | number;
|
||||||
export class UnsupportedGameHostCommandError extends Error {
|
}
|
||||||
constructor(message: string) {
|
|
||||||
super(message);
|
export interface CreateRoomCommand extends CreateRoomEnvironment {}
|
||||||
this.name = 'UnsupportedGameHostCommandError';
|
|
||||||
}
|
export class UnsupportedGameHostCommandError extends Error {
|
||||||
}
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
/** Builds platform-owned room requests exclusively through the first-slice contracts. */
|
this.name = 'UnsupportedGameHostCommandError';
|
||||||
export class PlatformCommands {
|
}
|
||||||
constructor(private readonly options: PlatformCommandsOptions) {}
|
}
|
||||||
|
|
||||||
getVipRooms(shortcodeList:readonly (string|number)[]):void {
|
/** Builds platform-owned room requests exclusively through the first-slice contracts. */
|
||||||
this.taskPlayerId();
|
export class PlatformCommands {
|
||||||
const {agentid,gameid}=this.options.runtimeConfig.identity;
|
constructor(private readonly options: PlatformCommandsOptions) {}
|
||||||
this.options.send.send(buildFirstSliceEnvelope('getInfoByShortCode',{agentid,gameid,shortcodeList:[...shortcodeList]}));
|
|
||||||
}
|
getPlayerInvitecode():void {
|
||||||
|
const playerid=this.taskPlayerId(),player=this.options.store.getState().players.entities[playerid];
|
||||||
joinVipRoom(roomcode:string|number,location:unknown):void {
|
const {openid,unionid}=player;
|
||||||
const playerid=this.taskPlayerId(),state=this.options.store.getState();
|
if(typeof openid!=='string'||!(typeof unionid==='string'||typeof unionid==='number'&&Number.isFinite(unionid)))throw Error('Invite query requires login openid and unionid');
|
||||||
const ip=state.players.entities[playerid].ip;
|
this.options.send.send(buildFirstSliceEnvelope('get_player_invitecode',{
|
||||||
if(typeof ip!=='string')throw Error('VIP join requires player login ip');
|
agentid:this.options.runtimeConfig.identity.agentid,playerid,unionid,openid,
|
||||||
const {agentid,gameid}=this.options.runtimeConfig.identity;
|
}));
|
||||||
this.options.send.send(buildFirstSliceEnvelope('self_join_room',{agentid,playerid,gameid,roomcode,joinmode:0,location,ip}));
|
}
|
||||||
}
|
|
||||||
|
bindInvitecode(invitecode:string):void {
|
||||||
getPlayerTasks(): void {
|
const playerid=this.taskPlayerId();
|
||||||
const playerid=this.taskPlayerId();
|
if(typeof invitecode!=='string')throw Error('invitecode must be a string');
|
||||||
const {agentid,gameid}=this.options.runtimeConfig.identity;
|
this.options.send.send(buildFirstSliceEnvelope('binding_invitecode',{
|
||||||
this.options.send.send(buildFirstSliceEnvelope('get_player_task',{agentid,playerid,gameid}));
|
agentid:this.options.runtimeConfig.identity.agentid,playerid,invitecode,
|
||||||
}
|
}));
|
||||||
|
}
|
||||||
claimTask(taskid:number|string):void {
|
|
||||||
const playerid=this.taskPlayerId();
|
getVipRooms(shortcodeList:readonly (string|number)[]):void {
|
||||||
const task=this.options.store.getState().tasks?.find(task=>String(task.taskid)===String(taskid));
|
this.taskPlayerId();
|
||||||
if(!task||task.state!==1)throw Error('Task is not claimable');
|
const {agentid,gameid}=this.options.runtimeConfig.identity;
|
||||||
// 11_GameUI claim click sends these three fields; Net._SendData adds none.
|
this.options.send.send(buildFirstSliceEnvelope('getInfoByShortCode',{agentid,gameid,shortcodeList:[...shortcodeList]}));
|
||||||
this.options.send.send(buildFirstSliceEnvelope('get_task_award',{
|
}
|
||||||
agentid:this.options.runtimeConfig.identity.agentid,playerid,taskid:task.taskid,
|
|
||||||
}));
|
joinVipRoom(roomcode:string|number,location:unknown):void {
|
||||||
}
|
const playerid=this.taskPlayerId(),state=this.options.store.getState();
|
||||||
|
const ip=state.players.entities[playerid].ip;
|
||||||
private taskPlayerId():number {
|
if(typeof ip!=='string')throw Error('VIP join requires player login ip');
|
||||||
const state=this.options.store.getState();
|
const {agentid,gameid}=this.options.runtimeConfig.identity;
|
||||||
if(state.app.phase!=='logged-in'||state.room.kind!=='outside')throw Error('Task command requires logged-in lobby');
|
this.options.send.send(buildFirstSliceEnvelope('self_join_room',{agentid,playerid,gameid,roomcode,joinmode:0,location,ip}));
|
||||||
return this.requireAuthenticatedPlayerId(state);
|
}
|
||||||
}
|
|
||||||
|
getPlayerTasks(): void {
|
||||||
prepare(): void {
|
const playerid=this.taskPlayerId();
|
||||||
const state = this.options.store.getState();
|
const {agentid,gameid}=this.options.runtimeConfig.identity;
|
||||||
this.options.send.send(buildPrepareRequest(
|
this.options.send.send(buildFirstSliceEnvelope('get_player_task',{agentid,playerid,gameid}));
|
||||||
this.options.runtimeConfig,
|
}
|
||||||
this.roomIdentity(state, this.requireInsideRoom(state)),
|
|
||||||
));
|
getVipRankList(limit:number):void {
|
||||||
}
|
this.taskPlayerId();if(!Number.isInteger(limit)||limit<=0)throw Error('Invalid vipRankLimit');
|
||||||
|
this.options.send.send(buildFirstSliceEnvelope('getVipRankList',{agentid:this.options.runtimeConfig.identity.agentid,limit}));
|
||||||
exitRoom(): void {
|
}
|
||||||
const state = this.options.store.getState();
|
joinOwnedRoom(roomcode:number,location:unknown):void {
|
||||||
const room = this.requireInsideRoom(state);
|
const playerid=this.taskPlayerId(),state=this.options.store.getState();
|
||||||
if (room.stage !== 0 && room.infinite !== 1) {
|
if(!Number.isSafeInteger(roomcode)||roomcode<=0)throw Error('Invalid owned roomcode');
|
||||||
throw new UnsupportedGameHostCommandError('room.exit requires stage=0 unless infinite=1');
|
const ip=state.players.entities[playerid].ip;if(typeof ip!=='string')throw Error('Owned room join requires player login ip');
|
||||||
}
|
const {agentid,gameid}=this.options.runtimeConfig.identity;
|
||||||
this.options.send.send(buildExitRoomRequest(
|
this.options.send.send(buildFirstSliceEnvelope('self_join_room',{agentid,playerid,gameid,roomcode,joinmode:1,location,ip}));
|
||||||
this.options.runtimeConfig,
|
}
|
||||||
this.roomIdentity(state, room),
|
getTreasureList():void {
|
||||||
));
|
this.taskPlayerId();const {agentid,gameid}=this.options.runtimeConfig.identity;
|
||||||
}
|
this.options.send.send(buildFirstSliceEnvelope('get_treasurelist',{agentid,gameid}));
|
||||||
|
}
|
||||||
joinRoom(command: JoinRoomCommand): void {
|
redeemCard(cardno:string):void {
|
||||||
const state = this.options.store.getState();
|
const playerid=this.taskPlayerId();
|
||||||
if (state.room.kind !== 'outside') {
|
if(typeof cardno!=='string'||cardno===''||/^[ ]+$/.test(cardno))throw Error('Invalid cardno');
|
||||||
throw new UnsupportedGameHostCommandError('room.join requires player to be outside a room');
|
this.options.send.send(buildFirstSliceEnvelope('topup_card',{agentid:this.options.runtimeConfig.identity.agentid,playerid,cardno}));
|
||||||
}
|
}
|
||||||
const playerid = this.requireAuthenticatedPlayerId(state);
|
|
||||||
this.options.send.send(buildJoinRoomRequest(
|
claimTask(taskid:number|string):void {
|
||||||
this.options.runtimeConfig,
|
const playerid=this.taskPlayerId();
|
||||||
{ playerid, roomcode: command.roomcode },
|
const task=this.options.store.getState().tasks?.find(task=>String(task.taskid)===String(taskid));
|
||||||
command,
|
if(!task||task.state!==1)throw Error('Task is not claimable');
|
||||||
));
|
// 11_GameUI claim click sends these three fields; Net._SendData adds none.
|
||||||
}
|
this.options.send.send(buildFirstSliceEnvelope('get_task_award',{
|
||||||
|
agentid:this.options.runtimeConfig.identity.agentid,playerid,taskid:task.taskid,
|
||||||
private requireInsideRoom(state: PlatformState): InsideRoomState {
|
}));
|
||||||
if (state.room.kind !== 'inside') {
|
}
|
||||||
throw new UnsupportedGameHostCommandError('room command requires player to be inside a room');
|
|
||||||
}
|
private taskPlayerId():number {
|
||||||
return state.room;
|
const state=this.options.store.getState();
|
||||||
}
|
if(state.app.phase!=='logged-in'||state.room.kind!=='outside')throw Error('Task command requires logged-in lobby');
|
||||||
|
return this.requireAuthenticatedPlayerId(state);
|
||||||
private requireAuthenticatedPlayerId(state: PlatformState): number {
|
}
|
||||||
const playerid = state.players.selfPlayerId;
|
|
||||||
if (playerid === null) {
|
prepare(): void {
|
||||||
throw new UnsupportedGameHostCommandError('room command requires an authenticated player');
|
const state = this.options.store.getState();
|
||||||
}
|
this.options.send.send(buildPrepareRequest(
|
||||||
if (state.players.entities[playerid] === undefined) {
|
this.options.runtimeConfig,
|
||||||
throw new Error('players.entities: missing authenticated player');
|
this.roomIdentity(state, this.requireInsideRoom(state)),
|
||||||
}
|
));
|
||||||
return playerid;
|
}
|
||||||
}
|
|
||||||
|
exitRoom(): void {
|
||||||
private roomIdentity(state: PlatformState, room: InsideRoomState): RoomIdentity {
|
const state = this.options.store.getState();
|
||||||
return {
|
const room = this.requireInsideRoom(state);
|
||||||
playerid: this.requireAuthenticatedPlayerId(state),
|
if (room.stage !== 0 && room.infinite !== 1) {
|
||||||
roomcode: room.roomcode,
|
throw new UnsupportedGameHostCommandError('room.exit requires stage=0 unless infinite=1');
|
||||||
};
|
}
|
||||||
}
|
this.options.send.send(buildExitRoomRequest(
|
||||||
}
|
this.options.runtimeConfig,
|
||||||
|
this.roomIdentity(state, room),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
createRoom(command: CreateRoomCommand): void {
|
||||||
|
const playerid = this.taskPlayerId();
|
||||||
|
const roomtype = cloneAndFreezeRoomtype(command.roomtype);
|
||||||
|
this.options.send.send(buildCreateRoomRequest(this.options.runtimeConfig, playerid, {...command, roomtype}));
|
||||||
|
}
|
||||||
|
|
||||||
|
joinRoom(command: JoinRoomCommand): void {
|
||||||
|
const state = this.options.store.getState();
|
||||||
|
if (state.room.kind !== 'outside') {
|
||||||
|
throw new UnsupportedGameHostCommandError('room.join requires player to be outside a room');
|
||||||
|
}
|
||||||
|
const playerid = this.requireAuthenticatedPlayerId(state);
|
||||||
|
this.options.send.send(buildJoinRoomRequest(
|
||||||
|
this.options.runtimeConfig,
|
||||||
|
{ playerid, roomcode: command.roomcode },
|
||||||
|
command,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private requireInsideRoom(state: PlatformState): InsideRoomState {
|
||||||
|
if (state.room.kind !== 'inside') {
|
||||||
|
throw new UnsupportedGameHostCommandError('room command requires player to be inside a room');
|
||||||
|
}
|
||||||
|
return state.room;
|
||||||
|
}
|
||||||
|
|
||||||
|
private requireAuthenticatedPlayerId(state: PlatformState): number {
|
||||||
|
const playerid = state.players.selfPlayerId;
|
||||||
|
if (playerid === null) {
|
||||||
|
throw new UnsupportedGameHostCommandError('room command requires an authenticated player');
|
||||||
|
}
|
||||||
|
if (state.players.entities[playerid] === undefined) {
|
||||||
|
throw new Error('players.entities: missing authenticated player');
|
||||||
|
}
|
||||||
|
return playerid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private roomIdentity(state: PlatformState, room: InsideRoomState): RoomIdentity {
|
||||||
|
return {
|
||||||
|
playerid: this.requireAuthenticatedPlayerId(state),
|
||||||
|
roomcode: room.roomcode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,243 +1,276 @@
|
|||||||
import type { GameHost } from '../sdk/contracts/index.ts';
|
import type { GameHost } from '../sdk/contracts/index.ts';
|
||||||
import {
|
import {
|
||||||
extractGameSessionOpenFaultErrors,
|
extractGameSessionOpenFaultErrors,
|
||||||
GameSessionHost,
|
GameSessionHost,
|
||||||
} from '../sdk/runtime/game-session-host.ts';
|
} from '../sdk/runtime/game-session-host.ts';
|
||||||
import {
|
import {
|
||||||
parseLoginResponse,
|
parseCreateRoomResponse,
|
||||||
parseSelfJoinRoomResponse,
|
parseLoginResponse,
|
||||||
type ParsedSelfExitPayload,
|
parseSelfJoinRoomResponse,
|
||||||
type ReconnectSnapshot,
|
type ParsedSelfExitPayload,
|
||||||
} from '../protocol/contracts/index.ts';
|
type ReconnectSnapshot,
|
||||||
import { PlatformStore } from './stores/platform-store.ts';
|
} from '../protocol/contracts/index.ts';
|
||||||
import type { PlatformState } from './stores/platform-types.ts';
|
import { cloneAndFreezeRoomtype, PlatformStore } from './stores/platform-store.ts';
|
||||||
|
import type { PlatformState } from './stores/platform-types.ts';
|
||||||
export interface GameHostLeasePort {
|
import type {LoginAccountIdentity} from '../protocol/contracts/login-contract.ts';
|
||||||
readonly host: GameHost;
|
|
||||||
invalidate(): void;
|
export interface GameHostLeasePort {
|
||||||
}
|
readonly host: GameHost;
|
||||||
|
invalidate(): void;
|
||||||
export type GameHostLeaseFactory = () => GameHostLeasePort;
|
}
|
||||||
|
|
||||||
/** A valid nonzero server outcome, delivered without changing canonical state. */
|
export type GameHostLeaseFactory = () => GameHostLeasePort;
|
||||||
export interface ServerDenial {
|
|
||||||
readonly rpc: 'player_login' | 'self_join_room';
|
/** A valid nonzero server outcome, delivered without changing canonical state. */
|
||||||
readonly state: number;
|
export interface ServerDenial {
|
||||||
readonly data: Readonly<Record<string, unknown>>;
|
readonly rpc: 'player_login' | 'self_join_room' | 'create_room';
|
||||||
}
|
readonly state: number;
|
||||||
|
readonly data: Readonly<Record<string, unknown>>;
|
||||||
export interface RuntimeSessionOptions {
|
}
|
||||||
readonly store: PlatformStore;
|
|
||||||
readonly gameSession: GameSessionHost;
|
export interface RuntimeSessionOptions {
|
||||||
readonly createGameHostLease: GameHostLeaseFactory;
|
readonly roomHistory?: import('../adapters/native/owned-room-history.ts').OwnedRoomHistory;
|
||||||
readonly onServerDenial: (outcome: ServerDenial) => void;
|
readonly store: PlatformStore;
|
||||||
}
|
readonly gameSession: GameSessionHost;
|
||||||
|
readonly createGameHostLease: GameHostLeaseFactory;
|
||||||
export type RuntimeSessionState = 'operational' | 'faulted' | 'kicked';
|
readonly onServerDenial: (outcome: ServerDenial) => void;
|
||||||
|
}
|
||||||
export class RuntimeSessionFault extends Error {
|
|
||||||
readonly errors: readonly unknown[];
|
export type RuntimeSessionState = 'operational' | 'faulted' | 'kicked';
|
||||||
|
|
||||||
constructor(operation: string, errors: readonly unknown[]) {
|
export class RuntimeSessionFault extends Error {
|
||||||
super(`RuntimeSession fault during ${operation}`);
|
readonly errors: readonly unknown[];
|
||||||
this.name = 'RuntimeSessionFault';
|
|
||||||
this.errors = Object.freeze([...errors]);
|
constructor(operation: string, errors: readonly unknown[]) {
|
||||||
}
|
super(`RuntimeSession fault during ${operation}`);
|
||||||
}
|
this.name = 'RuntimeSessionFault';
|
||||||
|
this.errors = Object.freeze([...errors]);
|
||||||
/** Coordinates the canonical platform snapshot with the one active game session. */
|
}
|
||||||
export class RuntimeSession {
|
}
|
||||||
private loginIsPending = false;
|
|
||||||
private wasKicked = false;
|
/** Coordinates the canonical platform snapshot with the one active game session. */
|
||||||
private terminalFault: RuntimeSessionFault | null = null;
|
export class RuntimeSession {
|
||||||
private activeLease: GameHostLeasePort | null = null;
|
private loginIsPending = false;
|
||||||
|
private lastJoinedRoomcode: string | null = null;
|
||||||
constructor(private readonly options: RuntimeSessionOptions) {}
|
/** Non-device login owns identity in authorization; device login owns it in the response. */
|
||||||
|
private authorizedIdentity:Pick<LoginAccountIdentity,'openid'|'unionid'>|undefined;
|
||||||
get loginPending(): boolean { return this.loginIsPending; }
|
private wasKicked = false;
|
||||||
|
private terminalFault: RuntimeSessionFault | null = null;
|
||||||
get kicked(): boolean { return this.wasKicked; }
|
private activeLease: GameHostLeasePort | null = null;
|
||||||
|
|
||||||
get state(): RuntimeSessionState {
|
constructor(private readonly options: RuntimeSessionOptions) {}
|
||||||
if (this.wasKicked) return 'kicked';
|
|
||||||
return this.terminalFault !== null ? 'faulted' : 'operational';
|
get loginPending(): boolean { return this.loginIsPending; }
|
||||||
}
|
|
||||||
|
get kicked(): boolean { return this.wasKicked; }
|
||||||
get fault(): Error | null { return this.terminalFault; }
|
|
||||||
|
get state(): RuntimeSessionState {
|
||||||
markLoginPending(): void {
|
if (this.wasKicked) return 'kicked';
|
||||||
this.requireOperational();
|
return this.terminalFault !== null ? 'faulted' : 'operational';
|
||||||
this.loginIsPending = true;
|
}
|
||||||
}
|
|
||||||
|
get fault(): Error | null { return this.terminalFault; }
|
||||||
handleLogin(data: unknown): void {
|
|
||||||
this.requireOperational();
|
markLoginPending(identity?:Pick<LoginAccountIdentity,'openid'|'unionid'>|null): void {
|
||||||
this.loginIsPending = false;
|
this.requireOperational();
|
||||||
const parsed = parseLoginResponse(data);
|
// No argument is a reconnect of the current account; null selects device response identity.
|
||||||
if (parsed.state !== 0) {
|
if(identity!==undefined)this.authorizedIdentity=identity===null?undefined:identity;
|
||||||
this.options.onServerDenial({ rpc: 'player_login', state: parsed.state, data: parsed.raw });
|
this.loginIsPending = true;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
const committed = this.options.store.applyLoginSuccess(parsed);
|
handleLogin(data: unknown): void {
|
||||||
if (parsed.room === null) {
|
this.requireOperational();
|
||||||
this.releaseOrFault('login lobby replacement');
|
this.loginIsPending = false;
|
||||||
return;
|
const parsed = parseLoginResponse(data);
|
||||||
}
|
if (parsed.state !== 0) {
|
||||||
this.openCommittedRoom(committed, parsed.reconnect, 'login room open');
|
this.options.onServerDenial({ rpc: 'player_login', state: parsed.state, data: parsed.raw });
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
handleSelfJoin(data: unknown): void {
|
this.lastJoinedRoomcode = null;
|
||||||
this.requireOperational();
|
const committed = this.options.store.applyLoginSuccess(parsed,this.authorizedIdentity);
|
||||||
const parsed = parseSelfJoinRoomResponse(data);
|
if (parsed.room === null) {
|
||||||
if (parsed.state !== 0) {
|
this.releaseOrFault('login lobby replacement');
|
||||||
this.options.onServerDenial({ rpc: 'self_join_room', state: parsed.state, data: parsed.raw });
|
return;
|
||||||
return;
|
}
|
||||||
}
|
this.openCommittedRoom(committed, parsed.reconnect, 'login room open');
|
||||||
const committed = this.options.store.replaceRoom(parsed);
|
}
|
||||||
this.openCommittedRoom(committed, parsed.reconnect, 'self join room open');
|
|
||||||
}
|
handleCreateRoom(data: unknown): void {
|
||||||
|
this.requireOperational();
|
||||||
handleSelfExit(parsed: ParsedSelfExitPayload): void {
|
const parsed = parseCreateRoomResponse(data);
|
||||||
this.requireOperational();
|
if (parsed.state !== 0) {
|
||||||
const state = this.options.store.getState();
|
this.options.onServerDenial({rpc: 'create_room', state: parsed.state, data: parsed.raw});
|
||||||
if (state.room.kind !== 'inside') {
|
return;
|
||||||
if (this.options.gameSession.state === 'idle' && this.activeLease === null) return;
|
}
|
||||||
const errors = [
|
if (parsed.room === null) throw new Error('create_room missing parsed room');
|
||||||
new Error('self_exit_room found an active game without room state'),
|
const current = this.options.store.getState().room;
|
||||||
...this.releaseGame(),
|
if (current.kind === 'inside' && current.roomcode === parsed.room.roomcode
|
||||||
];
|
&& this.options.gameSession.state === 'active') return;
|
||||||
this.throwFault('self exit inconsistent state', errors);
|
// Subgame code receives an owned immutable value, never the server response graph.
|
||||||
}
|
const roomtype = cloneAndFreezeRoomtype(parsed.room.roomtype);
|
||||||
if (parsed.seat !== undefined && parsed.seat !== state.room.selfSeat) {
|
const count = this.options.gameSession.entry.resolveSeatCount(roomtype);
|
||||||
throw new RangeError(
|
const committed = this.options.store.createRoom(parsed, count);
|
||||||
`self_exit_room seat mismatch: expected ${state.room.selfSeat}, received ${parsed.seat}`,
|
this.openCommittedRoom(committed, {present: false}, 'create room open');
|
||||||
);
|
}
|
||||||
}
|
|
||||||
if (this.options.gameSession.state !== 'active' || this.activeLease === null) {
|
handleSelfJoin(data: unknown): void {
|
||||||
const errors = [
|
this.requireOperational();
|
||||||
new Error('self_exit_room requires an active game session and host lease'),
|
const parsed = parseSelfJoinRoomResponse(data);
|
||||||
...this.releaseGame(),
|
this.options.roomHistory?.joined(parsed.raw);
|
||||||
];
|
if (parsed.state !== 0) {
|
||||||
this.throwFault('self exit inconsistent state', errors);
|
this.options.onServerDenial({ rpc: 'self_join_room', state: parsed.state, data: parsed.raw });
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
try {
|
if (parsed.room !== null && this.lastJoinedRoomcode === parsed.room.roomcode
|
||||||
this.options.store.clearRoom();
|
&& this.options.gameSession.state === 'active') return;
|
||||||
} catch (error) {
|
const committed = this.options.store.replaceRoom(parsed);
|
||||||
this.throwFault('self exit state commit', [error, ...this.releaseGame()]);
|
this.openCommittedRoom(committed, parsed.reconnect, 'self join room open');
|
||||||
}
|
this.lastJoinedRoomcode = parsed.room!.roomcode;
|
||||||
this.releaseOrFault('self exit cleanup');
|
}
|
||||||
}
|
|
||||||
|
handleSelfExit(parsed: ParsedSelfExitPayload): void {
|
||||||
handleServerSwitch(switchConnection: () => void): void {
|
this.requireOperational();
|
||||||
this.requireOperational();
|
const state = this.options.store.getState();
|
||||||
const errors = this.releaseGame();
|
if (state.room.kind !== 'inside') {
|
||||||
try {
|
if (this.options.gameSession.state === 'idle' && this.activeLease === null) return;
|
||||||
switchConnection();
|
const errors = [
|
||||||
} catch (error) {
|
new Error('self_exit_room found an active game without room state'),
|
||||||
errors.push(error);
|
...this.releaseGame(),
|
||||||
}
|
];
|
||||||
if (errors.length > 0) this.throwFault('server switch', errors);
|
this.throwFault('self exit inconsistent state', errors);
|
||||||
}
|
}
|
||||||
|
if (parsed.seat !== undefined && parsed.seat !== state.room.selfSeat) {
|
||||||
handleKick(_data: unknown, stopConnection: () => void): void {
|
throw new RangeError(
|
||||||
if (this.wasKicked) return;
|
`self_exit_room seat mismatch: expected ${state.room.selfSeat}, received ${parsed.seat}`,
|
||||||
this.requireOperational();
|
);
|
||||||
this.loginIsPending = false;
|
}
|
||||||
this.wasKicked = true;
|
if (this.options.gameSession.state !== 'active' || this.activeLease === null) {
|
||||||
|
const errors = [
|
||||||
const errors = this.releaseGame();
|
new Error('self_exit_room requires an active game session and host lease'),
|
||||||
try {
|
...this.releaseGame(),
|
||||||
stopConnection();
|
];
|
||||||
} catch (error) {
|
this.throwFault('self exit inconsistent state', errors);
|
||||||
errors.push(error);
|
}
|
||||||
}
|
|
||||||
if (errors.length > 0) this.throwFault('kick terminal cleanup', errors);
|
try {
|
||||||
}
|
this.options.roomHistory?.exited(parsed.raw,state.room.infinite);
|
||||||
|
this.options.store.clearRoom();
|
||||||
private openCommittedRoom(
|
} catch (error) {
|
||||||
state: PlatformState,
|
this.throwFault('self exit state commit', [error, ...this.releaseGame()]);
|
||||||
reconnect: ReconnectSnapshot,
|
}
|
||||||
operation: string,
|
this.releaseOrFault('self exit cleanup');
|
||||||
): void {
|
}
|
||||||
if (state.room.kind !== 'inside') {
|
|
||||||
this.throwFault(operation, [new Error('Cannot open a game session without committed room state')]);
|
handleServerSwitch(switchConnection: () => void): void {
|
||||||
}
|
this.requireOperational();
|
||||||
|
const errors = this.releaseGame();
|
||||||
const priorCleanupErrors = this.releaseGame();
|
try {
|
||||||
if (priorCleanupErrors.length > 0) this.throwFault(operation, priorCleanupErrors);
|
switchConnection();
|
||||||
if (this.gameWasDisposed()) return;
|
} catch (error) {
|
||||||
|
errors.push(error);
|
||||||
let lease: GameHostLeasePort;
|
}
|
||||||
try {
|
if (errors.length > 0) this.throwFault('server switch', errors);
|
||||||
lease = this.options.createGameHostLease();
|
}
|
||||||
if (!lease || typeof lease !== 'object' || typeof lease.invalidate !== 'function') {
|
|
||||||
throw new TypeError('createGameHostLease must return { host, invalidate }');
|
handleKick(_data: unknown, stopConnection: () => void): void {
|
||||||
}
|
if (this.wasKicked) return;
|
||||||
} catch (error) {
|
this.requireOperational();
|
||||||
this.throwFault(operation, [error]);
|
this.loginIsPending = false;
|
||||||
}
|
this.wasKicked = true;
|
||||||
this.activeLease = lease;
|
|
||||||
|
const errors = this.releaseGame();
|
||||||
try {
|
try {
|
||||||
this.options.gameSession.open(lease.host);
|
stopConnection();
|
||||||
if (this.gameWasDisposed()) return;
|
} catch (error) {
|
||||||
this.options.gameSession.publish({
|
errors.push(error);
|
||||||
type: 'room.entered',
|
}
|
||||||
roomtype: state.room.roomtype,
|
if (errors.length > 0) this.throwFault('kick terminal cleanup', errors);
|
||||||
});
|
}
|
||||||
if (this.gameWasDisposed()) return;
|
|
||||||
if (reconnect.present) this.options.gameSession.restore(reconnect.value);
|
private openCommittedRoom(
|
||||||
} catch (error) {
|
state: PlatformState,
|
||||||
const openErrors = extractGameSessionOpenFaultErrors(error);
|
reconnect: ReconnectSnapshot,
|
||||||
const errors = openErrors === null
|
operation: string,
|
||||||
? [error]
|
): void {
|
||||||
: [openErrors[0], openErrors[1]];
|
if (state.room.kind !== 'inside') {
|
||||||
errors.push(...this.releaseGame());
|
this.throwFault(operation, [new Error('Cannot open a game session without committed room state')]);
|
||||||
this.throwFault(operation, errors);
|
}
|
||||||
}
|
|
||||||
}
|
const priorCleanupErrors = this.releaseGame();
|
||||||
|
if (priorCleanupErrors.length > 0) this.throwFault(operation, priorCleanupErrors);
|
||||||
private releaseOrFault(operation: string): void {
|
if (this.gameWasDisposed()) return;
|
||||||
const errors = this.releaseGame();
|
|
||||||
if (errors.length > 0) this.throwFault(operation, errors);
|
let lease: GameHostLeasePort;
|
||||||
}
|
try {
|
||||||
|
lease = this.options.createGameHostLease();
|
||||||
private gameWasDisposed(): boolean {
|
if (!lease || typeof lease !== 'object' || typeof lease.invalidate !== 'function') {
|
||||||
return this.options.gameSession.state === 'disposed';
|
throw new TypeError('createGameHostLease must return { host, invalidate }');
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
private releaseGame(): unknown[] {
|
this.throwFault(operation, [error]);
|
||||||
const errors: unknown[] = [];
|
}
|
||||||
const lease = this.activeLease;
|
this.activeLease = lease;
|
||||||
this.activeLease = null;
|
|
||||||
|
try {
|
||||||
if (this.options.gameSession.state !== 'idle' && !this.gameWasDisposed()) {
|
this.options.gameSession.open(lease.host);
|
||||||
try {
|
if (this.gameWasDisposed()) return;
|
||||||
this.options.gameSession.close();
|
this.options.gameSession.publish({
|
||||||
} catch (error) {
|
type: 'room.entered',
|
||||||
errors.push(error);
|
roomtype: state.room.roomtype,
|
||||||
}
|
});
|
||||||
}
|
if (this.gameWasDisposed()) return;
|
||||||
if (lease !== null) {
|
if (reconnect.present) this.options.gameSession.restore(reconnect.value);
|
||||||
try {
|
} catch (error) {
|
||||||
lease.invalidate();
|
const openErrors = extractGameSessionOpenFaultErrors(error);
|
||||||
} catch (error) {
|
const errors = openErrors === null
|
||||||
errors.push(error);
|
? [error]
|
||||||
}
|
: [openErrors[0], openErrors[1]];
|
||||||
}
|
errors.push(...this.releaseGame());
|
||||||
return errors;
|
this.throwFault(operation, errors);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private throwFault(operation: string, errors: readonly unknown[]): never {
|
|
||||||
const fault = new RuntimeSessionFault(operation, errors);
|
private releaseOrFault(operation: string): void {
|
||||||
this.loginIsPending = false;
|
const errors = this.releaseGame();
|
||||||
this.terminalFault = fault;
|
if (errors.length > 0) this.throwFault(operation, errors);
|
||||||
throw fault;
|
}
|
||||||
}
|
|
||||||
|
private gameWasDisposed(): boolean {
|
||||||
private requireOperational(): void {
|
return this.options.gameSession.state === 'disposed';
|
||||||
if (this.terminalFault !== null) throw this.terminalFault;
|
}
|
||||||
if (this.wasKicked) throw new Error('RuntimeSession is kicked');
|
|
||||||
}
|
private releaseGame(): unknown[] {
|
||||||
}
|
this.lastJoinedRoomcode = null;
|
||||||
|
const errors: unknown[] = [];
|
||||||
|
const lease = this.activeLease;
|
||||||
|
this.activeLease = null;
|
||||||
|
|
||||||
|
if (this.options.gameSession.state !== 'idle' && !this.gameWasDisposed()) {
|
||||||
|
try {
|
||||||
|
this.options.gameSession.close();
|
||||||
|
} catch (error) {
|
||||||
|
errors.push(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lease !== null) {
|
||||||
|
try {
|
||||||
|
lease.invalidate();
|
||||||
|
} catch (error) {
|
||||||
|
errors.push(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private throwFault(operation: string, errors: readonly unknown[]): never {
|
||||||
|
const fault = new RuntimeSessionFault(operation, errors);
|
||||||
|
this.loginIsPending = false;
|
||||||
|
this.terminalFault = fault;
|
||||||
|
throw fault;
|
||||||
|
}
|
||||||
|
|
||||||
|
private requireOperational(): void {
|
||||||
|
if (this.terminalFault !== null) throw this.terminalFault;
|
||||||
|
if (this.wasKicked) throw new Error('RuntimeSession is kicked');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,16 @@
|
|||||||
import type { ServerDenial } from './runtime-session.ts';
|
import type { ServerDenial } from './runtime-session.ts';
|
||||||
|
|
||||||
/** Semantic scene intents; the headless runtime never imports Cocos. */
|
/** Semantic scene intents; the headless runtime never imports Cocos. */
|
||||||
export interface ScenePort {
|
export interface ScenePort {
|
||||||
showLoading(): void;
|
showLoading(): void;
|
||||||
showLogin(): void;
|
showLogin(): void;
|
||||||
showLobby(): void;
|
showLobby(): void;
|
||||||
showRoom(): void;
|
showRoom(): void;
|
||||||
showReconnect(): void;
|
showReconnect(): void;
|
||||||
showKicked(data: unknown): void;
|
showKicked(data: unknown): void;
|
||||||
/** Required when the host supports the server's generic message UI. */
|
/** Required when the host supports the server's generic message UI. */
|
||||||
showTips?(message: string, time: number, source?:'show_message'|'update_roomcard'): void;
|
showTips?(message: string, time: number, source?:'show_message'|'update_roomcard'|'update_bean'|'binding_invitecode'): void;
|
||||||
/** Recoverable server result; the consumer interprets the original failure payload. */
|
/** Recoverable server result; the consumer interprets the original failure payload. */
|
||||||
showServerDenial(outcome: ServerDenial): void;
|
showServerDenial(outcome: ServerDenial): void;
|
||||||
showFatal(error: Error): void;
|
showFatal(error: Error): void;
|
||||||
}
|
}
|
||||||
|
|||||||
+919
-836
File diff suppressed because it is too large
Load Diff
+66
-59
@@ -1,61 +1,68 @@
|
|||||||
import type { PlatformGameConnectionPhase } from '../../sdk/contracts/snapshots.ts';
|
import type { PlatformGameConnectionPhase } from '../../sdk/contracts/snapshots.ts';
|
||||||
import type { TaskData } from '../../core/types/task.ts';
|
import type { TaskData } from '../../core/types/task.ts';
|
||||||
|
|
||||||
export type PlatformConnectionPhase =
|
export type PlatformConnectionPhase =
|
||||||
| 'idle'
|
| 'idle'
|
||||||
| 'connecting'
|
| 'connecting'
|
||||||
| PlatformGameConnectionPhase;
|
| PlatformGameConnectionPhase;
|
||||||
|
|
||||||
export interface PlatformAppState {
|
export interface PlatformAppState {
|
||||||
readonly phase: PlatformConnectionPhase;
|
readonly phase: PlatformConnectionPhase;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PlatformPlayer {
|
export interface PlatformPlayer {
|
||||||
readonly playerid: number;
|
readonly playerid: number;
|
||||||
readonly nickname: string;
|
readonly nickname: string;
|
||||||
readonly avatar: string;
|
readonly avatar: string;
|
||||||
readonly sex: number;
|
readonly sex: number;
|
||||||
readonly bean: number;
|
readonly bean: number;
|
||||||
/** Account-only fields are absent on other room players; never default them in views. */
|
/** Account-only fields are absent on other room players; never default them in views. */
|
||||||
readonly roomcard?: number;
|
readonly roomcard?: number;
|
||||||
readonly taskstate?: number;
|
readonly openid?: string;
|
||||||
readonly advanced?: number;
|
readonly unionid?: string | number;
|
||||||
readonly score?: number;
|
readonly invitecode?: string | null;
|
||||||
readonly sign?: string | null;
|
readonly taskstate?: number;
|
||||||
readonly ip?: string;
|
readonly advanced?: number;
|
||||||
readonly isprepare?: number;
|
readonly score?: number;
|
||||||
readonly onstate?: number;
|
readonly sign?: string | null;
|
||||||
}
|
readonly ip?: string;
|
||||||
|
readonly isprepare?: number;
|
||||||
export interface PlayerDirectoryState {
|
readonly onstate?: number;
|
||||||
readonly selfPlayerId: number | null;
|
}
|
||||||
readonly entities: Readonly<Record<number, PlatformPlayer>>;
|
|
||||||
}
|
export interface PlayerDirectoryState {
|
||||||
|
readonly selfPlayerId: number | null;
|
||||||
export interface OutsideRoomState {
|
readonly entities: Readonly<Record<number, PlatformPlayer>>;
|
||||||
readonly kind: 'outside';
|
}
|
||||||
}
|
|
||||||
|
export interface OutsideRoomState {
|
||||||
export interface InsideRoomState {
|
readonly kind: 'outside';
|
||||||
readonly kind: 'inside';
|
}
|
||||||
readonly roomcode: string;
|
|
||||||
readonly selfSeat: number;
|
export interface InsideRoomState {
|
||||||
readonly seatPlayerIds: readonly (number | null)[];
|
readonly kind: 'inside';
|
||||||
readonly readySeats: readonly number[];
|
readonly roomcode: string;
|
||||||
readonly offlineSeats: readonly number[];
|
readonly selfSeat: number;
|
||||||
readonly roomtype: readonly unknown[];
|
readonly seatPlayerIds: readonly (number | null)[];
|
||||||
readonly stage: number;
|
readonly readySeats: readonly number[];
|
||||||
readonly needprepare: number;
|
readonly offlineSeats: readonly number[];
|
||||||
readonly infinite: number;
|
readonly roomtype: readonly unknown[];
|
||||||
}
|
readonly stage: number;
|
||||||
|
readonly needprepare: number;
|
||||||
export type RoomState = OutsideRoomState | InsideRoomState;
|
readonly infinite: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RoomState = OutsideRoomState | InsideRoomState;
|
||||||
|
|
||||||
export interface PlatformState {
|
export interface PlatformState {
|
||||||
readonly vipRooms?: readonly import('../../core/types/vip-room.ts').VipRoom[];
|
readonly vipRank?:{readonly entries:readonly import('../../protocol/contracts/rank-list.ts').RankEntry[]|null};
|
||||||
/** Absent until a task list response arrives; cleared by login replacement. */
|
readonly treasureList?:readonly import('../../protocol/contracts/rank-list.ts').RankEntry[];
|
||||||
readonly tasks?: readonly TaskData[];
|
/** Receipt identity allows repeated queries with an unchanged code to open the view. */
|
||||||
readonly app: PlatformAppState;
|
readonly inviteResponse?: {readonly kind:'query'|'binding'};
|
||||||
readonly players: PlayerDirectoryState;
|
readonly vipRooms?: readonly import('../../core/types/vip-room.ts').VipRoom[];
|
||||||
readonly room: RoomState;
|
/** Absent until a task list response arrives; cleared by login replacement. */
|
||||||
}
|
readonly tasks?: readonly TaskData[];
|
||||||
|
readonly app: PlatformAppState;
|
||||||
|
readonly players: PlayerDirectoryState;
|
||||||
|
readonly room: RoomState;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/** Game_Surface_3 Logic.InputRoomCode/pasteRoomCode and GameUI Spirit602. */
|
||||||
|
export class JoinRoomInput {
|
||||||
|
private value='';
|
||||||
|
private busy=false;
|
||||||
|
constructor(private readonly submit:(code:number)=>void){}
|
||||||
|
get code():string{return this.value;}
|
||||||
|
setBusy(value:boolean):void{this.busy=value;}
|
||||||
|
prefill(value:string):void{
|
||||||
|
if(!/^\d{0,6}$/.test(value))throw Error('Invalid saved room shortcode');
|
||||||
|
this.value=value;
|
||||||
|
}
|
||||||
|
digit(value:number):void{
|
||||||
|
if(!Number.isInteger(value)||value<0||value>9)throw Error('Invalid room digit');
|
||||||
|
if(this.busy)return;
|
||||||
|
if(this.value.length<6)this.value+=value;
|
||||||
|
if(this.value.length===6)this.submit(Number(this.value));
|
||||||
|
}
|
||||||
|
remove():void{if(!this.busy)this.value=this.value.slice(0,-1);}
|
||||||
|
clear():void{if(!this.busy)this.value='';}
|
||||||
|
paste(value:string):void{
|
||||||
|
if(this.busy)return;this.value=value.replace(/[^0-9]/g,'').slice(0,6);
|
||||||
|
if(this.value.length===6)this.submit(Number(this.value));
|
||||||
|
}
|
||||||
|
confirm():void{if(!this.busy&&Number(this.value)>0)this.submit(Number(this.value));}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "9301c0f4-8779-44f8-8d19-5f540b499e82",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
+2
File diff suppressed because one or more lines are too long
+9
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "a834856f-7a53-445b-b517-6588ad346e59",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -1,121 +1,131 @@
|
|||||||
import type { RemoteConfig } from '../config/remote-config.ts';
|
import type { RemoteConfig } from '../config/remote-config.ts';
|
||||||
|
|
||||||
export interface LobbyTemplate {
|
export interface LobbyTemplate {
|
||||||
isGameHall:boolean; mainMenuButton:boolean; hideNotice:boolean; vipInfinite:boolean;
|
isGameHall:boolean; mainMenuButton:boolean; hideNotice:boolean; vipInfinite:boolean;
|
||||||
}
|
}
|
||||||
/** Template-owned defaults: 02_Const.isGameHall and 00_SubGame_Config.sysConfig. */
|
/** Template-owned defaults: 02_Const.isGameHall and 00_SubGame_Config.sysConfig. */
|
||||||
export const LOBBY_TEMPLATE:Readonly<LobbyTemplate>=Object.freeze({
|
export const LOBBY_TEMPLATE:Readonly<LobbyTemplate>=Object.freeze({
|
||||||
isGameHall:false,mainMenuButton:false,hideNotice:false,vipInfinite:false,
|
isGameHall:false,mainMenuButton:false,hideNotice:false,vipInfinite:false,
|
||||||
});
|
});
|
||||||
export interface LobbyContext {versionState:0|1;launchMode:0|1;}
|
export interface LobbyContext {versionState:0|1;launchMode:0|1;}
|
||||||
const HALL_DEFAULTS=Object.freeze({androidPay:0,rankList:0 as number|string,storeUrl:'',bagUrl:'',actData:0 as unknown,worldRoomList:0 as number|string,wareHouse:0 as number|string});
|
const HALL_DEFAULTS=Object.freeze({androidPay:0,rankList:0 as number|string,isReSetOwnerNote:0 as number|string,storeUrl:'',bagUrl:'',actData:0 as unknown,worldRoomList:0 as number|string,wareHouse:0 as number|string});
|
||||||
const SYS_DEFAULTS=Object.freeze({hideTaskBtn:0 as number|string,hideWxShare:0 as number|string,miniPro:0 as number|string,hideRoomCard:1,cdKey:1,
|
const SYS_DEFAULTS=Object.freeze({vipRankLimit:10,hideTaskBtn:0 as number|string,hideWxShare:0 as number|string,miniPro:0 as number|string,hideRoomCard:1,cdKey:1,
|
||||||
ofcShortCode:null as unknown,hideSpid:null as unknown});
|
ofcShortCode:null as unknown,hideSpid:null as unknown});
|
||||||
function object(value:unknown,name:string):Record<string,unknown>{
|
function object(value:unknown,name:string):Record<string,unknown>{
|
||||||
if(value===null||typeof value!=='object'||Array.isArray(value))throw Error(`Invalid ${name}: expected object`);
|
if(value===null||typeof value!=='object'||Array.isArray(value))throw Error(`Invalid ${name}: expected object`);
|
||||||
return value as Record<string,unknown>;
|
return value as Record<string,unknown>;
|
||||||
}
|
}
|
||||||
/** This is the sole menu config source. Defaults reproduce 04_Data, then 12_Logic.getConfig_Succ merges. */
|
/** This is the sole menu config source. Defaults reproduce 04_Data, then 12_Logic.getConfig_Succ merges. */
|
||||||
export function resolveLobbyMenuConfig(remote:RemoteConfig,template:Readonly<LobbyTemplate>=LOBBY_TEMPLATE){
|
export function resolveLobbyMenuConfig(remote:RemoteConfig,template:Readonly<LobbyTemplate>=LOBBY_TEMPLATE){
|
||||||
const rawHall=remote.getValue('hall_config');
|
const rawHall=remote.getValue('hall_config');
|
||||||
const merged=rawHall==null?{}:object(rawHall,'hall_config');
|
const merged=rawHall==null?{}:object(rawHall,'hall_config');
|
||||||
const hall={...HALL_DEFAULTS,...merged};
|
const hall={...HALL_DEFAULTS,...merged};
|
||||||
for(const key of Object.keys(HALL_DEFAULTS) as (keyof typeof HALL_DEFAULTS)[]){
|
for(const key of Object.keys(HALL_DEFAULTS) as (keyof typeof HALL_DEFAULTS)[]){
|
||||||
// 04_Data.hallConfigName contains 'actData:' literally; top-level actData never overrides hall_config.actData.
|
// 04_Data.hallConfigName contains 'actData:' literally; top-level actData never overrides hall_config.actData.
|
||||||
if(key==='actData')continue;
|
if(key==='actData')continue;
|
||||||
const value=remote.getValue(key);if(value!=null)Object.assign(hall,{[key]:value});
|
const value=remote.getValue(key);if(value!=null)Object.assign(hall,{[key]:value});
|
||||||
}
|
}
|
||||||
for(const key of ['androidPay','rankList','worldRoomList','wareHouse'] as const){
|
for(const key of ['androidPay','rankList','worldRoomList','wareHouse'] as const){
|
||||||
if(!Number.isFinite(Number(hall[key])))throw Error(`Invalid ${key}`);
|
if(!Number.isFinite(Number(hall[key])))throw Error(`Invalid ${key}`);
|
||||||
if(key==='androidPay')hall[key]=Number(hall[key]);
|
if(key==='androidPay')hall[key]=Number(hall[key]);
|
||||||
}
|
}
|
||||||
for(const key of ['storeUrl','bagUrl'] as const)if(typeof hall[key]!=='string')throw Error(`Invalid ${key}`);
|
for(const key of ['storeUrl','bagUrl'] as const)if(typeof hall[key]!=='string')throw Error(`Invalid ${key}`);
|
||||||
const sys={...SYS_DEFAULTS};
|
const sys={...SYS_DEFAULTS};
|
||||||
for(const key of Object.keys(SYS_DEFAULTS) as (keyof typeof SYS_DEFAULTS)[]){
|
for(const key of Object.keys(SYS_DEFAULTS) as (keyof typeof SYS_DEFAULTS)[]){
|
||||||
const value=remote.getValue(key);if(value!=null)Object.assign(sys,{[key]:value});
|
const value=remote.getValue(key);if(value!=null)Object.assign(sys,{[key]:value});
|
||||||
}
|
}
|
||||||
for(const key of ['hideTaskBtn','hideWxShare','miniPro','hideRoomCard','cdKey'] as const){
|
if(!Number.isInteger(Number(sys.vipRankLimit))||Number(sys.vipRankLimit)<=0)throw Error('Invalid vipRankLimit');
|
||||||
if(!Number.isFinite(Number(sys[key])))throw Error(`Invalid ${key}`);
|
sys.vipRankLimit=Number(sys.vipRankLimit);
|
||||||
if(key==='hideRoomCard'||key==='cdKey')sys[key]=Number(sys[key]);
|
for(const key of ['hideTaskBtn','hideWxShare','miniPro','hideRoomCard','cdKey'] as const){
|
||||||
}
|
if(!Number.isFinite(Number(sys[key])))throw Error(`Invalid ${key}`);
|
||||||
if(sys.hideSpid!==null&&(!Array.isArray(sys.hideSpid)||sys.hideSpid.some(id=>!Number.isInteger(id))))throw Error('Invalid hideSpid');
|
if(key==='hideRoomCard'||key==='cdKey')sys[key]=Number(sys[key]);
|
||||||
const rawAgent=remote.getValue('agentmode');
|
}
|
||||||
const agentmode=rawAgent==null?1:Number(rawAgent);
|
if(sys.hideSpid!==null&&(!Array.isArray(sys.hideSpid)||sys.hideSpid.some(id=>!Number.isInteger(id))))throw Error('Invalid hideSpid');
|
||||||
if(![1,2,3].includes(agentmode))throw Error('Invalid agentmode');
|
const rawAgent=remote.getValue('agentmode');
|
||||||
return Object.freeze({agentmode,hall:Object.freeze(hall),sys:Object.freeze(sys),template});
|
const agentmode=rawAgent==null?1:Number(rawAgent);
|
||||||
|
if(![1,2,3].includes(agentmode))throw Error('Invalid agentmode');
|
||||||
|
// Original GameUI draw cases 568/483/3139: missing vipConfig means unopened;
|
||||||
|
// the earlier matchhtml condition is commented out in the source.
|
||||||
|
const vipConfig=remote.getValue('vipConfig');
|
||||||
|
const locked=Object.freeze({568:!hall.worldRoomList,483:!vipConfig,3139:!sys.ofcShortCode});
|
||||||
|
return Object.freeze({agentmode,hall:Object.freeze(hall),sys:Object.freeze(sys),template,locked});
|
||||||
}
|
}
|
||||||
export type LobbyMenuConfig=ReturnType<typeof resolveLobbyMenuConfig>;
|
export type LobbyMenuConfig=ReturnType<typeof resolveLobbyMenuConfig>;
|
||||||
export interface LobbyMenuState {visible:Record<number,boolean>;left:Record<number,number>;top:Record<number,number>;}
|
/** Original click messages intentionally use different comma characters. */
|
||||||
|
export function lobbyLockedTip(config:LobbyMenuConfig,action:'world-rooms'|'match'|'official-rooms'):string|null{
|
||||||
/** Ordered projection of JumpMenuScene. Unmentioned nodes retain their authored state. */
|
if(action==='world-rooms')return config.locked[568]?'暂未开放,敬请期待':null;
|
||||||
export function lobbyMenuState(config:LobbyMenuConfig,context:LobbyContext):LobbyMenuState{
|
return config.locked[action==='match'?483:3139]?'暂未开放,敬请期待':null;
|
||||||
const {hall,sys,template,agentmode}=config;
|
|
||||||
const visible:Record<number,boolean>={},left:Record<number,number>={},top:Record<number,number>={};
|
|
||||||
const hide=(ids:number[])=>{for(const id of ids)visible[id]=false;};
|
|
||||||
// Source hideSpid moves nodes before JumpMenuScene; later smart-bar placement can override X.
|
|
||||||
if(Array.isArray(sys.hideSpid))for(const id of sys.hideSpid){left[id]=1300;top[id]=730;}
|
|
||||||
hide([219,220]);
|
|
||||||
if(context.versionState===1){
|
|
||||||
if(context.launchMode===0)hide([234]);
|
|
||||||
hide([17,14,16,145,340,339,681,682,178,179,180,193,194,235,483,568,604,
|
|
||||||
286,157,287,10,508,497,496,509,3233,3232,76,3139,679,99,3120,3161,3162,3016,3018,3017]);
|
|
||||||
}else{
|
|
||||||
if(agentmode===1)hide([10,509]);
|
|
||||||
visible[235]=false;
|
|
||||||
if(hall.androidPay===1){visible[10]=true;visible[509]=true;}
|
|
||||||
visible[3018]=!template.isGameHall&&Array.isArray(hall.actData);
|
|
||||||
visible[3016]=Boolean(hall.storeUrl);
|
|
||||||
if(template.isGameHall||template.vipInfinite)hide([3139,679]);
|
|
||||||
if(template.mainMenuButton)hide([11,12,568,483]);
|
|
||||||
if(template.hideNotice)hide([16,180,193,194,682,178,179,145]);
|
|
||||||
if(sys.hideTaskBtn)hide([17]);
|
|
||||||
if(sys.miniPro||sys.hideWxShare)hide([14]);
|
|
||||||
if(!template.isGameHall){
|
|
||||||
const shown=[3120,14,17,13,7].filter(id=>visible[id]!==false);
|
|
||||||
shown.forEach((id,index)=>{left[id]=shown.length===1?558:shown.length===2?[376,740][index]:194+728*index/(shown.length-1);});
|
|
||||||
}
|
|
||||||
visible[3233]=Boolean(hall.rankList);
|
|
||||||
visible[3256]=false; // Both original deviceLogin branches hide this entry.
|
|
||||||
if(template.isGameHall){
|
|
||||||
hide([14,13,17,3120,99,3016,76,234,3057,145,3139,3232,3233,604]);
|
|
||||||
visible[235]=agentmode!==1;visible[15]=true;visible[5]=true;
|
|
||||||
const shown=[235,15,5,7].filter(id=>visible[id]!==false);
|
|
||||||
shown.forEach((id,index)=>{left[id]=300+520*index/(shown.length-1);top[id]=642;});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(sys.hideRoomCard===2)hide([286,10,287,3161]);
|
|
||||||
visible[3264]=sys.cdKey===2;
|
|
||||||
return {visible,left,top};
|
|
||||||
}
|
}
|
||||||
|
export interface LobbyMenuState {visible:Record<number,boolean>;left:Record<number,number>;top:Record<number,number>;}
|
||||||
/** openPallet runs its own visibility pass each time before placing visible entries. */
|
|
||||||
export function lobbyTrayState(config:LobbyMenuConfig,context:LobbyContext):LobbyMenuState{
|
/** Ordered projection of JumpMenuScene. Unmentioned nodes retain their authored state. */
|
||||||
const visible:Record<number,boolean>={15:false,604:Boolean(config.hall.rankList),
|
export function lobbyMenuState(config:LobbyMenuConfig,context:LobbyContext):LobbyMenuState{
|
||||||
235:config.agentmode!==1&&!config.sys.miniPro,3017:Boolean(config.hall.bagUrl),3057:Boolean(config.hall.wareHouse),5:true};
|
const {hall,sys,template,agentmode}=config;
|
||||||
const left:Record<number,number>={3121:200},top:Record<number,number>={};
|
const visible:Record<number,boolean>={},left:Record<number,number>={},top:Record<number,number>={};
|
||||||
if(context.versionState===1)return {visible:Object.fromEntries(Object.keys(visible).map(id=>[id,false])),left,top};
|
const hide=(ids:number[])=>{for(const id of ids)visible[id]=false;};
|
||||||
const shown=[15,604,235,3017,3057,5].filter(id=>visible[id]);
|
// Source hideSpid moves nodes before JumpMenuScene; later smart-bar placement can override X.
|
||||||
shown.forEach((id,index)=>{left[id]=216+155*index;top[id]=540;});
|
if(Array.isArray(sys.hideSpid))for(const id of sys.hideSpid){left[id]=1300;top[id]=730;}
|
||||||
return {visible,left,top};
|
hide([219,220]);
|
||||||
}
|
if(context.versionState===1){
|
||||||
|
if(context.launchMode===0)hide([234]);
|
||||||
/** IDs remain source traceability only; Cocos owns hit testing and prefab lifetimes. */
|
hide([17,14,16,145,340,339,681,682,178,179,180,193,194,235,483,568,604,
|
||||||
export const LOBBY_MENU_ACTIONS=Object.freeze({
|
286,157,287,10,508,497,496,509,3233,3232,76,3139,679,99,3120,3161,3162,3016,3018,3017]);
|
||||||
7:'settings',9:'profile',11:'create-room',12:'join-room',13:'records',14:'share',17:'tasks',679:'vip-rooms',
|
}else{
|
||||||
10:'buy-roomcard',286:'buy-roomcard',287:'buy-roomcard',3161:'buy-roomcard',
|
if(agentmode===1)hide([10,509]);
|
||||||
508:'buy-bean',509:'buy-bean',496:'buy-bean',3162:'buy-bean',
|
visible[235]=false;
|
||||||
483:'match',568:'world-rooms',3016:'store',3018:'activity',99:'my-rooms',76:'qrcode',
|
if(hall.androidPay===1){visible[10]=true;visible[509]=true;}
|
||||||
3139:'official-rooms',234:'more-games',3120:'tray',3233:'rank',3232:'vip-rank',3256:'bind-phone',3264:'cdkey',
|
visible[3018]=!template.isGameHall&&Array.isArray(hall.actData);
|
||||||
5:'notice',3017:'bag',235:'agent',604:'rank',3057:'warehouse',15:'feedback',
|
visible[3016]=Boolean(hall.storeUrl);
|
||||||
} as const);
|
if(template.isGameHall||template.vipInfinite)hide([3139,679]);
|
||||||
export type LobbyAction=typeof LOBBY_MENU_ACTIONS[keyof typeof LOBBY_MENU_ACTIONS];
|
if(template.mainMenuButton)hide([11,12,568,483]);
|
||||||
export const LOBBY_UNAVAILABLE:Readonly<Partial<Record<LobbyAction,string>>>=Object.freeze({
|
if(template.hideNotice)hide([16,180,193,194,682,178,179,145]);
|
||||||
'create-room':'创建房间:尚未接入目标子游戏规则','records':'战绩:尚未接入子游戏战绩模块',
|
if(sys.hideTaskBtn)hide([17]);
|
||||||
'buy-roomcard':'购买房卡:尚未接入支付模块','buy-bean':'购买星星:尚未接入支付模块',
|
if(sys.miniPro||sys.hideWxShare)hide([14]);
|
||||||
match:'比赛:尚未接入比赛模块','world-rooms':'星星场:尚未接入房间列表',store:'商城:尚未接入商城身份参数与原生接口',
|
if(!template.isGameHall){
|
||||||
activity:'活动:尚未接入活动身份参数与原生接口','my-rooms':'我的房间:尚未接入房间记录与进房',qrcode:'二维码:尚未接入分享模块',
|
const shown=[3120,14,17,13,7].filter(id=>visible[id]!==false);
|
||||||
'official-rooms':'官方试玩:尚未接入微房号列表','more-games':'更多游戏:尚未接入返回大厅与下载入口',
|
shown.forEach((id,index)=>{left[id]=shown.length===1?558:shown.length===2?[376,740][index]:194+728*index/(shown.length-1);});
|
||||||
rank:'排行:尚未接入排行榜', 'vip-rank':'VIP 排行:尚未接入排行榜','bind-phone':'绑定手机:尚未接入绑定流程',
|
}
|
||||||
cdkey:'兑换码:尚未接入兑换请求',bag:'背包:尚未接入背包身份参数与原生接口',agent:'代理:尚未接入代理入口',
|
visible[3233]=Boolean(hall.rankList);
|
||||||
warehouse:'保险箱:尚未接入仓库模块',feedback:'反馈:尚未接入意见提交',
|
visible[3256]=false; // Both original deviceLogin branches hide this entry.
|
||||||
});
|
if(template.isGameHall){
|
||||||
|
hide([14,13,17,3120,99,3016,76,234,3057,145,3139,3232,3233,604]);
|
||||||
|
visible[235]=agentmode!==1;visible[15]=true;visible[5]=true;
|
||||||
|
const shown=[235,15,5,7].filter(id=>visible[id]!==false);
|
||||||
|
shown.forEach((id,index)=>{left[id]=300+520*index/(shown.length-1);top[id]=642;});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(sys.hideRoomCard===2)hide([286,10,287,3161]);
|
||||||
|
visible[3264]=sys.cdKey===2;
|
||||||
|
return {visible,left,top};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** openPallet runs its own visibility pass each time before placing visible entries. */
|
||||||
|
export function lobbyTrayState(config:LobbyMenuConfig,context:LobbyContext):LobbyMenuState{
|
||||||
|
const visible:Record<number,boolean>={15:false,604:Boolean(config.hall.rankList),
|
||||||
|
235:config.agentmode!==1&&!config.sys.miniPro,3017:Boolean(config.hall.bagUrl),3057:Boolean(config.hall.wareHouse),5:true};
|
||||||
|
const left:Record<number,number>={3121:200},top:Record<number,number>={};
|
||||||
|
if(context.versionState===1)return {visible:Object.fromEntries(Object.keys(visible).map(id=>[id,false])),left,top};
|
||||||
|
const shown=[15,604,235,3017,3057,5].filter(id=>visible[id]);
|
||||||
|
shown.forEach((id,index)=>{left[id]=216+155*index;top[id]=540;});
|
||||||
|
return {visible,left,top};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** IDs remain source traceability only; Cocos owns hit testing and prefab lifetimes. */
|
||||||
|
export const LOBBY_MENU_ACTIONS=Object.freeze({
|
||||||
|
7:'settings',9:'profile',11:'create-room',12:'join-room',13:'records',14:'share',17:'tasks',679:'vip-rooms',
|
||||||
|
10:'buy-roomcard',286:'buy-roomcard',287:'buy-roomcard',3161:'buy-roomcard',
|
||||||
|
508:'buy-bean',509:'buy-bean',496:'buy-bean',3162:'buy-bean',
|
||||||
|
483:'match',568:'world-rooms',3016:'store',3018:'activity',99:'my-rooms',76:'qrcode',
|
||||||
|
3139:'official-rooms',234:'more-games',3120:'tray',3233:'rank',3232:'vip-rank',3256:'bind-phone',3264:'cdkey',
|
||||||
|
5:'notice',3017:'bag',235:'agent',604:'rank',3057:'warehouse',15:'feedback',
|
||||||
|
} as const);
|
||||||
|
export type LobbyAction=typeof LOBBY_MENU_ACTIONS[keyof typeof LOBBY_MENU_ACTIONS];
|
||||||
|
export const LOBBY_UNAVAILABLE:Readonly<Partial<Record<LobbyAction,string>>>=Object.freeze({
|
||||||
|
'create-room':'创建房间:尚未接入目标子游戏规则','records':'战绩:尚未接入子游戏战绩模块',
|
||||||
|
match:'比赛:尚未接入比赛模块','world-rooms':'星星场:尚未接入房间列表',store:'商城:尚未接入商城身份参数与原生接口',
|
||||||
|
activity:'活动:尚未接入活动身份参数与原生接口',
|
||||||
|
'official-rooms':'官方试玩:尚未接入微房号列表','more-games':'更多游戏:尚未接入返回大厅与下载入口',
|
||||||
|
'bind-phone':'绑定手机:尚未接入绑定流程',
|
||||||
|
bag:'背包:尚未接入背包身份参数与原生接口',agent:'代理:尚未接入代理入口',
|
||||||
|
warehouse:'保险箱:尚未接入仓库模块',feedback:'反馈:尚未接入意见提交',
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,66 +1,74 @@
|
|||||||
import type { RemoteConfig } from '../config/remote-config.ts';
|
import type { RemoteConfig } from '../config/remote-config.ts';
|
||||||
import type { PlatformState } from '../platform/stores/platform-types.ts';
|
import type { PlatformState } from '../platform/stores/platform-types.ts';
|
||||||
import { selectSelfPlayer } from '../platform/stores/selectors.ts';
|
import { selectSelfPlayer } from '../platform/stores/selectors.ts';
|
||||||
// Presentation checks required view inputs without depending on the protocol layer.
|
// Presentation checks required view inputs without depending on the protocol layer.
|
||||||
function requireNumber(value:unknown,path:string):number {if(typeof value!=='number'||!Number.isFinite(value))throw Error(`${path}: expected number`);return value;}
|
function requireNumber(value:unknown,path:string):number {if(typeof value!=='number'||!Number.isFinite(value))throw Error(`${path}: expected number`);return value;}
|
||||||
function requireInteger(value:unknown,path:string):number {const result=requireNumber(value,path);if(!Number.isInteger(result))throw Error(`${path}: expected integer`);return result;}
|
function requireInteger(value:unknown,path:string):number {const result=requireNumber(value,path);if(!Number.isInteger(result))throw Error(`${path}: expected integer`);return result;}
|
||||||
function requireString(value:unknown,path:string):string {if(typeof value!=='string')throw Error(`${path}: expected string`);return value;}
|
function requireString(value:unknown,path:string):string {if(typeof value!=='string')throw Error(`${path}: expected string`);return value;}
|
||||||
function requireRecord(value:unknown,path:string):Record<string,unknown> {if(!value||typeof value!=='object'||Array.isArray(value))throw Error(`${path}: expected object`);return value as Record<string,unknown>;}
|
function requireRecord(value:unknown,path:string):Record<string,unknown> {if(!value||typeof value!=='object'||Array.isArray(value))throw Error(`${path}: expected object`);return value as Record<string,unknown>;}
|
||||||
|
|
||||||
export function selectLobbyModel(state:PlatformState) {
|
export function selectLobbyModel(state:PlatformState) {
|
||||||
const self=selectSelfPlayer(state);
|
const self=selectSelfPlayer(state);
|
||||||
if(!self)throw Error('lobby requires authenticated player');
|
if(!self)throw Error('lobby requires authenticated player');
|
||||||
return Object.freeze({...self,
|
return Object.freeze({...self,
|
||||||
roomcard:requireNumber(self.roomcard,'players.self.roomcard'),
|
roomcard:requireNumber(self.roomcard,'players.self.roomcard'),
|
||||||
taskstate:requireInteger(self.taskstate,'players.self.taskstate'),
|
taskstate:requireInteger(self.taskstate,'players.self.taskstate'),
|
||||||
score:requireNumber(self.score,'players.self.score'),
|
score:requireNumber(self.score,'players.self.score'),
|
||||||
sign:self.sign===null?'这个家伙很懒~~':requireString(self.sign,'players.self.sign') || '这个家伙很懒~~',
|
sign:self.sign===null?'这个家伙很懒~~':requireString(self.sign,'players.self.sign') || '这个家伙很懒~~',
|
||||||
canAct:state.app.phase==='logged-in'&&state.room.kind==='outside',
|
canAct:state.app.phase==='logged-in'&&state.room.kind==='outside',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export type LobbyModel=ReturnType<typeof selectLobbyModel>;
|
export type LobbyModel=ReturnType<typeof selectLobbyModel>;
|
||||||
export interface Notice {type:0|1; text:string; image:string; url:string; height:number;}
|
/** Game_Config.Notice: speed 0.1 px/ms, 15 px per gblen byte. */
|
||||||
export interface LobbyPolicy {noticeHours:number; notices:readonly Notice[]; scroll:string;
|
export function announcementMotion(text:string,width:number,elapsedSeconds:number){
|
||||||
starName:string; roomCardName:string; contacts:readonly {label:string;value:string}[];}
|
if(!Number.isFinite(width)||width<=0)throw Error('Invalid announcement width');
|
||||||
|
if(!Number.isFinite(elapsedSeconds)||elapsedSeconds<0)throw Error('Invalid announcement elapsed time');
|
||||||
/** Source defaults: 04_Data.sysConfig and advertInfo_0/1/2; 12_Logic.get_paravalue. */
|
const textWidth=text.replace(/[^\x00-\xff]/g,'**').length*15;
|
||||||
export function resolveLobbyPolicy(remote:RemoteConfig):LobbyPolicy {
|
const duration=(width+textWidth)/100;
|
||||||
// 04_Data starts with 房卡; getConfig_Succ replaces only a truthy roomCardName.
|
return {visible:text.length>0,textWidth,duration,left:width-(elapsedSeconds%duration)*100};
|
||||||
const cardName=remote.getValue('roomCardName');
|
|
||||||
const roomCardName=cardName?requireString(cardName,'roomCardName'):'房卡';
|
|
||||||
const interval=remote.getValue('gameNoticeShow');
|
|
||||||
const noticeHours=interval==null?0:Number(interval);
|
|
||||||
if(!Number.isFinite(noticeHours)||noticeHours< -1)throw Error('Invalid gameNoticeShow');
|
|
||||||
const menuValue=remote.getValue('menunotice');
|
|
||||||
const menu=menuValue==null?{}:requireRecord(menuValue,'menunotice');
|
|
||||||
const notices:Notice[]=[];
|
|
||||||
for(let index=0;index<3;index++){
|
|
||||||
const value=menu[`advert${index}`];
|
|
||||||
if(value==null){
|
|
||||||
const message=index===0?remote.getValue('noticemsg'):null;
|
|
||||||
notices.push({type:0,text:message==null?'':requireString(message,'noticemsg').replace(/#/g,'\n'),image:'',url:'',height:1});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const data=requireRecord(value,`menunotice.advert${index}`);
|
|
||||||
const type=requireInteger(data.type,`menunotice.advert${index}.type`);
|
|
||||||
if(type!==0&&type!==1)throw Error(`menunotice.advert${index}.type must be 0 or 1`);
|
|
||||||
// Empty text and URL explicitly mean no content/action in the source.
|
|
||||||
const field=(key:string)=>data[key]==null?'':requireString(data[key],`menunotice.advert${index}.${key}`);
|
|
||||||
notices.push({type,text:type===0?field('msg').replace(/#/g,'\n'):'',image:type===1?field('imgUrl'):'',
|
|
||||||
url:field(type===0?'url':'imgAdUrl'),height:type===1?requireNumber(data.height,`menunotice.advert${index}.height`):1});
|
|
||||||
}
|
|
||||||
const scroll=remote.getValue('scrollmsg');
|
|
||||||
const star=remote.getValue('starName');
|
|
||||||
const contacts:Array<{label:string;value:string}>=[];
|
|
||||||
for(const [key,title] of [['service_wechat','客服微信:'],['service_qq','投诉建议:'],['service_tel','客服手机:']]){
|
|
||||||
const value=remote.getValue(key);
|
|
||||||
if(value!=null&&value!=='')contacts.push({label:title,value:requireString(value,key)});
|
|
||||||
}
|
|
||||||
return Object.freeze({roomCardName,noticeHours,notices:Object.freeze(notices),scroll:scroll==null?'':requireString(scroll,'scrollmsg'),
|
|
||||||
starName:star==null?'星星':requireString(star,'starName'),contacts:Object.freeze(contacts)});
|
|
||||||
}
|
|
||||||
/** Exact JudgeShow expiry: equality is not expired; reconnect never auto-opens. */
|
|
||||||
export function shouldShowNotice(hours:number,previous:number|null,now:number,reconnect:boolean):boolean {
|
|
||||||
if(reconnect||hours===-1)return false;
|
|
||||||
return hours===0||!previous||previous+hours*3600000<now;
|
|
||||||
}
|
}
|
||||||
|
export interface Notice {type:0|1; text:string; image:string; url:string; height:number;}
|
||||||
|
export interface LobbyPolicy {noticeHours:number; notices:readonly Notice[]; scroll:string;
|
||||||
|
starName:string; roomCardName:string; contacts:readonly {label:string;value:string}[];}
|
||||||
|
|
||||||
|
/** Source defaults: 04_Data.sysConfig and advertInfo_0/1/2; 12_Logic.get_paravalue. */
|
||||||
|
export function resolveLobbyPolicy(remote:RemoteConfig):LobbyPolicy {
|
||||||
|
// 04_Data starts with 房卡; getConfig_Succ replaces only a truthy roomCardName.
|
||||||
|
const cardName=remote.getValue('roomCardName');
|
||||||
|
const roomCardName=cardName?requireString(cardName,'roomCardName'):'房卡';
|
||||||
|
const interval=remote.getValue('gameNoticeShow');
|
||||||
|
const noticeHours=interval==null?0:Number(interval);
|
||||||
|
if(!Number.isFinite(noticeHours)||noticeHours< -1)throw Error('Invalid gameNoticeShow');
|
||||||
|
const menuValue=remote.getValue('menunotice');
|
||||||
|
const menu=menuValue==null?{}:requireRecord(menuValue,'menunotice');
|
||||||
|
const notices:Notice[]=[];
|
||||||
|
for(let index=0;index<3;index++){
|
||||||
|
const value=menu[`advert${index}`];
|
||||||
|
if(value==null){
|
||||||
|
const message=index===0?remote.getValue('noticemsg'):null;
|
||||||
|
notices.push({type:0,text:message==null?'':requireString(message,'noticemsg').replace(/#/g,'\n'),image:'',url:'',height:1});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const data=requireRecord(value,`menunotice.advert${index}`);
|
||||||
|
const type=requireInteger(data.type,`menunotice.advert${index}.type`);
|
||||||
|
if(type!==0&&type!==1)throw Error(`menunotice.advert${index}.type must be 0 or 1`);
|
||||||
|
// Empty text and URL explicitly mean no content/action in the source.
|
||||||
|
const field=(key:string)=>data[key]==null?'':requireString(data[key],`menunotice.advert${index}.${key}`);
|
||||||
|
notices.push({type,text:type===0?field('msg').replace(/#/g,'\n'):'',image:type===1?field('imgUrl'):'',
|
||||||
|
url:field(type===0?'url':'imgAdUrl'),height:type===1?requireNumber(data.height,`menunotice.advert${index}.height`):1});
|
||||||
|
}
|
||||||
|
const scroll=remote.getValue('scrollmsg');
|
||||||
|
const star=remote.getValue('starName');
|
||||||
|
const contacts:Array<{label:string;value:string}>=[];
|
||||||
|
for(const [key,title] of [['service_wechat','客服微信:'],['service_qq','投诉建议:'],['service_tel','客服手机:']]){
|
||||||
|
const value=remote.getValue(key);
|
||||||
|
if(value!=null&&value!=='')contacts.push({label:title,value:requireString(value,key)});
|
||||||
|
}
|
||||||
|
return Object.freeze({roomCardName,noticeHours,notices:Object.freeze(notices),scroll:scroll==null?'':requireString(scroll,'scrollmsg'),
|
||||||
|
starName:star==null?'星星':requireString(star,'starName'),contacts:Object.freeze(contacts)});
|
||||||
|
}
|
||||||
|
/** Exact JudgeShow expiry: equality is not expired; reconnect never auto-opens. */
|
||||||
|
export function shouldShowNotice(hours:number,previous:number|null,now:number,reconnect:boolean):boolean {
|
||||||
|
if(reconnect||hours===-1)return false;
|
||||||
|
return hours===0||!previous||previous+hours*3600000<now;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import {LEGACY_AUTH_DISTRICTS} from './legacy-auth-districts.ts';
|
||||||
|
|
||||||
|
/** Game_Surface_3 05_Func.IDValidator + 11_GameUI cases 484/493.
|
||||||
|
* Local format validation only. Deliberately retain the source's 15-digit,
|
||||||
|
* parent-district and month/day range semantics; this is not server identity proof.
|
||||||
|
*/
|
||||||
|
export function validateProfileAuth(name:string,id:string):'name'|'id'|null {
|
||||||
|
if(name.length<=1)return 'name';
|
||||||
|
const code=id.toUpperCase();
|
||||||
|
if(code.length!==15&&code.length!==18)return 'id';
|
||||||
|
const body=code.length===18?code.slice(0,17):code;
|
||||||
|
const address=body.slice(0,6);
|
||||||
|
const hasAddress=(key:string)=>Object.prototype.hasOwnProperty.call(LEGACY_AUTH_DISTRICTS,key);
|
||||||
|
if(!hasAddress(address)&&!hasAddress(address.slice(0,4)+'00')&&!hasAddress(address.slice(0,2)+'0000'))return 'id';
|
||||||
|
const birth=code.length===18?body.slice(6,14):body.slice(6,12);
|
||||||
|
const month=parseInt(birth.slice(-4,-2),10),day=parseInt(birth.slice(-2),10);
|
||||||
|
if(month>12||month===0||day>31||day===0)return 'id';
|
||||||
|
if(code.length===15)return null;
|
||||||
|
let sum=0;
|
||||||
|
for(let index=0;index<body.length;index++)sum+=parseInt(body[index],10)*(Math.pow(2,17-index)%11);
|
||||||
|
const value=12-sum%11;
|
||||||
|
const check=value===10?'X':String(value>10?value%11:value);
|
||||||
|
return check===code.slice(-1)?null:'id';
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "11f5830e-5f4b-46fb-9e06-b2b65edbed89",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export type LoadingRpc='get_player_task'|'get_task_award'|'getVipRankList';
|
||||||
|
/** Wire receipt, rather than a successful model update, completes request loading. */
|
||||||
|
export class RequestLoading {
|
||||||
|
private readonly pending=new Set<LoadingRpc>();
|
||||||
|
constructor(private readonly show:(visible:boolean)=>void){}
|
||||||
|
begin(rpc:LoadingRpc):void{const empty=this.pending.size===0;this.pending.add(rpc);if(empty)this.show(true);}
|
||||||
|
cancel(rpc:LoadingRpc):void{if(this.pending.delete(rpc)&&this.pending.size===0)this.show(false);}
|
||||||
|
received(message:{route:string;rpc:string;data?:unknown}):void{
|
||||||
|
if(message.route!=='agent')return;
|
||||||
|
if(message.rpc==='show_message'){this.clear();return;}
|
||||||
|
if(message.rpc==='get_player_task'||message.rpc==='get_task_award'||message.rpc==='getVipRankList')this.cancel(message.rpc);
|
||||||
|
}
|
||||||
|
clear():void{if(this.pending.size){this.pending.clear();this.show(false);}}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "8bb6a294-bfcf-4126-98f1-0845b58c72db",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -1,19 +1,27 @@
|
|||||||
import type { RuntimeConfig } from '../config/runtime-config.ts';
|
import type { RuntimeConfig } from '../config/runtime-config.ts';
|
||||||
/** Source: 12_Logic.getConfig_Succ then 05_Func.sharefriend. */
|
/** Open_ewm uses this exact config key; absent configuration intentionally does nothing. */
|
||||||
export function resolveSharePolicy(config:RuntimeConfig){
|
export function resolveQrCodePolicy(config:RuntimeConfig){
|
||||||
const remote=config.remoteConfig,raw=remote.getValue('hall_config');
|
const url=config.remoteConfig.getValue('WechatPublicNo_ewm');
|
||||||
if(raw!=null&&(typeof raw!=='object'||Array.isArray(raw)))throw Error('Invalid hall_config');
|
if(!url)return null;
|
||||||
const hall={shareDesc:'',shareBgUrl:'',shareImgUrl:'',shareType:0,...raw as object} as Record<string,unknown>;
|
if(typeof url!=='string')throw Error('Invalid WechatPublicNo_ewm');
|
||||||
for(const key of Object.keys(hall)){const value=remote.getValue(key);if(value!=null)hall[key]=value;}
|
const policy=resolveSharePolicy(config);
|
||||||
const text=(value:unknown,name:string)=>{if(typeof value!=='string')throw Error(`Invalid ${name}`);return value;};
|
return {url,share:{sharefriend:1,type:3,webpageUrl:url,title:policy.title+policy.description,description:undefined,sharetype:0}};
|
||||||
const download=remote.getValue('url_download'),name=remote.getValue('sharename'),id=config.identity;
|
|
||||||
return {url:download?`${text(download,'url_download')}?agentid=${id.agentid}&channelid=${id.channelid}&gameid=${id.gameid}&marketid=${id.marketid}`:'',
|
|
||||||
title:name?text(name,'sharename'):'',description:hall.shareDesc?text(hall.shareDesc,'shareDesc'):'hello world',
|
|
||||||
background:text(hall.shareBgUrl,'shareBgUrl'),image:text(hall.shareImgUrl,'shareImgUrl'),imageTimeline:!!hall.shareType};
|
|
||||||
}
|
|
||||||
export function sharePayload(policy:ReturnType<typeof resolveSharePolicy>,friend:1|2){
|
|
||||||
if(friend===2&&!policy.imageTimeline)return null; // Legacy case 75 has no link-sharing call.
|
|
||||||
return {sharefriend:friend,type:friend===1?1:3,webpageUrl:friend===1?policy.url:policy.image||policy.url,
|
|
||||||
title:friend===1?policy.title||policy.title+policy.description:policy.title+policy.description,
|
|
||||||
description:friend===1?policy.description:undefined,sharetype:0};
|
|
||||||
}
|
}
|
||||||
|
/** Source: 12_Logic.getConfig_Succ then 05_Func.sharefriend. */
|
||||||
|
export function resolveSharePolicy(config:RuntimeConfig){
|
||||||
|
const remote=config.remoteConfig,raw=remote.getValue('hall_config');
|
||||||
|
if(raw!=null&&(typeof raw!=='object'||Array.isArray(raw)))throw Error('Invalid hall_config');
|
||||||
|
const hall={shareDesc:'',shareBgUrl:'',shareImgUrl:'',shareType:0,...raw as object} as Record<string,unknown>;
|
||||||
|
for(const key of Object.keys(hall)){const value=remote.getValue(key);if(value!=null)hall[key]=value;}
|
||||||
|
const text=(value:unknown,name:string)=>{if(typeof value!=='string')throw Error(`Invalid ${name}`);return value;};
|
||||||
|
const download=remote.getValue('url_download'),name=remote.getValue('sharename'),id=config.identity;
|
||||||
|
return {url:download?`${text(download,'url_download')}?agentid=${id.agentid}&channelid=${id.channelid}&gameid=${id.gameid}&marketid=${id.marketid}`:'',
|
||||||
|
title:name?text(name,'sharename'):'',description:hall.shareDesc?text(hall.shareDesc,'shareDesc'):'hello world',
|
||||||
|
background:text(hall.shareBgUrl,'shareBgUrl'),image:text(hall.shareImgUrl,'shareImgUrl'),imageTimeline:!!hall.shareType};
|
||||||
|
}
|
||||||
|
export function sharePayload(policy:ReturnType<typeof resolveSharePolicy>,friend:1|2){
|
||||||
|
if(friend===2&&!policy.imageTimeline)return null; // Legacy case 75 has no link-sharing call.
|
||||||
|
return {sharefriend:friend,type:friend===1?1:3,webpageUrl:friend===1?policy.url:policy.image||policy.url,
|
||||||
|
title:friend===1?policy.title||policy.title+policy.description:policy.title+policy.description,
|
||||||
|
description:friend===1?policy.description:undefined,sharetype:0};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,52 +1,56 @@
|
|||||||
export {
|
export {
|
||||||
hasOwn,
|
hasOwn,
|
||||||
optionalField,
|
optionalField,
|
||||||
requireArray,
|
requireArray,
|
||||||
requireBoolean,
|
requireBoolean,
|
||||||
requireInteger,
|
requireInteger,
|
||||||
requireNumber,
|
requireNumber,
|
||||||
requireRecord,
|
requireRecord,
|
||||||
requireString,
|
requireString,
|
||||||
requireStringOrFiniteNumber,
|
requireStringOrFiniteNumber,
|
||||||
type JsonRecord,
|
type JsonRecord,
|
||||||
} from './validation.ts';
|
} from './validation.ts';
|
||||||
export {
|
export {
|
||||||
buildLoginRequest,
|
buildLoginRequest,
|
||||||
parseLoginResponse,
|
parseLoginResponse,
|
||||||
type DeviceLoginMode,
|
type DeviceLoginMode,
|
||||||
type FirstSliceRuntimeConfig,
|
type FirstSliceRuntimeConfig,
|
||||||
type LoginAccountIdentity,
|
type LoginAccountIdentity,
|
||||||
type LoginDeviceSnapshot,
|
type LoginDeviceSnapshot,
|
||||||
type LoginPlayerIdMode,
|
type LoginPlayerIdMode,
|
||||||
type LoginRequestPayload,
|
type LoginRequestPayload,
|
||||||
type ParsedLoginResponse,
|
type ParsedLoginResponse,
|
||||||
} from './login-contract.ts';
|
} from './login-contract.ts';
|
||||||
export {
|
export {
|
||||||
buildExitRoomRequest,
|
buildCreateRoomRequest,
|
||||||
buildJoinRoomRequest,
|
parseCreateRoomResponse,
|
||||||
buildPrepareRequest,
|
type ParsedCreateRoomResponse,
|
||||||
parseConnectAgentserverPayload,
|
type CreateRoomEnvironment,
|
||||||
parseConnectRoomserverPayload,
|
buildExitRoomRequest,
|
||||||
parseKickServerPayload,
|
buildJoinRoomRequest,
|
||||||
parseOtherExitRoomPayload,
|
buildPrepareRequest,
|
||||||
parseOtherJoinRoomPayload,
|
parseConnectAgentserverPayload,
|
||||||
parseOtherOfflinePayload,
|
parseConnectRoomserverPayload,
|
||||||
parseOtherOnlinePayload,
|
parseKickServerPayload,
|
||||||
parsePlayerPreparePayload,
|
parseOtherExitRoomPayload,
|
||||||
parseSelfExitRoomPayload,
|
parseOtherJoinRoomPayload,
|
||||||
parseSelfJoinRoomResponse,
|
parseOtherOfflinePayload,
|
||||||
type JoinRoomEnvironment,
|
parseOtherOnlinePayload,
|
||||||
type JoinRoomPayload,
|
parsePlayerPreparePayload,
|
||||||
type ParsedAgentserverPayload,
|
parseSelfExitRoomPayload,
|
||||||
type ParsedKickPayload,
|
parseSelfJoinRoomResponse,
|
||||||
type ParsedOnlinePayload,
|
type JoinRoomEnvironment,
|
||||||
type ParsedRoomEntry,
|
type JoinRoomPayload,
|
||||||
type ParsedRoomResponse,
|
type ParsedAgentserverPayload,
|
||||||
type ParsedRoomserverPayload,
|
type ParsedKickPayload,
|
||||||
type ParsedRoomSnapshot,
|
type ParsedOnlinePayload,
|
||||||
type ParsedSelfExitPayload,
|
type ParsedRoomEntry,
|
||||||
type ParsedSeatPayload,
|
type ParsedRoomResponse,
|
||||||
type ReconnectSnapshot,
|
type ParsedRoomserverPayload,
|
||||||
type RoomActionPayload,
|
type ParsedRoomSnapshot,
|
||||||
type RoomIdentity,
|
type ParsedSelfExitPayload,
|
||||||
} from './room-contracts.ts';
|
type ParsedSeatPayload,
|
||||||
|
type ReconnectSnapshot,
|
||||||
|
type RoomActionPayload,
|
||||||
|
type RoomIdentity,
|
||||||
|
} from './room-contracts.ts';
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import {requireArray,requireRecord,requireString,requireStringOrFiniteNumber} from './validation.ts';
|
||||||
|
export interface RankEntry {readonly name:string;readonly headimg:string;readonly playerid:string|number;readonly star:string|number;readonly signature:string;readonly notice?:string;readonly destwo?:string;}
|
||||||
|
/** Server rank entries are consumed as sent. Only rankList1 config replaces # line breaks. */
|
||||||
|
export function parseRankList(value:unknown,fromConfig=false):readonly RankEntry[]{
|
||||||
|
return Object.freeze(requireArray(value,'rank list').map((item,index)=>{
|
||||||
|
const p=`rank list[${index}]`,r=requireRecord(item,p);
|
||||||
|
const name=requireString(r.name,p+'.name'),signature=r.signature?requireString(r.signature,p+'.signature'):'';
|
||||||
|
return Object.freeze({name:fromConfig?configRankName(name):name,headimg:requireString(r.headimg,p+'.headimg'),playerid:requireStringOrFiniteNumber(r.playerid,p+'.playerid'),
|
||||||
|
star:requireStringOrFiniteNumber(r.star,p+'.star'),signature:fromConfig?signature.replace(/#/g,'\n'):signature});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Func.subString(name,16,true): Latin-1 counts one, other UTF-16 units two. */
|
||||||
|
function configRankName(name:string):string {
|
||||||
|
let length=0;
|
||||||
|
for(let i=0;i<name.length;i++){
|
||||||
|
length+=name.charCodeAt(i)>255?2:1;
|
||||||
|
if(length>16)return name.slice(0,i)+'...';
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseVipRankList(value:unknown):readonly RankEntry[]{
|
||||||
|
const raw=requireArray(value,'VIP rank list'),entries=parseRankList(raw);
|
||||||
|
return Object.freeze(entries.map((entry,i)=>{const item=requireRecord(raw[i],'VIP rank entry');
|
||||||
|
return Object.freeze({...entry,notice:item.notice?requireString(item.notice,'notice'):'',destwo:item.destwo?requireString(item.destwo,'destwo'):''});}));
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "744452fb-2d7e-4a0e-9c49-2e6994f5df84",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
+302
-243
@@ -1,243 +1,302 @@
|
|||||||
import {
|
import {
|
||||||
buildFirstSliceEnvelope,
|
buildFirstSliceEnvelope,
|
||||||
type FirstSliceEnvelope,
|
type FirstSliceEnvelope,
|
||||||
} from '../first-slice-routes.ts';
|
} from '../first-slice-routes.ts';
|
||||||
import {
|
import {
|
||||||
hasOwn,
|
hasOwn,
|
||||||
optionalField,
|
optionalField,
|
||||||
requireArray,
|
requireArray,
|
||||||
requireInteger,
|
requireInteger,
|
||||||
requireNumber,
|
requireNumber,
|
||||||
requireRecord,
|
requireRecord,
|
||||||
requireString,
|
requireString,
|
||||||
requireStringOrFiniteNumber,
|
requireStringOrFiniteNumber,
|
||||||
type JsonRecord,
|
type JsonRecord,
|
||||||
} from './validation.ts';
|
} from './validation.ts';
|
||||||
import type { FirstSliceRuntimeConfig } from './login-contract.ts';
|
import type { FirstSliceRuntimeConfig } from './login-contract.ts';
|
||||||
|
|
||||||
export type ReconnectSnapshot =
|
export type ReconnectSnapshot =
|
||||||
| { readonly present: false }
|
| { readonly present: false }
|
||||||
| { readonly present: true; readonly value: unknown };
|
| { readonly present: true; readonly value: unknown };
|
||||||
|
|
||||||
export interface ParsedRoomSnapshot {
|
export interface ParsedRoomSnapshot {
|
||||||
readonly roomcode: string;
|
readonly roomcode: string;
|
||||||
readonly seat: number;
|
readonly seat: number;
|
||||||
readonly roomtype: readonly unknown[];
|
readonly roomtype: readonly unknown[];
|
||||||
readonly players: readonly unknown[];
|
readonly players: readonly unknown[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ParsedRoomEntry {
|
export interface ParsedRoomEntry {
|
||||||
readonly room: ParsedRoomSnapshot;
|
readonly room: ParsedRoomSnapshot;
|
||||||
readonly reconnect: ReconnectSnapshot;
|
readonly reconnect: ReconnectSnapshot;
|
||||||
readonly raw: JsonRecord;
|
readonly raw: JsonRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function reconnectFrom(raw: JsonRecord): ReconnectSnapshot {
|
export function reconnectFrom(raw: JsonRecord): ReconnectSnapshot {
|
||||||
if (!Boolean(raw.deskinfo)) return { present: false };
|
if (!Boolean(raw.deskinfo)) return { present: false };
|
||||||
return { present: true, value: raw.deskinfo };
|
return { present: true, value: raw.deskinfo };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseRoomEntryFields(raw: JsonRecord): ParsedRoomSnapshot {
|
export function parseRoomEntryFields(raw: JsonRecord): ParsedRoomSnapshot {
|
||||||
const roomcode = requireString(raw.roomcode, '$.data.roomcode');
|
const roomcode = requireString(raw.roomcode, '$.data.roomcode');
|
||||||
const seat = requireInteger(raw.seat, '$.data.seat');
|
const seat = requireInteger(raw.seat, '$.data.seat');
|
||||||
const roomtype = requireArray(raw.roomtype, '$.data.roomtype');
|
const roomtype = requireArray(raw.roomtype, '$.data.roomtype');
|
||||||
const players = requireArray(raw.players, '$.data.players');
|
const players = requireArray(raw.players, '$.data.players');
|
||||||
if (seat < 0 || seat >= players.length) {
|
if (seat < 0 || seat >= players.length) {
|
||||||
throw new RangeError(`$.data.seat: expected server seat index within players, received number`);
|
throw new RangeError(`$.data.seat: expected server seat index within players, received number`);
|
||||||
}
|
}
|
||||||
return { roomcode, seat, roomtype, players };
|
return { roomcode, seat, roomtype, players };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RoomIdentity {
|
export interface RoomIdentity {
|
||||||
readonly playerid: number;
|
readonly playerid: number;
|
||||||
readonly roomcode: string;
|
readonly roomcode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RoomActionPayload extends Readonly<Record<string, unknown>> {
|
export interface RoomActionPayload extends Readonly<Record<string, unknown>> {
|
||||||
readonly agentid: string | number;
|
readonly agentid: string | number;
|
||||||
readonly playerid: number;
|
readonly playerid: number;
|
||||||
readonly gameid: string | number;
|
readonly gameid: string | number;
|
||||||
readonly roomcode: string;
|
readonly roomcode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function baseRoomAction(
|
function baseRoomAction(
|
||||||
runtimeConfig: FirstSliceRuntimeConfig,
|
runtimeConfig: FirstSliceRuntimeConfig,
|
||||||
room: RoomIdentity,
|
room: RoomIdentity,
|
||||||
): RoomActionPayload {
|
): RoomActionPayload {
|
||||||
const identity = requireRecord(runtimeConfig?.identity, '$.runtimeConfig.identity');
|
const identity = requireRecord(runtimeConfig?.identity, '$.runtimeConfig.identity');
|
||||||
const roomRecord = requireRecord(room, '$.room');
|
const roomRecord = requireRecord(room, '$.room');
|
||||||
return {
|
return {
|
||||||
agentid: requireStringOrFiniteNumber(
|
agentid: requireStringOrFiniteNumber(
|
||||||
identity.agentid,
|
identity.agentid,
|
||||||
'$.runtimeConfig.identity.agentid',
|
'$.runtimeConfig.identity.agentid',
|
||||||
),
|
),
|
||||||
playerid: requireInteger(roomRecord.playerid, '$.room.playerid'),
|
playerid: requireInteger(roomRecord.playerid, '$.room.playerid'),
|
||||||
gameid: requireStringOrFiniteNumber(identity.gameid, '$.runtimeConfig.identity.gameid'),
|
gameid: requireStringOrFiniteNumber(identity.gameid, '$.runtimeConfig.identity.gameid'),
|
||||||
roomcode: requireString(roomRecord.roomcode, '$.room.roomcode'),
|
roomcode: requireString(roomRecord.roomcode, '$.room.roomcode'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildPrepareRequest(
|
export function buildPrepareRequest(
|
||||||
runtimeConfig: FirstSliceRuntimeConfig,
|
runtimeConfig: FirstSliceRuntimeConfig,
|
||||||
room: RoomIdentity,
|
room: RoomIdentity,
|
||||||
): FirstSliceEnvelope<'player_prepare', RoomActionPayload> {
|
): FirstSliceEnvelope<'player_prepare', RoomActionPayload> {
|
||||||
return buildFirstSliceEnvelope('player_prepare', baseRoomAction(runtimeConfig, room));
|
return buildFirstSliceEnvelope('player_prepare', baseRoomAction(runtimeConfig, room));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildExitRoomRequest(
|
export function buildExitRoomRequest(
|
||||||
runtimeConfig: FirstSliceRuntimeConfig,
|
runtimeConfig: FirstSliceRuntimeConfig,
|
||||||
room: RoomIdentity,
|
room: RoomIdentity,
|
||||||
): FirstSliceEnvelope<'self_exit_room', RoomActionPayload> {
|
): FirstSliceEnvelope<'self_exit_room', RoomActionPayload> {
|
||||||
return buildFirstSliceEnvelope('self_exit_room', baseRoomAction(runtimeConfig, room));
|
return buildFirstSliceEnvelope('self_exit_room', baseRoomAction(runtimeConfig, room));
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JoinRoomEnvironment {
|
export interface JoinRoomEnvironment {
|
||||||
readonly location: unknown;
|
readonly location: unknown;
|
||||||
readonly ip: string;
|
readonly ip: string;
|
||||||
readonly vipMatch?: 1;
|
readonly vipMatch?: 1;
|
||||||
readonly match_id?: string | number;
|
readonly match_id?: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JoinRoomPayload extends RoomActionPayload {
|
export interface JoinRoomPayload extends Omit<RoomActionPayload, 'roomcode'> {
|
||||||
readonly location: unknown;
|
readonly roomcode: string | number;
|
||||||
readonly ip: string;
|
readonly location: unknown;
|
||||||
readonly vipMatch?: 1;
|
readonly ip: string;
|
||||||
readonly match_id?: string | number;
|
readonly vipMatch?: 1;
|
||||||
}
|
readonly match_id?: string | number;
|
||||||
|
}
|
||||||
export function buildJoinRoomRequest(
|
|
||||||
runtimeConfig: FirstSliceRuntimeConfig,
|
export function buildJoinRoomRequest(
|
||||||
room: RoomIdentity,
|
runtimeConfig: FirstSliceRuntimeConfig,
|
||||||
environment: JoinRoomEnvironment,
|
room: { readonly playerid: number; readonly roomcode: string | number },
|
||||||
): FirstSliceEnvelope<'self_join_room', JoinRoomPayload> {
|
environment: JoinRoomEnvironment,
|
||||||
const environmentRecord = requireRecord(environment, '$.environment');
|
): FirstSliceEnvelope<'self_join_room', JoinRoomPayload> {
|
||||||
if (!hasOwn(environmentRecord, 'location') || environmentRecord.location === undefined) {
|
const environmentRecord = requireRecord(environment, '$.environment');
|
||||||
throw new TypeError('$.environment.location: expected present field, received undefined');
|
if (!hasOwn(environmentRecord, 'location') || environmentRecord.location === undefined) {
|
||||||
}
|
throw new TypeError('$.environment.location: expected present field, received undefined');
|
||||||
if (environmentRecord.location !== null) {
|
}
|
||||||
requireRecord(environmentRecord.location, '$.environment.location');
|
if (environmentRecord.location !== null) {
|
||||||
}
|
requireRecord(environmentRecord.location, '$.environment.location');
|
||||||
const data: Record<string, unknown> = {
|
}
|
||||||
...baseRoomAction(runtimeConfig, room),
|
const data: Record<string, unknown> = {
|
||||||
location: environmentRecord.location,
|
...baseRoomAction(runtimeConfig, {playerid: room.playerid, roomcode: String(room.roomcode)}),
|
||||||
ip: requireString(environmentRecord.ip, '$.environment.ip'),
|
roomcode: requireStringOrFiniteNumber(room.roomcode, '$.room.roomcode'),
|
||||||
};
|
location: environmentRecord.location,
|
||||||
const vipMatch = optionalField(
|
ip: requireString(environmentRecord.ip, '$.environment.ip'),
|
||||||
environmentRecord,
|
};
|
||||||
'vipMatch',
|
const vipMatch = optionalField(
|
||||||
'$.environment',
|
environmentRecord,
|
||||||
(value, path): 1 => {
|
'vipMatch',
|
||||||
const parsed = requireNumber(value, path);
|
'$.environment',
|
||||||
if (parsed !== 1) {
|
(value, path): 1 => {
|
||||||
throw new RangeError(`${path}: expected number 1, received number`);
|
const parsed = requireNumber(value, path);
|
||||||
}
|
if (parsed !== 1) {
|
||||||
return 1;
|
throw new RangeError(`${path}: expected number 1, received number`);
|
||||||
},
|
}
|
||||||
);
|
return 1;
|
||||||
if (vipMatch !== undefined) data.vipMatch = vipMatch;
|
},
|
||||||
|
);
|
||||||
const matchId = optionalField(
|
if (vipMatch !== undefined) data.vipMatch = vipMatch;
|
||||||
environmentRecord,
|
|
||||||
'match_id',
|
const matchId = optionalField(
|
||||||
'$.environment',
|
environmentRecord,
|
||||||
requireStringOrFiniteNumber,
|
'match_id',
|
||||||
);
|
'$.environment',
|
||||||
if (matchId !== undefined) data.match_id = matchId;
|
requireStringOrFiniteNumber,
|
||||||
return buildFirstSliceEnvelope('self_join_room', data as JoinRoomPayload);
|
);
|
||||||
}
|
if (matchId !== undefined) data.match_id = matchId;
|
||||||
|
return buildFirstSliceEnvelope('self_join_room', data as JoinRoomPayload);
|
||||||
export interface ParsedRoomResponse {
|
}
|
||||||
readonly state: number;
|
|
||||||
readonly room: ParsedRoomSnapshot | null;
|
export interface ParsedRoomResponse {
|
||||||
readonly reconnect: ReconnectSnapshot;
|
readonly state: number;
|
||||||
readonly stage: 0 | 1;
|
readonly room: ParsedRoomSnapshot | null;
|
||||||
readonly raw: JsonRecord;
|
readonly reconnect: ReconnectSnapshot;
|
||||||
}
|
readonly stage: 0 | 1;
|
||||||
|
readonly raw: JsonRecord;
|
||||||
export function parseSelfJoinRoomResponse(value: unknown): ParsedRoomResponse {
|
}
|
||||||
const raw = requireRecord(value, '$.data');
|
|
||||||
const state = requireInteger(raw.state, '$.data.state');
|
export function parseSelfJoinRoomResponse(value: unknown): ParsedRoomResponse {
|
||||||
const room = state === 0 ? parseRoomEntryFields(raw) : null;
|
const raw = requireRecord(value, '$.data');
|
||||||
const parsedDeskwar = optionalField(raw, 'deskwar', '$.data', requireInteger);
|
const state = requireInteger(raw.state, '$.data.state');
|
||||||
if (parsedDeskwar !== undefined && parsedDeskwar !== 0 && parsedDeskwar !== 1) {
|
const room = state === 0 ? parseRoomEntryFields(raw) : null;
|
||||||
throw new RangeError('$.data.deskwar: expected protocol flag 0 or 1');
|
const parsedDeskwar = optionalField(raw, 'deskwar', '$.data', requireInteger);
|
||||||
}
|
if (parsedDeskwar !== undefined && parsedDeskwar !== 0 && parsedDeskwar !== 1) {
|
||||||
const reconnect = reconnectFrom(raw);
|
throw new RangeError('$.data.deskwar: expected protocol flag 0 or 1');
|
||||||
const stage = reconnect.present ? 1 : parsedDeskwar ?? 0;
|
}
|
||||||
return { state, room, reconnect, stage, raw };
|
const reconnect = reconnectFrom(raw);
|
||||||
}
|
const stage = reconnect.present ? 1 : parsedDeskwar ?? 0;
|
||||||
|
return { state, room, reconnect, stage, raw };
|
||||||
export interface ParsedSeatPayload {
|
}
|
||||||
readonly seat: number;
|
|
||||||
readonly raw: JsonRecord;
|
export interface ParsedSeatPayload {
|
||||||
}
|
readonly seat: number;
|
||||||
|
readonly raw: JsonRecord;
|
||||||
function parseSeatPayload(value: unknown): ParsedSeatPayload {
|
}
|
||||||
const raw = requireRecord(value, '$.data');
|
|
||||||
const seat = requireInteger(raw.seat, '$.data.seat');
|
function parseSeatPayload(value: unknown): ParsedSeatPayload {
|
||||||
if (seat < 0) throw new RangeError('$.data.seat: expected non-negative server seat, received number');
|
const raw = requireRecord(value, '$.data');
|
||||||
return { seat, raw };
|
const seat = requireInteger(raw.seat, '$.data.seat');
|
||||||
}
|
if (seat < 0) throw new RangeError('$.data.seat: expected non-negative server seat, received number');
|
||||||
|
return { seat, raw };
|
||||||
export const parsePlayerPreparePayload = parseSeatPayload;
|
}
|
||||||
export const parseOtherJoinRoomPayload = parseSeatPayload;
|
|
||||||
export const parseOtherExitRoomPayload = parseSeatPayload;
|
export const parsePlayerPreparePayload = parseSeatPayload;
|
||||||
export const parseOtherOfflinePayload = parseSeatPayload;
|
export const parseOtherJoinRoomPayload = parseSeatPayload;
|
||||||
|
export const parseOtherExitRoomPayload = parseSeatPayload;
|
||||||
export interface ParsedSelfExitPayload {
|
export const parseOtherOfflinePayload = parseSeatPayload;
|
||||||
readonly seat?: number;
|
|
||||||
readonly raw: JsonRecord;
|
export interface ParsedSelfExitPayload {
|
||||||
}
|
readonly seat?: number;
|
||||||
|
readonly raw: JsonRecord;
|
||||||
export function parseSelfExitRoomPayload(value: unknown): ParsedSelfExitPayload {
|
}
|
||||||
const raw = requireRecord(value, '$.data');
|
|
||||||
const seat = optionalField(raw, 'seat', '$.data', requireInteger);
|
export function parseSelfExitRoomPayload(value: unknown): ParsedSelfExitPayload {
|
||||||
if (seat !== undefined && seat < 0) {
|
const raw = requireRecord(value, '$.data');
|
||||||
throw new RangeError('$.data.seat: expected non-negative server seat, received number');
|
const seat = optionalField(raw, 'seat', '$.data', requireInteger);
|
||||||
}
|
if (seat !== undefined && seat < 0) {
|
||||||
return { ...(seat === undefined ? {} : { seat }), raw };
|
throw new RangeError('$.data.seat: expected non-negative server seat, received number');
|
||||||
}
|
}
|
||||||
|
return { ...(seat === undefined ? {} : { seat }), raw };
|
||||||
export interface ParsedOnlinePayload extends ParsedSeatPayload {
|
}
|
||||||
readonly ip: string;
|
|
||||||
}
|
export interface ParsedOnlinePayload extends ParsedSeatPayload {
|
||||||
|
readonly ip: string;
|
||||||
export function parseOtherOnlinePayload(value: unknown): ParsedOnlinePayload {
|
}
|
||||||
const parsed = parseSeatPayload(value);
|
|
||||||
return { seat: parsed.seat, ip: requireString(parsed.raw.ip, '$.data.ip'), raw: parsed.raw };
|
export function parseOtherOnlinePayload(value: unknown): ParsedOnlinePayload {
|
||||||
}
|
const parsed = parseSeatPayload(value);
|
||||||
|
return { seat: parsed.seat, ip: requireString(parsed.raw.ip, '$.data.ip'), raw: parsed.raw };
|
||||||
export interface ParsedRoomserverPayload {
|
}
|
||||||
readonly roomserver: string;
|
|
||||||
readonly raw: JsonRecord;
|
export interface ParsedRoomserverPayload {
|
||||||
}
|
readonly roomserver: string;
|
||||||
|
readonly raw: JsonRecord;
|
||||||
export function parseConnectRoomserverPayload(value: unknown): ParsedRoomserverPayload {
|
}
|
||||||
const raw = requireRecord(value, '$.data');
|
|
||||||
return { roomserver: requireString(raw.roomserver, '$.data.roomserver'), raw };
|
export function parseConnectRoomserverPayload(value: unknown): ParsedRoomserverPayload {
|
||||||
}
|
const raw = requireRecord(value, '$.data');
|
||||||
|
return { roomserver: requireString(raw.roomserver, '$.data.roomserver'), raw };
|
||||||
export interface ParsedAgentserverPayload {
|
}
|
||||||
readonly agentserver: string;
|
|
||||||
readonly opt: string;
|
export interface ParsedAgentserverPayload {
|
||||||
readonly raw: JsonRecord;
|
readonly agentserver: string;
|
||||||
}
|
readonly opt: string;
|
||||||
|
readonly raw: JsonRecord;
|
||||||
export function parseConnectAgentserverPayload(value: unknown): ParsedAgentserverPayload {
|
}
|
||||||
const raw = requireRecord(value, '$.data');
|
|
||||||
return {
|
export function parseConnectAgentserverPayload(value: unknown): ParsedAgentserverPayload {
|
||||||
agentserver: requireString(raw.agentserver, '$.data.agentserver'),
|
const raw = requireRecord(value, '$.data');
|
||||||
opt: requireString(raw.opt, '$.data.opt'),
|
return {
|
||||||
raw,
|
agentserver: requireString(raw.agentserver, '$.data.agentserver'),
|
||||||
};
|
opt: requireString(raw.opt, '$.data.opt'),
|
||||||
}
|
raw,
|
||||||
|
};
|
||||||
export interface ParsedKickPayload {
|
}
|
||||||
readonly msg: string;
|
|
||||||
readonly raw: JsonRecord;
|
export interface ParsedKickPayload {
|
||||||
}
|
readonly msg: string;
|
||||||
|
readonly raw: JsonRecord;
|
||||||
export function parseKickServerPayload(value: unknown): ParsedKickPayload {
|
}
|
||||||
const raw = requireRecord(value, '$.data');
|
|
||||||
return { msg: requireString(raw.msg, '$.data.msg'), raw };
|
export function parseKickServerPayload(value: unknown): ParsedKickPayload {
|
||||||
}
|
const raw = requireRecord(value, '$.data');
|
||||||
|
return { msg: requireString(raw.msg, '$.data.msg'), raw };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface CreateRoomEnvironment {
|
||||||
|
readonly roomtype: readonly unknown[];
|
||||||
|
readonly location: unknown;
|
||||||
|
readonly ip: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Transport contract only: roomtype contents belong to the subgame. */
|
||||||
|
export function buildCreateRoomRequest(
|
||||||
|
runtimeConfig: FirstSliceRuntimeConfig,
|
||||||
|
playerid: number,
|
||||||
|
command: CreateRoomEnvironment,
|
||||||
|
): FirstSliceEnvelope<'create_room', Readonly<Record<string, unknown>>> {
|
||||||
|
const identity = requireRecord(runtimeConfig.identity, '$.runtimeConfig.identity');
|
||||||
|
const raw = requireRecord(command, '$.command');
|
||||||
|
if (!hasOwn(raw, 'location') || raw.location === undefined) {
|
||||||
|
throw new TypeError('$.command.location: expected present field');
|
||||||
|
}
|
||||||
|
if (raw.location !== null) requireRecord(raw.location, '$.command.location');
|
||||||
|
return buildFirstSliceEnvelope('create_room', {
|
||||||
|
agentid: requireStringOrFiniteNumber(identity.agentid, '$.identity.agentid'),
|
||||||
|
playerid: requireInteger(playerid, '$.playerid'),
|
||||||
|
gameid: requireStringOrFiniteNumber(identity.gameid, '$.identity.gameid'),
|
||||||
|
roomtype: requireArray(raw.roomtype, '$.command.roomtype'),
|
||||||
|
ip: requireString(raw.ip, '$.command.ip'),
|
||||||
|
location: raw.location,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ParsedCreateRoomResponse {
|
||||||
|
readonly state: number;
|
||||||
|
readonly room: { readonly roomcode: string; readonly seat: number; readonly roomtype: readonly unknown[] } | null;
|
||||||
|
readonly needprepare: number;
|
||||||
|
readonly infinite: number;
|
||||||
|
readonly raw: JsonRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseCreateRoomResponse(value: unknown): ParsedCreateRoomResponse {
|
||||||
|
const raw = requireRecord(value, '$.data');
|
||||||
|
const state = requireInteger(raw.state, '$.data.state');
|
||||||
|
if (state !== 0) return {state, room: null, needprepare: 0, infinite: 0, raw};
|
||||||
|
// Desk.Init initializes these flags to zero; create_room overrides only when provided.
|
||||||
|
const flag = (key: string): number => {
|
||||||
|
if (!hasOwn(raw, key)) return 0;
|
||||||
|
const value = requireInteger(raw[key], `$.data.${key}`);
|
||||||
|
if (value !== 0 && value !== 1) throw new RangeError(`$.data.${key}: expected protocol flag`);
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
state,
|
||||||
|
room: {roomcode: requireString(raw.roomcode, '$.data.roomcode'),
|
||||||
|
seat: requireInteger(raw.seat, '$.data.seat'),
|
||||||
|
roomtype: requireArray(raw.roomtype, '$.data.roomtype')},
|
||||||
|
needprepare: flag('needprepare'), infinite: flag('infinite'), raw,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,77 +1,91 @@
|
|||||||
import { APP } from '../core/constants.ts';
|
import { APP } from '../core/constants.ts';
|
||||||
|
|
||||||
/** Legacy 12_Logic dispatches login replies from all three platform routes by rpc.
|
/** Legacy 12_Logic dispatches login replies from all three platform routes by rpc.
|
||||||
* This inbound contract is independent of the outbound agent route below. */
|
* This inbound contract is independent of the outbound agent route below. */
|
||||||
export function isLoginResponse(message:{route:string;rpc:string}):boolean{
|
export function isLoginResponse(message:{route:string;rpc:string}):boolean{
|
||||||
return message.rpc==='player_login'&&(message.route==='agent'||message.route==='room'||message.route==='platform');
|
return message.rpc==='player_login'&&(message.route==='agent'||message.route==='room'||message.route==='platform');
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FirstSliceRpc =
|
export type FirstSliceRpc =
|
||||||
| 'getInfoByShortCode'
|
| 'getVipRankList'
|
||||||
| 'get_player_task'
|
| 'get_treasurelist'
|
||||||
| 'get_task_award'
|
| 'topup_card'
|
||||||
| 'update_roomcard'
|
| 'get_player_invitecode'
|
||||||
| 'player_finish_task'
|
| 'binding_invitecode'
|
||||||
| 'player_login'
|
| 'getInfoByShortCode'
|
||||||
| 'self_join_room'
|
| 'get_player_task'
|
||||||
| 'player_prepare'
|
| 'get_task_award'
|
||||||
| 'self_exit_room'
|
| 'update_bean'
|
||||||
| 'other_join_room'
|
| 'update_roomcard'
|
||||||
| 'other_exit_room'
|
| 'player_finish_task'
|
||||||
| 'other_offline'
|
| 'player_login'
|
||||||
| 'other_online'
|
| 'create_room'
|
||||||
| 'connect_roomserver'
|
| 'self_join_room'
|
||||||
| 'connect_agentserver'
|
| 'player_prepare'
|
||||||
| 'kick_server'
|
| 'self_exit_room'
|
||||||
| 'kick_offline'
|
| 'other_join_room'
|
||||||
| 'show_message';
|
| 'other_exit_room'
|
||||||
|
| 'other_offline'
|
||||||
export type FirstSliceRoute = 'platform' | 'agent' | 'room';
|
| 'other_online'
|
||||||
|
| 'connect_roomserver'
|
||||||
/** 第一纵向切片 rpc → route 的唯一权威映射。 */
|
| 'connect_agentserver'
|
||||||
export const FIRST_SLICE_RPC_ROUTE = {
|
| 'kick_server'
|
||||||
getInfoByShortCode: 'agent',
|
| 'kick_offline'
|
||||||
get_player_task: 'agent',
|
| 'show_message';
|
||||||
get_task_award: 'agent',
|
|
||||||
update_roomcard: 'agent',
|
export type FirstSliceRoute = 'platform' | 'agent' | 'room';
|
||||||
player_finish_task: 'agent',
|
|
||||||
player_login: 'agent',
|
/** 第一纵向切片 rpc → route 的唯一权威映射。 */
|
||||||
self_join_room: 'agent',
|
export const FIRST_SLICE_RPC_ROUTE = {
|
||||||
player_prepare: 'room',
|
getVipRankList: 'agent',
|
||||||
self_exit_room: 'room',
|
get_treasurelist: 'agent',
|
||||||
other_join_room: 'room',
|
topup_card: 'agent',
|
||||||
other_exit_room: 'room',
|
get_player_invitecode: 'agent',
|
||||||
other_offline: 'room',
|
binding_invitecode: 'agent',
|
||||||
other_online: 'room',
|
getInfoByShortCode: 'agent',
|
||||||
connect_roomserver: 'room',
|
get_player_task: 'agent',
|
||||||
connect_agentserver: 'agent',
|
get_task_award: 'agent',
|
||||||
kick_server: 'agent',
|
update_bean: 'agent',
|
||||||
kick_offline: 'agent',
|
update_roomcard: 'agent',
|
||||||
show_message: 'agent',
|
player_finish_task: 'agent',
|
||||||
} as const satisfies Record<FirstSliceRpc, 'platform' | 'agent' | 'room'>;
|
player_login: 'agent',
|
||||||
|
create_room: 'agent',
|
||||||
export function routeForFirstSlice<Rpc extends FirstSliceRpc>(
|
self_join_room: 'agent',
|
||||||
rpc: Rpc,
|
player_prepare: 'room',
|
||||||
): (typeof FIRST_SLICE_RPC_ROUTE)[Rpc] {
|
self_exit_room: 'room',
|
||||||
if (!Object.prototype.hasOwnProperty.call(FIRST_SLICE_RPC_ROUTE, rpc)) {
|
other_join_room: 'room',
|
||||||
throw new Error(`FIRST_SLICE_RPC_ROUTE 未定义 rpc: ${String(rpc)}`);
|
other_exit_room: 'room',
|
||||||
}
|
other_offline: 'room',
|
||||||
return FIRST_SLICE_RPC_ROUTE[rpc];
|
other_online: 'room',
|
||||||
}
|
connect_roomserver: 'room',
|
||||||
|
connect_agentserver: 'agent',
|
||||||
export interface FirstSliceEnvelope<
|
kick_server: 'agent',
|
||||||
Rpc extends FirstSliceRpc,
|
kick_offline: 'agent',
|
||||||
Data extends Readonly<Record<string, unknown>>,
|
show_message: 'agent',
|
||||||
> {
|
} as const satisfies Record<FirstSliceRpc, 'platform' | 'agent' | 'room'>;
|
||||||
readonly app: 'youle';
|
|
||||||
readonly route: (typeof FIRST_SLICE_RPC_ROUTE)[Rpc];
|
export function routeForFirstSlice<Rpc extends FirstSliceRpc>(
|
||||||
readonly rpc: Rpc;
|
rpc: Rpc,
|
||||||
readonly data: Data;
|
): (typeof FIRST_SLICE_RPC_ROUTE)[Rpc] {
|
||||||
}
|
if (!Object.prototype.hasOwnProperty.call(FIRST_SLICE_RPC_ROUTE, rpc)) {
|
||||||
|
throw new Error(`FIRST_SLICE_RPC_ROUTE 未定义 rpc: ${String(rpc)}`);
|
||||||
export function buildFirstSliceEnvelope<
|
}
|
||||||
Rpc extends FirstSliceRpc,
|
return FIRST_SLICE_RPC_ROUTE[rpc];
|
||||||
Data extends Readonly<Record<string, unknown>>,
|
}
|
||||||
>(rpc: Rpc, data: Data): FirstSliceEnvelope<Rpc, Data> {
|
|
||||||
return { app: APP, route: routeForFirstSlice(rpc), rpc, data };
|
export interface FirstSliceEnvelope<
|
||||||
}
|
Rpc extends FirstSliceRpc,
|
||||||
|
Data extends Readonly<Record<string, unknown>>,
|
||||||
|
> {
|
||||||
|
readonly app: 'youle';
|
||||||
|
readonly route: (typeof FIRST_SLICE_RPC_ROUTE)[Rpc];
|
||||||
|
readonly rpc: Rpc;
|
||||||
|
readonly data: Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildFirstSliceEnvelope<
|
||||||
|
Rpc extends FirstSliceRpc,
|
||||||
|
Data extends Readonly<Record<string, unknown>>,
|
||||||
|
>(rpc: Rpc, data: Data): FirstSliceEnvelope<Rpc, Data> {
|
||||||
|
return { app: APP, route: routeForFirstSlice(rpc), rpc, data };
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,206 +1,228 @@
|
|||||||
import type { InboundMessage } from '../core/types/envelope.ts';
|
import type { InboundMessage } from '../core/types/envelope.ts';
|
||||||
import type { GameSessionHost } from '../sdk/runtime/game-session-host.ts';
|
import type { GameSessionHost } from '../sdk/runtime/game-session-host.ts';
|
||||||
import type { ConnectionControlPort } from '../platform/connection-intent.ts';
|
import type { ConnectionControlPort } from '../platform/connection-intent.ts';
|
||||||
import type { RuntimeSession } from '../platform/runtime-session.ts';
|
import type { RuntimeSession } from '../platform/runtime-session.ts';
|
||||||
import type { PlatformStore } from '../platform/stores/platform-store.ts';
|
import type { PlatformStore } from '../platform/stores/platform-store.ts';
|
||||||
import { requireRecord, requireString } from './contracts/validation.ts';
|
import { requireRecord, requireString } from './contracts/validation.ts';
|
||||||
import {
|
import {
|
||||||
parseConnectAgentserverPayload,
|
parseConnectAgentserverPayload,
|
||||||
parseConnectRoomserverPayload,
|
parseConnectRoomserverPayload,
|
||||||
parseKickServerPayload,
|
parseKickServerPayload,
|
||||||
parseOtherExitRoomPayload,
|
parseOtherExitRoomPayload,
|
||||||
parseOtherJoinRoomPayload,
|
parseOtherJoinRoomPayload,
|
||||||
parseOtherOfflinePayload,
|
parseOtherOfflinePayload,
|
||||||
parseOtherOnlinePayload,
|
parseOtherOnlinePayload,
|
||||||
parsePlayerPreparePayload,
|
parsePlayerPreparePayload,
|
||||||
parseSelfExitRoomPayload,
|
parseSelfExitRoomPayload,
|
||||||
} from './contracts/index.ts';
|
} from './contracts/index.ts';
|
||||||
import {
|
import {
|
||||||
buildFirstSliceEnvelope,
|
buildFirstSliceEnvelope,
|
||||||
isLoginResponse,
|
isLoginResponse,
|
||||||
FIRST_SLICE_RPC_ROUTE,
|
FIRST_SLICE_RPC_ROUTE,
|
||||||
routeForFirstSlice,
|
routeForFirstSlice,
|
||||||
type FirstSliceRpc,
|
type FirstSliceRpc,
|
||||||
} from './first-slice-routes.ts';
|
} from './first-slice-routes.ts';
|
||||||
|
|
||||||
export interface PlatformHandlersOptions {
|
export interface PlatformHandlersOptions {
|
||||||
readonly store: PlatformStore;
|
readonly store: PlatformStore;
|
||||||
readonly gameSession: GameSessionHost;
|
readonly gameSession: GameSessionHost;
|
||||||
readonly session: RuntimeSession;
|
readonly session: RuntimeSession;
|
||||||
readonly connection: ConnectionControlPort;
|
readonly connection: ConnectionControlPort;
|
||||||
readonly showMessage?: (message:string,time:number,source:'show_message'|'update_roomcard')=>void;
|
readonly showMessage?: (message:string,time:number,source:'show_message'|'update_roomcard'|'update_bean'|'binding_invitecode')=>void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PlatformDispatchResult = 'handled-platform';
|
export type PlatformDispatchResult = 'handled-platform';
|
||||||
|
|
||||||
/** Strict first-slice platform RPC dispatcher. */
|
/** Strict first-slice platform RPC dispatcher. */
|
||||||
export class PlatformHandlers {
|
export class PlatformHandlers {
|
||||||
constructor(private readonly options: PlatformHandlersOptions) {}
|
constructor(private readonly options: PlatformHandlersOptions) {}
|
||||||
|
|
||||||
dispatch(message: InboundMessage): PlatformDispatchResult {
|
dispatch(message: InboundMessage): PlatformDispatchResult {
|
||||||
if (!isPlatformRoute(message.route)) {
|
if (!isPlatformRoute(message.route)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`PlatformHandlers received non-platform route: route=${message.route} rpc=${message.rpc}`,
|
`PlatformHandlers received non-platform route: route=${message.route} rpc=${message.rpc}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!Object.prototype.hasOwnProperty.call(FIRST_SLICE_RPC_ROUTE, message.rpc)) {
|
if (!Object.prototype.hasOwnProperty.call(FIRST_SLICE_RPC_ROUTE, message.rpc)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unsupported platform rpc: route=${message.route} rpc=${message.rpc}`,
|
`Unsupported platform rpc: route=${message.route} rpc=${message.rpc}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const rpc = message.rpc as FirstSliceRpc;
|
const rpc = message.rpc as FirstSliceRpc;
|
||||||
const expectedRoute = routeForFirstSlice(rpc);
|
const expectedRoute = routeForFirstSlice(rpc);
|
||||||
if (message.route !== expectedRoute && !isLoginResponse(message)) {
|
if (message.route !== expectedRoute && !isLoginResponse(message) && !(rpc==='update_bean'&&message.route==='room')) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Platform rpc route mismatch: rpc=${rpc} expected=${expectedRoute} received=${message.route}`,
|
`Platform rpc route mismatch: rpc=${rpc} expected=${expectedRoute} received=${message.route}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (rpc) {
|
switch (rpc) {
|
||||||
case 'getInfoByShortCode':this.options.store.applyVipRooms(message.data);break;
|
case 'getVipRankList':this.options.store.applyVipRank(message.data);break;
|
||||||
case 'update_roomcard': {
|
case 'get_treasurelist':this.options.store.applyTreasureList(message.data);break;
|
||||||
const raw=requireRecord(message.data,'$.data');
|
case 'topup_card':break; // Original response is empty; updates arrive separately.
|
||||||
const text=raw.text?requireString(raw.text,'$.data.text'):null;
|
case 'get_player_invitecode':this.options.store.applyInvitecode(message.data);break;
|
||||||
this.options.store.applyRoomcardUpdate(raw);
|
case 'binding_invitecode':{
|
||||||
if(raw.change===undefined&&text){
|
if(!this.options.showMessage)throw Error('binding_invitecode requires a message UI');
|
||||||
if(!this.options.showMessage)throw Error('update_roomcard requires a message UI');
|
const text=this.options.store.applyInviteBinding(message.data);
|
||||||
this.options.showMessage(text,0,'update_roomcard');
|
this.options.showMessage(text,3000,'binding_invitecode');break;
|
||||||
}
|
}
|
||||||
break;
|
case 'getInfoByShortCode':this.options.store.applyVipRooms(message.data);break;
|
||||||
}
|
case 'update_bean': {
|
||||||
case 'get_player_task':this.options.store.applyTaskList(message.data);break;
|
const raw=requireRecord(message.data,'$.data');
|
||||||
case 'get_task_award':this.options.store.applyTaskAward(message.data);break;
|
const text=raw.text?requireString(raw.text,'$.data.text'):null;
|
||||||
case 'player_finish_task':this.options.store.applyTaskFinished(message.data);break;
|
this.options.store.applyBeanUpdate(raw);
|
||||||
case 'player_login':
|
if(raw.change===undefined&&raw.seat===undefined&&text){
|
||||||
this.options.session.handleLogin(message.data);
|
if(!this.options.showMessage)throw Error('update_bean requires a message UI');
|
||||||
break;
|
this.options.showMessage(text,0,'update_bean');
|
||||||
case 'self_join_room':
|
}
|
||||||
this.options.session.handleSelfJoin(message.data);
|
break;
|
||||||
break;
|
}
|
||||||
case 'player_prepare': {
|
case 'update_roomcard': {
|
||||||
const parsed = parsePlayerPreparePayload(message.data);
|
const raw=requireRecord(message.data,'$.data');
|
||||||
this.requireActiveGame(rpc);
|
const text=raw.text?requireString(raw.text,'$.data.text'):null;
|
||||||
this.options.store.playerReady(parsed);
|
this.options.store.applyRoomcardUpdate(raw);
|
||||||
this.options.gameSession.publish({ type: 'room.player-ready', seat: parsed.seat });
|
if(raw.change===undefined&&text){
|
||||||
break;
|
if(!this.options.showMessage)throw Error('update_roomcard requires a message UI');
|
||||||
}
|
this.options.showMessage(text,0,'update_roomcard');
|
||||||
case 'self_exit_room': {
|
}
|
||||||
const parsed = parseSelfExitRoomPayload(message.data);
|
break;
|
||||||
this.options.session.handleSelfExit(parsed);
|
}
|
||||||
break;
|
case 'get_player_task':this.options.store.applyTaskList(message.data);break;
|
||||||
}
|
case 'get_task_award':this.options.store.applyTaskAward(message.data);break;
|
||||||
case 'other_join_room': {
|
case 'player_finish_task':this.options.store.applyTaskFinished(message.data);break;
|
||||||
const parsed = parseOtherJoinRoomPayload(message.data);
|
case 'player_login':
|
||||||
this.requireActiveGame(rpc);
|
this.options.session.handleLogin(message.data);
|
||||||
this.options.store.playerJoin(parsed);
|
break;
|
||||||
this.options.gameSession.publish({ type: 'room.player-joined', seat: parsed.seat });
|
case 'create_room':
|
||||||
break;
|
this.options.session.handleCreateRoom(message.data);
|
||||||
}
|
break;
|
||||||
case 'other_exit_room': {
|
case 'self_join_room':
|
||||||
const parsed = parseOtherExitRoomPayload(message.data);
|
this.options.session.handleSelfJoin(message.data);
|
||||||
this.requireActiveGame(rpc);
|
break;
|
||||||
this.options.store.playerExit(parsed);
|
case 'player_prepare': {
|
||||||
this.options.gameSession.publish({ type: 'room.player-left', seat: parsed.seat });
|
const parsed = parsePlayerPreparePayload(message.data);
|
||||||
break;
|
this.requireActiveGame(rpc);
|
||||||
}
|
this.options.store.playerReady(parsed);
|
||||||
case 'other_offline': {
|
this.options.gameSession.publish({ type: 'room.player-ready', seat: parsed.seat });
|
||||||
const parsed = parseOtherOfflinePayload(message.data);
|
break;
|
||||||
this.requireActiveGame(rpc);
|
}
|
||||||
this.options.store.playerOffline(parsed);
|
case 'self_exit_room': {
|
||||||
this.options.gameSession.publish({ type: 'room.player-offline', seat: parsed.seat });
|
const parsed = parseSelfExitRoomPayload(message.data);
|
||||||
break;
|
this.options.session.handleSelfExit(parsed);
|
||||||
}
|
break;
|
||||||
case 'other_online': {
|
}
|
||||||
const parsed = parseOtherOnlinePayload(message.data);
|
case 'other_join_room': {
|
||||||
this.requireActiveGame(rpc);
|
const parsed = parseOtherJoinRoomPayload(message.data);
|
||||||
this.options.store.playerOnline(parsed);
|
this.requireActiveGame(rpc);
|
||||||
this.options.gameSession.publish({ type: 'room.player-online', seat: parsed.seat });
|
this.options.store.playerJoin(parsed);
|
||||||
break;
|
this.options.gameSession.publish({ type: 'room.player-joined', seat: parsed.seat });
|
||||||
}
|
break;
|
||||||
case 'connect_roomserver': {
|
}
|
||||||
const parsed = parseConnectRoomserverPayload(message.data);
|
case 'other_exit_room': {
|
||||||
const target = parseSwitchTarget(parsed.roomserver, '$.data.roomserver');
|
const parsed = parseOtherExitRoomPayload(message.data);
|
||||||
const resend = buildFirstSliceEnvelope('connect_roomserver', parsed.raw);
|
this.requireActiveGame(rpc);
|
||||||
this.options.session.handleServerSwitch(
|
this.options.store.playerExit(parsed);
|
||||||
() => this.options.connection.switchServer(target, resend),
|
this.options.gameSession.publish({ type: 'room.player-left', seat: parsed.seat });
|
||||||
);
|
break;
|
||||||
break;
|
}
|
||||||
}
|
case 'other_offline': {
|
||||||
case 'connect_agentserver': {
|
const parsed = parseOtherOfflinePayload(message.data);
|
||||||
const parsed = parseConnectAgentserverPayload(message.data);
|
this.requireActiveGame(rpc);
|
||||||
const target = parseSwitchTarget(parsed.agentserver, '$.data.agentserver');
|
this.options.store.playerOffline(parsed);
|
||||||
const resend = buildFirstSliceEnvelope('connect_agentserver', parsed.raw);
|
this.options.gameSession.publish({ type: 'room.player-offline', seat: parsed.seat });
|
||||||
this.options.session.handleServerSwitch(
|
break;
|
||||||
() => this.options.connection.switchServer(target, resend),
|
}
|
||||||
);
|
case 'other_online': {
|
||||||
break;
|
const parsed = parseOtherOnlinePayload(message.data);
|
||||||
}
|
this.requireActiveGame(rpc);
|
||||||
case 'show_message': {
|
this.options.store.playerOnline(parsed);
|
||||||
const data=requireRecord(message.data,'$.data');
|
this.options.gameSession.publish({ type: 'room.player-online', seat: parsed.seat });
|
||||||
const text=requireString(data.msg,'$.data.msg');
|
break;
|
||||||
// Desk.show_message converts the original time with Number; only >0 auto-closes.
|
}
|
||||||
const time=Number(data.time);
|
case 'connect_roomserver': {
|
||||||
if(!this.options.showMessage)throw Error('show_message requires a message UI');
|
const parsed = parseConnectRoomserverPayload(message.data);
|
||||||
this.options.showMessage(text,time,'show_message');
|
const target = parseSwitchTarget(parsed.roomserver, '$.data.roomserver');
|
||||||
break;
|
const resend = buildFirstSliceEnvelope('connect_roomserver', parsed.raw);
|
||||||
}
|
this.options.session.handleServerSwitch(
|
||||||
case 'kick_offline': {
|
() => this.options.connection.switchServer(target, resend),
|
||||||
const data=requireRecord(message.data,'$.data');
|
);
|
||||||
this.options.session.handleKick(data,()=>this.options.connection.stopForKick(data));
|
break;
|
||||||
break;
|
}
|
||||||
}
|
case 'connect_agentserver': {
|
||||||
case 'kick_server': {
|
const parsed = parseConnectAgentserverPayload(message.data);
|
||||||
const parsed = parseKickServerPayload(message.data);
|
const target = parseSwitchTarget(parsed.agentserver, '$.data.agentserver');
|
||||||
this.options.session.handleKick(
|
const resend = buildFirstSliceEnvelope('connect_agentserver', parsed.raw);
|
||||||
parsed.raw,
|
this.options.session.handleServerSwitch(
|
||||||
() => this.options.connection.stopForKick(parsed.raw),
|
() => this.options.connection.switchServer(target, resend),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
case 'show_message': {
|
||||||
return 'handled-platform';
|
const data=requireRecord(message.data,'$.data');
|
||||||
}
|
const text=requireString(data.msg,'$.data.msg');
|
||||||
|
// Desk.show_message converts the original time with Number; only >0 auto-closes.
|
||||||
private requireActiveGame(rpc: string): void {
|
const time=Number(data.time);
|
||||||
if (this.options.gameSession.state !== 'active') {
|
if(!this.options.showMessage)throw Error('show_message requires a message UI');
|
||||||
throw new Error(`${rpc} requires an active game session`);
|
this.options.showMessage(text,time,'show_message');
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
case 'kick_offline': {
|
||||||
|
const data=requireRecord(message.data,'$.data');
|
||||||
function isPlatformRoute(route: string): route is 'platform' | 'agent' | 'room' {
|
this.options.session.handleKick(data,()=>this.options.connection.stopForKick(data));
|
||||||
return route === 'platform' || route === 'agent' || route === 'room';
|
break;
|
||||||
}
|
}
|
||||||
|
case 'kick_server': {
|
||||||
function parseSwitchTarget(address: string, path: string): string {
|
const parsed = parseKickServerPayload(message.data);
|
||||||
if (address.length === 0 || address.trim() !== address) {
|
this.options.session.handleKick(
|
||||||
throw new TypeError(`${path}: expected a non-empty WebSocket server address`);
|
parsed.raw,
|
||||||
}
|
() => this.options.connection.stopForKick(parsed.raw),
|
||||||
const scheme = /^([A-Za-z][A-Za-z0-9+.-]*):\/\//.exec(address);
|
);
|
||||||
const target = scheme ? address : `ws://${address}`;
|
break;
|
||||||
const authority = /^[A-Za-z][A-Za-z0-9+.-]*:\/\/([^/?#]*)/.exec(target)?.[1];
|
}
|
||||||
if (authority === undefined || authority.length === 0) {
|
}
|
||||||
throw new TypeError(`${path}: expected a WebSocket address with a non-empty host`);
|
return 'handled-platform';
|
||||||
}
|
}
|
||||||
if (target.includes('#') || authority.includes('@')) {
|
|
||||||
throw new TypeError(`${path}: expected a WebSocket address without fragment or userinfo`);
|
private requireActiveGame(rpc: string): void {
|
||||||
}
|
if (this.options.gameSession.state !== 'active') {
|
||||||
|
throw new Error(`${rpc} requires an active game session`);
|
||||||
let parsed: URL;
|
}
|
||||||
try {
|
}
|
||||||
parsed = new URL(target);
|
}
|
||||||
} catch {
|
|
||||||
throw new TypeError(`${path}: expected a valid WebSocket server address`);
|
function isPlatformRoute(route: string): route is 'platform' | 'agent' | 'room' {
|
||||||
}
|
return route === 'platform' || route === 'agent' || route === 'room';
|
||||||
const protocol = parsed.protocol.toLowerCase();
|
}
|
||||||
if (
|
|
||||||
(protocol !== 'ws:' && protocol !== 'wss:')
|
function parseSwitchTarget(address: string, path: string): string {
|
||||||
|| parsed.hostname.length === 0
|
if (address.length === 0 || address.trim() !== address) {
|
||||||
|| parsed.username.length > 0
|
throw new TypeError(`${path}: expected a non-empty WebSocket server address`);
|
||||||
|| parsed.password.length > 0
|
}
|
||||||
|| parsed.hash.length > 0
|
const scheme = /^([A-Za-z][A-Za-z0-9+.-]*):\/\//.exec(address);
|
||||||
) {
|
const target = scheme ? address : `ws://${address}`;
|
||||||
throw new TypeError(`${path}: expected a valid WebSocket server address`);
|
const authority = /^[A-Za-z][A-Za-z0-9+.-]*:\/\/([^/?#]*)/.exec(target)?.[1];
|
||||||
}
|
if (authority === undefined || authority.length === 0) {
|
||||||
return target;
|
throw new TypeError(`${path}: expected a WebSocket address with a non-empty host`);
|
||||||
}
|
}
|
||||||
|
if (target.includes('#') || authority.includes('@')) {
|
||||||
|
throw new TypeError(`${path}: expected a WebSocket address without fragment or userinfo`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsed: URL;
|
||||||
|
try {
|
||||||
|
parsed = new URL(target);
|
||||||
|
} catch {
|
||||||
|
throw new TypeError(`${path}: expected a valid WebSocket server address`);
|
||||||
|
}
|
||||||
|
const protocol = parsed.protocol.toLowerCase();
|
||||||
|
if (
|
||||||
|
(protocol !== 'ws:' && protocol !== 'wss:')
|
||||||
|
|| parsed.hostname.length === 0
|
||||||
|
|| parsed.username.length > 0
|
||||||
|
|| parsed.password.length > 0
|
||||||
|
|| parsed.hash.length > 0
|
||||||
|
) {
|
||||||
|
throw new TypeError(`${path}: expected a valid WebSocket server address`);
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/** Pre-room capability: a game owns its page and validated, opaque wire configuration. */
|
||||||
|
export interface CreateRoomPageContext {
|
||||||
|
readonly previousRoomtype:unknown;
|
||||||
|
submit(roomtype:readonly unknown[]):void;
|
||||||
|
cancel():void;
|
||||||
|
reportError(message:string):void;
|
||||||
|
}
|
||||||
|
export interface CreateRoomPagePort {
|
||||||
|
open(context:CreateRoomPageContext):void;
|
||||||
|
setBusy(busy:boolean):void;
|
||||||
|
close():void;
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "fd4c9495-d6d5-49da-8476-a510df24f1c4",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
export * from './game-entry.ts';
|
export * from './game-entry.ts';
|
||||||
export * from './game-host.ts';
|
export * from './create-room-page.ts';
|
||||||
export * from './game-module.ts';
|
export * from './game-host.ts';
|
||||||
export * from './game-seat-mapper.ts';
|
export * from './game-module.ts';
|
||||||
export * from './platform-events.ts';
|
export * from './game-seat-mapper.ts';
|
||||||
export * from './snapshots.ts';
|
export * from './platform-events.ts';
|
||||||
|
export * from './snapshots.ts';
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"__type__": "cc.AnimationClip",
|
||||||
|
"_name": "AnnouncementHorn",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_native": "",
|
||||||
|
"sample": 2,
|
||||||
|
"speed": 1,
|
||||||
|
"wrapMode": 2,
|
||||||
|
"enableTrsBlending": false,
|
||||||
|
"_duration": 2,
|
||||||
|
"_hash": 0,
|
||||||
|
"_tracks": [
|
||||||
|
{
|
||||||
|
"__id__": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_exoticAnimation": null,
|
||||||
|
"_events": [],
|
||||||
|
"_embeddedPlayers": [],
|
||||||
|
"_additiveSettings": {
|
||||||
|
"__id__": 6
|
||||||
|
},
|
||||||
|
"_auxiliaryCurveEntries": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.animation.ObjectTrack",
|
||||||
|
"_binding": {
|
||||||
|
"__type__": "cc.animation.TrackBinding",
|
||||||
|
"path": {
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
"proxy": null
|
||||||
|
},
|
||||||
|
"_channel": {
|
||||||
|
"__id__": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.animation.TrackPath",
|
||||||
|
"_paths": [
|
||||||
|
{
|
||||||
|
"__id__": 3
|
||||||
|
},
|
||||||
|
"spriteFrame"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.animation.ComponentPath",
|
||||||
|
"component": "cc.Sprite"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.animation.Channel",
|
||||||
|
"_curve": {
|
||||||
|
"__id__": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.ObjectCurve",
|
||||||
|
"_times": [
|
||||||
|
0,
|
||||||
|
0.5,
|
||||||
|
1,
|
||||||
|
1.5
|
||||||
|
],
|
||||||
|
"_values": [
|
||||||
|
{
|
||||||
|
"__uuid__": "151439f3-c9bd-4fb2-97b5-5afc295d76e8@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "5647b603-4a35-4bd8-b955-a8ceca2a3f43@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "151439f3-c9bd-4fb2-97b5-5afc295d76e8@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "151439f3-c9bd-4fb2-97b5-5afc295d76e8@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.AnimationClipAdditiveSettings",
|
||||||
|
"enabled": false,
|
||||||
|
"refClip": null
|
||||||
|
}
|
||||||
|
]
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.0.4",
|
||||||
|
"importer": "animation-clip",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "aed03d13-bce1-42a0-9d23-ebd829d9ee55",
|
||||||
|
"files": [
|
||||||
|
".bin"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {
|
||||||
|
"name": "AnnouncementHorn"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"__type__": "cc.AnimationClip",
|
||||||
|
"_name": "RequestLoading",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_native": "",
|
||||||
|
"sample": 60,
|
||||||
|
"speed": 1,
|
||||||
|
"wrapMode": 2,
|
||||||
|
"enableTrsBlending": false,
|
||||||
|
"_duration": 1,
|
||||||
|
"_hash": 0,
|
||||||
|
"_tracks": [
|
||||||
|
{
|
||||||
|
"__id__": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_exoticAnimation": null,
|
||||||
|
"_events": [],
|
||||||
|
"_embeddedPlayers": [],
|
||||||
|
"_additiveSettings": {
|
||||||
|
"__id__": 5
|
||||||
|
},
|
||||||
|
"_auxiliaryCurveEntries": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.animation.RealTrack",
|
||||||
|
"_binding": {
|
||||||
|
"__type__": "cc.animation.TrackBinding",
|
||||||
|
"path": {
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
"proxy": null
|
||||||
|
},
|
||||||
|
"_channel": {
|
||||||
|
"__id__": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.animation.TrackPath",
|
||||||
|
"_paths": [
|
||||||
|
"angle"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.animation.Channel",
|
||||||
|
"_curve": {
|
||||||
|
"__id__": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealCurve",
|
||||||
|
"_times": [
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"_values": [
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealKeyframeValue",
|
||||||
|
"interpolationMode": 0,
|
||||||
|
"tangentWeightMode": 0,
|
||||||
|
"value": 0,
|
||||||
|
"rightTangent": 0,
|
||||||
|
"rightTangentWeight": 0,
|
||||||
|
"leftTangent": 0,
|
||||||
|
"leftTangentWeight": 0,
|
||||||
|
"easingMethod": 0,
|
||||||
|
"__editorExtras__": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.RealKeyframeValue",
|
||||||
|
"interpolationMode": 0,
|
||||||
|
"tangentWeightMode": 0,
|
||||||
|
"value": -360,
|
||||||
|
"rightTangent": 0,
|
||||||
|
"rightTangentWeight": 0,
|
||||||
|
"leftTangent": 0,
|
||||||
|
"leftTangentWeight": 0,
|
||||||
|
"easingMethod": 0,
|
||||||
|
"__editorExtras__": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preExtrapolation": 1,
|
||||||
|
"postExtrapolation": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.AnimationClipAdditiveSettings",
|
||||||
|
"enabled": false,
|
||||||
|
"refClip": null
|
||||||
|
}
|
||||||
|
]
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.0.4",
|
||||||
|
"importer": "animation-clip",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "987771c5-7f29-487a-b61a-476981cc9216",
|
||||||
|
"files": [
|
||||||
|
".bin"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {
|
||||||
|
"name": "RequestLoading"
|
||||||
|
}
|
||||||
|
}
|
||||||
+134
-134
@@ -1,134 +1,134 @@
|
|||||||
{
|
{
|
||||||
"ver": "1.0.27",
|
"ver": "1.0.27",
|
||||||
"importer": "image",
|
"importer": "image",
|
||||||
"imported": true,
|
"imported": true,
|
||||||
"uuid": "34daf5f4-3869-4f71-84c4-d09103701be6",
|
"uuid": "34daf5f4-3869-4f71-84c4-d09103701be6",
|
||||||
"files": [
|
"files": [
|
||||||
".json",
|
".json",
|
||||||
".png"
|
".png"
|
||||||
],
|
],
|
||||||
"subMetas": {
|
"subMetas": {
|
||||||
"6c48a": {
|
"6c48a": {
|
||||||
"importer": "texture",
|
"importer": "texture",
|
||||||
"uuid": "34daf5f4-3869-4f71-84c4-d09103701be6@6c48a",
|
"uuid": "34daf5f4-3869-4f71-84c4-d09103701be6@6c48a",
|
||||||
"displayName": "00042",
|
"displayName": "00042",
|
||||||
"id": "6c48a",
|
"id": "6c48a",
|
||||||
"name": "texture",
|
"name": "texture",
|
||||||
"userData": {
|
"userData": {
|
||||||
"wrapModeS": "clamp-to-edge",
|
"wrapModeS": "clamp-to-edge",
|
||||||
"wrapModeT": "clamp-to-edge",
|
"wrapModeT": "clamp-to-edge",
|
||||||
"imageUuidOrDatabaseUri": "34daf5f4-3869-4f71-84c4-d09103701be6",
|
"imageUuidOrDatabaseUri": "34daf5f4-3869-4f71-84c4-d09103701be6",
|
||||||
"isUuid": true,
|
"isUuid": true,
|
||||||
"visible": false,
|
"visible": false,
|
||||||
"minfilter": "linear",
|
"minfilter": "linear",
|
||||||
"magfilter": "linear",
|
"magfilter": "linear",
|
||||||
"mipfilter": "none",
|
"mipfilter": "none",
|
||||||
"anisotropy": 0
|
"anisotropy": 0
|
||||||
},
|
},
|
||||||
"ver": "1.0.22",
|
"ver": "1.0.22",
|
||||||
"imported": true,
|
"imported": true,
|
||||||
"files": [
|
"files": [
|
||||||
".json"
|
".json"
|
||||||
],
|
],
|
||||||
"subMetas": {}
|
"subMetas": {}
|
||||||
},
|
},
|
||||||
"f9941": {
|
"f9941": {
|
||||||
"importer": "sprite-frame",
|
"importer": "sprite-frame",
|
||||||
"uuid": "34daf5f4-3869-4f71-84c4-d09103701be6@f9941",
|
"uuid": "34daf5f4-3869-4f71-84c4-d09103701be6@f9941",
|
||||||
"displayName": "00042",
|
"displayName": "00042",
|
||||||
"id": "f9941",
|
"id": "f9941",
|
||||||
"name": "spriteFrame",
|
"name": "spriteFrame",
|
||||||
"userData": {
|
"userData": {
|
||||||
"trimThreshold": 1,
|
"trimThreshold": 1,
|
||||||
"rotated": false,
|
"rotated": false,
|
||||||
"offsetX": 0,
|
"offsetX": 0,
|
||||||
"offsetY": 0,
|
"offsetY": 0,
|
||||||
"trimX": 0,
|
"trimX": 0,
|
||||||
"trimY": 0,
|
"trimY": 0,
|
||||||
"width": 108,
|
"width": 108,
|
||||||
"height": 98,
|
"height": 98,
|
||||||
"rawWidth": 108,
|
"rawWidth": 108,
|
||||||
"rawHeight": 98,
|
"rawHeight": 98,
|
||||||
"borderTop": 48,
|
"borderTop": 26,
|
||||||
"borderBottom": 48,
|
"borderBottom": 34,
|
||||||
"borderLeft": 53,
|
"borderLeft": 30,
|
||||||
"borderRight": 53,
|
"borderRight": 31,
|
||||||
"packable": true,
|
"packable": true,
|
||||||
"pixelsToUnit": 100,
|
"pixelsToUnit": 100,
|
||||||
"pivotX": 0.5,
|
"pivotX": 0.5,
|
||||||
"pivotY": 0.5,
|
"pivotY": 0.5,
|
||||||
"meshType": 0,
|
"meshType": 0,
|
||||||
"vertices": {
|
"vertices": {
|
||||||
"rawPosition": [
|
"rawPosition": [
|
||||||
-54,
|
-54,
|
||||||
-49,
|
-49,
|
||||||
0,
|
0,
|
||||||
54,
|
54,
|
||||||
-49,
|
-49,
|
||||||
0,
|
0,
|
||||||
-54,
|
-54,
|
||||||
49,
|
49,
|
||||||
0,
|
0,
|
||||||
54,
|
54,
|
||||||
49,
|
49,
|
||||||
0
|
0
|
||||||
],
|
],
|
||||||
"indexes": [
|
"indexes": [
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
2,
|
2,
|
||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
3
|
3
|
||||||
],
|
],
|
||||||
"uv": [
|
"uv": [
|
||||||
0,
|
0,
|
||||||
98,
|
98,
|
||||||
108,
|
108,
|
||||||
98,
|
98,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
108,
|
108,
|
||||||
0
|
0
|
||||||
],
|
],
|
||||||
"nuv": [
|
"nuv": [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
1
|
1
|
||||||
],
|
],
|
||||||
"minPos": [
|
"minPos": [
|
||||||
-54,
|
-54,
|
||||||
-49,
|
-49,
|
||||||
0
|
0
|
||||||
],
|
],
|
||||||
"maxPos": [
|
"maxPos": [
|
||||||
54,
|
54,
|
||||||
49,
|
49,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"isUuid": true,
|
"isUuid": true,
|
||||||
"imageUuidOrDatabaseUri": "34daf5f4-3869-4f71-84c4-d09103701be6@6c48a",
|
"imageUuidOrDatabaseUri": "34daf5f4-3869-4f71-84c4-d09103701be6@6c48a",
|
||||||
"atlasUuid": "",
|
"atlasUuid": "",
|
||||||
"trimType": "none"
|
"trimType": "none"
|
||||||
},
|
},
|
||||||
"ver": "1.0.12",
|
"ver": "1.0.12",
|
||||||
"imported": true,
|
"imported": true,
|
||||||
"files": [
|
"files": [
|
||||||
".json"
|
".json"
|
||||||
],
|
],
|
||||||
"subMetas": {}
|
"subMetas": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"userData": {
|
"userData": {
|
||||||
"type": "sprite-frame",
|
"type": "sprite-frame",
|
||||||
"fixAlphaTransparencyArtifacts": false,
|
"fixAlphaTransparencyArtifacts": false,
|
||||||
"hasAlpha": true,
|
"hasAlpha": true,
|
||||||
"redirect": "34daf5f4-3869-4f71-84c4-d09103701be6@6c48a"
|
"redirect": "34daf5f4-3869-4f71-84c4-d09103701be6@6c48a"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.2 MiB |
@@ -45,9 +45,9 @@
|
|||||||
"offsetY": 0,
|
"offsetY": 0,
|
||||||
"trimX": 0,
|
"trimX": 0,
|
||||||
"trimY": 0,
|
"trimY": 0,
|
||||||
"width": 1600,
|
"width": 1280,
|
||||||
"height": 720,
|
"height": 720,
|
||||||
"rawWidth": 1600,
|
"rawWidth": 1280,
|
||||||
"rawHeight": 720,
|
"rawHeight": 720,
|
||||||
"borderTop": 0,
|
"borderTop": 0,
|
||||||
"borderBottom": 0,
|
"borderBottom": 0,
|
||||||
@@ -60,16 +60,16 @@
|
|||||||
"meshType": 0,
|
"meshType": 0,
|
||||||
"vertices": {
|
"vertices": {
|
||||||
"rawPosition": [
|
"rawPosition": [
|
||||||
-800,
|
-640,
|
||||||
-360,
|
-360,
|
||||||
0,
|
0,
|
||||||
800,
|
640,
|
||||||
-360,
|
-360,
|
||||||
0,
|
0,
|
||||||
-800,
|
-640,
|
||||||
360,
|
360,
|
||||||
0,
|
0,
|
||||||
800,
|
640,
|
||||||
360,
|
360,
|
||||||
0
|
0
|
||||||
],
|
],
|
||||||
@@ -84,11 +84,11 @@
|
|||||||
"uv": [
|
"uv": [
|
||||||
0,
|
0,
|
||||||
720,
|
720,
|
||||||
1600,
|
1280,
|
||||||
720,
|
720,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
1600,
|
1280,
|
||||||
0
|
0
|
||||||
],
|
],
|
||||||
"nuv": [
|
"nuv": [
|
||||||
@@ -102,12 +102,12 @@
|
|||||||
1
|
1
|
||||||
],
|
],
|
||||||
"minPos": [
|
"minPos": [
|
||||||
-800,
|
-640,
|
||||||
-360,
|
-360,
|
||||||
0
|
0
|
||||||
],
|
],
|
||||||
"maxPos": [
|
"maxPos": [
|
||||||
800,
|
640,
|
||||||
360,
|
360,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.2.0",
|
||||||
|
"importer": "directory",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "ad5e871f-b5d8-439a-852b-53d4d1d9f61b",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
+538
@@ -0,0 +1,538 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"__type__": "cc.Prefab",
|
||||||
|
"_name": "OwnerRoomItem",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_native": "",
|
||||||
|
"data": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"optimizationPolicy": 0,
|
||||||
|
"persistent": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Node",
|
||||||
|
"_name": "OwnerRoomItem",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_parent": null,
|
||||||
|
"_children": [
|
||||||
|
{
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 14
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_active": true,
|
||||||
|
"_components": [
|
||||||
|
{
|
||||||
|
"__id__": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 22
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_prefab": {
|
||||||
|
"__id__": 24
|
||||||
|
},
|
||||||
|
"_lpos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_lrot": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_lscale": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"z": 1
|
||||||
|
},
|
||||||
|
"_mobility": 0,
|
||||||
|
"_layer": 33554432,
|
||||||
|
"_euler": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_id": "5dxzP/UG5Fea3nNr557ORP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Node",
|
||||||
|
"_name": "Spirit100",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_parent": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"_children": [],
|
||||||
|
"_active": true,
|
||||||
|
"_components": [
|
||||||
|
{
|
||||||
|
"__id__": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_prefab": {
|
||||||
|
"__id__": 7
|
||||||
|
},
|
||||||
|
"_lpos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_lrot": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_lscale": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"z": 1
|
||||||
|
},
|
||||||
|
"_mobility": 0,
|
||||||
|
"_layer": 33554432,
|
||||||
|
"_euler": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_id": "9blwBxZBxC4LDYs+VIpNy1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.UITransform",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 4
|
||||||
|
},
|
||||||
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
|
"width": 264,
|
||||||
|
"height": 80
|
||||||
|
},
|
||||||
|
"_anchorPoint": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
},
|
||||||
|
"_id": "90IWdamq1CaK2+fcIY8I4G"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "l3ocOicG3kovqa6+Yow7SQ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Sprite",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 6
|
||||||
|
},
|
||||||
|
"_customMaterial": null,
|
||||||
|
"_srcBlendFactor": 2,
|
||||||
|
"_dstBlendFactor": 4,
|
||||||
|
"_color": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 255,
|
||||||
|
"g": 255,
|
||||||
|
"b": 255,
|
||||||
|
"a": 255
|
||||||
|
},
|
||||||
|
"_spriteFrame": {
|
||||||
|
"__uuid__": "5f783910-7fd0-4482-a206-8dcb57238c74@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
"_type": 0,
|
||||||
|
"_fillType": 0,
|
||||||
|
"_sizeMode": 0,
|
||||||
|
"_fillCenter": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"_fillStart": 0,
|
||||||
|
"_fillRange": 0,
|
||||||
|
"_isTrimmedMode": true,
|
||||||
|
"_useGrayscale": false,
|
||||||
|
"_atlas": null,
|
||||||
|
"_id": "e2Q9Z8vy1NVpG+xdDhdD9s"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "VMQNp5XtZ86iR1Z+hSYFOg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.PrefabInfo",
|
||||||
|
"root": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"asset": {
|
||||||
|
"__id__": 0
|
||||||
|
},
|
||||||
|
"fileId": "0lhvW5OWHIVi/ncFScuMKQ",
|
||||||
|
"instance": null,
|
||||||
|
"targetOverrides": null,
|
||||||
|
"nestedPrefabInstanceRoots": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Node",
|
||||||
|
"_name": "Spirit102",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_parent": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"_children": [],
|
||||||
|
"_active": false,
|
||||||
|
"_components": [
|
||||||
|
{
|
||||||
|
"__id__": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 11
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_prefab": {
|
||||||
|
"__id__": 13
|
||||||
|
},
|
||||||
|
"_lpos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 39,
|
||||||
|
"y": 2.5,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_lrot": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_lscale": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"z": 1
|
||||||
|
},
|
||||||
|
"_mobility": 0,
|
||||||
|
"_layer": 33554432,
|
||||||
|
"_euler": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_id": "89d19HpytGVox/SvGWJFDr"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.UITransform",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 8
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 10
|
||||||
|
},
|
||||||
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
|
"width": 14,
|
||||||
|
"height": 19
|
||||||
|
},
|
||||||
|
"_anchorPoint": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
},
|
||||||
|
"_id": "50k3QzjPhHhJYWzNgtUGBk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "1sIXmawSIfDNdF9tSIh6ig"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Sprite",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 8
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 12
|
||||||
|
},
|
||||||
|
"_customMaterial": null,
|
||||||
|
"_srcBlendFactor": 2,
|
||||||
|
"_dstBlendFactor": 4,
|
||||||
|
"_color": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 255,
|
||||||
|
"g": 255,
|
||||||
|
"b": 255,
|
||||||
|
"a": 255
|
||||||
|
},
|
||||||
|
"_spriteFrame": {
|
||||||
|
"__uuid__": "9d49bb70-608e-46d2-8672-5afb78bbd8d7@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
"_type": 0,
|
||||||
|
"_fillType": 0,
|
||||||
|
"_sizeMode": 0,
|
||||||
|
"_fillCenter": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"_fillStart": 0,
|
||||||
|
"_fillRange": 0,
|
||||||
|
"_isTrimmedMode": true,
|
||||||
|
"_useGrayscale": false,
|
||||||
|
"_atlas": null,
|
||||||
|
"_id": "32xJfAFBFPL57FrZT1Njpw"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "dYeagt3oXF7LLcLyBNmxcQ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.PrefabInfo",
|
||||||
|
"root": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"asset": {
|
||||||
|
"__id__": 0
|
||||||
|
},
|
||||||
|
"fileId": "xOj6F8iDH5ro1bLGRx3c5g",
|
||||||
|
"instance": null,
|
||||||
|
"targetOverrides": null,
|
||||||
|
"nestedPrefabInstanceRoots": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Node",
|
||||||
|
"_name": "Digits",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_parent": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"_children": [],
|
||||||
|
"_active": true,
|
||||||
|
"_components": [
|
||||||
|
{
|
||||||
|
"__id__": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 17
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_prefab": {
|
||||||
|
"__id__": 19
|
||||||
|
},
|
||||||
|
"_lpos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 32,
|
||||||
|
"y": 2.5,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_lrot": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_lscale": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"z": 1
|
||||||
|
},
|
||||||
|
"_mobility": 0,
|
||||||
|
"_layer": 33554432,
|
||||||
|
"_euler": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_id": "85eISb6stE0oj1mZHpP6zK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.UITransform",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 14
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 16
|
||||||
|
},
|
||||||
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
|
"width": 14,
|
||||||
|
"height": 19
|
||||||
|
},
|
||||||
|
"_anchorPoint": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0.5
|
||||||
|
},
|
||||||
|
"_id": "44soAEXzBJSaDlfZI1MuoB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "41Li0cKhzYiSg6SbbaoAGg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Layout",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 14
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 18
|
||||||
|
},
|
||||||
|
"_resizeMode": 1,
|
||||||
|
"_layoutType": 1,
|
||||||
|
"_cellSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
|
"width": 40,
|
||||||
|
"height": 40
|
||||||
|
},
|
||||||
|
"_startAxis": 0,
|
||||||
|
"_paddingLeft": 0,
|
||||||
|
"_paddingRight": 0,
|
||||||
|
"_paddingTop": 0,
|
||||||
|
"_paddingBottom": 0,
|
||||||
|
"_spacingX": 0,
|
||||||
|
"_spacingY": 0,
|
||||||
|
"_verticalDirection": 1,
|
||||||
|
"_horizontalDirection": 0,
|
||||||
|
"_constraint": 0,
|
||||||
|
"_constraintNum": 2,
|
||||||
|
"_affectedByScale": false,
|
||||||
|
"_isAlign": false,
|
||||||
|
"_id": "53I4gVRnlCNo+Wqps6VeDy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "k0owfG3taNqw+EIUHZ8V9g"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.PrefabInfo",
|
||||||
|
"root": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"asset": {
|
||||||
|
"__id__": 0
|
||||||
|
},
|
||||||
|
"fileId": "lC5WHe9437aSbTecw6lUiQ",
|
||||||
|
"instance": null,
|
||||||
|
"targetOverrides": null,
|
||||||
|
"nestedPrefabInstanceRoots": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.UITransform",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 21
|
||||||
|
},
|
||||||
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
|
"width": 264,
|
||||||
|
"height": 80
|
||||||
|
},
|
||||||
|
"_anchorPoint": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
},
|
||||||
|
"_id": "77ZqoCi+xJ/7Bjxxb0o4bl"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "KLcB/F2HEtr5tYEndV8qbg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "e40a5gQKgNA6a/yNIkJZlsD",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 23
|
||||||
|
},
|
||||||
|
"digitTemplate": {
|
||||||
|
"__id__": 11
|
||||||
|
},
|
||||||
|
"digitsRoot": {
|
||||||
|
"__id__": 14
|
||||||
|
},
|
||||||
|
"_id": "306fBHxpJHuLQI/kcAnGQS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "RCmOaA1Wm5nUHFrcVQJpOw"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.PrefabInfo",
|
||||||
|
"root": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"asset": {
|
||||||
|
"__id__": 0
|
||||||
|
},
|
||||||
|
"fileId": "0oOd9F3D7yWBfO1u4tL3BA",
|
||||||
|
"instance": null,
|
||||||
|
"targetOverrides": null,
|
||||||
|
"nestedPrefabInstanceRoots": null
|
||||||
|
}
|
||||||
|
]
|
||||||
+2
-2
@@ -2,12 +2,12 @@
|
|||||||
"ver": "1.1.50",
|
"ver": "1.1.50",
|
||||||
"importer": "prefab",
|
"importer": "prefab",
|
||||||
"imported": true,
|
"imported": true,
|
||||||
"uuid": "b13f487a-451e-41ce-a810-1fab0d52248f",
|
"uuid": "67117675-9f6d-4def-a67c-db0e9fb19b98",
|
||||||
"files": [
|
"files": [
|
||||||
".json"
|
".json"
|
||||||
],
|
],
|
||||||
"subMetas": {},
|
"subMetas": {},
|
||||||
"userData": {
|
"userData": {
|
||||||
"syncNodeName": "Layer9_Feedback"
|
"syncNodeName": "OwnerRoomItem"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+170
@@ -0,0 +1,170 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"__type__": "cc.Prefab",
|
||||||
|
"_name": "PlayerInfoTextItem",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_native": "",
|
||||||
|
"data": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"optimizationPolicy": 0,
|
||||||
|
"persistent": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Node",
|
||||||
|
"_name": "PlayerInfoTextItem",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_parent": null,
|
||||||
|
"_children": [],
|
||||||
|
"_active": true,
|
||||||
|
"_components": [
|
||||||
|
{
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_prefab": {
|
||||||
|
"__id__": 6
|
||||||
|
},
|
||||||
|
"_lpos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 10,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_lrot": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_lscale": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"z": 1
|
||||||
|
},
|
||||||
|
"_mobility": 0,
|
||||||
|
"_layer": 33554432,
|
||||||
|
"_euler": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.UITransform",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 3
|
||||||
|
},
|
||||||
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
|
"width": 371,
|
||||||
|
"height": 22.680000000000003
|
||||||
|
},
|
||||||
|
"_anchorPoint": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0,
|
||||||
|
"y": 1
|
||||||
|
},
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "JYB5ndvc0hWZFdTsDPujQg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Label",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 5
|
||||||
|
},
|
||||||
|
"_customMaterial": null,
|
||||||
|
"_srcBlendFactor": 2,
|
||||||
|
"_dstBlendFactor": 4,
|
||||||
|
"_color": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 0,
|
||||||
|
"g": 0,
|
||||||
|
"b": 0,
|
||||||
|
"a": 255
|
||||||
|
},
|
||||||
|
"_string": "资料文字",
|
||||||
|
"_horizontalAlign": 0,
|
||||||
|
"_verticalAlign": 0,
|
||||||
|
"_actualFontSize": 18.975,
|
||||||
|
"_fontSize": 18,
|
||||||
|
"_fontFamily": "Arial",
|
||||||
|
"_lineHeight": 18,
|
||||||
|
"_overflow": 3,
|
||||||
|
"_enableWrapText": true,
|
||||||
|
"_font": null,
|
||||||
|
"_isSystemFontUsed": true,
|
||||||
|
"_spacingX": 0,
|
||||||
|
"_isItalic": false,
|
||||||
|
"_isBold": false,
|
||||||
|
"_isUnderline": false,
|
||||||
|
"_underlineHeight": 2,
|
||||||
|
"_cacheMode": 0,
|
||||||
|
"_enableOutline": false,
|
||||||
|
"_outlineColor": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 0,
|
||||||
|
"g": 0,
|
||||||
|
"b": 0,
|
||||||
|
"a": 255
|
||||||
|
},
|
||||||
|
"_outlineWidth": 2,
|
||||||
|
"_enableShadow": false,
|
||||||
|
"_shadowColor": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 0,
|
||||||
|
"g": 0,
|
||||||
|
"b": 0,
|
||||||
|
"a": 255
|
||||||
|
},
|
||||||
|
"_shadowOffset": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 2,
|
||||||
|
"y": 2
|
||||||
|
},
|
||||||
|
"_shadowBlur": 2,
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "wX+Ww4pBz/TnLcrPEXSuAA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.PrefabInfo",
|
||||||
|
"root": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"asset": {
|
||||||
|
"__id__": 0
|
||||||
|
},
|
||||||
|
"fileId": "Pn6tcB1uaJ1EOUf2nLgpzw",
|
||||||
|
"instance": null,
|
||||||
|
"targetOverrides": null
|
||||||
|
}
|
||||||
|
]
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.50",
|
||||||
|
"importer": "prefab",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "9e6dbfc6-26c5-4eac-9cc8-8060a677d5ee",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {
|
||||||
|
"syncNodeName": "PlayerInfoTextItem"
|
||||||
|
}
|
||||||
|
}
|
||||||
+1921
-1921
File diff suppressed because it is too large
Load Diff
+13
-13
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"ver": "1.1.50",
|
"ver": "1.1.50",
|
||||||
"importer": "prefab",
|
"importer": "prefab",
|
||||||
"imported": true,
|
"imported": true,
|
||||||
"uuid": "b9f6f400-7b1b-4e51-a841-cc84dcddca82",
|
"uuid": "b9f6f400-7b1b-4e51-a841-cc84dcddca82",
|
||||||
"files": [
|
"files": [
|
||||||
".json"
|
".json"
|
||||||
],
|
],
|
||||||
"subMetas": {},
|
"subMetas": {},
|
||||||
"userData": {
|
"userData": {
|
||||||
"syncNodeName": "TaskItem"
|
"syncNodeName": "TaskItem"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1216
File diff suppressed because it is too large
Load Diff
+13
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.50",
|
||||||
|
"importer": "prefab",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "5925d1d2-f4b2-44c1-a37a-fb2bcdcf664e",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {
|
||||||
|
"syncNodeName": "VipRankItem"
|
||||||
|
}
|
||||||
|
}
|
||||||
+1327
-1327
File diff suppressed because it is too large
Load Diff
+13
-13
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"ver": "1.1.50",
|
"ver": "1.1.50",
|
||||||
"importer": "prefab",
|
"importer": "prefab",
|
||||||
"imported": true,
|
"imported": true,
|
||||||
"uuid": "fed2e71c-22ac-4e48-a42a-68e48a1929a7",
|
"uuid": "fed2e71c-22ac-4e48-a42a-68e48a1929a7",
|
||||||
"files": [
|
"files": [
|
||||||
".json"
|
".json"
|
||||||
],
|
],
|
||||||
"subMetas": {},
|
"subMetas": {},
|
||||||
"userData": {
|
"userData": {
|
||||||
"syncNodeName": "VipRoomItem1"
|
"syncNodeName": "VipRoomItem1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+566
-566
File diff suppressed because it is too large
Load Diff
+13
-13
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"ver": "1.1.50",
|
"ver": "1.1.50",
|
||||||
"importer": "prefab",
|
"importer": "prefab",
|
||||||
"imported": true,
|
"imported": true,
|
||||||
"uuid": "8cb43df4-d8fa-4b3c-ab01-497b23c68435",
|
"uuid": "8cb43df4-d8fa-4b3c-ab01-497b23c68435",
|
||||||
"files": [
|
"files": [
|
||||||
".json"
|
".json"
|
||||||
],
|
],
|
||||||
"subMetas": {},
|
"subMetas": {},
|
||||||
"userData": {
|
"userData": {
|
||||||
"syncNodeName": "VipRoomItem2"
|
"syncNodeName": "VipRoomItem2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1888
-674
File diff suppressed because it is too large
Load Diff
+692
-125
File diff suppressed because it is too large
Load Diff
+473
-1584
File diff suppressed because it is too large
Load Diff
+1029
-502
File diff suppressed because it is too large
Load Diff
+863
-1604
File diff suppressed because it is too large
Load Diff
+2084
-1201
File diff suppressed because it is too large
Load Diff
+201
-202
File diff suppressed because it is too large
Load Diff
+376
-187
File diff suppressed because it is too large
Load Diff
+41
-11
@@ -25,14 +25,14 @@
|
|||||||
"_active": true,
|
"_active": true,
|
||||||
"_components": [
|
"_components": [
|
||||||
{
|
{
|
||||||
"__id__": 24
|
"__id__": 25
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 26
|
"__id__": 27
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 28
|
"__id__": 29
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -82,14 +82,14 @@
|
|||||||
"_active": true,
|
"_active": true,
|
||||||
"_components": [
|
"_components": [
|
||||||
{
|
{
|
||||||
"__id__": 19
|
"__id__": 20
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 21
|
"__id__": 22
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 23
|
"__id__": 24
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -186,6 +186,7 @@
|
|||||||
"__id__": 5
|
"__id__": 5
|
||||||
},
|
},
|
||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
"width": 1600,
|
"width": 1600,
|
||||||
"height": 720
|
"height": 720
|
||||||
},
|
},
|
||||||
@@ -313,10 +314,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 16
|
"__id__": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 18
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 18
|
"__id__": 19
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -360,6 +364,7 @@
|
|||||||
"__id__": 13
|
"__id__": 13
|
||||||
},
|
},
|
||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
"width": 150,
|
"width": 150,
|
||||||
"height": 150
|
"height": 150
|
||||||
},
|
},
|
||||||
@@ -455,6 +460,29 @@
|
|||||||
"__type__": "cc.CompPrefabInfo",
|
"__type__": "cc.CompPrefabInfo",
|
||||||
"fileId": "3wlBO4ciKrxeTYsQdlQvBg"
|
"fileId": "3wlBO4ciKrxeTYsQdlQvBg"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Animation",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 11
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": null,
|
||||||
|
"playOnLoad": false,
|
||||||
|
"_clips": [
|
||||||
|
{
|
||||||
|
"__uuid__": "987771c5-7f29-487a-b61a-476981cc9216",
|
||||||
|
"__expectedType__": "cc.AnimationClip"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_defaultClip": {
|
||||||
|
"__uuid__": "987771c5-7f29-487a-b61a-476981cc9216",
|
||||||
|
"__expectedType__": "cc.AnimationClip"
|
||||||
|
},
|
||||||
|
"_id": "1b3VMKLpRFPKzD9o608yTZ"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"__type__": "cc.PrefabInfo",
|
"__type__": "cc.PrefabInfo",
|
||||||
"root": {
|
"root": {
|
||||||
@@ -478,9 +506,10 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 20
|
"__id__": 21
|
||||||
},
|
},
|
||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
"width": 1600,
|
"width": 1600,
|
||||||
"height": 720
|
"height": 720
|
||||||
},
|
},
|
||||||
@@ -505,7 +534,7 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 22
|
"__id__": 23
|
||||||
},
|
},
|
||||||
"_alignFlags": 45,
|
"_alignFlags": 45,
|
||||||
"_target": null,
|
"_target": null,
|
||||||
@@ -554,9 +583,10 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 25
|
"__id__": 26
|
||||||
},
|
},
|
||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
"width": 1600,
|
"width": 1600,
|
||||||
"height": 720
|
"height": 720
|
||||||
},
|
},
|
||||||
@@ -581,7 +611,7 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 27
|
"__id__": 28
|
||||||
},
|
},
|
||||||
"_alignFlags": 45,
|
"_alignFlags": 45,
|
||||||
"_target": null,
|
"_target": null,
|
||||||
|
|||||||
+445
-129
File diff suppressed because it is too large
Load Diff
-6762
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"__type__": "cc.Prefab",
|
"__type__": "cc.Prefab",
|
||||||
"_name": "Login_Layer",
|
"_name": "Login",
|
||||||
"_objFlags": 0,
|
"_objFlags": 0,
|
||||||
"__editorExtras__": {},
|
"__editorExtras__": {},
|
||||||
"_native": "",
|
"_native": "",
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__type__": "cc.Node",
|
"__type__": "cc.Node",
|
||||||
"_name": "Login_Layer",
|
"_name": "Login",
|
||||||
"_objFlags": 0,
|
"_objFlags": 0,
|
||||||
"__editorExtras__": {},
|
"__editorExtras__": {},
|
||||||
"_parent": null,
|
"_parent": null,
|
||||||
+14
-14
@@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"ver": "1.1.50",
|
"ver": "1.1.50",
|
||||||
"importer": "prefab",
|
"importer": "prefab",
|
||||||
"imported": true,
|
"imported": true,
|
||||||
"uuid": "b209cefb-b381-4c7c-b765-94a5ab75a89c",
|
"uuid": "b209cefb-b381-4c7c-b765-94a5ab75a89c",
|
||||||
"files": [
|
"files": [
|
||||||
".json"
|
".json"
|
||||||
],
|
],
|
||||||
"subMetas": {},
|
"subMetas": {},
|
||||||
"userData": {
|
"userData": {
|
||||||
"syncNodeName": "Login_Layer",
|
"syncNodeName": "Login",
|
||||||
"hasIcon": false
|
"hasIcon": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3649
-2492
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -8,6 +8,6 @@
|
|||||||
],
|
],
|
||||||
"subMetas": {},
|
"subMetas": {},
|
||||||
"userData": {
|
"userData": {
|
||||||
"syncNodeName": "Layer4_MainMenu"
|
"syncNodeName": "MainMenu"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3793
File diff suppressed because it is too large
Load Diff
+13
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.50",
|
||||||
|
"importer": "prefab",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "e7a05100-7de4-4935-896e-a59061377b87",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {
|
||||||
|
"syncNodeName": "NoticeBoard"
|
||||||
|
}
|
||||||
|
}
|
||||||
+1002
File diff suppressed because it is too large
Load Diff
+13
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.50",
|
||||||
|
"importer": "prefab",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "260ea2dc-696c-4a65-a2fd-109dd794bb45",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {
|
||||||
|
"syncNodeName": "OwnedRoomList"
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
+13
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.50",
|
||||||
|
"importer": "prefab",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "95ee9b79-8c30-4f54-a2cf-19d02083cd28",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {
|
||||||
|
"syncNodeName": "QrCode"
|
||||||
|
}
|
||||||
|
}
|
||||||
+1540
File diff suppressed because it is too large
Load Diff
+13
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.50",
|
||||||
|
"importer": "prefab",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "ada34847-93c7-48f2-bb0a-5c1db4b112f6",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {
|
||||||
|
"syncNodeName": "RedeemCode"
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-241
@@ -1,7 +1,7 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"__type__": "cc.Prefab",
|
"__type__": "cc.Prefab",
|
||||||
"_name": "Layer1_Logo",
|
"_name": "ResourceLoading",
|
||||||
"_objFlags": 0,
|
"_objFlags": 0,
|
||||||
"__editorExtras__": {},
|
"__editorExtras__": {},
|
||||||
"_native": "",
|
"_native": "",
|
||||||
@@ -13,22 +13,19 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__type__": "cc.Node",
|
"__type__": "cc.Node",
|
||||||
"_name": "Layer1_Logo",
|
"_name": "ResourceLoading",
|
||||||
"_objFlags": 0,
|
"_objFlags": 0,
|
||||||
"__editorExtras__": {},
|
"__editorExtras__": {},
|
||||||
"_parent": null,
|
"_parent": null,
|
||||||
"_children": [
|
"_children": [
|
||||||
{
|
{
|
||||||
"__id__": 2
|
"__id__": 2
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 50
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_active": true,
|
"_active": true,
|
||||||
"_components": [],
|
"_components": [],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 60
|
"__id__": 50
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -1130,241 +1127,6 @@
|
|||||||
"targetOverrides": null,
|
"targetOverrides": null,
|
||||||
"nestedPrefabInstanceRoots": null
|
"nestedPrefabInstanceRoots": null
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"__type__": "cc.Node",
|
|
||||||
"_name": "group-32",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"_parent": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_children": [
|
|
||||||
{
|
|
||||||
"__id__": 51
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"_active": true,
|
|
||||||
"_components": [],
|
|
||||||
"_prefab": {
|
|
||||||
"__id__": 59
|
|
||||||
},
|
|
||||||
"_lpos": {
|
|
||||||
"__type__": "cc.Vec3",
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
"z": 0
|
|
||||||
},
|
|
||||||
"_lrot": {
|
|
||||||
"__type__": "cc.Quat",
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
"z": 0,
|
|
||||||
"w": 1
|
|
||||||
},
|
|
||||||
"_lscale": {
|
|
||||||
"__type__": "cc.Vec3",
|
|
||||||
"x": 1,
|
|
||||||
"y": 1,
|
|
||||||
"z": 1
|
|
||||||
},
|
|
||||||
"_mobility": 0,
|
|
||||||
"_layer": 33554432,
|
|
||||||
"_euler": {
|
|
||||||
"__type__": "cc.Vec3",
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
"z": 0
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.Node",
|
|
||||||
"_name": "Spirit221",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"_parent": {
|
|
||||||
"__id__": 50
|
|
||||||
},
|
|
||||||
"_children": [],
|
|
||||||
"_active": true,
|
|
||||||
"_components": [
|
|
||||||
{
|
|
||||||
"__id__": 52
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 54
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 56
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"_prefab": {
|
|
||||||
"__id__": 58
|
|
||||||
},
|
|
||||||
"_lpos": {
|
|
||||||
"__type__": "cc.Vec3",
|
|
||||||
"x": -1612.5,
|
|
||||||
"y": 1099.952,
|
|
||||||
"z": 0
|
|
||||||
},
|
|
||||||
"_lrot": {
|
|
||||||
"__type__": "cc.Quat",
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
"z": 0,
|
|
||||||
"w": 1
|
|
||||||
},
|
|
||||||
"_lscale": {
|
|
||||||
"__type__": "cc.Vec3",
|
|
||||||
"x": 1,
|
|
||||||
"y": 1,
|
|
||||||
"z": 1
|
|
||||||
},
|
|
||||||
"_mobility": 0,
|
|
||||||
"_layer": 33554432,
|
|
||||||
"_euler": {
|
|
||||||
"__type__": "cc.Vec3",
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
"z": 0
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.UITransform",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 51
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 53
|
|
||||||
},
|
|
||||||
"_contentSize": {
|
|
||||||
"__type__": "cc.Size",
|
|
||||||
"width": 63,
|
|
||||||
"height": 63
|
|
||||||
},
|
|
||||||
"_anchorPoint": {
|
|
||||||
"__type__": "cc.Vec2",
|
|
||||||
"x": 0.5,
|
|
||||||
"y": 0.5
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "Y8tq/cDuhVvttPn+x9+bww"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.Sprite",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 51
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 55
|
|
||||||
},
|
|
||||||
"_customMaterial": null,
|
|
||||||
"_srcBlendFactor": 2,
|
|
||||||
"_dstBlendFactor": 4,
|
|
||||||
"_color": {
|
|
||||||
"__type__": "cc.Color",
|
|
||||||
"r": 255,
|
|
||||||
"g": 255,
|
|
||||||
"b": 255,
|
|
||||||
"a": 255
|
|
||||||
},
|
|
||||||
"_spriteFrame": {
|
|
||||||
"__uuid__": "974bf7e8-69fd-49ff-95fa-ed7ddb8d6a14@f9941",
|
|
||||||
"__expectedType__": "cc.SpriteFrame"
|
|
||||||
},
|
|
||||||
"_type": 0,
|
|
||||||
"_fillType": 0,
|
|
||||||
"_sizeMode": 0,
|
|
||||||
"_fillCenter": {
|
|
||||||
"__type__": "cc.Vec2",
|
|
||||||
"x": 0,
|
|
||||||
"y": 0
|
|
||||||
},
|
|
||||||
"_fillStart": 0,
|
|
||||||
"_fillRange": 0,
|
|
||||||
"_isTrimmedMode": true,
|
|
||||||
"_useGrayscale": false,
|
|
||||||
"_atlas": null,
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "t+vRkZQc/kDtmwdvT1STKA"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.Widget",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 51
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 57
|
|
||||||
},
|
|
||||||
"_alignFlags": 16,
|
|
||||||
"_target": null,
|
|
||||||
"_left": 0,
|
|
||||||
"_right": 0,
|
|
||||||
"_top": 0,
|
|
||||||
"_bottom": 0,
|
|
||||||
"_horizontalCenter": -1612.5,
|
|
||||||
"_verticalCenter": 0,
|
|
||||||
"_isAbsLeft": true,
|
|
||||||
"_isAbsRight": true,
|
|
||||||
"_isAbsTop": true,
|
|
||||||
"_isAbsBottom": true,
|
|
||||||
"_isAbsHorizontalCenter": true,
|
|
||||||
"_isAbsVerticalCenter": true,
|
|
||||||
"_originalWidth": 0,
|
|
||||||
"_originalHeight": 0,
|
|
||||||
"_alignMode": 2,
|
|
||||||
"_lockFlags": 0,
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "K2+PudUKVOrBYR09Lp3sAA"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.PrefabInfo",
|
|
||||||
"root": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"asset": {
|
|
||||||
"__id__": 0
|
|
||||||
},
|
|
||||||
"fileId": "J1IKV6k3EB5NkpQVaZVxiA",
|
|
||||||
"instance": null,
|
|
||||||
"targetOverrides": null,
|
|
||||||
"nestedPrefabInstanceRoots": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.PrefabInfo",
|
|
||||||
"root": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"asset": {
|
|
||||||
"__id__": 0
|
|
||||||
},
|
|
||||||
"fileId": "JccA+NmPM7lVqq5kAuMdjQ",
|
|
||||||
"instance": null,
|
|
||||||
"targetOverrides": null,
|
|
||||||
"nestedPrefabInstanceRoots": null
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"__type__": "cc.PrefabInfo",
|
"__type__": "cc.PrefabInfo",
|
||||||
"root": {
|
"root": {
|
||||||
+1
-1
@@ -8,6 +8,6 @@
|
|||||||
],
|
],
|
||||||
"subMetas": {},
|
"subMetas": {},
|
||||||
"userData": {
|
"userData": {
|
||||||
"syncNodeName": "Layer1_Logo"
|
"syncNodeName": "ResourceLoading"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.2.0",
|
||||||
|
"importer": "directory",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "bfaf200b-f8f5-4fcb-b9e3-7e4e831177ce",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.2.0",
|
||||||
|
"importer": "directory",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "c593ddf6-0173-48e8-96cb-dab1273e43d8",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import {_decorator,Button,Label,Toggle,ToggleContainer} from 'cc';
|
||||||
|
import {CreateRoomPage} from '../../scripts/platform-login/CreateRoomPage.ts';
|
||||||
|
import type {CreateRoomPageContext} from '../../framework/sdk/contracts/create-room-page.ts';
|
||||||
|
import {TemplateRoomRules} from './template-room-rules.ts';
|
||||||
|
const {ccclass,property}=_decorator;
|
||||||
|
@ccclass('TemplateCreateRoomView')
|
||||||
|
export class TemplateCreateRoomView extends CreateRoomPage {
|
||||||
|
@property([ToggleContainer]) groups:ToggleContainer[]=[];
|
||||||
|
@property([Toggle]) options:Toggle[]=[];
|
||||||
|
@property([Label]) optionLabels:Label[]=[];
|
||||||
|
@property(Button) submitButton:Button|null=null;
|
||||||
|
@property([Button]) cancelButtons:Button[]=[];
|
||||||
|
private readonly rules=new TemplateRoomRules();
|
||||||
|
private context:CreateRoomPageContext|null=null;
|
||||||
|
private busy=false;
|
||||||
|
private releases:Array<()=>void>=[];
|
||||||
|
open(context:CreateRoomPageContext):void{
|
||||||
|
this.close();
|
||||||
|
if(this.groups.length!==2||this.options.length!==4||this.optionLabels.length!==4||!this.submitButton||this.cancelButtons.length!==2)throw Error('TemplateCreateRoomView requires serialized controls');
|
||||||
|
this.rules.open(context.previousRoomtype);this.context=context;
|
||||||
|
// A saved source index may have no matching option; initialize that state explicitly.
|
||||||
|
this.groups.forEach(group=>group.allowSwitchOff=true);
|
||||||
|
this.optionLabels.forEach((label,i)=>label.string=this.rules.labels[i]);
|
||||||
|
this.options.forEach((toggle,i)=>{
|
||||||
|
toggle.isChecked=this.rules.selection[Math.floor(i/2)]===i%2;
|
||||||
|
const changed=()=>{if(!this.busy&&toggle.isChecked)this.rules.select(Math.floor(i/2) as 0|1,i%2);};
|
||||||
|
toggle.node.on(Toggle.EventType.TOGGLE,changed);this.releases.push(()=>toggle.node.off(Toggle.EventType.TOGGLE,changed));
|
||||||
|
});
|
||||||
|
this.groups.forEach(group=>group.allowSwitchOff=false);
|
||||||
|
const bind=(button:Button,action:()=>void)=>{const fn=()=>{if(!this.busy&&this.context)try{action();}catch(error){context.reportError(error instanceof Error?error.message:String(error));}};
|
||||||
|
button.node.on(Button.EventType.CLICK,fn);this.releases.push(()=>button.node.off(Button.EventType.CLICK,fn));};
|
||||||
|
bind(this.submitButton,()=>context.submit(this.rules.build()));
|
||||||
|
this.cancelButtons.forEach(button=>{const cancel=()=>context.cancel();button.node.on(Button.EventType.CLICK,cancel);this.releases.push(()=>button.node.off(Button.EventType.CLICK,cancel));});this.setBusy(false);
|
||||||
|
}
|
||||||
|
setBusy(busy:boolean):void{this.busy=busy;this.options.forEach(toggle=>toggle.interactable=!busy);if(this.submitButton)this.submitButton.interactable=!busy;}
|
||||||
|
close():void{for(const release of this.releases.splice(0))release();this.context=null;}
|
||||||
|
onDestroy():void{this.close();}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "ebbef7d9-7bda-4c61-9b0a-1ee56e7817fb",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.50",
|
||||||
|
"importer": "prefab",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "f4582832-23e2-4981-be26-08dfe832aa96",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {
|
||||||
|
"syncNodeName": "TemplateRoom"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import {_decorator,Button,Component,Label} from 'cc';
|
||||||
|
import type {PlatformGameSnapshot} from '../../framework/sdk/contracts/index.ts';
|
||||||
|
import type {TemplateRoomActions,TemplateRoomPresentation} from './template-game.ts';
|
||||||
|
const {ccclass,property}=_decorator;
|
||||||
|
/** Template lobby-to-room handoff surface; concrete subgames replace this presentation. */
|
||||||
|
@ccclass('TemplateRoomView')
|
||||||
|
export class TemplateRoomView extends Component implements TemplateRoomPresentation {
|
||||||
|
@property(Label) roomLabel:Label|null=null;
|
||||||
|
@property(Label) playersLabel:Label|null=null;
|
||||||
|
@property(Button) prepareButton:Button|null=null;
|
||||||
|
@property(Button) exitButton:Button|null=null;
|
||||||
|
private actions:TemplateRoomActions|null=null;
|
||||||
|
private bound=false;
|
||||||
|
private pending:'prepare'|'exit'|null=null;
|
||||||
|
private timer:ReturnType<typeof setTimeout>|null=null;
|
||||||
|
private snapshot:PlatformGameSnapshot|null=null;
|
||||||
|
private readonly prepare=()=>this.invoke('prepare');
|
||||||
|
private readonly exit=()=>this.invoke('exit');
|
||||||
|
render(snapshot:PlatformGameSnapshot,actions:TemplateRoomActions):void{
|
||||||
|
if(!this.roomLabel||!this.playersLabel||!this.prepareButton||!this.exitButton)throw Error('TemplateRoomView missing controls');
|
||||||
|
this.actions=actions;this.snapshot=snapshot;
|
||||||
|
if(!this.bound){this.prepareButton.node.on(Button.EventType.CLICK,this.prepare);this.exitButton.node.on(Button.EventType.CLICK,this.exit);this.bound=true;}
|
||||||
|
this.roomLabel.string=`房间号:${snapshot.room.roomcode}`;
|
||||||
|
this.playersLabel.string=snapshot.seats.map(p=>`${p.seat+1} ${p.nickname}${p.playerId===snapshot.self.playerId?'(我)':''} ${!p.online?'离线':p.ready?'已准备':'未准备'}`).join('\n');
|
||||||
|
const self=snapshot.seats.find(p=>p.playerId===snapshot.self.playerId);
|
||||||
|
if(self?.ready||snapshot.connection.phase!=='logged-in')this.clearPending();
|
||||||
|
this.prepareButton.interactable=this.pending===null&&snapshot.connection.phase==='logged-in'&&snapshot.room.stage===0&&!self?.ready;
|
||||||
|
this.exitButton.interactable=this.pending===null&&snapshot.connection.phase==='logged-in';
|
||||||
|
}
|
||||||
|
private invoke(kind:'prepare'|'exit'):void{
|
||||||
|
if(!this.actions||this.pending)return;
|
||||||
|
try{this.actions[kind]();this.pending=kind;this.prepareButton!.interactable=false;this.exitButton!.interactable=false;
|
||||||
|
this.timer=setTimeout(()=>{this.clearPending();if(this.snapshot&&this.actions)this.render(this.snapshot,this.actions);this.node.emit('room-action-timeout');},15000);
|
||||||
|
}catch(error){this.node.emit('room-action-error',error);}
|
||||||
|
}
|
||||||
|
private clearPending():void{if(this.timer!==null)clearTimeout(this.timer);this.timer=null;this.pending=null;}
|
||||||
|
close():void{this.clearPending();if(this.bound){this.prepareButton?.node.off(Button.EventType.CLICK,this.prepare);this.exitButton?.node.off(Button.EventType.CLICK,this.exit);}this.bound=false;this.actions=null;}
|
||||||
|
onDestroy():void{this.close();}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "e0dc1f88-a719-4b19-9827-2cf9e4452be9",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import type {GameEntry,GameHost,GameModule,PlatformGameSnapshot} from '../../framework/sdk/contracts/index.ts';
|
||||||
|
import {TemplateRoomRules} from './template-room-rules.ts';
|
||||||
|
export interface TemplateRoomActions {prepare():void;exit():void;}
|
||||||
|
export interface TemplateRoomPresentation {render(snapshot:PlatformGameSnapshot,actions:TemplateRoomActions):void;close():void;}
|
||||||
|
/** Template hooks are intentionally empty, matching 02_SubGame_Input.js. No gameplay is inferred. */
|
||||||
|
export function createTemplateGame(gameId:string|number){
|
||||||
|
let active:TemplateModule|null=null;
|
||||||
|
class TemplateModule implements GameModule {
|
||||||
|
private host:GameHost|null=null;
|
||||||
|
private unsubscribe:(()=>void)|null=null;
|
||||||
|
private view:TemplateRoomPresentation|null=null;
|
||||||
|
restored:unknown=null;
|
||||||
|
attach(host:GameHost):void{
|
||||||
|
if(this.host||active)throw Error('Template module already attached');
|
||||||
|
this.host=host;active=this;this.unsubscribe=host.subscribe(()=>this.render());
|
||||||
|
}
|
||||||
|
mount(view:TemplateRoomPresentation):void{this.view?.close();this.view=view;this.render();}
|
||||||
|
private requireHost():GameHost{if(!this.host)throw Error('Template room session disposed');return this.host;}
|
||||||
|
private render():void{this.view?.render(this.requireHost().getSnapshot(),{prepare:()=>this.requireHost().execute({type:'room.prepare'}),exit:()=>this.requireHost().execute({type:'room.exit'})});}
|
||||||
|
handlePlatformEvent():void{this.render();}
|
||||||
|
handleGameMessage():void{/* Original template _ReceiveData hook is empty. */}
|
||||||
|
restore(deskinfo:unknown):void{this.requireHost();this.restored=deskinfo;}
|
||||||
|
dispose():void{this.unsubscribe?.();this.unsubscribe=null;this.view?.close();this.view=null;this.host=null;this.restored=null;if(active===this)active=null;}
|
||||||
|
}
|
||||||
|
const rules=new TemplateRoomRules();
|
||||||
|
const entry:GameEntry={key:'Game_Surface_3',gameId,route:'Game_Surface_3',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 restoredSnapshot():unknown{return active?.restored;}};
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "c6a24a57-c9c3-4eda-9f82-73f1990703fb",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/** Source: 01_SubGame_modify.js. Options are presentation state, not a rule-array encoder. */
|
||||||
|
export class TemplateRoomRules {
|
||||||
|
readonly labels=['10局(房卡X1)','20局(房卡X2)','房主扣卡','每人扣卡'] as const;
|
||||||
|
private selected:[number,number]=[0,0];
|
||||||
|
get selection():readonly number[]{return [...this.selected];}
|
||||||
|
open(previous:unknown=null):void{
|
||||||
|
if(previous===null){this.selected=[0,0];return;}
|
||||||
|
if(!Array.isArray(previous)||!Number.isInteger(previous[0])||!Number.isInteger(previous[1]))throw Error('Invalid template saved roomtype');
|
||||||
|
this.selected=[previous[0]-1,previous[1]-1];
|
||||||
|
}
|
||||||
|
select(group:0|1,index:number):void{
|
||||||
|
if(!Number.isInteger(index)||index<0||index>1)throw Error('Invalid template option');
|
||||||
|
this.selected[group]=index;
|
||||||
|
}
|
||||||
|
build():unknown[]{return [1,4,1,2,2,[1,1,[1,2000,10],null,null,1],[1,0,5]];}
|
||||||
|
seatCount(roomtype:readonly unknown[]):number{
|
||||||
|
if(!Array.isArray(roomtype))throw Error('Template roomtype must be an array');
|
||||||
|
// Desk.Create uses Game_Config.Max.PlayerCnt because the template defines no getMaxPlayerCount hook.
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "0cad77f9-c126-4532-9ffb-70e650008a34",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
+1499
-1499
File diff suppressed because it is too large
Load Diff
@@ -52,7 +52,7 @@
|
|||||||
},
|
},
|
||||||
"autoReleaseAssets": false,
|
"autoReleaseAssets": false,
|
||||||
"_globals": {
|
"_globals": {
|
||||||
"__id__": 5
|
"__id__": 6
|
||||||
},
|
},
|
||||||
"_id": "1c10c2ee-adc2-496f-9005-d3e0e6ebbb56"
|
"_id": "1c10c2ee-adc2-496f-9005-d3e0e6ebbb56"
|
||||||
},
|
},
|
||||||
@@ -72,6 +72,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 4
|
"__id__": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 5
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": null,
|
"_prefab": null,
|
||||||
@@ -182,8 +185,24 @@
|
|||||||
"__uuid__": "e1b53f04-3e2f-4127-9a3f-f629bed6905e",
|
"__uuid__": "e1b53f04-3e2f-4127-9a3f-f629bed6905e",
|
||||||
"__expectedType__": "cc.Prefab"
|
"__expectedType__": "cc.Prefab"
|
||||||
},
|
},
|
||||||
"notices": {
|
"redeemCode": {
|
||||||
"__uuid__": "b13f487a-451e-41ce-a810-1fab0d52248f",
|
"__uuid__": "ada34847-93c7-48f2-bb0a-5c1db4b112f6",
|
||||||
|
"__expectedType__": "cc.Prefab"
|
||||||
|
},
|
||||||
|
"qrCode": {
|
||||||
|
"__uuid__": "95ee9b79-8c30-4f54-a2cf-19d02083cd28",
|
||||||
|
"__expectedType__": "cc.Prefab"
|
||||||
|
},
|
||||||
|
"noticeBoard": {
|
||||||
|
"__uuid__": "e7a05100-7de4-4935-896e-a59061377b87",
|
||||||
|
"__expectedType__": "cc.Prefab"
|
||||||
|
},
|
||||||
|
"ownedRoomList": {
|
||||||
|
"__uuid__": "260ea2dc-696c-4a65-a2fd-109dd794bb45",
|
||||||
|
"__expectedType__": "cc.Prefab"
|
||||||
|
},
|
||||||
|
"invite": {
|
||||||
|
"__uuid__": "a3db6daa-a98e-4a73-b0f7-cd122bdb11c5",
|
||||||
"__expectedType__": "cc.Prefab"
|
"__expectedType__": "cc.Prefab"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
@@ -206,6 +225,100 @@
|
|||||||
"__uuid__": "f4af0cc2-2514-4bd5-a0de-7a3dbb32438b",
|
"__uuid__": "f4af0cc2-2514-4bd5-a0de-7a3dbb32438b",
|
||||||
"__expectedType__": "cc.Prefab"
|
"__expectedType__": "cc.Prefab"
|
||||||
},
|
},
|
||||||
|
"rank": {
|
||||||
|
"__uuid__": "3eafe9c2-475b-4c81-b71a-37ffa2717c71",
|
||||||
|
"__expectedType__": "cc.Prefab"
|
||||||
|
},
|
||||||
|
"rankItem": {
|
||||||
|
"__uuid__": "5925d1d2-f4b2-44c1-a37a-fb2bcdcf664e",
|
||||||
|
"__expectedType__": "cc.Prefab"
|
||||||
|
},
|
||||||
|
"rankMedals": [
|
||||||
|
{
|
||||||
|
"__uuid__": "fb86ab44-8140-4d8e-82f1-84ae4b8f3216@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "10653735-f46a-4e0f-906f-dacb093dad23@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "87749e95-72aa-435e-93bb-1f41553922f0@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rankTitle": {
|
||||||
|
"__uuid__": "ecb18612-2066-42d2-8fa0-c0161124cf1e@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
"vipRankTitle": {
|
||||||
|
"__uuid__": "97b4bd7f-8716-497f-a375-d9c841cf34e9@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
"ownedRoomDigits": [
|
||||||
|
{
|
||||||
|
"__uuid__": "9d49bb70-608e-46d2-8672-5afb78bbd8d7@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "e71157d8-1261-4917-bac1-1b48ab5b6ab9@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "bade804f-d16a-46b6-8746-600ff6b93cce@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "dae7297a-cb1a-49d7-98a9-a3008a8afc7a@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "f1365825-aa47-4efe-936c-81284b0d5e1f@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "b4d68ae8-0799-47c1-8b30-26b2daed2d59@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "be16e577-8add-4c32-a56b-948a52e0180b@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "2fd355d7-493d-474c-b753-676c7ee9e50d@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "3c1709f3-a24f-4a9e-814c-04bc1441da55@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "4dd36de5-fadb-4852-8334-e9b5834a3957@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rankSymbols": [
|
||||||
|
{
|
||||||
|
"__uuid__": "6b9ad554-bf4a-4493-82dd-92b4328cb8a4@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "2f1e97ec-1bad-4731-9563-d90ae5b93111@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "a1016858-72af-4a2c-8d8e-7098eb82a310@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "a6986c72-2bef-4f50-b215-86d4de943b68@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__uuid__": "df99cc0a-0b2a-4fdd-b271-7932b2bea237@f9941",
|
||||||
|
"__expectedType__": "cc.SpriteFrame"
|
||||||
|
}
|
||||||
|
],
|
||||||
"digits": [
|
"digits": [
|
||||||
{
|
{
|
||||||
"__uuid__": "fc6e9a8e-0a6d-4555-bc3e-88fd427b9f3a@f9941",
|
"__uuid__": "fc6e9a8e-0a6d-4555-bc3e-88fd427b9f3a@f9941",
|
||||||
@@ -272,32 +385,56 @@
|
|||||||
],
|
],
|
||||||
"_id": "abZXhqPblEq57+36N2j8xH"
|
"_id": "abZXhqPblEq57+36N2j8xH"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"__type__": "ec19eRwzzZA6LQg5DFOX78H",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": null,
|
||||||
|
"createRoom": {
|
||||||
|
"__uuid__": "584dbb85-4acc-4bea-9d9b-e0cbeb8741da",
|
||||||
|
"__expectedType__": "cc.Prefab"
|
||||||
|
},
|
||||||
|
"room": {
|
||||||
|
"__uuid__": "f4582832-23e2-4981-be26-08dfe832aa96",
|
||||||
|
"__expectedType__": "cc.Prefab"
|
||||||
|
},
|
||||||
|
"roomScene": {
|
||||||
|
"__uuid__": "18f06ada-9d36-485c-9f44-556d6980ca7a",
|
||||||
|
"__expectedType__": "cc.SceneAsset"
|
||||||
|
},
|
||||||
|
"_id": "c2sXHnthJJQoYDj00RAeBa"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"__type__": "cc.SceneGlobals",
|
"__type__": "cc.SceneGlobals",
|
||||||
"ambient": {
|
"ambient": {
|
||||||
"__id__": 6
|
|
||||||
},
|
|
||||||
"shadows": {
|
|
||||||
"__id__": 7
|
"__id__": 7
|
||||||
},
|
},
|
||||||
"_skybox": {
|
"shadows": {
|
||||||
"__id__": 8
|
"__id__": 8
|
||||||
},
|
},
|
||||||
"fog": {
|
"_skybox": {
|
||||||
"__id__": 9
|
"__id__": 9
|
||||||
},
|
},
|
||||||
"octree": {
|
"fog": {
|
||||||
"__id__": 10
|
"__id__": 10
|
||||||
},
|
},
|
||||||
"skin": {
|
"octree": {
|
||||||
"__id__": 11
|
"__id__": 11
|
||||||
},
|
},
|
||||||
"lightProbeInfo": {
|
"skin": {
|
||||||
"__id__": 12
|
"__id__": 12
|
||||||
},
|
},
|
||||||
"postSettings": {
|
"lightProbeInfo": {
|
||||||
"__id__": 13
|
"__id__": 13
|
||||||
},
|
},
|
||||||
|
"postSettings": {
|
||||||
|
"__id__": 14
|
||||||
|
},
|
||||||
"bakedWithStationaryMainLight": false,
|
"bakedWithStationaryMainLight": false,
|
||||||
"bakedWithHighpLightmap": false
|
"bakedWithHighpLightmap": false
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,449 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"__type__": "cc.SceneAsset",
|
||||||
|
"_name": "TemplateRoom",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_native": "",
|
||||||
|
"scene": {
|
||||||
|
"__id__": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Scene",
|
||||||
|
"_name": "TemplateRoom",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_parent": null,
|
||||||
|
"_children": [
|
||||||
|
{
|
||||||
|
"__id__": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_active": true,
|
||||||
|
"_components": [],
|
||||||
|
"_prefab": null,
|
||||||
|
"_lpos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_lrot": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_lscale": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"z": 1
|
||||||
|
},
|
||||||
|
"_mobility": 0,
|
||||||
|
"_layer": 1073741824,
|
||||||
|
"_euler": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"autoReleaseAssets": false,
|
||||||
|
"_globals": {
|
||||||
|
"__id__": 8
|
||||||
|
},
|
||||||
|
"_id": "18f06ada-9d36-485c-9f44-556d6980ca7a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Node",
|
||||||
|
"_name": "Canvas",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_parent": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"_children": [
|
||||||
|
{
|
||||||
|
"__id__": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_active": true,
|
||||||
|
"_components": [
|
||||||
|
{
|
||||||
|
"__id__": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 7
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_prefab": null,
|
||||||
|
"_lpos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 800,
|
||||||
|
"y": 360.00000000000006,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_lrot": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_lscale": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"z": 1
|
||||||
|
},
|
||||||
|
"_mobility": 0,
|
||||||
|
"_layer": 33554432,
|
||||||
|
"_euler": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_id": "1cgbnsENBDoLhHJwvyJnKB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Node",
|
||||||
|
"_name": "Camera",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_parent": {
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
"_children": [],
|
||||||
|
"_active": true,
|
||||||
|
"_components": [
|
||||||
|
{
|
||||||
|
"__id__": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_prefab": null,
|
||||||
|
"_lpos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 1000
|
||||||
|
},
|
||||||
|
"_lrot": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_lscale": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"z": 1
|
||||||
|
},
|
||||||
|
"_mobility": 0,
|
||||||
|
"_layer": 1073741824,
|
||||||
|
"_euler": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_id": "9atEuWMGdBq70ouQQ4tRnO"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Camera",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 3
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": null,
|
||||||
|
"_projection": 0,
|
||||||
|
"_priority": 0,
|
||||||
|
"_fov": 45,
|
||||||
|
"_fovAxis": 0,
|
||||||
|
"_orthoHeight": 360,
|
||||||
|
"_near": 0,
|
||||||
|
"_far": 2000,
|
||||||
|
"_color": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 0,
|
||||||
|
"g": 0,
|
||||||
|
"b": 0,
|
||||||
|
"a": 255
|
||||||
|
},
|
||||||
|
"_depth": 1,
|
||||||
|
"_stencil": 0,
|
||||||
|
"_clearFlags": 7,
|
||||||
|
"_rect": {
|
||||||
|
"__type__": "cc.Rect",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
},
|
||||||
|
"_aperture": 19,
|
||||||
|
"_shutter": 7,
|
||||||
|
"_iso": 0,
|
||||||
|
"_screenScale": 1,
|
||||||
|
"_visibility": 1108344832,
|
||||||
|
"_targetTexture": null,
|
||||||
|
"_postProcess": null,
|
||||||
|
"_usePostProcess": false,
|
||||||
|
"_cameraType": -1,
|
||||||
|
"_trackingType": 0,
|
||||||
|
"_id": "4780+rx15BKqkTCexT+Fi8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.UITransform",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": null,
|
||||||
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
|
"width": 1600,
|
||||||
|
"height": 720
|
||||||
|
},
|
||||||
|
"_anchorPoint": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
},
|
||||||
|
"_id": "9cHcVMc5JHVJoaZqnwhdaW"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Canvas",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": null,
|
||||||
|
"_cameraComponent": {
|
||||||
|
"__id__": 4
|
||||||
|
},
|
||||||
|
"_alignCanvasWithScreen": true,
|
||||||
|
"_id": "f7IXBbP6ZNjqIM4Q5/I3dI"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Widget",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 2
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": null,
|
||||||
|
"_alignFlags": 45,
|
||||||
|
"_target": null,
|
||||||
|
"_left": 0,
|
||||||
|
"_right": 0,
|
||||||
|
"_top": 5.684341886080802e-14,
|
||||||
|
"_bottom": 5.684341886080802e-14,
|
||||||
|
"_horizontalCenter": 0,
|
||||||
|
"_verticalCenter": 0,
|
||||||
|
"_isAbsLeft": true,
|
||||||
|
"_isAbsRight": true,
|
||||||
|
"_isAbsTop": true,
|
||||||
|
"_isAbsBottom": true,
|
||||||
|
"_isAbsHorizontalCenter": true,
|
||||||
|
"_isAbsVerticalCenter": true,
|
||||||
|
"_originalWidth": 0,
|
||||||
|
"_originalHeight": 0,
|
||||||
|
"_alignMode": 2,
|
||||||
|
"_lockFlags": 0,
|
||||||
|
"_id": "9fI6665VZLG5Uuoch5UpgC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.SceneGlobals",
|
||||||
|
"ambient": {
|
||||||
|
"__id__": 9
|
||||||
|
},
|
||||||
|
"shadows": {
|
||||||
|
"__id__": 10
|
||||||
|
},
|
||||||
|
"_skybox": {
|
||||||
|
"__id__": 11
|
||||||
|
},
|
||||||
|
"fog": {
|
||||||
|
"__id__": 12
|
||||||
|
},
|
||||||
|
"octree": {
|
||||||
|
"__id__": 13
|
||||||
|
},
|
||||||
|
"skin": {
|
||||||
|
"__id__": 14
|
||||||
|
},
|
||||||
|
"lightProbeInfo": {
|
||||||
|
"__id__": 15
|
||||||
|
},
|
||||||
|
"postSettings": {
|
||||||
|
"__id__": 16
|
||||||
|
},
|
||||||
|
"bakedWithStationaryMainLight": false,
|
||||||
|
"bakedWithHighpLightmap": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.AmbientInfo",
|
||||||
|
"_skyColorHDR": {
|
||||||
|
"__type__": "cc.Vec4",
|
||||||
|
"x": 0.2,
|
||||||
|
"y": 0.5,
|
||||||
|
"z": 0.8,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_skyColor": {
|
||||||
|
"__type__": "cc.Vec4",
|
||||||
|
"x": 0.2,
|
||||||
|
"y": 0.5,
|
||||||
|
"z": 0.8,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_skyIllumHDR": 20000,
|
||||||
|
"_skyIllum": 20000,
|
||||||
|
"_groundAlbedoHDR": {
|
||||||
|
"__type__": "cc.Vec4",
|
||||||
|
"x": 0.2,
|
||||||
|
"y": 0.2,
|
||||||
|
"z": 0.2,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_groundAlbedo": {
|
||||||
|
"__type__": "cc.Vec4",
|
||||||
|
"x": 0.2,
|
||||||
|
"y": 0.2,
|
||||||
|
"z": 0.2,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_skyColorLDR": {
|
||||||
|
"__type__": "cc.Vec4",
|
||||||
|
"x": 0.2,
|
||||||
|
"y": 0.5,
|
||||||
|
"z": 0.8,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_skyIllumLDR": 20000,
|
||||||
|
"_groundAlbedoLDR": {
|
||||||
|
"__type__": "cc.Vec4",
|
||||||
|
"x": 0.2,
|
||||||
|
"y": 0.2,
|
||||||
|
"z": 0.2,
|
||||||
|
"w": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.ShadowsInfo",
|
||||||
|
"_enabled": false,
|
||||||
|
"_type": 0,
|
||||||
|
"_normal": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 1,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_distance": 0,
|
||||||
|
"_planeBias": 1,
|
||||||
|
"_shadowColor": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 0,
|
||||||
|
"g": 0,
|
||||||
|
"b": 0,
|
||||||
|
"a": 76
|
||||||
|
},
|
||||||
|
"_maxReceived": 4,
|
||||||
|
"_size": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 1024,
|
||||||
|
"y": 1024
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.SkyboxInfo",
|
||||||
|
"_envLightingType": 0,
|
||||||
|
"_envmapHDR": null,
|
||||||
|
"_envmap": null,
|
||||||
|
"_envmapLDR": null,
|
||||||
|
"_diffuseMapHDR": null,
|
||||||
|
"_diffuseMapLDR": null,
|
||||||
|
"_enabled": false,
|
||||||
|
"_useHDR": true,
|
||||||
|
"_editableMaterial": null,
|
||||||
|
"_reflectionHDR": null,
|
||||||
|
"_reflectionLDR": null,
|
||||||
|
"_rotationAngle": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.FogInfo",
|
||||||
|
"_type": 0,
|
||||||
|
"_fogColor": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 200,
|
||||||
|
"g": 200,
|
||||||
|
"b": 200,
|
||||||
|
"a": 255
|
||||||
|
},
|
||||||
|
"_enabled": false,
|
||||||
|
"_fogDensity": 0.3,
|
||||||
|
"_fogStart": 0.5,
|
||||||
|
"_fogEnd": 300,
|
||||||
|
"_fogAtten": 5,
|
||||||
|
"_fogTop": 1.5,
|
||||||
|
"_fogRange": 1.2,
|
||||||
|
"_accurate": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.OctreeInfo",
|
||||||
|
"_enabled": false,
|
||||||
|
"_minPos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": -1024,
|
||||||
|
"y": -1024,
|
||||||
|
"z": -1024
|
||||||
|
},
|
||||||
|
"_maxPos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 1024,
|
||||||
|
"y": 1024,
|
||||||
|
"z": 1024
|
||||||
|
},
|
||||||
|
"_depth": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.SkinInfo",
|
||||||
|
"_enabled": true,
|
||||||
|
"_blurRadius": 0.01,
|
||||||
|
"_sssIntensity": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.LightProbeInfo",
|
||||||
|
"_giScale": 1,
|
||||||
|
"_giSamples": 1024,
|
||||||
|
"_bounces": 2,
|
||||||
|
"_reduceRinging": 0,
|
||||||
|
"_showProbe": true,
|
||||||
|
"_showWireframe": true,
|
||||||
|
"_showConvex": false,
|
||||||
|
"_data": null,
|
||||||
|
"_lightProbeSphereVolume": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.PostSettingsInfo",
|
||||||
|
"_toneMappingType": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.50",
|
||||||
|
"importer": "scene",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "18f06ada-9d36-485c-9f44-556d6980ca7a",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { _decorator, Component, Label, Mask, Node, UITransform, Widget } from 'cc';
|
||||||
|
import { announcementMotion } from '../../framework/presentation/lobby-model.ts';
|
||||||
|
import { requireNode } from './node-bindings.ts';
|
||||||
|
const {ccclass}=_decorator;
|
||||||
|
function required<T>(value:T|null,name:string):T {if(value===null)throw Error(`Announcement missing ${name}`);return value;}
|
||||||
|
|
||||||
|
/** Native clipping and movement of the authored Announcement children. */
|
||||||
|
@ccclass('AnnouncementView')
|
||||||
|
export class AnnouncementView extends Component {
|
||||||
|
private background:Node|null=null;
|
||||||
|
private textNode:Node|null=null;
|
||||||
|
private viewport:Node|null=null;
|
||||||
|
private message='';
|
||||||
|
private elapsed=0;
|
||||||
|
private textY=0;
|
||||||
|
|
||||||
|
show(message:string):void {
|
||||||
|
if(!this.viewport){
|
||||||
|
const bg=requireNode(this.node,'Spirit219'),text=requireNode(this.node,'Spirit220');
|
||||||
|
const bgUI=required(bg.getComponent(UITransform),'Spirit219 UITransform');
|
||||||
|
// The text is animated; an authored Widget must not reset its X every frame.
|
||||||
|
const widget=text.getComponent(Widget);if(widget)widget.enabled=false;
|
||||||
|
const viewport=new Node('AnnouncementClip');viewport.layer=this.node.layer;viewport.parent=bg;
|
||||||
|
const clipUI=viewport.addComponent(UITransform);clipUI.setAnchorPoint(0,0);
|
||||||
|
clipUI.setContentSize(bgUI.contentSize);
|
||||||
|
viewport.setPosition(-bgUI.width*bgUI.anchorX,-bgUI.height*bgUI.anchorY,0);
|
||||||
|
viewport.addComponent(Mask);
|
||||||
|
this.textY=text.position.y-bg.position.y+bgUI.height*bgUI.anchorY;
|
||||||
|
text.parent=viewport;
|
||||||
|
const textUI=required(text.getComponent(UITransform),'Spirit220 UITransform');
|
||||||
|
textUI.setAnchorPoint(0,textUI.anchorY);
|
||||||
|
const label=required(text.getComponent(Label),'Spirit220 Label');
|
||||||
|
label.enableWrapText=false;label.overflow=Label.Overflow.NONE;label.horizontalAlign=Label.HorizontalAlign.LEFT;
|
||||||
|
this.background=bg;this.textNode=text;this.viewport=viewport;
|
||||||
|
}
|
||||||
|
this.message=message;this.elapsed=0;
|
||||||
|
required(required(this.textNode,'text').getComponent(Label),'label').string=message;
|
||||||
|
this.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
update(dt:number):void {if(this.viewport){this.elapsed+=dt;this.draw();}}
|
||||||
|
|
||||||
|
private draw():void {
|
||||||
|
const text=required(this.textNode,'text'),bg=required(this.background,'background');
|
||||||
|
const width=required(bg.getComponent(UITransform),'background UITransform').width;
|
||||||
|
const state=announcementMotion(this.message,width,this.elapsed);
|
||||||
|
bg.active=state.visible;text.active=state.visible;
|
||||||
|
text.setPosition(state.left,this.textY,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "4b4591ab-307a-4061-9690-7102b996a3ae",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import {_decorator,Component} from 'cc';
|
||||||
|
import type {CreateRoomPageContext,CreateRoomPagePort} from '../../framework/sdk/contracts/create-room-page.ts';
|
||||||
|
const {ccclass}=_decorator;
|
||||||
|
/** Inherit this component on the subgame prefab root. Platform never inspects its children. */
|
||||||
|
@ccclass('CreateRoomPage')
|
||||||
|
export class CreateRoomPage extends Component implements CreateRoomPagePort {
|
||||||
|
open(_context:CreateRoomPageContext):void{throw Error('Subgame must implement CreateRoomPage.open');}
|
||||||
|
setBusy(_busy:boolean):void{throw Error('Subgame must implement CreateRoomPage.setBusy');}
|
||||||
|
close():void{throw Error('Subgame must implement CreateRoomPage.close');}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "956dedf9-3650-418b-af8a-a755a26d991c",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import {_decorator,Button,Component,Label} from 'cc';
|
||||||
|
import {JoinRoomInput} from '../../framework/presentation/join-room-input.ts';
|
||||||
|
const {ccclass,property}=_decorator;
|
||||||
|
export interface JoinRoomViewContext {initialCode:string;submit(code:number):void;cancel():void;paste():Promise<string>;reportError(message:string):void;}
|
||||||
|
@ccclass('JoinRoomView')
|
||||||
|
export class JoinRoomView extends Component {
|
||||||
|
@property([Button]) digitButtons:Button[]=[];
|
||||||
|
@property([Label]) slots:Label[]=[];
|
||||||
|
@property(Button) clearButton:Button|null=null;
|
||||||
|
@property(Button) deleteButton:Button|null=null;
|
||||||
|
@property(Button) pasteButton:Button|null=null;
|
||||||
|
@property(Button) submitButton:Button|null=null;
|
||||||
|
@property([Button]) cancelButtons:Button[]=[];
|
||||||
|
private input:JoinRoomInput|null=null;
|
||||||
|
private releases:Array<()=>void>=[];
|
||||||
|
private generation=0;
|
||||||
|
private busy=false;
|
||||||
|
private pasting=false;
|
||||||
|
open(context:JoinRoomViewContext):void{
|
||||||
|
this.close();const generation=this.generation;
|
||||||
|
if(this.digitButtons.length!==10||this.slots.length!==6||!this.clearButton||!this.deleteButton||!this.pasteButton||!this.submitButton||this.cancelButtons.length!==2)throw Error('JoinRoomView requires serialized controls');
|
||||||
|
const input=this.input=new JoinRoomInput(code=>context.submit(code));input.prefill(context.initialCode);
|
||||||
|
const bind=(button:Button,action:()=>void)=>{const fn=()=>{if(this.busy||this.pasting)return;try{action();}catch(error){context.reportError(error instanceof Error?error.message:String(error));}finally{this.render();}};
|
||||||
|
button.node.on(Button.EventType.CLICK,fn);this.releases.push(()=>button.node.off(Button.EventType.CLICK,fn));};
|
||||||
|
this.digitButtons.forEach((button,digit)=>bind(button,()=>input.digit(digit)));
|
||||||
|
bind(this.clearButton,()=>input.clear());bind(this.deleteButton,()=>input.remove());bind(this.submitButton,()=>input.confirm());
|
||||||
|
bind(this.pasteButton,()=>{this.pasting=true;void context.paste().then(text=>{if(this.generation===generation){input.paste(text);this.render();}})
|
||||||
|
.catch(error=>{if(this.generation===generation)context.reportError(error instanceof Error?error.message:String(error));})
|
||||||
|
.finally(()=>{if(this.generation===generation)this.pasting=false;});});
|
||||||
|
this.cancelButtons.forEach(button=>{const fn=()=>context.cancel();button.node.on(Button.EventType.CLICK,fn);this.releases.push(()=>button.node.off(Button.EventType.CLICK,fn));});
|
||||||
|
this.setBusy(false);this.render();
|
||||||
|
}
|
||||||
|
setBusy(busy:boolean):void{this.busy=busy;this.input?.setBusy(busy);for(const b of [...this.digitButtons,this.clearButton,this.deleteButton,this.submitButton,this.pasteButton])if(b)b.interactable=!busy;}
|
||||||
|
private render():void{const code=this.input?.code;if(code===undefined)return;this.slots.forEach((slot,i)=>{slot.node.active=i<code.length;slot.string=code[i]??'';});}
|
||||||
|
close():void{this.generation++;this.pasting=false;for(const release of this.releases.splice(0))release();this.input=null;}
|
||||||
|
onDestroy():void{this.close();}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "033ab9dd-e1a1-48fb-9fd6-5474c1a45eb2",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -1,17 +1,28 @@
|
|||||||
import { _decorator, Component, Prefab, SpriteFrame } from 'cc';
|
import { _decorator, Component, Prefab, SpriteFrame } from 'cc';
|
||||||
const {ccclass,property}=_decorator;
|
const {ccclass,property}=_decorator;
|
||||||
/** Editor-owned asset references; no runtime UUID/path guessing. */
|
/** Editor-owned asset references; no runtime UUID/path guessing. */
|
||||||
@ccclass('LobbyAssets')
|
@ccclass('LobbyAssets')
|
||||||
export class LobbyAssets extends Component {
|
export class LobbyAssets extends Component {
|
||||||
@property(Prefab) menu:Prefab|null=null;
|
@property(Prefab) menu:Prefab|null=null;
|
||||||
@property(Prefab) header:Prefab|null=null;
|
@property(Prefab) header:Prefab|null=null;
|
||||||
@property(Prefab) noticeMenu:Prefab|null=null;
|
@property(Prefab) noticeMenu:Prefab|null=null;
|
||||||
@property(Prefab) notices:Prefab|null=null;
|
@property(Prefab) redeemCode:Prefab|null=null;
|
||||||
@property(Prefab) profile:Prefab|null=null;
|
@property(Prefab) qrCode:Prefab|null=null;
|
||||||
@property(Prefab) settings:Prefab|null=null;
|
@property(Prefab) noticeBoard:Prefab|null=null;
|
||||||
@property(Prefab) join:Prefab|null=null;
|
@property(Prefab) ownedRoomList:Prefab|null=null;
|
||||||
@property(Prefab) tasks:Prefab|null=null;
|
@property(Prefab) invite:Prefab|null=null;
|
||||||
|
@property(Prefab) profile:Prefab|null=null;
|
||||||
|
@property(Prefab) settings:Prefab|null=null;
|
||||||
|
@property(Prefab) join:Prefab|null=null;
|
||||||
|
@property(Prefab) tasks:Prefab|null=null;
|
||||||
@property(Prefab) share:Prefab|null=null;
|
@property(Prefab) share:Prefab|null=null;
|
||||||
@property([SpriteFrame]) digits:SpriteFrame[]=[];
|
@property(Prefab) rank:Prefab|null=null;
|
||||||
@property([SpriteFrame]) switches:SpriteFrame[]=[];
|
@property(Prefab) rankItem:Prefab|null=null;
|
||||||
}
|
@property([SpriteFrame]) rankMedals:SpriteFrame[]=[];
|
||||||
|
@property(SpriteFrame) rankTitle:SpriteFrame|null=null;
|
||||||
|
@property(SpriteFrame) vipRankTitle:SpriteFrame|null=null;
|
||||||
|
@property([SpriteFrame]) ownedRoomDigits:SpriteFrame[]=[];
|
||||||
|
@property([SpriteFrame]) rankSymbols:SpriteFrame[]=[];
|
||||||
|
@property([SpriteFrame]) digits:SpriteFrame[]=[];
|
||||||
|
@property([SpriteFrame]) switches:SpriteFrame[]=[];
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user