From 263695053d997e75ec4a10f0de3ea0cb8a0de80c Mon Sep 17 00:00:00 2001
From: Chris Xiong <chirs241097@gmail.com>
Date: Fri, 6 Sep 2024 23:02:34 -0400
Subject: 1.21 ... maybe (still testing)

Special thanks to Xenapte for the 1.20.6 patch.
---
 .../trashyaddon/commands/BlockDataCommand.java     |   12 +-
 .../trashyaddon/commands/CEntitySelector.java      |  405 ++++---
 .../commands/CEntitySelectorOptions.java           |  898 ++++++++-------
 .../commands/CEntitySelectorReader.java            | 1197 ++++++++++----------
 .../trashyaddon/commands/EntityDataCommand.java    |    4 +-
 .../trashyaddon/commands/MapDumpCommand.java       |   13 +-
 .../trashyaddon/commands/MapTallyCommand.java      |    7 +-
 .../commands/argument/CEntityArgumentType.java     |    2 +-
 .../trashyaddon/modules/AcceptablePrices.java      |    2 +-
 .../chrisoft/trashyaddon/modules/AutoTrade.java    |   14 +-
 .../trashyaddon/modules/MatchedItemHighlight.java  |    4 +-
 11 files changed, 1395 insertions(+), 1163 deletions(-)

(limited to 'src/main/java/org/chrisoft')

diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/BlockDataCommand.java b/src/main/java/org/chrisoft/trashyaddon/commands/BlockDataCommand.java
index 71915bc..11fa8a3 100644
--- a/src/main/java/org/chrisoft/trashyaddon/commands/BlockDataCommand.java
+++ b/src/main/java/org/chrisoft/trashyaddon/commands/BlockDataCommand.java
@@ -9,14 +9,16 @@ import net.minecraft.util.hit.BlockHitResult;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.world.RaycastContext;
 import net.minecraft.command.CommandSource;
-import net.minecraft.nbt.NbtCompound;
+import net.minecraft.component.DataComponentTypes;
+import net.minecraft.component.type.NbtComponent;
 import net.minecraft.text.Text;
-import net.minecraft.util.math.Box;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.entity.Entity;
+import net.minecraft.nbt.NbtCompound;
 import net.minecraft.util.math.Vec3d;
 
 import static com.mojang.brigadier.Command.SINGLE_SUCCESS;
+
 public class BlockDataCommand extends Command {
     private final MinecraftClient mc = MinecraftClient.getInstance();
     public BlockDataCommand() {
@@ -28,8 +30,8 @@ public class BlockDataCommand extends Command {
         builder.executes(context -> {
             double RANGE = 5;
             Entity player = mc.cameraEntity;
-            Vec3d rot = player.getRotationVec(mc.getTickDelta());
-            Vec3d min = player.getCameraPosVec(mc.getTickDelta());
+            Vec3d rot = player.getRotationVec(1.f);
+            Vec3d min = player.getCameraPosVec(1.f);
             Vec3d max = min.add(rot.multiply(RANGE));
             RaycastContext rc = new RaycastContext(min, max, RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.NONE, player);
             BlockHitResult bh = mc.world.raycast(rc);
@@ -51,7 +53,7 @@ public class BlockDataCommand extends Command {
                 error("block has no block entity");
                 return SINGLE_SUCCESS;
             }
-            NbtCompound bd = be.createNbt();
+            NbtCompound bd = be.createNbt(mc.world.getRegistryManager());
             info(NbtHelper.toPrettyPrintedText(bd));
 
             return SINGLE_SUCCESS;
diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelector.java b/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelector.java
index 8bfa233..9d41880 100644
--- a/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelector.java
+++ b/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelector.java
@@ -1,25 +1,28 @@
 package org.chrisoft.trashyaddon.commands;
 
-import com.google.common.collect.Lists;
 import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
 import java.util.function.BiConsumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
-import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.world.ClientWorld;
 import net.minecraft.command.argument.EntityArgumentType;
 import net.minecraft.entity.Entity;
 import net.minecraft.entity.EntityType;
 import net.minecraft.predicate.NumberRange;
+import net.minecraft.resource.featuretoggle.FeatureSet;
+import net.minecraft.server.command.ServerCommandSource;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.server.world.ServerWorld;
 import net.minecraft.text.Text;
 import net.minecraft.text.Texts;
 import net.minecraft.util.TypeFilter;
+import net.minecraft.util.Util;
 import net.minecraft.util.function.LazyIterationConsumer;
 import net.minecraft.util.math.Box;
 import net.minecraft.util.math.Vec3d;
@@ -27,131 +30,273 @@ import org.chrisoft.trashyaddon.mixin.ClientWorldMixin;
 import org.jetbrains.annotations.Nullable;
 
 public class CEntitySelector {
-   public static final int MAX_VALUE = Integer.MAX_VALUE;
-   public static final BiConsumer<Vec3d, List<? extends Entity>> ARBITRARY = (pos, entities) -> {
-   };
-   private static final TypeFilter<Entity, ?> PASSTHROUGH_FILTER = new TypeFilter<Entity, Entity>() {
-      public Entity downcast(Entity arg) {
-         return arg;
-      }
-
-      @Override
-      public Class<? extends Entity> getBaseClass() {
-         return Entity.class;
-      }
-   };
-   private final int limit;
-   private final boolean includesNonPlayers;
-   private final boolean localWorldOnly;
-   private final Predicate<Entity> basePredicate;
-   private final NumberRange.DoubleRange distance;
-   private final Function<Vec3d, Vec3d> positionOffset;
-   @Nullable
-   private final Box box;
-   private final BiConsumer<Vec3d, List<? extends Entity>> sorter;
-   private final boolean senderOnly;
-   @Nullable
-   private final String playerName;
-   @Nullable
-   private final UUID uuid;
-   private final TypeFilter<Entity, ?> entityFilter;
-   private final boolean usesAt;
-
-   public CEntitySelector(
-      int count,
-      boolean includesNonPlayers,
-      boolean localWorldOnly,
-      Predicate<Entity> basePredicate,
-      NumberRange.DoubleRange distance,
-      Function<Vec3d, Vec3d> positionOffset,
-      @Nullable Box box,
-      BiConsumer<Vec3d, List<? extends Entity>> sorter,
-      boolean senderOnly,
-      @Nullable String playerName,
-      @Nullable UUID uuid,
-      @Nullable EntityType<?> type,
-      boolean usesAt
-   ) {
-      this.limit = count;
-      this.includesNonPlayers = includesNonPlayers;
-      this.localWorldOnly = localWorldOnly;
-      this.basePredicate = basePredicate;
-      this.distance = distance;
-      this.positionOffset = positionOffset;
-      this.box = box;
-      this.sorter = sorter;
-      this.senderOnly = senderOnly;
-      this.playerName = playerName;
-      this.uuid = uuid;
-      this.entityFilter = (TypeFilter<Entity, ?>)(type == null ? PASSTHROUGH_FILTER : type);
-      this.usesAt = usesAt;
-   }
-
-   public int getLimit() {
-      return this.limit;
-   }
-
-   public boolean includesNonPlayers() {
-      return this.includesNonPlayers;
-   }
-
-   public boolean isSenderOnly() {
-      return this.senderOnly;
-   }
-
-   public boolean isLocalWorldOnly() {
-      return this.localWorldOnly;
-   }
-
-   public boolean usesAt() {
-      return this.usesAt;
-   }
-
-   private int getAppendLimit() {
-      return this.sorter == ARBITRARY ? this.limit : Integer.MAX_VALUE;
-   }
-
-   private Predicate<Entity> getPositionPredicate(Vec3d pos) {
-      Predicate<Entity> predicate = this.basePredicate;
-      if (this.box != null) {
-         Box lv = this.box.offset(pos);
-         predicate = predicate.and(entity -> lv.intersects(entity.getBoundingBox()));
-      }
-
-      if (!this.distance.isDummy()) {
-         predicate = predicate.and(entity -> this.distance.testSqrt(entity.squaredDistanceTo(pos)));
-      }
-
-      return predicate;
-   }
-   public List<? extends Entity> getClientSideEntityMatches(ClientWorld cw, Vec3d sourcePos) throws CommandSyntaxException {
-       Predicate<Entity> p = this.getPositionPredicate(sourcePos);
-       List<Entity> ret = new ArrayList<>();
-       int limit = this.getAppendLimit();
-       if (this.box != null)
-           cw.collectEntitiesByType(this.entityFilter, this.box.offset(sourcePos), p, ret, limit);
-       else {
-           ((ClientWorldMixin)cw).invokeGetEntityLookup().forEach(this.entityFilter, (e) -> {
-               if (p.test(e)) {
-                   ret.add(e);
-                   if (ret.size() >= limit)
-                       return LazyIterationConsumer.NextIteration.ABORT;
-               }
-               return LazyIterationConsumer.NextIteration.CONTINUE;
-           });
-       }
-       return ret;
-   }
-
-   private <T extends Entity> List<T> getEntities(Vec3d pos, List<T> entities) {
-      if (entities.size() > 1) {
-         this.sorter.accept(pos, entities);
-      }
-
-      return entities.subList(0, Math.min(this.limit, entities.size()));
-   }
-
-   public static Text getNames(List<? extends Entity> entities) {
-      return Texts.join(entities, Entity::getDisplayName);
-   }
+    public static final int MAX_VALUE = Integer.MAX_VALUE;
+    public static final BiConsumer<Vec3d, List<? extends Entity>> ARBITRARY = (pos, entities) -> {
+    };
+    private static final TypeFilter<Entity, ?> PASSTHROUGH_FILTER = new TypeFilter<Entity, Entity>() {
+        public Entity downcast(Entity arg) {
+            return arg;
+        }
+
+        @Override
+        public Class<? extends Entity> getBaseClass() {
+            return Entity.class;
+        }
+    };
+    private final int limit;
+    private final boolean includesNonPlayers;
+    private final boolean localWorldOnly;
+    private final List<Predicate<Entity>> predicates;
+    private final NumberRange.DoubleRange distance;
+    private final Function<Vec3d, Vec3d> positionOffset;
+    @Nullable
+    private final Box box;
+    private final BiConsumer<Vec3d, List<? extends Entity>> sorter;
+    private final boolean senderOnly;
+    @Nullable
+    private final String playerName;
+    @Nullable
+    private final UUID uuid;
+    private final TypeFilter<Entity, ?> entityFilter;
+    private final boolean usesAt;
+
+    public CEntitySelector(
+        int count,
+        boolean includesNonPlayers,
+        boolean localWorldOnly,
+        List<Predicate<Entity>> predicates,
+        NumberRange.DoubleRange distance,
+        Function<Vec3d, Vec3d> positionOffset,
+        @Nullable Box box,
+        BiConsumer<Vec3d, List<? extends Entity>> sorter,
+        boolean senderOnly,
+        @Nullable String playerName,
+        @Nullable UUID uuid,
+        @Nullable EntityType<?> type,
+        boolean usesAt
+    ) {
+        this.limit = count;
+        this.includesNonPlayers = includesNonPlayers;
+        this.localWorldOnly = localWorldOnly;
+        this.predicates = predicates;
+        this.distance = distance;
+        this.positionOffset = positionOffset;
+        this.box = box;
+        this.sorter = sorter;
+        this.senderOnly = senderOnly;
+        this.playerName = playerName;
+        this.uuid = uuid;
+        this.entityFilter = (TypeFilter<Entity, ?>)(type == null ? PASSTHROUGH_FILTER : type);
+        this.usesAt = usesAt;
+    }
+
+    public int getLimit() {
+        return this.limit;
+    }
+
+    public boolean includesNonPlayers() {
+        return this.includesNonPlayers;
+    }
+
+    public boolean isSenderOnly() {
+        return this.senderOnly;
+    }
+
+    public boolean isLocalWorldOnly() {
+        return this.localWorldOnly;
+    }
+
+    public boolean usesAt() {
+        return this.usesAt;
+    }
+
+    private void checkSourcePermission(ServerCommandSource source) throws CommandSyntaxException {
+        if (this.usesAt && !source.hasPermissionLevel(2)) {
+            throw EntityArgumentType.NOT_ALLOWED_EXCEPTION.create();
+        }
+    }
+
+    public Entity getEntity(ServerCommandSource source) throws CommandSyntaxException {
+        this.checkSourcePermission(source);
+        List<? extends Entity> list = this.getEntities(source);
+        if (list.isEmpty()) {
+            throw EntityArgumentType.ENTITY_NOT_FOUND_EXCEPTION.create();
+        } else if (list.size() > 1) {
+            throw EntityArgumentType.TOO_MANY_ENTITIES_EXCEPTION.create();
+        } else {
+            return list.get(0);
+        }
+    }
+
+    public List<? extends Entity> getEntities(ServerCommandSource source) throws CommandSyntaxException {
+        this.checkSourcePermission(source);
+        if (!this.includesNonPlayers) {
+            return this.getPlayers(source);
+        } else if (this.playerName != null) {
+            ServerPlayerEntity lv = source.getServer().getPlayerManager().getPlayer(this.playerName);
+            return lv == null ? List.of() : List.of(lv);
+        } else if (this.uuid != null) {
+            for (ServerWorld lv2 : source.getServer().getWorlds()) {
+                Entity lv3 = lv2.getEntity(this.uuid);
+                if (lv3 != null) {
+                    if (lv3.getType().isEnabled(source.getEnabledFeatures())) {
+                        return List.of(lv3);
+                    }
+                    break;
+                }
+            }
+
+            return List.of();
+        } else {
+            Vec3d lv4 = this.positionOffset.apply(source.getPosition());
+            Box lv5 = this.getOffsetBox(lv4);
+            if (this.senderOnly) {
+                Predicate<Entity> predicate = this.getPositionPredicate(lv4, lv5, null);
+                return source.getEntity() != null && predicate.test(source.getEntity()) ? List.of(source.getEntity()) : List.of();
+            } else {
+                Predicate<Entity> predicate = this.getPositionPredicate(lv4, lv5, source.getEnabledFeatures());
+                List<Entity> list = new ObjectArrayList();
+                if (this.isLocalWorldOnly()) {
+                    this.appendEntitiesFromWorld(list, source.getWorld(), lv5, predicate);
+                } else {
+                    for (ServerWorld lv6 : source.getServer().getWorlds()) {
+                        this.appendEntitiesFromWorld(list, lv6, lv5, predicate);
+                    }
+                }
+
+                return this.getEntities(lv4, list);
+            }
+        }
+    }
+
+    private void appendEntitiesFromWorld(List<Entity> entities, ServerWorld world, @Nullable Box box, Predicate<Entity> predicate) {
+        int i = this.getAppendLimit();
+        if (entities.size() < i) {
+            if (box != null) {
+                world.collectEntitiesByType(this.entityFilter, box, predicate, entities, i);
+            } else {
+                world.collectEntitiesByType(this.entityFilter, predicate, entities, i);
+            }
+        }
+    }
+
+    private int getAppendLimit() {
+        return this.sorter == ARBITRARY ? this.limit : Integer.MAX_VALUE;
+    }
+
+    public ServerPlayerEntity getPlayer(ServerCommandSource source) throws CommandSyntaxException {
+        this.checkSourcePermission(source);
+        List<ServerPlayerEntity> list = this.getPlayers(source);
+        if (list.size() != 1) {
+            throw EntityArgumentType.PLAYER_NOT_FOUND_EXCEPTION.create();
+        } else {
+            return list.get(0);
+        }
+    }
+
+    public List<ServerPlayerEntity> getPlayers(ServerCommandSource source) throws CommandSyntaxException {
+        this.checkSourcePermission(source);
+        if (this.playerName != null) {
+            ServerPlayerEntity lv = source.getServer().getPlayerManager().getPlayer(this.playerName);
+            return lv == null ? List.of() : List.of(lv);
+        } else if (this.uuid != null) {
+            ServerPlayerEntity lv = source.getServer().getPlayerManager().getPlayer(this.uuid);
+            return lv == null ? List.of() : List.of(lv);
+        } else {
+            Vec3d lv2 = this.positionOffset.apply(source.getPosition());
+            Box lv3 = this.getOffsetBox(lv2);
+            Predicate<Entity> predicate = this.getPositionPredicate(lv2, lv3, null);
+            if (this.senderOnly) {
+                if (source.getEntity() instanceof ServerPlayerEntity lv4 && predicate.test(lv4)) {
+                    return List.of(lv4);
+                }
+
+                return List.of();
+            } else {
+                int i = this.getAppendLimit();
+                List<ServerPlayerEntity> list;
+                if (this.isLocalWorldOnly()) {
+                    list = source.getWorld().getPlayers(predicate, i);
+                } else {
+                    list = new ObjectArrayList();
+
+                    for (ServerPlayerEntity lv5 : source.getServer().getPlayerManager().getPlayerList()) {
+                        if (predicate.test(lv5)) {
+                            list.add(lv5);
+                            if (list.size() >= i) {
+                                return list;
+                            }
+                        }
+                    }
+                }
+
+                return this.getEntities(lv2, list);
+            }
+        }
+    }
+
+    @Nullable
+    private Box getOffsetBox(Vec3d offset) {
+        return this.box != null ? this.box.offset(offset) : null;
+    }
+
+    private Predicate<Entity> getPositionPredicate(Vec3d pos, @Nullable Box box, @Nullable FeatureSet enabledFeatures) {
+        boolean bl = enabledFeatures != null;
+        boolean bl2 = box != null;
+        boolean bl3 = !this.distance.isDummy();
+        int i = (bl ? 1 : 0) + (bl2 ? 1 : 0) + (bl3 ? 1 : 0);
+        List<Predicate<Entity>> list;
+        if (i == 0) {
+            list = this.predicates;
+        } else {
+            List<Predicate<Entity>> list2 = new ObjectArrayList(this.predicates.size() + i);
+            list2.addAll(this.predicates);
+            if (bl) {
+                list2.add(entity -> entity.getType().isEnabled(enabledFeatures));
+            }
+
+            if (bl2) {
+                list2.add(entity -> box.intersects(entity.getBoundingBox()));
+            }
+
+            if (bl3) {
+                list2.add(entity -> this.distance.testSqrt(entity.squaredDistanceTo(pos)));
+            }
+
+            list = list2;
+        }
+
+        return Util.allOf(list);
+    }
+
+    public List<? extends Entity> getClientSideEntityMatches(ClientWorld cw, Vec3d sourcePos) throws CommandSyntaxException {
+        Predicate<Entity> p = this.getPositionPredicate(sourcePos, null, null);
+        List<Entity> ret = new ArrayList<>();
+        int limit = this.getAppendLimit();
+        if (this.box != null)
+            cw.collectEntitiesByType(this.entityFilter, this.box.offset(sourcePos), p, ret, limit);
+        else {
+            ((ClientWorldMixin)cw).invokeGetEntityLookup().forEach(this.entityFilter, (e) -> {
+                if (p.test(e)) {
+                    ret.add(e);
+                    if (ret.size() >= limit)
+                        return LazyIterationConsumer.NextIteration.ABORT;
+                }
+                return LazyIterationConsumer.NextIteration.CONTINUE;
+            });
+        }
+        return ret;
+    }
+
+    private <T extends Entity> List<T> getEntities(Vec3d pos, List<T> entities) {
+        if (entities.size() > 1) {
+            this.sorter.accept(pos, entities);
+        }
+
+        return entities.subList(0, Math.min(this.limit, entities.size()));
+    }
+
+    public static Text getNames(List<? extends Entity> entities) {
+        return Texts.join(entities, Entity::getDisplayName);
+    }
 }
diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorOptions.java b/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorOptions.java
index cb419f0..c49e80f 100644
--- a/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorOptions.java
+++ b/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorOptions.java
@@ -10,463 +10,515 @@ import java.util.Arrays;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Map.Entry;
 import java.util.function.Predicate;
-
-import meteordevelopment.meteorclient.utils.entity.EntityUtils;
+import net.minecraft.advancement.AdvancementEntry;
 import net.minecraft.advancement.AdvancementProgress;
+import net.minecraft.advancement.PlayerAdvancementTracker;
 import net.minecraft.advancement.criterion.CriterionProgress;
-import net.minecraft.client.network.ClientPlayerEntity;
 import net.minecraft.command.CommandSource;
-import net.minecraft.command.FloatRangeArgument;
 import net.minecraft.entity.EntityType;
 import net.minecraft.entity.LivingEntity;
 import net.minecraft.item.ItemStack;
+import net.minecraft.loot.condition.LootCondition;
+import net.minecraft.loot.context.LootContext;
+import net.minecraft.loot.context.LootContextParameterSet;
+import net.minecraft.loot.context.LootContextParameters;
+import net.minecraft.loot.context.LootContextTypes;
 import net.minecraft.nbt.NbtCompound;
 import net.minecraft.nbt.NbtHelper;
 import net.minecraft.nbt.StringNbtReader;
 import net.minecraft.predicate.NumberRange;
 import net.minecraft.registry.Registries;
+import net.minecraft.registry.RegistryKey;
 import net.minecraft.registry.RegistryKeys;
+import net.minecraft.registry.entry.RegistryEntry;
 import net.minecraft.registry.tag.TagKey;
 import net.minecraft.scoreboard.AbstractTeam;
 import net.minecraft.scoreboard.ReadableScoreboardScore;
 import net.minecraft.scoreboard.Scoreboard;
 import net.minecraft.scoreboard.ScoreboardObjective;
+import net.minecraft.server.ServerAdvancementLoader;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.server.world.ServerWorld;
 import net.minecraft.text.Text;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.math.MathHelper;
 import net.minecraft.world.GameMode;
+import net.minecraft.command.FloatRangeArgument;
 
 public class CEntitySelectorOptions {
-   private static final Map<String, CEntitySelectorOptions.SelectorOption> OPTIONS = Maps.newHashMap();
-   public static final DynamicCommandExceptionType UNKNOWN_OPTION_EXCEPTION = new DynamicCommandExceptionType(
-      option -> Text.stringifiedTranslatable("argument.entity.options.unknown", option)
-   );
-   public static final DynamicCommandExceptionType INAPPLICABLE_OPTION_EXCEPTION = new DynamicCommandExceptionType(
-      option -> Text.stringifiedTranslatable("argument.entity.options.inapplicable", option)
-   );
-   public static final SimpleCommandExceptionType NEGATIVE_DISTANCE_EXCEPTION = new SimpleCommandExceptionType(
-      Text.translatable("argument.entity.options.distance.negative")
-   );
-   public static final SimpleCommandExceptionType NEGATIVE_LEVEL_EXCEPTION = new SimpleCommandExceptionType(
-      Text.translatable("argument.entity.options.level.negative")
-   );
-   public static final SimpleCommandExceptionType TOO_SMALL_LEVEL_EXCEPTION = new SimpleCommandExceptionType(
-      Text.translatable("argument.entity.options.limit.toosmall")
-   );
-   public static final DynamicCommandExceptionType IRREVERSIBLE_SORT_EXCEPTION = new DynamicCommandExceptionType(
-      sortType -> Text.stringifiedTranslatable("argument.entity.options.sort.irreversible", sortType)
-   );
-   public static final DynamicCommandExceptionType INVALID_MODE_EXCEPTION = new DynamicCommandExceptionType(
-      gameMode -> Text.stringifiedTranslatable("argument.entity.options.mode.invalid", gameMode)
-   );
-   public static final DynamicCommandExceptionType INVALID_TYPE_EXCEPTION = new DynamicCommandExceptionType(
-      entity -> Text.stringifiedTranslatable("argument.entity.options.type.invalid", entity)
-   );
-
-   private static void putOption(String id, CEntitySelectorOptions.SelectorHandler handler, Predicate<CEntitySelectorReader> condition, Text description) {
-      OPTIONS.put(id, new CEntitySelectorOptions.SelectorOption(handler, condition, description));
-   }
-
-   public static void register() {
-      if (OPTIONS.isEmpty()) {
-         putOption("name", reader -> {
-            int i = reader.getReader().getCursor();
-            boolean bl = reader.readNegationCharacter();
-            String string = reader.getReader().readString();
-            if (reader.excludesName() && !bl) {
-               reader.getReader().setCursor(i);
-               throw INAPPLICABLE_OPTION_EXCEPTION.createWithContext(reader.getReader(), "name");
-            } else {
-               if (bl) {
-                  reader.setExcludesName(true);
-               } else {
-                  reader.setSelectsName(true);
-               }
-
-               reader.setPredicate(readerx -> readerx.getName().getString().equals(string) != bl);
-            }
-         }, reader -> !reader.selectsName(), Text.translatable("argument.entity.options.name.description"));
-         putOption("distance", reader -> {
-            int i = reader.getReader().getCursor();
-            NumberRange.DoubleRange lv = NumberRange.DoubleRange.parse(reader.getReader());
-            if ((!lv.min().isPresent() || !(lv.min().get() < 0.0)) && (!lv.max().isPresent() || !(lv.max().get() < 0.0))) {
-               reader.setDistance(lv);
-               reader.setLocalWorldOnly();
-            } else {
-               reader.getReader().setCursor(i);
-               throw NEGATIVE_DISTANCE_EXCEPTION.createWithContext(reader.getReader());
-            }
-         }, reader -> reader.getDistance().isDummy(), Text.translatable("argument.entity.options.distance.description"));
-         putOption("level", reader -> {
-            int i = reader.getReader().getCursor();
-            NumberRange.IntRange lv = NumberRange.IntRange.parse(reader.getReader());
-            if ((!lv.min().isPresent() || lv.min().get() >= 0) && (!lv.max().isPresent() || lv.max().get() >= 0)) {
-               reader.setLevelRange(lv);
-               reader.setIncludesNonPlayers(false);
-            } else {
-               reader.getReader().setCursor(i);
-               throw NEGATIVE_LEVEL_EXCEPTION.createWithContext(reader.getReader());
-            }
-         }, reader -> reader.getLevelRange().isDummy(), Text.translatable("argument.entity.options.level.description"));
-         putOption("x", reader -> {
-            reader.setLocalWorldOnly();
-            reader.setX(reader.getReader().readDouble());
-         }, reader -> reader.getX() == null, Text.translatable("argument.entity.options.x.description"));
-         putOption("y", reader -> {
-            reader.setLocalWorldOnly();
-            reader.setY(reader.getReader().readDouble());
-         }, reader -> reader.getY() == null, Text.translatable("argument.entity.options.y.description"));
-         putOption("z", reader -> {
-            reader.setLocalWorldOnly();
-            reader.setZ(reader.getReader().readDouble());
-         }, reader -> reader.getZ() == null, Text.translatable("argument.entity.options.z.description"));
-         putOption("dx", reader -> {
-            reader.setLocalWorldOnly();
-            reader.setDx(reader.getReader().readDouble());
-         }, reader -> reader.getDx() == null, Text.translatable("argument.entity.options.dx.description"));
-         putOption("dy", reader -> {
-            reader.setLocalWorldOnly();
-            reader.setDy(reader.getReader().readDouble());
-         }, reader -> reader.getDy() == null, Text.translatable("argument.entity.options.dy.description"));
-         putOption("dz", reader -> {
-            reader.setLocalWorldOnly();
-            reader.setDz(reader.getReader().readDouble());
-         }, reader -> reader.getDz() == null, Text.translatable("argument.entity.options.dz.description"));
-         putOption(
-            "x_rotation",
-            reader -> reader.setPitchRange(FloatRangeArgument.parse(reader.getReader(), true, MathHelper::wrapDegrees)),
-            reader -> reader.getPitchRange() == FloatRangeArgument.ANY,
-            Text.translatable("argument.entity.options.x_rotation.description")
-         );
-         putOption(
-            "y_rotation",
-            reader -> reader.setYawRange(FloatRangeArgument.parse(reader.getReader(), true, MathHelper::wrapDegrees)),
-            reader -> reader.getYawRange() == FloatRangeArgument.ANY,
-            Text.translatable("argument.entity.options.y_rotation.description")
-         );
-         putOption("limit", reader -> {
-            int i = reader.getReader().getCursor();
-            int j = reader.getReader().readInt();
-            if (j < 1) {
-               reader.getReader().setCursor(i);
-               throw TOO_SMALL_LEVEL_EXCEPTION.createWithContext(reader.getReader());
-            } else {
-               reader.setLimit(j);
-               reader.setHasLimit(true);
-            }
-         }, reader -> !reader.isSenderOnly() && !reader.hasLimit(), Text.translatable("argument.entity.options.limit.description"));
-         putOption(
-            "sort",
-            reader -> {
-               int i = reader.getReader().getCursor();
-               String string = reader.getReader().readUnquotedString();
-               reader.setSuggestionProvider(
-                  (builder, consumer) -> CommandSource.suggestMatching(Arrays.asList("nearest", "furthest", "random", "arbitrary"), builder)
-               );
-
-               reader.setSorter(switch (string) {
-                  case "nearest" -> CEntitySelectorReader.NEAREST;
-                  case "furthest" -> CEntitySelectorReader.FURTHEST;
-                  case "random" -> CEntitySelectorReader.RANDOM;
-                  case "arbitrary" -> CEntitySelector.ARBITRARY;
-                  default -> {
-                     reader.getReader().setCursor(i);
-                     throw IRREVERSIBLE_SORT_EXCEPTION.createWithContext(reader.getReader(), string);
-                  }
-               });
-               reader.setHasSorter(true);
-            },
-            reader -> !reader.isSenderOnly() && !reader.hasSorter(),
-            Text.translatable("argument.entity.options.sort.description")
-         );
-         putOption("gamemode", reader -> {
-            reader.setSuggestionProvider((builder, consumer) -> {
-               String stringx = builder.getRemaining().toLowerCase(Locale.ROOT);
-               boolean blx = !reader.excludesGameMode();
-               boolean bl2 = true;
-               if (!stringx.isEmpty()) {
-                  if (stringx.charAt(0) == '!') {
-                     blx = false;
-                     stringx = stringx.substring(1);
-                  } else {
-                     bl2 = false;
-                  }
-               }
-
-               for (GameMode lvx : GameMode.values()) {
-                  if (lvx.getName().toLowerCase(Locale.ROOT).startsWith(stringx)) {
-                     if (bl2) {
-                        builder.suggest("!" + lvx.getName());
-                     }
-
-                     if (blx) {
-                        builder.suggest(lvx.getName());
-                     }
-                  }
-               }
-
-               return builder.buildFuture();
-            });
-            int i = reader.getReader().getCursor();
-            boolean bl = reader.readNegationCharacter();
-            if (reader.excludesGameMode() && !bl) {
-               reader.getReader().setCursor(i);
-               throw INAPPLICABLE_OPTION_EXCEPTION.createWithContext(reader.getReader(), "gamemode");
-            } else {
-               String string = reader.getReader().readUnquotedString();
-               GameMode lv = GameMode.byName(string, null);
-               if (lv == null) {
-                  reader.getReader().setCursor(i);
-                  throw INVALID_MODE_EXCEPTION.createWithContext(reader.getReader(), string);
-               } else {
-                  reader.setIncludesNonPlayers(false);
-                  reader.setPredicate(entity -> {
-                     if (!(entity instanceof ClientPlayerEntity)) {
-                        return false;
-                     } else {
-                        GameMode lvx = EntityUtils.getGameMode((ClientPlayerEntity)entity);
-                        return bl ? lvx != lv : lvx == lv;
-                     }
-                  });
-                  if (bl) {
-                     reader.setExcludesGameMode(true);
-                  } else {
-                     reader.setSelectsGameMode(true);
-                  }
-               }
-            }
-         }, reader -> !reader.selectsGameMode(), Text.translatable("argument.entity.options.gamemode.description"));
-         putOption("team", reader -> {
-            boolean bl = reader.readNegationCharacter();
-            String string = reader.getReader().readUnquotedString();
-            reader.setPredicate(entity -> {
-               if (!(entity instanceof LivingEntity)) {
-                  return false;
-               } else {
-                  AbstractTeam lv = entity.getScoreboardTeam();
-                  String string2 = lv == null ? "" : lv.getName();
-                  return string2.equals(string) != bl;
-               }
-            });
-            if (bl) {
-               reader.setExcludesTeam(true);
-            } else {
-               reader.setSelectsTeam(true);
-            }
-         }, reader -> !reader.selectsTeam(), Text.translatable("argument.entity.options.team.description"));
-         putOption("type", reader -> {
-            reader.setSuggestionProvider((builder, consumer) -> {
-               CommandSource.suggestIdentifiers(Registries.ENTITY_TYPE.getIds(), builder, String.valueOf('!'));
-               CommandSource.suggestIdentifiers(Registries.ENTITY_TYPE.streamTags().map(TagKey::id), builder, "!#");
-               if (!reader.excludesEntityType()) {
-                  CommandSource.suggestIdentifiers(Registries.ENTITY_TYPE.getIds(), builder);
-                  CommandSource.suggestIdentifiers(Registries.ENTITY_TYPE.streamTags().map(TagKey::id), builder, String.valueOf('#'));
-               }
-
-               return builder.buildFuture();
-            });
-            int i = reader.getReader().getCursor();
-            boolean bl = reader.readNegationCharacter();
-            if (reader.excludesEntityType() && !bl) {
-               reader.getReader().setCursor(i);
-               throw INAPPLICABLE_OPTION_EXCEPTION.createWithContext(reader.getReader(), "type");
-            } else {
-               if (bl) {
-                  reader.setExcludesEntityType();
-               }
-
-               if (reader.readTagCharacter()) {
-                  TagKey<EntityType<?>> lv = TagKey.of(RegistryKeys.ENTITY_TYPE, Identifier.fromCommandInput(reader.getReader()));
-                  reader.setPredicate(entity -> entity.getType().isIn(lv) != bl);
-               } else {
-                  Identifier lv2 = Identifier.fromCommandInput(reader.getReader());
-                  EntityType<?> lv3 = Registries.ENTITY_TYPE.getOrEmpty(lv2).orElseThrow(() -> {
-                     reader.getReader().setCursor(i);
-                     return INVALID_TYPE_EXCEPTION.createWithContext(reader.getReader(), lv2.toString());
-                  });
-                  if (Objects.equals(EntityType.PLAYER, lv3) && !bl) {
-                     reader.setIncludesNonPlayers(false);
-                  }
-
-                  reader.setPredicate(entity -> Objects.equals(lv3, entity.getType()) != bl);
-                  if (!bl) {
-                     reader.setEntityType(lv3);
-                  }
-               }
-            }
-         }, reader -> !reader.selectsEntityType(), Text.translatable("argument.entity.options.type.description"));
-         putOption("tag", reader -> {
-            boolean bl = reader.readNegationCharacter();
-            String string = reader.getReader().readUnquotedString();
-            reader.setPredicate(entity -> "".equals(string) ? entity.getCommandTags().isEmpty() != bl : entity.getCommandTags().contains(string) != bl);
-         }, reader -> true, Text.translatable("argument.entity.options.tag.description"));
-         putOption("nbt", reader -> {
-            boolean bl = reader.readNegationCharacter();
-            NbtCompound lv = new StringNbtReader(reader.getReader()).parseCompound();
-            reader.setPredicate(entity -> {
-               NbtCompound lvx = entity.writeNbt(new NbtCompound());
-               if (entity instanceof ClientPlayerEntity) {
-                  ItemStack lv2 = ((ClientPlayerEntity)entity).getInventory().getMainHandStack();
-                  if (!lv2.isEmpty()) {
-                     lvx.put("SelectedItem", lv2.writeNbt(new NbtCompound()));
-                  }
-               }
-
-               return NbtHelper.matches(lv, lvx, true) != bl;
-            });
-         }, reader -> true, Text.translatable("argument.entity.options.nbt.description"));
-         putOption("scores", reader -> {
-            StringReader stringReader = reader.getReader();
-            Map<String, NumberRange.IntRange> map = Maps.newHashMap();
-            stringReader.expect('{');
-            stringReader.skipWhitespace();
-
-            while (stringReader.canRead() && stringReader.peek() != '}') {
-               stringReader.skipWhitespace();
-               String string = stringReader.readUnquotedString();
-               stringReader.skipWhitespace();
-               stringReader.expect('=');
-               stringReader.skipWhitespace();
-               NumberRange.IntRange lv = NumberRange.IntRange.parse(stringReader);
-               map.put(string, lv);
-               stringReader.skipWhitespace();
-               if (stringReader.canRead() && stringReader.peek() == ',') {
-                  stringReader.skip();
-               }
-            }
-
-            stringReader.expect('}');
-            if (!map.isEmpty()) {
-               reader.setPredicate(entity -> {
-                  Scoreboard lvx = entity.getServer().getScoreboard();
+    private static final Map<String, CEntitySelectorOptions.SelectorOption> OPTIONS = Maps.newHashMap();
+    public static final DynamicCommandExceptionType UNKNOWN_OPTION_EXCEPTION = new DynamicCommandExceptionType(
+        option -> Text.stringifiedTranslatable("argument.entity.options.unknown", option)
+    );
+    public static final DynamicCommandExceptionType INAPPLICABLE_OPTION_EXCEPTION = new DynamicCommandExceptionType(
+        option -> Text.stringifiedTranslatable("argument.entity.options.inapplicable", option)
+    );
+    public static final SimpleCommandExceptionType NEGATIVE_DISTANCE_EXCEPTION = new SimpleCommandExceptionType(
+        Text.translatable("argument.entity.options.distance.negative")
+    );
+    public static final SimpleCommandExceptionType NEGATIVE_LEVEL_EXCEPTION = new SimpleCommandExceptionType(
+        Text.translatable("argument.entity.options.level.negative")
+    );
+    public static final SimpleCommandExceptionType TOO_SMALL_LEVEL_EXCEPTION = new SimpleCommandExceptionType(
+        Text.translatable("argument.entity.options.limit.toosmall")
+    );
+    public static final DynamicCommandExceptionType IRREVERSIBLE_SORT_EXCEPTION = new DynamicCommandExceptionType(
+        sortType -> Text.stringifiedTranslatable("argument.entity.options.sort.irreversible", sortType)
+    );
+    public static final DynamicCommandExceptionType INVALID_MODE_EXCEPTION = new DynamicCommandExceptionType(
+        gameMode -> Text.stringifiedTranslatable("argument.entity.options.mode.invalid", gameMode)
+    );
+    public static final DynamicCommandExceptionType INVALID_TYPE_EXCEPTION = new DynamicCommandExceptionType(
+        entity -> Text.stringifiedTranslatable("argument.entity.options.type.invalid", entity)
+    );
+
+    private static void putOption(String id, CEntitySelectorOptions.SelectorHandler handler, Predicate<CEntitySelectorReader> condition, Text description) {
+        OPTIONS.put(id, new CEntitySelectorOptions.SelectorOption(handler, condition, description));
+    }
+
+    public static void register() {
+        if (OPTIONS.isEmpty()) {
+            putOption("name", reader -> {
+                int i = reader.getReader().getCursor();
+                boolean bl = reader.readNegationCharacter();
+                String string = reader.getReader().readString();
+                if (reader.excludesName() && !bl) {
+                    reader.getReader().setCursor(i);
+                    throw INAPPLICABLE_OPTION_EXCEPTION.createWithContext(reader.getReader(), "name");
+                } else {
+                    if (bl) {
+                        reader.setExcludesName(true);
+                    } else {
+                        reader.setSelectsName(true);
+                    }
+
+                    reader.addPredicate(entity -> entity.getName().getString().equals(string) != bl);
+                }
+            }, reader -> !reader.selectsName(), Text.translatable("argument.entity.options.name.description"));
+            putOption("distance", reader -> {
+                int i = reader.getReader().getCursor();
+                NumberRange.DoubleRange lv = NumberRange.DoubleRange.parse(reader.getReader());
+                if ((!lv.min().isPresent() || !(lv.min().get() < 0.0)) && (!lv.max().isPresent() || !(lv.max().get() < 0.0))) {
+                    reader.setDistance(lv);
+                    reader.setLocalWorldOnly();
+                } else {
+                    reader.getReader().setCursor(i);
+                    throw NEGATIVE_DISTANCE_EXCEPTION.createWithContext(reader.getReader());
+                }
+            }, reader -> reader.getDistance().isDummy(), Text.translatable("argument.entity.options.distance.description"));
+            putOption("level", reader -> {
+                int i = reader.getReader().getCursor();
+                NumberRange.IntRange lv = NumberRange.IntRange.parse(reader.getReader());
+                if ((!lv.min().isPresent() || lv.min().get() >= 0) && (!lv.max().isPresent() || lv.max().get() >= 0)) {
+                    reader.setLevelRange(lv);
+                    reader.setIncludesNonPlayers(false);
+                } else {
+                    reader.getReader().setCursor(i);
+                    throw NEGATIVE_LEVEL_EXCEPTION.createWithContext(reader.getReader());
+                }
+            }, reader -> reader.getLevelRange().isDummy(), Text.translatable("argument.entity.options.level.description"));
+            putOption("x", reader -> {
+                reader.setLocalWorldOnly();
+                reader.setX(reader.getReader().readDouble());
+            }, reader -> reader.getX() == null, Text.translatable("argument.entity.options.x.description"));
+            putOption("y", reader -> {
+                reader.setLocalWorldOnly();
+                reader.setY(reader.getReader().readDouble());
+            }, reader -> reader.getY() == null, Text.translatable("argument.entity.options.y.description"));
+            putOption("z", reader -> {
+                reader.setLocalWorldOnly();
+                reader.setZ(reader.getReader().readDouble());
+            }, reader -> reader.getZ() == null, Text.translatable("argument.entity.options.z.description"));
+            putOption("dx", reader -> {
+                reader.setLocalWorldOnly();
+                reader.setDx(reader.getReader().readDouble());
+            }, reader -> reader.getDx() == null, Text.translatable("argument.entity.options.dx.description"));
+            putOption("dy", reader -> {
+                reader.setLocalWorldOnly();
+                reader.setDy(reader.getReader().readDouble());
+            }, reader -> reader.getDy() == null, Text.translatable("argument.entity.options.dy.description"));
+            putOption("dz", reader -> {
+                reader.setLocalWorldOnly();
+                reader.setDz(reader.getReader().readDouble());
+            }, reader -> reader.getDz() == null, Text.translatable("argument.entity.options.dz.description"));
+            putOption(
+                "x_rotation",
+                reader -> reader.setPitchRange(FloatRangeArgument.parse(reader.getReader(), true, MathHelper::wrapDegrees)),
+                reader -> reader.getPitchRange() == FloatRangeArgument.ANY,
+                Text.translatable("argument.entity.options.x_rotation.description")
+            );
+            putOption(
+                "y_rotation",
+                reader -> reader.setYawRange(FloatRangeArgument.parse(reader.getReader(), true, MathHelper::wrapDegrees)),
+                reader -> reader.getYawRange() == FloatRangeArgument.ANY,
+                Text.translatable("argument.entity.options.y_rotation.description")
+            );
+            putOption("limit", reader -> {
+                int i = reader.getReader().getCursor();
+                int j = reader.getReader().readInt();
+                if (j < 1) {
+                    reader.getReader().setCursor(i);
+                    throw TOO_SMALL_LEVEL_EXCEPTION.createWithContext(reader.getReader());
+                } else {
+                    reader.setLimit(j);
+                    reader.setHasLimit(true);
+                }
+            }, reader -> !reader.isSenderOnly() && !reader.hasLimit(), Text.translatable("argument.entity.options.limit.description"));
+            putOption(
+                "sort",
+                reader -> {
+                    int i = reader.getReader().getCursor();
+                    String string = reader.getReader().readUnquotedString();
+                    reader.setSuggestionProvider(
+                        (builder, consumer) -> CommandSource.suggestMatching(Arrays.asList("nearest", "furthest", "random", "arbitrary"), builder)
+                    );
+
+                    reader.setSorter(switch (string) {
+                        case "nearest" -> CEntitySelectorReader.NEAREST;
+                        case "furthest" -> CEntitySelectorReader.FURTHEST;
+                        case "random" -> CEntitySelectorReader.RANDOM;
+                        case "arbitrary" -> CEntitySelector.ARBITRARY;
+                        default -> {
+                            reader.getReader().setCursor(i);
+                            throw IRREVERSIBLE_SORT_EXCEPTION.createWithContext(reader.getReader(), string);
+                        }
+                    });
+                    reader.setHasSorter(true);
+                },
+                reader -> !reader.isSenderOnly() && !reader.hasSorter(),
+                Text.translatable("argument.entity.options.sort.description")
+            );
+            putOption("gamemode", reader -> {
+                reader.setSuggestionProvider((builder, consumer) -> {
+                    String stringx = builder.getRemaining().toLowerCase(Locale.ROOT);
+                    boolean blx = !reader.excludesGameMode();
+                    boolean bl2 = true;
+                    if (!stringx.isEmpty()) {
+                        if (stringx.charAt(0) == '!') {
+                            blx = false;
+                            stringx = stringx.substring(1);
+                        } else {
+                            bl2 = false;
+                        }
+                    }
 
-                  for (Entry<String, NumberRange.IntRange> entry : map.entrySet()) {
-                     ScoreboardObjective lv2 = lvx.getNullableObjective(entry.getKey());
-                     if (lv2 == null) {
-                        return false;
-                     }
+                    for (GameMode lvx : GameMode.values()) {
+                        if (lvx.getName().toLowerCase(Locale.ROOT).startsWith(stringx)) {
+                            if (bl2) {
+                                builder.suggest("!" + lvx.getName());
+                            }
 
-                     ReadableScoreboardScore lv3 = lvx.getScore(entity, lv2);
-                     if (lv3 == null) {
+                            if (blx) {
+                                builder.suggest(lvx.getName());
+                            }
+                        }
+                    }
+
+                    return builder.buildFuture();
+                });
+                int i = reader.getReader().getCursor();
+                boolean bl = reader.readNegationCharacter();
+                if (reader.excludesGameMode() && !bl) {
+                    reader.getReader().setCursor(i);
+                    throw INAPPLICABLE_OPTION_EXCEPTION.createWithContext(reader.getReader(), "gamemode");
+                } else {
+                    String string = reader.getReader().readUnquotedString();
+                    GameMode lv = GameMode.byName(string, null);
+                    if (lv == null) {
+                        reader.getReader().setCursor(i);
+                        throw INVALID_MODE_EXCEPTION.createWithContext(reader.getReader(), string);
+                    } else {
+                        reader.setIncludesNonPlayers(false);
+                        reader.addPredicate(entity -> {
+                            if (!(entity instanceof ServerPlayerEntity)) {
+                                return false;
+                            } else {
+                                GameMode lvx = ((ServerPlayerEntity)entity).interactionManager.getGameMode();
+                                return bl ? lvx != lv : lvx == lv;
+                            }
+                        });
+                        if (bl) {
+                            reader.setExcludesGameMode(true);
+                        } else {
+                            reader.setSelectsGameMode(true);
+                        }
+                    }
+                }
+            }, reader -> !reader.selectsGameMode(), Text.translatable("argument.entity.options.gamemode.description"));
+            putOption("team", reader -> {
+                boolean bl = reader.readNegationCharacter();
+                String string = reader.getReader().readUnquotedString();
+                reader.addPredicate(entity -> {
+                    if (!(entity instanceof LivingEntity)) {
                         return false;
-                     }
+                    } else {
+                        AbstractTeam lv = entity.getScoreboardTeam();
+                        String string2 = lv == null ? "" : lv.getName();
+                        return string2.equals(string) != bl;
+                    }
+                });
+                if (bl) {
+                    reader.setExcludesTeam(true);
+                } else {
+                    reader.setSelectsTeam(true);
+                }
+            }, reader -> !reader.selectsTeam(), Text.translatable("argument.entity.options.team.description"));
+            putOption("type", reader -> {
+                reader.setSuggestionProvider((builder, consumer) -> {
+                    CommandSource.suggestIdentifiers(Registries.ENTITY_TYPE.getIds(), builder, String.valueOf('!'));
+                    CommandSource.suggestIdentifiers(Registries.ENTITY_TYPE.streamTags().map(TagKey::id), builder, "!#");
+                    if (!reader.excludesEntityType()) {
+                        CommandSource.suggestIdentifiers(Registries.ENTITY_TYPE.getIds(), builder);
+                        CommandSource.suggestIdentifiers(Registries.ENTITY_TYPE.streamTags().map(TagKey::id), builder, String.valueOf('#'));
+                    }
+
+                    return builder.buildFuture();
+                });
+                int i = reader.getReader().getCursor();
+                boolean bl = reader.readNegationCharacter();
+                if (reader.excludesEntityType() && !bl) {
+                    reader.getReader().setCursor(i);
+                    throw INAPPLICABLE_OPTION_EXCEPTION.createWithContext(reader.getReader(), "type");
+                } else {
+                    if (bl) {
+                        reader.setExcludesEntityType();
+                    }
+
+                    if (reader.readTagCharacter()) {
+                        TagKey<EntityType<?>> lv = TagKey.of(RegistryKeys.ENTITY_TYPE, Identifier.fromCommandInput(reader.getReader()));
+                        reader.addPredicate(entity -> entity.getType().isIn(lv) != bl);
+                    } else {
+                        Identifier lv2 = Identifier.fromCommandInput(reader.getReader());
+                        EntityType<?> lv3 = Registries.ENTITY_TYPE.getOrEmpty(lv2).orElseThrow(() -> {
+                            reader.getReader().setCursor(i);
+                            return INVALID_TYPE_EXCEPTION.createWithContext(reader.getReader(), lv2.toString());
+                        });
+                        if (Objects.equals(EntityType.PLAYER, lv3) && !bl) {
+                            reader.setIncludesNonPlayers(false);
+                        }
 
-                     if (!entry.getValue().test(lv3.getScore())) {
-                        return false;
-                     }
-                  }
+                        reader.addPredicate(entity -> Objects.equals(lv3, entity.getType()) != bl);
+                        if (!bl) {
+                            reader.setEntityType(lv3);
+                        }
+                    }
+                }
+            }, reader -> !reader.selectsEntityType(), Text.translatable("argument.entity.options.type.description"));
+            putOption("tag", reader -> {
+                boolean bl = reader.readNegationCharacter();
+                String string = reader.getReader().readUnquotedString();
+                reader.addPredicate(entity -> "".equals(string) ? entity.getCommandTags().isEmpty() != bl : entity.getCommandTags().contains(string) != bl);
+            }, reader -> true, Text.translatable("argument.entity.options.tag.description"));
+            putOption("nbt", reader -> {
+                boolean bl = reader.readNegationCharacter();
+                NbtCompound lv = new StringNbtReader(reader.getReader()).parseCompound();
+                reader.addPredicate(entity -> {
+                    NbtCompound lvx = entity.writeNbt(new NbtCompound());
+                    if (entity instanceof ServerPlayerEntity lv2) {
+                        ItemStack lv3 = lv2.getInventory().getMainHandStack();
+                        if (!lv3.isEmpty()) {
+                            lvx.put("SelectedItem", lv3.encode(lv2.getRegistryManager()));
+                        }
+                    }
+
+                    return NbtHelper.matches(lv, lvx, true) != bl;
+                });
+            }, reader -> true, Text.translatable("argument.entity.options.nbt.description"));
+            putOption("scores", reader -> {
+                StringReader stringReader = reader.getReader();
+                Map<String, NumberRange.IntRange> map = Maps.newHashMap();
+                stringReader.expect('{');
+                stringReader.skipWhitespace();
+
+                while (stringReader.canRead() && stringReader.peek() != '}') {
+                    stringReader.skipWhitespace();
+                    String string = stringReader.readUnquotedString();
+                    stringReader.skipWhitespace();
+                    stringReader.expect('=');
+                    stringReader.skipWhitespace();
+                    NumberRange.IntRange lv = NumberRange.IntRange.parse(stringReader);
+                    map.put(string, lv);
+                    stringReader.skipWhitespace();
+                    if (stringReader.canRead() && stringReader.peek() == ',') {
+                        stringReader.skip();
+                    }
+                }
+
+                stringReader.expect('}');
+                if (!map.isEmpty()) {
+                    reader.addPredicate(entity -> {
+                        Scoreboard lvx = entity.getServer().getScoreboard();
+
+                        for (Entry<String, NumberRange.IntRange> entry : map.entrySet()) {
+                            ScoreboardObjective lv2 = lvx.getNullableObjective(entry.getKey());
+                            if (lv2 == null) {
+                                return false;
+                            }
+
+                            ReadableScoreboardScore lv3 = lvx.getScore(entity, lv2);
+                            if (lv3 == null) {
+                                return false;
+                            }
+
+                            if (!entry.getValue().test(lv3.getScore())) {
+                                return false;
+                            }
+                        }
 
-                  return true;
-               });
-            }
+                        return true;
+                    });
+                }
+
+                reader.setSelectsScores(true);
+            }, reader -> !reader.selectsScores(), Text.translatable("argument.entity.options.scores.description"));
+            putOption("advancements", reader -> {
+                StringReader stringReader = reader.getReader();
+                Map<Identifier, Predicate<AdvancementProgress>> map = Maps.newHashMap();
+                stringReader.expect('{');
+                stringReader.skipWhitespace();
+
+                while (stringReader.canRead() && stringReader.peek() != '}') {
+                    stringReader.skipWhitespace();
+                    Identifier lv = Identifier.fromCommandInput(stringReader);
+                    stringReader.skipWhitespace();
+                    stringReader.expect('=');
+                    stringReader.skipWhitespace();
+                    if (stringReader.canRead() && stringReader.peek() == '{') {
+                        Map<String, Predicate<CriterionProgress>> map2 = Maps.newHashMap();
+                        stringReader.skipWhitespace();
+                        stringReader.expect('{');
+                        stringReader.skipWhitespace();
+
+                        while (stringReader.canRead() && stringReader.peek() != '}') {
+                            stringReader.skipWhitespace();
+                            String string = stringReader.readUnquotedString();
+                            stringReader.skipWhitespace();
+                            stringReader.expect('=');
+                            stringReader.skipWhitespace();
+                            boolean bl = stringReader.readBoolean();
+                            map2.put(string, criterionProgress -> criterionProgress.isObtained() == bl);
+                            stringReader.skipWhitespace();
+                            if (stringReader.canRead() && stringReader.peek() == ',') {
+                                stringReader.skip();
+                            }
+                        }
 
-            reader.setSelectsScores(true);
-         }, reader -> !reader.selectsScores(), Text.translatable("argument.entity.options.scores.description"));
-         putOption("advancements", reader -> {
-            StringReader stringReader = reader.getReader();
-            Map<Identifier, Predicate<AdvancementProgress>> map = Maps.newHashMap();
-            stringReader.expect('{');
-            stringReader.skipWhitespace();
-
-            while (stringReader.canRead() && stringReader.peek() != '}') {
-               stringReader.skipWhitespace();
-               Identifier lv = Identifier.fromCommandInput(stringReader);
-               stringReader.skipWhitespace();
-               stringReader.expect('=');
-               stringReader.skipWhitespace();
-               if (stringReader.canRead() && stringReader.peek() == '{') {
-                  Map<String, Predicate<CriterionProgress>> map2 = Maps.newHashMap();
-                  stringReader.skipWhitespace();
-                  stringReader.expect('{');
-                  stringReader.skipWhitespace();
-
-                  while (stringReader.canRead() && stringReader.peek() != '}') {
-                     stringReader.skipWhitespace();
-                     String string = stringReader.readUnquotedString();
-                     stringReader.skipWhitespace();
-                     stringReader.expect('=');
-                     stringReader.skipWhitespace();
-                     boolean bl = stringReader.readBoolean();
-                     map2.put(string, criterionProgress -> criterionProgress.isObtained() == bl);
-                     stringReader.skipWhitespace();
-                     if (stringReader.canRead() && stringReader.peek() == ',') {
+                        stringReader.skipWhitespace();
+                        stringReader.expect('}');
+                        stringReader.skipWhitespace();
+                        map.put(lv, advancementProgress -> {
+                            for (Entry<String, Predicate<CriterionProgress>> entry : map2.entrySet()) {
+                                CriterionProgress lvx = advancementProgress.getCriterionProgress(entry.getKey());
+                                if (lvx == null || !entry.getValue().test(lvx)) {
+                                    return false;
+                                }
+                            }
+
+                            return true;
+                        });
+                    } else {
+                        boolean bl2 = stringReader.readBoolean();
+                        map.put(lv, advancementProgress -> advancementProgress.isDone() == bl2);
+                    }
+
+                    stringReader.skipWhitespace();
+                    if (stringReader.canRead() && stringReader.peek() == ',') {
                         stringReader.skip();
-                     }
-                  }
-
-                  stringReader.skipWhitespace();
-                  stringReader.expect('}');
-                  stringReader.skipWhitespace();
-                  map.put(lv, advancementProgress -> {
-                     for (Entry<String, Predicate<CriterionProgress>> entry : map2.entrySet()) {
-                        CriterionProgress lvx = advancementProgress.getCriterionProgress(entry.getKey());
-                        if (lvx == null || !entry.getValue().test(lvx)) {
-                           return false;
+                    }
+                }
+
+                stringReader.expect('}');
+                if (!map.isEmpty()) {
+                    reader.addPredicate(entity -> {
+                        if (!(entity instanceof ServerPlayerEntity lvx)) {
+                            return false;
+                        } else {
+                            PlayerAdvancementTracker lv2 = lvx.getAdvancementTracker();
+                            ServerAdvancementLoader lv3 = lvx.getServer().getAdvancementLoader();
+
+                            for (Entry<Identifier, Predicate<AdvancementProgress>> entry : map.entrySet()) {
+                                AdvancementEntry lv4 = lv3.get(entry.getKey());
+                                if (lv4 == null || !entry.getValue().test(lv2.getProgress(lv4))) {
+                                    return false;
+                                }
+                            }
+
+                            return true;
                         }
-                     }
-
-                     return true;
-                  });
-               } else {
-                  boolean bl2 = stringReader.readBoolean();
-                  map.put(lv, advancementProgress -> advancementProgress.isDone() == bl2);
-               }
-
-               stringReader.skipWhitespace();
-               if (stringReader.canRead() && stringReader.peek() == ',') {
-                  stringReader.skip();
-               }
+                    });
+                    reader.setIncludesNonPlayers(false);
+                }
+
+                reader.setSelectsAdvancements(true);
+            }, reader -> !reader.selectsAdvancements(), Text.translatable("argument.entity.options.advancements.description"));
+            putOption(
+                "predicate",
+                reader -> {
+                    boolean bl = reader.readNegationCharacter();
+                    RegistryKey<LootCondition> lv = RegistryKey.of(RegistryKeys.PREDICATE, Identifier.fromCommandInput(reader.getReader()));
+                    reader.addPredicate(
+                        entity -> {
+                            if (!(entity.getWorld() instanceof ServerWorld)) {
+                                return false;
+                            } else {
+                                ServerWorld lvx = (ServerWorld)entity.getWorld();
+                                Optional<LootCondition> optional = lvx.getServer()
+                                    .getReloadableRegistries()
+                                    .createRegistryLookup()
+                                    .getOptionalEntry(RegistryKeys.PREDICATE, lv)
+                                    .map(RegistryEntry::value);
+                                if (optional.isEmpty()) {
+                                    return false;
+                                } else {
+                                    LootContextParameterSet lv2 = new LootContextParameterSet.Builder(lvx)
+                                        .add(LootContextParameters.THIS_ENTITY, entity)
+                                        .add(LootContextParameters.ORIGIN, entity.getPos())
+                                        .build(LootContextTypes.SELECTOR);
+                                    LootContext lv3 = new LootContext.Builder(lv2).build(Optional.empty());
+                                    lv3.markActive(LootContext.predicate(optional.get()));
+                                    return bl ^ optional.get().test(lv3);
+                                }
+                            }
+                        }
+                    );
+                },
+                reader -> true,
+                Text.translatable("argument.entity.options.predicate.description")
+            );
+        }
+    }
+
+    public static CEntitySelectorOptions.SelectorHandler getHandler(CEntitySelectorReader reader, String option, int restoreCursor) throws CommandSyntaxException {
+        CEntitySelectorOptions.SelectorOption lv = OPTIONS.get(option);
+        if (lv != null) {
+            if (lv.condition.test(reader)) {
+                return lv.handler;
+            } else {
+                throw INAPPLICABLE_OPTION_EXCEPTION.createWithContext(reader.getReader(), option);
             }
-
-            stringReader.expect('}');
-            if (!map.isEmpty()) {
-               reader.setPredicate(entity -> false); // no support
-               reader.setIncludesNonPlayers(false);
+        } else {
+            reader.getReader().setCursor(restoreCursor);
+            throw UNKNOWN_OPTION_EXCEPTION.createWithContext(reader.getReader(), option);
+        }
+    }
+
+    public static void suggestOptions(CEntitySelectorReader reader, SuggestionsBuilder suggestionBuilder) {
+        String string = suggestionBuilder.getRemaining().toLowerCase(Locale.ROOT);
+
+        for (Entry<String, CEntitySelectorOptions.SelectorOption> entry : OPTIONS.entrySet()) {
+            if (entry.getValue().condition.test(reader) && entry.getKey().toLowerCase(Locale.ROOT).startsWith(string)) {
+                suggestionBuilder.suggest(entry.getKey() + "=", entry.getValue().description);
             }
+        }
+    }
+
+    public interface SelectorHandler {
+        void handle(CEntitySelectorReader reader) throws CommandSyntaxException;
+    }
 
-            reader.setSelectsAdvancements(true);
-         }, reader -> !reader.selectsAdvancements(), Text.translatable("argument.entity.options.advancements.description"));
-         putOption(
-            "predicate",
-            reader -> {
-               reader.setPredicate(entity -> false); // no support
-            },
-            reader -> true,
-            Text.translatable("argument.entity.options.predicate.description")
-         );
-      }
-   }
-
-   public static CEntitySelectorOptions.SelectorHandler getHandler(CEntitySelectorReader reader, String option, int restoreCursor) throws CommandSyntaxException {
-      CEntitySelectorOptions.SelectorOption lv = OPTIONS.get(option);
-      if (lv != null) {
-         if (lv.condition.test(reader)) {
-            return lv.handler;
-         } else {
-            throw INAPPLICABLE_OPTION_EXCEPTION.createWithContext(reader.getReader(), option);
-         }
-      } else {
-         reader.getReader().setCursor(restoreCursor);
-         throw UNKNOWN_OPTION_EXCEPTION.createWithContext(reader.getReader(), option);
-      }
-   }
-
-   public static void suggestOptions(CEntitySelectorReader reader, SuggestionsBuilder suggestionBuilder) {
-      String string = suggestionBuilder.getRemaining().toLowerCase(Locale.ROOT);
-
-      for (Entry<String, CEntitySelectorOptions.SelectorOption> entry : OPTIONS.entrySet()) {
-         if (entry.getValue().condition.test(reader) && entry.getKey().toLowerCase(Locale.ROOT).startsWith(string)) {
-            suggestionBuilder.suggest(entry.getKey() + "=", entry.getValue().description);
-         }
-      }
-   }
-
-   public interface SelectorHandler {
-      void handle(CEntitySelectorReader reader) throws CommandSyntaxException;
-   }
-
-   static record SelectorOption(CEntitySelectorOptions.SelectorHandler handler, Predicate<CEntitySelectorReader> condition, Text description) {
-   }
+    static record SelectorOption(CEntitySelectorOptions.SelectorHandler handler, Predicate<CEntitySelectorReader> condition, Text description) {
+    }
 }
diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorReader.java b/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorReader.java
index c978d6e..14c4b7a 100644
--- a/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorReader.java
+++ b/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorReader.java
@@ -7,6 +7,7 @@ import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
 import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
 import com.mojang.brigadier.suggestion.Suggestions;
 import com.mojang.brigadier.suggestion.SuggestionsBuilder;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
@@ -17,610 +18,634 @@ import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.function.ToDoubleFunction;
-
-import net.minecraft.command.FloatRangeArgument;
 import net.minecraft.entity.Entity;
 import net.minecraft.entity.EntityType;
 import net.minecraft.predicate.NumberRange;
+import net.minecraft.server.network.ServerPlayerEntity;
 import net.minecraft.text.Text;
 import net.minecraft.util.math.Box;
 import net.minecraft.util.math.MathHelper;
 import net.minecraft.util.math.Vec3d;
+import net.minecraft.command.FloatRangeArgument;
 import org.jetbrains.annotations.Nullable;
 
 public class CEntitySelectorReader {
-   public static final char SELECTOR_PREFIX = '@';
-   private static final char ARGUMENTS_OPENING = '[';
-   private static final char ARGUMENTS_CLOSING = ']';
-   public static final char ARGUMENT_DEFINER = '=';
-   private static final char ARGUMENT_SEPARATOR = ',';
-   public static final char INVERT_MODIFIER = '!';
-   public static final char TAG_MODIFIER = '#';
-   private static final char NEAREST_PLAYER = 'p';
-   private static final char ALL_PLAYERS = 'a';
-   private static final char RANDOM_PLAYER = 'r';
-   private static final char SELF = 's';
-   private static final char ALL_ENTITIES = 'e';
-   public static final SimpleCommandExceptionType INVALID_ENTITY_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("argument.entity.invalid"));
-   public static final DynamicCommandExceptionType UNKNOWN_SELECTOR_EXCEPTION = new DynamicCommandExceptionType(
-      selectorType -> Text.stringifiedTranslatable("argument.entity.selector.unknown", selectorType)
-   );
-   public static final SimpleCommandExceptionType NOT_ALLOWED_EXCEPTION = new SimpleCommandExceptionType(
-      Text.translatable("argument.entity.selector.not_allowed")
-   );
-   public static final SimpleCommandExceptionType MISSING_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("argument.entity.selector.missing"));
-   public static final SimpleCommandExceptionType UNTERMINATED_EXCEPTION = new SimpleCommandExceptionType(
-      Text.translatable("argument.entity.options.unterminated")
-   );
-   public static final DynamicCommandExceptionType VALUELESS_EXCEPTION = new DynamicCommandExceptionType(
-      option -> Text.stringifiedTranslatable("argument.entity.options.valueless", option)
-   );
-   public static final BiConsumer<Vec3d, List<? extends Entity>> NEAREST = (pos, entities) -> entities.sort(
-         (entity1, entity2) -> Doubles.compare(entity1.squaredDistanceTo(pos), entity2.squaredDistanceTo(pos))
-      );
-   public static final BiConsumer<Vec3d, List<? extends Entity>> FURTHEST = (pos, entities) -> entities.sort(
-         (entity1, entity2) -> Doubles.compare(entity2.squaredDistanceTo(pos), entity1.squaredDistanceTo(pos))
-      );
-   public static final BiConsumer<Vec3d, List<? extends Entity>> RANDOM = (pos, entities) -> Collections.shuffle(entities);
-   public static final BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> DEFAULT_SUGGESTION_PROVIDER = (builder, consumer) -> builder.buildFuture();
-   private final StringReader reader;
-   private final boolean atAllowed;
-   private int limit;
-   private boolean includesNonPlayers;
-   private boolean localWorldOnly;
-   private NumberRange.DoubleRange distance = NumberRange.DoubleRange.ANY;
-   private NumberRange.IntRange levelRange = NumberRange.IntRange.ANY;
-   @Nullable
-   private Double x;
-   @Nullable
-   private Double y;
-   @Nullable
-   private Double z;
-   @Nullable
-   private Double dx;
-   @Nullable
-   private Double dy;
-   @Nullable
-   private Double dz;
-   private FloatRangeArgument pitchRange = FloatRangeArgument.ANY;
-   private FloatRangeArgument yawRange = FloatRangeArgument.ANY;
-   private Predicate<Entity> predicate = entity -> true;
-   private BiConsumer<Vec3d, List<? extends Entity>> sorter = CEntitySelector.ARBITRARY;
-   private boolean senderOnly;
-   @Nullable
-   private String playerName;
-   private int startCursor;
-   @Nullable
-   private UUID uuid;
-   private BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> suggestionProvider = DEFAULT_SUGGESTION_PROVIDER;
-   private boolean selectsName;
-   private boolean excludesName;
-   private boolean hasLimit;
-   private boolean hasSorter;
-   private boolean selectsGameMode;
-   private boolean excludesGameMode;
-   private boolean selectsTeam;
-   private boolean excludesTeam;
-   @Nullable
-   private EntityType<?> entityType;
-   private boolean excludesEntityType;
-   private boolean selectsScores;
-   private boolean selectsAdvancements;
-   private boolean usesAt;
-
-   public CEntitySelectorReader(StringReader reader) {
-      this(reader, true);
-   }
-
-   public CEntitySelectorReader(StringReader reader, boolean atAllowed) {
-      this.reader = reader;
-      this.atAllowed = atAllowed;
-   }
-
-   public CEntitySelector build() {
-      Box lv;
-      if (this.dx == null && this.dy == null && this.dz == null) {
-         if (this.distance.max().isPresent()) {
-            double d = this.distance.max().get();
-            lv = new Box(-d, -d, -d, d + 1.0, d + 1.0, d + 1.0);
-         } else {
-            lv = null;
-         }
-      } else {
-         lv = this.createBox(this.dx == null ? 0.0 : this.dx, this.dy == null ? 0.0 : this.dy, this.dz == null ? 0.0 : this.dz);
-      }
-
-      Function<Vec3d, Vec3d> function;
-      if (this.x == null && this.y == null && this.z == null) {
-         function = pos -> pos;
-      } else {
-         function = pos -> new Vec3d(this.x == null ? pos.x : this.x, this.y == null ? pos.y : this.y, this.z == null ? pos.z : this.z);
-      }
-
-      return new CEntitySelector(
-         this.limit,
-         this.includesNonPlayers,
-         this.localWorldOnly,
-         this.predicate,
-         this.distance,
-         function,
-         lv,
-         this.sorter,
-         this.senderOnly,
-         this.playerName,
-         this.uuid,
-         this.entityType,
-         this.usesAt
-      );
-   }
-
-   private Box createBox(double x, double y, double z) {
-      boolean bl = x < 0.0;
-      boolean bl2 = y < 0.0;
-      boolean bl3 = z < 0.0;
-      double g = bl ? x : 0.0;
-      double h = bl2 ? y : 0.0;
-      double i = bl3 ? z : 0.0;
-      double j = (bl ? 0.0 : x) + 1.0;
-      double k = (bl2 ? 0.0 : y) + 1.0;
-      double l = (bl3 ? 0.0 : z) + 1.0;
-      return new Box(g, h, i, j, k, l);
-   }
-
-   private void buildPredicate() {
-      if (this.pitchRange != FloatRangeArgument.ANY) {
-         this.predicate = this.predicate.and(this.rotationPredicate(this.pitchRange, Entity::getPitch));
-      }
-
-      if (this.yawRange != FloatRangeArgument.ANY) {
-         this.predicate = this.predicate.and(this.rotationPredicate(this.yawRange, Entity::getYaw));
-      }
-
-      if (!this.levelRange.isDummy()) {
-         this.predicate = this.predicate
-            .and(entity -> false); // no support for the xp predicate
-      }
-   }
-
-   private Predicate<Entity> rotationPredicate(FloatRangeArgument angleRange, ToDoubleFunction<Entity> entityToAngle) {
-      double d = (double)MathHelper.wrapDegrees(angleRange.min() == null ? 0.0F : angleRange.min());
-      double e = (double)MathHelper.wrapDegrees(angleRange.max() == null ? 359.0F : angleRange.max());
-      return entity -> {
-         double f = MathHelper.wrapDegrees(entityToAngle.applyAsDouble(entity));
-         return d > e ? f >= d || f <= e : f >= d && f <= e;
-      };
-   }
-
-   protected void readAtVariable() throws CommandSyntaxException {
-      this.usesAt = true;
-      this.suggestionProvider = this::suggestSelectorRest;
-      if (!this.reader.canRead()) {
-         throw MISSING_EXCEPTION.createWithContext(this.reader);
-      } else {
-         int i = this.reader.getCursor();
-         char c = this.reader.read();
-         if (c == 'p') {
-            this.limit = 1;
-            this.includesNonPlayers = false;
-            this.sorter = NEAREST;
-            this.setEntityType(EntityType.PLAYER);
-         } else if (c == 'a') {
-            this.limit = Integer.MAX_VALUE;
-            this.includesNonPlayers = false;
-            this.sorter = CEntitySelector.ARBITRARY;
-            this.setEntityType(EntityType.PLAYER);
-         } else if (c == 'r') {
-            this.limit = 1;
-            this.includesNonPlayers = false;
-            this.sorter = RANDOM;
-            this.setEntityType(EntityType.PLAYER);
-         } else if (c == 's') {
-            this.limit = 1;
-            this.includesNonPlayers = true;
-            this.senderOnly = true;
-         } else {
-            if (c != 'e') {
-               this.reader.setCursor(i);
-               throw UNKNOWN_SELECTOR_EXCEPTION.createWithContext(this.reader, "@" + c);
+    public static final char SELECTOR_PREFIX = '@';
+    private static final char ARGUMENTS_OPENING = '[';
+    private static final char ARGUMENTS_CLOSING = ']';
+    public static final char ARGUMENT_DEFINER = '=';
+    private static final char ARGUMENT_SEPARATOR = ',';
+    public static final char INVERT_MODIFIER = '!';
+    public static final char TAG_MODIFIER = '#';
+    private static final char NEAREST_PLAYER = 'p';
+    private static final char ALL_PLAYERS = 'a';
+    private static final char RANDOM_PLAYER = 'r';
+    private static final char SELF = 's';
+    private static final char ALL_ENTITIES = 'e';
+    private static final char NEAREST_ENTITY = 'n';
+    public static final SimpleCommandExceptionType INVALID_ENTITY_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("argument.entity.invalid"));
+    public static final DynamicCommandExceptionType UNKNOWN_SELECTOR_EXCEPTION = new DynamicCommandExceptionType(
+        selectorType -> Text.stringifiedTranslatable("argument.entity.selector.unknown", selectorType)
+    );
+    public static final SimpleCommandExceptionType NOT_ALLOWED_EXCEPTION = new SimpleCommandExceptionType(
+        Text.translatable("argument.entity.selector.not_allowed")
+    );
+    public static final SimpleCommandExceptionType MISSING_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("argument.entity.selector.missing"));
+    public static final SimpleCommandExceptionType UNTERMINATED_EXCEPTION = new SimpleCommandExceptionType(
+        Text.translatable("argument.entity.options.unterminated")
+    );
+    public static final DynamicCommandExceptionType VALUELESS_EXCEPTION = new DynamicCommandExceptionType(
+        option -> Text.stringifiedTranslatable("argument.entity.options.valueless", option)
+    );
+    public static final BiConsumer<Vec3d, List<? extends Entity>> NEAREST = (pos, entities) -> entities.sort(
+        (entity1, entity2) -> Doubles.compare(entity1.squaredDistanceTo(pos), entity2.squaredDistanceTo(pos))
+    );
+    public static final BiConsumer<Vec3d, List<? extends Entity>> FURTHEST = (pos, entities) -> entities.sort(
+        (entity1, entity2) -> Doubles.compare(entity2.squaredDistanceTo(pos), entity1.squaredDistanceTo(pos))
+    );
+    public static final BiConsumer<Vec3d, List<? extends Entity>> RANDOM = (pos, entities) -> Collections.shuffle(entities);
+    public static final BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> DEFAULT_SUGGESTION_PROVIDER = (builder, consumer) -> builder.buildFuture();
+    private final StringReader reader;
+    private final boolean atAllowed;
+    private int limit;
+    private boolean includesNonPlayers;
+    private boolean localWorldOnly;
+    private NumberRange.DoubleRange distance = NumberRange.DoubleRange.ANY;
+    private NumberRange.IntRange levelRange = NumberRange.IntRange.ANY;
+    @Nullable
+    private Double x;
+    @Nullable
+    private Double y;
+    @Nullable
+    private Double z;
+    @Nullable
+    private Double dx;
+    @Nullable
+    private Double dy;
+    @Nullable
+    private Double dz;
+    private FloatRangeArgument pitchRange = FloatRangeArgument.ANY;
+    private FloatRangeArgument yawRange = FloatRangeArgument.ANY;
+    private final List<Predicate<Entity>> predicates = new ArrayList<>();
+    private BiConsumer<Vec3d, List<? extends Entity>> sorter = CEntitySelector.ARBITRARY;
+    private boolean senderOnly;
+    @Nullable
+    private String playerName;
+    private int startCursor;
+    @Nullable
+    private UUID uuid;
+    private BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> suggestionProvider = DEFAULT_SUGGESTION_PROVIDER;
+    private boolean selectsName;
+    private boolean excludesName;
+    private boolean hasLimit;
+    private boolean hasSorter;
+    private boolean selectsGameMode;
+    private boolean excludesGameMode;
+    private boolean selectsTeam;
+    private boolean excludesTeam;
+    @Nullable
+    private EntityType<?> entityType;
+    private boolean excludesEntityType;
+    private boolean selectsScores;
+    private boolean selectsAdvancements;
+    private boolean usesAt;
+
+    public CEntitySelectorReader(StringReader reader, boolean atAllowed) {
+        this.reader = reader;
+        this.atAllowed = atAllowed;
+    }
+
+    public static <S> boolean shouldAllowAtSelectors(S source) {
+        /*if (source instanceof CommandSource lv && lv.hasPermissionLevel(2)) {
+            return true;
+        }
+
+        return false;*/
+        return true;
+    }
+
+    public CEntitySelector build() {
+        Box lv;
+        if (this.dx == null && this.dy == null && this.dz == null) {
+            if (this.distance.max().isPresent()) {
+                double d = this.distance.max().get();
+                lv = new Box(-d, -d, -d, d + 1.0, d + 1.0, d + 1.0);
+            } else {
+                lv = null;
+            }
+        } else {
+            lv = this.createBox(this.dx == null ? 0.0 : this.dx, this.dy == null ? 0.0 : this.dy, this.dz == null ? 0.0 : this.dz);
+        }
+
+        Function<Vec3d, Vec3d> function;
+        if (this.x == null && this.y == null && this.z == null) {
+            function = pos -> pos;
+        } else {
+            function = pos -> new Vec3d(this.x == null ? pos.x : this.x, this.y == null ? pos.y : this.y, this.z == null ? pos.z : this.z);
+        }
+
+        return new CEntitySelector(
+            this.limit,
+            this.includesNonPlayers,
+            this.localWorldOnly,
+            List.copyOf(this.predicates),
+            this.distance,
+            function,
+            lv,
+            this.sorter,
+            this.senderOnly,
+            this.playerName,
+            this.uuid,
+            this.entityType,
+            this.usesAt
+        );
+    }
+
+    private Box createBox(double x, double y, double z) {
+        boolean bl = x < 0.0;
+        boolean bl2 = y < 0.0;
+        boolean bl3 = z < 0.0;
+        double g = bl ? x : 0.0;
+        double h = bl2 ? y : 0.0;
+        double i = bl3 ? z : 0.0;
+        double j = (bl ? 0.0 : x) + 1.0;
+        double k = (bl2 ? 0.0 : y) + 1.0;
+        double l = (bl3 ? 0.0 : z) + 1.0;
+        return new Box(g, h, i, j, k, l);
+    }
+
+    private void buildPredicate() {
+        if (this.pitchRange != FloatRangeArgument.ANY) {
+            this.predicates.add(this.rotationPredicate(this.pitchRange, Entity::getPitch));
+        }
+
+        if (this.yawRange != FloatRangeArgument.ANY) {
+            this.predicates.add(this.rotationPredicate(this.yawRange, Entity::getYaw));
+        }
+
+        if (!this.levelRange.isDummy()) {
+            this.predicates.add(entity -> !(entity instanceof ServerPlayerEntity) ? false : this.levelRange.test(((ServerPlayerEntity)entity).experienceLevel));
+        }
+    }
+
+    private Predicate<Entity> rotationPredicate(FloatRangeArgument angleRange, ToDoubleFunction<Entity> entityToAngle) {
+        double d = (double)MathHelper.wrapDegrees(angleRange.min() == null ? 0.0F : angleRange.min());
+        double e = (double)MathHelper.wrapDegrees(angleRange.max() == null ? 359.0F : angleRange.max());
+        return entity -> {
+            double f = MathHelper.wrapDegrees(entityToAngle.applyAsDouble(entity));
+            return d > e ? f >= d || f <= e : f >= d && f <= e;
+        };
+    }
+
+    protected void readAtVariable() throws CommandSyntaxException {
+        this.usesAt = true;
+        this.suggestionProvider = this::suggestSelectorRest;
+        if (!this.reader.canRead()) {
+            throw MISSING_EXCEPTION.createWithContext(this.reader);
+        } else {
+            int i = this.reader.getCursor();
+            char c = this.reader.read();
+
+            if (switch (c) {
+                case 'a' -> {
+                    this.limit = Integer.MAX_VALUE;
+                    this.includesNonPlayers = false;
+                    this.sorter = CEntitySelector.ARBITRARY;
+                    this.setEntityType(EntityType.PLAYER);
+                    yield false;
+                }
+                default -> {
+                    this.reader.setCursor(i);
+                    throw UNKNOWN_SELECTOR_EXCEPTION.createWithContext(this.reader, "@" + c);
+                }
+                case 'e' -> {
+                    this.limit = Integer.MAX_VALUE;
+                    this.includesNonPlayers = true;
+                    this.sorter = CEntitySelector.ARBITRARY;
+                    yield true;
+                }
+                case 'n' -> {
+                    this.limit = 1;
+                    this.includesNonPlayers = true;
+                    this.sorter = NEAREST;
+                    yield true;
+                }
+                case 'p' -> {
+                    this.limit = 1;
+                    this.includesNonPlayers = false;
+                    this.sorter = NEAREST;
+                    this.setEntityType(EntityType.PLAYER);
+                    yield false;
+                }
+                case 'r' -> {
+                    this.limit = 1;
+                    this.includesNonPlayers = false;
+                    this.sorter = RANDOM;
+                    this.setEntityType(EntityType.PLAYER);
+                    yield false;
+                }
+                case 's' -> {
+                    this.limit = 1;
+                    this.includesNonPlayers = true;
+                    this.senderOnly = true;
+                    yield false;
+                }
+            }) {
+                this.predicates.add(Entity::isAlive);
             }
 
-            this.limit = Integer.MAX_VALUE;
+            this.suggestionProvider = this::suggestOpen;
+            if (this.reader.canRead() && this.reader.peek() == '[') {
+                this.reader.skip();
+                this.suggestionProvider = this::suggestOptionOrEnd;
+                this.readArguments();
+            }
+        }
+    }
+
+    protected void readRegular() throws CommandSyntaxException {
+        if (this.reader.canRead()) {
+            this.suggestionProvider = this::suggestNormal;
+        }
+
+        int i = this.reader.getCursor();
+        String string = this.reader.readString();
+
+        try {
+            this.uuid = UUID.fromString(string);
             this.includesNonPlayers = true;
-            this.sorter = CEntitySelector.ARBITRARY;
-            this.predicate = Entity::isAlive;
-         }
+        } catch (IllegalArgumentException var4) {
+            if (string.isEmpty() || string.length() > 16) {
+                this.reader.setCursor(i);
+                throw INVALID_ENTITY_EXCEPTION.createWithContext(this.reader);
+            }
+
+            this.includesNonPlayers = false;
+            this.playerName = string;
+        }
+
+        this.limit = 1;
+    }
+
+    protected void readArguments() throws CommandSyntaxException {
+        this.suggestionProvider = this::suggestOption;
+        this.reader.skipWhitespace();
+
+        while (this.reader.canRead() && this.reader.peek() != ']') {
+            this.reader.skipWhitespace();
+            int i = this.reader.getCursor();
+            String string = this.reader.readString();
+            CEntitySelectorOptions.SelectorHandler lv = CEntitySelectorOptions.getHandler(this, string, i);
+            this.reader.skipWhitespace();
+            if (!this.reader.canRead() || this.reader.peek() != '=') {
+                this.reader.setCursor(i);
+                throw VALUELESS_EXCEPTION.createWithContext(this.reader, string);
+            }
 
-         this.suggestionProvider = this::suggestOpen;
-         if (this.reader.canRead() && this.reader.peek() == '[') {
             this.reader.skip();
-            this.suggestionProvider = this::suggestOptionOrEnd;
-            this.readArguments();
-         }
-      }
-   }
-
-   protected void readRegular() throws CommandSyntaxException {
-      if (this.reader.canRead()) {
-         this.suggestionProvider = this::suggestNormal;
-      }
-
-      int i = this.reader.getCursor();
-      String string = this.reader.readString();
-
-      try {
-         this.uuid = UUID.fromString(string);
-         this.includesNonPlayers = true;
-      } catch (IllegalArgumentException var4) {
-         if (string.isEmpty() || string.length() > 16) {
-            this.reader.setCursor(i);
-            throw INVALID_ENTITY_EXCEPTION.createWithContext(this.reader);
-         }
-
-         this.includesNonPlayers = false;
-         this.playerName = string;
-      }
-
-      this.limit = 1;
-   }
-
-   protected void readArguments() throws CommandSyntaxException {
-      this.suggestionProvider = this::suggestOption;
-      this.reader.skipWhitespace();
-
-      while (this.reader.canRead() && this.reader.peek() != ']') {
-         this.reader.skipWhitespace();
-         int i = this.reader.getCursor();
-         String string = this.reader.readString();
-         CEntitySelectorOptions.SelectorHandler lv = CEntitySelectorOptions.getHandler(this, string, i);
-         this.reader.skipWhitespace();
-         if (!this.reader.canRead() || this.reader.peek() != '=') {
-            this.reader.setCursor(i);
-            throw VALUELESS_EXCEPTION.createWithContext(this.reader, string);
-         }
-
-         this.reader.skip();
-         this.reader.skipWhitespace();
-         this.suggestionProvider = DEFAULT_SUGGESTION_PROVIDER;
-         lv.handle(this);
-         this.reader.skipWhitespace();
-         this.suggestionProvider = this::suggestEndNext;
-         if (this.reader.canRead()) {
-            if (this.reader.peek() != ',') {
-               if (this.reader.peek() != ']') {
-                  throw UNTERMINATED_EXCEPTION.createWithContext(this.reader);
-               }
-               break;
+            this.reader.skipWhitespace();
+            this.suggestionProvider = DEFAULT_SUGGESTION_PROVIDER;
+            lv.handle(this);
+            this.reader.skipWhitespace();
+            this.suggestionProvider = this::suggestEndNext;
+            if (this.reader.canRead()) {
+                if (this.reader.peek() != ',') {
+                    if (this.reader.peek() != ']') {
+                        throw UNTERMINATED_EXCEPTION.createWithContext(this.reader);
+                    }
+                    break;
+                }
+
+                this.reader.skip();
+                this.suggestionProvider = this::suggestOption;
+            }
+        }
+
+        if (this.reader.canRead()) {
+            this.reader.skip();
+            this.suggestionProvider = DEFAULT_SUGGESTION_PROVIDER;
+        } else {
+            throw UNTERMINATED_EXCEPTION.createWithContext(this.reader);
+        }
+    }
+
+    public boolean readNegationCharacter() {
+        this.reader.skipWhitespace();
+        if (this.reader.canRead() && this.reader.peek() == '!') {
+            this.reader.skip();
+            this.reader.skipWhitespace();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public boolean readTagCharacter() {
+        this.reader.skipWhitespace();
+        if (this.reader.canRead() && this.reader.peek() == '#') {
+            this.reader.skip();
+            this.reader.skipWhitespace();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public StringReader getReader() {
+        return this.reader;
+    }
+
+    public void addPredicate(Predicate<Entity> predicate) {
+        this.predicates.add(predicate);
+    }
+
+    public void setLocalWorldOnly() {
+        this.localWorldOnly = true;
+    }
+
+    public NumberRange.DoubleRange getDistance() {
+        return this.distance;
+    }
+
+    public void setDistance(NumberRange.DoubleRange distance) {
+        this.distance = distance;
+    }
+
+    public NumberRange.IntRange getLevelRange() {
+        return this.levelRange;
+    }
+
+    public void setLevelRange(NumberRange.IntRange levelRange) {
+        this.levelRange = levelRange;
+    }
+
+    public FloatRangeArgument getPitchRange() {
+        return this.pitchRange;
+    }
+
+    public void setPitchRange(FloatRangeArgument pitchRange) {
+        this.pitchRange = pitchRange;
+    }
+
+    public FloatRangeArgument getYawRange() {
+        return this.yawRange;
+    }
+
+    public void setYawRange(FloatRangeArgument yawRange) {
+        this.yawRange = yawRange;
+    }
+
+    @Nullable
+    public Double getX() {
+        return this.x;
+    }
+
+    @Nullable
+    public Double getY() {
+        return this.y;
+    }
+
+    @Nullable
+    public Double getZ() {
+        return this.z;
+    }
+
+    public void setX(double x) {
+        this.x = x;
+    }
+
+    public void setY(double y) {
+        this.y = y;
+    }
+
+    public void setZ(double z) {
+        this.z = z;
+    }
+
+    public void setDx(double dx) {
+        this.dx = dx;
+    }
+
+    public void setDy(double dy) {
+        this.dy = dy;
+    }
+
+    public void setDz(double dz) {
+        this.dz = dz;
+    }
+
+    @Nullable
+    public Double getDx() {
+        return this.dx;
+    }
+
+    @Nullable
+    public Double getDy() {
+        return this.dy;
+    }
+
+    @Nullable
+    public Double getDz() {
+        return this.dz;
+    }
+
+    public void setLimit(int limit) {
+        this.limit = limit;
+    }
+
+    public void setIncludesNonPlayers(boolean includesNonPlayers) {
+        this.includesNonPlayers = includesNonPlayers;
+    }
+
+    public BiConsumer<Vec3d, List<? extends Entity>> getSorter() {
+        return this.sorter;
+    }
+
+    public void setSorter(BiConsumer<Vec3d, List<? extends Entity>> sorter) {
+        this.sorter = sorter;
+    }
+
+    public CEntitySelector read() throws CommandSyntaxException {
+        this.startCursor = this.reader.getCursor();
+        this.suggestionProvider = this::suggestSelector;
+        if (this.reader.canRead() && this.reader.peek() == '@') {
+            if (!this.atAllowed) {
+                throw NOT_ALLOWED_EXCEPTION.createWithContext(this.reader);
             }
 
             this.reader.skip();
-            this.suggestionProvider = this::suggestOption;
-         }
-      }
-
-      if (this.reader.canRead()) {
-         this.reader.skip();
-         this.suggestionProvider = DEFAULT_SUGGESTION_PROVIDER;
-      } else {
-         throw UNTERMINATED_EXCEPTION.createWithContext(this.reader);
-      }
-   }
-
-   public boolean readNegationCharacter() {
-      this.reader.skipWhitespace();
-      if (this.reader.canRead() && this.reader.peek() == '!') {
-         this.reader.skip();
-         this.reader.skipWhitespace();
-         return true;
-      } else {
-         return false;
-      }
-   }
-
-   public boolean readTagCharacter() {
-      this.reader.skipWhitespace();
-      if (this.reader.canRead() && this.reader.peek() == '#') {
-         this.reader.skip();
-         this.reader.skipWhitespace();
-         return true;
-      } else {
-         return false;
-      }
-   }
-
-   public StringReader getReader() {
-      return this.reader;
-   }
-
-   public void setPredicate(Predicate<Entity> predicate) {
-      this.predicate = this.predicate.and(predicate);
-   }
-
-   public void setLocalWorldOnly() {
-      this.localWorldOnly = true;
-   }
-
-   public NumberRange.DoubleRange getDistance() {
-      return this.distance;
-   }
-
-   public void setDistance(NumberRange.DoubleRange distance) {
-      this.distance = distance;
-   }
-
-   public NumberRange.IntRange getLevelRange() {
-      return this.levelRange;
-   }
-
-   public void setLevelRange(NumberRange.IntRange levelRange) {
-      this.levelRange = levelRange;
-   }
-
-   public FloatRangeArgument getPitchRange() {
-      return this.pitchRange;
-   }
-
-   public void setPitchRange(FloatRangeArgument pitchRange) {
-      this.pitchRange = pitchRange;
-   }
-
-   public FloatRangeArgument getYawRange() {
-      return this.yawRange;
-   }
-
-   public void setYawRange(FloatRangeArgument yawRange) {
-      this.yawRange = yawRange;
-   }
-
-   @Nullable
-   public Double getX() {
-      return this.x;
-   }
-
-   @Nullable
-   public Double getY() {
-      return this.y;
-   }
-
-   @Nullable
-   public Double getZ() {
-      return this.z;
-   }
-
-   public void setX(double x) {
-      this.x = x;
-   }
-
-   public void setY(double y) {
-      this.y = y;
-   }
-
-   public void setZ(double z) {
-      this.z = z;
-   }
-
-   public void setDx(double dx) {
-      this.dx = dx;
-   }
-
-   public void setDy(double dy) {
-      this.dy = dy;
-   }
-
-   public void setDz(double dz) {
-      this.dz = dz;
-   }
-
-   @Nullable
-   public Double getDx() {
-      return this.dx;
-   }
-
-   @Nullable
-   public Double getDy() {
-      return this.dy;
-   }
-
-   @Nullable
-   public Double getDz() {
-      return this.dz;
-   }
-
-   public void setLimit(int limit) {
-      this.limit = limit;
-   }
-
-   public void setIncludesNonPlayers(boolean includesNonPlayers) {
-      this.includesNonPlayers = includesNonPlayers;
-   }
-
-   public BiConsumer<Vec3d, List<? extends Entity>> getSorter() {
-      return this.sorter;
-   }
-
-   public void setSorter(BiConsumer<Vec3d, List<? extends Entity>> sorter) {
-      this.sorter = sorter;
-   }
-
-   public CEntitySelector read() throws CommandSyntaxException {
-      this.startCursor = this.reader.getCursor();
-      this.suggestionProvider = this::suggestSelector;
-      if (this.reader.canRead() && this.reader.peek() == '@') {
-         if (!this.atAllowed) {
-            throw NOT_ALLOWED_EXCEPTION.createWithContext(this.reader);
-         }
-
-         this.reader.skip();
-         this.readAtVariable();
-      } else {
-         this.readRegular();
-      }
-
-      this.buildPredicate();
-      return this.build();
-   }
-
-   private static void suggestSelector(SuggestionsBuilder builder) {
-      builder.suggest("@p", Text.translatable("argument.entity.selector.nearestPlayer"));
-      builder.suggest("@a", Text.translatable("argument.entity.selector.allPlayers"));
-      builder.suggest("@r", Text.translatable("argument.entity.selector.randomPlayer"));
-      builder.suggest("@s", Text.translatable("argument.entity.selector.self"));
-      builder.suggest("@e", Text.translatable("argument.entity.selector.allEntities"));
-   }
-
-   private CompletableFuture<Suggestions> suggestSelector(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
-      consumer.accept(builder);
-      if (this.atAllowed) {
-         suggestSelector(builder);
-      }
-
-      return builder.buildFuture();
-   }
-
-   private CompletableFuture<Suggestions> suggestNormal(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
-      SuggestionsBuilder suggestionsBuilder2 = builder.createOffset(this.startCursor);
-      consumer.accept(suggestionsBuilder2);
-      return builder.add(suggestionsBuilder2).buildFuture();
-   }
-
-   private CompletableFuture<Suggestions> suggestSelectorRest(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
-      SuggestionsBuilder suggestionsBuilder2 = builder.createOffset(builder.getStart() - 1);
-      suggestSelector(suggestionsBuilder2);
-      builder.add(suggestionsBuilder2);
-      return builder.buildFuture();
-   }
-
-   private CompletableFuture<Suggestions> suggestOpen(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
-      builder.suggest(String.valueOf('['));
-      return builder.buildFuture();
-   }
-
-   private CompletableFuture<Suggestions> suggestOptionOrEnd(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
-      builder.suggest(String.valueOf(']'));
-      CEntitySelectorOptions.suggestOptions(this, builder);
-      return builder.buildFuture();
-   }
-
-   private CompletableFuture<Suggestions> suggestOption(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
-      CEntitySelectorOptions.suggestOptions(this, builder);
-      return builder.buildFuture();
-   }
-
-   private CompletableFuture<Suggestions> suggestEndNext(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
-      builder.suggest(String.valueOf(','));
-      builder.suggest(String.valueOf(']'));
-      return builder.buildFuture();
-   }
-
-   private CompletableFuture<Suggestions> suggestDefinerNext(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
-      builder.suggest(String.valueOf('='));
-      return builder.buildFuture();
-   }
-
-   public boolean isSenderOnly() {
-      return this.senderOnly;
-   }
-
-   public void setSuggestionProvider(BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> suggestionProvider) {
-      this.suggestionProvider = suggestionProvider;
-   }
-
-   public CompletableFuture<Suggestions> listSuggestions(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
-      return this.suggestionProvider.apply(builder.createOffset(this.reader.getCursor()), consumer);
-   }
-
-   public boolean selectsName() {
-      return this.selectsName;
-   }
-
-   public void setSelectsName(boolean selectsName) {
-      this.selectsName = selectsName;
-   }
-
-   public boolean excludesName() {
-      return this.excludesName;
-   }
-
-   public void setExcludesName(boolean excludesName) {
-      this.excludesName = excludesName;
-   }
-
-   public boolean hasLimit() {
-      return this.hasLimit;
-   }
-
-   public void setHasLimit(boolean hasLimit) {
-      this.hasLimit = hasLimit;
-   }
-
-   public boolean hasSorter() {
-      return this.hasSorter;
-   }
-
-   public void setHasSorter(boolean hasSorter) {
-      this.hasSorter = hasSorter;
-   }
-
-   public boolean selectsGameMode() {
-      return this.selectsGameMode;
-   }
-
-   public void setSelectsGameMode(boolean selectsGameMode) {
-      this.selectsGameMode = selectsGameMode;
-   }
-
-   public boolean excludesGameMode() {
-      return this.excludesGameMode;
-   }
-
-   public void setExcludesGameMode(boolean excludesGameMode) {
-      this.excludesGameMode = excludesGameMode;
-   }
-
-   public boolean selectsTeam() {
-      return this.selectsTeam;
-   }
-
-   public void setSelectsTeam(boolean selectsTeam) {
-      this.selectsTeam = selectsTeam;
-   }
-
-   public boolean excludesTeam() {
-      return this.excludesTeam;
-   }
-
-   public void setExcludesTeam(boolean excludesTeam) {
-      this.excludesTeam = excludesTeam;
-   }
-
-   public void setEntityType(EntityType<?> entityType) {
-      this.entityType = entityType;
-   }
-
-   public void setExcludesEntityType() {
-      this.excludesEntityType = true;
-   }
-
-   public boolean selectsEntityType() {
-      return this.entityType != null;
-   }
-
-   public boolean excludesEntityType() {
-      return this.excludesEntityType;
-   }
-
-   public boolean selectsScores() {
-      return this.selectsScores;
-   }
-
-   public void setSelectsScores(boolean selectsScores) {
-      this.selectsScores = selectsScores;
-   }
+            this.readAtVariable();
+        } else {
+            this.readRegular();
+        }
+
+        this.buildPredicate();
+        return this.build();
+    }
+
+    private static void suggestSelector(SuggestionsBuilder builder) {
+        builder.suggest("@p", Text.translatable("argument.entity.selector.nearestPlayer"));
+        builder.suggest("@a", Text.translatable("argument.entity.selector.allPlayers"));
+        builder.suggest("@r", Text.translatable("argument.entity.selector.randomPlayer"));
+        builder.suggest("@s", Text.translatable("argument.entity.selector.self"));
+        builder.suggest("@e", Text.translatable("argument.entity.selector.allEntities"));
+        builder.suggest("@n", Text.translatable("argument.entity.selector.nearestEntity"));
+    }
+
+    private CompletableFuture<Suggestions> suggestSelector(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+        consumer.accept(builder);
+        if (this.atAllowed) {
+            suggestSelector(builder);
+        }
+
+        return builder.buildFuture();
+    }
+
+    private CompletableFuture<Suggestions> suggestNormal(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+        SuggestionsBuilder suggestionsBuilder2 = builder.createOffset(this.startCursor);
+        consumer.accept(suggestionsBuilder2);
+        return builder.add(suggestionsBuilder2).buildFuture();
+    }
+
+    private CompletableFuture<Suggestions> suggestSelectorRest(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+        SuggestionsBuilder suggestionsBuilder2 = builder.createOffset(builder.getStart() - 1);
+        suggestSelector(suggestionsBuilder2);
+        builder.add(suggestionsBuilder2);
+        return builder.buildFuture();
+    }
+
+    private CompletableFuture<Suggestions> suggestOpen(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+        builder.suggest(String.valueOf('['));
+        return builder.buildFuture();
+    }
+
+    private CompletableFuture<Suggestions> suggestOptionOrEnd(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+        builder.suggest(String.valueOf(']'));
+        CEntitySelectorOptions.suggestOptions(this, builder);
+        return builder.buildFuture();
+    }
+
+    private CompletableFuture<Suggestions> suggestOption(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+        CEntitySelectorOptions.suggestOptions(this, builder);
+        return builder.buildFuture();
+    }
+
+    private CompletableFuture<Suggestions> suggestEndNext(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+        builder.suggest(String.valueOf(','));
+        builder.suggest(String.valueOf(']'));
+        return builder.buildFuture();
+    }
+
+    private CompletableFuture<Suggestions> suggestDefinerNext(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+        builder.suggest(String.valueOf('='));
+        return builder.buildFuture();
+    }
+
+    public boolean isSenderOnly() {
+        return this.senderOnly;
+    }
+
+    public void setSuggestionProvider(BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> suggestionProvider) {
+        this.suggestionProvider = suggestionProvider;
+    }
+
+    public CompletableFuture<Suggestions> listSuggestions(SuggestionsBuilder builder, Consumer<SuggestionsBuilder> consumer) {
+        return this.suggestionProvider.apply(builder.createOffset(this.reader.getCursor()), consumer);
+    }
+
+    public boolean selectsName() {
+        return this.selectsName;
+    }
+
+    public void setSelectsName(boolean selectsName) {
+        this.selectsName = selectsName;
+    }
+
+    public boolean excludesName() {
+        return this.excludesName;
+    }
+
+    public void setExcludesName(boolean excludesName) {
+        this.excludesName = excludesName;
+    }
+
+    public boolean hasLimit() {
+        return this.hasLimit;
+    }
+
+    public void setHasLimit(boolean hasLimit) {
+        this.hasLimit = hasLimit;
+    }
+
+    public boolean hasSorter() {
+        return this.hasSorter;
+    }
+
+    public void setHasSorter(boolean hasSorter) {
+        this.hasSorter = hasSorter;
+    }
+
+    public boolean selectsGameMode() {
+        return this.selectsGameMode;
+    }
+
+    public void setSelectsGameMode(boolean selectsGameMode) {
+        this.selectsGameMode = selectsGameMode;
+    }
+
+    public boolean excludesGameMode() {
+        return this.excludesGameMode;
+    }
+
+    public void setExcludesGameMode(boolean excludesGameMode) {
+        this.excludesGameMode = excludesGameMode;
+    }
+
+    public boolean selectsTeam() {
+        return this.selectsTeam;
+    }
+
+    public void setSelectsTeam(boolean selectsTeam) {
+        this.selectsTeam = selectsTeam;
+    }
+
+    public boolean excludesTeam() {
+        return this.excludesTeam;
+    }
+
+    public void setExcludesTeam(boolean excludesTeam) {
+        this.excludesTeam = excludesTeam;
+    }
+
+    public void setEntityType(EntityType<?> entityType) {
+        this.entityType = entityType;
+    }
+
+    public void setExcludesEntityType() {
+        this.excludesEntityType = true;
+    }
+
+    public boolean selectsEntityType() {
+        return this.entityType != null;
+    }
+
+    public boolean excludesEntityType() {
+        return this.excludesEntityType;
+    }
+
+    public boolean selectsScores() {
+        return this.selectsScores;
+    }
+
+    public void setSelectsScores(boolean selectsScores) {
+        this.selectsScores = selectsScores;
+    }
 
-   public boolean selectsAdvancements() {
-      return this.selectsAdvancements;
-   }
+    public boolean selectsAdvancements() {
+        return this.selectsAdvancements;
+    }
 
-   public void setSelectsAdvancements(boolean selectsAdvancements) {
-      this.selectsAdvancements = selectsAdvancements;
-   }
+    public void setSelectsAdvancements(boolean selectsAdvancements) {
+        this.selectsAdvancements = selectsAdvancements;
+    }
 }
diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/EntityDataCommand.java b/src/main/java/org/chrisoft/trashyaddon/commands/EntityDataCommand.java
index 4f88655..a6ab279 100644
--- a/src/main/java/org/chrisoft/trashyaddon/commands/EntityDataCommand.java
+++ b/src/main/java/org/chrisoft/trashyaddon/commands/EntityDataCommand.java
@@ -27,8 +27,8 @@ public class EntityDataCommand extends Command {
             //info("erectile dysfunction start");
             double RANGE = 5;
             Entity player = mc.cameraEntity;
-            Vec3d rot = player.getRotationVec(mc.getTickDelta());
-            Vec3d min = player.getCameraPosVec(mc.getTickDelta());
+            Vec3d rot = player.getRotationVec(1.f);
+            Vec3d min = player.getCameraPosVec(1.f);
             Vec3d max = min.add(rot.multiply(RANGE));
             Box box = player.getBoundingBox().stretch(rot.multiply(RANGE));
             EntityHitResult eh = ProjectileUtil.raycast(player, min, max, box, x -> true ,RANGE * RANGE);
diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/MapDumpCommand.java b/src/main/java/org/chrisoft/trashyaddon/commands/MapDumpCommand.java
index 2bc7096..b09c9be 100644
--- a/src/main/java/org/chrisoft/trashyaddon/commands/MapDumpCommand.java
+++ b/src/main/java/org/chrisoft/trashyaddon/commands/MapDumpCommand.java
@@ -4,6 +4,8 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
 import meteordevelopment.meteorclient.MeteorClient;
 import meteordevelopment.meteorclient.commands.Command;
 import net.minecraft.command.CommandSource;
+import net.minecraft.component.DataComponentTypes;
+import net.minecraft.component.type.MapIdComponent;
 import net.minecraft.entity.EntityType;
 import net.minecraft.entity.decoration.ItemFrameEntity;
 import net.minecraft.client.MinecraftClient;
@@ -19,7 +21,6 @@ import java.util.zip.GZIPOutputStream;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.Vector;
-import java.util.OptionalInt;
 
 import static com.mojang.brigadier.Command.SINGLE_SUCCESS;
 
@@ -38,14 +39,14 @@ public class MapDumpCommand extends Command {
                 if (e.getType() != EntityType.ITEM_FRAME && e.getType() != EntityType.GLOW_ITEM_FRAME)
                     continue;
                 ItemFrameEntity ife = (ItemFrameEntity) e;
-                OptionalInt oid = ife.getMapId();
-                if (oid.isEmpty())
+                MapIdComponent idc = ife.getMapId(ife.getHeldItemStack());
+                if (idc == null)
                     continue;
-                int id = oid.getAsInt();
-                MapState mapState = FilledMapItem.getMapState(id, mc.world);
+                int id = idc.id();
+                MapState mapState = FilledMapItem.getMapState(idc, mc.world);
                 if (mapState == null)
                     continue;
-                String name = ife.getHeldItemStack().hasCustomName() ? ife.getHeldItemStack().getName().getString() : "";
+                String name = ife.getHeldItemStack().contains(DataComponentTypes.CUSTOM_NAME) ? ife.getHeldItemStack().getName().getString() : "";
                 byte[] u8name = name.getBytes(StandardCharsets.UTF_8);
                 int bsz = 4 + 4 + u8name.length + 16384;
                 ByteBuffer buf = ByteBuffer.allocate(bsz);
diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/MapTallyCommand.java b/src/main/java/org/chrisoft/trashyaddon/commands/MapTallyCommand.java
index cce0687..cfeaee8 100644
--- a/src/main/java/org/chrisoft/trashyaddon/commands/MapTallyCommand.java
+++ b/src/main/java/org/chrisoft/trashyaddon/commands/MapTallyCommand.java
@@ -8,6 +8,8 @@ import meteordevelopment.orbit.EventPriority;
 import meteordevelopment.orbit.listeners.IListener;
 import meteordevelopment.meteorclient.events.packets.InventoryEvent;
 import net.minecraft.command.CommandSource;
+import net.minecraft.component.DataComponentTypes;
+import net.minecraft.component.type.MapIdComponent;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.item.FilledMapItem;
 import net.minecraft.item.ItemStack;
@@ -79,7 +81,10 @@ public class MapTallyCommand extends Command {
                         int nmaps = 0;
                         for (ItemStack s : stacks.subList(0, stacks.size() - 36)) {
                             if (s.getItem() instanceof FilledMapItem) {
-                                ids.add(FilledMapItem.getMapId(s));
+                                MapIdComponent idc = s.getComponents().get(DataComponentTypes.MAP_ID);
+                                if (idc == null)
+                                    continue;
+                                ids.add(idc.id());
                                 ++nmaps;
                             }
                         }
diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/argument/CEntityArgumentType.java b/src/main/java/org/chrisoft/trashyaddon/commands/argument/CEntityArgumentType.java
index c13c08c..e9206f4 100644
--- a/src/main/java/org/chrisoft/trashyaddon/commands/argument/CEntityArgumentType.java
+++ b/src/main/java/org/chrisoft/trashyaddon/commands/argument/CEntityArgumentType.java
@@ -65,7 +65,7 @@ public class CEntityArgumentType implements ArgumentType<CEntitySelector> {
 
    public CEntitySelector parse(StringReader stringReader) throws CommandSyntaxException {
       int i = 0;
-      CEntitySelectorReader lv = new CEntitySelectorReader(stringReader);
+      CEntitySelectorReader lv = new CEntitySelectorReader(stringReader, true);
       CEntitySelector lv2 = lv.read();
       if (lv2.getLimit() > 1 && this.singleTarget) {
          if (this.playersOnly) {
diff --git a/src/main/java/org/chrisoft/trashyaddon/modules/AcceptablePrices.java b/src/main/java/org/chrisoft/trashyaddon/modules/AcceptablePrices.java
index 4809640..624a47f 100644
--- a/src/main/java/org/chrisoft/trashyaddon/modules/AcceptablePrices.java
+++ b/src/main/java/org/chrisoft/trashyaddon/modules/AcceptablePrices.java
@@ -76,7 +76,7 @@ public class AcceptablePrices implements ICopyable<AcceptablePrices>, ISerializa
             for (int i = 0; i < l.size(); ++i) {
                 NbtCompound a = l.getCompound(i);
                 String item_id = a.getString("Item");
-                Item item = Registries.ITEM.get(new Identifier(item_id));
+                Item item = Registries.ITEM.get(Identifier.of(item_id));
                 int price = a.getInt("Price");
                 ret.put(item, price);
             }
diff --git a/src/main/java/org/chrisoft/trashyaddon/modules/AutoTrade.java b/src/main/java/org/chrisoft/trashyaddon/modules/AutoTrade.java
index dfb343d..a062b38 100644
--- a/src/main/java/org/chrisoft/trashyaddon/modules/AutoTrade.java
+++ b/src/main/java/org/chrisoft/trashyaddon/modules/AutoTrade.java
@@ -39,7 +39,7 @@ public class AutoTrade extends Module {
         Items.ROTTEN_FLESH,
         Items.GOLD_INGOT,
         Items.RABBIT_FOOT,
-        Items.SCUTE,
+        Items.TURTLE_SCUTE,
         Items.GLASS_BOTTLE,
         Items.NETHER_WART,
         Items.WHEAT,
@@ -184,11 +184,11 @@ public class AutoTrade extends Module {
     private boolean isOfferEligible(TradeOffer o) {
         // I don't give a SHIT to trades that use the second slot
         // well actually I do, but I don't need THAT many enchanted books...
-        if (!o.getSecondBuyItem().getItem().equals(Items.AIR))
+        if (o.getSecondBuyItem().isPresent() && !o.getSecondBuyItem().get().equals(Items.AIR))
             return false;
         if (o.isDisabled())
             return false;
-        ItemStack mbuy = o.getAdjustedFirstBuyItem();
+        ItemStack mbuy = o.getDisplayedFirstBuyItem();
         ItemStack msell = o.getSellItem();
         List<Item> sells = sellingEnabled.get() ? this.sellsSetting.get() : List.of();
         List<Item> buys = buyingEnabled.get() ? this.buysSetting.get() : List.of();
@@ -223,7 +223,7 @@ public class AutoTrade extends Module {
         ticksRemaining = -0xdead;
         TradeOffer o = offers.get(currentOffer);
         Item tradedItem = o.getSellItem().getItem().equals(Items.EMERALD) ?
-            o.getAdjustedFirstBuyItem().getItem() : o.getSellItem().getItem();
+            o.getDisplayedFirstBuyItem().getItem() : o.getSellItem().getItem();
         countBefore = screen.getScreenHandler().slots.stream()
             .map(slot -> slot.getStack())
             .filter(s -> s.getItem().equals(tradedItem))
@@ -236,7 +236,7 @@ public class AutoTrade extends Module {
     private void finalizeCurrentTrade() {
         TradeOffer o = offers.get(currentOffer);
         Item tradedItem = o.getSellItem().getItem().equals(Items.EMERALD) ?
-            o.getAdjustedFirstBuyItem().getItem() : o.getSellItem().getItem();
+            o.getDisplayedFirstBuyItem().getItem() : o.getSellItem().getItem();
         countAfter = screen.getScreenHandler().slots.stream()
             .map(slot -> slot.getStack())
             .filter(s -> s.getItem().equals(tradedItem))
@@ -265,6 +265,7 @@ public class AutoTrade extends Module {
                     first = false;
                 }
                 info(f.toString());
+                f.close();
             }
             if (!itemsBought.isEmpty()) {
                 Formatter f = new Formatter();
@@ -275,6 +276,7 @@ public class AutoTrade extends Module {
                     first = false;
                 }
                 info(f.toString());
+                f.close();
             }
         }
         if (autoClose.get() && (!itemsBought.isEmpty() || !itemsSold.isEmpty()))
@@ -293,7 +295,7 @@ public class AutoTrade extends Module {
                 return;
             }
             if (waitState != WaitState.None && currentOffer < offers.size() && (offers.get(currentOffer).isDisabled() ||
-                screen.getScreenHandler().slots.get(0).getStack().getCount() < offers.get(currentOffer).getAdjustedFirstBuyItem().getCount())) {
+                screen.getScreenHandler().slots.get(0).getStack().getCount() < offers.get(currentOffer).getDisplayedFirstBuyItem().getCount())) {
                 // the WaitingForInventoryUpdate state is mostly useless because ScreenHandlerSlotUpdateS2CPacket
                 // isn't sent after the shift-click...
                 finalizeCurrentTrade();
diff --git a/src/main/java/org/chrisoft/trashyaddon/modules/MatchedItemHighlight.java b/src/main/java/org/chrisoft/trashyaddon/modules/MatchedItemHighlight.java
index 41910c7..2d5be7e 100644
--- a/src/main/java/org/chrisoft/trashyaddon/modules/MatchedItemHighlight.java
+++ b/src/main/java/org/chrisoft/trashyaddon/modules/MatchedItemHighlight.java
@@ -48,8 +48,8 @@ public class MatchedItemHighlight extends Module {
             for (int j = firstInventorySlot; j < screenHandler.slots.size(); ++ j) {
                 if (screenHandler.getSlot(j).getStack().isEmpty())
                     continue;
-                if (ItemStack.canCombine(screenHandler.getSlot(i).getStack(), screenHandler.getSlot(j).getStack()) ||
-                    ItemStack.canCombine(screenHandler.getSlot(j).getStack(), screenHandler.getSlot(i).getStack())) {
+                if (ItemStack.areItemsAndComponentsEqual(screenHandler.getSlot(i).getStack(), screenHandler.getSlot(j).getStack()) ||
+                    ItemStack.areItemsAndComponentsEqual(screenHandler.getSlot(j).getStack(), screenHandler.getSlot(i).getStack())) {
                     Color c;
                     if (!slotColors.get(i).equals(transparent))
                         c = new Color(slotColors.get(i));
-- 
cgit v1.2.3