Merge pull request #11 from C-H716/feature/autoPattern
Feature/autoPattern
This commit is contained in:
commit
38eb1788cf
|
|
@ -102,6 +102,10 @@ dependencies {
|
|||
//jec
|
||||
modCompileOnly "curse.maven:just-enough-characters-250702:6680042"
|
||||
|
||||
|
||||
//mae2
|
||||
// modRuntimeOnly "curse.maven:modern-ae2-additions-1028068:6342203"
|
||||
modCompileOnly "curse.maven:modern-ae2-additions-1028068:6342203"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
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();
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.extendedae_plus.mixin.autopattern;
|
||||
|
||||
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();
|
||||
}
|
||||
return base == null ? original : base;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.extendedae_plus.mixin.autopattern;
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
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 abstract class CraftingTreeProcessMixin {
|
||||
|
||||
@Shadow abstract void request(CraftingSimulationState inv, long times) throws CraftBranchFailure, InterruptedException;
|
||||
|
||||
@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;
|
||||
|
||||
CraftingTreeNodeAccessor parentAcc = (CraftingTreeNodeAccessor) craftingTreeNode;
|
||||
AEKey parentTarget = parentAcc.extendedae_plus$getWhat();
|
||||
long requested = RequestedAmountHolder.get();
|
||||
// 使用当前线程栈顶的值进行缩放,不在此处清理;构造完成后应该由调用方的 pop 恢复状态
|
||||
return PatternScaler.scale(proc, parentTarget, requested);
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("构建倍增样板出错", e);
|
||||
e.printStackTrace();
|
||||
return original;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.extendedae_plus.mixin.autopattern;
|
||||
|
||||
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) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 使用 indexOf 避免再次触发对 List.contains 的 redirect(防止递归)
|
||||
return list.indexOf(o) != -1;
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
return list.indexOf(o) != -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
src/main/java/com/extendedae_plus/util/ArraySimplifier.java
Normal file
78
src/main/java/com/extendedae_plus/util/ArraySimplifier.java
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
114
src/main/java/com/extendedae_plus/util/PatternScaler.java
Normal file
114
src/main/java/com/extendedae_plus/util/PatternScaler.java
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
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;
|
||||
|
||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
||||
|
||||
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();
|
||||
|
||||
// 新逻辑:不再对样板进行单位化处理
|
||||
// 找到目标输出在 outputs 中的索引(尝试匹配 target,否则取第一个非空输出)
|
||||
int targetOutIndex = -1;
|
||||
for (int i = 0; i < baseOutputs.length; i++) {
|
||||
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 perOperationTarget = 1L;
|
||||
if (targetOutIndex >= 0 && baseOutputs[targetOutIndex] != null) {
|
||||
long amt = baseOutputs[targetOutIndex].amount();
|
||||
if (amt > 0) perOperationTarget = amt;
|
||||
}
|
||||
|
||||
// 使用最小整数倍(ceil)策略:直接选择满足请求的最小倍数
|
||||
long multiplier = 1L;
|
||||
if (requestedAmount > 0) {
|
||||
long needed = requestedAmount / perOperationTarget + ((requestedAmount % perOperationTarget) == 0 ? 0 : 1);
|
||||
multiplier = needed <= 1L ? 1L : needed;
|
||||
}
|
||||
|
||||
// 构建压缩输入(将每个输入的 multiplier 翻倍,保留每个模板的原始数量)
|
||||
IInput[] scaledInputs = new IInput[baseInputs.length];
|
||||
for (int i = 0; i < baseInputs.length; i++) {
|
||||
var in = baseInputs[i];
|
||||
var template = in.getPossibleInputs();
|
||||
GenericStack[] scaledTemplates = new GenericStack[template.length];
|
||||
for (int j = 0; j < template.length; j++) {
|
||||
scaledTemplates[j] = new GenericStack(template[j].what(), template[j].amount());
|
||||
}
|
||||
scaledInputs[i] = new ScaledProcessingPattern.Input(scaledTemplates, in.getMultiplier() * multiplier);
|
||||
}
|
||||
|
||||
/* 4. 构建压缩输出 */
|
||||
GenericStack[] scaledCondensedOutputs = new GenericStack[baseOutputs.length];
|
||||
for (int i = 0; i < baseOutputs.length; i++) {
|
||||
GenericStack out = baseOutputs[i];
|
||||
if (out != null) {
|
||||
scaledCondensedOutputs[i] = new GenericStack(out.what(), out.amount() * multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
// 构建并打印稀疏表示(直接按 multiplier 放大)
|
||||
GenericStack[] scaledSparseInputs = new GenericStack[baseSparseInputs.length];
|
||||
for (int i = 0; i < baseSparseInputs.length; i++) {
|
||||
var in = baseSparseInputs[i];
|
||||
if (in != null) {
|
||||
scaledSparseInputs[i] = new GenericStack(in.what(), in.amount() * multiplier);
|
||||
}
|
||||
}
|
||||
GenericStack[] scaledSparseOutputs = new GenericStack[baseSparseOutputs.length];
|
||||
for (int i = 0; i < baseSparseOutputs.length; i++) {
|
||||
var out = baseSparseOutputs[i];
|
||||
if (out != null) {
|
||||
scaledSparseOutputs[i] = new GenericStack(out.what(), out.amount() * multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Debug 输出 */
|
||||
LOGGER.info("[extendedae_plus] 正在缩放样板: 目标物品: {} 请求数量: {} 缩放后输入: {} 缩放后输出: {} 缩放后稀疏输入: {} 缩放后稀疏输出: {}",
|
||||
target,
|
||||
requestedAmount,
|
||||
Arrays.toString(scaledInputs),
|
||||
Arrays.toString(scaledCondensedOutputs),
|
||||
Arrays.toString(scaledSparseInputs),
|
||||
Arrays.toString(scaledSparseOutputs));
|
||||
|
||||
|
||||
return new ScaledProcessingPattern(base,
|
||||
base.getDefinition(),
|
||||
scaledSparseInputs,
|
||||
scaledSparseOutputs,
|
||||
scaledInputs,
|
||||
scaledCondensedOutputs);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package com.extendedae_plus.util;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
/**
|
||||
* Thread-local stack holder for requested amounts to support nested requests.
|
||||
*/
|
||||
public final class RequestedAmountHolder {
|
||||
private static final ThreadLocal<Deque<Long>> HOLDER = ThreadLocal.withInitial(ArrayDeque::new);
|
||||
|
||||
private RequestedAmountHolder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a requested amount onto the thread-local stack.
|
||||
*/
|
||||
public static void push(long v) {
|
||||
Deque<Long> dq = HOLDER.get();
|
||||
dq.push(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop the top value from the thread-local stack. Safe if empty.
|
||||
*/
|
||||
public static void pop() {
|
||||
Deque<Long> dq = HOLDER.get();
|
||||
if (dq.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
dq.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Peek the current requested amount or return 0 if none.
|
||||
*/
|
||||
public static long get() {
|
||||
Deque<Long> dq = HOLDER.get();
|
||||
Long v = dq.peek();
|
||||
return v == null ? 0L : v;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -22,11 +22,12 @@
|
|||
"extendedae.HighlightButtonMixin",
|
||||
"extendedae.accessor.GuiExPatternTerminalAccessor",
|
||||
"extendedae.accessor.GuiExPatternTerminalSlotsRowAccessor",
|
||||
"jei.EncodePatternTransferHandlerMixin",
|
||||
"hooks.ModelBakeryMixin"
|
||||
"hooks.ModelBakeryMixin",
|
||||
"jei.EncodePatternTransferHandlerMixin"
|
||||
],
|
||||
"mixins": [
|
||||
"ae2.ContainerPatternEncodingTermMenuMixin",
|
||||
"ae2.CraftingCPUClusterMixin",
|
||||
"ae2.MEStorageMenuMixin",
|
||||
"ae2.PatternEncodingTermMenuMixin",
|
||||
"ae2.PatternProviderLogicAdvancedMixin",
|
||||
|
|
@ -37,10 +38,15 @@
|
|||
"ae2.accessor.PatternProviderLogicPatternInputsAccessor",
|
||||
"ae2.accessor.PatternProviderMenuAdvancedAccessor",
|
||||
"ae2WTlib.ContainerUWirelessExPatternTerminalMixin",
|
||||
"autopattern.CraftingCalculationAccessor",
|
||||
"autopattern.CraftingServiceGetProvidersMixin",
|
||||
"autopattern.CraftingTreeNodeAccessor",
|
||||
"autopattern.CraftingTreeNodeMixin",
|
||||
"autopattern.CraftingTreeProcessMixin",
|
||||
"autopattern.PatternProviderLogicContainsRedirectMixin",
|
||||
"extendedae.ContainerExPatternProviderMixin",
|
||||
"extendedae.ContainerExPatternTerminalMixin",
|
||||
"extendedae.ContainerWirelessExPatternTerminalMixin",
|
||||
"ae2.CraftingCPUClusterMixin",
|
||||
"extendedae.PartExPatternProviderMixin",
|
||||
"extendedae.TileExPatternProviderMixin"
|
||||
],
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user