Files
youlegames/codes/games/server/docs/guides/architecture/09-代码框架总结.md
2026-02-04 23:47:45 +08:00

1379 lines
40 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 代码框架总结
## 📋 文档概述
本文档对进贤麻将子游戏代码框架进行全面总结,包括:
- **架构总览** - 整体架构设计和分层结构
- **模块依赖** - 各模块间的依赖关系和调用链
- **设计模式** - 使用的设计模式和最佳实践
- **扩展指南** - 如何扩展和定制功能
- **最佳实践** - 开发规范和注意事项
**文档目标**:帮助开发者从宏观角度理解整个代码框架,掌握系统设计思想和扩展方法。
---
## 🏗️ 整体架构设计
### 三层架构模型
```
┌─────────────────────────────────────────────────────────────┐
│ 接口层 (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⃣ 接口层依赖
```javascript
// export.js 依赖
export.js
GameController (业务流程控制)
RoomConfigUtils (配置解析)
ErrorMessages (错误消息)
Logger (日志记录)
// import.js 依赖
import.js
无内部依赖 (纯粹封装框架接口)
```
#### 2⃣ 业务层依赖
```javascript
// 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⃣ 数据层依赖
```javascript
// 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** - 牌型分析工厂
#### 实现示例
```javascript
// 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** - 常量定义模块
#### 实现示例
```javascript
// 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通知操作完成
#### 实现示例
```javascript
// 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策略
#### 实现示例
```javascript
// 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** - 适配不同版本配置格式
#### 实现示例
```javascript
// 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处理流程模板
#### 实现示例
```javascript
// 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中定义方法
```javascript
// 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方法
```javascript
// 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: 客户端调用
```javascript
// 客户端代码
SendPack({
cmd: "DoPack",
gameid: "jinxianmahjong",
rpc: "player_swap_card",
data: {
card: "3m"
}
});
```
### 2. 添加新的游戏规则
#### 步骤1: 在RoomConfigUtils中定义规则位
```javascript
// shared/config/RoomConfigUtils.js
RoomConfigUtils.parse = function(roomtype) {
// ... 现有解析逻辑
// 新增规则位解析 (假设使用第14位)
var enableCardSwap = roomtype.charAt(13) === '1';
return {
// ... 现有配置
specialRules: {
enableCardSwap: enableCardSwap, // 新规则
// ... 其他规则
}
};
};
```
#### 步骤2: 在操作验证中使用规则
```javascript
// 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: 创建算法模块
```javascript
// 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中加载模块
```javascript
// mod.js
function loadGameModules() {
// ... 现有模块加载
// 加载新算法模块
require('./shared/algorithms/NewAlgorithm.js');
console.log("[mod_jinxianmahjong] NewAlgorithm.js 加载成功");
// ... 继续加载其他模块
}
```
#### 步骤3: 在业务代码中使用
```javascript
// 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: 创建常量文件
```javascript
// 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中注册
```javascript
// shared/constants/index.js
var NewConstants = require('./NewConstants.js');
module.exports = {
// ... 现有常量
NewConstants: NewConstants
};
```
---
## 📝 最佳实践
### 1. 代码组织原则
#### ✅ 单一职责原则 (SRP)
每个模块只负责一个功能:
```javascript
// ❌ 不好的实践: 一个类做太多事情
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)
对扩展开放,对修改关闭:
```javascript
// ✅ 使用策略模式扩展牌型检测
WinDetectionFactory.registerPattern = function(patternName, checkFunction) {
this.patterns[patternName] = checkFunction;
};
// 添加新牌型无需修改核心代码
WinDetectionFactory.registerPattern('custom_pattern', function(handCards) {
// 自定义牌型检测逻辑
});
```
#### ✅ 依赖倒置原则 (DIP)
依赖抽象而非具体实现:
```javascript
// ✅ 通过接口依赖
GameController.setGameService = function(gameService) {
// gameService可以是任何实现了胡牌检测接口的对象
this.gameService = gameService;
};
```
### 2. 错误处理规范
#### 统一错误返回格式
```javascript
// 所有方法返回统一格式
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统一处理
```javascript
// 使用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. 日志记录规范
#### 关键节点记录日志
```javascript
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. 性能优化建议
#### ✅ 避免重复计算
```javascript
// ❌ 不好: 重复计算
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;
};
```
#### ✅ 减少对象创建
```javascript
// ❌ 不好: 频繁创建临时对象
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);
}
```
#### ✅ 使用对象池
```javascript
// 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. 测试规范
#### 单元测试结构
```javascript
// 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. 命名规范
#### 变量命名
```javascript
// ✅ 好的命名: 清晰表达意图
var handCards = []; // 手牌
var jingInfo = {}; // 精牌信息
var isWinDetected = false; // 是否检测到胡牌
var totalScore = 0; // 总分数
// ❌ 不好的命名: 含义不明
var hc = [];
var ji = {};
var flag = false;
var num = 0;
```
#### 函数命名
```javascript
// ✅ 好的命名: 动词+名词,清晰描述功能
function dealCards(players, dealer) {}
function checkWinPattern(handCards) {}
function calculateJingScore(jingCards) {}
function validateOperation(operation) {}
// ❌ 不好的命名
function deal() {}
function check() {}
function calc() {}
function validate() {}
```
#### 常量命名
```javascript
// ✅ 大写字母+下划线
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"
**原因**: 依赖的模块还未加载就使用了
**解决方案**:
```javascript
// 检查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但未同步更新
**解决方案**:
```javascript
// ✅ 使用GameStateManager统一管理状态
GameStateManager.updatePlayerState(gameState, playerId, {
handCards: newHandCards,
score: newScore
});
// ❌ 直接修改可能导致不一致
gameState.players[playerId].handCards = newHandCards;
gameState.players[playerId].score = newScore; // 可能忘记更新其他相关字段
```
### 问题3: 精牌计算错误
**症状**: 精分计算结果与预期不符
**原因**: 没有正确识别精牌或计算逻辑错误
**解决方案**:
```javascript
// ✅ 使用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或事件监听器
**解决方案**:
```javascript
// ✅ 游戏结束后清理
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未调用
**解决方案**:
```javascript
// ✅ 确保所有分支都调用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](08-游戏流程概述.md) - 完整游戏流程
- **下一篇**: [10-快速入门指南.md](../development/10-快速入门指南.md) - 开发入门指南
- **参考文档**:
- [00-框架基础概述.md](../framework/00-框架基础概述.md) - 框架基础
- [04-游戏核心服务.md](../core/04-游戏核心服务.md) - 核心服务详解
- [05-共享代码模块.md](../core/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个)
1. `makewar` - 创建房间
2. `get_needroomcard` - 获取所需房卡数
3. `get_smallroundnumber` - 获取小局数
4. `get_roomtype` - 获取房间类型
5. `get_deskinfo` - 获取牌桌信息
6. `smallround_prepare` - 小局准备
7. `check_canhuprocess` - 检查胡牌过程
8. `check_canhupai` - 检查能否胡牌
9. `get_hu_describe` - 获取胡牌描述
10. `check_baopai` - 检查报牌
11. `check_baopai_process` - 检查报牌过程
12. `check_canhupai_fanpai` - 检查翻牌胡牌
13. `check_operate_fanpai` - 检查翻牌操作
14. `recieve_match_result` - 接收比赛结果
#### Import接口 (13个)
1. `smallround_finish` - 小局结束
2. `bigroom_finish` - 大局结束
3. `sendcards` - 发牌
4. `send_eachother_operate` - 发送玩家间操作
5. `send_hupai_broadcast` - 广播胡牌
6. `send_gangpai_broadcast` - 广播杠牌
7. `send_message` - 发送消息
8. `save_grade` - 保存战绩
9. `random` - 随机数
10. `get_trusteeship_level` - 获取托管等级
11. `get_ip_address` - 获取IP地址
12. `get_match_config` - 获取比赛配置
13. `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日
**维护者**: 进贤麻将开发团队