diff --git a/src/main/java/com/extendedae_plus/client/ModConfigScreen.java b/src/main/java/com/extendedae_plus/client/ModConfigScreen.java index b750c95..5a65d8d 100644 --- a/src/main/java/com/extendedae_plus/client/ModConfigScreen.java +++ b/src/main/java/com/extendedae_plus/client/ModConfigScreen.java @@ -18,6 +18,7 @@ public class ModConfigScreen extends Screen { private CycleButton crossDimToggle; private CycleButton providerRoundRobinToggle; private EditBox smartScalingMaxMulBox; + private EditBox smartScalingMinBenefitBox; private CycleButton showEncoderToggle; private CycleButton patternTerminalShowSlotsToggle; @@ -66,6 +67,13 @@ public class ModConfigScreen extends Screen { this.addRenderableWidget(smartScalingMaxMulBox); row++; + // smartScalingMinBenefitFactor: Int 1-1024 + smartScalingMinBenefitBox = new EditBox(this.font, rightX, y + row * rowHeight, boxWidth, 20, Component.translatable("config.extendedae_plus.smartScalingMinBenefitFactor")); + smartScalingMinBenefitBox.setValue(String.valueOf(ModConfigs.SMART_SCALING_MIN_BENEFIT_FACTOR.get())); + smartScalingMinBenefitBox.setFilter(s -> s.matches("\\d*") && parseIntOrDefault(s, 1) >= 1 && parseIntOrDefault(s, 1024) <= 1024); + this.addRenderableWidget(smartScalingMinBenefitBox); + row++; + // show encoder pattern player toggle showEncoderToggle = this.addRenderableWidget(createToggle(rightX, y + row * rowHeight, boxWidth, 20, ModConfigs.SHOW_ENCOD_PATTERN_PLAYER.get())); row++; @@ -95,6 +103,7 @@ public class ModConfigScreen extends Screen { boolean crossDim = crossDimToggle.getValue(); boolean providerRoundRobin = providerRoundRobinToggle.getValue(); int smartMaxMul = clamp(parseIntOrDefault(smartScalingMaxMulBox.getValue(), ModConfigs.SMART_SCALING_MAX_MULTIPLIER.get()), 0, 1048576); + int smartMinBenefit = clamp(parseIntOrDefault(smartScalingMinBenefitBox.getValue(), ModConfigs.SMART_SCALING_MIN_BENEFIT_FACTOR.get()), 1, 1024); boolean showEncoder = showEncoderToggle.getValue(); boolean patternShowSlots = patternTerminalShowSlotsToggle.getValue(); @@ -104,6 +113,7 @@ public class ModConfigScreen extends Screen { ModConfigs.WIRELESS_CROSS_DIM_ENABLE.set(crossDim); ModConfigs.PROVIDER_ROUND_ROBIN_ENABLE.set(providerRoundRobin); ModConfigs.SMART_SCALING_MAX_MULTIPLIER.set(smartMaxMul); + ModConfigs.SMART_SCALING_MIN_BENEFIT_FACTOR.set(smartMinBenefit); ModConfigs.SHOW_ENCOD_PATTERN_PLAYER.set(showEncoder); ModConfigs.PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT.set(patternShowSlots); @@ -144,8 +154,9 @@ public class ModConfigScreen extends Screen { g.drawString(this.font, Component.translatable("config.extendedae_plus.wirelessCrossDimEnable"), leftX, y + 2 * rowHeight + 6, labelColor, false); g.drawString(this.font, Component.translatable("config.extendedae_plus.providerRoundRobinEnable"), leftX, y + 3 * rowHeight + 6, labelColor, false); g.drawString(this.font, Component.translatable("config.extendedae_plus.smartScalingMaxMultiplier_with_range"), leftX, y + 4 * rowHeight + 6, labelColor, false); - g.drawString(this.font, Component.translatable("config.extendedae_plus.showEncoderPatternPlayer"), leftX, y + 5 * rowHeight + 6, labelColor, false); - g.drawString(this.font, Component.translatable("config.extendedae_plus.patternTerminalShowSlotsDefault"), leftX, y + 6 * rowHeight + 6, labelColor, false); + g.drawString(this.font, Component.translatable("config.extendedae_plus.smartScalingMinBenefitFactor"), leftX, y + 5 * rowHeight + 6, labelColor, false); + g.drawString(this.font, Component.translatable("config.extendedae_plus.showEncoderPatternPlayer"), leftX, y + 6 * rowHeight + 6, labelColor, false); + g.drawString(this.font, Component.translatable("config.extendedae_plus.patternTerminalShowSlotsDefault"), leftX, y + 7 * rowHeight + 6, labelColor, false); } private static int parseIntOrDefault(String s, int def) { diff --git a/src/main/java/com/extendedae_plus/config/ModConfigs.java b/src/main/java/com/extendedae_plus/config/ModConfigs.java index e07f9dc..5f8e545 100644 --- a/src/main/java/com/extendedae_plus/config/ModConfigs.java +++ b/src/main/java/com/extendedae_plus/config/ModConfigs.java @@ -11,6 +11,7 @@ public final class ModConfigs { public static final ForgeConfigSpec.BooleanValue PROVIDER_ROUND_ROBIN_ENABLE; public static final ForgeConfigSpec.BooleanValue PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT; public static final ForgeConfigSpec.IntValue SMART_SCALING_MAX_MULTIPLIER; + public static final ForgeConfigSpec.IntValue SMART_SCALING_MIN_BENEFIT_FACTOR; static { ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder(); @@ -61,6 +62,13 @@ public final class ModConfigs { "此倍数是针对单次样板产出的放大倍数上限,用于限制一次推送中按倍增缩放的规模") .defineInRange("smartScalingMaxMultiplier", 0, 0, 1048576); + // 智能倍增最小收益因子:若 requestedAmount < perOperationTarget * 此因子,则不启用缩放(默认 4) + SMART_SCALING_MIN_BENEFIT_FACTOR = builder + .comment( + "智能倍增最小收益因子(默认 4)", + "当目标请求量小于 perOperationTarget * 此因子 时,智能倍增将不被启用以避免无意义包装") + .defineInRange("smartScalingMinBenefitFactor", 4, 1, 1024); + // 模式访问终端(ExtendedAE 图样终端)默认是否显示槽位渲染(SlotsRow)。 // true: 默认显示(可通过界面按钮临时隐藏);false: 默认隐藏(可通过按钮显示) PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT = builder diff --git a/src/main/java/com/extendedae_plus/content/ScaledProcessingPattern.java b/src/main/java/com/extendedae_plus/content/ScaledProcessingPattern.java index 27a7c5a..0b672ff 100644 --- a/src/main/java/com/extendedae_plus/content/ScaledProcessingPattern.java +++ b/src/main/java/com/extendedae_plus/content/ScaledProcessingPattern.java @@ -17,27 +17,21 @@ import java.util.Objects; */ public final class ScaledProcessingPattern implements IPatternDetails { - private final AEProcessingPattern original; // 原始样板引用 - private final AEItemKey definition; // 样板物品 - private final GenericStack[] sparseInputs; // 缩放后的稀疏输入 - private final GenericStack[] sparseOutputs; // 缩放后的稀疏输出 - private final IInput[] inputs; // 缩放后的压缩输入 - private final GenericStack[] condensedOutputs; // 缩放后的压缩输出 + // 最小化实例字段:只保留原始样板引用、定义和倍数 + private final AEProcessingPattern original; // 原始样板引用 + private final AEItemKey definition; // 样板物品(直接委托自 original) + private final long multiplier; // 乘数(外部可视为视图参数) - public ScaledProcessingPattern( - AEProcessingPattern original, - AEItemKey definition, - GenericStack[] sparseInputs, - GenericStack[] sparseOutputs, - IInput[] inputs, - GenericStack[] condensedOutputs - ) { + // 延迟计算缓存(轻量化实例时避免在构造器中分配大数组) + 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.sparseInputs = Objects.requireNonNull(sparseInputs); - this.sparseOutputs = Objects.requireNonNull(sparseOutputs); - this.inputs = Objects.requireNonNull(inputs); - this.condensedOutputs = Objects.requireNonNull(condensedOutputs); + this.multiplier = multiplier <= 0 ? 1L : multiplier; } /* -------------------- API 实现 -------------------- */ @@ -53,25 +47,91 @@ public final class ScaledProcessingPattern implements IPatternDetails { @Override public IInput[] getInputs() { - return inputs; + 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; + } + } + } + return cached; } @Override public GenericStack[] getOutputs() { - return condensedOutputs; + 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; + } + } + } + return cached; } public GenericStack[] getSparseInputs() { - return sparseInputs; + 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; + } + } + } + return cached; } public GenericStack[] getSparseOutputs() { - return sparseOutputs; + 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; + } + } + } + return cached; } @Override public GenericStack getPrimaryOutput() { - if (condensedOutputs.length > 0) return condensedOutputs[0]; + var outs = getOutputs(); + if (outs.length > 0 && outs[0] != null) return outs[0]; return original.getPrimaryOutput(); } @@ -82,26 +142,29 @@ public final class ScaledProcessingPattern implements IPatternDetails { @Override public void pushInputsToExternalInventory(KeyCounter[] inputHolder, PatternInputSink inputSink) { - // 保持和 AEProcessingPattern 一致,用 sparseInputs 驱动 - if (sparseInputs.length == inputs.length) { + // 使用 lazy 计算的 sparseInputs 与 inputs 来驱动;当两者长度一致时直接委托 + GenericStack[] sInputs = getSparseInputs(); + IInput[] ins = getInputs(); + if (sInputs.length == ins.length) { IPatternDetails.super.pushInputsToExternalInventory(inputHolder, inputSink); - } else { - KeyCounter allInputs = new KeyCounter(); - for (KeyCounter counter : inputHolder) { - allInputs.addAll(counter); - } - for (GenericStack sparseInput : sparseInputs) { - 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); + 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); } } } @@ -117,18 +180,22 @@ public final class ScaledProcessingPattern implements IPatternDetails { 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; } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/ContainerPatternEncodingTermMenuMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/ContainerPatternEncodingTermMenuMixin.java index c2a5b31..0b09a57 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/ContainerPatternEncodingTermMenuMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/ContainerPatternEncodingTermMenuMixin.java @@ -113,11 +113,12 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo } } - // 服务器端:在构造样板返回前插入编码玩家的名称 - @Inject(method = "encodePattern", at = @At("TAIL"), remap = false, cancellable = true) - private void eap$writeEncodePlayerToPattern(CallbackInfoReturnable cir) { + @Inject(method = "encodePattern", at = @At("RETURN"), remap = false, cancellable = true) + private void onEncodePatternReturn(CallbackInfoReturnable cir) { ItemStack itemStack = cir.getReturnValue(); - itemStack.getOrCreateTag().putString("encodePlayer", this.epp$player.getGameProfile().getName()); - cir.setReturnValue(itemStack); + if (itemStack != null && !itemStack.isEmpty()) { + itemStack.getOrCreateTag().putString("encodePlayer", this.epp$player.getGameProfile().getName()); + cir.setReturnValue(itemStack); + } } } diff --git a/src/main/java/com/extendedae_plus/util/PatternScaler.java b/src/main/java/com/extendedae_plus/util/PatternScaler.java index 65aa98f..12acabd 100644 --- a/src/main/java/com/extendedae_plus/util/PatternScaler.java +++ b/src/main/java/com/extendedae_plus/util/PatternScaler.java @@ -1,15 +1,11 @@ package com.extendedae_plus.util; -import appeng.api.crafting.IPatternDetails.IInput; import appeng.api.stacks.AEKey; import appeng.api.stacks.GenericStack; import appeng.crafting.pattern.AEProcessingPattern; -import com.extendedae_plus.content.ScaledProcessingPattern; import com.extendedae_plus.api.SmartDoublingAwarePattern; import com.extendedae_plus.config.ModConfigs; - - -import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; +import com.extendedae_plus.content.ScaledProcessingPattern; public final class PatternScaler { private PatternScaler() { @@ -24,9 +20,6 @@ public final class PatternScaler { return null; } - GenericStack[] baseSparseInputs = base.getSparseInputs(); - GenericStack[] baseSparseOutputs = base.getSparseOutputs(); - IInput[] baseInputs = base.getInputs(); GenericStack[] baseOutputs = base.getOutputs(); // 新逻辑:不再对样板进行单位化处理 @@ -61,6 +54,15 @@ public final class PatternScaler { long needed = requestedAmount / perOperationTarget + ((requestedAmount % perOperationTarget) == 0 ? 0 : 1); multiplier = needed <= 1L ? 1L : needed; } + // 小请求绕过:若请求量小且不会带来收益,则不启用缩放(返回 null) + try { + int minBenefit = ModConfigs.SMART_SCALING_MIN_BENEFIT_FACTOR.get(); + if (minBenefit > 1 && requestedAmount > 0 && requestedAmount < perOperationTarget * (long) minBenefit) { + return null; + } + } catch (Throwable ignore) { + // 配置读取异常时保持默认行为(不绕过) + } // 应用配置的最大倍数上限(0 表示不限制) try { int maxMul = ModConfigs.SMART_SCALING_MAX_MULTIPLIER.get(); @@ -71,48 +73,7 @@ public final class PatternScaler { // 配置读取异常时不施加上限 } - // 构建压缩输入(将每个输入的 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); - } - - /* 4. 构建压缩输出 */ - GenericStack[] scaledCondensedOutputs = new GenericStack[baseOutputs.length]; - for (int i = 0; i < baseOutputs.length; i++) { - GenericStack out = baseOutputs[i]; - if (out != null) { - scaledCondensedOutputs[i] = new GenericStack(out.what(), out.amount() * multiplier); - } - } - - // 构建并打印稀疏表示(直接按 multiplier 放大) - GenericStack[] scaledSparseInputs = new GenericStack[baseSparseInputs.length]; - for (int i = 0; i < baseSparseInputs.length; i++) { - var in = baseSparseInputs[i]; - if (in != null) { - scaledSparseInputs[i] = new GenericStack(in.what(), in.amount() * multiplier); - } - } - GenericStack[] scaledSparseOutputs = new GenericStack[baseSparseOutputs.length]; - for (int i = 0; i < baseSparseOutputs.length; i++) { - var out = baseSparseOutputs[i]; - if (out != null) { - scaledSparseOutputs[i] = new GenericStack(out.what(), out.amount() * multiplier); - } - } - - return new ScaledProcessingPattern(base, - base.getDefinition(), - scaledSparseInputs, - scaledSparseOutputs, - scaledInputs, - scaledCondensedOutputs); + // 仅使用 multiplier 构建轻量化 ScaledProcessingPattern(具体视图按需计算) + return new ScaledProcessingPattern(base, base.getDefinition(), multiplier); } } diff --git a/src/main/resources/assets/extendedae_plus/lang/en_us.json b/src/main/resources/assets/extendedae_plus/lang/en_us.json index 6aac70c..3de5a8c 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -37,4 +37,5 @@ "config.extendedae_plus.state_off": "Off", "config.extendedae_plus.smartScalingMaxMultiplier": "Smart Scaling Max Multiplier", "config.extendedae_plus.smartScalingMaxMultiplier_with_range": "Smart Scaling Max Multiplier (0 = Unlimited)" + ,"config.extendedae_plus.smartScalingMinBenefitFactor": "Smart Scaling Min Benefit Factor (default 4)" } \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json index 83dcbbb..4e6ad18 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -58,5 +58,6 @@ "gui.extendedae_plus.global.all_on": "全部开启", "gui.extendedae_plus.global.all_off": "全部关闭", "config.extendedae_plus.smartScalingMaxMultiplier": "智能倍增最大倍数", - "config.extendedae_plus.smartScalingMaxMultiplier_with_range": "智能倍增最大倍数 (0为不限制)" + "config.extendedae_plus.smartScalingMaxMultiplier_with_range": "智能倍增最大倍数 (0为不限制)", + "config.extendedae_plus.smartScalingMinBenefitFactor": "智能倍增最小收益因子 (默认 4)" } \ No newline at end of file diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index d69fbb2..f07fb4c 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -35,6 +35,7 @@ "mixins": [ "ae2.AEProcessingPatternMixin", "ae2.CraftingCPUClusterMixin", + "ae2.EncodedPatternItemMixin", "ae2.accessor.MEStorageMenuAccessor", "ae2.accessor.PatternEncodingTermMenuAccessor", "ae2.accessor.PatternProviderLogicAccessor",