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
* 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

View File

@ -11,6 +11,7 @@ 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.*;
@ -25,8 +26,7 @@ public abstract class AbstractRecipeHandler<RECIPE extends IRecipe> {
protected @Nonnull List<RECIPE> recipeList = new ArrayList<>();
protected @Nonnull Long2ObjectMap<RECIPE> recipeCache = new Long2ObjectOpenHashMap<>();
protected long cacheSalt = 0L;
protected @Nonnull Long2ObjectMap<ObjectSet<RECIPE>> recipeCache = new Long2ObjectOpenHashMap<>();
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,20 +46,21 @@ 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) {
RECIPE recipe = recipeCache.get(RecipeHelper.hashMaterialsRaw(itemInputs, fluidInputs, cacheSalt));
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);
}
}
}
}
return null;
}
@ -95,15 +96,37 @@ public abstract class AbstractRecipeHandler<RECIPE extends IRecipe> {
}
public void refreshCache() {
cacheSalt = 0L;
do {
recipeCache.clear();
}
while (!fillHashCache());
fillHashCache();
}
public boolean fillHashCache() {
recipeLoop: for (RECIPE recipe : recipeList) {
protected void fillHashCache() {
for (RECIPE recipe : recipeList) {
List<Pair<List<ItemStack>, List<FluidStack>>> materialListTuples = new ArrayList<>();
if (!prepareMaterialListTuples(recipe, materialListTuples)) {
continue;
}
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);
if (recipeCache.containsKey(hash)) {
recipeCache.get(hash).add(recipe);
}
else {
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<>();
@ -122,39 +145,20 @@ public abstract class AbstractRecipeHandler<RECIPE extends IRecipe> {
for (int i = 0; i < itemInputLists.size(); i++) {
int maxNumber = itemInputLists.get(i).size() - 1;
if (maxNumber < 0) {
continue recipeLoop;
return false;
}
maxNumbers[i] = maxNumber;
}
for (int i = 0; i < fluidInputLists.size(); i++) {
int maxNumber = fluidInputLists.get(i).size() - 1;
if (maxNumber < 0) {
continue recipeLoop;
return false;
}
maxNumbers[i + itemInputLists.size()] = maxNumber;
}
List<Pair<List<ItemStack>, List<FluidStack>>> materialListTuples = new ArrayList<>();
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, cacheSalt);
if (recipeCache.containsKey(hash)) {
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 {
recipeCache.put(RecipeHelper.hashMaterials(items, fluids, cacheSalt), recipe);
}
}
}
}
}
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, long salt) {
public static long hashMaterialsRaw(List<ItemStack> items, List<Tank> fluids) {
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)) + salt;
hash = 31L * hash + (stack == null || stack.isEmpty() ? 0L : RecipeItemHelper.pack(stack));
}
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()) + salt;
hash = 31L * hash + (tank == null || tank.getFluid() == null ? 0L : tank.getFluid().getFluid().getName().hashCode());
}
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;
Iterator<ItemStack> itemIter = items.iterator();
while (itemIter.hasNext()) {
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();
while (fluidIter.hasNext()) {
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;
}

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