diff --git a/Plugins/.idea/artifacts/Mineplex_Bungee_Mineplexer_test.xml b/Plugins/.idea/artifacts/Mineplex_Bungee_Mineplexer_test.xml new file mode 100644 index 000000000..3febfc9cc --- /dev/null +++ b/Plugins/.idea/artifacts/Mineplex_Bungee_Mineplexer_test.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/../Testing/Bungee/plugins + + + + + \ No newline at end of file diff --git a/Plugins/.idea/modules.xml b/Plugins/.idea/modules.xml index b53f9c93a..7da29f6c1 100644 --- a/Plugins/.idea/modules.xml +++ b/Plugins/.idea/modules.xml @@ -2,20 +2,19 @@ - - - - - + + + + + - - - - - - + + + + + + - - + \ No newline at end of file diff --git a/Plugins/.idea/runConfigurations/Bungee.xml b/Plugins/.idea/runConfigurations/Bungee.xml new file mode 100644 index 000000000..5c4651df7 --- /dev/null +++ b/Plugins/.idea/runConfigurations/Bungee.xml @@ -0,0 +1,31 @@ + + + + + \ No newline at end of file diff --git a/Plugins/Libraries/BungeeCord.jar b/Plugins/Libraries/BungeeCord.jar index 78370ccee..244abcf2a 100644 Binary files a/Plugins/Libraries/BungeeCord.jar and b/Plugins/Libraries/BungeeCord.jar differ diff --git a/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/motd/Motd.java b/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/motd/Motd.java new file mode 100644 index 000000000..b049ac8c9 --- /dev/null +++ b/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/motd/Motd.java @@ -0,0 +1,179 @@ +package mineplex.bungee.motd; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import com.google.gson.Gson; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.Favicon; +import net.md_5.bungee.api.ServerPing; +import net.md_5.bungee.api.event.ProxyPingEvent; +import net.md_5.bungee.connection.CustomMotd; +import net.md_5.bungee.connection.InitialHandler; +import net.md_5.bungee.protocol.packet.StatusResponse; +import org.apache.commons.io.comparator.NameFileComparator; + +public class Motd implements CustomMotd +{ + private static final int MAX_TICKS = 30; + private static final char SNOWFLAKE = '❅'; + private static final String SNOWMAN = ChatColor.WHITE + "☃" + ChatColor.RESET; + private static final String LEFT_SWORD = "§b§l§m §8§l§m[ §r"; + private static final String RIGHT_SWORD = "§8§l§m ]§b§l§m §r"; + private static final ChatColor[] COLOR_ROTATION = + {ChatColor.RED, ChatColor.LIGHT_PURPLE, ChatColor.GOLD, ChatColor.BLUE, ChatColor.YELLOW, ChatColor.GREEN, ChatColor.AQUA}; + private static List ICON_FRAMES; + + private MotdManager _manager; + private ScheduledFuture _task; + private int _ticks = 0; + + public Motd(MotdManager manager) + { + _manager = manager; + } + + public void handlePing(final ProxyPingEvent pingResult, final InitialHandler initialHandler) + { + BungeeCord.getInstance().getConnectionThrottle().unthrottle(initialHandler.getAddress().getAddress()); + final Gson gson = initialHandler.getHandshake().getProtocolVersion() == 4?BungeeCord.getInstance().gsonLegacy:BungeeCord.getInstance().gson; + + _task = initialHandler.getChannelWrapper().getHandle().eventLoop().scheduleAtFixedRate(new Runnable() + { + @Override + public void run() + { + if (initialHandler.getChannelWrapper().getHandle().isOpen() && _ticks <= MAX_TICKS) + { + ServerPing ping = pingResult.getResponse(); + + String desc = getDesc(); + ping.setDescription(desc); + + Favicon icon = getIcon(); + if (icon != null) + ping.setFavicon(icon); + + initialHandler.unsafe().sendPacket(new StatusResponse(gson.toJson(ping))); + _ticks++; + } + else + { + _task.cancel(true); + if (initialHandler.getChannelWrapper().getHandle().isOpen()) + initialHandler.getChannelWrapper().getHandle().close(); + } + } + }, 0, 200, TimeUnit.MILLISECONDS); + } + + private String getDesc() + { + int insetSpaces = 8; + int maxSpaces = 6; + + ChatColor mineplexColor = COLOR_ROTATION[_ticks % COLOR_ROTATION.length]; + int spaceLeftCount; + int spaceNumber = _ticks % (maxSpaces*2); + if (spaceNumber > maxSpaces) + spaceLeftCount = 2*maxSpaces - spaceNumber; + else + spaceLeftCount = spaceNumber; + + String spacesLeft = getRepeatedCharacters(' ', spaceLeftCount); + String spacesRight = getRepeatedCharacters(' ', maxSpaces - spaceLeftCount); + String insets = getRepeatedCharacters(' ', insetSpaces); + String desc = insets + spacesLeft + LEFT_SWORD + spacesRight + getMineplex(mineplexColor) + spacesRight + RIGHT_SWORD + spacesLeft; + + List lines = _manager.getMotdLines(); + if (lines != null && lines.size() > 0) + { + int index = _ticks / (MAX_TICKS / (lines.size()-1)); + String currentLine = index >= lines.size() ? lines.get(lines.size() - 1) : lines.get(index); + desc += "\n" + currentLine; + } + + return desc; + } + + private Favicon getIcon() + { + Favicon icon = null; + + if (ICON_FRAMES.size() > 0) + { + icon = ICON_FRAMES.get(_ticks % ICON_FRAMES.size()); + } + + return icon; + } + + private String getMineplex(ChatColor color) + { + return " " + color + ChatColor.BOLD.toString() + "Mineplex" + ChatColor.RESET + ChatColor.WHITE + ChatColor.BOLD + " Games" + ChatColor.RESET + " "; + } + + private String getRepeatedCharacters(char c, int count) + { + char[] spaces = new char[count]; + for (int i = 0; i < count; i++) + { + spaces[i] = c; + } + return new String(spaces); + } + + private static Comparator FILE_NUMBER_COMPARATOR = new Comparator() + { + @Override + public int compare(File f1, File f2) + { + int compareValue = 0; + + try + { + int i1 = Integer.parseInt(f1.getName().substring(0, f1.getName().indexOf('.'))); + int i2 = Integer.parseInt(f2.getName().substring(0, f2.getName().indexOf('.'))); + return i1 - i2; + } + catch (Exception e) {} + + return compareValue; + } + }; + + static + { + // Load icon animations + ICON_FRAMES = new ArrayList(); + + File iconFolder = new File("server-icon"); + if (iconFolder.exists() && iconFolder.isDirectory()) + { + File[] files = iconFolder.listFiles(); + Arrays.sort(files, FILE_NUMBER_COMPARATOR); + for (int i = 0; i < files.length; i++) + { + File file = files[i]; + try + { + BufferedImage image = ImageIO.read(file); + Favicon favicon = Favicon.create(image); + ICON_FRAMES.add(favicon); + } + catch (Exception e) + { + // Just ignore extra files + } + } + } + } +} diff --git a/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/motd/MotdManager.java b/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/motd/MotdManager.java index 5f8456aa7..9d1e1ee4a 100644 --- a/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/motd/MotdManager.java +++ b/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/motd/MotdManager.java @@ -1,42 +1,52 @@ package mineplex.bungee.motd; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; import net.md_5.bungee.api.event.PreLoginEvent; import net.md_5.bungee.api.event.ProxyPingEvent; import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.api.plugin.Plugin; +import net.md_5.bungee.connection.CustomMotd; +import net.md_5.bungee.connection.CustomMotdFactory; +import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.event.EventHandler; -public class MotdManager implements Listener, Runnable +public class MotdManager implements Listener, Runnable, CustomMotdFactory { private Plugin _plugin; private MotdRepository _repository; - - private String _motd = "§b§l§m §8§l§m[ §r §9§lMineplex§r §f§lGames§r §8§l§m ]§b§l§m §r §c§l§m§kZ§6§l§m§kZ§e§l§m§kZ§a§l§m§kZ§b§l§m§kZ§r §f§lPLAY NOW§r §b§l§m§kZ§a§l§m§kZ§e§l§m§kZ§6§l§m§kZ§c§l§m§kZ"; + + private List _motdLines; public MotdManager(Plugin plugin) { _plugin = plugin; - _plugin.getProxy().getScheduler().schedule(_plugin, this, 30L, 30L, TimeUnit.SECONDS); + _plugin.getProxy().getScheduler().schedule(_plugin, this, 5L, 30L, TimeUnit.SECONDS); _plugin.getProxy().getPluginManager().registerListener(_plugin, this); _repository = new MotdRepository(); _repository.initialize(); + + InitialHandler.setCustomMotdFactory(this); // For animated motd } - @EventHandler - public void ServerPing(ProxyPingEvent event) + @Override + public void run() { - net.md_5.bungee.api.ServerPing serverPing = event.getResponse(); - - event.setResponse(new net.md_5.bungee.api.ServerPing(serverPing.getVersion(), serverPing.getPlayers(), _motd, serverPing.getFaviconObject())); + _motdLines = _repository.retrieveMotd(); + } + + public List getMotdLines() + { + return _motdLines; } @Override - public void run() + public CustomMotd makeMotd() { - _motd = _repository.retrieveMotd(); + return new Motd(this); } } diff --git a/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/motd/MotdRepository.java b/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/motd/MotdRepository.java index 35399f532..c4a2758d4 100644 --- a/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/motd/MotdRepository.java +++ b/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/motd/MotdRepository.java @@ -5,6 +5,10 @@ import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import net.md_5.bungee.api.ChatColor; public class MotdRepository { @@ -51,9 +55,15 @@ public class MotdRepository System.out.println("Initialized MOTD."); } - public String retrieveMotd() + public List retrieveMotd() { - String motd = "§b§l§m §8§l§m[ §r §9§lMineplex§r §f§lGames§r §8§l§m ]§b§l§m §r §c§l§m§kZ§6§l§m§kZ§e§l§m§kZ§a§l§m§kZ§b§l§m§kZ§r §f§lPLAY NOW§r §b§l§m§kZ§a§l§m§kZ§e§l§m§kZ§6§l§m§kZ§c§l§m§kZ"; +// String motd = "§b§l§m §8§l§m[ §r §9§lMineplex§r §f§lGames§r §8§l§m ]§b§l§m §r §c§l§m§kZ§6§l§m§kZ§e§l§m§kZ§a§l§m§kZ§b§l§m§kZ§r §f§lPLAY NOW§r §b§l§m§kZ§a§l§m§kZ§e§l§m§kZ§6§l§m§kZ§c§l§m§kZ"; + ArrayList lines = new ArrayList(); +// lines.add(" " + ChatColor.WHITE + ChatColor.BOLD + "New Game" + ChatColor.RED + ChatColor.BOLD + " Christmas Chaos"); +// lines.add(" " + ChatColor.WHITE + ChatColor.BOLD + "Winter Sale" + ChatColor.BLUE + ChatColor.BOLD + " 33% Off Everything"); +// lines.add(" " + ChatColor.WHITE + ChatColor.BOLD + "New Game" + ChatColor.RED + ChatColor.BOLD + " Christmas Chaos"); +// return lines; + ResultSet resultSet = null; PreparedStatement preparedStatement = null; @@ -67,7 +77,7 @@ public class MotdRepository while (resultSet.next()) { - return resultSet.getString(1); + lines.add(resultSet.getString(1)); } } catch (Exception exception) @@ -101,6 +111,6 @@ public class MotdRepository } } - return motd; + return lines; } } diff --git a/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/playerCount/PlayerCount.java b/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/playerCount/PlayerCount.java index 1f02f7c92..e13087c71 100644 --- a/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/playerCount/PlayerCount.java +++ b/Plugins/Mineplex.Bungee.Mineplexer/src/mineplex/bungee/playerCount/PlayerCount.java @@ -44,7 +44,7 @@ public class PlayerCount implements Listener, Runnable public void ServerPing(ProxyPingEvent event) { net.md_5.bungee.api.ServerPing serverPing = event.getResponse(); - - event.setResponse(new net.md_5.bungee.api.ServerPing(serverPing.getVersion(), new Players(_totalPlayers + 1, _totalPlayers, null), serverPing.getDescription(), serverPing.getFaviconObject())); + + event.setResponse(new net.md_5.bungee.api.ServerPing(serverPing.getVersion(), new Players(0, _totalPlayers, null), serverPing.getDescription(), serverPing.getFaviconObject())); } } diff --git a/Plugins/Mineplex.Bungee.Mineplexer/src/net/md_5/bungee/connection/CustomMotd.java b/Plugins/Mineplex.Bungee.Mineplexer/src/net/md_5/bungee/connection/CustomMotd.java new file mode 100644 index 000000000..2886bffd3 --- /dev/null +++ b/Plugins/Mineplex.Bungee.Mineplexer/src/net/md_5/bungee/connection/CustomMotd.java @@ -0,0 +1,8 @@ +package net.md_5.bungee.connection; + +import net.md_5.bungee.api.event.ProxyPingEvent; + +public interface CustomMotd +{ + public void handlePing(ProxyPingEvent event, InitialHandler initialHandler); +} diff --git a/Plugins/Mineplex.Bungee.Mineplexer/src/net/md_5/bungee/connection/CustomMotdFactory.java b/Plugins/Mineplex.Bungee.Mineplexer/src/net/md_5/bungee/connection/CustomMotdFactory.java new file mode 100644 index 000000000..53e21d844 --- /dev/null +++ b/Plugins/Mineplex.Bungee.Mineplexer/src/net/md_5/bungee/connection/CustomMotdFactory.java @@ -0,0 +1,6 @@ +package net.md_5.bungee.connection; + +public interface CustomMotdFactory +{ + public CustomMotd makeMotd(); +} diff --git a/Plugins/Mineplex.Bungee.Mineplexer/src/net/md_5/bungee/connection/InitialHandler.java b/Plugins/Mineplex.Bungee.Mineplexer/src/net/md_5/bungee/connection/InitialHandler.java new file mode 100644 index 000000000..a18770518 --- /dev/null +++ b/Plugins/Mineplex.Bungee.Mineplexer/src/net/md_5/bungee/connection/InitialHandler.java @@ -0,0 +1,469 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package net.md_5.bungee.connection; + +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import com.google.gson.Gson; +import java.beans.ConstructorProperties; +import java.math.BigInteger; +import java.net.InetSocketAddress; +import java.net.URLEncoder; +import java.nio.channels.Channel; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import javax.crypto.SecretKey; +import net.md_5.bungee.BungeeCipher; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.BungeeServerInfo; +import net.md_5.bungee.EncryptionUtil; +import net.md_5.bungee.UserConnection; +import net.md_5.bungee.Util; +import net.md_5.bungee.api.AbstractReconnectHandler; +import net.md_5.bungee.api.Callback; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.Favicon; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.ServerPing; +import net.md_5.bungee.api.ServerPing.PlayerInfo; +import net.md_5.bungee.api.ServerPing.Players; +import net.md_5.bungee.api.ServerPing.Protocol; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.config.ListenerInfo; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.api.connection.PendingConnection; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.connection.Connection.Unsafe; +import net.md_5.bungee.api.event.LoginEvent; +import net.md_5.bungee.api.event.PlayerHandshakeEvent; +import net.md_5.bungee.api.event.PostLoginEvent; +import net.md_5.bungee.api.event.PreLoginEvent; +import net.md_5.bungee.api.event.ProxyPingEvent; +import net.md_5.bungee.chat.ComponentSerializer; +import net.md_5.bungee.connection.LoginResult; +import net.md_5.bungee.connection.UpstreamBridge; +import net.md_5.bungee.http.HttpClient; +import net.md_5.bungee.netty.ChannelWrapper; +import net.md_5.bungee.netty.HandlerBoss; +import net.md_5.bungee.netty.PacketHandler; +import net.md_5.bungee.netty.cipher.CipherDecoder; +import net.md_5.bungee.netty.cipher.CipherEncoder; +import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.packet.EncryptionRequest; +import net.md_5.bungee.protocol.packet.EncryptionResponse; +import net.md_5.bungee.protocol.packet.Handshake; +import net.md_5.bungee.protocol.packet.Kick; +import net.md_5.bungee.protocol.packet.LegacyHandshake; +import net.md_5.bungee.protocol.packet.LegacyPing; +import net.md_5.bungee.protocol.packet.LoginRequest; +import net.md_5.bungee.protocol.packet.LoginSuccess; +import net.md_5.bungee.protocol.packet.PingPacket; +import net.md_5.bungee.protocol.packet.PluginMessage; +import net.md_5.bungee.protocol.packet.StatusRequest; +import net.md_5.bungee.protocol.packet.StatusResponse; + +public class InitialHandler extends PacketHandler implements PendingConnection { + + private static CustomMotdFactory _customMotdFactory; + + private final ProxyServer bungee; + private ChannelWrapper ch; + private final ListenerInfo listener; + private Handshake handshake; + private LoginRequest loginRequest; + private EncryptionRequest request; + private final List registerMessages = new ArrayList(); + private InitialHandler.State thisState; + private final Unsafe unsafe; + private boolean onlineMode; + private InetSocketAddress virtualHost; + private UUID uniqueId; + private UUID offlineId; + private LoginResult loginProfile; + private boolean legacy; + + public void connected(ChannelWrapper channel) throws Exception { + this.ch = channel; + } + + public void exception(Throwable t) throws Exception { + this.disconnect((String)(ChatColor.RED + Util.exception(t))); + } + + public void handle(PluginMessage pluginMessage) throws Exception { + if(pluginMessage.getTag().equals("REGISTER")) { + Preconditions.checkState(this.registerMessages.size() < 128, "Too many channels registered"); + this.registerMessages.add(pluginMessage); + } + + } + + public void handle(LegacyHandshake legacyHandshake) throws Exception { + this.legacy = true; + this.ch.getHandle().writeAndFlush(this.bungee.getTranslation("outdated_client", new Object[0])); + this.ch.close(); + } + + public void handle(LegacyPing ping) throws Exception { + this.legacy = true; + final boolean v1_5 = ping.isV1_5(); + ServerPing legacy = new ServerPing(new Protocol(this.bungee.getName() + " " + this.bungee.getGameVersion(), this.bungee.getProtocolVersion()), new Players(this.listener.getMaxPlayers(), this.bungee.getOnlineCount(), (PlayerInfo[])null), this.listener.getMotd(), (Favicon)null); + Callback callback = new Callback() { + public void done(ProxyPingEvent result, Throwable error) { + if(!InitialHandler.this.ch.isClosed()) { + ServerPing legacy = result.getResponse(); + String kickMessage; + if(v1_5) { + kickMessage = ChatColor.DARK_BLUE + "\u0000" + 127 + '\u0000' + legacy.getVersion().getName() + '\u0000' + InitialHandler.getFirstLine(legacy.getDescription()) + '\u0000' + legacy.getPlayers().getOnline() + '\u0000' + legacy.getPlayers().getMax(); + } else { + kickMessage = ChatColor.stripColor(InitialHandler.getFirstLine(legacy.getDescription())) + '§' + legacy.getPlayers().getOnline() + '§' + legacy.getPlayers().getMax(); + } + + InitialHandler.this.ch.getHandle().writeAndFlush(kickMessage); + InitialHandler.this.ch.close(); + } + } + }; + this.bungee.getPluginManager().callEvent(new ProxyPingEvent(this, legacy, callback)); + } + + private static String getFirstLine(String str) { + int pos = str.indexOf(10); + return pos == -1?str:str.substring(0, pos); + } + + public void handle(StatusRequest statusRequest) throws Exception { + Preconditions.checkState(this.thisState == InitialHandler.State.STATUS, "Not expecting STATUS"); + ServerInfo forced = AbstractReconnectHandler.getForcedHost(this); + String motd = forced != null?forced.getMotd():this.listener.getMotd(); + Callback pingBack = new Callback() { + public void done(ServerPing result, Throwable error) { + if(error != null) { + result = new ServerPing(); + result.setDescription(InitialHandler.this.bungee.getTranslation("ping_cannot_connect", new Object[0])); + InitialHandler.this.bungee.getLogger().log(Level.WARNING, "Error pinging remote server", error); + } + + Callback callback = new Callback() { + public void done(ProxyPingEvent pingResult, Throwable error) { + // MINEPLEX + if (_customMotdFactory != null) + { + _customMotdFactory.makeMotd().handlePing(pingResult, InitialHandler.this); + } + else + { + BungeeCord.getInstance().getConnectionThrottle().unthrottle(InitialHandler.this.getAddress().getAddress()); + Gson gson = InitialHandler.this.handshake.getProtocolVersion() == 4?BungeeCord.getInstance().gsonLegacy:BungeeCord.getInstance().gson; + InitialHandler.this.unsafe.sendPacket(new StatusResponse(gson.toJson(pingResult.getResponse()))); + } + } + }; + InitialHandler.this.bungee.getPluginManager().callEvent(new ProxyPingEvent(InitialHandler.this, result, callback)); + } + }; + if(forced != null && this.listener.isPingPassthrough()) { + ((BungeeServerInfo)forced).ping(pingBack, this.handshake.getProtocolVersion()); + } else { + int protocol = net.md_5.bungee.protocol.Protocol.supportedVersions.contains(Integer.valueOf(this.handshake.getProtocolVersion()))?this.handshake.getProtocolVersion():this.bungee.getProtocolVersion(); + pingBack.done(new ServerPing(new Protocol(this.bungee.getName() + " " + this.bungee.getGameVersion(), protocol), new Players(this.listener.getMaxPlayers(), this.bungee.getOnlineCount(), (PlayerInfo[])null), motd, BungeeCord.getInstance().config.getFaviconObject()), (Throwable)null); + } + + this.thisState = InitialHandler.State.PING; + } + + public void handle(PingPacket ping) throws Exception { + if (thisState == State.PING) return; // MINEPLEX + Preconditions.checkState(this.thisState == InitialHandler.State.PING, "Not expecting PING"); + this.unsafe.sendPacket(ping); + this.disconnect((String)""); + } + + public void handle(Handshake handshake) throws Exception { + Preconditions.checkState(this.thisState == InitialHandler.State.HANDSHAKE, "Not expecting HANDSHAKE"); + this.handshake = handshake; + this.ch.setVersion(handshake.getProtocolVersion()); + if(handshake.getHost().endsWith(".")) { + handshake.setHost(handshake.getHost().substring(0, handshake.getHost().length() - 1)); + } + + this.virtualHost = InetSocketAddress.createUnresolved(handshake.getHost(), handshake.getPort()); + this.bungee.getLogger().log(Level.INFO, "{0} has connected", this); + this.bungee.getPluginManager().callEvent(new PlayerHandshakeEvent(this, handshake)); + switch(handshake.getRequestedProtocol()) { + case 1: + this.thisState = InitialHandler.State.STATUS; + this.ch.setProtocol(net.md_5.bungee.protocol.Protocol.STATUS); + break; + case 2: + this.thisState = InitialHandler.State.USERNAME; + this.ch.setProtocol(net.md_5.bungee.protocol.Protocol.LOGIN); + break; + default: + throw new IllegalArgumentException("Cannot request protocol " + handshake.getRequestedProtocol()); + } + + } + + public void handle(LoginRequest loginRequest) throws Exception { + Preconditions.checkState(this.thisState == InitialHandler.State.USERNAME, "Not expecting USERNAME"); + this.loginRequest = loginRequest; + if(!net.md_5.bungee.protocol.Protocol.supportedVersions.contains(Integer.valueOf(this.handshake.getProtocolVersion()))) { + this.disconnect((String)this.bungee.getTranslation("outdated_server", new Object[0])); + } else if(this.getName().contains(".")) { + this.disconnect((String)this.bungee.getTranslation("name_invalid", new Object[0])); + } else if(this.getName().length() > 16) { + this.disconnect((String)this.bungee.getTranslation("name_too_long", new Object[0])); + } else { + int limit = BungeeCord.getInstance().config.getPlayerLimit(); + if(limit > 0 && this.bungee.getOnlineCount() > limit) { + this.disconnect((String)this.bungee.getTranslation("proxy_full", new Object[0])); + } else if(!this.isOnlineMode() && this.bungee.getPlayer(this.getName()) != null) { + this.disconnect((String)this.bungee.getTranslation("already_connected", new Object[0])); + } else { + Callback callback = new Callback() { + public void done(PreLoginEvent result, Throwable error) { + if(result.isCancelled()) { + InitialHandler.this.disconnect((String)result.getCancelReason()); + } else if(!InitialHandler.this.ch.isClosed()) { + if(InitialHandler.this.onlineMode) { + InitialHandler.this.unsafe().sendPacket(InitialHandler.this.request = EncryptionUtil.encryptRequest()); + } else { + InitialHandler.this.finish(); + } + + InitialHandler.this.thisState = InitialHandler.State.ENCRYPT; + } + } + }; + this.bungee.getPluginManager().callEvent(new PreLoginEvent(this, callback)); + } + } + } + + public void handle(EncryptionResponse encryptResponse) throws Exception { + Preconditions.checkState(this.thisState == InitialHandler.State.ENCRYPT, "Not expecting ENCRYPT"); + SecretKey sharedKey = EncryptionUtil.getSecret(encryptResponse, this.request); + BungeeCipher decrypt = EncryptionUtil.getCipher(false, sharedKey); + this.ch.addBefore("frame-decoder", "decrypt", new CipherDecoder(decrypt)); + BungeeCipher encrypt = EncryptionUtil.getCipher(true, sharedKey); + this.ch.addBefore("frame-prepender", "encrypt", new CipherEncoder(encrypt)); + String encName = URLEncoder.encode(this.getName(), "UTF-8"); + MessageDigest sha = MessageDigest.getInstance("SHA-1"); + byte[][] encodedHash = new byte[][]{this.request.getServerId().getBytes("ISO_8859_1"), sharedKey.getEncoded(), EncryptionUtil.keys.getPublic().getEncoded()}; + int authURL = encodedHash.length; + + for(int handler = 0; handler < authURL; ++handler) { + byte[] bit = encodedHash[handler]; + sha.update(bit); + } + + String var11 = URLEncoder.encode((new BigInteger(sha.digest())).toString(16), "UTF-8"); + String var12 = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=" + encName + "&serverId=" + var11; + Callback var13 = new Callback() { + public void done(String result, Throwable error) { + if(error == null) { + LoginResult obj = (LoginResult)BungeeCord.getInstance().gson.fromJson(result, LoginResult.class); + if(obj != null) { + InitialHandler.this.loginProfile = obj; + InitialHandler.this.uniqueId = Util.getUUID(obj.getId()); + InitialHandler.this.finish(); + return; + } + + InitialHandler.this.disconnect((String)"Not authenticated with Minecraft.net"); + } else { + InitialHandler.this.disconnect((String)InitialHandler.this.bungee.getTranslation("mojang_fail", new Object[0])); + InitialHandler.this.bungee.getLogger().log(Level.SEVERE, "Error authenticating " + InitialHandler.this.getName() + " with minecraft.net", error); + } + + } + }; + HttpClient.get(var12, this.ch.getHandle().eventLoop(), var13); + } + + private void finish() { + ProxiedPlayer old = this.bungee.getPlayer(this.getName()); + if(old != null) { + old.disconnect(this.bungee.getTranslation("already_connected", new Object[0])); + } + + this.offlineId = UUID.nameUUIDFromBytes(("OfflinePlayer:" + this.getName()).getBytes(Charsets.UTF_8)); + if(this.uniqueId == null) { + this.uniqueId = this.offlineId; + } + + Callback complete = new Callback() { + public void done(LoginEvent result, Throwable error) { + if(result.isCancelled()) { + InitialHandler.this.disconnect((String)result.getCancelReason()); + } else if(!InitialHandler.this.ch.isClosed()) { + InitialHandler.this.ch.getHandle().eventLoop().execute(new Runnable() { + public void run() { + if(InitialHandler.this.ch.getHandle().isActive()) { + if(InitialHandler.this.getVersion() >= 5) { + InitialHandler.this.unsafe.sendPacket(new LoginSuccess(InitialHandler.this.getUniqueId().toString(), InitialHandler.this.getName())); + } else { + InitialHandler.this.unsafe.sendPacket(new LoginSuccess(InitialHandler.this.getUUID(), InitialHandler.this.getName())); + } + + InitialHandler.this.ch.setProtocol(net.md_5.bungee.protocol.Protocol.GAME); + UserConnection userCon = new UserConnection(InitialHandler.this.bungee, InitialHandler.this.ch, InitialHandler.this.getName(), InitialHandler.this); + userCon.init(); + InitialHandler.this.bungee.getPluginManager().callEvent(new PostLoginEvent(userCon)); + ((HandlerBoss)InitialHandler.this.ch.getHandle().pipeline().get(HandlerBoss.class)).setHandler(new UpstreamBridge(InitialHandler.this.bungee, userCon)); + ServerInfo server; + if(InitialHandler.this.bungee.getReconnectHandler() != null) { + server = InitialHandler.this.bungee.getReconnectHandler().getServer(userCon); + } else { + server = AbstractReconnectHandler.getForcedHost(InitialHandler.this); + } + + if(server == null) { + server = InitialHandler.this.bungee.getServerInfo(InitialHandler.this.listener.getDefaultServer()); + } + + userCon.connect(server, (Callback)null, true); + InitialHandler.this.thisState = InitialHandler.State.FINISHED; + } + + } + }); + } + } + }; + this.bungee.getPluginManager().callEvent(new LoginEvent(this, complete)); + } + + public void disconnect(String reason) { + this.disconnect((BaseComponent[])TextComponent.fromLegacyText(reason)); + } + + public void disconnect(final BaseComponent... reason) { + if(!this.ch.isClosed()) { + this.ch.getHandle().eventLoop().schedule(new Runnable() { + public void run() { + InitialHandler.this.unsafe().sendPacket(new Kick(ComponentSerializer.toString(reason))); + InitialHandler.this.ch.close(); + } + }, 500L, TimeUnit.MILLISECONDS); + } + + } + + public void disconnect(BaseComponent reason) { + this.disconnect((BaseComponent[])(new BaseComponent[]{reason})); + } + + public String getName() { + return this.loginRequest == null?null:this.loginRequest.getData(); + } + + public int getVersion() { + return this.handshake == null?-1:this.handshake.getProtocolVersion(); + } + + public InetSocketAddress getAddress() { + return (InetSocketAddress)this.ch.getHandle().remoteAddress(); + } + + public Unsafe unsafe() { + return this.unsafe; + } + + public void setOnlineMode(boolean onlineMode) { + Preconditions.checkState(this.thisState == InitialHandler.State.USERNAME, "Can only set online mode status whilst state is username"); + this.onlineMode = onlineMode; + } + + public String getUUID() { + return this.uniqueId.toString().replaceAll("-", ""); + } + + public String toString() { + return "[" + (this.getName() != null?this.getName():this.getAddress()) + "] <-> InitialHandler"; + } + + @ConstructorProperties({"bungee", "listener"}) + public InitialHandler(ProxyServer bungee, ListenerInfo listener) { + this.thisState = InitialHandler.State.HANDSHAKE; + this.unsafe = new Unsafe() { + public void sendPacket(DefinedPacket packet) { + InitialHandler.this.ch.write(packet); + } + }; + this.onlineMode = BungeeCord.getInstance().config.isOnlineMode(); + this.bungee = bungee; + this.listener = listener; + } + + public ListenerInfo getListener() { + return this.listener; + } + + public Handshake getHandshake() { + return this.handshake; + } + + public LoginRequest getLoginRequest() { + return this.loginRequest; + } + + public List getRegisterMessages() { + return this.registerMessages; + } + + public boolean isOnlineMode() { + return this.onlineMode; + } + + public InetSocketAddress getVirtualHost() { + return this.virtualHost; + } + + public UUID getUniqueId() { + return this.uniqueId; + } + + public UUID getOfflineId() { + return this.offlineId; + } + + public LoginResult getLoginProfile() { + return this.loginProfile; + } + + public boolean isLegacy() { + return this.legacy; + } + + public ChannelWrapper getChannelWrapper() + { + return ch; //MINEPLEX + } + + private static enum State { + HANDSHAKE, + STATUS, + PING, + USERNAME, + ENCRYPT, + FINISHED; + + private State() { + } + } + + public static void setCustomMotdFactory(CustomMotdFactory factory) + { + _customMotdFactory = factory; + } +} diff --git a/Plugins/Mineplex.Bungee.Mineplexer/src/net/md_5/bungee/connection/PingHandler.java b/Plugins/Mineplex.Bungee.Mineplexer/src/net/md_5/bungee/connection/PingHandler.java new file mode 100644 index 000000000..f07679cd8 --- /dev/null +++ b/Plugins/Mineplex.Bungee.Mineplexer/src/net/md_5/bungee/connection/PingHandler.java @@ -0,0 +1,58 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package net.md_5.bungee.connection; + +import com.google.gson.Gson; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.Callback; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.ServerPing; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.netty.ChannelWrapper; +import net.md_5.bungee.netty.PacketHandler; +import net.md_5.bungee.protocol.MinecraftDecoder; +import net.md_5.bungee.protocol.MinecraftEncoder; +import net.md_5.bungee.protocol.Protocol; +import net.md_5.bungee.protocol.packet.Handshake; +import net.md_5.bungee.protocol.packet.StatusRequest; +import net.md_5.bungee.protocol.packet.StatusResponse; + +public class PingHandler extends PacketHandler { + private final ServerInfo target; + private final Callback callback; + private final int protocol; + private ChannelWrapper channel; + + public void connected(ChannelWrapper channel) throws Exception { + this.channel = channel; + MinecraftEncoder encoder = new MinecraftEncoder(Protocol.HANDSHAKE, false, this.protocol); + channel.getHandle().pipeline().addAfter("frame-decoder", "packet-decoder", new MinecraftDecoder(Protocol.STATUS, false, ProxyServer.getInstance().getProtocolVersion())); + channel.getHandle().pipeline().addAfter("frame-prepender", "packet-encoder", encoder); + channel.write(new Handshake(this.protocol, this.target.getAddress().getHostString(), this.target.getAddress().getPort(), 1)); + encoder.setProtocol(Protocol.STATUS); + channel.write(new StatusRequest()); + } + + public void exception(Throwable t) throws Exception { + this.callback.done(null, t); + } + + public void handle(StatusResponse statusResponse) throws Exception { + Gson gson = this.protocol == 4?BungeeCord.getInstance().gsonLegacy:BungeeCord.getInstance().gson; + this.callback.done(gson.fromJson(statusResponse.getResponse(), ServerPing.class), (Throwable)null); +// this.channel.close(); //MINEPLEX + } + + public String toString() { + return "[Ping Handler] -> " + this.target.getName(); + } + + public PingHandler(ServerInfo target, Callback callback, int protocol) { + this.target = target; + this.callback = callback; + this.protocol = protocol; + } +}