From f13e9cdf616030000eedb39e3c7678c7cd033dda Mon Sep 17 00:00:00 2001 From: AlexTheCoder Date: Tue, 5 Sep 2017 23:22:55 -0400 Subject: [PATCH] Clans queue system --- Plugins/Mineplex.ClansQueue.Common/pom.xml | 23 ++ .../clansqueue/common/ClansQueueMessage.java | 8 + .../common/ClansQueueMessageBody.java | 13 + .../common/ClansQueueMessenger.java | 115 ++++++ .../clansqueue/common/QueueConstant.java | 7 + .../clansqueue/common/SortableLinkedList.java | 13 + .../messages/ClansServerStatusMessage.java | 9 + .../PlayerJoinQueueCallbackMessage.java | 12 + .../messages/PlayerJoinQueueMessage.java | 12 + .../messages/PlayerLeaveQueueMessage.java | 11 + .../messages/PlayerSendToServerMessage.java | 11 + .../common/messages/QueueDeleteMessage.java | 8 + .../messages/QueuePauseBroadcastMessage.java | 9 + .../messages/QueuePauseUpdateMessage.java | 9 + .../common/messages/QueueStatusMessage.java | 20 + .../common/messages/ServerOfflineMessage.java | 8 + .../common/messages/ServerOnlineMessage.java | 8 + Plugins/Mineplex.ClansQueue/pom.xml | 23 ++ .../clansqueue/service/QueueService.java | 94 +++++ .../service/commands/CommandSystem.java | 64 ++++ .../service/commands/ConsoleCommand.java | 59 +++ .../service/commands/DeleteQueueCommand.java | 38 ++ .../service/commands/HelpCommand.java | 41 ++ .../service/commands/ListQueuesCommand.java | 29 ++ .../service/commands/PauseQueueCommand.java | 38 ++ .../service/commands/StopCommand.java | 21 ++ .../service/queue/ClansQueueManager.java | 198 ++++++++++ .../clansqueue/service/queue/ClansServer.java | 58 +++ .../clansqueue/service/queue/QueuePlayer.java | 33 ++ .../clansqueue/service/queue/ServerQueue.java | 222 +++++++++++ .../core/account/CoreClientManager.java | 1 - .../mineplex/core/updater/FileUpdater.java | 29 +- .../mineplex/core/updater/RestartHandler.java | 47 ++- .../updater/event/RestartServerEvent.java | 19 +- .../updater/event/RestartTriggerEvent.java | 50 +++ Plugins/Mineplex.Game.Clans/pom.xml | 5 + .../game/clans/clans/ClansManager.java | 38 +- .../game/clans/restart/RestartManager.java | 43 ++- Plugins/Mineplex.Hub.Clans/pom.xml | 5 + .../src/mineplex/clanshub/ClansHub.java | 2 +- .../mineplex/clanshub/ClansServerPage.java | 21 +- .../clanshub/ClansTransferManager.java | 35 +- .../mineplex/clanshub/JoinServerButton.java | 16 +- .../clanshub/queue/HubQueueManager.java | 353 ++++++++++++++++++ .../clanshub/queue/data/ClansQueueData.java | 30 ++ .../clanshub/queue/data/QueuePlayerData.java | 36 ++ Plugins/pom.xml | 2 + 47 files changed, 1858 insertions(+), 88 deletions(-) create mode 100644 Plugins/Mineplex.ClansQueue.Common/pom.xml create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/ClansQueueMessage.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/ClansQueueMessageBody.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/ClansQueueMessenger.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/QueueConstant.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/SortableLinkedList.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/ClansServerStatusMessage.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerJoinQueueCallbackMessage.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerJoinQueueMessage.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerLeaveQueueMessage.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerSendToServerMessage.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueueDeleteMessage.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueuePauseBroadcastMessage.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueuePauseUpdateMessage.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueueStatusMessage.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/ServerOfflineMessage.java create mode 100644 Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/ServerOnlineMessage.java create mode 100644 Plugins/Mineplex.ClansQueue/pom.xml create mode 100644 Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/QueueService.java create mode 100644 Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/CommandSystem.java create mode 100644 Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/ConsoleCommand.java create mode 100644 Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/DeleteQueueCommand.java create mode 100644 Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/HelpCommand.java create mode 100644 Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/ListQueuesCommand.java create mode 100644 Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/PauseQueueCommand.java create mode 100644 Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/StopCommand.java create mode 100644 Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/ClansQueueManager.java create mode 100644 Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/ClansServer.java create mode 100644 Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/QueuePlayer.java create mode 100644 Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/ServerQueue.java create mode 100644 Plugins/Mineplex.Core/src/mineplex/core/updater/event/RestartTriggerEvent.java create mode 100644 Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/queue/HubQueueManager.java create mode 100644 Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/queue/data/ClansQueueData.java create mode 100644 Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/queue/data/QueuePlayerData.java diff --git a/Plugins/Mineplex.ClansQueue.Common/pom.xml b/Plugins/Mineplex.ClansQueue.Common/pom.xml new file mode 100644 index 000000000..d468c56ca --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + + com.mineplex + mineplex-parent + dev-SNAPSHOT + ../plugin.xml + + + ClansQueue-Common + mineplex-clansqueue-common + + + + ${project.groupId} + mineplex-serverdata + ${project.version} + + + diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/ClansQueueMessage.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/ClansQueueMessage.java new file mode 100644 index 000000000..2bb7211c3 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/ClansQueueMessage.java @@ -0,0 +1,8 @@ +package com.mineplex.clansqueue.common; + +public class ClansQueueMessage +{ + protected String Origin; + protected String BodyClass; + protected String BodySerialized; +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/ClansQueueMessageBody.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/ClansQueueMessageBody.java new file mode 100644 index 000000000..9a81b2c75 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/ClansQueueMessageBody.java @@ -0,0 +1,13 @@ +package com.mineplex.clansqueue.common; + +import mineplex.serverdata.Utility; + +public abstract class ClansQueueMessageBody +{ + @Override + public final String toString() + { + super.toString(); + return Utility.serialize(this); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/ClansQueueMessenger.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/ClansQueueMessenger.java new file mode 100644 index 000000000..1de841e9f --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/ClansQueueMessenger.java @@ -0,0 +1,115 @@ +package com.mineplex.clansqueue.common; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; + +import mineplex.serverdata.Utility; +import mineplex.serverdata.servers.ServerManager; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPubSub; + +public class ClansQueueMessenger +{ + private static final String CHANNEL_NAME_BASE = "ClansQueueMessageChannel:"; + + private static final Map _messengers = new ConcurrentHashMap<>(); + + private final String _identifier; + private final JedisPool _readPool; + private final JedisPool _writePool; + @SuppressWarnings("rawtypes") + private final Map _bodyTypes = Collections.synchronizedMap(new HashMap<>()); + @SuppressWarnings("rawtypes") + private final Map> _listeners = Collections.synchronizedMap(new HashMap<>()); + + private ClansQueueMessenger(String identifier) + { + _identifier = identifier; + + _writePool = Utility.generatePool(ServerManager.getMasterConnection()); + _readPool = Utility.generatePool(ServerManager.getSlaveConnection()); + + initialize(); + } + + private void initialize() + { + new Thread("Clans Queue Messenger: " + _identifier) + { + public void run() + { + try (Jedis jedis = _readPool.getResource()) + { + jedis.subscribe(new ClansQueueMessageListener(ClansQueueMessenger.this), CHANNEL_NAME_BASE + "ALL", CHANNEL_NAME_BASE + _identifier); + } + } + }.start(); + } + + public void registerListener(Class messageType, BiConsumer callback) + { + _bodyTypes.putIfAbsent(messageType.getName(), messageType); + _listeners.computeIfAbsent(messageType.getName(), (type) -> new ArrayList<>()).add(callback); + } + + public void transmitMessage(ClansQueueMessageBody message) + { + transmitMessage(message, "ALL"); + } + + public void transmitMessage(ClansQueueMessageBody message, String target) + { + ClansQueueMessage msg = new ClansQueueMessage(); + msg.Origin = _identifier; + msg.BodyClass = message.getClass().getName(); + msg.BodySerialized = message.toString(); + + final String toSend = Utility.serialize(msg); + + new Thread(() -> + { + try (Jedis jedis = _writePool.getResource()) + { + jedis.publish(CHANNEL_NAME_BASE + target, toSend); + } + }).start(); + } + + @SuppressWarnings("unchecked") + public void receiveMessage(ClansQueueMessage message) + { + if (_listeners.containsKey(message.BodyClass) && _bodyTypes.containsKey(message.BodyClass)) + { + T body = Utility.deserialize(message.BodySerialized, (Class)_bodyTypes.get(message.BodyClass)); + _listeners.get(message.BodyClass).forEach(listener -> listener.accept(body, message.Origin)); + } + } + + private static class ClansQueueMessageListener extends JedisPubSub + { + private final ClansQueueMessenger _manager; + + private ClansQueueMessageListener(ClansQueueMessenger manager) + { + _manager = manager; + } + + @Override + public void onMessage(String channelName, String message) + { + ClansQueueMessage msg = Utility.deserialize(message, ClansQueueMessage.class); + _manager.receiveMessage(msg); + } + } + + public static ClansQueueMessenger getMessenger(String identifier) + { + return _messengers.computeIfAbsent(identifier, (id) -> new ClansQueueMessenger(id)); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/QueueConstant.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/QueueConstant.java new file mode 100644 index 000000000..505022227 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/QueueConstant.java @@ -0,0 +1,7 @@ +package com.mineplex.clansqueue.common; + +public class QueueConstant +{ + public static final String SERVICE_MESSENGER_IDENTIFIER = "Queue System"; + public static final int BYPASS_QUEUE_WEIGHT = -1; +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/SortableLinkedList.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/SortableLinkedList.java new file mode 100644 index 000000000..1cffc506a --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/SortableLinkedList.java @@ -0,0 +1,13 @@ +package com.mineplex.clansqueue.common; + +import java.util.LinkedList; + +public class SortableLinkedList> extends LinkedList +{ + private static final long serialVersionUID = -1751886037436467545L; + + public void sort() + { + sort((t1, t2) -> t1.compareTo(t2)); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/ClansServerStatusMessage.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/ClansServerStatusMessage.java new file mode 100644 index 000000000..929e084ee --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/ClansServerStatusMessage.java @@ -0,0 +1,9 @@ +package com.mineplex.clansqueue.common.messages; + +import com.mineplex.clansqueue.common.ClansQueueMessageBody; + +public class ClansServerStatusMessage extends ClansQueueMessageBody +{ + public String ServerName; + public int OpenSlots; +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerJoinQueueCallbackMessage.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerJoinQueueCallbackMessage.java new file mode 100644 index 000000000..73fa93238 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerJoinQueueCallbackMessage.java @@ -0,0 +1,12 @@ +package com.mineplex.clansqueue.common.messages; + +import java.util.UUID; + +import com.mineplex.clansqueue.common.ClansQueueMessageBody; + +public class PlayerJoinQueueCallbackMessage extends ClansQueueMessageBody +{ + public UUID PlayerUUID; + public String TargetServer; + public int Position; +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerJoinQueueMessage.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerJoinQueueMessage.java new file mode 100644 index 000000000..c31946294 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerJoinQueueMessage.java @@ -0,0 +1,12 @@ +package com.mineplex.clansqueue.common.messages; + +import java.util.UUID; + +import com.mineplex.clansqueue.common.ClansQueueMessageBody; + +public class PlayerJoinQueueMessage extends ClansQueueMessageBody +{ + public UUID PlayerUUID; + public String TargetServer; + public int PlayerPriority; +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerLeaveQueueMessage.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerLeaveQueueMessage.java new file mode 100644 index 000000000..dc177c457 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerLeaveQueueMessage.java @@ -0,0 +1,11 @@ +package com.mineplex.clansqueue.common.messages; + +import java.util.UUID; + +import com.mineplex.clansqueue.common.ClansQueueMessageBody; + +public class PlayerLeaveQueueMessage extends ClansQueueMessageBody +{ + public UUID PlayerUUID; + public String TargetServer; +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerSendToServerMessage.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerSendToServerMessage.java new file mode 100644 index 000000000..b1fe5fff0 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/PlayerSendToServerMessage.java @@ -0,0 +1,11 @@ +package com.mineplex.clansqueue.common.messages; + +import java.util.UUID; + +import com.mineplex.clansqueue.common.ClansQueueMessageBody; + +public class PlayerSendToServerMessage extends ClansQueueMessageBody +{ + public UUID PlayerUUID; + public String TargetServer; +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueueDeleteMessage.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueueDeleteMessage.java new file mode 100644 index 000000000..d8594724b --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueueDeleteMessage.java @@ -0,0 +1,8 @@ +package com.mineplex.clansqueue.common.messages; + +import com.mineplex.clansqueue.common.ClansQueueMessageBody; + +public class QueueDeleteMessage extends ClansQueueMessageBody +{ + public String ServerName; +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueuePauseBroadcastMessage.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueuePauseBroadcastMessage.java new file mode 100644 index 000000000..b871ee252 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueuePauseBroadcastMessage.java @@ -0,0 +1,9 @@ +package com.mineplex.clansqueue.common.messages; + +import com.mineplex.clansqueue.common.ClansQueueMessageBody; + +public class QueuePauseBroadcastMessage extends ClansQueueMessageBody +{ + public String ServerName; + public boolean Paused; +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueuePauseUpdateMessage.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueuePauseUpdateMessage.java new file mode 100644 index 000000000..c17064447 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueuePauseUpdateMessage.java @@ -0,0 +1,9 @@ +package com.mineplex.clansqueue.common.messages; + +import com.mineplex.clansqueue.common.ClansQueueMessageBody; + +public class QueuePauseUpdateMessage extends ClansQueueMessageBody +{ + public String ServerName; + public boolean Paused; +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueueStatusMessage.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueueStatusMessage.java new file mode 100644 index 000000000..135acb4cc --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/QueueStatusMessage.java @@ -0,0 +1,20 @@ +package com.mineplex.clansqueue.common.messages; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import com.mineplex.clansqueue.common.ClansQueueMessageBody; + +public class QueueStatusMessage extends ClansQueueMessageBody +{ + public final List Snapshots = new ArrayList<>(); + + public static class QueueSnapshot + { + public String ServerName; + public Map Queue; + public boolean Paused; + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/ServerOfflineMessage.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/ServerOfflineMessage.java new file mode 100644 index 000000000..51761ccb9 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/ServerOfflineMessage.java @@ -0,0 +1,8 @@ +package com.mineplex.clansqueue.common.messages; + +import com.mineplex.clansqueue.common.ClansQueueMessageBody; + +public class ServerOfflineMessage extends ClansQueueMessageBody +{ + public String ServerName; +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/ServerOnlineMessage.java b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/ServerOnlineMessage.java new file mode 100644 index 000000000..0d95cc1aa --- /dev/null +++ b/Plugins/Mineplex.ClansQueue.Common/src/com/mineplex/clansqueue/common/messages/ServerOnlineMessage.java @@ -0,0 +1,8 @@ +package com.mineplex.clansqueue.common.messages; + +import com.mineplex.clansqueue.common.ClansQueueMessageBody; + +public class ServerOnlineMessage extends ClansQueueMessageBody +{ + public String ServerName; +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue/pom.xml b/Plugins/Mineplex.ClansQueue/pom.xml new file mode 100644 index 000000000..e1e058ba4 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + + com.mineplex + mineplex-parent + dev-SNAPSHOT + ../plugin.xml + + + ClansQueue-Common + mineplex-clansqueue + + + + ${project.groupId} + mineplex-clansqueue-common + ${project.version} + + + diff --git a/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/QueueService.java b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/QueueService.java new file mode 100644 index 000000000..c2168c01e --- /dev/null +++ b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/QueueService.java @@ -0,0 +1,94 @@ +package com.mineplex.clansqueue.service; + +import java.io.File; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.mineplex.clansqueue.common.ClansQueueMessenger; +import com.mineplex.clansqueue.common.QueueConstant; +import com.mineplex.clansqueue.common.messages.ClansServerStatusMessage; +import com.mineplex.clansqueue.common.messages.PlayerJoinQueueMessage; +import com.mineplex.clansqueue.common.messages.PlayerLeaveQueueMessage; +import com.mineplex.clansqueue.common.messages.QueuePauseUpdateMessage; +import com.mineplex.clansqueue.common.messages.ServerOfflineMessage; +import com.mineplex.clansqueue.common.messages.ServerOnlineMessage; +import com.mineplex.clansqueue.service.commands.CommandSystem; +import com.mineplex.clansqueue.service.commands.ConsoleCommand; +import com.mineplex.clansqueue.service.queue.ClansQueueManager; + +import mineplex.serverdata.Region; + +public class QueueService +{ + public static void main(String[] args) + { + QueueService service = new QueueService(new File("eu.dat").exists()); + service.start(); + while (service.isRunning()) {}; + } + + private final Region _region; + private final AtomicBoolean _running; + private final Map _commandMap = Collections.synchronizedMap(new HashMap<>()); + private final CommandSystem _commandSystem; + private final ClansQueueManager _queueManager; + + private QueueService(boolean eu) + { + if (eu) + { + _region = Region.EU; + } + else + { + _region = Region.US; + } + _running = new AtomicBoolean(); + _commandSystem = new CommandSystem(this, _commandMap); + _queueManager = new ClansQueueManager(this); + } + + private void start() + { + System.out.println("[Queue Service] Enabling on region " + getRegion().name()); + _running.set(true); + _commandSystem.start(); + _queueManager.start(); + + ClansQueueMessenger messenger = ClansQueueMessenger.getMessenger(QueueConstant.SERVICE_MESSENGER_IDENTIFIER); + messenger.registerListener(ServerOnlineMessage.class, (online, origin) -> _queueManager.handleServerEnable(online.ServerName)); + messenger.registerListener(ServerOfflineMessage.class, (offline, origin) -> _queueManager.handleServerDisable(offline.ServerName)); + messenger.registerListener(QueuePauseUpdateMessage.class, (pause, origin) -> _queueManager.handleQueuePause(pause.ServerName, pause.Paused)); + messenger.registerListener(PlayerJoinQueueMessage.class, (join, origin) -> _queueManager.joinQueue(join.TargetServer, origin, join.PlayerUUID, join.PlayerPriority)); + messenger.registerListener(PlayerLeaveQueueMessage.class, (leave, origin) -> _queueManager.leaveQueue(leave.TargetServer, leave.PlayerUUID)); + messenger.registerListener(ClansServerStatusMessage.class, (status, origin) -> _queueManager.handleServerUpdate(status.ServerName, status.OpenSlots)); + } + + public ClansQueueManager getQueueManager() + { + return _queueManager; + } + + public boolean isRunning() + { + return _running.get(); + } + + public Region getRegion() + { + return _region; + } + + public void registerCommand(ConsoleCommand command) + { + _commandMap.put(command.getCommand().toLowerCase(), command); + } + + public void shutdown() + { + System.out.println("[Queue Service] Shutting down..."); + _running.set(false); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/CommandSystem.java b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/CommandSystem.java new file mode 100644 index 000000000..832a55097 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/CommandSystem.java @@ -0,0 +1,64 @@ +package com.mineplex.clansqueue.service.commands; + +import java.util.Map; +import java.util.Optional; +import java.util.Scanner; + +import com.mineplex.clansqueue.service.QueueService; + +public class CommandSystem extends Thread +{ + private final QueueService _service; + private final Map _commands; + + public CommandSystem(QueueService service, Map commands) + { + super("Command System"); + _service = service; + _commands = commands; + + _service.registerCommand(new HelpCommand(_commands)); + _service.registerCommand(new StopCommand(_service)); + _service.registerCommand(new DeleteQueueCommand(_service)); + _service.registerCommand(new ListQueuesCommand(_service)); + _service.registerCommand(new PauseQueueCommand(_service)); + } + + private boolean matches(String key, String input) + { + if (key.equalsIgnoreCase(input)) + { + return true; + } + if (input.toLowerCase().startsWith(key + " ")) + { + return true; + } + return false; + } + + @Override + public void run() + { + try (Scanner scanner = new Scanner(System.in)) + { + while (_service.isRunning()) + { + String input = scanner.nextLine(); + if (input.isEmpty()) + { + continue; + } + Optional opt = _commands.entrySet().stream().filter(entry -> matches(entry.getKey(), input)).map(Map.Entry::getValue).findAny(); + if (opt.isPresent()) + { + opt.get().call(input); + } + else + { + System.out.println("Command '" + input.split(" ")[0] + "' was not found. Run 'help' for a list of commands."); + } + } + } + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/ConsoleCommand.java b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/ConsoleCommand.java new file mode 100644 index 000000000..f962d4dd6 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/ConsoleCommand.java @@ -0,0 +1,59 @@ +package com.mineplex.clansqueue.service.commands; + +public abstract class ConsoleCommand +{ + private final String _command; + private final String _usageText; + private StringBuilder _outputBuilder; + + public ConsoleCommand(String command, String usageText) + { + _command = command; + _usageText = usageText; + } + + public String getCommand() + { + return _command; + } + + public String getUsageText() + { + return _usageText; + } + + protected final void addOutput(String text) + { + if (_outputBuilder == null) + { + _outputBuilder = new StringBuilder(); + } + else + { + _outputBuilder.append("\n"); + } + _outputBuilder.append(text); + } + + protected final void sendOutput() + { + System.out.println(_outputBuilder.toString()); + _outputBuilder = null; + } + + public final void call(String input) + { + String parsing = input.trim(); + if (parsing.length() > getCommand().length() + 2) + { + String[] args = parsing.substring(getCommand().length() + 1).split(" "); + use(args); + } + else + { + use(new String[] {}); + } + } + + protected abstract void use(String[] arguments); +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/DeleteQueueCommand.java b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/DeleteQueueCommand.java new file mode 100644 index 000000000..fd169d08e --- /dev/null +++ b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/DeleteQueueCommand.java @@ -0,0 +1,38 @@ +package com.mineplex.clansqueue.service.commands; + +import com.mineplex.clansqueue.service.QueueService; +import com.mineplex.clansqueue.service.queue.ClansServer; + +public class DeleteQueueCommand extends ConsoleCommand +{ + private final QueueService _service; + + public DeleteQueueCommand(QueueService service) + { + super("delete", "Deletes an existing server and queue"); + + _service = service; + } + + @Override + protected void use(String[] arguments) + { + if (arguments.length < 1) + { + addOutput("Usage: delete "); + sendOutput(); + return; + } + ClansServer server = _service.getQueueManager().getLoadedServer(arguments[0]); + if (server == null) + { + addOutput("Server '" + arguments[0] + "' was not found. Run 'list' for a list of servers."); + sendOutput(); + return; + } + + _service.getQueueManager().deleteServer(server); + addOutput("Server and queue deleted."); + sendOutput(); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/HelpCommand.java b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/HelpCommand.java new file mode 100644 index 000000000..808c1ab2d --- /dev/null +++ b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/HelpCommand.java @@ -0,0 +1,41 @@ +package com.mineplex.clansqueue.service.commands; + +import java.util.Map; + +public class HelpCommand extends ConsoleCommand +{ + private final Map _commands; + + public HelpCommand(Map commands) + { + super("help", "Lists commands and their usage"); + + _commands = commands; + } + + @Override + protected void use(String[] arguments) + { + if (arguments.length < 1) + { + addOutput("Commands:"); + _commands.values().forEach(command -> + { + addOutput(command.getCommand() + " : " + command.getUsageText()); + }); + } + else + { + if (_commands.containsKey(arguments[0].toLowerCase())) + { + ConsoleCommand cmd = _commands.get(arguments[0].toLowerCase()); + addOutput(cmd.getCommand() + " : " + cmd.getUsageText()); + } + else + { + addOutput("Command '" + arguments[0] + "' was not found. Run 'help' for a list of commands."); + } + } + sendOutput(); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/ListQueuesCommand.java b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/ListQueuesCommand.java new file mode 100644 index 000000000..55de6784c --- /dev/null +++ b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/ListQueuesCommand.java @@ -0,0 +1,29 @@ +package com.mineplex.clansqueue.service.commands; + +import java.util.stream.Collectors; + +import com.mineplex.clansqueue.service.QueueService; +import com.mineplex.clansqueue.service.queue.ClansServer; + +public class ListQueuesCommand extends ConsoleCommand +{ + private final QueueService _service; + + public ListQueuesCommand(QueueService service) + { + super("list", "Lists existing servers"); + + _service = service; + } + + @Override + protected void use(String[] arguments) + { + StringBuilder servers = new StringBuilder("Servers: ["); + servers.append(_service.getQueueManager().getLoadedServers().stream().map(ClansServer::getName).collect(Collectors.joining(", "))); + servers.append(']'); + + addOutput(servers.toString()); + sendOutput(); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/PauseQueueCommand.java b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/PauseQueueCommand.java new file mode 100644 index 000000000..c417883e6 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/PauseQueueCommand.java @@ -0,0 +1,38 @@ +package com.mineplex.clansqueue.service.commands; + +import com.mineplex.clansqueue.service.QueueService; +import com.mineplex.clansqueue.service.queue.ClansServer; + +public class PauseQueueCommand extends ConsoleCommand +{ + private final QueueService _service; + + public PauseQueueCommand(QueueService service) + { + super("pause", "Pauses an existing queue"); + + _service = service; + } + + @Override + protected void use(String[] arguments) + { + if (arguments.length < 1) + { + addOutput("Usage: pause "); + sendOutput(); + return; + } + ClansServer server = _service.getQueueManager().getLoadedServer(arguments[0]); + if (server == null) + { + addOutput("Server '" + arguments[0] + "' was not found. Run 'list' for a list of servers."); + sendOutput(); + return; + } + + _service.getQueueManager().handleQueuePause(server.getName(), true); + addOutput("Queue paused."); + sendOutput(); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/StopCommand.java b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/StopCommand.java new file mode 100644 index 000000000..a1c100d64 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/commands/StopCommand.java @@ -0,0 +1,21 @@ +package com.mineplex.clansqueue.service.commands; + +import com.mineplex.clansqueue.service.QueueService; + +public class StopCommand extends ConsoleCommand +{ + private final QueueService _service; + + public StopCommand(QueueService service) + { + super("stop", "Stops the Queue Service"); + + _service = service; + } + + @Override + protected void use(String[] arguments) + { + _service.shutdown(); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/ClansQueueManager.java b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/ClansQueueManager.java new file mode 100644 index 000000000..5c63540b2 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/ClansQueueManager.java @@ -0,0 +1,198 @@ +package com.mineplex.clansqueue.service.queue; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import com.mineplex.clansqueue.common.ClansQueueMessenger; +import com.mineplex.clansqueue.common.QueueConstant; +import com.mineplex.clansqueue.common.messages.PlayerJoinQueueCallbackMessage; +import com.mineplex.clansqueue.common.messages.PlayerSendToServerMessage; +import com.mineplex.clansqueue.common.messages.QueueDeleteMessage; +import com.mineplex.clansqueue.common.messages.QueuePauseBroadcastMessage; +import com.mineplex.clansqueue.common.messages.QueueStatusMessage; +import com.mineplex.clansqueue.common.messages.QueueStatusMessage.QueueSnapshot; +import com.mineplex.clansqueue.service.QueueService; + +public class ClansQueueManager +{ + private final Map _servers = new HashMap<>(); + private final Map _queues = new HashMap<>(); + private final Thread _updater; + + public ClansQueueManager(QueueService service) + { + _updater = new Thread(() -> + { + while (service.isRunning()) + { + try + { + updateQueues(); + Thread.sleep(5000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + }, "Queue Update Thread"); + } + + private QueueStatusMessage buildStatusMessage(Collection queues) + { + QueueStatusMessage message = new QueueStatusMessage(); + + queues.forEach(queue -> + { + QueueSnapshot snapshot = new QueueSnapshot(); + snapshot.Paused = queue.isPaused(); + snapshot.ServerName = queue.getServer().getName(); + snapshot.Queue = new HashMap<>(); + queue.getPlayers().values().forEach(player -> snapshot.Queue.put(player.PlayerUUID, player.Position)); + }); + + return message; + } + + private synchronized void updateQueues() + { + System.out.println("Updating queues"); + Collection queues = _queues.values(); + + queues.forEach(q -> + { + q.updatePositions(q.getServer().getOpenSlots()); + if (q.getServer().isOnline()) + { + q.getNextSend().entrySet().forEach(entry -> + { + PlayerSendToServerMessage message = new PlayerSendToServerMessage(); + message.PlayerUUID = entry.getKey(); + message.TargetServer = q.getServer().getName(); + ClansQueueMessenger.getMessenger(QueueConstant.SERVICE_MESSENGER_IDENTIFIER).transmitMessage(message, entry.getValue()); + }); + } + }); + + QueueStatusMessage message = buildStatusMessage(queues); + ClansQueueMessenger.getMessenger(QueueConstant.SERVICE_MESSENGER_IDENTIFIER).transmitMessage(message); + } + + public synchronized ClansServer getLoadedServer(String serverName) + { + return _servers.get(serverName); + } + + public synchronized Collection getLoadedServers() + { + return _servers.values(); + } + + public synchronized void deleteServer(ClansServer server) + { + _servers.remove(server.getName()); + _queues.remove(server); + QueueDeleteMessage message = new QueueDeleteMessage(); + ClansQueueMessenger.getMessenger(QueueConstant.SERVICE_MESSENGER_IDENTIFIER).transmitMessage(message); + } + + public synchronized void handleServerEnable(String serverName) + { + _servers.computeIfAbsent(serverName, (name) -> + { + ClansServer server = new ClansServer(name); + + _queues.put(server, new ServerQueue(server)); + + return server; + }).setOnline(true); + + System.out.println("Clans server " + serverName + " enabled."); + } + + public synchronized void handleServerDisable(String serverName) + { + _servers.computeIfAbsent(serverName, (name) -> + { + ClansServer server = new ClansServer(name); + + _queues.put(server, new ServerQueue(server)); + + return server; + }).setOnline(false); + } + + public synchronized void handleServerUpdate(String serverName, int openSlots) + { + _servers.computeIfAbsent(serverName, (name) -> + { + ClansServer server = new ClansServer(name); + + _queues.put(server, new ServerQueue(server)); + + return server; + }).setOpenSlots(openSlots); + } + + public synchronized void handleQueuePause(String serverName, boolean pause) + { + ClansServer server = _servers.get(serverName); + if (server != null) + { + _queues.get(server).setPaused(pause); + System.out.println("Clans server " + serverName + " queue pause: " + pause); + QueuePauseBroadcastMessage message = new QueuePauseBroadcastMessage(); + message.ServerName = serverName; + message.Paused = pause; + ClansQueueMessenger.getMessenger(QueueConstant.SERVICE_MESSENGER_IDENTIFIER).transmitMessage(message); + } + } + + public synchronized void joinQueue(String serverName, String currentServer, UUID uuid, int weight) + { + ClansServer server = _servers.get(serverName); + if (server != null) + { + ServerQueue queue = _queues.get(server); + if (weight == QueueConstant.BYPASS_QUEUE_WEIGHT) + { + queue.addBypasser(uuid, currentServer); + } + else + { + queue.addPlayer(uuid, currentServer, weight, player -> + { + PlayerJoinQueueCallbackMessage message = new PlayerJoinQueueCallbackMessage(); + message.PlayerUUID = uuid; + message.TargetServer = serverName; + message.Position = player.Position; + ClansQueueMessenger.getMessenger(QueueConstant.SERVICE_MESSENGER_IDENTIFIER).transmitMessage(message, currentServer); + QueueStatusMessage update = buildStatusMessage(Arrays.asList(queue)); + ClansQueueMessenger.getMessenger(QueueConstant.SERVICE_MESSENGER_IDENTIFIER).transmitMessage(update); + }); + } + } + } + + public synchronized void leaveQueue(String serverName, UUID uuid) + { + ClansServer server = _servers.get(serverName); + if (server != null) + { + ServerQueue queue = _queues.get(server); + queue.removePlayer(uuid, () -> + { + QueueStatusMessage message = buildStatusMessage(Arrays.asList(queue)); + ClansQueueMessenger.getMessenger(QueueConstant.SERVICE_MESSENGER_IDENTIFIER).transmitMessage(message); + }); + } + } + + public void start() + { + _updater.start(); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/ClansServer.java b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/ClansServer.java new file mode 100644 index 000000000..375a7ebc6 --- /dev/null +++ b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/ClansServer.java @@ -0,0 +1,58 @@ +package com.mineplex.clansqueue.service.queue; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +public class ClansServer +{ + private final String _serverName; + private final AtomicBoolean _online = new AtomicBoolean(); + private final AtomicInteger _openSlots = new AtomicInteger(); + + public ClansServer(String serverName) + { + _serverName = serverName; + } + + public String getName() + { + return _serverName; + } + + public boolean isOnline() + { + return _online.get(); + } + + public void setOnline(boolean online) + { + _online.set(online); + } + + public int getOpenSlots() + { + return _openSlots.get(); + } + + public void setOpenSlots(int openSlots) + { + _openSlots.set(openSlots); + } + + @Override + public int hashCode() + { + return _serverName.hashCode(); + } + + @Override + public boolean equals(Object o) + { + if (o == null || !getClass().isInstance(o)) + { + return false; + } + + return ((ClansServer)o)._serverName.equals(_serverName); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/QueuePlayer.java b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/QueuePlayer.java new file mode 100644 index 000000000..2c5048d8b --- /dev/null +++ b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/QueuePlayer.java @@ -0,0 +1,33 @@ +package com.mineplex.clansqueue.service.queue; + +import java.util.UUID; + +public class QueuePlayer +{ + public final UUID PlayerUUID; + public final String CurrentServer; + public int Position; + + public QueuePlayer(UUID uuid, String currentServer) + { + PlayerUUID = uuid; + CurrentServer = currentServer; + } + + @Override + public int hashCode() + { + return PlayerUUID.hashCode(); + } + + @Override + public boolean equals(Object o) + { + if (o == null || !getClass().isInstance(o)) + { + return false; + } + + return ((QueuePlayer)o).PlayerUUID.equals(PlayerUUID); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/ServerQueue.java b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/ServerQueue.java new file mode 100644 index 000000000..e9e269efc --- /dev/null +++ b/Plugins/Mineplex.ClansQueue/src/com/mineplex/clansqueue/service/queue/ServerQueue.java @@ -0,0 +1,222 @@ +package com.mineplex.clansqueue.service.queue; + +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Optional; +import java.util.Queue; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +import com.mineplex.clansqueue.common.SortableLinkedList; + +public class ServerQueue +{ + private final ClansServer _server; + private final Map _sending = new LinkedHashMap<>(); + private final Map _bypassing = new LinkedHashMap<>(); + private final SortableLinkedList _queues = new SortableLinkedList<>(); + private final Object _bypassLock = new Object(); + private final Object _queueLock = new Object(); + private final Object _sendLock = new Object(); + private final AtomicBoolean _paused = new AtomicBoolean(); + + public ServerQueue(ClansServer server) + { + _server = server; + } + + public ClansServer getServer() + { + return _server; + } + + public boolean isPaused() + { + return _paused.get() || !_server.isOnline(); + } + + public Map getNextSend() + { + synchronized (_sendLock) + { + Map sending = new LinkedHashMap<>(); + sending.putAll(_sending); + _sending.clear(); + return sending; + } + } + + public Map getPlayers() + { + synchronized (_queueLock) + { + Map players = new LinkedHashMap<>(); + _queues.forEach(queue -> queue._players.forEach(qp -> players.put(qp.PlayerUUID, qp))); + + return players; + } + } + + public void addBypasser(UUID uuid, String currentServer) + { + synchronized (_bypassLock) + { + _bypassing.put(uuid, currentServer); + } + } + + public void addPlayer(UUID uuid, String currentServer, int weight, Consumer callback) + { + synchronized (_queueLock) + { + Optional queueOpt = _queues.stream().filter(q -> q._weight == weight).findFirst(); + PlayerQueue queue = queueOpt.orElseGet(() -> + { + PlayerQueue creating = new PlayerQueue(weight); + if (_queues.add(creating)) + { + _queues.sort(); + } + + return creating; + }); + + QueuePlayer player = new QueuePlayer(uuid, currentServer); + queue._players.add(player); + + AtomicInteger position = new AtomicInteger(1); + if (_queues.removeIf(q -> q._players.isEmpty())) + { + _queues.sort(); + } + _queues.forEach(q -> q._players.forEach(qp -> + { + qp.Position = position.getAndIncrement(); + })); + + if (callback != null) + { + callback.accept(player); + } + } + } + + public void removePlayer(UUID uuid, Runnable after) + { + synchronized (_queueLock) + { + _queues.forEach(queue -> queue._players.removeIf(player -> player.PlayerUUID.equals(uuid))); + + AtomicInteger position = new AtomicInteger(1); + if (_queues.removeIf(q -> q._players.isEmpty())) + { + _queues.sort(); + } + _queues.forEach(q -> q._players.forEach(qp -> + { + qp.Position = position.getAndIncrement(); + })); + + if (after != null) + { + after.run(); + } + } + } + + public void setPaused(boolean paused) + { + _paused.set(paused); + } + + public void updatePositions(int openPlayerSlots) + { + Map send = new LinkedHashMap<>(); + if (_server.isOnline()) + { + synchronized (_bypassLock) + { + send.putAll(_bypassing); + _bypassing.clear(); + } + } + synchronized (_queueLock) + { + if (!isPaused() && openPlayerSlots > 0) + { + while (send.size() < openPlayerSlots) + { + if (_queues.removeIf(queue -> queue._players.isEmpty())) + { + _queues.sort(); + } + PlayerQueue queue = _queues.peek(); + if (queue == null) + { + break; + } + QueuePlayer player = queue._players.poll(); + send.put(player.PlayerUUID, player.CurrentServer); + } + } + AtomicInteger position = new AtomicInteger(1); + if (_queues.removeIf(queue -> queue._players.isEmpty())) + { + _queues.sort(); + } + _queues.forEach(queue -> queue._players.forEach(qp -> + { + qp.Position = position.getAndIncrement(); + })); + } + if (send.isEmpty()) + { + return; + } + synchronized (_sendLock) + { + _sending.putAll(send); + } + } + + private static class PlayerQueue implements Comparable + { + private final int _weight; + private final Queue _players = new LinkedList<>(); + + private PlayerQueue(int weight) + { + _weight = weight; + } + + @Override + public int compareTo(PlayerQueue queue) + { + if (queue == null) + { + throw new NullPointerException(); + } + return Integer.compare(queue._weight, _weight); + } + + @Override + public int hashCode() + { + return Integer.hashCode(_weight); + } + + @Override + public boolean equals(Object o) + { + if (o == null || !getClass().isInstance(o)) + { + return false; + } + + return ((PlayerQueue)o)._weight == _weight; + } + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.Core/src/mineplex/core/account/CoreClientManager.java b/Plugins/Mineplex.Core/src/mineplex/core/account/CoreClientManager.java index 0c6a0e75e..8089ec178 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/account/CoreClientManager.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/account/CoreClientManager.java @@ -617,7 +617,6 @@ public class CoreClientManager extends MiniPlugin if (client.hasPermission(Perm.JOIN_FULL)) { event.allow(); - event.setResult(PlayerLoginEvent.Result.ALLOWED); return; } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/updater/FileUpdater.java b/Plugins/Mineplex.Core/src/mineplex/core/updater/FileUpdater.java index 90b9c8919..e72629d7b 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/updater/FileUpdater.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/updater/FileUpdater.java @@ -22,12 +22,14 @@ import mineplex.core.account.permissions.PermissionGroup; import mineplex.core.common.util.C; import mineplex.core.common.util.F; import mineplex.core.common.util.NautHashMap; +import mineplex.core.common.util.UtilServer; import mineplex.core.portal.GenericServer; import mineplex.core.portal.Intent; import mineplex.core.portal.Portal; import mineplex.core.updater.command.BuildVersionCommand; import mineplex.core.updater.command.RestartServerCommand; import mineplex.core.updater.event.RestartServerEvent; +import mineplex.core.updater.event.RestartTriggerEvent; import mineplex.core.updater.event.UpdateEvent; import mineplex.serverdata.Region; import mineplex.serverdata.commands.RestartCommand; @@ -50,6 +52,7 @@ public class FileUpdater extends MiniPlugin private boolean _needUpdate; private boolean _enabled = true; + private boolean _restartTriggered = false; private Properties _buildProperties; @@ -77,7 +80,6 @@ public class FileUpdater extends MiniPlugin private void generatePermissions() { - PermissionGroup.MOD.setPermission(Perm.BVERSION_COMMAND, true, true); PermissionGroup.ADMIN.setPermission(Perm.RESTART_COMMAND, true, true); PermissionGroup.QAM.setPermission(Perm.RESTART_COMMAND, false, true); @@ -112,10 +114,17 @@ public class FileUpdater extends MiniPlugin if (event.getType() != UpdateType.SLOWER) return; - if (!_needUpdate || !_enabled) + if (!_needUpdate || !_enabled || _restartTriggered) return; - RestartServerEvent restartEvent = new RestartServerEvent(); + if (UtilServer.CallEvent(new RestartTriggerEvent(RestartTriggerEvent.RestartReason.UPDATE)).isCancelled()) + { + return; + } + + _restartTriggered = true; + + RestartServerEvent restartEvent = new RestartServerEvent(RestartServerEvent.RestartReason.UPDATE); getPluginManager().callEvent(restartEvent); @@ -126,20 +135,14 @@ public class FileUpdater extends MiniPlugin player.sendMessage(F.main("Updater", C.cGold + _serverName + C.cGray + " is restarting for an update.")); } - getPlugin().getServer().getScheduler().scheduleSyncDelayedTask(getPlugin(), new Runnable() + getPlugin().getServer().getScheduler().scheduleSyncDelayedTask(getPlugin(), () -> { - public void run() - { - _portal.sendAllPlayersToGenericServer(_transferHub, Intent.KICK); - } + _portal.sendAllPlayersToGenericServer(_transferHub, Intent.KICK); }, 60L); - getPlugin().getServer().getScheduler().scheduleSyncDelayedTask(getPlugin(), new Runnable() + getPlugin().getServer().getScheduler().scheduleSyncDelayedTask(getPlugin(), () -> { - public void run() - { - getPlugin().getServer().shutdown(); - } + getPlugin().getServer().shutdown(); }, 100L); } } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/updater/RestartHandler.java b/Plugins/Mineplex.Core/src/mineplex/core/updater/RestartHandler.java index e37961ef9..9001426f6 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/updater/RestartHandler.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/updater/RestartHandler.java @@ -1,14 +1,5 @@ package mineplex.core.updater; -import mineplex.core.common.util.F; -import mineplex.core.portal.GenericServer; -import mineplex.core.portal.Intent; -import mineplex.core.portal.Portal; -import mineplex.serverdata.Region; -import mineplex.serverdata.commands.CommandCallback; -import mineplex.serverdata.commands.RestartCommand; -import mineplex.serverdata.commands.ServerCommand; - import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -17,6 +8,18 @@ import org.bukkit.event.Listener; import org.bukkit.event.server.ServerListPingEvent; import org.bukkit.plugin.java.JavaPlugin; +import mineplex.core.common.util.F; +import mineplex.core.common.util.UtilServer; +import mineplex.core.portal.GenericServer; +import mineplex.core.portal.Intent; +import mineplex.core.portal.Portal; +import mineplex.core.updater.event.RestartServerEvent; +import mineplex.core.updater.event.RestartTriggerEvent; +import mineplex.serverdata.Region; +import mineplex.serverdata.commands.CommandCallback; +import mineplex.serverdata.commands.RestartCommand; +import mineplex.serverdata.commands.ServerCommand; + public class RestartHandler implements CommandCallback, Listener { private JavaPlugin _plugin; @@ -51,6 +54,16 @@ public class RestartHandler implements CommandCallback, Listener if (!serverName.equalsIgnoreCase(_serverName) || _region != region) return; + if (UtilServer.CallEvent(new RestartTriggerEvent(RestartTriggerEvent.RestartReason.COMMAND)).isCancelled()) + { + return; + } + + if (UtilServer.CallEvent(new RestartServerEvent(RestartServerEvent.RestartReason.COMMAND)).isCancelled()) + { + return; + } + _restarting = true; for (Player player : Bukkit.getOnlinePlayers()) @@ -58,21 +71,15 @@ public class RestartHandler implements CommandCallback, Listener player.sendMessage(F.main("Restart", "Server is restarting, you're being sent to a lobby.")); } - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Bukkit.getPluginManager().getPlugins()[0], new Runnable() + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(UtilServer.getPlugin(), () -> { - public void run() - { - Portal.getInstance().sendAllPlayersToGenericServer(GenericServer.HUB, Intent.KICK); - } + Portal.getInstance().sendAllPlayersToGenericServer(GenericServer.HUB, Intent.KICK); }, 60L); - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Bukkit.getPluginManager().getPlugins()[0], new Runnable() + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(UtilServer.getPlugin(), () -> { - public void run() - { - Bukkit.getServer().shutdown(); - } + Bukkit.getServer().shutdown(); }, 100L); } } -} +} \ No newline at end of file diff --git a/Plugins/Mineplex.Core/src/mineplex/core/updater/event/RestartServerEvent.java b/Plugins/Mineplex.Core/src/mineplex/core/updater/event/RestartServerEvent.java index a2c7e4fe5..072b6ded6 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/updater/event/RestartServerEvent.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/updater/event/RestartServerEvent.java @@ -8,6 +8,12 @@ public class RestartServerEvent extends Event implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean _cancelled = false; + private final RestartReason _reason; + + public RestartServerEvent(RestartReason reason) + { + _reason = reason; + } public HandlerList getHandlers() { @@ -18,6 +24,11 @@ public class RestartServerEvent extends Event implements Cancellable { return handlers; } + + public RestartReason getReason() + { + return _reason; + } @Override public boolean isCancelled() @@ -30,4 +41,10 @@ public class RestartServerEvent extends Event implements Cancellable { _cancelled = cancel; } -} + + public enum RestartReason + { + COMMAND, + UPDATE + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.Core/src/mineplex/core/updater/event/RestartTriggerEvent.java b/Plugins/Mineplex.Core/src/mineplex/core/updater/event/RestartTriggerEvent.java new file mode 100644 index 000000000..b897934e8 --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/updater/event/RestartTriggerEvent.java @@ -0,0 +1,50 @@ +package mineplex.core.updater.event; + +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class RestartTriggerEvent extends Event implements Cancellable +{ + private static final HandlerList handlers = new HandlerList(); + private boolean _cancelled = false; + private final RestartReason _reason; + + public RestartTriggerEvent(RestartReason reason) + { + _reason = reason; + } + + public HandlerList getHandlers() + { + return handlers; + } + + public static HandlerList getHandlerList() + { + return handlers; + } + + public RestartReason getReason() + { + return _reason; + } + + @Override + public boolean isCancelled() + { + return _cancelled; + } + + @Override + public void setCancelled(boolean cancel) + { + _cancelled = cancel; + } + + public enum RestartReason + { + COMMAND, + UPDATE + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.Game.Clans/pom.xml b/Plugins/Mineplex.Game.Clans/pom.xml index 73a6d5ad9..02d16f542 100644 --- a/Plugins/Mineplex.Game.Clans/pom.xml +++ b/Plugins/Mineplex.Game.Clans/pom.xml @@ -23,6 +23,11 @@ ${project.groupId} mineplex-minecraft-game-classcombat ${project.version} + + + ${project.groupId} + mineplex-clansqueue-common + ${project.version} diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java index 036d7a543..52928ef33 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java @@ -30,6 +30,7 @@ import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerLoginEvent.Result; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.server.ServerListPingEvent; import org.bukkit.event.vehicle.VehicleEnterEvent; @@ -38,6 +39,11 @@ import org.bukkit.plugin.java.JavaPlugin; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; +import com.mineplex.clansqueue.common.ClansQueueMessenger; +import com.mineplex.clansqueue.common.QueueConstant; +import com.mineplex.clansqueue.common.messages.ClansServerStatusMessage; +import com.mineplex.clansqueue.common.messages.ServerOfflineMessage; +import com.mineplex.clansqueue.common.messages.ServerOnlineMessage; import mineplex.core.Managers; import mineplex.core.MiniClientPlugin; @@ -511,6 +517,10 @@ public class ClansManager extends MiniClientPlugin implements IRelat _restartManager = new RestartManager(plugin); generatePermissions(); + + ServerOnlineMessage message = new ServerOnlineMessage(); + message.ServerName = UtilServer.getServerName(); + ClansQueueMessenger.getMessenger(UtilServer.getServerName()).transmitMessage(message, QueueConstant.SERVICE_MESSENGER_IDENTIFIER); } private void generatePermissions() @@ -1312,16 +1322,19 @@ public class ClansManager extends MiniClientPlugin implements IRelat _restartManager.onDisable(); _observerManager.onDisable(); Managers.get(MountManager.class).onDisable(); + ServerOfflineMessage message = new ServerOfflineMessage(); + message.ServerName = UtilServer.getServerName(); + ClansQueueMessenger.getMessenger(UtilServer.getServerName()).transmitMessage(message, QueueConstant.SERVICE_MESSENGER_IDENTIFIER); } - @EventHandler(priority = EventPriority.HIGHEST) - public void onJoin(PlayerLoginEvent event) + @EventHandler + public void transmitQueueStatus(UpdateEvent event) { - if (_restartManager.isRestarting()) + if (event.getType() != UpdateType.FAST) { return; } - + int online = 0; for (Player player : UtilServer.getPlayers()) @@ -1334,14 +1347,23 @@ public class ClansManager extends MiniClientPlugin implements IRelat online++; } - if (online >= UtilServer.getServer().getMaxPlayers() && !_clientManager.Get(event.getPlayer()).hasPermission(Perm.JOIN_FULL) && !event.getPlayer().isWhitelisted() && !event.getPlayer().isOp()) + ClansServerStatusMessage message = new ClansServerStatusMessage(); + message.ServerName = UtilServer.getServerName(); + message.OpenSlots = Math.max(0, Bukkit.getMaxPlayers() - online); + ClansQueueMessenger.getMessenger(UtilServer.getServerName()).transmitMessage(message, QueueConstant.SERVICE_MESSENGER_IDENTIFIER); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onJoin(PlayerLoginEvent event) + { + if (_restartManager.isRestarting()) { - event.disallow(PlayerLoginEvent.Result.KICK_OTHER, "This Clans server is full! Try again soon"); + return; } - else + + if (event.getResult() == Result.KICK_FULL) { event.allow(); - event.setResult(PlayerLoginEvent.Result.ALLOWED); } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/restart/RestartManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/restart/RestartManager.java index d7d2ac9bb..ce55be4b8 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/restart/RestartManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/restart/RestartManager.java @@ -3,16 +3,19 @@ package mineplex.game.clans.restart; import java.util.Calendar; import java.util.LinkedList; -import net.minecraft.server.v1_8_R3.MinecraftServer; - import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerLoginEvent.Result; +import org.bukkit.event.server.ServerCommandEvent; import org.bukkit.event.server.ServerListPingEvent; import org.bukkit.plugin.java.JavaPlugin; +import com.mineplex.clansqueue.common.ClansQueueMessenger; +import com.mineplex.clansqueue.common.QueueConstant; +import com.mineplex.clansqueue.common.messages.ServerOfflineMessage; + import mineplex.core.MiniPlugin; import mineplex.core.account.permissions.Permission; import mineplex.core.account.permissions.PermissionGroup; @@ -29,9 +32,11 @@ import mineplex.core.slack.SlackAPI; import mineplex.core.slack.SlackMessage; import mineplex.core.slack.SlackTeam; import mineplex.core.updater.UpdateType; +import mineplex.core.updater.event.RestartServerEvent; import mineplex.core.updater.event.UpdateEvent; import mineplex.game.clans.clans.ClansManager; import mineplex.game.clans.gameplay.safelog.npc.NPCManager; +import net.minecraft.server.v1_8_R3.MinecraftServer; public class RestartManager extends MiniPlugin { @@ -82,7 +87,6 @@ public class RestartManager extends MiniPlugin private void generatePermissions() { - PermissionGroup.CMOD.setPermission(Perm.RESTART_COMMAND, false, true); PermissionGroup.QAM.setPermission(Perm.RESTART_COMMAND, false, true); PermissionGroup.ADMIN.setPermission(Perm.RESTART_COMMAND, true, true); @@ -147,6 +151,9 @@ public class RestartManager extends MiniPlugin public void restart() { + ServerOfflineMessage message = new ServerOfflineMessage(); + message.ServerName = UtilServer.getServerName(); + ClansQueueMessenger.getMessenger(UtilServer.getServerName()).transmitMessage(message, QueueConstant.SERVICE_MESSENGER_IDENTIFIER); Bukkit.broadcastMessage(F.main("Clans", "This Clans server will be restarting in " + F.elem(UtilTime.MakeStr(120000)) + "!")); UtilTextMiddle.display(C.cRed + "Server Restart", C.cGray + "This server will restart in " + F.elem(UtilTime.MakeStr(120000)) + "!"); _restartTime = System.currentTimeMillis() + 120000; @@ -170,6 +177,36 @@ public class RestartManager extends MiniPlugin } } + @EventHandler + public void onRestart(RestartServerEvent event) + { + event.setCancelled(true); + + if (_restarting || _restartTime != -1) + { + return; + } + + restart(); + } + + @EventHandler + public void onShutdownCommand(ServerCommandEvent event) + { + String command = event.getCommand().toLowerCase().trim(); + if (command.equals("stop") || command.startsWith("stop ")) + { + event.setCancelled(true); + + if (_restarting || _restartTime != -1) + { + return; + } + + restart(); + } + } + @EventHandler public void checkRestart(UpdateEvent event) { diff --git a/Plugins/Mineplex.Hub.Clans/pom.xml b/Plugins/Mineplex.Hub.Clans/pom.xml index b5bc14c36..8da5f3fc8 100644 --- a/Plugins/Mineplex.Hub.Clans/pom.xml +++ b/Plugins/Mineplex.Hub.Clans/pom.xml @@ -23,6 +23,11 @@ ${project.groupId} mineplex-minecraft-game-core ${project.version} + + + ${project.groupId} + mineplex-clansqueue-common + ${project.version} diff --git a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansHub.java b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansHub.java index e44595555..60788e367 100644 --- a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansHub.java +++ b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansHub.java @@ -157,7 +157,7 @@ public class ClansHub extends JavaPlugin BoosterManager boosterManager = new BoosterManager(this, "", clientManager, donationManager, inventoryManager, thankManager); HubManager hubManager = new HubManager(this, blockRestore, clientManager, incognito, donationManager, inventoryManager, condition, disguiseManager, new TaskManager(this, clientManager), portal, partyManager, preferenceManager, petManager, pollManager, statsManager, achievementManager, hologramManager, npcManager, packetHandler, punish, serverStatusManager, customDataManager, thankManager, boosterManager, castleManager); - ClansTransferManager serverManager = new ClansTransferManager(this, clientManager, donationManager, partyManager, portal, hubManager); + ClansTransferManager serverManager = new ClansTransferManager(this, clientManager, donationManager, partyManager, portal); Chat chat = new Chat(this, incognito, clientManager, preferenceManager, achievementManager, serverStatusManager.getCurrentServerName()); new MessageManager(this, incognito, clientManager, preferenceManager, ignoreManager, punish, friendManager, chat); diff --git a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansServerPage.java b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansServerPage.java index 887b41520..62620ca77 100644 --- a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansServerPage.java +++ b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansServerPage.java @@ -5,6 +5,7 @@ import java.util.Collection; import org.bukkit.Material; import org.bukkit.entity.Player; +import mineplex.clanshub.queue.HubQueueManager; import mineplex.core.Managers; import mineplex.core.account.CoreClientManager; import mineplex.core.common.util.C; @@ -22,6 +23,8 @@ import mineplex.game.clans.core.repository.tokens.SimpleClanToken; */ public class ClansServerPage extends ShopPageBase { + private final HubQueueManager _queue = Managers.require(HubQueueManager.class); + public ClansServerPage(ClansTransferManager plugin, ClansServerShop shop, CoreClientManager clientManager, DonationManager donationManager, Player player) { @@ -127,10 +130,12 @@ public class ClansServerPage extends ShopPageBase servers = UtilAlg.sortSet(getPlugin().getServers(true), (o1, o2) -> diff --git a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansTransferManager.java b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansTransferManager.java index c36c47ee0..8e4dfbed6 100644 --- a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansTransferManager.java +++ b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansTransferManager.java @@ -13,6 +13,7 @@ import org.bukkit.plugin.java.JavaPlugin; import com.google.common.collect.Lists; +import mineplex.clanshub.queue.HubQueueManager; import mineplex.core.MiniDbClientPlugin; import mineplex.core.account.CoreClientManager; import mineplex.core.account.permissions.Permission; @@ -41,26 +42,24 @@ public class ClansTransferManager extends MiniDbClientPlugin { STAFF_PAGE, ALLOW_HARDCORE, - JOIN_FULL, } private static final long SERVER_RELOAD_INTERVAL = 5000; private PartyManager _party; private Portal _portal; - private HubManager _hub; private Region _region; private final Map _servers = new HashMap<>(); private boolean _loading = false; private long _lastLoaded; private ClansServerShop _serverShop; + private final HubQueueManager _queue = require(HubQueueManager.class); - public ClansTransferManager(JavaPlugin plugin, CoreClientManager client, DonationManager donation, PartyManager party, Portal portal, HubManager hub) + public ClansTransferManager(JavaPlugin plugin, CoreClientManager client, DonationManager donation, PartyManager party, Portal portal) { super("Server Transfer", plugin, client); _party = party; _portal = portal; - _hub = hub; _region = plugin.getConfig().getBoolean("serverstatus.us") ? Region.US : Region.EU; _serverShop = new ClansServerShop(this, client, donation); @@ -71,7 +70,7 @@ public class ClansTransferManager extends MiniDbClientPlugin { PermissionGroup.TRAINEE.setPermission(Perm.STAFF_PAGE, true, true); PermissionGroup.TRAINEE.setPermission(Perm.ALLOW_HARDCORE, true, true); - PermissionGroup.ULTRA.setPermission(Perm.JOIN_FULL, true, true); + PermissionGroup.CONTENT.setPermission(Perm.ALLOW_HARDCORE, true, true); } /** @@ -92,7 +91,7 @@ public class ClansTransferManager extends MiniDbClientPlugin List servers = Lists.newArrayList(); for (ServerInfo info : _servers.values()) { - if (!info.MOTD.equalsIgnoreCase("Restarting soon") || !onlineOnly) + if (!(info.MOTD.equalsIgnoreCase("Restarting soon") || _queue.getData(info) == null) || !onlineOnly) { servers.add(info); } @@ -109,7 +108,7 @@ public class ClansTransferManager extends MiniDbClientPlugin { for (ServerInfo server : _servers.values()) { - if (server.Name.equalsIgnoreCase(name) && !server.MOTD.equalsIgnoreCase("Restarting soon")) + if (server.Name.equalsIgnoreCase(name) && !server.MOTD.equalsIgnoreCase("Restarting soon") && _queue.getData(server) != null) { return server; } @@ -136,19 +135,6 @@ public class ClansTransferManager extends MiniDbClientPlugin } } - /** - * Selects a server to send a player to - * @param player The player to send - * @param serverInfo The server to send the player to - */ - public void selectServer(Player player, ServerInfo serverInfo) - { - player.leaveVehicle(); - player.eject(); - - _portal.sendPlayerToServer(player, serverInfo.Name, Intent.PLAYER_REQUEST); - } - @EventHandler public void reloadServers(UpdateEvent event) { @@ -157,13 +143,10 @@ public class ClansTransferManager extends MiniDbClientPlugin return; } _loading = true; - final Runnable after = new Runnable() + final Runnable after = () -> { - public void run() - { - _lastLoaded = System.currentTimeMillis(); - _loading = false; - } + _lastLoaded = System.currentTimeMillis(); + _loading = false; }; runAsync(() -> { diff --git a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/JoinServerButton.java b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/JoinServerButton.java index 2c15ae64d..b36467ec6 100644 --- a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/JoinServerButton.java +++ b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/JoinServerButton.java @@ -3,6 +3,8 @@ package mineplex.clanshub; import org.bukkit.entity.Player; import org.bukkit.event.inventory.ClickType; +import mineplex.clanshub.queue.HubQueueManager; +import mineplex.core.Managers; import mineplex.core.shop.item.IButton; import mineplex.core.shop.page.ShopPageBase; @@ -12,13 +14,12 @@ import mineplex.core.shop.page.ShopPageBase; public class JoinServerButton implements IButton { private ShopPageBase _page; - private ClansTransferManager _transferManager; private ServerInfo _serverInfo; + private final HubQueueManager _queue = Managers.require(HubQueueManager.class); - public JoinServerButton(ShopPageBase page, ClansTransferManager transferManager, ServerInfo serverInfo) + public JoinServerButton(ShopPageBase page, ServerInfo serverInfo) { _page = page; - _transferManager = transferManager; _serverInfo = serverInfo; } @@ -37,16 +38,13 @@ public class JoinServerButton implements IButton { if (serverInfo != null) { - System.out.println("Selecting server :" + serverInfo.Name); - int slots = 1; - - if (serverInfo.getAvailableSlots() < slots && !_page.getClientManager().Get(player).hasPermission(ClansTransferManager.Perm.JOIN_FULL)) + if (_queue.Get(player).TargetServer == null || !_queue.Get(player).TargetServer.equals(serverInfo.Name)) { - _page.playDenySound(player); + _queue.attemptEnterQueue(player, _queue.getData(serverInfo)); } else { - _transferManager.selectServer(player, serverInfo); + _queue.leaveQueue(player, true); } } else diff --git a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/queue/HubQueueManager.java b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/queue/HubQueueManager.java new file mode 100644 index 000000000..96a5402a9 --- /dev/null +++ b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/queue/HubQueueManager.java @@ -0,0 +1,353 @@ +package mineplex.clanshub.queue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerQuitEvent; + +import com.mineplex.clansqueue.common.ClansQueueMessenger; +import com.mineplex.clansqueue.common.QueueConstant; +import com.mineplex.clansqueue.common.messages.PlayerJoinQueueCallbackMessage; +import com.mineplex.clansqueue.common.messages.PlayerJoinQueueMessage; +import com.mineplex.clansqueue.common.messages.PlayerLeaveQueueMessage; +import com.mineplex.clansqueue.common.messages.PlayerSendToServerMessage; +import com.mineplex.clansqueue.common.messages.QueueDeleteMessage; +import com.mineplex.clansqueue.common.messages.QueuePauseBroadcastMessage; +import com.mineplex.clansqueue.common.messages.QueuePauseUpdateMessage; +import com.mineplex.clansqueue.common.messages.QueueStatusMessage; + +import mineplex.clanshub.ClansTransferManager; +import mineplex.clanshub.ServerInfo; +import mineplex.clanshub.queue.data.ClansQueueData; +import mineplex.clanshub.queue.data.QueuePlayerData; +import mineplex.core.Managers; +import mineplex.core.MiniClientPlugin; +import mineplex.core.ReflectivelyCreateMiniPlugin; +import mineplex.core.account.CoreClientManager; +import mineplex.core.account.permissions.Permission; +import mineplex.core.account.permissions.PermissionGroup; +import mineplex.core.command.CommandBase; +import mineplex.core.common.util.C; +import mineplex.core.common.util.F; +import mineplex.core.common.util.UtilPlayer; +import mineplex.core.common.util.UtilServer; +import mineplex.core.common.util.UtilTime; +import mineplex.core.common.util.UtilTime.TimeUnit; +import mineplex.core.portal.Intent; +import mineplex.core.portal.Portal; +import mineplex.core.portal.events.ServerTransferEvent; +import mineplex.core.punish.clans.ClansBanManager; + +@ReflectivelyCreateMiniPlugin +public class HubQueueManager extends MiniClientPlugin +{ + public enum Perm implements Permission + { + JOIN_PAUSED_QUEUE, + TOGGLE_QUEUE_PAUSE, + LIST_QUEUES, + } + + public enum QueuePriority implements Permission + { + BYPASS(-1, PermissionGroup.CONTENT, PermissionGroup.TRAINEE), + PRIORITY(7, PermissionGroup.BUILDER), + ETERNAL(6, PermissionGroup.ETERNAL), + TITAN(5, PermissionGroup.TITAN), + LEGEND(4, PermissionGroup.LEGEND), + HERO(3, PermissionGroup.HERO), + ULTRA(2, PermissionGroup.ULTRA), + DEFAULT(1, PermissionGroup.PLAYER) + ; + + private final int _weight; + private final List _granted; + + private QueuePriority(int weight, PermissionGroup... granted) + { + _weight = weight; + _granted = Collections.unmodifiableList(Arrays.asList(granted)); + } + + public int getWeight() + { + return _weight; + } + + public List getGranted() + { + return _granted; + } + } + + private final CoreClientManager _clientManager = require(CoreClientManager.class); + private final ClansBanManager _punish = require(ClansBanManager.class); + private final Portal _portal = require(Portal.class); + private final Comparator _prioritySorter = (q1, q2) -> + { + if (q1.getWeight() == -1 && q2.getWeight() != -1) + { + return -1; + } + if (q2.getWeight() == -1 && q1.getWeight() != -1) + { + return 1; + } + + return Integer.compare(q1.getWeight(), q2.getWeight()); + }; + private final Map _queueData = new HashMap<>(); + private final ClansQueueMessenger _messenger; + + private HubQueueManager() + { + super("Queue Manager"); + + generatePermissions(); + _messenger = ClansQueueMessenger.getMessenger(UtilServer.getServerName()); + + _messenger.registerListener(PlayerJoinQueueCallbackMessage.class, (callback, origin) -> + { + runSync(() -> + { + Player player = Bukkit.getPlayer(callback.PlayerUUID); + if (player != null) + { + QueuePlayerData data = Get(player); + data.Queued = true; + data.QueuePosition = callback.Position; + UtilPlayer.message(player, F.main(getName(), "You have joined the queue for server " + F.elem(data.TargetServer) + "! Your position: " + F.greenElem("#" + data.QueuePosition))); + } + }); + }); + _messenger.registerListener(PlayerSendToServerMessage.class, (callback, origin) -> + { + runSync(() -> + { + Player player = Bukkit.getPlayer(callback.PlayerUUID); + if (player != null) + { + player.leaveVehicle(); + player.eject(); + _portal.sendPlayerToServer(player, callback.TargetServer, Intent.FORCE_TRANSFER); + } + }); + }); + _messenger.registerListener(QueueStatusMessage.class, (status, origin) -> + { + runSync(() -> + { + status.Snapshots.forEach(snapshot -> + { + ClansQueueData data = _queueData.computeIfAbsent(snapshot.ServerName, (name) -> new ClansQueueData(name)); + + data.QueueMembers = snapshot.Queue.size(); + data.QueuePaused = snapshot.Paused; + snapshot.Queue.entrySet().forEach(entry -> + { + Player player = Bukkit.getPlayer(entry.getKey()); + if (player != null) + { + Get(player).QueuePosition = entry.getValue(); + UtilPlayer.message(player, F.main(getName(), "Your position: " + F.greenElem("#" + entry.getValue()))); + } + }); + }); + }); + }); + _messenger.registerListener(QueuePauseBroadcastMessage.class, (broadcast, origin) -> + { + runSync(() -> + { + ClansQueueData data = _queueData.computeIfAbsent(broadcast.ServerName, (name) -> new ClansQueueData(name)); + data.QueuePaused = broadcast.Paused; + GetValues().forEach(qp -> + { + if (qp.TargetServer != null && qp.TargetServer.equals(broadcast.ServerName)) + { + UtilPlayer.message(Bukkit.getPlayer(qp.UniqueId), F.main(getName(), "Queue pause status: " + F.elem(broadcast.Paused))); + } + }); + }); + }); + _messenger.registerListener(QueueDeleteMessage.class, (delete, origin) -> + { + runSync(() -> + { + GetValues().forEach(qp -> + { + if (qp.TargetServer != null && qp.TargetServer.equals(delete.ServerName)) + { + UtilPlayer.message(Bukkit.getPlayer(qp.UniqueId), F.main(getName(), "Queue deleted.")); + } + qp.Queued = false; + qp.QueuePosition = 0; + qp.TargetServer = null; + }); + _queueData.remove(delete.ServerName); + }); + }); + addCommand(new CommandBase(this, Perm.TOGGLE_QUEUE_PAUSE, "pausequeue") + { + @Override + public void Execute(Player caller, String[] args) + { + if (args.length < 1) + { + UtilPlayer.message(caller, F.main(getName(), "Usage: /pausequeue ")); + return; + } + ServerInfo info = Managers.get(ClansTransferManager.class).getServer(args[0]); + if (info != null) + { + ClansQueueData data = getData(info); + if (data != null) + { + QueuePauseUpdateMessage message = new QueuePauseUpdateMessage(); + message.ServerName = data.ServerName; + message.Paused = !data.QueuePaused; + _messenger.transmitMessage(message, QueueConstant.SERVICE_MESSENGER_IDENTIFIER); + UtilPlayer.message(caller, F.main(getName(), "Toggling queue pause")); + return; + } + } + UtilPlayer.message(caller, F.main(getName(), "Queue not found")); + } + }); + addCommand(new CommandBase(this, Perm.LIST_QUEUES, "listqueues") + { + @Override + public void Execute(Player caller, String[] args) + { + StringBuilder queues = new StringBuilder("Queues: ["); + queues.append(_queueData.values().stream().map(data -> data.ServerName).collect(Collectors.joining(", "))); + queues.append(']'); + UtilPlayer.message(caller, F.main(getName(), queues.toString())); + } + }); + } + + private void generatePermissions() + { + for (QueuePriority priority : QueuePriority.values()) + { + priority.getGranted().forEach(group -> group.setPermission(priority, true, true)); + } + PermissionGroup.ADMIN.setPermission(Perm.JOIN_PAUSED_QUEUE, true, true); + PermissionGroup.ADMIN.setPermission(Perm.LIST_QUEUES, true, true); + PermissionGroup.ADMIN.setPermission(Perm.TOGGLE_QUEUE_PAUSE, true, true); + } + + public QueuePriority getHighestPriority(Player player) + { + Optional opt = Stream.of(QueuePriority.values()).filter(_clientManager.Get(player)::hasPermission).sorted(_prioritySorter).findFirst(); + + if (opt.isPresent()) + { + return opt.get(); + } + + return QueuePriority.DEFAULT; + } + + public ClansQueueData getData(ServerInfo info) + { + return _queueData.get(info.Name); + } + + public void attemptEnterQueue(Player player, ClansQueueData data) + { + if (Get(player).TargetServer != null) + { + if (Get(player).Queued) + { + UtilPlayer.message(player, F.main(getName(), "You are already in a queue!")); + } + else + { + UtilPlayer.message(player, F.main(getName(), "You are already entering a queue!")); + } + return; + } + if (data.QueuePaused && !_clientManager.Get(player).hasPermission(Perm.JOIN_PAUSED_QUEUE)) + { + UtilPlayer.message(player, F.main(getName(), "That queue is paused and cannot currently be joined!")); + return; + } + Get(player).TargetServer = data.ServerName; + _punish.loadClient(player.getUniqueId(), client -> + { + if (client.isBanned()) + { + Get(player).TargetServer = null; + String time = UtilTime.convertString(client.getLongestBan().getTimeLeft(), 0, TimeUnit.FIT); + + if (client.getLongestBan().isPermanent()) + { + time = "Permanent"; + } + + String reason = C.cRedB + "You are banned from Clans for " + time + + "\n" + C.cWhite + client.getLongestBan().getReason(); + UtilPlayer.message(player, reason); + } + else + { + QueuePriority priority = getHighestPriority(player); + PlayerJoinQueueMessage message = new PlayerJoinQueueMessage(); + message.PlayerUUID = player.getUniqueId(); + message.TargetServer = data.ServerName; + message.PlayerPriority = priority.getWeight(); + _messenger.transmitMessage(message, QueueConstant.SERVICE_MESSENGER_IDENTIFIER); + UtilPlayer.message(player, F.main(getName(), "Joining queue...")); + } + }); + } + + public void leaveQueue(Player player, boolean informFailure) + { + if (!Get(player).Queued && informFailure) + { + UtilPlayer.message(player, F.main(getName(), "You are not part of a queue!")); + return; + } + PlayerLeaveQueueMessage message = new PlayerLeaveQueueMessage(); + message.PlayerUUID = player.getUniqueId(); + message.TargetServer = Get(player).TargetServer; + _messenger.transmitMessage(message, QueueConstant.SERVICE_MESSENGER_IDENTIFIER); + Get(player).TargetServer = null; + Get(player).QueuePosition = 0; + Get(player).Queued = false; + UtilPlayer.message(player, F.main(getName(), "You have left the queue for " + F.elem(message.TargetServer) + "!")); + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) + { + leaveQueue(event.getPlayer(), false); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onTransfer(ServerTransferEvent event) + { + leaveQueue(event.getPlayer(), false); + } + + @Override + protected QueuePlayerData addPlayer(UUID uuid) + { + return new QueuePlayerData(uuid); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/queue/data/ClansQueueData.java b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/queue/data/ClansQueueData.java new file mode 100644 index 000000000..4512e409e --- /dev/null +++ b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/queue/data/ClansQueueData.java @@ -0,0 +1,30 @@ +package mineplex.clanshub.queue.data; + +public class ClansQueueData +{ + public final String ServerName; + public int QueueMembers; + public boolean QueuePaused; + + public ClansQueueData(String serverName) + { + ServerName = serverName; + } + + @Override + public int hashCode() + { + return ServerName.hashCode(); + } + + @Override + public boolean equals(Object o) + { + if (o == null || !getClass().isInstance(o)) + { + return false; + } + + return ((ClansQueueData)o).ServerName.equals(ServerName); + } +} \ No newline at end of file diff --git a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/queue/data/QueuePlayerData.java b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/queue/data/QueuePlayerData.java new file mode 100644 index 000000000..c0b05f974 --- /dev/null +++ b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/queue/data/QueuePlayerData.java @@ -0,0 +1,36 @@ +package mineplex.clanshub.queue.data; + +import java.util.UUID; + +public class QueuePlayerData +{ + public final UUID UniqueId; + + public boolean Queued; + + public int QueuePosition; + + public String TargetServer; + + public QueuePlayerData(UUID uuid) + { + UniqueId = uuid; + } + + @Override + public boolean equals(Object o) + { + if (o == null || !getClass().isInstance(o)) + { + return false; + } + + return UniqueId.equals(((QueuePlayerData)o).UniqueId); + } + + @Override + public int hashCode() + { + return UniqueId.hashCode(); + } +} \ No newline at end of file diff --git a/Plugins/pom.xml b/Plugins/pom.xml index 95880e508..e6a20d519 100644 --- a/Plugins/pom.xml +++ b/Plugins/pom.xml @@ -25,6 +25,8 @@ Mineplex.Database Mineplex.DDoSProtectionSwitcher Mineplex.EnjinTranslator + Mineplex.ClansQueue.Common + Mineplex.ClansQueue Mineplex.Game.Clans Mineplex.Game.Clans.Core Mineplex.Game.Clans.Compensation