package net.buycraft.plugin.execution.strategy; import net.buycraft.plugin.IBuycraftPlatform; import net.buycraft.plugin.platform.NoBlocking; import java.math.BigDecimal; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.logging.Level; public class QueuedCommandExecutor implements CommandExecutor, Runnable { private static final long MAXIMUM_NOTIFICATION_TIME = TimeUnit.MILLISECONDS.toNanos(5); private static final int RUN_MAX_COMMANDS_BLOCKING = 10; private final IBuycraftPlatform platform; private final boolean blocking; private final Set<ToRunQueuedCommand> commandQueue = new LinkedHashSet<>(); private final PostCompletedCommandsTask completedCommandsTask; public QueuedCommandExecutor(IBuycraftPlatform platform, PostCompletedCommandsTask completedCommandsTask) { this.platform = platform; this.blocking = !platform.getClass().isAnnotationPresent(NoBlocking.class); this.completedCommandsTask = completedCommandsTask; } @Override public void queue(ToRunQueuedCommand command) { synchronized (commandQueue) { commandQueue.add(command); } } @Override public void run() { List<ToRunQueuedCommand> runThisTick = new ArrayList<>(); synchronized (commandQueue) { for (Iterator<ToRunQueuedCommand> it = commandQueue.iterator(); it.hasNext(); ) { ToRunQueuedCommand command = it.next(); if (command.canExecute(platform)) { runThisTick.add(command); it.remove(); } if (blocking && runThisTick.size() >= RUN_MAX_COMMANDS_BLOCKING) { break; } } } long start = System.nanoTime(); for (ToRunQueuedCommand command : runThisTick) { String finalCommand = platform.getPlaceholderManager().doReplace(command.getPlayer(), command.getCommand()); platform.log(Level.INFO, String.format("Dispatching command '%s' for player '%s'.", finalCommand, command.getPlayer().getName())); try { platform.dispatchCommand(finalCommand); completedCommandsTask.add(command.getCommand().getId()); } catch (Exception e) { platform.log(Level.SEVERE, String.format("Could not dispatch command '%s' for player '%s'. " + "This is typically a plugin error, not an issue with BuycraftX.", finalCommand, command.getPlayer().getName()), e); } } long fullTime = System.nanoTime() - start; if (fullTime > MAXIMUM_NOTIFICATION_TIME) { // Make the time much nicer. BigDecimal timeMs = new BigDecimal(fullTime).divide(new BigDecimal("1000000"), 2, BigDecimal.ROUND_CEILING); if (blocking) { platform.log(Level.SEVERE, "Command execution took " + timeMs.toPlainString() + "ms to complete. " + "This likely indicates an issue with one of your server's plugins, which can cause lag."); } else { platform.log(Level.SEVERE, "Command execution took " + timeMs.toPlainString() + "ms to complete. " + "This likely indicates an issue with one of your server's plugins, which will slow command execution."); } } } }