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;
+ }
+}