跳到主要内容

工作原理

一次切服发生了什么​

玩家在分流 a 分流 b
────────────────────────────────────────────────────────────
离开 a(/server b 或 Bungee 转服)
a:存档 rev 41(只有持有者 = a、版本 = 40 才写得进去)
a:归还租约 b:玩家进入,先冻结
b:取得租约(a 已归还)
b:读取 rev 41,校验 SHA-256
b:套用背包、经验、生命… → 解冻

如果 b 抢先一步(a 还没存完),b 拿不到租约就不会读取,而是每 0.5 秒重试一次,最多等 transfer.wait-for-previous-server-seconds(默认 5 秒)。a 一存完,b 就读到最新的 rev 41。

四个保证​

1. 租约:同时只有一个分流持有资料​

每位玩家在数据库里有一个「租约持有者」与「到期时间」。取得租约在 MySQL 事务里用行锁(SELECT … FOR UPDATE)完成,两个分流同时抢也只会有一个成功。持有中的分流每隔几秒续约一次。

2. 版本号:旧资料永远盖不掉新资料​

每次存档版本号 +1,写入条件是:

UPDATE … WHERE uuid = ? AND revision = <上一个版本> AND lease_owner = <我>

条件不符(租约被别人拿走、或版本落后)就写不进去,该分流会把玩家锁定并踢出,绝不覆盖。

3. 心跳接管:分流崩溃不会锁死玩家​

每个分流定期写心跳。租约只在持有分流还有心跳时有效:分流崩溃后心跳停止,超过 transfer.reclaim-dead-owner-after-seconds,其他分流就可以接管租约,载入最后一份完整存档。 另外每 30 秒自动清理一次无心跳分流的租约,分流重启时也会回收自己残留的租约。

4. 冻结:数据库卡顿也复制不了物品​

续约失败时,玩家立刻被冻结:不能点背包、丢弃、捡起、换手、互动、破坏 / 放置方块、攻击、吃东西、移动。 接管时间会被自动调整到「一定晚于冻结」(至少租约时间 + 8 秒),所以:

  • 只是数据库卡一下:玩家被冻结几秒,恢复后自动解冻,什么都没变。
  • 真的连不回去:租约到期前玩家被踢出,其他分流再接管——这段时间里玩家什么都动不了,所以不可能复制。

异常断线的额外保护​

正常离线与正常切服会立即归还租约。若分流崩溃、kill -9 或离线存档失败,旧租约到期后还会再隔离 snapshot-protection.unclean-lease-grace-seconds(默认 45 秒),之后才允许其他分流载入,避免把一份可能不完整的存档马上用掉。

Redis 快速通道(可选)​

开启后,Bungee 转服时来源分流会把刚存好的快照同时放进 Redis,目的分流读取后与 MySQL 的版本号、校验码比对。MySQL 永远是唯一的真实来源,Redis 故障或数据对不上都会自动回退 MySQL。

数据表​

数据表内容
cloudsync_player每位玩家的最新快照、版本号、租约
cloudsync_snapshot_history历史快照(每人保留最近 N 份)
cloudsync_transferBungee 转服交易与状态
cloudsync_heartbeat各分流心跳(人数、TPS、内存)
cloudsync_audit审计记录

前缀可用 storage.table-prefix 自定义。