FastSync

16

FastSync 跨服玩家数据同步插件

新一代跨服玩家数据同步插件,只服务高版本 Paper 系服务端,更轻量、更稳、根治"损坏物品导致进不去服 / 物品被删"的老大难问题。

FastSync 是 InvSync 作者面向 Minecraft 1.20.1+ 打造的下一代同步方案。它抛掉了老架构里为了兼容 1.12 一路背到今天的历史包袱(多版本反射、NBTAPI、Redis 硬依赖等),围绕两件事重做:让玩家出生的那一刻数据就已经在身上了,以及再脏的物品也绝不把你的数据搞丢


🎯 FastSync 是什么 / 定位

  • 新一代跨服同步:背包、末影箱、状态、经验、统计、成就、PDC、扩展数据在多个子服之间无缝流转。
  • 只支持高版本、更轻量:面向 1.20.1+,不再兼容低版本,砍掉 NBTAPI、Kryo、MongoDB 驱动等一大堆 shade 依赖,产物更小、启动更快。
  • 无 Redis:只要一个 MySQL 就能跑起来一个跨服网络,部署门槛大幅降低。
  • 根治损坏物品问题:把"未知附魔 / 组件损坏导致反序列化失败"这类问题做成了净化 + 隔离 + 复活的完整闭环,原始数据永不被覆盖丢弃

FastSync 与 InvSync 是两条产品线:InvSync 继续覆盖 1.12.2 – 26.x 的全版本老服,FastSync 则专注把 1.20.1+ 的现代 Paper 服做到极致。


⚠️ 支持范围(务必先看这里)

这是选型前最重要的两条硬性要求,不满足任何一条都无法使用 FastSync。

  • Minecraft 版本:1.20.1 及以上。低于 1.20.1 的服务端请使用 InvSync。
  • 服务端类型:仅 Paper 系(Paper / Purpur / Folia / Leaves 等基于 Paper 的分支)。
    不支持 Spigot、不支持 CraftBukkit。

为什么这么严格? FastSync 的物品序列化完全依赖 Paper 独有的官方 API ItemStack#serializeAsBytes()(1.20.1 起提供,字节内嵌 DataVersion,官方承诺跨版本自动升级)。Spigot / CraftBukkit 没有这个 API,因此无法运行。这也是 FastSync 能"根治损坏物品"和"旧版本存的物品在新版本服自动升级"的技术根基——门槛换来的是可靠性。

此外,代理端(Velocity / BungeeCord)是必装组件:FastSync 的预加载架构依赖代理提前通知子服,直连后端的玩家会走兜底路径并告警。


✨ 核心特性

🚀 preload-always:玩家出生即数据就绪,切服无卡顿

传统同步是"玩家先进服,再冻结住玩家,然后在主线程一件件 setItem 把背包塞回去",进服瞬间会有一段可感知的卡顿甚至短暂空背包。

FastSync 把同步永远放在玩家出生之前

  • 代理端在玩家切服前 500ms~2s 就通知目标子服提前抢锁、读库、并把原版字段(背包、末影箱、血量、饥饿、经验等)离线写进 playerdata/<uuid>.dat,由服务端自己加载。
  • 玩家真正连入时,数据已经在 .dat 里,出生即正确,主线程几乎只剩"应用扩展字段"的一点点开销。
  • 没有"进服后冻结玩家再 setItem"这个阶段,切服体感接近无缝。

登录屏障(AsyncPreLogin)会等待预载结果,即使预载没赶上,也会在这里现场抢锁+读库兜底,同样保证出生即正确,只是慢一点。

🗄️ 无 Redis:MySQL 租约锁 + 单调版本号

FastSync 用一个 MySQL 就完成了分布式一致性,完全不需要 Redis

  • 租约锁:玩家主表内嵌 lock_owner / lock_expires_at,抢锁 + 读数据一个原子 UPDATE 搞定;崩服后租约到期(默认 60s)其他服自动接管,不会永久锁死。
  • data_version 单调版本号:每次保存版本号 +1,保存时乐观写(WHERE data_version=期望值),旧数据永远盖不掉新数据。切服交接时版本号作为"凭据"传递,目标服读到 data_version ≥ 凭据 才算读到源服刚存的数据,彻底消灭"抢到锁却读到旧数据"的竞态。
  • 部署门槛低、不怕 Redis 断连关服:老架构里 Redis 断连会直接关服,对小服主过于残暴;FastSync 没有这个中间件,少一个运维炸弹。

📦 Paper 官方物品序列化:跨版本自动升级,不再依赖 NBTAPI

物品只走 Paper 官方的 ItemStack#serializeAsBytes() / deserializeBytes()

  • 字节内嵌 DataVersion旧版本服存的物品,在新版本服读取时会自动走原版 DataFixerUpper 升级(和世界存档同一套机制,官方承诺)。
  • 彻底不再依赖 NBTAPI(仅迁移工具例外),消灭了 NBTAPI 版本滞后带来的"not supported"告警和兼容风险。
  • 容器(背包/末影箱)采用按槽位独立成帧的二进制格式,一个物品字节坏了不影响同一容器里其他物品的解析——这正是下面"单品隔离"的基础。

注意:官方 API 只承诺"旧写新读"的升级方向。混版本网络请让数据只从低版本服流向高版本服;同版本网络无此顾虑。

🛡️ 损坏物品净化 + 隔离复活:数据永不丢

这是 FastSync 相对同类插件最核心的竞争力。1.20.5+ 的组件严格校验、1.21.6+ 更是"一律抛异常",一个带未知附魔的物品在同类插件(如 HuskSync)里会被直接删除并把删除结果回写数据库(issue #596),玩家的东西就这么没了。

FastSync 定下一条红线:解析失败时,原始字节永远不被覆盖丢弃,走三层治理:

  1. 直接解码 → 成功即返回。
  2. 净化重试:纯 Java 的 NBT 手术,剥离注册表中不存在的未知附魔(兼容 1.20.5+ 组件两种布局 + 1.20.1 老 tag,递归处理嵌套容器),重新解码 → 成功则物品保住,只丢那个未知附魔(≈原版行为)。
  3. 隔离 + 复活:净化仍救不回时,把原始字节 + 错误信息存入隔离表,槽位放一个占位屏障物品(写明原因、PDC 记录隔离 id);保存时写回的是隔离表里的原始字节而不是占位物品;等对应附魔/物品插件装回来后,下次加载自动复活原物品。

配套还提供 archive-original 开关:即使净化成功恢复了物品,也可把原始字节存档进隔离表以备回查。

对比:HuskSync 直接删物品 + 回写数据库(不可逆丢失);FastSync 保住能保的、隔离救不回的、并留好复活的路。

🌐 多代理集群

子服可以同时连接多个 Velocity / BungeeCord 代理proxy.urls 填多个地址,全部保持长连接)。一个玩家同一时刻只属于一个代理,由该代理独立完成它的切服编排,代理之间无需互通;锁与数据一致性全在 MySQL,与代理数量无关。适合多入口 / 多代理的大网拓扑。

💾 备份分级保留 + GUI 回档

内置玩家数据备份系统,可在退出 / 切服 / 关服 / 自动保存时打快照,采用分级保留策略(最近 N 个全留,再按天 / 周 / 月各留若干个,其余清理),防止空数据或误操作把有效备份冲掉。管理员可通过 /fastsync backup <玩家> 打开 GUI,列表 → 预览 → 确认回档,直观安全。

🔌 addon 扩展 + 内置第三方 hook

FastSync 提供对外的 addon APIonSave→bytes / onApply / shouldApply 门禁)和 Bukkit 事件(同步完成 / 进服门禁 / 物品清洗 / 物品隔离),第三方可安全地扩展同步任意自定义字段。开箱内置 5 个第三方 hook:

Hook 作用
CMI Fly Charge 同步 CMI 飞行充能
Vault Economy 同步 Vault 经济余额(默认关,按需开启)
DragonArmourers 同步龙之时装,同步完成后延迟重贴皮肤
LibsDisguises 同步伪装,同步完成后按已保存伪装重挂
EpicProfileSwitch 存档切换门禁(玩家没切档就不覆盖其扩展数据)

第三方 jar 放 libs/ 目录走 compileOnly,装了对应插件才生效。


🧾 同步内容清单

数据 说明 默认
背包 inventory 主背包
末影箱 ender-chest 末影箱
血量 health 当前生命值
最大血量 max-health 属性最大血量(跨版本反射兼容)
饥饿 food 饥饿值
饱和 saturation 饱和度
经验 exp 经验进度
等级 level 经验等级
药水效果 effects 药水效果(type 存 NamespacedKey 字符串最稳)
统计 statistics vanilla 统计数据
成就 advancements vanilla 成就
PDC persistent-data-container 玩家 PersistentDataContainer(很多插件用它存数据,官方字节序列化)
游戏模式 game-mode 生存 / 创造 / 冒险 / 旁观
手持槽 held-slot 快捷栏当前选中槽
addon 扩展数据 plugin-data 第三方 hook 数据:经济 / 时装 / 伪装 / 飞行充能等

通过 addon API 可安全地自行同步任意自定义字段。


⚙️ 配置文件说明(config.yml 挑重点)

# 本服标识(租约锁持有者名 + 代理端注册名)。留空自动用服务端根目录名。
# ⚠️ 同一网络内每台子服必须唯一!
server-name: ''

# 玩家档案目录(离线写 .dat 用)。留空自动用主世界的 playerdata。
player-data-folder: ''

mysql:
  host: 127.0.0.1
  port: 3306
  database: fastsync
  user: root
  passwd: password
  ssl: false

# 同步项开关(见上方"同步内容清单")
sync:
  inventory: true
  ender-chest: true
  # ... 各字段开关
  persistent-data-container: false   # PDC 按需开启
  plugin-data: true                  # addon 扩展数据

# 损坏物品处理(净化/隔离是默认且唯一行为,不提供"踢人"选项)
invalid-item:
  archive-original: true   # 净化成功也把原始字节存档进隔离表,可回查

# 租约锁
lock:
  ttl-ms: 60000                 # 租约有效期,崩服后其他服最多等这么久接管
  refresh-interval-ticks: 300   # 在线续约间隔(独立线程按 wall-clock 执行,不受 TPS 影响)

# 登录屏障
login:
  preload-await-ms: 5000   # 等代理预载结果的上限
  lock-wait-ms: 8000       # 兜底现场加载时等租约的上限
  deny-on-failure: true    # 数据无法就绪时拒绝登录(强烈建议 true,避免给空数据)

auto-save:
  enable: true
  interval-ticks: 6000

# 数据备份(快照存档,可回档)
backup:
  enable: true
  on-disconnect: true      # 退出/切服/关服时备份
  on-auto-save: false      # 每次自动保存也备份(默认关,避免备份表膨胀)
  retention:               # 分级保留
    latest: 10
    daily: 7
    weekly: 4
    monthly: 3

# 代理端连接(FastSyncVelocity / FastSyncBungee 的 WS 服务端)
proxy:
  secret: 'change-me'      # 必须与代理端配置一致
  urls:                    # 代理集群时把每个代理都填上,子服会全部保持长连接
    - 'ws://127.0.0.1:8765'

# 第三方插件同步 hook(装了对应插件才生效)
hook:
  cmi-fly-charge: true
  init-cmi-fly-charge: true   # false: DB 无充能数据则退出不保存,不用本服默认值初始化跨服玩家
  vault: false                # 经济默认关(跨服并发下有精度/竞态风险)
  init-vault: true
  dragon-armourers: true
  dragon-armourers-delay: 80  # 时装刷新延迟,必须等背包等数据落地后再刷
  libs-disguises: true
  epic-profile-switch: true

几个必须注意的点:

  • server-name 同一网络内每台子服必须唯一,它同时是租约锁持有者名和代理端注册名,重复会导致锁冲突。
  • login.deny-on-failure 强烈建议保持 true,否则数据无法就绪时会放行空数据,玩家本次会话不同步。
  • proxy.secret 必须和代理端配置一致;代理集群把每个代理地址都填进 proxy.urls
  • hook.vault 默认关闭:经济这类高频变动数据在跨服并发下有精度和竞态风险,按需开启。

🕹️ 指令与 GUI

主指令 /fastsync,权限 fastsync.admin

命令 作用 说明
/fastsync reload 重载配置
/fastsync status 查看运行状态 显示本服标识、持锁玩家数、预载缓存、代理地址
/fastsync save <玩家> 立即保存指定在线玩家数据 玩家需在线且数据已加载完成
/fastsync backup <玩家> 打开备份 GUI(后台执行者则打印列表) 列表 → 预览 → 确认回档
/fastsync backup save <玩家> 手动为在线玩家创建备份
/fastsync backup restore <id> 回档到指定备份 需玩家离线,防止覆盖在线状态
/fastsync quarantine <玩家> 打开隔离物品 GUI(后台执行者则打印列表) 查看该玩家的损坏物品隔离记录
/fastsync quarantine restore <id> 尝试解析隔离记录并取回物品到自己背包 仅玩家可执行;若仍无法解析会提示原因

GUI 操作/fastsync backup <玩家> 打开备份列表界面,点选可预览并二次确认回档;/fastsync quarantine <玩家> 打开隔离列表界面,可查看并取回被隔离的物品。图形化操作比手敲 id 更直观、更不易误操作。


🔄 与 InvSync 的区别对比

维度 InvSync(2.0) FastSync(新一代)
支持版本 1.12.2 – 26.x 全版本 仅 1.20.1+
服务端类型 Spigot / Paper 系均可 仅 Paper 系(不支持 Spigot/CraftBukkit)
中间件 需要 Redis(分布式锁 + 热缓存),断连会关服 无需 Redis,只要 MySQL
分布式一致性 Redis 锁 + TTL 续锁 MySQL 租约锁 + data_version 单调版本号乐观写
物品序列化 NBTAPI → SNBT → JSON → GZIP Paper 官方 serializeAsBytes(字节内嵌 DataVersion,跨版本自动升级)
跨版本升级 依赖 NBTAPI + 整 .dat DataFixer 官方 DFU,旧版本物品在新版本服自动升级
损坏物品策略 默认 deny(整背包中断 + 踢人循环);remove 会永久清洗 净化 + 隔离 + 复活,原始字节永不覆盖丢弃
预加载 增值模块,需额外购买 默认全程 preload-always,出生即就绪
冻结机制 进服后冻结玩家 + 主线程 setItem 无冻结阶段(仅 pre-quit 窗口的轻量冻结)
代理端 可选 必装组件,且支持多代理集群
包体积 约 13MB(shade 大量依赖) 显著更小(砍掉 NBTAPI/Kryo/Mongo 等)

📥 从 InvSync 迁移

FastSync 提供独立的一次性迁移工具 project-migrate(打包为 FastSyncMigrate 插件,迁移完可直接卸载)。

用法:

  1. 把源库(InvSync)和目标库(FastSync)的 JDBC 配置写进迁移插件的 config.yml
  2. 服务器空载时执行 /fastsyncmigrate run(别名 /fsmigrate,权限 fastsync.migrate)。
  3. 迁移异步执行、幂等overwrite=false 时只补 FastSync 缺失的玩家,可反复安全重跑),完成后卸载迁移插件即可。

字段兼容说明:

  • 会迁移:背包 inventory、末影箱 enderChest、统计 statistic、药水效果 buffs、成就 advancements、血量 health、最大血量 maxHealth、饥饿 food、经验 exp、等级 level。物品字节用 Paper serializeAsBytes 重新序列化,随迁移自动升级到目标版本。
  • addon 数据无缝迁移pluginData 的字节格式(GZIP KV)与 FastSync 完全一致,直接搬字节——CMI 飞行充能、Vault 经济等 addon 数据无需转换,玩家首次进 FastSync 服由对应 addon 自动 apply 回身上。
  • 暂不迁移persistentData(InvSync 存 NBT 文本,与 FastSync 官方字节格式不同)、otherData(mod 数据)——为避免损坏而跳过。

迁移前建议先备份数据库。迁移期间可双表共存,稳妥切换。


📌 一句话总结

如果你的网络是纯 Paper 系、1.20.1+ 的现代服,想要部署更简单(无 Redis)、切服更顺滑(出生即就绪)、物品数据更安全(净化+隔离+复活),FastSync 就是为你准备的下一代方案。老版本或 Spigot 服请继续使用 InvSync。