bukkit: Implement ItemStack and Block predicate arguments (#259)
This commit is contained in:
parent
0f1d05ef9c
commit
7da05da323
11 changed files with 748 additions and 32 deletions
|
|
@ -27,8 +27,10 @@ import cloud.commandframework.arguments.parser.ArgumentParser;
|
|||
import cloud.commandframework.arguments.standard.UUIDArgument;
|
||||
import cloud.commandframework.brigadier.CloudBrigadierManager;
|
||||
import cloud.commandframework.bukkit.internal.CraftBukkitReflection;
|
||||
import cloud.commandframework.bukkit.parsers.BlockPredicateArgument;
|
||||
import cloud.commandframework.bukkit.parsers.EnchantmentArgument;
|
||||
import cloud.commandframework.bukkit.parsers.ItemStackArgument;
|
||||
import cloud.commandframework.bukkit.parsers.ItemStackPredicateArgument;
|
||||
import cloud.commandframework.bukkit.parsers.location.Location2DArgument;
|
||||
import cloud.commandframework.bukkit.parsers.location.LocationArgument;
|
||||
import cloud.commandframework.bukkit.parsers.selector.MultipleEntitySelectorArgument;
|
||||
|
|
@ -90,9 +92,14 @@ public final class BukkitBrigadierMapper<C> {
|
|||
/* Map Enchantment */
|
||||
this.mapSimpleNMS(new TypeToken<EnchantmentArgument.EnchantmentParser<C>>() {
|
||||
}, "Enchantment");
|
||||
/* Map ItemStackArgument */
|
||||
/* Map Item arguments */
|
||||
this.mapSimpleNMS(new TypeToken<ItemStackArgument.Parser<C>>() {
|
||||
}, "ItemStack");
|
||||
this.mapSimpleNMS(new TypeToken<ItemStackPredicateArgument.Parser<C>>() {
|
||||
}, "ItemPredicate");
|
||||
/* Map Block arguments */
|
||||
this.mapSimpleNMS(new TypeToken<BlockPredicateArgument.Parser<C>>() {
|
||||
}, "BlockPredicate");
|
||||
/* Map Entity Selectors */
|
||||
this.mapNMS(new TypeToken<SingleEntitySelectorArgument.SingleEntitySelectorParser<C>>() {
|
||||
}, this.entitySelectorArgumentSupplier(true, false));
|
||||
|
|
@ -105,9 +112,9 @@ public final class BukkitBrigadierMapper<C> {
|
|||
/* Map Vec3 */
|
||||
this.mapNMS(new TypeToken<LocationArgument.LocationParser<C>>() {
|
||||
}, this::argumentVec3);
|
||||
/* Map Vec2I */
|
||||
/* Map Vec2 */
|
||||
this.mapNMS(new TypeToken<Location2DArgument.Location2DParser<C>>() {
|
||||
}, this::argumentVec2i);
|
||||
}, this::argumentVec2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -141,11 +148,11 @@ public final class BukkitBrigadierMapper<C> {
|
|||
}
|
||||
}
|
||||
|
||||
private @NonNull ArgumentType<?> argumentVec2i() {
|
||||
private @NonNull ArgumentType<?> argumentVec2() {
|
||||
try {
|
||||
return (ArgumentType<?>) getNMSArgument("Vec2I").getDeclaredConstructor().newInstance();
|
||||
return (ArgumentType<?>) getNMSArgument("Vec2").getDeclaredConstructor().newInstance();
|
||||
} catch (final Exception e) {
|
||||
this.commandManager.getOwningPlugin().getLogger().log(Level.INFO, "Failed to retrieve Vec2I argument", e);
|
||||
this.commandManager.getOwningPlugin().getLogger().log(Level.INFO, "Failed to retrieve Vec2 argument", e);
|
||||
return fallbackType();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,10 @@ import cloud.commandframework.bukkit.arguments.selector.SingleEntitySelector;
|
|||
import cloud.commandframework.bukkit.arguments.selector.SinglePlayerSelector;
|
||||
import cloud.commandframework.bukkit.data.ProtoItemStack;
|
||||
import cloud.commandframework.bukkit.internal.CraftBukkitReflection;
|
||||
import cloud.commandframework.bukkit.parsers.BlockPredicateArgument;
|
||||
import cloud.commandframework.bukkit.parsers.EnchantmentArgument;
|
||||
import cloud.commandframework.bukkit.parsers.ItemStackArgument;
|
||||
import cloud.commandframework.bukkit.parsers.ItemStackPredicateArgument;
|
||||
import cloud.commandframework.bukkit.parsers.MaterialArgument;
|
||||
import cloud.commandframework.bukkit.parsers.OfflinePlayerArgument;
|
||||
import cloud.commandframework.bukkit.parsers.PlayerArgument;
|
||||
|
|
@ -62,6 +64,7 @@ import org.bukkit.plugin.Plugin;
|
|||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
|
@ -169,6 +172,12 @@ public class BukkitCommandManager<C> extends CommandManager<C> implements Brigad
|
|||
this.getParserRegistry().registerParserSupplier(TypeToken.get(MultiplePlayerSelector.class), parserParameters ->
|
||||
new MultiplePlayerSelectorArgument.MultiplePlayerSelectorParser<>());
|
||||
|
||||
/* Register MC 1.13+ parsers */
|
||||
if (this.minecraftVersion >= BRIGADIER_MINIMUM_VERSION) {
|
||||
this.registerParserSupplierFor(ItemStackPredicateArgument.class);
|
||||
this.registerParserSupplierFor(BlockPredicateArgument.class);
|
||||
}
|
||||
|
||||
/* Register suggestion and state listener */
|
||||
this.owningPlugin.getServer().getPluginManager().registerEvents(
|
||||
new CloudBukkitListener<>(this),
|
||||
|
|
@ -396,6 +405,24 @@ public class BukkitCommandManager<C> extends CommandManager<C> implements Brigad
|
|||
return this.backwardsCommandSenderMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to call the method on the provided class matching the signature
|
||||
* <p>{@code private static void registerParserSupplier(BukkitCommandManager)}</p>
|
||||
* using reflection.
|
||||
*
|
||||
* @param argumentClass argument class
|
||||
*/
|
||||
private void registerParserSupplierFor(final @NonNull Class<?> argumentClass) {
|
||||
try {
|
||||
final Method registerParserSuppliers = argumentClass
|
||||
.getDeclaredMethod("registerParserSupplier", BukkitCommandManager.class);
|
||||
registerParserSuppliers.setAccessible(true);
|
||||
registerParserSuppliers.invoke(null, this);
|
||||
} catch (final ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
final void lockIfBrigadierCapable() {
|
||||
if (this.minecraftVersion >= BRIGADIER_MINIMUM_VERSION) {
|
||||
this.lockRegistration();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// 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.data;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* A {@link Predicate} for {@link Block Blocks} in a {@link World}, parsed from user input.
|
||||
*
|
||||
* <p>By default, a parsed {@link BlockPredicate} will not load chunks to perform tests. It will simply
|
||||
* return {@code false} when attempting to test a block in unloaded chunks.</p>
|
||||
*
|
||||
* <p>To get a {@link BlockPredicate} which will load chunks, use {@link #loadChunks()}.</p>
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public interface BlockPredicate extends Predicate<Block> {
|
||||
|
||||
/**
|
||||
* Get a version of this {@link BlockPredicate} which will load chunks in order to perform
|
||||
* tests.
|
||||
*
|
||||
* <p>If this {@link BlockPredicate} already loads chunks, it will simply return itself.</p>
|
||||
*
|
||||
* @return a {@link BlockPredicate} which loads chunks
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@NonNull BlockPredicate loadChunks();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// 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.data;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* {@link Predicate} for {@link ItemStack ItemStacks}, parsed from user input.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public interface ItemStackPredicate extends Predicate<ItemStack> {
|
||||
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@ import org.bukkit.Bukkit;
|
|||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
|
|
@ -99,15 +100,18 @@ public final class CraftBukkitReflection {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean classExists(final @NonNull String className) {
|
||||
public static @NonNull Constructor<?> needConstructor(final @NonNull Class<?> holder, final @NonNull Class<?>... parameters) {
|
||||
try {
|
||||
Class.forName(className);
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
return holder.getDeclaredConstructor(parameters);
|
||||
} catch (final NoSuchMethodException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean classExists(final @NonNull String className) {
|
||||
return findClass(className) != null;
|
||||
}
|
||||
|
||||
public static @NonNull Method needMethod(
|
||||
final @NonNull Class<?> holder,
|
||||
final @NonNull String name,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,281 @@
|
|||
//
|
||||
// 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;
|
||||
|
||||
import cloud.commandframework.ArgumentDescription;
|
||||
import cloud.commandframework.arguments.CommandArgument;
|
||||
import cloud.commandframework.arguments.parser.ArgumentParseResult;
|
||||
import cloud.commandframework.arguments.parser.ArgumentParser;
|
||||
import cloud.commandframework.brigadier.argument.WrappedBrigadierParser;
|
||||
import cloud.commandframework.bukkit.BukkitCommandManager;
|
||||
import cloud.commandframework.bukkit.data.BlockPredicate;
|
||||
import cloud.commandframework.bukkit.internal.CraftBukkitReflection;
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import org.bukkit.block.Block;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Argument type for parsing a {@link BlockPredicate}.
|
||||
*
|
||||
* <p>This argument type is only usable on Minecraft 1.13+, as it depends on Minecraft internals added in that version.</p>
|
||||
*
|
||||
* <p>This argument type only provides basic suggestions by default. Enabling Brigadier compatibility through
|
||||
* {@link BukkitCommandManager#registerBrigadier()} will allow client side validation and suggestions to be utilized.</p>
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public final class BlockPredicateArgument<C> extends CommandArgument<C, BlockPredicate> {
|
||||
|
||||
private BlockPredicateArgument(
|
||||
final boolean required,
|
||||
final @NonNull String name,
|
||||
final @NonNull String defaultValue,
|
||||
final @Nullable BiFunction<@NonNull CommandContext<C>, @NonNull String,
|
||||
@NonNull List<@NonNull String>> suggestionsProvider,
|
||||
final @NonNull ArgumentDescription defaultDescription
|
||||
) {
|
||||
super(required, name, new Parser<>(), defaultValue, BlockPredicate.class, suggestionsProvider, defaultDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Builder}.
|
||||
*
|
||||
* @param name Name of the argument
|
||||
* @param <C> Command sender type
|
||||
* @return Created builder
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static <C> BlockPredicateArgument.@NonNull Builder<C> builder(final @NonNull String name) {
|
||||
return new BlockPredicateArgument.Builder<>(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new required {@link BlockPredicateArgument}.
|
||||
*
|
||||
* @param name Argument name
|
||||
* @param <C> Command sender type
|
||||
* @return Created argument
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static <C> @NonNull BlockPredicateArgument<C> of(final @NonNull String name) {
|
||||
return BlockPredicateArgument.<C>builder(name).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new optional {@link BlockPredicateArgument}.
|
||||
*
|
||||
* @param name Argument name
|
||||
* @param <C> Command sender type
|
||||
* @return Created argument
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static <C> @NonNull BlockPredicateArgument<C> optional(final @NonNull String name) {
|
||||
return BlockPredicateArgument.<C>builder(name).asOptional().build();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builder for {@link BlockPredicateArgument}.
|
||||
*
|
||||
* @param <C> sender type
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static final class Builder<C> extends TypedBuilder<C, BlockPredicate, Builder<C>> {
|
||||
|
||||
private Builder(final @NonNull String name) {
|
||||
super(BlockPredicate.class, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull BlockPredicateArgument<C> build() {
|
||||
return new BlockPredicateArgument<>(
|
||||
this.isRequired(),
|
||||
this.getName(),
|
||||
this.getDefaultValue(),
|
||||
this.getSuggestionsProvider(),
|
||||
this.getDefaultDescription()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser for {@link BlockPredicateArgument}. Only supported on Minecraft 1.13 and newer CraftBukkit based servers.
|
||||
*
|
||||
* @param <C> sender type
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static final class Parser<C> implements ArgumentParser<C, BlockPredicate> {
|
||||
|
||||
private static final Class<?> TAG_REGISTRY_CLASS;
|
||||
|
||||
static {
|
||||
if (CraftBukkitReflection.MAJOR_REVISION >= 16) {
|
||||
TAG_REGISTRY_CLASS = CraftBukkitReflection.needNMSClass("ITagRegistry");
|
||||
} else {
|
||||
TAG_REGISTRY_CLASS = CraftBukkitReflection.needNMSClass("TagRegistry");
|
||||
}
|
||||
}
|
||||
|
||||
private static final Class<?> CRAFT_WORLD_CLASS = CraftBukkitReflection.needOBCClass("CraftWorld");
|
||||
private static final Class<?> MINECRAFT_SERVER_CLASS = CraftBukkitReflection.needNMSClass("MinecraftServer");
|
||||
private static final Class<?> COMMAND_LISTENER_WRAPPER_CLASS =
|
||||
CraftBukkitReflection.needNMSClass("CommandListenerWrapper");
|
||||
private static final Class<?> ARGUMENT_BLOCK_PREDICATE_CLASS =
|
||||
CraftBukkitReflection.needNMSClass("ArgumentBlockPredicate");
|
||||
private static final Class<?> ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS =
|
||||
CraftBukkitReflection.needNMSClass("ArgumentBlockPredicate$b");
|
||||
private static final Class<?> SHAPE_DETECTOR_BLOCK_CLASS = // BlockInWorld
|
||||
CraftBukkitReflection.needNMSClass("ShapeDetectorBlock");
|
||||
private static final Class<?> I_WORLD_READER_CLASS = CraftBukkitReflection.needNMSClass("IWorldReader");
|
||||
private static final Class<?> BLOCK_POSITION_CLASS = CraftBukkitReflection.needNMSClass("BlockPosition");
|
||||
private static final Constructor<?> BLOCK_POSITION_CTR =
|
||||
CraftBukkitReflection.needConstructor(BLOCK_POSITION_CLASS, int.class, int.class, int.class);
|
||||
private static final Constructor<?> SHAPE_DETECTOR_BLOCK_CTR = CraftBukkitReflection
|
||||
.needConstructor(SHAPE_DETECTOR_BLOCK_CLASS, I_WORLD_READER_CLASS, BLOCK_POSITION_CLASS, boolean.class);
|
||||
private static final Method GET_HANDLE_METHOD = CraftBukkitReflection.needMethod(CRAFT_WORLD_CLASS, "getHandle");
|
||||
private static final Method CREATE_PREDICATE_METHOD =
|
||||
CraftBukkitReflection.needMethod(ARGUMENT_BLOCK_PREDICATE_RESULT_CLASS, "create", TAG_REGISTRY_CLASS);
|
||||
private static final Method GET_SERVER_METHOD =
|
||||
CraftBukkitReflection.needMethod(COMMAND_LISTENER_WRAPPER_CLASS, "getServer");
|
||||
private static final Method GET_TAG_REGISTRY_METHOD =
|
||||
CraftBukkitReflection.needMethod(MINECRAFT_SERVER_CLASS, "getTagRegistry");
|
||||
|
||||
private final ArgumentParser<C, BlockPredicate> parser;
|
||||
|
||||
/**
|
||||
* Create a new {@link Parser}.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public Parser() {
|
||||
try {
|
||||
this.parser = this.createParser();
|
||||
} catch (final ReflectiveOperationException ex) {
|
||||
throw new RuntimeException("Failed to initialize BlockPredicate parser.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ArgumentParser<C, BlockPredicate> createParser() throws ReflectiveOperationException {
|
||||
return new WrappedBrigadierParser<C, Object>(
|
||||
(ArgumentType<Object>) ARGUMENT_BLOCK_PREDICATE_CLASS.getConstructor().newInstance()
|
||||
).map((ctx, result) -> {
|
||||
final Object commandSourceStack = ctx.get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER);
|
||||
try {
|
||||
final Object server = GET_SERVER_METHOD.invoke(commandSourceStack);
|
||||
final Object tagRegistry = GET_TAG_REGISTRY_METHOD.invoke(server);
|
||||
final Predicate<Object> predicate = (Predicate<Object>) CREATE_PREDICATE_METHOD.invoke(result, tagRegistry);
|
||||
return ArgumentParseResult.success(new BlockPredicateImpl(predicate));
|
||||
} catch (final ReflectiveOperationException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull ArgumentParseResult<@NonNull BlockPredicate> parse(
|
||||
@NonNull final CommandContext<@NonNull C> commandContext,
|
||||
@NonNull final Queue<@NonNull String> inputQueue
|
||||
) {
|
||||
return this.parser.parse(commandContext, inputQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<@NonNull String> suggestions(
|
||||
final @NonNull CommandContext<C> commandContext,
|
||||
final @NonNull String input
|
||||
) {
|
||||
return this.parser.suggestions(commandContext, input);
|
||||
}
|
||||
|
||||
private static final class BlockPredicateImpl implements BlockPredicate {
|
||||
|
||||
private final Predicate<Object> predicate;
|
||||
|
||||
BlockPredicateImpl(final @NonNull Predicate<Object> predicate) {
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
private boolean testImpl(final @NonNull Block block, final boolean loadChunks) {
|
||||
try {
|
||||
final Object blockInWorld = SHAPE_DETECTOR_BLOCK_CTR.newInstance(
|
||||
GET_HANDLE_METHOD.invoke(block.getWorld()),
|
||||
BLOCK_POSITION_CTR.newInstance(block.getX(), block.getY(), block.getZ()),
|
||||
loadChunks
|
||||
);
|
||||
return this.predicate.test(blockInWorld);
|
||||
} catch (final ReflectiveOperationException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(final @NonNull Block block) {
|
||||
return this.testImpl(block, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull BlockPredicate loadChunks() {
|
||||
return new BlockPredicate() {
|
||||
@Override
|
||||
public @NonNull BlockPredicate loadChunks() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(final Block block) {
|
||||
return BlockPredicateImpl.this.testImpl(block, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called reflectively by {@link BukkitCommandManager}.
|
||||
*
|
||||
* @param commandManager command manager
|
||||
* @param <C> sender type
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private static <C> void registerParserSupplier(final @NonNull BukkitCommandManager<C> commandManager) {
|
||||
commandManager.getParserRegistry()
|
||||
.registerParserSupplier(TypeToken.get(BlockPredicate.class), params -> new BlockPredicateArgument.Parser<>());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -92,7 +92,7 @@ public final class ItemStackArgument<C> extends CommandArgument<C, ProtoItemStac
|
|||
* @return Created argument
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static <C> @NonNull CommandArgument<C, ProtoItemStack> of(final @NonNull String name) {
|
||||
public static <C> @NonNull ItemStackArgument<C> of(final @NonNull String name) {
|
||||
return ItemStackArgument.<C>builder(name).build();
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ public final class ItemStackArgument<C> extends CommandArgument<C, ProtoItemStac
|
|||
* @return Created argument
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static <C> @NonNull CommandArgument<C, ProtoItemStack> optional(final @NonNull String name) {
|
||||
public static <C> @NonNull ItemStackArgument<C> optional(final @NonNull String name) {
|
||||
return ItemStackArgument.<C>builder(name).asOptional().build();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,257 @@
|
|||
//
|
||||
// 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;
|
||||
|
||||
import cloud.commandframework.ArgumentDescription;
|
||||
import cloud.commandframework.arguments.CommandArgument;
|
||||
import cloud.commandframework.arguments.parser.ArgumentParseResult;
|
||||
import cloud.commandframework.arguments.parser.ArgumentParser;
|
||||
import cloud.commandframework.brigadier.argument.WrappedBrigadierParser;
|
||||
import cloud.commandframework.bukkit.BukkitCommandManager;
|
||||
import cloud.commandframework.bukkit.data.ItemStackPredicate;
|
||||
import cloud.commandframework.bukkit.internal.CraftBukkitReflection;
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.StringRange;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Argument type for parsing an {@link ItemStackPredicate}.
|
||||
*
|
||||
* <p>This argument type is only usable on Minecraft 1.13+, as it depends on Minecraft internals added in that version.</p>
|
||||
*
|
||||
* <p>This argument type only provides basic suggestions by default. Enabling Brigadier compatibility through
|
||||
* {@link BukkitCommandManager#registerBrigadier()} will allow client side validation and suggestions to be utilized.</p>
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public final class ItemStackPredicateArgument<C> extends CommandArgument<C, ItemStackPredicate> {
|
||||
|
||||
private ItemStackPredicateArgument(
|
||||
final boolean required,
|
||||
final @NonNull String name,
|
||||
final @NonNull String defaultValue,
|
||||
final @Nullable BiFunction<@NonNull CommandContext<C>, @NonNull String,
|
||||
@NonNull List<@NonNull String>> suggestionsProvider,
|
||||
final @NonNull ArgumentDescription defaultDescription
|
||||
) {
|
||||
super(required, name, new Parser<>(), defaultValue, ItemStackPredicate.class, suggestionsProvider, defaultDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Builder}.
|
||||
*
|
||||
* @param name Name of the argument
|
||||
* @param <C> Command sender type
|
||||
* @return Created builder
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static <C> ItemStackPredicateArgument.@NonNull Builder<C> builder(final @NonNull String name) {
|
||||
return new ItemStackPredicateArgument.Builder<>(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new required {@link ItemStackPredicateArgument}.
|
||||
*
|
||||
* @param name Argument name
|
||||
* @param <C> Command sender type
|
||||
* @return Created argument
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static <C> @NonNull ItemStackPredicateArgument<C> of(final @NonNull String name) {
|
||||
return ItemStackPredicateArgument.<C>builder(name).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new optional {@link ItemStackPredicateArgument}.
|
||||
*
|
||||
* @param name Argument name
|
||||
* @param <C> Command sender type
|
||||
* @return Created argument
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static <C> @NonNull ItemStackPredicateArgument<C> optional(final @NonNull String name) {
|
||||
return ItemStackPredicateArgument.<C>builder(name).asOptional().build();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builder for {@link ItemStackPredicateArgument}.
|
||||
*
|
||||
* @param <C> sender type
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static final class Builder<C> extends TypedBuilder<C, ItemStackPredicate, Builder<C>> {
|
||||
|
||||
private Builder(final @NonNull String name) {
|
||||
super(ItemStackPredicate.class, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull ItemStackPredicateArgument<C> build() {
|
||||
return new ItemStackPredicateArgument<>(
|
||||
this.isRequired(),
|
||||
this.getName(),
|
||||
this.getDefaultValue(),
|
||||
this.getSuggestionsProvider(),
|
||||
this.getDefaultDescription()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser for {@link ItemStackPredicateArgument}. Only supported on Minecraft 1.13 and newer CraftBukkit based servers.
|
||||
*
|
||||
* @param <C> sender type
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static final class Parser<C> implements ArgumentParser<C, ItemStackPredicate> {
|
||||
|
||||
private static final Class<?> CRAFT_ITEM_STACK_CLASS =
|
||||
CraftBukkitReflection.needOBCClass("inventory.CraftItemStack");
|
||||
private static final Class<?> ARGUMENT_ITEM_PREDICATE_CLASS =
|
||||
CraftBukkitReflection.needNMSClass("ArgumentItemPredicate");
|
||||
private static final Class<?> ARGUMENT_ITEM_PREDICATE_RESULT_CLASS =
|
||||
CraftBukkitReflection.needNMSClass("ArgumentItemPredicate$b");
|
||||
private static final Method CREATE_PREDICATE_METHOD = CraftBukkitReflection.needMethod(
|
||||
ARGUMENT_ITEM_PREDICATE_RESULT_CLASS,
|
||||
"create",
|
||||
com.mojang.brigadier.context.CommandContext.class
|
||||
);
|
||||
private static final Method AS_NMS_COPY_METHOD =
|
||||
CraftBukkitReflection.needMethod(CRAFT_ITEM_STACK_CLASS, "asNMSCopy", ItemStack.class);
|
||||
|
||||
private final ArgumentParser<C, ItemStackPredicate> parser;
|
||||
|
||||
/**
|
||||
* Create a new {@link Parser}.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public Parser() {
|
||||
try {
|
||||
this.parser = this.createParser();
|
||||
} catch (final ReflectiveOperationException ex) {
|
||||
throw new RuntimeException("Failed to initialize ItemPredicate parser.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ArgumentParser<C, ItemStackPredicate> createParser() throws ReflectiveOperationException {
|
||||
return new WrappedBrigadierParser<C, Object>(
|
||||
(ArgumentType<Object>) ARGUMENT_ITEM_PREDICATE_CLASS.getConstructor().newInstance()
|
||||
).map((ctx, result) -> {
|
||||
final Object commandSourceStack = ctx.get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER);
|
||||
final com.mojang.brigadier.context.CommandContext<Object> dummy = createDummyContext(ctx, commandSourceStack);
|
||||
try {
|
||||
final Predicate<Object> predicate = (Predicate<Object>) CREATE_PREDICATE_METHOD.invoke(result, dummy);
|
||||
return ArgumentParseResult.success(new ItemStackPredicateImpl(predicate));
|
||||
} catch (final ReflectiveOperationException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static <C> com.mojang.brigadier.context.@NonNull CommandContext<Object> createDummyContext(
|
||||
final @NonNull CommandContext<C> ctx,
|
||||
final @NonNull Object commandSourceStack
|
||||
) {
|
||||
return new com.mojang.brigadier.context.CommandContext<>(
|
||||
commandSourceStack,
|
||||
ctx.getRawInputJoined(),
|
||||
Collections.emptyMap(),
|
||||
null,
|
||||
null,
|
||||
Collections.emptyList(),
|
||||
StringRange.at(0),
|
||||
null,
|
||||
null,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull ArgumentParseResult<@NonNull ItemStackPredicate> parse(
|
||||
@NonNull final CommandContext<@NonNull C> commandContext,
|
||||
@NonNull final Queue<@NonNull String> inputQueue
|
||||
) {
|
||||
return this.parser.parse(commandContext, inputQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<@NonNull String> suggestions(
|
||||
final @NonNull CommandContext<C> commandContext,
|
||||
final @NonNull String input
|
||||
) {
|
||||
return this.parser.suggestions(commandContext, input);
|
||||
}
|
||||
|
||||
private static final class ItemStackPredicateImpl implements ItemStackPredicate {
|
||||
|
||||
private final Predicate<Object> predicate;
|
||||
|
||||
ItemStackPredicateImpl(final @NonNull Predicate<Object> predicate) {
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(final @NonNull ItemStack itemStack) {
|
||||
try {
|
||||
return this.predicate.test(AS_NMS_COPY_METHOD.invoke(null, itemStack));
|
||||
} catch (final ReflectiveOperationException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called reflectively by {@link BukkitCommandManager}.
|
||||
*
|
||||
* @param commandManager command manager
|
||||
* @param <C> sender type
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private static <C> void registerParserSupplier(final @NonNull BukkitCommandManager<C> commandManager) {
|
||||
commandManager.getParserRegistry().registerParserSupplier(
|
||||
TypeToken.get(ItemStackPredicate.class),
|
||||
params -> new ItemStackPredicateArgument.Parser<>()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue