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] =?UTF-8?q?=E5=9C=A8=E6=9E=84=E9=80=A0=E5=90=88=E6=88=90?= =?UTF-8?q?=E6=A0=91=E5=89=8D=EF=BC=8C=E5=AF=B9=E6=A0=B7=E6=9D=BF=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E8=AE=BE=E7=BD=AE=EF=BC=8C=E4=BD=BF=E7=94=A8ScaledPro?= =?UTF-8?q?cessingPattern=E4=BB=A3=E6=9B=BFAEProcessingPattern=EF=BC=8C?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=A0=B7=E6=9D=BF=E7=9A=84=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=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" ],