diff --git a/CHANGELOG.md b/CHANGELOG.md index 0176c3e..28f0d04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,25 @@ ## [Unreleased] +## [1.4.3] +### ⚠️ 重要提醒 / Important Notice +- **更新会导致旧版的无限盘失效,如有需要请先导盘再更新** + - **Updating will invalidate old infinite disks. Please export data before updating if needed.** + +### Added / 新增 +- 装配矩阵上传核心 - 现在必须在装配矩阵中添加该核心才能正常使用自动上传合成样板功能 + - Assembly Matrix Upload Core - now required in Assembly Matrix to use automatic pattern upload functionality. +- 频道卡 - 手持频道卡右键可调整频率,将频道卡放入AE设备将自动连接上同频率的本模组的无线收发器 + - Channel Card - right-click while holding to adjust frequency, placing in AE devices automatically connects to same-frequency wireless transceivers. + +### Fixed / 修复 +- 修复合成计划中添加缺失物品时,流体、MEK化学品出现错误书签 + - Fixed incorrect bookmarks for fluids and Mekanism chemicals when adding missing items in crafting plan. +- 修复吞噬万籁的寂静的重大bug + - Fixed critical bug in Devourer of Cosmic Silence. +- 修复JEI模组不存在时导致的崩溃问题 + - Fixed crash when JEI mod is not present. + ## [1.4.2] ### Added / 新增 - 添加实体加速器,最高可加速 1024 倍(配置文件可配置能耗、黑名单、额外消耗倍率名单) diff --git a/build.gradle b/build.gradle index 2d61a88..9e12294 100644 --- a/build.gradle +++ b/build.gradle @@ -107,7 +107,7 @@ dependencies { //mae2 - modRuntimeOnly "curse.maven:modern-ae2-additions-1028068:6827727" + // modRuntimeOnly "curse.maven:modern-ae2-additions-1028068:6827727" modCompileOnly "curse.maven:modern-ae2-additions-1028068:6827727" //aea diff --git a/gradle.properties b/gradle.properties index d673cb8..ba9b599 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G loom.platform = forge # Mod properties -mod_version = 1.4.3-beta +mod_version = 1.4.3 maven_group = com.extendedae_plus archives_name = extendedae_plus diff --git a/src/main/java/com/extendedae_plus/client/InputEvents.java b/src/main/java/com/extendedae_plus/client/InputEvents.java index d5766a9..f99765f 100644 --- a/src/main/java/com/extendedae_plus/client/InputEvents.java +++ b/src/main/java/com/extendedae_plus/client/InputEvents.java @@ -33,10 +33,7 @@ public final class InputEvents { if (!ModList.get().isLoaded("jei")) { return; } - // 若 JEI 运行时尚未就绪,跳过 - if (JeiRuntimeProxy.get() == null) { - return; - } + // 注意:不要在 try/catch 之外直接访问 JEI 运行时,避免类加载崩溃 // 优先处理:Shift + 左键(拉取或下单) if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_LEFT && Screen.hasShiftDown()) { try { @@ -103,9 +100,7 @@ public final class InputEvents { if (!ModList.get().isLoaded("jei")) { return; } - if (JeiRuntimeProxy.get() == null) { - return; - } + // 注意:不要在 try/catch 之外直接访问 JEI 运行时,避免类加载崩溃 if (event.getKeyCode() != GLFW.GLFW_KEY_F) return; // 仅当鼠标确实悬停在 JEI 配料上时触发 diff --git a/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java b/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java index 1984717..6b8b229 100644 --- a/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java +++ b/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java @@ -183,10 +183,29 @@ public class ProviderSelectScreen extends Screen { } } + /** + * 将服务器发送的名称(可能是 Component JSON)反序列化为本地化文本 + */ + private String deserializeComponentName(String name) { + try { + // 如果名称是 JSON 格式的 Component,反序列化后获取本地化文本 + if (name.startsWith("{") || name.startsWith("\"")) { + Component component = Component.Serializer.fromJson(name); + if (component != null) { + return component.getString(); + } + } + } catch (Exception e) { + // 如果不是 JSON 或解析失败,使用原始字符串 + } + return name; + } + private String buildLabel(int idx) { String name = fNames.get(idx); int totalSlots = fTotalSlots.get(idx); int count = fCount.get(idx); + // 不显示具体 id,显示合并统计:名称(总空位)x数量 return name + " (" + totalSlots + ") x" + count; } @@ -215,7 +234,11 @@ public class ProviderSelectScreen extends Screen { String name = names.get(i); long id = ids.get(i); int slots = emptySlots.get(i); - Group g = map.computeIfAbsent(name, k -> new Group()); + + // 将 Component JSON 转换为本地化文本用于分组键 + String groupKey = deserializeComponentName(name); + + Group g = map.computeIfAbsent(groupKey, k -> new Group()); g.count++; g.totalSlots += Math.max(0, slots); // 挑选空位最多的作为代表 id;若并列,保留先到者 diff --git a/src/main/java/com/extendedae_plus/compat/CompatibilityTest.java b/src/main/java/com/extendedae_plus/compat/CompatibilityTest.java index ffb5556..8cfe537 100644 --- a/src/main/java/com/extendedae_plus/compat/CompatibilityTest.java +++ b/src/main/java/com/extendedae_plus/compat/CompatibilityTest.java @@ -13,28 +13,21 @@ public class CompatibilityTest { * 测试模组兼容性检测 */ public static void testCompatibility() { - Logger.EAP$LOGGER.info("=== ExtendedAE_Plus 兼容性测试开始 ==="); // 测试appflux模组检测 boolean appfluxExists = ModList.get().isLoaded("appflux"); - Logger.EAP$LOGGER.info("ExtendedAE-appflux模组检测结果: {}", appfluxExists ? "存在" : "不存在"); // 测试升级卡槽功能启用状态 boolean shouldEnableUpgrades = UpgradeSlotCompat.shouldEnableUpgradeSlots(); - Logger.EAP$LOGGER.info("升级卡槽功能启用状态: {}", shouldEnableUpgrades ? "启用" : "禁用"); // 测试Screen升级面板添加状态 boolean shouldAddPanel = UpgradeSlotCompat.shouldAddUpgradePanelToScreen(); - Logger.EAP$LOGGER.info("Screen升级面板添加状态: {}", shouldAddPanel ? "启用" : "禁用"); // 输出兼容性策略 if (appfluxExists) { - Logger.EAP$LOGGER.info("兼容性策略: 检测到ExtendedAE-appflux模组,将使用其升级卡槽功能"); } else { - Logger.EAP$LOGGER.info("兼容性策略: 未检测到ExtendedAE-appflux模组,将使用我们自己的升级卡槽功能"); } - - Logger.EAP$LOGGER.info("=== ExtendedAE_Plus 兼容性测试完成 ==="); + } /** diff --git a/src/main/java/com/extendedae_plus/integration/jei/JeiBookmarkBridge.java b/src/main/java/com/extendedae_plus/integration/jei/JeiBookmarkBridge.java new file mode 100644 index 0000000..9e1572d --- /dev/null +++ b/src/main/java/com/extendedae_plus/integration/jei/JeiBookmarkBridge.java @@ -0,0 +1,181 @@ +package com.extendedae_plus.integration.jei; + +import com.extendedae_plus.mixin.jei.accessor.BookmarkOverlayAccessor; +import mezz.jei.api.constants.VanillaTypes; +import mezz.jei.api.forge.ForgeTypes; +import mezz.jei.api.ingredients.IIngredientType; +import mezz.jei.api.ingredients.ITypedIngredient; +import mezz.jei.api.runtime.IBookmarkOverlay; +import mezz.jei.api.runtime.IJeiRuntime; +import mezz.jei.gui.bookmarks.BookmarkList; +import mezz.jei.gui.bookmarks.IngredientBookmark; +import mezz.jei.gui.overlay.elements.IElement; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fml.ModList; +import org.spongepowered.asm.mixin.Pseudo; + +import javax.annotation.Nullable; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * 将所有会引用 JEI GUI 内部类(如 BookmarkList、IngredientBookmark、IElement 等)的逻辑 + * 隔离在此桥接类中,避免在未安装 JEI 或 JEI 组件不完整时过早类加载导致的 NoClassDefFoundError。 + * + * 该类仅会被 {@link JeiRuntimeProxy} 通过反射调用。 + */ +@Pseudo +public final class JeiBookmarkBridge { + private JeiBookmarkBridge() {} + + // 通过 JeiRuntimeProxy 的包内可见方法安全地获取 Runtime + private static @Nullable IJeiRuntime getRuntime() { + try { + Class proxy = Class.forName("com.extendedae_plus.integration.jei.JeiRuntimeProxy"); + var m = proxy.getDeclaredMethod("get"); + Object rt = m.invoke(null); + return (IJeiRuntime) rt; + } catch (Throwable ignored) { + return null; + } + } + + public static List> getBookmarkList() { + IJeiRuntime rt = getRuntime(); + if (rt == null) return Collections.emptyList(); + IBookmarkOverlay bookmarkOverlay = rt.getBookmarkOverlay(); + if (bookmarkOverlay instanceof BookmarkOverlayAccessor accessor) { + BookmarkList bookmarkList = accessor.eap$getBookmarkList(); + return bookmarkList.getElements().stream().map(IElement::getTypedIngredient).toList(); + } + return Collections.emptyList(); + } + + public static void addBookmark(ItemStack stack) { + IJeiRuntime rt = getRuntime(); + if (rt == null) return; + + IBookmarkOverlay overlay = rt.getBookmarkOverlay(); + if (overlay instanceof BookmarkOverlayAccessor accessor) { + BookmarkList list = accessor.eap$getBookmarkList(); + Optional> typedOpt = rt.getIngredientManager() + .createTypedIngredient(VanillaTypes.ITEM_STACK, stack); + typedOpt.ifPresent(typed -> { + IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); + list.add(bookmark); // add 内部会自动保存到配置 + }); + } + } + + public static void addBookmark(FluidStack fluidStack) { + IJeiRuntime rt = getRuntime(); + if (rt == null) return; + + IBookmarkOverlay overlay = rt.getBookmarkOverlay(); + if (overlay instanceof BookmarkOverlayAccessor accessor) { + BookmarkList list = accessor.eap$getBookmarkList(); + Optional> typedOpt = rt.getIngredientManager() + .createTypedIngredient(ForgeTypes.FLUID_STACK, fluidStack); + typedOpt.ifPresent(typed -> { + IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); + list.add(bookmark); // add 内部会自动保存到配置 + }); + } + } + + /** + * 如果存在 Mekanism/appmek,则将 Mekanism 化学堆栈添加到 JEI 书签。 + */ + public static void addBookmark(Object chemicalStack) { + if (!ModList.get().isLoaded("mekanism") && !ModList.get().isLoaded("appmek")) return; + + IJeiRuntime rt = getRuntime(); + if (rt == null) return; + + IBookmarkOverlay overlay = rt.getBookmarkOverlay(); + if (overlay instanceof BookmarkOverlayAccessor accessor) { + BookmarkList list = accessor.eap$getBookmarkList(); + try { + if (chemicalStack == null) return; + + // Determine Mekanism JEI ingredient type constant by runtime class name + String clsName = chemicalStack.getClass().getName(); + String mekanismJeiClass = "mekanism.client.jei.MekanismJEI"; + Class jeiCls = Class.forName(mekanismJeiClass); + Field typeField = getField(clsName, jeiCls); + + if (typeField == null) return; + Object typeConst = typeField.get(null); + + // Use ingredient manager reflectively to create a typed ingredient + Object ingredientManager = rt.getIngredientManager(); + Method createTypedIngredient = ingredientManager.getClass().getMethod("createTypedIngredient", IIngredientType.class, Object.class); + Object opt = createTypedIngredient.invoke(ingredientManager, typeConst, chemicalStack); + if (!(opt instanceof Optional typedOpt)) return; + if (typedOpt.isPresent()) { + Object typed = typedOpt.get(); + // Find a compatible static create(...) method on IngredientBookmark where + // the second parameter is assignable from the actual ingredientManager instance. + Method createMethod = null; + for (Method m : IngredientBookmark.class.getMethods()) { + if (!m.getName().equals("create")) continue; + Class[] params = m.getParameterTypes(); + if (params.length != 2) continue; + // first param should accept the typed ingredient + boolean firstOk = params[0].isAssignableFrom(typed.getClass()) || params[0].isAssignableFrom(ITypedIngredient.class); + boolean secondOk = params[1].isAssignableFrom(ingredientManager.getClass()); + if (firstOk && secondOk) { + createMethod = m; + break; + } + } + if (createMethod != null) { + Object bookmark = createMethod.invoke(null, typed, ingredientManager); + if (bookmark != null) { + list.add((IngredientBookmark) bookmark); + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + private static @Nullable Field getField(String clsName, Class jeiCls) throws NoSuchFieldException { + Field typeField = null; + if ("mekanism.api.chemical.gas.GasStack".equals(clsName)) { + typeField = jeiCls.getField("TYPE_GAS"); + } else if ("mekanism.api.chemical.infuse.InfusionStack".equals(clsName)) { + typeField = jeiCls.getField("TYPE_INFUSION"); + } else if ("mekanism.api.chemical.pigment.PigmentStack".equals(clsName)) { + typeField = jeiCls.getField("TYPE_PIGMENT"); + } else if ("mekanism.api.chemical.slurry.SlurryStack".equals(clsName)) { + typeField = jeiCls.getField("TYPE_SLURRY"); + } + return typeField; + } + + /** + * 从 JEI 书签移除物品 + */ + public static void removeBookmark(ItemStack stack) { + IJeiRuntime rt = getRuntime(); + if (rt == null) return; + + IBookmarkOverlay overlay = rt.getBookmarkOverlay(); + if (overlay instanceof BookmarkOverlayAccessor accessor) { + BookmarkList list = accessor.eap$getBookmarkList(); + Optional> typedOpt = rt.getIngredientManager() + .createTypedIngredient(VanillaTypes.ITEM_STACK, stack); + typedOpt.ifPresent(typed -> { + IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); + list.remove(bookmark); + }); + } + } +} diff --git a/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java b/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java index bc5276e..38c3541 100644 --- a/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java +++ b/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java @@ -1,24 +1,16 @@ package com.extendedae_plus.integration.jei; -import com.extendedae_plus.mixin.jei.accessor.BookmarkOverlayAccessor; import mezz.jei.api.constants.VanillaTypes; -import mezz.jei.api.forge.ForgeTypes; -import mezz.jei.api.ingredients.IIngredientType; import mezz.jei.api.ingredients.ITypedIngredient; import mezz.jei.api.runtime.IBookmarkOverlay; import mezz.jei.api.runtime.IIngredientListOverlay; import mezz.jei.api.runtime.IJeiRuntime; -import mezz.jei.gui.bookmarks.BookmarkList; -import mezz.jei.gui.bookmarks.IngredientBookmark; -import mezz.jei.gui.overlay.elements.IElement; import net.minecraft.world.item.ItemStack; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fml.ModList; import org.spongepowered.asm.mixin.Pseudo; import javax.annotation.Nullable; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -131,48 +123,35 @@ public final class JeiRuntimeProxy { } /** - * 获取JEI书签列表 + * 获取 JEI 书签列表。为避免在未安装 JEI GUI 时崩溃,使用反射委托到桥接类。 */ public static List> getBookmarkList() { - IJeiRuntime rt = RUNTIME; - if (rt == null) return Collections.emptyList(); - IBookmarkOverlay bookmarkOverlay = rt.getBookmarkOverlay(); - if (bookmarkOverlay instanceof BookmarkOverlayAccessor accessor) { - BookmarkList bookmarkList = accessor.eap$getBookmarkList(); - return bookmarkList.getElements().stream().map(IElement::getTypedIngredient).toList(); + try { + Class bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge"); + var m = bridge.getMethod("getBookmarkList"); + @SuppressWarnings("unchecked") + List> list = (List>) m.invoke(null); + return list == null ? Collections.emptyList() : list; + } catch (Throwable ignored) { + return Collections.emptyList(); } - return Collections.emptyList(); } public static void addBookmark(ItemStack stack) { - IJeiRuntime rt = RUNTIME; - if (rt == null) return; - - IBookmarkOverlay overlay = rt.getBookmarkOverlay(); - if (overlay instanceof BookmarkOverlayAccessor accessor) { - BookmarkList list = accessor.eap$getBookmarkList(); - Optional> typedOpt = rt.getIngredientManager() - .createTypedIngredient(VanillaTypes.ITEM_STACK, stack); - typedOpt.ifPresent(typed -> { - IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); - list.add(bookmark); // add 内部会自动保存到配置 - }); + try { + Class bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge"); + var m = bridge.getMethod("addBookmark", ItemStack.class); + m.invoke(null, stack); + } catch (Throwable ignored) { } } public static void addBookmark(FluidStack fluidStack) { - IJeiRuntime rt = RUNTIME; - if (rt == null) return; - - IBookmarkOverlay overlay = rt.getBookmarkOverlay(); - if (overlay instanceof BookmarkOverlayAccessor accessor) { - BookmarkList list = accessor.eap$getBookmarkList(); - Optional> typedOpt = rt.getIngredientManager() - .createTypedIngredient(ForgeTypes.FLUID_STACK, fluidStack); - typedOpt.ifPresent(typed -> { - IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); - list.add(bookmark); // add 内部会自动保存到配置 - }); + try { + Class bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge"); + var m = bridge.getMethod("addBookmark", FluidStack.class); + m.invoke(null, fluidStack); + } catch (Throwable ignored) { } } @@ -180,91 +159,25 @@ public final class JeiRuntimeProxy { * 如果存在 Mekanism/appmek,则将 Mekanism 化学堆栈添加到 JEI 书签。 */ public static void addBookmark(Object chemicalStack) { - if (!ModList.get().isLoaded("mekanism") && !ModList.get().isLoaded("appmek")) return; - - IJeiRuntime rt = RUNTIME; - if (rt == null) return; - - IBookmarkOverlay overlay = rt.getBookmarkOverlay(); - if (overlay instanceof BookmarkOverlayAccessor accessor) { - BookmarkList list = accessor.eap$getBookmarkList(); - try { - if (chemicalStack == null) return; - - // Determine Mekanism JEI ingredient type constant by runtime class name - String clsName = chemicalStack.getClass().getName(); - String mekanismJeiClass = "mekanism.client.jei.MekanismJEI"; - Class jeiCls = Class.forName(mekanismJeiClass); - Field typeField = getField(clsName, jeiCls); - - if (typeField == null) return; - Object typeConst = typeField.get(null); - - // Use ingredient manager reflectively to create a typed ingredient - Object ingredientManager = rt.getIngredientManager(); - Method createTypedIngredient = ingredientManager.getClass().getMethod("createTypedIngredient", IIngredientType.class, Object.class); - Object opt = createTypedIngredient.invoke(ingredientManager, typeConst, chemicalStack); - if (!(opt instanceof Optional typedOpt)) return; - if (typedOpt.isPresent()) { - Object typed = typedOpt.get(); - // Find a compatible static create(...) method on IngredientBookmark where - // the second parameter is assignable from the actual ingredientManager instance. - Method createMethod = null; - for (Method m : IngredientBookmark.class.getMethods()) { - if (!m.getName().equals("create")) continue; - Class[] params = m.getParameterTypes(); - if (params.length != 2) continue; - // first param should accept the typed ingredient - boolean firstOk = params[0].isAssignableFrom(typed.getClass()) || params[0].isAssignableFrom(ITypedIngredient.class); - boolean secondOk = params[1].isAssignableFrom(ingredientManager.getClass()); - if (firstOk && secondOk) { - createMethod = m; - break; - } - } - if (createMethod != null) { - Object bookmark = createMethod.invoke(null, typed, ingredientManager); - if (bookmark != null) { - list.add((IngredientBookmark) bookmark); - } - } - } - } catch (Throwable e) { - e.printStackTrace(); - } + try { + Class bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge"); + var m = bridge.getMethod("addBookmark", Object.class); + m.invoke(null, chemicalStack); + } catch (Throwable ignored) { } } - private static @Nullable Field getField(String clsName, Class jeiCls) throws NoSuchFieldException { - Field typeField = null; - if ("mekanism.api.chemical.gas.GasStack".equals(clsName)) { - typeField = jeiCls.getField("TYPE_GAS"); - } else if ("mekanism.api.chemical.infuse.InfusionStack".equals(clsName)) { - typeField = jeiCls.getField("TYPE_INFUSION"); - } else if ("mekanism.api.chemical.pigment.PigmentStack".equals(clsName)) { - typeField = jeiCls.getField("TYPE_PIGMENT"); - } else if ("mekanism.api.chemical.slurry.SlurryStack".equals(clsName)) { - typeField = jeiCls.getField("TYPE_SLURRY"); - } - return typeField; - } + // Note: helper methods moved to bridge to avoid referencing JEI GUI at class load time. /** - * 从 JEI 书签移除物品 + * 从 JEI 书签移除物品(反射委托) */ public static void removeBookmark(ItemStack stack) { - IJeiRuntime rt = RUNTIME; - if (rt == null) return; - - IBookmarkOverlay overlay = rt.getBookmarkOverlay(); - if (overlay instanceof BookmarkOverlayAccessor accessor) { - BookmarkList list = accessor.eap$getBookmarkList(); - Optional> typedOpt = rt.getIngredientManager() - .createTypedIngredient(VanillaTypes.ITEM_STACK, stack); - typedOpt.ifPresent(typed -> { - IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); - list.remove(bookmark); - }); + try { + Class bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge"); + var m = bridge.getMethod("removeBookmark", ItemStack.class); + m.invoke(null, stack); + } catch (Throwable ignored) { } } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/InterfaceScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/InterfaceScreenMixin.java index 5bb6ec1..e998b5c 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/InterfaceScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/InterfaceScreenMixin.java @@ -248,7 +248,7 @@ public abstract class InterfaceScreenMixin { int imageWidth = ((AbstractContainerScreenAccessor) (Object) this).eap$getImageWidth(); // 按照样板供应器界面一致的布局:界面右缘外侧竖排 int bx = leftPos + imageWidth + 1; - int by = topPos + 20; + int by = topPos + 70; // 向下偏移25px (从20改为45) int spacing = 22; if (eap$divideBy2Button != null) { eap$divideBy2Button.setX(bx); eap$divideBy2Button.setY(by); } if (eap$x2Button != null) { eap$x2Button.setX(bx); eap$x2Button.setY(by + spacing); } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java index 25a4cfc..975459c 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java @@ -99,9 +99,11 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj at = @At("TAIL")) private void eap$compatInitUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) { try { + boolean upgradeSlots = UpgradeSlotCompat.shouldEnableUpgradeSlots(); boolean channelCard = UpgradeSlotCompat.shouldEnableChannelCard(); + if (upgradeSlots) { // 只有在升级槽功能启用时才创建升级槽 this.eap$compatUpgrades = UpgradeInventories.forMachine( diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/items/QuartzCuttingKnifeItemMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/items/QuartzCuttingKnifeItemMixin.java index 7a6efcf..31de43d 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/items/QuartzCuttingKnifeItemMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/items/QuartzCuttingKnifeItemMixin.java @@ -196,7 +196,6 @@ public abstract class QuartzCuttingKnifeItemMixin { } } } catch (ClassNotFoundException e) { - EAP$LOGGER.info("GregTech CEu 类未找到,跳过配方翻译处理"); return null; // GTCEu 不可用 } catch (NoSuchFieldException | NoSuchMethodException | IllegalAccessException | java.lang.reflect.InvocationTargetException e) { EAP$LOGGER.error("处理 GTCEu 配方翻译失败: {}", e.getMessage()); diff --git a/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java b/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java index af7cf1b..31cbfd9 100644 --- a/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java @@ -28,8 +28,10 @@ public class AppfluxPatternProviderLogicMixin { at = @At("TAIL")) private void eap$modifyAppfluxUpgradeSlots(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) { try { + // 只有当appflux存在且不启用我们的升级槽时才修改数量 if (!UpgradeSlotCompat.shouldEnableUpgradeSlots() && UpgradeSlotCompat.shouldEnableChannelCard()) { + // 使用反射找到appflux的升级槽字段并替换 try { Field upgradesField = this.getClass().getDeclaredField("af_$upgrades"); @@ -37,6 +39,7 @@ public class AppfluxPatternProviderLogicMixin { IUpgradeInventory currentUpgrades = (IUpgradeInventory) upgradesField.get(this); if (currentUpgrades != null) { + // 创建新的2槽升级槽 IUpgradeInventory newUpgrades = UpgradeInventories.forMachine( host.getTerminalIcon().getItem(), diff --git a/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternProviderMixin.java b/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternProviderMixin.java index 4b0df62..1b27516 100644 --- a/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternProviderMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternProviderMixin.java @@ -127,7 +127,6 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen {} (max={})", currentPage, newPage, maxPage); + // 强制重排(放在更新本地页码之后,确保布局读取到新页) this.repositionSlots(SlotSemantics.ENCODED_PATTERN); this.repositionSlots(SlotSemantics.STORAGE); this.hoveredSlot = null; @@ -185,8 +183,7 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen {} (max={})", currentPage, newPage, maxPage); + // 强制重排(放在更新本地页码之后,确保布局读取到新页) this.repositionSlots(SlotSemantics.ENCODED_PATTERN); this.repositionSlots(SlotSemantics.STORAGE); this.hoveredSlot = null; @@ -329,7 +326,7 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen patterns = craftingService.getCraftingFor(msg.what); - LogUtils.getLogger().info("EAP[S]: patterns found={} for key={}", patterns.size(), msg.what); if (patterns.isEmpty()) { return; } @@ -90,9 +85,6 @@ public class CraftingMonitorJumpC2SPacket { int providerCount = 0; for (var provider : providers) { providerCount++; - try { - LogUtils.getLogger().info("EAP[S]: provider class={}", provider.getClass().getName()); - } catch (Throwable ignored) {} if (provider instanceof PatternProviderLogic ppl) { // 使用 accessor 获取 host(受保护字段通过 accessor 访问) PatternProviderLogicHost host = ((PatternProviderLogicAccessor) ppl).eap$host(); @@ -105,7 +97,6 @@ public class CraftingMonitorJumpC2SPacket { BlockPos targetPos = pbe.getBlockPos().relative(dir); var tbe = serverLevel.getBlockEntity(targetPos); if (tbe instanceof MenuProvider provider1) { - LogUtils.getLogger().info("EAP[S]: open screen via MenuProvider at {}", targetPos); NetworkHooks.openScreen(player, provider1, targetPos); context.setPacketHandled(true); return; @@ -113,7 +104,6 @@ public class CraftingMonitorJumpC2SPacket { var tstate = serverLevel.getBlockState(targetPos); var provider2 = tstate.getMenuProvider(serverLevel, targetPos); if (provider2 != null) { - LogUtils.getLogger().info("EAP[S]: open screen via state.getMenuProvider at {}", targetPos); NetworkHooks.openScreen(player, provider2, targetPos); context.setPacketHandled(true); return; @@ -136,7 +126,6 @@ public class CraftingMonitorJumpC2SPacket { var state2 = serverLevel.getBlockState(targetPos); var hit = new BlockHitResult(Vec3.atCenterOf(targetPos), chosen.getOpposite(), targetPos, false); InteractionResult r = state2.use(serverLevel, player, hand, hit); - LogUtils.getLogger().info("EAP[S]: simulated use on {}, face={}, result={}", targetPos, chosen, r); if (r.consumesAction()) { context.setPacketHandled(true); return; @@ -144,9 +133,7 @@ public class CraftingMonitorJumpC2SPacket { } } } - LogUtils.getLogger().info("EAP[S]: providers count for one pattern: {}", providerCount); } - LogUtils.getLogger().info("EAP[S]: no target opened for key={}", msg.what); }); context.setPacketHandled(true); } diff --git a/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java b/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java index d9f2542..6715411 100644 --- a/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java +++ b/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java @@ -941,7 +941,9 @@ public class ExtendedAEPatternUploadUtil { // 尝试获取供应器的组信息来构建显示名称 var group = container.getTerminalGroup(); if (group != null) { - return group.name().getString(); + // 使用 Component 序列化来保持翻译键,而不是直接 getString() + // 这样客户端可以根据自己的语言设置进行翻译 + return Component.Serializer.toJson(group.name()); } } catch (Exception e) { // 忽略异常,使用默认名称 @@ -1183,7 +1185,11 @@ public class ExtendedAEPatternUploadUtil { if (container == null) return "未知供应器"; try { var group = container.getTerminalGroup(); - if (group != null) return group.name().getString(); + if (group != null) { + // 使用 Component 序列化来保持翻译键,而不是直接 getString() + // 这样客户端可以根据自己的语言设置进行翻译 + return Component.Serializer.toJson(group.name()); + } } catch (Throwable ignored) { } return "样板供应器";