aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api
diff options
context:
space:
mode:
authorMaxim <maxim235@gmx.de>2023-07-10 10:13:04 +0200
committerGitHub <noreply@github.com>2023-07-10 10:13:04 +0200
commitfe0387946550f89a403b09f4e8cf6e43ee8f2e8f (patch)
tree6b56a553c2a5bbf2427a93e92724452ba5bc60f7 /src/main/java/gregtech/api
parent9fe44bf0eaa55825c1c3cdf90f69aeeb668f45b1 (diff)
downloadGT5-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')
-rw-r--r--src/main/java/gregtech/api/enums/HeatingCoilLevel.java31
-rw-r--r--src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntity.java3
-rw-r--r--src/main/java/gregtech/api/interfaces/modularui/ControllerWithOptionalFeatures.java19
-rw-r--r--src/main/java/gregtech/api/interfaces/tileentity/IHasWorldObjectAndCoords.java5
-rw-r--r--src/main/java/gregtech/api/interfaces/tileentity/IRecipeLockable.java34
-rw-r--r--src/main/java/gregtech/api/logic/ProcessingLogic.java308
-rw-r--r--src/main/java/gregtech/api/metatileentity/BaseMetaTileEntity.java12
-rw-r--r--src/main/java/gregtech/api/metatileentity/MetaTileEntity.java6
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine.java16
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_ExtendedPowerMultiBlockBase.java69
-rw-r--r--src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java236
-rw-r--r--src/main/java/gregtech/api/multitileentity/machine/MultiTileBasicMachine.java7
-rw-r--r--src/main/java/gregtech/api/multitileentity/multiblock/base/Controller.java16
-rw-r--r--src/main/java/gregtech/api/recipe/check/CheckRecipeResult.java42
-rw-r--r--src/main/java/gregtech/api/recipe/check/CheckRecipeResultRegistry.java111
-rw-r--r--src/main/java/gregtech/api/recipe/check/FindRecipeResult.java107
-rw-r--r--src/main/java/gregtech/api/recipe/check/ResultInsufficientHeat.java57
-rw-r--r--src/main/java/gregtech/api/recipe/check/ResultInsufficientMachineTier.java54
-rw-r--r--src/main/java/gregtech/api/recipe/check/ResultInsufficientPower.java56
-rw-r--r--src/main/java/gregtech/api/recipe/check/SimpleCheckRecipeResult.java78
-rw-r--r--src/main/java/gregtech/api/recipe/check/SingleRecipeCheck.java404
-rw-r--r--src/main/java/gregtech/api/util/GT_OverclockCalculator.java13
-rw-r--r--src/main/java/gregtech/api/util/GT_ParallelHelper.java137
-rw-r--r--src/main/java/gregtech/api/util/GT_ProcessingArray_Manager.java9
-rw-r--r--src/main/java/gregtech/api/util/GT_Recipe.java893
-rw-r--r--src/main/java/gregtech/api/util/GT_Single_Recipe_Check.java421
-rw-r--r--src/main/java/gregtech/api/util/GT_Single_Recipe_Check_Processing_Array.java244
-rw-r--r--src/main/java/gregtech/api/util/GT_Utility.java17
-rw-r--r--src/main/java/gregtech/api/util/VoidProtectionHelper.java2
29 files changed, 2094 insertions, 1313 deletions
diff --git a/src/main/java/gregtech/api/enums/HeatingCoilLevel.java b/src/main/java/gregtech/api/enums/HeatingCoilLevel.java
index f80f8139c6..f281695d5a 100644
--- a/src/main/java/gregtech/api/enums/HeatingCoilLevel.java
+++ b/src/main/java/gregtech/api/enums/HeatingCoilLevel.java
@@ -1,5 +1,7 @@
package gregtech.api.enums;
+import javax.annotation.Nonnull;
+
import net.minecraft.util.StatCollector;
public enum HeatingCoilLevel {
@@ -32,7 +34,7 @@ public enum HeatingCoilLevel {
}
/**
- * @return the coil tier, used for discount in the Pyrolyse Oven for example.
+ * @return the coil tier, used for discount in the Pyrolyse Oven for example. LV == 0
*/
public byte getTier() {
return (byte) (this.ordinal() - 2);
@@ -52,16 +54,43 @@ public enum HeatingCoilLevel {
return 1 << Math.max(0, this.ordinal() - 5);
}
+ /**
+ * @return Translated name of this coil
+ */
public String getName() {
return StatCollector.translateToLocal("GT5U.coil." + this);
}
+ @Nonnull
public static HeatingCoilLevel getFromTier(byte tier) {
if (tier < 0 || tier > getMaxTier()) return HeatingCoilLevel.None;
return VALUES[tier + 2];
}
+ /**
+ * @param applyColor Whether to apply tiered color
+ * @return Translated coil name. Heat exceeding MAX is represented as "Eternal+".
+ */
+ @Nonnull
+ public static String getDisplayNameFromHeat(int heat, boolean applyColor) {
+ for (HeatingCoilLevel heatLevel : VALUES) {
+ if (heatLevel == HeatingCoilLevel.None || heatLevel == HeatingCoilLevel.ULV) continue;
+ if (heatLevel.getHeat() >= heat) {
+ String name = heatLevel.getName();
+ if (applyColor) {
+ name = GT_Values.TIER_COLORS[heatLevel.getTier() + 1] + name;
+ }
+ return name;
+ }
+ }
+ String name = HeatingCoilLevel.MAX.getName() + "+";
+ if (applyColor) {
+ name = GT_Values.TIER_COLORS[HeatingCoilLevel.MAX.getTier() + 1] + name;
+ }
+ return name;
+ }
+
public static int size() {
return VALUES.length;
}
diff --git a/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntity.java b/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntity.java
index 9a60092121..7b29f185c6 100644
--- a/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntity.java
+++ b/src/main/java/gregtech/api/interfaces/metatileentity/IMetaTileEntity.java
@@ -298,8 +298,7 @@ public interface IMetaTileEntity extends ISidedInventory, IFluidTank, IFluidHand
void receiveClientEvent(byte aEventID, byte aValue);
/**
- * Called to actually play the Sound. Do not insert Client/Server checks. That is already done for you. Do not
- * use @playSoundEffect, Minecraft doesn't like that at all. Use @playSound instead.
+ * Called to actually play the sound on client side. Client/Server check is already done.
*/
void doSound(byte aIndex, double aX, double aY, double aZ);
diff --git a/src/main/java/gregtech/api/interfaces/modularui/ControllerWithOptionalFeatures.java b/src/main/java/gregtech/api/interfaces/modularui/ControllerWithOptionalFeatures.java
index 9f862e254d..3d4ed80f67 100644
--- a/src/main/java/gregtech/api/interfaces/modularui/ControllerWithOptionalFeatures.java
+++ b/src/main/java/gregtech/api/interfaces/modularui/ControllerWithOptionalFeatures.java
@@ -20,6 +20,7 @@ import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
import gregtech.api.enums.SoundResource;
import gregtech.api.enums.VoidingMode;
import gregtech.api.gui.modularui.GT_UITextures;
+import gregtech.api.interfaces.tileentity.IRecipeLockable;
import gregtech.api.interfaces.tileentity.IVoidable;
/**
@@ -33,7 +34,7 @@ import gregtech.api.interfaces.tileentity.IVoidable;
* <li>Recipe locking</li>
* </ul>
*/
-public interface ControllerWithOptionalFeatures extends IVoidable {
+public interface ControllerWithOptionalFeatures extends IVoidable, IRecipeLockable {
boolean isAllowedToWork();
@@ -233,22 +234,6 @@ public interface ControllerWithOptionalFeatures extends IVoidable {
return (ButtonWidget) button;
}
- /**
- * Override this if you are a multi-block that has added support for single recipe locking.
- */
- boolean supportsSingleRecipeLocking();
-
- /**
- * @return true if recipe locking is enabled, else false. This is getter is used for displaying the icon in the GUI
- */
- boolean isRecipeLockingEnabled();
-
- void setRecipeLocking(boolean enabled);
-
- default boolean getDefaultRecipeLockingMode() {
- return false;
- }
-
Pos2d getRecipeLockingButtonPos();
default ButtonWidget createLockToSingleRecipeButton(IWidgetBuilder<?> builder) {
diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IHasWorldObjectAndCoords.java b/src/main/java/gregtech/api/interfaces/tileentity/IHasWorldObjectAndCoords.java
index 64d206aff7..6d81d5c401 100644
--- a/src/main/java/gregtech/api/interfaces/tileentity/IHasWorldObjectAndCoords.java
+++ b/src/main/java/gregtech/api/interfaces/tileentity/IHasWorldObjectAndCoords.java
@@ -136,8 +136,9 @@ public interface IHasWorldObjectAndCoords {
boolean isDead();
/**
- * Sends a Block Event to the Client TileEntity, the byte Parameters are only for validation as Minecraft doesn't
- * properly write Packet Data.
+ * Sends a Block Event to the Client TileEntity.
+ *
+ * @param aValue value to sync
*/
void sendBlockEvent(byte aID, byte aValue);
diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IRecipeLockable.java b/src/main/java/gregtech/api/interfaces/tileentity/IRecipeLockable.java
new file mode 100644
index 0000000000..f793221a50
--- /dev/null
+++ b/src/main/java/gregtech/api/interfaces/tileentity/IRecipeLockable.java
@@ -0,0 +1,34 @@
+package gregtech.api.interfaces.tileentity;
+
+import gregtech.api.recipe.check.SingleRecipeCheck;
+import gregtech.api.util.GT_Recipe;
+
+/**
+ * Machines implementing this interface can have logic to lock to a single recipe.
+ */
+public interface IRecipeLockable {
+
+ /**
+ * @return if this machine supports single recipe locking.
+ */
+ boolean supportsSingleRecipeLocking();
+
+ /**
+ * @return true if recipe locking is enabled, else false. This is getter is used for displaying the icon in the GUI
+ */
+ boolean isRecipeLockingEnabled();
+
+ void setRecipeLocking(boolean enabled);
+
+ default boolean getDefaultRecipeLockingMode() {
+ return false;
+ }
+
+ default SingleRecipeCheck getSingleRecipeCheck() {
+ return null;
+ }
+
+ default void setSingleRecipeCheck(SingleRecipeCheck recipeCheck) {}
+
+ GT_Recipe.GT_Recipe_Map getRecipeMap();
+}
diff --git a/src/main/java/gregtech/api/logic/ProcessingLogic.java b/src/main/java/gregtech/api/logic/ProcessingLogic.java
index fa0d285401..db97ac151f 100644
--- a/src/main/java/gregtech/api/logic/ProcessingLogic.java
+++ b/src/main/java/gregtech/api/logic/ProcessingLogic.java
@@ -1,39 +1,89 @@
package gregtech.api.logic;
+import java.util.List;
+import java.util.function.Supplier;
+
+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.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.check.FindRecipeResult;
+import gregtech.api.recipe.check.SingleRecipeCheck;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_ParallelHelper;
+import gregtech.api.util.GT_Recipe;
import gregtech.api.util.GT_Recipe.GT_Recipe_Map;
-public abstract class ProcessingLogic {
+/**
+ * Logic class to calculate result of recipe check from inputs, based on recipemap.
+ */
+@SuppressWarnings({ "unused", "UnusedReturnValue" })
+public class ProcessingLogic {
- protected GT_Recipe_Map recipeMap;
+ protected IVoidable machine;
+ protected IRecipeLockable recipeLockableMachine;
+ protected Supplier<GT_Recipe_Map> recipeMapSupplier;
+ protected GT_Recipe lastRecipe;
protected ItemStack[] inputItems;
protected ItemStack[] outputItems;
protected ItemStack[] currentOutputItems;
protected FluidStack[] inputFluids;
protected FluidStack[] outputFluids;
protected FluidStack[] currentOutputFluids;
- protected long eut;
- protected long duration;
+ protected long calculatedEut;
+ protected int duration;
+ protected long availableVoltage;
+ protected long availableAmperage;
+ protected int overClockTimeReduction = 1;
+ protected int overClockPowerIncrease = 2;
+ protected boolean protectItems;
+ protected boolean protectFluids;
+ protected boolean isRecipeLocked;
+ protected int maxParallel = 1;
+ protected int calculatedParallels = 0;
+ protected Supplier<Integer> maxParallelSupplier;
+ protected int batchSize = 1;
public ProcessingLogic() {}
+ // region Setters
+
public ProcessingLogic setInputItems(ItemStack... itemInputs) {
this.inputItems = itemInputs;
return this;
}
+ public ProcessingLogic setInputItems(List<ItemStack> itemOutputs) {
+ this.inputItems = itemOutputs.toArray(new ItemStack[0]);
+ return this;
+ }
+
public ProcessingLogic setInputFluids(FluidStack... fluidInputs) {
this.inputFluids = fluidInputs;
return this;
}
+ public ProcessingLogic setInputFluids(List<FluidStack> fluidInputs) {
+ this.inputFluids = fluidInputs.toArray(new FluidStack[0]);
+ return this;
+ }
+
+ /**
+ * Overwrites item output result of the calculation.
+ */
public ProcessingLogic setOutputItems(ItemStack... itemOutputs) {
this.outputItems = itemOutputs;
return this;
}
+ /**
+ * Overwrites fluid output result of the calculation.
+ */
public ProcessingLogic setOutputFluids(FluidStack... fluidOutputs) {
this.outputFluids = fluidOutputs;
return this;
@@ -49,35 +99,263 @@ public abstract class ProcessingLogic {
return this;
}
+ /**
+ * Enables single recipe locking mode.
+ */
+ public ProcessingLogic setRecipeLocking(IRecipeLockable recipeLockableMachine, boolean isRecipeLocked) {
+ this.recipeLockableMachine = recipeLockableMachine;
+ this.isRecipeLocked = isRecipeLocked;
+ return this;
+ }
+
+ /**
+ * Sets max amount of parallel.
+ */
+ public ProcessingLogic setMaxParallel(int maxParallel) {
+ this.maxParallel = maxParallel;
+ return this;
+ }
+
+ /**
+ * Sets method to get max amount of parallel.
+ */
+ public ProcessingLogic setMaxParallelSupplier(Supplier<Integer> supplier) {
+ this.maxParallelSupplier = supplier;
+ return this;
+ }
+
+ /**
+ * Sets batch size for batch mode.
+ */
+ public ProcessingLogic setBatchSize(int size) {
+ this.batchSize = size;
+ return this;
+ }
+
public ProcessingLogic setRecipeMap(GT_Recipe_Map recipeMap) {
- this.recipeMap = recipeMap;
+ return setRecipeMapSupplier(() -> recipeMap);
+ }
+
+ public ProcessingLogic setRecipeMapSupplier(Supplier<GT_Recipe_Map> supplier) {
+ this.recipeMapSupplier = supplier;
+ return this;
+ }
+
+ /**
+ * Sets machine used for void protection logic.
+ */
+ public ProcessingLogic setMachine(IVoidable machine) {
+ this.machine = machine;
return this;
}
- public ProcessingLogic setDuration(long duration) {
+ /**
+ * Overwrites duration result of the calculation.
+ */
+ public ProcessingLogic setDuration(int duration) {
this.duration = duration;
return this;
}
- public ProcessingLogic setEut(long eut) {
- this.eut = eut;
+ /**
+ * Overwrites EU/t result of the calculation.
+ */
+ public ProcessingLogic setCalculatedEut(long calculatedEut) {
+ this.calculatedEut = calculatedEut;
+ return this;
+ }
+
+ /**
+ * Sets voltage of the machine. It doesn't need to be actual voltage (excluding amperage) of the machine;
+ * For example, most of the multiblock machines set maximum possible input power (including amperage) as voltage
+ * and 1 as amperage. That way recipemap search will be executed with overclocked voltage.
+ */
+ public ProcessingLogic setAvailableVoltage(long voltage) {
+ availableVoltage = voltage;
+ return this;
+ }
+
+ /**
+ * Sets amperage of the machine. This amperage doesn't involve in EU/t when searching recipemap.
+ * Useful for preventing tier skip but still considering amperage for parallel.
+ */
+ public ProcessingLogic setAvailableAmperage(long amperage) {
+ availableAmperage = amperage;
return this;
}
+ public ProcessingLogic setVoidProtection(boolean protectItems, boolean protectFluids) {
+ this.protectItems = protectItems;
+ this.protectFluids = protectFluids;
+ return this;
+ }
+
+ /**
+ * Sets custom overclock ratio. 2/4 by default.
+ * Parameters represent number of bit shift, so 1 -> 2x, 2 -> 4x.
+ */
+ public ProcessingLogic setOverclock(int timeReduction, int powerIncrease) {
+ this.overClockTimeReduction = timeReduction;
+ this.overClockPowerIncrease = powerIncrease;
+ return this;
+ }
+
+ /**
+ * Sets overclock ratio to 4/4.
+ */
+ public ProcessingLogic enablePerfectOverclock() {
+ return this.setOverclock(2, 2);
+ }
+
/**
- * Clears everything stored in the Processing Logic other than the Recipe map used
+ * Clears calculated results and provided machine inputs to prepare for the next machine operation.
*/
public ProcessingLogic clear() {
this.inputItems = null;
this.inputFluids = null;
this.outputItems = null;
this.outputFluids = null;
- this.eut = 0;
+ this.calculatedEut = 0;
this.duration = 0;
+ this.calculatedParallels = 0;
return this;
}
- public abstract boolean process();
+ // endregion
+
+ // region Logic
+
+ /**
+ * Executes the recipe check: Find recipe from recipemap, Calculate parallel, overclock and outputs.
+ */
+ @Nonnull
+ public CheckRecipeResult process() {
+ if (recipeMapSupplier == null) return CheckRecipeResultRegistry.NO_RECIPE;
+
+ GT_Recipe_Map recipeMap = recipeMapSupplier.get();
+ if (recipeMap == null) return CheckRecipeResultRegistry.NO_RECIPE;
+
+ if (maxParallelSupplier != null) {
+ maxParallel = maxParallelSupplier.get();
+ }
+
+ FindRecipeResult findRecipeResult;
+ if (isRecipeLocked && recipeLockableMachine != null && recipeLockableMachine.getSingleRecipeCheck() != null) {
+ // Recipe checker is already built, we'll use it
+ SingleRecipeCheck singleRecipeCheck = recipeLockableMachine.getSingleRecipeCheck();
+ // Validate recipe here, otherwise machine will show "not enough output space"
+ // even if recipe cannot be found
+ if (singleRecipeCheck.checkRecipeInputs(false, 1, inputItems, inputFluids) == 0) {
+ return CheckRecipeResultRegistry.NO_RECIPE;
+ }
+ findRecipeResult = FindRecipeResult.ofSuccess(
+ recipeLockableMachine.getSingleRecipeCheck()
+ .getRecipe());
+ } else {
+ findRecipeResult = recipeMap
+ .findRecipeWithResult(lastRecipe, false, false, availableVoltage, inputFluids, null, inputItems);
+ }
+
+ GT_Recipe recipe;
+ if (findRecipeResult.isSuccessful()) {
+ recipe = findRecipeResult.getRecipeNonNull();
+ CheckRecipeResult result = validateRecipe(recipe);
+ if (!result.wasSuccessful()) {
+ return result;
+ } else {
+ lastRecipe = recipe;
+ }
+ } else {
+ if (findRecipeResult.getState() == FindRecipeResult.State.INSUFFICIENT_VOLTAGE) {
+ return CheckRecipeResultRegistry.insufficientPower(findRecipeResult.getRecipeNonNull().mEUt);
+ } else {
+ return CheckRecipeResultRegistry.NO_RECIPE;
+ }
+ }
+
+ GT_ParallelHelper helper = createParallelHelper(recipe);
+
+ helper.build();
+
+ if (helper.getCurrentParallel() <= 0) return CheckRecipeResultRegistry.OUTPUT_FULL;
+
+ calculatedParallels = helper.getCurrentParallel();
+
+ GT_OverclockCalculator calculator = createOverclockCalculator(recipe, helper);
+
+ calculator.calculate();
+ if (calculator.getConsumption() == Long.MAX_VALUE) {
+ return CheckRecipeResultRegistry.POWER_OVERFLOW;
+ }
+ if (calculator.getDuration() == Integer.MAX_VALUE) {
+ return CheckRecipeResultRegistry.DURATION_OVERFLOW;
+ }
+
+ calculatedEut = calculator.getConsumption();
+
+ double finalDuration = calculateDuration(recipe, helper, calculator);
+ if (finalDuration >= Integer.MAX_VALUE) {
+ return CheckRecipeResultRegistry.DURATION_OVERFLOW;
+ }
+ duration = (int) finalDuration;
+
+ outputItems = helper.getItemOutputs();
+ outputFluids = helper.getFluidOutputs();
+
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ /**
+ * Override to tweak final duration that will be set as a result of this logic class.
+ */
+ protected double calculateDuration(@Nonnull GT_Recipe recipe, @Nonnull GT_ParallelHelper helper,
+ @Nonnull GT_OverclockCalculator calculator) {
+ return calculator.getDuration() * helper.getDurationMultiplierDouble();
+ }
+
+ /**
+ * Override to tweak parallel logic if needed.
+ */
+ @Nonnull
+ protected GT_ParallelHelper createParallelHelper(@Nonnull GT_Recipe recipe) {
+ return new GT_ParallelHelper().setRecipe(recipe)
+ .setItemInputs(inputItems)
+ .setFluidInputs(inputFluids)
+ .setAvailableEUt(availableVoltage * availableAmperage)
+ .setMachine(machine, protectItems, protectFluids)
+ .setRecipeLocked(recipeLockableMachine, isRecipeLocked)
+ .setMaxParallel(maxParallel)
+ .enableBatchMode(batchSize)
+ .enableConsumption()
+ .enableOutputCalculation();
+ }
+
+ /**
+ * Override to do additional check for finding recipe if needed, mainly for special value of the recipe.
+ */
+ @Nonnull
+ protected CheckRecipeResult validateRecipe(@Nonnull GT_Recipe recipe) {
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ /**
+ * Override to tweak overclock logic if needed.
+ */
+ @Nonnull
+ protected GT_OverclockCalculator createOverclockCalculator(@Nonnull GT_Recipe recipe,
+ @Nonnull GT_ParallelHelper helper) {
+ return new GT_OverclockCalculator().setRecipeEUt(recipe.mEUt)
+ .setParallel((int) Math.floor(helper.getCurrentParallel() / helper.getDurationMultiplierDouble()))
+ .setDuration(recipe.mDuration)
+ .setAmperage(availableAmperage)
+ .setEUt(availableVoltage)
+ .setDurationDecreasePerOC(overClockTimeReduction)
+ .setEUtIncreasePerOC(overClockPowerIncrease);
+ }
+
+ // endregion
+
+ // region Getters
public ItemStack[] getOutputItems() {
return outputItems;
@@ -87,11 +365,13 @@ public abstract class ProcessingLogic {
return outputFluids;
}
- public long getDuration() {
+ public int getDuration() {
return duration;
}
- public long getEut() {
- return eut;
+ public long getCalculatedEut() {
+ return calculatedEut;
}
+
+ // endregion
}
diff --git a/src/main/java/gregtech/api/metatileentity/BaseMetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/BaseMetaTileEntity.java
index a23fbd8a85..72ca9f9124 100644
--- a/src/main/java/gregtech/api/metatileentity/BaseMetaTileEntity.java
+++ b/src/main/java/gregtech/api/metatileentity/BaseMetaTileEntity.java
@@ -595,6 +595,12 @@ public class BaseMetaTileEntity extends CommonMetaTileEntity
} catch (Throwable e) {
e.printStackTrace();
e.printStackTrace(GT_Log.err);
+ try {
+ mMetaTileEntity.onTickFail(this, mTickTimer);
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ ex.printStackTrace(GT_Log.err);
+ }
}
if (aSideServer && hasValidMetaTileEntity()) {
@@ -1020,6 +1026,9 @@ public class BaseMetaTileEntity extends CommonMetaTileEntity
@Override
public void disableWorking() {
mWorks = false;
+ if (hasValidMetaTileEntity()) {
+ mMetaTileEntity.onDisableWorking();
+ }
}
@Override
@@ -1060,6 +1069,9 @@ public class BaseMetaTileEntity extends CommonMetaTileEntity
@Override
public void setActive(boolean aActive) {
mActive = aActive;
+ if (hasValidMetaTileEntity()) {
+ mMetaTileEntity.onSetActive(aActive);
+ }
}
@Override
diff --git a/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java
index 453849923d..1ac42a2ea1 100644
--- a/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java
+++ b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java
@@ -319,6 +319,12 @@ public abstract class MetaTileEntity implements IMetaTileEntity, ICleanroomRecei
}
}
+ public void onTickFail(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {}
+
+ public void onSetActive(boolean active) {}
+
+ public void onDisableWorking() {}
+
@Override
public void inValidate() {
/* Do nothing */
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine.java
index f251b5c02f..ac037b7919 100644
--- a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine.java
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_BasicMachine.java
@@ -69,6 +69,7 @@ import gregtech.api.interfaces.modularui.IAddGregtechLogo;
import gregtech.api.interfaces.modularui.IAddUIWidgets;
import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.recipe.check.FindRecipeResult;
import gregtech.api.render.TextureFactory;
import gregtech.api.util.GT_ClientPreference;
import gregtech.api.util.GT_CoverBehaviorBase;
@@ -1085,15 +1086,24 @@ public abstract class GT_MetaTileEntity_BasicMachine extends GT_MetaTileEntity_B
public int checkRecipe(boolean skipOC) {
GT_Recipe_Map tMap = getRecipeList();
if (tMap == null) return DID_NOT_FIND_RECIPE;
- GT_Recipe tRecipe = tMap.findRecipe(
- getBaseMetaTileEntity(),
+ FindRecipeResult result = tMap.findRecipeWithResult(
mLastRecipe,
false,
+ false,
V[mTier],
new FluidStack[] { getFillableStack() },
getSpecialSlot(),
getAllInputs());
- if (tRecipe == null) return DID_NOT_FIND_RECIPE;
+ if (result.getState() == FindRecipeResult.State.EXPLODE && getBaseMetaTileEntity() != null) {
+ getBaseMetaTileEntity().doExplosion(V[mTier] * 4);
+ return DID_NOT_FIND_RECIPE;
+ }
+ if (result.getState() == FindRecipeResult.State.ON_FIRE && getBaseMetaTileEntity() != null) {
+ getBaseMetaTileEntity().setOnFire();
+ return DID_NOT_FIND_RECIPE;
+ }
+ if (!result.isSuccessful()) return DID_NOT_FIND_RECIPE;
+ GT_Recipe tRecipe = result.getRecipeNonNull();
if (GT_Mod.gregtechproxy.mLowGravProcessing && (tRecipe.mSpecialValue == -100 || tRecipe.mSpecialValue == -300)
&& !isValidForLowGravity(tRecipe, getBaseMetaTileEntity().getWorld().provider.dimensionId))
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_ExtendedPowerMultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_ExtendedPowerMultiBlockBase.java
index 47dd69ba6c..ef3f587c01 100644
--- a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_ExtendedPowerMultiBlockBase.java
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_ExtendedPowerMultiBlockBase.java
@@ -5,6 +5,8 @@ import static gregtech.api.enums.GT_Values.VN;
import java.util.ArrayList;
import java.util.List;
+import javax.annotation.Nonnull;
+
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
@@ -14,6 +16,9 @@ import net.minecraft.util.StatCollector;
import net.minecraft.world.World;
import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
import gregtech.api.util.GT_ExoticEnergyInputHelper;
import gregtech.api.util.GT_Utility;
@@ -127,6 +132,70 @@ public abstract class GT_MetaTileEntity_ExtendedPowerMultiBlockBase<T extends GT
}
@Override
+ public @Nonnull CheckRecipeResult checkProcessing() {
+ // If no logic is found, try legacy checkRecipe
+ if (processingLogic == null) {
+ // noinspection deprecation
+ return checkRecipe(mInventory[1]) ? CheckRecipeResultRegistry.SUCCESSFUL
+ : CheckRecipeResultRegistry.NO_RECIPE;
+ }
+
+ CheckRecipeResult result = CheckRecipeResultRegistry.NO_RECIPE;
+
+ processingLogic.clear();
+ processingLogic.setMachine(this);
+ processingLogic.setRecipeMapSupplier(this::getRecipeMap);
+ processingLogic.setVoidProtection(protectsExcessItem(), protectsExcessFluid());
+ processingLogic.setBatchSize(isBatchModeEnabled() ? getMaxBatchSize() : 1);
+ processingLogic.setRecipeLocking(this, isRecipeLockingEnabled());
+ processingLogic.setInputFluids(getStoredFluids());
+ setProcessingLogicPower(processingLogic);
+ if (isInputSeparationEnabled()) {
+ for (GT_MetaTileEntity_Hatch_InputBus bus : mInputBusses) {
+ List<ItemStack> inputItems = new ArrayList<>();
+ for (int i = bus.getSizeInventory() - 1; i >= 0; i--) {
+ ItemStack stored = bus.getStackInSlot(i);
+ if (stored != null) {
+ inputItems.add(stored);
+ }
+ }
+ processingLogic.setInputItems(inputItems.toArray(new ItemStack[0]));
+ result = processingLogic.process();
+ if (result.wasSuccessful()) break;
+ }
+ } else {
+ processingLogic.setInputItems(getStoredInputs());
+ result = processingLogic.process();
+ }
+
+ // inputs are consumed by `process()`
+ updateSlots();
+
+ if (!result.wasSuccessful()) return result;
+
+ mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000);
+ mEfficiencyIncrease = 10000;
+
+ lEUt = processingLogic.getCalculatedEut();
+ mMaxProgresstime = processingLogic.getDuration();
+
+ if (lEUt > 0) {
+ lEUt = (-lEUt);
+ }
+
+ mOutputItems = processingLogic.getOutputItems();
+ mOutputFluids = processingLogic.getOutputFluids();
+
+ return result;
+ }
+
+ @Override
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(GT_Utility.roundDownVoltage(getAverageInputVoltage()));
+ logic.setAvailableAmperage(getMaxInputAmps());
+ }
+
+ @Override
public String[] getInfoData() {
int mPollutionReduction = 0;
for (GT_MetaTileEntity_Hatch_Muffler tHatch : mMufflerHatches) {
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java
index 1c93779f01..0d383bc768 100644
--- a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java
+++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java
@@ -30,10 +30,12 @@ import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.FluidStack;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.TestOnly;
import org.lwjgl.input.Keyboard;
import com.google.common.collect.Iterables;
+import com.gtnewhorizons.modularui.api.math.Alignment;
import com.gtnewhorizons.modularui.api.math.Pos2d;
import com.gtnewhorizons.modularui.api.screen.ModularWindow;
import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
@@ -60,26 +62,28 @@ import gregtech.api.interfaces.modularui.IAddUIWidgets;
import gregtech.api.interfaces.modularui.IBindPlayerInventoryUI;
import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
import gregtech.api.items.GT_MetaGenerated_Tool;
+import gregtech.api.logic.ProcessingLogic;
import gregtech.api.metatileentity.MetaTileEntity;
import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.check.SingleRecipeCheck;
import gregtech.api.util.GT_ExoticEnergyInputHelper;
import gregtech.api.util.GT_Log;
-import gregtech.api.util.GT_ModHandler;
import gregtech.api.util.GT_ParallelHelper;
import gregtech.api.util.GT_Recipe;
import gregtech.api.util.GT_Recipe.GT_Recipe_Map;
-import gregtech.api.util.GT_Single_Recipe_Check;
import gregtech.api.util.GT_Utility;
import gregtech.api.util.GT_Waila;
import gregtech.api.util.OutputHatchWrapper;
import gregtech.api.util.VoidProtectionHelper;
import gregtech.client.GT_SoundLoop;
import gregtech.common.GT_Pollution;
+import gregtech.common.gui.modularui.widget.CheckRecipeResultSyncer;
import gregtech.common.items.GT_MetaGenerated_Tool_01;
import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_InputBus_ME;
import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_OutputBus_ME;
import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_Output_ME;
-import gregtech.common.tileentities.machines.multi.GT_MetaTileEntity_DrillerBase;
import gregtech.common.tileentities.machines.multi.GT_MetaTileEntity_LargeTurbine;
import mcp.mobius.waila.api.IWailaConfigHandler;
import mcp.mobius.waila.api.IWailaDataAccessor;
@@ -105,11 +109,14 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
protected boolean inputSeparation = getDefaultInputSeparationMode();
protected VoidingMode voidingMode = getDefaultVoidingMode();
protected boolean batchMode = getDefaultBatchMode();
+ private @Nonnull CheckRecipeResult checkRecipeResult = CheckRecipeResultRegistry.NONE;
+ private boolean isScheduledForResetCheckRecipeResult;
+
protected static final String INPUT_SEPARATION_NBT_KEY = "inputSeparation";
protected static final String VOID_EXCESS_NBT_KEY = "voidExcess";
protected static final String VOIDING_MODE_NBT_KEY = "voidingMode";
protected static final String BATCH_MODE_NBT_KEY = "batchMode";
- public GT_Single_Recipe_Check mSingleRecipeCheck = null;
+ protected SingleRecipeCheck mSingleRecipeCheck = null;
public ArrayList<GT_MetaTileEntity_Hatch_Input> mInputHatches = new ArrayList<>();
public ArrayList<GT_MetaTileEntity_Hatch_Output> mOutputHatches = new ArrayList<>();
@@ -120,6 +127,7 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
public ArrayList<GT_MetaTileEntity_Hatch_Energy> mEnergyHatches = new ArrayList<>();
public ArrayList<GT_MetaTileEntity_Hatch_Maintenance> mMaintenanceHatches = new ArrayList<>();
protected List<GT_MetaTileEntity_Hatch> mExoticEnergyHatches = new ArrayList<>();
+ protected final ProcessingLogic processingLogic;
@SideOnly(Side.CLIENT)
protected GT_SoundLoop activitySoundLoop;
@@ -130,6 +138,7 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
public GT_MetaTileEntity_MultiBlockBase(int aID, String aName, String aNameRegional) {
super(aID, aName, aNameRegional, 2);
+ this.processingLogic = null;
GT_MetaTileEntity_MultiBlockBase.disableMaintenance = GregTech_API.sMachineFile
.get(ConfigCategories.machineconfig, "MultiBlockMachines.disableMaintenance", false);
this.damageFactorLow = GregTech_API.sMachineFile
@@ -141,6 +150,7 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
public GT_MetaTileEntity_MultiBlockBase(String aName) {
super(aName, 2);
+ this.processingLogic = createProcessingLogic();
GT_MetaTileEntity_MultiBlockBase.disableMaintenance = GregTech_API.sMachineFile
.get(ConfigCategories.machineconfig, "MultiBlockMachines.disableMaintenance", false);
this.damageFactorLow = GregTech_API.sMachineFile
@@ -273,7 +283,7 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
if (supportsSingleRecipeLocking()) {
mLockedToSingleRecipe = aNBT.getBoolean("mLockedToSingleRecipe");
if (mLockedToSingleRecipe && aNBT.hasKey("mSingleRecipeCheck", Constants.NBT.TAG_COMPOUND)) {
- GT_Single_Recipe_Check c = loadSingleRecipeChecker(aNBT.getCompoundTag("mSingleRecipeCheck"));
+ SingleRecipeCheck c = loadSingleRecipeChecker(aNBT.getCompoundTag("mSingleRecipeCheck"));
if (c != null) mSingleRecipeCheck = c;
// the old recipe is gone. we disable the machine to prevent making garbage in case of shared inputs
// maybe use a better way to inform player in the future.
@@ -311,8 +321,8 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
mCrowbar = aNBT.getBoolean("mCrowbar");
}
- protected GT_Single_Recipe_Check loadSingleRecipeChecker(NBTTagCompound aNBT) {
- return GT_Single_Recipe_Check.tryLoad(this, aNBT);
+ protected SingleRecipeCheck loadSingleRecipeChecker(NBTTagCompound aNBT) {
+ return SingleRecipeCheck.tryLoad(getRecipeMap(), aNBT);
}
@Override
@@ -411,6 +421,17 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
}
}
+ @Override
+ public void onTickFail(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
+ super.onTickFail(aBaseMetaTileEntity, aTick);
+ if (aBaseMetaTileEntity.isServerSide()) {
+ aBaseMetaTileEntity.disableWorking();
+ checkRecipeResult = CheckRecipeResultRegistry.CRASH;
+ // Don't let `onSetActive` to overwrite
+ isScheduledForResetCheckRecipeResult = false;
+ }
+ }
+
private void checkMaintenance() {
if (disableMaintenance) {
mWrench = true;
@@ -444,14 +465,26 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
}
}
- protected boolean checkRecipe() {
+ /**
+ * Starts checking recipe with some operations needed to actually run the check. Overriding this without due care
+ * may result in dupe of items, hence it's marked as final.
+ * <p>
+ * See {@link #createProcessingLogic()} or {@link #checkProcessing()} for what you want to override.
+ *
+ * @return If successfully found recipe and/or started processing
+ */
+ protected final boolean checkRecipe() {
startRecipeProcessing();
- boolean result = checkRecipe(mInventory[1]);
- if (result && getProcessStartSound() != null) {
- sendLoopStart(PROCESS_START_SOUND_INDEX);
+ CheckRecipeResult result = checkProcessing();
+ if (!CheckRecipeResultRegistry.isRegistered(result.getID())) {
+ throw new RuntimeException(String.format("Result %s is not registered for registry", result.getID()));
+ }
+ if (result.wasSuccessful()) {
+ sendStartMultiBlockSoundLoop();
}
+ this.checkRecipeResult = result;
endRecipeProcessing();
- return result;
+ return result.wasSuccessful();
}
private boolean shouldCheckRecipeThisTick(long aTick) {
@@ -544,6 +577,12 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
return mPollution < 10000;
}
+ protected void sendStartMultiBlockSoundLoop() {
+ if (getProcessStartSound() != null) {
+ sendLoopStart(PROCESS_START_SOUND_INDEX);
+ }
+ }
+
@Override
public void doSound(byte aIndex, double aX, double aY, double aZ) {
super.doSound(aIndex, aX, aY, aZ);
@@ -631,9 +670,86 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
public abstract boolean isCorrectMachinePart(ItemStack aStack);
/**
- * Checks the Recipe
+ * @deprecated Use {@link #createProcessingLogic()} or {@link #checkProcessing()}
+ */
+ @Deprecated
+ public boolean checkRecipe(ItemStack aStack) {
+ return false;
+ }
+
+ /**
+ * Checks recipe and setup machine if it's successful.
+ * <p>
+ * For generic machine working with recipemap, use {@link #createProcessingLogic()} to make use of shared codebase.
*/
- public abstract boolean checkRecipe(ItemStack aStack);
+ @Nonnull
+ public CheckRecipeResult checkProcessing() {
+ // If no logic is found, try legacy checkRecipe
+ if (processingLogic == null) {
+ return checkRecipe(mInventory[1]) ? CheckRecipeResultRegistry.SUCCESSFUL
+ : CheckRecipeResultRegistry.NO_RECIPE;
+ }
+
+ CheckRecipeResult result = CheckRecipeResultRegistry.NO_RECIPE;
+
+ processingLogic.clear();
+ processingLogic.setMachine(this);
+ processingLogic.setRecipeMapSupplier(this::getRecipeMap);
+ processingLogic.setVoidProtection(protectsExcessItem(), protectsExcessFluid());
+ processingLogic.setBatchSize(isBatchModeEnabled() ? getMaxBatchSize() : 1);
+ processingLogic.setRecipeLocking(this, isRecipeLockingEnabled());
+ processingLogic.setInputFluids(getStoredFluids());
+ setProcessingLogicPower(processingLogic);
+ if (isInputSeparationEnabled()) {
+ for (GT_MetaTileEntity_Hatch_InputBus bus : mInputBusses) {
+ List<ItemStack> inputItems = new ArrayList<>();
+ for (int i = bus.getSizeInventory() - 1; i >= 0; i--) {
+ ItemStack stored = bus.getStackInSlot(i);
+ if (stored != null) {
+ inputItems.add(stored);
+ }
+ }
+ processingLogic.setInputItems(inputItems.toArray(new ItemStack[0]));
+ result = processingLogic.process();
+ if (result.wasSuccessful()) break;
+ }
+ } else {
+ processingLogic.setInputItems(getStoredInputs());
+ result = processingLogic.process();
+ }
+
+ // inputs are consumed by `process()`
+ updateSlots();
+
+ if (!result.wasSuccessful()) return result;
+
+ mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000);
+ mEfficiencyIncrease = 10000;
+
+ if (processingLogic.getCalculatedEut() > Integer.MAX_VALUE) {
+ return CheckRecipeResultRegistry.POWER_OVERFLOW;
+ }
+ mEUt = (int) processingLogic.getCalculatedEut();
+ mMaxProgresstime = processingLogic.getDuration();
+
+ if (mEUt > 0) {
+ mEUt = (-mEUt);
+ }
+
+ mOutputItems = processingLogic.getOutputItems();
+ mOutputFluids = processingLogic.getOutputFluids();
+
+ return result;
+ }
+
+ protected void setProcessingLogicPower(ProcessingLogic logic) {
+ logic.setAvailableVoltage(GT_Utility.roundDownVoltage(getMaxInputVoltage()));
+ logic.setAvailableAmperage(1);
+ }
+
+ protected int getMaxBatchSize() {
+ return 128;
+ }
/**
* Checks the Machine. You have to assign the MetaTileEntities for the Hatches here.
@@ -678,6 +794,7 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
mMaxProgresstime = 0;
mEfficiencyIncrease = 0;
getBaseMetaTileEntity().disableWorking();
+ checkRecipeResult = CheckRecipeResultRegistry.NONE;
}
public void criticalStopMachine() {
@@ -839,6 +956,9 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
return injected > 0;
}
+ /**
+ * Sums up voltage of energy hatches. Amperage does not matter.
+ */
public long getMaxInputVoltage() {
long rVoltage = 0;
for (GT_MetaTileEntity_Hatch_Energy tHatch : mEnergyHatches)
@@ -847,6 +967,21 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
return rVoltage;
}
+ /**
+ * Sums up max input EU/t of energy hatches, amperage included.
+ */
+ public long getMaxInputPower() {
+ long eut = 0;
+ for (GT_MetaTileEntity_Hatch_Energy tHatch : mEnergyHatches) if (isValidMetaTileEntity(tHatch)) {
+ IGregTechTileEntity baseTile = tHatch.getBaseMetaTileEntity();
+ eut += baseTile.getInputVoltage() * baseTile.getInputAmperage();
+ }
+ return eut;
+ }
+
+ /**
+ * Returns voltage tier of energy hatches. If multiple tiers are found, returns 0.
+ */
public long getInputVoltageTier() {
long rTier = 0;
if (mEnergyHatches.size() > 0) {
@@ -1130,10 +1265,21 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
return rList;
}
+ @Override
public GT_Recipe_Map getRecipeMap() {
return null;
}
+ /**
+ * Creates logic to run recipe check based on recipemap. This runs only once, on class instantiation.
+ * <p>
+ * If this machine doesn't use recipemap or does some complex things, override {@link #checkProcessing()}.
+ */
+ @ApiStatus.OverrideOnly
+ protected ProcessingLogic createProcessingLogic() {
+ return null;
+ }
+
public void updateSlots() {
for (GT_MetaTileEntity_Hatch_Input tHatch : mInputHatches)
if (isValidMetaTileEntity(tHatch)) tHatch.updateSlots();
@@ -1523,6 +1669,20 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
}
}
+ @Override
+ public void onSetActive(boolean active) {
+ if (isScheduledForResetCheckRecipeResult && !active) {
+ checkRecipeResult = CheckRecipeResultRegistry.NONE;
+ isScheduledForResetCheckRecipeResult = false;
+ }
+ }
+
+ @Override
+ public void onDisableWorking() {
+ // This prevents deleting result instantly when turning off machine
+ isScheduledForResetCheckRecipeResult = true;
+ }
+
protected void setMufflers(boolean state) {
for (GT_MetaTileEntity_Hatch_Muffler aMuffler : mMufflerHatches) {
final IGregTechTileEntity iGTTileEntity = aMuffler.getBaseMetaTileEntity();
@@ -1614,17 +1774,25 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
@Override
public boolean isAllowedToWork() {
- return getBaseMetaTileEntity().isAllowedToWork();
+ return getBaseMetaTileEntity() != null && getBaseMetaTileEntity().isAllowedToWork();
}
@Override
public void disableWorking() {
- getBaseMetaTileEntity().disableWorking();
+ if (getBaseMetaTileEntity() != null) {
+ getBaseMetaTileEntity().disableWorking();
+ }
}
@Override
public void enableWorking() {
- getBaseMetaTileEntity().enableWorking();
+ if (getBaseMetaTileEntity() != null) {
+ getBaseMetaTileEntity().enableWorking();
+ }
+ }
+
+ public ItemStack getControllerSlot() {
+ return mInventory[1];
}
@Override
@@ -1764,7 +1932,20 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
@Override
public void setRecipeLocking(boolean enabled) {
- this.mLockedToSingleRecipe = enabled;
+ mLockedToSingleRecipe = enabled;
+ if (!enabled) {
+ setSingleRecipeCheck(null);
+ }
+ }
+
+ @Override
+ public void setSingleRecipeCheck(SingleRecipeCheck recipeCheck) {
+ mSingleRecipeCheck = recipeCheck;
+ }
+
+ @Override
+ public SingleRecipeCheck getSingleRecipeCheck() {
+ return mSingleRecipeCheck;
}
@Override
@@ -1889,22 +2070,17 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity
new TextWidget(GT_Utility.trans("142", "Running perfectly.")).setDefaultColor(COLOR_TEXT_WHITE.get())
.setEnabled(
widget -> getBaseMetaTileEntity().getErrorDisplayID() == 0 && getBaseMetaTileEntity().isActive()));
-
screenElements.widget(
- new TextWidget(GT_Utility.trans("143", "Missing Mining Pipe")).setDefaultColor(COLOR_TEXT_WHITE.get())
- .setEnabled(widget -> {
- if (getBaseMetaTileEntity().getErrorDisplayID() == 0
- && this instanceof GT_MetaTileEntity_DrillerBase) {
- final ItemStack tItem = inventorySlot.getMcSlot()
- .getStack();
- return tItem == null
- || !GT_Utility.areStacksEqual(tItem, GT_ModHandler.getIC2Item("miningPipe", 1L));
- }
- return false;
- }));
+ TextWidget.dynamicString(() -> checkRecipeResult.getDisplayString())
+ .setSynced(false)
+ .setTextAlignment(Alignment.CenterLeft)
+ .setEnabled(widget -> GT_Utility.isStringValid(checkRecipeResult.getDisplayString())))
+ .widget(new CheckRecipeResultSyncer(() -> checkRecipeResult, (result) -> checkRecipeResult = result));
+
screenElements.widget(
new TextWidget(GT_Utility.trans("144", "Missing Turbine Rotor")).setDefaultColor(COLOR_TEXT_WHITE.get())
.setEnabled(widget -> {
+ if (getBaseMetaTileEntity().isAllowedToWork()) return false;
if (getBaseMetaTileEntity().getErrorDisplayID() == 0
&& this instanceof GT_MetaTileEntity_LargeTurbine) {
final ItemStack tItem = inventorySlot.getMcSlot()
diff --git a/src/main/java/gregtech/api/multitileentity/machine/MultiTileBasicMachine.java b/src/main/java/gregtech/api/multitileentity/machine/MultiTileBasicMachine.java
index 61adafce13..b8ac727a91 100644
--- a/src/main/java/gregtech/api/multitileentity/machine/MultiTileBasicMachine.java
+++ b/src/main/java/gregtech/api/multitileentity/machine/MultiTileBasicMachine.java
@@ -42,6 +42,7 @@ import gregtech.api.multitileentity.MultiTileEntityRegistry;
import gregtech.api.multitileentity.base.TickableMultiTileEntity;
import gregtech.api.multitileentity.interfaces.IMultiTileMachine;
import gregtech.api.net.GT_Packet_MultiTileEntity;
+import gregtech.api.recipe.check.CheckRecipeResult;
import gregtech.api.render.TextureFactory;
import gregtech.api.util.GT_Utility;
import gregtech.client.GT_SoundLoop;
@@ -539,17 +540,17 @@ public abstract class MultiTileBasicMachine extends TickableMultiTileEntity impl
}
ProcessingLogic logic = ((ProcessingLogicHost) this).getProcessingLogic();
logic.clear();
- boolean result = logic.setInputItems(getInputItems())
+ CheckRecipeResult result = logic.setInputItems(getInputItems())
.setInputFluids(getInputFluids())
.setCurrentOutputItems(
outputInventory.getStacks()
.toArray(new ItemStack[0]))
.process();
setDuration(logic.getDuration());
- setEut(logic.getEut());
+ setEut(logic.getCalculatedEut());
setItemOutputs(logic.getOutputItems());
setFluidOutputs(logic.getOutputFluids());
- return result;
+ return result.wasSuccessful();
}
/**
diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/Controller.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/Controller.java
index b96a822e4b..cdefa21e71 100644
--- a/src/main/java/gregtech/api/multitileentity/multiblock/base/Controller.java
+++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/Controller.java
@@ -95,7 +95,10 @@ import gregtech.api.multitileentity.machine.MultiTileBasicMachine;
import gregtech.api.multitileentity.multiblock.casing.FunctionalCasing;
import gregtech.api.multitileentity.multiblock.casing.UpgradeCasing;
import gregtech.api.objects.GT_ItemStack;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
import gregtech.api.util.GT_Multiblock_Tooltip_Builder;
+import gregtech.api.util.GT_Recipe;
import gregtech.api.util.GT_Utility;
import gregtech.api.util.GT_Waila;
import gregtech.common.tileentities.casings.upgrade.Inventory;
@@ -1675,7 +1678,7 @@ public abstract class Controller<T extends Controller<T>> extends MultiTileBasic
}
ProcessingLogic logic = ((ProcessingLogicHost) this).getProcessingLogic();
logic.clear();
- boolean result = false;
+ CheckRecipeResult result = CheckRecipeResultRegistry.NO_RECIPE;
if (isSeparateInputs()) {
// TODO: Add separation with fluids
for (Pair<ItemStack[], String> inventory : getItemInputsForEachInventory()) {
@@ -1684,7 +1687,7 @@ public abstract class Controller<T extends Controller<T>> extends MultiTileBasic
result = logic.setInputItems(inventory.getLeft())
.setCurrentOutputItems(getOutputItems())
.process();
- if (result) {
+ if (result.wasSuccessful()) {
inventoryName = inventory.getRight();
break;
}
@@ -1698,10 +1701,10 @@ public abstract class Controller<T extends Controller<T>> extends MultiTileBasic
.process();
}
setDuration(logic.getDuration());
- setEut(logic.getEut());
+ setEut(logic.getCalculatedEut());
setItemOutputs(logic.getOutputItems());
setFluidOutputs(logic.getOutputFluids());
- return result;
+ return result.wasSuccessful();
}
public IItemHandlerModifiable getOutputInventory() {
@@ -2067,6 +2070,11 @@ public abstract class Controller<T extends Controller<T>> extends MultiTileBasic
}
@Override
+ public GT_Recipe.GT_Recipe_Map getRecipeMap() {
+ return null;
+ }
+
+ @Override
public Pos2d getRecipeLockingButtonPos() {
return new Pos2d(0, 0);
}
diff --git a/src/main/java/gregtech/api/recipe/check/CheckRecipeResult.java b/src/main/java/gregtech/api/recipe/check/CheckRecipeResult.java
new file mode 100644
index 0000000000..ab1db6ecd2
--- /dev/null
+++ b/src/main/java/gregtech/api/recipe/check/CheckRecipeResult.java
@@ -0,0 +1,42 @@
+package gregtech.api.recipe.check;
+
+import net.minecraft.network.PacketBuffer;
+
+/**
+ * Class to indicate the result of recipe check in the machine. It doesn't need to be actual result of recipemap check,
+ * but can also be status of whether to start the machine. Examples can be found at {@link CheckRecipeResultRegistry}.
+ * <p>
+ * Sample instance must be registered to {@link CheckRecipeResultRegistry}.
+ */
+public interface CheckRecipeResult {
+
+ /**
+ * @return Unique registry ID
+ */
+ String getID();
+
+ /**
+ * @return If recipe check is successful
+ */
+ boolean wasSuccessful();
+
+ /**
+ * @return Actual text to show on client GUI
+ */
+ String getDisplayString();
+
+ /**
+ * Create new instance to receive packet.
+ */
+ CheckRecipeResult newInstance();
+
+ /**
+ * Encode value to sync.
+ */
+ void encode(PacketBuffer buffer);
+
+ /**
+ * Decode synced value.
+ */
+ void decode(PacketBuffer buffer);
+}
diff --git a/src/main/java/gregtech/api/recipe/check/CheckRecipeResultRegistry.java b/src/main/java/gregtech/api/recipe/check/CheckRecipeResultRegistry.java
new file mode 100644
index 0000000000..26f9dedd7d
--- /dev/null
+++ b/src/main/java/gregtech/api/recipe/check/CheckRecipeResultRegistry.java
@@ -0,0 +1,111 @@
+package gregtech.api.recipe.check;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public final class CheckRecipeResultRegistry {
+
+ private static final Map<String, CheckRecipeResult> registry = new HashMap<>();
+
+ /**
+ * Registers CheckRecipeResult. No duplicated IDs are allowed.
+ *
+ * @param sample Sample object to register
+ */
+ public static void register(CheckRecipeResult sample) {
+ if (isRegistered(sample.getID())) {
+ throw new IllegalStateException(
+ String.format(
+ "ID %s is already registered for %s",
+ sample.getID(),
+ registry.get(sample.getID())
+ .getClass()
+ .getCanonicalName()));
+ }
+ registry.put(sample.getID(), sample);
+ }
+
+ public static CheckRecipeResult getSampleFromRegistry(String id) {
+ if (!isRegistered(id)) {
+ throw new RuntimeException("Unknown id: " + id);
+ }
+ return registry.get(id);
+ }
+
+ public static boolean isRegistered(String id) {
+ return registry.containsKey(id);
+ }
+
+ /**
+ * Successfully found recipe.
+ */
+ public static final CheckRecipeResult SUCCESSFUL = SimpleCheckRecipeResult.ofSuccess("success");
+ /**
+ * All requirements met to generator power.
+ */
+ public static final CheckRecipeResult GENERATING = SimpleCheckRecipeResult.ofSuccess("generating");
+ /**
+ * Cannot find recipe.
+ */
+ public static final CheckRecipeResult NO_RECIPE = SimpleCheckRecipeResult.ofFailure("no_recipe");
+ /**
+ * Cannot process recipe because output is full.
+ */
+ public static final CheckRecipeResult OUTPUT_FULL = SimpleCheckRecipeResult.ofFailure("output_full");
+ /**
+ * Default unknown state.
+ */
+ public static final CheckRecipeResult NONE = SimpleCheckRecipeResult.ofFailure("none");
+ /**
+ * Code crashed.
+ */
+ public static final CheckRecipeResult CRASH = SimpleCheckRecipeResult.ofFailure("crash");
+ /**
+ * Cannot find valid fuel for generator.
+ */
+ public static final CheckRecipeResult NO_FUEL_FOUND = SimpleCheckRecipeResult.ofFailure("no_fuel");
+ /**
+ * Cannot find valid turbine.
+ */
+ public static final CheckRecipeResult NO_TURBINE_FOUND = SimpleCheckRecipeResult.ofFailure("no_turbine");
+ /**
+ * No data sticks found for Assembly Line.
+ */
+ public static final CheckRecipeResult NO_DATA_STICKS = SimpleCheckRecipeResult.ofFailure("no_data_sticks");
+ /**
+ * EU/t overflowed.
+ */
+ public static final CheckRecipeResult POWER_OVERFLOW = SimpleCheckRecipeResult.ofFailure("power_overflow");
+ /**
+ * Progress time overflowed.
+ */
+ public static final CheckRecipeResult DURATION_OVERFLOW = SimpleCheckRecipeResult.ofFailure("duration_overflow");
+
+ /**
+ * Cannot process recipe because the machine cannot handle required EUt.
+ */
+ public static CheckRecipeResult insufficientPower(long required) {
+ return new ResultInsufficientPower(required);
+ }
+
+ /**
+ * Cannot process recipe because the machine cannot handle its heat.
+ */
+ public static CheckRecipeResult insufficientHeat(int required) {
+ return new ResultInsufficientHeat(required);
+ }
+
+ /**
+ * Cannot process recipe because the machine is tiered and its tier is too low.
+ */
+ public static CheckRecipeResult insufficientMachineTier(int required) {
+ return new ResultInsufficientMachineTier(required);
+ }
+
+ static {
+ register(new SimpleCheckRecipeResult(false, ""));
+ register(new ResultInsufficientPower(0));
+ register(new ResultInsufficientHeat(0));
+ register(new ResultInsufficientMachineTier(0));
+ }
+}
diff --git a/src/main/java/gregtech/api/recipe/check/FindRecipeResult.java b/src/main/java/gregtech/api/recipe/check/FindRecipeResult.java
new file mode 100644
index 0000000000..5791cb05e1
--- /dev/null
+++ b/src/main/java/gregtech/api/recipe/check/FindRecipeResult.java
@@ -0,0 +1,107 @@
+package gregtech.api.recipe.check;
+
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import gregtech.api.util.GT_Recipe;
+
+/**
+ * Wrapper class to get result of recipe search for recipemap. Note that this only validates recipe input and voltage,
+ * and does not involve in actual check in the machine such as output space or special value.
+ */
+public class FindRecipeResult {
+
+ @Nonnull
+ private final State state;
+ @Nullable
+ private final GT_Recipe recipe;
+
+ private FindRecipeResult(@Nonnull State state, @Nullable GT_Recipe recipe) {
+ this.state = state;
+ this.recipe = recipe;
+ }
+
+ @Nonnull
+ public State getState() {
+ return state;
+ }
+
+ public boolean isSuccessful() {
+ return state.success;
+ }
+
+ /**
+ * If you already checked {@link #isSuccessful()}, you can use {@link #getRecipeNonNull()} instead.
+ */
+ @Nullable
+ public GT_Recipe getRecipe() {
+ return recipe;
+ }
+
+ /**
+ * You should use this ONLY WHEN state == FOUND or INSUFFICIENT_VOLTAGE.
+ */
+ @Nonnull
+ public GT_Recipe getRecipeNonNull() {
+ return Objects.requireNonNull(recipe);
+ }
+
+ /**
+ * Successfully found recipe.
+ */
+ public static FindRecipeResult ofSuccess(@Nonnull GT_Recipe recipe) {
+ return new FindRecipeResult(State.FOUND, Objects.requireNonNull(recipe));
+ }
+
+ /**
+ * Recipe was found, but voltage is not sufficient to run.
+ */
+ public static FindRecipeResult ofInsufficientVoltage(@Nonnull GT_Recipe recipe) {
+ return new FindRecipeResult(State.INSUFFICIENT_VOLTAGE, Objects.requireNonNull(recipe));
+ }
+
+ /**
+ * No recipe found.
+ */
+ public static final FindRecipeResult NOT_FOUND = new FindRecipeResult(State.NOT_FOUND, null);
+ /**
+ * For Microwave.
+ */
+ public static final FindRecipeResult EXPLODE = new FindRecipeResult(State.EXPLODE, null);
+ /**
+ * For Microwave.
+ */
+ public static final FindRecipeResult ON_FIRE = new FindRecipeResult(State.ON_FIRE, null);
+
+ public enum State {
+
+ /**
+ * Successfully found recipe.
+ */
+ FOUND(true),
+ /**
+ * Recipe was found, but voltage is not sufficient to run.
+ */
+ INSUFFICIENT_VOLTAGE(false),
+ /**
+ * No recipe found.
+ */
+ NOT_FOUND(false),
+ /**
+ * For Microwave.
+ */
+ EXPLODE(false),
+ /**
+ * For Microwave.
+ */
+ ON_FIRE(false);
+
+ private final boolean success;
+
+ State(boolean success) {
+ this.success = success;
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/recipe/check/ResultInsufficientHeat.java b/src/main/java/gregtech/api/recipe/check/ResultInsufficientHeat.java
new file mode 100644
index 0000000000..96c8955130
--- /dev/null
+++ b/src/main/java/gregtech/api/recipe/check/ResultInsufficientHeat.java
@@ -0,0 +1,57 @@
+package gregtech.api.recipe.check;
+
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.util.StatCollector;
+
+import gregtech.api.enums.HeatingCoilLevel;
+import gregtech.api.util.GT_Utility;
+
+public class ResultInsufficientHeat implements CheckRecipeResult {
+
+ private int required;
+
+ ResultInsufficientHeat(int required) {
+ this.required = required;
+ }
+
+ @Override
+ public String getID() {
+ return "insufficient_heat";
+ }
+
+ @Override
+ public boolean wasSuccessful() {
+ return false;
+ }
+
+ @Override
+ public String getDisplayString() {
+ return StatCollector.translateToLocalFormatted(
+ "GT5U.gui.text.insufficient_heat",
+ GT_Utility.formatNumbers(required),
+ HeatingCoilLevel.getDisplayNameFromHeat(required, true));
+ }
+
+ @Override
+ public CheckRecipeResult newInstance() {
+ return new ResultInsufficientHeat(0);
+ }
+
+ @Override
+ public void encode(PacketBuffer buffer) {
+ buffer.writeVarIntToBuffer(required);
+ }
+
+ @Override
+ public void decode(PacketBuffer buffer) {
+ required = buffer.readVarIntFromBuffer();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ResultInsufficientHeat that = (ResultInsufficientHeat) o;
+ return required == that.required;
+ }
+}
diff --git a/src/main/java/gregtech/api/recipe/check/ResultInsufficientMachineTier.java b/src/main/java/gregtech/api/recipe/check/ResultInsufficientMachineTier.java
new file mode 100644
index 0000000000..1e00e791d1
--- /dev/null
+++ b/src/main/java/gregtech/api/recipe/check/ResultInsufficientMachineTier.java
@@ -0,0 +1,54 @@
+package gregtech.api.recipe.check;
+
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.util.StatCollector;
+
+import gregtech.api.util.GT_Utility;
+
+public class ResultInsufficientMachineTier implements CheckRecipeResult {
+
+ private int required;
+
+ ResultInsufficientMachineTier(int required) {
+ this.required = required;
+ }
+
+ @Override
+ public String getID() {
+ return "insufficient_machine_tier";
+ }
+
+ @Override
+ public boolean wasSuccessful() {
+ return false;
+ }
+
+ @Override
+ public String getDisplayString() {
+ return StatCollector
+ .translateToLocalFormatted("GT5U.gui.text.insufficient_machine_tier", GT_Utility.formatNumbers(required));
+ }
+
+ @Override
+ public CheckRecipeResult newInstance() {
+ return new ResultInsufficientMachineTier(0);
+ }
+
+ @Override
+ public void encode(PacketBuffer buffer) {
+ buffer.writeVarIntToBuffer(required);
+ }
+
+ @Override
+ public void decode(PacketBuffer buffer) {
+ required = buffer.readVarIntFromBuffer();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ResultInsufficientMachineTier that = (ResultInsufficientMachineTier) o;
+ return required == that.required;
+ }
+}
diff --git a/src/main/java/gregtech/api/recipe/check/ResultInsufficientPower.java b/src/main/java/gregtech/api/recipe/check/ResultInsufficientPower.java
new file mode 100644
index 0000000000..ca6b31ccef
--- /dev/null
+++ b/src/main/java/gregtech/api/recipe/check/ResultInsufficientPower.java
@@ -0,0 +1,56 @@
+package gregtech.api.recipe.check;
+
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.util.StatCollector;
+
+import gregtech.api.util.GT_Utility;
+
+public class ResultInsufficientPower implements CheckRecipeResult {
+
+ private long required;
+
+ ResultInsufficientPower(long required) {
+ this.required = required;
+ }
+
+ @Override
+ public String getID() {
+ return "insufficient_power";
+ }
+
+ @Override
+ public boolean wasSuccessful() {
+ return false;
+ }
+
+ @Override
+ public String getDisplayString() {
+ return StatCollector.translateToLocalFormatted(
+ "GT5U.gui.text.insufficient_power",
+ GT_Utility.formatNumbers(required),
+ GT_Utility.getColoredTierNameFromVoltage(required));
+ }
+
+ @Override
+ public CheckRecipeResult newInstance() {
+ return new ResultInsufficientPower(0);
+ }
+
+ @Override
+ public void encode(PacketBuffer buffer) {
+ buffer.writeLong(required);
+ }
+
+ @Override
+ public void decode(PacketBuffer buffer) {
+ required = buffer.readLong();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ResultInsufficientPower that = (ResultInsufficientPower) o;
+ return required == that.required;
+ }
+}
diff --git a/src/main/java/gregtech/api/recipe/check/SimpleCheckRecipeResult.java b/src/main/java/gregtech/api/recipe/check/SimpleCheckRecipeResult.java
new file mode 100644
index 0000000000..767a168125
--- /dev/null
+++ b/src/main/java/gregtech/api/recipe/check/SimpleCheckRecipeResult.java
@@ -0,0 +1,78 @@
+package gregtech.api.recipe.check;
+
+import java.util.Objects;
+
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.util.StatCollector;
+
+import com.gtnewhorizons.modularui.common.internal.network.NetworkUtils;
+
+/**
+ * Simple implementation of {@link CheckRecipeResult}. You can create new object without registering it.
+ */
+public class SimpleCheckRecipeResult implements CheckRecipeResult {
+
+ private boolean success;
+ private String key;
+
+ SimpleCheckRecipeResult(boolean success, String key) {
+ this.success = success;
+ this.key = key;
+ }
+
+ @Override
+ public String getID() {
+ return "simple_result";
+ }
+
+ @Override
+ public boolean wasSuccessful() {
+ return success;
+ }
+
+ @Override
+ public String getDisplayString() {
+ return StatCollector.translateToLocal("GT5U.gui.text." + key);
+ }
+
+ @Override
+ public CheckRecipeResult newInstance() {
+ return new SimpleCheckRecipeResult(false, "");
+ }
+
+ @Override
+ public void encode(PacketBuffer buffer) {
+ buffer.writeBoolean(success);
+ NetworkUtils.writeStringSafe(buffer, key);
+ }
+
+ @Override
+ public void decode(PacketBuffer buffer) {
+ success = buffer.readBoolean();
+ key = NetworkUtils.readStringSafe(buffer);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SimpleCheckRecipeResult that = (SimpleCheckRecipeResult) o;
+ return success == that.success && Objects.equals(key, that.key);
+ }
+
+ /**
+ * Creates new result with successful state. Add your localized description with `GT5U.gui.text.{key}`.
+ * This is already registered to registry.
+ */
+ public static CheckRecipeResult ofSuccess(String key) {
+ return new SimpleCheckRecipeResult(true, key);
+ }
+
+ /**
+ * Creates new result object with failed state. Add your localized description with `GT5U.gui.text.{key}`.
+ * This is already registered to registry.
+ */
+ public static CheckRecipeResult ofFailure(String key) {
+ return new SimpleCheckRecipeResult(false, key);
+ }
+}
diff --git a/src/main/java/gregtech/api/recipe/check/SingleRecipeCheck.java b/src/main/java/gregtech/api/recipe/check/SingleRecipeCheck.java
new file mode 100644
index 0000000000..d0a952b8d5
--- /dev/null
+++ b/src/main/java/gregtech/api/recipe/check/SingleRecipeCheck.java
@@ -0,0 +1,404 @@
+package gregtech.api.recipe.check;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+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 gregtech.api.enums.GT_Values;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.GT_Utility.ItemId;
+
+/**
+ * Used by machines that are locked to a single recipe, for faster recipe check.
+ * <p>
+ * Computation time will be like these:
+ * <ul>
+ * Normal recipe check:
+ * <ul>
+ * {@link GT_Recipe.GT_Recipe_Map#findRecipeWithResult Find recipe from recipemap}: O(NCR)
+ * where N = number of machine inputs, C = average amount of recipe candidates found for specific input,
+ * R = computation time to {@link GT_Recipe#isRecipeInputEqual check if inputs match to recipe}
+ * </ul>
+ * <ul>
+ * {@link GT_Recipe#isRecipeInputEqual Check if inputs match to recipe}: O(NM)
+ * where N = number of machine inputs, M = number of recipe inputs
+ * </ul>
+ * </ul>
+ * <ul>
+ * {@link #checkRecipeInputs Single recipe check}: O(N + M)
+ * where N = number of machine inputs, M = number of recipe inputs
+ * </ul>
+ */
+public class SingleRecipeCheck {
+
+ @Nonnull
+ private final GT_Recipe recipe;
+ @Nonnull
+ private final GT_Recipe.GT_Recipe_Map recipeMap;
+ @Nonnull
+ private final ImmutableMap<ItemId, Integer> itemCost;
+ @Nonnull
+ private final ImmutableMap<Fluid, Integer> fluidCost;
+
+ private final int totalItemCost;
+ private final int totalFluidCost;
+
+ private SingleRecipeCheck(@Nonnull GT_Recipe recipe, @Nonnull GT_Recipe.GT_Recipe_Map recipeMap,
+ @Nonnull ImmutableMap<ItemId, Integer> itemCost, @Nonnull ImmutableMap<Fluid, Integer> fluidCost) {
+ this.recipe = recipe;
+ this.recipeMap = recipeMap;
+ this.itemCost = itemCost;
+ this.fluidCost = fluidCost;
+
+ this.totalItemCost = itemCost.values()
+ .stream()
+ .mapToInt(Integer::intValue)
+ .sum();
+ this.totalFluidCost = fluidCost.values()
+ .stream()
+ .mapToInt(Integer::intValue)
+ .sum();
+ }
+
+ @Nonnull
+ public GT_Recipe getRecipe() {
+ return recipe;
+ }
+
+ @Nonnull
+ public GT_Recipe.GT_Recipe_Map getRecipeMap() {
+ return recipeMap;
+ }
+
+ /**
+ * Returns the number of parallel recipes, or 0 if recipe is not satisfied at all.
+ */
+ public int checkRecipeInputs(boolean consumeInputs, int maxParallel, ItemStack[] itemInputs,
+ FluidStack[] fluidInputs) {
+ int currentParallel = maxParallel;
+
+ if (totalItemCost > 0) {
+ // Create map for item -> stored amount
+ Map<ItemId, Integer> itemMap = new HashMap<>();
+ for (ItemStack itemStack : itemInputs) {
+ if (itemStack == null) continue;
+ itemMap.merge(ItemId.createNoCopy(itemStack), itemStack.stackSize, Integer::sum);
+ }
+
+ // Check how many parallels can it perform for each item
+ for (Map.Entry<ItemId, Integer> costEntry : itemCost.entrySet()) {
+ currentParallel = Math
+ .min(currentParallel, itemMap.getOrDefault(costEntry.getKey(), 0) / costEntry.getValue());
+ if (currentParallel <= 0) {
+ return 0;
+ }
+ }
+ }
+
+ if (totalFluidCost > 0) {
+ // Create map for fluid -> stored amount
+ Map<Fluid, Integer> fluidMap = new HashMap<>();
+ for (FluidStack fluidStack : fluidInputs) {
+ if (fluidStack == null) continue;
+ fluidMap.merge(fluidStack.getFluid(), fluidStack.amount, Integer::sum);
+ }
+
+ // Check how many parallels can it perform for each fluid
+ for (Map.Entry<Fluid, Integer> costEntry : fluidCost.entrySet()) {
+ currentParallel = Math
+ .min(currentParallel, fluidMap.getOrDefault(costEntry.getKey(), 0) / costEntry.getValue());
+ if (currentParallel <= 0) {
+ return 0;
+ }
+ }
+ }
+
+ final int finalParallel = currentParallel;
+ if (consumeInputs) {
+ if (totalItemCost > 0) {
+ int remainingItemCost = totalItemCost * finalParallel;
+ Map<ItemId, Integer> runningItemCost = itemCost.entrySet()
+ .stream()
+ .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue() * finalParallel));
+
+ for (ItemStack itemStack : itemInputs) {
+ if (itemStack == null) continue;
+ ItemId key = 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 all item costs are paid, we don't need to iterate inputs furthermore
+ 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 : fluidInputs) {
+ if (fluidStack == null) continue;
+ 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 all fluid costs are paid, we don't need to iterate inputs furthermore
+ if (remainingFluidCost <= 0) {
+ break;
+ }
+ }
+ }
+ }
+
+ return finalParallel;
+ }
+
+ 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();
+ tag.setString("recipemap", recipeMap.mUnlocalizedName);
+ 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);
+ tag.setTag("itemCost", writeList(itemCost.entrySet(), e -> {
+ NBTTagCompound ret = new NBTTagCompound();
+ ret.setTag(
+ "id",
+ e.getKey()
+ .writeToNBT());
+ ret.setInteger("count", e.getValue());
+ return ret;
+ }));
+ 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 SingleRecipeCheck tryLoad(GT_Recipe.GT_Recipe_Map recipeMap, NBTTagCompound tag) {
+ if (tag == null || tag.hasNoTags()) return null;
+
+ GT_Recipe.GT_Recipe_Map mapToUse;
+ if (tag.hasKey("recipemap")) {
+ String mapName = tag.getString("recipemap");
+ GT_Recipe.GT_Recipe_Map foundMap = GT_Recipe.GT_Recipe_Map.findRecipeMap(mapName);
+ if (foundMap != null) {
+ mapToUse = foundMap;
+ } else {
+ mapToUse = recipeMap;
+ }
+ } else {
+ mapToUse = recipeMap;
+ }
+ if (mapToUse == null) {
+ return null;
+ }
+
+ GT_Recipe foundRecipe = tryFindRecipe(mapToUse, tag);
+ if (foundRecipe == null) return null;
+ return new SingleRecipeCheck(foundRecipe, mapToUse, loadItemCost(tag), loadFluidCost(tag));
+ }
+
+ private 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")));
+ }
+
+ private static ImmutableMap<ItemId, Integer> loadItemCost(NBTTagCompound tag) {
+ return GT_Utility.streamCompounds(tag.getTagList("itemCost", Constants.NBT.TAG_COMPOUND))
+ .collect(
+ GT_Utility
+ .toImmutableMapSerial(t -> ItemId.create(t.getCompoundTag("id")), t -> t.getInteger("count")));
+ }
+
+ private static GT_Recipe tryFindRecipe(@Nonnull GT_Recipe.GT_Recipe_Map recipeMap, NBTTagCompound tag) {
+ 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(null, 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;
+ }
+
+ private static ImmutableMap<ItemId, Integer> buildItemMap(ItemStack[] inputs) {
+ Map<ItemId, Integer> itemMap = new HashMap<>();
+ for (ItemStack itemStack : inputs) {
+ if (itemStack == null) continue;
+ itemMap.merge(ItemId.create(itemStack), itemStack.stackSize, Integer::sum);
+ }
+ return ImmutableMap.copyOf(itemMap);
+ }
+
+ private static ImmutableMap<Fluid, Integer> buildFluidMap(FluidStack[] fluids) {
+ Map<Fluid, Integer> fluidMap = new HashMap<>();
+ for (FluidStack fluidStack : fluids) {
+ if (fluidStack == null) continue;
+ fluidMap.merge(fluidStack.getFluid(), fluidStack.amount, Integer::sum);
+ }
+ return ImmutableMap.copyOf(fluidMap);
+ }
+
+ public static Builder builder(@Nonnull GT_Recipe.GT_Recipe_Map recipeMap) {
+ return new Builder(Objects.requireNonNull(recipeMap));
+ }
+
+ public static class Builder {
+
+ private final GT_Recipe.GT_Recipe_Map recipeMap;
+
+ // 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<ItemId, Integer> beforeItems;
+ private Map<Fluid, Integer> beforeFluids;
+ private Map<ItemId, Integer> afterItems;
+ private Map<Fluid, Integer> afterFluids;
+
+ private GT_Recipe recipe;
+
+ private Builder(@Nonnull GT_Recipe.GT_Recipe_Map recipeMap) {
+ this.recipeMap = recipeMap;
+ }
+
+ public Builder setBefore(ItemStack[] inputs, FluidStack[] fluids) {
+ beforeItems = buildItemMap(inputs);
+ beforeFluids = buildFluidMap(fluids);
+ return this;
+ }
+
+ public Builder setAfter(ItemStack[] inputs, FluidStack[] fluids) {
+ afterItems = buildItemMap(inputs);
+ afterFluids = buildFluidMap(fluids);
+ return this;
+ }
+
+ public Builder setRecipe(@Nonnull GT_Recipe recipe) {
+ this.recipe = recipe;
+ return this;
+ }
+
+ private ImmutableMap<ItemId, Integer> buildItemCost() {
+ ImmutableMap.Builder<ItemId, Integer> itemCostBuilder = ImmutableMap.builder();
+ for (Map.Entry<ItemId, Integer> entry : beforeItems.entrySet()) {
+ int cost = entry.getValue() - afterItems.getOrDefault(entry.getKey(), 0);
+ if (cost > 0) {
+ itemCostBuilder.put(entry.getKey(), cost);
+ }
+ }
+ return itemCostBuilder.build();
+ }
+
+ private ImmutableMap<Fluid, Integer> buildFluidCost() {
+ 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 fluidCostBuilder.build();
+ }
+
+ public SingleRecipeCheck build() {
+ if (recipe == null) {
+ throw new IllegalStateException("recipe is not set");
+ }
+ return new SingleRecipeCheck(recipe, recipeMap, buildItemCost(), buildFluidCost());
+ }
+ }
+}
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));
}