2023-08-22 07:54:28 +02:00
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
|
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
|
|
|
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/CraftingMenu.java b/src/main/java/net/minecraft/world/inventory/CraftingMenu.java
|
2023-09-22 18:59:52 +02:00
|
|
|
index a7aa2a4845cbf5a0843dcb93f7bdc5501f62a145..4c8ce073094e55ea0df67fe02c0d1cc8aef76562 100644
|
2023-08-22 07:54:28 +02:00
|
|
|
--- a/src/main/java/net/minecraft/world/inventory/CraftingMenu.java
|
|
|
|
+++ b/src/main/java/net/minecraft/world/inventory/CraftingMenu.java
|
2023-09-22 18:59:52 +02:00
|
|
|
@@ -76,7 +76,8 @@ public class CraftingMenu extends RecipeBookMenu<CraftingContainer> {
|
2023-08-22 07:54:28 +02:00
|
|
|
if (!world.isClientSide) {
|
|
|
|
ServerPlayer entityplayer = (ServerPlayer) player;
|
|
|
|
ItemStack itemstack = ItemStack.EMPTY;
|
2023-09-22 18:59:52 +02:00
|
|
|
- Optional<RecipeHolder<CraftingRecipe>> optional = world.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, craftingInventory, world);
|
|
|
|
+ final RecipeHolder<?> currentRecipe = craftingInventory.getCurrentRecipe(); // Paper - check last recipe used first
|
|
|
|
+ Optional<RecipeHolder<CraftingRecipe>> optional = currentRecipe == null ? world.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, craftingInventory, world) : world.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, craftingInventory, world, currentRecipe.id()).map(com.mojang.datafixers.util.Pair::getSecond); // Paper - check last recipe used first
|
2023-08-22 07:54:28 +02:00
|
|
|
|
|
|
|
if (optional.isPresent()) {
|
2023-09-22 18:59:52 +02:00
|
|
|
RecipeHolder<CraftingRecipe> recipeholder = (RecipeHolder) optional.get();
|
2023-08-22 07:54:28 +02:00
|
|
|
diff --git a/src/main/java/net/minecraft/world/inventory/ResultSlot.java b/src/main/java/net/minecraft/world/inventory/ResultSlot.java
|
2023-09-22 19:59:56 +02:00
|
|
|
index 525ba0bdc4c6782480930bea94f73a72efe6fc4c..2554f7b1d687f83f42d69bf8ab54336ba793e301 100644
|
2023-08-22 07:54:28 +02:00
|
|
|
--- a/src/main/java/net/minecraft/world/inventory/ResultSlot.java
|
|
|
|
+++ b/src/main/java/net/minecraft/world/inventory/ResultSlot.java
|
|
|
|
@@ -59,7 +59,7 @@ public class ResultSlot extends Slot {
|
|
|
|
@Override
|
|
|
|
public void onTake(Player player, ItemStack stack) {
|
|
|
|
this.checkTakeAchievements(stack);
|
|
|
|
- NonNullList<ItemStack> nonNullList = player.level().getRecipeManager().getRemainingItemsFor(RecipeType.CRAFTING, this.craftSlots, player.level());
|
2023-09-22 18:59:52 +02:00
|
|
|
+ NonNullList<ItemStack> nonNullList = player.level().getRecipeManager().getRemainingItemsFor(RecipeType.CRAFTING, this.craftSlots, player.level(), this.craftSlots.getCurrentRecipe() != null ? this.craftSlots.getCurrentRecipe().id() : null); // Paper - check last recipe used first
|
2023-08-22 07:54:28 +02:00
|
|
|
|
|
|
|
for(int i = 0; i < nonNullList.size(); ++i) {
|
|
|
|
ItemStack itemStack = this.craftSlots.getItem(i);
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java b/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java
|
2023-12-05 18:20:55 +01:00
|
|
|
index 43aacadcf8be10432a61c83f69ee86580c86d0a3..388363948595247471fa4c3c6801fc493d61c0d0 100644
|
2023-08-22 07:54:28 +02:00
|
|
|
--- a/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java
|
|
|
|
+++ b/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java
|
2023-09-22 18:59:52 +02:00
|
|
|
@@ -122,13 +122,16 @@ public class RecipeManager extends SimpleJsonResourceReloadListener {
|
|
|
|
RecipeHolder<T> recipeholder = (RecipeHolder) map.get(id);
|
2023-08-22 07:54:28 +02:00
|
|
|
|
2023-09-22 18:59:52 +02:00
|
|
|
if (recipeholder != null && recipeholder.value().matches(inventory, world)) {
|
|
|
|
+ inventory.setCurrentRecipe(recipeholder); // Paper
|
|
|
|
return Optional.of(Pair.of(id, recipeholder));
|
2023-08-22 07:54:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
+ inventory.setCurrentRecipe(null); // Paper - clear before it might be set again
|
|
|
|
return map.entrySet().stream().filter((entry) -> {
|
2023-09-22 18:59:52 +02:00
|
|
|
return ((RecipeHolder) entry.getValue()).value().matches(inventory, world);
|
2023-08-22 07:54:28 +02:00
|
|
|
}).findFirst().map((entry) -> {
|
|
|
|
+ inventory.setCurrentRecipe(entry.getValue()); // Paper
|
2023-09-22 18:59:52 +02:00
|
|
|
return Pair.of((ResourceLocation) entry.getKey(), (RecipeHolder) entry.getValue());
|
2023-08-22 07:54:28 +02:00
|
|
|
});
|
|
|
|
}
|
2023-09-22 18:59:52 +02:00
|
|
|
@@ -150,7 +153,12 @@ public class RecipeManager extends SimpleJsonResourceReloadListener {
|
2023-08-22 07:54:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public <C extends Container, T extends Recipe<C>> NonNullList<ItemStack> getRemainingItemsFor(RecipeType<T> type, C inventory, Level world) {
|
2023-09-22 18:59:52 +02:00
|
|
|
- Optional<RecipeHolder<T>> optional = this.getRecipeFor(type, inventory, world);
|
2023-08-22 07:54:28 +02:00
|
|
|
+ // Paper start - check last recipe used first
|
|
|
|
+ return this.getRemainingItemsFor(type, inventory, world, null);
|
|
|
|
+ }
|
|
|
|
+ public <C extends Container, T extends Recipe<C>> NonNullList<ItemStack> getRemainingItemsFor(RecipeType<T> type, C inventory, Level world, @Nullable ResourceLocation firstToCheck) {
|
2023-09-22 18:59:52 +02:00
|
|
|
+ Optional<RecipeHolder<T>> optional = firstToCheck == null ? this.getRecipeFor(type, inventory, world) : this.getRecipeFor(type, inventory, world, firstToCheck).map(Pair::getSecond);
|
2023-08-22 07:54:28 +02:00
|
|
|
+ // Paper end
|
|
|
|
|
|
|
|
if (optional.isPresent()) {
|
2023-09-22 18:59:52 +02:00
|
|
|
return ((RecipeHolder) optional.get()).value().getRemainingItems(inventory);
|