forked from datawhale/whale-town-end
* **新增 Zulip 模块**:包含完整的集成服务,涵盖客户端池(client pool)、会话管理及事件处理。 * **新增 WebSocket 网关**:用于处理 Zulip 的实时事件监听与双向通信。 * **新增安全服务**:支持 API 密钥加密存储及凭据的安全管理。 * **新增配置管理服务**:支持配置热加载(hot-reload),实现动态配置更新。 * **新增错误处理与监控服务**:提升系统的可靠性与可观测性。 * **新增消息过滤服务**:用于内容校验及速率限制(流控)。 * **新增流初始化与会话清理服务**:优化资源管理与回收。 * **完善测试覆盖**:包含单元测试及端到端(e2e)集成测试。 * **完善详细文档**:包括 API 参考手册、配置指南及集成概述。 * **新增地图配置系统**:实现游戏地点与 Zulip Stream(频道)及 Topic(话题)的逻辑映射。 * **新增环境变量配置**:涵盖 Zulip 服务器地址、身份验证及监控相关设置。 * **更新 App 模块**:注册并启用新的 Zulip 集成模块。 * **更新 Redis 接口**:以支持增强型的会话管理功能。 * **实现 WebSocket 协议支持**:确保与 Zulip 之间的实时双向通信。
71 lines
3.9 KiB
Markdown
71 lines
3.9 KiB
Markdown
游戏属性: 2d社交属性, 无战斗的社群mmo游戏
|
||
核心目的(游戏内外无缝互通): 不玩这个游戏但是在zulip的社群成员, 也可以跨平台和游戏内的成员聊天
|
||
|
||
核心设计理念:
|
||
Stream (流) -> Topic (话题) 线程模型,天然契合 MMO 中的 Zone (区域) -> Context (情境) 逻辑
|
||
我们需要解决的核心问题是:如何将2D 空间位置(Game State)映射到Zulip 的信息组织形式(Message State),同时利用 Zulip 的 API Key 机制完成无缝认证
|
||
|
||
---
|
||
1. 核心逻辑架构 (The Core Logic)
|
||
在设计 API 之前,我们需要定义 mappings(映射关系):
|
||
- Game World / Map ←→ Zulip Stream (e.g., #Novice_Village)
|
||
- Interactive Object / Event ←→ Zulip Topic (e.g., Notice Board, Tavern Gossip)
|
||
- Whisper / Party ←→ Zulip Private Message
|
||
|
||
---
|
||
架构图示:
|
||
Client (Game) $$\xrightarrow{\text{Game Token}}$$ Game Middleware API $$\xrightarrow{\text{Zulip API Key}}$$ Zulip Server
|
||
$$Client (Godot) \xleftrightarrow{\text{WebSocket}} Node.js Server \xleftrightarrow{\text{REST/Long-Poll}} Zulip Server$$
|
||
设计理由:不建议让客户端直接直连 Zulip。我们需要一层中间件(Middleware)来控制权限、注入游戏数据(如玩家坐标、当前的动作状态),并防止用户在该 API Key 下进行非游戏允许的 Zulip 操作(如随意创建 Stream)。
|
||
|
||
---
|
||
2. 设计思路一: "统一网关"(Unified Gateway)
|
||
2.1 详细数据流设计 (Data Flow)
|
||
我们需要在 Node.js 中维护一个 Session Manager。
|
||
A. 登录与握手 (Initialization)
|
||
1. Godot: 发送登录包 {"type": "login", "token": "user_game_token"}。
|
||
2. Node.js:
|
||
- 验证游戏 Token。
|
||
- 查找该用户的 Zulip API Key(通常存储在数据库中,或者首次登录时让用户提供)。
|
||
- 关键步骤: Node.js 服务器为该特定用户实例化一个 Zulip Client,并向 Zulip 申请注册一个 Event Queue。
|
||
- 将 Socket_ID 与 Zulip_Queue_ID 绑定。
|
||
B. 发送消息 (Upstream: Godot -> Node -> Zulip)
|
||
1. Godot: 玩家输入 "Hello",Godot 通过 WebSocket 发送简化的包:
|
||
2. JSON
|
||
{
|
||
"t": "chat",
|
||
"content": "Hello",
|
||
"scope": "local" // 或者 "topic_name"
|
||
}
|
||
1. Node.js:
|
||
- 收到包,解析出这是聊天请求。
|
||
- 上下文注入: Node 知道玩家当前在 Map_101 (对应 Zulip Stream #Tavern)。
|
||
- API 调用: Node 使用该用户的 Zulip Client,调用 Zulip API 发送消息到 #Tavern。
|
||
- 优势: 这里可以做风控(比如禁止发脏话、频率限制),Godot 端根本无法绕过。
|
||
C. 接收消息 (Downstream: Zulip -> Node -> Godot)
|
||
1. Node.js:
|
||
- 服务器内部有一个循环(或者异步监听器),轮询 Zulip 的事件队列。
|
||
- 收到 Zulip 的 message 事件:User_B 在 #Tavern 说了 "Hi"。
|
||
- 空间过滤: Node 检查当前连接的所有 WebSocket,找出所有位于 Map_101 的玩家。
|
||
- 广播: 将消息打包成游戏协议,通过 WebSocket 推送给这些玩家:
|
||
2. JSON
|
||
{
|
||
"t": "chat_render",
|
||
"from": "User_B",
|
||
"txt": "Hi",
|
||
"bubble": true
|
||
}
|
||
1. Godot: 收到包,直接调用 show_bubble()。
|
||
|
||
---
|
||
2.3 这个方案的权衡分析 (Trade-off Analysis)
|
||
这种改变带来的本质变化:
|
||
优势 (The Wins)
|
||
1. 客户端极度简化 (Thin Client):
|
||
- Godot 里不需要写 HTTP Request,不需要处理 Long Polling 的异常断连,不需要解析复杂的 JSON 结构。
|
||
- Godot 只需要处理 on_websocket_packet_received。
|
||
2. 安全性 (Security):
|
||
- Zulip API Key 永不下发: 用户的 Zulip 凭证永远只保存在服务器端。如果客户端直接拿 Key,黑客可以通过解包 Godot 拿到 Key 然后去 Zulip 乱发消息。
|
||
- 位置欺诈完全消除: 因为 Stream 的选择权在 Node 手里,玩家无法做到“人在新手村,却往高等级区域频道发消息”。
|
||
3. 协议统一:
|
||
- 不再需要处理 HTTP (Zulip) 和 WebSocket (Game) 两种并发逻辑。网络层代码减少一半。 |