Skip to content

Instantly share code, notes, and snippets.

@MarshalX
Last active May 6, 2024 19:14
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save MarshalX/40861e1d02cbbc6f23acd3eced9db1a0 to your computer and use it in GitHub Desktop.
Save MarshalX/40861e1d02cbbc6f23acd3eced9db1a0 to your computer and use it in GitHub Desktop.
Fix of https://gist.github.com/ewized/97814f57ac85af7128bf. Python3 class to ping a Minecraft server and get its response including ping in ms
#!/usr/bin/python3
import socket
import struct
import json
import time
class StatusPing:
""" Get the ping status for the Minecraft server """
def __init__(self, host='localhost', port=25565, timeout=5):
""" Init the hostname and the port """
self._host = host
self._port = port
self._timeout = timeout
def _unpack_varint(self, sock):
""" Unpack the varint """
data = 0
for i in range(5):
ordinal = sock.recv(1)
if len(ordinal) == 0:
break
byte = ord(ordinal)
data |= (byte & 0x7F) << 7 * i
if not byte & 0x80:
break
return data
def _pack_varint(self, data):
""" Pack the var int """
ordinal = b''
while True:
byte = data & 0x7F
data >>= 7
ordinal += struct.pack('B', byte | (0x80 if data > 0 else 0))
if data == 0:
break
return ordinal
def _pack_data(self, data):
""" Page the data """
if type(data) is str:
data = data.encode('utf8')
return self._pack_varint(len(data)) + data
elif type(data) is int:
return struct.pack('H', data)
elif type(data) is float:
return struct.pack('Q', int(data))
else:
return data
def _send_data(self, connection, *args):
""" Send the data on the connection """
data = b''
for arg in args:
data += self._pack_data(arg)
connection.send(self._pack_varint(len(data)) + data)
def _read_fully(self, connection, extra_varint=False):
""" Read the connection and return the bytes """
packet_length = self._unpack_varint(connection)
packet_id = self._unpack_varint(connection)
byte = b''
if extra_varint:
# Packet contained netty header offset for this
if packet_id > packet_length:
self._unpack_varint(connection)
extra_length = self._unpack_varint(connection)
while len(byte) < extra_length:
byte += connection.recv(extra_length)
else:
byte = connection.recv(packet_length)
return byte
def get_status(self):
""" Get the status response """
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as connection:
connection.settimeout(self._timeout)
connection.connect((self._host, self._port))
# Send handshake + status request
self._send_data(connection, b'\x00\x00', self._host, self._port, b'\x01')
self._send_data(connection, b'\x00')
# Read response, offset for string length
data = self._read_fully(connection, extra_varint=True)
# Send and read unix time
self._send_data(connection, b'\x01', time.time() * 1000)
unix = self._read_fully(connection)
# Load json and return
response = json.loads(data.decode('utf8'))
response['ping'] = int(time.time() * 1000) - struct.unpack('Q', unix)[0]
return response
@tripleInt
Copy link

for python 3.9:
Traceback (most recent call last):
File "E:\test.py", line 112, in
print(status_ping.get_status())
File "E:\test.py", line 108, in get_status
response['ping'] = int(time.time() * 1000) - struct.unpack('Q', unix)[0]
struct.error: unpack requires a buffer of 8 bytes

@MarshalX
Copy link
Author

MarshalX commented Sep 7, 2021

for python 3.9:
Traceback (most recent call last):
File "E:\test.py", line 112, in
print(status_ping.get_status())
File "E:\test.py", line 108, in get_status
response['ping'] = int(time.time() * 1000) - struct.unpack('Q', unix)[0]
struct.error: unpack requires a buffer of 8 bytes

give me some ip and port pls to reproduce it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment