diff --git a/api/src/main/java/us/ajg0702/queue/api/AjQueueAPI.java b/api/src/main/java/us/ajg0702/queue/api/AjQueueAPI.java index 5a2fa72..c17d341 100644 --- a/api/src/main/java/us/ajg0702/queue/api/AjQueueAPI.java +++ b/api/src/main/java/us/ajg0702/queue/api/AjQueueAPI.java @@ -40,6 +40,12 @@ public abstract class AjQueueAPI { */ public abstract void setTimeBetweenPlayers(); + /** + * Gets the server time manager instance, which tracks when players last changed servers + * @return The server time manager instance + */ + public abstract ServerTimeManager getServerTimeManager(); + /** * Gets the ajQueue config * @return the ajQueue config diff --git a/api/src/main/java/us/ajg0702/queue/api/ServerTimeManager.java b/api/src/main/java/us/ajg0702/queue/api/ServerTimeManager.java new file mode 100644 index 0000000..7e75b95 --- /dev/null +++ b/api/src/main/java/us/ajg0702/queue/api/ServerTimeManager.java @@ -0,0 +1,12 @@ +package us.ajg0702.queue.api; + +import us.ajg0702.queue.api.players.AdaptedPlayer; + +public interface ServerTimeManager { + /** + * Gets the time that the player specified was last seen switching servers + * @param player The player to check + * @return The time that they last switched servers, in miliseconds since midnight, January 1, 1970, UTC + */ + long getLastServerChange(AdaptedPlayer player); +} diff --git a/api/src/main/java/us/ajg0702/queue/api/premium/Logic.java b/api/src/main/java/us/ajg0702/queue/api/premium/Logic.java index 8e1667d..a08fe98 100644 --- a/api/src/main/java/us/ajg0702/queue/api/premium/Logic.java +++ b/api/src/main/java/us/ajg0702/queue/api/premium/Logic.java @@ -59,7 +59,12 @@ public interface Logic { } if(fulljoinPriority > 0) { - if(server.isFull() && server.canJoinFull(player)) { + if(server.isFull() && (server.canJoinFull(player) || + ( + player.hasPermission("ajqueue.make-room") && + AjQueueAPI.getInstance().getConfig().getBoolean("enable-make-room-permission") + ) + )) { highest = Math.max(highest, fulljoinPriority); } } 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 8497be3..28b5198 100644 --- a/common/src/main/java/us/ajg0702/queue/common/EventHandlerImpl.java +++ b/common/src/main/java/us/ajg0702/queue/common/EventHandlerImpl.java @@ -58,6 +58,8 @@ public class EventHandlerImpl implements EventHandler { if(queues.size() > 0) { main.getQueueManager().sendMessage(main.getQueueManager().getSingleServer(player).findPlayer(player)); } + + main.serverTimeManager.playerChanged(player); } @Override @@ -77,6 +79,7 @@ public class EventHandlerImpl implements EventHandler { } main.getQueueManager().clear(player); QueueCommand.cooldowns.remove(player); + main.serverTimeManager.removePlayer(player); } @Override @@ -120,6 +123,8 @@ public class EventHandlerImpl implements EventHandler { } } + main.serverTimeManager.playerChanged(player); + } @Override 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 3b69bab..4688b6a 100644 --- a/common/src/main/java/us/ajg0702/queue/common/QueueMain.java +++ b/common/src/main/java/us/ajg0702/queue/common/QueueMain.java @@ -27,6 +27,14 @@ public class QueueMain extends AjQueueAPI { } private double timeBetweenPlayers; + + protected ServerTimeManagerImpl serverTimeManager = new ServerTimeManagerImpl(); + + @Override + public ServerTimeManager getServerTimeManager() { + return serverTimeManager; + } + @Override public double getTimeBetweenPlayers() { return timeBetweenPlayers; @@ -230,6 +238,7 @@ public class QueueMain extends AjQueueAPI { d.put("status.now-in-queue", "&aYou are now queued for {SERVER}! &7You are in position &f{POS}&7 of &f{LEN}&7.\n&7Type &f/leavequeue&7 or &fclick here&7 to leave the queue!"); d.put("status.now-in-empty-queue", ""); d.put("status.sending-now", "&aSending you to &f{SERVER} &anow.."); + d.put("status.making-room", "Making room for you.."); d.put("errors.server-not-exist", "&cThe server {SERVER} does not exist!"); d.put("errors.already-queued", "&cYou are already queued for that server!"); @@ -241,6 +250,10 @@ public class QueueMain extends AjQueueAPI { 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("errors.kicked-to-make-room", "You were moved to the lobby to make room for another player."); + d.put("errors.make-room-failed.player", "Failed to make room for you in that server."); + d.put("errors.make-room-failed.admin", "Failed to make room for you in that server. Check the console for more information."); + d.put("commands.leave-queue", "&aYou left the queue for {SERVER}!"); d.put("commands.reload", "&aConfig and messages reloaded successfully!"); 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 6138455..3105f06 100644 --- a/common/src/main/java/us/ajg0702/queue/common/QueueManagerImpl.java +++ b/common/src/main/java/us/ajg0702/queue/common/QueueManagerImpl.java @@ -660,7 +660,93 @@ public class QueueManagerImpl implements QueueManager { if(!server.canAccess(nextPlayer)) continue; - if(selected.isFull() && !selected.canJoinFull(nextPlayer)) continue; + if( + selected.isFull() && + !selected.canJoinFull(nextPlayer) && + !( + nextPlayer.hasPermission("ajqueue.make-room") && + main.getConfig().getBoolean("enable-make-room-permission") + ) + ) continue; + + + // ajqueue.make-room logic + if( + selected.isFull() && + !selected.canJoinFull(nextPlayer) && + main.getConfig().getBoolean("enable-make-room-permission") && + nextPlayer.hasPermission("ajqueue.make-room") && + !server.isGroup() + ) { + List players = selected.getPlayers(); + + // first, we need to find what the lowest priority on the server is + int lowestPriority = Integer.MAX_VALUE; + for (AdaptedPlayer player : players) { + int priority = main.getLogic().getPermissionGetter().getPriority(player); + if(priority < lowestPriority) lowestPriority = priority; + } + + boolean kickLongest = main.getConfig().getBoolean("make-room-kick-longest-playtime"); + + long selectedTime = kickLongest ? Long.MAX_VALUE : 0; + AdaptedPlayer selectedPlayer = null; + for (AdaptedPlayer player : players) { + long switchTime = main.getServerTimeManager().getLastServerChange(player); + if(selectedPlayer == null) { + selectedPlayer = player; + selectedTime = switchTime; + continue; + } + if(kickLongest) { + if(switchTime < selectedTime) { + selectedTime = switchTime; + selectedPlayer = player; + } + } else { + if(switchTime > selectedTime) { + selectedTime = switchTime; + selectedPlayer = player; + } + } + } + + + if(selectedPlayer == null) { + main.getLogger().warn( + "Unable to find player to kick from " + selected.getName() + " " + + "to let " + nextPlayer.getName() + "join!" + ); + } else { + Debug.info( + "Selected " + selectedPlayer.getName() + " " + + "to make room for " + nextPlayer.getName() + " in " + selected.getName() + ); + String kickToName = main.getConfig().getString("make-room-kick-to"); + AdaptedServer kickTo = main.getPlatformMethods().getServer(kickToName); + if(kickTo == null) { + main.getLogger().warn( + "Unable to make room due to '" + kickToName + "' not existing! " + + "Please configure make-room-kick-to in the config" + ); + boolean isAdmin = nextPlayer.hasPermission("ajqueue.manage"); + nextPlayer.sendMessage( + main.getMessages().getComponent( + isAdmin ? "errors.make-room-failed.admin" : "errors.make-room-failed.player" + ) + ); + } else { + selectedPlayer.connect(kickTo); + selectedPlayer.sendMessage(main.getMessages().getComponent("errors.kicked-to-make-room")); + + if(main.getTimeBetweenPlayers() >= 1d) { + nextPlayer.sendMessage(main.getMessages().getComponent("status.making-room")); + } + + continue; + } + } + } if(main.getConfig().getBoolean("enable-bypasspaused-permission")) { if(server.isPaused() && !nextPlayer.hasPermission("ajqueue.bypasspaused")) continue; diff --git a/common/src/main/java/us/ajg0702/queue/common/ServerTimeManagerImpl.java b/common/src/main/java/us/ajg0702/queue/common/ServerTimeManagerImpl.java new file mode 100644 index 0000000..5f37296 --- /dev/null +++ b/common/src/main/java/us/ajg0702/queue/common/ServerTimeManagerImpl.java @@ -0,0 +1,27 @@ +package us.ajg0702.queue.common; + +import us.ajg0702.queue.api.ServerTimeManager; +import us.ajg0702.queue.api.players.AdaptedPlayer; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ServerTimeManagerImpl implements ServerTimeManager { + + Map serverSwitches = new ConcurrentHashMap<>(); + + + @Override + public long getLastServerChange(AdaptedPlayer player) { + return serverSwitches.get(player.getUniqueId()); + } + + public void playerChanged(AdaptedPlayer player) { + serverSwitches.put(player.getUniqueId(), System.currentTimeMillis()); + } + + public void removePlayer(AdaptedPlayer player) { + serverSwitches.remove(player.getUniqueId()); + } +} diff --git a/common/src/main/resources/config.yml b/common/src/main/resources/config.yml index 05a0438..9b73ccd 100644 --- a/common/src/main/resources/config.yml +++ b/common/src/main/resources/config.yml @@ -129,6 +129,7 @@ give-pausedbypass-players-priority: 0 # What priority should we give players that are able to bypass full servers priority when the server is full? # This will have no effect if the server isn't full +# If you are using make-room, this also applies to that # If you set to 0, this will be disabled # If you have the free version, set it to 1 to enable give-fulljoin-players-priority: 0 @@ -279,6 +280,27 @@ require-queueserver-permission: false # Default: 10 max-tries: 10 + +# Should we enable the ajqueue.make-room permission? +# The make-room permission will force there to be room in a server. +# So, if a player with this permission queues for a server and has this permission, +# someone from the server will be moved to the lobby to make room +# This can be further configured using the next few options +# Default: false +enable-make-room-permission: false + +# What server should the make-room permission move players to? +# Default: lobby +make-room-kick-to: lobby + +# For the make-room permission, players with the lowest priority are kicked first. +# Of those players, this option decides which to kick. +# true - kick players who have been on the server the longest +# false - kick players who have been on nthe server the shortest +# Default: true +make-room-kick-longest-playtime: true + + # Should we check to make sure that people don't get sent quicker than wait-time? # Default: true check-last-player-sent-time: true @@ -377,4 +399,4 @@ debug: false # Don't touch this number please -config-version: 38 +config-version: 39