diff --git a/patches/minecraft/net/minecraft/command/arguments/ArgumentTypes.java.patch b/patches/minecraft/net/minecraft/command/arguments/ArgumentTypes.java.patch new file mode 100644 index 000000000..0b86c8a58 --- /dev/null +++ b/patches/minecraft/net/minecraft/command/arguments/ArgumentTypes.java.patch @@ -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; ++ } + } diff --git a/patches/minecraft/net/minecraft/network/play/server/SCommandListPacket.java.patch b/patches/minecraft/net/minecraft/network/play/server/SCommandListPacket.java.patch deleted file mode 100644 index 238f88650..000000000 --- a/patches/minecraft/net/minecraft/network/play/server/SCommandListPacket.java.patch +++ /dev/null @@ -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 requiredargumentbuilder = RequiredArgumentBuilder.argument(s, argumenttype); diff --git a/src/main/java/net/minecraftforge/common/ForgeMod.java b/src/main/java/net/minecraftforge/common/ForgeMod.java index 1624fff48..4d8c03114 100644 --- a/src/main/java/net/minecraftforge/common/ForgeMod.java +++ b/src/main/java/net/minecraftforge/common/ForgeMod.java @@ -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) diff --git a/src/main/java/net/minecraftforge/network/CommandTreeCleaner.java b/src/main/java/net/minecraftforge/network/CommandTreeCleaner.java new file mode 100644 index 000000000..09430c081 --- /dev/null +++ b/src/main/java/net/minecraftforge/network/CommandTreeCleaner.java @@ -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 RootCommandNode cleanArgumentTypes(RootCommandNode root, Predicate> argumentTypeFilter) + { + Predicate> nodeFilter = node -> !(node instanceof ArgumentCommandNode) || argumentTypeFilter.test(((ArgumentCommandNode)node).getType()); + return (RootCommandNode)processCommandNode(root, nodeFilter, new HashMap<>()); + } + + private static CommandNode processCommandNode(CommandNode node, Predicate> nodeFilter, Map, CommandNode> newNodes) + { + CommandNode existingNode = newNodes.get(node); + if (existingNode == null) + { + CommandNode 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 CommandNode cloneNode(CommandNode node, Predicate> nodeFilter, Map, CommandNode> newNodes) + { + if (node instanceof RootCommandNode) + { + return new RootCommandNode<>(); + } + else + { + ArgumentBuilder 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(); + } + } + +} diff --git a/src/main/java/net/minecraftforge/network/VanillaConnectionNetworkFilter.java b/src/main/java/net/minecraftforge/network/VanillaConnectionNetworkFilter.java index a6c6a3720..e395e5170 100644 --- a/src/main/java/net/minecraftforge/network/VanillaConnectionNetworkFilter.java +++ b/src/main/java/net/minecraftforge/network/VanillaConnectionNetworkFilter.java @@ -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>, Function, ? extends IPacket>> handlers = ImmutableMap.>, Function, ? 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 root = packet.getRoot(); + RootCommandNode 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 out) { diff --git a/src/main/java/net/minecraftforge/server/command/EnumArgument.java b/src/main/java/net/minecraftforge/server/command/EnumArgument.java index 7be8fe708..139b701d8 100644 --- a/src/main/java/net/minecraftforge/server/command/EnumArgument.java +++ b/src/main/java/net/minecraftforge/server/command/EnumArgument.java @@ -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> implements ArgumentType { return Stream.of(enumClass.getEnumConstants()).map(Object::toString).collect(Collectors.toList()); } - /* JAVAC HATES RAW TYPES! - @SuppressWarnings({"rawtypes", "unchecked"}) - public static class Serialzier implements IArgumentSerializer { + public static class Serializer implements IArgumentSerializer> + { @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()); } } - */ } diff --git a/src/main/resources/forge.sas b/src/main/resources/forge.sas index 8df7c53ca..1fcf2458c 100644 --- a/src/main/resources/forge.sas +++ b/src/main/resources/forge.sas @@ -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