diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 2d839d5..41683b6 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("java") + id("java-library") id("maven-publish") } @@ -8,8 +9,8 @@ version = rootProject.version repositories { mavenCentral() - maven("https://repo.thenextlvl.net/releases") maven("https://repo.papermc.io/repository/maven-public/") + maven("https://repo.thenextlvl.net/releases") } java { @@ -23,15 +24,16 @@ tasks.compileJava { } dependencies { - compileOnly("com.google.code.gson:gson:2.13.2") - compileOnly("net.kyori:adventure-api:4.26.0-SNAPSHOT") + api("net.thenextlvl:static-binder:0.1.2") - implementation("net.thenextlvl.core:files:3.0.1") - implementation("net.thenextlvl.core:i18n:3.2.2") + compileOnlyApi("org.jetbrains:annotations:26.0.2-1") + compileOnlyApi("org.jspecify:jspecify:1.0.0") } publishing { publications.create("maven") { + artifactId = "commander" + groupId = "net.thenextlvl" pom.url.set("https://thenextlvl.net/docs/commander") pom.scm { val repository = "TheNextLvl-net/commander" diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java new file mode 100644 index 0000000..8f9e611 --- /dev/null +++ b/api/src/main/java/module-info.java @@ -0,0 +1,10 @@ +import org.jspecify.annotations.NullMarked; + +@NullMarked +module net.thenextlvl.commander { + exports net.thenextlvl.commander; + + requires static org.jetbrains.annotations; + requires static org.jspecify; + requires net.thenextlvl.binder; +} \ No newline at end of file diff --git a/api/src/main/java/net/thenextlvl/commander/CommandFinder.java b/api/src/main/java/net/thenextlvl/commander/CommandFinder.java index caa5f8f..8be59df 100644 --- a/api/src/main/java/net/thenextlvl/commander/CommandFinder.java +++ b/api/src/main/java/net/thenextlvl/commander/CommandFinder.java @@ -1,69 +1,58 @@ package net.thenextlvl.commander; +import net.thenextlvl.binder.StaticBinder; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Unmodifiable; -import org.jspecify.annotations.NullMarked; -import java.util.Set; import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; -import java.util.stream.Collectors; import java.util.stream.Stream; /** * The CommandFinder interface defines methods for finding commands based on a given input. */ -@NullMarked +@ApiStatus.NonExtendable public interface CommandFinder { + static CommandFinder instance() { + return StaticBinder.getInstance(CommandFinder.class.getClassLoader()).find(CommandFinder.class); + } + /** * Finds and returns a set of commands that match the given pattern. * * @param pattern The pattern used to search for commands. - * @return An unmodifiable set of strings representing the commands that match the pattern. + * @return A stream of strings representing the commands that match the pattern. */ @Unmodifiable - Set findCommands(Pattern pattern); + @Contract(pure = true) + Stream findCommands(Pattern pattern); /** * Filters and finds commands from the provided stream that match the given pattern. * * @param commands The stream of commands to be searched. * @param pattern The pattern used to filter matching commands. - * @return An unmodifiable set of strings representing the commands that match the given pattern. + * @return A stream of strings representing the commands that match the given pattern. */ - default @Unmodifiable Set findCommands(Stream commands, Pattern pattern) { - return commands.filter(command -> - pattern.matcher(command).matches() - ).collect(Collectors.toSet()); - } + @Contract(pure = true) + Stream findCommands(Stream commands, Pattern pattern); /** * This method finds commands based on a given input. * * @param commands The stream of commands to search for. * @param input The input used to search for commands. - * @return An unmodifiable set of strings representing the found commands. + * @return A stream of strings representing the found commands. */ - default @Unmodifiable Set findCommands(Stream commands, String input) { - try { - return findCommands(commands, Pattern.compile(input.replace("*", ".*"))); - } catch (PatternSyntaxException e) { - var escaped = Pattern.quote(input).replace("\\*", "*"); - return findCommands(commands, Pattern.compile(escaped.replace("*", ".*"))); - } - } + @Contract(pure = true) + Stream findCommands(Stream commands, String input); /** * Finds commands based on the given input. * * @param input The input used to search for commands. - * @return An unmodifiable set of strings representing the found commands. + * @return A stream of strings representing the found commands. */ - default @Unmodifiable Set findCommands(String input) { - try { - return findCommands(Pattern.compile(input.replace("*", ".*"))); - } catch (PatternSyntaxException e) { - var escaped = Pattern.quote(input).replace("\\*", "*"); - return findCommands(Pattern.compile(escaped.replace("*", ".*"))); - } - } + @Contract(pure = true) + Stream findCommands(String input); } diff --git a/api/src/main/java/net/thenextlvl/commander/CommandRegistry.java b/api/src/main/java/net/thenextlvl/commander/CommandRegistry.java index 39034a3..f33a8e0 100644 --- a/api/src/main/java/net/thenextlvl/commander/CommandRegistry.java +++ b/api/src/main/java/net/thenextlvl/commander/CommandRegistry.java @@ -1,12 +1,18 @@ package net.thenextlvl.commander; +import net.thenextlvl.binder.StaticBinder; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Unmodifiable; -import org.jspecify.annotations.NullMarked; import java.util.Set; -@NullMarked +@ApiStatus.NonExtendable public interface CommandRegistry { + static CommandRegistry instance() { + return StaticBinder.getInstance(CommandRegistry.class.getClassLoader()).find(CommandRegistry.class); + } + /** * Retrieves the set of commands that are currently hidden from visibility. *

@@ -16,6 +22,7 @@ public interface CommandRegistry { * @return an unmodifiable set of strings representing the commands that are currently hidden */ @Unmodifiable + @Contract(pure = true) Set hiddenCommands(); /** @@ -24,6 +31,7 @@ public interface CommandRegistry { * @return an unmodifiable set of strings representing the commands that are unregistered */ @Unmodifiable + @Contract(pure = true) Set unregisteredCommands(); /** @@ -43,6 +51,7 @@ public interface CommandRegistry { * @param command the name of the command to check * @return true if the command is hidden, false otherwise */ + @Contract(pure = true) boolean isHidden(String command); /** @@ -51,6 +60,7 @@ public interface CommandRegistry { * @param command the name of the command to check * @return true if the command is unregistered, false otherwise */ + @Contract(pure = true) boolean isUnregistered(String command); /** diff --git a/api/src/main/java/net/thenextlvl/commander/Commander.java b/api/src/main/java/net/thenextlvl/commander/Commander.java deleted file mode 100644 index 47a8103..0000000 --- a/api/src/main/java/net/thenextlvl/commander/Commander.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.thenextlvl.commander; - -import core.i18n.file.ComponentBundle; -import org.jspecify.annotations.NullMarked; - -@NullMarked -public interface Commander { - /** - * Retrieves the CommandFinder instance associated with the Commander. - * - * @return the CommandFinder instance - */ - CommandFinder commandFinder(); - - /** - * Retrieves the ComponentBundle associated with the Commander. - * - * @return the ComponentBundle instance - */ - ComponentBundle bundle(); - - /** - * Retrieves the CommandRegistry instance associated with the Commander. - * - * @return the CommandRegistry instance - */ - CommandRegistry commandRegistry(); - - /** - * Get the permission override instance. - * This method returns a PermissionOverride instance, which is used to manipulate permissions for commands. - * - * @return the PermissionOverride instance - */ - PermissionOverride permissionOverride(); -} diff --git a/api/src/main/java/net/thenextlvl/commander/PermissionOverride.java b/api/src/main/java/net/thenextlvl/commander/PermissionOverride.java index 83a265b..ba1f614 100644 --- a/api/src/main/java/net/thenextlvl/commander/PermissionOverride.java +++ b/api/src/main/java/net/thenextlvl/commander/PermissionOverride.java @@ -1,13 +1,19 @@ package net.thenextlvl.commander; +import net.thenextlvl.binder.StaticBinder; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Unmodifiable; -import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import java.util.Map; -@NullMarked +@ApiStatus.NonExtendable public interface PermissionOverride { + static PermissionOverride instance() { + return StaticBinder.getInstance(PermissionOverride.class.getClassLoader()).find(PermissionOverride.class); + } + /** * Retrieves a mapping of commands and their associated original permissions. *

@@ -18,6 +24,7 @@ public interface PermissionOverride { * The permission value may be null if no original permission was assigned. */ @Unmodifiable + @Contract(pure = true) Map originalPermissions(); /** @@ -30,6 +37,7 @@ public interface PermissionOverride { * The permission value may be null if no override is applied to the command. */ @Unmodifiable + @Contract(pure = true) Map overrides(); /** @@ -39,6 +47,7 @@ public interface PermissionOverride { * @return the originally assigned permission as a string, or null if no original permission exists for the command */ @Nullable + @Contract(pure = true) String originalPermission(String command); /** @@ -49,6 +58,7 @@ public interface PermissionOverride { * @return the effective permission as a string, or null if no permission is assigned to the command */ @Nullable + @Contract(pure = true) String permission(String command); /** @@ -58,6 +68,7 @@ public interface PermissionOverride { * @param command the name of the command to check * @return true if the command's permission is overridden, false otherwise */ + @Contract(pure = true) boolean isOverridden(String command); /** diff --git a/build.gradle.kts b/build.gradle.kts index 1813d98..108160e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ group = "net.thenextlvl.commander" -version = "4.4.0" +version = "5.0.0" plugins { id("com.gradleup.shadow") version "9.2.2" apply false diff --git a/commons/build.gradle.kts b/commons/build.gradle.kts new file mode 100644 index 0000000..7050b12 --- /dev/null +++ b/commons/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + id("java") + id("java-library") +} + +group = rootProject.group +version = rootProject.version + +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") + maven("https://repo.thenextlvl.net/releases") +} + +dependencies { + api("net.thenextlvl.core:files:3.0.1") + api("net.thenextlvl.core:i18n:3.2.2") + api(project(":api")) + + compileOnly("com.google.code.gson:gson:2.13.2") + compileOnly("com.mojang:brigadier:1.3.10") + compileOnly("net.kyori:adventure-api:4.26.0-SNAPSHOT") +} diff --git a/commons/src/main/java/net/thenextlvl/commander/CommanderCommons.java b/commons/src/main/java/net/thenextlvl/commander/CommanderCommons.java new file mode 100644 index 0000000..cabbe46 --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/CommanderCommons.java @@ -0,0 +1,70 @@ +package net.thenextlvl.commander; + +import core.i18n.file.ComponentBundle; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import net.thenextlvl.commander.access.BrigadierAccess; +import org.jspecify.annotations.NullMarked; +import org.slf4j.Logger; + +import java.nio.file.Path; +import java.util.Locale; +import java.util.stream.Stream; + +@NullMarked +public abstract class CommanderCommons { + private final Path dataPath; + private final ComponentBundle bundle; + + protected CommanderCommons(Path dataPath) { + this.dataPath = dataPath; + var key = Key.key("commander", "translations"); + var translations = dataPath.resolve("translations"); + this.bundle = ComponentBundle.builder(key, translations) + .placeholder("prefix", "prefix") + .miniMessage(MiniMessage.builder().tags(TagResolver.resolver( + TagResolver.standard(), + Placeholder.parsed("root_command", getRootCommand()) + )).build()) + .resource("commander.properties", Locale.US) + .resource("commander_german.properties", Locale.GERMANY) + .build(); + } + + public ComponentBundle bundle() { + return bundle; + } + + public Path getDataPath() { + return dataPath; + } + + public abstract CommandFinder commandFinder(); + + public abstract CommonCommandRegistry commandRegistry(); + + public abstract CommonPermissionOverride permissionOverride(); + + public abstract Logger logger(); + + public abstract BrigadierAccess brigadierAccess(); + + public abstract String getRootCommand(); + + public abstract Stream getKnownCommands(); + + public abstract Stream getKnownPermissions(); + + public abstract void updateCommands(); + + public abstract void conflictSave(Audience audience); + + public abstract void hiddenConflictSave(Audience audience); + + public abstract void unregisteredConflictSave(Audience audience); + + public abstract void permissionConflictSave(Audience audience); +} diff --git a/commons/src/main/java/net/thenextlvl/commander/CommonCommandFinder.java b/commons/src/main/java/net/thenextlvl/commander/CommonCommandFinder.java new file mode 100644 index 0000000..0f5d10a --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/CommonCommandFinder.java @@ -0,0 +1,36 @@ +package net.thenextlvl.commander; + +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; +import java.util.stream.Stream; + +@NullMarked +public abstract class CommonCommandFinder implements CommandFinder { + @Override + public @Unmodifiable Stream findCommands(Stream commands, Pattern pattern) { + return commands.filter(command -> pattern.matcher(command).matches()); + } + + @Override + public @Unmodifiable Stream findCommands(Stream commands, String input) { + try { + return findCommands(commands, Pattern.compile(input.replace("*", ".*"))); + } catch (PatternSyntaxException e) { + var escaped = Pattern.quote(input).replace("\\*", "*"); + return findCommands(commands, Pattern.compile(escaped.replace("*", ".*"))); + } + } + + @Override + public @Unmodifiable Stream findCommands(String input) { + try { + return findCommands(Pattern.compile(input.replace("*", ".*"))); + } catch (PatternSyntaxException e) { + var escaped = Pattern.quote(input).replace("\\*", "*"); + return findCommands(Pattern.compile(escaped.replace("*", ".*"))); + } + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/CommonCommandRegistry.java b/commons/src/main/java/net/thenextlvl/commander/CommonCommandRegistry.java new file mode 100644 index 0000000..2090df0 --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/CommonCommandRegistry.java @@ -0,0 +1,191 @@ +package net.thenextlvl.commander; + +import com.google.gson.reflect.TypeToken; +import core.file.FileIO; +import core.file.format.GsonFile; +import core.io.IO; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.minimessage.tag.resolver.Formatter; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.thenextlvl.commander.util.FileUtil; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@NullMarked +public abstract class CommonCommandRegistry implements CommandRegistry { + protected final FileIO> hiddenFile; + protected final FileIO> unregisteredFile; + protected final CommanderCommons commons; + + protected String hiddenDigest; + protected String unregisteredDigest; + protected long hiddenLastModified; + protected long unregisteredLastModified; + + protected CommonCommandRegistry(CommanderCommons commons) { + this.hiddenFile = new GsonFile>( + IO.of(commons.getDataPath().resolve("hidden-commands.json")), + new HashSet<>(), new TypeToken<>() { + }).reload().saveIfAbsent(); + this.unregisteredFile = new GsonFile>( + IO.of(commons.getDataPath().resolve("removed-commands.json")), + new HashSet<>(), new TypeToken<>() { + }).reload().saveIfAbsent(); + this.commons = commons; + this.hiddenDigest = FileUtil.digest(hiddenFile); + this.unregisteredDigest = FileUtil.digest(unregisteredFile); + this.hiddenLastModified = FileUtil.lastModified(hiddenFile); + this.unregisteredLastModified = FileUtil.lastModified(unregisteredFile); + } + + @Override + public @Unmodifiable Set hiddenCommands() { + return Set.copyOf(hiddenFile.getRoot()); + } + + @Override + public @Unmodifiable Set unregisteredCommands() { + return Set.copyOf(unregisteredFile.getRoot()); + } + + @Override + public boolean hide(String command) { + var commands = commons.commandFinder().findCommands(command); + if (!Stream.concat(commands, Stream.of(command)) + .map(hiddenFile.getRoot()::add) + .reduce(false, Boolean::logicalOr)) return false; + commons.updateCommands(); + return true; + } + + @Override + public boolean isHidden(String command) { + return hiddenFile.getRoot().contains(command); + } + + @Override + public boolean isUnregistered(String command) { + return unregisteredFile.getRoot().contains(command); + } + + @Override + public boolean register(String command) { + if (!commons.commandFinder().findCommands(unregisteredFile.getRoot().stream(), command) + .filter(unregisteredFile.getRoot()::remove) + .map(this::internalRegister) + .reduce(false, Boolean::logicalOr)) return false; + commons.updateCommands(); + return true; + } + + @Override + public boolean reveal(String command) { + if (!commons.commandFinder().findCommands(hiddenFile.getRoot().stream(), command) + .map(hiddenFile.getRoot()::remove) + .reduce(false, Boolean::logicalOr)) return false; + commons.updateCommands(); + return true; + } + + @Override + public void unregisterCommands() { + unregisteredCommands().stream() + .filter(command -> !command.equals(commons.getRootCommand())) + .forEach(this::internalUnregister); + } + + @Override + public boolean unregister(String command) { + if (!commons.commandFinder().findCommands(command) + .filter(s -> !s.equals(commons.getRootCommand())) + .filter(unregisteredFile.getRoot()::add) + .map(this::internalUnregister) + .reduce(false, Boolean::logicalOr)) return false; + commons.updateCommands(); + return true; + } + + public boolean save(boolean force) { + return saveHidden(force) & saveUnregistered(force); + } + + public boolean saveHidden(boolean force) { + if (!force && FileUtil.hasChanged(hiddenFile, hiddenDigest, hiddenLastModified)) return false; + hiddenFile.save(); + hiddenDigest = FileUtil.digest(hiddenFile); + hiddenLastModified = FileUtil.lastModified(hiddenFile); + return true; + } + + public boolean saveUnregistered(boolean force) { + if (!force && FileUtil.hasChanged(unregisteredFile, unregisteredDigest, unregisteredLastModified)) return false; + unregisteredFile.save(); + unregisteredDigest = FileUtil.digest(unregisteredFile); + unregisteredLastModified = FileUtil.lastModified(unregisteredFile); + return true; + } + + public boolean reload(Audience audience) { + return reloadHidden(audience) | reloadUnregistered(audience); + } + + private boolean reloadHidden(Audience audience) { + var previous = hiddenFile.getRoot(); + var current = hiddenFile.reload(); + hiddenDigest = FileUtil.digest(hiddenFile); + hiddenLastModified = FileUtil.lastModified(hiddenFile); + if (previous.equals(current.getRoot())) return false; + var difference = difference(previous, current.getRoot()); + var additions = difference.entrySet().stream() + .filter(Map.Entry::getValue).count(); + commons.bundle().sendMessage(audience, "command.reload.changes", + Formatter.number("additions", additions), + Formatter.number("deletions", difference.size() - additions), + Placeholder.parsed("file", "hidden-commands.json")); + commons.updateCommands(); + return true; + } + + private boolean reloadUnregistered(Audience audience) { + var previous = unregisteredFile.getRoot(); + var current = unregisteredFile.reload(); + unregisteredDigest = FileUtil.digest(unregisteredFile); + unregisteredLastModified = FileUtil.lastModified(unregisteredFile); + if (previous.equals(current.getRoot())) return false; + var difference = difference(previous, current.getRoot()); + var additions = difference.entrySet().stream() + .filter(Map.Entry::getValue).count(); + difference.forEach((command, added) -> { + if (added) internalUnregister(command); + else internalRegister(command); + }); + commons.bundle().sendMessage(audience, "command.reload.changes", + Formatter.number("additions", additions), + Formatter.number("deletions", difference.size() - additions), + Placeholder.parsed("file", "unregistered-commands.json")); + commons.updateCommands(); + return true; + } + + protected abstract boolean internalRegister(String command); + + protected abstract boolean internalUnregister(String command); + + private Map difference(Set previous, Set current) { + var differences = new HashMap(); + differences.putAll(current.stream() + .filter(s -> !previous.contains(s)) + .collect(Collectors.toMap(s -> s, s -> true))); + differences.putAll(previous.stream() + .filter(s -> !current.contains(s)) + .collect(Collectors.toMap(s -> s, s -> false))); + return differences; + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/CommonPermissionOverride.java b/commons/src/main/java/net/thenextlvl/commander/CommonPermissionOverride.java new file mode 100644 index 0000000..94b395d --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/CommonPermissionOverride.java @@ -0,0 +1,118 @@ +package net.thenextlvl.commander; + +import com.google.gson.reflect.TypeToken; +import core.file.FileIO; +import core.file.format.GsonFile; +import core.io.IO; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.minimessage.tag.resolver.Formatter; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.thenextlvl.commander.util.FileUtil; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +@NullMarked +public abstract class CommonPermissionOverride implements PermissionOverride { + protected final FileIO> overridesFile; + protected final CommanderCommons commons; + + protected String overridesDigest; + protected long overridesLastModified; + + public CommonPermissionOverride(CommanderCommons commons) { + this.overridesFile = new GsonFile>( + IO.of(commons.getDataPath().resolve("permission-overrides.json")), + new HashMap<>(), new TypeToken<>() { + }).reload().saveIfAbsent(); + this.overridesDigest = FileUtil.digest(overridesFile); + this.overridesLastModified = FileUtil.lastModified(overridesFile); + this.commons = commons; + } + + public boolean save(boolean force) { + if (!force && FileUtil.hasChanged(overridesFile, overridesDigest, overridesLastModified)) return false; + overridesFile.save(); + overridesDigest = FileUtil.digest(overridesFile); + overridesLastModified = FileUtil.lastModified(overridesFile); + return true; + } + + public boolean reload(Audience audience) { + var previous = overridesFile.getRoot(); + var current = overridesFile.reload(); + overridesDigest = FileUtil.digest(overridesFile); + overridesLastModified = FileUtil.lastModified(overridesFile); + if (previous.equals(current.getRoot())) return false; + var difference = difference(previous, current.getRoot()); + var additions = difference.entrySet().stream() + .filter(Map.Entry::getValue).count(); + commons.bundle().sendMessage(audience, "command.reload.changes", + Formatter.number("additions", additions), + Formatter.number("deletions", difference.size() - additions), + Placeholder.parsed("file", "permission-overrides.json")); + difference.forEach((command, added) -> { + if (added) override(command.command(), command.permission()); + else reset(command.command()); + }); + return true; + } + + @Override + public @Unmodifiable Map overrides() { + return Map.copyOf(overridesFile.getRoot()); + } + + @Override + public @Nullable String permission(String command) { + return overridesFile.getRoot().get(command); + } + + @Override + public boolean isOverridden(String command) { + return overridesFile.getRoot().containsKey(command); + } + + @Override + public boolean override(String command, @Nullable String permission) { + var commands = commons.commandFinder().findCommands(command); + Stream.concat(commands, Stream.of(command)) + .filter(s -> internalOverride(s, permission)) + .forEach(s -> overridesFile.getRoot().put(s, permission)); + commons.updateCommands(); + return true; + } + + @Override + public boolean reset(String command) { + var commands = commons.commandFinder().findCommands(overridesFile.getRoot().keySet().stream(), command); + var reset = Stream.concat(commands, Stream.of(command)).toList(); + reset.forEach(overridesFile.getRoot()::remove); + if (!reset.stream().map(this::internalReset).reduce(false, Boolean::logicalOr)) return false; + commons.updateCommands(); + return true; + } + + protected abstract boolean internalOverride(String command, @Nullable String permission); + + protected abstract boolean internalReset(String command); + + protected Map difference(Map previous, Map current) { + var differences = new HashMap(); + current.entrySet().stream() + .filter(entry -> !Objects.equals(previous.get(entry.getKey()), entry.getValue())) + .forEach(entry -> differences.put(new PermissionOverride(entry.getKey(), entry.getValue()), true)); + previous.entrySet().stream() + .filter(entry -> !current.containsKey(entry.getKey())) + .forEach(entry -> differences.put(new PermissionOverride(entry.getKey(), entry.getValue()), false)); + return differences; + } + + protected record PermissionOverride(String command, @Nullable String permission) { + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/access/BrigadierAccess.java b/commons/src/main/java/net/thenextlvl/commander/access/BrigadierAccess.java new file mode 100644 index 0000000..41220c0 --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/access/BrigadierAccess.java @@ -0,0 +1,18 @@ +package net.thenextlvl.commander.access; + +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import net.kyori.adventure.audience.Audience; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public abstract class BrigadierAccess { + public abstract LiteralArgumentBuilder literal(String name); + + public abstract RequiredArgumentBuilder argument(String name, ArgumentType type); + + public abstract boolean hasPermission(S source, String permission); + + public abstract Audience audience(S source); +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/CommanderCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/CommanderCommand.java new file mode 100644 index 0000000..820bc18 --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/CommanderCommand.java @@ -0,0 +1,26 @@ +package net.thenextlvl.commander.command; + +import com.mojang.brigadier.tree.LiteralCommandNode; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.command.brigadier.BrigadierCommand; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public final class CommanderCommand extends BrigadierCommand { + private CommanderCommand(CommanderCommons commons) { + super(commons, commons.getRootCommand(), "commander.command"); + } + + public static LiteralCommandNode create(CommanderCommons commons) { + return new CommanderCommand(commons).create() + .then(HideCommand.create(commons)) + .then(PermissionCommand.create(commons)) + .then(RegisterCommand.create(commons)) + .then(ReloadCommand.create(commons)) + .then(ResetCommand.create(commons)) + .then(RevealCommand.create(commons)) + .then(SaveCommand.create(commons)) + .then(UnregisterCommand.create(commons)) + .build(); + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/HideCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/HideCommand.java new file mode 100644 index 0000000..4591b77 --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/HideCommand.java @@ -0,0 +1,41 @@ +package net.thenextlvl.commander.command; + +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.command.brigadier.SimpleCommand; +import org.jspecify.annotations.NullMarked; + +@NullMarked +final class HideCommand extends SimpleCommand { + private HideCommand(CommanderCommons commons) { + super(commons, "hide", "commander.command.hide"); + } + + public static ArgumentBuilder create(CommanderCommons commons) { + var command = new HideCommand(commons); + return command.create().then(commons.brigadierAccess().argument("command", StringArgumentType.string()) + .suggests((context, suggestions) -> { + commons.getKnownCommands() + .filter(s -> !commons.commandRegistry().isUnregistered(s)) + .filter(s -> !commons.commandRegistry().isHidden(s)) + .map(StringArgumentType::escapeIfRequired) + .filter(s -> s.contains(suggestions.getRemaining())) + .forEach(suggestions::suggest); + return suggestions.buildFuture(); + }).executes(command)); + } + + @Override + public int run(CommandContext context) { + var sender = commons.brigadierAccess().audience(context.getSource()); + var command = context.getArgument("command", String.class); + var success = commons.commandRegistry().hide(command); + var message = success ? "command.hidden" : "nothing.changed"; + commons.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); + if (success) commons.hiddenConflictSave(sender); + return success ? SINGLE_SUCCESS : 0; + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/PermissionCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/PermissionCommand.java new file mode 100644 index 0000000..5305cdc --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/PermissionCommand.java @@ -0,0 +1,22 @@ +package net.thenextlvl.commander.command; + +import com.mojang.brigadier.builder.ArgumentBuilder; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.command.brigadier.BrigadierCommand; +import org.jspecify.annotations.NullMarked; + +@NullMarked +final class PermissionCommand extends BrigadierCommand { + private PermissionCommand(CommanderCommons commons) { + super(commons, "permission", "commander.command.permission"); + } + + public static ArgumentBuilder create(CommanderCommons commons) { + var command = new PermissionCommand(commons); + return command.create() + .then(PermissionQueryCommand.create(commons)) + .then(PermissionResetCommand.create(commons)) + .then(PermissionSetCommand.create(commons)) + .then(PermissionUnsetCommand.create(commons)); + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/PermissionQueryCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/PermissionQueryCommand.java new file mode 100644 index 0000000..20923be --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/PermissionQueryCommand.java @@ -0,0 +1,36 @@ +package net.thenextlvl.commander.command; + +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.command.brigadier.SimpleCommand; +import net.thenextlvl.commander.command.suggestion.CommandSuggestionProvider; +import org.jspecify.annotations.NullMarked; + +@NullMarked +final class PermissionQueryCommand extends SimpleCommand { + private PermissionQueryCommand(CommanderCommons commons) { + super(commons, "query", "commander.command.permission.query"); + } + + public static ArgumentBuilder create(CommanderCommons commons) { + var command = new PermissionQueryCommand(commons); + return command.create().then(commons.brigadierAccess().argument("command", StringArgumentType.string()) + .suggests(new CommandSuggestionProvider<>(commons)) + .executes(command)); + } + + @Override + public int run(CommandContext context) { + var sender = commons.brigadierAccess().audience(context.getSource()); + var command = context.getArgument("command", String.class); + var permission = commons.permissionOverride().permission(command); + var message = permission != null ? "permission.query.defined" : "permission.query.undefined"; + commons.bundle().sendMessage(sender, message, + Placeholder.parsed("permission", String.valueOf(permission)), + Placeholder.parsed("command", command)); + return SINGLE_SUCCESS; + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/PermissionResetCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/PermissionResetCommand.java new file mode 100644 index 0000000..f5712b1 --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/PermissionResetCommand.java @@ -0,0 +1,41 @@ +package net.thenextlvl.commander.command; + +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.command.brigadier.SimpleCommand; +import org.jspecify.annotations.NullMarked; + +@NullMarked +final class PermissionResetCommand extends SimpleCommand { + private PermissionResetCommand(CommanderCommons commons) { + super(commons, "reset", "commander.command.permission.reset"); + } + + public static ArgumentBuilder create(CommanderCommons plugin) { + var command = new PermissionResetCommand(plugin); + return command.create().then(plugin.brigadierAccess().argument("command", StringArgumentType.string()) + .suggests((context, suggestions) -> { + plugin.permissionOverride().overrides().keySet().stream() + .filter(s -> !plugin.commandRegistry().isUnregistered(s)) + .map(StringArgumentType::escapeIfRequired) + .filter(s -> s.contains(suggestions.getRemaining())) + .forEach(suggestions::suggest); + return suggestions.buildFuture(); + }) + .executes(command)); + } + + @Override + public int run(CommandContext context) { + var sender = commons.brigadierAccess().audience(context.getSource()); + var command = context.getArgument("command", String.class); + var success = commons.permissionOverride().reset(command); + var message = success ? "permission.reset" : "nothing.changed"; + commons.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); + if (success) commons.permissionConflictSave(sender); + return success ? SINGLE_SUCCESS : 0; + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/PermissionSetCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/PermissionSetCommand.java new file mode 100644 index 0000000..bfbf55d --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/PermissionSetCommand.java @@ -0,0 +1,45 @@ +package net.thenextlvl.commander.command; + +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.command.brigadier.SimpleCommand; +import net.thenextlvl.commander.command.suggestion.CommandSuggestionProvider; +import org.jspecify.annotations.NullMarked; + +@NullMarked +final class PermissionSetCommand extends SimpleCommand { + private PermissionSetCommand(CommanderCommons commons) { + super(commons, "set", "commander.command.permission.set"); + } + + public static ArgumentBuilder create(CommanderCommons commons) { + var command = new PermissionSetCommand(commons); + return command.create().then(commons.brigadierAccess().argument("command", StringArgumentType.string()) + .suggests(new CommandSuggestionProvider<>(commons)) + .then(commons.brigadierAccess().argument("permission", StringArgumentType.string()) + .suggests((context, suggestions) -> { + commons.getKnownPermissions() + .map(StringArgumentType::escapeIfRequired) + .filter(s -> s.contains(suggestions.getRemaining())) + .forEach(suggestions::suggest); + return suggestions.buildFuture(); + }).executes(command))); + } + + @Override + public int run(CommandContext context) { + var sender = commons.brigadierAccess().audience(context.getSource()); + var command = context.getArgument("command", String.class); + var permission = context.getArgument("permission", String.class); + var success = commons.permissionOverride().override(command, permission); + var message = success ? "permission.set" : "nothing.changed"; + commons.bundle().sendMessage(sender, message, + Placeholder.parsed("permission", permission), + Placeholder.parsed("command", command)); + if (success) commons.permissionConflictSave(sender); + return success ? SINGLE_SUCCESS : 0; + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/PermissionUnsetCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/PermissionUnsetCommand.java new file mode 100644 index 0000000..eb1eaaf --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/PermissionUnsetCommand.java @@ -0,0 +1,38 @@ +package net.thenextlvl.commander.command; + +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.command.brigadier.SimpleCommand; +import net.thenextlvl.commander.command.suggestion.CommandSuggestionProvider; +import org.jspecify.annotations.NullMarked; + +@NullMarked +final class PermissionUnsetCommand extends SimpleCommand { + private PermissionUnsetCommand(CommanderCommons commons) { + super(commons, "unset", "commander.command.permission.unset"); + } + + public static ArgumentBuilder create(CommanderCommons commons) { + var command = new PermissionUnsetCommand(commons); + return command.create().then(commons.brigadierAccess().argument("command", StringArgumentType.string()) + .suggests(new CommandSuggestionProvider<>(commons)) + .executes(command)); + } + + @Override + public int run(CommandContext context) throws CommandSyntaxException { + var sender = commons.brigadierAccess().audience(context.getSource()); + var command = context.getArgument("command", String.class); + var success = commons.permissionOverride().override(command, null); + var message = success ? "permission.unset" : "nothing.changed"; + commons.bundle().sendMessage(sender, message, + Placeholder.parsed("permission", "null"), + Placeholder.parsed("command", command)); + if (success) commons.permissionConflictSave(sender); + return success ? SINGLE_SUCCESS : 0; + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/RegisterCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/RegisterCommand.java new file mode 100644 index 0000000..1af84ca --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/RegisterCommand.java @@ -0,0 +1,41 @@ +package net.thenextlvl.commander.command; + +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.command.brigadier.SimpleCommand; +import org.jspecify.annotations.NullMarked; + +@NullMarked +final class RegisterCommand extends SimpleCommand { + private RegisterCommand(CommanderCommons commons) { + super(commons, "register", "commander.command.register"); + } + + public static ArgumentBuilder create(CommanderCommons commons) { + var command = new RegisterCommand(commons); + return command.create() + .then(commons.brigadierAccess().argument("command", StringArgumentType.string()) + .suggests((context, suggestions) -> { + commons.commandRegistry().unregisteredCommands().stream() + .map(StringArgumentType::escapeIfRequired) + .filter(s -> s.contains(suggestions.getRemaining())) + .forEach(suggestions::suggest); + return suggestions.buildFuture(); + }).executes(command)); + } + + @Override + public int run(CommandContext context) throws CommandSyntaxException { + var sender = commons.brigadierAccess().audience(context.getSource()); + var command = context.getArgument("command", String.class); + var success = commons.commandRegistry().register(command); + var message = success ? "command.registered" : "nothing.changed"; + commons.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); + if (success) commons.unregisteredConflictSave(sender); + return success ? SINGLE_SUCCESS : 0; + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/ReloadCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/ReloadCommand.java new file mode 100644 index 0000000..300150c --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/ReloadCommand.java @@ -0,0 +1,36 @@ +package net.thenextlvl.commander.command; + +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.command.brigadier.SimpleCommand; +import org.jspecify.annotations.NullMarked; + +@NullMarked +final class ReloadCommand extends SimpleCommand { + private ReloadCommand(CommanderCommons commons) { + super(commons, "reload", "commander.command.reload"); + } + + public static ArgumentBuilder create(CommanderCommons plugin) { + var command = new ReloadCommand(plugin); + return command.create().executes(command); + } + + @Override + public int run(CommandContext context) throws CommandSyntaxException { + var sender = commons.brigadierAccess().audience(context.getSource()); + try { + var success = commons.commandRegistry().reload(sender) | commons.permissionOverride().reload(sender); + commons.bundle().sendMessage(sender, success ? "command.reload.success" : "nothing.changed"); + return success ? SINGLE_SUCCESS : 0; + } catch (Exception e) { + commons.bundle().sendMessage(sender, "command.reload.failed", + Placeholder.parsed("error", e.getMessage())); + commons.logger().warn("Failed to reload command configurations", e); + return 0; + } + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/ResetCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/ResetCommand.java new file mode 100644 index 0000000..55b0469 --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/ResetCommand.java @@ -0,0 +1,49 @@ +package net.thenextlvl.commander.command; + +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.command.brigadier.SimpleCommand; +import org.jspecify.annotations.NullMarked; + +@NullMarked +final class ResetCommand extends SimpleCommand { + private ResetCommand(CommanderCommons commons) { + super(commons, "reset", "commander.command.reset"); + } + + public static ArgumentBuilder create(CommanderCommons commons) { + var command = new ResetCommand(commons); + return command.create().then(commons.brigadierAccess().argument("command", StringArgumentType.string()) + .suggests((context, suggestions) -> { + commons.commandRegistry().hiddenCommands().stream() + .map(StringArgumentType::escapeIfRequired) + .filter(s -> s.contains(suggestions.getRemaining())) + .forEach(suggestions::suggest); + commons.commandRegistry().unregisteredCommands().stream() + .map(StringArgumentType::escapeIfRequired) + .filter(s -> s.contains(suggestions.getRemaining())) + .forEach(suggestions::suggest); + commons.permissionOverride().overrides().keySet().stream() + .map(StringArgumentType::escapeIfRequired) + .filter(s -> s.contains(suggestions.getRemaining())) + .forEach(suggestions::suggest); + return suggestions.buildFuture(); + }).executes(command)); + } + + @Override + public int run(CommandContext context) { + var sender = commons.brigadierAccess().audience(context.getSource()); + var command = context.getArgument("command", String.class); + var reset = commons.permissionOverride().reset(command) + | commons.commandRegistry().register(command) + | commons.commandRegistry().reveal(command); + var message = reset ? "command.reset" : "nothing.changed"; + commons.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); + if (reset) commons.conflictSave(sender); + return reset ? SINGLE_SUCCESS : 0; + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/RevealCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/RevealCommand.java new file mode 100644 index 0000000..df9f009 --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/RevealCommand.java @@ -0,0 +1,40 @@ +package net.thenextlvl.commander.command; + +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.command.brigadier.SimpleCommand; +import org.jspecify.annotations.NullMarked; + +@NullMarked +final class RevealCommand extends SimpleCommand { + private RevealCommand(CommanderCommons commons) { + super(commons, "reveal", "commander.command.reveal"); + } + + public static ArgumentBuilder create(CommanderCommons commons) { + var command = new RevealCommand(commons); + return command.create().then(commons.brigadierAccess().argument("command", StringArgumentType.string()) + .suggests((context, suggestions) -> { + commons.commandRegistry().hiddenCommands().stream() + .filter(s -> !commons.commandRegistry().isUnregistered(s)) + .map(StringArgumentType::escapeIfRequired) + .filter(s -> s.contains(suggestions.getRemaining())) + .forEach(suggestions::suggest); + return suggestions.buildFuture(); + }).executes(command)); + } + + @Override + public int run(CommandContext context) { + var sender = commons.brigadierAccess().audience(context.getSource()); + var command = context.getArgument("command", String.class); + var success = commons.commandRegistry().reveal(command); + var message = success ? "command.revealed" : "nothing.changed"; + commons.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); + if (success) commons.hiddenConflictSave(sender); + return success ? SINGLE_SUCCESS : 0; + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/SaveCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/SaveCommand.java new file mode 100644 index 0000000..b32257a --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/SaveCommand.java @@ -0,0 +1,29 @@ +package net.thenextlvl.commander.command; + +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.command.brigadier.SimpleCommand; +import org.jspecify.annotations.NullMarked; + +@NullMarked +final class SaveCommand extends SimpleCommand { + private SaveCommand(CommanderCommons commons) { + super(commons, "save", "commander.command.save"); + } + + public static ArgumentBuilder create(CommanderCommons commons) { + var command = new SaveCommand(commons); + return command.create().executes(command); + } + + @Override + public int run(CommandContext context) throws CommandSyntaxException { + var sender = commons.brigadierAccess().audience(context.getSource()); + var saved = commons.commandRegistry().save(true) & commons.permissionOverride().save(true); + var message = saved ? "command.saved" : "command.save.conflict"; + commons.bundle().sendMessage(sender, message); + return saved ? SINGLE_SUCCESS : 0; + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/UnregisterCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/UnregisterCommand.java new file mode 100644 index 0000000..c9102aa --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/UnregisterCommand.java @@ -0,0 +1,36 @@ +package net.thenextlvl.commander.command; + +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.command.brigadier.SimpleCommand; +import net.thenextlvl.commander.command.suggestion.CommandSuggestionProvider; +import org.jspecify.annotations.NullMarked; + +@NullMarked +final class UnregisterCommand extends SimpleCommand { + private UnregisterCommand(CommanderCommons commons) { + super(commons, "unregister", "commander.command.unregister"); + } + + public static ArgumentBuilder create(CommanderCommons commons) { + var command = new UnregisterCommand(commons); + return command.create().then(commons.brigadierAccess().argument("command", StringArgumentType.string()) + .suggests(new CommandSuggestionProvider<>(commons)) + .executes(command)); + } + + @Override + public int run(CommandContext context) throws CommandSyntaxException { + var sender = commons.brigadierAccess().audience(context.getSource()); + var command = context.getArgument("command", String.class); + var success = commons.commandRegistry().unregister(command); + var message = success ? "command.unregistered" : "nothing.changed"; + commons.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); + if (success) commons.unregisteredConflictSave(sender); + return success ? SINGLE_SUCCESS : 0; + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/brigadier/BrigadierCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/brigadier/BrigadierCommand.java new file mode 100644 index 0000000..0269059 --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/brigadier/BrigadierCommand.java @@ -0,0 +1,42 @@ +package net.thenextlvl.commander.command.brigadier; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.thenextlvl.commander.CommanderCommons; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.util.Optional; + +@NullMarked +public abstract class BrigadierCommand { + protected final CommanderCommons commons; + + private final @Nullable String permission; + private final String name; + + protected BrigadierCommand(CommanderCommons commons, String name, @Nullable String permission) { + this.commons = commons; + this.permission = permission; + this.name = name; + } + + protected LiteralArgumentBuilder create() { + return commons.brigadierAccess().literal(name) + .requires(this::canUse); + } + + protected boolean canUse(S source) { + return permission == null || commons.brigadierAccess().hasPermission(source, permission); + } + + protected Optional tryGetArgument(CommandContext context, String name, Class type) { + try { + return Optional.of(context.getArgument(name, type)); + } catch (IllegalArgumentException e) { + if (e.getMessage().equals("No such argument '" + name + "' exists on this command")) + return Optional.empty(); + throw e; + } + } +} diff --git a/commons/src/main/java/net/thenextlvl/commander/command/brigadier/SimpleCommand.java b/commons/src/main/java/net/thenextlvl/commander/command/brigadier/SimpleCommand.java new file mode 100644 index 0000000..798c8d0 --- /dev/null +++ b/commons/src/main/java/net/thenextlvl/commander/command/brigadier/SimpleCommand.java @@ -0,0 +1,13 @@ +package net.thenextlvl.commander.command.brigadier; + +import com.mojang.brigadier.Command; +import net.thenextlvl.commander.CommanderCommons; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked +public abstract class SimpleCommand extends BrigadierCommand implements Command { + protected SimpleCommand(CommanderCommons commons, String name, @Nullable String permission) { + super(commons, name, permission); + } +} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/suggestion/CommandSuggestionProvider.java b/commons/src/main/java/net/thenextlvl/commander/command/suggestion/CommandSuggestionProvider.java similarity index 53% rename from velocity/src/main/java/net/thenextlvl/commander/velocity/command/suggestion/CommandSuggestionProvider.java rename to commons/src/main/java/net/thenextlvl/commander/command/suggestion/CommandSuggestionProvider.java index bbb8b06..73c0240 100644 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/suggestion/CommandSuggestionProvider.java +++ b/commons/src/main/java/net/thenextlvl/commander/command/suggestion/CommandSuggestionProvider.java @@ -1,28 +1,27 @@ -package net.thenextlvl.commander.velocity.command.suggestion; +package net.thenextlvl.commander.command.suggestion; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import com.velocitypowered.api.command.CommandSource; -import net.thenextlvl.commander.velocity.CommanderPlugin; +import net.thenextlvl.commander.CommanderCommons; import org.jspecify.annotations.NullMarked; import java.util.concurrent.CompletableFuture; @NullMarked -public class CommandSuggestionProvider implements SuggestionProvider { - private final CommanderPlugin plugin; +public class CommandSuggestionProvider implements SuggestionProvider { + private final CommanderCommons commons; - public CommandSuggestionProvider(CommanderPlugin plugin) { - this.plugin = plugin; + public CommandSuggestionProvider(CommanderCommons commons) { + this.commons = commons; } @Override - public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) { - plugin.server().getCommandManager().getAliases().stream() - .filter(s -> !plugin.commandRegistry().isUnregistered(s)) + public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) { + commons.getKnownCommands() + .filter(s -> !commons.commandRegistry().isUnregistered(s)) .map(StringArgumentType::escapeIfRequired) .filter(s -> s.contains(builder.getRemaining())) .forEach(builder::suggest); diff --git a/api/src/main/java/net/thenextlvl/commander/util/FileUtil.java b/commons/src/main/java/net/thenextlvl/commander/util/FileUtil.java similarity index 96% rename from api/src/main/java/net/thenextlvl/commander/util/FileUtil.java rename to commons/src/main/java/net/thenextlvl/commander/util/FileUtil.java index ed3c280..09cd1e0 100644 --- a/api/src/main/java/net/thenextlvl/commander/util/FileUtil.java +++ b/commons/src/main/java/net/thenextlvl/commander/util/FileUtil.java @@ -2,7 +2,6 @@ import core.file.FileIO; import core.io.PathIO; -import org.jetbrains.annotations.ApiStatus; import java.io.IOException; import java.nio.file.Files; @@ -11,7 +10,6 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -@ApiStatus.Internal public final class FileUtil { private static final int BUFFER_SIZE = 8192; diff --git a/api/src/main/resources/commander.properties b/commons/src/main/resources/commander.properties similarity index 88% rename from api/src/main/resources/commander.properties rename to commons/src/main/resources/commander.properties index 81e7f63..793f797 100644 --- a/api/src/main/resources/commander.properties +++ b/commons/src/main/resources/commander.properties @@ -1,5 +1,5 @@ prefix=Commander ยป -permission.reset= Reset the permission of to +permission.reset= The permission of has been reset permission.set= Set the permission of to permission.query.defined= The permission of is permission.query.undefined= The permission of is undefined @@ -17,5 +17,4 @@ command.save.conflict= Configuration files have been changed on di \ to reload the configuration files and discard your current changes,\ \ or save'>/ save\ \ to force the current changes to be saved. -command.unknown= The command () does not exist nothing.changed= Nothing could be changed \ No newline at end of file diff --git a/api/src/main/resources/commander_german.properties b/commons/src/main/resources/commander_german.properties similarity index 91% rename from api/src/main/resources/commander_german.properties rename to commons/src/main/resources/commander_german.properties index a833a24..f9ca160 100644 --- a/api/src/main/resources/commander_german.properties +++ b/commons/src/main/resources/commander_german.properties @@ -1,5 +1,5 @@ prefix=Commander ยป -permission.reset= Die Berechtigung auf wurde zurรผckgesetzt zu +permission.reset= Die Berechtigung auf wurde zurรผckgesetzt permission.set= Die Berechtigung auf wurde zu geรคndert permission.query.defined= Die Berechtigung auf ist permission.query.undefined= Die Berechtigung auf ist undefiniert @@ -17,5 +17,4 @@ command.save.conflict= Die Konfigurationsdateien wurden auf der Fe \ um die Konfigurationsdateien neu zu laden und deine aktuellen ร„nderungen zu verwerfen, oder\ \ save'>/ save,\ \ um die aktuellen ร„nderungen zu speichern. -command.unknown= Der Befehl () existiert nicht nothing.changed= Es konnte nichts geรคndert werden \ No newline at end of file diff --git a/paper/build.gradle.kts b/paper/build.gradle.kts index de126b2..9f31cca 100644 --- a/paper/build.gradle.kts +++ b/paper/build.gradle.kts @@ -22,7 +22,6 @@ tasks.compileJava { repositories { mavenCentral() - maven("https://jitpack.io") maven("https://repo.thenextlvl.net/releases") maven("https://repo.papermc.io/repository/maven-public/") } @@ -30,10 +29,8 @@ repositories { dependencies { compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT") - implementation(project(":api")) + implementation(project(":commons")) implementation("org.bstats:bstats-bukkit:3.1.0") - implementation("net.thenextlvl.core:files:3.0.1") - implementation("net.thenextlvl.core:i18n:3.2.2") implementation("net.thenextlvl.core:paper:2.3.1") } @@ -52,6 +49,34 @@ paper { foliaSupported = true website = "https://thenextlvl.net" authors = listOf("NonSwag") + + permissions { + register("commander.admin") { + children = listOf( + "commander.command.hide", + "commander.command.permission.query", + "commander.command.permission.reset", + "commander.command.permission.set", + "commander.command.permission.unset", + "commander.command.register", + "commander.command.reload", + "commander.command.reveal", + "commander.command.save", + "commander.command.unregister", + ) + } + register("commander.command.hide") { children = listOf("commander.command") } + register("commander.command.permission") { children = listOf("commander.command") } + register("commander.command.permission.query") { children = listOf("commander.command.permission") } + register("commander.command.permission.reset") { children = listOf("commander.command.permission") } + register("commander.command.permission.set") { children = listOf("commander.command.permission") } + register("commander.command.permission.unset") { children = listOf("commander.command.permission") } + register("commander.command.register") { children = listOf("commander.command") } + register("commander.command.reload") { children = listOf("commander.command") } + register("commander.command.reveal") { children = listOf("commander.command") } + register("commander.command.save") { children = listOf("commander.command") } + register("commander.command.unregister") { children = listOf("commander.command") } + } } val versionString: String = project.version as String diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/CommanderPlugin.java b/paper/src/main/java/net/thenextlvl/commander/paper/CommanderPlugin.java index cffa783..fbb03f2 100644 --- a/paper/src/main/java/net/thenextlvl/commander/paper/CommanderPlugin.java +++ b/paper/src/main/java/net/thenextlvl/commander/paper/CommanderPlugin.java @@ -1,114 +1,58 @@ package net.thenextlvl.commander.paper; -import core.i18n.file.ComponentBundle; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; -import net.thenextlvl.commander.CommandFinder; -import net.thenextlvl.commander.Commander; -import net.thenextlvl.commander.paper.command.CommanderCommand; -import net.thenextlvl.commander.paper.implementation.PaperCommandFinder; -import net.thenextlvl.commander.paper.implementation.PaperCommandRegistry; -import net.thenextlvl.commander.paper.implementation.PaperPermissionOverride; +import com.mojang.brigadier.CommandDispatcher; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; +import net.thenextlvl.commander.command.CommanderCommand; import net.thenextlvl.commander.paper.listener.CommandListener; import net.thenextlvl.commander.paper.version.CommanderVersionChecker; import org.bstats.bukkit.Metrics; -import org.bukkit.plugin.ServicePriority; import org.bukkit.plugin.java.JavaPlugin; import org.jspecify.annotations.NullMarked; - -import java.nio.file.Path; -import java.util.Locale; +import org.jspecify.annotations.Nullable; @NullMarked -public class CommanderPlugin extends JavaPlugin implements Commander { - public static final String ROOT_COMMAND = "command"; +public class CommanderPlugin extends JavaPlugin { private final Metrics metrics = new Metrics(this, 22782); private final CommanderVersionChecker versionChecker = new CommanderVersionChecker(this); + public final PaperCommander commons = new PaperCommander(this); // todo: weaken visibility - private final Key key = Key.key("commander", "translations"); - private final Path translations = getDataPath().resolve("translations"); - private final ComponentBundle bundle = ComponentBundle.builder(key, translations) - .placeholder("prefix", "prefix") - .miniMessage(MiniMessage.builder().tags(TagResolver.resolver( - TagResolver.standard(), - Placeholder.parsed("root_command", ROOT_COMMAND) - )).build()) - .resource("commander.properties", Locale.US) - .resource("commander_german.properties", Locale.GERMANY) - .build(); + private @Nullable CommandDispatcher commandDispatcher = null; - private final PaperCommandFinder commandFinder = new PaperCommandFinder(this); - private final PaperCommandRegistry commandRegistry = new PaperCommandRegistry(this); - private final PaperPermissionOverride permissionOverride = new PaperPermissionOverride(this); + public CommanderPlugin() { + registerCommands(); + } @Override public void onLoad() { - getServer().getServicesManager().register(Commander.class, this, this, ServicePriority.Highest); versionChecker.checkVersion(); } @Override public void onEnable() { getServer().getGlobalRegionScheduler().execute(this, () -> { - permissionOverride().overridePermissions(); - commandRegistry().unregisterCommands(); + commons.permissionOverride().overridePermissions(); + commons.commandRegistry().unregisterCommands(); }); registerListeners(); - registerCommands(); } @Override public void onDisable() { - commandRegistry.save(true); - permissionOverride.save(true); + commons.commandRegistry().save(true); + commons.permissionOverride().save(true); metrics.shutdown(); } - @Override - public CommandFinder commandFinder() { - return commandFinder; - } - - @Override - public ComponentBundle bundle() { - return bundle; - } - - @Override - public PaperCommandRegistry commandRegistry() { - return commandRegistry; - } - - @Override - public PaperPermissionOverride permissionOverride() { - return permissionOverride; - } - - public void conflictSave(Audience audience) { - if (commandRegistry.save(false) & permissionOverride.save(false)) return; - bundle().sendMessage(audience, "command.save.conflict"); - } - - public void hiddenConflictSave(Audience audience) { - if (commandRegistry.saveHidden(false)) return; - bundle().sendMessage(audience, "command.save.conflict"); - } - - public void unregisteredConflictSave(Audience audience) { - if (commandRegistry.saveUnregistered(false)) return; - bundle().sendMessage(audience, "command.save.conflict"); - } - - public void permissionConflictSave(Audience audience) { - if (permissionOverride.save(false)) return; - bundle().sendMessage(audience, "command.save.conflict"); + public @Nullable CommandDispatcher commandDispatcher() { + return commandDispatcher; } private void registerCommands() { - CommanderCommand.register(this); + getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS.newHandler(event -> { + event.registrar().register(CommanderCommand.create(commons), "The main command to interact with Commander"); + commandDispatcher = event.registrar().getDispatcher(); + })); } private void registerListeners() { diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/PaperCommander.java b/paper/src/main/java/net/thenextlvl/commander/paper/PaperCommander.java new file mode 100644 index 0000000..3121423 --- /dev/null +++ b/paper/src/main/java/net/thenextlvl/commander/paper/PaperCommander.java @@ -0,0 +1,119 @@ +package net.thenextlvl.commander.paper; + +import net.kyori.adventure.audience.Audience; +import net.thenextlvl.binder.StaticBinder; +import net.thenextlvl.commander.CommandFinder; +import net.thenextlvl.commander.CommandRegistry; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.PermissionOverride; +import net.thenextlvl.commander.access.BrigadierAccess; +import net.thenextlvl.commander.paper.access.PaperBrigadierAccess; +import net.thenextlvl.commander.paper.implementation.PaperCommandFinder; +import net.thenextlvl.commander.paper.implementation.PaperCommandRegistry; +import net.thenextlvl.commander.paper.implementation.PaperPermissionOverride; +import org.bukkit.Server; +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.bukkit.permissions.Permission; +import org.jspecify.annotations.NullMarked; +import org.slf4j.Logger; + +import java.util.stream.Stream; + +@NullMarked +public class PaperCommander extends CommanderCommons { + private final PaperBrigadierAccess brigadierAccess = new PaperBrigadierAccess(); + + private final PaperCommandFinder commandFinder; + private final PaperCommandRegistry commandRegistry; + private final PaperPermissionOverride permissionOverride; + + private final CommanderPlugin plugin; + + public PaperCommander(CommanderPlugin plugin) { + super(plugin.getDataPath()); + this.plugin = plugin; + this.commandFinder = new PaperCommandFinder(this); + this.commandRegistry = new PaperCommandRegistry(this); + this.permissionOverride = new PaperPermissionOverride(this); + StaticBinder.getInstance(CommandFinder.class.getClassLoader()).bind(CommandFinder.class, commandFinder); + StaticBinder.getInstance(CommandRegistry.class.getClassLoader()).bind(CommandRegistry.class, commandRegistry); + StaticBinder.getInstance(PermissionOverride.class.getClassLoader()).bind(PermissionOverride.class, permissionOverride); + } + + public CommanderPlugin getPlugin() { + return plugin; + } + + public Server getServer() { + return plugin.getServer(); + } + + @Override + public Logger logger() { + return plugin.getSLF4JLogger(); + } + + @Override + @SuppressWarnings("unchecked") + public BrigadierAccess brigadierAccess() { + return (BrigadierAccess) brigadierAccess; + } + + @Override + public String getRootCommand() { + return "command"; + } + + @Override + public Stream getKnownCommands() { + return plugin.getServer().getCommandMap().getKnownCommands().values().stream() + .map(Command::getLabel); + } + + @Override + public Stream getKnownPermissions() { + return plugin.getServer().getPluginManager().getPermissions().stream() + .map(Permission::getName); + } + + @Override + public void updateCommands() { + plugin.getServer().getOnlinePlayers().forEach(Player::updateCommands); + } + + @Override + public PaperCommandFinder commandFinder() { + return commandFinder; + } + + @Override + public PaperCommandRegistry commandRegistry() { + return commandRegistry; + } + + @Override + public PaperPermissionOverride permissionOverride() { + return permissionOverride; + } + + public void conflictSave(Audience audience) { + if (commandRegistry().save(false) & permissionOverride().save(false)) return; + bundle().sendMessage(audience, "command.save.conflict"); + } + + public void hiddenConflictSave(Audience audience) { + if (commandRegistry().saveHidden(false)) return; + bundle().sendMessage(audience, "command.save.conflict"); + } + + public void unregisteredConflictSave(Audience audience) { + if (commandRegistry().saveUnregistered(false)) return; + bundle().sendMessage(audience, "command.save.conflict"); + } + + public void permissionConflictSave(Audience audience) { + if (permissionOverride().save(false)) return; + bundle().sendMessage(audience, "command.save.conflict"); + } +} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/access/PaperBrigadierAccess.java b/paper/src/main/java/net/thenextlvl/commander/paper/access/PaperBrigadierAccess.java new file mode 100644 index 0000000..988a847 --- /dev/null +++ b/paper/src/main/java/net/thenextlvl/commander/paper/access/PaperBrigadierAccess.java @@ -0,0 +1,33 @@ +package net.thenextlvl.commander.paper.access; + +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.Commands; +import net.kyori.adventure.audience.Audience; +import net.thenextlvl.commander.access.BrigadierAccess; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class PaperBrigadierAccess extends BrigadierAccess { + @Override + public LiteralArgumentBuilder literal(String name) { + return Commands.literal(name); + } + + @Override + public RequiredArgumentBuilder argument(String name, ArgumentType type) { + return Commands.argument(name, type); + } + + @Override + public boolean hasPermission(CommandSourceStack source, String permission) { + return source.getSender().hasPermission(permission); + } + + @Override + public Audience audience(CommandSourceStack source) { + return source.getSender(); + } +} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/CommanderCommand.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/CommanderCommand.java deleted file mode 100644 index 073d125..0000000 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/CommanderCommand.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.thenextlvl.commander.paper.command; - -import io.papermc.paper.command.brigadier.Commands; -import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; -import net.thenextlvl.commander.paper.CommanderPlugin; -import org.jspecify.annotations.NullMarked; - -@NullMarked -public class CommanderCommand { - public static void register(CommanderPlugin plugin) { - var command = Commands.literal(CommanderPlugin.ROOT_COMMAND) - .requires(stack -> stack.getSender().hasPermission("commander.admin")) - .then(HideCommand.create(plugin)) - .then(PermissionCommand.create(plugin)) - .then(RegisterCommand.create(plugin)) - .then(ReloadCommand.create(plugin)) - .then(ResetCommand.create(plugin)) - .then(RevealCommand.create(plugin)) - .then(SaveCommand.create(plugin)) - .then(UnregisterCommand.create(plugin)) - .build(); - plugin.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS.newHandler(event -> - event.registrar().register(command))); - } -} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/HideCommand.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/HideCommand.java deleted file mode 100644 index d8c71fa..0000000 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/HideCommand.java +++ /dev/null @@ -1,44 +0,0 @@ -package net.thenextlvl.commander.paper.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import io.papermc.paper.command.brigadier.Commands; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.paper.CommanderPlugin; -import org.bukkit.entity.Player; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class HideCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return Commands.literal("hide") - .then(Commands.argument("command", StringArgumentType.string()) - .suggests((context, suggestions) -> { - plugin.getServer().getCommandMap().getKnownCommands().values().stream() - .map(org.bukkit.command.Command::getLabel) - .filter(s -> !plugin.commandRegistry().isUnregistered(s)) - .filter(s -> !plugin.commandRegistry().isHidden(s)) - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - return suggestions.buildFuture(); - }) - .executes(context -> hide(context, plugin))); - } - - private static int hide(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource().getSender(); - var command = context.getArgument("command", String.class); - var success = plugin.commandRegistry().hide(command); - var message = success ? "command.hidden" : "nothing.changed"; - plugin.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); - if (success) { - plugin.getServer().getOnlinePlayers().forEach(Player::updateCommands); - plugin.hiddenConflictSave(sender); - } - return Command.SINGLE_SUCCESS; - } -} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionCommand.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionCommand.java deleted file mode 100644 index dad9b5d..0000000 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionCommand.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.thenextlvl.commander.paper.command; - -import com.mojang.brigadier.builder.ArgumentBuilder; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import io.papermc.paper.command.brigadier.Commands; -import net.thenextlvl.commander.paper.CommanderPlugin; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class PermissionCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return Commands.literal("permission") - .then(PermissionQueryCommand.create(plugin)) - .then(PermissionResetCommand.create(plugin)) - .then(PermissionSetCommand.create(plugin)) - .then(PermissionUnsetCommand.create(plugin)); - } -} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionQueryCommand.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionQueryCommand.java deleted file mode 100644 index c03014d..0000000 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionQueryCommand.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.thenextlvl.commander.paper.command; - -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import io.papermc.paper.command.brigadier.Commands; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.paper.CommanderPlugin; -import net.thenextlvl.commander.paper.command.suggestion.CommandSuggestionProvider; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class PermissionQueryCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return Commands.literal("query") - .then(Commands.argument("command", StringArgumentType.string()) - .suggests(new CommandSuggestionProvider(plugin)) - .executes(context -> query(context, plugin))); - } - - private static int query(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource().getSender(); - var command = context.getArgument("command", String.class); - var registered = plugin.getServer().getCommandMap().getKnownCommands().get(command); - var permission = registered != null ? registered.getPermission() : null; - var message = registered == null ? "command.unknown" : permission != null ? - "permission.query.defined" : "permission.query.undefined"; - plugin.bundle().sendMessage(sender, message, - Placeholder.parsed("permission", String.valueOf(permission)), - Placeholder.parsed("command", command)); - return com.mojang.brigadier.Command.SINGLE_SUCCESS; - } -} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionResetCommand.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionResetCommand.java deleted file mode 100644 index 5ffe15e..0000000 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionResetCommand.java +++ /dev/null @@ -1,48 +0,0 @@ -package net.thenextlvl.commander.paper.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import io.papermc.paper.command.brigadier.Commands; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.paper.CommanderPlugin; -import org.bukkit.entity.Player; -import org.jspecify.annotations.NullMarked; - -import java.util.Optional; - -@NullMarked -class PermissionResetCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return Commands.literal("reset") - .then(Commands.argument("command", StringArgumentType.string()) - .suggests((context, suggestions) -> { - plugin.permissionOverride().originalPermissions().keySet().stream() - .filter(s -> !plugin.commandRegistry().isUnregistered(s)) - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - return suggestions.buildFuture(); - }) - .executes(context -> reset(context, plugin))); - } - - private static int reset(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource().getSender(); - var command = context.getArgument("command", String.class); - var success = plugin.permissionOverride().reset(command); - var message = success ? "permission.reset" : "nothing.changed"; - var permission = Optional.ofNullable(plugin.getServer().getCommandMap().getCommand(command)) - .map(org.bukkit.command.Command::getPermission) - .orElse("null"); - plugin.bundle().sendMessage(sender, message, Placeholder.parsed("command", command), - Placeholder.parsed("permission", permission)); - if (success) { - plugin.getServer().getOnlinePlayers().forEach(Player::updateCommands); - plugin.permissionConflictSave(sender); - } - return Command.SINGLE_SUCCESS; - } -} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionSetCommand.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionSetCommand.java deleted file mode 100644 index 1886238..0000000 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionSetCommand.java +++ /dev/null @@ -1,48 +0,0 @@ -package net.thenextlvl.commander.paper.command; - -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import io.papermc.paper.command.brigadier.Commands; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.paper.CommanderPlugin; -import net.thenextlvl.commander.paper.command.suggestion.CommandSuggestionProvider; -import org.bukkit.entity.Player; -import org.bukkit.permissions.Permission; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class PermissionSetCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return Commands.literal("set") - .then(Commands.argument("command", StringArgumentType.string()) - .suggests(new CommandSuggestionProvider(plugin)) - .then(Commands.argument("permission", StringArgumentType.string()) - .suggests((context, suggestions) -> { - plugin.getServer().getPluginManager().getPermissions().stream() - .map(Permission::getName) - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - return suggestions.buildFuture(); - }) - .executes(context -> set(context, plugin)))); - } - - private static int set(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource().getSender(); - var command = context.getArgument("command", String.class); - var permission = context.getArgument("permission", String.class); - var success = plugin.permissionOverride().override(command, permission); - var message = success ? "permission.set" : "nothing.changed"; - plugin.bundle().sendMessage(sender, message, - Placeholder.parsed("permission", permission), - Placeholder.parsed("command", command)); - if (success) { - plugin.getServer().getOnlinePlayers().forEach(Player::updateCommands); - plugin.permissionConflictSave(sender); - } - return com.mojang.brigadier.Command.SINGLE_SUCCESS; - } -} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionUnsetCommand.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionUnsetCommand.java deleted file mode 100644 index ee47b68..0000000 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/PermissionUnsetCommand.java +++ /dev/null @@ -1,37 +0,0 @@ -package net.thenextlvl.commander.paper.command; - -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import io.papermc.paper.command.brigadier.Commands; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.paper.CommanderPlugin; -import net.thenextlvl.commander.paper.command.suggestion.CommandSuggestionProvider; -import org.bukkit.entity.Player; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class PermissionUnsetCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return Commands.literal("unset") - .then(Commands.argument("command", StringArgumentType.string()) - .suggests(new CommandSuggestionProvider(plugin)) - .executes(context -> unset(context, plugin))); - } - - private static int unset(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource().getSender(); - var command = context.getArgument("command", String.class); - var success = plugin.permissionOverride().override(command, null); - var message = success ? "permission.set" : "nothing.changed"; - plugin.bundle().sendMessage(sender, message, - Placeholder.parsed("permission", "null"), - Placeholder.parsed("command", command)); - if (success) { - plugin.getServer().getOnlinePlayers().forEach(Player::updateCommands); - plugin.permissionConflictSave(sender); - } - return com.mojang.brigadier.Command.SINGLE_SUCCESS; - } -} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/RegisterCommand.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/RegisterCommand.java deleted file mode 100644 index 4c89396..0000000 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/RegisterCommand.java +++ /dev/null @@ -1,41 +0,0 @@ -package net.thenextlvl.commander.paper.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import io.papermc.paper.command.brigadier.Commands; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.paper.CommanderPlugin; -import org.bukkit.entity.Player; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class RegisterCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return Commands.literal("register") - .then(Commands.argument("command", StringArgumentType.string()) - .suggests((context, suggestions) -> { - plugin.commandRegistry().unregisteredCommands().stream() - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - return suggestions.buildFuture(); - }) - .executes(context -> register(context, plugin))); - } - - private static int register(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource().getSender(); - var command = context.getArgument("command", String.class); - var success = plugin.commandRegistry().register(command); - var message = success ? "command.registered" : "nothing.changed"; - plugin.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); - if (success) { - plugin.getServer().getOnlinePlayers().forEach(Player::updateCommands); - plugin.unregisteredConflictSave(sender); - } - return Command.SINGLE_SUCCESS; - } -} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/ReloadCommand.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/ReloadCommand.java deleted file mode 100644 index f4aca03..0000000 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/ReloadCommand.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.thenextlvl.commander.paper.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import io.papermc.paper.command.brigadier.Commands; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.paper.CommanderPlugin; -import org.bukkit.entity.Player; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class ReloadCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return Commands.literal("reload").executes(context -> reload(context, plugin)); - } - - private static int reload(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource().getSender(); - try { - var success = plugin.commandRegistry().reload(sender) | plugin.permissionOverride().reload(sender); - if (success && sender instanceof Player player) player.updateCommands(); - plugin.bundle().sendMessage(sender, success ? "command.reload.success" : "nothing.changed"); - return success ? Command.SINGLE_SUCCESS : 0; - } catch (Exception e) { - plugin.bundle().sendMessage(sender, "command.reload.failed", - Placeholder.parsed("error", e.getMessage())); - plugin.getComponentLogger().warn("Failed to reload command configurations", e); - return 0; - } - } -} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/ResetCommand.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/ResetCommand.java deleted file mode 100644 index 73bc19d..0000000 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/ResetCommand.java +++ /dev/null @@ -1,51 +0,0 @@ -package net.thenextlvl.commander.paper.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import io.papermc.paper.command.brigadier.Commands; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.paper.CommanderPlugin; -import org.bukkit.entity.Player; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class ResetCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return Commands.literal("reset") - .then(Commands.argument("command", StringArgumentType.string()) - .suggests((context, suggestions) -> { - plugin.commandRegistry().hiddenCommands().stream() - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - plugin.commandRegistry().unregisteredCommands().stream() - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - plugin.permissionOverride().originalPermissions().keySet().stream() - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - return suggestions.buildFuture(); - }) - .executes(context -> reset(context, plugin))); - } - - private static int reset(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource().getSender(); - var command = context.getArgument("command", String.class); - var reset = plugin.permissionOverride().reset(command) - | plugin.commandRegistry().register(command) - | plugin.commandRegistry().reveal(command); - var message = reset ? "command.reset" : "nothing.changed"; - plugin.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); - if (reset) { - plugin.getServer().getOnlinePlayers().forEach(Player::updateCommands); - plugin.conflictSave(sender); - } - return Command.SINGLE_SUCCESS; - } -} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/RevealCommand.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/RevealCommand.java deleted file mode 100644 index 405c43f..0000000 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/RevealCommand.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.thenextlvl.commander.paper.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import io.papermc.paper.command.brigadier.Commands; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.paper.CommanderPlugin; -import org.bukkit.entity.Player; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class RevealCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return Commands.literal("reveal") - .then(Commands.argument("command", StringArgumentType.string()) - .suggests((context, suggestions) -> { - plugin.commandRegistry().hiddenCommands().stream() - .filter(s -> !plugin.commandRegistry().isUnregistered(s)) - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - return suggestions.buildFuture(); - }) - .executes(context -> reveal(context, plugin))); - } - - private static int reveal(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource().getSender(); - var command = context.getArgument("command", String.class); - var success = plugin.commandRegistry().reveal(command); - var message = success ? "command.revealed" : "nothing.changed"; - plugin.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); - if (success) { - plugin.getServer().getOnlinePlayers().forEach(Player::updateCommands); - plugin.hiddenConflictSave(sender); - } - return Command.SINGLE_SUCCESS; - } -} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/SaveCommand.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/SaveCommand.java deleted file mode 100644 index 8ba850f..0000000 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/SaveCommand.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.thenextlvl.commander.paper.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import io.papermc.paper.command.brigadier.Commands; -import net.thenextlvl.commander.paper.CommanderPlugin; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class SaveCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return Commands.literal("save") - .executes(context -> save(context, plugin)); - } - - private static int save(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource().getSender(); - var saved = plugin.commandRegistry().save(true) & plugin.permissionOverride().save(true); - var message = saved ? "command.saved" : "command.save.conflict"; - plugin.bundle().sendMessage(sender, message); - return Command.SINGLE_SUCCESS; - } -} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/UnregisterCommand.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/UnregisterCommand.java deleted file mode 100644 index 108055b..0000000 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/UnregisterCommand.java +++ /dev/null @@ -1,35 +0,0 @@ -package net.thenextlvl.commander.paper.command; - -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import io.papermc.paper.command.brigadier.Commands; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.paper.CommanderPlugin; -import net.thenextlvl.commander.paper.command.suggestion.CommandSuggestionProvider; -import org.bukkit.entity.Player; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class UnregisterCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return Commands.literal("unregister") - .then(Commands.argument("command", StringArgumentType.string()) - .suggests(new CommandSuggestionProvider(plugin)) - .executes(context -> unregister(context, plugin))); - } - - private static int unregister(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource().getSender(); - var command = context.getArgument("command", String.class); - var success = plugin.commandRegistry().unregister(command); - var message = success ? "command.unregistered" : "nothing.changed"; - plugin.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); - if (success) { - plugin.getServer().getOnlinePlayers().forEach(Player::updateCommands); - plugin.unregisteredConflictSave(sender); - } - return com.mojang.brigadier.Command.SINGLE_SUCCESS; - } -} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/suggestion/CommandSuggestionProvider.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/suggestion/CommandSuggestionProvider.java deleted file mode 100644 index 5bcae2a..0000000 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/suggestion/CommandSuggestionProvider.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.thenextlvl.commander.paper.command.suggestion; - -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.suggestion.SuggestionProvider; -import com.mojang.brigadier.suggestion.Suggestions; -import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import io.papermc.paper.command.brigadier.CommandSourceStack; -import net.thenextlvl.commander.paper.CommanderPlugin; -import org.bukkit.command.Command; -import org.jspecify.annotations.NullMarked; - -import java.util.concurrent.CompletableFuture; - -@NullMarked -public class CommandSuggestionProvider implements SuggestionProvider { - private final CommanderPlugin plugin; - - public CommandSuggestionProvider(CommanderPlugin plugin) { - this.plugin = plugin; - } - - @Override - public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) { - plugin.getServer().getCommandMap().getKnownCommands().values().stream() - .map(Command::getLabel) - .filter(s -> !plugin.commandRegistry().isUnregistered(s)) - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(builder.getRemaining())) - .forEach(builder::suggest); - return builder.buildFuture(); - } -} diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/implementation/PaperCommandFinder.java b/paper/src/main/java/net/thenextlvl/commander/paper/implementation/PaperCommandFinder.java index 2d32458..297e9ff 100644 --- a/paper/src/main/java/net/thenextlvl/commander/paper/implementation/PaperCommandFinder.java +++ b/paper/src/main/java/net/thenextlvl/commander/paper/implementation/PaperCommandFinder.java @@ -1,23 +1,23 @@ package net.thenextlvl.commander.paper.implementation; -import net.thenextlvl.commander.CommandFinder; -import net.thenextlvl.commander.paper.CommanderPlugin; +import net.thenextlvl.commander.CommonCommandFinder; +import net.thenextlvl.commander.paper.PaperCommander; import org.jspecify.annotations.NullMarked; -import java.util.Set; import java.util.regex.Pattern; +import java.util.stream.Stream; @NullMarked -public class PaperCommandFinder implements CommandFinder { - private final CommanderPlugin plugin; +public class PaperCommandFinder extends CommonCommandFinder { + private final PaperCommander commander; - public PaperCommandFinder(CommanderPlugin plugin) { - this.plugin = plugin; + public PaperCommandFinder(PaperCommander commander) { + this.commander = commander; } @Override - public Set findCommands(Pattern pattern) { - return findCommands(plugin.getServer().getCommandMap().getKnownCommands().entrySet() + public Stream findCommands(Pattern pattern) { + return findCommands(commander.getServer().getCommandMap().getKnownCommands().entrySet() .stream().mapMulti((entry, consumer) -> { consumer.accept(entry.getKey()); entry.getValue().getAliases().forEach(consumer); diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/implementation/PaperCommandRegistry.java b/paper/src/main/java/net/thenextlvl/commander/paper/implementation/PaperCommandRegistry.java index 72ef0a3..26fdddf 100644 --- a/paper/src/main/java/net/thenextlvl/commander/paper/implementation/PaperCommandRegistry.java +++ b/paper/src/main/java/net/thenextlvl/commander/paper/implementation/PaperCommandRegistry.java @@ -1,189 +1,32 @@ package net.thenextlvl.commander.paper.implementation; -import com.google.gson.reflect.TypeToken; -import core.file.FileIO; -import core.file.format.GsonFile; -import core.io.IO; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.CommandRegistry; -import net.thenextlvl.commander.paper.CommanderPlugin; -import net.thenextlvl.commander.util.FileUtil; +import net.thenextlvl.commander.CommonCommandRegistry; +import net.thenextlvl.commander.paper.PaperCommander; import org.bukkit.command.Command; import org.jspecify.annotations.NullMarked; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; @NullMarked -public class PaperCommandRegistry implements CommandRegistry { +public class PaperCommandRegistry extends CommonCommandRegistry { private final Map commands = new HashMap<>(); - private final FileIO> hiddenFile; - private final FileIO> unregisteredFile; - private final CommanderPlugin plugin; - private String hiddenDigest; - private String unregisteredDigest; - private long hiddenLastModified; - private long unregisteredLastModified; - - public PaperCommandRegistry(CommanderPlugin plugin) { - this.hiddenFile = new GsonFile>( - IO.of(plugin.getDataFolder(), "hidden-commands.json"), - new HashSet<>(), new TypeToken<>() { - }).reload().saveIfAbsent(); - this.unregisteredFile = new GsonFile>( - IO.of(plugin.getDataFolder(), "removed-commands.json"), - new HashSet<>(), new TypeToken<>() { - }).reload().saveIfAbsent(); - this.plugin = plugin; - this.hiddenDigest = FileUtil.digest(hiddenFile); - this.unregisteredDigest = FileUtil.digest(unregisteredFile); - this.hiddenLastModified = FileUtil.lastModified(hiddenFile); - this.unregisteredLastModified = FileUtil.lastModified(unregisteredFile); - } - - @Override - public Set hiddenCommands() { - return new HashSet<>(hiddenFile.getRoot()); - } - - @Override - public Set unregisteredCommands() { - return new HashSet<>(unregisteredFile.getRoot()); - } - - @Override - public boolean hide(String command) { - return !plugin.commandFinder().findCommands(command).stream() - .filter(hiddenFile.getRoot()::add) - .toList().isEmpty(); - } - - @Override - public boolean isHidden(String command) { - return hiddenFile.getRoot().contains(command); - } - - @Override - public boolean isUnregistered(String command) { - return unregisteredFile.getRoot().contains(command); - } - - @Override - public boolean register(String command) { - return !plugin.commandFinder().findCommands(new HashSet<>(unregisteredFile.getRoot()).stream(), command).stream() - .filter(unregisteredFile.getRoot()::remove) - .filter(this::internalRegister) - .toList().isEmpty(); - } - - @Override - public boolean reveal(String command) { - return !plugin.commandFinder().findCommands(new HashSet<>(hiddenFile.getRoot()).stream(), command).stream() - .filter(hiddenFile.getRoot()::remove) - .toList().isEmpty(); - } - - @Override - public boolean unregister(String command) { - return !plugin.commandFinder().findCommands(command).stream() - .filter(s -> !s.equals("commander:command")) - .filter(unregisteredFile.getRoot()::add) - .filter(this::internalUnregister) - .toList().isEmpty(); - } - - public boolean save(boolean force) { - return saveHidden(force) & saveUnregistered(force); - } - - public boolean saveHidden(boolean force) { - if (!force && FileUtil.hasChanged(hiddenFile, hiddenDigest, hiddenLastModified)) return false; - hiddenFile.save(); - hiddenDigest = FileUtil.digest(hiddenFile); - hiddenLastModified = FileUtil.lastModified(hiddenFile); - return true; - } - - public boolean saveUnregistered(boolean force) { - if (!force && FileUtil.hasChanged(unregisteredFile, unregisteredDigest, unregisteredLastModified)) return false; - unregisteredFile.save(); - unregisteredDigest = FileUtil.digest(unregisteredFile); - unregisteredLastModified = FileUtil.lastModified(unregisteredFile); - return true; + public PaperCommandRegistry(PaperCommander commons) { + super(commons); } @Override - public void unregisterCommands() { - unregisteredCommands().stream() - .filter(command -> !command.equals("commander:command")) - .forEach(this::internalUnregister); - } - - public boolean reload(Audience audience) { - return reloadHidden(audience) | reloadUnregistered(audience); - } - - private boolean reloadUnregistered(Audience audience) { - var previous = unregisteredFile.getRoot(); - var current = unregisteredFile.reload(); - unregisteredDigest = FileUtil.digest(unregisteredFile); - unregisteredLastModified = FileUtil.lastModified(unregisteredFile); - if (previous.equals(current.getRoot())) return false; - var difference = difference(previous, current.getRoot()); - var additions = difference.entrySet().stream() - .filter(Map.Entry::getValue).count(); - plugin.bundle().sendMessage(audience, "command.reload.changes", - Placeholder.parsed("additions", String.valueOf(additions)), - Placeholder.parsed("deletions", String.valueOf(difference.size() - additions)), - Placeholder.parsed("file", "unregistered-commands.json")); - difference.forEach((command, added) -> { - if (added) internalUnregister(command); - else internalRegister(command); - }); - return true; - } - - private boolean reloadHidden(Audience audience) { - var previous = hiddenFile.getRoot(); - var current = hiddenFile.reload(); - hiddenDigest = FileUtil.digest(hiddenFile); - hiddenLastModified = FileUtil.lastModified(hiddenFile); - if (previous.equals(current.getRoot())) return false; - var difference = difference(previous, current.getRoot()); - var additions = difference.entrySet().stream() - .filter(Map.Entry::getValue).count(); - plugin.bundle().sendMessage(audience, "command.reload.changes", - Placeholder.parsed("additions", String.valueOf(additions)), - Placeholder.parsed("deletions", String.valueOf(difference.size() - additions)), - Placeholder.parsed("file", "hidden-commands.json")); - return true; - } - - private Map difference(Set previous, Set current) { - var differences = new HashMap(); - differences.putAll(current.stream() - .filter(s -> !previous.contains(s)) - .collect(Collectors.toMap(s -> s, s -> true))); - differences.putAll(previous.stream() - .filter(s -> !current.contains(s)) - .collect(Collectors.toMap(s -> s, s -> false))); - return differences; - } - - private boolean internalRegister(String command) { + protected boolean internalRegister(String command) { var register = commands.remove(command); if (register == null) return false; - plugin.getServer().getCommandMap().getKnownCommands().put(command, register); + ((PaperCommander) commons).getServer().getCommandMap().getKnownCommands().put(command, register); return true; } - private boolean internalUnregister(String command) { - var registered = plugin.getServer().getCommandMap().getKnownCommands().remove(command); + @Override + protected boolean internalUnregister(String command) { + var registered = ((PaperCommander) commons).getServer().getCommandMap().getKnownCommands().remove(command); if (registered == null) return false; commands.put(command, registered); return true; diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/implementation/PaperPermissionOverride.java b/paper/src/main/java/net/thenextlvl/commander/paper/implementation/PaperPermissionOverride.java index 46df7d5..ed69a80 100644 --- a/paper/src/main/java/net/thenextlvl/commander/paper/implementation/PaperPermissionOverride.java +++ b/paper/src/main/java/net/thenextlvl/commander/paper/implementation/PaperPermissionOverride.java @@ -1,50 +1,26 @@ package net.thenextlvl.commander.paper.implementation; -import com.google.gson.reflect.TypeToken; -import core.file.FileIO; -import core.file.format.GsonFile; -import core.io.IO; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.PermissionOverride; -import net.thenextlvl.commander.paper.CommanderPlugin; -import net.thenextlvl.commander.util.FileUtil; +import net.thenextlvl.commander.CommonPermissionOverride; +import net.thenextlvl.commander.paper.PaperCommander; import org.jetbrains.annotations.Unmodifiable; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Objects; @NullMarked -public class PaperPermissionOverride implements PermissionOverride { +public class PaperPermissionOverride extends CommonPermissionOverride { private final Map originalPermissions = new HashMap<>(); - private final FileIO> overridesFile; - private final CommanderPlugin plugin; - private String overridesDigest; - private long overridesLastModified; - - public PaperPermissionOverride(CommanderPlugin plugin) { - this.overridesFile = new GsonFile>( - IO.of(plugin.getDataFolder(), "permission-overrides.json"), - new HashMap<>(), new TypeToken<>() { - }).reload().saveIfAbsent(); - this.plugin = plugin; - this.overridesDigest = FileUtil.digest(overridesFile); - this.overridesLastModified = FileUtil.lastModified(overridesFile); + public PaperPermissionOverride(PaperCommander commander) { + super(commander); } @Override public @Unmodifiable Map originalPermissions() { - return new HashMap<>(originalPermissions); - } - - @Override - public @Unmodifiable Map overrides() { - return new HashMap<>(overridesFile.getRoot()); + return Map.copyOf(originalPermissions); } @Override @@ -52,100 +28,42 @@ public PaperPermissionOverride(CommanderPlugin plugin) { return originalPermissions.get(command); } - @Override - public @Nullable String permission(String command) { - return overridesFile.getRoot().get(command); - } - - @Override - public boolean isOverridden(String command) { - return overridesFile.getRoot().containsKey(command); - } - - @Override - public boolean override(String command, @Nullable String permission) { - var commands = plugin.commandFinder().findCommands(command).stream() - .filter(s -> internalOverride(s, permission)) - .toList(); - commands.forEach(s -> overridesFile.getRoot().put(s, permission)); - return !commands.isEmpty(); - } - - @Override - public boolean reset(String command) { - var commands = plugin.commandFinder().findCommands(new HashSet<>(overridesFile.getRoot().keySet()).stream(), command); - commands.forEach(overridesFile.getRoot()::remove); - return !commands.stream() - .filter(this::internalReset) - .toList().isEmpty(); - } - - public void save() { - save(true); - } - - public boolean save(boolean force) { - if (!force && FileUtil.hasChanged(overridesFile, overridesDigest, overridesLastModified)) return false; - overridesFile.save(); - overridesDigest = FileUtil.digest(overridesFile); - overridesLastModified = FileUtil.lastModified(overridesFile); - return true; - } - @Override public void overridePermissions() { overridesFile.getRoot().forEach(this::internalOverride); } - public boolean reload(Audience audience) { - var previous = overridesFile.getRoot(); - var current = overridesFile.reload(); - overridesDigest = FileUtil.digest(overridesFile); - overridesLastModified = FileUtil.lastModified(overridesFile); - if (previous.equals(current.getRoot())) return false; - var difference = difference(previous, current.getRoot()); - var additions = difference.entrySet().stream() - .filter(Map.Entry::getValue).count(); - plugin.bundle().sendMessage(audience, "command.reload.changes", - Placeholder.parsed("additions", String.valueOf(additions)), - Placeholder.parsed("deletions", String.valueOf(difference.size() - additions)), - Placeholder.parsed("file", "permission-overrides.json")); - difference.forEach((command, added) -> { - if (added) override(command.command(), command.permission()); - else reset(command.command()); - }); - return true; - } + @Override + protected boolean internalOverride(String command, @Nullable String permission) { + var plugin = ((PaperCommander) commons).getPlugin(); + var registered = plugin.getServer().getCommandMap().getKnownCommands().get(command); - private Map difference(Map previous, Map current) { - var differences = new HashMap(); - current.entrySet().stream() - .filter(entry -> !Objects.equals(previous.get(entry.getKey()), entry.getValue())) - .forEach(entry -> differences.put(new PermissionOverride(entry.getKey(), entry.getValue()), true)); - previous.entrySet().stream() - .filter(entry -> !current.containsKey(entry.getKey())) - .forEach(entry -> differences.put(new PermissionOverride(entry.getKey(), entry.getValue()), false)); - return differences; - } + var registeredPermission = registered != null ? registered.getPermission() : null; + if (Objects.equals(registeredPermission, permission)) return false; + originalPermissions.putIfAbsent(command, registeredPermission); - private record PermissionOverride(String command, @Nullable String permission) { - } + var dispatcher = plugin.commandDispatcher(); + var child = dispatcher != null ? dispatcher.getRoot().getChild(command) : null; + if (child != null) return true; - private boolean internalOverride(String command, @Nullable String permission) { - var registered = plugin.getServer().getCommandMap().getKnownCommands().get(command); if (registered == null) return false; - var registeredPermission = registered.getPermission(); - if (Objects.equals(registeredPermission, permission)) return false; - originalPermissions.putIfAbsent(command, registeredPermission); registered.setPermission(permission); - return true; + return Objects.equals(registered.getPermission(), permission); } - private boolean internalReset(String command) { + @Override + protected boolean internalReset(String command) { + var plugin = ((PaperCommander) commons).getPlugin(); var registered = plugin.getServer().getCommandMap().getKnownCommands().get(command); - if (registered == null) return false; + if (!originalPermissions.containsKey(command)) return false; var permission = originalPermissions.remove(command); + + var dispatcher = plugin.commandDispatcher(); + var child = dispatcher != null ? dispatcher.getRoot().getChild(command) : null; + if (child != null) return true; + + if (registered == null) return false; if (Objects.equals(registered.getPermission(), permission)) return false; registered.setPermission(permission); return Objects.equals(registered.getPermission(), permission); diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/listener/CommandListener.java b/paper/src/main/java/net/thenextlvl/commander/paper/listener/CommandListener.java index 1080c2b..cf9e00d 100644 --- a/paper/src/main/java/net/thenextlvl/commander/paper/listener/CommandListener.java +++ b/paper/src/main/java/net/thenextlvl/commander/paper/listener/CommandListener.java @@ -1,10 +1,15 @@ package net.thenextlvl.commander.paper.listener; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.util.TriState; import net.thenextlvl.commander.paper.CommanderPlugin; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerCommandSendEvent; import org.jspecify.annotations.NullMarked; @@ -16,9 +21,27 @@ public CommandListener(CommanderPlugin plugin) { this.plugin = plugin; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGHEST) public void onCommandSend(PlayerCommandSendEvent event) { if (event.getPlayer().permissionValue("commander.bypass").equals(TriState.TRUE)) return; - event.getCommands().removeAll(plugin.commandRegistry().hiddenCommands()); + event.getCommands().removeAll(plugin.commons.commandRegistry().hiddenCommands()); + event.getCommands().removeIf(command -> { + var permission = plugin.commons.permissionOverride().permission(command); + return permission != null && !event.getPlayer().hasPermission(permission); + }); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onCommandPreprocess(PlayerCommandPreprocessEvent event) { + var noSlash = event.getMessage().substring(1); + var command = noSlash.split(" ", 2)[0]; + var permission = plugin.commons.permissionOverride().permission(command); + if (permission == null || event.getPlayer().hasPermission(permission)) return; + event.getPlayer().sendMessage(Component.translatable("command.unknown.command").appendNewline() + .append(Component.text().append(Component.text(noSlash).decorate(TextDecoration.UNDERLINED)) + .append(Component.translatable("command.context.here").decorate(TextDecoration.ITALIC)) + .clickEvent(ClickEvent.suggestCommand(event.getMessage()))) + .color(NamedTextColor.RED)); + event.setCancelled(true); } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 1651e9c..6fc9453 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,5 +4,6 @@ plugins { rootProject.name = "commander" include("api") +include("commons") include("paper") -include("velocity") +include("velocity") \ No newline at end of file diff --git a/velocity/build.gradle.kts b/velocity/build.gradle.kts index d386380..80b8b57 100644 --- a/velocity/build.gradle.kts +++ b/velocity/build.gradle.kts @@ -27,10 +27,8 @@ repositories { dependencies { compileOnly("com.velocitypowered:velocity-api:3.4.0-SNAPSHOT") - implementation(project(":api")) + implementation(project(":commons")) implementation("org.bstats:bstats-velocity:3.1.0") - implementation("net.thenextlvl.core:files:3.0.1") - implementation("net.thenextlvl.core:i18n:3.2.2") implementation("net.thenextlvl.core:version-checker:2.1.2") { exclude("com.google.code.gson") } diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/CommanderPlugin.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/CommanderPlugin.java index d9d3ca8..309efab 100644 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/CommanderPlugin.java +++ b/velocity/src/main/java/net/thenextlvl/commander/velocity/CommanderPlugin.java @@ -1,24 +1,15 @@ package net.thenextlvl.commander.velocity; import com.google.inject.Inject; +import com.velocitypowered.api.command.BrigadierCommand; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.proxy.ProxyServer; -import core.i18n.file.ComponentBundle; import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; -import net.thenextlvl.commander.CommandFinder; -import net.thenextlvl.commander.Commander; -import net.thenextlvl.commander.velocity.command.CommanderCommand; -import net.thenextlvl.commander.velocity.implementation.ProxyCommandFinder; -import net.thenextlvl.commander.velocity.implementation.ProxyCommandRegistry; -import net.thenextlvl.commander.velocity.implementation.ProxyPermissionOverride; +import net.thenextlvl.commander.command.CommanderCommand; import net.thenextlvl.commander.velocity.listener.CommandListener; import net.thenextlvl.commander.velocity.version.CommanderVersionChecker; import org.bstats.velocity.Metrics; @@ -26,45 +17,27 @@ import org.slf4j.Logger; import java.nio.file.Path; -import java.util.Locale; @NullMarked @Plugin(id = "commander", name = "Commander", authors = "NonSwag", url = "https://thenextlvl.net", - version = "4.4.0") -public class CommanderPlugin implements Commander { - public static final String ROOT_COMMAND = "commandv"; - private final ComponentBundle bundle; - private final ProxyCommandFinder commandFinder; - private final ProxyCommandRegistry commandRegistry; - private final ProxyPermissionOverride permissionOverride; + version = "5.0.0") +public class CommanderPlugin { private final Metrics.Factory metricsFactory; private final ProxyServer server; private final Logger logger; - private final Path dataFolder; + public final ProxyCommander commons; // todo: weaken visibility + private final Path dataPath; @Inject - public CommanderPlugin(ProxyServer server, Logger logger, @DataDirectory Path dataFolder, Metrics.Factory metricsFactory) { + public CommanderPlugin(ProxyServer server, Logger logger, @DataDirectory Path dataPath, Metrics.Factory metricsFactory) { this.server = server; this.logger = logger; - this.dataFolder = dataFolder; + this.dataPath = dataPath; this.metricsFactory = metricsFactory; - var key = Key.key("commander", "translations"); - var translations = dataFolder.resolve("translations"); - this.bundle = ComponentBundle.builder(key, translations) - .placeholder("prefix", "prefix") - .miniMessage(MiniMessage.builder().tags(TagResolver.resolver( - TagResolver.standard(), - Placeholder.parsed("root_command", ROOT_COMMAND) - )).build()) - .resource("commander.properties", Locale.US) - .resource("commander_german.properties", Locale.GERMANY) - .build(); - this.commandFinder = new ProxyCommandFinder(this); - this.commandRegistry = new ProxyCommandRegistry(this); - this.permissionOverride = new ProxyPermissionOverride(this); + this.commons = new ProxyCommander(this); new CommanderVersionChecker(this).checkVersion(); } @@ -72,55 +45,35 @@ public CommanderPlugin(ProxyServer server, Logger logger, @DataDirectory Path da public void onProxyInitialize(ProxyInitializeEvent event) { metricsFactory.make(this, 22782); server().getEventManager().register(this, new CommandListener(this)); - var meta = server.getCommandManager().metaBuilder(ROOT_COMMAND).plugin(this).build(); - server().getCommandManager().register(meta, CommanderCommand.create(this)); - commandRegistry().unregisterCommands(); + var meta = server.getCommandManager().metaBuilder(commons.getRootCommand()).plugin(this).build(); + server().getCommandManager().register(meta, new BrigadierCommand(CommanderCommand.create(commons))); + commons.commandRegistry().unregisterCommands(); } @Subscribe(priority = 999) public void onProxyShutdown(ProxyShutdownEvent event) { - commandRegistry.save(true); - permissionOverride.save(true); - } - - @Override - public CommandFinder commandFinder() { - return commandFinder; - } - - @Override - public ComponentBundle bundle() { - return bundle; - } - - @Override - public ProxyCommandRegistry commandRegistry() { - return commandRegistry; - } - - @Override - public ProxyPermissionOverride permissionOverride() { - return permissionOverride; + commons.commandRegistry().save(true); + commons.permissionOverride().save(true); } public void conflictSave(Audience audience) { - if (commandRegistry.save(false) & permissionOverride.save(false)) return; - bundle().sendMessage(audience, "command.save.conflict"); + if (commons.commandRegistry().save(false) & commons.permissionOverride().save(false)) return; + commons.bundle().sendMessage(audience, "command.save.conflict"); } public void hiddenConflictSave(Audience audience) { - if (commandRegistry.saveHidden(false)) return; - bundle().sendMessage(audience, "command.save.conflict"); + if (commons.commandRegistry().saveHidden(false)) return; + commons.bundle().sendMessage(audience, "command.save.conflict"); } public void unregisteredConflictSave(Audience audience) { - if (commandRegistry.saveUnregistered(false)) return; - bundle().sendMessage(audience, "command.save.conflict"); + if (commons.commandRegistry().saveUnregistered(false)) return; + commons.bundle().sendMessage(audience, "command.save.conflict"); } public void permissionConflictSave(Audience audience) { - if (permissionOverride.save(false)) return; - bundle().sendMessage(audience, "command.save.conflict"); + if (commons.permissionOverride().save(false)) return; + commons.bundle().sendMessage(audience, "command.save.conflict"); } public ProxyServer server() { @@ -131,7 +84,7 @@ public Logger logger() { return logger; } - public Path dataFolder() { - return dataFolder; + public Path dataPath() { + return dataPath; } } diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/ProxyCommander.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/ProxyCommander.java new file mode 100644 index 0000000..cf03723 --- /dev/null +++ b/velocity/src/main/java/net/thenextlvl/commander/velocity/ProxyCommander.java @@ -0,0 +1,113 @@ +package net.thenextlvl.commander.velocity; + +import com.velocitypowered.api.proxy.ProxyServer; +import net.kyori.adventure.audience.Audience; +import net.thenextlvl.binder.StaticBinder; +import net.thenextlvl.commander.CommandFinder; +import net.thenextlvl.commander.CommandRegistry; +import net.thenextlvl.commander.CommanderCommons; +import net.thenextlvl.commander.PermissionOverride; +import net.thenextlvl.commander.access.BrigadierAccess; +import net.thenextlvl.commander.velocity.access.ProxyBrigadierAccess; +import net.thenextlvl.commander.velocity.implementation.ProxyCommandFinder; +import net.thenextlvl.commander.velocity.implementation.ProxyCommandRegistry; +import net.thenextlvl.commander.velocity.implementation.ProxyPermissionOverride; +import org.jspecify.annotations.NullMarked; +import org.slf4j.Logger; + +import java.util.stream.Stream; + +@NullMarked +public class ProxyCommander extends CommanderCommons { + private final ProxyBrigadierAccess brigadierAccess = new ProxyBrigadierAccess(); + + private final ProxyCommandFinder commandFinder; + private final ProxyCommandRegistry commandRegistry; + private final ProxyPermissionOverride permissionOverride; + + private final CommanderPlugin plugin; + + public ProxyCommander(CommanderPlugin plugin) { + super(plugin.dataPath()); + this.plugin = plugin; + this.commandFinder = new ProxyCommandFinder(this); + this.commandRegistry = new ProxyCommandRegistry(this); + this.permissionOverride = new ProxyPermissionOverride(this); + StaticBinder.getInstance(CommandFinder.class.getClassLoader()).bind(CommandFinder.class, commandFinder); + StaticBinder.getInstance(CommandRegistry.class.getClassLoader()).bind(CommandRegistry.class, commandRegistry); + StaticBinder.getInstance(PermissionOverride.class.getClassLoader()).bind(PermissionOverride.class, permissionOverride); + } + + public ProxyServer server() { + return plugin.server(); + } + + @Override + public Logger logger() { + return plugin.logger(); + } + + @Override + @SuppressWarnings("unchecked") + public BrigadierAccess brigadierAccess() { + return (BrigadierAccess) brigadierAccess; + } + + @Override + public String getRootCommand() { + return "commandv"; + } + + @Override + public Stream getKnownCommands() { + return Stream.empty(); + } + + @Override + public Stream getKnownPermissions() { + return Stream.empty(); + } + + @Override + public void updateCommands() { + } + + @Override + public void conflictSave(Audience audience) { + if (commandRegistry().save(false) & permissionOverride().save(false)) return; + bundle().sendMessage(audience, "command.save.conflict"); + } + + @Override + public void hiddenConflictSave(Audience audience) { + if (commandRegistry().saveHidden(false)) return; + bundle().sendMessage(audience, "command.save.conflict"); + } + + @Override + public void unregisteredConflictSave(Audience audience) { + if (commandRegistry().saveUnregistered(false)) return; + bundle().sendMessage(audience, "command.save.conflict"); + } + + @Override + public void permissionConflictSave(Audience audience) { + if (permissionOverride().save(false)) return; + bundle().sendMessage(audience, "command.save.conflict"); + } + + @Override + public ProxyCommandFinder commandFinder() { + return commandFinder; + } + + @Override + public ProxyCommandRegistry commandRegistry() { + return commandRegistry; + } + + @Override + public ProxyPermissionOverride permissionOverride() { + return permissionOverride; + } +} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/access/ProxyBrigadierAccess.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/access/ProxyBrigadierAccess.java new file mode 100644 index 0000000..54f6192 --- /dev/null +++ b/velocity/src/main/java/net/thenextlvl/commander/velocity/access/ProxyBrigadierAccess.java @@ -0,0 +1,32 @@ +package net.thenextlvl.commander.velocity.access; + +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.velocitypowered.api.command.CommandSource; +import net.kyori.adventure.audience.Audience; +import net.thenextlvl.commander.access.BrigadierAccess; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public final class ProxyBrigadierAccess extends BrigadierAccess { + @Override + public LiteralArgumentBuilder literal(String name) { + return LiteralArgumentBuilder.literal(name); + } + + @Override + public RequiredArgumentBuilder argument(String name, ArgumentType type) { + return RequiredArgumentBuilder.argument(name, type); + } + + @Override + public boolean hasPermission(CommandSource source, String permission) { + return source.hasPermission(permission); + } + + @Override + public Audience audience(CommandSource source) { + return source; + } +} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/CommanderCommand.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/command/CommanderCommand.java deleted file mode 100644 index a1905c9..0000000 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/CommanderCommand.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.thenextlvl.commander.velocity.command; - -import com.velocitypowered.api.command.BrigadierCommand; -import net.thenextlvl.commander.velocity.CommanderPlugin; -import org.jspecify.annotations.NullMarked; - -@NullMarked -public class CommanderCommand { - public static BrigadierCommand create(CommanderPlugin plugin) { - var command = BrigadierCommand.literalArgumentBuilder("commandv") - .requires(source -> source.hasPermission("commander.admin")) - .then(HideCommand.create(plugin)) - .then(PermissionCommand.create(plugin)) - .then(RegisterCommand.create(plugin)) - .then(ReloadCommand.create(plugin)) - .then(ResetCommand.create(plugin)) - .then(RevealCommand.create(plugin)) - .then(SaveCommand.create(plugin)) - .then(UnregisterCommand.create(plugin)) - .build(); - return new BrigadierCommand(command); - } -} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/HideCommand.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/command/HideCommand.java deleted file mode 100644 index bfc34cd..0000000 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/HideCommand.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.thenextlvl.commander.velocity.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import com.velocitypowered.api.command.BrigadierCommand; -import com.velocitypowered.api.command.CommandSource; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.velocity.CommanderPlugin; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class HideCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return BrigadierCommand.literalArgumentBuilder("hide") - .then(BrigadierCommand.requiredArgumentBuilder("command", StringArgumentType.string()) - .suggests((context, suggestions) -> { - plugin.server().getCommandManager().getAliases().stream() - .filter(s -> !plugin.commandRegistry().isUnregistered(s)) - .filter(s -> !plugin.commandRegistry().isHidden(s)) - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - return suggestions.buildFuture(); - }) - .executes(context -> hide(context, plugin))); - } - - private static int hide(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource(); - var command = context.getArgument("command", String.class); - var success = plugin.commandRegistry().hide(command); - var message = success ? "command.hidden" : "nothing.changed"; - plugin.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); - if (success) plugin.hiddenConflictSave(sender); - return Command.SINGLE_SUCCESS; - } -} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/PermissionCommand.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/command/PermissionCommand.java deleted file mode 100644 index ec83bb4..0000000 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/PermissionCommand.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.thenextlvl.commander.velocity.command; - -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.velocitypowered.api.command.BrigadierCommand; -import com.velocitypowered.api.command.CommandSource; -import net.thenextlvl.commander.velocity.CommanderPlugin; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class PermissionCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return BrigadierCommand.literalArgumentBuilder("permission") - .then(PermissionQueryCommand.create(plugin)) - .then(PermissionResetCommand.create(plugin)) - .then(PermissionSetCommand.create(plugin)); - } -} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/PermissionQueryCommand.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/command/PermissionQueryCommand.java deleted file mode 100644 index b7ee536..0000000 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/PermissionQueryCommand.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.thenextlvl.commander.velocity.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import com.velocitypowered.api.command.BrigadierCommand; -import com.velocitypowered.api.command.CommandSource; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.velocity.CommanderPlugin; -import net.thenextlvl.commander.velocity.command.suggestion.CommandSuggestionProvider; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class PermissionQueryCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return BrigadierCommand.literalArgumentBuilder("query") - .then(BrigadierCommand.requiredArgumentBuilder("command", StringArgumentType.string()) - .suggests(new CommandSuggestionProvider(plugin)) - .executes(context -> query(context, plugin))); - } - - private static int query(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource(); - var command = context.getArgument("command", String.class); - var permission = plugin.permissionOverride().permission(command); - var message = permission != null ? "permission.query.defined" : "permission.query.undefined"; - plugin.bundle().sendMessage(sender, message, - Placeholder.parsed("permission", String.valueOf(permission)), - Placeholder.parsed("command", command)); - return Command.SINGLE_SUCCESS; - } -} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/PermissionResetCommand.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/command/PermissionResetCommand.java deleted file mode 100644 index 502a09a..0000000 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/PermissionResetCommand.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.thenextlvl.commander.velocity.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import com.velocitypowered.api.command.BrigadierCommand; -import com.velocitypowered.api.command.CommandSource; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.velocity.CommanderPlugin; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class PermissionResetCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return BrigadierCommand.literalArgumentBuilder("reset") - .then(BrigadierCommand.requiredArgumentBuilder("command", StringArgumentType.string()) - .suggests((context, suggestions) -> { - plugin.permissionOverride().overrides().keySet().stream() - .filter(s -> !plugin.commandRegistry().isUnregistered(s)) - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - return suggestions.buildFuture(); - }) - .executes(context -> reset(context, plugin))); - } - - private static int reset(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource(); - var command = context.getArgument("command", String.class); - var success = plugin.permissionOverride().reset(command); - var message = success ? "permission.reset" : "nothing.changed"; - plugin.bundle().sendMessage(sender, message, Placeholder.parsed("command", command), - Placeholder.parsed("permission", "null")); - if (success) plugin.permissionConflictSave(sender); - return Command.SINGLE_SUCCESS; - } -} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/PermissionSetCommand.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/command/PermissionSetCommand.java deleted file mode 100644 index cfdce83..0000000 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/PermissionSetCommand.java +++ /dev/null @@ -1,46 +0,0 @@ -package net.thenextlvl.commander.velocity.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import com.velocitypowered.api.command.BrigadierCommand; -import com.velocitypowered.api.command.CommandSource; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.velocity.CommanderPlugin; -import net.thenextlvl.commander.velocity.command.suggestion.CommandSuggestionProvider; -import org.jspecify.annotations.NullMarked; - -import java.util.Objects; - -@NullMarked -class PermissionSetCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return BrigadierCommand.literalArgumentBuilder("set") - .then(BrigadierCommand.requiredArgumentBuilder("command", StringArgumentType.string()) - .suggests(new CommandSuggestionProvider(plugin)) - .then(BrigadierCommand.requiredArgumentBuilder("permission", StringArgumentType.string()) - .suggests((context, suggestions) -> { - plugin.permissionOverride().overrides().values().stream() - .filter(Objects::nonNull) - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - return suggestions.buildFuture(); - }) - .executes(context -> set(context, plugin)))); - } - - private static int set(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource(); - var command = context.getArgument("command", String.class); - var permission = context.getArgument("permission", String.class); - var success = plugin.permissionOverride().override(command, permission); - var message = success ? "permission.set" : "nothing.changed"; - plugin.bundle().sendMessage(sender, message, - Placeholder.parsed("permission", permission), - Placeholder.parsed("command", command)); - if (success) plugin.permissionConflictSave(sender); - return Command.SINGLE_SUCCESS; - } -} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/RegisterCommand.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/command/RegisterCommand.java deleted file mode 100644 index 71ff14a..0000000 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/RegisterCommand.java +++ /dev/null @@ -1,37 +0,0 @@ -package net.thenextlvl.commander.velocity.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import com.velocitypowered.api.command.BrigadierCommand; -import com.velocitypowered.api.command.CommandSource; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.velocity.CommanderPlugin; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class RegisterCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return BrigadierCommand.literalArgumentBuilder("register") - .then(BrigadierCommand.requiredArgumentBuilder("command", StringArgumentType.string()) - .suggests((context, suggestions) -> { - plugin.commandRegistry().unregisteredCommands().stream() - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - return suggestions.buildFuture(); - }) - .executes(context -> register(context, plugin))); - } - - private static int register(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource(); - var command = context.getArgument("command", String.class); - var success = plugin.commandRegistry().register(command); - var message = success ? "command.registered" : "nothing.changed"; - plugin.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); - if (success) plugin.unregisteredConflictSave(sender); - return Command.SINGLE_SUCCESS; - } -} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/ReloadCommand.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/command/ReloadCommand.java deleted file mode 100644 index 0d37f59..0000000 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/ReloadCommand.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.thenextlvl.commander.velocity.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import com.velocitypowered.api.command.BrigadierCommand; -import com.velocitypowered.api.command.CommandSource; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.velocity.CommanderPlugin; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class ReloadCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return BrigadierCommand.literalArgumentBuilder("reload") - .executes(context -> reload(context, plugin)); - } - - private static int reload(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource(); - try { - var success = plugin.commandRegistry().reload(sender) | plugin.permissionOverride().reload(sender); - plugin.bundle().sendMessage(sender, success ? "command.reload.success" : "nothing.changed"); - return success ? Command.SINGLE_SUCCESS : 0; - } catch (Exception e) { - plugin.bundle().sendMessage(sender, "command.reload.failed", - Placeholder.parsed("error", e.getMessage())); - plugin.logger().warn("Failed to reload command configurations", e); - return 0; - } - } -} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/ResetCommand.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/command/ResetCommand.java deleted file mode 100644 index 0339383..0000000 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/ResetCommand.java +++ /dev/null @@ -1,47 +0,0 @@ -package net.thenextlvl.commander.velocity.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import com.velocitypowered.api.command.BrigadierCommand; -import com.velocitypowered.api.command.CommandSource; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.velocity.CommanderPlugin; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class ResetCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return BrigadierCommand.literalArgumentBuilder("reset") - .then(BrigadierCommand.requiredArgumentBuilder("command", StringArgumentType.string()) - .suggests((context, suggestions) -> { - plugin.commandRegistry().hiddenCommands().stream() - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - plugin.commandRegistry().unregisteredCommands().stream() - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - plugin.permissionOverride().overrides().keySet().stream() - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - return suggestions.buildFuture(); - }) - .executes(context -> reset(context, plugin))); - } - - private static int reset(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource(); - var command = context.getArgument("command", String.class); - var reset = plugin.permissionOverride().reset(command) - | plugin.commandRegistry().register(command) - | plugin.commandRegistry().reveal(command); - var message = reset ? "command.reset" : "nothing.changed"; - plugin.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); - if (reset) plugin.conflictSave(sender); - return Command.SINGLE_SUCCESS; - } -} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/RevealCommand.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/command/RevealCommand.java deleted file mode 100644 index d291c08..0000000 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/RevealCommand.java +++ /dev/null @@ -1,38 +0,0 @@ -package net.thenextlvl.commander.velocity.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import com.velocitypowered.api.command.BrigadierCommand; -import com.velocitypowered.api.command.CommandSource; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.velocity.CommanderPlugin; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class RevealCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return BrigadierCommand.literalArgumentBuilder("reveal") - .then(BrigadierCommand.requiredArgumentBuilder("command", StringArgumentType.string()) - .suggests((context, suggestions) -> { - plugin.commandRegistry().hiddenCommands().stream() - .filter(s -> !plugin.commandRegistry().isUnregistered(s)) - .map(StringArgumentType::escapeIfRequired) - .filter(s -> s.contains(suggestions.getRemaining())) - .forEach(suggestions::suggest); - return suggestions.buildFuture(); - }) - .executes(context -> reveal(context, plugin))); - } - - private static int reveal(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource(); - var command = context.getArgument("command", String.class); - var success = plugin.commandRegistry().reveal(command); - var message = success ? "command.revealed" : "nothing.changed"; - plugin.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); - if (success) plugin.hiddenConflictSave(sender); - return Command.SINGLE_SUCCESS; - } -} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/SaveCommand.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/command/SaveCommand.java deleted file mode 100644 index e917b3b..0000000 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/SaveCommand.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.thenextlvl.commander.velocity.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import com.velocitypowered.api.command.BrigadierCommand; -import com.velocitypowered.api.command.CommandSource; -import net.thenextlvl.commander.velocity.CommanderPlugin; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class SaveCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return BrigadierCommand.literalArgumentBuilder("save") - .executes(context -> save(context, plugin)); - } - - private static int save(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource(); - var saved = plugin.commandRegistry().save(true) & plugin.permissionOverride().save(true); - var message = saved ? "command.saved" : "command.save.conflict"; - plugin.bundle().sendMessage(sender, message); - return Command.SINGLE_SUCCESS; - } -} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/UnregisterCommand.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/command/UnregisterCommand.java deleted file mode 100644 index 2919854..0000000 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/command/UnregisterCommand.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.thenextlvl.commander.velocity.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import com.velocitypowered.api.command.BrigadierCommand; -import com.velocitypowered.api.command.CommandSource; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.velocity.CommanderPlugin; -import net.thenextlvl.commander.velocity.command.suggestion.CommandSuggestionProvider; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class UnregisterCommand { - public static ArgumentBuilder create(CommanderPlugin plugin) { - return BrigadierCommand.literalArgumentBuilder("unregister") - .then(BrigadierCommand.requiredArgumentBuilder("command", StringArgumentType.string()) - .suggests(new CommandSuggestionProvider(plugin)) - .executes(context -> unregister(context, plugin))); - } - - private static int unregister(CommandContext context, CommanderPlugin plugin) { - var sender = context.getSource(); - var command = context.getArgument("command", String.class); - var success = plugin.commandRegistry().unregister(command); - var message = success ? "command.unregistered" : "nothing.changed"; - plugin.bundle().sendMessage(sender, message, Placeholder.parsed("command", command)); - if (success) plugin.unregisteredConflictSave(sender); - return Command.SINGLE_SUCCESS; - } -} diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/implementation/ProxyCommandFinder.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/implementation/ProxyCommandFinder.java index ed8fc51..d9e77ae 100644 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/implementation/ProxyCommandFinder.java +++ b/velocity/src/main/java/net/thenextlvl/commander/velocity/implementation/ProxyCommandFinder.java @@ -1,22 +1,22 @@ package net.thenextlvl.commander.velocity.implementation; -import net.thenextlvl.commander.CommandFinder; -import net.thenextlvl.commander.velocity.CommanderPlugin; +import net.thenextlvl.commander.CommonCommandFinder; +import net.thenextlvl.commander.velocity.ProxyCommander; import org.jspecify.annotations.NullMarked; -import java.util.Set; import java.util.regex.Pattern; +import java.util.stream.Stream; @NullMarked -public class ProxyCommandFinder implements CommandFinder { - private final CommanderPlugin plugin; +public class ProxyCommandFinder extends CommonCommandFinder { + private final ProxyCommander commander; - public ProxyCommandFinder(CommanderPlugin plugin) { - this.plugin = plugin; + public ProxyCommandFinder(ProxyCommander commander) { + this.commander = commander; } @Override - public Set findCommands(Pattern pattern) { - return findCommands(plugin.server().getCommandManager().getAliases().stream(), pattern); + public Stream findCommands(Pattern pattern) { + return findCommands(commander.server().getCommandManager().getAliases().stream(), pattern); } } diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/implementation/ProxyCommandRegistry.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/implementation/ProxyCommandRegistry.java index 6729f90..19be1c2 100644 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/implementation/ProxyCommandRegistry.java +++ b/velocity/src/main/java/net/thenextlvl/commander/velocity/implementation/ProxyCommandRegistry.java @@ -1,179 +1,23 @@ package net.thenextlvl.commander.velocity.implementation; -import com.google.gson.reflect.TypeToken; -import core.file.FileIO; -import core.file.format.GsonFile; -import core.io.IO; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.CommandRegistry; -import net.thenextlvl.commander.util.FileUtil; -import net.thenextlvl.commander.velocity.CommanderPlugin; +import net.thenextlvl.commander.CommonCommandRegistry; +import net.thenextlvl.commander.velocity.ProxyCommander; import org.jspecify.annotations.NullMarked; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - @NullMarked -public class ProxyCommandRegistry implements CommandRegistry { - private final FileIO> hiddenFile; - private final FileIO> unregisteredFile; - private final CommanderPlugin plugin; - - private String hiddenDigest; - private String unregisteredDigest; - private long hiddenLastModified; - private long unregisteredLastModified; - - public ProxyCommandRegistry(CommanderPlugin plugin) { - this.hiddenFile = new GsonFile>( - IO.of(plugin.dataFolder().toFile(), "hidden-commands.json"), - new HashSet<>(), new TypeToken<>() { - }).reload().saveIfAbsent(); - this.unregisteredFile = new GsonFile>( - IO.of(plugin.dataFolder().toFile(), "removed-commands.json"), - new HashSet<>(), new TypeToken<>() { - }).reload().saveIfAbsent(); - this.plugin = plugin; - this.hiddenDigest = FileUtil.digest(hiddenFile); - this.unregisteredDigest = FileUtil.digest(unregisteredFile); - this.hiddenLastModified = FileUtil.lastModified(hiddenFile); - this.unregisteredLastModified = FileUtil.lastModified(unregisteredFile); - } - - @Override - public Set hiddenCommands() { - return new HashSet<>(hiddenFile.getRoot()); - } - - @Override - public Set unregisteredCommands() { - return new HashSet<>(unregisteredFile.getRoot()); - } - - @Override - public boolean hide(String command) { - return !plugin.commandFinder().findCommands(command).stream() - .filter(hiddenFile.getRoot()::add) - .toList().isEmpty(); - } - - @Override - public boolean isHidden(String command) { - return hiddenFile.getRoot().contains(command); +public class ProxyCommandRegistry extends CommonCommandRegistry { + public ProxyCommandRegistry(ProxyCommander commander) { + super(commander); } @Override - public boolean isUnregistered(String command) { - return unregisteredFile.getRoot().contains(command); - } - - @Override - public boolean register(String command) { - return !plugin.commandFinder().findCommands(new HashSet<>(unregisteredFile.getRoot()).stream(), command).stream() - .filter(unregisteredFile.getRoot()::remove) - .toList().isEmpty(); - } - - @Override - public boolean reveal(String command) { - return !plugin.commandFinder().findCommands(new HashSet<>(hiddenFile.getRoot()).stream(), command).stream() - .filter(hiddenFile.getRoot()::remove) - .toList().isEmpty(); - } - - @Override - public boolean unregister(String command) { - return !plugin.commandFinder().findCommands(command).stream() - .filter(s -> !s.equals("commandv")) - .filter(plugin.server().getCommandManager()::hasCommand) - .filter(unregisteredFile.getRoot()::add) - .filter(this::internalUnregister) - .toList().isEmpty(); - } - - public boolean save(boolean force) { - return saveHidden(force) & saveUnregistered(force); - } - - public boolean saveHidden(boolean force) { - if (!force && FileUtil.hasChanged(hiddenFile, hiddenDigest, hiddenLastModified)) return false; - hiddenFile.save(); - hiddenDigest = FileUtil.digest(hiddenFile); - hiddenLastModified = FileUtil.lastModified(hiddenFile); - return true; - } - - public boolean saveUnregistered(boolean force) { - if (!force && FileUtil.hasChanged(unregisteredFile, unregisteredDigest, unregisteredLastModified)) return false; - unregisteredFile.save(); - unregisteredDigest = FileUtil.digest(unregisteredFile); - unregisteredLastModified = FileUtil.lastModified(unregisteredFile); + protected boolean internalRegister(String command) { return true; } @Override - public void unregisterCommands() { - unregisteredCommands().forEach(this::internalUnregister); - } - - public boolean reload(Audience audience) { - var hidden = reloadHidden(audience); - var unregistered = reloadUnregistered(audience); - return hidden || unregistered; - } - - private boolean reloadUnregistered(Audience audience) { - var previous = unregisteredFile.getRoot(); - var current = unregisteredFile.reload(); - unregisteredDigest = FileUtil.digest(unregisteredFile); - unregisteredLastModified = FileUtil.lastModified(unregisteredFile); - if (previous.equals(current.getRoot())) return false; - var difference = difference(previous, current.getRoot()); - var additions = difference.entrySet().stream() - .filter(Map.Entry::getValue).count(); - plugin.bundle().sendMessage(audience, "command.reload.changes", - Placeholder.parsed("additions", String.valueOf(additions)), - Placeholder.parsed("deletions", String.valueOf(difference.size() - additions)), - Placeholder.parsed("file", "unregistered-commands.json")); - difference.forEach((command, added) -> { - if (added) internalUnregister(command); - }); - return true; - } - - private boolean reloadHidden(Audience audience) { - var previous = hiddenFile.getRoot(); - var current = hiddenFile.reload(); - hiddenDigest = FileUtil.digest(hiddenFile); - hiddenLastModified = FileUtil.lastModified(hiddenFile); - if (previous.equals(current.getRoot())) return false; - var difference = difference(previous, current.getRoot()); - var additions = difference.entrySet().stream() - .filter(Map.Entry::getValue).count(); - plugin.bundle().sendMessage(audience, "command.reload.changes", - Placeholder.parsed("additions", String.valueOf(additions)), - Placeholder.parsed("deletions", String.valueOf(difference.size() - additions)), - Placeholder.parsed("file", "hidden-commands.json")); - return true; - } - - private Map difference(Set previous, Set current) { - var differences = new HashMap(); - differences.putAll(current.stream() - .filter(s -> !previous.contains(s)) - .collect(Collectors.toMap(s -> s, s -> true))); - differences.putAll(previous.stream() - .filter(s -> !current.contains(s)) - .collect(Collectors.toMap(s -> s, s -> false))); - return differences; - } - - private boolean internalUnregister(String command) { - plugin.server().getCommandManager().unregister(command); + protected boolean internalUnregister(String command) { + ((ProxyCommander) commons).server().getCommandManager().unregister(command); return true; } } diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/implementation/ProxyPermissionOverride.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/implementation/ProxyPermissionOverride.java index 3fa4120..66356a8 100644 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/implementation/ProxyPermissionOverride.java +++ b/velocity/src/main/java/net/thenextlvl/commander/velocity/implementation/ProxyPermissionOverride.java @@ -1,130 +1,40 @@ package net.thenextlvl.commander.velocity.implementation; -import com.google.gson.reflect.TypeToken; -import core.file.FileIO; -import core.file.format.GsonFile; -import core.io.IO; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.thenextlvl.commander.PermissionOverride; -import net.thenextlvl.commander.util.FileUtil; -import net.thenextlvl.commander.velocity.CommanderPlugin; +import net.thenextlvl.commander.CommonPermissionOverride; +import net.thenextlvl.commander.velocity.ProxyCommander; import org.jetbrains.annotations.Unmodifiable; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Objects; @NullMarked -public class ProxyPermissionOverride implements PermissionOverride { - private final FileIO> overridesFile; - private final CommanderPlugin plugin; - - private String overridesDigest; - private long overridesLastModified; - - public ProxyPermissionOverride(CommanderPlugin plugin) { - this.overridesFile = new GsonFile>( - IO.of(plugin.dataFolder().toFile(), "permission-overrides.json"), - new HashMap<>(), new TypeToken<>() { - }).reload().saveIfAbsent(); - this.plugin = plugin; - this.overridesDigest = FileUtil.digest(overridesFile); - this.overridesLastModified = FileUtil.lastModified(overridesFile); - } - - @Override - @Deprecated - public @Unmodifiable Map originalPermissions() throws UnsupportedOperationException { - throw new UnsupportedOperationException(); +public class ProxyPermissionOverride extends CommonPermissionOverride { + public ProxyPermissionOverride(ProxyCommander commander) { + super(commander); } @Override - public @Unmodifiable Map overrides() { - return new HashMap<>(overridesFile.getRoot()); + public @Unmodifiable Map originalPermissions() { + return Map.of(); } @Override - @Deprecated - public @Nullable String originalPermission(String command) throws UnsupportedOperationException { - throw new UnsupportedOperationException(); + public @Nullable String originalPermission(String command) { + return null; } @Override - public @Nullable String permission(String command) { - return overridesFile.getRoot().get(command); + public void overridePermissions() { } @Override - public boolean isOverridden(String command) { - return overridesFile.getRoot().containsKey(command); - } - - @Override - public boolean override(String command, @Nullable String permission) { - return !plugin.commandFinder().findCommands(command).stream() - .filter(s -> !Objects.equals(overridesFile.getRoot().put(s, permission), permission)) - .toList().isEmpty(); - } - - @Override - public boolean reset(String command) { - var overridden = new HashSet<>(overridesFile.getRoot().keySet()).stream(); - var commands = plugin.commandFinder().findCommands(overridden, command); - return !commands.stream() - .filter(this::isOverridden) - .map(overridesFile.getRoot()::remove) - .toList().isEmpty(); - } - - public boolean save(boolean force) { - if (!force && FileUtil.hasChanged(overridesFile, overridesDigest, overridesLastModified)) return false; - overridesFile.save(); - overridesDigest = FileUtil.digest(overridesFile); - overridesLastModified = FileUtil.lastModified(overridesFile); + protected boolean internalOverride(String command, @Nullable String permission) { return true; } @Override - @Deprecated - public void overridePermissions() throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - public boolean reload(Audience audience) { - var previous = overridesFile.getRoot(); - var current = overridesFile.reload(); - overridesDigest = FileUtil.digest(overridesFile); - overridesLastModified = FileUtil.lastModified(overridesFile); - if (previous.equals(current.getRoot())) return false; - var difference = difference(previous, current.getRoot()); - var additions = difference.entrySet().stream() - .filter(Map.Entry::getValue).count(); - plugin.bundle().sendMessage(audience, "command.reload.changes", - Placeholder.parsed("additions", String.valueOf(additions)), - Placeholder.parsed("deletions", String.valueOf(difference.size() - additions)), - Placeholder.parsed("file", "permission-overrides.json")); - difference.forEach((command, added) -> { - if (added) override(command.command(), command.permission()); - else reset(command.command()); - }); + protected boolean internalReset(String command) { return true; } - - private Map difference(Map previous, Map current) { - var differences = new HashMap(); - current.entrySet().stream() - .filter(entry -> !Objects.equals(previous.get(entry.getKey()), entry.getValue())) - .forEach(entry -> differences.put(new PermissionOverride(entry.getKey(), entry.getValue()), true)); - previous.entrySet().stream() - .filter(entry -> !current.containsKey(entry.getKey())) - .forEach(entry -> differences.put(new PermissionOverride(entry.getKey(), entry.getValue()), false)); - return differences; - } - - private record PermissionOverride(String command, @Nullable String permission) { - } } \ No newline at end of file diff --git a/velocity/src/main/java/net/thenextlvl/commander/velocity/listener/CommandListener.java b/velocity/src/main/java/net/thenextlvl/commander/velocity/listener/CommandListener.java index 724651e..5bcbbec 100644 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/listener/CommandListener.java +++ b/velocity/src/main/java/net/thenextlvl/commander/velocity/listener/CommandListener.java @@ -4,6 +4,11 @@ import com.velocitypowered.api.event.command.CommandExecuteEvent; import com.velocitypowered.api.event.command.PlayerAvailableCommandsEvent; import com.velocitypowered.api.permission.Tristate; +import com.velocitypowered.api.proxy.ConsoleCommandSource; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; import net.thenextlvl.commander.velocity.CommanderPlugin; import org.jspecify.annotations.NullMarked; @@ -19,8 +24,8 @@ public CommandListener(CommanderPlugin commander) { public void onCommandSend(PlayerAvailableCommandsEvent event) { if (event.getPlayer().getPermissionValue("commander.bypass").equals(Tristate.TRUE)) return; event.getRootNode().getChildren().removeIf(commandNode -> { - if (commander.commandRegistry().isHidden(commandNode.getName())) return true; - var permission = commander.permissionOverride().permission(commandNode.getName()); + if (commander.commons.commandRegistry().isHidden(commandNode.getName())) return true; + var permission = commander.commons.permissionOverride().permission(commandNode.getName()); return permission != null && !event.getPlayer().hasPermission(permission); }); } @@ -28,9 +33,16 @@ public void onCommandSend(PlayerAvailableCommandsEvent event) { @Subscribe(priority = -1) public void onPlayerChat(CommandExecuteEvent event) { if (!event.getResult().isAllowed()) return; - var command = event.getCommand().replaceFirst("/", "").stripLeading(); - var permission = commander.permissionOverride().permission(command); + var noSlash = event.getCommand().replaceFirst("/", ""); + var command = noSlash.split(" ", 2)[0]; + if (event.getCommandSource() instanceof ConsoleCommandSource) return; + var permission = commander.commons.permissionOverride().permission(command); if (permission == null || event.getCommandSource().hasPermission(permission)) return; - event.setResult(CommandExecuteEvent.CommandResult.forwardToServer()); + event.getCommandSource().sendMessage(Component.translatable("command.unknown.command").appendNewline() + .append(Component.text().append(Component.text(noSlash).decorate(TextDecoration.UNDERLINED)) + .append(Component.translatable("command.context.here").decorate(TextDecoration.ITALIC)) + .clickEvent(ClickEvent.suggestCommand(event.getCommand()))) + .color(NamedTextColor.RED)); + event.setResult(CommandExecuteEvent.CommandResult.denied()); } }