From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Mon, 5 Nov 2018 04:23:51 +0000
Subject: [PATCH] Restore custom InventoryHolder support

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.

== AT ==
public-f net.minecraft.world.inventory.AbstractContainerMenu dataSlots
public-f net.minecraft.world.inventory.AbstractContainerMenu remoteDataSlots

Co-authored-by: Shane Freeder <theboyetronic@gmail.com>

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.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+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;
+        }
+        if (adventure$title == null) {
+            adventure$title = type.defaultTitle();
+        }
+        this.adventure$title = adventure$title;
+        this.title = LegacyComponentSerializer.legacySection().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<ItemStack> 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<HumanEntity> 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 {
     public CraftContainer(final Inventory inventory, final Player player, int id) {
         this(new CraftAbstractInventoryView() {
 
-            private final String originalTitle = (inventory instanceof CraftInventoryCustom) ? ((CraftInventoryCustom.MinecraftInventory) ((CraftInventory) inventory).getInventory()).getTitle() : inventory.getType().getDefaultTitle();
+            private final String originalTitle = inventory instanceof CraftInventoryCustom ? ((CraftInventoryCustom) inventory).getTitle() : inventory.getType().getDefaultTitle(); // Paper
             private String title = this.originalTitle;
 
             @Override
@@ -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
 
@@ -0,0 +0,0 @@ public class CraftContainer extends AbstractContainerMenu {
             this.lastSlots = this.delegate.lastSlots;
             this.slots = this.delegate.slots;
             this.remoteSlots = this.delegate.remoteSlots;
+            // Paper start - copy data slots for InventoryView#set/getProperty
+            this.dataSlots = this.delegate.dataSlots;
+            this.remoteDataSlots = this.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<ItemStack> 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
     }
 
     // Paper start
@@ -0,0 +0,0 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat
             ((RandomizableContainerBlockEntity) te).name = io.papermc.paper.adventure.PaperAdventure.asVanilla(title);
         }
 
-        return getInventory(te);
+        return this.getInventory(owner, type, te); // Paper
     }
     // Paper end
 
@@ -0,0 +0,0 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat
             ((RandomizableContainerBlockEntity) te).name = 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
         @Override
         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);
+            ((AbstractFurnaceBlockEntity) tileEntity).name = io.papermc.paper.adventure.PaperAdventure.asVanilla(title);
+            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).name = 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).name = 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).name = 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);
         }
     }
