新版缩放
This commit is contained in:
parent
af0d9e25dc
commit
c02d0eb263
|
|
@ -4,200 +4,107 @@ import appeng.api.crafting.IPatternDetails;
|
|||
import appeng.api.stacks.AEItemKey;
|
||||
import appeng.api.stacks.AEKey;
|
||||
import appeng.api.stacks.GenericStack;
|
||||
import appeng.api.stacks.KeyCounter;
|
||||
import appeng.crafting.pattern.AEProcessingPattern;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 缩放后的处理样板,结构完全模拟 AEProcessingPattern。
|
||||
* 保持 sparse/condensed/inputs 的一致性,同时保存原始样板。
|
||||
*/
|
||||
public class ScaledProcessingPattern implements IPatternDetails {
|
||||
|
||||
// 最小化实例字段:只保留原始样板引用、定义和倍数
|
||||
private final AEProcessingPattern original; // 原始样板引用
|
||||
private final AEItemKey definition; // 样板物品(直接委托自 original)
|
||||
private final long multiplier; // 乘数(外部可视为视图参数)
|
||||
private final AEProcessingPattern original;
|
||||
private final long multiplier;
|
||||
|
||||
// 延迟计算缓存(轻量化实例时避免在构造器中分配大数组)
|
||||
private transient volatile IInput[] inputsCache;
|
||||
private transient volatile GenericStack[] outputsCache;
|
||||
private transient volatile GenericStack[] sparseInputsCache;
|
||||
private transient volatile GenericStack[] sparseOutputsCache;
|
||||
|
||||
public ScaledProcessingPattern(AEProcessingPattern original, AEItemKey definition, long multiplier) {
|
||||
this.original = Objects.requireNonNull(original);
|
||||
this.definition = Objects.requireNonNull(definition);
|
||||
this.multiplier = multiplier <= 0 ? 1L : multiplier;
|
||||
public ScaledProcessingPattern(AEProcessingPattern original, long multiplier) {
|
||||
if (original == null) throw new IllegalArgumentException("original cannot be null");
|
||||
if (multiplier <= 0) throw new IllegalArgumentException("multiplier must be > 0");
|
||||
this.original = original;
|
||||
this.multiplier = multiplier;
|
||||
}
|
||||
|
||||
/* -------------------- API 实现 -------------------- */
|
||||
|
||||
public AEProcessingPattern getOriginal() {
|
||||
return original;
|
||||
}
|
||||
public AEProcessingPattern getOriginal() { return original; }
|
||||
public long getMultiplier() { return multiplier; }
|
||||
|
||||
@Override
|
||||
public AEItemKey getDefinition() {
|
||||
return definition;
|
||||
return original.getDefinition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IInput[] getInputs() {
|
||||
IInput[] cached = this.inputsCache;
|
||||
if (cached == null) {
|
||||
synchronized (this) {
|
||||
cached = this.inputsCache;
|
||||
if (cached == null) {
|
||||
var base = original.getInputs();
|
||||
IInput[] arr = new IInput[base.length];
|
||||
for (int i = 0; i < base.length; i++) {
|
||||
var in = base[i];
|
||||
// 不复制 template 数组,直接复用原始的 possible inputs;仅放大 multiplier
|
||||
arr[i] = new Input(in.getPossibleInputs(), in.getMultiplier() * this.multiplier);
|
||||
}
|
||||
this.inputsCache = arr;
|
||||
cached = arr;
|
||||
}
|
||||
}
|
||||
IPatternDetails.IInput[] orig = original.getInputs();
|
||||
IInput[] scaled = new IInput[orig.length];
|
||||
for (int i = 0; i < orig.length; i++) {
|
||||
scaled[i] = new ScaledInput(orig[i], multiplier);
|
||||
}
|
||||
return cached;
|
||||
return scaled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericStack[] getOutputs() {
|
||||
GenericStack[] cached = this.outputsCache;
|
||||
if (cached == null) {
|
||||
synchronized (this) {
|
||||
cached = this.outputsCache;
|
||||
if (cached == null) {
|
||||
var baseOutputs = original.getOutputs();
|
||||
GenericStack[] arr = new GenericStack[baseOutputs.length];
|
||||
for (int i = 0; i < baseOutputs.length; i++) {
|
||||
var o = baseOutputs[i];
|
||||
if (o != null) arr[i] = new GenericStack(o.what(), o.amount() * this.multiplier);
|
||||
}
|
||||
this.outputsCache = arr;
|
||||
cached = arr;
|
||||
}
|
||||
GenericStack[] orig = original.getOutputs();
|
||||
GenericStack[] scaled = new GenericStack[orig.length];
|
||||
for (int i = 0; i < orig.length; i++) {
|
||||
if (orig[i] != null) {
|
||||
scaled[i] = new GenericStack(orig[i].what(), orig[i].amount() * multiplier);
|
||||
}
|
||||
}
|
||||
return cached;
|
||||
return scaled;
|
||||
}
|
||||
|
||||
// 兼容性方法
|
||||
public GenericStack[] getSparseInputs() {
|
||||
GenericStack[] cached = this.sparseInputsCache;
|
||||
if (cached == null) {
|
||||
synchronized (this) {
|
||||
cached = this.sparseInputsCache;
|
||||
if (cached == null) {
|
||||
var base = original.getSparseInputs();
|
||||
GenericStack[] arr = new GenericStack[base.length];
|
||||
for (int i = 0; i < base.length; i++) {
|
||||
var v = base[i];
|
||||
if (v != null) arr[i] = new GenericStack(v.what(), v.amount() * this.multiplier);
|
||||
}
|
||||
this.sparseInputsCache = arr;
|
||||
cached = arr;
|
||||
}
|
||||
GenericStack[] orig = original.getSparseInputs();
|
||||
GenericStack[] scaled = new GenericStack[orig.length];
|
||||
for (int i = 0; i < orig.length; i++) {
|
||||
if (orig[i] != null) {
|
||||
scaled[i] = new GenericStack(orig[i].what(), orig[i].amount() * multiplier);
|
||||
}
|
||||
}
|
||||
return cached;
|
||||
return scaled;
|
||||
}
|
||||
|
||||
public GenericStack[] getSparseOutputs() {
|
||||
GenericStack[] cached = this.sparseOutputsCache;
|
||||
if (cached == null) {
|
||||
synchronized (this) {
|
||||
cached = this.sparseOutputsCache;
|
||||
if (cached == null) {
|
||||
var base = original.getSparseOutputs();
|
||||
GenericStack[] arr = new GenericStack[base.length];
|
||||
for (int i = 0; i < base.length; i++) {
|
||||
var v = base[i];
|
||||
if (v != null) arr[i] = new GenericStack(v.what(), v.amount() * this.multiplier);
|
||||
}
|
||||
this.sparseOutputsCache = arr;
|
||||
cached = arr;
|
||||
}
|
||||
GenericStack[] orig = original.getSparseOutputs();
|
||||
GenericStack[] scaled = new GenericStack[orig.length];
|
||||
for (int i = 0; i < orig.length; i++) {
|
||||
if (orig[i] != null) {
|
||||
scaled[i] = new GenericStack(orig[i].what(), orig[i].amount() * multiplier);
|
||||
}
|
||||
}
|
||||
return cached;
|
||||
return scaled;
|
||||
}
|
||||
|
||||
// equals / hashCode 必须包含 multiplier!不同倍率 = 不同 key
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = original.hashCode();
|
||||
h = 31 * h + Long.hashCode(multiplier);
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericStack getPrimaryOutput() {
|
||||
var outs = getOutputs();
|
||||
if (outs.length > 0 && outs[0] != null) return outs[0];
|
||||
return original.getPrimaryOutput();
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof ScaledProcessingPattern sp)) return false;
|
||||
return sp.original.equals(this.original) && sp.multiplier == this.multiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsPushInputsToExternalInventory() {
|
||||
return original.supportsPushInputsToExternalInventory();
|
||||
public String toString() {
|
||||
return "Scaled[" + original.getDefinition().getItem() + " × " + multiplier + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushInputsToExternalInventory(KeyCounter[] inputHolder, PatternInputSink inputSink) {
|
||||
// 使用 lazy 计算的 sparseInputs 与 inputs 来驱动;当两者长度一致时直接委托
|
||||
GenericStack[] sInputs = getSparseInputs();
|
||||
IInput[] ins = getInputs();
|
||||
if (sInputs.length == ins.length) {
|
||||
IPatternDetails.super.pushInputsToExternalInventory(inputHolder, inputSink);
|
||||
return;
|
||||
private static class ScaledInput implements IInput {
|
||||
private final IInput delegate;
|
||||
private final long mul;
|
||||
|
||||
private ScaledInput(IInput delegate, long mul) {
|
||||
this.delegate = delegate;
|
||||
this.mul = mul;
|
||||
}
|
||||
|
||||
KeyCounter allInputs = new KeyCounter();
|
||||
for (KeyCounter counter : inputHolder) {
|
||||
allInputs.addAll(counter);
|
||||
}
|
||||
for (GenericStack sparseInput : sInputs) {
|
||||
if (sparseInput != null) {
|
||||
AEKey key = sparseInput.what();
|
||||
long amount = sparseInput.amount();
|
||||
long available = allInputs.get(key);
|
||||
if (available < amount) {
|
||||
throw new RuntimeException("Expected at least %d of %s when pushing scaled pattern, but only %d available"
|
||||
.formatted(amount, key, available));
|
||||
}
|
||||
inputSink.pushInput(key, amount);
|
||||
allInputs.remove(key, amount);
|
||||
}
|
||||
}
|
||||
@Override public GenericStack[] getPossibleInputs() { return delegate.getPossibleInputs(); }
|
||||
@Override public long getMultiplier() { return delegate.getMultiplier() * mul; }
|
||||
@Override public boolean isValid(AEKey input, Level level) { return delegate.isValid(input, level); }
|
||||
@Override public @Nullable AEKey getRemainingKey(AEKey template) { return delegate.getRemainingKey(template); }
|
||||
}
|
||||
|
||||
/* -------------------- 缩放输入代理 -------------------- */
|
||||
|
||||
public static final class Input implements IPatternDetails.IInput {
|
||||
private final GenericStack[] template;
|
||||
private final long multiplier;
|
||||
|
||||
public Input(GenericStack[] template, long multiplier) {
|
||||
this.template = template;
|
||||
this.multiplier = multiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericStack[] getPossibleInputs() {
|
||||
return this.template;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMultiplier() {
|
||||
return this.multiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(AEKey input, Level level) {
|
||||
return input.matches(this.template[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable AEKey getRemainingKey(AEKey template) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package com.extendedae_plus.ae.api.crafting;
|
||||
|
||||
import appeng.api.stacks.AEItemKey;
|
||||
import appeng.api.stacks.AEKey;
|
||||
import appeng.api.stacks.GenericStack;
|
||||
import appeng.api.stacks.KeyCounter;
|
||||
import appeng.crafting.pattern.AEProcessingPattern;
|
||||
import net.minecraft.core.Direction;
|
||||
|
|
@ -18,8 +16,8 @@ public final class ScaledProcessingPatternAdv extends ScaledProcessingPattern im
|
|||
|
||||
private final AdvPatternDetails adv;
|
||||
|
||||
public ScaledProcessingPatternAdv(AEProcessingPattern original, AEItemKey definition, long multiplier) {
|
||||
super(original, definition, multiplier);
|
||||
public ScaledProcessingPatternAdv(AEProcessingPattern original, long multiplier) {
|
||||
super(original, multiplier);
|
||||
this.adv = (AdvPatternDetails) original;
|
||||
}
|
||||
|
||||
|
|
@ -40,30 +38,30 @@ public final class ScaledProcessingPatternAdv extends ScaledProcessingPattern im
|
|||
|
||||
@Override
|
||||
public void pushInputsToExternalInventory(KeyCounter[] inputHolder, PatternInputSink inputSink) {
|
||||
// 使用 lazy 计算的 sparseInputs 与 inputs 来驱动;当两者长度一致时直接委托
|
||||
GenericStack[] sInputs = getSparseInputs();
|
||||
IInput[] ins = getInputs();
|
||||
if (sInputs.length == ins.length) {
|
||||
super.pushInputsToExternalInventory(inputHolder, inputSink);
|
||||
return;
|
||||
}
|
||||
|
||||
KeyCounter allInputs = new KeyCounter();
|
||||
for (KeyCounter counter : inputHolder) {
|
||||
allInputs.addAll(counter);
|
||||
}
|
||||
for (GenericStack sparseInput : sInputs) {
|
||||
if (sparseInput != null) {
|
||||
AEKey key = sparseInput.what();
|
||||
long amount = sparseInput.amount();
|
||||
long available = allInputs.get(key);
|
||||
if (available < amount) {
|
||||
throw new RuntimeException("Expected at least %d of %s when pushing scaled pattern, but only %d available"
|
||||
.formatted(amount, key, available));
|
||||
}
|
||||
inputSink.pushInput(key, amount);
|
||||
allInputs.remove(key, amount);
|
||||
}
|
||||
}
|
||||
// // 使用 lazy 计算的 sparseInputs 与 inputs 来驱动;当两者长度一致时直接委托
|
||||
// GenericStack[] sInputs = getSparseInputs();
|
||||
// IInput[] ins = getInputs();
|
||||
// if (sInputs.length == ins.length) {
|
||||
// super.pushInputsToExternalInventory(inputHolder, inputSink);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// KeyCounter allInputs = new KeyCounter();
|
||||
// for (KeyCounter counter : inputHolder) {
|
||||
// allInputs.addAll(counter);
|
||||
// }
|
||||
// for (GenericStack sparseInput : sInputs) {
|
||||
// if (sparseInput != null) {
|
||||
// AEKey key = sparseInput.what();
|
||||
// long amount = sparseInput.amount();
|
||||
// long available = allInputs.get(key);
|
||||
// if (available < amount) {
|
||||
// throw new RuntimeException("Expected at least %d of %s when pushing scaled pattern, but only %d available"
|
||||
// .formatted(amount, key, available));
|
||||
// }
|
||||
// inputSink.pushInput(key, amount);
|
||||
// allInputs.remove(key, amount);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
package com.extendedae_plus.mixin.ae2.autopattern;
|
||||
|
||||
import appeng.api.crafting.IPatternDetails;
|
||||
import appeng.crafting.inv.CraftingSimulationState;
|
||||
import appeng.crafting.pattern.AEProcessingPattern;
|
||||
import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern;
|
||||
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
||||
import com.extendedae_plus.util.smartDoubling.PatternScaler;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(value = CraftingSimulationState.class, remap = false)
|
||||
public abstract class CraftingSimulationStateMixin {
|
||||
|
||||
@Shadow private Map<IPatternDetails, Long> crafts;
|
||||
|
||||
/** 仅用于无限制缩放时的合并缓存 */
|
||||
private final Map<AEProcessingPattern, ScaledProcessingPattern> scaledCache = new IdentityHashMap<>();
|
||||
|
||||
@Inject(method = "addCrafting", at = @At("HEAD"), cancellable = true)
|
||||
private void onAddCrafting(IPatternDetails details, long craftsAmount, CallbackInfo ci) {
|
||||
ci.cancel();
|
||||
if (craftsAmount <= 0 || details == null) return;
|
||||
|
||||
if (details instanceof AEProcessingPattern processingPattern) {
|
||||
boolean allowScaling = true;
|
||||
int perCraftLimit = 0;
|
||||
|
||||
if (processingPattern instanceof ISmartDoublingAwarePattern aware) {
|
||||
allowScaling = aware.eap$allowScaling();
|
||||
perCraftLimit = aware.eap$getMultiplierLimit();
|
||||
}
|
||||
|
||||
if (!allowScaling) {
|
||||
crafts.merge(processingPattern, craftsAmount, Long::sum);
|
||||
return;
|
||||
}
|
||||
|
||||
// === 需求为 1 → 直接用原样板 ===
|
||||
if (craftsAmount == 1) {
|
||||
crafts.merge(processingPattern, 1L, Long::sum);
|
||||
return;
|
||||
}
|
||||
|
||||
// === 计算实际限制 ===
|
||||
if (perCraftLimit > 0) {
|
||||
perCraftLimit = Math.min(perCraftLimit, PatternScaler.getComputedMul(processingPattern, perCraftLimit));
|
||||
}
|
||||
|
||||
if (perCraftLimit <= 0) {
|
||||
// 无限制 → 合并缩放
|
||||
mergeUnlimited(processingPattern, craftsAmount);
|
||||
} else {
|
||||
// 有限制 → 拆分
|
||||
splitLimited(processingPattern, craftsAmount, perCraftLimit);
|
||||
}
|
||||
|
||||
} else {
|
||||
crafts.merge(details, craftsAmount, Long::sum);
|
||||
}
|
||||
}
|
||||
|
||||
/** 无限制:合并倍率 */
|
||||
private void mergeUnlimited(AEProcessingPattern original, long multiplier) {
|
||||
ScaledProcessingPattern existing = scaledCache.get(original);
|
||||
long total = multiplier;
|
||||
|
||||
if (existing != null) {
|
||||
total += existing.getMultiplier();
|
||||
crafts.remove(existing);
|
||||
}
|
||||
|
||||
IPatternDetails scaled = PatternScaler.createScaled(original, total);
|
||||
if (scaled instanceof ScaledProcessingPattern sp) {
|
||||
scaledCache.put(original, sp);
|
||||
crafts.put(sp, 1L);
|
||||
} else {
|
||||
crafts.put(original, total); // 退化为原样板
|
||||
}
|
||||
}
|
||||
|
||||
/** 有限制:拆分 full + remainder */
|
||||
private void splitLimited(AEProcessingPattern original, long totalAmount, int limit) {
|
||||
long fullCrafts = totalAmount / limit;
|
||||
long remainder = totalAmount % limit;
|
||||
|
||||
if (fullCrafts > 0) {
|
||||
IPatternDetails scaled = PatternScaler.createScaled(original, limit);
|
||||
crafts.merge(scaled, fullCrafts, Long::sum);
|
||||
}
|
||||
|
||||
if (remainder > 0) {
|
||||
IPatternDetails scaled = PatternScaler.createScaled(original, remainder);
|
||||
crafts.merge(scaled, 1L, Long::sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,184 +1,60 @@
|
|||
package com.extendedae_plus.util.smartDoubling;
|
||||
|
||||
import appeng.api.crafting.IPatternDetails;
|
||||
import appeng.api.stacks.AEFluidKey;
|
||||
import appeng.api.stacks.AEItemKey;
|
||||
import appeng.api.stacks.AEKey;
|
||||
import appeng.api.stacks.GenericStack;
|
||||
import appeng.crafting.pattern.AEProcessingPattern;
|
||||
import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern;
|
||||
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
||||
import com.extendedae_plus.config.ModConfig;
|
||||
import net.minecraftforge.fml.loading.LoadingModList;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
public final class PatternScaler {
|
||||
// ---------- 静态缓存反射 ----------
|
||||
private static final boolean advAvailable;
|
||||
private static final Constructor<?> advCtor;
|
||||
private static final Class<?> advIfaceClass;
|
||||
private PatternScaler() {}
|
||||
|
||||
static {
|
||||
boolean available = false;
|
||||
Constructor<?> ctor = null;
|
||||
Class<?> iface = null;
|
||||
|
||||
try {
|
||||
// 尝试加载扩展类
|
||||
Class<?> clazz = Class.forName("com.extendedae_plus.ae.api.crafting.ScaledProcessingPatternAdv");
|
||||
ctor = clazz.getConstructor(AEProcessingPattern.class, AEItemKey.class, long.class);
|
||||
|
||||
// 加载接口
|
||||
iface = Class.forName("net.pedroksl.advanced_ae.common.patterns.AdvPatternDetails");
|
||||
|
||||
// 检查是否安装 Advanced AE
|
||||
if (LoadingModList.get() != null && LoadingModList.get().getModFileById("advanced_ae") != null) {
|
||||
available = true;
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
/**
|
||||
* 创建缩放样板。
|
||||
* <p>如果 multiplier ≤ 1,直接返回原始样板(避免无意义包装)。</p>
|
||||
*/
|
||||
public static IPatternDetails createScaled(AEProcessingPattern original, long multiplier) {
|
||||
if (multiplier <= 1) {
|
||||
return original; // 关键:demand=1 或 limit=1 → 直接用原样板
|
||||
}
|
||||
|
||||
advAvailable = available;
|
||||
advCtor = ctor;
|
||||
advIfaceClass = iface;
|
||||
}
|
||||
|
||||
private PatternScaler() {
|
||||
}
|
||||
|
||||
public static ScaledProcessingPattern scale(AEProcessingPattern base, AEKey target, long requestedAmount) {
|
||||
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;
|
||||
}
|
||||
|
||||
GenericStack[] baseOutputs = base.getOutputs();
|
||||
|
||||
// 新逻辑:不再对样板进行单位化处理
|
||||
// 找到目标输出在 outputs 中的索引(尝试匹配 target,否则取第一个非空输出)
|
||||
int targetOutIndex = -1;
|
||||
for (int i = 0; i < baseOutputs.length; i++) {
|
||||
var out = baseOutputs[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.length; i++) {
|
||||
if (baseOutputs[i] != null) {
|
||||
targetOutIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (targetOutIndex == -1 && baseOutputs.length > 0) targetOutIndex = 0;
|
||||
|
||||
long perOperationTarget = 1L;
|
||||
if (targetOutIndex >= 0 && baseOutputs[targetOutIndex] != null) {
|
||||
long amt = baseOutputs[targetOutIndex].amount();
|
||||
if (amt > 0) perOperationTarget = amt;
|
||||
}
|
||||
|
||||
long multiplier = 1L; // 默认倍数
|
||||
|
||||
// ---------------------- 优先模式限制 ----------------------
|
||||
boolean patternHasLimit = false;
|
||||
if (base instanceof ISmartDoublingAwarePattern aware) {
|
||||
int patternMulLimit = aware.eap$getMultiplierLimit();
|
||||
if (patternMulLimit > 0) {
|
||||
multiplier = patternMulLimit; // 直接使用模式限制作为倍数
|
||||
patternHasLimit = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 全局逻辑(仅在没有模式限制时生效) ----------------------
|
||||
if (!patternHasLimit && requestedAmount > 0) {
|
||||
// 计算满足请求量的最小倍数
|
||||
long needed = requestedAmount / perOperationTarget + ((requestedAmount % perOperationTarget) == 0 ? 0 : 1);
|
||||
multiplier = Math.max(needed, 1L);
|
||||
|
||||
// 应用全局上限
|
||||
int maxMul = ModConfig.INSTANCE.smartScalingMaxMultiplier;
|
||||
if (maxMul > 0 && multiplier > maxMul) multiplier = maxMul;
|
||||
}
|
||||
|
||||
// ---------------------- 小请求绕过 ----------------------
|
||||
try {
|
||||
int minBenefit = ModConfig.INSTANCE.smartScalingMinBenefitFactor;
|
||||
if (minBenefit > 1 && requestedAmount > 0 && requestedAmount < perOperationTarget * (long) minBenefit) {
|
||||
return null;
|
||||
}
|
||||
} catch (Throwable ignore) {}
|
||||
|
||||
// ---------- Advanced AE 扩展 ----------
|
||||
if (advAvailable && advIfaceClass != null && advCtor != null) {
|
||||
try {
|
||||
if (advIfaceClass.isInstance(base)) {
|
||||
return (ScaledProcessingPattern) advCtor.newInstance(base, base.getDefinition(), multiplier);
|
||||
}
|
||||
} catch (Throwable ignore) {
|
||||
// 出错就退回普通逻辑
|
||||
}
|
||||
}
|
||||
|
||||
// 仅使用 multiplier 构建轻量化 ScaledProcessingPattern(具体视图按需计算)
|
||||
return new ScaledProcessingPattern(base, base.getDefinition(), multiplier);
|
||||
}
|
||||
|
||||
public static int getComputedMul(AEProcessingPattern proc, int limit) {
|
||||
int computedMul = 0; // 默认 0 表示不限制
|
||||
|
||||
if (limit > 0) {
|
||||
long minMul = Long.MAX_VALUE;
|
||||
|
||||
for (var input : proc.getInputs()) {
|
||||
long amt = input.getMultiplier();
|
||||
if (amt <= 0) continue;
|
||||
|
||||
AEKey key = input.getPossibleInputs()[0].what();
|
||||
|
||||
// 使用统一单位换算
|
||||
long unitMultiplier = getUnitMultiplier(key);
|
||||
long limitInAEUnit = limit * unitMultiplier;
|
||||
|
||||
// 计算该输入允许的倍数
|
||||
long allowedMul = limitInAEUnit / amt;
|
||||
|
||||
// 保证至少为 1
|
||||
allowedMul = Math.max(1, allowedMul);
|
||||
|
||||
// 取最小值,保证所有输入都不超过 limit
|
||||
minMul = Math.min(minMul, allowedMul);
|
||||
}
|
||||
|
||||
if (minMul != Long.MAX_VALUE) {
|
||||
computedMul = (int) Math.min(minMul, Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
return computedMul;
|
||||
return new ScaledProcessingPattern(original, multiplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 AEKey 的单位换算系数
|
||||
* 物品默认 1,流体默认 1000,其它类型可通过扩展接口提供
|
||||
* 计算基于 limit 的最大允许倍率(单次输出主物品 ≤ limit)
|
||||
*/
|
||||
public static int getComputedMul(AEProcessingPattern proc, int limit) {
|
||||
if (limit <= 0) return 0; // 0 = 不限制
|
||||
|
||||
long minMul = Long.MAX_VALUE;
|
||||
|
||||
for (var input : proc.getInputs()) {
|
||||
long amt = input.getMultiplier();
|
||||
if (amt <= 0) continue;
|
||||
|
||||
AEKey key = input.getPossibleInputs()[0].what();
|
||||
long unitMultiplier = getUnitMultiplier(key);
|
||||
long limitInAEUnit = (long) limit * unitMultiplier;
|
||||
|
||||
long allowedMul = limitInAEUnit / amt;
|
||||
allowedMul = Math.max(1, allowedMul); // 至少 1
|
||||
minMul = Math.min(minMul, allowedMul);
|
||||
}
|
||||
|
||||
return minMul != Long.MAX_VALUE ? (int) Math.min(minMul, Integer.MAX_VALUE) : 0;
|
||||
}
|
||||
|
||||
private static long getUnitMultiplier(AEKey key) {
|
||||
if (key instanceof AEItemKey) return 1L;
|
||||
if (key instanceof AEFluidKey) return 1000L;
|
||||
|
||||
// 支持 Mekanism Chemical 等(反射安全)
|
||||
try {
|
||||
// 反射判断扩展 Key 类型
|
||||
if (key.getClass().getName().equals("me.ramidzkh.mekae2.ae2.MekanismKey")) {
|
||||
if ("me.ramidzkh.mekae2.ae2.MekanismKey".equals(key.getClass().getName())) {
|
||||
return 1000L;
|
||||
}
|
||||
// 根据需要继续增加
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
return 1L; // 默认单位
|
||||
return 1L;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -53,9 +53,7 @@
|
|||
"ae2.accessor.PatternProviderMenuAccessor",
|
||||
"ae2.autopattern.CraftingProviderListAccessor",
|
||||
"ae2.autopattern.CraftingServiceGetProvidersMixin",
|
||||
"ae2.autopattern.CraftingTreeNodeAccessor",
|
||||
"ae2.autopattern.CraftingTreeNodeMixin",
|
||||
"ae2.autopattern.CraftingTreeProcessMixin",
|
||||
"ae2.autopattern.CraftingSimulationStateMixin",
|
||||
"ae2.autopattern.PatternProviderLogicContainsRedirectMixin",
|
||||
"ae2.compat.PatternProviderCompatMixin",
|
||||
"ae2.compat.PatternProviderLogicCompatMixin",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user