From 333e8a35ca7a1722b2bb0fe437c9d9fa67257410 Mon Sep 17 00:00:00 2001 From: Elenterius Date: Tue, 1 Oct 2024 20:50:15 +0200 Subject: [PATCH] fix: fix bio-forge not counting ingredients with nbt data correctly --- .../client/gui/BioForgeScreenController.java | 24 +++++++----- .../crafting/recipe/BioForgeRecipe.java | 31 ++++++++++++++++ .../crafting/recipe/IngredientStack.java | 13 +++++++ .../biomancy/menu/BioForgeMenu.java | 37 +++---------------- .../biomancy/util/ItemStackCounter.java | 30 +++++++++++---- 5 files changed, 87 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/github/elenterius/biomancy/client/gui/BioForgeScreenController.java b/src/main/java/com/github/elenterius/biomancy/client/gui/BioForgeScreenController.java index 4f2c4f00d..ce3f0939a 100644 --- a/src/main/java/com/github/elenterius/biomancy/client/gui/BioForgeScreenController.java +++ b/src/main/java/com/github/elenterius/biomancy/client/gui/BioForgeScreenController.java @@ -11,6 +11,7 @@ import com.github.elenterius.biomancy.menu.BioForgeTab; import com.github.elenterius.biomancy.mixin.accessor.RecipeCollectionAccessor; import com.github.elenterius.biomancy.network.ModNetworkHandler; +import com.github.elenterius.biomancy.util.ItemStackCounter; import com.google.common.collect.Lists; import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectSet; @@ -23,7 +24,6 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.stats.RecipeBook; import net.minecraft.util.Mth; -import net.minecraft.world.entity.player.StackedContents; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Recipe; @@ -40,7 +40,7 @@ class BioForgeScreenController { return b.sortPriority() - a.sortPriority(); }; private static RecipeSelection recipeSelection = RecipeSelection.EMPTY; //volatile client cache - private final StackedContents itemCounter; + private final ItemStackCounter itemCounter; private final List tabs; private final Minecraft minecraft; private final BioForgeMenu menu; @@ -59,8 +59,8 @@ public BioForgeScreenController(Minecraft minecraft, BioForgeMenu menu) { tabs = ModBioForgeTabs.REGISTRY.get().getValues().stream().sorted(CATEGORY_COMPARATOR).toList(); playerInvChanges = getPlayer().getInventory().getTimesChanged(); - itemCounter = new StackedContents(); - getPlayer().getInventory().fillStackedContents(itemCounter); + itemCounter = new ItemStackCounter(); + itemCounter.accountStacks(getPlayer().getInventory().items); //restore selected recipe from volatile client cache if (recipeSelection != RecipeSelection.EMPTY && menu.getSelectedRecipe() == null && recipeSelection.recipe != null) { @@ -145,8 +145,7 @@ public BioForgeRecipe getRecipeByGrid(int gridIndex) { } public int getTotalItemCountInPlayerInv(ItemStack stack) { - int index = StackedContents.getStackingIndex(stack); - return itemCounter.contents.get(index); + return itemCounter.getCount(stack); } public boolean hasSufficientIngredientCount(IngredientStack ingredientStack) { @@ -236,7 +235,7 @@ public void trackPlayerInvChanges() { private void countPlayerInvItems() { itemCounter.clear(); - getPlayer().getInventory().fillStackedContents(itemCounter); + itemCounter.accountStacks(getPlayer().getInventory().items); updateAndSearchRecipes(); } @@ -247,7 +246,7 @@ public void updateSearchString(String searchString) { currentSearchString = searchString; } - private static void canCraftRecipe(RecipeCollection recipeCollection, StackedContents handler, RecipeBook book, boolean isCreativePlayer) { + private static void canCraftRecipe(RecipeCollection recipeCollection, ItemStackCounter itemCounter, RecipeBook book, boolean isCreativePlayer) { RecipeCollectionAccessor accessor = (RecipeCollectionAccessor) recipeCollection; Set> fitDimensions = accessor.biomancy$getFitDimensions(); Set> craftable = accessor.biomancy$getCraftable(); @@ -263,8 +262,13 @@ private static void canCraftRecipe(RecipeCollection recipeCollection, StackedCon fitDimensions.remove(recipe); } - if (canCraftRecipe && handler.canCraft(recipe, null)) { - craftable.add(recipe); + if (recipe instanceof BioForgeRecipe bioForgeRecipe) { + if (canCraftRecipe && bioForgeRecipe.isCraftable(itemCounter)) { + craftable.add(recipe); + } + else { + craftable.remove(recipe); + } } else { craftable.remove(recipe); diff --git a/src/main/java/com/github/elenterius/biomancy/crafting/recipe/BioForgeRecipe.java b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/BioForgeRecipe.java index 897cd3ed9..cba340723 100644 --- a/src/main/java/com/github/elenterius/biomancy/crafting/recipe/BioForgeRecipe.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/BioForgeRecipe.java @@ -5,6 +5,7 @@ import com.github.elenterius.biomancy.init.ModItems; import com.github.elenterius.biomancy.init.ModRecipes; import com.github.elenterius.biomancy.menu.BioForgeTab; +import com.github.elenterius.biomancy.util.ItemStackCounter; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonSyntaxException; @@ -76,6 +77,36 @@ public boolean isCraftable(StackedContents itemCounter) { return true; } + public boolean isCraftable(ItemStackCounter itemCounter) { + int[] residuals = new int[ingredients.size()]; + int totalResidual = 0; + for (int i = 0; i < ingredients.size(); i++) { + int count = ingredients.get(i).count(); + residuals[i] = count; + totalResidual += count; + } + + for (ItemStackCounter.CountedItem countedItem : itemCounter.getItemCounts()) { + if (totalResidual <= 0) return true; + + int available = countedItem.amount(); + + for (int i = 0; i < ingredients.size(); i++) { + if (available <= 0) break; + + final int residual = residuals[i]; + if (residual > 0 && ingredients.get(i).testItem(countedItem.stack())) { + final int amount = Math.min(residual, available); + residuals[i] -= amount; + available -= amount; + totalResidual -= amount; + } + } + } + + return totalResidual <= 0; + } + @Override public boolean matches(Container inv, Level level) { int[] countedIngredients = new int[ingredients.size()]; diff --git a/src/main/java/com/github/elenterius/biomancy/crafting/recipe/IngredientStack.java b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/IngredientStack.java index 5240b4ac6..a955e7aad 100644 --- a/src/main/java/com/github/elenterius/biomancy/crafting/recipe/IngredientStack.java +++ b/src/main/java/com/github/elenterius/biomancy/crafting/recipe/IngredientStack.java @@ -1,5 +1,6 @@ package com.github.elenterius.biomancy.crafting.recipe; +import com.github.elenterius.biomancy.util.ItemStackCounter; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import it.unimi.dsi.fastutil.ints.IntList; @@ -38,6 +39,18 @@ public boolean hasSufficientCount(StackedContents itemCounter) { return false; } + public boolean hasSufficientCount(ItemStackCounter itemCounter) { + List itemCounts = itemCounter.getItemCounts(); + + int n = 0; + for (ItemStackCounter.CountedItem countedItem : itemCounts) { + if (ingredient.test(countedItem.stack())) n += countedItem.amount(); + if (n >= count) return true; + } + + return false; + } + public List getItemsWithCount() { if (count == 1) return List.of(ingredient.getItems()); return Arrays.stream(ingredient.getItems()).map(this::copyItemStackWithCount).toList(); diff --git a/src/main/java/com/github/elenterius/biomancy/menu/BioForgeMenu.java b/src/main/java/com/github/elenterius/biomancy/menu/BioForgeMenu.java index fd483d8cd..d8682517f 100644 --- a/src/main/java/com/github/elenterius/biomancy/menu/BioForgeMenu.java +++ b/src/main/java/com/github/elenterius/biomancy/menu/BioForgeMenu.java @@ -12,6 +12,7 @@ import com.github.elenterius.biomancy.menu.slot.FuelSlot; import com.github.elenterius.biomancy.menu.slot.ISlotZone; import com.github.elenterius.biomancy.menu.slot.OutputSlot; +import com.github.elenterius.biomancy.util.ItemStackCounter; import com.github.elenterius.biomancy.util.SoundUtil; import com.github.elenterius.biomancy.util.fuel.FuelHandler; import net.minecraft.network.FriendlyByteBuf; @@ -20,7 +21,6 @@ import net.minecraft.util.Mth; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.player.StackedContents; import net.minecraft.world.inventory.ContainerLevelAccess; import net.minecraft.world.inventory.ResultContainer; import net.minecraft.world.inventory.Slot; @@ -37,7 +37,7 @@ public class BioForgeMenu extends PlayerContainerMenu { private final BehavioralInventory fuelInventory; private final BioForgeStateData stateData; private int playerInvChanges; - private final StackedContents itemCounter = new StackedContents(); + private final ItemStackCounter itemCounter = new ItemStackCounter(); @Nullable private BioForgeRecipe selectedRecipe; @@ -97,7 +97,7 @@ private void trackPlayerInvChanges(ServerPlayer serverPlayer, Inventory inventor private void countPlayerInvItems(ServerPlayer serverPlayer, Inventory inventory) { itemCounter.clear(); - inventory.fillStackedContents(itemCounter); + itemCounter.accountStacks(inventory.items); updateResultSlot(serverPlayer); } @@ -105,7 +105,7 @@ private void updateResultSlot(ServerPlayer serverPlayer) { ItemStack resultStack = ItemStack.EMPTY; BioForgeRecipe recipe = getSelectedRecipe(); - if (recipe != null && resultContainer.setRecipeUsed(serverPlayer.level(), serverPlayer, recipe) && canCraft(serverPlayer, recipe)) { + if (recipe != null && resultContainer.setRecipeUsed(serverPlayer.level(), serverPlayer, recipe) && canCraft(recipe)) { resultStack = recipe.getResultItem(serverPlayer.level().registryAccess()).copy(); } @@ -312,33 +312,8 @@ public void onTake(Player player, ItemStack stack) { } - private boolean canCraft(Player player, @Nullable BioForgeRecipe recipe) { - if (recipe == null || getFuelAmount() < recipe.getCraftingCostNutrients() || !recipe.isCraftable(itemCounter)) return false; - - Inventory inventory = player.getInventory(); - - //count available ingredients - List ingredients = recipe.getIngredientQuantities(); - int[] countedIngredients = new int[ingredients.size()]; - for (int idx = 0; idx < inventory.items.size(); idx++) { - ItemStack foundStack = inventory.items.get(idx); - if (!foundStack.isEmpty()) { - for (int i = 0; i < ingredients.size(); i++) { - if (ingredients.get(i).testItem(foundStack)) { - countedIngredients[i] += foundStack.getCount(); - break; - } - } - } - } - - //check that all requirements are fulfilled - for (int i = 0; i < ingredients.size(); i++) { - int requiredCount = ingredients.get(i).count(); - if (countedIngredients[i] < requiredCount) return false; - } - - return true; + private boolean canCraft(@Nullable BioForgeRecipe recipe) { + return recipe != null && getFuelAmount() >= recipe.getCraftingCostNutrients() && recipe.isCraftable(itemCounter); } private void consumeCraftingIngredients(Player player, BioForgeRecipe recipe) { diff --git a/src/main/java/com/github/elenterius/biomancy/util/ItemStackCounter.java b/src/main/java/com/github/elenterius/biomancy/util/ItemStackCounter.java index fbf73be21..d776dab1a 100644 --- a/src/main/java/com/github/elenterius/biomancy/util/ItemStackCounter.java +++ b/src/main/java/com/github/elenterius/biomancy/util/ItemStackCounter.java @@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.ints.IntComparators; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import net.minecraft.core.NonNullList; import net.minecraft.world.Container; import net.minecraft.world.item.ItemStack; import net.minecraftforge.items.IItemHandler; @@ -59,19 +60,13 @@ record EntryKey(int hash, ItemStack stack) implements HashKey { public EntryKey(int hash, ItemStack stack) { this.hash = hash; - this.stack = copyWithMinCount(stack); + this.stack = stack.copyWithCount(1); } EntryKey(ItemStack stack) { this(Objects.hash(ForgeRegistries.ITEMS.getKey(stack.getItem()), stack.getTag()), stack); } - private static ItemStack copyWithMinCount(ItemStack stack) { - ItemStack copy = stack.copy(); - copy.setCount(1); - return copy; - } - @Override public boolean equals(Object obj) { if (this == obj) return true; @@ -87,9 +82,21 @@ public int hashCode() { } private final Object2IntMap countedItemStacks = new Object2IntOpenHashMap<>(); + private List countedItems = null; public record CountedItem(ItemStack stack, int amount) {} + public List getItemCounts() { + if (countedItems != null) return countedItems; + + countedItems = countedItemStacks.object2IntEntrySet().stream() + .sorted((a, b) -> IntComparators.OPPOSITE_COMPARATOR.compare(a.getIntValue(), b.getIntValue())) + .map(entry -> new CountedItem(entry.getKey().stack(), entry.getIntValue())) + .toList(); + + return countedItems; + } + public List getItemCountSorted(int limit, boolean ascending) { IntComparator comparator = ascending ? IntComparators.NATURAL_COMPARATOR : IntComparators.OPPOSITE_COMPARATOR; return countedItemStacks.object2IntEntrySet().stream() @@ -107,6 +114,12 @@ public void accountStack(ItemStack stack) { accountStack(stack, stack.getCount()); } + public void accountStacks(NonNullList stacks) { + for (ItemStack stack : stacks) { + accountStack(stack); + } + } + public void accountStacks(Container container) { for (int i = 0; i < container.getContainerSize(); i++) { accountStack(container.getItem(i)); @@ -132,6 +145,8 @@ private void put(ItemStack template, int amount) { EntryKey entryKey = new EntryKey(key.hash(), template); countedItemStacks.put(entryKey, amount); } + + countedItems = null; } public boolean has(ItemStack template) { @@ -144,6 +159,7 @@ public int getCount(ItemStack template) { public void clear() { countedItemStacks.clear(); + countedItems = null; } }