diff --git a/.github/workflows/publish.yml b/.github/workflows/build-and-publish.yml
similarity index 59%
rename from .github/workflows/publish.yml
rename to .github/workflows/build-and-publish.yml
index eb18b0c8..088ac21c 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/build-and-publish.yml
@@ -1,12 +1,15 @@
-name: Publish to GitHub Packages
+name: Build and upload
on:
+ push:
+ pull_request:
+ types:
+ - opened
+ - reopened
+ workflow_dispatch:
release:
types:
- - published
- push:
- branches:
- - new-command-system-1.20.6
+ - created
env:
USERNAME: ${{ github.actor }}
@@ -17,13 +20,12 @@ jobs:
build:
runs-on: ubuntu-latest
permissions:
- contents: write
- packages: write
+ contents: read
steps:
- name: Get current date
run: echo "WLIB_BUILD_DATE=$(date +'%Y-%m-%d_%H-%M-%S')" >> "$GITHUB_ENV"
- name: Get snapshot number
- run: echo "WLIB_SNAPSHOT_NUMBER=$((${{ github.run_number }}+${{ github.run_attempt }}))" >> $GITHUB_ENV
+ run: echo "WLIB_SNAPSHOT_NUMBER=$((${{ github.run_number }}+${{ github.run_attempt }}+68))" >> $GITHUB_ENV
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
@@ -48,5 +50,27 @@ jobs:
for file in */build/libs/*.jar; do
gh release upload ${{ github.event.release.tag_name }} $file
done
- - name: Publish
+ publish:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ packages: write
+ if: ${{ github.event_name != 'pull_request' }}
+ needs:
+ - build
+ steps:
+ - name: Get current date
+ run: echo "WLIB_BUILD_DATE=$(date +'%Y-%m-%d_%H-%M-%S')" >> "$GITHUB_ENV"
+ - name: Get snapshot number
+ run: echo "WLIB_SNAPSHOT_NUMBER=$((${{ github.run_number }}+${{ github.run_attempt }}+68))" >> $GITHUB_ENV
+ - uses: actions/checkout@v4
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
+ with:
+ java-version: 21
+ distribution: temurin
+ - name: Setup Gradle
+ uses: gradle/actions/setup-gradle@v4
+ - name: Clean, Build and Publish
run: ./gradlew publish
+
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
deleted file mode 100644
index aae6a066..00000000
--- a/.github/workflows/build.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-name: Build and upload
-
-on:
- push:
- pull_request:
- types:
- - opened
- - reopened
- workflow_dispatch:
-
-env:
- USERNAME: ${{ github.actor }}
- TOKEN: ${{ secrets.GITHUB_TOKEN }}
- WLIB_SNAPSHOT: ${{ github.event_name != 'release' }}
-
-jobs:
- build:
- runs-on: ubuntu-latest
- permissions:
- contents: read
- steps:
- - name: Get current date
- run: echo "WLIB_BUILD_DATE=$(date +'%Y-%m-%d_%H-%M-%S')" >> "$GITHUB_ENV"
- - name: Get snapshot number
- run: echo "WLIB_SNAPSHOT_NUMBER=$((${{ github.run_number }}+${{ github.run_attempt }}))" >> $GITHUB_ENV
- - uses: actions/checkout@v4
- - name: Set up JDK 21
- uses: actions/setup-java@v4
- with:
- java-version: 21
- distribution: temurin
- - name: Setup Gradle
- uses: gradle/actions/setup-gradle@v4
- - name: Build
- run: ./gradlew build
- - name: Upload results
- uses: actions/upload-artifact@v4
- with:
- path: "**/build/libs/*.jar"
- name: WLib-${{ env.WLIB_BUILD_DATE }}
- if-no-files-found: error
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 0e100001..9137fbef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
**/build/
.idea/
.gradle/
+/build.properties
diff --git a/build.gradle b/build.gradle
index 014ee24d..14cae67c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,7 @@
plugins {
- id("com.gradleup.shadow") version "8.3.2"
+ id("com.gradleup.shadow") version "9.2.2"
id 'maven-publish'
+ id("systems.manifold.manifold-gradle-plugin") version "0.0.2-alpha" apply false
}
subprojects {
@@ -9,7 +10,7 @@ subprojects {
apply plugin: 'maven-publish'
group = 'com.wizardlybump17.wlib'
- version = "1.6.8-ncs-1.20.6+" + ("true".equalsIgnoreCase(System.getenv("WLIB_SNAPSHOT")) ? "-SNAPSHOT." + System.getenv("WLIB_SNAPSHOT_NUMBER") : "")
+ version = "1.6.8-ncs-2-1.20.6+" + ("true".equalsIgnoreCase(System.getenv("WLIB_SNAPSHOT")) ? "-SNAPSHOT." + System.getenv("WLIB_SNAPSHOT_NUMBER") : "")
repositories {
mavenLocal()
@@ -20,9 +21,6 @@ subprojects {
}
}
- sourceCompatibility = '21'
- targetCompatibility = '21'
-
tasks {
compileJava {
options.encoding = 'UTF-8'
@@ -38,26 +36,22 @@ subprojects {
java {
withSourcesJar()
+ sourceCompatibility = 21
+ targetCompatibility = 21
}
}
subprojects {
dependencies {
- testImplementation(platform("org.junit:junit-bom:5.10.3"))
- testImplementation("org.junit.jupiter:junit-jupiter:5.10.3")
+ testImplementation(platform("org.junit:junit-bom:6.0.0"))
+ testImplementation("org.junit.jupiter:junit-jupiter")
+ testImplementation("org.junit.platform:junit-platform-launcher")
}
test {
useJUnitPlatform()
}
- if (project.name == 'versions' || project.name.matches('v\\d_\\d+_R\\d+')) {
- tasks.withType(PublishToMavenRepository).configureEach {
- it.enabled = false
- }
- return
- }
-
publishing {
repositories {
maven {
@@ -72,23 +66,10 @@ subprojects {
publications {
gpr(MavenPublication) {
from(components.java)
- pom.withXml {
- asNode().dependencies.dependency.each { dep ->
- if (dep.artifactId.last().value().last().matches('v\\d_\\d+_R\\d+'))
- dep.parent().remove(dep)
- }
- }
}
}
}
- dependencies {
- testImplementation(
- platform('org.junit:junit-bom:5.10.2'),
- 'org.junit.jupiter:junit-jupiter:5.10.2'
- )
- }
-
test {
useJUnitPlatform()
testLogging {
diff --git a/bukkit-utils/src/main/java/com/wizardlybump17/wlib/util/bukkit/MiniMessageUtil.java b/bukkit-utils/src/main/java/com/wizardlybump17/wlib/util/bukkit/MiniMessageUtil.java
index 6cbc701a..c0157604 100644
--- a/bukkit-utils/src/main/java/com/wizardlybump17/wlib/util/bukkit/MiniMessageUtil.java
+++ b/bukkit-utils/src/main/java/com/wizardlybump17/wlib/util/bukkit/MiniMessageUtil.java
@@ -1,15 +1,22 @@
package com.wizardlybump17.wlib.util.bukkit;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonPrimitive;
+import com.wizardlybump17.wlib.util.StringUtil;
import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.Map;
public final class MiniMessageUtil {
+ private static final @NotNull TextComponent NULL = Component.text("null");
+
private MiniMessageUtil() {
}
@@ -21,18 +28,60 @@ private MiniMessageUtil() {
TagResolver[] resolvers = new TagResolver[placeholders.size()];
int resolverIndex = 0;
for (Map.Entry entry : placeholders.entrySet()) {
- String key = entry.getKey();
+ String key = StringUtil.pascalToCamel(entry.getKey());
Object value = entry.getValue();
resolvers[resolverIndex++] = TagResolver.builder()
.tag(
key,
- Tag.inserting(value instanceof Component component ? component : Component.text(String.valueOf(value)))
+ Tag.inserting(getInsertion(value))
+ )
+ .build();
+ }
+ return miniMessage.deserialize(message, resolvers);
+ }
+
+ public static @NotNull Component getMessage(@NotNull String message, @NotNull String placeholderKey, @NotNull Object placeholderValue, @NotNull String prefix, @NotNull Map additionalPlaceholders) {
+ MiniMessage miniMessage = MiniMessage.miniMessage();
+ if (additionalPlaceholders.isEmpty())
+ return getMessage(message, Map.of(placeholderKey, placeholderValue));
+
+ TagResolver[] resolvers = new TagResolver[additionalPlaceholders.size() + 1];
+ int resolverIndex = 0;
+ for (Map.Entry entry : additionalPlaceholders.entrySet()) {
+ String key = StringUtil.pascalToCamel(entry.getKey());
+ Object value = entry.getValue();
+ resolvers[resolverIndex++] = TagResolver.builder()
+ .tag(
+ prefix + key,
+ Tag.inserting(getInsertion(value))
)
.build();
}
+ resolvers[resolverIndex] = TagResolver.builder()
+ .tag(
+ prefix + placeholderKey,
+ Tag.inserting(getInsertion(placeholderValue))
+ )
+ .build();
return miniMessage.deserialize(message, resolvers);
}
+ private static @NotNull Component getInsertion(@Nullable Object object) {
+ if (object == null)
+ return NULL;
+
+ return switch (object) {
+ case Component component -> component;
+ case JsonPrimitive jsonPrimitive -> Component.text(jsonPrimitive.getAsString());
+ case JsonNull ignored -> NULL;
+ default -> Component.text(String.valueOf(object));
+ };
+ }
+
+ public static @NotNull Component getMessage(@NotNull String message, @NotNull String placeholderKey, @NotNull Object placeholderValue, @NotNull Map additionalPlaceholders) {
+ return getMessage(message, placeholderKey, placeholderValue, "", additionalPlaceholders);
+ }
+
public static @NotNull Component getMessage(@NotNull String message) {
return getMessage(message, Map.of());
}
diff --git a/bukkit-utils/src/main/java/com/wizardlybump17/wlib/util/bukkit/collector/ComponentCollector.java b/bukkit-utils/src/main/java/com/wizardlybump17/wlib/util/bukkit/collector/ComponentCollector.java
new file mode 100644
index 00000000..efd535a2
--- /dev/null
+++ b/bukkit-utils/src/main/java/com/wizardlybump17/wlib/util/bukkit/collector/ComponentCollector.java
@@ -0,0 +1,65 @@
+package com.wizardlybump17.wlib.util.bukkit.collector;
+
+import net.kyori.adventure.text.Component;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+
+public class ComponentCollector implements Collector {
+
+ public static final @NotNull ComponentCollector COMMA = new ComponentCollector(Component.text(", "));
+ public static final @NotNull ComponentCollector NEW_LINE = new ComponentCollector(Component.text("\n"));
+ public static final @NotNull ComponentCollector EMPTY = new ComponentCollector(Component.empty());
+
+ private final @NotNull Component separator;
+
+ public ComponentCollector(@NotNull Component separator) {
+ this.separator = separator;
+ }
+
+ @Override
+ public @NotNull Supplier supplier() {
+ return () -> new Component[1];
+ }
+
+ @Override
+ public @NotNull BiConsumer accumulator() {
+ return (array, component) -> {
+ if (array[0] == null)
+ array[0] = component;
+ else
+ array[0] = array[0].append(separator).append(component);
+ };
+ }
+
+ @Override
+ public @NotNull BinaryOperator combiner() {
+ return (left, right) -> {
+ if (left[0] == null)
+ left[0] = right[0];
+ else
+ left[0] = left[0].append(separator).append(right[0]);
+ return left;
+ };
+ }
+
+ @Override
+ public @NotNull Function finisher() {
+ return array -> Objects.requireNonNullElseGet(array[0], Component::empty);
+ }
+
+ @Override
+ public @NotNull Set characteristics() {
+ return Set.of();
+ }
+
+ public @NotNull Component getSeparator() {
+ return separator;
+ }
+}
diff --git a/bungee/build.gradle b/bungee/build.gradle
index 9b89a640..713c008e 100644
--- a/bungee/build.gradle
+++ b/bungee/build.gradle
@@ -8,7 +8,7 @@ dependencies {
compileOnly(
'org.projectlombok:lombok:1.18.32',
'org.jetbrains:annotations:23.0.0',
- 'net.md-5:bungeecord-api:1.18-R0.1-SNAPSHOT',
+ 'net.md-5:bungeecord-api:1.21-R0.3',
)
annotationProcessor('org.projectlombok:lombok:1.18.32')
implementation(
diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommand.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommand.java
deleted file mode 100644
index 494572cd..00000000
--- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommand.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.wizardlybump17.wlib.bungee.command;
-
-import com.wizardlybump17.wlib.command.CommandManager;
-import com.wizardlybump17.wlib.command.holder.Command;
-import com.wizardlybump17.wlib.command.holder.CommandExecutor;
-
-public class BungeeCommand implements Command {
-
- private CommandExecutor executor;
-
- @Override
- public void setExecutor(CommandExecutor executor) {
- this.executor = executor;
- }
-
- @Override
- public CommandExecutor getExecutor() {
- return executor;
- }
-
- @Override
- public CommandExecutor getDefaultExecutor(CommandManager manager, String name) {
- return new BungeeCommandExecutor(manager, name);
- }
-}
diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandExecutor.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandExecutor.java
deleted file mode 100644
index cca749bd..00000000
--- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandExecutor.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.wizardlybump17.wlib.bungee.command;
-
-import com.wizardlybump17.wlib.bungee.command.sender.BungeeCommandSender;
-import com.wizardlybump17.wlib.command.CommandManager;
-import com.wizardlybump17.wlib.command.exception.CommandException;
-import com.wizardlybump17.wlib.command.holder.CommandExecutor;
-import net.md_5.bungee.api.plugin.Command;
-
-import java.util.logging.Level;
-
-public class BungeeCommandExecutor extends Command implements CommandExecutor {
-
- private final CommandManager manager;
-
- public BungeeCommandExecutor(CommandManager manager, String name) {
- super(name);
- this.manager = manager;
- }
-
- @Override
- public void execute(com.wizardlybump17.wlib.command.sender.CommandSender> sender, String commandName, String[] args) throws CommandException {
- manager.execute(sender, commandName + " " + String.join(" ", args));
- }
-
- @Override
- public void execute(net.md_5.bungee.api.CommandSender sender, String[] args) {
- try {
- execute(new BungeeCommandSender(sender), getName(), args);
- } catch (CommandException e) {
- manager.getHolder().getLogger().log(Level.SEVERE, "Error while executing a command", e);
- }
- }
-}
diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandHolder.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandHolder.java
deleted file mode 100644
index 878d093c..00000000
--- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandHolder.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.wizardlybump17.wlib.bungee.command;
-
-import com.wizardlybump17.wlib.command.holder.Command;
-import com.wizardlybump17.wlib.command.holder.CommandHolder;
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-import net.md_5.bungee.api.plugin.Plugin;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.logging.Logger;
-
-@RequiredArgsConstructor
-public class BungeeCommandHolder implements CommandHolder {
-
- private final Plugin plugin;
- private final Map commands = new HashMap<>();
-
- @Override
- public Command getCommand(String name) {
- if (commands.containsKey(name))
- return commands.get(name);
- BungeeCommand command = new BungeeCommand();
- commands.put(name, command);
- return command;
- }
-
- @Override
- public Plugin getHandle() {
- return plugin;
- }
-
- @Override
- public @NonNull Logger getLogger() {
- return plugin.getLogger();
- }
-}
diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandManager.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandManager.java
deleted file mode 100644
index bfd1bafa..00000000
--- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/BungeeCommandManager.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.wizardlybump17.wlib.bungee.command;
-
-import com.wizardlybump17.wlib.command.CommandManager;
-import com.wizardlybump17.wlib.command.registered.RegisteredCommand;
-import net.md_5.bungee.api.plugin.Command;
-import net.md_5.bungee.api.plugin.Plugin;
-import org.jetbrains.annotations.NotNull;
-
-public class BungeeCommandManager extends CommandManager {
-
- public BungeeCommandManager(BungeeCommandHolder holder) {
- super(holder);
- }
-
- @Override
- public void registerCommands(@NotNull Object @NotNull ... objects) {
- super.registerCommands(objects);
- for (Object object : objects) {
- for (RegisteredCommand command : getCommands(object)) {
- Plugin plugin = (Plugin) holder.getHandle();
- plugin.getProxy().getPluginManager().registerCommand(plugin, (Command) holder.getCommand(command.getName()).getExecutor());
- }
- }
- }
-}
diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/BungeeCommandSender.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/BungeeCommandSender.java
index 702f31eb..f211ce88 100644
--- a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/BungeeCommandSender.java
+++ b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/command/sender/BungeeCommandSender.java
@@ -1,11 +1,16 @@
package com.wizardlybump17.wlib.bungee.command.sender;
+import com.wizardlybump17.wlib.bungee.util.collector.ComponentCollector;
import lombok.RequiredArgsConstructor;
+import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ConnectedPlayer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import java.util.ArrayList;
+import java.util.List;
import java.util.UUID;
@RequiredArgsConstructor
@@ -28,6 +33,35 @@ public void sendMessage(String... messages) {
handle.sendMessage(TextComponent.fromLegacyText(String.join("\n", messages)));
}
+ @Override
+ public void sendMessage(@Nullable Object message) {
+ switch (message) {
+ case null -> handle.sendMessage(new TextComponent("null"));
+ case BaseComponent component -> handle.sendMessage(component);
+ default -> handle.sendMessage(new TextComponent(String.valueOf(message)));
+ }
+ }
+
+ @Override
+ public void sendMessage(@Nullable Object @Nullable ... messages) {
+ if (messages == null) {
+ sendMessage((Object) null);
+ return;
+ }
+
+ List components = new ArrayList<>(messages.length);
+ for (Object message : messages) {
+ switch (message) {
+ case null -> components.add(new TextComponent("null"));
+ case BaseComponent component -> components.add(component);
+ case String string -> components.add(new TextComponent(string));
+ default -> components.add(new TextComponent(String.valueOf(message)));
+ }
+ }
+
+ handle.sendMessage(components.stream().collect(ComponentCollector.NEW_LINE));
+ }
+
@Override
public String getName() {
return handle.getName();
diff --git a/bungee/src/main/java/com/wizardlybump17/wlib/bungee/util/collector/ComponentCollector.java b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/util/collector/ComponentCollector.java
new file mode 100644
index 00000000..55c83b05
--- /dev/null
+++ b/bungee/src/main/java/com/wizardlybump17/wlib/bungee/util/collector/ComponentCollector.java
@@ -0,0 +1,72 @@
+package com.wizardlybump17.wlib.bungee.util.collector;
+
+import net.md_5.bungee.api.chat.BaseComponent;
+import net.md_5.bungee.api.chat.TextComponent;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+
+public class ComponentCollector implements Collector {
+
+ public static final @NotNull ComponentCollector COMMA = new ComponentCollector(new TextComponent(", "));
+ public static final @NotNull ComponentCollector NEW_LINE = new ComponentCollector(new TextComponent("\n"));
+ public static final @NotNull ComponentCollector EMPTY = new ComponentCollector(new TextComponent());
+
+ private final @NotNull BaseComponent separator;
+
+ public ComponentCollector(@NotNull BaseComponent separator) {
+ this.separator = separator;
+ }
+
+ @Override
+ public @NotNull Supplier supplier() {
+ return () -> new BaseComponent[1];
+ }
+
+ @Override
+ public @NotNull BiConsumer accumulator() {
+ return (array, component) -> {
+ if (array[0] == null)
+ array[0] = component.duplicate();
+ else {
+ array[0] = array[0].duplicate();
+ array[0].addExtra(separator);
+ array[0].addExtra(component);
+ }
+ };
+ }
+
+ @Override
+ public @NotNull BinaryOperator combiner() {
+ return (left, right) -> {
+ if (left[0] == null)
+ left[0] = right[0].duplicate();
+ else {
+ left[0] = left[0].duplicate();
+ left[0].addExtra(separator);
+ left[0].addExtra(right[0].duplicate());
+ }
+ return left;
+ };
+ }
+
+ @Override
+ public @NotNull Function finisher() {
+ return array -> Objects.requireNonNullElseGet(array[0], TextComponent::new);
+ }
+
+ @Override
+ public @NotNull Set characteristics() {
+ return Set.of();
+ }
+
+ public @NotNull BaseComponent getSeparator() {
+ return separator.duplicate();
+ }
+}
diff --git a/commands/build.gradle b/commands/build.gradle
deleted file mode 100644
index 59d9b615..00000000
--- a/commands/build.gradle
+++ /dev/null
@@ -1,19 +0,0 @@
-dependencies {
- compileOnly(
- 'org.projectlombok:lombok:1.18.32',
- 'org.jetbrains:annotations:23.0.0',
- )
- annotationProcessor('org.projectlombok:lombok:1.18.32')
- implementation(
- project(':objects'),
- project(':utils')
- )
- implementation("com.github.sisyphsu:dateparser:1.0.11") {
- exclude(group: "org.projectlombok", module: "lombok")
- }
- testCompileOnly("org.jetbrains:annotations:26.0.1")
-}
-
-shadowJar {
- relocate("com.github.sisyphsu", "com.wizardlybump17.wlib.libs.com.github.sisyphsu")
-}
diff --git a/commands/build.gradle.kts b/commands/build.gradle.kts
new file mode 100644
index 00000000..10b877a5
--- /dev/null
+++ b/commands/build.gradle.kts
@@ -0,0 +1,12 @@
+val annotations = "26.0.1"
+val gson = "2.13.2"
+
+dependencies {
+ implementation("org.jetbrains:annotations:${annotations}")
+ implementation(project(":objects"))
+ implementation(project(":utils"))
+
+ implementation("com.google.code.gson:gson:${gson}")
+
+ testImplementation("org.jetbrains:annotations:${annotations}")
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/Argument.java b/commands/src/main/java/com/wizardlybump17/wlib/command/Argument.java
deleted file mode 100644
index 71416e57..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/Argument.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.wizardlybump17.wlib.command;
-
-import lombok.AccessLevel;
-import lombok.Data;
-import lombok.Getter;
-
-/**
- * Class used in the arguments to represent a command argument.
- * It stores useful information like the argument name and the user input
- * @param the argument type
- */
-@Data
-public class Argument {
-
- private final String name;
- @Getter(AccessLevel.NONE)
- private final T value;
- private final String input;
-
- public T value() {
- return value;
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/Command.java b/commands/src/main/java/com/wizardlybump17/wlib/command/Command.java
new file mode 100644
index 00000000..3419d4f2
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/Command.java
@@ -0,0 +1,78 @@
+package com.wizardlybump17.wlib.command;
+
+import com.wizardlybump17.wlib.command.node.CommandNode;
+import com.wizardlybump17.wlib.command.node.LiteralCommandNode;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Comparator;
+import java.util.Objects;
+
+public class Command implements Comparable {
+
+ public static final @NotNull Comparator COMPARATOR = Comparator.comparing(Command::getFullCommand);
+
+ private final @NotNull LiteralCommandNode root;
+
+ public Command(@NotNull LiteralCommandNode root) {
+ this.root = root;
+ }
+
+ public @NotNull LiteralCommandNode getRoot() {
+ return root;
+ }
+
+ public @NotNull Command merge(@NotNull Command other) {
+ if (other.getClass() != getClass())
+ return other.merge(this);
+ return new Command(root.merge(other.getRoot()));
+ }
+
+ @Override
+ public String toString() {
+ return "Command{" +
+ "root=" + root +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Command command = (Command) o;
+ return Objects.equals(root, command.root);
+ }
+
+ public boolean equalsIgnoreExecutor(@Nullable Object other) {
+ if (other == null || getClass() != other.getClass())
+ return false;
+ Command command = (Command) other;
+ return root.equalsIgnoreExecutor(command.root);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(root);
+ }
+
+ @Override
+ public int compareTo(@NotNull Command other) {
+ return COMPARATOR.compare(this, other);
+ }
+
+ public @NotNull String getFullCommand() {
+ return root.getFullCommand();
+ }
+
+ public @Nullable CommandNode> findNode(@NotNull String name) {
+ return root.findChild(name);
+ }
+
+ public @NotNull String getName() {
+ return root.getName();
+ }
+
+ public int getTotalNodes() {
+ return root.getTotalNodes();
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/CommandManager.java b/commands/src/main/java/com/wizardlybump17/wlib/command/CommandManager.java
deleted file mode 100644
index 08bff110..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/CommandManager.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package com.wizardlybump17.wlib.command;
-
-import com.wizardlybump17.wlib.command.data.CommandData;
-import com.wizardlybump17.wlib.command.exception.CommandException;
-import com.wizardlybump17.wlib.command.extractor.CommandExtractor;
-import com.wizardlybump17.wlib.command.extractor.DirectCommandExtractor;
-import com.wizardlybump17.wlib.command.extractor.MethodCommandExtractor;
-import com.wizardlybump17.wlib.command.holder.CommandHolder;
-import com.wizardlybump17.wlib.command.registered.RegisteredCommand;
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.*;
-
-public class CommandManager {
-
- private final @NotNull Map> commands = new HashMap<>();
- protected final @NotNull CommandHolder> holder;
- private final @NotNull Set commandExtractors = new HashSet<>();
-
- public CommandManager(@NotNull CommandHolder> holder) {
- this.holder = holder;
- commandExtractors.add(new MethodCommandExtractor());
- commandExtractors.add(new DirectCommandExtractor());
- }
-
- public void registerCommands(@NotNull Object @NotNull ... objects) {
- for (Object object : objects)
- for (CommandExtractor extractor : commandExtractors)
- registerCommands(extractor.extract(this, holder, object));
- }
-
- public void registerCommands(@NotNull Collection commands) {
- commands.forEach(this::registerCommand);
- }
-
- public void registerCommand(@NotNull RegisteredCommand command) {
- command.onRegister(this);
- List commands = this.commands.computeIfAbsent(command.getName().toLowerCase(), $ -> new ArrayList<>());
- commands.add(command);
- commands.sort(null);
- }
-
- public void unregisterCommands() {
- commands.forEach((name, commands) -> {
- commands.forEach(command -> command.onUnregister(this));
- commands.clear();
- });
- commands.clear();
- }
-
- public void execute(@NotNull CommandSender> sender, @NotNull String string) throws CommandException {
- if (commands.isEmpty())
- return;
-
- int spaceIndex = string.indexOf(' ');
- String name;
- if (spaceIndex == -1)
- name = string;
- else
- name = string.substring(0, spaceIndex);
-
- for (RegisteredCommand registeredCommand : commands.getOrDefault(name, List.of())) {
- CommandData command = registeredCommand.getCommand();
- CommandResult result = registeredCommand.execute(sender, string);
-
- switch (result) {
- case PERMISSION_FAIL -> {
- String message = command.getPermissionMessage();
- if (message != null)
- sender.sendMessage(message);
- return;
- }
-
- case INVALID_SENDER -> {
- String message = command.getInvalidSenderMessage();
- if (message != null)
- sender.sendMessage(message);
- return;
- }
-
- case SUCCESS, EXCEPTION -> {
- return;
- }
- }
- }
- }
-
- public @NotNull List<@NotNull String> autoComplete(@NotNull CommandSender> sender, @NotNull String string) {
- return List.of();
- }
-
- public @NotNull List getCommands(@NotNull String name) {
- List commands = this.commands.get(name);
- return commands == null ? List.of() : List.copyOf(commands);
- }
-
- public @NotNull List getCommands(@NotNull Object object) {
- List result = new ArrayList<>(commands.size());
- for (List commands : this.commands.values())
- for (RegisteredCommand command : commands)
- if (command.isOwnedBy(object))
- result.add(command);
- return result;
- }
-
- public @NotNull CommandHolder> getHolder() {
- return holder;
- }
-
- public @NotNull Map> getCommands() {
- return Map.copyOf(commands);
- }
-
- public @NotNull Set getCommandExtractors() {
- return Set.copyOf(commandExtractors);
- }
-
- public void addCommandExtractor(@NotNull CommandExtractor extractor) {
- commandExtractors.add(extractor);
- }
-
- public void removeCommandExtractor(@NotNull CommandExtractor extractor) {
- commandExtractors.remove(extractor);
- }
-
- public boolean hasCommandExtractor(@NotNull CommandExtractor extractor) {
- return commandExtractors.contains(extractor);
- }
-}
\ No newline at end of file
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/CommandResult.java b/commands/src/main/java/com/wizardlybump17/wlib/command/CommandResult.java
deleted file mode 100644
index 875696af..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/CommandResult.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.wizardlybump17.wlib.command;
-
-public enum CommandResult {
-
- SUCCESS,
- ARGS_FAIL,
- EXCEPTION,
- PERMISSION_FAIL,
- METHOD_FAIL,
- INVALID_SENDER
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/annotation/Command.java b/commands/src/main/java/com/wizardlybump17/wlib/command/annotation/Command.java
index b941b23b..bdd69c96 100644
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/annotation/Command.java
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/annotation/Command.java
@@ -1,6 +1,5 @@
package com.wizardlybump17.wlib.command.annotation;
-import com.wizardlybump17.wlib.command.sender.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.ElementType;
@@ -8,85 +7,11 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-/**
- * An annotation that describes a command.
- * For a command to be valid the first parameter of the method must implements {@link CommandSender}
- */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Command {
- /**
- * How the sender must type the command in order for it to be triggered.
- * Example:
command hello world
- * In the example above, the sender must type /command hello world, so it can be triggered.
- * To add parameters that depends on the sender input, just put the parameter between <>.
- *
- * Example: command <hello> world
- * The type of each parameter is defined in the method that have this annotation.
- * An example for the command above:
- *
- * @Command(execution = "command <hello> world")
- * public void commandHelloWorld(GenericSender sender, String hello) {
- * System.out.println(sender.getName() + " executed the hello world command with the argument " + hello);
- * }
- *
- *
- * @return how the command must be sent to be triggered
- * @see com.wizardlybump17.wlib.command.args.reader.ArgsReader
- */
- String execution();
+ @NotNull String value();
- /**
- * @return which permission the sender must have to trigger this command
- */
- String permission() default "";
-
- /**
- * Used when the {@link CommandSender} does not have the required {@link #permission()}.
- * @return the message to be sent when the {@link CommandSender} does not have the required {@link #permission()}
- */
- String permissionMessage() default "";
-
- /**
- * @return if the {@link #permissionMessage()} is a field in the class that have this annotation
- */
- boolean permissionMessageIsField() default false;
-
- /**
- * Sets the priority of this command. If the priority is -1, then the priority check is the same as
- * {@code this.execution().split(" ").length}
- *
- * @return the priority of this command
- */
- int priority() default -1;
-
- /**
- * Sets the options of this command.
- * The Bukkit implementation does nothing with this
- *
- * @return the options of this command
- */
- String[] options() default {};
-
- /**
- * @return the description of this command
- */
- String description() default "";
-
- /**
- * Used when the {@link CommandSender} is not valid for this command.
- * @return the message to be sent when the {@link CommandSender} is not valid for this command
- */
- String invalidSenderMessage() default "";
-
- /**
- * @return if the {@link #invalidSenderMessage()} is a field in the class that have this annotation
- */
- boolean invalidSenderMessageIsField() default false;
-
- /**
- * @return the type of the {@link CommandSender#getHandle()} that can execute this command
- */
- @NotNull Class> senderType() default Object.class;
+ @NotNull String permission() default "";
}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/Description.java b/commands/src/main/java/com/wizardlybump17/wlib/command/annotation/NonNullInput.java
similarity index 71%
rename from commands/src/main/java/com/wizardlybump17/wlib/command/Description.java
rename to commands/src/main/java/com/wizardlybump17/wlib/command/annotation/NonNullInput.java
index f66546fc..1060aab8 100644
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/Description.java
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/annotation/NonNullInput.java
@@ -1,4 +1,4 @@
-package com.wizardlybump17.wlib.command;
+package com.wizardlybump17.wlib.command.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -7,7 +7,5 @@
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
-public @interface Description {
-
- String value();
+public @interface NonNullInput {
}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsNode.java
deleted file mode 100644
index 23d07d47..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsNode.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.wizardlybump17.wlib.command.args;
-
-import com.wizardlybump17.wlib.command.Argument;
-import com.wizardlybump17.wlib.command.args.reader.ArgsReader;
-import com.wizardlybump17.wlib.command.args.reader.ArgsReaderException;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * Represents a node of a command argument
- */
-@Data
-@AllArgsConstructor
-public class ArgsNode {
-
- public static final Object EMPTY = new Object();
-
- @NotNull
- private final String name;
- private final boolean userInput;
- @Nullable
- private final ArgsReader> reader;
- @Nullable
- private final String description;
- private final boolean isArgument;
-
- /**
- * Parses the given input
- * @param input the input
- * @return the parsed object
- * @throws ArgsReaderException if the input is invalid
- */
- public Object parse(String input) throws ArgsReaderException {
- if (reader == null)
- return EMPTY;
-
- Object object = reader.read(input);
- if (isArgument)
- return new Argument<>(name, object, input);
-
- return object;
- }
-
- public static @NotNull ArgsNode literal(@NotNull String string) {
- return new ArgsNode(string, false, null, null, false);
- }
-
- public static @NotNull ArgsNode userInput(@NotNull String name, @NotNull ArgsReader> reader) {
- return new ArgsNode(name, true, reader, null, false);
- }
-
- public static @NotNull ArgsNode userInput(@NotNull String name, @NotNull Class> type) {
- return userInput(name, ArgsReaderRegistry.INSTANCE.getReader(type));
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsReaderRegistry.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsReaderRegistry.java
deleted file mode 100644
index 09c8f99d..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsReaderRegistry.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.wizardlybump17.wlib.command.args;
-
-import com.wizardlybump17.wlib.command.args.reader.*;
-import com.wizardlybump17.wlib.object.Registry;
-
-/**
- * A registry for the {@link ArgsReader}.
- * The key is the {@link ArgsReader#getClass()} and the value is the {@link ArgsReader}
- */
-public class ArgsReaderRegistry extends Registry>, ArgsReader>> {
-
- public static final ArgsReaderRegistry INSTANCE = new ArgsReaderRegistry();
-
- static {
- INSTANCE.add(new StringReader());
- INSTANCE.add(new ByteReader());
- INSTANCE.add(new ByteArrayReader());
- INSTANCE.add(new ShortReader());
- INSTANCE.add(new ShortArrayReader());
- INSTANCE.add(new IntegerReader());
- INSTANCE.add(new IntegerArrayReader());
- INSTANCE.add(new FloatReader());
- INSTANCE.add(new FloatArrayReader());
- INSTANCE.add(new DoubleReader());
- INSTANCE.add(new DoubleArrayReader());
- INSTANCE.add(new BigIntegerArgsReader());
- INSTANCE.add(new BigDecimalArgsReader());
- INSTANCE.add(new UUIDReader());
- INSTANCE.add(new BooleanReader());
- INSTANCE.add(new DateReader());
- }
-
- private ArgsReaderRegistry() {
- }
-
- @SuppressWarnings("unchecked")
- public void add(ArgsReader> reader) {
- put((Class extends ArgsReader>>) reader.getClass(), reader);
- }
-
- /**
- * Gets the first reader that can read the specified type
- * @param type the type
- * @return the reader
- */
- public ArgsReader> getReader(Class> type) {
- for (ArgsReader> reader : getMap().values())
- if (reader.getTypes().contains(type))
- return reader;
- return null;
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsReaderType.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsReaderType.java
deleted file mode 100644
index 2e8fc4ca..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/ArgsReaderType.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.wizardlybump17.wlib.command.args;
-
-import com.wizardlybump17.wlib.command.args.reader.ArgsReader;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Specified the {@link ArgsReader} of the parameter
- */
-@Target(ElementType.PARAMETER)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface ArgsReaderType {
-
- /**
- * @return the class of the {@link ArgsReader} to use
- */
- Class extends ArgsReader>> value();
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ArgsReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ArgsReader.java
deleted file mode 100644
index d8a6fa5a..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ArgsReader.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-import lombok.NonNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Used to read and convert the args from string to the specified type
- * @param which type the string will be converted to
- */
-public abstract class ArgsReader {
-
- private List> typesCache;
-
- /**
- * The type that the string will be converted to.
- * If it is {@code null} then you should use the {@link com.wizardlybump17.wlib.command.args.ArgsReaderType} annotation on your parameter
- * @return the type that the string will be converted to
- * @deprecated Use the {@link #getTypes()} instead
- */
- @Nullable
- @Deprecated
- public abstract Class getType();
-
- /**
- * @return the types that {@code this} {@link ArgsReader} can accept in the method parameter
- */
- public @NonNull List> getTypes() {
- if (typesCache == null)
- typesCache = Collections.singletonList(getType());
- return typesCache;
- }
-
- public abstract T read(String string) throws ArgsReaderException;
-
- /**
- * Gives the suggestions for the specified sender and current argument (optional).
- * @param sender who is executing the command
- * @param current the current argument
- * @return a {@link List} of suggestions
- */
- @NonNull
- public List<@NonNull String> autoComplete(@NonNull CommandSender> sender, @NonNull String current) {
- return Collections.emptyList();
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ArgsReaderException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ArgsReaderException.java
deleted file mode 100644
index e24704dd..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ArgsReaderException.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-public class ArgsReaderException extends Exception {
-
- public ArgsReaderException(String message) {
- super(message);
- }
-
- public ArgsReaderException(Throwable cause) {
- super(cause);
- }
-
- public ArgsReaderException(String message, Throwable cause) {
- super(message, cause);
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BigDecimalArgsReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BigDecimalArgsReader.java
deleted file mode 100644
index 638016ba..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BigDecimalArgsReader.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-import java.math.BigDecimal;
-
-public class BigDecimalArgsReader extends ArgsReader {
-
- @Override
- public Class getType() {
- return BigDecimal.class;
- }
-
- @Override
- public BigDecimal read(String string) {
- try {
- return new BigDecimal(string);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BigIntegerArgsReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BigIntegerArgsReader.java
deleted file mode 100644
index bf39e180..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BigIntegerArgsReader.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-import java.math.BigInteger;
-
-public class BigIntegerArgsReader extends ArgsReader {
-
- @Override
- public Class getType() {
- return BigInteger.class;
- }
-
- @Override
- public BigInteger read(String string) {
- try {
- return new BigInteger(string);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BooleanReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BooleanReader.java
deleted file mode 100644
index 539558be..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/BooleanReader.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-import lombok.NonNull;
-
-import java.util.List;
-
-public class BooleanReader extends ArgsReader {
-
- public static final @NonNull List> TYPES = List.of(boolean.class, Boolean.class);
- public static final List SUGGESTIONS = List.of("true", "false");
-
- @Override
- public Class getType() {
- return boolean.class;
- }
-
- @Override
- public @NonNull List> getTypes() {
- return TYPES;
- }
-
- @Override
- public Boolean read(String string) throws ArgsReaderException {
- if (string.equalsIgnoreCase("true"))
- return true;
- if (string.equalsIgnoreCase("false"))
- return false;
-
- return null;
- }
-
- @Override
- public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender> sender, @NonNull String current) {
- return SUGGESTIONS;
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ByteArrayReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ByteArrayReader.java
deleted file mode 100644
index ed29be28..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ByteArrayReader.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-public class ByteArrayReader extends ArgsReader {
-
- @Override
- public Class getType() {
- return byte[].class;
- }
-
- @Override
- public byte[] read(String string) throws ArgsReaderException {
- try {
- final String[] strings = string.split(" ");
-
- byte[] result = new byte[strings.length];
- for (int i = 0; i < result.length; i++)
- result[i] = Byte.parseByte(strings[i]);
- return result;
- } catch (NumberFormatException e) {
- throw new ArgsReaderException("expected a byte array in string form but got " + string);
- }
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ByteReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ByteReader.java
deleted file mode 100644
index 00c9056c..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ByteReader.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-import lombok.NonNull;
-
-import java.util.List;
-
-public class ByteReader extends ArgsReader {
-
- public static final List SUGGESTIONS = List.of("-128", "0", "127");
- public static final @NonNull List> TYPES = List.of(byte.class, Byte.class);
-
- @Override
- public Class getType() {
- return byte.class;
- }
-
- @Override
- public @NonNull List> getTypes() {
- return TYPES;
- }
-
- @Override
- public Byte read(String string) throws ArgsReaderException {
- try {
- return Byte.parseByte(string);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender> sender, @NonNull String current) {
- return SUGGESTIONS;
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DateReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DateReader.java
deleted file mode 100644
index 70f576eb..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DateReader.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-import com.github.sisyphsu.dateparser.DateParserUtils;
-import lombok.NonNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.time.format.DateTimeParseException;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Supplier;
-
-public class DateReader extends ArgsReader {
-
- //i could do a dedicated system for this, but this one is simpler and works so idc
- public static final @NonNull Map> PROVIDERS = new HashMap<>();
-
- static {
- PROVIDERS.put("now", Date::new);
- PROVIDERS.put("today", () -> getToday().getTime());
- PROVIDERS.put("yesterday", () -> {
- Calendar today = getToday();
- today.add(Calendar.DAY_OF_YEAR, -1);
- return today.getTime();
- });
- PROVIDERS.put("tomorrow", () -> {
- Calendar today = getToday();
- today.add(Calendar.DAY_OF_YEAR, 1);
- return today.getTime();
- });
- }
-
- @Override
- public @NonNull Class getType() {
- return Date.class;
- }
-
- @Override
- public @Nullable Date read(@NonNull String string) {
- Supplier provider = PROVIDERS.get(string.toLowerCase());
- if (provider != null)
- return provider.get();
-
- try {
- return DateParserUtils.parseDate(string);
- } catch (DateTimeParseException e) {
- return null; //i need to rework the command system :C
- }
- }
-
- public static @NonNull Calendar getToday() {
- Calendar calendar = Calendar.getInstance();
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.MILLISECOND, 0);
- return calendar;
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DoubleArrayReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DoubleArrayReader.java
deleted file mode 100644
index 7ebd303f..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DoubleArrayReader.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-public class DoubleArrayReader extends ArgsReader {
-
- @Override
- public Class getType() {
- return double[].class;
- }
-
- @Override
- public double[] read(String string) throws ArgsReaderException {
- try {
- final String[] strings = string.split(" ");
-
- double[] result = new double[strings.length];
- for (int i = 0; i < result.length; i++)
- result[i] = Double.parseDouble(strings[i]);
- return result;
- } catch (NumberFormatException e) {
- throw new ArgsReaderException("expected a double array in string form but got " + string);
- }
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DoubleReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DoubleReader.java
deleted file mode 100644
index b7c4f356..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/DoubleReader.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-import lombok.NonNull;
-
-import java.util.List;
-
-public class DoubleReader extends ArgsReader {
-
- public static final List SUGGESTIONS = List.of("1", "1.5", "10", "10.5");
- public static final @NonNull List> TYPES = List.of(double.class, Double.class);
-
- @Override
- public Class getType() {
- return double.class;
- }
-
- @Override
- public @NonNull List> getTypes() {
- return TYPES;
- }
-
- @Override
- public Double read(String string) throws ArgsReaderException {
- try {
- return Double.parseDouble(string);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender> sender, @NonNull String current) {
- return SUGGESTIONS;
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/FloatArrayReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/FloatArrayReader.java
deleted file mode 100644
index bdb28e86..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/FloatArrayReader.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-public class FloatArrayReader extends ArgsReader {
-
- @Override
- public Class getType() {
- return float[].class;
- }
-
- @Override
- public float[] read(String string) throws ArgsReaderException {
- try {
- final String[] strings = string.split(" ");
-
- float[] result = new float[strings.length];
- for (int i = 0; i < result.length; i++)
- result[i] = Float.parseFloat(strings[i]);
- return result;
- } catch (NumberFormatException e) {
- throw new ArgsReaderException("expected a float array in string form but got " + string);
- }
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/FloatReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/FloatReader.java
deleted file mode 100644
index f90a346a..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/FloatReader.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-import lombok.NonNull;
-
-import java.util.List;
-
-public class FloatReader extends ArgsReader {
-
- public static final List SUGGESTIONS = List.of("1", "1.5", "10", "10.5");
- public static final @NonNull List> TYPES = List.of(float.class, Float.class);
-
- @Override
- public Class getType() {
- return float.class;
- }
-
- @Override
- public @NonNull List> getTypes() {
- return TYPES;
- }
-
- @Override
- public Float read(String string) throws ArgsReaderException {
- try {
- return Float.parseFloat(string);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender> sender, @NonNull String current) {
- return SUGGESTIONS;
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/IntegerArrayReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/IntegerArrayReader.java
deleted file mode 100644
index 43f4e59e..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/IntegerArrayReader.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-public class IntegerArrayReader extends ArgsReader {
-
- @Override
- public Class getType() {
- return int[].class;
- }
-
- @Override
- public int[] read(String string) throws ArgsReaderException {
- try {
- final String[] strings = string.split(" ");
-
- int[] result = new int[strings.length];
- for (int i = 0; i < result.length; i++)
- result[i] = Integer.parseInt(strings[i]);
- return result;
- } catch (NumberFormatException e) {
- throw new ArgsReaderException("expected a int array in string form but got " + string);
- }
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/IntegerReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/IntegerReader.java
deleted file mode 100644
index 5123f358..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/IntegerReader.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-import lombok.NonNull;
-
-import java.util.List;
-
-public class IntegerReader extends ArgsReader {
-
- public static final List SUGGESTIONS = List.of("1", "10", "100");
- public static final @NonNull List> TYPES = List.of(int.class, Integer.class);
-
- @Override
- public Class getType() {
- return int.class;
- }
-
- @Override
- public @NonNull List> getTypes() {
- return TYPES;
- }
-
- @Override
- public Integer read(String string) throws ArgsReaderException {
- try {
- return Integer.parseInt(string);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender> sender, @NonNull String current) {
- return SUGGESTIONS;
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ShortArrayReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ShortArrayReader.java
deleted file mode 100644
index 62315102..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ShortArrayReader.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-public class ShortArrayReader extends ArgsReader {
-
- @Override
- public Class getType() {
- return short[].class;
- }
-
- @Override
- public short[] read(String string) throws ArgsReaderException {
- try {
- final String[] strings = string.split(" ");
-
- short[] result = new short[strings.length];
- for (int i = 0; i < result.length; i++)
- result[i] = Short.parseShort(strings[i]);
- return result;
- } catch (NumberFormatException e) {
- throw new ArgsReaderException("expected a short array in string form but got " + string);
- }
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ShortReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ShortReader.java
deleted file mode 100644
index 14e84005..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/ShortReader.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-import lombok.NonNull;
-
-import java.util.List;
-
-public class ShortReader extends ArgsReader {
-
- public static final List SUGGESTIONS = List.of("-32768", "0", "32767");
- public static final @NonNull List> TYPES = List.of(short.class, Short.class);
-
- @Override
- public Class getType() {
- return short.class;
- }
-
- @Override
- public @NonNull List> getTypes() {
- return TYPES;
- }
-
- @Override
- public Short read(String string) throws ArgsReaderException {
- try {
- return Short.parseShort(string);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender> sender, @NonNull String current) {
- return SUGGESTIONS;
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/StringReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/StringReader.java
deleted file mode 100644
index 93f2c9c4..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/StringReader.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-import lombok.NonNull;
-
-import java.util.List;
-
-public class StringReader extends ArgsReader {
-
- public static final List SUGGESTIONS = List.of("dummy", "text");
-
- @Override
- public Class getType() {
- return String.class;
- }
-
- @Override
- public String read(String string) {
- return string;
- }
-
- @Override
- public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender> sender, @NonNull String current) {
- return SUGGESTIONS;
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/UUIDReader.java b/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/UUIDReader.java
deleted file mode 100644
index d9f4c7db..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/args/reader/UUIDReader.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.wizardlybump17.wlib.command.args.reader;
-
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-import lombok.NonNull;
-
-import java.util.List;
-import java.util.UUID;
-
-public class UUIDReader extends ArgsReader {
-
- public static final List SUGGESTIONS = List.of("00000000-0000-0000-0000-000000000000");
-
- @Override
- public Class getType() {
- return UUID.class;
- }
-
- @Override
- public UUID read(String string) throws ArgsReaderException {
- try {
- return UUID.fromString(string);
- } catch (IllegalArgumentException e) {
- return null;
- }
- }
-
- @Override
- public @NonNull List<@NonNull String> autoComplete(@NonNull CommandSender> sender, @NonNull String current) {
- return SUGGESTIONS;
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/completer/ArgumentCompleter.java b/commands/src/main/java/com/wizardlybump17/wlib/command/completer/ArgumentCompleter.java
deleted file mode 100644
index f0145c0d..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/completer/ArgumentCompleter.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.wizardlybump17.wlib.command.completer;
-
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-import lombok.NonNull;
-
-import java.util.List;
-
-public interface ArgumentCompleter {
-
- @NonNull
- List complete(@NonNull CommandSender> sender, @NonNull String @NonNull [] args);
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/completer/ArgumentCompleterRegistry.java b/commands/src/main/java/com/wizardlybump17/wlib/command/completer/ArgumentCompleterRegistry.java
deleted file mode 100644
index 3758074a..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/completer/ArgumentCompleterRegistry.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.wizardlybump17.wlib.command.completer;
-
-import com.wizardlybump17.wlib.object.Registry;
-
-public class ArgumentCompleterRegistry extends Registry, ArgumentCompleter> {
-
- public static final ArgumentCompleterRegistry INSTANCE = new ArgumentCompleterRegistry();
-
- private ArgumentCompleterRegistry() {
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/context/CommandContext.java b/commands/src/main/java/com/wizardlybump17/wlib/command/context/CommandContext.java
new file mode 100644
index 00000000..82a63e15
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/context/CommandContext.java
@@ -0,0 +1,66 @@
+package com.wizardlybump17.wlib.command.context;
+
+import com.wizardlybump17.wlib.command.Command;
+import com.wizardlybump17.wlib.command.node.CommandNode;
+import com.wizardlybump17.wlib.command.sender.CommandSender;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.Unmodifiable;
+
+import java.util.*;
+
+public record CommandContext(@NotNull Command command, @NotNull CommandSender> sender, @NotNull CommandNodeArguments arguments, int lastInputIndex, @NotNull CommandNode> lastNode) {
+
+ public record CommandNodeArgument(@NotNull CommandNode node, @Nullable String input, @Nullable T data) {
+ }
+
+ public static final class CommandNodeArguments {
+
+ private final @NotNull @Unmodifiable Map> arguments;
+
+ public CommandNodeArguments(@NotNull List> arguments) {
+ Map> argumentsMap = new LinkedHashMap<>();
+ for (CommandNodeArgument> argument : arguments)
+ argumentsMap.put(argument.node().getName(), argument);
+ this.arguments = Collections.unmodifiableMap(argumentsMap);
+ }
+
+ public boolean hasArgument(@NotNull String key) {
+ return arguments.containsKey(key);
+ }
+
+ @SuppressWarnings("unchecked")
+ public @NotNull Optional> getArgument(@NotNull String key) {
+ return Optional.ofNullable((CommandNodeArgument) arguments.get(key));
+ }
+
+ @SuppressWarnings("unchecked")
+ public @NotNull Optional getArgumentData(@NotNull String key) {
+ return (Optional) getArgument(key).map(CommandNodeArgument::data);
+ }
+
+ public @NotNull @Unmodifiable Map> getArguments() {
+ return arguments;
+ }
+
+ @Override
+ public String toString() {
+ return "CommandNodeArguments{" +
+ "arguments=" + arguments +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass())
+ return false;
+ CommandNodeArguments arguments1 = (CommandNodeArguments) o;
+ return Objects.equals(arguments, arguments1.arguments);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(arguments);
+ }
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/data/AnnotationCommandData.java b/commands/src/main/java/com/wizardlybump17/wlib/command/data/AnnotationCommandData.java
deleted file mode 100644
index e45d26f0..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/data/AnnotationCommandData.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package com.wizardlybump17.wlib.command.data;
-
-import com.wizardlybump17.wlib.command.annotation.Command;
-import com.wizardlybump17.wlib.util.ReflectionUtil;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-public class AnnotationCommandData extends CommandData {
-
- private final @NotNull Command annotation;
- private final @NotNull Object object;
- private final @NotNull Map fieldCache = new HashMap<>();
-
- public AnnotationCommandData(@NotNull Command annotation, @NotNull Object object) {
- this.annotation = annotation;
- this.object = object;
- }
-
- @Override
- public @NotNull String getExecution() {
- return annotation.execution();
- }
-
- @Override
- public @Nullable String getPermission() {
- String permission = annotation.permission();
- return permission.isBlank() ? super.getPermission() : permission;
- }
-
- @Override
- public @Nullable String getPermissionMessage() {
- return getMessage(annotation.permissionMessage(), annotation.permissionMessageIsField(), super.getPermissionMessage());
- }
-
- @Override
- public int getPriority() {
- int priority = annotation.priority();
- return priority == -1 ? super.getPriority() : priority;
- }
-
- @Override
- public @Nullable String getDescription() {
- String description = annotation.description();
- return description.isBlank() ? super.getDescription() : description;
- }
-
- @Override
- public @Nullable String getInvalidSenderMessage() {
- return getMessage(annotation.invalidSenderMessage(), annotation.invalidSenderMessageIsField(), super.getInvalidSenderMessage());
- }
-
- protected @Nullable String getMessage(@NotNull String message, boolean isField, @Nullable String defaultMessage) {
- if (isField) {
- return ReflectionUtil.getFieldValue(
- fieldCache.computeIfAbsent(
- message,
- $ -> ReflectionUtil.getField(message, object.getClass())
- ),
- object
- );
- }
- return message.isBlank() ? defaultMessage : message;
- }
-
- public @NotNull Command getAnnotation() {
- return annotation;
- }
-
- @Override
- public @NotNull Class> getSenderType() {
- return annotation.senderType();
- }
-
- @Override
- public boolean equals(Object object1) {
- if (object1 == null || getClass() != object1.getClass())
- return false;
- AnnotationCommandData that = (AnnotationCommandData) object1;
- return Objects.equals(annotation, that.annotation) && Objects.equals(object, that.object) && Objects.equals(fieldCache, that.fieldCache);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(annotation, object, fieldCache);
- }
-
- @Override
- public String toString() {
- return "AnnotationCommandData{" +
- "annotation=" + annotation +
- ", object=" + object +
- ", fieldCache=" + fieldCache +
- '}';
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/data/CommandData.java b/commands/src/main/java/com/wizardlybump17/wlib/command/data/CommandData.java
deleted file mode 100644
index 9dd74ef5..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/data/CommandData.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.wizardlybump17.wlib.command.data;
-
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public abstract class CommandData {
-
- /**
- * How the sender must type the command in order for it to be triggered.
- * Example:
command hello world
- * In the example above, the sender must type /command hello world, so it can be triggered.
- * To add parameters that depends on the sender input, just put the parameter between <>.
- *
- * Example: command <hello> world
- * The type of each parameter is defined in the method that have this annotation.
- * An example for the command above:
- *
- * @Command(execution = "command <hello> world")
- * public void commandHelloWorld(GenericSender sender, String hello) {
- * System.out.println(sender.getName() + " executed the hello world command with the argument " + hello);
- * }
- *
- *
- * @return how the command must be sent to be triggered
- * @see com.wizardlybump17.wlib.command.args.reader.ArgsReader
- */
- public abstract @NotNull String getExecution();
-
- /**
- * @return which permission the sender must have to trigger this command
- */
- public @Nullable String getPermission() {
- return null;
- }
-
- /**
- * Used when the {@link CommandSender} does not have the required {@link #getPermission()}.
- * @return the message to be sent when the {@link CommandSender} does not have the required {@link #getPermission()}
- */
- public @Nullable String getPermissionMessage() {
- return null;
- }
-
- /**
- * @return the priority of this command
- */
- public int getPriority() {
- return getExecution().split(" ").length;
- }
-
- /**
- * @return the description of this command
- */
- public @Nullable String getDescription() {
- return null;
- }
-
- /**
- * Used when the {@link CommandSender} is not valid for this command.
- * @return the message to be sent when the {@link CommandSender} is not valid for this command
- */
- public @Nullable String getInvalidSenderMessage() {
- return null;
- }
-
- public final @NotNull String getName() {
- return getExecution().split(" ")[0];
- }
-
- /**
- * @return the {@link CommandSender#getHandle()} that can execute this command
- */
- public @NotNull Class> getSenderType() {
- return Object.class;
- }
-
- public static @NotNull SimpleCommandData.Builder builder() {
- return new SimpleCommandData.Builder();
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/data/SimpleCommandData.java b/commands/src/main/java/com/wizardlybump17/wlib/command/data/SimpleCommandData.java
deleted file mode 100644
index 66905c05..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/data/SimpleCommandData.java
+++ /dev/null
@@ -1,216 +0,0 @@
-package com.wizardlybump17.wlib.command.data;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.function.Supplier;
-
-public class SimpleCommandData extends CommandData {
-
- private final @NotNull Supplier execution;
- private final @NotNull Supplier permission;
- private final @NotNull Supplier permissionMessage;
- private final @NotNull Supplier priority;
- private final @NotNull Supplier description;
- private final @NotNull Supplier invalidSenderMessage;
- private final @NotNull Supplier> senderType;
-
- public SimpleCommandData(@NotNull Supplier execution, @NotNull Supplier permission, @NotNull Supplier permissionMessage, @NotNull Supplier priority, @NotNull Supplier description, @NotNull Supplier invalidSenderMessage, @NotNull Supplier> senderType) {
- this.execution = execution;
- this.permission = permission;
- this.permissionMessage = permissionMessage;
- this.priority = priority;
- this.description = description;
- this.invalidSenderMessage = invalidSenderMessage;
- this.senderType = senderType;
- }
-
- public SimpleCommandData(@NotNull String execution, @Nullable String permission, @Nullable String permissionMessage, int priority, @Nullable String description, @Nullable String invalidSenderMessage, @NotNull Class> senderType) {
- this(
- () -> execution,
- () -> permission,
- () -> permissionMessage,
- () -> priority,
- () -> description,
- () -> invalidSenderMessage,
- () -> senderType
- );
- }
-
- @Override
- public @NotNull String getExecution() {
- return execution.get();
- }
-
- @Override
- public @Nullable String getPermission() {
- return permission.get();
- }
-
- @Override
- public @Nullable String getPermissionMessage() {
- return permissionMessage.get();
- }
-
- @Override
- public int getPriority() {
- return priority.get();
- }
-
- @Override
- public @Nullable String getDescription() {
- return description.get();
- }
-
- @Override
- public @Nullable String getInvalidSenderMessage() {
- return invalidSenderMessage.get();
- }
-
- @Override
- public @NotNull Class> getSenderType() {
- return senderType.get();
- }
-
- public static class Builder {
-
- Builder() {
- }
-
- private @Nullable Supplier execution;
- private @Nullable Supplier permission;
- private @Nullable Supplier permissionMessage;
- private @Nullable Supplier priority;
- private @Nullable Supplier description;
- private @Nullable Supplier invalidSenderMessage;
- private @Nullable Supplier> senderType;
-
- public @Nullable String execution() {
- return execution == null ? null : execution.get();
- }
-
- public @NotNull Builder execution(@Nullable String execution) {
- this.execution = () -> execution;
- return this;
- }
-
- public @NotNull Builder execution(@Nullable Supplier execution) {
- this.execution = execution;
- return this;
- }
-
- public @Nullable String permission() {
- return permission == null ? null : permission.get();
- }
-
- public @NotNull Builder permission(@Nullable String permission) {
- this.permission = () -> permission;
- return this;
- }
-
- public @NotNull Builder permission(@Nullable Supplier permission) {
- this.permission = permission;
- return this;
- }
-
- public @Nullable String permissionMessage() {
- return permissionMessage == null ? null : permissionMessage.get();
- }
-
- public @NotNull Builder permissionMessage(@Nullable String permissionMessage) {
- this.permissionMessage = () -> permissionMessage;
- return this;
- }
-
- public @NotNull Builder permissionMessage(@Nullable Supplier permissionMessage) {
- this.permissionMessage = permissionMessage;
- return this;
- }
-
- public @Nullable Integer priority() {
- return priority == null ? null : priority.get();
- }
-
- public @NotNull Builder priority(@Nullable Integer priority) {
- this.priority = () -> priority;
- return this;
- }
-
- public @NotNull Builder priority(@Nullable Supplier priority) {
- this.priority = priority;
- return this;
- }
-
- public @Nullable String description() {
- return description == null ? null : description.get();
- }
-
- public @NotNull Builder description(@Nullable String description) {
- this.description = () -> description;
- return this;
- }
-
- public @NotNull Builder description(@Nullable Supplier description) {
- this.description = description;
- return this;
- }
-
- public @Nullable String invalidSenderMessage() {
- return invalidSenderMessage == null ? null : invalidSenderMessage.get();
- }
-
- public @NotNull Builder invalidSenderMessage(@Nullable String invalidSenderMessage) {
- this.invalidSenderMessage = () -> invalidSenderMessage;
- return this;
- }
-
- public @NotNull Builder invalidSenderMessage(@Nullable Supplier invalidSenderMessage) {
- this.invalidSenderMessage = invalidSenderMessage;
- return this;
- }
-
- public @Nullable Class> senderType() {
- return senderType == null ? null : senderType.get();
- }
-
- public @NotNull Builder senderType(@Nullable Class> senderType) {
- this.senderType = () -> senderType;
- return this;
- }
-
- public @NotNull Builder senderType(@Nullable Supplier> senderType) {
- this.senderType = senderType;
- return this;
- }
-
- public @NotNull CommandData build() {
- String execution = execution();
- if (execution == null)
- throw new NullPointerException("The execution can not be null");
-
- String permission = permission();
- String permissionMessage = permissionMessage();
-
- Integer priority = priority();
- if (priority == null)
- priority = execution.split(" ").length;
-
- String description = description();
- String invalidSenderMessage = invalidSenderMessage();
-
- Class> senderType = senderType();
- if (senderType == null)
- senderType = Object.class;
-
- return new SimpleCommandData(
- execution,
- permission,
- permissionMessage,
- priority,
- description,
- invalidSenderMessage,
- senderType
- );
- }
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/exception/CommandException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/CommandException.java
deleted file mode 100644
index 7c99b73f..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/exception/CommandException.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.wizardlybump17.wlib.command.exception;
-
-import org.jetbrains.annotations.NotNull;
-
-public class CommandException extends Exception {
-
- public CommandException() {
- }
-
- public CommandException(@NotNull String message) {
- super(message);
- }
-
- public CommandException(@NotNull String message, @NotNull Throwable cause) {
- super(message, cause);
- }
-
- public CommandException(@NotNull Throwable cause) {
- super(cause);
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/exception/CommandExecutionException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/CommandExecutionException.java
new file mode 100644
index 00000000..9438ec26
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/CommandExecutionException.java
@@ -0,0 +1,23 @@
+package com.wizardlybump17.wlib.command.exception;
+
+import org.jetbrains.annotations.NotNull;
+
+public class CommandExecutionException extends RuntimeException {
+
+ public static final @NotNull String MESSAGE = "Error while executing the command %s, index %s, node %s";
+
+ public CommandExecutionException() {
+ }
+
+ public CommandExecutionException(@NotNull String message) {
+ super(message);
+ }
+
+ public CommandExecutionException(@NotNull String message, @NotNull Throwable cause) {
+ super(message, cause);
+ }
+
+ public CommandExecutionException(@NotNull Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/exception/InputParsingException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/InputParsingException.java
new file mode 100644
index 00000000..9f4c4315
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/InputParsingException.java
@@ -0,0 +1,14 @@
+package com.wizardlybump17.wlib.command.exception;
+
+import org.jetbrains.annotations.NotNull;
+
+public class InputParsingException extends Exception {
+
+ public InputParsingException(@NotNull String message) {
+ super(message);
+ }
+
+ public InputParsingException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/exception/InvalidInputException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/InvalidInputException.java
new file mode 100644
index 00000000..410fa29e
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/InvalidInputException.java
@@ -0,0 +1,14 @@
+package com.wizardlybump17.wlib.command.exception;
+
+import org.jetbrains.annotations.NotNull;
+
+public class InvalidInputException extends Exception {
+
+ public InvalidInputException(@NotNull String message) {
+ super(message);
+ }
+
+ public InvalidInputException(@NotNull String message, @NotNull Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/exception/SuggesterException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/SuggesterException.java
new file mode 100644
index 00000000..92c90364
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/SuggesterException.java
@@ -0,0 +1,21 @@
+package com.wizardlybump17.wlib.command.exception;
+
+import org.jetbrains.annotations.NotNull;
+
+public class SuggesterException extends Exception {
+
+ public SuggesterException() {
+ }
+
+ public SuggesterException(@NotNull String message) {
+ super(message);
+ }
+
+ public SuggesterException(@NotNull String message, @NotNull Throwable cause) {
+ super(message, cause);
+ }
+
+ public SuggesterException(@NotNull Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/exception/extractor/CommandExtractorException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/extractor/CommandExtractorException.java
new file mode 100644
index 00000000..fb6767aa
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/extractor/CommandExtractorException.java
@@ -0,0 +1,22 @@
+package com.wizardlybump17.wlib.command.exception.extractor;
+
+import org.jetbrains.annotations.NotNull;
+
+public abstract class CommandExtractorException extends Exception {
+
+ public CommandExtractorException() {
+ super();
+ }
+
+ public CommandExtractorException(@NotNull String message) {
+ super(message);
+ }
+
+ public CommandExtractorException(@NotNull String message, @NotNull Throwable cause) {
+ super(message, cause);
+ }
+
+ public CommandExtractorException(@NotNull Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/exception/extractor/method/MethodCommandNodeFactoryNotFoundException.java b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/extractor/method/MethodCommandNodeFactoryNotFoundException.java
new file mode 100644
index 00000000..2a3f5195
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/exception/extractor/method/MethodCommandNodeFactoryNotFoundException.java
@@ -0,0 +1,15 @@
+package com.wizardlybump17.wlib.command.exception.extractor.method;
+
+import com.wizardlybump17.wlib.command.exception.extractor.CommandExtractorException;
+import org.jetbrains.annotations.NotNull;
+
+public class MethodCommandNodeFactoryNotFoundException extends CommandExtractorException {
+
+ public MethodCommandNodeFactoryNotFoundException() {
+ super();
+ }
+
+ public MethodCommandNodeFactoryNotFoundException(@NotNull String message) {
+ super(message);
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/executor/CommandExecutor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/executor/CommandExecutor.java
deleted file mode 100644
index a0c3720a..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/executor/CommandExecutor.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.wizardlybump17.wlib.command.executor;
-
-import com.wizardlybump17.wlib.command.exception.CommandException;
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Map;
-
-public interface CommandExecutor {
-
- void execute(@NotNull CommandSender> sender, @NotNull Map args) throws CommandException;
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/executor/CommandNodeExecutor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/executor/CommandNodeExecutor.java
new file mode 100644
index 00000000..e4bb8599
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/executor/CommandNodeExecutor.java
@@ -0,0 +1,11 @@
+package com.wizardlybump17.wlib.command.executor;
+
+import com.wizardlybump17.wlib.command.context.CommandContext;
+import com.wizardlybump17.wlib.command.result.CommandResult;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public interface CommandNodeExecutor {
+
+ @Nullable CommandResult execute(@NotNull CommandContext context) throws Throwable;
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/CommandExtractor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/CommandExtractor.java
index 8f1d4373..d6dc28e4 100644
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/CommandExtractor.java
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/CommandExtractor.java
@@ -1,13 +1,20 @@
package com.wizardlybump17.wlib.command.extractor;
-import com.wizardlybump17.wlib.command.CommandManager;
-import com.wizardlybump17.wlib.command.holder.CommandHolder;
-import com.wizardlybump17.wlib.command.registered.RegisteredCommand;
+import com.wizardlybump17.wlib.command.Command;
+import com.wizardlybump17.wlib.command.exception.extractor.CommandExtractorException;
+import com.wizardlybump17.wlib.command.extractor.method.MethodCommandExtractor;
+import com.wizardlybump17.wlib.command.registry.MethodCommandNodeFactoryRegistry;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public interface CommandExtractor {
- @NotNull List extract(@NotNull CommandManager manager, @NotNull CommandHolder> holder, @NotNull Object object);
+ boolean isAccepted(@NotNull Object object);
+
+ @NotNull List extract(@NotNull Object object) throws CommandExtractorException;
+
+ static @NotNull MethodCommandExtractor method(@NotNull MethodCommandNodeFactoryRegistry factoryRegistry) {
+ return new MethodCommandExtractor(factoryRegistry);
+ }
}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/DirectCommandExtractor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/DirectCommandExtractor.java
deleted file mode 100644
index bb21f6c5..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/DirectCommandExtractor.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.wizardlybump17.wlib.command.extractor;
-
-import com.wizardlybump17.wlib.command.CommandManager;
-import com.wizardlybump17.wlib.command.holder.CommandHolder;
-import com.wizardlybump17.wlib.command.registered.RegisteredCommand;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.List;
-
-public class DirectCommandExtractor implements CommandExtractor {
-
- @Override
- public @NotNull List extract(@NotNull CommandManager manager, @NotNull CommandHolder> holder, @NotNull Object object) {
- return object instanceof RegisteredCommand command ? List.of(command) : List.of();
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/MethodCommandExtractor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/MethodCommandExtractor.java
deleted file mode 100644
index 354da31d..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/MethodCommandExtractor.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.wizardlybump17.wlib.command.extractor;
-
-import com.wizardlybump17.wlib.command.CommandManager;
-import com.wizardlybump17.wlib.command.annotation.Command;
-import com.wizardlybump17.wlib.command.holder.CommandHolder;
-import com.wizardlybump17.wlib.command.registered.RegisteredCommand;
-import com.wizardlybump17.wlib.command.registered.RegisteredMethodCommand;
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-import org.jetbrains.annotations.NotNull;
-
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Level;
-
-public class MethodCommandExtractor implements CommandExtractor {
-
- @Override
- public @NotNull List extract(@NotNull CommandManager manager, @NotNull CommandHolder> holder, @NotNull Object object) {
- List commands = new ArrayList<>();
- for (Method method : object.getClass().getDeclaredMethods()) {
- if (!method.isAnnotationPresent(Command.class) || method.getParameterCount() == 0 || !CommandSender.class.isAssignableFrom(method.getParameterTypes()[0]))
- continue;
-
- RegisteredMethodCommand command;
- try {
- command = new RegisteredMethodCommand(
- method.getAnnotation(Command.class),
- object,
- method
- );
- } catch (Exception e) {
- holder.getLogger().log(Level.SEVERE, "Error while creating a command", e);
- continue;
- }
-
- commands.add(command);
- com.wizardlybump17.wlib.command.holder.Command holderCommand = holder.getCommand(command.getName());
- if (holderCommand != null)
- holderCommand.setExecutor(holderCommand.getDefaultExecutor(manager, command.getName()));
- }
- return commands;
- }
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/MethodCommandExtractor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/MethodCommandExtractor.java
new file mode 100644
index 00000000..0b599fae
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/MethodCommandExtractor.java
@@ -0,0 +1,184 @@
+package com.wizardlybump17.wlib.command.extractor.method;
+
+import com.wizardlybump17.wlib.command.Command;
+import com.wizardlybump17.wlib.command.context.CommandContext;
+import com.wizardlybump17.wlib.command.exception.extractor.method.MethodCommandNodeFactoryNotFoundException;
+import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor;
+import com.wizardlybump17.wlib.command.extractor.CommandExtractor;
+import com.wizardlybump17.wlib.command.extractor.method.executor.AbstractMethodCommandNodeExecutor;
+import com.wizardlybump17.wlib.command.extractor.method.factory.MethodCommandNodeFactory;
+import com.wizardlybump17.wlib.command.node.CommandNode;
+import com.wizardlybump17.wlib.command.node.LiteralCommandNode;
+import com.wizardlybump17.wlib.command.registry.MethodCommandNodeFactoryRegistry;
+import com.wizardlybump17.wlib.command.result.CommandResult;
+import com.wizardlybump17.wlib.command.sender.CommandSender;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MethodCommandExtractor implements CommandExtractor {
+
+ private final @NotNull MethodCommandNodeFactoryRegistry factoryRegistry;
+
+ public MethodCommandExtractor(@NotNull MethodCommandNodeFactoryRegistry factoryRegistry) {
+ this.factoryRegistry = factoryRegistry;
+ }
+
+ @Override
+ public boolean isAccepted(@NotNull Object object) {
+ return true;
+ }
+
+ public @NotNull MethodCommandNodeFactoryRegistry getFactoryRegistry() {
+ return factoryRegistry;
+ }
+
+ @Override
+ public @NotNull List extract(@NotNull Object object) throws MethodCommandNodeFactoryNotFoundException {
+ List commands = new ArrayList<>();
+
+ Class> clazz = object.getClass();
+
+ for (Method method : clazz.getMethods()) {
+ com.wizardlybump17.wlib.command.annotation.Command annotation = method.getAnnotation(com.wizardlybump17.wlib.command.annotation.Command.class);
+ if (annotation == null)
+ continue;
+
+ Parameter[] parameters = method.getParameters();
+ Class> returnType = method.getReturnType();
+
+ String[] commandParts = annotation.value().split(" ");
+
+ CommandNode> root = null;
+
+ int parameterIndex = parameters.length - 1;
+ for (int i = commandParts.length - 1; i >= 0; i--) {
+ String part = commandParts[i];
+
+ CommandNode> oldRoot = root;
+ root = createNode(factoryRegistry, part, parameters, parameterIndex, root, annotation, object, method);
+ if (!(root instanceof LiteralCommandNode))
+ parameterIndex--;
+
+ if (oldRoot == null)
+ root = root.withExecutor(createExecutor(object, parameters, returnType, method));
+ }
+
+ if (root == null)
+ throw new IllegalArgumentException();
+
+ commands.add(new Command((LiteralCommandNode) root));
+ }
+
+ return commands;
+ }
+
+ private static @NotNull CommandNode> createNode(@NotNull MethodCommandNodeFactoryRegistry factoryRegistry, @NotNull String part, @NotNull Parameter @NotNull [] parameters, int parameterIndex, @Nullable CommandNode> root, @NotNull com.wizardlybump17.wlib.command.annotation.Command annotation, @NotNull Object object, @NotNull Method method) throws MethodCommandNodeFactoryNotFoundException {
+ CommandNode> newNode;
+
+ boolean argument = part.charAt(0) == '<' && part.charAt(part.length() - 1) == '>';
+ if (argument)
+ part = part.substring(1, part.length() - 1);
+
+ if (parameterIndex < 0) {
+ newNode = new LiteralCommandNode(part, root == null ? List.of() : List.of(root), null, null);
+ } else {
+ if (argument) {
+ Parameter parameter = parameters[parameterIndex];
+ Class> parameterType = parameter.getType();
+
+ MethodCommandNodeFactory factory = factoryRegistry.getFactory(parameterType);
+ if (factory == null)
+ throw new MethodCommandNodeFactoryNotFoundException("MethodCommandNodeFactory not found for the parameter " + parameter);
+
+ newNode = factory.create(object, method, annotation, parameter, part, root);
+ } else {
+ newNode = new LiteralCommandNode(part, root == null ? List.of() : List.of(root), null, null);
+ }
+ }
+
+ if (!annotation.permission().isEmpty())
+ newNode = newNode.withPermission(annotation.permission());
+
+ return newNode;
+ }
+
+ public static @NotNull CommandNodeExecutor> createExecutor(@NotNull Object object, @NotNull String methodName, @NotNull Class> @NotNull ... parameterTypes) {
+ try {
+ Method method = object.getClass().getMethod(methodName, parameterTypes);
+ return createExecutor(object, method.getParameters(), method.getReturnType(), method);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+
+ /**
+ *
+ * If empty -> check the return type;
+ * If CommandSender only -> pass only the command sender and check the return type;
+ * If CommandContext only -> pass only the command context and check the return type;
+ * If CommandSender + more parameters -> pass the command sender and the parameters and check the return type;
+ * If CommandContext + more parameters -> pass the command context and the parameters and check the return type;
+ * If only parameters -> pass the parameters and check the return type.
+ *
+ *
+ * @param object the object containing the method
+ * @param parameters the parameters of the method
+ * @param returnType the return type of the method
+ * @param method the {@link Method}
+ * @return the appropriate CommandNodeExecutor based on the parameters and return type of the method
+ */
+ private static @NotNull CommandNodeExecutor> createExecutor(@NotNull Object object, @NotNull Parameter @NotNull [] parameters, @NotNull Class> returnType, @NotNull Method method) {
+ if (parameters.length == 0) { //no parameters
+ if (CommandResult.class.isAssignableFrom(returnType)) { //CommandResult return type
+ return new AbstractMethodCommandNodeExecutor.NoArgumentsCommandResultExecutor<>(object, method);
+ } else {
+ return new AbstractMethodCommandNodeExecutor.NoArgumentsExecutor<>(object, method);
+ }
+ }
+
+ Parameter firstParameter = parameters[0];
+ Class> firstParameterType = firstParameter.getType();
+
+ if (parameters.length == 1) {
+ if (CommandSender.class.isAssignableFrom(firstParameterType)) { //CommandSender only
+ if (CommandResult.class.isAssignableFrom(returnType)) { //CommandResult return type
+ return new AbstractMethodCommandNodeExecutor.CommandSenderCommandResultExecutor<>(object, method);
+ } else { //anything else return type
+ return new AbstractMethodCommandNodeExecutor.CommandSenderExecutor<>(object, method);
+ }
+ } else if (CommandContext.class.isAssignableFrom(firstParameterType)) { //CommandContext only
+ if (CommandResult.class.isAssignableFrom(returnType)) { //CommandResult return type
+ return new AbstractMethodCommandNodeExecutor.CommandContextCommandResultExecutor<>(object, method);
+ } else { //anything else return type
+ return new AbstractMethodCommandNodeExecutor.CommandContextExecutor<>(object, method);
+ }
+ }
+ }
+
+ if (CommandSender.class.isAssignableFrom(firstParameterType)) { //CommandSender + more arguments
+ if (CommandResult.class.isAssignableFrom(returnType)) { //CommandResult return type
+ return new AbstractMethodCommandNodeExecutor.CommandSenderAndArgumentsCommandResultExecutor<>(object, method);
+ } else { //anything else return type
+ return new AbstractMethodCommandNodeExecutor.CommandSenderAndArgumentsExecutor<>(object, method);
+ }
+ } else if (CommandContext.class.isAssignableFrom(firstParameterType)) { //CommandContext + more arguments
+ if (CommandResult.class.isAssignableFrom(returnType)) { //CommandResult return type
+ return new AbstractMethodCommandNodeExecutor.CommandContextAndArgumentsCommandResultExecutor<>(object, method);
+ } else { //anything else return type
+ return new AbstractMethodCommandNodeExecutor.CommandContextAndArgumentsExecutor<>(object, method);
+ }
+ } else {
+ if (CommandResult.class.isAssignableFrom(returnType)) { //CommandResult return type
+ return new AbstractMethodCommandNodeExecutor.ArgumentsCommandResultExecutor<>(object, method);
+ } else { //anything else return type
+ return new AbstractMethodCommandNodeExecutor.ArgumentsExecutor<>(object, method);
+ }
+ }
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/executor/AbstractMethodCommandNodeExecutor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/executor/AbstractMethodCommandNodeExecutor.java
new file mode 100644
index 00000000..221b0bbe
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/executor/AbstractMethodCommandNodeExecutor.java
@@ -0,0 +1,295 @@
+package com.wizardlybump17.wlib.command.extractor.method.executor;
+
+import com.wizardlybump17.wlib.command.context.CommandContext;
+import com.wizardlybump17.wlib.command.node.LiteralCommandNode;
+import com.wizardlybump17.wlib.command.result.CommandResult;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@ApiStatus.Internal
+public abstract sealed class AbstractMethodCommandNodeExecutor implements MethodCommandNodeExecutor {
+
+ private final @NotNull Object object;
+ private final @NotNull MethodHandle methodHandle;
+ private final @NotNull Method method;
+
+ public AbstractMethodCommandNodeExecutor(@NotNull Object object, @NotNull Method method) {
+ this.object = object;
+ try {
+ this.methodHandle = MethodHandles.publicLookup().findVirtual(object.getClass(), method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ throw new IllegalArgumentException(e);
+ }
+ this.method = method;
+ }
+
+ @Override
+ public @NotNull Object object() {
+ return object;
+ }
+
+ @Override
+ public @NotNull MethodHandle methodHandle() {
+ return methodHandle;
+ }
+
+ @Override
+ public @NotNull Method method() {
+ return method;
+ }
+
+ @Override
+ public boolean equals(Object object1) {
+ if (object1 == null || getClass() != object1.getClass())
+ return false;
+ AbstractMethodCommandNodeExecutor> that = (AbstractMethodCommandNodeExecutor>) object1;
+ return Objects.equals(object, that.object)
+ && Objects.equals(method, that.method);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(object, method);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{" +
+ "object=" + object +
+ ", methodHandle=" + methodHandle +
+ ", method=" + method +
+ '}';
+ }
+
+ @ApiStatus.Internal
+ public static final class CommandSenderCommandResultExecutor extends AbstractMethodCommandNodeExecutor {
+
+ public CommandSenderCommandResultExecutor(@NotNull Object object, @NotNull Method method) {
+ super(object, method);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable {
+ List parameters = new ArrayList<>();
+ parameters.add(object());
+ parameters.add(context.sender());
+ return (CommandResult) methodHandle().invokeWithArguments(parameters);
+ }
+ }
+
+ @ApiStatus.Internal
+ public static final class CommandSenderExecutor extends AbstractMethodCommandNodeExecutor {
+
+ public CommandSenderExecutor(@NotNull Object object, @NotNull Method method) {
+ super(object, method);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable{
+ List parameters = new ArrayList<>();
+ parameters.add(object());
+ parameters.add(context.sender());
+ return (CommandResult) CommandResult.successful(methodHandle().invokeWithArguments(parameters));
+ }
+ }
+
+ @ApiStatus.Internal
+ public static final class CommandContextCommandResultExecutor extends AbstractMethodCommandNodeExecutor {
+
+ public CommandContextCommandResultExecutor(@NotNull Object object, @NotNull Method method) {
+ super(object, method);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable {
+ List parameters = new ArrayList<>();
+ parameters.add(object());
+ parameters.add(context);
+ return (CommandResult) methodHandle().invokeWithArguments(parameters);
+ }
+ }
+
+ @ApiStatus.Internal
+ public static final class CommandContextExecutor extends AbstractMethodCommandNodeExecutor {
+
+ public CommandContextExecutor(@NotNull Object object, @NotNull Method method) {
+ super(object, method);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable {
+ List parameters = new ArrayList<>();
+ parameters.add(object());
+ parameters.add(context);
+ return (CommandResult) CommandResult.successful(methodHandle().invokeWithArguments(parameters));
+ }
+ }
+
+ @ApiStatus.Internal
+ public static final class CommandSenderAndArgumentsCommandResultExecutor extends AbstractMethodCommandNodeExecutor {
+
+ public CommandSenderAndArgumentsCommandResultExecutor(@NotNull Object object, @NotNull Method method) {
+ super(object, method);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable {
+ List parameters = new ArrayList<>();
+ parameters.add(object());
+ parameters.add(context.sender());
+ context.arguments().getArguments().forEach((nodeName, argument) -> {
+ if (!(argument.node() instanceof LiteralCommandNode))
+ parameters.add(argument.data());
+ });
+ return (CommandResult) methodHandle().invokeWithArguments(parameters);
+ }
+ }
+
+ @ApiStatus.Internal
+ public static final class CommandSenderAndArgumentsExecutor extends AbstractMethodCommandNodeExecutor {
+
+ public CommandSenderAndArgumentsExecutor(@NotNull Object object, @NotNull Method method) {
+ super(object, method);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable {
+ List parameters = new ArrayList<>();
+ parameters.add(object());
+ parameters.add(context.sender());
+ context.arguments().getArguments().forEach((nodeName, argument) -> {
+ if (!(argument.node() instanceof LiteralCommandNode))
+ parameters.add(argument.data());
+ });
+ return (CommandResult) CommandResult.successful(methodHandle().invokeWithArguments(parameters));
+ }
+ }
+
+ @ApiStatus.Internal
+ public static final class CommandContextAndArgumentsCommandResultExecutor extends AbstractMethodCommandNodeExecutor {
+
+ public CommandContextAndArgumentsCommandResultExecutor(@NotNull Object object, @NotNull Method method) {
+ super(object, method);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable {
+ List parameters = new ArrayList<>();
+ parameters.add(object());
+ parameters.add(context);
+ context.arguments().getArguments().forEach((nodeName, argument) -> {
+ if (!(argument.node() instanceof LiteralCommandNode))
+ parameters.add(argument.data());
+ });
+ return (CommandResult) methodHandle().invokeWithArguments(parameters);
+ }
+ }
+
+ @ApiStatus.Internal
+ public static final class CommandContextAndArgumentsExecutor extends AbstractMethodCommandNodeExecutor {
+
+ public CommandContextAndArgumentsExecutor(@NotNull Object object, @NotNull Method method) {
+ super(object, method);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable {
+ List parameters = new ArrayList<>();
+ parameters.add(object());
+ parameters.add(context);
+ context.arguments().getArguments().forEach((nodeName, argument) -> {
+ if (!(argument.node() instanceof LiteralCommandNode))
+ parameters.add(argument.data());
+ });
+ return (CommandResult) CommandResult.successful(methodHandle().invokeWithArguments(parameters));
+ }
+ }
+
+ @ApiStatus.Internal
+ public static final class ArgumentsCommandResultExecutor extends AbstractMethodCommandNodeExecutor {
+
+ public ArgumentsCommandResultExecutor(@NotNull Object object, @NotNull Method method) {
+ super(object, method);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable {
+ List parameters = new ArrayList<>();
+ parameters.add(object());
+ context.arguments().getArguments().forEach((nodeName, argument) -> {
+ if (!(argument.node() instanceof LiteralCommandNode))
+ parameters.add(argument.data());
+ });
+ return (CommandResult) methodHandle().invokeWithArguments(parameters);
+ }
+ }
+
+ @ApiStatus.Internal
+ public static final class ArgumentsExecutor extends AbstractMethodCommandNodeExecutor {
+
+ public ArgumentsExecutor(@NotNull Object object, @NotNull Method method) {
+ super(object, method);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable {
+ List parameters = new ArrayList<>();
+ parameters.add(object());
+ context.arguments().getArguments().forEach((nodeName, argument) -> {
+ if (!(argument.node() instanceof LiteralCommandNode))
+ parameters.add(argument.data());
+ });
+ return (CommandResult) CommandResult.successful(methodHandle().invokeWithArguments(parameters));
+ }
+ }
+
+ @ApiStatus.Internal
+ public static final class NoArgumentsExecutor extends AbstractMethodCommandNodeExecutor {
+
+ public NoArgumentsExecutor(@NotNull Object object, @NotNull Method method) {
+ super(object, method);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable {
+ List parameters = new ArrayList<>();
+ parameters.add(object());
+ return (CommandResult) CommandResult.successful(methodHandle().invokeWithArguments(parameters));
+ }
+ }
+
+ @ApiStatus.Internal
+ public static final class NoArgumentsCommandResultExecutor extends AbstractMethodCommandNodeExecutor {
+
+ public NoArgumentsCommandResultExecutor(@NotNull Object object, @NotNull Method method) {
+ super(object, method);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public @NotNull CommandResult execute(@NotNull CommandContext context) throws Throwable {
+ List parameters = new ArrayList<>();
+ parameters.add(object());
+ return (CommandResult) methodHandle().invokeWithArguments(parameters);
+ }
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/executor/MethodCommandNodeExecutor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/executor/MethodCommandNodeExecutor.java
new file mode 100644
index 00000000..9b814159
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/executor/MethodCommandNodeExecutor.java
@@ -0,0 +1,16 @@
+package com.wizardlybump17.wlib.command.extractor.method.executor;
+
+import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor;
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.reflect.Method;
+
+public sealed interface MethodCommandNodeExecutor extends CommandNodeExecutor permits AbstractMethodCommandNodeExecutor {
+
+ @NotNull Object object();
+
+ @NotNull MethodHandle methodHandle();
+
+ @NotNull Method method();
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/MethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/MethodCommandNodeFactory.java
new file mode 100644
index 00000000..9ed8be17
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/MethodCommandNodeFactory.java
@@ -0,0 +1,34 @@
+package com.wizardlybump17.wlib.command.extractor.method.factory;
+
+import com.wizardlybump17.wlib.command.annotation.Command;
+import com.wizardlybump17.wlib.command.node.CommandNode;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+public abstract class MethodCommandNodeFactory {
+
+ public abstract @NotNull CommandNode> create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode> root);
+
+ public abstract @NotNull Class> @NotNull [] getSupportedTypes();
+
+ public boolean isSupported(@NotNull Class> type) {
+ if (isStrict()) {
+ for (Class> supportedType : getSupportedTypes())
+ if (supportedType.equals(type))
+ return true;
+ return false;
+ }
+
+ for (Class> supportedType : getSupportedTypes())
+ if (supportedType.isAssignableFrom(type))
+ return true;
+ return false;
+ }
+
+ public boolean isStrict() {
+ return true;
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/InstantCommandNode.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/InstantCommandNode.java
new file mode 100644
index 00000000..36030ef2
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/InstantCommandNode.java
@@ -0,0 +1,46 @@
+package com.wizardlybump17.wlib.command.extractor.method.factory.object;
+
+import com.wizardlybump17.wlib.command.exception.InputParsingException;
+import com.wizardlybump17.wlib.command.executor.CommandNodeExecutor;
+import com.wizardlybump17.wlib.command.input.AllowedInputs;
+import com.wizardlybump17.wlib.command.node.CommandNode;
+import com.wizardlybump17.wlib.command.suggestion.Suggester;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.time.Instant;
+import java.time.format.DateTimeParseException;
+import java.util.List;
+
+public class InstantCommandNode extends CommandNode {
+
+ public InstantCommandNode(@NotNull String name, @NotNull List> children, @NotNull AllowedInputs allowedInputs, @Nullable Suggester suggester, @Nullable CommandNodeExecutor> executor, @Nullable String permission) {
+ super(name, children, allowedInputs, suggester, executor, permission);
+ }
+
+ @Override
+ public @Nullable Instant parse(@NotNull String input) throws InputParsingException {
+ if (input.equalsIgnoreCase("null"))
+ return null;
+ try {
+ return Instant.parse(input);
+ } catch (DateTimeParseException e) {
+ throw new InputParsingException("Could not parse as Instant: " + input, e);
+ }
+ }
+
+ @Override
+ public @NotNull InstantCommandNode withChildren(@NotNull List> children) {
+ return new InstantCommandNode(getName(), children, getAllowedInputs(), getSuggester(), getExecutor(), getPermission());
+ }
+
+ @Override
+ public @NotNull InstantCommandNode withExecutor(@Nullable CommandNodeExecutor> executor) {
+ return new InstantCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), executor, getPermission());
+ }
+
+ @Override
+ public @NotNull InstantCommandNode withPermission(@Nullable String permission) {
+ return new InstantCommandNode(getName(), getChildren(), getAllowedInputs(), getSuggester(), getExecutor(), permission);
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/JsonElementMethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/JsonElementMethodCommandNodeFactory.java
new file mode 100644
index 00000000..8c9072a3
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/JsonElementMethodCommandNodeFactory.java
@@ -0,0 +1,52 @@
+package com.wizardlybump17.wlib.command.extractor.method.factory.object;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.wizardlybump17.wlib.command.annotation.Command;
+import com.wizardlybump17.wlib.command.annotation.NonNullInput;
+import com.wizardlybump17.wlib.command.extractor.method.factory.MethodCommandNodeFactory;
+import com.wizardlybump17.wlib.command.input.AllowedInputs;
+import com.wizardlybump17.wlib.command.node.CommandNode;
+import com.wizardlybump17.wlib.command.node.object.JsonElementCommandNode;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.List;
+
+public class JsonElementMethodCommandNodeFactory extends MethodCommandNodeFactory {
+
+ private final @NotNull Gson gson;
+
+ public JsonElementMethodCommandNodeFactory(@NotNull Gson gson) {
+ this.gson = gson;
+ }
+
+ @Override
+ public @NotNull JsonElementCommandNode create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode> root) {
+ return new JsonElementCommandNode(
+ name,
+ root == null ? List.of() : List.of(root),
+ parameter.isAnnotationPresent(NonNullInput.class) ? AllowedInputs.anyNotNull() : AllowedInputs.anyNullable(),
+ null,
+ null,
+ null,
+ gson
+ );
+ }
+
+ @Override
+ public @NotNull Class> @NotNull [] getSupportedTypes() {
+ return new Class[] {JsonElement.class};
+ }
+
+ @Override
+ public boolean isStrict() {
+ return false;
+ }
+
+ public @NotNull Gson getGson() {
+ return gson;
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/UUIDMethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/UUIDMethodCommandNodeFactory.java
new file mode 100644
index 00000000..cb3817c4
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/object/UUIDMethodCommandNodeFactory.java
@@ -0,0 +1,38 @@
+package com.wizardlybump17.wlib.command.extractor.method.factory.object;
+
+import com.wizardlybump17.wlib.command.annotation.Command;
+import com.wizardlybump17.wlib.command.annotation.NonNullInput;
+import com.wizardlybump17.wlib.command.extractor.method.factory.MethodCommandNodeFactory;
+import com.wizardlybump17.wlib.command.input.object.AllowedUUIDInputs;
+import com.wizardlybump17.wlib.command.node.CommandNode;
+import com.wizardlybump17.wlib.command.node.object.UUIDCommandNode;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.List;
+import java.util.UUID;
+
+public class UUIDMethodCommandNodeFactory extends MethodCommandNodeFactory {
+
+ @Override
+ public @NotNull CommandNode> create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode> root) {
+ if (!isSupported(parameter.getType()))
+ throw new IllegalArgumentException("Unsupported type. We only accept UUIDs: " + parameter);
+
+ return new UUIDCommandNode(
+ name,
+ root == null ? List.of() : List.of(root),
+ parameter.isAnnotationPresent(NonNullInput.class) ? AllowedUUIDInputs.anyNotNull() : AllowedUUIDInputs.anyNullable(),
+ null,
+ null,
+ null
+ );
+ }
+
+ @Override
+ public @NotNull Class> @NotNull [] getSupportedTypes() {
+ return new Class[] {UUID.class};
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/BooleanMethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/BooleanMethodCommandNodeFactory.java
new file mode 100644
index 00000000..9cbe2856
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/BooleanMethodCommandNodeFactory.java
@@ -0,0 +1,37 @@
+package com.wizardlybump17.wlib.command.extractor.method.factory.primitive;
+
+import com.wizardlybump17.wlib.command.annotation.Command;
+import com.wizardlybump17.wlib.command.input.primitive.AllowedBooleanInputs;
+import com.wizardlybump17.wlib.command.node.CommandNode;
+import com.wizardlybump17.wlib.command.node.primitive.BooleanCommandNode;
+import com.wizardlybump17.wlib.command.suggestion.primitive.BooleanSuggester;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.List;
+
+public class BooleanMethodCommandNodeFactory extends PrimitiveMethodCommandNodeFactory {
+
+ @Override
+ public @NotNull BooleanCommandNode create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode> root) {
+ Class> type = parameter.getType();
+ if (isSupported(type))
+ throw new IllegalArgumentException("Unsupported type. We only accept booleans: " + type);
+
+ return new BooleanCommandNode(
+ name,
+ root == null ? List.of() : List.of(root),
+ AllowedBooleanInputs.anyNotNull(),
+ BooleanSuggester.any(),
+ null,
+ null
+ );
+ }
+
+ @Override
+ public @NotNull Class> @NotNull [] getSupportedTypes() {
+ return new Class[] {boolean.class, Boolean.class};
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/CharacterMethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/CharacterMethodCommandNodeFactory.java
new file mode 100644
index 00000000..cd662956
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/CharacterMethodCommandNodeFactory.java
@@ -0,0 +1,37 @@
+package com.wizardlybump17.wlib.command.extractor.method.factory.primitive;
+
+import com.wizardlybump17.wlib.command.annotation.Command;
+import com.wizardlybump17.wlib.command.input.primitive.AllowedCharacterInputs;
+import com.wizardlybump17.wlib.command.node.CommandNode;
+import com.wizardlybump17.wlib.command.node.primitive.AbstractPrimitiveCommandNode;
+import com.wizardlybump17.wlib.command.node.primitive.CharacterCommandNode;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.List;
+
+public class CharacterMethodCommandNodeFactory extends PrimitiveMethodCommandNodeFactory {
+
+ @Override
+ public @NotNull AbstractPrimitiveCommandNode> create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode> root) {
+ Class> type = parameter.getType();
+ if (!isSupported(type))
+ throw new IllegalArgumentException("Unsupported type. We only accept chars: " + type);
+
+ return new CharacterCommandNode(
+ name,
+ root == null ? List.of() : List.of(root),
+ AllowedCharacterInputs.anyNotNull(),
+ null,
+ null,
+ null
+ );
+ }
+
+ @Override
+ public @NotNull Class> @NotNull [] getSupportedTypes() {
+ return new Class>[] {char.class, Character.class};
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/NumberMethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/NumberMethodCommandNodeFactory.java
new file mode 100644
index 00000000..2210ae5d
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/NumberMethodCommandNodeFactory.java
@@ -0,0 +1,100 @@
+package com.wizardlybump17.wlib.command.extractor.method.factory.primitive;
+
+import com.wizardlybump17.wlib.command.annotation.Command;
+import com.wizardlybump17.wlib.command.input.primitive.number.*;
+import com.wizardlybump17.wlib.command.node.CommandNode;
+import com.wizardlybump17.wlib.command.node.primitive.number.*;
+import com.wizardlybump17.wlib.command.suggestion.primitive.number.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.List;
+
+public class NumberMethodCommandNodeFactory extends PrimitiveMethodCommandNodeFactory {
+
+ @Override
+ public @NotNull NumberCommandNode> create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode> root) {
+ Class> type = parameter.getType();
+ if (!isSupported(type))
+ throw new IllegalArgumentException("Unsupported type. We accept only primitive numbers: " + type);
+
+ List> children = root == null ? List.of() : List.of(root);
+
+ if (type == byte.class || type == Byte.class) {
+ return new ByteCommandNode(
+ name,
+ children,
+ AllowedByteInputs.unlimited(),
+ ByteSuggester.unlimited(),
+ null,
+ null
+ );
+ }
+ if (type == short.class || type == Short.class) {
+ return new ShortCommandNode(
+ name,
+ children,
+ AllowedShortInputs.unlimited(),
+ ShortSuggester.unlimited(),
+ null,
+ null
+ );
+ }
+ if (type == int.class || type == Integer.class) {
+ return new IntegerCommandNode(
+ name,
+ children,
+ AllowedIntegerInputs.unlimited(),
+ IntegerSuggester.unlimited(),
+ null,
+ null
+ );
+ }
+ if (type == long.class || type == Long.class) {
+ return new LongCommandNode(
+ name,
+ children,
+ AllowedLongInputs.unlimited(),
+ LongSuggester.unlimited(),
+ null,
+ null
+ );
+ }
+ if (type == float.class || type == Float.class) {
+ return new FloatCommandNode(
+ name,
+ children,
+ AllowedFloatInputs.unlimited(),
+ FloatSuggester.unlimited(),
+ null,
+ null
+ );
+ }
+ if (type == double.class || type == Double.class) {
+ return new DoubleCommandNode(
+ name,
+ children,
+ AllowedDoubleInputs.unlimited(),
+ DoubleSuggester.unlimited(),
+ null,
+ null
+ );
+ }
+
+ throw new IllegalArgumentException("Unsupported type. We accept only primitive numbers: " + type);
+ }
+
+ @Override
+ public @NotNull Class> @NotNull [] getSupportedTypes() {
+ return new Class>[] {
+ byte.class, Byte.class,
+ short.class, Short.class,
+ int.class, Integer.class,
+ long.class, Long.class,
+ float.class, Float.class,
+ double.class, Double.class
+ };
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/PrimitiveMethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/PrimitiveMethodCommandNodeFactory.java
new file mode 100644
index 00000000..1ded737b
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/primitive/PrimitiveMethodCommandNodeFactory.java
@@ -0,0 +1,16 @@
+package com.wizardlybump17.wlib.command.extractor.method.factory.primitive;
+
+import com.wizardlybump17.wlib.command.annotation.Command;
+import com.wizardlybump17.wlib.command.extractor.method.factory.MethodCommandNodeFactory;
+import com.wizardlybump17.wlib.command.node.CommandNode;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+public abstract class PrimitiveMethodCommandNodeFactory extends MethodCommandNodeFactory {
+
+ @Override
+ public abstract @NotNull CommandNode> create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode> root);
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/string/StringMethodCommandNodeFactory.java b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/string/StringMethodCommandNodeFactory.java
new file mode 100644
index 00000000..eb29fc96
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/extractor/method/factory/string/StringMethodCommandNodeFactory.java
@@ -0,0 +1,39 @@
+package com.wizardlybump17.wlib.command.extractor.method.factory.string;
+
+import com.wizardlybump17.wlib.command.annotation.Command;
+import com.wizardlybump17.wlib.command.annotation.NonNullInput;
+import com.wizardlybump17.wlib.command.extractor.method.factory.MethodCommandNodeFactory;
+import com.wizardlybump17.wlib.command.input.string.AllowedStringInputs;
+import com.wizardlybump17.wlib.command.node.CommandNode;
+import com.wizardlybump17.wlib.command.node.string.StringCommandNode;
+import com.wizardlybump17.wlib.command.suggestion.string.StringSuggester;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.List;
+
+public class StringMethodCommandNodeFactory extends MethodCommandNodeFactory {
+
+ @Override
+ public @NotNull CommandNode> create(@NotNull Object object, @NotNull Method method, @NotNull Command commandAnnotation, @NotNull Parameter parameter, @NotNull String name, @Nullable CommandNode> root) {
+ Class> type = parameter.getType();
+ if (!isSupported(type))
+ throw new IllegalArgumentException("Unsupported type. We accept only Strings: " + type);
+
+ return new StringCommandNode(
+ name,
+ root == null ? List.of() : List.of(root),
+ parameter.isAnnotationPresent(NonNullInput.class) ? AllowedStringInputs.anyNotNull() : AllowedStringInputs.anyNullable(),
+ StringSuggester.any(),
+ null,
+ null
+ );
+ }
+
+ @Override
+ public @NotNull Class> @NotNull [] getSupportedTypes() {
+ return new Class[] {String.class};
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/holder/Command.java b/commands/src/main/java/com/wizardlybump17/wlib/command/holder/Command.java
deleted file mode 100644
index e766809d..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/holder/Command.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.wizardlybump17.wlib.command.holder;
-
-import com.wizardlybump17.wlib.command.CommandManager;
-
-public interface Command {
-
- void setExecutor(CommandExecutor executor);
-
- CommandExecutor getExecutor();
-
- /**
- * Used by the {@link com.wizardlybump17.wlib.command.CommandManager} when creating a new command.
- * This is used to set the default command executor.
- * @param manager the command manager
- * @param name the command name
- * @return the default command executor
- */
- CommandExecutor getDefaultExecutor(CommandManager manager, String name);
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/holder/CommandExecutor.java b/commands/src/main/java/com/wizardlybump17/wlib/command/holder/CommandExecutor.java
deleted file mode 100644
index 05321945..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/holder/CommandExecutor.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.wizardlybump17.wlib.command.holder;
-
-import com.wizardlybump17.wlib.command.args.reader.ArgsReaderException;
-import com.wizardlybump17.wlib.command.exception.CommandException;
-import com.wizardlybump17.wlib.command.sender.CommandSender;
-
-/**
- * Interface for intercepting commands when they are called
- */
-public interface CommandExecutor {
-
- void execute(CommandSender> sender, String commandName, String[] args) throws ArgsReaderException, CommandException;
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/holder/CommandHolder.java b/commands/src/main/java/com/wizardlybump17/wlib/command/holder/CommandHolder.java
deleted file mode 100644
index c5c8e0ef..00000000
--- a/commands/src/main/java/com/wizardlybump17/wlib/command/holder/CommandHolder.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.wizardlybump17.wlib.command.holder;
-
-import lombok.NonNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.logging.Logger;
-
-/**
- * Represents something that can hold commands
- * @param The holder type
- */
-public interface CommandHolder {
-
- @Nullable Command getCommand(String name);
-
- @NonNull H getHandle();
-
- @NonNull
- Logger getLogger();
-}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/AllowedInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/AllowedInputs.java
new file mode 100644
index 00000000..afa40388
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/AllowedInputs.java
@@ -0,0 +1,67 @@
+package com.wizardlybump17.wlib.command.input;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Objects;
+
+public interface AllowedInputs {
+
+ boolean isAllowed(@Nullable T input);
+
+ @SuppressWarnings("unchecked")
+ static @NotNull Any anyNullable() {
+ return (Any) Any.NULLABLE;
+ }
+
+ @SuppressWarnings("unchecked")
+ static @NotNull Any anyNotNull() {
+ return (Any) Any.NOT_NULL;
+ }
+
+ static AllowedListInputs.@NotNull Values values(@NotNull List values) {
+ return new AllowedListInputs.Values<>(values);
+ }
+
+ final class Any implements AllowedInputs {
+
+ private static final @NotNull Any NULLABLE = new Any<>(true);
+ private static final @NotNull Any NOT_NULL = new Any<>(false);
+
+ private final boolean nullable;
+
+ Any(boolean nullable) {
+ this.nullable = nullable;
+ }
+
+ @Override
+ public boolean isAllowed(@Nullable T input) {
+ return nullable || input != null;
+ }
+
+ public boolean nullable() {
+ return nullable;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Any> any = (Any>) o;
+ return nullable == any.nullable;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(nullable);
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedInputs$Any{" +
+ "nullable=" + nullable +
+ '}';
+ }
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/AllowedListInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/AllowedListInputs.java
new file mode 100644
index 00000000..5a6a4fbb
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/AllowedListInputs.java
@@ -0,0 +1,53 @@
+package com.wizardlybump17.wlib.command.input;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Objects;
+
+public interface AllowedListInputs extends AllowedInputs {
+
+ @NotNull List allowedValues();
+
+ @Override
+ default boolean isAllowed(@Nullable T input) {
+ if (input == null)
+ return false;
+ return allowedValues().contains(input);
+ }
+
+ final class Values implements AllowedListInputs {
+
+ private final @NotNull List values;
+
+ Values(@NotNull List values) {
+ this.values = List.copyOf(values);
+ }
+
+ @Override
+ public @NotNull List allowedValues() {
+ return values;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Values> values1 = (Values>) o;
+ return Objects.equals(values, values1.values);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(values);
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedListInputs$Values{" +
+ "values=" + values +
+ '}';
+ }
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/RangedAllowedInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/RangedAllowedInputs.java
new file mode 100644
index 00000000..2cf43936
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/RangedAllowedInputs.java
@@ -0,0 +1,12 @@
+package com.wizardlybump17.wlib.command.input;
+
+import org.jetbrains.annotations.NotNull;
+
+public interface RangedAllowedInputs extends AllowedInputs {
+
+ @NotNull T from();
+
+ @NotNull T to();
+
+ boolean isInRange(@NotNull T input);
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/SingleValueInput.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/SingleValueInput.java
new file mode 100644
index 00000000..a78c8a29
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/SingleValueInput.java
@@ -0,0 +1,14 @@
+package com.wizardlybump17.wlib.command.input;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public interface SingleValueInput extends AllowedInputs {
+
+ @NotNull T value();
+
+ @Override
+ default boolean isAllowed(@Nullable T input) {
+ return value().equals(input);
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/object/AllowedUUIDInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/object/AllowedUUIDInputs.java
new file mode 100644
index 00000000..3dc4e768
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/object/AllowedUUIDInputs.java
@@ -0,0 +1,139 @@
+package com.wizardlybump17.wlib.command.input.object;
+
+import com.wizardlybump17.wlib.command.input.AllowedInputs;
+import com.wizardlybump17.wlib.command.input.AllowedListInputs;
+import com.wizardlybump17.wlib.command.input.SingleValueInput;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+
+public interface AllowedUUIDInputs extends AllowedInputs {
+
+ static @NotNull Value value(@NotNull UUID value) {
+ return new Value(value);
+ }
+
+ static @NotNull Values values(@NotNull List values) {
+ return new Values(List.copyOf(values));
+ }
+
+ static @NotNull Values values(@NotNull UUID @NotNull ... values) {
+ return new Values(List.of(values));
+ }
+
+ static @NotNull Any anyNullable() {
+ return Any.NULLABLE;
+ }
+
+ static @NotNull Any anyNotNull() {
+ return Any.NOT_NULL;
+ }
+
+ final class Value implements AllowedUUIDInputs, SingleValueInput {
+
+ private final @NotNull UUID value;
+
+ Value(@NotNull UUID value) {
+ this.value = value;
+ }
+
+ @Override
+ public @NotNull UUID value() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == null || getClass() != object.getClass())
+ return false;
+ Value value1 = (Value) object;
+ return Objects.equals(value, value1.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(value);
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedUUIDInputs$Value{" +
+ "value=" + value +
+ '}';
+ }
+ }
+
+ final class Values implements AllowedUUIDInputs, AllowedListInputs {
+
+ private final @NotNull List values;
+
+ private Values(@NotNull List values) {
+ this.values = values;
+ }
+
+ @Override
+ public @NotNull List allowedValues() {
+ return values;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == null || getClass() != object.getClass())
+ return false;
+ AllowedUUIDInputs.Values values1 = (AllowedUUIDInputs.Values) object;
+ return Objects.equals(values, values1.values);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(values);
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedUUIDInputs$Values{" +
+ "values=" + values +
+ '}';
+ }
+ }
+
+ final class Any implements AllowedUUIDInputs {
+
+ private static final @NotNull AllowedUUIDInputs.Any NULLABLE = new AllowedUUIDInputs.Any(true);
+ private static final @NotNull AllowedUUIDInputs.Any NOT_NULL = new AllowedUUIDInputs.Any(false);
+
+ private final boolean nullable;
+
+ private Any(boolean nullable) {
+ this.nullable = nullable;
+ }
+
+ @Override
+ public boolean isAllowed(@Nullable UUID input) {
+ return nullable || input != null;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == null || getClass() != object.getClass())
+ return false;
+ AllowedUUIDInputs.Any any = (AllowedUUIDInputs.Any) object;
+ return nullable == any.nullable;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(nullable);
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedUUIDInputs$Any{" +
+ "nullable=" + nullable +
+ '}';
+ }
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/AllowedBooleanInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/AllowedBooleanInputs.java
new file mode 100644
index 00000000..154211a6
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/AllowedBooleanInputs.java
@@ -0,0 +1,103 @@
+package com.wizardlybump17.wlib.command.input.primitive;
+
+import com.wizardlybump17.wlib.command.input.SingleValueInput;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Objects;
+
+public interface AllowedBooleanInputs extends PrimitiveAllowedInputs {
+
+ static @NotNull True trueValue() {
+ return True.INSTANCE;
+ }
+
+ static @NotNull False falseValue() {
+ return False.INSTANCE;
+ }
+
+ static @NotNull Any anyNullable() {
+ return Any.NULLABLE;
+ }
+
+ static @NotNull Any anyNotNull() {
+ return Any.NOT_NULL;
+ }
+
+ final class True implements AllowedBooleanInputs, SingleValueInput {
+
+ private static final @NotNull True INSTANCE = new True();
+
+ private True() {
+ }
+
+ @Override
+ public @NotNull Boolean value() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedBooleanInputs$True{}";
+ }
+ }
+
+ final class False implements AllowedBooleanInputs, SingleValueInput {
+
+ private static final @NotNull False INSTANCE = new False();
+
+ private False() {
+ }
+
+ @Override
+ public @NotNull Boolean value() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedBooleanInputs$False{}";
+ }
+ }
+
+ final class Any implements AllowedBooleanInputs {
+
+ private static final @NotNull Any NULLABLE = new Any(true);
+ private static final @NotNull Any NOT_NULL = new Any(false);
+
+ private final boolean nullable;
+
+ private Any(boolean nullable) {
+ this.nullable = nullable;
+ }
+
+ public boolean nullable() {
+ return nullable;
+ }
+
+ @Override
+ public boolean isAllowed(@Nullable Boolean input) {
+ return nullable || input != null;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == null || getClass() != object.getClass())
+ return false;
+ Any any = (Any) object;
+ return nullable == any.nullable;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(nullable);
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedBooleanInputs$Any{" +
+ "nullable=" + nullable +
+ '}';
+ }
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/AllowedCharacterInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/AllowedCharacterInputs.java
new file mode 100644
index 00000000..79046455
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/AllowedCharacterInputs.java
@@ -0,0 +1,190 @@
+package com.wizardlybump17.wlib.command.input.primitive;
+
+import com.wizardlybump17.wlib.command.input.AllowedListInputs;
+import com.wizardlybump17.wlib.command.input.SingleValueInput;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public interface AllowedCharacterInputs extends PrimitiveAllowedInputs {
+
+ static @NotNull Value value(char value) {
+ return new Value(value, false);
+ }
+
+ static @NotNull Value valueIgnoreCase(char value) {
+ return new Value(value, true);
+ }
+
+ static @NotNull Values values(@NotNull List values) {
+ values = List.copyOf(values);
+ return new Values(values, values, false);
+ }
+
+ static @NotNull Values values(@NotNull Character @NotNull ... values) {
+ List valuesList = List.of(values);
+ return new Values(valuesList, valuesList, false);
+ }
+
+ static @NotNull Values valuesIgnoreCase(@NotNull List values) {
+ return new Values(
+ List.copyOf(values),
+ values.stream()
+ .map(Character::toLowerCase)
+ .collect(Collectors.toList()),
+ true
+ );
+ }
+
+ static @NotNull Any anyNullable() {
+ return Any.NULLABLE;
+ }
+
+ static @NotNull Any anyNotNull() {
+ return Any.NOT_NULL;
+ }
+
+ final class Value implements AllowedCharacterInputs, SingleValueInput {
+
+ private final char value;
+ private final boolean ignoreCase;
+
+ private Value(char value, boolean ignoreCase) {
+ this.value = value;
+ this.ignoreCase = ignoreCase;
+ }
+
+ @Override
+ public boolean isAllowed(@Nullable Character input) {
+ if (input == null)
+ return false;
+ return ignoreCase ? Character.toLowerCase(input) == Character.toLowerCase(value) : input == value;
+ }
+
+ @Override
+ public @NotNull Character value() {
+ return value;
+ }
+
+ public boolean ignoreCase() {
+ return ignoreCase;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == null || getClass() != object.getClass())
+ return false;
+ Value value1 = (Value) object;
+ return value == value1.value && ignoreCase == value1.ignoreCase;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(value, ignoreCase);
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedCharacterInputs$Value{" +
+ "value=" + value +
+ ", ignoreCase=" + ignoreCase +
+ '}';
+ }
+ }
+
+ final class Values implements AllowedCharacterInputs, AllowedListInputs {
+
+ private final @NotNull List values;
+ private final @NotNull List toCheck;
+ private final boolean ignoreCase;
+
+ private Values(@NotNull List values, @NotNull List toCheck, boolean ignoreCase) {
+ this.values = values;
+ this.toCheck = toCheck;
+ this.ignoreCase = ignoreCase;
+ }
+
+ @Override
+ public @NotNull List allowedValues() {
+ return values;
+ }
+
+ public boolean isIgnoreCase() {
+ return ignoreCase;
+ }
+
+ @Override
+ public boolean isAllowed(@Nullable Character input) {
+ if (input == null)
+ return false;
+ if (ignoreCase)
+ return toCheck.contains(Character.toLowerCase(input));
+ return toCheck.contains(input);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == null || getClass() != object.getClass())
+ return false;
+ AllowedCharacterInputs.Values values1 = (AllowedCharacterInputs.Values) object;
+ return ignoreCase == values1.ignoreCase && Objects.equals(values, values1.values) && Objects.equals(toCheck, values1.toCheck);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(values, toCheck, ignoreCase);
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedCharacterInputs$Values{" +
+ "values=" + values +
+ ", ignoreCase=" + ignoreCase +
+ '}';
+ }
+ }
+
+ final class Any implements AllowedCharacterInputs {
+
+ private static final @NotNull Any NULLABLE = new Any(true);
+ private static final @NotNull Any NOT_NULL = new Any(false);
+
+ private final boolean nullable;
+
+ private Any(boolean nullable) {
+ this.nullable = nullable;
+ }
+
+ public boolean nullable() {
+ return nullable;
+ }
+
+ @Override
+ public boolean isAllowed(@Nullable Character input) {
+ return nullable || input != null;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == null || getClass() != object.getClass())
+ return false;
+ Any any = (Any) object;
+ return nullable == any.nullable;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(nullable);
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedCharacterInputs$Any{" +
+ "nullable=" + nullable +
+ '}';
+ }
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/PrimitiveAllowedInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/PrimitiveAllowedInputs.java
new file mode 100644
index 00000000..4c815ba3
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/PrimitiveAllowedInputs.java
@@ -0,0 +1,6 @@
+package com.wizardlybump17.wlib.command.input.primitive;
+
+import com.wizardlybump17.wlib.command.input.AllowedInputs;
+
+public interface PrimitiveAllowedInputs extends AllowedInputs
{
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedByteInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedByteInputs.java
new file mode 100644
index 00000000..93d349d9
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedByteInputs.java
@@ -0,0 +1,147 @@
+package com.wizardlybump17.wlib.command.input.primitive.number;
+
+import com.wizardlybump17.wlib.command.input.primitive.PrimitiveAllowedInputs;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public interface AllowedByteInputs extends PrimitiveAllowedInputs {
+
+ static @NotNull Range range(byte from, byte to) {
+ return new Range(from, to);
+ }
+
+ static @NotNull Unlimited unlimited() {
+ return Unlimited.INSTANCE;
+ }
+
+ static @NotNull Positive positive() {
+ return Positive.INSTANCE;
+ }
+
+ static @NotNull Negative negative() {
+ return Negative.INSTANCE;
+ }
+
+ static @NotNull Values values(@NotNull List values) {
+ return new Values(values);
+ }
+
+ static @NotNull Value value(byte value) {
+ return new Value(value);
+ }
+
+ final class Range extends AllowedNumberInputs.Ranged implements AllowedByteInputs {
+
+ Range(byte from, byte to) {
+ super(from, to);
+ if (from >= to)
+ throw new IllegalArgumentException("from must be less than to");
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedByteInputs$Range{" +
+ "from=" + from() +
+ ", to=" + to() +
+ "}";
+ }
+ }
+
+ final class Unlimited extends AllowedNumberInputs.Unlimited implements AllowedByteInputs {
+
+ static final @NotNull AllowedByteInputs.Unlimited INSTANCE = new AllowedByteInputs.Unlimited();
+
+ private Unlimited() {
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedByteInputs$Unlimited{}";
+ }
+ }
+
+ final class Positive extends AllowedNumberInputs.Positive implements AllowedByteInputs {
+
+ static final @NotNull AllowedByteInputs.Positive INSTANCE = new AllowedByteInputs.Positive();
+
+ private Positive() {
+ }
+
+ @Override
+ public @NotNull Byte from() {
+ return 0;
+ }
+
+ @Override
+ public @NotNull Byte to() {
+ return Byte.MAX_VALUE;
+ }
+
+ @Override
+ public boolean isInRange(@NotNull Byte input) {
+ return input > -1;
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedByteInputs$Positive{}";
+ }
+ }
+
+ final class Negative extends AllowedNumberInputs.Negative implements AllowedByteInputs {
+
+ static final @NotNull AllowedByteInputs.Negative INSTANCE = new AllowedByteInputs.Negative();
+
+ private Negative() {
+ }
+
+ @Override
+ public @NotNull Byte from() {
+ return -1;
+ }
+
+ @Override
+ public @NotNull Byte to() {
+ return Byte.MIN_VALUE;
+ }
+
+ @Override
+ public boolean isInRange(@NotNull Byte input) {
+ return input < 0;
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedByteInputs$Negative{}";
+ }
+ }
+
+ final class Value extends AllowedNumberInputs.Value implements AllowedByteInputs {
+
+ Value(byte value) {
+ super(value);
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedByteInputs$Value{"
+ + "value=" + value()
+ + '}';
+ }
+ }
+
+ final class Values extends AllowedNumberInputs.Values implements AllowedByteInputs {
+
+ Values(@NotNull List values) {
+ super(values);
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedByteInputs$Values{"
+ + "values=" + allowedValues()
+ + '}';
+ }
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedDoubleInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedDoubleInputs.java
new file mode 100644
index 00000000..e4124abc
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedDoubleInputs.java
@@ -0,0 +1,147 @@
+package com.wizardlybump17.wlib.command.input.primitive.number;
+
+import com.wizardlybump17.wlib.command.input.primitive.PrimitiveAllowedInputs;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public interface AllowedDoubleInputs extends PrimitiveAllowedInputs {
+
+ static @NotNull Range range(double from, double to) {
+ return new Range(from, to);
+ }
+
+ static @NotNull Unlimited unlimited() {
+ return Unlimited.INSTANCE;
+ }
+
+ static @NotNull Positive positive() {
+ return Positive.INSTANCE;
+ }
+
+ static @NotNull Negative negative() {
+ return Negative.INSTANCE;
+ }
+
+ static @NotNull Values values(@NotNull List values) {
+ return new Values(values);
+ }
+
+ static @NotNull Value value(double value) {
+ return new Value(value);
+ }
+
+ final class Range extends AllowedNumberInputs.Ranged implements AllowedDoubleInputs {
+
+ Range(double from, double to) {
+ super(from, to);
+ if (from >= to)
+ throw new IllegalArgumentException("from must be less than to");
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedByteInputs$Range{" +
+ "from=" + from() +
+ ", to=" + to() +
+ "}";
+ }
+ }
+
+ final class Unlimited extends AllowedNumberInputs.Unlimited implements AllowedDoubleInputs {
+
+ static final @NotNull AllowedDoubleInputs.Unlimited INSTANCE = new AllowedDoubleInputs.Unlimited();
+
+ private Unlimited() {
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedDoubleInputs$Unlimited{}";
+ }
+ }
+
+ final class Positive extends AllowedNumberInputs.Positive implements AllowedDoubleInputs {
+
+ static final @NotNull AllowedDoubleInputs.Positive INSTANCE = new AllowedDoubleInputs.Positive();
+
+ private Positive() {
+ }
+
+ @Override
+ public @NotNull Double from() {
+ return 0.0;
+ }
+
+ @Override
+ public @NotNull Double to() {
+ return Double.MAX_VALUE;
+ }
+
+ @Override
+ public boolean isInRange(@NotNull Double input) {
+ return input > -1;
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedDoubleInputs$Positive{}";
+ }
+ }
+
+ final class Negative extends AllowedNumberInputs.Negative implements AllowedDoubleInputs {
+
+ static final @NotNull AllowedDoubleInputs.Negative INSTANCE = new AllowedDoubleInputs.Negative();
+
+ private Negative() {
+ }
+
+ @Override
+ public @NotNull Double from() {
+ return -1.0;
+ }
+
+ @Override
+ public @NotNull Double to() {
+ return Double.MIN_VALUE;
+ }
+
+ @Override
+ public boolean isInRange(@NotNull Double input) {
+ return input < 0;
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedDoubleInputs$Negative{}";
+ }
+ }
+
+ final class Value extends AllowedNumberInputs.Value implements AllowedDoubleInputs {
+
+ Value(double value) {
+ super(value);
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedDoubleInputs$Value{" +
+ "value=" + value() +
+ "}";
+ }
+ }
+
+ final class Values extends AllowedNumberInputs.Values implements AllowedDoubleInputs {
+
+ Values(@NotNull List values) {
+ super(values);
+ }
+
+ @Override
+ public String toString() {
+ return "AllowedDoubleInputs$Values{"
+ + "values=" + allowedValues()
+ + '}';
+ }
+ }
+}
diff --git a/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedFloatInputs.java b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedFloatInputs.java
new file mode 100644
index 00000000..f1c606a3
--- /dev/null
+++ b/commands/src/main/java/com/wizardlybump17/wlib/command/input/primitive/number/AllowedFloatInputs.java
@@ -0,0 +1,147 @@
+package com.wizardlybump17.wlib.command.input.primitive.number;
+
+import com.wizardlybump17.wlib.command.input.primitive.PrimitiveAllowedInputs;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public interface AllowedFloatInputs extends PrimitiveAllowedInputs {
+
+ static @NotNull Range range(float from, float to) {
+ return new Range(from, to);
+ }
+
+ static @NotNull Unlimited unlimited() {
+ return Unlimited.INSTANCE;
+ }
+
+ static @NotNull Positive positive() {
+ return Positive.INSTANCE;
+ }
+
+ static @NotNull Negative negative() {
+ return Negative.INSTANCE;
+ }
+
+ static @NotNull Values values(@NotNull List values) {
+ return new Values(values);
+ }
+
+ static @NotNull Value value(float value) {
+ return new Value(value);
+ }
+
+ final class Range extends AllowedNumberInputs.Ranged