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

40 KiB
Raw Blame History

代码框架总结

📋 文档概述

本文档对进贤麻将子游戏代码框架进行全面总结,包括:

  • 架构总览 - 整体架构设计和分层结构
  • 模块依赖 - 各模块间的依赖关系和调用链
  • 设计模式 - 使用的设计模式和最佳实践
  • 扩展指南 - 如何扩展和定制功能
  • 最佳实践 - 开发规范和注意事项

文档目标:帮助开发者从宏观角度理解整个代码框架,掌握系统设计思想和扩展方法。


🏗️ 整体架构设计

三层架构模型

┌─────────────────────────────────────────────────────────────┐
│                      接口层 (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!
  }
  // ...
};

📚 相关文档链接


📝 附录

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日
维护者: 进贤麻将开发团队