From 20071e126ddd47aef08ba90808edfe82bfef9a9b Mon Sep 17 00:00:00 2001 From: GaLi <3096147684@qq.com> Date: Fri, 22 Aug 2025 13:21:01 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E4=BF=AE=E4=BF=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/ExPatternButtonsAccessor.java | 11 + .../api/ExPatternPageAccessor.java | 8 + .../mixin/ae2/PatternProviderScreenMixin.java | 11 + .../mixin/ae2/SlotGridLayoutMixin.java | 51 ++- .../extendedae/GuiExPatternProviderMixin.java | 414 +++++++++--------- 5 files changed, 273 insertions(+), 222 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/api/ExPatternButtonsAccessor.java create mode 100644 src/main/java/com/extendedae_plus/api/ExPatternPageAccessor.java diff --git a/src/main/java/com/extendedae_plus/api/ExPatternButtonsAccessor.java b/src/main/java/com/extendedae_plus/api/ExPatternButtonsAccessor.java new file mode 100644 index 0000000..d97f448 --- /dev/null +++ b/src/main/java/com/extendedae_plus/api/ExPatternButtonsAccessor.java @@ -0,0 +1,11 @@ +package com.extendedae_plus.api; + +/** + * 由 {@code GuiExPatternProviderMixin} 实现,用于从通用的 Screen Mixin 中更新按钮布局。 + */ +public interface ExPatternButtonsAccessor { + /** + * 在每帧调用以维护扩展样板供应器右侧按钮的可见性、重注册(窗口尺寸变化)与定位。 + */ + void eap$updateButtonsLayout(); +} diff --git a/src/main/java/com/extendedae_plus/api/ExPatternPageAccessor.java b/src/main/java/com/extendedae_plus/api/ExPatternPageAccessor.java new file mode 100644 index 0000000..fca633f --- /dev/null +++ b/src/main/java/com/extendedae_plus/api/ExPatternPageAccessor.java @@ -0,0 +1,8 @@ +package com.extendedae_plus.api; + +/** + * 由 GuiExPatternProviderMixin 实现,用于在客户端侧提供当前页号,避免反射读取 AE2 内部字段失败。 + */ +public interface ExPatternPageAccessor { + int eap$getCurrentPage(); +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderScreenMixin.java index 4d3fc71..ed04380 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderScreenMixin.java @@ -11,6 +11,8 @@ import appeng.menu.implementations.PatternProviderMenu; import com.extendedae_plus.api.PatternProviderMenuAdvancedSync; import com.extendedae_plus.network.ModNetwork; import com.extendedae_plus.network.ToggleAdvancedBlockingC2SPacket; +import com.extendedae_plus.api.ExPatternButtonsAccessor; +import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; import org.spongepowered.asm.mixin.Mixin; @@ -94,5 +96,14 @@ public abstract class PatternProviderScreenMixin } this.eap$AdvancedBlockingEnabled = desired; this.eap$AdvancedBlockingToggle.set(desired ? YesNo.YES : YesNo.NO); + + // 如果当前屏幕是 ExtendedAE 的 GuiExPatternProvider,则委托布局更新到 accessor + if ((Object) this instanceof GuiExPatternProvider) { + try { + ((ExPatternButtonsAccessor) this).eap$updateButtonsLayout(); + } catch (Throwable t) { + LOGGER.debug("[EAP] updateButtonsLayout skipped: {}", t.toString()); + } + } } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/SlotGridLayoutMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/SlotGridLayoutMixin.java index e4a9eff..e101c32 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/SlotGridLayoutMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/SlotGridLayoutMixin.java @@ -8,6 +8,7 @@ import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.lang.reflect.Field; @Mixin(SlotGridLayout.class) public abstract class SlotGridLayoutMixin { @@ -27,18 +28,54 @@ public abstract class SlotGridLayoutMixin { return; } - // 计算当前页码 - int currentPage = semanticIdx / SLOTS_PER_PAGE; - - // 计算在当前页中的位置 + // 读取实际当前页码:优先从 GUI accessor,其次反射容器,失败则为 0 + int currentPage = 0; + try { + if (screen instanceof com.extendedae_plus.api.ExPatternPageAccessor accessor) { + currentPage = accessor.eap$getCurrentPage(); + } else { + var menu = ((com.glodblock.github.extendedae.client.gui.GuiExPatternProvider) screen).getMenu(); + Field fieldPage = eap$findFieldRecursive(menu.getClass(), "page"); + if (fieldPage != null) { + fieldPage.setAccessible(true); + currentPage = (Integer) fieldPage.get(menu); + } + } + } catch (Throwable ignored) { + } + + // 该槽位属于第几页 + int slotPage = semanticIdx / SLOTS_PER_PAGE; + if (slotPage != currentPage) { + // 非当前页:将其移出视野,避免渲染与鼠标命中 + cir.setReturnValue(new Point(-10000, -10000)); + cir.cancel(); + return; + } + + // 当前页中的位置(0..35) int slotInPage = semanticIdx % SLOTS_PER_PAGE; int row = slotInPage / 9; // 0-3 int col = slotInPage % 9; // 0-8 - + // 计算目标位置(始终在前4行) int targetX = x + col * 18; int targetY = y + row * 18; - + cir.setReturnValue(new Point(targetX, targetY)); + cir.cancel(); } -} \ No newline at end of file + + @Unique + private static Field eap$findFieldRecursive(Class cls, String name) { + Class c = cls; + while (c != null && c != Object.class) { + try { + return c.getDeclaredField(name); + } catch (NoSuchFieldException ignored) {} + c = c.getSuperclass(); + } + return null; + } +} + \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/extendedae/GuiExPatternProviderMixin.java b/src/main/java/com/extendedae_plus/mixin/extendedae/GuiExPatternProviderMixin.java index 08c2960..882fe9e 100644 --- a/src/main/java/com/extendedae_plus/mixin/extendedae/GuiExPatternProviderMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/extendedae/GuiExPatternProviderMixin.java @@ -7,12 +7,12 @@ import appeng.client.gui.style.ScreenStyle; import appeng.menu.SlotSemantics; import com.extendedae_plus.NewIcon; import com.glodblock.github.extendedae.client.button.ActionEPPButton; +import com.extendedae_plus.api.ExPatternButtonsAccessor; +import com.extendedae_plus.config.ModConfigs; import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; import com.glodblock.github.extendedae.container.ContainerExPatternProvider; import com.glodblock.github.extendedae.network.EPPNetworkHandler; import com.glodblock.github.glodium.network.packet.CGenericPacket; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; @@ -25,9 +25,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.lang.reflect.Field; import java.util.List; +import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; @Mixin(GuiExPatternProvider.class) -public abstract class GuiExPatternProviderMixin extends PatternProviderScreen { +public abstract class GuiExPatternProviderMixin extends PatternProviderScreen implements ExPatternButtonsAccessor, com.extendedae_plus.api.ExPatternPageAccessor { @Unique ScreenStyle eap$screenStyle; @@ -41,224 +42,68 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen SLOTS_PER_PAGE) { - Font fontRenderer = Minecraft.getInstance().font; - - // 获取当前页码 - int currentPage = getCurrentPage(); - int maxPage = getMaxPage(); - - // 获取ae通用界面样式 - int color = eap$screenStyle.getColor(PaletteColor.DEFAULT_TEXT_COLOR).toARGB(); - // 调整页码显示位置:在"样板"文字的右边 - guiGraphics.drawString(font, Component.literal("第 " + (currentPage + 1) + "/" + maxPage + " 页"), - leftPos + 8 + 50, topPos + 30, color, false); - } - } - - @Unique - public void updateBeforeRender() { - super.updateBeforeRender(); - try { - ContainerExPatternProvider menu1 = this.getMenu(); - - // 调用showPage方法 - java.lang.reflect.Method showPageMethod = menu1.getClass().getMethod("showPage"); - showPageMethod.invoke(menu1); - - // 获取当前页码和最大页码 - Field fieldPage = menu1.getClass().getDeclaredField("page"); - fieldPage.setAccessible(true); - Integer page = (Integer) fieldPage.get(menu1); - - Field fieldMaxPage = menu1.getClass().getDeclaredField("maxPage"); - fieldMaxPage.setAccessible(true); - Integer maxPage = (Integer) fieldMaxPage.get(menu1); - - // 更新按钮可见性 - 始终显示,支持循环翻页 - if (nextPage != null && prevPage != null) { - this.nextPage.setVisibility(true); - this.prevPage.setVisibility(true); - } - if (x2Button != null) { - this.x2Button.setVisibility(true); - } - if (divideBy2Button != null) { - this.divideBy2Button.setVisibility(true); - } - if (x10Button != null) { - this.x10Button.setVisibility(true); - } - if (divideBy10Button != null) { - this.divideBy10Button.setVisibility(true); - } - - // 调整槽位位置 - this.eap$adjustSlotPositions(page); - } catch (Exception e) { - // 忽略反射错误 - } - - // 如果屏幕尺寸发生变化(窗口/GUI缩放),重新注册按钮,避免被 Screen.init 清空 - if (this.width != eap$lastScreenWidth || this.height != eap$lastScreenHeight) { - eap$lastScreenWidth = this.width; - eap$lastScreenHeight = this.height; - try { - if (this.divideBy2Button != null) { - this.removeWidget(this.divideBy2Button); - this.addRenderableWidget(this.divideBy2Button); - } - if (this.x2Button != null) { - this.removeWidget(this.x2Button); - this.addRenderableWidget(this.x2Button); - } - if (this.divideBy5Button != null) { - this.removeWidget(this.divideBy5Button); - this.addRenderableWidget(this.divideBy5Button); - } - if (this.x5Button != null) { - this.removeWidget(this.x5Button); - this.addRenderableWidget(this.x5Button); - } - if (this.divideBy10Button != null) { - this.removeWidget(this.divideBy10Button); - this.addRenderableWidget(this.divideBy10Button); - } - if (this.x10Button != null) { - this.removeWidget(this.x10Button); - this.addRenderableWidget(this.x10Button); - } - } catch (Throwable ignored) {} - } - - // 每帧定位四个按钮到 GUI 右缘外侧一点(使用绝对屏幕坐标) - int bx = this.leftPos + this.imageWidth + 1; // 向右平移 1px 到面板外侧 - int by = this.topPos + 20; - int spacing = 22; - if (this.divideBy2Button != null) { - this.divideBy2Button.setVisibility(true); - this.divideBy2Button.setX(bx); - this.divideBy2Button.setY(by); - } - if (this.x2Button != null) { - this.x2Button.setVisibility(true); - this.x2Button.setX(bx); - this.x2Button.setY(by + spacing); - } - if (this.divideBy10Button != null) { - this.divideBy10Button.setVisibility(true); - this.divideBy10Button.setX(bx); - this.divideBy10Button.setY(by + spacing * 4); - } - if (this.x10Button != null) { - this.x10Button.setVisibility(true); - this.x10Button.setX(bx); - this.x10Button.setY(by + spacing * 5); - } - // 新增 /5 与 x5 的定位(位于 /2、x2 之后) - if (this.divideBy5Button != null) { - this.divideBy5Button.setVisibility(true); - this.divideBy5Button.setX(bx); - this.divideBy5Button.setY(by + spacing * 2); - } - if (this.x5Button != null) { - this.x5Button.setVisibility(true); - this.x5Button.setX(bx); - this.x5Button.setY(by + spacing * 3); - } - } - @Unique - private void eap$adjustSlotPositions(int currentPage) { - try { - List slots = this.getMenu().getSlots(SlotSemantics.ENCODED_PATTERN); - int totalSlots = slots.size(); - - if (totalSlots <= SLOTS_PER_PAGE) { - return; // 不需要翻页 - } - - int slot_id = 0; - for (Slot s : slots) { - int page_id = slot_id / SLOTS_PER_PAGE; - - if (page_id == currentPage) { - // 当前页的槽位需要调整位置 - int slotInPage = slot_id % SLOTS_PER_PAGE; - int row = slotInPage / 9; // 0-3 - int col = slotInPage % 9; // 0-8 - - // 计算目标位置(始终在前4行) - int x = 8 + col * 18; - int y = 42 + row * 18; - - // 使用反射设置槽位位置,支持混淆环境 - Field xField = null; - Field yField = null; - - // 尝试不同的字段名(开发环境和生产环境可能不同) - String[] xFieldNames = {"x", "field_75262_c"}; - String[] yFieldNames = {"y", "field_75263_d"}; - - for (String fieldName : xFieldNames) { - try { - xField = Slot.class.getDeclaredField(fieldName); - xField.setAccessible(true); - break; - } catch (NoSuchFieldException ignored) {} - } - - for (String fieldName : yFieldNames) { - try { - yField = Slot.class.getDeclaredField(fieldName); - yField.setAccessible(true); - break; - } catch (NoSuchFieldException ignored) {} - } - - if (xField != null && yField != null) { - xField.set(s, x); - yField.set(s, y); - } - } - ++slot_id; - } - } catch (Exception e) { - // 忽略反射错误 - } - } + + // 移除手动挪动 Slot 坐标,交由 SlotGridLayout + 原生布局控制 @Unique private int getCurrentPage() { - try { - ContainerExPatternProvider menu1 = this.getMenu(); - Field fieldPage = menu1.getClass().getDeclaredField("page"); - fieldPage.setAccessible(true); - return (Integer) fieldPage.get(menu1); - } catch (Exception e) { - return 0; - } + // 优先使用本地 GUI 维护的页码 + return Math.max(0, eap$currentPage % Math.max(1, eap$maxPageLocal)); } @Unique private int getMaxPage() { + // 优先使用配置倍数 + try { + int cfg = ModConfigs.PAGE_MULTIPLIER.get(); + if (cfg > 1) return cfg; + } catch (Throwable ignored) {} try { ContainerExPatternProvider menu1 = this.getMenu(); - Field fieldMaxPage = menu1.getClass().getDeclaredField("maxPage"); - fieldMaxPage.setAccessible(true); - return (Integer) fieldMaxPage.get(menu1); - } catch (Exception e) { - return 1; + Field fieldMaxPage = eap$findFieldRecursive(menu1.getClass(), "maxPage"); + if (fieldMaxPage != null) { + fieldMaxPage.setAccessible(true); + Object v = fieldMaxPage.get(menu1); + if (v instanceof Integer i) return i; + } + } catch (Throwable ignored) {} + // 回退:用槽位总数计算 + try { + int totalSlots = this.getMenu().getSlots(SlotSemantics.ENCODED_PATTERN).size(); + return Math.max(1, (int) Math.ceil(totalSlots / (double) SLOTS_PER_PAGE)); + } catch (Throwable ignored) {} + return 1; + } + + @Unique + private static Field eap$findFieldRecursive(Class cls, String name) { + Class c = cls; + while (c != null && c != Object.class) { + try { + return c.getDeclaredField(name); + } catch (NoSuchFieldException ignored) {} + c = c.getSuperclass(); + } + return null; + } + + @Unique + private static void eap$setIntFieldRecursive(Object obj, String name, int value) { + if (obj == null) return; + Field f = eap$findFieldRecursive(obj.getClass(), name); + if (f != null) { + try { f.setAccessible(true); f.set(obj, value); } catch (Throwable ignored) {} } } @@ -277,29 +122,75 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen SLOTS_PER_PAGE) { + int cfgPages = 1; + try { cfgPages = Math.max(1, ModConfigs.PAGE_MULTIPLIER.get()); } catch (Throwable ignored) {} + int calcPages = Math.max(1, (int) Math.ceil(totalSlots / (double) SLOTS_PER_PAGE)); + int desiredMaxPage = Math.max(cfgPages, calcPages); + LOGGER.info("[EAP] GuiExPatternProvider init: totalSlots={}, cfgPages={}, calcPages={}, desiredMaxPage={}", totalSlots, cfgPages, calcPages, desiredMaxPage); + // 更新本地最大页 + this.eap$maxPageLocal = Math.max(1, desiredMaxPage); + this.eap$currentPage = 0; + try { + Field fMax = eap$findFieldRecursive(menu.getClass(), "maxPage"); + if (fMax != null) { fMax.setAccessible(true); fMax.set(menu, desiredMaxPage); } + } catch (Throwable ignored) {} + + // 翻页按钮(当存在多页时显示;支持仅由配置决定的“空白页”) + if (desiredMaxPage > 1) { this.prevPage = new ActionEPPButton((b) -> { int currentPage = getCurrentPage(); - int maxPage = getMaxPage(); + int maxPage = Math.max(this.eap$maxPageLocal, getMaxPage()); int newPage = (currentPage - 1 + maxPage) % maxPage; try { ContainerExPatternProvider menu1 = this.getMenu(); - java.lang.reflect.Method setPageMethod = menu1.getClass().getMethod("setPage", int.class); - setPageMethod.invoke(menu1, newPage); + // 尝试调用 setPage + try { + java.lang.reflect.Method setPageMethod = menu1.getClass().getMethod("setPage", int.class); + setPageMethod.invoke(menu1, newPage); + } catch (Throwable ignored2) {} + // 直接写入 page 字段,确保生效 + Field f = eap$findFieldRecursive(menu1.getClass(), "page"); + if (f != null) { + f.setAccessible(true); + f.set(menu1, newPage); + } } catch (Exception ignored) {} + // 同步到本地 GUI 页码 + this.eap$currentPage = newPage; + // 日志与强制重排(放在更新本地页码之后,确保布局读取到新页) + LOGGER.info("[EAP] PrevPage clicked: {} -> {} (max={})", currentPage, newPage, maxPage); + this.repositionSlots(SlotSemantics.ENCODED_PATTERN); + this.repositionSlots(SlotSemantics.STORAGE); + this.hoveredSlot = null; }, Icon.ARROW_LEFT); this.nextPage = new ActionEPPButton((b) -> { int currentPage = getCurrentPage(); - int maxPage = getMaxPage(); + int maxPage = Math.max(this.eap$maxPageLocal, getMaxPage()); int newPage = (currentPage + 1) % maxPage; try { ContainerExPatternProvider menu1 = this.getMenu(); - java.lang.reflect.Method setPageMethod = menu1.getClass().getMethod("setPage", int.class); - setPageMethod.invoke(menu1, newPage); + // 尝试调用 setPage + try { + java.lang.reflect.Method setPageMethod = menu1.getClass().getMethod("setPage", int.class); + setPageMethod.invoke(menu1, newPage); + } catch (Throwable ignored2) {} + // 直接写入 page 字段,确保生效 + Field f = eap$findFieldRecursive(menu1.getClass(), "page"); + if (f != null) { + f.setAccessible(true); + f.set(menu1, newPage); + } } catch (Exception ignored) {} + // 同步到本地 GUI 页码 + this.eap$currentPage = newPage; + // 日志与强制重排(放在更新本地页码之后,确保布局读取到新页) + LOGGER.info("[EAP] NextPage clicked: {} -> {} (max={})", currentPage, newPage, maxPage); + this.repositionSlots(SlotSemantics.ENCODED_PATTERN); + this.repositionSlots(SlotSemantics.STORAGE); + this.hoveredSlot = null; }, Icon.ARROW_RIGHT); this.addToLeftToolbar(this.nextPage); @@ -346,8 +237,101 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen