Skip to content

可扩展式在线多人卡牌游戏平台 #1

@lin594

Description

@lin594

1. 项目愿景与核心理念

在当今互联网技术快速发展的背景下,在线多人游戏越来越受到玩家的青睐。本项目旨在构建一个高度可扩展的在线多人卡牌游戏平台,其核心理念是 “万物皆对象”

在 PocketBase 的技术栈下,这意味着游戏中的所有概念——规则、牌桌、玩家、牌局、甚至每一次出牌——都是数据库中的一个记录。通过这种设计,我们可以实现:

  • 规则即配置: 游戏规则不再硬编码,而是作为数据存储。添加新游戏或修改现有游戏规则,只需创建或修改一条记录,无需重新部署核心代码。
  • 事件溯源: 游戏的每一个动作都被记录下来,形成一个不可篡改的事件流。这天然支持了防作弊、断线重连、游戏复盘等功能。
  • 状态机驱动: 牌桌作为游戏控制器,其状态(等待中、游戏中、已结束)驱动着整个游戏流程,逻辑清晰,易于维护。

2. 技术架构总览

2.1 后端

  • 核心: PocketBase
  • 功能: 用户认证、数据存储、实时通信、文件管理。
  • 扩展: 通过 Go 的 Hooks 和自定义 API 路由实现游戏核心逻辑。

2.2 前端

  • 技术: 待定 (建议为 Vue/React/Svelte 等现代框架)
  • 原则: 事件驱动,状态同步。客户端主要负责渲染UI和发送用户操作指令,所有核心逻辑判断由服务器完成。

2.3 核心设计模式

  1. 状态机: Table 记录是总控制器,其 status 字段(waiting, playing, finished)驱动游戏进程。
  2. 事件溯源: GameAction 集合记录所有操作。GameState 是由这些动作按顺序计算得出的最新快照。

3. 数据库设计

以下是 PocketBase 中的核心集合设计:

3.1 game_rules (游戏规则集合)

定义“怎么玩”,是可扩展性的基石。

字段名 类型 说明
name Text 规则名称,如 "四色牌"。
description Text 规则描述。
config_json JSON 核心。包含所有游戏逻辑配置(牌堆、初始手牌、计分规则等)。
logic_file Text 关联的服务器端逻辑文件名,如 four_color_card.js

3.2 tables (牌桌集合)

游戏控制器,管理玩家和游戏生命周期。

字段名 类型 说明
name Text 牌桌名称。
rule Relation (game_rules) 关联到此牌桌使用的规则。
owner Relation (users) 牌桌创建者。
status Select waiting, playing, finished
players Relation (users, multiple) 当前在牌桌的玩家列表。
player_states JSON 记录每个玩家的准备状态、分数等。
current_game Relation (game_states) 关联到当前进行中的游戏状态记录。
is_private Bool 是否为私密房间。
password Text 房间密码。

3.3 game_states (游戏状态集合)

存储某一局游戏的实时快照。每一局游戏创建一条新记录。

字段名 类型 说明
table Relation (tables) 所属的牌桌。
round_number Number 当前是第几局。
current_player_turn Relation (users) 当前轮到哪个玩家。
player_hands JSON 核心。所有玩家的手牌。{"user_id_1": ["card_id_A", ...]}
deck JSON 牌堆中剩余的牌。
discard_pile JSON 弃牌堆。
last_play JSON 上一次出牌信息。
player_melds JSON 记录每个玩家已公示的牌组(坎、鱼等)。
game_specific_data JSON 万能字段,存储游戏特有状态(如庄家ID、翻牌信息)。

3.4 game_actions (游戏动作集合)

事件溯源的核心,记录每一步操作。

字段名 类型 说明
table Relation (tables) 动作发生的牌桌。
game_state Relation (game_states) 动作发生时的游戏状态。
player Relation (users) 执行动作的玩家。
sequence_number Number 关键。动作的序号,确保顺序。
action_type Select play_cards, chi, peng, kai, hu
action_data JSON 动作的具体数据。
timestamp Auto 动作发生时间。

4. 服务器端逻辑设计

4.1 可插拔游戏引擎

创建一个自定义 API 端点,如 /api/game/action。客户端所有游戏操作都通过 POST 请求发送到此端点。

工作流程:

  1. 接收请求: 验证玩家身份和权限。
  2. 状态检查: 获取当前 game_state,检查是否轮到该玩家。
  3. 规则验证: 根据 table 关联的 game_rule,动态加载对应的 logic_file (如 four_color_card.js),并调用其中的验证函数(如 validateChi)。
  4. 记录动作: 如果合法,在 game_actions 集合中创建一条新记录。
  5. 更新状态: 根据动作更新 game_states 记录。
  6. 广播: 使用 PocketBase 的 Realtime API,向订阅了该 table_id 的所有客户端广播这个新创建的 game_action 记录。

4.2 游戏逻辑文件结构

在 PocketBase 项目根目录下创建 game_logics 文件夹,存放不同游戏的 JS 逻辑文件。

/pb_project
├── /game_logics
│ ├── four_color_card.js
│ ├── doudizhu.js
│ └── …
├── …
└── main.go

5. 客户端设计原则

  1. 状态同步: 客户端加入牌桌后,订阅该牌桌的 game_actions 实时更新。
  2. 渲染UI: 客户端本地维护一个游戏状态。收到服务器广播的 game_action 后,将其应用到本地状态并刷新UI。
  3. 用户交互: 玩家操作(如出牌)时,客户端向 /api/game/action 发送请求,等待服务器验证和广播。

6. 第一套规则实现:四色牌

6.1 游戏规则说明

  • 牌组: {将士象车马炮卒} * {黄,红,绿,白} + {公侯伯子男} (统称“金条”,算红色特殊将)。
  • 玩家: 4人。
  • 开局: 庄家21张,其他人20张。
  • 定庄:
    • 首局随机一人翻牌,根据牌面颜色决定,黄=1, 红=2, 绿=3, 白=4点,暨如果翻到牌的颜色是黄色则自己是庄。
    • 大胡后,胜利方的对家成为新庄。
    • 小胡后,庄家连庄。
  • 翻牌: 庄家比其他玩家多一张牌(即第1张牌,随机时翻开的那张牌),这张牌公开。
  • 运转规则:
    1. 玩家打出一张牌。下家选择“响应”或从牌堆翻一张牌。
    2. 翻牌后,优先检查其他玩家是否有“胡、开、碰”等高优先级操作。若无,翻牌的玩家可以“响应”这张牌。
    3. 如果玩家最终无法“响应”,则这张翻出的牌成为他打出的牌。
  • 响应操作:
    • : 手牌和响应牌能组成全部牌组,即可胡牌。
    • : 手上有三张相同的牌(一坎),再拿到第四张,可“开”,计6点。
    • : 手上有两张相同的牌,再拿到第三张,可“碰”,计1点。
    • : 只能吃上家的牌。响应牌+手牌能组成特定牌组。
  • 牌组定义:
    • : 3张同色同字,计3点。(金条坎计9点)
    • : 4张同色同字,计8点。(金条鱼计24点)
    • 吃的组合:
      • 同色:车马炮、将士象,计1点。
      • 异色卒:3张不同色卒计1点,4张不同色卒计2点。
      • 单张:将计1点,金条计3点。
  • 胡牌计分:
    • 小胡: 无“鱼”或“开”。得分 = (基础分3点+吃+碰+未开的坎)。
    • 大胡: 有“鱼”或“开”。得分 = (基础分3点+吃+碰+未开的坎 + 开+鱼) * 2。
    • 结算: 赢家向其他三人收取分数。其他玩家之间根据“坎、开、鱼”互相结算。
    • 流局: 牌库剩8张时流局,庄家连庄。

6.2 配置与实现方案

game_rules 记录

  • name: "四色牌"
  • logic_file: "four_color_card.js"
  • config_json: (见下文)

config_json 设计

{
“meta”: { “player_count”: { “min”: 4, “max”: 4 }, “deck_type”: “four_color_custom” },
“setup”: {
“initial_cards”: { “dealer”: 21, “others”: 20 },
“determine_dealer_function”: “determineDealer”,
“reveal_bonus_card_function”: “revealBonusCard”
},
“turn”: { “order”: “clockwise”, “actions_allowed”: [“play”, “respond”, “draw”] },
“validation”: {
“play_function”: “validatePlay”, “chi_function”: “validateChi”,
“peng_function”: “validatePeng”, “kai_function”: “validateKai”,
“hu_function”: “validateHu”, “win_condition_function”: “checkWinCondition”
},
“scoring”: { “score_function”: “calculateFinalScores” },
“custom_data”: {
“deck_definition”: {
“suits”: [“yellow”, “red”, “green”, “white”],
“ranks”: [“将”, “士”, “象”, “车”, “马”, “炮”, “卒”],
“special_rank”: { “name”: “jin_tiao”, “cards”: [“公”, “侯”, “伯”, “子”, “男”], “color”: “red” }
},
“meld_definitions”: {
“kan”: { “name”: “坎”, “size”: 3, “points”: 3 },
“yu”: { “name”: “鱼”, “size”: 4, “points”: 8 },
“kai”: { “name”: “开”, “points”: 6 }, “peng”: { “name”: “碰”, “points”: 1 },
“chi”: { “name”: “吃”, “points”: 1 }
},
“chi_patterns”: [
{ “type”: “sequence”, “ranks”: [“车”, “马”, “炮”], “points”: 1 },
{ “type”: “sequence”, “ranks”: [“将”, “士”, “象”], “points”: 1 },
{ “type”: “soldier_diff_3”, “points”: 1 }, { “type”: “soldier_diff_4”, “points”: 2 },
{ “type”: “single_jiang”, “points”: 1 }, { “type”: “single_jin_tiao”, “points”: 3 }
],
“scoring_rules”: {
“jin_tiao_kan_multiplier”: 3, “jin_tiao_yu_multiplier”: 3,
“da_hu_multiplier”: 2, “liuju_deck_limit”: 8
},
“action_priorities”: [“hu”, “kai”, “peng”, “chi”]
}
}

four_color_card.js 逻辑文件结构

此文件需导出 config_json 中指定的所有函数,如 determineDealer, validateChi, calculateFinalScores 等。例如,validateChi 函数会检查玩家是否只能吃上家,以及其组合是否符合 chi_patterns 中的任一模式。


7. API 接口设计

为确保前后端分离及未来前端的可复用性,设计如下 RESTful API。

7.1 认证

  • POST /api/users/login - 用户登录
  • POST /api/users/register - 用户注册
  • POST /api/users/logout - 用户登出

7.2 游戏大厅

  • GET /api/collections/rules/records - 获取所有可用的游戏规则列表。
  • GET /api/collections/tables/records - 获取所有公开的牌桌列表(可按规则筛选)。
  • POST /api/collections/tables/records - 创建一个新的牌桌(需传入 rule_id 等信息)。

7.3 房间管理

  • POST /api/tables/{tableId}/join - 加入指定牌桌。
  • POST /api/tables/{tableId}/leave - 离开指定牌桌。
  • POST /api/tables/{tableId}/ready - 玩家切换准备状态。

7.4 游戏内逻辑

  • POST /api/game/action - 核心接口。发送游戏动作。
    • Body: { "table_id": "...", "action_type": "chi", "action_data": {...} }
  • GET /api/game/state/{tableId} - 获取指定牌桌的最新游戏状态(用于断线重连)。

7.5 实时事件

客户端通过 PocketBase SDK 订阅特定频道来接收实时更新。

  • tables:{tableId}:actions - 订阅牌桌 {tableId} 的所有游戏动作。当有新 game_action 记录创建时,服务器会向此频道推送该记录。

8. 可扩展性论证

此架构设计天然支持扩展:

  1. 添加新游戏: 只需在 game_rules 中新增一条记录,并编写对应的 logic_file。核心 API 和数据库结构无需改动。
  2. 修改现有游戏: 大部分修改(如调整分数、改变牌组)只需编辑 config_json 字段。
  3. 前端复用: 游戏逻辑与 UI 完全分离。前端只需关注如何渲染 game_state 和响应用户操作,甚至可以开发一个通用的游戏桌面,动态加载不同游戏的 UI 组件。

9. 开发路线图

  1. Phase 1: 核心架构搭建

    • 设计并创建 PocketBase 集合 (game_rules, tables, game_states, game_actions)。
    • 实现自定义 API 端点 /api/game/action 的基础框架。
    • 实现动态加载和执行 JS 逻辑文件的机制。
    • 实现牌桌创建、加入、离开、准备等基础功能。
  2. Phase 2: 四色牌规则实现

    • 完善 four_color_card.js 中的所有逻辑函数(重点是 validateHuvalidateChi)。
    • 完成四色牌的完整游戏流程测试。
    • 实现计分和结算逻辑。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions