diff --git a/src/main/java/com/extendedae_plus/api/IExPatternButtonsAccessor.java b/src/main/java/com/extendedae_plus/api/IExPatternButton.java similarity index 87% rename from src/main/java/com/extendedae_plus/api/IExPatternButtonsAccessor.java rename to src/main/java/com/extendedae_plus/api/IExPatternButton.java index ac4d1ec..f71052a 100644 --- a/src/main/java/com/extendedae_plus/api/IExPatternButtonsAccessor.java +++ b/src/main/java/com/extendedae_plus/api/IExPatternButton.java @@ -3,7 +3,7 @@ package com.extendedae_plus.api; /** * 由 {@code GuiExPatternProviderMixin} 实现,用于从通用的 Screen Mixin 中更新按钮布局。 */ -public interface IExPatternButtonsAccessor { +public interface IExPatternButton { /** * 在每帧调用以维护扩展样板供应器右侧按钮的可见性、重注册(窗口尺寸变化)与定位。 */ diff --git a/src/main/java/com/extendedae_plus/api/IExPatternPageAccessor.java b/src/main/java/com/extendedae_plus/api/IExPatternPage.java similarity index 75% rename from src/main/java/com/extendedae_plus/api/IExPatternPageAccessor.java rename to src/main/java/com/extendedae_plus/api/IExPatternPage.java index c368e26..cf89eb5 100644 --- a/src/main/java/com/extendedae_plus/api/IExPatternPageAccessor.java +++ b/src/main/java/com/extendedae_plus/api/IExPatternPage.java @@ -3,6 +3,8 @@ package com.extendedae_plus.api; /** * 由 GuiExPatternProviderMixin 实现,用于在客户端侧提供当前页号,避免反射读取 AE2 内部字段失败。 */ -public interface IExPatternPageAccessor { +public interface IExPatternPage { int eap$getCurrentPage(); + + int eap$getMaxPageLocal(); } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java index cb16304..8b36b32 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java @@ -11,9 +11,8 @@ import appeng.client.gui.me.crafting.CraftingCPUScreen; import appeng.client.gui.style.PaletteColor; import appeng.client.gui.style.ScreenStyle; import appeng.client.gui.style.Text; -import appeng.client.gui.style.TextAlignment; import appeng.menu.slot.AppEngSlot; -import com.extendedae_plus.api.IExPatternPageAccessor; +import com.extendedae_plus.api.IExPatternPage; import com.extendedae_plus.content.ClientPatternHighlightStore; import com.extendedae_plus.init.ModNetwork; import com.extendedae_plus.mixin.accessor.AbstractContainerScreenAccessor; @@ -26,6 +25,7 @@ import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.Rect2i; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.contents.TranslatableContents; @@ -40,63 +40,45 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(AEBaseScreen.class) public abstract class AEBaseScreenMixin { - /** - * 在 AEBaseScreen 的 mouseClicked 入口拦截 CraftingCPUScreen 的 Shift+左键, - * 读取鼠标下的 AEKey 并发送 CraftingMonitorJumpC2SPacket。 + * 在 AEBaseScreen 的 mouseClicked 入口拦截 CraftingCPUScreen 的 Shift+点击操作。 + * 左键:发送 CraftingMonitorJumpC2SPacket(跳转至样板所在界面)。 + * 右键:发送 CraftingMonitorOpenProviderC2SPacket(打开样板供应器UI)。 */ @Inject(method = "mouseClicked", at = @At("HEAD"), cancellable = true) - private void eap$craftingCpuShiftLeftClick(double mouseX, double mouseY, int button, CallbackInfoReturnable cir) { + private void eap$craftingCpuShiftClick(double mouseX, double mouseY, int button, CallbackInfoReturnable cir) { // 仅处理 CraftingCPUScreen 实例 Object self = this; if (!(self instanceof CraftingCPUScreen screen)) { return; } - // 仅在 Shift + 左键 时触发 - if (button != 0 || !net.minecraft.client.gui.screens.Screen.hasShiftDown()) { - return; - } - try { - StackWithBounds hovered = screen.getStackUnderMouse(mouseX, mouseY); - if (hovered == null || hovered.stack() == null) { - return; - } - AEKey key = hovered.stack().what(); - if (key == null) { - return; - } - ModNetwork.CHANNEL.sendToServer(new CraftingMonitorJumpC2SPacket(key)); - cir.setReturnValue(true); - } catch (Throwable ignored) {} - } - /** - * 在 AEBaseScreen 的 mouseClicked 入口拦截 CraftingCPUScreen 的 Shift+右键, - * 读取鼠标下的 AEKey 并发送 CraftingMonitorOpenProviderC2SPacket(打开样板供应器UI)。 - */ - @Inject(method = "mouseClicked", at = @At("HEAD"), cancellable = true) - private void eap$craftingCpuShiftRightClick(double mouseX, double mouseY, int button, CallbackInfoReturnable cir) { - // 仅处理 CraftingCPUScreen 实例 - Object self = this; - if (!(self instanceof CraftingCPUScreen screen)) { - return; - } - // 仅在 Shift + 右键 时触发 - if (button != 1 || !net.minecraft.client.gui.screens.Screen.hasShiftDown()) { + // 仅在按下 Shift 且为左右键时触发 + if (!Screen.hasShiftDown() || (button != 0 && button != 1)) { return; } + try { StackWithBounds hovered = screen.getStackUnderMouse(mouseX, mouseY); if (hovered == null || hovered.stack() == null) { return; } + AEKey key = hovered.stack().what(); if (key == null) { return; } - ModNetwork.CHANNEL.sendToServer(new CraftingMonitorOpenProviderC2SPacket(key)); + + // 左键发送跳转包,右键发送打开供应器包 + if (button == 0) { + ModNetwork.CHANNEL.sendToServer(new CraftingMonitorJumpC2SPacket(key)); + } else { + ModNetwork.CHANNEL.sendToServer(new CraftingMonitorOpenProviderC2SPacket(key)); + } + cir.setReturnValue(true); - } catch (Throwable ignored) {} + } catch (Throwable ignored) { + } } /** @@ -138,11 +120,14 @@ public abstract class AEBaseScreenMixin { if (key != null && ClientPatternHighlightStore.hasHighlight(key)) { try { GuiUtil.drawSlotRainbowHighlight(guiGraphics, s.x, s.y); - } catch (Throwable ignored) {} + } catch (Throwable ignored) { + } } } - } catch (Throwable ignore) {} - } catch (Throwable ignore) {} + } catch (Throwable ignore) { + } + } catch (Throwable ignore) { + } } // 在 AEBaseScreen.drawText 完成某个文本绘制后,若该文本为“样板”标签,则紧接着绘制页码。 @@ -152,121 +137,64 @@ public abstract class AEBaseScreenMixin { @Nullable TextOverride override, CallbackInfo ci) { Object self = this; - if (!(self instanceof GuiExPatternProvider)) { + if (!(self instanceof GuiExPatternProvider)) return; + + // 判断是否是“样板”标题 + Component content = text.getText(); + if (!"gui.ae2.Patterns".equals(content.getContents() instanceof TranslatableContents tc ? tc.getKey() : null)) { return; } try { - // 解析最终用于显示的标签内容 - Component content = text.getText(); - if (override != null && override.getContent() != null) { - content = override.getContent().copy().withStyle(content.getStyle()); - } - - // 计算“样板”文本起点与宽度,按对齐方式与缩放修正 x/y - int imageWidth = ((AbstractContainerScreenAccessor) this).eap$getImageWidth(); - int imageHeight = ((AbstractContainerScreenAccessor) this).eap$getImageHeight(); - - Rect2i bounds = new Rect2i(0, 0, imageWidth, imageHeight); - Point pos = text.getPosition().resolve(bounds); - - float scale = text.getScale(); - - Font font = ((ScreenAccessor) this).eap$getFont(); - // 只关心第一行(标题类文本无换行或 maxWidth<=0) - var contentLine = (text.getMaxWidth() <= 0) - ? content.getVisualOrderText() - : font.split(content, text.getMaxWidth()).get(0); - int lineWidth = font.width(contentLine); - - int x = pos.getX(); - int y = pos.getY(); - // 对齐修正 - var align = text.getAlign(); - if (align == TextAlignment.CENTER) { - int textPx = Math.round(lineWidth * scale); - x -= textPx / 2; - } else if (align == TextAlignment.RIGHT) { - int textPx = Math.round(lineWidth * scale); - x -= textPx; - } - - // 判断是否为“样板”组标题(多语言兼容且避免标题) - boolean isPatterns = false; - // 1) 基于翻译键 - var contents = content.getContents(); - if (contents instanceof TranslatableContents tc) { - String key = tc.getKey(); - if (key != null && key.endsWith(".patterns")) { - isPatterns = true; - } - } - // 2) 基于已知本地化键的字符串解析 - if (!isPatterns) { - String label = content.getString(); - if (label != null) { - if (label.equals(Component.translatable("gui.pattern_provider.patterns").getString())) - isPatterns = true; - else if (label.equals(Component.translatable("gui.extendedae.patterns").getString())) - isPatterns = true; - else if (label.equals(Component.translatable("gui.ae2.patterns").getString())) isPatterns = true; - } - } - // 3) 容错:中文“样板”且在标题下方(放宽到 y>=14)或文本正好等于“样板” - if (!isPatterns) { - String s = content.getString(); - if (s != null && ("样板".equals(s) || (s.contains("样板") && y >= 14))) { - isPatterns = true; - } - } - if (!isPatterns) return; - + // ---- 获取页码 ---- int cur = 1; int max = 1; - if (self instanceof IExPatternPageAccessor accessor) { + if (self instanceof IExPatternPage accessor) { cur = Math.max(0, accessor.eap$getCurrentPage()) + 1; - } - try { - var fMax = self.getClass().getDeclaredField("eap$maxPageLocal"); - fMax.setAccessible(true); - Object v = fMax.get(self); - if (v instanceof Integer i) { - max = Math.max(1, i); - } - } catch (Throwable ignored) { + max = Math.max(max, accessor.eap$getMaxPageLocal()); } - String pageText = "第" + cur + "页" + "/" + max + "页"; + // ---- 构造翻译文本 ---- + Component pageText = Component.translatable("gui.extendedae.pattern_page", cur, max); - ScreenStyle style = ((AEBaseScreenAccessor) this).eap$getStyle(); + // ---- 计算绘制坐标 ---- + AbstractContainerScreenAccessor screen = (AbstractContainerScreenAccessor) this; + int imageWidth = screen.eap$getImageWidth(); + int imageHeight = screen.eap$getImageHeight(); + Point pos = text.getPosition().resolve( + new Rect2i(0, 0, imageWidth, imageHeight) + ); + + Font font = ((ScreenAccessor) this).eap$getFont(); + float scale = text.getScale(); + int lineWidth = font.width(content.getVisualOrderText()); + + int x = pos.getX() + lineWidth + 4; // 右侧偏移4像素 + int y = pos.getY(); + + // ---- 绘制 ---- int color = 0xFFFFFFFF; + ScreenStyle style = ((AEBaseScreenAccessor) this).eap$getStyle(); if (style != null) { - try { - color = style.getColor(PaletteColor.DEFAULT_TEXT_COLOR).toARGB(); - } catch (Throwable ignored) { - } - } - int padding = 4; - if (scale == 1.0f) { - guiGraphics.drawString(font, pageText, x + lineWidth + padding, y, color, false); - } else { - guiGraphics.pose().pushPose(); - guiGraphics.pose().translate(x, y, 1); - guiGraphics.pose().scale(scale, scale, 1); - guiGraphics.drawString(font, pageText, lineWidth + padding, 0, color, false); - guiGraphics.pose().popPose(); + color = style.getColor(PaletteColor.DEFAULT_TEXT_COLOR).toARGB(); } + + guiGraphics.pose().pushPose(); + guiGraphics.pose().translate(x, y, 1); + if (scale != 1.0f) guiGraphics.pose().scale(scale, scale, 1); + guiGraphics.drawString(font, pageText, 0, 0, color, false); + guiGraphics.pose().popPose(); } catch (Throwable ignored) {} } @Shadow(remap = false) - protected void setTextContent(String id, Component content) {}; + protected void setTextContent(String id, Component content) {} @Inject(method = "updateBeforeRender", at = @At("RETURN"), remap = false) private void onUpdateBeforeRender(CallbackInfo ci) { try { AEBaseScreen self = (AEBaseScreen) (Object) this; - if (self instanceof PatternProviderScreen screen){ + if (self instanceof PatternProviderScreen screen) { Component t = screen.getTitle(); if (t != null && !t.getString().isEmpty()) { this.setTextContent(AEBaseScreen.TEXT_ID_DIALOG_TITLE, t); diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenMixin.java index f670b38..d68870d 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenMixin.java @@ -6,7 +6,7 @@ import appeng.client.gui.implementations.PatternProviderScreen; import appeng.client.gui.style.ScreenStyle; import appeng.client.gui.widgets.SettingToggleButton; import appeng.menu.implementations.PatternProviderMenu; -import com.extendedae_plus.api.IExPatternButtonsAccessor; +import com.extendedae_plus.api.IExPatternButton; import com.extendedae_plus.api.IPatternProviderMenuAdvancedSync; import com.extendedae_plus.api.smartDoubling.IPatternProviderMenuDoublingSync; import com.extendedae_plus.init.ModNetwork; @@ -204,7 +204,7 @@ public abstract class PatternProviderScreenMixin private void eap$updateExGuiLayout() { if ((Object) this instanceof GuiExPatternProvider) { try { - ((IExPatternButtonsAccessor) this).eap$updateButtonsLayout(); + ((IExPatternButton) this).eap$updateButtonsLayout(); } catch (Throwable t) { EAP$LOGGER.debug("[EAP] updateButtonsLayout skipped: {}", t.toString()); } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/SlotGridLayoutMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/SlotGridLayoutMixin.java index b40d099..f744c99 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/SlotGridLayoutMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/SlotGridLayoutMixin.java @@ -2,52 +2,31 @@ package com.extendedae_plus.mixin.ae2.client.gui; import appeng.client.Point; import appeng.client.gui.layout.SlotGridLayout; -import com.extendedae_plus.api.IExPatternPageAccessor; +import com.extendedae_plus.api.IExPatternPage; +import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Mixin; -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 { - - @Unique - private static final int SLOTS_PER_PAGE = 36; - @Inject(method = "getRowBreakPosition", at = @At("HEAD"), cancellable = true, remap = false) private static void onGetRowBreakPosition(int x, int y, int semanticIdx, int cols, CallbackInfoReturnable cir) { - // 仅在 9 列布局 且 当前屏幕为 扩展样板供应器 时处理 - if (cols != 9) { - return; - } + // 仅在 9 列布局 且 当前屏幕为 扩展样板供应器时处理 + if (cols != 9) return; var screen = Minecraft.getInstance().screen; - if (!(screen instanceof com.glodblock.github.extendedae.client.gui.GuiExPatternProvider)) { - return; - } + if (!(screen instanceof GuiExPatternProvider gui)) return; - // 读取实际当前页码:优先从 GUI accessor,其次反射容器,失败则为 0 - int currentPage = 0; - try { - if (screen instanceof IExPatternPageAccessor 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 currentPage = (gui instanceof IExPatternPage accessor) ? + accessor.eap$getCurrentPage() : + 0; // 该槽位属于第几页 - int slotPage = semanticIdx / SLOTS_PER_PAGE; + int slotPage = semanticIdx / 36; if (slotPage != currentPage) { // 非当前页:将其移出视野,避免渲染与鼠标命中 cir.setReturnValue(new Point(-10000, -10000)); @@ -56,7 +35,7 @@ public abstract class SlotGridLayoutMixin { } // 当前页中的位置(0..35) - int slotInPage = semanticIdx % SLOTS_PER_PAGE; + int slotInPage = semanticIdx % 36; int row = slotInPage / 9; // 0-3 int col = slotInPage % 9; // 0-8 @@ -67,17 +46,5 @@ public abstract class SlotGridLayoutMixin { cir.setReturnValue(new Point(targetX, targetY)); cir.cancel(); } - - @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/client/gui/GuiExPatternProviderMixin.java b/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternProviderMixin.java index 1b27516..429db47 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 @@ -5,8 +5,8 @@ import appeng.client.gui.implementations.PatternProviderScreen; import appeng.client.gui.style.ScreenStyle; import appeng.menu.SlotSemantics; import com.extendedae_plus.ae.client.gui.NewIcon; -import com.extendedae_plus.api.IExPatternButtonsAccessor; -import com.extendedae_plus.api.IExPatternPageAccessor; +import com.extendedae_plus.api.IExPatternButton; +import com.extendedae_plus.api.IExPatternPage; import com.extendedae_plus.config.ModConfig; import com.glodblock.github.extendedae.client.button.ActionEPPButton; import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; @@ -24,10 +24,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.lang.reflect.Field; import java.lang.reflect.Method; -import static com.extendedae_plus.util.Logger.EAP$LOGGER; - @Mixin(GuiExPatternProvider.class) -public abstract class GuiExPatternProviderMixin extends PatternProviderScreen implements IExPatternButtonsAccessor, IExPatternPageAccessor { +public abstract class GuiExPatternProviderMixin extends PatternProviderScreen implements IExPatternButton, IExPatternPage { @Unique ScreenStyle eap$screenStyle; @@ -239,6 +237,11 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen