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

Multiblock regression and bug fixes, new Jammed state #6

Merged
merged 7 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ protected void trySearchNewRecipe() {
IMultipleTankHandler importFluids = getInputTank();

// see if the last recipe we used still works
if (this.previousRecipe != null && this.previousRecipe.matches(false, importInventory, importFluids))
if (checkPreviousRecipe())
currentRecipe = this.previousRecipe;
// If there is no active recipe, then we need to find one.
else {
Expand All @@ -201,11 +201,17 @@ protected void trySearchNewRecipe() {
// proceed if we have a usable recipe.
if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe))
setupRecipe(currentRecipe);

// Inputs have been inspected.
metaTileEntity.getNotifiedItemInputList().clear();
metaTileEntity.getNotifiedFluidInputList().clear();
}

protected boolean checkPreviousRecipe() {
if(this.previousRecipe == null) return false;
if(this.previousRecipe.getEUt() > this.getMaxVoltage()) return false;
return this.previousRecipe.matches(false, getInputInventory(), getInputTank());
}

protected int getMinTankCapacity(IMultipleTankHandler tanks) {
if(tanks.getTanks() == 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@

import java.util.List;

import static gregtech.api.metatileentity.MetaTileEntity.addItemsToItemHandler;
import static gregtech.api.metatileentity.MetaTileEntity.addFluidsToFluidHandler;

public class MultiblockRecipeLogic extends AbstractRecipeLogic {

/** Indicates that a structure fails to meet requirements for proceeding with the active recipe */
protected boolean isJammed = false;

private boolean invalidated = true;

public MultiblockRecipeLogic(RecipeMapMultiblockController tileEntity) {
super(tileEntity, tileEntity.recipeMap);
Expand All @@ -28,14 +35,7 @@ public void updateWorkable() {
* Used to reset cached values in the Recipe Logic on structure deform
*/
public void invalidate() {
previousRecipe = null;
progressTime = 0;
maxProgressTime = 0;
recipeEUt = 0;
fluidOutputs = null;
itemOutputs = null;
isOutputsFull = false;
setActive(false); // this marks dirty for us
invalidated = true;
}

public IEnergyContainer getEnergyContainer() {
Expand Down Expand Up @@ -106,4 +106,61 @@ protected boolean drawEnergy(int recipeEUt) {
protected long getMaxVoltage() {
return Math.max(getEnergyContainer().getInputVoltage(), getEnergyContainer().getOutputVoltage());
}

public boolean isJammed() {
return this.isJammed;
}

private void checkIfJammed() {
if(metaTileEntity instanceof RecipeMapMultiblockController controller) {
// determine if outputs will fit
boolean canFitItems = addItemsToItemHandler(getOutputInventory(), true, itemOutputs);
boolean canFitFluids = addFluidsToFluidHandler(getOutputTank(), true, fluidOutputs);

// clear output notifications since we just checked them
metaTileEntity.getNotifiedItemOutputList().clear();
metaTileEntity.getNotifiedFluidOutputList().clear();

// Jam if we can't output all items and fluids, or we fail whatever other conditions the controller imposes
this.isJammed = !(canFitItems && canFitFluids && controller.checkRecipe(previousRecipe, false));
}
}

private boolean hasOutputChanged() {
return hasNotifiedOutputs() &&
(!metaTileEntity.getNotifiedItemOutputList().isEmpty() ||
!metaTileEntity.getNotifiedFluidOutputList().isEmpty());
}

@Override
protected void updateRecipeProgress() {
// Recheck jammed status after the structure has been invalidated or output inventories were modified
if(invalidated || hasOutputChanged())
checkIfJammed();

// Only proceed if we're not jammed
if(!isJammed)
// if the recipe is running
if(progressTime < maxProgressTime) {
// clear invalidation flag
invalidated = false;
// do normal update check
super.updateRecipeProgress();
} else
// the recipe is done but was probably jammed. Try to complete it.
completeRecipe();
}

@Override
protected void completeRecipe() {
/*
Since multiblocks can share parts, if multiple machines try to output on the same tick and can't,
the excess outputs would be silently voided. Avoid this scenario by doing a final Jammed state check.
*/
checkIfJammed();

// If we're not jammed, proceed with completing the recipe. Otherwise, wait for outputs to notify.
if(!this.isJammed)
super.completeRecipe();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,10 @@ protected void addDisplayText(List<ITextComponent> textList) {
textList.add(new TextComponentTranslation("gregtech.multiblock.max_energy_per_tick", maxVoltage, voltageName));
}

if (!recipeMapWorkable.isWorkingEnabled()) {
if(recipeMapWorkable.isJammed()) {
textList.add(new TextComponentTranslation("gregtech.multiblock.jammed"));
} else if (!recipeMapWorkable.isWorkingEnabled()) {
textList.add(new TextComponentTranslation("gregtech.multiblock.work_paused"));

} else if (recipeMapWorkable.isActive()) {
textList.add(new TextComponentTranslation("gregtech.multiblock.running"));
int currentProgress = (int) (recipeMapWorkable.getProgressPercent() * 100);
Expand Down
50 changes: 45 additions & 5 deletions src/main/java/gregtech/api/recipes/Recipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.items.IItemHandlerModifiable;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -236,15 +237,43 @@ public NonNullList<ItemStack> getOutputs() {
return outputs;
}

public List<ItemStack> getResultItemOutputs(int maxOutputSlots, Random random, int tier) {
/**
* Computes real outputs of a recipe, truncated to a maximum number of output slots.
*
* @param maxOutputSlots the number of output slots to consider
* @param random the Random to use for chanced recipe outputs
* @param overclocks the number of overclocks for applying tiered bonuses
* @return the outputs of the current iteration of running the recipe
*/
public List<ItemStack> getResultItemOutputs(int maxOutputSlots, @NotNull Random random, int overclocks) {
assert maxOutputSlots >= 0;
assert overclocks >= 0;

// Nothing to return if there are no output slots
if(maxOutputSlots == 0)
return Collections.emptyList();

// Get fixed and chanced outputs
ArrayList<ItemStack> outputs = new ArrayList<>(GTUtility.copyStackList(getOutputs()));
List<ChanceEntry> chancedOutputsList = getChancedOutputs();

// If there's enough fixed outputs to reach the max, return as many as will fit.
if (outputs.size() >= maxOutputSlots)
return outputs.subList(0, maxOutputSlots);

// if there are no chanced outputs, then we can just return the standard outputs.
if(chancedOutputsList.isEmpty())
return outputs;

// Truncate the chanced outputs list to fit remaining available space
int maxChancedSlots = maxOutputSlots - outputs.size();
if (chancedOutputsList.size() > maxChancedSlots) {
chancedOutputsList = chancedOutputsList.subList(0, Math.max(0, maxChancedSlots));
}
if (chancedOutputsList.size() > maxChancedSlots)
chancedOutputsList = chancedOutputsList.subList(0, maxChancedSlots);

// Roll each chanced output to see if it is actually produced
final RecipeMap.IChanceFunction cf = RecipeMap.getChanceFunction();
for (ChanceEntry chancedOutput : chancedOutputsList) {
int outputChance = RecipeMap.getChanceFunction().chanceFor(chancedOutput.getChance(), chancedOutput.getBoostPerTier(), tier);
int outputChance = cf.chanceFor(chancedOutput.getChance(), chancedOutput.getBoostPerTier(), overclocks);
if (random.nextInt(Recipe.getMaxChancedValue()) <= outputChance) {
outputs.add(chancedOutput.getItemStack().copy());
}
Expand Down Expand Up @@ -304,6 +333,17 @@ public boolean hasValidInputsForDisplay() {
return hasValidInputs;
}

/**
* Retrieve a property or fallback value from the property store
* @param property the desired property
* @param defaultValue fallback value
* @param <T> type of the property
* @return the requested property's value, or the fallback value if a value isn't available.
*/
public <T> T getProperty(RecipeProperty<T> property, T defaultValue) {
return recipePropertyStorage.getRecipePropertyValue(property, defaultValue);
}

//region RecipeProperties

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import gregtech.api.multiblock.PatternMatchContext;
import gregtech.api.recipes.Recipe;
import gregtech.api.recipes.RecipeMaps;
import gregtech.api.recipes.recipeproperties.*;
import gregtech.api.render.ICubeRenderer;
import gregtech.api.render.OrientedOverlayRenderer;
import gregtech.api.render.Textures;
Expand Down Expand Up @@ -68,8 +69,7 @@ public void invalidateStructure() {

@Override
public boolean checkRecipe(Recipe recipe, boolean consumeIfSuccess) {
int recipeRequiredTemp = recipe.getIntegerProperty("blast_furnace_temperature");
return this.blastFurnaceTemperature >= recipeRequiredTemp;
return this.blastFurnaceTemperature >= recipe.getProperty(BlastTemperatureProperty.getInstance(),0);
}

public static Predicate<BlockWorldState> heatingCoilPredicate() {
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/assets/gregtech/lang/en_us.lang
Original file line number Diff line number Diff line change
Expand Up @@ -2962,6 +2962,7 @@ gregtech.fluid_pipe.throughput=Transfer: §e%,d mb/t
gregtech.fluid_pipe.max_temperature=Max Temperature: §c%,dK
gregtech.fluid_pipe.non_gas_proof=Can't transfer gases.

gregtech.multiblock.jammed=§cUnable to complete the recipe with the current configuration.
gregtech.multiblock.work_paused=Work Paused.
gregtech.multiblock.running=Running perfectly.
gregtech.multiblock.idling=Idling.
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/assets/gregtech/lang/ru_ru.lang
Original file line number Diff line number Diff line change
Expand Up @@ -2859,6 +2859,7 @@ gregtech.fluid_pipe.throughput=Пропускная способность: §e%
gregtech.fluid_pipe.max_temperature=Максимальная температура: §c%dK
gregtech.fluid_pipe.non_gas_proof=Не может переносить газы.

gregtech.multiblock.jammed=§cНевозможно завершить рецепт с текущей конфигурацией (машины).
gregtech.multiblock.work_paused=Работа приостановлена.
gregtech.multiblock.running=Работает отлично.
gregtech.multiblock.idling=Холостой ход.
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/assets/gregtech/lang/zh_cn.lang
Original file line number Diff line number Diff line change
Expand Up @@ -2838,6 +2838,7 @@ gregtech.fluid_pipe.throughput=传输速率: §e%d 升/刻
gregtech.fluid_pipe.max_temperature=温度上限: §c%d K
gregtech.fluid_pipe.non_gas_proof=不能传输气体.

gregtech.multiblock.jammed=§c当前(机器)配置无法完成配方。
gregtech.multiblock.work_paused=暂停.
gregtech.multiblock.running=运行正常.
gregtech.multiblock.idling=待机.
Expand Down