commit b463a4b9196d7e34883be33a4322e095b1073a41 Author: Nathan Adams Date: Sun Nov 6 05:57:38 2011 +0000 Initial commit diff --git a/minecraft_query.py b/minecraft_query.py new file mode 100644 index 0000000..e641d1f --- /dev/null +++ b/minecraft_query.py @@ -0,0 +1,146 @@ +import socket +import struct + +class MinecraftQuery: + MAGIC_PREFIX = '\xFE\xFD' + PACKET_TYPE_CHALLENGE = 9 + PACKET_TYPE_QUERY = 0 + HUMAN_READABLE_NAMES = dict( + game_id = "Game Name", + gametype = "Game Type", + motd = "Message of the Day", + hostname = "Server Address", + hostport = "Server Port", + map = "Main World Name", + maxplayers = "Maximum Players", + numplayers = "Players Online", + players = "List of Players", + plugins = "List of Plugins", + raw_plugins = "Raw Plugin Info", + software = "Server Software", + version = "Game Version", + ) + + def __init__(self, host, port, timeout=10, id=0xBEEF, retries=2): + self.addr = (host, port) + self.id = id + self.id_packed = struct.pack('>l', id) + self.challenge_packed = struct.pack('>l', 0) + self.retries = 0 + self.max_retries = retries + + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.socket.settimeout(timeout) + + def send_raw(self, data): + self.socket.sendto(self.MAGIC_PREFIX + data, self.addr) + + def send_packet(self, type, data=''): + self.send_raw(struct.pack('>B', type) + self.id_packed + self.challenge_packed + data) + + def read_packet(self): + buff = self.socket.recvfrom(1460)[0] + type = struct.unpack('>B', buff[0])[0] + id = struct.unpack('>l', buff[1:5])[0] + return type, id, buff[5:] + + def handshake(self, bypass_retries=False): + self.send_packet(self.PACKET_TYPE_CHALLENGE) + + try: + type, id, buff = self.read_packet() + except: + if not bypass_retries: + self.retries += 1 + + if self.retries < self.max_retries: + self.handshake(bypass_retries=bypass_retries) + return + else: + raise + + self.challenge = int(buff[:-1]) + self.challenge_packed = struct.pack('>l', self.challenge) + + def get_status(self): + if not hasattr(self, 'challenge'): + self.handshake() + + self.send_packet(self.PACKET_TYPE_QUERY) + + try: + type, id, buff = self.read_packet() + except: + self.handshake() + return self.get_status() + + data = {} + + data['motd'], data['gametype'], data['map'], data['numplayers'], data['maxplayers'], buff = buff.split('\x00', 5) + data['hostport'] = struct.unpack('