Allow for custom argument types by filtering them on vanilla connections (#7463)

This commit is contained in:
Take Weiland 2020-11-10 21:24:25 +01:00 committed by GitHub
parent f3b53001c9
commit bca20ace4e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 151 additions and 27 deletions

View file

@ -0,0 +1,11 @@
--- a/net/minecraft/command/arguments/ArgumentTypes.java
+++ b/net/minecraft/command/arguments/ArgumentTypes.java
@@ -220,4 +220,8 @@
this.field_197481_c = p_i48088_3_;
}
}
+ @javax.annotation.Nullable public static ResourceLocation getId(ArgumentType<?> type) {
+ Entry<?> entry = func_201040_a(type);
+ return entry == null ? null : entry.field_197481_c;
+ }
}

View file

@ -1,12 +0,0 @@
--- a/net/minecraft/network/play/server/SCommandListPacket.java
+++ b/net/minecraft/network/play/server/SCommandListPacket.java
@@ -119,6 +119,9 @@
String s = p_197695_0_.func_150789_c(32767);
ArgumentType<?> argumenttype = ArgumentTypes.func_197486_a(p_197695_0_);
if (argumenttype == null) {
+ if ((i & 16) != 0) { // FORGE: Flush unused suggestion data
+ p_197695_0_.func_192575_l();
+ }
return null;
} else {
RequiredArgumentBuilder<ISuggestionProvider, ?> requiredargumentbuilder = RequiredArgumentBuilder.argument(s, argumenttype);

View file

@ -19,6 +19,9 @@
package net.minecraftforge.common;
import net.minecraft.command.arguments.ArgumentSerializer;
import net.minecraft.command.arguments.ArgumentTypes;
import net.minecraft.command.arguments.IArgumentSerializer;
import net.minecraft.entity.ai.attributes.Attribute;
import net.minecraft.entity.ai.attributes.RangedAttribute;
import net.minecraft.util.SoundEvent;
@ -34,6 +37,8 @@ import net.minecraftforge.fml.event.server.FMLServerStoppingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.loading.progress.StartupMessageManager;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.server.command.EnumArgument;
import net.minecraftforge.server.command.ModIdArgument;
import net.minecraftforge.versions.forge.ForgeVersion;
import net.minecraftforge.versions.mcp.MCPVersion;
@ -139,13 +144,14 @@ public class ForgeMod implements WorldPersistenceHooks.WorldPersistenceHook
VersionChecker.startVersionCheck();
/*
* We can't actually add any of these, because vanilla clients will choke on unknown argument types
* So our custom arguments will not validate client-side, but they do still work
ArgumentTypes.register("forge:enum", EnumArgument.class, new EnumArgument.Serializer());
registerArgumentTypes();
}
@SuppressWarnings({"unchecked", "rawtypes"})
private void registerArgumentTypes()
{
ArgumentTypes.register("forge:enum", EnumArgument.class, (IArgumentSerializer) new EnumArgument.Serializer());
ArgumentTypes.register("forge:modid", ModIdArgument.class, new ArgumentSerializer<>(ModIdArgument::modIdArgument));
ArgumentTypes.register("forge:structure_type", StructureArgument.class, new ArgumentSerializer<>(StructureArgument::structure));
*/
}
public void serverStopping(FMLServerStoppingEvent evt)

View file

@ -0,0 +1,89 @@
/*
* 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.HashMap;
import java.util.Map;
import java.util.function.Predicate;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.tree.ArgumentCommandNode;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
class CommandTreeCleaner
{
/**
* Cleans the command tree starting at the given root node from any argument types that do not match the given predicate.
* Any {@code ArgumentCommandNode}s that have an unmatched argument type will be stripped from the tree.
* @return A new command tree, stripped of any unmatched argument types
*/
public static <S> RootCommandNode<S> cleanArgumentTypes(RootCommandNode<S> root, Predicate<ArgumentType<?>> argumentTypeFilter)
{
Predicate<CommandNode<?>> nodeFilter = node -> !(node instanceof ArgumentCommandNode<?, ?>) || argumentTypeFilter.test(((ArgumentCommandNode<?, ?>)node).getType());
return (RootCommandNode<S>)processCommandNode(root, nodeFilter, new HashMap<>());
}
private static <S> CommandNode<S> processCommandNode(CommandNode<S> node, Predicate<CommandNode<?>> nodeFilter, Map<CommandNode<S>, CommandNode<S>> newNodes)
{
CommandNode<S> existingNode = newNodes.get(node);
if (existingNode == null)
{
CommandNode<S> newNode = cloneNode(node, nodeFilter, newNodes);
newNodes.put(node, newNode);
node.getChildren().stream()
.filter(nodeFilter)
.map(child -> processCommandNode(child, nodeFilter, newNodes))
.forEach(newNode::addChild);
return newNode;
}
else
{
return existingNode;
}
}
private static <S> CommandNode<S> cloneNode(CommandNode<S> node, Predicate<CommandNode<?>> nodeFilter, Map<CommandNode<S>, CommandNode<S>> newNodes)
{
if (node instanceof RootCommandNode<?>)
{
return new RootCommandNode<>();
}
else
{
ArgumentBuilder<S, ?> builder = node.createBuilder();
if (node.getRedirect() != null)
{
if (nodeFilter.test(node.getRedirect()))
{
builder.forward(processCommandNode(node.getRedirect(), nodeFilter, newNodes), node.getRedirectModifier(), node.isFork());
}
else
{
builder.redirect(null);
}
}
return builder.build();
}
}
}

View file

@ -28,8 +28,11 @@ import javax.annotation.Nonnull;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import net.minecraft.command.ISuggestionProvider;
import net.minecraft.command.arguments.ArgumentTypes;
import net.minecraft.network.IPacket;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SCommandListPacket;
import net.minecraft.network.play.server.SEntityPropertiesPacket;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.network.NetworkHooks;
@ -39,6 +42,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.common.collect.ImmutableMap;
import com.mojang.brigadier.tree.RootCommandNode;
/**
* A filter for network packets, used to filter/modify parts of vanilla network messages that
@ -51,6 +55,7 @@ public class VanillaConnectionNetworkFilter extends MessageToMessageEncoder<IPac
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))
.put(handler(SCommandListPacket.class, VanillaConnectionNetworkFilter::filterCommandList))
.build();
@ -89,6 +94,21 @@ public class VanillaConnectionNetworkFilter extends MessageToMessageEncoder<IPac
return newPacket;
}
/**
* Filter for SCommandListPacket. Uses {@link CommandTreeCleaner} to filter out any ArgumentTypes that are not in the "minecraft" or "brigadier" namespace.
* A vanilla client would fail to deserialize the packet and disconnect with an error message if these were sent.
*/
@Nonnull
private static SCommandListPacket filterCommandList(SCommandListPacket packet)
{
RootCommandNode<ISuggestionProvider> root = packet.getRoot();
RootCommandNode<ISuggestionProvider> newRoot = CommandTreeCleaner.cleanArgumentTypes(root, argType -> {
ResourceLocation id = ArgumentTypes.getId(argType);
return id != null && (id.getNamespace().equals("minecraft") || id.getNamespace().equals("brigadier"));
});
return new SCommandListPacket(newRoot);
}
@Override
protected void encode(ChannelHandlerContext ctx, IPacket<?> msg, List<Object> out)
{

View file

@ -19,6 +19,7 @@
package net.minecraftforge.server.command;
import com.google.gson.JsonObject;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.context.CommandContext;
@ -26,6 +27,9 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.command.ISuggestionProvider;
import net.minecraft.command.arguments.IArgumentSerializer;
import net.minecraft.network.PacketBuffer;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@ -56,28 +60,33 @@ public class EnumArgument<T extends Enum<T>> implements ArgumentType<T> {
return Stream.of(enumClass.getEnumConstants()).map(Object::toString).collect(Collectors.toList());
}
/* JAVAC HATES RAW TYPES!
@SuppressWarnings({"rawtypes", "unchecked"})
public static class Serialzier implements IArgumentSerializer<EnumArgument> {
public static class Serializer implements IArgumentSerializer<EnumArgument<?>>
{
@Override
public void write(EnumArgument argument, PacketBuffer buffer) {
public void write(EnumArgument<?> argument, PacketBuffer buffer)
{
buffer.writeString(argument.enumClass.getName());
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public EnumArgument<?> read(PacketBuffer buffer) {
try {
public EnumArgument<?> read(PacketBuffer buffer)
{
try
{
String name = buffer.readString();
return new EnumArgument(Class.forName(name));
} catch (ClassNotFoundException e) {
}
catch (ClassNotFoundException e)
{
return null;
}
}
@Override
public void write(EnumArgument argument, JsonObject json) {
public void write(EnumArgument<?> argument, JsonObject json)
{
json.addProperty("enum", argument.enumClass.getName());
}
}
*/
}

View file

@ -75,6 +75,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/SCommandListPacket func_197693_a()Lcom/mojang/brigadier/tree/RootCommandNode; # getRoot
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