From a4f7087b92946ad5e3e4e7e874a262c05d97a05c Mon Sep 17 00:00:00 2001 From: David Quintana Date: Wed, 10 Mar 2021 23:04:32 +0100 Subject: [PATCH] Introduce a new feature that lets mods know if optional mods and optional network channels are present in the remote. Allow connections from forge versions that don't have the vanilla splitter. --- .../fml/network/FMLConnectionData.java | 69 +++++++++++++++++++ .../fml/network/FMLHandshakeHandler.java | 4 ++ .../fml/network/FMLNetworkConstants.java | 1 + .../fml/network/NetworkHooks.java | 8 +++ .../fml/network/NetworkInstance.java | 7 ++ .../fml/network/NetworkRegistry.java | 20 ++++++ .../network/event/EventNetworkChannel.java | 6 ++ .../fml/network/simple/SimpleChannel.java | 7 ++ .../network/ForgeConnectionNetworkFilter.java | 21 +++++- .../network/VanillaPacketSplitter.java | 35 ++++++++-- 10 files changed, 172 insertions(+), 6 deletions(-) create mode 100644 src/main/java/net/minecraftforge/fml/network/FMLConnectionData.java diff --git a/src/main/java/net/minecraftforge/fml/network/FMLConnectionData.java b/src/main/java/net/minecraftforge/fml/network/FMLConnectionData.java new file mode 100644 index 0000000000..ac81e79468 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/network/FMLConnectionData.java @@ -0,0 +1,69 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.fml.network; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import net.minecraft.util.ResourceLocation; + +import java.util.List; +import java.util.Map; + +public class FMLConnectionData +{ + private ImmutableList modList; + private ImmutableMap channels; + + /* package private */ FMLConnectionData(List modList, Map channels) + { + this.modList = ImmutableList.copyOf(modList); + this.channels = ImmutableMap.copyOf(channels); + } + + /** + * Returns the list of mods present in the remote. + * WARNING: This list is not authoritative. + * A mod missing from the list does not mean the mod isn't there, + * and similarly a mod present in the list does not mean it is there. + * People using hacked clients WILL hack the mod lists to make them look correct. + * Do not use this as an anti-cheat feature! + * + * @return An immutable list of MODIDs + */ + public ImmutableList getModList() + { + return modList; + } + + /** + * Returns the list of network channels present in the remote. + * WARNING: This list is not authoritative. + * A channel missing from the list does not mean the remote won't accept packets with that channel ID, + * and similarly a channel present in the list does not mean the remote won't ignore it. + * People using hacked clients WILL hack the channel list to make it look correct. + * Do not use this as an anti-cheat feature! + * + * @return An immutable map of channel IDs and their respective protocol versions. + */ + public ImmutableMap getChannels() + { + return channels; + } +} diff --git a/src/main/java/net/minecraftforge/fml/network/FMLHandshakeHandler.java b/src/main/java/net/minecraftforge/fml/network/FMLHandshakeHandler.java index 32ff2f7430..e8f2ac3a5f 100644 --- a/src/main/java/net/minecraftforge/fml/network/FMLHandshakeHandler.java +++ b/src/main/java/net/minecraftforge/fml/network/FMLHandshakeHandler.java @@ -184,6 +184,8 @@ public class FMLHandshakeHandler { LOGGER.debug(FMLHSMARKER, "Accepted server connection"); // Set the modded marker on the channel so we know we got packets c.get().getNetworkManager().channel().attr(FMLNetworkConstants.FML_NETVERSION).set(FMLNetworkConstants.NETVERSION); + c.get().getNetworkManager().channel().attr(FMLNetworkConstants.FML_CONNECTION_DATA) + .set(new FMLConnectionData(serverModList.getModList(), serverModList.getChannels())); this.registriesToReceive = new HashSet<>(serverModList.getRegistries()); this.registrySnapshots = Maps.newHashMap(); @@ -203,6 +205,8 @@ public class FMLHandshakeHandler { { LOGGER.debug(FMLHSMARKER, "Received client connection with modlist [{}]", String.join(", ", clientModList.getModList())); boolean accepted = NetworkRegistry.validateServerChannels(clientModList.getChannels()); + c.get().getNetworkManager().channel().attr(FMLNetworkConstants.FML_CONNECTION_DATA) + .set(new FMLConnectionData(clientModList.getModList(), clientModList.getChannels())); c.get().setPacketHandled(true); if (!accepted) { LOGGER.error(FMLHSMARKER, "Terminating connection with client, mismatched mod list"); diff --git a/src/main/java/net/minecraftforge/fml/network/FMLNetworkConstants.java b/src/main/java/net/minecraftforge/fml/network/FMLNetworkConstants.java index 897f5ed860..48ce24f8d3 100644 --- a/src/main/java/net/minecraftforge/fml/network/FMLNetworkConstants.java +++ b/src/main/java/net/minecraftforge/fml/network/FMLNetworkConstants.java @@ -41,6 +41,7 @@ public class FMLNetworkConstants static final AttributeKey FML_NETVERSION = AttributeKey.valueOf("fml:netversion"); static final AttributeKey FML_HANDSHAKE_HANDLER = AttributeKey.valueOf("fml:handshake"); static final AttributeKey FML_MC_REGISTRY = AttributeKey.valueOf("minecraft:netregistry"); + static final AttributeKey FML_CONNECTION_DATA = AttributeKey.valueOf("fml:conndata"); static final ResourceLocation FML_HANDSHAKE_RESOURCE = new ResourceLocation("fml:handshake"); static final ResourceLocation FML_PLAY_RESOURCE = new ResourceLocation("fml:play"); static final ResourceLocation MC_REGISTER_RESOURCE = new ResourceLocation("minecraft:register"); diff --git a/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java b/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java index 0990d28375..308c88998f 100644 --- a/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java +++ b/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java @@ -53,6 +53,8 @@ import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.PlayerContainerEvent; import net.minecraftforge.fml.config.ConfigTracker; +import javax.annotation.Nullable; + public class NetworkHooks { private static final Logger LOGGER = LogManager.getLogger(); @@ -255,4 +257,10 @@ public class NetworkHooks FMLNetworkConstants.playChannel.sendTo(new FMLPlayMessages.SyncCustomTagTypes(customTagTypes), player.connection.getConnection(), NetworkDirection.PLAY_TO_CLIENT); } } + + @Nullable + public static FMLConnectionData getConnectionData(NetworkManager mgr) + { + return mgr.channel().attr(FMLNetworkConstants.FML_CONNECTION_DATA).get(); + } } diff --git a/src/main/java/net/minecraftforge/fml/network/NetworkInstance.java b/src/main/java/net/minecraftforge/fml/network/NetworkInstance.java index 5caff9b33e..892ded4fd0 100644 --- a/src/main/java/net/minecraftforge/fml/network/NetworkInstance.java +++ b/src/main/java/net/minecraftforge/fml/network/NetworkInstance.java @@ -19,6 +19,8 @@ package net.minecraftforge.fml.network; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.network.NetworkManager; import net.minecraft.util.ResourceLocation; import net.minecraftforge.eventbus.api.BusBuilder; @@ -108,4 +110,9 @@ public class NetworkInstance void dispatchEvent(final NetworkEvent networkEvent) { this.networkEventBus.post(networkEvent); } + + public boolean isRemotePresent(NetworkManager manager) { + FMLConnectionData connectionData = NetworkHooks.getConnectionData(manager); + return connectionData != null && connectionData.getChannels().containsKey(channelName); + } } diff --git a/src/main/java/net/minecraftforge/fml/network/NetworkRegistry.java b/src/main/java/net/minecraftforge/fml/network/NetworkRegistry.java index 24a7ba82c0..080eef292d 100644 --- a/src/main/java/net/minecraftforge/fml/network/NetworkRegistry.java +++ b/src/main/java/net/minecraftforge/fml/network/NetworkRegistry.java @@ -63,6 +63,26 @@ public class NetworkRegistry @SuppressWarnings("RedundantStringConstructorCall") public static String ACCEPTVANILLA = new String("ALLOWVANILLA \uD83D\uDC93\uD83D\uDC93\uD83D\uDC93"); + /** + * Makes a version predicate that accepts connections to vanilla or without the channel. + * @param protocolVersion The protocol version, which will be matched exactly. + * @return A new predicate with the new conditions. + */ + public static Predicate acceptMissingOr(final String protocolVersion) + { + return acceptMissingOr(protocolVersion::equals); + } + + /** + * Makes a version predicate that accepts connections to vanilla or without the channel. + * @param versionCheck The main version predicate, which should check the version number of the protocol. + * @return A new predicate with the new conditions. + */ + public static Predicate acceptMissingOr(Predicate versionCheck) + { + return versionCheck.or(ABSENT::equals).or(ACCEPTVANILLA::equals); + } + public static List getServerNonVanillaNetworkMods() { return listRejectedVanillaMods(NetworkInstance::tryClientVersionOnServer); diff --git a/src/main/java/net/minecraftforge/fml/network/event/EventNetworkChannel.java b/src/main/java/net/minecraftforge/fml/network/event/EventNetworkChannel.java index 0380f6a516..6e74b90da4 100644 --- a/src/main/java/net/minecraftforge/fml/network/event/EventNetworkChannel.java +++ b/src/main/java/net/minecraftforge/fml/network/event/EventNetworkChannel.java @@ -19,7 +19,9 @@ package net.minecraftforge.fml.network.event; +import net.minecraft.network.NetworkManager; import net.minecraftforge.fml.network.NetworkEvent; +import net.minecraftforge.fml.network.NetworkHooks; import net.minecraftforge.fml.network.NetworkInstance; import java.util.function.Consumer; @@ -47,4 +49,8 @@ public class EventNetworkChannel { instance.unregisterObject(object); } + + public boolean isRemotePresent(NetworkManager manager) { + return instance.isRemotePresent(manager); + } } diff --git a/src/main/java/net/minecraftforge/fml/network/simple/SimpleChannel.java b/src/main/java/net/minecraftforge/fml/network/simple/SimpleChannel.java index dc79cd05c3..0ec6d95bd1 100644 --- a/src/main/java/net/minecraftforge/fml/network/simple/SimpleChannel.java +++ b/src/main/java/net/minecraftforge/fml/network/simple/SimpleChannel.java @@ -134,6 +134,13 @@ public class SimpleChannel context.getPacketDispatcher().sendPacket(instance.getChannelName(), toBuffer(msgToReply).getLeft()); } + /** + * Returns true if the channel is present in the given connection. + */ + public boolean isRemotePresent(NetworkManager manager) { + return instance.isRemotePresent(manager); + } + /** * Build a new MessageBuilder. The type should implement {@link java.util.function.IntSupplier} if it is a login * packet. diff --git a/src/main/java/net/minecraftforge/network/ForgeConnectionNetworkFilter.java b/src/main/java/net/minecraftforge/network/ForgeConnectionNetworkFilter.java index 683de06418..fa3474c731 100644 --- a/src/main/java/net/minecraftforge/network/ForgeConnectionNetworkFilter.java +++ b/src/main/java/net/minecraftforge/network/ForgeConnectionNetworkFilter.java @@ -1,3 +1,22 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + package net.minecraftforge.network; import java.util.List; @@ -34,7 +53,7 @@ public class ForgeConnectionNetworkFilter extends VanillaPacketFilter protected boolean isNecessary(NetworkManager manager) { // not needed on local connections, because packets are not encoded to bytes there - return !manager.isMemoryConnection() && !NetworkHooks.isVanillaConnection(manager); + return !manager.isMemoryConnection() && !VanillaPacketSplitter.isRemoteCompatible(manager); } private static void splitPacket(IPacket packet, List> out) diff --git a/src/main/java/net/minecraftforge/network/VanillaPacketSplitter.java b/src/main/java/net/minecraftforge/network/VanillaPacketSplitter.java index 164d821f44..d036514e8a 100644 --- a/src/main/java/net/minecraftforge/network/VanillaPacketSplitter.java +++ b/src/main/java/net/minecraftforge/network/VanillaPacketSplitter.java @@ -1,3 +1,22 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + package net.minecraftforge.network; import java.io.IOException; @@ -5,21 +24,20 @@ import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.function.Predicate; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.minecraft.client.Minecraft; import net.minecraft.client.network.play.ClientPlayNetHandler; -import net.minecraft.network.IPacket; -import net.minecraft.network.PacketBuffer; -import net.minecraft.network.PacketDirection; -import net.minecraft.network.ProtocolType; +import net.minecraft.network.*; import net.minecraft.network.play.server.SCustomPayloadPlayPacket; import net.minecraft.network.play.server.STagsListPacket; import net.minecraft.network.play.server.SUpdateRecipesPacket; import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.network.NetworkDirection; import net.minecraftforge.fml.network.NetworkEvent; +import net.minecraftforge.fml.network.NetworkHooks; import net.minecraftforge.fml.network.NetworkRegistry; import net.minecraftforge.fml.network.event.EventNetworkChannel; @@ -54,9 +72,12 @@ public class VanillaPacketSplitter private static final byte STATE_FIRST = 1; private static final byte STATE_LAST = 2; + private static EventNetworkChannel channel; + public static void register() { - EventNetworkChannel channel = NetworkRegistry.newEventChannel(CHANNEL, () -> VERSION, VERSION::equals, VERSION::equals); + Predicate versionCheck = NetworkRegistry.acceptMissingOr(VERSION); + channel = NetworkRegistry.newEventChannel(CHANNEL, () -> VERSION, versionCheck, versionCheck); channel.addListener(VanillaPacketSplitter::onClientPacket); } @@ -185,4 +206,8 @@ public class VanillaPacketSplitter } } + public static boolean isRemoteCompatible(NetworkManager manager) + { + return !NetworkHooks.isVanillaConnection(manager) && channel.isRemotePresent(manager); + } }