aboutsummaryrefslogblamecommitdiff
path: root/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorReader.java
blob: 14c4b7a1db51ce10ae92bdf13012b094b41187e8 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                                                   
                           









                                              


                                           
                                                       



                                          
                                                


                                          





























































































































































































































                                                                                                                                                                                                

             


















                                                                     
                                           


























                                                                                                           
 
                               







































































































































































                                                                                    


                               





































































































































































                                                                                                                                                        
 


                                          
 


                                                                     
 
package org.chrisoft.trashyaddon.commands;

import com.google.common.primitives.Doubles;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
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;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToDoubleFunction;
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';
    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.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.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.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 void setSelectsAdvancements(boolean selectsAdvancements) {
        this.selectsAdvancements = selectsAdvancements;
    }
}