Minecraft server status checker
Example usage
With the default port 25565
:
python mc_status.py --host example.example.com
Or, with a custom port:
python mc_status.py --host example.example.com --port 1234
References
https://wiki.vg/Server_List_Ping#Status_Request
https://gist.github.com/barneygale/1209061
https://gist.github.com/clarence112/9a3e971283d7f4052a0c33f11de9b7c5
Python script
import socket
import struct
import json
import argparse
def unpack_varint(s):
d = 0
for i in range(5):
b = ord(s.recv(1))
d |= (b & 0x7F) << 7*i
if not b & 0x80:
break
return d
def pack_varint(d):
o = b""
while True:
b = d & 0x7F
d >>= 7
o += struct.pack("B", b | (0x80 if d > 0 else 0))
if d == 0:
break
return o
def pack_data(d):
h = pack_varint(len(d))
if type(d) == str:
d = bytes(d, "utf-8")
return h + d
def pack_port(i):
return struct.pack('>H', i)
def get_info(host='localhost', port=25565):
s = None
# First try to resolve and connect the host as a domain that points to an A record
try:
host = socket.getaddrinfo(host, None, socket.AF_INET)[0][4][0]
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
except socket.gaierror:
pass
# Then try to resolve and connect the host as an AAAA record
if not s:
try:
host = socket.getaddrinfo(host, None, socket.AF_INET6)[0][4][0]
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
s.connect((host, port, 0, 0))
except socket.gaierror:
pass
# If the host is still unresolved, raise an error
if not s:
raise socket.gaierror(f"Host could not be resolved: {host}")
# Send handshake + status request
s.send(pack_data(b"\x00\x00" + pack_data(host.encode('utf8')) + pack_port(port) + b"\x01"))
s.send(pack_data("\x00"))
# Read response
unpack_varint(s) # Packet length
unpack_varint(s) # Packet ID
l = unpack_varint(s) # String length
d = b""
while len(d) < l:
d += s.recv(1024)
# Close our socket
s.close()
# Load json and return
return json.loads(d.decode('utf8'))
def main():
parser = argparse.ArgumentParser(description='Get Minecraft server information.')
parser.add_argument('--host', type=str, help='The host of the server.')
parser.add_argument('--port', type=int, default=25565, help='The port of the server.')
args = parser.parse_args()
print(get_info(args.host, args.port))
if __name__ == "__main__":
main()