Compare commits

...

2 Commits

Author SHA1 Message Date
Tom Dodd a49c75bce0 v2o.4.7 2020-10-05 09:00:46 +01:00
Tom Dodd 84360b38c0 v2o.4.7 2020-10-05 08:37:24 +01:00
5 changed files with 66 additions and 65 deletions

View File

@ -3,7 +3,7 @@ v2o.4.7
+ Added quantum computer Python Qiskit code generation + Added quantum computer Python Qiskit code generation
* Code is now generated by using a code generator component rather than the controller * Code is now generated by using a code generator component rather than the controller
+ Added salt to recipe hash caching to avoid clashes (salt is incremented if a clash occurs and hashes are recalculated) + Fixed issues with rare hash clashes while caching recipe inputs
* Fixed broken quantum computer component recipes * Fixed broken quantum computer component recipes

View File

@ -11,6 +11,7 @@ import com.google.common.collect.Lists;
import crafttweaker.annotations.ZenRegister; import crafttweaker.annotations.ZenRegister;
import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.longs.*; import it.unimi.dsi.fastutil.longs.*;
import it.unimi.dsi.fastutil.objects.*;
import nc.recipe.ingredient.*; import nc.recipe.ingredient.*;
import nc.tile.internal.fluid.Tank; import nc.tile.internal.fluid.Tank;
import nc.util.*; import nc.util.*;
@ -25,8 +26,7 @@ public abstract class AbstractRecipeHandler<RECIPE extends IRecipe> {
protected @Nonnull List<RECIPE> recipeList = new ArrayList<>(); protected @Nonnull List<RECIPE> recipeList = new ArrayList<>();
protected @Nonnull Long2ObjectMap<RECIPE> recipeCache = new Long2ObjectOpenHashMap<>(); protected @Nonnull Long2ObjectMap<ObjectSet<RECIPE>> recipeCache = new Long2ObjectOpenHashMap<>();
protected long cacheSalt = 0L;
private static List<Class<?>> validItemInputs = Lists.newArrayList(IItemIngredient.class, ArrayList.class, String.class, Item.class, Block.class, ItemStack.class, ItemStack[].class); private static List<Class<?>> validItemInputs = Lists.newArrayList(IItemIngredient.class, ArrayList.class, String.class, Item.class, Block.class, ItemStack.class, ItemStack[].class);
private static List<Class<?>> validFluidInputs = Lists.newArrayList(IFluidIngredient.class, ArrayList.class, String.class, Fluid.class, FluidStack.class, FluidStack[].class); private static List<Class<?>> validFluidInputs = Lists.newArrayList(IFluidIngredient.class, ArrayList.class, String.class, Fluid.class, FluidStack.class, FluidStack[].class);
@ -46,18 +46,19 @@ public abstract class AbstractRecipeHandler<RECIPE extends IRecipe> {
@ZenMethod @ZenMethod
public abstract List<RECIPE> getRecipeList(); public abstract List<RECIPE> getRecipeList();
public Long2ObjectMap<RECIPE> getRecipeCache() {
return recipeCache;
}
public abstract void addRecipe(Object... objects); public abstract void addRecipe(Object... objects);
public @Nullable RecipeInfo<RECIPE> getRecipeInfoFromInputs(List<ItemStack> itemInputs, List<Tank> fluidInputs) { public @Nullable RecipeInfo<RECIPE> getRecipeInfoFromInputs(List<ItemStack> itemInputs, List<Tank> fluidInputs) {
RECIPE recipe = recipeCache.get(RecipeHelper.hashMaterialsRaw(itemInputs, fluidInputs, cacheSalt)); long hash = RecipeHelper.hashMaterialsRaw(itemInputs, fluidInputs);
if (recipe != null) { if (recipeCache.containsKey(hash)) {
RecipeMatchResult matchResult = recipe.matchInputs(itemInputs, fluidInputs); ObjectSet<RECIPE> set = recipeCache.get(hash);
if (matchResult.matches()) { for (RECIPE recipe : set) {
return new RecipeInfo(recipe, matchResult); if (recipe != null) {
RecipeMatchResult matchResult = recipe.matchInputs(itemInputs, fluidInputs);
if (matchResult.matches()) {
return new RecipeInfo(recipe, matchResult);
}
}
} }
} }
return null; return null;
@ -95,65 +96,68 @@ public abstract class AbstractRecipeHandler<RECIPE extends IRecipe> {
} }
public void refreshCache() { public void refreshCache() {
cacheSalt = 0L; recipeCache.clear();
do { fillHashCache();
recipeCache.clear();
}
while (!fillHashCache());
} }
public boolean fillHashCache() { protected void fillHashCache() {
recipeLoop: for (RECIPE recipe : recipeList) { for (RECIPE recipe : recipeList) {
List<List<ItemStack>> itemInputLists = new ArrayList<>();
List<List<FluidStack>> fluidInputLists = new ArrayList<>();
for (IItemIngredient item : recipe.getItemIngredients()) {
itemInputLists.add(item.getInputStackHashingList());
}
for (IFluidIngredient fluid : recipe.getFluidIngredients()) {
fluidInputLists.add(fluid.getInputStackHashingList());
}
int arrSize = recipe.getItemIngredients().size() + recipe.getFluidIngredients().size();
int[] inputNumbers = new int[arrSize];
Arrays.fill(inputNumbers, 0);
int[] maxNumbers = new int[arrSize];
for (int i = 0; i < itemInputLists.size(); i++) {
int maxNumber = itemInputLists.get(i).size() - 1;
if (maxNumber < 0) {
continue recipeLoop;
}
maxNumbers[i] = maxNumber;
}
for (int i = 0; i < fluidInputLists.size(); i++) {
int maxNumber = fluidInputLists.get(i).size() - 1;
if (maxNumber < 0) {
continue recipeLoop;
}
maxNumbers[i + itemInputLists.size()] = maxNumber;
}
List<Pair<List<ItemStack>, List<FluidStack>>> materialListTuples = new ArrayList<>(); List<Pair<List<ItemStack>, List<FluidStack>>> materialListTuples = new ArrayList<>();
RecipeTupleGenerator.INSTANCE.generateMaterialListTuples(materialListTuples, maxNumbers, inputNumbers, itemInputLists, fluidInputLists); if (!prepareMaterialListTuples(recipe, materialListTuples)) {
continue;
}
for (Pair<List<ItemStack>, List<FluidStack>> materials : materialListTuples) { for (Pair<List<ItemStack>, List<FluidStack>> materials : materialListTuples) {
for (List<ItemStack> items : PermutationHelper.permutations(materials.getLeft())) { for (List<ItemStack> items : PermutationHelper.permutations(materials.getLeft())) {
for (List<FluidStack> fluids : PermutationHelper.permutations(materials.getRight())) { for (List<FluidStack> fluids : PermutationHelper.permutations(materials.getRight())) {
long hash = RecipeHelper.hashMaterials(items, fluids, cacheSalt); long hash = RecipeHelper.hashMaterials(items, fluids);
if (recipeCache.containsKey(hash)) { if (recipeCache.containsKey(hash)) {
cacheSalt++; recipeCache.get(hash).add(recipe);
NCUtil.getLogger().info(getRecipeName() + " encountered a hash clash [" + RecipeHelper.getRecipeString(recipe) + " == " + RecipeHelper.getRecipeString(recipeCache.get(hash)) + "]! Incrementing salt to " + cacheSalt + " and restarting caching...");
return false;
} }
else { else {
recipeCache.put(RecipeHelper.hashMaterials(items, fluids, cacheSalt), recipe); ObjectSet<RECIPE> set = new ObjectOpenHashSet<>();
set.add(recipe);
recipeCache.put(hash, set);
} }
} }
} }
} }
} }
}
protected boolean prepareMaterialListTuples(RECIPE recipe, List<Pair<List<ItemStack>, List<FluidStack>>> materialListTuples) {
List<List<ItemStack>> itemInputLists = new ArrayList<>();
List<List<FluidStack>> fluidInputLists = new ArrayList<>();
for (IItemIngredient item : recipe.getItemIngredients()) {
itemInputLists.add(item.getInputStackHashingList());
}
for (IFluidIngredient fluid : recipe.getFluidIngredients()) {
fluidInputLists.add(fluid.getInputStackHashingList());
}
int arrSize = recipe.getItemIngredients().size() + recipe.getFluidIngredients().size();
int[] inputNumbers = new int[arrSize];
Arrays.fill(inputNumbers, 0);
int[] maxNumbers = new int[arrSize];
for (int i = 0; i < itemInputLists.size(); i++) {
int maxNumber = itemInputLists.get(i).size() - 1;
if (maxNumber < 0) {
return false;
}
maxNumbers[i] = maxNumber;
}
for (int i = 0; i < fluidInputLists.size(); i++) {
int maxNumber = fluidInputLists.get(i).size() - 1;
if (maxNumber < 0) {
return false;
}
maxNumbers[i + itemInputLists.size()] = maxNumber;
}
RecipeTupleGenerator.INSTANCE.generateMaterialListTuples(materialListTuples, maxNumbers, inputNumbers, itemInputLists, fluidInputLists);
return true; return true;
} }

View File

@ -509,32 +509,32 @@ public class RecipeHelper {
return new OreIngredient(oreName, stackSize); return new OreIngredient(oreName, stackSize);
} }
public static long hashMaterialsRaw(List<ItemStack> items, List<Tank> fluids, long salt) { public static long hashMaterialsRaw(List<ItemStack> items, List<Tank> fluids) {
long hash = 1L; long hash = 1L;
Iterator<ItemStack> itemIter = items.iterator(); Iterator<ItemStack> itemIter = items.iterator();
while (itemIter.hasNext()) { while (itemIter.hasNext()) {
ItemStack stack = itemIter.next(); ItemStack stack = itemIter.next();
hash = 31L * hash + (stack == null || stack.isEmpty() ? 0L : RecipeItemHelper.pack(stack)) + salt; hash = 31L * hash + (stack == null || stack.isEmpty() ? 0L : RecipeItemHelper.pack(stack));
} }
Iterator<Tank> fluidIter = fluids.iterator(); Iterator<Tank> fluidIter = fluids.iterator();
while (fluidIter.hasNext()) { while (fluidIter.hasNext()) {
Tank tank = fluidIter.next(); Tank tank = fluidIter.next();
hash = 31L * hash + (tank == null || tank.getFluid() == null ? 0L : tank.getFluid().getFluid().getName().hashCode()) + salt; hash = 31L * hash + (tank == null || tank.getFluid() == null ? 0L : tank.getFluid().getFluid().getName().hashCode());
} }
return hash; return hash;
} }
public static long hashMaterials(List<ItemStack> items, List<FluidStack> fluids, long salt) { public static long hashMaterials(List<ItemStack> items, List<FluidStack> fluids) {
long hash = 1L; long hash = 1L;
Iterator<ItemStack> itemIter = items.iterator(); Iterator<ItemStack> itemIter = items.iterator();
while (itemIter.hasNext()) { while (itemIter.hasNext()) {
ItemStack stack = itemIter.next(); ItemStack stack = itemIter.next();
hash = 31L * hash + (stack == null || stack.isEmpty() ? 0L : RecipeItemHelper.pack(stack)) + salt; hash = 31L * hash + (stack == null || stack.isEmpty() ? 0L : RecipeItemHelper.pack(stack));
} }
Iterator<FluidStack> fluidIter = fluids.iterator(); Iterator<FluidStack> fluidIter = fluids.iterator();
while (fluidIter.hasNext()) { while (fluidIter.hasNext()) {
FluidStack stack = fluidIter.next(); FluidStack stack = fluidIter.next();
hash = 31L * hash + (stack == null ? 0L : stack.getFluid().getName().hashCode()) + salt; hash = 31L * hash + (stack == null ? 0L : stack.getFluid().getName().hashCode());
} }
return hash; return hash;
} }

View File

@ -98,7 +98,7 @@ public class ManufactoryRecipes extends ProcessorRecipeHandler {
addLogRecipes(); addLogRecipes();
} }
private static final Set<String> BLACKLIST = Sets.newHashSet("silicon"); private static final Set<String> BLACKLIST = Sets.newHashSet("Silicon");
public void addMetalProcessingRecipes() { public void addMetalProcessingRecipes() {
for (String ingot : OreDictionary.getOreNames()) { for (String ingot : OreDictionary.getOreNames()) {

View File

@ -1,6 +1,3 @@
Add salt-based fix for recipe hash caching (start from zero, if there's a clash, start again after incrementing)
Test with iron + coal alloy furnace recipe in modified Enigmatica 2 instance
Suggestions from: This probably wont happen but in stead of multiblock reactors Suggestions from: This probably wont happen but in stead of multiblock reactors
Adjacent MSR vessels with same filter form bundle to share flux, shared criticality factor is sum of single vessel criticality factors Adjacent MSR vessels with same filter form bundle to share flux, shared criticality factor is sum of single vessel criticality factors