From 2d038b48c0ce1af9f28273fe610edc6e5246ac48 Mon Sep 17 00:00:00 2001 From: Antony Riley Date: Sun, 2 Aug 2015 05:35:01 +0300 Subject: [PATCH] Various fixes, untested. --- .../fr/Alphart/BungeePlayerCounter/BPC.java | 118 +++-- .../BungeePlayerCounter/GsonLoader.java | 69 +++ .../Servers/PingResponse.java | 60 +++ .../BungeePlayerCounter/Servers/Pinger.java | 468 ++++++++---------- .../Servers/ServerCoordinator.java | 8 +- .../Servers/ServerGroup.java | 150 +++--- 6 files changed, 484 insertions(+), 389 deletions(-) create mode 100644 src/main/java/fr/Alphart/BungeePlayerCounter/GsonLoader.java create mode 100644 src/main/java/fr/Alphart/BungeePlayerCounter/Servers/PingResponse.java diff --git a/src/main/java/fr/Alphart/BungeePlayerCounter/BPC.java b/src/main/java/fr/Alphart/BungeePlayerCounter/BPC.java index 366d8b5..d74f90b 100644 --- a/src/main/java/fr/Alphart/BungeePlayerCounter/BPC.java +++ b/src/main/java/fr/Alphart/BungeePlayerCounter/BPC.java @@ -25,63 +25,68 @@ import fr.Alphart.BungeePlayerCounter.Servers.ServerCoordinator; @Getter -public class BPC extends JavaPlugin{ +public class BPC extends JavaPlugin { + private static String prefix = _("&4[&aBPC&4]&e"); @Getter - private static BPC instance; - private ServerCoordinator serverCoordinator; - private PluginMessageWriter pmWriter; - private Configuration conf; - private ScoreboardHandler scoreboardHandler; - private boolean debugMode = false; - - public void onEnable() { - BPC.instance = this; - - conf = new Configuration(getConfig()); - pmWriter = new PluginMessageWriter(conf.getPluginMessageChannel()); - serverCoordinator = new ServerCoordinator(); - scoreboardHandler = new ScoreboardHandler(serverCoordinator); - - Bukkit.getPluginManager().registerEvents(new PlayerListener(), this); - getCommand("bpc").setExecutor(new BaseCommands()); - } - - public void onDisable(){ - Bukkit.getMessenger().unregisterOutgoingPluginChannel(this); - Bukkit.getMessenger().unregisterIncomingPluginChannel(this); - HandlerList.unregisterAll(this); + private static BPC instance; + private ServerCoordinator serverCoordinator; + private PluginMessageWriter pmWriter; + private Configuration conf; + private ScoreboardHandler scoreboardHandler; + private boolean debugMode = false; + + public void onEnable() { + if (!GsonLoader.loadGson()) { + getServer().getPluginManager().disablePlugin(this); + return; + } + BPC.instance = this; + + conf = new Configuration(getConfig()); + pmWriter = new PluginMessageWriter(conf.getPluginMessageChannel()); + serverCoordinator = new ServerCoordinator(); + scoreboardHandler = new ScoreboardHandler(serverCoordinator); + + Bukkit.getPluginManager().registerEvents(new PlayerListener(), this); + getCommand("bpc").setExecutor(new BaseCommands()); + } + + public void onDisable() { + Bukkit.getMessenger().unregisterOutgoingPluginChannel(this); + Bukkit.getMessenger().unregisterIncomingPluginChannel(this); + HandlerList.unregisterAll(this); Bukkit.getScheduler().cancelTasks(this); BPC.instance = null; - } + } - public void reload() { - onDisable(); - reloadConfig(); - onEnable(); - if(!Bukkit.getOnlinePlayers().isEmpty()){ + public void reload() { + onDisable(); + reloadConfig(); + onEnable(); + if (!Bukkit.getOnlinePlayers().isEmpty()) { final Player sender = Bukkit.getOnlinePlayers().iterator().next(); - if(serverCoordinator.getCurrentServer().isEmpty()){ + if (serverCoordinator.getCurrentServer().isEmpty()) { BPC.getInstance().getPmWriter().sendGetCurrentServerMessage(sender); } - if(serverCoordinator.getServerGroups().size() <= 1){ + if (serverCoordinator.getServerGroups().size() <= 1) { BPC.getInstance().getPmWriter().sendGetServersListMessage(sender); } } - } - - public boolean toggleDebug(){ - if(debugMode){ - for(final Handler handler : getLogger().getHandlers()){ + } + + public boolean toggleDebug() { + if (debugMode) { + for (final Handler handler : getLogger().getHandlers()) { getLogger().removeHandler(handler); } getLogger().setLevel(Level.INFO); getLogger().setUseParentHandlers(true); getLogger().info("The debug mode is now disabled ! Log are available in the debug.log file located in BPC folder"); - }else{ - try{ + } else { + try { final File debugFile = new File(getDataFolder(), "debug.log"); - if(debugFile.exists()){ + if (debugFile.exists()) { debugFile.delete(); } // Write header into debug log @@ -95,44 +100,51 @@ public boolean toggleDebug(){ handler.setFormatter(new Formatter() { private final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); private final String pattern = "time [level] message\n"; + @Override public String format(LogRecord record) { return pattern.replace("level", record.getLevel().getName()) - .replace("message", record.getMessage()) - .replace("[BungeePlayerCounter]", "") - .replace("time", sdf.format(Calendar.getInstance().getTime())); + .replace("message", record.getMessage()) + .replace("[BungeePlayerCounter]", "") + .replace("time", sdf.format(Calendar.getInstance().getTime())); } }); getLogger().addHandler(handler); getLogger().setLevel(Level.CONFIG); getLogger().info("The debug mode is now enabled ! Log are available in the debug.log file located in BPC folder"); getLogger().setUseParentHandlers(false); - }catch(final Exception e){ + } catch (final Exception e) { getLogger().log(Level.SEVERE, "An exception occured during the initialization of debug logging file", e); } } return debugMode = !debugMode; } - - public static String _(final String message, final Object... args){ + + public static String _(final String message, final Object... args) { return ChatColor.translateAlternateColorCodes('&', String.format(message, args)); } - public static String __(final String message, final Object... args){ + + public static String __(final String message, final Object... args) { return prefix + ChatColor.translateAlternateColorCodes('&', String.format(message, args)); } - public static void info(final String message){ + + public static void info(final String message) { getInstance().getLogger().info(message); } - public static void debug(final String message, final Object... args){ + + public static void debug(final String message, final Object... args) { getInstance().getLogger().log(Level.CONFIG, String.format(message, args)); } - public static void debug(final String message, final Throwable throwable){ + + public static void debug(final String message, final Throwable throwable) { getInstance().getLogger().log(Level.CONFIG, message, throwable); } - public static void severe(final String message){ + + public static void severe(final String message) { getInstance().getLogger().log(Level.SEVERE, message); } - public static void severe(final String message, final Throwable throwable){ + + public static void severe(final String message, final Throwable throwable) { getInstance().getLogger().log(Level.SEVERE, message + "Please report this :", throwable); } -} \ No newline at end of file +} diff --git a/src/main/java/fr/Alphart/BungeePlayerCounter/GsonLoader.java b/src/main/java/fr/Alphart/BungeePlayerCounter/GsonLoader.java new file mode 100644 index 0000000..c412b4f --- /dev/null +++ b/src/main/java/fr/Alphart/BungeePlayerCounter/GsonLoader.java @@ -0,0 +1,69 @@ +package fr.Alphart.BungeePlayerCounter; + +import com.google.gson.Gson; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; + +public final class GsonLoader { + public static boolean loadGson() { + try { + Class.forName("com.google.gson.Gson"); + return true; + } catch (final Throwable t) { + BPC.info("Gson wasn't found... Please update to spigot 1.8.3 or earlier." + + "BPC will try to dynamically load it."); + } + final File bpcFolder = BPC.getInstance().getDataFolder(); + final File gsonPath = new File(bpcFolder + File.separator + "lib" + File.separator + + "gson.jar"); + new File(bpcFolder + File.separator + "lib").mkdir(); + + // Download the driver if it doesn't exist + if (!gsonPath.exists()) { + BPC.info("Gson was not found. It is being downloaded, please wait ..."); + + final String gsonDL = "https://repo1.maven.org/maven2/com/google/code/gson/gson/2.3.1/gson-2.3.1.jar"; + FileOutputStream fos = null; + try { + final ReadableByteChannel rbc = Channels.newChannel(new URL(gsonDL).openStream()); + fos = new FileOutputStream(gsonPath); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + } catch (final IOException e) { + BPC.severe("An error occured during the download of Gson.", e); + return false; + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + BPC.info("Gson has been successfully downloaded."); + } + + try { + URLClassLoader systemClassLoader; + URL gsonUrl; + Class sysclass; + gsonUrl = gsonPath.toURI().toURL(); + systemClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); + sysclass = URLClassLoader.class; + final Method method = sysclass.getDeclaredMethod("addURL", new Class[]{URL.class}); + method.setAccessible(true); + method.invoke(systemClassLoader, new Object[]{gsonUrl}); + + return true; + } catch (final Throwable t) { + BPC.severe("Gson cannot be loaded.", t); + } + return false; + } +} diff --git a/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/PingResponse.java b/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/PingResponse.java new file mode 100644 index 0000000..693c06f --- /dev/null +++ b/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/PingResponse.java @@ -0,0 +1,60 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package fr.Alphart.BungeePlayerCounter.Servers; + +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * + * @author antony + */ +@Getter +@ToString +public class PingResponse { + private String description; + private Players players; + private Version version; + private String favicon; + @Setter + private long time; + private final Pinger outer; + + public PingResponse(final Pinger outer) { + this.outer = outer; + } + + public boolean isFull() { + return players.max <= players.online; + } + + @Getter + @ToString + public class Players { + + private int max; + private int online; + private List sample; + + @Getter + public class Player { + + private String name; + private String id; + } + } + + @Getter + @ToString + public class Version { + + private String name; + private String protocol; + } + +} diff --git a/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/Pinger.java b/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/Pinger.java index 2810da8..97fb8e1 100644 --- a/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/Pinger.java +++ b/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/Pinger.java @@ -1,311 +1,253 @@ package fr.Alphart.BungeePlayerCounter.Servers; -import java.io.ByteArrayOutputStream; +import com.google.common.base.Charsets; import java.io.DataInputStream; import java.io.DataOutputStream; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStream; -import java.lang.reflect.Method; import java.net.ConnectException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; -import java.util.List; - -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; import com.google.gson.Gson; import fr.Alphart.BungeePlayerCounter.BPC; -import fr.Alphart.BungeePlayerCounter.Servers.Pinger.VarIntStreams.VarIntDataInputStream; -import fr.Alphart.BungeePlayerCounter.Servers.Pinger.VarIntStreams.VarIntDataOutputStream; - -public class Pinger implements Runnable { - private static Gson gson; - private InetSocketAddress address; - private String parentGroupName; - private boolean online = false; - private int maxPlayers = -1; - - public Pinger(final String parentGroupName, final InetSocketAddress address) { - if(gson == null){ - loadGson(); - try{ - gson = new Gson(); - BPC.debug("Gson was loaded with success !"); - }catch(final Throwable t){ - BPC.severe("Gson cannot be downloaded or loaded ... Please update to spigot 1.8.3 or earlier", t); - } - } - this.parentGroupName = parentGroupName; - this.address = address; - } - - public boolean isOnline() { - return online; - } - - - public int getMaxPlayers() { - return maxPlayers; - } - - @Override - public void run() { - try { - final PingResponse response = ping(address, 1000); - online = true; - maxPlayers = response.getPlayers().getMax(); - BPC.debug("Successfully pinged " + parentGroupName + " group, result : " + response); - } catch (IOException e) { - if (!(e instanceof ConnectException) && !(e instanceof SocketTimeoutException)) { - BPC.severe("An unexcepted error occured while pinging " + parentGroupName + " server", e); - } - online = false; - } - } - public static PingResponse ping(final InetSocketAddress host, final int timeout) throws IOException{ +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.concurrent.Callable; + +public class Pinger implements Callable { + private static final long PING_PAYLOAD = 0xdecafcafebabeL; + private static final int PROCOL_VERSION = 4; // 1.7.2 protocol version + private static final int MAX_PACKET_LENGTH = 0x10000; // 64kb + private static final Gson gson = new Gson(); + private final InetSocketAddress address; + private final String parentGroupName; + private final byte[] handshakePacket; + private final byte[] requestPacket; + private final byte[] pingPacket; + + public Pinger(final String parentGroupName, final InetSocketAddress address) { + this.parentGroupName = parentGroupName; + this.address = address; + handshakePacket = createQueryPacket(); + requestPacket = createRequestPacket(); + pingPacket = createPingPacket(); + } + + private byte[] createQueryPacket() { + try { + // See http://wiki.vg/index.php?title=Protocol&oldid=5486#Handshake + ByteArrayOutputStream out = new ByteArrayOutputStream(); + MCDataOutputStream dataOut = new MCDataOutputStream(out); + dataOut.writeVarInt(0); // Packet id + dataOut.writeVarInt(PROCOL_VERSION); // Protocol version. + dataOut.writeMCString(address.getHostString()); + dataOut.writeShort(address.getPort()); + dataOut.writeVarInt(1); + return out.toByteArray(); + } catch (IOException ex) { + // Should not happen. + throw new IllegalStateException(ex); + } + } + + private byte[] createRequestPacket() { + try { + // See http://wiki.vg/index.php?title=Protocol&oldid=5486#Request + ByteArrayOutputStream out = new ByteArrayOutputStream(); + MCDataOutputStream dataOut = new MCDataOutputStream(out); + dataOut.writeVarInt(0); // Packet id + return out.toByteArray(); + } catch (IOException ex) { + // Should not happen. + throw new IllegalStateException(ex); + } + } + + private byte[] createPingPacket() { + try { + // See http://wiki.vg/index.php?title=Protocol&oldid=5486#Ping_2 + // the protocol document states that we should send a time + // here, however whatever we send is returned by the server + // in the ping response, so lets just pick an easily identifiable + // number. + ByteArrayOutputStream out = new ByteArrayOutputStream(); + MCDataOutputStream dataOut = new MCDataOutputStream(out); + dataOut.writeVarInt(0); // Packet id + dataOut.writeLong(PING_PAYLOAD); // + return out.toByteArray(); + } catch (IOException ex) { + // Should not happen. + throw new IllegalStateException(ex); + } + } + + @Override + public PingResponse call() { + try { + final PingResponse response = ping(address, 1000); + BPC.debug("Successfully pinged " + parentGroupName + " group, result : " + response); + return response; + } catch (IOException e) { + if (!(e instanceof ConnectException) && !(e instanceof SocketTimeoutException)) { + BPC.severe("An unexcepted error occured while pinging " + parentGroupName + " server", e); + } + } + return null; + } + + public PingResponse ping(final InetSocketAddress host, final int timeout) throws IOException { Socket socket = null; - try{ + try { + MCDataOutputStream dataOutputStream; + MCDataInputStream dataInputStream; + String json; + byte[] packet; + MCDataInputStream packetIn; + long pingTimestamp; + long pongTimestamp; + long pongPayload; + int pktId; + socket = new Socket(); - OutputStream outputStream; - VarIntDataOutputStream dataOutputStream; - InputStream inputStream; - InputStreamReader inputStreamReader; - + socket.setSoTimeout(timeout); - + socket.connect(host, timeout); - - outputStream = socket.getOutputStream(); - dataOutputStream = new VarIntDataOutputStream(outputStream); - - inputStream = socket.getInputStream(); - inputStreamReader = new InputStreamReader(inputStream); - - // Write handshake, protocol=4 and state=1 - ByteArrayOutputStream b = new ByteArrayOutputStream(); - VarIntDataOutputStream handshake = new VarIntDataOutputStream(b); - handshake.writeByte(0x00); - handshake.writeVarInt(4); - handshake.writeVarInt(host.getHostString().length()); - handshake.writeBytes(host.getHostString()); - handshake.writeShort(host.getPort()); - handshake.writeVarInt(1); - dataOutputStream.writeVarInt(b.size()); - dataOutputStream.write(b.toByteArray()); - - // Send ping request - dataOutputStream.writeVarInt(1); - dataOutputStream.writeByte(0x00); - VarIntDataInputStream dataInputStream = new VarIntDataInputStream(inputStream); - dataInputStream.readVarInt(); - int id = dataInputStream.readVarInt(); - if (id == -1) { - throw new IOException("Premature end of stream."); - } - if (id != 0x00) { - throw new IOException(String.format("Invalid packetID. Expecting %d got %d", 0x00, id)); - } - int length = dataInputStream.readVarInt(); - if (length == -1) { - throw new IOException("Premature end of stream."); - } - - if (length == 0) { - throw new IOException("Invalid string length."); + + dataOutputStream = new MCDataOutputStream(new BufferedOutputStream(socket.getOutputStream(), 2048)); + dataInputStream = new MCDataInputStream(socket.getInputStream()); + + // Write handshake + request + dataOutputStream.writePacket(handshakePacket); + dataOutputStream.writePacket(requestPacket); + dataOutputStream.flush(); + + // Read handshake response. + packet = dataInputStream.readPacket(); + packetIn = new MCDataInputStream(new ByteArrayInputStream(packet)); + pktId = packetIn.readVarInt(); + if (pktId != 0) { + throw new IOException("Server sent unexpected response to handshake, expected packet id: 0 got: " + pktId); } - - // Read ping response - byte[] in = new byte[length]; - dataInputStream.readFully(in); - String json = new String(in); - - // Send ping packet (to get ping value in ms) - long now = System.currentTimeMillis(); - dataOutputStream.writeByte(0x09); - dataOutputStream.writeByte(0x01); - dataOutputStream.writeLong(now); - - // Read ping value in ms - dataInputStream.readVarInt(); - id = dataInputStream.readVarInt(); - if (id == -1) { - throw new IOException("Premature end of stream."); + json = packetIn.readMCString(); // Grab the ping response. + + + // Write ping request + dataOutputStream.writePacket(pingPacket); + dataOutputStream.flush(); + pingTimestamp = System.nanoTime(); // Record time of sending the ping + + // Read ping response. + packet = dataInputStream.readPacket(); + pongTimestamp = System.nanoTime(); // Record time of receiving the response + packetIn = new MCDataInputStream(new ByteArrayInputStream(packet)); + pktId = packetIn.readVarInt(); + if (pktId != 1) { + throw new IOException("Server sent unexpected response to ping, expected packet id: 1 got: " + pktId); } - if (id != 0x01) { - throw new IOException(String.format("Invalid packetID. Expecting %d got %d", 0x01, id)); + pongPayload = packetIn.readLong(); + if (PING_PAYLOAD != pongPayload) { + // Hack to print returned ping response as an unsigned 64 bit long in hex. + throw new IOException(String.format("Expected ping response payload 0x%x got 0x%x%x", PING_PAYLOAD, (pongPayload>>32)&0xFFFFFFFFL, pongPayload&0xFFFFFFFFL)); } - long pingtime = dataInputStream.readLong(); - + synchronized (gson) { final PingResponse response = gson.fromJson(json, PingResponse.class); - response.setTime((int) (now - pingtime)); - dataOutputStream.close(); - outputStream.close(); - inputStreamReader.close(); - inputStream.close(); - socket.close(); + response.setTime(pongTimestamp - pingTimestamp); return response; } - }catch(final IOException e){ + } catch (final IOException e) { throw e; - }finally{ - if(socket != null){ + } finally { + if (socket != null) { socket.close(); } } } - - public Gson loadGson(){ - try{ - Class.forName("com.google.gson.Gson"); - return new Gson(); - }catch(final Throwable t){ - BPC.info("Gson wasn't found... Please update to spigot 1.8.3 or earlier." - + "BPC will try to dynamically load it."); + + + + /** + * Enhanced DataIS which reads VarInt type + */ + public final static class MCDataInputStream extends DataInputStream { + + public MCDataInputStream(final InputStream is) { + super(is); } - final File bpcFolder = BPC.getInstance().getDataFolder(); - final File gsonPath = new File(bpcFolder + File.separator + "lib" + File.separator - + "gson.jar"); - new File(bpcFolder + File.separator + "lib").mkdir(); - - // Download the driver if it doesn't exist - if (!gsonPath.exists()) { - BPC.info("Gson was not found. It is being downloaded, please wait ..."); - - final String gsonDL = "https://repo1.maven.org/maven2/com/google/code/gson/gson/2.3.1/gson-2.3.1.jar"; - FileOutputStream fos = null; - try { - final ReadableByteChannel rbc = Channels.newChannel(new URL(gsonDL).openStream()); - fos = new FileOutputStream(gsonPath); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - } catch (final IOException e) { - BPC.severe("An error occured during the download of Gson.", e); - return null; - } finally { - if(fos != null){ - try { - fos.close(); - } catch (IOException e) { - e.printStackTrace(); - } + + public int readVarInt() throws IOException { + int i = 0; + int j = 0; + while (true) { + int k = readByte(); + i |= (k & 0x7F) << j++ * 7; + if (j > 5) { + throw new IOException("VarInt too big"); + } + if ((k & 0x80) != 0x80) { + return i; } } - BPC.info("Gson has been successfully downloaded."); } - try { - URLClassLoader systemClassLoader; - URL gsonUrl; - Class sysclass; - gsonUrl = gsonPath.toURI().toURL(); - systemClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); - sysclass = URLClassLoader.class; - final Method method = sysclass.getDeclaredMethod("addURL", new Class[] { URL.class }); - method.setAccessible(true); - method.invoke(systemClassLoader, new Object[] { gsonUrl }); - - return (Gson) Class.forName("com.google.gson.Gson", true, systemClassLoader).newInstance(); - } catch (final Throwable t) { - BPC.severe("Gson cannot be loaded.", t); + public String readMCString() throws IOException { + int strlen = readVarInt(); + byte[] stringData = new byte[strlen]; + readFully(stringData); + return new String(stringData, Charsets.UTF_8); } - return null; - } - - @Getter - @ToString - public class PingResponse { - private String description; - private Players players; - private Version version; - private String favicon; - @Setter - private int time; - - public boolean isFull(){ - return players.max <= players.online; - } - - @Getter - @ToString - public class Players { - private int max; - private int online; - private List sample; - - @Getter - public class Player { - private String name; - private String id; + public byte[] readPacket() throws IOException { + int pktLen = readVarInt(); + if (pktLen <= 0 || pktLen > MAX_PACKET_LENGTH) { + throw new IOException("Packet length invalid, expected 0 < length <= " + MAX_PACKET_LENGTH + " got length: " + pktLen); } + byte[] packet = new byte[pktLen]; + readFully(packet); + return packet; } - - @Getter - @ToString - public class Version { - private String name; - private String protocol; - } + } - - static class VarIntStreams { - /** - * Enhanced DataIS which reads VarInt type - */ - public static class VarIntDataInputStream extends DataInputStream{ - - public VarIntDataInputStream(final InputStream is) { - super(is); - } - - public int readVarInt() throws IOException { - int i = 0; - int j = 0; - while (true) { - int k = readByte(); - i |= (k & 0x7F) << j++ * 7; - if (j > 5) - throw new RuntimeException("VarInt too big"); - if ((k & 0x80) != 128) - break; + + /** + * Enhanced DataOS which writes VarInt type + */ + public final static class MCDataOutputStream extends DataOutputStream { + + public MCDataOutputStream(final OutputStream os) { + super(os); + } + + public void writeVarInt(int paramInt) throws IOException { + while (true) { + if ((paramInt & 0xFFFFFF80) == 0) { + writeByte(paramInt); + return; } - return i; + + writeByte(paramInt & 0x7F | 0x80); + paramInt >>>= 7; } - } - /** - * Enhanced DataOS which writes VarInt type - */ - public static class VarIntDataOutputStream extends DataOutputStream{ - public VarIntDataOutputStream(final OutputStream os) { - super(os); - } - - public void writeVarInt(int paramInt) throws IOException { - while (true) { - if ((paramInt & 0xFFFFFF80) == 0) { - writeByte(paramInt); - return; - } - - writeByte(paramInt & 0x7F | 0x80); - paramInt >>>= 7; - } + public void writeMCString(String s) throws IOException { + byte[] stringData = s.getBytes(Charsets.UTF_8); + writeVarInt(stringData.length); + write(stringData); + } + + public void writePacket(byte[] pkt) throws IOException { + if (pkt.length == 0 || pkt.length > MAX_PACKET_LENGTH) { + throw new IOException("Packet length invalid, expected 0 < length <= " + MAX_PACKET_LENGTH + " got length: " + pkt.length); } + writeVarInt(pkt.length); + write(pkt); } } - -} \ No newline at end of file +} diff --git a/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/ServerCoordinator.java b/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/ServerCoordinator.java index de414a4..30408dd 100644 --- a/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/ServerCoordinator.java +++ b/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/ServerCoordinator.java @@ -84,16 +84,16 @@ public void run() { } /** - * This task should be runned async as it uses the network and might induce high latency + * This task should be run async as it uses the network and might induce high latency */ class UpdateProxyPlayerCountTask implements Runnable{ final Pinger ping = new Pinger("bungee", BPC.getInstance().getConf().getProxyAddress()); @Override public void run() { - ping.run(); - if(ping.isOnline()){ - bungeeMaxPlayer = ping.getMaxPlayers(); + PingResponse pingResponse = ping.call(); + if (pingResponse != null) { + bungeeMaxPlayer = pingResponse.getPlayers().getMax(); } } diff --git a/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/ServerGroup.java b/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/ServerGroup.java index 113d23f..9533fcb 100644 --- a/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/ServerGroup.java +++ b/src/main/java/fr/Alphart/BungeePlayerCounter/Servers/ServerGroup.java @@ -17,81 +17,93 @@ */ @Getter public class ServerGroup { + @Getter - private String displayName; + private String displayName; @Getter private int playerCount; - private Map serversPC; - private Pinger ping = null; - private InetSocketAddress address = null; + private Map serversPC; + private Pinger ping = null; + private InetSocketAddress address = null; + private PingResponse lastPing = null; - /** - * Constructor - * - * @param displayName - * @param servers - */ - public ServerGroup(String name, List servers) { - displayName = ChatColor.translateAlternateColorCodes('&', name); - if (displayName.length() > 16) - displayName = displayName.substring(0, 16); - serversPC = new HashMap(); - for (String serverName : servers) { - serversPC.put(serverName, 0); - } - } + /** + * Constructor + * + * @param displayName + * @param servers + */ + public ServerGroup(String name, List servers) { + displayName = ChatColor.translateAlternateColorCodes('&', name); + if (displayName.length() > 16) { + displayName = displayName.substring(0, 16); + } + serversPC = new HashMap(); + for (String serverName : servers) { + serversPC.put(serverName, 0); + } + } - public ServerGroup(String name, List servers, InetSocketAddress address, int updateInterval) { - displayName = ChatColor.translateAlternateColorCodes('&', name); - if (displayName.length() > 16) - displayName = displayName.substring(0, 16); - serversPC = new HashMap(); - for (String serverName : servers) { - serversPC.put(serverName, 0); - } - this.address = address; - ping = new Pinger(name, address); - Bukkit.getScheduler().runTaskTimerAsynchronously(BPC.getInstance(), ping, 20L, + public ServerGroup(String name, List servers, InetSocketAddress address, int updateInterval) { + displayName = ChatColor.translateAlternateColorCodes('&', name); + if (displayName.length() > 16) { + displayName = displayName.substring(0, 16); + } + serversPC = new HashMap(); + for (String serverName : servers) { + serversPC.put(serverName, 0); + } + this.address = address; + ping = new Pinger(name, address); + Bukkit.getScheduler().runTaskTimerAsynchronously(BPC.getInstance(), new PingTask(), 20L, 20L * updateInterval); - } + } + + private void calculatePlayerCount() { + int totalPC = 0; + for (Integer serverPC : serversPC.values()) { + totalPC += serverPC; + } + playerCount = totalPC; + } + + /** + * Set the player count of a server and then update the total player count + * + * @param server + * @param playerCount + */ + public void updatePlayerCount(String server, Integer playerCount) { + if (serversPC.containsKey(server)) { + serversPC.put(server, playerCount); + } + calculatePlayerCount(); + } + + public boolean isOnline() { + if (ping == null) { + throw new IllegalStateException("This server group has no defined address and its current stat cannot be computed."); + } + return lastPing != null; + } + + public boolean isAdressSet() { + return address != null; + } - private void calculatePlayerCount() { - int totalPC = 0; - for (Integer serverPC : serversPC.values()) { - totalPC += serverPC; - } - playerCount = totalPC; - } + /** + * Check if this group contain the current server (server where the plugin + * is running now) + */ + public boolean doesContainCurrentServer() { + return serversPC.containsKey(BPC.getInstance().getServerCoordinator().getCurrentServer()); + } - /** - * Set the player count of a server and then update the total player count - * - * @param server - * @param playerCount - */ - public void updatePlayerCount(String server, Integer playerCount) { - if (serversPC.containsKey(server)) { - serversPC.put(server, playerCount); - } - calculatePlayerCount(); - } + private class PingTask implements Runnable { - public boolean isOnline() { - if(ping == null){ - throw new IllegalStateException("This server group has no defined address and its current stat cannot be computed."); - } - return ping.isOnline(); - } - - public boolean isAdressSet(){ - return address != null; - } - - /** - * Check if this group contain the current server - * (server where the plugin is running now) - */ - public boolean doesContainCurrentServer(){ - return serversPC.containsKey(BPC.getInstance().getServerCoordinator().getCurrentServer()); - } -} \ No newline at end of file + @Override + public void run() { + lastPing = ping.call(); + } + } +}