-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
1. 项目愿景与核心理念
在当今互联网技术快速发展的背景下,在线多人游戏越来越受到玩家的青睐。本项目旨在构建一个高度可扩展的在线多人卡牌游戏平台,其核心理念是 “万物皆对象”。
在 PocketBase 的技术栈下,这意味着游戏中的所有概念——规则、牌桌、玩家、牌局、甚至每一次出牌——都是数据库中的一个记录。通过这种设计,我们可以实现:
- 规则即配置: 游戏规则不再硬编码,而是作为数据存储。添加新游戏或修改现有游戏规则,只需创建或修改一条记录,无需重新部署核心代码。
- 事件溯源: 游戏的每一个动作都被记录下来,形成一个不可篡改的事件流。这天然支持了防作弊、断线重连、游戏复盘等功能。
- 状态机驱动: 牌桌作为游戏控制器,其状态(等待中、游戏中、已结束)驱动着整个游戏流程,逻辑清晰,易于维护。
2. 技术架构总览
2.1 后端
- 核心: PocketBase
- 功能: 用户认证、数据存储、实时通信、文件管理。
- 扩展: 通过 Go 的 Hooks 和自定义 API 路由实现游戏核心逻辑。
2.2 前端
- 技术: 待定 (建议为 Vue/React/Svelte 等现代框架)
- 原则: 事件驱动,状态同步。客户端主要负责渲染UI和发送用户操作指令,所有核心逻辑判断由服务器完成。
2.3 核心设计模式
- 状态机:
Table记录是总控制器,其status字段(waiting,playing,finished)驱动游戏进程。 - 事件溯源:
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 请求发送到此端点。
工作流程:
- 接收请求: 验证玩家身份和权限。
- 状态检查: 获取当前
game_state,检查是否轮到该玩家。 - 规则验证: 根据
table关联的game_rule,动态加载对应的logic_file(如four_color_card.js),并调用其中的验证函数(如validateChi)。 - 记录动作: 如果合法,在
game_actions集合中创建一条新记录。 - 更新状态: 根据动作更新
game_states记录。 - 广播: 使用 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. 客户端设计原则
- 状态同步: 客户端加入牌桌后,订阅该牌桌的
game_actions实时更新。 - 渲染UI: 客户端本地维护一个游戏状态。收到服务器广播的
game_action后,将其应用到本地状态并刷新UI。 - 用户交互: 玩家操作(如出牌)时,客户端向
/api/game/action发送请求,等待服务器验证和广播。
6. 第一套规则实现:四色牌
6.1 游戏规则说明
- 牌组: {将士象车马炮卒} * {黄,红,绿,白} + {公侯伯子男} (统称“金条”,算红色特殊将)。
- 玩家: 4人。
- 开局: 庄家21张,其他人20张。
- 定庄:
- 首局随机一人翻牌,根据牌面颜色决定,黄=1, 红=2, 绿=3, 白=4点,暨如果翻到牌的颜色是黄色则自己是庄。
- 大胡后,胜利方的对家成为新庄。
- 小胡后,庄家连庄。
- 翻牌: 庄家比其他玩家多一张牌(即第1张牌,随机时翻开的那张牌),这张牌公开。
- 运转规则:
- 玩家打出一张牌。下家选择“响应”或从牌堆翻一张牌。
- 翻牌后,优先检查其他玩家是否有“胡、开、碰”等高优先级操作。若无,翻牌的玩家可以“响应”这张牌。
- 如果玩家最终无法“响应”,则这张翻出的牌成为他打出的牌。
- 响应操作:
- 胡: 手牌和响应牌能组成全部牌组,即可胡牌。
- 开: 手上有三张相同的牌(一坎),再拿到第四张,可“开”,计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": {...} }
- Body:
GET /api/game/state/{tableId}- 获取指定牌桌的最新游戏状态(用于断线重连)。
7.5 实时事件
客户端通过 PocketBase SDK 订阅特定频道来接收实时更新。
tables:{tableId}:actions- 订阅牌桌{tableId}的所有游戏动作。当有新game_action记录创建时,服务器会向此频道推送该记录。
8. 可扩展性论证
此架构设计天然支持扩展:
- 添加新游戏: 只需在
game_rules中新增一条记录,并编写对应的logic_file。核心 API 和数据库结构无需改动。 - 修改现有游戏: 大部分修改(如调整分数、改变牌组)只需编辑
config_json字段。 - 前端复用: 游戏逻辑与 UI 完全分离。前端只需关注如何渲染
game_state和响应用户操作,甚至可以开发一个通用的游戏桌面,动态加载不同游戏的 UI 组件。
9. 开发路线图
-
Phase 1: 核心架构搭建
- 设计并创建 PocketBase 集合 (
game_rules,tables,game_states,game_actions)。 - 实现自定义 API 端点
/api/game/action的基础框架。 - 实现动态加载和执行 JS 逻辑文件的机制。
- 实现牌桌创建、加入、离开、准备等基础功能。
- 设计并创建 PocketBase 集合 (
-
Phase 2: 四色牌规则实现
- 完善
four_color_card.js中的所有逻辑函数(重点是validateHu和validateChi)。 - 完成四色牌的完整游戏流程测试。
- 实现计分和结算逻辑。
- 完善
Metadata
Metadata
Assignees
Labels
No labels