diff --git a/src/main/java/com/extendedae_plus/client/ModConfigScreen.java b/src/main/java/com/extendedae_plus/client/ModConfigScreen.java index 3780f02..62ffe64 100644 --- a/src/main/java/com/extendedae_plus/client/ModConfigScreen.java +++ b/src/main/java/com/extendedae_plus/client/ModConfigScreen.java @@ -16,6 +16,7 @@ public class ModConfigScreen extends Screen { private EditBox pageMultiplierBox; private EditBox wirelessMaxRangeBox; private CycleButton crossDimToggle; + private CycleButton providerRoundRobinToggle; public ModConfigScreen(Screen parent) { super(Component.translatable("screen.extendedae_plus.title")); @@ -48,11 +49,11 @@ public class ModConfigScreen extends Screen { row++; // cross dim toggle - crossDimToggle = this.addRenderableWidget( - CycleButton.onOffBuilder(ModConfigs.WIRELESS_CROSS_DIM_ENABLE.get()) - .create(rightX, y + row * rowHeight, boxWidth, 20, Component.translatable("config.extendedae_plus.wirelessCrossDimEnable"), - (b, v) -> {}) - ); + crossDimToggle = this.addRenderableWidget(createToggle(rightX, y + row * rowHeight, boxWidth, 20, ModConfigs.WIRELESS_CROSS_DIM_ENABLE.get())); + row++; + + // provider round-robin toggle (smart doubling) + providerRoundRobinToggle = this.addRenderableWidget(createToggle(rightX, y + row * rowHeight, boxWidth, 20, ModConfigs.PROVIDER_ROUND_ROBIN_ENABLE.get())); row++; // 按钮:保存、返回 @@ -74,16 +75,26 @@ public class ModConfigScreen extends Screen { int pageMul = clamp(parseIntOrDefault(pageMultiplierBox.getValue(), ModConfigs.PAGE_MULTIPLIER.get()), 1, 64); double maxRange = clamp(parseDoubleOrDefault(wirelessMaxRangeBox.getValue(), ModConfigs.WIRELESS_MAX_RANGE.get()), 1.0, 4096.0); boolean crossDim = crossDimToggle.getValue(); + boolean providerRoundRobin = providerRoundRobinToggle.getValue(); // 应用到 Forge 配置值 ModConfigs.PAGE_MULTIPLIER.set(pageMul); ModConfigs.WIRELESS_MAX_RANGE.set(maxRange); ModConfigs.WIRELESS_CROSS_DIM_ENABLE.set(crossDim); + ModConfigs.PROVIDER_ROUND_ROBIN_ENABLE.set(providerRoundRobin); // Forge 会在合适的时机写回到配置文件;部分改动可能需要重启游戏或世界才完全生效 onClose(); } + // Helper to create a boolean on/off CycleButton which shows localized on/off text + private CycleButton createToggle(int x, int y, int width, int height, boolean initial) { + CycleButton btn = CycleButton.onOffBuilder(initial) + .create(x, y, width, height, Component.empty(), (b, v) -> b.setMessage(Component.translatable(v ? "config.extendedae_plus.state_on" : "config.extendedae_plus.state_off"))); + btn.setMessage(Component.translatable(initial ? "config.extendedae_plus.state_on" : "config.extendedae_plus.state_off")); + return btn; + } + @Override public void onClose() { Minecraft.getInstance().setScreen(parent); @@ -107,6 +118,7 @@ public class ModConfigScreen extends Screen { g.drawString(this.font, Component.translatable("config.extendedae_plus.pageMultiplier_with_range"), leftX, y + 0 * rowHeight + 6, labelColor, false); g.drawString(this.font, Component.translatable("config.extendedae_plus.wirelessMaxRange_with_range"), leftX, y + 1 * rowHeight + 6, labelColor, false); 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); } 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 4a1409b..8689d7e 100644 --- a/src/main/java/com/extendedae_plus/config/ModConfigs.java +++ b/src/main/java/com/extendedae_plus/config/ModConfigs.java @@ -7,6 +7,7 @@ public final class ModConfigs { public static final ForgeConfigSpec.IntValue PAGE_MULTIPLIER; public static final ForgeConfigSpec.DoubleValue WIRELESS_MAX_RANGE; public static final ForgeConfigSpec.BooleanValue WIRELESS_CROSS_DIM_ENABLE; + public static final ForgeConfigSpec.BooleanValue PROVIDER_ROUND_ROBIN_ENABLE; static { ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder(); @@ -32,6 +33,15 @@ public final class ModConfigs { "是否允许无线收发器跨维度建立连接", "开启后,从端可连接到不同维度的主端(忽略距离限制)") .define("wirelessCrossDimEnable", true); + + // 智能倍增后,是否在样板供应器间轮询分配请求量(开启:按 provider 均分;关闭:不拆分) + PROVIDER_ROUND_ROBIN_ENABLE = builder + .comment( + "智能倍增时是否对样板供应器轮询分配", + "仅多个供应器有相同样板时生效,开启后请求会均分到所有可用供应器,关闭则全部分配给单一供应器", + "注意:所有相关供应器需开启智能倍增,否则可能失效", + "默认: true") + .define("providerRoundRobinEnable", true); builder.pop(); COMMON_SPEC = builder.build(); } diff --git a/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingCalculationAccessor.java b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingCalculationAccessor.java deleted file mode 100644 index 96bb300..0000000 --- a/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingCalculationAccessor.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.extendedae_plus.mixin.autopattern; - -import appeng.api.stacks.AEKey; -import appeng.crafting.CraftingCalculation; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(CraftingCalculation.class) -public interface CraftingCalculationAccessor { - @Accessor("output") - AEKey extendedae_plus$getOutput(); - - @Accessor("requestedAmount") - long extendedae_plus$getRequestedAmount(); -} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingServiceGetProvidersMixin.java b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingServiceGetProvidersMixin.java index 5604bbb..5ae4d03 100644 --- a/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingServiceGetProvidersMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingServiceGetProvidersMixin.java @@ -16,7 +16,7 @@ public class CraftingServiceGetProvidersMixin { @ModifyArg(method = "getProviders(Lappeng/api/crafting/IPatternDetails;)Ljava/lang/Iterable;", at = @At(value = "INVOKE", target = "Lappeng/me/service/helpers/NetworkCraftingProviders;getMediums(Lappeng/api/crafting/IPatternDetails;)Ljava/lang/Iterable;"), index = 0) - private IPatternDetails extendedae_plus$modifyGetProvidersArg(IPatternDetails original) { + private IPatternDetails eap$modifyGetProvidersArg(IPatternDetails original) { IPatternDetails base = null; if (original instanceof ScaledProcessingPattern scaledProcessingPattern) { base = scaledProcessingPattern.getOriginal(); diff --git a/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeNodeAccessor.java b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeNodeAccessor.java index 81d60e2..b6a274f 100644 --- a/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeNodeAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeNodeAccessor.java @@ -7,10 +7,6 @@ import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(CraftingTreeNode.class) public interface CraftingTreeNodeAccessor { - @Accessor("what") - AEKey extendedae_plus$getWhat(); - - @Accessor("amount") - long extendedae_plus$getAmount(); + AEKey eap$getWhat(); } diff --git a/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeProcessMixin.java b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeProcessMixin.java index 644f1a3..9311e06 100644 --- a/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeProcessMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeProcessMixin.java @@ -1,23 +1,26 @@ package com.extendedae_plus.mixin.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.CraftBranchFailure; import appeng.crafting.CraftingCalculation; import appeng.crafting.CraftingTreeNode; import appeng.crafting.CraftingTreeProcess; -import appeng.crafting.inv.CraftingSimulationState; import appeng.crafting.pattern.AEProcessingPattern; -import com.extendedae_plus.util.PatternScaler; -import com.extendedae_plus.util.RequestedAmountHolder; +import appeng.me.service.CraftingService; import com.extendedae_plus.api.SmartDoublingAwarePattern; import com.extendedae_plus.content.ScaledProcessingPattern; +import com.extendedae_plus.util.PatternScaler; +import com.extendedae_plus.config.ModConfigs; +import com.extendedae_plus.util.RequestedAmountHolder; 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.ModifyVariable; +import java.util.List; +import java.util.stream.StreamSupport; + import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; /** @@ -27,14 +30,12 @@ import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; @Mixin(CraftingTreeProcess.class) public abstract class CraftingTreeProcessMixin { - @Shadow abstract void request(CraftingSimulationState inv, long times) throws CraftBranchFailure, InterruptedException; - @ModifyVariable( method = "(Lappeng/api/networking/crafting/ICraftingService;Lappeng/crafting/CraftingCalculation;Lappeng/api/crafting/IPatternDetails;Lappeng/crafting/CraftingTreeNode;)V", at = @At("HEAD"), argsOnly = true ) - private static IPatternDetails extendedae_plus$replaceDetailsAtHead(IPatternDetails original, ICraftingService cc, CraftingCalculation job, IPatternDetails details, CraftingTreeNode craftingTreeNode) { + private static IPatternDetails eap$replaceDetailsAtHead(IPatternDetails original, ICraftingService cc, CraftingCalculation job, IPatternDetails details, CraftingTreeNode craftingTreeNode) { try { // 若传入的 details 已经是缩放样板,且原始样板不允许缩放,则直接解包为原始样板 if (details instanceof ScaledProcessingPattern sp) { @@ -52,13 +53,45 @@ public abstract class CraftingTreeProcessMixin { } CraftingTreeNodeAccessor parentAcc = (CraftingTreeNodeAccessor) craftingTreeNode; - AEKey parentTarget = parentAcc.extendedae_plus$getWhat(); + AEKey parentTarget = parentAcc.eap$getWhat(); long requested = RequestedAmountHolder.get(); - // 使用当前线程栈顶的值进行缩放,不在此处清理;构造完成后应该由调用方的 pop 恢复状态 - var scaled = PatternScaler.scale(proc, parentTarget, requested); + + // 根据配置决定是否在 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 providers = craftingService.getProviders(original); + + // 计算 provider 数量;尝试用反射读取内部 providers 列表以避免消费迭代器 + int size; + try { + var cls = providers.getClass(); + var f = cls.getDeclaredField("providers"); // private ArrayList + 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; } } diff --git a/src/main/java/com/extendedae_plus/mixin/autopattern/PatternProviderLogicContainsRedirectMixin.java b/src/main/java/com/extendedae_plus/mixin/autopattern/PatternProviderLogicContainsRedirectMixin.java index 7674936..bd1500f 100644 --- a/src/main/java/com/extendedae_plus/mixin/autopattern/PatternProviderLogicContainsRedirectMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/autopattern/PatternProviderLogicContainsRedirectMixin.java @@ -21,7 +21,7 @@ public class PatternProviderLogicContainsRedirectMixin { value = "INVOKE", target = "Ljava/util/List;contains(Ljava/lang/Object;)Z") ) - private boolean extendedae_plus$patternsContains(List list, Object o) { + private boolean eap$patternsContains(List list, Object o) { try { if (o instanceof ScaledProcessingPattern scaled) { IPatternDetails base = scaled.getOriginal(); diff --git a/src/main/java/com/extendedae_plus/util/ArraySimplifier.java b/src/main/java/com/extendedae_plus/util/ArraySimplifier.java deleted file mode 100644 index 076eb31..0000000 --- a/src/main/java/com/extendedae_plus/util/ArraySimplifier.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.extendedae_plus.util; - -import java.util.Arrays; - -public class ArraySimplifier { - - // 计算两个数的GCD using Euclidean algorithm (long版本) - public static long gcd(long a, long b) { - while (b != 0) { - long temp = b; - b = a % b; - a = temp; - } - return a; - } - - // 计算整个数组的GCD - public static long findGCD(long[] arr) { - if (arr.length == 0) { - return 0; - } - long result = arr[0]; - for (int i = 1; i < arr.length; i++) { - result = gcd(result, arr[i]); - // 如果已经找到GCD为1,可以提前终止 - if (result == 1) { - break; - } - } - return result; - } - - // 简化数组:每个元素除以数组的GCD - public static long[] simplifyFraction(long[] arr) { - if (arr.length == 0) { - return new long[0]; - } - long gcd = findGCD(arr); - if (gcd == 0) { - // 如果GCD为0(所有元素为0),返回原数组的副本 - return Arrays.copyOf(arr, arr.length); - } - long[] simplified = new long[arr.length]; - for (int i = 0; i < arr.length; i++) { - simplified[i] = arr[i] / gcd; - } - return simplified; - } - - // 将两个数组合并为一个新数组(先放 a 后放 b) - public static long[] combine(long[] a, long[] b) { - long[] out = new long[a.length + b.length]; - System.arraycopy(a, 0, out, 0, a.length); - System.arraycopy(b, 0, out, a.length, b.length); - return out; - } - - // 寻找数组的 GCD,遇到 1 则立即返回 1(早期退出优化) - public static long findGCDWithEarlyExit(long[] arr) { - if (arr.length == 0) return 0; - long result = 0; - for (long v : arr) { - if (v == 1) return 1; // already irreducible - if (v == 0) continue; - if (result == 0) result = v; else result = gcd(result, v); - if (result == 1) return 1; - } - return result == 0 ? 0 : Math.abs(result); - } - - // 根据给定的 gcd 返回一个已除以 gcd 的新数组;如果 gcd==1 返回原数组(避免不必要的分配) - public static long[] simplifyByGcd(long[] arr, long gcd) { - if (gcd <= 1) return arr; - long[] out = new long[arr.length]; - for (int i = 0; i < arr.length; i++) out[i] = arr[i] / gcd; - return out; - } -} \ No newline at end of file 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 5a2adf2..3a7c889 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -25,4 +25,14 @@ "extendedae_plus.tooltip.frequency": "Frequency: %d", "extendedae_plus.tooltip.master_mode": "Mode: %s", "extendedae_plus.tooltip.locked": "Locked: %s" + , + "screen.extendedae_plus.title": "ExtendedAE Plus Config", + "config.extendedae_plus.pageMultiplier": "Pattern Provider Page Multiplier", + "config.extendedae_plus.pageMultiplier_with_range": "Pattern Provider Page Multiplier (1-64)", + "config.extendedae_plus.wirelessMaxRange": "Wireless Max Range", + "config.extendedae_plus.wirelessMaxRange_with_range": "Wireless Max Range (1-4096)", + "config.extendedae_plus.wirelessCrossDimEnable": "Allow Wireless Cross-Dimension", + "config.extendedae_plus.providerRoundRobinEnable": "Enable Provider Round-Robin (Smart Doubling)", + "config.extendedae_plus.state_on": "On", + "config.extendedae_plus.state_off": "Off" } \ 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 e8db96c..8d1a562 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -44,6 +44,9 @@ "config.extendedae_plus.wirelessMaxRange": "无线最大距离", "config.extendedae_plus.wirelessMaxRange_with_range": "无线最大距离 (1-4096)", "config.extendedae_plus.wirelessCrossDimEnable": "无线收发器允许跨维度连接", + "config.extendedae_plus.providerRoundRobinEnable": "启用样板供应器轮询分配(智能翻倍)", + "config.extendedae_plus.state_on": "开", + "config.extendedae_plus.state_off": "关", "block.extendedae_plus.network_pattern_controller": "样板供应器状态控制器", "item.extendedae_plus.network_pattern_controller": "样板供应器状态控制器", "gui.extendedae_plus.global.toggle_blocking": "切换阻挡模式", diff --git a/src/main/resources/assets/extendedae_plus/textures/block/network_pattern_controller.png b/src/main/resources/assets/extendedae_plus/textures/block/network_pattern_controller.png index bcfbe84..56c43c2 100644 Binary files a/src/main/resources/assets/extendedae_plus/textures/block/network_pattern_controller.png and b/src/main/resources/assets/extendedae_plus/textures/block/network_pattern_controller.png differ diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index f855dd3..cb4d066 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -26,15 +26,15 @@ "jei.EncodePatternTransferHandlerMixin" ], "mixins": [ + "ae2.AEProcessingPatternMixin", "ae2.ContainerPatternEncodingTermMenuMixin", "ae2.CraftingCPUClusterMixin", "ae2.MEStorageMenuMixin", "ae2.PatternEncodingTermMenuMixin", "ae2.PatternProviderLogicAdvancedMixin", - "ae2.PatternProviderMenuAdvancedMixin", "ae2.PatternProviderLogicDoublingMixin", + "ae2.PatternProviderMenuAdvancedMixin", "ae2.PatternProviderMenuDoublingMixin", - "ae2.AEProcessingPatternMixin", "ae2.accessor.MEStorageMenuAccessor", "ae2.accessor.PatternEncodingTermMenuAccessor", "ae2.accessor.PatternProviderLogicAccessor", @@ -42,7 +42,7 @@ "ae2.accessor.PatternProviderLogicPatternsAccessor", "ae2.accessor.PatternProviderMenuAdvancedAccessor", "ae2WTlib.ContainerUWirelessExPatternTerminalMixin", - "autopattern.CraftingCalculationAccessor", + "autopattern.CraftingProviderListAccessor", "autopattern.CraftingServiceGetProvidersMixin", "autopattern.CraftingTreeNodeAccessor", "autopattern.CraftingTreeNodeMixin",