# Export接口详细说明 > **文档目标**:详细说明框架调用子游戏的8个必需接口,包括接口功能、参数、返回值、调用时机和实现要点。 ## 📚 目录 1. [Export接口概述](#1-export接口概述) 2. [8个必需接口详解](#2-8个必需接口详解) 3. [RoomAdapter适配器](#3-roomadapter适配器) 4. [配置解析机制](#4-配置解析机制) 5. [实现模板和示例](#5-实现模板和示例) 6. [常见问题](#6-常见问题) --- ## 1. Export接口概述 ### 1.1 什么是Export接口 Export接口是**框架调用子游戏**的标准接口集合,定义在`export.js`文件中。框架通过这些接口来: - 查询房间创建成本 - 初始化游戏(开战) - 处理断线重连 - 处理玩家进出 - 处理房间解散 ``` ┌─────────────────┐ │ 友乐框架 │ │ (youle_app) │ └────────┬────────┘ │ 调用export接口 ↓ ┌─────────────────┐ │ export.js │ ← 子游戏实现的8个必需接口 │ (子游戏接口) │ └─────────────────┘ ``` ### 1.2 8个必需接口清单 | 序号 | 接口名称 | 调用时机 | 主要作用 | |-----|---------|---------|---------| | 1 | `get_needroomcard` | 创建房间时 | 计算所需房卡数 | | 2 | `get_asetcount` | 创建房间时 | 获取房间总局数 | | 3 | `get_needroomcard_joinroom` | 玩家加入房间时 | 计算加入所需房卡数 | | 4 | `makewar` | 游戏开始时 | 初始化游戏状态 ⭐ | | 5 | `get_deskinfo` | 断线重连/中途加入时 | 返回当前游戏状态 | | 6 | `get_disbandRoom` | 解散房间时 | 清理游戏资源 | | 7 | `player_enter` | 玩家进入房间时 | 处理玩家加入 | | 8 | `player_leave` | 玩家离开房间时 | 处理玩家离开 | ### 1.3 实现方式 ```javascript // export.js文件结构 var cls_jinxianmahjong_export = { new: function() { var exp = {}; // 实现8个必需接口 exp.get_needroomcard = function(roomtype, o_game_config) { }; exp.get_asetcount = function(roomtype, o_game_config) { }; exp.get_needroomcard_joinroom = function(roomtype, o_game_config) { }; exp.makewar = function(o_room, o_game_config) { }; exp.get_deskinfo = function(o_room, seat) { }; exp.get_disbandRoom = function(o_room) { }; exp.player_enter = function(o_room, seat) { }; exp.player_leave = function(o_room, seat) { }; return exp; } }; // 挂载到模块 mod_jinxianmahjong.export = cls_jinxianmahjong_export.new(); ``` --- ## 2. 8个必需接口详解 ### 2.1 get_needroomcard - 获取创建房间所需房卡 #### 功能说明 计算创建房间需要消耗的房卡数量,由框架在玩家创建房间时调用。 #### 接口定义 ```javascript exp.get_needroomcard = function(roomtype, o_game_config) { // 返回所需房卡数 return number; } ``` #### 参数说明 **roomtype** - 房间类型配置 - **类型**: `string` 或 `Array` - **格式1**: 字符串 `"1234567890"` (10位) - **格式2**: 数组 `["1","2","3","4","5","6","7","8","9","0"]` - **说明**: 每位代表不同的游戏配置 **进贤麻将roomtype结构**: ```javascript // 索引 含义 取值 // [0] 局数配置 "1"=8局, "2"=16局, "3"=24局 // [1] 精牌玩法 "1"=带精, "2"=不带精 // [2] 胡牌类型 "1"=平胡, "2"=清一色等 // [3] 庄家规则 "1"=轮流坐庄, "2"=固定庄家 // [4] 上下翻 "1"=启用, "2"=禁用 // [5] 埋地雷 "1"=启用, "2"=禁用 // [6] 同一首歌 "1"=启用, "2"=禁用 // [7] AA制扣卡 "1"=房主扣卡, "2"=AA制 // [8] 允许中途加入 "1"=允许, "2"=不允许 // [9] 保留位 暂时未用 // 示例 "1312111000" // 8局、带精、清一色、轮流坐庄、启用上下翻、启用埋地雷、启用同一首歌 ``` **o_game_config** - 游戏配置对象(可选) - **类型**: `Object` - **包含**: 解析后的游戏配置信息 #### 返回值 **类型**: `Number` **规则**(进贤麻将): - 8局房间:1张房卡 - 16局房间:2张房卡 - 24局房间:3张房卡 #### 调用时机 ``` 用户点击"创建房间" ↓ 客户端发送创建房间请求 ↓ 框架调用 get_needroomcard() ← 这里 ↓ 检查房主房卡数量是否足够 ↓ 扣除房卡,创建房间 ``` #### 实现示例(进贤麻将) ```javascript exp.get_needroomcard = function(roomtype, o_game_config) { console.log("[export.get_needroomcard] 计算房卡需求,roomtype:", roomtype); // 调用RoomAdapter统一处理 if (!RoomAdapter) { throw new Error("RoomAdapter未加载"); } return RoomAdapter.calculateRoomCardCost(roomtype, o_game_config); }; // RoomAdapter中的实现 RoomAdapter.calculateRoomCardCost = function(roomtype, config) { // 解析roomtype var rtArray = Array.isArray(roomtype) ? roomtype : roomtype.split(''); var roundsConfig = rtArray[0]; // 局数配置 // 根据局数返回房卡数 switch(roundsConfig) { case "1": return 1; // 8局 case "2": return 2; // 16局 case "3": return 3; // 24局 default: return 1; } }; ``` --- ### 2.2 get_asetcount - 获取房间局数 #### 功能说明 返回房间的总局数设置,框架用此值设置`o_room.asetcount`属性。 #### 接口定义 ```javascript exp.get_asetcount = function(roomtype, o_game_config) { // 返回房间总局数 return number; } ``` #### 参数说明 参数与`get_needroomcard`相同。 #### 返回值 **类型**: `Number` **规则**(进贤麻将): - roomtype[0] = "1" → 返回 8 - roomtype[0] = "2" → 返回 16 - roomtype[0] = "3" → 返回 24 #### 调用时机 与`get_needroomcard`在同一时刻调用,用于设置房间的局数上限。 #### 实现示例 ```javascript exp.get_asetcount = function(roomtype, o_game_config) { console.log("[export.get_asetcount] 获取房间局数,roomtype:", roomtype); if (!RoomAdapter) { throw new Error("RoomAdapter未加载"); } return RoomAdapter.getRoomTotalRounds(roomtype, o_game_config); }; // RoomAdapter中的实现 RoomAdapter.getRoomTotalRounds = function(roomtype, config) { var rtArray = Array.isArray(roomtype) ? roomtype : roomtype.split(''); var roundsConfig = rtArray[0]; switch(roundsConfig) { case "1": return 8; // 8局 case "2": return 16; // 16局 case "3": return 24; // 24局 default: return 8; } }; ``` --- ### 2.3 get_needroomcard_joinroom - 获取加入房间所需房卡 #### 功能说明 计算玩家加入已存在房间时需要消耗的房卡数量。大多数房卡游戏返回0(加入不扣卡)。 #### 接口定义 ```javascript exp.get_needroomcard_joinroom = function(roomtype, o_game_config) { // 返回加入所需房卡数 return number; } ``` #### 参数说明 参数与`get_needroomcard`相同。 #### 返回值 **类型**: `Number` **常见值**: - `0` - 加入房间不消耗房卡(房主付费模式) - `> 0` - AA制,每人都需要房卡 **进贤麻将规则**: - 根据roomtype[7]判断: - "1" = 房主付费模式,返回 0 - "2" = AA制,返回与`get_needroomcard`相同的值 #### 实现示例 ```javascript exp.get_needroomcard_joinroom = function(roomtype, o_game_config) { console.log("[export.get_needroomcard_joinroom] 计算加入房间房卡需求"); if (!RoomAdapter) { throw new Error("RoomAdapter未加载"); } return RoomAdapter.calculateJoinRoomCardCost(roomtype, o_game_config); }; // RoomAdapter中的实现 RoomAdapter.calculateJoinRoomCardCost = function(roomtype, config) { var rtArray = Array.isArray(roomtype) ? roomtype : roomtype.split(''); var payMode = rtArray[7]; // AA制配置 if (payMode === "2") { // AA制:每人都需要房卡 return this.calculateRoomCardCost(roomtype, config); } // 房主付费模式 return 0; }; ``` --- ### 2.4 makewar - 开战函数 ⭐⭐⭐⭐⭐ #### 功能说明 **最重要的接口**,当房间满员或房主手动开战时调用。负责: 1. 创建游戏桌对象(o_desk) 2. 初始化游戏状态(gameState) 3. 发牌、选精等游戏初始化 4. 返回开战数据包给所有玩家 #### 接口定义 ```javascript exp.makewar = function(o_room, o_game_config) { // 返回开战结果和初始游戏状态 return Object; } ``` #### 参数说明 **o_room** - 平台房间对象 ```javascript { roomcode: 100001, // 房间号码 roomtype: "1312111000", // 房间类型配置 asetcount: 8, // 总局数 currentRound: 1, // 当前局数(初始为1) status: 1, // 房间状态 seatlist: [ // 玩家座位列表 { seat: 0, playerid: "player001", nickname: "玩家1", avatar: "avatar1.jpg", status: 1 // 玩家状态 }, // ... 其他座位 ], method: { // 房间方法(发包接口) sendpack_toall: function(msg) {}, // 广播给所有人 sendpack_toseat: function(msg, seat) {}, // 发给指定座位 sendpack_toother: function(msg, seat) {} // 发给其他人 } } ``` **o_game_config** - 游戏配置对象(可选) #### 返回值 **类型**: `Object` **标准返回结构**: ```javascript { success: true, // 操作是否成功 message: "游戏开始", // 结果消息 gameState: { // 初始游戏状态 phase: "dealing", // 游戏阶段 currentRound: 1, // 当前局数 dealer: 0, // 庄家座位号 playersState: [ // 玩家状态数组 { seat: 0, playerid: "player001", handCards: [...], // 手牌(对该玩家可见) openMelds: [], // 明牌(吃碰杠) discardedCards: [],// 出牌区 score: 0, // 分数 status: "playing" // 状态 } // ... ], gameData: { // 游戏数据 deck: [...], // 剩余牌堆 jingCard: "5m", // 精牌 jingStatus: { }, // 精牌状态 currentPlayer: 0 // 当前操作玩家 } } } ``` #### 调用时机 ``` 房间满员或房主点击"开始游戏" ↓ 框架调用 makewar(o_room, config) ← 这里 ↓ 子游戏创建o_desk对象 ↓ 初始化gameState ↓ 发牌、选精 ↓ 返回开战数据包 ↓ 框架广播给所有玩家 ``` #### 核心要点 **1. 创建游戏桌对象** ```javascript // 必须创建o_desk对象 var o_desk = { o_room: o_room, // 指向房间对象 gameState: gameState, // 游戏状态 gameController: controller, // 游戏控制器 debug: { // 调试接口 save_receivepack: function() {}, save_sendpack: function() {} } }; // 双向关联 o_room.o_desk = o_desk; o_desk.o_room = o_room; ``` **2. 初始化gameState** ```javascript // 使用GameStateManager创建游戏状态 var gameState = GameStateManager.createGameState({ roomcode: o_room.roomcode, roomtype: o_room.roomtype, players: o_room.seatlist }); ``` **3. 游戏初始化流程** ```javascript 1. 洗牌、发牌 2. 丢骰子选精 3. 确定庄家 4. 给庄家14张牌(其他玩家13张) 5. 初始化玩家状态 ``` #### 实现示例(进贤麻将) ```javascript exp.makewar = function(o_room, o_game_config) { try { console.log("[export.makewar] 开始游戏,房间ID:", o_room.roomcode); // 使用RoomAdapter处理开战逻辑 if (RoomAdapter) { return RoomAdapter.handleMakeWar(o_room, o_game_config); } else { throw new Error("RoomAdapter未加载"); } } catch (error) { console.error("[export.makewar] 开战失败:", error); return { success: false, message: '游戏初始化失败: ' + error.message }; } }; // RoomAdapter中的实现(简化版) RoomAdapter.handleMakeWar = function(o_room, o_game_config) { // 1. 解析房间配置 var roomConfig = RuleConfigParser.parse(o_room.roomtype); // 2. 创建游戏状态 var gameState = GameStateManager.createGameState({ roomcode: o_room.roomcode, roomtype: o_room.roomtype, players: o_room.seatlist, rulesConfig: roomConfig }); // 3. 创建游戏桌对象 var o_desk = { o_room: o_room, gameState: gameState, gameService: new MahjongGameService(gameState), debug: createDebugInterface() }; // 4. 双向关联 o_room.o_desk = o_desk; o_desk.o_room = o_room; // 5. 初始化游戏(发牌、选精) o_desk.gameService.initializeRound(); // 6. 返回开战数据 return { success: true, message: "游戏开始", gameState: gameState }; }; ``` --- ### 2.5 get_deskinfo - 获取牌桌信息(断线重连) #### 功能说明 玩家断线重连或中途加入房间时,返回当前的完整游戏状态,让玩家能够继续游戏。 #### 接口定义 ```javascript exp.get_deskinfo = function(o_room, seat) { // 返回玩家的游戏状态 return Object; } ``` #### 参数说明 **o_room** - 房间对象(同makewar) **seat** - 玩家座位号 - **类型**: `Number` - **范围**: 0-3(4人麻将) - **说明**: 重连玩家的座位号 #### 返回值 **类型**: `Object` **返回结构**(与makewar类似,但要考虑玩家视角): ```javascript { success: true, roomcode: 100001, seat: 0, // 该玩家的座位 phase: "playing", // 当前游戏阶段 currentRound: 3, // 当前局数 totalRounds: 8, // 总局数 dealer: 1, // 当前庄家 currentPlayer: 2, // 当前操作玩家 myState: { // 我的状态 handCards: [...], // 我的手牌 openMelds: [...], // 我的明牌 discardedCards: [...], // 我的出牌区 score: 150, // 我的分数 availableOperations: [...] // 可执行的操作 }, othersState: [ // 其他玩家状态 { seat: 1, handCardCount: 13, // 手牌数量(不显示具体牌) openMelds: [...], // 明牌 discardedCards: [...], // 出牌区 score: -50 } // ... ], gameData: { // 公共游戏数据 remainingCards: 52, // 剩余牌数 jingCard: "5m", // 精牌 lastDiscard: "3p" // 最后打出的牌 } } ``` #### 调用时机 ``` 玩家断线 ↓ 玩家重新连接 ↓ 客户端发送重连请求 ↓ 框架调用 get_deskinfo(o_room, seat) ← 这里 ↓ 返回当前游戏状态 ↓ 客户端重建界面 ``` #### 重要注意事项 **1. 信息可见性** - 只返回该玩家应该看到的信息 - 不能泄露其他玩家的手牌 - 公共信息(明牌、出牌区)全部可见 **2. 状态完整性** - 必须包含所有重建界面所需的信息 - 当前游戏进度、玩家状态、可执行操作等 **3. 性能考虑** - 断线重连是常见操作,需要快速响应 - 数据结构要紧凑,避免冗余信息 #### 实现示例 ```javascript exp.get_deskinfo = function(o_room, seat) { try { console.log("[export.get_deskinfo] 获取桌面信息,座位:", seat); if (!o_room.o_desk) { return { success: false, message: "房间尚未开始游戏" }; } // 使用RoomAdapter获取桌面信息 if (RoomAdapter) { return RoomAdapter.getDeskInfo(o_room, seat); } else { throw new Error("RoomAdapter未加载"); } } catch (error) { console.error("[export.get_deskinfo] 获取桌面信息失败:", error); return { success: false, message: '获取牌桌信息失败: ' + error.message }; } }; // RoomAdapter中的实现 RoomAdapter.getDeskInfo = function(o_room, seat) { var gameState = o_room.o_desk.gameState; var playerState = gameState.playersState[seat]; // 构建玩家视角的游戏状态 return { success: true, roomcode: o_room.roomcode, seat: seat, phase: gameState.phase, currentRound: gameState.currentRound, totalRounds: gameState.rulesConfig.gameRules.maxRounds, dealer: gameState.dealer, currentPlayer: gameState.currentPlayer, // 我的状态(完整信息) myState: { handCards: playerState.handCards, openMelds: playerState.openMelds, discardedCards: playerState.discardedCards, score: playerState.score, availableOperations: this.getAvailableOperations(gameState, seat) }, // 其他玩家状态(部分信息) othersState: this.getOthersState(gameState, seat), // 公共游戏数据 gameData: { remainingCards: gameState.gameData.deck.length, jingCard: gameState.gameData.jingCard, lastDiscard: gameState.lastDiscard } }; }; ``` --- ### 2.6 get_disbandRoom - 解散房间处理 #### 功能说明 房间解散时的清理工作,释放资源,保存最后的状态。 #### 接口定义 ```javascript exp.get_disbandRoom = function(o_room) { // 返回解散结果 return Object; } ``` #### 参数说明 **o_room** - 房间对象 #### 返回值 ```javascript { success: true, message: "房间已解散", finalScores: [ // 最终分数 { seat: 0, playerid: "p1", score: 100 }, { seat: 1, playerid: "p2", score: -50 }, // ... ], statistics: { // 统计信息 totalRounds: 5, // 完成局数 timestamp: 1234567890 } } ``` #### 调用时机 ``` 玩家申请解散房间 ↓ 投票通过 ↓ 框架调用 get_disbandRoom(o_room) ← 这里 ↓ 子游戏清理资源 ↓ 返回最终结果 ↓ 框架广播解散消息 ``` #### 实现示例 ```javascript exp.get_disbandRoom = function(o_room) { try { console.log("[export.get_disbandRoom] 解散房间,房间ID:", o_room.roomcode); // 使用RoomAdapter处理房间解散 if (RoomAdapter) { return RoomAdapter.handleDisbandRoom(o_room); } else { throw new Error("RoomAdapter未加载"); } } catch (error) { console.error("[export.get_disbandRoom] 解散房间失败:", error); return { success: false, message: '解散房间失败: ' + error.message }; } }; ``` --- ### 2.7 player_enter - 玩家进入房间 #### 功能说明 玩家加入房间时的处理,更新玩家列表,通知其他玩家。 #### 接口定义 ```javascript exp.player_enter = function(o_room, seat) { // 返回进入结果 return Object; } ``` #### 参数说明 **o_room** - 房间对象 **seat** - 玩家座位号 #### 返回值 ```javascript { success: true, seat: 0, playerInfo: { playerid: "player001", nickname: "玩家1", avatar: "avatar1.jpg" } } ``` #### 实现示例 ```javascript exp.player_enter = function(o_room, seat) { try { console.log("[export.player_enter] 玩家进入房间,座位:", seat); if (RoomAdapter) { return RoomAdapter.handlePlayerEnter(o_room, seat); } else { throw new Error("RoomAdapter未加载"); } } catch (error) { console.error("[export.player_enter] 玩家进入失败:", error); return { success: false, message: error.message }; } }; ``` --- ### 2.8 player_leave - 玩家离开房间 #### 功能说明 玩家离开房间时的处理,更新房间状态,通知其他玩家。 #### 接口定义 ```javascript exp.player_leave = function(o_room, seat) { // 返回离开结果 return Object; } ``` #### 参数说明 **o_room** - 房间对象 **seat** - 玩家座位号 #### 返回值 ```javascript { success: true, seat: 0, message: "玩家已离开" } ``` #### 实现示例 ```javascript exp.player_leave = function(o_room, seat) { try { console.log("[export.player_leave] 玩家离开房间,座位:", seat); if (RoomAdapter) { return RoomAdapter.handlePlayerLeave(o_room, seat); } else { throw new Error("RoomAdapter未加载"); } } catch (error) { console.error("[export.player_leave] 玩家离开失败:", error); return { success: false, message: error.message }; } }; ``` --- ## 3. RoomAdapter适配器 ### 3.1 RoomAdapter的作用 RoomAdapter是进贤麻将中用于**统一处理Export接口逻辑**的适配器类,负责: 1. **接口实现集中管理**:所有Export接口的实际逻辑 2. **配置解析**:解析roomtype配置 3. **状态管理**:管理游戏状态的创建和更新 4. **资源管理**:管理游戏资源的分配和释放 ### 3.2 架构关系 ``` export.js (接口定义) ↓ 调用 RoomAdapter (实现逻辑) ↓ 调用 GameStateManager (状态管理) GameController (游戏控制) MahjongGameService (游戏服务) ``` ### 3.3 为什么使用RoomAdapter **优点**: 1. **解耦**:export.js只负责接口定义,不包含具体逻辑 2. **复用**:多个接口可以共享相同的逻辑代码 3. **测试**:可以独立测试RoomAdapter的逻辑 4. **维护**:修改逻辑只需要修改RoomAdapter **示例**: ```javascript // export.js - 简洁的接口定义 exp.makewar = function(o_room, o_game_config) { return RoomAdapter.handleMakeWar(o_room, o_game_config); }; // RoomAdapter.js - 复杂的实现逻辑 RoomAdapter.handleMakeWar = function(o_room, o_game_config) { // 1. 解析配置 var config = RuleConfigParser.parse(o_room.roomtype); // 2. 创建状态 var gameState = GameStateManager.createGameState({...}); // 3. 创建服务 var gameService = new MahjongGameService(gameState); // 4. 初始化游戏 gameService.initializeRound(); // 5. 返回结果 return { success: true, gameState: gameState }; }; ``` --- ## 4. 配置解析机制 ### 4.1 roomtype配置解析 进贤麻将使用10位配置字符串: ```javascript // roomtype示例: "1312111000" // 位置0: "1" = 8局 // 位置1: "3" = 带精(某种变体) // 位置2: "1" = 平胡 // 位置3: "2" = 固定庄家 // 位置4: "1" = 启用上下翻 // 位置5: "1" = 启用埋地雷 // 位置6: "1" = 启用同一首歌 // 位置7: "0" = 房主付费 // 位置8-9: 保留 ``` ### 4.2 RuleConfigParser ```javascript // 使用RuleConfigParser解析配置 var config = RuleConfigParser.parse(roomtype); // 返回结构 { gameRules: { maxRounds: 8, // 总局数 useJing: true, // 是否使用精牌 dealerRule: "rotate" // 庄家规则 }, startHuRules: { mainRule: "8子起" // 起胡规则 }, specialRules: { shangxiafan: true, // 上下翻 maileilei: true, // 埋地雷 tongyishouage: true // 同一首歌 }, paymentRules: { mode: "owner" // 付费模式 } } ``` --- ## 5. 实现模板和示例 ### 5.1 完整的export.js模板 ```javascript var cls_jinxianmahjong_export = { new: function() { var exp = {}; // 1. 获取创建房间所需房卡数 exp.get_needroomcard = function(roomtype, o_game_config) { return RoomAdapter.calculateRoomCardCost(roomtype, o_game_config); }; // 2. 获取房间局数 exp.get_asetcount = function(roomtype, o_game_config) { return RoomAdapter.getRoomTotalRounds(roomtype, o_game_config); }; // 3. 获取加入房间所需房卡数 exp.get_needroomcard_joinroom = function(roomtype, o_game_config) { return RoomAdapter.calculateJoinRoomCardCost(roomtype, o_game_config); }; // 4. 开战函数 exp.makewar = function(o_room, o_game_config) { return RoomAdapter.handleMakeWar(o_room, o_game_config); }; // 5. 获取牌桌信息(断线重连) exp.get_deskinfo = function(o_room, seat) { return RoomAdapter.getDeskInfo(o_room, seat); }; // 6. 解散房间处理 exp.get_disbandRoom = function(o_room) { return RoomAdapter.handleDisbandRoom(o_room); }; // 7. 玩家进入房间 exp.player_enter = function(o_room, seat) { return RoomAdapter.handlePlayerEnter(o_room, seat); }; // 8. 玩家离开房间 exp.player_leave = function(o_room, seat) { return RoomAdapter.handlePlayerLeave(o_room, seat); }; return exp; } }; // 挂载到模块 mod_jinxianmahjong.export = cls_jinxianmahjong_export.new(); ``` --- ## 6. 常见问题 ### Q1: 为什么get_needroomcard和get_asetcount都需要roomtype参数? A: 因为房卡数量和局数都由roomtype决定。例如: - 8局房间:1张房卡 - 16局房间:2张房卡 - 24局房间:3张房卡 ### Q2: makewar什么时候调用? A: 两种情况: 1. 房间满员时自动开战 2. 房主手动点击"开始游戏" ### Q3: o_room和o_desk的关系? A: - `o_room`:平台管理的房间对象(框架提供) - `o_desk`:子游戏的桌对象(子游戏创建) - 通过`o_room.o_desk`和`o_desk.o_room`双向关联 ### Q4: get_deskinfo需要返回什么? A: 需要返回足够重建游戏界面的所有信息: - 当前游戏进度 - 玩家手牌(仅该玩家) - 所有玩家的明牌和出牌区 - 当前可执行的操作 ### Q5: 断线重连和中途加入有什么区别? A: - **断线重连**:玩家之前在游戏中,重新连接 - **中途加入**:观战者或新玩家加入正在进行的游戏 两者都调用`get_deskinfo`,但返回的信息可能不同。 ### Q6: RoomAdapter是必需的吗? A: 不是必需的,但**强烈推荐**。你也可以直接在export.js中实现所有逻辑,但这会导致代码难以维护。 ### Q7: 如何测试Export接口? A: ```javascript // 模拟房间对象 var mockRoom = { roomcode: 100001, roomtype: "1312111000", seatlist: [/* ... */] }; // 测试get_needroomcard var cost = mod_jinxianmahjong.export.get_needroomcard( mockRoom.roomtype, {} ); console.log("房卡需求:", cost); // 应该输出: 1 // 测试get_asetcount var rounds = mod_jinxianmahjong.export.get_asetcount( mockRoom.roomtype, {} ); console.log("房间局数:", rounds); // 应该输出: 8 ``` --- ## 7. 下一步 阅读以下文档继续学习: - [02-Import接口说明](./02-Import接口说明.md) - 子游戏调用框架的接口 - [03-RPC处理机制](./03-RPC处理机制.md) - 客户端请求的处理流程 - [04-游戏核心服务](../core/04-游戏核心服务.md) - GameController、MahjongGameService等 --- **相关代码文件**: - `server/games2/jinxianmahjong/export.js` - `server/games2/jinxianmahjong/game/RoomAdapter.js` - `server/games2/jinxianmahjong/rules/RuleConfigParser.js`