server.py 3.16 KB
Newer Older
m!nus's avatar
m!nus committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
from random import randint
from time import time
import re
import logging
logging.basicConfig(format="[%(asctime)s] %(levelname)s: %(funcName)s: %(message)s", level=logging.DEBUG)
L = logging.getLogger(__name__)

from .base import *
from .player import Player

class Server(object):
	class Info(Request):
		packet_request = 10*b'\xff' + b'gie3'
		packet_response = 10*b'\xff' + b'inf3'

		def __init__(self, address, data_cb):
			self.address = address
			self.time_sent = None
			self.latency = None
			self.data_cb = data_cb
			self.token = randint(1,255)

		def get_data(self):
24
			return self.packet_request + bytes([self.token])
m!nus's avatar
m!nus committed
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43

		def sent(self):
			self.time_sent = time()

		def response_received(self, data):
			if len(data) <= len(self.packet_response) or not data.startswith(self.packet_response):
				return True # that's not it, wait for more
			pieces = data[len(self.packet_response):].split(b'\x00')
			try:
				if pieces.pop(0).decode("ascii") != str(self.token):
					L.warning("Invalid token received, waiting for more data")
					return True
			except ValueError:
				L.warning("Invalid token received, waiting for more data")
				return True
			self.latency = time() - self.time_sent
			self.data_cb(pieces)

	def __init__(self, socket, address, master=None):
44
		self.address = address
m!nus's avatar
m!nus committed
45 46 47 48 49 50
		self._socket = socket
		self.master = master
		self.data = None
		self.reset()

	def reset(self):
51
		self._request = self.Info(self.address, self.parse)
m!nus's avatar
m!nus committed
52 53 54
		self.latency = None
		self.playerlist = []
		self.version = None
55
		self.name = str(self.address)
m!nus's avatar
m!nus committed
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
		self.map = None
		self.gametype = None
		self.password = None
		self.players = None
		self.max_players = None
		self.clients = None
		self.max_clients = None

	def request_info(self):
		#L.debug("Requesting {}".format(self.address))
		self._socket.send(self._request)

	def parse(self, pieces):
		L.debug("Response received from {}".format(self.address))
		self.latency = self._request.latency
		it = iter(pieces)
		try:
73 74 75 76 77 78 79 80 81
			self.version = next(it) #.decode('utf8')
			self.name = next(it) #.decode('utf8')
			self.map = next(it) #.decode('utf8')
			self.gametype = next(it) #.decode('utf8')
			self.password = (next(it)=='1')
			self.players = int(next(it))
			self.max_players = int(next(it))
			self.clients = int(next(it))
			self.max_clients = int(next(it))
m!nus's avatar
m!nus committed
82 83
			for _ in range(self.clients):
				player = Player()
84 85 86 87 88
				player.name=next(it) #.decode('utf8')
				player.clan=next(it) #.decode('utf8')
				player.country = int(next(it))
				player.score = int(next(it))
				player.playing = (next(it)=='1')
m!nus's avatar
m!nus committed
89 90 91 92 93 94 95 96 97 98 99
				player.server = self
				self.playerlist.append(player)
		except StopIteration:
			L.debug(repr(pieces))
			#raise
			self.reset()
			# TODO: raise?
			L.warning('unexpected end of data for server {}'.format(self))
		except Exception:
			L.debug(repr(pieces))
			raise
100
		self.on_info_receive(self)
m!nus's avatar
m!nus committed
101

m!nus's avatar
m!nus committed
102
	def __str__(self):
m!nus's avatar
m!nus committed
103
		return "<Server name='{name}' address='{address}' master='{master}'>".format(**self.__dict__)
104 105 106 107 108 109 110 111 112 113 114 115

	"""
	Events

	replace with your own code or subclass
	Example: server.on_info_receive = lambda server: print(server)
	"""

	def on_info_receive(self, server):
		"""Info has been received"""
		# TODO: this basically gets the server passed twice
		pass