From 697050ebed365e307f152df196dc98f3c7db846a Mon Sep 17 00:00:00 2001 From: Pedro270707 Date: Sat, 23 Sep 2023 21:38:58 -0300 Subject: [PATCH] Added distance and type options --- .../commander/CommanderHelper.java | 8 +- .../content/helpers/EntitySelector.java | 42 +++-- .../helpers/EntitySelectorOptions.java | 91 ++++++++++- .../content/helpers/EntitySelectorParser.java | 95 ++++++++++-- .../content/helpers/MinMaxBounds.java | 145 ++++++++++++++++++ src/main/resources/lang/commander/en_US.lang | 12 ++ 6 files changed, 357 insertions(+), 36 deletions(-) create mode 100644 src/main/java/net/pedroricardo/commander/content/helpers/MinMaxBounds.java diff --git a/src/main/java/net/pedroricardo/commander/CommanderHelper.java b/src/main/java/net/pedroricardo/commander/CommanderHelper.java index df37bea..df433ce 100644 --- a/src/main/java/net/pedroricardo/commander/CommanderHelper.java +++ b/src/main/java/net/pedroricardo/commander/CommanderHelper.java @@ -73,6 +73,12 @@ public static boolean isIgnorableKey(int key) { return IGNORABLE_KEYS.contains(key); } + public static CompletableFuture suggest(String string, SuggestionsBuilder suggestionsBuilder) { + String stringRemaining = suggestionsBuilder.getRemaining().toLowerCase(Locale.ROOT); + if (matchesSubStr(stringRemaining, string.toLowerCase(Locale.ROOT))) suggestionsBuilder.suggest(string); + return suggestionsBuilder.buildFuture(); + } + public static CompletableFuture suggest(Iterable iterable, SuggestionsBuilder suggestionsBuilder) { String string = suggestionsBuilder.getRemaining().toLowerCase(Locale.ROOT); for (String string2 : iterable) { @@ -96,7 +102,7 @@ public static boolean matchesSubStr(String string, String string2) { public static Optional getStringToSuggest(String checkedString, String input) { if (checkedString.startsWith(input)) { return Optional.of(checkedString); - } else if (checkedString.substring(checkedString.indexOf('.') + 1).startsWith(input)) { + } else if (checkedString.contains(".") && checkedString.substring(checkedString.indexOf('.') + 1).startsWith(input)) { return Optional.of(checkedString.substring(checkedString.indexOf('.') + 1)); } return Optional.empty(); diff --git a/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelector.java b/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelector.java index e9d815e..c9b3366 100644 --- a/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelector.java +++ b/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelector.java @@ -17,27 +17,31 @@ public class EntitySelector { private final boolean includesEntities; private final BiConsumer> order; private final @Nullable Class limitToType; + private final boolean typeInverse; private final boolean currentEntity; private final Predicate predicate; private final @Nullable String entityId; private final @Nullable String playerName; + private final MinMaxBounds.Doubles distance; - public EntitySelector(int maxResults, boolean includesEntities, BiConsumer> order, @Nullable Class limitToType, boolean currentEntity, Predicate predicate, @Nullable String entityId, @Nullable String playerName) { + public EntitySelector(int maxResults, boolean includesEntities, BiConsumer> order, @Nullable Class limitToType, boolean typeInverse, boolean currentEntity, Predicate predicate, @Nullable String entityId, @Nullable String playerName, MinMaxBounds.Doubles distance) { this.maxResults = maxResults; this.includesEntities = includesEntities; this.order = order; this.limitToType = limitToType; + this.typeInverse = typeInverse; this.currentEntity = currentEntity; this.predicate = predicate; this.entityId = entityId; this.playerName = playerName; + this.distance = distance; } - public List get(CommanderCommandSource commandSource) throws CommandSyntaxException { + public List get(CommanderCommandSource source) throws CommandSyntaxException { // Entity ID/player if (this.entityId != null) { List entities = new ArrayList<>(); - for (Entity entity : commandSource.getWorld().loadedEntityList) { + for (Entity entity : source.getWorld().loadedEntityList) { if ((Commander.ENTITY_PREFIX + entity.hashCode()).equals(this.entityId)) { entities.add(entity); } @@ -45,7 +49,7 @@ public List get(CommanderCommandSource commandSource) throws C return entities.subList(0, Math.min(entities.size(), this.maxResults)); } else if (this.playerName != null) { List players = new ArrayList<>(); - for (EntityPlayer player : commandSource.getWorld().players) { + for (EntityPlayer player : source.getWorld().players) { if (player.username.equals(this.playerName) || player.nickname.equals(this.playerName)) { players.add(player); } @@ -53,30 +57,29 @@ public List get(CommanderCommandSource commandSource) throws C return players.subList(0, Math.min(players.size(), this.maxResults)); } - // Player only? + // Player only List entities; if (this.includesEntities) { - entities = commandSource.getWorld().loadedEntityList; + entities = source.getWorld().loadedEntityList; } else { - entities = commandSource.getWorld().players; + entities = source.getWorld().players; } - // Limit to entity type - if (limitToType != null) { - List temp = new ArrayList<>(entities); - for (Entity entity : entities) { - if (!limitToType.isInstance(entity)) { - temp.remove(entity); - } + List temp = new ArrayList<>(entities); + for (Entity entity : entities) { + if ((limitToType != null && limitToType.isInstance(entity) == this.typeInverse) + || !predicate.test(entity) + || !this.distanceContains(source, entity)) { + temp.remove(entity); } - entities = temp; } + entities = temp; // Sorting order - this.order.accept(commandSource.getSender(), entities); + this.order.accept(source.getSender(), entities); - List listAfterPredicate = new ArrayList<>(); // Predicate + List listAfterPredicate = new ArrayList<>(); for (Entity entity : entities) { if (!predicate.test(entity)) continue; listAfterPredicate.add(entity); @@ -88,6 +91,11 @@ public List get(CommanderCommandSource commandSource) throws C return listAfterPredicate; } + private boolean distanceContains(CommanderCommandSource source, Entity entity) { + if (this.distance.isAny()) return true; + return source.getSender() != null && this.distance.contains(source.getSender().distanceTo(entity)); + } + public int getMaxResults() { return this.maxResults; } diff --git a/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelectorOptions.java b/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelectorOptions.java index 11a671f..f250ea2 100644 --- a/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelectorOptions.java +++ b/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelectorOptions.java @@ -1,25 +1,32 @@ package net.pedroricardo.commander.content.helpers; -import com.mojang.brigadier.Message; 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 net.minecraft.core.entity.Entity; +import net.minecraft.core.entity.EntityDispatcher; import net.minecraft.core.entity.player.EntityPlayer; import net.minecraft.core.lang.I18n; import net.minecraft.core.lang.text.Text; import net.minecraft.core.lang.text.TextTranslatable; import net.minecraft.core.player.gamemode.Gamemode; +import net.pedroricardo.commander.Commander; import net.pedroricardo.commander.CommanderHelper; import java.util.*; +import java.util.function.BiConsumer; import java.util.function.Predicate; public class EntitySelectorOptions { private static final DynamicCommandExceptionType INAPPLICABLE_OPTION = new DynamicCommandExceptionType(value -> (() -> I18n.getInstance().translateKeyAndFormat("argument_types.commander.entity.selector.options.inapplicable", value))); private static final DynamicCommandExceptionType UNKNOWN_OPTION = new DynamicCommandExceptionType(value -> (() -> I18n.getInstance().translateKeyAndFormat("argument_types.commander.entity.selector.options.unknown", value))); private static final DynamicCommandExceptionType UNKNOWN_GAME_MODE = new DynamicCommandExceptionType(value -> (() -> I18n.getInstance().translateKeyAndFormat("argument_types.commander.entity.selector.options.gamemode.invalid", value))); + private static final DynamicCommandExceptionType UNKNOWN_SORT = new DynamicCommandExceptionType(value -> (() -> I18n.getInstance().translateKeyAndFormat("argument_types.commander.entity.selector.options.sort.invalid", value))); + private static final DynamicCommandExceptionType UNKNOWN_ENTITY_TYPE = new DynamicCommandExceptionType(value -> (() -> I18n.getInstance().translateKeyAndFormat("argument_types.commander.entity.selector.options.type.invalid", value))); + private static final SimpleCommandExceptionType NEGATIVE_DISTANCE = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("argument_types.commander.entity.selector.options.distance.invalid")); + private static final SimpleCommandExceptionType LIMIT_TOO_SMALL = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("argument_types.commander.entity.selector.options.limit.invalid")); private final StringReader reader; private final String key; @@ -36,6 +43,87 @@ public static void register(String key, Modifier modifier, Predicate { + int cursor = parser.getReader().getCursor(); + MinMaxBounds.Doubles bounds = MinMaxBounds.Doubles.fromReader(parser.getReader()); + if ((bounds.getMin() != null && bounds.getMin() < 0) || (bounds.getMax() != null && bounds.getMax() < 0)) { + parser.getReader().setCursor(cursor); + throw NEGATIVE_DISTANCE.createWithContext(parser.getReader()); + } + parser.setDistance(bounds); + }, parser -> parser.getDistance().isAny(), new TextTranslatable("argument_types.commander.entity.selector.options.distance.description")); + register("type", (parser) -> { + int cursor = parser.getReader().getCursor(); + boolean invert = parser.shouldInvertValue(); + + parser.setSuggestions((builder, consumer) -> { + String string = builder.getRemaining().toLowerCase(Locale.ROOT); + if (!string.isEmpty()) { + if (string.charAt(0) == '!') { + string = string.substring(1); + } + } + CommanderHelper.suggest("!Player", builder); + CommanderHelper.suggest("Player", builder); + for (String key : EntityDispatcher.stringToClassMapping.keySet()) { + if (!key.toLowerCase(Locale.ROOT).startsWith(string)) continue; + CommanderHelper.suggest("!" + key, builder); + if (invert) continue; + CommanderHelper.suggest(key, builder); + } + return builder.buildFuture(); + }); + + if (invert) { + parser.setTypeInverse(true); + } + String type = parser.getReader().readUnquotedString(); + if (type.equals("Player")) { + parser.setLimitToType(EntityPlayer.class); + } else { + if (EntityDispatcher.stringToClassMapping.containsKey(type)) { + parser.setLimitToType(EntityDispatcher.stringToClassMapping.get(type)); + } else { + parser.getReader().setCursor(cursor); + throw UNKNOWN_ENTITY_TYPE.createWithContext(parser.getReader(), type); + } + } + }, parser -> !parser.hasType(), new TextTranslatable("argument_types.commander.entity.selector.options.type.description")); + register("limit", (parser) -> { + int cursor = parser.getReader().getCursor(); + int limit = parser.getReader().readInt(); + if (limit < 1) { + parser.getReader().setCursor(cursor); + throw LIMIT_TOO_SMALL.createWithContext(parser.getReader()); + } + parser.setMaxResults(limit); + parser.setHasLimit(true); + }, parser -> !parser.hasLimit(), new TextTranslatable("argument_types.commander.entity.selector.options.limit.description")); + register("sort", (parser) -> { + int i = parser.getReader().getCursor(); + String string = parser.getReader().readUnquotedString(); + parser.setSuggestions((suggestionsBuilder, consumer) -> CommanderHelper.suggest(Arrays.asList("nearest", "furthest", "random", "arbitrary"), suggestionsBuilder)); + BiConsumer> sort; + switch (string) { + case "nearest": + sort = EntitySelectorParser.ORDER_NEAREST; + break; + case "furthest": + sort = EntitySelectorParser.ORDER_FURTHEST; + break; + case "random": + sort = EntitySelectorParser.ORDER_RANDOM; + break; + case "arbitrary": + sort = EntitySelectorParser.ORDER_ARBITRARY; + break; + default: + parser.getReader().setCursor(i); + throw UNKNOWN_SORT.createWithContext(parser.getReader(), string); + } + parser.setOrder(sort); + parser.setSorted(true); + }, parser -> !parser.isSorted(), new TextTranslatable("argument_types.commander.entity.selector.options.sort.description")); register("gamemode", (parser) -> { parser.setSuggestions((builder, consumer) -> { String string = builder.getRemaining().toLowerCase(Locale.ROOT); @@ -75,6 +163,7 @@ public static void register(String key, Modifier modifier, Predicate { if (!(entity instanceof EntityPlayer)) return false; return CommanderHelper.matchesKeyString(((EntityPlayer) entity).gamemode.languageKey, value) != invert; diff --git a/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelectorParser.java b/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelectorParser.java index 4fbba0f..6ead1e1 100644 --- a/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelectorParser.java +++ b/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelectorParser.java @@ -3,6 +3,7 @@ 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; @@ -22,10 +23,13 @@ import java.util.function.Predicate; public class EntitySelectorParser { - private final BiConsumer> ORDER_RANDOM = (sender, entities) -> Collections.shuffle(entities); - private final BiConsumer> ORDER_ARBITRARY = (sender, entities) -> {}; - private final BiConsumer> ORDER_CLOSEST = (sender, entities) -> entities.sort((firstEntity, secondEntity) -> Doubles.compare(firstEntity.distanceTo(sender), secondEntity.distanceTo(sender))); - private final BiConsumer> ORDER_FARTHEST = (sender, entities) -> entities.sort((firstEntity, secondEntity) -> Doubles.compare(secondEntity.distanceTo(sender), firstEntity.distanceTo(sender))); + private static final SimpleCommandExceptionType EXPECTED_END_OF_OPTIONS = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("argument_types.commander.entity.selector.options.unterminated")); + private static final DynamicCommandExceptionType EXPECTED_OPTION_VALUE = new DynamicCommandExceptionType((value) -> () -> I18n.getInstance().translateKeyAndFormat("argument_types.commander.entity.selector.options.valueless", value)); + + public static final BiConsumer> ORDER_RANDOM = (sender, entities) -> Collections.shuffle(entities); + public static final BiConsumer> ORDER_ARBITRARY = (sender, entities) -> {}; + public static final BiConsumer> ORDER_NEAREST = (sender, entities) -> entities.sort((firstEntity, secondEntity) -> Doubles.compare(firstEntity.distanceTo(sender), secondEntity.distanceTo(sender))); + public static final BiConsumer> ORDER_FURTHEST = (sender, entities) -> entities.sort((firstEntity, secondEntity) -> Doubles.compare(secondEntity.distanceTo(sender), firstEntity.distanceTo(sender))); private final SimpleCommandExceptionType SELECTORS_NOT_ALLOWED = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("argument_types.commander.entity.invalid_selector.selectors_not_allowed")); @@ -38,10 +42,12 @@ public class EntitySelectorParser { private boolean includesEntities; private BiConsumer> order; private @Nullable Class limitToType; + private boolean typeInverse = false; private boolean currentEntity; private String entityId; private String playerName; - private boolean allowSelectors; + private MinMaxBounds.Doubles distance = MinMaxBounds.Doubles.ANY; + private final boolean allowSelectors; private final BiFunction, CompletableFuture> NO_SUGGESTIONS = (builder, consumer) -> builder.buildFuture(); @@ -65,29 +71,28 @@ private void parseSelector() throws CommandSyntaxException { case 'p': this.maxResults = 1; this.includesEntities = false; - this.order = ORDER_CLOSEST; - this.limitToType = EntityPlayer.class; + this.order = ORDER_NEAREST; + this.setLimitToType(EntityPlayer.class); this.currentEntity = false; break; case 'a': this.maxResults = Integer.MAX_VALUE; this.includesEntities = false; this.order = ORDER_ARBITRARY; - this.limitToType = EntityPlayer.class; + this.setLimitToType(EntityPlayer.class); this.currentEntity = false; break; case 'r': this.maxResults = 1; this.includesEntities = false; this.order = ORDER_RANDOM; - this.limitToType = EntityPlayer.class; + this.setLimitToType(EntityPlayer.class); this.currentEntity = false; break; case 's': this.maxResults = 1; this.includesEntities = true; this.order = ORDER_ARBITRARY; - this.limitToType = EntityPlayer.class; this.currentEntity = true; break; case 'e': @@ -133,7 +138,7 @@ public EntitySelector parse() throws CommandSyntaxException { } else { this.parseNameOrEntityId(); } - return new EntitySelector(this.maxResults, this.includesEntities, this.order, this.limitToType, this.currentEntity, this.predicate, this.entityId, this.playerName); + return new EntitySelector(this.maxResults, this.includesEntities, this.order, this.limitToType, this.typeInverse, this.currentEntity, this.predicate, this.entityId, this.playerName, this.distance); } private void parseOptions() throws CommandSyntaxException { @@ -147,8 +152,7 @@ private void parseOptions() throws CommandSyntaxException { this.reader.skipWhitespace(); if (!this.reader.canRead() || this.reader.peek() != '=') { this.reader.setCursor(i); - throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherParseException().createWithContext(this.reader, key); -// throw ERROR_EXPECTED_OPTION_VALUE.createWithContext(this.reader, key); + throw EXPECTED_OPTION_VALUE.createWithContext(this.reader, key); } this.reader.skip(); this.reader.skipWhitespace(); @@ -163,12 +167,10 @@ private void parseOptions() throws CommandSyntaxException { continue; } if (this.reader.peek() == ']') break; - throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(this.reader); - // throw ERROR_EXPECTED_END_OF_OPTIONS.createWithContext(this.reader); + throw EXPECTED_END_OF_OPTIONS.createWithContext(this.reader); } if (!this.reader.canRead()) { - throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(this.reader); - // throw ERROR_EXPECTED_END_OF_OPTIONS.createWithContext(this.reader); + throw EXPECTED_END_OF_OPTIONS.createWithContext(this.reader); } this.reader.skip(); this.suggestions = NO_SUGGESTIONS; @@ -255,6 +257,9 @@ public BiFunction, CompletableF private boolean hasGamemodeEquals = false; private boolean hasGamemodeNotEquals = false; + private boolean isSorted = false; + private boolean hasLimit = false; + private boolean hasType = false; public boolean hasGamemodeEquals() { return this.hasGamemodeEquals; @@ -271,4 +276,60 @@ public boolean hasGamemodeNotEquals() { public void setHasGamemodeNotEquals(boolean bl) { this.hasGamemodeNotEquals = bl; } + + public boolean isSorted() { + return this.isSorted; + } + + public void setSorted(boolean bl) { + this.isSorted = bl; + } + + public boolean hasLimit() { + return this.hasLimit; + } + + public void setHasLimit(boolean bl) { + this.hasLimit = bl; + } + + public boolean hasType() { + return this.limitToType != null; + } + + public boolean isTypeInverse() { + return this.typeInverse; + } + + public void setTypeInverse(boolean bl) { + this.typeInverse = bl; + } + + public MinMaxBounds.Doubles getDistance() { + return this.distance; + } + + public void setDistance(MinMaxBounds.Doubles distance) { + this.distance = distance; + } + + public void setMaxResults(int maxResults) { + this.maxResults = maxResults; + } + + public void setIncludesEntities(boolean includesEntities) { + this.includesEntities = includesEntities; + } + + public void setOrder(BiConsumer> order) { + this.order = order; + } + + public void setLimitToType(Class type) { + this.limitToType = type; + } + + public void setCurrentEntity(boolean currentEntity) { + this.currentEntity = currentEntity; + } } diff --git a/src/main/java/net/pedroricardo/commander/content/helpers/MinMaxBounds.java b/src/main/java/net/pedroricardo/commander/content/helpers/MinMaxBounds.java new file mode 100644 index 0000000..8b9d2ea --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/helpers/MinMaxBounds.java @@ -0,0 +1,145 @@ +package net.pedroricardo.commander.content.helpers; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import net.minecraft.core.lang.I18n; +import net.pedroricardo.commander.Commander; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Function; +import java.util.function.Supplier; + +public abstract class MinMaxBounds { + private static final SimpleCommandExceptionType EMPTY_VALUE = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("argument_types.commander.range.empty")); + private static final SimpleCommandExceptionType SWAPPED_VALUES = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("argument_types.commander.range.swapped")); + + @Nullable + protected final T min; + @Nullable + protected final T max; + + public MinMaxBounds(@Nullable T min, @Nullable T max) { + this.min = min; + this.max = max; + } + + public T getMax() { + return this.max; + } + + public T getMin() { + return this.min; + } + + public boolean isAny() { + return this.max == null && this.min == null; + } + + public static > R fromReader(StringReader reader, Function parser, Supplier supplier, BoundsFromReaderFactory boundsFromReaderFactory) throws CommandSyntaxException { + if (!reader.canRead()) throw EMPTY_VALUE.createWithContext(reader); + int cursor = reader.getCursor(); + try { + T number1 = readNumber(reader, parser, supplier); + T number2; + if (reader.canRead(2) && reader.peek() == '.' && reader.peek(1) == '.') { + reader.skip(); + reader.skip(); + number2 = readNumber(reader, parser, supplier); + } else { + number2 = number1; + } + if (number1 == null && number2 == null) { + throw EMPTY_VALUE.createWithContext(reader); + } + return boundsFromReaderFactory.create(reader, number1, number2); + } catch (CommandSyntaxException exception) { + reader.setCursor(cursor); + throw EMPTY_VALUE.createWithContext(reader); + } + } + + private static T readNumber(StringReader reader, Function parser, Supplier supplier) throws CommandSyntaxException { + int cursor = reader.getCursor(); + while (reader.canRead() && isAllowedChatCharacter(reader)) { + reader.skip(); + } + String numberString = reader.getString().substring(cursor, reader.getCursor()); + if (numberString.isEmpty()) { + return null; + } + try { + return parser.apply(numberString); + } catch (NumberFormatException e) { + throw supplier.get().createWithContext(reader); + } + } + + private static boolean isAllowedChatCharacter(StringReader reader) { + char c = reader.peek(); + if (c >= '0' && c <= '9' || c == '-') { + return true; + } + if (c == '.') { + return !reader.canRead(2) || reader.peek(1) != '.'; + } + return false; + } + + @FunctionalInterface + protected interface BoundsFromReaderFactory> { + R create(StringReader reader, T min, T max) throws CommandSyntaxException; + } + + public static class Integers extends MinMaxBounds { + public static Integers ANY = new Integers(null, null); + + public Integers(@Nullable Integer min, @Nullable Integer max) { + super(min, max); + } + + private static Integers create(StringReader stringReader, @Nullable Integer integer1, @Nullable Integer integer2) throws CommandSyntaxException { + if (integer1 != null && integer2 != null && integer1 > integer2) { + throw SWAPPED_VALUES.createWithContext(stringReader); + } + return new Integers(integer1, integer2); + } + + public boolean contains(int number) { + if (this.min != null && this.min > number) { + return false; + } + return this.max == null || this.max >= number; + } + + public static MinMaxBounds.Integers fromReader(StringReader reader) throws CommandSyntaxException { + return MinMaxBounds.fromReader(reader, Integer::parseInt, CommandSyntaxException.BUILT_IN_EXCEPTIONS::readerExpectedInt, Integers::create); + } + } + + public static class Doubles extends MinMaxBounds { + public static Doubles ANY = new Doubles(null, null); + + public Doubles(@Nullable Double min, @Nullable Double max) { + super(min, max); + } + + private static Doubles create(StringReader stringReader, @Nullable Double double1, @Nullable Double double2) throws CommandSyntaxException { + if (double1 != null && double2 != null && double1 > double2) { + throw SWAPPED_VALUES.createWithContext(stringReader); + } + return new Doubles(double1, double2); + } + + public boolean contains(double number) { + if (this.min != null && this.min > number) { + return false; + } + return this.max == null || this.max >= number; + } + + public static MinMaxBounds.Doubles fromReader(StringReader reader) throws CommandSyntaxException { + return MinMaxBounds.fromReader(reader, Double::parseDouble, CommandSyntaxException.BUILT_IN_EXCEPTIONS::readerExpectedInt, Doubles::create); + } + } +} diff --git a/src/main/resources/lang/commander/en_US.lang b/src/main/resources/lang/commander/en_US.lang index 848f6d5..2d37d44 100644 --- a/src/main/resources/lang/commander/en_US.lang +++ b/src/main/resources/lang/commander/en_US.lang @@ -14,8 +14,20 @@ argument_types.commander.entity.selector.self=Current entity argument_types.commander.entity.selector.all_entities=All entities argument_types.commander.entity.selector.options.inapplicable=Option '%s' isn't applicable here argument_types.commander.entity.selector.options.unknown=Unknown option '%s' +argument_types.commander.entity.selector.options.unterminated=Expected end of options +argument_types.commander.entity.selector.options.valueless=Expected value for option '%s' argument_types.commander.entity.selector.options.gamemode.invalid=Invalid or unknown game mode '%s' argument_types.commander.entity.selector.options.gamemode.description=Players with game mode +argument_types.commander.entity.selector.options.sort.description=Sort the entities +argument_types.commander.entity.selector.options.sort.invalid=Invalid or unknown sort type '%s' +argument_types.commander.entity.selector.options.limit.description=Maximum number of entities to return +argument_types.commander.entity.selector.options.limit.invalid=Limit must be at least 1 +argument_types.commander.entity.selector.options.type.description=Entities of type +argument_types.commander.entity.selector.options.type.invalid=Invalid or unknown entity type '%s' +argument_types.commander.entity.selector.options.distance.description=Distance to entity +argument_types.commander.entity.selector.options.distance.invalid=Distance cannot be negative +argument_types.commander.range.empty=Expected value or range of values +argument_types.commander.range.swapped=Min cannot be bigger than max commands.commander.achievement.grant.exception_already_has_achievement=Player already has achievement commands.commander.achievement.grant.success_multiple_entities=Granted achievement §1[%s]§r to %s entities commands.commander.achievement.grant.success_single_entity=Granted achievement §1[%s]§r to %s