Files
whale-town-end/docs/systems/zulip/guide.md
angjustinl 55cfda0532 feat(zulip): 添加全面的 Zulip 集成系统
* **新增 Zulip 模块**:包含完整的集成服务,涵盖客户端池(client pool)、会话管理及事件处理。
* **新增 WebSocket 网关**:用于处理 Zulip 的实时事件监听与双向通信。
* **新增安全服务**:支持 API 密钥加密存储及凭据的安全管理。
* **新增配置管理服务**:支持配置热加载(hot-reload),实现动态配置更新。
* **新增错误处理与监控服务**:提升系统的可靠性与可观测性。
* **新增消息过滤服务**:用于内容校验及速率限制(流控)。
* **新增流初始化与会话清理服务**:优化资源管理与回收。
* **完善测试覆盖**:包含单元测试及端到端(e2e)集成测试。
* **完善详细文档**:包括 API 参考手册、配置指南及集成概述。
* **新增地图配置系统**:实现游戏地点与 Zulip Stream(频道)及 Topic(话题)的逻辑映射。
* **新增环境变量配置**:涵盖 Zulip 服务器地址、身份验证及监控相关设置。
* **更新 App 模块**:注册并启用新的 Zulip 集成模块。
* **更新 Redis 接口**:以支持增强型的会话管理功能。
* **实现 WebSocket 协议支持**:确保与 Zulip 之间的实时双向通信。
2025-12-25 22:22:30 +08:00

71 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
游戏属性: 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) 两种并发逻辑。网络层代码减少一半。