mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-12-28 03:57:36 +01:00
Add timeout for command executions (#2887)
This commit is contained in:
parent
cb9e0899fc
commit
997e3c7ef7
@ -60,6 +60,7 @@ import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.util.ImmutableCollectors;
|
||||
|
||||
@ -67,11 +68,14 @@ import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
@ -129,8 +133,17 @@ public class CommandManager {
|
||||
}
|
||||
|
||||
public CompletableFuture<CommandResult> executeCommand(Sender sender, String label, List<String> args) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
this.lock.lock();
|
||||
SchedulerAdapter scheduler = this.plugin.getBootstrap().getScheduler();
|
||||
|
||||
// schedule a future to execute the command
|
||||
AtomicReference<Thread> thread = new AtomicReference<>();
|
||||
CompletableFuture<CommandResult> future = CompletableFuture.supplyAsync(() -> {
|
||||
thread.set(Thread.currentThread());
|
||||
if (!this.lock.tryLock()) {
|
||||
Message.ALREADY_EXECUTING_COMMAND.send(sender);
|
||||
this.lock.lock();
|
||||
}
|
||||
|
||||
try {
|
||||
return execute(sender, label, args);
|
||||
} catch (Throwable e) {
|
||||
@ -138,8 +151,26 @@ public class CommandManager {
|
||||
return null;
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
thread.set(null);
|
||||
}
|
||||
}, this.plugin.getBootstrap().getScheduler().async());
|
||||
}, scheduler.async());
|
||||
|
||||
// catch if the command doesn't complete within a given time
|
||||
scheduler.awaitTimeout(future, 10, TimeUnit.SECONDS, () -> handleCommandTimeout(thread, args));
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
private void handleCommandTimeout(AtomicReference<Thread> thread, List<String> args) {
|
||||
Thread executorThread = thread.get();
|
||||
if (executorThread == null) {
|
||||
this.plugin.getLogger().warn("Command execution " + args + " has not completed - but executor thread is null!");
|
||||
} else {
|
||||
String stackTrace = Arrays.stream(executorThread.getStackTrace())
|
||||
.map(el -> " " + el.toString())
|
||||
.collect(Collectors.joining("\n"));
|
||||
this.plugin.getLogger().warn("Command execution " + args + " has not completed. Trace: \n" + stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasPermissionForAny(Sender sender) {
|
||||
|
@ -165,6 +165,12 @@ public interface Message {
|
||||
.append(FULL_STOP)
|
||||
);
|
||||
|
||||
Args0 ALREADY_EXECUTING_COMMAND = () -> prefixed(translatable()
|
||||
// "&7Another command is being executed, waiting for it to finish..."
|
||||
.key("luckperms.commandsystem.already-executing-command")
|
||||
.color(GRAY)
|
||||
);
|
||||
|
||||
Args2<String, String> FIRST_TIME_SETUP = (label, username) -> join(newline(),
|
||||
// "&3It seems that no permissions have been setup yet!"
|
||||
// "&3Before you can use any of the LuckPerms commands in-game, you need to use the console to give yourself access."
|
||||
|
@ -25,8 +25,11 @@
|
||||
|
||||
package me.lucko.luckperms.common.plugin.scheduler;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* A scheduler for running tasks using the systems provided by the platform
|
||||
@ -85,6 +88,27 @@ public interface SchedulerAdapter {
|
||||
*/
|
||||
SchedulerTask asyncRepeating(Runnable task, long interval, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Waits for the given time for {@code future} to complete. If the future isn't completed,
|
||||
* {@code onTimeout} is executed.
|
||||
*
|
||||
* @param future the future to wait for
|
||||
* @param timeout the time to wait
|
||||
* @param unit the unit of timeout
|
||||
* @param onTimeout the function to execute when the timeout expires
|
||||
*/
|
||||
default void awaitTimeout(CompletableFuture<?> future, long timeout, TimeUnit unit, Runnable onTimeout) {
|
||||
executeAsync(() -> {
|
||||
try {
|
||||
future.get(timeout, unit);
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
// ignore
|
||||
} catch (TimeoutException e) {
|
||||
onTimeout.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the scheduler instance.
|
||||
*
|
||||
|
@ -5,6 +5,7 @@ luckperms.commandsystem.available-commands=Use {0} to view available commands
|
||||
luckperms.commandsystem.command-not-recognised=Command not recognised
|
||||
luckperms.commandsystem.no-permission=You do not have permission to use this command!
|
||||
luckperms.commandsystem.no-permission-subcommands=You do not have permission to use any sub commands
|
||||
luckperms.commandsystem.already-executing-command=Another command is being executed, waiting for it to finish...
|
||||
luckperms.commandsystem.usage.sub-commands-header=Sub Commands
|
||||
luckperms.commandsystem.usage.usage-header=Command Usage
|
||||
luckperms.commandsystem.usage.arguments-header=Arguments
|
||||
|
Loading…
Reference in New Issue
Block a user