From 7e896541bd3d75a178d3b5553a7fa5ecefda9b03 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sat, 26 Aug 2023 12:28:42 -0700
Subject: [PATCH] Add titleOverride to InventoryOpenEvent (#7540)

---
 ...-titleOverride-to-InventoryOpenEvent.patch |  52 ++++++++
 ...-titleOverride-to-InventoryOpenEvent.patch | 120 ++++++++++++++++++
 2 files changed, 172 insertions(+)
 create mode 100644 patches/api/Add-titleOverride-to-InventoryOpenEvent.patch
 create mode 100644 patches/server/Add-titleOverride-to-InventoryOpenEvent.patch

diff --git a/patches/api/Add-titleOverride-to-InventoryOpenEvent.patch b/patches/api/Add-titleOverride-to-InventoryOpenEvent.patch
new file mode 100644
index 0000000000..b62c3e7df4
--- /dev/null
+++ b/patches/api/Add-titleOverride-to-InventoryOpenEvent.patch
@@ -0,0 +1,52 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <jake.m.potrebic@gmail.com>
+Date: Fri, 4 Mar 2022 12:45:21 -0800
+Subject: [PATCH] Add titleOverride to InventoryOpenEvent
+
+
+diff --git a/src/main/java/org/bukkit/event/inventory/InventoryOpenEvent.java b/src/main/java/org/bukkit/event/inventory/InventoryOpenEvent.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/org/bukkit/event/inventory/InventoryOpenEvent.java
++++ b/src/main/java/org/bukkit/event/inventory/InventoryOpenEvent.java
+@@ -0,0 +0,0 @@ import org.jetbrains.annotations.NotNull;
+ public class InventoryOpenEvent extends InventoryEvent implements Cancellable {
+     private static final HandlerList handlers = new HandlerList();
+     private boolean cancelled;
++    private net.kyori.adventure.text.Component titleOverride; // Paper
+ 
+     public InventoryOpenEvent(@NotNull InventoryView transaction) {
+         super(transaction);
+@@ -0,0 +0,0 @@ public class InventoryOpenEvent extends InventoryEvent implements Cancellable {
+         cancelled = cancel;
+     }
+ 
++    // Paper start
++    /**
++     * Gets the title override set by another event or null
++     * if not set.
++     *
++     * @return the title override or null
++     */
++    public net.kyori.adventure.text.@org.jetbrains.annotations.Nullable Component titleOverride() {
++        return this.titleOverride;
++    }
++
++    /**
++     * Sets the title override or clears the override.
++     * <p>
++     * This is only the title sent to the client in the open packet, this doesn't change
++     * the title returned by {@link InventoryView#title()}, hence "override".
++     * <p>
++     * <b>NOTE:</b> Horse inventories are a special case where setting this will
++     * have no effect. Horse inventory titles are set by the horse display name.
++     *
++     * @param titleOverride the title override or null
++     */
++    public void titleOverride(net.kyori.adventure.text.@org.jetbrains.annotations.Nullable Component titleOverride) {
++        this.titleOverride = titleOverride;
++    }
++    // Paper end
++
+     @NotNull
+     @Override
+     public HandlerList getHandlers() {
diff --git a/patches/server/Add-titleOverride-to-InventoryOpenEvent.patch b/patches/server/Add-titleOverride-to-InventoryOpenEvent.patch
new file mode 100644
index 0000000000..ab47d06d23
--- /dev/null
+++ b/patches/server/Add-titleOverride-to-InventoryOpenEvent.patch
@@ -0,0 +1,120 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <jake.m.potrebic@gmail.com>
+Date: Fri, 4 Mar 2022 12:45:03 -0800
+Subject: [PATCH] Add titleOverride to InventoryOpenEvent
+
+
+diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
+@@ -0,0 +0,0 @@ public class ServerPlayer extends Player {
+             this.nextContainerCounter();
+             AbstractContainerMenu container = factory.createMenu(this.containerCounter, this.getInventory(), this);
+ 
++            Component title = null; // Paper
+             // CraftBukkit start - Inventory open hook
+             if (container != null) {
+                 container.setTitle(factory.getDisplayName());
+ 
+                 boolean cancelled = false;
+-                container = CraftEventFactory.callInventoryOpenEvent(this, container, cancelled);
++                // Paper start
++                final com.mojang.datafixers.util.Pair<net.kyori.adventure.text.Component, AbstractContainerMenu> result = CraftEventFactory.callInventoryOpenEventWithTitle(this, container, cancelled);
++                container = result.getSecond();
++                title = PaperAdventure.asVanilla(result.getFirst());
++                // Paper end
+                 if (container == null && !cancelled) { // Let pre-cancelled events fall through
+                     // SPIGOT-5263 - close chest if cancelled
+                     if (factory instanceof Container) {
+@@ -0,0 +0,0 @@ public class ServerPlayer extends Player {
+             } else {
+                 // CraftBukkit start
+                 this.containerMenu = container;
+-                if (!this.isImmobile()) this.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), container.getTitle())); // Paper
++                if (!this.isImmobile()) this.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), Objects.requireNonNullElseGet(title, container::getTitle))); // Paper
+                 // CraftBukkit end
+                 this.initMenu(container);
+                 return OptionalInt.of(this.containerCounter);
+diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
+@@ -0,0 +0,0 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
+         Preconditions.checkArgument(windowType != null, "Unknown windowType");
+         AbstractContainerMenu container = new CraftContainer(inventory, player, player.nextContainerCounter());
+ 
+-        container = CraftEventFactory.callInventoryOpenEvent(player, container);
++        // Paper start
++        final com.mojang.datafixers.util.Pair<net.kyori.adventure.text.Component, AbstractContainerMenu> result = CraftEventFactory.callInventoryOpenEventWithTitle(player, container);
++        container = result.getSecond();
++        // Paper end
+         if (container == null) return;
+ 
+         //String title = container.getBukkitView().getTitle(); // Paper - comment
+         net.kyori.adventure.text.Component adventure$title = container.getBukkitView().title(); // Paper
+         if (adventure$title == null) adventure$title = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(container.getBukkitView().getTitle()); // Paper
++        if (result.getFirst() != null) adventure$title = result.getFirst(); // Paper
+ 
+         //player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, CraftChatMessage.fromString(title)[0])); // Paper - comment
+         if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
+@@ -0,0 +0,0 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
+         }
+ 
+         // Trigger an INVENTORY_OPEN event
+-        container = CraftEventFactory.callInventoryOpenEvent(player, container);
++        // Paper start
++        final com.mojang.datafixers.util.Pair<net.kyori.adventure.text.Component, AbstractContainerMenu> result = CraftEventFactory.callInventoryOpenEventWithTitle(player, container);
++        container = result.getSecond();
++        // Paper end
+         if (container == null) {
+             return;
+         }
+@@ -0,0 +0,0 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
+         //String title = inventory.getTitle(); // Paper - comment
+         net.kyori.adventure.text.Component adventure$title = inventory.title(); // Paper
+         if (adventure$title == null) adventure$title = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(inventory.getTitle()); // Paper
++        if (result.getFirst() != null) adventure$title = result.getFirst(); // Paper
+         //player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, CraftChatMessage.fromString(title)[0])); // Paper - comment
+         if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
+         player.containerMenu = container;
+diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+@@ -0,0 +0,0 @@ public class CraftEventFactory {
+     }
+ 
+     public static AbstractContainerMenu callInventoryOpenEvent(ServerPlayer player, AbstractContainerMenu container) {
+-        return CraftEventFactory.callInventoryOpenEvent(player, container, false);
++        // Paper start
++        return callInventoryOpenEventWithTitle(player, container).getSecond();
++    }
++    public static com.mojang.datafixers.util.Pair<net.kyori.adventure.text.@org.jetbrains.annotations.Nullable Component, @org.jetbrains.annotations.Nullable AbstractContainerMenu> callInventoryOpenEventWithTitle(ServerPlayer player, AbstractContainerMenu container) {
++        return CraftEventFactory.callInventoryOpenEventWithTitle(player, container, false);
++        // Paper end
+     }
+ 
++    @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - use method that acknowledges title overrides
+     public static AbstractContainerMenu callInventoryOpenEvent(ServerPlayer player, AbstractContainerMenu container, boolean cancelled) {
++        // Paper start
++        return callInventoryOpenEventWithTitle(player, container, cancelled).getSecond();
++    }
++    public static com.mojang.datafixers.util.Pair<net.kyori.adventure.text.@org.jetbrains.annotations.Nullable Component, @org.jetbrains.annotations.Nullable AbstractContainerMenu> callInventoryOpenEventWithTitle(ServerPlayer player, AbstractContainerMenu container, boolean cancelled) {
++        // Paper end
+         if (player.containerMenu != player.inventoryMenu) { // fire INVENTORY_CLOSE if one already open
+             player.connection.handleContainerClose(new ServerboundContainerClosePacket(player.containerMenu.containerId), InventoryCloseEvent.Reason.OPEN_NEW); // Paper
+         }
+@@ -0,0 +0,0 @@ public class CraftEventFactory {
+ 
+         if (event.isCancelled()) {
+             container.transferTo(player.containerMenu, craftPlayer);
+-            return null;
++            return com.mojang.datafixers.util.Pair.of(null, null); // Paper - title override
+         }
+ 
+-        return container;
++        return com.mojang.datafixers.util.Pair.of(event.titleOverride(), container); // Paper - title override
+     }
+ 
+     public static ItemStack callPreCraftEvent(Container matrix, Container resultInventory, ItemStack result, InventoryView lastCraftView, boolean isRepair) {