Folia 与多世界实例
预加载世界池、离线世界导入、远端隔离回退、容量计算与调度排查。
“多世界”在 Paper 与 Folia 上不是同一件事
Paper 可以由插件在运行期创建、加载和卸载实例世界,也可以启用单容器世界模式。
Folia 不允许插件安全地在运行期创建或卸载世界,因此 LiDungeon 采用两级方案:
- 优先使用管理员预先加载的多世界池;
- 没有有效池时,回退到每个维度一个已加载世界中的远端隔离区域。
两种 Folia 模式都不会在运行时动态创建/卸载世界。
世界池配置
Folia:
MultiWorld:
Enabled: true
Worlds:
NORMAL:
- lidungeon_world_1
- lidungeon_world_2
NETHER:
- lidungeon_nether_1
THE_END:
- lidungeon_end_1
每个池世界同时只分配给一个正在运行或初始化的地牢实例。实例结束并清理后,世界归还池中复用。
世界必须满足的条件
- 在 LiDungeon 加载地牢前已经出现在 Bukkit 世界列表中;
- 世界名完全匹配;
- 世界环境与配置段匹配;
- 不被其他插件在运行中卸载;
- 预留足够磁盘和区块容量;
- 不作为普通生存世界供玩家自由活动。
仅在 Multiverse 配置中“存在”不等于启动时已经“加载”。必须检查实际服务端启动顺序和 LiDungeon 控制台日志。
容量计算
某维度实际并发容量取以下限制的最小值:
有效池世界数
全局 General.MaxInstances
单地牢 General.MaxInstances
服务器资源可承受实例数
例如 NORMAL 有 3 个有效池世界、全局上限 10、地牢上限 5,则 NORMAL 池独占容量是 3。超出后玩家会排队,不能靠提高配置数字突破世界池数量。
回退模式
出现以下情况会跳过池项:
- 世界未加载;
- 世界名为空或重复;
- 维度不匹配;
- 初始化后无法取得世界。
若一个维度最终没有有效池世界,插件记录中文警告并创建单容器回退,在远距离隔离各实例。回退仍可玩,但并发弹性和隔离性低于独占世界池。
Folia 制图流程
Folia 没有提供可供插件安全调用的运行期世界创建/卸载 API,因此 /lidungeon create 会直接给出中文说明,不执行 WorldCreator.createWorld()。管理员有两种制图方式。
方式一:在 Paper 制作后迁移
- 在同版本 Paper 测试服创建或导入地图;
- 完成编辑、保存、重启复测;
- 停服复制
plugins/LiDungeon/maps/<地牢>/到 Folia; - 提前准备并加载池世界;
- 启动 Folia,检查池日志;
- 用一个测试实例验证;
- 再逐步增加并发。
方式二:在 Folia 导入离线世界
从 .9 起,/lidungeon import 不再为了读取出生点临时加载世界。它会读取离线 level.dat,在异步线程复制目录,再把完成品载入为地牢配置。
操作前检查:
- 世界目录位于服务端根目录,例如
<服务端>/ender_arena/; - 目录中至少有
level.dat和该维度的区块数据; - 该世界当前没有被服务端、Multiverse 或其他插件加载;
plugins/LiDungeon/maps/ender_arena/尚不存在;- 确认维度参数:主世界
NORMAL、下界NETHER、末地THE_END。
然后执行:
/lidungeon import ender_arena THE_END
导入内部顺序如下:
检查已加载世界和同名地牢
→ 异步读取 level.dat 出生点
→ 复制到 plugins/LiDungeon/import-staging/
→ 完整复制成功后移入 maps/ender_arena/
→ 加载地牢并写入维度、大厅出生点
这样即使复制中途失败,也不会把半份地图直接暴露为正式地牢。若 level.dat 缺少有效出生坐标,插件会使用 X=0, Y=64, Z=0 并显示提醒;随后执行 /lidungeon edit ender_arena,站到安全位置后运行 /lidungeon setlobby 和 /lidungeon setspawn。
若提示世界仍在加载,不要反复执行命令。先从预加载配置中移除该世界并完整重启,使目录保留但 Bukkit 世界注册表不再包含它,然后重新导入。世界池供“实例运行”使用,待导入的源世界必须保持离线,两者不要使用同一个世界名。
不要在 Folia 正式服高峰期导入大型世界;文件复制虽然异步执行,仍会占用磁盘吞吐。Folia 运行中仍不支持热建、热载或热卸世界。
调度与编辑保存
当前版本使用全局、区域、实体和异步调度器:
- 全局状态在全局调度器处理;
- 方块/区块工作回到所属区域;
- 玩家和实体操作回到实体调度器;
- 文件复制、压缩等放到异步线程;
- 编辑流程不应在全局线程等待区域任务的 CountDownLatch。
经典地牢编辑的完整方块区域会在最后一名编辑者退出后提交;自动保存会先保存功能、触发器、条件等元数据。看到保存消息后仍要等待正常退出流程,不要立即强制关服。
Folia 上的排查信号
| 日志/现象 | 含义 | 处理 |
|---|---|---|
| “世界尚未加载,已跳过” | Bukkit 未登记该世界 | 修启动加载顺序,重启 |
| “维度与配置段不一致” | NORMAL/NETHER/THE_END 填错 | 移到正确列表 |
| “已回退到单世界远端隔离区” | 没有有效池 | 可临时运行,正式服应修池 |
| “Folia 不能导入仍在加载的世界” | 导入源仍在 Bukkit 世界列表 | 移除预加载并完整重启,再导入离线目录 |
CraftServer.createWorld UnsupportedOperationException |
正在运行 .8 或更早版本 |
更新到 .9,删除旧 Jar 后完整重启 |
| 玩家一直排队 | 实例/池容量已满或加载未完成 | 看 /lidungeon status 与实例日志 |
| watchdog 显示全局线程等待 | 可能仍有同步等待链 | 保存完整线程栈与时间点,停止新增实例并排查插件版本 |
生产建议
- 每个池世界只服务 LiDungeon;
- 池容量至少覆盖计划峰值,并留 1 个测试/故障余量;
- 复杂地图提高
InstanceLoadTimeout,但不要用超大超时掩盖死锁; - 先压测经典地牢,再压测程序化投放;
- 更新世界池后完整重启;
- 保留每个池世界的离线备份,但实例运行数据仍以地牢模板和玩家数据为核心。