From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sun, 22 Aug 2021 15:21:57 -0700
Subject: [PATCH] Fix entity type tags suggestions in selectors

This would really be better as a client fix because just to fix it
all EntityArguments have been told to ask the server for completions
when if this was fixed on the client, that wouldn't be needed.

Mojira Issue: https://bugs.mojang.com/browse/MC-235045

diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java
index c2af60bdb755034e46b85143826a29c4482d58f5..ae5dd08de75a7ed231295f306fd0974da3988249 100644
--- a/src/main/java/net/minecraft/commands/CommandSourceStack.java
+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java
@@ -416,4 +416,20 @@ public class CommandSourceStack implements SharedSuggestionProvider, com.destroy
         return this.source.getBukkitSender(this);
     }
     // CraftBukkit end
+    // Paper start - override getSelectedEntities
+    @Override
+    public Collection<String> getSelectedEntities() {
+        if (io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && this.source instanceof ServerPlayer player) {
+            double pickDistance = player.gameMode.getGameModeForPlayer().isCreative() ? 5.0F : 4.5F;
+            Vec3 min = player.getEyePosition(1.0F);
+            Vec3 viewVector = player.getViewVector(1.0F);
+            Vec3 max = min.add(viewVector.x * pickDistance, viewVector.y * pickDistance, viewVector.z * pickDistance);
+            net.minecraft.world.phys.AABB aabb = player.getBoundingBox().expandTowards(viewVector.scale(pickDistance)).inflate(1.0D, 1.0D, 1.0D);
+            pickDistance = player.gameMode.getGameModeForPlayer().isCreative() ? 6.0F : pickDistance;
+            net.minecraft.world.phys.EntityHitResult hitResult = net.minecraft.world.entity.projectile.ProjectileUtil.getEntityHitResult(player, min, max, aabb, (e) -> !e.isSpectator() && e.isPickable(), pickDistance);
+            return hitResult != null ? java.util.Collections.singletonList(hitResult.getEntity().getStringUUID()) : SharedSuggestionProvider.super.getSelectedEntities();
+        }
+        return SharedSuggestionProvider.super.getSelectedEntities();
+    }
+    // Paper end
 }
diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
index 9cf69ef59ef4330a69753cab7aa8ec7716587d32..6a82d83bca27b3b023d9d6771d194b4db9c97dc2 100644
--- a/src/main/java/net/minecraft/commands/Commands.java
+++ b/src/main/java/net/minecraft/commands/Commands.java
@@ -441,6 +441,7 @@ public class Commands {
     private void fillUsableCommands(CommandNode<CommandSourceStack> tree, CommandNode<SharedSuggestionProvider> result, CommandSourceStack source, Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> resultNodes) {
         Iterator iterator = tree.getChildren().iterator();
 
+        boolean registeredAskServerSuggestionsForTree = false; // Paper - tell clients to ask server for suggestions for EntityArguments
         while (iterator.hasNext()) {
             CommandNode<CommandSourceStack> commandnode2 = (CommandNode) iterator.next();
             // Paper start
@@ -467,6 +468,12 @@ public class Commands {
 
                     if (requiredargumentbuilder.getSuggestionsProvider() != null) {
                         requiredargumentbuilder.suggests(SuggestionProviders.safelySwap(requiredargumentbuilder.getSuggestionsProvider()));
+                        // Paper start - tell clients to ask server for suggestions for EntityArguments
+                        registeredAskServerSuggestionsForTree = requiredargumentbuilder.getSuggestionsProvider() == net.minecraft.commands.synchronization.SuggestionProviders.ASK_SERVER;
+                    } else if (io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && !registeredAskServerSuggestionsForTree && requiredargumentbuilder.getType() instanceof net.minecraft.commands.arguments.EntityArgument) {
+                        requiredargumentbuilder.suggests(requiredargumentbuilder.getType()::listSuggestions);
+                        registeredAskServerSuggestionsForTree = true; // You can only
+                        // Paper end - tell clients to ask server for suggestions for EntityArguments
                     }
                 }
 
diff --git a/src/main/java/net/minecraft/commands/arguments/EntityArgument.java b/src/main/java/net/minecraft/commands/arguments/EntityArgument.java
index 850db283bf12345e9e7d7e8e590dbe8135c6dce1..a2ea64b7ec5f47224312a1e08dd64347be6f7c43 100644
--- a/src/main/java/net/minecraft/commands/arguments/EntityArgument.java
+++ b/src/main/java/net/minecraft/commands/arguments/EntityArgument.java
@@ -128,7 +128,7 @@ public class EntityArgument implements ArgumentType<EntitySelector> {
             StringReader stringreader = new StringReader(suggestionsbuilder.getInput());
 
             stringreader.setCursor(suggestionsbuilder.getStart());
-            EntitySelectorParser argumentparserselector = new EntitySelectorParser(stringreader, icompletionprovider.hasPermission(2));
+            EntitySelectorParser argumentparserselector = new EntitySelectorParser(stringreader, icompletionprovider.hasPermission(2), true); // Paper
 
             try {
                 argumentparserselector.parse();
diff --git a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
index cdc1319a7cb45a7c5ea30885958b8c3c54f0a241..4cc2ee22369aef22c9bf9d2b244c7278d6db39bb 100644
--- a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
+++ b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelectorParser.java
@@ -113,12 +113,19 @@ public class EntitySelectorParser {
     private boolean hasScores;
     private boolean hasAdvancements;
     private boolean usesSelectors;
+    public boolean parsingEntityArgumentSuggestions; // Paper - track when parsing EntityArgument suggestions
 
     public EntitySelectorParser(StringReader reader) {
         this(reader, true);
     }
 
     public EntitySelectorParser(StringReader reader, boolean atAllowed) {
+        // Paper start
+        this(reader, atAllowed, false);
+    }
+    public EntitySelectorParser(StringReader reader, boolean atAllowed, boolean parsingEntityArgumentSuggestions) {
+        this.parsingEntityArgumentSuggestions = parsingEntityArgumentSuggestions;
+        // Paper end
         this.distance = MinMaxBounds.Doubles.ANY;
         this.level = MinMaxBounds.Ints.ANY;
         this.rotX = WrappedMinMaxBounds.ANY;
diff --git a/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java b/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
index d34d70029ef30ea8c4f1598f676ad65557c066e1..b6abbf5775b8aa73d32494e6ac0e9afbeb534ae2 100644
--- a/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
+++ b/src/main/java/net/minecraft/commands/arguments/selector/options/EntitySelectorOptions.java
@@ -66,6 +66,19 @@ public class EntitySelectorOptions {
     public static final DynamicCommandExceptionType ERROR_ENTITY_TYPE_INVALID = new DynamicCommandExceptionType((entity) -> {
         return Component.translatable("argument.entity.options.type.invalid", entity);
     });
+    // Paper start
+    public static final DynamicCommandExceptionType ERROR_ENTITY_TAG_INVALID = new DynamicCommandExceptionType((object) -> {
+        return io.papermc.paper.adventure.PaperAdventure
+            .asVanilla(net.kyori.adventure.text.Component
+                .text("Invalid or unknown entity type tag '" + object + "'")
+                .hoverEvent(net.kyori.adventure.text.event.HoverEvent
+                    .showText(net.kyori.adventure.text.Component
+                        .text("You can disable this error in 'paper.yml'")
+                    )
+                )
+            );
+    });
+    // Paper end
 
     private static void register(String id, EntitySelectorOptions.Modifier handler, Predicate<EntitySelectorParser> condition, Component description) {
         OPTIONS.put(id, new EntitySelectorOptions.Option(handler, condition, description));
@@ -313,6 +326,12 @@ public class EntitySelectorOptions {
 
                     if (reader.isTag()) {
                         TagKey<EntityType<?>> tagKey = TagKey.create(Registries.ENTITY_TYPE, ResourceLocation.read(reader.getReader()));
+                        // Paper start - throw error if invalid entity tag (only on suggestions to keep cmd success behavior)
+                        if (reader.parsingEntityArgumentSuggestions && io.papermc.paper.configuration.GlobalConfiguration.get().commands.fixTargetSelectorTagCompletion && net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.getTag(tagKey).isEmpty()) {
+                            reader.getReader().setCursor(i);
+                            throw ERROR_ENTITY_TAG_INVALID.createWithContext(reader.getReader(), tagKey);
+                        }
+                        // Paper end
                         reader.addPredicate((entity) -> {
                             return entity.getType().is(tagKey) != bl;
                         });
