欺骗酒馆 - 多人联机架构设计文档参考
04/02104 浏览开发心得
1. 项目概览
多人社交欺骗卡牌游戏。玩家轮流出牌并声称牌型,其他玩家可质疑;质疑失败者面对俄罗斯轮盘赌,中弹即淘汰,最后存活者获胜。
本游戏因为出现了QKA牌,所以不允许上架,所以不要做任何和棋牌沾边的游戏,连花色和点数都不要出现,否则很可能就是心血白费了。

游戏主页面

对局页面
网络架构: 常驻服(Persistent World),服务端长期运行,管理多个并发房间。
完整目录结构scripts/
├── main.lua # 入口:检测模式,加载 Client 或 Server
├── config/
│ ├── Settings.lua # 游戏规则、牌组、房间状态枚举、网络事件、UI 配置
│ └── NamePool.lua # 随机昵称池(50 个酒馆风格名字,不放回抽取)
├── network/
│ ├── Shared.lua # 双端共享:事件注册、场景创建、JSON 工具
│ ├── Server.lua # 服务端入口:连接管理、匹配调度、房间生命周期
│ └── Client.lua # 客户端入口:UI 初始化、网络事件处理、操作发送
├── server/
│ ├── MatchQueue.lua # 匹配队列:FIFO 排队 + AI 超时补位
│ ├── Room.lua # 房间状态机:多回合对局管理、AI 调度
│ ├── GameRules.lua # 纯游戏逻辑:洗牌、发牌、质疑判定、左轮机制
│ └── ai/
│ ├── AIPlayer.lua # AI 入口:串联五层架构,暴露 decidePlay/decideChallenge
│ ├── StateLayer.lua # 第 1 层:生成只读局面快照
│ ├── ProbabilityLayer.lua# 第 2 层:纯数学概率计算
│ ├── StrategyLayer.lua # 第 3 层:4 种 AI 人格策略
│ ├── PsychologyLayer.lua # 第 4 层:行为修饰(延迟、犹豫)
│ └── MemoryLayer.lua # 第 5 层:跨轮记忆(当前为空壳实现)
└── client/
├── ScreenManager.lua # 界面状态管理:大厅/游戏/结算切换
├── LobbyScreen.lua # 大厅 UI:匹配按钮、在线统计
├── GameScreen.lua # 游戏 UI:手牌、操作按钮、表情、左轮动画
├── ResultScreen.lua # 结算 UI:胜负展示
├── CardRenderer.lua # NanoVG 手牌扇形渲染
├── TableRenderer.lua # NanoVG 牌桌和座位渲染
├── AnimationManager.lua # 动画工具
└── AudioManager.lua # 音频管理:BGM、音效
2. 常驻服架构
配置: max_players: 100, persistent_world.enabled: true。服务端 headless 持续运行。
入口分发 (main.lua): IsServerMode() → 加载 network/Server;否则加载 network/Client。
服务端全局状态:connections_: connKey → 玩家信息(connection, userId, nickname, room, seat)rooms_: roomId → Room 实例(独立状态机)MatchQueue: 单例匹配队列
主循环每帧执行: (1) 每 2s 广播大厅统计 → (2) 更新匹配队列 → (3) 更新所有房间,清理已结束的房间。
3. 客户端-服务端通信
使用引擎 Remote Event + VariantMap,复杂数据用 cjson 序列化。双端启动时统一注册事件,客户端要注册客户端和服务端都有的事件,服务端同理,才能通信。
客户端 → 服务端 (9 个): ClientReady, RequestMatch, RequestSolo, CancelMatch, PlayCards(Cards+ClaimCount), Challenge, ReadyNext, LeaveRoom, SendEmoji(EmojiKey)
服务端 → 客户端 (19 个): LobbyStats, MatchQueued, MatchFound, GameStart, DealHand(单播), TurnStart, CardsPlayed, ChallengeStart, ChallengeResult, PlayerPenalty, PlayerEliminated, UpdateCardCount, PlayerSafe, RevolverResult, GameOver, PlayerJoined, PlayerLeft, TimerSync, ErrorMsg, EmojiBroadcast
连接生命周期: 连接 → ClientIdentity → ClientReady → 游戏交互 → Disconnected(从队列/房间清理)。AI 以 "ai_" 前缀 connKey 标识,无 connection 对象。
4. 匹配队列
FIFO 队列。RequestMatch → 入队 → 人数 >= 4 时取出前 4 人创建房间。
AI 超时补位: 排队 15 秒未满员 → 自动生成 AI 补足缺额 → 触发匹配。计时器在入队/匹配/补位/清空时重置。
单人对局: RequestSolo → 立即创建房间(1 人 + 3 AI),AI 昵称从预设池随机选取。
5. 房间管理与多回合
状态机

房间创建
匹配成功 → 分配座位 → NamePool 抽取昵称 → 初始化左轮(6弹仓随机1发) → AI 座位分配随机人格 → 广播 MatchFound → StartGame。
回合流程
StartNewRound: 洗牌(20张: 6K+6Q+6A+2Joker) → 发牌(每人≤5张) → 随机声明牌型(K/Q/A) → 广播 GameStart + 单播 DealHand → DEALING → TURN_ACTIVE。
TURN_ACTIVE: 广播 TurnStart → AI 走 TryAIPlay();等待出牌/质疑/超时(随机出1张)。
出牌后 NextTurn: 手牌空 → 标记 safe → 检查是否只剩1人有牌(该人进左轮);否则轮转到下一个 alive 且非 safe 的座位。
质疑结算: RESOLVING → 判定说谎(任何牌 ≠ 声明类型且 ≠ Joker) → 输家进左轮 → 3s 后 AfterResolve。
左轮: 扣扳机(shots++ == bulletPos → 中弹) → 广播结果 → 7s 动画 → 中弹淘汰则检查胜负;存活则从该座位开始新一轮。
胜负判定: 只剩1人 alive → 获胜;只剩1人有牌 → 该人进左轮。
断线处理: 从队列/房间移除 → 标记 alive=false → 广播 PlayerLeft → 若当前出牌者断线则 NextTurn → 检查是否只剩1人。
6. AI 系统
生成场景: 匹配超时补位("ai_{id}") 或 单人对局("ai_solo_{id}")。
五层决策架构
1. StateLayer: 生成只读局面快照(手牌分类、公开信息,不偷看他人手牌)
2. ProbabilityLayer: 纯数学 — 评估出牌可信度/风险;基于手牌推算对方说谎概率
3. StrategyLayer: 结合人格参数做决策,含 10% 随机扰动和 8% 随机翻转
4. PsychologyLayer: 行为修饰 — 5~10s 延迟、犹豫标记(不改变决策本身)
5. MemoryLayer: 跨轮记忆
四种人格: balanced(均衡)、cautious(谨慎/多真牌少质疑)、aggressive(激进/频繁质疑)、bluffer(骗术师/大量说谎快速清牌)。房间创建时随机分配。
调度机制: AI 回合时设置延迟回调(≥1.5s),执行前验证状态防止中途中断。



