Skip to content

Commit 04024b0

Browse files
committed
Added Server List Ping Implementation
1 parent c3b5895 commit 04024b0

File tree

3 files changed

+194
-0
lines changed

3 files changed

+194
-0
lines changed

mojang/api/net/slp/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .slp import ping

mojang/api/net/slp/slp.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import io
2+
import json
3+
import socket
4+
import struct
5+
import time
6+
7+
from .utils import *
8+
9+
10+
def _slp_current(sock: socket.socket, hostname='localhost', port=25565):
11+
## Prepare and send handshake
12+
buffer = io.BytesIO()
13+
_write_varint(0x00, buffer)
14+
_write_varint(0x00, buffer)
15+
_write_string(hostname, buffer)
16+
_write_short(port,buffer)
17+
_write_varint(0x01,buffer)
18+
19+
with sock.makefile('wb') as fp:
20+
_write_varint(len(buffer.getvalue()), fp)
21+
fp.write(buffer.getvalue())
22+
23+
## Receive server info
24+
buffer = io.BytesIO()
25+
_write_varint(0x00, buffer)
26+
27+
with sock.makefile('wb') as fp:
28+
_write_varint(len(buffer.getvalue()), fp)
29+
fp.write(buffer.getvalue())
30+
31+
with sock.makefile('rb') as fp:
32+
packet_length = _read_varint(fp)
33+
packet_id = _read_varint(fp)
34+
data = json.loads(_read_string(fp))
35+
36+
## Ping
37+
buffer = io.BytesIO()
38+
_write_varint(0x01, buffer)
39+
_write_long(int(time.time() * 1000), buffer)
40+
41+
with sock.makefile('wb') as fp:
42+
_write_varint(len(buffer.getvalue()), fp)
43+
fp.write(buffer.getvalue())
44+
45+
with sock.makefile('rb') as fp:
46+
packet_length = _read_varint(fp)
47+
packet_id = _read_varint(fp)
48+
itime = _read_long(fp)
49+
50+
ping = int(time.time() * 1000) - itime
51+
return {
52+
'protocol_version': data['version']['protocol'],
53+
'version': data['version']['name'],
54+
'motd': data['description']['text'],
55+
'players': {
56+
'count': (data['players']['online'], data['players']['max']),
57+
'list': data['players'].get('sample', []),
58+
},
59+
'ping': ping
60+
}
61+
62+
def _slp_1_6(sock: socket.socket, hostname='localhost', port=25565):
63+
# TODO: Test with a 1.6 server
64+
# Send request
65+
header = struct.pack('>BBB', 0xFE, 0x01, 0xFA)
66+
command = struct.pack('>h22s', 11, 'MC|PingHost'.encode('utf-16be'))
67+
encoded_hostname = hostname.encode('utf-16be')
68+
rest = struct.pack('>hBh{}si'.format(len(encoded_hostname)), 7 + len(encoded_hostname), 80, len(hostname), encoded_hostname, port)
69+
packet = header + command + rest
70+
71+
sock.send(packet)
72+
73+
d = sock.recv(1024)
74+
print(d)
75+
76+
def _slp_prior_1_6(sock: socket.socket, **kwargs):
77+
sock.send(struct.pack('>BB', 0xFE, 0x01))
78+
79+
with sock.makefile('rb') as fp:
80+
data = fp.read()
81+
protocol_ver, version, motd, nplayers, maxplayers = data[9:].decode('utf-16-be').split('\x00')
82+
83+
return {
84+
'protocol_version': protocol_ver,
85+
'version': version,
86+
'motd': motd,
87+
'players': {
88+
'count': (nplayers, maxplayers),
89+
'list': []
90+
},
91+
'ping': None
92+
}
93+
94+
def _slp_prior_1_4(sock: socket.socket, **kwargs):
95+
sock.send(struct.pack('>B', 0xFE))
96+
97+
with sock.makefile('rb') as fp:
98+
data = fp.read()
99+
motd, nplayers, maxplayers = data[3:].decode('utf-16-be').split('\xa7')
100+
101+
return {
102+
'protocol_version': None,
103+
'version': None,
104+
'motd': motd,
105+
'players': {
106+
'count': (nplayers, maxplayers),
107+
'list': []
108+
},
109+
'ping': None
110+
}
111+
112+
113+
def ping(host: str, port: int):
114+
result = None
115+
_versions = [_slp_current, _slp_1_6, _slp_prior_1_6, _slp_prior_1_4]
116+
117+
while len(_versions) > 0:
118+
_slp = _versions.pop(0)
119+
120+
try:
121+
# Try a slp version
122+
with socket.socket() as sock:
123+
sock.connect((host, port))
124+
_slp_result = _slp(sock, hostname=host, port=port)
125+
126+
# Check if version worked
127+
if _slp_result:
128+
result = _slp_result
129+
break
130+
except:
131+
pass
132+
133+
return result

mojang/api/net/slp/utils.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import struct
2+
import io
3+
4+
__all__ = [
5+
'_write_varint',
6+
'_read_varint',
7+
'_write_string',
8+
'_read_string',
9+
'_write_short',
10+
'_read_short',
11+
'_write_long',
12+
'_read_long'
13+
]
14+
15+
def _write_varint(value: int, buffer: io.BufferedWriter):
16+
while True:
17+
byte = value & 0x7f
18+
value >>= 7
19+
20+
if value > 0:
21+
byte |= 0x80
22+
23+
buffer.write(struct.pack('B', byte))
24+
25+
if value == 0:
26+
break
27+
28+
def _read_varint(buffer: io.BufferedReader):
29+
val = 0
30+
for i in range(5):
31+
byte = buffer.read(1)
32+
33+
val |= (ord(byte) & 0x7f) << (7*i)
34+
35+
if ord(byte) & 0x80 == 0:
36+
break
37+
38+
return val
39+
40+
def _write_string(value: str, buffer: io.BufferedWriter):
41+
encoded = value.encode('utf-8')
42+
_write_varint(len(encoded), buffer)
43+
buffer.write(encoded)
44+
45+
def _read_string(buffer: io.BufferedReader):
46+
size = _read_varint(buffer)
47+
encoded = buffer.read(size)
48+
return encoded.decode('utf-8')
49+
50+
def _write_short(value: int, buffer: io.BufferedWriter):
51+
buffer.write(struct.pack('H', value))
52+
53+
def _read_short(buffer: io.BufferedReader):
54+
return struct.unpack('H', buffer.read(2))[0]
55+
56+
def _write_long(value: int, buffer: io.BufferedWriter):
57+
buffer.write(struct.pack('q', value))
58+
59+
def _read_long(buffer: io.BufferedReader):
60+
return struct.unpack('q', buffer.read(8))[0]

0 commit comments

Comments
 (0)