Rewrite Bukkit entity selector arguments using WrappedBrigadierParser instead of Bukkit API
- Proper handling of spaces - Possible to use built-in minecraft translations for brigadier exceptions - Fixes suggestions on Paper in combination with their option to fix tag selector suggestions - Added option to fail parse when the result collection is empty
This commit is contained in:
parent
09a66cef95
commit
2f34437398
12 changed files with 908 additions and 290 deletions
|
|
@ -39,7 +39,9 @@ import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import org.apiguardian.api.API;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
|
@ -56,6 +58,7 @@ public final class WrappedBrigadierParser<C, T> implements ArgumentParser<C, T>
|
||||||
|
|
||||||
private final Supplier<ArgumentType<T>> nativeType;
|
private final Supplier<ArgumentType<T>> nativeType;
|
||||||
private final int expectedArgumentCount;
|
private final int expectedArgumentCount;
|
||||||
|
private final @Nullable ParseFunction<T> parse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an argument parser based on a brigadier command.
|
* Create an argument parser based on a brigadier command.
|
||||||
|
|
@ -101,10 +104,28 @@ public final class WrappedBrigadierParser<C, T> implements ArgumentParser<C, T>
|
||||||
public WrappedBrigadierParser(
|
public WrappedBrigadierParser(
|
||||||
final Supplier<ArgumentType<T>> nativeType,
|
final Supplier<ArgumentType<T>> nativeType,
|
||||||
final int expectedArgumentCount
|
final int expectedArgumentCount
|
||||||
|
) {
|
||||||
|
this(nativeType, expectedArgumentCount, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an argument parser based on a brigadier command.
|
||||||
|
*
|
||||||
|
* @param nativeType the native command type provider, calculated lazily
|
||||||
|
* @param expectedArgumentCount the number of arguments the brigadier type is expected to consume
|
||||||
|
* @param parse special function to replace {@link ArgumentType#parse(StringReader)} (for CraftBukkit weirdness)
|
||||||
|
* @since 1.8.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.8.0")
|
||||||
|
public WrappedBrigadierParser(
|
||||||
|
final Supplier<ArgumentType<T>> nativeType,
|
||||||
|
final int expectedArgumentCount,
|
||||||
|
final @Nullable ParseFunction<T> parse
|
||||||
) {
|
) {
|
||||||
requireNonNull(nativeType, "brigadierType");
|
requireNonNull(nativeType, "brigadierType");
|
||||||
this.nativeType = nativeType;
|
this.nativeType = nativeType;
|
||||||
this.expectedArgumentCount = expectedArgumentCount;
|
this.expectedArgumentCount = expectedArgumentCount;
|
||||||
|
this.parse = parse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -135,7 +156,10 @@ public final class WrappedBrigadierParser<C, T> implements ArgumentParser<C, T>
|
||||||
|
|
||||||
// Then try to parse
|
// Then try to parse
|
||||||
try {
|
try {
|
||||||
return ArgumentParseResult.success(this.nativeType.get().parse(reader));
|
final T result = this.parse != null
|
||||||
|
? this.parse.apply(this.nativeType.get(), reader)
|
||||||
|
: this.nativeType.get().parse(reader);
|
||||||
|
return ArgumentParseResult.success(result);
|
||||||
} catch (final CommandSyntaxException ex) {
|
} catch (final CommandSyntaxException ex) {
|
||||||
return ArgumentParseResult.failure(ex);
|
return ArgumentParseResult.failure(ex);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -191,4 +215,24 @@ public final class WrappedBrigadierParser<C, T> implements ArgumentParser<C, T>
|
||||||
public int getRequestedArgumentCount() {
|
public int getRequestedArgumentCount() {
|
||||||
return this.expectedArgumentCount;
|
return this.expectedArgumentCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function which can call {@link ArgumentType#parse(StringReader)} or another method.
|
||||||
|
*
|
||||||
|
* @param <T> result type
|
||||||
|
* @since 1.8.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.8.0")
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ParseFunction<T> {
|
||||||
|
/**
|
||||||
|
* Apply the parse function.
|
||||||
|
*
|
||||||
|
* @param type argument type
|
||||||
|
* @param reader string reader
|
||||||
|
* @return result
|
||||||
|
* @throws CommandSyntaxException on failure
|
||||||
|
*/
|
||||||
|
T apply(ArgumentType<T> type, StringReader reader) throws CommandSyntaxException;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import cloud.commandframework.captions.Caption;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import org.apiguardian.api.API;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -58,7 +59,11 @@ public final class BukkitCaptionKeys {
|
||||||
public static final Caption ARGUMENT_PARSE_FAILURE_WORLD = of("argument.parse.failure.world");
|
public static final Caption ARGUMENT_PARSE_FAILURE_WORLD = of("argument.parse.failure.world");
|
||||||
/**
|
/**
|
||||||
* Variables: {input}
|
* Variables: {input}
|
||||||
|
*
|
||||||
|
* @deprecated parsing is now handled by Brigadier and will throw {@link com.mojang.brigadier.exceptions.CommandSyntaxException} instead.
|
||||||
*/
|
*/
|
||||||
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
|
@Deprecated
|
||||||
public static final Caption ARGUMENT_PARSE_FAILURE_SELECTOR_MALFORMED = of("argument.parse.failure.selector.malformed");
|
public static final Caption ARGUMENT_PARSE_FAILURE_SELECTOR_MALFORMED = of("argument.parse.failure.selector.malformed");
|
||||||
/**
|
/**
|
||||||
* Variables: None
|
* Variables: None
|
||||||
|
|
@ -66,17 +71,29 @@ public final class BukkitCaptionKeys {
|
||||||
public static final Caption ARGUMENT_PARSE_FAILURE_SELECTOR_UNSUPPORTED = of("argument.parse.failure.selector.unsupported");
|
public static final Caption ARGUMENT_PARSE_FAILURE_SELECTOR_UNSUPPORTED = of("argument.parse.failure.selector.unsupported");
|
||||||
/**
|
/**
|
||||||
* Variables: None
|
* Variables: None
|
||||||
|
*
|
||||||
|
* @deprecated parsing is now handled by Brigadier and will throw {@link com.mojang.brigadier.exceptions.CommandSyntaxException} instead.
|
||||||
*/
|
*/
|
||||||
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
|
@Deprecated
|
||||||
public static final Caption ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_PLAYERS = of(
|
public static final Caption ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_PLAYERS = of(
|
||||||
"argument.parse.failure.selector.too_many_players");
|
"argument.parse.failure.selector.too_many_players");
|
||||||
/**
|
/**
|
||||||
* Variables: None
|
* Variables: None
|
||||||
|
*
|
||||||
|
* @deprecated parsing is now handled by Brigadier and will throw {@link com.mojang.brigadier.exceptions.CommandSyntaxException} instead.
|
||||||
*/
|
*/
|
||||||
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
|
@Deprecated
|
||||||
public static final Caption ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_ENTITIES = of(
|
public static final Caption ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_ENTITIES = of(
|
||||||
"argument.parse.failure.selector.too_many_entities");
|
"argument.parse.failure.selector.too_many_entities");
|
||||||
/**
|
/**
|
||||||
* Variables: None
|
* Variables: None
|
||||||
|
*
|
||||||
|
* @deprecated parsing is now handled by Brigadier and will throw {@link com.mojang.brigadier.exceptions.CommandSyntaxException} instead.
|
||||||
*/
|
*/
|
||||||
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
|
@Deprecated
|
||||||
public static final Caption ARGUMENT_PARSE_FAILURE_SELECTOR_NON_PLAYER = of(
|
public static final Caption ARGUMENT_PARSE_FAILURE_SELECTOR_NON_PLAYER = of(
|
||||||
"argument.parse.failure.selector.non_player_in_player_selector");
|
"argument.parse.failure.selector.non_player_in_player_selector");
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
package cloud.commandframework.bukkit;
|
package cloud.commandframework.bukkit;
|
||||||
|
|
||||||
import cloud.commandframework.captions.SimpleCaptionRegistry;
|
import cloud.commandframework.captions.SimpleCaptionRegistry;
|
||||||
|
import org.apiguardian.api.API;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Caption registry that uses bi-functions to produce messages
|
* Caption registry that uses bi-functions to produce messages
|
||||||
|
|
@ -54,7 +55,11 @@ public class BukkitCaptionRegistry<C> extends SimpleCaptionRegistry<C> {
|
||||||
public static final String ARGUMENT_PARSE_FAILURE_WORLD = "'{input}' is not a valid Minecraft world";
|
public static final String ARGUMENT_PARSE_FAILURE_WORLD = "'{input}' is not a valid Minecraft world";
|
||||||
/**
|
/**
|
||||||
* Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_SELECTOR_MALFORMED}
|
* Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_SELECTOR_MALFORMED}
|
||||||
|
*
|
||||||
|
* @deprecated parsing is now handled by Brigadier and will throw {@link com.mojang.brigadier.exceptions.CommandSyntaxException} instead.
|
||||||
*/
|
*/
|
||||||
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
|
@Deprecated
|
||||||
public static final String ARGUMENT_PARSE_FAILURE_SELECTOR_MALFORMED = "Selector '{input}' is malformed.";
|
public static final String ARGUMENT_PARSE_FAILURE_SELECTOR_MALFORMED = "Selector '{input}' is malformed.";
|
||||||
/**
|
/**
|
||||||
* Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_SELECTOR_UNSUPPORTED}
|
* Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_SELECTOR_UNSUPPORTED}
|
||||||
|
|
@ -63,15 +68,27 @@ public class BukkitCaptionRegistry<C> extends SimpleCaptionRegistry<C> {
|
||||||
"Entity selector argument type not supported below Minecraft 1.13.";
|
"Entity selector argument type not supported below Minecraft 1.13.";
|
||||||
/**
|
/**
|
||||||
* Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_PLAYERS}
|
* Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_PLAYERS}
|
||||||
|
*
|
||||||
|
* @deprecated parsing is now handled by Brigadier and will throw {@link com.mojang.brigadier.exceptions.CommandSyntaxException} instead.
|
||||||
*/
|
*/
|
||||||
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
|
@Deprecated
|
||||||
public static final String ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_PLAYERS = "More than 1 player selected in single player selector";
|
public static final String ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_PLAYERS = "More than 1 player selected in single player selector";
|
||||||
/**
|
/**
|
||||||
* Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_ENTITIES}
|
* Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_ENTITIES}
|
||||||
|
*
|
||||||
|
* @deprecated parsing is now handled by Brigadier and will throw {@link com.mojang.brigadier.exceptions.CommandSyntaxException} instead.
|
||||||
*/
|
*/
|
||||||
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
|
@Deprecated
|
||||||
public static final String ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_ENTITIES = "More than 1 entity selected in single entity selector.";
|
public static final String ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_ENTITIES = "More than 1 entity selected in single entity selector.";
|
||||||
/**
|
/**
|
||||||
* Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_SELECTOR_NON_PLAYER}
|
* Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_SELECTOR_NON_PLAYER}
|
||||||
|
*
|
||||||
|
* @deprecated parsing is now handled by Brigadier and will throw {@link com.mojang.brigadier.exceptions.CommandSyntaxException} instead.
|
||||||
*/
|
*/
|
||||||
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
|
@Deprecated
|
||||||
public static final String ARGUMENT_PARSE_FAILURE_SELECTOR_NON_PLAYER = "Non-player(s) selected in player selector.";
|
public static final String ARGUMENT_PARSE_FAILURE_SELECTOR_NON_PLAYER = "Non-player(s) selected in player selector.";
|
||||||
/**
|
/**
|
||||||
* Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_LOCATION_INVALID_FORMAT}
|
* Default caption for {@link BukkitCaptionKeys#ARGUMENT_PARSE_FAILURE_LOCATION_INVALID_FORMAT}
|
||||||
|
|
@ -106,6 +123,7 @@ public class BukkitCaptionRegistry<C> extends SimpleCaptionRegistry<C> {
|
||||||
"Invalid input '{input}', requires an explicit namespace.";
|
"Invalid input '{input}', requires an explicit namespace.";
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
protected BukkitCaptionRegistry() {
|
protected BukkitCaptionRegistry() {
|
||||||
super();
|
super();
|
||||||
this.registerMessageFactory(
|
this.registerMessageFactory(
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import cloud.commandframework.CommandTree;
|
||||||
import cloud.commandframework.arguments.parser.ParserParameters;
|
import cloud.commandframework.arguments.parser.ParserParameters;
|
||||||
import cloud.commandframework.brigadier.BrigadierManagerHolder;
|
import cloud.commandframework.brigadier.BrigadierManagerHolder;
|
||||||
import cloud.commandframework.brigadier.CloudBrigadierManager;
|
import cloud.commandframework.brigadier.CloudBrigadierManager;
|
||||||
|
import cloud.commandframework.bukkit.annotation.specifier.AllowEmptySelection;
|
||||||
import cloud.commandframework.bukkit.annotation.specifier.DefaultNamespace;
|
import cloud.commandframework.bukkit.annotation.specifier.DefaultNamespace;
|
||||||
import cloud.commandframework.bukkit.annotation.specifier.RequireExplicitNamespace;
|
import cloud.commandframework.bukkit.annotation.specifier.RequireExplicitNamespace;
|
||||||
import cloud.commandframework.bukkit.argument.NamespacedKeyArgument;
|
import cloud.commandframework.bukkit.argument.NamespacedKeyArgument;
|
||||||
|
|
@ -157,15 +158,28 @@ public class BukkitCommandManager<C> extends CommandManager<C> implements Brigad
|
||||||
new Location2DArgument.Location2DParser<>());
|
new Location2DArgument.Location2DParser<>());
|
||||||
this.parserRegistry().registerParserSupplier(TypeToken.get(ProtoItemStack.class), parserParameters ->
|
this.parserRegistry().registerParserSupplier(TypeToken.get(ProtoItemStack.class), parserParameters ->
|
||||||
new ItemStackArgument.Parser<>());
|
new ItemStackArgument.Parser<>());
|
||||||
|
|
||||||
/* Register Entity Selector Parsers */
|
/* Register Entity Selector Parsers */
|
||||||
this.parserRegistry().registerParserSupplier(TypeToken.get(SingleEntitySelector.class), parserParameters ->
|
this.parserRegistry().registerParserSupplier(TypeToken.get(SingleEntitySelector.class), parserParameters ->
|
||||||
new SingleEntitySelectorArgument.SingleEntitySelectorParser<>());
|
new SingleEntitySelectorArgument.SingleEntitySelectorParser<>());
|
||||||
this.parserRegistry().registerParserSupplier(TypeToken.get(SinglePlayerSelector.class), parserParameters ->
|
this.parserRegistry().registerParserSupplier(TypeToken.get(SinglePlayerSelector.class), parserParameters ->
|
||||||
new SinglePlayerSelectorArgument.SinglePlayerSelectorParser<>());
|
new SinglePlayerSelectorArgument.SinglePlayerSelectorParser<>());
|
||||||
this.parserRegistry().registerParserSupplier(TypeToken.get(MultipleEntitySelector.class), parserParameters ->
|
this.parserRegistry().registerAnnotationMapper(
|
||||||
new MultipleEntitySelectorArgument.MultipleEntitySelectorParser<>());
|
AllowEmptySelection.class,
|
||||||
this.parserRegistry().registerParserSupplier(TypeToken.get(MultiplePlayerSelector.class), parserParameters ->
|
(annotation, type) -> ParserParameters.single(BukkitParserParameters.ALLOW_EMPTY_SELECTOR_RESULT, annotation.value())
|
||||||
new MultiplePlayerSelectorArgument.MultiplePlayerSelectorParser<>());
|
);
|
||||||
|
this.parserRegistry().registerParserSupplier(
|
||||||
|
TypeToken.get(MultipleEntitySelector.class),
|
||||||
|
parserParameters -> new MultipleEntitySelectorArgument.MultipleEntitySelectorParser<>(
|
||||||
|
parserParameters.get(BukkitParserParameters.ALLOW_EMPTY_SELECTOR_RESULT, true)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this.parserRegistry().registerParserSupplier(
|
||||||
|
TypeToken.get(MultiplePlayerSelector.class),
|
||||||
|
parserParameters -> new MultiplePlayerSelectorArgument.MultiplePlayerSelectorParser<>(
|
||||||
|
parserParameters.get(BukkitParserParameters.ALLOW_EMPTY_SELECTOR_RESULT, true)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
if (CraftBukkitReflection.classExists("org.bukkit.NamespacedKey")) {
|
if (CraftBukkitReflection.classExists("org.bukkit.NamespacedKey")) {
|
||||||
this.registerParserSupplierFor(NamespacedKeyArgument.class);
|
this.registerParserSupplierFor(NamespacedKeyArgument.class);
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ package cloud.commandframework.bukkit;
|
||||||
|
|
||||||
import cloud.commandframework.arguments.parser.ParserParameter;
|
import cloud.commandframework.arguments.parser.ParserParameter;
|
||||||
import io.leangen.geantyref.TypeToken;
|
import io.leangen.geantyref.TypeToken;
|
||||||
|
import org.apiguardian.api.API;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -32,11 +33,23 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
*
|
*
|
||||||
* @since 1.7.0
|
* @since 1.7.0
|
||||||
*/
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.7.0")
|
||||||
public final class BukkitParserParameters {
|
public final class BukkitParserParameters {
|
||||||
|
|
||||||
private BukkitParserParameters() {
|
private BukkitParserParameters() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to specify if an empty result is allowed for
|
||||||
|
* {@link cloud.commandframework.bukkit.parsers.selector.MultiplePlayerSelectorArgument} and
|
||||||
|
* {@link cloud.commandframework.bukkit.parsers.selector.MultipleEntitySelectorArgument}.
|
||||||
|
*
|
||||||
|
* @since 1.8.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.8.0")
|
||||||
|
public static final ParserParameter<Boolean> ALLOW_EMPTY_SELECTOR_RESULT =
|
||||||
|
create("allow_empty_selector_result", TypeToken.get(Boolean.class));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets to require explicit namespaces for {@link cloud.commandframework.bukkit.argument.NamespacedKeyArgument}
|
* Sets to require explicit namespaces for {@link cloud.commandframework.bukkit.argument.NamespacedKeyArgument}
|
||||||
* (i.e. 'test' will be rejected but 'test:test' will pass).
|
* (i.e. 'test' will be rejected but 'test:test' will pass).
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package cloud.commandframework.bukkit.annotation.specifier;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import org.apiguardian.api.API;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation used to specify if an empty result is allowed for
|
||||||
|
* {@link cloud.commandframework.bukkit.parsers.selector.MultiplePlayerSelectorArgument} and
|
||||||
|
* {@link cloud.commandframework.bukkit.parsers.selector.MultipleEntitySelectorArgument}.
|
||||||
|
*
|
||||||
|
* @since 1.8.0
|
||||||
|
*/
|
||||||
|
@Target(ElementType.PARAMETER)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@API(status = API.Status.STABLE, since = "1.8.0")
|
||||||
|
public @interface AllowEmptySelection {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to allow empty results.
|
||||||
|
*
|
||||||
|
* @return value
|
||||||
|
*/
|
||||||
|
boolean value() default true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -25,17 +25,12 @@ package cloud.commandframework.bukkit.parsers.selector;
|
||||||
|
|
||||||
import cloud.commandframework.ArgumentDescription;
|
import cloud.commandframework.ArgumentDescription;
|
||||||
import cloud.commandframework.arguments.CommandArgument;
|
import cloud.commandframework.arguments.CommandArgument;
|
||||||
import cloud.commandframework.arguments.parser.ArgumentParseResult;
|
|
||||||
import cloud.commandframework.arguments.parser.ArgumentParser;
|
|
||||||
import cloud.commandframework.bukkit.BukkitCommandContextKeys;
|
|
||||||
import cloud.commandframework.bukkit.CloudBukkitCapabilities;
|
|
||||||
import cloud.commandframework.bukkit.arguments.selector.MultipleEntitySelector;
|
import cloud.commandframework.bukkit.arguments.selector.MultipleEntitySelector;
|
||||||
import cloud.commandframework.context.CommandContext;
|
import cloud.commandframework.context.CommandContext;
|
||||||
import cloud.commandframework.exceptions.parsing.NoInputProvidedException;
|
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import org.bukkit.Bukkit;
|
import org.apiguardian.api.API;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
@ -43,6 +38,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
public final class MultipleEntitySelectorArgument<C> extends CommandArgument<C, MultipleEntitySelector> {
|
public final class MultipleEntitySelectorArgument<C> extends CommandArgument<C, MultipleEntitySelector> {
|
||||||
|
|
||||||
private MultipleEntitySelectorArgument(
|
private MultipleEntitySelectorArgument(
|
||||||
|
final boolean allowEmpty,
|
||||||
final boolean required,
|
final boolean required,
|
||||||
final @NonNull String name,
|
final @NonNull String name,
|
||||||
final @NonNull String defaultValue,
|
final @NonNull String defaultValue,
|
||||||
|
|
@ -50,8 +46,8 @@ public final class MultipleEntitySelectorArgument<C> extends CommandArgument<C,
|
||||||
@NonNull List<@NonNull String>> suggestionsProvider,
|
@NonNull List<@NonNull String>> suggestionsProvider,
|
||||||
final @NonNull ArgumentDescription defaultDescription
|
final @NonNull ArgumentDescription defaultDescription
|
||||||
) {
|
) {
|
||||||
super(required, name, new MultipleEntitySelectorParser<>(), defaultValue, MultipleEntitySelector.class,
|
super(required, name, new MultipleEntitySelectorParser<>(allowEmpty), defaultValue,
|
||||||
suggestionsProvider, defaultDescription
|
MultipleEntitySelector.class, suggestionsProvider, defaultDescription
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,9 +57,25 @@ public final class MultipleEntitySelectorArgument<C> extends CommandArgument<C,
|
||||||
* @param name Name of the argument
|
* @param name Name of the argument
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created builder
|
* @return Created builder
|
||||||
|
* @deprecated prefer {@link #builder(String)}
|
||||||
*/
|
*/
|
||||||
public static <C> MultipleEntitySelectorArgument.@NonNull Builder<C> newBuilder(final @NonNull String name) {
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
return new MultipleEntitySelectorArgument.Builder<>(name);
|
@Deprecated
|
||||||
|
public static <C> Builder<C> newBuilder(final @NonNull String name) {
|
||||||
|
return builder(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link Builder}.
|
||||||
|
*
|
||||||
|
* @param name argument name
|
||||||
|
* @param <C> sender type
|
||||||
|
* @return new builder
|
||||||
|
* @since 1.8.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.8.0")
|
||||||
|
public static <C> @NonNull Builder<C> builder(final @NonNull String name) {
|
||||||
|
return new Builder<>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -73,8 +85,8 @@ public final class MultipleEntitySelectorArgument<C> extends CommandArgument<C,
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created argument
|
* @return Created argument
|
||||||
*/
|
*/
|
||||||
public static <C> @NonNull CommandArgument<C, MultipleEntitySelector> of(final @NonNull String name) {
|
public static <C> @NonNull MultipleEntitySelectorArgument<C> of(final @NonNull String name) {
|
||||||
return MultipleEntitySelectorArgument.<C>newBuilder(name).asRequired().build();
|
return MultipleEntitySelectorArgument.<C>builder(name).asRequired().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -84,8 +96,8 @@ public final class MultipleEntitySelectorArgument<C> extends CommandArgument<C,
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created argument
|
* @return Created argument
|
||||||
*/
|
*/
|
||||||
public static <C> @NonNull CommandArgument<C, MultipleEntitySelector> optional(final @NonNull String name) {
|
public static <C> @NonNull MultipleEntitySelectorArgument<C> optional(final @NonNull String name) {
|
||||||
return MultipleEntitySelectorArgument.<C>newBuilder(name).asOptional().build();
|
return MultipleEntitySelectorArgument.<C>builder(name).asOptional().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -96,20 +108,35 @@ public final class MultipleEntitySelectorArgument<C> extends CommandArgument<C,
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created argument
|
* @return Created argument
|
||||||
*/
|
*/
|
||||||
public static <C> @NonNull CommandArgument<C, MultipleEntitySelector> optional(
|
public static <C> @NonNull MultipleEntitySelectorArgument<C> optional(
|
||||||
final @NonNull String name,
|
final @NonNull String name,
|
||||||
final @NonNull String defaultEntitySelector
|
final @NonNull String defaultEntitySelector
|
||||||
) {
|
) {
|
||||||
return MultipleEntitySelectorArgument.<C>newBuilder(name).asOptionalWithDefault(defaultEntitySelector).build();
|
return MultipleEntitySelectorArgument.<C>builder(name).asOptionalWithDefault(defaultEntitySelector).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final class Builder<C> extends CommandArgument.Builder<C, MultipleEntitySelector> {
|
public static final class Builder<C> extends CommandArgument.TypedBuilder<C, MultipleEntitySelector, Builder<C>> {
|
||||||
|
|
||||||
|
private boolean allowEmpty = true;
|
||||||
|
|
||||||
private Builder(final @NonNull String name) {
|
private Builder(final @NonNull String name) {
|
||||||
super(MultipleEntitySelector.class, name);
|
super(MultipleEntitySelector.class, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to allow empty results.
|
||||||
|
*
|
||||||
|
* @param allowEmpty whether to allow empty results
|
||||||
|
* @return builder instance
|
||||||
|
* @since 1.8.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.8.0")
|
||||||
|
public @NonNull Builder<C> allowEmpty(final boolean allowEmpty) {
|
||||||
|
this.allowEmpty = allowEmpty;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder a new argument
|
* Builder a new argument
|
||||||
*
|
*
|
||||||
|
|
@ -117,51 +144,51 @@ public final class MultipleEntitySelectorArgument<C> extends CommandArgument<C,
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public @NonNull MultipleEntitySelectorArgument<C> build() {
|
public @NonNull MultipleEntitySelectorArgument<C> build() {
|
||||||
return new MultipleEntitySelectorArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(),
|
return new MultipleEntitySelectorArgument<>(
|
||||||
this.getSuggestionsProvider(), this.getDefaultDescription()
|
this.allowEmpty,
|
||||||
|
this.isRequired(),
|
||||||
|
this.getName(),
|
||||||
|
this.getDefaultValue(),
|
||||||
|
this.getSuggestionsProvider(),
|
||||||
|
this.getDefaultDescription()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final class MultipleEntitySelectorParser<C> implements ArgumentParser<C, MultipleEntitySelector> {
|
public static final class MultipleEntitySelectorParser<C> extends SelectorUtils.EntitySelectorParser<C, MultipleEntitySelector> {
|
||||||
|
|
||||||
|
private final boolean allowEmpty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link MultipleEntitySelectorParser}.
|
||||||
|
*
|
||||||
|
* @param allowEmpty Whether to allow an empty result
|
||||||
|
* @since 1.8.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.8.0")
|
||||||
|
public MultipleEntitySelectorParser(final boolean allowEmpty) {
|
||||||
|
super(false);
|
||||||
|
this.allowEmpty = allowEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link MultipleEntitySelectorParser}.
|
||||||
|
*/
|
||||||
|
public MultipleEntitySelectorParser() {
|
||||||
|
this(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull ArgumentParseResult<MultipleEntitySelector> parse(
|
public MultipleEntitySelector mapResult(
|
||||||
final @NonNull CommandContext<C> commandContext,
|
final @NonNull String input,
|
||||||
final @NonNull Queue<@NonNull String> inputQueue
|
final SelectorUtils.@NonNull EntitySelectorWrapper wrapper
|
||||||
) {
|
) throws Exception {
|
||||||
if (!commandContext.get(BukkitCommandContextKeys.CLOUD_BUKKIT_CAPABILITIES).contains(
|
final List<Entity> entities = wrapper.entities();
|
||||||
CloudBukkitCapabilities.BRIGADIER)) {
|
if (entities.isEmpty() && !this.allowEmpty) {
|
||||||
return ArgumentParseResult.failure(new SelectorParseException(
|
throw ((SimpleCommandExceptionType) NO_ENTITIES_EXCEPTION_TYPE.get()).create();
|
||||||
"",
|
|
||||||
commandContext,
|
|
||||||
SelectorParseException.FailureReason.UNSUPPORTED_VERSION,
|
|
||||||
MultipleEntitySelectorParser.class
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
final String input = inputQueue.peek();
|
return new MultipleEntitySelector(input, entities);
|
||||||
if (input == null) {
|
|
||||||
return ArgumentParseResult.failure(new NoInputProvidedException(
|
|
||||||
MultipleEntitySelectorParser.class,
|
|
||||||
commandContext
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Entity> entities;
|
|
||||||
try {
|
|
||||||
entities = Bukkit.selectEntities(commandContext.get(BukkitCommandContextKeys.BUKKIT_COMMAND_SENDER), input);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return ArgumentParseResult.failure(new SelectorParseException(
|
|
||||||
input,
|
|
||||||
commandContext,
|
|
||||||
SelectorParseException.FailureReason.MALFORMED_SELECTOR,
|
|
||||||
MultipleEntitySelectorParser.class
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
inputQueue.remove();
|
|
||||||
return ArgumentParseResult.success(new MultipleEntitySelector(input, entities));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,21 +26,17 @@ package cloud.commandframework.bukkit.parsers.selector;
|
||||||
import cloud.commandframework.ArgumentDescription;
|
import cloud.commandframework.ArgumentDescription;
|
||||||
import cloud.commandframework.arguments.CommandArgument;
|
import cloud.commandframework.arguments.CommandArgument;
|
||||||
import cloud.commandframework.arguments.parser.ArgumentParseResult;
|
import cloud.commandframework.arguments.parser.ArgumentParseResult;
|
||||||
import cloud.commandframework.arguments.parser.ArgumentParser;
|
|
||||||
import cloud.commandframework.bukkit.BukkitCommandContextKeys;
|
|
||||||
import cloud.commandframework.bukkit.CloudBukkitCapabilities;
|
|
||||||
import cloud.commandframework.bukkit.arguments.selector.MultiplePlayerSelector;
|
import cloud.commandframework.bukkit.arguments.selector.MultiplePlayerSelector;
|
||||||
import cloud.commandframework.bukkit.parsers.PlayerArgument;
|
import cloud.commandframework.bukkit.parsers.PlayerArgument;
|
||||||
import cloud.commandframework.context.CommandContext;
|
import cloud.commandframework.context.CommandContext;
|
||||||
import cloud.commandframework.exceptions.parsing.NoInputProvidedException;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
import org.apiguardian.api.API;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
@ -48,6 +44,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
public final class MultiplePlayerSelectorArgument<C> extends CommandArgument<C, MultiplePlayerSelector> {
|
public final class MultiplePlayerSelectorArgument<C> extends CommandArgument<C, MultiplePlayerSelector> {
|
||||||
|
|
||||||
private MultiplePlayerSelectorArgument(
|
private MultiplePlayerSelectorArgument(
|
||||||
|
final boolean allowEmpty,
|
||||||
final boolean required,
|
final boolean required,
|
||||||
final @NonNull String name,
|
final @NonNull String name,
|
||||||
final @NonNull String defaultValue,
|
final @NonNull String defaultValue,
|
||||||
|
|
@ -55,7 +52,7 @@ public final class MultiplePlayerSelectorArgument<C> extends CommandArgument<C,
|
||||||
@NonNull List<@NonNull String>> suggestionsProvider,
|
@NonNull List<@NonNull String>> suggestionsProvider,
|
||||||
final @NonNull ArgumentDescription defaultDescription
|
final @NonNull ArgumentDescription defaultDescription
|
||||||
) {
|
) {
|
||||||
super(required, name, new MultiplePlayerSelectorParser<>(), defaultValue, MultiplePlayerSelector.class,
|
super(required, name, new MultiplePlayerSelectorParser<>(allowEmpty), defaultValue, MultiplePlayerSelector.class,
|
||||||
suggestionsProvider, defaultDescription
|
suggestionsProvider, defaultDescription
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -66,9 +63,25 @@ public final class MultiplePlayerSelectorArgument<C> extends CommandArgument<C,
|
||||||
* @param name Name of the argument
|
* @param name Name of the argument
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created builder
|
* @return Created builder
|
||||||
|
* @deprecated prefer {@link #builder(String)}
|
||||||
*/
|
*/
|
||||||
public static <C> MultiplePlayerSelectorArgument.Builder<C> newBuilder(final @NonNull String name) {
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
return new MultiplePlayerSelectorArgument.Builder<>(name);
|
@Deprecated
|
||||||
|
public static <C> Builder<C> newBuilder(final @NonNull String name) {
|
||||||
|
return builder(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link Builder}.
|
||||||
|
*
|
||||||
|
* @param name argument name
|
||||||
|
* @param <C> sender type
|
||||||
|
* @return new builder
|
||||||
|
* @since 1.8.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.8.0")
|
||||||
|
public static <C> @NonNull Builder<C> builder(final @NonNull String name) {
|
||||||
|
return new Builder<>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -78,8 +91,8 @@ public final class MultiplePlayerSelectorArgument<C> extends CommandArgument<C,
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created argument
|
* @return Created argument
|
||||||
*/
|
*/
|
||||||
public static <C> @NonNull CommandArgument<C, MultiplePlayerSelector> of(final @NonNull String name) {
|
public static <C> @NonNull MultiplePlayerSelectorArgument<C> of(final @NonNull String name) {
|
||||||
return MultiplePlayerSelectorArgument.<C>newBuilder(name).asRequired().build();
|
return MultiplePlayerSelectorArgument.<C>builder(name).asRequired().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -89,8 +102,8 @@ public final class MultiplePlayerSelectorArgument<C> extends CommandArgument<C,
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created argument
|
* @return Created argument
|
||||||
*/
|
*/
|
||||||
public static <C> @NonNull CommandArgument<C, MultiplePlayerSelector> optional(final @NonNull String name) {
|
public static <C> @NonNull MultiplePlayerSelectorArgument<C> optional(final @NonNull String name) {
|
||||||
return MultiplePlayerSelectorArgument.<C>newBuilder(name).asOptional().build();
|
return MultiplePlayerSelectorArgument.<C>builder(name).asOptional().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -101,20 +114,35 @@ public final class MultiplePlayerSelectorArgument<C> extends CommandArgument<C,
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created argument
|
* @return Created argument
|
||||||
*/
|
*/
|
||||||
public static <C> @NonNull CommandArgument<C, MultiplePlayerSelector> optional(
|
public static <C> @NonNull MultiplePlayerSelectorArgument<C> optional(
|
||||||
final @NonNull String name,
|
final @NonNull String name,
|
||||||
final @NonNull String defaultEntitySelector
|
final @NonNull String defaultEntitySelector
|
||||||
) {
|
) {
|
||||||
return MultiplePlayerSelectorArgument.<C>newBuilder(name).asOptionalWithDefault(defaultEntitySelector).build();
|
return MultiplePlayerSelectorArgument.<C>builder(name).asOptionalWithDefault(defaultEntitySelector).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final class Builder<C> extends CommandArgument.Builder<C, MultiplePlayerSelector> {
|
public static final class Builder<C> extends CommandArgument.TypedBuilder<C, MultiplePlayerSelector, Builder<C>> {
|
||||||
|
|
||||||
|
private boolean allowEmpty = true;
|
||||||
|
|
||||||
private Builder(final @NonNull String name) {
|
private Builder(final @NonNull String name) {
|
||||||
super(MultiplePlayerSelector.class, name);
|
super(MultiplePlayerSelector.class, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to allow empty results.
|
||||||
|
*
|
||||||
|
* @param allowEmpty whether to allow empty results
|
||||||
|
* @return builder instance
|
||||||
|
* @since 1.8.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.8.0")
|
||||||
|
public @NonNull Builder<C> allowEmpty(final boolean allowEmpty) {
|
||||||
|
this.allowEmpty = allowEmpty;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder a new argument
|
* Builder a new argument
|
||||||
*
|
*
|
||||||
|
|
@ -122,83 +150,66 @@ public final class MultiplePlayerSelectorArgument<C> extends CommandArgument<C,
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public @NonNull MultiplePlayerSelectorArgument<C> build() {
|
public @NonNull MultiplePlayerSelectorArgument<C> build() {
|
||||||
return new MultiplePlayerSelectorArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(),
|
return new MultiplePlayerSelectorArgument<>(
|
||||||
this.getSuggestionsProvider(), this.getDefaultDescription()
|
this.allowEmpty,
|
||||||
|
this.isRequired(),
|
||||||
|
this.getName(),
|
||||||
|
this.getDefaultValue(),
|
||||||
|
this.getSuggestionsProvider(),
|
||||||
|
this.getDefaultDescription()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final class MultiplePlayerSelectorParser<C> implements ArgumentParser<C, MultiplePlayerSelector> {
|
public static final class MultiplePlayerSelectorParser<C> extends SelectorUtils.PlayerSelectorParser<C, MultiplePlayerSelector> {
|
||||||
|
|
||||||
|
private final boolean allowEmpty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link MultiplePlayerSelectorParser}.
|
||||||
|
*
|
||||||
|
* @param allowEmpty Whether to allow an empty result
|
||||||
|
* @since 1.8.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.8.0")
|
||||||
|
public MultiplePlayerSelectorParser(final boolean allowEmpty) {
|
||||||
|
super(false);
|
||||||
|
this.allowEmpty = allowEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link MultiplePlayerSelectorParser}.
|
||||||
|
*/
|
||||||
|
public MultiplePlayerSelectorParser() {
|
||||||
|
this(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull ArgumentParseResult<MultiplePlayerSelector> parse(
|
public MultiplePlayerSelector mapResult(
|
||||||
|
final @NonNull String input,
|
||||||
|
final SelectorUtils.@NonNull EntitySelectorWrapper wrapper
|
||||||
|
) throws Exception {
|
||||||
|
final List<Player> players = wrapper.players();
|
||||||
|
if (players.isEmpty() && !this.allowEmpty) {
|
||||||
|
throw ((SimpleCommandExceptionType) NO_PLAYERS_EXCEPTION_TYPE.get()).create();
|
||||||
|
}
|
||||||
|
return new MultiplePlayerSelector(input, new ArrayList<>(players));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NonNull ArgumentParseResult<MultiplePlayerSelector> legacyParse(
|
||||||
final @NonNull CommandContext<C> commandContext,
|
final @NonNull CommandContext<C> commandContext,
|
||||||
final @NonNull Queue<@NonNull String> inputQueue
|
final @NonNull Queue<@NonNull String> inputQueue
|
||||||
) {
|
) {
|
||||||
final String input = inputQueue.peek();
|
final String input = inputQueue.peek();
|
||||||
if (input == null) {
|
@SuppressWarnings("deprecation") final @Nullable Player player = Bukkit.getPlayer(input);
|
||||||
return ArgumentParseResult.failure(new NoInputProvidedException(
|
|
||||||
MultiplePlayerSelectorParser.class,
|
if (player == null) {
|
||||||
commandContext
|
return ArgumentParseResult.failure(new PlayerArgument.PlayerParseException(input, commandContext));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!commandContext.get(BukkitCommandContextKeys.CLOUD_BUKKIT_CAPABILITIES).contains(
|
|
||||||
CloudBukkitCapabilities.BRIGADIER)) {
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
Player player = Bukkit.getPlayer(input);
|
|
||||||
|
|
||||||
if (player == null) {
|
|
||||||
return ArgumentParseResult.failure(new PlayerArgument.PlayerParseException(input, commandContext));
|
|
||||||
}
|
|
||||||
inputQueue.remove();
|
|
||||||
return ArgumentParseResult.success(new MultiplePlayerSelector(input, ImmutableList.of(player)));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Entity> entities;
|
|
||||||
try {
|
|
||||||
entities = Bukkit.selectEntities(commandContext.get(BukkitCommandContextKeys.BUKKIT_COMMAND_SENDER), input);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return ArgumentParseResult.failure(new SelectorParseException(
|
|
||||||
input,
|
|
||||||
commandContext,
|
|
||||||
SelectorParseException.FailureReason.MALFORMED_SELECTOR,
|
|
||||||
MultiplePlayerSelectorParser.class
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Entity e : entities) {
|
|
||||||
if (!(e instanceof Player)) {
|
|
||||||
return ArgumentParseResult.failure(new SelectorParseException(
|
|
||||||
input,
|
|
||||||
commandContext,
|
|
||||||
SelectorParseException.FailureReason.NON_PLAYER_IN_PLAYER_SELECTOR,
|
|
||||||
MultiplePlayerSelectorParser.class
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inputQueue.remove();
|
inputQueue.remove();
|
||||||
return ArgumentParseResult.success(new MultiplePlayerSelector(input, entities));
|
return ArgumentParseResult.success(new MultiplePlayerSelector(input, ImmutableList.of(player)));
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull List<@NonNull String> suggestions(
|
|
||||||
final @NonNull CommandContext<C> commandContext,
|
|
||||||
final @NonNull String input
|
|
||||||
) {
|
|
||||||
List<String> output = new ArrayList<>();
|
|
||||||
|
|
||||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
|
||||||
final CommandSender bukkit = commandContext.get(BukkitCommandContextKeys.BUKKIT_COMMAND_SENDER);
|
|
||||||
if (bukkit instanceof Player && !((Player) bukkit).canSee(player)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
output.add(player.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import cloud.commandframework.captions.Caption;
|
||||||
import cloud.commandframework.captions.CaptionVariable;
|
import cloud.commandframework.captions.CaptionVariable;
|
||||||
import cloud.commandframework.context.CommandContext;
|
import cloud.commandframework.context.CommandContext;
|
||||||
import cloud.commandframework.exceptions.parsing.ParserException;
|
import cloud.commandframework.exceptions.parsing.ParserException;
|
||||||
|
import org.apiguardian.api.API;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -73,7 +74,10 @@ public final class SelectorParseException extends ParserException {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the reason of failure for the selector parser
|
* Get the reason of failure for the selector parser.
|
||||||
|
*
|
||||||
|
* <p>Note: The only type currently used is {@link FailureReason#UNSUPPORTED_VERSION}, other exceptions
|
||||||
|
* are now handled by Brigadier in the form of {@link com.mojang.brigadier.exceptions.CommandSyntaxException}.</p>
|
||||||
*
|
*
|
||||||
* @return Failure reason
|
* @return Failure reason
|
||||||
* @since 1.2.0
|
* @since 1.2.0
|
||||||
|
|
@ -90,9 +94,29 @@ public final class SelectorParseException extends ParserException {
|
||||||
public enum FailureReason {
|
public enum FailureReason {
|
||||||
|
|
||||||
UNSUPPORTED_VERSION(BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_SELECTOR_UNSUPPORTED),
|
UNSUPPORTED_VERSION(BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_SELECTOR_UNSUPPORTED),
|
||||||
|
/**
|
||||||
|
* @deprecated parsing is now handled by Brigadier and will throw {@link com.mojang.brigadier.exceptions.CommandSyntaxException} instead.
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
|
@Deprecated
|
||||||
MALFORMED_SELECTOR(BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_SELECTOR_MALFORMED),
|
MALFORMED_SELECTOR(BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_SELECTOR_MALFORMED),
|
||||||
|
/**
|
||||||
|
* @deprecated parsing is now handled by Brigadier and will throw {@link com.mojang.brigadier.exceptions.CommandSyntaxException} instead.
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
|
@Deprecated
|
||||||
TOO_MANY_PLAYERS(BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_PLAYERS),
|
TOO_MANY_PLAYERS(BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_PLAYERS),
|
||||||
|
/**
|
||||||
|
* @deprecated parsing is now handled by Brigadier and will throw {@link com.mojang.brigadier.exceptions.CommandSyntaxException} instead.
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
|
@Deprecated
|
||||||
TOO_MANY_ENTITIES(BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_ENTITIES),
|
TOO_MANY_ENTITIES(BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_SELECTOR_TOO_MANY_ENTITIES),
|
||||||
|
/**
|
||||||
|
* @deprecated parsing is now handled by Brigadier and will throw {@link com.mojang.brigadier.exceptions.CommandSyntaxException} instead.
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
|
@Deprecated
|
||||||
NON_PLAYER_IN_PLAYER_SELECTOR(BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_SELECTOR_NON_PLAYER);
|
NON_PLAYER_IN_PLAYER_SELECTOR(BukkitCaptionKeys.ARGUMENT_PARSE_FAILURE_SELECTOR_NON_PLAYER);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,472 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021 Alexander Söderberg & Contributors
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
package cloud.commandframework.bukkit.parsers.selector;
|
||||||
|
|
||||||
|
import cloud.commandframework.arguments.parser.ArgumentParseResult;
|
||||||
|
import cloud.commandframework.arguments.parser.ArgumentParser;
|
||||||
|
import cloud.commandframework.brigadier.argument.WrappedBrigadierParser;
|
||||||
|
import cloud.commandframework.bukkit.BukkitCommandContextKeys;
|
||||||
|
import cloud.commandframework.bukkit.internal.CraftBukkitReflection;
|
||||||
|
import cloud.commandframework.bukkit.internal.MinecraftArgumentTypes;
|
||||||
|
import cloud.commandframework.context.CommandContext;
|
||||||
|
import com.google.common.base.Suppliers;
|
||||||
|
import com.mojang.brigadier.StringReader;
|
||||||
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||||
|
import io.leangen.geantyref.GenericTypeReflector;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.lang.reflect.WildcardType;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.NamespacedKey;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
|
||||||
|
@DefaultQualifier(NonNull.class)
|
||||||
|
final class SelectorUtils {
|
||||||
|
|
||||||
|
private SelectorUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <C, T> @Nullable ArgumentParser<C, T> createModernParser(
|
||||||
|
final boolean single,
|
||||||
|
final boolean playersOnly,
|
||||||
|
final SelectorMapper<T> mapper
|
||||||
|
) {
|
||||||
|
if (CraftBukkitReflection.MAJOR_REVISION < 13) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final ArgumentParser<C, Object> wrappedBrigParser = new WrappedBrigadierParser<>(
|
||||||
|
() -> createEntityArgument(single, playersOnly),
|
||||||
|
ArgumentParser.DEFAULT_ARGUMENT_COUNT,
|
||||||
|
EntityArgumentParseFunction.INSTANCE
|
||||||
|
);
|
||||||
|
return new ModernSelectorParser<>(wrappedBrigParser, mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static ArgumentType<Object> createEntityArgument(final boolean single, final boolean playersOnly) {
|
||||||
|
final Constructor<?> constructor =
|
||||||
|
MinecraftArgumentTypes.getClassByKey(NamespacedKey.minecraft("entity")).getDeclaredConstructors()[0];
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
try {
|
||||||
|
return (ArgumentType<Object>) constructor.newInstance(single, playersOnly);
|
||||||
|
} catch (final ReflectiveOperationException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class EntityArgumentParseFunction implements WrappedBrigadierParser.ParseFunction<Object> {
|
||||||
|
static final EntityArgumentParseFunction INSTANCE = new EntityArgumentParseFunction();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object apply(
|
||||||
|
final ArgumentType<Object> type,
|
||||||
|
final StringReader reader
|
||||||
|
) throws CommandSyntaxException {
|
||||||
|
final @Nullable Method specialParse = CraftBukkitReflection.findMethod(
|
||||||
|
type.getClass(),
|
||||||
|
"parse",
|
||||||
|
StringReader.class,
|
||||||
|
boolean.class
|
||||||
|
);
|
||||||
|
if (specialParse == null) {
|
||||||
|
return type.parse(reader);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return specialParse.invoke(
|
||||||
|
type,
|
||||||
|
reader,
|
||||||
|
true // CraftBukkit overridePermissions param
|
||||||
|
);
|
||||||
|
} catch (final InvocationTargetException ex) {
|
||||||
|
final Throwable cause = ex.getCause();
|
||||||
|
if (cause instanceof CommandSyntaxException) {
|
||||||
|
throw (CommandSyntaxException) cause;
|
||||||
|
}
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
} catch (final ReflectiveOperationException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract static class SelectorParser<C, T> implements ArgumentParser<C, T>, SelectorMapper<T> {
|
||||||
|
protected static final Supplier<Object> NO_PLAYERS_EXCEPTION_TYPE =
|
||||||
|
Suppliers.memoize(() -> findExceptionType("argument.entity.notfound.player"));
|
||||||
|
protected static final Supplier<Object> NO_ENTITIES_EXCEPTION_TYPE =
|
||||||
|
Suppliers.memoize(() -> findExceptionType("argument.entity.notfound.entity"));
|
||||||
|
|
||||||
|
private final @Nullable ArgumentParser<C, T> modernParser;
|
||||||
|
|
||||||
|
protected SelectorParser(
|
||||||
|
final boolean single,
|
||||||
|
final boolean playersOnly
|
||||||
|
) {
|
||||||
|
this.modernParser = createModernParser(single, playersOnly, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ArgumentParseResult<T> legacyParse(
|
||||||
|
final CommandContext<C> commandContext,
|
||||||
|
final Queue<String> inputQueue
|
||||||
|
) {
|
||||||
|
return ArgumentParseResult.failure(new SelectorParseException(
|
||||||
|
"",
|
||||||
|
commandContext,
|
||||||
|
SelectorParseException.FailureReason.UNSUPPORTED_VERSION,
|
||||||
|
this.getClass()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<String> legacySuggestions(
|
||||||
|
final CommandContext<C> commandContext,
|
||||||
|
final String input
|
||||||
|
) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentParseResult<T> parse(
|
||||||
|
final CommandContext<C> commandContext,
|
||||||
|
final Queue<String> inputQueue
|
||||||
|
) {
|
||||||
|
if (this.modernParser != null) {
|
||||||
|
return this.modernParser.parse(commandContext, inputQueue);
|
||||||
|
}
|
||||||
|
return this.legacyParse(commandContext, inputQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> suggestions(
|
||||||
|
final CommandContext<C> commandContext,
|
||||||
|
final String input
|
||||||
|
) {
|
||||||
|
if (this.modernParser != null) {
|
||||||
|
return this.modernParser.suggestions(commandContext, input);
|
||||||
|
}
|
||||||
|
return this.legacySuggestions(commandContext, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns SimpleCommandExceptionType, does not reference in signature for ABI with pre-1.13
|
||||||
|
private static Object findExceptionType(final String type) {
|
||||||
|
final Field[] fields = MinecraftArgumentTypes.getClassByKey(NamespacedKey.minecraft("entity")).getDeclaredFields();
|
||||||
|
return Arrays.stream(fields)
|
||||||
|
.filter(field -> Modifier.isStatic(field.getModifiers()) && field.getType() == SimpleCommandExceptionType.class)
|
||||||
|
.map(field -> {
|
||||||
|
try {
|
||||||
|
final @Nullable Object fieldValue = field.get(null);
|
||||||
|
if (fieldValue == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final Field messageField = SimpleCommandExceptionType.class.getDeclaredField("message");
|
||||||
|
messageField.setAccessible(true);
|
||||||
|
if (messageField.get(fieldValue).toString().contains(type)) {
|
||||||
|
return fieldValue;
|
||||||
|
}
|
||||||
|
} catch (final ReflectiveOperationException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Could not find exception type '" + type + "'"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract static class EntitySelectorParser<C, T> extends SelectorParser<C, T> {
|
||||||
|
protected EntitySelectorParser(final boolean single) {
|
||||||
|
super(single, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract static class PlayerSelectorParser<C, T> extends SelectorParser<C, T> {
|
||||||
|
protected PlayerSelectorParser(final boolean single) {
|
||||||
|
super(single, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> legacySuggestions(
|
||||||
|
final CommandContext<C> commandContext,
|
||||||
|
final String input
|
||||||
|
) {
|
||||||
|
final List<String> suggestions = new ArrayList<>();
|
||||||
|
|
||||||
|
for (final Player player : Bukkit.getOnlinePlayers()) {
|
||||||
|
final CommandSender bukkit = commandContext.get(BukkitCommandContextKeys.BUKKIT_COMMAND_SENDER);
|
||||||
|
if (bukkit instanceof Player && !((Player) bukkit).canSee(player)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
suggestions.add(player.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return suggestions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ModernSelectorParser<C, T> implements ArgumentParser<C, T> {
|
||||||
|
private final ArgumentParser<C, Object> wrappedBrigadierParser;
|
||||||
|
private final SelectorMapper<T> mapper;
|
||||||
|
|
||||||
|
ModernSelectorParser(
|
||||||
|
final ArgumentParser<C, Object> wrapperBrigParser,
|
||||||
|
final SelectorMapper<T> mapper
|
||||||
|
) {
|
||||||
|
this.wrappedBrigadierParser = wrapperBrigParser;
|
||||||
|
this.mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentParseResult<T> parse(
|
||||||
|
final CommandContext<C> commandContext,
|
||||||
|
final Queue<String> inputQueue
|
||||||
|
) {
|
||||||
|
final List<String> originalInputQueue = new ArrayList<>(inputQueue);
|
||||||
|
|
||||||
|
final ArgumentParseResult<Object> result = this.wrappedBrigadierParser.parse(commandContext, inputQueue);
|
||||||
|
if (result.getFailure().isPresent()) {
|
||||||
|
return ArgumentParseResult.failure(result.getFailure().get());
|
||||||
|
} else if (result.getParsedValue().isPresent()) {
|
||||||
|
try {
|
||||||
|
final int consumed = originalInputQueue.size() - inputQueue.size();
|
||||||
|
final String input = String.join(" ", originalInputQueue.subList(0, consumed));
|
||||||
|
return ArgumentParseResult.success(this.mapper.mapResult(
|
||||||
|
input,
|
||||||
|
new EntitySelectorWrapper(commandContext, result.getParsedValue().get())
|
||||||
|
));
|
||||||
|
} catch (final CommandSyntaxException ex) {
|
||||||
|
inputQueue.clear();
|
||||||
|
inputQueue.addAll(originalInputQueue);
|
||||||
|
return ArgumentParseResult.failure(ex);
|
||||||
|
} catch (final Exception ex) {
|
||||||
|
throw rethrow(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> suggestions(
|
||||||
|
final CommandContext<C> commandContext,
|
||||||
|
final String input
|
||||||
|
) {
|
||||||
|
return this.wrappedBrigadierParser.suggestions(commandContext, input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class EntitySelectorWrapper {
|
||||||
|
private static volatile @Nullable Methods methods;
|
||||||
|
|
||||||
|
private final CommandContext<?> commandContext;
|
||||||
|
private final Object selector;
|
||||||
|
|
||||||
|
private static final class Methods {
|
||||||
|
private Method getBukkitEntity;
|
||||||
|
private Method entity;
|
||||||
|
private Method player;
|
||||||
|
private Method entities;
|
||||||
|
private Method players;
|
||||||
|
|
||||||
|
Methods(final CommandContext<?> commandContext, final Object selector) {
|
||||||
|
final Object nativeSender = commandContext.get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER);
|
||||||
|
final Class<?> nativeSenderClass = nativeSender.getClass();
|
||||||
|
for (final Method method : selector.getClass().getDeclaredMethods()) {
|
||||||
|
if (method.getParameterCount() != 1 || !method.getParameterTypes()[0].equals(nativeSenderClass)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Class<?> returnType = method.getReturnType();
|
||||||
|
if (List.class.isAssignableFrom(returnType)) {
|
||||||
|
final ParameterizedType stringListType = (ParameterizedType) method.getGenericReturnType();
|
||||||
|
Type listType = stringListType.getActualTypeArguments()[0];
|
||||||
|
while (listType instanceof WildcardType) {
|
||||||
|
listType = ((WildcardType) listType).getUpperBounds()[0];
|
||||||
|
}
|
||||||
|
final Class<?> clazz = listType instanceof Class
|
||||||
|
? (Class<?>) listType
|
||||||
|
: GenericTypeReflector.erase(listType);
|
||||||
|
final @Nullable Method getBukkitEntity = findGetBukkitEntityMethod(clazz);
|
||||||
|
if (getBukkitEntity == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final Class<?> bukkitType = getBukkitEntity.getReturnType();
|
||||||
|
if (Player.class.isAssignableFrom(bukkitType)) {
|
||||||
|
if (this.players != null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
this.players = method;
|
||||||
|
} else {
|
||||||
|
if (this.entities != null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
this.entities = method;
|
||||||
|
}
|
||||||
|
} else if (returnType != Void.TYPE) {
|
||||||
|
final @Nullable Method getBukkitEntity = findGetBukkitEntityMethod(returnType);
|
||||||
|
if (getBukkitEntity == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final Class<?> bukkitType = getBukkitEntity.getReturnType();
|
||||||
|
if (Player.class.isAssignableFrom(bukkitType)) {
|
||||||
|
if (this.player != null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
this.player = method;
|
||||||
|
} else {
|
||||||
|
if (this.entity != null || this.getBukkitEntity != null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
this.entity = method;
|
||||||
|
this.getBukkitEntity = getBukkitEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Objects.requireNonNull(this.getBukkitEntity);
|
||||||
|
Objects.requireNonNull(this.player);
|
||||||
|
Objects.requireNonNull(this.entity);
|
||||||
|
Objects.requireNonNull(this.players);
|
||||||
|
Objects.requireNonNull(this.entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @Nullable Method findGetBukkitEntityMethod(final Class<?> returnType) {
|
||||||
|
@Nullable Method getBukkitEntity;
|
||||||
|
try {
|
||||||
|
getBukkitEntity = returnType.getDeclaredMethod("getBukkitEntity");
|
||||||
|
} catch (final ReflectiveOperationException ex) {
|
||||||
|
try {
|
||||||
|
getBukkitEntity = returnType.getMethod("getBukkitEntity");
|
||||||
|
} catch (final ReflectiveOperationException ex0) {
|
||||||
|
getBukkitEntity = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getBukkitEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EntitySelectorWrapper(
|
||||||
|
final CommandContext<?> commandContext,
|
||||||
|
final Object selector
|
||||||
|
) {
|
||||||
|
this.commandContext = commandContext;
|
||||||
|
this.selector = selector;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Methods methods(final CommandContext<?> commandContext, final Object selector) {
|
||||||
|
if (methods == null) {
|
||||||
|
synchronized (Methods.class) {
|
||||||
|
if (methods == null) {
|
||||||
|
methods = new Methods(commandContext, selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Methods methods() {
|
||||||
|
return methods(this.commandContext, this.selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity singleEntity() {
|
||||||
|
return reflectiveOperation(() -> (Entity) this.methods().getBukkitEntity.invoke(this.methods().entity.invoke(
|
||||||
|
this.selector,
|
||||||
|
this.commandContext.<Object>get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER)
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Player singlePlayer() {
|
||||||
|
return reflectiveOperation(() -> (Player) this.methods().getBukkitEntity.invoke(this.methods().player.invoke(
|
||||||
|
this.selector,
|
||||||
|
this.commandContext.<Object>get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER)
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Entity> entities() {
|
||||||
|
return reflectiveOperation(() -> ((List<Object>) this.methods().entities.invoke(
|
||||||
|
this.selector,
|
||||||
|
this.commandContext.<Object>get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER)
|
||||||
|
))
|
||||||
|
.stream()
|
||||||
|
.map(o -> reflectiveOperation(() -> (Entity) this.methods().getBukkitEntity.invoke(o)))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Player> players() {
|
||||||
|
return reflectiveOperation(() -> ((List<Object>) this.methods().players.invoke(
|
||||||
|
this.selector,
|
||||||
|
this.commandContext.<Object>get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER)
|
||||||
|
))
|
||||||
|
.stream()
|
||||||
|
.map(o -> reflectiveOperation(() -> (Player) this.methods().getBukkitEntity.invoke(o)))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface ReflectiveOperation<T> {
|
||||||
|
T run() throws ReflectiveOperationException;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T reflectiveOperation(final ReflectiveOperation<T> op) {
|
||||||
|
try {
|
||||||
|
return op.run();
|
||||||
|
} catch (final InvocationTargetException ex) {
|
||||||
|
if (ex.getCause() instanceof CommandSyntaxException) {
|
||||||
|
throw rethrow(ex.getCause());
|
||||||
|
}
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
} catch (final ReflectiveOperationException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface SelectorMapper<T> {
|
||||||
|
T mapResult(String input, EntitySelectorWrapper wrapper) throws Exception; // throws CommandSyntaxException
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <X extends Throwable> RuntimeException rethrow(final Throwable t) throws X {
|
||||||
|
throw (X) t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,18 +25,12 @@ package cloud.commandframework.bukkit.parsers.selector;
|
||||||
|
|
||||||
import cloud.commandframework.ArgumentDescription;
|
import cloud.commandframework.ArgumentDescription;
|
||||||
import cloud.commandframework.arguments.CommandArgument;
|
import cloud.commandframework.arguments.CommandArgument;
|
||||||
import cloud.commandframework.arguments.parser.ArgumentParseResult;
|
|
||||||
import cloud.commandframework.arguments.parser.ArgumentParser;
|
|
||||||
import cloud.commandframework.bukkit.BukkitCommandContextKeys;
|
|
||||||
import cloud.commandframework.bukkit.CloudBukkitCapabilities;
|
|
||||||
import cloud.commandframework.bukkit.arguments.selector.SingleEntitySelector;
|
import cloud.commandframework.bukkit.arguments.selector.SingleEntitySelector;
|
||||||
import cloud.commandframework.context.CommandContext;
|
import cloud.commandframework.context.CommandContext;
|
||||||
import cloud.commandframework.exceptions.parsing.NoInputProvidedException;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import org.bukkit.Bukkit;
|
import org.apiguardian.api.API;
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
|
@ -67,9 +61,25 @@ public final class SingleEntitySelectorArgument<C> extends CommandArgument<C, Si
|
||||||
* @param name Name of the argument
|
* @param name Name of the argument
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created builder
|
* @return Created builder
|
||||||
|
* @deprecated prefer {@link #builder(String)}
|
||||||
*/
|
*/
|
||||||
public static <C> SingleEntitySelectorArgument.@NonNull Builder<C> newBuilder(final @NonNull String name) {
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
return new SingleEntitySelectorArgument.Builder<>(name);
|
@Deprecated
|
||||||
|
public static <C> @NonNull Builder<C> newBuilder(final @NonNull String name) {
|
||||||
|
return builder(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link Builder}.
|
||||||
|
*
|
||||||
|
* @param name argument name
|
||||||
|
* @param <C> sender type
|
||||||
|
* @return new builder
|
||||||
|
* @since 1.8.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.8.0")
|
||||||
|
public static <C> @NonNull Builder<C> builder(final @NonNull String name) {
|
||||||
|
return new Builder<>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -79,8 +89,8 @@ public final class SingleEntitySelectorArgument<C> extends CommandArgument<C, Si
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created argument
|
* @return Created argument
|
||||||
*/
|
*/
|
||||||
public static <C> @NonNull CommandArgument<C, SingleEntitySelector> of(final @NonNull String name) {
|
public static <C> @NonNull SingleEntitySelectorArgument<C> of(final @NonNull String name) {
|
||||||
return SingleEntitySelectorArgument.<C>newBuilder(name).asRequired().build();
|
return SingleEntitySelectorArgument.<C>builder(name).asRequired().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -90,8 +100,8 @@ public final class SingleEntitySelectorArgument<C> extends CommandArgument<C, Si
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created argument
|
* @return Created argument
|
||||||
*/
|
*/
|
||||||
public static <C> @NonNull CommandArgument<C, SingleEntitySelector> optional(final @NonNull String name) {
|
public static <C> @NonNull SingleEntitySelectorArgument<C> optional(final @NonNull String name) {
|
||||||
return SingleEntitySelectorArgument.<C>newBuilder(name).asOptional().build();
|
return SingleEntitySelectorArgument.<C>builder(name).asOptional().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -102,15 +112,15 @@ public final class SingleEntitySelectorArgument<C> extends CommandArgument<C, Si
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created argument
|
* @return Created argument
|
||||||
*/
|
*/
|
||||||
public static <C> @NonNull CommandArgument<C, SingleEntitySelector> optional(
|
public static <C> @NonNull SingleEntitySelectorArgument<C> optional(
|
||||||
final @NonNull String name,
|
final @NonNull String name,
|
||||||
final @NonNull String defaultEntitySelector
|
final @NonNull String defaultEntitySelector
|
||||||
) {
|
) {
|
||||||
return SingleEntitySelectorArgument.<C>newBuilder(name).asOptionalWithDefault(defaultEntitySelector).build();
|
return SingleEntitySelectorArgument.<C>builder(name).asOptionalWithDefault(defaultEntitySelector).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final class Builder<C> extends CommandArgument.Builder<C, SingleEntitySelector> {
|
public static final class Builder<C> extends CommandArgument.TypedBuilder<C, SingleEntitySelector, Builder<C>> {
|
||||||
|
|
||||||
private Builder(final @NonNull String name) {
|
private Builder(final @NonNull String name) {
|
||||||
super(SingleEntitySelector.class, name);
|
super(SingleEntitySelector.class, name);
|
||||||
|
|
@ -123,60 +133,32 @@ public final class SingleEntitySelectorArgument<C> extends CommandArgument<C, Si
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public @NonNull SingleEntitySelectorArgument<C> build() {
|
public @NonNull SingleEntitySelectorArgument<C> build() {
|
||||||
return new SingleEntitySelectorArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(),
|
return new SingleEntitySelectorArgument<>(
|
||||||
this.getSuggestionsProvider(), this.getDefaultDescription()
|
this.isRequired(),
|
||||||
|
this.getName(),
|
||||||
|
this.getDefaultValue(),
|
||||||
|
this.getSuggestionsProvider(),
|
||||||
|
this.getDefaultDescription()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final class SingleEntitySelectorParser<C> implements ArgumentParser<C, SingleEntitySelector> {
|
public static final class SingleEntitySelectorParser<C> extends SelectorUtils.EntitySelectorParser<C, SingleEntitySelector> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link SingleEntitySelectorParser}.
|
||||||
|
*/
|
||||||
|
public SingleEntitySelectorParser() {
|
||||||
|
super(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull ArgumentParseResult<SingleEntitySelector> parse(
|
public SingleEntitySelector mapResult(
|
||||||
final @NonNull CommandContext<C> commandContext,
|
final @NonNull String input,
|
||||||
final @NonNull Queue<@NonNull String> inputQueue
|
final SelectorUtils.@NonNull EntitySelectorWrapper wrapper
|
||||||
) {
|
) {
|
||||||
if (!commandContext.get(BukkitCommandContextKeys.CLOUD_BUKKIT_CAPABILITIES).contains(
|
return new SingleEntitySelector(input, Collections.singletonList(wrapper.singleEntity()));
|
||||||
CloudBukkitCapabilities.BRIGADIER)) {
|
|
||||||
return ArgumentParseResult.failure(new SelectorParseException(
|
|
||||||
"",
|
|
||||||
commandContext,
|
|
||||||
SelectorParseException.FailureReason.UNSUPPORTED_VERSION,
|
|
||||||
SingleEntitySelectorParser.class
|
|
||||||
));
|
|
||||||
}
|
|
||||||
final String input = inputQueue.peek();
|
|
||||||
if (input == null) {
|
|
||||||
return ArgumentParseResult.failure(new NoInputProvidedException(
|
|
||||||
SingleEntitySelectorParser.class,
|
|
||||||
commandContext
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Entity> entities;
|
|
||||||
try {
|
|
||||||
entities = Bukkit.selectEntities(commandContext.get(BukkitCommandContextKeys.BUKKIT_COMMAND_SENDER), input);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return ArgumentParseResult.failure(new SelectorParseException(
|
|
||||||
input,
|
|
||||||
commandContext,
|
|
||||||
SelectorParseException.FailureReason.MALFORMED_SELECTOR,
|
|
||||||
SingleEntitySelectorParser.class
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entities.size() > 1) {
|
|
||||||
return ArgumentParseResult.failure(new SelectorParseException(
|
|
||||||
input,
|
|
||||||
commandContext,
|
|
||||||
SelectorParseException.FailureReason.TOO_MANY_ENTITIES,
|
|
||||||
SingleEntitySelectorParser.class
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
inputQueue.remove();
|
|
||||||
return ArgumentParseResult.success(new SingleEntitySelector(input, entities));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,21 +26,16 @@ package cloud.commandframework.bukkit.parsers.selector;
|
||||||
import cloud.commandframework.ArgumentDescription;
|
import cloud.commandframework.ArgumentDescription;
|
||||||
import cloud.commandframework.arguments.CommandArgument;
|
import cloud.commandframework.arguments.CommandArgument;
|
||||||
import cloud.commandframework.arguments.parser.ArgumentParseResult;
|
import cloud.commandframework.arguments.parser.ArgumentParseResult;
|
||||||
import cloud.commandframework.arguments.parser.ArgumentParser;
|
|
||||||
import cloud.commandframework.bukkit.BukkitCommandContextKeys;
|
|
||||||
import cloud.commandframework.bukkit.CloudBukkitCapabilities;
|
|
||||||
import cloud.commandframework.bukkit.arguments.selector.SinglePlayerSelector;
|
import cloud.commandframework.bukkit.arguments.selector.SinglePlayerSelector;
|
||||||
import cloud.commandframework.bukkit.parsers.PlayerArgument;
|
import cloud.commandframework.bukkit.parsers.PlayerArgument;
|
||||||
import cloud.commandframework.context.CommandContext;
|
import cloud.commandframework.context.CommandContext;
|
||||||
import cloud.commandframework.exceptions.parsing.NoInputProvidedException;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.ArrayList;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
import org.apiguardian.api.API;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
@ -72,9 +67,25 @@ public final class SinglePlayerSelectorArgument<C> extends CommandArgument<C, Si
|
||||||
* @param name Name of the argument
|
* @param name Name of the argument
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created builder
|
* @return Created builder
|
||||||
|
* @deprecated prefer {@link #builder(String)}
|
||||||
*/
|
*/
|
||||||
public static <C> SinglePlayerSelectorArgument.@NonNull Builder<C> newBuilder(final @NonNull String name) {
|
@API(status = API.Status.DEPRECATED, since = "1.8.0")
|
||||||
return new SinglePlayerSelectorArgument.Builder<>(name);
|
@Deprecated
|
||||||
|
public static <C> @NonNull Builder<C> newBuilder(final @NonNull String name) {
|
||||||
|
return builder(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link Builder}.
|
||||||
|
*
|
||||||
|
* @param name argument name
|
||||||
|
* @param <C> sender type
|
||||||
|
* @return new builder
|
||||||
|
* @since 1.8.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.8.0")
|
||||||
|
public static <C> @NonNull Builder<C> builder(final @NonNull String name) {
|
||||||
|
return new Builder<>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -84,8 +95,8 @@ public final class SinglePlayerSelectorArgument<C> extends CommandArgument<C, Si
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created argument
|
* @return Created argument
|
||||||
*/
|
*/
|
||||||
public static <C> @NonNull CommandArgument<C, SinglePlayerSelector> of(final @NonNull String name) {
|
public static <C> @NonNull SinglePlayerSelectorArgument<C> of(final @NonNull String name) {
|
||||||
return SinglePlayerSelectorArgument.<C>newBuilder(name).asRequired().build();
|
return SinglePlayerSelectorArgument.<C>builder(name).asRequired().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -95,8 +106,8 @@ public final class SinglePlayerSelectorArgument<C> extends CommandArgument<C, Si
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created argument
|
* @return Created argument
|
||||||
*/
|
*/
|
||||||
public static <C> @NonNull CommandArgument<C, SinglePlayerSelector> optional(final @NonNull String name) {
|
public static <C> @NonNull SinglePlayerSelectorArgument<C> optional(final @NonNull String name) {
|
||||||
return SinglePlayerSelectorArgument.<C>newBuilder(name).asOptional().build();
|
return SinglePlayerSelectorArgument.<C>builder(name).asOptional().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -107,15 +118,15 @@ public final class SinglePlayerSelectorArgument<C> extends CommandArgument<C, Si
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
* @return Created argument
|
* @return Created argument
|
||||||
*/
|
*/
|
||||||
public static <C> @NonNull CommandArgument<C, SinglePlayerSelector> optional(
|
public static <C> @NonNull SinglePlayerSelectorArgument<C> optional(
|
||||||
final @NonNull String name,
|
final @NonNull String name,
|
||||||
final @NonNull String defaultEntitySelector
|
final @NonNull String defaultEntitySelector
|
||||||
) {
|
) {
|
||||||
return SinglePlayerSelectorArgument.<C>newBuilder(name).asOptionalWithDefault(defaultEntitySelector).build();
|
return SinglePlayerSelectorArgument.<C>builder(name).asOptionalWithDefault(defaultEntitySelector).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final class Builder<C> extends CommandArgument.Builder<C, SinglePlayerSelector> {
|
public static final class Builder<C> extends CommandArgument.TypedBuilder<C, SinglePlayerSelector, Builder<C>> {
|
||||||
|
|
||||||
private Builder(final @NonNull String name) {
|
private Builder(final @NonNull String name) {
|
||||||
super(SinglePlayerSelector.class, name);
|
super(SinglePlayerSelector.class, name);
|
||||||
|
|
@ -128,91 +139,48 @@ public final class SinglePlayerSelectorArgument<C> extends CommandArgument<C, Si
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public @NonNull SinglePlayerSelectorArgument<C> build() {
|
public @NonNull SinglePlayerSelectorArgument<C> build() {
|
||||||
return new SinglePlayerSelectorArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(),
|
return new SinglePlayerSelectorArgument<>(
|
||||||
this.getSuggestionsProvider(), this.getDefaultDescription()
|
this.isRequired(),
|
||||||
|
this.getName(),
|
||||||
|
this.getDefaultValue(),
|
||||||
|
this.getSuggestionsProvider(),
|
||||||
|
this.getDefaultDescription()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final class SinglePlayerSelectorParser<C> implements ArgumentParser<C, SinglePlayerSelector> {
|
public static final class SinglePlayerSelectorParser<C> extends SelectorUtils.PlayerSelectorParser<C, SinglePlayerSelector> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link SinglePlayerSelectorParser}.
|
||||||
|
*/
|
||||||
|
public SinglePlayerSelectorParser() {
|
||||||
|
super(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull ArgumentParseResult<SinglePlayerSelector> parse(
|
public SinglePlayerSelector mapResult(
|
||||||
|
final @NonNull String input,
|
||||||
|
final SelectorUtils.@NonNull EntitySelectorWrapper wrapper
|
||||||
|
) {
|
||||||
|
return new SinglePlayerSelector(input, Collections.singletonList(wrapper.singlePlayer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NonNull ArgumentParseResult<SinglePlayerSelector> legacyParse(
|
||||||
final @NonNull CommandContext<C> commandContext,
|
final @NonNull CommandContext<C> commandContext,
|
||||||
final @NonNull Queue<@NonNull String> inputQueue
|
final @NonNull Queue<@NonNull String> inputQueue
|
||||||
) {
|
) {
|
||||||
final String input = inputQueue.peek();
|
final String input = inputQueue.peek();
|
||||||
if (input == null) {
|
@SuppressWarnings("deprecation") final @Nullable Player player = Bukkit.getPlayer(input);
|
||||||
return ArgumentParseResult.failure(new NoInputProvidedException(
|
|
||||||
SinglePlayerSelectorParser.class,
|
|
||||||
commandContext
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!commandContext.get(BukkitCommandContextKeys.CLOUD_BUKKIT_CAPABILITIES).contains(
|
if (player == null) {
|
||||||
CloudBukkitCapabilities.BRIGADIER)) {
|
return ArgumentParseResult.failure(new PlayerArgument.PlayerParseException(input, commandContext));
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
Player player = Bukkit.getPlayer(input);
|
|
||||||
|
|
||||||
if (player == null) {
|
|
||||||
return ArgumentParseResult.failure(new PlayerArgument.PlayerParseException(input, commandContext));
|
|
||||||
}
|
|
||||||
inputQueue.remove();
|
|
||||||
return ArgumentParseResult.success(new SinglePlayerSelector(input, ImmutableList.of(player)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Entity> entities;
|
|
||||||
try {
|
|
||||||
entities = Bukkit.selectEntities(commandContext.get(BukkitCommandContextKeys.BUKKIT_COMMAND_SENDER), input);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return ArgumentParseResult.failure(new SelectorParseException(
|
|
||||||
input,
|
|
||||||
commandContext,
|
|
||||||
SelectorParseException.FailureReason.MALFORMED_SELECTOR,
|
|
||||||
SinglePlayerSelectorParser.class
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Entity e : entities) {
|
|
||||||
if (!(e instanceof Player)) {
|
|
||||||
return ArgumentParseResult.failure(new SelectorParseException(
|
|
||||||
input,
|
|
||||||
commandContext,
|
|
||||||
SelectorParseException.FailureReason.NON_PLAYER_IN_PLAYER_SELECTOR,
|
|
||||||
SinglePlayerSelectorParser.class
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (entities.size() > 1) {
|
|
||||||
return ArgumentParseResult.failure(new SelectorParseException(
|
|
||||||
input,
|
|
||||||
commandContext,
|
|
||||||
SelectorParseException.FailureReason.TOO_MANY_PLAYERS,
|
|
||||||
SinglePlayerSelectorParser.class
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
inputQueue.remove();
|
inputQueue.remove();
|
||||||
return ArgumentParseResult.success(new SinglePlayerSelector(input, entities));
|
return ArgumentParseResult.success(new SinglePlayerSelector(input, ImmutableList.of(player)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull List<@NonNull String> suggestions(
|
|
||||||
final @NonNull CommandContext<C> commandContext,
|
|
||||||
final @NonNull String input
|
|
||||||
) {
|
|
||||||
List<String> output = new ArrayList<>();
|
|
||||||
|
|
||||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
|
||||||
final CommandSender bukkit = commandContext.get(BukkitCommandContextKeys.BUKKIT_COMMAND_SENDER);
|
|
||||||
if (bukkit instanceof Player && !((Player) bukkit).canSee(player)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
output.add(player.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue