Skip to content
This repository was archived by the owner on Oct 24, 2023. It is now read-only.

Commit c58f14d

Browse files
committed
Initial
0 parents  commit c58f14d

File tree

7 files changed

+390
-0
lines changed

7 files changed

+390
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea/
2+
target/

README.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
How to include the API with Maven:
2+
```xml
3+
<repositories>
4+
<repository>
5+
<id>jitpack.io</id>
6+
<url>https://jitpack.io</url>
7+
</repository>
8+
</repositories>
9+
```
10+
```xml
11+
<dependencies>
12+
<dependency>
13+
<groupId>com.github.elijuh</groupId>
14+
<artifactId>ServerPingerAPI</artifactId>
15+
<version>1.0</version>
16+
</dependency>
17+
</dependencies>
18+
```
19+
20+
How to include the API with Gradle:
21+
```groovy
22+
repositories {
23+
maven { url 'https://jitpack.io' }
24+
}
25+
dependencies {
26+
compileOnly "com.github.elijuh:ServerPingerAPI:1.0"
27+
}
28+
```
29+
30+
Example Usage of ServerPinger
31+
```java
32+
public class Test {
33+
public static void main(String[] args) {
34+
// Creating an instance
35+
ServerPinger pinger = new ServerPinger();
36+
37+
try {
38+
// Pinging hypixel.net server-list information
39+
ServerResponse response = pinger.ping("hypixel.net");
40+
41+
// Ping in ms
42+
long ping = response.getPing();
43+
44+
// Description of the server
45+
ServerResponse.Description description = response.getDescription();
46+
47+
// Version of the server
48+
ServerResponse.Version version = response.getVersion();
49+
50+
// Player information
51+
ServerResponse.Players players = response.getPlayers();
52+
53+
int online = players.getOnline();
54+
int maxPlayers = players.getMax();
55+
56+
// Some servers show a list of the players online, but in this example; hypixel does not.
57+
List<ServerResponse.Player> playerList = players.getSample();
58+
59+
// You can also get the Base64 for the server icon
60+
String iconBase = response.getFavicon();
61+
62+
// Displaying information to console
63+
System.out.println("Ping: " + ping + "ms");
64+
System.out.println("Description: " + description.getText());
65+
System.out.println("Version: " + version.getName());
66+
System.out.println("Player Count: " + online + "/" + maxPlayers);
67+
System.out.println("Players: " + playerList);
68+
System.out.println("Icon Base64: " + iconBase);
69+
} catch (IOException e) {
70+
e.printStackTrace();
71+
}
72+
}
73+
}
74+
```

pom.xml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>com.elijuh</groupId>
8+
<artifactId>ServerPingerAPI</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
11+
<packaging>jar</packaging>
12+
<name>ServerPingerAPI</name>
13+
14+
<properties>
15+
<java.version>17</java.version>
16+
<maven.compiler.source>17</maven.compiler.source>
17+
<maven.compiler.target>17</maven.compiler.target>
18+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
19+
</properties>
20+
21+
<build>
22+
<defaultGoal>clean install</defaultGoal>
23+
<plugins>
24+
<plugin>
25+
<groupId>org.apache.maven.plugins</groupId>
26+
<artifactId>maven-compiler-plugin</artifactId>
27+
<version>3.8.1</version>
28+
<configuration>
29+
<source>${java.version}</source>
30+
<target>${java.version}</target>
31+
</configuration>
32+
</plugin>
33+
<plugin>
34+
<groupId>org.apache.maven.plugins</groupId>
35+
<artifactId>maven-shade-plugin</artifactId>
36+
<version>3.2.1</version>
37+
<executions>
38+
<execution>
39+
<phase>package</phase>
40+
<goals>
41+
<goal>shade</goal>
42+
</goals>
43+
<configuration>
44+
<createDependencyReducedPom>false</createDependencyReducedPom>
45+
</configuration>
46+
</execution>
47+
</executions>
48+
</plugin>
49+
</plugins>
50+
<resources>
51+
<resource>
52+
<directory>src/main/resources</directory>
53+
<filtering>true</filtering>
54+
</resource>
55+
</resources>
56+
</build>
57+
58+
<repositories>
59+
<repository>
60+
<id>jitpack.io</id>
61+
<url>https://jitpack.io/</url>
62+
</repository>
63+
</repositories>
64+
65+
<dependencies>
66+
<dependency>
67+
<groupId>org.projectlombok</groupId>
68+
<artifactId>lombok</artifactId>
69+
<version>1.18.24</version>
70+
<scope>provided</scope>
71+
</dependency>
72+
<dependency>
73+
<groupId>dnsjava</groupId>
74+
<artifactId>dnsjava</artifactId>
75+
<version>3.5.1</version>
76+
</dependency>
77+
<dependency>
78+
<groupId>com.google.code.gson</groupId>
79+
<artifactId>gson</artifactId>
80+
<version>2.9.0</version>
81+
</dependency>
82+
<dependency>
83+
<groupId>org.slf4j</groupId>
84+
<artifactId>slf4j-simple</artifactId>
85+
<version>1.7.36</version>
86+
</dependency>
87+
</dependencies>
88+
89+
</project>
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package com.elijuh.serverpinger;
2+
3+
import com.google.gson.Gson;
4+
import com.google.gson.JsonElement;
5+
import com.google.gson.JsonObject;
6+
import com.google.gson.JsonParser;
7+
import lombok.Getter;
8+
import org.xbill.DNS.*;
9+
import org.xbill.DNS.Record;
10+
11+
import java.io.ByteArrayOutputStream;
12+
import java.io.DataInputStream;
13+
import java.io.DataOutputStream;
14+
import java.io.IOException;
15+
import java.net.InetSocketAddress;
16+
import java.net.Socket;
17+
18+
@Getter
19+
public class ServerPinger {
20+
private final Gson gson = new Gson();
21+
22+
public ServerResponse ping(String hostname) throws IOException {
23+
return ping(ServerPingerOptions.builder().hostname(hostname).build());
24+
}
25+
26+
public ServerResponse ping(ServerPingerOptions options) throws IOException {
27+
if (options.getHostname() == null) {
28+
throw new IllegalArgumentException("hostname cannot be null");
29+
}
30+
31+
String hostname = options.getHostname();
32+
int port = options.getPort();
33+
34+
try {
35+
Record[] records = new Lookup(String.format("_minecraft._tcp.%s", hostname), Type.SRV).run();
36+
37+
if (records != null) {
38+
for (Record record : records) {
39+
SRVRecord srv = (SRVRecord) record;
40+
41+
hostname = srv.getTarget().toString().replaceFirst("\\.$", "");
42+
port = srv.getPort();
43+
}
44+
45+
}
46+
} catch (TextParseException e) {
47+
e.printStackTrace();
48+
}
49+
50+
String json;
51+
long ping;
52+
53+
try (Socket socket = new Socket()) {
54+
long start = System.currentTimeMillis();
55+
socket.connect(new InetSocketAddress(hostname, port), options.getTimeout());
56+
ping = System.currentTimeMillis() - start;
57+
58+
try (DataInputStream in = new DataInputStream(socket.getInputStream());
59+
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
60+
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
61+
DataOutputStream handshake = new DataOutputStream(bytes)) {
62+
63+
handshake.writeByte(0x00);
64+
Utils.writeVarInt(handshake, -1);
65+
Utils.writeVarInt(handshake, options.getHostname().length());
66+
handshake.writeBytes(options.getHostname());
67+
handshake.writeShort(options.getPort());
68+
Utils.writeVarInt(handshake, 1);
69+
70+
Utils.writeVarInt(out, bytes.size());
71+
out.write(bytes.toByteArray());
72+
73+
out.writeByte(0x01);
74+
out.writeByte(0x00);
75+
76+
Utils.readVarInt(in);
77+
Utils.readVarInt(in);
78+
79+
int length = Utils.readVarInt(in);
80+
81+
byte[] data = new byte[length];
82+
in.readFully(data);
83+
json = new String(data, options.getCharset());
84+
85+
out.writeByte(0x09);
86+
out.writeByte(0x01);
87+
out.writeLong(System.currentTimeMillis());
88+
89+
Utils.readVarInt(in);
90+
Utils.readVarInt(in);
91+
}
92+
}
93+
94+
JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject();
95+
JsonElement descriptionJsonElement = jsonObject.get("description");
96+
97+
if (!descriptionJsonElement.isJsonObject()) {
98+
String description = descriptionJsonElement.getAsString();
99+
JsonObject descriptionJsonObject = new JsonObject();
100+
descriptionJsonObject.addProperty("text", description);
101+
jsonObject.add("description", descriptionJsonObject);
102+
103+
}
104+
105+
ServerResponse response = gson.fromJson(jsonObject, ServerResponse.class);
106+
response.setPing(ping);
107+
108+
return response;
109+
}
110+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.elijuh.serverpinger;
2+
3+
import lombok.Builder;
4+
import lombok.Getter;
5+
6+
@Builder
7+
public class ServerPingerOptions {
8+
9+
@Getter
10+
private String hostname;
11+
12+
@Getter
13+
@Builder.Default
14+
private String charset = "UTF-8";
15+
16+
@Getter
17+
@Builder.Default
18+
private int port = 25565;
19+
20+
@Getter
21+
@Builder.Default
22+
private int timeout = 5000;
23+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.elijuh.serverpinger;
2+
3+
import lombok.Getter;
4+
import lombok.RequiredArgsConstructor;
5+
import lombok.Setter;
6+
import lombok.ToString;
7+
8+
import java.util.List;
9+
10+
@Getter
11+
@ToString
12+
@RequiredArgsConstructor
13+
public class ServerResponse {
14+
private final Description description;
15+
private final Players players;
16+
private final Version version;
17+
18+
@Setter
19+
private String favicon;
20+
21+
@Setter
22+
private long ping;
23+
24+
@Getter
25+
@ToString
26+
public static class Description {
27+
private String text;
28+
}
29+
30+
@Getter
31+
@ToString
32+
public static class Players {
33+
private int max;
34+
private int online;
35+
private List<Player> sample;
36+
}
37+
38+
@Getter
39+
@ToString
40+
public static class Player {
41+
private String name;
42+
private String id;
43+
}
44+
45+
@Getter
46+
@ToString
47+
public static class Version {
48+
private String name;
49+
private int protocol;
50+
}
51+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.elijuh.serverpinger;
2+
3+
import lombok.experimental.UtilityClass;
4+
5+
import java.io.DataInputStream;
6+
import java.io.DataOutputStream;
7+
import java.io.IOException;
8+
9+
@UtilityClass
10+
class Utils {
11+
private static final int SEGMENT_BITS = 0x7F;
12+
private static final int CONTINUE_BIT = 0x80;
13+
14+
public int readVarInt(DataInputStream in) throws IOException {
15+
int value = 0, position = 0;
16+
while (true) {
17+
byte currentByte = in.readByte();
18+
value |= (currentByte & SEGMENT_BITS) << position;
19+
20+
if ((currentByte & CONTINUE_BIT) == 0) break;
21+
22+
position += 7;
23+
24+
if (position >= 32) throw new RuntimeException("VarInt is too big");
25+
}
26+
27+
return value;
28+
}
29+
30+
public void writeVarInt(DataOutputStream out, int value) throws IOException {
31+
while (true) {
32+
if ((value & ~SEGMENT_BITS) == 0) {
33+
out.writeByte(value);
34+
return;
35+
}
36+
37+
out.writeByte((value & SEGMENT_BITS) | CONTINUE_BIT);
38+
value >>>= 7;
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)