Velocity queueing solution
at master 189 lines 7.6 kB view raw
1/* 2 * ProxyQueues, a Velocity queueing solution 3 * 4 * Copyright (c) 2021 James Lyne 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in all 14 * copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 */ 23 24package uk.co.notnull.proxyqueues.queues; 25 26import ch.jalu.configme.SettingsManager; 27import com.velocitypowered.api.event.PostOrder; 28import com.velocitypowered.api.event.Subscribe; 29import com.velocitypowered.api.event.connection.DisconnectEvent; 30import com.velocitypowered.api.event.player.KickedFromServerEvent; 31import com.velocitypowered.api.event.player.ServerPostConnectEvent; 32import com.velocitypowered.api.event.player.ServerPreConnectEvent; 33import com.velocitypowered.api.proxy.Player; 34import com.velocitypowered.api.proxy.ServerConnection; 35import com.velocitypowered.api.proxy.server.RegisteredServer; 36import net.kyori.adventure.text.Component; 37import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; 38import uk.co.notnull.proxyqueues.Messages; 39import uk.co.notnull.proxyqueues.ProxyQueuesImpl; 40import uk.co.notnull.proxyqueues.api.QueueType; 41import uk.co.notnull.proxyqueues.api.queues.QueuePlayer; 42import uk.co.notnull.proxyqueues.configuration.sections.ConfigOptions; 43 44import java.time.Instant; 45import java.util.*; 46 47public class ProxyQueueEventHandler { 48 private final ProxyQueuesImpl proxyQueues; 49 private final SettingsManager settingsManager; 50 private final ProxyQueueImpl queue; 51 52 public ProxyQueueEventHandler(ProxyQueuesImpl proxyQueues, ProxyQueueImpl queue) { 53 this.settingsManager = proxyQueues.getSettingsHandler().getSettingsManager(); 54 this.proxyQueues = proxyQueues; 55 this.queue = queue; 56 } 57 58 @Subscribe(priority = Short.MIN_VALUE / 2) 59 public void onPreConnect(ServerPreConnectEvent event) { 60 if(!event.getResult().isAllowed()) { 61 return; 62 } 63 64 RegisteredServer server = event.getOriginalServer(); 65 Player player = event.getPlayer(); 66 RegisteredServer redirected = event.getResult().getServer().orElse(null); 67 68 if(redirected != null && !redirected.equals(server)) { 69 server = redirected; 70 } 71 72 // Check if the server has a queue 73 if(!server.equals(queue.getServer()) || !this.queue.isActive()) { 74 return; 75 } 76 77 // Get the queue 78 Optional<QueuePlayer> queuePlayer = queue.getQueuePlayer(player, true); 79 80 if(queuePlayer.isEmpty()) { 81 if(event.getPlayer().getCurrentServer().isEmpty()) { 82 Optional<RegisteredServer> waitingServer = proxyQueues.getWaitingServer(); 83 84 if(waitingServer.isPresent()) { 85 event.setResult(ServerPreConnectEvent.ServerResult.allowed(waitingServer.get())); 86 } else { 87 player.disconnect(Messages.getComponent("cannot-connect-directly")); 88 89 return; 90 } 91 } else { 92 // Cancel the event so they don't go right away 93 event.setResult(ServerPreConnectEvent.ServerResult.denied()); 94 } 95 96 // Add the player to the queue 97 queue.addPlayer(player); 98 } else if(!queuePlayer.get().isConnecting()) { 99 event.setResult(ServerPreConnectEvent.ServerResult.denied()); 100 } 101 } 102 103 @Subscribe(priority = Short.MIN_VALUE / 2) 104 public void onConnected(ServerPostConnectEvent event) { 105 RegisteredServer server = event.getPlayer().getCurrentServer() 106 .map(ServerConnection::getServer).orElse(null); 107 108 if(server != null) { 109 if(server.equals(queue.getServer())) { 110 queue.removePlayer(event.getPlayer(), true); 111 } else { 112 queue.getQueuePlayer(event.getPlayer(), true).ifPresent(p -> { 113 queue.getNotifier().notifyPlayer(p); 114 }); 115 } 116 } 117 118 RegisteredServer previousServer = event.getPreviousServer(); 119 120 if(previousServer != null && previousServer.equals(this.queue.getServer())) { 121 queue.clearConnectedState(event.getPlayer()); 122 } 123 } 124 125 /** 126 * Handles a player disconnecting from the proxy 127 * If they are queued their lastSeen time will be set 128 * Otherwise they will be added to the priority queue with a lastSeen time set 129 * @param event - The event 130 */ 131 @Subscribe(priority = Short.MAX_VALUE / 2) 132 public void onLeave(DisconnectEvent event) { 133 Player player = event.getPlayer(); 134 Optional<QueuePlayer> queuePlayer = queue.getQueuePlayer(player, false); 135 queue.clearConnectedState(player); 136 137 proxyQueues.getLogger().debug("Handling proxy disconnect for " + player.getUsername()); 138 139 if (queuePlayer.isPresent()) { 140 proxyQueues.getLogger().debug("Player in queue, setting last seen"); 141 ((QueuePlayerImpl) queuePlayer.get()).setLastSeen(Instant.now()); 142 return; 143 } 144 145 player.getCurrentServer().ifPresent(server -> { 146 if(server.getServer().equals(queue.getServer())) { 147 boolean staff = player.hasPermission(settingsManager.getProperty(ConfigOptions.STAFF_PERMISSION)); 148 proxyQueues.getLogger().debug( 149 "Player not in queue, adding to " + (staff ? "staff" : "priority") + " queue"); 150 queue.addPlayer(player, staff ? QueueType.STAFF : QueueType.PRIORITY); 151 } 152 }); 153 } 154 155 @Subscribe(priority = Short.MIN_VALUE + 1) 156 public void onKick(KickedFromServerEvent event) { 157 if (!event.getServer().equals(queue.getServer()) || event.kickedDuringServerConnect()) { 158 return; 159 } 160 161 proxyQueues.getLogger() 162 .debug("Player " + event.getPlayer().getUsername() + " kicked from " + 163 event.getServer().getServerInfo().getName() + ". Reason: " + event.getServerKickReason()); 164 165 Component reason = event.getServerKickReason().orElse(Component.empty()); 166 String reasonPlain = PlainTextComponentSerializer.plainText().serialize(reason); 167 List<String> fatalErrors = proxyQueues.getSettingsHandler().getSettingsManager().getProperty( 168 ConfigOptions.FATAL_ERRORS); 169 Optional<RegisteredServer> waitingServer = proxyQueues.getWaitingServer(); 170 171 boolean fatal = fatalErrors.stream().anyMatch(reasonPlain::contains); 172 173 if (!fatal) { 174 proxyQueues.getLogger() 175 .debug("Reason not fatal, re-queueing " + event.getPlayer().getUsername()); 176 177 boolean staff = event.getPlayer() 178 .hasPermission(settingsManager.getProperty(ConfigOptions.STAFF_PERMISSION)); 179 180 queue.addPlayer(event.getPlayer(), staff ? QueueType.STAFF : QueueType.PRIORITY); 181 182 if (event.getResult() instanceof KickedFromServerEvent.DisconnectPlayer && waitingServer.isPresent()) { 183 event.setResult(KickedFromServerEvent.RedirectPlayer.create(waitingServer.get())); 184 } 185 } else { 186 proxyQueues.getLogger().debug("Reason fatal"); 187 } 188 } 189}