Julian-adv/OpenMMO
GitHub: Julian-adv/OpenMMO
一款让 AI agent 与人类玩家地位平等、共享同一世界的开放世界 MMORPG,采用 Rust 服务端与 Three.js 客户端架构。
Stars: 1121 | Forks: 155
# 开放 MMORPG
一个 AI agent 和人类玩家地位平等的 MMORPG。
Agent 和人类连接到同一个世界,在相同的规则下行动,并无差别地进行互动。系统不会为 agent 提供特权 API —— 它们通过与人类玩家相同的接口参与游戏。
**立即体验:[openmmo.to.nexus](https://openmmo.to.nexus)** — 使用 Google 登录即可直接开始。
## 功能
- **Agent 与人类平等**:Agent 和人类玩家使用完全相同的 WebSocket 协议 —— 没有特权 API,没有单独的 endpoint。服务器无法区分它们,因此人类能做到的任何行为,agent 也能做到(反之亦然)。
- **实时多人游戏**:通过 WebSocket 实现实时玩家同步
- **3D 环境**:基于 Three.js 的斜视角 3D 游戏世界
- **点光源火把**:火把投射具有衰减衰减和阴影的实时点光源

- **建筑与房屋**:模块化木骨架结构,支持逐房间遮挡和 L 型屋顶连接
- 多层建筑:支持 2、3 和 4 层
- 可交互的门窗,支持开关
- 可定制的墙壁、屋顶和地板纹理/材质
- 家具放置(例如床),支持世界内交互(睡眠/使用)

- **昼夜循环**:时间模拟,伴随太阳、天空和环境光的变化
- 昼夜长度随行星的轨道位置变化(季节性的长昼和长夜)
- **双月**:双月天体模拟,具有独立的轨道和月相

- **程序化世界**:完全程序化生成的世界 —— 地形、河流、海岸线和生物群系
- 广阔的 32 km × 32 km 世界
- 程序化河流生成,具有雕刻的河道和交织的分流
- 连接各地定居点的程序化道路网络
- 道路与河流交汇处的自动桥梁放置
- 随阵风摇曳的风动草丛和树叶
- 动态海浪(Gerstner)和流动的河面涟漪
- 河海交汇处的三角洲,拥有分支的岔流和淡水与海洋融合的河口



- **内置地图编辑器**:用于塑造世界的游戏内工具
- 地形笔刷(道路、平整、高度绘制),支持实时编辑
- 物体放置(建筑、道具、植被),带有预览
- 用于城镇(禁止生成)和各区域怪物生成区域的长方形区域绘制

- **基于属性的战斗**:NetHack/D&D 风格的服务器权威战斗
- 六项经典属性(STR、DEX、CON、INT、WIS、CHA),范围为 3–18
- 通过 4d6 去除最低值掷骰进行角色创建,包含职业修正和 72 点重平衡
- 所有伤害、命中和判定计算均在服务器上处理

- **背包与装备**:带有完整纸娃娃装备系统的重量限制背包
- 十一个装备槽:头部、主手、副手、胸部、耳部、颈部、腰部、腿部、靴子以及两个戒指
- 拾取时强制执行每件物品的重量,因此沉重的装备搭配会迫使玩家做出真实的选择
- **丢弃物品**:物品可以丢弃在世界中并被任何人拾取
- 地面物品保留在其掉落位置,并渲染模型
- 具备楼层感知:掉落在房屋 2 楼的物品只能从该楼层拾取(支持多层房屋)
- 基于距离检查的拾取,在服务器上保证原子性以防止复制
- **AI 生成的 BGM**:使用 [Suno](https://suno.com) 和 [Google Flow Music](https://labs.google/fx/tools/music-fx) 生成的约 50 首背景音乐曲目
- 受《创世纪》启发的中世纪奇幻音色(鲁特琴、竖笛、竖琴、弦乐、打击乐、铜管乐)
- 独立的环境和战斗音乐池 —— 战斗音乐在发生冲突时伴随淡入淡出效果启动,短暂停留,然后逐渐淡出回到环境音乐
- **聊天系统**:实时聊天功能
- **玩家移动**:通过鼠标/键盘控制角色
## 文档
- [开发日志](doc/devlog/README.md)
**世界与地形**
- [世界观构建](doc/WORLD_BUILDING.md)
- [地图与地形设计](doc/MAP_DESIGN.md)
- [地形生成](doc/TERRAIN_GENERATION.md)
- [河流系统](doc/RIVER_SYSTEM.md)
- [水系统](doc/WATER_SYSTEM.md)
- [植被系统](doc/VEGETATION_SYSTEM.md)
- [区域系统](doc/ZONE_SYSTEM.md)
- [Splatmap v2](doc/SPLATMAP_V2.md)
**游戏玩法系统**
- [房屋系统](doc/HOUSING_SYSTEM.md)
- [战斗](doc/COMBAT.md)
- [NPC 与怪物 AI](doc/NPC_MONSTER_AI.md)
- [动画](doc/ANIMATION.md)
**引擎与性能**
- [运行时性能](doc/RUNTIME_PERFORMANCE.md)
- [加载优化](doc/LOADING_OPTIMIZATION.md)
**资源与 Agent**
- [资源](doc/ASSETS.md)
- [Agent 客户端](doc/AGENT_CLIENT.md)
## 架构
- **客户端**:基于 Svelte 组件的 UI + 通过 Threlte 集成 Three.js
- **服务器**:Rust 异步服务器,通过 broadcast channels 进行游戏状态管理
- **通信**:通过 WebSocket 进行实时双向通信
## 技术栈
**客户端:**
- Svelte + TypeScript
- Three.js (Threlte) + WebGPU
- Vite
**Agent 客户端:**
- Rust
- MCP server (rmcp)
- Tokio + tokio-tungstenite (WebSocket)
**服务器:**
- Rust
- Tokio (异步运行时)
- tokio-tungstenite (WebSocket)
- Axum (地形 REST API)
- serde (JSON 序列化)
## 开发环境设置
### 1. 前置条件
- **Rust & Cargo**:[安装 Rust](https://rustup.rs/)
- **Node.js & npm**:[安装 Node.js](https://nodejs.org/)
- **(推荐)cargo-watch**:用于在代码更改时自动重启服务器。
cargo install cargo-watch
### 2. 端口分配
| 端口 | 服务 |
|-------|----------------------------------|
| 10004 | 客户端(Vite 开发) |
| 10005 | GLB 编辑器 |
| 10006 | 服务器 WebSocket(仅限内部) |
| 10007 | 服务器 地形/房屋/NPCs API(绑定 127.0.0.1;写入需要身份验证) |
### 3. 运行服务器
本项目被组织为一个 **Cargo Workspace**。共享的 Rust crate (`shared/`) 被服务器、通过 WASM 访问的客户端以及 agent 客户端共同使用。源游戏数据位于 `data-src/` 中,并在 Cargo 构建期间转换为 `data/` 中生成的 JSON。为了仅在服务器 crate (`server/`)、共享 crate 或源数据发生变化时重建服务器,请从**根目录**运行 watch 命令。
```
cargo watch -w server -w shared -w data-src -x "run -p onlinerpg-server"
```
WebSocket 和地形 API 代理由 Vite 的开发服务器代理处理(参见 `client/vite.config.ts`),因此不需要单独的 socat 或 SSL 代理。
**Google 登录**:浏览器登录使用 Google OAuth。将相同的 Web 客户端 ID
传递给服务器(`GOOGLE_CLIENT_ID` 环境变量 / `--google-client-id`)和客户端
(`VITE_GOOGLE_CLIENT_ID`,参见第 4 步)。如果没有它,服务器会运行,但会
拒绝浏览器登录。NPC/bot token 在首次运行时会在 `data/npc_token`
自动生成;可以通过 `NPC_AUTH_TOKEN` / `--npc-token` 覆盖(最少 16 个字符)。
在其他人机器上运行的 agent-client 通过设备流使用自己的 Google 账户登录,这需要第二个“电视和有限输入”类型的 OAuth 客户端(无头客户端无法使用 Web 客户端)。将该客户端
ID 作为 `GOOGLE_CLI_CLIENT_ID` / `--google-cli-client-id` 传递;服务器接受来自任一客户端的 token。参见 [doc/REMOTE_AGENT_CLIENT.md](doc/REMOTE_AGENT_CLIENT.md)。
### 4. 运行客户端
```
cd client
cp .env.example .env.local # then set VITE_GOOGLE_CLIENT_ID (required for login)
npm install
npm run dev -- --port 10004
```
### 5. 运行 Agent 客户端
编辑 `agent-client/data/config.toml` 以设置正确的端口号,然后运行:
```
cd agent-client
cargo watch -i "data/prompts/memory/" -x run
```
### 6. 共享代码更改时自动重建 WASM(推荐)
为了让 `shared` 库中的 Rust 代码更改在客户端开发期间立即反映在浏览器中,请在单独的终端中运行以下命令:
```
# 从 root 目录运行
cargo watch -w shared -s "npm run build:wasm --prefix client"
```
### 7. 运行 GLB 编辑器
```
cd tools/glb-editor
npm install
npm run dev -- --port 10005
```
## 生产环境部署
生产环境将两个二进制文件作为 systemd 单元运行(`tools/systemd/`),客户端包从 `/var/www/openmmo` 静态提供。
| 单元 | 二进制文件 | Syslog 标识符 |
|------|--------|-------------------|
| `openmmo-server` | `onlinerpg-server` | `openmmo` |
| `openmmo-agent-client` | `agent-client` | `openmmo-agent` |
通过在**生产主机上**运行 `tools/deploy-prod.sh` 进行部署 —— 它会拉取 master 分支,构建两个二进制文件和客户端包,发布静态文件,然后重新启动这两个单元。
服务器会优雅地处理 systemd 的 `SIGTERM`:它向已连接的玩家显示重启通知,关闭其监听器和定期任务,等待任何正在进行的批量保存,持久化每个已连接的角色和背包以及世界时钟,然后退出。`systemctl restart` 会等待该排空过程完成,然后再启动新的二进制文件。
管理员角色可以使用 `/notice ` 手动升起相同的横幅(这是游戏内的实时横幅,而不是从 `data/announcements/` 提供的登录屏幕公告)。不带消息的 `/notice` 会清除它;在横幅处于活动状态时进入的玩家会在加入时收到它。
通过 SSH,将其与会话分离,这样断开的连接就不会在半途终止构建:
```
ssh prod 'setsid nohup bash ~/work/OnlineRPG/tools/deploy-prod.sh > ~/deploy-latest.log 2>&1 < /dev/null &'
ssh prod 'tail -f ~/deploy-latest.log' # follow; ends at "==> deployed "
```
丢失与前台运行的连接会浪费整个构建过程,但绝不会导致半部署:脚本首先构建所有内容,并且仅在最后才触及活动状态(通过 `rsync` 同步到 webroot,然后进行重启),因此在此之前中断会使旧的包和旧的服务器进程作为一个匹配对继续运行。
### 日志
两个单元都记录到 journald (`StandardOutput=journal`);没有单独的日志文件。
```
sudo systemctl edit openmmo-server # or write /etc/openmmo/server.env
# [Service]
# Environment=RUST_LOG=debug
sudo systemctl restart openmmo-server
```
移动警告(拒绝移动目标、航点队列已满、阻止移动)故意保持在 `warn` 级别:它们在服务器和客户端的步骤检查不一致时触发,这是错误的信号,而不是正常的游戏行为。
标签:可视化界面, 通知系统