From f254da43f4c2d6cb38b6c795ec1842f128488f54 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Tue, 2 Sep 2025 02:05:16 +0800 Subject: [PATCH 1/9] =?UTF-8?q?feat:=201.=E5=88=B6=E4=BD=9C=E4=B8=AD?= =?UTF-8?q?=E6=89=93=E5=BC=80=E4=BE=9B=E5=BA=94=E5=99=A8=E9=AB=98=E4=BA=AE?= =?UTF-8?q?=E5=AF=B9=E5=BA=94=E6=A0=B7=E6=9D=BF=202.=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=AB=98=E4=BA=AE=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/PatternHighlightStore.java | 47 ++++++++ .../ae2/client/gui/AEBaseScreenMixin.java | 45 +++++--- .../client/gui/PatternProviderCloseMixin.java | 28 +++++ .../client/gui/GuiExPatternTerminalMixin.java | 85 +------------- .../CraftingMonitorOpenProviderC2SPacket.java | 4 + .../com/extendedae_plus/util/GuiUtil.java | 107 ++++++++++++++++++ .../resources/extendedae_plus.mixins.json | 2 + 7 files changed, 222 insertions(+), 96 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/content/PatternHighlightStore.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java diff --git a/src/main/java/com/extendedae_plus/content/PatternHighlightStore.java b/src/main/java/com/extendedae_plus/content/PatternHighlightStore.java new file mode 100644 index 0000000..6afba04 --- /dev/null +++ b/src/main/java/com/extendedae_plus/content/PatternHighlightStore.java @@ -0,0 +1,47 @@ +package com.extendedae_plus.content; + +import appeng.api.crafting.IPatternDetails; + +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +public final class PatternHighlightStore { + // 使用同步的 WeakHashMap 存储高亮状态,键为 IPatternDetails,值为 Boolean + private static final Map HIGHLIGHTS = Collections.synchronizedMap(new WeakHashMap<>()); + + // 私有构造方法,防止实例化 + private PatternHighlightStore() {} + + /** + * 设置指定 details 的高亮状态。 + * @param details 需要设置的 IPatternDetails 实例 + * @param highlighted 是否高亮 + */ + public static void setHighlight(IPatternDetails details, boolean highlighted) { + if (details == null) return; + if (highlighted) { + HIGHLIGHTS.put(details, Boolean.TRUE); // 设置为高亮 + } else { + HIGHLIGHTS.remove(details); // 移除高亮 + } + } + + /** + * 获取指定 details 的高亮状态。 + * @param details 需要查询的 IPatternDetails 实例 + * @return 是否高亮 + */ + public static boolean getHighlight(IPatternDetails details) { + if (details == null) return false; + Boolean v = HIGHLIGHTS.get(details); + return v != null && v; + } + + /** + * 清空所有高亮状态(在供应器界面关闭时调用)。 + */ + public static void clearAll() { + HIGHLIGHTS.clear(); + } +} \ No newline at end of file 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 880b6c0..0e857dc 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 @@ -1,5 +1,6 @@ package com.extendedae_plus.mixin.ae2.client.gui; +import appeng.api.crafting.PatternDetailsHelper; import appeng.api.stacks.AEKey; import appeng.client.Point; import appeng.client.gui.AEBaseScreen; @@ -12,12 +13,14 @@ import appeng.client.gui.style.Text; import appeng.client.gui.style.TextAlignment; import appeng.menu.slot.AppEngSlot; import com.extendedae_plus.api.ExPatternPageAccessor; +import com.extendedae_plus.content.PatternHighlightStore; import com.extendedae_plus.network.CraftingMonitorJumpC2SPacket; import com.extendedae_plus.network.CraftingMonitorOpenProviderC2SPacket; import com.extendedae_plus.network.ModNetwork; import com.extendedae_plus.util.GuiUtil; import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; import com.mojang.logging.LogUtils; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.renderer.Rect2i; @@ -150,40 +153,49 @@ public abstract class AEBaseScreenMixin { @Inject(method = "renderSlot", at = @At("TAIL")) private void eap$renderSlotAmounts(GuiGraphics guiGraphics, Slot s, CallbackInfo ci) { Object self = this; - + // 只处理AppEngSlot类型的槽位 if (!(s instanceof AppEngSlot appEngSlot)) { return; } - + // 检查槽位是否可见且有效 if (!appEngSlot.isActive() || !appEngSlot.isSlotEnabled()) { return; } - + // 获取槽位中的物品 var itemStack = appEngSlot.getItem(); if (itemStack.isEmpty()) { return; } - + // 使用GuiUtil的格式化方法获取数量文本 String amountText = GuiUtil.getPatternOutputText(itemStack); if (amountText.isEmpty()) { return; } - + // 在槽位右下角绘制数量文本 Font font = eap$getFont(self); GuiUtil.drawAmountText(guiGraphics, font, amountText, appEngSlot.x, appEngSlot.y, 0.6f); + + try { + var details = PatternDetailsHelper.decodePattern(itemStack, Minecraft.getInstance().level, false); + if (PatternHighlightStore.getHighlight(details)) { + try { + GuiUtil.drawSlotRainbowHighlight(guiGraphics, s.x, s.y); + } catch (Throwable ignored) {} + } + } catch (Throwable ignore) {} } // 在 AEBaseScreen.drawText 完成某个文本绘制后,若该文本为“样板”标签,则紧接着绘制页码。 @Inject(method = "drawText", at = @At("TAIL"), remap = false) private void eap$appendPageAfterPatternsLabel(GuiGraphics guiGraphics, - Text text, - @Nullable TextOverride override, - CallbackInfo ci) { + Text text, + @Nullable TextOverride override, + CallbackInfo ci) { Object self = this; if (!(self instanceof GuiExPatternProvider)) { return; @@ -237,8 +249,10 @@ public abstract class AEBaseScreenMixin { 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; + 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; } } @@ -263,16 +277,18 @@ public abstract class AEBaseScreenMixin { if (v instanceof Integer i) { max = Math.max(1, i); } - } catch (Throwable ignored) {} + } catch (Throwable ignored) { + } - String pageText = "第"+cur+"页" + "/" + max + "页"; + String pageText = "第" + cur + "页" + "/" + max + "页"; ScreenStyle style = eap$getStyle(self); int color = 0xFFFFFFFF; if (style != null) { try { color = style.getColor(PaletteColor.DEFAULT_TEXT_COLOR).toARGB(); - } catch (Throwable ignored) {} + } catch (Throwable ignored) { + } } int padding = 4; if (scale == 1.0f) { @@ -284,6 +300,7 @@ public abstract class AEBaseScreenMixin { guiGraphics.drawString(font, pageText, lineWidth + padding, 0, color, false); guiGraphics.pose().popPose(); } - } catch (Throwable ignored) {} + } catch (Throwable ignored) { + } } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java new file mode 100644 index 0000000..eed7a29 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java @@ -0,0 +1,28 @@ +package com.extendedae_plus.mixin.ae2.client.gui; + +import appeng.client.gui.implementations.PatternProviderScreen; +import com.extendedae_plus.content.PatternHighlightStore; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.world.inventory.AbstractContainerMenu; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(value = AbstractContainerScreen.class, remap = false) +public class PatternProviderCloseMixin { + + @Shadow + protected AbstractContainerMenu menu; + + @Inject(method = "removed", at = @At("HEAD")) + private void onRemoved(CallbackInfo ci) { + try { + if (((Object) this) instanceof PatternProviderScreen) { + PatternHighlightStore.clearAll(); + } + } catch (Throwable ignored) { + } + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternTerminalMixin.java b/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternTerminalMixin.java index 819d78f..e8b5402 100644 --- a/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternTerminalMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternTerminalMixin.java @@ -4,7 +4,6 @@ import appeng.api.crafting.PatternDetailsHelper; import appeng.client.gui.AEBaseScreen; import appeng.client.gui.Icon; import appeng.client.gui.me.patternaccess.PatternContainerRecord; -import appeng.client.gui.me.patternaccess.PatternSlot; import appeng.client.gui.style.ScreenStyle; import appeng.client.gui.widgets.AETextField; import appeng.client.gui.widgets.IconButton; @@ -13,6 +12,7 @@ import com.extendedae_plus.config.ModConfigs; import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalAccessor; import com.extendedae_plus.network.ModNetwork; import com.extendedae_plus.network.OpenProviderUiC2SPacket; +import com.extendedae_plus.util.GuiUtil; import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; @@ -76,41 +76,6 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen super(menu, playerInventory, title, style); } - @Unique - private static int eap$withAlpha(int rgb, int alpha255) { - return ((alpha255 & 0xFF) << 24) | (rgb & 0x00FFFFFF); - } - - /** - * 将 HSV 转换为 RGB(返回 0xRRGGBB,不含 alpha)。 - * h: 0.0~1.0,s: 0.0~1.0,v: 0.0~1.0 - */ - @Unique - private static int eap$hsvToRgb(float h, float s, float v) { - if (s <= 0.0f) { - int g = Math.round(v * 255.0f); - return (g << 16) | (g << 8) | g; - } - float hh = (h - (float) Math.floor(h)) * 6.0f; - int sector = (int) Math.floor(hh); - float f = hh - sector; - float p = v * (1.0f - s); - float q = v * (1.0f - s * f); - float t = v * (1.0f - s * (1.0f - f)); - float r, g, b; - switch (sector) { - case 0: r = v; g = t; b = p; break; - case 1: r = q; g = v; b = p; break; - case 2: r = p; g = v; b = t; break; - case 3: r = p; g = q; b = v; break; - case 4: r = t; g = p; b = v; break; - default: r = v; g = p; b = q; break; - } - int ri = Math.round(r * 255.0f); - int gi = Math.round(g * 255.0f); - int bi = Math.round(b * 255.0f); - return (ri << 16) | (gi << 8) | bi; - } /** * 获取当前选择的样板供应器ID @@ -579,53 +544,9 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen return; } - // 彩虹色的流转:基于时间在 HSV 色环上循环(4 秒为一周期) - long now = System.currentTimeMillis(); - final long rainbowPeriodMs = 4000L; - float hue = (now % rainbowPeriodMs) / (float) rainbowPeriodMs; // 0.0 ~ 1.0 - int rainbowRgb = eap$hsvToRgb(hue, 1.0f, 1.0f); + // 使用 GuiUtil 的通用绘制方法绘制槽位高亮(包含彩虹流转效果) + GuiUtil.drawPatternSlotHighlights(guiGraphics, this.menu.slots, this.matchedStack, this.matchedProvider); - for (Slot slot : this.menu.slots) { - if (!(slot instanceof PatternSlot ps)) { - continue; - } - - int sx = slot.x; - int sy = slot.y; - - boolean isMatchedSlot = this.matchedStack != null && this.matchedStack.contains(slot.getItem()); - boolean isMatchedProvider = false; - try { - PatternContainerRecord container = ps.getMachineInv(); - isMatchedProvider = this.matchedProvider != null && this.matchedProvider.contains(container); - } catch (Throwable ignored) { - } - - // 依据命中状态选择颜色方案 - int borderColor; - int backgroundColor; - - if (isMatchedSlot) { - // 命中槽位:使用彩虹色边框与浅底色(固定透明度,呈现色相流转效果) - borderColor = eap$withAlpha(rainbowRgb, 0xA0); - backgroundColor = eap$withAlpha(rainbowRgb, 0x3C); - } else if (!isMatchedProvider) { - borderColor = eap$withAlpha(0xFFFFFF, 0x40); - backgroundColor = eap$withAlpha(0x000000, 0x18); - } else { - borderColor = eap$withAlpha(0xFFFFFF, 0x30); - backgroundColor = eap$withAlpha(0xFFFFFF, 0x14); - } - - // 绘制 18x18 边框(1px 宽) - eap$fill(guiGraphics, new Rect2i(sx - 1, sy - 1, 18, 1), borderColor); - eap$fill(guiGraphics, new Rect2i(sx - 1, sy + 16, 18, 1), borderColor); - eap$fill(guiGraphics, new Rect2i(sx - 1, sy, 1, 16), borderColor); - eap$fill(guiGraphics, new Rect2i(sx + 16, sy, 1, 16), borderColor); - - // 绘制 16x16 浅底色(半透明,叠加在槽位上方) - eap$fill(guiGraphics, new Rect2i(sx, sy, 16, 16), backgroundColor); - } } @Unique diff --git a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java index b1329fe..20bfabf 100644 --- a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java @@ -11,6 +11,7 @@ import appeng.menu.AEBaseMenu; import appeng.menu.locator.MenuLocators; import appeng.menu.me.crafting.CraftingCPUMenu; import appeng.parts.AEBasePart; +import com.extendedae_plus.content.PatternHighlightStore; import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor; import com.mojang.logging.LogUtils; import net.minecraft.network.FriendlyByteBuf; @@ -99,6 +100,9 @@ public class CraftingMonitorOpenProviderC2SPacket { } else { host.openMenu(player, MenuLocators.forBlockEntity(pbe)); } + + PatternHighlightStore.setHighlight(pattern, true); + context.setPacketHandled(true); return; } catch (Throwable t) { diff --git a/src/main/java/com/extendedae_plus/util/GuiUtil.java b/src/main/java/com/extendedae_plus/util/GuiUtil.java index 2320665..1c804d9 100644 --- a/src/main/java/com/extendedae_plus/util/GuiUtil.java +++ b/src/main/java/com/extendedae_plus/util/GuiUtil.java @@ -2,11 +2,17 @@ package com.extendedae_plus.util; import appeng.api.crafting.PatternDetailsHelper; import appeng.api.stacks.GenericStack; +import appeng.client.gui.me.patternaccess.PatternContainerRecord; +import appeng.client.gui.me.patternaccess.PatternSlot; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; +import java.util.List; +import java.util.Set; + /** * GUI工具类,提供样板获取、绘制等通用功能 @@ -77,4 +83,105 @@ public class GuiUtil { guiGraphics.drawString(font, text, (int)(textX / scale), (int)(textY / scale), 0xFFFFFFFF, true); guiGraphics.pose().popPose(); } + + // Helper: add alpha channel to RGB (rgb is 0xRRGGBB) + private static int withAlpha(int rgb, int alpha255) { + return ((alpha255 & 0xFF) << 24) | (rgb & 0x00FFFFFF); + } + + // HSV -> RGB (returns 0xRRGGBB) + private static int hsvToRgb(float h, float s, float v) { + if (s <= 0.0f) { + int g = Math.round(v * 255.0f); + return (g << 16) | (g << 8) | g; + } + float hh = (h - (float) Math.floor(h)) * 6.0f; + int sector = (int) Math.floor(hh); + float f = hh - sector; + float p = v * (1.0f - s); + float q = v * (1.0f - s * f); + float t = v * (1.0f - s * (1.0f - f)); + float r, g, b; + switch (sector) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + default: r = v; g = p; b = q; break; + } + int ri = Math.round(r * 255.0f); + int gi = Math.round(g * 255.0f); + int bi = Math.round(b * 255.0f); + return (ri << 16) | (gi << 8) | bi; + } + + // 返回当前时间对应的彩虹色(0xRRGGBB),周期固定为 4000ms + private static int getRainbowRgb() { + long now = System.currentTimeMillis(); + final long rainbowPeriodMs = 4000L; + float hue = (now % rainbowPeriodMs) / (float) rainbowPeriodMs; // 0.0 ~ 1.0 + return hsvToRgb(hue, 1.0f, 1.0f); + } + + // 在给定槽位坐标绘制 1px 边框(18x18)和 16x16 半透明背景 + private static void drawSlotBox(GuiGraphics guiGraphics, int sx, int sy, int borderColor, int backgroundColor) { + guiGraphics.fill(sx - 1, sy - 1, sx + 17, sy, borderColor); + guiGraphics.fill(sx - 1, sy + 16, sx + 17, sy + 17, borderColor); + guiGraphics.fill(sx - 1, sy, sx, sy + 16, borderColor); + guiGraphics.fill(sx + 16, sy, sx + 17, sy + 16, borderColor); + guiGraphics.fill(sx, sy, sx + 16, sy + 16, backgroundColor); + } + + /** + * 在槽位上绘制彩色流转的高亮和浅底色 + */ + public static void drawPatternSlotHighlights(GuiGraphics guiGraphics, List slots, Set matchedStack, Set matchedProvider) { + if (slots == null) return; + + int rainbowRgb = getRainbowRgb(); + + for (Slot slot : slots) { + if (!(slot instanceof PatternSlot ps)) { + continue; + } + + int sx = slot.x; + int sy = slot.y; + + boolean isMatchedSlot = matchedStack != null && matchedStack.contains(slot.getItem()); + boolean isMatchedProvider = false; + try { + PatternContainerRecord container = ps.getMachineInv(); + isMatchedProvider = matchedProvider != null && matchedProvider.contains(container); + } catch (Throwable ignored) { + } + + int borderColor; + int backgroundColor; + + if (isMatchedSlot) { + borderColor = withAlpha(rainbowRgb, 0xA0); + backgroundColor = withAlpha(rainbowRgb, 0x3C); + } else if (!isMatchedProvider) { + borderColor = withAlpha(0xFFFFFF, 0x40); + backgroundColor = withAlpha(0x000000, 0x18); + } else { + borderColor = withAlpha(0xFFFFFF, 0x30); + backgroundColor = withAlpha(0xFFFFFF, 0x14); + } + + drawSlotBox(guiGraphics, sx, sy, borderColor, backgroundColor); + } + } + + /** + * 在指定槽位坐标绘制彩虹流转的边框与浅底色(用于非 PatternSlot 的高亮场景) + */ + public static void drawSlotRainbowHighlight(GuiGraphics guiGraphics, int sx, int sy) { + int rainbowRgb = getRainbowRgb(); + int borderColor = withAlpha(rainbowRgb, 0xA0); + int backgroundColor = withAlpha(rainbowRgb, 0x3C); + drawSlotBox(guiGraphics, sx, sy, borderColor, backgroundColor); + } } \ No newline at end of file diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index e001c30..5772e34 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -15,6 +15,7 @@ "ae2.accessor.PatternAccessTermScreenSlotsRowAccessor", "ae2.client.gui.AEBaseScreenMixin", "ae2.client.gui.PatternEncodingTermScreenMixin", + "ae2.client.gui.PatternProviderCloseMixin", "ae2.client.gui.PatternProviderScreenMixin", "ae2.client.gui.SlotGridLayoutMixin", "extendedae.accessor.GuiExPatternTerminalAccessor", @@ -30,6 +31,7 @@ "mixins": [ "ae2.AEProcessingPatternMixin", "ae2.CraftingCPUClusterMixin", + "ae2.IPatternDetailsMixin", "ae2.accessor.MEStorageMenuAccessor", "ae2.accessor.PatternEncodingTermMenuAccessor", "ae2.accessor.PatternProviderLogicAccessor", From 29b92e4ee174b25e39599663dfc74eee7b2357e8 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Tue, 2 Sep 2025 02:36:07 +0800 Subject: [PATCH 2/9] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E7=AB=AF=E5=90=91=E7=9B=AE=E6=A0=87=E7=8E=A9?= =?UTF-8?q?=E5=AE=B6=E5=8F=91=E9=80=81=E6=A0=B7=E6=9D=BF=E9=AB=98=E4=BA=AE?= =?UTF-8?q?=E5=8C=85=EF=BC=8C=E7=A7=BB=E9=99=A4=E5=85=A8=E5=B1=80=E9=AB=98?= =?UTF-8?q?=E4=BA=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/ClientPatternHighlightStore.java | 34 ++++++++++++++ .../ae2/client/gui/AEBaseScreenMixin.java | 17 ++++--- .../client/gui/PatternProviderCloseMixin.java | 4 +- .../CraftingMonitorOpenProviderC2SPacket.java | 8 +++- .../extendedae_plus/network/ModNetwork.java | 6 +++ .../network/SetPatternHighlightS2CPacket.java | 45 +++++++++++++++++++ 6 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/content/ClientPatternHighlightStore.java create mode 100644 src/main/java/com/extendedae_plus/network/SetPatternHighlightS2CPacket.java diff --git a/src/main/java/com/extendedae_plus/content/ClientPatternHighlightStore.java b/src/main/java/com/extendedae_plus/content/ClientPatternHighlightStore.java new file mode 100644 index 0000000..2e9f0c4 --- /dev/null +++ b/src/main/java/com/extendedae_plus/content/ClientPatternHighlightStore.java @@ -0,0 +1,34 @@ +package com.extendedae_plus.content; + +import appeng.api.stacks.AEKey; +import appeng.api.crafting.IPatternDetails; +import appeng.client.gui.me.patternaccess.PatternContainerRecord; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * 客户端本地的高亮存储,仅作用于接收该客户端。 + * 使用 AEKey 作为标识,渲染时可通过 AEKey 与本地解码的 IPatternDetails 比对。 + */ +public final class ClientPatternHighlightStore { + private static final Set HIGHLIGHTS = Collections.synchronizedSet(new HashSet<>()); + + private ClientPatternHighlightStore() {} + + public static void setHighlight(AEKey key, boolean highlight) { + if (key == null) return; + if (highlight) HIGHLIGHTS.add(key); + else HIGHLIGHTS.remove(key); + } + + public static boolean hasHighlight(AEKey key) { + if (key == null) return false; + return HIGHLIGHTS.contains(key); + } + + public static void clearAll() { HIGHLIGHTS.clear(); } +} + + 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 0e857dc..0fcb7bd 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 @@ -13,7 +13,7 @@ import appeng.client.gui.style.Text; import appeng.client.gui.style.TextAlignment; import appeng.menu.slot.AppEngSlot; import com.extendedae_plus.api.ExPatternPageAccessor; -import com.extendedae_plus.content.PatternHighlightStore; +import com.extendedae_plus.content.ClientPatternHighlightStore; import com.extendedae_plus.network.CraftingMonitorJumpC2SPacket; import com.extendedae_plus.network.CraftingMonitorOpenProviderC2SPacket; import com.extendedae_plus.network.ModNetwork; @@ -182,11 +182,16 @@ public abstract class AEBaseScreenMixin { try { var details = PatternDetailsHelper.decodePattern(itemStack, Minecraft.getInstance().level, false); - if (PatternHighlightStore.getHighlight(details)) { - try { - GuiUtil.drawSlotRainbowHighlight(guiGraphics, s.x, s.y); - } catch (Throwable ignored) {} - } + try { + if (details != null && details.getOutputs() != null && details.getOutputs().length > 0) { + AEKey key = details.getOutputs()[0].what(); + if (key != null && ClientPatternHighlightStore.hasHighlight(key)) { + try { + GuiUtil.drawSlotRainbowHighlight(guiGraphics, s.x, s.y); + } catch (Throwable ignored) {} + } + } + } catch (Throwable ignore) {} } catch (Throwable ignore) {} } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java index eed7a29..767e888 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java @@ -1,7 +1,7 @@ package com.extendedae_plus.mixin.ae2.client.gui; import appeng.client.gui.implementations.PatternProviderScreen; -import com.extendedae_plus.content.PatternHighlightStore; +import com.extendedae_plus.content.ClientPatternHighlightStore; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.world.inventory.AbstractContainerMenu; import org.spongepowered.asm.mixin.Mixin; @@ -20,7 +20,7 @@ public class PatternProviderCloseMixin { private void onRemoved(CallbackInfo ci) { try { if (((Object) this) instanceof PatternProviderScreen) { - PatternHighlightStore.clearAll(); + ClientPatternHighlightStore.clearAll(); } } catch (Throwable ignored) { } diff --git a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java index 20bfabf..c3a258e 100644 --- a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java @@ -94,6 +94,12 @@ public class CraftingMonitorOpenProviderC2SPacket { // 直接打开供应器自身的 UI(调用 Host 默认方法) try { + // 告知目标玩家客户端高亮该 AEKey(避免全局服务端状态污染) + try { + AEKey key = pattern.getOutputs()[0].what(); + ModNetwork.CHANNEL.sendTo(new SetPatternHighlightS2CPacket(key, true), player.connection.connection, net.minecraftforge.network.NetworkDirection.PLAY_TO_CLIENT); + } catch (Throwable t) {} + // 部件与方块实体分别选择定位器 if (host instanceof AEBasePart part) { host.openMenu(player, MenuLocators.forPart(part)); @@ -101,8 +107,6 @@ public class CraftingMonitorOpenProviderC2SPacket { host.openMenu(player, MenuLocators.forBlockEntity(pbe)); } - PatternHighlightStore.setHighlight(pattern, true); - context.setPacketHandled(true); return; } catch (Throwable t) { diff --git a/src/main/java/com/extendedae_plus/network/ModNetwork.java b/src/main/java/com/extendedae_plus/network/ModNetwork.java index a869cfe..8b4599c 100644 --- a/src/main/java/com/extendedae_plus/network/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/network/ModNetwork.java @@ -60,6 +60,12 @@ public class ModNetwork { .consumerNetworkThread(ProvidersListS2CPacket::handle) .add(); + CHANNEL.messageBuilder(SetPatternHighlightS2CPacket.class, nextId(), NetworkDirection.PLAY_TO_CLIENT) + .encoder(SetPatternHighlightS2CPacket::encode) + .decoder(SetPatternHighlightS2CPacket::decode) + .consumerNetworkThread(SetPatternHighlightS2CPacket::handle) + .add(); + CHANNEL.messageBuilder(ToggleAdvancedBlockingC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER) .encoder(ToggleAdvancedBlockingC2SPacket::encode) .decoder(ToggleAdvancedBlockingC2SPacket::decode) diff --git a/src/main/java/com/extendedae_plus/network/SetPatternHighlightS2CPacket.java b/src/main/java/com/extendedae_plus/network/SetPatternHighlightS2CPacket.java new file mode 100644 index 0000000..6da5fda --- /dev/null +++ b/src/main/java/com/extendedae_plus/network/SetPatternHighlightS2CPacket.java @@ -0,0 +1,45 @@ +package com.extendedae_plus.network; + +import appeng.api.stacks.AEKey; +import com.extendedae_plus.content.ClientPatternHighlightStore; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.network.NetworkEvent; + +import java.util.function.Supplier; + +/** + * S2C: 指示客户端对某个 AEKey 的样板进行高亮/取消高亮(仅作用于接收该包的客户端) + */ +public class SetPatternHighlightS2CPacket { + private final AEKey key; + private final boolean highlight; + + public SetPatternHighlightS2CPacket(AEKey key, boolean highlight) { + this.key = key; + this.highlight = highlight; + } + + public static void encode(SetPatternHighlightS2CPacket msg, FriendlyByteBuf buf) { + AEKey.writeKey(buf, msg.key); + buf.writeBoolean(msg.highlight); + } + + public static SetPatternHighlightS2CPacket decode(FriendlyByteBuf buf) { + AEKey key = AEKey.readKey(buf); + boolean h = buf.readBoolean(); + return new SetPatternHighlightS2CPacket(key, h); + } + + public static void handle(SetPatternHighlightS2CPacket msg, Supplier ctxSupplier) { + var ctx = ctxSupplier.get(); + ctx.enqueueWork(() -> { + try { + ClientPatternHighlightStore.setHighlight(msg.key, msg.highlight); + } catch (Throwable ignored) { + } + }); + ctx.setPacketHandled(true); + } +} + + From 1ca8c8c48e422744b660d13ea944ff91f8e77be9 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Tue, 2 Sep 2025 12:46:23 +0800 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20=E5=88=B6=E4=BD=9C=E4=B8=AD?= =?UTF-8?q?=E6=89=93=E5=BC=80=E4=BE=9B=E5=BA=94=E5=99=A8=E8=B7=B3=E8=BD=AC?= =?UTF-8?q?=E5=88=B0=E5=AF=B9=E5=BA=94=E6=A0=B7=E6=9D=BF=E6=89=80=E5=9C=A8?= =?UTF-8?q?=E7=9A=84=E9=A1=B5=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/gui/GuiExPatternProviderMixin.java | 7 ++- .../CraftingMonitorOpenProviderC2SPacket.java | 30 ++++++++-- .../extendedae_plus/network/ModNetwork.java | 6 ++ .../network/SetProviderPageS2CPacket.java | 58 +++++++++++++++++++ 4 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/network/SetProviderPageS2CPacket.java 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 c4286d4..42b1ce2 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 @@ -21,6 +21,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.lang.reflect.Field; +import java.lang.reflect.Method; import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; @@ -49,8 +50,8 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen= 0) { + int pageId = foundSlot / 36; + if (pageId > 0) { + // 发送 S2C 包通知客户端切换到指定页(客户端会写入 mixin 字段并重排槽位) + ModNetwork.CHANNEL.sendTo(new SetProviderPageS2CPacket(pageId), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT); + } + } context.setPacketHandled(true); return; } catch (Throwable t) { diff --git a/src/main/java/com/extendedae_plus/network/ModNetwork.java b/src/main/java/com/extendedae_plus/network/ModNetwork.java index 8b4599c..78e3adb 100644 --- a/src/main/java/com/extendedae_plus/network/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/network/ModNetwork.java @@ -66,6 +66,12 @@ public class ModNetwork { .consumerNetworkThread(SetPatternHighlightS2CPacket::handle) .add(); + CHANNEL.messageBuilder(SetProviderPageS2CPacket.class, nextId(), NetworkDirection.PLAY_TO_CLIENT) + .encoder(SetProviderPageS2CPacket::encode) + .decoder(SetProviderPageS2CPacket::decode) + .consumerNetworkThread(SetProviderPageS2CPacket::handle) + .add(); + CHANNEL.messageBuilder(ToggleAdvancedBlockingC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER) .encoder(ToggleAdvancedBlockingC2SPacket::encode) .decoder(ToggleAdvancedBlockingC2SPacket::decode) diff --git a/src/main/java/com/extendedae_plus/network/SetProviderPageS2CPacket.java b/src/main/java/com/extendedae_plus/network/SetProviderPageS2CPacket.java new file mode 100644 index 0000000..1c57644 --- /dev/null +++ b/src/main/java/com/extendedae_plus/network/SetProviderPageS2CPacket.java @@ -0,0 +1,58 @@ +package com.extendedae_plus.network; + +import appeng.menu.SlotSemantics; +import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.network.NetworkEvent; + +import java.lang.reflect.Field; +import java.util.function.Supplier; + +/** + * S2C: 指示客户端在已打开的样板供应器界面切换到指定页 + */ +public class SetProviderPageS2CPacket { + private final int page; + + public SetProviderPageS2CPacket(int page) { + this.page = page; + } + + public static void encode(SetProviderPageS2CPacket msg, FriendlyByteBuf buf) { + buf.writeVarInt(msg.page); + } + + public static SetProviderPageS2CPacket decode(FriendlyByteBuf buf) { + int p = buf.readVarInt(); + return new SetProviderPageS2CPacket(p); + } + + public static void handle(SetProviderPageS2CPacket msg, Supplier ctxSupplier) { + var ctx = ctxSupplier.get(); + ctx.enqueueWork(() -> { + try { + Screen screen = Minecraft.getInstance().screen; + if (screen instanceof GuiExPatternProvider guiExPatternProvider) { + Field currentPage = screen.getClass().getDeclaredField("eap$currentPage"); + currentPage.setAccessible(true); + currentPage.setInt(guiExPatternProvider, msg.page); + + + guiExPatternProvider.repositionSlots(SlotSemantics.ENCODED_PATTERN); + guiExPatternProvider.repositionSlots(SlotSemantics.STORAGE); + + Field hs = screen.getClass().getDeclaredField("hoveredSlot"); + hs.setAccessible(true); + hs.set(screen, null); + } + } catch (Throwable ignored) { + } + } + ); + ctx.setPacketHandled(true); + } +} + + From 8fec6fff87561944b468e141f453a303505e3b07 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Tue, 2 Sep 2025 19:24:04 +0800 Subject: [PATCH 4/9] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E5=88=B6=E4=BD=9C?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E8=B7=B3=E8=BD=AC=E6=A0=B7=E6=9D=BF=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E6=89=93=E5=BC=80=E6=9C=AA=E6=B4=BB=E8=B7=83=E4=BE=9B?= =?UTF-8?q?=E5=BA=94=E5=99=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CraftingMonitorOpenProviderC2SPacket.java | 79 +++++++----- .../util/PatternProviderDataUtil.java | 122 +++++------------- 2 files changed, 84 insertions(+), 117 deletions(-) diff --git a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java index feb3285..5f984b2 100644 --- a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java @@ -13,7 +13,6 @@ import appeng.menu.me.crafting.CraftingCPUMenu; import appeng.parts.AEBasePart; import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor; import com.extendedae_plus.util.PatternProviderDataUtil; -import com.mojang.logging.LogUtils; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; import net.minecraftforge.network.NetworkDirection; @@ -49,11 +48,8 @@ public class CraftingMonitorOpenProviderC2SPacket { ServerPlayer player = context.getSender(); if (player == null) return; - LogUtils.getLogger().info("EAP[S]: recv CraftingMonitorOpenProviderC2SPacket key={} from {}", msg.what, player.getGameProfile().getName()); - // 必须在 CraftingCPU 界面内 if (!(player.containerMenu instanceof CraftingCPUMenu menu)) { - LogUtils.getLogger().info("EAP[S]: not in CraftingCPUMenu, abort"); return; } @@ -64,19 +60,16 @@ public class CraftingMonitorOpenProviderC2SPacket { grid = host.getActionableNode().getGrid(); } if (grid == null) { - LogUtils.getLogger().info("EAP[S]: grid is null, abort"); return; } var cs = grid.getCraftingService(); if (!(cs instanceof CraftingService craftingService)) { - LogUtils.getLogger().info("EAP[S]: craftingService is null/unsupported, abort"); return; } // 1) 根据 AEKey 找到可能的样板(pattern) Collection patterns = craftingService.getCraftingFor(msg.what); - LogUtils.getLogger().info("EAP[S]: patterns found={} for key={}", patterns.size(), msg.what); if (patterns.isEmpty()) { return; } @@ -91,14 +84,35 @@ public class CraftingMonitorOpenProviderC2SPacket { if (host == null) continue; var pbe = host.getBlockEntity(); if (pbe == null) continue; - // 在服务端上下文中执行,pbe 仅用于构造菜单定位器 + + // 跳过未连接到网格或不活跃的 provider(例如缺少频道/通道) + try { + var providerGrid = ppl.getGrid(); + if (providerGrid == null || !providerGrid.equals(grid)) { + continue; + } + // 如果 provider 自身对外提供的通道/频道信息不可用或不活跃,跳过 + try { + // 尝试通过 provider 的主节点判断是否有 channel + var mainNodeField = ppl.getClass().getDeclaredField("mainNode"); + mainNodeField.setAccessible(true); + var mainNode = mainNodeField.get(ppl); + if (mainNode == null) continue; + var getChannelsMethod = mainNode.getClass().getMethod("getChannels"); + Object channels = null; + channels = getChannelsMethod.invoke(mainNode); + if (channels instanceof java.util.Collection) { + if (((java.util.Collection) channels).isEmpty()) continue; + } + } catch (Exception ignored) { + // 无法判断 channel 时继续:不因反射失败而阻止正常 provider + } + } catch (Exception e) { + continue; + } // 直接打开供应器自身的 UI(调用 Host 默认方法) try { - // 告知目标玩家客户端高亮该 AEKey(避免全局服务端状态污染) - AEKey key = pattern.getOutputs()[0].what(); - ModNetwork.CHANNEL.sendTo(new SetPatternHighlightS2CPacket(key, true), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT); - // 部件与方块实体分别选择定位器并打开界面 if (host instanceof AEBasePart part) { host.openMenu(player, MenuLocators.forPart(part)); @@ -106,18 +120,8 @@ public class CraftingMonitorOpenProviderC2SPacket { host.openMenu(player, MenuLocators.forBlockEntity(pbe)); } - // 先在该 provider 中定位 pattern 的槽位索引,以便计算页码 - int foundSlot = -1; - var list = PatternProviderDataUtil.getAllPatternData(ppl); - for (var pd : list) { - - if (pd != null && pd.getPatternDetails() != null - && pd.getPatternDetails().getDefinition().equals(pattern.getDefinition())) { - foundSlot = pd.getSlotIndex(); - break; - } - - } + // 先在该 provider 中定位 pattern 的槽位索引,以便计算页码(尽量早退出,按槽位逐个解码) + int foundSlot = findSlotIndexInProvider(ppl, pattern); if (foundSlot >= 0) { int pageId = foundSlot / 36; if (pageId > 0) { @@ -125,19 +129,36 @@ public class CraftingMonitorOpenProviderC2SPacket { ModNetwork.CHANNEL.sendTo(new SetProviderPageS2CPacket(pageId), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT); } } - context.setPacketHandled(true); + + // 最后发送高亮包,保证界面已打开 + if (pattern.getOutputs() != null && pattern.getOutputs().length > 0 && pattern.getOutputs()[0] != null) { + AEKey key = pattern.getOutputs()[0].what(); + ModNetwork.CHANNEL.sendTo(new SetPatternHighlightS2CPacket(key, true), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT); + } + return; - } catch (Throwable t) { - LogUtils.getLogger().error("EAP[S]: open provider UI failed at {}", pbe.getBlockPos(), t); + } catch (Exception ignored) { } } } } - - LogUtils.getLogger().info("EAP[S]: no provider UI opened for key={}", msg.what); }); context.setPacketHandled(true); } + private static int findSlotIndexInProvider(PatternProviderLogic ppl, IPatternDetails pattern) { + try { + // 通过逐槽位解码并在找到匹配定义时立即返回索引,避免分配大量对象 + var list = PatternProviderDataUtil.getAllPatternData(ppl); + for (var pd : list) { + if (pd != null && pd.getPatternDetails() != null && pd.getPatternDetails().getDefinition().equals(pattern.getDefinition())) { + return pd.getSlotIndex(); + } + } + } catch (Exception ignored) { + } + return -1; + } + } diff --git a/src/main/java/com/extendedae_plus/util/PatternProviderDataUtil.java b/src/main/java/com/extendedae_plus/util/PatternProviderDataUtil.java index 15b0ad7..c9285c1 100644 --- a/src/main/java/com/extendedae_plus/util/PatternProviderDataUtil.java +++ b/src/main/java/com/extendedae_plus/util/PatternProviderDataUtil.java @@ -6,8 +6,11 @@ import appeng.api.inventories.InternalInventory; import appeng.api.stacks.AEKey; import appeng.api.stacks.GenericStack; import appeng.helpers.patternprovider.PatternProviderLogic; +import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import appeng.helpers.patternprovider.PatternProviderLogicHost; +import net.minecraft.world.level.block.entity.BlockEntity; import java.util.ArrayList; import java.util.HashMap; @@ -230,26 +233,8 @@ public class PatternProviderDataUtil { if (patternInventory == null) { return patternDataList; } - - // 通过反射安全地访问host字段获取Level - Level level = null; - try { - var hostField = patternProvider.getClass().getDeclaredField("host"); - hostField.setAccessible(true); - var host = hostField.get(patternProvider); - if (host != null) { - var getBlockEntityMethod = host.getClass().getMethod("getBlockEntity"); - var blockEntity = getBlockEntityMethod.invoke(host); - if (blockEntity != null) { - var getLevelMethod = blockEntity.getClass().getMethod("getLevel"); - level = (Level) getLevelMethod.invoke(blockEntity); - } - } - } catch (Exception e) { - // 如果反射失败,返回空列表 - return patternDataList; - } - + // 获取 Level(使用 mixin accessor 替代反射) + Level level = getPatternProviderLevel(patternProvider); if (level == null) { return patternDataList; } @@ -353,24 +338,7 @@ public class PatternProviderDataUtil { return null; } - // 通过反射安全地访问host字段获取Level - Level level = null; - try { - var hostField = patternProvider.getClass().getDeclaredField("host"); - hostField.setAccessible(true); - var host = hostField.get(patternProvider); - if (host != null) { - var getBlockEntityMethod = host.getClass().getMethod("getBlockEntity"); - var blockEntity = getBlockEntityMethod.invoke(host); - if (blockEntity != null) { - var getLevelMethod = blockEntity.getClass().getMethod("getLevel"); - level = (Level) getLevelMethod.invoke(blockEntity); - } - } - } catch (Exception e) { - return null; - } - + Level level = getPatternProviderLevel(patternProvider); if (level == null) { return null; } @@ -862,50 +830,30 @@ public class PatternProviderDataUtil { // 1. 设置物品到库存 patternInventory.setItemDirect(slot, newPattern); - // 2. 标记数据为脏数据,确保保存到磁盘 + // 2. 标记数据为脏数据,确保保存到磁盘(尝试使用 mixin accessor 替代反射) try { - // 通过反射获取host并标记为脏数据 - var hostField = patternProvider.getClass().getDeclaredField("host"); - hostField.setAccessible(true); - var host = hostField.get(patternProvider); - - if (host != null) { - // 获取BlockEntity并标记为脏数据 - var getBlockEntityMethod = host.getClass().getMethod("getBlockEntity"); - var blockEntity = getBlockEntityMethod.invoke(host); - - if (blockEntity != null) { - // 调用setChanged()方法标记为脏数据 - var setChangedMethod = blockEntity.getClass().getMethod("setChanged"); - setChangedMethod.invoke(blockEntity); - - // 尝试触发网络同步 - try { - var levelField = blockEntity.getClass().getSuperclass().getDeclaredField("level"); - levelField.setAccessible(true); - Level level = (Level) levelField.get(blockEntity); - - if (level != null && !level.isClientSide()) { - // 服务器端:强制同步到客户端 - var getBlockPosMethod = blockEntity.getClass().getMethod("getBlockPos"); - var blockPos = getBlockPosMethod.invoke(blockEntity); - - if (blockPos != null) { - // 通知客户端方块状态变更 - var getBlockStateMethod = blockEntity.getClass().getMethod("getBlockState"); - var blockState = getBlockStateMethod.invoke(blockEntity); - level.sendBlockUpdated((net.minecraft.core.BlockPos) blockPos, - (net.minecraft.world.level.block.state.BlockState) blockState, - (net.minecraft.world.level.block.state.BlockState) blockState, 3); - } + if (patternProvider instanceof PatternProviderLogicAccessor accessor) { + var host = accessor.eap$host(); + if (host != null) { + BlockEntity be = host.getBlockEntity(); + if (be != null) { + try { + be.setChanged(); + } catch (Exception ignored) { + } + try { + Level level = be.getLevel(); + if (level != null && !level.isClientSide()) { + var pos = be.getBlockPos(); + var state = be.getBlockState(); + level.sendBlockUpdated(pos, state, state, 3); + } + } catch (Exception ignored) { } - } catch (Exception syncException) { - // 网络同步失败不影响主要功能 } } } - } catch (Exception e) { - // 如果反射失败,使用备用方案 + } catch (Exception ignored) { } // 3. 强制更新样板缓存 @@ -920,20 +868,18 @@ public class PatternProviderDataUtil { * ExtendedAE风格:安全获取样板供应器的Level对象 */ private static Level getPatternProviderLevel(PatternProviderLogic patternProvider) { + if (patternProvider == null) return null; try { - var hostField = patternProvider.getClass().getDeclaredField("host"); - hostField.setAccessible(true); - var host = hostField.get(patternProvider); - if (host != null) { - var getBlockEntityMethod = host.getClass().getMethod("getBlockEntity"); - var blockEntity = getBlockEntityMethod.invoke(host); - if (blockEntity != null) { - var getLevelMethod = blockEntity.getClass().getMethod("getLevel"); - return (Level) getLevelMethod.invoke(blockEntity); + if (patternProvider instanceof PatternProviderLogicAccessor accessor) { + var host = accessor.eap$host(); + if (host != null) { + BlockEntity be = host.getBlockEntity(); + if (be != null) { + return be.getLevel(); + } } } - } catch (Exception e) { - // 静默处理异常,返回null让调用者处理 + } catch (Exception ignored) { } return null; } From 146cce9de18fb508bc8a5e9a82c66ea001520d5c Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Tue, 2 Sep 2025 19:43:00 +0800 Subject: [PATCH 5/9] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PatternProviderLogicAccessor.java | 4 + .../CraftingMonitorOpenProviderC2SPacket.java | 45 +------ .../util/PatternProviderDataUtil.java | 112 ++++++++++++++++-- 3 files changed, 107 insertions(+), 54 deletions(-) diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicAccessor.java index 7cba84b..1c93b75 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicAccessor.java @@ -1,5 +1,6 @@ package com.extendedae_plus.mixin.ae2.accessor; +import appeng.api.networking.IManagedGridNode; import appeng.helpers.patternprovider.PatternProviderLogic; import appeng.helpers.patternprovider.PatternProviderLogicHost; import org.spongepowered.asm.mixin.Mixin; @@ -9,4 +10,7 @@ import org.spongepowered.asm.mixin.gen.Accessor; public interface PatternProviderLogicAccessor { @Accessor("host") PatternProviderLogicHost eap$host(); + + @Accessor("mainNode") + IManagedGridNode eap$mainNode(); } diff --git a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java index 5f984b2..f8e198f 100644 --- a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java @@ -85,31 +85,8 @@ public class CraftingMonitorOpenProviderC2SPacket { var pbe = host.getBlockEntity(); if (pbe == null) continue; - // 跳过未连接到网格或不活跃的 provider(例如缺少频道/通道) - try { - var providerGrid = ppl.getGrid(); - if (providerGrid == null || !providerGrid.equals(grid)) { - continue; - } - // 如果 provider 自身对外提供的通道/频道信息不可用或不活跃,跳过 - try { - // 尝试通过 provider 的主节点判断是否有 channel - var mainNodeField = ppl.getClass().getDeclaredField("mainNode"); - mainNodeField.setAccessible(true); - var mainNode = mainNodeField.get(ppl); - if (mainNode == null) continue; - var getChannelsMethod = mainNode.getClass().getMethod("getChannels"); - Object channels = null; - channels = getChannelsMethod.invoke(mainNode); - if (channels instanceof java.util.Collection) { - if (((java.util.Collection) channels).isEmpty()) continue; - } - } catch (Exception ignored) { - // 无法判断 channel 时继续:不因反射失败而阻止正常 provider - } - } catch (Exception e) { - continue; - } + // 跳过未连接到网格或不活跃的 provider(使用 util 判断并传入当前 grid) + if (!PatternProviderDataUtil.isProviderAvailable(ppl, grid)) continue; // 直接打开供应器自身的 UI(调用 Host 默认方法) try { @@ -121,7 +98,7 @@ public class CraftingMonitorOpenProviderC2SPacket { } // 先在该 provider 中定位 pattern 的槽位索引,以便计算页码(尽量早退出,按槽位逐个解码) - int foundSlot = findSlotIndexInProvider(ppl, pattern); + int foundSlot = PatternProviderDataUtil.findSlotForPattern(ppl, pattern.getDefinition()); if (foundSlot >= 0) { int pageId = foundSlot / 36; if (pageId > 0) { @@ -145,20 +122,4 @@ public class CraftingMonitorOpenProviderC2SPacket { }); context.setPacketHandled(true); } - - private static int findSlotIndexInProvider(PatternProviderLogic ppl, IPatternDetails pattern) { - try { - // 通过逐槽位解码并在找到匹配定义时立即返回索引,避免分配大量对象 - var list = PatternProviderDataUtil.getAllPatternData(ppl); - for (var pd : list) { - if (pd != null && pd.getPatternDetails() != null && pd.getPatternDetails().getDefinition().equals(pattern.getDefinition())) { - return pd.getSlotIndex(); - } - } - } catch (Exception ignored) { - } - return -1; - } - - } diff --git a/src/main/java/com/extendedae_plus/util/PatternProviderDataUtil.java b/src/main/java/com/extendedae_plus/util/PatternProviderDataUtil.java index c9285c1..9209f3e 100644 --- a/src/main/java/com/extendedae_plus/util/PatternProviderDataUtil.java +++ b/src/main/java/com/extendedae_plus/util/PatternProviderDataUtil.java @@ -3,13 +3,14 @@ package com.extendedae_plus.util; import appeng.api.crafting.IPatternDetails; import appeng.api.crafting.PatternDetailsHelper; import appeng.api.inventories.InternalInventory; +import appeng.api.networking.IGrid; import appeng.api.stacks.AEKey; import appeng.api.stacks.GenericStack; import appeng.helpers.patternprovider.PatternProviderLogic; import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor; +import com.mojang.logging.LogUtils; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; -import appeng.helpers.patternprovider.PatternProviderLogicHost; import net.minecraft.world.level.block.entity.BlockEntity; import java.util.ArrayList; @@ -242,12 +243,15 @@ public class PatternProviderDataUtil { // 遍历所有样板槽位 for (int i = 0; i < patternInventory.size(); i++) { ItemStack patternStack = patternInventory.getStackInSlot(i); - if (!patternStack.isEmpty()) { + if (patternStack.isEmpty()) continue; + try { // 解码样板 IPatternDetails patternDetails = PatternDetailsHelper.decodePattern(patternStack, level); if (patternDetails != null) { patternDataList.add(new PatternData(patternDetails, patternStack, i)); } + } catch (Exception e) { + if (DEBUG) LogUtils.getLogger().debug("Pattern decode failed at slot {}: {}", i, e.toString()); } } @@ -397,6 +401,59 @@ public class PatternProviderDataUtil { return patternProvider.getGrid() != null; } + /** + * 判断 provider 是否可用并属于指定网格(在线且有频道/处于活跃状态) + */ + public static boolean isProviderAvailable(PatternProviderLogic provider, IGrid expectedGrid) { + if (provider == null || expectedGrid == null) return false; + try { + var grid = provider.getGrid(); + if (grid == null || !grid.equals(expectedGrid)) return false; + + // 使用 accessor 获取 mainNode,再调用 isActive + if (provider instanceof PatternProviderLogicAccessor accessor) { + var mainNode = accessor.eap$mainNode(); + if (mainNode == null) return false; + try { + var isActiveMethod = mainNode.getClass().getMethod("isActive"); + Object active = isActiveMethod.invoke(mainNode); + if (active instanceof Boolean && !((Boolean) active)) return false; + } catch (NoSuchMethodException nsme) { + // 没有 isActive 方法时,退回到检查 channels + try { + var getChannels = mainNode.getClass().getMethod("getChannels"); + Object channels = getChannels.invoke(mainNode); + if (channels instanceof java.util.Collection) { + if (((java.util.Collection) channels).isEmpty()) return false; + } + } catch (Exception ignored) { + // 无法判断 channels 时,认为不可用 + return false; + } + } + } else { + // 没有 accessor 的情况,尽量通过反射判断 mainNode.channels + try { + var mainNodeField = provider.getClass().getDeclaredField("mainNode"); + mainNodeField.setAccessible(true); + var mainNode = mainNodeField.get(provider); + if (mainNode == null) return false; + var getChannelsMethod = mainNode.getClass().getMethod("getChannels"); + Object channels = getChannelsMethod.invoke(mainNode); + if (channels instanceof java.util.Collection) { + return !((java.util.Collection) channels).isEmpty(); + } + } catch (Exception e) { + return false; + } + } + + return true; + } catch (Exception e) { + return false; + } + } + /** * 检查样板供应器是否处于活跃状态 * @@ -411,18 +468,21 @@ public class PatternProviderDataUtil { if (grid == null) { return false; } - // 检查网格节点是否活跃 + // 检查网格节点是否活跃(使用 accessor 代替反射) try { - // 使用反射安全地访问mainNode字段 - var mainNodeField = patternProvider.getClass().getDeclaredField("mainNode"); - mainNodeField.setAccessible(true); - var mainNode = mainNodeField.get(patternProvider); - if (mainNode != null) { - var isActiveMethod = mainNode.getClass().getMethod("isActive"); - return (Boolean) isActiveMethod.invoke(mainNode); + if (patternProvider instanceof PatternProviderLogicAccessor accessor) { + var mainNode = accessor.eap$mainNode(); + if (mainNode != null) { + try { + var isActiveMethod = mainNode.getClass().getMethod("isActive"); + return (Boolean) isActiveMethod.invoke(mainNode); + } catch (Exception e) { + // 无法调用 isActive 时,认为活跃 + return true; + } + } } - } catch (Exception e) { - // 如果反射失败,假设是活跃的 + } catch (Exception ignored) { } return true; } @@ -490,6 +550,34 @@ public class PatternProviderDataUtil { return scalePatternAmountsExtendedAEStyle(patternProvider, multiplier, true); } + /** + * 查找 provider 中匹配给定定义的样板槽位(轻量、按需解码并早退出) + * @param patternProvider 要搜索的 provider + * @param targetDefinition pattern.getDefinition() 返回的对象(用于 equals 比较) + * @return 找到的槽位索引,未找到返回 -1 + */ + public static int findSlotForPattern(PatternProviderLogic patternProvider, Object targetDefinition) { + if (patternProvider == null || targetDefinition == null) return -1; + InternalInventory inv = patternProvider.getPatternInv(); + if (inv == null) return -1; + Level level = getPatternProviderLevel(patternProvider); + if (level == null) return -1; + + for (int i = 0; i < inv.size(); i++) { + ItemStack s = inv.getStackInSlot(i); + if (s.isEmpty()) continue; + try { + IPatternDetails d = PatternDetailsHelper.decodePattern(s, level); + if (d != null && d.getDefinition().equals(targetDefinition)) { + return i; + } + } catch (Exception e) { + if (DEBUG) LogUtils.getLogger().debug("findSlotForPattern decode failed at {}: {}", i, e.toString()); + } + } + return -1; + } + /** * ExtendedAE风格的样板复制倍增 * 支持更精确的样板处理和错误恢复 From 88e65874862ed8c9dc79d731a813be39ea903418 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Tue, 2 Sep 2025 20:55:14 +0800 Subject: [PATCH 6/9] =?UTF-8?q?feat:=E6=B7=BB=E5=8A=A0=E4=BE=9B=E5=BA=94?= =?UTF-8?q?=E5=99=A8Gui=E7=95=8C=E9=9D=A2=E6=98=BE=E7=A4=BA=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E7=9A=84=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae2/client/gui/AEBaseScreenMixin.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) 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 0fcb7bd..b741be3 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 @@ -6,6 +6,7 @@ import appeng.client.Point; import appeng.client.gui.AEBaseScreen; import appeng.client.gui.StackWithBounds; import appeng.client.gui.TextOverride; +import appeng.client.gui.implementations.PatternProviderScreen; import appeng.client.gui.me.crafting.CraftingCPUScreen; import appeng.client.gui.style.PaletteColor; import appeng.client.gui.style.ScreenStyle; @@ -29,6 +30,7 @@ import net.minecraft.network.chat.contents.TranslatableContents; import net.minecraft.world.inventory.Slot; import org.jetbrains.annotations.Nullable; 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; @@ -308,4 +310,21 @@ public abstract class AEBaseScreenMixin { } catch (Throwable ignored) { } } + + + @Shadow + 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){ + Component t = screen.getTitle(); + if (t != null && !t.getString().isEmpty()) { + this.setTextContent(AEBaseScreen.TEXT_ID_DIALOG_TITLE, t); + } + } + } catch (Throwable ignored) {} + } } From b1a562a7e3fec19dc604ae63b4db8d55beefc063 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Tue, 2 Sep 2025 22:11:19 +0800 Subject: [PATCH 7/9] =?UTF-8?q?feat:=E6=B7=BB=E5=8A=A0=E5=88=B6=E4=BD=9C?= =?UTF-8?q?=E4=B8=AD=E8=B7=B3=E8=BD=AC=E6=A0=B7=E6=9D=BF=E4=BE=9B=E5=BA=94?= =?UTF-8?q?=E5=99=A8=E7=95=8C=E9=9D=A2=E5=90=8C=E6=97=B6=E9=AB=98=E4=BA=AE?= =?UTF-8?q?=E5=AF=B9=E5=BA=94=E6=A0=B7=E6=9D=BF=E4=BE=9B=E5=BA=94=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CraftingMonitorOpenProviderC2SPacket.java | 42 +++++++++++++++++++ .../resources/extendedae_plus.mixins.json | 1 - 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java index f8e198f..6680f2f 100644 --- a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java @@ -13,14 +13,26 @@ import appeng.menu.me.crafting.CraftingCPUMenu; import appeng.parts.AEBasePart; import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor; import com.extendedae_plus.util.PatternProviderDataUtil; +import com.glodblock.github.extendedae.util.FCClientUtil; +import com.glodblock.github.glodium.util.GlodUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.AABB; import net.minecraftforge.network.NetworkDirection; import net.minecraftforge.network.NetworkEvent; import java.util.Collection; +import java.util.Objects; import java.util.function.Supplier; +import static com.glodblock.github.extendedae.client.render.EAEHighlightHandler.highlight; + /** * 客户端从 CraftingCPUScreen 发送:鼠标下条目对应的 AEKey。 * 服务端在当前打开的 CraftingCPUMenu 所属网络中,定位匹配该 AEKey 的样板供应器, @@ -93,10 +105,15 @@ public class CraftingMonitorOpenProviderC2SPacket { // 部件与方块实体分别选择定位器并打开界面 if (host instanceof AEBasePart part) { host.openMenu(player, MenuLocators.forPart(part)); + highlightWithMessage(pbe.getBlockPos(), part.getSide(), Objects.requireNonNull(pbe.getLevel()).dimension(), 1.0, player); } else { host.openMenu(player, MenuLocators.forBlockEntity(pbe)); + highlightWithMessage(pbe.getBlockPos(), null, Objects.requireNonNull(pbe.getLevel()).dimension(), 1.0, player); } + // 高亮打开的供应器位置并发送聊天提示 + + // 先在该 provider 中定位 pattern 的槽位索引,以便计算页码(尽量早退出,按槽位逐个解码) int foundSlot = PatternProviderDataUtil.findSlotForPattern(ppl, pattern.getDefinition()); if (foundSlot >= 0) { @@ -122,4 +139,29 @@ public class CraftingMonitorOpenProviderC2SPacket { }); context.setPacketHandled(true); } + + private static void highlightWithMessage(BlockPos pos, Direction face, ResourceKey dim, double multiplier, Player player) { + if (pos == null || dim == null) { + return; + } + long endTime = System.currentTimeMillis() + (long) (6000 * GlodUtil.clamp(multiplier, 1, 30)); + if (face == null) { + highlight(pos, dim, endTime); + } else { + var origin = new AABB(2 / 16D, 2 / 16D, 0, 14 / 16D, 14 / 16D, 2 / 16D).move(pos); + var center = new AABB(pos).getCenter(); + switch (face) { + case WEST -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.Y, (float) (Math.PI / 2)); + case SOUTH -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.Y, (float) Math.PI); + case EAST -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.Y, (float) (-Math.PI / 2)); + case UP -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.X, (float) (-Math.PI / 2)); + case DOWN -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.X, (float) (Math.PI / 2)); + } + highlight(pos, face, dim, endTime, origin); + } + + if (player != null) { + player.displayClientMessage(Component.translatable("chat.ex_pattern_access_terminal.pos", pos.toShortString(), dim.location().getPath()), false); + } + } } diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 5772e34..6e1c457 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -31,7 +31,6 @@ "mixins": [ "ae2.AEProcessingPatternMixin", "ae2.CraftingCPUClusterMixin", - "ae2.IPatternDetailsMixin", "ae2.accessor.MEStorageMenuAccessor", "ae2.accessor.PatternEncodingTermMenuAccessor", "ae2.accessor.PatternProviderLogicAccessor", From 2547cd1abddcf8085e5c6bf1839f8cd96243208d Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Tue, 2 Sep 2025 22:16:17 +0800 Subject: [PATCH 8/9] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=9C=AA=E7=94=A8?= =?UTF-8?q?=E5=88=B0=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/PatternHighlightStore.java | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 src/main/java/com/extendedae_plus/content/PatternHighlightStore.java diff --git a/src/main/java/com/extendedae_plus/content/PatternHighlightStore.java b/src/main/java/com/extendedae_plus/content/PatternHighlightStore.java deleted file mode 100644 index 6afba04..0000000 --- a/src/main/java/com/extendedae_plus/content/PatternHighlightStore.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.extendedae_plus.content; - -import appeng.api.crafting.IPatternDetails; - -import java.util.Collections; -import java.util.Map; -import java.util.WeakHashMap; - -public final class PatternHighlightStore { - // 使用同步的 WeakHashMap 存储高亮状态,键为 IPatternDetails,值为 Boolean - private static final Map HIGHLIGHTS = Collections.synchronizedMap(new WeakHashMap<>()); - - // 私有构造方法,防止实例化 - private PatternHighlightStore() {} - - /** - * 设置指定 details 的高亮状态。 - * @param details 需要设置的 IPatternDetails 实例 - * @param highlighted 是否高亮 - */ - public static void setHighlight(IPatternDetails details, boolean highlighted) { - if (details == null) return; - if (highlighted) { - HIGHLIGHTS.put(details, Boolean.TRUE); // 设置为高亮 - } else { - HIGHLIGHTS.remove(details); // 移除高亮 - } - } - - /** - * 获取指定 details 的高亮状态。 - * @param details 需要查询的 IPatternDetails 实例 - * @return 是否高亮 - */ - public static boolean getHighlight(IPatternDetails details) { - if (details == null) return false; - Boolean v = HIGHLIGHTS.get(details); - return v != null && v; - } - - /** - * 清空所有高亮状态(在供应器界面关闭时调用)。 - */ - public static void clearAll() { - HIGHLIGHTS.clear(); - } -} \ No newline at end of file From d9e0a720e1dafcffdea52f1b096eace120c20882 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Tue, 2 Sep 2025 23:46:14 +0800 Subject: [PATCH 9/9] =?UTF-8?q?fix=EF=BC=9A=E9=BB=98=E8=AE=A4=E6=A0=B7?= =?UTF-8?q?=E6=9D=BF=E4=B8=8D=E7=BF=BB=E5=80=8D=EF=BC=8C=E9=81=BF=E5=85=8D?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E6=9C=AA=E8=BF=9B=E8=A1=8C=E9=80=82=E9=85=8D?= =?UTF-8?q?=E7=9A=84=E4=BE=9B=E5=BA=94=E5=99=A8=E6=97=A0=E6=B3=95=E5=8F=91?= =?UTF-8?q?=E9=85=8D=E6=9D=90=E6=96=99=E3=80=82=E5=8E=BB=E6=8E=89=E5=AF=B9?= =?UTF-8?q?=E9=AB=98=E7=BA=A7ae=E7=9A=84=E6=A0=B7=E6=9D=BF=E4=BE=9B?= =?UTF-8?q?=E5=BA=94=E5=99=A8=E7=BF=BB=E5=80=8D=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mixin/ae2/AEProcessingPatternMixin.java | 2 +- ...ernProviderLogicContainsRedirectMixin.java | 39 ------------------- 2 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/autopattern/adaptation/AdvPatternProviderLogicContainsRedirectMixin.java diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/AEProcessingPatternMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/AEProcessingPatternMixin.java index 4381601..28030b2 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/AEProcessingPatternMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/AEProcessingPatternMixin.java @@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.Unique; @Mixin(value = AEProcessingPattern.class, remap = false) public class AEProcessingPatternMixin implements SmartDoublingAwarePattern { @Unique - private boolean eap$allowScaling = true; // 默认允许缩放 + private boolean eap$allowScaling = false; // 默认不允许缩放 @Override public boolean eap$allowScaling() { diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/adaptation/AdvPatternProviderLogicContainsRedirectMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/adaptation/AdvPatternProviderLogicContainsRedirectMixin.java deleted file mode 100644 index 1ff3e78..0000000 --- a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/adaptation/AdvPatternProviderLogicContainsRedirectMixin.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.extendedae_plus.mixin.ae2.autopattern.adaptation; - -import appeng.api.crafting.IPatternDetails; -import com.extendedae_plus.content.ScaledProcessingPattern; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Pseudo; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -import java.util.List; - -/**适配 - * Redirect PatternProviderLogic.pushPattern 中对 List.contains 的调用, - * 在遇到缩放样板时回退匹配到原始样板实例。 - */ -@Pseudo -@Mixin(targets = "net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic", remap = false) -public class AdvPatternProviderLogicContainsRedirectMixin { - - @Redirect(method = "pushPattern", - at = @At( - value = "INVOKE", - target = "Ljava/util/List;contains(Ljava/lang/Object;)Z") - ) - private boolean eap$patternsContains(List list, Object o) { - try { - if (o instanceof ScaledProcessingPattern scaled) { - IPatternDetails base = scaled.getOriginal(); - if (base != null && list.indexOf(base) != -1) { - return true; - } - } - // 使用 indexOf 避免再次触发对 List.contains 的 redirect(防止递归) - return list.indexOf(o) != -1; - } catch (Throwable t) { - return list.indexOf(o) != -1; - } - } -} \ No newline at end of file