一、为什么从一张图变成两台服
之前这台机器上只有一张「源石试炼 V1.1」。
它是一张 Fabric 1.21.1 的明日方舟集成战略风格地图:选干员、走节点、打怪、拿藏品,一局打完可以再来一局。地图很好玩,但它更像一个“副本”,不太适合作为每天上线的常驻世界。
于是这次把它拆成了两台服务器:
- 原版生存:常驻入口,长期世界,12 人上限
- 源石试炼:按需副本,6 人上限,没人时自动关闭
两台服不是两个世界塞进一个服务端,而是两个独立的 Java 进程,各有自己的世界、端口、模组、RCON 和备份。
二、最终结构
| 项目 | 原版生存 | 源石试炼 |
|---|---|---|
| 地址 | game.mc.dusktyh.cn:25565 |
game.mc.dusktyh.cn:25566 |
| 版本 | 1.21.1 / Fabric | 1.21.1 / Fabric |
| 人数 | 12 | 6 |
| 模式 | 生存 | 冒险 / Roguelike |
| 自启 | 是 | 否 |
| 空闲关闭 | 否 | 约 10 分钟 |
| CPU | 1-3(3 核) | 4-5(2 核) |
CPU0 特意空出来留给系统、nginx 和数据库。两个 Java 进程分别绑定不同的核心,避免互相抢 CPU。
这里顺便纠正一个常见说法:Java 服务端不是“只吃一个核”。主 tick 线程确实是单线程,但 GC、Netty、区块生成、部分模组都会用额外的线程。给每个服只分一个核,反而容易在 GC 或跑图时卡顿。所以这次给生存服 3 核,源石试炼 2 核。
三、两服怎么切换
从生存去源石
在原版生存里输入:
/源石试炼
也可以使用 ASCII 别名:
/source
/sourcetrial
如果源石试炼没开,命令会先启动它,等端口就绪后再把玩家传送过去。
从源石回生存
/生存
别名:
/survival
/vanilla
中文命令不是普通命令
Minecraft 的 Brigadier 命令名只接受 ASCII 字符,/源石试炼 这种中文命令没法用普通方式注册。
所以这里写了一个服务端小模组,在命令解析前拦截原始命令字符串,再执行启动、等待和传送。玩家不需要安装任何客户端模组。
四、死亡榜
两服都支持:
/deathtop
/deathtop off
右侧会显示 死亡榜,计分板 objective 是 ft_deaths,死亡时由原版自动累加。
两服规则不同:
- 原版生存:永久累计,不重置
- 源石试炼:新人初始化时重置全部记录,包括离线玩家
源石的重置点写在地图的 settings:init 函数里,和“每当有新玩家进入都需要初始化”的流程绑在一起。
测试用的假人分数也已经清理,不会出现在真实排行榜里。
五、生存服墓碑
生存服加了自制墓碑模组。
玩家死亡后:
- 主背包、盔甲、副手、光标物品都会被收进墓碑箱
- 箱子标题是“玩家名 的墓碑”
- 在死亡点半径 3 格内寻找位置,优先原地、优先上方能打开的地方
- 不替换实心方块;可以替换水/岩浆,箱子
waterlogged=false - 物品超过 27 格时优先形成双箱(54 格),位置不足才退回第二个独立箱
- 虚空死亡会回退到最后安全站立点,再回退到世界出生点,不会把物品丢进虚空
- 空背包死亡不生成墓碑,连续空手死亡也不会刷出空箱
同时改了两条规则:
doFireTick=false:火焰不蔓延keepInventory=false:死亡物品进入墓碑,而不是留在身上
六、爆炸:只保护苦力怕
这一点专门改过:
- 苦力怕爆炸:不破坏方块
- TNT、恶魂火球、末影水晶等:仍按原版破坏地形
- 所有爆炸对实体的伤害保持原版
为什么不把所有爆炸都关掉?因为 TNT 炸山、炸建筑、做刷怪塔都是正常玩法。只让苦力怕不拆家,已经能解决大部分“回家发现墙没了”的问题。
墓碑箱本身防火,但 TNT 仍可能炸毁它。如果以后需要,可以再单独给墓碑加“防爆但允许 TNT 炸地形”的方块级保护。
七、登录提示
玩家进服约 2 秒后:
- 正版登录:聊天栏显示“你现在使用的是正版登录”
- 离线登录:左下角聊天框弹出登录指南,提示如何注册和登录
判定方式是 UUID 版本:
- 正版 Mojang UUID:v4
- 离线 UUID:v3
这是纯服务端逻辑,客户端不需要装模组。
八、性能
改造完成后的一个快照:
| 指标 | 值 |
|---|---|
| 在线 | 8 / 12 |
| TPS | 20.0 |
| 平均 tick | 13.2ms |
| P95 / P99 | 17.4ms / 19.6ms |
| 堆内存 | 902MB / 2GB |
| 系统可用内存 | 约 4.8GB |
20 TPS 就是原版满速,13.2ms 距离 50ms 的 tick 上限还有很大余量。
服务端优化模组全部是纯服务端:
- ServerCore
- Noisium
- Clumps
- Alternate Current
- Lithium
- FerriteCore
- Krypton
ServerCore 的动态视距和激活范围保持关闭,避免影响源石试炼的自定义怪物和交互。
12 人满员时如果 tick 变高,再考虑降 simulation-distance、开 ServerCore 动态调节或做区块预生成。
九、备份上云
以前备份只在本地,磁盘坏了就一起没。
这次把两服的备份都同步到了雨云对象存储:
- 源石:
mc-fabric/YYYY/MM/DD/ - 原版:
mc-vanilla/YYYY/MM/DD/ - 每份包含
.gpg加密包、.sha256、latest.txt - 本地保留 7 份
- 远端保留 14 天
备份内容包括:
- 世界
- 配置
- EasyAuth 的 MySQL 导出
- 自制服务端模组
上传后会做远端列表校验和 SHA256 校验。源石和原版各有一个每天的 systemd timer,时间错开,避免同时读盘。
十、还没完全收尾的地方
Tab 列表
有玩家反馈 Tab 列表里看不到离线登录玩家。
服务端查过了:
/list能看到所有在线玩家- 独立 1.21.1 客户端收到的
player_info里,离线 UUID 玩家的listed=1 hide-online-players=false- 两服
vanish-until-auth=false - 没有安装 Vanish
所以服务端确实把离线玩家发送到了 Tab 列表。问题大概率在客户端侧的 TabList / 玩家列表类模组,或者客户端设置。后面需要拿纯净客户端和客户端模组列表对照。
其他
- 墓碑没有做“仅主人可开”,也没有防玩家拆除
- TNT 仍能炸毁墓碑
- 源石试炼数据包较重,6 人峰值还要实测
- 雨云备份目前没有失败告警,可以后续加通知
十一、服务器信息
| 项目 | 内容 |
|---|---|
| 原版生存 | game.mc.dusktyh.cn:25565 |
| 源石试炼 | game.mc.dusktyh.cn:25566,或从生存输入 /源石试炼 |
| 版本 | Minecraft 1.21.1 |
| 客户端 | 原版即可,不需要装模组 |
| 人数 | 生存 12,源石 6 |
| 交流群 | Q 群 421345512 |
服务器由站长自费维护,非商业性质。世界每天自动备份,本地和雨云各留一份。
从一张 Roguelike 地图,到一台常驻生存服加一个按需副本,这次改造的重点其实不是“加了多少功能”,而是把切换、登录、死亡、备份这些日常运维的坑一个个补上。
接下来如果 Tab 列表问题确认是客户端模组导致的,会再补一篇排查记录。
