Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
private @Nullable EnderChestBlockEntity activeChest;
-
- public PlayerEnderChestContainer() {
- super(27);
+ // CraftBukkit start
+ private final Player owner;
+
Expand All @@ -20,7 +21,7 @@
+ }
+
+ public PlayerEnderChestContainer(Player owner) {
super(27);
+ super(io.papermc.paper.configuration.GlobalConfiguration.get().misc.enderChestSlotCount); // Paper - configurable ender chest slot count
+ this.owner = owner;
+ // CraftBukkit end
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
--- a/net/minecraft/world/level/block/EnderChestBlock.java
+++ b/net/minecraft/world/level/block/EnderChestBlock.java
@@ -78,16 +_,17 @@
@@ -78,16 +_,30 @@
PlayerEnderChestContainer enderChestInventory = player.getEnderChestInventory();
if (enderChestInventory != null && level.getBlockEntity(pos) instanceof EnderChestBlockEntity enderChestBlockEntity) {
BlockPos blockPos = pos.above();
Expand All @@ -19,7 +19,20 @@
+ enderChestInventory.setActiveChest(enderChestBlockEntity); // Needs to happen before ChestMenu.threeRows as it is required for opening animations
+ if (level instanceof ServerLevel serverLevel && player.openMenu(
+ new SimpleMenuProvider(
+ (containerId, playerInventory, player1) -> ChestMenu.threeRows(containerId, playerInventory, enderChestInventory), CONTAINER_TITLE
+ (containerId, playerInventory, player1) -> {
+ final int rows = Math.clamp(enderChestInventory.getContainerSize() / 9, 1, 6);
+ // Paper start - configurable ender chest slot count
+ final net.minecraft.world.inventory.MenuType<?> menuType = switch (rows) {
+ case 1 -> net.minecraft.world.inventory.MenuType.GENERIC_9x1;
+ case 2 -> net.minecraft.world.inventory.MenuType.GENERIC_9x2;
+ case 3 -> net.minecraft.world.inventory.MenuType.GENERIC_9x3;
+ case 4 -> net.minecraft.world.inventory.MenuType.GENERIC_9x4;
+ case 5 -> net.minecraft.world.inventory.MenuType.GENERIC_9x5;
+ default -> net.minecraft.world.inventory.MenuType.GENERIC_9x6;
+ };
+ return new ChestMenu(menuType, containerId, playerInventory, enderChestInventory, rows);
+ // Paper end - configurable ender chest slot count
+ }, CONTAINER_TITLE
+ )
+ ).isPresent()) {
+ // Paper end - Fix InventoryOpenEvent cancellation - moved up;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
--- a/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java
@@ -49,6 +_,42 @@
@@ -40,15 +_,51 @@
public static final int OPENING_TICK_LENGTH = 10;
public static final float MAX_LID_HEIGHT = 0.5F;
public static final float MAX_LID_ROTATION = 270.0F;
- private static final int[] SLOTS = IntStream.range(0, 27).toArray();
+ private static final int[] SLOTS = IntStream.range(0, io.papermc.paper.configuration.GlobalConfiguration.get().misc.shulkerBoxSlotCount).toArray(); // Paper - configurable shulker box slot count
private static final Component DEFAULT_NAME = Component.translatable("container.shulkerBox");
- private NonNullList<ItemStack> itemStacks = NonNullList.withSize(27, ItemStack.EMPTY);
+ private NonNullList<ItemStack> itemStacks = NonNullList.withSize(io.papermc.paper.configuration.GlobalConfiguration.get().misc.shulkerBoxSlotCount, ItemStack.EMPTY); // Paper - configurable shulker box slot count
public int openCount;
private ShulkerBoxBlockEntity.AnimationStatus animationStatus = ShulkerBoxBlockEntity.AnimationStatus.CLOSED;
private float progress;
private float progressOld;
private final @Nullable DyeColor color;

Expand Down Expand Up @@ -59,3 +70,36 @@
this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount);
if (this.openCount <= 0) {
this.level.gameEvent(user.getLivingEntity(), GameEvent.CONTAINER_CLOSE, this.worldPosition);
@@ -231,7 +_,8 @@

@Override
public int[] getSlotsForFace(Direction side) {
- return SLOTS;
+ // Paper - configurable shulker box slot count
+ return this.getContainerSize() == SLOTS.length ? SLOTS : IntStream.range(0, this.getContainerSize()).toArray();
}

@Override
@@ -254,7 +_,21 @@

@Override
protected AbstractContainerMenu createMenu(int id, Inventory player) {
- return new ShulkerBoxMenu(id, player, this);
+ // Paper start - configurable shulker box slot count
+ final int rows = Math.clamp(this.getContainerSize() / 9, 1, 6);
+ if (rows == 3) {
+ return new ShulkerBoxMenu(id, player, this);
+ }
+ final net.minecraft.world.inventory.MenuType<?> menuType = switch (rows) {
+ case 1 -> net.minecraft.world.inventory.MenuType.GENERIC_9x1;
+ case 2 -> net.minecraft.world.inventory.MenuType.GENERIC_9x2;
+ case 3 -> net.minecraft.world.inventory.MenuType.GENERIC_9x3;
+ case 4 -> net.minecraft.world.inventory.MenuType.GENERIC_9x4;
+ case 5 -> net.minecraft.world.inventory.MenuType.GENERIC_9x5;
+ default -> net.minecraft.world.inventory.MenuType.GENERIC_9x6;
+ };
+ return new net.minecraft.world.inventory.ChestMenu(menuType, id, player, this, rows);
+ // Paper end - configurable shulker box slot count
}

public boolean isClosed() {
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,25 @@ private void postProcess() {
public boolean enableNether = true;
@Comment("Keeps Paper's fix for MC-159283 enabled. Disable to use vanilla End ring terrain.")
public boolean fixFarEndTerrainGeneration = true;
@Comment("Number of ender chest slots. Supports 9-54 slots and is normalized to a multiple of 9.")
public int enderChestSlotCount = 27;
@Comment("Number of shulker box slots. Supports 9-54 slots and is normalized to a multiple of 9.")
public int shulkerBoxSlotCount = 27;

@PostProcess
private void postProcess() {
this.enderChestSlotCount = this.normalizeContainerSlotCount(this.enderChestSlotCount, "misc.ender-chest-slot-count");
this.shulkerBoxSlotCount = this.normalizeContainerSlotCount(this.shulkerBoxSlotCount, "misc.shulker-box-slot-count");
}

private int normalizeContainerSlotCount(final int configured, final String key) {
final int clamped = Math.clamp(configured, 9, 54);
final int normalized = Math.clamp(((clamped + 4) / 9) * 9, 9, 54);
if (configured != normalized) {
LOGGER.warn("Invalid {} value '{}', using '{}'. Valid values are multiples of 9 between 9 and 54.", key, configured, normalized);
}
return normalized;
}
}

public BlockUpdates blockUpdates;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,24 @@ public InventoryView getBukkitView() {
public static net.minecraft.world.inventory.MenuType getNotchInventoryType(Inventory inventory) {
final InventoryType type = inventory.getType();
switch (type) {
case SHULKER_BOX:
if (inventory.getSize() == 27) {
return net.minecraft.world.inventory.MenuType.SHULKER_BOX;
}
switch (inventory.getSize()) {
case 9:
return net.minecraft.world.inventory.MenuType.GENERIC_9x1;
case 18:
return net.minecraft.world.inventory.MenuType.GENERIC_9x2;
case 36:
return net.minecraft.world.inventory.MenuType.GENERIC_9x4;
case 45:
return net.minecraft.world.inventory.MenuType.GENERIC_9x5;
case 54:
return net.minecraft.world.inventory.MenuType.GENERIC_9x6;
default:
throw new IllegalArgumentException("Unsupported shulker inventory size " + inventory.getSize());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of throwing new IllegalArgumentException("Unsupported shulker inventory size " + inventory.getSize()), it would be better to return net.minecraft.world.inventory.MenuType.SHULKER_BOX and log a warning such as LOGGER.warn("Unsupported shulker inventory size {}, using the default value.", inventory.getSize());. This makes the code more compatible and resilient by falling back to a safe default instead of failing completely.

Copy link
Copy Markdown
Author

@cev-api cev-api Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested invalid config values locally. These are normalized during configuration load before they reach CraftContainer, so -52 became 9 for ender chests and 992 became 54 for shulker boxes, with startup warnings logged. Because of that, unsupported sizes should not occur through normal configuration, which is why I left the IllegalArgumentException in place here rather than silently falling back to a potentially mismatched menu type.

[15:30:56 WARN]: [io.papermc.paper.configuration.GlobalConfiguration] Invalid misc.ender-chest-slot-count value '-52', using '9'. Valid values are multiples of 9 between 9 and 54.
[15:30:56 WARN]: [io.papermc.paper.configuration.GlobalConfiguration] Invalid misc.shulker-box-slot-count value '992', using '54'. Valid values are multiples of 9 between 9 and 54.

I also tested reducing the configured size after storing items. In that case, items outside the reduced container size were not recovered after increasing the size again and restarting.

}
case CHEST:
case ENDER_CHEST:
case BARREL:
Expand Down Expand Up @@ -178,7 +196,11 @@ private void setupSlots(Container top, net.minecraft.world.entity.player.Invento
this.delegate = new BeaconMenu(windowId, bottom);
break;
case SHULKER_BOX:
this.delegate = new ShulkerBoxMenu(windowId, bottom, top);
if (top.getContainerSize() == 27) {
this.delegate = new ShulkerBoxMenu(windowId, bottom, top);
} else {
this.delegate = new ChestMenu(CraftContainer.getNotchInventoryType(view.getTopInventory()), windowId, bottom, top, top.getContainerSize() / 9);
}
break;
case BLAST_FURNACE:
this.delegate = new BlastFurnaceMenu(windowId, bottom, top, new SimpleContainerData(4));
Expand Down