From 1988d9e773264294b2d04e3149c23fc50e392c29 Mon Sep 17 00:00:00 2001 From: rasmus123d <59487370+RasmusKD@users.noreply.github.com> Date: Sun, 2 Aug 2026 14:52:55 +0200 Subject: [PATCH 1/2] Fix jukebox disc duplication and item loss in pipes The jukebox branch created a new disc ItemStack and only emptied the jukebox if every item was delivered. When the network was full or missing, the copy dropped at the piston while the original kept playing, so a clocked piston printed discs. It was also the only branch that never fed leftovers, so an empty jukebox silently destroyed any items a pipe request delivered into it. Now the disc is removed from the jukebox before the event fires, and undelivered items go through leftovers like the other branches. --- .../sk89q/craftbook/mechanics/pipe/Pipes.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/sk89q/craftbook/mechanics/pipe/Pipes.java b/src/main/java/com/sk89q/craftbook/mechanics/pipe/Pipes.java index 19b134e53..a8ddea815 100644 --- a/src/main/java/com/sk89q/craftbook/mechanics/pipe/Pipes.java +++ b/src/main/java/com/sk89q/craftbook/mechanics/pipe/Pipes.java @@ -429,28 +429,30 @@ private void startPipe(Block block, List items, boolean request) { Jukebox juke = (Jukebox) fac.getState(); if (juke.getPlaying() != Material.AIR) { + // Remove the disc before firing the event. Inferring "the disc left + // the jukebox" from the item list afterwards duplicated the disc + // whenever delivery was refused: a copy dropped at the piston while + // the original kept playing. items.add(new ItemStack(juke.getPlaying())); + juke.setPlaying(Material.AIR); + juke.update(); + } + if (!items.isEmpty()) { PipeSuckEvent event = new PipeSuckEvent(block, new ArrayList<>(items), fac); Bukkit.getPluginManager().callEvent(event); items.clear(); items.addAll(event.getItems()); - if (!event.isCancelled()) { + if (!event.isCancelled() && !items.isEmpty()) { visitedPipes.add(fac.getLocation().toVector()); searchNearbyPipes(block, visitedPipes, items); } - - if (!items.isEmpty()) { - for (ItemStack item : items) { - if (!ItemUtil.isStackValid(item)) continue; - block.getWorld().dropItem(BlockUtil.getBlockCentre(block), item); - } - } else { - juke.setPlaying(Material.AIR); - juke.update(); - } } + // Route undelivered items through leftovers like the other branches; + // this was the only branch that never fed it, so an empty jukebox + // destroyed any items a request delivered into it. + leftovers.addAll(items); } else { PipeSuckEvent event = new PipeSuckEvent(block, new ArrayList<>(items), fac); Bukkit.getPluginManager().callEvent(event); From 3e1006c7c60a335aa720cf06edb9539d88feacc7 Mon Sep 17 00:00:00 2001 From: rasmus123d <59487370+RasmusKD@users.noreply.github.com> Date: Sun, 2 Aug 2026 15:04:44 +0200 Subject: [PATCH 2/2] Fix pipes destroying the disc in a playing jukebox The put-path test was inverted: a disc was only inserted when the jukebox was already playing, so an empty jukebox never accepted one, and when it did fire, setPlaying overwrote the playing disc and destroyed it. Now only an empty jukebox accepts a disc, and a disc that does not fit flows on through the pipe like any other undelivered item. --- .../com/sk89q/craftbook/mechanics/pipe/Pipes.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/sk89q/craftbook/mechanics/pipe/Pipes.java b/src/main/java/com/sk89q/craftbook/mechanics/pipe/Pipes.java index a8ddea815..6a4a3d2b8 100644 --- a/src/main/java/com/sk89q/craftbook/mechanics/pipe/Pipes.java +++ b/src/main/java/com/sk89q/craftbook/mechanics/pipe/Pipes.java @@ -182,14 +182,21 @@ private void searchNearbyPipes(Block block, Set visitedPipes, List its = new ArrayList<>(event.getItems()); - if (juke.getPlaying() != Material.AIR) { + // Only an empty jukebox accepts a disc. The test was inverted, so + // an empty jukebox never took one, and a playing one had its disc + // overwritten by setPlaying and destroyed. Discs that do not fit + // stay in the list and flow on through the pipe. + if (juke.getPlaying() == Material.AIR) { Iterator iter = its.iterator(); while (iter.hasNext()) { ItemStack st = iter.next(); if (!st.getType().isRecord()) continue; juke.setPlaying(st.getType()); juke.update(); - iter.remove(); + if (st.getAmount() > 1) + st.setAmount(st.getAmount() - 1); + else + iter.remove(); break; } }