From bd3de00f13ac4f858cdc166cde71156b1124c4a7 Mon Sep 17 00:00:00 2001 From: GaLi <3096147684@qq.com> Date: Wed, 20 Aug 2025 16:53:59 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=AB=98=E7=BA=A7=E9=98=BB?= =?UTF-8?q?=E6=8C=A1=E6=8C=89=E9=92=AE(=E4=BB=85ui=E5=92=8C=E5=88=87?= =?UTF-8?q?=E6=8D=A2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/ClientAdvancedBlockingState.java | 27 ++++ .../PatternProviderLogicAccessor.java | 13 ++ .../PatternProviderMenuAdvancedAccessor.java | 13 ++ .../PatternProviderLogicAdvancedMixin.java | 46 ++++++ .../ae2/PatternProviderMenuAdvancedMixin.java | 95 +++++++++++ .../mixin/ae2/PatternProviderScreenMixin.java | 147 ++++++++++++++++++ .../mixin/api/AdvancedBlockingHolder.java | 6 + .../api/PatternProviderMenuAdvancedSync.java | 5 + .../AdvancedBlockingSyncS2CPacket.java | 41 +++++ .../extendedae_plus/network/ModNetwork.java | 12 ++ .../ToggleAdvancedBlockingC2SPacket.java | 59 +++++++ .../resources/extendedae_plus.mixins.json | 7 +- 12 files changed, 470 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/extendedae_plus/client/ClientAdvancedBlockingState.java create mode 100644 src/main/java/com/extendedae_plus/mixin/accessor/PatternProviderLogicAccessor.java create mode 100644 src/main/java/com/extendedae_plus/mixin/accessor/PatternProviderMenuAdvancedAccessor.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderLogicAdvancedMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderMenuAdvancedMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderScreenMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/api/AdvancedBlockingHolder.java create mode 100644 src/main/java/com/extendedae_plus/mixin/api/PatternProviderMenuAdvancedSync.java create mode 100644 src/main/java/com/extendedae_plus/network/AdvancedBlockingSyncS2CPacket.java create mode 100644 src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java diff --git a/src/main/java/com/extendedae_plus/client/ClientAdvancedBlockingState.java b/src/main/java/com/extendedae_plus/client/ClientAdvancedBlockingState.java new file mode 100644 index 0000000..c39f2c9 --- /dev/null +++ b/src/main/java/com/extendedae_plus/client/ClientAdvancedBlockingState.java @@ -0,0 +1,27 @@ +package com.extendedae_plus.client; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public final class ClientAdvancedBlockingState { + private static final Map states = new ConcurrentHashMap<>(); + + private ClientAdvancedBlockingState() {} + + public static String key(String dimension, long blockPosLong) { + return dimension + "@" + blockPosLong; + } + + public static void set(String key, boolean v) { + states.put(key, v); + System.out.println("[EPP][CLIENT][S2C] Received advancedBlocking key=" + key + ", value=" + v); + } + + public static boolean has(String key) { + return states.containsKey(key); + } + + public static boolean get(String key) { + return states.getOrDefault(key, false); + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/accessor/PatternProviderLogicAccessor.java b/src/main/java/com/extendedae_plus/mixin/accessor/PatternProviderLogicAccessor.java new file mode 100644 index 0000000..7818382 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/accessor/PatternProviderLogicAccessor.java @@ -0,0 +1,13 @@ +package com.extendedae_plus.mixin.accessor; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import appeng.helpers.patternprovider.PatternProviderLogic; +import appeng.helpers.patternprovider.PatternProviderLogicHost; + +@Mixin(PatternProviderLogic.class) +public interface PatternProviderLogicAccessor { + @Accessor("host") + PatternProviderLogicHost ext$host(); +} diff --git a/src/main/java/com/extendedae_plus/mixin/accessor/PatternProviderMenuAdvancedAccessor.java b/src/main/java/com/extendedae_plus/mixin/accessor/PatternProviderMenuAdvancedAccessor.java new file mode 100644 index 0000000..af5c494 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/accessor/PatternProviderMenuAdvancedAccessor.java @@ -0,0 +1,13 @@ +package com.extendedae_plus.mixin.accessor; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import appeng.menu.implementations.PatternProviderMenu; +import appeng.helpers.patternprovider.PatternProviderLogic; + +@Mixin(PatternProviderMenu.class) +public interface PatternProviderMenuAdvancedAccessor { + @Accessor("logic") + PatternProviderLogic ext$logic(); +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderLogicAdvancedMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderLogicAdvancedMixin.java new file mode 100644 index 0000000..cefd499 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderLogicAdvancedMixin.java @@ -0,0 +1,46 @@ +package com.extendedae_plus.mixin.ae2; + +import appeng.helpers.patternprovider.PatternProviderLogic; +import net.minecraft.nbt.CompoundTag; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.extendedae_plus.api.AdvancedBlockingHolder; + +@Mixin(PatternProviderLogic.class) +public class PatternProviderLogicAdvancedMixin implements AdvancedBlockingHolder { + @Unique + private static final String EPP_ADV_BLOCKING_KEY = "epp_advanced_blocking"; + + @Unique + private boolean epp$advancedBlocking = false; + + @Override + public boolean ext$getAdvancedBlocking() { + return epp$advancedBlocking; + } + + @Override + public void ext$setAdvancedBlocking(boolean value) { + this.epp$advancedBlocking = value; + } + + @Inject(method = "writeToNBT", at = @At("TAIL"), remap = false) + private void epp$writeAdvancedToNbt(CompoundTag tag, CallbackInfo ci) { + tag.putBoolean(EPP_ADV_BLOCKING_KEY, this.epp$advancedBlocking); + System.out.println("[EPP][NBT] writeToNBT: " + EPP_ADV_BLOCKING_KEY + "=" + this.epp$advancedBlocking); + } + + @Inject(method = "readFromNBT", at = @At("TAIL"), remap = false) + private void epp$readAdvancedFromNbt(CompoundTag tag, CallbackInfo ci) { + if (tag.contains(EPP_ADV_BLOCKING_KEY)) { + this.epp$advancedBlocking = tag.getBoolean(EPP_ADV_BLOCKING_KEY); + System.out.println("[EPP][NBT] readFromNBT: " + EPP_ADV_BLOCKING_KEY + "=" + this.epp$advancedBlocking); + } else { + System.out.println("[EPP][NBT] readFromNBT: key missing, default=" + this.epp$advancedBlocking); + } + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderMenuAdvancedMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderMenuAdvancedMixin.java new file mode 100644 index 0000000..919dfe6 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderMenuAdvancedMixin.java @@ -0,0 +1,95 @@ +package com.extendedae_plus.mixin.ae2; + +import appeng.menu.implementations.PatternProviderMenu; +import appeng.helpers.patternprovider.PatternProviderLogic; +import appeng.menu.guisync.GuiSync; +import appeng.helpers.patternprovider.PatternProviderLogicHost; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.MenuType; +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; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import com.extendedae_plus.api.AdvancedBlockingHolder; +import com.extendedae_plus.api.PatternProviderMenuAdvancedSync; + +@Mixin(PatternProviderMenu.class) +public abstract class PatternProviderMenuAdvancedMixin implements PatternProviderMenuAdvancedSync { + static { + System.out.println("[EPP][MIXIN] Loaded PatternProviderMenuAdvancedMixin"); + } + @Shadow + protected abstract boolean isServerSide(); + + @Shadow + protected PatternProviderLogic logic; + + // 选择一个未占用的 GUI 同步 id(AE2 已用到 7),这里使用 20 以避冲突 + @GuiSync(20) + public boolean eppAdvancedBlocking = false; + + @Inject(method = "broadcastChanges", at = @At("HEAD")) + private void epp$syncAdvancedBlocking(CallbackInfo ci) { + if (this.isServerSide()) { + var l = this.logic; + if (l instanceof AdvancedBlockingHolder holder) { + this.eppAdvancedBlocking = holder.ext$getAdvancedBlocking(); + System.out.println("[EPP][MENU][S->C] broadcastChanges: eppAdvancedBlocking=" + this.eppAdvancedBlocking); + } + } else { + System.out.println("[EPP][MENU][CLIENT] broadcastChanges called on client side"); + } + } + + @Inject(method = "broadcastChanges", at = @At("TAIL")) + private void epp$syncAdvancedBlockingTail(CallbackInfo ci) { + System.out.println("[EPP][MENU] broadcastChanges tail"); + } + + // 构造器尾注入(public ctor) + @Inject(method = "(ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", at = @At("TAIL")) + private void epp$initAdvancedSync_Public(int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { + try { + var l = this.logic; + if (l instanceof AdvancedBlockingHolder holder) { + this.eppAdvancedBlocking = holder.ext$getAdvancedBlocking(); + System.out.println("[EPP][MENU] -public set initial eppAdvancedBlocking=" + this.eppAdvancedBlocking); + } + } catch (Throwable t) { + System.out.println("[EPP][MENU] -public init error: " + t); + } + } + + // 构造器尾注入(protected ctor with MenuType) + @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", at = @At("TAIL")) + private void epp$initAdvancedSync_Protected(MenuType menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { + try { + var l = this.logic; + if (l instanceof AdvancedBlockingHolder holder) { + this.eppAdvancedBlocking = holder.ext$getAdvancedBlocking(); + System.out.println("[EPP][MENU] -protected set initial eppAdvancedBlocking=" + this.eppAdvancedBlocking); + } + } catch (Throwable t) { + System.out.println("[EPP][MENU] -protected init error: " + t); + } + } + + @Override + public boolean ext$getAdvancedBlockingSynced() { + return this.eppAdvancedBlocking; + } + + // 调试:当 Screen 每帧读取这些 getter 时打印,验证 Mixin 是否生效 + @Inject(method = "getBlockingMode", at = @At("HEAD"), remap = false) + private void epp$debug_getBlockingMode(CallbackInfoReturnable cir) { + System.out.println("[EPP][MENU][DEBUG] getBlockingMode() called; eppAdvancedBlocking=" + this.eppAdvancedBlocking); + } + + @Inject(method = "getShowInAccessTerminal", at = @At("HEAD"), remap = false) + private void epp$debug_getShowInAccessTerminal(CallbackInfoReturnable cir) { + System.out.println("[EPP][MENU][DEBUG] getShowInAccessTerminal() called; eppAdvancedBlocking=" + this.eppAdvancedBlocking); + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderScreenMixin.java new file mode 100644 index 0000000..b4cd606 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/PatternProviderScreenMixin.java @@ -0,0 +1,147 @@ +package com.extendedae_plus.mixin.ae2; + +import appeng.client.gui.AEBaseScreen; +import appeng.client.gui.Icon; +import appeng.client.gui.implementations.PatternProviderScreen; +import appeng.client.gui.style.ScreenStyle; +import appeng.client.gui.widgets.ToggleButton; +import appeng.menu.implementations.PatternProviderMenu; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Inventory; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.extendedae_plus.api.PatternProviderMenuAdvancedSync; +import com.extendedae_plus.network.ModNetwork; +import com.extendedae_plus.network.ToggleAdvancedBlockingC2SPacket; +import com.extendedae_plus.client.ClientAdvancedBlockingState; +import com.extendedae_plus.mixin.accessor.PatternProviderMenuAdvancedAccessor; +import com.extendedae_plus.mixin.accessor.PatternProviderLogicAccessor; + +/** + * 为 AE2 原版样板供应器界面添加“高级阻挡模式”按钮(仅客户端UI反馈)。 + * - 位于左侧工具栏 + * - 点击后切换图标(YES/NO)并切换 tooltip 提示 + * - 当前不做任何网络/服务端逻辑 + */ +@Mixin(PatternProviderScreen.class) +public abstract class PatternProviderScreenMixin extends AEBaseScreen { + + @Unique + private ToggleButton eppAdvancedBlockingToggle; + + @Unique + private boolean eppAdvancedBlockingEnabled = false; + + @Unique + private String eppProviderKey = null; + + public PatternProviderScreenMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) { + super(menu, playerInventory, title, style); + } + + @Inject(method = "", at = @At("RETURN")) + private void epp$initAdvancedBlocking(C menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) { + // 计算供应器唯一键:维度ID + 方块坐标 + try { + var logic = ((PatternProviderMenuAdvancedAccessor) menu).ext$logic(); + var host = ((PatternProviderLogicAccessor) logic).ext$host(); + var be = host.getBlockEntity(); + var level = be.getLevel(); + String dimId = level.dimension().location().toString(); + long posLong = be.getBlockPos().asLong(); + this.eppProviderKey = ClientAdvancedBlockingState.key(dimId, posLong); + System.out.println("[EPP][CLIENT] init: providerKey=" + this.eppProviderKey); + } catch (Throwable t) { + System.out.println("[EPP][CLIENT] init: providerKey resolve failed: " + t); + } + + // 优先使用该供应器最近一次 S2C 状态;否则回退读取 @GuiSync 初始化 + if (this.eppProviderKey != null && ClientAdvancedBlockingState.has(this.eppProviderKey)) { + this.eppAdvancedBlockingEnabled = ClientAdvancedBlockingState.get(this.eppProviderKey); + System.out.println("[EPP][CLIENT] init: use ClientState key=" + this.eppProviderKey + ", value=" + this.eppAdvancedBlockingEnabled); + } else if (menu instanceof PatternProviderMenuAdvancedSync sync) { + this.eppAdvancedBlockingEnabled = sync.ext$getAdvancedBlockingSynced(); + System.out.println("[EPP][CLIENT] init: use GuiSync value=" + this.eppAdvancedBlockingEnabled); + } + // 使用 ToggleButton 以便在 YES/NO 图标与提示之间动态切换 + this.eppAdvancedBlockingToggle = new ToggleButton( + Icon.BLOCKING_MODE_YES, + Icon.BLOCKING_MODE_NO, + // 提示文本:名称与说明 + Component.literal("高级阻挡模式"), + Component.literal("高级阻挡模式:当开启时,执行更严格的阻挡判定"), + (state) -> { + // 客户端立即反馈:切换图标/提示 + this.eppAdvancedBlockingEnabled = state; + this.eppAdvancedBlockingToggle.setState(state); + System.out.println("[EPP][CLIENT] Click toggle: state=" + state); + // 发送 C2S 切换请求 + ModNetwork.CHANNEL.sendToServer(new ToggleAdvancedBlockingC2SPacket()); + // 可根据状态调整提示文本(演示性:开启/关闭不同第二行) + if (state) { + this.eppAdvancedBlockingToggle.setTooltipOn(java.util.List.of( + Component.literal("高级阻挡模式"), + Component.literal("高级阻挡模式:已开启"))); + this.eppAdvancedBlockingToggle.setTooltipOff(java.util.List.of( + Component.literal("高级阻挡模式"), + Component.literal("高级阻挡模式:已开启"))); + } else { + this.eppAdvancedBlockingToggle.setTooltipOn(java.util.List.of( + Component.literal("高级阻挡模式"), + Component.literal("高级阻挡模式:已关闭"))); + this.eppAdvancedBlockingToggle.setTooltipOff(java.util.List.of( + Component.literal("高级阻挡模式"), + Component.literal("高级阻挡模式:已关闭"))); + } + } + ); + this.eppAdvancedBlockingToggle.setState(this.eppAdvancedBlockingEnabled); + // 初始 tooltip + this.eppAdvancedBlockingToggle.setTooltipOn(java.util.List.of( + Component.literal("高级阻挡模式"), + Component.literal(this.eppAdvancedBlockingEnabled ? "高级阻挡模式:已开启" : "高级阻挡模式:已关闭") + )); + this.eppAdvancedBlockingToggle.setTooltipOff(java.util.List.of( + Component.literal("高级阻挡模式"), + Component.literal(this.eppAdvancedBlockingEnabled ? "高级阻挡模式:已开启" : "高级阻挡模式:已关闭") + )); + + this.addToLeftToolbar(this.eppAdvancedBlockingToggle); + } + + // 每帧刷新:从菜单同步布尔值,保持按钮状态一致 + @Inject(method = "updateBeforeRender", at = @At("TAIL"), remap = false) + private void epp$updateAdvancedBlocking(CallbackInfo ci) { + // 打印一条轻量 tick 日志以确认该方法被调用(频繁输出可在验证后移除) + // System.out.println("[EPP][CLIENT] updateBeforeRender tick, local=" + this.eppAdvancedBlockingEnabled); + + if (this.eppAdvancedBlockingToggle == null) return; + + boolean desired = this.eppAdvancedBlockingEnabled; + // 优先使用该供应器最近一次 S2C 值 + if (this.eppProviderKey != null && ClientAdvancedBlockingState.has(this.eppProviderKey)) { + desired = ClientAdvancedBlockingState.get(this.eppProviderKey); + } else if (this.menu instanceof PatternProviderMenuAdvancedSync sync) { + desired = sync.ext$getAdvancedBlockingSynced(); + } + + if (desired != this.eppAdvancedBlockingEnabled) { + this.eppAdvancedBlockingEnabled = desired; + this.eppAdvancedBlockingToggle.setState(desired); + System.out.println("[EPP][CLIENT] updateBeforeRender apply: eppAdvancedBlocking=" + desired); + // 同步 tooltip 二行提示 + this.eppAdvancedBlockingToggle.setTooltipOn(java.util.List.of( + Component.literal("高级阻挡模式"), + Component.literal(desired ? "高级阻挡模式:已开启" : "高级阻挡模式:已关闭") + )); + this.eppAdvancedBlockingToggle.setTooltipOff(java.util.List.of( + Component.literal("高级阻挡模式"), + Component.literal(desired ? "高级阻挡模式:已开启" : "高级阻挡模式:已关闭") + )); + } + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/api/AdvancedBlockingHolder.java b/src/main/java/com/extendedae_plus/mixin/api/AdvancedBlockingHolder.java new file mode 100644 index 0000000..7f7d539 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/api/AdvancedBlockingHolder.java @@ -0,0 +1,6 @@ +package com.extendedae_plus.api; + +public interface AdvancedBlockingHolder { + boolean ext$getAdvancedBlocking(); + void ext$setAdvancedBlocking(boolean value); +} diff --git a/src/main/java/com/extendedae_plus/mixin/api/PatternProviderMenuAdvancedSync.java b/src/main/java/com/extendedae_plus/mixin/api/PatternProviderMenuAdvancedSync.java new file mode 100644 index 0000000..e987d45 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/api/PatternProviderMenuAdvancedSync.java @@ -0,0 +1,5 @@ +package com.extendedae_plus.api; + +public interface PatternProviderMenuAdvancedSync { + boolean ext$getAdvancedBlockingSynced(); +} diff --git a/src/main/java/com/extendedae_plus/network/AdvancedBlockingSyncS2CPacket.java b/src/main/java/com/extendedae_plus/network/AdvancedBlockingSyncS2CPacket.java new file mode 100644 index 0000000..357d0f3 --- /dev/null +++ b/src/main/java/com/extendedae_plus/network/AdvancedBlockingSyncS2CPacket.java @@ -0,0 +1,41 @@ +package com.extendedae_plus.network; + +import com.extendedae_plus.client.ClientAdvancedBlockingState; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.network.NetworkEvent; + +import java.util.function.Supplier; + +public class AdvancedBlockingSyncS2CPacket { + private final String dimensionId; + private final long blockPosLong; + private final boolean enabled; + + public AdvancedBlockingSyncS2CPacket(String dimensionId, long blockPosLong, boolean enabled) { + this.dimensionId = dimensionId; + this.blockPosLong = blockPosLong; + this.enabled = enabled; + } + + public static void encode(AdvancedBlockingSyncS2CPacket msg, FriendlyByteBuf buf) { + buf.writeUtf(msg.dimensionId); + buf.writeLong(msg.blockPosLong); + buf.writeBoolean(msg.enabled); + } + + public static AdvancedBlockingSyncS2CPacket decode(FriendlyByteBuf buf) { + String dim = buf.readUtf(); + long pos = buf.readLong(); + boolean en = buf.readBoolean(); + return new AdvancedBlockingSyncS2CPacket(dim, pos, en); + } + + public static void handle(AdvancedBlockingSyncS2CPacket msg, Supplier ctxSupplier) { + var ctx = ctxSupplier.get(); + ctx.enqueueWork(() -> { + String key = ClientAdvancedBlockingState.key(msg.dimensionId, msg.blockPosLong); + ClientAdvancedBlockingState.set(key, msg.enabled); + }); + ctx.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 12de85b..7679c5e 100644 --- a/src/main/java/com/extendedae_plus/network/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/network/ModNetwork.java @@ -56,6 +56,18 @@ public class ModNetwork { .decoder(ProvidersListS2CPacket::decode) .consumerNetworkThread(ProvidersListS2CPacket::handle) .add(); + + CHANNEL.messageBuilder(ToggleAdvancedBlockingC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER) + .encoder(ToggleAdvancedBlockingC2SPacket::encode) + .decoder(ToggleAdvancedBlockingC2SPacket::decode) + .consumerNetworkThread(ToggleAdvancedBlockingC2SPacket::handle) + .add(); + + CHANNEL.messageBuilder(AdvancedBlockingSyncS2CPacket.class, nextId(), NetworkDirection.PLAY_TO_CLIENT) + .encoder(AdvancedBlockingSyncS2CPacket::encode) + .decoder(AdvancedBlockingSyncS2CPacket::decode) + .consumerNetworkThread(AdvancedBlockingSyncS2CPacket::handle) + .add(); } private static int nextId() { return id++; } diff --git a/src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java b/src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java new file mode 100644 index 0000000..aeb9a09 --- /dev/null +++ b/src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java @@ -0,0 +1,59 @@ +package com.extendedae_plus.network; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.network.NetworkEvent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.network.NetworkDirection; + +import java.util.function.Supplier; + +import appeng.menu.implementations.PatternProviderMenu; +import com.extendedae_plus.mixin.accessor.PatternProviderMenuAdvancedAccessor; +import com.extendedae_plus.api.AdvancedBlockingHolder; +import com.extendedae_plus.mixin.accessor.PatternProviderLogicAccessor; + +/** + * C2S:切换高级阻挡模式。 + * 不含额外负载,直接基于玩家当前打开的 PatternProviderMenu 进行切换。 + */ +public class ToggleAdvancedBlockingC2SPacket { + public ToggleAdvancedBlockingC2SPacket() {} + + public static void encode(ToggleAdvancedBlockingC2SPacket msg, FriendlyByteBuf buf) {} + + public static ToggleAdvancedBlockingC2SPacket decode(FriendlyByteBuf buf) { + return new ToggleAdvancedBlockingC2SPacket(); + } + + public static void handle(ToggleAdvancedBlockingC2SPacket msg, Supplier ctxSupplier) { + var ctx = ctxSupplier.get(); + ctx.enqueueWork(() -> { + ServerPlayer player = ctx.getSender(); + if (player == null) return; + if (!(player.containerMenu instanceof PatternProviderMenu menu)) return; + + // 通过 accessor 获取逻辑与当前状态 + var accessor = (PatternProviderMenuAdvancedAccessor) menu; + var logic = accessor.ext$logic(); + if (logic instanceof AdvancedBlockingHolder holder) { + boolean current = holder.ext$getAdvancedBlocking(); + boolean next = !current; + System.out.println("[EPP][C2S] ToggleAdvancedBlockingC2SPacket: player=" + player.getGameProfile().getName() + + ", menu=" + menu.getClass().getName() + + ", before=" + current + ", after=" + next); + holder.ext$setAdvancedBlocking(next); + // 关键:保存持久化,触发 AE2 写入逻辑(writeToNBT),并由菜单 @GuiSync 同步回客户端 + logic.saveChanges(); + System.out.println("[EPP][C2S] logic.saveChanges() called for advancedBlocking=" + next); + // 直接下发 S2C 强制同步(带供应器标识:维度+方块坐标) + var host = ((PatternProviderLogicAccessor) logic).ext$host(); + var be = host.getBlockEntity(); + var level = be.getLevel(); + String dimId = level.dimension().location().toString(); + long posLong = be.getBlockPos().asLong(); + ModNetwork.CHANNEL.sendTo(new AdvancedBlockingSyncS2CPacket(dimId, posLong, next), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT); + } + }); + ctx.setPacketHandled(true); + } +} diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 609cbb6..053fba2 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -10,6 +10,7 @@ "HighlightButtonMixin", "PickFromWirelessMixin", "PatternEncodingTermScreenMixin", + "ae2.PatternProviderScreenMixin", "jei.EncodePatternTransferHandlerMixin", "ae2.QuartzCuttingKnifeItemMixin", "accessor.AEBaseScreenAccessor", @@ -27,7 +28,11 @@ "ContainerPatternEncodingTermMenuMixin", "MEStorageMenuMixin", "accessor.MEStorageMenuAccessor", - "accessor.PatternEncodingTermMenuAccessor" + "accessor.PatternEncodingTermMenuAccessor", + "ae2.PatternProviderLogicAdvancedMixin", + "ae2.PatternProviderMenuAdvancedMixin", + "accessor.PatternProviderMenuAdvancedAccessor", + "accessor.PatternProviderLogicAccessor" ], "injectors": { "defaultRequire": 1