diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java index 04bec4e..365391f 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java @@ -29,7 +29,7 @@ public class ExtendedAEPlus { * 通用初始化设置 */ private void commonSetup(final FMLCommonSetupEvent event) { - // 注册网络处理器 - NetworkHandler.registerPackets(); + // 注册网络处理器(确保在注册窗口关闭前完成) + event.enqueueWork(NetworkHandler::initialize); } } diff --git a/src/main/java/com/extendedae_plus/mixin/ContainerExPatternProviderMixin.java b/src/main/java/com/extendedae_plus/mixin/ContainerExPatternProviderMixin.java index 3deb0c9..cc52b36 100644 --- a/src/main/java/com/extendedae_plus/mixin/ContainerExPatternProviderMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ContainerExPatternProviderMixin.java @@ -5,7 +5,13 @@ import appeng.menu.SlotSemantics; import appeng.menu.guisync.GuiSync; import appeng.menu.implementations.PatternProviderMenu; import appeng.menu.slot.AppEngSlot; +import appeng.crafting.pattern.EncodedPatternItem; +import appeng.crafting.pattern.AEProcessingPattern; +import appeng.api.stacks.GenericStack; +import appeng.api.crafting.PatternDetailsHelper; import com.glodblock.github.extendedae.container.ContainerExPatternProvider; +import com.glodblock.github.glodium.network.packet.sync.IActionHolder; +import com.glodblock.github.glodium.network.packet.sync.Paras; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.Slot; @@ -14,12 +20,15 @@ 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 org.jetbrains.annotations.NotNull; import java.lang.reflect.Field; import java.util.List; +import java.util.Map; +import java.util.function.Consumer; @Mixin(value = ContainerExPatternProvider.class, priority = 3000) -public abstract class ContainerExPatternProviderMixin extends PatternProviderMenu { +public abstract class ContainerExPatternProviderMixin extends PatternProviderMenu implements IActionHolder { @GuiSync(11451) @Unique @@ -31,6 +40,9 @@ public abstract class ContainerExPatternProviderMixin extends PatternProviderMen @Unique private static final int SLOTS_PER_PAGE = 36; // 每页显示36个槽位 + @Unique + private final Map> actions = createHolder(); + public ContainerExPatternProviderMixin(MenuType menuType, int id, Inventory playerInventory, PatternProviderLogicHost host) { super(menuType, id, playerInventory, host); } @@ -64,10 +76,20 @@ public abstract class ContainerExPatternProviderMixin extends PatternProviderMen } } - @Inject(method = "", at = @At("TAIL"), remap = false) + @Inject(method = "", at = @At("TAIL")) public void init(int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { int maxSlots = this.getSlots(SlotSemantics.ENCODED_PATTERN).size(); this.maxPage = (maxSlots + SLOTS_PER_PAGE - 1) / SLOTS_PER_PAGE; + + // 注册通用动作(供 CGenericPacket 分发) + this.actions.put("multiply2", p -> { System.out.println("[EAE+][Server] multiply2"); modifyPatterns(2, false); }); + this.actions.put("divide2", p -> { System.out.println("[EAE+][Server] divide2"); modifyPatterns(2, true); }); + this.actions.put("multiply5", p -> { System.out.println("[EAE+][Server] multiply5"); modifyPatterns(5, false); }); + this.actions.put("divide5", p -> { System.out.println("[EAE+][Server] divide5"); modifyPatterns(5, true); }); + this.actions.put("multiply10",p -> { System.out.println("[EAE+][Server] multiply10");modifyPatterns(10, false);}); + this.actions.put("divide10", p -> { System.out.println("[EAE+][Server] divide10"); modifyPatterns(10, true); }); + + System.out.println("[EAE+][Server] ContainerExPatternProvider actions registered: " + this.actions.keySet()); } @Unique @@ -79,4 +101,72 @@ public abstract class ContainerExPatternProviderMixin extends PatternProviderMen public void setPage(int page) { this.page = page; } -} \ No newline at end of file + + @Unique + private void modifyPatterns(int scale, boolean div) { + if (scale <= 0) return; + for (var slot : this.getSlots(SlotSemantics.ENCODED_PATTERN)) { + var stack = slot.getItem(); + if (stack.getItem() instanceof EncodedPatternItem pattern) { + var detail = pattern.decode(stack, this.getPlayer().level(), false); + if (detail instanceof AEProcessingPattern process) { + var input = process.getSparseInputs(); + var output = process.getOutputs(); + if (checkModify(input, scale, div) && checkModify(output, scale, div)) { + var mulInput = new GenericStack[input.length]; + var mulOutput = new GenericStack[output.length]; + modifyStacks(input, mulInput, scale, div); + modifyStacks(output, mulOutput, scale, div); + var newPattern = PatternDetailsHelper.encodeProcessingPattern(mulInput, mulOutput); + slot.set(newPattern); + } + } + } + } + } + + @Unique + private boolean checkModify(GenericStack[] stacks, int scale, boolean div) { + if (stacks == null) return false; + if (div) { + for (var stack : stacks) { + if (stack != null) { + if (stack.amount() % scale != 0) { + return false; + } + } + } + return true; + } else { + for (var stack : stacks) { + if (stack != null) { + long upper = 999999L * stack.what().getAmountPerUnit(); + if (stack.amount() * scale > upper) { + return false; + } + } + } + return true; + } + } + + @Unique + private void modifyStacks(GenericStack[] src, GenericStack[] dst, int scale, boolean div) { + for (int i = 0; i < src.length; i++) { + var stack = src[i]; + if (stack != null) { + long amt = stack.amount(); + long newAmt = div ? (amt / scale) : (amt * scale); + dst[i] = new GenericStack(stack.what(), newAmt); + } else { + dst[i] = null; + } + } + } + + @NotNull + @Override + public Map> getActionMap() { + return this.actions; + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/ContainerExPatternTerminalMixin.java b/src/main/java/com/extendedae_plus/mixin/ContainerExPatternTerminalMixin.java index b22205a..e82d026 100644 --- a/src/main/java/com/extendedae_plus/mixin/ContainerExPatternTerminalMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ContainerExPatternTerminalMixin.java @@ -1,15 +1,25 @@ package com.extendedae_plus.mixin; import appeng.menu.guisync.GuiSync; +import com.extendedae_plus.util.ExtendedAEPatternUploadUtil; +import appeng.api.util.IConfigurableObject; import com.glodblock.github.extendedae.container.ContainerExPatternTerminal; +import com.glodblock.github.glodium.network.packet.sync.IActionHolder; +import com.glodblock.github.glodium.network.packet.sync.Paras; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.NotNull; 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 java.util.Map; +import java.util.function.Consumer; +import net.minecraft.world.entity.player.Player; + @Mixin(ContainerExPatternTerminal.class) -public abstract class ContainerExPatternTerminalMixin { +public abstract class ContainerExPatternTerminalMixin implements IActionHolder { @GuiSync(11452) @Unique @@ -29,4 +39,34 @@ public abstract class ContainerExPatternTerminalMixin { public void toggleHidePatternSlots() { this.hidePatternSlots = !this.hidePatternSlots; } -} \ No newline at end of file + + @Unique + private final Map> actions = createHolder(); + + @Unique + private Player epp$player; + + @Inject(method = "", at = @At("TAIL")) + private void init(int id, net.minecraft.world.entity.player.Inventory playerInventory, IConfigurableObject host, CallbackInfo ci) { + this.epp$player = playerInventory.player; + // 注册上传动作:参数顺序必须与客户端 CGenericPacket 保持一致 + this.actions.put("upload", p -> { + try { + int playerSlotIndex = p.get(0); + long providerId = p.get(1); + var sp = (ServerPlayer) this.epp$player; + System.out.println("[EAE+][Server] upload: slot=" + playerSlotIndex + ", provider=" + providerId); + ExtendedAEPatternUploadUtil.uploadPatternToProvider(sp, playerSlotIndex, providerId); + } catch (Throwable t) { + t.printStackTrace(); + } + }); + System.out.println("[EAE+][Server] ExPatternTerminal actions registered: " + this.actions.keySet()); + } + + @NotNull + @Override + public Map> getActionMap() { + return this.actions; + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/GuiExPatternProviderMixin.java b/src/main/java/com/extendedae_plus/mixin/GuiExPatternProviderMixin.java index 0cc8580..b644ce8 100644 --- a/src/main/java/com/extendedae_plus/mixin/GuiExPatternProviderMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/GuiExPatternProviderMixin.java @@ -9,9 +9,10 @@ import appeng.menu.SlotSemantics; import com.extendedae_plus.NewIcon; import com.extendedae_plus.util.PatternProviderUIHelper; import com.glodblock.github.extendedae.client.button.ActionEPPButton; +import com.glodblock.github.extendedae.network.EPPNetworkHandler; +import com.glodblock.github.glodium.network.packet.CGenericPacket; import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; import com.glodblock.github.extendedae.container.ContainerExPatternProvider; -import com.extendedae_plus.network.UpdatePagePacket; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; @@ -271,194 +272,86 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen SLOTS_PER_PAGE) { - // 前进后退按钮 - this.prevPage = new ActionEPPButton((b) -> { - int currentPage = getCurrentPage(); - int maxPage = getMaxPage(); - // 循环翻页:第一页向前翻到最后一页 - int newPage = (currentPage - 1 + maxPage) % maxPage; - try { - ContainerExPatternProvider menu1 = this.getMenu(); - java.lang.reflect.Method setPageMethod = menu1.getClass().getMethod("setPage", int.class); - setPageMethod.invoke(menu1, newPage); - } catch (Exception e) { - // 忽略反射错误 - } - }, Icon.ARROW_LEFT); + // 翻页按钮(仅在需要时显示) + int totalSlots = this.getMenu().getSlots(SlotSemantics.ENCODED_PATTERN).size(); + if (totalSlots > SLOTS_PER_PAGE) { + this.prevPage = new ActionEPPButton((b) -> { + int currentPage = getCurrentPage(); + int maxPage = getMaxPage(); + int newPage = (currentPage - 1 + maxPage) % maxPage; + try { + ContainerExPatternProvider menu1 = this.getMenu(); + java.lang.reflect.Method setPageMethod = menu1.getClass().getMethod("setPage", int.class); + setPageMethod.invoke(menu1, newPage); + } catch (Exception ignored) {} + }, Icon.ARROW_LEFT); - this.nextPage = new ActionEPPButton((b) -> { - int currentPage = getCurrentPage(); - int maxPage = getMaxPage(); - // 循环翻页:最后一页向后翻到第一页 - int newPage = (currentPage + 1) % maxPage; - try { - ContainerExPatternProvider menu1 = this.getMenu(); - java.lang.reflect.Method setPageMethod = menu1.getClass().getMethod("setPage", int.class); - setPageMethod.invoke(menu1, newPage); - } catch (Exception e) { - // 忽略反射错误 - } - }, Icon.ARROW_RIGHT); + this.nextPage = new ActionEPPButton((b) -> { + int currentPage = getCurrentPage(); + int maxPage = getMaxPage(); + int newPage = (currentPage + 1) % maxPage; + try { + ContainerExPatternProvider menu1 = this.getMenu(); + java.lang.reflect.Method setPageMethod = menu1.getClass().getMethod("setPage", int.class); + setPageMethod.invoke(menu1, newPage); + } catch (Exception ignored) {} + }, Icon.ARROW_RIGHT); this.addToLeftToolbar(this.nextPage); this.addToLeftToolbar(this.prevPage); } - - // x2 按钮 - 单机模式直接调用服务器端逻辑 + + // 倍增/除法按钮,通过 ExtendedAE 的通用包派发 this.x2Button = new ActionEPPButton((b) -> { - try { - // 单机模式:直接在逻辑服务器端执行样板倍增 - net.minecraft.client.Minecraft minecraft = net.minecraft.client.Minecraft.getInstance(); - if (minecraft.level != null && minecraft.player != null) { - // 获取逻辑服务器端的玩家实例 - net.minecraft.server.level.ServerPlayer serverPlayer = minecraft.getSingleplayerServer() - .getPlayerList().getPlayer(minecraft.player.getUUID()); - - if (serverPlayer != null) { - // 在服务器端执行样板倍增逻辑 - executePatternScalingOnServer(serverPlayer, "MULTIPLY", 2.0); - } else { - System.out.println("ExtendedAE Plus: 无法获取服务器端玩家实例"); - } - } else { - System.out.println("ExtendedAE Plus: 单机服务器未启动或玩家为null"); - } - - } catch (Exception e) { - System.out.println("ExtendedAE Plus: 执行样板倍增时发生错误:" + e.getMessage()); - e.printStackTrace(); - } + System.out.println("[EAE+][Client] click multiply2"); + EPPNetworkHandler.INSTANCE.sendToServer(new CGenericPacket("multiply2")); }, NewIcon.MULTIPLY2); this.x2Button.setVisibility(true); - - // /2 按钮 - 单机模式直接调用服务器端逻辑 + this.divideBy2Button = new ActionEPPButton((b) -> { - try { - // 单机模式:直接在逻辑服务器端执行样板除法 - net.minecraft.client.Minecraft minecraft = net.minecraft.client.Minecraft.getInstance(); - if (minecraft.level != null && minecraft.player != null) { - // 获取逻辑服务器端的玩家实例 - net.minecraft.server.level.ServerPlayer serverPlayer = minecraft.getSingleplayerServer() - .getPlayerList().getPlayer(minecraft.player.getUUID()); - - if (serverPlayer != null) { - // 在服务器端执行样板除法逻辑 - executePatternScalingOnServer(serverPlayer, "DIVIDE", 2.0); - } else { - System.out.println("ExtendedAE Plus: 无法获取服务器端玩家实例"); - } - } else { - System.out.println("ExtendedAE Plus: 单机服务器未启动或玩家为null"); - } - - } catch (Exception e) { - System.out.println("ExtendedAE Plus: 执行样板除法时发生错误:" + e.getMessage()); - e.printStackTrace(); - } + System.out.println("[EAE+][Client] click divide2"); + EPPNetworkHandler.INSTANCE.sendToServer(new CGenericPacket("divide2")); }, NewIcon.DIVIDE2); this.divideBy2Button.setVisibility(true); - - // 使用原版渲染注册,确保在屏幕中绘制 - this.addRenderableWidget(this.divideBy2Button); - this.addRenderableWidget(this.x2Button); - // x10 按钮 - 单机模式直接调用服务器端逻辑 this.x10Button = new ActionEPPButton((b) -> { - try { - net.minecraft.client.Minecraft minecraft = net.minecraft.client.Minecraft.getInstance(); - if (minecraft.level != null && minecraft.player != null) { - net.minecraft.server.level.ServerPlayer serverPlayer = minecraft.getSingleplayerServer() - .getPlayerList().getPlayer(minecraft.player.getUUID()); - if (serverPlayer != null) { - executePatternScalingOnServer(serverPlayer, "MULTIPLY", 10.0); - } else { - System.out.println("ExtendedAE Plus: 无法获取服务器端玩家实例"); - } - } else { - System.out.println("ExtendedAE Plus: 单机服务器未启动或玩家为null"); - } - } catch (Exception e) { - System.out.println("ExtendedAE Plus: 执行样板x10倍增时发生错误:" + e.getMessage()); - e.printStackTrace(); - } + System.out.println("[EAE+][Client] click multiply10"); + EPPNetworkHandler.INSTANCE.sendToServer(new CGenericPacket("multiply10")); }, NewIcon.MULTIPLY10); this.x10Button.setVisibility(true); - // x5 按钮 - 单机模式直接调用服务器端逻辑 - this.x5Button = new ActionEPPButton((b) -> { - try { - net.minecraft.client.Minecraft minecraft = net.minecraft.client.Minecraft.getInstance(); - if (minecraft.level != null && minecraft.player != null) { - net.minecraft.server.level.ServerPlayer serverPlayer = minecraft.getSingleplayerServer() - .getPlayerList().getPlayer(minecraft.player.getUUID()); - if (serverPlayer != null) { - executePatternScalingOnServer(serverPlayer, "MULTIPLY", 5.0); - } else { - System.out.println("ExtendedAE Plus: 无法获取服务器端玩家实例"); - } - } else { - System.out.println("ExtendedAE Plus: 单机服务器未启动或玩家为null"); - } - } catch (Exception e) { - System.out.println("ExtendedAE Plus: 执行样板x5倍增时发生错误:" + e.getMessage()); - e.printStackTrace(); - } - }, NewIcon.MULTIPLY5); - this.x5Button.setVisibility(true); - - // /10 按钮 - 单机模式直接调用服务器端逻辑 this.divideBy10Button = new ActionEPPButton((b) -> { - try { - net.minecraft.client.Minecraft minecraft = net.minecraft.client.Minecraft.getInstance(); - if (minecraft.level != null && minecraft.player != null) { - net.minecraft.server.level.ServerPlayer serverPlayer = minecraft.getSingleplayerServer() - .getPlayerList().getPlayer(minecraft.player.getUUID()); - if (serverPlayer != null) { - executePatternScalingOnServer(serverPlayer, "DIVIDE", 10.0); - } else { - System.out.println("ExtendedAE Plus: 无法获取服务器端玩家实例"); - } - } else { - System.out.println("ExtendedAE Plus: 单机服务器未启动或玩家为null"); - } - } catch (Exception e) { - System.out.println("ExtendedAE Plus: 执行样板/10时发生错误:" + e.getMessage()); - e.printStackTrace(); - } + System.out.println("[EAE+][Client] click divide10"); + EPPNetworkHandler.INSTANCE.sendToServer(new CGenericPacket("divide10")); }, NewIcon.DIVIDE10); this.divideBy10Button.setVisibility(true); - // /5 按钮 - 单机模式直接调用服务器端逻辑 this.divideBy5Button = new ActionEPPButton((b) -> { - try { - net.minecraft.client.Minecraft minecraft = net.minecraft.client.Minecraft.getInstance(); - if (minecraft.level != null && minecraft.player != null) { - net.minecraft.server.level.ServerPlayer serverPlayer = minecraft.getSingleplayerServer() - .getPlayerList().getPlayer(minecraft.player.getUUID()); - if (serverPlayer != null) { - executePatternScalingOnServer(serverPlayer, "DIVIDE", 5.0); - } else { - System.out.println("ExtendedAE Plus: 无法获取服务器端玩家实例"); - } - } else { - System.out.println("ExtendedAE Plus: 单机服务器未启动或玩家为null"); - } - } catch (Exception e) { - System.out.println("ExtendedAE Plus: 执行样板/5时发生错误:" + e.getMessage()); - e.printStackTrace(); - } + System.out.println("[EAE+][Client] click divide5"); + EPPNetworkHandler.INSTANCE.sendToServer(new CGenericPacket("divide5")); }, NewIcon.DIVIDE5); this.divideBy5Button.setVisibility(true); - // 注册新增按钮 + this.x5Button = new ActionEPPButton((b) -> { + System.out.println("[EAE+][Client] click multiply5"); + EPPNetworkHandler.INSTANCE.sendToServer(new CGenericPacket("multiply5")); + }, NewIcon.MULTIPLY5); + this.x5Button.setVisibility(true); + + // 注册可渲染按钮 + this.addRenderableWidget(this.divideBy2Button); + this.addRenderableWidget(this.x2Button); this.addRenderableWidget(this.divideBy5Button); this.addRenderableWidget(this.x5Button); this.addRenderableWidget(this.divideBy10Button); diff --git a/src/main/java/com/extendedae_plus/mixin/GuiExPatternTerminalMixin.java b/src/main/java/com/extendedae_plus/mixin/GuiExPatternTerminalMixin.java index 5554a37..21ff573 100644 --- a/src/main/java/com/extendedae_plus/mixin/GuiExPatternTerminalMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/GuiExPatternTerminalMixin.java @@ -5,6 +5,8 @@ import appeng.client.gui.AEBaseScreen; import appeng.client.gui.style.ScreenStyle; import appeng.client.gui.widgets.IconButton; import com.extendedae_plus.util.ExtendedAEPatternUploadUtil; +import com.glodblock.github.extendedae.network.EPPNetworkHandler; +import com.glodblock.github.glodium.network.packet.CGenericPacket; import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal; import com.glodblock.github.extendedae.client.gui.GuiWirelessExPAT; import com.glodblock.github.extendedae.container.ContainerExPatternTerminal; @@ -109,28 +111,10 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen { - // 获取服务器端的玩家实例 - if (this.minecraft.getSingleplayerServer() != null) { - var serverPlayer = this.minecraft.getSingleplayerServer().getPlayerList() - .getPlayer(this.minecraft.player.getUUID()); - - if (serverPlayer != null) { - // 直接调用服务器端上传逻辑 - boolean success = ExtendedAEPatternUploadUtil.uploadPatternToProvider( - serverPlayer, - playerSlotIndex, - currentlychooicepatterprovider - ); - - // 取消上传完成后的左下角提示 - } - } - }); + // 通过 ExtendedAE 内置网络系统发送通用动作到服务端 + // 动作: "upload",参数: 槽位索引(int)、供应器ID(long) + System.out.println("[EAE+][Client] send upload: slot=" + playerSlotIndex + ", provider=" + currentlychooicepatterprovider); + EPPNetworkHandler.INSTANCE.sendToServer(new CGenericPacket("upload", playerSlotIndex, currentlychooicepatterprovider)); } else { this.minecraft.player.displayClientMessage( diff --git a/src/main/java/com/extendedae_plus/network/NetworkHandler.java b/src/main/java/com/extendedae_plus/network/NetworkHandler.java index c7a0697..07dd0dd 100644 --- a/src/main/java/com/extendedae_plus/network/NetworkHandler.java +++ b/src/main/java/com/extendedae_plus/network/NetworkHandler.java @@ -14,19 +14,40 @@ import net.minecraftforge.network.simple.SimpleChannel; public class NetworkHandler { private static final String PROTOCOL_VERSION = "1"; - public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel( - new ResourceLocation("extendedae_plus", "main"), - () -> PROTOCOL_VERSION, - PROTOCOL_VERSION::equals, - PROTOCOL_VERSION::equals - ); - + private static SimpleChannel INSTANCE; private static int packetId = 0; + private static boolean registered = false; + + /** + * 在合法的注册阶段创建通道。可重复调用(幂等)。 + */ + private static void initChannel() { + if (INSTANCE == null) { + INSTANCE = NetworkRegistry.newSimpleChannel( + new ResourceLocation("extendedae_plus", "main"), + () -> PROTOCOL_VERSION, + PROTOCOL_VERSION::equals, + PROTOCOL_VERSION::equals + ); + } + } + + /** + * 供 Mod 早期调用,确保在注册窗口关闭前完成通道与包注册。 + */ + public static void initialize() { + if (!registered) { + initChannel(); + registerPackets(); + } + } /** * 注册所有网络包 */ public static void registerPackets() { + if (registered) return; + initChannel(); // 样板上传请求包(客户端 -> 服务器) INSTANCE.messageBuilder(PatternUploadPacket.class, packetId++, NetworkDirection.PLAY_TO_SERVER) .decoder(PatternUploadPacket::decode) @@ -54,12 +75,15 @@ public class NetworkHandler { .encoder(PatternScalingResultPacket::encode) .consumerMainThread(PatternScalingResultPacket::handle) .add(); + registered = true; } /** * 发送包到服务器 */ public static void sendToServer(Object packet) { + // 如果出现 null,说明初始化阶段未被调用,避免在锁定后临时创建通道 + if (INSTANCE == null) throw new IllegalStateException("Network channel not initialized"); INSTANCE.sendToServer(packet); } @@ -67,6 +91,7 @@ public class NetworkHandler { * 发送包到指定客户端 */ public static void sendToClient(Object packet, ServerPlayer player) { + if (INSTANCE == null) throw new IllegalStateException("Network channel not initialized"); INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), packet); } }