新版智能倍增;添加智能倍增对高级处理样板支持
This commit is contained in:
parent
0a820c734b
commit
e478bf7e35
|
|
@ -7,10 +7,10 @@ import appeng.api.stacks.GenericStack;
|
||||||
import appeng.api.stacks.KeyCounter;
|
import appeng.api.stacks.KeyCounter;
|
||||||
import appeng.crafting.pattern.AEProcessingPattern;
|
import appeng.crafting.pattern.AEProcessingPattern;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
|
@ -18,126 +18,163 @@ import java.util.Objects;
|
||||||
* 缩放后的处理样板,结构完全模拟 AEProcessingPattern。
|
* 缩放后的处理样板,结构完全模拟 AEProcessingPattern。
|
||||||
* 保持 sparse/condensed/inputs 的一致性,同时保存原始样板。
|
* 保持 sparse/condensed/inputs 的一致性,同时保存原始样板。
|
||||||
*/
|
*/
|
||||||
public final class ScaledProcessingPattern implements IPatternDetails {
|
public class ScaledProcessingPattern implements IPatternDetails {
|
||||||
|
protected final @NotNull IPatternDetails original;
|
||||||
|
protected final long multiplier;
|
||||||
|
|
||||||
private final AEProcessingPattern original; // 原始样板引用
|
public ScaledProcessingPattern(@NotNull IPatternDetails original, long multiplier) {
|
||||||
private final AEItemKey definition; // 样板物品
|
if (multiplier <= 0) throw new IllegalArgumentException("multiplier must be > 0");
|
||||||
private final List<GenericStack> sparseInputs; // 缩放后的稀疏输入(List 以适配 1.21 API)
|
this.original = original;
|
||||||
private final List<GenericStack> sparseOutputs; // 缩放后的稀疏输出(List 以适配 1.21 API)
|
this.multiplier = multiplier;
|
||||||
private final IInput[] inputs; // 缩放后的压缩输入
|
|
||||||
private final List<GenericStack> condensedOutputs; // 缩放后的压缩输出(List 以适配 1.21 API)
|
|
||||||
|
|
||||||
public ScaledProcessingPattern(
|
|
||||||
AEProcessingPattern original,
|
|
||||||
AEItemKey definition,
|
|
||||||
List<GenericStack> sparseInputs,
|
|
||||||
List<GenericStack> sparseOutputs,
|
|
||||||
IInput[] inputs,
|
|
||||||
List<GenericStack> condensedOutputs
|
|
||||||
) {
|
|
||||||
this.original = Objects.requireNonNull(original);
|
|
||||||
this.definition = Objects.requireNonNull(definition);
|
|
||||||
this.sparseInputs = Collections.unmodifiableList(new ArrayList<>(Objects.requireNonNull(sparseInputs)));
|
|
||||||
this.sparseOutputs = Collections.unmodifiableList(new ArrayList<>(Objects.requireNonNull(sparseOutputs)));
|
|
||||||
this.inputs = Objects.requireNonNull(inputs);
|
|
||||||
this.condensedOutputs = Collections.unmodifiableList(new ArrayList<>(Objects.requireNonNull(condensedOutputs)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------- API 实现 -------------------- */
|
public @NotNull IPatternDetails getOriginal() {return this.original;}
|
||||||
|
|
||||||
public AEProcessingPattern getOriginal() {
|
|
||||||
return this.original;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AEItemKey getDefinition() {
|
public AEItemKey getDefinition() {
|
||||||
return this.definition;
|
return this.original.getDefinition();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IInput[] getInputs() {
|
public IInput[] getInputs() {
|
||||||
return this.inputs;
|
IInput[] original = this.original.getInputs();
|
||||||
}
|
IInput[] scaled = new IInput[original.length];
|
||||||
|
for (int i = 0; i < original.length; i++) {
|
||||||
@Override
|
scaled[i] = new ScaledInput(original[i], this.multiplier);
|
||||||
public GenericStack getPrimaryOutput() {
|
}
|
||||||
if (!this.condensedOutputs.isEmpty()) return this.condensedOutputs.get(0);
|
return scaled;
|
||||||
return this.original.getPrimaryOutput();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GenericStack> getOutputs() {
|
public List<GenericStack> getOutputs() {
|
||||||
return this.condensedOutputs;
|
var original = this.original.getOutputs();
|
||||||
|
List<GenericStack> scaled = new ArrayList<>(original.size());
|
||||||
|
for (GenericStack g : original) {
|
||||||
|
if (g != null) {
|
||||||
|
scaled.add(new GenericStack(g.what(), g.amount() * this.multiplier));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scaled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsPushInputsToExternalInventory() {
|
public void pushInputsToExternalInventory(KeyCounter[] inputHolder, PatternInputSink sink) {
|
||||||
return this.original.supportsPushInputsToExternalInventory();
|
// 如果 sparseInputs 与 inputs 一一对应,则无需 reorder
|
||||||
}
|
if (((AEProcessingPattern) this.original).getSparseInputs().size() == this.original.getInputs().length) {
|
||||||
|
// AEProcessingPattern 的默认逻辑
|
||||||
|
IPatternDetails.super.pushInputsToExternalInventory(inputHolder, sink);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
// 否则必须按 sparse 输入顺序推送
|
||||||
public void pushInputsToExternalInventory(KeyCounter[] inputHolder, PatternInputSink inputSink) {
|
var allInputs = new KeyCounter();
|
||||||
// 保持和 AEProcessingPattern 一致,用 sparseInputs 驱动
|
for (var ctr : inputHolder) {
|
||||||
if (this.sparseInputs.size() == this.inputs.length) {
|
allInputs.addAll(ctr);
|
||||||
IPatternDetails.super.pushInputsToExternalInventory(inputHolder, inputSink);
|
}
|
||||||
} else {
|
|
||||||
KeyCounter allInputs = new KeyCounter();
|
var sparse = this.getSparseInputs(); // 使用已缩放倍率的顺序表
|
||||||
for (KeyCounter counter : inputHolder) {
|
|
||||||
allInputs.addAll(counter);
|
for (var sparseInput : sparse) {
|
||||||
}
|
if (sparseInput == null) continue;
|
||||||
for (GenericStack sparseInput : this.sparseInputs) {
|
|
||||||
if (sparseInput != null) {
|
var key = sparseInput.what();
|
||||||
AEKey key = sparseInput.what();
|
long amount = sparseInput.amount();
|
||||||
long amount = sparseInput.amount();
|
|
||||||
long available = allInputs.get(key);
|
long available = allInputs.get(key);
|
||||||
if (available < amount) {
|
if (available < amount) {
|
||||||
throw new RuntimeException("Expected at least %d of %s when pushing scaled pattern, but only %d available"
|
throw new IllegalStateException(
|
||||||
.formatted(amount, key, available));
|
"Expected " + amount + " of " + key + " but only " + available + " available"
|
||||||
}
|
);
|
||||||
inputSink.pushInput(key, amount);
|
|
||||||
allInputs.remove(key, amount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sink.pushInput(key, amount);
|
||||||
|
allInputs.remove(key, amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<GenericStack> getSparseInputs() {
|
protected List<GenericStack> getSparseInputs() {
|
||||||
return this.sparseInputs;
|
var original = ((AEProcessingPattern) this.original).getSparseInputs();
|
||||||
|
List<GenericStack> scaled = new ArrayList<>(original.size());
|
||||||
|
for (GenericStack g : original) {
|
||||||
|
if (g != null) {
|
||||||
|
scaled.add(new GenericStack(g.what(), g.amount() * this.multiplier));
|
||||||
|
} else {
|
||||||
|
scaled.add(null); // 保持 null 位
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scaled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<GenericStack> getSparseOutputs() {
|
public List<GenericStack> getSparseOutputs() {
|
||||||
return this.sparseOutputs;
|
var original = ((AEProcessingPattern) this.original).getSparseOutputs();
|
||||||
|
List<GenericStack> scaled = new ArrayList<>(original.size());
|
||||||
|
for (GenericStack g : original) {
|
||||||
|
if (g != null) {
|
||||||
|
scaled.add(new GenericStack(g.what(), g.amount() * this.multiplier));
|
||||||
|
} else {
|
||||||
|
scaled.add(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scaled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------- 缩放输入代理 -------------------- */
|
// equals / hashCode 必须包含 multiplier!不同倍率 = 不同 key
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int h = this.original.hashCode();
|
||||||
|
h = 31 * h + Long.hashCode(this.multiplier);
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
public static final class Input implements IPatternDetails.IInput {
|
@Override
|
||||||
private final GenericStack[] template;
|
public boolean equals(Object obj) {
|
||||||
private final long multiplier;
|
if (this == obj) return true;
|
||||||
|
if (!(obj instanceof ScaledProcessingPattern sp)) return false;
|
||||||
|
return sp.original.equals(this.original) && sp.multiplier == this.multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
public Input(GenericStack[] template, long multiplier) {
|
@Override
|
||||||
this.template = template;
|
public String toString() {
|
||||||
this.multiplier = multiplier;
|
StringBuilder sb = new StringBuilder("Scaled[Mult=")
|
||||||
}
|
.append(this.multiplier)
|
||||||
|
.append("] ");
|
||||||
|
|
||||||
|
sb.append("Inputs: [");
|
||||||
|
sb.append(String.join(", ",
|
||||||
|
java.util.Arrays.stream(this.original.getInputs())
|
||||||
|
.filter(i -> i.getPossibleInputs() != null && i.getPossibleInputs().length > 0)
|
||||||
|
.map(i -> {
|
||||||
|
GenericStack stack = i.getPossibleInputs()[0];
|
||||||
|
return stack.what() + "×" + i.getMultiplier();
|
||||||
|
})
|
||||||
|
.toArray(String[]::new)
|
||||||
|
));
|
||||||
|
sb.append("] ");
|
||||||
|
|
||||||
|
sb.append("Outputs: [");
|
||||||
|
sb.append(String.join(", ",
|
||||||
|
this.original.getOutputs().stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(s -> s.what() + "×" + s.amount())
|
||||||
|
.toArray(String[]::new)
|
||||||
|
));
|
||||||
|
sb.append("]");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private record ScaledInput(IInput original, long multiplier) implements IInput {
|
||||||
|
@Override
|
||||||
|
public GenericStack[] getPossibleInputs() {return this.original.getPossibleInputs();}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GenericStack[] getPossibleInputs() {
|
public long getMultiplier() {return this.original.getMultiplier() * this.multiplier;}
|
||||||
return this.template;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getMultiplier() {
|
public boolean isValid(AEKey input, Level level) {return this.original.isValid(input, level);}
|
||||||
return this.multiplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid(AEKey input, Level level) {
|
public @Nullable AEKey getRemainingKey(AEKey template) {return this.original.getRemainingKey(template);}
|
||||||
return input.matches(this.template[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable AEKey getRemainingKey(AEKey template) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
package com.extendedae_plus.api.crafting;
|
||||||
|
|
||||||
|
import appeng.api.crafting.IPatternDetails;
|
||||||
|
import appeng.api.stacks.AEKey;
|
||||||
|
import appeng.api.stacks.GenericStack;
|
||||||
|
import appeng.api.stacks.KeyCounter;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.pedroksl.advanced_ae.common.patterns.AdvProcessingPattern;
|
||||||
|
import net.pedroksl.advanced_ae.common.patterns.IAdvPatternDetails;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advanced AE 扩展版,额外实现 AdvPatternDetails 接口。
|
||||||
|
* 仅在 Advanced AE 加载时使用。
|
||||||
|
*/
|
||||||
|
public final class ScaledProcessingPatternAdv extends ScaledProcessingPattern implements IPatternDetails, IAdvPatternDetails {
|
||||||
|
private final LinkedHashMap<AEKey, Direction> dirMap;
|
||||||
|
|
||||||
|
public ScaledProcessingPatternAdv(@NotNull IPatternDetails original, long multiplier) {
|
||||||
|
super(original, multiplier);
|
||||||
|
this.dirMap = ((AdvProcessingPattern) original).getDirectionMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull AdvProcessingPattern getOriginal() {return (AdvProcessingPattern) this.original;}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pushInputsToExternalInventory(KeyCounter[] inputHolder, PatternInputSink sink) {
|
||||||
|
// 如果 sparseInputs 与 inputs 一一对应,则无需 reorder
|
||||||
|
if (((AdvProcessingPattern) this.original).getSparseInputs().size() == this.original.getInputs().length) {
|
||||||
|
// AEProcessingPattern 的默认逻辑
|
||||||
|
this.original.pushInputsToExternalInventory(inputHolder, sink);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则必须按 sparse 输入顺序推送
|
||||||
|
var allInputs = new KeyCounter();
|
||||||
|
for (var counter : inputHolder) allInputs.addAll(counter);
|
||||||
|
|
||||||
|
for (var sparseInput : this.getSparseInputs()) {
|
||||||
|
if (sparseInput == null) continue;
|
||||||
|
var key = sparseInput.what();
|
||||||
|
long amount = sparseInput.amount();
|
||||||
|
long available = allInputs.get(key);
|
||||||
|
if (available < amount) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Expected " + amount + " of " + key + " but only " + available + " available"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
sink.pushInput(key, amount);
|
||||||
|
allInputs.remove(key, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<GenericStack> getSparseInputs() {
|
||||||
|
var original = ((AdvProcessingPattern) this.original).getSparseInputs();
|
||||||
|
List<GenericStack> scaled = new ArrayList<>(original.size());
|
||||||
|
for (GenericStack g : original) {
|
||||||
|
if (g != null) {
|
||||||
|
scaled.add(new GenericStack(g.what(), g.amount() * this.multiplier));
|
||||||
|
} else {
|
||||||
|
scaled.add(null); // 保持 null 位
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GenericStack> getSparseOutputs() {
|
||||||
|
var original = ((AdvProcessingPattern) this.original).getSparseOutputs();
|
||||||
|
List<GenericStack> scaled = new ArrayList<>(original.size());
|
||||||
|
for (GenericStack g : original) {
|
||||||
|
if (g != null) {
|
||||||
|
scaled.add(new GenericStack(g.what(), g.amount() * this.multiplier));
|
||||||
|
} else {
|
||||||
|
scaled.add(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean directionalInputsSet() {
|
||||||
|
return this.dirMap != null && !this.dirMap.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedHashMap<AEKey, Direction> getDirectionMap() {
|
||||||
|
return this.dirMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Direction getDirectionSideForInputKey(AEKey key) {
|
||||||
|
return this.dirMap.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,4 +3,9 @@ package com.extendedae_plus.api.smartDoubling;
|
||||||
public interface ISmartDoublingAwarePattern {
|
public interface ISmartDoublingAwarePattern {
|
||||||
boolean eap$allowScaling();
|
boolean eap$allowScaling();
|
||||||
void eap$setAllowScaling(boolean allow);
|
void eap$setAllowScaling(boolean allow);
|
||||||
|
|
||||||
|
// 翻倍限制:0 表示无限制
|
||||||
|
int eap$getMultiplierLimit();
|
||||||
|
|
||||||
|
void eap$setMultiplierLimit(int limit);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ public class AdvPatternProviderLogicContainsRedirectMixin {
|
||||||
try {
|
try {
|
||||||
if (o instanceof ScaledProcessingPattern scaled) {
|
if (o instanceof ScaledProcessingPattern scaled) {
|
||||||
IPatternDetails base = scaled.getOriginal();
|
IPatternDetails base = scaled.getOriginal();
|
||||||
if (base != null && list.indexOf(base) != -1) {
|
if (list.indexOf(base) != -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.extendedae_plus.mixin.advancedae;
|
||||||
|
|
||||||
|
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
||||||
|
import net.pedroksl.advanced_ae.common.patterns.AdvProcessingPattern;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
||||||
|
@Mixin(value = AdvProcessingPattern.class, remap = false)
|
||||||
|
public class AdvProcessingPatternMixin implements ISmartDoublingAwarePattern {
|
||||||
|
@Unique
|
||||||
|
private boolean eap$allowScaling = false; // 默认不允许缩放
|
||||||
|
@Unique
|
||||||
|
private int eap$multiplierLimit = 0; // 模式级别的倍数上限,0 表示不限制
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean eap$allowScaling() {
|
||||||
|
return this.eap$allowScaling;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eap$setAllowScaling(boolean allow) {
|
||||||
|
this.eap$allowScaling = allow;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int eap$getMultiplierLimit() {
|
||||||
|
return this.eap$multiplierLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eap$setMultiplierLimit(int limit) {
|
||||||
|
this.eap$multiplierLimit = Math.max(0, limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package com.extendedae_plus.mixin.advancedae.helpers;
|
package com.extendedae_plus.mixin.advancedae.helpers;
|
||||||
|
|
||||||
import appeng.api.crafting.IPatternDetails;
|
import appeng.api.crafting.IPatternDetails;
|
||||||
import appeng.crafting.pattern.AEProcessingPattern;
|
|
||||||
import com.extendedae_plus.api.smartDoubling.ISmartDoubling;
|
import com.extendedae_plus.api.smartDoubling.ISmartDoubling;
|
||||||
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
||||||
import com.extendedae_plus.mixin.advancedae.accessor.AdvPatternProviderLogicPatternsAccessor;
|
import com.extendedae_plus.mixin.advancedae.accessor.AdvPatternProviderLogicPatternsAccessor;
|
||||||
|
|
@ -35,7 +34,7 @@ public class AdvPatternProviderLogicDoublingMixin implements ISmartDoubling {
|
||||||
try {
|
try {
|
||||||
var list = ((AdvPatternProviderLogicPatternsAccessor) this).eap$patterns();
|
var list = ((AdvPatternProviderLogicPatternsAccessor) this).eap$patterns();
|
||||||
for (IPatternDetails details : list) {
|
for (IPatternDetails details : list) {
|
||||||
if (details instanceof AEProcessingPattern proc && proc instanceof ISmartDoublingAwarePattern aware) {
|
if (details instanceof ISmartDoublingAwarePattern aware) {
|
||||||
aware.eap$setAllowScaling(value);
|
aware.eap$setAllowScaling(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -63,7 +62,7 @@ public class AdvPatternProviderLogicDoublingMixin implements ISmartDoubling {
|
||||||
var list = ((AdvPatternProviderLogicPatternsAccessor) this).eap$patterns();
|
var list = ((AdvPatternProviderLogicPatternsAccessor) this).eap$patterns();
|
||||||
boolean allow = this.eap$smartDoubling;
|
boolean allow = this.eap$smartDoubling;
|
||||||
for (IPatternDetails details : list) {
|
for (IPatternDetails details : list) {
|
||||||
if (details instanceof AEProcessingPattern proc && proc instanceof ISmartDoublingAwarePattern aware) {
|
if (details instanceof ISmartDoublingAwarePattern aware) {
|
||||||
aware.eap$setAllowScaling(allow);
|
aware.eap$setAllowScaling(allow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ import org.spongepowered.asm.mixin.Unique;
|
||||||
public class AEProcessingPatternMixin implements ISmartDoublingAwarePattern {
|
public class AEProcessingPatternMixin implements ISmartDoublingAwarePattern {
|
||||||
@Unique
|
@Unique
|
||||||
private boolean eap$allowScaling = false; // 默认不允许缩放
|
private boolean eap$allowScaling = false; // 默认不允许缩放
|
||||||
|
@Unique
|
||||||
|
private int eap$multiplierLimit = 0; // 模式级别的倍数上限,0 表示不限制
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean eap$allowScaling() {
|
public boolean eap$allowScaling() {
|
||||||
|
|
@ -19,4 +21,14 @@ public class AEProcessingPatternMixin implements ISmartDoublingAwarePattern {
|
||||||
public void eap$setAllowScaling(boolean allow) {
|
public void eap$setAllowScaling(boolean allow) {
|
||||||
this.eap$allowScaling = allow;
|
this.eap$allowScaling = allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int eap$getMultiplierLimit() {
|
||||||
|
return this.eap$multiplierLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eap$setMultiplierLimit(int limit) {
|
||||||
|
this.eap$multiplierLimit = Math.max(0, limit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.extendedae_plus.mixin.ae2.autopattern;
|
||||||
|
|
||||||
|
import appeng.api.networking.IGrid;
|
||||||
|
import appeng.api.networking.crafting.CalculationStrategy;
|
||||||
|
import appeng.api.networking.crafting.ICraftingSimulationRequester;
|
||||||
|
import appeng.api.stacks.GenericStack;
|
||||||
|
import appeng.crafting.CraftingCalculation;
|
||||||
|
import com.extendedae_plus.api.smartDoubling.ICraftingCalculationExt;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
@SuppressWarnings({"AddedMixinMembersNamePattern"})
|
||||||
|
@Mixin(value = CraftingCalculation.class, remap = false)
|
||||||
|
public class CraftingCalculationMixin implements ICraftingCalculationExt {
|
||||||
|
@Unique private IGrid grid;
|
||||||
|
|
||||||
|
@Inject(method = "<init>",at = @At("RETURN"))
|
||||||
|
private void init(Level level, IGrid grid, ICraftingSimulationRequester simRequester, GenericStack output, CalculationStrategy strategy, CallbackInfo ci) {
|
||||||
|
this.grid = grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IGrid getGrid() {
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.extendedae_plus.mixin.ae2.autopattern;
|
||||||
|
|
||||||
|
import appeng.api.crafting.IPatternDetails;
|
||||||
|
import appeng.crafting.inv.CraftingSimulationState;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mixin(value = CraftingSimulationState.class,remap = false)
|
||||||
|
public interface CraftingSimulationStateAccessor {
|
||||||
|
@Accessor("crafts")
|
||||||
|
Map<IPatternDetails, Long> getCrafts();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
package com.extendedae_plus.mixin.ae2.autopattern;
|
||||||
|
|
||||||
|
import appeng.api.crafting.IPatternDetails;
|
||||||
|
import appeng.crafting.CraftingCalculation;
|
||||||
|
import appeng.crafting.CraftingPlan;
|
||||||
|
import appeng.crafting.inv.CraftingSimulationState;
|
||||||
|
import appeng.me.service.CraftingService;
|
||||||
|
import com.extendedae_plus.api.smartDoubling.ICraftingCalculationExt;
|
||||||
|
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
||||||
|
import com.extendedae_plus.config.ModConfigs;
|
||||||
|
import com.extendedae_plus.util.smartDoubling.PatternScaler;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mixin(value = CraftingSimulationState.class, remap = false)
|
||||||
|
public abstract class CraftingSimulationStateMixin {
|
||||||
|
/**
|
||||||
|
* 替换 CraftingPlan 构建逻辑,在此统一处理样板倍率
|
||||||
|
*/
|
||||||
|
@Inject(method = "buildCraftingPlan", at = @At("HEAD"))
|
||||||
|
private static void onBuildCraftingPlan(CraftingSimulationState state,
|
||||||
|
CraftingCalculation calculation,
|
||||||
|
long calculatedAmount,
|
||||||
|
CallbackInfoReturnable<CraftingPlan> cir) {
|
||||||
|
CraftingSimulationStateAccessor accessor = (CraftingSimulationStateAccessor) state;
|
||||||
|
Map<IPatternDetails, Long> crafts = accessor.getCrafts();
|
||||||
|
// 存放最终分配后的 crafts
|
||||||
|
Map<IPatternDetails, Long> finalCrafts = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
for (Map.Entry<IPatternDetails, Long> entry : crafts.entrySet()) {
|
||||||
|
IPatternDetails processingPattern = entry.getKey();
|
||||||
|
long totalAmount = entry.getValue();
|
||||||
|
|
||||||
|
// 非 AEProcessingPattern 直接保留
|
||||||
|
if (!(processingPattern instanceof ISmartDoublingAwarePattern aware)) {
|
||||||
|
finalCrafts.put(processingPattern, totalAmount);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean allowScaling = aware.eap$allowScaling();
|
||||||
|
int perCraftLimit = aware.eap$getMultiplierLimit();
|
||||||
|
|
||||||
|
|
||||||
|
if (!allowScaling || totalAmount <= 1) {
|
||||||
|
finalCrafts.put(processingPattern, totalAmount);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (perCraftLimit <= 0 && ModConfigs.SMART_SCALING_MAX_MULTIPLIER.get() > 0) {
|
||||||
|
perCraftLimit = ModConfigs.SMART_SCALING_MAX_MULTIPLIER.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (perCraftLimit <= 0) {
|
||||||
|
// 检查是否开启 provider 轮询分配功能
|
||||||
|
if (ModConfigs.PROVIDER_ROUND_ROBIN_ENABLE.getRaw()) {
|
||||||
|
CraftingService craftingService = (CraftingService) ((ICraftingCalculationExt) calculation).getGrid().getCraftingService();
|
||||||
|
int providerCount = Math.max(Iterables.size(craftingService.getProviders(processingPattern)), 1);
|
||||||
|
|
||||||
|
// totalAmount < providerCount → 只激活 totalAmount 台 provider
|
||||||
|
if (totalAmount < providerCount) {
|
||||||
|
providerCount = (int) totalAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
long base = totalAmount / providerCount;
|
||||||
|
long remainder = totalAmount % providerCount;
|
||||||
|
|
||||||
|
// base+1 组(数量 remainder 个)
|
||||||
|
if (remainder > 0) {
|
||||||
|
IPatternDetails scaledPlus = PatternScaler.createScaled(processingPattern, base + 1);
|
||||||
|
finalCrafts.merge(scaledPlus, remainder, Long::sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
// base 组(数量 providerCount - remainder 个)
|
||||||
|
long countBase = providerCount - remainder;
|
||||||
|
if (countBase > 0) {
|
||||||
|
IPatternDetails scaledBase = PatternScaler.createScaled(processingPattern, base);
|
||||||
|
finalCrafts.merge(scaledBase, countBase, Long::sum);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 未开启轮询 → 直接分配一次总量
|
||||||
|
IPatternDetails scaled = PatternScaler.createScaled(processingPattern, totalAmount);
|
||||||
|
finalCrafts.put(scaled, 1L);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 有限制 → 拆分 full + remainder
|
||||||
|
long fullCrafts = totalAmount / perCraftLimit;
|
||||||
|
long remainder = totalAmount % perCraftLimit;
|
||||||
|
|
||||||
|
if (fullCrafts > 0) {
|
||||||
|
IPatternDetails scaledFull = PatternScaler.createScaled(processingPattern, perCraftLimit);
|
||||||
|
finalCrafts.put(scaledFull, fullCrafts);
|
||||||
|
}
|
||||||
|
if (remainder > 0) {
|
||||||
|
IPatternDetails scaledRem = PatternScaler.createScaled(processingPattern, remainder);
|
||||||
|
finalCrafts.put(scaledRem, 1L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crafts.clear();
|
||||||
|
crafts.putAll(finalCrafts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
package com.extendedae_plus.mixin.ae2.autopattern;
|
|
||||||
|
|
||||||
import appeng.api.stacks.AEKey;
|
|
||||||
import appeng.crafting.CraftingTreeNode;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
|
||||||
|
|
||||||
@Mixin(CraftingTreeNode.class)
|
|
||||||
public interface CraftingTreeNodeAccessor {
|
|
||||||
@Accessor("what")
|
|
||||||
AEKey eap$getWhat();
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
package com.extendedae_plus.mixin.ae2.autopattern;
|
|
||||||
|
|
||||||
import appeng.api.stacks.KeyCounter;
|
|
||||||
import appeng.crafting.CraftingTreeNode;
|
|
||||||
import appeng.crafting.inv.CraftingSimulationState;
|
|
||||||
import com.extendedae_plus.util.smartDoubling.RequestedAmountHolder;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
|
||||||
|
|
||||||
@Mixin(value = CraftingTreeNode.class,remap = false)
|
|
||||||
public class CraftingTreeNodeMixin {
|
|
||||||
@Inject(method = "request(Lappeng/crafting/inv/CraftingSimulationState;JLappeng/api/stacks/KeyCounter;)V",
|
|
||||||
at = @At(value = "INVOKE",
|
|
||||||
target = "Lappeng/crafting/CraftingTreeNode;addContainerItems(Lappeng/api/stacks/AEKey;JLappeng/api/stacks/KeyCounter;)V"),
|
|
||||||
locals = LocalCapture.CAPTURE_FAILHARD)
|
|
||||||
private void captureRequestedAmount(CraftingSimulationState inv, long requestedAmount, KeyCounter containerItems, CallbackInfo ci) {
|
|
||||||
// push the requestedAmount before addContainerItems is called
|
|
||||||
RequestedAmountHolder.push(requestedAmount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
package com.extendedae_plus.mixin.ae2.autopattern;
|
|
||||||
|
|
||||||
import appeng.api.crafting.IPatternDetails;
|
|
||||||
import appeng.api.networking.crafting.ICraftingProvider;
|
|
||||||
import appeng.api.networking.crafting.ICraftingService;
|
|
||||||
import appeng.api.stacks.AEKey;
|
|
||||||
import appeng.crafting.CraftingCalculation;
|
|
||||||
import appeng.crafting.CraftingTreeNode;
|
|
||||||
import appeng.crafting.CraftingTreeProcess;
|
|
||||||
import appeng.crafting.pattern.AEProcessingPattern;
|
|
||||||
import appeng.me.service.CraftingService;
|
|
||||||
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
|
||||||
import com.extendedae_plus.api.crafting.ScaledProcessingPattern;
|
|
||||||
import com.extendedae_plus.config.ModConfigs;
|
|
||||||
import com.extendedae_plus.util.smartDoubling.PatternScaler;
|
|
||||||
import com.extendedae_plus.util.smartDoubling.RequestedAmountHolder;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.StreamSupport;
|
|
||||||
|
|
||||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注入 CraftingTreeProcess 构造器尾部:将 AEProcessingPattern 替换为 ScaledProcessingPattern
|
|
||||||
* 以确保后续执行使用放大后的输入/输出视图。
|
|
||||||
*/
|
|
||||||
@Mixin(CraftingTreeProcess.class)
|
|
||||||
public abstract class CraftingTreeProcessMixin {
|
|
||||||
|
|
||||||
@ModifyVariable(
|
|
||||||
method = "<init>(Lappeng/api/networking/crafting/ICraftingService;Lappeng/crafting/CraftingCalculation;Lappeng/api/crafting/IPatternDetails;Lappeng/crafting/CraftingTreeNode;)V",
|
|
||||||
at = @At("HEAD"),
|
|
||||||
argsOnly = true
|
|
||||||
)
|
|
||||||
private static IPatternDetails eap$replaceDetailsAtHead(IPatternDetails original, ICraftingService cc, CraftingCalculation job, IPatternDetails details, CraftingTreeNode craftingTreeNode) {
|
|
||||||
try {
|
|
||||||
// 若传入的 details 已经是缩放样板,且原始样板不允许缩放,则直接解包为原始样板
|
|
||||||
if (details instanceof ScaledProcessingPattern sp) {
|
|
||||||
var proc0 = sp.getOriginal();
|
|
||||||
if (proc0 instanceof ISmartDoublingAwarePattern aware0 && !aware0.eap$allowScaling()) {
|
|
||||||
return proc0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(details instanceof AEProcessingPattern proc)) return original;
|
|
||||||
|
|
||||||
// 若样板标记为不允许缩放,则直接跳过
|
|
||||||
if (proc instanceof ISmartDoublingAwarePattern aware && !aware.eap$allowScaling()) {
|
|
||||||
return original;
|
|
||||||
}
|
|
||||||
|
|
||||||
CraftingTreeNodeAccessor parentAcc = (CraftingTreeNodeAccessor) craftingTreeNode;
|
|
||||||
AEKey parentTarget = parentAcc.eap$getWhat();
|
|
||||||
long requested = RequestedAmountHolder.get();
|
|
||||||
RequestedAmountHolder.pop();
|
|
||||||
|
|
||||||
// 根据配置决定是否在 provider 间轮询分配请求量(默认开启)
|
|
||||||
long perProvider = 1L;
|
|
||||||
if (!ModConfigs.PROVIDER_ROUND_ROBIN_ENABLE.get()) {
|
|
||||||
// 关闭轮询:直接使用完整请求量,不需要查询 provider 列表
|
|
||||||
perProvider = requested;
|
|
||||||
if (perProvider <= 0) perProvider = 1L;
|
|
||||||
} else {
|
|
||||||
CraftingService craftingService = (CraftingService) cc;
|
|
||||||
Iterable<ICraftingProvider> providers = craftingService.getProviders(original);
|
|
||||||
|
|
||||||
// 计算 provider 数量;尝试用反射读取内部 providers 列表以避免消费迭代器
|
|
||||||
int size;
|
|
||||||
try {
|
|
||||||
var cls = providers.getClass();
|
|
||||||
var f = cls.getDeclaredField("providers"); // private ArrayList<ICraftingProvider>
|
|
||||||
f.setAccessible(true);
|
|
||||||
List<?> list = (List<?>) f.get(providers);
|
|
||||||
size = list == null ? 0 : list.size();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
// 反射失败回退为遍历计数(会消费迭代器)
|
|
||||||
size = (int) StreamSupport.stream(providers.spliterator(), false).count();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将 requested 在 providers 间均分,向上取整保证每个 provider 分配整数且总量不少于 requested
|
|
||||||
if (size > 0) {
|
|
||||||
perProvider = requested / size + ((requested % size) == 0 ? 0 : 1);
|
|
||||||
if (perProvider <= 0) perProvider = 1L;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用每-provider 的分配量来缩放样板
|
|
||||||
var scaled = PatternScaler.scale(proc, parentTarget, perProvider);
|
|
||||||
return scaled != null ? scaled : original;
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.warn("构建倍增样板出错", e);
|
|
||||||
e.printStackTrace();
|
|
||||||
return original;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -25,7 +25,7 @@ public class PatternProviderLogicContainsRedirectMixin {
|
||||||
try {
|
try {
|
||||||
if (o instanceof ScaledProcessingPattern scaled) {
|
if (o instanceof ScaledProcessingPattern scaled) {
|
||||||
IPatternDetails base = scaled.getOriginal();
|
IPatternDetails base = scaled.getOriginal();
|
||||||
if (base != null && list.indexOf(base) != -1) {
|
if (list.indexOf(base) != -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,124 +1,106 @@
|
||||||
package com.extendedae_plus.util.smartDoubling;
|
package com.extendedae_plus.util.smartDoubling;
|
||||||
|
|
||||||
import appeng.api.crafting.IPatternDetails.IInput;
|
import appeng.api.crafting.IPatternDetails;
|
||||||
|
import appeng.api.stacks.AEFluidKey;
|
||||||
|
import appeng.api.stacks.AEItemKey;
|
||||||
import appeng.api.stacks.AEKey;
|
import appeng.api.stacks.AEKey;
|
||||||
import appeng.api.stacks.GenericStack;
|
|
||||||
import appeng.crafting.pattern.AEProcessingPattern;
|
import appeng.crafting.pattern.AEProcessingPattern;
|
||||||
import com.extendedae_plus.api.crafting.ScaledProcessingPattern;
|
import com.extendedae_plus.api.crafting.ScaledProcessingPattern;
|
||||||
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
import com.extendedae_plus.api.crafting.ScaledProcessingPatternAdv;
|
||||||
import com.extendedae_plus.config.ModConfigs;
|
import net.neoforged.fml.loading.LoadingModList;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.List;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
public final class PatternScaler {
|
public final class PatternScaler {
|
||||||
private PatternScaler() {
|
private static final boolean advAvailable;
|
||||||
|
private static final Constructor<?> advCtor;
|
||||||
|
private static final Class<?> advIfaceClass;
|
||||||
|
|
||||||
|
static {
|
||||||
|
boolean available = false;
|
||||||
|
Constructor<?> ctor = null;
|
||||||
|
Class<?> iface = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 尝试加载扩展类
|
||||||
|
Class<?> clazz = Class.forName("com.extendedae_plus.api.crafting.ScaledProcessingPatternAdv");
|
||||||
|
ctor = clazz.getConstructor(IPatternDetails.class, long.class);
|
||||||
|
|
||||||
|
// 加载接口
|
||||||
|
iface = Class.forName("net.pedroksl.advanced_ae.common.patterns.IAdvPatternDetails");
|
||||||
|
|
||||||
|
// 检查是否安装 Advanced AE
|
||||||
|
if (LoadingModList.get() != null && LoadingModList.get().getModFileById("advanced_ae") != null) {
|
||||||
|
available = true;
|
||||||
|
}
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
advAvailable = available;
|
||||||
|
advCtor = ctor;
|
||||||
|
advIfaceClass = iface;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ScaledProcessingPattern scale(AEProcessingPattern base, AEKey target, long requestedAmount) {
|
private PatternScaler() {}
|
||||||
if (base == null) throw new IllegalArgumentException("base");
|
|
||||||
if (target == null) throw new IllegalArgumentException("target");
|
|
||||||
|
|
||||||
// 双保险:若样板标记为不允许缩放,直接放弃缩放(返回 null 表示调用方应保持原样板)
|
/**
|
||||||
if (base instanceof ISmartDoublingAwarePattern aware && !aware.eap$allowScaling()) {
|
* 创建缩放样板。
|
||||||
return null;
|
* 自动支持原版 AE 和可选 AAE 的 AdvProcessingPattern。
|
||||||
}
|
*/
|
||||||
|
public static IPatternDetails createScaled(IPatternDetails base, long multiplier) {
|
||||||
List<GenericStack> baseSparseInputs = base.getSparseInputs();
|
// 尝试 Advanced AE 扩展
|
||||||
List<GenericStack> baseSparseOutputs = base.getSparseOutputs();
|
if (advAvailable && advIfaceClass != null && advCtor != null) {
|
||||||
IInput[] baseInputs = base.getInputs();
|
try {
|
||||||
List<GenericStack> baseOutputs = base.getOutputs();
|
if (advIfaceClass.isInstance(base)) {
|
||||||
|
return (ScaledProcessingPatternAdv) advCtor.newInstance(base, multiplier);
|
||||||
// 新逻辑:不再对样板进行单位化处理
|
|
||||||
// 找到目标输出在 outputs 中的索引(尝试匹配 target,否则取第一个非空输出)
|
|
||||||
int targetOutIndex = -1;
|
|
||||||
for (int i = 0; i < baseOutputs.size(); i++) {
|
|
||||||
var out = baseOutputs.get(i);
|
|
||||||
if (out != null && target != null && out.what() != null && out.what().equals(target)) {
|
|
||||||
targetOutIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (targetOutIndex == -1) {
|
|
||||||
for (int i = 0; i < baseOutputs.size(); i++) {
|
|
||||||
if (baseOutputs.get(i) != null) {
|
|
||||||
targetOutIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException ignored) {
|
||||||
|
// 出错退回普通逻辑
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (targetOutIndex == -1 && !baseOutputs.isEmpty()) targetOutIndex = 0;
|
|
||||||
|
|
||||||
long perOperationTarget = 1L;
|
// 回退原版
|
||||||
if (targetOutIndex >= 0 && baseOutputs.get(targetOutIndex) != null) {
|
return new ScaledProcessingPattern(base, multiplier);
|
||||||
long amt = baseOutputs.get(targetOutIndex).amount();
|
}
|
||||||
if (amt > 0) perOperationTarget = amt;
|
|
||||||
|
/**
|
||||||
|
* 计算基于 limit 的最大允许倍率(单次输出主物品 ≤ limit)
|
||||||
|
*/
|
||||||
|
public static int getComputedMul(AEProcessingPattern proc, int limit) {
|
||||||
|
if (limit <= 0) return 0; // 0 = 不限制
|
||||||
|
|
||||||
|
long minMul = Long.MAX_VALUE;
|
||||||
|
|
||||||
|
for (IPatternDetails.IInput input : proc.getInputs()) {
|
||||||
|
long amt = input.getMultiplier();
|
||||||
|
if (amt <= 0) continue;
|
||||||
|
var possible = input.getPossibleInputs();
|
||||||
|
if (possible == null || possible.length == 0) continue;
|
||||||
|
AEKey key = possible[0].what();
|
||||||
|
long unitMul = getUnitMultiplier(key);
|
||||||
|
long limitInUnit = (long) limit * unitMul;
|
||||||
|
|
||||||
|
long allowed = limitInUnit / amt;
|
||||||
|
allowed = Math.max(1L, allowed);
|
||||||
|
minMul = Math.min(minMul, allowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用最小整数倍(ceil)策略:直接选择满足请求的最小倍数
|
if (minMul == Long.MAX_VALUE) return 0; // 无有效输入 → 不限制
|
||||||
long multiplier = 1L;
|
return minMul > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) minMul;
|
||||||
if (requestedAmount > 0) {
|
}
|
||||||
long needed = requestedAmount / perOperationTarget + ((requestedAmount % perOperationTarget) == 0 ? 0 : 1);
|
|
||||||
multiplier = needed <= 1L ? 1L : needed;
|
private static long getUnitMultiplier(AEKey key) {
|
||||||
}
|
if (key instanceof AEItemKey) return 1L;
|
||||||
// 应用配置的最大倍数上限(0 表示不限制)
|
if (key instanceof AEFluidKey) return 1000L;
|
||||||
|
|
||||||
|
// 支持 Mekanism Chemical 等(反射安全)
|
||||||
try {
|
try {
|
||||||
int maxMul = ModConfigs.SMART_SCALING_MAX_MULTIPLIER.get();
|
if ("me.ramidzkh.mekae2.ae2.MekanismKey".equals(key.getClass().getName())) {
|
||||||
if (maxMul > 0 && multiplier > maxMul) {
|
return 1000L;
|
||||||
multiplier = maxMul;
|
|
||||||
}
|
}
|
||||||
} catch (Throwable ignore) {
|
} catch (Exception ignored) {
|
||||||
// 配置读取异常时不施加上限
|
|
||||||
}
|
}
|
||||||
|
return 1L;
|
||||||
// 构建压缩输入(将每个输入的 multiplier 翻倍,保留每个模板的原始数量)
|
|
||||||
IInput[] scaledInputs = new IInput[baseInputs.length];
|
|
||||||
for (int i = 0; i < baseInputs.length; i++) {
|
|
||||||
var in = baseInputs[i];
|
|
||||||
var template = in.getPossibleInputs();
|
|
||||||
GenericStack[] scaledTemplates = new GenericStack[template.length];
|
|
||||||
for (int j = 0; j < template.length; j++) {
|
|
||||||
scaledTemplates[j] = new GenericStack(template[j].what(), template[j].amount());
|
|
||||||
}
|
|
||||||
scaledInputs[i] = new ScaledProcessingPattern.Input(scaledTemplates, in.getMultiplier() * multiplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建压缩输出(List)
|
|
||||||
List<GenericStack> scaledCondensedOutputs = new ArrayList<>(baseOutputs.size());
|
|
||||||
for (int i = 0; i < baseOutputs.size(); i++) {
|
|
||||||
GenericStack out = baseOutputs.get(i);
|
|
||||||
if (out != null) {
|
|
||||||
scaledCondensedOutputs.add(new GenericStack(out.what(), out.amount() * multiplier));
|
|
||||||
} else {
|
|
||||||
scaledCondensedOutputs.add(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建稀疏表示(List,直接按 multiplier 放大)
|
|
||||||
List<GenericStack> scaledSparseInputs = new ArrayList<>(baseSparseInputs.size());
|
|
||||||
for (int i = 0; i < baseSparseInputs.size(); i++) {
|
|
||||||
var in = baseSparseInputs.get(i);
|
|
||||||
if (in != null) {
|
|
||||||
scaledSparseInputs.add(new GenericStack(in.what(), in.amount() * multiplier));
|
|
||||||
} else {
|
|
||||||
scaledSparseInputs.add(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
List<GenericStack> scaledSparseOutputs = new ArrayList<>(baseSparseOutputs.size());
|
|
||||||
for (int i = 0; i < baseSparseOutputs.size(); i++) {
|
|
||||||
var out = baseSparseOutputs.get(i);
|
|
||||||
if (out != null) {
|
|
||||||
scaledSparseOutputs.add(new GenericStack(out.what(), out.amount() * multiplier));
|
|
||||||
} else {
|
|
||||||
scaledSparseOutputs.add(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ScaledProcessingPattern(base,
|
|
||||||
base.getDefinition(),
|
|
||||||
scaledSparseInputs,
|
|
||||||
scaledSparseOutputs,
|
|
||||||
scaledInputs,
|
|
||||||
scaledCondensedOutputs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
package com.extendedae_plus.util.smartDoubling;
|
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
|
||||||
import java.util.Deque;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thread-local stack holder for requested amounts to support nested requests.
|
|
||||||
*/
|
|
||||||
public final class RequestedAmountHolder {
|
|
||||||
private static final ThreadLocal<Deque<Long>> HOLDER = ThreadLocal.withInitial(ArrayDeque::new);
|
|
||||||
|
|
||||||
private RequestedAmountHolder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Push a requested amount onto the thread-local stack.
|
|
||||||
*/
|
|
||||||
public static void push(long v) {
|
|
||||||
Deque<Long> dq = HOLDER.get();
|
|
||||||
dq.push(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pop the top value from the thread-local stack. Safe if empty.
|
|
||||||
*/
|
|
||||||
public static void pop() {
|
|
||||||
Deque<Long> dq = HOLDER.get();
|
|
||||||
if (dq.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dq.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Peek the current requested amount or return 0 if none.
|
|
||||||
*/
|
|
||||||
public static long get() {
|
|
||||||
Deque<Long> dq = HOLDER.get();
|
|
||||||
Long v = dq.peek();
|
|
||||||
return v == null ? 0L : v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
"plugin": "com.extendedae_plus.mixin.ExtendedAEPlusMixinPlugin",
|
"plugin": "com.extendedae_plus.mixin.ExtendedAEPlusMixinPlugin",
|
||||||
"mixins": [
|
"mixins": [
|
||||||
"advancedae.AdvPatternProviderLogicContainsRedirectMixin",
|
"advancedae.AdvPatternProviderLogicContainsRedirectMixin",
|
||||||
|
"advancedae.AdvProcessingPatternMixin",
|
||||||
"advancedae.accessor.AdvCraftingCPULogicAccessor",
|
"advancedae.accessor.AdvCraftingCPULogicAccessor",
|
||||||
"advancedae.accessor.AdvExecutingCraftingJobAccessor",
|
"advancedae.accessor.AdvExecutingCraftingJobAccessor",
|
||||||
"advancedae.accessor.AdvExecutingCraftingJobTaskProgressAccessor",
|
"advancedae.accessor.AdvExecutingCraftingJobTaskProgressAccessor",
|
||||||
|
|
@ -13,7 +14,6 @@
|
||||||
"advancedae.compat.PatternProviderLogicVirtualCompletionMixin",
|
"advancedae.compat.PatternProviderLogicVirtualCompletionMixin",
|
||||||
"advancedae.helpers.AdvPatternProviderLogicAdvancedMixin",
|
"advancedae.helpers.AdvPatternProviderLogicAdvancedMixin",
|
||||||
"advancedae.helpers.AdvPatternProviderLogicDoublingMixin",
|
"advancedae.helpers.AdvPatternProviderLogicDoublingMixin",
|
||||||
"advancedae.compat.PatternProviderLogicVirtualCompletionMixin",
|
|
||||||
"advancedae.menu.AdvPatternProviderMenuAdvancedMixin",
|
"advancedae.menu.AdvPatternProviderMenuAdvancedMixin",
|
||||||
"ae2.AEProcessingPatternMixin",
|
"ae2.AEProcessingPatternMixin",
|
||||||
"ae2.CraftingCalculationMixin",
|
"ae2.CraftingCalculationMixin",
|
||||||
|
|
@ -28,10 +28,10 @@
|
||||||
"ae2.accessor.PatternProviderLogicPatternInputsAccessor",
|
"ae2.accessor.PatternProviderLogicPatternInputsAccessor",
|
||||||
"ae2.accessor.PatternProviderLogicPatternsAccessor",
|
"ae2.accessor.PatternProviderLogicPatternsAccessor",
|
||||||
"ae2.accessor.PatternProviderMenuAdvancedAccessor",
|
"ae2.accessor.PatternProviderMenuAdvancedAccessor",
|
||||||
|
"ae2.autopattern.CraftingCalculationMixin",
|
||||||
"ae2.autopattern.CraftingServiceGetProvidersMixin",
|
"ae2.autopattern.CraftingServiceGetProvidersMixin",
|
||||||
"ae2.autopattern.CraftingTreeNodeAccessor",
|
"ae2.autopattern.CraftingSimulationStateAccessor",
|
||||||
"ae2.autopattern.CraftingTreeNodeMixin",
|
"ae2.autopattern.CraftingSimulationStateMixin",
|
||||||
"ae2.autopattern.CraftingTreeProcessMixin",
|
|
||||||
"ae2.autopattern.PatternProviderLogicContainsRedirectMixin",
|
"ae2.autopattern.PatternProviderLogicContainsRedirectMixin",
|
||||||
"ae2.compat.PatternProviderLogicCompatMixin",
|
"ae2.compat.PatternProviderLogicCompatMixin",
|
||||||
"ae2.helpers.InterfaceLogicChannelCardMixin",
|
"ae2.helpers.InterfaceLogicChannelCardMixin",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user