22 KiB
22 KiB
Net-Games 网游系统架构文档
版本: v2.0
更新日期: 2026-04-09
状态: 生产就绪
1. 系统概述
1.1 项目定位
基于 Flask-SocketIO 的多人实时棋牌游戏引擎,支持人类玩家与 AI 玩家对战。采用权威服务端架构,所有游戏逻辑在服务端验证执行。
1.2 核心特性
- ✅ 权威服务端架构(防作弊)
- ✅ 用户认证系统(登录/注册/积分)
- ✅ 多角色支持:人类、规则AI、DeepSeek大模型AI
- ✅ 房间制对战(2-3人)
- ✅ AI托管机制(断线/超时自动托管)
- ✅ 回合超时管理(30秒/回合)
- ✅ 断线重连支持
- ✅ 单点登录控制
- ✅ 可扩展的游戏插件机制
1.3 技术栈
| 层级 | 技术 | 说明 |
|---|---|---|
| 服务端 | Python 3.11 + Flask | Web框架 |
| 实时通信 | Flask-SocketIO | WebSocket封装 |
| 数据库 | SQLite | 用户认证、积分存储 |
| 前端 | 原生HTML/JS/CSS | 无需构建 |
| AI接口 | DeepSeek API | 大模型决策 |
| 部署 | Systemd | 系统服务 |
2. 目录结构
/home/ubuntu/nanocode/net-games/
├── server.py # 主服务入口 (1772行)
├── config.py # 全局配置
├── net_games_auth.db # SQLite数据库
│
├── engine/ # 游戏引擎核心
│ ├── __init__.py
│ ├── room_manager.py # 房间管理器 (391行)
│ ├── game_base.py # 游戏基类 (236行)
│ ├── player.py # 玩家抽象 (154行)
│ ├── ai_adapter.py # AI适配器基类
│ └── test_game.py # 测试游戏
│
├── doudizhu/ # 斗地主游戏实现
│ ├── __init__.py
│ ├── game.py # 斗地主游戏逻辑 (470行)
│ ├── game_logic.py # 牌型判断、规则
│ └── ai_adapters.py # 斗地主AI适配器 (443行)
│
├── chinese-chess/ # 中国象棋(预留)
│ └── ...
│
├── client/ # 斗地主客户端
│ ├── index.html
│ ├── style.css
│ └── game.js # 前端逻辑 (1617行)
│
├── hub/ # 游戏大厅
│ └── index.html
│
├── docs/ # 文档
│ ├── ARCHITECTURE.md # 本文件
│ ├── API.md # API接口文档
│ ├── DATABASE.md # 数据库设计
│ ├── UI_DESIGN_GUIDELINES.md # UI设计规范
│ └── PHASE1_SUMMARY.md # 阶段总结
│
└── instance/ # 数据目录
└── net_games_auth.db
3. 系统架构
3.1 整体架构图
┌─────────────────────────────────────────────────────────────────┐
│ 客户端层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 浏览器玩家 │ │ 浏览器玩家 │ │ AI玩家 │ │
│ │ (Socket.IO)│ │ (Socket.IO)│ │ (内部调用) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
└─────────┼────────────────┼────────────────┼────────────────────┘
│ │ │
└────────────────┴────────────────┘
│ WebSocket (Socket.IO)
▼
┌─────────────────────────────────────────────────────────────────┐
│ 服务层 (Flask) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ HTTP Routes │ │
│ │ /api/auth/* /api/user/* /api/rooms/* /api/admin/* │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Socket.IO Handlers │ │
│ │ connect create_room join_room game_action ... │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ RoomManager (单例) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Room #1 │ │ Room #2 │ │ Room #N │ │ │
│ │ │ 斗地主 │ │ 斗地主 │ │ 象棋 │ │ │
│ │ │ 3玩家 │ │ 2玩家 │ │ 2玩家 │ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │
│ └───────┼────────────┼────────────┼───────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Game Instances │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ DoudizhuGame │ │ TestGame │ ... │ │
│ │ │ - game logic │ │ - game logic │ │ │
│ │ │ - state mgmt │ │ - state mgmt │ │ │
│ │ └──────┬───────┘ └──────────────┘ │ │
│ └─────────┼───────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ AI Adapter Center │ │
│ │ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ RuleAI │ │ DeepSeekAI │ │ MinimaxAI │ │ │
│ │ │ (斗地主) │ │ (通用) │ │ (象棋预留) │ │ │
│ │ └──────────┘ └──────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Database (SQLite) │ │
│ │ ┌─────────┐ ┌─────────┐ │ │
│ │ │ users │ │ rooms │ (内存缓存) │ │
│ │ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
3.2 核心组件职责
| 组件 | 文件 | 职责 |
|---|---|---|
| Flask App | server.py |
HTTP服务、Socket.IO、认证、API路由 |
| RoomManager | engine/room_manager.py |
单例模式管理所有房间生命周期 |
| Room | engine/room_manager.py |
房间状态、玩家管理、游戏实例容器 |
| GameBase | engine/game_base.py |
游戏抽象基类,定义通用接口 |
| Player | engine/player.py |
玩家基类,HumanPlayer/AIPlayer |
| AIAdapter | engine/ai_adapter.py |
AI决策接口统一封装 |
| DoudizhuGame | doudizhu/game.py |
斗地主具体实现 |
3.3 关键数据流
3.3.1 游戏操作处理流程
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │ │ Server │ │ Room │ │ Game │
└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ game_action │ │ │
│ ──────────────>│ │ │
│ │ handle_game_action │
│ │ ──────────────────────────────>│
│ │ │ │
│ │ │ game.handle_action()
│ │ │ │
│ │ │ │
│ │ │ 验证操作合法性 │
│ │ │ 更新游戏状态 │
│ │ │ │
│ │ state_update │ │
│ <──────────────│ (broadcast) │ │
│ │ │ │
│ │ check_winner() │ │
│ │ ──────────────────────────────>│
│ │ │ │
│ game_ended │ │ │
│ <──────────────│ (broadcast) │ │
3.3.2 AI决策流程
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Game │ │ Server │ │ AI Thread│ │ AIAdapter│
└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ get_current_player() │ │
│ ───────────────────────────────>│ │
│ │ │ │
│ │ process_ai_turn│ │
│ │ ──────────────>│ │
│ │ │ make_decision()│
│ │ │ ──────────────>│
│ │ │ │
│ │ │ 异步API调用 │
│ │ │ (DeepSeek) │
│ │ │ <──────────────│
│ │ │ │
│ │ │ handle_action()│
│ │ │ ──────────────>│
│ │ state_update │ │
│ │ (broadcast) │ │
4. 核心类设计
4.1 RoomManager (单例模式)
class RoomManager:
_instance = None
_lock = Lock()
def __new__(cls):
# 线程安全的单例
def create_room(game_type, max_players, config)
def join_room(room_code, player) -> (bool, str)
def start_game(room_id) -> (bool, str)
def get_room(room_id=None, room_code=None) -> Room
def cleanup_inactive_rooms(max_inactive_minutes=30)
4.2 Room 类
class Room:
id: str # room_uuid
code: str # A1B2C3 (6位房间号)
game_type: str # doudizhu
status: str # waiting/playing/ended/closed
players: Dict[str, Player]
seats: Dict[int, Optional[str]]
host_id: Optional[str]
game: Optional[GameBase]
def add_player(player) -> bool
def remove_player(player_id) -> bool
def start_game(game_class) -> bool
def to_dict(include_game_state=True) -> dict
4.3 GameBase 抽象基类
class GameBase(ABC):
room_id: str
game_type: str
state: GameState
players: Dict[str, Player]
player_order: List[str]
@abstractmethod
def handle_action(player_id, action, data) -> dict
@abstractmethod
def get_valid_actions(player_id) -> List[str]
@abstractmethod
def check_winner() -> Optional[str]
def to_dict() -> dict
4.4 Player 类层次
class Player(ABC):
id: str
name: str
type: PlayerType
is_connected: bool
is_ai_controlled: bool # AI托管状态
offline_since: Optional[float]
def connect(socket_id)
def disconnect()
def enable_ai_control()
def disable_ai_control()
class HumanPlayer(Player):
@property
def is_ai(self) -> False
class AIPlayer(Player):
ai_type: str # rule/deepseek/minimax
@property
def is_ai(self) -> True
5. 关键机制
5.1 单点登录与身份识别
# 使用用户名或昵称作为身份标识
identity_key = current_user.username if current_user.is_authenticated else player_name
# 活跃玩家映射
active_usernames: Dict[str, str] = {} # username -> player_id
# 用户-房间映射(支持跨浏览器重连)
user_room_map: Dict[str, str] = {} # username -> room_id
# Socket-玩家映射
socket_player_map: Dict[str, str] = {} # socket_id -> player_id
单点登录检查流程:
- 创建房间/加入房间时检查
identity_key是否在active_usernames - 如果存在且玩家在线,拒绝新连接
- 如果存在但玩家离线,允许重连并替换
- 断开连接时从
active_usernames移除
5.2 AI托管机制
触发条件:
- 玩家主动断开连接(关闭浏览器)→ 30秒后自动托管
- 回合超时(30秒无操作)→ 累计3次后自动托管
- 玩家主动点击"托管"按钮
托管流程:
def ai_control_after_timeout(room_id, player_id, timeout=30):
time.sleep(timeout)
if player.offline_since is None:
return # 已重连,取消托管
player.enable_ai_control()
# 通知所有玩家
emit('player_ai_controlled', {...})
# 如果是当前回合,立即触发AI
if current_turn == player_id:
check_and_trigger_ai(room_id)
取消托管:
- 玩家重连时自动取消托管
- 玩家手动点击"取消托管"
5.3 回合超时管理
# 配置
TURN_TIMEOUT_SECONDS = 30 # 每回合30秒
MAX_TIMEOUT_COUNT = 3 # 最大超时3次
# 定时器管理
room_turn_timers: Dict[str, Timer] = {} # room_id -> Timer
player_timeout_count: Dict[str, int] = {} # player_id -> count
def start_turn_timer(room_id):
cancel_turn_timer(room_id)
timer = threading.Timer(TURN_TIMEOUT_SECONDS, handle_turn_timeout, args=[room_id])
room_turn_timers[room_id] = timer
def handle_turn_timeout(room_id):
player_timeout_count[player_id] += 1
if timeout_count >= MAX_TIMEOUT_COUNT:
player.enable_ai_control() # 启用托管
else:
_execute_auto_pass(room, game, player_id) # 自动不要
5.4 断线重连机制
重连匹配优先级:
- 已登录用户通过
username匹配 - 通过
player_id匹配(保存在localStorage) - 通过
player_name匹配(已断开状态的玩家)
重连流程:
1. 客户端尝试 reconnect_room
2. 服务端通过 user_room_map 或 player_name 查找原玩家
3. 单点登录检查(确保没有其他活跃连接)
4. player.connect(new_socket_id) - 更新连接状态
5. player.disable_ai_control() - 取消托管
6. 加入 Socket.IO room
7. 广播 player_reconnected 事件
8. 恢复游戏状态
5.5 房间清理策略
def cleanup_inactive_rooms(max_inactive_minutes=30):
for room in rooms:
all_disconnected = all(not p.is_connected for p in room.human_players)
inactive_time = now - room.last_activity
if all_disconnected:
if room.status == 'ended' and inactive_time > 5*60:
cleanup_room(room) # 已结束,5分钟后清理
elif inactive_time > 10*60:
cleanup_room(room) # 进行中,10分钟后清理
elif inactive_time > max_inactive_minutes*60:
cleanup_room(room) # 不活跃,30分钟后清理
清理任务每5分钟运行一次(后台线程)。
6. 游戏扩展机制
6.1 添加新游戏的步骤
- 创建游戏目录
newgame/
├── __init__.py
├── game.py # 继承 GameBase
├── game_logic.py # 游戏规则
└── ai_adapters.py # AI适配器
- 实现 GameBase 子类
class NewGame(GameBase):
@property
def min_players(self) -> int: return 2
@property
def max_players(self) -> int: return 2
def handle_action(self, player_id, action, data) -> dict:
# 实现游戏逻辑
pass
def check_winner(self) -> Optional[str]:
# 检查获胜者
pass
- 注册游戏类型 (server.py)
from newgame import NewGame
room_manager.register_game('newgame', NewGame)
6.2 AI适配器接口
class AIAdapter(ABC):
@abstractmethod
async def make_decision(self, game_state, available_actions, player_context):
return {
"action": "play",
"data": {"card_ids": [...]},
"reasoning": "决策理由"
}
7. 部署与运维
7.1 服务配置
# /etc/systemd/system/net-games.service
[Unit]
Description=Net-Games Server
After=network.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/nanocode/net-games
Environment=NET_GAMES_HOST=0.0.0.0
Environment=NET_GAMES_PORT=8900
Environment=NET_GAMES_DEBUG=false
Environment=DEEPSEEK_API_KEY=xxx
ExecStart=/usr/bin/python3 /home/ubuntu/nanocode/net-games/server.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
7.2 端口规划
| 服务 | 端口 | 说明 |
|---|---|---|
| Net-Games | 8900 | 网游系统(本服务) |
| Happy-Games | 8895 | 单机小游戏 |
| Personal-Blog | 8888/8889 | 博客系统 |
7.3 监控指标
- 在线房间数 (
/api/stats) - 在线玩家数
- AI调用次数/成本
- 平均游戏时长
8. 安全设计
8.1 认证安全
- 密码使用 werkzeug 哈希存储
- Session cookie 设置 HttpOnly, SameSite
- Flask-Login 管理登录状态
8.2 游戏安全
- 权威服务端:所有操作服务端验证
- 客户端仅展示,不执行游戏逻辑
- AI API Key 仅在服务端使用
8.3 防作弊
- 牌面仅发送给对应玩家(player_view)
- 操作合法性服务端验证
- 单点登录防止多开
9. 更新日志
| 日期 | 版本 | 变更内容 |
|---|---|---|
| 2026-04-09 | v2.0 | 更新为实际实现版本,添加认证系统、AI托管、超时管理、断线重连 |
| 2026-04-08 | v1.0 | 初始设计文档,基础架构设计 |
10. 参考文档
- API.md - 详细接口文档
- DATABASE.md - 数据库设计
- UI_DESIGN_GUIDELINES.md - UI设计规范
文档版本: v2.0
最后更新: 2026-04-09
维护者: 开发团队