From 8d130b7d415a088bdbd8e5e42f1749f055615afa Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sat, 22 Aug 2026 19:54:58 -0700 Subject: [PATCH 01/63] initial work --- .../gtceu/api/multiblock/MultiPredicate.java | 121 +++++++++++++----- .../multiblock/predicates/BasePredicate.java | 71 +++------- .../predicates/PredicateSettings.java | 55 ++++++++ .../predicates/TestablePredicate.java | 4 +- 4 files changed, 159 insertions(+), 92 deletions(-) create mode 100644 src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 9c989b9d977..f8757a6200e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -2,8 +2,10 @@ import com.gregtechceu.gtceu.api.multiblock.error.PatternStringError; import com.gregtechceu.gtceu.api.multiblock.predicates.BasePredicate; +import com.gregtechceu.gtceu.api.multiblock.predicates.PredicateSettings; import com.gregtechceu.gtceu.api.multiblock.util.BlockInfo; +import dev.latvian.mods.rhino.util.RemapForJS; import net.minecraft.network.chat.Component; import lombok.Getter; @@ -15,6 +17,7 @@ import java.util.*; import java.util.function.Consumer; +import java.util.function.UnaryOperator; public abstract class MultiPredicate { @@ -34,6 +37,15 @@ public abstract class MultiPredicate { @Accessors(chain = true) private boolean controller; + // there needs to be a way to know whether to adhere to this settings or predicates/childrens settings + // like if this == null, check children, else only check this + @Nullable + @Getter + private PredicateSettings settings = null; + + @Getter + private final List additionalTooltips = new ArrayList<>(); + @Nullable @Getter @Setter @@ -167,6 +179,16 @@ public boolean hasAir() { return this.hasAir; } + private MultiPredicate deepCopy() { + MultiPredicate copy = this.type.makePredicate( + children().stream().map(MultiPredicate::deepCopy).toList(), + predicates().stream().map(BasePredicate::copy).toList(), + this.hasAir); + copy.additionalTooltips.addAll(this.additionalTooltips); + copy.setController(this.controller); + return copy; + } + @CheckReturnValue private MultiPredicate mutatedCopy(Consumer mutation) { List copiedPredicates = new ArrayList<>(this.predicates.size()); @@ -184,14 +206,73 @@ private MultiPredicate mutatedCopy(Consumer mutation) { return copy; } + @RemapForJS("addTooltip") @CheckReturnValue public MultiPredicate addTooltips(Component tooltip) { - return mutatedCopy(p -> p.addTooltips(tooltip)); + MultiPredicate copy = deepCopy(); + copy.additionalTooltips.add(tooltip); + return copy; + } + + @CheckReturnValue + public MultiPredicate addTooltips(Component... tooltip) { + MultiPredicate copy = deepCopy(); + Collections.addAll(copy.additionalTooltips, tooltip); + return copy; + } + + private void updateSettings(UnaryOperator configurator) { + PredicateSettings copy = Objects.requireNonNullElseGet(this.settings, PredicateSettings::create); + this.settings = configurator.apply(copy); } @CheckReturnValue public MultiPredicate setPriority(int priority) { - return mutatedCopy(p -> p.setPriority(priority)); + MultiPredicate copy = deepCopy(); + copy.updateSettings(s -> s.withPriority(priority)); + return copy; + } + + @CheckReturnValue + public MultiPredicate setMinCount(int min) { + MultiPredicate copy = deepCopy(); + copy.updateSettings(s -> s.withMinCount(min)); + return copy; + } + + @CheckReturnValue + public MultiPredicate setMaxCount(int max) { + MultiPredicate copy = deepCopy(); + copy.updateSettings(s -> s.withMaxCount(max)); + return copy; + } + + @CheckReturnValue + public MultiPredicate setMinSliceCount(int min) { + MultiPredicate copy = deepCopy(); + copy.updateSettings(s -> s.withMinSliceCount(min)); + return copy; + } + + @CheckReturnValue + public MultiPredicate setMaxSliceCount(int max) { + MultiPredicate copy = deepCopy(); + copy.updateSettings(s -> s.withMaxSliceCount(max)); + return copy; + } + + @CheckReturnValue + public MultiPredicate setPreviewCount(int previewCount) { + MultiPredicate copy = deepCopy(); + copy.updateSettings(s -> s.withPreviewCount(previewCount)); + return copy; + } + + @CheckReturnValue + public MultiPredicate setDisableRenderFormed(boolean disable) { + MultiPredicate copy = deepCopy(); + copy.updateSettings(s -> s.withDisableRenderFormed(disable)); + return copy; } @CheckReturnValue @@ -204,11 +285,6 @@ public MultiPredicate setMinGlobalLimited(int min, int previewCount) { return this.setMinCount(min).setPreviewCount(previewCount); } - @CheckReturnValue - public MultiPredicate setMinCount(int min) { - return mutatedCopy(p -> p.setMinCount(min)); - } - @CheckReturnValue public MultiPredicate setMaxGlobalLimited(int max) { return this.setMaxCount(max); @@ -220,12 +296,7 @@ public MultiPredicate setMaxGlobalLimited(int max, int previewCount) { } @CheckReturnValue - public MultiPredicate setMaxCount(int max) { - return mutatedCopy(p -> p.setMaxCount(max)); - } - - @CheckReturnValue - public MultiPredicate setGlobalMinMax(int min, int max) { + public MultiPredicate ranged(int min, int max) { return this.setMinCount(min).setMaxCount(max); } @@ -239,11 +310,6 @@ public MultiPredicate setMinLayerLimited(int min, int previewCount) { return this.setMinSliceCount(min).setPreviewCount(previewCount); } - @CheckReturnValue - public MultiPredicate setMinSliceCount(int min) { - return mutatedCopy(p -> p.setMinSliceCount(min)); - } - @CheckReturnValue public MultiPredicate setMaxLayerLimited(int max) { return this.setMaxSliceCount(max); @@ -255,17 +321,7 @@ public MultiPredicate setMaxLayerLimited(int max, int previewCount) { } @CheckReturnValue - public MultiPredicate setMaxSliceCount(int max) { - return mutatedCopy(p -> p.setMaxSliceCount(max)); - } - - @CheckReturnValue - public MultiPredicate setPreviewCount(int previewCount) { - return mutatedCopy(p -> p.setPreviewCount(previewCount)); - } - - @CheckReturnValue - public MultiPredicate setLayerMinMax(int min, int max) { + public MultiPredicate sliceRanged(int min, int max) { return this.setMinSliceCount(min).setMaxSliceCount(max); } @@ -276,7 +332,7 @@ public MultiPredicate setLayerMinMax(int min, int max) { */ @CheckReturnValue public MultiPredicate setExactLimit(int limit) { - return this.setGlobalMinMax(limit, limit); + return this.ranged(limit, limit); } @CheckReturnValue @@ -284,11 +340,6 @@ public MultiPredicate disabledRenderFormed() { return setDisableRenderFormed(true); } - @CheckReturnValue - public MultiPredicate setDisableRenderFormed(boolean disable) { - return mutatedCopy(p -> p.setDisableRenderFormed(disable)); - } - /// @return a new multi predicate where any predicate may pass or be present in the multiblock public MultiPredicate or(@Nullable MultiPredicate other) { return combine(this, Logic.OR, other); diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index 3bc17c0bc5d..937754d80f1 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -28,29 +28,8 @@ public abstract class BasePredicate implements Comparable { private final List candidates = computeCandidates(); @Getter - @Setter - protected int priority = 0; - @Getter - @Setter - protected int minCount = -1; - @Getter - @Setter - protected int maxCount = -1; - @Getter - @Setter - protected int minSliceCount = -1; - @Getter - @Setter - protected int maxSliceCount = -1; - @Getter - @Setter - protected int previewCount = -1; - @Getter - @Setter - protected boolean disableRenderFormed = false; - @Getter - @Setter - private @Nullable String nbtParser; // unsure what this does + protected PredicateSettings settings = PredicateSettings.create(); + @Setter private @Nullable MultiPredicate parent; @@ -64,6 +43,9 @@ public MultiPredicate getParent() { /// the main testing method public abstract boolean test(PredicateContext ctx); + // this is called after calling getPredicateAtPos() + public abstract List getRecipeViewerTooltips(MultiPredicate root); + public abstract void onError(PredicateContext ctx); /// @param root the top-most multi predicate for this multi predicate @@ -73,14 +55,7 @@ public MultiPredicate getParent() { public abstract BasePredicate copy(); protected void copyTo(BasePredicate other) { - other.priority = this.priority; - other.minCount = this.minCount; - other.maxCount = this.maxCount; - other.minSliceCount = this.minSliceCount; - other.maxSliceCount = this.maxSliceCount; - other.previewCount = this.previewCount; - other.disableRenderFormed = this.disableRenderFormed; - other.nbtParser = this.nbtParser; + other.settings = this.settings.copy(); other.additionalTooltips.addAll(this.additionalTooltips); } @@ -97,7 +72,7 @@ public boolean checkMaxCount(PredicateContext context) { /// test against global max count public boolean testGlobalMax(PredicateContext ctx) { int count = ctx.incrementGlobalCount(this); - if (testGlobalMax(count)) return true; + if (getSettings().testGlobalMax(count)) return true; ctx.appendError(SinglePredicateError.maxCount(this, count)); return false; } @@ -106,7 +81,7 @@ public boolean testGlobalMax(PredicateContext ctx) { public boolean testSliceMax(PredicateContext ctx) { if (!ctx.isCheckLayer()) return true; int count = ctx.incrementSliceCount(this); - if (testSliceMax(count)) return true; + if (getSettings().testSliceMax(count)) return true; ctx.appendError(SinglePredicateError.maxLayerCount(this, count)); return false; } @@ -115,40 +90,24 @@ public boolean testSliceMax(PredicateContext ctx) { public boolean testGlobalMin(PredicateContext ctx) { if (getMinCount() == -1) return true; int count = ctx.getGlobalCount(this); - if (testGlobalMin(count)) return true; + if (getSettings().testGlobalMin(count)) return true; ctx.appendError(SinglePredicateError.minCount(this, count)); return false; } + private int getMinCount() { + return getSettings().minCount(); + } + /// test against slice min count public boolean testSliceMin(PredicateContext ctx) { if (!ctx.isCheckLayer()) return true; int count = ctx.getSliceCount(this); - if (testSliceMin(count)) return true; + if (getSettings().testSliceMin(count)) return true; ctx.appendError(SinglePredicateError.minLayerCount(this, count)); return false; } - /// simple test against global min count - public boolean testGlobalMin(int count) { - return minCount == -1 || count >= minCount; - } - - /// simple test against slice min count - public boolean testSliceMin(int count) { - return minSliceCount == -1 || count >= minSliceCount; - } - - /// simple test against global max count - public boolean testGlobalMax(int count) { - return maxCount == -1 || count <= maxCount; - } - - /// simple test against slice max count - public boolean testSliceMax(int count) { - return maxSliceCount == -1 || count <= maxSliceCount; - } - /// computes the candidates for this predicate public abstract List computeCandidates(); @@ -182,6 +141,6 @@ public String toString() { @Override public int compareTo(BasePredicate o) { - return Integer.compare(this.priority, o.priority); + return this.settings.comparePriority(o.settings); } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java new file mode 100644 index 00000000000..45a68580b4e --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java @@ -0,0 +1,55 @@ +package com.gregtechceu.gtceu.api.multiblock.predicates; + +import lombok.With; + +@With +public record PredicateSettings(int priority, + int minCount, int maxCount, + int minSliceCount, int maxSliceCount, + int previewCount, boolean disableRenderFormed) { + + public PredicateSettings copy() { + return new PredicateSettings( + this.priority, + this.minCount, + this.maxCount, + this.minSliceCount, + this.maxSliceCount, + this.previewCount, + this.disableRenderFormed + ); + } + + public int comparePriority(PredicateSettings other) { + return Integer.compare(this.priority, other.priority); + } + + /// simple test against global min count + public boolean testGlobalMin(int count) { + return minCount == -1 || count >= minCount; + } + + /// simple test against slice min count + public boolean testSliceMin(int count) { + return minSliceCount == -1 || count >= minSliceCount; + } + + /// simple test against global max count + public boolean testGlobalMax(int count) { + return maxCount == -1 || count <= maxCount; + } + + /// simple test against slice max count + public boolean testSliceMax(int count) { + return maxSliceCount == -1 || count <= maxSliceCount; + } + + public static PredicateSettings create() { + return new PredicateSettings( + 0, + -1, -1, + -1, -1, + -1, false + ); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java index d3ab1de5f3d..17ac04700b3 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java @@ -57,7 +57,9 @@ class TestablePredicate extends BasePredicate { @Override public List getRecipeViewerTooltips(MultiPredicate root) { - List tooltips = new ArrayList<>(this.getAdditionalTooltips()); + List tooltips = new ArrayList<>(root.getAdditionalTooltips()); + int minCount = getSettings().minCount(); + int maxCount = getSettings().maxCount(); if (minCount == maxCount && maxCount != -1) { tooltips.add(Component.translatable("gtceu.multiblock.pattern.exact_count", minCount)); } else if (minCount != maxCount && minCount != -1 && maxCount != -1) { From 4c11a47c9132cc3ffdb4020255fdfa854e7e9e66 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 23 Aug 2026 13:11:39 -0700 Subject: [PATCH 02/63] work 2 PredicateSettings codec (unused) update xorpred nonvalid global/slice count key to PredicateSettings --- .../gtceu/api/multiblock/MultiPredicate.java | 81 ++++++++++++------- .../api/multiblock/PredicateContext.java | 14 ++-- .../gtceu/api/multiblock/XorPredicate.java | 9 ++- .../error/SinglePredicateError.java | 8 +- .../pattern/MultiblockPatternBuilder.java | 10 ++- .../multiblock/predicates/BasePredicate.java | 8 +- .../predicates/PredicateSettings.java | 42 +++++++++- .../util/AbstractStructureHelper.java | 4 +- .../multiblock/util/BlockPatternHelper.java | 7 +- 9 files changed, 125 insertions(+), 58 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index f8757a6200e..2cf2982bf46 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -5,9 +5,9 @@ import com.gregtechceu.gtceu.api.multiblock.predicates.PredicateSettings; import com.gregtechceu.gtceu.api.multiblock.util.BlockInfo; -import dev.latvian.mods.rhino.util.RemapForJS; import net.minecraft.network.chat.Component; +import dev.latvian.mods.rhino.util.RemapForJS; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; @@ -18,6 +18,7 @@ import java.util.*; import java.util.function.Consumer; import java.util.function.UnaryOperator; +import java.util.stream.Stream; public abstract class MultiPredicate { @@ -100,7 +101,11 @@ protected void onError(PredicateContext ctx) { /// Usually used for testing the global min of predicates public final boolean postGlobalTest(PredicateContext ctx) { ctx.setStage(PredicateContext.PredicateStage.GLOBAL_MIN); - if (testGlobalMin(ctx)) return true; + boolean passed = testGlobalMin(ctx); + if (this.settings != null) { + passed &= this.settings.testGlobalMin(ctx); + } + if (passed) return true; for (Component content : getDescriptiveContents()) { ctx.appendError(PatternStringError.of(content)); } @@ -113,7 +118,11 @@ public final boolean postGlobalTest(PredicateContext ctx) { /// Usually used for testing the slice min of predicates public final boolean postSliceTest(PredicateContext ctx) { ctx.setStage(PredicateContext.PredicateStage.SLICE_MIN); - if (testSliceMin(ctx)) return true; + boolean passed = testSliceMin(ctx); + if (this.settings != null) { + passed &= this.settings.testSliceMin(ctx); + } + if (passed) return true; for (Component content : getDescriptiveContents()) { ctx.appendError(PatternStringError.of(content)); } @@ -125,10 +134,18 @@ public final boolean postSliceTest(PredicateContext ctx) { /// test against global/slice max counts public boolean testMaxCount(BasePredicate passedPredicate, PredicateContext context) { context.setStage(PredicateContext.PredicateStage.GLOBAL_MAX); - if (!passedPredicate.testGlobalMax(context)) - return false; - context.setStage(PredicateContext.PredicateStage.SLICE_MAX); - return passedPredicate.testSliceMax(context); + boolean passed = passedPredicate.testGlobalMax(context); + if (this.settings != null) { + passed &= this.settings.testGlobalMax(context); + } + if (passed) { + context.setStage(PredicateContext.PredicateStage.SLICE_MAX); + passed = passedPredicate.testSliceMax(context); + if (this.settings != null) { + passed &= this.settings.testSliceMax(context); + } + } + return passed; } public List> getCandidates() { @@ -142,6 +159,13 @@ public List> getCandidates() { return Collections.unmodifiableList(result); } + /// @return a flat list of candidates for filling out the JEI preview + public List getFlatCandidates() { + return getCandidates().stream() + .flatMap(Collection::stream) + .toList(); + } + public void resetLogic() { this.children.forEach(MultiPredicate::resetLogic); } @@ -189,23 +213,6 @@ private MultiPredicate deepCopy() { return copy; } - @CheckReturnValue - private MultiPredicate mutatedCopy(Consumer mutation) { - List copiedPredicates = new ArrayList<>(this.predicates.size()); - for (BasePredicate predicate : this.predicates) { - BasePredicate copy = predicate.copy(); - mutation.accept(copy); - copiedPredicates.add(copy); - } - List copiedChildren = new ArrayList<>(this.children.size()); - for (MultiPredicate child : this.children) { - copiedChildren.add(child.mutatedCopy(mutation)); - } - MultiPredicate copy = this.type.makePredicate(copiedChildren, copiedPredicates, this.hasAir); - copy.setController(this.controller); - return copy; - } - @RemapForJS("addTooltip") @CheckReturnValue public MultiPredicate addTooltips(Component tooltip) { @@ -221,9 +228,27 @@ public MultiPredicate addTooltips(Component... tooltip) { return copy; } + // spotless:off + protected List gatherSettings() { + if (this.settings != null) { + return List.of(this.settings); + } + return Stream.concat( + predicates().stream().map(BasePredicate::getSettings), + children().stream().flatMap(mp -> mp.gatherSettings().stream()) + ).toList(); + } + // spotless:on + + protected PredicateSettings getOrCreateSettings() { + if (this.settings == null) { + this.settings = PredicateSettings.create(); + } + return this.settings; + } + private void updateSettings(UnaryOperator configurator) { - PredicateSettings copy = Objects.requireNonNullElseGet(this.settings, PredicateSettings::create); - this.settings = configurator.apply(copy); + this.settings = configurator.apply(getOrCreateSettings()); } @CheckReturnValue @@ -372,9 +397,9 @@ public static MultiPredicate xor(List predicates) { return of(Logic.XOR, predicates); } - /// @return {@code true} if this multi predicate has only one predicate and has no children + /// @return {@code true} if this multi predicate has only one predicate, has no children, and has no parent public boolean isSingle() { - return predicates.size() == 1 && isLeaf(); + return predicates.size() == 1 && isLeaf() && isRoot(); } /// @return {@code true} if this multi predicate has no parent diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java index f88dada6a89..40257668f46 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java @@ -3,7 +3,7 @@ import com.gregtechceu.gtceu.api.multiblock.error.PatternError; import com.gregtechceu.gtceu.api.multiblock.pattern.CurrentBlockInfo; import com.gregtechceu.gtceu.api.multiblock.pattern.PatternState; -import com.gregtechceu.gtceu.api.multiblock.predicates.BasePredicate; +import com.gregtechceu.gtceu.api.multiblock.predicates.PredicateSettings; import com.gregtechceu.gtceu.api.multiblock.util.BlockInfo; import net.minecraft.core.BlockPos; @@ -29,8 +29,8 @@ public class PredicateContext { private final PatternState state; @Getter protected CurrentBlockInfo currentBlockInfo = new CurrentBlockInfo(); - protected final Object2IntMap globalCount = new Object2IntOpenHashMap<>(); - protected final Object2IntMap layerCount = new Object2IntOpenHashMap<>(); + protected final Object2IntMap globalCount = new Object2IntOpenHashMap<>(); + protected final Object2IntMap layerCount = new Object2IntOpenHashMap<>(); private final Int2ObjectMap> sliceErrors = new Int2ObjectAVLTreeMap<>( IntComparators.NATURAL_COMPARATOR); @@ -131,20 +131,20 @@ public void clearLayerCounts() { this.layerCount.clear(); } - public int incrementGlobalCount(BasePredicate predicate) { + public int incrementGlobalCount(PredicateSettings predicate) { return this.globalCount.mergeInt(predicate, 1, Integer::sum); } - public int incrementSliceCount(BasePredicate predicate) { + public int incrementSliceCount(PredicateSettings predicate) { if (!checkLayer) return 0; return this.layerCount.mergeInt(predicate, 1, Integer::sum); } - public int getGlobalCount(BasePredicate predicate) { + public int getGlobalCount(PredicateSettings predicate) { return this.globalCount.getInt(predicate); } - public int getSliceCount(BasePredicate predicate) { + public int getSliceCount(PredicateSettings predicate) { if (!checkLayer) return 0; return this.layerCount.getInt(predicate); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java index f98b5b68cb1..7e05abdf849 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java @@ -2,6 +2,7 @@ import com.gregtechceu.gtceu.api.multiblock.error.PatternStringError; import com.gregtechceu.gtceu.api.multiblock.predicates.BasePredicate; +import com.gregtechceu.gtceu.api.multiblock.predicates.PredicateSettings; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; @@ -21,8 +22,12 @@ public class XorPredicate extends MultiPredicate { public XorPredicate(List children, List predicates, boolean hasAir) { super(Logic.XOR, children, predicates, hasAir); - this.noneValid = expand().stream() - .anyMatch(p -> p.getMinCount() <= 0 && p.getMinSliceCount() <= 0); + for (PredicateSettings setting : gatherSettings()) { + if (setting.minCount() <= 0 && setting.minSliceCount() <= 0) { + this.noneValid = true; + break; + } + } } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/error/SinglePredicateError.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/error/SinglePredicateError.java index 01bf9f8e558..820141b9b73 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/error/SinglePredicateError.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/error/SinglePredicateError.java @@ -45,10 +45,10 @@ public class SinglePredicateError extends PatternError { public SinglePredicateError(BasePredicate failingPredicate, ErrorType type, int actualCount) { this(type, actualCount, - failingPredicate.getMinCount(), - failingPredicate.getMaxCount(), - failingPredicate.getMinSliceCount(), - failingPredicate.getMaxSliceCount(), + failingPredicate.getSettings().minCount(), + failingPredicate.getSettings().maxCount(), + failingPredicate.getSettings().minSliceCount(), + failingPredicate.getSettings().maxSliceCount(), failingPredicate.toString(), failingPredicate.getCandidates()); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/MultiblockPatternBuilder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/MultiblockPatternBuilder.java index ee6afd34d6d..7d4d4fc4ff3 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/MultiblockPatternBuilder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/MultiblockPatternBuilder.java @@ -3,6 +3,7 @@ import com.gregtechceu.gtceu.api.multiblock.MultiPredicate; import com.gregtechceu.gtceu.api.multiblock.OriginOffset; import com.gregtechceu.gtceu.api.multiblock.Predicates; +import com.gregtechceu.gtceu.api.multiblock.predicates.PredicateSettings; import com.gregtechceu.gtceu.api.multiblock.util.RelativeDirection; import com.google.common.base.Joiner; @@ -177,15 +178,16 @@ private void checkGlobalConstraints() { if (predicate == null) throw new IllegalArgumentException("Predicate for symbol " + symbol + " was null."); int maxCount = -1; for (var basePredicate : predicate.expand()) { - if (basePredicate.getMaxCount() == -1) { + PredicateSettings settings = basePredicate.getSettings(); + if (settings.maxCount() == -1) { maxCount = -1; break; } - if (basePredicate.getMinCount() == basePredicate.getMaxCount()) { + if (settings.minCount() == settings.maxCount()) { if (maxCount == -1) { - maxCount = basePredicate.getMinCount(); + maxCount = settings.minCount(); } else { - maxCount += basePredicate.getMinCount(); + maxCount += settings.minCount(); } } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index 937754d80f1..37edc8557c4 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -71,7 +71,7 @@ public boolean checkMaxCount(PredicateContext context) { /// test against global max count public boolean testGlobalMax(PredicateContext ctx) { - int count = ctx.incrementGlobalCount(this); + int count = ctx.incrementGlobalCount(getSettings()); if (getSettings().testGlobalMax(count)) return true; ctx.appendError(SinglePredicateError.maxCount(this, count)); return false; @@ -80,7 +80,7 @@ public boolean testGlobalMax(PredicateContext ctx) { /// test against slice max count public boolean testSliceMax(PredicateContext ctx) { if (!ctx.isCheckLayer()) return true; - int count = ctx.incrementSliceCount(this); + int count = ctx.incrementSliceCount(getSettings()); if (getSettings().testSliceMax(count)) return true; ctx.appendError(SinglePredicateError.maxLayerCount(this, count)); return false; @@ -89,7 +89,7 @@ public boolean testSliceMax(PredicateContext ctx) { /// test against global min count public boolean testGlobalMin(PredicateContext ctx) { if (getMinCount() == -1) return true; - int count = ctx.getGlobalCount(this); + int count = ctx.getGlobalCount(getSettings()); if (getSettings().testGlobalMin(count)) return true; ctx.appendError(SinglePredicateError.minCount(this, count)); return false; @@ -102,7 +102,7 @@ private int getMinCount() { /// test against slice min count public boolean testSliceMin(PredicateContext ctx) { if (!ctx.isCheckLayer()) return true; - int count = ctx.getSliceCount(this); + int count = ctx.getSliceCount(getSettings()); if (getSettings().testSliceMin(count)) return true; ctx.appendError(SinglePredicateError.minLayerCount(this, count)); return false; diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java index 45a68580b4e..9c28e2d5227 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java @@ -1,5 +1,9 @@ package com.gregtechceu.gtceu.api.multiblock.predicates; +import com.gregtechceu.gtceu.api.multiblock.PredicateContext; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import lombok.With; @With @@ -8,6 +12,18 @@ public record PredicateSettings(int priority, int minSliceCount, int maxSliceCount, int previewCount, boolean disableRenderFormed) { + // spotless:off + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.INT.fieldOf("priority").forGetter(PredicateSettings::priority), + Codec.INT.fieldOf("min_count").forGetter(PredicateSettings::minCount), + Codec.INT.fieldOf("max_count").forGetter(PredicateSettings::maxCount), + Codec.INT.fieldOf("min_layer_count").forGetter(PredicateSettings::minSliceCount), + Codec.INT.fieldOf("max_layer_count").forGetter(PredicateSettings::maxSliceCount), + Codec.INT.fieldOf("preview_count").forGetter(PredicateSettings::previewCount), + Codec.BOOL.fieldOf("disable_render_formed").forGetter(PredicateSettings::disableRenderFormed) + ).apply(instance, PredicateSettings::new)); + // spotless:on + public PredicateSettings copy() { return new PredicateSettings( this.priority, @@ -16,8 +32,7 @@ public PredicateSettings copy() { this.minSliceCount, this.maxSliceCount, this.previewCount, - this.disableRenderFormed - ); + this.disableRenderFormed); } public int comparePriority(PredicateSettings other) { @@ -44,12 +59,31 @@ public boolean testSliceMax(int count) { return maxSliceCount == -1 || count <= maxSliceCount; } + /// simple test against global min count + public boolean testGlobalMin(PredicateContext ctx) { + return testGlobalMin(ctx.getGlobalCount(this)); + } + + /// simple test against slice min count + public boolean testSliceMin(PredicateContext ctx) { + return testSliceMin(ctx.getSliceCount(this)); + } + + /// simple test against global max count, increments global count + public boolean testGlobalMax(PredicateContext ctx) { + return testGlobalMax(ctx.incrementGlobalCount(this)); + } + + /// simple test against slice max count, increments slice count + public boolean testSliceMax(PredicateContext ctx) { + return testSliceMax(ctx.incrementSliceCount(this)); + } + public static PredicateSettings create() { return new PredicateSettings( 0, -1, -1, -1, -1, - -1, false - ); + -1, false); } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/AbstractStructureHelper.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/AbstractStructureHelper.java index 2207ae75b9f..8f2433aae3c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/AbstractStructureHelper.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/AbstractStructureHelper.java @@ -75,13 +75,13 @@ public abstract MultiPredicate getPredicateFromPos(IBlockPattern pattern, BlockP protected int getMinCount(MultiPredicate predicate, BasePredicate basePredicate) { if (!minMaxPreferences.contains(predicate, basePredicate)) - return basePredicate.getMinCount(); + return basePredicate.getSettings().minCount(); return minMaxPreferences.get(predicate, basePredicate).leftInt(); } protected int getMaxCount(MultiPredicate predicate, BasePredicate basePredicate) { if (!minMaxPreferences.contains(predicate, basePredicate)) - return basePredicate.getMaxCount(); + return basePredicate.getSettings().maxCount(); return minMaxPreferences.get(predicate, basePredicate).rightInt(); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/BlockPatternHelper.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/BlockPatternHelper.java index 60ad5e39036..4ff87b9aa69 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/BlockPatternHelper.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/BlockPatternHelper.java @@ -138,7 +138,7 @@ private boolean tryMinCount(Map resultStructure, MultiPredi int totalAlreadyPopulated = countPopulatedGlobal(resultStructure, basePredicate); int layerAlreadyPopulated = countPopulatedInLayer(resultStructure, basePredicate, dir, offset); boolean globalMinMet = minCount == -1 || totalAlreadyPopulated >= minCount; - boolean sliceMinMet = basePredicate.testSliceMin(layerAlreadyPopulated); + boolean sliceMinMet = basePredicate.getSettings().testSliceMin(layerAlreadyPopulated); if (globalMinMet && sliceMinMet) continue; @@ -173,7 +173,7 @@ private boolean tryMaxCount(Map resultStructure, MultiPredi int totalAlreadyPopulated = countPopulatedGlobal(resultStructure, basePredicate); int layerAlreadyPopulated = countPopulatedInLayer(resultStructure, basePredicate, dir, offset); if (maxCount != -1 && totalAlreadyPopulated >= maxCount) continue; - if (!basePredicate.testSliceMax(layerAlreadyPopulated + 1)) continue; + if (!basePredicate.getSettings().testSliceMax(layerAlreadyPopulated + 1)) continue; BlockInfo toInsert = blockPreferences.get(predicate, basePredicate); if (toInsert == null) { @@ -222,7 +222,8 @@ private boolean isValidCandidate(Map resultStructure, Multi int layerAlreadyPopulated = countPopulatedInLayer(resultStructure, basePredicate, sliceDir, sliceCoord); if (maxCount != -1 && totalAlreadyPopulated >= maxCount) continue; - if (basePredicate.getMaxSliceCount() == -1 || layerAlreadyPopulated < basePredicate.getMaxSliceCount()) { + if (basePredicate.getSettings().maxSliceCount() == -1 || + layerAlreadyPopulated < basePredicate.getSettings().maxSliceCount()) { return true; } } From 452854756f74c6afa473dd22be3ec71673248e73 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 23 Aug 2026 18:58:32 -0700 Subject: [PATCH 03/63] lang --- .../gtceu/api/multiblock/predicates/TestablePredicate.java | 2 ++ .../java/com/gregtechceu/gtceu/data/lang/LangHandler.java | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java index 17ac04700b3..2546dce6f20 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java @@ -55,6 +55,8 @@ class TestablePredicate extends BasePredicate { this.onError = onError; } + /// @param root the top-most multi predicate for this multi predicate + /// @return a list of components to be displayed while hovering over a block in the Multiblock Preview @Override public List getRecipeViewerTooltips(MultiPredicate root) { List tooltips = new ArrayList<>(root.getAdditionalTooltips()); diff --git a/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java b/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java index 8af6da707c8..1fcf4d44658 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java @@ -1235,6 +1235,10 @@ public static void init(RegistrateLangProvider provider) { provider.add("gtceu.multiblock.pattern.min_count", "§cAt least: %d§r"); provider.add("gtceu.multiblock.pattern.max_count", "§cAt most: %d§r"); multilineLang(provider, "gtceu.multiblock.pattern.error", "Expected components at:\n %s, %s, %s."); + provider.add("gtceu.multiblock.pattern.exact_count", "§cExactly: %d§r"); + provider.add("gtceu.multiblock.pattern.between_count", "§cBetween %d and %d§r"); + provider.add("gtceu.multiblock.pattern.min_count", "§cAt least: %d§r"); + provider.add("gtceu.multiblock.pattern.max_count", "§cAt most: %d§r"); provider.add("gtceu.multiblock.pattern.error.limited.exact", "§cExactly: %d§r"); provider.add("gtceu.multiblock.pattern.error.limited.range", "§cBetween %d and %d§r"); provider.add("gtceu.multiblock.pattern.error.limited.max_count", "§cMaximum:§r %d has %d"); From 8e70819fa78e44391ee7164fa37018245ed49ef4 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 23 Aug 2026 19:24:03 -0700 Subject: [PATCH 04/63] reduce changes --- .../gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 2cf2982bf46..4c96b86837f 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -159,13 +159,6 @@ public List> getCandidates() { return Collections.unmodifiableList(result); } - /// @return a flat list of candidates for filling out the JEI preview - public List getFlatCandidates() { - return getCandidates().stream() - .flatMap(Collection::stream) - .toList(); - } - public void resetLogic() { this.children.forEach(MultiPredicate::resetLogic); } @@ -449,6 +442,7 @@ public String toString() { builder.append('{'); StringJoiner joiner = new StringJoiner(", "); this.forEach(p -> joiner.add(p.toString())); + this.forEachChild(mp -> joiner.add(mp.toString())); builder.append(joiner); builder.append('}'); return builder.toString(); From cb7db47af329d206d9191961f217ca6d61e13cf3 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 23 Aug 2026 19:48:14 -0700 Subject: [PATCH 05/63] fix none valid in xor add set settings copy settings --- .../gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 9 ++++++++- .../gregtechceu/gtceu/api/multiblock/XorPredicate.java | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 4c96b86837f..c20059873e1 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -201,6 +201,7 @@ private MultiPredicate deepCopy() { children().stream().map(MultiPredicate::deepCopy).toList(), predicates().stream().map(BasePredicate::copy).toList(), this.hasAir); + copy.setSettings(this.settings); copy.additionalTooltips.addAll(this.additionalTooltips); copy.setController(this.controller); return copy; @@ -241,7 +242,13 @@ protected PredicateSettings getOrCreateSettings() { } private void updateSettings(UnaryOperator configurator) { - this.settings = configurator.apply(getOrCreateSettings()); + setSettings(Objects.requireNonNull(configurator.apply(getOrCreateSettings()))); + } + + protected void setSettings(@Nullable PredicateSettings settings) { + if (settings != null) { + this.settings = settings.copy(); + } } @CheckReturnValue diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java index 7e05abdf849..b18b617f8c7 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java @@ -22,12 +22,19 @@ public class XorPredicate extends MultiPredicate { public XorPredicate(List children, List predicates, boolean hasAir) { super(Logic.XOR, children, predicates, hasAir); + } + + @Override + protected void setSettings(@Nullable PredicateSettings settings) { + super.setSettings(settings); + boolean noneValid = false; for (PredicateSettings setting : gatherSettings()) { if (setting.minCount() <= 0 && setting.minSliceCount() <= 0) { - this.noneValid = true; + noneValid = true; break; } } + this.noneValid = noneValid; } @Override From 88a1fa2610b73a6aadeabc4cf15503735039f5db Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 23 Aug 2026 19:53:44 -0700 Subject: [PATCH 06/63] check return value --- .../com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index c20059873e1..47d12172510 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -196,6 +196,7 @@ public boolean hasAir() { return this.hasAir; } + @CheckReturnValue private MultiPredicate deepCopy() { MultiPredicate copy = this.type.makePredicate( children().stream().map(MultiPredicate::deepCopy).toList(), From e07d8bab4ebd8e1ee953394bddd3ecc3aaf1f188 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 23 Aug 2026 20:27:44 -0700 Subject: [PATCH 07/63] simplify methods --- .../gtceu/api/multiblock/MultiPredicate.java | 52 ++++++++----------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 47d12172510..72d58df7bce 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -31,15 +31,15 @@ public abstract class MultiPredicate { private final List predicates; private final List children; private final boolean hasAir; + @Getter private final Logic type; + @Getter @Setter @Accessors(chain = true) private boolean controller; - // there needs to be a way to know whether to adhere to this settings or predicates/childrens settings - // like if this == null, check children, else only check this @Nullable @Getter private PredicateSettings settings = null; @@ -208,19 +208,22 @@ private MultiPredicate deepCopy() { return copy; } - @RemapForJS("addTooltip") @CheckReturnValue - public MultiPredicate addTooltips(Component tooltip) { + public MultiPredicate copyWith(Consumer configurator) { MultiPredicate copy = deepCopy(); - copy.additionalTooltips.add(tooltip); + configurator.accept(copy); return copy; } + @RemapForJS("addTooltip") + @CheckReturnValue + public MultiPredicate addTooltips(Component tooltip) { + return copyWith(p -> p.additionalTooltips.add(tooltip)); + } + @CheckReturnValue public MultiPredicate addTooltips(Component... tooltip) { - MultiPredicate copy = deepCopy(); - Collections.addAll(copy.additionalTooltips, tooltip); - return copy; + return copyWith(p -> Collections.addAll(p.additionalTooltips, tooltip)); } // spotless:off @@ -242,6 +245,11 @@ protected PredicateSettings getOrCreateSettings() { return this.settings; } + @CheckReturnValue + public MultiPredicate copyWithSettings(UnaryOperator configurator) { + return copyWith(p -> p.updateSettings(configurator)); + } + private void updateSettings(UnaryOperator configurator) { setSettings(Objects.requireNonNull(configurator.apply(getOrCreateSettings()))); } @@ -254,51 +262,37 @@ protected void setSettings(@Nullable PredicateSettings settings) { @CheckReturnValue public MultiPredicate setPriority(int priority) { - MultiPredicate copy = deepCopy(); - copy.updateSettings(s -> s.withPriority(priority)); - return copy; + return copyWithSettings(s -> s.withPriority(priority)); } @CheckReturnValue public MultiPredicate setMinCount(int min) { - MultiPredicate copy = deepCopy(); - copy.updateSettings(s -> s.withMinCount(min)); - return copy; + return copyWithSettings(s -> s.withMinCount(min)); } @CheckReturnValue public MultiPredicate setMaxCount(int max) { - MultiPredicate copy = deepCopy(); - copy.updateSettings(s -> s.withMaxCount(max)); - return copy; + return copyWithSettings(s -> s.withMaxCount(max)); } @CheckReturnValue public MultiPredicate setMinSliceCount(int min) { - MultiPredicate copy = deepCopy(); - copy.updateSettings(s -> s.withMinSliceCount(min)); - return copy; + return copyWithSettings(s -> s.withMinSliceCount(min)); } @CheckReturnValue public MultiPredicate setMaxSliceCount(int max) { - MultiPredicate copy = deepCopy(); - copy.updateSettings(s -> s.withMaxSliceCount(max)); - return copy; + return copyWithSettings(s -> s.withMaxSliceCount(max)); } @CheckReturnValue public MultiPredicate setPreviewCount(int previewCount) { - MultiPredicate copy = deepCopy(); - copy.updateSettings(s -> s.withPreviewCount(previewCount)); - return copy; + return copyWithSettings(s -> s.withPreviewCount(previewCount)); } @CheckReturnValue public MultiPredicate setDisableRenderFormed(boolean disable) { - MultiPredicate copy = deepCopy(); - copy.updateSettings(s -> s.withDisableRenderFormed(disable)); - return copy; + return copyWithSettings(s -> s.withDisableRenderFormed(disable)); } @CheckReturnValue From cd5b41afe4a8f6168690d0ff870a50f98932d4fa Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Thu, 27 Aug 2026 19:21:11 -0700 Subject: [PATCH 08/63] add SettingsHolder interface with shortcut methods count with new interface instead of PredicateSettings organize methods in MultiPredicate --- .../gtceu/api/multiblock/MultiPredicate.java | 402 +++++++++++------- .../api/multiblock/PredicateContext.java | 14 +- .../gtceu/api/multiblock/Predicates.java | 38 +- .../gtceu/api/multiblock/XorPredicate.java | 24 +- .../error/SinglePredicateError.java | 8 +- .../pattern/MultiblockPatternBuilder.java | 9 +- .../multiblock/predicates/BasePredicate.java | 39 +- .../predicates/PredicateSettings.java | 46 +- .../multiblock/predicates/SettingsHolder.java | 94 ++++ .../multiblock/util/BlockPatternHelper.java | 4 +- .../common/data/machines/GCYMMachines.java | 58 +-- .../common/data/machines/GTMachineUtils.java | 22 +- .../common/data/machines/GTMultiMachines.java | 78 ++-- .../data/machines/GTResearchMachines.java | 32 +- .../electric/CentralMonitorMachine.java | 12 +- .../multiblock/electric/CleanroomMachine.java | 6 +- .../testmultis/PCBFactoryMachine.java | 2 +- 17 files changed, 522 insertions(+), 366 deletions(-) create mode 100644 src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 72d58df7bce..aefb98250f5 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -3,6 +3,7 @@ import com.gregtechceu.gtceu.api.multiblock.error.PatternStringError; import com.gregtechceu.gtceu.api.multiblock.predicates.BasePredicate; import com.gregtechceu.gtceu.api.multiblock.predicates.PredicateSettings; +import com.gregtechceu.gtceu.api.multiblock.predicates.SettingsHolder; import com.gregtechceu.gtceu.api.multiblock.util.BlockInfo; import net.minecraft.network.chat.Component; @@ -13,6 +14,7 @@ import lombok.experimental.Accessors; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.CheckReturnValue; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -20,7 +22,7 @@ import java.util.function.UnaryOperator; import java.util.stream.Stream; -public abstract class MultiPredicate { +public abstract class MultiPredicate implements SettingsHolder { private static final MultiPredicate EMPTY = of(Logic.OR, List.of()); @@ -40,6 +42,21 @@ public abstract class MultiPredicate { @Accessors(chain = true) private boolean controller; + /* + I was curious on your opinion on how recursiveness of setting mins on multipredicates should be done, if at all + + a.setmin(4).and(b.setmin(4) -> at least 4 of a and 4 of b + + a.and(b).setmin(4) -> at least 4 of a and b combined + + should we allow something like + a.and(b).setmin(4,true) or .setminRecursive(4) -> at least 4 of a and 4 of b + + for the case where the user doesn't have access to the original a and b predicates? + how do we see this "recursive" idea for the difference between children and predicates? and what about when you have multiple nested children? + or should we just not bother? + I'm leaning to not bother, just make the predicates yourself and edit them acordingly, but wanna discuss it at least + */ @Nullable @Getter private PredicateSettings settings = null; @@ -52,15 +69,6 @@ public abstract class MultiPredicate { @Setter private MultiPredicate parent; - public static MultiPredicate of(BasePredicate predicate) { - return Logic.OR.makePredicate(predicate, predicate == BasePredicate.AIR); - } - - /// @param predicates list must be modifiable - private static MultiPredicate of(Logic type, List predicates) { - return type.makePredicate(List.of(), predicates, predicates.stream().anyMatch(p -> p == BasePredicate.AIR)); - } - /// @param children list of multi predicate children /// @param predicates list of testable predicates, should be sorted already protected MultiPredicate(Logic type, List children, List predicates, @@ -103,7 +111,7 @@ public final boolean postGlobalTest(PredicateContext ctx) { ctx.setStage(PredicateContext.PredicateStage.GLOBAL_MIN); boolean passed = testGlobalMin(ctx); if (this.settings != null) { - passed &= this.settings.testGlobalMin(ctx); + passed &= SettingsHolder.super.testGlobalMin(ctx.getGlobalCount(this)); } if (passed) return true; for (Component content : getDescriptiveContents()) { @@ -120,7 +128,7 @@ public final boolean postSliceTest(PredicateContext ctx) { ctx.setStage(PredicateContext.PredicateStage.SLICE_MIN); boolean passed = testSliceMin(ctx); if (this.settings != null) { - passed &= this.settings.testSliceMin(ctx); + passed &= SettingsHolder.super.testSliceMin(ctx.getSliceCount(this)); } if (passed) return true; for (Component content : getDescriptiveContents()) { @@ -136,13 +144,13 @@ public boolean testMaxCount(BasePredicate passedPredicate, PredicateContext cont context.setStage(PredicateContext.PredicateStage.GLOBAL_MAX); boolean passed = passedPredicate.testGlobalMax(context); if (this.settings != null) { - passed &= this.settings.testGlobalMax(context); + passed &= this.testGlobalMax(context.incrementGlobalCount(this)); } if (passed) { context.setStage(PredicateContext.PredicateStage.SLICE_MAX); passed = passedPredicate.testSliceMax(context); if (this.settings != null) { - passed &= this.settings.testSliceMax(context); + passed &= this.testSliceMax(context.incrementSliceCount(this)); } } return passed; @@ -196,47 +204,71 @@ public boolean hasAir() { return this.hasAir; } - @CheckReturnValue - private MultiPredicate deepCopy() { - MultiPredicate copy = this.type.makePredicate( - children().stream().map(MultiPredicate::deepCopy).toList(), - predicates().stream().map(BasePredicate::copy).toList(), - this.hasAir); - copy.setSettings(this.settings); - copy.additionalTooltips.addAll(this.additionalTooltips); - copy.setController(this.controller); - return copy; + /// @return {@code true} if this multi predicate has only one predicate, has no children, and has no parent + public boolean isSingle() { + return predicates.size() == 1 && isLeaf() && isRoot(); } - @CheckReturnValue - public MultiPredicate copyWith(Consumer configurator) { - MultiPredicate copy = deepCopy(); - configurator.accept(copy); - return copy; + /// @return {@code true} if this multi predicate has no parent + public boolean isRoot() { + return getParent() == null; } - @RemapForJS("addTooltip") - @CheckReturnValue - public MultiPredicate addTooltips(Component tooltip) { - return copyWith(p -> p.additionalTooltips.add(tooltip)); + /// @return {@code true} if this multi predicate has children and is not a root predicate + public boolean isBranch() { + return !isLeaf() && !isRoot(); } - @CheckReturnValue - public MultiPredicate addTooltips(Component... tooltip) { - return copyWith(p -> Collections.addAll(p.additionalTooltips, tooltip)); + /// @return {@code true} if this multi predicate has no children multi predicates + public boolean isLeaf() { + return this.children.isEmpty(); } - // spotless:off - protected List gatherSettings() { - if (this.settings != null) { - return List.of(this.settings); + public List getDescriptiveContents() { + List list = new ArrayList<>(); + Component logicLine = switch (this.type) { + case OR -> Component.literal("any of:"); + case AND -> Component.literal("all of:"); + case XOR -> Component.literal("one of:"); + }; + list.add(logicLine); + for (BasePredicate predicate : predicates()) { + // todo prettier string? + list.add(Component.literal(predicate.toString())); + } + for (MultiPredicate child : children()) { + list.addAll(child.getDescriptiveContents()); } - return Stream.concat( - predicates().stream().map(BasePredicate::getSettings), - children().stream().flatMap(mp -> mp.gatherSettings().stream()) - ).toList(); + return list; + } + + protected void forEach(Consumer action) { + this.predicates.forEach(action); + } + + public List predicates() { + return this.predicates; + } + + public void forEachChild(Consumer action) { + this.children.forEach(action); } - // spotless:on + + public List children() { + return this.children; + } + + /// @return a flattened list of all base predicates + public List expand() { + if (isLeaf()) return this.predicates; + List expanded = new ArrayList<>(this.predicates); + forEachChild(mp -> expanded.addAll(mp.expand())); + return expanded; + } + + /* + * MUTATE AND DO NOT COPY + */ protected PredicateSettings getOrCreateSettings() { if (this.settings == null) { @@ -245,189 +277,210 @@ protected PredicateSettings getOrCreateSettings() { return this.settings; } + @RemapForJS("addTooltip") + @Contract(mutates = "this") + public MultiPredicate addTooltips(Component tooltip) { + this.additionalTooltips.add(tooltip); + return this; + } + @CheckReturnValue - public MultiPredicate copyWithSettings(UnaryOperator configurator) { - return copyWith(p -> p.updateSettings(configurator)); + @Contract(mutates = "this") + public MultiPredicate addTooltips(Component... tooltip) { + Collections.addAll(this.additionalTooltips, tooltip); + return this; } private void updateSettings(UnaryOperator configurator) { + if (isSingle()) { + predicates().get(0).updateSettings(configurator); + } setSettings(Objects.requireNonNull(configurator.apply(getOrCreateSettings()))); } - protected void setSettings(@Nullable PredicateSettings settings) { + public void setSettings(@Nullable PredicateSettings settings) { if (settings != null) { this.settings = settings.copy(); } } - @CheckReturnValue - public MultiPredicate setPriority(int priority) { - return copyWithSettings(s -> s.withPriority(priority)); + @Override + public void setPriority(int priority) { + if (this.settings != null) { + updateSettings(s -> s.withPriority(priority)); + } } - @CheckReturnValue - public MultiPredicate setMinCount(int min) { - return copyWithSettings(s -> s.withMinCount(min)); + @Override + public void setMinCount(int minCount) { + if (this.settings != null) { + updateSettings(s -> s.withMinCount(minCount)); + } } - @CheckReturnValue - public MultiPredicate setMaxCount(int max) { - return copyWithSettings(s -> s.withMaxCount(max)); + @Override + public void setMaxCount(int maxCount) { + if (this.settings != null) { + updateSettings(s -> s.withMaxCount(maxCount)); + } } - @CheckReturnValue - public MultiPredicate setMinSliceCount(int min) { - return copyWithSettings(s -> s.withMinSliceCount(min)); + @Override + public void setMinSliceCount(int minSliceCount) { + if (this.settings != null) { + updateSettings(s -> s.withMinSliceCount(minSliceCount)); + } } - @CheckReturnValue - public MultiPredicate setMaxSliceCount(int max) { - return copyWithSettings(s -> s.withMaxSliceCount(max)); + @Override + public void setMaxSliceCount(int maxSliceCount) { + if (this.settings != null) { + updateSettings(s -> s.withMaxSliceCount(maxSliceCount)); + } } - @CheckReturnValue - public MultiPredicate setPreviewCount(int previewCount) { - return copyWithSettings(s -> s.withPreviewCount(previewCount)); + @Override + public void setPreviewCount(int previewCount) { + if (this.settings != null) { + updateSettings(s -> s.withPreviewCount(previewCount)); + } } - @CheckReturnValue - public MultiPredicate setDisableRenderFormed(boolean disable) { - return copyWithSettings(s -> s.withDisableRenderFormed(disable)); + @Override + public void setDisableRenderFormed(boolean disableRenderFormed) { + if (this.settings != null) { + updateSettings(s -> s.withDisableRenderFormed(disableRenderFormed)); + } } - @CheckReturnValue - public MultiPredicate setMinGlobalLimited(int min) { - return this.setMinCount(min); - } + /* + * MUTATE AND COPY + */ @CheckReturnValue - public MultiPredicate setMinGlobalLimited(int min, int previewCount) { - return this.setMinCount(min).setPreviewCount(previewCount); + private MultiPredicate deepCopy() { + MultiPredicate copy = this.type.makePredicate( + children().stream().map(MultiPredicate::deepCopy).toList(), + predicates().stream().map(BasePredicate::copy).toList(), + this.hasAir); + copy.setSettings(this.settings); + copy.additionalTooltips.addAll(this.additionalTooltips); + copy.setController(this.controller); + return copy; } @CheckReturnValue - public MultiPredicate setMaxGlobalLimited(int max) { - return this.setMaxCount(max); + public MultiPredicate copyWith(Consumer configurator) { + MultiPredicate copy = deepCopy(); + configurator.accept(copy); + return copy; } + @Override @CheckReturnValue - public MultiPredicate setMaxGlobalLimited(int max, int previewCount) { - return this.setMaxCount(max).setPreviewCount(previewCount); + public MultiPredicate withSettings(UnaryOperator configurator) { + return copyWith(p -> p.setSettings(configurator.apply(getOrCreateSettings()))); } @CheckReturnValue - public MultiPredicate ranged(int min, int max) { - return this.setMinCount(min).setMaxCount(max); + public MultiPredicate withPriority(int priority) { + return withSettings(s -> s.withPriority(priority)); } @CheckReturnValue - public MultiPredicate setMinLayerLimited(int min) { - return this.setMinSliceCount(min); + public MultiPredicate withMinCount(int min) { + return withSettings(s -> s.withMinCount(min)); } @CheckReturnValue - public MultiPredicate setMinLayerLimited(int min, int previewCount) { - return this.setMinSliceCount(min).setPreviewCount(previewCount); + public MultiPredicate withMaxCount(int max) { + return withSettings(s -> s.withMaxCount(max)); } @CheckReturnValue - public MultiPredicate setMaxLayerLimited(int max) { - return this.setMaxSliceCount(max); + public MultiPredicate withMinSliceCount(int min) { + return withSettings(s -> s.withMinSliceCount(min)); } @CheckReturnValue - public MultiPredicate setMaxLayerLimited(int max, int previewCount) { - return this.setMaxSliceCount(max).setPreviewCount(previewCount); + public MultiPredicate withMaxSliceCount(int max) { + return withSettings(s -> s.withMaxSliceCount(max)); } @CheckReturnValue - public MultiPredicate sliceRanged(int min, int max) { - return this.setMinSliceCount(min).setMaxSliceCount(max); + public MultiPredicate withPreviewCount(int previewCount) { + return withSettings(s -> s.withPreviewCount(previewCount)); } - /** - * Sets the Minimum and Maximum limit to the passed value - * - * @param limit The Maximum and Minimum limit - */ @CheckReturnValue - public MultiPredicate setExactLimit(int limit) { - return this.ranged(limit, limit); + public MultiPredicate withDisableRenderFormed(boolean disable) { + return withSettings(s -> s.withDisableRenderFormed(disable)); } @CheckReturnValue - public MultiPredicate disabledRenderFormed() { - return setDisableRenderFormed(true); + public MultiPredicate withMinGlobalLimited(int min) { + return this.withMinCount(min); } - /// @return a new multi predicate where any predicate may pass or be present in the multiblock - public MultiPredicate or(@Nullable MultiPredicate other) { - return combine(this, Logic.OR, other); + @CheckReturnValue + public MultiPredicate withMinGlobalLimited(int min, int previewCount) { + return withSettings(s -> s.withMinCount(min).withPreviewCount(previewCount)); } - /// @return a new multi predicate where every predicate must pass or be present in the multiblock - public MultiPredicate and(@Nullable MultiPredicate other) { - return combine(this, Logic.AND, other); + @CheckReturnValue + public MultiPredicate withMaxGlobalLimited(int max) { + return this.withMaxCount(max); } - /// @return a new multi predicate with every predicate exclusively
- /// OR-ed (only one predicate may be present in the multi) - public MultiPredicate xor(@Nullable MultiPredicate other) { - return combine(this, Logic.XOR, other); + @CheckReturnValue + public MultiPredicate withMaxGlobalLimited(int max, int previewCount) { + return withSettings(s -> s.withMaxCount(max).withPreviewCount(previewCount)); } - /// @return a new multi predicate where any predicate may pass or be present in the multiblock - public static MultiPredicate or(List predicates) { - return of(Logic.OR, predicates); + @CheckReturnValue + public MultiPredicate withGlobalMinMax(int min, int max) { + return withSettings(s -> s.withMinCount(min).withMaxCount(max)); } - /// @return a new multi predicate where every predicate must pass or be present in the multiblock - public static MultiPredicate and(List predicates) { - return of(Logic.AND, predicates); + @CheckReturnValue + public MultiPredicate withMinLayerLimited(int min) { + return this.withMinSliceCount(min); } - /// @return a new multi predicate with every predicate exclusively
- /// OR-ed (only one predicate may be present in the multi) - public static MultiPredicate xor(List predicates) { - return of(Logic.XOR, predicates); + @CheckReturnValue + public MultiPredicate withMinLayerLimited(int min, int previewCount) { + return withSettings(s -> s.withMinSliceCount(min).withPreviewCount(previewCount)); } - /// @return {@code true} if this multi predicate has only one predicate, has no children, and has no parent - public boolean isSingle() { - return predicates.size() == 1 && isLeaf() && isRoot(); + @CheckReturnValue + public MultiPredicate withMaxLayerLimited(int max) { + return this.withMaxSliceCount(max); } - /// @return {@code true} if this multi predicate has no parent - public boolean isRoot() { - return getParent() == null; + @CheckReturnValue + public MultiPredicate withMaxLayerLimited(int max, int previewCount) { + return withSettings(s -> s.withMaxSliceCount(max).withPreviewCount(previewCount)); } - /// @return {@code true} if this multi predicate has children and is not a root predicate - public boolean isBranch() { - return !isLeaf() && !isRoot(); + @CheckReturnValue + public MultiPredicate withLayerMinMax(int min, int max) { + return withSettings(s -> s.withMinSliceCount(min).withMaxSliceCount(max)); } - /// @return {@code true} if this multi predicate has no children multi predicates - public boolean isLeaf() { - return this.children.isEmpty(); + /** + * Sets the Minimum and Maximum limit to the passed value + * + * @param limit The Maximum and Minimum limit + */ + @CheckReturnValue + public MultiPredicate withExactLimit(int limit) { + return this.withGlobalMinMax(limit, limit); } - public List getDescriptiveContents() { - List list = new ArrayList<>(); - Component logicLine = switch (this.type) { - case OR -> Component.literal("any of:"); - case AND -> Component.literal("all of:"); - case XOR -> Component.literal("one of:"); - }; - list.add(logicLine); - for (BasePredicate predicate : this.predicates) { - // todo prettier string? - list.add(Component.literal(predicate.toString())); - } - for (MultiPredicate child : children()) { - list.addAll(child.getDescriptiveContents()); - } - return list; + /// @return a copy of this multi predicate with render formed disabled + @CheckReturnValue + public MultiPredicate disabledRenderFormed() { + return withDisableRenderFormed(true); } @Override @@ -450,32 +503,40 @@ public String toString() { return builder.toString(); } - protected void forEach(Consumer action) { - this.predicates.forEach(action); + /* + * LOGIC AND COMBINATION + */ + + /// @return a new multi predicate where any predicate may pass or be present in the multiblock + public MultiPredicate or(@Nullable MultiPredicate other) { + return combine(this, Logic.OR, other); } - public List predicates() { - return this.predicates; + /// @return a new multi predicate where every predicate must pass or be present in the multiblock + public MultiPredicate and(@Nullable MultiPredicate other) { + return combine(this, Logic.AND, other); } - public void forEachChild(Consumer action) { - this.children.forEach(action); + /// @return a new multi predicate with every predicate exclusively
+ /// OR-ed (only one predicate may be present in the multi) + public MultiPredicate xor(@Nullable MultiPredicate other) { + return combine(this, Logic.XOR, other); } - public List children() { - return this.children; + /// @return a new multi predicate where any predicate may pass or be present in the multiblock + public static MultiPredicate or(List predicates) { + return of(Logic.OR, predicates); } - /// @return a flattened list of all base predicates - public List expand() { - if (isLeaf()) return this.predicates; - List expanded = new ArrayList<>(this.predicates); - forEachChild(mp -> expanded.addAll(mp.expand())); - return expanded; + /// @return a new multi predicate where every predicate must pass or be present in the multiblock + public static MultiPredicate and(List predicates) { + return of(Logic.AND, predicates); } - public static MultiPredicate empty() { - return EMPTY; + /// @return a new multi predicate with every predicate exclusively
+ /// OR-ed (only one predicate may be present in the multi) + public static MultiPredicate xor(List predicates) { + return of(Logic.XOR, predicates); } /// @param a left operand @@ -505,6 +566,19 @@ private static void appendPredicates(Logic type, MultiPredicate multiPredicate, } } + public static MultiPredicate empty() { + return EMPTY; + } + + public static MultiPredicate of(BasePredicate predicate) { + return Logic.OR.makePredicate(predicate, predicate == BasePredicate.AIR); + } + + /// @param predicates list must be modifiable + private static MultiPredicate of(Logic type, List predicates) { + return type.makePredicate(List.of(), predicates, predicates.stream().anyMatch(p -> p == BasePredicate.AIR)); + } + protected enum Logic { OR, diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java index 40257668f46..45df4f7afe2 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java @@ -3,7 +3,7 @@ import com.gregtechceu.gtceu.api.multiblock.error.PatternError; import com.gregtechceu.gtceu.api.multiblock.pattern.CurrentBlockInfo; import com.gregtechceu.gtceu.api.multiblock.pattern.PatternState; -import com.gregtechceu.gtceu.api.multiblock.predicates.PredicateSettings; +import com.gregtechceu.gtceu.api.multiblock.predicates.SettingsHolder; import com.gregtechceu.gtceu.api.multiblock.util.BlockInfo; import net.minecraft.core.BlockPos; @@ -29,8 +29,8 @@ public class PredicateContext { private final PatternState state; @Getter protected CurrentBlockInfo currentBlockInfo = new CurrentBlockInfo(); - protected final Object2IntMap globalCount = new Object2IntOpenHashMap<>(); - protected final Object2IntMap layerCount = new Object2IntOpenHashMap<>(); + protected final Object2IntMap globalCount = new Object2IntOpenHashMap<>(); + protected final Object2IntMap layerCount = new Object2IntOpenHashMap<>(); private final Int2ObjectMap> sliceErrors = new Int2ObjectAVLTreeMap<>( IntComparators.NATURAL_COMPARATOR); @@ -131,20 +131,20 @@ public void clearLayerCounts() { this.layerCount.clear(); } - public int incrementGlobalCount(PredicateSettings predicate) { + public int incrementGlobalCount(SettingsHolder predicate) { return this.globalCount.mergeInt(predicate, 1, Integer::sum); } - public int incrementSliceCount(PredicateSettings predicate) { + public int incrementSliceCount(SettingsHolder predicate) { if (!checkLayer) return 0; return this.layerCount.mergeInt(predicate, 1, Integer::sum); } - public int getGlobalCount(PredicateSettings predicate) { + public int getGlobalCount(SettingsHolder predicate) { return this.globalCount.getInt(predicate); } - public int getSliceCount(PredicateSettings predicate) { + public int getSliceCount(SettingsHolder predicate) { if (!checkLayer) return 0; return this.layerCount.getInt(predicate); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/Predicates.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/Predicates.java index af3e9170d94..6b5306bfbc0 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/Predicates.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/Predicates.java @@ -259,8 +259,8 @@ public static MultiPredicate autoAbilities(GTRecipeType[] recipeType, for (var type : recipeType) { if (type.getMaxInputs(EURecipeCapability.CAP) > 0) { predicate = predicate.and(abilities(PartAbility.INPUT_ENERGY) - .setMinCount(1).setMaxCount(2) - .setPreviewCount(1).setPriority(1)); + .withGlobalMinMax(1, 2) + .withPreviewCount(1).withPriority(1)); break; } } @@ -269,8 +269,9 @@ public static MultiPredicate autoAbilities(GTRecipeType[] recipeType, for (var type : recipeType) { if (type.getMaxOutputs(EURecipeCapability.CAP) > 0) { predicate = predicate.and(abilities(PartAbility.OUTPUT_ENERGY) - .setMinCount(1).setMaxCount(2) - .setPreviewCount(1).setPriority(1)); + .withGlobalMinMax(1, 2) + .withPreviewCount(1) + .withPriority(1)); break; } } @@ -279,7 +280,7 @@ public static MultiPredicate autoAbilities(GTRecipeType[] recipeType, for (var type : recipeType) { if (type.getMaxInputs(ItemRecipeCapability.CAP) > 0) { predicate = predicate.and(abilities(PartAbility.IMPORT_ITEMS) - .setPreviewCount(1).setPriority(2)); + .withPreviewCount(1).withPriority(2)); break; } } @@ -288,7 +289,7 @@ public static MultiPredicate autoAbilities(GTRecipeType[] recipeType, for (var type : recipeType) { if (type.getMaxOutputs(ItemRecipeCapability.CAP) > 0) { predicate = predicate.and(abilities(PartAbility.EXPORT_ITEMS) - .setPreviewCount(1).setPriority(2)); + .withPreviewCount(1).withPriority(2)); break; } } @@ -297,7 +298,7 @@ public static MultiPredicate autoAbilities(GTRecipeType[] recipeType, for (var type : recipeType) { if (type.getMaxInputs(FluidRecipeCapability.CAP) > 0) { predicate = predicate.and(abilities(PartAbility.IMPORT_FLUIDS) - .setPreviewCount(1).setPriority(3)); + .withPreviewCount(1).withPriority(3)); break; } } @@ -306,7 +307,7 @@ public static MultiPredicate autoAbilities(GTRecipeType[] recipeType, for (var type : recipeType) { if (type.getMaxOutputs(FluidRecipeCapability.CAP) > 0) { predicate = predicate.and(abilities(PartAbility.EXPORT_FLUIDS) - .setPreviewCount(1).setPriority(3)); + .withPreviewCount(1).withPriority(3)); break; } } @@ -319,20 +320,19 @@ public static MultiPredicate autoAbilities(boolean checkMaintenance, boolean che MultiPredicate predicate = MultiPredicate.empty(); if (checkMaintenance) { predicate = predicate.and(abilities(PartAbility.MAINTENANCE) - .setMinCount(ConfigHolder.INSTANCE.machines.enableMaintenance ? 1 : 0) - .setMaxCount(1) - .setPriority(1)); + .withMinCount(ConfigHolder.INSTANCE.machines.enableMaintenance ? 1 : 0) + .withMaxCount(1) + .withPriority(1)); } if (checkMuffler) { predicate = predicate.and(abilities(PartAbility.MUFFLER) - .setExactLimit(1) - .setPriority(2)); + .withExactLimit(1) + .withPriority(2)); } if (checkParallel) { predicate = predicate.and(abilities(PartAbility.PARALLEL_HATCH) - .setMaxCount(1) - .setPreviewCount(1) - .setPriority(3)); + .withMaxGlobalLimited(1, 1) + .withPriority(3)); } return predicate; } @@ -345,7 +345,7 @@ public static MultiPredicate heatingCoils() { .sorted(Comparator.comparingInt(e -> e.getKey().getTier())) .map(e -> e.getValue().get())) .addTooltips(Component.translatable("gtceu.multiblock.pattern.error.coils")) - .setPriority(0); + .withPriority(0); } public static MultiPredicate cleanroomFilters() { @@ -373,8 +373,8 @@ public static MultiPredicate powerSubstationBatteries() { if (ConfigHolder.INSTANCE.machines.enableResearch) { return abilities(PartAbility.DATA_ACCESS) .xor(abilities(PartAbility.OPTICAL_DATA_RECEPTION)) - .setExactLimit(1) - .setPriority(1); + .withExactLimit(1) + .withPriority(1); } return null; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java index b18b617f8c7..5c28f2073b1 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java @@ -25,16 +25,26 @@ public XorPredicate(List children, List predicate } @Override - protected void setSettings(@Nullable PredicateSettings settings) { + public void setSettings(@Nullable PredicateSettings settings) { super.setSettings(settings); - boolean noneValid = false; - for (PredicateSettings setting : gatherSettings()) { - if (setting.minCount() <= 0 && setting.minSliceCount() <= 0) { - noneValid = true; - break; + this.noneValid = isNoneValid(this); + } + + private static boolean isNoneValid(MultiPredicate multiPredicate) { + PredicateSettings settings = multiPredicate.getSettings(); + if (settings != null) return settings.isNoneValid(); + + for (BasePredicate predicate : multiPredicate.predicates()) { + if (predicate.getSettings().isNoneValid()) { + return true; + } + } + for (MultiPredicate child : multiPredicate.children()) { + if (isNoneValid(child)) { + return true; } } - this.noneValid = noneValid; + return false; } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/error/SinglePredicateError.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/error/SinglePredicateError.java index 820141b9b73..01bf9f8e558 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/error/SinglePredicateError.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/error/SinglePredicateError.java @@ -45,10 +45,10 @@ public class SinglePredicateError extends PatternError { public SinglePredicateError(BasePredicate failingPredicate, ErrorType type, int actualCount) { this(type, actualCount, - failingPredicate.getSettings().minCount(), - failingPredicate.getSettings().maxCount(), - failingPredicate.getSettings().minSliceCount(), - failingPredicate.getSettings().maxSliceCount(), + failingPredicate.getMinCount(), + failingPredicate.getMaxCount(), + failingPredicate.getMinSliceCount(), + failingPredicate.getMaxSliceCount(), failingPredicate.toString(), failingPredicate.getCandidates()); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/MultiblockPatternBuilder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/MultiblockPatternBuilder.java index 7d4d4fc4ff3..916e33bdf29 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/MultiblockPatternBuilder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/MultiblockPatternBuilder.java @@ -178,16 +178,15 @@ private void checkGlobalConstraints() { if (predicate == null) throw new IllegalArgumentException("Predicate for symbol " + symbol + " was null."); int maxCount = -1; for (var basePredicate : predicate.expand()) { - PredicateSettings settings = basePredicate.getSettings(); - if (settings.maxCount() == -1) { + if (basePredicate.getMaxCount() == -1) { maxCount = -1; break; } - if (settings.minCount() == settings.maxCount()) { + if (basePredicate.getMinCount() == basePredicate.getMaxCount()) { if (maxCount == -1) { - maxCount = settings.minCount(); + maxCount = basePredicate.getMinCount(); } else { - maxCount += settings.minCount(); + maxCount += basePredicate.getMinCount(); } } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index 37edc8557c4..4a0eb55324c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -13,8 +13,9 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.function.UnaryOperator; -public abstract class BasePredicate implements Comparable { +public abstract class BasePredicate implements Comparable, SettingsHolder { public static final BasePredicate AIR = new PredicateBuilder("Air") .predicate(ctx -> ctx.state().isAir()) @@ -71,8 +72,8 @@ public boolean checkMaxCount(PredicateContext context) { /// test against global max count public boolean testGlobalMax(PredicateContext ctx) { - int count = ctx.incrementGlobalCount(getSettings()); - if (getSettings().testGlobalMax(count)) return true; + int count = ctx.incrementGlobalCount(this); + if (testGlobalMax(count)) return true; ctx.appendError(SinglePredicateError.maxCount(this, count)); return false; } @@ -80,8 +81,8 @@ public boolean testGlobalMax(PredicateContext ctx) { /// test against slice max count public boolean testSliceMax(PredicateContext ctx) { if (!ctx.isCheckLayer()) return true; - int count = ctx.incrementSliceCount(getSettings()); - if (getSettings().testSliceMax(count)) return true; + int count = ctx.incrementSliceCount(this); + if (testSliceMax(count)) return true; ctx.appendError(SinglePredicateError.maxLayerCount(this, count)); return false; } @@ -89,21 +90,17 @@ public boolean testSliceMax(PredicateContext ctx) { /// test against global min count public boolean testGlobalMin(PredicateContext ctx) { if (getMinCount() == -1) return true; - int count = ctx.getGlobalCount(getSettings()); - if (getSettings().testGlobalMin(count)) return true; + int count = ctx.getGlobalCount(this); + if (testGlobalMin(count)) return true; ctx.appendError(SinglePredicateError.minCount(this, count)); return false; } - private int getMinCount() { - return getSettings().minCount(); - } - /// test against slice min count public boolean testSliceMin(PredicateContext ctx) { if (!ctx.isCheckLayer()) return true; - int count = ctx.getSliceCount(getSettings()); - if (getSettings().testSliceMin(count)) return true; + int count = ctx.getSliceCount(this); + if (testSliceMin(count)) return true; ctx.appendError(SinglePredicateError.minLayerCount(this, count)); return false; } @@ -143,4 +140,20 @@ public String toString() { public int compareTo(BasePredicate o) { return this.settings.comparePriority(o.settings); } + + @Override + public BasePredicate withSettings(UnaryOperator configurator) { + BasePredicate copy = copy(); + copy.updateSettings(configurator); + return copy; + } + + public void updateSettings(UnaryOperator configurator) { + setSettings(configurator.apply(getSettings())); + } + + @Override + public void setSettings(PredicateSettings settings) { + this.settings = settings; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java index 9c28e2d5227..f905b7b8ba8 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java @@ -24,6 +24,12 @@ public record PredicateSettings(int priority, ).apply(instance, PredicateSettings::new)); // spotless:on + + // helper for xor + public boolean isNoneValid() { + return minCount <= 0 && minSliceCount <= 0; + } + public PredicateSettings copy() { return new PredicateSettings( this.priority, @@ -39,46 +45,6 @@ public int comparePriority(PredicateSettings other) { return Integer.compare(this.priority, other.priority); } - /// simple test against global min count - public boolean testGlobalMin(int count) { - return minCount == -1 || count >= minCount; - } - - /// simple test against slice min count - public boolean testSliceMin(int count) { - return minSliceCount == -1 || count >= minSliceCount; - } - - /// simple test against global max count - public boolean testGlobalMax(int count) { - return maxCount == -1 || count <= maxCount; - } - - /// simple test against slice max count - public boolean testSliceMax(int count) { - return maxSliceCount == -1 || count <= maxSliceCount; - } - - /// simple test against global min count - public boolean testGlobalMin(PredicateContext ctx) { - return testGlobalMin(ctx.getGlobalCount(this)); - } - - /// simple test against slice min count - public boolean testSliceMin(PredicateContext ctx) { - return testSliceMin(ctx.getSliceCount(this)); - } - - /// simple test against global max count, increments global count - public boolean testGlobalMax(PredicateContext ctx) { - return testGlobalMax(ctx.incrementGlobalCount(this)); - } - - /// simple test against slice max count, increments slice count - public boolean testSliceMax(PredicateContext ctx) { - return testSliceMax(ctx.incrementSliceCount(this)); - } - public static PredicateSettings create() { return new PredicateSettings( 0, diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java new file mode 100644 index 00000000000..8ad7ed1fa6c --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -0,0 +1,94 @@ +package com.gregtechceu.gtceu.api.multiblock.predicates; + +import java.util.function.UnaryOperator; + +public interface SettingsHolder { + + // getters + PredicateSettings getSettings(); + + default int getPriority() { + return getSettings().priority(); + } + + default int getMinCount() { + return getSettings().minCount(); + } + + default int getMaxCount() { + return getSettings().maxCount(); + } + + default int getMinSliceCount() { + return getSettings().minSliceCount(); + } + + default int getMaxSliceCount() { + return getSettings().maxSliceCount(); + } + + default int getPreviewCount() { + return getSettings().previewCount(); + } + + default boolean isRenderFormedDisabled() { + return getSettings().disableRenderFormed(); + } + + // mutate only + void setSettings(PredicateSettings settings); + + void setPriority(int priority); + + void setMinCount(int minCount); + + void setMaxCount(int maxCount); + + void setMinSliceCount(int minSliceCount); + + void setMaxSliceCount(int maxSliceCount); + + void setPreviewCount(int previewCount); + + void setDisableRenderFormed(boolean disableRenderFormed); + + + // copy and mutate + /// @return a copy with these settings applied + SettingsHolder withSettings(UnaryOperator configurator); + + SettingsHolder withPriority(int priority); + + SettingsHolder withMinCount(int minCount); + + SettingsHolder withMaxCount(int maxCount); + + SettingsHolder withMinSliceCount(int minSliceCount); + + SettingsHolder withMaxSliceCount(int maxSliceCount); + + SettingsHolder withPreviewCount(int previewCount); + + SettingsHolder withDisableRenderFormed(boolean disableRenderFormed); + + // test methods + /// simple test against global min count + default boolean testGlobalMin(int count) { + return getMinCount() == -1 || count >= getMinCount(); + } + + /// simple test against slice min count + default boolean testSliceMin(int count) { + return getMinSliceCount() == -1 || count >= getMinSliceCount(); + } + + /// simple test against global max count + default boolean testGlobalMax(int count) { + return getMaxCount() == -1 || count <= getMaxCount(); + } + + /// simple test against slice max count + default boolean testSliceMax(int count) { + return getMaxSliceCount() == -1 || count <= getMaxSliceCount(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/BlockPatternHelper.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/BlockPatternHelper.java index 4ff87b9aa69..df3af0196ae 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/BlockPatternHelper.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/BlockPatternHelper.java @@ -138,7 +138,7 @@ private boolean tryMinCount(Map resultStructure, MultiPredi int totalAlreadyPopulated = countPopulatedGlobal(resultStructure, basePredicate); int layerAlreadyPopulated = countPopulatedInLayer(resultStructure, basePredicate, dir, offset); boolean globalMinMet = minCount == -1 || totalAlreadyPopulated >= minCount; - boolean sliceMinMet = basePredicate.getSettings().testSliceMin(layerAlreadyPopulated); + boolean sliceMinMet = basePredicate.testSliceMin(layerAlreadyPopulated); if (globalMinMet && sliceMinMet) continue; @@ -173,7 +173,7 @@ private boolean tryMaxCount(Map resultStructure, MultiPredi int totalAlreadyPopulated = countPopulatedGlobal(resultStructure, basePredicate); int layerAlreadyPopulated = countPopulatedInLayer(resultStructure, basePredicate, dir, offset); if (maxCount != -1 && totalAlreadyPopulated >= maxCount) continue; - if (!basePredicate.getSettings().testSliceMax(layerAlreadyPopulated + 1)) continue; + if (!basePredicate.testSliceMax(layerAlreadyPopulated + 1)) continue; BlockInfo toInsert = blockPreferences.get(predicate, basePredicate); if (toInsert == null) { diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GCYMMachines.java b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GCYMMachines.java index c951e273b8e..9b4127ea787 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GCYMMachines.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GCYMMachines.java @@ -95,7 +95,7 @@ public static void init() {} .slice("XXXXX", "XGGGX", "XGGGX", "XAAAX") .slice("XXXXX", "XXXXX", "XXSXX", "XXXXX") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_SECURE_MACERATION.get()).setMinGlobalLimited(55) + .where('X', blocks(CASING_SECURE_MACERATION.get()).withMinGlobalLimited(55) .and(Predicates.autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, false, true))) .where('G', Predicates.blocks(CRUSHING_WHEELS.get())) @@ -124,7 +124,7 @@ public static void init() {} .slice("XXXXX", "XTTTX", "X X") .slice("XXXXX", "XXSXX", "XXXXX") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_WATERTIGHT.get()).setMinGlobalLimited(55) + .where('X', blocks(CASING_WATERTIGHT.get()).withMinGlobalLimited(55) .and(Predicates.autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, false, true))) .where(' ', Predicates.air()) @@ -154,7 +154,7 @@ public static void init() {} .slice("XXXXX", "XAPAX", "XXXXX") .slice("#XXX#", "XXSXX", "#XXX#") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_VIBRATION_SAFE.get()).setMinGlobalLimited(40) + .where('X', blocks(CASING_VIBRATION_SAFE.get()).withMinGlobalLimited(40) .and(Predicates.autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, false, true))) .where('P', Predicates.blocks(CASING_STEEL_PIPE.get())) @@ -182,7 +182,7 @@ public static void init() {} .slice("XXXXX", "XAPAX", "XAAAX", "XAPAX", "XAAAX", "##F##") .slice("#XXX#", "#XSX#", "#XXX#", "#XXX#", "#XXX#", "##F##") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_REACTION_SAFE.get()).setMinGlobalLimited(50) + .where('X', blocks(CASING_REACTION_SAFE.get()).withMinGlobalLimited(50) .and(autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, false, true))) .where('F', frames(GTMaterials.HastelloyX)) @@ -214,7 +214,7 @@ public static void init() {} .slice("XXXXX", "XCCCX", "XCCCX") .slice("XXXXX", "XXSXX", "XXXXX") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_NONCONDUCTING.get()).setMinGlobalLimited(30) + .where('X', blocks(CASING_NONCONDUCTING.get()).withMinGlobalLimited(30) .and(Predicates.autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, false, true))) .where('C', blocks(ELECTROLYTIC_CELL.get())) @@ -240,7 +240,7 @@ public static void init() {} .slice("XCXCX", "XCXCX", "XCXCX") .slice("XXXXX", "XXSXX", "XXXXX") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_NONCONDUCTING.get()).setMinGlobalLimited(35) + .where('X', blocks(CASING_NONCONDUCTING.get()).withMinGlobalLimited(35) .and(Predicates.autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, false, true))) .where('C', blocks(ELECTROLYTIC_CELL.get())) @@ -267,7 +267,7 @@ public static void init() {} .slice("XXX", "XAX", "XXX") .slice("XXX", "XSX", "XXX") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_TUNGSTENSTEEL_ROBUST.get()).setMinGlobalLimited(30) + .where('X', blocks(CASING_TUNGSTENSTEEL_ROBUST.get()).withMinGlobalLimited(30) .and(Predicates.autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, false, true))) .where('A', Predicates.air()) @@ -295,10 +295,10 @@ public static void init() {} .slice("XXXXXXXXX", "XAAAXAAAX", "XGGGXXXXX") .slice("XXXXXXXXX", "XGGGXXSXX", "XGGGX###X") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_LARGE_SCALE_ASSEMBLING.get()).setMinGlobalLimited(40) + .where('X', blocks(CASING_LARGE_SCALE_ASSEMBLING.get()).withMinGlobalLimited(40) .and(Predicates.autoAbilities(definition.getRecipeTypes(), false, false, true, true, true, true)) - .and(Predicates.abilities(INPUT_ENERGY).setExactLimit(1)) + .and(Predicates.abilities(INPUT_ENERGY).withExactLimit(1)) .and(Predicates.autoAbilities(true, false, true))) .where('G', Predicates.blocks(CASING_TEMPERED_GLASS.get())) .where('A', Predicates.air()) @@ -329,10 +329,10 @@ public static void init() {} .slice("XXXXXXX", "XTTTTXX", "XXXXXXX") .slice("#####XX", "#####SX", "#####XX") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_LARGE_SCALE_ASSEMBLING.get()).setMinGlobalLimited(55) + .where('X', blocks(CASING_LARGE_SCALE_ASSEMBLING.get()).withMinGlobalLimited(55) .and(Predicates.autoAbilities(definition.getRecipeTypes(), false, false, true, true, true, true)) - .and(Predicates.abilities(INPUT_ENERGY).setExactLimit(1)) + .and(Predicates.abilities(INPUT_ENERGY).withExactLimit(1)) .and(Predicates.autoAbilities(true, false, true))) .where('T', Predicates.blocks(CASING_TEMPERED_GLASS.get())) .where('G', Predicates.blocks(CASING_GRATE.get())) @@ -361,7 +361,7 @@ public static void init() {} .slice("XXXXX", "XACAX", "XACAX", "XXXXX") .slice("#XXX#", "#XSX#", "#XXX#", "#XXX#") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_HIGH_TEMPERATURE_SMELTING.get()).setMinGlobalLimited(45) + .where('X', blocks(CASING_HIGH_TEMPERATURE_SMELTING.get()).withMinGlobalLimited(45) .and(Predicates.autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, false, true))) .where('C', Predicates.blocks(MOLYBDENUM_DISILICIDE_COIL_BLOCK.get())) @@ -394,7 +394,7 @@ public static void init() {} .slice("XXSXX", "XXGXX", "XXGXX", "XXXXX") .where('S', controller(blocks(definition.get()))) .where('C', blocks(CASING_TUNGSTENSTEEL_PIPE.get())) - .where('X', blocks(CASING_LASER_SAFE_ENGRAVING.get()).setMinGlobalLimited(50) + .where('X', blocks(CASING_LASER_SAFE_ENGRAVING.get()).withMinGlobalLimited(50) .and(Predicates.autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, false, true))) .where('G', blocks(CASING_TEMPERED_GLASS.get())) @@ -422,7 +422,7 @@ public static void init() {} .slice("XXXXX", "XAXAX", "XKKKX", "XKKKX", "X###X") .slice("#X#X#", "#X#X#", "#XSX#", "XXXXX", "#XXX#") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_VIBRATION_SAFE.get()).setMinGlobalLimited(50) + .where('X', blocks(CASING_VIBRATION_SAFE.get()).withMinGlobalLimited(50) .and(Predicates.autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, false, true))) .where('K', blocks(CASING_GRATE.get())) @@ -452,7 +452,7 @@ public static void init() {} .slice("XXXXX", "CAAAC", "GAAAG", "CAAAC", "XXXXX") .slice("#XSX#", "#CCC#", "#GGG#", "#CCC#", "#XXX#") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_HIGH_TEMPERATURE_SMELTING.get()).setMinGlobalLimited(30) + .where('X', blocks(CASING_HIGH_TEMPERATURE_SMELTING.get()).withMinGlobalLimited(30) .and(autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, false, false))) .where('C', heatingCoils()) @@ -500,7 +500,7 @@ public static void init() {} .slice("XXX", "XTX", "XXX") .slice("XXX", "XSX", "XXX") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_WATERTIGHT.get()).setMinGlobalLimited(30) + .where('X', blocks(CASING_WATERTIGHT.get()).withMinGlobalLimited(30) .and(autoAbilities(definition.getRecipeTypes())) .and(autoAbilities(true, false, true))) .where('T', blocks(CASING_STEEL_PIPE.get())) @@ -526,7 +526,7 @@ public static void init() {} .slice("XXXXXXX", "XAXGGGX", "XXXXXXX") .slice("XXXXXXX", "XSXCCCX", "XXXXXXX") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_STRESS_PROOF.get()).setMinGlobalLimited(40) + .where('X', blocks(CASING_STRESS_PROOF.get()).withMinGlobalLimited(40) .and(autoAbilities(definition.getRecipeTypes())) .and(autoAbilities(true, false, true))) .where('G', blocks(CASING_STEEL_GEARBOX.get())) @@ -555,7 +555,7 @@ public static void init() {} .slice("XXXXX", "XCCCX", "XAAAX", "XXAXX", "##X##") .slice("#XXX#", "#XSX#", "#XXX#", "#XXX#", "#####") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_CORROSION_PROOF.get()).setMinGlobalLimited(50) + .where('X', blocks(CASING_CORROSION_PROOF.get()).withMinGlobalLimited(50) .and(autoAbilities(definition.getRecipeTypes())) .and(autoAbilities(true, false, true))) .where('P', blocks(CASING_STEEL_PIPE.get())) @@ -583,7 +583,7 @@ public static void init() {} .slice("XXXXXXX", "XAXCCCX", "XXXAAAX", "##XXXXX") .slice("XXXXXXX", "XSXGGGX", "XXXGGGX", "##XXXXX") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_SHOCK_PROOF.get()).setMinGlobalLimited(65) + .where('X', blocks(CASING_SHOCK_PROOF.get()).withMinGlobalLimited(65) .and(autoAbilities(definition.getRecipeTypes())) .and(autoAbilities(true, false, true))) .where('G', blocks(CASING_TEMPERED_GLASS.get())) @@ -606,11 +606,11 @@ public static void init() {} .recipeModifiers(GTRecipeModifiers.PARALLEL_HATCH, OC_NON_PERFECT_SUBTICK, BATCH_MODE) .appearanceBlock(CASING_WATERTIGHT) .pattern(definition -> { - MultiPredicate casingPredicate = blocks(CASING_WATERTIGHT.get()).setMinGlobalLimited(40); + MultiPredicate casingPredicate = blocks(CASING_WATERTIGHT.get()).withMinGlobalLimited(40); MultiPredicate exportPredicate = abilities(PartAbility.EXPORT_FLUIDS_1X); if (GTCEu.Mods.isAE2Loaded()) exportPredicate = exportPredicate.xor(blocks(GTAEMachines.FLUID_EXPORT_HATCH_ME.get())); - exportPredicate = exportPredicate.setMaxLayerLimited(1); + exportPredicate = exportPredicate.withMaxLayerLimited(1); return MultiblockPatternBuilder.start(UP, BACK, RIGHT) .slice("#YYY#", "YYYYY", "YYYYY", "YYYYY", "#YYY#") .slice("#YSY#", "YAAAY", "YAAAY", "YAAAY", "#YYY#") @@ -618,8 +618,8 @@ public static void init() {} .slice("#####", "#ZZZ#", "#ZZZ#", "#ZZZ#", "#####") .where('S', controller(blocks(definition.get()))) .where('Y', casingPredicate.and(abilities(IMPORT_ITEMS)) - .and(abilities(INPUT_ENERGY).setMinGlobalLimited(1).setMaxGlobalLimited(2)) - .and(abilities(IMPORT_FLUIDS).setMinGlobalLimited(1)) + .and(abilities(INPUT_ENERGY).withMinGlobalLimited(1).withMaxGlobalLimited(2)) + .and(abilities(IMPORT_FLUIDS).withMinGlobalLimited(1)) .and(abilities(EXPORT_ITEMS)) .and(autoAbilities(true, false, true))) .where('X', casingPredicate.and(exportPredicate)) @@ -650,7 +650,7 @@ public static void init() {} .slice("XXXXX", "XCACX", "XXXXX") .slice("XXXXX", "XXSXX", "XXXXX") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_WATERTIGHT.get()).setMinGlobalLimited(25) + .where('X', blocks(CASING_WATERTIGHT.get()).withMinGlobalLimited(25) .and(autoAbilities(definition.getRecipeTypes())) .and(autoAbilities(true, false, true))) .where('C', blocks(CASING_STEEL_PIPE.get())) @@ -677,7 +677,7 @@ public static void init() {} .sliceRepeatable(2, 2, "##XXX", "##XPX", "##XGX") .slice("##XXX", "##XXX", "##XXX") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_STRESS_PROOF.get()).setMinGlobalLimited(40) + .where('X', blocks(CASING_STRESS_PROOF.get()).withMinGlobalLimited(40) .and(autoAbilities(definition.getRecipeTypes())) .and(autoAbilities(true, false, true))) .where('P', blocks(CASING_TITANIUM_PIPE.get())) @@ -706,7 +706,7 @@ public static void init() {} .slice("XXXXX", "XCACX", "XCACX", "XXXXX") .slice("#XXX#", "#XSX#", "#XXX#", "#XXX#") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_WATERTIGHT.get()).setMinGlobalLimited(45) + .where('X', blocks(CASING_WATERTIGHT.get()).withMinGlobalLimited(45) .and(autoAbilities(definition.getRecipeTypes())) .and(autoAbilities(true, false, true))) .where('C', blocks(CASING_STEEL_PIPE.get())) @@ -732,7 +732,7 @@ public static void init() {} .slice("XXXXX", "X#CCX", "XXXXX") .slice("XXXXX", "XSXXX", "XXX##") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_STRESS_PROOF.get()).setMinGlobalLimited(25) + .where('X', blocks(CASING_STRESS_PROOF.get()).withMinGlobalLimited(25) .and(autoAbilities(definition.getRecipeTypes())) .and(autoAbilities(true, false, true))) .where('C', blocks(CASING_TITANIUM_GEARBOX.get())) @@ -757,7 +757,7 @@ public static void init() {} .recipeModifiers(GTRecipeModifiers.PARALLEL_HATCH, GTRecipeModifiers::ebfOverclock, BATCH_MODE) .appearanceBlock(CASING_HIGH_TEMPERATURE_SMELTING) .pattern(definition -> { - MultiPredicate casing = blocks(CASING_HIGH_TEMPERATURE_SMELTING.get()).setMinGlobalLimited(360); + MultiPredicate casing = blocks(CASING_HIGH_TEMPERATURE_SMELTING.get()).withMinGlobalLimited(360); return MultiblockPatternBuilder.start(FRONT, UP, RIGHT) // spotless:off .slice("##XXXXXXXXX##", "##XXXXXXXXX##", "#############", "#############", "#############", "#############", "#############", "#############", "#############", "#############", "#############", "#############", "#############", "#############", "#############", "#############", "#############") @@ -832,7 +832,7 @@ public static void init() {} .slice("#XXXXX#####", "#XXSXX#####", "#XGGGX#####", "#XGGGX#####", "#XGGGX#####", "#XXXXX#####", "###########") // spotless:on .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_ALUMINIUM_FROSTPROOF.get()).setMinGlobalLimited(140) + .where('X', blocks(CASING_ALUMINIUM_FROSTPROOF.get()).withMinGlobalLimited(140) .and(autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, false, true))) .where('G', blocks(CASING_TEMPERED_GLASS.get())) diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMachineUtils.java b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMachineUtils.java index 0bbef969da9..c166b7e34f0 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMachineUtils.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMachineUtils.java @@ -546,7 +546,7 @@ public static MultiblockMachineDefinition registerMultiblockTank(GTRegistrate re .slice("CCC", "CSC", "CCC") .where('S', controller(blocks(definition.get()))) .where('C', blocks(casing.get()) - .and(blocks(valve.get()).setMaxGlobalLimited(2, 0))) + .and(blocks(valve.get()).withMaxGlobalLimited(2, 0))) .where('#', air()) .build()) .appearanceBlock(casing); @@ -604,15 +604,15 @@ public static MultiblockMachineDefinition registerLargeBoiler(GTRegistrate regis controller.getBlockPos().below().getY() == part.getBlockPos().getY() ? fireBox.get().defaultBlockState() : casing.get().defaultBlockState()) .pattern((definition) -> { - MultiPredicate fireboxPred = blocks(ALL_FIREBOXES.get(firebox).get()).setMinGlobalLimited(3) - .and(Predicates.abilities(PartAbility.IMPORT_FLUIDS).setMinGlobalLimited(1) + MultiPredicate fireboxPred = blocks(ALL_FIREBOXES.get(firebox).get()).withMinGlobalLimited(3) + .and(Predicates.abilities(PartAbility.IMPORT_FLUIDS).withMinGlobalLimited(1) .setPreviewCount(1)) - .and(Predicates.abilities(PartAbility.IMPORT_ITEMS).setMaxGlobalLimited(1) + .and(Predicates.abilities(PartAbility.IMPORT_ITEMS).withMaxGlobalLimited(1) .setPreviewCount(1)) - .and(Predicates.abilities(PartAbility.MUFFLER).setExactLimit(1)); + .and(Predicates.abilities(PartAbility.MUFFLER).withExactLimit(1)); if (ConfigHolder.INSTANCE.machines.enableMaintenance) { - fireboxPred = fireboxPred.and(Predicates.abilities(PartAbility.MAINTENANCE).setExactLimit(1)); + fireboxPred = fireboxPred.and(Predicates.abilities(PartAbility.MAINTENANCE).withExactLimit(1)); } return MultiblockPatternBuilder.start(FRONT, UP, RIGHT) @@ -622,8 +622,8 @@ public static MultiblockMachineDefinition registerLargeBoiler(GTRegistrate regis .where('S', Predicates.controller(blocks(definition.getBlock()))) .where('P', blocks(pipe.get())) .where('X', fireboxPred) - .where('C', blocks(casing.get()).setMinGlobalLimited(20) - .and(Predicates.abilities(PartAbility.EXPORT_FLUIDS).setMinGlobalLimited(1) + .where('C', blocks(casing.get()).withMinGlobalLimited(20) + .and(Predicates.abilities(PartAbility.EXPORT_FLUIDS).withMinGlobalLimited(1) .setPreviewCount(1))) .build(); }) @@ -665,7 +665,7 @@ public static MultiblockMachineDefinition registerLargeCombustionEngine(GTRegist .slice("AAA", "AYA", "AAA") .where('X', blocks(casing.get())) .where('G', blocks(gear.get())) - .where('C', blocks(casing.get()).setMinGlobalLimited(3) + .where('C', blocks(casing.get()).withMinGlobalLimited(3) .and(autoAbilities(definition.getRecipeTypes(), false, false, true, true, true, true)) .and(autoAbilities(true, true, false))) .where('D', @@ -727,8 +727,8 @@ public static MultiblockMachineDefinition registerLargeTurbine(GTRegistrate regi .where('S', controller(blocks(definition.getBlock()))) .where('G', blocks(gear.get())) .where('C', blocks(casing.get())) - .where('R', rotorHolder(tier).setExactLimit(1) - .and(abilities(PartAbility.OUTPUT_ENERGY).setExactLimit(1))) + .where('R', rotorHolder(tier).withExactLimit(1) + .and(abilities(PartAbility.OUTPUT_ENERGY).withExactLimit(1))) .where('H', blocks(casing.get()) .and(autoAbilities(definition.getRecipeTypes(), false, false, true, true, true, true)) .and(autoAbilities(true, needsMuffler, false))) diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java index 90d6430d4ba..cd4d0e24cf4 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java @@ -103,7 +103,7 @@ public class GTMultiMachines { .slice("XXX", "X#X", "XXX") .slice("XXX", "XYX", "XXX") .where('X', - blocks(CASING_COKE_BRICKS.get()).and(blocks(COKE_OVEN_HATCH.get()).setMaxGlobalLimited(5))) + blocks(CASING_COKE_BRICKS.get()).and(blocks(COKE_OVEN_HATCH.get()).withMaxGlobalLimited(5))) .where('#', Predicates.air()) .where('Y', Predicates.controller(blocks(definition.getBlock()))) .build()) @@ -149,7 +149,7 @@ public class GTMultiMachines { .slice("XXX", "C#C", "C#C", "XMX") .slice("XSX", "CCC", "CCC", "XXX") .where('S', controller(blocks(definition.getBlock()))) - .where('X', blocks(CASING_INVAR_HEATPROOF.get()).setMinGlobalLimited(9) + .where('X', blocks(CASING_INVAR_HEATPROOF.get()).withMinGlobalLimited(9) .and(autoAbilities(definition.getRecipeTypes())) .and(autoAbilities(true, false, false))) .where('M', abilities(PartAbility.MUFFLER)) @@ -193,7 +193,7 @@ public class GTMultiMachines { .recipeModifiers(DEFAULT_ENVIRONMENT_REQUIREMENT, OC_PERFECT_SUBTICK, BATCH_MODE) .appearanceBlock(CASING_PTFE_INERT) .pattern(definition -> { - var casing = blocks(CASING_PTFE_INERT.get()).setMinGlobalLimited(10); + var casing = blocks(CASING_PTFE_INERT.get()).withMinGlobalLimited(10); var abilities = Predicates.autoAbilities(definition.getRecipeTypes()) .and(Predicates.autoAbilities(true, false, false)); return MultiblockPatternBuilder.start(FRONT, UP, RIGHT) @@ -203,7 +203,7 @@ public class GTMultiMachines { .where('S', Predicates.controller(blocks(definition.getBlock()))) .where('X', casing.and(abilities)) .where('P', blocks(CASING_POLYTETRAFLUOROETHYLENE_PIPE.get())) - .where('C', Predicates.heatingCoils().setExactLimit(1) + .where('C', Predicates.heatingCoils().withExactLimit(1) .and(abilities) .and(casing)) .build(); @@ -223,7 +223,7 @@ public class GTMultiMachines { .slice("XXX", "X#X", "XXX") .slice("XXX", "XSX", "XXX") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_STEEL_SOLID.get()).setMinGlobalLimited(14) + .where('X', blocks(CASING_STEEL_SOLID.get()).withMinGlobalLimited(14) .and(Predicates.autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, true, false))) .where('#', Predicates.air()) @@ -245,7 +245,7 @@ public class GTMultiMachines { .slice("XXX", "XSX", "XXX") .where('S', Predicates.controller(blocks(definition.get()))) .where('X', - blocks(MACHINE_CASING_ULV.get()).setMinGlobalLimited(6) + blocks(MACHINE_CASING_ULV.get()).withMinGlobalLimited(6) .and(Predicates.autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, true, false))) .where('C', Predicates.heatingCoils()) @@ -282,7 +282,7 @@ public class GTMultiMachines { .slice("XXX", "C#C", "XMX") .slice("XSX", "CCC", "XXX") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(CASING_INVAR_HEATPROOF.get()).setMinGlobalLimited(9) + .where('X', blocks(CASING_INVAR_HEATPROOF.get()).withMinGlobalLimited(9) .and(autoAbilities(definition.getRecipeTypes())) .and(autoAbilities(true, false, false))) .where('M', abilities(PartAbility.MUFFLER)) @@ -325,7 +325,7 @@ public class GTMultiMachines { .slice("HCHCH", "H###H", "HCHCH") .slice("HCHCH", "HCOCH", "HCHCH") .where('O', Predicates.controller(blocks(definition.get()))) - .where('H', blocks(CASING_STAINLESS_CLEAN.get()).setMinGlobalLimited(12) + .where('H', blocks(CASING_STAINLESS_CLEAN.get()).withMinGlobalLimited(12) .and(Predicates.autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, true, false))) .where('#', Predicates.air()) @@ -360,9 +360,9 @@ public class GTMultiMachines { if (GTCEu.Mods.isAE2Loaded()) { exportPredicate = exportPredicate.xor(blocks(GTAEMachines.FLUID_EXPORT_HATCH_ME.get())); } - exportPredicate = exportPredicate.setMaxLayerLimited(1); + exportPredicate = exportPredicate.withMaxLayerLimited(1); MultiPredicate maint = autoAbilities(true, false, false) - .setMaxGlobalLimited(1); + .withMaxGlobalLimited(1); return MultiblockPatternBuilder.start(UP, BACK, RIGHT) .slice("YSY", "YYY", "YYY") .slice("ZZZ", "Z#Z", "ZZZ") @@ -370,10 +370,10 @@ public class GTMultiMachines { .slice("XXX", "XXX", "XXX") .where('S', Predicates.controller(blocks(definition.getBlock()))) .where('Y', blocks(CASING_STAINLESS_CLEAN.get()) - .and(Predicates.abilities(PartAbility.EXPORT_ITEMS).setMaxGlobalLimited(1)) - .and(Predicates.abilities(PartAbility.INPUT_ENERGY).setMinGlobalLimited(1) - .setMaxGlobalLimited(2)) - .and(Predicates.abilities(PartAbility.IMPORT_FLUIDS).setExactLimit(1)) + .and(Predicates.abilities(PartAbility.EXPORT_ITEMS).withMaxGlobalLimited(1)) + .and(Predicates.abilities(PartAbility.INPUT_ENERGY).withMinGlobalLimited(1) + .withMaxGlobalLimited(2)) + .and(Predicates.abilities(PartAbility.IMPORT_FLUIDS).withExactLimit(1)) .and(maint)) .where('Z', blocks(CASING_STAINLESS_CLEAN.get()) .and(exportPredicate) @@ -399,7 +399,7 @@ public class GTMultiMachines { .slice("XXX", "X#X", "XXX") .slice("XXX", "XSX", "XXX") .where('S', Predicates.controller(blocks(definition.getBlock()))) - .where('X', blocks(CASING_ALUMINIUM_FROSTPROOF.get()).setMinGlobalLimited(14) + .where('X', blocks(CASING_ALUMINIUM_FROSTPROOF.get()).withMinGlobalLimited(14) .and(Predicates.autoAbilities(definition.getRecipeTypes())) .and(Predicates.autoAbilities(true, false, false))) .where('#', Predicates.air()) @@ -424,13 +424,13 @@ public class GTMultiMachines { .and(!ConfigHolder.INSTANCE.machines.orderedAssemblyLineFluids ? Predicates.abilities(PartAbility.IMPORT_FLUIDS_1X, PartAbility.IMPORT_FLUIDS_4X, PartAbility.IMPORT_FLUIDS_9X) : - Predicates.abilities(PartAbility.IMPORT_FLUIDS_1X).setMaxGlobalLimited(4))) + Predicates.abilities(PartAbility.IMPORT_FLUIDS_1X).withMaxGlobalLimited(4))) .where('O', Predicates.abilities(PartAbility.EXPORT_ITEMS) .addTooltips(Component.translatable("gtceu.multiblock.pattern.location_end"))) .where('Y', blocks(CASING_STEEL_SOLID.get()).and(Predicates.abilities(PartAbility.INPUT_ENERGY) - .setMinGlobalLimited(1).setMaxGlobalLimited(2))) + .withMinGlobalLimited(1).withMaxGlobalLimited(2))) .where('I', blocks(ITEM_IMPORT_BUS[0].getBlock())) .where('G', blocks(CASING_GRATE.get())) .where('A', blocks(CASING_ASSEMBLY_CONTROL.get())) @@ -496,10 +496,10 @@ public class GTMultiMachines { .slice("XXX", "XSX", "XXX") .where('S', Predicates.controller(blocks(definition.getBlock()))) .where('#', Predicates.air()) - .where('X', blocks(CASING_BRONZE_BRICKS.get()).setMinGlobalLimited(14) + .where('X', blocks(CASING_BRONZE_BRICKS.get()).withMinGlobalLimited(14) .and(Predicates.abilities(PartAbility.STEAM_IMPORT_ITEMS).setPreviewCount(1)) .and(Predicates.abilities(PartAbility.STEAM_EXPORT_ITEMS).setPreviewCount(1)) - .and(Predicates.abilities(PartAbility.STEAM).setExactLimit(1))) + .and(Predicates.abilities(PartAbility.STEAM).withExactLimit(1))) .build()) .workableCasingModel(GTCEu.id("block/casings/solid/machine_casing_bronze_plated_bricks"), GTCEu.id("block/multiblock/steam_grinder")) @@ -521,11 +521,11 @@ public class GTMultiMachines { .where('S', Predicates.controller(blocks(definition.getBlock()))) .where('#', Predicates.air()) .where(' ', Predicates.any()) - .where('X', blocks(CASING_BRONZE_BRICKS.get()).setMinGlobalLimited(6) + .where('X', blocks(CASING_BRONZE_BRICKS.get()).withMinGlobalLimited(6) .and(Predicates.abilities(PartAbility.STEAM_IMPORT_ITEMS).setPreviewCount(1)) .and(Predicates.abilities(PartAbility.STEAM_EXPORT_ITEMS).setPreviewCount(1))) .where('F', blocks(FIREBOX_BRONZE.get()) - .and(Predicates.abilities(PartAbility.STEAM).setExactLimit(1))) + .and(Predicates.abilities(PartAbility.STEAM).withExactLimit(1))) .build()) .modelProperty(GTMachineModelProperties.RECIPE_LOGIC_STATUS, RecipeLogic.Status.IDLE) .model(createWorkableCasingMachineModel(GTCEu.id("block/casings/solid/machine_casing_bronze_plated_bricks"), @@ -572,12 +572,12 @@ public class GTMultiMachines { .where('G', blocks(FUSION_GLASS.get()).or(casing)) .where('E', casing.and( blocks(PartAbility.INPUT_ENERGY.getBlockRange(tier, UV).toArray(Block[]::new)) - .setMinGlobalLimited(1).setPreviewCount(16))) + .withMinGlobalLimited(1).setPreviewCount(16))) .where('C', casing) .where('K', blocks(FusionReactorMachine.getCoilState(tier))) .where('O', casing.or(abilities(PartAbility.EXPORT_FLUIDS))) .where('A', air()) - .where('I', casing.and(abilities(PartAbility.IMPORT_FLUIDS).setMinGlobalLimited(2))) + .where('I', casing.and(abilities(PartAbility.IMPORT_FLUIDS).withMinGlobalLimited(2))) .where('#', any()) .build(); }) @@ -609,10 +609,10 @@ public class GTMultiMachines { .slice("XXX", "FCF", "FCF", "FCF", "#F#", "#F#", "#F#") .slice("XSX", "#F#", "#F#", "#F#", "###", "###", "###") .where('S', controller(blocks(definition.get()))) - .where('X', blocks(FluidDrillMachine.getCasingState(tier)).setMinGlobalLimited(3) - .and(abilities(PartAbility.INPUT_ENERGY).setMinGlobalLimited(1) - .setMaxGlobalLimited(2)) - .and(abilities(PartAbility.EXPORT_FLUIDS).setMaxGlobalLimited(1))) + .where('X', blocks(FluidDrillMachine.getCasingState(tier)).withMinGlobalLimited(3) + .and(abilities(PartAbility.INPUT_ENERGY).withMinGlobalLimited(1) + .withMaxGlobalLimited(2)) + .and(abilities(PartAbility.EXPORT_FLUIDS).withMaxGlobalLimited(1))) .where('C', blocks(FluidDrillMachine.getCasingState(tier))) .where('F', frames(FluidDrillMachine.getFrameMaterial(tier))) .where('#', any()) @@ -635,10 +635,10 @@ public class GTMultiMachines { .slice("XSX", "#F#", "#F#", "#F#", "###", "###", "###") .where('S', controller(blocks(definition.getBlock()))) .where('X', blocks(LargeMinerMachine.getCasingState(tier)) - .and(abilities(PartAbility.EXPORT_ITEMS).setExactLimit(1).setPreviewCount(1)) - .and(abilities(PartAbility.IMPORT_FLUIDS).setExactLimit(1).setPreviewCount(1)) - .and(abilities(PartAbility.INPUT_ENERGY).setMinGlobalLimited(1) - .setMaxGlobalLimited(2).setPreviewCount(1))) + .and(abilities(PartAbility.EXPORT_ITEMS).withExactLimit(1).setPreviewCount(1)) + .and(abilities(PartAbility.IMPORT_FLUIDS).withExactLimit(1).setPreviewCount(1)) + .and(abilities(PartAbility.INPUT_ENERGY).withMinGlobalLimited(1) + .withMaxGlobalLimited(2).setPreviewCount(1))) .where('C', blocks(LargeMinerMachine.getCasingState(tier))) .where('F', frames(LargeMinerMachine.getMaterial(tier))) .where('#', any()) @@ -773,7 +773,7 @@ public class GTMultiMachines { .slice("XXX", "XCX", "XXX") .slice("XXX", "XSX", "XXX") .where('S', controller(blocks(definition.getBlock()))) - .where('X', blocks(GTBlocks.HIGH_POWER_CASING.get()).setMinGlobalLimited(12) + .where('X', blocks(GTBlocks.HIGH_POWER_CASING.get()).withMinGlobalLimited(12) .and(ActiveTransformerMachine.getHatchPredicates())) .where('C', blocks(GTBlocks.SUPERCONDUCTING_COIL.get())) .build()) @@ -808,12 +808,12 @@ public class GTMultiMachines { .where('C', blocks(CASING_PALLADIUM_SUBSTATION.get())) .where('X', blocks(CASING_PALLADIUM_SUBSTATION.get()) - .setMinGlobalLimited(PowerSubstationMachine.MIN_CASINGS) + .withMinGlobalLimited(PowerSubstationMachine.MIN_CASINGS) .and(autoAbilities(true, false, false)) .and(abilities(PartAbility.INPUT_ENERGY, PartAbility.SUBSTATION_INPUT_ENERGY, - PartAbility.INPUT_LASER).setMinGlobalLimited(1)) + PartAbility.INPUT_LASER).withMinGlobalLimited(1)) .and(abilities(PartAbility.OUTPUT_ENERGY, PartAbility.SUBSTATION_OUTPUT_ENERGY, - PartAbility.OUTPUT_LASER).setMinGlobalLimited(1))) + PartAbility.OUTPUT_LASER).withMinGlobalLimited(1))) .where('G', blocks(CASING_LAMINATED_GLASS.get())) .where('B', Predicates.powerSubstationBatteries()) .build()) @@ -860,10 +860,10 @@ public class GTMultiMachines { .slice("XSX", "#F#", "#F#", "#F#", "###", "###", "###") .where('S', controller(blocks(definition.get()))) .where('X', - blocks(BedrockOreMinerMachine.getCasingState(tier)).setMinGlobalLimited(3) - .and(abilities(PartAbility.INPUT_ENERGY).setMinGlobalLimited(1) - .setMaxGlobalLimited(2)) - .and(abilities(PartAbility.EXPORT_ITEMS).setMaxGlobalLimited(1))) + blocks(BedrockOreMinerMachine.getCasingState(tier)).withMinGlobalLimited(3) + .and(abilities(PartAbility.INPUT_ENERGY).withMinGlobalLimited(1) + .withMaxGlobalLimited(2)) + .and(abilities(PartAbility.EXPORT_ITEMS).withMaxGlobalLimited(1))) .where('C', blocks(BedrockOreMinerMachine.getCasingState(tier))) .where('F', frames(BedrockOreMinerMachine.getFrameMaterial(tier))) .where('#', any()) diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTResearchMachines.java b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTResearchMachines.java index eefb8023a7a..5d62e587dd5 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTResearchMachines.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTResearchMachines.java @@ -71,8 +71,8 @@ public class GTResearchMachines { .where('V', blocks(COMPUTER_HEAT_VENT.get())) .where('A', blocks(ADVANCED_COMPUTER_CASING.get())) .where('P', blocks(COMPUTER_CASING.get()) - .and(abilities(PartAbility.INPUT_ENERGY).setMinGlobalLimited(1).setMaxGlobalLimited(2, 1)) - .and(abilities(PartAbility.COMPUTATION_DATA_RECEPTION).setExactLimit(1)) + .and(abilities(PartAbility.INPUT_ENERGY).withMinGlobalLimited(1).withMaxGlobalLimited(2, 1)) + .and(abilities(PartAbility.COMPUTATION_DATA_RECEPTION).withExactLimit(1)) .and(autoAbilities(true, false, false))) .where('H', abilities(PartAbility.OBJECT_HOLDER)) .build()) @@ -109,13 +109,13 @@ public class GTResearchMachines { .slice("XCCCX", "XCSCX", "XCCCX") .where('S', controller(blocks(definition.getBlock()))) .where('X', blocks(COMPUTER_HEAT_VENT.get())) - .where('D', blocks(COMPUTER_CASING.get()).setMinGlobalLimited(3) - .and(abilities(PartAbility.DATA_ACCESS).setPreviewCount(3)) - .and(abilities(PartAbility.OPTICAL_DATA_TRANSMISSION).setMinGlobalLimited(1, 1)) - .and(abilities(PartAbility.OPTICAL_DATA_RECEPTION).setPreviewCount(1))) + .where('D', blocks(COMPUTER_CASING.get()).withMinGlobalLimited(3) + .and(abilities(PartAbility.DATA_ACCESS).withPreviewCount(3)) + .and(abilities(PartAbility.OPTICAL_DATA_TRANSMISSION).withMinGlobalLimited(1, 1)) + .and(abilities(PartAbility.OPTICAL_DATA_RECEPTION).withPreviewCount(1))) .where('A', blocks(COMPUTER_CASING.get())) - .where('C', blocks(HIGH_POWER_CASING.get()).setMinGlobalLimited(4) - .and(abilities(PartAbility.INPUT_ENERGY).setMinGlobalLimited(1).setMaxGlobalLimited(2, 1)) + .where('C', blocks(HIGH_POWER_CASING.get()).withMinGlobalLimited(4) + .and(abilities(PartAbility.INPUT_ENERGY).withMinGlobalLimited(1).withMaxGlobalLimited(2, 1)) .and(autoAbilities(true, false, false))) .build()) .workableCasingModel(GTCEu.id("block/casings/hpca/high_power_casing"), @@ -139,10 +139,10 @@ public class GTResearchMachines { .slice("XXX", "XSX", "XXX") .where('S', controller(blocks(definition.getBlock()))) .where('A', blocks(ADVANCED_COMPUTER_CASING.get())) - .where('X', blocks(COMPUTER_CASING.get()).setMinGlobalLimited(7) - .and(abilities(PartAbility.INPUT_ENERGY).setMinGlobalLimited(1).setMaxGlobalLimited(2, 1)) - .and(abilities(PartAbility.COMPUTATION_DATA_TRANSMISSION).setMinGlobalLimited(1, 1)) - .and(abilities(PartAbility.COMPUTATION_DATA_RECEPTION).setMinGlobalLimited(1, 2)) + .where('X', blocks(COMPUTER_CASING.get()).withMinGlobalLimited(7) + .and(abilities(PartAbility.INPUT_ENERGY).withMinGlobalLimited(1).withMaxGlobalLimited(2, 1)) + .and(abilities(PartAbility.COMPUTATION_DATA_TRANSMISSION).withMinGlobalLimited(1, 1)) + .and(abilities(PartAbility.COMPUTATION_DATA_RECEPTION).withMinGlobalLimited(1, 2)) .and(autoAbilities(true, false, false))) .build()) .sidedWorkableCasingModel(GTCEu.id("block/casings/hpca/computer_casing"), @@ -168,10 +168,10 @@ public class GTResearchMachines { .where('A', blocks(ADVANCED_COMPUTER_CASING.get())) .where('V', blocks(COMPUTER_HEAT_VENT.get())) .where('X', abilities(PartAbility.HPCA_COMPONENT)) - .where('C', blocks(COMPUTER_CASING.get()).setMinGlobalLimited(5) - .and(abilities(PartAbility.INPUT_ENERGY).setMinGlobalLimited(1).setMaxGlobalLimited(2, 1)) - .and(abilities(PartAbility.IMPORT_FLUIDS).setMaxGlobalLimited(1)) - .and(abilities(PartAbility.COMPUTATION_DATA_TRANSMISSION).setExactLimit(1)) + .where('C', blocks(COMPUTER_CASING.get()).withMinGlobalLimited(5) + .and(abilities(PartAbility.INPUT_ENERGY).withMinGlobalLimited(1).withMaxGlobalLimited(2, 1)) + .and(abilities(PartAbility.IMPORT_FLUIDS).withMaxGlobalLimited(1)) + .and(abilities(PartAbility.COMPUTATION_DATA_TRANSMISSION).withExactLimit(1)) .and(autoAbilities(true, false, false))) .build()) .sidedWorkableCasingModel(GTCEu.id("block/casings/hpca/computer_casing"), diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java index 03ce28e1fce..0205691f8bf 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java @@ -75,12 +75,12 @@ public static MultiPredicate getMultiPredicate() { if (MULTI_PREDICATE == null) { MULTI_PREDICATE = Predicates.machines(GTMachines.MONITOR) .and(Predicates.abilities(PartAbility.INPUT_ENERGY) - .setMinGlobalLimited(1).setMaxGlobalLimited(2).setPreviewCount(1)) - .and(Predicates.abilities(PartAbility.DATA_ACCESS).setPreviewCount(1) - .or(Predicates.machines(GTMachines.BATTERY_BUFFER_4).setPreviewCount(0)) - .or(Predicates.machines(GTMachines.BATTERY_BUFFER_16).setPreviewCount(0)) - .setMaxGlobalLimited(4)) - .and(Predicates.machines(GTMachines.HULL).setPreviewCount(0)) + .withGlobalMinMax(1, 2).withPreviewCount(1)) + .and(Predicates.abilities(PartAbility.DATA_ACCESS).withPreviewCount(1) + .or(Predicates.machines(GTMachines.BATTERY_BUFFER_4).withPreviewCount(0)) + .or(Predicates.machines(GTMachines.BATTERY_BUFFER_16)) + .withMaxGlobalLimited(4, 0)) + .and(Predicates.machines(GTMachines.HULL).withPreviewCount(0)) .and(Predicates.machines(GTMachines.ADVANCED_MONITOR)) .and(Predicates.blocks(GTBlocks.CASING_ALUMINIUM_FROSTPROOF.get())); } diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CleanroomMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CleanroomMachine.java index f2fc44eaf50..1b0d5b8360d 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CleanroomMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CleanroomMachine.java @@ -316,12 +316,12 @@ public static Function getPattern() return (definition) -> { MultiPredicate wallPredicate = states(getCasingState(), getGlassState()).or(getValidFloorBlocks()); MultiPredicate energyPredicate = autoAbilities(true, false, false).and(abilities(PartAbility.INPUT_ENERGY) - .setMinGlobalLimited(1).setMaxGlobalLimited(3)); + .withMinGlobalLimited(1).withMaxGlobalLimited(3)); MultiPredicate edgePredicate = wallPredicate.and(energyPredicate); MultiPredicate facePredicate = wallPredicate.and(energyPredicate) - .and(doorPredicate().setMaxGlobalLimited(8)) - .and(abilities(PartAbility.PASSTHROUGH_HATCH).setMaxGlobalLimited(30)); + .and(doorPredicate().withMaxGlobalLimited(8)) + .and(abilities(PartAbility.PASSTHROUGH_HATCH).withMaxGlobalLimited(30)); MultiPredicate filterPredicate = cleanroomFilters(); MultiPredicate innerPredicate = innerPredicate(); MultiPredicate verticalEdgePredicate = edgePredicate.and(blocks(getGlassState().getBlock())); diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/testmultis/PCBFactoryMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/testmultis/PCBFactoryMachine.java index c4ec9877989..84208ed3374 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/testmultis/PCBFactoryMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/testmultis/PCBFactoryMachine.java @@ -27,7 +27,7 @@ public IBlockPattern getDefaultStructurePattern() { .where('C', /* * Predicates.autoAbilities(true, false, false) * .or( - */Predicates.blocks(CASING_GRATE.get()).setMinGlobalLimited(12)) + */Predicates.blocks(CASING_GRATE.get()).withMinGlobalLimited(12)) .where('S', Predicates.controller(Predicates.blocks(getDefinition().getBlock()))) .where('B', Predicates.frames(GTMaterials.Steel)) .build(); From 14762aa13a44bccc89bf563b43c99696f73f6314 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Thu, 27 Aug 2026 19:28:54 -0700 Subject: [PATCH 09/63] attempt to retain settings when combining `MultiPredicate`s --- .../gtceu/api/multiblock/MultiPredicate.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index aefb98250f5..edd8153ee7a 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -277,6 +277,14 @@ protected PredicateSettings getOrCreateSettings() { return this.settings; } + /// Sets the setting of this multipredicate to its children, then makes {@code this.settings} null + public void applySettingsToChildren() { + if (this.settings == null || isSingle()) return; + forEach(p -> p.setSettings(this.settings)); + forEachChild(mp -> mp.setSettings(this.settings)); + this.settings = null; + } + @RemapForJS("addTooltip") @Contract(mutates = "this") public MultiPredicate addTooltips(Component tooltip) { @@ -548,6 +556,10 @@ public static MultiPredicate xor(List predicates) { private static MultiPredicate combine(MultiPredicate a, Logic type, @Nullable MultiPredicate b) { if (b == null || b.isEmpty()) return a; // no op if (a.isEmpty()) return b; + if (!Objects.equals(a.settings, b.settings)) { + a.applySettingsToChildren(); + b.applySettingsToChildren(); + } List predicates = new ArrayList<>(); List children = new ArrayList<>(); appendPredicates(type, a, predicates, children); From 393bb9232d841ce09b0540c85927d6d7bdaa8626 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Thu, 27 Aug 2026 19:37:16 -0700 Subject: [PATCH 10/63] organize BasePredicate methods --- .../multiblock/predicates/BasePredicate.java | 100 +++++++++++++++--- .../predicates/TestablePredicate.java | 4 +- 2 files changed, 89 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index 4a0eb55324c..4f20cf5c541 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -53,17 +53,6 @@ public MultiPredicate getParent() { /// @return a list of components to be displayed while hovering over a block in the Multiblock Preview public abstract List getRecipeViewerTooltips(MultiPredicate root); - public abstract BasePredicate copy(); - - protected void copyTo(BasePredicate other) { - other.settings = this.settings.copy(); - other.additionalTooltips.addAll(this.additionalTooltips); - } - - public void addTooltips(Component tooltip) { - this.additionalTooltips.add(tooltip); - } - /// delegates to {@link MultiPredicate#testMaxCount(BasePredicate, PredicateContext)}, /// with this predicate as the passing predicate public boolean checkMaxCount(PredicateContext context) { @@ -141,6 +130,15 @@ public int compareTo(BasePredicate o) { return this.settings.comparePriority(o.settings); } + // COPY AND MUTATE + + public abstract BasePredicate copy(); + + protected void copyTo(BasePredicate other) { + other.setSettings(this.settings.copy()); + other.additionalTooltips.addAll(this.additionalTooltips); + } + @Override public BasePredicate withSettings(UnaryOperator configurator) { BasePredicate copy = copy(); @@ -148,12 +146,88 @@ public BasePredicate withSettings(UnaryOperator configurator) return copy; } - public void updateSettings(UnaryOperator configurator) { - setSettings(configurator.apply(getSettings())); + @Override + public BasePredicate withDisableRenderFormed(boolean disableRenderFormed) { + return withSettings(s -> s.withDisableRenderFormed(disableRenderFormed)); + } + + @Override + public BasePredicate withPreviewCount(int previewCount) { + return withSettings(s -> s.withPreviewCount(previewCount)); + } + + @Override + public BasePredicate withMaxSliceCount(int maxSliceCount) { + return withSettings(s -> s.withMaxSliceCount(maxSliceCount)); + } + + @Override + public BasePredicate withMinSliceCount(int minSliceCount) { + return withSettings(s -> s.withMinSliceCount(minSliceCount)); + } + + @Override + public BasePredicate withMaxCount(int maxCount) { + return withSettings(s -> s.withMaxCount(maxCount)); + } + + @Override + public BasePredicate withMinCount(int minCount) { + return withSettings(s -> s.withMinCount(minCount)); + } + + @Override + public BasePredicate withPriority(int priority) { + return withSettings(s -> s.withPriority(priority)); } @Override public void setSettings(PredicateSettings settings) { this.settings = settings; } + + // MUTATE ONLY + + public void addTooltips(Component tooltip) { + this.additionalTooltips.add(tooltip); + } + + public void updateSettings(UnaryOperator configurator) { + setSettings(configurator.apply(getSettings())); + } + + @Override + public void setDisableRenderFormed(boolean disableRenderFormed) { + updateSettings(s -> s.withDisableRenderFormed(disableRenderFormed)); + } + + @Override + public void setPreviewCount(int previewCount) { + updateSettings(s -> s.withPreviewCount(previewCount)); + } + + @Override + public void setMaxSliceCount(int maxSliceCount) { + updateSettings(s -> s.withMaxSliceCount(maxSliceCount)); + } + + @Override + public void setMinSliceCount(int minSliceCount) { + updateSettings(s -> s.withMinSliceCount(minSliceCount)); + } + + @Override + public void setMaxCount(int maxCount) { + updateSettings(s -> s.withMaxCount(maxCount)); + } + + @Override + public void setMinCount(int minCount) { + updateSettings(s -> s.withMinCount(minCount)); + } + + @Override + public void setPriority(int priority) { + updateSettings(s -> s.withPriority(priority)); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java index 2546dce6f20..407760c17fe 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java @@ -60,8 +60,8 @@ class TestablePredicate extends BasePredicate { @Override public List getRecipeViewerTooltips(MultiPredicate root) { List tooltips = new ArrayList<>(root.getAdditionalTooltips()); - int minCount = getSettings().minCount(); - int maxCount = getSettings().maxCount(); + int minCount = getMinCount(); + int maxCount = getMaxCount(); if (minCount == maxCount && maxCount != -1) { tooltips.add(Component.translatable("gtceu.multiblock.pattern.exact_count", minCount)); } else if (minCount != maxCount && minCount != -1 && maxCount != -1) { From ca0e8b90c4604100c53c94b8b951d13beec6dc8b Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Thu, 27 Aug 2026 19:50:37 -0700 Subject: [PATCH 11/63] update method usages --- .../common/data/machines/GTMachineUtils.java | 12 ++++++------ .../common/data/machines/GTMultiMachines.java | 17 ++++++++--------- .../electric/ActiveTransformerMachine.java | 12 ++++++------ 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMachineUtils.java b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMachineUtils.java index c166b7e34f0..97b19c0dc01 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMachineUtils.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMachineUtils.java @@ -605,10 +605,10 @@ public static MultiblockMachineDefinition registerLargeBoiler(GTRegistrate regis fireBox.get().defaultBlockState() : casing.get().defaultBlockState()) .pattern((definition) -> { MultiPredicate fireboxPred = blocks(ALL_FIREBOXES.get(firebox).get()).withMinGlobalLimited(3) - .and(Predicates.abilities(PartAbility.IMPORT_FLUIDS).withMinGlobalLimited(1) - .setPreviewCount(1)) - .and(Predicates.abilities(PartAbility.IMPORT_ITEMS).withMaxGlobalLimited(1) - .setPreviewCount(1)) + .and(Predicates.abilities(PartAbility.IMPORT_FLUIDS) + .withMinGlobalLimited(1, 1)) + .and(Predicates.abilities(PartAbility.IMPORT_ITEMS) + .withMaxGlobalLimited(1, 1)) .and(Predicates.abilities(PartAbility.MUFFLER).withExactLimit(1)); if (ConfigHolder.INSTANCE.machines.enableMaintenance) { @@ -623,8 +623,8 @@ public static MultiblockMachineDefinition registerLargeBoiler(GTRegistrate regis .where('P', blocks(pipe.get())) .where('X', fireboxPred) .where('C', blocks(casing.get()).withMinGlobalLimited(20) - .and(Predicates.abilities(PartAbility.EXPORT_FLUIDS).withMinGlobalLimited(1) - .setPreviewCount(1))) + .and(Predicates.abilities(PartAbility.EXPORT_FLUIDS) + .withMinGlobalLimited(1, 1))) .build(); }) .recoveryItems( diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java index cd4d0e24cf4..d56cc53d798 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java @@ -497,8 +497,8 @@ public class GTMultiMachines { .where('S', Predicates.controller(blocks(definition.getBlock()))) .where('#', Predicates.air()) .where('X', blocks(CASING_BRONZE_BRICKS.get()).withMinGlobalLimited(14) - .and(Predicates.abilities(PartAbility.STEAM_IMPORT_ITEMS).setPreviewCount(1)) - .and(Predicates.abilities(PartAbility.STEAM_EXPORT_ITEMS).setPreviewCount(1)) + .and(Predicates.abilities(PartAbility.STEAM_IMPORT_ITEMS).withPreviewCount(1)) + .and(Predicates.abilities(PartAbility.STEAM_EXPORT_ITEMS).withPreviewCount(1)) .and(Predicates.abilities(PartAbility.STEAM).withExactLimit(1))) .build()) .workableCasingModel(GTCEu.id("block/casings/solid/machine_casing_bronze_plated_bricks"), @@ -522,8 +522,8 @@ public class GTMultiMachines { .where('#', Predicates.air()) .where(' ', Predicates.any()) .where('X', blocks(CASING_BRONZE_BRICKS.get()).withMinGlobalLimited(6) - .and(Predicates.abilities(PartAbility.STEAM_IMPORT_ITEMS).setPreviewCount(1)) - .and(Predicates.abilities(PartAbility.STEAM_EXPORT_ITEMS).setPreviewCount(1))) + .and(Predicates.abilities(PartAbility.STEAM_IMPORT_ITEMS).withPreviewCount(1)) + .and(Predicates.abilities(PartAbility.STEAM_EXPORT_ITEMS).withPreviewCount(1))) .where('F', blocks(FIREBOX_BRONZE.get()) .and(Predicates.abilities(PartAbility.STEAM).withExactLimit(1))) .build()) @@ -572,7 +572,7 @@ public class GTMultiMachines { .where('G', blocks(FUSION_GLASS.get()).or(casing)) .where('E', casing.and( blocks(PartAbility.INPUT_ENERGY.getBlockRange(tier, UV).toArray(Block[]::new)) - .withMinGlobalLimited(1).setPreviewCount(16))) + .withMinGlobalLimited(1, 16))) .where('C', casing) .where('K', blocks(FusionReactorMachine.getCoilState(tier))) .where('O', casing.or(abilities(PartAbility.EXPORT_FLUIDS))) @@ -635,10 +635,9 @@ public class GTMultiMachines { .slice("XSX", "#F#", "#F#", "#F#", "###", "###", "###") .where('S', controller(blocks(definition.getBlock()))) .where('X', blocks(LargeMinerMachine.getCasingState(tier)) - .and(abilities(PartAbility.EXPORT_ITEMS).withExactLimit(1).setPreviewCount(1)) - .and(abilities(PartAbility.IMPORT_FLUIDS).withExactLimit(1).setPreviewCount(1)) - .and(abilities(PartAbility.INPUT_ENERGY).withMinGlobalLimited(1) - .withMaxGlobalLimited(2).setPreviewCount(1))) + .and(abilities(PartAbility.EXPORT_ITEMS).withExactLimit(1).withPreviewCount(1)) + .and(abilities(PartAbility.IMPORT_FLUIDS).withExactLimit(1).withPreviewCount(1)) + .and(abilities(PartAbility.INPUT_ENERGY).withGlobalMinMax(1, 2).withPreviewCount(1))) .where('C', blocks(LargeMinerMachine.getCasingState(tier))) .where('F', frames(LargeMinerMachine.getMaterial(tier))) .where('#', any()) diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/ActiveTransformerMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/ActiveTransformerMachine.java index 3391715c93b..fecb6149b96 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/ActiveTransformerMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/ActiveTransformerMachine.java @@ -158,12 +158,12 @@ public void invalidateStructure(String name) { } public static MultiPredicate getHatchPredicates() { - return abilities(PartAbility.INPUT_ENERGY).setPreviewCount(1) - .or(abilities(PartAbility.OUTPUT_ENERGY).setPreviewCount(2)) - .or(abilities(PartAbility.SUBSTATION_INPUT_ENERGY).setPreviewCount(1)) - .or(abilities(PartAbility.SUBSTATION_OUTPUT_ENERGY).setPreviewCount(1)) - .or(abilities(PartAbility.INPUT_LASER).setPreviewCount(1)) - .or(abilities(PartAbility.OUTPUT_LASER).setPreviewCount(1)); + return abilities(PartAbility.INPUT_ENERGY).withPreviewCount(1) + .or(abilities(PartAbility.OUTPUT_ENERGY).withPreviewCount(2)) + .or(abilities(PartAbility.SUBSTATION_INPUT_ENERGY).withPreviewCount(1)) + .or(abilities(PartAbility.SUBSTATION_OUTPUT_ENERGY).withPreviewCount(1)) + .or(abilities(PartAbility.INPUT_LASER).withPreviewCount(1)) + .or(abilities(PartAbility.OUTPUT_LASER).withPreviewCount(1)); } public List getWidgetsForDisplay(PanelSyncManager syncManager) { From defb7d3d739882e0a3c44edec1a775e258a37407 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Thu, 27 Aug 2026 20:51:30 -0700 Subject: [PATCH 12/63] spotless --- .../gtceu/api/multiblock/MultiPredicate.java | 29 ++++++++++--------- .../gtceu/api/multiblock/Predicates.java | 6 ++-- .../pattern/MultiblockPatternBuilder.java | 1 - .../predicates/PredicateSettings.java | 3 -- .../multiblock/predicates/SettingsHolder.java | 1 - .../common/data/machines/GTMultiMachines.java | 3 +- 6 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index edd8153ee7a..681d37d5038 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -20,7 +20,6 @@ import java.util.*; import java.util.function.Consumer; import java.util.function.UnaryOperator; -import java.util.stream.Stream; public abstract class MultiPredicate implements SettingsHolder { @@ -43,19 +42,21 @@ public abstract class MultiPredicate implements SettingsHolder { private boolean controller; /* - I was curious on your opinion on how recursiveness of setting mins on multipredicates should be done, if at all - - a.setmin(4).and(b.setmin(4) -> at least 4 of a and 4 of b - - a.and(b).setmin(4) -> at least 4 of a and b combined - - should we allow something like - a.and(b).setmin(4,true) or .setminRecursive(4) -> at least 4 of a and 4 of b - - for the case where the user doesn't have access to the original a and b predicates? - how do we see this "recursive" idea for the difference between children and predicates? and what about when you have multiple nested children? - or should we just not bother? - I'm leaning to not bother, just make the predicates yourself and edit them acordingly, but wanna discuss it at least + * I was curious on your opinion on how recursiveness of setting mins on multipredicates should be done, if at all + * + * a.setmin(4).and(b.setmin(4) -> at least 4 of a and 4 of b + * + * a.and(b).setmin(4) -> at least 4 of a and b combined + * + * should we allow something like + * a.and(b).setmin(4,true) or .setminRecursive(4) -> at least 4 of a and 4 of b + * + * for the case where the user doesn't have access to the original a and b predicates? + * how do we see this "recursive" idea for the difference between children and predicates? and what about when you + * have multiple nested children? + * or should we just not bother? + * I'm leaning to not bother, just make the predicates yourself and edit them acordingly, but wanna discuss it at + * least */ @Nullable @Getter diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/Predicates.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/Predicates.java index 6b5306bfbc0..f93dd0eb2fa 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/Predicates.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/Predicates.java @@ -269,9 +269,9 @@ public static MultiPredicate autoAbilities(GTRecipeType[] recipeType, for (var type : recipeType) { if (type.getMaxOutputs(EURecipeCapability.CAP) > 0) { predicate = predicate.and(abilities(PartAbility.OUTPUT_ENERGY) - .withGlobalMinMax(1, 2) - .withPreviewCount(1) - .withPriority(1)); + .withGlobalMinMax(1, 2) + .withPreviewCount(1) + .withPriority(1)); break; } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/MultiblockPatternBuilder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/MultiblockPatternBuilder.java index 916e33bdf29..ee6afd34d6d 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/MultiblockPatternBuilder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/MultiblockPatternBuilder.java @@ -3,7 +3,6 @@ import com.gregtechceu.gtceu.api.multiblock.MultiPredicate; import com.gregtechceu.gtceu.api.multiblock.OriginOffset; import com.gregtechceu.gtceu.api.multiblock.Predicates; -import com.gregtechceu.gtceu.api.multiblock.predicates.PredicateSettings; import com.gregtechceu.gtceu.api.multiblock.util.RelativeDirection; import com.google.common.base.Joiner; diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java index f905b7b8ba8..e804e79d578 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java @@ -1,7 +1,5 @@ package com.gregtechceu.gtceu.api.multiblock.predicates; -import com.gregtechceu.gtceu.api.multiblock.PredicateContext; - import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import lombok.With; @@ -24,7 +22,6 @@ public record PredicateSettings(int priority, ).apply(instance, PredicateSettings::new)); // spotless:on - // helper for xor public boolean isNoneValid() { return minCount <= 0 && minSliceCount <= 0; diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index 8ad7ed1fa6c..8f742ec23cc 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -52,7 +52,6 @@ default boolean isRenderFormedDisabled() { void setDisableRenderFormed(boolean disableRenderFormed); - // copy and mutate /// @return a copy with these settings applied SettingsHolder withSettings(UnaryOperator configurator); diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java index d56cc53d798..c2614da41d9 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java @@ -637,7 +637,8 @@ public class GTMultiMachines { .where('X', blocks(LargeMinerMachine.getCasingState(tier)) .and(abilities(PartAbility.EXPORT_ITEMS).withExactLimit(1).withPreviewCount(1)) .and(abilities(PartAbility.IMPORT_FLUIDS).withExactLimit(1).withPreviewCount(1)) - .and(abilities(PartAbility.INPUT_ENERGY).withGlobalMinMax(1, 2).withPreviewCount(1))) + .and(abilities(PartAbility.INPUT_ENERGY).withGlobalMinMax(1, 2) + .withPreviewCount(1))) .where('C', blocks(LargeMinerMachine.getCasingState(tier))) .where('F', frames(LargeMinerMachine.getMaterial(tier))) .where('#', any()) From b06806c40f8293819b2c37e268d72a1b3d765687 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Thu, 27 Aug 2026 20:56:51 -0700 Subject: [PATCH 13/63] revert these methods back --- .../gtceu/api/multiblock/util/AbstractStructureHelper.java | 4 ++-- .../gtceu/api/multiblock/util/BlockPatternHelper.java | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/AbstractStructureHelper.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/AbstractStructureHelper.java index 8f2433aae3c..2207ae75b9f 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/AbstractStructureHelper.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/AbstractStructureHelper.java @@ -75,13 +75,13 @@ public abstract MultiPredicate getPredicateFromPos(IBlockPattern pattern, BlockP protected int getMinCount(MultiPredicate predicate, BasePredicate basePredicate) { if (!minMaxPreferences.contains(predicate, basePredicate)) - return basePredicate.getSettings().minCount(); + return basePredicate.getMinCount(); return minMaxPreferences.get(predicate, basePredicate).leftInt(); } protected int getMaxCount(MultiPredicate predicate, BasePredicate basePredicate) { if (!minMaxPreferences.contains(predicate, basePredicate)) - return basePredicate.getSettings().maxCount(); + return basePredicate.getMaxCount(); return minMaxPreferences.get(predicate, basePredicate).rightInt(); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/BlockPatternHelper.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/BlockPatternHelper.java index df3af0196ae..60ad5e39036 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/BlockPatternHelper.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/util/BlockPatternHelper.java @@ -222,8 +222,7 @@ private boolean isValidCandidate(Map resultStructure, Multi int layerAlreadyPopulated = countPopulatedInLayer(resultStructure, basePredicate, sliceDir, sliceCoord); if (maxCount != -1 && totalAlreadyPopulated >= maxCount) continue; - if (basePredicate.getSettings().maxSliceCount() == -1 || - layerAlreadyPopulated < basePredicate.getSettings().maxSliceCount()) { + if (basePredicate.getMaxSliceCount() == -1 || layerAlreadyPopulated < basePredicate.getMaxSliceCount()) { return true; } } From bbb572cd5e56898aceb352f8747a73becd34a9f9 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Fri, 28 Aug 2026 16:45:00 -0700 Subject: [PATCH 14/63] settings is nonnull by defualt, meaning changes to settings are applied to the MultiPredicate instead of its children javadoc --- .../gtceu/api/multiblock/MultiPredicate.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 681d37d5038..32915e80b45 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -58,9 +58,10 @@ public abstract class MultiPredicate implements SettingsHolder { * I'm leaning to not bother, just make the predicates yourself and edit them acordingly, but wanna discuss it at * least */ + /// Nonnull by default, see {@link #recursive()} @Nullable @Getter - private PredicateSettings settings = null; + private PredicateSettings settings = PredicateSettings.create(); @Getter private final List additionalTooltips = new ArrayList<>(); @@ -300,7 +301,9 @@ public MultiPredicate addTooltips(Component... tooltip) { return this; } - private void updateSettings(UnaryOperator configurator) { + /// If single, mutates the only predicate
+ /// Otherwise mutates this multipredicate, adds setting if it was null + public void updateSettings(UnaryOperator configurator) { if (isSingle()) { predicates().get(0).updateSettings(configurator); } @@ -385,10 +388,25 @@ public MultiPredicate copyWith(Consumer configurator) { return copy; } + /// Mark this multipredicate as recursive (`this.settings = null`), + /// meaning that settings are applied to children instead of itself + /// @return a copy of this multipredicate with `this.settings = null` + @CheckReturnValue + public MultiPredicate recursive() { + return copyWith(mp -> mp.settings = null); + } + @Override @CheckReturnValue public MultiPredicate withSettings(UnaryOperator configurator) { - return copyWith(p -> p.setSettings(configurator.apply(getOrCreateSettings()))); + if (this.settings == null) { + return copyWith(mp -> { + // only one level deep + mp.forEach(p -> p.updateSettings(configurator)); + mp.forEachChild(p -> p.updateSettings(configurator)); + }); + } + return copyWith(p -> p.updateSettings(configurator)); } @CheckReturnValue From 61223230258e1383c3075d0b158b23ceaa99e160 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:04:53 -0700 Subject: [PATCH 15/63] default methods and remove redundancy add builder pattern generic to SettingsHolder --- .../gtceu/api/multiblock/MultiPredicate.java | 98 +++---------------- .../multiblock/predicates/BasePredicate.java | 82 +--------------- .../multiblock/predicates/SettingsHolder.java | 65 +++++++++--- 3 files changed, 64 insertions(+), 181 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 32915e80b45..31474eab100 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -21,7 +21,7 @@ import java.util.function.Consumer; import java.util.function.UnaryOperator; -public abstract class MultiPredicate implements SettingsHolder { +public abstract class MultiPredicate implements SettingsHolder { private static final MultiPredicate EMPTY = of(Logic.OR, List.of()); @@ -303,11 +303,19 @@ public MultiPredicate addTooltips(Component... tooltip) { /// If single, mutates the only predicate
/// Otherwise mutates this multipredicate, adds setting if it was null + @Override public void updateSettings(UnaryOperator configurator) { + this.updateSettings(configurator, false); + } + + public void updateSettings(UnaryOperator configurator, boolean shouldCreate) { if (isSingle()) { predicates().get(0).updateSettings(configurator); } - setSettings(Objects.requireNonNull(configurator.apply(getOrCreateSettings()))); + PredicateSettings settings = shouldCreate ? getOrCreateSettings() : getSettings(); + if (settings != null) { + setSettings(Objects.requireNonNull(configurator.apply(settings))); + } } public void setSettings(@Nullable PredicateSettings settings) { @@ -316,55 +324,6 @@ public void setSettings(@Nullable PredicateSettings settings) { } } - @Override - public void setPriority(int priority) { - if (this.settings != null) { - updateSettings(s -> s.withPriority(priority)); - } - } - - @Override - public void setMinCount(int minCount) { - if (this.settings != null) { - updateSettings(s -> s.withMinCount(minCount)); - } - } - - @Override - public void setMaxCount(int maxCount) { - if (this.settings != null) { - updateSettings(s -> s.withMaxCount(maxCount)); - } - } - - @Override - public void setMinSliceCount(int minSliceCount) { - if (this.settings != null) { - updateSettings(s -> s.withMinSliceCount(minSliceCount)); - } - } - - @Override - public void setMaxSliceCount(int maxSliceCount) { - if (this.settings != null) { - updateSettings(s -> s.withMaxSliceCount(maxSliceCount)); - } - } - - @Override - public void setPreviewCount(int previewCount) { - if (this.settings != null) { - updateSettings(s -> s.withPreviewCount(previewCount)); - } - } - - @Override - public void setDisableRenderFormed(boolean disableRenderFormed) { - if (this.settings != null) { - updateSettings(s -> s.withDisableRenderFormed(disableRenderFormed)); - } - } - /* * MUTATE AND COPY */ @@ -403,47 +362,12 @@ public MultiPredicate withSettings(UnaryOperator configurator return copyWith(mp -> { // only one level deep mp.forEach(p -> p.updateSettings(configurator)); - mp.forEachChild(p -> p.updateSettings(configurator)); + mp.forEachChild(p -> p.updateSettings(configurator, true)); }); } return copyWith(p -> p.updateSettings(configurator)); } - @CheckReturnValue - public MultiPredicate withPriority(int priority) { - return withSettings(s -> s.withPriority(priority)); - } - - @CheckReturnValue - public MultiPredicate withMinCount(int min) { - return withSettings(s -> s.withMinCount(min)); - } - - @CheckReturnValue - public MultiPredicate withMaxCount(int max) { - return withSettings(s -> s.withMaxCount(max)); - } - - @CheckReturnValue - public MultiPredicate withMinSliceCount(int min) { - return withSettings(s -> s.withMinSliceCount(min)); - } - - @CheckReturnValue - public MultiPredicate withMaxSliceCount(int max) { - return withSettings(s -> s.withMaxSliceCount(max)); - } - - @CheckReturnValue - public MultiPredicate withPreviewCount(int previewCount) { - return withSettings(s -> s.withPreviewCount(previewCount)); - } - - @CheckReturnValue - public MultiPredicate withDisableRenderFormed(boolean disable) { - return withSettings(s -> s.withDisableRenderFormed(disable)); - } - @CheckReturnValue public MultiPredicate withMinGlobalLimited(int min) { return this.withMinCount(min); diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index 4f20cf5c541..bf38ffcb463 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -15,7 +15,7 @@ import java.util.*; import java.util.function.UnaryOperator; -public abstract class BasePredicate implements Comparable, SettingsHolder { +public abstract class BasePredicate implements Comparable, SettingsHolder { public static final BasePredicate AIR = new PredicateBuilder("Air") .predicate(ctx -> ctx.state().isAir()) @@ -130,8 +130,6 @@ public int compareTo(BasePredicate o) { return this.settings.comparePriority(o.settings); } - // COPY AND MUTATE - public abstract BasePredicate copy(); protected void copyTo(BasePredicate other) { @@ -139,6 +137,8 @@ protected void copyTo(BasePredicate other) { other.additionalTooltips.addAll(this.additionalTooltips); } + // COPY AND MUTATE + @Override public BasePredicate withSettings(UnaryOperator configurator) { BasePredicate copy = copy(); @@ -146,88 +146,14 @@ public BasePredicate withSettings(UnaryOperator configurator) return copy; } - @Override - public BasePredicate withDisableRenderFormed(boolean disableRenderFormed) { - return withSettings(s -> s.withDisableRenderFormed(disableRenderFormed)); - } - - @Override - public BasePredicate withPreviewCount(int previewCount) { - return withSettings(s -> s.withPreviewCount(previewCount)); - } - - @Override - public BasePredicate withMaxSliceCount(int maxSliceCount) { - return withSettings(s -> s.withMaxSliceCount(maxSliceCount)); - } - - @Override - public BasePredicate withMinSliceCount(int minSliceCount) { - return withSettings(s -> s.withMinSliceCount(minSliceCount)); - } - - @Override - public BasePredicate withMaxCount(int maxCount) { - return withSettings(s -> s.withMaxCount(maxCount)); - } - - @Override - public BasePredicate withMinCount(int minCount) { - return withSettings(s -> s.withMinCount(minCount)); - } - - @Override - public BasePredicate withPriority(int priority) { - return withSettings(s -> s.withPriority(priority)); - } + // MUTATE ONLY @Override public void setSettings(PredicateSettings settings) { this.settings = settings; } - // MUTATE ONLY - public void addTooltips(Component tooltip) { this.additionalTooltips.add(tooltip); } - - public void updateSettings(UnaryOperator configurator) { - setSettings(configurator.apply(getSettings())); - } - - @Override - public void setDisableRenderFormed(boolean disableRenderFormed) { - updateSettings(s -> s.withDisableRenderFormed(disableRenderFormed)); - } - - @Override - public void setPreviewCount(int previewCount) { - updateSettings(s -> s.withPreviewCount(previewCount)); - } - - @Override - public void setMaxSliceCount(int maxSliceCount) { - updateSettings(s -> s.withMaxSliceCount(maxSliceCount)); - } - - @Override - public void setMinSliceCount(int minSliceCount) { - updateSettings(s -> s.withMinSliceCount(minSliceCount)); - } - - @Override - public void setMaxCount(int maxCount) { - updateSettings(s -> s.withMaxCount(maxCount)); - } - - @Override - public void setMinCount(int minCount) { - updateSettings(s -> s.withMinCount(minCount)); - } - - @Override - public void setPriority(int priority) { - updateSettings(s -> s.withPriority(priority)); - } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index 8f742ec23cc..32ef89de7d0 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -2,7 +2,7 @@ import java.util.function.UnaryOperator; -public interface SettingsHolder { +public interface SettingsHolder> { // getters PredicateSettings getSettings(); @@ -38,37 +38,70 @@ default boolean isRenderFormedDisabled() { // mutate only void setSettings(PredicateSettings settings); - void setPriority(int priority); + /// mutates this object with the configured setting + default void updateSettings(UnaryOperator configurator) { + setSettings(configurator.apply(getSettings())); + } + + default void setPriority(int priority) { + updateSettings(s -> s.withPriority(priority)); + } - void setMinCount(int minCount); + default void setMinCount(int minCount) { + updateSettings(s -> s.withMinCount(minCount)); + } - void setMaxCount(int maxCount); + default void setMaxCount(int maxCount) { + updateSettings(s -> s.withMaxCount(maxCount)); + } - void setMinSliceCount(int minSliceCount); + default void setMinSliceCount(int minSliceCount) { + updateSettings(s -> s.withMinSliceCount(minSliceCount)); + } - void setMaxSliceCount(int maxSliceCount); + default void setMaxSliceCount(int maxSliceCount) { + updateSettings(s -> s.withMaxSliceCount(maxSliceCount)); + } - void setPreviewCount(int previewCount); + default void setPreviewCount(int previewCount) { + updateSettings(s -> s.withPreviewCount(previewCount)); + } - void setDisableRenderFormed(boolean disableRenderFormed); + default void setDisableRenderFormed(boolean disableRenderFormed) { + updateSettings(s -> s.withDisableRenderFormed(disableRenderFormed)); + } // copy and mutate /// @return a copy with these settings applied - SettingsHolder withSettings(UnaryOperator configurator); + S withSettings(UnaryOperator configurator); - SettingsHolder withPriority(int priority); + default S withPriority(int priority) { + return withSettings(s -> s.withPriority(priority)); + } - SettingsHolder withMinCount(int minCount); + default S withMinCount(int minCount) { + return withSettings(s -> s.withMinCount(minCount)); + } - SettingsHolder withMaxCount(int maxCount); + default S withMaxCount(int maxCount) { + return withSettings(s -> s.withMaxCount(maxCount)); + } - SettingsHolder withMinSliceCount(int minSliceCount); + default S withMinSliceCount(int minSliceCount) { + return withSettings(s -> s.withMinSliceCount(minSliceCount)); + } - SettingsHolder withMaxSliceCount(int maxSliceCount); + default S withMaxSliceCount(int maxSliceCount) { + return withSettings(s -> s.withMaxSliceCount(maxSliceCount)); + } - SettingsHolder withPreviewCount(int previewCount); + default S withPreviewCount(int previewCount) { + return withSettings(s -> s.withPreviewCount(previewCount)); + } - SettingsHolder withDisableRenderFormed(boolean disableRenderFormed); + default S withDisableRenderFormed(boolean disableRenderFormed) { + return withSettings(s -> s.withDisableRenderFormed(disableRenderFormed)); + } // test methods /// simple test against global min count From 1fa811c7ef0fc2a4609342fda99127bc80d70098 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:12:41 -0700 Subject: [PATCH 16/63] return --- .../com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 31474eab100..534dac3ed10 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -311,6 +311,7 @@ public void updateSettings(UnaryOperator configurator) { public void updateSettings(UnaryOperator configurator, boolean shouldCreate) { if (isSingle()) { predicates().get(0).updateSettings(configurator); + return; } PredicateSettings settings = shouldCreate ? getOrCreateSettings() : getSettings(); if (settings != null) { From 8adce245e1dd263803eb50bfb46933ebd94bdf2e Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:16:32 -0700 Subject: [PATCH 17/63] update usages --- .../gtceu/api/multiblock/PredicateContext.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java index 45df4f7afe2..1e4c30d55fa 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java @@ -29,8 +29,8 @@ public class PredicateContext { private final PatternState state; @Getter protected CurrentBlockInfo currentBlockInfo = new CurrentBlockInfo(); - protected final Object2IntMap globalCount = new Object2IntOpenHashMap<>(); - protected final Object2IntMap layerCount = new Object2IntOpenHashMap<>(); + protected final Object2IntMap> globalCount = new Object2IntOpenHashMap<>(); + protected final Object2IntMap> layerCount = new Object2IntOpenHashMap<>(); private final Int2ObjectMap> sliceErrors = new Int2ObjectAVLTreeMap<>( IntComparators.NATURAL_COMPARATOR); @@ -131,20 +131,20 @@ public void clearLayerCounts() { this.layerCount.clear(); } - public int incrementGlobalCount(SettingsHolder predicate) { + public int incrementGlobalCount(SettingsHolder predicate) { return this.globalCount.mergeInt(predicate, 1, Integer::sum); } - public int incrementSliceCount(SettingsHolder predicate) { + public int incrementSliceCount(SettingsHolder predicate) { if (!checkLayer) return 0; return this.layerCount.mergeInt(predicate, 1, Integer::sum); } - public int getGlobalCount(SettingsHolder predicate) { + public int getGlobalCount(SettingsHolder predicate) { return this.globalCount.getInt(predicate); } - public int getSliceCount(SettingsHolder predicate) { + public int getSliceCount(SettingsHolder predicate) { if (!checkLayer) return 0; return this.layerCount.getInt(predicate); } From 7a8805b70c64d1d1e22a861d7a70447002804caf Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:26:10 -0700 Subject: [PATCH 18/63] make settings a constructor param improve nonevalid in xor --- .../gtceu/api/multiblock/AndPredicate.java | 8 +++- .../gtceu/api/multiblock/MultiPredicate.java | 25 ++++++----- .../gtceu/api/multiblock/OrPredicate.java | 8 +++- .../gtceu/api/multiblock/XorPredicate.java | 45 ++++++++++--------- 4 files changed, 49 insertions(+), 37 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java index 38b02e4b74e..66f36342532 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java @@ -1,13 +1,17 @@ package com.gregtechceu.gtceu.api.multiblock; import com.gregtechceu.gtceu.api.multiblock.predicates.BasePredicate; +import com.gregtechceu.gtceu.api.multiblock.predicates.PredicateSettings; + +import org.jetbrains.annotations.Nullable; import java.util.List; public class AndPredicate extends MultiPredicate { - public AndPredicate(List children, List predicates, boolean hasAir) { - super(Logic.AND, children, predicates, hasAir); + public AndPredicate(List children, List predicates, boolean hasAir, + @Nullable PredicateSettings settings) { + super(Logic.AND, children, predicates, hasAir, settings); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 534dac3ed10..30ad047851d 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -61,7 +61,7 @@ public abstract class MultiPredicate implements SettingsHolder { /// Nonnull by default, see {@link #recursive()} @Nullable @Getter - private PredicateSettings settings = PredicateSettings.create(); + private PredicateSettings settings; @Getter private final List additionalTooltips = new ArrayList<>(); @@ -74,13 +74,14 @@ public abstract class MultiPredicate implements SettingsHolder { /// @param children list of multi predicate children /// @param predicates list of testable predicates, should be sorted already protected MultiPredicate(Logic type, List children, List predicates, - boolean hasAir) { + boolean hasAir, @Nullable PredicateSettings settings) { predicates.forEach(p -> p.setParent(this)); children.forEach(mp -> mp.setParent(this)); this.predicates = Collections.unmodifiableList(predicates); this.children = Collections.unmodifiableList(children); this.type = type; this.hasAir = hasAir; + this.settings = Objects.requireNonNullElseGet(settings, PredicateSettings::create); } /// @return innermost base predicate that passes state check at given pos @@ -330,12 +331,11 @@ public void setSettings(@Nullable PredicateSettings settings) { */ @CheckReturnValue - private MultiPredicate deepCopy() { + protected MultiPredicate deepCopy() { MultiPredicate copy = this.type.makePredicate( children().stream().map(MultiPredicate::deepCopy).toList(), predicates().stream().map(BasePredicate::copy).toList(), - this.hasAir); - copy.setSettings(this.settings); + this.hasAir, this.settings); copy.additionalTooltips.addAll(this.additionalTooltips); copy.setController(this.controller); return copy; @@ -509,7 +509,7 @@ private static MultiPredicate combine(MultiPredicate a, Logic type, @Nullable Mu appendPredicates(type, a, predicates, children); appendPredicates(type, b, predicates, children); predicates.sort(BasePredicate::compareTo); - return type.makePredicate(children, predicates, a.hasAir || b.hasAir); + return type.makePredicate(children, predicates, a.hasAir || b.hasAir, null); } private static void appendPredicates(Logic type, MultiPredicate multiPredicate, @@ -532,7 +532,8 @@ public static MultiPredicate of(BasePredicate predicate) { /// @param predicates list must be modifiable private static MultiPredicate of(Logic type, List predicates) { - return type.makePredicate(List.of(), predicates, predicates.stream().anyMatch(p -> p == BasePredicate.AIR)); + return type.makePredicate(List.of(), predicates, predicates.stream() + .anyMatch(p -> p == BasePredicate.AIR), null); } protected enum Logic { @@ -542,16 +543,16 @@ protected enum Logic { XOR; public MultiPredicate makePredicate(List children, List predicates, - boolean hasAir) { + boolean hasAir, @Nullable PredicateSettings settings) { return switch (this) { - case OR -> new OrPredicate(children, predicates, hasAir); - case AND -> new AndPredicate(children, predicates, hasAir); - case XOR -> new XorPredicate(children, predicates, hasAir); + case OR -> new OrPredicate(children, predicates, hasAir, settings); + case AND -> new AndPredicate(children, predicates, hasAir, settings); + case XOR -> new XorPredicate(children, predicates, hasAir, settings); }; } public MultiPredicate makePredicate(BasePredicate predicate, boolean hasAir) { - return makePredicate(List.of(), List.of(predicate), hasAir); + return makePredicate(List.of(), List.of(predicate), hasAir, null); } } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java index 1cee4a452e2..d833240454c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java @@ -1,13 +1,17 @@ package com.gregtechceu.gtceu.api.multiblock; import com.gregtechceu.gtceu.api.multiblock.predicates.BasePredicate; +import com.gregtechceu.gtceu.api.multiblock.predicates.PredicateSettings; + +import org.jetbrains.annotations.Nullable; import java.util.List; public class OrPredicate extends MultiPredicate { - public OrPredicate(List children, List predicates, boolean hasAir) { - super(Logic.OR, children, predicates, hasAir); + public OrPredicate(List children, List predicates, boolean hasAir, + @Nullable PredicateSettings settings) { + super(Logic.OR, children, predicates, hasAir, settings); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java index 5c28f2073b1..636c314424f 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Objects; +import java.util.function.UnaryOperator; public class XorPredicate extends MultiPredicate { @@ -20,33 +21,18 @@ public class XorPredicate extends MultiPredicate { /// meaning that it is possible that no predicates may be present in the multi. protected boolean noneValid; - public XorPredicate(List children, List predicates, boolean hasAir) { - super(Logic.XOR, children, predicates, hasAir); + public XorPredicate(List children, List predicates, boolean hasAir, + @Nullable PredicateSettings settings) { + super(Logic.XOR, children, predicates, hasAir, settings); + this.noneValid = isNoneValid(this); } @Override - public void setSettings(@Nullable PredicateSettings settings) { - super.setSettings(settings); + public void updateSettings(UnaryOperator configurator, boolean shouldCreate) { + super.updateSettings(configurator, shouldCreate); this.noneValid = isNoneValid(this); } - private static boolean isNoneValid(MultiPredicate multiPredicate) { - PredicateSettings settings = multiPredicate.getSettings(); - if (settings != null) return settings.isNoneValid(); - - for (BasePredicate predicate : multiPredicate.predicates()) { - if (predicate.getSettings().isNoneValid()) { - return true; - } - } - for (MultiPredicate child : multiPredicate.children()) { - if (isNoneValid(child)) { - return true; - } - } - return false; - } - @Override public @Nullable BasePredicate getPredicateAtPos(PredicateContext context) { context.setStage(PredicateContext.PredicateStage.INTERNAL); @@ -111,6 +97,23 @@ private static void xorError(PredicateContext context, BasePredicate predicate, context.skipFlipCheck(); } + private static boolean isNoneValid(MultiPredicate multiPredicate) { + PredicateSettings settings = multiPredicate.getSettings(); + if (settings != null) return settings.isNoneValid(); + + for (BasePredicate predicate : multiPredicate.predicates()) { + if (predicate.getSettings().isNoneValid()) { + return true; + } + } + for (MultiPredicate child : multiPredicate.children()) { + if (isNoneValid(child)) { + return true; + } + } + return false; + } + private static PassedPredicate ofPredicate(BasePredicate predicate) { return new PassedPredicate(predicate, null); } From 712c57e3e0297e3f1ebc77a24d7fd9e9b776654f Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:17:23 -0700 Subject: [PATCH 19/63] fix rebase --- .../gtceu/api/multiblock/predicates/BasePredicate.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index bf38ffcb463..9755e2d486f 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -44,9 +44,6 @@ public MultiPredicate getParent() { /// the main testing method public abstract boolean test(PredicateContext ctx); - // this is called after calling getPredicateAtPos() - public abstract List getRecipeViewerTooltips(MultiPredicate root); - public abstract void onError(PredicateContext ctx); /// @param root the top-most multi predicate for this multi predicate From 1054e40a447052331ce106d505cfe8bf1c9a9763 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:23:57 -0700 Subject: [PATCH 20/63] move if check inside lambda --- .../gtceu/api/multiblock/MultiPredicate.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 30ad047851d..9fce1a5d6d9 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -359,14 +359,15 @@ public MultiPredicate recursive() { @Override @CheckReturnValue public MultiPredicate withSettings(UnaryOperator configurator) { - if (this.settings == null) { - return copyWith(mp -> { + return copyWith(p -> { + if (this.settings == null) { // only one level deep - mp.forEach(p -> p.updateSettings(configurator)); - mp.forEachChild(p -> p.updateSettings(configurator, true)); - }); - } - return copyWith(p -> p.updateSettings(configurator)); + p.forEach(predicate -> predicate.updateSettings(configurator)); + p.forEachChild(child -> child.updateSettings(configurator, true)); + } else { + p.updateSettings(configurator); + } + }); } @CheckReturnValue From 2c543e1bfa0e67f8b7d00d79bb1624b05f131cfe Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:27:47 -0700 Subject: [PATCH 21/63] remove comment --- .../gtceu/api/multiblock/MultiPredicate.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 9fce1a5d6d9..fd2d7545b40 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -41,23 +41,6 @@ public abstract class MultiPredicate implements SettingsHolder { @Accessors(chain = true) private boolean controller; - /* - * I was curious on your opinion on how recursiveness of setting mins on multipredicates should be done, if at all - * - * a.setmin(4).and(b.setmin(4) -> at least 4 of a and 4 of b - * - * a.and(b).setmin(4) -> at least 4 of a and b combined - * - * should we allow something like - * a.and(b).setmin(4,true) or .setminRecursive(4) -> at least 4 of a and 4 of b - * - * for the case where the user doesn't have access to the original a and b predicates? - * how do we see this "recursive" idea for the difference between children and predicates? and what about when you - * have multiple nested children? - * or should we just not bother? - * I'm leaning to not bother, just make the predicates yourself and edit them acordingly, but wanna discuss it at - * least - */ /// Nonnull by default, see {@link #recursive()} @Nullable @Getter From 44b7b4d0bd222ec96c9e9906c24e4f0f720ccbda Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:31:32 -0700 Subject: [PATCH 22/63] get rid of additional tooltips for MultiPredicates --- .../gtceu/api/multiblock/MultiPredicate.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index fd2d7545b40..6e752011456 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -46,9 +46,6 @@ public abstract class MultiPredicate implements SettingsHolder { @Getter private PredicateSettings settings; - @Getter - private final List additionalTooltips = new ArrayList<>(); - @Nullable @Getter @Setter @@ -274,14 +271,16 @@ public void applySettingsToChildren() { @RemapForJS("addTooltip") @Contract(mutates = "this") public MultiPredicate addTooltips(Component tooltip) { - this.additionalTooltips.add(tooltip); + forEach(p -> p.addTooltips(tooltip)); + forEachChild(mp -> mp.addTooltips(tooltip)); return this; } @CheckReturnValue @Contract(mutates = "this") public MultiPredicate addTooltips(Component... tooltip) { - Collections.addAll(this.additionalTooltips, tooltip); + forEach(p -> Collections.addAll(p.getAdditionalTooltips(), tooltip)); + forEachChild(mp -> mp.addTooltips(tooltip)); return this; } @@ -319,7 +318,6 @@ protected MultiPredicate deepCopy() { children().stream().map(MultiPredicate::deepCopy).toList(), predicates().stream().map(BasePredicate::copy).toList(), this.hasAir, this.settings); - copy.additionalTooltips.addAll(this.additionalTooltips); copy.setController(this.controller); return copy; } From 42fcab391a124ddfeb4b02a2fd6b6a3292b1c9bb Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 30 Aug 2026 22:43:57 -0700 Subject: [PATCH 23/63] forgot this --- .../gtceu/api/multiblock/predicates/TestablePredicate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java index 407760c17fe..10852a3fc41 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java @@ -59,7 +59,7 @@ class TestablePredicate extends BasePredicate { /// @return a list of components to be displayed while hovering over a block in the Multiblock Preview @Override public List getRecipeViewerTooltips(MultiPredicate root) { - List tooltips = new ArrayList<>(root.getAdditionalTooltips()); + List tooltips = new ArrayList<>(); int minCount = getMinCount(); int maxCount = getMaxCount(); if (minCount == maxCount && maxCount != -1) { From d9e8856246eb59ce4d61e52de6388e4e8e640f86 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 30 Aug 2026 22:51:54 -0700 Subject: [PATCH 24/63] wtf --- .../java/com/gregtechceu/gtceu/data/lang/LangHandler.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java b/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java index 1fcf4d44658..8af6da707c8 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java @@ -1235,10 +1235,6 @@ public static void init(RegistrateLangProvider provider) { provider.add("gtceu.multiblock.pattern.min_count", "§cAt least: %d§r"); provider.add("gtceu.multiblock.pattern.max_count", "§cAt most: %d§r"); multilineLang(provider, "gtceu.multiblock.pattern.error", "Expected components at:\n %s, %s, %s."); - provider.add("gtceu.multiblock.pattern.exact_count", "§cExactly: %d§r"); - provider.add("gtceu.multiblock.pattern.between_count", "§cBetween %d and %d§r"); - provider.add("gtceu.multiblock.pattern.min_count", "§cAt least: %d§r"); - provider.add("gtceu.multiblock.pattern.max_count", "§cAt most: %d§r"); provider.add("gtceu.multiblock.pattern.error.limited.exact", "§cExactly: %d§r"); provider.add("gtceu.multiblock.pattern.error.limited.range", "§cBetween %d and %d§r"); provider.add("gtceu.multiblock.pattern.error.limited.max_count", "§cMaximum:§r %d has %d"); From 13ec77d65cbf5c6c5b994115da00d515bc8de732 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Fri, 4 Sep 2026 02:34:28 -0700 Subject: [PATCH 25/63] better null check on set settings --- .../gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 6e752011456..517f4a3925d 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -303,9 +303,7 @@ public void updateSettings(UnaryOperator configurator, boolea } public void setSettings(@Nullable PredicateSettings settings) { - if (settings != null) { - this.settings = settings.copy(); - } + this.settings = settings == null ? null : settings.copy(); } /* @@ -334,7 +332,7 @@ public MultiPredicate copyWith(Consumer configurator) { /// @return a copy of this multipredicate with `this.settings = null` @CheckReturnValue public MultiPredicate recursive() { - return copyWith(mp -> mp.settings = null); + return copyWith(mp -> mp.setSettings(null)); } @Override From d2f56d2b10d509a3f71ff085d7a84b880ca6073c Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Fri, 4 Sep 2026 15:39:23 -0700 Subject: [PATCH 26/63] improve multi predicate settings on copy --- .../gtceu/api/multiblock/MultiPredicate.java | 47 +++++++++++++------ 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 517f4a3925d..0cb3555a2fb 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -61,7 +61,7 @@ protected MultiPredicate(Logic type, List children, List - /// Otherwise mutates this multipredicate, adds setting if it was null + /// Otherwise mutates this multipredicate, adds setting if it was null and {@code shouldCreate} is true @Override public void updateSettings(UnaryOperator configurator) { this.updateSettings(configurator, false); @@ -339,11 +339,13 @@ public MultiPredicate recursive() { @CheckReturnValue public MultiPredicate withSettings(UnaryOperator configurator) { return copyWith(p -> { + // is this (the current reference being copied) have their settings set to null? if (this.settings == null) { - // only one level deep + // apply the settings to each predicate and child, creating new settings if needed p.forEach(predicate -> predicate.updateSettings(configurator)); p.forEachChild(child -> child.updateSettings(configurator, true)); } else { + // the current reference has settings, it should've been copied by this point p.updateSettings(configurator); } }); @@ -474,27 +476,42 @@ public static MultiPredicate xor(List predicates) { /// @param a left operand /// @param type logic of the new predicate /// @param b right operand, may be null - /// @return If {@code b == null}, returns {@code a}.
+ /// @return If {@code b == null || b == EMPTY}, returns {@code a}.
/// If {@code a == EMPTY}, returns {@code b}.
/// Otherwise, returns a new MultiPredicate that combines {@code a} and {@code b} private static MultiPredicate combine(MultiPredicate a, Logic type, @Nullable MultiPredicate b) { if (b == null || b.isEmpty()) return a; // no op if (a.isEmpty()) return b; - if (!Objects.equals(a.settings, b.settings)) { - a.applySettingsToChildren(); - b.applySettingsToChildren(); - } + List predicates = new ArrayList<>(); List children = new ArrayList<>(); - appendPredicates(type, a, predicates, children); - appendPredicates(type, b, predicates, children); + + // if a and b have different settings, they need to be added + // as children to the new predicate to maintain expected behavior + boolean equalSettings = Objects.equals(a.getSettings(), b.getSettings()); + + appendPredicates(type, a.deepCopy(), predicates, children, equalSettings); + appendPredicates(type, b.deepCopy(), predicates, children, equalSettings); + predicates.sort(BasePredicate::compareTo); - return type.makePredicate(children, predicates, a.hasAir || b.hasAir, null); + + // if the settings are the same, we can reuse one of them + PredicateSettings newSettings; + if (!equalSettings) { + newSettings = PredicateSettings.create(); + } else if (a.getSettings() == null) { + newSettings = null; + } else { + newSettings = a.getSettings().copy(); + } + + return type.makePredicate(children, predicates, a.hasAir || b.hasAir, newSettings); } private static void appendPredicates(Logic type, MultiPredicate multiPredicate, - List predicates, List children) { - if (multiPredicate.isSingle() || multiPredicate.isType(type)) { + List predicates, List children, + boolean equalSettings) { + if (multiPredicate.isSingle() || (multiPredicate.isType(type) && equalSettings)) { predicates.addAll(multiPredicate.predicates()); children.addAll(multiPredicate.children()); } else { @@ -510,10 +527,10 @@ public static MultiPredicate of(BasePredicate predicate) { return Logic.OR.makePredicate(predicate, predicate == BasePredicate.AIR); } - /// @param predicates list must be modifiable + /// @return A multi predicate with default settings private static MultiPredicate of(Logic type, List predicates) { return type.makePredicate(List.of(), predicates, predicates.stream() - .anyMatch(p -> p == BasePredicate.AIR), null); + .anyMatch(p -> p == BasePredicate.AIR), PredicateSettings.create()); } protected enum Logic { From cd23c1d678c5a5781ec750bde0d8bc9ef28bdc02 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 6 Sep 2026 20:57:25 -0700 Subject: [PATCH 27/63] delete unused method --- .../gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 0cb3555a2fb..51ae28014dd 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -260,14 +260,6 @@ protected PredicateSettings getOrCreateSettings() { return this.settings; } - /// Sets the setting of this multipredicate to its children, then makes {@code this.settings} null - public void applySettingsToChildren() { - if (this.settings == null || isSingle()) return; - forEach(p -> p.setSettings(this.settings)); - forEachChild(mp -> mp.setSettings(this.settings)); - this.settings = null; - } - @RemapForJS("addTooltip") @Contract(mutates = "this") public MultiPredicate addTooltips(Component tooltip) { From 73fa38ffb563b22f893e7def2bdf5080fa0dc4c1 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 6 Sep 2026 20:58:34 -0700 Subject: [PATCH 28/63] add hasSettings method --- .../gtceu/api/multiblock/MultiPredicate.java | 5 +++++ .../api/multiblock/predicates/BasePredicate.java | 5 +++++ .../multiblock/predicates/SettingsHolder.java | 16 +++++++++------- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 51ae28014dd..a5d0ae076ee 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -249,6 +249,11 @@ public List expand() { return expanded; } + @Override + public boolean hasSettings() { + return this.settings != null; + } + /* * MUTATE AND DO NOT COPY */ diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index 9755e2d486f..34e0865b531 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -127,6 +127,11 @@ public int compareTo(BasePredicate o) { return this.settings.comparePriority(o.settings); } + @Override + public boolean hasSettings() { + return true; + } + public abstract BasePredicate copy(); protected void copyTo(BasePredicate other) { diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index 32ef89de7d0..9649115777e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -7,32 +7,34 @@ public interface SettingsHolder> { // getters PredicateSettings getSettings(); + boolean hasSettings(); + default int getPriority() { - return getSettings().priority(); + return hasSettings() ? getSettings().priority() : -1; } default int getMinCount() { - return getSettings().minCount(); + return hasSettings() ? getSettings().minCount() : -1; } default int getMaxCount() { - return getSettings().maxCount(); + return hasSettings() ? getSettings().maxCount() : -1; } default int getMinSliceCount() { - return getSettings().minSliceCount(); + return hasSettings() ? getSettings().minSliceCount() : -1; } default int getMaxSliceCount() { - return getSettings().maxSliceCount(); + return hasSettings() ? getSettings().maxSliceCount() : -1; } default int getPreviewCount() { - return getSettings().previewCount(); + return hasSettings() ? getSettings().previewCount() : -1; } default boolean isRenderFormedDisabled() { - return getSettings().disableRenderFormed(); + return hasSettings() && getSettings().disableRenderFormed(); } // mutate only From 00b24600ecd01d5ae79af7c698039bdf4deb37d8 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 6 Sep 2026 21:00:15 -0700 Subject: [PATCH 29/63] improve testing settings add a bunch of comments --- .../gtceu/api/multiblock/AndPredicate.java | 36 +++++++++++++------ .../gtceu/api/multiblock/MultiPredicate.java | 32 ++++++++--------- .../gtceu/api/multiblock/OrPredicate.java | 36 +++++++++++++------ .../gtceu/api/multiblock/XorPredicate.java | 12 +++++-- .../multiblock/predicates/SettingsHolder.java | 21 +++++++++++ 5 files changed, 99 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java index 66f36342532..689ddbf776f 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java @@ -16,31 +16,47 @@ public AndPredicate(List children, List predicate @Override protected boolean testGlobalMin(PredicateContext ctx) { + boolean result = true; for (BasePredicate predicate : predicates()) { if (!predicate.testGlobalMin(ctx)) { - return false; + result = false; + break; } } - for (MultiPredicate child : children()) { - if (!child.testGlobalMin(ctx)) { - return false; + if (result) { + for (MultiPredicate child : children()) { + if (!child.testGlobalMin(ctx)) { + result = false; + break; + } } } - return true; + if (result) { + result = !hasSettings() || TestType.GLOBAL_MIN.testSettings(this, ctx); + } + return result; } @Override protected boolean testSliceMin(PredicateContext ctx) { + boolean result = true; for (BasePredicate predicate : predicates()) { if (!predicate.testSliceMin(ctx)) { - return false; + result = false; + break; } } - for (MultiPredicate child : children()) { - if (!child.testSliceMin(ctx)) { - return false; + if (result) { + for (MultiPredicate child : children()) { + if (!child.testSliceMin(ctx)) { + result = false; + break; + } } } - return true; + if (result) { + result = !hasSettings() || TestType.SLICE_MIN.testSettings(this, ctx); + } + return result; } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index a5d0ae076ee..a60c77155eb 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -109,11 +109,7 @@ public final boolean postGlobalTest(PredicateContext ctx) { /// Usually used for testing the slice min of predicates public final boolean postSliceTest(PredicateContext ctx) { ctx.setStage(PredicateContext.PredicateStage.SLICE_MIN); - boolean passed = testSliceMin(ctx); - if (this.settings != null) { - passed &= SettingsHolder.super.testSliceMin(ctx.getSliceCount(this)); - } - if (passed) return true; + if (testSliceMin(ctx)) return true; for (Component content : getDescriptiveContents()) { ctx.appendError(PatternStringError.of(content)); } @@ -125,18 +121,11 @@ public final boolean postSliceTest(PredicateContext ctx) { /// test against global/slice max counts public boolean testMaxCount(BasePredicate passedPredicate, PredicateContext context) { context.setStage(PredicateContext.PredicateStage.GLOBAL_MAX); - boolean passed = passedPredicate.testGlobalMax(context); - if (this.settings != null) { - passed &= this.testGlobalMax(context.incrementGlobalCount(this)); + if (!(passedPredicate.testGlobalMax(context) && TestType.GLOBAL_MAX.testSettings(this, context))) { + return false; } - if (passed) { - context.setStage(PredicateContext.PredicateStage.SLICE_MAX); - passed = passedPredicate.testSliceMax(context); - if (this.settings != null) { - passed &= this.testSliceMax(context.incrementSliceCount(this)); - } - } - return passed; + context.setStage(PredicateContext.PredicateStage.SLICE_MAX); + return passedPredicate.testSliceMax(context) && TestType.SLICE_MAX.testSettings(this, context); } public List> getCandidates() { @@ -290,12 +279,23 @@ public void updateSettings(UnaryOperator configurator) { public void updateSettings(UnaryOperator configurator, boolean shouldCreate) { if (isSingle()) { + // the idea is that if we only have a single predicate, we mutate that predicate instead of ourselves + // as we're basically the same as that predicate predicates().get(0).updateSettings(configurator); return; } + // shouldCreate should be true when updating settings through withSettings() + // otherwise it is false PredicateSettings settings = shouldCreate ? getOrCreateSettings() : getSettings(); if (settings != null) { setSettings(Objects.requireNonNull(configurator.apply(settings))); + } else { + // update predicate settings + forEach(p -> p.updateSettings(configurator)); + // update children + // if they have settings, they should mutate themselves (non-recursive) + // otherwise they should mutate their predicates and children instead (recursive) + forEachChild(mp -> mp.updateSettings(configurator)); } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java index d833240454c..ac3b44954cc 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java @@ -16,31 +16,47 @@ public OrPredicate(List children, List predicates @Override protected boolean testGlobalMin(PredicateContext ctx) { + boolean result = false; for (BasePredicate predicate : predicates()) { if (predicate.testGlobalMin(ctx)) { - return true; + result = true; + break; } } - for (MultiPredicate child : children()) { - if (child.testGlobalMin(ctx)) { - return true; + if (!result) { + for (MultiPredicate child : children()) { + if (child.testGlobalMin(ctx)) { + result = true; + break; + } } } - return false; + if (result) { + result = TestType.GLOBAL_MIN.testSettings(this, ctx); + } + return result; } @Override protected boolean testSliceMin(PredicateContext ctx) { + boolean result = false; for (BasePredicate predicate : predicates()) { if (predicate.testSliceMin(ctx)) { - return true; + result = true; + break; } } - for (MultiPredicate child : children()) { - if (child.testSliceMin(ctx)) { - return true; + if (!result) { + for (MultiPredicate child : children()) { + if (child.testSliceMin(ctx)) { + result = true; + break; + } } } - return false; + if (result) { + result = TestType.SLICE_MIN.testSettings(this, ctx); + } + return result; } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java index 636c314424f..09ac46b8bb9 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java @@ -72,13 +72,21 @@ public void updateSettings(UnaryOperator configurator, boolea @Override protected boolean testGlobalMin(PredicateContext ctx) { if (passedPredicate == null && noneValid) return true; - return passedPredicate != null && passedPredicate.testGlobalMin(ctx); + boolean passed = passedPredicate != null && passedPredicate.testGlobalMin(ctx); + if (passed) { + passed = TestType.GLOBAL_MIN.testSettings(this, ctx); + } + return passed; } @Override protected boolean testSliceMin(PredicateContext ctx) { if (passedPredicate == null && noneValid) return true; - return passedPredicate != null && passedPredicate.testSliceMin(ctx); + boolean passed = passedPredicate != null && passedPredicate.testSliceMin(ctx); + if (passed && hasSettings()) { + passed = TestType.SLICE_MIN.testSettings(this, ctx); + } + return passed; } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index 9649115777e..d215ce7df47 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -1,5 +1,7 @@ package com.gregtechceu.gtceu.api.multiblock.predicates; +import com.gregtechceu.gtceu.api.multiblock.PredicateContext; + import java.util.function.UnaryOperator; public interface SettingsHolder> { @@ -125,4 +127,23 @@ default boolean testGlobalMax(int count) { default boolean testSliceMax(int count) { return getMaxSliceCount() == -1 || count <= getMaxSliceCount(); } + + enum TestType { + GLOBAL_MIN, + GLOBAL_MAX, + SLICE_MIN, + SLICE_MAX; + + // should return true if null settings + // otherwise test settings + public boolean testSettings(SettingsHolder holder, PredicateContext ctx) { + if (!holder.hasSettings()) return true; + return switch (this) { + case GLOBAL_MIN -> holder.testGlobalMin(ctx.getGlobalCount(holder)); + case GLOBAL_MAX -> holder.testGlobalMax(ctx.incrementGlobalCount(holder)); + case SLICE_MIN -> holder.testSliceMin(ctx.getSliceCount(holder)); + case SLICE_MAX -> holder.testSliceMax(ctx.incrementSliceCount(holder)); + }; + } + } } From 36982ee64e897b0c760ab8d8e22d8e5cd19f5b69 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 6 Sep 2026 21:15:52 -0700 Subject: [PATCH 30/63] improve copy and mutate methods add settings changed method --- .../gtceu/api/multiblock/AndPredicate.java | 8 +- .../gtceu/api/multiblock/MultiPredicate.java | 81 ++++++++++--------- .../gtceu/api/multiblock/OrPredicate.java | 8 +- .../gtceu/api/multiblock/XorPredicate.java | 10 +-- .../multiblock/predicates/SettingsHolder.java | 3 +- 5 files changed, 53 insertions(+), 57 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java index 689ddbf776f..e28fc909a7c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java @@ -1,17 +1,13 @@ package com.gregtechceu.gtceu.api.multiblock; import com.gregtechceu.gtceu.api.multiblock.predicates.BasePredicate; -import com.gregtechceu.gtceu.api.multiblock.predicates.PredicateSettings; - -import org.jetbrains.annotations.Nullable; import java.util.List; public class AndPredicate extends MultiPredicate { - public AndPredicate(List children, List predicates, boolean hasAir, - @Nullable PredicateSettings settings) { - super(Logic.AND, children, predicates, hasAir, settings); + public AndPredicate(List children, List predicates, boolean hasAir) { + super(Logic.AND, children, predicates, hasAir); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index a60c77155eb..e3e3b042fcd 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -53,15 +53,14 @@ public abstract class MultiPredicate implements SettingsHolder { /// @param children list of multi predicate children /// @param predicates list of testable predicates, should be sorted already - protected MultiPredicate(Logic type, List children, List predicates, - boolean hasAir, @Nullable PredicateSettings settings) { + protected MultiPredicate(Logic type, List children, + List predicates, boolean hasAir) { predicates.forEach(p -> p.setParent(this)); children.forEach(mp -> mp.setParent(this)); this.predicates = Collections.unmodifiableList(predicates); this.children = Collections.unmodifiableList(children); this.type = type; this.hasAir = hasAir; - this.settings = settings; // settings set as-is, ideally copied prior } /// @return innermost base predicate that passes state check at given pos @@ -282,25 +281,30 @@ public void updateSettings(UnaryOperator configurator, boolea // the idea is that if we only have a single predicate, we mutate that predicate instead of ourselves // as we're basically the same as that predicate predicates().get(0).updateSettings(configurator); - return; - } - // shouldCreate should be true when updating settings through withSettings() - // otherwise it is false - PredicateSettings settings = shouldCreate ? getOrCreateSettings() : getSettings(); - if (settings != null) { - setSettings(Objects.requireNonNull(configurator.apply(settings))); + onSettingsChanged(); } else { - // update predicate settings - forEach(p -> p.updateSettings(configurator)); - // update children - // if they have settings, they should mutate themselves (non-recursive) - // otherwise they should mutate their predicates and children instead (recursive) - forEachChild(mp -> mp.updateSettings(configurator)); + // shouldCreate should be true when updating settings through withSettings() + // otherwise it is false + PredicateSettings settings = shouldCreate ? getOrCreateSettings() : getSettings(); + if (settings != null) { + setSettings(Objects.requireNonNull(configurator.apply(settings))); + } else { + // update predicate settings + forEach(p -> p.updateSettings(configurator)); + // update children + // if they have settings, they should mutate themselves (non-recursive) + // otherwise they should mutate their predicates and children instead (recursive) + forEachChild(mp -> mp.updateSettings(configurator)); + onSettingsChanged(); + } } } + protected void onSettingsChanged() {} + public void setSettings(@Nullable PredicateSettings settings) { this.settings = settings == null ? null : settings.copy(); + onSettingsChanged(); } /* @@ -309,10 +313,10 @@ public void setSettings(@Nullable PredicateSettings settings) { @CheckReturnValue protected MultiPredicate deepCopy() { - MultiPredicate copy = this.type.makePredicate( - children().stream().map(MultiPredicate::deepCopy).toList(), - predicates().stream().map(BasePredicate::copy).toList(), - this.hasAir, this.settings); + List copiedPredicates = predicates().stream().map(BasePredicate::copy).toList(); + List copiedChildren = children().stream().map(MultiPredicate::deepCopy).toList(); + MultiPredicate copy = this.type.makePredicate(copiedChildren, copiedPredicates, this.hasAir); + copy.setSettings(this.settings); copy.setController(this.controller); return copy; } @@ -336,15 +340,16 @@ public MultiPredicate recursive() { @CheckReturnValue public MultiPredicate withSettings(UnaryOperator configurator) { return copyWith(p -> { - // is this (the current reference being copied) have their settings set to null? - if (this.settings == null) { - // apply the settings to each predicate and child, creating new settings if needed - p.forEach(predicate -> predicate.updateSettings(configurator)); - p.forEachChild(child -> child.updateSettings(configurator, true)); - } else { - // the current reference has settings, it should've been copied by this point - p.updateSettings(configurator); - } + p.updateSettings(configurator); + // // is this (the current reference being copied) have their settings set to null? + // if (this.settings == null) { + // // apply the settings to each predicate and child, creating new settings if needed + // p.forEach(predicate -> predicate.updateSettings(configurator)); + // p.forEachChild(child -> child.updateSettings(configurator, true)); + // } else { + // // the current reference has settings, it should've been copied by this point + // p.updateSettings(configurator); + // } }); } @@ -502,7 +507,7 @@ private static MultiPredicate combine(MultiPredicate a, Logic type, @Nullable Mu newSettings = a.getSettings().copy(); } - return type.makePredicate(children, predicates, a.hasAir || b.hasAir, newSettings); + return type.makePredicate(children, predicates, a.hasAir || b.hasAir); } private static void appendPredicates(Logic type, MultiPredicate multiPredicate, @@ -526,8 +531,10 @@ public static MultiPredicate of(BasePredicate predicate) { /// @return A multi predicate with default settings private static MultiPredicate of(Logic type, List predicates) { - return type.makePredicate(List.of(), predicates, predicates.stream() - .anyMatch(p -> p == BasePredicate.AIR), PredicateSettings.create()); + MultiPredicate predicate = type.makePredicate(List.of(), predicates, predicates.stream() + .anyMatch(p -> p == BasePredicate.AIR)); + predicate.setSettings(PredicateSettings.create()); + return predicate; } protected enum Logic { @@ -537,16 +544,16 @@ protected enum Logic { XOR; public MultiPredicate makePredicate(List children, List predicates, - boolean hasAir, @Nullable PredicateSettings settings) { + boolean hasAir) { return switch (this) { - case OR -> new OrPredicate(children, predicates, hasAir, settings); - case AND -> new AndPredicate(children, predicates, hasAir, settings); - case XOR -> new XorPredicate(children, predicates, hasAir, settings); + case OR -> new OrPredicate(children, predicates, hasAir); + case AND -> new AndPredicate(children, predicates, hasAir); + case XOR -> new XorPredicate(children, predicates, hasAir); }; } public MultiPredicate makePredicate(BasePredicate predicate, boolean hasAir) { - return makePredicate(List.of(), List.of(predicate), hasAir, null); + return makePredicate(List.of(), List.of(predicate), hasAir); } } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java index ac3b44954cc..2e4509f1299 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java @@ -1,17 +1,13 @@ package com.gregtechceu.gtceu.api.multiblock; import com.gregtechceu.gtceu.api.multiblock.predicates.BasePredicate; -import com.gregtechceu.gtceu.api.multiblock.predicates.PredicateSettings; - -import org.jetbrains.annotations.Nullable; import java.util.List; public class OrPredicate extends MultiPredicate { - public OrPredicate(List children, List predicates, boolean hasAir, - @Nullable PredicateSettings settings) { - super(Logic.OR, children, predicates, hasAir, settings); + public OrPredicate(List children, List predicates, boolean hasAir) { + super(Logic.OR, children, predicates, hasAir); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java index 09ac46b8bb9..b9ed3550dd1 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java @@ -11,7 +11,6 @@ import java.util.List; import java.util.Objects; -import java.util.function.UnaryOperator; public class XorPredicate extends MultiPredicate { @@ -21,15 +20,12 @@ public class XorPredicate extends MultiPredicate { /// meaning that it is possible that no predicates may be present in the multi. protected boolean noneValid; - public XorPredicate(List children, List predicates, boolean hasAir, - @Nullable PredicateSettings settings) { - super(Logic.XOR, children, predicates, hasAir, settings); - this.noneValid = isNoneValid(this); + public XorPredicate(List children, List predicates, boolean hasAir) { + super(Logic.XOR, children, predicates, hasAir); } @Override - public void updateSettings(UnaryOperator configurator, boolean shouldCreate) { - super.updateSettings(configurator, shouldCreate); + protected void onSettingsChanged() { this.noneValid = isNoneValid(this); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index d215ce7df47..dfdfeb94472 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -12,7 +12,7 @@ public interface SettingsHolder> { boolean hasSettings(); default int getPriority() { - return hasSettings() ? getSettings().priority() : -1; + return hasSettings() ? getSettings().priority() : -1; } default int getMinCount() { @@ -129,6 +129,7 @@ default boolean testSliceMax(int count) { } enum TestType { + GLOBAL_MIN, GLOBAL_MAX, SLICE_MIN, From 0926714154d74790a04e3440bbda2b4fbf795a88 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 6 Sep 2026 21:25:51 -0700 Subject: [PATCH 31/63] go up parent chain to test their settings missed global test --- .../gtceu/api/multiblock/MultiPredicate.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index e3e3b042fcd..d7aca5a3aa6 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -91,11 +91,7 @@ protected void onError(PredicateContext ctx) { /// Usually used for testing the global min of predicates public final boolean postGlobalTest(PredicateContext ctx) { ctx.setStage(PredicateContext.PredicateStage.GLOBAL_MIN); - boolean passed = testGlobalMin(ctx); - if (this.settings != null) { - passed &= SettingsHolder.super.testGlobalMin(ctx.getGlobalCount(this)); - } - if (passed) return true; + if (testGlobalMin(ctx)) return true; for (Component content : getDescriptiveContents()) { ctx.appendError(PatternStringError.of(content)); } @@ -120,11 +116,23 @@ public final boolean postSliceTest(PredicateContext ctx) { /// test against global/slice max counts public boolean testMaxCount(BasePredicate passedPredicate, PredicateContext context) { context.setStage(PredicateContext.PredicateStage.GLOBAL_MAX); - if (!(passedPredicate.testGlobalMax(context) && TestType.GLOBAL_MAX.testSettings(this, context))) { + if (!(passedPredicate.testGlobalMax(context) && testParents(TestType.GLOBAL_MAX, passedPredicate, context))) { return false; } context.setStage(PredicateContext.PredicateStage.SLICE_MAX); - return passedPredicate.testSliceMax(context) && TestType.SLICE_MAX.testSettings(this, context); + return passedPredicate.testSliceMax(context) && testParents(TestType.SLICE_MAX, passedPredicate, context); + } + + // go up the parent chain to test settings of parents + private boolean testParents(TestType type, BasePredicate passedPredicate, PredicateContext context) { + MultiPredicate parent = passedPredicate.getParent(); + while (parent != null) { + if (!type.testSettings(parent, context)) { + return false; + } + parent = parent.getParent(); + } + return true; } public List> getCandidates() { From 0e9ac9be621008c7c7c4b09bab851ab5b4c04693 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 6 Sep 2026 21:46:07 -0700 Subject: [PATCH 32/63] cleanup --- .../gtceu/api/multiblock/MultiPredicate.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index d7aca5a3aa6..ffcd60c33a8 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -347,18 +347,7 @@ public MultiPredicate recursive() { @Override @CheckReturnValue public MultiPredicate withSettings(UnaryOperator configurator) { - return copyWith(p -> { - p.updateSettings(configurator); - // // is this (the current reference being copied) have their settings set to null? - // if (this.settings == null) { - // // apply the settings to each predicate and child, creating new settings if needed - // p.forEach(predicate -> predicate.updateSettings(configurator)); - // p.forEachChild(child -> child.updateSettings(configurator, true)); - // } else { - // // the current reference has settings, it should've been copied by this point - // p.updateSettings(configurator); - // } - }); + return copyWith(p -> p.updateSettings(configurator)); } @CheckReturnValue From 5cb70acf0298f6d3f78b82035ea51ca2102ed2da Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 6 Sep 2026 21:46:15 -0700 Subject: [PATCH 33/63] oops --- .../gtceu/api/multiblock/predicates/TestablePredicate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java index 10852a3fc41..e8862f177ca 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java @@ -59,7 +59,7 @@ class TestablePredicate extends BasePredicate { /// @return a list of components to be displayed while hovering over a block in the Multiblock Preview @Override public List getRecipeViewerTooltips(MultiPredicate root) { - List tooltips = new ArrayList<>(); + List tooltips = new ArrayList<>(this.getAdditionalTooltips()); int minCount = getMinCount(); int maxCount = getMaxCount(); if (minCount == maxCount && maxCount != -1) { From 19b7b6dc12a10115717160fd8fc7ddc9c4767fe2 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 6 Sep 2026 21:48:48 -0700 Subject: [PATCH 34/63] mark set methods as internal and javadoc --- .../gtceu/api/multiblock/MultiPredicate.java | 5 +++-- .../multiblock/predicates/SettingsHolder.java | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index ffcd60c33a8..62051ae6c22 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -277,13 +277,14 @@ public MultiPredicate addTooltips(Component... tooltip) { return this; } - /// If single, mutates the only predicate
- /// Otherwise mutates this multipredicate, adds setting if it was null and {@code shouldCreate} is true @Override public void updateSettings(UnaryOperator configurator) { this.updateSettings(configurator, false); } + /// Mutates this multi predicate with the configured settings + /// @param shouldCreate if settings should be able to be created for this multi predicate + @ApiStatus.Internal public void updateSettings(UnaryOperator configurator, boolean shouldCreate) { if (isSingle()) { // the idea is that if we only have a single predicate, we mutate that predicate instead of ourselves diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index dfdfeb94472..21a2b0d41e8 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -1,6 +1,7 @@ package com.gregtechceu.gtceu.api.multiblock.predicates; import com.gregtechceu.gtceu.api.multiblock.PredicateContext; +import org.jetbrains.annotations.ApiStatus; import java.util.function.UnaryOperator; @@ -40,37 +41,53 @@ default boolean isRenderFormedDisabled() { } // mutate only + @ApiStatus.Internal void setSettings(PredicateSettings settings); /// mutates this object with the configured setting + @ApiStatus.Internal default void updateSettings(UnaryOperator configurator) { setSettings(configurator.apply(getSettings())); } + /// mutates this object with the given priority + @ApiStatus.Internal default void setPriority(int priority) { updateSettings(s -> s.withPriority(priority)); } + /// mutates this object with the given min global count + @ApiStatus.Internal default void setMinCount(int minCount) { updateSettings(s -> s.withMinCount(minCount)); } + /// mutates this object with the given max global count + @ApiStatus.Internal default void setMaxCount(int maxCount) { updateSettings(s -> s.withMaxCount(maxCount)); } + /// mutates this object with the given min slice count + @ApiStatus.Internal default void setMinSliceCount(int minSliceCount) { updateSettings(s -> s.withMinSliceCount(minSliceCount)); } + /// mutates this object with the given max slice count + @ApiStatus.Internal default void setMaxSliceCount(int maxSliceCount) { updateSettings(s -> s.withMaxSliceCount(maxSliceCount)); } + /// mutates this object with the given preview count + @ApiStatus.Internal default void setPreviewCount(int previewCount) { updateSettings(s -> s.withPreviewCount(previewCount)); } + /// mutates this object with the given render formed disabled + @ApiStatus.Internal default void setDisableRenderFormed(boolean disableRenderFormed) { updateSettings(s -> s.withDisableRenderFormed(disableRenderFormed)); } From 29e687191afd3b310d6bd88429b0c8c28bae6266 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 6 Sep 2026 21:51:02 -0700 Subject: [PATCH 35/63] make MultiPredicate recursive when combining --- .../gtceu/api/multiblock/MultiPredicate.java | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 62051ae6c22..0c240f9748e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -486,32 +486,18 @@ private static MultiPredicate combine(MultiPredicate a, Logic type, @Nullable Mu List predicates = new ArrayList<>(); List children = new ArrayList<>(); - // if a and b have different settings, they need to be added - // as children to the new predicate to maintain expected behavior - boolean equalSettings = Objects.equals(a.getSettings(), b.getSettings()); - - appendPredicates(type, a.deepCopy(), predicates, children, equalSettings); - appendPredicates(type, b.deepCopy(), predicates, children, equalSettings); + appendPredicates(type, a.deepCopy(), predicates, children); + appendPredicates(type, b.deepCopy(), predicates, children); predicates.sort(BasePredicate::compareTo); - // if the settings are the same, we can reuse one of them - PredicateSettings newSettings; - if (!equalSettings) { - newSettings = PredicateSettings.create(); - } else if (a.getSettings() == null) { - newSettings = null; - } else { - newSettings = a.getSettings().copy(); - } - - return type.makePredicate(children, predicates, a.hasAir || b.hasAir); + return type.makePredicate(children, predicates, a.hasAir || b.hasAir) + .recursive(); } private static void appendPredicates(Logic type, MultiPredicate multiPredicate, - List predicates, List children, - boolean equalSettings) { - if (multiPredicate.isSingle() || (multiPredicate.isType(type) && equalSettings)) { + List predicates, List children) { + if (multiPredicate.isSingle() || multiPredicate.isType(type)) { predicates.addAll(multiPredicate.predicates()); children.addAll(multiPredicate.children()); } else { From 343ee87c2d2dba03f34a53648ab1fccd6785f8de Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 6 Sep 2026 23:31:17 -0700 Subject: [PATCH 36/63] better settings merging? --- .../gtceu/api/multiblock/MultiPredicate.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 0c240f9748e..8744d0a71f1 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -486,13 +486,21 @@ private static MultiPredicate combine(MultiPredicate a, Logic type, @Nullable Mu List predicates = new ArrayList<>(); List children = new ArrayList<>(); - appendPredicates(type, a.deepCopy(), predicates, children); - appendPredicates(type, b.deepCopy(), predicates, children); + boolean canMerge = !a.hasSettings() && !b.hasSettings(); + + if (!canMerge) { + children.add(a); + children.add(b); + } else { + appendPredicates(type, a.deepCopy(), predicates, children); + appendPredicates(type, b.deepCopy(), predicates, children); + } predicates.sort(BasePredicate::compareTo); - return type.makePredicate(children, predicates, a.hasAir || b.hasAir) - .recursive(); + MultiPredicate combined = type.makePredicate(children, predicates, a.hasAir || b.hasAir); + combined.setSettings(canMerge ? null : PredicateSettings.create()); + return combined; } private static void appendPredicates(Logic type, MultiPredicate multiPredicate, From e04c029a8691d066fd098cb7a4669ed597918d76 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Sun, 6 Sep 2026 23:59:45 -0700 Subject: [PATCH 37/63] better settings merging? pt 2 --- .../gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 8744d0a71f1..998dea1bfce 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -486,20 +486,23 @@ private static MultiPredicate combine(MultiPredicate a, Logic type, @Nullable Mu List predicates = new ArrayList<>(); List children = new ArrayList<>(); - boolean canMerge = !a.hasSettings() && !b.hasSettings(); + boolean canMerge = !a.hasSettings() && !b.hasSettings() && a.isType(b.getType()); + PredicateSettings settings; if (!canMerge) { children.add(a); children.add(b); + settings = null; } else { appendPredicates(type, a.deepCopy(), predicates, children); appendPredicates(type, b.deepCopy(), predicates, children); + settings = a.getSettings(); } predicates.sort(BasePredicate::compareTo); MultiPredicate combined = type.makePredicate(children, predicates, a.hasAir || b.hasAir); - combined.setSettings(canMerge ? null : PredicateSettings.create()); + combined.setSettings(settings); return combined; } From d14abccdb489d74151c0637420b90f7efa5f392b Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 00:24:49 -0700 Subject: [PATCH 38/63] what about not merging --- .../gtceu/api/multiblock/MultiPredicate.java | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 998dea1bfce..2c949ffca89 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -483,26 +483,8 @@ private static MultiPredicate combine(MultiPredicate a, Logic type, @Nullable Mu if (b == null || b.isEmpty()) return a; // no op if (a.isEmpty()) return b; - List predicates = new ArrayList<>(); - List children = new ArrayList<>(); - - boolean canMerge = !a.hasSettings() && !b.hasSettings() && a.isType(b.getType()); - PredicateSettings settings; - - if (!canMerge) { - children.add(a); - children.add(b); - settings = null; - } else { - appendPredicates(type, a.deepCopy(), predicates, children); - appendPredicates(type, b.deepCopy(), predicates, children); - settings = a.getSettings(); - } - - predicates.sort(BasePredicate::compareTo); - - MultiPredicate combined = type.makePredicate(children, predicates, a.hasAir || b.hasAir); - combined.setSettings(settings); + MultiPredicate combined = type.makePredicate(List.of(a, b), List.of(), a.hasAir || b.hasAir); + combined.setSettings(null); return combined; } From c1cbce8c602d1076c3373959114c8779b05dbd88 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 00:25:40 -0700 Subject: [PATCH 39/63] oops --- .../gtceu/api/multiblock/MultiPredicate.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 2c949ffca89..49079700bf4 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -488,16 +488,6 @@ private static MultiPredicate combine(MultiPredicate a, Logic type, @Nullable Mu return combined; } - private static void appendPredicates(Logic type, MultiPredicate multiPredicate, - List predicates, List children) { - if (multiPredicate.isSingle() || multiPredicate.isType(type)) { - predicates.addAll(multiPredicate.predicates()); - children.addAll(multiPredicate.children()); - } else { - children.add(multiPredicate); - } - } - public static MultiPredicate empty() { return EMPTY; } From 03561cc9c6c1d97072b4f5bc491a0f2d1b3ee35b Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 00:39:48 -0700 Subject: [PATCH 40/63] mmm yes spot --- .../gtceu/api/multiblock/predicates/SettingsHolder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index 21a2b0d41e8..c8fb473b319 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -1,6 +1,7 @@ package com.gregtechceu.gtceu.api.multiblock.predicates; import com.gregtechceu.gtceu.api.multiblock.PredicateContext; + import org.jetbrains.annotations.ApiStatus; import java.util.function.UnaryOperator; @@ -50,7 +51,7 @@ default void updateSettings(UnaryOperator configurator) { setSettings(configurator.apply(getSettings())); } - /// mutates this object with the given priority + /// mutates this object with the given priority @ApiStatus.Internal default void setPriority(int priority) { updateSettings(s -> s.withPriority(priority)); From 69fc43087a24d53e0aa4d383cfe8aa021a905e48 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 00:45:31 -0700 Subject: [PATCH 41/63] not null settings --- .../com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 49079700bf4..b4e6f30ea4c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -484,7 +484,7 @@ private static MultiPredicate combine(MultiPredicate a, Logic type, @Nullable Mu if (a.isEmpty()) return b; MultiPredicate combined = type.makePredicate(List.of(a, b), List.of(), a.hasAir || b.hasAir); - combined.setSettings(null); + combined.setSettings(PredicateSettings.create()); return combined; } From b80be87beedd4d7581ab91c8f2665ae21a7277a2 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 02:12:02 -0700 Subject: [PATCH 42/63] consider: copying the child --- .../com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index b4e6f30ea4c..5355e742d91 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -483,7 +483,8 @@ private static MultiPredicate combine(MultiPredicate a, Logic type, @Nullable Mu if (b == null || b.isEmpty()) return a; // no op if (a.isEmpty()) return b; - MultiPredicate combined = type.makePredicate(List.of(a, b), List.of(), a.hasAir || b.hasAir); + List children = List.of(a.deepCopy(), b.deepCopy()); + MultiPredicate combined = type.makePredicate(children, List.of(), a.hasAir || b.hasAir); combined.setSettings(PredicateSettings.create()); return combined; } From 4abf7be530b1e88bea62aa8013af48d2a5cada58 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 02:25:42 -0700 Subject: [PATCH 43/63] mark air, any, and empty as not mutable --- .../gtceu/api/multiblock/MultiPredicate.java | 44 +++++++++++++------ .../multiblock/predicates/BasePredicate.java | 20 +++++++-- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 5355e742d91..e77eac7e50c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -23,11 +23,11 @@ public abstract class MultiPredicate implements SettingsHolder { - private static final MultiPredicate EMPTY = of(Logic.OR, List.of()); + private static final MultiPredicate EMPTY = of(Logic.OR, List.of()).markImmutable(); - public static final MultiPredicate AIR = of(BasePredicate.AIR); + public static final MultiPredicate AIR = of(BasePredicate.AIR).markImmutable(); - public static final MultiPredicate ANY = of(BasePredicate.ANY); + public static final MultiPredicate ANY = of(BasePredicate.ANY).markImmutable(); private final List predicates; private final List children; @@ -37,7 +37,6 @@ public abstract class MultiPredicate implements SettingsHolder { private final Logic type; @Getter - @Setter @Accessors(chain = true) private boolean controller; @@ -48,8 +47,8 @@ public abstract class MultiPredicate implements SettingsHolder { @Nullable @Getter - @Setter private MultiPredicate parent; + private boolean mutable = true; /// @param children list of multi predicate children /// @param predicates list of testable predicates, should be sorted already @@ -124,6 +123,7 @@ public boolean testMaxCount(BasePredicate passedPredicate, PredicateContext cont } // go up the parent chain to test settings of parents + private boolean testParents(TestType type, BasePredicate passedPredicate, PredicateContext context) { MultiPredicate parent = passedPredicate.getParent(); while (parent != null) { @@ -134,7 +134,6 @@ private boolean testParents(TestType type, BasePredicate passedPredicate, Predic } return true; } - public List> getCandidates() { List> result = new ArrayList<>(); for (BasePredicate predicate : predicates()) { @@ -262,19 +261,19 @@ protected PredicateSettings getOrCreateSettings() { } @RemapForJS("addTooltip") - @Contract(mutates = "this") public MultiPredicate addTooltips(Component tooltip) { - forEach(p -> p.addTooltips(tooltip)); - forEachChild(mp -> mp.addTooltips(tooltip)); - return this; + var mutated = mutable ? this : deepCopy(); + mutated.forEach(p -> p.addTooltips(tooltip)); + mutated.forEachChild(mp -> mp.addTooltips(tooltip)); + return mutated; } @CheckReturnValue - @Contract(mutates = "this") public MultiPredicate addTooltips(Component... tooltip) { - forEach(p -> Collections.addAll(p.getAdditionalTooltips(), tooltip)); - forEachChild(mp -> mp.addTooltips(tooltip)); - return this; + var mutated = mutable ? this : deepCopy(); + mutated.forEach(p -> Collections.addAll(p.getAdditionalTooltips(), tooltip)); + mutated.forEachChild(mp -> mp.addTooltips(tooltip)); + return mutated; } @Override @@ -286,6 +285,7 @@ public void updateSettings(UnaryOperator configurator) { /// @param shouldCreate if settings should be able to be created for this multi predicate @ApiStatus.Internal public void updateSettings(UnaryOperator configurator, boolean shouldCreate) { + if (!mutable) return; if (isSingle()) { // the idea is that if we only have a single predicate, we mutate that predicate instead of ourselves // as we're basically the same as that predicate @@ -311,11 +311,27 @@ public void updateSettings(UnaryOperator configurator, boolea protected void onSettingsChanged() {} + private MultiPredicate markImmutable() { + this.mutable = false; + return this; + } + public void setSettings(@Nullable PredicateSettings settings) { + if (!mutable) return; this.settings = settings == null ? null : settings.copy(); onSettingsChanged(); } + public void setParent(MultiPredicate parent) { + if (mutable) this.parent = parent; + } + + public MultiPredicate setController(boolean controller) { + var mutated = mutable ? this : deepCopy(); + mutated.controller = controller; + return mutated; + } + /* * MUTATE AND COPY */ diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index 34e0865b531..8e332a88c5e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -19,11 +19,15 @@ public abstract class BasePredicate implements Comparable, Settin public static final BasePredicate AIR = new PredicateBuilder("Air") .predicate(ctx -> ctx.state().isAir()) - .build(); + .build() + .markImmutable(); public static final BasePredicate ANY = new PredicateBuilder("Any") .predicate(ctx -> true) - .build(); + .build() + .markImmutable(); + + private boolean mutable = true; @Getter(lazy = true) private final List candidates = computeCandidates(); @@ -127,6 +131,12 @@ public int compareTo(BasePredicate o) { return this.settings.comparePriority(o.settings); } + + private BasePredicate markImmutable() { + this.mutable = false; + return this; + } + @Override public boolean hasSettings() { return true; @@ -144,7 +154,9 @@ protected void copyTo(BasePredicate other) { @Override public BasePredicate withSettings(UnaryOperator configurator) { BasePredicate copy = copy(); - copy.updateSettings(configurator); + if (mutable) { + copy.updateSettings(configurator); + } return copy; } @@ -152,7 +164,7 @@ public BasePredicate withSettings(UnaryOperator configurator) @Override public void setSettings(PredicateSettings settings) { - this.settings = settings; + if (mutable) this.settings = settings; } public void addTooltips(Component tooltip) { From 45833f651ffed15dc56fcc1cdf9735bbec9bcbfe Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 02:28:15 -0700 Subject: [PATCH 44/63] forgor some spots --- .../gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 3 +-- .../gtceu/api/multiblock/predicates/BasePredicate.java | 9 +++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index e77eac7e50c..7bc917c05a8 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -10,11 +10,9 @@ import dev.latvian.mods.rhino.util.RemapForJS; import lombok.Getter; -import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.CheckReturnValue; -import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -134,6 +132,7 @@ private boolean testParents(TestType type, BasePredicate passedPredicate, Predic } return true; } + public List> getCandidates() { List> result = new ArrayList<>(); for (BasePredicate predicate : predicates()) { diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index 8e332a88c5e..50391262520 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -9,7 +9,6 @@ import net.minecraft.world.item.ItemStack; import lombok.Getter; -import lombok.Setter; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -35,7 +34,6 @@ public abstract class BasePredicate implements Comparable, Settin @Getter protected PredicateSettings settings = PredicateSettings.create(); - @Setter private @Nullable MultiPredicate parent; @Getter @@ -131,7 +129,6 @@ public int compareTo(BasePredicate o) { return this.settings.comparePriority(o.settings); } - private BasePredicate markImmutable() { this.mutable = false; return this; @@ -167,7 +164,11 @@ public void setSettings(PredicateSettings settings) { if (mutable) this.settings = settings; } + public void setParent(@Nullable MultiPredicate parent) { + if (mutable) this.parent = parent; + } + public void addTooltips(Component tooltip) { - this.additionalTooltips.add(tooltip); + if (mutable) this.additionalTooltips.add(tooltip); } } From 17c9500c1d211874131f970d0dd7d492ea263a34 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 03:07:34 -0700 Subject: [PATCH 45/63] address comments ensure copied predicates are sorted --- .../gtceu/api/multiblock/MultiPredicate.java | 18 ++++++------------ .../gtceu/api/multiblock/XorPredicate.java | 2 +- .../electric/CentralMonitorMachine.java | 4 ++-- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 7bc917c05a8..53e75002911 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -277,13 +277,6 @@ public MultiPredicate addTooltips(Component... tooltip) { @Override public void updateSettings(UnaryOperator configurator) { - this.updateSettings(configurator, false); - } - - /// Mutates this multi predicate with the configured settings - /// @param shouldCreate if settings should be able to be created for this multi predicate - @ApiStatus.Internal - public void updateSettings(UnaryOperator configurator, boolean shouldCreate) { if (!mutable) return; if (isSingle()) { // the idea is that if we only have a single predicate, we mutate that predicate instead of ourselves @@ -291,9 +284,7 @@ public void updateSettings(UnaryOperator configurator, boolea predicates().get(0).updateSettings(configurator); onSettingsChanged(); } else { - // shouldCreate should be true when updating settings through withSettings() - // otherwise it is false - PredicateSettings settings = shouldCreate ? getOrCreateSettings() : getSettings(); + PredicateSettings settings = getSettings(); if (settings != null) { setSettings(Objects.requireNonNull(configurator.apply(settings))); } else { @@ -337,7 +328,8 @@ public MultiPredicate setController(boolean controller) { @CheckReturnValue protected MultiPredicate deepCopy() { - List copiedPredicates = predicates().stream().map(BasePredicate::copy).toList(); + List copiedPredicates = predicates().stream() + .map(BasePredicate::copy).sorted(BasePredicate::compareTo).toList(); List copiedChildren = children().stream().map(MultiPredicate::deepCopy).toList(); MultiPredicate copy = this.type.makePredicate(copiedChildren, copiedPredicates, this.hasAir); copy.setSettings(this.settings); @@ -509,7 +501,9 @@ public static MultiPredicate empty() { } public static MultiPredicate of(BasePredicate predicate) { - return Logic.OR.makePredicate(predicate, predicate == BasePredicate.AIR); + MultiPredicate multiPredicate = Logic.OR.makePredicate(predicate, predicate == BasePredicate.AIR); + multiPredicate.setSettings(PredicateSettings.create()); + return multiPredicate; } /// @return A multi predicate with default settings diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java index b9ed3550dd1..01cf84ff2e6 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java @@ -103,7 +103,7 @@ private static void xorError(PredicateContext context, BasePredicate predicate, private static boolean isNoneValid(MultiPredicate multiPredicate) { PredicateSettings settings = multiPredicate.getSettings(); - if (settings != null) return settings.isNoneValid(); + if (settings != null && settings.isNoneValid()) return true; for (BasePredicate predicate : multiPredicate.predicates()) { if (predicate.getSettings().isNoneValid()) { diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java index 0205691f8bf..db1e10b8005 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java @@ -78,8 +78,8 @@ public static MultiPredicate getMultiPredicate() { .withGlobalMinMax(1, 2).withPreviewCount(1)) .and(Predicates.abilities(PartAbility.DATA_ACCESS).withPreviewCount(1) .or(Predicates.machines(GTMachines.BATTERY_BUFFER_4).withPreviewCount(0)) - .or(Predicates.machines(GTMachines.BATTERY_BUFFER_16)) - .withMaxGlobalLimited(4, 0)) + .or(Predicates.machines(GTMachines.BATTERY_BUFFER_16).withPreviewCount(0)) + .withMaxGlobalLimited(4)) .and(Predicates.machines(GTMachines.HULL).withPreviewCount(0)) .and(Predicates.machines(GTMachines.ADVANCED_MONITOR)) .and(Predicates.blocks(GTBlocks.CASING_ALUMINIUM_FROSTPROOF.get())); From f23cb835ef4341763f189ae489037cfea8f47daa Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 03:51:27 -0700 Subject: [PATCH 46/63] also sort children on copy idk why i did this mutable check in BasePredicate --- .../com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 3 ++- .../gtceu/api/multiblock/predicates/BasePredicate.java | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 53e75002911..c6158ae5810 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -330,7 +330,8 @@ public MultiPredicate setController(boolean controller) { protected MultiPredicate deepCopy() { List copiedPredicates = predicates().stream() .map(BasePredicate::copy).sorted(BasePredicate::compareTo).toList(); - List copiedChildren = children().stream().map(MultiPredicate::deepCopy).toList(); + List copiedChildren = children().stream().map(MultiPredicate::deepCopy) + .sorted(Comparator.comparingInt(SettingsHolder::getPriority)).toList(); MultiPredicate copy = this.type.makePredicate(copiedChildren, copiedPredicates, this.hasAir); copy.setSettings(this.settings); copy.setController(this.controller); diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index 50391262520..835a700d641 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -151,9 +151,7 @@ protected void copyTo(BasePredicate other) { @Override public BasePredicate withSettings(UnaryOperator configurator) { BasePredicate copy = copy(); - if (mutable) { - copy.updateSettings(configurator); - } + copy.updateSettings(configurator); return copy; } From 63c3ff2c877fef6c126b83a88bd2e07b2904e312 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 13:45:12 -0700 Subject: [PATCH 47/63] remove unused method --- .../gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index c6158ae5810..e136778993f 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -252,13 +252,6 @@ public boolean hasSettings() { * MUTATE AND DO NOT COPY */ - protected PredicateSettings getOrCreateSettings() { - if (this.settings == null) { - this.settings = PredicateSettings.create(); - } - return this.settings; - } - @RemapForJS("addTooltip") public MultiPredicate addTooltips(Component tooltip) { var mutated = mutable ? this : deepCopy(); From 55529e06ffe5c69b1bd3bc061ba3078856490837 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 14:03:32 -0700 Subject: [PATCH 48/63] javadoc and annotate with methods add min and max priority references return min priority when without settings sort high to low --- .../gtceu/api/multiblock/MultiPredicate.java | 12 +++++++++--- .../predicates/PredicateSettings.java | 3 +++ .../multiblock/predicates/SettingsHolder.java | 17 ++++++++++++++++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index e136778993f..1f8aaa56933 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -322,9 +322,15 @@ public MultiPredicate setController(boolean controller) { @CheckReturnValue protected MultiPredicate deepCopy() { List copiedPredicates = predicates().stream() - .map(BasePredicate::copy).sorted(BasePredicate::compareTo).toList(); - List copiedChildren = children().stream().map(MultiPredicate::deepCopy) - .sorted(Comparator.comparingInt(SettingsHolder::getPriority)).toList(); + .map(BasePredicate::copy) + // sort high to low + .sorted(Collections.reverseOrder(BasePredicate::compareTo)) + .toList(); + List copiedChildren = children().stream() + .map(MultiPredicate::deepCopy) + // sort high to low + .sorted(Comparator.>comparingInt(SettingsHolder::getPriority).reversed()) + .toList(); MultiPredicate copy = this.type.makePredicate(copiedChildren, copiedPredicates, this.hasAir); copy.setSettings(this.settings); copy.setController(this.controller); diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java index e804e79d578..cef2e6e2fb7 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java @@ -22,6 +22,9 @@ public record PredicateSettings(int priority, ).apply(instance, PredicateSettings::new)); // spotless:on + public static final int MAX_PRIORITY = Integer.MAX_VALUE; + public static final int MIN_PRIORITY = Integer.MIN_VALUE; + // helper for xor public boolean isNoneValid() { return minCount <= 0 && minSliceCount <= 0; diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index c8fb473b319..8524a7c2a79 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -3,6 +3,7 @@ import com.gregtechceu.gtceu.api.multiblock.PredicateContext; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.CheckReturnValue; import java.util.function.UnaryOperator; @@ -14,7 +15,7 @@ public interface SettingsHolder> { boolean hasSettings(); default int getPriority() { - return hasSettings() ? getSettings().priority() : -1; + return hasSettings() ? getSettings().priority() : PredicateSettings.MIN_PRIORITY; } default int getMinCount() { @@ -97,30 +98,44 @@ default void setDisableRenderFormed(boolean disableRenderFormed) { /// @return a copy with these settings applied S withSettings(UnaryOperator configurator); + /// @return a copy of this object with the given priority + @CheckReturnValue default S withPriority(int priority) { return withSettings(s -> s.withPriority(priority)); } + /// @return a copy of this object with the given min global count + @CheckReturnValue default S withMinCount(int minCount) { return withSettings(s -> s.withMinCount(minCount)); } + /// @return a copy of this object with the given max global count + @CheckReturnValue default S withMaxCount(int maxCount) { return withSettings(s -> s.withMaxCount(maxCount)); } + /// @return a copy of this object with the given min slice count + @CheckReturnValue default S withMinSliceCount(int minSliceCount) { return withSettings(s -> s.withMinSliceCount(minSliceCount)); } + /// @return a copy of this object with the given max slice count + @CheckReturnValue default S withMaxSliceCount(int maxSliceCount) { return withSettings(s -> s.withMaxSliceCount(maxSliceCount)); } + /// @return a copy of this object with the given preview count + @CheckReturnValue default S withPreviewCount(int previewCount) { return withSettings(s -> s.withPreviewCount(previewCount)); } + /// @return a copy of this object with the given render form disabled + @CheckReturnValue default S withDisableRenderFormed(boolean disableRenderFormed) { return withSettings(s -> s.withDisableRenderFormed(disableRenderFormed)); } From 566893d41b26b69f8742c2bed32f72074f9ef41e Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 14:07:51 -0700 Subject: [PATCH 49/63] move comparable interface to SettingsHolder --- .../gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 6 +++--- .../gtceu/api/multiblock/predicates/BasePredicate.java | 7 +------ .../gtceu/api/multiblock/predicates/SettingsHolder.java | 7 ++++++- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 1f8aaa56933..568efc0c852 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -323,13 +323,13 @@ public MultiPredicate setController(boolean controller) { protected MultiPredicate deepCopy() { List copiedPredicates = predicates().stream() .map(BasePredicate::copy) - // sort high to low + // sort high to low (descending) .sorted(Collections.reverseOrder(BasePredicate::compareTo)) .toList(); List copiedChildren = children().stream() .map(MultiPredicate::deepCopy) - // sort high to low - .sorted(Comparator.>comparingInt(SettingsHolder::getPriority).reversed()) + // sort high to low (descending) + .sorted(Collections.reverseOrder(MultiPredicate::compareTo)) .toList(); MultiPredicate copy = this.type.makePredicate(copiedChildren, copiedPredicates, this.hasAir); copy.setSettings(this.settings); diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index 835a700d641..f117baa4693 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -14,7 +14,7 @@ import java.util.*; import java.util.function.UnaryOperator; -public abstract class BasePredicate implements Comparable, SettingsHolder { +public abstract class BasePredicate implements SettingsHolder { public static final BasePredicate AIR = new PredicateBuilder("Air") .predicate(ctx -> ctx.state().isAir()) @@ -124,11 +124,6 @@ public String toString() { return builder.toString(); } - @Override - public int compareTo(BasePredicate o) { - return this.settings.comparePriority(o.settings); - } - private BasePredicate markImmutable() { this.mutable = false; return this; diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index 8524a7c2a79..d8b5940ce2a 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -7,7 +7,7 @@ import java.util.function.UnaryOperator; -public interface SettingsHolder> { +public interface SettingsHolder> extends Comparable> { // getters PredicateSettings getSettings(); @@ -161,6 +161,11 @@ default boolean testSliceMax(int count) { return getMaxSliceCount() == -1 || count <= getMaxSliceCount(); } + @Override + default int compareTo(SettingsHolder o) { + return Integer.compare(getPriority(), o.getPriority()); + } + enum TestType { GLOBAL_MIN, From 53bd8a2a134e8b383c5901b5750d7b75a33b060e Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 14:09:45 -0700 Subject: [PATCH 50/63] delete unused method --- .../gtceu/api/multiblock/predicates/PredicateSettings.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java index cef2e6e2fb7..eef8b431e34 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/PredicateSettings.java @@ -41,10 +41,6 @@ public PredicateSettings copy() { this.disableRenderFormed); } - public int comparePriority(PredicateSettings other) { - return Integer.compare(this.priority, other.priority); - } - public static PredicateSettings create() { return new PredicateSettings( 0, From 92091f01b93e969d2da7468c4aead83661d30f46 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 14:41:26 -0700 Subject: [PATCH 51/63] problem with immutable predicates: they don't set their parents copy air and any predicates mark fields as internal actually is there any point in the immutable field if we also copy? --- .../gtceu/api/multiblock/MultiPredicate.java | 32 ++++++++++++------- .../gtceu/api/multiblock/Predicates.java | 4 +-- .../multiblock/predicates/BasePredicate.java | 23 +++++++++++++ 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 568efc0c852..51796438b92 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -6,6 +6,8 @@ import com.gregtechceu.gtceu.api.multiblock.predicates.SettingsHolder; import com.gregtechceu.gtceu.api.multiblock.util.BlockInfo; +import lombok.AccessLevel; +import lombok.Setter; import net.minecraft.network.chat.Component; import dev.latvian.mods.rhino.util.RemapForJS; @@ -23,14 +25,30 @@ public abstract class MultiPredicate implements SettingsHolder { private static final MultiPredicate EMPTY = of(Logic.OR, List.of()).markImmutable(); - public static final MultiPredicate AIR = of(BasePredicate.AIR).markImmutable(); + /// use {@link Predicates#air()} instead + @ApiStatus.Internal + public static final MultiPredicate AIR = of(BasePredicate.AIR) + .isAir(true).markImmutable(); - public static final MultiPredicate ANY = of(BasePredicate.ANY).markImmutable(); + /// use {@link Predicates#any()} instead + @ApiStatus.Internal + public static final MultiPredicate ANY = of(BasePredicate.ANY) + .isAny(true).markImmutable(); private final List predicates; private final List children; private final boolean hasAir; + @Accessors(fluent = true) + @Setter(AccessLevel.PRIVATE) + @Getter + private boolean isAir = false; + + @Accessors(fluent = true) + @Setter(AccessLevel.PRIVATE) + @Getter + private boolean isAny = false; + @Getter private final Logic type; @@ -169,14 +187,6 @@ public boolean isEmpty() { return this == EMPTY; } - public boolean isAny() { - return this == ANY; - } - - public boolean isAir() { - return this == AIR; - } - public boolean hasAir() { return this.hasAir; } @@ -509,7 +519,7 @@ public static MultiPredicate of(BasePredicate predicate) { /// @return A multi predicate with default settings private static MultiPredicate of(Logic type, List predicates) { MultiPredicate predicate = type.makePredicate(List.of(), predicates, predicates.stream() - .anyMatch(p -> p == BasePredicate.AIR)); + .anyMatch(BasePredicate::isAir)); predicate.setSettings(PredicateSettings.create()); return predicate; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/Predicates.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/Predicates.java index f93dd0eb2fa..e2e40ce2eb4 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/Predicates.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/Predicates.java @@ -202,11 +202,11 @@ public static MultiPredicate fluidTag(TagKey tag) { } public static MultiPredicate any() { - return MultiPredicate.ANY; + return MultiPredicate.ANY.deepCopy(); } public static MultiPredicate air() { - return MultiPredicate.AIR; + return MultiPredicate.AIR.deepCopy(); } public static MultiPredicate abilities(PartAbility ability) { diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index f117baa4693..923fb8851da 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -2,13 +2,18 @@ import com.gregtechceu.gtceu.api.multiblock.MultiPredicate; import com.gregtechceu.gtceu.api.multiblock.PredicateContext; +import com.gregtechceu.gtceu.api.multiblock.Predicates; import com.gregtechceu.gtceu.api.multiblock.error.SinglePredicateError; import com.gregtechceu.gtceu.api.multiblock.util.BlockInfo; +import lombok.AccessLevel; +import lombok.Setter; +import lombok.experimental.Accessors; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import lombok.Getter; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -16,14 +21,20 @@ public abstract class BasePredicate implements SettingsHolder { + /// use {@link Predicates#air()} instead + @ApiStatus.Internal public static final BasePredicate AIR = new PredicateBuilder("Air") .predicate(ctx -> ctx.state().isAir()) .build() + .isAir(true) .markImmutable(); + /// use {@link Predicates#any()} instead + @ApiStatus.Internal public static final BasePredicate ANY = new PredicateBuilder("Any") .predicate(ctx -> true) .build() + .isAny(true) .markImmutable(); private boolean mutable = true; @@ -39,6 +50,16 @@ public abstract class BasePredicate implements SettingsHolder { @Getter private final List additionalTooltips = new ArrayList<>(); + @Accessors(fluent = true) + @Setter(AccessLevel.PRIVATE) + @Getter + private boolean isAir = false; + + @Accessors(fluent = true) + @Setter(AccessLevel.PRIVATE) + @Getter + private boolean isAny = false; + public MultiPredicate getParent() { return Objects.requireNonNull(this.parent); } @@ -139,6 +160,8 @@ public boolean hasSettings() { protected void copyTo(BasePredicate other) { other.setSettings(this.settings.copy()); other.additionalTooltips.addAll(this.additionalTooltips); + other.isAir = this.isAir; + other.isAny = this.isAny; } // COPY AND MUTATE From 8073f408ca670a44ed4c75f6a9b350669e56f14e Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 14:42:55 -0700 Subject: [PATCH 52/63] why spots --- .../gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 4 ++-- .../gtceu/api/multiblock/predicates/BasePredicate.java | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 51796438b92..1f7f7d21eae 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -6,12 +6,12 @@ import com.gregtechceu.gtceu.api.multiblock.predicates.SettingsHolder; import com.gregtechceu.gtceu.api.multiblock.util.BlockInfo; -import lombok.AccessLevel; -import lombok.Setter; import net.minecraft.network.chat.Component; import dev.latvian.mods.rhino.util.RemapForJS; +import lombok.AccessLevel; import lombok.Getter; +import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.CheckReturnValue; diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index 923fb8851da..be1d661ca30 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -2,17 +2,16 @@ import com.gregtechceu.gtceu.api.multiblock.MultiPredicate; import com.gregtechceu.gtceu.api.multiblock.PredicateContext; -import com.gregtechceu.gtceu.api.multiblock.Predicates; import com.gregtechceu.gtceu.api.multiblock.error.SinglePredicateError; import com.gregtechceu.gtceu.api.multiblock.util.BlockInfo; -import lombok.AccessLevel; -import lombok.Setter; -import lombok.experimental.Accessors; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; +import lombok.AccessLevel; import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; From ed9f8e2db8e18a6bef2f9487d8580aa0900d3432 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Mon, 7 Sep 2026 16:59:29 -0700 Subject: [PATCH 53/63] move mp settings check into mp --- .../gtceu/api/multiblock/AndPredicate.java | 36 ++++++------------- .../gtceu/api/multiblock/MultiPredicate.java | 9 +++-- .../gtceu/api/multiblock/OrPredicate.java | 36 ++++++------------- .../gtceu/api/multiblock/XorPredicate.java | 12 ++----- 4 files changed, 28 insertions(+), 65 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java index e28fc909a7c..38b02e4b74e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/AndPredicate.java @@ -12,47 +12,31 @@ public AndPredicate(List children, List predicate @Override protected boolean testGlobalMin(PredicateContext ctx) { - boolean result = true; for (BasePredicate predicate : predicates()) { if (!predicate.testGlobalMin(ctx)) { - result = false; - break; + return false; } } - if (result) { - for (MultiPredicate child : children()) { - if (!child.testGlobalMin(ctx)) { - result = false; - break; - } + for (MultiPredicate child : children()) { + if (!child.testGlobalMin(ctx)) { + return false; } } - if (result) { - result = !hasSettings() || TestType.GLOBAL_MIN.testSettings(this, ctx); - } - return result; + return true; } @Override protected boolean testSliceMin(PredicateContext ctx) { - boolean result = true; for (BasePredicate predicate : predicates()) { if (!predicate.testSliceMin(ctx)) { - result = false; - break; + return false; } } - if (result) { - for (MultiPredicate child : children()) { - if (!child.testSliceMin(ctx)) { - result = false; - break; - } + for (MultiPredicate child : children()) { + if (!child.testSliceMin(ctx)) { + return false; } } - if (result) { - result = !hasSettings() || TestType.SLICE_MIN.testSettings(this, ctx); - } - return result; + return true; } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 1f7f7d21eae..8502dd3fc7a 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -106,7 +106,9 @@ protected void onError(PredicateContext ctx) { /// Usually used for testing the global min of predicates public final boolean postGlobalTest(PredicateContext ctx) { ctx.setStage(PredicateContext.PredicateStage.GLOBAL_MIN); - if (testGlobalMin(ctx)) return true; + if (testGlobalMin(ctx) && TestType.GLOBAL_MIN.testSettings(this, ctx)) { + return true; + } for (Component content : getDescriptiveContents()) { ctx.appendError(PatternStringError.of(content)); } @@ -119,7 +121,9 @@ public final boolean postGlobalTest(PredicateContext ctx) { /// Usually used for testing the slice min of predicates public final boolean postSliceTest(PredicateContext ctx) { ctx.setStage(PredicateContext.PredicateStage.SLICE_MIN); - if (testSliceMin(ctx)) return true; + if (testSliceMin(ctx) && TestType.SLICE_MIN.testSettings(this, ctx)) { + return true; + } for (Component content : getDescriptiveContents()) { ctx.appendError(PatternStringError.of(content)); } @@ -139,7 +143,6 @@ public boolean testMaxCount(BasePredicate passedPredicate, PredicateContext cont } // go up the parent chain to test settings of parents - private boolean testParents(TestType type, BasePredicate passedPredicate, PredicateContext context) { MultiPredicate parent = passedPredicate.getParent(); while (parent != null) { diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java index 2e4509f1299..1cee4a452e2 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/OrPredicate.java @@ -12,47 +12,31 @@ public OrPredicate(List children, List predicates @Override protected boolean testGlobalMin(PredicateContext ctx) { - boolean result = false; for (BasePredicate predicate : predicates()) { if (predicate.testGlobalMin(ctx)) { - result = true; - break; + return true; } } - if (!result) { - for (MultiPredicate child : children()) { - if (child.testGlobalMin(ctx)) { - result = true; - break; - } + for (MultiPredicate child : children()) { + if (child.testGlobalMin(ctx)) { + return true; } } - if (result) { - result = TestType.GLOBAL_MIN.testSettings(this, ctx); - } - return result; + return false; } @Override protected boolean testSliceMin(PredicateContext ctx) { - boolean result = false; for (BasePredicate predicate : predicates()) { if (predicate.testSliceMin(ctx)) { - result = true; - break; + return true; } } - if (!result) { - for (MultiPredicate child : children()) { - if (child.testSliceMin(ctx)) { - result = true; - break; - } + for (MultiPredicate child : children()) { + if (child.testSliceMin(ctx)) { + return true; } } - if (result) { - result = TestType.SLICE_MIN.testSettings(this, ctx); - } - return result; + return false; } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java index 01cf84ff2e6..4aa981b9624 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java @@ -68,21 +68,13 @@ protected void onSettingsChanged() { @Override protected boolean testGlobalMin(PredicateContext ctx) { if (passedPredicate == null && noneValid) return true; - boolean passed = passedPredicate != null && passedPredicate.testGlobalMin(ctx); - if (passed) { - passed = TestType.GLOBAL_MIN.testSettings(this, ctx); - } - return passed; + return passedPredicate != null && passedPredicate.testGlobalMin(ctx); } @Override protected boolean testSliceMin(PredicateContext ctx) { if (passedPredicate == null && noneValid) return true; - boolean passed = passedPredicate != null && passedPredicate.testSliceMin(ctx); - if (passed && hasSettings()) { - passed = TestType.SLICE_MIN.testSettings(this, ctx); - } - return passed; + return passedPredicate != null && passedPredicate.testSliceMin(ctx); } @Override From c5c6093950ef2e96126dee3edb034ec6fe550797 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Tue, 8 Sep 2026 01:02:07 -0700 Subject: [PATCH 54/63] comment to javadoc --- .../gtceu/api/multiblock/predicates/SettingsHolder.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index d8b5940ce2a..c2d6032abfe 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -173,8 +173,9 @@ enum TestType { SLICE_MIN, SLICE_MAX; - // should return true if null settings - // otherwise test settings + /// @implNote The count of the holder WILL be incremented for glabal/slice max + /// @return {@code true}, if the holder does not have settings, + /// or passes their settings according to the type public boolean testSettings(SettingsHolder holder, PredicateContext ctx) { if (!holder.hasSettings()) return true; return switch (this) { From 607aa05449f3da90d3974b1535f17ead7042029f Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Tue, 8 Sep 2026 01:07:21 -0700 Subject: [PATCH 55/63] add additional test method to SettingsHolder --- .../gtceu/api/multiblock/MultiPredicate.java | 6 +++--- .../multiblock/predicates/SettingsHolder.java | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 8502dd3fc7a..06e20c38d8b 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -106,7 +106,7 @@ protected void onError(PredicateContext ctx) { /// Usually used for testing the global min of predicates public final boolean postGlobalTest(PredicateContext ctx) { ctx.setStage(PredicateContext.PredicateStage.GLOBAL_MIN); - if (testGlobalMin(ctx) && TestType.GLOBAL_MIN.testSettings(this, ctx)) { + if (testGlobalMin(ctx) && TestType.GLOBAL_MIN.testCounts(this, ctx)) { return true; } for (Component content : getDescriptiveContents()) { @@ -121,7 +121,7 @@ public final boolean postGlobalTest(PredicateContext ctx) { /// Usually used for testing the slice min of predicates public final boolean postSliceTest(PredicateContext ctx) { ctx.setStage(PredicateContext.PredicateStage.SLICE_MIN); - if (testSliceMin(ctx) && TestType.SLICE_MIN.testSettings(this, ctx)) { + if (testSliceMin(ctx) && TestType.SLICE_MIN.testCounts(this, ctx)) { return true; } for (Component content : getDescriptiveContents()) { @@ -146,7 +146,7 @@ public boolean testMaxCount(BasePredicate passedPredicate, PredicateContext cont private boolean testParents(TestType type, BasePredicate passedPredicate, PredicateContext context) { MultiPredicate parent = passedPredicate.getParent(); while (parent != null) { - if (!type.testSettings(parent, context)) { + if (!type.testAndIncrement(parent, context)) { return false; } parent = parent.getParent(); diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index c2d6032abfe..dffabb51fa9 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -176,13 +176,24 @@ enum TestType { /// @implNote The count of the holder WILL be incremented for glabal/slice max /// @return {@code true}, if the holder does not have settings, /// or passes their settings according to the type - public boolean testSettings(SettingsHolder holder, PredicateContext ctx) { + public boolean testAndIncrement(SettingsHolder holder, PredicateContext ctx) { if (!holder.hasSettings()) return true; return switch (this) { - case GLOBAL_MIN -> holder.testGlobalMin(ctx.getGlobalCount(holder)); case GLOBAL_MAX -> holder.testGlobalMax(ctx.incrementGlobalCount(holder)); - case SLICE_MIN -> holder.testSliceMin(ctx.getSliceCount(holder)); case SLICE_MAX -> holder.testSliceMax(ctx.incrementSliceCount(holder)); + default -> testCounts(holder, ctx); + }; + } + + /// @return {@code true}, if the holder does not have settings, + /// or passes their settings according to the type + public boolean testCounts(SettingsHolder holder, PredicateContext ctx) { + if (!holder.hasSettings()) return true; + return switch (this) { + case GLOBAL_MAX -> holder.testGlobalMax(ctx.getGlobalCount(holder)); + case SLICE_MAX -> holder.testSliceMax(ctx.getSliceCount(holder)); + case GLOBAL_MIN -> holder.testGlobalMin(ctx.getGlobalCount(holder)); + case SLICE_MIN -> holder.testSliceMin(ctx.getSliceCount(holder)); }; } } From e55483e871d536a350bc3e64be5e514a5281b2e6 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Tue, 8 Sep 2026 04:24:49 -0700 Subject: [PATCH 56/63] attempt equals impl at least merge if b is single --- .../gtceu/api/multiblock/MultiPredicate.java | 16 ++++++++++++++-- .../gtceu/api/multiblock/PredicateContext.java | 4 ++-- .../multiblock/predicates/BasePredicate.java | 17 +++++++++++++++-- .../predicates/TestablePredicate.java | 15 +++++++++++++++ 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 06e20c38d8b..55a6649d93f 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -20,6 +20,7 @@ import java.util.*; import java.util.function.Consumer; import java.util.function.UnaryOperator; +import java.util.stream.Stream; public abstract class MultiPredicate implements SettingsHolder { @@ -503,8 +504,19 @@ private static MultiPredicate combine(MultiPredicate a, Logic type, @Nullable Mu if (b == null || b.isEmpty()) return a; // no op if (a.isEmpty()) return b; - List children = List.of(a.deepCopy(), b.deepCopy()); - MultiPredicate combined = type.makePredicate(children, List.of(), a.hasAir || b.hasAir); + List children; + List predicates; + if (b.isSingle()) { + predicates = Stream.concat(a.predicates().stream(), Stream.of(b.predicates().get(0))) + .map(BasePredicate::copy) + .toList(); + children = a.children().stream().map(MultiPredicate::deepCopy).toList(); + } else { + predicates = List.of(); + children = List.of(a.deepCopy(), b.deepCopy()); + } + + MultiPredicate combined = type.makePredicate(children, predicates, a.hasAir || b.hasAir); combined.setSettings(PredicateSettings.create()); return combined; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java index 1e4c30d55fa..c9bd2865487 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateContext.java @@ -29,8 +29,8 @@ public class PredicateContext { private final PatternState state; @Getter protected CurrentBlockInfo currentBlockInfo = new CurrentBlockInfo(); - protected final Object2IntMap> globalCount = new Object2IntOpenHashMap<>(); - protected final Object2IntMap> layerCount = new Object2IntOpenHashMap<>(); + protected final Object2IntMap> globalCount = new Object2IntArrayMap<>(); + protected final Object2IntMap> layerCount = new Object2IntArrayMap<>(); private final Int2ObjectMap> sliceErrors = new Int2ObjectAVLTreeMap<>( IntComparators.NATURAL_COMPARATOR); diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index be1d661ca30..816491224ad 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -20,7 +20,7 @@ public abstract class BasePredicate implements SettingsHolder { - /// use {@link Predicates#air()} instead + /// use {@link com.gregtechceu.gtceu.api.multiblock.Predicates#air() Predicates.air()} instead @ApiStatus.Internal public static final BasePredicate AIR = new PredicateBuilder("Air") .predicate(ctx -> ctx.state().isAir()) @@ -28,7 +28,7 @@ public abstract class BasePredicate implements SettingsHolder { .isAir(true) .markImmutable(); - /// use {@link Predicates#any()} instead + /// use {@link com.gregtechceu.gtceu.api.multiblock.Predicates#any() Predicates.any()} instead @ApiStatus.Internal public static final BasePredicate ANY = new PredicateBuilder("Any") .predicate(ctx -> true) @@ -144,6 +144,19 @@ public String toString() { return builder.toString(); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof BasePredicate predicate)) return false; + return Objects.equals(getCandidates(), predicate.candidates) && + Objects.equals(settings, predicate.settings); + } + + @Override + public int hashCode() { + return Objects.hash(getCandidates(), settings); + } + private BasePredicate markImmutable() { this.mutable = false; return this; diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java index e8862f177ca..0e636a4d728 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; @@ -93,6 +94,20 @@ public boolean test(PredicateContext ctx) { return this.predicate.test(ctx); } + @Override + public boolean equals(Object o) { + if (!(o instanceof TestablePredicate that)) return false; + if (!super.equals(o)) return false; + return Objects.equals(contents, that.contents) && + Objects.equals(name, that.name) && + Objects.equals(predicate, that.predicate); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), contents, name); + } + @Override public BasePredicate copy() { TestablePredicate copy = new TestablePredicate(this.name, this.predicate, this.candidates, this.contents, From 2225a966df8aefec99eb1a4af17be0728c8c7fd6 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:35:53 -0700 Subject: [PATCH 57/63] what if... don't copy? also don't increment count for -1 global/slice max --- .../gregtechceu/gtceu/api/multiblock/MultiPredicate.java | 6 +++--- .../gtceu/api/multiblock/predicates/SettingsHolder.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 55a6649d93f..9efc7690641 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -508,12 +508,12 @@ private static MultiPredicate combine(MultiPredicate a, Logic type, @Nullable Mu List predicates; if (b.isSingle()) { predicates = Stream.concat(a.predicates().stream(), Stream.of(b.predicates().get(0))) - .map(BasePredicate::copy) .toList(); - children = a.children().stream().map(MultiPredicate::deepCopy).toList(); + children = a.children().stream() + .toList(); } else { predicates = List.of(); - children = List.of(a.deepCopy(), b.deepCopy()); + children = List.of(a, b); } MultiPredicate combined = type.makePredicate(children, predicates, a.hasAir || b.hasAir); diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index dffabb51fa9..67a7ff0d8c6 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -179,8 +179,8 @@ enum TestType { public boolean testAndIncrement(SettingsHolder holder, PredicateContext ctx) { if (!holder.hasSettings()) return true; return switch (this) { - case GLOBAL_MAX -> holder.testGlobalMax(ctx.incrementGlobalCount(holder)); - case SLICE_MAX -> holder.testSliceMax(ctx.incrementSliceCount(holder)); + case GLOBAL_MAX -> holder.getMaxCount() == -1 || holder.testGlobalMax(ctx.incrementGlobalCount(holder)); + case SLICE_MAX -> holder.getMaxSliceCount() == -1 || holder.testSliceMax(ctx.incrementSliceCount(holder)); default -> testCounts(holder, ctx); }; } From e36a6df87fb759fac5e0aa39e2a18e8771ef4540 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Tue, 8 Sep 2026 17:01:30 -0700 Subject: [PATCH 58/63] grr spots --- .../gtceu/api/multiblock/predicates/SettingsHolder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index 67a7ff0d8c6..9a2d44636e3 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -180,7 +180,8 @@ public boolean testAndIncrement(SettingsHolder holder, PredicateContext ctx) if (!holder.hasSettings()) return true; return switch (this) { case GLOBAL_MAX -> holder.getMaxCount() == -1 || holder.testGlobalMax(ctx.incrementGlobalCount(holder)); - case SLICE_MAX -> holder.getMaxSliceCount() == -1 || holder.testSliceMax(ctx.incrementSliceCount(holder)); + case SLICE_MAX -> holder.getMaxSliceCount() == -1 || + holder.testSliceMax(ctx.incrementSliceCount(holder)); default -> testCounts(holder, ctx); }; } From d415be3cc67e6f39a1bd7efa007878b3e0ed32f6 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Wed, 9 Sep 2026 00:07:48 -0700 Subject: [PATCH 59/63] fix equals impl in base predicate --- .../gtceu/api/multiblock/MultiPredicate.java | 4 +++- .../api/multiblock/predicates/BasePredicate.java | 14 +++++++------- .../multiblock/predicates/TestablePredicate.java | 5 ++++- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 9efc7690641..189bae822ea 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -508,12 +508,14 @@ private static MultiPredicate combine(MultiPredicate a, Logic type, @Nullable Mu List predicates; if (b.isSingle()) { predicates = Stream.concat(a.predicates().stream(), Stream.of(b.predicates().get(0))) + .map(BasePredicate::copy) .toList(); children = a.children().stream() + .map(MultiPredicate::deepCopy) .toList(); } else { predicates = List.of(); - children = List.of(a, b); + children = Stream.of(a, b).map(MultiPredicate::deepCopy).toList(); } MultiPredicate combined = type.makePredicate(children, predicates, a.hasAir || b.hasAir); diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index 816491224ad..fd199277ed6 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -148,13 +148,13 @@ public String toString() { public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof BasePredicate predicate)) return false; - return Objects.equals(getCandidates(), predicate.candidates) && - Objects.equals(settings, predicate.settings); - } - - @Override - public int hashCode() { - return Objects.hash(getCandidates(), settings); + List thisCandidates = this.getCandidates(); + List thatCandidates = predicate.getCandidates(); + if (thisCandidates.size() != thatCandidates.size()) return false; + for (int i = 0; i < thisCandidates.size(); i++) { + if (!Objects.equals(thisCandidates.get(i), thatCandidates.get(i))) return false; + } + return true; } private BasePredicate markImmutable() { diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java index 0e636a4d728..e88c181348e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestablePredicate.java @@ -105,7 +105,10 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(super.hashCode(), contents, name); + if (contents == null) return Objects.hash(null, name); + StringBuilder builder = new StringBuilder(); + contents.accept(builder); + return Objects.hash(builder.toString(), name); } @Override From 9502350136e6971f3883ca4907771a414dc84ddd Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Wed, 9 Sep 2026 00:10:53 -0700 Subject: [PATCH 60/63] for some reason i thought i needed this guard, but actually i don't --- .../gtceu/api/multiblock/predicates/SettingsHolder.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index 9a2d44636e3..dffabb51fa9 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -179,9 +179,8 @@ enum TestType { public boolean testAndIncrement(SettingsHolder holder, PredicateContext ctx) { if (!holder.hasSettings()) return true; return switch (this) { - case GLOBAL_MAX -> holder.getMaxCount() == -1 || holder.testGlobalMax(ctx.incrementGlobalCount(holder)); - case SLICE_MAX -> holder.getMaxSliceCount() == -1 || - holder.testSliceMax(ctx.incrementSliceCount(holder)); + case GLOBAL_MAX -> holder.testGlobalMax(ctx.incrementGlobalCount(holder)); + case SLICE_MAX -> holder.testSliceMax(ctx.incrementSliceCount(holder)); default -> testCounts(holder, ctx); }; } From 7aaedcd1b99cf20ecbcf25067fc3669ab334d590 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Wed, 9 Sep 2026 01:59:04 -0700 Subject: [PATCH 61/63] improve MultiPredicate toString --- .../gtceu/api/multiblock/MultiPredicate.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index 189bae822ea..d8a4a0d5c60 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -440,21 +440,20 @@ public MultiPredicate disabledRenderFormed() { @Override public String toString() { - StringBuilder builder = new StringBuilder("MultiPredicate"); + if (isSingle()) return predicates().get(0).toString(); + StringBuilder builder = new StringBuilder(); + if (isController()) builder.append("C"); builder.append('['); - if (isController()) builder.append("Controller=true, "); - switch (this.type) { - case OR -> builder.append("Logic=OR"); - case AND -> builder.append("Logic=AND"); - case XOR -> builder.append("Logic=XOR"); - } - builder.append(']'); - builder.append('{'); - StringJoiner joiner = new StringJoiner(", "); + var delimiter = switch (this.type) { + case OR -> " OR "; + case AND -> " AND "; + case XOR -> " XOR "; + }; + StringJoiner joiner = new StringJoiner(delimiter); this.forEach(p -> joiner.add(p.toString())); this.forEachChild(mp -> joiner.add(mp.toString())); builder.append(joiner); - builder.append('}'); + builder.append(']'); return builder.toString(); } From 8e225ded1ee662ecf105abd2eadc27007fb45180 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Wed, 9 Sep 2026 02:30:41 -0700 Subject: [PATCH 62/63] SinglePredicateError now accepts a SettingsHolder along with a list of candidates --- .../error/SinglePredicateError.java | 38 ++++++++++--------- .../multiblock/predicates/BasePredicate.java | 8 ++-- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/error/SinglePredicateError.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/error/SinglePredicateError.java index 01bf9f8e558..4acee63f462 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/error/SinglePredicateError.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/error/SinglePredicateError.java @@ -1,7 +1,7 @@ package com.gregtechceu.gtceu.api.multiblock.error; import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.multiblock.predicates.BasePredicate; +import com.gregtechceu.gtceu.api.multiblock.predicates.SettingsHolder; import com.gregtechceu.gtceu.api.multiblock.util.BlockInfo; import net.minecraft.network.chat.Component; @@ -42,15 +42,15 @@ public class SinglePredicateError extends PatternError { public final int predMaxLayerCount; public final String debugName; - public SinglePredicateError(BasePredicate failingPredicate, - ErrorType type, int actualCount) { + public SinglePredicateError(SettingsHolder holder, + List candidates, ErrorType type, int actualCount) { this(type, actualCount, - failingPredicate.getMinCount(), - failingPredicate.getMaxCount(), - failingPredicate.getMinSliceCount(), - failingPredicate.getMaxSliceCount(), - failingPredicate.toString(), - failingPredicate.getCandidates()); + holder.getMinCount(), + holder.getMaxCount(), + holder.getMinSliceCount(), + holder.getMaxSliceCount(), + holder.toString(), + candidates); } public SinglePredicateError(ErrorType type, int actualCount, int minCount, int maxCount, int minLayerCount, @@ -80,20 +80,24 @@ public PatternErrorUI getPatternErrorUIModifier() { }; } - public static SinglePredicateError maxCount(BasePredicate failingPredicate, int actualCount) { - return new SinglePredicateError(failingPredicate, ErrorType.MAX_COUNT, actualCount); + public static SinglePredicateError maxCount(SettingsHolder failingPredicate, List candidates, + int actualCount) { + return new SinglePredicateError(failingPredicate, candidates, ErrorType.MAX_COUNT, actualCount); } - public static SinglePredicateError minCount(BasePredicate failingPredicate, int actualCount) { - return new SinglePredicateError(failingPredicate, ErrorType.MIN_COUNT, actualCount); + public static SinglePredicateError minCount(SettingsHolder failingPredicate, List candidates, + int actualCount) { + return new SinglePredicateError(failingPredicate, candidates, ErrorType.MIN_COUNT, actualCount); } - public static SinglePredicateError maxLayerCount(BasePredicate failingPredicate, int actualCount) { - return new SinglePredicateError(failingPredicate, ErrorType.MAX_LAYER_COUNT, actualCount); + public static SinglePredicateError maxLayerCount(SettingsHolder failingPredicate, List candidates, + int actualCount) { + return new SinglePredicateError(failingPredicate, candidates, ErrorType.MAX_LAYER_COUNT, actualCount); } - public static SinglePredicateError minLayerCount(BasePredicate failingPredicate, int actualCount) { - return new SinglePredicateError(failingPredicate, ErrorType.MIN_LAYER_COUNT, actualCount); + public static SinglePredicateError minLayerCount(SettingsHolder failingPredicate, List candidates, + int actualCount) { + return new SinglePredicateError(failingPredicate, candidates, ErrorType.MIN_LAYER_COUNT, actualCount); } @Getter diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index fd199277ed6..8a0014f949a 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -82,7 +82,7 @@ public boolean checkMaxCount(PredicateContext context) { public boolean testGlobalMax(PredicateContext ctx) { int count = ctx.incrementGlobalCount(this); if (testGlobalMax(count)) return true; - ctx.appendError(SinglePredicateError.maxCount(this, count)); + ctx.appendError(SinglePredicateError.maxCount(this, getCandidates(), count)); return false; } @@ -91,7 +91,7 @@ public boolean testSliceMax(PredicateContext ctx) { if (!ctx.isCheckLayer()) return true; int count = ctx.incrementSliceCount(this); if (testSliceMax(count)) return true; - ctx.appendError(SinglePredicateError.maxLayerCount(this, count)); + ctx.appendError(SinglePredicateError.maxLayerCount(this, getCandidates(), count)); return false; } @@ -100,7 +100,7 @@ public boolean testGlobalMin(PredicateContext ctx) { if (getMinCount() == -1) return true; int count = ctx.getGlobalCount(this); if (testGlobalMin(count)) return true; - ctx.appendError(SinglePredicateError.minCount(this, count)); + ctx.appendError(SinglePredicateError.minCount(this, getCandidates(), count)); return false; } @@ -109,7 +109,7 @@ public boolean testSliceMin(PredicateContext ctx) { if (!ctx.isCheckLayer()) return true; int count = ctx.getSliceCount(this); if (testSliceMin(count)) return true; - ctx.appendError(SinglePredicateError.minLayerCount(this, count)); + ctx.appendError(SinglePredicateError.minLayerCount(this, getCandidates(), count)); return false; } From 8894474c755d0b8a485cd60da379934f7ae5eca6 Mon Sep 17 00:00:00 2001 From: Ghzdude <44148655+ghzdude@users.noreply.github.com> Date: Wed, 9 Sep 2026 02:45:18 -0700 Subject: [PATCH 63/63] oh boy sure love making big changes move TestType into own class create PredicateResult to keep track of MultiPredicate call chain `getPredicateAtPos()` now returns a nonnull result rework XOR to not require a full override of `getPredicateAtPos()` delete parent field in BasePredicate and MultiPredicate remove leaf and branch methods in MultiPredicate --- .../gtceu/api/multiblock/MultiPredicate.java | 80 +++++----------- .../gtceu/api/multiblock/PredicateResult.java | 96 +++++++++++++++++++ .../gtceu/api/multiblock/XorPredicate.java | 50 ++++------ .../api/multiblock/pattern/BlockPattern.java | 9 +- .../multiblock/pattern/ExpandablePattern.java | 9 +- .../multiblock/predicates/BasePredicate.java | 17 ---- .../multiblock/predicates/SettingsHolder.java | 34 ------- .../api/multiblock/predicates/TestType.java | 35 +++++++ .../electric/CentralMonitorMachine.java | 2 +- .../widgets/MultiblockPreviewWidget.java | 2 +- 10 files changed, 184 insertions(+), 150 deletions(-) create mode 100644 src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateResult.java create mode 100644 src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestType.java diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java index d8a4a0d5c60..321c02c553d 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/MultiPredicate.java @@ -4,6 +4,7 @@ import com.gregtechceu.gtceu.api.multiblock.predicates.BasePredicate; import com.gregtechceu.gtceu.api.multiblock.predicates.PredicateSettings; import com.gregtechceu.gtceu.api.multiblock.predicates.SettingsHolder; +import com.gregtechceu.gtceu.api.multiblock.predicates.TestType; import com.gregtechceu.gtceu.api.multiblock.util.BlockInfo; import net.minecraft.network.chat.Component; @@ -62,17 +63,12 @@ public abstract class MultiPredicate implements SettingsHolder { @Getter private PredicateSettings settings; - @Nullable - @Getter - private MultiPredicate parent; private boolean mutable = true; /// @param children list of multi predicate children /// @param predicates list of testable predicates, should be sorted already protected MultiPredicate(Logic type, List children, List predicates, boolean hasAir) { - predicates.forEach(p -> p.setParent(this)); - children.forEach(mp -> mp.setParent(this)); this.predicates = Collections.unmodifiableList(predicates); this.children = Collections.unmodifiableList(children); this.type = type; @@ -80,25 +76,34 @@ protected MultiPredicate(Logic type, List children, } /// @return innermost base predicate that passes state check at given pos - public @Nullable BasePredicate getPredicateAtPos(PredicateContext context) { + public PredicateResult getPredicateAtPos(PredicateContext context) { context.setStage(PredicateContext.PredicateStage.INTERNAL); for (BasePredicate predicate : predicates()) { if (predicate.test(context)) { - return predicate; + PredicateResult result = onPredicateMatched(PredicateResult.of(predicate, this), context); + if (result.failed()) return PredicateResult.noMatch(); + if (result.hasMatched()) return result; } } for (MultiPredicate predicates : children()) { - BasePredicate p = predicates.getPredicateAtPos(context); - if (p != null) return p; - } - if (isRoot()) { - onError(context); + var result = predicates.getPredicateAtPos(context); + if (result.hasMatched()) { + result = onPredicateMatched(result, context); + return result.failed() ? PredicateResult.noMatch() : result.appendParent(this); + } } - return null; + + return PredicateResult.noMatch(); + } + + /// @param result a result with the passed predicate and call chain + /// @return by default returns {@code result}, but can return a modified result (see {@link XorPredicate}) + protected PredicateResult onPredicateMatched(PredicateResult result, PredicateContext context) { + return result; } /// called when all predicates failed - protected void onError(PredicateContext ctx) { + public void onError(PredicateContext ctx) { this.forEach(p -> p.onError(ctx)); this.forEachChild(mp -> mp.onError(ctx)); } @@ -133,28 +138,6 @@ public final boolean postSliceTest(PredicateContext ctx) { protected abstract boolean testSliceMin(PredicateContext ctx); - /// test against global/slice max counts - public boolean testMaxCount(BasePredicate passedPredicate, PredicateContext context) { - context.setStage(PredicateContext.PredicateStage.GLOBAL_MAX); - if (!(passedPredicate.testGlobalMax(context) && testParents(TestType.GLOBAL_MAX, passedPredicate, context))) { - return false; - } - context.setStage(PredicateContext.PredicateStage.SLICE_MAX); - return passedPredicate.testSliceMax(context) && testParents(TestType.SLICE_MAX, passedPredicate, context); - } - - // go up the parent chain to test settings of parents - private boolean testParents(TestType type, BasePredicate passedPredicate, PredicateContext context) { - MultiPredicate parent = passedPredicate.getParent(); - while (parent != null) { - if (!type.testAndIncrement(parent, context)) { - return false; - } - parent = parent.getParent(); - } - return true; - } - public List> getCandidates() { List> result = new ArrayList<>(); for (BasePredicate predicate : predicates()) { @@ -195,24 +178,9 @@ public boolean hasAir() { return this.hasAir; } - /// @return {@code true} if this multi predicate has only one predicate, has no children, and has no parent + /// @return {@code true} if this multi predicate has only one predicate and has no children public boolean isSingle() { - return predicates.size() == 1 && isLeaf() && isRoot(); - } - - /// @return {@code true} if this multi predicate has no parent - public boolean isRoot() { - return getParent() == null; - } - - /// @return {@code true} if this multi predicate has children and is not a root predicate - public boolean isBranch() { - return !isLeaf() && !isRoot(); - } - - /// @return {@code true} if this multi predicate has no children multi predicates - public boolean isLeaf() { - return this.children.isEmpty(); + return predicates.size() == 1 && this.children.isEmpty(); } public List getDescriptiveContents() { @@ -251,7 +219,7 @@ public List children() { /// @return a flattened list of all base predicates public List expand() { - if (isLeaf()) return this.predicates; + if (this.children.isEmpty()) return this.predicates; List expanded = new ArrayList<>(this.predicates); forEachChild(mp -> expanded.addAll(mp.expand())); return expanded; @@ -319,10 +287,6 @@ public void setSettings(@Nullable PredicateSettings settings) { onSettingsChanged(); } - public void setParent(MultiPredicate parent) { - if (mutable) this.parent = parent; - } - public MultiPredicate setController(boolean controller) { var mutated = mutable ? this : deepCopy(); mutated.controller = controller; diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateResult.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateResult.java new file mode 100644 index 00000000000..6cbc6018322 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/PredicateResult.java @@ -0,0 +1,96 @@ +package com.gregtechceu.gtceu.api.multiblock; + +import com.gregtechceu.gtceu.api.multiblock.error.SinglePredicateError; +import com.gregtechceu.gtceu.api.multiblock.predicates.BasePredicate; +import com.gregtechceu.gtceu.api.multiblock.predicates.TestType; + +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +// if failed, return early with no match +public record PredicateResult(@Nullable BasePredicate match, List parents, boolean failed) { + + private static final PredicateResult NO_MATCH = new PredicateResult(null, List.of(), true); + + public static PredicateResult noMatch() { + return NO_MATCH; + } + + public static PredicateResult of(BasePredicate match, MultiPredicate parent) { + return new PredicateResult(Objects.requireNonNull(match), List.of(Objects.requireNonNull(parent)), false); + } + + public PredicateResult appendParent(MultiPredicate parent) { + if (failed) return this; + ArrayList parents = new ArrayList<>(this.parents); + parents.add(parent); + return new PredicateResult(this.match, parents, false); + } + + public PredicateResult setFailed() { + return new PredicateResult(this.match, this.parents, true); + } + + public boolean hasMatched() { + return this.match != null; + } + + // return true if passed, else false + public boolean testMaxCount(PredicateContext context) { + Objects.requireNonNull(this.match, "matched base predicate must not be null"); + + context.setStage(PredicateContext.PredicateStage.GLOBAL_MAX); + if (!testParents(TestType.GLOBAL_MAX, context)) { + return false; + } + + context.setStage(PredicateContext.PredicateStage.SLICE_MAX); + return testParents(TestType.SLICE_MAX, context); + } + + public boolean contains(MultiPredicate predicate) { + return parents.contains(predicate); + } + + public boolean isTop(MultiPredicate predicate) { + if (parents.isEmpty()) return false; + return Objects.equals(getTop(), predicate); + } + + public @Nullable MultiPredicate getTop() { + if (parents.isEmpty()) return null; + return parents.get(0); + } + + public @Nullable MultiPredicate getBottom() { + if (parents.isEmpty()) return null; + return parents.get(parents.size() - 1); + } + + // go up the parent chain to test settings of parents + // if any return false, fail + private boolean testParents(TestType type, PredicateContext context) { + Objects.requireNonNull(this.match, "matched base predicate must not be null"); + if (type == TestType.GLOBAL_MAX ? !this.match.testGlobalMax(context) : !this.match.testSliceMax(context)) { + return false; + } + for (MultiPredicate parent : this.parents) { + if (!type.testAndIncrement(parent, context)) { + var error = type == TestType.GLOBAL_MAX ? + SinglePredicateError.maxCount(parent, + parent.getCandidates().stream().flatMap(Collection::stream).toList(), + context.getGlobalCount(this.match)) : + SinglePredicateError.maxLayerCount(parent, + parent.getCandidates().stream().flatMap(Collection::stream).toList(), + context.getSliceCount(this.match)); + context.appendError(error); + return false; + } + } + return true; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java index 4aa981b9624..90b1517081a 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/XorPredicate.java @@ -30,39 +30,27 @@ protected void onSettingsChanged() { } @Override - public @Nullable BasePredicate getPredicateAtPos(PredicateContext context) { - context.setStage(PredicateContext.PredicateStage.INTERNAL); - for (BasePredicate predicate : predicates()) { - if (predicate.test(context)) { - if (this.passedPredicate != null && !this.passedPredicate.is(predicate)) { - xorError(context, predicate, this.passedPredicate); - // error - return null; - } - if (this.passedPredicate == null) { - this.passedPredicate = ofPredicate(predicate); - } - return predicate; + protected PredicateResult onPredicateMatched(PredicateResult result, PredicateContext context) { + if (result.match() == null) return result; + if (result.isTop(this)) { + if (this.passedPredicate != null && !this.passedPredicate.is(result.match())) { + xorError(context, result.match(), this.passedPredicate); + return result.setFailed(); } - } - for (MultiPredicate child : children()) { - BasePredicate p = child.getPredicateAtPos(context); - if (p != null) { - if (this.passedPredicate != null && !this.passedPredicate.is(child)) { - xorError(context, p, this.passedPredicate); - // error - return null; - } - if (this.passedPredicate == null) { - this.passedPredicate = ofChild(child); - } - return p; + if (this.passedPredicate == null) { + this.passedPredicate = ofPredicate(result.match()); + } + } else { + MultiPredicate bottom = Objects.requireNonNull(result.getBottom()); + if (this.passedPredicate != null && !this.passedPredicate.is(bottom)) { + xorError(context, result.match(), this.passedPredicate); + return result.setFailed(); + } + if (this.passedPredicate == null) { + this.passedPredicate = ofChild(bottom); } } - if (isRoot()) { - onError(context); - } - return null; + return result; } @Override @@ -95,7 +83,7 @@ private static void xorError(PredicateContext context, BasePredicate predicate, private static boolean isNoneValid(MultiPredicate multiPredicate) { PredicateSettings settings = multiPredicate.getSettings(); - if (settings != null && settings.isNoneValid()) return true; + if (settings != null && !settings.isNoneValid()) return false; for (BasePredicate predicate : multiPredicate.predicates()) { if (predicate.getSettings().isNoneValid()) { diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/BlockPattern.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/BlockPattern.java index 350604e4cf0..d6dcb8e7d1d 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/BlockPattern.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/BlockPattern.java @@ -3,7 +3,7 @@ import com.gregtechceu.gtceu.api.multiblock.MultiPredicate; import com.gregtechceu.gtceu.api.multiblock.OriginOffset; import com.gregtechceu.gtceu.api.multiblock.PredicateContext; -import com.gregtechceu.gtceu.api.multiblock.predicates.BasePredicate; +import com.gregtechceu.gtceu.api.multiblock.PredicateResult; import com.gregtechceu.gtceu.api.multiblock.util.RelativeDirection; import net.minecraft.core.BlockPos; @@ -214,16 +214,17 @@ public boolean checkSlice(BlockPos.MutableBlockPos controllerPos, PatternState p if (!multiPredicate.isAny()) patternState.updateCache(); // state check - BasePredicate innerPredicate = multiPredicate.getPredicateAtPos(context); + PredicateResult result = multiPredicate.getPredicateAtPos(context); // all predicates failed - if (innerPredicate == null) { + if (!result.hasMatched()) { + multiPredicate.onError(context); // errors get committed in BasicSliceStrategy return false; } // max count checks - if (!innerPredicate.checkMaxCount(context)) { + if (!result.testMaxCount(context)) { // errors get committed in BasicSliceStrategy return false; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/ExpandablePattern.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/ExpandablePattern.java index bcdd2ed7ea3..c223e326877 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/ExpandablePattern.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/pattern/ExpandablePattern.java @@ -3,7 +3,7 @@ import com.gregtechceu.gtceu.api.multiblock.MultiPredicate; import com.gregtechceu.gtceu.api.multiblock.OriginOffset; import com.gregtechceu.gtceu.api.multiblock.PredicateContext; -import com.gregtechceu.gtceu.api.multiblock.predicates.BasePredicate; +import com.gregtechceu.gtceu.api.multiblock.PredicateResult; import com.gregtechceu.gtceu.api.multiblock.util.RelativeDirection; import net.minecraft.core.BlockPos; @@ -176,16 +176,17 @@ public boolean checkPatternAt(Level level, PatternState patternState, BlockPos c if (!multiPredicate.isAny()) patternState.updateCache(); // state check - BasePredicate innerPredicate = multiPredicate.getPredicateAtPos(context); + PredicateResult result = multiPredicate.getPredicateAtPos(context); // all predicates failed - if (innerPredicate == null) { + if (!result.hasMatched()) { + multiPredicate.onError(context); context.commitSliceErrors(); // this is actually global errors, not slice return false; } // max count checks - if (!innerPredicate.checkMaxCount(context)) { + if (!result.testMaxCount(context)) { context.commitSliceErrors(); // this is actually global errors, not slice return false; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java index 8a0014f949a..407b0f38333 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/BasePredicate.java @@ -13,7 +13,6 @@ import lombok.Setter; import lombok.experimental.Accessors; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.function.UnaryOperator; @@ -44,8 +43,6 @@ public abstract class BasePredicate implements SettingsHolder { @Getter protected PredicateSettings settings = PredicateSettings.create(); - private @Nullable MultiPredicate parent; - @Getter private final List additionalTooltips = new ArrayList<>(); @@ -59,10 +56,6 @@ public abstract class BasePredicate implements SettingsHolder { @Getter private boolean isAny = false; - public MultiPredicate getParent() { - return Objects.requireNonNull(this.parent); - } - /// the main testing method public abstract boolean test(PredicateContext ctx); @@ -72,12 +65,6 @@ public MultiPredicate getParent() { /// @return a list of components to be displayed while hovering over a block in the Multiblock Preview public abstract List getRecipeViewerTooltips(MultiPredicate root); - /// delegates to {@link MultiPredicate#testMaxCount(BasePredicate, PredicateContext)}, - /// with this predicate as the passing predicate - public boolean checkMaxCount(PredicateContext context) { - return getParent().testMaxCount(this, context); - } - /// test against global max count public boolean testGlobalMax(PredicateContext ctx) { int count = ctx.incrementGlobalCount(this); @@ -192,10 +179,6 @@ public void setSettings(PredicateSettings settings) { if (mutable) this.settings = settings; } - public void setParent(@Nullable MultiPredicate parent) { - if (mutable) this.parent = parent; - } - public void addTooltips(Component tooltip) { if (mutable) this.additionalTooltips.add(tooltip); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java index dffabb51fa9..412216bdbf8 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/SettingsHolder.java @@ -1,7 +1,5 @@ package com.gregtechceu.gtceu.api.multiblock.predicates; -import com.gregtechceu.gtceu.api.multiblock.PredicateContext; - import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.CheckReturnValue; @@ -165,36 +163,4 @@ default boolean testSliceMax(int count) { default int compareTo(SettingsHolder o) { return Integer.compare(getPriority(), o.getPriority()); } - - enum TestType { - - GLOBAL_MIN, - GLOBAL_MAX, - SLICE_MIN, - SLICE_MAX; - - /// @implNote The count of the holder WILL be incremented for glabal/slice max - /// @return {@code true}, if the holder does not have settings, - /// or passes their settings according to the type - public boolean testAndIncrement(SettingsHolder holder, PredicateContext ctx) { - if (!holder.hasSettings()) return true; - return switch (this) { - case GLOBAL_MAX -> holder.testGlobalMax(ctx.incrementGlobalCount(holder)); - case SLICE_MAX -> holder.testSliceMax(ctx.incrementSliceCount(holder)); - default -> testCounts(holder, ctx); - }; - } - - /// @return {@code true}, if the holder does not have settings, - /// or passes their settings according to the type - public boolean testCounts(SettingsHolder holder, PredicateContext ctx) { - if (!holder.hasSettings()) return true; - return switch (this) { - case GLOBAL_MAX -> holder.testGlobalMax(ctx.getGlobalCount(holder)); - case SLICE_MAX -> holder.testSliceMax(ctx.getSliceCount(holder)); - case GLOBAL_MIN -> holder.testGlobalMin(ctx.getGlobalCount(holder)); - case SLICE_MIN -> holder.testSliceMin(ctx.getSliceCount(holder)); - }; - } - } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestType.java b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestType.java new file mode 100644 index 00000000000..b5dda5a29ac --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/multiblock/predicates/TestType.java @@ -0,0 +1,35 @@ +package com.gregtechceu.gtceu.api.multiblock.predicates; + +import com.gregtechceu.gtceu.api.multiblock.PredicateContext; + +public enum TestType { + + GLOBAL_MIN, + GLOBAL_MAX, + SLICE_MIN, + SLICE_MAX; + + /// @return {@code true}, if the holder does not have settings, + /// or passes their settings according to the type + /// @implNote The count of the holder WILL be incremented for glabal/slice max + public boolean testAndIncrement(SettingsHolder holder, PredicateContext ctx) { + if (!holder.hasSettings()) return true; + return switch (this) { + case GLOBAL_MAX -> holder.testGlobalMax(ctx.incrementGlobalCount(holder)); + case SLICE_MAX -> holder.testSliceMax(ctx.incrementSliceCount(holder)); + default -> testCounts(holder, ctx); + }; + } + + /// @return {@code true}, if the holder does not have settings, + /// or passes their settings according to the type + public boolean testCounts(SettingsHolder holder, PredicateContext ctx) { + if (!holder.hasSettings()) return true; + return switch (this) { + case GLOBAL_MAX -> holder.testGlobalMax(ctx.getGlobalCount(holder)); + case SLICE_MAX -> holder.testSliceMax(ctx.getSliceCount(holder)); + case GLOBAL_MIN -> holder.testGlobalMin(ctx.getGlobalCount(holder)); + case SLICE_MIN -> holder.testSliceMin(ctx.getSliceCount(holder)); + }; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java index db1e10b8005..9e875a033e9 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java @@ -139,7 +139,7 @@ public static boolean isValidMonitorBlock(Level level, BlockPos pos) { CONTEXT.setCheckLayer(false); CONTEXT.updateLevel(level); CONTEXT.updatePos(pos); - return getMultiPredicate().getPredicateAtPos(CONTEXT) != null; + return getMultiPredicate().getPredicateAtPos(CONTEXT).hasMatched(); } public void updateStructureDimensions() { diff --git a/src/main/java/com/gregtechceu/gtceu/integration/recipeviewer/widgets/MultiblockPreviewWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/recipeviewer/widgets/MultiblockPreviewWidget.java index 6ed942556e8..6328c8632e2 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/recipeviewer/widgets/MultiblockPreviewWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/recipeviewer/widgets/MultiblockPreviewWidget.java @@ -184,7 +184,7 @@ public MultiblockPreviewWidget(MultiblockMachineDefinition definition, Multibloc MultiPredicate root = Objects.requireNonNull(this.multiblockSchemaInfo.getStructureHelper()) .getPredicateFromPos(value, hit.getBlockPos(), frontFacing, upFacing, isFlipped); root.resetLogic(); - BasePredicate predicate = root.getPredicateAtPos(context); + BasePredicate predicate = root.getPredicateAtPos(context).match(); if (predicate != null) { predicate.getRecipeViewerTooltips(root).forEach(t -> text.add(t).newLine()); }