Velocity queueing solution
1/*
2 * ProxyQueues, a Velocity queueing solution
3 * Copyright (c) 2021 James Lyne
4 *
5 * Some portions of this file were taken from https://github.com/darbyjack/DeluxeQueues
6 * These portions are Copyright (c) 2019 Glare
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in all
16 * copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26package uk.co.notnull.proxyqueues.queues;
27
28import ch.jalu.configme.SettingsManager;
29import com.velocitypowered.api.proxy.Player;
30import com.velocitypowered.api.proxy.ServerConnection;
31import com.velocitypowered.api.proxy.server.RegisteredServer;
32import uk.co.notnull.proxyqueues.api.MessageType;
33import uk.co.notnull.proxyqueues.ProxyQueuesImpl;
34import uk.co.notnull.proxyqueues.api.queues.ProxyQueue;
35import uk.co.notnull.proxyqueues.api.queues.QueueHandler;
36import uk.co.notnull.proxyqueues.configuration.sections.ConfigOptions;
37import uk.co.notnull.proxyqueues.Messages;
38import org.jetbrains.annotations.NotNull;
39
40import java.util.*;
41import java.util.concurrent.ConcurrentHashMap;
42
43public final class QueueHandlerImpl implements QueueHandler {
44
45 private final ConcurrentHashMap<RegisteredServer, ProxyQueue> queues;
46 private final SettingsManager settingsManager;
47 private final ProxyQueuesImpl proxyQueues;
48
49 public QueueHandlerImpl(SettingsManager settingsManager, ProxyQueuesImpl proxyQueues) {
50 this.settingsManager = settingsManager;
51 this.proxyQueues = proxyQueues;
52 this.queues = new ConcurrentHashMap<>();
53
54 updateQueues();
55 }
56
57 /**
58 * Create a new queue for a server
59 * @param server The server the new queue is for
60 * @return ProxyQueue - The queue
61 */
62 public ProxyQueue createQueue(@NotNull RegisteredServer server, int requiredPlayers, int maxNormal, int maxPriority, int maxStaff) {
63 return queues.compute(server, (s, queue) -> {
64 if(queue == null) {
65 proxyQueues.getLogger().info("Creating queue for " + server.getServerInfo().getName());
66 return new ProxyQueueImpl(proxyQueues, s, requiredPlayers, maxNormal, maxPriority, maxStaff);
67 } else {
68 proxyQueues.getLogger().info("Updating queue for " + server.getServerInfo().getName());
69 queue.setPlayersRequired(requiredPlayers);
70 queue.setMaxSlots(maxNormal);
71 queue.setPriorityMaxSlots(maxPriority);
72 queue.setStaffMaxSlots(maxStaff);
73
74 return queue;
75 }
76 });
77 }
78
79 /**
80 * Delete a queue if it exists
81 * @param server The server to remove the queue for
82 */
83 public void deleteQueue(@NotNull RegisteredServer server) {
84 queues.computeIfPresent(server, (s, queue) -> {
85 queue.destroy();
86 return null;
87 });
88 }
89
90 /**
91 * Get a queue from it's server
92 * @param server the server to get the queue for
93 * @return the queue
94 */
95 public ProxyQueue getQueue(@NotNull RegisteredServer server) {
96 return queues.get(server);
97 }
98
99 /**
100 * Get a queue from it's server
101 * @param player the server to get the queue from
102 * @return the queue
103 */
104 public Optional<ProxyQueue> getCurrentQueue(@NotNull Player player) {
105 return queues.values().stream()
106 .filter(q -> q.isPlayerQueued(player)).findFirst();
107 }
108
109 public Optional<ProxyQueue> getCurrentQueue(@NotNull UUID uuid) {
110 return queues.values().stream()
111 .filter(q -> q.isPlayerQueued(uuid)).findFirst();
112 }
113
114 /**
115 * Remove a player from all queues
116 * @param player the player to remove
117 */
118 public void clearPlayer(Player player) {
119 clearPlayer(player, true);
120 }
121
122 /**
123 * Remove a player from all queues
124 * @param uuid the UUID of the player to remove
125 */
126 public void clearPlayer(UUID uuid) {
127 clearPlayer(uuid, true);
128 }
129
130 /**
131 * Remove a player from all queues
132 * @param player The player to remove
133 */
134 public void clearPlayer(Player player, boolean silent) {
135 queues.forEach((server, queue) -> queue.removePlayer(player, false));
136
137 if(silent) {
138 return;
139 }
140
141 notifyQueueRemoval(player, "commands.leave-success", MessageType.INFO);
142 }
143
144 /**
145 * Remove a player from all queues
146 * @param uuid The UUID of the player to remove
147 */
148 public void clearPlayer(UUID uuid, boolean silent) {
149 queues.forEach((server, queue) -> queue.removePlayer(uuid, false));
150
151 if(silent) {
152 return;
153 }
154
155 ProxyQueuesImpl.getInstance().getProxyServer().getPlayer(uuid).ifPresent(
156 onlinePlayer -> notifyQueueRemoval(onlinePlayer, "commands.leave-success", MessageType.INFO));
157 }
158
159 public void kickPlayer(Player player) {
160 clearPlayer(player, true);
161 notifyQueueRemoval(player, "errors.queue-removed", MessageType.ERROR);
162 }
163
164 public void kickPlayer(UUID uuid) {
165 clearPlayer(uuid, true);
166
167 ProxyQueuesImpl.getInstance().getProxyServer().getPlayer(uuid).ifPresent(
168 onlinePlayer -> notifyQueueRemoval(onlinePlayer, "errors.queue-removed", MessageType.ERROR));
169 }
170
171 private void notifyQueueRemoval(Player player, String message, MessageType messageType) {
172 if(!player.isActive()) {
173 return;
174 }
175
176 RegisteredServer waitingServer = proxyQueues.getWaitingServer().orElse(null);
177 Optional<ServerConnection> currentServer = player.getCurrentServer();
178
179 if(currentServer.isPresent() && currentServer.get().getServer().equals(waitingServer)) {
180 player.disconnect(Messages.getComponent(message));
181 } else {
182 proxyQueues.sendMessage(player, messageType, message);
183 }
184 }
185
186 /**
187 * Updates all queues from the config, creating/updating/deleting as required
188 */
189 public void updateQueues() {
190 ArrayList<RegisteredServer> queuedServers = new ArrayList<>();
191
192 settingsManager.getProperty(ConfigOptions.QUEUE_SERVERS).forEach(s -> {
193 try {
194 String[] split = s.split(";");
195 String serverName = split[0];
196
197 int requiredPlayers = Integer.parseInt(split[1]),
198 maxNormal = Integer.parseInt(split[2]),
199 maxPriority = Integer.parseInt(split[3]),
200 maxStaff = Integer.parseInt(split[4]);
201
202 RegisteredServer server = proxyQueues.getProxyServer().getServer(serverName).orElseThrow();
203
204 ProxyQueue queue = createQueue(server, requiredPlayers, maxNormal, maxPriority, maxStaff);
205 queue.setDelayLength(settingsManager.getProperty(ConfigOptions.DELAY_LENGTH));
206
207 queuedServers.add(server);
208 } catch (Exception ex) {
209 proxyQueues.getLogger().warn("It seems like one of your servers was configured invalidly in the config.");
210 ex.printStackTrace();
211 }
212 });
213
214 Iterator<Map.Entry<RegisteredServer, ProxyQueue>> it = queues.entrySet().iterator();
215
216 //Delete queues removed from config
217 while(it.hasNext()) {
218 Map.Entry<RegisteredServer, ProxyQueue> pair = it.next();
219
220 if(!queuedServers.contains(pair.getKey())) {
221 proxyQueues.getLogger().info("Deleting queue for " + pair.getKey().getServerInfo().getName());
222 pair.getValue().destroy();
223 it.remove();
224 }
225 }
226 }
227}