From d8beaa7fda5ddd6a3eb9a2e719c92bd412833b98 Mon Sep 17 00:00:00 2001 From: GoodrichDev <147654894+GoodrichDev@users.noreply.github.com> Date: Tue, 21 Apr 2026 00:39:34 -0700 Subject: [PATCH 1/5] Preserve hidden ender chest rows when permissions change - Use the full backing ender chest storage size when loading and saving ender chest contents. Permission-based ender chest rows still limit the visible/accessible container size, but hidden slots are now preserved in persistent player data. This prevents items in higher rows from being dropped from EnderItems when a player loses permissions such as `purpur.enderchest.rows.six`. --- .../0003-Barrels-and-enderchests-6-rows.patch | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch index d2c0d15a2..af1bf9102 100644 --- a/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch +++ b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch @@ -107,6 +107,31 @@ index 4df3a32faf85595372f4b250482d852c985ea3ab..8347150af55119d772b797e79be412f7 public void setActiveChest(final EnderChestBlockEntity activeChest) { this.activeChest = activeChest; } +@@ -40,19 +47,21 @@ public class PlayerEnderChestContainer extends SimpleContainer { + } + + public void fromSlots(final ValueInput.TypedInputList list) { +- for (int i = 0; i < this.getContainerSize(); i++) { ++ int storageSlotCount = super.getContainerSize(); // Purpur - preserve hidden ender chest rows ++ for (int i = 0; i < storageSlotCount; i++) { + this.setItem(i, ItemStack.EMPTY); + } + + for (ItemStackWithSlot item : list) { +- if (item.isValidInContainer(this.getContainerSize())) { ++ if (item.isValidInContainer(storageSlotCount)) { + this.setItem(item.slot(), item.stack()); + } + } + } + + public void storeAsSlots(final ValueOutput.TypedOutputList output) { +- for (int i = 0; i < this.getContainerSize(); i++) { ++ int storageSlotCount = super.getContainerSize(); // Purpur - preserve hidden ender chest rows ++ for (int i = 0; i < storageSlotCount; i++) { + ItemStack itemStack = this.getItem(i); + if (!itemStack.isEmpty()) { + output.add(new ItemStackWithSlot(i, itemStack)); diff --git a/net/minecraft/world/level/block/EnderChestBlock.java b/net/minecraft/world/level/block/EnderChestBlock.java index 24a2c411da0ebbb7f97d621bb76ff686621f9aae..7ba9e8f6414246b589ff423fac63f80505326505 100644 --- a/net/minecraft/world/level/block/EnderChestBlock.java From 3931d693ff90ae746fd561a9d9b96c8d4894343a Mon Sep 17 00:00:00 2001 From: GoodrichDev <147654894+GoodrichDev@users.noreply.github.com> Date: Fri, 24 Apr 2026 16:45:51 -0700 Subject: [PATCH 2/5] Add config for hidden ender chest item persistence Add a settings.blocks.ender_chest.preserve-hidden-items option to control the recent change that preserves permission-hidden ender chest rows. The option defaults to true, and disabling it restores the legacy behavior that trims saved contents to the visible row count. --- .../features/0003-Barrels-and-enderchests-6-rows.patch | 4 ++-- .../src/main/java/org/purpurmc/purpur/PurpurConfig.java | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch index af1bf9102..ddb7ac4a9 100644 --- a/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch +++ b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch @@ -112,7 +112,7 @@ index 4df3a32faf85595372f4b250482d852c985ea3ab..8347150af55119d772b797e79be412f7 public void fromSlots(final ValueInput.TypedInputList list) { - for (int i = 0; i < this.getContainerSize(); i++) { -+ int storageSlotCount = super.getContainerSize(); // Purpur - preserve hidden ender chest rows ++ int storageSlotCount = org.purpurmc.purpur.PurpurConfig.enderChestPreserveHiddenItems ? super.getContainerSize() : this.getContainerSize(); // Purpur - preserve hidden ender chest rows + for (int i = 0; i < storageSlotCount; i++) { this.setItem(i, ItemStack.EMPTY); } @@ -127,7 +127,7 @@ index 4df3a32faf85595372f4b250482d852c985ea3ab..8347150af55119d772b797e79be412f7 public void storeAsSlots(final ValueOutput.TypedOutputList output) { - for (int i = 0; i < this.getContainerSize(); i++) { -+ int storageSlotCount = super.getContainerSize(); // Purpur - preserve hidden ender chest rows ++ int storageSlotCount = org.purpurmc.purpur.PurpurConfig.enderChestPreserveHiddenItems ? super.getContainerSize() : this.getContainerSize(); // Purpur - preserve hidden ender chest rows + for (int i = 0; i < storageSlotCount; i++) { ItemStack itemStack = this.getItem(i); if (!itemStack.isEmpty()) { diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/purpur-server/src/main/java/org/purpurmc/purpur/PurpurConfig.java index a337df7cd..c54ff3b8f 100644 --- a/purpur-server/src/main/java/org/purpurmc/purpur/PurpurConfig.java +++ b/purpur-server/src/main/java/org/purpurmc/purpur/PurpurConfig.java @@ -329,6 +329,7 @@ private static void commandSettings() { public static int barrelRows = 3; public static boolean enderChestSixRows = false; public static boolean enderChestPermissionRows = false; + public static boolean enderChestPreserveHiddenItems = true; public static boolean cryingObsidianValidForPortalFrame = false; public static int beeInsideBeeHive = 3; public static boolean anvilCumulativeCost = true; @@ -373,6 +374,7 @@ private static void blockSettings() { enderChestSixRows = getBoolean("settings.blocks.ender_chest.six-rows", enderChestSixRows); org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27); enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows); + enderChestPreserveHiddenItems = getBoolean("settings.blocks.ender_chest.preserve-hidden-items", enderChestPreserveHiddenItems); cryingObsidianValidForPortalFrame = getBoolean("settings.blocks.crying_obsidian.valid-for-portal-frame", cryingObsidianValidForPortalFrame); beeInsideBeeHive = getInt("settings.blocks.beehive.max-bees-inside", beeInsideBeeHive); anvilCumulativeCost = getBoolean("settings.blocks.anvil.cumulative-cost", anvilCumulativeCost); From 93147ff87db2124034ae4f10fef3c2ea1966e101 Mon Sep 17 00:00:00 2001 From: granny Date: Fri, 1 May 2026 15:35:05 -0700 Subject: [PATCH 3/5] hardcode the size when saving/loading 6-row enderchest --- .../features/0003-Barrels-and-enderchests-6-rows.patch | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch index 6966dc46a..8f6917038 100644 --- a/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch +++ b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch @@ -84,7 +84,7 @@ index e77bfcd31cdcfd5836dc5db561e3fe2bc552a4b1..afa2ead0548766669201526d83f351c2 return new ChestMenu(MenuType.GENERIC_9x6, containerId, inventory, container, 6); } diff --git a/net/minecraft/world/inventory/PlayerEnderChestContainer.java b/net/minecraft/world/inventory/PlayerEnderChestContainer.java -index 4df3a32faf85595372f4b250482d852c985ea3ab..8347150af55119d772b797e79be412f7e17a0f1f 100644 +index 4df3a32faf85595372f4b250482d852c985ea3ab..b792b83a67293baaab02f5e46bad88afbfdfe0dc 100644 --- a/net/minecraft/world/inventory/PlayerEnderChestContainer.java +++ b/net/minecraft/world/inventory/PlayerEnderChestContainer.java @@ -26,11 +26,18 @@ public class PlayerEnderChestContainer extends SimpleContainer { @@ -112,7 +112,7 @@ index 4df3a32faf85595372f4b250482d852c985ea3ab..8347150af55119d772b797e79be412f7 public void fromSlots(final ValueInput.TypedInputList list) { - for (int i = 0; i < this.getContainerSize(); i++) { -+ int storageSlotCount = org.purpurmc.purpur.PurpurConfig.enderChestPreserveHiddenItems ? super.getContainerSize() : this.getContainerSize(); // Purpur - preserve hidden ender chest rows ++ int storageSlotCount = org.purpurmc.purpur.PurpurConfig.enderChestSixRows && org.purpurmc.purpur.PurpurConfig.enderChestPreserveHiddenItems ? 54 : this.getContainerSize(); // Purpur - Barrels and enderchests 6 rows + for (int i = 0; i < storageSlotCount; i++) { this.setItem(i, ItemStack.EMPTY); } @@ -127,7 +127,7 @@ index 4df3a32faf85595372f4b250482d852c985ea3ab..8347150af55119d772b797e79be412f7 public void storeAsSlots(final ValueOutput.TypedOutputList output) { - for (int i = 0; i < this.getContainerSize(); i++) { -+ int storageSlotCount = org.purpurmc.purpur.PurpurConfig.enderChestPreserveHiddenItems ? super.getContainerSize() : this.getContainerSize(); // Purpur - preserve hidden ender chest rows ++ int storageSlotCount = org.purpurmc.purpur.PurpurConfig.enderChestSixRows && org.purpurmc.purpur.PurpurConfig.enderChestPreserveHiddenItems ? 54 : this.getContainerSize(); // Purpur - Barrels and enderchests 6 rows + for (int i = 0; i < storageSlotCount; i++) { ItemStack itemStack = this.getItem(i); if (!itemStack.isEmpty()) { From 1aca15d90f57e216bdfbae0b1bb60eeba60d3e11 Mon Sep 17 00:00:00 2001 From: granny Date: Fri, 1 May 2026 15:42:29 -0700 Subject: [PATCH 4/5] rename option from preserve-hidden-items to persist-hidden-rows --- .../features/0003-Barrels-and-enderchests-6-rows.patch | 6 +++--- .../src/main/java/org/purpurmc/purpur/PurpurConfig.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch index 8f6917038..bfd07454a 100644 --- a/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch +++ b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch @@ -84,7 +84,7 @@ index e77bfcd31cdcfd5836dc5db561e3fe2bc552a4b1..afa2ead0548766669201526d83f351c2 return new ChestMenu(MenuType.GENERIC_9x6, containerId, inventory, container, 6); } diff --git a/net/minecraft/world/inventory/PlayerEnderChestContainer.java b/net/minecraft/world/inventory/PlayerEnderChestContainer.java -index 4df3a32faf85595372f4b250482d852c985ea3ab..b792b83a67293baaab02f5e46bad88afbfdfe0dc 100644 +index 4df3a32faf85595372f4b250482d852c985ea3ab..e41cb93b2ebc51a03bec3df672a570d90050eca1 100644 --- a/net/minecraft/world/inventory/PlayerEnderChestContainer.java +++ b/net/minecraft/world/inventory/PlayerEnderChestContainer.java @@ -26,11 +26,18 @@ public class PlayerEnderChestContainer extends SimpleContainer { @@ -112,7 +112,7 @@ index 4df3a32faf85595372f4b250482d852c985ea3ab..b792b83a67293baaab02f5e46bad88af public void fromSlots(final ValueInput.TypedInputList list) { - for (int i = 0; i < this.getContainerSize(); i++) { -+ int storageSlotCount = org.purpurmc.purpur.PurpurConfig.enderChestSixRows && org.purpurmc.purpur.PurpurConfig.enderChestPreserveHiddenItems ? 54 : this.getContainerSize(); // Purpur - Barrels and enderchests 6 rows ++ int storageSlotCount = org.purpurmc.purpur.PurpurConfig.enderChestSixRows && org.purpurmc.purpur.PurpurConfig.enderChestPersistHiddenRows ? 54 : this.getContainerSize(); // Purpur - Barrels and enderchests 6 rows + for (int i = 0; i < storageSlotCount; i++) { this.setItem(i, ItemStack.EMPTY); } @@ -127,7 +127,7 @@ index 4df3a32faf85595372f4b250482d852c985ea3ab..b792b83a67293baaab02f5e46bad88af public void storeAsSlots(final ValueOutput.TypedOutputList output) { - for (int i = 0; i < this.getContainerSize(); i++) { -+ int storageSlotCount = org.purpurmc.purpur.PurpurConfig.enderChestSixRows && org.purpurmc.purpur.PurpurConfig.enderChestPreserveHiddenItems ? 54 : this.getContainerSize(); // Purpur - Barrels and enderchests 6 rows ++ int storageSlotCount = org.purpurmc.purpur.PurpurConfig.enderChestSixRows && org.purpurmc.purpur.PurpurConfig.enderChestPersistHiddenRows ? 54 : this.getContainerSize(); // Purpur - Barrels and enderchests 6 rows + for (int i = 0; i < storageSlotCount; i++) { ItemStack itemStack = this.getItem(i); if (!itemStack.isEmpty()) { diff --git a/purpur-server/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/purpur-server/src/main/java/org/purpurmc/purpur/PurpurConfig.java index c54ff3b8f..7e0c527a1 100644 --- a/purpur-server/src/main/java/org/purpurmc/purpur/PurpurConfig.java +++ b/purpur-server/src/main/java/org/purpurmc/purpur/PurpurConfig.java @@ -329,7 +329,7 @@ private static void commandSettings() { public static int barrelRows = 3; public static boolean enderChestSixRows = false; public static boolean enderChestPermissionRows = false; - public static boolean enderChestPreserveHiddenItems = true; + public static boolean enderChestPersistHiddenRows = true; public static boolean cryingObsidianValidForPortalFrame = false; public static int beeInsideBeeHive = 3; public static boolean anvilCumulativeCost = true; @@ -374,7 +374,7 @@ private static void blockSettings() { enderChestSixRows = getBoolean("settings.blocks.ender_chest.six-rows", enderChestSixRows); org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27); enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows); - enderChestPreserveHiddenItems = getBoolean("settings.blocks.ender_chest.preserve-hidden-items", enderChestPreserveHiddenItems); + enderChestPersistHiddenRows = getBoolean("settings.blocks.ender_chest.persist-hidden-rows", enderChestPersistHiddenRows); cryingObsidianValidForPortalFrame = getBoolean("settings.blocks.crying_obsidian.valid-for-portal-frame", cryingObsidianValidForPortalFrame); beeInsideBeeHive = getInt("settings.blocks.beehive.max-bees-inside", beeInsideBeeHive); anvilCumulativeCost = getBoolean("settings.blocks.anvil.cumulative-cost", anvilCumulativeCost); From b0eb07c11f224b9354fcb45e5655cdf88b460ac8 Mon Sep 17 00:00:00 2001 From: granny Date: Fri, 1 May 2026 15:45:38 -0700 Subject: [PATCH 5/5] add missing Purpur comments --- .../0003-Barrels-and-enderchests-6-rows.patch | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch index bfd07454a..b1cdbe6c5 100644 --- a/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch +++ b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch @@ -84,7 +84,7 @@ index e77bfcd31cdcfd5836dc5db561e3fe2bc552a4b1..afa2ead0548766669201526d83f351c2 return new ChestMenu(MenuType.GENERIC_9x6, containerId, inventory, container, 6); } diff --git a/net/minecraft/world/inventory/PlayerEnderChestContainer.java b/net/minecraft/world/inventory/PlayerEnderChestContainer.java -index 4df3a32faf85595372f4b250482d852c985ea3ab..e41cb93b2ebc51a03bec3df672a570d90050eca1 100644 +index 4df3a32faf85595372f4b250482d852c985ea3ab..33c4c2cc8b488df5276d21e0f4e29cc0072a7682 100644 --- a/net/minecraft/world/inventory/PlayerEnderChestContainer.java +++ b/net/minecraft/world/inventory/PlayerEnderChestContainer.java @@ -26,11 +26,18 @@ public class PlayerEnderChestContainer extends SimpleContainer { @@ -107,19 +107,21 @@ index 4df3a32faf85595372f4b250482d852c985ea3ab..e41cb93b2ebc51a03bec3df672a570d9 public void setActiveChest(final EnderChestBlockEntity activeChest) { this.activeChest = activeChest; } -@@ -40,19 +47,21 @@ public class PlayerEnderChestContainer extends SimpleContainer { +@@ -40,19 +47,25 @@ public class PlayerEnderChestContainer extends SimpleContainer { } public void fromSlots(final ValueInput.TypedInputList list) { - for (int i = 0; i < this.getContainerSize(); i++) { -+ int storageSlotCount = org.purpurmc.purpur.PurpurConfig.enderChestSixRows && org.purpurmc.purpur.PurpurConfig.enderChestPersistHiddenRows ? 54 : this.getContainerSize(); // Purpur - Barrels and enderchests 6 rows ++ // Purpur start - Barrels and enderchests 6 rows ++ int storageSlotCount = org.purpurmc.purpur.PurpurConfig.enderChestSixRows && org.purpurmc.purpur.PurpurConfig.enderChestPersistHiddenRows ? 54 : this.getContainerSize(); + for (int i = 0; i < storageSlotCount; i++) { ++ // Purpur end - Barrels and enderchests 6 rows this.setItem(i, ItemStack.EMPTY); } for (ItemStackWithSlot item : list) { - if (item.isValidInContainer(this.getContainerSize())) { -+ if (item.isValidInContainer(storageSlotCount)) { ++ if (item.isValidInContainer(storageSlotCount)) { // Purpur - Barrels and enderchests 6 rows this.setItem(item.slot(), item.stack()); } } @@ -127,8 +129,10 @@ index 4df3a32faf85595372f4b250482d852c985ea3ab..e41cb93b2ebc51a03bec3df672a570d9 public void storeAsSlots(final ValueOutput.TypedOutputList output) { - for (int i = 0; i < this.getContainerSize(); i++) { ++ // Purpur start - Barrels and enderchests 6 rows + int storageSlotCount = org.purpurmc.purpur.PurpurConfig.enderChestSixRows && org.purpurmc.purpur.PurpurConfig.enderChestPersistHiddenRows ? 54 : this.getContainerSize(); // Purpur - Barrels and enderchests 6 rows + for (int i = 0; i < storageSlotCount; i++) { ++ // Purpur end - Barrels and enderchests 6 rows ItemStack itemStack = this.getItem(i); if (!itemStack.isEmpty()) { output.add(new ItemStackWithSlot(i, itemStack));