初步替换GuiExPatternTerminalMixin原有冗杂的反射调用

This commit is contained in:
C-H716 2025-10-31 17:46:41 +08:00
parent f8cc6fe5aa
commit e2ff23f107
5 changed files with 106 additions and 302 deletions

View File

@ -1,27 +1,15 @@
package com.extendedae_plus.mixin.extendedae.accessor; package com.extendedae_plus.mixin.extendedae.accessor;
import appeng.client.gui.widgets.AETextField; import appeng.client.gui.widgets.AETextField;
import appeng.client.gui.widgets.Scrollbar;
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal; import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.ArrayList;
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
@Mixin(value = GuiExPatternTerminal.class, remap = false) @Mixin(value = GuiExPatternTerminal.class, remap = false)
public interface GuiExPatternTerminalAccessor { public interface GuiExPatternTerminalAccessor {
@Accessor("scrollbar")
Scrollbar getScrollbar();
@Accessor("visibleRows")
int getVisibleRows();
@Accessor("rows")
ArrayList<?> getRows();
@Accessor("searchOutField") @Accessor("searchOutField")
AETextField getSearchOutField(); AETextField getSearchOutField();
} }

View File

@ -0,0 +1,14 @@
package com.extendedae_plus.mixin.extendedae.accessor;
import appeng.api.implementations.blockentities.PatternContainerGroup;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@OnlyIn(Dist.CLIENT)
@Mixin(targets = "com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal$GroupHeaderRow", remap = false)
public interface GuiExPatternTerminalGroupHeaderRowAccessor {
@Invoker("group")
PatternContainerGroup Group();
}

View File

@ -1,20 +0,0 @@
package com.extendedae_plus.mixin.extendedae.accessor;
import appeng.client.gui.me.patternaccess.PatternContainerRecord;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@OnlyIn(Dist.CLIENT)
@Mixin(targets = "com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal$SlotsRow", remap = false)
public interface GuiExPatternTerminalSlotsRowAccessor {
@Accessor("container")
PatternContainerRecord getContainer();
@Accessor("offset")
int getOffset();
@Accessor("slots")
int getSlots();
}

View File

@ -1,24 +1,29 @@
package com.extendedae_plus.mixin.extendedae.client.gui; package com.extendedae_plus.mixin.extendedae.client.gui;
import appeng.api.crafting.PatternDetailsHelper; import appeng.api.crafting.PatternDetailsHelper;
import appeng.api.implementations.blockentities.PatternContainerGroup;
import appeng.client.gui.AEBaseScreen; import appeng.client.gui.AEBaseScreen;
import appeng.client.gui.Icon; import appeng.client.gui.Icon;
import appeng.client.gui.me.patternaccess.PatternContainerRecord; import appeng.client.gui.me.patternaccess.PatternContainerRecord;
import appeng.client.gui.style.ScreenStyle; import appeng.client.gui.style.ScreenStyle;
import appeng.client.gui.widgets.AETextField; import appeng.client.gui.widgets.AETextField;
import appeng.client.gui.widgets.IconButton; import appeng.client.gui.widgets.IconButton;
import appeng.client.gui.widgets.Scrollbar;
import appeng.menu.AEBaseMenu; import appeng.menu.AEBaseMenu;
import com.extendedae_plus.config.ModConfig; import com.extendedae_plus.config.ModConfig;
import com.extendedae_plus.init.ModNetwork; import com.extendedae_plus.init.ModNetwork;
import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalAccessor; import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalGroupHeaderRowAccessor;
import com.extendedae_plus.network.provider.OpenProviderUiC2SPacket; import com.extendedae_plus.network.provider.OpenProviderUiC2SPacket;
import com.extendedae_plus.util.GuiUtil; import com.extendedae_plus.util.GuiUtil;
import com.glodblock.github.extendedae.client.button.HighlightButton;
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal; import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
import com.glodblock.github.extendedae.network.EPPNetworkHandler;
import com.glodblock.github.glodium.network.packet.CGenericPacket;
import com.google.common.collect.HashMultimap;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.Tooltip; import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
@ -28,55 +33,44 @@ import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.Slot; import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import org.apache.logging.log4j.LogManager; import org.spongepowered.asm.mixin.*;
import org.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
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.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.lang.reflect.Constructor; import java.util.ArrayList;
import java.lang.reflect.Method; import java.util.HashMap;
import java.util.*; import java.util.Map;
import java.util.Set;
@Pseudo @Pseudo
@Mixin(value = GuiExPatternTerminal.class) @Mixin(value = GuiExPatternTerminal.class)
public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu> { public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu> {
@Shadow(remap = false) @Final private static int GUI_PADDING_X;
@Unique @Shadow(remap = false) @Final private static int GUI_PADDING_Y;
private static final String UPLOAD_SUCCESS_MESSAGE = "✅ ExtendedAE Plus: 样板快速上传成功!"; @Shadow(remap = false) @Final private static int GUI_HEADER_HEIGHT;
@Unique @Shadow(remap = false) @Final private static int ROW_HEIGHT;
private static final String UPLOAD_FAILED_MESSAGE = "❌ ExtendedAE Plus: 样板上传失败,请检查供应器状态"; @Shadow(remap = false) @Final private static int TEXT_MAX_WIDTH;
@Unique @Unique private final Map<Integer, Button> eap$openUIButtons = new HashMap<>();
private static final String NO_PROVIDER_MESSAGE = "ExtendedAE Plus: 请先选择一个样板供应器点击GroupHeader旁的按钮"; @Unique private IconButton eap$toggleSlotsButton;
@Unique @Unique private boolean eap$showSlots = false; // 默认由配置初始化
private IconButton eap$toggleSlotsButton; @Unique private long eap$currentlyChoicePatterProvider = -1; // 当前选择的样板供应器ID
@Unique @Shadow(remap = false) @Final private AETextField searchOutField;
private boolean eap$showSlots = false; // 默认由配置初始化 @Shadow(remap = false) @Final private AETextField searchInField;
@Unique @Shadow(remap = false) @Final private Set<ItemStack> matchedStack;
private long eap$currentlyChoicePatterProvider = -1; // 当前选择的样板供应器ID @Shadow(remap = false) @Final private Set<PatternContainerRecord> matchedProvider;
@Unique @Shadow(remap = false) @Final private HashMultimap<PatternContainerGroup, PatternContainerRecord> byGroup;
private final Map<Integer, Button> eap$openUIButtons = new HashMap<>(); @Shadow(remap = false) @Final private HashMap<Long, GuiExPatternTerminal.PatternProviderInfo> infoMap;
@Shadow(remap = false) @Final private Scrollbar scrollbar;
@Unique @Shadow(remap = false) @Final private ArrayList<?> rows;
private static final Logger EAP_LOGGER = LogManager.getLogger("ExtendedAE_Plus"); @Shadow(remap = false) private int visibleRows;
@Shadow(remap = false) @Final private HashMap<Integer, HighlightButton> highlightBtns;
@Unique
private boolean eap$debugLoggedOnce = false;
@Shadow(remap = false) private AETextField searchOutField;
@Shadow(remap = false) private AETextField searchInField;
@Shadow(remap = false) private Set<ItemStack> matchedStack;
@Shadow(remap = false) private Set<PatternContainerRecord> matchedProvider;
public GuiExPatternTerminalMixin(AEBaseMenu menu, Inventory playerInventory, Component title, ScreenStyle style) { public GuiExPatternTerminalMixin(AEBaseMenu menu, Inventory playerInventory, Component title, ScreenStyle style) {
super(menu, playerInventory, title, style); super(menu, playerInventory, title, style);
} }
/** /**
* 获取当前选择的样板供应器ID * 获取当前选择的样板供应器ID
*/ */
@ -138,27 +132,8 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
if (this.minecraft.player != null) { if (this.minecraft.player != null) {
// 获取要上传的物品 // 获取要上传的物品
ItemStack itemToUpload = this.minecraft.player.getInventory().getItem(playerSlotIndex); ItemStack itemToUpload = this.minecraft.player.getInventory().getItem(playerSlotIndex);
if (!itemToUpload.isEmpty() && PatternDetailsHelper.isEncodedPattern(itemToUpload)) { if (!itemToUpload.isEmpty() && PatternDetailsHelper.isEncodedPattern(itemToUpload)) {
// 通过反射调用 ExtendedAE 的网络发送软依赖 EPPNetworkHandler.INSTANCE.sendToServer(new CGenericPacket("upload", playerSlotIndex, eap$currentlyChoicePatterProvider));
try {
Class<?> EPPNetworkHandlerClass = Class.forName("com.glodblock.github.extendedae.network.EPPNetworkHandler");
Object handlerInstance = EPPNetworkHandlerClass.getField("INSTANCE").get(null);
Class<?> packetClass = Class.forName("com.glodblock.github.glodium.network.packet.CGenericPacket");
Constructor<?> constructor = packetClass.getConstructor(String.class, Object[].class);
Object packet = constructor.newInstance("upload", new Object[]{playerSlotIndex, eap$currentlyChoicePatterProvider});
Class<?> iMessage = Class.forName("com.glodblock.github.glodium.network.packet.IMessage");
Method sendToServer = EPPNetworkHandlerClass.getMethod("sendToServer", iMessage);
sendToServer.invoke(handlerInstance, packet);
} catch (Throwable t) {
this.minecraft.player.displayClientMessage(
Component.literal("❌ ExtendedAE Plus: 未找到 ExtendedAE 网络支持(可能未安装或版本不兼容)"),
true
);
}
} else { } else {
this.minecraft.player.displayClientMessage( this.minecraft.player.displayClientMessage(
Component.literal("❌ ExtendedAE Plus: 无效的样板物品"), Component.literal("❌ ExtendedAE Plus: 无效的样板物品"),
@ -168,123 +143,80 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
} }
} }
@Unique /**
private int getIntConst(Class<?> cls, String name, int defVal) { * 尝试打开指定行对应的样板供应器的 UI
try { * 该方法基于 GroupHeaderRow 获取分组信息再获取该分组下的第一个 PatternContainerRecord
var f = cls.getDeclaredField(name); * 并通过 serverId 获取 PatternProviderInfo 来发送 C2S 包打开目标供应器界面
f.setAccessible(true); *
return (int) f.get(null); * @param rowIndex 要操作的行索引
} catch (Throwable t) { */
return defVal;
}
}
@Unique @Unique
private void eap$tryOpenProviderUI(int rowIndex) { private void eap$tryOpenProviderUI(int rowIndex) {
try { try {
// 使用 Accessor 获取 rows避免取到父类导致失败 // 获取指定行
GuiExPatternTerminalAccessor acc = (GuiExPatternTerminalAccessor) this;
ArrayList<?> rows = acc.getRows();
// 找到该分组对应的第一个 PatternContainerRecord
Class<?> cls = GuiExPatternTerminal.class;
var byGroupField = cls.getDeclaredField("byGroup");
byGroupField.setAccessible(true);
Object byGroup = byGroupField.get(this); // HashMultimap<PatternContainerGroup, PatternContainerRecord>
Object headerRow = rows.get(rowIndex); Object headerRow = rows.get(rowIndex);
var groupField = headerRow.getClass().getDeclaredField("group"); PatternContainerGroup group = ((GuiExPatternTerminalGroupHeaderRowAccessor) headerRow).Group();
groupField.setAccessible(true); // 获取该组下的所有 PatternContainerRecord
Object group = groupField.get(headerRow); Set<PatternContainerRecord> containers = byGroup.get(group);
// 调用 byGroup.get(group)再取第一个元素
Collection<?> containers = (Collection<?>) byGroup.getClass().getMethod("get", Object.class).invoke(byGroup, group);
if (containers == null || containers.isEmpty()) { if (containers == null || containers.isEmpty()) {
return; return; // 分组为空无供应器
} }
Object firstRecord = containers.iterator().next(); // PatternContainerRecord
long serverId = (long) firstRecord.getClass().getMethod("getServerId").invoke(firstRecord);
// 通过 infoMap 获取位置信息 // 取该组下第一个 PatternContainerRecord
var infoMapField = cls.getDeclaredField("infoMap"); PatternContainerRecord firstRecord = containers.iterator().next();
infoMapField.setAccessible(true); long serverId = firstRecord.getServerId(); // 获取供应器服务器 ID
@SuppressWarnings("unchecked")
HashMap<Long, Object> infoMap = (HashMap<Long, Object>) infoMapField.get(this); // 通过 infoMap 获取供应器位置信息
Object info = infoMap.get(serverId); GuiExPatternTerminal.PatternProviderInfo patternProviderInfo = infoMap.get(serverId);
if (info == null) { if (patternProviderInfo == null) {
// 无位置信息提示 // 如果没有位置信息提示玩家
if (this.minecraft != null && this.minecraft.player != null) { if (this.minecraft != null && this.minecraft.player != null) {
this.minecraft.player.displayClientMessage(Component.literal("未找到该供应器的位置信息无法打开UI"), true); this.minecraft.player.displayClientMessage(
Component.literal("未找到该供应器的位置信息无法打开UI"),
true
);
} }
return; return;
} }
// PatternProviderInfo record: pos(), face(), playerWorld() // 获取位置信息和朝向
Object pos = info.getClass().getMethod("pos").invoke(info); BlockPos pos = patternProviderInfo.pos();
Object face = info.getClass().getMethod("face").invoke(info); // 可能为 null方块型供应器 Direction face = patternProviderInfo.face();
Object playerWorld = info.getClass().getMethod("playerWorld").invoke(info); ResourceKey<Level> playerWorld = patternProviderInfo.playerWorld();
// 避免对 MC 类进行反射使用强制类型转换后直接调用方法 Forge 运行时重映射保证 // 转换为 C2S 包所需类型
long posLong = ((BlockPos) pos).asLong(); long posLong = pos.asLong();
String dimStr = ((ResourceKey<Level>) playerWorld).location().toString(); ResourceLocation dimStr = playerWorld.location();
int faceOrd = -1; int faceOrd = (face != null) ?
if (face != null) { face.ordinal() :
faceOrd = ((Direction) face).ordinal(); -1;
}
// 发送我们自己的 C2S OpenProviderUiC2SPacket // 发送打开 UI C2S
try { ModNetwork.CHANNEL.sendToServer(new OpenProviderUiC2SPacket(
ModNetwork.CHANNEL.sendToServer(new OpenProviderUiC2SPacket( posLong,
posLong, dimStr,
new ResourceLocation(dimStr), faceOrd
faceOrd ));
)); } catch (Throwable ignored) {
} catch (Throwable t) { // 静默失败不影响界面操作
// 静默失败不提示玩家
}
} catch (Throwable t) {
// 静默失败不输出日志
} }
} }
/** @Shadow
* 重置当前选择的样板供应器ID private void refreshList() {}
*/
@Unique @Shadow
public void resetCurrentlyChoicePatternProvider() { private void resetScrollbar() {}
this.eap$currentlyChoicePatterProvider = -1;
}
@Inject(method = "<init>", at = @At("TAIL"), remap = false) @Inject(method = "<init>", at = @At("TAIL"), remap = false)
private void injectConstructor(CallbackInfo ci) { private void injectConstructor(CallbackInfo ci) {
// 根据配置初始化默认显示/隐藏状态 // 根据配置初始化默认显示/隐藏状态
try { this.eap$showSlots = ModConfig.INSTANCE.patternTerminalShowSlotsDefault;
this.eap$showSlots = ModConfig.INSTANCE.patternTerminalShowSlotsDefault;
} catch (Throwable ignored) {
}
// 创建切换槽位显示的按钮 // 创建切换槽位显示的按钮
this.eap$toggleSlotsButton = new IconButton((b) -> { this.eap$toggleSlotsButton = new IconButton((b) -> {
this.eap$showSlots = !this.eap$showSlots; // 开关状态 this.eap$showSlots = !this.eap$showSlots; // 开关状态
// 通过反射调用refreshList方法 - 先尝试当前类失败后尝试父类 refreshList();
try {
Method refreshMethod = null;
try {
// 先尝试在当前类中查找
refreshMethod = this.getClass().getDeclaredMethod("refreshList");
} catch (NoSuchMethodException e1) {
// 如果当前类没有尝试在父类中查找
try {
refreshMethod = this.getClass().getSuperclass().getDeclaredMethod("refreshList");
} catch (NoSuchMethodException e2) {
throw e2;
}
}
refreshMethod.setAccessible(true);
refreshMethod.invoke(this);
} catch (Exception ignored) {
}
}) { }) {
@Override @Override
protected Icon getIcon() { protected Icon getIcon() {
@ -311,47 +243,8 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
// 移除并清理按钮避免旧位置残留 // 移除并清理按钮避免旧位置残留
this.eap$openUIButtons.values().forEach(this::removeWidget); this.eap$openUIButtons.values().forEach(this::removeWidget);
this.eap$openUIButtons.clear(); this.eap$openUIButtons.clear();
refreshList();
// 重置一次滚动条避免可见行/偏移在缩放后与 UI 尺寸不一致 resetScrollbar();
try {
Method resetScrollbarMethod = null;
try {
resetScrollbarMethod = this.getClass().getDeclaredMethod("resetScrollbar");
} catch (NoSuchMethodException e1) {
try {
resetScrollbarMethod = this.getClass().getSuperclass().getDeclaredMethod("resetScrollbar");
} catch (NoSuchMethodException e2) {
resetScrollbarMethod = null;
}
}
if (resetScrollbarMethod != null) {
resetScrollbarMethod.setAccessible(true);
resetScrollbarMethod.invoke(this);
}
} catch (Throwable ignored) {
}
// 刷新列表使 rows/visibleRows 立即以新尺寸重算
try {
Method refreshMethod = null;
try {
refreshMethod = this.getClass().getDeclaredMethod("refreshList");
} catch (NoSuchMethodException e1) {
try {
refreshMethod = this.getClass().getSuperclass().getDeclaredMethod("refreshList");
} catch (NoSuchMethodException e2) {
refreshMethod = null;
}
}
if (refreshMethod != null) {
refreshMethod.setAccessible(true);
refreshMethod.invoke(this);
}
} catch (Throwable ignored) {
}
// 下次绘制重新输出一次调试行便于确认缩放后的 rows/scroll
this.eap$debugLoggedOnce = false;
} catch (Throwable ignored) { } catch (Throwable ignored) {
} }
} }
@ -368,7 +261,9 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
// 更新按钮图标 // 更新按钮图标
if (this.eap$toggleSlotsButton != null) { if (this.eap$toggleSlotsButton != null) {
this.eap$toggleSlotsButton.setTooltip(Tooltip.create(Component.translatable( this.eap$toggleSlotsButton.setTooltip(Tooltip.create(Component.translatable(
this.eap$showSlots ? "gui.expatternprovider.hide_slots" : "gui.expatternprovider.show_slots" this.eap$showSlots ?
"gui.expatternprovider.hide_slots" :
"gui.expatternprovider.show_slots"
))); )));
} }
// 清理旧的打开UI按钮 // 清理旧的打开UI按钮
@ -382,41 +277,8 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
// 在refreshList结束后根据showSlots状态过滤SlotsRow // 在refreshList结束后根据showSlots状态过滤SlotsRow
if (!this.eap$showSlots) { if (!this.eap$showSlots) {
try { try {
// 通过反射访问rows字段 - 先尝试当前类失败后尝试父类
java.lang.reflect.Field rowsField = null;
try {
// 先尝试在当前类中查找
rowsField = this.getClass().getDeclaredField("rows");
} catch (NoSuchFieldException e1) {
// 如果当前类没有尝试在父类中查找
try {
rowsField = this.getClass().getSuperclass().getDeclaredField("rows");
} catch (NoSuchFieldException e2) {
throw e2;
}
}
rowsField.setAccessible(true);
java.util.ArrayList<?> rows = (java.util.ArrayList<?>) rowsField.get(this);
// 通过反射访问highlightBtns字段
java.lang.reflect.Field highlightBtnsField = null;
try {
// 先尝试在当前类中查找
highlightBtnsField = this.getClass().getDeclaredField("highlightBtns");
} catch (NoSuchFieldException e1) {
// 如果当前类没有尝试在父类中查找
try {
highlightBtnsField = this.getClass().getSuperclass().getDeclaredField("highlightBtns");
} catch (NoSuchFieldException e2) {
throw e2;
}
}
highlightBtnsField.setAccessible(true);
@SuppressWarnings("unchecked")
java.util.HashMap<Integer, Object> highlightBtns = (java.util.HashMap<Integer, Object>) highlightBtnsField.get(this);
// 创建新的索引映射 // 创建新的索引映射
java.util.HashMap<Integer, Object> newHighlightBtns = new java.util.HashMap<>(); HashMap<Integer, HighlightButton> newHighlightBtns = new HashMap<>();
int newIndex = 0; int newIndex = 0;
// 移除所有SlotsRow只保留GroupHeaderRow同时重新映射高亮按钮索引 // 移除所有SlotsRow只保留GroupHeaderRow同时重新映射高亮按钮索引
@ -427,7 +289,7 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
if (className.equals("GroupHeaderRow")) { if (className.equals("GroupHeaderRow")) {
// 保留GroupHeaderRow并重新映射对应的高亮按钮 // 保留GroupHeaderRow并重新映射对应的高亮按钮
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
java.util.ArrayList<Object> typedRows = (java.util.ArrayList<Object>) rows; ArrayList<Object> typedRows = (ArrayList<Object>) rows;
typedRows.set(newIndex, row); typedRows.set(newIndex, row);
// 查找原来在这个位置的高亮按钮 // 查找原来在这个位置的高亮按钮
@ -435,7 +297,7 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
// 所以按钮的索引指向的是第一个SlotsRow的位置 // 所以按钮的索引指向的是第一个SlotsRow的位置
// 我们需要查找索引为 i+1 的按钮第一个SlotsRow的位置 // 我们需要查找索引为 i+1 的按钮第一个SlotsRow的位置
if (highlightBtns.containsKey(i + 1)) { if (highlightBtns.containsKey(i + 1)) {
Object button = highlightBtns.get(i + 1); HighlightButton button = highlightBtns.get(i + 1);
newHighlightBtns.put(newIndex, button); newHighlightBtns.put(newIndex, button);
} }
@ -455,24 +317,7 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
highlightBtns.putAll(newHighlightBtns); highlightBtns.putAll(newHighlightBtns);
// 强制刷新滚动条 // 强制刷新滚动条
try { resetScrollbar();
Method resetScrollbarMethod = null;
try {
// 先尝试在当前类中查找
resetScrollbarMethod = this.getClass().getDeclaredMethod("resetScrollbar");
} catch (NoSuchMethodException e1) {
// 如果当前类没有尝试在父类中查找
try {
resetScrollbarMethod = this.getClass().getSuperclass().getDeclaredMethod("resetScrollbar");
} catch (NoSuchMethodException e2) {
throw e2;
}
}
resetScrollbarMethod.setAccessible(true);
resetScrollbarMethod.invoke(this);
} catch (Exception ignored) {
}
} catch (Exception ignored) { } catch (Exception ignored) {
} }
} }
@ -482,29 +327,13 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
private void eap$afterDrawFG(GuiGraphics guiGraphics, int offsetX, int offsetY, int mouseX, int mouseY, CallbackInfo ci) { private void eap$afterDrawFG(GuiGraphics guiGraphics, int offsetX, int offsetY, int mouseX, int mouseY, CallbackInfo ci) {
// 动态放置/创建每个组标题后的打开UI按钮 // 动态放置/创建每个组标题后的打开UI按钮
try { try {
// 使用 Accessor 获取必要的字段避免反射失败 int currentScroll = scrollbar.getCurrentScroll();
GuiExPatternTerminalAccessor acc = (GuiExPatternTerminalAccessor) this;
java.util.ArrayList<?> rows = acc.getRows();
int currentScroll = acc.getScrollbar().getCurrentScroll();
// 直接引用目标类以获取其静态常量
Class<?> cls = GuiExPatternTerminal.class;
int GUI_PADDING_X = getIntConst(cls, "GUI_PADDING_X", 22);
int GUI_PADDING_Y = getIntConst(cls, "GUI_PADDING_Y", 6);
int GUI_HEADER_HEIGHT = getIntConst(cls, "GUI_HEADER_HEIGHT", 51);
int ROW_HEIGHT = getIntConst(cls, "ROW_HEIGHT", 18);
int TEXT_MAX_WIDTH = getIntConst(cls, "TEXT_MAX_WIDTH", 155);
int visibleRows = acc.getVisibleRows();
// 生产环境移除调试日志
// 先隐藏旧按钮避免残留 // 先隐藏旧按钮避免残留
for (Button b : this.eap$openUIButtons.values()) { for (Button b : this.eap$openUIButtons.values()) {
b.visible = false; b.visible = false;
} }
int shownCount = 0;
for (int i = 0; i < visibleRows; i++) { for (int i = 0; i < visibleRows; i++) {
int rowIndex = currentScroll + i; int rowIndex = currentScroll + i;
if (rowIndex < 0 || rowIndex >= rows.size()) { if (rowIndex < 0 || rowIndex >= rows.size()) {
@ -530,7 +359,6 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
} }
btn.setPosition(bx, by); btn.setPosition(bx, by);
btn.visible = true; btn.visible = true;
shownCount++;
} }
// 生产环境移除调试日志 // 生产环境移除调试日志
} catch (Throwable ignored) { } catch (Throwable ignored) {
@ -546,11 +374,5 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
// 使用 GuiUtil 的通用绘制方法绘制槽位高亮包含彩虹流转效果 // 使用 GuiUtil 的通用绘制方法绘制槽位高亮包含彩虹流转效果
GuiUtil.drawPatternSlotHighlights(guiGraphics, this.menu.slots, this.matchedStack, this.matchedProvider); GuiUtil.drawPatternSlotHighlights(guiGraphics, this.menu.slots, this.matchedStack, this.matchedProvider);
}
@Unique
private void eap$fill(GuiGraphics guiGraphics, Rect2i rect, int argb) {
this.fillRect(guiGraphics, rect, argb);
} }
} }

View File

@ -21,7 +21,7 @@
"ae2.items.QuartzCuttingKnifeItemMixin", "ae2.items.QuartzCuttingKnifeItemMixin",
"ae2.menu.CraftConfirmMenuGoBackMixin", "ae2.menu.CraftConfirmMenuGoBackMixin",
"extendedae.accessor.GuiExPatternTerminalAccessor", "extendedae.accessor.GuiExPatternTerminalAccessor",
"extendedae.accessor.GuiExPatternTerminalSlotsRowAccessor", "extendedae.accessor.GuiExPatternTerminalGroupHeaderRowAccessor",
"extendedae.client.HighlightButtonMixin", "extendedae.client.HighlightButtonMixin",
"extendedae.client.gui.GuiExPatternProviderMixin", "extendedae.client.gui.GuiExPatternProviderMixin",
"extendedae.client.gui.GuiExPatternTerminalMixin", "extendedae.client.gui.GuiExPatternTerminalMixin",