Files
youlegames/codes/games/server/docs/guides/development/10-快速入门指南.md
2026-02-04 23:47:45 +08:00

873 lines
22 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.
# 快速入门指南
## 📋 文档概述
本文档为进贤麻将子游戏提供快速入门指南,帮助开发者:
- **快速理解** - 10分钟理解项目核心结构
- **快速定位** - 知道去哪里找需要的代码
- **快速上手** - 掌握常见开发任务的实现方法
- **快速解决** - 查找常见问题的解决方案
**文档目标**: 让新开发者能在**2小时内**理解项目结构并完成第一个功能修改。
---
## 🚀 10分钟快速理解
### 核心概念速览
#### 1⃣ 三文件架构 (最重要!)
```
mod.js → 模块入口,加载所有文件
export.js → 框架调用游戏的14个接口
import.js → 游戏调用框架的13个接口
```
**记住**:
- 框架要执行游戏逻辑 → 调用 `export` 接口
- 游戏要通知客户端 → 调用 `import` 接口
#### 2⃣ 请求处理流程
```
客户端发送操作
packet.js 路由到 mod.js
RpcHandler 处理请求
GameController 执行逻辑
import 发送响应给客户端
```
#### 3⃣ 核心目录结构
```javascript
games2/jinxianmahjong/
mod.js, export.js, import.js // 三文件架构
rpc/RpcHandler.js // 客户端请求处理(34个RPC方法)
game/
GameController.js // 游戏流程控制(开始、发牌、结算)
OperationManager.js // 操作管理(吃碰杠胡验证)
MahjongGameService.js // 胡牌检测和计分
shared/
algorithms/ // 算法(精牌、胡牌检测、计分)
dataStructures/ // 数据结构(牌对象、状态管理)
config/ // 配置解析
constants/ // 常量定义(12个模块)
utils/ // 工具(Logger、ErrorHandler)
```
### 关键文件速查表
| 需求 | 去哪里找 | 核心文件 |
|------|---------|----------|
| **客户端请求如何处理?** | `rpc/` | RpcHandler.js |
| **游戏怎么开始的?** | `game/` | GameController.js |
| **怎么判断能否胡牌?** | `shared/algorithms/` | WinDetectionFactory.js |
| **精牌怎么计算的?** | `shared/algorithms/` | JingAlgorithm.js |
| **分数怎么算的?** | `shared/algorithms/` | ScoreCalculation.js |
| **游戏状态怎么管理?** | `shared/dataStructures/` | GameStateManager.js |
| **房间配置怎么解析?** | `shared/config/` | RoomConfigUtils.js |
| **如何发送消息给客户端?** | 根目录 | import.js |
| **框架如何调用游戏?** | 根目录 | export.js |
---
## 📖 学习路径
### 路径1: 新手入门 (推荐顺序)
```
第1步: 理解架构
阅读 00-框架基础概述.md
理解三文件架构和RPC机制
预计时间: 30分钟
第2步: 理解接口
阅读 01-Export接口说明.md
阅读 02-Import接口说明.md
理解框架与游戏的交互
预计时间: 30分钟
第3步: 理解流程
阅读 08-游戏流程概述.md
理解游戏从开始到结束的完整流程
预计时间: 40分钟
第4步: 动手实践
完成一个简单的功能修改
(例如: 添加日志、修改分数)
预计时间: 20分钟
```
**总计**: 2小时入门
### 路径2: 算法研究 (进阶开发者)
```
1. 阅读 05-共享代码模块.md (核心算法详解)
2. 研究 JingAlgorithm.js (精牌系统)
3. 研究 WinDetectionFactory.js (胡牌检测)
4. 研究 ScoreCalculation.js (计分系统)
```
### 路径3: 功能开发 (实战导向)
```
1. 阅读 03-RPC处理机制.md (理解请求处理)
2. 阅读 04-游戏核心服务.md (理解服务层)
3. 阅读 09-代码框架总结.md (理解架构设计)
4. 开始功能开发
```
---
## 💡 常见开发任务
### 任务1: 添加新的RPC方法
**场景**: 需要处理客户端的新操作请求
**步骤**:
```javascript
// 1. 在 rpc/RpcHandler.js 中添加方法
RpcHandler.prototype.player_new_operation = function(pack, room, callback) {
var playerId = pack.playerid;
var data = pack.data;
// 验证操作
var validation = this._validateOperation(room.gameState, playerId, data);
if (!validation.valid) {
return callback({ error: validation.error });
}
// 执行操作
var result = this._performOperation(room.gameState, playerId, data);
// 广播消息
room.import.broadcast(room, {
type: 'new_operation',
playerId: playerId,
data: result
});
callback({ success: true, data: result });
};
// 2. 在 mod.js 中注册RPC方法
mod_jinxianmahjong.player_new_operation = function(pack) {
var room = this.getRoom(pack.roomcode);
return this.rpcHandler.player_new_operation(pack, room, function(result) {
return result;
});
};
```
**相关文档**: [03-RPC处理机制.md](../framework/03-RPC处理机制.md)
---
### 任务2: 修改计分规则
**场景**: 需要调整某种牌型的分数
**步骤**:
```javascript
// 在 shared/algorithms/ScoreCalculation.js 中修改
ScoreCalculation.calculateWinScore = function(winResult, gameState) {
var scores = {
baseScore: 0,
bonusScore: 0,
jingScore: 0,
totalScore: 0
};
// 修改牌型分数
var pattern = winResult.bestPattern;
switch (pattern.type) {
case 'pinghu':
scores.baseScore = pattern.hasJing ? 4 : 8;
break;
case 'qidui':
scores.baseScore = pattern.hasJing ? 8 : 64;
break;
case 'custom_pattern': // 新增牌型
scores.baseScore = 16;
break;
// ...
}
// 计算精分
scores.jingScore = JingAlgorithm.calculateJingScore(
gameState.players[winResult.winnerId].handCards,
gameState.jingInfo
);
// 总分
scores.totalScore = scores.baseScore + scores.bonusScore + scores.jingScore;
return scores;
};
```
**相关文档**: [05-共享代码模块.md](../core/05-共享代码模块.md#scorecalculation-计分系统)
---
### 任务3: 添加新的游戏规则
**场景**: 需要支持新的房间配置选项
**步骤**:
```javascript
// 1. 在 shared/config/RoomConfigUtils.js 中添加解析
RoomConfigUtils.parse = function(roomtype) {
// ...现有解析逻辑
// 新增规则位解析(假设使用第14位)
var enableNewRule = roomtype.charAt(13) === '1';
return {
// ...现有配置
specialRules: {
enableNewRule: enableNewRule, // 新规则
// ...其他规则
}
};
};
// 2. 在游戏逻辑中使用规则
// game/OperationManager.js
OperationManager.validateOperation = function(gameState, operation) {
// 检查新规则
if (gameState.rules.specialRules.enableNewRule) {
// 执行特殊验证逻辑
}
// ...其他验证
};
```
**相关文档**: [06-规则配置系统.md](../core/06-规则配置系统.md)
---
### 任务4: 添加日志和调试信息
**场景**: 需要追踪代码执行流程
**步骤**:
```javascript
var Logger = require('./utils/Logger.js');
// 在任何需要调试的地方添加日志
function someFunction(param) {
Logger.info('函数开始执行', { param: param });
try {
// 业务逻辑
Logger.debug('中间状态', { state: currentState });
// 更多逻辑
Logger.info('操作成功', { result: result });
return result;
} catch (error) {
Logger.error('操作失败', {
error: error.message,
stack: error.stack
});
throw error;
}
}
// 查看日志历史
var history = Logger.getHistory();
console.log('最近100条日志:', history.slice(-100));
// 查看日志统计
var stats = Logger.getStatistics();
console.log('错误数量:', stats.ERROR);
```
**相关文档**: [07-工具模块.md](07-工具模块.md#logger-日志管理器)
---
### 任务5: 添加新的牌型检测
**场景**: 需要支持新的胡牌牌型
**步骤**:
```javascript
// 在 shared/algorithms/WinDetectionFactory.js 中添加
WinDetectionFactory.detectWin = function(handTiles, jingInfo, gameState) {
var patterns = [];
// ...现有牌型检测
// 添加新牌型检测
var customPatternResult = this._checkCustomPattern(handTiles, jingInfo);
if (customPatternResult.canWin) {
patterns.push({
type: 'custom_pattern',
name: '自定义牌型',
hasJing: customPatternResult.hasJing,
baseScore: 32,
description: '特殊组合牌型',
combinations: customPatternResult.combinations
});
}
return {
canWin: patterns.length > 0,
patterns: patterns,
bestPattern: this._selectBestPattern(patterns)
};
};
// 实现检测逻辑
WinDetectionFactory._checkCustomPattern = function(handTiles, jingInfo) {
// 实现牌型检测算法
// 返回 { canWin: boolean, hasJing: boolean, combinations: [...] }
};
```
**相关文档**: [05-共享代码模块.md](../core/05-共享代码模块.md#windetectionfactory-胡牌检测工厂)
---
## 🔍 代码导航技巧
### 技巧1: 快速查找功能入口
```javascript
// 想知道某个操作是如何处理的?
// 1. 找到客户端发送的RPC方法名,例如: "player_discard"
// 2. 在 rpc/RpcHandler.js 中搜索该方法
// 3. 阅读该方法的实现
// 示例: 查找出牌操作
// → 搜索 "player_discard"
// → 找到 RpcHandler.prototype.player_discard
// → 理解处理流程
```
### 技巧2: 追踪数据流
```javascript
// 数据流追踪顺序:
// 1. 客户端发送 → packet.js
// 2. 路由到模块 → mod.js
// 3. RPC处理 → rpc/RpcHandler.js
// 4. 业务逻辑 → game/GameController.js 或其他服务
// 5. 状态更新 → shared/dataStructures/GameStateManager.js
// 6. 响应发送 → import.js → 客户端接收
```
### 技巧3: 查找算法实现
```javascript
// 想知道某个算法是如何实现的?
// 1. 确定算法类型(精牌/胡牌检测/计分等)
// 2. 到 shared/algorithms/ 目录查找
// 3. 查看对应的算法文件
// 示例算法文件:
// - JingAlgorithm.js → 精牌相关算法
// - WinDetectionFactory.js → 胡牌检测
// - ScoreCalculation.js → 计分计算
// - PatternFactory.js → 牌型分析
```
### 技巧4: 理解配置选项
```javascript
// 想知道某个规则位的含义?
// 1. 打开 shared/config/RoomConfigUtils.js
// 2. 查看 parse() 方法
// 3. 找到对应位的解析代码
// 或者查看 shared/constants/RoomConstants.js
// 查看所有规则的定义和说明
```
---
## ❓ 常见问题解答 (FAQ)
### Q1: 如何查看游戏当前状态?
**A**: 游戏状态保存在 `room.gameState` 对象中
```javascript
// 在任何有 room 对象的地方
console.log('当前阶段:', room.gameState.phase);
console.log('当前玩家:', room.gameState.currentPlayer);
console.log('庄家:', room.gameState.dealer);
console.log('精牌信息:', room.gameState.jingInfo);
// 查看玩家手牌
var player = room.gameState.players[0];
console.log('玩家0手牌:', player.handCards);
console.log('玩家0分数:', player.score);
```
**相关文档**: [04-游戏核心服务.md](../core/04-游戏核心服务.md#gamestate-游戏状态对象)
---
### Q2: 如何判断一个操作是否合法?
**A**: 使用 `OperationManager` 进行验证
```javascript
var OperationManager = require('./game/OperationManager.js');
// 验证出牌操作
var validation = OperationManager.validateDiscard(
gameState,
playerId,
tileCode
);
if (!validation.valid) {
console.error('操作不合法:', validation.error);
return { error: validation.error };
}
// 验证吃牌操作
var validation = OperationManager.validateChow(
gameState,
playerId,
tiles
);
```
**相关文档**: [04-游戏核心服务.md](../core/04-游戏核心服务.md#operationmanager-操作管理器)
---
### Q3: 精牌系统是如何工作的?
**A**: 精牌系统由 `JingAlgorithm.js` 实现
```javascript
var JingAlgorithm = require('./shared/algorithms/JingAlgorithm.js');
// 1. 确定精牌
var jingInfo = JingAlgorithm.determineJingCards(
flippedCard, // 翻开的牌
diceResult // 骰子结果
);
// 返回: { zhengJing: '5m', fuJing: '6m' }
// 2. 计算精分
var jingScore = JingAlgorithm.calculateJingScore(
handCards,
jingInfo
);
// 3. 检查是否比精
var biJingResult = JingAlgorithm.checkBiJing(
winnerCards,
loserCards,
jingInfo
);
```
**相关文档**: [05-共享代码模块.md](../core/05-共享代码模块.md#jingalgorithm-精牌算法)
---
### Q4: 如何发送消息给客户端?
**A**: 使用 `import` 接口
```javascript
// 1. 发送给单个玩家
room.import.sendToPlayer(playerId, {
cmd: 'game_update',
data: { /* ... */ }
});
// 2. 广播给所有玩家
room.import.broadcast(room, {
cmd: 'player_operation',
data: { /* ... */ }
});
// 3. 广播给除某玩家外的其他玩家
room.import.broadcastExcept(room, excludePlayerId, {
cmd: 'player_discard',
data: { /* ... */ }
});
```
**相关文档**: [02-Import接口说明.md](../framework/02-Import接口说明.md)
---
### Q5: gameState 和 room 有什么区别?
**A**:
- **room**: 框架层的房间对象,包含玩家列表、房间配置等
- **gameState**: 游戏层的状态对象,包含游戏逻辑相关的数据
```javascript
// room 对象(框架层)
room.roomcode // 房间号
room.roomtype // 房间配置
room.players // 玩家列表(框架格式)
// gameState 对象(游戏层)
room.gameState.phase // 游戏阶段
room.gameState.players // 玩家状态(游戏格式)
room.gameState.jingInfo // 精牌信息
room.gameState.currentPlayer // 当前玩家
// gameState 保存在 room 对象中
// 通过 room.gameState 访问
```
---
### Q6: 如何添加错误处理?
**A**: 使用 `ErrorHandler` 统一处理
```javascript
var ErrorHandler = require('./utils/ErrorHandler.js');
try {
// 可能出错的代码
someRiskyOperation();
} catch (error) {
// 记录错误
ErrorHandler.handle(error, {
context: 'someRiskyOperation',
severity: 'high',
playerId: playerId,
roomcode: roomcode
});
// 返回错误信息
return {
success: false,
error: 'OPERATION_FAILED',
message: error.message
};
}
// 查看错误统计
var stats = ErrorHandler.getStatistics();
console.log('错误分类统计:', stats.byCategory);
```
**相关文档**: [07-工具模块.md](07-工具模块.md#errorhandler-错误处理器)
---
### Q7: 如何理解游戏阶段(phase)?
**A**: 游戏有7个主要阶段
```javascript
// 游戏阶段常量
GAME_PHASES = {
WAITING: 'waiting', // 等待玩家准备
DEALING: 'dealing', // 发牌阶段
JING_DETERMINING: 'jing_determining', // 确定精牌
PLAYING: 'playing', // 游戏进行中
RESPONDING: 'responding', // 等待玩家响应
ROUND_END: 'round_end', // 单局结束
GAME_END: 'game_end' // 游戏结束
};
// 状态转换示例
// WAITING → DEALING → JING_DETERMINING → PLAYING → ROUND_END
```
**相关文档**: [08-游戏流程概述.md](../architecture/08-游戏流程概述.md#游戏状态机)
---
### Q8: 如何修改房间配置解析?
**A**: 修改 `RoomConfigUtils.parse()` 方法
```javascript
// shared/config/RoomConfigUtils.js
RoomConfigUtils.parse = function(roomtype) {
// roomtype 是13位字符串,例如: "1311111110000"
// 解析各位配置
var config = {
roundCount: this._parseRoundCount(roomtype[0]),
useJing: roomtype[1] === '1',
allowChiPengGang: roomtype[2] === '1',
// ...更多配置
};
return config;
};
```
**相关文档**: [06-规则配置系统.md](../core/06-规则配置系统.md)
---
### Q9: 如何追踪某个牌的来源?
**A**: 使用 `MahjongCard``sourceInfo` 属性
```javascript
// 每张牌都有来源信息
var card = {
code: '3m',
uniqueId: 'card_1234567890',
sourceInfo: {
sourceType: 'dealt', // dealt/drawn/discarded等
playerId: 'player1',
timestamp: 1234567890,
round: 1
}
};
// 检查牌的来源
if (card.sourceInfo.sourceType === 'dealt') {
console.log('这张牌是发牌时获得的');
} else if (card.sourceInfo.sourceType === 'drawn') {
console.log('这张牌是摸牌时获得的');
}
```
**相关文档**: [05-共享代码模块.md](../core/05-共享代码模块.md#mahjongcard-麻将牌对象)
---
### Q10: 如何查看所有可用的常量?
**A**: 查看 `shared/constants/index.js`
```javascript
var Constants = require('./shared/constants/index.js');
// 12个常量模块
console.log(Constants.GameConstants); // 游戏常量
console.log(Constants.OperationTypes); // 操作类型
console.log(Constants.ErrorMessages); // 错误消息
console.log(Constants.RoomConstants); // 房间常量
console.log(Constants.ScoreConstants); // 分数常量
console.log(Constants.TimeoutConstants); // 超时常量
// ... 更多常量模块
// 使用常量
var GAME_PHASES = Constants.GameConstants.GAME_PHASES;
var ERROR_INVALID_OPERATION = Constants.ErrorMessages.INVALID_OPERATION;
```
**相关文档**: [06-规则配置系统.md](../core/06-规则配置系统.md#常量系统)
---
## 📚 进阶学习资源
### 完整文档列表
#### 框架基础
1. [00-框架基础概述.md](../framework/00-框架基础概述.md) - 友乐平台架构和ES5规范
2. [01-Export接口说明.md](../framework/01-Export接口说明.md) - 框架调用游戏的14个接口
3. [02-Import接口说明.md](../framework/02-Import接口说明.md) - 游戏调用框架的13个接口
4. [03-RPC处理机制.md](../framework/03-RPC处理机制.md) - 客户端请求处理流程
#### 核心服务
5. [04-游戏核心服务.md](../core/04-游戏核心服务.md) - GameController等5大核心服务
6. [05-共享代码模块.md](../core/05-共享代码模块.md) - 精牌算法等核心算法详解
7. [06-规则配置系统.md](../core/06-规则配置系统.md) - 房间配置和规则解析
#### 架构设计
8. [08-游戏流程概述.md](../architecture/08-游戏流程概述.md) - 完整游戏流程和状态机
9. [09-代码框架总结.md](../architecture/09-代码框架总结.md) - 架构设计和设计模式
#### 开发工具
10. [07-工具模块.md](07-工具模块.md) - Logger和ErrorHandler使用指南
### 外部参考资料
- `docs/important/game/进贤麻将规则手册.md` - 完整游戏规则
- `docs/important/game/进贤麻将技术设计要点.md` - 技术设计细节
- `docs/important/server/服务器子游戏开发要求.md` - 开发规范
- `docs/analysis/JingAlgorithm核心算法分析.md` - 精牌算法深度分析
---
## 🎯 学习检查清单
完成以下检查项,确保你已经掌握了基础知识:
### 基础理解 ✅
- [ ] 理解三文件架构(mod.js/export.js/import.js)
- [ ] 知道RPC请求的处理流程
- [ ] 了解游戏的7个阶段
- [ ] 知道gameState对象的结构
- [ ] 理解精牌系统的基本概念
### 代码定位 ✅
- [ ] 能快速找到RPC方法的实现位置
- [ ] 知道算法文件在哪个目录
- [ ] 知道如何查看游戏配置的解析
- [ ] 能找到日志和错误处理的工具类
### 实践能力 ✅
- [ ] 能添加一条日志语句
- [ ] 能修改一个计分规则
- [ ] 能添加一个简单的RPC方法
- [ ] 能查看和理解gameState的内容
### 问题解决 ✅
- [ ] 遇到错误知道如何查看日志
- [ ] 知道如何验证操作是否合法
- [ ] 知道如何发送消息给客户端
- [ ] 知道去哪里查找算法实现
---
## 💪 实战练习
### 练习1: 添加一条日志 (难度: ⭐)
**任务**: 在玩家出牌时添加日志记录
```javascript
// 在 rpc/RpcHandler.js 的 player_discard 方法中
RpcHandler.prototype.player_discard = function(pack, room, callback) {
var Logger = require('../utils/Logger.js');
// 添加这行日志
Logger.info('玩家出牌', {
playerId: pack.playerid,
tile: pack.data.tile,
roomcode: room.roomcode
});
// ...原有代码
};
```
### 练习2: 修改七对分数 (难度: ⭐⭐)
**任务**: 将有精七对的分数从8分改为10分
```javascript
// 在 shared/algorithms/ScoreCalculation.js 中找到
case 'qidui':
scores.baseScore = pattern.hasJing ? 8 : 64; // 原来
// 修改为
case 'qidui':
scores.baseScore = pattern.hasJing ? 10 : 64; // 修改后
```
### 练习3: 添加操作验证 (难度: ⭐⭐⭐)
**任务**: 添加一个验证,禁止玩家在特定情况下出某张牌
```javascript
// 在 game/OperationManager.js 中
OperationManager.validateDiscard = function(gameState, playerId, tileCode) {
// 原有验证...
// 添加自定义验证
if (gameState.rules.specialRules.forbidSpecialCard) {
if (tileCode === '1m') { // 禁止出1万
return {
valid: false,
error: 'FORBIDDEN_CARD',
message: '当前规则禁止出这张牌'
};
}
}
return { valid: true };
};
```
---
## 🔗 快速链接
### 核心文件直达
- [mod.js](../../games2/jinxianmahjong/mod.js) - 模块入口
- [export.js](../../games2/jinxianmahjong/export.js) - Export接口
- [import.js](../../games2/jinxianmahjong/import.js) - Import接口
- [RpcHandler.js](../../games2/jinxianmahjong/rpc/RpcHandler.js) - RPC处理器
- [GameController.js](../../games2/jinxianmahjong/game/GameController.js) - 游戏控制器
### 算法文件直达
- [JingAlgorithm.js](../../games2/jinxianmahjong/shared/algorithms/JingAlgorithm.js) - 精牌算法
- [WinDetectionFactory.js](../../games2/jinxianmahjong/shared/algorithms/WinDetectionFactory.js) - 胡牌检测
- [ScoreCalculation.js](../../games2/jinxianmahjong/shared/algorithms/ScoreCalculation.js) - 计分系统
### 工具文件直达
- [Logger.js](../../games2/jinxianmahjong/utils/Logger.js) - 日志工具
- [ErrorHandler.js](../../games2/jinxianmahjong/utils/ErrorHandler.js) - 错误处理
---
## 📞 获取帮助
### 遇到问题时的步骤
1. **查看日志**: 检查Logger输出,了解程序执行流程
2. **查看文档**: 在对应的文档中查找相关说明
3. **搜索代码**: 在项目中搜索相关关键词
4. **查看示例**: 参考其他类似功能的实现
5. **调试代码**: 添加日志语句追踪执行流程
### 文档导航
- **概念不清楚** → 查看框架基础文档(00-03)
- **不知道怎么实现** → 查看核心服务文档(04-06)
- **不理解流程** → 查看游戏流程文档(08)
- **想了解设计** → 查看架构总结文档(09)
---
**文档版本**: v1.0
**最后更新**: 2025年10月15日
**维护者**: 进贤麻将开发团队
---
## 🎉 开始你的开发之旅!
现在你已经掌握了快速入门的所有知识,可以开始实际开发了!
**建议的第一步**:
1. 选择一个感兴趣的功能
2. 找到对应的代码位置
3. 添加一些日志了解执行流程
4. 尝试做一个小修改
5. 测试验证你的修改
**记住**:
- 📖 遇到问题先查文档
- 🔍 善用搜索找代码
- 📝 多写日志帮助理解
- 🧪 改完代码记得测试
祝你开发愉快! 🚀