def __init__(self, action=None, response=None, notify=None, react={}): self._action = mapping('game_action') if action: self._action.extend(action) self._response = mapping('game_response') if response: self._response.extend(response) self._notify = mapping('game_notify') if notify: self._notify.extend(notify) rmap = GAME_REACTION.copy() rmap.update(react) self._reactor = Reactor(rmap) self._queue = deque() self._qlock = Lock() self._clisten = socket(AF_INET, SOCK_STREAM) self._clisten.setblocking(0) self._clisten.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) self._nlisten = socket(AF_INET, SOCK_STREAM) self._nlisten.setblocking(0) self._nlisten.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) self._poll = poll() self._clients = set() self._idle = {} # map of idle clients by their fileno self._anotifys = {} # map of anonymous notify sockets by their claim key
class GameServer: """ Base server class. Accepts control/notify connections, manages a list of clients, and receives instructions from clients. """ def __init__(self, action=None, response=None, notify=None, react={}): self._action = mapping('game_action') if action: self._action.extend(action) self._response = mapping('game_response') if response: self._response.extend(response) self._notify = mapping('game_notify') if notify: self._notify.extend(notify) rmap = GAME_REACTION.copy() rmap.update(react) self._reactor = Reactor(rmap) self._queue = deque() self._qlock = Lock() self._clisten = socket(AF_INET, SOCK_STREAM) self._clisten.setblocking(0) self._clisten.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) self._nlisten = socket(AF_INET, SOCK_STREAM) self._nlisten.setblocking(0) self._nlisten.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) self._poll = poll() self._clients = set() self._idle = {} # map of idle clients by their fileno self._anotifys = {} # map of anonymous notify sockets by their claim key def debug(self, message): print("SRV: {0}".format(message)) def start(self, host='127.0.0.1', cport=10000, nport=10001, backlog=5, ntimeout=5, pollrate=.001): self.debug("starting") self.host = host self.cport = cport self.nport = nport # listens for incoming control connections self._clisten.bind((host, cport)) self._clisten.listen(backlog) self.debug("listening for control connections") # listens for incoming notify connections self._nlisten.bind((host, nport)) self._nlisten.listen(backlog) self.debug("listening for notify connections") self._poll.register(self._clisten.fileno(), POLLIN) self._poll.register(self._nlisten.fileno(), POLLIN) self.debug("beginning socket poll loop") while 1: waitfor = [] for fileno, event in self._poll.poll(pollrate): self._poll.unregister(fileno) if fileno == self._clisten.fileno(): thread = Thread(target=self._caccept) elif fileno == self._nlisten.fileno(): thread = Thread(target=self._naccept) else: client = self._idle.get(fileno) if client: del self._idle[fileno] thread = Thread(target=self._clientdata, args=[client]) else: raise Exception("Unrecognized select: {0}".format(fileno)) waitfor.append(thread) thread.start() while len(waitfor): waitfor.pop().join() # all wait threads finished; process queue synchronously self._processqueue() print("Here") def queue(self, event): with self._qlock: self._queue.append(event) def addclient(self, client): self._clients.add(client) self.idleclient(client) client.debug("connected") def removeclient(self, client): self._clients.remove(client) client.debug("disconnected") client.realm = None client.control.close() client.control = None if client.notify: client.notify.close() client.notify = None def idleclient(self, client): self._idle[client.fileno()] = client self._poll.register(client.fileno(), POLLIN) def addnotify(self, notify): while 1: key = urandom(32) if not key in self._anotifys: self._anotifys[key] = notify notify.send(Message('NotifyKey', key=key)) break def claimnotify(self, key): """ Claims an anonymous notify socket, returning and dereferencing it. """ notify = self._anotifys.get(key) if notify: del self._anotifys[key] return notify else: return None def _processqueue(self): with self._qlock: while len(self._queue): self._queue.popleft().process() def _clientdata(self, client): try: reaction = self._reactor.dispatch(client, client.control.recv()) except (MessageCodeError, socketerror, structerror) as e: if isinstance(e, MessageCodeError): client.debug("Received invalid message code: {0}".format(e.code)) elif isinstance(e, socketerror): client.debug("{0} occurred during message reception".format(errorcode[e.errno])) elif isinstance(e, structerror): client.debug("Failed to unpack message data") self.queue(Event(self.removeclient, client)) else: if reaction.READONLY: reaction.process() else: self.queue(reaction) def _caccept(self): """ Connects a new client. """ sock, (host, port) = self._clisten.accept() self._poll.register(self._clisten, POLLIN) sock.setblocking(0) client = Client(self, Stream(recv=self._action, send=self._response, sock=sock, host=host, port=port)) self.queue(Event(self.addclient, client)) def _naccept(self): """ Accepts an incoming notify connection and adds it to the list of anonymous notify streams, to later be claimed by a client. Sends the claim key to the client for this purpose. """ sock, (host, port) = self._nlisten.accept() self._poll.register(self._nlisten, POLLIN) sock.setblocking(0) stream = Stream(send=self._notify, sock=sock, host=host, port=port) self.queue(Event(self.addnotify, stream))