Compare commits

..

No commits in common. "a49c75bce0b017d594d6ed1a9ac3bc88c67669f7" and "a1c74b64c0584dae538e483dacfeadce60460bb9" have entirely different histories.

5 changed files with 65 additions and 66 deletions

View File

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

View File

@ -11,7 +11,6 @@ import com.google.common.collect.Lists;
import crafttweaker.annotations.ZenRegister;
import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.longs.*;
import it.unimi.dsi.fastutil.objects.*;
import nc.recipe.ingredient.*;
import nc.tile.internal.fluid.Tank;
import nc.util.*;
@ -26,7 +25,8 @@ public abstract class AbstractRecipeHandler<RECIPE extends IRecipe> {
protected @Nonnull List<RECIPE> recipeList = new ArrayList<>();
protected @Nonnull Long2ObjectMap<ObjectSet<RECIPE>> recipeCache = new Long2ObjectOpenHashMap<>();
protected @Nonnull Long2ObjectMap<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<?>> validFluidInputs = Lists.newArrayList(IFluidIngredient.class, ArrayList.class, String.class, Fluid.class, FluidStack.class, FluidStack[].class);
@ -46,19 +46,18 @@ public abstract class AbstractRecipeHandler<RECIPE extends IRecipe> {
@ZenMethod
public abstract List<RECIPE> getRecipeList();
public Long2ObjectMap<RECIPE> getRecipeCache() {
return recipeCache;
}
public abstract void addRecipe(Object... objects);
public @Nullable RecipeInfo<RECIPE> getRecipeInfoFromInputs(List<ItemStack> itemInputs, List<Tank> fluidInputs) {
long hash = RecipeHelper.hashMaterialsRaw(itemInputs, fluidInputs);
if (recipeCache.containsKey(hash)) {
ObjectSet<RECIPE> set = recipeCache.get(hash);
for (RECIPE recipe : set) {
if (recipe != null) {
RecipeMatchResult matchResult = recipe.matchInputs(itemInputs, fluidInputs);
if (matchResult.matches()) {
return new RecipeInfo(recipe, matchResult);
}
}
RECIPE recipe = recipeCache.get(RecipeHelper.hashMaterialsRaw(itemInputs, fluidInputs, cacheSalt));
if (recipe != null) {
RecipeMatchResult matchResult = recipe.matchInputs(itemInputs, fluidInputs);
if (matchResult.matches()) {
return new RecipeInfo(recipe, matchResult);
}
}
return null;
@ -96,68 +95,65 @@ public abstract class AbstractRecipeHandler<RECIPE extends IRecipe> {
}
public void refreshCache() {
recipeCache.clear();
fillHashCache();
cacheSalt = 0L;
do {
recipeCache.clear();
}
while (!fillHashCache());
}
protected void fillHashCache() {
for (RECIPE recipe : recipeList) {
public boolean fillHashCache() {
recipeLoop: 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<>();
if (!prepareMaterialListTuples(recipe, materialListTuples)) {
continue;
}
RecipeTupleGenerator.INSTANCE.generateMaterialListTuples(materialListTuples, maxNumbers, inputNumbers, itemInputLists, fluidInputLists);
for (Pair<List<ItemStack>, List<FluidStack>> materials : materialListTuples) {
for (List<ItemStack> items : PermutationHelper.permutations(materials.getLeft())) {
for (List<FluidStack> fluids : PermutationHelper.permutations(materials.getRight())) {
long hash = RecipeHelper.hashMaterials(items, fluids);
long hash = RecipeHelper.hashMaterials(items, fluids, cacheSalt);
if (recipeCache.containsKey(hash)) {
recipeCache.get(hash).add(recipe);
cacheSalt++;
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 {
ObjectSet<RECIPE> set = new ObjectOpenHashSet<>();
set.add(recipe);
recipeCache.put(hash, set);
recipeCache.put(RecipeHelper.hashMaterials(items, fluids, cacheSalt), recipe);
}
}
}
}
}
}
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;
}

View File

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

View File

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

View File

@ -1,3 +1,6 @@
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
Adjacent MSR vessels with same filter form bundle to share flux, shared criticality factor is sum of single vessel criticality factors