From 04b4941e65693f8d6b55f924781d7dd7cd26b1d5 Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Sat, 18 May 2024 01:24:05 -0400 Subject: commands: EntityHighlight, improved MapLocate. --- .../commands/CEntitySelectorOptions.java | 472 +++++++++++++++++++++ 1 file changed, 472 insertions(+) create mode 100644 src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorOptions.java (limited to 'src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorOptions.java') diff --git a/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorOptions.java b/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorOptions.java new file mode 100644 index 0000000..cb419f0 --- /dev/null +++ b/src/main/java/org/chrisoft/trashyaddon/commands/CEntitySelectorOptions.java @@ -0,0 +1,472 @@ +package org.chrisoft.trashyaddon.commands; + +import com.google.common.collect.Maps; +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.SuggestionsBuilder; +import java.util.Arrays; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Map.Entry; +import java.util.function.Predicate; + +import meteordevelopment.meteorclient.utils.entity.EntityUtils; +import net.minecraft.advancement.AdvancementProgress; +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.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.RegistryKeys; +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.text.Text; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.GameMode; + +public class CEntitySelectorOptions { + private static final Map 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 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> 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 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(); + + for (Entry 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; + }); + } + + reader.setSelectsScores(true); + }, reader -> !reader.selectsScores(), Text.translatable("argument.entity.options.scores.description")); + putOption("advancements", reader -> { + StringReader stringReader = reader.getReader(); + Map> 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> 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(); + } + } + + stringReader.skipWhitespace(); + stringReader.expect('}'); + stringReader.skipWhitespace(); + map.put(lv, advancementProgress -> { + for (Entry> 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.expect('}'); + if (!map.isEmpty()) { + reader.setPredicate(entity -> false); // no support + reader.setIncludesNonPlayers(false); + } + + 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 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 condition, Text description) { + } +} -- cgit v1.2.3