40 KiB
40 KiB
代码框架总结
📋 文档概述
本文档对进贤麻将子游戏代码框架进行全面总结,包括:
- 架构总览 - 整体架构设计和分层结构
- 模块依赖 - 各模块间的依赖关系和调用链
- 设计模式 - 使用的设计模式和最佳实践
- 扩展指南 - 如何扩展和定制功能
- 最佳实践 - 开发规范和注意事项
文档目标:帮助开发者从宏观角度理解整个代码框架,掌握系统设计思想和扩展方法。
🏗️ 整体架构设计
三层架构模型
┌─────────────────────────────────────────────────────────────┐
│ 接口层 (Interface Layer) │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Export.js │◄────框架────┤ Import.js │ │
│ │ (框架→游戏) │ │ (游戏→框架) │ │
│ └──────────────┘ └──────────────┘ │
│ ↕ ↕ │
│ 14个框架接口 13个框架接口 │
└─────────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────────┐
│ 业务层 (Business Layer) │
│ ┌──────────────────────────────────────────────┐ │
│ │ RpcHandler (RPC路由器) │ │
│ │ • 客户端→服务器RPC调用路由 │ │
│ │ • 34个RPC方法实现 │ │
│ └───────┬──────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ GameController (游戏流程控制器) │ │
│ │ • 游戏生命周期管理 (开始、发牌、精牌、结算) │ │
│ │ • 状态转换协调 │ │
│ │ • 服务编排 │ │
│ └───┬─────────────┬─────────────┬────────────────────┘ │
│ ↓ ↓ ↓ │
│ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
│ │Operation │ │Mahjong │ │AI Manager │ │
│ │Manager │ │Game │ │(AI托管) │ │
│ │(操作管理)│ │Service │ └─────────────┘ │
│ └──────────┘ │(胡牌计分)│ │
│ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────────┐
│ 数据层 (Data Layer) │
│ ┌──────────────────────────────────────────────┐ │
│ │ GameStateManager (游戏状态管理器) │ │
│ │ • 游戏状态对象 (gameState) │ │
│ │ • 状态机管理 (7个游戏阶段) │ │
│ │ • 玩家状态管理 │ │
│ │ • 操作历史记录 │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 共享模块 (Shared Modules) │ │
│ │ ┌────────────┐ ┌────────────┐ │ │
│ │ │MahjongCard │ │Jing │ │ │
│ │ │(牌对象) │ │Algorithm │ │ │
│ │ └────────────┘ └────────────┘ │ │
│ │ ┌────────────┐ ┌────────────┐ │ │
│ │ │WinDetection│ │Score │ │ │
│ │ │Factory │ │Calculation │ │ │
│ │ └────────────┘ └────────────┘ │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 配置与常量 (Config & Constants) │ │
│ │ • RoomConfigUtils (房间配置解析) │ │
│ │ • GameConstants (12个常量模块) │ │
│ │ • ErrorMessages (错误消息定义) │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
架构分层职责
| 分层 | 职责 | 核心组件 |
|---|---|---|
| 接口层 | 框架与游戏双向通信 | Export.js, Import.js |
| 业务层 | 游戏逻辑实现和流程控制 | RpcHandler, GameController, Services |
| 数据层 | 状态管理、数据存储、算法实现 | GameStateManager, Shared Modules, Config |
🔗 模块依赖关系
完整依赖图
mod.js (模块入口)
↓ require
┌───────────────────────────────────────────────────┐
│ 接口层加载 │
│ ├─ export.js ────────────┐ │
│ └─ import.js │ │
└───────────────────────────┼───────────────────────┘
│
┌───────────────────────────┼───────────────────────┐
│ 业务层加载 │ │
│ ├─ RpcHandler.js ────────┤ │
│ ├─ GameController.js │ │
│ ├─ OperationManager.js │ │
│ ├─ MahjongGameService.js │ │
│ └─ AIManager.js │ │
└───────────────────────────┼───────────────────────┘
│
┌───────────────────────────┼───────────────────────┐
│ 数据层加载 (被业务层依赖)│ │
│ ├─ GameStateManager.js ──┘ │
│ ├─ shared/dataStructures/ │
│ │ └─ MahjongCard.js │
│ ├─ shared/algorithms/ │
│ │ ├─ JingAlgorithm.js │
│ │ ├─ WinDetectionFactory.js │
│ │ ├─ ScoreCalculation.js │
│ │ └─ PatternFactory.js │
│ ├─ shared/config/ │
│ │ ├─ RoomConfigUtils.js │
│ │ └─ RuleConfigParser.js │
│ └─ shared/constants/ │
│ └─ index.js (导出12个常量模块) │
└───────────────────────────────────────────────────┘
核心依赖关系详解
1️⃣ 接口层依赖
// export.js 依赖
export.js
├─ GameController (业务流程控制)
├─ RoomConfigUtils (配置解析)
├─ ErrorMessages (错误消息)
└─ Logger (日志记录)
// import.js 依赖
import.js
└─ 无内部依赖 (纯粹封装框架接口)
2️⃣ 业务层依赖
// RpcHandler.js 依赖
RpcHandler
├─ GameController (委托业务逻辑)
├─ OperationManager (操作验证)
├─ ErrorHandler (错误处理)
└─ Logger (日志记录)
// GameController.js 依赖
GameController
├─ GameStateManager (状态管理)
├─ MahjongGameService (胡牌计分)
├─ OperationManager (操作管理)
├─ AIManager (AI托管)
├─ DiceService (掷骰子)
├─ CardUtils (牌工具)
└─ JingAlgorithm (精牌算法)
// OperationManager.js 依赖
OperationManager
├─ GameStateManager (状态查询)
├─ WinDetectionFactory (胡牌检测)
├─ CardUtils (牌型检查)
└─ GameConstants (常量)
// MahjongGameService.js 依赖
MahjongGameService
├─ WinDetectionFactory (胡牌检测)
├─ ScoreCalculation (计分计算)
├─ JingAlgorithm (精分计算、比精)
├─ PatternFactory (牌型分析)
└─ BiJingSystem (比精系统)
3️⃣ 数据层依赖
// GameStateManager.js 依赖
GameStateManager
├─ GameConstants (游戏常量)
├─ RoomConfigUtils (房间配置)
└─ ErrorMessages (错误消息)
// WinDetectionFactory.js 依赖
WinDetectionFactory
├─ MahjongCard (牌对象)
├─ JingAlgorithm (精牌处理)
├─ PatternFactory (牌型检测)
└─ GameConstants (牌型常量)
// ScoreCalculation.js 依赖
ScoreCalculation
├─ JingAlgorithm (精分计算)
├─ GameConstants (分数常量)
└─ PatternFactory (牌型信息)
// JingAlgorithm.js 依赖
JingAlgorithm
├─ MahjongCard (牌对象操作)
└─ GameConstants (精牌规则常量)
// MahjongCard.js 依赖
MahjongCard
└─ 无依赖 (基础数据结构)
依赖层级图
Level 0 (基础层 - 无依赖)
├─ MahjongCard
├─ GameConstants
└─ ErrorMessages
Level 1 (算法层 - 依赖Level 0)
├─ JingAlgorithm (依赖: MahjongCard)
├─ RoomConfigUtils (依赖: GameConstants)
├─ PatternFactory (依赖: MahjongCard)
└─ CardUtils (依赖: MahjongCard)
Level 2 (服务层 - 依赖Level 0-1)
├─ WinDetectionFactory (依赖: MahjongCard, JingAlgorithm, PatternFactory)
├─ ScoreCalculation (依赖: JingAlgorithm, PatternFactory)
├─ GameStateManager (依赖: GameConstants, RoomConfigUtils)
└─ DiceService (依赖: GameConstants)
Level 3 (业务层 - 依赖Level 0-2)
├─ OperationManager (依赖: GameStateManager, WinDetectionFactory)
├─ MahjongGameService (依赖: WinDetectionFactory, ScoreCalculation, JingAlgorithm)
└─ AIManager (依赖: OperationManager, GameStateManager)
Level 4 (控制层 - 依赖Level 0-3)
└─ GameController (依赖: GameStateManager, OperationManager, MahjongGameService, AIManager)
Level 5 (RPC层 - 依赖Level 0-4)
└─ RpcHandler (依赖: GameController, OperationManager)
Level 6 (接口层 - 依赖Level 0-5)
└─ export.js (依赖: GameController, RoomConfigUtils)
🎨 设计模式应用
1. 工厂模式 (Factory Pattern)
应用场景
- MahjongCard创建 - 统一牌对象创建
- WinDetectionFactory - 胡牌检测策略工厂
- PatternFactory - 牌型分析工厂
实现示例
// MahjongCard.js - 工厂方法
MahjongCard.create = function(code) {
return new MahjongCard(code);
};
MahjongCard.createBatch = function(codes) {
return codes.map(function(code) {
return MahjongCard.create(code);
});
};
// WinDetectionFactory.js - 策略工厂
WinDetectionFactory.detectWin = function(handTiles, jingInfo, gameState) {
// 根据不同牌型策略检测
var strategies = [
this._checkPinghu, // 平胡策略
this._checkQidui, // 七对策略
this._checkSipeng, // 四碰策略
this._checkShisanlan, // 十三烂策略
this._checkQixingShisanlan // 七星十三烂策略
];
var results = [];
for (var i = 0; i < strategies.length; i++) {
var result = strategies[i].call(this, handTiles, jingInfo);
if (result.canWin) {
results.push(result);
}
}
return this._selectBestPattern(results);
};
优势:
- ✅ 统一对象创建接口
- ✅ 封装复杂创建逻辑
- ✅ 支持策略扩展
2. 单例模式 (Singleton Pattern)
应用场景
- Logger - 全局日志记录器
- ErrorHandler - 全局错误处理器
- Constants - 常量定义模块
实现示例
// Logger.js - 单例实现
var Logger = (function() {
var instance;
function createLogger() {
var config = {
level: 'INFO',
maxHistorySize: 1000,
enableConsole: true
};
var history = [];
var statistics = {
DEBUG: 0,
INFO: 0,
WARN: 0,
ERROR: 0
};
return {
log: function(level, message, data) {
// 实现日志记录
},
getHistory: function() {
return history;
},
getStatistics: function() {
return statistics;
}
};
}
return {
getInstance: function() {
if (!instance) {
instance = createLogger();
}
return instance;
}
};
})();
// 使用
var logger = Logger.getInstance();
logger.log('INFO', '游戏开始');
优势:
- ✅ 全局唯一实例
- ✅ 统一访问入口
- ✅ 资源共享
3. 观察者模式 (Observer Pattern)
应用场景
- 游戏状态变化通知 - GameStateManager通知状态变化
- 操作事件通知 - OperationManager通知操作完成
实现示例
// GameStateManager.js - 观察者模式
GameStateManager.transitionToPhase = function(gameState, newPhase, transitionData) {
var oldPhase = gameState.phase;
// 1. 状态转换
gameState.previousPhase = oldPhase;
gameState.phase = newPhase;
// 2. 通知观察者
this._notifyPhaseChange(gameState, oldPhase, newPhase, transitionData);
return {
success: true,
oldPhase: oldPhase,
newPhase: newPhase
};
};
GameStateManager._notifyPhaseChange = function(gameState, oldPhase, newPhase, data) {
// 触发状态变化事件
if (typeof gameState.onPhaseChange === 'function') {
gameState.onPhaseChange({
oldPhase: oldPhase,
newPhase: newPhase,
data: data,
timestamp: Date.now()
});
}
};
优势:
- ✅ 解耦状态变化和业务响应
- ✅ 支持多个观察者
- ✅ 灵活的事件处理
4. 策略模式 (Strategy Pattern)
应用场景
- 胡牌检测 - 不同牌型检测策略
- AI决策 - 不同难度AI策略
实现示例
// AIManager.js - 策略模式
AIManager.decideOperation = function(gameState, playerId) {
var aiLevel = this._getAILevel(playerId);
// 根据AI等级选择策略
var strategy;
switch (aiLevel) {
case 'easy':
strategy = this._easyStrategy;
break;
case 'medium':
strategy = this._mediumStrategy;
break;
case 'hard':
strategy = this._hardStrategy;
break;
default:
strategy = this._mediumStrategy;
}
return strategy.call(this, gameState, playerId);
};
// 简单策略 - 随机操作
AIManager._easyStrategy = function(gameState, playerId) {
var availableOps = this._getAvailableOperations(gameState, playerId);
if (availableOps.length === 0) return null;
var randomIndex = Math.floor(Math.random() * availableOps.length);
return availableOps[randomIndex];
};
// 中等策略 - 优先高价值操作
AIManager._mediumStrategy = function(gameState, playerId) {
var availableOps = this._getAvailableOperations(gameState, playerId);
if (availableOps.length === 0) return null;
// 按优先级排序: 胡 > 杠 > 碰 > 吃
availableOps.sort(function(a, b) {
return a.priority - b.priority;
});
return availableOps[0];
};
优势:
- ✅ 算法独立封装
- ✅ 易于切换和扩展
- ✅ 符合开闭原则
5. 适配器模式 (Adapter Pattern)
应用场景
- RoomAdapter - 适配友乐平台房间接口
- ConfigAdapter - 适配不同版本配置格式
实现示例
// RoomAdapter.js - 适配器模式
var RoomAdapter = {
/**
* 将友乐平台房间对象转换为游戏内部格式
*/
adaptRoom: function(o_room) {
return {
roomcode: o_room.roomcode,
roomtype: o_room.roomtype,
players: this._adaptPlayers(o_room.players),
config: this._parseRoomConfig(o_room.roomtype)
};
},
_adaptPlayers: function(platformPlayers) {
return platformPlayers.map(function(player, index) {
return {
id: player.playerid,
seat: index,
nickname: player.nickname,
avatar: player.avatar,
score: player.gameinfo ? player.gameinfo.score : 0
};
});
},
_parseRoomConfig: function(roomtype) {
return RoomConfigUtils.parse(roomtype);
}
};
优势:
- ✅ 隔离平台差异
- ✅ 简化业务逻辑
- ✅ 提高可移植性
6. 模板方法模式 (Template Method Pattern)
应用场景
- GameController.startGame - 游戏开始流程模板
- RpcHandler.handleOperation - RPC处理流程模板
实现示例
// RpcHandler.js - 模板方法
RpcHandler.prototype.handleOperation = function(pack, room, callback) {
// 模板流程
// 1. 前置验证
var validationResult = this._validateRequest(pack, room);
if (!validationResult.valid) {
return callback({ error: validationResult.error });
}
// 2. 权限检查
var authResult = this._checkPermission(pack, room);
if (!authResult.authorized) {
return callback({ error: 'PERMISSION_DENIED' });
}
// 3. 执行操作 (子类实现)
var result = this._performOperation(pack, room);
// 4. 后置处理
this._afterOperation(pack, room, result);
// 5. 返回结果
callback(result);
};
// 子类实现具体操作
RpcHandler.prototype._performOperation = function(pack, room) {
// 由具体RPC方法实现
throw new Error('_performOperation must be implemented');
};
优势:
- ✅ 统一处理流程
- ✅ 强制执行规范
- ✅ 减少重复代码
🔧 扩展指南
1. 添加新的RPC方法
步骤1: 在RpcHandler中定义方法
// rpc/RpcHandler.js
/**
* 新增RPC: 玩家请求换牌 (示例)
*/
RpcHandler.prototype.player_swap_card = function(pack, room, callback) {
var playerId = pack.playerid;
var cardToSwap = pack.data.card;
var gameState = room.gameState;
// 1. 验证操作合法性
if (gameState.phase !== 'playing') {
return callback({
error: 'INVALID_PHASE',
message: '当前阶段不允许换牌'
});
}
// 2. 执行换牌逻辑
var result = this._performCardSwap(gameState, playerId, cardToSwap);
// 3. 广播换牌消息
if (result.success) {
room.import.broadcast_operation(room, {
type: 'card_swap',
playerId: playerId,
card: cardToSwap
});
}
callback(result);
};
RpcHandler.prototype._performCardSwap = function(gameState, playerId, card) {
// 实现换牌逻辑
return { success: true };
};
步骤2: 在mod.js中注册RPC方法
// mod.js
mod_jinxianmahjong.player_swap_card = function(pack) {
return mod_jinxianmahjong.rpcHandler.player_swap_card(
pack,
mod_jinxianmahjong.room,
function(result) {
return result;
}
);
};
步骤3: 客户端调用
// 客户端代码
SendPack({
cmd: "DoPack",
gameid: "jinxianmahjong",
rpc: "player_swap_card",
data: {
card: "3m"
}
});
2. 添加新的游戏规则
步骤1: 在RoomConfigUtils中定义规则位
// shared/config/RoomConfigUtils.js
RoomConfigUtils.parse = function(roomtype) {
// ... 现有解析逻辑
// 新增规则位解析 (假设使用第14位)
var enableCardSwap = roomtype.charAt(13) === '1';
return {
// ... 现有配置
specialRules: {
enableCardSwap: enableCardSwap, // 新规则
// ... 其他规则
}
};
};
步骤2: 在操作验证中使用规则
// game/OperationManager.js
OperationManager.validateCardSwap = function(gameState, playerId, card) {
// 检查规则是否启用
if (!gameState.rules.specialRules.enableCardSwap) {
return {
valid: false,
error: 'CARD_SWAP_DISABLED',
message: '当前房间规则不允许换牌'
};
}
// ... 其他验证逻辑
return { valid: true };
};
3. 添加新的共享算法模块
步骤1: 创建算法模块
// shared/algorithms/NewAlgorithm.js
/**
* 新算法模块 (示例: 牌型强度评估)
*/
var NewAlgorithm = {
/**
* 评估手牌强度
* @param {Array<MahjongCard>} handCards - 手牌
* @param {Object} jingInfo - 精牌信息
* @returns {Object} 评估结果
*/
evaluateHandStrength: function(handCards, jingInfo) {
var strength = {
score: 0,
level: 'low', // low/medium/high
factors: []
};
// 1. 计算精牌数量
var jingCount = this._countJingCards(handCards, jingInfo);
strength.score += jingCount * 10;
strength.factors.push({ type: 'jing', count: jingCount });
// 2. 计算刻子数量
var keziCount = this._countKezi(handCards);
strength.score += keziCount * 15;
strength.factors.push({ type: 'kezi', count: keziCount });
// 3. 计算顺子数量
var shunziCount = this._countShunzi(handCards);
strength.score += shunziCount * 10;
strength.factors.push({ type: 'shunzi', count: shunziCount });
// 4. 确定等级
if (strength.score >= 50) {
strength.level = 'high';
} else if (strength.score >= 30) {
strength.level = 'medium';
}
return strength;
},
_countJingCards: function(handCards, jingInfo) {
var count = 0;
for (var i = 0; i < handCards.length; i++) {
if (handCards[i].code === jingInfo.zhengJing ||
handCards[i].code === jingInfo.fuJing) {
count++;
}
}
return count;
},
// ... 其他辅助方法
};
// 导出 (Node.js)
if (typeof module !== 'undefined' && module.exports) {
module.exports = NewAlgorithm;
}
// 导出 (浏览器)
if (typeof window !== 'undefined') {
window.NewAlgorithm = NewAlgorithm;
}
步骤2: 在mod.js中加载模块
// mod.js
function loadGameModules() {
// ... 现有模块加载
// 加载新算法模块
require('./shared/algorithms/NewAlgorithm.js');
console.log("[mod_jinxianmahjong] NewAlgorithm.js 加载成功");
// ... 继续加载其他模块
}
步骤3: 在业务代码中使用
// AIManager.js
AIManager.decideDiscard = function(gameState, playerId) {
var player = gameState.players[playerId];
var handCards = player.handCards;
// 使用新算法评估手牌
var strength = NewAlgorithm.evaluateHandStrength(
handCards,
gameState.jingInfo
);
console.log('手牌强度:', strength.level, '分数:', strength.score);
// 根据评估结果做决策
// ...
};
4. 添加新的常量模块
步骤1: 创建常量文件
// shared/constants/NewConstants.js
/**
* 新功能常量定义 (示例: 换牌功能)
*/
var NewConstants = {
// 换牌规则
SWAP_RULES: {
MAX_SWAPS_PER_ROUND: 3, // 每局最多换牌次数
SWAP_TIME_LIMIT: 10000, // 换牌时限(毫秒)
SWAP_COST: 1 // 换牌消耗(房卡)
},
// 换牌状态
SWAP_STATUS: {
AVAILABLE: 'available', // 可以换牌
IN_PROGRESS: 'in_progress', // 换牌中
COMPLETED: 'completed', // 已完成
TIMEOUT: 'timeout' // 超时
}
};
// 导出
if (typeof module !== 'undefined' && module.exports) {
module.exports = NewConstants;
}
步骤2: 在constants/index.js中注册
// shared/constants/index.js
var NewConstants = require('./NewConstants.js');
module.exports = {
// ... 现有常量
NewConstants: NewConstants
};
📝 最佳实践
1. 代码组织原则
✅ 单一职责原则 (SRP)
每个模块只负责一个功能:
// ❌ 不好的实践: 一个类做太多事情
var GameManager = {
dealCards: function() {},
checkWin: function() {},
calculateScore: function() {},
updateDatabase: function() {},
sendNotification: function() {}
};
// ✅ 好的实践: 职责分离
var GameController = {
dealCards: function() {}
};
var WinDetection = {
checkWin: function() {}
};
var ScoreCalculation = {
calculateScore: function() {}
};
✅ 开闭原则 (OCP)
对扩展开放,对修改关闭:
// ✅ 使用策略模式扩展牌型检测
WinDetectionFactory.registerPattern = function(patternName, checkFunction) {
this.patterns[patternName] = checkFunction;
};
// 添加新牌型无需修改核心代码
WinDetectionFactory.registerPattern('custom_pattern', function(handCards) {
// 自定义牌型检测逻辑
});
✅ 依赖倒置原则 (DIP)
依赖抽象而非具体实现:
// ✅ 通过接口依赖
GameController.setGameService = function(gameService) {
// gameService可以是任何实现了胡牌检测接口的对象
this.gameService = gameService;
};
2. 错误处理规范
统一错误返回格式
// 所有方法返回统一格式
function someOperation() {
try {
// 操作逻辑
return {
success: true,
data: result
};
} catch (error) {
return {
success: false,
error: error.code || 'UNKNOWN_ERROR',
message: error.message,
details: error.details
};
}
}
使用ErrorHandler统一处理
// 使用ErrorHandler记录和处理错误
var ErrorHandler = require('./shared/utils/ErrorHandler.js');
function riskyOperation() {
try {
// ... 可能出错的代码
} catch (error) {
ErrorHandler.handle(error, {
context: 'riskyOperation',
severity: 'high',
additionalInfo: { /* ... */ }
});
return {
success: false,
error: 'OPERATION_FAILED'
};
}
}
3. 日志记录规范
关键节点记录日志
var Logger = require('./shared/utils/Logger.js');
function startGame(room) {
Logger.info('游戏开始', {
roomcode: room.roomcode,
playerCount: room.playerCount,
timestamp: Date.now()
});
try {
// 游戏逻辑
Logger.debug('发牌完成', { cards: dealedCards });
Logger.info('精牌确定', { jingCard: jingCard });
} catch (error) {
Logger.error('游戏开始失败', {
error: error.message,
stack: error.stack
});
}
}
日志级别使用规范
| 级别 | 使用场景 | 示例 |
|---|---|---|
| TRACE | 最详细的调试信息 | 每次牌型检测的详细步骤 |
| DEBUG | 开发调试信息 | 中间变量值、函数调用参数 |
| INFO | 重要业务流程节点 | 游戏开始、发牌完成、胡牌 |
| WARN | 潜在问题警告 | 配置缺失使用默认值、性能警告 |
| ERROR | 错误但程序可继续 | 玩家操作失败、数据验证失败 |
| FATAL | 致命错误需要停止 | 数据库连接失败、核心模块加载失败 |
4. 性能优化建议
✅ 避免重复计算
// ❌ 不好: 重复计算
function checkWin(handCards, jingInfo) {
var jingCards = getJingCards(jingInfo); // 每次调用都计算
// ...
}
// ✅ 好: 缓存计算结果
GameStateManager.createGameState = function(roomcode, roomtype, players) {
var gameState = {
// ...
jingInfo: null,
_jingCardsCache: null // 缓存精牌列表
};
// 添加getter缓存精牌列表
Object.defineProperty(gameState, 'jingCards', {
get: function() {
if (!this._jingCardsCache && this.jingInfo) {
this._jingCardsCache = getJingCards(this.jingInfo);
}
return this._jingCardsCache;
}
});
return gameState;
};
✅ 减少对象创建
// ❌ 不好: 频繁创建临时对象
for (var i = 0; i < 1000; i++) {
var result = {
success: true,
data: someData
};
process(result);
}
// ✅ 好: 复用对象
var result = { success: false, data: null };
for (var i = 0; i < 1000; i++) {
result.success = true;
result.data = someData;
process(result);
}
✅ 使用对象池
// MahjongCard对象池
var MahjongCardPool = {
pool: [],
maxSize: 200,
acquire: function(code) {
var card = this.pool.pop();
if (card) {
card.code = code;
card.reset();
return card;
}
return new MahjongCard(code);
},
release: function(card) {
if (this.pool.length < this.maxSize) {
card.reset();
this.pool.push(card);
}
}
};
5. 测试规范
单元测试结构
// tests/units/test_module_name.js
console.log('\n========================================');
console.log('模块名称 单元测试');
console.log('========================================\n');
// 加载模块
var ModuleName = require('../../path/to/ModuleName.js');
// 测试用例1: 基础功能
console.log('【测试1】基础功能测试\n');
var result1 = ModuleName.someFunction(input1);
if (result1.success) {
console.log('✅ 测试通过');
} else {
console.log('❌ 测试失败:', result1.error);
}
// 测试用例2: 边界条件
console.log('\n【测试2】边界条件测试\n');
// ...
// 测试用例3: 错误处理
console.log('\n【测试3】错误处理测试\n');
// ...
console.log('\n========================================');
console.log('测试完成');
console.log('========================================\n');
6. 命名规范
变量命名
// ✅ 好的命名: 清晰表达意图
var handCards = []; // 手牌
var jingInfo = {}; // 精牌信息
var isWinDetected = false; // 是否检测到胡牌
var totalScore = 0; // 总分数
// ❌ 不好的命名: 含义不明
var hc = [];
var ji = {};
var flag = false;
var num = 0;
函数命名
// ✅ 好的命名: 动词+名词,清晰描述功能
function dealCards(players, dealer) {}
function checkWinPattern(handCards) {}
function calculateJingScore(jingCards) {}
function validateOperation(operation) {}
// ❌ 不好的命名
function deal() {}
function check() {}
function calc() {}
function validate() {}
常量命名
// ✅ 大写字母+下划线
var MAX_PLAYERS = 4;
var DEFAULT_HAND_SIZE = 13;
var JING_BASE_SCORE = 2;
// ❌ 小写或驼峰
var maxPlayers = 4;
var defaultHandSize = 13;
🚨 常见问题与解决方案
问题1: 模块加载顺序错误
症状: 提示"XXX is not defined"或"Cannot read property of undefined"
原因: 依赖的模块还未加载就使用了
解决方案:
// 检查mod.js中的加载顺序
// 确保被依赖的模块先加载
// ✅ 正确顺序
require('./shared/dataStructures/MahjongCard.js'); // 基础
require('./shared/algorithms/JingAlgorithm.js'); // 依赖MahjongCard
require('./shared/algorithms/WinDetectionFactory.js'); // 依赖JingAlgorithm
// ❌ 错误顺序
require('./shared/algorithms/WinDetectionFactory.js'); // 依赖未加载!
require('./shared/algorithms/JingAlgorithm.js');
require('./shared/dataStructures/MahjongCard.js');
问题2: 状态不一致
症状: gameState中的数据与预期不符,操作验证失败
原因: 多处修改gameState但未同步更新
解决方案:
// ✅ 使用GameStateManager统一管理状态
GameStateManager.updatePlayerState(gameState, playerId, {
handCards: newHandCards,
score: newScore
});
// ❌ 直接修改可能导致不一致
gameState.players[playerId].handCards = newHandCards;
gameState.players[playerId].score = newScore; // 可能忘记更新其他相关字段
问题3: 精牌计算错误
症状: 精分计算结果与预期不符
原因: 没有正确识别精牌或计算逻辑错误
解决方案:
// ✅ 使用JingAlgorithm统一处理
var jingScore = JingAlgorithm.calculateJingScore(
handCards,
jingInfo,
{
includeZhengJing: true,
includeFuJing: true,
excludeUsedCards: true // 排除已用于组合的精牌
}
);
// ❌ 手动计算容易出错
var jingScore = 0;
for (var i = 0; i < handCards.length; i++) {
if (handCards[i].code === jingInfo.zhengJing) {
jingScore += 2;
}
// 可能遗漏副精、杠精等特殊情况
}
问题4: 内存泄漏
症状: 服务器运行一段时间后内存持续增长
原因: 游戏结束后未清理gameState或事件监听器
解决方案:
// ✅ 游戏结束后清理
GameController.endGame = function(room) {
// 1. 保存游戏记录
this._saveGameRecord(room.gameState);
// 2. 清理事件监听器
room.gameState.onPhaseChange = null;
room.gameState.onOperationComplete = null;
// 3. 清理定时器
if (room.gameState.operationTimer) {
clearTimeout(room.gameState.operationTimer);
}
// 4. 释放gameState引用
room.gameState = null;
};
问题5: RPC调用超时
症状: 客户端请求超时,无响应
原因: 服务端处理时间过长或callback未调用
解决方案:
// ✅ 确保所有分支都调用callback
RpcHandler.prototype.player_operation = function(pack, room, callback) {
try {
// 验证
if (!this._validate(pack)) {
return callback({ error: 'INVALID_REQUEST' }); // 及时返回
}
// 处理
var result = this._process(pack, room);
// 返回结果
callback(result);
} catch (error) {
// 错误处理也要调用callback
callback({ error: 'INTERNAL_ERROR', message: error.message });
}
};
// ❌ 可能遗漏callback导致超时
RpcHandler.prototype.player_operation = function(pack, room, callback) {
if (!this._validate(pack)) {
return; // 忘记调用callback!
}
// ...
};
📚 相关文档链接
- 上一篇: 08-游戏流程概述.md - 完整游戏流程
- 下一篇: 10-快速入门指南.md - 开发入门指南
- 参考文档:
- 00-框架基础概述.md - 框架基础
- 04-游戏核心服务.md - 核心服务详解
- 05-共享代码模块.md - 算法模块详解
📝 附录
A. 模块清单
完整的模块列表和文件路径:
| 模块分类 | 模块名称 | 文件路径 |
|---|---|---|
| 入口 | mod.js | /server/games2/jinxianmahjong/mod.js |
| 接口 | Export | /server/games2/jinxianmahjong/export.js |
| 接口 | Import | /server/games2/jinxianmahjong/import.js |
| RPC | RpcHandler | /server/games2/jinxianmahjong/rpc/RpcHandler.js |
| 控制 | GameController | /server/games2/jinxianmahjong/game/GameController.js |
| 服务 | OperationManager | /server/games2/jinxianmahjong/game/OperationManager.js |
| 服务 | MahjongGameService | /server/games2/jinxianmahjong/game/MahjongGameService.js |
| 服务 | AIManager | /server/games2/jinxianmahjong/game/AIManager.js |
| 数据 | GameStateManager | /server/games2/jinxianmahjong/shared/dataStructures/GameStateManager.js |
| 数据 | MahjongCard | /server/games2/jinxianmahjong/shared/dataStructures/MahjongCard.js |
| 算法 | JingAlgorithm | /server/games2/jinxianmahjong/shared/algorithms/JingAlgorithm.js |
| 算法 | WinDetectionFactory | /server/games2/jinxianmahjong/shared/algorithms/WinDetectionFactory.js |
| 算法 | ScoreCalculation | /server/games2/jinxianmahjong/shared/algorithms/ScoreCalculation.js |
| 算法 | PatternFactory | /server/games2/jinxianmahjong/shared/algorithms/PatternFactory.js |
| 配置 | RoomConfigUtils | /server/games2/jinxianmahjong/shared/config/RoomConfigUtils.js |
| 配置 | RuleConfigParser | /server/games2/jinxianmahjong/shared/config/RuleConfigParser.js |
| 常量 | Constants (12个模块) | /server/games2/jinxianmahjong/shared/constants/ |
| 工具 | Logger | /server/games2/jinxianmahjong/shared/utils/Logger.js |
| 工具 | ErrorHandler | /server/games2/jinxianmahjong/shared/utils/ErrorHandler.js |
B. 接口清单
Export接口 (14个)
makewar- 创建房间get_needroomcard- 获取所需房卡数get_smallroundnumber- 获取小局数get_roomtype- 获取房间类型get_deskinfo- 获取牌桌信息smallround_prepare- 小局准备check_canhuprocess- 检查胡牌过程check_canhupai- 检查能否胡牌get_hu_describe- 获取胡牌描述check_baopai- 检查报牌check_baopai_process- 检查报牌过程check_canhupai_fanpai- 检查翻牌胡牌check_operate_fanpai- 检查翻牌操作recieve_match_result- 接收比赛结果
Import接口 (13个)
smallround_finish- 小局结束bigroom_finish- 大局结束sendcards- 发牌send_eachother_operate- 发送玩家间操作send_hupai_broadcast- 广播胡牌send_gangpai_broadcast- 广播杠牌send_message- 发送消息save_grade- 保存战绩random- 随机数get_trusteeship_level- 获取托管等级get_ip_address- 获取IP地址get_match_config- 获取比赛配置finish_gametask- 完成游戏任务
C. 关键算法复杂度
| 算法 | 时间复杂度 | 空间复杂度 | 说明 |
|---|---|---|---|
| 平胡检测 | O(n²) | O(n) | n为手牌数量,需要尝试多种组合 |
| 七对检测 | O(n) | O(n) | 线性扫描即可 |
| 精分计算 | O(n) | O(1) | 遍历手牌一次 |
| 比精算法 | O(n) | O(n) | 需要构建牌型组合 |
| 操作优先级 | O(m log m) | O(m) | m为可用操作数量,需要排序 |
文档版本: v1.0
最后更新: 2025年10月15日
维护者: 进贤麻将开发团队