Velocity queueing solution
at master 9.6 kB view raw
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; 27 28import cloud.commandframework.CommandManager; 29import cloud.commandframework.annotations.AnnotationParser; 30import cloud.commandframework.execution.CommandExecutionCoordinator; 31import cloud.commandframework.meta.SimpleCommandMeta; 32import cloud.commandframework.minecraft.extras.MinecraftExceptionHandler; 33import cloud.commandframework.velocity.VelocityCommandManager; 34import com.velocitypowered.api.command.CommandSource; 35import com.velocitypowered.api.event.ResultedEvent; 36import com.velocitypowered.api.event.Subscribe; 37import com.velocitypowered.api.event.connection.LoginEvent; 38import com.velocitypowered.api.event.connection.PluginMessageEvent; 39import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; 40import com.velocitypowered.api.plugin.annotation.DataDirectory; 41import com.velocitypowered.api.proxy.ProxyServer; 42import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; 43import com.velocitypowered.api.proxy.server.RegisteredServer; 44import net.kyori.adventure.text.ComponentLike; 45import net.kyori.adventure.text.minimessage.MiniMessage; 46import org.spongepowered.configurate.ConfigurationNode; 47import org.spongepowered.configurate.yaml.YamlConfigurationLoader; 48import uk.co.notnull.proxyqueues.api.MessageType; 49import uk.co.notnull.proxyqueues.api.ProxyQueues; 50import uk.co.notnull.proxyqueues.api.queues.QueueHandler; 51import uk.co.notnull.proxyqueues.configuration.SettingsHandler; 52import uk.co.notnull.proxyqueues.configuration.sections.ConfigOptions; 53import uk.co.notnull.proxyqueues.metrics.Metrics; 54import uk.co.notnull.proxyqueues.queues.QueueHandlerImpl; 55import org.slf4j.Logger; 56import uk.co.notnull.vanishbridge.helper.CloudHelper; 57import uk.co.notnull.vanishbridge.helper.VanishBridgeHelper; 58 59import javax.inject.Inject; 60import java.io.File; 61import java.io.IOException; 62import java.io.InputStream; 63import java.nio.file.Files; 64import java.nio.file.Path; 65import java.util.Collections; 66import java.util.Map; 67import java.util.Optional; 68import java.util.function.Function; 69 70public final class ProxyQueuesImpl implements ProxyQueues { 71 public static final MinecraftChannelIdentifier playerWaitingIdentifier = 72 MinecraftChannelIdentifier.create("proxyqueues", "player_waiting"); 73 74 private static ProxyQueuesImpl instance; 75 private final SettingsHandler settingsHandler; 76 private QueueHandlerImpl queueHandler; 77 private static final MiniMessage miniMessage = MiniMessage.miniMessage(); 78 private int playerLimit; 79 80 private final ProxyServer proxyServer; 81 private final Logger logger; 82 private final Path dataFolder; 83 84 @Inject 85 public ProxyQueuesImpl(ProxyServer proxy, Logger logger, @DataDirectory Path dataFolder) { 86 this.proxyServer = proxy; 87 this.logger = logger; 88 this.dataFolder = dataFolder; 89 instance = this; 90 91 createFile("config.yml"); 92 createFile("messages.yml"); 93 94 loadMessagesConfig(); 95 96 settingsHandler = new SettingsHandler(this); 97 playerLimit = settingsHandler.getSettingsManager().getProperty(ConfigOptions.PLAYER_LIMIT); 98 } 99 100 public void loadMessagesConfig() { 101 //Message config 102 ConfigurationNode messagesConfiguration; 103 104 try { 105 messagesConfiguration = YamlConfigurationLoader.builder().file( 106 new File(dataFolder.toAbsolutePath().toString(), "messages.yml")).build().load(); 107 Messages.set(messagesConfiguration); 108 } catch (IOException e) { 109 logger.error("Error loading messages.yml"); 110 e.printStackTrace(); 111 } 112 } 113 114 @Subscribe 115 public void onProxyInitialize(ProxyInitializeEvent event) { 116 startQueues(); 117 initCommands(); 118 119 if(this.getProxyServer().getPluginManager().isLoaded("prometheus-exporter")) { 120 try { 121 new Metrics(this); 122 } catch(IllegalArgumentException e) { 123 logger.warn("Failed to register prometheus metrics", e); 124 } 125 } 126 127 if(this.getProxyServer().getPluginManager().isLoaded("proxydiscord")) { 128 new ProxyDiscordHandler(this); 129 } 130 131 proxyServer.getChannelRegistrar().register(playerWaitingIdentifier); 132 new VanishBridgeHelper(proxyServer); 133 } 134 135 @Subscribe 136 public void onLogin(LoginEvent event) { 137 if(playerLimit > -1 && proxyServer.getPlayerCount() >= playerLimit && 138 !event.getPlayer().hasPermission("proxyqueues.bypass-limit")) { 139 event.setResult(ResultedEvent.ComponentResult.denied(Messages.getComponent("errors.player-limit"))); 140 } 141 } 142 143 @Subscribe 144 public void onPluginMessageFromPlayer(PluginMessageEvent event) { 145 // Don't allow spoofing of plugin messages 146 if (playerWaitingIdentifier.equals(event.getIdentifier())) { 147 event.setResult(PluginMessageEvent.ForwardResult.handled()); 148 } 149 } 150 151 public void initCommands() { 152 CommandManager<CommandSource> manager = new VelocityCommandManager<>( 153 proxyServer.getPluginManager().fromInstance(this).orElseThrow(), 154 proxyServer, 155 CommandExecutionCoordinator.simpleCoordinator(), 156 Function.identity(), 157 Function.identity()); 158 159 new MinecraftExceptionHandler<CommandSource>() 160 .withArgumentParsingHandler() 161 .withInvalidSenderHandler() 162 .withInvalidSyntaxHandler() 163 .withNoPermissionHandler() 164 .withCommandExecutionHandler() 165 .withDecorator(message -> message) 166 .apply(manager, p -> p); 167 168 AnnotationParser<CommandSource> annotationParser = new AnnotationParser<>( 169 manager, 170 CommandSource.class, 171 parameters -> SimpleCommandMeta.empty() 172 ); 173 174 CloudHelper.registerVisiblePlayerArgument(manager); 175 annotationParser.parse(new Commands(this, manager)); 176 } 177 178 /** 179 * Create a file to be used in the plugin 180 * @param name the name of the file 181 */ 182 private void createFile(String name) { 183 File folder = dataFolder.toFile(); 184 185 if (!folder.exists()) { 186 folder.mkdir(); 187 } 188 File languageFolder = new File(folder, "languages"); 189 if (!languageFolder.exists()) { 190 languageFolder.mkdirs(); 191 } 192 193 File file = new File(folder, name); 194 195 if (!file.exists()) { 196 try (InputStream in = getClass().getClassLoader().getResourceAsStream(name)) { 197 Files.copy(in, file.toPath()); 198 } catch (IOException ex) { 199 ex.printStackTrace(); 200 } 201 } 202 } 203 204 public SettingsHandler getSettingsHandler() { 205 return this.settingsHandler; 206 } 207 208 public QueueHandler getQueueHandler() { 209 return this.queueHandler; 210 } 211 212 public void startQueues() { 213 queueHandler = new QueueHandlerImpl(settingsHandler.getSettingsManager(), this); 214 } 215 216 public ProxyServer getProxyServer() { 217 return proxyServer; 218 } 219 220 public Logger getLogger() { 221 return logger; 222 } 223 224 public File getDataFolder() { 225 return dataFolder.toFile(); 226 } 227 228 public Optional<RegisteredServer> getWaitingServer() { 229 String waitingServerName = getSettingsHandler().getSettingsManager().getProperty(ConfigOptions.WAITING_SERVER); 230 return getProxyServer().getServer(waitingServerName); 231 } 232 233 public static ProxyQueuesImpl getInstance() { 234 return instance; 235 } 236 237 public void sendMessage(CommandSource player, String message) { 238 player.sendMessage(miniMessage.deserialize(message)); 239 } 240 241 public void sendMessage(CommandSource player, ComponentLike message) { 242 player.sendMessage(message); 243 } 244 245 public void sendMessage(CommandSource player, MessageType messageType, String message) { 246 sendMessage(player, messageType, message, Collections.emptyMap()); 247 } 248 249 public void sendMessage(CommandSource player, MessageType messageType, String message, Map<String, String> replacements) { 250 player.sendMessage( 251 miniMessage.deserialize(Messages.getPrefix(messageType) + Messages.get(message, replacements))); 252 } 253 254 public void setPlayerLimit(int playerLimit) { 255 this.playerLimit = Math.max(playerLimit, -1); 256 } 257}