#!/usr/bin/python3
import socket
import time
import sys
import argparse
from pprint import pprint

DEBUG = False

def decode_array(content):
    length = decode_int()
    l = []
    for _ in range(length):
        l.append(content())
    return l

def decode_str():
    global pointer
    l = decode_int()
    data = info[pointer:pointer+l].decode("utf8")
    pointer += l
    if DEBUG:
        print(f"read string {data} ({l})")
    return data

def decode_int():
    global pointer
    i = int(info[pointer])
    if DEBUG:
        print(f"read int {i}")
    pointer += 1
    return i

def decode_version():
    version = (decode_int(), decode_int(), decode_int())
    return version

def skip(amount):
    global pointer
    b = info[pointer:pointer+amount]
    if DEBUG:
        print(f"skipped {amount} bytes: {b} ({b.hex()})")
    pointer += amount

def version_to_string(version):
    return f"{version[0]}.{version[1]}.{version[2]}"

def query_server(host, port=34197):
    global info
    s = socket.socket(type=socket.SOCK_DGRAM)
    s.settimeout(5)
    s.sendto(bytes.fromhex("30"), (host, port))
    info = s.recvfrom(512)[0]

def decode_packet():
    global pointer
    s = {}

    pointer = 0
    skip(13)
    s["server_name"] = decode_str()
    s["version"] = decode_version()
    skip(2)
    s["server_description"] = decode_str()
    skip(8)
    s["bind_addr"] = decode_str()
    skip(3)
    s["mods"] = decode_array(lambda: (decode_str(), decode_version()))
    skip(4)
    s["tags"] = decode_array(decode_str)
    s["players"] = decode_array(decode_str)

    return s

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--host", required=True)
    parser.add_argument("--port", "-p", type=int, default=34197)
    args = parser.parse_args()

    try:
        query_server(args.host, args.port)
    except Exception as e:
        print(f"CRITICAL: {e}")
        exit(2)

    s = decode_packet()

    print(f"OK: {s['server_name']}: Version {version_to_string(s['version'])}, {len(s['players'])} Players|num_players={len(s['players'])}")

