Tags
Creators
Details
Licensed MIT
Published 10 months ago
Updated 3 weeks ago
All versions
3.2.0
Release
BS CustomJukebox 3.2.03 months ago 136
Compatibility
Minecraft: Java Edition
26.2
26.1.x
1.21.4–1.21.11
Platforms
Folia
Paper
Purpur
Spigot
Changes
[3.2.0] - 2026-07-12
Added
- Configurable "Now Playing" announcements: the title shown in the center of the screen and the actionbar message when a custom disc starts can now be disabled individually via
playback.show-titleandplayback.show-actionbarinconfig.json(both default totrue, matching previous behavior). - Automatic config merging: on load,
config.jsonanddisc.jsonare compared against the plugin's bundled defaults and any keys introduced by a newer version are added automatically, without overwriting existing values. New options (like theshow-*flags above) now appear in existing config files after an update/reloadinstead of having to be added by hand. Indisc.jsonthediscs/categories/playlistssections are treated as user content and are never re-seeded with the default examples, so deleted example discs do not reappear. - Configurable backup count: the number of timestamped
config/discbackups kept is now set viasettings.max-backups(default5, same as before). Set it to0to disable backups entirely — existing backup files are then pruned on the next save. Values are clamped to a maximum of 100. Previously the count was hard-coded to 5.
Fixed
- GUI system rebuilt on InventoryHolder: All plugin GUIs (Admin panel, disc/category/playlist editors, jukebox disc GUI) are now identified via a custom
InventoryHolderinstead of window titles and per-player context maps. This fixes a family of serious bugs:- Closing a GUI with ESC no longer leaves a stale session that hijacked clicks in chests/containers opened afterwards (previously this could overwrite real chest contents with GUI items and spawn free disc copies via the delete-confirmation dialog).
- Opening the playlist editor or disc editor from the admin panel no longer breaks their click handling (the close event of the previous menu wiped the new context).
- The same click is no longer processed by two GUIs at once (admin panel + disc editor).
- The jukebox disc GUI no longer depends on a lossy title round-trip — hex/gradient
gui-titlevalues previously broke the comparison and made GUI items freely extractable. /cjb guiuses the same holder-tagged inventory, and all GUI click handlers cancel the click before any session/permission checks, so a missing session can never make GUI items extractable.
- Item safety in GUIs:
InventoryDragEventis now cancelled in all GUIs (items could previously be dragged into a GUI and lost), and collect-to-cursor double-clicks can no longer pull items out. - Logout cleanup: All GUI/wizard sessions are cleared on player quit. Previously a player who logged out mid-wizard had every chat message swallowed and processed as wizard input after rejoining.
- Folia: auto-stop/loop tasks are now cancellable:
SchedulerUtilreturns a platform-independentTaskHandle(wrapping Folia'sScheduledTask.cancel()). Stale auto-stop tasks could previously stop a newer playback at the same jukebox (e.g. disc B stopped mid-song at disc A's old end time), and orphaned loop tasks multiplied. Tasks additionally verify they still belong to the same playback session when firing. - Playlists survive volume/mute restarts:
/cjb mute,/cjb unmuteand/cjb volume <x> restartno longer wipe the running playlist queue (playback previously went silent after the current track). - Skeleton-kill fragment drops actually work: the guaranteed drop when a skeleton kills a creeper never triggered (
getKiller()only returns players). Now resolved via the last damage cause, including arrows, covering Skeleton/Stray/Bogged. - Off-hand discs are recognized: interaction handlers now use the hand that triggered the event. Off-hand discs previously got inserted by vanilla without the custom sound; the GUI also opened twice. With the GUI enabled, an off-hand disc now takes priority over opening the GUI.
- Ejecting by disc swap stops the sound: right-clicking an occupied jukebox while holding another disc lets vanilla eject the record — the custom sound now stops with it (previously it kept playing over the empty jukebox).
- Hopper support: a hopper extracting the disc stops the custom sound (it previously played to the end); a hopper inserting a custom disc starts the custom sound. Extraction is verified one tick later so transfers cancelled by protection plugins don't cut the music.
- Disc identity via PersistentDataContainer: disc and fragment items now carry their ID in the PDC. Two discs sharing the same CustomModelData are no longer confused when editing/deleting/giving (legacy items still match by model data). Note: deleting a disc and recreating it under a new ID invalidates already-issued items of the old ID — they intentionally no longer match by texture alone.
- Thread-safety on Folia: disc/category/playlist/language registries are now
ConcurrentHashMap(a/cjb reloadcould previously throwConcurrentModificationExceptionin event handlers on other region threads);UpdateCheckerresults, mute state and playback flags arevolatile;onDisableno longer calls the Bukkit scheduler on Folia (threwUnsupportedOperationException). - config.json saves atomically (temp file + atomic move, like disc.json) and
/cjb reloadreloads the persisted mute state. - Validation restored: disc/category/playlist IDs must be
[A-Za-z0-9_-]+(IDs with spaces were previously creatable but unusable in commands); sound keys are validated as resource locations again — the wizard's error messages for these cases were dead code. - Gradients fixed:
<gradient:#RRGGBB:#RRGGBB>was destroyed by the hex pre-pass inAdventureUtiland never rendered; hex colors are no longer downsampled to legacy codes on serialization. /cjb volume: tab-completion no longer suggests locale-formatted values like0,5that the parser rejects;NaNno longer passes the range check.- Permission
customjukebox.fragmentis now actually checked by/cjb fragment(previously it requiredcustomjukebox.give, and the declared node was dead). Migration note: servers that grantedcustomjukebox.givedirectly (not viacustomjukebox.admin) must additionally grantcustomjukebox.fragmentto keep/cjb fragmentaccess. - Playback tracking leak: discs without a configured duration no longer leave a tracking entry behind forever (fallback cleanup after 1 hour);
loopwithout a duration now logs a warning instead of being silently ignored. - Orphan cleanup: deleting a disc removes it from all playlists; deleting a category detaches it from its discs.
- Deleting playlists/categories in the admin panel now requires a confirming second right-click (they were previously deleted instantly and irreversibly); failed deletions report an error.
- Chat-input hardening: input modes are consumed atomically (rapid double messages were processed twice), the disc editor's chat handler respects cancelled events, and all chat/wizard handlers re-check the admin permission before applying input.
- Log hygiene: per-click and per-playlist-progression INFO logs are now behind the debug flag.
Changed
ParrotDanceListenerno longer schedules hundreds of empty per-parrot tasks (the vanilla dance animation cannot be forced via the Bukkit API); it now shows a short note-particle burst above nearby parrots instead.
Performance
- Fewer disk writes on delete: deleting a disc or category now rewrites
disc.jsona single time instead of once per affected playlist/disc (each of which previously also created a backup and scanned the backup directory). - Folia scheduler: reflection handles are resolved once against the public scheduler interfaces and cached, instead of being looked up on every schedule/cancel call on the playback hot path.
- Cheaper event hot paths: hopper transfers are filtered by item/inventory type before any block-state lookup, and GUI event handlers use a non-snapshotting holder check — so hopper-heavy servers and players interacting with vanilla containers no longer pay for block-entity snapshots on every event.
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:SFWdW3tI:i2yY7AAH"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:SFWdW3tI:i2yY7AAH"
}

