def write_msg_to(socket, msg): if not isinstance(msg, Message): debug_print('BAD. attempting to write_msg_to:', msg) else: debug_print('out --> msg', msg, 'to socket', socket) ''' send the given msg into the socket returns True if writing completes ''' my_file = StringIO() packer = msgpack.Packer() my_file.write(packer.pack(msg.serialize())) my_file = StringIO(my_file.getvalue()) # debug_print(vars(my_file)) tot_bytes = len(my_file.buf) sent_now = 1 to_send = tot_bytes while sent_now != 0: # 0 means send done try: sent_now = socket.send(my_file.read(to_send)) to_send -= sent_now if to_send == 0: return True except: return False return True
def header_matches_string(self, string): try: assert string in header2int return header2int[string] == self.msg_header except Exception as e: debug_print('header_matches_string FAILED') raise RuntimeError(e)
def __repr__(self): try: assert 0 <= self.msg_header < len(int2header) return 'Message::' + int2header[self.msg_header] + ' from ' \ + str(self.sender) + ' with args:' + str(self.args) except Exception as e: debug_print('Msg REPR FAILED') raise RuntimeError(e)
def main_loop(self): # split thread into outgoing and incoming debug_print('main loop!') incoming_thread = threading.Thread( target=self.main_incoming_loop, args=(), ) incoming_thread.daemon = True incoming_thread.start() self.main_outgoing_loop()
def read_msg_from(sock, timeout=None): assert timeout is None or isinstance(timeout, float) ''' attempts to read exactly ONE message from the given socket may return Message may return MessageError.CRASH if timeout is not None: may return MessageError.TIMEOUT ''' unpacker = msgpack.Unpacker() while True: try: sock.settimeout(timeout) x = sock.recv(1) if x == '': debug_print('socket dead!') return MessageError.CRASH # connection closed unpacker.feed(x) for package in unpacker: x = Message.deserialize(package) debug_print(' ::read msg', x) return x except socket.timeout: debug_print("timeout error") return MessageError.TIMEOUT except Exception as e: debug_print("sth else occurred: ", e) return MessageError.CRASH
def measure_rtt_to(ip, port): debug_print('rtt to...', ip, port, 'is...') start_time = time.time() result = Client.sock_client( ip, port, timeout=das_game_settings.client_ping_max_time) rtt = (time.time() - start_time if result is not None else das_game_settings.client_ping_max_time) if result is not None: result.close() debug_print('rtt', rtt) logging.info( ('Rtt to {ip} {port} was measured to be {rtt} sec').format( ip=ip, port=port, rtt=rtt)) return rtt
def __init__(self, msg_header, sender, args): # print('init', msg_header, sender, args) try: assert isinstance(msg_header, int) and \ msg_header in range(0, len(int2header)) assert isinstance(sender, int) or isinstance( sender, list) or isinstance(sender, tuple) assert isinstance(args, list) self.msg_header = msg_header if isinstance(sender, int): self.sender = sender else: self.sender = tuple(sender) self.args = args # print('WENT OK', self.msg_header, self.sender, self.args) except Exception as e: print('MESSAGE INIT FAILED!', msg_header, sender, args) # print('went bad!') debug_print('MESSAGE INIT FAILED', msg_header, sender, args) raise e
def __init__(self, player): # TODO player reconnect after crash # TODO handle S2C_REFUSE self._name = generate_name() log_filename = 'client_{name}.log'.format(name=self._name) logging.basicConfig(filename=log_filename, filemode='a', level=das_game_settings.logging_level, format='%(asctime)s.%(msecs)03d <client:' + self._name + '> %(message)s', datefmt='%a %H:%M:%S') logging.info( "Client `{name}` started logging! :D".format(name=self._name)) assert isinstance(player, client_player.Player) self._player = player # in order of descending 'quality self.sorted_server_ids = Client._ordered_server_list() debug_print('self.sorted_server_ids', self.sorted_server_ids) self._connect_to_a_server()
def generate_messages_from(sock, timeout=True): debug_print('ITSYABOI, GENERATOR') assert timeout is None or isinstance(timeout, float) ''' Generator object. will read and yield messages from new_socket if timeout==True, may yield None objects. This allows the caller to regularly if ''' unpacker = msgpack.Unpacker() sock.settimeout(timeout) try: while True: try: x = sock.recv(1) if x == '': debug_print('socket dead!') return unpacker.feed(x) for package in unpacker: yield Message.deserialize(package) except socket.timeout: if timeout: yield MessageError.TIMEOUT except GeneratorExit: debug_print('generator dieded') return except Exception: yield MessageError.CRASH return
def write_many_msgs_to(socket, msg_iterable): packer = msgpack.Packer() all_went_perfectly = True try: for msg in msg_iterable: if not isinstance(msg, Message): debug_print('BAD. attempting to write_msg_to:', msg) else: debug_print('try out --> msg', msg, 'to socket', socket) my_file = StringIO() my_file.write(packer.pack(msg.serialize())) my_file = StringIO(my_file.getvalue()) # tot_bytes = len(my_file.buf) sent_now = 1 while sent_now != 0: # 0 means send done try: sent_now = socket.send(my_file.read(256)) except: all_went_perfectly = False except: all_went_perfectly = False return all_went_perfectly
def main_incoming_loop(self): debug_print('main incoming') # while True: # msg = messaging.read_msg_from(self._server_socket, timeout=None) while True: debug_print('Remade update generator') update_generator = messaging.generate_messages_from( self._server_socket, timeout=None) for msg in update_generator: debug_print(str(msg)) if msg != MessageError.CRASH: if msg.header_matches_string('UPDATE'): new_state = DragonArena.deserialize(msg.args[1]) if das_game_settings.client_visualizer: ascii_draw(new_state, me=self._my_id) self._protected_game_state.replace_arena(new_state) logging.info( 'Received a server update. Replaced arena') debug_print('replaced arena! :D') if new_state.game_over and not das_game_settings.suppress_game_over: logging.info(('Latest game state is a GAME OVER ' 'state...')) time.sleep(2.0) logging.info(('Latest game state is a GAME OVER ' 'state...')) print(('GAME OVER! {winners} win!').format( winners=new_state.get_winner())) logging.info(('GAME OVER! {winners} win!').format( winners=new_state.get_winner())) os._exit(0) else: break debug_print("MY SERVER CRASHED") logging.info(('Incoming handler detected crash! ' 'Re-establishing connection...')) self._connect_to_a_server(reconnect=True) logging.info('Connection back up!')
def main_outgoing_loop(self): req_generator = self._player.main_loop(self._protected_game_state, self._my_id) debug_print('ready?') for request in req_generator: logging.info( 'Player yielded request: {request}'.format(request=request)) assert isinstance(request, Message) debug_print('forwarding', request) if not messaging.write_msg_to(self._server_socket, request): logging.info("Forwarding the player request has failed! " "Incoming handler should fix this eventually..") debug_print('no more requests. Player closed down I guess')
def main_loop(protected_dragon_arena, my_id): assert isinstance(protected_dragon_arena, protected.ProtectedDragonArena) debug_print('bot player main loop') debug_print('my id', my_id) # has self._game_state_copy st = das_game_settings.bot_action_period try: while True: # TODO: while game.playing # Roy: And I'm not dead? time.sleep(random.uniform(st * 0.8, st / 0.8)) with protected_dragon_arena as da: try: choice = BotPlayer._choose_action_return_message( da, my_id) except Exception as e: choice = None debug_print("BOT CRASHED WHEN DECIDING", e) # `with` expired. dragon arena unlocked if choice is not None: yield choice except GeneratorExit: # clean up generator return
def _connect_to_a_server(self, reconnect=False): logging.info("Connecting to server...") backoff = 0.01 while True: for serv_id in self.sorted_server_ids: try: ip, port = das_game_settings.server_addresses[serv_id] debug_print('Trying server at', ip, port) logging.info( "Trying server (id={serv_id}) at {ip} {port}".format( serv_id=serv_id, ip=ip, port=port)) '''1. get socket''' self._server_socket = Client.sock_client(ip, port) if self._server_socket is None: logging.info("No dice.") continue logging.info("Got a socket! Sending HELLO") debug_print('self._server_socket', self._server_socket) '''2. send hello''' if not reconnect: self._random_salt = random.randint(0, 999999) logging.info(("This is a fresh connection. " "my salt is {salt}").format( salt=self._random_salt)) hello_msg = messaging.M_C2S_HELLO(self._random_salt) else: logging.info("(This is a RE-connection. " "my salt is still {salt})".format( salt=self._random_salt)) hello_msg = messaging.M_C2S_HELLO_AGAIN( self._random_salt, self._my_id, self._secret) debug_print('about to send msg', str(hello_msg)) messaging.write_msg_to(self._server_socket, hello_msg) '''2. get reply (expect welcome)''' reply_msg = messaging.read_msg_from( self._server_socket, timeout=das_game_settings.client_handshake_timeout) if messaging.is_message_with_header_string( reply_msg, 'REFUSE'): debug_print( 'got refused by server_id {serv_id}'.format( serv_id=serv_id)) logging.info("Refused!") continue if not messaging.is_message_with_header_string( reply_msg, 'S2C_WELCOME'): logging.info( ('CRASH or TIMEOUT for ' 'server_id {serv_id}').format(serv_id=serv_id)) raise RuntimeError('crash or timeout') '''3. get my knight's ID''' self._my_id = tuple(reply_msg.args[0]) self._secret = reply_msg.args[1] logging.info(('Successful connection to Server_id ' '{serv_id}. My knight ID is {kid}').format( serv_id=serv_id, kid=self._my_id)) '''4. wait for 1st game update''' first_update =\ messaging.read_msg_from( self._server_socket, timeout=max(das_game_settings.max_done_wait, das_game_settings. client_handshake_timeout)) if not messaging.is_message_with_header_string( first_update, 'UPDATE'): logging.info(('Got {msg} but I expected my first ' 'update').format(msg=first_update)) raise RuntimeError('got' + str(first_update) + 'instead of first update') logging.info('Got my first update!') '''5. try deserialize and extract game state''' self._protected_game_state = protected.ProtectedDragonArena( DragonArena.deserialize(first_update.args[1])) logging.info( ('Successfully deserialized game state. ' 'Hash is {h}').format(h=self._protected_game_state. _dragon_arena.get_hash())) return # exit the loop except Exception as e: debug_print('CONNECTION WENT AWRY :(', e) logging.info(('Connection went wrong! Reason: {e}').format( e=str(e))) debug_print('failed to connect to everyone! D:') time.sleep(backoff) logging.info(('Backing off. Sleeping {sec}').format(sec=backoff)) debug_print('backing off...', backoff) backoff = sqrt(backoff * 1.7)