Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better idling for metatileentities and multiblocks #1554

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c6454cc
Reworked trySearchNewRecipe and findRecipe
PrototypeTrousers Mar 15, 2021
9c622be
rework logic
PrototypeTrousers Mar 15, 2021
3c0e6f1
Refactor trySearchNewRecipe; add test to show issue re: lastItemOutputs
Exaxxion Mar 16, 2021
2655473
Changed item merging from full inventory copy to partial stacks copy
PrototypeTrousers Mar 25, 2021
27f4700
Implement notifiable item/fluid handlers
PrototypeTrousers Mar 30, 2021
1146268
enable the recipe logic to make use of the notifiable handlers
PrototypeTrousers Mar 30, 2021
ba882ee
address issues bought up on discussion
PrototypeTrousers Mar 31, 2021
5c8815d
made a declaration more obvious and with more detailed explanation
PrototypeTrousers Mar 31, 2021
281cabc
reverted change due to NPE
PrototypeTrousers Mar 31, 2021
d2a0a7d
fix multismelter getting stuck when its output gets full
PrototypeTrousers Apr 2, 2021
268a938
switched from HashSet to ArrayList.
PrototypeTrousers Apr 2, 2021
3b6cd4c
switch to a linked list
PrototypeTrousers Apr 9, 2021
279dd31
Simplified handler logic by only allowing one metatile to be notified
PrototypeTrousers Apr 11, 2021
e634951
address issues brought up
PrototypeTrousers May 25, 2021
8d04e76
Revert "Changed item merging from full inventory copy to partial stac…
PrototypeTrousers Mar 25, 2021
063a3e5
pass handlers around instead of a boolean
PrototypeTrousers May 29, 2021
84a29ae
fix tests due to world being null
PrototypeTrousers May 29, 2021
381d009
formatting
PrototypeTrousers May 30, 2021
754e1b4
update javadoc
PrototypeTrousers May 30, 2021
698d225
simplify logic
PrototypeTrousers Jun 2, 2021
694d97b
match deprecated behaviour
PrototypeTrousers Jun 5, 2021
b424b73
fix merge conflict
PrototypeTrousers Jun 5, 2021
36ac872
removed unnecesarry override and imports
PrototypeTrousers Jun 5, 2021
4eaff0f
address reviewed issues
PrototypeTrousers Jun 6, 2021
dfd6617
Add notifiable handlers to steam variant of metaTileEntities
PrototypeTrousers Jun 6, 2021
c847713
make the if statements more readable
PrototypeTrousers Jul 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/main/java/gregtech/api/capability/INotifiableHandler.java
Original file line number Diff line number Diff line change
@@ -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 {
LAGIdiot marked this conversation as resolved.
Show resolved Hide resolved

/**
* Adds the notified handler to the notified list
*
* @param isExport boolean specifying if a handler is an output handler
*/

default <T> 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this really be a default method?
Is there a case where an implementing class doesn't implement this method?


}
}
148 changes: 86 additions & 62 deletions src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
PrototypeTrousers marked this conversation as resolved.
Show resolved Hide resolved
@Deprecated protected ItemStack[] lastItemInputs;
@Deprecated protected FluidStack[] lastFluidInputs;
protected Recipe previousRecipe;
protected boolean allowOverclocking = true;
private long overclockVoltage = 0;
Expand All @@ -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;
PrototypeTrousers marked this conversation as resolved.
Show resolved Hide resolved
protected boolean invalidInputsForRecipes;

public AbstractRecipeLogic(MetaTileEntity tileEntity, RecipeMap<?> recipeMap) {
super(tileEntity);
Expand Down Expand Up @@ -106,12 +108,14 @@ public <T> T getCapability(Capability<T> 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();
}
}
Expand All @@ -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)) {
Expand Down Expand Up @@ -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() {
Expand All @@ -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
PrototypeTrousers marked this conversation as resolved.
Show resolved Hide resolved
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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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));
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading