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 MTEs #91

Merged
merged 29 commits into from
Aug 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
738e4d9
Revert "attempt to fix redstone world corruption"
PrototypeTrousers Aug 15, 2021
b143975
Reworked trySearchNewRecipe and findRecipe
PrototypeTrousers Mar 15, 2021
b46aa0f
rework logic
PrototypeTrousers Mar 15, 2021
08fa382
Refactor trySearchNewRecipe; add test to show issue re: lastItemOutputs
Exaxxion Mar 16, 2021
071cdff
Changed item merging from full inventory copy to partial stacks copy
PrototypeTrousers Mar 25, 2021
bccb08a
Implement notifiable item/fluid handlers
PrototypeTrousers Mar 30, 2021
cd81511
enable the recipe logic to make use of the notifiable handlers
PrototypeTrousers Mar 30, 2021
db86ed3
address issues bought up on discussion
PrototypeTrousers Mar 31, 2021
8f036f6
made a declaration more obvious and with more detailed explanation
PrototypeTrousers Mar 31, 2021
8bbc7a4
reverted change due to NPE
PrototypeTrousers Mar 31, 2021
b320be2
fix multismelter getting stuck when its output gets full
PrototypeTrousers Apr 2, 2021
c82d2d2
switched from HashSet to ArrayList.
PrototypeTrousers Apr 2, 2021
563fb00
switch to a linked list
PrototypeTrousers Apr 9, 2021
6f995b7
Simplified handler logic by only allowing one metatile to be notified
PrototypeTrousers Apr 11, 2021
a88a1f0
address issues brought up
PrototypeTrousers May 25, 2021
b46b640
Revert "Changed item merging from full inventory copy to partial stac…
PrototypeTrousers Mar 25, 2021
ca69f6b
pass handlers around instead of a boolean
PrototypeTrousers May 29, 2021
435af7b
fix tests due to world being null
PrototypeTrousers May 29, 2021
3b71019
formatting
PrototypeTrousers May 30, 2021
7f6423a
update javadoc
PrototypeTrousers May 30, 2021
d9efe01
simplify logic
PrototypeTrousers Jun 2, 2021
1820287
match deprecated behaviour
PrototypeTrousers Jun 5, 2021
9f967d2
fix merge conflict
PrototypeTrousers Jun 5, 2021
80e7167
removed unnecesarry override and imports
PrototypeTrousers Jun 5, 2021
e74964e
address reviewed issues
PrototypeTrousers Jun 6, 2021
56a085a
Add notifiable handlers to steam variant of metaTileEntities
PrototypeTrousers Jun 6, 2021
cdf1362
make the if statements more readable
PrototypeTrousers Jul 22, 2021
9f9c4b4
gtceu rebased
PrototypeTrousers Aug 15, 2021
2283e49
add missing mte / multi
PrototypeTrousers Aug 17, 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 {

/**
* 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) {

}
}
148 changes: 86 additions & 62 deletions src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -115,12 +117,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 @@ -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)) {
Expand Down Expand Up @@ -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() {
Expand All @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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));
}
}
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