From 8d6e0fef90fb94c08989f1d1cddc21a3d6669190 Mon Sep 17 00:00:00 2001 From: Skullian <133133667+Skullians@users.noreply.github.com> Date: Sun, 3 May 2026 18:45:43 +0100 Subject: [PATCH 1/3] feat(minestom): add ItemStackParser --- .../minestom/MinestomCommandManager.java | 4 +- .../minestom/caption/MinestomCaptionKeys.java | 5 + .../MinestomDefaultCaptionsProvider.java | 6 + .../cloud/minestom/data/ProtoItemStack.java | 67 +++++++++ .../cloud/minestom/data/package-info.java | 4 + .../minestom/parser/ItemStackParser.java | 139 ++++++++++++++++++ 6 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 cloud-minestom/src/main/java/org/incendo/cloud/minestom/data/ProtoItemStack.java create mode 100644 cloud-minestom/src/main/java/org/incendo/cloud/minestom/data/package-info.java create mode 100644 cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/ItemStackParser.java 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 ba968eff..61decdab 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 @@ -40,6 +40,7 @@ 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.location.PosParser; import org.incendo.cloud.minestom.parser.location.VecParser; @@ -82,7 +83,8 @@ public MinestomCommandManager( .registerParser(GameModeParser.gameModeParser()) .registerParser(DimensionTypeParser.dimensionTypeParser()) .registerParser(PosParser.posParser()) - .registerParser(VecParser.vecParser()); + .registerParser(VecParser.vecParser()) + .registerParser(ItemStackParser.itemStackParser()); 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 48558016..4a8f706f 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 @@ -72,6 +72,11 @@ public final class MinestomCaptionKeys { */ public static final Caption ARGUMENT_PARSE_FAILURE_POS = of("argument.parse.failure.pos"); + /** + * Variables: {@code } + */ + public static final Caption ARGUMENT_PARSE_FAILURE_ITEM_STACK = of("argument.parse.failure.item_stack"); + 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 973b8b2d..bc7f8993 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 @@ -69,6 +69,11 @@ public final class MinestomDefaultCaptionsProvider extends DelegatingCaptionP */ public static final String ARGUMENT_PARSE_FAILURE_POS = "'' is not a valid pos (expected: x y z, optionally with yaw and pitch)"; + /** + * Default caption for {@link MinestomCaptionKeys#ARGUMENT_PARSE_FAILURE_ITEM_STACK} + */ + public static final String ARGUMENT_PARSE_FAILURE_ITEM_STACK = "'' is not a valid item"; + 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) @@ -77,6 +82,7 @@ public final class MinestomDefaultCaptionsProvider extends DelegatingCaptionP .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) .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 new file mode 100644 index 00000000..266f5475 --- /dev/null +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/data/ProtoItemStack.java @@ -0,0 +1,67 @@ +// +// 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.data; + +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Intermediary result for an argument which parses a {@link net.minestom.server.item.Material} and optional NBT data. + * + * @since 2.1.0 + */ +@API(status = API.Status.STABLE, since = "2.0.0") +public final class ProtoItemStack { + + private final Material material; + + /** + * Creates a new proto item stack. + * + * @param material the material + */ + public ProtoItemStack(final @NonNull Material material) { + this.material = material; + } + + /** + * Creates an {@link ItemStack} from this proto item stack with the given amount. + * + * @param amount the stack size + * @return the created item stack + * @throws IllegalArgumentException if the amount exceeds the material's max stack size + */ + public @NonNull ItemStack createItemStack(final int amount) { + if (amount > this.material.maxStackSize()) { + throw new IllegalArgumentException(String.format( + "The maximum stack size for %s is %d", + this.material.name(), + this.material.maxStackSize() + )); + } + return ItemStack.of(this.material, amount); + } +} diff --git a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/data/package-info.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/data/package-info.java new file mode 100644 index 00000000..6c1624f0 --- /dev/null +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/data/package-info.java @@ -0,0 +1,4 @@ +/** + * cloud-minestom data holders + */ +package org.incendo.cloud.minestom.data; diff --git a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/ItemStackParser.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/ItemStackParser.java new file mode 100644 index 00000000..4c1846ed --- /dev/null +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/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; + +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; + } + } +} From d3d3e97e32ab956c795f83588223448e21dbd7b9 Mon Sep 17 00:00:00 2001 From: Skullian <133133667+Skullians@users.noreply.github.com> Date: Mon, 4 May 2026 01:27:34 +0100 Subject: [PATCH 2/3] feat: add some more parsers --- README.md | 23 +-- .../minestom/MinestomCommandManager.java | 12 +- .../minestom/caption/MinestomCaptionKeys.java | 20 ++- .../MinestomDefaultCaptionsProvider.java | 24 ++- .../minestom/parser/EnchantmentParser.java | 138 ++++++++++++++++++ ...eModeParser.java => SoundEventParser.java} | 52 +++---- .../parser/attribute/AttributeParser.java | 132 +++++++++++++++++ .../parser/attribute/package-info.java | 4 + .../parser/{ => item}/ItemStackParser.java | 2 +- .../minestom/parser/item/package-info.java | 4 + .../parser/location/package-info.java | 2 +- .../cloud/minestom/parser/package-info.java | 2 +- 12 files changed, 362 insertions(+), 53 deletions(-) create mode 100644 cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/EnchantmentParser.java rename cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/{GameModeParser.java => SoundEventParser.java} (67%) create mode 100644 cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/attribute/AttributeParser.java create mode 100644 cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/attribute/package-info.java rename cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/{ => item}/ItemStackParser.java (99%) create mode 100644 cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/item/package-info.java 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..84cf3e58 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.item.ItemStackParser; 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/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/ItemStackParser.java b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/item/ItemStackParser.java similarity index 99% rename from cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/ItemStackParser.java rename to cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/item/ItemStackParser.java index 4c1846ed..e2f04ed4 100644 --- a/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/ItemStackParser.java +++ b/cloud-minestom/src/main/java/org/incendo/cloud/minestom/parser/item/ItemStackParser.java @@ -21,7 +21,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // -package org.incendo.cloud.minestom.parser; +package org.incendo.cloud.minestom.parser.item; import java.util.stream.Collectors; import net.kyori.adventure.key.Key; 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; From 0da0c7de75922ec8a4168d66ab0255d0f1ecf6c3 Mon Sep 17 00:00:00 2001 From: Skullian <133133667+Skullians@users.noreply.github.com> Date: Mon, 4 May 2026 01:31:47 +0100 Subject: [PATCH 3/3] fix: compile --- .../org/incendo/cloud/minestom/MinestomCommandManager.java | 1 - .../incendo/cloud/minestom/caption/MinestomCaptionKeys.java | 5 ----- 2 files changed, 6 deletions(-) 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 b39ceed2..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 @@ -44,7 +44,6 @@ 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.item.ItemStackParser; import org.incendo.cloud.minestom.parser.location.PosParser; import org.incendo.cloud.minestom.parser.location.VecParser; 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 334949a3..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 @@ -87,11 +87,6 @@ public final class MinestomCaptionKeys { */ public static final Caption ARGUMENT_PARSE_FAILURE_ATTRIBUTE = of("argument.parse.failure.attribute"); - /** - * Variables: {@code } - */ - public static final Caption ARGUMENT_PARSE_FAILURE_ITEM_STACK = of("argument.parse.failure.item_stack"); - private MinestomCaptionKeys() { }