aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/logic
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/gregtech/api/logic')
-rw-r--r--src/main/java/gregtech/api/logic/AbstractProcessingLogic.java346
-rw-r--r--src/main/java/gregtech/api/logic/ComplexParallelProcessingLogic.java219
-rw-r--r--src/main/java/gregtech/api/logic/ControllerFluidLogic.java152
-rw-r--r--src/main/java/gregtech/api/logic/ControllerItemLogic.java148
-rw-r--r--src/main/java/gregtech/api/logic/FluidInventoryLogic.java269
-rw-r--r--src/main/java/gregtech/api/logic/ItemInventoryLogic.java314
-rw-r--r--src/main/java/gregtech/api/logic/MuTEProcessingLogic.java256
-rw-r--r--src/main/java/gregtech/api/logic/NullPowerLogic.java5
-rw-r--r--src/main/java/gregtech/api/logic/PollutionLogic.java17
-rw-r--r--src/main/java/gregtech/api/logic/PowerLogic.java129
-rw-r--r--src/main/java/gregtech/api/logic/ProcessingLogic.java323
-rw-r--r--src/main/java/gregtech/api/logic/interfaces/FluidInventoryLogicHost.java95
-rw-r--r--src/main/java/gregtech/api/logic/interfaces/ItemInventoryLogicHost.java172
-rw-r--r--src/main/java/gregtech/api/logic/interfaces/PollutionLogicHost.java8
-rw-r--r--src/main/java/gregtech/api/logic/interfaces/PowerLogicHost.java48
-rw-r--r--src/main/java/gregtech/api/logic/interfaces/ProcessingLogicHost.java80
16 files changed, 2094 insertions, 487 deletions
diff --git a/src/main/java/gregtech/api/logic/AbstractProcessingLogic.java b/src/main/java/gregtech/api/logic/AbstractProcessingLogic.java
new file mode 100644
index 0000000000..ae78bbacc2
--- /dev/null
+++ b/src/main/java/gregtech/api/logic/AbstractProcessingLogic.java
@@ -0,0 +1,346 @@
+package gregtech.api.logic;
+
+import java.util.function.Supplier;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.api.interfaces.tileentity.IVoidable;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_ParallelHelper;
+import gregtech.api.util.GT_Recipe;
+
+/**
+ * Logic class to calculate result of recipe check from inputs.
+ */
+@SuppressWarnings({ "unused", "UnusedReturnValue" })
+public abstract class AbstractProcessingLogic<P extends AbstractProcessingLogic<P>> {
+
+ protected IVoidable machine;
+ protected Supplier<RecipeMap<?>> recipeMapSupplier;
+ protected GT_Recipe lastRecipe;
+ protected RecipeMap<?> lastRecipeMap;
+ protected ItemStack[] outputItems;
+ protected FluidStack[] outputFluids;
+ 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 int maxParallel = 1;
+ protected Supplier<Integer> maxParallelSupplier;
+ protected int calculatedParallels = 0;
+ protected int batchSize = 1;
+ protected float euModifier = 1.0f;
+ protected float speedBoost = 1.0f;
+ protected boolean amperageOC = true;
+ protected boolean isCleanroom;
+
+ // #region Setters
+
+ /**
+ * Overwrites item output result of the calculation.
+ */
+ public P setOutputItems(ItemStack... itemOutputs) {
+ this.outputItems = itemOutputs;
+ return getThis();
+ }
+
+ /**
+ * Overwrites fluid output result of the calculation.
+ */
+ public P setOutputFluids(FluidStack... fluidOutputs) {
+ this.outputFluids = fluidOutputs;
+ return getThis();
+ }
+
+ public P setIsCleanroom(boolean isCleanroom) {
+ this.isCleanroom = isCleanroom;
+ return getThis();
+ }
+
+ /**
+ * Sets max amount of parallel.
+ */
+ public P setMaxParallel(int maxParallel) {
+ this.maxParallel = maxParallel;
+ return getThis();
+ }
+
+ /**
+ * Sets method to get max amount of parallel.
+ */
+ public P setMaxParallelSupplier(Supplier<Integer> supplier) {
+ this.maxParallelSupplier = supplier;
+ return getThis();
+ }
+
+ /**
+ * Sets batch size for batch mode.
+ */
+ public P setBatchSize(int size) {
+ this.batchSize = size;
+ return getThis();
+ }
+
+ public P setRecipeMap(RecipeMap<?> recipeMap) {
+ return setRecipeMapSupplier(() -> recipeMap);
+ }
+
+ public P setRecipeMapSupplier(Supplier<RecipeMap<?>> supplier) {
+ this.recipeMapSupplier = supplier;
+ return getThis();
+ }
+
+ public P setEuModifier(float modifier) {
+ this.euModifier = modifier;
+ return getThis();
+ }
+
+ public P setSpeedBonus(float speedModifier) {
+ this.speedBoost = speedModifier;
+ return getThis();
+ }
+
+ /**
+ * Sets machine used for void protection logic.
+ */
+ public P setMachine(IVoidable machine) {
+ this.machine = machine;
+ return getThis();
+ }
+
+ /**
+ * Overwrites duration result of the calculation.
+ */
+ public P setDuration(int duration) {
+ this.duration = duration;
+ return getThis();
+ }
+
+ /**
+ * Overwrites EU/t result of the calculation.
+ */
+ public P setCalculatedEut(long calculatedEut) {
+ this.calculatedEut = calculatedEut;
+ return getThis();
+ }
+
+ /**
+ * 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 P setAvailableVoltage(long voltage) {
+ availableVoltage = voltage;
+ return getThis();
+ }
+
+ /**
+ * 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 P setAvailableAmperage(long amperage) {
+ availableAmperage = amperage;
+ return getThis();
+ }
+
+ public P setVoidProtection(boolean protectItems, boolean protectFluids) {
+ this.protectItems = protectItems;
+ this.protectFluids = protectFluids;
+ return getThis();
+ }
+
+ /**
+ * Sets custom overclock ratio. 2/4 by default.
+ * Parameters represent number of bit shift, so 1 -> 2x, 2 -> 4x.
+ */
+ public P setOverclock(int timeReduction, int powerIncrease) {
+ this.overClockTimeReduction = timeReduction;
+ this.overClockPowerIncrease = powerIncrease;
+ return getThis();
+ }
+
+ /**
+ * Sets overclock ratio to 4/4.
+ */
+ public P enablePerfectOverclock() {
+ return this.setOverclock(2, 2);
+ }
+
+ /**
+ * Sets whether the multi should use amperage to OC or not
+ */
+ public P setAmperageOC(boolean amperageOC) {
+ this.amperageOC = amperageOC;
+ return getThis();
+ }
+
+ /**
+ * Clears calculated results (and provided machine inputs) to prepare for the next machine operation.
+ */
+ public P clear() {
+ this.calculatedEut = 0;
+ this.duration = 0;
+ this.calculatedParallels = 0;
+ return getThis();
+ }
+
+ // #endregion
+
+ // #region Logic
+
+ /**
+ * Executes the recipe check: Find recipe from recipemap, Calculate parallel, overclock and outputs.
+ */
+ @Nonnull
+ public abstract CheckRecipeResult process();
+
+ /**
+ * Refreshes recipemap to use. Remember to call this before {@link #process} to make sure correct recipemap is used.
+ *
+ * @return Recipemap to use now
+ */
+ protected RecipeMap<?> preProcess() {
+ RecipeMap<?> recipeMap;
+ if (recipeMapSupplier == null) {
+ recipeMap = null;
+ } else {
+ recipeMap = recipeMapSupplier.get();
+ }
+ if (lastRecipeMap != recipeMap) {
+ lastRecipe = null;
+ lastRecipeMap = recipeMap;
+ }
+
+ if (maxParallelSupplier != null) {
+ maxParallel = maxParallelSupplier.get();
+ }
+
+ return recipeMap;
+ }
+
+ /**
+ * Check has been succeeded, so it applies the recipe and calculated parameters.
+ * At this point, inputs have been already consumed.
+ */
+ @Nonnull
+ protected CheckRecipeResult applyRecipe(@Nonnull GT_Recipe recipe, @Nonnull GT_ParallelHelper helper,
+ @Nonnull GT_OverclockCalculator calculator, @Nonnull CheckRecipeResult result) {
+ if (recipe.mCanBeBuffered) {
+ lastRecipe = recipe;
+ } else {
+ lastRecipe = null;
+ }
+ calculatedParallels = helper.getCurrentParallel();
+
+ 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;
+
+ CheckRecipeResult hookResult = onRecipeStart(recipe);
+ if (!hookResult.wasSuccessful()) {
+ return hookResult;
+ }
+
+ outputItems = helper.getItemOutputs();
+ outputFluids = helper.getFluidOutputs();
+
+ return result;
+ }
+
+ /**
+ * 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 do additional check for found recipe if needed.
+ */
+ @Nonnull
+ protected CheckRecipeResult validateRecipe(@Nonnull GT_Recipe recipe) {
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ /**
+ * Override to perform additional logic when recipe starts.
+ *
+ * This is called when the recipe processing logic has finished all
+ * checks, consumed all inputs, but has not yet set the outputs to
+ * be produced. Returning a result other than SUCCESSFUL will void
+ * all inputs!
+ */
+ @Nonnull
+ protected CheckRecipeResult onRecipeStart(@Nonnull GT_Recipe recipe) {
+ return CheckRecipeResultRegistry.SUCCESSFUL;
+ }
+
+ /**
+ * Override to tweak overclock logic if needed.
+ */
+ @Nonnull
+ protected GT_OverclockCalculator createOverclockCalculator(@Nonnull GT_Recipe recipe) {
+ return new GT_OverclockCalculator().setRecipeEUt(recipe.mEUt)
+ .setAmperage(availableAmperage)
+ .setEUt(availableVoltage)
+ .setDuration(recipe.mDuration)
+ .setSpeedBoost(speedBoost)
+ .setEUtDiscount(euModifier)
+ .setAmperageOC(amperageOC)
+ .setDurationDecreasePerOC(overClockTimeReduction)
+ .setEUtIncreasePerOC(overClockPowerIncrease);
+ }
+ // #endregion
+
+ // #region Getters
+
+ public ItemStack[] getOutputItems() {
+ return outputItems;
+ }
+
+ public FluidStack[] getOutputFluids() {
+ return outputFluids;
+ }
+
+ public int getDuration() {
+ return duration;
+ }
+
+ public long getCalculatedEut() {
+ return calculatedEut;
+ }
+
+ public int getCurrentParallels() {
+ return calculatedParallels;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Nonnull
+ public P getThis() {
+ return (P) this;
+ }
+
+ // #endregion
+}
diff --git a/src/main/java/gregtech/api/logic/ComplexParallelProcessingLogic.java b/src/main/java/gregtech/api/logic/ComplexParallelProcessingLogic.java
index 3c7974db9e..72a74ebd04 100644
--- a/src/main/java/gregtech/api/logic/ComplexParallelProcessingLogic.java
+++ b/src/main/java/gregtech/api/logic/ComplexParallelProcessingLogic.java
@@ -5,180 +5,117 @@ import java.util.stream.LongStream;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.FluidStack;
-import gregtech.api.multitileentity.multiblock.base.Controller;
-import gregtech.api.recipe.RecipeMap;
-import gregtech.api.util.GT_OverclockCalculator;
-import gregtech.api.util.GT_ParallelHelper;
-import gregtech.api.util.GT_Recipe;
+public class ComplexParallelProcessingLogic<P extends ComplexParallelProcessingLogic<P>>
+ extends MuTEProcessingLogic<P> {
-public class ComplexParallelProcessingLogic {
+ protected int maxComplexParallels;
+ protected ItemStack[][] outputItems;
+ protected FluidStack[][] outputFluids;
+ protected long[] calculatedEutValues;
+ protected int[] durations;
+ protected int[] progresses;
- protected Controller<?> tileEntity;
- protected RecipeMap<?> recipeMap;
- protected boolean hasPerfectOverclock;
- protected final int maxComplexParallels;
- protected final ItemStack[][] outputItems;
- protected final ItemStack[][] inputItems;
- protected final FluidStack[][] inputFluids;
- protected final FluidStack[][] outputFluids;
- protected final long[] availableEut;
- protected final long[] eut;
- protected final long[] durations;
- protected final boolean[] isItemVoidProtected;
- protected final boolean[] isFluidVoidProtected;
-
- public ComplexParallelProcessingLogic(int maxComplexParallels) {
- this(null, maxComplexParallels);
- }
-
- public ComplexParallelProcessingLogic(RecipeMap<?> recipeMap, int maxComplexParallels) {
+ public P setMaxComplexParallel(int maxComplexParallels) {
this.maxComplexParallels = maxComplexParallels;
- this.recipeMap = recipeMap;
- inputItems = new ItemStack[maxComplexParallels][];
- outputItems = new ItemStack[maxComplexParallels][];
- inputFluids = new FluidStack[maxComplexParallels][];
- outputFluids = new FluidStack[maxComplexParallels][];
- eut = new long[maxComplexParallels];
- availableEut = new long[maxComplexParallels];
- durations = new long[maxComplexParallels];
- isItemVoidProtected = new boolean[maxComplexParallels];
- isFluidVoidProtected = new boolean[maxComplexParallels];
- }
-
- public ComplexParallelProcessingLogic setRecipeMap(RecipeMap<?> recipeMap) {
- this.recipeMap = recipeMap;
- return this;
+ reinitializeProcessingArrays();
+ return getThis();
}
- public ComplexParallelProcessingLogic setInputItems(int index, ItemStack... itemInputs) {
+ public ItemStack[] getOutputItems(int index) {
if (index >= 0 && index < maxComplexParallels) {
- inputItems[index] = itemInputs;
+ return outputItems[index];
}
- return this;
+ return null;
}
- public ComplexParallelProcessingLogic setInputFluids(int index, FluidStack... inputFluids) {
+ public FluidStack[] getOutputFluids(int index) {
if (index >= 0 && index < maxComplexParallels) {
- this.inputFluids[index] = inputFluids;
+ return outputFluids[index];
}
- return this;
+ return null;
}
- public ComplexParallelProcessingLogic setTileEntity(Controller<?> tileEntity) {
- this.tileEntity = tileEntity;
- return this;
+ @Override
+ public boolean canWork() {
+ for (int i = 0; i < maxComplexParallels; i++) {
+ if (progresses[i] >= durations[i]) {
+ return machineHost.isAllowedToWork();
+ }
+ }
+ return false;
}
- public ComplexParallelProcessingLogic setEut(int index, long eut) {
- if (index >= 0 && index < maxComplexParallels) {
- availableEut[index] = eut;
- }
- return this;
+ @Override
+ public long getCalculatedEut() {
+ return LongStream.of(this.calculatedEutValues)
+ .sum();
}
- public ComplexParallelProcessingLogic setVoidProtection(int index, boolean protectItem, boolean protectFluid) {
- if (index >= 0 && index < maxComplexParallels) {
- isItemVoidProtected[index] = protectItem;
- isFluidVoidProtected[index] = protectFluid;
- }
- return this;
+ public int getDuration(int index) {
+ return durations[index];
}
- public ComplexParallelProcessingLogic setPerfectOverclock(boolean shouldOverclockPerfectly) {
- this.hasPerfectOverclock = shouldOverclockPerfectly;
- return this;
+ public int getProgress(int index) {
+ return progresses[index];
}
- public ComplexParallelProcessingLogic clear() {
+ @Override
+ public void progress() {
for (int i = 0; i < maxComplexParallels; i++) {
- outputItems[i] = null;
- outputFluids[i] = null;
- durations[i] = 0;
- eut[i] = 0;
+ if (progresses[i] == durations[i]) {
+ progresses[i] = 0;
+ durations[i] = 0;
+ output(i);
+ continue;
+ }
+ progresses[i] = progresses[i] + 1;
}
- return this;
}
- public ComplexParallelProcessingLogic clear(int index) {
- if (index >= 0 && index < maxComplexParallels) {
- inputItems[index] = null;
- inputFluids[index] = null;
- outputItems[index] = null;
- outputFluids[index] = null;
- durations[index] = 0;
- availableEut[index] = 0;
- eut[index] = 0;
+ @Override
+ public void startCheck() {
+ for (int i = 0; i < maxComplexParallels; i++) {
+ if (durations[i] > 0) continue;
+ recipeResult = process();
+ calculatedEutValues[i] = calculatedEut;
+ durations[i] = duration;
+ progresses[i] = 0;
+ outputItems[i] = getOutputItems();
+ outputFluids[i] = getOutputFluids();
}
- return this;
}
- public boolean process(int index) {
- if (recipeMap == null) {
- return false;
- }
- GT_Recipe recipe = recipeMap
- .findRecipe(tileEntity, false, false, availableEut[index], inputFluids[index], inputItems[index]);
- if (recipe == null) {
- return false;
- }
-
- GT_ParallelHelper helper = new GT_ParallelHelper().setRecipe(recipe)
- .setItemInputs(inputItems[index])
- .setFluidInputs(inputFluids[index])
- .setAvailableEUt(availableEut[index])
- .setMachine(tileEntity, isItemVoidProtected[index], isFluidVoidProtected[index])
- .setConsumption(true)
- .setOutputCalculation(true);
-
- helper.build();
-
- if (helper.getCurrentParallel() <= 0) {
- return false;
- }
-
- GT_OverclockCalculator calculator = new GT_OverclockCalculator().setRecipeEUt(recipe.mEUt)
- .setDuration(recipe.mDuration)
- .setEUt(availableEut[index]);
+ protected void output(int index) {
+ setOutputItems(getOutputItems(index));
+ setOutputFluids(getOutputFluids(index));
+ output();
+ }
- if (hasPerfectOverclock) {
- calculator.enablePerfectOC();
+ protected void reinitializeProcessingArrays() {
+ ItemStack[][] oldOutputItems = outputItems;
+ FluidStack[][] oldOutputFluids = outputFluids;
+ long[] oldCalculatedEutValues = calculatedEutValues;
+ int[] oldDurations = durations;
+ int[] oldProgresses = progresses;
+ outputItems = new ItemStack[maxComplexParallels][];
+ outputFluids = new FluidStack[maxComplexParallels][];
+ calculatedEutValues = new long[maxComplexParallels];
+ durations = new int[maxComplexParallels];
+ progresses = new int[maxComplexParallels];
+ for (int i = 0; i < oldOutputItems.length; i++) {
+ outputItems[i] = oldOutputItems[i];
}
-
- if (calculator.getConsumption() == Long.MAX_VALUE - 1 || calculator.getDuration() == Integer.MAX_VALUE - 1) {
- return false;
+ for (int i = 0; i < oldOutputFluids.length; i++) {
+ outputFluids[i] = oldOutputFluids[i];
}
-
- durations[index] = calculator.getDuration();
- eut[index] = calculator.getConsumption();
- outputItems[index] = helper.getItemOutputs();
- outputFluids[index] = helper.getFluidOutputs();
-
- return true;
- }
-
- public long getDuration(int index) {
- if (index >= 0 && index < maxComplexParallels) {
- return durations[index];
+ for (int i = 0; i < oldCalculatedEutValues.length; i++) {
+ calculatedEutValues[i] = oldCalculatedEutValues[i];
}
- return 0;
- }
-
- public long getTotalEU() {
- return LongStream.of(eut)
- .sum();
- }
-
- public ItemStack[] getOutputItems(int index) {
- if (index >= 0 && index < maxComplexParallels) {
- return outputItems[index];
+ for (int i = 0; i < oldDurations[i]; i++) {
+ durations[i] = oldDurations[i];
}
- return null;
- }
-
- public FluidStack[] getOutputFluids(int index) {
- if (index >= 0 && index < maxComplexParallels) {
- return outputFluids[index];
+ for (int i = 0; i < oldProgresses.length; i++) {
+ progresses[i] = oldProgresses[i];
}
- return null;
}
}
diff --git a/src/main/java/gregtech/api/logic/ControllerFluidLogic.java b/src/main/java/gregtech/api/logic/ControllerFluidLogic.java
new file mode 100644
index 0000000000..211c1c2982
--- /dev/null
+++ b/src/main/java/gregtech/api/logic/ControllerFluidLogic.java
@@ -0,0 +1,152 @@
+package gregtech.api.logic;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraftforge.common.util.Constants;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+/**
+ * Controller logic for Fluid inventories
+ *
+ * @author BlueWeabo
+ */
+public class ControllerFluidLogic {
+
+ private final Map<UUID, FluidInventoryLogic> inventories = new HashMap<>();
+ private final Set<Pair<UUID, FluidInventoryLogic>> unallocatedInventories = new HashSet<>();
+
+ public void addInventory(@Nonnull UUID id, @Nonnull FluidInventoryLogic inventory) {
+ Pair<UUID, FluidInventoryLogic> found = checkIfInventoryExistsAsUnallocated(inventory);
+ if (inventory.isUpgradeInventory() && found != null) {
+ unallocatedInventories.remove(found);
+ inventories.put(id, found.getRight());
+ return;
+ }
+ inventories.put(id, inventory);
+ }
+
+ @Nonnull
+ public UUID addInventory(@Nonnull FluidInventoryLogic inventory) {
+ Pair<UUID, FluidInventoryLogic> found = checkIfInventoryExistsAsUnallocated(inventory);
+ if (inventory.isUpgradeInventory() && found != null) {
+ unallocatedInventories.remove(found);
+ inventories.put(found.getLeft(), found.getRight());
+ return Objects.requireNonNull(found.getLeft());
+ }
+ UUID generatedUUID = Objects.requireNonNull(UUID.randomUUID());
+ inventories.put(generatedUUID, inventory);
+ return generatedUUID;
+ }
+
+ @Nullable
+ private Pair<UUID, FluidInventoryLogic> checkIfInventoryExistsAsUnallocated(
+ @Nonnull FluidInventoryLogic inventory) {
+ if (unallocatedInventories.size() == 0) {
+ return null;
+ }
+ return unallocatedInventories.stream()
+ .filter(
+ unallocated -> unallocated.getRight()
+ .getTier() == inventory.getTier())
+ .findFirst()
+ .get();
+ }
+
+ /**
+ * Removes the inventory with said id and gives it back to be processed if needed.
+ */
+ @Nonnull
+ public FluidInventoryLogic removeInventory(@Nonnull UUID id) {
+ return Objects.requireNonNull(inventories.remove(id));
+ }
+
+ @Nonnull
+ public FluidInventoryLogic getAllInventoryLogics() {
+ return new FluidInventoryLogic(
+ inventories.values()
+ .stream()
+ .map(inv -> inv.getInventory())
+ .collect(Collectors.toList()));
+ }
+
+ @Nonnull
+ public FluidInventoryLogic getInventoryLogic(@Nullable UUID id) {
+ if (id == null) return getAllInventoryLogics();
+ return Objects.requireNonNull(inventories.getOrDefault(id, getAllInventoryLogics()));
+ }
+
+ @Nonnull
+ public Set<Entry<UUID, FluidInventoryLogic>> getAllInventoryLogicsAsEntrySet() {
+ return Objects.requireNonNull(inventories.entrySet());
+ }
+
+ @Nonnull
+ public String getInventoryDisplayName(@Nullable UUID id) {
+ if (id == null) return "";
+ FluidInventoryLogic logic = inventories.get(id);
+ if (logic == null) return "";
+ String displayName = logic.getDisplayName();
+ if (displayName == null) return Objects.requireNonNull(id.toString());
+ return displayName;
+ }
+
+ public void setInventoryDisplayName(@Nullable UUID id, @Nullable String displayName) {
+ if (id == null) return;
+ FluidInventoryLogic logic = inventories.get(id);
+ if (logic == null) return;
+ logic.setDisplayName(displayName);
+ }
+
+ @Nonnull
+ public NBTTagCompound saveToNBT() {
+ NBTTagCompound nbt = new NBTTagCompound();
+ NBTTagList inventoriesNBT = new NBTTagList();
+ inventories.forEach((uuid, inventory) -> {
+ NBTTagCompound inventoryNBT = new NBTTagCompound();
+ inventoryNBT.setTag("inventory", inventory.saveToNBT());
+ inventoryNBT.setString("uuid", uuid.toString());
+ inventoryNBT.setInteger(
+ "invSize",
+ inventory.getInventory()
+ .getTanks());
+ inventoryNBT.setLong(
+ "tankCapacity",
+ inventory.getInventory()
+ .getTankCapacity(0));
+ inventoriesNBT.appendTag(inventoryNBT);
+ });
+ nbt.setTag("inventories", inventoriesNBT);
+ return nbt;
+ }
+
+ public void loadFromNBT(@Nonnull NBTTagCompound nbt) {
+ NBTTagList inventoriesNBT = nbt.getTagList("inventories", Constants.NBT.TAG_COMPOUND);
+ if (inventoriesNBT == null) return;
+ for (int i = 0; i < inventoriesNBT.tagCount(); i++) {
+ NBTTagCompound inventoryNBT = inventoriesNBT.getCompoundTagAt(i);
+ UUID uuid = UUID.fromString(inventoryNBT.getString("uuid"));
+ FluidInventoryLogic inventory = new FluidInventoryLogic(
+ inventoryNBT.getInteger("invSize"),
+ inventoryNBT.getLong("tankCapacity"));
+ inventory.loadFromNBT(inventoryNBT.getCompoundTag("inventory"));
+ if (inventory.isUpgradeInventory()) {
+ unallocatedInventories.add(Pair.of(uuid, inventory));
+ } else {
+ inventories.put(uuid, inventory);
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/logic/ControllerItemLogic.java b/src/main/java/gregtech/api/logic/ControllerItemLogic.java
new file mode 100644
index 0000000000..2863c2f49c
--- /dev/null
+++ b/src/main/java/gregtech/api/logic/ControllerItemLogic.java
@@ -0,0 +1,148 @@
+package gregtech.api.logic;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraftforge.common.util.Constants;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+/**
+ * Logic of the Item logic for the controller. This is controlling all of the inventories.
+ *
+ * @author BlueWeabo
+ */
+public class ControllerItemLogic {
+
+ private final Map<UUID, ItemInventoryLogic> inventories = new HashMap<>();
+ private final Set<Pair<UUID, ItemInventoryLogic>> unallocatedInventories = new HashSet<>();
+
+ public void addInventory(@Nonnull UUID id, @Nonnull ItemInventoryLogic inventory) {
+ Pair<UUID, ItemInventoryLogic> found = checkIfInventoryExistsAsUnallocated(inventory);
+ if (inventory.isUpgradeInventory() && found != null) {
+ unallocatedInventories.remove(found);
+ inventories.put(id, found.getRight());
+ return;
+ }
+ inventories.put(id, inventory);
+ }
+
+ @Nonnull
+ public UUID addInventory(@Nonnull ItemInventoryLogic inventory) {
+ Pair<UUID, ItemInventoryLogic> found = checkIfInventoryExistsAsUnallocated(inventory);
+ if (inventory.isUpgradeInventory() && found != null) {
+ unallocatedInventories.remove(found);
+ inventories.put(found.getLeft(), found.getRight());
+ return Objects.requireNonNull(found.getLeft());
+ }
+ UUID generatedUUID = Objects.requireNonNull(UUID.randomUUID());
+ inventories.put(generatedUUID, inventory);
+ return generatedUUID;
+ }
+
+ @Nullable
+ private Pair<UUID, ItemInventoryLogic> checkIfInventoryExistsAsUnallocated(@Nonnull ItemInventoryLogic inventory) {
+ if (unallocatedInventories.size() == 0) {
+ return null;
+ }
+ return unallocatedInventories.stream()
+ .filter(
+ unallocated -> unallocated.getRight()
+ .getTier() == inventory.getTier()
+ && unallocated.getRight()
+ .getSlots() == inventory.getSlots())
+ .findFirst()
+ .get();
+ }
+
+ /**
+ * Removes the inventory with said id and gives it back to be processed if needed.
+ */
+ @Nonnull
+ public ItemInventoryLogic removeInventory(@Nonnull UUID id) {
+ return Objects.requireNonNull(inventories.remove(id));
+ }
+
+ @Nonnull
+ public ItemInventoryLogic getAllInventoryLogics() {
+ return new ItemInventoryLogic(
+ inventories.values()
+ .stream()
+ .map(inv -> inv.getInventory())
+ .collect(Collectors.toList()));
+ }
+
+ @Nonnull
+ public ItemInventoryLogic getInventoryLogic(@Nullable UUID id) {
+ if (id == null) return getAllInventoryLogics();
+ return Objects.requireNonNull(inventories.getOrDefault(id, getAllInventoryLogics()));
+ }
+
+ @Nonnull
+ public Set<Entry<UUID, ItemInventoryLogic>> getAllInventoryLogicsAsEntrySet() {
+ return Objects.requireNonNull(inventories.entrySet());
+ }
+
+ @Nullable
+ public String getInventoryDisplayName(@Nullable UUID id) {
+ if (id == null) return "";
+ ItemInventoryLogic logic = inventories.get(id);
+ if (logic == null) return "";
+ String displayName = logic.getDisplayName();
+ if (displayName == null) return Objects.requireNonNull(id.toString());
+ return displayName;
+ }
+
+ public void setInventoryDisplayName(@Nullable UUID id, @Nullable String displayName) {
+ if (id == null) return;
+ ItemInventoryLogic logic = inventories.get(id);
+ if (logic == null) return;
+ logic.setDisplayName(displayName);
+ }
+
+ @Nonnull
+ public NBTTagCompound saveToNBT() {
+ NBTTagCompound nbt = new NBTTagCompound();
+ NBTTagList inventoriesNBT = new NBTTagList();
+ inventories.forEach((uuid, inventory) -> {
+ NBTTagCompound inventoryNBT = new NBTTagCompound();
+ inventoryNBT.setTag("inventory", inventory.saveToNBT());
+ inventoryNBT.setString("uuid", uuid.toString());
+ inventoryNBT.setInteger(
+ "invSize",
+ inventory.getInventory()
+ .getSlots());
+ inventoriesNBT.appendTag(inventoryNBT);
+ });
+ nbt.setTag("inventories", inventoriesNBT);
+ return nbt;
+ }
+
+ public void loadFromNBT(@Nonnull NBTTagCompound nbt) {
+ NBTTagList inventoriesNBT = nbt.getTagList("inventories", Constants.NBT.TAG_COMPOUND);
+ if (inventoriesNBT == null) return;
+ for (int i = 0; i < inventoriesNBT.tagCount(); i++) {
+ NBTTagCompound inventoryNBT = inventoriesNBT.getCompoundTagAt(i);
+ UUID uuid = UUID.fromString(inventoryNBT.getString("uuid"));
+ ItemInventoryLogic inventory = new ItemInventoryLogic(inventoryNBT.getInteger("invSize"));
+ NBTTagCompound internalInventoryNBT = inventoryNBT.getCompoundTag("inventory");
+ if (internalInventoryNBT != null) inventory.loadFromNBT(internalInventoryNBT);
+ if (inventory.isUpgradeInventory()) {
+ unallocatedInventories.add(Pair.of(uuid, inventory));
+ } else {
+ inventories.put(uuid, inventory);
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/logic/FluidInventoryLogic.java b/src/main/java/gregtech/api/logic/FluidInventoryLogic.java
new file mode 100644
index 0000000000..88c0c954ec
--- /dev/null
+++ b/src/main/java/gregtech/api/logic/FluidInventoryLogic.java
@@ -0,0 +1,269 @@
+package gregtech.api.logic;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+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.FluidStack;
+
+import com.gtnewhorizons.modularui.api.fluids.FluidTanksHandler;
+import com.gtnewhorizons.modularui.api.fluids.IFluidTankLong;
+import com.gtnewhorizons.modularui.api.fluids.IFluidTanksHandler;
+import com.gtnewhorizons.modularui.api.fluids.ListFluidHandler;
+import com.gtnewhorizons.modularui.api.math.Size;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget;
+import com.gtnewhorizons.modularui.common.widget.Scrollable;
+
+/**
+ * Generic Fluid logic for MuTEs.
+ *
+ * @author BlueWeabo
+ */
+public class FluidInventoryLogic {
+
+ private static final int DEFAULT_COLUMNS_PER_ROW = 4;
+ private static final int POSITION_INTERVAL = 18;
+ private static final Size SIZE = new Size(18, 18);
+
+ protected String displayName = "";
+ @Nonnull
+ protected final IFluidTanksHandler inventory;
+ protected final Map<Fluid, IFluidTankLong> fluidToTankMap;
+ protected int tier = 0;
+ protected boolean isUpgradeInventory = false;
+
+ public FluidInventoryLogic(int numberOfSlots, long capacityOfEachTank) {
+ this(new FluidTanksHandler(numberOfSlots, capacityOfEachTank), 0, false);
+ }
+
+ public FluidInventoryLogic(int numberOfSlots, long capacityOfEachTank, int tier) {
+ this(new FluidTanksHandler(numberOfSlots, capacityOfEachTank), tier, false);
+ }
+
+ public FluidInventoryLogic(int numberOfSlots, long capacityOfEachTank, int tier, boolean isUpgradeInventory) {
+ this(new FluidTanksHandler(numberOfSlots, capacityOfEachTank), tier, isUpgradeInventory);
+ }
+
+ public FluidInventoryLogic(@Nonnull IFluidTanksHandler inventory, int tier, boolean isUpgradeInventory) {
+ this.inventory = inventory;
+ fluidToTankMap = new HashMap<>(inventory.getTanks());
+ this.tier = tier;
+ this.isUpgradeInventory = isUpgradeInventory;
+ }
+
+ public FluidInventoryLogic(Collection<IFluidTanksHandler> inventories) {
+ this(new ListFluidHandler(inventories), -1, false);
+ }
+
+ @Nullable
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public int getTier() {
+ return tier;
+ }
+
+ public boolean isUpgradeInventory() {
+ return isUpgradeInventory;
+ }
+
+ public void setDisplayName(@Nullable String displayName) {
+ this.displayName = displayName;
+ }
+
+ /**
+ *
+ * @return The Fluid Inventory Logic as an NBTTagList to be saved in another nbt as how one wants.
+ */
+ @Nonnull
+ public NBTTagCompound saveToNBT() {
+ final NBTTagCompound nbt = new NBTTagCompound();
+ final NBTTagList tList = new NBTTagList();
+ for (int tankNumber = 0; tankNumber < inventory.getTanks(); tankNumber++) {
+ final IFluidTankLong tank = inventory.getFluidTank(tankNumber);
+ if (tank == null) continue;
+
+ final NBTTagCompound tag = new NBTTagCompound();
+ tag.setByte("s", (byte) tankNumber);
+ tank.saveToNBT(tag);
+ tList.appendTag(tag);
+ }
+ nbt.setTag("inventory", tList);
+ nbt.setInteger("tier", tier);
+ if (displayName != null) {
+ nbt.setString("displayName", displayName);
+ }
+ nbt.setBoolean("isUpgradeInventory", isUpgradeInventory);
+ return nbt;
+ }
+
+ /**
+ * Loads the Item Inventory Logic from an NBTTagList.
+ */
+ public void loadFromNBT(@Nonnull NBTTagCompound nbt) {
+ NBTTagList nbtList = nbt.getTagList("inventory", Constants.NBT.TAG_COMPOUND);
+ for (int i = 0; i < nbtList.tagCount(); i++) {
+ final NBTTagCompound tankNBT = nbtList.getCompoundTagAt(i);
+ final int tank = tankNBT.getShort("s");
+ if (tank >= 0 && tank < inventory.getTanks()) inventory.getFluidTank(tank)
+ .loadFromNBT(tankNBT);
+ if (inventory.getFluidInTank(tank) != null) {
+ fluidToTankMap.put(inventory.getFluidInTank(tank), inventory.getFluidTank(tank));
+ }
+ }
+ tier = nbt.getInteger("tier");
+ if (nbt.hasKey("displayName")) {
+ displayName = nbt.getString("displayName");
+ }
+ isUpgradeInventory = nbt.getBoolean("isUpgradeInventory");
+ }
+
+ @Nonnull
+ public IFluidTanksHandler getInventory() {
+ return inventory;
+ }
+
+ @Nonnull
+ public FluidStack[] getStoredFluids() {
+ final FluidStack[] fluids = inventory.getFluids()
+ .stream()
+ .filter(fluid -> fluid != null)
+ .collect(Collectors.toList())
+ .toArray(new FluidStack[0]);
+ if (fluids == null) {
+ return new FluidStack[0];
+ }
+ return fluids;
+ }
+
+ public boolean isFluidValid(@Nullable Fluid fluid) {
+ return fluid != null;
+ }
+
+ /**
+ * @param fluid What we are trying to input
+ * @param amount amount of fluid we are trying to put
+ * @return amount of fluid filled into the tank
+ */
+ public long fill(@Nullable Fluid fluid, long amount, boolean simulate) {
+ if (!isFluidValid(fluid)) return 0;
+ IFluidTankLong tank = fluidToTankMap.get(fluid);
+ if (tank != null) {
+ return tank.fill(fluid, amount, !simulate);
+ }
+ int tankNumber = 0;
+ tank = inventory.getFluidTank(tankNumber++);
+ while (tank.getStoredFluid() != fluid && tank.getStoredFluid() != null) {
+ tank = inventory.getFluidTank(tankNumber++);
+ }
+ fluidToTankMap.put(fluid, tank);
+ return tank.fill(fluid, amount, !simulate);
+ }
+
+ @Nullable
+ public FluidStack fill(@Nullable FluidStack fluid) {
+ if (fluid == null) return null;
+ for (int i = 0; i < inventory.getTanks(); i++) {
+ fill(fluid.getFluid(), fluid.amount, false);
+ }
+ return fluid;
+ }
+
+ /**
+ * Try and drain the first fluid found for that amount. Used by GT_Cover_Pump
+ *
+ * @param amount Fluid to drain from the tank
+ * @return A fluidstack with the possible amount drained
+ */
+ @Nullable
+ public FluidStack drain(long amount, boolean simulate) {
+ for (int i = 0; i < inventory.getTanks(); i++) {
+ Fluid fluid = inventory.getFluidInTank(i);
+ FluidStack drained = drain(fluid, amount, simulate);
+ if (drained != null) return drained;
+ }
+
+ return null;
+ }
+
+ @Nullable
+ public FluidStack drain(Fluid fluid, long amount, boolean simulate) {
+ if (!isFluidValid(fluid)) return null;
+ IFluidTankLong tank = fluidToTankMap.get(fluid);
+ if (tank != null) {
+ return tank.drain(amount, !simulate);
+ }
+ int tankNumber = 0;
+ tank = inventory.getFluidTank(tankNumber++);
+ while (tank.getStoredFluid() != fluid) {
+ tank = inventory.getFluidTank(tankNumber++);
+ }
+ fluidToTankMap.put(fluid, tank);
+ return tank.drain(amount, !simulate);
+ }
+
+ public void update() {
+ for (int i = 0; i < inventory.getTanks(); i++) {
+ IFluidTankLong tank = inventory.getFluidTank(i);
+ if (tank.getFluidAmountLong() > 0) continue;
+ tank.setFluid(null, 0);
+ }
+ }
+
+ public long calculateAmountOfTimesFluidCanBeTaken(Fluid fluid, long amountToTake) {
+ if (!isFluidValid(fluid)) return 0;
+ IFluidTankLong tank = fluidToTankMap.get(fluid);
+ if (tank == null) return 0;
+ return tank.getFluidAmountLong() / amountToTake;
+ }
+
+ @Nonnull
+ public Map<Fluid, Long> getMapOfStoredFluids() {
+ Map<Fluid, Long> map = new HashMap<>();
+ for (int i = 0; i < inventory.getTanks(); i++) {
+ IFluidTankLong tank = inventory.getFluidTank(i);
+ if (tank == null) continue;
+ Fluid fluid = tank.getStoredFluid();
+ if (fluid == null) continue;
+ map.put(fluid, map.getOrDefault(fluid, 0L) + tank.getFluidAmountLong());
+ }
+ return map;
+ }
+
+ /**
+ * Return a scrollable widget with only the inventory.
+ */
+ @Nonnull
+ public Widget getGuiPart() {
+ return getGUIPart(DEFAULT_COLUMNS_PER_ROW);
+ }
+
+ /**
+ * Return a scrollable widget with only the inventory.
+ */
+ @Nonnull
+ public Widget getGUIPart(int columnsPerRow) {
+ final Scrollable scrollable = new Scrollable();
+ scrollable.setVerticalScroll();
+ for (int rows = 0; rows * 4 < inventory.getTanks(); rows++) {
+ final int columnsToMake = Math.min(inventory.getTanks() - rows * 4, 4);
+ for (int column = 0; column < columnsToMake; column++) {
+ final FluidSlotWidget fluidSlot = new FluidSlotWidget(inventory, rows * 4 + column);
+ scrollable.widget(
+ fluidSlot.setPos(column * POSITION_INTERVAL, rows * POSITION_INTERVAL)
+ .setSize(SIZE));
+ }
+ }
+ return scrollable;
+ }
+}
diff --git a/src/main/java/gregtech/api/logic/ItemInventoryLogic.java b/src/main/java/gregtech/api/logic/ItemInventoryLogic.java
new file mode 100644
index 0000000000..69005216b8
--- /dev/null
+++ b/src/main/java/gregtech/api/logic/ItemInventoryLogic.java
@@ -0,0 +1,314 @@
+package gregtech.api.logic;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraftforge.common.util.Constants;
+
+import com.gtnewhorizons.modularui.api.forge.IItemHandlerModifiable;
+import com.gtnewhorizons.modularui.api.forge.ItemStackHandler;
+import com.gtnewhorizons.modularui.api.forge.ListItemHandler;
+import com.gtnewhorizons.modularui.api.math.Size;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.widget.Scrollable;
+import com.gtnewhorizons.modularui.common.widget.SlotWidget;
+
+import gregtech.api.util.GT_Utility;
+import gregtech.api.util.item.ItemHolder;
+
+/**
+ * Generic Item logic for MuTEs.
+ *
+ * @author BlueWeabo
+ */
+public class ItemInventoryLogic {
+
+ private static final int DEFAULT_COLUMNS_PER_ROW = 4;
+ private static final int POSITION_INTERVAL = 18;
+ private static final Size SIZE = new Size(18, 18);
+
+ protected String displayName;
+ @Nonnull
+ protected final IItemHandlerModifiable inventory;
+ protected UUID connectedFluidInventory;
+ protected int tier;
+ protected boolean isUpgradeInventory;
+ protected Map<ItemHolder, Long> cachedItemMap;
+ protected boolean inRecipeCheck;
+
+ public ItemInventoryLogic(int numberOfSlots) {
+ this(numberOfSlots, 0);
+ }
+
+ public ItemInventoryLogic(int numberOfSlots, int tier) {
+ this(new ItemStackHandler(numberOfSlots), tier, false);
+ }
+
+ public ItemInventoryLogic(int numberOfSlots, int tier, boolean isUpgradeInventory) {
+ this(new ItemStackHandler(numberOfSlots), tier, isUpgradeInventory);
+ }
+
+ public ItemInventoryLogic(@Nonnull IItemHandlerModifiable inventory, int tier, boolean isUpgradeInventory) {
+ this.inventory = inventory;
+ this.tier = tier;
+ this.isUpgradeInventory = isUpgradeInventory;
+ }
+
+ public ItemInventoryLogic(Collection<IItemHandlerModifiable> inventories) {
+ this(new ListItemHandler(inventories), -1, false);
+ }
+
+ @Nullable
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public int getTier() {
+ return tier;
+ }
+
+ public boolean isUpgradeInventory() {
+ return isUpgradeInventory;
+ }
+
+ public int getSlots() {
+ return getInventory().getSlots();
+ }
+
+ public void setDisplayName(@Nullable String displayName) {
+ this.displayName = displayName;
+ }
+
+ @Nullable
+ public UUID getConnectedFluidInventoryID() {
+ return connectedFluidInventory;
+ }
+
+ public void setConnectedFluidInventoryID(@Nullable UUID connectedFluidTank) {
+ this.connectedFluidInventory = connectedFluidTank;
+ }
+
+ /**
+ *
+ * @return The Item Inventory Logic as an NBTTagCompound to be saved in another nbt as how one wants.
+ */
+ @Nonnull
+ public NBTTagCompound saveToNBT() {
+ final NBTTagCompound nbt = new NBTTagCompound();
+ final NBTTagList tList = new NBTTagList();
+ for (int slot = 0; slot < inventory.getSlots(); slot++) {
+ final ItemStack tStack = inventory.getStackInSlot(slot);
+ if (tStack == null) continue;
+
+ final NBTTagCompound tag = new NBTTagCompound();
+ tag.setByte("s", (byte) slot);
+ tStack.writeToNBT(tag);
+ tList.appendTag(tag);
+ }
+ nbt.setTag("inventory", tList);
+ nbt.setInteger("tier", tier);
+ if (displayName != null) {
+ nbt.setString("displayName", displayName);
+ }
+ nbt.setBoolean("isUpgradeInventory", isUpgradeInventory);
+ if (connectedFluidInventory != null) {
+ nbt.setString("connectedFluidInventory", connectedFluidInventory.toString());
+ }
+ return nbt;
+ }
+
+ /**
+ * Loads the Item Inventory Logic from an NBTTagCompound.
+ */
+ public void loadFromNBT(@Nonnull NBTTagCompound nbt) {
+ tier = nbt.getInteger("tier");
+ if (nbt.hasKey("displayName")) {
+ displayName = nbt.getString("displayName");
+ }
+
+ isUpgradeInventory = nbt.getBoolean("isUpgradeInventory");
+ if (nbt.hasKey("connectedFluidInventory")) {
+ connectedFluidInventory = UUID.fromString(nbt.getString("connectedFluidInventory"));
+ }
+
+ NBTTagList nbtList = nbt.getTagList("inventory", Constants.NBT.TAG_COMPOUND);
+ if (nbtList == null) return;
+
+ for (int i = 0; i < nbtList.tagCount(); i++) {
+ final NBTTagCompound tNBT = nbtList.getCompoundTagAt(i);
+ final int tSlot = tNBT.getShort("s");
+ if (tSlot >= 0 && tSlot < inventory.getSlots()) {
+ inventory.setStackInSlot(tSlot, GT_Utility.loadItem(tNBT));
+ }
+ }
+ }
+
+ @Nonnull
+ public IItemHandlerModifiable getInventory() {
+ return inventory;
+ }
+
+ @Nonnull
+ public ItemStack[] getStoredItems() {
+ final ItemStack[] items = inventory.getStacks()
+ .stream()
+ .filter(item -> item != null)
+ .collect(Collectors.toList())
+ .toArray(new ItemStack[0]);
+ if (items == null) {
+ return new ItemStack[0];
+ }
+ return items;
+ }
+
+ public boolean isStackValid(ItemStack item) {
+ return true;
+ }
+
+ @Nullable
+ public ItemStack insertItem(ItemStack item) {
+ if (!isStackValid(item)) return item;
+ for (int i = 0; i < inventory.getSlots() && item != null && item.stackSize > 0; i++) {
+ item = inventory.insertItem(i, item, false);
+ }
+ return item;
+ }
+
+ @Nullable
+ public ItemStack extractItem(int slot, int amount) {
+ return inventory.extractItem(slot, amount, false);
+ }
+
+ public boolean subtractItemAmount(@Nonnull ItemHolder item, long amount, boolean simulate) {
+ Map<ItemHolder, Long> itemMap = getMapOfStoredItems();
+ if (!itemMap.containsKey(item)) {
+ return false;
+ }
+
+ if (itemMap.get(item) < amount) {
+ return false;
+ }
+
+ if (simulate) {
+ return true;
+ }
+
+ itemMap.put(item, itemMap.get(item) - amount);
+ return true;
+ }
+
+ @Nullable
+ public ItemStack getItemInSlot(int slot) {
+ return inventory.getStackInSlot(slot);
+ }
+
+ public void sort() {
+ Map<ItemHolder, Long> itemMap = getMapOfStoredItems();
+ List<ItemHolder> sortedItems = itemMap.keySet()
+ .stream()
+ .sorted(
+ Comparator.comparing(
+ a -> a.getItem()
+ .getUnlocalizedName() + a.getMeta()))
+ .collect(Collectors.toList());
+ putInItemsFromMap(itemMap, sortedItems);
+ }
+
+ public void update(boolean shouldSort) {
+ if (shouldSort) {
+ sort();
+ }
+
+ for (int i = 0; i < inventory.getSlots(); i++) {
+ ItemStack item = inventory.getStackInSlot(i);
+ if (item == null) continue;
+ if (item.stackSize > 0) continue;
+ inventory.setStackInSlot(i, null);
+ }
+ }
+
+ /**
+ * Return a scrollable widget with only the inventory.
+ */
+ @Nonnull
+ public Widget getGuiPart() {
+ return getGUIPart(DEFAULT_COLUMNS_PER_ROW);
+ }
+
+ /**
+ * Return a scrollable widget with only the inventory.
+ */
+ @Nonnull
+ public Widget getGUIPart(int columnsPerRow) {
+ final Scrollable scrollable = new Scrollable();
+ scrollable.setVerticalScroll();
+ for (int rows = 0; rows * columnsPerRow < Math.min(inventory.getSlots(), 128); rows++) {
+ final int columnsToMake = Math
+ .min(Math.min(inventory.getSlots(), 128) - rows * columnsPerRow, columnsPerRow);
+ for (int column = 0; column < columnsToMake; column++) {
+ scrollable.widget(
+ new SlotWidget(inventory, rows * columnsPerRow + column)
+ .setPos(column * POSITION_INTERVAL, rows * POSITION_INTERVAL)
+ .setSize(SIZE));
+ }
+ }
+ return scrollable;
+ }
+
+ public void startRecipeCheck() {
+ cachedItemMap = getMapOfStoredItems();
+ inRecipeCheck = true;
+ }
+
+ public void stopRecipeCheck() {
+ inRecipeCheck = false;
+ putInItemsFromMap(cachedItemMap, null);
+ cachedItemMap = null;
+ }
+
+ @Nonnull
+ public Map<ItemHolder, Long> getMapOfStoredItems() {
+ if (inRecipeCheck) return cachedItemMap;
+ Map<ItemHolder, Long> items = new HashMap<>();
+ for (int i = 0; i < inventory.getSlots(); i++) {
+ ItemStack item = extractItem(i, Integer.MAX_VALUE);
+ if (item == null) continue;
+ ItemHolder itemHolder = new ItemHolder(item);
+ items.put(itemHolder, items.getOrDefault(itemHolder, 0L) + item.stackSize);
+ }
+ return items;
+ }
+
+ protected void putInItemsFromMap(@Nonnull Map<ItemHolder, Long> itemMap, @Nullable List<ItemHolder> sortedList) {
+ for (ItemHolder itemHolder : (sortedList == null ? itemMap.keySet() : sortedList)) {
+ long itemAmount = itemMap.get(itemHolder);
+ ItemStack item = new ItemStack(itemHolder.getItem(), 0, itemHolder.getMeta());
+ item.setTagCompound(itemHolder.getNBT());
+ while (itemAmount > 0) {
+ item.stackSize = (int) Math.min(item.getMaxStackSize(), itemAmount);
+ itemAmount -= item.stackSize;
+ insertItem(item);
+ }
+ }
+ }
+
+ public long calculateAmountOfTimesItemCanBeTaken(ItemHolder item, long amount) {
+ return getMapOfStoredItems().getOrDefault(item, 0L) / amount;
+ }
+
+ public Set<ItemHolder> getSetOfStoredItems() {
+ return getMapOfStoredItems().keySet();
+ }
+}
diff --git a/src/main/java/gregtech/api/logic/MuTEProcessingLogic.java b/src/main/java/gregtech/api/logic/MuTEProcessingLogic.java
new file mode 100644
index 0000000000..da53c8875d
--- /dev/null
+++ b/src/main/java/gregtech/api/logic/MuTEProcessingLogic.java
@@ -0,0 +1,256 @@
+package gregtech.api.logic;
+
+import static net.minecraftforge.common.util.Constants.NBT.TAG_COMPOUND;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraftforge.fluids.FluidStack;
+
+import com.gtnewhorizons.modularui.api.screen.ModularWindow;
+import com.gtnewhorizons.modularui.api.widget.Widget;
+import com.gtnewhorizons.modularui.common.widget.Scrollable;
+
+import gregtech.api.enums.InventoryType;
+import gregtech.api.logic.interfaces.ProcessingLogicHost;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_ParallelHelper;
+import gregtech.api.util.GT_Recipe;
+import gregtech.api.util.GT_Utility;
+
+/**
+ * Processing logic class, dedicated for MultiTileEntities.
+ */
+public class MuTEProcessingLogic<P extends MuTEProcessingLogic<P>> extends AbstractProcessingLogic<P> {
+
+ protected boolean hasWork;
+ protected int progress;
+ protected ProcessingLogicHost<P> machineHost;
+ @Nonnull
+ protected CheckRecipeResult recipeResult = CheckRecipeResultRegistry.NONE;
+ @Nullable
+ protected UUID itemOutputID;
+ @Nullable
+ protected UUID fluidOutputID;
+
+ public P setMachineHost(@Nonnull ProcessingLogicHost<P> machineHost) {
+ this.machineHost = machineHost;
+ return getThis();
+ }
+
+ // #region Logic
+
+ @Nonnull
+ @Override
+ public CheckRecipeResult process() {
+ RecipeMap<?> recipeMap = preProcess();
+
+ ItemInventoryLogic itemInput = null;
+ FluidInventoryLogic fluidInput = null;
+ if (machineHost.isInputSeparated()) {
+ for (Map.Entry<UUID, ItemInventoryLogic> itemEntry : machineHost
+ .getAllItemInventoryLogics(InventoryType.Input)) {
+ itemOutputID = Objects.requireNonNull(itemEntry.getKey());
+ itemInput = Objects.requireNonNull(itemEntry.getValue());
+ fluidInput = Objects.requireNonNull(
+ machineHost.getFluidLogic(InventoryType.Input, itemInput.getConnectedFluidInventoryID()));
+ fluidOutputID = itemInput.getConnectedFluidInventoryID();
+ }
+ } else {
+ itemInput = Objects.requireNonNull(machineHost.getItemLogic(InventoryType.Input, null));
+ fluidInput = Objects.requireNonNull(machineHost.getFluidLogic(InventoryType.Input, null));
+ }
+
+ CheckRecipeResult recipeValidatorResult = null;
+ if (recipeValidatorResult != null) {
+ return recipeValidatorResult;
+ }
+
+ return processRecipe(null, Objects.requireNonNull(itemInput), Objects.requireNonNull(fluidInput));
+ }
+
+ @Nonnull
+ protected CheckRecipeResult processRecipe(@Nonnull List<GT_Recipe> recipes, @Nonnull ItemInventoryLogic itemInput,
+ @Nonnull FluidInventoryLogic fluidInput) {
+ CheckRecipeResult result = CheckRecipeResultRegistry.INTERNAL_ERROR;
+ for (GT_Recipe recipe : recipes) {
+ Objects.requireNonNull(recipe);
+ GT_ParallelHelper helper = createParallelHelper(recipe, itemInput, fluidInput);
+ GT_OverclockCalculator calculator = createOverclockCalculator(recipe);
+ helper.setCalculator(calculator);
+ helper.build();
+ result = helper.getResult();
+ if (result.wasSuccessful()) {
+ return applyRecipe(recipe, helper, calculator, result);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Override if you don't work with regular gt recipe maps
+ */
+ @Nonnull
+ protected Object findRecipe(@Nullable RecipeMap<?> map, @Nonnull ItemInventoryLogic itemInput,
+ @Nonnull FluidInventoryLogic fluidInput) {
+ if (map == null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Nonnull
+ protected GT_ParallelHelper createParallelHelper(@Nonnull GT_Recipe recipe, @Nonnull ItemInventoryLogic itemInput,
+ @Nonnull FluidInventoryLogic fluidInput) {
+ return new GT_ParallelHelper().setRecipe(recipe)
+ .setItemInputInventory(itemInput)
+ .setFluidInputInventory(fluidInput)
+ .setAvailableEUt(availableVoltage * availableAmperage)
+ .setMaxParallel(maxParallel)
+ .setEUtModifier(euModifier)
+ .enableBatchMode(batchSize)
+ .setConsumption(true)
+ .setOutputCalculation(true)
+ .setMuTEMode(true);
+ }
+
+ // #endregion
+
+ // #region Getters
+
+ @Nonnull
+ public CheckRecipeResult getResult() {
+ return recipeResult;
+ }
+
+ public int getProgress() {
+ return progress;
+ }
+
+ // #endregion
+
+ // #region Other
+
+ public void startCheck() {
+ recipeResult = process();
+ }
+
+ public void progress() {
+ if (!hasWork) return;
+ if (progress == duration) {
+ progress = 0;
+ duration = 0;
+ calculatedEut = 0;
+ output();
+ return;
+ }
+ progress++;
+ }
+
+ protected void output() {
+ ItemInventoryLogic itemOutput = machineHost.getItemLogic(InventoryType.Output, itemOutputID);
+ FluidInventoryLogic fluidOutput = machineHost.getFluidLogic(InventoryType.Output, fluidOutputID);
+ if (itemOutput == null || fluidOutput == null) return;
+ for (ItemStack item : outputItems) {
+ if (item == null) continue;
+ itemOutput.insertItem(item);
+ }
+ for (FluidStack fluid : outputFluids) {
+ if (fluid == null) continue;
+ fluidOutput.fill(fluid.getFluid(), fluid.amount, false);
+ }
+ outputItems = new ItemStack[0];
+ outputFluids = new FluidStack[0];
+ }
+
+ public boolean canWork() {
+ return !hasWork && machineHost.isAllowedToWork();
+ }
+
+ /**
+ * By how much to increase the progress?
+ *
+ * @param progressAmount in ticks
+ */
+ public void increaseProgress(int progressAmount) {
+ progress += progressAmount;
+ }
+
+ public NBTTagCompound saveToNBT() {
+ NBTTagCompound logicNBT = new NBTTagCompound();
+ logicNBT.setLong("eutConsumption", calculatedEut);
+ logicNBT.setInteger("duration", duration);
+ logicNBT.setInteger("progress", progress);
+ logicNBT.setBoolean("hasWork", hasWork);
+ if (outputItems != null) {
+ NBTTagList itemOutputsNBT = new NBTTagList();
+ for (ItemStack item : outputItems) {
+ itemOutputsNBT.appendTag(GT_Utility.saveItem(item));
+ }
+ logicNBT.setTag("itemOutputs", itemOutputsNBT);
+ }
+ if (outputFluids != null) {
+ NBTTagList fluidOutputsNBT = new NBTTagList();
+ for (FluidStack fluid : outputFluids) {
+ fluidOutputsNBT.appendTag(fluid.writeToNBT(new NBTTagCompound()));
+ }
+ logicNBT.setTag("fluidOutputs", fluidOutputsNBT);
+ }
+ if (itemOutputID != null) {
+ logicNBT.setString("itemOutputID", itemOutputID.toString());
+ }
+ if (fluidOutputID != null) {
+ logicNBT.setString("fluidOutputID", fluidOutputID.toString());
+ }
+ return logicNBT;
+ }
+
+ public void loadFromNBT(@Nonnull NBTTagCompound logicNBT) {
+ calculatedEut = logicNBT.getLong("eutConsumption");
+ duration = logicNBT.getInteger("duration");
+ progress = logicNBT.getInteger("progress");
+ hasWork = logicNBT.getBoolean("hasWork");
+ if (logicNBT.hasKey("itemOutputs")) {
+ NBTTagList itemOutputsNBT = logicNBT.getTagList("itemOutputs", TAG_COMPOUND);
+ outputItems = new ItemStack[itemOutputsNBT.tagCount()];
+ for (int i = 0; i < itemOutputsNBT.tagCount(); i++) {
+ outputItems[i] = GT_Utility.loadItem(itemOutputsNBT.getCompoundTagAt(i));
+ }
+ }
+ if (logicNBT.hasKey("fluidOutputs")) {
+ NBTTagList fluidOutputsNBT = logicNBT.getTagList("fluidOutputs", TAG_COMPOUND);
+ outputFluids = new FluidStack[fluidOutputsNBT.tagCount()];
+ for (int i = 0; i < fluidOutputsNBT.tagCount(); i++) {
+ outputFluids[i] = FluidStack.loadFluidStackFromNBT(fluidOutputsNBT.getCompoundTagAt(i));
+ }
+ }
+ if (logicNBT.hasKey("itemOutputID")) {
+ itemOutputID = UUID.fromString(logicNBT.getString("itemOutputID"));
+ }
+ if (logicNBT.hasKey("fluidOutputID")) {
+ fluidOutputID = UUID.fromString(logicNBT.getString("fluidOutputID"));
+ }
+ }
+
+ /**
+ * Returns a gui part, which will be displayed in a separate tab on the machine's gui.
+ */
+ @Nonnull
+ public Widget getGUIPart(ModularWindow.Builder builder) {
+ return new Scrollable();
+ }
+
+ // #endregion
+}
diff --git a/src/main/java/gregtech/api/logic/NullPowerLogic.java b/src/main/java/gregtech/api/logic/NullPowerLogic.java
new file mode 100644
index 0000000000..0017f0e647
--- /dev/null
+++ b/src/main/java/gregtech/api/logic/NullPowerLogic.java
@@ -0,0 +1,5 @@
+package gregtech.api.logic;
+
+public class NullPowerLogic extends PowerLogic {
+
+}
diff --git a/src/main/java/gregtech/api/logic/PollutionLogic.java b/src/main/java/gregtech/api/logic/PollutionLogic.java
deleted file mode 100644
index 8e1172e105..0000000000
--- a/src/main/java/gregtech/api/logic/PollutionLogic.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package gregtech.api.logic;
-
-public class PollutionLogic {
-
- private int pollutionAmount;
-
- public PollutionLogic() {}
-
- public PollutionLogic setPollutionAmount(int pollutionAmount) {
- this.pollutionAmount = pollutionAmount;
- return this;
- }
-
- public int getPollutionAmount() {
- return pollutionAmount;
- }
-}
diff --git a/src/main/java/gregtech/api/logic/PowerLogic.java b/src/main/java/gregtech/api/logic/PowerLogic.java
index ac12ef8917..ad19987a76 100644
--- a/src/main/java/gregtech/api/logic/PowerLogic.java
+++ b/src/main/java/gregtech/api/logic/PowerLogic.java
@@ -1,54 +1,97 @@
package gregtech.api.logic;
+import static gregtech.common.misc.WirelessNetworkManager.addEUToGlobalEnergyMap;
+
+import java.util.UUID;
+
+import javax.annotation.Nonnull;
+
import net.minecraft.nbt.NBTTagCompound;
import gregtech.api.enums.GT_Values.NBT;
+/**
+ * Power logic for machines. This is used to store all the important variables for a machine to have energy and use it
+ * in any way.
+ *
+ * @author BlueWeabo, Maxim
+ */
public class PowerLogic {
- public static int NONE = 0;
- public static int RECEIVER = 1;
- public static int EMITTER = 2;
- public static int BOTH = RECEIVER | EMITTER;
+ public static final int NONE = 0;
+ public static final int RECEIVER = 1;
+ public static final int EMITTER = 2;
+ public static final int BOTH = RECEIVER | EMITTER;
+ private static float wirelessChargeFactor = 0.5F;
private long storedEnergy = 0;
private long energyCapacity = 0;
private long voltage = 0;
private long amperage = 0;
private int type = 0;
private boolean canUseLaser = false;
+ private boolean canUseWireless = false;
+ private UUID owner;
public PowerLogic() {}
+ /**
+ * Sets the max voltage the logic can accept
+ */
+ @Nonnull
public PowerLogic setMaxVoltage(long voltage) {
this.voltage = voltage;
return this;
}
+ /**
+ * Sets the maximum amount of energy the machine can store inside of it
+ */
+ @Nonnull
public PowerLogic setEnergyCapacity(long energyCapacity) {
this.energyCapacity = energyCapacity;
return this;
}
- public PowerLogic setAmperage(long amperage) {
+ /**
+ * Sets the maximum amount of amps a machine can receive from an emitter
+ */
+ @Nonnull
+ public PowerLogic setMaxAmperage(long amperage) {
this.amperage = amperage;
return this;
}
+ /**
+ * Sets the type of power logic this is. Whether it will receive EU or emit it to others, or do both
+ */
+ @Nonnull
public PowerLogic setType(int type) {
this.type = type;
return this;
}
- public PowerLogic disableLaser() {
- canUseLaser = false;
+ /**
+ * If this power logic can use lasers to be used for it
+ */
+ @Nonnull
+ public PowerLogic setCanUseLaser(boolean canUse) {
+ canUseLaser = canUse;
return this;
}
- public PowerLogic enableLaser() {
- canUseLaser = true;
+ /**
+ * If the power logic should use wireless EU first before using its internal buffer
+ */
+ @Nonnull
+ public PowerLogic setCanUseWireless(boolean canUse, UUID owner) {
+ canUseWireless = canUse;
+ this.owner = owner;
return this;
}
+ /**
+ * Adding energy directly to the buffer, but only if it has the capacity.
+ */
public boolean addEnergyUnsafe(long totalEUAdded) {
if (storedEnergy + totalEUAdded >= energyCapacity) {
return false;
@@ -58,6 +101,9 @@ public class PowerLogic {
return true;
}
+ /**
+ * Adding energy to the buffer if the voltage given isn't higher than the voltage of the logic
+ */
public boolean addEnergy(long voltage, long amperage) {
if (voltage > this.voltage) {
return false;
@@ -66,11 +112,23 @@ public class PowerLogic {
return addEnergyUnsafe(voltage * amperage);
}
+ /**
+ * Same as {@link #addEnergy(long, long)}, but only 1 amp of it
+ */
public boolean addEnergy(long voltage) {
return addEnergy(voltage, 1);
}
+ /**
+ * Injecting energy in the multiblock ampere per ampere until full or until we have added the maximum possible
+ * amperes for this tick
+ *
+ * @param voltage At what voltage are the amps?
+ * @param availableAmperage How much amperage do we have available
+ * @return Amount of amperes used
+ */
public long injectEnergy(long voltage, long availableAmperage) {
+ if (canUseWireless) return 0;
long usedAmperes = 0;
while (addEnergy(voltage, 1) && usedAmperes < amperage) {
usedAmperes++;
@@ -79,7 +137,17 @@ public class PowerLogic {
return usedAmperes;
}
+ /**
+ * Remove energy from the logic only if it has enough to be removed.
+ */
public boolean removeEnergyUnsafe(long totalEURemoved) {
+ if (canUseWireless) {
+ if (storedEnergy < energyCapacity * wirelessChargeFactor) {
+ if (addEUToGlobalEnergyMap(owner, -(energyCapacity - storedEnergy))) {
+ storedEnergy = energyCapacity;
+ }
+ }
+ }
if (storedEnergy - totalEURemoved < 0) {
return false;
}
@@ -88,6 +156,9 @@ public class PowerLogic {
return true;
}
+ /**
+ * Remove the given voltage for the amount of amperage if the removed isn't higher than the logic's voltage
+ */
public boolean removeEnergy(long voltage, long amperage) {
if (voltage > this.voltage) {
return false;
@@ -96,31 +167,61 @@ public class PowerLogic {
return removeEnergyUnsafe(voltage * amperage);
}
+ /**
+ * Same as {@link #removeEnergy(long, long)}, but with only 1 amperage
+ */
public boolean removeEnergy(long voltage) {
return removeEnergy(voltage, 1);
}
+ /**
+ * @return The maximum energy that can be stored.
+ */
public long getCapacity() {
return energyCapacity;
}
+ /**
+ * @return The maximum voltage that is available
+ */
public long getVoltage() {
return voltage;
}
+ /**
+ * @return The current energy stored
+ */
public long getStoredEnergy() {
return storedEnergy;
}
+ /**
+ * @return The current maximum Amperage
+ */
+ public long getMaxAmperage() {
+ return amperage;
+ }
+
+ /**
+ * Is the logic a receiver to receive energy
+ */
public boolean isEnergyReceiver() {
return (type & RECEIVER) > 0;
}
+ /**
+ * Is the logic a emitter to emit energy
+ */
public boolean isEnergyEmitter() {
return (type & EMITTER) > 0;
}
- public void writeToNBT(NBTTagCompound nbt) {
+ /**
+ * Saves the power logic to its own nbt tag before saving it to the given one.
+ *
+ * @param nbt Tag where you want to save the power logic tag to.
+ */
+ public void saveToNBT(NBTTagCompound nbt) {
NBTTagCompound powerLogic = new NBTTagCompound();
powerLogic.setLong(NBT.POWER_LOGIC_ENERGY_CAPACITY, energyCapacity);
powerLogic.setLong(NBT.POWER_LOGIC_STORED_ENERGY, storedEnergy);
@@ -130,6 +231,11 @@ public class PowerLogic {
nbt.setTag(NBT.POWER_LOGIC, powerLogic);
}
+ /**
+ * Loads the power logic from its own nbt after getting it from the given one
+ *
+ * @param nbt Tag where the power logic tag was saved to
+ */
public void loadFromNBT(NBTTagCompound nbt) {
NBTTagCompound powerLogic = nbt.getCompoundTag(NBT.POWER_LOGIC);
energyCapacity = powerLogic.getLong(NBT.POWER_LOGIC_ENERGY_CAPACITY);
@@ -139,6 +245,9 @@ public class PowerLogic {
type = powerLogic.getInteger(NBT.POWER_LOGIC_TYPE);
}
+ /**
+ * Can we use lasers for inputting EU
+ */
public boolean canUseLaser() {
return canUseLaser;
}
diff --git a/src/main/java/gregtech/api/logic/ProcessingLogic.java b/src/main/java/gregtech/api/logic/ProcessingLogic.java
index 6b9f2d454f..4d203ed80f 100644
--- a/src/main/java/gregtech/api/logic/ProcessingLogic.java
+++ b/src/main/java/gregtech/api/logic/ProcessingLogic.java
@@ -1,7 +1,6 @@
package gregtech.api.logic;
import java.util.List;
-import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
@@ -10,10 +9,7 @@ import javax.annotation.Nullable;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.FluidStack;
-import org.jetbrains.annotations.NotNull;
-
import gregtech.api.interfaces.tileentity.IRecipeLockable;
-import gregtech.api.interfaces.tileentity.IVoidable;
import gregtech.api.recipe.RecipeMap;
import gregtech.api.recipe.check.CheckRecipeResult;
import gregtech.api.recipe.check.CheckRecipeResultRegistry;
@@ -26,90 +22,45 @@ import gregtech.api.util.GT_Recipe;
* Logic class to calculate result of recipe check from inputs, based on recipemap.
*/
@SuppressWarnings({ "unused", "UnusedReturnValue" })
-public class ProcessingLogic {
+public class ProcessingLogic extends AbstractProcessingLogic<ProcessingLogic> {
- protected IVoidable machine;
protected IRecipeLockable recipeLockableMachine;
- protected Supplier<RecipeMap<?>> recipeMapSupplier;
- protected GT_Recipe lastRecipe;
- protected RecipeMap<?> lastRecipeMap;
protected ItemStack specialSlotItem;
protected ItemStack[] inputItems;
- protected ItemStack[] outputItems;
- protected ItemStack[] currentOutputItems;
protected FluidStack[] inputFluids;
- protected FluidStack[] outputFluids;
- protected FluidStack[] currentOutputFluids;
- 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;
- protected float euModifier = 1.0f;
- protected float speedBoost = 1.0f;
- protected boolean amperageOC = true;
public ProcessingLogic() {}
- // region Setters
+ // #region Setters
+ @Nonnull
public ProcessingLogic setInputItems(ItemStack... itemInputs) {
this.inputItems = itemInputs;
- return this;
+ return getThis();
}
+ @Nonnull
public ProcessingLogic setInputItems(List<ItemStack> itemOutputs) {
this.inputItems = itemOutputs.toArray(new ItemStack[0]);
- return this;
+ return getThis();
}
+ @Nonnull
public ProcessingLogic setInputFluids(FluidStack... fluidInputs) {
this.inputFluids = fluidInputs;
- return this;
+ return getThis();
}
+ @Nonnull
public ProcessingLogic setInputFluids(List<FluidStack> fluidInputs) {
this.inputFluids = fluidInputs.toArray(new FluidStack[0]);
- return this;
+ return getThis();
}
public ProcessingLogic setSpecialSlotItem(ItemStack specialSlotItem) {
this.specialSlotItem = specialSlotItem;
- 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;
- }
-
- public ProcessingLogic setCurrentOutputItems(ItemStack... currentOutputItems) {
- this.currentOutputItems = currentOutputItems;
- return this;
- }
-
- public ProcessingLogic setCurrentOutputFluids(FluidStack... currentOutputFluids) {
- this.currentOutputFluids = currentOutputFluids;
- return this;
+ return getThis();
}
/**
@@ -118,129 +69,13 @@ public class ProcessingLogic {
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(RecipeMap<?> recipeMap) {
- return setRecipeMapSupplier(() -> recipeMap);
- }
-
- public ProcessingLogic setRecipeMapSupplier(Supplier<RecipeMap<?>> supplier) {
- this.recipeMapSupplier = supplier;
- return this;
- }
-
- public ProcessingLogic setEuModifier(float modifier) {
- this.euModifier = modifier;
- return this;
- }
-
- public ProcessingLogic setSpeedBonus(float speedModifier) {
- this.speedBoost = speedModifier;
- return this;
- }
-
- /**
- * Sets machine used for void protection logic.
- */
- public ProcessingLogic setMachine(IVoidable machine) {
- this.machine = machine;
- return this;
- }
-
- /**
- * Overwrites duration result of the calculation.
- */
- public ProcessingLogic setDuration(int duration) {
- this.duration = duration;
- return this;
- }
-
- /**
- * 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);
- }
-
- /**
- * Sets wether the multi should use amperage to OC or not
- */
- public ProcessingLogic setAmperageOC(boolean amperageOC) {
- this.amperageOC = amperageOC;
- return this;
+ return getThis();
}
/**
* Clears calculated results and provided machine inputs to prepare for the next machine operation.
*/
+
public ProcessingLogic clear() {
this.inputItems = null;
this.inputFluids = null;
@@ -250,32 +85,19 @@ public class ProcessingLogic {
this.calculatedEut = 0;
this.duration = 0;
this.calculatedParallels = 0;
- return this;
+ return getThis();
}
- // endregion
+ // #endregion
- // region Logic
+ // #region Logic
/**
* Executes the recipe check: Find recipe from recipemap, Calculate parallel, overclock and outputs.
*/
@Nonnull
public CheckRecipeResult process() {
- RecipeMap<?> recipeMap;
- if (recipeMapSupplier == null) {
- recipeMap = null;
- } else {
- recipeMap = recipeMapSupplier.get();
- }
- if (lastRecipeMap != recipeMap) {
- lastRecipe = null;
- lastRecipeMap = recipeMap;
- }
-
- if (maxParallelSupplier != null) {
- maxParallel = maxParallelSupplier.get();
- }
+ RecipeMap<?> recipeMap = preProcess();
if (inputItems == null) {
inputItems = new ItemStack[0];
@@ -297,7 +119,6 @@ public class ProcessingLogic {
recipeLockableMachine.getSingleRecipeCheck()
.getRecipe()).checkRecipeResult;
}
-
Stream<GT_Recipe> matchedRecipes = findRecipeMatches(recipeMap);
Iterable<GT_Recipe> recipeIterable = matchedRecipes::iterator;
CheckRecipeResult checkRecipeResult = CheckRecipeResultRegistry.NO_RECIPE;
@@ -342,53 +163,6 @@ public class ProcessingLogic {
}
/**
- * Check has been succeeded, so it applies the recipe and calculated parameters.
- * At this point, inputs have been already consumed.
- */
- private CheckRecipeResult applyRecipe(@NotNull GT_Recipe recipe, GT_ParallelHelper helper,
- GT_OverclockCalculator calculator, CheckRecipeResult result) {
- if (recipe.mCanBeBuffered) {
- lastRecipe = recipe;
- } else {
- lastRecipe = null;
- }
- calculatedParallels = helper.getCurrentParallel();
-
- 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;
-
- CheckRecipeResult hookResult = onRecipeStart(recipe);
- if (!hookResult.wasSuccessful()) {
- return hookResult;
- }
-
- outputItems = helper.getItemOutputs();
- outputFluids = helper.getFluidOutputs();
-
- return result;
- }
-
- /**
- * 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();
- }
-
- /**
* Finds a list of matched recipes. At this point no additional check to the matched recipe has been done.
* <p>
* Override {@link #validateRecipe} to have custom check.
@@ -409,14 +183,6 @@ public class ProcessingLogic {
}
/**
- * Override to do additional check for found recipe if needed.
- */
- @Nonnull
- protected CheckRecipeResult validateRecipe(@Nonnull GT_Recipe recipe) {
- return CheckRecipeResultRegistry.SUCCESSFUL;
- }
-
- /**
* Override to tweak parallel logic if needed.
*/
@Nonnull
@@ -434,60 +200,7 @@ public class ProcessingLogic {
.setOutputCalculation(true);
}
- /**
- * Override to tweak overclock logic if needed.
- */
- @Nonnull
- protected GT_OverclockCalculator createOverclockCalculator(@Nonnull GT_Recipe recipe) {
- return new GT_OverclockCalculator().setRecipeEUt(recipe.mEUt)
- .setAmperage(availableAmperage)
- .setEUt(availableVoltage)
- .setDuration(recipe.mDuration)
- .setSpeedBoost(speedBoost)
- .setEUtDiscount(euModifier)
- .setAmperageOC(amperageOC)
- .setDurationDecreasePerOC(overClockTimeReduction)
- .setEUtIncreasePerOC(overClockPowerIncrease);
- }
-
- /**
- * Override to perform additional logic when recipe starts.
- *
- * This is called when the recipe processing logic has finished all
- * checks, consumed all inputs, but has not yet set the outputs to
- * be produced. Returning a result other than SUCCESSFUL will void
- * all inputs!
- */
- @Nonnull
- protected CheckRecipeResult onRecipeStart(@Nonnull GT_Recipe recipe) {
- return CheckRecipeResultRegistry.SUCCESSFUL;
- }
-
- // endregion
-
- // region Getters
-
- public ItemStack[] getOutputItems() {
- return outputItems;
- }
-
- public FluidStack[] getOutputFluids() {
- return outputFluids;
- }
-
- public int getDuration() {
- return duration;
- }
-
- public long getCalculatedEut() {
- return calculatedEut;
- }
-
- public int getCurrentParallels() {
- return calculatedParallels;
- }
-
- // endregion
+ // #endregion
/**
* Represents the status of check recipe calculation. {@link #successfullyConsumedInputs} does not necessarily mean
diff --git a/src/main/java/gregtech/api/logic/interfaces/FluidInventoryLogicHost.java b/src/main/java/gregtech/api/logic/interfaces/FluidInventoryLogicHost.java
new file mode 100644
index 0000000000..c12333a4c6
--- /dev/null
+++ b/src/main/java/gregtech/api/logic/interfaces/FluidInventoryLogicHost.java
@@ -0,0 +1,95 @@
+package gregtech.api.logic.interfaces;
+
+import static com.google.common.primitives.Ints.saturatedCast;
+
+import java.util.HashSet;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+import net.minecraftforge.fluids.IFluidHandler;
+
+import gregtech.api.enums.InventoryType;
+import gregtech.api.logic.FluidInventoryLogic;
+
+public interface FluidInventoryLogicHost extends IFluidHandler {
+
+ /**
+ * To be used for single blocks or when directly interacting with the controller
+ *
+ * @param side The side from where fluids are being inputted or extracted from
+ * @param type The type of inventory being accessed. For inputting its Input, For outputting its Output.
+ * @return The Fluid Logic responsible for said type. Can return null if the side is invalid
+ */
+ @Nullable
+ FluidInventoryLogic getFluidLogic(@Nonnull ForgeDirection side, @Nonnull InventoryType type);
+
+ /**
+ * Only to be used by MultiBlockPart for accessing the Controller Inventory
+ *
+ * @param type Type of inventory, is it Input or Output
+ * @param id ID of the locked inventory. A null id is all inventories of said controller of said type
+ * @return The Fluid Logic responsible for everything that should be done with said inventory
+ */
+ @Nonnull
+ default FluidInventoryLogic getFluidLogic(@Nonnull InventoryType type, @Nullable UUID id) {
+ return Objects.requireNonNull(getFluidLogic(ForgeDirection.UNKNOWN, type));
+ }
+
+ /**
+ * Returns an empty set if the type is {@link InventoryType#Both} or when the machine isn't a controller.
+ */
+ @Nonnull
+ default Set<Entry<UUID, FluidInventoryLogic>> getAllFluidInventoryLogics(@Nonnull InventoryType type) {
+ return new HashSet<>();
+ }
+
+ @Override
+ default boolean canDrain(@Nonnull ForgeDirection from, Fluid fluid) {
+ FluidInventoryLogic logic = getFluidLogic(from, InventoryType.Output);
+ return logic != null;
+ }
+
+ @Override
+ default boolean canFill(@Nonnull ForgeDirection from, Fluid fluid) {
+ FluidInventoryLogic logic = getFluidLogic(from, InventoryType.Input);
+ return logic != null;
+ }
+
+ @Override
+ @Nullable
+ default FluidStack drain(@Nonnull ForgeDirection from, @Nonnull FluidStack resource, boolean doDrain) {
+ FluidInventoryLogic logic = getFluidLogic(from, InventoryType.Output);
+ if (logic == null) return null;
+ return logic.drain(resource.getFluid(), resource.amount, !doDrain);
+ }
+
+ @Override
+ @Nullable
+ default FluidStack drain(@Nonnull ForgeDirection from, int maxDrain, boolean doDrain) {
+ FluidInventoryLogic logic = getFluidLogic(from, InventoryType.Output);
+ if (logic == null) return null;
+ return logic.drain(maxDrain, !doDrain);
+ }
+
+ @Override
+ default int fill(@Nonnull ForgeDirection from, @Nonnull FluidStack resource, boolean doFill) {
+ FluidInventoryLogic logic = getFluidLogic(from, InventoryType.Input);
+ if (logic == null) return 0;
+ return saturatedCast(logic.fill(resource.getFluid(), resource.amount, !doFill));
+ }
+
+ @Override
+ @Nullable
+ default FluidTankInfo[] getTankInfo(@Nonnull ForgeDirection from) {
+ return null;
+ }
+}
diff --git a/src/main/java/gregtech/api/logic/interfaces/ItemInventoryLogicHost.java b/src/main/java/gregtech/api/logic/interfaces/ItemInventoryLogicHost.java
new file mode 100644
index 0000000000..a65f3c50f1
--- /dev/null
+++ b/src/main/java/gregtech/api/logic/interfaces/ItemInventoryLogicHost.java
@@ -0,0 +1,172 @@
+package gregtech.api.logic.interfaces;
+
+import java.util.HashSet;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.ISidedInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import gregtech.api.enums.InventoryType;
+import gregtech.api.logic.ItemInventoryLogic;
+
+public interface ItemInventoryLogicHost extends ISidedInventory {
+
+ /**
+ * To be used for single blocks or when directly interacting with the controller
+ *
+ * @param side The side from where items are being inputted or extracted from
+ * @param type The type of inventory being accessed. For inputting its Input, For outputting its Output.
+ * @return The Item Logic responsible for said type. Will return null if the side is not valid
+ */
+ @Nullable
+ ItemInventoryLogic getItemLogic(@Nonnull ForgeDirection side, @Nonnull InventoryType type);
+
+ /**
+ * Only to be used by MultiBlockPart for accessing the Controller Inventory
+ *
+ * @param type Type of inventory, is it Input or Output
+ * @param id ID of the locked inventory. A null id is all inventories of said controller of said type
+ * @return The Item Logic responsible for everything that should be done with said inventory
+ */
+ @Nonnull
+ default ItemInventoryLogic getItemLogic(@Nonnull InventoryType type, @Nullable UUID id) {
+ return Objects.requireNonNull(getItemLogic(ForgeDirection.UNKNOWN, type));
+ }
+
+ /**
+ * Only to be used for MultiBlockPart
+ *
+ * @return
+ */
+ @Nullable
+ default InventoryType getItemInventoryType() {
+ return null;
+ }
+
+ /**
+ * Returns an empty set if the type is {@link InventoryType#Both} or this is used when the machine isn't a
+ * controller
+ */
+ @Nonnull
+ default Set<Entry<UUID, ItemInventoryLogic>> getAllItemInventoryLogics(@Nonnull InventoryType type) {
+ return new HashSet<>();
+ }
+
+ @Override
+ @Nullable
+ default ItemStack decrStackSize(int slot, int count) {
+ InventoryType type = getItemInventoryType();
+ if (type == InventoryType.Both) return null;
+ ItemInventoryLogic logic = getItemLogic(ForgeDirection.UNKNOWN, type == null ? InventoryType.Output : type);
+ if (logic == null) return null;
+ return logic.extractItem(slot, count);
+ }
+
+ @Override
+ default int getSizeInventory() {
+ InventoryType type = getItemInventoryType();
+ if (type == InventoryType.Both) return 0;
+ ItemInventoryLogic logic = getItemLogic(ForgeDirection.UNKNOWN, type == null ? InventoryType.Output : type);
+ if (logic == null) return 0;
+ return logic.getSlots();
+ }
+
+ @Override
+ @Nullable
+ default ItemStack getStackInSlot(int slot) {
+ InventoryType type = getItemInventoryType();
+ if (type == InventoryType.Both) return null;
+ ItemInventoryLogic logic = getItemLogic(ForgeDirection.UNKNOWN, type == null ? InventoryType.Output : type);
+ if (logic == null) return null;
+ return logic.getInventory()
+ .getStackInSlot(slot);
+ }
+
+ @Override
+ default boolean isItemValidForSlot(int slot, @Nullable ItemStack stack) {
+ InventoryType type = getItemInventoryType();
+ if (type == InventoryType.Both) return false;
+ ItemInventoryLogic logic = getItemLogic(ForgeDirection.UNKNOWN, type == null ? InventoryType.Output : type);
+ if (logic == null) return false;
+ return logic.getInventory()
+ .isItemValid(slot, stack);
+ }
+
+ @Override
+ default void setInventorySlotContents(int slot, @Nullable ItemStack stack) {
+ InventoryType type = getItemInventoryType();
+ if (type == InventoryType.Both) return;
+ ItemInventoryLogic logic = getItemLogic(ForgeDirection.UNKNOWN, type == null ? InventoryType.Output : type);
+ if (logic == null) return;
+ logic.getInventory()
+ .setStackInSlot(slot, stack);
+ }
+
+ @Override
+ default boolean canExtractItem(int ignoredSlot, ItemStack ignoredItem, int side) {
+ InventoryType type = getItemInventoryType();
+ if (type == null) return false;
+ return getItemLogic(ForgeDirection.getOrientation(side), type) != null;
+ }
+
+ @Override
+ default boolean canInsertItem(int ignoredSlot, ItemStack ignoredItem, int side) {
+ InventoryType type = getItemInventoryType();
+ if (type == null) return false;
+ return getItemInventoryType() != InventoryType.Output
+ && getItemLogic(ForgeDirection.getOrientation(side), type) != null;
+ }
+
+ @Override
+ default int[] getAccessibleSlotsFromSide(int side) {
+ InventoryType type = getItemInventoryType();
+ if (type == null) return new int[0];
+ ItemInventoryLogic logic = getItemLogic(ForgeDirection.UNKNOWN, type == null ? InventoryType.Output : type);
+ if (logic == null) return new int[0];
+ int[] indexes = new int[logic.getSlots()];
+ for (int i = 0; i < logic.getSlots(); i++) {
+ indexes[i] = i;
+ }
+ return indexes;
+ }
+
+ @Override
+ default void closeInventory() {}
+
+ @Override
+ default String getInventoryName() {
+ return "";
+ }
+
+ @Override
+ default int getInventoryStackLimit() {
+ return 64;
+ }
+
+ @Override
+ default ItemStack getStackInSlotOnClosing(int index) {
+ return null;
+ }
+
+ @Override
+ default boolean hasCustomInventoryName() {
+ return false;
+ }
+
+ @Override
+ default boolean isUseableByPlayer(@Nonnull EntityPlayer player) {
+ return false;
+ }
+
+ @Override
+ default void openInventory() {}
+
+}
diff --git a/src/main/java/gregtech/api/logic/interfaces/PollutionLogicHost.java b/src/main/java/gregtech/api/logic/interfaces/PollutionLogicHost.java
deleted file mode 100644
index 657efbb74d..0000000000
--- a/src/main/java/gregtech/api/logic/interfaces/PollutionLogicHost.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package gregtech.api.logic.interfaces;
-
-import gregtech.api.logic.PollutionLogic;
-
-public interface PollutionLogicHost {
-
- PollutionLogic getPollutionLogic();
-}
diff --git a/src/main/java/gregtech/api/logic/interfaces/PowerLogicHost.java b/src/main/java/gregtech/api/logic/interfaces/PowerLogicHost.java
index 8604c160fb..4903d7fa23 100644
--- a/src/main/java/gregtech/api/logic/interfaces/PowerLogicHost.java
+++ b/src/main/java/gregtech/api/logic/interfaces/PowerLogicHost.java
@@ -1,18 +1,60 @@
package gregtech.api.logic.interfaces;
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+
import net.minecraftforge.common.util.ForgeDirection;
+import gregtech.api.interfaces.tileentity.IEnergyConnected;
import gregtech.api.logic.PowerLogic;
+/**
+ * Power logic class for one to use to enable a machine to use energy
+ */
public interface PowerLogicHost {
- PowerLogic getPowerLogic(ForgeDirection side);
+ /**
+ *
+ * @param side Side being access to try and get the power logic from
+ * @return Can return NullPowerLogic if the side doesn't allow the return of the logic. That power logic is unusable
+ */
+ @Nonnull
+ PowerLogic getPowerLogic(@Nonnull ForgeDirection side);
+
+ /**
+ * Gives the power logic ignoring the side.
+ */
+ @Nonnull
+ default PowerLogic getPowerLogic() {
+ return Objects.requireNonNull(getPowerLogic(ForgeDirection.UNKNOWN));
+ }
+ /**
+ * Shortcut to the method of {@link PowerLogic#isEnergyReceiver()}
+ */
default boolean isEnergyReceiver() {
- return false;
+ return getPowerLogic().isEnergyReceiver();
}
+ /**
+ * Shortcut to the method of {@link PowerLogic#isEnergyEmitter()}
+ */
default boolean isEnergyEmitter() {
- return false;
+ return getPowerLogic().isEnergyEmitter();
}
+
+ /**
+ * Method for emitting energy to other blocks and machines. Override when it needs to be changed.
+ */
+ default void emitEnergyFromLogic() {
+ IEnergyConnected.Util.emitEnergyToNetwork(this, getPowerOutputSide());
+ }
+
+ /**
+ * From where does the machine output energy from?
+ * When the output side is {@link ForgeDirection#UNKNOWN} then it won't output energy
+ */
+ @Nonnull
+ ForgeDirection getPowerOutputSide();
}
diff --git a/src/main/java/gregtech/api/logic/interfaces/ProcessingLogicHost.java b/src/main/java/gregtech/api/logic/interfaces/ProcessingLogicHost.java
index 55418208b0..b8291c9843 100644
--- a/src/main/java/gregtech/api/logic/interfaces/ProcessingLogicHost.java
+++ b/src/main/java/gregtech/api/logic/interfaces/ProcessingLogicHost.java
@@ -1,8 +1,82 @@
package gregtech.api.logic.interfaces;
-import gregtech.api.logic.ProcessingLogic;
+import javax.annotation.Nonnull;
-public interface ProcessingLogicHost {
+import gregtech.api.enums.VoidingMode;
+import gregtech.api.interfaces.tileentity.IMachineProgress;
+import gregtech.api.interfaces.tileentity.IVoidable;
+import gregtech.api.logic.MuTEProcessingLogic;
- ProcessingLogic getProcessingLogic();
+public interface ProcessingLogicHost<P extends MuTEProcessingLogic<P>>
+ extends IVoidable, ItemInventoryLogicHost, FluidInventoryLogicHost, IMachineProgress {
+
+ /**
+ * Get the processing logic for the current machine
+ */
+ @Nonnull
+ P getProcessingLogic();
+
+ boolean isInputSeparated();
+
+ void setInputSeparation(Boolean inputSeparation);
+
+ default boolean supportsInputSeparation() {
+ return true;
+ }
+
+ default boolean getDefaultInputSeparationMode() {
+ return false;
+ }
+
+ boolean isRecipeLockingEnabled();
+
+ void setRecipeLocking(Boolean recipeLocked);
+
+ default boolean supportsSingleRecipeLocking() {
+ return true;
+ }
+
+ default boolean getDefaultRecipeLockingMode() {
+ return false;
+ }
+
+ default boolean supportsBatchMode() {
+ return true;
+ }
+
+ void setBatchMode(Boolean batchMode);
+
+ boolean isBatchModeEnabled();
+
+ default boolean getDefaultBatchMode() {
+ return false;
+ }
+
+ /**
+ * Get what the machine can void or not
+ */
+ @Nonnull
+ VoidingMode getVoidMode();
+
+ /**
+ * Called when the processing logic should be updated by {@link #needsUpdate()}
+ */
+ default void updateProcessingLogic(@Nonnull P processingLogic) {}
+
+ /**
+ * Called before the recipe check, but after any other updates
+ */
+ default void setProcessingLogicPower(@Nonnull P processingLogic) {}
+
+ /**
+ * DO NOT CALL YOURSELF!!!
+ *
+ * If you want to make the processing logic be updated call {@link #setProcessingUpdate(boolean)}
+ */
+ boolean needsUpdate();
+
+ /**
+ * To be called when one needs to updated the processing logic. That can be when parallel changes, ect.
+ */
+ void setProcessingUpdate(boolean update);
}