diff --git a/src/main/java/net/minecraftforge/fml/network/ConnectionType.java b/src/main/java/net/minecraftforge/fml/network/ConnectionType.java index ad1a1d52f..2af87980b 100644 --- a/src/main/java/net/minecraftforge/fml/network/ConnectionType.java +++ b/src/main/java/net/minecraftforge/fml/network/ConnectionType.java @@ -27,7 +27,8 @@ public enum ConnectionType private final Function versionExtractor; - ConnectionType(Function versionExtractor) { + ConnectionType(Function versionExtractor) + { this.versionExtractor = versionExtractor; } @@ -36,7 +37,14 @@ public enum ConnectionType return vers.startsWith(FMLNetworkConstants.FMLNETMARKER) ? MODDED : VANILLA; } - public int getFMLVersionNumber(final String fmlVersion) { + public int getFMLVersionNumber(final String fmlVersion) + { return versionExtractor.apply(fmlVersion); } + + public boolean isVanilla() + { + return this == VANILLA; + } + } diff --git a/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java b/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java index e34321410..5c23babbe 100644 --- a/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java +++ b/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java @@ -27,6 +27,8 @@ import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; import net.minecraft.tags.ITagCollection; import net.minecraft.tags.ITagCollectionSupplier; import net.minecraft.util.text.StringTextComponent; @@ -62,7 +64,17 @@ public class NetworkHooks public static ConnectionType getConnectionType(final Supplier connection) { - return ConnectionType.forVersionFlag(connection.get().channel().attr(FMLNetworkConstants.FML_NETVERSION).get()); + return getConnectionType(connection.get().channel()); + } + + public static ConnectionType getConnectionType(ChannelHandlerContext context) + { + return getConnectionType(context.channel()); + } + + private static ConnectionType getConnectionType(Channel channel) + { + return ConnectionType.forVersionFlag(channel.attr(FMLNetworkConstants.FML_NETVERSION).get()); } public static IPacket getEntitySpawningPacket(Entity entity) diff --git a/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java b/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java index 93ab48e04..d3c3e8579 100644 --- a/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java +++ b/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java @@ -68,6 +68,7 @@ import net.minecraftforge.fml.loading.moddiscovery.ModFile; import net.minecraftforge.fml.packs.ModFileResourcePack; import net.minecraftforge.fml.packs.ResourcePackLoader; import net.minecraftforge.forgespi.language.IModInfo; +import net.minecraftforge.network.VanillaConnectionNetworkFilter; import net.minecraftforge.registries.GameData; public class ServerLifecycleHooks @@ -168,6 +169,7 @@ public class ServerLifecycleHooks if (packet.getRequestedState() == ProtocolType.STATUS) return true; NetworkHooks.registerServerLoginChannel(manager, packet); + VanillaConnectionNetworkFilter.injectIfNecessary(manager); return true; } diff --git a/src/main/java/net/minecraftforge/network/VanillaConnectionNetworkFilter.java b/src/main/java/net/minecraftforge/network/VanillaConnectionNetworkFilter.java new file mode 100644 index 000000000..a6c6a3720 --- /dev/null +++ b/src/main/java/net/minecraftforge/network/VanillaConnectionNetworkFilter.java @@ -0,0 +1,98 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2020. + * + * 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.AbstractMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import javax.annotation.Nonnull; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageEncoder; +import net.minecraft.network.IPacket; +import net.minecraft.network.NetworkManager; +import net.minecraft.network.play.server.SEntityPropertiesPacket; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.network.NetworkHooks; +import net.minecraftforge.registries.ForgeRegistries; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.google.common.collect.ImmutableMap; + +/** + * A filter for network packets, used to filter/modify parts of vanilla network messages that + * will cause errors or warnings on vanilla clients, for example entity attributes that are added by Forge or mods. + */ +public class VanillaConnectionNetworkFilter extends MessageToMessageEncoder> +{ + + private static final Logger LOGGER = LogManager.getLogger(); + + private static final Map>, Function, ? extends IPacket>> handlers = ImmutableMap.>, Function, ? extends IPacket>>builder() + .put(handler(SEntityPropertiesPacket.class, VanillaConnectionNetworkFilter::filterEntityProperties)) + .build(); + + + public static void injectIfNecessary(NetworkManager manager) + { + if (NetworkHooks.isVanillaConnection(manager)) + { + manager.channel().pipeline().addBefore("packet_handler", "forge:vanilla_filter", new VanillaConnectionNetworkFilter()); + LOGGER.debug("Injected into {}", manager); + } + } + + /** + * Helper function for building the handler map. + */ + @Nonnull + private static > Map.Entry>, Function, ? extends IPacket>> handler(Class cls, Function> function) + { + return new AbstractMap.SimpleEntry<>(cls, function.compose(cls::cast)); + } + + /** + * Filter for SEntityPropertiesPacket. Filters out any entity attributes that are not in the "minecraft" namespace. + * A vanilla client would ignore these with an error log. + */ + @Nonnull + private static SEntityPropertiesPacket filterEntityProperties(SEntityPropertiesPacket msg) + { + SEntityPropertiesPacket newPacket = new SEntityPropertiesPacket(); + msg.getSnapshots().stream() + .filter(snapshot -> { + ResourceLocation key = ForgeRegistries.ATTRIBUTES.getKey(snapshot.func_240834_a_()); + return key != null && key.getNamespace().equals("minecraft"); + }) + .forEach(snapshot -> newPacket.getSnapshots().add(snapshot)); + return newPacket; + } + + @Override + protected void encode(ChannelHandlerContext ctx, IPacket msg, List out) + { + Function, ? extends IPacket> function = handlers.getOrDefault(msg.getClass(), Function.identity()); + out.add(function.apply(msg)); + } +} diff --git a/src/main/resources/forge.sas b/src/main/resources/forge.sas index 0c824c9cf..c7af72d21 100644 --- a/src/main/resources/forge.sas +++ b/src/main/resources/forge.sas @@ -74,6 +74,7 @@ net/minecraft/item/crafting/IRecipe func_193358_e()Ljava/lang/String; # getGroup net/minecraft/item/crafting/SingleItemRecipe func_193358_e()Ljava/lang/String; net/minecraft/nbt/CompressedStreamTools func_74797_a(Ljava/io/File;)Lnet/minecraft/nbt/CompoundNBT; # read net/minecraft/nbt/CompressedStreamTools func_74795_b(Lnet/minecraft/nbt/CompoundNBT;Ljava/io/File;)V # write +net/minecraft/network/play/server/SEntityPropertiesPacket func_149441_d()Ljava/util/List; # getSnapshots net/minecraft/potion/Effect func_220303_e()Lnet/minecraft/potion/EffectType; # getEffectType net/minecraft/potion/Effect func_111186_k()Ljava/util/Map; # getAttributeModifierMap net/minecraft/potion/Effect func_188408_i()Z # isBeneficial