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 0000000000..34cf1c3635 --- /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 index 6b92342620..c85045228f 100755 --- a/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java @@ -15,6 +15,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; @@ -23,7 +24,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; @@ -36,8 +36,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; @@ -54,6 +54,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; public AbstractRecipeLogic(MetaTileEntity tileEntity, RecipeMap recipeMap) { super(tileEntity); @@ -106,12 +108,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(); } } @@ -122,6 +126,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)) { @@ -149,23 +187,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); - 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); } - 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() { @@ -187,40 +228,16 @@ protected Recipe findRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMul return recipeMap.findRecipe(maxVoltage, inputs, fluidInputs, getMinTankCapacity(getOutputTank())); } + /** + * @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) { @@ -236,11 +253,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) { @@ -321,9 +347,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() { @@ -356,7 +379,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 0000000000..7e5d3ab455 --- /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 0000000000..2c54e096db --- /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 0000000000..88e3c58e07 --- /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/metatileentity/MetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java index 4ca7412551..2c4575294d 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; @@ -42,6 +40,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; @@ -52,8 +51,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; @@ -90,6 +88,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; @@ -257,6 +259,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 @@ -1213,6 +1239,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/WorkableTieredMetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/WorkableTieredMetaTileEntity.java index a64c9b1383..2ae8d2cd26 100644 --- a/src/main/java/gregtech/api/metatileentity/WorkableTieredMetaTileEntity.java +++ b/src/main/java/gregtech/api/metatileentity/WorkableTieredMetaTileEntity.java @@ -4,9 +4,7 @@ import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; import gregtech.api.GTValues; -import gregtech.api.capability.impl.RecipeLogicEnergy; -import gregtech.api.capability.impl.FilteredFluidHandler; -import gregtech.api.capability.impl.FluidTankList; +import gregtech.api.capability.impl.*; import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeMap; import gregtech.api.render.OrientedOverlayRenderer; @@ -56,13 +54,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 @@ -70,7 +68,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; } @@ -82,7 +80,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 ca3213547f..1d343bbb68 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 73f3f9d9e9..ef6d75300c 100644 --- a/src/main/java/gregtech/api/metatileentity/multiblock/MultiblockControllerBase.java +++ b/src/main/java/gregtech/api/metatileentity/multiblock/MultiblockControllerBase.java @@ -184,6 +184,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/common/metatileentities/electric/MetaTileEntityMacerator.java b/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntityMacerator.java index f019595b7e..e20fc866a5 100644 --- a/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntityMacerator.java +++ b/src/main/java/gregtech/common/metatileentities/electric/MetaTileEntityMacerator.java @@ -9,9 +9,9 @@ import gregtech.api.recipes.RecipeMap; import gregtech.api.render.OrientedOverlayRenderer; import gregtech.api.util.GTUtility; +import gregtech.api.capability.impl.NotifiableItemStackHandler; import net.minecraft.util.ResourceLocation; import net.minecraftforge.items.IItemHandlerModifiable; -import net.minecraftforge.items.ItemStackHandler; public class MetaTileEntityMacerator extends SimpleMachineMetaTileEntity { @@ -41,11 +41,11 @@ protected int getMachineTierForRecipe(Recipe recipe) { @Override protected IItemHandlerModifiable createExportItemHandler() { - return new ItemStackHandler(outputAmount); + return new NotifiableItemStackHandler(outputAmount, this, true); } @Override public MetaTileEntity createMetaTileEntity(MetaTileEntityHolder holder) { return new MetaTileEntityMacerator(metaTileEntityId, workable.recipeMap, outputAmount, renderer, getTier()); } -} \ No newline at end of file +} 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 332cff55ec..1664c39e60 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; @@ -24,7 +25,6 @@ import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; -import net.minecraftforge.fluids.FluidTank; import net.minecraftforge.fluids.IFluidTank; import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.ItemStackHandler; @@ -36,14 +36,14 @@ public class MetaTileEntityFluidHatch extends MetaTileEntityMultiblockPart imple private static final int INITIAL_INVENTORY_SIZE = 8000; private ItemStackHandler containerInventory; - private FluidTank fluidTank; + private NotifiableFluidTank fluidTank; private boolean isExportHatch; public MetaTileEntityFluidHatch(ResourceLocation metaTileEntityId, int tier, boolean isExportHatch) { super(metaTileEntityId, tier); this.containerInventory = new ItemStackHandler(2); this.isExportHatch = isExportHatch; - this.fluidTank = new FluidTank(getInventorySize()); + this.fluidTank = new NotifiableFluidTank(getInventorySize(), this, isExportHatch); initializeInventory(); } @@ -120,6 +120,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 a3703f5588..39783c005e 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 10878bcb90..1651bc56c4 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiFurnace.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiFurnace.java @@ -105,25 +105,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 + if (hasNotifiedInputs() || + previousRecipe == null || + !previousRecipe.matches(false, importInventory, importFluids)) { + //Inputs changed, try searching new recipe for given inputs currentRecipe = findRecipe(maxVoltage, importInventory, importFluids); - if (currentRecipe != null) { - this.previousRecipe = currentRecipe; - } - } else if (previousRecipe != null && previousRecipe.matches(false, importInventory, importFluids)) { + } 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 @@ -139,6 +142,9 @@ 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 */ + boolean matchedRecipe = false; + boolean canFitOutputs = true; + for(int index = 0; index < inputs.getSlots() && currentItemsEngaged < maxItemsLimit; index++) { // Skip this slot if it is empty. @@ -151,8 +157,10 @@ protected Recipe findRecipe(long maxVoltage, Collections.singletonList(currentInputItem), Collections.emptyList(), 0); CountableIngredient inputIngredient; - if(matchingRecipe != null) + if(matchingRecipe != null) { inputIngredient = matchingRecipe.getInputs().get(0); + matchedRecipe = true; + } else continue; @@ -179,7 +187,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) @@ -197,10 +205,10 @@ protected Recipe findRecipe(long maxVoltage, } } - // If there were no accepted ingredients, then there is no recipe to process. - if(recipeInputs.isEmpty()) { - //Set here to prevent recipe deadlock on world load with full output bus - forceRecipeRecheck = true; + this.invalidInputsForRecipes = !matchedRecipe; + this.isOutputsFull = !canFitOutputs; + + if (recipeInputs.isEmpty()) { 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 5a7704f877..25076e17ba 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 2efacbf4a1..f6d2c28419 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 c9d59185bc..9966264a14 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 375aefb678..f6d39bfb28 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 c21c7eb069..b382694c51 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 bb19827bed..e9a50894f5 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 0000000000..aa3a3d78ab --- /dev/null +++ b/src/test/java/gregtech/api/capability/impl/AbstractRecipeLogicTest.java @@ -0,0 +1,131 @@ +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)); + + 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 0000000000..837f4cf1c4 --- /dev/null +++ b/src/test/java/gregtech/api/capability/impl/MultiblockRecipeLogicTest.java @@ -0,0 +1,265 @@ +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)); + + 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))); + } +}