diff --git a/api/src/main/java/us/ajg0702/queue/api/commands/ICommandSender.java b/api/src/main/java/us/ajg0702/queue/api/commands/ICommandSender.java index 041aa1d..022ba8a 100644 --- a/api/src/main/java/us/ajg0702/queue/api/commands/ICommandSender.java +++ b/api/src/main/java/us/ajg0702/queue/api/commands/ICommandSender.java @@ -3,8 +3,15 @@ package us.ajg0702.queue.api.commands; import net.kyori.adventure.audience.Audience; import us.ajg0702.queue.api.util.Handle; +import java.util.UUID; + @SuppressWarnings("BooleanMethodIsAlwaysInverted") public interface ICommandSender extends Handle, Audience { boolean hasPermission(String permission); boolean isPlayer(); + + /** + * @throws IllegalStateException if the sender is not a player + */ + UUID getUniqueId() throws IllegalStateException; } diff --git a/build.gradle.kts b/build.gradle.kts index 8c96ca6..cd5e4b7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ repositories { } allprojects { - version = "2.3.0" + version = "2.3.1" group = "us.ajg0702" plugins.apply("java") diff --git a/common/src/main/java/us/ajg0702/queue/commands/commands/PlayerSender.java b/common/src/main/java/us/ajg0702/queue/commands/commands/PlayerSender.java index 57da37c..d640008 100644 --- a/common/src/main/java/us/ajg0702/queue/commands/commands/PlayerSender.java +++ b/common/src/main/java/us/ajg0702/queue/commands/commands/PlayerSender.java @@ -3,6 +3,8 @@ package us.ajg0702.queue.commands.commands; import us.ajg0702.queue.api.commands.ICommandSender; import us.ajg0702.queue.api.players.AdaptedPlayer; +import java.util.UUID; + public class PlayerSender implements ICommandSender { final AdaptedPlayer handle; @@ -21,6 +23,11 @@ public class PlayerSender implements ICommandSender { return true; } + @Override + public UUID getUniqueId() throws IllegalStateException { + return handle.getUniqueId(); + } + @Override public AdaptedPlayer getHandle() { return handle; diff --git a/common/src/main/java/us/ajg0702/queue/commands/commands/manage/ManageCommand.java b/common/src/main/java/us/ajg0702/queue/commands/commands/manage/ManageCommand.java index 586adce..fddb210 100644 --- a/common/src/main/java/us/ajg0702/queue/commands/commands/manage/ManageCommand.java +++ b/common/src/main/java/us/ajg0702/queue/commands/commands/manage/ManageCommand.java @@ -37,6 +37,7 @@ public class ManageCommand extends BaseCommand { addSubCommand(new Update(main)); addSubCommand(new Kick(main)); addSubCommand(new KickAll(main)); + addSubCommand(new PauseQueueServer(main)); } diff --git a/common/src/main/java/us/ajg0702/queue/commands/commands/manage/PauseQueueServer.java b/common/src/main/java/us/ajg0702/queue/commands/commands/manage/PauseQueueServer.java new file mode 100644 index 0000000..0a416e2 --- /dev/null +++ b/common/src/main/java/us/ajg0702/queue/commands/commands/manage/PauseQueueServer.java @@ -0,0 +1,81 @@ +package us.ajg0702.queue.commands.commands.manage; + +import com.google.common.collect.ImmutableList; +import us.ajg0702.queue.api.commands.ICommandSender; +import us.ajg0702.queue.api.commands.ISubCommand; +import us.ajg0702.queue.api.players.AdaptedPlayer; +import us.ajg0702.queue.commands.SubCommand; +import us.ajg0702.queue.common.QueueMain; +import us.ajg0702.queue.common.utils.Debug; +import us.ajg0702.utils.common.Messages; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public class PauseQueueServer extends SubCommand { + + public static final List pausedPlayers = new CopyOnWriteArrayList<>(); + + final QueueMain main; + public PauseQueueServer(QueueMain main) { + this.main = main; + } + + @Override + public String getName() { + return "pausequeueserver"; + } + + @Override + public ImmutableList getAliases() { + return ImmutableList.of("pauseqs"); + } + + @Override + public ImmutableList getSubCommands() { + return ImmutableList.of(); + } + + @Override + public String getPermission() { + return "ajqueue.manage.pausequeueserver"; + } + + @Override + public boolean showInTabComplete() { + return true; + } + + @Override + public Messages getMessages() { + return main.getMessages(); + } + + @Override + public void execute(ICommandSender sender, String[] args) { + if(!checkPermission(sender)) return; + + if(!sender.isPlayer()) { + sender.sendMessage(getMessages().getComponent("errors.player-only")); + return; + } + + AdaptedPlayer player = main.getPlatformMethods().getPlayer(sender.getUniqueId()); + if(pausedPlayers.contains(player)) { + pausedPlayers.remove(player); + sender.sendMessage(getMessages().getComponent("commands.pausequeueserver.unpaused")); + return; + } + + pausedPlayers.add(player); + sender.sendMessage(getMessages().getComponent("commands.pausequeueserver.paused")); + } + + @Override + public List autoComplete(ICommandSender sender, String[] args) { + return Collections.emptyList(); + } +} diff --git a/common/src/main/java/us/ajg0702/queue/commands/commands/queue/QueueCommand.java b/common/src/main/java/us/ajg0702/queue/commands/commands/queue/QueueCommand.java index 0677fb4..7882641 100644 --- a/common/src/main/java/us/ajg0702/queue/commands/commands/queue/QueueCommand.java +++ b/common/src/main/java/us/ajg0702/queue/commands/commands/queue/QueueCommand.java @@ -11,9 +11,13 @@ import us.ajg0702.utils.common.Messages; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class QueueCommand extends BaseCommand { + public static Map cooldowns = new ConcurrentHashMap<>(); + private final QueueMain main; public QueueCommand(QueueMain main) { @@ -58,6 +62,14 @@ public class QueueCommand extends BaseCommand { } AdaptedPlayer player = main.getPlatformMethods().senderToPlayer(sender); + long lastUse = cooldowns.getOrDefault(player, 0L); + if(System.currentTimeMillis() - lastUse < main.getConfig().getDouble("queue-command-cooldown") * 1000L) { + sender.sendMessage(main.getMessages().getComponent("errors.too-fast-queue")); + return; + } + + cooldowns.put(player, System.currentTimeMillis()); + if(args.length > 0) { if(main.getConfig().getBoolean("require-permission") && !player.hasPermission("ajqueue.queue."+args[0])) { sender.sendMessage(getMessages().getComponent("noperm")); @@ -79,7 +91,11 @@ public class QueueCommand extends BaseCommand { return new ArrayList<>(); } if(args.length == 1) { - return filterCompletion(main.getQueueManager().getServerNames(), args[0]); + List servers = filterCompletion(main.getQueueManager().getServerNames(), args[0]); + if(main.getConfig().getBoolean("require-permission")) { + servers.removeIf(s -> !sender.hasPermission("ajqueue.queue." + s)); + } + return servers; } return new ArrayList<>(); } diff --git a/common/src/main/java/us/ajg0702/queue/common/EventHandlerImpl.java b/common/src/main/java/us/ajg0702/queue/common/EventHandlerImpl.java index fdc0319..d15af1a 100644 --- a/common/src/main/java/us/ajg0702/queue/common/EventHandlerImpl.java +++ b/common/src/main/java/us/ajg0702/queue/common/EventHandlerImpl.java @@ -11,6 +11,8 @@ import us.ajg0702.queue.api.players.QueuePlayer; import us.ajg0702.queue.api.queues.QueueServer; import us.ajg0702.queue.api.server.AdaptedServer; import us.ajg0702.queue.commands.commands.PlayerSender; +import us.ajg0702.queue.commands.commands.manage.PauseQueueServer; +import us.ajg0702.queue.commands.commands.queue.QueueCommand; import us.ajg0702.queue.common.players.QueuePlayerImpl; import us.ajg0702.queue.common.utils.Debug; import us.ajg0702.utils.common.TimeUtils; @@ -144,15 +146,13 @@ public class EventHandlerImpl implements EventHandler { @Override public void onPlayerJoin(AdaptedPlayer player) { - new Thread(() -> { - try { - TimeUnit.SECONDS.sleep(2); - } catch (InterruptedException ignored) { - } - if (main.getUpdater().isUpdateAvailable() && player.hasPermission("ajqueue.manage.update")) { - player.sendMessage(main.getMessages().getComponent("updater.update-available")); - } - }).start(); + if(player.hasPermission("ajqueue.manage.update")) { + main.getTaskManager().runLater(() -> { + if (main.getUpdater().isUpdateAvailable() && !main.getUpdater().isAlreadyDownloaded()) { + player.sendMessage(main.getMessages().getComponent("updater.update-available")); + } + }, 2, TimeUnit.SECONDS); + } ImmutableList queues = main.getQueueManager().findPlayerInQueues(player); for(QueuePlayer queuePlayer : queues) { @@ -194,16 +194,22 @@ public class EventHandlerImpl implements EventHandler { } } + if(main.getConfig().getBoolean("include-server-switch-in-cooldown")) { + QueueCommand.cooldowns.put(player, System.currentTimeMillis()); + } - String serverName = player.getServerName(); - List svs = main.getConfig().getStringList("queue-servers"); - for(String s : svs) { - if(!s.contains(":")) continue; - String[] parts = s.split(":"); - String from = parts[0]; - QueueServer to = main.getQueueManager().findServer(parts[1]); - if(from.equalsIgnoreCase(serverName) && to != null) { - main.getQueueManager().addToQueue(player, to); + + if(!PauseQueueServer.pausedPlayers.contains(player)) { + String serverName = player.getServerName(); + List svs = main.getConfig().getStringList("queue-servers"); + for(String s : svs) { + if(!s.contains(":")) continue; + String[] parts = s.split(":"); + String from = parts[0]; + QueueServer to = main.getQueueManager().findServer(parts[1]); + if(from.equalsIgnoreCase(serverName) && to != null) { + main.getQueueManager().addToQueue(player, to); + } } } diff --git a/common/src/main/java/us/ajg0702/queue/common/QueueMain.java b/common/src/main/java/us/ajg0702/queue/common/QueueMain.java index 0973e5a..b46f843 100644 --- a/common/src/main/java/us/ajg0702/queue/common/QueueMain.java +++ b/common/src/main/java/us/ajg0702/queue/common/QueueMain.java @@ -212,6 +212,7 @@ public class QueueMain extends AjQueueAPI { d.put("errors.wrong-version.base", "You must be on {VERSIONS} to join this server!"); d.put("errors.wrong-version.or", " or "); d.put("errors.wrong-version.comma", ", "); + d.put("errors.too-fast-queue", "You're queueing too fast!"); d.put("commands.leave-queue", "&aYou left the queue for {SERVER}!"); d.put("commands.reload", "&aConfig and messages reloaded successfully!"); @@ -222,6 +223,9 @@ public class QueueMain extends AjQueueAPI { d.put("commands.kick.success", "Kicked {PLAYER} from {NUM} queue{s}!"); d.put("commands.kickall.usage", "Usage: /ajqueue kickall "); d.put("commands.kickall.success", "Kicked {NUM} player{s} from {SERVER}!"); + d.put("commands.pausequeueserver.unpaused", "You are no longer paused! You can now use queue-servers normally."); + d.put("commands.pausequeueserver.paused", "You are now paused! You will no longer be sent using queue-servers."); + d.put("commands.pausequeueserver.reminder", "Reminder: You are currently paused for queue-servers, so you will not be sent using them! Use /ajQueue pausequeueserver to un-pause and return to normal behaviour"); d.put("noperm", "&cYou do not have permission to do this!"); diff --git a/common/src/main/java/us/ajg0702/queue/common/QueueManagerImpl.java b/common/src/main/java/us/ajg0702/queue/common/QueueManagerImpl.java index ca5395c..af92142 100644 --- a/common/src/main/java/us/ajg0702/queue/common/QueueManagerImpl.java +++ b/common/src/main/java/us/ajg0702/queue/common/QueueManagerImpl.java @@ -9,6 +9,7 @@ import us.ajg0702.queue.api.players.QueuePlayer; import us.ajg0702.queue.api.premium.Logic; import us.ajg0702.queue.api.queues.QueueServer; import us.ajg0702.queue.api.server.AdaptedServer; +import us.ajg0702.queue.commands.commands.manage.PauseQueueServer; import us.ajg0702.queue.common.players.QueuePlayerImpl; import us.ajg0702.queue.common.queues.QueueServerImpl; import us.ajg0702.queue.common.utils.Debug; @@ -427,6 +428,8 @@ public class QueueManagerImpl implements QueueManager { } } + protected final Map pausedAntiSpam = new ConcurrentHashMap<>(); + @Override public void sendQueueEvents() { if(main.getConfig().getBoolean("force-queue-server-target")) { @@ -440,6 +443,14 @@ public class QueueManagerImpl implements QueueManager { QueueServer to = findServer(toName); if(from == null || to == null) continue; from.getPlayers().forEach(player -> { + if(PauseQueueServer.pausedPlayers.contains(player)) { + long lastReminder = pausedAntiSpam.getOrDefault(player, 0L); + if(System.currentTimeMillis() - lastReminder > 60e3) { // 60 second cooldown on the reminder messages + player.sendMessage(main.getMessages().getComponent("commands.pausequeueserver.reminder")); + pausedAntiSpam.put(player, System.currentTimeMillis()); + } + return; + } if(!getPlayerQueues(player).contains(to)) { addToQueue(player, to); } diff --git a/common/src/main/resources/config.yml b/common/src/main/resources/config.yml index 42d98c1..e74f6e3 100644 --- a/common/src/main/resources/config.yml +++ b/common/src/main/resources/config.yml @@ -1,5 +1,5 @@ # Dont touch this number please -config-version: 34 +config-version: 35 # This is the main config for ajQueue. @@ -313,6 +313,12 @@ give-fulljoin-players-priority: 0 # not in the queue for the target server, it will add them. force-queue-server-target: true +# How long should the cooldown for queue commands be? (in seconds) +queue-command-cooldown: 3 + +# Should any server switch (including the initial join) count against the queue command cooldown? +include-server-switch-in-cooldown: false + # The minimum time between pinging the server. # If ajQueue is pinging your backend servers too often, raise this number diff --git a/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/commands/BungeeSender.java b/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/commands/BungeeSender.java index de75704..6171802 100644 --- a/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/commands/BungeeSender.java +++ b/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/commands/BungeeSender.java @@ -8,6 +8,8 @@ import org.jetbrains.annotations.NotNull; import us.ajg0702.queue.api.commands.ICommandSender; import us.ajg0702.queue.platforms.bungeecord.BungeeQueue; +import java.util.UUID; + public class BungeeSender implements ICommandSender { final CommandSender handle; @@ -27,6 +29,12 @@ public class BungeeSender implements ICommandSender { return handle instanceof ProxiedPlayer; } + @Override + public UUID getUniqueId() throws IllegalStateException { + if(!(handle instanceof ProxiedPlayer)) throw new IllegalStateException("Cannot get UUID of non-player!"); + return ((ProxiedPlayer) handle).getUniqueId(); + } + @Override public void sendMessage(@NotNull Component message) { if(PlainTextComponentSerializer.plainText().serialize(message).isEmpty()) return; diff --git a/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/players/BungeePlayer.java b/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/players/BungeePlayer.java index f443692..dff7d5a 100644 --- a/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/players/BungeePlayer.java +++ b/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/players/BungeePlayer.java @@ -20,6 +20,7 @@ import us.ajg0702.queue.platforms.bungeecord.server.BungeeServer; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.UUID; public class BungeePlayer implements AdaptedPlayer, Audience { @@ -172,4 +173,17 @@ public class BungeePlayer implements AdaptedPlayer, Audience { } return true; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BungeePlayer that = (BungeePlayer) o; + return handle.equals(that.handle); + } + + @Override + public int hashCode() { + return Objects.hash(handle); + } } diff --git a/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/server/BungeeServer.java b/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/server/BungeeServer.java index 777a12f..c414a32 100644 --- a/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/server/BungeeServer.java +++ b/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/server/BungeeServer.java @@ -2,6 +2,7 @@ package us.ajg0702.queue.platforms.bungeecord.server; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; +import org.jetbrains.annotations.Nullable; import us.ajg0702.queue.api.AjQueueAPI; import us.ajg0702.queue.api.players.AdaptedPlayer; import us.ajg0702.queue.api.server.AdaptedServer; @@ -51,8 +52,9 @@ public class BungeeServer implements AdaptedServer { if(debug) logger.info("[pinger] [" + getName() + "] sending ping"); handle.ping((pp, error) -> { - if(error != null) { + if(error != null || pp == null) { markOffline(debug, logger, future, sent, error); + return; } offlineTime = 0; @@ -71,7 +73,7 @@ public class BungeeServer implements AdaptedServer { return future; } - private void markOffline(boolean debug, QueueLogger logger, CompletableFuture future, long sent, Throwable e) { + private void markOffline(boolean debug, QueueLogger logger, CompletableFuture future, long sent, @Nullable Throwable e) { long lastOnline = lastSuccessfullPing == null ? 0 : lastSuccessfullPing.getFetchedTime(); offlineTime = (int) Math.min(sent - lastOnline, Integer.MAX_VALUE); diff --git a/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/server/BungeeServerPing.java b/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/server/BungeeServerPing.java index cc1ff7e..6c476ba 100644 --- a/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/server/BungeeServerPing.java +++ b/platforms/bungeecord/src/main/java/us/ajg0702/queue/platforms/bungeecord/server/BungeeServerPing.java @@ -4,6 +4,7 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer; import net.md_5.bungee.api.ServerPing; import net.md_5.bungee.api.chat.BaseComponent; +import org.jetbrains.annotations.NotNull; import us.ajg0702.queue.api.server.AdaptedServerPing; public class BungeeServerPing implements AdaptedServerPing { @@ -11,7 +12,7 @@ public class BungeeServerPing implements AdaptedServerPing { final ServerPing handle; private final long sent; - public BungeeServerPing(ServerPing handle, long sent) { + public BungeeServerPing(@NotNull ServerPing handle, long sent) { this.handle = handle; this.sent = sent; } diff --git a/platforms/velocity/src/main/java/us/ajg0702/queue/platforms/velocity/commands/VelocitySender.java b/platforms/velocity/src/main/java/us/ajg0702/queue/platforms/velocity/commands/VelocitySender.java index b38642f..636e67a 100644 --- a/platforms/velocity/src/main/java/us/ajg0702/queue/platforms/velocity/commands/VelocitySender.java +++ b/platforms/velocity/src/main/java/us/ajg0702/queue/platforms/velocity/commands/VelocitySender.java @@ -2,11 +2,14 @@ package us.ajg0702.queue.platforms.velocity.commands; import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.proxy.ConsoleCommandSource; +import com.velocitypowered.api.proxy.Player; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.jetbrains.annotations.NotNull; import us.ajg0702.queue.api.commands.ICommandSender; +import java.util.UUID; + public class VelocitySender implements ICommandSender { final CommandSource handle; @@ -26,6 +29,12 @@ public class VelocitySender implements ICommandSender { return !(handle instanceof ConsoleCommandSource); } + @Override + public UUID getUniqueId() throws IllegalStateException { + if(!(handle instanceof Player)) throw new IllegalStateException("Cannot get UUID of non-player!"); + return ((Player) handle).getUniqueId(); + } + @Override public void sendMessage(@NotNull Component message) { if(PlainTextComponentSerializer.plainText().serialize(message).isEmpty()) return; diff --git a/platforms/velocity/src/main/java/us/ajg0702/queue/platforms/velocity/players/VelocityPlayer.java b/platforms/velocity/src/main/java/us/ajg0702/queue/platforms/velocity/players/VelocityPlayer.java index a362b61..5200956 100644 --- a/platforms/velocity/src/main/java/us/ajg0702/queue/platforms/velocity/players/VelocityPlayer.java +++ b/platforms/velocity/src/main/java/us/ajg0702/queue/platforms/velocity/players/VelocityPlayer.java @@ -18,6 +18,7 @@ import us.ajg0702.queue.common.QueueMain; import us.ajg0702.queue.common.utils.Debug; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.UUID; @@ -205,4 +206,17 @@ public class VelocityPlayer implements AdaptedPlayer, Audience { } return true; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + VelocityPlayer that = (VelocityPlayer) o; + return handle.equals(that.handle); + } + + @Override + public int hashCode() { + return Objects.hash(handle); + } } diff --git a/spigot/src/main/java/us/ajg0702/queue/spigot/Commands.java b/spigot/src/main/java/us/ajg0702/queue/spigot/Commands.java index acaa951..7acad6d 100644 --- a/spigot/src/main/java/us/ajg0702/queue/spigot/Commands.java +++ b/spigot/src/main/java/us/ajg0702/queue/spigot/Commands.java @@ -53,10 +53,15 @@ public class Commands implements CommandExecutor { player = tply; srvname = args[1]; } + + if(player == null) { + sender.sendMessage("I need to know what player to send!"); + return true; + } + if(pl.getAConfig().getBoolean("send-queue-commands-in-batches")) { pl.queuebatch.put(player, srvname); } else { - assert player != null; pl.sendMessage(player, "queue", srvname); }