From 267f61030549063d34c71833757f0b0a1eb26069 Mon Sep 17 00:00:00 2001 From: XxFran10xX <318299142+XxFran10xX@users.noreply.github.com> Date: Sat, 31 Oct 2026 11:53:40 +0100 Subject: [PATCH] perf: read inventory holders without copying the block Opening and clicking the studio menu was slow on Main because every getHolder() call on a container inventory copies the whole block state, so a click with a large chest open could stall the server. The menu and the jukebox hopper check now use getHolder(false), which returns the same holder without the copy. The code only checks the holder's type, so behaviour does not change. No call site needed the copying form. Co-Authored-By: Claude Opus 5.5 --- .../musicalinstruments/studio/StudioListener.java | 2 +- .../tfminecraft/musicalinstruments/studio/StudioMenu.java | 6 +++--- .../musicalinstruments/studio/StudioListenerTest.java | 4 ++-- .../musicalinstruments/studio/StudioMenuTest.java | 1 + 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/tfminecraft/musicalinstruments/studio/StudioListener.java b/src/main/java/net/tfminecraft/musicalinstruments/studio/StudioListener.java index bfc3470..4804559 100644 --- a/src/main/java/net/tfminecraft/musicalinstruments/studio/StudioListener.java +++ b/src/main/java/net/tfminecraft/musicalinstruments/studio/StudioListener.java @@ -129,7 +129,7 @@ public void breakBlock(BlockBreakEvent event) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void hopper(InventoryMoveItemEvent event) { // Custom editions require our insertion lifecycle; vanilla discs retain hopper behavior. - if (studio.discs().custom(event.getItem()) && event.getDestination().getHolder() instanceof Jukebox) { + if (studio.discs().custom(event.getItem()) && event.getDestination().getHolder(false) instanceof Jukebox) { event.setCancelled(true); } } diff --git a/src/main/java/net/tfminecraft/musicalinstruments/studio/StudioMenu.java b/src/main/java/net/tfminecraft/musicalinstruments/studio/StudioMenu.java index 3c707e6..03ddd51 100644 --- a/src/main/java/net/tfminecraft/musicalinstruments/studio/StudioMenu.java +++ b/src/main/java/net/tfminecraft/musicalinstruments/studio/StudioMenu.java @@ -185,7 +185,7 @@ private ItemStack button(Material material, String title, NamedTextColor color, @EventHandler public void click(InventoryClickEvent event) { - if (!(event.getView().getTopInventory().getHolder() instanceof Holder holder)) return; + if (!(event.getView().getTopInventory().getHolder(false) instanceof Holder holder)) return; // Normal inventory clicks let the player pick up and put down the physical disc. if (event.getRawSlot() >= holder.inventory.getSize() && (event.getClick() == ClickType.LEFT || event.getClick() == ClickType.RIGHT)) return; @@ -312,7 +312,7 @@ private void refresh() { @EventHandler public void drag(InventoryDragEvent event) { - if (!(event.getView().getTopInventory().getHolder() instanceof Holder holder)) return; + if (!(event.getView().getTopInventory().getHolder(false) instanceof Holder holder)) return; if (event.getRawSlots().stream().noneMatch(slot -> slot < holder.inventory.getSize())) return; event.setCancelled(true); if (holder.screen == Screen.EDITOR && event.getRawSlots().equals(java.util.Set.of(50)) @@ -334,7 +334,7 @@ public void close() { // The holder of the player's open container; MockBukkit reports no top inventory when none is open. private static InventoryHolder open(Player player) { - return player.getOpenInventory().getTopInventory() instanceof Inventory top ? top.getHolder() : null; + return player.getOpenInventory().getTopInventory() instanceof Inventory top ? top.getHolder(false) : null; } private static final class Holder implements InventoryHolder { diff --git a/src/test/java/net/tfminecraft/musicalinstruments/studio/StudioListenerTest.java b/src/test/java/net/tfminecraft/musicalinstruments/studio/StudioListenerTest.java index c5f11f9..774c561 100644 --- a/src/test/java/net/tfminecraft/musicalinstruments/studio/StudioListenerTest.java +++ b/src/test/java/net/tfminecraft/musicalinstruments/studio/StudioListenerTest.java @@ -300,10 +300,10 @@ void worldLifecycleAndHoppersRespectCustomRecordingPlayback() { verify(studio, times(2)).unload(world.getUID(), 2, 3); var source = mock(org.bukkit.inventory.Inventory.class); var destination = mock(org.bukkit.inventory.Inventory.class); - when(destination.getHolder()).thenReturn(box); + when(destination.getHolder(false)).thenReturn(box); var blocked = new org.bukkit.event.inventory.InventoryMoveItemEvent(source, disc, destination, true); listener.hopper(blocked); assertTrue(blocked.isCancelled()); - when(destination.getHolder()).thenReturn(null); + when(destination.getHolder(false)).thenReturn(null); var ordinary = new org.bukkit.event.inventory.InventoryMoveItemEvent(source, disc, destination, true); listener.hopper(ordinary); assertFalse(ordinary.isCancelled()); when(discs.custom(disc)).thenReturn(false); diff --git a/src/test/java/net/tfminecraft/musicalinstruments/studio/StudioMenuTest.java b/src/test/java/net/tfminecraft/musicalinstruments/studio/StudioMenuTest.java index f241bf3..9de876c 100644 --- a/src/test/java/net/tfminecraft/musicalinstruments/studio/StudioMenuTest.java +++ b/src/test/java/net/tfminecraft/musicalinstruments/studio/StudioMenuTest.java @@ -88,6 +88,7 @@ void setup() throws Exception { when(scheduler.runTaskTimer(eq(plugin), any(Runnable.class), eq(10L), eq(10L))).thenReturn(refreshTask); when(server.createInventory(any(InventoryHolder.class), anyInt(), any(Component.class))).thenAnswer(call -> { when(inventory.getHolder()).thenReturn(call.getArgument(0)); + when(inventory.getHolder(false)).thenReturn(call.getArgument(0)); when(inventory.getSize()).thenReturn(call.getArgument(1)); return inventory; });