diff --git a/api/src/main/java/net/thenextlvl/commander/CommandFinder.java b/api/src/main/java/net/thenextlvl/commander/CommandFinder.java new file mode 100644 index 0000000..c37382d --- /dev/null +++ b/api/src/main/java/net/thenextlvl/commander/CommandFinder.java @@ -0,0 +1,26 @@ +package net.thenextlvl.commander; + +import java.util.Set; +import java.util.stream.Stream; + +/** + * The CommandFinder interface defines methods for finding commands based on a given input. + */ +public interface CommandFinder { + /** + * Finds commands based on the given input. + * + * @param input The input used to search for commands. + * @return A set of strings representing the found commands. + */ + Set findCommands(String input); + + /** + * 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 A set of strings representing the found commands. + */ + Set findCommands(Stream commands, String input); +} diff --git a/api/src/main/java/net/thenextlvl/commander/Commander.java b/api/src/main/java/net/thenextlvl/commander/Commander.java index 2fd42bb..a13edcd 100644 --- a/api/src/main/java/net/thenextlvl/commander/Commander.java +++ b/api/src/main/java/net/thenextlvl/commander/Commander.java @@ -5,6 +5,12 @@ @MethodsReturnNotNullByDefault public interface Commander { + /** + * Retrieves the CommandFinder instance associated with the Commander. + * + * @return the CommandFinder instance + */ + CommandFinder commandFinder(); /** * Retrieves the ComponentBundle associated with the Commander. 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 25b9eb7..e3f3a56 100644 --- a/paper/src/main/java/net/thenextlvl/commander/paper/CommanderPlugin.java +++ b/paper/src/main/java/net/thenextlvl/commander/paper/CommanderPlugin.java @@ -8,6 +8,7 @@ import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; 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 net.thenextlvl.commander.paper.listener.CommandListener; @@ -38,6 +39,7 @@ public class CommanderPlugin extends JavaPlugin implements Commander { Placeholder.component("prefix", bundle.component(Locale.US, "prefix")) )).build()); + private final PaperCommandFinder commandFinder = new PaperCommandFinder(this); private final PaperCommandRegistry commandRegistry = new PaperCommandRegistry(this); private final PaperPermissionOverride permissionOverride = new PaperPermissionOverride(this); diff --git a/paper/src/main/java/net/thenextlvl/commander/paper/command/CommandSuggestionProvider.java b/paper/src/main/java/net/thenextlvl/commander/paper/command/CommandSuggestionProvider.java index bcc1359..92a7264 100644 --- a/paper/src/main/java/net/thenextlvl/commander/paper/command/CommandSuggestionProvider.java +++ b/paper/src/main/java/net/thenextlvl/commander/paper/command/CommandSuggestionProvider.java @@ -20,12 +20,12 @@ class CommandSuggestionProvider implements SuggestionProvider getSuggestions(CommandContext context, SuggestionsBuilder builder) { - Bukkit.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); + Bukkit.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 new file mode 100644 index 0000000..7f6990a --- /dev/null +++ b/paper/src/main/java/net/thenextlvl/commander/paper/implementation/PaperCommandFinder.java @@ -0,0 +1,30 @@ +package net.thenextlvl.commander.paper.implementation; + +import lombok.RequiredArgsConstructor; +import net.thenextlvl.commander.CommandFinder; +import net.thenextlvl.commander.paper.CommanderPlugin; + +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@RequiredArgsConstructor +public class PaperCommandFinder implements CommandFinder { + private final CommanderPlugin plugin; + + public Set findCommands(String input) { + return findCommands(plugin.getServer().getCommandMap().getKnownCommands().entrySet() + .stream().mapMulti((entry, consumer) -> { + consumer.accept(entry.getKey()); + entry.getValue().getAliases().forEach(consumer); + }), input); + } + + public Set findCommands(Stream commands, String input) { + var pattern = Pattern.compile(input.replace("*", ".*")); + return commands.filter(command -> + pattern.matcher(command).matches() + ).collect(Collectors.toSet()); + } +} 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 5bab6c8..e593113 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 @@ -5,8 +5,8 @@ import core.file.format.GsonFile; import core.io.IO; import lombok.Getter; -import net.thenextlvl.commander.paper.CommanderPlugin; import net.thenextlvl.commander.CommandRegistry; +import net.thenextlvl.commander.paper.CommanderPlugin; import org.bukkit.Bukkit; import org.bukkit.command.Command; @@ -20,32 +20,35 @@ public class PaperCommandRegistry implements CommandRegistry { private final Map commands = new HashMap<>(); private final FileIO> hiddenFile; private final FileIO> unregisteredFile; + private final CommanderPlugin plugin; public PaperCommandRegistry(CommanderPlugin plugin) { this.hiddenFile = new GsonFile>( IO.of(plugin.getDataFolder(), "hidden-commands.json"), new HashSet<>(), new TypeToken<>() { - }).saveIfAbsent(); + }).reload().saveIfAbsent(); this.unregisteredFile = new GsonFile>( IO.of(plugin.getDataFolder(), "removed-commands.json"), new HashSet<>(), new TypeToken<>() { - }).saveIfAbsent(); + }).reload().saveIfAbsent(); + this.plugin = plugin; } @Override public Set hiddenCommands() { - return Set.copyOf(hiddenFile.getRoot()); + return new HashSet<>(hiddenFile.getRoot()); } @Override public Set unregisteredCommands() { - return Set.copyOf(unregisteredFile.getRoot()); + return new HashSet<>(unregisteredFile.getRoot()); } @Override public boolean hide(String command) { - return Bukkit.getCommandMap().getKnownCommands().containsKey(command) - && hiddenFile.getRoot().add(command); + return !plugin.commandFinder().findCommands(command).stream() + .filter(hiddenFile.getRoot()::add) + .toList().isEmpty(); } @Override @@ -60,22 +63,33 @@ public boolean isUnregistered(String command) { @Override public boolean register(String command) { - return unregisteredFile.getRoot().remove(command) && internalRegister(command); + return !plugin.commandFinder().findCommands(new HashSet<>(commands.keySet()).stream(), command).stream() + .filter(unregisteredFile.getRoot()::remove) + .filter(this::internalRegister) + .toList().isEmpty(); } @Override public boolean reveal(String command) { - return hiddenFile.getRoot().remove(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 unregisteredFile.getRoot().add(command) && internalUnregister(command); + return !plugin.commandFinder().findCommands(command).stream() + .filter(s -> !s.equals("commander:command")) + .filter(unregisteredFile.getRoot()::add) + .filter(this::internalUnregister) + .toList().isEmpty(); } @Override public void unregisterCommands() { - unregisteredCommands().forEach(this::internalUnregister); + unregisteredCommands().stream() + .filter(command -> !command.equals("commander:command")) + .forEach(this::internalUnregister); } private boolean internalRegister(String command) { 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 929b4bc..49f3671 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 @@ -11,29 +11,34 @@ import org.bukkit.Bukkit; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; @Getter @RequiredArgsConstructor public class PaperPermissionOverride implements PermissionOverride { private final Map originalPermissions = new HashMap<>(); private final FileIO> overridesFile; + private final CommanderPlugin plugin; public PaperPermissionOverride(CommanderPlugin plugin) { this.overridesFile = new GsonFile>( IO.of(plugin.getDataFolder(), "permission-overrides.json"), new HashMap<>(), new TypeToken<>() { - }).saveIfAbsent(); + }).reload().saveIfAbsent(); + this.plugin = plugin; } @Override public Map overrides() { - return Map.copyOf(overridesFile.getRoot()); + return new HashMap<>(overridesFile.getRoot()); } @Override public Map originalPermissions() { - return Map.copyOf(originalPermissions); + return new HashMap<>(originalPermissions); } @Override @@ -53,15 +58,20 @@ public boolean isOverridden(String command) { @Override public boolean override(String command, @Nullable String permission) { - overridesFile.getRoot().put(command, permission); - return internalOverride(command, 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) { - if (!isOverridden(command)) return false; - overridesFile.getRoot().remove(command); - return internalReset(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(); } @Override 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 c891782..dcc4681 100644 --- a/velocity/src/main/java/net/thenextlvl/commander/velocity/CommanderPlugin.java +++ b/velocity/src/main/java/net/thenextlvl/commander/velocity/CommanderPlugin.java @@ -17,6 +17,7 @@ import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; 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.velocity.listener.CommandListener; @@ -39,6 +40,7 @@ public class CommanderPlugin implements Commander { private final CommanderVersionChecker versionChecker = new CommanderVersionChecker(this); private final ComponentBundle bundle; + private final ProxyCommandFinder commandFinder; private final ProxyCommandRegistry commandRegistry; private final ProxyPermissionOverride permissionOverride; private final Metrics.Factory metricsFactory; @@ -60,6 +62,7 @@ public CommanderPlugin(ProxyServer server, Logger logger, @DataDirectory Path da TagResolver.standard(), Placeholder.component("prefix", bundle.component(Locale.US, "prefix")) )).build()); + this.commandFinder = new ProxyCommandFinder(this); this.commandRegistry = new ProxyCommandRegistry(this); this.permissionOverride = new ProxyPermissionOverride(this); checkVersionUpdate(); 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 new file mode 100644 index 0000000..a7c492d --- /dev/null +++ b/velocity/src/main/java/net/thenextlvl/commander/velocity/implementation/ProxyCommandFinder.java @@ -0,0 +1,28 @@ +package net.thenextlvl.commander.velocity.implementation; + +import lombok.RequiredArgsConstructor; +import net.thenextlvl.commander.CommandFinder; +import net.thenextlvl.commander.velocity.CommanderPlugin; + +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@RequiredArgsConstructor +public class ProxyCommandFinder implements CommandFinder { + private final CommanderPlugin plugin; + + @Override + public Set findCommands(String input) { + return findCommands(plugin.server().getCommandManager().getAliases().stream(), input); + } + + @Override + public Set findCommands(Stream commands, String input) { + var pattern = Pattern.compile(input.replace("*", ".*")); + return commands.filter(command -> + pattern.matcher(command).matches() + ).collect(Collectors.toSet()); + } +} 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 0b1ed14..3969157 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 @@ -21,27 +21,29 @@ public ProxyCommandRegistry(CommanderPlugin plugin) { this.hiddenFile = new GsonFile>( IO.of(plugin.dataFolder().toFile(), "hidden-commands.json"), new HashSet<>(), new TypeToken<>() { - }).saveIfAbsent(); + }).reload().saveIfAbsent(); this.unregisteredFile = new GsonFile>( IO.of(plugin.dataFolder().toFile(), "removed-commands.json"), new HashSet<>(), new TypeToken<>() { - }).saveIfAbsent(); + }).reload().saveIfAbsent(); this.plugin = plugin; } @Override public Set hiddenCommands() { - return Set.copyOf(hiddenFile.getRoot()); + return new HashSet<>(hiddenFile.getRoot()); } @Override public Set unregisteredCommands() { - return Set.copyOf(unregisteredFile.getRoot()); + return new HashSet<>(unregisteredFile.getRoot()); } @Override public boolean hide(String command) { - return hiddenFile.getRoot().add(command); + return !plugin.commandFinder().findCommands(command).stream() + .filter(hiddenFile.getRoot()::add) + .toList().isEmpty(); } @Override @@ -56,18 +58,26 @@ public boolean isUnregistered(String command) { @Override public boolean register(String command) { - return unregisteredFile.getRoot().remove(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 hiddenFile.getRoot().remove(command); + return !plugin.commandFinder().findCommands(new HashSet<>(hiddenFile.getRoot()).stream(), command).stream() + .filter(hiddenFile.getRoot()::remove) + .toList().isEmpty(); } @Override public boolean unregister(String command) { - if (!plugin.server().getCommandManager().hasCommand(command)) return false; - return unregisteredFile.getRoot().add(command) && internalUnregister(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(); } @Override 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 e80aa19..f371a35 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 @@ -11,6 +11,7 @@ import org.jetbrains.annotations.Nullable; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Objects; @@ -18,17 +19,19 @@ @RequiredArgsConstructor public class ProxyPermissionOverride implements PermissionOverride { private final FileIO> overridesFile; + private final CommanderPlugin plugin; public ProxyPermissionOverride(CommanderPlugin plugin) { this.overridesFile = new GsonFile>( IO.of(plugin.dataFolder().toFile(), "permission-overrides.json"), new HashMap<>(), new TypeToken<>() { - }).saveIfAbsent(); + }).reload().saveIfAbsent(); + this.plugin = plugin; } @Override public Map overrides() { - return Map.copyOf(overridesFile.getRoot()); + return new HashMap<>(overridesFile.getRoot()); } @Override @@ -55,14 +58,19 @@ public boolean isOverridden(String command) { @Override public boolean override(String command, @Nullable String permission) { - return !Objects.equals(overridesFile.getRoot().put(command, permission), 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) { - if (!isOverridden(command)) return false; - overridesFile.getRoot().remove(command); - return true; + 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(); } @Override