From ac69f75d23618d914c0d705b578ac47dbac2bff0 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Tue, 17 Dec 2024 11:45:39 +0100 Subject: [PATCH] Apply Improve-performance-of-mass-crafts directly --- ...5-Improve-performance-of-mass-crafts.patch | 92 ------------------- .../inventory/CraftingContainer.java.patch | 4 +- .../world/inventory/CraftingMenu.java.patch | 27 +++++- .../world/inventory/ResultSlot.java.patch | 11 +++ .../TransientCraftingContainer.java.patch | 6 +- 5 files changed, 42 insertions(+), 98 deletions(-) delete mode 100644 feature-patches/1065-Improve-performance-of-mass-crafts.patch create mode 100644 paper-server/patches/sources/net/minecraft/world/inventory/ResultSlot.java.patch diff --git a/feature-patches/1065-Improve-performance-of-mass-crafts.patch b/feature-patches/1065-Improve-performance-of-mass-crafts.patch deleted file mode 100644 index c09f2cf750..0000000000 --- a/feature-patches/1065-Improve-performance-of-mass-crafts.patch +++ /dev/null @@ -1,92 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jake Potrebic -Date: Sun, 13 Aug 2023 15:41:52 -0700 -Subject: [PATCH] Improve performance of mass crafts - -When the server crafts all available items in CraftingMenu or InventoryMenu the game -checks either 4 or 9 times for each individual craft for a matching recipe for that container. -This check can be expensive if 64 total crafts are being performed with the recipe matching logic -being run 64 * 9 + 64 times. A breakdown of those times is below. This patch caches the last matching -recipe so that it is checked first and only if it doesn't match does the rest of the matching logic run. - -Shift-click crafts are processed one at a time, so shift clicking on an item in the result of a iron block craft -where all the 9 inputs are full stacks of iron will run 64 iron block crafts. For each of those crafts, the -'remaining' blocks are calculated. This is due to recipes that have leftover items like buckets. This is done -for each craft, and done once to get the full 9 leftover items which are usually air. Then 1 item is removed -from each of the 9 inputs and each time that happens, logic is triggered to update the result itemstack. So -for each craft, that logic is run 9 times (hence the 64 * 9). The + 64 is from the 64 checks for remaining items. - -After this patch, the full iteration over all recipes checking for a match should run once for a full craft to find the -initial recipe match. Then that recipe will be checked first for all future recipe match checks. - -diff --git a/src/main/java/net/minecraft/world/inventory/CraftingContainer.java b/src/main/java/net/minecraft/world/inventory/CraftingContainer.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/inventory/CraftingContainer.java -+++ b/src/main/java/net/minecraft/world/inventory/CraftingContainer.java -@@ -0,0 +0,0 @@ public interface CraftingContainer extends Container, StackedContentsCompatible - List getItems(); - - // CraftBukkit start -- default RecipeHolder getCurrentRecipe() { -+ default RecipeHolder getCurrentRecipe() { // Paper - use correct generic - return null; - } - -- default void setCurrentRecipe(RecipeHolder recipe) { -+ default void setCurrentRecipe(RecipeHolder recipe) { // Paper - use correct generic - } - // CraftBukkit end - -diff --git a/src/main/java/net/minecraft/world/inventory/CraftingMenu.java b/src/main/java/net/minecraft/world/inventory/CraftingMenu.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/inventory/CraftingMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/CraftingMenu.java -@@ -0,0 +0,0 @@ public class CraftingMenu extends AbstractCraftingMenu { - CraftingInput craftinginput = craftingInventory.asCraftInput(); - ServerPlayer entityplayer = (ServerPlayer) player; - ItemStack itemstack = ItemStack.EMPTY; -+ if (recipe == null) recipe = craftingInventory.getCurrentRecipe(); // Paper - Perf: Improve mass crafting; check last recipe used first - Optional> optional = world.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, craftinginput, world, recipe); - craftingInventory.setCurrentRecipe(optional.orElse(null)); // CraftBukkit - -diff --git a/src/main/java/net/minecraft/world/inventory/ResultSlot.java b/src/main/java/net/minecraft/world/inventory/ResultSlot.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/inventory/ResultSlot.java -+++ b/src/main/java/net/minecraft/world/inventory/ResultSlot.java -@@ -0,0 +0,0 @@ public class ResultSlot extends Slot { - private NonNullList getRemainingItems(CraftingInput input, Level world) { - return world instanceof ServerLevel serverLevel - ? serverLevel.recipeAccess() -- .getRecipeFor(RecipeType.CRAFTING, input, serverLevel) -+ .getRecipeFor(RecipeType.CRAFTING, input, serverLevel, this.craftSlots.getCurrentRecipe()) // Paper - Perf: Improve mass crafting; check last recipe used first - .map(recipe -> recipe.value().getRemainingItems(input)) - .orElseGet(() -> copyAllInputItems(input)) - : CraftingRecipe.defaultCraftingReminder(input); -diff --git a/src/main/java/net/minecraft/world/inventory/TransientCraftingContainer.java b/src/main/java/net/minecraft/world/inventory/TransientCraftingContainer.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/inventory/TransientCraftingContainer.java -+++ b/src/main/java/net/minecraft/world/inventory/TransientCraftingContainer.java -@@ -0,0 +0,0 @@ public class TransientCraftingContainer implements CraftingContainer { - - // CraftBukkit start - add fields - public List transaction = new java.util.ArrayList(); -- private RecipeHolder currentRecipe; -+ private RecipeHolder currentRecipe; // Paper - use correct generic - public Container resultInventory; - private Player owner; - private int maxStack = MAX_STACK; -@@ -0,0 +0,0 @@ public class TransientCraftingContainer implements CraftingContainer { - } - - @Override -- public RecipeHolder getCurrentRecipe() { -+ public RecipeHolder getCurrentRecipe() { // Paper - use correct generic - return this.currentRecipe; - } - - @Override -- public void setCurrentRecipe(RecipeHolder currentRecipe) { -+ public void setCurrentRecipe(RecipeHolder currentRecipe) { // Paper - use correct generic - this.currentRecipe = currentRecipe; - } - diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/CraftingContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/CraftingContainer.java.patch index 2225d1d404..2aef7bff97 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/CraftingContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/CraftingContainer.java.patch @@ -5,11 +5,11 @@ List getItems(); + // CraftBukkit start -+ default net.minecraft.world.item.crafting.RecipeHolder getCurrentRecipe() { ++ default net.minecraft.world.item.crafting.RecipeHolder getCurrentRecipe() { + return null; + } + -+ default void setCurrentRecipe(net.minecraft.world.item.crafting.RecipeHolder recipe) { ++ default void setCurrentRecipe(net.minecraft.world.item.crafting.RecipeHolder recipe) { + } + // CraftBukkit end + diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/CraftingMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/CraftingMenu.java.patch index f490a6a0d3..d04a6f281e 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/CraftingMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/CraftingMenu.java.patch @@ -18,9 +18,34 @@ this.access = access; this.player = playerInventory.player; this.addResultSlot(this.player, 124, 35); -@@ -56,6 +_,7 @@ +@@ -55,7 +_,32 @@ + CraftingInput craftInput = craftSlots.asCraftInput(); ServerPlayer serverPlayer = (ServerPlayer)player; ItemStack itemStack = ItemStack.EMPTY; ++ // Paper start - Perf: Improve mass crafting; check last recipe used first ++ /* ++ When the server crafts all available items in CraftingMenu or InventoryMenu the game ++ checks either 4 or 9 times for each individual craft for a matching recipe for that container. ++ This check can be expensive if 64 total crafts are being performed with the recipe matching logic ++ being run 64 * 9 + 64 times. A breakdown of those times is below. This caches the last matching ++ recipe so that it is checked first and only if it doesn't match does the rest of the matching logic run. ++ ++ Shift-click crafts are processed one at a time, so shift clicking on an item in the result of a iron block craft ++ where all the 9 inputs are full stacks of iron will run 64 iron block crafts. For each of those crafts, the ++ 'remaining' blocks are calculated. This is due to recipes that have leftover items like buckets. This is done ++ for each craft, and done once to get the full 9 leftover items which are usually air. Then 1 item is removed ++ from each of the 9 inputs and each time that happens, logic is triggered to update the result itemstack. So ++ for each craft, that logic is run 9 times (hence the 64 * 9). The + 64 is from the 64 checks for remaining items. ++ ++ After this change, the full iteration over all recipes checking for a match should run once for a full craft to find the ++ initial recipe match. Then that recipe will be checked first for all future recipe match checks. ++ ++ See also: ResultSlot class ++ */ ++ if (recipe == null) { ++ recipe = craftSlots.getCurrentRecipe(); ++ } ++ // Paper end - Perf: Improve mass crafting; check last recipe used first Optional> recipeFor = level.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, craftInput, level, recipe); + craftSlots.setCurrentRecipe(recipeFor.orElse(null)); // CraftBukkit if (recipeFor.isPresent()) { diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/ResultSlot.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/ResultSlot.java.patch new file mode 100644 index 0000000000..c8c1e2c156 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/inventory/ResultSlot.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/inventory/ResultSlot.java ++++ b/net/minecraft/world/inventory/ResultSlot.java +@@ -72,7 +_,7 @@ + private NonNullList getRemainingItems(CraftingInput input, Level level) { + return level instanceof ServerLevel serverLevel + ? serverLevel.recipeAccess() +- .getRecipeFor(RecipeType.CRAFTING, input, serverLevel) ++ .getRecipeFor(RecipeType.CRAFTING, input, serverLevel, this.craftSlots.getCurrentRecipe()) // Paper - Perf: Improve mass crafting; check last recipe used first + .map(recipe -> recipe.value().getRemainingItems(input)) + .orElseGet(() -> copyAllInputItems(input)) + : CraftingRecipe.defaultCraftingReminder(input); diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/TransientCraftingContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/TransientCraftingContainer.java.patch index 898f5850c4..85f7ca8636 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/TransientCraftingContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/TransientCraftingContainer.java.patch @@ -6,7 +6,7 @@ + // CraftBukkit start - add fields + public List transaction = new java.util.ArrayList<>(); -+ private net.minecraft.world.item.crafting.RecipeHolder currentRecipe; ++ private net.minecraft.world.item.crafting.RecipeHolder currentRecipe; + public net.minecraft.world.Container resultInventory; + private Player owner; + private int maxStack = MAX_STACK; @@ -51,12 +51,12 @@ + } + + @Override -+ public net.minecraft.world.item.crafting.RecipeHolder getCurrentRecipe() { ++ public net.minecraft.world.item.crafting.RecipeHolder getCurrentRecipe() { + return this.currentRecipe; + } + + @Override -+ public void setCurrentRecipe(net.minecraft.world.item.crafting.RecipeHolder currentRecipe) { ++ public void setCurrentRecipe(net.minecraft.world.item.crafting.RecipeHolder currentRecipe) { + this.currentRecipe = currentRecipe; + } +