package com.lifeknight.relaymcbungeemain.network; import com.google.common.io.Files; import com.lifeknight.relaymcbungeemain.Main; import com.lifeknight.relaymcbungeemain.utilities.Server; import com.lifeknight.relaymcbungeemain.utilities.Utilities; import com.lifeknight.relayutils.RelayUtils; import com.lifeknight.relayutils.basic.Miscellaneous; import com.lifeknight.relayutils.basic.Text; import com.lifeknight.relayutils.game.MainGame; import com.lifeknight.relayutils.network.Command; import com.lifeknight.relayutils.network.Message; import com.lifeknight.relayutils.network.Recipient; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeoutException; public class MessageServerHandler extends ChannelInboundHandlerAdapter { private static final UUID CLIENT_KEY = UUID.fromString("ec573093-e335-4af5-8a9b-bd97baf11b10"); public static final Map<String, MessageServerHandler> CHANNELS = new HashMap<>(); private long lastPing = System.currentTimeMillis(); private boolean hasAccess = false; private String serverName = null; private boolean updatedPlugin = false; private ChannelHandlerContext channelHandlerContext; public void sendMessage(Message message) { if (RelayUtils.messageLog) { Main.info("Sending message: %s | %s", message.getRecipient(), message.getCommand()); } this.channelHandlerContext.writeAndFlush(message); } public void sendMessage(Command command, Object... arguments) { this.sendMessage(new Message(command, arguments)); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { this.lastPing = System.currentTimeMillis(); Message message = (Message) msg; String[] arguments = message.getArgumentsStringArray(); if (message.getCommandCommand() == Command.TAKE_KEY) { UUID uuid = UUID.fromString(arguments[0]); if (!uuid.equals(CLIENT_KEY)) { ctx.close(); Main.warn("Closed connection; incorrect key was provided."); } else { this.serverName = message.getSender(); this.hasAccess = true; this.sendMessage(Command.ACCESS_GRANTED); if (arguments.length > 1) { String ip = arguments[1]; Server server = Server.getServerFromIP(ip); if (server != null) { this.serverName = server.getName(); if (!this.serverName.isEmpty()) { this.sendMessage(Command.NAME, server.getName()); } } else { server = Server.getServer(this.serverName); if (!this.serverName.equalsIgnoreCase("paper")) { Utilities.createNewServerInfo(this.serverName, ip); } } if (arguments.length > 2) { long bytes = Long.parseLong(arguments[2]); int gigaBytes = (int) (bytes / 1000000000L); if (server != null) { server.setMemoryGB(gigaBytes); } } } Main.info("Authorized connection: %s", this.serverName); CHANNELS.put(this.serverName, this); if (this.hasServer()) { this.getServer().setAvailableForPlayers(true); } this.getPluginVersion(); } } else if (message.getCommandCommand() == Command.PLUGIN) { if (message.hasArguments()) { if (arguments[0].equals("version")) { this.checkVersion(arguments[1]); } } } if (this.hasServer()) { this.getServer().setMessageClientActive(true); } Main.onMessageReceived(message); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { this.channelHandlerContext = ctx; if (RelayUtils.log) { Main.info("Channel active: %s", ctx.channel().remoteAddress()); } Main.schedule(() -> { if (!this.hasAccess) { if (ctx.channel().remoteAddress() != null) { Main.info("Closed connection with %s; no key was sent.", ctx.channel().remoteAddress()); } ctx.close(); } }, 5); if (this.hasServer()) { this.getServer().setMessageClientActive(true); } } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { if (this.hasServer()) { this.getServer().setMessageClientActive(false); } if (this.serverName != null) { Main.info("Connection lost: %s", this.serverName); } CHANNELS.remove(this.serverName); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (cause instanceof TimeoutException) { if (this.serverName != null) { Main.info("Timed out: %s", this.serverName); } } else { Main.error("Exception caught (%s): %s\n %s\n[%s]", this.serverName, cause.getMessage(), ctx.channel().remoteAddress(), Text.separateBy(Miscellaneous.fromAndTo(cause.getStackTrace(), 0, 5), "\n")); } } public static void sendMessageToServer(Message message) throws NullPointerException { MessageServerHandler messageServerHandler = CHANNELS.get(message.getRecipient()); if (messageServerHandler == null) { Main.error("Tried to send message, could not find channel: %s", message.getRecipient()); } else { messageServerHandler.sendMessage(message); } } public static void broadcastMessage(Message message) { for (MessageServerHandler messageServerHandler : CHANNELS.values()) { if (!messageServerHandler.serverName.equalsIgnoreCase(message.getSender())) { messageServerHandler.sendMessage(message); } } } public static void broadcastMessage(Command command, Object... arguments) { broadcastMessage(new Message(Recipient.NONE, command, arguments)); } public void getPluginVersion() { this.sendMessage(Command.PLUGIN, "version"); } public void updatePlugin(File plugin) { try { byte[] data = Files.toByteArray(plugin); Byte[] result = new Byte[data.length]; for (int i = 0; i < data.length; ++i) { result[i] = data[i]; } this.sendMessage(Command.PLUGIN, "set", plugin.getName(), Text.toCSV(result)); this.updatedPlugin = true; } catch (Exception exception) { Main.info("An error occurred while trying to update the plugin of %s: %s", this.serverName, exception.getMessage()); } } public void checkVersion(String pluginFileName) { if (this.hasServer()) { File pluginFile = Main.getPlugin(this.getServer()); if (!pluginFileName.isEmpty()) { for (MainGame value : MainGame.values()) { if (Text.comparablyContains(pluginFileName, value)) { if (this.getServer().getMainGame() != value) { this.updatePlugin(pluginFile); return; } } } } if (pluginFile != null && !pluginFile.getName().equals(pluginFileName)) { if (Utilities.parseVersion(pluginFile.getName()).isGreater(Utilities.parseVersion(pluginFileName)) && !this.updatedPlugin) { this.updatePlugin(pluginFile); Main.info("Updating plugin for %s. (%s vs %s)", this.serverName, pluginFile.getName(), pluginFileName); } else if (!this.updatedPlugin) { Main.info("%s is outdated! (%s)", pluginFile.getName(), pluginFileName); } } } } public Server getServer() { return Server.getServer(this.serverName); } public boolean hasServer() { return this.getServer() != null; } public long getLastPing() { return this.lastPing; } public void closeConnection() { if (this.hasServer()) { this.getServer().setMessageClientActive(false); } if (this.channelHandlerContext != null) { this.channelHandlerContext.close(); try { this.channelInactive(this.channelHandlerContext); } catch (Exception e) { Main.error("An error occurred while deliberately closing connection: %s", e.getMessage()); } } } }