diff --git a/src/main/java/com/extendedae_plus/ae/api/crafting/ScaledProcessingPattern.java b/src/main/java/com/extendedae_plus/ae/api/crafting/ScaledProcessingPattern.java index c402127..979cebc 100644 --- a/src/main/java/com/extendedae_plus/ae/api/crafting/ScaledProcessingPattern.java +++ b/src/main/java/com/extendedae_plus/ae/api/crafting/ScaledProcessingPattern.java @@ -4,200 +4,107 @@ 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 class ScaledProcessingPattern implements IPatternDetails { - // 最小化实例字段:只保留原始样板引用、定义和倍数 - private final AEProcessingPattern original; // 原始样板引用 - private final AEItemKey definition; // 样板物品(直接委托自 original) - private final long multiplier; // 乘数(外部可视为视图参数) + private final AEProcessingPattern original; + private final long multiplier; - // 延迟计算缓存(轻量化实例时避免在构造器中分配大数组) - private transient volatile IInput[] inputsCache; - private transient volatile GenericStack[] outputsCache; - private transient volatile GenericStack[] sparseInputsCache; - private transient volatile GenericStack[] sparseOutputsCache; - - public ScaledProcessingPattern(AEProcessingPattern original, AEItemKey definition, long multiplier) { - this.original = Objects.requireNonNull(original); - this.definition = Objects.requireNonNull(definition); - this.multiplier = multiplier <= 0 ? 1L : multiplier; + public ScaledProcessingPattern(AEProcessingPattern original, long multiplier) { + if (original == null) throw new IllegalArgumentException("original cannot be null"); + if (multiplier <= 0) throw new IllegalArgumentException("multiplier must be > 0"); + this.original = original; + this.multiplier = multiplier; } - /* -------------------- API 实现 -------------------- */ - - public AEProcessingPattern getOriginal() { - return original; - } + public AEProcessingPattern getOriginal() { return original; } + public long getMultiplier() { return multiplier; } @Override public AEItemKey getDefinition() { - return definition; + return original.getDefinition(); } @Override public IInput[] getInputs() { - IInput[] cached = this.inputsCache; - if (cached == null) { - synchronized (this) { - cached = this.inputsCache; - if (cached == null) { - var base = original.getInputs(); - IInput[] arr = new IInput[base.length]; - for (int i = 0; i < base.length; i++) { - var in = base[i]; - // 不复制 template 数组,直接复用原始的 possible inputs;仅放大 multiplier - arr[i] = new Input(in.getPossibleInputs(), in.getMultiplier() * this.multiplier); - } - this.inputsCache = arr; - cached = arr; - } - } + IPatternDetails.IInput[] orig = original.getInputs(); + IInput[] scaled = new IInput[orig.length]; + for (int i = 0; i < orig.length; i++) { + scaled[i] = new ScaledInput(orig[i], multiplier); } - return cached; + return scaled; } @Override public GenericStack[] getOutputs() { - GenericStack[] cached = this.outputsCache; - if (cached == null) { - synchronized (this) { - cached = this.outputsCache; - if (cached == null) { - var baseOutputs = original.getOutputs(); - GenericStack[] arr = new GenericStack[baseOutputs.length]; - for (int i = 0; i < baseOutputs.length; i++) { - var o = baseOutputs[i]; - if (o != null) arr[i] = new GenericStack(o.what(), o.amount() * this.multiplier); - } - this.outputsCache = arr; - cached = arr; - } + GenericStack[] orig = original.getOutputs(); + GenericStack[] scaled = new GenericStack[orig.length]; + for (int i = 0; i < orig.length; i++) { + if (orig[i] != null) { + scaled[i] = new GenericStack(orig[i].what(), orig[i].amount() * multiplier); } } - return cached; + return scaled; } + // 兼容性方法 public GenericStack[] getSparseInputs() { - GenericStack[] cached = this.sparseInputsCache; - if (cached == null) { - synchronized (this) { - cached = this.sparseInputsCache; - if (cached == null) { - var base = original.getSparseInputs(); - GenericStack[] arr = new GenericStack[base.length]; - for (int i = 0; i < base.length; i++) { - var v = base[i]; - if (v != null) arr[i] = new GenericStack(v.what(), v.amount() * this.multiplier); - } - this.sparseInputsCache = arr; - cached = arr; - } + GenericStack[] orig = original.getSparseInputs(); + GenericStack[] scaled = new GenericStack[orig.length]; + for (int i = 0; i < orig.length; i++) { + if (orig[i] != null) { + scaled[i] = new GenericStack(orig[i].what(), orig[i].amount() * multiplier); } } - return cached; + return scaled; } public GenericStack[] getSparseOutputs() { - GenericStack[] cached = this.sparseOutputsCache; - if (cached == null) { - synchronized (this) { - cached = this.sparseOutputsCache; - if (cached == null) { - var base = original.getSparseOutputs(); - GenericStack[] arr = new GenericStack[base.length]; - for (int i = 0; i < base.length; i++) { - var v = base[i]; - if (v != null) arr[i] = new GenericStack(v.what(), v.amount() * this.multiplier); - } - this.sparseOutputsCache = arr; - cached = arr; - } + GenericStack[] orig = original.getSparseOutputs(); + GenericStack[] scaled = new GenericStack[orig.length]; + for (int i = 0; i < orig.length; i++) { + if (orig[i] != null) { + scaled[i] = new GenericStack(orig[i].what(), orig[i].amount() * multiplier); } } - return cached; + return scaled; + } + + // equals / hashCode 必须包含 multiplier!不同倍率 = 不同 key + @Override + public int hashCode() { + int h = original.hashCode(); + h = 31 * h + Long.hashCode(multiplier); + return h; } @Override - public GenericStack getPrimaryOutput() { - var outs = getOutputs(); - if (outs.length > 0 && outs[0] != null) return outs[0]; - return original.getPrimaryOutput(); + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof ScaledProcessingPattern sp)) return false; + return sp.original.equals(this.original) && sp.multiplier == this.multiplier; } @Override - public boolean supportsPushInputsToExternalInventory() { - return original.supportsPushInputsToExternalInventory(); + public String toString() { + return "Scaled[" + original.getDefinition().getItem() + " × " + multiplier + "]"; } - @Override - public void pushInputsToExternalInventory(KeyCounter[] inputHolder, PatternInputSink inputSink) { - // 使用 lazy 计算的 sparseInputs 与 inputs 来驱动;当两者长度一致时直接委托 - GenericStack[] sInputs = getSparseInputs(); - IInput[] ins = getInputs(); - if (sInputs.length == ins.length) { - IPatternDetails.super.pushInputsToExternalInventory(inputHolder, inputSink); - return; + private static class ScaledInput implements IInput { + private final IInput delegate; + private final long mul; + + private ScaledInput(IInput delegate, long mul) { + this.delegate = delegate; + this.mul = mul; } - KeyCounter allInputs = new KeyCounter(); - for (KeyCounter counter : inputHolder) { - allInputs.addAll(counter); - } - for (GenericStack sparseInput : sInputs) { - 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); - } - } + @Override public GenericStack[] getPossibleInputs() { return delegate.getPossibleInputs(); } + @Override public long getMultiplier() { return delegate.getMultiplier() * mul; } + @Override public boolean isValid(AEKey input, Level level) { return delegate.isValid(input, level); } + @Override public @Nullable AEKey getRemainingKey(AEKey template) { return delegate.getRemainingKey(template); } } - - /* -------------------- 缩放输入代理 -------------------- */ - - 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; - } - - @Override - public GenericStack[] getPossibleInputs() { - return this.template; - } - - @Override - public long getMultiplier() { - return this.multiplier; - } - - @Override - public boolean isValid(AEKey input, Level level) { - return input.matches(this.template[0]); - } - - @Override - public @Nullable AEKey getRemainingKey(AEKey template) { - return null; - } - } -} +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/ae/api/crafting/ScaledProcessingPatternAdv.java b/src/main/java/com/extendedae_plus/ae/api/crafting/ScaledProcessingPatternAdv.java index f36a716..8721b2c 100644 --- a/src/main/java/com/extendedae_plus/ae/api/crafting/ScaledProcessingPatternAdv.java +++ b/src/main/java/com/extendedae_plus/ae/api/crafting/ScaledProcessingPatternAdv.java @@ -1,8 +1,6 @@ package com.extendedae_plus.ae.api.crafting; -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.core.Direction; @@ -18,8 +16,8 @@ public final class ScaledProcessingPatternAdv extends ScaledProcessingPattern im private final AdvPatternDetails adv; - public ScaledProcessingPatternAdv(AEProcessingPattern original, AEItemKey definition, long multiplier) { - super(original, definition, multiplier); + public ScaledProcessingPatternAdv(AEProcessingPattern original, long multiplier) { + super(original, multiplier); this.adv = (AdvPatternDetails) original; } @@ -40,30 +38,30 @@ public final class ScaledProcessingPatternAdv extends ScaledProcessingPattern im @Override public void pushInputsToExternalInventory(KeyCounter[] inputHolder, PatternInputSink inputSink) { - // 使用 lazy 计算的 sparseInputs 与 inputs 来驱动;当两者长度一致时直接委托 - GenericStack[] sInputs = getSparseInputs(); - IInput[] ins = getInputs(); - if (sInputs.length == ins.length) { - super.pushInputsToExternalInventory(inputHolder, inputSink); - return; - } - - KeyCounter allInputs = new KeyCounter(); - for (KeyCounter counter : inputHolder) { - allInputs.addAll(counter); - } - for (GenericStack sparseInput : sInputs) { - 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); - } - } +// // 使用 lazy 计算的 sparseInputs 与 inputs 来驱动;当两者长度一致时直接委托 +// GenericStack[] sInputs = getSparseInputs(); +// IInput[] ins = getInputs(); +// if (sInputs.length == ins.length) { +// super.pushInputsToExternalInventory(inputHolder, inputSink); +// return; +// } +// +// KeyCounter allInputs = new KeyCounter(); +// for (KeyCounter counter : inputHolder) { +// allInputs.addAll(counter); +// } +// for (GenericStack sparseInput : sInputs) { +// 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); +// } +// } } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingSimulationStateMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingSimulationStateMixin.java new file mode 100644 index 0000000..071f2b1 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingSimulationStateMixin.java @@ -0,0 +1,103 @@ +package com.extendedae_plus.mixin.ae2.autopattern; + +import appeng.api.crafting.IPatternDetails; +import appeng.crafting.inv.CraftingSimulationState; +import appeng.crafting.pattern.AEProcessingPattern; +import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern; +import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern; +import com.extendedae_plus.util.smartDoubling.PatternScaler; +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.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.IdentityHashMap; +import java.util.Map; + +@Mixin(value = CraftingSimulationState.class, remap = false) +public abstract class CraftingSimulationStateMixin { + + @Shadow private Map crafts; + + /** 仅用于无限制缩放时的合并缓存 */ + private final Map scaledCache = new IdentityHashMap<>(); + + @Inject(method = "addCrafting", at = @At("HEAD"), cancellable = true) + private void onAddCrafting(IPatternDetails details, long craftsAmount, CallbackInfo ci) { + ci.cancel(); + if (craftsAmount <= 0 || details == null) return; + + if (details instanceof AEProcessingPattern processingPattern) { + boolean allowScaling = true; + int perCraftLimit = 0; + + if (processingPattern instanceof ISmartDoublingAwarePattern aware) { + allowScaling = aware.eap$allowScaling(); + perCraftLimit = aware.eap$getMultiplierLimit(); + } + + if (!allowScaling) { + crafts.merge(processingPattern, craftsAmount, Long::sum); + return; + } + + // === 需求为 1 → 直接用原样板 === + if (craftsAmount == 1) { + crafts.merge(processingPattern, 1L, Long::sum); + return; + } + + // === 计算实际限制 === + if (perCraftLimit > 0) { + perCraftLimit = Math.min(perCraftLimit, PatternScaler.getComputedMul(processingPattern, perCraftLimit)); + } + + if (perCraftLimit <= 0) { + // 无限制 → 合并缩放 + mergeUnlimited(processingPattern, craftsAmount); + } else { + // 有限制 → 拆分 + splitLimited(processingPattern, craftsAmount, perCraftLimit); + } + + } else { + crafts.merge(details, craftsAmount, Long::sum); + } + } + + /** 无限制:合并倍率 */ + private void mergeUnlimited(AEProcessingPattern original, long multiplier) { + ScaledProcessingPattern existing = scaledCache.get(original); + long total = multiplier; + + if (existing != null) { + total += existing.getMultiplier(); + crafts.remove(existing); + } + + IPatternDetails scaled = PatternScaler.createScaled(original, total); + if (scaled instanceof ScaledProcessingPattern sp) { + scaledCache.put(original, sp); + crafts.put(sp, 1L); + } else { + crafts.put(original, total); // 退化为原样板 + } + } + + /** 有限制:拆分 full + remainder */ + private void splitLimited(AEProcessingPattern original, long totalAmount, int limit) { + long fullCrafts = totalAmount / limit; + long remainder = totalAmount % limit; + + if (fullCrafts > 0) { + IPatternDetails scaled = PatternScaler.createScaled(original, limit); + crafts.merge(scaled, fullCrafts, Long::sum); + } + + if (remainder > 0) { + IPatternDetails scaled = PatternScaler.createScaled(original, remainder); + crafts.merge(scaled, 1L, Long::sum); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/util/smartDoubling/PatternScaler.java b/src/main/java/com/extendedae_plus/util/smartDoubling/PatternScaler.java index 5aad36b..b707441 100644 --- a/src/main/java/com/extendedae_plus/util/smartDoubling/PatternScaler.java +++ b/src/main/java/com/extendedae_plus/util/smartDoubling/PatternScaler.java @@ -1,184 +1,60 @@ package com.extendedae_plus.util.smartDoubling; +import appeng.api.crafting.IPatternDetails; import appeng.api.stacks.AEFluidKey; import appeng.api.stacks.AEItemKey; import appeng.api.stacks.AEKey; -import appeng.api.stacks.GenericStack; import appeng.crafting.pattern.AEProcessingPattern; import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern; -import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern; -import com.extendedae_plus.config.ModConfig; -import net.minecraftforge.fml.loading.LoadingModList; - -import java.lang.reflect.Constructor; public final class PatternScaler { - // ---------- 静态缓存反射 ---------- - private static final boolean advAvailable; - private static final Constructor advCtor; - private static final Class advIfaceClass; + private PatternScaler() {} - static { - boolean available = false; - Constructor ctor = null; - Class iface = null; - - try { - // 尝试加载扩展类 - Class clazz = Class.forName("com.extendedae_plus.ae.api.crafting.ScaledProcessingPatternAdv"); - ctor = clazz.getConstructor(AEProcessingPattern.class, AEItemKey.class, long.class); - - // 加载接口 - iface = Class.forName("net.pedroksl.advanced_ae.common.patterns.AdvPatternDetails"); - - // 检查是否安装 Advanced AE - if (LoadingModList.get() != null && LoadingModList.get().getModFileById("advanced_ae") != null) { - available = true; - } - } catch (Throwable ignored) { + /** + * 创建缩放样板。 + *

如果 multiplier ≤ 1,直接返回原始样板(避免无意义包装)。

+ */ + public static IPatternDetails createScaled(AEProcessingPattern original, long multiplier) { + if (multiplier <= 1) { + return original; // 关键:demand=1 或 limit=1 → 直接用原样板 } - - advAvailable = available; - advCtor = ctor; - advIfaceClass = iface; - } - - 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"); - - // 双保险:若样板标记为不允许缩放,直接放弃缩放(返回 null 表示调用方应保持原样板) - if (base instanceof ISmartDoublingAwarePattern aware && !aware.eap$allowScaling()) { - return null; - } - - 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; - } - - long multiplier = 1L; // 默认倍数 - - // ---------------------- 优先模式限制 ---------------------- - boolean patternHasLimit = false; - if (base instanceof ISmartDoublingAwarePattern aware) { - int patternMulLimit = aware.eap$getMultiplierLimit(); - if (patternMulLimit > 0) { - multiplier = patternMulLimit; // 直接使用模式限制作为倍数 - patternHasLimit = true; - } - } - - // ---------------------- 全局逻辑(仅在没有模式限制时生效) ---------------------- - if (!patternHasLimit && requestedAmount > 0) { - // 计算满足请求量的最小倍数 - long needed = requestedAmount / perOperationTarget + ((requestedAmount % perOperationTarget) == 0 ? 0 : 1); - multiplier = Math.max(needed, 1L); - - // 应用全局上限 - int maxMul = ModConfig.INSTANCE.smartScalingMaxMultiplier; - if (maxMul > 0 && multiplier > maxMul) multiplier = maxMul; - } - - // ---------------------- 小请求绕过 ---------------------- - try { - int minBenefit = ModConfig.INSTANCE.smartScalingMinBenefitFactor; - if (minBenefit > 1 && requestedAmount > 0 && requestedAmount < perOperationTarget * (long) minBenefit) { - return null; - } - } catch (Throwable ignore) {} - - // ---------- Advanced AE 扩展 ---------- - if (advAvailable && advIfaceClass != null && advCtor != null) { - try { - if (advIfaceClass.isInstance(base)) { - return (ScaledProcessingPattern) advCtor.newInstance(base, base.getDefinition(), multiplier); - } - } catch (Throwable ignore) { - // 出错就退回普通逻辑 - } - } - - // 仅使用 multiplier 构建轻量化 ScaledProcessingPattern(具体视图按需计算) - return new ScaledProcessingPattern(base, base.getDefinition(), multiplier); - } - - public static int getComputedMul(AEProcessingPattern proc, int limit) { - int computedMul = 0; // 默认 0 表示不限制 - - if (limit > 0) { - long minMul = Long.MAX_VALUE; - - for (var input : proc.getInputs()) { - long amt = input.getMultiplier(); - if (amt <= 0) continue; - - AEKey key = input.getPossibleInputs()[0].what(); - - // 使用统一单位换算 - long unitMultiplier = getUnitMultiplier(key); - long limitInAEUnit = limit * unitMultiplier; - - // 计算该输入允许的倍数 - long allowedMul = limitInAEUnit / amt; - - // 保证至少为 1 - allowedMul = Math.max(1, allowedMul); - - // 取最小值,保证所有输入都不超过 limit - minMul = Math.min(minMul, allowedMul); - } - - if (minMul != Long.MAX_VALUE) { - computedMul = (int) Math.min(minMul, Integer.MAX_VALUE); - } - } - - return computedMul; + return new ScaledProcessingPattern(original, multiplier); } /** - * 获取 AEKey 的单位换算系数 - * 物品默认 1,流体默认 1000,其它类型可通过扩展接口提供 + * 计算基于 limit 的最大允许倍率(单次输出主物品 ≤ limit) */ + public static int getComputedMul(AEProcessingPattern proc, int limit) { + if (limit <= 0) return 0; // 0 = 不限制 + + long minMul = Long.MAX_VALUE; + + for (var input : proc.getInputs()) { + long amt = input.getMultiplier(); + if (amt <= 0) continue; + + AEKey key = input.getPossibleInputs()[0].what(); + long unitMultiplier = getUnitMultiplier(key); + long limitInAEUnit = (long) limit * unitMultiplier; + + long allowedMul = limitInAEUnit / amt; + allowedMul = Math.max(1, allowedMul); // 至少 1 + minMul = Math.min(minMul, allowedMul); + } + + return minMul != Long.MAX_VALUE ? (int) Math.min(minMul, Integer.MAX_VALUE) : 0; + } + private static long getUnitMultiplier(AEKey key) { if (key instanceof AEItemKey) return 1L; if (key instanceof AEFluidKey) return 1000L; + // 支持 Mekanism Chemical 等(反射安全) try { - // 反射判断扩展 Key 类型 - if (key.getClass().getName().equals("me.ramidzkh.mekae2.ae2.MekanismKey")) { + if ("me.ramidzkh.mekae2.ae2.MekanismKey".equals(key.getClass().getName())) { return 1000L; } - // 根据需要继续增加 } catch (Exception ignored) {} - - return 1L; // 默认单位 + return 1L; } -} +} \ 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 19d339a..90943de 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -53,9 +53,7 @@ "ae2.accessor.PatternProviderMenuAccessor", "ae2.autopattern.CraftingProviderListAccessor", "ae2.autopattern.CraftingServiceGetProvidersMixin", - "ae2.autopattern.CraftingTreeNodeAccessor", - "ae2.autopattern.CraftingTreeNodeMixin", - "ae2.autopattern.CraftingTreeProcessMixin", + "ae2.autopattern.CraftingSimulationStateMixin", "ae2.autopattern.PatternProviderLogicContainsRedirectMixin", "ae2.compat.PatternProviderCompatMixin", "ae2.compat.PatternProviderLogicCompatMixin",