From fee486f01747ad8f6c89c3e19dbbce43c806c93d Mon Sep 17 00:00:00 2001 From: Dennis C Date: Mon, 30 Jun 2025 16:55:20 +0200 Subject: [PATCH] Add separate client-side payload handler registration (#2272) --- .../ClientCommonPacketListenerImpl.java.patch | 2 +- ...ConfigurationPacketListenerImpl.java.patch | 10 +- .../client/internal/NeoForgeClientProxy.java | 28 -- .../client/loading/ClientModLoader.java | 6 +- .../network/ClientPacketDistributor.java | 31 ++ .../{handlers => }/ClientPayloadHandler.java | 40 +-- .../RegisterClientPayloadHandlersEvent.java | 47 +++ .../{handlers => event}/package-info.java | 2 +- .../neoforge/client/network/package-info.java | 13 + .../registration/ClientNetworkRegistry.java | 272 ++++++++++++++++++ .../network/registration/package-info.java | 13 + .../neoforge/internal/NeoForgeProxy.java | 17 -- .../network/NetworkInitialization.java | 45 +-- .../neoforge/network/PacketDistributor.java | 14 +- .../filters/GenericPacketSplitter.java | 2 +- .../handling/DirectionalPayloadHandler.java | 22 -- .../payload/AdvancedAddEntityPayload.java | 3 +- .../AdvancedContainerSetDataPayload.java | 3 +- .../payload/AdvancedOpenScreenPayload.java | 3 +- .../payload/AuxiliaryLightDataPayload.java | 3 +- .../payload/ClientDispatchPayload.java | 26 -- .../ClientboundCustomSetTimePayload.java | 3 +- .../network/payload/ConfigFilePayload.java | 3 +- .../payload/FrozenRegistryPayload.java | 3 +- .../FrozenRegistrySyncCompletedPayload.java | 3 +- .../FrozenRegistrySyncStartPayload.java | 3 +- .../payload/KnownRegistryDataMapsPayload.java | 3 +- .../network/payload/RecipeContentPayload.java | 3 +- .../payload/RegistryDataMapSyncPayload.java | 2 +- .../network/registration/NetworkRegistry.java | 237 +++++---------- .../registration/PayloadRegistrar.java | 115 ++++++-- .../registration/PayloadRegistration.java | 3 - .../testframework/impl/TestFrameworkImpl.java | 5 +- .../TestFrameworkPayloadInitialization.java | 4 +- .../oldtest/misc/ModMismatchTest.java | 1 + 35 files changed, 615 insertions(+), 375 deletions(-) create mode 100644 src/client/java/net/neoforged/neoforge/client/network/ClientPacketDistributor.java rename src/client/java/net/neoforged/neoforge/client/network/{handlers => }/ClientPayloadHandler.java (82%) create mode 100644 src/client/java/net/neoforged/neoforge/client/network/event/RegisterClientPayloadHandlersEvent.java rename src/client/java/net/neoforged/neoforge/client/network/{handlers => event}/package-info.java (85%) create mode 100644 src/client/java/net/neoforged/neoforge/client/network/package-info.java create mode 100644 src/client/java/net/neoforged/neoforge/client/network/registration/ClientNetworkRegistry.java create mode 100644 src/client/java/net/neoforged/neoforge/client/network/registration/package-info.java delete mode 100644 src/main/java/net/neoforged/neoforge/network/handling/DirectionalPayloadHandler.java delete mode 100644 src/main/java/net/neoforged/neoforge/network/payload/ClientDispatchPayload.java diff --git a/patches/net/minecraft/client/multiplayer/ClientCommonPacketListenerImpl.java.patch b/patches/net/minecraft/client/multiplayer/ClientCommonPacketListenerImpl.java.patch index bcde10e61b..25cbefa5cd 100644 --- a/patches/net/minecraft/client/multiplayer/ClientCommonPacketListenerImpl.java.patch +++ b/patches/net/minecraft/client/multiplayer/ClientCommonPacketListenerImpl.java.patch @@ -47,7 +47,7 @@ + + // Neo: Handle modded payloads. Vanilla payloads do not get sent to the modded handling pass. Additional payloads cannot be registered in the minecraft domain. + if (net.neoforged.neoforge.network.registration.NetworkRegistry.isModdedPayload(p_295727_.payload())) { -+ net.neoforged.neoforge.network.registration.NetworkRegistry.handleModdedPayload(this, p_295727_); ++ net.neoforged.neoforge.client.network.registration.ClientNetworkRegistry.handleModdedPayload(this, p_295727_); + return; + } + diff --git a/patches/net/minecraft/client/multiplayer/ClientConfigurationPacketListenerImpl.java.patch b/patches/net/minecraft/client/multiplayer/ClientConfigurationPacketListenerImpl.java.patch index 616f053113..ef091acb43 100644 --- a/patches/net/minecraft/client/multiplayer/ClientConfigurationPacketListenerImpl.java.patch +++ b/patches/net/minecraft/client/multiplayer/ClientConfigurationPacketListenerImpl.java.patch @@ -17,7 +17,7 @@ + // Neo: Fallback detection layer for vanilla servers + if (this.connectionType.isOther()) { + this.initializedConnection = true; -+ net.neoforged.neoforge.network.registration.NetworkRegistry.initializeOtherConnection(this); ++ net.neoforged.neoforge.client.network.registration.ClientNetworkRegistry.initializeOtherConnection(this); + } } @@ -45,7 +45,7 @@ + // Packets can only be sent after the outbound protocol is set up again + if (!this.initializedConnection && this.connectionType.isOther()) { + // Neo: Fallback detection for servers with a delayed brand payload (BungeeCord) -+ net.neoforged.neoforge.network.registration.NetworkRegistry.initializeOtherConnection(this); ++ net.neoforged.neoforge.client.network.registration.ClientNetworkRegistry.initializeOtherConnection(this); + } + net.neoforged.neoforge.network.registration.NetworkRegistry.onConfigurationFinished(this); this.connection.send(ServerboundFinishConfigurationPacket.INSTANCE); @@ -67,14 +67,14 @@ + // Handle the query payload by responding with the client's network channels. Update the connection type accordingly. + if (packet.payload() instanceof net.neoforged.neoforge.network.payload.ModdedNetworkQueryPayload) { + this.connectionType = net.neoforged.neoforge.network.connection.ConnectionType.NEOFORGE; -+ net.neoforged.neoforge.network.registration.NetworkRegistry.onNetworkQuery(this); ++ net.neoforged.neoforge.client.network.registration.ClientNetworkRegistry.onNetworkQuery(this); + return; + } + + // Receiving a modded network payload implies a successful negotiation by the server. + if (packet.payload() instanceof net.neoforged.neoforge.network.payload.ModdedNetworkPayload moddedNetworkPayload) { + this.initializedConnection = true; -+ net.neoforged.neoforge.network.registration.NetworkRegistry.initializeNeoForgeConnection(this, moddedNetworkPayload.setup()); ++ net.neoforged.neoforge.client.network.registration.ClientNetworkRegistry.initializeNeoForgeConnection(this, moddedNetworkPayload.setup()); + return; + } + @@ -87,7 +87,7 @@ + // Receiving a brand payload without having transitioned to a Neo connection implies a non-modded connection has begun. + if (this.connectionType.isOther() && packet.payload() instanceof net.minecraft.network.protocol.common.custom.BrandPayload) { + this.initializedConnection = true; -+ net.neoforged.neoforge.network.registration.NetworkRegistry.initializeOtherConnection(this); ++ net.neoforged.neoforge.client.network.registration.ClientNetworkRegistry.initializeOtherConnection(this); + // Continue processing the brand payload + } + diff --git a/src/client/java/net/neoforged/neoforge/client/internal/NeoForgeClientProxy.java b/src/client/java/net/neoforged/neoforge/client/internal/NeoForgeClientProxy.java index 0edf28b2ea..024b9bfe07 100644 --- a/src/client/java/net/neoforged/neoforge/client/internal/NeoForgeClientProxy.java +++ b/src/client/java/net/neoforged/neoforge/client/internal/NeoForgeClientProxy.java @@ -5,47 +5,19 @@ package net.neoforged.neoforge.client.internal; -import java.util.Objects; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.core.HolderLookup; import net.minecraft.core.Registry; -import net.minecraft.network.protocol.common.ClientCommonPacketListener; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import net.minecraft.util.thread.BlockableEventLoop; import net.minecraft.world.item.TooltipFlag; -import net.neoforged.neoforge.client.network.handlers.ClientPayloadHandler; -import net.neoforged.neoforge.client.network.handling.ClientPayloadContext; import net.neoforged.neoforge.internal.NeoForgeProxy; -import net.neoforged.neoforge.network.handling.IPayloadContext; -import net.neoforged.neoforge.network.payload.ClientDispatchPayload; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public class NeoForgeClientProxy extends NeoForgeProxy { - @Override - public void sendToServer(CustomPacketPayload payload, CustomPacketPayload... payloads) { - ClientPacketListener listener = Objects.requireNonNull(Minecraft.getInstance().getConnection()); - listener.send(payload); - for (CustomPacketPayload otherPayload : payloads) { - listener.send(otherPayload); - } - } - - @Override - public IPayloadContext newClientPayloadContext(ClientCommonPacketListener listener, ResourceLocation payloadId) { - return new ClientPayloadContext(listener, payloadId); - } - - @Override - public void handleClientPayload(ClientDispatchPayload payload, IPayloadContext context) { - ClientPayloadHandler.dispatch(payload, context); - } - @Override public BlockableEventLoop getClientExecutor() { return Minecraft.getInstance(); diff --git a/src/client/java/net/neoforged/neoforge/client/loading/ClientModLoader.java b/src/client/java/net/neoforged/neoforge/client/loading/ClientModLoader.java index 56bece72c9..ca4209577d 100644 --- a/src/client/java/net/neoforged/neoforge/client/loading/ClientModLoader.java +++ b/src/client/java/net/neoforged/neoforge/client/loading/ClientModLoader.java @@ -26,6 +26,7 @@ import net.neoforged.fml.VersionChecker; import net.neoforged.fml.loading.EarlyLoadingScreenController; import net.neoforged.neoforge.client.config.NeoForgeClientConfig; import net.neoforged.neoforge.client.gui.LoadingErrorScreen; +import net.neoforged.neoforge.client.network.registration.ClientNetworkRegistry; import net.neoforged.neoforge.internal.CommonModLoader; import net.neoforged.neoforge.logging.CrashReportExtender; import net.neoforged.neoforge.resource.ResourcePackLoader; @@ -97,7 +98,10 @@ public class ClientModLoader extends CommonModLoader { } private static void finishModLoading(Executor syncExecutor, Executor parallelExecutor) { - catchLoadingException(() -> finish(syncExecutor, parallelExecutor)); + catchLoadingException(() -> { + finish(syncExecutor, parallelExecutor); + ModLoader.runInitTask("Client network registry lock", syncExecutor, () -> {}, ClientNetworkRegistry::setup); + }); loading = false; loadingComplete = true; } diff --git a/src/client/java/net/neoforged/neoforge/client/network/ClientPacketDistributor.java b/src/client/java/net/neoforged/neoforge/client/network/ClientPacketDistributor.java new file mode 100644 index 0000000000..2300fa7393 --- /dev/null +++ b/src/client/java/net/neoforged/neoforge/client/network/ClientPacketDistributor.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.network; + +import java.util.Objects; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; + +/** + * Means to distribute serverbound packets + */ +public final class ClientPacketDistributor { + private ClientPacketDistributor() {} + + /** + * Send the given payload(s) to the server + */ + public static void sendToServer(CustomPacketPayload payload, CustomPacketPayload... payloads) { + ClientPacketListener listener = Objects.requireNonNull(Minecraft.getInstance().getConnection()); + Objects.requireNonNull(payload, "Cannot send null payload"); + listener.send(payload); + for (CustomPacketPayload otherPayload : payloads) { + Objects.requireNonNull(otherPayload, "Cannot send null payload"); + listener.send(otherPayload); + } + } +} diff --git a/src/client/java/net/neoforged/neoforge/client/network/handlers/ClientPayloadHandler.java b/src/client/java/net/neoforged/neoforge/client/network/ClientPayloadHandler.java similarity index 82% rename from src/client/java/net/neoforged/neoforge/client/network/handlers/ClientPayloadHandler.java rename to src/client/java/net/neoforged/neoforge/client/network/ClientPayloadHandler.java index 5c72d2caea..b57c4d52b6 100644 --- a/src/client/java/net/neoforged/neoforge/client/network/handlers/ClientPayloadHandler.java +++ b/src/client/java/net/neoforged/neoforge/client/network/ClientPayloadHandler.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: LGPL-2.1-only */ -package net.neoforged.neoforge.client.network.handlers; +package net.neoforged.neoforge.client.network; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -25,19 +25,23 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.item.crafting.RecipeMap; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.client.event.RecipesReceivedEvent; +import net.neoforged.neoforge.client.network.event.RegisterClientPayloadHandlersEvent; import net.neoforged.neoforge.client.registries.ClientRegistryManager; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.world.AuxiliaryLightManager; import net.neoforged.neoforge.common.world.LevelChunkAuxiliaryLightManager; import net.neoforged.neoforge.entity.IEntityWithComplexSpawn; +import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; import net.neoforged.neoforge.network.ConfigSync; import net.neoforged.neoforge.network.handling.IPayloadContext; import net.neoforged.neoforge.network.payload.AdvancedAddEntityPayload; import net.neoforged.neoforge.network.payload.AdvancedContainerSetDataPayload; import net.neoforged.neoforge.network.payload.AdvancedOpenScreenPayload; import net.neoforged.neoforge.network.payload.AuxiliaryLightDataPayload; -import net.neoforged.neoforge.network.payload.ClientDispatchPayload; import net.neoforged.neoforge.network.payload.ClientboundCustomSetTimePayload; import net.neoforged.neoforge.network.payload.ConfigFilePayload; import net.neoforged.neoforge.network.payload.FrozenRegistryPayload; @@ -53,28 +57,28 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; @ApiStatus.Internal -public final class ClientPayloadHandler { +@EventBusSubscriber(modid = NeoForgeVersion.MOD_ID, value = Dist.CLIENT) +final class ClientPayloadHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ClientPayloadHandler.class); private static final Set toSynchronize = Sets.newConcurrentHashSet(); private static final Map synchronizedRegistries = Maps.newConcurrentMap(); private ClientPayloadHandler() {} - public static void dispatch(ClientDispatchPayload payload, IPayloadContext context) { - switch (payload) { - case AdvancedAddEntityPayload advancedAddEntityPayload -> handle(advancedAddEntityPayload, context); - case AdvancedContainerSetDataPayload advancedContainerSetDataPayload -> handle(advancedContainerSetDataPayload, context); - case AdvancedOpenScreenPayload advancedOpenScreenPayload -> handle(advancedOpenScreenPayload, context); - case AuxiliaryLightDataPayload auxiliaryLightDataPayload -> handle(auxiliaryLightDataPayload, context); - case ClientboundCustomSetTimePayload clientboundCustomSetTimePayload -> handle(clientboundCustomSetTimePayload, context); - case ConfigFilePayload configFilePayload -> handle(configFilePayload, context); - case FrozenRegistryPayload frozenRegistryPayload -> handle(frozenRegistryPayload, context); - case FrozenRegistrySyncCompletedPayload frozenRegistrySyncCompletedPayload -> handle(frozenRegistrySyncCompletedPayload, context); - case FrozenRegistrySyncStartPayload frozenRegistrySyncStartPayload -> handle(frozenRegistrySyncStartPayload, context); - case KnownRegistryDataMapsPayload knownRegistryDataMapsPayload -> ClientRegistryManager.handleKnownDataMaps(knownRegistryDataMapsPayload, context); - case RecipeContentPayload recipeContentPayload -> handle(recipeContentPayload, context); - case RegistryDataMapSyncPayload registryDataMapSyncPayload -> ClientRegistryManager.handleDataMapSync(registryDataMapSyncPayload, context); - } + @SubscribeEvent + private static void register(RegisterClientPayloadHandlersEvent event) { + event.register(ConfigFilePayload.TYPE, ClientPayloadHandler::handle); + event.register(FrozenRegistrySyncStartPayload.TYPE, ClientPayloadHandler::handle); + event.register(FrozenRegistryPayload.TYPE, ClientPayloadHandler::handle); + event.register(FrozenRegistrySyncCompletedPayload.TYPE, ClientPayloadHandler::handle); + event.register(KnownRegistryDataMapsPayload.TYPE, ClientRegistryManager::handleKnownDataMaps); + event.register(AdvancedAddEntityPayload.TYPE, ClientPayloadHandler::handle); + event.register(AdvancedOpenScreenPayload.TYPE, ClientPayloadHandler::handle); + event.register(AuxiliaryLightDataPayload.TYPE, ClientPayloadHandler::handle); + event.register(RegistryDataMapSyncPayload.TYPE, ClientRegistryManager::handleDataMapSync); + event.register(AdvancedContainerSetDataPayload.TYPE, ClientPayloadHandler::handle); + event.register(ClientboundCustomSetTimePayload.TYPE, ClientPayloadHandler::handle); + event.register(RecipeContentPayload.TYPE, ClientPayloadHandler::handle); } private static void handle(FrozenRegistryPayload payload, IPayloadContext context) { diff --git a/src/client/java/net/neoforged/neoforge/client/network/event/RegisterClientPayloadHandlersEvent.java b/src/client/java/net/neoforged/neoforge/client/network/event/RegisterClientPayloadHandlersEvent.java new file mode 100644 index 0000000000..e684fd42a6 --- /dev/null +++ b/src/client/java/net/neoforged/neoforge/client/network/event/RegisterClientPayloadHandlersEvent.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.network.event; + +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.IModBusEvent; +import net.neoforged.neoforge.client.network.registration.ClientNetworkRegistry; +import net.neoforged.neoforge.network.handling.IPayloadHandler; +import net.neoforged.neoforge.network.registration.HandlerThread; +import org.jetbrains.annotations.ApiStatus; + +/** + * Event fired on the mod event bus when the {@link ClientNetworkRegistry} is being set up. + *

+ * This event is used to assign payload handlers to clientbound payload types. + */ +public class RegisterClientPayloadHandlersEvent extends Event implements IModBusEvent { + @ApiStatus.Internal + public RegisterClientPayloadHandlersEvent() {} + + /** + * Registers the provided {@link IPayloadHandler} as the client handler to be invoked on the main thread + * for the provided {@link CustomPacketPayload.Type} + * + * @param type The payload type to register the handler for + * @param handler The client-side payload handler to register + */ + public void register(CustomPacketPayload.Type type, IPayloadHandler handler) { + this.register(type, HandlerThread.MAIN, handler); + } + + /** + * Registers the provided {@link IPayloadHandler} as the client handler to be invoked on the specified thread + * for the provided {@link CustomPacketPayload.Type} + * + * @param type The payload type to register the handler for + * @param thread The thread the handler should be invoked on + * @param handler The client-side payload handler to register + */ + public void register(CustomPacketPayload.Type type, HandlerThread thread, IPayloadHandler handler) { + ClientNetworkRegistry.register(type, thread, handler); + } +} diff --git a/src/client/java/net/neoforged/neoforge/client/network/handlers/package-info.java b/src/client/java/net/neoforged/neoforge/client/network/event/package-info.java similarity index 85% rename from src/client/java/net/neoforged/neoforge/client/network/handlers/package-info.java rename to src/client/java/net/neoforged/neoforge/client/network/event/package-info.java index 878af221ed..26ad89cda3 100644 --- a/src/client/java/net/neoforged/neoforge/client/network/handlers/package-info.java +++ b/src/client/java/net/neoforged/neoforge/client/network/event/package-info.java @@ -6,7 +6,7 @@ @FieldsAreNonnullByDefault @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault -package net.neoforged.neoforge.client.network.handlers; +package net.neoforged.neoforge.client.network.event; import javax.annotation.ParametersAreNonnullByDefault; import net.minecraft.FieldsAreNonnullByDefault; diff --git a/src/client/java/net/neoforged/neoforge/client/network/package-info.java b/src/client/java/net/neoforged/neoforge/client/network/package-info.java new file mode 100644 index 0000000000..9af93da019 --- /dev/null +++ b/src/client/java/net/neoforged/neoforge/client/network/package-info.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +@FieldsAreNonnullByDefault +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package net.neoforged.neoforge.client.network; + +import javax.annotation.ParametersAreNonnullByDefault; +import net.minecraft.FieldsAreNonnullByDefault; +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/client/java/net/neoforged/neoforge/client/network/registration/ClientNetworkRegistry.java b/src/client/java/net/neoforged/neoforge/client/network/registration/ClientNetworkRegistry.java new file mode 100644 index 0000000000..3a0bb39595 --- /dev/null +++ b/src/client/java/net/neoforged/neoforge/client/network/registration/ClientNetworkRegistry.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.network.registration; + +import com.google.common.collect.ImmutableSet; +import com.mojang.logging.LogUtils; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import net.minecraft.network.ConnectionProtocol; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.PacketFlow; +import net.minecraft.network.protocol.common.ClientCommonPacketListener; +import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; +import net.minecraft.network.protocol.common.custom.BrandPayload; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.network.protocol.configuration.ClientConfigurationPacketListener; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.fml.ModLoader; +import net.neoforged.fml.config.ConfigTracker; +import net.neoforged.neoforge.client.network.event.RegisterClientPayloadHandlersEvent; +import net.neoforged.neoforge.client.network.handling.ClientPayloadContext; +import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; +import net.neoforged.neoforge.network.configuration.CheckExtensibleEnums; +import net.neoforged.neoforge.network.configuration.CheckFeatureFlags; +import net.neoforged.neoforge.network.connection.ConnectionType; +import net.neoforged.neoforge.network.filters.NetworkFilters; +import net.neoforged.neoforge.network.handling.IPayloadHandler; +import net.neoforged.neoforge.network.handling.MainThreadPayloadHandler; +import net.neoforged.neoforge.network.negotiation.NegotiableNetworkComponent; +import net.neoforged.neoforge.network.negotiation.NegotiationResult; +import net.neoforged.neoforge.network.negotiation.NetworkComponentNegotiator; +import net.neoforged.neoforge.network.payload.MinecraftRegisterPayload; +import net.neoforged.neoforge.network.payload.ModdedNetworkPayload; +import net.neoforged.neoforge.network.payload.ModdedNetworkQueryPayload; +import net.neoforged.neoforge.network.registration.ChannelAttributes; +import net.neoforged.neoforge.network.registration.HandlerThread; +import net.neoforged.neoforge.network.registration.NetworkChannel; +import net.neoforged.neoforge.network.registration.NetworkPayloadSetup; +import net.neoforged.neoforge.network.registration.NetworkRegistry; +import net.neoforged.neoforge.network.registration.PayloadRegistration; +import org.jetbrains.annotations.ApiStatus; +import org.slf4j.Logger; + +@ApiStatus.Internal +public final class ClientNetworkRegistry extends NetworkRegistry { + private static final Logger LOGGER = LogUtils.getLogger(); + + private static boolean setupClient = false; + + private ClientNetworkRegistry() {} + + /** + * Sets up the client network registry by firing {@link RegisterClientPayloadHandlersEvent}, updating the payload + * registrations for clientbound payloads in {@link #PAYLOAD_REGISTRATIONS}. + */ + public static void setup() { + if (!NetworkRegistry.setup) { + throw new IllegalStateException("ClientNetworkRegistry cannot be set up before main NetworkRegistry"); + } + if (setupClient) { + throw new IllegalStateException("The client network registry can only be set up once."); + } + + ModLoader.postEvent(new RegisterClientPayloadHandlersEvent()); + + List missingHandlers = PAYLOAD_REGISTRATIONS.values() + .stream() + .map(Map::values) + .flatMap(Collection::stream) + .filter(reg -> { + if (!reg.matchesFlow(PacketFlow.CLIENTBOUND)) { + return false; + } + + for (ConnectionProtocol protocol : reg.protocols()) { + if (!CLIENTBOUND_HANDLERS.get(protocol).containsKey(reg.id())) { + return true; + } + } + return false; + }) + .map(PayloadRegistration::id) + .distinct() + .toList(); + if (!missingHandlers.isEmpty()) { + throw new IllegalStateException("Some clientbound payloads are missing client-side handlers: " + missingHandlers); + } + + setupClient = true; + } + + @SuppressWarnings("unchecked") + public static void register(CustomPacketPayload.Type type, HandlerThread thread, IPayloadHandler handler) { + if (setupClient) { + throw new UnsupportedOperationException("Cannot register client-side handler for payload " + type.id() + " after registration phase."); + } + + if (thread == HandlerThread.MAIN) { + handler = new MainThreadPayloadHandler<>(handler); + } + + boolean found = false; + for (var entry : PAYLOAD_REGISTRATIONS.entrySet()) { + ConnectionProtocol protocol = entry.getKey(); + Map> registrations = entry.getValue(); + + PayloadRegistration registration = (PayloadRegistration) registrations.get(type.id()); + if (registration == null) { + continue; + } + + if (!registration.matchesFlow(PacketFlow.CLIENTBOUND)) { + throw new IllegalArgumentException("Cannot register client handler for serverbound payload " + type); + } + + found = true; + + registerHandler(CLIENTBOUND_HANDLERS, protocol, PacketFlow.CLIENTBOUND, type, handler); + } + if (!found) { + throw new IllegalArgumentException("Cannot register client handler for unknown payload type " + type); + } + } + + /** + * Handles modded payloads on the client. Invoked after built-in handling. + *

+ * Called on the network thread. + * + * @param listener The listener which received the packet. + * @param packet The packet that was received. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static void handleModdedPayload(ClientCommonPacketListener listener, ClientboundCustomPayloadPacket packet) { + NetworkPayloadSetup payloadSetup = ChannelAttributes.getPayloadSetup(listener.getConnection()); + // Check if channels were negotiated. + if (payloadSetup == null) { + LOGGER.warn("Received a modded payload before channel negotiation; disconnecting."); + listener.getConnection().disconnect(Component.translatable("multiplayer.disconnect.incompatible", "NeoForge %s (No Payload Setup)".formatted(NeoForgeVersion.getVersion()))); + return; + } + + ResourceLocation payloadId = packet.payload().type().id(); + ClientPayloadContext context = new ClientPayloadContext(listener, payloadId); + + if (CLIENTBOUND_HANDLERS.containsKey(listener.protocol())) { + // Get the configuration channel for the packet. + NetworkChannel channel = payloadSetup.getChannel(listener.protocol(), payloadId); + + // Check if the channel should even be processed. + if (channel == null && !hasAdhocChannel(listener.protocol(), packet.payload().type().id(), PacketFlow.CLIENTBOUND)) { + LOGGER.warn("Received a modded payload with an unknown or unaccepted channel; disconnecting."); + listener.getConnection().disconnect(Component.translatable("multiplayer.disconnect.incompatible", "NeoForge %s (No Channel for %s)".formatted(NeoForgeVersion.getVersion(), payloadId.toString()))); + return; + } + + IPayloadHandler handler = CLIENTBOUND_HANDLERS.get(listener.protocol()).get(payloadId); + if (handler == null) { + LOGGER.error("Received a modded payload with no registration; disconnecting."); + listener.getConnection().disconnect(Component.translatable("multiplayer.disconnect.incompatible", "NeoForge %s (No Handler for %s)".formatted(NeoForgeVersion.getVersion(), payloadId.toString()))); + dumpStackToLog(); // This case is only likely when handling packets without serialization, i.e. from a compound listener, so this can help debug why. + return; + } + + handler.handle(packet.payload(), context); + } else { + LOGGER.error("Received a modded payload while not in the configuration or play phase. Disconnecting."); + listener.getConnection().disconnect(Component.translatable("multiplayer.disconnect.incompatible", "NeoForge %s (Invalid Protocol %s)".formatted(NeoForgeVersion.getVersion(), listener.protocol().name()))); + } + } + + /** + * Invoked by the client when a modded server queries it for its available channels. The negotiation happens solely on the server side, and the result is later transmitted to the client. + *

+ * Invoked on the network thread. + * + * @param listener The listener which received the query. + */ + public static void onNetworkQuery(ClientConfigurationPacketListener listener) { + listener.send(ModdedNetworkQueryPayload.fromRegistry(PAYLOAD_REGISTRATIONS)); + } + + /** + * Invoked by the client to indicate that it detect a connection to a modded server, by receiving a {@link ModdedNetworkPayload}. + * This will configure the active connection to the server to use the channels that were negotiated. + *

+ * Once this method completes a {@link NetworkPayloadSetup} will be present on the connection. + *

+ * Invoked on the network thread. + * + * @param listener The listener which received the payload. + * @param setup The network channels that were negotiated. + */ + public static void initializeNeoForgeConnection(ClientConfigurationPacketListener listener, NetworkPayloadSetup setup) { + ChannelAttributes.setPayloadSetup(listener.getConnection(), setup); + ChannelAttributes.setConnectionType(listener.getConnection(), listener.getConnectionType()); + + // Only inject filters once the payload setup is stored, as the filters might check for available channels. + NetworkFilters.injectIfNecessary(listener.getConnection()); + + final ImmutableSet.Builder nowListeningOn = ImmutableSet.builder(); + nowListeningOn.addAll(getInitialListeningChannels(listener.flow())); + nowListeningOn.addAll(setup.getChannels(ConnectionProtocol.CONFIGURATION).keySet()); + listener.send(new MinecraftRegisterPayload(nowListeningOn.build())); + } + + /** + * Invoked by the client when no {@link ModdedNetworkQueryPayload} has been received, but instead a {@link BrandPayload} has been received as the first packet during negotiation in the configuration phase. + *

+ * If this happens then the client will do a negotiation of its own internal channel configuration, to check if any mods are installed that require a modded connection to the server. + * If those are found then the connection is aborted and the client disconnects from the server. + *

+ * This method should never be invoked on a connection where the server is {@link ConnectionType#NEOFORGE}. + *

+ * Invoked on the network thread. + * + * @param listener The listener which received the brand payload. + */ + public static void initializeOtherConnection(ClientConfigurationPacketListener listener) { + // Because we are in vanilla land, no matter what we are not able to support any custom channels. + ChannelAttributes.setPayloadSetup(listener.getConnection(), NetworkPayloadSetup.empty()); + ChannelAttributes.setConnectionType(listener.getConnection(), listener.getConnectionType()); + + for (ConnectionProtocol protocol : PAYLOAD_REGISTRATIONS.keySet()) { + NegotiationResult negotiationResult = NetworkComponentNegotiator.negotiate( + List.of(), + PAYLOAD_REGISTRATIONS.get(protocol).entrySet().stream() + .map(entry -> new NegotiableNetworkComponent(entry.getKey(), entry.getValue().version(), entry.getValue().flow(), entry.getValue().optional())) + .toList()); + + // Negotiation failed. Disconnect the client. + if (!negotiationResult.success()) { + listener.getConnection().disconnect(Component.translatableWithFallback("neoforge.network.negotiation.failure.vanilla.server.not_supported", + "You are trying to connect to a server that is not running NeoForge, but you have mods that require it. A connection could not be established.", NeoForgeVersion.getVersion())); + return; + } + } + + // We are on the client, connected to a vanilla server, make sure we don't have any extended enums that may be sent to the server + if (!CheckExtensibleEnums.handleVanillaServerConnection(listener)) { + return; + } + // We are on the client, connected to a vanilla server, make sure we don't have any modded feature flags + if (!CheckFeatureFlags.handleVanillaServerConnection(listener)) { + return; + } + + // We are on the client, connected to a vanilla server, We have to load the default configs. + ConfigTracker.INSTANCE.loadDefaultServerConfigs(); + + NetworkFilters.injectIfNecessary(listener.getConnection()); + + ImmutableSet.Builder nowListeningOn = ImmutableSet.builder(); + nowListeningOn.addAll(getInitialListeningChannels(listener.flow())); + PAYLOAD_REGISTRATIONS.get(ConnectionProtocol.CONFIGURATION).entrySet().stream() + .filter(registration -> registration.getValue().matchesFlow(listener.flow())) + .filter(registration -> registration.getValue().optional()) + .forEach(registration -> nowListeningOn.add(registration.getKey())); + listener.send(new MinecraftRegisterPayload(nowListeningOn.build())); + } + + /** + * Used in place of {@link Thread#dumpStack()} as that logs to {@link System#err}. + */ + private static void dumpStackToLog() { + LOGGER.error("", new Exception("Stack Trace")); + } +} diff --git a/src/client/java/net/neoforged/neoforge/client/network/registration/package-info.java b/src/client/java/net/neoforged/neoforge/client/network/registration/package-info.java new file mode 100644 index 0000000000..4132ab655d --- /dev/null +++ b/src/client/java/net/neoforged/neoforge/client/network/registration/package-info.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +@FieldsAreNonnullByDefault +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package net.neoforged.neoforge.client.network.registration; + +import javax.annotation.ParametersAreNonnullByDefault; +import net.minecraft.FieldsAreNonnullByDefault; +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/main/java/net/neoforged/neoforge/internal/NeoForgeProxy.java b/src/main/java/net/neoforged/neoforge/internal/NeoForgeProxy.java index 79705e0bda..916fa10fc2 100644 --- a/src/main/java/net/neoforged/neoforge/internal/NeoForgeProxy.java +++ b/src/main/java/net/neoforged/neoforge/internal/NeoForgeProxy.java @@ -7,16 +7,11 @@ package net.neoforged.neoforge.internal; import net.minecraft.core.HolderLookup; import net.minecraft.core.Registry; -import net.minecraft.network.protocol.common.ClientCommonPacketListener; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.util.thread.BlockableEventLoop; import net.minecraft.world.item.TooltipFlag; import net.neoforged.fml.loading.FMLLoader; -import net.neoforged.neoforge.network.handling.IPayloadContext; -import net.neoforged.neoforge.network.payload.ClientDispatchPayload; import net.neoforged.neoforge.server.ServerLifecycleHooks; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -44,18 +39,6 @@ public class NeoForgeProxy { }; } - public void sendToServer(CustomPacketPayload payload, CustomPacketPayload... payloads) { - throw new UnsupportedOperationException("Cannot send serverbound payloads on the server"); - } - - public IPayloadContext newClientPayloadContext(ClientCommonPacketListener listener, ResourceLocation payloadId) { - throw new UnsupportedOperationException("Cannot create ClientPayloadContext on the server"); - } - - public void handleClientPayload(ClientDispatchPayload payload, IPayloadContext context) { - throw new UnsupportedOperationException("Cannot handle client payload on the server"); - } - public BlockableEventLoop getClientExecutor() { throw new UnsupportedOperationException("Cannot access client on the server"); } diff --git a/src/main/java/net/neoforged/neoforge/network/NetworkInitialization.java b/src/main/java/net/neoforged/neoforge/network/NetworkInitialization.java index 0d3848339c..5f944586f6 100644 --- a/src/main/java/net/neoforged/neoforge/network/NetworkInitialization.java +++ b/src/main/java/net/neoforged/neoforge/network/NetworkInitialization.java @@ -7,19 +7,15 @@ package net.neoforged.neoforge.network; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; -import net.neoforged.neoforge.internal.NeoForgeProxy; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; import net.neoforged.neoforge.network.configuration.CheckExtensibleEnums; import net.neoforged.neoforge.network.configuration.CheckFeatureFlags; import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; import net.neoforged.neoforge.network.handlers.ServerPayloadHandler; -import net.neoforged.neoforge.network.handling.DirectionalPayloadHandler; -import net.neoforged.neoforge.network.handling.IPayloadHandler; import net.neoforged.neoforge.network.payload.AdvancedAddEntityPayload; import net.neoforged.neoforge.network.payload.AdvancedContainerSetDataPayload; import net.neoforged.neoforge.network.payload.AdvancedOpenScreenPayload; import net.neoforged.neoforge.network.payload.AuxiliaryLightDataPayload; -import net.neoforged.neoforge.network.payload.ClientDispatchPayload; import net.neoforged.neoforge.network.payload.ClientboundCustomSetTimePayload; import net.neoforged.neoforge.network.payload.ConfigFilePayload; import net.neoforged.neoforge.network.payload.ExtensibleEnumAcknowledgePayload; @@ -39,10 +35,8 @@ import org.jetbrains.annotations.ApiStatus; @ApiStatus.Internal @EventBusSubscriber(modid = NeoForgeVersion.MOD_ID) -public class NetworkInitialization { - private static IPayloadHandler clientHandler() { - return NeoForgeProxy.INSTANCE::handleClientPayload; - } +final class NetworkInitialization { + private NetworkInitialization() {} @SubscribeEvent private static void register(final RegisterPayloadHandlersEvent event) { @@ -51,24 +45,20 @@ public class NetworkInitialization { registrar .commonToClient( ConfigFilePayload.TYPE, - ConfigFilePayload.STREAM_CODEC, - clientHandler()) + ConfigFilePayload.STREAM_CODEC) .configurationToClient( FrozenRegistrySyncStartPayload.TYPE, - FrozenRegistrySyncStartPayload.STREAM_CODEC, - clientHandler()) + FrozenRegistrySyncStartPayload.STREAM_CODEC) .configurationToClient( FrozenRegistryPayload.TYPE, - FrozenRegistryPayload.STREAM_CODEC, - clientHandler()) + FrozenRegistryPayload.STREAM_CODEC) .configurationBidirectional( FrozenRegistrySyncCompletedPayload.TYPE, FrozenRegistrySyncCompletedPayload.STREAM_CODEC, - new DirectionalPayloadHandler<>(clientHandler(), ServerPayloadHandler::handle)) + ServerPayloadHandler::handle) .configurationToClient( KnownRegistryDataMapsPayload.TYPE, - KnownRegistryDataMapsPayload.STREAM_CODEC, - clientHandler()) + KnownRegistryDataMapsPayload.STREAM_CODEC) .configurationToClient( ExtensibleEnumDataPayload.TYPE, ExtensibleEnumDataPayload.STREAM_CODEC, @@ -91,30 +81,23 @@ public class NetworkInitialization { CheckFeatureFlags::handleServerboundPayload) .playToClient( AdvancedAddEntityPayload.TYPE, - AdvancedAddEntityPayload.STREAM_CODEC, - clientHandler()) + AdvancedAddEntityPayload.STREAM_CODEC) .playToClient( AdvancedOpenScreenPayload.TYPE, - AdvancedOpenScreenPayload.STREAM_CODEC, - clientHandler()) + AdvancedOpenScreenPayload.STREAM_CODEC) .playToClient( AuxiliaryLightDataPayload.TYPE, - AuxiliaryLightDataPayload.STREAM_CODEC, - clientHandler()) + AuxiliaryLightDataPayload.STREAM_CODEC) .playToClient( RegistryDataMapSyncPayload.TYPE, - RegistryDataMapSyncPayload.STREAM_CODEC, - clientHandler()) + RegistryDataMapSyncPayload.STREAM_CODEC) .playToClient(AdvancedContainerSetDataPayload.TYPE, - AdvancedContainerSetDataPayload.STREAM_CODEC, - clientHandler()) + AdvancedContainerSetDataPayload.STREAM_CODEC) .playToClient( ClientboundCustomSetTimePayload.TYPE, - ClientboundCustomSetTimePayload.STREAM_CODEC, - clientHandler()) + ClientboundCustomSetTimePayload.STREAM_CODEC) .playToClient( RecipeContentPayload.TYPE, - RecipeContentPayload.STREAM_CODEC, - clientHandler()); + RecipeContentPayload.STREAM_CODEC); } } diff --git a/src/main/java/net/neoforged/neoforge/network/PacketDistributor.java b/src/main/java/net/neoforged/neoforge/network/PacketDistributor.java index a199543205..d7e11927fe 100644 --- a/src/main/java/net/neoforged/neoforge/network/PacketDistributor.java +++ b/src/main/java/net/neoforged/neoforge/network/PacketDistributor.java @@ -19,23 +19,17 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.ChunkPos; -import net.neoforged.neoforge.internal.NeoForgeProxy; import net.neoforged.neoforge.server.ServerLifecycleHooks; import org.jetbrains.annotations.Nullable; /** - * Means to distribute packets in various ways + * Means to distribute packets in various ways. + *

+ * Serverbound payloads can be sent via {@code ClientPacketDistributor#sendToServer()}. */ public final class PacketDistributor { private PacketDistributor() {} - /** - * Send the given payload(s) to the server - */ - public static void sendToServer(CustomPacketPayload payload, CustomPacketPayload... payloads) { - NeoForgeProxy.INSTANCE.sendToServer(payload, payloads); - } - /** * Send the given payload(s) to the given player */ @@ -110,10 +104,12 @@ public final class PacketDistributor { } private static Packet makeClientboundPacket(CustomPacketPayload payload, CustomPacketPayload... payloads) { + Objects.requireNonNull(payload, "Cannot send null payload"); if (payloads.length > 0) { final List> packets = new ArrayList<>(); packets.add(new ClientboundCustomPayloadPacket(payload)); for (CustomPacketPayload otherPayload : payloads) { + Objects.requireNonNull(otherPayload, "Cannot send null payload"); packets.add(new ClientboundCustomPayloadPacket(otherPayload)); } return new ClientboundBundlePacket(packets); diff --git a/src/main/java/net/neoforged/neoforge/network/filters/GenericPacketSplitter.java b/src/main/java/net/neoforged/neoforge/network/filters/GenericPacketSplitter.java index 41f503017e..0d4fa31f31 100644 --- a/src/main/java/net/neoforged/neoforge/network/filters/GenericPacketSplitter.java +++ b/src/main/java/net/neoforged/neoforge/network/filters/GenericPacketSplitter.java @@ -61,7 +61,7 @@ public class GenericPacketSplitter extends MessageToMessageEncoder> im private static void register(final RegisterPayloadHandlersEvent event) { event.registrar("1") .optional() - .commonBidirectional(SplitPacketPayload.TYPE, SplitPacketPayload.STREAM_CODEC, GenericPacketSplitter::handle); + .commonBidirectional(SplitPacketPayload.TYPE, SplitPacketPayload.STREAM_CODEC, GenericPacketSplitter::handle, GenericPacketSplitter::handle); } private static void handle(SplitPacketPayload payload, IPayloadContext context) { diff --git a/src/main/java/net/neoforged/neoforge/network/handling/DirectionalPayloadHandler.java b/src/main/java/net/neoforged/neoforge/network/handling/DirectionalPayloadHandler.java deleted file mode 100644 index 1d456d87c2..0000000000 --- a/src/main/java/net/neoforged/neoforge/network/handling/DirectionalPayloadHandler.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) NeoForged and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.network.handling; - -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; - -/** - * Helper class that merges two unidirectional handlers into a single bidirectional handler. - */ -public record DirectionalPayloadHandler(IPayloadHandler clientSide, IPayloadHandler serverSide) implements IPayloadHandler { - @Override - public void handle(T payload, IPayloadContext context) { - if (context.flow().isClientbound()) { - clientSide.handle(payload, context); - } else if (context.flow().isServerbound()) { - serverSide.handle(payload, context); - } - } -} diff --git a/src/main/java/net/neoforged/neoforge/network/payload/AdvancedAddEntityPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/AdvancedAddEntityPayload.java index d25e8ff809..b161f17e23 100644 --- a/src/main/java/net/neoforged/neoforge/network/payload/AdvancedAddEntityPayload.java +++ b/src/main/java/net/neoforged/neoforge/network/payload/AdvancedAddEntityPayload.java @@ -8,6 +8,7 @@ package net.neoforged.neoforge.network.payload; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; import net.neoforged.neoforge.common.util.FriendlyByteBufUtil; @@ -23,7 +24,7 @@ import org.jetbrains.annotations.ApiStatus; * @param customPayload The custom data of the entity to add. */ @ApiStatus.Internal -public record AdvancedAddEntityPayload(int entityId, byte[] customPayload) implements ClientDispatchPayload { +public record AdvancedAddEntityPayload(int entityId, byte[] customPayload) implements CustomPacketPayload { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(NeoForgeVersion.MOD_ID, "advanced_add_entity")); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( ByteBufCodecs.VAR_INT, diff --git a/src/main/java/net/neoforged/neoforge/network/payload/AdvancedContainerSetDataPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/AdvancedContainerSetDataPayload.java index 6a2859c48d..e710699567 100644 --- a/src/main/java/net/neoforged/neoforge/network/payload/AdvancedContainerSetDataPayload.java +++ b/src/main/java/net/neoforged/neoforge/network/payload/AdvancedContainerSetDataPayload.java @@ -8,6 +8,7 @@ package net.neoforged.neoforge.network.payload; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.network.protocol.game.ClientboundContainerSetDataPacket; import net.minecraft.resources.ResourceLocation; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; @@ -21,7 +22,7 @@ import org.jetbrains.annotations.ApiStatus; * @param value The value of the dataslot. */ @ApiStatus.Internal -public record AdvancedContainerSetDataPayload(byte containerId, short dataId, int value) implements ClientDispatchPayload { +public record AdvancedContainerSetDataPayload(byte containerId, short dataId, int value) implements CustomPacketPayload { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(NeoForgeVersion.MOD_ID, "advanced_container_set_data")); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( diff --git a/src/main/java/net/neoforged/neoforge/network/payload/AdvancedOpenScreenPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/AdvancedOpenScreenPayload.java index 9f3d3cd9d5..e37b334d18 100644 --- a/src/main/java/net/neoforged/neoforge/network/payload/AdvancedOpenScreenPayload.java +++ b/src/main/java/net/neoforged/neoforge/network/payload/AdvancedOpenScreenPayload.java @@ -11,6 +11,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.inventory.MenuType; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; @@ -26,7 +27,7 @@ import org.jetbrains.annotations.ApiStatus; * @param additionalData The additional data to pass to the screen. */ @ApiStatus.Internal -public record AdvancedOpenScreenPayload(int windowId, MenuType menuType, Component name, byte[] additionalData) implements ClientDispatchPayload { +public record AdvancedOpenScreenPayload(int windowId, MenuType menuType, Component name, byte[] additionalData) implements CustomPacketPayload { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(NeoForgeVersion.MOD_ID, "advanced_open_screen")); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( diff --git a/src/main/java/net/neoforged/neoforge/network/payload/AuxiliaryLightDataPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/AuxiliaryLightDataPayload.java index 21762bd97c..601fd92e77 100644 --- a/src/main/java/net/neoforged/neoforge/network/payload/AuxiliaryLightDataPayload.java +++ b/src/main/java/net/neoforged/neoforge/network/payload/AuxiliaryLightDataPayload.java @@ -11,6 +11,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.ChunkPos; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; @@ -18,7 +19,7 @@ import net.neoforged.neoforge.network.codec.NeoForgeStreamCodecs; import org.jetbrains.annotations.ApiStatus; @ApiStatus.Internal -public record AuxiliaryLightDataPayload(ChunkPos pos, Map entries) implements ClientDispatchPayload { +public record AuxiliaryLightDataPayload(ChunkPos pos, Map entries) implements CustomPacketPayload { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(NeoForgeVersion.MOD_ID, "auxiliary_light_data")); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( NeoForgeStreamCodecs.CHUNK_POS, diff --git a/src/main/java/net/neoforged/neoforge/network/payload/ClientDispatchPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/ClientDispatchPayload.java deleted file mode 100644 index 28467af14b..0000000000 --- a/src/main/java/net/neoforged/neoforge/network/payload/ClientDispatchPayload.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) NeoForged and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.network.payload; - -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; - -/** - * Internal marker classes for packets for which the handler dispatch happens in {@code ClientPayloadHandler}. - * This is meant to be a temporary workaround until we rework the networking API to allow for separate handler registration. - */ -public sealed interface ClientDispatchPayload extends CustomPacketPayload - permits AdvancedAddEntityPayload, - AdvancedContainerSetDataPayload, - AdvancedOpenScreenPayload, - AuxiliaryLightDataPayload, - ClientboundCustomSetTimePayload, - ConfigFilePayload, - FrozenRegistryPayload, - FrozenRegistrySyncCompletedPayload, - FrozenRegistrySyncStartPayload, - KnownRegistryDataMapsPayload, - RecipeContentPayload, - RegistryDataMapSyncPayload {} diff --git a/src/main/java/net/neoforged/neoforge/network/payload/ClientboundCustomSetTimePayload.java b/src/main/java/net/neoforged/neoforge/network/payload/ClientboundCustomSetTimePayload.java index 76b959527a..67add5300b 100644 --- a/src/main/java/net/neoforged/neoforge/network/payload/ClientboundCustomSetTimePayload.java +++ b/src/main/java/net/neoforged/neoforge/network/payload/ClientboundCustomSetTimePayload.java @@ -8,12 +8,13 @@ package net.neoforged.neoforge.network.payload; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; import org.jetbrains.annotations.ApiStatus; @ApiStatus.Internal -public record ClientboundCustomSetTimePayload(long gameTime, long dayTime, boolean gameRule, float dayTimeFraction, float dayTimePerTick) implements ClientDispatchPayload { +public record ClientboundCustomSetTimePayload(long gameTime, long dayTime, boolean gameRule, float dayTimeFraction, float dayTimePerTick) implements CustomPacketPayload { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(NeoForgeVersion.MOD_ID, "custom_time_packet")); diff --git a/src/main/java/net/neoforged/neoforge/network/payload/ConfigFilePayload.java b/src/main/java/net/neoforged/neoforge/network/payload/ConfigFilePayload.java index a415d6028d..5500ba7185 100644 --- a/src/main/java/net/neoforged/neoforge/network/payload/ConfigFilePayload.java +++ b/src/main/java/net/neoforged/neoforge/network/payload/ConfigFilePayload.java @@ -8,6 +8,7 @@ package net.neoforged.neoforge.network.payload; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; import net.neoforged.neoforge.network.codec.NeoForgeStreamCodecs; @@ -23,7 +24,7 @@ import org.jetbrains.annotations.ApiStatus; * @param contents The contents of the config file. */ @ApiStatus.Internal -public record ConfigFilePayload(String fileName, byte[] contents) implements ClientDispatchPayload { +public record ConfigFilePayload(String fileName, byte[] contents) implements CustomPacketPayload { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(NeoForgeVersion.MOD_ID, "config_file")); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( ByteBufCodecs.STRING_UTF8, diff --git a/src/main/java/net/neoforged/neoforge/network/payload/FrozenRegistryPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/FrozenRegistryPayload.java index ce6b851200..545eebd317 100644 --- a/src/main/java/net/neoforged/neoforge/network/payload/FrozenRegistryPayload.java +++ b/src/main/java/net/neoforged/neoforge/network/payload/FrozenRegistryPayload.java @@ -7,6 +7,7 @@ package net.neoforged.neoforge.network.payload; 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.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; import net.neoforged.neoforge.registries.RegistrySnapshot; @@ -19,7 +20,7 @@ import org.jetbrains.annotations.ApiStatus; * @param snapshot The snapshot of the registry */ @ApiStatus.Internal -public record FrozenRegistryPayload(ResourceLocation registryName, RegistrySnapshot snapshot) implements ClientDispatchPayload { +public record FrozenRegistryPayload(ResourceLocation registryName, RegistrySnapshot snapshot) implements CustomPacketPayload { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(NeoForgeVersion.MOD_ID, "frozen_registry")); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( ResourceLocation.STREAM_CODEC, diff --git a/src/main/java/net/neoforged/neoforge/network/payload/FrozenRegistrySyncCompletedPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/FrozenRegistrySyncCompletedPayload.java index 6e71cac63e..5a687ad133 100644 --- a/src/main/java/net/neoforged/neoforge/network/payload/FrozenRegistrySyncCompletedPayload.java +++ b/src/main/java/net/neoforged/neoforge/network/payload/FrozenRegistrySyncCompletedPayload.java @@ -7,6 +7,7 @@ package net.neoforged.neoforge.network.payload; 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.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; import org.jetbrains.annotations.ApiStatus; @@ -15,7 +16,7 @@ import org.jetbrains.annotations.ApiStatus; * This payload is sent to the client when the server has finished sending all the frozen registries. */ @ApiStatus.Internal -public final class FrozenRegistrySyncCompletedPayload implements ClientDispatchPayload { +public final class FrozenRegistrySyncCompletedPayload implements CustomPacketPayload { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(NeoForgeVersion.MOD_ID, "frozen_registry_sync_completed")); public static final FrozenRegistrySyncCompletedPayload INSTANCE = new FrozenRegistrySyncCompletedPayload(); public static final StreamCodec STREAM_CODEC = StreamCodec.unit(INSTANCE); diff --git a/src/main/java/net/neoforged/neoforge/network/payload/FrozenRegistrySyncStartPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/FrozenRegistrySyncStartPayload.java index a097b163b4..c0e13005f8 100644 --- a/src/main/java/net/neoforged/neoforge/network/payload/FrozenRegistrySyncStartPayload.java +++ b/src/main/java/net/neoforged/neoforge/network/payload/FrozenRegistrySyncStartPayload.java @@ -9,6 +9,7 @@ import java.util.List; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; import org.jetbrains.annotations.ApiStatus; @@ -22,7 +23,7 @@ import org.jetbrains.annotations.ApiStatus; * @param toAccess The registries to access. */ @ApiStatus.Internal -public record FrozenRegistrySyncStartPayload(List toAccess) implements ClientDispatchPayload { +public record FrozenRegistrySyncStartPayload(List toAccess) implements CustomPacketPayload { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(NeoForgeVersion.MOD_ID, "frozen_registry_sync_start")); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( ResourceLocation.STREAM_CODEC.apply(ByteBufCodecs.list()), diff --git a/src/main/java/net/neoforged/neoforge/network/payload/KnownRegistryDataMapsPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/KnownRegistryDataMapsPayload.java index 1e85a2d93c..16ef76aa6e 100644 --- a/src/main/java/net/neoforged/neoforge/network/payload/KnownRegistryDataMapsPayload.java +++ b/src/main/java/net/neoforged/neoforge/network/payload/KnownRegistryDataMapsPayload.java @@ -12,13 +12,14 @@ import net.minecraft.core.Registry; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.neoforged.neoforge.network.codec.NeoForgeStreamCodecs; import org.jetbrains.annotations.ApiStatus; @ApiStatus.Internal -public record KnownRegistryDataMapsPayload(Map>, List> dataMaps) implements ClientDispatchPayload { +public record KnownRegistryDataMapsPayload(Map>, List> dataMaps) implements CustomPacketPayload { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath("neoforge", "known_registry_data_maps")); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( ByteBufCodecs.map( diff --git a/src/main/java/net/neoforged/neoforge/network/payload/RecipeContentPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/RecipeContentPayload.java index c25367e597..84c7a68bdd 100644 --- a/src/main/java/net/neoforged/neoforge/network/payload/RecipeContentPayload.java +++ b/src/main/java/net/neoforged/neoforge/network/payload/RecipeContentPayload.java @@ -13,6 +13,7 @@ import net.minecraft.core.registries.Registries; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.crafting.RecipeHolder; import net.minecraft.world.item.crafting.RecipeMap; @@ -26,7 +27,7 @@ import org.jetbrains.annotations.ApiStatus; @ApiStatus.Internal public record RecipeContentPayload( Set> recipeTypes, - List> recipes) implements ClientDispatchPayload { + List> recipes) implements CustomPacketPayload { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(NeoForgeVersion.MOD_ID, "recipe_content")); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( diff --git a/src/main/java/net/neoforged/neoforge/network/payload/RegistryDataMapSyncPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/RegistryDataMapSyncPayload.java index d336c76dc9..5ef10d6935 100644 --- a/src/main/java/net/neoforged/neoforge/network/payload/RegistryDataMapSyncPayload.java +++ b/src/main/java/net/neoforged/neoforge/network/payload/RegistryDataMapSyncPayload.java @@ -28,7 +28,7 @@ import org.jetbrains.annotations.ApiStatus; @ApiStatus.Internal @SuppressWarnings({ "unchecked", "rawtypes" }) public record RegistryDataMapSyncPayload(ResourceKey> registryKey, - Map, ?>> dataMaps) implements ClientDispatchPayload { + Map, ?>> dataMaps) implements CustomPacketPayload { public static final CustomPacketPayload.Type> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath("neoforge", "registry_data_map_sync")); public static final StreamCodec> STREAM_CODEC = StreamCodec.ofMember( RegistryDataMapSyncPayload::write, RegistryDataMapSyncPayload::decode); diff --git a/src/main/java/net/neoforged/neoforge/network/registration/NetworkRegistry.java b/src/main/java/net/neoforged/neoforge/network/registration/NetworkRegistry.java index dd720028b6..96dca88c0e 100644 --- a/src/main/java/net/neoforged/neoforge/network/registration/NetworkRegistry.java +++ b/src/main/java/net/neoforged/neoforge/network/registration/NetworkRegistry.java @@ -36,20 +36,14 @@ import net.minecraft.network.protocol.common.ClientCommonPacketListener; import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; import net.minecraft.network.protocol.common.ServerCommonPacketListener; import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket; -import net.minecraft.network.protocol.common.custom.BrandPayload; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.network.protocol.common.custom.DiscardedPayload; -import net.minecraft.network.protocol.configuration.ClientConfigurationPacketListener; import net.minecraft.network.protocol.configuration.ServerConfigurationPacketListener; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.network.ServerConfigurationPacketListenerImpl; import net.neoforged.fml.ModLoader; -import net.neoforged.fml.config.ConfigTracker; import net.neoforged.neoforge.common.extensions.ICommonPacketListener; -import net.neoforged.neoforge.internal.NeoForgeProxy; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; -import net.neoforged.neoforge.network.configuration.CheckExtensibleEnums; -import net.neoforged.neoforge.network.configuration.CheckFeatureFlags; import net.neoforged.neoforge.network.configuration.CommonRegisterTask; import net.neoforged.neoforge.network.configuration.CommonVersionTask; import net.neoforged.neoforge.network.connection.ConnectionType; @@ -98,7 +92,7 @@ public class NetworkRegistry { * Map of NeoForge payloads that may be sent before channel negotiation. * TODO: Separate by protocol + flow. */ - private static final Map> BUILTIN_PAYLOADS = ImmutableMap.of( + protected static final Map> BUILTIN_PAYLOADS = ImmutableMap.of( MinecraftRegisterPayload.ID, MinecraftRegisterPayload.STREAM_CODEC, MinecraftUnregisterPayload.ID, MinecraftUnregisterPayload.STREAM_CODEC, ModdedNetworkQueryPayload.ID, ModdedNetworkQueryPayload.STREAM_CODEC, @@ -111,13 +105,19 @@ public class NetworkRegistry { * Registry of all custom payload handlers. The initial state of this map should reflect the protocols which support custom payloads. * TODO: Change key type to a combination of protocol + flow. */ - private static final Map>> PAYLOAD_REGISTRATIONS = ImmutableMap.of( + protected static final Map>> PAYLOAD_REGISTRATIONS = ImmutableMap.of( + ConnectionProtocol.CONFIGURATION, new HashMap<>(), + ConnectionProtocol.PLAY, new HashMap<>()); + protected static final Map>> SERVERBOUND_HANDLERS = ImmutableMap.of( + ConnectionProtocol.CONFIGURATION, new HashMap<>(), + ConnectionProtocol.PLAY, new HashMap<>()); + protected static final Map>> CLIENTBOUND_HANDLERS = ImmutableMap.of( ConnectionProtocol.CONFIGURATION, new HashMap<>(), ConnectionProtocol.PLAY, new HashMap<>()); - private static boolean setup = false; + protected static boolean setup = false; - private NetworkRegistry() {} + protected NetworkRegistry() {} /** * Sets up the network registry by firing {@link RegisterPayloadHandlersEvent}, storing the resulting payload registrations in {@link #PAYLOAD_REGISTRATIONS}. @@ -135,19 +135,27 @@ public class NetworkRegistry { /** * Registers a new payload. * - * @param The class of the payload. - * @param The class of the ByteBuf. Only {@link ConnectionProtocol#PLAY play} payloads may use {@link RegistryFriendlyByteBuf}. - * @param type The type of the payload. - * @param codec The codec for the payload. - * @param handler The handler for the payload. This handler should expect to receive the payload on all declared protocols and flows. It will be executed on the network thread. - * @param protocols The protocols this payload supports being sent over. Only {@link ConnectionProtocol#CONFIGURATION configuration} and {@link ConnectionProtocol#PLAY play} are supported. - * @param flow The flow of this payload. Specify {@link Optional#empty()} to support sending in both directions. - * @param version The version of the payload. Increase the payload version if the codec logic or handler logic changes. Neo-Neo connections with mismatched versions are denied. - * @param optional If the payload is optional. Any connection with missing non-optional payloads is denied. + * @param The class of the payload. + * @param The class of the ByteBuf. Only {@link ConnectionProtocol#PLAY play} payloads may use {@link RegistryFriendlyByteBuf}. + * @param type The type of the payload. + * @param codec The codec for the payload. + * @param serverHandler The server-side handler for the payload. This handler will be executed on the network thread. + * @param clientHandler The client-side handler for the payload. This handler will be executed on the network thread. + * @param protocols The protocols this payload supports being sent over. Only {@link ConnectionProtocol#CONFIGURATION configuration} and {@link ConnectionProtocol#PLAY play} are supported. + * @param flow The flow of this payload. Specify {@link Optional#empty()} to support sending in both directions. + * @param version The version of the payload. Increase the payload version if the codec logic or handler logic changes. Neo-Neo connections with mismatched versions are denied. + * @param optional If the payload is optional. Any connection with missing non-optional payloads is denied. */ @SuppressWarnings({ "unchecked", "rawtypes" }) - public static void register(CustomPacketPayload.Type type, StreamCodec codec, IPayloadHandler handler, - List protocols, Optional flow, String version, boolean optional) { + public static void register( + CustomPacketPayload.Type type, + StreamCodec codec, + @Nullable IPayloadHandler serverHandler, + @Nullable IPayloadHandler clientHandler, + List protocols, + Optional flow, + String version, + boolean optional) { if (setup) { throw new UnsupportedOperationException("Cannot register payload " + type.id() + " after registration phase."); } @@ -164,7 +172,11 @@ public class NetworkRegistry { throw new UnsupportedOperationException("Cannot register payload " + type.id() + " using the domain \"minecraft\"."); } - PayloadRegistration reg = new PayloadRegistration(type, codec, handler, protocols, flow, version.strip(), optional); + PayloadRegistration reg = new PayloadRegistration(type, codec, protocols, flow, version.strip(), optional); + + if (reg.matchesFlow(PacketFlow.SERVERBOUND) && serverHandler == null) { + throw new IllegalArgumentException("Cannot register serverbound payload " + type.id() + " without a server-side handler"); + } for (ConnectionProtocol protocol : protocols) { Map> byProtocol = PAYLOAD_REGISTRATIONS.get(protocol); @@ -178,9 +190,34 @@ public class NetworkRegistry { } byProtocol.put(type.id(), reg); + + if (serverHandler != null && reg.matchesFlow(PacketFlow.SERVERBOUND)) { + registerHandler(SERVERBOUND_HANDLERS, protocol, PacketFlow.SERVERBOUND, type, serverHandler); + } + if (clientHandler != null && reg.matchesFlow(PacketFlow.CLIENTBOUND)) { + registerHandler(CLIENTBOUND_HANDLERS, protocol, PacketFlow.CLIENTBOUND, type, clientHandler); + } } } + protected static void registerHandler( + Map>> handlers, + ConnectionProtocol protocol, + PacketFlow flow, + CustomPacketPayload.Type type, + IPayloadHandler handler) { + Map> byProtocol = handlers.get(protocol); + if (byProtocol == null) { + throw new UnsupportedOperationException("Cannot register handler for payload " + type.id() + " for unsupported protocol: " + protocol.name()); + } + + if (byProtocol.containsKey(type.id())) { + throw new UnsupportedOperationException("Duplicate " + flow.id() + " handler registration for payload " + type.id()); + } + + byProtocol.put(type.id(), handler); + } + /** * Attempts to retrieve the {@link StreamCodec} for a non-vanilla payload. *

@@ -261,7 +298,7 @@ public class NetworkRegistry { ServerPayloadContext context = new ServerPayloadContext(listener, packet.payload().type().id()); - if (PAYLOAD_REGISTRATIONS.containsKey(listener.protocol())) { + if (SERVERBOUND_HANDLERS.containsKey(listener.protocol())) { // Get the configuration channel for the packet. NetworkChannel channel = payloadSetup.getChannel(listener.protocol(), context.payloadId()); @@ -272,68 +309,21 @@ public class NetworkRegistry { return; } - PayloadRegistration registration = PAYLOAD_REGISTRATIONS.get(listener.protocol()).get(context.payloadId()); - if (registration == null) { + IPayloadHandler handler = SERVERBOUND_HANDLERS.get(listener.protocol()).get(context.payloadId()); + if (handler == null) { LOGGER.error("Received a modded payload {} with no registration; disconnecting.", context.payloadId()); listener.disconnect(Component.translatable("multiplayer.disconnect.incompatible", "NeoForge %s (No Handler for %s)".formatted(NeoForgeVersion.getVersion(), context.payloadId().toString()))); dumpStackToLog(); // This case is only likely when handling packets without serialization, i.e. from a compound listener, so this can help debug why. return; } - registration.handler().handle(packet.payload(), context); + handler.handle(packet.payload(), context); } else { LOGGER.error("Received a modded payload {} while not in the configuration or play phase; disconnecting.", context.payloadId()); listener.disconnect(Component.translatable("multiplayer.disconnect.incompatible", "NeoForge %s (Invalid Protocol %s)".formatted(NeoForgeVersion.getVersion(), listener.protocol().name()))); } } - /** - * Handles modded payloads on the client. Invoked after built-in handling. - *

- * Called on the network thread. - * - * @param listener The listener which received the packet. - * @param packet The packet that was received. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static void handleModdedPayload(ClientCommonPacketListener listener, ClientboundCustomPayloadPacket packet) { - NetworkPayloadSetup payloadSetup = ChannelAttributes.getPayloadSetup(listener.getConnection()); - // Check if channels were negotiated. - if (payloadSetup == null) { - LOGGER.warn("Received a modded payload before channel negotiation; disconnecting."); - listener.getConnection().disconnect(Component.translatable("multiplayer.disconnect.incompatible", "NeoForge %s (No Payload Setup)".formatted(NeoForgeVersion.getVersion()))); - return; - } - - var payloadId = packet.payload().type().id(); - IPayloadContext context = NeoForgeProxy.INSTANCE.newClientPayloadContext(listener, payloadId); - - if (PAYLOAD_REGISTRATIONS.containsKey(listener.protocol())) { - // Get the configuration channel for the packet. - NetworkChannel channel = payloadSetup.getChannel(listener.protocol(), payloadId); - - // Check if the channel should even be processed. - if (channel == null && !hasAdhocChannel(listener.protocol(), packet.payload().type().id(), PacketFlow.CLIENTBOUND)) { - LOGGER.warn("Received a modded payload with an unknown or unaccepted channel; disconnecting."); - listener.getConnection().disconnect(Component.translatable("multiplayer.disconnect.incompatible", "NeoForge %s (No Channel for %s)".formatted(NeoForgeVersion.getVersion(), payloadId.toString()))); - return; - } - - PayloadRegistration registration = PAYLOAD_REGISTRATIONS.get(listener.protocol()).get(payloadId); - if (registration == null) { - LOGGER.error("Received a modded payload with no registration; disconnecting."); - listener.getConnection().disconnect(Component.translatable("multiplayer.disconnect.incompatible", "NeoForge %s (No Handler for %s)".formatted(NeoForgeVersion.getVersion(), payloadId.toString()))); - dumpStackToLog(); // This case is only likely when handling packets without serialization, i.e. from a compound listener, so this can help debug why. - return; - } - - registration.handler().handle(packet.payload(), context); - } else { - LOGGER.error("Received a modded payload while not in the configuration or play phase. Disconnecting."); - listener.getConnection().disconnect(Component.translatable("multiplayer.disconnect.incompatible", "NeoForge %s (Invalid Protocol %s)".formatted(NeoForgeVersion.getVersion(), listener.protocol().name()))); - } - } - /** * Invoked by the server when it completes the negotiation with the client during the configuration phase. *

@@ -346,9 +336,8 @@ public class NetworkRegistry { *

* Invoked on the network thread. * - * @param listener The listener which completed the negotiation. - * @param configuration The configuration channels that the client has available. - * @param play The play channels that the client has available. + * @param listener The listener which completed the negotiation. + * @param clientChannels The network channels that the client has available. */ public static void initializeNeoForgeConnection(ServerConfigurationPacketListener listener, Map> clientChannels) { ChannelAttributes.setPayloadSetup(listener.getConnection(), NetworkPayloadSetup.empty()); @@ -479,103 +468,11 @@ public class NetworkRegistry { * @param flow The flow of the packet. * @return True if the packet is ad-hoc readable, false otherwise. */ - private static boolean hasAdhocChannel(ConnectionProtocol protocol, ResourceLocation id, PacketFlow flow) { + protected static boolean hasAdhocChannel(ConnectionProtocol protocol, ResourceLocation id, PacketFlow flow) { PayloadRegistration reg = PAYLOAD_REGISTRATIONS.getOrDefault(protocol, Collections.emptyMap()).get(id); return reg != null && reg.optional() && reg.matchesFlow(flow); } - /** - * Invoked by the client when a modded server queries it for its available channels. The negotiation happens solely on the server side, and the result is later transmitted to the client. - *

- * Invoked on the network thread. - * - * @param listener The listener which received the query. - */ - public static void onNetworkQuery(ClientConfigurationPacketListener listener) { - listener.send(ModdedNetworkQueryPayload.fromRegistry(PAYLOAD_REGISTRATIONS)); - } - - /** - * Invoked by the client to indicate that it detect a connection to a modded server, by receiving a {@link ModdedNetworkPayload}. - * This will configure the active connection to the server to use the channels that were negotiated. - *

- * Once this method completes a {@link NetworkPayloadSetup} will be present on the connection. - *

- * Invoked on the network thread. - * - * @param listener The listener which received the payload. - * @param configuration The configuration channels that were negotiated. - * @param play The play channels that were negotiated. - */ - public static void initializeNeoForgeConnection(ClientConfigurationPacketListener listener, NetworkPayloadSetup setup) { - ChannelAttributes.setPayloadSetup(listener.getConnection(), setup); - ChannelAttributes.setConnectionType(listener.getConnection(), listener.getConnectionType()); - - // Only inject filters once the payload setup is stored, as the filters might check for available channels. - NetworkFilters.injectIfNecessary(listener.getConnection()); - - final ImmutableSet.Builder nowListeningOn = ImmutableSet.builder(); - nowListeningOn.addAll(getInitialListeningChannels(listener.flow())); - nowListeningOn.addAll(setup.getChannels(ConnectionProtocol.CONFIGURATION).keySet()); - listener.send(new MinecraftRegisterPayload(nowListeningOn.build())); - } - - /** - * Invoked by the client when no {@link ModdedNetworkQueryPayload} has been received, but instead a {@link BrandPayload} has been received as the first packet during negotiation in the configuration phase. - *

- * If this happens then the client will do a negotiation of its own internal channel configuration, to check if any mods are installed that require a modded connection to the server. - * If those are found then the connection is aborted and the client disconnects from the server. - *

- * This method should never be invoked on a connection where the server is {@link ConnectionType#NEOFORGE}. - *

- * Invoked on the network thread. - * - * @param listener The listener which received the brand payload. - * @return True if the vanilla connection should be handled by the client, false otherwise. - */ - public static void initializeOtherConnection(ClientConfigurationPacketListener listener) { - // Because we are in vanilla land, no matter what we are not able to support any custom channels. - ChannelAttributes.setPayloadSetup(listener.getConnection(), NetworkPayloadSetup.empty()); - ChannelAttributes.setConnectionType(listener.getConnection(), listener.getConnectionType()); - - for (ConnectionProtocol protocol : PAYLOAD_REGISTRATIONS.keySet()) { - NegotiationResult negotiationResult = NetworkComponentNegotiator.negotiate( - List.of(), - PAYLOAD_REGISTRATIONS.get(protocol).entrySet().stream() - .map(entry -> new NegotiableNetworkComponent(entry.getKey(), entry.getValue().version(), entry.getValue().flow(), entry.getValue().optional())) - .toList()); - - // Negotiation failed. Disconnect the client. - if (!negotiationResult.success()) { - listener.getConnection().disconnect(Component.translatableWithFallback("neoforge.network.negotiation.failure.vanilla.server.not_supported", - "You are trying to connect to a server that is not running NeoForge, but you have mods that require it. A connection could not be established.", NeoForgeVersion.getVersion())); - return; - } - } - - // We are on the client, connected to a vanilla server, make sure we don't have any extended enums that may be sent to the server - if (!CheckExtensibleEnums.handleVanillaServerConnection(listener)) { - return; - } - // We are on the client, connected to a vanilla server, make sure we don't have any modded feature flags - if (!CheckFeatureFlags.handleVanillaServerConnection(listener)) { - return; - } - - // We are on the client, connected to a vanilla server, We have to load the default configs. - ConfigTracker.INSTANCE.loadDefaultServerConfigs(); - - NetworkFilters.injectIfNecessary(listener.getConnection()); - - ImmutableSet.Builder nowListeningOn = ImmutableSet.builder(); - nowListeningOn.addAll(getInitialListeningChannels(listener.flow())); - PAYLOAD_REGISTRATIONS.get(ConnectionProtocol.CONFIGURATION).entrySet().stream() - .filter(registration -> registration.getValue().matchesFlow(listener.flow())) - .filter(registration -> registration.getValue().optional()) - .forEach(registration -> nowListeningOn.add(registration.getKey())); - listener.send(new MinecraftRegisterPayload(nowListeningOn.build())); - } - /** * Checks if the packet listener's connection can send/receive the given payload. * @@ -783,7 +680,7 @@ public class NetworkRegistry { *

* Updates the ad-hoc channels to prepare for the game phase by removing the initial channels and building a new list based on the connection type. * - * @param listener + * @param listener The packet listener finishing the configuration phase */ public static void onConfigurationFinished(ICommonPacketListener listener) { NetworkPayloadSetup setup = ChannelAttributes.getPayloadSetup(listener.getConnection()); diff --git a/src/main/java/net/neoforged/neoforge/network/registration/PayloadRegistrar.java b/src/main/java/net/neoforged/neoforge/network/registration/PayloadRegistrar.java index 366e9c8b50..0d56264f10 100644 --- a/src/main/java/net/neoforged/neoforge/network/registration/PayloadRegistrar.java +++ b/src/main/java/net/neoforged/neoforge/network/registration/PayloadRegistrar.java @@ -13,9 +13,9 @@ import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.PacketFlow; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.neoforged.neoforge.network.handling.DirectionalPayloadHandler; import net.neoforged.neoforge.network.handling.IPayloadHandler; import net.neoforged.neoforge.network.handling.MainThreadPayloadHandler; +import org.jetbrains.annotations.Nullable; /** * Builder-style helper for registering {@link CustomPacketPayload}s, used for modded networking. @@ -41,81 +41,132 @@ public class PayloadRegistrar { /** * Registers a client-bound payload for the play phase. */ - public PayloadRegistrar playToClient(CustomPacketPayload.Type type, StreamCodec reader, IPayloadHandler handler) { - register(type, reader, handler, List.of(ConnectionProtocol.PLAY), Optional.of(PacketFlow.CLIENTBOUND), version, optional); + public PayloadRegistrar playToClient(CustomPacketPayload.Type type, StreamCodec codec, IPayloadHandler handler) { + register(type, codec, null, handler, List.of(ConnectionProtocol.PLAY), Optional.of(PacketFlow.CLIENTBOUND), version, optional); + return this; + } + + /** + * Registers a client-bound payload for the play phase without a handler. The missing handler must be registered via {@code RegisterClientPayloadHandlersEvent}. + */ + public PayloadRegistrar playToClient(CustomPacketPayload.Type type, StreamCodec codec) { + register(type, codec, null, null, List.of(ConnectionProtocol.PLAY), Optional.of(PacketFlow.CLIENTBOUND), version, optional); return this; } /** * Registers a server-bound payload for the play phase. */ - public PayloadRegistrar playToServer(CustomPacketPayload.Type type, StreamCodec reader, IPayloadHandler handler) { - register(type, reader, handler, List.of(ConnectionProtocol.PLAY), Optional.of(PacketFlow.SERVERBOUND), version, optional); + public PayloadRegistrar playToServer(CustomPacketPayload.Type type, StreamCodec codec, IPayloadHandler handler) { + register(type, codec, handler, null, List.of(ConnectionProtocol.PLAY), Optional.of(PacketFlow.SERVERBOUND), version, optional); return this; } /** * Registers a bidirectional payload for the play phase. *

- * Consider using {@link DirectionalPayloadHandler} to wrap client and server handlers. + * If the provided {@linkplain IPayloadHandler client handler} is null, it must be registered via {@code RegisterClientPayloadHandlersEvent} */ - public PayloadRegistrar playBidirectional(CustomPacketPayload.Type type, StreamCodec reader, IPayloadHandler handler) { - register(type, reader, handler, List.of(ConnectionProtocol.PLAY), Optional.empty(), version, optional); + public PayloadRegistrar playBidirectional(CustomPacketPayload.Type type, StreamCodec codec, IPayloadHandler serverHandler, @Nullable IPayloadHandler clientHandler) { + register(type, codec, serverHandler, clientHandler, List.of(ConnectionProtocol.PLAY), Optional.empty(), version, optional); return this; } + /** + * Registers a bidirectional payload for the play phase. + *

+ * The provided handler is registered for serverbound payloads and the client-side handler must be registered via {@code RegisterClientPayloadHandlersEvent} + */ + public PayloadRegistrar playBidirectional(CustomPacketPayload.Type type, StreamCodec codec, IPayloadHandler serverHandler) { + return this.playBidirectional(type, codec, serverHandler, null); + } + /** * Registers a client-bound payload for the configuration phase. */ - public PayloadRegistrar configurationToClient(CustomPacketPayload.Type type, StreamCodec reader, IPayloadHandler handler) { - register(type, reader, handler, List.of(ConnectionProtocol.CONFIGURATION), Optional.of(PacketFlow.CLIENTBOUND), version, optional); + public PayloadRegistrar configurationToClient(CustomPacketPayload.Type type, StreamCodec codec, IPayloadHandler handler) { + register(type, codec, null, handler, List.of(ConnectionProtocol.CONFIGURATION), Optional.of(PacketFlow.CLIENTBOUND), version, optional); + return this; + } + + /** + * Registers a client-bound payload for the configuration phase without a handler. The missing handler must be registered via {@code RegisterClientPayloadHandlersEvent}. + */ + public PayloadRegistrar configurationToClient(CustomPacketPayload.Type type, StreamCodec codec) { + register(type, codec, null, null, List.of(ConnectionProtocol.CONFIGURATION), Optional.of(PacketFlow.CLIENTBOUND), version, optional); return this; } /** * Registers a server-bound payload for the configuration phase. */ - public PayloadRegistrar configurationToServer(CustomPacketPayload.Type type, StreamCodec reader, IPayloadHandler handler) { - register(type, reader, handler, List.of(ConnectionProtocol.CONFIGURATION), Optional.of(PacketFlow.SERVERBOUND), version, optional); + public PayloadRegistrar configurationToServer(CustomPacketPayload.Type type, StreamCodec codec, IPayloadHandler handler) { + register(type, codec, handler, null, List.of(ConnectionProtocol.CONFIGURATION), Optional.of(PacketFlow.SERVERBOUND), version, optional); return this; } /** * Registers a bidirectional payload for the configuration phase. *

- * Consider using {@link DirectionalPayloadHandler} to wrap client and server handlers. + * If the provided {@linkplain IPayloadHandler client handler} is null, it must be registered via {@code RegisterClientPayloadHandlersEvent} */ - public PayloadRegistrar configurationBidirectional(CustomPacketPayload.Type type, StreamCodec reader, IPayloadHandler handler) { - register(type, reader, handler, List.of(ConnectionProtocol.CONFIGURATION), Optional.empty(), version, optional); + public PayloadRegistrar configurationBidirectional(CustomPacketPayload.Type type, StreamCodec codec, IPayloadHandler serverHandler, @Nullable IPayloadHandler clientHandler) { + register(type, codec, serverHandler, clientHandler, List.of(ConnectionProtocol.CONFIGURATION), Optional.empty(), version, optional); return this; } + /** + * Registers a bidirectional payload for the configuration phase. + *

+ * The provided handler is registered for serverbound payloads and the client-side handler must be registered via {@code RegisterClientPayloadHandlersEvent} + */ + public PayloadRegistrar configurationBidirectional(CustomPacketPayload.Type type, StreamCodec codec, IPayloadHandler serverHandler) { + return this.configurationBidirectional(type, codec, serverHandler, null); + } + /** * Registers a client-bound payload for all phases. */ - public PayloadRegistrar commonToClient(CustomPacketPayload.Type type, StreamCodec reader, IPayloadHandler handler) { - register(type, reader, handler, List.of(ConnectionProtocol.PLAY, ConnectionProtocol.CONFIGURATION), Optional.of(PacketFlow.CLIENTBOUND), version, optional); + public PayloadRegistrar commonToClient(CustomPacketPayload.Type type, StreamCodec codec, IPayloadHandler handler) { + register(type, codec, null, handler, List.of(ConnectionProtocol.PLAY, ConnectionProtocol.CONFIGURATION), Optional.of(PacketFlow.CLIENTBOUND), version, optional); + return this; + } + + /** + * Registers a client-bound payload for all phases without a handler. The missing handler must be registered via {@code RegisterClientPayloadHandlersEvent}. + */ + public PayloadRegistrar commonToClient(CustomPacketPayload.Type type, StreamCodec codec) { + register(type, codec, null, null, List.of(ConnectionProtocol.PLAY, ConnectionProtocol.CONFIGURATION), Optional.of(PacketFlow.CLIENTBOUND), version, optional); return this; } /** * Registers a server-bound payload for all phases. */ - public PayloadRegistrar commonToServer(CustomPacketPayload.Type type, StreamCodec reader, IPayloadHandler handler) { - register(type, reader, handler, List.of(ConnectionProtocol.PLAY, ConnectionProtocol.CONFIGURATION), Optional.of(PacketFlow.SERVERBOUND), version, optional); + public PayloadRegistrar commonToServer(CustomPacketPayload.Type type, StreamCodec codec, IPayloadHandler handler) { + register(type, codec, handler, null, List.of(ConnectionProtocol.PLAY, ConnectionProtocol.CONFIGURATION), Optional.of(PacketFlow.SERVERBOUND), version, optional); return this; } /** * Registers a bidirectional payload for all phases. *

- * Consider using {@link DirectionalPayloadHandler} to wrap client and server handlers. + * If the provided {@linkplain IPayloadHandler client handler} is null, it must be registered via {@code RegisterClientPayloadHandlersEvent} */ - public PayloadRegistrar commonBidirectional(CustomPacketPayload.Type type, StreamCodec reader, IPayloadHandler handler) { - register(type, reader, handler, List.of(ConnectionProtocol.PLAY, ConnectionProtocol.CONFIGURATION), Optional.empty(), version, optional); + public PayloadRegistrar commonBidirectional(CustomPacketPayload.Type type, StreamCodec codec, IPayloadHandler serverHandler, @Nullable IPayloadHandler clientHandler) { + register(type, codec, serverHandler, clientHandler, List.of(ConnectionProtocol.PLAY, ConnectionProtocol.CONFIGURATION), Optional.empty(), version, optional); return this; } + /** + * Registers a bidirectional payload for all phases. + *

+ * The provided handler is registered for serverbound payloads and the client-side handler must be registered via {@code RegisterClientPayloadHandlersEvent} + */ + public PayloadRegistrar commonBidirectional(CustomPacketPayload.Type type, StreamCodec codec, IPayloadHandler serverHandler) { + return this.commonBidirectional(type, codec, serverHandler, null); + } + /** * Creates a copy of this registrar with a different default handling thread. *

@@ -161,11 +212,23 @@ public class PayloadRegistrar { return clone; } - private void register(CustomPacketPayload.Type type, StreamCodec codec, IPayloadHandler handler, - List protocols, Optional flow, String version, boolean optional) { + private void register( + CustomPacketPayload.Type type, + StreamCodec codec, + @Nullable IPayloadHandler serverHandler, + @Nullable IPayloadHandler clientHandler, + List protocols, + Optional flow, + String version, + boolean optional) { if (this.thread == HandlerThread.MAIN) { - handler = new MainThreadPayloadHandler<>(handler); + if (serverHandler != null) { + serverHandler = new MainThreadPayloadHandler<>(serverHandler); + } + if (clientHandler != null) { + clientHandler = new MainThreadPayloadHandler<>(clientHandler); + } } - NetworkRegistry.register(type, codec, handler, protocols, flow, version, optional); + NetworkRegistry.register(type, codec, serverHandler, clientHandler, protocols, flow, version, optional); } } diff --git a/src/main/java/net/neoforged/neoforge/network/registration/PayloadRegistration.java b/src/main/java/net/neoforged/neoforge/network/registration/PayloadRegistration.java index c3982787d4..cb0fe9ccc3 100644 --- a/src/main/java/net/neoforged/neoforge/network/registration/PayloadRegistration.java +++ b/src/main/java/net/neoforged/neoforge/network/registration/PayloadRegistration.java @@ -13,7 +13,6 @@ import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.PacketFlow; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; -import net.neoforged.neoforge.network.handling.IPayloadHandler; import org.jetbrains.annotations.ApiStatus; /** @@ -21,7 +20,6 @@ import org.jetbrains.annotations.ApiStatus; * * @param type The type of the payload * @param codec The codec for the payload - * @param handler The handler for the payload * @param protocols The protocols this payload supports * @param flow The flow this payload supports (empty if both) * @param version The version of the payload @@ -32,7 +30,6 @@ import org.jetbrains.annotations.ApiStatus; public record PayloadRegistration( CustomPacketPayload.Type type, StreamCodec codec, - IPayloadHandler handler, List protocols, Optional flow, String version, diff --git a/testframework/src/main/java/net/neoforged/testframework/impl/TestFrameworkImpl.java b/testframework/src/main/java/net/neoforged/testframework/impl/TestFrameworkImpl.java index d8f441b575..2363b0df07 100644 --- a/testframework/src/main/java/net/neoforged/testframework/impl/TestFrameworkImpl.java +++ b/testframework/src/main/java/net/neoforged/testframework/impl/TestFrameworkImpl.java @@ -42,6 +42,7 @@ import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.ModContainer; import net.neoforged.fml.loading.FMLLoader; import net.neoforged.neoforge.client.event.ClientPlayerNetworkEvent; +import net.neoforged.neoforge.client.network.ClientPacketDistributor; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.event.entity.player.PlayerEvent; import net.neoforged.neoforge.event.server.ServerStartedEvent; @@ -322,7 +323,7 @@ public class TestFrameworkImpl implements MutableTestFramework { final ChangeStatusPayload packet = new ChangeStatusPayload(this, test.id(), newStatus); sendPacketIfOn( () -> PacketDistributor.sendToAllPlayers(packet), - () -> PacketDistributor.sendToServer(packet), + () -> ClientPacketDistributor.sendToServer(packet), null); } @@ -349,7 +350,7 @@ public class TestFrameworkImpl implements MutableTestFramework { final ChangeEnabledPayload packet = new ChangeEnabledPayload(TestFrameworkImpl.this, test.id(), enabled); sendPacketIfOn( () -> PacketDistributor.sendToAllPlayers(packet), - () -> PacketDistributor.sendToServer(packet), + () -> ClientPacketDistributor.sendToServer(packet), null); } diff --git a/testframework/src/main/java/net/neoforged/testframework/impl/packet/TestFrameworkPayloadInitialization.java b/testframework/src/main/java/net/neoforged/testframework/impl/packet/TestFrameworkPayloadInitialization.java index ebc7b9ae34..2018b4bcc9 100644 --- a/testframework/src/main/java/net/neoforged/testframework/impl/packet/TestFrameworkPayloadInitialization.java +++ b/testframework/src/main/java/net/neoforged/testframework/impl/packet/TestFrameworkPayloadInitialization.java @@ -21,10 +21,10 @@ public record TestFrameworkPayloadInitialization(MutableTestFramework framework) registrar.playBidirectional(ChangeStatusPayload.ID, StreamCodec.of((RegistryFriendlyByteBuf buf, ChangeStatusPayload packet) -> packet.write(buf), buf -> ChangeStatusPayload.decode(framework, buf)), - (payload, context) -> payload.handle(context)); + ChangeStatusPayload::handle, ChangeStatusPayload::handle); registrar.playBidirectional(ChangeEnabledPayload.ID, StreamCodec.of((buf, packet) -> packet.write(buf), buf -> ChangeEnabledPayload.decode(framework, buf)), - (payload, context) -> payload.handle(context)); + ChangeEnabledPayload::handle, ChangeEnabledPayload::handle); } } diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/misc/ModMismatchTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/misc/ModMismatchTest.java index f4fce91b5f..fba4db7b32 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/misc/ModMismatchTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/misc/ModMismatchTest.java @@ -57,6 +57,7 @@ public class ModMismatchTest implements IPayloadHandler