diff --git a/pom.xml b/pom.xml
index 35934c0..6427ea9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
net.rubeta
LegacyPing
- 1.4
+ 1.5
jar
LegacyPing
diff --git a/src/main/java/net/rubeta/legacyping/ServerThread.java b/src/main/java/net/rubeta/legacyping/ServerThread.java
index 180e4bf..2a46093 100644
--- a/src/main/java/net/rubeta/legacyping/ServerThread.java
+++ b/src/main/java/net/rubeta/legacyping/ServerThread.java
@@ -5,16 +5,19 @@ import org.bukkit.craftbukkit.CraftServer;
import java.io.*;
import java.net.*;
+import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class ServerThread extends Thread {
private static final String MODERN_RESPONSE =
- "{\"version\": {\"name\": \"%version%\",\"protocol\": -%protocol%}," +
+ "{\"version\": {\"name\": \"%version%\",\"protocol\": %protocol%}," +
"\"players\": {\"max\": %max%,\"online\": %now%}," +
"\"description\": {\"text\": \"%motd%\"}," +
"\"enforcesSecureChat\": false,\"previewsChat\": true}";
+ private static final String DISCONNECT_REASON = "[\"\",{\"text\":\"This is b1.7.3 server. Visit \"}," +
+ "{\"text\":\"https://rubeta.net\",\"underlined\":true},{\"text\":\" for more information.\"}]";
private final ServerSocket serverSocket;
private final AtomicBoolean working = new AtomicBoolean(false);
private final Server minecraftServer;
@@ -25,7 +28,7 @@ public class ServerThread extends Thread {
this.minecraftServer = server;
this.setName("LegacyPing thread listening " + host + ":" + port);
this.version = version;
- this.protocol = Math.abs(protocol);
+ this.protocol = protocol;
final InetAddress address = InetAddress.getByName(host);
this.serverSocket = new ServerSocket(port, 10, address);
this.serverSocket.setSoTimeout((int) TimeUnit.SECONDS.toMillis(10L));
@@ -47,6 +50,7 @@ public class ServerThread extends Thread {
new ServerThread("", 25565).start();
}
+ @SuppressWarnings("unused")
@Override
public void run() {
this.working.set(true);
@@ -54,17 +58,32 @@ public class ServerThread extends Thread {
try {
final Socket socket = this.serverSocket.accept();
socket.setTrafficClass(SocketOptions.TCP_NODELAY & SocketOptions.IP_TOS);
- System.out.println("[LegacyPing] Got new ping from " + socket.getRemoteSocketAddress());
- final InputStream inputStream = socket.getInputStream();
- final OutputStream outputStream = socket.getOutputStream();
- final int firstByte = inputStream.read();
- if (firstByte == 254) {
- this.responseForLegacyPing(outputStream);
+// System.out.println("[LegacyPing] Got new ping from " + socket.getRemoteSocketAddress());
+ final DataInputStream inputStream = new DataInputStream(socket.getInputStream());
+ final DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
+ final int packetLength = inputStream.readByte() | inputStream.readByte() << 1; // idk why it doesn't a VarInt
+ final int protocolVersion = this.readVarInt(inputStream);
+ final String hostname = this.readUtf(inputStream);
+ final int port = inputStream.readShort();
+ final int status = this.readVarInt(inputStream);
+// System.out.println(packetLength + "; Protocol version: " + protocolVersion + ", host: " + hostname
+// + ", port: " + port + ", status: " + status);
+ if (status != 2) {
+ this.responseForModernPing(outputStream, MODERN_RESPONSE);
} else {
- this.responseForModernPing(outputStream);
+ this.responseForModernPing(outputStream, DISCONNECT_REASON);
}
outputStream.flush();
- //socket.close();
+
+ inputStream.readShort(); // legacy data
+
+ this.writeVarInt(outputStream, this.readVarInt(inputStream)); // Packet length (9)
+ this.writeVarInt(outputStream, this.readVarInt(inputStream)); // Packet id (1)
+ outputStream.writeLong(inputStream.readLong()); // timestamp
+
+ inputStream.close();
+ outputStream.close();
+ socket.close();
} catch (SocketTimeoutException ignore) {
} catch (IOException ex) {
System.out.println("Exception caught: " + ex);
@@ -80,32 +99,20 @@ public class ServerThread extends Thread {
}
}
- @SuppressWarnings("UnnecessaryUnicodeEscape")
- private void responseForLegacyPing(final OutputStream outputStream) throws IOException {
- final DataOutputStream clientOutput = new DataOutputStream(outputStream);
- clientOutput.write(255);
- final String response = this.getMotd() + '\u00a7' + this.getOnlinePlayers() + '\u00a7' + this.getMaxPlayers();
- clientOutput.writeShort(response.length());
- for (char c : response.toCharArray()) {
- clientOutput.writeChar(c);
- }
- // Packet data end
- }
-
- private void responseForModernPing(final OutputStream outputStream) throws IOException {
- final String responseJson = MODERN_RESPONSE
+ private void responseForModernPing(final DataOutputStream outputStream, String response) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ final String responseJson = response
.replace("%version%", String.valueOf(this.version))
.replace("%protocol%", String.valueOf(this.protocol))
.replace("%max%", String.valueOf(this.getMaxPlayers()))
.replace("%now%", String.valueOf(this.getOnlinePlayers()))
.replace("%motd%", this.getMotd());
- byte[] bytes = responseJson.getBytes();
- final DataOutputStream clientOutput = new DataOutputStream(outputStream);
- this.writeVarInt(clientOutput, bytes.length + 3);
- this.writeVarInt(clientOutput, 0);
- this.writeVarInt(clientOutput, bytes.length);
- clientOutput.write(bytes);
- // Packet data end
+ this.writeVarInt(dos, 0);
+ this.writeUtf(dos, responseJson);
+ dos.close();
+ this.writeVarInt(outputStream, baos.size());
+ outputStream.write(baos.toByteArray());
}
private String getMotd() {
@@ -120,14 +127,42 @@ public class ServerThread extends Thread {
return this.minecraftServer == null ? 37 : this.minecraftServer.getMaxPlayers();
}
- private void writeVarInt(DataOutputStream out, int paramInt) throws IOException {
- while (true) {
- if ((paramInt & 0xFFFFFF80) == 0) {
- out.writeByte(paramInt);
- return;
+ public int readVarInt(DataInputStream in) throws IOException {
+ int i = 0;
+ int j = 0;
+ byte b0;
+ do {
+ b0 = in.readByte();
+ i |= (b0 & 127) << j++ * 7;
+ if (j > 5) {
+ throw new RuntimeException("VarInt too big");
}
- out.writeByte(paramInt & 0x7F | 0x80);
+ } while((b0 & 128) == 128);
+ return i;
+ }
+
+ private void writeVarInt(DataOutputStream out, int paramInt) throws IOException {
+ while((paramInt & 0xffffff80) != 0) {
+ out.writeByte(paramInt & 0x7f | 0x80);
paramInt >>>= 7;
}
+ out.writeByte(paramInt);
+ }
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ private String readUtf(DataInputStream in) throws IOException {
+ int length = this.readVarInt(in);
+ byte[] data = new byte[length];
+ in.read(data);
+ return new String(data, StandardCharsets.UTF_8);
+ }
+
+ private void writeUtf(DataOutputStream out, String string) throws IOException {
+ byte[] data = string.getBytes(StandardCharsets.UTF_8);
+ if (data.length > 32767) {
+ throw new RuntimeException("String too big (was " + data.length + " bytes encoded, max 32767");
+ }
+ this.writeVarInt(out, data.length);
+ out.write(data);
}
}