diff --git a/src/main/java/gregtech/api/capability/INotifiableHandler.java b/src/main/java/gregtech/api/capability/INotifiableHandler.java new file mode 100644 index 00000000000..34cf1c36351 --- /dev/null +++ b/src/main/java/gregtech/api/capability/INotifiableHandler.java @@ -0,0 +1,33 @@ +package gregtech.api.capability; + +import gregtech.api.metatileentity.MetaTileEntity; + +/** + * For Item and Fluid handlers capable of notifying entities when + * their contents change + */ +public interface INotifiableHandler { + + /** + * Adds the notified handler to the notified list + * + * @param isExport boolean specifying if a handler is an output handler + */ + + default void addToNotifiedList(MetaTileEntity metaTileEntity, T handler, boolean isExport) { + if (metaTileEntity != null && metaTileEntity.isValid()) { + if (isExport) { + metaTileEntity.addNotifiedOutput(handler); + } else { + metaTileEntity.addNotifiedInput(handler); + } + } + } + + /** + * @param metaTileEntity MetaTileEntity to be notified + */ + default void setNotifiableMetaTileEntity(MetaTileEntity metaTileEntity) { + + } +} diff --git a/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java old mode 100755 new mode 100644 index eb5503f6021..3ffe267310c --- a/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java @@ -16,6 +16,7 @@ import net.minecraft.nbt.NBTTagList; import net.minecraft.network.PacketBuffer; import net.minecraft.util.NonNullList; +import net.minecraft.world.*; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.util.Constants; import net.minecraftforge.fluids.FluidStack; @@ -24,7 +25,6 @@ import net.minecraftforge.items.IItemHandlerModifiable; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.function.LongSupplier; @@ -37,8 +37,8 @@ public abstract class AbstractRecipeLogic extends MTETrait implements IWorkable public final RecipeMap recipeMap; protected boolean forceRecipeRecheck; - protected ItemStack[] lastItemInputs; - protected FluidStack[] lastFluidInputs; + @Deprecated protected ItemStack[] lastItemInputs; + @Deprecated protected FluidStack[] lastFluidInputs; protected Recipe previousRecipe; protected boolean allowOverclocking = true; private long overclockVoltage = 0; @@ -55,6 +55,8 @@ public abstract class AbstractRecipeLogic extends MTETrait implements IWorkable protected boolean workingEnabled = true; protected boolean hasNotEnoughEnergy; protected boolean wasActiveAndNeedsUpdate; + protected boolean isOutputsFull; + protected boolean invalidInputsForRecipes; protected boolean hasPerfectOC = false; @@ -115,12 +117,14 @@ public T getCapability(Capability capability) { @Override public void update() { - if (!getMetaTileEntity().getWorld().isRemote) { + World world = getMetaTileEntity().getWorld(); + if (world != null && !world.isRemote) { if (workingEnabled) { if (progressTime > 0) { updateRecipeProgress(); } - if (progressTime == 0) { + //check everything that would make a recipe never start here. + if (progressTime == 0 && shouldSearchForRecipes()) { trySearchNewRecipe(); } } @@ -131,6 +135,40 @@ public void update() { } } + protected boolean shouldSearchForRecipes() { + return canWorkWithInputs() && canFitNewOutputs(); + } + + protected boolean hasNotifiedInputs() { + return (metaTileEntity.getNotifiedItemInputList().size() > 0 || + metaTileEntity.getNotifiedFluidInputList().size() > 0); + } + + protected boolean hasNotifiedOutputs() { + return (metaTileEntity.getNotifiedItemOutputList().size() > 0 || + metaTileEntity.getNotifiedFluidOutputList().size() > 0); + } + + protected boolean canFitNewOutputs() { + // if the output is full check if the output changed so we can process recipes results again. + if (this.isOutputsFull && !hasNotifiedOutputs()) return false; + else { + this.isOutputsFull = false; + metaTileEntity.getNotifiedItemOutputList().clear(); + metaTileEntity.getNotifiedFluidOutputList().clear(); + } + return true; + } + + protected boolean canWorkWithInputs() { + // if the inputs were bad last time, check if they've changed before trying to find a new recipe. + if (this.invalidInputsForRecipes && !hasNotifiedInputs()) return false; + else { + this.invalidInputsForRecipes = false; + } + return true; + } + protected void updateRecipeProgress() { boolean drawEnergy = drawEnergy(recipeEUt); if (drawEnergy || (recipeEUt < 0)) { @@ -158,23 +196,26 @@ protected void trySearchNewRecipe() { Recipe currentRecipe = null; IItemHandlerModifiable importInventory = getInputInventory(); IMultipleTankHandler importFluids = getInputTank(); - if (previousRecipe != null && previousRecipe.matches(false, importInventory, importFluids)) { - //if previous recipe still matches inputs, try to use it - currentRecipe = previousRecipe; - } else { - boolean dirty = checkRecipeInputsDirty(importInventory, importFluids); - if (dirty || forceRecipeRecheck) { - this.forceRecipeRecheck = false; - //else, try searching new recipe for given inputs - currentRecipe = findRecipe(maxVoltage, importInventory, importFluids, MatchingMode.DEFAULT); - if (currentRecipe != null) { - this.previousRecipe = currentRecipe; - } - } + + // see if the last recipe we used still works + if (this.previousRecipe != null && this.previousRecipe.matches(false, importInventory, importFluids)) + currentRecipe = this.previousRecipe; + // If there is no active recipe, then we need to find one. + else { + currentRecipe = findRecipe(maxVoltage, importInventory, importFluids, MatchingMode.DEFAULT); } - if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe)) { - setupRecipe(currentRecipe); + // If a recipe was found, then inputs were valid. Cache found recipe. + if (currentRecipe != null) { + this.previousRecipe = currentRecipe; } + this.invalidInputsForRecipes = (currentRecipe == null); + + // proceed if we have a usable recipe. + if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe)) + setupRecipe(currentRecipe); + // Inputs have been inspected. + metaTileEntity.getNotifiedItemInputList().clear(); + metaTileEntity.getNotifiedFluidInputList().clear(); } public void forceRecipeRecheck() { @@ -196,40 +237,16 @@ protected Recipe findRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMul return recipeMap.findRecipe(maxVoltage, inputs, fluidInputs, getMinTankCapacity(getOutputTank()), mode); } + /** + * @deprecated Use {@link #hasNotifiedInputs() } instead + * Left here for binary compatibility purposes + */ + @Deprecated protected boolean checkRecipeInputsDirty(IItemHandler inputs, IMultipleTankHandler fluidInputs) { - boolean shouldRecheckRecipe = false; - if (lastItemInputs == null || lastItemInputs.length != inputs.getSlots()) { - this.lastItemInputs = new ItemStack[inputs.getSlots()]; - Arrays.fill(lastItemInputs, ItemStack.EMPTY); - } - if (lastFluidInputs == null || lastFluidInputs.length != fluidInputs.getTanks()) { - this.lastFluidInputs = new FluidStack[fluidInputs.getTanks()]; - } - for (int i = 0; i < lastItemInputs.length; i++) { - ItemStack currentStack = inputs.getStackInSlot(i); - ItemStack lastStack = lastItemInputs[i]; - if (!areItemStacksEqual(currentStack, lastStack)) { - this.lastItemInputs[i] = currentStack.isEmpty() ? ItemStack.EMPTY : currentStack.copy(); - shouldRecheckRecipe = true; - } else if (currentStack.getCount() != lastStack.getCount()) { - lastStack.setCount(currentStack.getCount()); - shouldRecheckRecipe = true; - } - } - for (int i = 0; i < lastFluidInputs.length; i++) { - FluidStack currentStack = fluidInputs.getTankAt(i).getFluid(); - FluidStack lastStack = lastFluidInputs[i]; - if ((currentStack == null && lastStack != null) || - (currentStack != null && !currentStack.isFluidEqual(lastStack))) { - this.lastFluidInputs[i] = currentStack == null ? null : currentStack.copy(); - shouldRecheckRecipe = true; - } else if (currentStack != null && lastStack != null && - currentStack.amount != lastStack.amount) { - lastStack.amount = currentStack.amount; - shouldRecheckRecipe = true; - } - } - return shouldRecheckRecipe; + boolean isDirty = this.hasNotifiedInputs(); + metaTileEntity.getNotifiedItemInputList().clear(); + metaTileEntity.getNotifiedFluidInputList().clear(); + return isDirty; } protected static boolean areItemStacksEqual(ItemStack stackA, ItemStack stackB) { @@ -245,11 +262,20 @@ protected boolean setupAndConsumeRecipeInputs(Recipe recipe) { IItemHandlerModifiable exportInventory = getOutputInventory(); IMultipleTankHandler importFluids = getInputTank(); IMultipleTankHandler exportFluids = getOutputTank(); - return (totalEUt >= 0 ? getEnergyStored() >= (totalEUt > getEnergyCapacity() / 2 ? resultOverclock[0] : totalEUt) : - (getEnergyStored() - resultOverclock[0] <= getEnergyCapacity())) && - MetaTileEntity.addItemsToItemHandler(exportInventory, true, recipe.getAllItemOutputs(exportInventory.getSlots())) && - MetaTileEntity.addFluidsToFluidHandler(exportFluids, true, recipe.getFluidOutputs()) && - recipe.matches(true, importInventory, importFluids); + if (!(totalEUt >= 0 ? getEnergyStored() >= (totalEUt > getEnergyCapacity() / 2 ? resultOverclock[0] : totalEUt) : + (getEnergyStored() - resultOverclock[0] <= getEnergyCapacity()))) { + return false; + } + if (!MetaTileEntity.addItemsToItemHandler(exportInventory, true, recipe.getAllItemOutputs(exportInventory.getSlots()))) { + this.isOutputsFull = true; + return false; + } + if (!MetaTileEntity.addFluidsToFluidHandler(exportFluids, true, recipe.getFluidOutputs())) { + this.isOutputsFull = true; + return false; + } + this.isOutputsFull = false; + return recipe.matches(true, importInventory, importFluids); } protected int[] calculateOverclock(int EUt, int duration) { @@ -329,9 +355,6 @@ protected void completeRecipe() { this.itemOutputs = null; this.hasNotEnoughEnergy = false; this.wasActiveAndNeedsUpdate = true; - //force recipe recheck because inputs could have changed since last time - //we checked them before starting our recipe, especially if recipe took long time - this.forceRecipeRecheck = true; } public double getProgressPercent() { @@ -364,7 +387,8 @@ public void setMaxProgress(int maxProgress) { protected void setActive(boolean active) { this.isActive = active; metaTileEntity.markDirty(); - if (!metaTileEntity.getWorld().isRemote) { + World world = metaTileEntity.getWorld(); + if (world != null && !world.isRemote) { writeCustomData(1, buf -> buf.writeBoolean(active)); } } diff --git a/src/main/java/gregtech/api/capability/impl/NotifiableFilteredFluidHandler.java b/src/main/java/gregtech/api/capability/impl/NotifiableFilteredFluidHandler.java new file mode 100644 index 00000000000..7e5d3ab4554 --- /dev/null +++ b/src/main/java/gregtech/api/capability/impl/NotifiableFilteredFluidHandler.java @@ -0,0 +1,27 @@ +package gregtech.api.capability.impl; + +import gregtech.api.capability.INotifiableHandler; +import gregtech.api.metatileentity.MetaTileEntity; + +public class NotifiableFilteredFluidHandler extends FilteredFluidHandler implements INotifiableHandler { + + MetaTileEntity notifiableEntity; + private final boolean isExport; + + public NotifiableFilteredFluidHandler(int capacity, MetaTileEntity entityToNotify, boolean isExport) { + super(capacity); + this.notifiableEntity = entityToNotify; + this.isExport = isExport; + } + + @Override + protected void onContentsChanged() { + super.onContentsChanged(); + addToNotifiedList(notifiableEntity, this, isExport); + } + + @Override + public void setNotifiableMetaTileEntity(MetaTileEntity metaTileEntity) { + this.notifiableEntity = metaTileEntity; + } +} diff --git a/src/main/java/gregtech/api/capability/impl/NotifiableFluidTank.java b/src/main/java/gregtech/api/capability/impl/NotifiableFluidTank.java new file mode 100644 index 00000000000..2c54e096dbc --- /dev/null +++ b/src/main/java/gregtech/api/capability/impl/NotifiableFluidTank.java @@ -0,0 +1,28 @@ +package gregtech.api.capability.impl; + +import gregtech.api.capability.INotifiableHandler; +import gregtech.api.metatileentity.MetaTileEntity; +import net.minecraftforge.fluids.FluidTank; + +public class NotifiableFluidTank extends FluidTank implements INotifiableHandler { + + MetaTileEntity notifiableEntity; + private final boolean isExport; + + public NotifiableFluidTank(int capacity, MetaTileEntity entityToNotify, boolean isExport) { + super(capacity); + this.notifiableEntity = entityToNotify; + this.isExport = isExport; + } + + @Override + protected void onContentsChanged() { + super.onContentsChanged(); + addToNotifiedList(notifiableEntity, this, isExport); + } + + @Override + public void setNotifiableMetaTileEntity(MetaTileEntity metaTileEntity) { + this.notifiableEntity = metaTileEntity; + } +} diff --git a/src/main/java/gregtech/api/capability/impl/NotifiableItemStackHandler.java b/src/main/java/gregtech/api/capability/impl/NotifiableItemStackHandler.java new file mode 100644 index 00000000000..88e3c58e07b --- /dev/null +++ b/src/main/java/gregtech/api/capability/impl/NotifiableItemStackHandler.java @@ -0,0 +1,29 @@ +package gregtech.api.capability.impl; + +import gregtech.api.capability.INotifiableHandler; +import gregtech.api.metatileentity.MetaTileEntity; +import net.minecraftforge.items.IItemHandlerModifiable; +import net.minecraftforge.items.ItemStackHandler; + +public class NotifiableItemStackHandler extends ItemStackHandler implements IItemHandlerModifiable, INotifiableHandler { + + MetaTileEntity notifiableEntity; + private final boolean isExport; + + public NotifiableItemStackHandler(int slots, MetaTileEntity entityToNotify, boolean isExport) { + super(slots); + this.notifiableEntity = entityToNotify; + this.isExport = isExport; + } + + @Override + public void onContentsChanged(int slot) { + super.onContentsChanged(slot); + addToNotifiedList(notifiableEntity, this, isExport); + } + + @Override + public void setNotifiableMetaTileEntity(MetaTileEntity metaTileEntity) { + this.notifiableEntity = metaTileEntity; + } +} diff --git a/src/main/java/gregtech/api/capability/impl/SteamMultiWorkable.java b/src/main/java/gregtech/api/capability/impl/SteamMultiWorkable.java index bb58279cc0b..451d280d563 100644 --- a/src/main/java/gregtech/api/capability/impl/SteamMultiWorkable.java +++ b/src/main/java/gregtech/api/capability/impl/SteamMultiWorkable.java @@ -34,22 +34,29 @@ protected void trySearchNewRecipe() { long maxVoltage = getMaxVoltage(); // Will always be LV voltage Recipe currentRecipe = null; IItemHandlerModifiable importInventory = getInputInventory(); - boolean dirty = checkRecipeInputsDirty(importInventory, null); - - if (dirty || forceRecipeRecheck) { - this.forceRecipeRecheck = false; - - currentRecipe = findRecipe(maxVoltage, importInventory, null, MatchingMode.DEFAULT); - if (currentRecipe != null) { - this.previousRecipe = currentRecipe; - } - } else if (previousRecipe != null && previousRecipe.matches(false, importInventory, new FluidTankList(false))) { + IMultipleTankHandler importFluids = getInputTank(); + + //inverse of logic in normal AbstractRecipeLogic + //for MultiSmelter, we can reuse previous recipe if inputs didn't change + //otherwise, we need to recompute it for new ingredients + //but technically, it means we can cache multi smelter recipe, but changing inputs have more priority + if (hasNotifiedInputs() || + previousRecipe == null || + !previousRecipe.matches(false, importInventory, importFluids, MatchingMode.IGNORE_FLUIDS)) { + //Inputs changed, try searching new recipe for given inputs + currentRecipe = findRecipe(maxVoltage, importInventory, importFluids, MatchingMode.IGNORE_FLUIDS); + } else { + //if previous recipe still matches inputs, try to use it currentRecipe = previousRecipe; } - - if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe)) { + if (currentRecipe != null) + // replace old recipe with new one + this.previousRecipe = currentRecipe; + // proceed if we have a usable recipe. + if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe)) setupRecipe(currentRecipe); - } + // Inputs have been inspected. + metaTileEntity.getNotifiedItemInputList().clear(); } @Override @@ -64,6 +71,9 @@ protected Recipe findRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMul /* Iterate over input items looking for more items to process until we * have touched every item, or are at maximum item capacity */ + boolean matchedRecipe = false; + boolean canFitOutputs = true; + for (int index = 0; index < inputs.getSlots() && currentItemsEngaged < MAX_PROCESSES; index++) { final ItemStack currentInputItem = inputs.getStackInSlot(index); @@ -78,6 +88,7 @@ protected Recipe findRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMul MatchingMode.DEFAULT); CountableIngredient inputIngredient; if (matchingRecipe != null) { + matchedRecipe = true; if (matchingRecipe.getOutputs().isEmpty()) return doChancedOnlyRecipe(matchingRecipe, currentInputItem); inputIngredient = matchingRecipe.getInputs().get(0); @@ -104,7 +115,7 @@ protected Recipe findRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMul computeOutputItemStacks(temp, matchingRecipe.getOutputs().get(0), recipeMultiplier); // Check to see if we have output space available for the recipe - boolean canFitOutputs = InventoryUtils.simulateItemStackMerge(temp, this.getOutputInventory()); + canFitOutputs = InventoryUtils.simulateItemStackMerge(temp, this.getOutputInventory()); if (!canFitOutputs) break; @@ -120,9 +131,10 @@ protected Recipe findRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMul } } - // No recipe was found + this.invalidInputsForRecipes = !matchedRecipe; + this.isOutputsFull = !canFitOutputs; + if (recipeInputs.isEmpty()) { - forceRecipeRecheck = true; return null; } diff --git a/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java index d0ebffa1d58..f690b8a4d39 100644 --- a/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java +++ b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java @@ -12,9 +12,7 @@ import gregtech.api.GregTechAPI; import gregtech.api.capability.GregtechTileCapabilities; import gregtech.api.capability.IEnergyContainer; -import gregtech.api.capability.impl.FluidHandlerProxy; -import gregtech.api.capability.impl.FluidTankList; -import gregtech.api.capability.impl.ItemHandlerProxy; +import gregtech.api.capability.impl.*; import gregtech.api.cover.CoverBehavior; import gregtech.api.cover.CoverDefinition; import gregtech.api.cover.ICoverable; @@ -43,6 +41,7 @@ import net.minecraftforge.common.util.Constants.NBT; import net.minecraftforge.fluids.FluidActionResult; import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTank; import net.minecraftforge.fluids.FluidUtil; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; @@ -53,8 +52,7 @@ import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import java.util.function.Consumer; import static gregtech.api.util.InventoryUtils.simulateItemStackMerge; @@ -91,6 +89,10 @@ public abstract class MetaTileEntity implements ICoverable { protected boolean isFragile = false; private final CoverBehavior[] coverBehaviors = new CoverBehavior[6]; + protected List notifiedItemOutputList = new ArrayList<>(); + protected List notifiedItemInputList = new ArrayList<>(); + protected List notifiedFluidInputList = new ArrayList<>(); + protected List notifiedFluidOutputList = new ArrayList<>(); public MetaTileEntity(ResourceLocation metaTileEntityId) { this.metaTileEntityId = metaTileEntityId; @@ -250,6 +252,30 @@ public final String getMetaFullName() { return getMetaName() + ".name"; } + public void addNotifiedInput(T input) { + if (input instanceof IItemHandlerModifiable) { + if (!notifiedItemInputList.contains(input)) { + this.notifiedItemInputList.add((IItemHandlerModifiable) input); + } + } else if (input instanceof FluidTank) { + if (!notifiedFluidInputList.contains(input)) { + this.notifiedFluidInputList.add((FluidTank) input); + } + } + } + + public void addNotifiedOutput(T output) { + if (output instanceof IItemHandlerModifiable) { + if (!notifiedItemOutputList.contains(output)) { + this.notifiedItemOutputList.add((IItemHandlerModifiable) output); + } + } else if (output instanceof NotifiableFluidTank) { + if (!notifiedFluidOutputList.contains(output)) { + this.notifiedFluidOutputList.add((NotifiableFluidTank) output); + } + } + } + /** * Adds a trait to this meta tile entity * traits are objects linked with meta tile entity and performing certain @@ -1206,6 +1232,22 @@ public FluidTankList getExportFluids() { return exportFluids; } + public List getNotifiedItemOutputList() { + return notifiedItemOutputList; + } + + public List getNotifiedItemInputList() { + return notifiedItemInputList; + } + + public List getNotifiedFluidInputList() { + return notifiedFluidInputList; + } + + public List getNotifiedFluidOutputList() { + return notifiedFluidOutputList; + } + public boolean isFragile() { return isFragile; } diff --git a/src/main/java/gregtech/api/metatileentity/MetaTileEntityHolder.java b/src/main/java/gregtech/api/metatileentity/MetaTileEntityHolder.java index c4147e7b813..3b34661d759 100644 --- a/src/main/java/gregtech/api/metatileentity/MetaTileEntityHolder.java +++ b/src/main/java/gregtech/api/metatileentity/MetaTileEntityHolder.java @@ -6,10 +6,8 @@ import gregtech.api.block.machines.BlockMachine; import gregtech.api.cover.CoverBehavior; import gregtech.api.gui.IUIHolder; -import gregtech.api.util.FirstTickScheduler; import gregtech.api.util.GTControlledRegistry; import gregtech.api.util.GTLog; -import gregtech.api.util.IFirstTickTask; import net.minecraft.block.state.IBlockState; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.PacketBuffer; @@ -28,7 +26,7 @@ import java.util.List; import java.util.stream.Collectors; -public class MetaTileEntityHolder extends TickableTileEntityBase implements IUIHolder, IFirstTickTask { +public class MetaTileEntityHolder extends TickableTileEntityBase implements IUIHolder { private MetaTileEntity metaTileEntity; private boolean needToUpdateLightning = false; @@ -220,14 +218,7 @@ public void markAsDirty() { public void onLoad() { super.onLoad(); if (metaTileEntity != null) { - FirstTickScheduler.addTask(this); - } - } - - @Override - public void handleFirstTick() { - if (this.metaTileEntity != null) { - this.metaTileEntity.onLoad(); + metaTileEntity.onLoad(); } } diff --git a/src/main/java/gregtech/api/metatileentity/WorkableTieredMetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/WorkableTieredMetaTileEntity.java index 14d5d3ffb24..e72cf5f5726 100644 --- a/src/main/java/gregtech/api/metatileentity/WorkableTieredMetaTileEntity.java +++ b/src/main/java/gregtech/api/metatileentity/WorkableTieredMetaTileEntity.java @@ -4,10 +4,7 @@ import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; import gregtech.api.GTValues; -import gregtech.api.capability.impl.EnergyContainerHandler; -import gregtech.api.capability.impl.FilteredFluidHandler; -import gregtech.api.capability.impl.FluidTankList; -import gregtech.api.capability.impl.RecipeLogicEnergy; +import gregtech.api.capability.impl.*; import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeMap; import gregtech.api.render.OrientedOverlayRenderer; @@ -74,13 +71,13 @@ public void renderMetaTileEntity(CCRenderState renderState, Matrix4 translation, @Override protected IItemHandlerModifiable createImportItemHandler() { if (workable == null) return new ItemStackHandler(0); - return new ItemStackHandler(workable.recipeMap.getMaxInputs()); + return new NotifiableItemStackHandler(workable.recipeMap.getMaxInputs(), this, false); } @Override protected IItemHandlerModifiable createExportItemHandler() { if (workable == null) return new ItemStackHandler(0); - return new ItemStackHandler(workable.recipeMap.getMaxOutputs()); + return new NotifiableItemStackHandler(workable.recipeMap.getMaxOutputs(), this, true); } @Override @@ -88,7 +85,7 @@ protected FluidTankList createImportFluidHandler() { if (workable == null) return new FluidTankList(false); FilteredFluidHandler[] fluidImports = new FilteredFluidHandler[workable.recipeMap.getMaxFluidInputs()]; for (int i = 0; i < fluidImports.length; i++) { - FilteredFluidHandler filteredFluidHandler = new FilteredFluidHandler(getInputTankCapacity(i)); + NotifiableFilteredFluidHandler filteredFluidHandler = new NotifiableFilteredFluidHandler(getInputTankCapacity(i), this, false); filteredFluidHandler.setFillPredicate(this::canInputFluid); fluidImports[i] = filteredFluidHandler; } @@ -100,7 +97,7 @@ protected FluidTankList createExportFluidHandler() { if (workable == null) return new FluidTankList(false); FluidTank[] fluidExports = new FluidTank[workable.recipeMap.getMaxFluidOutputs()]; for (int i = 0; i < fluidExports.length; i++) { - fluidExports[i] = new FluidTank(getOutputTankCapacity(i)); + fluidExports[i] = new NotifiableFluidTank(getOutputTankCapacity(i), this, true); } return new FluidTankList(false, fluidExports); } diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/IMultiblockPart.java b/src/main/java/gregtech/api/metatileentity/multiblock/IMultiblockPart.java index ca3213547fe..1d343bbb68d 100644 --- a/src/main/java/gregtech/api/metatileentity/multiblock/IMultiblockPart.java +++ b/src/main/java/gregtech/api/metatileentity/multiblock/IMultiblockPart.java @@ -1,5 +1,7 @@ package gregtech.api.metatileentity.multiblock; +import gregtech.api.metatileentity.MetaTileEntity; + public interface IMultiblockPart { boolean isAttachedToMultiBlock(); @@ -8,4 +10,7 @@ public interface IMultiblockPart { void removeFromMultiBlock(MultiblockControllerBase controllerBase); + default void setupNotifiableMetaTileEntity(MetaTileEntity metaTileEntity) { + } + } diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/MultiblockControllerBase.java b/src/main/java/gregtech/api/metatileentity/multiblock/MultiblockControllerBase.java index 0a204ac3fd3..0238c023354 100644 --- a/src/main/java/gregtech/api/metatileentity/multiblock/MultiblockControllerBase.java +++ b/src/main/java/gregtech/api/metatileentity/multiblock/MultiblockControllerBase.java @@ -210,6 +210,7 @@ protected void checkStructurePattern() { } if (checkStructureComponents(parts, abilities)) { parts.forEach(part -> part.addToMultiBlock(this)); + parts.forEach(part -> part.setupNotifiableMetaTileEntity(this)); this.multiblockParts.addAll(parts); this.multiblockAbilities.putAll(abilities); this.structureFormed = true; diff --git a/src/main/java/gregtech/api/pipenet/tile/TileEntityPipeBase.java b/src/main/java/gregtech/api/pipenet/tile/TileEntityPipeBase.java index 5e1b3ba86d8..b99275fa97e 100644 --- a/src/main/java/gregtech/api/pipenet/tile/TileEntityPipeBase.java +++ b/src/main/java/gregtech/api/pipenet/tile/TileEntityPipeBase.java @@ -8,8 +8,7 @@ import gregtech.api.pipenet.WorldPipeNet; import gregtech.api.pipenet.block.BlockPipe; import gregtech.api.pipenet.block.IPipeType; -import gregtech.api.util.FirstTickScheduler; -import gregtech.api.util.IFirstTickTask; +import gregtech.common.ConfigHolder; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.nbt.NBTTagCompound; @@ -26,7 +25,7 @@ import javax.annotation.Nullable; import java.util.function.Consumer; -public abstract class TileEntityPipeBase & IPipeType, NodeDataType> extends SyncedTileEntityBase implements IPipeTile, IFirstTickTask { +public abstract class TileEntityPipeBase & IPipeType, NodeDataType> extends SyncedTileEntityBase implements IPipeTile { private TIntIntMap openConnectionsMap = new TIntIntHashMap(); private int openConnections = 0; @@ -347,11 +346,6 @@ public void readFromNBT(@Nonnull NBTTagCompound compound) { @Override public void onLoad() { super.onLoad(); - FirstTickScheduler.addTask(this); - } - - @Override - public void handleFirstTick() { this.coverableImplementation.onLoad(); } diff --git a/src/main/java/gregtech/api/util/FirstTickScheduler.java b/src/main/java/gregtech/api/util/FirstTickScheduler.java deleted file mode 100644 index 1e6f87ae7f6..00000000000 --- a/src/main/java/gregtech/api/util/FirstTickScheduler.java +++ /dev/null @@ -1,48 +0,0 @@ -package gregtech.api.util; - -import com.google.common.collect.Maps; -import com.google.common.collect.Queues; -import gregtech.api.GTValues; -import net.minecraft.world.World; -import net.minecraftforge.event.world.WorldEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.EventPriority; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.gameevent.TickEvent; - -import java.util.Map; -import java.util.Queue; - -@Mod.EventBusSubscriber(modid = GTValues.MODID) -public class FirstTickScheduler { - - private static final Map> tasksByWorld = Maps.newConcurrentMap(); - - public static void addTask(final IFirstTickTask task) { - final World world = task.getWorld(); - if (!world.isRemote) { - final Queue tasks = tasksByWorld.computeIfAbsent(world, - k -> Queues.newConcurrentLinkedQueue()); - tasks.add(task); - } else task.handleFirstTick(); - } - - @SubscribeEvent - public static void onWorldUnload(WorldEvent.Unload event) { - tasksByWorld.remove(event.getWorld()); - } - - @SubscribeEvent(priority = EventPriority.HIGHEST) - public static void onWorldTick(TickEvent.WorldTickEvent event) { - if (event.phase == TickEvent.Phase.START) { - final Queue tasks = tasksByWorld.get(event.world); - if (tasks != null) { - IFirstTickTask task = tasks.poll(); - while (task != null) { - task.handleFirstTick(); - task = tasks.poll(); - } - } - } - } -} diff --git a/src/main/java/gregtech/api/util/IFirstTickTask.java b/src/main/java/gregtech/api/util/IFirstTickTask.java deleted file mode 100644 index 38f5fb2a7fe..00000000000 --- a/src/main/java/gregtech/api/util/IFirstTickTask.java +++ /dev/null @@ -1,10 +0,0 @@ -package gregtech.api.util; - -import net.minecraft.world.World; - -public interface IFirstTickTask { - - void handleFirstTick(); - - World getWorld(); -} diff --git a/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntityGasCollector.java b/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntityGasCollector.java index af5a8098a14..5e34d91802c 100644 --- a/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntityGasCollector.java +++ b/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntityGasCollector.java @@ -63,33 +63,35 @@ protected void trySearchNewRecipe() { Recipe currentRecipe = null; IItemHandlerModifiable importInventory = getInputInventory(); IMultipleTankHandler importFluids = getInputTank(); - if (previousRecipe != null && previousRecipe.matches(false, importInventory, importFluids)) { - //if previous recipe still matches inputs, try to use it - currentRecipe = previousRecipe; - } else { - boolean dirty = checkRecipeInputsDirty(importInventory, importFluids); - if (dirty || forceRecipeRecheck) { - this.forceRecipeRecheck = false; - //else, try searching new recipe for given inputs - currentRecipe = findRecipe(maxVoltage, importInventory, importFluids, MatchingMode.DEFAULT); - if (currentRecipe != null) { - List recipeDimensions = currentRecipe.getProperty(GasCollectorDimensionProperty.getInstance(), new ArrayList<>()); - boolean isDimensionValid = false; - for (Integer dimension : recipeDimensions) { - if (dimension == getCurrentDimension()) { - this.previousRecipe = currentRecipe; - isDimensionValid = true; - break; - } + + // see if the last recipe we used still works + if (this.previousRecipe != null && this.previousRecipe.matches(false, importInventory, importFluids, MatchingMode.IGNORE_FLUIDS)) + currentRecipe = this.previousRecipe; + // If there is no active recipe, then we need to find one. + else { + currentRecipe = findRecipe(maxVoltage, importInventory, importFluids, MatchingMode.IGNORE_FLUIDS); + if (currentRecipe != null) { + List recipeDimensions = currentRecipe.getProperty(GasCollectorDimensionProperty.getInstance(), new ArrayList<>()); + boolean isDimensionValid = false; + for (Integer dimension : recipeDimensions) { + if (dimension == getCurrentDimension()) { + this.previousRecipe = currentRecipe; + isDimensionValid = true; + break; } - if (!isDimensionValid) - currentRecipe = null; } + if (!isDimensionValid) + currentRecipe = null; } } - if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe)) { + this.invalidInputsForRecipes = (currentRecipe == null); + + // proceed if we have a usable recipe. + if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe)) setupRecipe(currentRecipe); - } + // Inputs have been inspected. + metaTileEntity.getNotifiedItemInputList().clear(); + metaTileEntity.getNotifiedFluidInputList().clear(); } } } diff --git a/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntityMacerator.java b/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntityMacerator.java index d2008fb3537..b10b12e9c90 100644 --- a/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntityMacerator.java +++ b/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntityMacerator.java @@ -9,12 +9,12 @@ import gregtech.api.recipes.RecipeMap; import gregtech.api.render.OrientedOverlayRenderer; import gregtech.api.util.GTUtility; +import gregtech.api.capability.impl.NotifiableItemStackHandler; import net.minecraft.client.resources.I18n; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; import net.minecraftforge.items.IItemHandlerModifiable; -import net.minecraftforge.items.ItemStackHandler; import javax.annotation.Nullable; import java.util.List; @@ -47,7 +47,7 @@ protected int getMachineTierForRecipe(Recipe recipe) { @Override protected IItemHandlerModifiable createExportItemHandler() { - return new ItemStackHandler(outputAmount); + return new NotifiableItemStackHandler(outputAmount, this, true); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntitySimpleOreWasher.java b/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntitySimpleOreWasher.java index bf2e0498c48..f5d1430ef74 100644 --- a/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntitySimpleOreWasher.java +++ b/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntitySimpleOreWasher.java @@ -1,5 +1,6 @@ package gregtech.common.metatileentities.electric; +import gregtech.api.capability.impl.NotifiableItemStackHandler; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.MetaTileEntityHolder; import gregtech.api.metatileentity.SimpleMachineMetaTileEntity; @@ -17,7 +18,7 @@ public MetaTileEntitySimpleOreWasher(ResourceLocation metaTileEntityId, RecipeMa @Override protected IItemHandlerModifiable createExportItemHandler() { - return new ItemStackHandler(1); + return new NotifiableItemStackHandler(1, this, true); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/electric/multiblockpart/MetaTileEntityFluidHatch.java b/src/main/java/gregtech/common/metatileentities/electric/multiblockpart/MetaTileEntityFluidHatch.java index 2b3d7304aab..5fc2021ee4e 100644 --- a/src/main/java/gregtech/common/metatileentities/electric/multiblockpart/MetaTileEntityFluidHatch.java +++ b/src/main/java/gregtech/common/metatileentities/electric/multiblockpart/MetaTileEntityFluidHatch.java @@ -17,6 +17,7 @@ import gregtech.api.metatileentity.multiblock.MultiblockAbility; import gregtech.api.render.SimpleOverlayRenderer; import gregtech.api.render.Textures; +import gregtech.api.capability.impl.NotifiableFluidTank; import net.minecraft.client.resources.I18n; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; @@ -43,7 +44,7 @@ public MetaTileEntityFluidHatch(ResourceLocation metaTileEntityId, int tier, boo super(metaTileEntityId, tier); this.containerInventory = new ItemStackHandler(2); this.isExportHatch = isExportHatch; - this.fluidTank = new FluidTank(getInventorySize()); + this.fluidTank = new NotifiableFluidTank(getInventorySize(), this, isExportHatch); initializeInventory(); } @@ -122,6 +123,20 @@ public void registerAbilities(List abilityList) { abilityList.addAll(isExportHatch ? this.exportFluids.getFluidTanks() : this.importFluids.getFluidTanks()); } + @Override + public void setupNotifiableMetaTileEntity(MetaTileEntity metaTileEntity) { + NotifiableFluidTank handler = null; + if (isExportHatch) { + handler = (NotifiableFluidTank) getExportFluids().getTankAt(0); + } else { + handler = (NotifiableFluidTank) getImportFluids().getTankAt(0); + } + if (handler != null) { + handler.setNotifiableMetaTileEntity(metaTileEntity); + handler.addToNotifiedList(this, handler, isExportHatch); + } + } + @Override protected ModularUI createUI(EntityPlayer entityPlayer) { return createTankUI((isExportHatch ? exportFluids : importFluids).getTankAt(0), containerInventory, getMetaFullName(), entityPlayer) diff --git a/src/main/java/gregtech/common/metatileentities/electric/multiblockpart/MetaTileEntityItemBus.java b/src/main/java/gregtech/common/metatileentities/electric/multiblockpart/MetaTileEntityItemBus.java index 1e243d96394..7f1d62ea9f7 100644 --- a/src/main/java/gregtech/common/metatileentities/electric/multiblockpart/MetaTileEntityItemBus.java +++ b/src/main/java/gregtech/common/metatileentities/electric/multiblockpart/MetaTileEntityItemBus.java @@ -13,6 +13,7 @@ import gregtech.api.metatileentity.multiblock.MultiblockAbility; import gregtech.api.render.SimpleOverlayRenderer; import gregtech.api.render.Textures; +import gregtech.api.capability.impl.NotifiableItemStackHandler; import net.minecraft.client.resources.I18n; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; @@ -70,12 +71,12 @@ private int getInventorySize() { @Override protected IItemHandlerModifiable createExportItemHandler() { - return isExportHatch ? new ItemStackHandler(getInventorySize()) : new ItemStackHandler(0); + return isExportHatch ? new NotifiableItemStackHandler(getInventorySize(), getController(), true) : new ItemStackHandler(0); } @Override protected IItemHandlerModifiable createImportItemHandler() { - return isExportHatch ? new ItemStackHandler(0) : new ItemStackHandler(getInventorySize()); + return isExportHatch ? new ItemStackHandler(0) : new NotifiableItemStackHandler(getInventorySize(), getController(), false); } @Override @@ -88,6 +89,20 @@ public void registerAbilities(List abilityList) { abilityList.add(isExportHatch ? this.exportItems : this.importItems); } + @Override + public void setupNotifiableMetaTileEntity(MetaTileEntity metaTileEntity) { + NotifiableItemStackHandler handler = null; + if (isExportHatch && getExportItems() instanceof NotifiableItemStackHandler) { + handler = (NotifiableItemStackHandler) getExportItems(); + } else if (!isExportHatch && getImportItems() instanceof NotifiableItemStackHandler) { + handler = (NotifiableItemStackHandler) getImportItems(); + } + if (handler != null) { + handler.setNotifiableMetaTileEntity(metaTileEntity); + handler.addToNotifiedList(this, handler, isExportHatch); + } + } + @Override protected ModularUI createUI(EntityPlayer entityPlayer) { int rowSize = (int) Math.sqrt(getInventorySize()); diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiFurnace.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiFurnace.java index a6b167be7df..2cabbc60431 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiFurnace.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiFurnace.java @@ -119,25 +119,28 @@ protected void trySearchNewRecipe() { Recipe currentRecipe = null; IItemHandlerModifiable importInventory = getInputInventory(); IMultipleTankHandler importFluids = getInputTank(); - boolean dirty = checkRecipeInputsDirty(importInventory, importFluids); + //inverse of logic in normal AbstractRecipeLogic //for MultiSmelter, we can reuse previous recipe if inputs didn't change //otherwise, we need to recompute it for new ingredients //but technically, it means we can cache multi smelter recipe, but changing inputs have more priority - if (dirty || forceRecipeRecheck) { - this.forceRecipeRecheck = false; - //else, try searching new recipe for given inputs - currentRecipe = findRecipe(maxVoltage, importInventory, importFluids, MatchingMode.DEFAULT); - if (currentRecipe != null) { - this.previousRecipe = currentRecipe; - } - } else if (previousRecipe != null && previousRecipe.matches(false, importInventory, importFluids)) { + if (hasNotifiedInputs() || + previousRecipe == null || + !previousRecipe.matches(false, importInventory, importFluids)) { + //Inputs changed, try searching new recipe for given inputs + currentRecipe = findRecipe(maxVoltage, importInventory, importFluids, MatchingMode.IGNORE_FLUIDS); + } else { //if previous recipe still matches inputs, try to use it currentRecipe = previousRecipe; } - if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe)) { + if (currentRecipe != null) + // replace old recipe with new one + this.previousRecipe = currentRecipe; + // proceed if we have a usable recipe. + if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe)) setupRecipe(currentRecipe); - } + // Inputs have been inspected. + metaTileEntity.getNotifiedItemInputList().clear(); } @Override @@ -152,7 +155,10 @@ protected Recipe findRecipe(long maxVoltage, /* Iterate over the input items looking for more things to add until we run either out of input items * or we have exceeded the number of items permissible from the smelting bonus */ - for (int index = 0; index < inputs.getSlots() && currentItemsEngaged < maxItemsLimit; index++) { + boolean matchedRecipe = false; + boolean canFitOutputs = true; + + for(int index = 0; index < inputs.getSlots() && currentItemsEngaged < maxItemsLimit; index++) { // Skip this slot if it is empty. final ItemStack currentInputItem = inputs.getStackInSlot(index); @@ -164,8 +170,10 @@ protected Recipe findRecipe(long maxVoltage, Collections.singletonList(currentInputItem), Collections.emptyList(), 0, MatchingMode.DEFAULT); CountableIngredient inputIngredient; - if (matchingRecipe != null) + if(matchingRecipe != null) { inputIngredient = matchingRecipe.getInputs().get(0); + matchedRecipe = true; + } else continue; @@ -192,7 +200,7 @@ protected Recipe findRecipe(long maxVoltage, computeOutputItemStacks(temp, matchingRecipe.getOutputs().get(0), recipeMultiplier); // determine if there is enough room in the output to fit all of this - boolean canFitOutputs = InventoryUtils.simulateItemStackMerge(temp, this.getOutputInventory()); + canFitOutputs = InventoryUtils.simulateItemStackMerge(temp, this.getOutputInventory()); // if there isn't, we can't process this recipe. if (!canFitOutputs) @@ -210,10 +218,10 @@ protected Recipe findRecipe(long maxVoltage, } } - // If there were no accepted ingredients, then there is no recipe to process. + this.invalidInputsForRecipes = !matchedRecipe; + this.isOutputsFull = !canFitOutputs; + if (recipeInputs.isEmpty()) { - //Set here to prevent recipe deadlock on world load with full output bus - forceRecipeRecheck = true; return null; } diff --git a/src/main/java/gregtech/common/metatileentities/steam/SteamAlloySmelter.java b/src/main/java/gregtech/common/metatileentities/steam/SteamAlloySmelter.java index e89739a8bdb..37db0e5cdf9 100644 --- a/src/main/java/gregtech/common/metatileentities/steam/SteamAlloySmelter.java +++ b/src/main/java/gregtech/common/metatileentities/steam/SteamAlloySmelter.java @@ -1,5 +1,6 @@ package gregtech.common.metatileentities.steam; +import gregtech.api.capability.impl.NotifiableItemStackHandler; import gregtech.api.gui.ModularUI; import gregtech.api.gui.resources.TextureArea; import gregtech.api.gui.widgets.ProgressWidget; @@ -13,7 +14,6 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.ResourceLocation; import net.minecraftforge.items.IItemHandlerModifiable; -import net.minecraftforge.items.ItemStackHandler; public class SteamAlloySmelter extends SteamMetaTileEntity { @@ -32,13 +32,13 @@ protected boolean isBrickedCasing() { } @Override - public IItemHandlerModifiable createImportItemHandler() { - return new ItemStackHandler(2); + protected IItemHandlerModifiable createImportItemHandler() { + return new NotifiableItemStackHandler(2, this, false); } @Override - public IItemHandlerModifiable createExportItemHandler() { - return new ItemStackHandler(1); + protected IItemHandlerModifiable createExportItemHandler() { + return new NotifiableItemStackHandler(1, this, true); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/steam/SteamCompressor.java b/src/main/java/gregtech/common/metatileentities/steam/SteamCompressor.java index 13ba51831cc..e016557810d 100644 --- a/src/main/java/gregtech/common/metatileentities/steam/SteamCompressor.java +++ b/src/main/java/gregtech/common/metatileentities/steam/SteamCompressor.java @@ -1,5 +1,6 @@ package gregtech.common.metatileentities.steam; +import gregtech.api.capability.impl.NotifiableItemStackHandler; import gregtech.api.gui.ModularUI; import gregtech.api.gui.widgets.ProgressWidget; import gregtech.api.gui.widgets.ProgressWidget.MoveType; @@ -12,7 +13,6 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.ResourceLocation; import net.minecraftforge.items.IItemHandlerModifiable; -import net.minecraftforge.items.ItemStackHandler; public class SteamCompressor extends SteamMetaTileEntity { @@ -26,13 +26,13 @@ public MetaTileEntity createMetaTileEntity(MetaTileEntityHolder holder) { } @Override - public IItemHandlerModifiable createImportItemHandler() { - return new ItemStackHandler(1); + protected IItemHandlerModifiable createImportItemHandler() { + return new NotifiableItemStackHandler(1, this, false); } @Override - public IItemHandlerModifiable createExportItemHandler() { - return new ItemStackHandler(1); + protected IItemHandlerModifiable createExportItemHandler() { + return new NotifiableItemStackHandler(1, this, true); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/steam/SteamExtractor.java b/src/main/java/gregtech/common/metatileentities/steam/SteamExtractor.java index 02174e5a6dc..f17942fbeb7 100644 --- a/src/main/java/gregtech/common/metatileentities/steam/SteamExtractor.java +++ b/src/main/java/gregtech/common/metatileentities/steam/SteamExtractor.java @@ -1,5 +1,6 @@ package gregtech.common.metatileentities.steam; +import gregtech.api.capability.impl.NotifiableItemStackHandler; import gregtech.api.gui.ModularUI; import gregtech.api.gui.widgets.ProgressWidget; import gregtech.api.gui.widgets.SlotWidget; @@ -11,7 +12,6 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.ResourceLocation; import net.minecraftforge.items.IItemHandlerModifiable; -import net.minecraftforge.items.ItemStackHandler; public class SteamExtractor extends SteamMetaTileEntity { @@ -25,13 +25,13 @@ public MetaTileEntity createMetaTileEntity(MetaTileEntityHolder holder) { } @Override - public IItemHandlerModifiable createImportItemHandler() { - return new ItemStackHandler(1); + protected IItemHandlerModifiable createImportItemHandler() { + return new NotifiableItemStackHandler(1, this, false); } @Override - public IItemHandlerModifiable createExportItemHandler() { - return new ItemStackHandler(1); + protected IItemHandlerModifiable createExportItemHandler() { + return new NotifiableItemStackHandler(1, this, true); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/steam/SteamFurnace.java b/src/main/java/gregtech/common/metatileentities/steam/SteamFurnace.java index 1e42b622935..68305c610bd 100644 --- a/src/main/java/gregtech/common/metatileentities/steam/SteamFurnace.java +++ b/src/main/java/gregtech/common/metatileentities/steam/SteamFurnace.java @@ -1,5 +1,6 @@ package gregtech.common.metatileentities.steam; +import gregtech.api.capability.impl.NotifiableItemStackHandler; import gregtech.api.gui.ModularUI; import gregtech.api.gui.widgets.ProgressWidget; import gregtech.api.gui.widgets.ProgressWidget.MoveType; @@ -12,7 +13,6 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.ResourceLocation; import net.minecraftforge.items.IItemHandlerModifiable; -import net.minecraftforge.items.ItemStackHandler; public class SteamFurnace extends SteamMetaTileEntity { @@ -31,13 +31,13 @@ protected boolean isBrickedCasing() { } @Override - public IItemHandlerModifiable createImportItemHandler() { - return new ItemStackHandler(1); + protected IItemHandlerModifiable createImportItemHandler() { + return new NotifiableItemStackHandler(1, this, false); } @Override - public IItemHandlerModifiable createExportItemHandler() { - return new ItemStackHandler(1); + protected IItemHandlerModifiable createExportItemHandler() { + return new NotifiableItemStackHandler(1, this, true); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/steam/SteamHammer.java b/src/main/java/gregtech/common/metatileentities/steam/SteamHammer.java index 0af37c20400..c8a3bbc5a0f 100644 --- a/src/main/java/gregtech/common/metatileentities/steam/SteamHammer.java +++ b/src/main/java/gregtech/common/metatileentities/steam/SteamHammer.java @@ -1,5 +1,6 @@ package gregtech.common.metatileentities.steam; +import gregtech.api.capability.impl.NotifiableItemStackHandler; import gregtech.api.gui.ModularUI; import gregtech.api.gui.widgets.ImageWidget; import gregtech.api.gui.widgets.ProgressWidget; @@ -12,7 +13,6 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.ResourceLocation; import net.minecraftforge.items.IItemHandlerModifiable; -import net.minecraftforge.items.ItemStackHandler; public class SteamHammer extends SteamMetaTileEntity { @@ -26,13 +26,13 @@ public MetaTileEntity createMetaTileEntity(MetaTileEntityHolder holder) { } @Override - public IItemHandlerModifiable createImportItemHandler() { - return new ItemStackHandler(1); + protected IItemHandlerModifiable createImportItemHandler() { + return new NotifiableItemStackHandler(1, this, false); } @Override - public IItemHandlerModifiable createExportItemHandler() { - return new ItemStackHandler(1); + protected IItemHandlerModifiable createExportItemHandler() { + return new NotifiableItemStackHandler(1, this, true); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/steam/SteamMacerator.java b/src/main/java/gregtech/common/metatileentities/steam/SteamMacerator.java index 29e4264a570..9870187b1ff 100644 --- a/src/main/java/gregtech/common/metatileentities/steam/SteamMacerator.java +++ b/src/main/java/gregtech/common/metatileentities/steam/SteamMacerator.java @@ -1,5 +1,6 @@ package gregtech.common.metatileentities.steam; +import gregtech.api.capability.impl.NotifiableItemStackHandler; import gregtech.api.capability.impl.RecipeLogicSteam; import gregtech.api.gui.ModularUI; import gregtech.api.gui.widgets.ProgressWidget; @@ -12,7 +13,6 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.ResourceLocation; import net.minecraftforge.items.IItemHandlerModifiable; -import net.minecraftforge.items.ItemStackHandler; public class SteamMacerator extends SteamMetaTileEntity { @@ -28,13 +28,13 @@ public MetaTileEntity createMetaTileEntity(MetaTileEntityHolder holder) { } @Override - public IItemHandlerModifiable createImportItemHandler() { - return new ItemStackHandler(1); + protected IItemHandlerModifiable createImportItemHandler() { + return new NotifiableItemStackHandler(1, this, false); } @Override - public IItemHandlerModifiable createExportItemHandler() { - return new ItemStackHandler(1); + protected IItemHandlerModifiable createExportItemHandler() { + return new NotifiableItemStackHandler(1, this, true); } @Override diff --git a/src/test/java/gregtech/api/capability/impl/AbstractRecipeLogicTest.java b/src/test/java/gregtech/api/capability/impl/AbstractRecipeLogicTest.java new file mode 100644 index 00000000000..e40d54b25f8 --- /dev/null +++ b/src/test/java/gregtech/api/capability/impl/AbstractRecipeLogicTest.java @@ -0,0 +1,132 @@ +package gregtech.api.capability.impl; + +import gregtech.api.*; +import gregtech.api.metatileentity.*; +import gregtech.api.recipes.*; +import gregtech.api.recipes.builders.*; +import gregtech.api.render.*; +import gregtech.api.util.world.DummyWorld; +import net.minecraft.init.*; +import net.minecraft.item.*; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.*; +import net.minecraft.world.World; +import org.junit.*; + +import static org.junit.Assert.*; + +public class AbstractRecipeLogicTest { + + @BeforeClass + public static void init() { + Bootstrap.register(); + } + + @Test + public void trySearchNewRecipe() { + + World world = DummyWorld.INSTANCE; + + // Create an empty recipe map to work with + RecipeMap map = new RecipeMap<>("chemical_reactor", + 0, + 2, + 0, + 2, + 0, + 3, + 0, + 2, + new SimpleRecipeBuilder().EUt(30), + false); + + MetaTileEntity at = + GregTechAPI.registerMetaTileEntity(190, + new SimpleMachineMetaTileEntity( + new ResourceLocation(GTValues.MODID, "chemical_reactor.lv"), + map, + Textures.CHEMICAL_REACTOR_OVERLAY, + 1)); + + MetaTileEntity atte = new MetaTileEntityHolder().setMetaTileEntity(at); + atte.getHolder().setWorld(world); + map.recipeBuilder() + .inputs(new ItemStack(Blocks.COBBLESTONE)) + .outputs(new ItemStack(Blocks.STONE)) + .EUt(1).duration(1) + .buildAndRegister(); + + AbstractRecipeLogic arl = new AbstractRecipeLogic(atte, map) { + @Override + protected long getEnergyStored() { + return Long.MAX_VALUE; + } + + @Override + protected long getEnergyCapacity() { + return Long.MAX_VALUE; + } + + @Override + protected boolean drawEnergy(int recipeEUt) { + return true; + } + + @Override + protected long getMaxVoltage() { + return 32; + } + }; + + arl.isOutputsFull = false; + arl.invalidInputsForRecipes = false; + arl.trySearchNewRecipe(); + + // no recipe found + assertTrue(arl.invalidInputsForRecipes); + assertFalse(arl.isActive); + assertNull(arl.previousRecipe); + + // put an item in the inventory that will trigger recipe recheck + arl.getInputInventory().insertItem(0, new ItemStack(Blocks.COBBLESTONE, 16), false); + // Inputs change. did we detect it ? + assertTrue(arl.hasNotifiedInputs()); + arl.trySearchNewRecipe(); + assertFalse(arl.invalidInputsForRecipes); + assertNotNull(arl.previousRecipe); + assertTrue(arl.isActive); + assertEquals(15, arl.getInputInventory().getStackInSlot(0).getCount()); + //assert the consumption of the inputs did not mark the arl to look for a new recipe + assertFalse(arl.hasNotifiedInputs()); + + // Save a reference to the old recipe so we can make sure it's getting reused + Recipe prev = arl.previousRecipe; + + // Finish the recipe, the output should generate, and the next iteration should begin + arl.update(); + assertEquals(prev, arl.previousRecipe); + assertTrue(AbstractRecipeLogic.areItemStacksEqual(arl.getOutputInventory().getStackInSlot(0), + new ItemStack(Blocks.STONE, 1))); + assertTrue(arl.isActive); + + // Complete the second iteration, but the machine stops because its output is now full + arl.getOutputInventory().setStackInSlot(0, new ItemStack(Blocks.STONE, 63)); + arl.getOutputInventory().setStackInSlot(1, new ItemStack(Blocks.STONE, 64)); + arl.update(); + assertFalse(arl.isActive); + assertTrue(arl.isOutputsFull); + + // Try to process again and get failed out because of full buffer. + arl.update(); + assertFalse(arl.isActive); + assertTrue(arl.isOutputsFull); + + // Some room is freed in the output bus, so we can continue now. + arl.getOutputInventory().setStackInSlot(1, ItemStack.EMPTY); + arl.update(); + assertTrue(arl.isActive); + assertFalse(arl.isOutputsFull); + assertTrue(AbstractRecipeLogic.areItemStacksEqual(arl.getOutputInventory().getStackInSlot(0), + new ItemStack(Blocks.STONE, 1))); + } +} diff --git a/src/test/java/gregtech/api/capability/impl/MultiblockRecipeLogicTest.java b/src/test/java/gregtech/api/capability/impl/MultiblockRecipeLogicTest.java new file mode 100644 index 00000000000..5592306d5a6 --- /dev/null +++ b/src/test/java/gregtech/api/capability/impl/MultiblockRecipeLogicTest.java @@ -0,0 +1,266 @@ +package gregtech.api.capability.impl; + +import gregtech.api.GTValues; +import gregtech.api.GregTechAPI; + +import gregtech.api.capability.IMultipleTankHandler; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.metatileentity.MetaTileEntityHolder; +import gregtech.api.metatileentity.multiblock.*; +import gregtech.api.recipes.Recipe; +import gregtech.api.recipes.RecipeMap; +import gregtech.api.recipes.RecipeMaps; +import gregtech.api.recipes.builders.BlastRecipeBuilder; +import gregtech.api.util.world.DummyWorld; +import gregtech.common.metatileentities.electric.multiblockpart.MetaTileEntityFluidHatch; +import gregtech.common.metatileentities.electric.multiblockpart.MetaTileEntityItemBus; +import gregtech.common.metatileentities.electric.multiblockpart.MetaTileEntityMultiblockPart; +import gregtech.common.metatileentities.multi.electric.MetaTileEntityElectricBlastFurnace; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import net.minecraft.init.Bootstrap; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; +import net.minecraftforge.items.IItemHandlerModifiable; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Field; + +import static org.junit.Assert.*; +import static org.junit.Assert.assertTrue; + +public class MultiblockRecipeLogicTest { + + @BeforeClass + public static void init() { + Bootstrap.register(); + } + + private static ResourceLocation gregtechId(String name) { + return new ResourceLocation(GTValues.MODID, name); + } + + @Test + public void trySearchNewRecipe() { + + World world = DummyWorld.INSTANCE; + + // Create an empty recipe map to work with + RecipeMap map = new RecipeMap<>("blast_furnace", + 1, + 3, + 1, + 2, + 0, + 1, + 0, + 1, + new BlastRecipeBuilder().EUt(32), + false); + + RecipeMaps.BLAST_RECIPES.recipeBuilder() + .inputs(new ItemStack(Blocks.COBBLESTONE)) + .outputs(new ItemStack(Blocks.STONE)) + .EUt(1).duration(1) + .blastFurnaceTemp(1) + .buildAndRegister(); + + RecipeMapMultiblockController mbt = + GregTechAPI.registerMetaTileEntity(511, + new MetaTileEntityElectricBlastFurnace( + // super function calls the world, which equal null in test + new ResourceLocation(GTValues.MODID, "electric_blast_furnace")) { + @Override + protected void reinitializeStructurePattern() { + + } + + // function checks for the temperature of the recipe against the coils + @Override + public boolean checkRecipe(Recipe recipe, boolean consumeIfSuccess) { + return true; + } + }); + + //isValid() check in the dirtying logic requires both a metatileentity and a holder + try { + Field field = MetaTileEntity.class.getDeclaredField("holder"); + field.setAccessible(true); + field.set(mbt, new MetaTileEntityHolder()); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + + try { + Field field = MetaTileEntityHolder.class.getDeclaredField("metaTileEntity"); + field.setAccessible(true); + field.set(mbt.getHolder(), mbt); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + + mbt.getHolder().setWorld(world); + + //Controller and isAttachedToMultiBlock need the world so we fake it here. + MetaTileEntityItemBus importItemBus = new MetaTileEntityItemBus(gregtechId("item_bus.export.lv"), 1, false) { + @Override + public boolean isAttachedToMultiBlock() { + return true; + } + + @Override + public MultiblockControllerBase getController() { + return mbt; + } + }; + MetaTileEntityItemBus exportItemBus = new MetaTileEntityItemBus(gregtechId("item_bus.export.lv"), 1, true) { + @Override + public boolean isAttachedToMultiBlock() { + return true; + } + + @Override + public MultiblockControllerBase getController() { + return mbt; + } + }; + MetaTileEntityFluidHatch importFluidBus = new MetaTileEntityFluidHatch(gregtechId("fluid_hatch.import.lv"), 1, false) { + @Override + public boolean isAttachedToMultiBlock() { + return true; + } + + @Override + public MultiblockControllerBase getController() { + return mbt; + } + }; + MetaTileEntityFluidHatch exportFluidBus = new MetaTileEntityFluidHatch(gregtechId("fluid_hatch.export.lv"), 1, true) { + @Override + public boolean isAttachedToMultiBlock() { + return true; + } + + @Override + public MultiblockControllerBase getController() { + return mbt; + } + }; + + //Controller is a private field but we need that information + try { + Field field = MetaTileEntityMultiblockPart.class.getDeclaredField("controllerTile"); + field.setAccessible(true); + field.set(importItemBus, mbt); + field.set(exportItemBus, mbt); + field.set(importFluidBus, mbt); + field.set(exportFluidBus, mbt); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + + MultiblockRecipeLogic mbl = new MultiblockRecipeLogic(mbt) { + + @Override + protected long getEnergyStored() { + return Long.MAX_VALUE; + } + + @Override + protected long getEnergyCapacity() { + return Long.MAX_VALUE; + } + + @Override + protected boolean drawEnergy(int recipeEUt) { + return true; + } + + @Override + protected long getMaxVoltage() { + return 32; + } + + // since the hatches were not really added to a valid multiblock structure, + // refer to their inventories directly + @Override + protected IItemHandlerModifiable getInputInventory() { + return importItemBus.getImportItems(); + } + + @Override + protected IItemHandlerModifiable getOutputInventory() { + return exportItemBus.getExportItems(); + } + + @Override + protected IMultipleTankHandler getInputTank() { + return importFluidBus.getImportFluids(); + } + + @Override + protected IMultipleTankHandler getOutputTank() { + return importFluidBus.getExportFluids(); + } + + }; + + mbl.isOutputsFull = false; + mbl.invalidInputsForRecipes = false; + mbl.trySearchNewRecipe(); + + // no recipe found + assertTrue(mbl.invalidInputsForRecipes); + assertFalse(mbl.isActive); + assertNull(mbl.previousRecipe); + + // put an item in the inventory that will trigger recipe recheck + mbl.getInputInventory().insertItem(0, new ItemStack(Blocks.COBBLESTONE, 16), false); + // Inputs change. did we detect it ? + assertTrue(mbl.hasNotifiedInputs()); + mbl.trySearchNewRecipe(); + assertFalse(mbl.invalidInputsForRecipes); + assertNotNull(mbl.previousRecipe); + assertTrue(mbl.isActive); + assertEquals(15, mbl.getInputInventory().getStackInSlot(0).getCount()); + //assert the consumption of the inputs did not mark the arl to look for a new recipe + assertFalse(mbl.hasNotifiedInputs()); + + // Save a reference to the old recipe so we can make sure it's getting reused + Recipe prev = mbl.previousRecipe; + + // Finish the recipe, the output should generate, and the next iteration should begin + mbl.updateWorkable(); + assertEquals(prev, mbl.previousRecipe); + assertTrue(AbstractRecipeLogic.areItemStacksEqual(mbl.getOutputInventory().getStackInSlot(0), + new ItemStack(Blocks.STONE, 1))); + assertTrue(mbl.isActive); + + // Complete the second iteration, but the machine stops because its output is now full + mbl.getOutputInventory().setStackInSlot(0, new ItemStack(Blocks.STONE, 63)); + mbl.getOutputInventory().setStackInSlot(1, new ItemStack(Blocks.STONE, 64)); + mbl.getOutputInventory().setStackInSlot(2, new ItemStack(Blocks.STONE, 64)); + mbl.getOutputInventory().setStackInSlot(3, new ItemStack(Blocks.STONE, 64)); + mbl.updateWorkable(); + assertFalse(mbl.isActive); + assertTrue(mbl.isOutputsFull); + + // Try to process again and get failed out because of full buffer. + mbl.updateWorkable(); + assertFalse(mbl.isActive); + assertTrue(mbl.isOutputsFull); + + // Some room is freed in the output bus, so we can continue now. + mbl.getOutputInventory().setStackInSlot(1, ItemStack.EMPTY); + assertTrue(mbl.hasNotifiedOutputs()); + mbl.updateWorkable(); + assertTrue(mbl.isActive); + assertFalse(mbl.isOutputsFull); + mbl.completeRecipe(); + assertTrue(AbstractRecipeLogic.areItemStacksEqual(mbl.getOutputInventory().getStackInSlot(0), + new ItemStack(Blocks.STONE, 1))); + } +}