From 6a536b90d0047d47db77468cd3cfb9601914afad Mon Sep 17 00:00:00 2001 From: GaLicn <3096147684@qq.com> Date: Sat, 16 Aug 2025 11:29:27 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B8=8A=E4=BC=A0=E6=A0=B7?= =?UTF-8?q?=E6=9D=BF=E6=8C=89=E9=92=AE=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mixin/PatternEncodingTermScreenMixin.java | 125 ++++++++++++++---- .../mixin/accessor/AEBaseScreenAccessor.java | 14 ++ .../mixin/accessor/AEBaseScreenInvoker.java | 11 ++ .../AbstractContainerScreenAccessor.java | 15 +++ .../mixin/accessor/ScreenAccessor.java | 19 +++ .../mixin/accessor/ScreenInvoker.java | 14 ++ .../resources/extendedae_plus.mixins.json | 5 +- 7 files changed, 179 insertions(+), 24 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/mixin/accessor/AEBaseScreenAccessor.java create mode 100644 src/main/java/com/extendedae_plus/mixin/accessor/AEBaseScreenInvoker.java create mode 100644 src/main/java/com/extendedae_plus/mixin/accessor/AbstractContainerScreenAccessor.java create mode 100644 src/main/java/com/extendedae_plus/mixin/accessor/ScreenAccessor.java create mode 100644 src/main/java/com/extendedae_plus/mixin/accessor/ScreenInvoker.java diff --git a/src/main/java/com/extendedae_plus/mixin/PatternEncodingTermScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/PatternEncodingTermScreenMixin.java index 239e862..57b3da8 100644 --- a/src/main/java/com/extendedae_plus/mixin/PatternEncodingTermScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/PatternEncodingTermScreenMixin.java @@ -1,63 +1,142 @@ package com.extendedae_plus.mixin; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; 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.CallbackInfo; import net.minecraft.client.gui.components.Tooltip; -import net.minecraft.client.gui.components.Button; +import net.minecraft.client.renderer.Rect2i; import net.minecraft.network.chat.Component; -import net.minecraft.world.entity.player.Inventory; import appeng.client.gui.Icon; import appeng.client.gui.AEBaseScreen; import appeng.client.gui.me.items.PatternEncodingTermScreen; import appeng.client.gui.style.ScreenStyle; +import appeng.client.gui.style.WidgetStyle; import appeng.client.gui.widgets.IconButton; -import appeng.menu.me.items.PatternEncodingTermMenu; import appeng.menu.AEBaseMenu; import com.extendedae_plus.network.ModNetwork; -import com.extendedae_plus.network.UploadEncodedPatternToProviderC2SPacket; +import com.extendedae_plus.mixin.accessor.AEBaseScreenAccessor; +import com.extendedae_plus.mixin.accessor.AbstractContainerScreenAccessor; +import com.extendedae_plus.mixin.accessor.ScreenAccessor; /** - * 在图样编码终端右侧工具栏加入一个上传按钮: + * 在图样编码终端界面加入一个上传按钮: * 点击后把当前“已编码样板”上传到任意可用的样板供应器(服务端自动选择)。 + * 通过解析 AE2 样式中 encodePattern 的坐标,将按钮放在其左侧紧挨位置。 */ @Mixin(AEBaseScreen.class) public abstract class PatternEncodingTermScreenMixin { - // 使用 @Shadow 访问 AEBaseScreen 的受保护方法 - @Shadow - protected abstract B addToLeftToolbar(B button); - @Unique - private boolean extendedae_plus$uploadBtnAdded = false; + private IconButton extendedae_plus$uploadBtn; - @Inject(method = "init", at = @At("HEAD")) + @Inject(method = "init", at = @At("TAIL")) private void extendedae_plus$addUploadButton(CallbackInfo ci) { // 仅在图样编码终端界面中添加按钮 if (!(((Object) this) instanceof PatternEncodingTermScreen)) { return; } - // 幂等:避免每次 init() 都重复添加按钮 - if (extendedae_plus$uploadBtnAdded) { - return; - } - var uploadBtn = new IconButton(btn -> ModNetwork.CHANNEL - .sendToServer(new com.extendedae_plus.network.RequestProvidersListC2SPacket())) { + // 复用已存在的按钮实例,避免重复创建 + if (extendedae_plus$uploadBtn == null) { + extendedae_plus$uploadBtn = new IconButton(btn -> ModNetwork.CHANNEL + .sendToServer(new com.extendedae_plus.network.RequestProvidersListC2SPacket())) { @Override protected Icon getIcon() { return Icon.ARROW_UP; } - }; - uploadBtn.setTooltip(Tooltip.create(Component.translatable("extendedae_plus.button.choose_provider"))); + }; + extendedae_plus$uploadBtn.setTooltip(Tooltip.create(Component.translatable("extendedae_plus.button.choose_provider"))); + } - // 直接调用 @Shadow 方法,避免跨包 protected 访问问题 - this.addToLeftToolbar(uploadBtn); - extendedae_plus$uploadBtnAdded = true; + // 解析 encodePattern 的样式位置 + try { + ScreenStyle style = ((AEBaseScreenAccessor) (Object) this).extendedae_plus$getStyle(); + WidgetStyle ws = style.getWidget("encodePattern"); + int leftPos = ((AbstractContainerScreenAccessor) (Object) this).extendedae_plus$getLeftPos(); + int topPos = ((AbstractContainerScreenAccessor) (Object) this).extendedae_plus$getTopPos(); + int imageWidth = ((AbstractContainerScreenAccessor) (Object) this).extendedae_plus$getImageWidth(); + int imageHeight = ((AbstractContainerScreenAccessor) (Object) this).extendedae_plus$getImageHeight(); + Rect2i bounds = new Rect2i(leftPos, topPos, imageWidth, imageHeight); + var pos = ws.resolve(bounds); + int targetW = ws.getWidth() > 0 ? ws.getWidth() : 18; + int targetH = ws.getHeight() > 0 ? ws.getHeight() : 18; + // 尺寸与 encodePattern 一致 + extendedae_plus$uploadBtn.setWidth(targetW); + extendedae_plus$uploadBtn.setHeight(targetH); + // 放在其左侧紧挨(预留 2px 间距) + extendedae_plus$uploadBtn.setX(pos.getX() - targetW - 2); + extendedae_plus$uploadBtn.setY(pos.getY()); + } catch (Throwable t) { + // 回退:放在界面右侧大致位置,避免不可见 + extendedae_plus$uploadBtn.setWidth(18); + extendedae_plus$uploadBtn.setHeight(18); + int leftPos = ((AbstractContainerScreenAccessor) (Object) this).extendedae_plus$getLeftPos(); + int topPos = ((AbstractContainerScreenAccessor) (Object) this).extendedae_plus$getTopPos(); + int imageWidth = ((AbstractContainerScreenAccessor) (Object) this).extendedae_plus$getImageWidth(); + extendedae_plus$uploadBtn.setX(leftPos + imageWidth - 18 - 8); + extendedae_plus$uploadBtn.setY(topPos + 88); + } + + // 直接向 renderables / children 列表添加,避免依赖受保护方法 + var accessor = (ScreenAccessor) (Object) this; + var renderables = accessor.extendedae_plus$getRenderables(); + var children = accessor.extendedae_plus$getChildren(); + if (!renderables.contains(extendedae_plus$uploadBtn)) { + renderables.add(extendedae_plus$uploadBtn); + } + if (!children.contains(extendedae_plus$uploadBtn)) { + children.add(extendedae_plus$uploadBtn); + } + } + + @Inject(method = "containerTick", at = @At("TAIL")) + private void extendedae_plus$ensureUploadButton(CallbackInfo ci) { + if (!(((Object) this) instanceof PatternEncodingTermScreen)) { + return; + } + if (extendedae_plus$uploadBtn == null) { + return; + } + var renderables2 = ((ScreenAccessor) (Object) this).extendedae_plus$getRenderables(); + if (!renderables2.contains(extendedae_plus$uploadBtn)) { + // 被其它模组清空/替换后,重新计算一次位置并补回 + try { + ScreenStyle style = ((AEBaseScreenAccessor) (Object) this).extendedae_plus$getStyle(); + WidgetStyle ws = style.getWidget("encodePattern"); + int leftPos = ((AbstractContainerScreenAccessor) (Object) this).extendedae_plus$getLeftPos(); + int topPos = ((AbstractContainerScreenAccessor) (Object) this).extendedae_plus$getTopPos(); + int imageWidth = ((AbstractContainerScreenAccessor) (Object) this).extendedae_plus$getImageWidth(); + int imageHeight = ((AbstractContainerScreenAccessor) (Object) this).extendedae_plus$getImageHeight(); + Rect2i bounds = new Rect2i(leftPos, topPos, imageWidth, imageHeight); + var pos = ws.resolve(bounds); + int targetW = ws.getWidth() > 0 ? ws.getWidth() : 18; + int targetH = ws.getHeight() > 0 ? ws.getHeight() : 18; + extendedae_plus$uploadBtn.setWidth(targetW); + extendedae_plus$uploadBtn.setHeight(targetH); + extendedae_plus$uploadBtn.setX(pos.getX() - targetW - 2); + extendedae_plus$uploadBtn.setY(pos.getY()); + } catch (Throwable t) { + int leftPos = ((AbstractContainerScreenAccessor) (Object) this).extendedae_plus$getLeftPos(); + int topPos = ((AbstractContainerScreenAccessor) (Object) this).extendedae_plus$getTopPos(); + int imageWidth = ((AbstractContainerScreenAccessor) (Object) this).extendedae_plus$getImageWidth(); + extendedae_plus$uploadBtn.setWidth(18); + extendedae_plus$uploadBtn.setHeight(18); + extendedae_plus$uploadBtn.setX(leftPos + imageWidth - 18 - 8); + extendedae_plus$uploadBtn.setY(topPos + 88); + } + var accessor2 = (ScreenAccessor) (Object) this; + var r = accessor2.extendedae_plus$getRenderables(); + var c = accessor2.extendedae_plus$getChildren(); + if (!r.contains(extendedae_plus$uploadBtn)) { + r.add(extendedae_plus$uploadBtn); + } + if (!c.contains(extendedae_plus$uploadBtn)) { + c.add(extendedae_plus$uploadBtn); + } + } } } diff --git a/src/main/java/com/extendedae_plus/mixin/accessor/AEBaseScreenAccessor.java b/src/main/java/com/extendedae_plus/mixin/accessor/AEBaseScreenAccessor.java new file mode 100644 index 0000000..bdcad30 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/accessor/AEBaseScreenAccessor.java @@ -0,0 +1,14 @@ +package com.extendedae_plus.mixin.accessor; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import appeng.client.gui.AEBaseScreen; +import appeng.client.gui.style.ScreenStyle; +import appeng.menu.AEBaseMenu; + +@Mixin(value = AEBaseScreen.class, remap = false) +public interface AEBaseScreenAccessor { + @Accessor(value = "style", remap = false) + ScreenStyle extendedae_plus$getStyle(); +} diff --git a/src/main/java/com/extendedae_plus/mixin/accessor/AEBaseScreenInvoker.java b/src/main/java/com/extendedae_plus/mixin/accessor/AEBaseScreenInvoker.java new file mode 100644 index 0000000..46317d3 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/accessor/AEBaseScreenInvoker.java @@ -0,0 +1,11 @@ +package com.extendedae_plus.mixin.accessor; + +import org.spongepowered.asm.mixin.Mixin; + +import appeng.client.gui.AEBaseScreen; +import appeng.menu.AEBaseMenu; + +@Mixin(AEBaseScreen.class) +public interface AEBaseScreenInvoker { + // 空接口:避免在 AEBaseScreen 上声明不存在方法的 Invoker 导致编译错误 +} diff --git a/src/main/java/com/extendedae_plus/mixin/accessor/AbstractContainerScreenAccessor.java b/src/main/java/com/extendedae_plus/mixin/accessor/AbstractContainerScreenAccessor.java new file mode 100644 index 0000000..9e46c7e --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/accessor/AbstractContainerScreenAccessor.java @@ -0,0 +1,15 @@ +package com.extendedae_plus.mixin.accessor; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.world.inventory.AbstractContainerMenu; + +@Mixin(AbstractContainerScreen.class) +public interface AbstractContainerScreenAccessor { + @Accessor("leftPos") int extendedae_plus$getLeftPos(); + @Accessor("topPos") int extendedae_plus$getTopPos(); + @Accessor("imageWidth") int extendedae_plus$getImageWidth(); + @Accessor("imageHeight") int extendedae_plus$getImageHeight(); +} diff --git a/src/main/java/com/extendedae_plus/mixin/accessor/ScreenAccessor.java b/src/main/java/com/extendedae_plus/mixin/accessor/ScreenAccessor.java new file mode 100644 index 0000000..c3e6b90 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/accessor/ScreenAccessor.java @@ -0,0 +1,19 @@ +package com.extendedae_plus.mixin.accessor; + +import java.util.List; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.client.gui.components.Renderable; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; + +@Mixin(Screen.class) +public interface ScreenAccessor { + @Accessor("renderables") + List extendedae_plus$getRenderables(); + + @Accessor("children") + List extendedae_plus$getChildren(); +} diff --git a/src/main/java/com/extendedae_plus/mixin/accessor/ScreenInvoker.java b/src/main/java/com/extendedae_plus/mixin/accessor/ScreenInvoker.java new file mode 100644 index 0000000..ee64dfe --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/accessor/ScreenInvoker.java @@ -0,0 +1,14 @@ +package com.extendedae_plus.mixin.accessor; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import net.minecraft.client.gui.components.Renderable; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; + +@Mixin(Screen.class) +public interface ScreenInvoker { + @Invoker("addRenderableWidget") + W extendedae_plus$invokeAddRenderableWidget(W widget); +} diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index fc58dbc..6c8c556 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -10,7 +10,10 @@ "HighlightButtonMixin", "PickFromWirelessMixin", "PatternEncodingTermScreenMixin", - "jei.EncodePatternTransferHandlerMixin" + "jei.EncodePatternTransferHandlerMixin", + "accessor.AEBaseScreenAccessor", + "accessor.AbstractContainerScreenAccessor", + "accessor.ScreenAccessor" ], "mixins": [ "ContainerExPatternProviderMixin",