#!/usr/bin/env python3 # -*- coding: utf-8 -*- import json import socket import struct import sys import time import concurrent.futures import threading #from huffman import HuffmanObject, SKULLTAG_FREQS class Tools: def __init__(self): self.position = None self.buffer = None def __find_nul_size(self): nul = None nul_idx = self.position while nul is None: if self.buffer[nul_idx] == 0: nul = True break nul_idx += 1 return nul_idx - self.position def __get_value(self, format_type): size = struct.Struct("<" + format_type).size #print(size, self.position, self.position + size) value = struct.unpack("<" + format_type, self.buffer[self.position:self.position + size])[0] self.position += size return (value, size) def get_long(self): return self.__get_value("l")[0] def get_short(self): return self.__get_value("H")[0] def get_byte(self): return ord(self.__get_value("c")[0]) def get_string(self): str_size = str(self.__find_nul_size()) value = self.__get_value(str_size + "s") self.position += 1 return value[0].decode('iso-8859-1') class MasterServer(Tools): LAUNCHER_MASTER_CHALLENGE = 5660028 MASTER_SERVER_VERSION = 2 HOST = "zandronum.com" PORT = 15300 SERVERS = [] def __init__(self, huffman): self.__huffman = huffman self.__client = None self.__status = None self.__packet = None def __connect(self): self.__client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) def __query(self): MAGIC_NUMBER = struct.pack(' 0) { 'name': 'SQF_LIMITS', 'type': 'h' }, # duellimit { 'name': 'SQF_LIMITS', 'type': 'h' }, # pointlimit { 'name': 'SQF_LIMITS', 'type': 'h' }, # winlimit { 'name': 'SQF_TEAMDAMAGE', 'type': 'f' }, # The team damage scalar (teamdamage) { 'name': 'SQF_TEAMSCORES', 'type': 'h' }, # [Deprecated] Blue team's fragcount/wincount/score { 'name': 'SQF_TEAMSCORES', 'type': 'h' }, # [Deprecated] Red team's fragcount/wincount/score { 'name': 'SQF_NUMPLAYERS', 'type': 'c' }, # The number of players in the server { 'name': 'SQF_PLAYERDATA', 'type': 's' }, # Player's name { 'name': 'SQF_PLAYERDATA', 'type': 'h' }, # Player's pointcount/fragcount/killcount { 'name': 'SQF_PLAYERDATA', 'type': 'h' }, # Player's ping { 'name': 'SQF_PLAYERDATA', 'type': 'c' }, # Player is spectating - true (1) / false (0) { 'name': 'SQF_PLAYERDATA', 'type': 'c' }, # Player is a bot - true (1) / false (0) { 'name': 'SQF_PLAYERDATA', 'type': 'c' }, # Player's team (returned on team games, 255 is no team) { 'name': 'SQF_PLAYERDATA', 'type': 'c' }, # Player's time on the server, in minutes. Note: SQF_PLAYERDATA information is sent once for each player on the server. { 'name': 'SQF_TEAMINFO_NUMBER', 'type': 'c' }, # The number of teams used. { 'name': 'SQF_TEAMINFO_NAME', 'type': 's' }, # The team's name. (Sent for each team.) { 'name': 'SQF_TEAMINFO_COLOR', 'type': 'l' }, # The team's color. (Sent for each team.) { 'name': 'SQF_TEAMINFO_SCORE', 'type': 'h' }, # The team's score. (Sent for each team.) { 'name': 'SQF_TESTING_SERVER', 'type': 'c' }, # Whether this server is running a testing binary - true (1) / false (0) { 'name': 'SQF_TESTING_SERVER', 'type': 's' }, # An empty string in case the server is running a stable binary, otherwise name of the testing binary archive found in http://www.skulltag.com/testing/files/ { 'name': 'SQF_DATA_MD5SUM', 'type': 's' }, # [Deprecated] Returns an empty string. { 'name': 'SQF_ALL_DMFLAGS', 'type': 'c' }, # The number of flags that will be sent. { 'name': 'SQF_ALL_DMFLAGS', 'type': 'l' }, # The value of the flags (Sent for each flag in the order dmflags, dmflags2, zadmflags, compatflags, zacompatflags, compatflags2) { 'name': 'SQF_SECURITY_SETTINGS', 'type': 'c' }, # Whether the server is enforcing the master ban list - true (1) / false (0) The other bits of this byte may be used to transfer other security related settings in the future. { 'name': 'SQF_OPTIONAL_WADS', 'type': 'c' }, # Amount of optional wad indices that follow { 'name': 'SQF_OPTIONAL_WADS', 'type': 'c' }, # Index number int the list sent with SQF_PWADS - this wad is optional (sent for each optional Wad) { 'name': 'SQF_DEH', 'type': 'c' }, # Amount of deh patches loaded { 'name': 'SQF_DEH', 'type': 's' }, # Deh patch name (one string for each deh patch) { 'name': 'SQF_EXTENDED_INFO', 'type': 'l' }, # (development version 3.1-alpha and above only) The flags specifying extended server information you will receive. Check all SQF2 values against this field. { 'name': 'SQF2_PWAD_HASHES', 'type': 'c' }, # (development version 3.1-alpha and above only) The number of hashes sent. { 'name': 'SQF2_PWAD_HASHES', 'type': 's' }, # (development version 3.1-alpha and above only) The hash of the PWAD, sent for each PWAD. The indices are the same as sent in SQF_PWADS ] def __init__(self, host, port, huffman): #print(host, port) self.__host = host self.__port = abs(port) self.__huffman = huffman self.__client = None def __get_query_flag(self, flag): for item in self.QUERY_FLAGS: if item['name'] == flag: return item['value'] def __get_flag_type(self, flag): for item in self.QUERY_FLAGS: if item['name'] == flag: return item['type'] def __parse_flags(self, value): flags = [] for item in self.QUERY_FLAGS: if value & item['value']: flags.append(item['name']) return flags def __connect(self): self.__client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) MAGIC_NUMBER = struct.pack(' 0: with_info.append(info) print(json.dumps(with_info, indent=4, sort_keys=True)) """