diff options
author | Maxim <maxim235@gmx.de> | 2023-07-10 10:13:04 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-10 10:13:04 +0200 |
commit | fe0387946550f89a403b09f4e8cf6e43ee8f2e8f (patch) | |
tree | 6b56a553c2a5bbf2427a93e92724452ba5bc60f7 /src/main/java/gregtech/api/util | |
parent | 9fe44bf0eaa55825c1c3cdf90f69aeeb668f45b1 (diff) | |
download | GT5-Unofficial-fe0387946550f89a403b09f4e8cf6e43ee8f2e8f.tar.gz GT5-Unofficial-fe0387946550f89a403b09f4e8cf6e43ee8f2e8f.tar.bz2 GT5-Unofficial-fe0387946550f89a403b09f4e8cf6e43ee8f2e8f.zip |
Generic processing logic (#2096)
* Added enumeration for check recipe result
* Rework processing logic to work with MTEs too
* Switched first few multiblocks to new checkRecipe method
* Applied generic logic to EBF
* Added support for long power base and applied generic processing logic to more machines
* Address some feedback
* Added more setter to further configure the processing logic
* Change internal checkRecipe to work with checkRecipeResult, to allow the injection of custom failure messages
* Suppress warning, change access
* Merge recipeMap and mapSupplier
* Move calls to setMetaTEController and setRecipeMap to base classes
* Make processingLogic final
* Make results non-null
* Rename `ProcessingLogic#checkRecipe` -> `#validateRecipe`
Otherwise it's confusing with `GT_MetaTileEntity_MultiBlockBase#checkRecipe`
* oops
* Added recipe locking to generic processing logic
* Rename: getWorldObject -> getIHasWorldObjectAndCoords
* Annotate missing overrides
* Renamed recipeLockableController -> recipeLockableMachine
* Migrated Cleanroom
* Migrated pyrolyse oven
* Migrated driller
* Migrated charcoal pit
* Migrated DT
* Rename: controller -> machine
* Make recipemaps override base `findRecipe` and mark others final
* Remove unused maps
* Add FindRecipeResult
* Remove IHasWorldObjectAndCoords parameter from findRecipe
This removes argument for printer recipemap to pass for GT_ModHandler.getAllRecipeOutput, but I don't think there's any mod that adds world-specific coloring recipe.
* Added method to set processing logic power so machines can override it on demand
* Restructure CheckRecipeResult, show voltage required and move package
* Reword: insufficient voltage -> power
* Change text color: dark gray -> gray
* Added findRecipeResult for insufficient heat
* Migrated PCB factory
* Fix result not being reset on shut down
* Show coil tier for heat
* clean
* Reverted migration of driller base
* Migrated TPM
* Moved getting of parallel supplier, to accomodate TPM
* Migrated power gen multiblocks
* Migrated Assembling Line
* Migrated fusion
* Migrated multi smelter
* Migrated boiler
* Migrated DTPF
* Migrated ore factory
* Migrated heat exchanger
* Make checkRecipe() final, javadoc, minor cleanup
* Fix overclock behavior with multiple hatches, javadoc, minor cleanup
* Minor fix for javadoc
* Fixed creation of OC calculator not factoring in batch mode correctly
* Make GT_ParallelHelper#setRecipe nonnull
* Rework SimpleCheckRecipeResult to not require registration
* Migrate charcoal pit and cleanroom
* Fix result not being reset when turning off machine
* Add API for BW to make recipemap sensitive to special slot on recipe search
* Migrated PA
* Migrated driller base
* Make ProcessingLogic#duration int
* Minor cleanup
* missing recipe locking for long multi
* Fix NPE
* Show crash message and turn off machine
* minor javadoc fix
* Fixed power setting for extended power base
* Integrate SingleRecipeCheck into ProcessingLogic, fix duration overflow, fix duration for batch mode, migrate PA for GT_MetaTileEntity_ExtendedPowerMultiBlockBase
* Fixed ME stocking busses
* Minor PA fixes
* Cleanup item collecting logic & apply to normal multi as well
* Oversight
* Derp
* Multiple voltage instead of amperage with recipe map amperage, since usually amperage is intended for parallel, not for recipe checking
* Fix missing EU modifiers on PCB factory OC calculator
* Removed left over OC method
* Fixed duration calculation of PCB not applying roughness multiplier
* Fixed OC parameters
* Make createOverclockCalculator nonnull & more nonnull annotations
* Fixed input processing voltage
* Round down voltage for other machines too
* Revert Nano Forge, return correct long voltage
* Use region / endregion
---------
Co-authored-by: miozune <miozune@gmail.com>
Diffstat (limited to 'src/main/java/gregtech/api/util')
8 files changed, 499 insertions, 1237 deletions
diff --git a/src/main/java/gregtech/api/util/GT_OverclockCalculator.java b/src/main/java/gregtech/api/util/GT_OverclockCalculator.java index ba90240f14..6636b27bc7 100644 --- a/src/main/java/gregtech/api/util/GT_OverclockCalculator.java +++ b/src/main/java/gregtech/api/util/GT_OverclockCalculator.java @@ -1,5 +1,7 @@ package gregtech.api.util; +import javax.annotation.Nonnull; + public class GT_OverclockCalculator { /** @@ -15,7 +17,7 @@ public class GT_OverclockCalculator { * GT++ machines * mHeatDiscountAmount - The value used for discount final eut per 900 heat */ - private float mEUtDiscount = 1, mSpeedBoost = 1, mHeatDiscountAmount = 0.95f; + private double mEUtDiscount = 1, mSpeedBoost = 1, mHeatDiscountAmount = 0.95f; /** * mEUtIncreasePerOC - How much the bits should be moved to the left when it is overclocking (Going up, 2 meaning * it is multiplied with 4x) @@ -42,6 +44,15 @@ public class GT_OverclockCalculator { private static final int HEAT_PERFECT_OVERCLOCK_THRESHOLD = 1800; /** + * Creates calculator that doesn't do OC at all. + */ + public static GT_OverclockCalculator ofNoOverclock(@Nonnull GT_Recipe recipe) { + return new GT_OverclockCalculator().setRecipeEUt(recipe.mEUt) + .setDuration(recipe.mDuration) + .setEUt(recipe.mEUt); + } + + /** * An Overclock helper for calculating overclocks in many different situations */ public GT_OverclockCalculator() {} diff --git a/src/main/java/gregtech/api/util/GT_ParallelHelper.java b/src/main/java/gregtech/api/util/GT_ParallelHelper.java index 9ceec7af44..4c9f53745b 100644 --- a/src/main/java/gregtech/api/util/GT_ParallelHelper.java +++ b/src/main/java/gregtech/api/util/GT_ParallelHelper.java @@ -1,11 +1,17 @@ package gregtech.api.util; +import java.util.Objects; + +import javax.annotation.Nonnull; + import net.minecraft.item.ItemStack; import net.minecraftforge.fluids.FluidStack; +import gregtech.api.interfaces.tileentity.IRecipeLockable; import gregtech.api.interfaces.tileentity.IVoidable; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase; import gregtech.api.objects.XSTR; +import gregtech.api.recipe.check.SingleRecipeCheck; @SuppressWarnings({ "unused", "UnusedReturnValue" }) public class GT_ParallelHelper { @@ -15,6 +21,14 @@ public class GT_ParallelHelper { */ private IVoidable machine; /** + * Machine used for single recipe locking calculation + */ + private IRecipeLockable singleRecipeMachine; + /** + * Is locked to a single recipe? + */ + private boolean isRecipeLocked; + /** * Recipe used when trying to calculate parallels */ private GT_Recipe mRecipe; @@ -79,7 +93,7 @@ public class GT_ParallelHelper { /** * What is the duration multiplier with batch mode enabled */ - private float mDurationMultiplier; + private double mDurationMultiplier; /** * Modifier which is applied on the recipe eut. Useful for GT++ machines */ @@ -128,8 +142,14 @@ public class GT_ParallelHelper { /** * Sets the recipe, which will be used for the parallel calculation */ - public GT_ParallelHelper setRecipe(GT_Recipe aRecipe) { - mRecipe = aRecipe; + public GT_ParallelHelper setRecipe(@Nonnull GT_Recipe aRecipe) { + mRecipe = Objects.requireNonNull(aRecipe); + return this; + } + + public GT_ParallelHelper setRecipeLocked(IRecipeLockable singleRecipeMachine, boolean isRecipeLocked) { + this.singleRecipeMachine = singleRecipeMachine; + this.isRecipeLocked = isRecipeLocked; return this; } @@ -186,7 +206,7 @@ public class GT_ParallelHelper { * modifier of 1 does nothing */ public GT_ParallelHelper enableBatchMode(int aBatchModifier) { - mBatchMode = true; + mBatchMode = aBatchModifier > 1; mBatchModifier = aBatchModifier; return this; } @@ -207,6 +227,9 @@ public class GT_ParallelHelper { if (mBuilt) { throw new IllegalStateException("Tried to build twice"); } + if (mRecipe == null) { + throw new IllegalStateException("Recipe is not set"); + } mBuilt = true; determineParallel(); return this; @@ -225,7 +248,7 @@ public class GT_ParallelHelper { /** * @return The duration multiplier if batch mode was enabled for the multiblock */ - public float getDurationMultiplier() { + public double getDurationMultiplierDouble() { if (!mBuilt) { throw new IllegalStateException("Tried to get duration multiplier before building"); } @@ -236,6 +259,14 @@ public class GT_ParallelHelper { } /** + * @deprecated Use {@link #getDurationMultiplierDouble()} + */ + @Deprecated + public float getDurationMultiplier() { + return (float) getDurationMultiplierDouble(); + } + + /** * @return The ItemOutputs from the recipe */ public ItemStack[] getItemOutputs() { @@ -264,35 +295,50 @@ public class GT_ParallelHelper { if (mRecipe.mEUt > mAvailableEUt) { return; } - ItemStack[] tItemInputs = null; - FluidStack[] tFluidInputs = null; - long tCurrentUsage = 0; - // see if people want to consume their inputs with the Parallel Helper or not + if (mItemInputs == null) { + mItemInputs = new ItemStack[0]; + } + if (mFluidInputs == null) { + mFluidInputs = new FluidStack[0]; + } + + ItemStack[] tItemInputs; + FluidStack[] tFluidInputs; + // see if people want to consume their inputs or not if (mConsume) { tItemInputs = mItemInputs; tFluidInputs = mFluidInputs; } else { - if (mItemInputs == null) { - tItemInputs = new ItemStack[] {}; - } else { - tItemInputs = new ItemStack[mItemInputs.length]; - for (int i = 0; i < mItemInputs.length; i++) { - tItemInputs[i] = mItemInputs[i].copy(); - } + // copy to prevent consuming original inputs + tItemInputs = new ItemStack[mItemInputs.length]; + for (int i = 0; i < mItemInputs.length; i++) { + tItemInputs[i] = mItemInputs[i].copy(); } - - if (mFluidInputs == null) { - mFluidInputs = new FluidStack[] {}; - } else { - tFluidInputs = new FluidStack[mFluidInputs.length]; - for (int i = 0; i < mFluidInputs.length; i++) { - tFluidInputs[i] = mFluidInputs[i].copy(); - } + tFluidInputs = new FluidStack[mFluidInputs.length]; + for (int i = 0; i < mFluidInputs.length; i++) { + tFluidInputs[i] = mFluidInputs[i].copy(); } } + if (mBatchMode) { mMaxParallel *= mBatchModifier; } + + SingleRecipeCheck recipeCheck = null; + SingleRecipeCheck.Builder tSingleRecipeCheckBuilder = null; + if (isRecipeLocked && singleRecipeMachine != null) { + recipeCheck = singleRecipeMachine.getSingleRecipeCheck(); + if (recipeCheck == null) { + // Machine is configured to lock to a single recipe, but haven't built the recipe checker yet. + // Build the checker on next successful recipe. + GT_Recipe.GT_Recipe_Map recipeMap = singleRecipeMachine.getRecipeMap(); + if (recipeMap != null) { + tSingleRecipeCheckBuilder = SingleRecipeCheck.builder(recipeMap) + .setBefore(tItemInputs, tFluidInputs); + } + } + } + // Let's look at how many parallels we can get with void protection if (protectExcessItem || protectExcessFluid) { if (machine == null) { @@ -307,31 +353,50 @@ public class GT_ParallelHelper { mMaxParallel = Math.min(voidProtectionHelper.getMaxParallel(), mMaxParallel); } - float tRecipeEUt = mRecipe.mEUt * mEUtModifier; + final int tRecipeEUt = (int) Math.ceil(mRecipe.mEUt * mEUtModifier); + final int batchCorrectedMaxParallel = mMaxParallel / mBatchModifier; // Consume inputs to determine normal parallel - for (; mCurrentParallel < mMaxParallel / mBatchModifier - && tCurrentUsage < (mAvailableEUt - tRecipeEUt); mCurrentParallel++) { - if (mRecipe.isRecipeInputEqual(true, false, tFluidInputs, tItemInputs)) { + if (recipeCheck != null) { + int actualMaxParallel = (int) Math.min(batchCorrectedMaxParallel, mAvailableEUt / tRecipeEUt); + mCurrentParallel = recipeCheck.checkRecipeInputs(true, actualMaxParallel, tItemInputs, tFluidInputs); + } else { + long tCurrentUsage = 0; + boolean builtRecipeCheck = false; + for (; mCurrentParallel < batchCorrectedMaxParallel + && tCurrentUsage < (mAvailableEUt - tRecipeEUt); mCurrentParallel++) { + if (!mRecipe.isRecipeInputEqual(true, false, tFluidInputs, tItemInputs)) { + break; + } tCurrentUsage += tRecipeEUt; - } else { - break; + if (tSingleRecipeCheckBuilder != null && !builtRecipeCheck) { + // If recipe checker is not built yet, build and set it + SingleRecipeCheck builtCheck = tSingleRecipeCheckBuilder.setAfter(tItemInputs, tFluidInputs) + .setRecipe(mRecipe) + .build(); + singleRecipeMachine.setSingleRecipeCheck(builtCheck); + builtRecipeCheck = true; + } } } // If Batch Mode is enabled determine how many extra parallels we can get - if (mBatchMode) { + if (mBatchMode && mCurrentParallel > 0) { int tExtraParallels = 0; - while (tExtraParallels < mCurrentParallel * (mBatchModifier - 1) - && mRecipe.isRecipeInputEqual(false, false, tFluidInputs, tItemInputs)) { - mRecipe.isRecipeInputEqual(true, false, tFluidInputs, tItemInputs); - tExtraParallels++; + final int maxExtraParallels = mCurrentParallel * (mBatchModifier - 1); + if (recipeCheck != null) { + tExtraParallels = recipeCheck.checkRecipeInputs(true, maxExtraParallels, tItemInputs, tFluidInputs); + } else { + while (tExtraParallels < maxExtraParallels + && mRecipe.isRecipeInputEqual(true, false, tFluidInputs, tItemInputs)) { + tExtraParallels++; + } } mDurationMultiplier = 1.0f + (float) tExtraParallels / mCurrentParallel; mCurrentParallel += tExtraParallels; } // If we want to calculate outputs we do it here - if (mCalculateOutputs) { + if (mCalculateOutputs && mCurrentParallel > 0) { if (mRecipe.mOutputs != null) { mItemOutputs = new ItemStack[mRecipe.mOutputs.length]; for (int i = 0; i < mRecipe.mOutputs.length; i++) { diff --git a/src/main/java/gregtech/api/util/GT_ProcessingArray_Manager.java b/src/main/java/gregtech/api/util/GT_ProcessingArray_Manager.java index 66bc16cae3..0ef28bd705 100644 --- a/src/main/java/gregtech/api/util/GT_ProcessingArray_Manager.java +++ b/src/main/java/gregtech/api/util/GT_ProcessingArray_Manager.java @@ -2,6 +2,8 @@ package gregtech.api.util; import java.util.HashMap; +import net.minecraft.item.ItemStack; + import gregtech.api.enums.SoundResource; import gregtech.api.util.GT_Recipe.GT_Recipe_Map; @@ -38,4 +40,11 @@ public class GT_ProcessingArray_Manager { } return null; } + + public static String getMachineName(ItemStack stack) { + int length = stack.getUnlocalizedName() + .length(); + return stack.getUnlocalizedName() + .substring(17, length - 8); // trim "gt.blockmachines." and ".tier.xx" + } } diff --git a/src/main/java/gregtech/api/util/GT_Recipe.java b/src/main/java/gregtech/api/util/GT_Recipe.java index 748b922b44..d26b8105cf 100644 --- a/src/main/java/gregtech/api/util/GT_Recipe.java +++ b/src/main/java/gregtech/api/util/GT_Recipe.java @@ -9,6 +9,7 @@ import static gregtech.api.enums.Mods.GTPlusPlus; import static gregtech.api.enums.Mods.GregTech; import static gregtech.api.enums.Mods.NEICustomDiagrams; import static gregtech.api.enums.Mods.Railcraft; +import static gregtech.api.recipe.check.FindRecipeResult.*; import static gregtech.api.util.GT_RecipeBuilder.handleRecipeCollision; import static gregtech.api.util.GT_RecipeConstants.ADDITIVE_AMOUNT; import static gregtech.api.util.GT_RecipeMapUtil.FIRST_FLUIDSTACK_INPUT; @@ -104,11 +105,11 @@ import gregtech.api.gui.modularui.FallbackableSteamTexture; import gregtech.api.gui.modularui.GT_UITextures; import gregtech.api.gui.modularui.SteamTexture; import gregtech.api.interfaces.IGT_RecipeMap; -import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gregtech.api.interfaces.tileentity.IHasWorldObjectAndCoords; import gregtech.api.objects.GT_ItemStack; import gregtech.api.objects.ItemData; import gregtech.api.objects.MaterialStack; +import gregtech.api.recipe.check.FindRecipeResult; import gregtech.api.util.extensions.ArrayExt; import gregtech.common.gui.modularui.UIHelper; import gregtech.common.items.GT_FluidDisplayItem; @@ -3051,6 +3052,14 @@ public class GT_Recipe implements Comparable<GT_Recipe> { .orElse(Collections.emptyList()))); } + @Nullable + public static GT_Recipe_Map findRecipeMap(@Nonnull String unlocalizedName) { + return sMappings.stream() + .filter(m -> unlocalizedName.equals(m.mUnlocalizedName)) + .findFirst() + .orElse(null); + } + /** * HashMap of Recipes based on their Items */ @@ -3105,6 +3114,11 @@ public class GT_Recipe implements Comparable<GT_Recipe> { private boolean mUsesSpecialSlot = false; /** + * Whether this recipemap checks for equality of special slot when searching recipe. + */ + private boolean isSpecialSlotSensitive = false; + + /** * How many fluid inputs does this recipemap has at most. Currently used only for NEI slot placements and does * not actually restrict number of fluids used in the recipe. */ @@ -3188,6 +3202,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> { private int neiTextColorOverride = -1; private INEISpecialInfoFormatter neiSpecialInfoFormatter; + private final boolean checkForCollision = true; private boolean allowNoInput; private boolean allowNoInputFluid; @@ -3293,6 +3308,11 @@ public class GT_Recipe implements Comparable<GT_Recipe> { return this; } + public GT_Recipe_Map setSpecialSlotSensitive(boolean isSpecialSlotSensitive) { + this.isSpecialSlotSensitive = isSpecialSlotSensitive; + return this; + } + public GT_Recipe_Map setNEIUnificateOutput(boolean mNEIUnificateOutput) { this.mNEIUnificateOutput = mNEIUnificateOutput; return this; @@ -3882,12 +3902,14 @@ public class GT_Recipe implements Comparable<GT_Recipe> { return aFluid != null && mRecipeFluidNameMap.contains(aFluid.getName()); } - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, boolean aNotUnificated, long aVoltage, + @Nullable + public final GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, boolean aNotUnificated, long aVoltage, FluidStack[] aFluids, ItemStack... aInputs) { return findRecipe(aTileEntity, null, aNotUnificated, aVoltage, aFluids, null, aInputs); } - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, boolean aNotUnificated, + @Nullable + public final GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, boolean aNotUnificated, boolean aDontCheckStackSizes, long aVoltage, FluidStack[] aFluids, ItemStack... aInputs) { return findRecipe( aTileEntity, @@ -3900,13 +3922,16 @@ public class GT_Recipe implements Comparable<GT_Recipe> { aInputs); } - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - long aVoltage, FluidStack[] aFluids, ItemStack... aInputs) { + @Nullable + public final GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, + boolean aNotUnificated, long aVoltage, FluidStack[] aFluids, ItemStack... aInputs) { return findRecipe(aTileEntity, aRecipe, aNotUnificated, aVoltage, aFluids, null, aInputs); } - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - boolean aDontCheckStackSizes, long aVoltage, FluidStack[] aFluids, ItemStack... aInputs) { + @Nullable + public final GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, + boolean aNotUnificated, boolean aDontCheckStackSizes, long aVoltage, FluidStack[] aFluids, + ItemStack... aInputs) { return findRecipe( aTileEntity, aRecipe, @@ -3918,16 +3943,32 @@ public class GT_Recipe implements Comparable<GT_Recipe> { aInputs); } - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { + @Nullable + public final GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, + boolean aNotUnificated, long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { return findRecipe(aTileEntity, aRecipe, aNotUnificated, false, aVoltage, aFluids, aSpecialSlot, aInputs); } + // TODO: make this final after migrating BW + @SuppressWarnings("unused") + @Nullable + public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, + boolean aDontCheckStackSizes, long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, + ItemStack... aInputs) { + FindRecipeResult result = findRecipeWithResult( + aRecipe, + aNotUnificated, + aDontCheckStackSizes, + aVoltage, + aFluids, + aSpecialSlot, + aInputs); + return result.isSuccessful() ? result.getRecipe() : null; + } + /** * finds a Recipe matching the aFluid and ItemStack Inputs. * - * @param aTileEntity an Object representing the current coordinates of the executing - * Block/Entity/Whatever. This may be null, especially during Startup. * @param aRecipe in case this is != null it will try to use this Recipe first when looking things * up. * @param aNotUnificated if this is T the Recipe searcher will unificate the ItemStack Inputs @@ -3938,13 +3979,14 @@ public class GT_Recipe implements Comparable<GT_Recipe> { * @param aSpecialSlot the content of the Special Slot, the regular Manager doesn't do anything with * this, but some custom ones do. * @param aInputs the Item Inputs - * @return the Recipe it has found or null for no matching Recipe + * @return Result of the recipe search */ - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, + @Nonnull + public FindRecipeResult findRecipeWithResult(GT_Recipe aRecipe, boolean aNotUnificated, boolean aDontCheckStackSizes, long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { // No Recipes? Well, nothing to be found then. - if (mRecipeList.isEmpty()) return null; + if (mRecipeList.isEmpty()) return NOT_FOUND; // Some Recipe Classes require a certain amount of Inputs of certain kinds. Like "at least 1 Fluid + 1 // Stack" or "at least 2 Stacks" before they start searching for Recipes. @@ -3952,16 +3994,16 @@ public class GT_Recipe implements Comparable<GT_Recipe> { // their Machines to select Sub Recipes. if (GregTech_API.sPostloadFinished) { if (mMinimalInputFluids > 0) { - if (aFluids == null) return null; + if (aFluids == null) return NOT_FOUND; int tAmount = 0; for (FluidStack aFluid : aFluids) if (aFluid != null) tAmount++; - if (tAmount < mMinimalInputFluids) return null; + if (tAmount < mMinimalInputFluids) return NOT_FOUND; } if (mMinimalInputItems > 0) { - if (aInputs == null) return null; + if (aInputs == null) return NOT_FOUND; int tAmount = 0; for (ItemStack aInput : aInputs) if (aInput != null) tAmount++; - if (tAmount < mMinimalInputItems) return null; + if (tAmount < mMinimalInputItems) return NOT_FOUND; } } @@ -3970,19 +4012,37 @@ public class GT_Recipe implements Comparable<GT_Recipe> { // Check the Recipe which has been used last time in order to not have to search for it again, if possible. if (aRecipe != null) if (!aRecipe.mFakeRecipe && aRecipe.mCanBeBuffered - && aRecipe.isRecipeInputEqual(false, aDontCheckStackSizes, aFluids, aInputs)) - return aRecipe.mEnabled && aVoltage * mAmperage >= aRecipe.mEUt ? aRecipe : null; + && aRecipe.isRecipeInputEqual(false, aDontCheckStackSizes, aFluids, aInputs)) { + if (!isSpecialSlotSensitive + || GT_Utility.areStacksEqualOrNull((ItemStack) aRecipe.mSpecialItems, aSpecialSlot)) { + return aRecipe.mEnabled && aVoltage * mAmperage >= aRecipe.mEUt + ? FindRecipeResult.ofSuccess(aRecipe) + : FindRecipeResult.ofInsufficientVoltage(aRecipe); + } + } // Now look for the Recipes inside the Item HashMaps, but only when the Recipes usually have Items. if (mUsualInputCount > 0 && aInputs != null) for (ItemStack tStack : aInputs) if (tStack != null) { Collection<GT_Recipe> tRecipes = mRecipeItemMap.get(new GT_ItemStack(tStack)); if (tRecipes != null) for (GT_Recipe tRecipe : tRecipes) if (!tRecipe.mFakeRecipe - && tRecipe.isRecipeInputEqual(false, aDontCheckStackSizes, aFluids, aInputs)) - return tRecipe.mEnabled && aVoltage * mAmperage >= tRecipe.mEUt ? tRecipe : null; + && tRecipe.isRecipeInputEqual(false, aDontCheckStackSizes, aFluids, aInputs)) { + if (!isSpecialSlotSensitive + || GT_Utility.areStacksEqualOrNull((ItemStack) tRecipe.mSpecialItems, aSpecialSlot)) { + return tRecipe.mEnabled && aVoltage * mAmperage >= tRecipe.mEUt + ? FindRecipeResult.ofSuccess(tRecipe) + : FindRecipeResult.ofInsufficientVoltage(tRecipe); + } + } tRecipes = mRecipeItemMap.get(new GT_ItemStack(tStack, true)); if (tRecipes != null) for (GT_Recipe tRecipe : tRecipes) if (!tRecipe.mFakeRecipe - && tRecipe.isRecipeInputEqual(false, aDontCheckStackSizes, aFluids, aInputs)) - return tRecipe.mEnabled && aVoltage * mAmperage >= tRecipe.mEUt ? tRecipe : null; + && tRecipe.isRecipeInputEqual(false, aDontCheckStackSizes, aFluids, aInputs)) { + if (!isSpecialSlotSensitive + || GT_Utility.areStacksEqualOrNull((ItemStack) tRecipe.mSpecialItems, aSpecialSlot)) { + return tRecipe.mEnabled && aVoltage * mAmperage >= tRecipe.mEUt + ? FindRecipeResult.ofSuccess(tRecipe) + : FindRecipeResult.ofInsufficientVoltage(tRecipe); + } + } } // If the minimal Amount of Items for the Recipe is 0, then it could be a Fluid-Only Recipe, so check that @@ -3990,12 +4050,18 @@ public class GT_Recipe implements Comparable<GT_Recipe> { if (mMinimalInputItems == 0 && aFluids != null) for (FluidStack aFluid : aFluids) if (aFluid != null) { Collection<GT_Recipe> tRecipes = mRecipeFluidMap.get(aFluid.getFluid()); if (tRecipes != null) for (GT_Recipe tRecipe : tRecipes) if (!tRecipe.mFakeRecipe - && tRecipe.isRecipeInputEqual(false, aDontCheckStackSizes, aFluids, aInputs)) - return tRecipe.mEnabled && aVoltage * mAmperage >= tRecipe.mEUt ? tRecipe : null; + && tRecipe.isRecipeInputEqual(false, aDontCheckStackSizes, aFluids, aInputs)) { + if (!isSpecialSlotSensitive + || GT_Utility.areStacksEqualOrNull((ItemStack) tRecipe.mSpecialItems, aSpecialSlot)) { + return tRecipe.mEnabled && aVoltage * mAmperage >= tRecipe.mEUt + ? FindRecipeResult.ofSuccess(tRecipe) + : FindRecipeResult.ofInsufficientVoltage(tRecipe); + } + } } // And nothing has been found. - return null; + return NOT_FOUND; } protected GT_Recipe addToItemMap(GT_Recipe aRecipe) { @@ -4854,24 +4920,28 @@ public class GT_Recipe implements Comparable<GT_Recipe> { aNEIAllowed); } + @Nonnull @Override - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { - if (aInputs == null || aInputs.length == 0 || aInputs[0] == null) return null; - if (aRecipe != null && aRecipe.isRecipeInputEqual(false, true, aFluids, aInputs)) return aRecipe; + public FindRecipeResult findRecipeWithResult(GT_Recipe aRecipe, boolean aNotUnificated, + boolean aDontCheckStackSizes, long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, + ItemStack... aInputs) { + if (aInputs == null || aInputs.length == 0 || aInputs[0] == null) return NOT_FOUND; + if (aRecipe != null && aRecipe.isRecipeInputEqual(false, true, aFluids, aInputs)) + return FindRecipeResult.ofSuccess(aRecipe); ItemStack tOutput = GT_ModHandler.getSmeltingOutput(aInputs[0], false, null); - return tOutput == null ? null - : new GT_Recipe( - false, - new ItemStack[] { GT_Utility.copyAmount(1, aInputs[0]) }, - new ItemStack[] { tOutput }, - null, - null, - null, - null, - 128, - 4, - 0); + return tOutput == null ? NOT_FOUND + : FindRecipeResult.ofSuccess( + new GT_Recipe( + false, + new ItemStack[] { GT_Utility.copyAmount(1, aInputs[0]) }, + new ItemStack[] { tOutput }, + null, + null, + null, + null, + 128, + 4, + 0)); } @Override @@ -4907,25 +4977,30 @@ public class GT_Recipe implements Comparable<GT_Recipe> { aNEIAllowed); } + @Nonnull @Override - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { - if (aInputs == null || aInputs.length == 0 || aInputs[0] == null) return null; - if (aRecipe != null && aRecipe.isRecipeInputEqual(false, true, aFluids, aInputs)) return aRecipe; + public FindRecipeResult findRecipeWithResult(GT_Recipe aRecipe, boolean aNotUnificated, + boolean aDontCheckStackSizes, long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, + ItemStack... aInputs) { + if (aInputs == null || aInputs.length == 0 || aInputs[0] == null) return NOT_FOUND; + if (aRecipe != null && aRecipe.isRecipeInputEqual(false, true, aFluids, aInputs)) + return FindRecipeResult.ofSuccess(aRecipe); ItemStack tOutput = GT_ModHandler.getSmeltingOutput(aInputs[0], false, null); if (GT_Utility.areStacksEqual(aInputs[0], new ItemStack(Items.book, 1, W))) { - return new GT_Recipe( - false, - new ItemStack[] { GT_Utility.copyAmount(1, aInputs[0]) }, - new ItemStack[] { GT_Utility.getWrittenBook("Manual_Microwave", ItemList.Book_Written_03.get(1)) }, - null, - null, - null, - null, - 32, - 4, - 0); + return FindRecipeResult.ofSuccess( + new GT_Recipe( + false, + new ItemStack[] { GT_Utility.copyAmount(1, aInputs[0]) }, + new ItemStack[] { + GT_Utility.getWrittenBook("Manual_Microwave", ItemList.Book_Written_03.get(1)) }, + null, + null, + null, + null, + 32, + 4, + 0)); } // Check Container Item of Input since it is around the Input, then the Input itself, then Container Item of @@ -4938,12 +5013,9 @@ public class GT_Recipe implements Comparable<GT_Recipe> { || GT_Utility.areStacksEqual(tStack, new ItemStack(Items.firework_charge, 1, W), true) || GT_Utility.areStacksEqual(tStack, new ItemStack(Items.fireworks, 1, W), true) || GT_Utility.areStacksEqual(tStack, new ItemStack(Items.fire_charge, 1, W), true)) { - if (aTileEntity instanceof IGregTechTileEntity) { - GT_Log.exp.println( - "Microwave Explosion due to TNT || EGG || FIREWORKCHARGE || FIREWORK || FIRE CHARGE"); - ((IGregTechTileEntity) aTileEntity).doExplosion(aVoltage * 4); - } - return null; + GT_Log.exp.println( + "Microwave Explosion due to TNT || EGG || FIREWORKCHARGE || FIREWORK || FIRE CHARGE"); + return EXPLODE; } ItemData tData = GT_OreDictUnificator.getItemData(tStack); @@ -4951,59 +5023,45 @@ public class GT_Recipe implements Comparable<GT_Recipe> { if (tData.mMaterial != null && tData.mMaterial.mMaterial != null) { if (tData.mMaterial.mMaterial.contains(SubTag.METAL) || tData.mMaterial.mMaterial.contains(SubTag.EXPLOSIVE)) { - if (aTileEntity instanceof IGregTechTileEntity) { - GT_Log.exp.println("Microwave Explosion due to METAL insertion"); - ((IGregTechTileEntity) aTileEntity).doExplosion(aVoltage * 4); - } - return null; + GT_Log.exp.println("Microwave Explosion due to METAL insertion"); + return EXPLODE; } if (tData.mMaterial.mMaterial.contains(SubTag.FLAMMABLE)) { - if (aTileEntity instanceof IGregTechTileEntity) { - GT_Log.exp.println("Microwave INFLAMMATION due to FLAMMABLE insertion"); - ((IGregTechTileEntity) aTileEntity).setOnFire(); - } - return null; + GT_Log.exp.println("Microwave INFLAMMATION due to FLAMMABLE insertion"); + return ON_FIRE; } } for (MaterialStack tMaterial : tData.mByProducts) if (tMaterial != null) { if (tMaterial.mMaterial.contains(SubTag.METAL) || tMaterial.mMaterial.contains(SubTag.EXPLOSIVE)) { - if (aTileEntity instanceof IGregTechTileEntity) { - GT_Log.exp.println("Microwave Explosion due to METAL insertion"); - ((IGregTechTileEntity) aTileEntity).doExplosion(aVoltage * 4); - } - return null; + GT_Log.exp.println("Microwave Explosion due to METAL insertion"); + return EXPLODE; } if (tMaterial.mMaterial.contains(SubTag.FLAMMABLE)) { - if (aTileEntity instanceof IGregTechTileEntity) { - ((IGregTechTileEntity) aTileEntity).setOnFire(); - GT_Log.exp.println("Microwave INFLAMMATION due to FLAMMABLE insertion"); - } - return null; + GT_Log.exp.println("Microwave INFLAMMATION due to FLAMMABLE insertion"); + return ON_FIRE; } } } if (TileEntityFurnace.getItemBurnTime(tStack) > 0) { - if (aTileEntity instanceof IGregTechTileEntity) { - ((IGregTechTileEntity) aTileEntity).setOnFire(); - GT_Log.exp.println("Microwave INFLAMMATION due to BURNABLE insertion"); - } - return null; + GT_Log.exp.println("Microwave INFLAMMATION due to BURNABLE insertion"); + return ON_FIRE; } } - return tOutput == null ? null - : new GT_Recipe( - false, - new ItemStack[] { GT_Utility.copyAmount(1, aInputs[0]) }, - new ItemStack[] { tOutput }, - null, - null, - null, - null, - 32, - 4, - 0); + return tOutput == null ? NOT_FOUND + : FindRecipeResult.ofSuccess( + new GT_Recipe( + false, + new ItemStack[] { GT_Utility.copyAmount(1, aInputs[0]) }, + new ItemStack[] { tOutput }, + null, + null, + null, + null, + 32, + 4, + 0)); } @Override @@ -5039,14 +5097,29 @@ public class GT_Recipe implements Comparable<GT_Recipe> { aNEIAllowed); } + @Nonnull @Override - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { + public FindRecipeResult findRecipeWithResult(GT_Recipe aRecipe, boolean aNotUnificated, + boolean aDontCheckStackSizes, long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, + ItemStack... aInputs) { if (aInputs == null || aInputs.length == 0 || !ItemList.IC2_Scrapbox.isStackEqual(aInputs[0], false, true)) - return super.findRecipe(aTileEntity, aRecipe, aNotUnificated, aVoltage, aFluids, aSpecialSlot, aInputs); + return super.findRecipeWithResult( + aRecipe, + aNotUnificated, + aDontCheckStackSizes, + aVoltage, + aFluids, + aSpecialSlot, + aInputs); ItemStack tOutput = GT_ModHandler.getRandomScrapboxDrop(); - if (tOutput == null) - return super.findRecipe(aTileEntity, aRecipe, aNotUnificated, aVoltage, aFluids, aSpecialSlot, aInputs); + if (tOutput == null) return super.findRecipeWithResult( + aRecipe, + aNotUnificated, + aDontCheckStackSizes, + aVoltage, + aFluids, + aSpecialSlot, + aInputs); GT_Recipe rRecipe = new GT_Recipe( false, new ItemStack[] { ItemList.IC2_Scrapbox.get(1) }, @@ -5063,7 +5136,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> { // Due to its randomness it is not good if there are Items in the Output Slot, because those Items could // manipulate the outcome. rRecipe.mNeedsEmptyOutput = true; - return rRecipe; + return FindRecipeResult.ofSuccess(rRecipe); } @Override @@ -5099,39 +5172,46 @@ public class GT_Recipe implements Comparable<GT_Recipe> { aNEIAllowed); } + @Nonnull @Override - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { - GT_Recipe rRecipe = super.findRecipe( - aTileEntity, + public FindRecipeResult findRecipeWithResult(GT_Recipe aRecipe, boolean aNotUnificated, + boolean aDontCheckStackSizes, long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, + ItemStack... aInputs) { + FindRecipeResult result = super.findRecipeWithResult( aRecipe, aNotUnificated, + aDontCheckStackSizes, aVoltage, aFluids, aSpecialSlot, aInputs); if (aInputs == null || aInputs.length == 0 || aInputs[0] == null - || rRecipe != null - || !GregTech_API.sPostloadFinished) return rRecipe; + || result.isSuccessful() + || !GregTech_API.sPostloadFinished) return result; + if (aFluids != null && aFluids.length > 0 && aFluids[0] != null) { ItemStack tOutput = GT_Utility.fillFluidContainer(aFluids[0], aInputs[0], false, true); FluidStack tFluid = GT_Utility.getFluidForFilledItem(tOutput, true); - if (tFluid != null) rRecipe = new GT_Recipe( - false, - new ItemStack[] { GT_Utility.copyAmount(1, aInputs[0]) }, - new ItemStack[] { tOutput }, - null, - null, - new FluidStack[] { tFluid }, - null, - Math.max(tFluid.amount / 64, 16), - 1, - 0); + if (tFluid != null) { + GT_Recipe recipe = new GT_Recipe( + false, + new ItemStack[] { GT_Utility.copyAmount(1, aInputs[0]) }, + new ItemStack[] { tOutput }, + null, + null, + new FluidStack[] { tFluid }, + null, + Math.max(tFluid.amount / 64, 16), + 1, + 0); + recipe.mCanBeBuffered = false; + return FindRecipeResult.ofSuccess(recipe); + } } - if (rRecipe == null) { - FluidStack tFluid = GT_Utility.getFluidForFilledItem(aInputs[0], true); - if (tFluid != null) rRecipe = new GT_Recipe( + FluidStack tFluid = GT_Utility.getFluidForFilledItem(aInputs[0], true); + if (tFluid != null) { + GT_Recipe recipe = new GT_Recipe( false, new ItemStack[] { GT_Utility.copyAmount(1, aInputs[0]) }, new ItemStack[] { GT_Utility.getContainerItem(aInputs[0], true) }, @@ -5142,9 +5222,10 @@ public class GT_Recipe implements Comparable<GT_Recipe> { Math.max(tFluid.amount / 64, 16), 1, 0); + recipe.mCanBeBuffered = false; + return FindRecipeResult.ofSuccess(recipe); } - if (rRecipe != null) rRecipe.mCanBeBuffered = false; - return rRecipe; + return NOT_FOUND; } @Override @@ -5191,329 +5272,31 @@ public class GT_Recipe implements Comparable<GT_Recipe> { aNEIAllowed); } + @Nonnull @Override - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { - if (aInputs == null || aInputs.length == 0 || aInputs[0] == null) return null; - if (aRecipe != null && aRecipe.isRecipeInputEqual(false, true, aFluids, aInputs)) return aRecipe; - return new GT_Recipe( - false, - new ItemStack[] { GT_Utility.copyAmount(1, aInputs[0]) }, - new ItemStack[] { GT_ModHandler.getRecyclerOutput(aInputs[0], 0) }, - null, - new int[] { 1250 }, - null, - null, - 45, - 1, - 0); - } - - @Override - public boolean containsInput(ItemStack aStack) { - return GT_ModHandler.getRecyclerOutput(aStack, 0) != null; - } - } - - /** - * Special Class for Compressor Recipe handling. - */ - public static class GT_Recipe_Map_Compressor extends GT_Recipe_Map_NonGTRecipes { - - public GT_Recipe_Map_Compressor(Collection<GT_Recipe> aRecipeList, String aUnlocalizedName, String aLocalName, - String aNEIName, String aNEIGUIPath, int aUsualInputCount, int aUsualOutputCount, int aMinimalInputItems, - int aMinimalInputFluids, int aAmperage, String aNEISpecialValuePre, int aNEISpecialValueMultiplier, - String aNEISpecialValuePost, boolean aShowVoltageAmperageInNEI, boolean aNEIAllowed) { - super( - aRecipeList, - aUnlocalizedName, - aLocalName, - aNEIName, - aNEIGUIPath, - aUsualInputCount, - aUsualOutputCount, - aMinimalInputItems, - aMinimalInputFluids, - aAmperage, - aNEISpecialValuePre, - aNEISpecialValueMultiplier, - aNEISpecialValuePost, - aShowVoltageAmperageInNEI, - aNEIAllowed); - } - - @Override - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { - if (aInputs == null || aInputs.length == 0 || aInputs[0] == null) return null; - if (aRecipe != null && aRecipe.isRecipeInputEqual(false, true, aFluids, aInputs)) return aRecipe; - ItemStack tComparedInput = GT_Utility.copyOrNull(aInputs[0]); - ItemStack[] tOutputItems = GT_ModHandler.getMachineOutput( - tComparedInput, - ic2.api.recipe.Recipes.compressor.getRecipes(), - true, - new NBTTagCompound(), - null, - null, - null); - return GT_Utility.arrayContainsNonNull(tOutputItems) - ? new GT_Recipe( - false, - new ItemStack[] { - GT_Utility.copyAmount(aInputs[0].stackSize - tComparedInput.stackSize, aInputs[0]) }, - tOutputItems, - null, - null, - null, - null, - 400, - 2, - 0) - : null; - } - - @Override - public boolean containsInput(ItemStack aStack) { - return GT_Utility.arrayContainsNonNull( - GT_ModHandler.getMachineOutput( - GT_Utility.copyAmount(64, aStack), - ic2.api.recipe.Recipes.compressor.getRecipes(), - false, - new NBTTagCompound(), - null, - null, - null)); - } - } - - /** - * Special Class for Extractor Recipe handling. - */ - public static class GT_Recipe_Map_Extractor extends GT_Recipe_Map_NonGTRecipes { - - public GT_Recipe_Map_Extractor(Collection<GT_Recipe> aRecipeList, String aUnlocalizedName, String aLocalName, - String aNEIName, String aNEIGUIPath, int aUsualInputCount, int aUsualOutputCount, int aMinimalInputItems, - int aMinimalInputFluids, int aAmperage, String aNEISpecialValuePre, int aNEISpecialValueMultiplier, - String aNEISpecialValuePost, boolean aShowVoltageAmperageInNEI, boolean aNEIAllowed) { - super( - aRecipeList, - aUnlocalizedName, - aLocalName, - aNEIName, - aNEIGUIPath, - aUsualInputCount, - aUsualOutputCount, - aMinimalInputItems, - aMinimalInputFluids, - aAmperage, - aNEISpecialValuePre, - aNEISpecialValueMultiplier, - aNEISpecialValuePost, - aShowVoltageAmperageInNEI, - aNEIAllowed); - } - - @Override - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { - if (aInputs == null || aInputs.length == 0 || aInputs[0] == null) return null; - if (aRecipe != null && aRecipe.isRecipeInputEqual(false, true, aFluids, aInputs)) return aRecipe; - ItemStack tComparedInput = GT_Utility.copyOrNull(aInputs[0]); - ItemStack[] tOutputItems = GT_ModHandler.getMachineOutput( - tComparedInput, - ic2.api.recipe.Recipes.extractor.getRecipes(), - true, - new NBTTagCompound(), - null, - null, - null); - return GT_Utility.arrayContainsNonNull(tOutputItems) - ? new GT_Recipe( - false, - new ItemStack[] { - GT_Utility.copyAmount(aInputs[0].stackSize - tComparedInput.stackSize, aInputs[0]) }, - tOutputItems, - null, - null, - null, - null, - 400, - 2, - 0) - : null; - } - - @Override - public boolean containsInput(ItemStack aStack) { - return GT_Utility.arrayContainsNonNull( - GT_ModHandler.getMachineOutput( - GT_Utility.copyAmount(64, aStack), - ic2.api.recipe.Recipes.extractor.getRecipes(), - false, - new NBTTagCompound(), - null, - null, - null)); - } - } - - /** - * Special Class for Thermal Centrifuge Recipe handling. - */ - public static class GT_Recipe_Map_ThermalCentrifuge extends GT_Recipe_Map_NonGTRecipes { - - public GT_Recipe_Map_ThermalCentrifuge(Collection<GT_Recipe> aRecipeList, String aUnlocalizedName, - String aLocalName, String aNEIName, String aNEIGUIPath, int aUsualInputCount, int aUsualOutputCount, - int aMinimalInputItems, int aMinimalInputFluids, int aAmperage, String aNEISpecialValuePre, - int aNEISpecialValueMultiplier, String aNEISpecialValuePost, boolean aShowVoltageAmperageInNEI, - boolean aNEIAllowed) { - super( - aRecipeList, - aUnlocalizedName, - aLocalName, - aNEIName, - aNEIGUIPath, - aUsualInputCount, - aUsualOutputCount, - aMinimalInputItems, - aMinimalInputFluids, - aAmperage, - aNEISpecialValuePre, - aNEISpecialValueMultiplier, - aNEISpecialValuePost, - aShowVoltageAmperageInNEI, - aNEIAllowed); - } - - @Override - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { - if (aInputs == null || aInputs.length == 0 || aInputs[0] == null) return null; - if (aRecipe != null && aRecipe.isRecipeInputEqual(false, true, aFluids, aInputs)) return aRecipe; - ItemStack tComparedInput = GT_Utility.copyOrNull(aInputs[0]); - ItemStack[] tOutputItems = GT_ModHandler.getMachineOutput( - tComparedInput, - ic2.api.recipe.Recipes.centrifuge.getRecipes(), - true, - new NBTTagCompound(), - null, - null, - null); - return GT_Utility.arrayContainsNonNull(tOutputItems) - ? new GT_Recipe( - false, - new ItemStack[] { - GT_Utility.copyAmount(aInputs[0].stackSize - tComparedInput.stackSize, aInputs[0]) }, - tOutputItems, - null, - null, - null, - null, - 400, - 48, - 0) - : null; - } - - @Override - public boolean containsInput(ItemStack aStack) { - return GT_Utility.arrayContainsNonNull( - GT_ModHandler.getMachineOutput( - GT_Utility.copyAmount(64, aStack), - ic2.api.recipe.Recipes.centrifuge.getRecipes(), - false, - new NBTTagCompound(), - null, - null, - null)); - } - } - - /** - * Special Class for Ore Washer Recipe handling. - */ - public static class GT_Recipe_Map_OreWasher extends GT_Recipe_Map_NonGTRecipes { - - public GT_Recipe_Map_OreWasher(Collection<GT_Recipe> aRecipeList, String aUnlocalizedName, String aLocalName, - String aNEIName, String aNEIGUIPath, int aUsualInputCount, int aUsualOutputCount, int aMinimalInputItems, - int aMinimalInputFluids, int aAmperage, String aNEISpecialValuePre, int aNEISpecialValueMultiplier, - String aNEISpecialValuePost, boolean aShowVoltageAmperageInNEI, boolean aNEIAllowed) { - super( - aRecipeList, - aUnlocalizedName, - aLocalName, - aNEIName, - aNEIGUIPath, - aUsualInputCount, - aUsualOutputCount, - aMinimalInputItems, - aMinimalInputFluids, - aAmperage, - aNEISpecialValuePre, - aNEISpecialValueMultiplier, - aNEISpecialValuePost, - aShowVoltageAmperageInNEI, - aNEIAllowed); - } - - @Override - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { - if (aInputs == null || aInputs.length == 0 - || aInputs[0] == null - || aFluids == null - || aFluids.length < 1 - || !GT_ModHandler.isWater(aFluids[0])) return null; - if (aRecipe != null && aRecipe.isRecipeInputEqual(false, true, aFluids, aInputs)) return aRecipe; - ItemStack tComparedInput = GT_Utility.copyOrNull(aInputs[0]); - NBTTagCompound aRecipeMetaData = new NBTTagCompound(); - ItemStack[] tOutputItems = GT_ModHandler.getMachineOutput( - tComparedInput, - ic2.api.recipe.Recipes.oreWashing.getRecipes(), - true, - aRecipeMetaData, - null, - null, - null); - return GT_Utility.arrayContainsNonNull(tOutputItems) - ? new GT_Recipe( + public FindRecipeResult findRecipeWithResult(GT_Recipe aRecipe, boolean aNotUnificated, + boolean aDontCheckStackSizes, long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, + ItemStack... aInputs) { + if (aInputs == null || aInputs.length == 0 || aInputs[0] == null) return NOT_FOUND; + if (aRecipe != null && aRecipe.isRecipeInputEqual(false, true, aFluids, aInputs)) + return FindRecipeResult.ofSuccess(aRecipe); + return FindRecipeResult.ofSuccess( + new GT_Recipe( false, - new ItemStack[] { - GT_Utility.copyAmount(aInputs[0].stackSize - tComparedInput.stackSize, aInputs[0]) }, - tOutputItems, + new ItemStack[] { GT_Utility.copyAmount(1, aInputs[0]) }, + new ItemStack[] { GT_ModHandler.getRecyclerOutput(aInputs[0], 0) }, null, + new int[] { 1250 }, null, - new FluidStack[] { new FluidStack( - aFluids[0].getFluid(), - ((NBTTagCompound) aRecipeMetaData.getTag("return")).getInteger("amount")) }, null, - 400, - 16, - 0) - : null; + 45, + 1, + 0)); } @Override public boolean containsInput(ItemStack aStack) { - return GT_Utility.arrayContainsNonNull( - GT_ModHandler.getMachineOutput( - GT_Utility.copyAmount(64, aStack), - ic2.api.recipe.Recipes.oreWashing.getRecipes(), - false, - new NBTTagCompound(), - null, - null, - null)); - } - - @Override - public boolean containsInput(FluidStack aFluid) { - return GT_ModHandler.isWater(aFluid); - } - - @Override - public boolean containsInput(Fluid aFluid) { - return GT_ModHandler.isWater(new FluidStack(aFluid, 0)); + return GT_ModHandler.getRecyclerOutput(aStack, 0) != null; } } @@ -5544,20 +5327,36 @@ public class GT_Recipe implements Comparable<GT_Recipe> { aNEIAllowed); } + @Nonnull @Override - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { + public FindRecipeResult findRecipeWithResult(GT_Recipe aRecipe, boolean aNotUnificated, + boolean aDontCheckStackSizes, long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, + ItemStack... aInputs) { if (aInputs == null || aInputs.length == 0 || aInputs[0] == null || !GregTech_API.sPostloadFinished) - return super.findRecipe(aTileEntity, aRecipe, aNotUnificated, aVoltage, aFluids, aSpecialSlot, aInputs); - aRecipe = super.findRecipe(aTileEntity, aRecipe, aNotUnificated, aVoltage, aFluids, aSpecialSlot, aInputs); - if (aRecipe != null) return aRecipe; + return super.findRecipeWithResult( + aRecipe, + aNotUnificated, + aDontCheckStackSizes, + aVoltage, + aFluids, + aSpecialSlot, + aInputs); + FindRecipeResult result = super.findRecipeWithResult( + aRecipe, + aNotUnificated, + aDontCheckStackSizes, + aVoltage, + aFluids, + aSpecialSlot, + aInputs); + if (result.isSuccessful()) return result; try { List<ItemStack> tRecipeOutputs = mods.railcraft.api.crafting.RailcraftCraftingManager.rockCrusher .getRecipe(GT_Utility.copyAmount(1, aInputs[0])) .getRandomizedOuputs(); if (tRecipeOutputs != null) { - aRecipe = new GT_Recipe( + GT_Recipe recipe = new GT_Recipe( false, new ItemStack[] { GT_Utility.copyAmount(1, aInputs[0]) }, tRecipeOutputs.toArray(new ItemStack[0]), @@ -5568,9 +5367,9 @@ public class GT_Recipe implements Comparable<GT_Recipe> { 800, 2, 0); - aRecipe.mCanBeBuffered = false; - aRecipe.mNeedsEmptyOutput = true; - return aRecipe; + recipe.mCanBeBuffered = false; + recipe.mNeedsEmptyOutput = true; + return FindRecipeResult.ofSuccess(recipe); } } catch (NoClassDefFoundError e) { if (D1) GT_Log.err.println("Railcraft Not loaded"); @@ -5587,20 +5386,22 @@ public class GT_Recipe implements Comparable<GT_Recipe> { null, null, null); - return GT_Utility.arrayContainsNonNull(tOutputItems) - ? new GT_Recipe( - false, - new ItemStack[] { - GT_Utility.copyAmount(aInputs[0].stackSize - tComparedInput.stackSize, aInputs[0]) }, - tOutputItems, - null, - null, - null, - null, - 400, - 2, - 0) - : null; + if (tComparedInput != null && GT_Utility.arrayContainsNonNull(tOutputItems)) { + return FindRecipeResult.ofSuccess( + new GT_Recipe( + false, + new ItemStack[] { + GT_Utility.copyAmount(aInputs[0].stackSize - tComparedInput.stackSize, aInputs[0]) }, + tOutputItems, + null, + null, + null, + null, + 400, + 2, + 0)); + } + return NOT_FOUND; } @Override @@ -5644,11 +5445,20 @@ public class GT_Recipe implements Comparable<GT_Recipe> { aNEIAllowed); } + @Nonnull @Override - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { + public FindRecipeResult findRecipeWithResult(GT_Recipe aRecipe, boolean aNotUnificated, + boolean aDontCheckStackSizes, long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, + ItemStack... aInputs) { - GT_Recipe rRecipe = super.findRecipe(aTileEntity, aRecipe, true, aVoltage, aFluids, aSpecialSlot, aInputs); + FindRecipeResult result = super.findRecipeWithResult( + aRecipe, + true, + aDontCheckStackSizes, + aVoltage, + aFluids, + aSpecialSlot, + aInputs); /* * Doesnt work, keep it as a reminder tho if (rRecipe == null){ Set<ItemStack> aInputs2 = new * TreeSet<ItemStack>(); for (ItemStack aInput : aInputs) { aInputs2.add(aInput); } for (ItemStack aInput : @@ -5662,9 +5472,10 @@ public class GT_Recipe implements Comparable<GT_Recipe> { */ if (aInputs == null || aInputs.length == 0 || aInputs[0] == null - || rRecipe == null - || !GregTech_API.sPostloadFinished) return rRecipe; + || !result.isSuccessful() + || !GregTech_API.sPostloadFinished) return result; + GT_Recipe rRecipe = result.getRecipeNonNull(); for (ItemStack aInput : aInputs) { if (ItemList.Paper_Printed_Pages.isStackEqual(aInput, false, true)) { rRecipe = rRecipe.copy(); @@ -5672,7 +5483,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> { rRecipe.mOutputs[0].setTagCompound(aInput.getTagCompound()); } } - return rRecipe; + return FindRecipeResult.ofSuccess(rRecipe); } } @@ -5703,12 +5514,12 @@ public class GT_Recipe implements Comparable<GT_Recipe> { aNEIAllowed); } + @Nonnull @Override - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, + public FindRecipeResult findRecipeWithResult(GT_Recipe aRecipe, boolean aNotUnificated, boolean aDontCheckStackSizes, long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { - GT_Recipe rRecipe = super.findRecipe( - aTileEntity, + FindRecipeResult result = super.findRecipeWithResult( aRecipe, aNotUnificated, aDontCheckStackSizes, @@ -5716,8 +5527,10 @@ public class GT_Recipe implements Comparable<GT_Recipe> { aFluids, aSpecialSlot, aInputs); - if (aInputs == null || aInputs.length < 2 || !GregTech_API.sPostloadFinished) return rRecipe; - if (rRecipe == null) return findRenamingRecipe(aInputs); + if (aInputs == null || aInputs.length < 2 || !GregTech_API.sPostloadFinished) return result; + if (!result.isSuccessful()) { + return findRenamingRecipe(aInputs); + } for (ItemStack aMold : aInputs) { if (ItemList.Shape_Mold_Credit.isStackEqual(aMold, false, true)) { NBTTagCompound tNBT = aMold.getTagCompound(); @@ -5725,13 +5538,14 @@ public class GT_Recipe implements Comparable<GT_Recipe> { if (!tNBT.hasKey("credit_security_id")) tNBT.setLong("credit_security_id", System.nanoTime()); aMold.setTagCompound(tNBT); + GT_Recipe rRecipe = result.getRecipeNonNull(); rRecipe = rRecipe.copy(); rRecipe.mCanBeBuffered = false; rRecipe.mOutputs[0].setTagCompound(tNBT); - return rRecipe; + return FindRecipeResult.ofSuccess(rRecipe); } } - return rRecipe; + return result; } private ItemStack findNameMoldIndex(ItemStack[] inputs) { @@ -5749,12 +5563,14 @@ public class GT_Recipe implements Comparable<GT_Recipe> { return null; } - private GT_Recipe findRenamingRecipe(ItemStack[] inputs) { + @Nonnull + private FindRecipeResult findRenamingRecipe(ItemStack[] inputs) { ItemStack mold = findNameMoldIndex(inputs); - if (mold == null) return null; + if (mold == null) return NOT_FOUND; ItemStack input = findStackToRename(inputs, mold); - if (input == null) return null; + if (input == null) return NOT_FOUND; ItemStack output = GT_Utility.copyAmount(1, input); + if (output == null) return NOT_FOUND; output.setStackDisplayName(mold.getDisplayName()); GT_Recipe recipe = new GT_Recipe( false, @@ -5768,7 +5584,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> { 8, 0); recipe.mCanBeBuffered = false; - return recipe; + return FindRecipeResult.ofSuccess(recipe); } } @@ -5799,13 +5615,15 @@ public class GT_Recipe implements Comparable<GT_Recipe> { aNEIAllowed); } + @Nonnull @Override - public GT_Recipe findRecipe(IHasWorldObjectAndCoords aTileEntity, GT_Recipe aRecipe, boolean aNotUnificated, - long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, ItemStack... aInputs) { - GT_Recipe rRecipe = super.findRecipe( - aTileEntity, + public FindRecipeResult findRecipeWithResult(GT_Recipe aRecipe, boolean aNotUnificated, + boolean aDontCheckStackSizes, long aVoltage, FluidStack[] aFluids, ItemStack aSpecialSlot, + ItemStack... aInputs) { + FindRecipeResult result = super.findRecipeWithResult( aRecipe, aNotUnificated, + aDontCheckStackSizes, aVoltage, aFluids, aSpecialSlot, @@ -5815,7 +5633,7 @@ public class GT_Recipe implements Comparable<GT_Recipe> { || aFluids == null || aFluids.length == 0 || aFluids[0] == null - || !GregTech_API.sPostloadFinished) return rRecipe; + || !GregTech_API.sPostloadFinished) return result; Dyes aDye = null; for (Dyes tDye : Dyes.VALUES) if (tDye.isFluidDye(aFluids[0])) { @@ -5823,11 +5641,11 @@ public class GT_Recipe implements Comparable<GT_Recipe> { break; } - if (aDye == null) return rRecipe; + if (aDye == null) return result; - if (rRecipe == null) { + if (!result.isSuccessful()) { ItemStack tOutput = GT_ModHandler.getAllRecipeOutput( - aTileEntity == null ? null : aTileEntity.getWorld(), + null, aInputs[0], aInputs[0], aInputs[0], @@ -5837,67 +5655,72 @@ public class GT_Recipe implements Comparable<GT_Recipe> { aInputs[0], aInputs[0], aInputs[0]); - if (tOutput != null) return addRecipe( - new GT_Recipe( - true, - new ItemStack[] { GT_Utility.copyAmount(8, aInputs[0]) }, - new ItemStack[] { tOutput }, - null, - null, - new FluidStack[] { new FluidStack(aFluids[0].getFluid(), (int) L) }, - null, - 256, - 2, - 0), - false, - false, - true); + if (tOutput != null) { + GT_Recipe recipe = addRecipe( + new GT_Recipe( + true, + new ItemStack[] { GT_Utility.copyAmount(8, aInputs[0]) }, + new ItemStack[] { tOutput }, + null, + null, + new FluidStack[] { new FluidStack(aFluids[0].getFluid(), (int) L) }, + null, + 256, + 2, + 0), + false, + false, + true); + return recipe != null ? FindRecipeResult.ofSuccess(recipe) : NOT_FOUND; + } - tOutput = GT_ModHandler.getAllRecipeOutput( - aTileEntity == null ? null : aTileEntity.getWorld(), - aInputs[0], - ItemList.DYE_ONLY_ITEMS[aDye.mIndex].get(1)); - if (tOutput != null) return addRecipe( - new GT_Recipe( - true, - new ItemStack[] { GT_Utility.copyAmount(1, aInputs[0]) }, - new ItemStack[] { tOutput }, - null, - null, - new FluidStack[] { new FluidStack(aFluids[0].getFluid(), (int) L) }, - null, - 32, - 2, - 0), - false, - false, - true); + tOutput = GT_ModHandler + .getAllRecipeOutput(null, aInputs[0], ItemList.DYE_ONLY_ITEMS[aDye.mIndex].get(1)); + if (tOutput != null) { + GT_Recipe recipe = addRecipe( + new GT_Recipe( + true, + new ItemStack[] { GT_Utility.copyAmount(1, aInputs[0]) }, + new ItemStack[] { tOutput }, + null, + null, + new FluidStack[] { new FluidStack(aFluids[0].getFluid(), (int) L) }, + null, + 32, + 2, + 0), + false, + false, + true); + return recipe != null ? FindRecipeResult.ofSuccess(recipe) : NOT_FOUND; + } } else { + GT_Recipe rRecipe = result.getRecipeNonNull(); if (aInputs[0].getItem() == Items.paper) { - if (!ItemList.Tool_DataStick.isStackEqual(aSpecialSlot, false, true)) return null; + if (!ItemList.Tool_DataStick.isStackEqual(aSpecialSlot, false, true)) return NOT_FOUND; NBTTagCompound tNBT = aSpecialSlot.getTagCompound(); if (tNBT == null || GT_Utility.isStringInvalid(tNBT.getString("title")) - || GT_Utility.isStringInvalid(tNBT.getString("author"))) return null; + || GT_Utility.isStringInvalid(tNBT.getString("author"))) return NOT_FOUND; rRecipe = rRecipe.copy(); rRecipe.mCanBeBuffered = false; rRecipe.mOutputs[0].setTagCompound(tNBT); - return rRecipe; + return FindRecipeResult.ofSuccess(rRecipe); } if (aInputs[0].getItem() == Items.map) { - if (!ItemList.Tool_DataStick.isStackEqual(aSpecialSlot, false, true)) return null; + if (!ItemList.Tool_DataStick.isStackEqual(aSpecialSlot, false, true)) return NOT_FOUND; NBTTagCompound tNBT = aSpecialSlot.getTagCompound(); - if (tNBT == null || !tNBT.hasKey("map_id")) return null; + if (tNBT == null || !tNBT.hasKey("map_id")) return NOT_FOUND; rRecipe = rRecipe.copy(); rRecipe.mCanBeBuffered = false; rRecipe.mOutputs[0].setItemDamage(tNBT.getShort("map_id")); - return rRecipe; + return FindRecipeResult.ofSuccess(rRecipe); } if (ItemList.Paper_Punch_Card_Empty.isStackEqual(aInputs[0], false, true)) { - if (!ItemList.Tool_DataStick.isStackEqual(aSpecialSlot, false, true)) return null; + if (!ItemList.Tool_DataStick.isStackEqual(aSpecialSlot, false, true)) return NOT_FOUND; NBTTagCompound tNBT = aSpecialSlot.getTagCompound(); - if (tNBT == null || !tNBT.hasKey("GT.PunchCardData")) return null; + if (tNBT == null || !tNBT.hasKey("GT.PunchCardData")) return NOT_FOUND; rRecipe = rRecipe.copy(); rRecipe.mCanBeBuffered = false; @@ -5906,10 +5729,10 @@ public class GT_Recipe implements Comparable<GT_Recipe> { new NBTTagCompound(), "GT.PunchCardData", tNBT.getString("GT.PunchCardData"))); - return rRecipe; + return FindRecipeResult.ofSuccess(rRecipe); } } - return rRecipe; + return result; } @Override diff --git a/src/main/java/gregtech/api/util/GT_Single_Recipe_Check.java b/src/main/java/gregtech/api/util/GT_Single_Recipe_Check.java deleted file mode 100644 index f939275e6c..0000000000 --- a/src/main/java/gregtech/api/util/GT_Single_Recipe_Check.java +++ /dev/null @@ -1,421 +0,0 @@ -package gregtech.api.util; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; - -import javax.annotation.Nullable; - -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTBase; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraftforge.common.util.Constants; -import net.minecraftforge.fluids.Fluid; -import net.minecraftforge.fluids.FluidRegistry; -import net.minecraftforge.fluids.FluidStack; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; - -import gregtech.api.enums.GT_Values; -import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase; - -/** Used by machines that are locked to a single recipe, for fast computation. */ -public class GT_Single_Recipe_Check { - - protected final GT_MetaTileEntity_MultiBlockBase multiBlockBase; - protected final GT_Recipe recipe; - protected final ImmutableMap<GT_Utility.ItemId, Integer> itemCost; - protected final ImmutableMap<Fluid, Integer> fluidCost; - - protected final int totalItemCost; - protected final int totalFluidCost; - - protected GT_Single_Recipe_Check(GT_MetaTileEntity_MultiBlockBase multiBlockBase, GT_Recipe recipe, - ImmutableMap<GT_Utility.ItemId, Integer> itemCost, ImmutableMap<Fluid, Integer> fluidCost) { - this.multiBlockBase = multiBlockBase; - this.recipe = recipe; - this.itemCost = itemCost; - this.fluidCost = fluidCost; - - this.totalItemCost = itemCost.values() - .stream() - .mapToInt(Integer::intValue) - .sum(); - this.totalFluidCost = fluidCost.values() - .stream() - .mapToInt(Integer::intValue) - .sum(); - } - - public GT_Recipe getRecipe() { - return recipe; - } - - /** - * Use this method if recipes cannot require more than a single stack of any item or fluid. In particular, - * {@link GT_MetaTileEntity_MultiBlockBase#getCompactedInputs()} and - * {@link GT_MetaTileEntity_MultiBlockBase#getCompactedFluids()} both enforce this single-stack restriction, so any - * multi-block that calls those can use this method. - */ - @SuppressWarnings("JavadocReference") - public boolean checkRecipeInputsSingleStack(boolean consumeInputs) { - Map<GT_Utility.ItemId, ItemStack> itemMap = null; - if (totalItemCost > 0) { - itemMap = new HashMap<>(); - for (ItemStack itemStack : multiBlockBase.getStoredInputs()) { - itemMap.merge( - GT_Utility.ItemId.createNoCopy(itemStack), - itemStack, - (a, b) -> a.stackSize >= b.stackSize ? a : b); - } - - for (Map.Entry<GT_Utility.ItemId, Integer> entry : itemCost.entrySet()) { - ItemStack itemStack = itemMap.get(entry.getKey()); - if (itemStack == null || itemStack.stackSize < entry.getValue()) { - return false; - } - } - } - - Map<Fluid, FluidStack> fluidMap = null; - if (totalFluidCost > 0) { - fluidMap = new HashMap<>(); - for (FluidStack fluidStack : multiBlockBase.getStoredFluids()) { - fluidMap.merge(fluidStack.getFluid(), fluidStack, (a, b) -> a.amount >= b.amount ? a : b); - } - - for (Map.Entry<Fluid, Integer> entry : fluidCost.entrySet()) { - FluidStack fluidStack = fluidMap.get(entry.getKey()); - if (fluidStack == null || fluidStack.amount < entry.getValue()) { - return false; - } - } - } - - if (consumeInputs) { - if (totalItemCost > 0) { - for (Map.Entry<GT_Utility.ItemId, Integer> entry : itemCost.entrySet()) { - itemMap.get(entry.getKey()).stackSize -= entry.getValue(); - } - } - - if (totalFluidCost > 0) { - for (Map.Entry<Fluid, Integer> entry : fluidCost.entrySet()) { - fluidMap.get(entry.getKey()).amount -= entry.getValue(); - } - } - } - - return true; - } - - /** - * Use this method if recipes can require more than a single stack of any item or fluid. It is less efficient, - * though. - */ - public boolean checkRecipeInputs(boolean consumeInputs) { - List<ItemStack> items = null; - if (totalItemCost > 0) { - items = multiBlockBase.getStoredInputs(); - - Map<GT_Utility.ItemId, Integer> itemMap = new HashMap<>(); - for (ItemStack itemStack : items) { - itemMap.merge(GT_Utility.ItemId.createNoCopy(itemStack), itemStack.stackSize, Integer::sum); - } - - for (Map.Entry<GT_Utility.ItemId, Integer> entry : itemCost.entrySet()) { - if (itemMap.getOrDefault(entry.getKey(), 0) < entry.getValue()) { - return false; - } - } - } - - List<FluidStack> fluids = null; - if (totalFluidCost > 0) { - fluids = multiBlockBase.getStoredFluids(); - - Map<Fluid, Integer> fluidMap = new HashMap<>(); - for (FluidStack fluidStack : fluids) { - fluidMap.merge(fluidStack.getFluid(), fluidStack.amount, Integer::sum); - } - - for (Map.Entry<Fluid, Integer> entry : fluidCost.entrySet()) { - if (fluidMap.getOrDefault(entry.getKey(), 0) < entry.getValue()) { - return false; - } - } - } - - if (consumeInputs) { - if (totalItemCost > 0) { - int remainingItemCost = totalItemCost; - Map<GT_Utility.ItemId, Integer> runningItemCost = Maps.newHashMap(itemCost); - for (ItemStack itemStack : items) { - GT_Utility.ItemId key = GT_Utility.ItemId.createNoCopy(itemStack); - int runningCost = runningItemCost.getOrDefault(key, 0); - int paid = Math.min(itemStack.stackSize, runningCost); - itemStack.stackSize -= paid; - runningItemCost.put(key, runningCost - paid); - - remainingItemCost -= paid; - if (remainingItemCost <= 0) { - break; - } - } - } - - if (totalFluidCost > 0) { - int remainingFluidCost = totalFluidCost; - Map<Fluid, Integer> runningFluidCost = Maps.newHashMap(fluidCost); - for (FluidStack fluidStack : fluids) { - Fluid key = fluidStack.getFluid(); - int runningCost = runningFluidCost.getOrDefault(key, 0); - int paid = Math.min(fluidStack.amount, runningCost); - fluidStack.amount -= paid; - runningFluidCost.put(key, runningCost - paid); - - remainingFluidCost -= paid; - if (remainingFluidCost <= 0) { - break; - } - } - } - } - - return true; - } - - public NBTTagCompound writeToNBT() { - // here we encode recipe input, output and all other important values - // at load time we do a recipe check again, so in case the recipe is gone, we can stop tracking - // of course the next step would be auto migrating to new recipe (if any), but given - // we don't yet have a mean to uniquely name a recipe, this will have to make do. - // consider move serialization code to GT_Recipe once this has been proven to work - NBTTagCompound tag = new NBTTagCompound(); - if (recipe == null) return tag; - - if (recipe.mInputs != null) { - tag.setTag("inputs", writeList(recipe.mInputs, GT_Utility::saveItem)); - } - if (recipe.mOutputs != null) { - tag.setTag("outputs", writeList(recipe.mOutputs, GT_Utility::saveItem)); - } - if (recipe.mChances != null) { - tag.setIntArray("chances", recipe.mChances); - } - if (recipe.mFluidInputs != null) { - tag.setTag( - "fInputs", - writeList( - recipe.mFluidInputs, - s -> s == null ? new NBTTagCompound() : s.writeToNBT(new NBTTagCompound()))); - } - if (recipe.mFluidOutputs != null) { - tag.setTag( - "fOutputs", - writeList( - recipe.mFluidOutputs, - s -> s == null ? new NBTTagCompound() : s.writeToNBT(new NBTTagCompound()))); - } - tag.setInteger("eut", recipe.mEUt); - tag.setInteger("duration", recipe.mDuration); - tag.setInteger("specialValue", recipe.mSpecialValue); - if (itemCost != null) { - tag.setTag("itemCost", writeList(itemCost.entrySet(), e -> { - NBTTagCompound ret = new NBTTagCompound(); - ret.setTag( - "id", - e.getKey() - .writeToNBT()); - ret.setInteger("count", e.getValue()); - return ret; - })); - } - if (fluidCost != null) { - tag.setTag("fluidCost", writeList(fluidCost.entrySet(), e -> { - NBTTagCompound ret = new NBTTagCompound(); - ret.setString( - "id", - e.getKey() - .getName()); - ret.setInteger("count", e.getValue()); - return ret; - })); - } - return tag; - } - - private static <T, NBT extends NBTBase> NBTTagList writeList(T[] arr, Function<T, NBT> ser) { - return writeList(Arrays.asList(arr), ser); - } - - private static <T, NBT extends NBTBase> NBTTagList writeList(Collection<T> arr, Function<T, NBT> ser) { - NBTTagList l = new NBTTagList(); - for (T t : arr) { - l.appendTag(ser.apply(t)); - } - return l; - } - - @Nullable - public static GT_Single_Recipe_Check tryLoad(GT_MetaTileEntity_MultiBlockBase parent, NBTTagCompound tag) { - return tryLoad(parent, parent.getRecipeMap(), tag); - } - - @Nullable - - public static GT_Single_Recipe_Check tryLoad(GT_MetaTileEntity_MultiBlockBase parent, - GT_Recipe.GT_Recipe_Map recipeMap, NBTTagCompound tag) { - GT_Recipe found = tryFindRecipe(parent, recipeMap, tag); - if (found == null) return null; - return new GT_Single_Recipe_Check(parent, found, loadItemCost(tag), loadFluidCost(tag)); - } - - protected static ImmutableMap<Fluid, Integer> loadFluidCost(NBTTagCompound tag) { - return GT_Utility.streamCompounds(tag.getTagList("fluidCost", Constants.NBT.TAG_COMPOUND)) - .collect( - GT_Utility - .toImmutableMapSerial(t -> FluidRegistry.getFluid(t.getString("id")), t -> t.getInteger("count"))); - } - - protected static ImmutableMap<GT_Utility.ItemId, Integer> loadItemCost(NBTTagCompound tag) { - return GT_Utility.streamCompounds(tag.getTagList("itemCost", Constants.NBT.TAG_COMPOUND)) - .collect( - GT_Utility.toImmutableMapSerial( - t -> GT_Utility.ItemId.create(t.getCompoundTag("id")), - t -> t.getInteger("count"))); - } - - protected static GT_Recipe tryFindRecipe(GT_MetaTileEntity_MultiBlockBase parent, GT_Recipe.GT_Recipe_Map recipeMap, - NBTTagCompound tag) { - if (tag == null || tag.hasNoTags()) return null; - ItemStack[] inputs = GT_Utility.streamCompounds(tag.getTagList("inputs", Constants.NBT.TAG_COMPOUND)) - .map(GT_Utility::loadItem) - .toArray(ItemStack[]::new); - ItemStack[] outputs = GT_Utility.streamCompounds(tag.getTagList("outputs", Constants.NBT.TAG_COMPOUND)) - .map(GT_Utility::loadItem) - .toArray(ItemStack[]::new); - FluidStack[] fInputs = GT_Utility.streamCompounds(tag.getTagList("fInputs", Constants.NBT.TAG_COMPOUND)) - .map(FluidStack::loadFluidStackFromNBT) - .toArray(FluidStack[]::new); - FluidStack[] fOutputs = GT_Utility.streamCompounds(tag.getTagList("fOutputs", Constants.NBT.TAG_COMPOUND)) - .map(FluidStack::loadFluidStackFromNBT) - .toArray(FluidStack[]::new); - int eut = tag.getInteger("eut"); - GT_Recipe found = recipeMap - .findRecipe(parent.getBaseMetaTileEntity(), false, GT_Values.V[GT_Utility.getTier(eut)], fInputs, inputs); - int[] chances = tag.getIntArray("chances"); - if (chances.length == 0) chances = null; - if (found == null || !GT_Utility.equals(inputs, found.mInputs) - || !Arrays.equals(fInputs, found.mFluidInputs) - || !GT_Utility.equals(outputs, found.mOutputs) - || !Arrays.equals(fOutputs, found.mFluidOutputs) - || !Arrays.equals(chances, found.mChances) - || found.mDuration != tag.getInteger("duration") - || found.mEUt != eut - || found.mSpecialValue != tag.getInteger("specialValue")) return null; - return found; - } - - protected static Map<GT_Utility.ItemId, Integer> buildItemMap(GT_MetaTileEntity_MultiBlockBase multiBlockBase) { - Map<GT_Utility.ItemId, Integer> itemMap = new HashMap<>(); - for (ItemStack itemStack : multiBlockBase.getStoredInputs()) { - itemMap.merge(GT_Utility.ItemId.create(itemStack), itemStack.stackSize, Integer::sum); - } - return itemMap; - } - - protected static Map<Fluid, Integer> buildFluidMap(GT_MetaTileEntity_MultiBlockBase multiBlockBase) { - Map<Fluid, Integer> fluidMap = new HashMap<>(); - for (FluidStack fluidStack : multiBlockBase.getStoredFluids()) { - fluidMap.merge(fluidStack.getFluid(), fluidStack.amount, Integer::sum); - } - return fluidMap; - } - - public static Builder builder(GT_MetaTileEntity_MultiBlockBase multiBlockBase) { - return new Builder(multiBlockBase); - } - - public static final class Builder { - - private final GT_MetaTileEntity_MultiBlockBase multiBlockBase; - - // In order to compute which items and fluids are consumed by the recipe, we compare the - // multi-block's items and fluids before and after inputs are consumed by the recipe. - private Map<GT_Utility.ItemId, Integer> beforeItems; - private Map<Fluid, Integer> beforeFluids; - private Map<GT_Utility.ItemId, Integer> afterItems; - private Map<Fluid, Integer> afterFluids; - - private GT_Recipe recipe; - - private Builder(GT_MetaTileEntity_MultiBlockBase multiBlockBase) { - this.multiBlockBase = multiBlockBase; - } - - /** Call this before inputs are consumed by the recipe. */ - public Builder setBefore(ItemStack[] inputs, FluidStack[] fluids) { - beforeItems = buildItemMapDirect(inputs); - beforeFluids = buildFluidMapDirect(fluids); - return this; - } - - /** Call this after inputs are consumed by the recipe. */ - public Builder setAfter(ItemStack[] inputs, FluidStack[] fluids) { - afterItems = buildItemMapDirect(inputs); - afterFluids = buildFluidMapDirect(fluids); - return this; - } - - public Builder setRecipe(GT_Recipe recipe) { - this.recipe = recipe; - return this; - } - - static Map<GT_Utility.ItemId, Integer> buildItemMapDirect(ItemStack[] inputs) { - Map<GT_Utility.ItemId, Integer> itemMap = new HashMap<>(); - for (ItemStack itemStack : inputs) { - itemMap.merge(GT_Utility.ItemId.create(itemStack), itemStack.stackSize, Integer::sum); - } - return itemMap; - } - - static Map<Fluid, Integer> buildFluidMapDirect(FluidStack[] fluids) { - Map<Fluid, Integer> fluidMap = new HashMap<>(); - for (FluidStack fluidStack : fluids) { - fluidMap.merge(fluidStack.getFluid(), fluidStack.amount, Integer::sum); - } - return fluidMap; - } - - public GT_Single_Recipe_Check build() { - ImmutableMap.Builder<GT_Utility.ItemId, Integer> itemCostBuilder = ImmutableMap.builder(); - for (Map.Entry<GT_Utility.ItemId, Integer> entry : beforeItems.entrySet()) { - int cost = entry.getValue() - afterItems.getOrDefault(entry.getKey(), 0); - if (cost > 0) { - itemCostBuilder.put(entry.getKey(), cost); - } - } - - ImmutableMap.Builder<Fluid, Integer> fluidCostBuilder = ImmutableMap.builder(); - for (Map.Entry<Fluid, Integer> entry : beforeFluids.entrySet()) { - int cost = entry.getValue() - afterFluids.getOrDefault(entry.getKey(), 0); - if (cost > 0) { - fluidCostBuilder.put(entry.getKey(), cost); - } - } - - return new GT_Single_Recipe_Check( - multiBlockBase, - recipe, - itemCostBuilder.build(), - fluidCostBuilder.build()); - } - } -} diff --git a/src/main/java/gregtech/api/util/GT_Single_Recipe_Check_Processing_Array.java b/src/main/java/gregtech/api/util/GT_Single_Recipe_Check_Processing_Array.java deleted file mode 100644 index ad0ca76a6d..0000000000 --- a/src/main/java/gregtech/api/util/GT_Single_Recipe_Check_Processing_Array.java +++ /dev/null @@ -1,244 +0,0 @@ -package gregtech.api.util; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import javax.annotation.Nullable; - -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraftforge.fluids.Fluid; -import net.minecraftforge.fluids.FluidStack; - -import com.google.common.collect.ImmutableMap; - -import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase; - -/** Processing Array-specialized version of {@link gregtech.api.util.GT_Single_Recipe_Check}. */ -public class GT_Single_Recipe_Check_Processing_Array extends GT_Single_Recipe_Check { - - protected final int recipeAmperage; - - /** - * The machine type that the locked recipe is for. We will not allow running with other machines. - */ - protected final ItemStack machineStack; - - protected GT_Single_Recipe_Check_Processing_Array(GT_MetaTileEntity_MultiBlockBase multiBlockBase, GT_Recipe recipe, - ImmutableMap<GT_Utility.ItemId, Integer> itemCost, ImmutableMap<Fluid, Integer> fluidCost, int recipeAmperage, - ItemStack machineStack) { - super(multiBlockBase, recipe, itemCost, fluidCost); - - this.recipeAmperage = recipeAmperage; - this.machineStack = machineStack; - } - - public int getRecipeAmperage() { - return recipeAmperage; - } - - @Override - public boolean checkRecipeInputsSingleStack(boolean consumeInputs) { - throw new UnsupportedOperationException("Use checkRecipeInputs(boolean, int) instead."); - } - - @Override - public boolean checkRecipeInputs(boolean consumeInputs) { - throw new UnsupportedOperationException("Use checkRecipeInputs(boolean, int) instead."); - } - - /** Returns the number of parallel recipes, or 0 if recipe is not satisfied at all. */ - public int checkRecipeInputs(boolean consumeInputs, int maxParallel) { - if (!GT_Utility.areStacksEqual(machineStack, multiBlockBase.mInventory[1])) { - // Machine stack was modified. This is not allowed, so stop processing. - return 0; - } - int parallel = maxParallel; - - List<ItemStack> items = null; - if (totalItemCost > 0) { - items = multiBlockBase.getStoredInputs(); - - Map<GT_Utility.ItemId, Integer> itemMap = new HashMap<>(); - for (ItemStack itemStack : items) { - itemMap.merge(GT_Utility.ItemId.createNoCopy(itemStack), itemStack.stackSize, Integer::sum); - } - - for (Map.Entry<GT_Utility.ItemId, Integer> entry : itemCost.entrySet()) { - parallel = Math.min(parallel, itemMap.getOrDefault(entry.getKey(), 0) / entry.getValue()); - if (parallel <= 0) { - return 0; - } - } - } - - List<FluidStack> fluids = null; - if (totalFluidCost > 0) { - fluids = multiBlockBase.getStoredFluids(); - - Map<Fluid, Integer> fluidMap = new HashMap<>(); - for (FluidStack fluidStack : fluids) { - fluidMap.merge(fluidStack.getFluid(), fluidStack.amount, Integer::sum); - } - - for (Map.Entry<Fluid, Integer> entry : fluidCost.entrySet()) { - parallel = Math.min(parallel, fluidMap.getOrDefault(entry.getKey(), 0) / entry.getValue()); - if (parallel <= 0) { - return 0; - } - } - } - - final int finalParallel = parallel; - if (consumeInputs) { - if (totalItemCost > 0) { - int remainingItemCost = totalItemCost * finalParallel; - Map<GT_Utility.ItemId, Integer> runningItemCost = itemCost.entrySet() - .stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue() * finalParallel)); - - for (ItemStack itemStack : items) { - GT_Utility.ItemId key = GT_Utility.ItemId.createNoCopy(itemStack); - int runningCost = runningItemCost.getOrDefault(key, 0); - int paid = Math.min(itemStack.stackSize, runningCost); - itemStack.stackSize -= paid; - runningItemCost.put(key, runningCost - paid); - - remainingItemCost -= paid; - if (remainingItemCost <= 0) { - break; - } - } - } - - if (totalFluidCost > 0) { - int remainingFluidCost = totalFluidCost * finalParallel; - Map<Fluid, Integer> runningFluidCost = fluidCost.entrySet() - .stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue() * finalParallel)); - - for (FluidStack fluidStack : fluids) { - Fluid key = fluidStack.getFluid(); - int runningCost = runningFluidCost.getOrDefault(key, 0); - int paid = Math.min(fluidStack.amount, runningCost); - fluidStack.amount -= paid; - runningFluidCost.put(key, runningCost - paid); - - remainingFluidCost -= paid; - if (remainingFluidCost <= 0) { - break; - } - } - } - } - - return finalParallel; - } - - @Nullable - - public static GT_Single_Recipe_Check tryLoad(GT_MetaTileEntity_MultiBlockBase parent, - GT_Recipe.GT_Recipe_Map recipeMap, NBTTagCompound tag, ItemStack machineStack) { - if (recipeMap == null || machineStack == null) return null; - GT_Recipe found = tryFindRecipe(parent, recipeMap, tag); - if (found == null) return null; - return new GT_Single_Recipe_Check_Processing_Array( - parent, - found, - loadItemCost(tag), - loadFluidCost(tag), - recipeMap.mAmperage, - machineStack.copy()); - } - - public static Builder processingArrayBuilder(GT_MetaTileEntity_MultiBlockBase multiBlockBase) { - return new Builder(multiBlockBase); - } - - public static final class Builder { - - private final GT_MetaTileEntity_MultiBlockBase multiBlockBase; - - // In order to compute which items and fluids are consumed by the recipe, we compare the - // multi-block's items and fluids before and after inputs are consumed by the recipe. - private Map<GT_Utility.ItemId, Integer> beforeItems; - private Map<Fluid, Integer> beforeFluids; - private Map<GT_Utility.ItemId, Integer> afterItems; - private Map<Fluid, Integer> afterFluids; - - private GT_Recipe recipe; - private int recipeAmperage; - - private Builder(GT_MetaTileEntity_MultiBlockBase multiBlockBase) { - this.multiBlockBase = multiBlockBase; - } - - /** Call this before inputs are consumed by the recipe. */ - public Builder setBefore(ItemStack[] inputs, FluidStack[] fluids) { - beforeItems = buildItemMapDirect(inputs); - beforeFluids = buildFluidMapDirect(fluids); - return this; - } - - static Map<GT_Utility.ItemId, Integer> buildItemMapDirect(ItemStack[] inputs) { - Map<GT_Utility.ItemId, Integer> itemMap = new HashMap<>(); - for (ItemStack itemStack : inputs) { - itemMap.merge(GT_Utility.ItemId.create(itemStack), itemStack.stackSize, Integer::sum); - } - return itemMap; - } - - static Map<Fluid, Integer> buildFluidMapDirect(FluidStack[] fluids) { - Map<Fluid, Integer> fluidMap = new HashMap<>(); - for (FluidStack fluidStack : fluids) { - fluidMap.merge(fluidStack.getFluid(), fluidStack.amount, Integer::sum); - } - return fluidMap; - } - - /** Call this after inputs are consumed by the recipe. */ - public Builder setAfter(ItemStack[] inputs, FluidStack[] fluids) { - afterItems = buildItemMapDirect(inputs); - afterFluids = buildFluidMapDirect(fluids); - return this; - } - - public Builder setRecipe(GT_Recipe recipe) { - this.recipe = recipe; - return this; - } - - public Builder setRecipeAmperage(int recipeAmperage) { - this.recipeAmperage = recipeAmperage; - return this; - } - - public GT_Single_Recipe_Check_Processing_Array build() { - ImmutableMap.Builder<GT_Utility.ItemId, Integer> itemCostBuilder = ImmutableMap.builder(); - for (Map.Entry<GT_Utility.ItemId, Integer> entry : beforeItems.entrySet()) { - int cost = entry.getValue() - afterItems.getOrDefault(entry.getKey(), 0); - if (cost > 0) { - itemCostBuilder.put(entry.getKey(), cost); - } - } - - ImmutableMap.Builder<Fluid, Integer> fluidCostBuilder = ImmutableMap.builder(); - for (Map.Entry<Fluid, Integer> entry : beforeFluids.entrySet()) { - int cost = entry.getValue() - afterFluids.getOrDefault(entry.getKey(), 0); - if (cost > 0) { - fluidCostBuilder.put(entry.getKey(), cost); - } - } - - return new GT_Single_Recipe_Check_Processing_Array( - multiBlockBase, - recipe, - itemCostBuilder.build(), - fluidCostBuilder.build(), - recipeAmperage, - multiBlockBase.mInventory[1].copy()); - } - } -} diff --git a/src/main/java/gregtech/api/util/GT_Utility.java b/src/main/java/gregtech/api/util/GT_Utility.java index d891545122..108b9dc2d9 100644 --- a/src/main/java/gregtech/api/util/GT_Utility.java +++ b/src/main/java/gregtech/api/util/GT_Utility.java @@ -493,6 +493,16 @@ public class GT_Utility { return ceilDiv(voltage, GT_Values.V[tier]); } + /** + * Rounds down partial voltage that exceeds tiered voltage, e.g. 4096 -> 2048 (EV) + */ + public static long roundDownVoltage(long voltage) { + if (voltage > V[V.length - 1]) { + return voltage; + } + return V[GT_Utility.getTier(voltage)]; + } + public static String getColoredTierNameFromVoltage(long voltage) { return getColoredTierNameFromTier(getTier(voltage)); } @@ -501,6 +511,9 @@ public class GT_Utility { return GT_Values.TIER_COLORS[tier] + GT_Values.VN[tier] + EnumChatFormatting.RESET; } + /** + * @return e.g. {@code " (LV)"} + */ @Nonnull public static String getTierNameWithParentheses(long voltage) { byte tier = getTier(voltage); @@ -1713,6 +1726,10 @@ public class GT_Utility { || Items.feather.getDamage(aStack2) == W); } + public static boolean areStacksEqualOrNull(ItemStack stack1, ItemStack stack2) { + return (stack1 == null && stack2 == null) || GT_Utility.areStacksEqual(stack1, stack2); + } + /** * Treat both null list, or both null item stack at same list position as equal. * <p> diff --git a/src/main/java/gregtech/api/util/VoidProtectionHelper.java b/src/main/java/gregtech/api/util/VoidProtectionHelper.java index 94c2dfcaaf..3ca9621f06 100644 --- a/src/main/java/gregtech/api/util/VoidProtectionHelper.java +++ b/src/main/java/gregtech/api/util/VoidProtectionHelper.java @@ -243,6 +243,8 @@ public class VoidProtectionHelper { Map<ItemStack, ParallelData> tParallels = new ItemStackMap<>(); int tSlotsFree = 0; for (ItemStack tItem : itemOutputs) { + // GT_RecipeBuilder doesn't handle null item output + if (tItem == null) continue; tItemOutputMap.merge(tItem, tItem.stackSize, Integer::sum); tParallels.put(tItem, new ParallelData(0, 0)); } |