Clans queue system

This commit is contained in:
AlexTheCoder 2017-09-05 23:22:55 -04:00
parent 0a5dae5857
commit f13e9cdf61
47 changed files with 1858 additions and 88 deletions

View File

@ -0,0 +1,23 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mineplex</groupId>
<artifactId>mineplex-parent</artifactId>
<version>dev-SNAPSHOT</version>
<relativePath>../plugin.xml</relativePath>
</parent>
<name>ClansQueue-Common</name>
<artifactId>mineplex-clansqueue-common</artifactId>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mineplex-serverdata</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,8 @@
package com.mineplex.clansqueue.common;
public class ClansQueueMessage
{
protected String Origin;
protected String BodyClass;
protected String BodySerialized;
}

View File

@ -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);
}
}

View File

@ -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<String, ClansQueueMessenger> _messengers = new ConcurrentHashMap<>();
private final String _identifier;
private final JedisPool _readPool;
private final JedisPool _writePool;
@SuppressWarnings("rawtypes")
private final Map<String, Class> _bodyTypes = Collections.synchronizedMap(new HashMap<>());
@SuppressWarnings("rawtypes")
private final Map<String, List<BiConsumer>> _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 <T extends ClansQueueMessageBody> void registerListener(Class<T> messageType, BiConsumer<T, String> 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 <T extends ClansQueueMessageBody> void receiveMessage(ClansQueueMessage message)
{
if (_listeners.containsKey(message.BodyClass) && _bodyTypes.containsKey(message.BodyClass))
{
T body = Utility.deserialize(message.BodySerialized, (Class<T>)_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));
}
}

View File

@ -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;
}

View File

@ -0,0 +1,13 @@
package com.mineplex.clansqueue.common;
import java.util.LinkedList;
public class SortableLinkedList<T extends Comparable<T>> extends LinkedList<T>
{
private static final long serialVersionUID = -1751886037436467545L;
public void sort()
{
sort((t1, t2) -> t1.compareTo(t2));
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,8 @@
package com.mineplex.clansqueue.common.messages;
import com.mineplex.clansqueue.common.ClansQueueMessageBody;
public class QueueDeleteMessage extends ClansQueueMessageBody
{
public String ServerName;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<QueueSnapshot> Snapshots = new ArrayList<>();
public static class QueueSnapshot
{
public String ServerName;
public Map<UUID, Integer> Queue;
public boolean Paused;
}
}

View File

@ -0,0 +1,8 @@
package com.mineplex.clansqueue.common.messages;
import com.mineplex.clansqueue.common.ClansQueueMessageBody;
public class ServerOfflineMessage extends ClansQueueMessageBody
{
public String ServerName;
}

View File

@ -0,0 +1,8 @@
package com.mineplex.clansqueue.common.messages;
import com.mineplex.clansqueue.common.ClansQueueMessageBody;
public class ServerOnlineMessage extends ClansQueueMessageBody
{
public String ServerName;
}

View File

@ -0,0 +1,23 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mineplex</groupId>
<artifactId>mineplex-parent</artifactId>
<version>dev-SNAPSHOT</version>
<relativePath>../plugin.xml</relativePath>
</parent>
<name>ClansQueue-Common</name>
<artifactId>mineplex-clansqueue</artifactId>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mineplex-clansqueue-common</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -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<String, ConsoleCommand> _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);
}
}

View File

@ -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<String, ConsoleCommand> _commands;
public CommandSystem(QueueService service, Map<String, ConsoleCommand> 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<ConsoleCommand> 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.");
}
}
}
}
}

View File

@ -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);
}

View File

@ -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 <Server>");
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();
}
}

View File

@ -0,0 +1,41 @@
package com.mineplex.clansqueue.service.commands;
import java.util.Map;
public class HelpCommand extends ConsoleCommand
{
private final Map<String, ConsoleCommand> _commands;
public HelpCommand(Map<String, ConsoleCommand> 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();
}
}

View File

@ -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();
}
}

View File

@ -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 <Server>");
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();
}
}

View File

@ -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();
}
}

View File

@ -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<String, ClansServer> _servers = new HashMap<>();
private final Map<ClansServer, ServerQueue> _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<ServerQueue> 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<ServerQueue> 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<ClansServer> 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();
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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<UUID, String> _sending = new LinkedHashMap<>();
private final Map<UUID, String> _bypassing = new LinkedHashMap<>();
private final SortableLinkedList<PlayerQueue> _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<UUID, String> getNextSend()
{
synchronized (_sendLock)
{
Map<UUID, String> sending = new LinkedHashMap<>();
sending.putAll(_sending);
_sending.clear();
return sending;
}
}
public Map<UUID, QueuePlayer> getPlayers()
{
synchronized (_queueLock)
{
Map<UUID, QueuePlayer> 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<QueuePlayer> callback)
{
synchronized (_queueLock)
{
Optional<PlayerQueue> 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<UUID, String> 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<PlayerQueue>
{
private final int _weight;
private final Queue<QueuePlayer> _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;
}
}
}

View File

@ -617,7 +617,6 @@ public class CoreClientManager extends MiniPlugin
if (client.hasPermission(Perm.JOIN_FULL))
{
event.allow();
event.setResult(PlayerLoginEvent.Result.ALLOWED);
return;
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -23,6 +23,11 @@
<groupId>${project.groupId}</groupId>
<artifactId>mineplex-minecraft-game-classcombat</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mineplex-clansqueue-common</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -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<ClientClan> 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<ClientClan> 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<ClientClan> 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);
}
}

View File

@ -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)
{

View File

@ -23,6 +23,11 @@
<groupId>${project.groupId}</groupId>
<artifactId>mineplex-minecraft-game-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mineplex-clansqueue-common</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -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);

View File

@ -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<ClansTransferManager, ClansServerShop>
{
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<ClansTransferManager, ClansSer
String serverName = C.cYellow + "Server Name: " + C.cWhite + clan.getHomeServer();
String serverDesc = C.cYellow + "Server Status: " + C.cWhite + serverStatus;
String players = (serverOnline ? C.cYellow + "Players: " + C.cWhite + serverInfo.CurrentPlayers + "/" + serverInfo.MaxPlayers : "" );
String queue1 = C.cYellow + "Queue Status: " + (_queue.getData(serverInfo).QueuePaused ? C.cRed + "Paused" : C.cGreen + "Active");
String queue2 = C.cYellow + "Your Position: " + ((_queue.Get(getPlayer()).TargetServer != null && _queue.Get(getPlayer()).TargetServer.equals(serverInfo.Name)) ? (_queue.Get(getPlayer()).Queued ? C.cGreen + "#" + _queue.Get(getPlayer()).QueuePosition : C.cGray + "Joining...") : C.cGray + "Not Joined");
String change = C.cRed + "Note: " + C.cWhite + "You must leave your Clan to ";
String change2 = C.cWhite + "play on a different Clans Server!";
ShopItem shopItem = new ShopItem(Material.EMERALD_BLOCK, title, new String[] {" ", serverName, serverDesc, players, " ", change, change2, " "}, 0, true, true);
addButton(13, shopItem, new JoinServerButton(this, getPlugin(), getServerInfo(clan.getHomeServer())));
ShopItem shopItem = new ShopItem(Material.EMERALD_BLOCK, title, new String[] {" ", serverName, serverDesc, players, " ", queue1, queue2, " ", change, change2, " "}, 0, true, true);
addButton(13, shopItem, new JoinServerButton(this, getServerInfo(clan.getHomeServer())));
}
private void buildJoinServer(int slot, ServerInfo server)
@ -139,6 +144,8 @@ public class ClansServerPage extends ShopPageBase<ClansTransferManager, ClansSer
String desc1 = C.cYellow + "Server Name " + C.cWhite + server.Name;
String desc2 = C.cYellow + "Players " + C.cWhite + server.CurrentPlayers + "/" + server.MaxPlayers;
String desc3 = C.cYellow + "Mode " + C.cWhite + (server.Hardcore ? "Hardcore" : "Casual");
String queue1 = C.cYellow + "Queue Status: " + (_queue.getData(server).QueuePaused ? C.cRed + "Paused" : C.cGreen + "Active");
String queue2 = C.cYellow + "Your Position: " + ((_queue.Get(getPlayer()).TargetServer != null && _queue.Get(getPlayer()).TargetServer.equals(server.Name)) ? (_queue.Get(getPlayer()).Queued ? C.cGreen + "#" + _queue.Get(getPlayer()).QueuePosition : C.cGray + "Joining...") : C.cGray + "Not Joined");
String desc4 = "";
String desc5 = "";
if (!server.Hardcore || canPlayHardcore())
@ -152,14 +159,14 @@ public class ClansServerPage extends ShopPageBase<ClansTransferManager, ClansSer
desc5 = C.cWhite + "You need to play Casual for " + timeTillUnlockHardcore() + " to unlock Hardcore play!";
}
ShopItem shopItem = new ShopItem(Material.GOLD_BLOCK, title, new String[] {" ", desc1, desc2, desc3, " ", desc4, desc5}, 0, true, true);
ShopItem shopItem = new ShopItem(Material.GOLD_BLOCK, title, new String[] {" ", desc1, desc2, desc3, " ", queue1, queue2, " ", desc4, desc5}, 0, true, true);
if (server.Hardcore && !canPlayHardcore())
{
addButtonNoAction(slot, shopItem);
}
else
{
addButton(slot, shopItem, new JoinServerButton(this, getPlugin(), server));
addButton(slot, shopItem, new JoinServerButton(this, server));
}
}
@ -175,9 +182,11 @@ public class ClansServerPage extends ShopPageBase<ClansTransferManager, ClansSer
String serverName = C.cYellow + "Server Name: " + C.cWhite + clan.getHomeServer();
String serverDesc = C.cYellow + "Server Status: " + C.cWhite + serverStatus;
String players = C.cYellow + "Players: " + C.cWhite + (serverOnline ? serverInfo.CurrentPlayers + "/" + serverInfo.MaxPlayers : "0/0");
String queue1 = C.cYellow + "Queue Status: " + (_queue.getData(serverInfo).QueuePaused ? C.cRed + "Paused" : C.cGreen + "Active");
String queue2 = C.cYellow + "Your Position: " + ((_queue.Get(getPlayer()).TargetServer != null && _queue.Get(getPlayer()).TargetServer.equals(serverInfo.Name)) ? (_queue.Get(getPlayer()).Queued ? C.cGreen + "#" + _queue.Get(getPlayer()).QueuePosition : C.cGray + "Joining...") : C.cGray + "Not Joined");
String mode = C.cYellow + "Mode " + C.cWhite + (serverInfo.Hardcore ? "Hardcore" : "Casual");
ShopItem shopItem = new ShopItem(Material.EMERALD_BLOCK, title, new String[] {" ", serverName, serverDesc, players, mode, " "}, 0, true, true);
addButton(13, shopItem, new JoinServerButton(this, getPlugin(), getServerInfo(clan.getHomeServer())));
ShopItem shopItem = new ShopItem(Material.EMERALD_BLOCK, title, new String[] {" ", serverName, serverDesc, players, queue1, queue2, mode, " "}, 0, true, true);
addButton(13, shopItem, new JoinServerButton(this, getServerInfo(clan.getHomeServer())));
}
Collection<ServerInfo> servers = UtilAlg.sortSet(getPlugin().getServers(true), (o1, o2) ->

View File

@ -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<SimpleClanToken>
{
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<MinecraftServer, ServerInfo> _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<SimpleClanToken>
{
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<SimpleClanToken>
List<ServerInfo> 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<SimpleClanToken>
{
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<SimpleClanToken>
}
}
/**
* 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<SimpleClanToken>
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(() ->
{

View File

@ -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

View File

@ -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<QueuePlayerData>
{
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<PermissionGroup> _granted;
private QueuePriority(int weight, PermissionGroup... granted)
{
_weight = weight;
_granted = Collections.unmodifiableList(Arrays.asList(granted));
}
public int getWeight()
{
return _weight;
}
public List<PermissionGroup> 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<QueuePriority> _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<String, ClansQueueData> _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<HubQueueManager>(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 <Server>"));
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<HubQueueManager>(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<QueuePriority> 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);
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -25,6 +25,8 @@
<module>Mineplex.Database</module>
<module>Mineplex.DDoSProtectionSwitcher</module>
<module>Mineplex.EnjinTranslator</module>
<module>Mineplex.ClansQueue.Common</module>
<module>Mineplex.ClansQueue</module>
<module>Mineplex.Game.Clans</module>
<module>Mineplex.Game.Clans.Core</module>
<module>Mineplex.Game.Clans.Compensation</module>