Commit 6d6deda9 authored by m!nus's avatar m!nus
Browse files

rewrote address handling, removed old example code

parent 8f0d857d
#!/usr/bin/env python2
from teeworlds.teeworlds import Teeworlds
# set up stuff
tw = Teeworlds(timeout=2)
# ask the masters for servers
tw.query_masters()
# query servers, wait for responses
# stops if no packet is received for `timeout` seconds
tw.run_loop()
# filter the serverlist
servers = tw.serverlist.find(name="^C", # Server whose name begins with "C", regex style
gametype="CTF", # gametype contains "CTF"
maxping=0.1) # ping is lower or equal to 100ms
# sort by ping
servers.sort(key=lambda s: s.latency)
# display a nice list
for server in servers:
print("{server: <64} [{gametype: ^16}] on {master}: {clients: >2}/{max_clients: <2} - {latency: >4.0f} ms" \
.format(server=server.name, gametype=server.gametype, master=server.master.name, clients=server.clients, \
max_clients=server.max_clients, latency=server.latency*1000))
# check if m!nus is currently playing
minus_list = tw.playerlist.find(name="^m!nus$")
if minus_list.players:
minus = minus_list.players[0]
print("m!nus is currently playing on {server} ({address}) with {players} other player(s)."
.format(server=minus.server.name, address=minus.server.address, players=(minus.server.players-1)))
else:
print("m!nus isn't playing at the moment.")
#!/usr/bin/env python2
import sys
from teeworlds.teeworlds import Teeworlds
# set up stuff
tw = Teeworlds(timeout=2)
# ask the masters for servers
tw.query_masters()
# query servers, wait for responses
# stops if no packet is received for `timeout` seconds
tw.run_loop()
# filter the serverlist
ip_filter = "^"+sys.argv[1].replace(".", "\\.")
servers = tw.serverlist.find(address=ip_filter)
# sort by ping
servers.sort(key=lambda s: s.address)
# display a nice list
for server in servers:
print("{server: <64} {address: <15} [{gametype: ^16}] on {master}: {clients: >2}/{max_clients: <2} - {latency: >4.0f} ms" \
.format(server=server.name, address=server.address, gametype=server.gametype, master=server.master.name, clients=server.clients, \
max_clients=server.max_clients, latency=server.latency*1000))
# check if m!nus is currently playing
#minus_list = tw.playerlist.find(name="^m!nus$")
#if minus_list.players:
# minus = minus_list.players[0]
# print("m!nus is currently playing on {server} ({address}) with {players} other player(s)."
# .format(server=minus.server.name, address=minus.server.address, players=(minus.server.players-1)))
#else:
# print("m!nus isn't playing at the moment.")
...@@ -13,40 +13,61 @@ L = logging.getLogger(__name__) ...@@ -13,40 +13,61 @@ L = logging.getLogger(__name__)
def get_address(host, port=8303, family=0): def get_address(host, port=8303, family=0):
try: try:
info = socket.getaddrinfo(host, port, family, socket.SOCK_DGRAM) info = socket.getaddrinfo(host, port, family, socket.SOCK_DGRAM)
return info[0][4] return Address(info[0][4][0], info[0][4][1], family)
except socket.gaierror as e: except socket.gaierror as e:
L.warning('getaddrinfo failed: ' + str(e)) L.warning('getaddrinfo failed: ' + str(e))
return None return None
def is_ipv6(address): def listdata2addresslist(listdata):
if isinstance(address, tuple): address = address[0]
# TODO: should be more solid
return True if ':' in address else False
def ip_from_data(data):
"""takes 6 or 18 bytes of data and extracts IPv4/v6 addresses from it """takes 6 or 18 bytes of data and extracts IPv4/v6 addresses from it
returns a tuple (family, address) returns a tuple (family, address)
""" """
addresses = []
for i in range(0, len(listdata), 18):
data = listdata[i:i+18]
# ::ffff:0:0/96 == IPv4 mapping # ::ffff:0:0/96 == IPv4 mapping
if data[0:12] == b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff": if data[0:12] == b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff":
data = data[12:18] data = data[12:18]
if len(data) == 6: if len(data) == 6:
return (socket.inet_ntoa(data[:4]), unpack("!H", data[4:])[0]) host = socket.inet_ntoa(data[:4])
port = unpack("!H", data[4:])[0]
addresses.append(Address(host, port, socket.AF_INET))
elif len(data) == 18: elif len(data) == 18:
address = None host = None
port = unpack("!H", data[16:])[0] port = unpack("!H", data[16:])[0]
if sys.platform == "win32": if sys.platform == "win32":
segments = [] segments = []
for (a, b) in (data[:16:2], data[1:16:2]): for (a, b) in (data[:16:2], data[1:16:2]):
segments.append("{:x}".format((ord(a)<<8) + ord(b))) segments.append("{:x}".format((ord(a)<<8) + ord(b)))
address = ':'.join(segments) host = ':'.join(segments)
else: else:
address = socket.inet_ntop(socket.AF_INET6, data[:16]) host = socket.inet_ntop(socket.AF_INET6, data[:16])
return (address, port) addresses.append(Address(host, port, socket.AF_INET6))
else: else:
raise Exception("Invalid IP data") raise Exception("Invalid IP data")
return addresses
class Address(object):
def __init__(self, host=None, port=None, family=None):
self.host = host
self.port = port
self.family = family
def address_tuple(self):
return (self.host, self.port)
def __lt__(self, other):
return self.address_tuple() < other.address_tuple()
def __str__(self):
if self.family == socket.AF_INET6:
return "[{host}]:{port}".format(self.host, self.port)
return "{host}:{port}".format(host=self.host, port=self.port)
class Request(object): class Request(object):
"""network communication data wrapper""" """network communication data wrapper"""
...@@ -62,7 +83,7 @@ class Request(object): ...@@ -62,7 +83,7 @@ class Request(object):
return False return False
def get_address(self): def get_address(self):
"""must return destination address as a tuple (host, port)""" """must return destination address, an Address object"""
return self.address return self.address
def get_data(self): def get_data(self):
...@@ -115,12 +136,13 @@ class EventSocket(object): ...@@ -115,12 +136,13 @@ class EventSocket(object):
def _send(self, request): def _send(self, request):
"""Actually send request""" """Actually send request"""
socket_type = socket.AF_INET socket_type = socket.AF_INET
if is_ipv6(request.get_address()): addr = request.get_address()
if addr.family == socket.AF_INET6:
if not self.has_ipv6: if not self.has_ipv6:
raise socket.error("Cannot send IPv6 packet without IPv6 socket") raise socket.error("Cannot send IPv6 packet without IPv6 socket")
socket_type = socket.AF_INET6 socket_type = socket.AF_INET6
self._requests[request.get_address()].add(request) self._requests[addr.address_tuple()].add(request)
length = self._sockets[socket_type].sendto(request.get_data(), request.get_address()) length = self._sockets[socket_type].sendto(request.get_data(), addr.address_tuple())
L.debug("Sent {}".format(request)) L.debug("Sent {}".format(request))
request.sent() request.sent()
self._packet_rate += 1 self._packet_rate += 1
...@@ -142,7 +164,6 @@ class EventSocket(object): ...@@ -142,7 +164,6 @@ class EventSocket(object):
try: try:
length = self._send(request) length = self._send(request)
if length != len(data): if length != len(data):
# TODO: resend?
L.warning('Sent {} of {} bytes of {}'.format(length, len(data))) L.warning('Sent {} of {} bytes of {}'.format(length, len(data)))
except socket.error as e: except socket.error as e:
if e.errno == 10054: # ICMP port unreachable if e.errno == 10054: # ICMP port unreachable
......
...@@ -58,15 +58,13 @@ class MasterServer(object): ...@@ -58,15 +58,13 @@ class MasterServer(object):
def __init__(self, socket, address, name=None): def __init__(self, socket, address, name=None):
self._socket = socket self._socket = socket
self._address = address self.address = address
self.address = ("[{host}]:{port}" if is_ipv6(address) else "{host}:{port}") \
.format(host=address[0], port=address[1])
self.name = name or "None" self.name = name or "None"
self.latency = None self.latency = None
self.serverlist = [] self.serverlist = []
self.count = None self.count = None
self._request_count = self.Count(self._address, self._count_response) self._request_count = self.Count(self.address, self._count_response)
self._request_list = self.List(self._address, self.add_from_serverlist) self._request_list = self.List(self.address, self.add_from_serverlist)
def request_list(self): def request_list(self):
self.on_list_request(self._request_list) self.on_list_request(self._request_list)
...@@ -82,8 +80,8 @@ class MasterServer(object): ...@@ -82,8 +80,8 @@ class MasterServer(object):
""" """
if len(data) % self.List.serveraddr_size != 0: if len(data) % self.List.serveraddr_size != 0:
L.warning("Serverlist data length is not a multiple of {}".format(self.List.serveraddr_size)) L.warning("Serverlist data length is not a multiple of {}".format(self.List.serveraddr_size))
for i in range(0, len(data), self.List.serveraddr_size): for address in listdata2addresslist(data):
server = Server(self._socket, ip_from_data(data[i:i+self.List.serveraddr_size]), master=self) server = Server(self._socket, address, master=self)
self.serverlist.append(server) self.serverlist.append(server)
self.on_server_add(server) self.on_server_add(server)
......
...@@ -41,20 +41,18 @@ class Server(object): ...@@ -41,20 +41,18 @@ class Server(object):
self.data_cb(pieces) self.data_cb(pieces)
def __init__(self, socket, address, master=None): def __init__(self, socket, address, master=None):
self._address = address self.address = address
self.address = ("[{host}]:{port}" if is_ipv6(address) else "{host}:{port}") \
.format(host=address[0], port=address[1])
self._socket = socket self._socket = socket
self.master = master self.master = master
self.data = None self.data = None
self.reset() self.reset()
def reset(self): def reset(self):
self._request = self.Info(self._address, self.parse) self._request = self.Info(self.address, self.parse)
self.latency = None self.latency = None
self.playerlist = [] self.playerlist = []
self.version = None self.version = None
self.name = self.address self.name = str(self.address)
self.map = None self.map = None
self.gametype = None self.gametype = None
self.password = None self.password = None
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment