diff --git a/src/main/java/net/minestom/server/command/CommandManager.java b/src/main/java/net/minestom/server/command/CommandManager.java index b31a4d54e..876bd0cd5 100644 --- a/src/main/java/net/minestom/server/command/CommandManager.java +++ b/src/main/java/net/minestom/server/command/CommandManager.java @@ -1,5 +1,6 @@ package net.minestom.server.command; +import net.minestom.server.MinecraftServer; import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.CommandDispatcher; import net.minestom.server.command.builder.CommandResult; @@ -13,7 +14,9 @@ import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Method; import java.util.*; +import java.util.concurrent.Callable; /** * Manager used to register {@link Command commands}. @@ -21,6 +24,7 @@ import java.util.*; * It is also possible to simulate a command using {@link #execute(CommandSender, String)}. */ public final class CommandManager { + private static final boolean ASYNC_VIRTUAL = Boolean.getBoolean("minestom.command.async-virtual"); public static final String COMMAND_PREFIX = "/"; @@ -97,27 +101,56 @@ public final class CommandManager { * @param command the raw command string (without the command prefix) * @return the execution result */ - public @NotNull CommandResult execute(@NotNull CommandSender sender, @NotNull String command) { - command = command.trim(); - // Command event - if (sender instanceof Player player) { - PlayerCommandEvent playerCommandEvent = new PlayerCommandEvent(player, command); - EventDispatcher.call(playerCommandEvent); - if (playerCommandEvent.isCancelled()) - return CommandResult.of(CommandResult.Type.CANCELLED, command); - command = playerCommandEvent.getCommand(); - } - // Process the command - final CommandParser.Result parsedCommand = parseCommand(sender, command); - final ExecutableCommand executable = parsedCommand.executable(); - final ExecutableCommand.Result executeResult = executable.execute(sender); - final CommandResult result = resultConverter(executable, executeResult, command); - if (result.getType() == CommandResult.Type.UNKNOWN) { - if (unknownCommandCallback != null) { - this.unknownCommandCallback.apply(sender, command); + public @NotNull CommandResult execute(@NotNull CommandSender sender, @NotNull String rawCommand) { + Callable callable = () -> { + var command = rawCommand.trim(); + // Command event + if (sender instanceof Player player) { + PlayerCommandEvent playerCommandEvent = new PlayerCommandEvent(player, command); + EventDispatcher.call(playerCommandEvent); + if (playerCommandEvent.isCancelled()) + return CommandResult.of(CommandResult.Type.CANCELLED, command); + command = playerCommandEvent.getCommand(); } + // Process the command + final CommandParser.Result parsedCommand = parseCommand(sender, command); + final ExecutableCommand executable = parsedCommand.executable(); + final ExecutableCommand.Result executeResult = executable.execute(sender); + final CommandResult result = resultConverter(executable, executeResult, command); + if (result.getType() == CommandResult.Type.UNKNOWN) { + if (unknownCommandCallback != null) { + this.unknownCommandCallback.apply(sender, command); + } + } + return result; + }; + + + try { + if (ASYNC_VIRTUAL) { + class Reflection { + static Method startVirtualThread = null; + } + if (Reflection.startVirtualThread == null) { + Reflection.startVirtualThread = Thread.class.getDeclaredMethod("startVirtualThread", Runnable.class); + Reflection.startVirtualThread.setAccessible(true); + } + + Reflection.startVirtualThread.invoke(null, (Runnable) () -> { + try { + callable.call(); + } catch (Exception e) { + MinecraftServer.getExceptionManager().handleException(e); + } + }); + return CommandResult.of(CommandResult.Type.UNKNOWN, rawCommand); + } else { + return callable.call(); + } + } catch (Exception e) { + MinecraftServer.getExceptionManager().handleException(e); + return CommandResult.of(CommandResult.Type.UNKNOWN, rawCommand); } - return result; } /**