Merge pull request #1705 from nheir/tw_api

tw_api: fix getinfo returning None, add comments
This commit is contained in:
oy 2018-11-21 23:19:08 +01:00 committed by GitHub
commit fe57e857ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -12,14 +12,14 @@ MASTERSERVER_PORT = 8283
TIMEOUT = 2 TIMEOUT = 2
SERVERTYPE_NORMAL = 0 # src/mastersrv/mastersrv.h
PACKET_GETLIST = b"\xff\xff\xff\xffreq2" PACKET_GETLIST = b"\xff\xff\xff\xffreq2"
PACKET_LIST = b"\xff\xff\xff\xfflis2" PACKET_LIST = b"\xff\xff\xff\xfflis2"
PACKET_GETINFO = b"\xff\xff\xff\xffgie3" PACKET_GETINFO = b"\xff\xff\xff\xffgie3"
PACKET_INFO = b"\xff\xff\xff\xffinf3" PACKET_INFO = b"\xff\xff\xff\xffinf3"
# see CNetBase::SendControlMsgWithToken
def pack_control_msg_with_token(token_srv,token_cl): def pack_control_msg_with_token(token_srv,token_cl):
NET_PACKETFLAG_CONTROL = 1 NET_PACKETFLAG_CONTROL = 1
NET_CTRLMSG_TOKEN = 5 NET_CTRLMSG_TOKEN = 5
@ -45,6 +45,7 @@ def unpack_control_msg_with_token(msg):
token_srv = (b[8] << 24) + (b[9] << 16) + (b[10] << 8) + (b[11]) token_srv = (b[8] << 24) + (b[9] << 16) + (b[10] << 8) + (b[11])
return token_cl,token_srv return token_cl,token_srv
# CNetBase::SendPacketConnless
def header_connless(token_srv, token_cl): def header_connless(token_srv, token_cl):
NET_PACKETFLAG_CONNLESS = 8 NET_PACKETFLAG_CONNLESS = 8
NET_PACKETVERSION = 1 NET_PACKETVERSION = 1
@ -60,18 +61,7 @@ def header_connless(token_srv, token_cl):
b[8] = (token_cl) & 0xff b[8] = (token_cl) & 0xff
return bytes(b) return bytes(b)
class Server_Info(threading.Thread): # CVariableInt::Unpack from src/engine/shared/compression.cpp
def __init__(self, address):
self.address = address
self.finished = False
threading.Thread.__init__(self, target = self.run)
def run(self):
self.info = None
self.info = get_server_info(self.address)
self.finished = True
def unpack_int(b): def unpack_int(b):
l = list(b[:5]) l = list(b[:5])
i = 0 i = 0
@ -103,15 +93,31 @@ def unpack_int(b):
res ^= -Sign res ^= -Sign
return res, b[i:] return res, b[i:]
class Server_Info(threading.Thread):
def __init__(self, address):
self.address = address
self.finished = False
threading.Thread.__init__(self, target = self.run)
def run(self):
self.info = None
self.info = get_server_info(self.address)
self.finished = True
def get_server_info(address): def get_server_info(address):
try: try:
sock = socket(AF_INET, SOCK_DGRAM) sock = socket(AF_INET, SOCK_DGRAM)
sock.settimeout(TIMEOUT) sock.settimeout(TIMEOUT)
token = random.randrange(0x100000000) token = random.randrange(0x100000000)
# Token request
sock.sendto(pack_control_msg_with_token(-1,token),address) sock.sendto(pack_control_msg_with_token(-1,token),address)
data, addr = sock.recvfrom(1024) data, addr = sock.recvfrom(1024)
token_cl, token_srv = unpack_control_msg_with_token(data) token_cl, token_srv = unpack_control_msg_with_token(data)
assert token_cl == token, "Server %s send wrong token: %d (%d expected)" % (address, token_cl, token) assert token_cl == token, "Server %s send wrong token: %d (%d expected)" % (address, token_cl, token)
# Get info request
sock.sendto(header_connless(token_srv, token_cl) + PACKET_GETINFO + b'\x00', address) sock.sendto(header_connless(token_srv, token_cl) + PACKET_GETINFO + b'\x00', address)
data, addr = sock.recvfrom(1024) data, addr = sock.recvfrom(1024)
head = header_connless(token_cl, token_srv) + PACKET_INFO + b'\x00' head = header_connless(token_cl, token_srv) + PACKET_INFO + b'\x00'
@ -152,7 +158,7 @@ def get_server_info(address):
return server_info return server_info
except AssertionError as e: except AssertionError as e:
print(*e.args) print(*e.args)
except OSError as e: except OSError as e: # Timeout
print('> Server %s did not answer' % (address,)) print('> Server %s did not answer' % (address,))
except: except:
# import traceback # import traceback
@ -160,7 +166,7 @@ def get_server_info(address):
pass pass
finally: finally:
sock.close() sock.close()
return None return None
class Master_Server_Info(threading.Thread): class Master_Server_Info(threading.Thread):
@ -183,23 +189,30 @@ def get_list(address):
sock.settimeout(TIMEOUT) sock.settimeout(TIMEOUT)
token = random.randrange(0x100000000) token = random.randrange(0x100000000)
# Token request
sock.sendto(pack_control_msg_with_token(-1,token),address) sock.sendto(pack_control_msg_with_token(-1,token),address)
data, addr = sock.recvfrom(1024) data, addr = sock.recvfrom(1024)
token_cl, token_srv = unpack_control_msg_with_token(data) token_cl, token_srv = unpack_control_msg_with_token(data)
assert token_cl == token, "Master %s send wrong token: %d (%d expected)" % (address, token_cl, token) assert token_cl == token, "Master %s send wrong token: %d (%d expected)" % (address, token_cl, token)
# Get list request
sock.sendto(header_connless(token_srv, token_cl) + PACKET_GETLIST, addr) sock.sendto(header_connless(token_srv, token_cl) + PACKET_GETLIST, addr)
head = header_connless(token_cl, token_srv) + PACKET_LIST head = header_connless(token_cl, token_srv) + PACKET_LIST
while 1: while 1:
data, addr = sock.recvfrom(1024) data, addr = sock.recvfrom(1024)
# Header should keep consistent
assert data[:len(head)] == head, "Master %s list header mismatch: %r != %r (expected)" % (address, data[:len(head)], head) assert data[:len(head)] == head, "Master %s list header mismatch: %r != %r (expected)" % (address, data[:len(head)], head)
data = data[len(head):] data = data[len(head):]
num_servers = len(data) // 18 num_servers = len(data) // 18
for n in range(0, num_servers): for n in range(0, num_servers):
# IPv4
if data[n*18:n*18+12] == b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff": if data[n*18:n*18+12] == b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff":
ip = ".".join(map(str, data[n*18+12:n*18+16])) ip = ".".join(map(str, data[n*18+12:n*18+16]))
# IPv6
else: else:
ip = ":".join(map(str, data[n*18:n*18+16])) ip = ":".join(map(str, data[n*18:n*18+16]))
port = ((data[n*18+16])<<8) + data[n*18+17] port = ((data[n*18+16])<<8) + data[n*18+17]
@ -207,7 +220,7 @@ def get_list(address):
except AssertionError as e: except AssertionError as e:
print(*e.args) print(*e.args)
except OSError as e: except OSError as e: # Timeout
if not servers: if not servers:
print('> Master %s did not answer' % (address,)) print('> Master %s did not answer' % (address,))
except: except:
@ -217,48 +230,45 @@ def get_list(address):
return servers return servers
if __name__ == '__main__':
master_servers = []
for i in range(1, NUM_MASTERSERVERS+1):
m = Master_Server_Info(("master%d.teeworlds.com"%i, MASTERSERVER_PORT))
master_servers.append(m)
m.start()
time.sleep(0.001) # avoid issues
master_servers = [] servers = []
for i in range(1, NUM_MASTERSERVERS+1): while len(master_servers) != 0:
m = Master_Server_Info(("master%d.teeworlds.com"%i, MASTERSERVER_PORT)) if master_servers[0].finished == True:
master_servers.append(m) if master_servers[0].servers:
m.start() servers += master_servers[0].servers
time.sleep(0.001) # avoid issues del master_servers[0]
time.sleep(0.001) # be nice
servers = [] servers_info = []
while len(master_servers) != 0: print(str(len(servers)) + " servers")
if master_servers[0].finished == True:
if master_servers[0].servers:
servers += master_servers[0].servers
del master_servers[0]
time.sleep(0.001) # be nice
servers_info = [] for server in servers:
s = Server_Info(server)
servers_info.append(s)
s.start()
time.sleep(0.001) # avoid issues
print(str(len(servers)) + " servers") num_players = 0
num_clients = 0
for server in servers: while len(servers_info) != 0:
s = Server_Info(server) if servers_info[0].finished == True:
servers_info.append(s) if servers_info[0].info:
s.start() num_players += servers_info[0].info["num_players"]
time.sleep(0.001) # avoid issues num_clients += servers_info[0].info["num_clients"]
num_players = 0 del servers_info[0]
num_clients = 0
player_names = [] time.sleep(0.001) # be nice
while len(servers_info) != 0:
if servers_info[0].finished == True:
if servers_info[0].info: print(str(num_players) + " players and " + str(num_clients-num_players) + " spectators")
num_players += servers_info[0].info["num_players"]
num_clients += servers_info[0].info["num_clients"]
del servers_info[0]
time.sleep(0.001) # be nice
print(str(num_players) + " players and " + str(num_clients-num_players) + " spectators")