基于 CraftEngine 与 Residence 的领地及全服公共音乐盒
领地/全服公共播放 · 自动转换 MP3/OGG · SQLite/MySQL 收藏 · Luminol/Folia 支持
PlayerMusic 会为每个已开启音乐功能的 Residence 领地维护一套公共播放会话,并额外提供一套管理员控制的全服会话。两种会话互相独立:控制或停止全服音乐不会修改任何领地会话,领地中的玩家仍可正常使用原版物品 GUI 选歌、收藏、暂停、切歌和切换播放模式。
- 作者: xWtree
- 项目主页: https://github.com/mcxqk/PlayerMusic
- 组织主页: https://github.com/mcxqk
- 许可证: GNU GPL v3.0
- 每个 Residence 领地拥有独立的公共音乐播放会话。
- 同一领地中的玩家共同听歌,任意拥有使用权限的玩家都能控制该领地的音乐。
- 管理员可开启独立的全服播放会话,所有在线玩家都能听到,且不会停止或改写任何领地会话。
- Residence 提供普通与顶级两级音乐 flag;顶级音乐领地会覆盖当前位置的普通音乐领地。
- 玩家可在 GUI 中按 10% 档位调整个人音乐音量,设置持久化到 SQLite/MySQL。
- 支持“全部音乐”“我的收藏”“收音机”三种列表。
- 支持单曲、随机、顺序三种播放模式。
- 支持上一页、下一页、暂停、继续、停止和下一首。
- 支持右键歌曲加入或取消收藏。
- 支持
.mp3与.ogg,自动生成 CraftEngine 声音资源。 - 内置 Windows/Linux x64 FFmpeg,通常不需要服务器额外安装,并支持调整转码码率减小资源包。
- 使用 SHA-256 增量缓存,未变化的音乐不会重复转换。
- 收藏数据支持 SQLite 与 MySQL,默认使用 SQLite。
- 使用 Folia/Paper 官方调度接口,支持 Luminol/Folia Region 线程模型。
- 配置、消息、数据库连接和音乐库支持
/playermusic reload热重载;少数标识项需要重启。
| 项目 | 目标版本 | 说明 |
|---|---|---|
| 服务端 | Luminol 1.21.11-DEV@311f4dc |
同时面向兼容 Paper 1.21.11 API 的 Folia 服务端 |
| Java | 21 | 必须使用 Java 21 运行和构建 |
| CraftEngine | 26.7.3 | 必装前置,用于承载并发送 OGG 声音资源 |
| Residence | 6.0.1.8 | 必装前置,用于确定播放范围和领地开关 |
| FFmpeg | 内置 4.4.1 | 内置版本支持 Windows/Linux x64,其他平台可配置外部程序 |
plugin.yml 已声明 folia-supported: true。这表示插件允许在 Folia 上加载;项目同时使用统一调度封装、玩家实体调度和线程安全状态表处理实际兼容性。
-
确认服务器已经安装 CraftEngine 26.7.3 和 Residence 6.0.1.8。
-
关闭服务器,将
PlayerMusic-1.1.5-folia.jar放入服务器的plugins/。 -
启动服务器,等待生成
plugins/PlayerMusic/。 -
将
.mp3或.ogg文件放入plugins/PlayerMusic/music/。 -
站在目标领地内执行:
/res set playermusic true -
执行音乐库重载:
/playermusic reload -
如果提示资源有变化,再执行:
/ce reload all -
在线玩家没有收到新资源包时,执行:
/ce feature send-pack -
玩家站在已开启音乐功能的领地内,执行:
/playermusic gui
| 场景 | 实际行为 |
|---|---|
| 播放会话范围 | 每个 Residence 领地各自维护一套会话,不同领地互不影响 |
| 全服播放 | 全服会话独立于 Residence;开始、暂停、切歌和停止全服音乐都不会修改领地会话 |
| 全服与领地同时播放 | 两套声音使用不同声音事件,可同时存在;停止其中一个不会误停另一个 |
| 领地内现有玩家 | 选歌后会收到同一首歌曲的播放调用,共享列表、模式、暂停、停止和切歌状态 |
| 中途进入领地 | 会加入当前会话,但声音会从当前歌曲开头播放 |
| 离开领地 | 默认最多约 20 tick 后停止该领地的声音 |
| 关闭领地 flag | 默认最多约 20 tick 后,领地内玩家停止收到该会话声音 |
| 重新选歌 | 替换当前领地的旧会话,先停止旧声音,再播放新歌曲 |
| 暂停后继续 | 会重新从当前歌曲开头播放,不能从暂停位置续播 |
| 单曲播放 | 当前歌曲结束后停止会话;手动“下一首”仍会立即切换 |
| 随机播放 | 从当前列表随机选择下一首,列表大于一首时不会连续重复同一首 |
| 顺序播放 | 按当前列表顺序播放,到末尾后回到第一首 |
| 收音机 | 从全部音乐中持续随机播放,点击后立即随机开播 |
| 收藏列表会话 | 开播时固定本次会话的收藏歌曲快照,之后修改收藏不会改写正在播放的队列 |
PlayerMusic 同步的是领地播放会话和控制状态,不是客户端音频的精确时间轴。当前 Bukkit/CraftEngine 自定义声音播放接口可以播放或停止声音,但不能让服务器指定“从歌曲第 N 秒开始”。因此:
- 同一时间已经在领地中的玩家会接近同时开始播放。
- 中途进入的玩家会从这首歌的开头开始,而不是追到其他玩家的当前秒数。
- 暂停后继续会重新播放当前歌曲,而不是从暂停位置恢复。
- 服务器仍按公共会话的歌曲时长统一决定何时切换下一首。
GUI 固定为 54 格,歌曲区域每页最多显示 36 首。
- 左键歌曲: 在当前领地播放该歌曲。
- 右键歌曲: 加入收藏或取消收藏。
- 歌曲 Lore 会显示时长、当前列表、播放状态、收藏状态和操作提示。
- 当前歌曲、列表和播放模式会使用附魔光效高亮。
| 选项 | 说明 |
|---|---|
| 全部音乐 | 显示音乐库中的全部歌曲 |
| 我的收藏 | 只显示当前玩家收藏的歌曲 |
| 收音机 | 从全部音乐中持续随机播放 |
| 单曲播放 | 当前歌曲自然结束后停止 |
| 随机播放 | 随机选择下一首,避免连续重复 |
| 顺序播放 | 按列表顺序循环播放 |
底栏还提供页码、上一页、下一页、暂停/继续、停止、下一首和关闭按钮。点击“个人音乐音量”可调节音量:左键增加 10%,右键减少 10%,Shift+点击恢复 100%。调整只影响当前玩家,并保存到当前数据库;正在播放的声音会按新音量从头重播。
主命令别名:/pmusic、/musicbox。
| 命令 | 执行者 | 权限 | 说明 |
|---|---|---|---|
/playermusic |
玩家 | playermusic.use |
不带参数时等同于打开 GUI |
/playermusic gui |
玩家 | playermusic.use |
在当前已启用的领地打开音乐盒 GUI |
/playermusic stop |
玩家 | playermusic.use |
停止玩家当前所在领地的公共播放会话 |
/playermusic stop |
控制台 | 控制台 | 停止服务器中的全服与全部领地播放会话 |
/playermusic global |
玩家 | playermusic.global |
打开全服播放 GUI,不要求位于 Residence 领地 |
/playermusic global stop |
玩家或控制台 | playermusic.global |
只停止全服播放,不停止任何领地音乐 |
/playermusic reload |
玩家或控制台 | playermusic.admin |
重载配置、消息、数据库和音乐资源 |
执行 /playermusic reload 时会关闭已打开的音乐 GUI,并停止现有播放会话。重载过程使用全局互斥锁;已有重载任务运行时,新的请求会收到“正在重载”提示,不会重复转换。
| 权限节点 | 默认值 | 包含功能 |
|---|---|---|
playermusic.use |
所有玩家 | 打开 GUI、选歌、收藏、切换模式、暂停、继续、停止当前领地和切歌 |
playermusic.admin |
仅 OP | 执行 /playermusic reload |
playermusic.global |
仅 OP | 打开全服 GUI 并控制全服播放、暂停、切歌和停止 |
权限节点只控制 PlayerMusic 自己的命令和 GUI。普通 playermusic flag 已向 Residence 默认组开放;顶级 playermusicpriority flag 仍要求 Residence 管理员权限。
LuckPerms 示例:
# 允许默认玩家使用音乐盒
/lp group default permission set playermusic.use true
# 禁止某个组使用音乐盒
/lp group muted permission set playermusic.use false
# 允许管理员热重载插件
/lp group admin permission set playermusic.admin true
# 允许管理员控制全服音乐
/lp group admin permission set playermusic.global true
PlayerMusic 只允许普通玩家修改自己领地的普通音乐 flag,不会授予 /resadmin 或顶级 flag 权限。
PlayerMusic 注册两个默认关闭的 Residence flag:普通 playermusic 与顶级 playermusicpriority。
# 普通领地主开启/关闭普通音乐
/res set playermusic true
/res set playermusic false
# Residence 管理员开启/关闭顶级音乐
/resadmin set playermusicpriority true
/resadmin set playermusicpriority false
- 默认组可使用
/res set playermusic,但不能设置playermusicpriority。 - 顶级 flag 只通过 Residence 管理权限设置;同一位置存在顶级与普通 flag 时,顶级优先。
- 子领地与父领地按继承层级解析:最近的显式设置生效,生效的顶级领地覆盖普通领地。
- 两种 flag 都未开启时不能打开领地 GUI 或开始播放。
- 玩家离开已开启的领地后会停止听到该领地声音。
- 每个领地的播放会话互相独立。
- 修改
config.yml中的residence-flag后必须重启服务器,不能只执行热重载。
.mp3:通过 FFmpeg 转换为 OGG Vorbis。.ogg:直接复制到 CraftEngine 资源包。- 只扫描
music/目录第一层,不递归扫描子目录。 - GUI 显示名称取自文件名,并移除扩展名。
示例:
plugins/PlayerMusic/music/
├─ 夜空中最亮的星.mp3
├─ Summer.ogg
└─ 大厅背景音乐.mp3
插件使用 music-cache.yml 保存源文件 SHA-256、转换结果 SHA-256、时长和转换参数:
- 未变化的歌曲直接复用旧 OGG,不再次调用 FFmpeg。
- 整个音乐目录都未变化时,跳过 CraftEngine 资源重建。
- 提示“音乐文件没有变化”时,不需要执行
/ce reload all。 - 新增、修改或删除歌曲后,需要执行
/ce reload all。 music-index.yml固定“源文件名 -> 声音 ID”,避免歌曲增删后收藏 ID 无故变化。- 重载失败时保留上一版可用的 CraftEngine 资源,不用损坏的新文件覆盖旧包。
生成位置默认为:
plugins/CraftEngine/resources/playermusic/
├─ pack.yml
├─ configuration/sounds.yml
└─ resourcepack/assets/playermusic/sounds/*.ogg
请勿直接修改自动生成的 playermusic 资源目录;源音乐应统一放在 plugins/PlayerMusic/music/。
默认配置:
ffmpeg-command: bundled
ffmpeg-timeout-seconds: 120
ffmpeg-bitrate-kbps: 96bundled 会在首次需要转换 MP3 时释放插件内置的 FFmpeg。ffmpeg-bitrate-kbps 控制 MP3 转 OGG 的目标码率,默认 96kbps;降低它可以减小资源包,提高它会增加音质和体积。旧配置值 ffmpeg 也会自动使用内置版本。需要使用自定义版本时,可填写 PATH 中的命令或绝对路径:
ffmpeg-command: "D:/tools/ffmpeg/bin/ffmpeg.exe"内置程序目前支持 Windows x64 和 Linux x64。其他操作系统或 CPU 架构必须提供外部 FFmpeg。
默认配置位于 plugins/PlayerMusic/config.yml,所有选项都带中文注释。
| 配置项 | 默认值 | 热重载 | 说明 |
|---|---|---|---|
residence-flag |
playermusic |
否 | Residence 自定义 flag,修改后必须重启 |
priority-residence-flag |
playermusicpriority |
否 | 顶级 Residence flag,修改后必须重启 |
player-data-key |
name |
否 | 收藏使用玩家名或 UUID,修改后必须重启 |
database.type |
sqlite |
是 | 可选 sqlite 或 mysql |
database.table-prefix |
playermusic_ |
是 | 仅允许字母、数字、下划线,长度 1-32 |
craftengine-resources |
plugins/CraftEngine/resources |
是 | CraftEngine 资源根目录 |
music-directory |
music |
是 | 相对于插件数据目录的音乐源目录 |
ffmpeg-command |
bundled |
是 | 内置命令、PATH 命令或绝对路径 |
ffmpeg-timeout-seconds |
120 |
是 | 单首 MP3 转换超时秒数,最小为 1 |
ffmpeg-bitrate-kbps |
96 |
是 | MP3 转 OGG 的目标码率,范围 32-320;越低资源包越小 |
fallback-duration-seconds |
180 |
是 | 无法读取 OGG 时长时使用的回退时长 |
volume |
1.0 |
是 | 服务器总音量;最终音量为总音量 × 玩家个人音量 |
sound-category |
RECORDS |
是 | Bukkit 声音分类,默认对应唱片机/唱片音量 |
actionbar |
true |
是 | 是否在动作栏显示播放状态 |
start-delay-ticks |
1 |
是 | 开始播放前延迟,最小 1 tick |
residence-check-interval-ticks |
20 |
是 | 玩家范围和 flag 复查周期,最小 1 tick |
gui-title |
<dark_gray>领地音乐盒 |
是 | 支持 MiniMessage |
page-size |
36 |
是 | 每页歌曲数,范围 1-36 |
messages.yml 也会随 /playermusic reload 重新读取,支持 MiniMessage。动态歌曲名通过安全组件占位符插入,不会被当作 MiniMessage 标签解析。
收藏数据支持 SQLite 和 MySQL,JAR 已包含两种 JDBC 驱动。
database:
type: sqlite
table-prefix: playermusic_
sqlite:
file: favorites.db默认数据库文件为 plugins/PlayerMusic/favorites.db,适合单服直接使用。
database:
type: mysql
table-prefix: playermusic_
mysql:
host: localhost
port: 3306
database: playermusic
username: root
password: "请修改为真实密码"
use-ssl: false修改数据库配置后执行 /playermusic reload 即可热切换。需要注意:
- SQLite 与 MySQL 之间不会自动迁移收藏数据。
- 切换成功后,新收藏读写进入新数据库。
- 切换失败时会保留旧连接或暂时禁用收藏,并在控制台输出错误。
- 不要把包含真实 MySQL 密码的服务器配置提交到公开仓库。
player-data-key: namename:按玩家名保存,适合离线服,默认使用此模式。uuid:按 UUID 保存,适合正版服或 UUID 稳定的服务器。- 修改该值后必须重启服务器,旧标识数据不会自动迁移。
plugins/PlayerMusic/
├─ config.yml # 主配置,带中文注释
├─ messages.yml # MiniMessage 消息配置
├─ favorites.db # 默认 SQLite 收藏数据库
├─ music/ # 管理员放入 MP3/OGG 的目录
├─ music-cache.yml # 音乐增量缓存,请勿手改
├─ music-index.yml # 稳定声音 ID 索引,请勿手改
└─ tools/ # 内置 FFmpeg 首次使用后释放到此处
从旧版升级时,插件会在区分大小写的系统上尝试把 plugins/playermusic 迁移为 plugins/PlayerMusic。升级前必须删除旧 JAR,不能让两个版本同时加载。命令、权限、CraftEngine 命名空间和 Residence flag 继续使用小写 playermusic,原有设置无需重做。
依次检查:
/playermusic reload是否成功。- 资源变化后是否执行了
/ce reload all。 - 是否执行
/ce feature send-pack给在线玩家重新发送资源包。 - 客户端是否接受并成功加载服务器资源包。
- 客户端“唱片机/唱片”音量是否开启。
sound-category是否仍为预期分类。- 当前领地是否执行了
/res set playermusic true或/resadmin set playermusicpriority true。
不会。插件会对源文件和生成的 OGG 计算 SHA-256。未变化的歌曲直接复用,整个音乐目录未变化时还会跳过 CraftEngine 资源重建。
- 新增歌曲:需要。
- 修改歌曲内容:需要。
- 删除歌曲:需要。
- 只修改数据库、GUI 标题、消息、音量等配置:不需要。
- 重载提示“音乐文件没有变化”:不需要。
CraftEngine 最终仍通过客户端声音接口播放 OGG。该接口没有“从指定秒数开始”参数,所以后进入的玩家只能从当前歌曲开头播放。插件可以同步切歌和停止,但不能让客户端跳转到相同音频位置。
- 保持
ffmpeg-command: bundled,让插件使用内置版本。 - 确认操作系统为 Windows/Linux x64。
- 其他平台请安装 FFmpeg,并在配置中填写绝对路径。
- 资源包过大时可降低
ffmpeg-bitrate-kbps后重新执行/playermusic reload。
SQLite 和 MySQL 是两套独立数据源,插件不会自动迁移。切回原数据库仍可读取原数据;需要迁移时应由管理员自行处理数据库记录。
residence-flag、priority-residence-flag 或 player-data-key 发生变化时不能热应用。恢复原值后再次重载,或者完整重启服务器。
项目不提交 Residence 第三方 JAR。构建时必须显式指定 Residence 6.0.1.8:
gradlew clean test build -PresidenceJar=D:\path\to\Residence6.0.1.8.jar
构建产物:
build/libs/PlayerMusic-1.1.5-folia.jar
最终 JAR 包含 MySQL、SQLite JDBC 驱动,以及 JAVE2 3.5.0 提供的 Windows/Linux x64 FFmpeg 4.4.1 原生程序。
PlayerMusic 使用 GNU GPL v3.0 开源。
最终 JAR 通过 JAVE2 3.5.0 携带 FFmpeg 4.4.1 Windows/Linux x64 原生程序。JAVE2 原生包和该 FFmpeg 构建均按 GPL v3.0 分发,与本项目许可证保持一致。