Skip to content
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 @@ -383,12 +383,10 @@ public Builder addCustomProgressLine(RecipeLogic recipeLogic) {
public Builder addRecipeFailReasonLine(RecipeLogic recipeLogic) {
if (!isStructureFormed || !recipeLogic.isIdle())
return this;
var reasons = recipeLogic.getFailureReasons();
if (!reasons.isEmpty()) {
var reason = recipeLogic.getBestFailureReason();
if (reason != null) {
textList.add(Component.translatable("gtceu.recipe_logic.setup_fail").withStyle(ChatFormatting.RED));
for (var reason : reasons) {
textList.add(Component.literal(" - ").append(reason));
}
textList.add(Component.literal(" - ").append(reason));
}
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ public List<IWidget> getWidgetsForDisplay(PanelSyncManager syncManager) {
widgets.add(GTMultiblockTextUtil.addSubtickParallelsLine(this, syncManager));
widgets.add(GTMultiblockTextUtil.addTotalRunsLine(this, syncManager));
widgets.add(GTMultiblockTextUtil.addOutputLines(this, syncManager));
widgets.addAll(GTMultiblockTextUtil.addRecipeFailReasonLines(this, syncManager));
return widgets;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import com.gregtechceu.gtceu.api.recipe.ActionResult;
import com.gregtechceu.gtceu.api.recipe.GTRecipe;
import com.gregtechceu.gtceu.api.recipe.RecipeHelper;
import com.gregtechceu.gtceu.api.recipe.modifier.ModifierFunction;
import com.gregtechceu.gtceu.api.registry.GTRegistries;
import com.gregtechceu.gtceu.api.sound.AutoReleasedSound;
import com.gregtechceu.gtceu.api.sync_system.ClassSyncData;
Expand Down Expand Up @@ -83,18 +82,21 @@ public enum Status implements StringRepresentable {
@RerenderOnChanged
protected boolean isActive;

@Getter
/**
* Why the machine isn't running: either why the in-flight recipe stalled, or
* why the closest-matching candidate was rejected. Cleared once the machine starts working again.
*/
@Nullable
@SaveField
@SyncToClient
private Component waitingReason = null;
@Getter
protected Component bestFailureReason;

/** The recipe {@link #bestFailureReason} belongs to. */
@Nullable
@Getter
@SyncToClient
protected final List<Component> failureReasons = new ArrayList<>();
protected GTRecipe bestFailureRecipe;

@Getter
protected final Map<GTRecipe, Component> failureReasonMap = new HashMap<>();
protected double bestFailureScore = Double.NEGATIVE_INFINITY;
/**
* unsafe, it may not be found from {@link RecipeManager}. Do not index it.
*/
Expand All @@ -103,10 +105,7 @@ public enum Status implements StringRepresentable {
@SaveField
@SyncToClient
protected GTRecipe lastRecipe;
@Getter
@SaveField
@SyncToClient
protected int consecutiveRecipes = 0; // Consecutive recipes that have been run

/**
* safe, it is the origin recipe before {@link IRecipeLogicMachine#fullModifyRecipe(GTRecipe)}'
* which can be found
Expand All @@ -116,6 +115,12 @@ public enum Status implements StringRepresentable {
@Getter
@SaveField
protected GTRecipe lastOriginRecipe;

@Getter
@SaveField
@SyncToClient
protected int consecutiveRecipes = 0; // Consecutive recipes that have been run

@SaveField
@Getter
@SyncToClient
Expand Down Expand Up @@ -197,8 +202,7 @@ public void resetRecipeLogic() {
duration = 0;
isActive = false;
lastFailedMatches = null;
waitingReason = null;
failureReasons.clear();
clearFailureReason();
if (status != Status.SUSPEND) {
setStatus(Status.IDLE);
}
Expand Down Expand Up @@ -282,10 +286,6 @@ public void serverTick() {
// No recipes available and the machine wants to unsubscribe until notified
unsubscribe = true;
}
if (isIdle()) {
failureReasons.clear();
failureReasons.addAll(failureReasonMap.values());
}
if (unsubscribe && subscription != null) {
subscription.unsubscribe();
subscription = null;
Expand All @@ -310,7 +310,7 @@ public boolean checkMatchedRecipeAvailable(GTRecipe match) {
if (recipeMatch.isSuccess()) {
setupRecipe(modified);
} else {
putFailureReason(this, match, recipeMatch.reason());
recordFailureReason(match, recipeMatch.reason(), recipeMatch.score());
}
if (lastRecipe != null && getStatus() == Status.WORKING) {
lastOriginRecipe = match;
Expand Down Expand Up @@ -358,6 +358,7 @@ public void handleRecipeWorking() {

if (getMachine() instanceof MultiblockControllerMachine && !preventPowerFail) {
runAttempt = 0;
// The reason recorded by setWaiting above carries over into SUSPEND.
setStatus(Status.SUSPEND);
}
}
Expand Down Expand Up @@ -385,20 +386,26 @@ public Iterator<GTRecipe> searchRecipe() {

public void findAndHandleRecipe() {
lastFailedMatches = null;
clearFailureReason();

// try to execute last recipe if possible
if (!recipeDirty && lastRecipe != null && checkRecipe(lastRecipe).isSuccess()) {
GTRecipe recipe = lastRecipe;
lastRecipe = null;
lastOriginRecipe = null;
setupRecipe(recipe);
} else {
// try to find and handle a new recipe
failureReasonMap.clear();
lastRecipe = null;
lastOriginRecipe = null;
handleSearchingRecipes(searchRecipe());
GTRecipe last = lastRecipe;
if (!recipeDirty && last != null) {
var lastCheck = checkRecipe(last);
if (lastCheck.isSuccess()) {
lastRecipe = null;
lastOriginRecipe = null;
setupRecipe(last);
recipeDirty = false;
return;
}
recordFailureReason(last, lastCheck.reason(), Double.POSITIVE_INFINITY);
}

// try to find and handle a new recipe
lastRecipe = null;
lastOriginRecipe = null;
handleSearchingRecipes(searchRecipe());
syncDataHolder.markClientSyncFieldDirty("lastRecipe");
recipeDirty = false;
}
Expand Down Expand Up @@ -451,7 +458,7 @@ public void setupRecipe(GTRecipe recipe) {
if (lastRecipe != null && !recipe.equals(lastRecipe)) {
chanceCaches.clear();
}
failureReasonMap.clear();
clearFailureReason();
recipeDirty = false;
lastRecipe = recipe;
setStatus(Status.WORKING);
Expand All @@ -477,17 +484,16 @@ public void setStatus(Status status) {
syncDataHolder.markClientSyncFieldDirty("status");
setRenderState(getRenderState().setValue(GTMachineModelProperties.RECIPE_LOGIC_STATUS, status));
updateTickSubscription();
if (this.status != Status.WAITING) {
waitingReason = null;
syncDataHolder.markClientSyncFieldDirty("waitingReason");
if (this.status == Status.WORKING || this.status == Status.IDLE) {
clearFailureReason();
}
}
}

public void setWaiting(@Nullable Component reason) {
setStatus(Status.WAITING);
waitingReason = reason;
syncDataHolder.markClientSyncFieldDirty("waitingReason");
clearFailureReason();
recordFailureReason(lastRecipe, reason, Double.POSITIVE_INFINITY);
getRLMachine().onWaiting();
}

Expand Down Expand Up @@ -523,6 +529,7 @@ public boolean isWorkingEnabled() {
public void setWorkingEnabled(boolean isWorkingAllowed) {
if (isRemote()) return;
if (!isWorkingAllowed && getStatus() == Status.IDLE) {
clearFailureReason();
setStatus(Status.SUSPEND);
} else {
setSuspendAfterFinish(!isWorkingAllowed);
Expand Down Expand Up @@ -658,16 +665,6 @@ public void updateSound() {
}
}

public List<Component> getWaitingReasons() {
if (isWaiting() && waitingReason != null) {
return List.of(waitingReason);
}
if (isIdle() && !failureReasons.isEmpty()) {
return failureReasons;
}
return Collections.emptyList();
}

protected IdentityHashMap<RecipeCapability<?>, Object2IntMap<?>> makeChanceCaches() {
IdentityHashMap<RecipeCapability<?>, Object2IntMap<?>> map = new IdentityHashMap<>();
for (RecipeCapability<?> cap : GTRegistries.RECIPE_CAPABILITIES) {
Expand Down Expand Up @@ -735,18 +732,31 @@ public Tag serializeNBT(IdentityHashMap<RecipeCapability<?>, Object2IntMap<?>> v

public static void putFailureReason(Object machine, GTRecipe recipe, Component reason) {
if (machine instanceof IRecipeLogicMachine rlm) {
putFailureReason(rlm.getRecipeLogic(), recipe, reason);
putFailureReason(rlm.getRecipeLogic(), recipe, reason, Double.POSITIVE_INFINITY);
}
}

public static void putFailureReason(RecipeLogic logic, GTRecipe recipe, Component reason) {
var map = logic.getFailureReasonMap();
if (map.containsKey(recipe)) {
if (reason != ModifierFunction.DEFAULT_FAILURE) {
map.put(recipe, reason);
public static void putFailureReason(RecipeLogic logic, GTRecipe recipe, Component reason, double score) {
logic.recordFailureReason(recipe, reason, score);
}

/**
* Record a failure reason as the one to display, along with the recipe it belongs to.
*/
protected void recordFailureReason(@Nullable GTRecipe recipe, @Nullable Component reason, double score) {
if (reason != null && !reason.getString().isBlank()) {
if (score > bestFailureScore) {
bestFailureScore = score;
bestFailureReason = reason;
bestFailureRecipe = recipe;
}
} else {
map.put(recipe, reason);
}
}

/** Forget the currently-displayed failure reason. */
protected void clearFailureReason() {
bestFailureReason = null;
bestFailureRecipe = null;
bestFailureScore = Double.NEGATIVE_INFINITY;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,27 @@
/**
* @param isSuccess is action success
* @param reason if fail, fail reason
* @param score how close the recipe was to succeeding, in {@code [0, 1]}. {@code 1} means fully satisfied
* (success), {@code 0} means nothing matched. Used to pick the most-relevant failure reason to
* display. Failures that don't measure contents (conditions, missing capabilities) use {@code 0}.
*/
public record ActionResult(boolean isSuccess, @Nullable Component reason, @Nullable RecipeCapability<?> capability,
@Nullable IO io) {
@Nullable IO io, double score) {

public final static ActionResult SUCCESS = new ActionResult(true, null, null, null);
public final static ActionResult FAIL_NO_REASON = new ActionResult(false, null, null, null);
public final static ActionResult SUCCESS = new ActionResult(true, null, null, null, 1.0);
public final static ActionResult FAIL_NO_REASON = new ActionResult(false, null, null, null, 0.0);
public final static ActionResult PASS_NO_CONTENTS = new ActionResult(true,
Component.translatable("gtceu.recipe_logic.no_contents"), null, null);
Component.translatable("gtceu.recipe_logic.no_contents"), null, null, 1.0);
public final static ActionResult FAIL_NO_CAPABILITIES = new ActionResult(false,
Component.translatable("gtceu.recipe_logic.no_capabilities"), null, null);
Component.translatable("gtceu.recipe_logic.no_capabilities"), null, null, 0.0);

public static ActionResult fail(@Nullable Component component, @Nullable RecipeCapability<?> capability, IO io) {
return new ActionResult(false, component, capability, io);
return fail(component, capability, io, 0.0);
}

public static ActionResult fail(@Nullable Component component, @Nullable RecipeCapability<?> capability, IO io,
double score) {
return new ActionResult(false, component, capability, io, score);
}

public Component reason() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ public static ActionResult handleRecipe(IRecipeCapabilityHolder holder, GTRecipe
}
String key = "gtceu.recipe_logic.insufficient_" + (io == IO.IN ? "in" : "out");
return ActionResult.fail(Component.translatable(key)
.append(": ").append(result.capability().getName()), result.capability(), io);
.append(": ").append(result.capability().getName()), result.capability(), io, result.score());
}

public static ActionResult matchContents(IRecipeCapabilityHolder holder, GTRecipe recipe) {
Expand Down
Loading
Loading