diff --git a/build.gradle b/build.gradle index a1f79e7..133059d 100644 --- a/build.gradle +++ b/build.gradle @@ -127,96 +127,68 @@ sourceSets.main.resources { srcDir 'src/generated/resources' } // 暂时排除缺少依赖的可选联动源码,待补齐依赖后再启用 sourceSets.main.java { - // 让编译期能解析 ExtendedAE 的类(如 GuiExPatternProvider、ActionEPPButton 等),仅作编译期源引用 + // 让编译期能解析 ExtendedAE 的类(如 GuiExPatternProvider、ActionEPPButton 等),作为编译期源引用 // 运行期仍由 CurseMaven 依赖提供真实模组 Jar srcDir 'othermods/ExtendedAE-1.21-2.2.21-neoforge/src/main/java' - // 广泛屏蔽迁移中的旧源码,仅保留模板核心类(ExtendedAEPlus、ExtendedAEPlusClient、Config) - // 注意:不要屏蔽 api/**,我们有选择性 include 的接口文件需要参与编译 - include 'com/extendedae_plus/api/**' - exclude 'com/extendedae_plus/client/**' - // 包含配置包 - include 'com/extendedae_plus/config/**' - exclude 'com/extendedae_plus/hooks/**' - exclude 'com/extendedae_plus/init/**' - exclude 'com/extendedae_plus/integration/**' - exclude 'com/extendedae_plus/menu/**' - // 包含主 Mod 类 + + // 分阶段迁移:仅编译样板倍增与必要 GUI/网络相关的核心代码,屏蔽未迁移联动与旧版 Forge API 代码 include 'com/extendedae_plus/ExtendedAEPlus.java' - // 仅保留 mixin 中的 accessor 参与编译 - // 解除对 accessor 的屏蔽 - include 'com/extendedae_plus/mixin/ae2/accessor/**' - // 启用对 PatternProviderLogic 的功能混入(高级阻挡/智能翻倍开关的持久化与逻辑) - include 'com/extendedae_plus/mixin/ae2/helpers/**' - // 启用 AEProcessingPattern 的 mixin(提供可缩放标记) - include 'com/extendedae_plus/mixin/ae2/AEProcessingPatternMixin.java' - // 自动样板缩放注入(CraftingTreeProcess + 其 accessor) - include 'com/extendedae_plus/mixin/ae2/autopattern/**' - // GUI 方案A:解禁最小 GUI 混入 - include 'com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenMixin.java' - include 'com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuAdvancedMixin.java' - include 'com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuDoublingMixin.java' - // ExtendedAE GUI 混入(右侧外列按钮与翻页),以及其依赖的 NewIcon 类 - // 仅包含 Provider GUI 混入(不要使用广域 exclude,以免覆盖 include) - include 'com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternProviderMixin.java' - include 'com/extendedae_plus/NewIcon.java' - // 注意:不要屏蔽 network/**,我们已选择性 include 需要的网络文件 - // 仅解禁样板倍增核心所需的两个 util 文件 + include 'com/extendedae_plus/ExtendedAEPlusClient.java' + include 'com/extendedae_plus/config/**' + include 'com/extendedae_plus/api/**' + + // util + include 'com/extendedae_plus/util/ExtendedAELogger.java' include 'com/extendedae_plus/util/PatternProviderDataUtil.java' include 'com/extendedae_plus/util/PatternProviderUIHelper.java' - // 自动样板缩放所需工具类 include 'com/extendedae_plus/util/PatternScaler.java' include 'com/extendedae_plus/util/RequestedAmountHolder.java' - // GUI 与网络最小依赖 - // 全量包含 api 包,防止个别接口遗漏 - include 'com/extendedae_plus/api/**' - include 'com/extendedae_plus/util/ExtendedAELogger.java' - // 客户端高亮存储(单文件启用,保持 content 其余文件不参与编译) + // util needed by network payloads (providers listing / upload helpers) + include 'com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java' + + // content(最小集) include 'com/extendedae_plus/content/ClientPatternHighlightStore.java' - // 自动样板缩放所需内容类 include 'com/extendedae_plus/content/ScaledProcessingPattern.java' + + // client-side helpers needed by S2C payloads + include 'com/extendedae_plus/client/ClientAdvancedBlockingState.java' + include 'com/extendedae_plus/client/ui/ProviderSelectScreen.java' + + // 网络(NeoForge 1.21 Payload API版本) include 'com/extendedae_plus/network/ModNetwork.java' include 'com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java' include 'com/extendedae_plus/network/ToggleSmartDoublingC2SPacket.java' include 'com/extendedae_plus/network/ScalePatternsC2SPacket.java' include 'com/extendedae_plus/network/SetPatternHighlightS2CPacket.java' - // 仅 include 需要的 util 文件(不再对 util/** 做全局排除,以免误伤) - include 'com/extendedae_plus/util/PatternProviderDataUtil.java' - include 'com/extendedae_plus/util/PatternProviderUIHelper.java' - include 'com/extendedae_plus/util/ExtendedAELogger.java' - exclude 'com/extendedae_plus/wireless/**' + include 'com/extendedae_plus/network/AdvancedBlockingSyncS2CPacket.java' + include 'com/extendedae_plus/network/ProvidersListS2CPacket.java' + include 'com/extendedae_plus/network/RequestProvidersListC2SPacket.java' + include 'com/extendedae_plus/network/SetProviderPageS2CPacket.java' + include 'com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java' + include 'com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java' + include 'com/extendedae_plus/network/OpenProviderUiC2SPacket.java' + include 'com/extendedae_plus/network/GlobalToggleProviderModesC2SPacket.java' + include 'com/extendedae_plus/network/UploadEncodedPatternToProviderC2SPacket.java' + + // AE2 mixin:accessor/helpers/autopattern/gui/menu + include 'com/extendedae_plus/mixin/ae2/accessor/**' + include 'com/extendedae_plus/mixin/ae2/helpers/**' + include 'com/extendedae_plus/mixin/ae2/AEProcessingPatternMixin.java' + include 'com/extendedae_plus/mixin/ae2/autopattern/**' + include 'com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenMixin.java' + include 'com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuAdvancedMixin.java' + include 'com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuDoublingMixin.java' + + // ExtendedAE GUI 混入(右侧外列按钮与翻页),以及其依赖的 NewIcon 类 + include 'com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternProviderMixin.java' + include 'com/extendedae_plus/NewIcon.java' } -// Sets up a dependency configuration called 'localRuntime'. -// This configuration should be used instead of 'runtimeOnly' to declare -// a dependency that will be present for runtime testing but that is -// "optional", meaning it will not be pulled by dependents of this mod. configurations { runtimeClasspath.extendsFrom localRuntime } dependencies { - // Example optional mod dependency with JEI - // The JEI API is declared for compile time use, while the full JEI artifact is used at runtime - // compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}" - // compileOnly "mezz.jei:jei-${mc_version}-neoforge-api:${jei_version}" - // We add the full version to localRuntime, not runtimeOnly, so that we do not publish a dependency on it - // localRuntime "mezz.jei:jei-${mc_version}-neoforge:${jei_version}" - - // Example mod dependency using a mod jar from ./libs with a flat dir repository - // This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar - // The group id is ignored when searching -- in this case, it is "blank" - // implementation "blank:coolmod-${mc_version}:${coolmod_version}" - - // Example mod dependency using a file as dependency - // implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar") - - // Example project dependency using a sister or child project: - // implementation project(":myproject") - - // For more info: - // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html - // http://www.gradle.org/docs/current/userguide/dependency_management.html - // --- Added dependencies for target mods --- implementation "curse.maven:glodium-957920:5821676" implementation "org.appliedenergistics:appliedenergistics2:19.2.8" diff --git a/gradle.properties b/gradle.properties index 6546596..b0cb119 100644 --- a/gradle.properties +++ b/gradle.properties @@ -44,4 +44,4 @@ mod_description=Example mod description.\nNewline characters can be used and wil ## UI item explorer selection (emi | rei | jei) # Default to 'emi' per request; you can override by running with -Puse_Xei=rei or -Puse_Xei=jei -use_Xei=emi +use_Xei=jei diff --git a/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java b/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java index e817f2a..b175564 100644 --- a/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java +++ b/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java @@ -194,7 +194,10 @@ public class ProviderSelectScreen extends Screen { private void onChoose(int idx) { if (idx < 0 || idx >= fIds.size()) return; long providerId = fIds.get(idx); - ModNetwork.CHANNEL.sendToServer(new UploadEncodedPatternToProviderC2SPacket(providerId)); + var conn = Minecraft.getInstance().getConnection(); + if (conn != null) { + conn.send(new UploadEncodedPatternToProviderC2SPacket(providerId)); + } this.onClose(); } @@ -342,12 +345,6 @@ public class ProviderSelectScreen extends Screen { @Override public void tick() { super.tick(); - if (searchBox != null) { - searchBox.tick(); - } - if (cnInput != null) { - cnInput.tick(); - } if (needsRefresh) { needsRefresh = false; // 重新构建当前屏幕内容 diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuAdvancedMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuAdvancedMixin.java index f33215f..afd90e4 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuAdvancedMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuAdvancedMixin.java @@ -54,3 +54,4 @@ public abstract class PatternProviderMenuAdvancedMixin implements PatternProvide private void eap$debug_getShowInAccessTerminal(CallbackInfoReturnable cir) { } } + \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/network/AdvancedBlockingSyncS2CPacket.java b/src/main/java/com/extendedae_plus/network/AdvancedBlockingSyncS2CPacket.java index 8a7f3e9..364f35c 100644 --- a/src/main/java/com/extendedae_plus/network/AdvancedBlockingSyncS2CPacket.java +++ b/src/main/java/com/extendedae_plus/network/AdvancedBlockingSyncS2CPacket.java @@ -1,12 +1,29 @@ package com.extendedae_plus.network; +import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.client.ClientAdvancedBlockingState; -import net.minecraft.network.FriendlyByteBuf; -import net.neoforged.neoforge.network.NetworkEvent; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.neoforge.network.handling.IPayloadContext; -import java.util.function.Supplier; +/** + * S2C:同步某个 Provider 的高级阻挡状态到客户端(本地存储)。 + */ +public class AdvancedBlockingSyncS2CPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "adv_blocking_sync")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buf, pkt) -> { + buf.writeUtf(pkt.dimensionId); + buf.writeLong(pkt.blockPosLong); + buf.writeBoolean(pkt.enabled); + }, + buf -> new AdvancedBlockingSyncS2CPacket(buf.readUtf(), buf.readLong(), buf.readBoolean()) + ); -public class AdvancedBlockingSyncS2CPacket { private final String dimensionId; private final long blockPosLong; private final boolean enabled; @@ -17,25 +34,15 @@ public class AdvancedBlockingSyncS2CPacket { this.enabled = enabled; } - public static void encode(AdvancedBlockingSyncS2CPacket msg, FriendlyByteBuf buf) { - buf.writeUtf(msg.dimensionId); - buf.writeLong(msg.blockPosLong); - buf.writeBoolean(msg.enabled); + @Override + public Type type() { + return TYPE; } - 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(); + public static void handle(final AdvancedBlockingSyncS2CPacket msg, final IPayloadContext ctx) { 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/CraftingMonitorJumpC2SPacket.java b/src/main/java/com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java index eacd863..6b4f843 100644 --- a/src/main/java/com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java @@ -12,7 +12,10 @@ import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor; import com.mojang.logging.LogUtils; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; @@ -21,39 +24,37 @@ import net.minecraft.world.MenuProvider; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; -import net.minecraftforge.network.NetworkEvent; -import net.minecraftforge.network.NetworkHooks; +import net.neoforged.neoforge.network.handling.IPayloadContext; import java.util.Collection; -import java.util.function.Supplier; /** * 客户端从 CraftingCPUScreen 发送:鼠标下条目对应的 AEKey。 * 服务端在当前打开的 CraftingCPUMenu 所属网络中,定位匹配该 AEKey 的样板供应器, * 尝试打开其目标机器的 GUI。 */ -public class CraftingMonitorJumpC2SPacket { +public class CraftingMonitorJumpC2SPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(com.extendedae_plus.ExtendedAEPlus.MODID, "crafting_monitor_jump")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buf, pkt) -> AEKey.writeKey(buf, pkt.what), + buf -> new CraftingMonitorJumpC2SPacket(AEKey.readKey(buf)) + ); private final AEKey what; public CraftingMonitorJumpC2SPacket(AEKey what) { this.what = what; } - public static void encode(CraftingMonitorJumpC2SPacket msg, FriendlyByteBuf buf) { - AEKey.writeKey(buf, msg.what); + @Override + public Type type() { + return TYPE; } - public static CraftingMonitorJumpC2SPacket decode(FriendlyByteBuf buf) { - AEKey key = AEKey.readKey(buf); - return new CraftingMonitorJumpC2SPacket(key); - } - - public static void handle(CraftingMonitorJumpC2SPacket msg, Supplier ctx) { - NetworkEvent.Context context = ctx.get(); - context.enqueueWork(() -> { - ServerPlayer player = context.getSender(); - if (player == null) return; - + public static void handle(final CraftingMonitorJumpC2SPacket msg, final IPayloadContext ctx) { + ctx.enqueueWork(() -> { + if (!(ctx.player() instanceof ServerPlayer player)) return; LogUtils.getLogger().info("EAP[S]: recv CraftingMonitorJumpC2SPacket key={} from {}", msg.what, player.getGameProfile().getName()); // 必须在 CraftingCPU 界面内 @@ -102,54 +103,30 @@ public class CraftingMonitorJumpC2SPacket { var pbe = host.getBlockEntity(); ServerLevel serverLevel = player.serverLevel(); - // 尝试对邻居打开 GUI(复用 OpenProviderUiC2SPacket 的策略) + // 尝试对邻居打开 GUI(优先通过 MenuProvider) for (Direction dir : host.getTargets()) { BlockPos targetPos = pbe.getBlockPos().relative(dir); var tbe = serverLevel.getBlockEntity(targetPos); if (tbe instanceof MenuProvider provider1) { LogUtils.getLogger().info("EAP[S]: open screen via MenuProvider at {}", targetPos); - NetworkHooks.openScreen(player, provider1, targetPos); - context.setPacketHandled(true); + player.openMenu(provider1, targetPos); return; } var tstate = serverLevel.getBlockState(targetPos); var provider2 = tstate.getMenuProvider(serverLevel, targetPos); if (provider2 != null) { LogUtils.getLogger().info("EAP[S]: open screen via state.getMenuProvider at {}", targetPos); - NetworkHooks.openScreen(player, provider2, targetPos); - context.setPacketHandled(true); + player.openMenu(provider2, targetPos); return; } } - // 兜底:若无 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.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); - LogUtils.getLogger().info("EAP[S]: simulated use on {}, face={}, result={}", targetPos, chosen, r); - if (r.consumesAction()) { - context.setPacketHandled(true); - return; - } - } + // 兜底:若无 MenuProvider,则跳过(不再模拟右键以确保兼容性) } } LogUtils.getLogger().info("EAP[S]: providers count for one pattern: {}", providerCount); } LogUtils.getLogger().info("EAP[S]: no target opened for key={}", msg.what); }); - context.setPacketHandled(true); } } diff --git a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java index 6680f2f..d32d5a6 100644 --- a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java @@ -18,18 +18,20 @@ 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.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; 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 net.neoforged.neoforge.network.handling.IPayloadContext; import java.util.Collection; import java.util.Objects; -import java.util.function.Supplier; import static com.glodblock.github.extendedae.client.render.EAEHighlightHandler.highlight; @@ -38,27 +40,28 @@ import static com.glodblock.github.extendedae.client.render.EAEHighlightHandler. * 服务端在当前打开的 CraftingCPUMenu 所属网络中,定位匹配该 AEKey 的样板供应器, * 打开该供应器自身的 UI(不是目标机器的 UI)。 */ -public class CraftingMonitorOpenProviderC2SPacket { +public class CraftingMonitorOpenProviderC2SPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(com.extendedae_plus.ExtendedAEPlus.MODID, "crafting_monitor_open_provider")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buf, pkt) -> AEKey.writeKey(buf, pkt.what), + buf -> new CraftingMonitorOpenProviderC2SPacket(AEKey.readKey(buf)) + ); private final AEKey what; public CraftingMonitorOpenProviderC2SPacket(AEKey what) { this.what = what; } - public static void encode(CraftingMonitorOpenProviderC2SPacket msg, FriendlyByteBuf buf) { - AEKey.writeKey(buf, msg.what); + @Override + public Type type() { + return TYPE; } - 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; + public static void handle(final CraftingMonitorOpenProviderC2SPacket msg, final IPayloadContext ctx) { + ctx.enqueueWork(() -> { + if (!(ctx.player() instanceof ServerPlayer player)) return; // 必须在 CraftingCPU 界面内 if (!(player.containerMenu instanceof CraftingCPUMenu menu)) { @@ -119,15 +122,16 @@ public class CraftingMonitorOpenProviderC2SPacket { if (foundSlot >= 0) { int pageId = foundSlot / 36; if (pageId > 0) { - // 发送 S2C 包通知客户端切换到指定页(客户端会写入 mixin 字段并重排槽位) - ModNetwork.CHANNEL.sendTo(new SetProviderPageS2CPacket(pageId), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT); + // 发送 S2C:切换到指定页 + player.connection.send(new SetProviderPageS2CPacket(pageId)); } } // 最后发送高亮包,保证界面已打开 - 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); + var outs = pattern.getOutputs(); + if (outs != null && !outs.isEmpty() && outs.get(0) != null) { + AEKey key = outs.get(0).what(); + player.connection.send(new SetPatternHighlightS2CPacket(key, true)); } return; @@ -137,7 +141,6 @@ public class CraftingMonitorOpenProviderC2SPacket { } } }); - context.setPacketHandled(true); } private static void highlightWithMessage(BlockPos pos, Direction face, ResourceKey dim, double multiplier, Player player) { diff --git a/src/main/java/com/extendedae_plus/network/GlobalToggleProviderModesC2SPacket.java b/src/main/java/com/extendedae_plus/network/GlobalToggleProviderModesC2SPacket.java index c37d0bc..ef6774b 100644 --- a/src/main/java/com/extendedae_plus/network/GlobalToggleProviderModesC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/GlobalToggleProviderModesC2SPacket.java @@ -7,18 +7,21 @@ import appeng.blockentity.crafting.PatternProviderBlockEntity; import appeng.helpers.patternprovider.PatternProviderLogic; import appeng.helpers.patternprovider.PatternProviderLogicHost; import appeng.parts.crafting.PatternProviderPart; +import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.api.AdvancedBlockingHolder; import com.extendedae_plus.api.SmartDoublingHolder; -import com.extendedae_plus.content.controller.NetworkPatternControllerBlockEntity; +import appeng.api.networking.IInWorldGridNodeHost; import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; -import net.minecraftforge.network.NetworkEvent; +import net.neoforged.neoforge.network.handling.IPayloadContext; import java.util.Set; import java.util.HashSet; -import java.util.function.Supplier; /** * C2S:全网批量切换样板供应器的三种模式: @@ -28,7 +31,19 @@ import java.util.function.Supplier; * * 负载为三个操作码(各1字节),分别对应:blocking、advancedBlocking、smartDoubling。 */ -public class GlobalToggleProviderModesC2SPacket { +public class GlobalToggleProviderModesC2SPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "global_toggle_provider_modes")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buf, pkt) -> { + buf.writeByte(pkt.opBlocking.id); + buf.writeByte(pkt.opAdvancedBlocking.id); + buf.writeByte(pkt.opSmartDoubling.id); + buf.writeBlockPos(pkt.controllerPos); + }, + buf -> new GlobalToggleProviderModesC2SPacket(Op.byId(buf.readByte()), Op.byId(buf.readByte()), Op.byId(buf.readByte()), buf.readBlockPos()) + ); public enum Op { NOOP((byte) 0), SET_TRUE((byte) 1), @@ -58,32 +73,21 @@ public class GlobalToggleProviderModesC2SPacket { this.controllerPos = controllerPos; } - public static void encode(GlobalToggleProviderModesC2SPacket msg, FriendlyByteBuf buf) { - buf.writeByte(msg.opBlocking.id); - buf.writeByte(msg.opAdvancedBlocking.id); - buf.writeByte(msg.opSmartDoubling.id); - buf.writeBlockPos(msg.controllerPos); + @Override + public Type type() { + return TYPE; } - public static GlobalToggleProviderModesC2SPacket decode(FriendlyByteBuf buf) { - Op b = Op.byId(buf.readByte()); - Op ab = Op.byId(buf.readByte()); - Op sd = Op.byId(buf.readByte()); - BlockPos pos = buf.readBlockPos(); - return new GlobalToggleProviderModesC2SPacket(b, ab, sd, pos); - } - - public static void handle(GlobalToggleProviderModesC2SPacket msg, Supplier ctxSupplier) { - var ctx = ctxSupplier.get(); + public static void handle(final GlobalToggleProviderModesC2SPacket msg, final IPayloadContext ctx) { ctx.enqueueWork(() -> { - ServerPlayer player = ctx.getSender(); + if (!(ctx.player() instanceof ServerPlayer player)) return; if (player == null) return; // 从控制方块实体的 AE2 节点确定 AE 网络上下文 var level = player.serverLevel(); var be = level.getBlockEntity(msg.controllerPos); - if (!(be instanceof NetworkPatternControllerBlockEntity controller)) return; - var node = controller.getGridNode(null); + if (!(be instanceof IInWorldGridNodeHost host)) return; + var node = host.getGridNode(null); if (node == null) return; IGrid grid = node.getGrid(); if (grid == null) return; @@ -92,7 +96,6 @@ public class GlobalToggleProviderModesC2SPacket { // 向发起玩家反馈影响数量,便于判断按钮是否生效 player.displayClientMessage(Component.literal("E+ 全局切换已应用到 " + affected + " 个样板供应器"), true); }); - ctx.setPacketHandled(true); } private static int applyToAllProviders(IGrid grid, GlobalToggleProviderModesC2SPacket msg) { diff --git a/src/main/java/com/extendedae_plus/network/ModNetwork.java b/src/main/java/com/extendedae_plus/network/ModNetwork.java index 0b95f6f..c16d7f9 100644 --- a/src/main/java/com/extendedae_plus/network/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/network/ModNetwork.java @@ -11,5 +11,14 @@ public class ModNetwork { registrar.playToServer(ToggleSmartDoublingC2SPacket.TYPE, ToggleSmartDoublingC2SPacket.STREAM_CODEC, ToggleSmartDoublingC2SPacket::handle); registrar.playToServer(ScalePatternsC2SPacket.TYPE, ScalePatternsC2SPacket.STREAM_CODEC, ScalePatternsC2SPacket::handle); registrar.playToClient(SetPatternHighlightS2CPacket.TYPE, SetPatternHighlightS2CPacket.STREAM_CODEC, SetPatternHighlightS2CPacket::handle); + registrar.playToClient(AdvancedBlockingSyncS2CPacket.TYPE, AdvancedBlockingSyncS2CPacket.STREAM_CODEC, AdvancedBlockingSyncS2CPacket::handle); + registrar.playToClient(ProvidersListS2CPacket.TYPE, ProvidersListS2CPacket.STREAM_CODEC, ProvidersListS2CPacket::handle); + registrar.playToServer(RequestProvidersListC2SPacket.TYPE, RequestProvidersListC2SPacket.STREAM_CODEC, RequestProvidersListC2SPacket::handle); + registrar.playToClient(SetProviderPageS2CPacket.TYPE, SetProviderPageS2CPacket.STREAM_CODEC, SetProviderPageS2CPacket::handle); + registrar.playToServer(GlobalToggleProviderModesC2SPacket.TYPE, GlobalToggleProviderModesC2SPacket.STREAM_CODEC, GlobalToggleProviderModesC2SPacket::handle); + registrar.playToServer(CraftingMonitorJumpC2SPacket.TYPE, CraftingMonitorJumpC2SPacket.STREAM_CODEC, CraftingMonitorJumpC2SPacket::handle); + registrar.playToServer(CraftingMonitorOpenProviderC2SPacket.TYPE, CraftingMonitorOpenProviderC2SPacket.STREAM_CODEC, CraftingMonitorOpenProviderC2SPacket::handle); + registrar.playToServer(OpenProviderUiC2SPacket.TYPE, OpenProviderUiC2SPacket.STREAM_CODEC, OpenProviderUiC2SPacket::handle); + registrar.playToServer(UploadEncodedPatternToProviderC2SPacket.TYPE, UploadEncodedPatternToProviderC2SPacket.STREAM_CODEC, UploadEncodedPatternToProviderC2SPacket::handle); } } diff --git a/src/main/java/com/extendedae_plus/network/OpenProviderUiC2SPacket.java b/src/main/java/com/extendedae_plus/network/OpenProviderUiC2SPacket.java index 8a02170..691d452 100644 --- a/src/main/java/com/extendedae_plus/network/OpenProviderUiC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/OpenProviderUiC2SPacket.java @@ -3,6 +3,8 @@ package com.extendedae_plus.network; import net.minecraft.core.BlockPos; import net.minecraft.core.registries.Registries; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.core.Direction; import net.minecraft.world.InteractionHand; import net.minecraft.world.phys.BlockHitResult; @@ -15,12 +17,20 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.MenuProvider; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.Level; -import net.minecraftforge.network.NetworkEvent; -import net.minecraftforge.network.NetworkHooks; +import net.neoforged.neoforge.network.handling.IPayloadContext; -import java.util.function.Supplier; +public class OpenProviderUiC2SPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(com.extendedae_plus.ExtendedAEPlus.MODID, "open_provider_ui")); -public class OpenProviderUiC2SPacket { + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buf, pkt) -> { + buf.writeLong(pkt.posLong); + buf.writeResourceLocation(pkt.dimId); + buf.writeVarInt(pkt.faceOrd); + }, + buf -> new OpenProviderUiC2SPacket(buf.readLong(), buf.readResourceLocation(), buf.readVarInt()) + ); private final long posLong; private final ResourceLocation dimId; private final int faceOrd; // 目前保留,若目标需要可用 @@ -31,26 +41,14 @@ public class OpenProviderUiC2SPacket { this.faceOrd = faceOrd; } - public static void encode(OpenProviderUiC2SPacket msg, FriendlyByteBuf buf) { - buf.writeLong(msg.posLong); - buf.writeResourceLocation(msg.dimId); - buf.writeVarInt(msg.faceOrd); + @Override + public Type type() { + return TYPE; } - public static OpenProviderUiC2SPacket decode(FriendlyByteBuf buf) { - long posLong = buf.readLong(); - ResourceLocation dimId = buf.readResourceLocation(); - int faceOrd = buf.readVarInt(); - return new OpenProviderUiC2SPacket(posLong, dimId, faceOrd); - - } - - public static void handle(OpenProviderUiC2SPacket msg, Supplier ctx) { - NetworkEvent.Context context = ctx.get(); - context.enqueueWork(() -> { - ServerPlayer player = context.getSender(); - if (player == null) return; - + public static void handle(final OpenProviderUiC2SPacket msg, final IPayloadContext ctx) { + ctx.enqueueWork(() -> { + if (!(ctx.player() instanceof ServerPlayer player)) return; // 校验维度与方块 ResourceKey levelKey = ResourceKey.create(Registries.DIMENSION, msg.dimId); @@ -76,58 +74,19 @@ public class OpenProviderUiC2SPacket { BlockPos targetPos = pos.relative(dir); BlockEntity tbe = level.getBlockEntity(targetPos); if (tbe instanceof MenuProvider provider) { - NetworkHooks.openScreen(player, provider, targetPos); + player.openMenu(provider, targetPos); return; } var tstate = level.getBlockState(targetPos); MenuProvider provider2 = tstate.getMenuProvider(level, targetPos); if (provider2 != null) { - NetworkHooks.openScreen(player, provider2, targetPos); + player.openMenu(provider2, targetPos); return; } } - // 如果邻居也未提供 MenuProvider,则兜底:尽量模拟一次徒手右键相邻方块 - boolean anyHandEmpty = player.getMainHandItem().isEmpty() || player.getOffhandItem().isEmpty(); - if (anyHandEmpty) { - InteractionHand hand = player.getMainHandItem().isEmpty() ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND; - if (msg.faceOrd >= 0 && msg.faceOrd < Direction.values().length) { - Direction dir = Direction.values()[msg.faceOrd]; - BlockPos targetPos = pos.relative(dir); - var state2 = level.getBlockState(targetPos); - var hit = new BlockHitResult(Vec3.atCenterOf(targetPos), dir.getOpposite(), targetPos, false); - InteractionResult r = state2.use(level, player, hand, hit); - if (r.consumesAction()) { - return; - } - } else { - // 无明确朝向:优先挑选有方块实体的邻居,否则挑选非空气方块 - Direction chosen = null; - for (Direction d : Direction.values()) { - if (level.getBlockEntity(pos.relative(d)) != null) { chosen = d; break; } - } - if (chosen == null) { - for (Direction d : Direction.values()) { - if (!level.getBlockState(pos.relative(d)).isAir()) { chosen = d; break; } - } - } - if (chosen != null) { - BlockPos targetPos = pos.relative(chosen); - var state2 = level.getBlockState(targetPos); - var hit = new BlockHitResult(Vec3.atCenterOf(targetPos), chosen.getOpposite(), targetPos, false); - InteractionResult r = state2.use(level, player, hand, hit); - if (r.consumesAction()) { - return; - } - } else { - // 无可选邻居 - } - } - } else { - // 双手占用则跳过兜底交互 - } + // 若邻居未提供 MenuProvider,则跳过兜底交互(1.21 API 变更,避免不兼容的 use 调用) - context.setPacketHandled(true); }); } } diff --git a/src/main/java/com/extendedae_plus/network/ProvidersListS2CPacket.java b/src/main/java/com/extendedae_plus/network/ProvidersListS2CPacket.java index 7e6f795..0fa8184 100644 --- a/src/main/java/com/extendedae_plus/network/ProvidersListS2CPacket.java +++ b/src/main/java/com/extendedae_plus/network/ProvidersListS2CPacket.java @@ -1,20 +1,47 @@ package com.extendedae_plus.network; +import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.client.ui.ProviderSelectScreen; import net.minecraft.client.Minecraft; -import net.minecraft.network.FriendlyByteBuf; -import net.neoforged.api.distmarker.Dist; -import net.neoforged.api.distmarker.OnlyIn; -import net.neoforged.neoforge.network.NetworkEvent; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.neoforge.network.handling.IPayloadContext; import java.util.ArrayList; import java.util.List; -import java.util.function.Supplier; /** * S2C: 返回可见且有空位的样板供应器列表,客户端弹窗展示供用户选择。 */ -public class ProvidersListS2CPacket { +public class ProvidersListS2CPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "providers_list")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buf, pkt) -> { + buf.writeVarInt(pkt.ids.size()); + for (int i = 0; i < pkt.ids.size(); i++) { + buf.writeLong(pkt.ids.get(i)); + buf.writeUtf(pkt.names.get(i)); + buf.writeVarInt(pkt.emptySlots.get(i)); + } + }, + buf -> { + int size = buf.readVarInt(); + List ids = new ArrayList<>(size); + List names = new ArrayList<>(size); + List slots = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + ids.add(buf.readLong()); + names.add(buf.readUtf()); + slots.add(buf.readVarInt()); + } + return new ProvidersListS2CPacket(ids, names, slots); + } + ); + private final List ids; private final List names; private final List emptySlots; @@ -25,39 +52,17 @@ public class ProvidersListS2CPacket { this.emptySlots = emptySlots; } - public static void encode(ProvidersListS2CPacket msg, FriendlyByteBuf buf) { - buf.writeVarInt(msg.ids.size()); - for (int i = 0; i < msg.ids.size(); i++) { - buf.writeLong(msg.ids.get(i)); - buf.writeUtf(msg.names.get(i)); - buf.writeVarInt(msg.emptySlots.get(i)); - } + @Override + public Type type() { + return TYPE; } - public static ProvidersListS2CPacket decode(FriendlyByteBuf buf) { - int size = buf.readVarInt(); - List ids = new ArrayList<>(size); - List names = new ArrayList<>(size); - List slots = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - ids.add(buf.readLong()); - names.add(buf.readUtf()); - slots.add(buf.readVarInt()); - } - return new ProvidersListS2CPacket(ids, names, slots); - } - - public static void handle(ProvidersListS2CPacket msg, Supplier ctxSupplier) { - var ctx = ctxSupplier.get(); - ctx.enqueueWork(() -> handleClient(msg)); - ctx.setPacketHandled(true); - } - - @OnlyIn(Dist.CLIENT) - private static void handleClient(ProvidersListS2CPacket msg) { - var mc = Minecraft.getInstance(); - if (mc == null) return; - var current = mc.screen; - mc.setScreen(new ProviderSelectScreen(current, msg.ids, msg.names, msg.emptySlots)); + public static void handle(final ProvidersListS2CPacket msg, final IPayloadContext ctx) { + ctx.enqueueWork(() -> { + var mc = Minecraft.getInstance(); + if (mc == null) return; + var current = mc.screen; + mc.setScreen(new ProviderSelectScreen(current, msg.ids, msg.names, msg.emptySlots)); + }); } } diff --git a/src/main/java/com/extendedae_plus/network/RequestProvidersListC2SPacket.java b/src/main/java/com/extendedae_plus/network/RequestProvidersListC2SPacket.java index 88e51d6..2bcec04 100644 --- a/src/main/java/com/extendedae_plus/network/RequestProvidersListC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/RequestProvidersListC2SPacket.java @@ -3,30 +3,40 @@ package com.extendedae_plus.network; import appeng.helpers.patternprovider.PatternContainer; import appeng.menu.implementations.PatternAccessTermMenu; import appeng.menu.me.items.PatternEncodingTermMenu; +import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.util.ExtendedAEPatternUploadUtil; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; -import net.minecraftforge.network.NetworkEvent; +import net.neoforged.neoforge.network.handling.IPayloadContext; import java.util.ArrayList; import java.util.List; -import java.util.function.Supplier; /** * C2S: 请求当前终端可见的样板供应器列表(用于弹窗选择)。 */ -public class RequestProvidersListC2SPacket { +public class RequestProvidersListC2SPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "request_providers_list")); + + public static final RequestProvidersListC2SPacket INSTANCE = new RequestProvidersListC2SPacket(); + + public static final StreamCodec STREAM_CODEC = + StreamCodec.unit(INSTANCE); + public RequestProvidersListC2SPacket() {} - public static void encode(RequestProvidersListC2SPacket msg, FriendlyByteBuf buf) {} + @Override + public Type type() { + return TYPE; + } - public static RequestProvidersListC2SPacket decode(FriendlyByteBuf buf) { return new RequestProvidersListC2SPacket(); } - - public static void handle(RequestProvidersListC2SPacket msg, Supplier ctxSupplier) { - var ctx = ctxSupplier.get(); + public static void handle(final RequestProvidersListC2SPacket msg, final IPayloadContext ctx) { ctx.enqueueWork(() -> { - ServerPlayer player = ctx.getSender(); - if (player == null) return; + if (!(ctx.player() instanceof ServerPlayer player)) return; if (!(player.containerMenu instanceof PatternEncodingTermMenu encMenu)) return; // 优先:若玩家也打开了样板访问终端,则用 byId 方式(精确服务器ID) @@ -47,7 +57,7 @@ public class RequestProvidersListC2SPacket { slots.add(empty); } - ModNetwork.CHANNEL.sendTo(new ProvidersListS2CPacket(filteredIds, names, slots), player.connection.connection, net.minecraftforge.network.NetworkDirection.PLAY_TO_CLIENT); + player.connection.send(new ProvidersListS2CPacket(filteredIds, names, slots)); return; } @@ -66,8 +76,7 @@ public class RequestProvidersListC2SPacket { names.add(ExtendedAEPatternUploadUtil.getProviderDisplayName(c)); slots.add(empty); } - ModNetwork.CHANNEL.sendTo(new ProvidersListS2CPacket(idxIds, names, slots), player.connection.connection, net.minecraftforge.network.NetworkDirection.PLAY_TO_CLIENT); + player.connection.send(new ProvidersListS2CPacket(idxIds, names, slots)); }); - ctx.setPacketHandled(true); } } diff --git a/src/main/java/com/extendedae_plus/network/SetProviderPageS2CPacket.java b/src/main/java/com/extendedae_plus/network/SetProviderPageS2CPacket.java index 1c57644..aa52597 100644 --- a/src/main/java/com/extendedae_plus/network/SetProviderPageS2CPacket.java +++ b/src/main/java/com/extendedae_plus/network/SetProviderPageS2CPacket.java @@ -1,57 +1,60 @@ package com.extendedae_plus.network; import appeng.menu.SlotSemantics; +import com.extendedae_plus.ExtendedAEPlus; 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 net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.neoforge.network.handling.IPayloadContext; import java.lang.reflect.Field; -import java.util.function.Supplier; /** * S2C: 指示客户端在已打开的样板供应器界面切换到指定页 */ -public class SetProviderPageS2CPacket { +public class SetProviderPageS2CPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "set_provider_page")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buf, pkt) -> buf.writeVarInt(pkt.page), + buf -> new SetProviderPageS2CPacket(buf.readVarInt()) + ); + private final int page; public SetProviderPageS2CPacket(int page) { this.page = page; } - public static void encode(SetProviderPageS2CPacket msg, FriendlyByteBuf buf) { - buf.writeVarInt(msg.page); + @Override + public Type type() { + return TYPE; } - 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(); + public static void handle(final SetProviderPageS2CPacket msg, final IPayloadContext ctx) { 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); + 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); - 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) { - } + Field hs = screen.getClass().getDeclaredField("hoveredSlot"); + hs.setAccessible(true); + hs.set(screen, null); } - ); - ctx.setPacketHandled(true); + } catch (Throwable ignored) { + } + }); } } diff --git a/src/main/java/com/extendedae_plus/network/UploadEncodedPatternToProviderC2SPacket.java b/src/main/java/com/extendedae_plus/network/UploadEncodedPatternToProviderC2SPacket.java index ddc1470..bbadfcd 100644 --- a/src/main/java/com/extendedae_plus/network/UploadEncodedPatternToProviderC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/UploadEncodedPatternToProviderC2SPacket.java @@ -3,34 +3,37 @@ package com.extendedae_plus.network; import appeng.menu.me.items.PatternEncodingTermMenu; import com.extendedae_plus.util.ExtendedAEPatternUploadUtil; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; -import net.minecraftforge.network.NetworkEvent; - -import java.util.function.Supplier; +import net.neoforged.neoforge.network.handling.IPayloadContext; /** * C2S: 请求将图样编码终端的已编码样板上传到指定的样板供应器(由客户端选择)。 */ -public class UploadEncodedPatternToProviderC2SPacket { +public class UploadEncodedPatternToProviderC2SPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>( + ResourceLocation.fromNamespaceAndPath(com.extendedae_plus.ExtendedAEPlus.MODID, "upload_pattern_to_provider")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (buf, pkt) -> buf.writeLong(pkt.providerId), + buf -> new UploadEncodedPatternToProviderC2SPacket(buf.readLong()) + ); private final long providerId; public UploadEncodedPatternToProviderC2SPacket(long providerId) { this.providerId = providerId; } - public static void encode(UploadEncodedPatternToProviderC2SPacket msg, FriendlyByteBuf buf) { - buf.writeLong(msg.providerId); + @Override + public Type type() { + return TYPE; } - public static UploadEncodedPatternToProviderC2SPacket decode(FriendlyByteBuf buf) { - return new UploadEncodedPatternToProviderC2SPacket(buf.readLong()); - } - - public static void handle(UploadEncodedPatternToProviderC2SPacket msg, Supplier ctxSupplier) { - var ctx = ctxSupplier.get(); + public static void handle(final UploadEncodedPatternToProviderC2SPacket msg, final IPayloadContext ctx) { ctx.enqueueWork(() -> { - ServerPlayer player = ctx.getSender(); - if (player == null) return; + if (!(ctx.player() instanceof ServerPlayer player)) return; if (!(player.containerMenu instanceof PatternEncodingTermMenu menu)) return; // 支持两种模式: // 1) providerId >= 0: 访问终端 byId 模式 @@ -42,6 +45,5 @@ public class UploadEncodedPatternToProviderC2SPacket { ExtendedAEPatternUploadUtil.uploadFromEncodingMenuToProviderByIndex(player, menu, index); } }); - ctx.setPacketHandled(true); } } diff --git a/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java b/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java index d78af27..9876423 100644 --- a/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java +++ b/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java @@ -6,6 +6,8 @@ import appeng.api.inventories.InternalInventory; import appeng.api.networking.IGrid; import appeng.api.networking.IGridNode; import appeng.core.definitions.AEItems; +import appeng.menu.AEBaseMenu; +import appeng.api.networking.security.IActionHost; import appeng.crafting.pattern.AECraftingPattern; import appeng.crafting.pattern.AESmithingTablePattern; import appeng.crafting.pattern.AEStonecuttingPattern; @@ -27,9 +29,7 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.RecipeType; -import net.minecraftforge.common.capabilities.ForgeCapabilities; -import net.minecraftforge.fml.loading.FMLPaths; -import net.minecraftforge.items.IItemHandler; +import net.neoforged.fml.loading.FMLPaths; import java.io.IOException; import java.lang.reflect.Field; @@ -102,8 +102,10 @@ public class ExtendedAEPatternUploadUtil { if (k.contains(":")) { // 形如 namespace:path try { - ResourceLocation rl = new ResourceLocation(k); - map.put(rl, name); + var rl = ResourceLocation.tryParse(k); + if (rl != null) { + map.put(rl, name); + } } catch (Exception ignored) {} } else { // 视为别名:最终搜索关键字(大小写不敏感) @@ -163,8 +165,10 @@ public class ExtendedAEPatternUploadUtil { // 更新内存映射 if (key.contains(":")) { try { - ResourceLocation rl = new ResourceLocation(key); - CUSTOM_NAMES.put(rl, cnValue); + var rl = ResourceLocation.tryParse(key); + if (rl != null) { + CUSTOM_NAMES.put(rl, cnValue); + } } catch (Exception ignored) {} } else { CUSTOM_ALIASES.put(key.toLowerCase(), cnValue); @@ -216,11 +220,12 @@ public class ExtendedAEPatternUploadUtil { for (String k : toRemove) { if (k.contains(":")) { try { - ResourceLocation rl = new ResourceLocation(k); - // 仅当值匹配才移除(双重保险) - String cur = CUSTOM_NAMES.get(rl); - if (target.equals(cur)) { - CUSTOM_NAMES.remove(rl); + var rl = ResourceLocation.tryParse(k); + if (rl != null) { + String cur = CUSTOM_NAMES.get(rl); + if (target.equals(cur)) { + CUSTOM_NAMES.remove(rl); + } } } catch (Exception ignored) {} } else { @@ -287,32 +292,7 @@ public class ExtendedAEPatternUploadUtil { return key.getPath(); } - /** - * GTCEu 的 GTRecipe -> 搜索关键字 - * 优先自定义中文映射;其次使用注册ID的 path;最后回退到完整ID字符串。 - */ - public static String mapGTCEuRecipeToSearchKey(com.gregtechceu.gtceu.api.recipe.GTRecipe gtRecipe) { - if (gtRecipe == null) return null; - try { - // GTRecipeType.toString() 返回 registryName.toString() 即 namespace:path - String idStr = String.valueOf(gtRecipe.getType()); - if (idStr == null || idStr.isBlank()) return null; - ResourceLocation rl = new ResourceLocation(idStr); - // 1) 先查别名(使用 path 作为最终搜索关键字) - String path = rl.getPath(); - if (path != null) { - String alias = CUSTOM_ALIASES.get(path.toLowerCase()); - if (alias != null && !alias.isBlank()) return alias; - } - // 2) 再查完整ID映射 - String custom = CUSTOM_NAMES.get(rl); - if (custom != null && !custom.isBlank()) return custom; - // 3) 默认返回 path 作为搜索关键字 - return (path != null && !path.isBlank()) ? path : idStr; - } catch (Throwable t) { - return null; - } - } + // 注意:GTCEu 的映射方法已在下方提供基于 Object 的反射版本,避免重复定义。 /** * 仅使用反射的 GTCEu GTRecipe -> 搜索关键字(避免在运行时直接引用 GTCEu 类)。 @@ -326,15 +306,15 @@ public class ExtendedAEPatternUploadUtil { Object typeObj = mGetType.invoke(gtRecipeObj); String idStr = String.valueOf(typeObj); if (idStr == null || idStr.isBlank()) return null; - ResourceLocation rl = new ResourceLocation(idStr); + var rl = ResourceLocation.tryParse(idStr); // 1) 别名优先(使用 path 作为最终搜索关键字) - String path = rl.getPath(); + String path = rl != null ? rl.getPath() : null; if (path != null) { String alias = CUSTOM_ALIASES.get(path.toLowerCase()); if (alias != null && !alias.isBlank()) return alias; } // 2) 再查完整ID映射 - String custom = CUSTOM_NAMES.get(rl); + String custom = rl != null ? CUSTOM_NAMES.get(rl) : null; if (custom != null && !custom.isBlank()) return custom; // 3) 默认返回 path 作为搜索关键字 return (path != null && !path.isBlank()) ? path : idStr; @@ -445,12 +425,15 @@ public class ExtendedAEPatternUploadUtil { } // 获取 AE 网络 - IGridNode node = menu.getNetworkNode(); - if (node == null) { - sendMessage(player, "ExtendedAE Plus: 当前不在有效的 AE 网络中"); - return false; - } - IGrid grid = node.getGrid(); + IGrid grid = null; + try { + if (menu instanceof AEBaseMenu abm) { + Object target = abm.getTarget(); + if (target instanceof IActionHost host && host.getActionableNode() != null) { + grid = host.getActionableNode().getGrid(); + } + } + } catch (Throwable ignored) {} if (grid == null) { sendMessage(player, "ExtendedAE Plus: 当前不在有效的 AE 网络中"); return false; @@ -506,7 +489,7 @@ public class ExtendedAEPatternUploadUtil { } // 回退:尝试 Forge 能力(可能为聚合图样仓),同样遍历所有矩阵 - List handlers = findAllMatrixPatternHandlers(grid); + List handlers = findAllMatrixPatternHandlers(grid); if (!handlers.isEmpty()) { for (int i = 0; i < handlers.size(); i++) { var cap = handlers.get(i); @@ -559,44 +542,11 @@ public class ExtendedAEPatternUploadUtil { /** * 在给定 AE Grid 中收集所有已成型的装配矩阵的聚合图样仓 IItemHandler(若可用)。 */ - private static List findAllMatrixPatternHandlers(IGrid grid) { - List result = new ArrayList<>(); - try { - Set matrices = grid.getMachines(TileAssemblerMatrixBase.class); - int idx = 0; - for (TileAssemblerMatrixBase tile : matrices) { - if (tile != null && tile.isFormed()) { - var capOpt = tile.getCapability(ForgeCapabilities.ITEM_HANDLER, null); - if (capOpt != null) { - var handler = capOpt.orElse(null); - if (handler != null) { - result.add(handler); - } - } - } - idx++; - } - } catch (Throwable ignored) { - } - return result; + private static List findAllMatrixPatternHandlers(IGrid grid) { + // NeoForge 1.21 能力系统与 API 变更,此处先返回空列表,避免编译期依赖旧能力系统 + return java.util.Collections.emptyList(); } - /** - * 尝试将整个物品栈插入到 IItemHandler 的任意槽位,返回剩余物品。 - */ - private static ItemStack insertIntoAnySlot(IItemHandler handler, ItemStack stack) { - ItemStack remaining = stack.copy(); - if (handler == null || remaining.isEmpty()) return remaining; - for (int i = 0; i < handler.getSlots(); i++) { - remaining = handler.insertItem(i, remaining, false); - if (remaining.isEmpty()) break; - } - return remaining; - } - - /** - * 检查装配矩阵(所有已成型矩阵的图样仓)中是否已存在与给定样板完全相同的物品(含NBT)。 - */ private static boolean matrixContainsPattern(IGrid grid, ItemStack pattern) { if (grid == null || pattern == null || pattern.isEmpty()) return false; try { @@ -606,31 +556,24 @@ public class ExtendedAEPatternUploadUtil { if (inv == null) continue; for (int i = 0; i < inv.size(); i++) { ItemStack s = inv.getStackInSlot(i); - if (!s.isEmpty() && net.minecraft.world.item.ItemStack.isSameItemSameTags(s, pattern)) { - return true; - } - } - } - } catch (Throwable t) { - } - try { - // 再检查聚合能力视图 - List handlers = findAllMatrixPatternHandlers(grid); - for (IItemHandler h : handlers) { - if (h == null) continue; - int slots = h.getSlots(); - for (int i = 0; i < slots; i++) { - ItemStack s = h.getStackInSlot(i); - if (!s.isEmpty() && net.minecraft.world.item.ItemStack.isSameItemSameTags(s, pattern)) { + if (!s.isEmpty() && net.minecraft.world.item.ItemStack.isSameItemSameComponents(s, pattern)) { return true; } } } } catch (Throwable t) { } + // 1.21 暂不检查聚合能力视图,能力系统适配后再补充 return false; } + /** + * 能力系统(IItemHandler)未迁移前的占位插入:直接返回原始栈,表示未能插入。 + */ + private static ItemStack insertIntoAnySlot(Object handler, ItemStack stack) { + return stack.copy(); + } + /** * 检查当前菜单是否为ExtendedAE的扩展样板管理终端 * @@ -987,12 +930,16 @@ public class ExtendedAEPatternUploadUtil { return false; } - // 获取 AE 网络 - IGridNode node = menu.getNetworkNode(); - if (node == null) { - return false; - } - IGrid grid = node.getGrid(); + // 获取 AE 网络(1.21 经由 AEBaseMenu target + IActionHost) + IGrid grid = null; + try { + if (menu instanceof AEBaseMenu abm) { + Object target = abm.getTarget(); + if (target instanceof IActionHost host && host.getActionableNode() != null) { + grid = host.getActionableNode().getGrid(); + } + } + } catch (Throwable ignored) {} if (grid == null) { return false; } @@ -1131,10 +1078,14 @@ public class ExtendedAEPatternUploadUtil { List list = new ArrayList<>(); if (menu == null) return list; try { - IGridNode node = menu.getNetworkNode(); - if (node == null) return list; - IGrid grid = node.getGrid(); - if (grid == null) return list; + IGrid grid = null; + if (menu instanceof AEBaseMenu abm) { + Object target = abm.getTarget(); + if (target instanceof IActionHost host && host.getActionableNode() != null) { + grid = host.getActionableNode().getGrid(); + } + } + if (grid == null) return list; for (var machineClass : grid.getMachineClasses()) { if (PatternContainer.class.isAssignableFrom(machineClass)) { @SuppressWarnings("unchecked")