From a75d7fbc5c24053e24af02830952f43358e7f04f Mon Sep 17 00:00:00 2001 From: GaLi <133291877+GaLicn@users.noreply.github.com> Date: Wed, 27 Aug 2025 22:19:55 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=90=88=E6=88=90=E7=9B=91?= =?UTF-8?q?=E6=8E=A7=E7=95=8C=E9=9D=A2shift=E5=B7=A6=E9=94=AE=E6=89=93?= =?UTF-8?q?=E5=BC=80=E6=9C=BA=E5=99=A8ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EPlusCraftingCubeModelProvider.java | 6 +- .../mixin/ae2/AEBaseScreenMixin.java | 35 ++++++ .../ae2/accessor/CraftingCPUMenuAccessor.java | 12 -- .../network/CraftingMonitorJumpC2SPacket.java | 55 ++++----- .../CraftingMonitorOpenProviderC2SPacket.java | 115 ++++++++++++++++++ .../extendedae_plus/network/ModNetwork.java | 6 + .../resources/extendedae_plus.mixins.json | 1 - 7 files changed, 186 insertions(+), 44 deletions(-) delete mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingCPUMenuAccessor.java create mode 100644 src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java diff --git a/src/main/java/com/extendedae_plus/client/render/crafting/EPlusCraftingCubeModelProvider.java b/src/main/java/com/extendedae_plus/client/render/crafting/EPlusCraftingCubeModelProvider.java index 3e98d51..850836a 100644 --- a/src/main/java/com/extendedae_plus/client/render/crafting/EPlusCraftingCubeModelProvider.java +++ b/src/main/java/com/extendedae_plus/client/render/crafting/EPlusCraftingCubeModelProvider.java @@ -21,7 +21,7 @@ import java.util.List; import java.util.function.Function; /** - * 参照 MAE2 的 DynamicCraftingCubeModelProvider,实现形成态光照模型。 + * 形成态光照模型。 */ public class EPlusCraftingCubeModelProvider extends AbstractCraftingUnitModelProvider { @@ -29,13 +29,13 @@ public class EPlusCraftingCubeModelProvider public static final ChunkRenderTypeSet CUTOUT = ChunkRenderTypeSet.of(RenderType.cutout()); private static final List MATERIALS = new ArrayList<>(); - // 与 MAE2 一致:将环形边框与基础发光底图放在本模组命名空间 + //将环形边框与基础发光底图放在本模组命名空间 protected static final Material RING_CORNER = texture(ExtendedAEPlus.MODID, "ring_corner"); protected static final Material RING_SIDE_HOR = texture(ExtendedAEPlus.MODID, "ring_side_hor"); protected static final Material RING_SIDE_VER = texture(ExtendedAEPlus.MODID, "ring_side_ver"); protected static final Material LIGHT_BASE = texture(ExtendedAEPlus.MODID, "light_base"); - // 我们自己的亮面贴图(formed 时使用) + // 亮面贴图(formed 时使用) protected static final Material ACCELERATOR_4X_LIGHT = texture(ExtendedAEPlus.MODID, "4x_accelerator_light"); protected static final Material ACCELERATOR_16X_LIGHT = texture(ExtendedAEPlus.MODID, diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/AEBaseScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/AEBaseScreenMixin.java index d6cbb04..4194d24 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/AEBaseScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/AEBaseScreenMixin.java @@ -14,6 +14,7 @@ import appeng.menu.slot.AppEngSlot; import com.extendedae_plus.api.ExPatternPageAccessor; import com.extendedae_plus.network.CraftingMonitorJumpC2SPacket; import com.extendedae_plus.network.ModNetwork; +import com.extendedae_plus.network.CraftingMonitorOpenProviderC2SPacket; import com.extendedae_plus.util.GuiUtil; import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; import com.mojang.logging.LogUtils; @@ -79,6 +80,40 @@ public abstract class AEBaseScreenMixin { } } + /** + * 在 AEBaseScreen 的 mouseClicked 入口拦截 CraftingCPUScreen 的 Shift+右键, + * 读取鼠标下的 AEKey 并发送 CraftingMonitorOpenProviderC2SPacket(打开样板供应器UI)。 + */ + @Inject(method = "mouseClicked", at = @At("HEAD"), cancellable = true) + private void eap$craftingCpuShiftRightClick(double mouseX, double mouseY, int button, CallbackInfoReturnable cir) { + // 仅处理 CraftingCPUScreen 实例 + Object self = this; + if (!(self instanceof CraftingCPUScreen screen)) { + return; + } + // 仅在 Shift + 右键 时触发 + if (button != 1 || !net.minecraft.client.gui.screens.Screen.hasShiftDown()) { + return; + } + try { + StackWithBounds hovered = screen.getStackUnderMouse(mouseX, mouseY); + if (hovered == null || hovered.stack() == null) { + return; + } + AEKey key = hovered.stack().what(); + if (key == null) { + return; + } + // Debug: 标记一次发送(打开供应器UI) + try { + LogUtils.getLogger().info("EAP: Send CraftingMonitorOpenProviderC2SPacket: {}", key); + } catch (Throwable ignored2) {} + ModNetwork.CHANNEL.sendToServer(new CraftingMonitorOpenProviderC2SPacket(key)); + cir.setReturnValue(true); + } catch (Throwable ignored) { + } + } + @Unique private static int eap$getIntField(Object self, String name, int def) { Class c = self.getClass(); diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingCPUMenuAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingCPUMenuAccessor.java deleted file mode 100644 index 96684a6..0000000 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingCPUMenuAccessor.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.extendedae_plus.mixin.ae2.accessor; - -import appeng.api.networking.IGrid; -import appeng.menu.me.crafting.CraftingCPUMenu; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(CraftingCPUMenu.class) -public interface CraftingCPUMenuAccessor { - @Accessor("grid") - IGrid getGrid(); -} diff --git a/src/main/java/com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java b/src/main/java/com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java index 6211f57..eacd863 100644 --- a/src/main/java/com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java @@ -8,9 +8,7 @@ import appeng.api.stacks.AEKey; import appeng.helpers.patternprovider.PatternProviderLogic; import appeng.helpers.patternprovider.PatternProviderLogicHost; import appeng.me.service.CraftingService; -import appeng.menu.me.crafting.CraftingCPUMenu; import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor; -import com.extendedae_plus.mixin.ae2.accessor.CraftingCPUMenuAccessor; import com.mojang.logging.LogUtils; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -59,12 +57,17 @@ public class CraftingMonitorJumpC2SPacket { LogUtils.getLogger().info("EAP[S]: recv CraftingMonitorJumpC2SPacket key={} from {}", msg.what, player.getGameProfile().getName()); // 必须在 CraftingCPU 界面内 - if (!(player.containerMenu instanceof CraftingCPUMenu menu)) { + if (!(player.containerMenu instanceof appeng.menu.me.crafting.CraftingCPUMenu menu)) { LogUtils.getLogger().info("EAP[S]: not in CraftingCPUMenu, abort"); return; } - // 直接通过 accessor 从菜单获取 Grid,避免对方块实体/level 的依赖 - IGrid grid = ((CraftingCPUMenuAccessor) menu).getGrid(); + + // 通过菜单 target(可能是 BlockEntity/Part/ItemHost)按 IActionHost 获取 Grid + IGrid grid = null; + Object target = ((appeng.menu.AEBaseMenu) menu).getTarget(); + if (target instanceof IActionHost host && host.getActionableNode() != null) { + grid = host.getActionableNode().getGrid(); + } if (grid == null) { LogUtils.getLogger().info("EAP[S]: grid is null, abort"); return; @@ -97,8 +100,7 @@ public class CraftingMonitorJumpC2SPacket { PatternProviderLogicHost host = ((PatternProviderLogicAccessor) ppl).eap$host(); if (host == null) continue; var pbe = host.getBlockEntity(); - var level = pbe.getLevel(); - if (!(level instanceof ServerLevel serverLevel)) continue; + ServerLevel serverLevel = player.serverLevel(); // 尝试对邻居打开 GUI(复用 OpenProviderUiC2SPacket 的策略) for (Direction dir : host.getTargets()) { @@ -120,29 +122,26 @@ public class CraftingMonitorJumpC2SPacket { } } - // 兜底:若无 MenuProvider,模拟徒手右键一次(优先有方块实体的面) - boolean anyHandEmpty = player.getMainHandItem().isEmpty() || player.getOffhandItem().isEmpty(); - if (anyHandEmpty) { - InteractionHand hand = player.getMainHandItem().isEmpty() ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND; - Direction chosen = null; + // 兜底:若无 MenuProvider,始终模拟一次右键(优先有方块实体的一面) + InteractionHand hand = player.getMainHandItem().isEmpty() ? InteractionHand.MAIN_HAND : InteractionHand.MAIN_HAND; + Direction chosen = null; + for (Direction d : host.getTargets()) { + if (serverLevel.getBlockEntity(pbe.getBlockPos().relative(d)) != null) { chosen = d; break; } + } + if (chosen == null) { for (Direction d : host.getTargets()) { - if (serverLevel.getBlockEntity(pbe.getBlockPos().relative(d)) != null) { chosen = d; break; } + if (!serverLevel.getBlockState(pbe.getBlockPos().relative(d)).isAir()) { chosen = d; break; } } - if (chosen == null) { - for (Direction d : host.getTargets()) { - if (!serverLevel.getBlockState(pbe.getBlockPos().relative(d)).isAir()) { chosen = d; break; } - } - } - if (chosen != null) { - BlockPos targetPos = pbe.getBlockPos().relative(chosen); - var state2 = serverLevel.getBlockState(targetPos); - var hit = new BlockHitResult(Vec3.atCenterOf(targetPos), chosen.getOpposite(), targetPos, false); - InteractionResult r = state2.use(serverLevel, player, hand, hit); - if (r.consumesAction()) { - LogUtils.getLogger().info("EAP[S]: opened via simulated use at {} ({}), result={}", targetPos, chosen, r); - context.setPacketHandled(true); - return; - } + } + if (chosen != null) { + BlockPos targetPos = pbe.getBlockPos().relative(chosen); + var state2 = serverLevel.getBlockState(targetPos); + var hit = new BlockHitResult(Vec3.atCenterOf(targetPos), chosen.getOpposite(), targetPos, false); + InteractionResult r = state2.use(serverLevel, player, hand, hit); + LogUtils.getLogger().info("EAP[S]: simulated use on {}, face={}, result={}", targetPos, chosen, r); + if (r.consumesAction()) { + context.setPacketHandled(true); + return; } } } diff --git a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java new file mode 100644 index 0000000..216546c --- /dev/null +++ b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java @@ -0,0 +1,115 @@ +package com.extendedae_plus.network; + +import appeng.api.crafting.IPatternDetails; +import appeng.api.networking.IGrid; +import appeng.api.networking.security.IActionHost; +import appeng.api.stacks.AEKey; +import appeng.helpers.patternprovider.PatternProviderLogic; +import appeng.helpers.patternprovider.PatternProviderLogicHost; +import appeng.me.service.CraftingService; +import appeng.menu.AEBaseMenu; +import appeng.menu.me.crafting.CraftingCPUMenu; +import appeng.menu.locator.MenuLocators; +import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor; +import com.mojang.logging.LogUtils; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.network.NetworkEvent; + +import java.util.Collection; +import java.util.function.Supplier; + +/** + * 客户端从 CraftingCPUScreen 发送:鼠标下条目对应的 AEKey。 + * 服务端在当前打开的 CraftingCPUMenu 所属网络中,定位匹配该 AEKey 的样板供应器, + * 打开该供应器自身的 UI(不是目标机器的 UI)。 + */ +public class CraftingMonitorOpenProviderC2SPacket { + private final AEKey what; + + public CraftingMonitorOpenProviderC2SPacket(AEKey what) { + this.what = what; + } + + public static void encode(CraftingMonitorOpenProviderC2SPacket msg, FriendlyByteBuf buf) { + AEKey.writeKey(buf, msg.what); + } + + public static CraftingMonitorOpenProviderC2SPacket decode(FriendlyByteBuf buf) { + AEKey key = AEKey.readKey(buf); + return new CraftingMonitorOpenProviderC2SPacket(key); + } + + public static void handle(CraftingMonitorOpenProviderC2SPacket msg, Supplier ctx) { + NetworkEvent.Context context = ctx.get(); + context.enqueueWork(() -> { + 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; + } + + // 通过菜单的 target(可能是 BlockEntity/Part/ItemHost),按 IActionHost 获取 Grid + IGrid grid = null; + Object target = ((AEBaseMenu) menu).getTarget(); + if (target instanceof IActionHost host && host.getActionableNode() != null) { + 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; + } + + // 2) 遍历提供该样板的 Provider,定位 PatternProviderLogic + for (var pattern : patterns) { + var providers = craftingService.getProviders(pattern); + for (var provider : providers) { + if (provider instanceof PatternProviderLogic ppl) { + // accessor 获取 host + PatternProviderLogicHost host = ((PatternProviderLogicAccessor) ppl).eap$host(); + if (host == null) continue; + var pbe = host.getBlockEntity(); + if (pbe == null) continue; + // 在服务端上下文中执行,pbe 仅用于构造菜单定位器 + + // 直接打开供应器自身的 UI(调用 Host 默认方法) + try { + // 部件与方块实体分别选择定位器 + if (host instanceof appeng.parts.AEBasePart part) { + host.openMenu(player, MenuLocators.forPart(part)); + } else { + host.openMenu(player, MenuLocators.forBlockEntity(pbe)); + } + context.setPacketHandled(true); + return; + } catch (Throwable t) { + LogUtils.getLogger().error("EAP[S]: open provider UI failed at {}", pbe.getBlockPos(), t); + } + } + } + } + + LogUtils.getLogger().info("EAP[S]: no provider UI opened for key={}", msg.what); + }); + context.setPacketHandled(true); + } +} diff --git a/src/main/java/com/extendedae_plus/network/ModNetwork.java b/src/main/java/com/extendedae_plus/network/ModNetwork.java index cc50a47..3cfe50a 100644 --- a/src/main/java/com/extendedae_plus/network/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/network/ModNetwork.java @@ -77,6 +77,12 @@ public class ModNetwork { .decoder(CraftingMonitorJumpC2SPacket::decode) .consumerNetworkThread(CraftingMonitorJumpC2SPacket::handle) .add(); + + CHANNEL.messageBuilder(CraftingMonitorOpenProviderC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER) + .encoder(CraftingMonitorOpenProviderC2SPacket::encode) + .decoder(CraftingMonitorOpenProviderC2SPacket::decode) + .consumerNetworkThread(CraftingMonitorOpenProviderC2SPacket::handle) + .add(); } private static int nextId() { return id++; } diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index f66260c..e515bd3 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -31,7 +31,6 @@ "ae2.PatternEncodingTermMenuMixin", "ae2.PatternProviderLogicAdvancedMixin", "ae2.PatternProviderMenuAdvancedMixin", - "ae2.accessor.CraftingCPUMenuAccessor", "ae2.accessor.MEStorageMenuAccessor", "ae2.accessor.PatternEncodingTermMenuAccessor", "ae2.accessor.PatternProviderLogicAccessor",