在构造合成树前,对样板进行设置,使用ScaledProcessingPattern代替AEProcessingPattern,实现样板的自动倍增

This commit is contained in:
C-H716 2025-08-27 21:44:06 +08:00
parent 83c52fc659
commit 6924ff968a
6 changed files with 307 additions and 3 deletions

View File

@ -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;
}
}
}

View File

@ -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 = "<init>(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;
}
}
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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"
],