package de.zh32.pingtest; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.util.List; /** * @author zh32 <zh32 at zh32.de> */ public class ServerListPing17 { private InetSocketAddress host; private int timeout = 7000; private Gson gson = new Gson(); public InetSocketAddress getHost() { return host; } public void setHost(InetSocketAddress host) { this.host = host; } public Gson getGson() { return gson; } public void setGson(Gson gson) { this.gson = gson; } public void setAddress(InetSocketAddress host) { this.host = host; } public InetSocketAddress getAddress() { return this.host; } void setTimeout(int timeout) { this.timeout = timeout; } int getTimeout() { return this.timeout; } public int readVarInt(DataInputStream in) throws IOException { int i = 0; int j = 0; while (true) { int k = in.readByte(); i |= (k & 0x7F) << j++ * 7; if (j > 5) { throw new RuntimeException("VarInt too big"); } if ((k & 0x80) != 128) { break; } } return i; } public void writeVarInt(DataOutputStream out, int paramInt) throws IOException { while (true) { if ((paramInt & 0xFFFFFF80) == 0) { out.writeByte(paramInt); return; } out.writeByte(paramInt & 0x7F | 0x80); paramInt >>>= 7; } } public StatusResponse fetchData() throws IOException { Socket socket = new Socket(); OutputStream outputStream; DataOutputStream dataOutputStream; InputStream inputStream; InputStreamReader inputStreamReader; socket.setSoTimeout(this.timeout); socket.connect(host, timeout); outputStream = socket.getOutputStream(); dataOutputStream = new DataOutputStream(outputStream); inputStream = socket.getInputStream(); inputStreamReader = new InputStreamReader(inputStream); ByteArrayOutputStream b = new ByteArrayOutputStream(); DataOutputStream handshake = new DataOutputStream(b); handshake.writeByte(0x00); // packet id for handshake writeVarInt(handshake, 4); // protocol version writeVarInt(handshake, this.host.getAddress().getHostAddress().length()); // host length handshake.writeBytes(this.host.getAddress().getHostAddress()); // host string handshake.writeShort(host.getPort()); // port writeVarInt(handshake, 1); // state (1 for handshake) writeVarInt(dataOutputStream, b.size()); // prepend size dataOutputStream.write(b.toByteArray()); // write handshake packet dataOutputStream.writeByte(0x01); // size is only 1 dataOutputStream.writeByte(0x00); // packet id for ping DataInputStream dataInputStream = new DataInputStream(inputStream); int size = readVarInt(dataInputStream); // size of packet int id = readVarInt(dataInputStream); // packet id if (id == -1) { socket.close(); throw new IOException("Premature end of stream."); } if (id != 0x00) { // we want a status response socket.close(); throw new IOException("Invalid packetID"); } int length = readVarInt(dataInputStream); // length of json string if (length == -1) { socket.close(); throw new IOException("Premature end of stream."); } if (length == 0) { socket.close(); throw new IOException("Invalid string length."); } byte[] in = new byte[length]; dataInputStream.readFully(in); // read json string String json = new String(in); // System.out.println(json); StatusResponse response = null; try { response = gson.fromJson(json, StatusResponseOld.class); } catch (JsonSyntaxException jse) { response = gson.fromJson(json, StatusResponseNew.class); } if (response == null) { socket.close(); throw new IOException("Cannot parse JSON."); } try { long now = System.currentTimeMillis(); dataOutputStream.writeByte(0x09); // size of packet dataOutputStream.writeByte(0x01); // 0x01 for ping dataOutputStream.writeLong(now); // time!? readVarInt(dataInputStream); id = readVarInt(dataInputStream); if (id == -1) { socket.close(); throw new IOException("Premature end of stream."); } if (id != 0x01) { socket.close(); throw new IOException("Invalid packetID"); } long pingtime = dataInputStream.readLong(); // read response response.setTime((int) (now - pingtime)); } catch (IOException exception) { } dataOutputStream.close(); outputStream.close(); inputStreamReader.close(); inputStream.close(); socket.close(); return response; } public interface StatusResponse { public String getDescription(); public Players getPlayers(); public Version getVersion(); public String getFavicon(); public int getTime(); public void setTime(int time); } public class StatusResponseOld implements StatusResponse { private String description; private Players players; private Version version; private String favicon; private int time; public String getDescription() { return description; } public Players getPlayers() { return players; } public Version getVersion() { return version; } public String getFavicon() { return favicon; } public int getTime() { return time; } public void setTime(int time) { this.time = time; } } public class StatusResponseNew implements StatusResponse { private Description description; private Players players; private Version version; private String favicon; private int time; public String getDescription() { return description.getText(); } public Players getPlayers() { return players; } public Version getVersion() { return version; } public String getFavicon() { return favicon; } public int getTime() { return time; } public void setTime(int time) { this.time = time; } } public class Description { private String text; public String getText() { return text; } } public class Players { private int max; private int online; private List<Player> sample; public int getMax() { return max; } public int getOnline() { return online; } public List<Player> getSample() { return sample; } } public class Player { private String name; private String id; public String getName() { return name; } public String getId() { return id; } } public class Version { private String name; private String protocol; public String getName() { return name; } public String getProtocol() { return protocol; } } }