aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/util/GT_AssemblyLineUtils.java
diff options
context:
space:
mode:
authorRaven Szewczyk <git@eigenraven.me>2022-08-27 10:19:57 +0100
committerGitHub <noreply@github.com>2022-08-27 11:19:57 +0200
commit6f31720697bcc351421a4d86ba3bf749375dd12c (patch)
tree3adf8f318f22c892d74cd7c9d30b6dd3f11f11bd /src/main/java/gregtech/api/util/GT_AssemblyLineUtils.java
parentc3eac50decd33ee2be8703dfb2ecf9cdc31c2b67 (diff)
downloadGT5-Unofficial-6f31720697bcc351421a4d86ba3bf749375dd12c.tar.gz
GT5-Unofficial-6f31720697bcc351421a4d86ba3bf749375dd12c.tar.bz2
GT5-Unofficial-6f31720697bcc351421a4d86ba3bf749375dd12c.zip
Update buildscript & apply spotless (#1306)
* Update dependencies * Update buildscript, apply spotless
Diffstat (limited to 'src/main/java/gregtech/api/util/GT_AssemblyLineUtils.java')
-rw-r--r--src/main/java/gregtech/api/util/GT_AssemblyLineUtils.java1023
1 files changed, 514 insertions, 509 deletions
diff --git a/src/main/java/gregtech/api/util/GT_AssemblyLineUtils.java b/src/main/java/gregtech/api/util/GT_AssemblyLineUtils.java
index 4f9036f2c7..f20a78d494 100644
--- a/src/main/java/gregtech/api/util/GT_AssemblyLineUtils.java
+++ b/src/main/java/gregtech/api/util/GT_AssemblyLineUtils.java
@@ -2,17 +2,17 @@ package gregtech.api.util;
import static gregtech.GT_Mod.GT_FML_LOGGER;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Objects;
-
import cpw.mods.fml.common.FMLCommonHandler;
import gregtech.api.enums.GT_Values;
import gregtech.api.enums.ItemList;
import gregtech.api.objects.GT_ItemStack;
import gregtech.api.util.GT_Recipe.GT_Recipe_AssemblyLine;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+import javax.annotation.Nonnull;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
@@ -20,509 +20,514 @@ import net.minecraft.nbt.NBTTagString;
import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.fluids.FluidStack;
-import javax.annotation.Nonnull;
-
public class GT_AssemblyLineUtils {
- /**
- * A cache of Recipes using the Output as Key.
- */
- private static HashMap<GT_ItemStack, GT_Recipe_AssemblyLine> sRecipeCacheByOutput = new HashMap<GT_ItemStack, GT_Recipe_AssemblyLine>();
- /**
- * A cache of Recipes using the Recipe Hash String as Key.
- */
- private static HashMap<String, GT_Recipe_AssemblyLine> sRecipeCacheByRecipeHash = new HashMap<String, GT_Recipe_AssemblyLine>();
-
-
-
- /**
- * Checks the DataStick for deprecated/invalid recipes, updating them as required.
- * @param aDataStick - The DataStick to process
- * @return Is this DataStick now valid with a current recipe?
- */
- public static GT_Recipe_AssemblyLine processDataStick(ItemStack aDataStick) {
- if (!isItemDataStick(aDataStick)) {
- return null;
- }
- if (doesDataStickNeedUpdate(aDataStick)) {
- ItemStack aStickOutput = getDataStickOutput(aDataStick);
- if (aStickOutput != null) {
- GT_Recipe_AssemblyLine aIntendedRecipe = findAssemblyLineRecipeByOutput(aStickOutput);
- if (aIntendedRecipe != null && setAssemblyLineRecipeOnDataStick(aDataStick, aIntendedRecipe))
- return aIntendedRecipe;
- }
- }
- return null;
- }
-
-
- /**
- * Finds an Assembly Line recipe from a DataStick.
- * @param aDataStick - The DataStick to check.
- * @return The GT_Recipe_AssemblyLine recipe contained on the DataStick, if any.
- */
- public static GT_Recipe_AssemblyLine findAssemblyLineRecipeFromDataStick(ItemStack aDataStick) {
- return findAssemblyLineRecipeFromDataStick(aDataStick, false).getRecipe();
- }
-
- /**
- * Finds an Assembly Line recipe from a DataStick.
- * @param aDataStick - The DataStick to check.
- * @param aReturnBuiltRecipe - Do we return a GT_Recipe_AssemblyLine built from the data on the Data Stick instead of searching the Recipe Map?
- * @return The GT_Recipe_AssemblyLine recipe contained on the DataStick, if any.
- */
- @Nonnull
- public static LookupResult findAssemblyLineRecipeFromDataStick(ItemStack aDataStick, boolean aReturnBuiltRecipe) {
- if (!isItemDataStick(aDataStick) || !doesDataStickHaveOutput(aDataStick)) {
- return LookupResultType.INVALID_STICK.getResult();
- }
- List<ItemStack> aInputs = new ArrayList<>(15);
- ItemStack aOutput = getDataStickOutput(aDataStick);
- List<List<ItemStack>> mOreDictAlt = new ArrayList<>(15);
- List<FluidStack> aFluidInputs = new ArrayList<>(4);
-
- NBTTagCompound aTag = aDataStick.getTagCompound();
- if (aTag == null) {
- return LookupResultType.INVALID_STICK.getResult();
- }
-
- //Get From Cache
- if (doesDataStickHaveRecipeHash(aDataStick)) {
- GT_Recipe_AssemblyLine aRecipeFromCache = sRecipeCacheByRecipeHash.get(getHashFromDataStack(aDataStick));
- if (aRecipeFromCache != null && GT_Utility.areStacksEqual(aOutput, aRecipeFromCache.mOutput)) {
- return LookupResultType.VALID_STACK_AND_VALID_HASH.getResult(aRecipeFromCache);
- } // else: no cache, or the old recipe run into a hash collision with a different new recipe
- }
-
- for (int i = 0; i < 15; i++) {
- int count = aTag.getInteger("a" + i);
- if (!aTag.hasKey("" + i) && count <= 0) {
- continue;
- }
-
- List<ItemStack> tAltCurrent = new ArrayList<>();
- for (int j = 0; j < count; j++) {
- ItemStack tLoaded = GT_Utility.loadItem(aTag, "a" + i + ":" + j);
- if (tLoaded == null) {
- continue;
- }
- tAltCurrent.add(tLoaded);
- if (GT_Values.D1) {
- GT_FML_LOGGER.info("Item Alt " + i + " : " + tLoaded.getUnlocalizedName());
- }
- }
- mOreDictAlt.add(tAltCurrent);
- ItemStack tLoaded = GT_Utility.loadItem(aTag, "" + i);
- if (tLoaded == null) {
- continue;
- }
- aInputs.add(tLoaded);
- if (GT_Values.D1) {
- GT_FML_LOGGER.info("Item " + i + " : " + tLoaded.getUnlocalizedName());
- }
- }
-
- if (GT_Values.D1) {
- GT_FML_LOGGER.info("All Items done, start fluid check");
- }
- for (int i = 0; i < 4; i++) {
- if (!aTag.hasKey("f" + i)) continue;
- FluidStack tLoaded = GT_Utility.loadFluid(aTag, "f" + i);
- if (tLoaded == null) continue;
- aFluidInputs.add(tLoaded);
- if (GT_Values.D1) {
- GT_FML_LOGGER.info("Fluid " + i + " " + tLoaded.getUnlocalizedName());
- }
- }
- if (!aTag.hasKey("output") || !aTag.hasKey("time") || aTag.getInteger("time") <= 0 || !aTag.hasKey("eu") || !GT_Utility.isStackValid(aOutput)) {
- return LookupResultType.INVALID_STICK.getResult();
- }
- if (GT_Values.D1) {
- GT_FML_LOGGER.info("Found Data Stick recipe");
- }
-
- int aTime = aTag.getInteger("time");
- int aEU = aTag.getInteger("eu");
-
- // Try build a recipe instance
- if (aReturnBuiltRecipe) {
- return LookupResultType.VALID_STACK_AND_VALID_HASH.getResult(new GT_Recipe_AssemblyLine(null, 0, aInputs.toArray(new ItemStack[0]), aFluidInputs.toArray(new FluidStack[0]), aOutput, aTime, aEU));
- }
-
-
- for (GT_Recipe_AssemblyLine aRecipe : GT_Recipe.GT_Recipe_AssemblyLine.sAssemblylineRecipes) {
- if (aRecipe.mEUt != aEU || aRecipe.mDuration != aTime) continue;
- if (!GT_Utility.areStacksEqual(aOutput, aRecipe.mOutput, true)) continue;
- if (!GT_Utility.areStackListsEqual(Arrays.asList(aRecipe.mInputs), aInputs, false, true)) continue;
- if (!Objects.equals(Arrays.asList(aRecipe.mFluidInputs), aFluidInputs)) continue;
- if (!areStacksEqual(aRecipe.mOreDictAlt, mOreDictAlt)) continue;
-
- // Cache it
- String aRecipeHash = generateRecipeHash(aRecipe);
- sRecipeCacheByRecipeHash.put(aRecipeHash, aRecipe);
- sRecipeCacheByOutput.put(new GT_ItemStack(aRecipe.mOutput), aRecipe);
- if (doesDataStickHaveRecipeHash(aDataStick)) {
- String aStickHash = getHashFromDataStack(aDataStick);
- if (aRecipeHash.equals(aStickHash))
- return LookupResultType.VALID_STACK_AND_VALID_HASH.getResult(aRecipe);
- }
- return LookupResultType.VALID_STACK_AND_VALID_RECIPE.getResult(aRecipe);
- }
- return LookupResultType.VALID_STACK_BUT_INVALID_RECIPE.getResult();
- }
-
- private static boolean areStacksEqual(ItemStack[][] lhs, List<List<ItemStack>> rhs) {
- for (int i = 0; i < lhs.length; i++) {
- if (!areStacksEqual(lhs[i], rhs.get(i)))
- return false;
- }
- return true;
- }
-
- private static boolean areStacksEqual(ItemStack[] lhs, List<ItemStack> rhs) {
- return lhs == null ? rhs.isEmpty() : !rhs.isEmpty() && GT_Utility.areStackListsEqual(Arrays.asList(lhs), rhs, false, true);
- }
-
-
- /**
- * Finds a GT_Recipe_AssemblyLine based on the expected output ItemStack.
- * @param aOutput - The Output of a GT_Recipe_AssemblyLine.
- * @return First found GT_Recipe_AssemblyLine with matching output.
- */
- public static GT_Recipe_AssemblyLine findAssemblyLineRecipeByOutput(ItemStack aOutput) {
- if (aOutput == null) {
- return null;
- }
-
- // Check the cache
- GT_ItemStack aCacheStack = new GT_ItemStack(aOutput);
- GT_Recipe_AssemblyLine aRecipeFromCache = sRecipeCacheByOutput.get(aCacheStack);
- if (aRecipeFromCache != null) {
- return aRecipeFromCache;
- }
-
- // Iterate all recipes and return the first matching based on Output.
- for (GT_Recipe_AssemblyLine aRecipe : GT_Recipe.GT_Recipe_AssemblyLine.sAssemblylineRecipes) {
- ItemStack aRecipeOutput = aRecipe.mOutput;
- if (GT_Utility.areStacksEqual(aRecipeOutput, aOutput)) {
- // Cache it to prevent future iterations of all recipes
- sRecipeCacheByOutput.put(aCacheStack, aRecipe);
- sRecipeCacheByRecipeHash.put(generateRecipeHash(aRecipe), aRecipe);
- return aRecipe;
- }
- }
- return null;
- }
-
- /**
- * @param aRecipe - The recipe to generate a Recipe Hash String from.
- * @return The Recipe Hash String.
- */
- public static String generateRecipeHash(GT_Recipe_AssemblyLine aRecipe) {
- String aHash = "Invalid.Recipe.Hash";
- if (aRecipe != null) {
- aHash = "Hash."+aRecipe.getPersistentHash();
- }
- return aHash;
- }
-
- /**
- * @param aRecipe - The recipe to add to internal caches
- * @throws IllegalArgumentException if given recipe collide with any existing recipe in the cache
- */
- public static void addRecipeToCache(GT_Recipe_AssemblyLine aRecipe) {
- if (aRecipe != null) {
- String aHash = "Hash." + aRecipe.getPersistentHash();
- GT_Recipe_AssemblyLine existing = sRecipeCacheByOutput.put(new GT_ItemStack(aRecipe.mOutput), aRecipe);
- if (existing != null)
- throw new IllegalArgumentException("Duplicate assline recipe for " + aRecipe.mOutput);
- existing = sRecipeCacheByRecipeHash.put(aHash, aRecipe);
- if (existing != null && !existing.equals(aRecipe))
- throw new IllegalArgumentException("Recipe hash collision for " + aRecipe + " and " + existing);
- }
- }
-
- /**
- * @param aHash - Recipe hash String, may be null but will just be treated as invalid.
- * @return Is this Recipe Hash String valid?
- */
- public static boolean isValidHash(String aHash) {
- if (aHash != null && aHash.length() > 0) {
- // persistent hash can never be 0
- return !aHash.equals("Invalid.Recipe.Hash") && !aHash.equals("Hash.0");
- }
- return false;
- }
-
- /**
- * @param aStack - The ItemStack to check.
- * @return Is this ItemStack a Data Stick?
- */
- public static boolean isItemDataStick(ItemStack aStack) {
- return GT_Utility.isStackValid(aStack) && ItemList.Tool_DataStick.isStackEqual(aStack, false, true);
- }
-
- /**
- * @param aDataStick - The Data Stick to check.
- * @return Does this Data Stick have a valid output ItemStack?
- */
- public static boolean doesDataStickHaveOutput(ItemStack aDataStick) {
- if (isItemDataStick(aDataStick) && aDataStick.hasTagCompound() && aDataStick.getTagCompound().hasKey("output")) {
- return true;
- }
- return false;
- }
-
- /**
- * @param aDataStick - The Data Stick to check.
- * @return Does this Data Stick need recipe data updated.
- */
- public static boolean doesDataStickNeedUpdate(ItemStack aDataStick) {
- if (isItemDataStick(aDataStick) && doesDataStickHaveRecipeHash(aDataStick)) {
- String aStickHash = getHashFromDataStack(aDataStick);
- if (isValidHash(aStickHash) && doesDataStickHaveOutput(aDataStick)) {
- ItemStack aStickOutput = getDataStickOutput(aDataStick);
- GT_Recipe_AssemblyLine aIntendedRecipe = findAssemblyLineRecipeByOutput(aStickOutput);
- if (aStickHash.equals(generateRecipeHash(aIntendedRecipe))) {
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * @param aDataStick - The Data Stick to check.
- * @return Does this have a Recipe Hash String at all?
- */
- public static boolean doesDataStickHaveRecipeHash(ItemStack aDataStick) {
- if (isItemDataStick(aDataStick) && aDataStick.hasTagCompound()) {
- NBTTagCompound aNBT = aDataStick.getTagCompound();
- if (aNBT.hasKey("Data.Recipe.Hash") && !aNBT.getString("Data.Recipe.Hash").equals("Hash.0")) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Get the Output ItemStack from a Data Stick.
- * @param aDataStick - The Data Stick to check.
- * @return Output ItemStack contained on the Data Stick.
- */
- public static ItemStack getDataStickOutput(ItemStack aDataStick) {
- if (doesDataStickHaveOutput(aDataStick)) {
- ItemStack aOutput = GT_Utility.loadItem(aDataStick.getTagCompound(), "output");
- return aOutput;
- }
- return null;
- }
-
- /**
- * @param aDataStick - The Data Stick to process.
- * @return The stored Recipe Hash String on the Data Stick, will return an invalid Hash if one is not found. <p>
- * The hash will be guaranteed to pass isValidHash(). <p>
- * Will not return Null.
- */
- public static String getHashFromDataStack(ItemStack aDataStick) {
- if (isItemDataStick(aDataStick) && aDataStick.hasTagCompound()) {
- NBTTagCompound aNBT = aDataStick.getTagCompound();
- if (aNBT.hasKey("Data.Recipe.Hash", NBT.TAG_STRING)) {
- String hash = aNBT.getString("Data.Recipe.Hash");
- if (isValidHash(hash))
- return hash;
- }
- }
- return "Invalid.Recipe.Hash";
- }
-
- /**
- *
- * @param aDataStick - The Data Stick to update.
- * @param aRecipeHash - The Recipe Hash String to update with.
- * @return Did we update the Recipe Hash String on the Data Stick?
- */
- public static boolean setRecipeHashOnDataStick(ItemStack aDataStick, String aRecipeHash) {
- if (isItemDataStick(aDataStick) && aDataStick.hasTagCompound()) {
- NBTTagCompound aNBT = aDataStick.getTagCompound();
- aNBT.setString("Data.Recipe.Hash", aRecipeHash);
- aDataStick.setTagCompound(aNBT);
- return true;
- }
- return false;
- }
-
- /**
- *
- * @param aDataStick - The Data Stick to update.
- * @param aNewRecipe - The New GT_Recipe_AssemblyLine recipe to update it with.
- * @return Did we set the new recipe data & Recipe Hash String on the Data Stick?
- */
- public static boolean setAssemblyLineRecipeOnDataStick(ItemStack aDataStick, GT_Recipe_AssemblyLine aNewRecipe) {
- if (isItemDataStick(aDataStick)) {
- String s = aNewRecipe.mOutput.getDisplayName();
- if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
- s = GT_Assemblyline_Server.lServerNames.get(aNewRecipe.mOutput.getDisplayName());
- if (s == null) {
- s = aNewRecipe.mOutput.getDisplayName();
- }
- }
-
- String aHash = generateRecipeHash(aNewRecipe);
- if (GT_Values.D1) {
- GT_Recipe_AssemblyLine aOldRecipe = findAssemblyLineRecipeFromDataStick(aDataStick, true).recipe;
- GT_FML_LOGGER.info("Updating data stick: "+aDataStick.getDisplayName()+" | Old Recipe Hash: "+generateRecipeHash(aOldRecipe)+", New Recipe Hash: "+aHash);
- }
-
- String author = "Assembling Line Recipe Generator";
- String displayName = null;
- if (aDataStick.hasTagCompound()) {
- NBTTagCompound tag = aDataStick.getTagCompound();
- if (tag.hasKey("author", NBT.TAG_STRING)) {
- author = tag.getString("author");
- }
- if (tag.hasKey("display", NBT.TAG_COMPOUND)) {
- NBTTagCompound displayTag = tag.getCompoundTag("display");
- if (displayTag.hasKey("Name", NBT.TAG_STRING))
- displayName = displayTag.getString("Name");
- }
- }
-
- //remove possible old NBTTagCompound
- aDataStick.setTagCompound(new NBTTagCompound());
- if (displayName != null)
- aDataStick.setStackDisplayName(displayName);
- if (GT_Values.D1) {
- GT_Utility.ItemNBT.setBookTitle(aDataStick, s + " Construction Data ("+aHash+")");
- }
- else {
- GT_Utility.ItemNBT.setBookTitle(aDataStick, s + " Construction Data");
- }
-
- NBTTagCompound tNBT = aDataStick.getTagCompound();
- if (tNBT == null) {
- tNBT = new NBTTagCompound();
- }
-
- tNBT.setTag("output", aNewRecipe.mOutput.writeToNBT(new NBTTagCompound()));
- tNBT.setInteger("time", aNewRecipe.mDuration);
- tNBT.setInteger("eu", aNewRecipe.mEUt);
- for (int i = 0; i < aNewRecipe.mInputs.length; i++) {
- tNBT.setTag("" + i, aNewRecipe.mInputs[i].writeToNBT(new NBTTagCompound()));
- }
- for (int i = 0; i < aNewRecipe.mOreDictAlt.length; i++) {
- if (aNewRecipe.mOreDictAlt[i] != null && aNewRecipe.mOreDictAlt[i].length > 0) {
- tNBT.setInteger("a" + i, aNewRecipe.mOreDictAlt[i].length);
- for (int j = 0; j < aNewRecipe.mOreDictAlt[i].length; j++) {
- tNBT.setTag("a" + i + ":" + j, aNewRecipe.mOreDictAlt[i][j].writeToNBT(new NBTTagCompound()));
- }
- }
- }
- for (int i = 0; i < aNewRecipe.mFluidInputs.length; i++) {
- tNBT.setTag("f" + i, aNewRecipe.mFluidInputs[i].writeToNBT(new NBTTagCompound()));
- }
- tNBT.setString("author", author);
- NBTTagList tNBTList = new NBTTagList();
- s = aNewRecipe.mOutput.getDisplayName();
- if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
- s = GT_Assemblyline_Server.lServerNames.get(aNewRecipe.mOutput.getDisplayName());
- if (s == null)
- s = aNewRecipe.mOutput.getDisplayName();
- }
- tNBTList.appendTag(new NBTTagString("Construction plan for " + aNewRecipe.mOutput.stackSize + " " + s + ". Needed EU/t: " + aNewRecipe.mEUt + " Production time: " + (aNewRecipe.mDuration / 20)));
- for (int i = 0; i < aNewRecipe.mInputs.length; i++) {
- if (aNewRecipe.mOreDictAlt[i] != null) {
- int count = 0;
- StringBuilder tBuilder = new StringBuilder("Input Bus " + (i + 1) + ": ");
- for (ItemStack tStack : aNewRecipe.mOreDictAlt[i]) {
- if (tStack != null) {
- s = tStack.getDisplayName();
- if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
- s = GT_Assemblyline_Server.lServerNames.get(tStack.getDisplayName());
- if (s == null)
- s = tStack.getDisplayName();
- }
-
-
- tBuilder.append(count == 0 ? "" : "\nOr ").append(tStack.stackSize).append(" ").append(s);
- count++;
- }
- }
- if (count > 0) tNBTList.appendTag(new NBTTagString(tBuilder.toString()));
- } else if (aNewRecipe.mInputs[i] != null) {
- s = aNewRecipe.mInputs[i].getDisplayName();
- if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
- s = GT_Assemblyline_Server.lServerNames.get(aNewRecipe.mInputs[i].getDisplayName());
- if (s == null)
- s = aNewRecipe.mInputs[i].getDisplayName();
- }
- tNBTList.appendTag(new NBTTagString("Input Bus " + (i + 1) + ": " + aNewRecipe.mInputs[i].stackSize + " " + s));
- }
- }
- for (int i = 0; i < aNewRecipe.mFluidInputs.length; i++) {
- if (aNewRecipe.mFluidInputs[i] != null) {
- s = aNewRecipe.mFluidInputs[i].getLocalizedName();
- if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
- s = GT_Assemblyline_Server.lServerNames.get(aNewRecipe.mFluidInputs[i].getLocalizedName());
- if (s == null)
- s = aNewRecipe.mFluidInputs[i].getLocalizedName();
- }
- tNBTList.appendTag(new NBTTagString("Input Hatch " + (i + 1) + ": " + aNewRecipe.mFluidInputs[i].amount + "L " + s));
- }
- }
- tNBT.setTag("pages", tNBTList);
- tNBT.setLong("lastUpdate", System.currentTimeMillis());
- aDataStick.setTagCompound(tNBT);
- // Set recipe hash
- setRecipeHashOnDataStick(aDataStick, aHash);
- return true;
- }
- return false;
- }
-
- public enum LookupResultType {
- INVALID_STICK(true),
- VALID_STACK_BUT_INVALID_RECIPE(true),
- VALID_STACK_AND_VALID_RECIPE(false),
- VALID_STACK_AND_VALID_HASH(false);
-
- private final boolean recipeNull;
- private LookupResult singletonResult;
-
- LookupResultType(boolean recipeNull) {
- this.recipeNull = recipeNull;
- }
-
- public LookupResult getResult() {
- if (!recipeNull)
- throw new IllegalArgumentException("This result type require a nonnull recipe");
- if (singletonResult == null)
- singletonResult = new LookupResult(null, this);
- return singletonResult;
- }
-
- public LookupResult getResult(GT_Recipe_AssemblyLine recipe) {
- if ((recipe == null) != recipeNull)
- throw new IllegalArgumentException("This result type does not allow given input");
- return new LookupResult(recipe, this);
- }
- }
-
- public static class LookupResult {
- private final GT_Recipe_AssemblyLine recipe;
- private final LookupResultType type;
-
- LookupResult(GT_Recipe_AssemblyLine recipe, LookupResultType type) {
- this.recipe = recipe;
- this.type = type;
- }
-
- public GT_Recipe_AssemblyLine getRecipe() {
- return recipe;
- }
-
- public LookupResultType getType() {
- return type;
- }
- }
+ /**
+ * A cache of Recipes using the Output as Key.
+ */
+ private static HashMap<GT_ItemStack, GT_Recipe_AssemblyLine> sRecipeCacheByOutput =
+ new HashMap<GT_ItemStack, GT_Recipe_AssemblyLine>();
+ /**
+ * A cache of Recipes using the Recipe Hash String as Key.
+ */
+ private static HashMap<String, GT_Recipe_AssemblyLine> sRecipeCacheByRecipeHash =
+ new HashMap<String, GT_Recipe_AssemblyLine>();
+
+ /**
+ * Checks the DataStick for deprecated/invalid recipes, updating them as required.
+ * @param aDataStick - The DataStick to process
+ * @return Is this DataStick now valid with a current recipe?
+ */
+ public static GT_Recipe_AssemblyLine processDataStick(ItemStack aDataStick) {
+ if (!isItemDataStick(aDataStick)) {
+ return null;
+ }
+ if (doesDataStickNeedUpdate(aDataStick)) {
+ ItemStack aStickOutput = getDataStickOutput(aDataStick);
+ if (aStickOutput != null) {
+ GT_Recipe_AssemblyLine aIntendedRecipe = findAssemblyLineRecipeByOutput(aStickOutput);
+ if (aIntendedRecipe != null && setAssemblyLineRecipeOnDataStick(aDataStick, aIntendedRecipe))
+ return aIntendedRecipe;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Finds an Assembly Line recipe from a DataStick.
+ * @param aDataStick - The DataStick to check.
+ * @return The GT_Recipe_AssemblyLine recipe contained on the DataStick, if any.
+ */
+ public static GT_Recipe_AssemblyLine findAssemblyLineRecipeFromDataStick(ItemStack aDataStick) {
+ return findAssemblyLineRecipeFromDataStick(aDataStick, false).getRecipe();
+ }
+
+ /**
+ * Finds an Assembly Line recipe from a DataStick.
+ * @param aDataStick - The DataStick to check.
+ * @param aReturnBuiltRecipe - Do we return a GT_Recipe_AssemblyLine built from the data on the Data Stick instead of searching the Recipe Map?
+ * @return The GT_Recipe_AssemblyLine recipe contained on the DataStick, if any.
+ */
+ @Nonnull
+ public static LookupResult findAssemblyLineRecipeFromDataStick(ItemStack aDataStick, boolean aReturnBuiltRecipe) {
+ if (!isItemDataStick(aDataStick) || !doesDataStickHaveOutput(aDataStick)) {
+ return LookupResultType.INVALID_STICK.getResult();
+ }
+ List<ItemStack> aInputs = new ArrayList<>(15);
+ ItemStack aOutput = getDataStickOutput(aDataStick);
+ List<List<ItemStack>> mOreDictAlt = new ArrayList<>(15);
+ List<FluidStack> aFluidInputs = new ArrayList<>(4);
+
+ NBTTagCompound aTag = aDataStick.getTagCompound();
+ if (aTag == null) {
+ return LookupResultType.INVALID_STICK.getResult();
+ }
+
+ // Get From Cache
+ if (doesDataStickHaveRecipeHash(aDataStick)) {
+ GT_Recipe_AssemblyLine aRecipeFromCache = sRecipeCacheByRecipeHash.get(getHashFromDataStack(aDataStick));
+ if (aRecipeFromCache != null && GT_Utility.areStacksEqual(aOutput, aRecipeFromCache.mOutput)) {
+ return LookupResultType.VALID_STACK_AND_VALID_HASH.getResult(aRecipeFromCache);
+ } // else: no cache, or the old recipe run into a hash collision with a different new recipe
+ }
+
+ for (int i = 0; i < 15; i++) {
+ int count = aTag.getInteger("a" + i);
+ if (!aTag.hasKey("" + i) && count <= 0) {
+ continue;
+ }
+
+ List<ItemStack> tAltCurrent = new ArrayList<>();
+ for (int j = 0; j < count; j++) {
+ ItemStack tLoaded = GT_Utility.loadItem(aTag, "a" + i + ":" + j);
+ if (tLoaded == null) {
+ continue;
+ }
+ tAltCurrent.add(tLoaded);
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Item Alt " + i + " : " + tLoaded.getUnlocalizedName());
+ }
+ }
+ mOreDictAlt.add(tAltCurrent);
+ ItemStack tLoaded = GT_Utility.loadItem(aTag, "" + i);
+ if (tLoaded == null) {
+ continue;
+ }
+ aInputs.add(tLoaded);
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Item " + i + " : " + tLoaded.getUnlocalizedName());
+ }
+ }
+
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("All Items done, start fluid check");
+ }
+ for (int i = 0; i < 4; i++) {
+ if (!aTag.hasKey("f" + i)) continue;
+ FluidStack tLoaded = GT_Utility.loadFluid(aTag, "f" + i);
+ if (tLoaded == null) continue;
+ aFluidInputs.add(tLoaded);
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Fluid " + i + " " + tLoaded.getUnlocalizedName());
+ }
+ }
+ if (!aTag.hasKey("output")
+ || !aTag.hasKey("time")
+ || aTag.getInteger("time") <= 0
+ || !aTag.hasKey("eu")
+ || !GT_Utility.isStackValid(aOutput)) {
+ return LookupResultType.INVALID_STICK.getResult();
+ }
+ if (GT_Values.D1) {
+ GT_FML_LOGGER.info("Found Data Stick recipe");
+ }
+
+ int aTime = aTag.getInteger("time");
+ int aEU = aTag.getInteger("eu");
+
+ // Try build a recipe instance
+ if (aReturnBuiltRecipe) {
+ return LookupResultType.VALID_STACK_AND_VALID_HASH.getResult(new GT_Recipe_AssemblyLine(
+ null,
+ 0,
+ aInputs.toArray(new ItemStack[0]),
+ aFluidInputs.toArray(new FluidStack[0]),
+ aOutput,
+ aTime,
+ aEU));
+ }
+
+ for (GT_Recipe_AssemblyLine aRecipe : GT_Recipe.GT_Recipe_AssemblyLine.sAssemblylineRecipes) {
+ if (aRecipe.mEUt != aEU || aRecipe.mDuration != aTime) continue;
+ if (!GT_Utility.areStacksEqual(aOutput, aRecipe.mOutput, true)) continue;
+ if (!GT_Utility.areStackListsEqual(Arrays.asList(aRecipe.mInputs), aInputs, false, true)) continue;
+ if (!Objects.equals(Arrays.asList(aRecipe.mFluidInputs), aFluidInputs)) continue;
+ if (!areStacksEqual(aRecipe.mOreDictAlt, mOreDictAlt)) continue;
+
+ // Cache it
+ String aRecipeHash = generateRecipeHash(aRecipe);
+ sRecipeCacheByRecipeHash.put(aRecipeHash, aRecipe);
+ sRecipeCacheByOutput.put(new GT_ItemStack(aRecipe.mOutput), aRecipe);
+ if (doesDataStickHaveRecipeHash(aDataStick)) {
+ String aStickHash = getHashFromDataStack(aDataStick);
+ if (aRecipeHash.equals(aStickHash))
+ return LookupResultType.VALID_STACK_AND_VALID_HASH.getResult(aRecipe);
+ }
+ return LookupResultType.VALID_STACK_AND_VALID_RECIPE.getResult(aRecipe);
+ }
+ return LookupResultType.VALID_STACK_BUT_INVALID_RECIPE.getResult();
+ }
+
+ private static boolean areStacksEqual(ItemStack[][] lhs, List<List<ItemStack>> rhs) {
+ for (int i = 0; i < lhs.length; i++) {
+ if (!areStacksEqual(lhs[i], rhs.get(i))) return false;
+ }
+ return true;
+ }
+
+ private static boolean areStacksEqual(ItemStack[] lhs, List<ItemStack> rhs) {
+ return lhs == null
+ ? rhs.isEmpty()
+ : !rhs.isEmpty() && GT_Utility.areStackListsEqual(Arrays.asList(lhs), rhs, false, true);
+ }
+
+ /**
+ * Finds a GT_Recipe_AssemblyLine based on the expected output ItemStack.
+ * @param aOutput - The Output of a GT_Recipe_AssemblyLine.
+ * @return First found GT_Recipe_AssemblyLine with matching output.
+ */
+ public static GT_Recipe_AssemblyLine findAssemblyLineRecipeByOutput(ItemStack aOutput) {
+ if (aOutput == null) {
+ return null;
+ }
+
+ // Check the cache
+ GT_ItemStack aCacheStack = new GT_ItemStack(aOutput);
+ GT_Recipe_AssemblyLine aRecipeFromCache = sRecipeCacheByOutput.get(aCacheStack);
+ if (aRecipeFromCache != null) {
+ return aRecipeFromCache;
+ }
+
+ // Iterate all recipes and return the first matching based on Output.
+ for (GT_Recipe_AssemblyLine aRecipe : GT_Recipe.GT_Recipe_AssemblyLine.sAssemblylineRecipes) {
+ ItemStack aRecipeOutput = aRecipe.mOutput;
+ if (GT_Utility.areStacksEqual(aRecipeOutput, aOutput)) {
+ // Cache it to prevent future iterations of all recipes
+ sRecipeCacheByOutput.put(aCacheStack, aRecipe);
+ sRecipeCacheByRecipeHash.put(generateRecipeHash(aRecipe), aRecipe);
+ return aRecipe;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @param aRecipe - The recipe to generate a Recipe Hash String from.
+ * @return The Recipe Hash String.
+ */
+ public static String generateRecipeHash(GT_Recipe_AssemblyLine aRecipe) {
+ String aHash = "Invalid.Recipe.Hash";
+ if (aRecipe != null) {
+ aHash = "Hash." + aRecipe.getPersistentHash();
+ }
+ return aHash;
+ }
+
+ /**
+ * @param aRecipe - The recipe to add to internal caches
+ * @throws IllegalArgumentException if given recipe collide with any existing recipe in the cache
+ */
+ public static void addRecipeToCache(GT_Recipe_AssemblyLine aRecipe) {
+ if (aRecipe != null) {
+ String aHash = "Hash." + aRecipe.getPersistentHash();
+ GT_Recipe_AssemblyLine existing = sRecipeCacheByOutput.put(new GT_ItemStack(aRecipe.mOutput), aRecipe);
+ if (existing != null) throw new IllegalArgumentException("Duplicate assline recipe for " + aRecipe.mOutput);
+ existing = sRecipeCacheByRecipeHash.put(aHash, aRecipe);
+ if (existing != null && !existing.equals(aRecipe))
+ throw new IllegalArgumentException("Recipe hash collision for " + aRecipe + " and " + existing);
+ }
+ }
+
+ /**
+ * @param aHash - Recipe hash String, may be null but will just be treated as invalid.
+ * @return Is this Recipe Hash String valid?
+ */
+ public static boolean isValidHash(String aHash) {
+ if (aHash != null && aHash.length() > 0) {
+ // persistent hash can never be 0
+ return !aHash.equals("Invalid.Recipe.Hash") && !aHash.equals("Hash.0");
+ }
+ return false;
+ }
+
+ /**
+ * @param aStack - The ItemStack to check.
+ * @return Is this ItemStack a Data Stick?
+ */
+ public static boolean isItemDataStick(ItemStack aStack) {
+ return GT_Utility.isStackValid(aStack) && ItemList.Tool_DataStick.isStackEqual(aStack, false, true);
+ }
+
+ /**
+ * @param aDataStick - The Data Stick to check.
+ * @return Does this Data Stick have a valid output ItemStack?
+ */
+ public static boolean doesDataStickHaveOutput(ItemStack aDataStick) {
+ if (isItemDataStick(aDataStick)
+ && aDataStick.hasTagCompound()
+ && aDataStick.getTagCompound().hasKey("output")) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param aDataStick - The Data Stick to check.
+ * @return Does this Data Stick need recipe data updated.
+ */
+ public static boolean doesDataStickNeedUpdate(ItemStack aDataStick) {
+ if (isItemDataStick(aDataStick) && doesDataStickHaveRecipeHash(aDataStick)) {
+ String aStickHash = getHashFromDataStack(aDataStick);
+ if (isValidHash(aStickHash) && doesDataStickHaveOutput(aDataStick)) {
+ ItemStack aStickOutput = getDataStickOutput(aDataStick);
+ GT_Recipe_AssemblyLine aIntendedRecipe = findAssemblyLineRecipeByOutput(aStickOutput);
+ if (aStickHash.equals(generateRecipeHash(aIntendedRecipe))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @param aDataStick - The Data Stick to check.
+ * @return Does this have a Recipe Hash String at all?
+ */
+ public static boolean doesDataStickHaveRecipeHash(ItemStack aDataStick) {
+ if (isItemDataStick(aDataStick) && aDataStick.hasTagCompound()) {
+ NBTTagCompound aNBT = aDataStick.getTagCompound();
+ if (aNBT.hasKey("Data.Recipe.Hash")
+ && !aNBT.getString("Data.Recipe.Hash").equals("Hash.0")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the Output ItemStack from a Data Stick.
+ * @param aDataStick - The Data Stick to check.
+ * @return Output ItemStack contained on the Data Stick.
+ */
+ public static ItemStack getDataStickOutput(ItemStack aDataStick) {
+ if (doesDataStickHaveOutput(aDataStick)) {
+ ItemStack aOutput = GT_Utility.loadItem(aDataStick.getTagCompound(), "output");
+ return aOutput;
+ }
+ return null;
+ }
+
+ /**
+ * @param aDataStick - The Data Stick to process.
+ * @return The stored Recipe Hash String on the Data Stick, will return an invalid Hash if one is not found. <p>
+ * The hash will be guaranteed to pass isValidHash(). <p>
+ * Will not return Null.
+ */
+ public static String getHashFromDataStack(ItemStack aDataStick) {
+ if (isItemDataStick(aDataStick) && aDataStick.hasTagCompound()) {
+ NBTTagCompound aNBT = aDataStick.getTagCompound();
+ if (aNBT.hasKey("Data.Recipe.Hash", NBT.TAG_STRING)) {
+ String hash = aNBT.getString("Data.Recipe.Hash");
+ if (isValidHash(hash)) return hash;
+ }
+ }
+ return "Invalid.Recipe.Hash";
+ }
+
+ /**
+ *
+ * @param aDataStick - The Data Stick to update.
+ * @param aRecipeHash - The Recipe Hash String to update with.
+ * @return Did we update the Recipe Hash String on the Data Stick?
+ */
+ public static boolean setRecipeHashOnDataStick(ItemStack aDataStick, String aRecipeHash) {
+ if (isItemDataStick(aDataStick) && aDataStick.hasTagCompound()) {
+ NBTTagCompound aNBT = aDataStick.getTagCompound();
+ aNBT.setString("Data.Recipe.Hash", aRecipeHash);
+ aDataStick.setTagCompound(aNBT);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ *
+ * @param aDataStick - The Data Stick to update.
+ * @param aNewRecipe - The New GT_Recipe_AssemblyLine recipe to update it with.
+ * @return Did we set the new recipe data & Recipe Hash String on the Data Stick?
+ */
+ public static boolean setAssemblyLineRecipeOnDataStick(ItemStack aDataStick, GT_Recipe_AssemblyLine aNewRecipe) {
+ if (isItemDataStick(aDataStick)) {
+ String s = aNewRecipe.mOutput.getDisplayName();
+ if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
+ s = GT_Assemblyline_Server.lServerNames.get(aNewRecipe.mOutput.getDisplayName());
+ if (s == null) {
+ s = aNewRecipe.mOutput.getDisplayName();
+ }
+ }
+
+ String aHash = generateRecipeHash(aNewRecipe);
+ if (GT_Values.D1) {
+ GT_Recipe_AssemblyLine aOldRecipe = findAssemblyLineRecipeFromDataStick(aDataStick, true).recipe;
+ GT_FML_LOGGER.info("Updating data stick: " + aDataStick.getDisplayName() + " | Old Recipe Hash: "
+ + generateRecipeHash(aOldRecipe) + ", New Recipe Hash: " + aHash);
+ }
+
+ String author = "Assembling Line Recipe Generator";
+ String displayName = null;
+ if (aDataStick.hasTagCompound()) {
+ NBTTagCompound tag = aDataStick.getTagCompound();
+ if (tag.hasKey("author", NBT.TAG_STRING)) {
+ author = tag.getString("author");
+ }
+ if (tag.hasKey("display", NBT.TAG_COMPOUND)) {
+ NBTTagCompound displayTag = tag.getCompoundTag("display");
+ if (displayTag.hasKey("Name", NBT.TAG_STRING)) displayName = displayTag.getString("Name");
+ }
+ }
+
+ // remove possible old NBTTagCompound
+ aDataStick.setTagCompound(new NBTTagCompound());
+ if (displayName != null) aDataStick.setStackDisplayName(displayName);
+ if (GT_Values.D1) {
+ GT_Utility.ItemNBT.setBookTitle(aDataStick, s + " Construction Data (" + aHash + ")");
+ } else {
+ GT_Utility.ItemNBT.setBookTitle(aDataStick, s + " Construction Data");
+ }
+
+ NBTTagCompound tNBT = aDataStick.getTagCompound();
+ if (tNBT == null) {
+ tNBT = new NBTTagCompound();
+ }
+
+ tNBT.setTag("output", aNewRecipe.mOutput.writeToNBT(new NBTTagCompound()));
+ tNBT.setInteger("time", aNewRecipe.mDuration);
+ tNBT.setInteger("eu", aNewRecipe.mEUt);
+ for (int i = 0; i < aNewRecipe.mInputs.length; i++) {
+ tNBT.setTag("" + i, aNewRecipe.mInputs[i].writeToNBT(new NBTTagCompound()));
+ }
+ for (int i = 0; i < aNewRecipe.mOreDictAlt.length; i++) {
+ if (aNewRecipe.mOreDictAlt[i] != null && aNewRecipe.mOreDictAlt[i].length > 0) {
+ tNBT.setInteger("a" + i, aNewRecipe.mOreDictAlt[i].length);
+ for (int j = 0; j < aNewRecipe.mOreDictAlt[i].length; j++) {
+ tNBT.setTag("a" + i + ":" + j, aNewRecipe.mOreDictAlt[i][j].writeToNBT(new NBTTagCompound()));
+ }
+ }
+ }
+ for (int i = 0; i < aNewRecipe.mFluidInputs.length; i++) {
+ tNBT.setTag("f" + i, aNewRecipe.mFluidInputs[i].writeToNBT(new NBTTagCompound()));
+ }
+ tNBT.setString("author", author);
+ NBTTagList tNBTList = new NBTTagList();
+ s = aNewRecipe.mOutput.getDisplayName();
+ if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
+ s = GT_Assemblyline_Server.lServerNames.get(aNewRecipe.mOutput.getDisplayName());
+ if (s == null) s = aNewRecipe.mOutput.getDisplayName();
+ }
+ tNBTList.appendTag(new NBTTagString("Construction plan for " + aNewRecipe.mOutput.stackSize + " " + s
+ + ". Needed EU/t: " + aNewRecipe.mEUt + " Production time: " + (aNewRecipe.mDuration / 20)));
+ for (int i = 0; i < aNewRecipe.mInputs.length; i++) {
+ if (aNewRecipe.mOreDictAlt[i] != null) {
+ int count = 0;
+ StringBuilder tBuilder = new StringBuilder("Input Bus " + (i + 1) + ": ");
+ for (ItemStack tStack : aNewRecipe.mOreDictAlt[i]) {
+ if (tStack != null) {
+ s = tStack.getDisplayName();
+ if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
+ s = GT_Assemblyline_Server.lServerNames.get(tStack.getDisplayName());
+ if (s == null) s = tStack.getDisplayName();
+ }
+
+ tBuilder.append(count == 0 ? "" : "\nOr ")
+ .append(tStack.stackSize)
+ .append(" ")
+ .append(s);
+ count++;
+ }
+ }
+ if (count > 0) tNBTList.appendTag(new NBTTagString(tBuilder.toString()));
+ } else if (aNewRecipe.mInputs[i] != null) {
+ s = aNewRecipe.mInputs[i].getDisplayName();
+ if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
+ s = GT_Assemblyline_Server.lServerNames.get(aNewRecipe.mInputs[i].getDisplayName());
+ if (s == null) s = aNewRecipe.mInputs[i].getDisplayName();
+ }
+ tNBTList.appendTag(new NBTTagString(
+ "Input Bus " + (i + 1) + ": " + aNewRecipe.mInputs[i].stackSize + " " + s));
+ }
+ }
+ for (int i = 0; i < aNewRecipe.mFluidInputs.length; i++) {
+ if (aNewRecipe.mFluidInputs[i] != null) {
+ s = aNewRecipe.mFluidInputs[i].getLocalizedName();
+ if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
+ s = GT_Assemblyline_Server.lServerNames.get(aNewRecipe.mFluidInputs[i].getLocalizedName());
+ if (s == null) s = aNewRecipe.mFluidInputs[i].getLocalizedName();
+ }
+ tNBTList.appendTag(new NBTTagString(
+ "Input Hatch " + (i + 1) + ": " + aNewRecipe.mFluidInputs[i].amount + "L " + s));
+ }
+ }
+ tNBT.setTag("pages", tNBTList);
+ tNBT.setLong("lastUpdate", System.currentTimeMillis());
+ aDataStick.setTagCompound(tNBT);
+ // Set recipe hash
+ setRecipeHashOnDataStick(aDataStick, aHash);
+ return true;
+ }
+ return false;
+ }
+
+ public enum LookupResultType {
+ INVALID_STICK(true),
+ VALID_STACK_BUT_INVALID_RECIPE(true),
+ VALID_STACK_AND_VALID_RECIPE(false),
+ VALID_STACK_AND_VALID_HASH(false);
+
+ private final boolean recipeNull;
+ private LookupResult singletonResult;
+
+ LookupResultType(boolean recipeNull) {
+ this.recipeNull = recipeNull;
+ }
+
+ public LookupResult getResult() {
+ if (!recipeNull) throw new IllegalArgumentException("This result type require a nonnull recipe");
+ if (singletonResult == null) singletonResult = new LookupResult(null, this);
+ return singletonResult;
+ }
+
+ public LookupResult getResult(GT_Recipe_AssemblyLine recipe) {
+ if ((recipe == null) != recipeNull)
+ throw new IllegalArgumentException("This result type does not allow given input");
+ return new LookupResult(recipe, this);
+ }
+ }
+
+ public static class LookupResult {
+ private final GT_Recipe_AssemblyLine recipe;
+ private final LookupResultType type;
+
+ LookupResult(GT_Recipe_AssemblyLine recipe, LookupResultType type) {
+ this.recipe = recipe;
+ this.type = type;
+ }
+
+ public GT_Recipe_AssemblyLine getRecipe() {
+ return recipe;
+ }
+
+ public LookupResultType getType() {
+ return type;
+ }
+ }
}