From 6924ff968a5522fb91353b5c342c8392da3bfdae Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Wed, 27 Aug 2025 21:44:06 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E5=9C=A8=E6=9E=84=E9=80=A0=E5=90=88?= =?UTF-8?q?=E6=88=90=E6=A0=91=E5=89=8D=EF=BC=8C=E5=AF=B9=E6=A0=B7=E6=9D=BF?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E8=AE=BE=E7=BD=AE=EF=BC=8C=E4=BD=BF=E7=94=A8?= =?UTF-8?q?ScaledProcessingPattern=E4=BB=A3=E6=9B=BFAEProcessingPattern?= =?UTF-8?q?=EF=BC=8C=E5=AE=9E=E7=8E=B0=E6=A0=B7=E6=9D=BF=E7=9A=84=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=80=8D=E5=A2=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/ScaledProcessingPattern.java | 136 ++++++++++++++++++ .../mixin/ae2/CraftingTreeProcessMixin.java | 45 ++++++ .../accessor/CraftingCalculationAccessor.java | 15 ++ .../accessor/CraftingTreeNodeAccessor.java | 16 +++ .../extendedae_plus/util/PatternScaler.java | 88 ++++++++++++ .../resources/extendedae_plus.mixins.json | 10 +- 6 files changed, 307 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/content/ScaledProcessingPattern.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/CraftingTreeProcessMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingCalculationAccessor.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingTreeNodeAccessor.java create mode 100644 src/main/java/com/extendedae_plus/util/PatternScaler.java diff --git a/src/main/java/com/extendedae_plus/content/ScaledProcessingPattern.java b/src/main/java/com/extendedae_plus/content/ScaledProcessingPattern.java new file mode 100644 index 0000000..27a7c5a --- /dev/null +++ b/src/main/java/com/extendedae_plus/content/ScaledProcessingPattern.java @@ -0,0 +1,136 @@ +package com.extendedae_plus.content; + +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 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; // 缩放后的压缩输出 + + public ScaledProcessingPattern( + AEProcessingPattern original, + AEItemKey definition, + GenericStack[] sparseInputs, + GenericStack[] sparseOutputs, + IInput[] inputs, + GenericStack[] condensedOutputs + ) { + 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); + } + + /* -------------------- API 实现 -------------------- */ + + public AEProcessingPattern getOriginal() { + return original; + } + + @Override + public AEItemKey getDefinition() { + return definition; + } + + @Override + public IInput[] getInputs() { + return inputs; + } + + @Override + public GenericStack[] getOutputs() { + return condensedOutputs; + } + + public GenericStack[] getSparseInputs() { + return sparseInputs; + } + + public GenericStack[] getSparseOutputs() { + return sparseOutputs; + } + + @Override + public GenericStack getPrimaryOutput() { + if (condensedOutputs.length > 0) return condensedOutputs[0]; + return original.getPrimaryOutput(); + } + + @Override + public boolean supportsPushInputsToExternalInventory() { + return original.supportsPushInputsToExternalInventory(); + } + + @Override + public void pushInputsToExternalInventory(KeyCounter[] inputHolder, PatternInputSink inputSink) { + // 保持和 AEProcessingPattern 一致,用 sparseInputs 驱动 + if (sparseInputs.length == inputs.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); + } + } + } + } + + /* -------------------- 缩放输入代理 -------------------- */ + + 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; + } + + public GenericStack[] getPossibleInputs() { + return this.template; + } + + public long getMultiplier() { + return this.multiplier; + } + + public boolean isValid(AEKey input, Level level) { + return input.matches(this.template[0]); + } + + public @Nullable AEKey getRemainingKey(AEKey template) { + return null; + } + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/CraftingTreeProcessMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/CraftingTreeProcessMixin.java new file mode 100644 index 0000000..47c21fa --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/CraftingTreeProcessMixin.java @@ -0,0 +1,45 @@ +package com.extendedae_plus.mixin.ae2; + +import appeng.api.crafting.IPatternDetails; +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 com.extendedae_plus.mixin.ae2.accessor.CraftingCalculationAccessor; +import com.extendedae_plus.mixin.ae2.accessor.CraftingTreeNodeAccessor; +import com.extendedae_plus.util.PatternScaler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +/** + * 注入 CraftingTreeProcess 构造器尾部:将 AEProcessingPattern 替换为 ScaledProcessingPattern + * 以确保后续执行使用放大后的输入/输出视图。 + */ +@Mixin(CraftingTreeProcess.class) +public class CraftingTreeProcessMixin { + + @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) { + try { + if (!(details instanceof AEProcessingPattern proc)) return original; + + CraftingCalculationAccessor jobAcc = (CraftingCalculationAccessor) job; + long requested = jobAcc.extendedae_plus$getRequestedAmount(); + + CraftingTreeNodeAccessor parentAcc = (CraftingTreeNodeAccessor) craftingTreeNode; + AEKey parentTarget = parentAcc.extendedae_plus$getWhat(); + + System.out.println("[extendedae_plus] Replacing constructor details at HEAD for: " + parentTarget + " x " + requested); + + return PatternScaler.scale(proc, parentTarget, requested); + } catch (Exception e) { + System.err.println("[extendedae_plus] Error replacing pattern at HEAD: " + e.getMessage()); + e.printStackTrace(); + return original; + } + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingCalculationAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingCalculationAccessor.java new file mode 100644 index 0000000..859866b --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingCalculationAccessor.java @@ -0,0 +1,15 @@ +package com.extendedae_plus.mixin.ae2.accessor; + +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/ae2/accessor/CraftingTreeNodeAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingTreeNodeAccessor.java new file mode 100644 index 0000000..39b7dbc --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingTreeNodeAccessor.java @@ -0,0 +1,16 @@ +package com.extendedae_plus.mixin.ae2.accessor; + +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 extendedae_plus$getWhat(); + + @Accessor("amount") + long extendedae_plus$getAmount(); +} diff --git a/src/main/java/com/extendedae_plus/util/PatternScaler.java b/src/main/java/com/extendedae_plus/util/PatternScaler.java new file mode 100644 index 0000000..f57f620 --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/PatternScaler.java @@ -0,0 +1,88 @@ +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 java.util.Arrays; + +public final class PatternScaler { + 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"); + + GenericStack[] baseSparseInputs = base.getSparseInputs(); + GenericStack[] baseSparseOutputs = base.getSparseOutputs(); + IInput[] baseInputs = base.getInputs(); + GenericStack[] baseOutputs = base.getOutputs(); + + /* 1. 构建缩放后的 sparseInputs */ + GenericStack[] scaledSparseInputs = new GenericStack[baseSparseInputs.length]; + for (int i = 0; i < baseSparseInputs.length; i++) { + GenericStack in = baseSparseInputs[i]; + if (in != null) { + scaledSparseInputs[i] = new GenericStack(in.what(), requestedAmount); + } + } + + /* 2. 构建缩放后的 sparseOutputs */ + GenericStack[] scaledSparseOutputs = new GenericStack[baseSparseOutputs.length]; + for (int i = 0; i < baseSparseOutputs.length; i++) { + GenericStack out = baseSparseOutputs[i]; + if (out != null) { + scaledSparseOutputs[i] = new GenericStack(out.what(), requestedAmount); + } + } + + /* 3. 构建压缩输入(ScaledInput) */ + 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(), 1); + } + scaledInputs[i] = new ScaledProcessingPattern.Input(scaledTemplates, requestedAmount); + } + + /* 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(), requestedAmount); + } + } + + /* Debug 输出 */ + System.out.println("[extendedae_plus] 正在缩放样板:"); + System.out.println(" 原始样板: " + base); + System.out.println(" 目标物品: " + target); + System.out.println(" 请求数量: " + requestedAmount); + System.out.println(" 缩放后输入: " + Arrays.toString(scaledInputs)); + System.out.println(" 缩放后输出: " + Arrays.toString(scaledCondensedOutputs)); + System.out.println(" 缩放后稀疏输入: " + Arrays.toString(scaledSparseInputs)); + System.out.println(" 缩放后稀疏输出: " + Arrays.toString(scaledSparseOutputs)); + + + return new ScaledProcessingPattern(base, + base.getDefinition(), + scaledSparseInputs, + scaledSparseOutputs, + scaledInputs, + scaledCondensedOutputs); + } + + private static long safeMul(long a, long b) { + if (a == 0 || b == 0) return 0; + if (a > Long.MAX_VALUE / b) return Long.MAX_VALUE; + return a * b; + } +} diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 9e0c12e..878166e 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -23,15 +23,20 @@ "extendedae.HighlightButtonMixin", "extendedae.accessor.GuiExPatternTerminalAccessor", "extendedae.accessor.GuiExPatternTerminalSlotsRowAccessor", - "jei.EncodePatternTransferHandlerMixin", - "hooks.ModelBakeryMixin" + "hooks.ModelBakeryMixin", + "jei.EncodePatternTransferHandlerMixin" ], "mixins": [ + "ae2.AEProcessingPatternMixin", "ae2.ContainerPatternEncodingTermMenuMixin", + "ae2.CraftingCPUClusterMixin", + "ae2.CraftingTreeProcessMixin", "ae2.MEStorageMenuMixin", "ae2.PatternEncodingTermMenuMixin", "ae2.PatternProviderLogicAdvancedMixin", "ae2.PatternProviderMenuAdvancedMixin", + "ae2.accessor.CraftingCalculationAccessor", + "ae2.accessor.CraftingTreeNodeAccessor", "ae2.accessor.MEStorageMenuAccessor", "ae2.accessor.PatternEncodingTermMenuAccessor", "ae2.accessor.PatternProviderLogicAccessor", @@ -41,7 +46,6 @@ "extendedae.ContainerExPatternProviderMixin", "extendedae.ContainerExPatternTerminalMixin", "extendedae.ContainerWirelessExPatternTerminalMixin", - "ae2.CraftingCPUClusterMixin", "extendedae.PartExPatternProviderMixin", "extendedae.TileExPatternProviderMixin" ], From 1d55920af930504bdbd25c97f337ca52414fafb6 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Thu, 28 Aug 2025 12:28:25 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E5=AF=B9=E4=BE=9B=E5=BA=94=E5=99=A8?= =?UTF-8?q?=E5=8F=91=E9=85=8D=E6=9D=90=E6=96=99=E8=BF=9B=E8=A1=8C=E6=8B=A6?= =?UTF-8?q?=E6=88=AA=EF=BC=8C=E4=BD=BF=E7=94=A8ScaledProcessingPattern?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae2/CraftingServiceGetProvidersMixin.java | 29 ++++++++++++++ ...ernProviderLogicContainsRedirectMixin.java | 40 +++++++++++++++++++ .../resources/extendedae_plus.mixins.json | 7 +++- 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/CraftingServiceGetProvidersMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderLogicContainsRedirectMixin.java diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/CraftingServiceGetProvidersMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/CraftingServiceGetProvidersMixin.java new file mode 100644 index 0000000..2361c8a --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/CraftingServiceGetProvidersMixin.java @@ -0,0 +1,29 @@ +package com.extendedae_plus.mixin.ae2; + +import appeng.api.crafting.IPatternDetails; +import appeng.me.service.CraftingService; +import com.extendedae_plus.content.ScaledProcessingPattern; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +/** + * 在 CraftingService.getProviders 调用点修改传入的 IPatternDetails 参数(回退到网络注册的原始样板) + */ +@Mixin(value = CraftingService.class, remap = false) +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) { + IPatternDetails base = null; + if (original instanceof ScaledProcessingPattern scaledProcessingPattern) { + base = scaledProcessingPattern.getOriginal(); + System.out.println("[extendedae_plus] CraftingService.getProviders mixin invoked for: " + base); + } + return base == null ? original : base; + } +} + + diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderLogicContainsRedirectMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderLogicContainsRedirectMixin.java new file mode 100644 index 0000000..ec589d9 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderLogicContainsRedirectMixin.java @@ -0,0 +1,40 @@ +package com.extendedae_plus.mixin.ae2; + +import appeng.api.crafting.IPatternDetails; +import appeng.helpers.patternprovider.PatternProviderLogic; +import com.extendedae_plus.content.ScaledProcessingPattern; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.List; + +/** + * Redirect PatternProviderLogic.pushPattern 中对 List.contains 的调用, + * 在遇到缩放样板时回退匹配到原始样板实例。 + */ +@Mixin(value = PatternProviderLogic.class, remap = false) +public class PatternProviderLogicContainsRedirectMixin { + + @Redirect(method = "pushPattern", + at = @At( + value = "INVOKE", + target = "Ljava/util/List;contains(Ljava/lang/Object;)Z") + ) + private boolean extendedae_plus$patternsContains(List list, Object o) { + try { + if (o instanceof ScaledProcessingPattern scaled) { + IPatternDetails base = scaled.getOriginal(); + if (base != null && list.indexOf(base) != -1) { + System.out.println("[extendedae_plus] contains-redirect: matched base pattern for scaled pattern"); + return true; + } + } + // 使用 indexOf 避免再次触发对 List.contains 的 redirect(防止递归) + return list.indexOf(o) != -1; + } catch (Throwable t) { + t.printStackTrace(); + return list.indexOf(o) != -1; + } + } +} \ 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 878166e..1418956 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -27,13 +27,18 @@ "jei.EncodePatternTransferHandlerMixin" ], "mixins": [ - "ae2.AEProcessingPatternMixin", "ae2.ContainerPatternEncodingTermMenuMixin", "ae2.CraftingCPUClusterMixin", + "ae2.CraftingServiceGetProvidersMixin", "ae2.CraftingTreeProcessMixin", "ae2.MEStorageMenuMixin", + "ae2.NetworkCraftingProvidersGetMediumsMixin", + "ae2.NetworkCraftingProvidersProviderStateMixin", "ae2.PatternEncodingTermMenuMixin", "ae2.PatternProviderLogicAdvancedMixin", + "ae2.PatternProviderLogicContainsRedirectMixin", + "ae2.PatternProviderLogicMixin", + "ae2.PatternProviderLogicPushPatternMixin", "ae2.PatternProviderMenuAdvancedMixin", "ae2.accessor.CraftingCalculationAccessor", "ae2.accessor.CraftingTreeNodeAccessor", From ac522c7751f8ec6d2537de2044b465ff7c76f829 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Thu, 28 Aug 2025 15:23:06 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E4=BF=AE=E5=A4=8DScaledProcessingPattern?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E8=BF=87=E7=A8=8B=E4=B8=AD=E4=BA=A7=E5=87=BA?= =?UTF-8?q?=E6=AF=94=E7=9A=84=E8=AE=A1=E7=AE=97=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extendedae_plus/util/ArraySimplifier.java | 78 +++++++++++++++++ .../extendedae_plus/util/PatternScaler.java | 85 +++++++++++++------ 2 files changed, 138 insertions(+), 25 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/util/ArraySimplifier.java diff --git a/src/main/java/com/extendedae_plus/util/ArraySimplifier.java b/src/main/java/com/extendedae_plus/util/ArraySimplifier.java new file mode 100644 index 0000000..076eb31 --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/ArraySimplifier.java @@ -0,0 +1,78 @@ +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/java/com/extendedae_plus/util/PatternScaler.java b/src/main/java/com/extendedae_plus/util/PatternScaler.java index f57f620..13712e9 100644 --- a/src/main/java/com/extendedae_plus/util/PatternScaler.java +++ b/src/main/java/com/extendedae_plus/util/PatternScaler.java @@ -21,35 +21,56 @@ public final class PatternScaler { IInput[] baseInputs = base.getInputs(); GenericStack[] baseOutputs = base.getOutputs(); - /* 1. 构建缩放后的 sparseInputs */ - GenericStack[] scaledSparseInputs = new GenericStack[baseSparseInputs.length]; - for (int i = 0; i < baseSparseInputs.length; i++) { - GenericStack in = baseSparseInputs[i]; - if (in != null) { - scaledSparseInputs[i] = new GenericStack(in.what(), requestedAmount); - } + // 计算每个压缩输入槽位的总量(per operation): multiplier * template.amount + long[] inputsCounts = new long[baseInputs.length]; + for (int i = 0; i < baseInputs.length; i++) { + var in = baseInputs[i]; + var first = in.getPossibleInputs()[0]; + inputsCounts[i] = in.getMultiplier() * first.amount(); } - /* 2. 构建缩放后的 sparseOutputs */ - GenericStack[] scaledSparseOutputs = new GenericStack[baseSparseOutputs.length]; - for (int i = 0; i < baseSparseOutputs.length; i++) { - GenericStack out = baseSparseOutputs[i]; - if (out != null) { - scaledSparseOutputs[i] = new GenericStack(out.what(), requestedAmount); - } + // 计算每个输出的数量(per operation) + long[] outputsCounts = new long[baseOutputs.length]; + for (int i = 0; i < baseOutputs.length; i++) { + var out = baseOutputs[i]; + outputsCounts[i] = out == null ? 0L : out.amount(); } - /* 3. 构建压缩输入(ScaledInput) */ + // 合并为一个数组并计算 gcd(使用早期退出优化) + long[] combined = ArraySimplifier.combine(inputsCounts, outputsCounts); + long gcd = ArraySimplifier.findGCDWithEarlyExit(combined); + if (gcd <= 0) gcd = 1; + + // 如果 gcd == 1,则无需分配新的数组,直接使用 combined 作为 simplified 视图 + long[] simplified = ArraySimplifier.simplifyByGcd(combined, gcd); + + // 找到目标输出在 outputs 中的索引 + int 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 simplifiedTargetPerUnit = simplified[inputsCounts.length + Math.max(0, targetOutIndex)]; + if (simplifiedTargetPerUnit <= 0) simplifiedTargetPerUnit = 1; + + // 单位数:需要多少 "最简约单位" 才能满足 requestedAmount(向上取整) + long units = (requestedAmount + simplifiedTargetPerUnit - 1) / simplifiedTargetPerUnit; + + // 构建压缩输入(ScaledInput)——模板数量为 simplifiedInputs, multiplier 为 units 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]; + long simplifiedInputAmount = simplified[i]; for (int j = 0; j < template.length; j++) { - scaledTemplates[j] = new GenericStack(template[j].what(), 1); + scaledTemplates[j] = new GenericStack(template[j].what(), simplifiedInputAmount); } - scaledInputs[i] = new ScaledProcessingPattern.Input(scaledTemplates, requestedAmount); + scaledInputs[i] = new ScaledProcessingPattern.Input(scaledTemplates, units); } /* 4. 构建压缩输出 */ @@ -57,10 +78,30 @@ public final class PatternScaler { for (int i = 0; i < baseOutputs.length; i++) { GenericStack out = baseOutputs[i]; if (out != null) { - scaledCondensedOutputs[i] = new GenericStack(out.what(), requestedAmount); + long simplifiedOutAmount = simplified[inputsCounts.length + i]; + scaledCondensedOutputs[i] = new GenericStack(out.what(), simplifiedOutAmount * units); } } + // 构建并打印稀疏表示(按 unit * simplified / gcd 映射回原稀疏槽) + GenericStack[] scaledSparseInputs = new GenericStack[baseSparseInputs.length]; + for (int i = 0; i < baseSparseInputs.length; i++) { + var in = baseSparseInputs[i]; + if (in != null) { + long scaledAmount = in.amount() * units / gcd; + scaledSparseInputs[i] = new GenericStack(in.what(), scaledAmount); + } + } + GenericStack[] scaledSparseOutputs = new GenericStack[baseSparseOutputs.length]; + for (int i = 0; i < baseSparseOutputs.length; i++) { + var out = baseSparseOutputs[i]; + if (out != null) { + long scaledAmount = out.amount() * units / gcd; + scaledSparseOutputs[i] = new GenericStack(out.what(), scaledAmount); + } + } + + /* Debug 输出 */ System.out.println("[extendedae_plus] 正在缩放样板:"); System.out.println(" 原始样板: " + base); @@ -79,10 +120,4 @@ public final class PatternScaler { scaledInputs, scaledCondensedOutputs); } - - private static long safeMul(long a, long b) { - if (a == 0 || b == 0) return 0; - if (a > Long.MAX_VALUE / b) return Long.MAX_VALUE; - return a * b; - } } From 9c206089265f47dcf8699ac52b4d6a20820bb41a Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Fri, 29 Aug 2025 01:32:58 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E4=BF=AE=E6=94=B9autoPattern=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E7=BB=93=E6=9E=84=EF=BC=8C=E5=85=88=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E5=86=8D=E6=91=86=E6=94=BE=E8=87=B3=E6=AD=A3=E7=A1=AE=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mixin/ae2/CraftingTreeProcessMixin.java | 45 ------------------- .../CraftingCalculationAccessor.java | 2 +- .../CraftingServiceGetProvidersMixin.java | 2 +- .../CraftingTreeNodeAccessor.java | 2 +- .../autopattern/CraftingTreeProcessMixin.java | 34 ++++++++++++++ .../resources/extendedae_plus.mixins.json | 12 ++--- 6 files changed, 41 insertions(+), 56 deletions(-) delete mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/CraftingTreeProcessMixin.java rename src/main/java/com/extendedae_plus/mixin/{ae2/accessor => autopattern}/CraftingCalculationAccessor.java (88%) rename src/main/java/com/extendedae_plus/mixin/{ae2 => autopattern}/CraftingServiceGetProvidersMixin.java (96%) rename src/main/java/com/extendedae_plus/mixin/{ae2/accessor => autopattern}/CraftingTreeNodeAccessor.java (88%) create mode 100644 src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeProcessMixin.java diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/CraftingTreeProcessMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/CraftingTreeProcessMixin.java deleted file mode 100644 index 47c21fa..0000000 --- a/src/main/java/com/extendedae_plus/mixin/ae2/CraftingTreeProcessMixin.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.extendedae_plus.mixin.ae2; - -import appeng.api.crafting.IPatternDetails; -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 com.extendedae_plus.mixin.ae2.accessor.CraftingCalculationAccessor; -import com.extendedae_plus.mixin.ae2.accessor.CraftingTreeNodeAccessor; -import com.extendedae_plus.util.PatternScaler; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyVariable; - -/** - * 注入 CraftingTreeProcess 构造器尾部:将 AEProcessingPattern 替换为 ScaledProcessingPattern - * 以确保后续执行使用放大后的输入/输出视图。 - */ -@Mixin(CraftingTreeProcess.class) -public class CraftingTreeProcessMixin { - - @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) { - try { - if (!(details instanceof AEProcessingPattern proc)) return original; - - CraftingCalculationAccessor jobAcc = (CraftingCalculationAccessor) job; - long requested = jobAcc.extendedae_plus$getRequestedAmount(); - - CraftingTreeNodeAccessor parentAcc = (CraftingTreeNodeAccessor) craftingTreeNode; - AEKey parentTarget = parentAcc.extendedae_plus$getWhat(); - - System.out.println("[extendedae_plus] Replacing constructor details at HEAD for: " + parentTarget + " x " + requested); - - return PatternScaler.scale(proc, parentTarget, requested); - } catch (Exception e) { - System.err.println("[extendedae_plus] Error replacing pattern at HEAD: " + e.getMessage()); - e.printStackTrace(); - return original; - } - } -} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingCalculationAccessor.java b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingCalculationAccessor.java similarity index 88% rename from src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingCalculationAccessor.java rename to src/main/java/com/extendedae_plus/mixin/autopattern/CraftingCalculationAccessor.java index 859866b..96bb300 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingCalculationAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingCalculationAccessor.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.mixin.ae2.accessor; +package com.extendedae_plus.mixin.autopattern; import appeng.api.stacks.AEKey; import appeng.crafting.CraftingCalculation; diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/CraftingServiceGetProvidersMixin.java b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingServiceGetProvidersMixin.java similarity index 96% rename from src/main/java/com/extendedae_plus/mixin/ae2/CraftingServiceGetProvidersMixin.java rename to src/main/java/com/extendedae_plus/mixin/autopattern/CraftingServiceGetProvidersMixin.java index 2361c8a..fc6ea61 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/CraftingServiceGetProvidersMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingServiceGetProvidersMixin.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.mixin.ae2; +package com.extendedae_plus.mixin.autopattern; import appeng.api.crafting.IPatternDetails; import appeng.me.service.CraftingService; diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingTreeNodeAccessor.java b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeNodeAccessor.java similarity index 88% rename from src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingTreeNodeAccessor.java rename to src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeNodeAccessor.java index 39b7dbc..81d60e2 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingTreeNodeAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeNodeAccessor.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.mixin.ae2.accessor; +package com.extendedae_plus.mixin.autopattern; import appeng.api.stacks.AEKey; import appeng.crafting.CraftingTreeNode; diff --git a/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeProcessMixin.java b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeProcessMixin.java new file mode 100644 index 0000000..cada53a --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeProcessMixin.java @@ -0,0 +1,34 @@ +package com.extendedae_plus.mixin.autopattern; + +import appeng.crafting.CraftingTreeProcess; +import org.spongepowered.asm.mixin.Mixin; + +/** + * 注入 CraftingTreeProcess 构造器尾部:将 AEProcessingPattern 替换为 ScaledProcessingPattern + * 以确保后续执行使用放大后的输入/输出视图。 + */ +@Mixin(CraftingTreeProcess.class) +public class CraftingTreeProcessMixin { + +// @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) { +// try { +// if (!(details instanceof AEProcessingPattern proc)) return original; +// +// CraftingCalculationAccessor jobAcc = (CraftingCalculationAccessor) job; +// long requested = jobAcc.extendedae_plus$getRequestedAmount(); +// +// CraftingTreeNodeAccessor parentAcc = (CraftingTreeNodeAccessor) craftingTreeNode; +// AEKey parentTarget = parentAcc.extendedae_plus$getWhat(); +// +// System.out.println("[extendedae_plus] Replacing constructor details at HEAD for: " + parentTarget + " x " + requested); +// +// return PatternScaler.scale(proc, parentTarget, requested); +// } catch (Exception e) { +// System.err.println("[extendedae_plus] Error replacing pattern at HEAD: " + e.getMessage()); +// e.printStackTrace(); +// return original; +// } +// } +} diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 1418956..a5eb682 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -29,25 +29,21 @@ "mixins": [ "ae2.ContainerPatternEncodingTermMenuMixin", "ae2.CraftingCPUClusterMixin", - "ae2.CraftingServiceGetProvidersMixin", - "ae2.CraftingTreeProcessMixin", "ae2.MEStorageMenuMixin", - "ae2.NetworkCraftingProvidersGetMediumsMixin", - "ae2.NetworkCraftingProvidersProviderStateMixin", "ae2.PatternEncodingTermMenuMixin", "ae2.PatternProviderLogicAdvancedMixin", "ae2.PatternProviderLogicContainsRedirectMixin", - "ae2.PatternProviderLogicMixin", - "ae2.PatternProviderLogicPushPatternMixin", "ae2.PatternProviderMenuAdvancedMixin", - "ae2.accessor.CraftingCalculationAccessor", - "ae2.accessor.CraftingTreeNodeAccessor", "ae2.accessor.MEStorageMenuAccessor", "ae2.accessor.PatternEncodingTermMenuAccessor", "ae2.accessor.PatternProviderLogicAccessor", "ae2.accessor.PatternProviderLogicPatternInputsAccessor", "ae2.accessor.PatternProviderMenuAdvancedAccessor", "ae2WTlib.ContainerUWirelessExPatternTerminalMixin", + "autopattern.CraftingCalculationAccessor", + "autopattern.CraftingServiceGetProvidersMixin", + "autopattern.CraftingTreeNodeAccessor", + "autopattern.CraftingTreeProcessMixin", "extendedae.ContainerExPatternProviderMixin", "extendedae.ContainerExPatternTerminalMixin", "extendedae.ContainerWirelessExPatternTerminalMixin", From 692ffb7396f1c6053d1cc158de346993fe4b8dba Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Fri, 29 Aug 2025 13:35:11 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E4=BF=AE=E5=A4=8DScaledProcessingPattern?= =?UTF-8?q?=E6=9C=AA=E6=8C=89=E7=85=A7=E5=AE=9E=E9=99=85=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E9=87=8F=E7=BC=A9=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- gradle.properties | 2 +- .../CraftingServiceGetProvidersMixin.java | 1 - .../autopattern/CraftingTreeNodeMixin.java | 30 ++++++++ .../autopattern/CraftingTreeProcessMixin.java | 62 ++++++++++------ ...ernProviderLogicContainsRedirectMixin.java | 3 +- .../extendedae_plus/util/PatternScaler.java | 17 ++--- .../util/RequestedAmountHolder.java | 71 +++++++++++++++++++ .../resources/extendedae_plus.mixins.json | 3 +- 9 files changed, 155 insertions(+), 36 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeNodeMixin.java rename src/main/java/com/extendedae_plus/mixin/{ae2 => autopattern}/PatternProviderLogicContainsRedirectMixin.java (89%) create mode 100644 src/main/java/com/extendedae_plus/util/RequestedAmountHolder.java diff --git a/build.gradle b/build.gradle index d31f0f1..a3e41cd 100644 --- a/build.gradle +++ b/build.gradle @@ -103,7 +103,7 @@ dependencies { modCompileOnly "curse.maven:just-enough-characters-250702:6680042" //mae2 - modRuntimeOnly "curse.maven:modern-ae2-additions-1028068:6342203" +// modRuntimeOnly "curse.maven:modern-ae2-additions-1028068:6342203" modCompileOnly "curse.maven:modern-ae2-additions-1028068:6342203" } diff --git a/gradle.properties b/gradle.properties index d28f3d4..b3b0a9f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G loom.platform = forge # Mod properties -mod_version = 1.3.3 +mod_version = 1.3.4-beta maven_group = com.extendedae_plus archives_name = extendedae_plus 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 fc6ea61..5604bbb 100644 --- a/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingServiceGetProvidersMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingServiceGetProvidersMixin.java @@ -20,7 +20,6 @@ public class CraftingServiceGetProvidersMixin { IPatternDetails base = null; if (original instanceof ScaledProcessingPattern scaledProcessingPattern) { base = scaledProcessingPattern.getOriginal(); - System.out.println("[extendedae_plus] CraftingService.getProviders mixin invoked for: " + base); } return base == null ? original : base; } diff --git a/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeNodeMixin.java b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeNodeMixin.java new file mode 100644 index 0000000..d792687 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeNodeMixin.java @@ -0,0 +1,30 @@ +package com.extendedae_plus.mixin.autopattern; + +import appeng.api.stacks.KeyCounter; +import appeng.crafting.CraftingTreeNode; +import appeng.crafting.inv.CraftingSimulationState; +import com.extendedae_plus.util.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); + } + + @Inject(method = "request(Lappeng/crafting/inv/CraftingSimulationState;JLappeng/api/stacks/KeyCounter;)V", + at = @At(value = "RETURN")) + private void clearRequestedAmountOnReturn(CraftingSimulationState inv, long requestedAmount, KeyCounter containerItems, CallbackInfo ci) { + // pop the pushed requested amount on return + RequestedAmountHolder.pop(); + } +} 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 cada53a..5a1e0aa 100644 --- a/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeProcessMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeProcessMixin.java @@ -1,34 +1,52 @@ package com.extendedae_plus.mixin.autopattern; +import appeng.api.crafting.IPatternDetails; +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 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 static com.extendedae_plus.util.ExtendedAELogger.LOGGER; /** * 注入 CraftingTreeProcess 构造器尾部:将 AEProcessingPattern 替换为 ScaledProcessingPattern * 以确保后续执行使用放大后的输入/输出视图。 */ @Mixin(CraftingTreeProcess.class) -public class CraftingTreeProcessMixin { +public abstract class CraftingTreeProcessMixin { -// @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) { -// try { -// if (!(details instanceof AEProcessingPattern proc)) return original; -// -// CraftingCalculationAccessor jobAcc = (CraftingCalculationAccessor) job; -// long requested = jobAcc.extendedae_plus$getRequestedAmount(); -// -// CraftingTreeNodeAccessor parentAcc = (CraftingTreeNodeAccessor) craftingTreeNode; -// AEKey parentTarget = parentAcc.extendedae_plus$getWhat(); -// -// System.out.println("[extendedae_plus] Replacing constructor details at HEAD for: " + parentTarget + " x " + requested); -// -// return PatternScaler.scale(proc, parentTarget, requested); -// } catch (Exception e) { -// System.err.println("[extendedae_plus] Error replacing pattern at HEAD: " + e.getMessage()); -// e.printStackTrace(); -// return original; -// } -// } + @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) { + try { + if (!(details instanceof AEProcessingPattern proc)) return original; + + CraftingTreeNodeAccessor parentAcc = (CraftingTreeNodeAccessor) craftingTreeNode; + AEKey parentTarget = parentAcc.extendedae_plus$getWhat(); + long requested = RequestedAmountHolder.get(); + // 记录所使用的请求数量和当前线程堆栈快照,便于诊断 PatternScaler.scale 使用了哪个值以及何时清理 + LOGGER.info("[extendedae_plus] 在 CraftingTreeProcess 构造中使用请求数量: {} ; stackDepth={} ; stackSnapshot={}", requested, RequestedAmountHolder.depth(), RequestedAmountHolder.snapshot()); + // 使用当前线程栈顶的值进行缩放,不在此处清理;构造完成后应该由调用方的 pop 恢复状态 + return PatternScaler.scale(proc, parentTarget, requested); + } catch (Exception e) { + LOGGER.warn("构建倍增样板出错", e); + e.printStackTrace(); + return original; + } + } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderLogicContainsRedirectMixin.java b/src/main/java/com/extendedae_plus/mixin/autopattern/PatternProviderLogicContainsRedirectMixin.java similarity index 89% rename from src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderLogicContainsRedirectMixin.java rename to src/main/java/com/extendedae_plus/mixin/autopattern/PatternProviderLogicContainsRedirectMixin.java index ec589d9..2bb8107 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderLogicContainsRedirectMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/autopattern/PatternProviderLogicContainsRedirectMixin.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.mixin.ae2; +package com.extendedae_plus.mixin.autopattern; import appeng.api.crafting.IPatternDetails; import appeng.helpers.patternprovider.PatternProviderLogic; @@ -26,7 +26,6 @@ public class PatternProviderLogicContainsRedirectMixin { if (o instanceof ScaledProcessingPattern scaled) { IPatternDetails base = scaled.getOriginal(); if (base != null && list.indexOf(base) != -1) { - System.out.println("[extendedae_plus] contains-redirect: matched base pattern for scaled pattern"); return true; } } diff --git a/src/main/java/com/extendedae_plus/util/PatternScaler.java b/src/main/java/com/extendedae_plus/util/PatternScaler.java index 13712e9..46abac6 100644 --- a/src/main/java/com/extendedae_plus/util/PatternScaler.java +++ b/src/main/java/com/extendedae_plus/util/PatternScaler.java @@ -8,6 +8,8 @@ import com.extendedae_plus.content.ScaledProcessingPattern; import java.util.Arrays; +import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; + public final class PatternScaler { private PatternScaler() { } @@ -103,14 +105,13 @@ public final class PatternScaler { /* Debug 输出 */ - System.out.println("[extendedae_plus] 正在缩放样板:"); - System.out.println(" 原始样板: " + base); - System.out.println(" 目标物品: " + target); - System.out.println(" 请求数量: " + requestedAmount); - System.out.println(" 缩放后输入: " + Arrays.toString(scaledInputs)); - System.out.println(" 缩放后输出: " + Arrays.toString(scaledCondensedOutputs)); - System.out.println(" 缩放后稀疏输入: " + Arrays.toString(scaledSparseInputs)); - System.out.println(" 缩放后稀疏输出: " + Arrays.toString(scaledSparseOutputs)); + LOGGER.info("[extendedae_plus] 正在缩放样板: 目标物品: {} 请求数量: {} 缩放后输入: {} 缩放后输出: {} 缩放后稀疏输入: {} 缩放后稀疏输出: {}", + target, + requestedAmount, + Arrays.toString(scaledInputs), + Arrays.toString(scaledCondensedOutputs), + Arrays.toString(scaledSparseInputs), + Arrays.toString(scaledSparseOutputs)); return new ScaledProcessingPattern(base, diff --git a/src/main/java/com/extendedae_plus/util/RequestedAmountHolder.java b/src/main/java/com/extendedae_plus/util/RequestedAmountHolder.java new file mode 100644 index 0000000..88336dd --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/RequestedAmountHolder.java @@ -0,0 +1,71 @@ +package com.extendedae_plus.util; + +import java.util.ArrayDeque; +import java.util.Deque; +import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; + +/** + * Thread-local stack holder for requested amounts to support nested requests. + */ +public final class RequestedAmountHolder { + private static final ThreadLocal> HOLDER = ThreadLocal.withInitial(ArrayDeque::new); + + private RequestedAmountHolder() { + } + + /** + * Push a requested amount onto the thread-local stack. + */ + public static void push(long v) { + Deque dq = HOLDER.get(); + dq.push(v); + LOGGER.info("[extendedae_plus] 请求数量已推入堆栈: {} ; depth={}", v, dq.size()); + } + + /** + * Pop the top value from the thread-local stack. Safe if empty. + */ + public static void pop() { + Deque dq = HOLDER.get(); + if (dq.isEmpty()) { + LOGGER.info("[extendedae_plus] 请求数量堆栈为空,无法弹出"); + return; + } + Long popped = dq.pop(); + LOGGER.info("[extendedae_plus] 请求数量已弹出堆栈: {} ; depth={}", popped, dq.size()); + } + + /** + * Peek the current requested amount or return 0 if none. + */ + public static long get() { + Deque dq = HOLDER.get(); + Long v = dq.peek(); + LOGGER.info("[extendedae_plus] 当前请求数量: {} ; depth={}", v, dq.size()); + return v == null ? 0L : v; + } + + /** + * Clear the entire stack for this thread. + */ + public static void clearAll() { + HOLDER.get().clear(); + LOGGER.info("[extendedae_plus] 请求数量堆栈已清空"); + } + + /** + * 返回当前线程堆栈深度(仅供日志/诊断使用)。 + */ + public static int depth() { + return HOLDER.get().size(); + } + + /** + * 返回当前线程堆栈的字符串表示(仅供日志/诊断使用)。 + */ + public static String snapshot() { + return HOLDER.get().toString(); + } +} + + diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index a5eb682..52f97e1 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -32,7 +32,6 @@ "ae2.MEStorageMenuMixin", "ae2.PatternEncodingTermMenuMixin", "ae2.PatternProviderLogicAdvancedMixin", - "ae2.PatternProviderLogicContainsRedirectMixin", "ae2.PatternProviderMenuAdvancedMixin", "ae2.accessor.MEStorageMenuAccessor", "ae2.accessor.PatternEncodingTermMenuAccessor", @@ -43,7 +42,9 @@ "autopattern.CraftingCalculationAccessor", "autopattern.CraftingServiceGetProvidersMixin", "autopattern.CraftingTreeNodeAccessor", + "autopattern.CraftingTreeNodeMixin", "autopattern.CraftingTreeProcessMixin", + "autopattern.PatternProviderLogicContainsRedirectMixin", "extendedae.ContainerExPatternProviderMixin", "extendedae.ContainerExPatternTerminalMixin", "extendedae.ContainerWirelessExPatternTerminalMixin", From 49574cb630826c3d231b9107cebbd46a504eb665 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Fri, 29 Aug 2025 14:37:47 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E4=BF=AE=E6=94=B9ScaledProcessingPattern?= =?UTF-8?q?=E5=80=8D=E4=B9=98=E7=AD=96=E7=95=A5=E4=B8=BA=E6=9C=80=E5=B0=8F?= =?UTF-8?q?=E6=95=B4=E6=95=B0=E5=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../autopattern/CraftingTreeProcessMixin.java | 2 - .../extendedae_plus/util/PatternScaler.java | 70 ++++++++----------- .../util/RequestedAmountHolder.java | 29 +------- 3 files changed, 31 insertions(+), 70 deletions(-) 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 5a1e0aa..484b8d8 100644 --- a/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeProcessMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/autopattern/CraftingTreeProcessMixin.java @@ -39,8 +39,6 @@ public abstract class CraftingTreeProcessMixin { CraftingTreeNodeAccessor parentAcc = (CraftingTreeNodeAccessor) craftingTreeNode; AEKey parentTarget = parentAcc.extendedae_plus$getWhat(); long requested = RequestedAmountHolder.get(); - // 记录所使用的请求数量和当前线程堆栈快照,便于诊断 PatternScaler.scale 使用了哪个值以及何时清理 - LOGGER.info("[extendedae_plus] 在 CraftingTreeProcess 构造中使用请求数量: {} ; stackDepth={} ; stackSnapshot={}", requested, RequestedAmountHolder.depth(), RequestedAmountHolder.snapshot()); // 使用当前线程栈顶的值进行缩放,不在此处清理;构造完成后应该由调用方的 pop 恢复状态 return PatternScaler.scale(proc, parentTarget, requested); } catch (Exception e) { diff --git a/src/main/java/com/extendedae_plus/util/PatternScaler.java b/src/main/java/com/extendedae_plus/util/PatternScaler.java index 46abac6..dc15972 100644 --- a/src/main/java/com/extendedae_plus/util/PatternScaler.java +++ b/src/main/java/com/extendedae_plus/util/PatternScaler.java @@ -23,56 +23,49 @@ public final class PatternScaler { IInput[] baseInputs = base.getInputs(); GenericStack[] baseOutputs = base.getOutputs(); - // 计算每个压缩输入槽位的总量(per operation): multiplier * template.amount - long[] inputsCounts = new long[baseInputs.length]; - for (int i = 0; i < baseInputs.length; i++) { - var in = baseInputs[i]; - var first = in.getPossibleInputs()[0]; - inputsCounts[i] = in.getMultiplier() * first.amount(); - } - - // 计算每个输出的数量(per operation) - long[] outputsCounts = new long[baseOutputs.length]; - for (int i = 0; i < baseOutputs.length; i++) { - var out = baseOutputs[i]; - outputsCounts[i] = out == null ? 0L : out.amount(); - } - - // 合并为一个数组并计算 gcd(使用早期退出优化) - long[] combined = ArraySimplifier.combine(inputsCounts, outputsCounts); - long gcd = ArraySimplifier.findGCDWithEarlyExit(combined); - if (gcd <= 0) gcd = 1; - - // 如果 gcd == 1,则无需分配新的数组,直接使用 combined 作为 simplified 视图 - long[] simplified = ArraySimplifier.simplifyByGcd(combined, gcd); - - // 找到目标输出在 outputs 中的索引 + // 新逻辑:不再对样板进行单位化处理 + // 找到目标输出在 outputs 中的索引(尝试匹配 target,否则取第一个非空输出) int targetOutIndex = -1; for (int i = 0; i < baseOutputs.length; i++) { - if (baseOutputs[i] != null) { + 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 simplifiedTargetPerUnit = simplified[inputsCounts.length + Math.max(0, targetOutIndex)]; - if (simplifiedTargetPerUnit <= 0) simplifiedTargetPerUnit = 1; + long perOperationTarget = 1L; + if (targetOutIndex >= 0 && baseOutputs[targetOutIndex] != null) { + long amt = baseOutputs[targetOutIndex].amount(); + if (amt > 0) perOperationTarget = amt; + } - // 单位数:需要多少 "最简约单位" 才能满足 requestedAmount(向上取整) - long units = (requestedAmount + simplifiedTargetPerUnit - 1) / simplifiedTargetPerUnit; + // 使用最小整数倍(ceil)策略:直接选择满足请求的最小倍数 + long multiplier = 1L; + if (requestedAmount > 0) { + long needed = requestedAmount / perOperationTarget + ((requestedAmount % perOperationTarget) == 0 ? 0 : 1); + multiplier = needed <= 1L ? 1L : needed; + } - // 构建压缩输入(ScaledInput)——模板数量为 simplifiedInputs, multiplier 为 units + // 构建压缩输入(将每个输入的 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]; - long simplifiedInputAmount = simplified[i]; for (int j = 0; j < template.length; j++) { - scaledTemplates[j] = new GenericStack(template[j].what(), simplifiedInputAmount); + scaledTemplates[j] = new GenericStack(template[j].what(), template[j].amount()); } - scaledInputs[i] = new ScaledProcessingPattern.Input(scaledTemplates, units); + scaledInputs[i] = new ScaledProcessingPattern.Input(scaledTemplates, in.getMultiplier() * multiplier); } /* 4. 构建压缩输出 */ @@ -80,26 +73,23 @@ public final class PatternScaler { for (int i = 0; i < baseOutputs.length; i++) { GenericStack out = baseOutputs[i]; if (out != null) { - long simplifiedOutAmount = simplified[inputsCounts.length + i]; - scaledCondensedOutputs[i] = new GenericStack(out.what(), simplifiedOutAmount * units); + scaledCondensedOutputs[i] = new GenericStack(out.what(), out.amount() * multiplier); } } - // 构建并打印稀疏表示(按 unit * simplified / gcd 映射回原稀疏槽) + // 构建并打印稀疏表示(直接按 multiplier 放大) GenericStack[] scaledSparseInputs = new GenericStack[baseSparseInputs.length]; for (int i = 0; i < baseSparseInputs.length; i++) { var in = baseSparseInputs[i]; if (in != null) { - long scaledAmount = in.amount() * units / gcd; - scaledSparseInputs[i] = new GenericStack(in.what(), scaledAmount); + 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) { - long scaledAmount = out.amount() * units / gcd; - scaledSparseOutputs[i] = new GenericStack(out.what(), scaledAmount); + scaledSparseOutputs[i] = new GenericStack(out.what(), out.amount() * multiplier); } } diff --git a/src/main/java/com/extendedae_plus/util/RequestedAmountHolder.java b/src/main/java/com/extendedae_plus/util/RequestedAmountHolder.java index 88336dd..e70187a 100644 --- a/src/main/java/com/extendedae_plus/util/RequestedAmountHolder.java +++ b/src/main/java/com/extendedae_plus/util/RequestedAmountHolder.java @@ -2,7 +2,6 @@ package com.extendedae_plus.util; import java.util.ArrayDeque; import java.util.Deque; -import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; /** * Thread-local stack holder for requested amounts to support nested requests. @@ -19,7 +18,6 @@ public final class RequestedAmountHolder { public static void push(long v) { Deque dq = HOLDER.get(); dq.push(v); - LOGGER.info("[extendedae_plus] 请求数量已推入堆栈: {} ; depth={}", v, dq.size()); } /** @@ -28,11 +26,9 @@ public final class RequestedAmountHolder { public static void pop() { Deque dq = HOLDER.get(); if (dq.isEmpty()) { - LOGGER.info("[extendedae_plus] 请求数量堆栈为空,无法弹出"); return; } - Long popped = dq.pop(); - LOGGER.info("[extendedae_plus] 请求数量已弹出堆栈: {} ; depth={}", popped, dq.size()); + dq.pop(); } /** @@ -41,31 +37,8 @@ public final class RequestedAmountHolder { public static long get() { Deque dq = HOLDER.get(); Long v = dq.peek(); - LOGGER.info("[extendedae_plus] 当前请求数量: {} ; depth={}", v, dq.size()); return v == null ? 0L : v; } - - /** - * Clear the entire stack for this thread. - */ - public static void clearAll() { - HOLDER.get().clear(); - LOGGER.info("[extendedae_plus] 请求数量堆栈已清空"); - } - - /** - * 返回当前线程堆栈深度(仅供日志/诊断使用)。 - */ - public static int depth() { - return HOLDER.get().size(); - } - - /** - * 返回当前线程堆栈的字符串表示(仅供日志/诊断使用)。 - */ - public static String snapshot() { - return HOLDER.get().toString(); - } }