diff --git a/build-data/paper.at b/build-data/paper.at index 6f731e0677..2e798b98b6 100644 --- a/build-data/paper.at +++ b/build-data/paper.at @@ -311,3 +311,7 @@ public-f net.minecraft.world.level.chunk.ChunkGenerator strongholdSeed # More Sculk Sensor API public-f net.minecraft.world.level.gameevent.vibrations.VibrationListener listenerRange + +# Fix custom inventory holders +public-f net.minecraft.world.inventory.AbstractContainerMenu dataSlots +public-f net.minecraft.world.inventory.AbstractContainerMenu remoteDataSlots diff --git a/patches/server/Adventure.patch b/patches/server/Adventure.patch index 705ec69115..fbcfba54be 100644 --- a/patches/server/Adventure.patch +++ b/patches/server/Adventure.patch @@ -3278,11 +3278,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper start + public Inventory createInventory(InventoryHolder holder, InventoryType type, net.kyori.adventure.text.Component title) { -+ // Paper start -+ if (holder != null) { -+ return DEFAULT_CONVERTER.createInventory(holder, type, title); -+ } -+ //noinspection ConstantConditions // Paper end + return converterMap.get(type).createInventory(holder, type, title); + } + // Paper end diff --git a/patches/server/Restore-custom-InventoryHolder-support.patch b/patches/server/Restore-custom-InventoryHolder-support.patch index abeb3eb978..4b66dbc3f4 100644 --- a/patches/server/Restore-custom-InventoryHolder-support.patch +++ b/patches/server/Restore-custom-InventoryHolder-support.patch @@ -1,46 +1,349 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Shane Freeder +From: Jake Potrebic Date: Mon, 5 Nov 2018 04:23:51 +0000 Subject: [PATCH] Restore custom InventoryHolder support +Co-authored-by: Shane Freeder + Upstream removed the ability to consistently use a custom InventoryHolder, However, the implementation does not use an InventoryHolder in any form outside of custom inventories. -We can take that knowledge and apply some expected behavior, if we're given -an inventory holder, we should use it and return a custom inventory with the -holder, otherwise, create an inventory backed by the intended inventory, as -per upstream behavior. - -This provides a "best of both worlds" scenario: plugins with InventoryHolder's -will always work as intended in the past, those without will create implementation -based inventories. - -diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftInventoryCreator.java b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftInventoryCreator.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftInventoryCreator.java -+++ b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftInventoryCreator.java -@@ -0,0 +0,0 @@ public final class CraftInventoryCreator { - } - - public Inventory createInventory(InventoryHolder holder, InventoryType type) { -+ // Paper start -+ if (holder != null) { -+ return this.DEFAULT_CONVERTER.createInventory(holder, type); +diff --git a/src/main/java/io/papermc/paper/inventory/PaperInventoryCustomHolderContainer.java b/src/main/java/io/papermc/paper/inventory/PaperInventoryCustomHolderContainer.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/inventory/PaperInventoryCustomHolderContainer.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.inventory; ++ ++import io.papermc.paper.adventure.PaperAdventure; ++import net.kyori.adventure.text.Component; ++import net.minecraft.world.Container; ++import net.minecraft.world.entity.player.Player; ++import net.minecraft.world.item.ItemStack; ++import net.minecraft.world.level.block.entity.BaseContainerBlockEntity; ++import org.bukkit.Location; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++import org.bukkit.event.inventory.InventoryType; ++import org.bukkit.inventory.InventoryHolder; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++import java.util.List; ++ ++@DefaultQualifier(NonNull.class) ++public final class PaperInventoryCustomHolderContainer implements Container { ++ ++ private final InventoryHolder owner; ++ private final Container delegate; ++ private final InventoryType type; ++ private final String title; ++ private final Component adventure$title; ++ ++ public PaperInventoryCustomHolderContainer(InventoryHolder owner, Container delegate, InventoryType type) { ++ this.owner = owner; ++ this.delegate = delegate; ++ this.type = type; ++ @Nullable Component adventure$title = null; ++ if (delegate instanceof BaseContainerBlockEntity blockEntity) { ++ adventure$title = blockEntity.getCustomName() != null ? PaperAdventure.asAdventure(blockEntity.getCustomName()) : null; + } -+ //noinspection ConstantConditions // Paper end - return this.converterMap.get(type).createInventory(holder, type); ++ if (adventure$title == null) { ++ adventure$title = type.defaultTitle(); ++ } ++ this.adventure$title = adventure$title; ++ this.title = PaperAdventure.LEGACY_SECTION_UXRC.serialize(this.adventure$title); ++ } ++ ++ public Component title() { ++ return this.adventure$title; ++ } ++ ++ public String getTitle() { ++ return this.title; ++ } ++ ++ public InventoryType getType() { ++ return this.type; ++ } ++ ++ @Override ++ public int getContainerSize() { ++ return this.delegate.getContainerSize(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return this.delegate.isEmpty(); ++ } ++ ++ @Override ++ public ItemStack getItem(int slot) { ++ return this.delegate.getItem(slot); ++ } ++ ++ @Override ++ public ItemStack removeItem(int slot, int amount) { ++ return this.delegate.removeItem(slot, amount); ++ } ++ ++ @Override ++ public ItemStack removeItemNoUpdate(int slot) { ++ return this.delegate.removeItemNoUpdate(slot); ++ } ++ ++ @Override ++ public void setItem(int slot, ItemStack stack) { ++ this.delegate.setItem(slot, stack); ++ } ++ ++ @Override ++ public int getMaxStackSize() { ++ return this.delegate.getMaxStackSize(); ++ } ++ ++ @Override ++ public void setChanged() { ++ this.delegate.setChanged(); ++ } ++ ++ @Override ++ public boolean stillValid(Player player) { ++ return this.delegate.stillValid(player); ++ } ++ ++ @Override ++ public List getContents() { ++ return this.delegate.getContents(); ++ } ++ ++ @Override ++ public void onOpen(CraftHumanEntity who) { ++ this.delegate.onOpen(who); ++ } ++ ++ @Override ++ public void onClose(CraftHumanEntity who) { ++ this.delegate.onClose(who); ++ } ++ ++ @Override ++ public List getViewers() { ++ return this.delegate.getViewers(); ++ } ++ ++ @Override ++ public InventoryHolder getOwner() { ++ return this.owner; ++ } ++ ++ @Override ++ public void setMaxStackSize(int size) { ++ this.delegate.setMaxStackSize(size); ++ } ++ ++ @Override ++ public Location getLocation() { ++ return this.delegate.getLocation(); ++ } ++ ++ @Override ++ public void clearContent() { ++ this.delegate.clearContent(); ++ } ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java +@@ -0,0 +0,0 @@ public class CraftContainer extends AbstractContainerMenu { + // Paper start + @Override + public net.kyori.adventure.text.Component title() { +- return inventory instanceof CraftInventoryCustom ? ((CraftInventoryCustom.MinecraftInventory) ((CraftInventory) inventory).getInventory()).title() : net.kyori.adventure.text.Component.text(inventory.getType().getDefaultTitle()); ++ return inventory instanceof CraftInventoryCustom custom ? custom.title() : inventory.getType().defaultTitle(); // Paper + } + // Paper end + + @Override + public String getTitle() { +- return inventory instanceof CraftInventoryCustom ? ((CraftInventoryCustom.MinecraftInventory) ((CraftInventory) inventory).getInventory()).getTitle() : inventory.getType().getDefaultTitle(); ++ return inventory instanceof CraftInventoryCustom custom ? custom.getTitle() : inventory.getType().getDefaultTitle(); // Paper + } + }, player, id); + } +@@ -0,0 +0,0 @@ public class CraftContainer extends AbstractContainerMenu { + this.lastSlots = delegate.lastSlots; + this.slots = delegate.slots; + this.remoteSlots = delegate.remoteSlots; ++ // Paper start - copy data slots for InventoryView#set/getProperty ++ this.dataSlots = delegate.dataSlots; ++ this.remoteDataSlots = delegate.remoteDataSlots; ++ // Paper end + } + + // SPIGOT-4598 - we should still delegate the shift click handler +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java +@@ -0,0 +0,0 @@ public class CraftInventory implements Inventory { + return InventoryType.BREWING; + } else if (this.inventory instanceof CraftInventoryCustom.MinecraftInventory) { + return ((CraftInventoryCustom.MinecraftInventory) this.inventory).getType(); ++ // Paper start ++ } else if (this.inventory instanceof io.papermc.paper.inventory.PaperInventoryCustomHolderContainer holderContainer) { ++ return holderContainer.getType(); ++ // Paper end + } else if (this.inventory instanceof PlayerEnderChestContainer) { + return InventoryType.ENDER_CHEST; + } else if (this.inventory instanceof MerchantContainer) { +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java +@@ -0,0 +0,0 @@ import org.bukkit.event.inventory.InventoryType; + import org.bukkit.inventory.InventoryHolder; + + public class CraftInventoryCustom extends CraftInventory { ++ // Paper start ++ public CraftInventoryCustom(InventoryHolder owner, InventoryType type, Container delegate) { ++ super(new io.papermc.paper.inventory.PaperInventoryCustomHolderContainer(owner, delegate, type)); ++ } ++ // Paper end + public CraftInventoryCustom(InventoryHolder owner, InventoryType type) { + super(new MinecraftInventory(owner, type)); + } +@@ -0,0 +0,0 @@ public class CraftInventoryCustom extends CraftInventory { + public CraftInventoryCustom(InventoryHolder owner, int size, String title) { + super(new MinecraftInventory(owner, size, title)); + } ++ // Paper start ++ public String getTitle() { ++ if (this.inventory instanceof MinecraftInventory minecraftInventory) { ++ return minecraftInventory.getTitle(); ++ } else if (this.inventory instanceof io.papermc.paper.inventory.PaperInventoryCustomHolderContainer customHolderContainer) { ++ return customHolderContainer.getTitle(); ++ } else { ++ throw new UnsupportedOperationException(this.inventory.getClass() + " isn't a recognized Container type here"); ++ } ++ } ++ ++ public net.kyori.adventure.text.Component title() { ++ if (this.inventory instanceof MinecraftInventory minecraftInventory) { ++ return minecraftInventory.title(); ++ } else if (this.inventory instanceof io.papermc.paper.inventory.PaperInventoryCustomHolderContainer customHolderContainer) { ++ return customHolderContainer.title(); ++ } else { ++ throw new UnsupportedOperationException(this.inventory.getClass() + " isn't a recognized Container type here"); ++ } ++ } ++ // Paper end + + static class MinecraftInventory implements Container { + private final NonNullList items; +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java +@@ -0,0 +0,0 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat + + @Override + public Inventory createInventory(InventoryHolder holder, InventoryType type) { +- return this.getInventory(this.getTileEntity()); ++ return this.getInventory(holder, type, this.getTileEntity()); // Paper } -@@ -0,0 +0,0 @@ public final class CraftInventoryCreator { + // Paper start +@@ -0,0 +0,0 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat + ((RandomizableContainerBlockEntity) te).setCustomName(io.papermc.paper.adventure.PaperAdventure.asVanilla(title)); + } + +- return getInventory(te); ++ return this.getInventory(owner, type, te); // Paper + } // Paper end - public Inventory createInventory(InventoryHolder holder, InventoryType type, String title) { -+ // Paper start -+ if (holder != null) { -+ return DEFAULT_CONVERTER.createInventory(holder, type, title); -+ } -+ //noinspection ConstantConditions // Paper end - return this.converterMap.get(type).createInventory(holder, type, title); +@@ -0,0 +0,0 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat + ((RandomizableContainerBlockEntity) te).setCustomName(CraftChatMessage.fromStringOrNull(title)); + } + +- return this.getInventory(te); ++ return this.getInventory(holder, type, te); // Paper } ++ @Deprecated // Paper - use getInventory with owner and type + public Inventory getInventory(Container tileEntity) { ++ // Paper start ++ return this.getInventory(null, null, tileEntity); ++ } ++ ++ public Inventory getInventory(InventoryHolder owner, InventoryType type, Container tileEntity) { // Paper ++ if (owner != null) return new org.bukkit.craftbukkit.inventory.CraftInventoryCustom(owner, type, tileEntity); // Paper ++ // Paper end + return new CraftInventory(tileEntity); + } + +@@ -0,0 +0,0 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat + public Inventory createInventory(InventoryHolder owner, InventoryType type, net.kyori.adventure.text.Component title) { + Container tileEntity = getTileEntity(); + ((AbstractFurnaceBlockEntity) tileEntity).setCustomName(io.papermc.paper.adventure.PaperAdventure.asVanilla(title)); +- return getInventory(tileEntity); ++ return this.getInventory(owner, type, tileEntity); // Paper + } + // Paper end + +@@ -0,0 +0,0 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat + public Inventory createInventory(InventoryHolder owner, InventoryType type, String title) { + Container tileEntity = this.getTileEntity(); + ((AbstractFurnaceBlockEntity) tileEntity).setCustomName(CraftChatMessage.fromStringOrNull(title)); +- return this.getInventory(tileEntity); ++ return this.getInventory(owner, type, tileEntity); // Paper + } + + @Override + public Inventory getInventory(Container tileEntity) { ++ // Paper start ++ return getInventory(null, null, tileEntity); ++ } ++ ++ @Override ++ public Inventory getInventory(InventoryHolder owner, InventoryType type, net.minecraft.world.Container tileEntity) { // Paper ++ if (owner != null) return new org.bukkit.craftbukkit.inventory.CraftInventoryCustom(owner, type, tileEntity); // Paper ++ // Paper end + return new CraftInventoryFurnace((AbstractFurnaceBlockEntity) tileEntity); + } + } +@@ -0,0 +0,0 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat + if (tileEntity instanceof BrewingStandBlockEntity) { + ((BrewingStandBlockEntity) tileEntity).setCustomName(io.papermc.paper.adventure.PaperAdventure.asVanilla(title)); + } +- return getInventory(tileEntity); ++ return this.getInventory(owner, type, tileEntity); // Paper + } + // Paper end + +@@ -0,0 +0,0 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat + if (tileEntity instanceof BrewingStandBlockEntity) { + ((BrewingStandBlockEntity) tileEntity).setCustomName(CraftChatMessage.fromStringOrNull(title)); + } +- return this.getInventory(tileEntity); ++ return this.getInventory(holder, type, tileEntity); // Paper + } + + @Override + public Inventory getInventory(Container tileEntity) { ++ // Paper start ++ return getInventory(null, null, tileEntity); ++ } ++ ++ @Override ++ public Inventory getInventory(InventoryHolder owner, InventoryType type, net.minecraft.world.Container tileEntity) { // Paper ++ if (owner != null) return new org.bukkit.craftbukkit.inventory.CraftInventoryCustom(owner, type, tileEntity); // Paper ++ // Paper end + return new CraftInventoryBrewer(tileEntity); + } + }