returnList = new ArrayList<>();
-
- for (int i = 0; i < inputSize; i++) {
- InputTypeContainer expectedTypeContainer = inputTypes.get(i);
- Object value = inputObjects.get(i);
- Class> expectedType = expectedTypeContainer.typeSignature().toRawType();
-
- if (!expectedType.isInstance(value)) {
- throw ExceptionBuilder.createCommandException("Expected input object of type " + expectedType.getSimpleName() + ", got " + value.getClass().getSimpleName() + ".");
- }
-
- returnList.add(expectedType.cast(value));
- }
-
- return returnList;
- }
- //endregion
-
-
- /**
- * Retrieves a Brigadier compatible suggestion provider for this argument parser.
- *
- * This method evaluates {@link #getSuggestions()} to dynamically supply tab completion results.
- *
- * @param the command source type
- *
- * @return the suggestion provider context for Brigadier nodes
- *
- * @since 0.0.10
- */
- public final SuggestionProvider getSuggestionProvider() {
- return (ctx, builder) -> {
- Supplier> suggestionSupplier = getSuggestions();
- if (suggestionSupplier == null) return builder.buildFuture();
-
- List suggestions = suggestionSupplier.get();
- String remaining = builder.getRemaining().toLowerCase();
-
- for (String suggestion : suggestions) {
- if (suggestion.toLowerCase().startsWith(remaining)) {
- builder.suggest(suggestion);
- }
- }
-
- return builder.buildFuture();
- };
- }
-
- /**
- * Returns a dynamically loaded list of suggestions for the argument at a given context state.
- *
- * @return a supplier providing available string suggestions, or null if none exist
- *
- * @since 0.0.10
- */
- public @Nullable Supplier> getSuggestions() {
- return null;
- }
-
-
- /**
- * Retrieves the core type signature handled by this parser.
- *
- * @return the type signature
- *
- * @since 0.0.10
- */
- public TypeSignature> getTypeSignature() {
- return typeSignature;
- }
-
- /**
- * Retrieves the argument name meant for display in syntax sequences.
- *
- * @return the argument label
- *
- * @since 0.0.10
- */
- public String getArgumentName() {
- return argumentName;
- }
-
-}
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/ArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/ArgumentParser.java
index 53a0242d..7625ad05 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/ArgumentParser.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/ArgumentParser.java
@@ -7,17 +7,216 @@
package xyz.bitsquidd.bits.mc.command.argument.parser;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import com.mojang.brigadier.suggestion.SuggestionProvider;
+import org.jetbrains.annotations.Nullable;
+
+import xyz.bitsquidd.bits.mc.command.BitsCommandManager;
+import xyz.bitsquidd.bits.mc.command.argument.InputTypeContainer;
+import xyz.bitsquidd.bits.mc.command.argument.parser.impl.GreedyStringArgumentParser;
+import xyz.bitsquidd.bits.mc.command.exception.ExceptionBuilder;
+import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
+import xyz.bitsquidd.bits.wrapper.GreedyString;
+import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.RecordComponent;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
/**
- * Annotation for marking classes as argument parsers.
+ * A parser responsible for converting a list of raw command arguments into a specific object type.
+ *
+ * This class serves as the foundation for all custom argument parsing within the Bits command framework.
+ * Implementations define how string inputs or other basic types from Brigadier are structurally resolved
+ * into complex objects, and optionally provide tab-completion suggestions.
+ *
+ * Example parser implementation:
+ *
{@code
+ * public class PlayerArgumentParser extends AbstractArgumentParser {
+ * public PlayerArgumentParser() {
+ * super(TypeSignature.of(Player.class), "player_name");
+ * }
+ *
+ * // Override methods to dictate parsing logic and input types.
+ * }
+ * }
+ *
+ * @param The type this parser converts to.
+ * @param The type of the input data (e.g., String, Integer, World) this parser expects to receive from Brigadier or parser.
*
- * @since 0.0.13
+ * @since 0.0.10
*/
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-public @interface ArgumentParser {}
+public abstract class ArgumentParser {
+ private final TypeSignature> typeSignature; // The type signature this parser handles
+ private final String argumentName; // The name of the argument, used while displaying suggestions
+ private final Class dataClass; // The class of the input data type
+ private final @Nullable Constructor recordConstructor;
+
+ /**
+ * @param typeSignature the expected type this parser handles
+ * @param argumentName the label to display in command syntax suggestions
+ * @param dataClass the class of the input data type
+ *
+ * @since 0.0.10
+ */
+ protected ArgumentParser(TypeSignature> typeSignature, String argumentName, Class dataClass) {
+ this.typeSignature = typeSignature;
+ this.argumentName = argumentName;
+ this.dataClass = dataClass;
+ this.recordConstructor = dataClass.isRecord() ? resolveCanonicalConstructor(dataClass) : null;
+ }
+
+
+ private static Constructor resolveCanonicalConstructor(Class recordClass) {
+ Class>[] paramTypes = Arrays.stream(recordClass.getRecordComponents())
+ .map(RecordComponent::getType)
+ .toArray(Class>[]::new);
+
+ try {
+ return recordClass.getDeclaredConstructor(paramTypes);
+ } catch (NoSuchMethodException e) {
+ // Unreachable: every record has a canonical constructor.
+ throw new IllegalStateException("No canonical constructor found for record " + recordClass.getSimpleName(), e);
+ }
+ }
+
+
+ /**
+ * Parses the provided primitive objects into the target complex object type {@code O}.
+ *
+ * @param data the raw inputs captured from the command context
+ * @param ctx the command execution context
+ *
+ * @return the successfully parsed object
+ *
+ * @throws CommandSyntaxException if the input is malformed or invalid
+ * @since 0.0.10
+ */
+ public abstract O parse(D data, BitsCommandContext> ctx) throws CommandSyntaxException;
+
+ /**
+ * Returns a list of required objects the parser expects in.
+ * In the case of most custom parsers this will be a String.
+ *
+ * In the case a non-vanilla primitive is passed in, we expect a parser to be present for this.
+ * For example:
+ * An Integer parser would expect a single int {@code List.of(Integer.class)}
+ * A Bukkit Location parser may expect three doubles and a World {@code List.of(Double.class, Double.class, Double.class, World.class)}
+ *
+ *
+ * @return A list of expected required objects.
+ *
+ * @since 0.0.20
+ */
+ public final List getInputTypes() {
+ // Special case for GreedyString: it captures all remaining input as a single string.
+ if (this instanceof GreedyStringArgumentParser) return List.of(new InputTypeContainer(TypeSignature.of(GreedyString.class), getArgumentName()));
+
+ // Special case for non-record types: treat the entire data class the input.
+ if (!dataClass.isRecord()) return List.of(new InputTypeContainer(TypeSignature.of(dataClass), argumentName));
+
+ RecordComponent[] components = dataClass.getRecordComponents();
+ List inputTypes = new ArrayList<>(components.length);
+
+ for (RecordComponent component : components) {
+ inputTypes.add(new InputTypeContainer(TypeSignature.of(component.getType()), component.getName()));
+ }
+
+ return inputTypes;
+ }
+
+
+ public final D toData(List inputObjects) throws CommandSyntaxException {
+ List inputTypes = getInputTypes();
+
+ if (inputObjects.size() != inputTypes.size()) throw ExceptionBuilder.createCommandException("Expected exactly " + inputTypes.size() + " input object" + (inputTypes.size() > 1 ? "s" : "") + ", got " + inputObjects.size() + ".");
+ Object[] validated = new Object[inputTypes.size()];
+
+ for (int i = 0; i < inputTypes.size(); i++) {
+ Class> expectedType = inputTypes.get(i).typeSignature().toRawType();
+ Object value = inputObjects.get(i);
+
+ if (!expectedType.isInstance(value)) throw ExceptionBuilder.createCommandException("Expected input object of type " + expectedType.getSimpleName() + ", got " + value.getClass().getSimpleName() + ".");
+ validated[i] = value;
+ }
+
+ // Single-value case: D itself is the scalar type (e.g. D = Double)
+ if (!dataClass.isRecord()) return dataClass.cast(validated[0]);
+
+ try {
+ return Objects.requireNonNull(this.recordConstructor).newInstance(validated);
+ } catch (ReflectiveOperationException e) {
+ throw ExceptionBuilder.createCommandException("Failed to construct " + dataClass.getSimpleName() + ": " + e.getMessage());
+ }
+ }
+
+
+ /**
+ * Retrieves a Brigadier compatible suggestion provider for this argument parser.
+ *
+ * This method evaluates {@link #getSuggestions} to dynamically supply tab completion results.
+ *
+ * @param the command source type
+ *
+ * @return the suggestion provider context for Brigadier nodes
+ *
+ * @since 0.0.10
+ */
+ public final SuggestionProvider createSuggestionProvider() {
+ SuggestionSupplier suggestionSupplier = getSuggestions();
+ if (suggestionSupplier == null) return (ctx, builder) -> builder.buildFuture(); // No suggestions available
+ BitsCommandManager manager = BitsCommandManager.get();
+
+ return (ctx, builder) -> {
+ List suggestions = suggestionSupplier.getSuggestions(manager.createContext(ctx));
+ String remaining = builder.getRemaining();
+
+ for (String suggestion : suggestions) {
+ if (suggestion.toLowerCase().startsWith(remaining.toLowerCase())) {
+ builder.suggest(suggestion);
+ }
+ }
+
+ return builder.buildFuture();
+ };
+ }
+
+ /**
+ * Returns a dynamically loaded list of suggestions for the argument at a given context state.
+ *
+ * @return a supplier providing available string suggestions, or null if none exist
+ *
+ * @since 0.0.10
+ */
+ public @Nullable SuggestionSupplier getSuggestions() {
+ return null;
+ }
+
+
+ /**
+ * Retrieves the core type signature handled by this parser.
+ *
+ * @return the type signature
+ *
+ * @since 0.0.10
+ */
+ public TypeSignature> getTypeSignature() {
+ return typeSignature;
+ }
+
+ /**
+ * Retrieves the argument name meant for display in syntax sequences.
+ *
+ * @return the argument label
+ *
+ * @since 0.0.10
+ */
+ public String getArgumentName() {
+ return argumentName;
+ }
+
+}
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/BasicArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/BasicArgumentParser.java
new file mode 100644
index 00000000..4be1e3e1
--- /dev/null
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/BasicArgumentParser.java
@@ -0,0 +1,23 @@
+/*
+ * This file is part of a Bit libraries package.
+ * Licensed under the GNU Lesser General Public License v3.0.
+ *
+ * Copyright (c) 2023-2026 ImBit
+ */
+
+package xyz.bitsquidd.bits.mc.command.argument.parser;
+
+import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
+
+
+/**
+ * Argument parser designed for types that can be directly parsed from a single string input without any additional validation or transformation.
+ *
+ * @param
+ */
+public abstract class BasicArgumentParser extends ArgumentParser {
+ protected BasicArgumentParser(TypeSignature> typeSignature, String argumentName) {
+ super(typeSignature, argumentName, String.class);
+ }
+
+}
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/SuggestionSupplier.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/SuggestionSupplier.java
new file mode 100644
index 00000000..411eeb82
--- /dev/null
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/SuggestionSupplier.java
@@ -0,0 +1,25 @@
+/*
+ * This file is part of a Bit libraries package.
+ * Licensed under the GNU Lesser General Public License v3.0.
+ *
+ * Copyright (c) 2023-2026 ImBit
+ */
+
+package xyz.bitsquidd.bits.mc.command.argument.parser;
+
+import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
+
+import java.util.List;
+
+
+@FunctionalInterface
+public interface SuggestionSupplier {
+
+ List getSuggestions(BitsCommandContext ctx);
+
+
+ static SuggestionSupplier empty() {
+ return ctx -> List.of();
+ }
+
+}
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/GreedyStringArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/GreedyStringArgumentParser.java
index e1aedc2a..7546c63d 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/GreedyStringArgumentParser.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/GreedyStringArgumentParser.java
@@ -8,14 +8,11 @@
package xyz.bitsquidd.bits.mc.command.argument.parser.impl;
import xyz.bitsquidd.bits.mc.command.BrigadierTreeGenerator;
-import xyz.bitsquidd.bits.mc.command.argument.InputTypeContainer;
-import xyz.bitsquidd.bits.mc.command.argument.parser.AbstractArgumentParser;
import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
import xyz.bitsquidd.bits.wrapper.GreedyString;
import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
-import java.util.List;
/**
* Argument parser for {@link GreedyString} values.
@@ -24,20 +21,15 @@
*
* @since 0.0.10
*/
-@ArgumentParser
-public final class GreedyStringArgumentParser extends AbstractArgumentParser {
- public GreedyStringArgumentParser() {
- super(TypeSignature.of(GreedyString.class), "String...");
- }
- @Override
- public GreedyString parse(List inputObjects, BitsCommandContext> ctx) {
- return GreedyString.of((String)inputObjects.getFirst()); // Note: can't use singletonInputValidation as GreedyString is special...
+public final class GreedyStringArgumentParser extends ArgumentParser {
+ public GreedyStringArgumentParser() {
+ super(TypeSignature.of(GreedyString.class), "String...", String.class);
}
@Override
- public List getInputTypes() {
- return List.of(new InputTypeContainer(TypeSignature.of(GreedyString.class), getArgumentName()));
+ public GreedyString parse(String data, BitsCommandContext> ctx) {
+ return GreedyString.of(data);
}
}
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/KeyArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/KeyArgumentParser.java
new file mode 100644
index 00000000..0664f6b0
--- /dev/null
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/KeyArgumentParser.java
@@ -0,0 +1,51 @@
+/*
+ * This file is part of a Bit libraries package.
+ * Licensed under the GNU Lesser General Public License v3.0.
+ *
+ * Copyright (c) 2023-2026 ImBit
+ */
+
+package xyz.bitsquidd.bits.mc.command.argument.parser.impl;
+
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import net.kyori.adventure.key.Key;
+
+import xyz.bitsquidd.bits.mc.command.argument.parser.BasicArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.SuggestionSupplier;
+import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
+import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
+
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Argument parser for handling {@link Key Keys}.
+ *
+ * @since 0.0.20
+ */
+
+public final class KeyArgumentParser extends BasicArgumentParser {
+ public KeyArgumentParser() {
+ super(TypeSignature.of(Key.class), "Key");
+ }
+
+ @Override
+ public Key parse(String data, BitsCommandContext> ctx) throws CommandSyntaxException {
+ return Key.key(data);
+ }
+
+ @Override
+ public SuggestionSupplier getSuggestions() {
+ return ctx -> {
+ String lastInput = ctx.getLastInput();
+ if (lastInput.isEmpty()) return Collections.emptyList();
+ if (lastInput.contains(Key.DEFAULT_SEPARATOR + "")) return Collections.emptyList();
+ return List.of(
+ lastInput + Key.DEFAULT_SEPARATOR,
+ Key.MINECRAFT_NAMESPACE + Key.DEFAULT_SEPARATOR + lastInput
+ );
+ };
+ }
+
+}
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/LinkArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/LinkArgumentParser.java
index e15fe7f0..60671b53 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/LinkArgumentParser.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/LinkArgumentParser.java
@@ -9,15 +9,13 @@
import com.mojang.brigadier.exceptions.CommandSyntaxException;
-import xyz.bitsquidd.bits.mc.command.argument.parser.AbstractArgumentParser;
-import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.BasicArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.SuggestionSupplier;
import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
import xyz.bitsquidd.bits.wrapper.Link;
import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
import java.util.List;
-import java.util.function.Supplier;
-import java.util.stream.Stream;
//TODO add better validation for this, enum for a type? WWW. HTTPS:// etc.?
@@ -29,21 +27,20 @@
*
* @since 0.0.10
*/
-@ArgumentParser
-public final class LinkArgumentParser extends AbstractArgumentParser {
+
+public final class LinkArgumentParser extends BasicArgumentParser {
public LinkArgumentParser() {
super(TypeSignature.of(Link.class), "URL");
}
@Override
- public Link parse(List inputObjects, BitsCommandContext> ctx) throws CommandSyntaxException {
- String inputString = singletonInputValidation(inputObjects, String.class);
- return Link.of(inputString);
+ public Link parse(String data, BitsCommandContext> ctx) throws CommandSyntaxException {
+ return Link.of(data);
}
@Override
- public Supplier> getSuggestions() {
- return () -> Stream.of("https://", "http://", "www.").toList();
+ public SuggestionSupplier getSuggestions() {
+ return ctx -> List.of("https://", "http://", "www.");
}
}
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/UUIDArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/UUIDArgumentParser.java
index 710b65b8..327b6786 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/UUIDArgumentParser.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/UUIDArgumentParser.java
@@ -9,12 +9,13 @@
import com.mojang.brigadier.exceptions.CommandSyntaxException;
-import xyz.bitsquidd.bits.mc.command.argument.parser.AbstractArgumentParser;
-import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.BasicArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.SuggestionSupplier;
import xyz.bitsquidd.bits.mc.command.exception.ExceptionBuilder;
import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
+import java.util.Collections;
import java.util.List;
import java.util.UUID;
@@ -32,32 +33,53 @@
*
* @since 0.0.10
*/
-@ArgumentParser
-public final class UUIDArgumentParser extends AbstractArgumentParser {
+
+public final class UUIDArgumentParser extends BasicArgumentParser {
+ private static final List DASH_POSITIONS = List.of(8, 12, 16, 20);
+ private static final char DASH_CHAR = '-';
+ private static final int HEX_CHAR_COUNT = 32;
+ private static final int CANONICAL_LENGTH = HEX_CHAR_COUNT + DASH_POSITIONS.size();
public UUIDArgumentParser() {
super(TypeSignature.of(UUID.class), "UUID");
}
@Override
- public UUID parse(List inputObjects, BitsCommandContext> ctx) throws CommandSyntaxException {
- String inputString = singletonInputValidation(inputObjects, String.class);
-
- String hexOnly = inputString.replaceAll("[^a-fA-F0-9]", "");
-
- if (hexOnly.length() != 32) throw ExceptionBuilder.createCommandException("Invalid UUID format: " + inputString);
+ public UUID parse(String data, BitsCommandContext> ctx) throws CommandSyntaxException {
+ String hexOnly = data.replaceAll("[^a-fA-F0-9]", "");
+ if (hexOnly.length() != HEX_CHAR_COUNT) throw ExceptionBuilder.createCommandException("Invalid UUID format: " + data);
- String canonical = hexOnly.substring(0, 8) + "-"
- + hexOnly.substring(8, 12) + "-"
- + hexOnly.substring(12, 16) + "-"
- + hexOnly.substring(16, 20) + "-"
- + hexOnly.substring(20);
+ StringBuilder canonical = new StringBuilder(CANONICAL_LENGTH);
+ for (int i = 0; i < hexOnly.length(); i++) {
+ if (DASH_POSITIONS.contains(i)) canonical.append(DASH_CHAR);
+ canonical.append(hexOnly.charAt(i));
+ }
try {
- return UUID.fromString(canonical);
+ return UUID.fromString(canonical.toString());
} catch (IllegalArgumentException e) {
- throw ExceptionBuilder.createCommandException("Invalid UUID format: " + inputString);
+ throw ExceptionBuilder.createCommandException("Invalid UUID format: " + data);
}
}
+ @Override
+ public SuggestionSupplier getSuggestions() {
+ return ctx -> {
+ String lastInput = ctx.getLastInput();
+ if (lastInput.isEmpty()) return Collections.emptyList();
+ if (lastInput.length() >= CANONICAL_LENGTH) return Collections.emptyList();
+
+ String hexOnly = lastInput.replaceAll("[^a-fA-F0-9]", "");
+ if (hexOnly.length() > HEX_CHAR_COUNT) return Collections.emptyList();
+
+ StringBuilder suggestion = new StringBuilder(lastInput);
+ for (int pos : DASH_POSITIONS) {
+ if (suggestion.length() > pos && suggestion.charAt(pos) != DASH_CHAR) {
+ suggestion.insert(pos, DASH_CHAR);
+ }
+ }
+ return List.of(suggestion.toString());
+ };
+ }
+
}
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/VoidArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/VoidArgumentParser.java
index 85451c0f..cb7afb28 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/VoidArgumentParser.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/VoidArgumentParser.java
@@ -7,12 +7,10 @@
package xyz.bitsquidd.bits.mc.command.argument.parser.impl;
-import xyz.bitsquidd.bits.mc.command.argument.parser.AbstractArgumentParser;
-import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.BasicArgumentParser;
import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
-import java.util.List;
/**
* A fallback argument parser that explicitly consumes and returns {@code null} for {@link Void} types.
@@ -21,15 +19,16 @@
*
* @since 0.0.10
*/
-@ArgumentParser
-public final class VoidArgumentParser extends AbstractArgumentParser {
- public VoidArgumentParser() {
+public final class VoidArgumentParser extends BasicArgumentParser {
+ public static final VoidArgumentParser INSTANCE = new VoidArgumentParser();
+
+ private VoidArgumentParser() {
super(TypeSignature.of(Void.class), "Void");
}
@Override
- public Void parse(List inputObjects, BitsCommandContext> ctx) {
+ public Void parse(String inputObjects, BitsCommandContext> ctx) {
return null;
}
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/abs/AbstractEnumArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/abs/EnumArgumentParser.java
similarity index 64%
rename from minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/abs/AbstractEnumArgumentParser.java
rename to minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/abs/EnumArgumentParser.java
index 3242f6e7..560cee41 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/abs/AbstractEnumArgumentParser.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/abs/EnumArgumentParser.java
@@ -10,15 +10,16 @@
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import org.jetbrains.annotations.Nullable;
-import xyz.bitsquidd.bits.mc.command.argument.parser.AbstractArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.BasicArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.SuggestionSupplier;
import xyz.bitsquidd.bits.mc.command.exception.ExceptionBuilder;
import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
import java.util.List;
-import java.util.function.Supplier;
import java.util.stream.Stream;
+
/**
* Base abstract parser providing common parsing and suggestion logic for enum types.
*
@@ -34,12 +35,12 @@
* }
* }
*
- * @param the specific enum type
+ * @param the specific enum type
*
* @since 0.0.10
*/
-public abstract class AbstractEnumArgumentParser> extends AbstractArgumentParser {
- private final Class enumClass;
+public abstract class EnumArgumentParser> extends BasicArgumentParser {
+ private final Class enumClass;
/**
* @param enumClass the class object representing the enum to parse
@@ -47,29 +48,27 @@ public abstract class AbstractEnumArgumentParser> extends Abst
* @throws IllegalArgumentException if the provided class is not an enum
* @since 0.0.10
*/
- public AbstractEnumArgumentParser(Class enumClass) {
+ public EnumArgumentParser(Class enumClass) {
super(TypeSignature.of(Enum.class), enumClass.getName());
this.enumClass = enumClass;
if (!enumClass.isEnum()) throw new IllegalArgumentException("Provided class " + enumClass.getName() + " is not an enum!");
}
@Override
- public T parse(List inputObjects, BitsCommandContext> ctx) throws CommandSyntaxException {
- String inputString = singletonInputValidation(inputObjects, String.class);
-
- T enumValue;
+ public E parse(String data, BitsCommandContext> ctx) throws CommandSyntaxException {
+ E enumValue;
try {
- enumValue = Enum.valueOf(enumClass, inputString);
+ enumValue = Enum.valueOf(enumClass, data);
} catch (IllegalArgumentException e) {
- throw ExceptionBuilder.createCommandException(inputString + " is not a valid " + enumClass.getSimpleName() + ".");
+ throw ExceptionBuilder.createCommandException(data + " is not a valid " + enumClass.getSimpleName() + ".");
}
return enumValue;
}
@Override
- public @Nullable Supplier> getSuggestions() {
- return () -> enumClass.isEnum() ? Stream.of(enumClass.getEnumConstants()).map(Enum::name).toList() : List.of();
+ public @Nullable SuggestionSupplier getSuggestions() {
+ return ctx -> enumClass.isEnum() ? Stream.of(enumClass.getEnumConstants()).map(Enum::name).toList() : List.of();
}
}
\ No newline at end of file
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/generic/GenericEnumParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/generic/GenericEnumParser.java
index 0f522bcf..afddfa5e 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/generic/GenericEnumParser.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/generic/GenericEnumParser.java
@@ -9,21 +9,21 @@
import com.mojang.brigadier.exceptions.CommandSyntaxException;
-import xyz.bitsquidd.bits.mc.command.argument.parser.AbstractArgumentParser;
-import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
-import xyz.bitsquidd.bits.mc.command.argument.parser.impl.abs.AbstractEnumArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.BasicArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.SuggestionSupplier;
+import xyz.bitsquidd.bits.mc.command.argument.parser.impl.abs.EnumArgumentParser;
import xyz.bitsquidd.bits.mc.command.exception.ExceptionBuilder;
import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
+import xyz.bitsquidd.bits.util.wrapper.Enums;
import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
-import java.util.List;
-import java.util.function.Supplier;
import java.util.stream.Stream;
+
/**
* A fallback, generically instantiated parser that handles unmapped enum types automatically.
*
- * Developer Note: Custom enum parsers should override {@link AbstractEnumArgumentParser}
+ * Developer Note: Custom enum parsers should override {@link EnumArgumentParser}
* if you want more fine-grained control over enum arguments and tab completion.
*
* This implementation serves as a basic parser that is fallen back upon when no specific enum parser is available.
@@ -32,9 +32,8 @@
*
* @since 0.0.10
*/
-@ArgumentParser
-public final class GenericEnumParser> extends AbstractArgumentParser {
+public final class GenericEnumParser> extends BasicArgumentParser {
private final Class enumClass;
public GenericEnumParser(Class enumClass) {
@@ -43,19 +42,14 @@ public GenericEnumParser(Class enumClass) {
}
@Override
- public E parse(List inputObjects, BitsCommandContext> ctx) throws CommandSyntaxException {
- String inputString = singletonInputValidation(inputObjects, String.class);
-
- for (E constant : enumClass.getEnumConstants()) {
- if (constant.name().equalsIgnoreCase(inputString)) return constant;
- }
-
- throw ExceptionBuilder.createCommandException("Enum constant not found: " + inputString + " for enum " + enumClass.getSimpleName() + ".");
+ public E parse(String data, BitsCommandContext> ctx) throws CommandSyntaxException {
+ return Enums.getFromIdentifier(enumClass, data)
+ .orElseThrow(() -> ExceptionBuilder.createCommandException("Enum constant not found: " + data + " for enum " + enumClass.getSimpleName() + "."));
}
@Override
- public Supplier> getSuggestions() {
- return () -> Stream.of(enumClass.getEnumConstants()).map(Enum::name).toList();
+ public SuggestionSupplier getSuggestions() {
+ return ctx -> Stream.of(enumClass.getEnumConstants()).map(Enum::name).toList();
}
}
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/BooleanArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/BooleanArgumentParser.java
index 444646e2..8ed2b8c1 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/BooleanArgumentParser.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/BooleanArgumentParser.java
@@ -7,17 +7,17 @@
package xyz.bitsquidd.bits.mc.command.argument.parser.impl.primitive;
-import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.SuggestionSupplier;
import java.util.List;
-import java.util.function.Supplier;
+
/**
* Argument parser for Boolean values.
*
* @since 0.0.10
*/
-@ArgumentParser
+
public final class BooleanArgumentParser extends PrimitiveArgumentParser {
public BooleanArgumentParser() {
@@ -25,8 +25,8 @@ public BooleanArgumentParser() {
}
@Override
- public Supplier> getSuggestions() {
- return () -> List.of("true", "false");
+ public SuggestionSupplier getSuggestions() {
+ return ctx -> List.of("true", "false");
}
}
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/DoubleArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/DoubleArgumentParser.java
index 987fa30e..44dd84c1 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/DoubleArgumentParser.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/DoubleArgumentParser.java
@@ -7,14 +7,13 @@
package xyz.bitsquidd.bits.mc.command.argument.parser.impl.primitive;
-import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
/**
* Argument parser for Double values.
*
* @since 0.0.10
*/
-@ArgumentParser
+
public final class DoubleArgumentParser extends PrimitiveArgumentParser {
public DoubleArgumentParser() {
super(Double.class, "Double");
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/FloatArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/FloatArgumentParser.java
index b0bc401d..22162ca9 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/FloatArgumentParser.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/FloatArgumentParser.java
@@ -7,14 +7,13 @@
package xyz.bitsquidd.bits.mc.command.argument.parser.impl.primitive;
-import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
/**
* Argument parser for Float values.
*
* @since 0.0.10
*/
-@ArgumentParser
+
public final class FloatArgumentParser extends PrimitiveArgumentParser {
public FloatArgumentParser() {
super(Float.class, "Float");
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/IntegerArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/IntegerArgumentParser.java
index 79be0c9a..c04430a7 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/IntegerArgumentParser.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/IntegerArgumentParser.java
@@ -7,14 +7,13 @@
package xyz.bitsquidd.bits.mc.command.argument.parser.impl.primitive;
-import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
/**
* Argument parser for Integer values.
*
* @since 0.0.10
*/
-@ArgumentParser
+
public final class IntegerArgumentParser extends PrimitiveArgumentParser {
public IntegerArgumentParser() {
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/LongArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/LongArgumentParser.java
index d59ce089..8e4e88df 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/LongArgumentParser.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/LongArgumentParser.java
@@ -7,14 +7,13 @@
package xyz.bitsquidd.bits.mc.command.argument.parser.impl.primitive;
-import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
/**
* Argument parser for Long values.
*
* @since 0.0.10
*/
-@ArgumentParser
+
public final class LongArgumentParser extends PrimitiveArgumentParser {
public LongArgumentParser() {
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/PrimitiveArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/PrimitiveArgumentParser.java
index 12fb813c..ab6d26bc 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/PrimitiveArgumentParser.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/PrimitiveArgumentParser.java
@@ -9,12 +9,10 @@
import com.mojang.brigadier.exceptions.CommandSyntaxException;
-import xyz.bitsquidd.bits.mc.command.argument.InputTypeContainer;
-import xyz.bitsquidd.bits.mc.command.argument.parser.AbstractArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
-import java.util.List;
/**
* A foundation parser designed specifically for types that correspond to underlying Brigadier primitives.
@@ -26,21 +24,15 @@
*
* @since 0.0.10
*/
-public abstract class PrimitiveArgumentParser extends AbstractArgumentParser {
+public abstract class PrimitiveArgumentParser extends ArgumentParser {
protected PrimitiveArgumentParser(Class outputClass, String argumentName) {
- super(TypeSignature.of(outputClass), argumentName);
+ super(TypeSignature.of(outputClass), argumentName, outputClass);
}
@Override
- @SuppressWarnings("unchecked")
- public final O parse(List inputObjects, BitsCommandContext> ctx) throws CommandSyntaxException {
- return (O)singletonInputValidation(inputObjects, getTypeSignature().toRawType());
- }
-
- @Override
- public final List getInputTypes() {
- return List.of(new InputTypeContainer(getTypeSignature(), getArgumentName()));
+ public final O parse(O data, BitsCommandContext> ctx) throws CommandSyntaxException {
+ return data;
}
}
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/StringArgumentParser.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/StringArgumentParser.java
index c8373738..fd241bea 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/StringArgumentParser.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/primitive/StringArgumentParser.java
@@ -7,14 +7,13 @@
package xyz.bitsquidd.bits.mc.command.argument.parser.impl.primitive;
-import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
/**
* Argument parser for String values.
*
* @since 0.0.10
*/
-@ArgumentParser
+
public final class StringArgumentParser extends PrimitiveArgumentParser {
public StringArgumentParser() {
super(String.class, "String");
diff --git a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/util/CommandParameterInfo.java b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/util/CommandParameterInfo.java
index 145cdc0c..346be50f 100644
--- a/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/util/CommandParameterInfo.java
+++ b/minecraft/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/util/CommandParameterInfo.java
@@ -12,7 +12,7 @@
import xyz.bitsquidd.bits.mc.command.BitsCommandManager;
import xyz.bitsquidd.bits.mc.command.argument.BrigadierArgumentMapping;
-import xyz.bitsquidd.bits.mc.command.argument.parser.AbstractArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
import java.lang.reflect.Parameter;
@@ -39,7 +39,7 @@ public class CommandParameterInfo {
private final TypeSignature> typeSignature; // TypeSignature of the parameter
- private final AbstractArgumentParser> parser;
+ private final ArgumentParser, ?> parser;
private final List heldArguments = new ArrayList<>(); // Direct mapping from the custom args to brigadier ArgumentTypes
@@ -81,13 +81,13 @@ public CommandParameterInfo(Parameter parameter) {
if (useArgSuggestions) {
TypeSignature> inputType = parser.getInputTypes().get(i).typeSignature();
- AbstractArgumentParser> inputParser = BitsCommandManager.get().getArgumentRegistry().getParser(inputType);
+ ArgumentParser, ?> inputParser = BitsCommandManager.get().getArgumentRegistry().getParser(inputType);
// Use the input-specific parser's suggestions
- if (hasSuggestions(inputParser)) argumentBuilder.suggests(inputParser.getSuggestionProvider());
+ if (hasSuggestions(inputParser)) argumentBuilder.suggests(inputParser.createSuggestionProvider());
} else {
// Use the main parser's suggestions
- if (hasSuggestions(parser)) argumentBuilder.suggests(parser.getSuggestionProvider());
+ if (hasSuggestions(parser)) argumentBuilder.suggests(parser.createSuggestionProvider());
}
brigadierArguments.add(argumentBuilder);
@@ -95,7 +95,7 @@ public CommandParameterInfo(Parameter parameter) {
return brigadierArguments;
}
- private boolean hasSuggestions(AbstractArgumentParser> parser) {
+ private boolean hasSuggestions(ArgumentParser, ?> parser) {
return parser.getSuggestions() != null;
}
@@ -106,7 +106,7 @@ private boolean hasSuggestions(AbstractArgumentParser> parser) {
*
* @since 0.0.10
*/
- public AbstractArgumentParser> getParser() {
+ public ArgumentParser, ?> getParser() {
return parser;
}
diff --git a/minecraft/paper/gradle/libs.versions.toml b/minecraft/paper/gradle/libs.versions.toml
index b9bcfb98..cc649d63 100644
--- a/minecraft/paper/gradle/libs.versions.toml
+++ b/minecraft/paper/gradle/libs.versions.toml
@@ -1,8 +1,7 @@
[versions]
-paper-api = "26.1.2.build.+"
+paper-api = "26.2-rc-2.build.+"
[libraries]
-paper-api = { group = "io.papermc.paper", name = "paper-api", version.ref = "paper-api" }
bstats = { group = "org.bstats", name = "bstats-bukkit", version = "3.2.1" }
[plugins]
diff --git a/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/BlockPosArgumentParser.java b/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/BlockPosArgumentParser.java
index c885f865..02f8107f 100644
--- a/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/BlockPosArgumentParser.java
+++ b/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/BlockPosArgumentParser.java
@@ -7,38 +7,28 @@
package xyz.bitsquidd.bits.mc.command.argument.parser.impl;
-import com.mojang.brigadier.exceptions.CommandSyntaxException;
-
-import xyz.bitsquidd.bits.mc.command.argument.InputTypeContainer;
-import xyz.bitsquidd.bits.mc.command.argument.parser.AbstractArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
import xyz.bitsquidd.bits.paper.location.wrapper.BlockPos;
import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
-import java.util.List;
-public final class BlockPosArgumentParser extends AbstractArgumentParser {
+public final class BlockPosArgumentParser extends ArgumentParser {
+
+ public record BlockPosData(
+ double x,
+ double y,
+ double z
+ ) {}
public BlockPosArgumentParser() {
- super(TypeSignature.of(BlockPos.class), "BlockPos");
+ super(TypeSignature.of(BlockPos.class), "BlockPos", BlockPosData.class);
}
- @Override
- public BlockPos parse(List inputObjects, BitsCommandContext> ctx) throws CommandSyntaxException {
- List inputs = inputValidation(inputObjects);
- double x = (double)inputs.get(0);
- double y = (double)inputs.get(1);
- double z = (double)inputs.get(2);
- return BlockPos.of(x, y, z);
- }
@Override
- public List getInputTypes() {
- return List.of(
- new InputTypeContainer(TypeSignature.of(Double.class), "x"),
- new InputTypeContainer(TypeSignature.of(Double.class), "y"),
- new InputTypeContainer(TypeSignature.of(Double.class), "z")
- );
+ public BlockPos parse(BlockPosData data, BitsCommandContext> ctx) {
+ return BlockPos.of(data.x(), data.y(), data.z());
}
}
diff --git a/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/LocationArgumentParser.java b/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/LocationArgumentParser.java
index eb438a13..0da13020 100644
--- a/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/LocationArgumentParser.java
+++ b/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/LocationArgumentParser.java
@@ -7,42 +7,31 @@
package xyz.bitsquidd.bits.mc.command.argument.parser.impl;
-import com.mojang.brigadier.exceptions.CommandSyntaxException;
import org.bukkit.Location;
import org.bukkit.World;
-import xyz.bitsquidd.bits.mc.command.argument.InputTypeContainer;
-import xyz.bitsquidd.bits.mc.command.argument.parser.AbstractArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
-import java.util.List;
-public final class LocationArgumentParser extends AbstractArgumentParser {
+public final class LocationArgumentParser extends ArgumentParser {
+
+ public record LocationData(
+ double x,
+ double y,
+ double z,
+ World world
+ ) {}
public LocationArgumentParser() {
- super(TypeSignature.of(Location.class), "Location");
+ super(TypeSignature.of(Location.class), "Location", LocationData.class);
}
- @Override
- public Location parse(List inputObjects, BitsCommandContext> ctx) throws CommandSyntaxException {
- List inputs = inputValidation(inputObjects);
- double x = (double)inputs.get(0);
- double y = (double)inputs.get(1);
- double z = (double)inputs.get(2);
- World world = (World)inputs.get(3);
-
- return new Location(world, x, y, z);
- }
@Override
- public List getInputTypes() {
- return List.of(
- new InputTypeContainer(TypeSignature.of(Double.class), "x"),
- new InputTypeContainer(TypeSignature.of(Double.class), "y"),
- new InputTypeContainer(TypeSignature.of(Double.class), "z"),
- new InputTypeContainer(TypeSignature.of(World.class), "world")
- );
+ public Location parse(LocationData data, BitsCommandContext> ctx) {
+ return new Location(data.world(), data.x(), data.y(), data.z());
}
}
diff --git a/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/PlayerCollectionArgumentParser.java b/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/PlayerCollectionArgumentParser.java
index 0606bd0c..eda97994 100644
--- a/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/PlayerCollectionArgumentParser.java
+++ b/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/PlayerCollectionArgumentParser.java
@@ -15,8 +15,8 @@
import org.jetbrains.annotations.Nullable;
import xyz.bitsquidd.bits.mc.command.PaperBitsCommandContext;
-import xyz.bitsquidd.bits.mc.command.argument.InputTypeContainer;
-import xyz.bitsquidd.bits.mc.command.argument.parser.AbstractArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.SuggestionSupplier;
import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
@@ -25,9 +25,9 @@
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
-import java.util.function.Supplier;
-public final class PlayerCollectionArgumentParser extends AbstractArgumentParser> {
+
+public final class PlayerCollectionArgumentParser extends ArgumentParser, EntitySelector> {
// TODO: Just pull from vanilla EntitySelector. Also pull the completion.
private enum SelectorType {
@@ -58,14 +58,12 @@ public Collection get(BitsCommandContext> ctx) {
}
public PlayerCollectionArgumentParser() {
- super(TypeSignature.of(Collection.class, Player.class), "Players");
+ super(TypeSignature.of(Collection.class, Player.class), "Players", EntitySelector.class);
}
@Override
- public Collection parse(List inputObjects, BitsCommandContext> ctx) throws CommandSyntaxException {
- EntitySelector entitySelctor = singletonInputValidation(inputObjects, EntitySelector.class);
-
- return entitySelctor.findPlayers((CommandSourceStack)ctx.getBrigadierContext().getSource())
+ public Collection parse(EntitySelector data, BitsCommandContext> ctx) throws CommandSyntaxException {
+ return data.findPlayers((CommandSourceStack)ctx.getBrigadierContext().getSource())
.stream()
.map(playerEntity -> playerEntity.getBukkitEntity().getPlayer())
.map(Objects::requireNonNull)
@@ -73,18 +71,13 @@ public Collection parse(List inputObjects, BitsCommandContext>
}
@Override
- public List getInputTypes() {
- return List.of(new InputTypeContainer(TypeSignature.of(EntitySelector.class), getArgumentName()));
- }
-
- @Override
- public Supplier> getSuggestions() {
+ public SuggestionSupplier getSuggestions() {
List suggestions = new ArrayList<>();
suggestions.add(SelectorType.ALL.selector);
suggestions.add(SelectorType.SELF.selector);
suggestions.addAll(Bukkit.getOnlinePlayers().stream().map(Player::getName).toList());
- return () -> suggestions;
+ return _ -> suggestions;
}
}
diff --git a/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/PlayerSingleArgumentParser.java b/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/PlayerSingleArgumentParser.java
index fc16ca69..0bfd347e 100644
--- a/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/PlayerSingleArgumentParser.java
+++ b/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/PlayerSingleArgumentParser.java
@@ -13,42 +13,34 @@
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
-import xyz.bitsquidd.bits.mc.command.argument.InputTypeContainer;
-import xyz.bitsquidd.bits.mc.command.argument.parser.AbstractArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.SuggestionSupplier;
import xyz.bitsquidd.bits.mc.command.exception.ExceptionBuilder;
import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
-import java.util.List;
import java.util.Objects;
-import java.util.function.Supplier;
-public final class PlayerSingleArgumentParser extends AbstractArgumentParser {
+
+public final class PlayerSingleArgumentParser extends ArgumentParser {
public PlayerSingleArgumentParser() {
- super(TypeSignature.of(Player.class), "Player");
+ super(TypeSignature.of(Player.class), "Player", EntitySelector.class);
}
@Override
- public Player parse(List inputObjects, BitsCommandContext> ctx) throws CommandSyntaxException {
- EntitySelector entitySelctor = singletonInputValidation(inputObjects, EntitySelector.class);
-
- return entitySelctor.findPlayers((CommandSourceStack)ctx.getBrigadierContext().getSource())
+ public Player parse(EntitySelector data, BitsCommandContext> ctx) throws CommandSyntaxException {
+ return data.findPlayers((CommandSourceStack)ctx.getBrigadierContext().getSource())
.stream()
.map(playerEntity -> playerEntity.getBukkitEntity().getPlayer())
.map(Objects::requireNonNull)
.findFirst()
- .orElseThrow(() -> ExceptionBuilder.createCommandException("Player not found: " + entitySelctor));
- }
-
- @Override
- public List getInputTypes() {
- return List.of(new InputTypeContainer(TypeSignature.of(EntitySelector.class), getArgumentName()));
+ .orElseThrow(() -> ExceptionBuilder.createCommandException("Player not found: " + data));
}
@Override
- public Supplier> getSuggestions() {
- return () -> Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
+ public SuggestionSupplier getSuggestions() {
+ return _ -> Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
}
}
diff --git a/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/WorldArgumentParser.java b/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/WorldArgumentParser.java
index 9603dcbb..16852fd9 100644
--- a/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/WorldArgumentParser.java
+++ b/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/WorldArgumentParser.java
@@ -8,35 +8,33 @@
package xyz.bitsquidd.bits.mc.command.argument.parser.impl;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import net.kyori.adventure.key.Key;
import org.bukkit.Bukkit;
import org.bukkit.World;
-import xyz.bitsquidd.bits.mc.command.argument.parser.AbstractArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.SuggestionSupplier;
import xyz.bitsquidd.bits.mc.command.exception.ExceptionBuilder;
import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
-import java.util.List;
-import java.util.function.Supplier;
-public final class WorldArgumentParser extends AbstractArgumentParser {
+public final class WorldArgumentParser extends ArgumentParser {
public WorldArgumentParser() {
- super(TypeSignature.of(World.class), "World");
+ super(TypeSignature.of(World.class), "World", Key.class);
}
@Override
- public World parse(List inputObjects, BitsCommandContext> ctx) throws CommandSyntaxException {
- String inputString = singletonInputValidation(inputObjects, String.class);
-
- World world = Bukkit.getWorld(inputString);
- if (world == null) throw ExceptionBuilder.createCommandException("World not found: " + inputString + ".");
+ public World parse(Key data, BitsCommandContext> ctx) throws CommandSyntaxException {
+ World world = Bukkit.getWorld(data);
+ if (world == null) throw ExceptionBuilder.createCommandException("World not found: " + data + ".");
return world;
}
@Override
- public Supplier> getSuggestions() {
- return () -> Bukkit.getWorlds().stream().map(World::getName).toList();
+ public SuggestionSupplier getSuggestions() {
+ return _ -> Bukkit.getWorlds().stream().map(w -> "\"" + w.key() + "\"").toList();
}
}
diff --git a/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/WorldDefinitionArgumentParser.java b/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/WorldDefinitionArgumentParser.java
new file mode 100644
index 00000000..81e647c6
--- /dev/null
+++ b/minecraft/paper/module/command/src/main/java/xyz/bitsquidd/bits/mc/command/argument/parser/impl/WorldDefinitionArgumentParser.java
@@ -0,0 +1,67 @@
+/*
+ * This file is part of a Bit libraries package.
+ * Licensed under the GNU Lesser General Public License v3.0.
+ *
+ * Copyright (c) 2023-2026 ImBit
+ */
+
+package xyz.bitsquidd.bits.mc.command.argument.parser.impl;
+
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import net.kyori.adventure.key.Key;
+import org.bukkit.Bukkit;
+
+import xyz.bitsquidd.bits.mc.command.argument.parser.ArgumentParser;
+import xyz.bitsquidd.bits.mc.command.argument.parser.SuggestionSupplier;
+import xyz.bitsquidd.bits.mc.command.util.BitsCommandContext;
+import xyz.bitsquidd.bits.paper.wrapper.WorldDefinition;
+import xyz.bitsquidd.bits.wrapper.type.TypeSignature;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import static xyz.bitsquidd.bits.paper.util.Keys.NSK;
+
+
+public final class WorldDefinitionArgumentParser extends ArgumentParser {
+ private static final Path DIMENSIONS_PATH = Bukkit.getWorldContainer().toPath().resolve("world/dimensions");
+
+ public WorldDefinitionArgumentParser() {
+ super(TypeSignature.of(WorldDefinition.class), "WorldDefinition", Key.class);
+ }
+
+ @Override
+ public WorldDefinition parse(Key data, BitsCommandContext> ctx) throws CommandSyntaxException {
+ return WorldDefinition.of(NSK(data));
+ }
+
+ @Override
+ public SuggestionSupplier getSuggestions() {
+ Set worldKeys = new HashSet<>();
+ try (Stream namespaces = Files.list(DIMENSIONS_PATH)) {
+ namespaces.forEach(namespacePath -> {
+ String namespace = namespacePath.getFileName().toString();
+ try (Stream dimensions = Files.list(namespacePath)) {
+ dimensions.forEach(p -> {
+ File dimensionFile = p.toFile();
+ if (!dimensionFile.isDirectory()) return;
+ worldKeys.add(Key.key(namespace, dimensionFile.getName()));
+ });
+ } catch (IOException e) {
+ // If we can't read the dimensions directory, we just skip it.
+ }
+ });
+ } catch (IOException e) {
+ // If we can't read the dimensions directory, we just return an empty list of suggestions.
+ return SuggestionSupplier.empty();
+ }
+
+ return _ -> worldKeys.stream().map(k -> "\"" + k + "\"").toList();
+ }
+
+}
diff --git a/minecraft/paper/src/main/java/xyz/bitsquidd/bits/paper/location/wrapper/ChunkCoordinate.java b/minecraft/paper/src/main/java/xyz/bitsquidd/bits/paper/location/wrapper/ChunkCoordinate.java
index ab9cde47..aa840a33 100644
--- a/minecraft/paper/src/main/java/xyz/bitsquidd/bits/paper/location/wrapper/ChunkCoordinate.java
+++ b/minecraft/paper/src/main/java/xyz/bitsquidd/bits/paper/location/wrapper/ChunkCoordinate.java
@@ -7,12 +7,15 @@
package xyz.bitsquidd.bits.paper.location.wrapper;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.LevelChunk;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import java.util.Objects;
+
/**
* Immutable record representing the coordinates of a chunk in a specific world.
*
@@ -40,11 +43,27 @@ public static ChunkCoordinate fromChunk(Chunk chunk) {
);
}
+ public static ChunkCoordinate fromChunk(ChunkAccess chunkAccess) {
+ if (!(chunkAccess instanceof LevelChunk levelChunk)) throw new IllegalArgumentException("ChunkAccess must be an instance of LevelChunk");
+
+ return new ChunkCoordinate(
+ levelChunk.getPos().x(),
+ levelChunk.getPos().z(),
+ levelChunk.getLevel().getWorld()
+ );
+ }
+
+
public int manhattanDistance(ChunkCoordinate other) {
if (!Objects.equals(this.world, other.world)) throw new IllegalArgumentException("Cannot calculate distance between chunks in different worlds");
return Math.abs(this.x - other.x) + Math.abs(this.z - other.z);
}
+ public int euclideanDistance(ChunkCoordinate other) {
+ if (!Objects.equals(this.world, other.world)) throw new IllegalArgumentException("Cannot calculate distance between chunks in different worlds");
+ return (int)Math.sqrt(Math.pow(this.x - other.x, 2) + Math.pow(this.z - other.z, 2));
+ }
+
@Override
public int hashCode() {
diff --git a/minecraft/paper/src/main/java/xyz/bitsquidd/bits/paper/util/Keys.java b/minecraft/paper/src/main/java/xyz/bitsquidd/bits/paper/util/Keys.java
new file mode 100644
index 00000000..9bc576b9
--- /dev/null
+++ b/minecraft/paper/src/main/java/xyz/bitsquidd/bits/paper/util/Keys.java
@@ -0,0 +1,29 @@
+package xyz.bitsquidd.bits.paper.util;
+
+import net.kyori.adventure.key.Key;
+import org.bukkit.NamespacedKey;
+
+import java.util.Locale;
+
+
+public final class Keys {
+ private Keys() {}
+
+ public static NamespacedKey NSK(final Key key) {
+ return new NamespacedKey(key.namespace(), key.value());
+ }
+
+ public static String sanitize(String key) {
+ // Normalize to lower\-case and strip surrounding whitespace
+ String sanitized = key.trim().toLowerCase(Locale.ROOT);
+
+ // Replace any sequence of invalid characters with a single underscore
+ sanitized = sanitized.replaceAll("[^a-z0-9_]+", "_");
+
+ // Remove leading/trailing underscores
+ sanitized = sanitized.replaceAll("^_+|_+$", "");
+
+ return sanitized;
+ }
+
+}
diff --git a/minecraft/paper/src/main/java/xyz/bitsquidd/bits/paper/wrapper/WorldDefinition.java b/minecraft/paper/src/main/java/xyz/bitsquidd/bits/paper/wrapper/WorldDefinition.java
new file mode 100644
index 00000000..0a00452f
--- /dev/null
+++ b/minecraft/paper/src/main/java/xyz/bitsquidd/bits/paper/wrapper/WorldDefinition.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of a Bit libraries package.
+ * Licensed under the GNU Lesser General Public License v3.0.
+ *
+ * Copyright (c) 2023-2026 ImBit
+ */
+
+package xyz.bitsquidd.bits.paper.wrapper;
+
+import net.kyori.adventure.key.Key;
+import org.bukkit.Bukkit;
+import org.bukkit.NamespacedKey;
+import org.bukkit.World;
+import org.bukkit.WorldCreator;
+
+import java.util.Objects;
+
+
+/**
+ * A representation of a Bukkit world definition. May be currently loaded or unloaded.
+ *
+ * @since 0.0.20
+ */
+public final class WorldDefinition {
+ public final NamespacedKey key;
+
+ private WorldDefinition(NamespacedKey key) {
+ this.key = key;
+ }
+
+ public static WorldDefinition of(Key key) {
+ return new WorldDefinition(new NamespacedKey(key.namespace(), key.value()));
+ }
+
+
+ public World load() {
+ World loaded = Bukkit.getWorld(key);
+ if (loaded != null) return loaded;
+
+ return Objects.requireNonNull(Bukkit.createWorld(WorldCreator.ofKey(key)), "Failed to load world with key: " + key);
+ }
+
+ public void unload(final boolean save) {
+ World loaded = Bukkit.getWorld(key);
+ if (loaded != null) Bukkit.unloadWorld(loaded, save);
+ }
+
+
+}
diff --git a/minecraft/paper/src/main/java/xyz/bitsquidd/bits/paper/wrapper/package-info.java b/minecraft/paper/src/main/java/xyz/bitsquidd/bits/paper/wrapper/package-info.java
new file mode 100644
index 00000000..1f9ea5e4
--- /dev/null
+++ b/minecraft/paper/src/main/java/xyz/bitsquidd/bits/paper/wrapper/package-info.java
@@ -0,0 +1,4 @@
+@NotNullByDefault
+package xyz.bitsquidd.bits.paper.wrapper;
+
+import org.jetbrains.annotations.NotNullByDefault;
\ No newline at end of file
diff --git a/minecraft/src/main/java/xyz/bitsquidd/bits/mc/animation/impl/BasicAnimation.java b/minecraft/src/main/java/xyz/bitsquidd/bits/mc/animation/impl/BasicAnimation.java
index 391198c2..abc7feaf 100644
--- a/minecraft/src/main/java/xyz/bitsquidd/bits/mc/animation/impl/BasicAnimation.java
+++ b/minecraft/src/main/java/xyz/bitsquidd/bits/mc/animation/impl/BasicAnimation.java
@@ -59,8 +59,7 @@ public AnimationPose evaluate(int ticks) {
float localProgress = Math.min((progress - kf.progress()) / intervalLength, 1f);
float easedProgress = kf.easing().progress(localProgress);
- AnimationPose delta = AnimationPose.lerp(AnimationPose.identity(), kf.pose(), easedProgress);
- result = AnimationPose.add(result, delta);
+ result = AnimationPose.lerp(kf.pose(), next.pose(), easedProgress);
}
return result;
}