Merge pull request #832 from GreYFoX/oypull1
[twcon.git] / scripts / tw_api.py
blob36cc5f5e49494abdc2bc9313126f3b808f01cff5
1 # coding: utf-8
2 from socket import *
3 import struct
4 import sys
5 import threading
6 import time
10 NUM_MASTERSERVERS = 4
11 MASTERSERVER_PORT = 8300
13 TIMEOUT = 2
15 SERVERTYPE_NORMAL = 0
16 SERVERTYPE_LEGACY = 1
18 PACKET_GETLIST = "\x20\x00\x00\x00\x00\x00\xff\xff\xff\xffreqt"
19 PACKET_GETLIST2 = "\x20\x00\x00\x00\x00\x00\xff\xff\xff\xffreq2"
20 PACKET_GETINFO = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgief"
21 PACKET_GETINFO2 = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgie2" + "\x00"
22 PACKET_GETINFO3 = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgie3" + "\x00"
26 class Server_Info(threading.Thread):
28 def __init__(self, address, type):
29 self.address = address
30 self.type = type
31 self.finished = False
32 threading.Thread.__init__(self, target = self.run)
34 def run(self):
35 self.info = None
36 if self.type == SERVERTYPE_NORMAL:
37 self.info = get_server_info3(self.address)
38 elif self.type == SERVERTYPE_LEGACY:
39 self.info = get_server_info(self.address)
40 if self.info:
41 self.info = get_server_info2(self.address)
42 self.finished = True
45 def get_server_info(address):
46 try:
47 sock = socket(AF_INET, SOCK_DGRAM)
48 sock.settimeout(TIMEOUT);
49 sock.sendto(PACKET_GETINFO, address)
50 data, addr = sock.recvfrom(1024)
51 sock.close()
53 data = data[14:] # skip header
54 slots = data.split("\x00")
56 server_info = {}
57 server_info["version"] = slots[0]
58 server_info["name"] = slots[1]
59 server_info["map"] = slots[2]
60 server_info["gametype"] = slots[3]
61 server_info["flags"] = int(slots[4])
62 server_info["progression"] = int(slots[5])
63 server_info["num_players"] = int(slots[6])
64 server_info["max_players"] = int(slots[7])
65 server_info["players"] = []
67 for i in xrange(0, server_info["num_players"]):
68 player = {}
69 player["name"] = slots[8+i*2]
70 player["score"] = int(slots[8+i*2+1])
71 server_info["players"].append(player)
73 return server_info
75 except:
76 sock.close()
77 return None
80 def get_server_info2(address):
81 try:
82 sock = socket(AF_INET, SOCK_DGRAM)
83 sock.settimeout(TIMEOUT);
84 sock.sendto(PACKET_GETINFO2, address)
85 data, addr = sock.recvfrom(1024)
86 sock.close()
88 data = data[14:] # skip header
89 slots = data.split("\x00")
91 server_info = {}
92 server_info["token"] = slots[0]
93 server_info["version"] = slots[1]
94 server_info["name"] = slots[2]
95 server_info["map"] = slots[3]
96 server_info["gametype"] = slots[4]
97 server_info["flags"] = int(slots[5])
98 server_info["progression"] = int(slots[6])
99 server_info["num_players"] = int(slots[7])
100 server_info["max_players"] = int(slots[8])
101 server_info["players"] = []
103 for i in xrange(0, server_info["num_players"]):
104 player = {}
105 player["name"] = slots[9+i*2]
106 player["score"] = int(slots[9+i*2+1])
107 server_info["players"].append(player)
109 return server_info
111 except:
112 sock.close()
113 return None
116 def get_server_info3(address):
117 try:
118 sock = socket(AF_INET, SOCK_DGRAM)
119 sock.settimeout(TIMEOUT);
120 sock.sendto(PACKET_GETINFO3, address)
121 data, addr = sock.recvfrom(1400)
122 sock.close()
124 data = data[14:] # skip header
125 slots = data.split("\x00")
127 server_info = {}
128 server_info["token"] = slots[0]
129 server_info["version"] = slots[1]
130 server_info["name"] = slots[2]
131 server_info["map"] = slots[3]
132 server_info["gametype"] = slots[4]
133 server_info["flags"] = int(slots[5])
134 server_info["num_players"] = int(slots[6])
135 server_info["max_players"] = int(slots[7])
136 server_info["num_clients"] = int(slots[8])
137 server_info["max_clients"] = int(slots[9])
138 server_info["players"] = []
140 for i in xrange(0, server_info["num_clients"]):
141 player = {}
142 player["name"] = slots[10+i*5]
143 player["clan"] = slots[10+i*5+1]
144 player["country"] = int(slots[10+i*5+2])
145 player["score"] = int(slots[10+i*5+3])
146 if int(slots[10+i*5+4]):
147 player["player"] = True
148 else:
149 player["player"] = False
150 server_info["players"].append(player)
152 return server_info
154 except:
155 sock.close()
156 return None
160 class Master_Server_Info(threading.Thread):
162 def __init__(self, address):
163 self.address = address
164 self.finished = False
165 threading.Thread.__init__(self, target = self.run)
167 def run(self):
168 self.servers = get_list(self.address) + get_list2(self.address)
169 self.finished = True
172 def get_list(address):
173 servers = []
175 try:
176 sock = socket(AF_INET, SOCK_DGRAM)
177 sock.settimeout(TIMEOUT)
178 sock.sendto(PACKET_GETLIST, address)
180 while 1:
181 data, addr = sock.recvfrom(1024)
183 data = data[14:]
184 num_servers = len(data) / 6
186 for n in range(0, num_servers):
187 ip = ".".join(map(str, map(ord, data[n*6:n*6+4])))
188 port = ord(data[n*6+5]) * 256 + ord(data[n*6+4])
189 servers += [[(ip, port), SERVERTYPE_LEGACY]]
191 except:
192 sock.close()
194 return servers
197 def get_list2(address):
198 servers = []
200 try:
201 sock = socket(AF_INET, SOCK_DGRAM)
202 sock.settimeout(TIMEOUT)
203 sock.sendto(PACKET_GETLIST2, address)
205 while 1:
206 data, addr = sock.recvfrom(1400)
208 data = data[14:]
209 num_servers = len(data) / 18
211 for n in range(0, num_servers):
212 if data[n*18:n*18+12] == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff":
213 ip = ".".join(map(str, map(ord, data[n*18+12:n*18+16])))
214 else:
215 ip = ":".join(map(str, map(ord, data[n*18:n*18+16])))
216 port = (ord(data[n*18+16])<<8) + ord(data[n*18+17])
217 servers += [[(ip, port), SERVERTYPE_NORMAL]]
219 except:
220 sock.close()
222 return servers
226 master_servers = []
228 for i in range(1, NUM_MASTERSERVERS+1):
229 m = Master_Server_Info(("master%d.teeworlds.com"%i, MASTERSERVER_PORT))
230 master_servers.append(m)
231 m.start()
232 time.sleep(0.001) # avoid issues
234 servers = []
236 while len(master_servers) != 0:
237 if master_servers[0].finished == True:
238 if master_servers[0].servers:
239 servers += master_servers[0].servers
240 del master_servers[0]
241 time.sleep(0.001) # be nice
243 servers_info = []
245 print str(len(servers)) + " servers"
247 for server in servers:
248 s = Server_Info(server[0], server[1])
249 servers_info.append(s)
250 s.start()
251 time.sleep(0.001) # avoid issues
253 num_players = 0
254 num_clients = 0
256 while len(servers_info) != 0:
257 if servers_info[0].finished == True:
259 if servers_info[0].info:
260 num_players += servers_info[0].info["num_players"]
261 if servers_info[0].type == SERVERTYPE_NORMAL:
262 num_clients += servers_info[0].info["num_clients"]
263 else:
264 num_clients += servers_info[0].info["num_players"]
266 del servers_info[0]
268 time.sleep(0.001) # be nice
270 print str(num_players) + " players and " + str(num_clients-num_players) + " spectators"