Skip to content

Commit

Permalink
Faster Recipe Matching (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
PrototypeTrousers authored Sep 8, 2021
1 parent 52a61b1 commit baa8059
Show file tree
Hide file tree
Showing 7 changed files with 458 additions and 43 deletions.
1 change: 0 additions & 1 deletion src/main/java/gregtech/GregTechMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ public void onInit(FMLInitializationEvent event) {
GTLog.logger.fatal("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
}
}
RecipeMap.sortMaps();

if (GTValues.isModLoaded(GTValues.MODID_TOP)) {
GTLog.logger.info("TheOneProbe found. Enabling integration...");
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/gregtech/api/recipes/KeySharedStack.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package gregtech.api.recipes;

import gregtech.api.util.ItemStackKey;
import net.minecraft.item.ItemStack;

import javax.annotation.Nonnull;
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;

public class KeySharedStack {

private static final WeakHashMap<ItemStackKey,WeakReference<ItemStackKey>> registeredItemStackKeys = new WeakHashMap<>();

private KeySharedStack() {

}

public static synchronized ItemStackKey getRegisteredStack(final @Nonnull ItemStack itemStack) {
if (itemStack.isEmpty()) {
throw new IllegalArgumentException("stack cannot be empty");
}

int oldStackSize = itemStack.getCount();
itemStack.setCount(1);

ItemStackKey search = new ItemStackKey(itemStack, false);
WeakReference<ItemStackKey> weak = registeredItemStackKeys.get(search);
ItemStackKey ret = null;

if (weak != null) {
ret = weak.get();
}

if (ret == null) {
ret = new ItemStackKey(itemStack);
registeredItemStackKeys.put(ret, new WeakReference<>(ret));
}
itemStack.setCount(oldStackSize);

return ret;
}
}
155 changes: 155 additions & 0 deletions src/main/java/gregtech/api/recipes/Recipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import gregtech.api.recipes.recipeproperties.RecipeProperty;
import gregtech.api.recipes.recipeproperties.RecipePropertyStorage;
import gregtech.api.util.GTUtility;
import gregtech.api.util.ItemStackHashStrategy;
import net.minecraft.item.ItemStack;
import net.minecraft.util.NonNullList;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.items.IItemHandlerModifiable;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.tuple.Pair;

import java.util.*;
Expand Down Expand Up @@ -60,6 +62,10 @@ public static String formatChanceValue(int outputChance) {

private final RecipePropertyStorage recipePropertyStorage;

private static final ItemStackHashStrategy hashStrategy = ItemStackHashStrategy.comparingAll();

private final int hashCode;

public Recipe(List<CountableIngredient> inputs, List<ItemStack> outputs, List<ChanceEntry> chancedOutputs,
List<FluidStack> fluidInputs, List<FluidStack> fluidOutputs,
int duration, int EUt, boolean hidden) {
Expand All @@ -77,6 +83,7 @@ public Recipe(List<CountableIngredient> inputs, List<ItemStack> outputs, List<Ch

//sort not consumables inputs to the end
this.inputs.sort((ing1, ing2) -> ing1.getCount() == 0 ? 1 : 0);
this.hashCode = makeHashCode();
}

public final boolean matches(boolean consumeIfSuccessful, IItemHandlerModifiable inputs, IMultipleTankHandler fluidInputs, MatchingMode matchingMode) {
Expand Down Expand Up @@ -211,6 +218,154 @@ private Pair<Boolean, Integer[]> matchesFluid(List<FluidStack> fluidInputs) {
return Pair.of(true, fluidAmountInTank);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Recipe recipe = (Recipe) o;
return this.EUt == recipe.EUt &&
this.duration == recipe.duration &&
hasSameInputs(recipe) &&
hasSameOutputs(recipe) &&
hasSameChancedOutputs(recipe) &&
hasSameFluidInputs(recipe) &&
hasSameFluidOutputs(recipe) &&
hasSameRecipeProperties(recipe);
}

private int makeHashCode() {
int hash = Objects.hash(EUt, duration);
hash += hashInputs() * 7;
hash += hashOutputs() * 11;
hash += hashChancedOutputs() * 13;
hash += hashFluidList(this.fluidInputs) * 17;
hash += hashFluidList(this.fluidOutputs) * 19;
hash += hashRecipeProperties() * 23;
return hash;
}

@Override
public int hashCode() {
return this.hashCode;
}

private int hashInputs() {
int hash = 0;
for (CountableIngredient countableIngredient : this.inputs) {
for (ItemStack is : countableIngredient.getIngredient().getMatchingStacks()) {
hash += ItemStackHashStrategy.comparingAllButCount().hashCode(is);
hash += countableIngredient.getCount();
}
}
return hash;
}

private boolean hasSameInputs(Recipe otherRecipe) {
if (this.inputs.size() != otherRecipe.inputs.size()) return false;
for (int i = 0; i < inputs.size(); i++) {
for (int j = 0; j < this.inputs.get(i).getIngredient().getMatchingStacks().length; j++) {
if (!hashStrategy.equals(this.inputs.get(i).getIngredient().getMatchingStacks()[j],
otherRecipe.inputs.get(i).getIngredient().getMatchingStacks()[j])) {
return false;
}
}
}
return true;
}

private int hashOutputs() {
int hash = 0;
for (ItemStack is : this.outputs) {
hash += hashStrategy.hashCode(is);
}
return hash;
}

private boolean hasSameOutputs(Recipe otherRecipe) {
if (this.outputs.size() != otherRecipe.outputs.size()) return false;
for (int i = 0; i < outputs.size(); i++) {
if (!hashStrategy.equals(this.outputs.get(i), otherRecipe.outputs.get(i))) {
return false;
}
}
return true;
}

private int hashChancedOutputs() {
int hash = 0;
for (ChanceEntry chanceEntry : this.chancedOutputs) {
hash += hashStrategy.hashCode(chanceEntry.itemStack);
hash += chanceEntry.chance;
hash += chanceEntry.boostPerTier;
}
return hash;
}

private boolean hasSameChancedOutputs(Recipe otherRecipe) {
if (this.chancedOutputs.size() != otherRecipe.chancedOutputs.size()) return false;
for (int i = 0; i < chancedOutputs.size(); i++) {
if (!hashStrategy.equals(this.chancedOutputs.get(i).itemStack, otherRecipe.chancedOutputs.get(i).itemStack)) {
return false;
}
}
return true;
}

public int hashFluidList(List<FluidStack> fluids) {
int hash = 0;
for (FluidStack fluidStack : fluids) {
hash += new FluidKey(fluidStack).hashCode();
}
return hash;
}

private boolean hasSameFluidInputs(Recipe otherRecipe) {
if (this.fluidInputs.size() != otherRecipe.fluidInputs.size()) return false;
for (int i = 0; i < fluidInputs.size(); i++) {
if (!fluidInputs.get(i).isFluidStackIdentical(otherRecipe.fluidInputs.get(i))) {
return false;
}
}
return true;
}

private boolean hasSameFluidOutputs(Recipe otherRecipe) {
if (this.fluidOutputs.size() != otherRecipe.fluidOutputs.size()) return false;
for (int i = 0; i < fluidOutputs.size(); i++) {
if (!fluidOutputs.get(i).isFluidStackIdentical(otherRecipe.fluidOutputs.get(i))) {
return false;
}
}
return true;
}

private int hashRecipeProperties() {
int hash = 0;
for (Map.Entry<RecipeProperty<?>, Object> propertyObjectEntry : this.recipePropertyStorage.getRecipeProperties()) {
hash += propertyObjectEntry.getKey().hashCode();
}
return hash;
}

private boolean hasSameRecipeProperties(Recipe otherRecipe) {
if (this.getPropertyCount() != otherRecipe.getPropertyCount()) return false;
return this.recipePropertyStorage.getRecipeProperties().containsAll(otherRecipe.recipePropertyStorage.getRecipeProperties());
}

@Override
public String toString() {
return new ToStringBuilder(this)
.append("inputs", inputs)
.append("outputs", outputs)
.append("chancedOutputs", chancedOutputs)
.append("fluidInputs", fluidInputs)
.append("fluidOutputs", fluidOutputs)
.append("duration", duration)
.append("EUt", EUt)
.append("hidden", hidden)
.toString();
}

///////////////////
// Getters //
///////////////////
Expand Down
Loading

0 comments on commit baa8059

Please sign in to comment.