Workaround for modded attributes on vanilla entities logging warnings on vanilla clients

Switch to a handler-based approach per packet class, simplify patch, add some docs

Add license header and docs

Switch to a network handler based approach

Switch to a network handler based approach

revert build gradle change

Simplify Packet patch

Add license header to IForgePacket

Revert ForgeHooks changes

Less hacky and more generic way to filter packets for vanilla connections

_Actually_ Order SAS.
It's early

Order SAS

simplify vanilla connection check

Remove unneeded import change
This commit is contained in:
diesieben07 2020-09-27 19:09:31 +02:00 committed by cpw
parent 5a7facc20f
commit acd18455c2
No known key found for this signature in database
GPG key ID: 8EB3DF749553B1B7
5 changed files with 124 additions and 3 deletions

View file

@ -27,7 +27,8 @@ public enum ConnectionType
private final Function<String, Integer> versionExtractor;
ConnectionType(Function<String, Integer> versionExtractor) {
ConnectionType(Function<String, Integer> 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;
}
}

View file

@ -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<NetworkManager> 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)

View file

@ -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;
}

View file

@ -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<IPacket<?>>
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Map<Class<? extends IPacket<?>>, Function<IPacket<?>, ? extends IPacket<?>>> handlers = ImmutableMap.<Class<? extends IPacket<?>>, Function<IPacket<?>, ? 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 <T extends IPacket<?>> Map.Entry<Class<? extends IPacket<?>>, Function<IPacket<?>, ? extends IPacket<?>>> handler(Class<T> cls, Function<T, ? extends IPacket<?>> 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<Object> out)
{
Function<IPacket<?>, ? extends IPacket<?>> function = handlers.getOrDefault(msg.getClass(), Function.identity());
out.add(function.apply(msg));
}
}

View file

@ -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