diff --git a/README.md b/README.md index a305bacc..99a263a3 100644 --- a/README.md +++ b/README.md @@ -51,15 +51,20 @@ As of right now there's only two built-in parsers that I bothered to add. ## Included parsers -| Parser | Type | Description | -|-----------------------|-----------------|-----------------------------------------------------------------------------------------------------| -| `PlayerParser` | `Player` | Resolves an online player by username | -| `EntityTypeParser` | `EntityType` | Resolves an entity type by namespaced key | -| `InstanceParser` | `Instance` | Resolves a loaded instance by UUID | -| `GameModeParser` | `GameMode` | Resolves a game mode by name | -| `DimensionTypeParser` | `DimensionType` | Resolves a dimension type by namespaced key | -| `VecParser` | `Vec` | Resolves a `Vec` from `x y z` (supports relative coordinates (`~ ~ ~`) | -| `PosParser` | `Pos` | Resolves a `Pos` from `x y z`, (+ optional pitch/yaw - also supports relative coords (`~ ~ ~ ~ ~`)) | +You'll see a decent amount of random parsers that I've added after needing them lol + +| Parser | Type | Description | +|----------------------------|----------------------------|-----------------------------------------------------------------------------------------------------| +| `PlayerParser` | `Player` | Resolves an online player by username | +| `EntityTypeParser` | `EntityType` | Resolves an entity type by namespaced key | +| `InstanceParser` | `Instance` | Resolves a loaded instance by UUID | +| `DimensionTypeParser` | `DimensionType` | Resolves a dimension type by namespaced key | +| `VecParser` | `Vec` | Resolves a `Vec` from `x y z` (supports relative coordinates (`~ ~ ~`) | +| `PosParser` | `Pos` | Resolves a `Pos` from `x y z`, (+ optional pitch/yaw - also supports relative coords (`~ ~ ~ ~ ~`)) | +| `ItemStackParser` | `ItemStack` | Resolves an `ItemStack` from `id:meta:count` | +| `EnchantmentParser` | `RegistryKey` | Resolves an enchantment registry key by namespaced key | +| `SoundEventParser` | `SoundEvent` | Resolves a builtin sound event by namespaced key | +| `AttributeParser` | `Attribute` | Resolves an entity attribute by namespaced key | ## links diff --git a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/MinestomCommandManager.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/MinestomCommandManager.java index 61decdab..2147ab2f 100644 --- a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/MinestomCommandManager.java +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/MinestomCommandManager.java @@ -37,11 +37,13 @@ import org.incendo.cloud.execution.ExecutionCoordinator; import org.incendo.cloud.minestom.caption.MinestomDefaultCaptionsProvider; import org.incendo.cloud.minestom.parser.DimensionTypeParser; +import org.incendo.cloud.minestom.parser.EnchantmentParser; import org.incendo.cloud.minestom.parser.EntityTypeParser; -import org.incendo.cloud.minestom.parser.GameModeParser; import org.incendo.cloud.minestom.parser.InstanceParser; import org.incendo.cloud.minestom.parser.ItemStackParser; import org.incendo.cloud.minestom.parser.PlayerParser; +import org.incendo.cloud.minestom.parser.SoundEventParser; +import org.incendo.cloud.minestom.parser.attribute.AttributeParser; import org.incendo.cloud.minestom.parser.location.PosParser; import org.incendo.cloud.minestom.parser.location.VecParser; @@ -80,11 +82,13 @@ public MinestomCommandManager( .registerParser(PlayerParser.playerParser()) .registerParser(EntityTypeParser.entityTypeParser()) .registerParser(InstanceParser.instanceParser()) - .registerParser(GameModeParser.gameModeParser()) .registerParser(DimensionTypeParser.dimensionTypeParser()) .registerParser(PosParser.posParser()) .registerParser(VecParser.vecParser()) - .registerParser(ItemStackParser.itemStackParser()); + .registerParser(ItemStackParser.itemStackParser()) + .registerParser(EnchantmentParser.enchantmentParser()) + .registerParser(SoundEventParser.soundEventParser()) + .registerParser(AttributeParser.attributeParser()); this.captionRegistry().registerProvider(new MinestomDefaultCaptionsProvider<>()); diff --git a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/caption/MinestomCaptionKeys.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/caption/MinestomCaptionKeys.java index 4a8f706f..5e83df27 100644 --- a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/caption/MinestomCaptionKeys.java +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/caption/MinestomCaptionKeys.java @@ -52,11 +52,6 @@ public final class MinestomCaptionKeys { */ public static final Caption ARGUMENT_PARSE_FAILURE_INSTANCE = of("argument.parse.failure.instance"); - /** - * Variables: {@code } - */ - public static final Caption ARGUMENT_PARSE_FAILURE_GAME_MODE = of("argument.parse.failure.game_mode"); - /** * Variables: {@code } */ @@ -77,6 +72,21 @@ public final class MinestomCaptionKeys { */ public static final Caption ARGUMENT_PARSE_FAILURE_ITEM_STACK = of("argument.parse.failure.item_stack"); + /** + * Variables: {@code } + */ + public static final Caption ARGUMENT_PARSE_FAILURE_ENCHANTMENT = of("argument.parse.failure.enchantment"); + + /** + * Variables: {@code } + */ + public static final Caption ARGUMENT_PARSE_FAILURE_SOUND_EVENT = of("argument.parse.failure.sound_event"); + + /** + * Variables: {@code } + */ + public static final Caption ARGUMENT_PARSE_FAILURE_ATTRIBUTE = of("argument.parse.failure.attribute"); + private MinestomCaptionKeys() { } diff --git a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/caption/MinestomDefaultCaptionsProvider.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/caption/MinestomDefaultCaptionsProvider.java index bc7f8993..fd242706 100644 --- a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/caption/MinestomDefaultCaptionsProvider.java +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/caption/MinestomDefaultCaptionsProvider.java @@ -49,11 +49,6 @@ public final class MinestomDefaultCaptionsProvider extends DelegatingCaptionP */ public static final String ARGUMENT_PARSE_FAILURE_INSTANCE = "No instance found for input ''"; - /** - * Default caption for {@link MinestomCaptionKeys#ARGUMENT_PARSE_FAILURE_GAME_MODE} - */ - public static final String ARGUMENT_PARSE_FAILURE_GAME_MODE = "'' is not a valid game mode"; - /** * Default caption for {@link MinestomCaptionKeys#ARGUMENT_PARSE_FAILURE_DIMENSION_TYPE} */ @@ -74,15 +69,32 @@ public final class MinestomDefaultCaptionsProvider extends DelegatingCaptionP */ public static final String ARGUMENT_PARSE_FAILURE_ITEM_STACK = "'' is not a valid item"; + /** + * Default caption for {@link MinestomCaptionKeys#ARGUMENT_PARSE_FAILURE_ENCHANTMENT} + */ + public static final String ARGUMENT_PARSE_FAILURE_ENCHANTMENT = "'' is not a valid enchantment"; + + /** + * Default caption for {@link MinestomCaptionKeys#ARGUMENT_PARSE_FAILURE_SOUND_EVENT} + */ + public static final String ARGUMENT_PARSE_FAILURE_SOUND_EVENT = "'' is not a valid sound event"; + + /** + * Default caption for {@link MinestomCaptionKeys#ARGUMENT_PARSE_FAILURE_ATTRIBUTE} + */ + public static final String ARGUMENT_PARSE_FAILURE_ATTRIBUTE = "'' is not a valid attribute"; + private static final CaptionProvider PROVIDER = CaptionProvider.constantProvider() .putCaption(MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_PLAYER, ARGUMENT_PARSE_FAILURE_PLAYER) .putCaption(MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_ENTITY_TYPE, ARGUMENT_PARSE_FAILURE_ENTITY_TYPE) .putCaption(MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_INSTANCE, ARGUMENT_PARSE_FAILURE_INSTANCE) - .putCaption(MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_GAME_MODE, ARGUMENT_PARSE_FAILURE_GAME_MODE) .putCaption(MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_DIMENSION_TYPE, ARGUMENT_PARSE_FAILURE_DIMENSION_TYPE) .putCaption(MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_VEC, ARGUMENT_PARSE_FAILURE_VEC) .putCaption(MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_POS, ARGUMENT_PARSE_FAILURE_POS) .putCaption(MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_ITEM_STACK, ARGUMENT_PARSE_FAILURE_ITEM_STACK) + .putCaption(MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_ENCHANTMENT, ARGUMENT_PARSE_FAILURE_ENCHANTMENT) + .putCaption(MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_SOUND_EVENT, ARGUMENT_PARSE_FAILURE_SOUND_EVENT) + .putCaption(MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_ATTRIBUTE, ARGUMENT_PARSE_FAILURE_ATTRIBUTE) .build(); @SuppressWarnings("unchecked") diff --git a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/data/ProtoItemStack.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/data/ProtoItemStack.java index bfd95fbb..266f5475 100644 --- a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/data/ProtoItemStack.java +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/data/ProtoItemStack.java @@ -47,16 +47,6 @@ public ProtoItemStack(final @NonNull Material material) { this.material = material; } - /** - * Get the {@link Material} of this {@link ProtoItemStack}. - * - * @return the {@link Material} - * @since 1.5.0 - */ - public @NonNull Material material() { - return this.material; - } - /** * Creates an {@link ItemStack} from this proto item stack with the given amount. * diff --git a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/EnchantmentParser.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/EnchantmentParser.java new file mode 100644 index 00000000..864b5981 --- /dev/null +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/EnchantmentParser.java @@ -0,0 +1,138 @@ +// +// MIT License +// +// Copyright (c) 2024 Incendo +// +// 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 org.incendo.cloud.minestom.parser; + +import java.util.stream.Collectors; +import net.kyori.adventure.key.Key; +import net.minestom.server.MinecraftServer; +import net.minestom.server.item.enchant.Enchantment; +import net.minestom.server.registry.RegistryKey; +import org.apiguardian.api.API; +import org.incendo.cloud.caption.CaptionVariable; +import org.incendo.cloud.component.CommandComponent; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.exception.parsing.ParserException; +import org.incendo.cloud.minestom.caption.MinestomCaptionKeys; +import org.incendo.cloud.parser.ArgumentParseResult; +import org.incendo.cloud.parser.ArgumentParser; +import org.incendo.cloud.parser.ParserDescriptor; +import org.incendo.cloud.suggestion.BlockingSuggestionProvider; +import org.jspecify.annotations.NonNull; + +/** + * Parser for {@link RegistryKey} referencing an {@link Enchantment}, resolved by namespaced key. + * + * @param command sender type + */ +public final class EnchantmentParser implements ArgumentParser>, BlockingSuggestionProvider.Strings { + + /** + * Creates a new enchantment parser. + * + * @param command sender type + * @return the created parser + * @since 2.0.0 + */ + @API(status = API.Status.STABLE, since = "2.0.0") + @SuppressWarnings("unchecked") + public static @NonNull ParserDescriptor> enchantmentParser() { + return ParserDescriptor.of(new EnchantmentParser<>(), (Class>) (Class) RegistryKey.class); + } + + /** + * Returns a {@link CommandComponent.Builder} using {@link #enchantmentParser()} as the parser. + * + * @param the command sender type + * @return the component builder + * @since 2.0.0 + */ + @API(status = API.Status.STABLE, since = "2.0.0") + @SuppressWarnings("unchecked") + public static CommandComponent.@NonNull Builder> enchantmentComponent() { + return CommandComponent.>builder().parser(enchantmentParser()); + } + + @Override + public @NonNull ArgumentParseResult<@NonNull RegistryKey> parse( + final @NonNull CommandContext<@NonNull C> commandContext, + final @NonNull CommandInput commandInput + ) { + final String input = commandInput.readString(); + final Key key = input.contains(":") ? Key.key(input) : Key.key("minecraft", input); + final RegistryKey registryKey = MinecraftServer.getEnchantmentRegistry().getKey(key); + if (registryKey == null) { + return ArgumentParseResult.failure( + new EnchantmentParseException(input, commandContext) + ); + } + return ArgumentParseResult.success(registryKey); + } + + @Override + public @NonNull Iterable<@NonNull String> stringSuggestions( + final @NonNull CommandContext commandContext, + final @NonNull CommandInput input + ) { + return MinecraftServer.getEnchantmentRegistry().keys().stream() + .map(k -> k.key().value()) + .collect(Collectors.toList()); + } + + /** + * Exception thrown when an enchantment cannot be found for the input provided. + */ + public static final class EnchantmentParseException extends ParserException { + + private final String input; + + /** + * Create a new enchantment parse exception. + * + * @param input string input + * @param context command context + */ + public EnchantmentParseException( + final @NonNull String input, + final @NonNull CommandContext context + ) { + super( + EnchantmentParser.class, + context, + MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_ENCHANTMENT, + CaptionVariable.of("input", input) + ); + this.input = input; + } + + /** + * Returns the supplied input. + * + * @return input value + */ + public @NonNull String input() { + return this.input; + } + } +} diff --git a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/GameModeParser.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/SoundEventParser.java similarity index 67% rename from cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/GameModeParser.java rename to cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/SoundEventParser.java index 6c9f2292..41ce9183 100644 --- a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/GameModeParser.java +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/SoundEventParser.java @@ -23,10 +23,9 @@ // package org.incendo.cloud.minestom.parser; -import java.util.Arrays; -import java.util.Locale; import java.util.stream.Collectors; -import net.minestom.server.entity.GameMode; +import net.kyori.adventure.key.Key; +import net.minestom.server.sound.SoundEvent; import org.apiguardian.api.API; import org.incendo.cloud.caption.CaptionVariable; import org.incendo.cloud.component.CommandComponent; @@ -41,47 +40,48 @@ import org.jspecify.annotations.NonNull; /** - * Parser for {@link GameMode game modes}. + * Parser for {@link SoundEvent}, resolved by namespaced key. * * @param command sender type */ -public final class GameModeParser implements ArgumentParser, BlockingSuggestionProvider.Strings { +public final class SoundEventParser implements ArgumentParser, BlockingSuggestionProvider.Strings { /** - * Creates a new game mode parser. + * Creates a new sound event parser. * * @param command sender type * @return the created parser * @since 2.0.0 */ @API(status = API.Status.STABLE, since = "2.0.0") - public static @NonNull ParserDescriptor gameModeParser() { - return ParserDescriptor.of(new GameModeParser<>(), GameMode.class); + public static @NonNull ParserDescriptor soundEventParser() { + return ParserDescriptor.of(new SoundEventParser<>(), SoundEvent.class); } /** - * Returns a {@link CommandComponent.Builder} using {@link #gameModeParser()} as the parser. + * Returns a {@link CommandComponent.Builder} using {@link #soundEventParser()} as the parser. * * @param the command sender type * @return the component builder * @since 2.0.0 */ @API(status = API.Status.STABLE, since = "2.0.0") - public static CommandComponent.@NonNull Builder gameModeComponent() { - return CommandComponent.builder().parser(gameModeParser()); + public static CommandComponent.@NonNull Builder soundEventComponent() { + return CommandComponent.builder().parser(soundEventParser()); } @Override - public @NonNull ArgumentParseResult<@NonNull GameMode> parse( + public @NonNull ArgumentParseResult<@NonNull SoundEvent> parse( final @NonNull CommandContext<@NonNull C> commandContext, final @NonNull CommandInput commandInput ) { final String input = commandInput.readString(); - return Arrays.stream(GameMode.values()) - .filter(gm -> gm.name().equalsIgnoreCase(input)) - .findFirst() - .map(ArgumentParseResult::success) - .orElseGet(() -> ArgumentParseResult.failure(new GameModeParseException(input, commandContext))); + final Key key = input.contains(":") ? Key.key(input) : Key.key("minecraft", input); + final SoundEvent sound = SoundEvent.fromKey(key); + if (sound == null) { + return ArgumentParseResult.failure(new SoundEventParseException(input, commandContext)); + } + return ArgumentParseResult.success(sound); } @Override @@ -89,32 +89,32 @@ public final class GameModeParser implements ArgumentParser, Blo final @NonNull CommandContext commandContext, final @NonNull CommandInput input ) { - return Arrays.stream(GameMode.values()) - .map(gm -> gm.name().toLowerCase(Locale.ROOT)) + return SoundEvent.values().stream() + .map(s -> s.key().value()) .collect(Collectors.toList()); } /** - * Exception thrown when a game mode cannot be found for the input provided. + * Exception thrown when a sound event cannot be found for the input provided. */ - public static final class GameModeParseException extends ParserException { + public static final class SoundEventParseException extends ParserException { private final String input; /** - * Create a new game mode parse exception. + * Create a new sound event parse exception. * - * @param input string input + * @param input string input * @param context command context */ - public GameModeParseException( + public SoundEventParseException( final @NonNull String input, final @NonNull CommandContext context ) { super( - GameModeParser.class, + SoundEventParser.class, context, - MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_GAME_MODE, + MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_SOUND_EVENT, CaptionVariable.of("input", input) ); this.input = input; diff --git a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/attribute/AttributeParser.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/attribute/AttributeParser.java new file mode 100644 index 00000000..7f6ba4a6 --- /dev/null +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/attribute/AttributeParser.java @@ -0,0 +1,132 @@ +// +// MIT License +// +// Copyright (c) 2024 Incendo +// +// 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 org.incendo.cloud.minestom.parser.attribute; + +import java.util.stream.Collectors; +import net.kyori.adventure.key.Key; +import net.minestom.server.entity.attribute.Attribute; +import org.apiguardian.api.API; +import org.incendo.cloud.caption.CaptionVariable; +import org.incendo.cloud.component.CommandComponent; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.exception.parsing.ParserException; +import org.incendo.cloud.minestom.caption.MinestomCaptionKeys; +import org.incendo.cloud.parser.ArgumentParseResult; +import org.incendo.cloud.parser.ArgumentParser; +import org.incendo.cloud.parser.ParserDescriptor; +import org.incendo.cloud.suggestion.BlockingSuggestionProvider; +import org.jspecify.annotations.NonNull; + +/** + * Parser for {@link Attribute}, by namespaced key. + * + * @param command sender type + */ +public final class AttributeParser implements ArgumentParser, BlockingSuggestionProvider.Strings { + + /** + * Creates a new attribute parser. + * + * @param command sender type + * @return the created parser + * @since 2.0.0 + */ + @API(status = API.Status.STABLE, since = "2.0.0") + public static @NonNull ParserDescriptor attributeParser() { + return ParserDescriptor.of(new AttributeParser<>(), Attribute.class); + } + + /** + * Returns a {@link CommandComponent.Builder} using {@link #attributeParser()} as the parser. + * + * @param the command sender type + * @return the component builder + * @since 2.0.0 + */ + @API(status = API.Status.STABLE, since = "2.0.0") + public static CommandComponent.@NonNull Builder attributeComponent() { + return CommandComponent.builder().parser(attributeParser()); + } + + @Override + public @NonNull ArgumentParseResult<@NonNull Attribute> parse( + final @NonNull CommandContext<@NonNull C> commandContext, + final @NonNull CommandInput commandInput + ) { + final String input = commandInput.readString(); + final Key key = input.contains(":") ? Key.key(input) : Key.key("minecraft", input); + final Attribute attribute = Attribute.fromKey(key); + if (attribute == null) { + return ArgumentParseResult.failure(new AttributeParseException(input, commandContext)); + } + return ArgumentParseResult.success(attribute); + } + + @Override + public @NonNull Iterable<@NonNull String> stringSuggestions( + final @NonNull CommandContext commandContext, + final @NonNull CommandInput input + ) { + return Attribute.values().stream() + .map(a -> a.key().value()) + .collect(Collectors.toList()); + } + + /** + * Exception thrown when an attribute cannot be found for the input provided. + */ + public static final class AttributeParseException extends ParserException { + + private final String input; + + /** + * Create a new attribute parse exception. + * + * @param input string input + * @param context command context + */ + public AttributeParseException( + final @NonNull String input, + final @NonNull CommandContext context + ) { + super( + AttributeParser.class, + context, + MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_ATTRIBUTE, + CaptionVariable.of("input", input) + ); + this.input = input; + } + + /** + * Returns the supplied input. + * + * @return input value + */ + public @NonNull String input() { + return this.input; + } + } +} diff --git a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/attribute/package-info.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/attribute/package-info.java new file mode 100644 index 00000000..ba7ec2d2 --- /dev/null +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/attribute/package-info.java @@ -0,0 +1,4 @@ +/** + * cloud-minestom attribute-specific command arguments + */ +package org.incendo.cloud.minestom.parser.attribute; diff --git a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/item/ItemStackParser.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/item/ItemStackParser.java new file mode 100644 index 00000000..e2f04ed4 --- /dev/null +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/item/ItemStackParser.java @@ -0,0 +1,139 @@ +// +// MIT License +// +// Copyright (c) 2024 Incendo +// +// 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 org.incendo.cloud.minestom.parser.item; + +import java.util.stream.Collectors; +import net.kyori.adventure.key.Key; +import net.minestom.server.item.Material; +import org.apiguardian.api.API; +import org.incendo.cloud.caption.CaptionVariable; +import org.incendo.cloud.component.CommandComponent; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.exception.parsing.ParserException; +import org.incendo.cloud.minestom.caption.MinestomCaptionKeys; +import org.incendo.cloud.minestom.data.ProtoItemStack; +import org.incendo.cloud.parser.ArgumentParseResult; +import org.incendo.cloud.parser.ArgumentParser; +import org.incendo.cloud.parser.ParserDescriptor; +import org.incendo.cloud.suggestion.BlockingSuggestionProvider; +import org.jspecify.annotations.NonNull; + +/** + * Parser for {@link ProtoItemStack}. + * + * @param command sender type + */ +public final class ItemStackParser implements ArgumentParser, BlockingSuggestionProvider.Strings { + + /** + * Creates a new item stack parser. + * + * @param command sender type + * @return the created parser + * @since 2.0.0 + */ + @API(status = API.Status.STABLE, since = "2.0.0") + public static @NonNull ParserDescriptor itemStackParser() { + return ParserDescriptor.of(new ItemStackParser<>(), ProtoItemStack.class); + } + + /** + * Returns a {@link CommandComponent.Builder} using {@link #itemStackParser()} as the parser. + * + * @param the command sender type + * @return the component builder + * @since 2.0.0 + */ + @API(status = API.Status.STABLE, since = "2.0.0") + public static CommandComponent.@NonNull Builder itemStackComponent() { + return CommandComponent.builder().parser(itemStackParser()); + } + + @Override + public @NonNull ArgumentParseResult<@NonNull ProtoItemStack> parse( + final @NonNull CommandContext<@NonNull C> commandContext, + final @NonNull CommandInput commandInput + ) { + final String input = commandInput.readString(); + final Key key; + try { + key = Key.key(input); + } catch (final Exception e) { + return ArgumentParseResult.failure(new ItemStackParseException(input, commandContext)); + } + + final Material material = Material.fromKey(key); + if (material == null) { + return ArgumentParseResult.failure(new ItemStackParseException(input, commandContext)); + } + return ArgumentParseResult.success(new ProtoItemStack(material)); + } + + @Override + public @NonNull Iterable<@NonNull String> stringSuggestions( + final @NonNull CommandContext commandContext, + final @NonNull CommandInput input + ) { + return Material.values().stream() + .map(m -> m.key().value()) + .collect(Collectors.toList()); + } + + /** + * Exception thrown when an item stack cannot be parsed from the input provided. + */ + public static final class ItemStackParseException extends ParserException { + + private final String input; + + /** + * Create a new item stack parse exception. + * + * @param input string input + * @param context command context + */ + public ItemStackParseException( + final @NonNull String input, + final @NonNull CommandContext context + ) { + super( + ItemStackParser.class, + context, + MinestomCaptionKeys.ARGUMENT_PARSE_FAILURE_ITEM_STACK, + CaptionVariable.of("input", input) + ); + this.input = input; + } + + /** + * Returns the supplied input. + * + * @return input value + */ + public @NonNull String input() { + return this.input; + } + } +} diff --git a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/item/package-info.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/item/package-info.java new file mode 100644 index 00000000..c24fa98b --- /dev/null +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/item/package-info.java @@ -0,0 +1,4 @@ +/** + * cloud-minestom item-specific command arguments + */ +package org.incendo.cloud.minestom.parser.item; diff --git a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/location/package-info.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/location/package-info.java index 5246c5db..cf02038e 100644 --- a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/location/package-info.java +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/location/package-info.java @@ -1,4 +1,4 @@ /** - * cloud-bukkit location-specific command arguments + * cloud-minestom location-specific command arguments */ package org.incendo.cloud.minestom.parser.location; diff --git a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/package-info.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/package-info.java index bd401ff2..9052e0d7 100644 --- a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/package-info.java +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/package-info.java @@ -1,4 +1,4 @@ /** - * cloud-bukkit command arguments + * cloud-minestom command arguments */ package org.incendo.cloud.minestom.parser;