perf(CraftingStation): cache last matched recipe (#304)

This commit is contained in:
Caedis authored and GitHub committed 2026-07-17 18:14:05 +02:00
1 parent b8dd2f9fd9
commit 2123db72e1
6 files changed
+119 -18

No files matched your search

@@ -1,6 +1,7 @@
package tconstruct.tools.inventory;
import java.lang.ref.WeakReference;
import java.util.List;
import javax.annotation.Nonnull;
@@ -15,6 +16,7 @@ import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.world.World;
import tconstruct.items.tools.Arrow;
@@ -48,6 +50,12 @@ public class CraftingStationContainer extends Container {
public CraftingStationLogic logic;
EntityPlayer player;
/** Last matched recipe, tried first to avoid rescanning the whole recipe list on every matrix change. */
private IRecipe lastRecipe;
/** While true, matrix changes don't recompute the result. Batches ingredient consumption into one lookup. */
private boolean suppressCraftingUpdates;
public CraftingStationContainer(InventoryPlayer inventoryplayer, CraftingStationLogic logic, int x, int y, int z) {
this.worldObj = logic.getWorldObj();
this.player = inventoryplayer.player;
@@ -74,6 +82,7 @@ public class CraftingStationContainer extends Container {
// 0 - crafting slot
this.addSlotToContainer(
new SlotCraftingStation(
this,
inventoryplayer.player,
this.craftMatrix,
this.craftResult,
@@ -280,11 +289,74 @@ public class CraftingStationContainer extends Container {
}
public void onCraftMatrixChanged(IInventory par1IInventory) {
if (suppressCraftingUpdates) return;
ItemStack tool = modifyItem();
if (tool != null) this.craftResult.setInventorySlotContents(0, tool);
else this.craftResult.setInventorySlotContents(
0,
CraftingManager.getInstance().findMatchingRecipe(this.craftMatrix, this.worldObj));
else this.craftResult.setInventorySlotContents(0, findMatchingRecipeCached());
}
/** Like {@link CraftingManager#findMatchingRecipe} but tries the last matched recipe first. */
private ItemStack findMatchingRecipeCached() {
// Vanilla's two-item tool repair takes precedence over the recipe list
if (isVanillaToolRepair()) {
return CraftingManager.getInstance().findMatchingRecipe(this.craftMatrix, this.worldObj);
}
IRecipe cached = this.lastRecipe;
if (cached != null && cached.matches(this.craftMatrix, this.worldObj)) {
return cached.getCraftingResult(this.craftMatrix);
}
this.lastRecipe = null;
@SuppressWarnings("unchecked")
List<IRecipe> recipes = CraftingManager.getInstance().getRecipeList();
for (int i = 0; i < recipes.size(); i++) {
IRecipe recipe = recipes.get(i);
if (recipe.matches(this.craftMatrix, this.worldObj)) {
this.lastRecipe = recipe;
return recipe.getCraftingResult(this.craftMatrix);
}
}
return null;
}
/**
* Mirrors the repair check in {@link CraftingManager#findMatchingRecipe}: two size-1 stacks of one repairable item.
*/
private boolean isVanillaToolRepair() {
ItemStack first = null;
ItemStack second = null;
int found = 0;
for (int i = 0; i < this.craftMatrix.getSizeInventory(); i++) {
ItemStack stack = this.craftMatrix.getStackInSlot(i);
if (stack == null) continue;
found++;
if (found == 1) first = stack;
else if (found == 2) second = stack;
else return false;
}
return found == 2 && first.getItem() == second.getItem()
&& first.stackSize == 1
&& second.stackSize == 1
&& first.getItem().isRepairable();
}
/**
* Suppresses result updates until {@link #endBatchCraftingUpdate}; each consumed ingredient fires a lookup
* otherwise.
*/
void beginBatchCraftingUpdate() {
suppressCraftingUpdates = true;
}
/** Re-enables result updates and recomputes once. */
void endBatchCraftingUpdate() {
suppressCraftingUpdates = false;
this.onCraftMatrixChanged(this.craftMatrix);
}
@Override
@@ -438,16 +510,19 @@ public class CraftingStationContainer extends Container {
public void dumpCraftingGrid() {
if (logic.slotCount == 0) return;
// 46 is the first slot index of the attached inventory
for (int i = 0; i < 9; i++) {
ItemStack stack = craftMatrix.getStackInSlot(i);
if (stack != null && stack.stackSize > 0) {
if (mergeItemStack(stack, 46, 46 + logic.slotCount, false)) {
craftMatrix.setInventorySlotContents(i, stack.stackSize > 0 ? stack : null);
beginBatchCraftingUpdate();
try {
// 46 is the first slot index of the attached inventory
for (int i = 0; i < 9; i++) {
ItemStack stack = craftMatrix.getStackInSlot(i);
if (stack != null && stack.stackSize > 0) {
if (mergeItemStack(stack, 46, 46 + logic.slotCount, false)) {
craftMatrix.setInventorySlotContents(i, stack.stackSize > 0 ? stack : null);
}
}
}
} finally {
endBatchCraftingUpdate();
}
this.onCraftMatrixChanged(this.craftMatrix);
}
}
@@ -11,18 +11,30 @@ import tconstruct.library.tools.AbilityHelper;
public class SlotCraftingStation extends SlotCrafting {
private final CraftingStationContainer container;
private final IInventory matrix;
private final EntityPlayer player;
public SlotCraftingStation(EntityPlayer par1EntityPlayer, IInventory par2IInventory, IInventory par3iInventory,
int par4, int par5, int par6) {
public SlotCraftingStation(CraftingStationContainer container, EntityPlayer par1EntityPlayer,
IInventory par2IInventory, IInventory par3iInventory, int par4, int par5, int par6) {
super(par1EntityPlayer, par2IInventory, par3iInventory, par4, par5, par6);
this.container = container;
this.matrix = par2IInventory;
this.player = par1EntityPlayer;
}
@Override
public void onPickupFromSlot(EntityPlayer player, ItemStack stack) {
// Batch ingredient consumption into a single recipe lookup
container.beginBatchCraftingUpdate();
try {
consumeIngredients(player, stack);
} finally {
container.endBatchCraftingUpdate();
}
}
private void consumeIngredients(EntityPlayer player, ItemStack stack) {
ItemStack tool = this.matrix.getStackInSlot(4);
if (stack.getItem() instanceof IModifyable modifyable && tool != null
&& tool.getItem() instanceof IModifyable) {
@@ -32,6 +32,12 @@ public class CraftingStationLogic extends InventoryLogic implements ISidedInvent
public int invRows, invColumns, slotCount;
private static final int[] NO_SLOTS = new int[0];
/** Cached result of {@link #getInventories()}; rebuilt when the adjacent inventories are rescanned. */
@SuppressWarnings("rawtypes")
private WeakReference[] inventories = new WeakReference[4];
public CraftingStationLogic() {
super(10); // 9 for crafting, 1 for output
}
@@ -115,6 +121,8 @@ public class CraftingStationLogic extends InventoryLogic implements ISidedInvent
}
}
this.inventories = new WeakReference[] { this.chest, this.doubleChest, this.patternChest, this.furnace };
return new CraftingStationContainer(inventoryplayer, this, x, y, z);
}
@@ -168,12 +176,12 @@ public class CraftingStationLogic extends InventoryLogic implements ISidedInvent
@SuppressWarnings("rawtypes")
public WeakReference[] getInventories() {
return new WeakReference[] { this.chest, this.doubleChest, this.patternChest, this.furnace };
return this.inventories;
}
@Override
public int[] getAccessibleSlotsFromSide(int var1) {
return new int[] {};
return NO_SLOTS;
}
@Override
@@ -16,6 +16,8 @@ import tconstruct.tools.inventory.PartCrafterContainer;
public class PartBuilderLogic extends InventoryLogic implements ISidedInventory {
private static final int[] NO_SLOTS = new int[0];
boolean craftedTop;
boolean craftedBottom;
@@ -138,7 +140,7 @@ public class PartBuilderLogic extends InventoryLogic implements ISidedInventory
@Override
public int[] getAccessibleSlotsFromSide(int side) {
return new int[0];
return NO_SLOTS;
}
@Override
@@ -15,6 +15,8 @@ import tconstruct.tools.inventory.PatternShaperContainer;
public class StencilTableLogic extends InventoryLogic implements ISidedInventory {
private static final int[] NO_SLOTS = new int[0];
private ItemStack selectedStack;
public StencilTableLogic() {
@@ -119,7 +121,7 @@ public class StencilTableLogic extends InventoryLogic implements ISidedInventory
@Override
public int[] getAccessibleSlotsFromSide(int side) {
return new int[0];
return NO_SLOTS;
}
@Override
@@ -20,6 +20,8 @@ import tconstruct.tools.inventory.ToolStationContainer;
public class ToolStationLogic extends InventoryLogic implements ISidedInventory {
private static final int[] NO_SLOTS = new int[0];
public ItemStack previousTool;
public String toolName;
@@ -158,7 +160,7 @@ public class ToolStationLogic extends InventoryLogic implements ISidedInventory
@Override
public int[] getAccessibleSlotsFromSide(int side) {
return new int[0];
return NO_SLOTS;
}
@Override