📦 PackSync

轻量级资源包同步插件 — 群组服跨子服自动同步 CraftEngine 资源包

📦 Paper / Purpur 1.21+ 📤 Sender / Receiver 双模 🔄 文件变化自动检测 ⚡ 异步IO零卡顿 🔗 信号文件机制

为什么需要 PackSync?

在群组服架构中,CraftEngine 资源包通常只在主城服(开发服)上生成。但登录服等其他子服也需要向玩家分发相同的资源包。PackSync 解决了这个痛点:

  • 主城服修改资源包后,自动检测变化并复制到所有子服
  • 子服收到文件后,自动执行 ce upload 上传给在线玩家
  • 全程无需人工干预,无需重启服务器

核心功能

📤
Sender 发送端
监听资源包文件变化 (SHA-1 哈希比对), 变化时自动复制到所有目标子服
📥
Receiver 接收端
检测信号文件, 收到同步信号后自动执行上传命令, 让在线玩家收到新资源包
🔄
自动同步
可配置定时检查 (默认每10秒), 资源包变化后全自动同步, 零人工干预
📡
信号文件机制
通过 .packsync_signal 文件通知接收端, 无需数据库/消息队列, 极简可靠
异步IO
文件复制和哈希计算全部在异步线程执行, 不阻塞主线程, 零卡顿
🛠️
简洁命令
3条命令搞定一切: sync 手动同步, status 查看状态, reload 重载配置

📥 安装与部署

环境要求

项目要求
服务端Paper / Purpur / Spigot 1.21+
JavaJava 21+
必须依赖
可选依赖CraftEngine (资源包来源)

群组服部署方案

以典型的 登录服 + 主城服 群组为例:

架构图:
主城服 (Sender)                    登录服 (Receiver)
┌─────────────────┐              ┌─────────────────┐
│ CraftEngine     │              │ CraftEngine     │
│ ↓ 生成资源包     │   PackSync   │ ↓ 收到新文件     │
│ resource_pack.zip│ ──复制文件──→ │ resource_pack.zip│
│                 │ ──信号文件──→ │ .packsync_signal │
│ PackSync=sender │              │ PackSync=receiver│
└─────────────────┘              │ ↓ 执行 ce upload │
                                 └─────────────────┘

安装步骤

1. 主城服 (Sender)

  1. PackSync-1.0.0.jar 放入 plugins/
  2. 启动服务器,自动生成 config.yml
  3. 编辑配置:设置 role: sender
  4. 配置 sender.targets 列表,填入登录服资源包的绝对路径
  5. 执行 /packsync reload

2. 登录服 (Receiver)

  1. PackSync-1.0.0.jar 放入 plugins/
  2. 启动服务器,自动生成 config.yml
  3. 编辑配置:设置 role: receiver
  4. 确认 receiver.commandce upload (或你的上传命令)
  5. 执行 /packsync reload
注意: Sender 的 targets 路径必须是绝对路径,且 Sender 服务器进程需要对目标目录有写入权限。同一台物理机上的多个子服可以直接使用本地路径。

📤 Sender 发送端

Sender 模式运行在资源包的生产端(通常是主城服),负责检测文件变化并同步到所有目标子服。

工作流程

  1. 插件启动时,计算源文件的 SHA-1 哈希作为基准
  2. 定时任务每 N 秒 (默认10秒) 重新计算哈希
  3. 若哈希值发生变化 → 触发同步
  4. 异步复制文件到所有 targets 目标路径
  5. 在每个目标路径的同级目录放置 .packsync_signal 信号文件
  6. 信号文件内容为当前时间戳,供 Receiver 端检测

配置项

配置说明默认值
sender.source源资源包路径 (相对服务器根目录)./plugins/CraftEngine/generated/resource_pack.zip
sender.targets目标路径列表 (绝对路径)[]
sender.auto-sync是否启用自动同步true
sender.watch-interval检查间隔 (秒)10

配置示例

role: sender

sender:
  source: "./plugins/CraftEngine/generated/resource_pack.zip"
  targets:
    - "D:/Server/登录服/plugins/CraftEngine/generated/resource_pack.zip"
    - "D:/Server/资源服/plugins/CraftEngine/generated/resource_pack.zip"
  auto-sync: true
  watch-interval: 10

手动同步

执行 /packsync sync 可立即触发一次同步,不需要等待文件变化。

📥 Receiver 接收端

Receiver 模式运行在资源包的分发端(通常是登录服),负责检测信号文件并执行上传命令。

工作流程

  1. 定时任务每 N 秒 (默认5秒) 检查信号文件是否存在
  2. 检测到 .packsync_signal → 读取并删除信号文件
  3. 等待配置的延迟时间 (默认3秒,确保文件写入完成)
  4. 主线程执行控制台命令 (默认 ce upload)
  5. CraftEngine 重新上传资源包,在线玩家收到新资源包

配置项

配置说明默认值
receiver.command收到信号后执行的控制台命令ce upload
receiver.check-interval信号检查间隔 (秒)5
receiver.command-delay执行命令前的延迟 (秒)3

配置示例

role: receiver

receiver:
  command: "ce upload"
  check-interval: 5
  command-delay: 3

信号文件

信号文件 .packsync_signal 位于 sender.source 配置指向的资源包同目录下。文件内容为毫秒时间戳,仅用于触发通知,读取后自动删除。

提示: Receiver 端也需要配置 sender.source 路径 (虽然不作为发送方使用),因为信号文件的位置基于该路径推算。

⌨️ 命令

命令列表

命令说明权限
/packsync sync手动触发资源包同步 (仅 Sender 模式)packsync.admin
/packsync status查看当前状态 (模式/文件/哈希/目标数等)packsync.admin
/packsync reload重载配置文件并重新初始化packsync.admin
/packsync显示帮助信息packsync.admin
别名: /packsync 可缩写为 /ps/rpsync

sync 详解

Sender 模式下,立即将源文件复制到所有目标路径。会显示每个目标的成功/失败状态及文件大小。

/packsync sync

[PackSync] 开始同步资源包 (12.3 MB)...
[PackSync]   ✔ 1.登录服 [ 30000 ]
[PackSync] 同步完成! 成功: 1, 失败: 0

status 详解

显示当前插件状态,Sender 和 Receiver 模式显示不同信息:

Sender 模式

=== PackSync 状态 ===
模式: sender
源文件: D:\Server\主城服\plugins\CraftEngine\generated\resource_pack.zip
文件存在: 是
文件大小: 12.3 MB
SHA-1: 3a7f8b2c1d9e...
目标数: 1
自动同步: 开启

Receiver 模式

=== PackSync 状态 ===
模式: receiver
信号文件: D:\Server\登录服\plugins\CraftEngine\generated\.packsync_signal
执行命令: ce upload
信号存在: 否

📁 配置文件

完整 config.yml

# ============================================
#  PackSync v1.0.0 配置文件
#  轻量级资源包同步插件
# ============================================

# 角色模式
# sender   - 资源包生产端 (主城服等, 负责CE开发)
# receiver - 资源包接收端 (登录服等, 负责向玩家分发)
role: sender

# ===== Sender (发送端) 配置 =====
sender:
  # CE生成的资源包路径 (相对于服务器根目录)
  source: "./plugins/CraftEngine/generated/resource_pack.zip"
  # 同步目标列表 (使用绝对路径, 指向目标服的CE generated目录)
  targets:
    - "D:/Server/登录服/plugins/CraftEngine/generated/resource_pack.zip"
  # CE生成资源包后自动同步 (监听文件变化)
  auto-sync: true
  # 自动同步检查间隔 (秒)
  watch-interval: 10

# ===== Receiver (接收端) 配置 =====
receiver:
  # 检测到同步信号后执行的控制台命令
  command: "ce upload"
  # 信号检查间隔 (秒)
  check-interval: 5
  # 执行命令前的延迟 (秒, 等待文件写入完成)
  command-delay: 3

配置项速查

配置项类型说明默认值
roleString工作模式: senderreceiversender
sender.sourceString源资源包路径 (相对服务器根目录)./plugins/CraftEngine/generated/resource_pack.zip
sender.targetsList目标文件绝对路径列表[]
sender.auto-syncBoolean是否启用定时自动检测true
sender.watch-intervalInteger自动检测间隔 (秒)10
receiver.commandString收到信号后执行的命令ce upload
receiver.check-intervalInteger信号文件检查间隔 (秒)5
receiver.command-delayInteger执行命令前延迟 (秒)3

🔐 权限

权限说明默认
packsync.admin允许使用所有 PackSync 管理命令op
说明: PackSync 作为运维工具, 仅需一个管理员权限节点。所有自动同步功能无需权限, 插件启动后自动运行。

🏗️ 架构原理

设计理念

PackSync 采用无中心化的文件信号机制,避免引入额外的消息队列或数据库依赖:

  • 文件复制: Sender 直接通过文件系统复制文件到目标路径
  • 信号通知: 在目标目录创建 .packsync_signal 文件通知 Receiver
  • 变化检测: SHA-1 哈希比对,只有文件内容真正变化才触发同步

SHA-1 哈希检测

Sender 启动时计算源文件的完整 SHA-1 哈希作为基准值。定时任务每次重新读取文件并计算哈希,与基准值比对:

  • 哈希相同 → 文件未变化,跳过
  • 哈希不同 → 文件已更新,触发同步并更新基准值
性能: SHA-1 计算在异步线程执行。对于 10-20 MB 的资源包, 计算耗时通常不到 50ms, 对服务器 TPS 无影响。

信号文件协议

属性说明
文件名.packsync_signal
位置与资源包同目录 (如 plugins/CraftEngine/generated/)
内容Unix 毫秒时间戳 (如 1712649600000)
生命周期Sender 创建 → Receiver 读取后立即删除

异步处理

以下操作全部在异步线程执行,不阻塞服务器主线程:

  • SHA-1 哈希计算
  • 文件复制 (Files.copy)
  • 信号文件读写

唯一在主线程执行的操作是 Receiver 的控制台命令 (Bukkit.dispatchCommand),因为 Bukkit API 要求命令在主线程执行。

适用场景与限制

✅ 适用❌ 不适用
同一台物理机上的多个子服跨物理机/跨网络的服务器
CraftEngine 资源包同步需要双向同步的场景
少量大文件 (资源包)大量小文件同步
注意: PackSync 依赖文件系统直接访问, 仅适用于子服在同一台物理机上的情况。跨机器同步请使用 rsync/SFTP 等工具。

常见问题

Q: Sender 和 Receiver 可以在同一个服务器上吗?

不可以。每个服务器实例只能配置一种模式。如果主城服同时也需要接收资源包更新,那它应该设为 sender 模式(因为它就是生产端)。

Q: 源文件不存在怎么办?

Sender 启动时会输出警告日志。自动同步任务会跳过不存在的文件。CraftEngine 首次生成资源包后,PackSync 会自动检测到并开始同步。

Q: 目标目录不存在会报错吗?

不会。PackSync 会自动创建目标文件的父目录 (Files.createDirectories)。

Q: 同步大文件时会卡服吗?

不会。文件复制和哈希计算全部在 BukkitScheduler.runTaskAsynchronously 中执行,不占用主线程。

Q: Receiver 的 command 可以是多条命令吗?

目前只支持单条命令。如需多条命令,建议用另一个插件(如 TrMenu/DeluxeMenus)包装成一条,或修改源码。

Q: 如何增加新的目标子服?

编辑 Sender 的 config.yml,在 sender.targets 列表中添加新路径,然后执行 /packsync reload 即可。

Q: 检查间隔设多少合适?

  • Sender: 10-30 秒。资源包不会频繁变化,无需太高频率
  • Receiver: 3-5 秒。信号文件检测开销极小,可以更频繁