- 版权类型
- 原创
- 语言支持
- 中文(简体)
- 前置组件
- Levilamina-Rust-Loader v1.0.0+(https://www.minebbs.com/resources/levilamina-rust-loader-levilamina-rust.17046/ )
- 适配版本(基岩)
- 26.x
[MD]
# Atlas-plus
一个**实时**的 Minecraft **基岩版专用服务器**俯视地图,以
[LeviLamina](https://github.com/LiteLDev/LeviLamina) Rust 插件的形式实现。
[/MD]
[MD]
Atlas-plus 用两种方式渲染世界,并把它们融合在一起:
- **磁盘引导(bootstrap)**——启用时,读取一次世界的 LevelDB(严格只读),把整个已探索的世界渲染成底图。
- **实时层**——之后直接从正在运行的服务器内存中读取方块,**不必等待存档**,持续保持有变化的区域是最新的,并显示实时玩家标记。
地图由一个内置的小型 Web 服务器提供。**没有任何数据是走普通 HTTP 拉取的**:浏览器只打开一条 WebSocket 连接,地图清单、玩家坐标、瓦片图像变化时都由服务器主动推送下去——所以没有轮询,浏览器也没有任何东西可以缓存成旧数据。瓦片仍然会以和磁盘版 [Atlas] 相同的格式写入磁盘,所以任何读取 Atlas 瓦片的工具都能读这些文件。
> **对 TPS 友好是设计原则。** 实时层每个周期扫描的区块数有硬性上限,默认只关心每个玩家附近的几个区块,加上被世界变化事件标记的区块。画面静止时不产生任何额外开销。
---
## 特性
- 🗺️ **磁盘完整底图** + 玩家所在处的**实时更新**。
- ⚡ **开销有界**——无论哪种更新模式,每周期扫描的区块数都有一个硬上限。
- 🎛️ **可配置的更新策略**:就近玩家、世界变化事件、两者结合、全范围扫描,或关闭。
- 🖼️ **Minecraft 风格的 canvas 地图查看器**——自包含的单页地图,只推像素,不会出现空白帧。
- 🧭 平移 / 缩放 / 网格 / 维度切换 / 出生点标记 / 实时玩家标记。
- 🔌 **一切实时数据都走同一条 WebSocket**——地图清单、玩家坐标、**以及瓦片图像**,变化时都通过 `/ws` 推下去(瓦片以二进制 PNG 帧的形式)。没有 HTTP 轮询,浏览器也从不通过 HTTP 拉取瓦片,所以根本没有东西可以被缓存成旧版本。连接会自动重连,重连后会立刻收到一次完整的当前状态回放。
- 🛠️ **管理员命令**——`/atlas url | status`。
- 📦 零外部依赖服务——手写的 HTTP + WebSocket 服务器,原生 JS 查看器,前端无需构建步骤。
---
## 环境要求
- 一台 LeviLamina 基岩版服务器。
- LeviLamina 的 **Rust 加载器**插件(`levilamina-rust-loader`),用于加载 Rust `.dll` 插件。
- 若要从源码构建:需要 Rust 工具链(stable)以及能访问 crates.io 的网络。
---
## 安装
**方式 A——直接使用编译好的文件。** 把 `atlas_plus.dll` 和 `manifest.json` 放进服务器 `mods/` 下的某个文件夹(或你的加载器识别 Rust 插件的位置),与 Rust 加载器放在一起,然后启动服务器。
**方式 B——从源码构建。**
```bash
git clone https://github.com/Maskviva/Atlas-plus
cd Atlas-plus
cargo build --release
# → target/release/atlas_plus.dll (Windows /你的 BDS 所在平台)
```
把 `target/release/atlas_plus.dll` 和 `manifest.json` 一起打包分发。`manifest.json` 声明了对
`levilamina-rust-loader` 的依赖,所以加载器必须已安装。
首次运行时,Atlas-plus 会写出一份填满默认值的 `plugins/Atlas-plus/config.json`。修改它之后,重启插件(或重启服务器)即可生效。
---
## 快速开始
1. 安装好插件后启动服务器。
2. 在控制台留意 `Atlas-plus live map: http://<host>:8899/` 这一行。
3. 打开这个地址。页面会立刻通过 WebSocket 建立连接;底图会在后台从磁盘流式加载进来,玩家附近的区域会随你观察实时更新。
4. 游戏内用 `/atlas status` 查看状态,或修改 `config.json` 后重启来调整行为。
---
## `/atlas` 命令
`atlas` 是 Atlas-plus 的管理命令(任何玩家都能执行,它只用于查看状态)。
| 命令 | 作用 |
| --- | --- |
| `/atlas url` | 打印实时地图的访问地址。 |
| `/atlas status` | 显示在线玩家数、启用的维度、更新模式、渲染版本号,以及地图地址。 |
配置的修改会在插件下一次启用时生效(重载插件或重启服务器)——**没有**运行时修改配置的命令。
---
## 配置项
`plugins/Atlas-plus/config.json`——首次运行时会写出默认值。未识别的键会被忽略。
### Web 服务器
| 字段 | 默认值 | 说明 |
| --- | --- | --- |
| `http_bind` | `"0.0.0.0"` | Web 服务器(以及 WebSocket)绑定的网卡地址。 |
| `http_port` | `8899` | 地图地址为 `http://<host>:<port>/`;实时数据的连接地址为 `ws://<host>:<port>/ws`。 |
| `http_workers` | `3` | 极轻量的 HTTP 工作线程数;2–4 就够用。每条打开的 WebSocket 连接会在这个线程池之外单独起一个线程。 |
| `out_dir` | `"plugins/Atlas-plus/map"` | 瓦片 / `map.json` / 查看器页面写到磁盘的位置(保留是为了和 Atlas 瓦片格式兼容,也方便你自己额外用别的方式提供服务;内置查看器本身**不会**读取这里的文件——它走 WebSocket)。 |
| `world_name` | `"Atlas-plus (live)"` | 显示在查看器里的世界名。 |
### 渲染范围
| 字段 | 默认值 | 说明 |
| --- | --- | --- |
| `dimensions` | `[0]` | `0` 主世界,`1` 下界,`2` 末地。 |
| `render_radius_chunks` | `6` | 每个玩家周围的实时方形范围——**仅在 `update_mode: "full"` 时生效**。 |
| `pinned_areas` | `[]` | 始终保持刷新的区域,例如出生点:`[{ "dim":0, "x":0, "z":0, "radius_chunks":6 }]`。请用 `tickingarea` 让这些区块保持加载。 |
### 磁盘引导(启用时执行一次,在后台进行)
| 字段 | 默认值 | 说明 |
| --- | --- | --- |
| `bootstrap` | `true` | 读取磁盘上的世界并渲染已探索的地图。**只读**——绝不写入数据库。 |
| `world_dir` | `null` | 世界文件夹(包含 `db/`、`level.dat`)。`null` = 从 `server.properties` 自动检测。 |
| `bootstrap_radius_chunks` | `null` | 把引导范围限制在出生点 + pinned 区域周围的这个区块半径内。`null` = 整个世界。超大世界建议设置,以控制解码时的内存占用。 |
| `bootstrap_throttle_ms` | `10` | 每批 region 之间的暂停时间,让引导过程平缓地进行。 |
### 实时更新策略
| `update_mode` | 行为 |
| --- | --- |
| `"nearby+events"`(默认) | 每个玩家**最近的 4 个区块**保持刷新,加上任何被世界变化事件触碰到的区块。 |
| `"nearby"` | 只刷新每个玩家最近的 4 个区块。 |
| `"events"` | 只刷新被事件触碰到的区块(空闲时几乎零开销)。 |
| `"full"` | 持续轮转刷新每个玩家周围整个 `render_radius_chunks` 方形范围(开销最大)。 |
| `"off"` | 不进行实时扫描(仅有引导底图 + 玩家标记)。 |
**覆盖的事件:** 玩家**放置 / 破坏 / 交互**方块,以及**火焰蔓延**——这些是加载器暴露出来的世界变化事件。TNT 等爆炸、生物破坏地形、红石都**不会**触发加载器事件;实际使用中这些情况大多发生在玩家附近,默认模式里 `nearby` 的那一半会顺带覆盖到结果。无论哪种模式,剩余的扫描预算都会缓慢轮转覆盖 pinned 区域。
### 扫描节奏(服务器线程开销)
| 字段 | 默认值 | 说明 |
| --- | --- | --- |
| `chunks_per_cycle` | `6` | 每个周期扫描区块数的**硬上限**,对所有模式都生效。是控制 TPS 影响的主要旋钮。 |
| `cycle_interval_ms` | `250` | 周期之间的间隔。如果感觉到卡顿(tick lag)就调大它。 |
### 扫描深度 / 正确性
| 字段 | 默认值 | 说明 |
| --- | --- | --- |
| `slab_height` | `16` | 每次 `scan_region` 调用扫描的纵向切片高度。 |
| `scan_top` / `scan_bottom` | `null` | 覆盖扫描的 Y 范围(对所有维度生效)。把 `scan_top` 设成你实际建筑的最高高度是提速最明显的单项设置——但超过这个高度的东西不会显示。 |
| `use_height_cache` | `true` | 记住每个区块表面的高度,让重复扫描从接近顶部的位置开始。建议保持开启。 |
| `rebuild_headroom` | `16` | 在缓存的顶部高度之上再向上多扫这么多格,让小的新建筑无需完整重扫就能显示。 |
| `full_rescan_every` | `16` | 每扫描第 N 次就强制对该区块做一次全范围重扫(用于捕捉非常高的新建筑)。 |
### 渲染器
| 字段 | 默认值 | 说明 |
| --- | --- | --- |
| `flush_interval_ms` | `1000` | 两次瓦片刷新之间的最小间隔(防抖)——同时也是变化的瓦片被推送到连接上的频率。玩家标记的更新独立于此,更新更快。 |
| `max_regions` | `48` | 保留在内存中的 512×512 region 缓冲区数量上限(超出后按最久未用淘汰)。每个约 2 MB,用来控制内存占用。被淘汰的瓦片会在淘汰前先刷新到磁盘和连接上。 |
---
## 工作原理
启用时,一个后台线程会用一个小型的纯 Rust 只读读取器(SST 表 + WAL 重放,Mojang 的原始 deflate 格式)读取磁盘上的 LevelDB,计算每个已探索区块的表面,按 region 分组、节流地把它们发给渲染器,作为底图。
LeviLamina 的每个回调都运行在服务器线程上,所以实时扫描循环用 `schedule_after` 在服务器线程上自我调度节奏,并直接调用 `scan_region`(这是读取实时方块唯一安全的方式)。每个周期它会根据 `update_mode` 决定要重扫哪些区块,最多扫描 `chunks_per_cycle` 个区块,并把表面数据交给一个**后台渲染器**线程。实时扫描器已经画过的区块,不会被(更旧的)引导数据覆盖。没有被加载的区块,通过 API 读到的会是空气;Atlas-plus 会检测出"全部是空气"的扫描结果,遇到这种情况会保留已有像素而不是把它们擦除。
渲染器持有像素缓冲区,防抖之后会对发生变化的 region 做地形阴影处理、把它们的 PNG 瓦片写入磁盘(用于和 Atlas 瓦片格式兼容)——而真正让浏览器既不闪烁、又不可能被缓存耍的关键在于:**把刚好变化的那些瓦片以二进制帧的形式推送到 WebSocket 上**(一个包含维度/缩放级别/瓦片坐标的小头部,后面跟着原始 PNG 字节)。更新后的地图清单,以及玩家移动时的坐标,都以 JSON 文本帧的形式走同一条连接推送下去。浏览器根本没有任何 HTTP 请求可以从缓存里应付过去——瓦片像素抵达页面的唯一途径,就是通过一条打开的连接被主动推送。一个刚连上(或重新连上)的浏览器会立刻收到一次完整状态的回放:当前清单、玩家列表,以及服务器当前持有的每一张瓦片,一次性追上进度,而不用等下一次变化发生。
**瓦片规格:** 缩放级别 0 是每个 32×32 区块的 region 对应一张 512×512 的图像(瓦片坐标 = region 坐标,1 像素 = 1 格方块);更高的缩放级别是 2×2 降采样。磁盘上 `out_dir` 里的副本使用和磁盘版 Atlas 相同的 `tiles/<维度>/<缩放级别>/<rx>_<rz>.png` 目录结构,方便与其他工具兼容——但内置查看器本身从不通过 HTTP 把它们读回来。
---
## 查看器
查看器是一个自包含的、Minecraft 风格的世界地图页面,served 在你的 `out_dir` 根目录(`index.html` + `map-viewer.js`)。在任意浏览器中打开 `http://<host>:<port>/` 即可——无需构建步骤,无外部资源依赖。页面加载后会打开 `ws://<host>:<port>/ws`,并通过这一条连接接收地图清单、玩家坐标,以及每一张瓦片图像;瓦片帧用 `createImageBitmap` 解码,直接画到 `<canvas>` 上。连接断开时会自动重连(有短暂的退避延迟),重连后会收到一次完整当前状态的回放。它也可以通过 `<iframe>` 指向被服务的 `index.html`,嵌入到别的页面里。
---
## 性能调优
- **服务器感觉卡顿?** 调低 `chunks_per_cycle` 和/或调高 `cycle_interval_ms`(这个上限在所有模式下都生效)。把 `scan_top` 设成你实际建筑的最高高度;保持 `use_height_cache` 开启。默认的 `update_mode` 已经很轻量——`"events"` 在空闲时几乎零开销,`"off"` 则完全停止实时扫描。
- **变化显示得太慢?** 调高 `chunks_per_cycle` 和/或调低 `cycle_interval_ms`。在 `"full"` 模式下,也可以调低 `render_radius_chunks`。还可以调低 `flush_interval_ms`,让变化的瓦片更快推送到浏览器。
- **世界很大,引导过程吃内存?** 设置 `bootstrap_radius_chunks`(例如 `128`)。
- **高层建筑偶尔在顶部缺失?** 调低 `full_rescan_every`,或调高 `rebuild_headroom`(也可以直接设置明确的 `scan_top`)。
---
## 说明与已知限制
- 基于加载器安全层 `levilamina-rust-loader` API 编写(v1.0.0 / ABI v4)。如果加载器版本不同导致无法编译或加载,请对齐版本。
- 有意**没有**设置 `panic = "abort"`——加载器会用 `catch_unwind` 包裹插件入口点,如果设置了 abort,一次 panic 会直接带崩整个服务器。
- 磁盘引导读取的是服务器正在打开使用的数据库。对数据库本身是安全的(只读),但如果存档正好在读取过程中落地,可能导致这次读取失败;这种情况会被记录到日志,地图会转为仅靠实时层继续填充。
- WebSocket 的握手和帧编解码是手写的一个小型 RFC 6455 实现(加载器本身不提供 WebSocket 支持,没有现成的可以依赖)——没有使用外部 crate,也没有 TLS。如果你需要 `wss://`,请把它放在反向代理后面。
- 与 Mojang 或 Microsoft 无关联。
---
## 构建
```bash
cargo build --release
```
依赖项:`levilamina-rust-loader`(通过 git 引入的 Rust 加载器 crate)、`image`(仅 PNG 功能)、`serde` /
`serde_json`,以及 `flate2`(供 LevelDB 读取器使用)。未设置 `panic = "abort"`。
---
## 许可证
本仓库采用 Apache-2.0 开源协议。
[Atlas]: https://www.minebbs.com/resources/atlasmap.17092/
[/MD]