Files
youlegames/codes/games/server/docs/guides/framework/01-Export接口说明.md
2026-02-04 23:47:45 +08:00

28 KiB
Raw Blame History

Export接口详细说明

文档目标详细说明框架调用子游戏的8个必需接口包括接口功能、参数、返回值、调用时机和实现要点。

📚 目录

  1. Export接口概述
  2. 8个必需接口详解
  3. RoomAdapter适配器
  4. 配置解析机制
  5. 实现模板和示例
  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 实现方式

// 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 - 获取创建房间所需房卡

功能说明

计算创建房间需要消耗的房卡数量,由框架在玩家创建房间时调用。

接口定义

exp.get_needroomcard = function(roomtype, o_game_config) {
    // 返回所需房卡数
    return number;
}

参数说明

roomtype - 房间类型配置

  • 类型: stringArray
  • 格式1: 字符串 "1234567890" (10位)
  • 格式2: 数组 ["1","2","3","4","5","6","7","8","9","0"]
  • 说明: 每位代表不同的游戏配置

进贤麻将roomtype结构

// 索引  含义              取值
// [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()  ← 这里
    ↓
检查房主房卡数量是否足够
    ↓
扣除房卡,创建房间

实现示例(进贤麻将)

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属性。

接口定义

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在同一时刻调用,用于设置房间的局数上限。

实现示例

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加入不扣卡

接口定义

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相同的值

实现示例

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. 返回开战数据包给所有玩家

接口定义

exp.makewar = function(o_room, o_game_config) {
    // 返回开战结果和初始游戏状态
    return Object;
}

参数说明

o_room - 平台房间对象

{
    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

标准返回结构

{
    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. 创建游戏桌对象

// 必须创建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

// 使用GameStateManager创建游戏状态
var gameState = GameStateManager.createGameState({
    roomcode: o_room.roomcode,
    roomtype: o_room.roomtype,
    players: o_room.seatlist
});

3. 游戏初始化流程

1. 洗牌发牌
2. 丢骰子选精
3. 确定庄家
4. 给庄家14张牌其他玩家13张
5. 初始化玩家状态

实现示例(进贤麻将)

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 - 获取牌桌信息(断线重连)

功能说明

玩家断线重连或中途加入房间时,返回当前的完整游戏状态,让玩家能够继续游戏。

接口定义

exp.get_deskinfo = function(o_room, seat) {
    // 返回玩家的游戏状态
    return Object;
}

参数说明

o_room - 房间对象同makewar

seat - 玩家座位号

  • 类型: Number
  • 范围: 0-34人麻将
  • 说明: 重连玩家的座位号

返回值

类型: Object

返回结构与makewar类似但要考虑玩家视角

{
    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. 性能考虑

  • 断线重连是常见操作,需要快速响应
  • 数据结构要紧凑,避免冗余信息

实现示例

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 - 解散房间处理

功能说明

房间解散时的清理工作,释放资源,保存最后的状态。

接口定义

exp.get_disbandRoom = function(o_room) {
    // 返回解散结果
    return Object;
}

参数说明

o_room - 房间对象

返回值

{
    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)  ← 这里
    ↓
子游戏清理资源
    ↓
返回最终结果
    ↓
框架广播解散消息

实现示例

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 - 玩家进入房间

功能说明

玩家加入房间时的处理,更新玩家列表,通知其他玩家。

接口定义

exp.player_enter = function(o_room, seat) {
    // 返回进入结果
    return Object;
}

参数说明

o_room - 房间对象
seat - 玩家座位号

返回值

{
    success: true,
    seat: 0,
    playerInfo: {
        playerid: "player001",
        nickname: "玩家1",
        avatar: "avatar1.jpg"
    }
}

实现示例

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 - 玩家离开房间

功能说明

玩家离开房间时的处理,更新房间状态,通知其他玩家。

接口定义

exp.player_leave = function(o_room, seat) {
    // 返回离开结果
    return Object;
}

参数说明

o_room - 房间对象
seat - 玩家座位号

返回值

{
    success: true,
    seat: 0,
    message: "玩家已离开"
}

实现示例

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

示例

// 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位配置字符串

// roomtype示例: "1312111000"
// 位置0: "1" = 8局
// 位置1: "3" = 带精(某种变体)
// 位置2: "1" = 平胡
// 位置3: "2" = 固定庄家
// 位置4: "1" = 启用上下翻
// 位置5: "1" = 启用埋地雷
// 位置6: "1" = 启用同一首歌
// 位置7: "0" = 房主付费
// 位置8-9: 保留

4.2 RuleConfigParser

// 使用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模板

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_desko_desk.o_room双向关联

Q4: get_deskinfo需要返回什么

A: 需要返回足够重建游戏界面的所有信息:

  • 当前游戏进度
  • 玩家手牌(仅该玩家)
  • 所有玩家的明牌和出牌区
  • 当前可执行的操作

Q5: 断线重连和中途加入有什么区别?

A:

  • 断线重连:玩家之前在游戏中,重新连接
  • 中途加入:观战者或新玩家加入正在进行的游戏

两者都调用get_deskinfo,但返回的信息可能不同。

Q6: RoomAdapter是必需的吗

A: 不是必需的,但强烈推荐。你也可以直接在export.js中实现所有逻辑但这会导致代码难以维护。

Q7: 如何测试Export接口

A:

// 模拟房间对象
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. 下一步

阅读以下文档继续学习:


相关代码文件

  • server/games2/jinxianmahjong/export.js
  • server/games2/jinxianmahjong/game/RoomAdapter.js
  • server/games2/jinxianmahjong/rules/RuleConfigParser.js