Repository: cloudnetservice/cloudnet-v3
Issue: Command system does unnecessary work

**Description of the bug:**
The command system executes the validation condition for a command multiple times to filter for the invalid message.


**Steps to reproduce this bug:**
For example use the ``perms <user>`` command, this will check multiple times if the user exists, which might take some time when having a bigger database.

Changed files:
cloudnet/src/main/java/de/dytanic/cloudnet/command/sub/SubCommandHandler.java

diff --git a/cloudnet/src/main/java/de/dytanic/cloudnet/command/sub/SubCommandHandler.java b/cloudnet/src/main/java/de/dytanic/cloudnet/command/sub/SubCommandHandler.java
index 374c62a87..c35c82836 100644
--- a/cloudnet/src/main/java/de/dytanic/cloudnet/command/sub/SubCommandHandler.java
+++ b/cloudnet/src/main/java/de/dytanic/cloudnet/command/sub/SubCommandHandler.java
@@ -9,7 +9,10 @@ import de.dytanic.cloudnet.common.Properties;
 import de.dytanic.cloudnet.common.collection.Pair;
 import de.dytanic.cloudnet.common.language.LanguageManager;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 public class SubCommandHandler extends Command implements ITabCompleter {
@@ -66,33 +69,35 @@ public class SubCommandHandler extends Command implements ITabCompleter {
 
     @Override
     public void execute(ICommandSender sender, String command, String[] args, String commandLine, Properties properties) {
-        Optional<Pair<SubCommand, SubCommandArgument<?>[]>> optionalSubCommand = this.subCommands.stream()
-                .map(subCommand -> new Pair<>(subCommand, subCommand.parseArgs(args)))
-                .filter(pair -> pair.getSecond() != null && pair.getSecond().length != 0)
-                .findFirst();
-
-        if (!optionalSubCommand.isPresent()) {
-            Optional<String> optionalInvalidMessage = this.subCommands.stream()
-                    .map(subCommand -> subCommand.getInvalidArgumentMessage(args))
-                    .filter(Objects::nonNull)
-                    .filter(pair -> pair.getSecond() == 0) // all static values must match
-                    .findFirst()
-                    .map(Pair::getFirst);
-
-            if (optionalInvalidMessage.isPresent()) {
-                sender.sendMessage(optionalInvalidMessage.get());
-            } else {
-                this.sendHelp(sender);
+        SubCommand subCommand = null;
+        SubCommandArgument<?>[] arguments = null;
+
+        for (SubCommand registeredCommand : this.subCommands) {
+            arguments = registeredCommand.parseArgs(args);
+            if (arguments != null && arguments.length > 0) {
+                subCommand = registeredCommand;
+                break;
+            }
+        }
+
+        if (subCommand == null) {
+            for (SubCommand registeredCommand : this.subCommands) {
+                Pair<String, Integer> invalidArgumentMessage = registeredCommand.getInvalidArgumentMessage(args);
+                if (invalidArgumentMessage != null && invalidArgumentMessage.getSecond() == 0) {
+                    sender.sendMessage(invalidArgumentMessage.getFirst());
+                    return;
+                }
             }
 
+            this.sendHelp(sender);
             return;
         }
 
-        Pair<SubCommand, SubCommandArgument<?>[]> subCommandPair = optionalSubCommand.get();
-
-        SubCommand subCommand = subCommandPair.getFirst();
-        SubCommandArgument<?>[] parsedArgs = subCommandPair.getSecond();
+        this.executeCommand(sender, command, args, commandLine, subCommand, arguments);
+    }
 
+    protected void executeCommand(ICommandSender sender, String command, String[] args, String commandLine,
+                                  SubCommand subCommand, SubCommandArgument<?>[] parsedArgs) {
         if (subCommand.isOnlyConsole() && !(sender instanceof ConsoleCommandSender)) {
             sender.sendMessage(LanguageManager.getMessage("command-sub-only-console"));
             return;