async def _abnormal_shutdown(exception, server=None): # Print complete traceback to console etype, evalue, etraceback = (type(exception), exception, exception.__traceback__) info = 'TSUSERVERDR HAS ENCOUNTERED A FATAL PYTHON ERROR.' info += "\r\n" + "".join( traceback.format_exception(etype, evalue, etraceback)) logger.log_print(info) logger.log_error(info, server=server, errortype='P') logger.log_server('Server is shutting down due to an unhandled exception.') logger.log_print('Attempting a graceful shutdown.') if not server: logger.log_pserver('Server has successfully shut down.') return try: await server.normal_shutdown() except Exception as exception2: logger.log_print('Unable to gracefully shut down: Forcing a shutdown.') etype, evalue, etraceback = (type(exception2), exception2, exception2.__traceback__) info = "\r\n" + "".join( traceback.format_exception(etype, evalue, etraceback)) logger.log_print(info) logger.log_error(info, server=server, errortype='P')
def data_received(self, data): """ Handles any data received from the network. Receives data, parses them into a command and passes it to the command handler. :param data: bytes of data """ buf = data if buf is None: buf = b'' # try to decode as utf-8, ignore any erroneous characters self.buffer += buf.decode('utf-8', 'ignore') self.buffer = self.buffer.translate({ord(c): None for c in '\0'}) if len(self.buffer) > 8192: msg = self.buffer if len(self.buffer) < 512 else self.buffer[:512] + '...' logger.log_server('Terminated {} (packet too long): sent {} ({} bytes)' .format(self.client.get_ipreal(), msg, len(self.buffer))) self.client.disconnect() return found_message = False for msg in self.get_messages(): found_message = True if len(msg) < 2: # This immediatelly kills any client that does not even try to follow the proper # client protocol msg = self.buffer if len(self.buffer) < 512 else self.buffer[:512] + '...' logger.log_server('Terminated {} (packet too short): sent {} ({} bytes)' .format(self.client.get_ipreal(), msg, len(self.buffer))) self.client.disconnect() return # general netcode structure is not great if msg[0] in ('#', '3', '4'): if msg[0] == '#': msg = msg[1:] raw_parameters = msg.split('#') raw_parameters[0] = fanta_decrypt(raw_parameters[0]) msg = '#'.join(raw_parameters) logger.log_debug('[INC][RAW]{}'.format(msg), self.client) try: if self.server.print_packets: print(f'> {self.client.id}: {msg}') self.server.log_packet(self.client, msg, True) # Decode AO clients' encoding cmd, *args = Constants.decode_ao_packet(msg.split('#')) try: dispatched = self.net_cmd_dispatcher[cmd] except KeyError: logger.log_pserver(f'Client {self.client.id} sent abnormal packet {msg} ' f'(client version: {self.client.version}).') else: dispatched(self, args) except AOProtocolError.InvalidInboundPacketArguments: pass except Exception as ex: self.server.send_error_report(self.client, cmd, args, ex) if not found_message: # This immediatelly kills any client that does not even try to follow the proper # client protocol msg = self.buffer if len(self.buffer) < 512 else self.buffer[:512] + '...' logger.log_server('Terminated {} (packet syntax unrecognized): sent {} ({} bytes)' .format(self.client.get_ipreal(), msg, len(self.buffer))) self.client.disconnect()
def start(self): try: self.loop = asyncio.get_event_loop() except RuntimeError: self.loop = asyncio.new_event_loop() self.tasker = Tasker(self, self.loop) bound_ip = '0.0.0.0' if self.config['local']: bound_ip = '127.0.0.1' server_name = 'localhost' logger.log_print('Starting a local server...') else: server_name = self.config['masterserver_name'] logger.log_print('Starting a nonlocal server...') ao_server_crt = self.loop.create_server(lambda: self.protocol(self), bound_ip, self.config['port']) ao_server = self.loop.run_until_complete(ao_server_crt) logger.log_pserver('Server started successfully!') if self.config['local']: host_ip = '127.0.0.1' else: try: host_ip = (urllib.request.urlopen('https://api.ipify.org', context=ssl.SSLContext()) .read().decode('utf8')) except urllib.error.URLError as ex: host_ip = None logger.log_pdebug('Unable to obtain personal IP from https://api.ipify.org\n' '{}: {}\n' 'Players may be unable to join.' .format(type(ex).__name__, ex.reason)) if host_ip is not None: logger.log_pdebug('Server should be now accessible from {}:{}:{}' .format(host_ip, self.config['port'], server_name)) if not self.config['local']: logger.log_pdebug('If you want to join your server from this device, you may need to ' 'join with this IP instead: 127.0.0.1:{}:localhost' .format(self.config['port'])) if self.config['local']: self.local_connection = asyncio.ensure_future(self.tasker.do_nothing(), loop=self.loop) if self.config['use_district']: self.district_client = DistrictClient(self) self.district_connection = asyncio.ensure_future(self.district_client.connect(), loop=self.loop) print(' ') logger.log_print('Attempting to connect to district at {}:{}.' .format(self.config['district_ip'], self.config['district_port'])) if self.config['use_masterserver']: self.ms_client = MasterServerClient(self) self.masterserver_connection = asyncio.ensure_future(self.ms_client.connect(), loop=self.loop) print(' ') logger.log_print('Attempting to connect to the master server at {}:{} with the ' 'following details:'.format(self.config['masterserver_ip'], self.config['masterserver_port'])) logger.log_print('*Server name: {}'.format(self.config['masterserver_name'])) logger.log_print('*Server description: {}' .format(self.config['masterserver_description'])) try: self.loop.run_forever() except KeyboardInterrupt: pass print('') # Lame logger.log_pdebug('You have initiated a server shut down.') self.shutdown() ao_server.close() self.loop.run_until_complete(ao_server.wait_closed()) self.loop.close() logger.log_pserver('Server has successfully shut down.')
async def _normal_shutdown(server=None): if not server: logger.log_pserver('Server has successfully shut down.') return await server.normal_shutdown()
async def start(self): self.loop = asyncio.get_event_loop() self.error_queue = asyncio.Queue() self.tasker = Tasker(self) bound_ip = '0.0.0.0' if self.config['local']: bound_ip = '127.0.0.1' server_name = 'localhost' logger.log_print('Starting a local server...') else: server_name = self.config['masterserver_name'] logger.log_print('Starting a nonlocal server...') # Check if port is available port = self.config['port'] with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: try: s.bind((bound_ip, port)) except socket.error as exc: if exc.errno == errno.EADDRINUSE: msg = ( f'Port {port} is in use by another application. Make sure to close any ' f'conflicting applications (even another instance of this server) and ' f'try again.') raise ServerError(msg) raise exc except OverflowError as exc: msg = str(exc).replace('bind(): ', '').capitalize() msg += ' Make sure to set your port number to an appropriate value and try again.' raise ServerError(msg) # Yes there is a race condition here (between checking if port is available, and actually # using it). The only side effect of a race condition is a slightly less nice error # message, so it's not that big of a deal. self._server = await self.loop.create_server( lambda: self.protocol(self), bound_ip, port, start_serving=False) asyncio.create_task(self._server.serve_forever()) logger.log_pserver('Server started successfully!') if self.config['local']: host_ip = '127.0.0.1' else: try: host_ip = (urllib.request.urlopen( 'https://api.ipify.org', context=ssl.SSLContext()).read().decode('utf8')) except urllib.error.URLError as ex: host_ip = None logger.log_pdebug( 'Unable to obtain personal IP from https://api.ipify.org\n' '{}: {}\n' 'Players may be unable to join.'.format( type(ex).__name__, ex.reason)) if host_ip is not None: logger.log_pdebug( 'Server should be now accessible from {}:{}:{}'.format( host_ip, self.config['port'], server_name)) if not self.config['local']: logger.log_pdebug( 'If you want to join your server from this device, you may need to ' 'join with this IP instead: 127.0.0.1:{}:localhost'.format( self.config['port'])) if self.config['local']: self.local_connection = asyncio.create_task( self.tasker.do_nothing()) if self.config['use_district']: self.district_client = DistrictClient(self) self.district_connection = asyncio.create_task( self.district_client.connect()) print(' ') logger.log_print( 'Attempting to connect to district at {}:{}.'.format( self.config['district_ip'], self.config['district_port'])) if self.config['use_masterserver']: self.ms_client = MasterServerClient(self) self.masterserver_connection = asyncio.create_task( self.ms_client.connect()) print(' ') logger.log_print( 'Attempting to connect to the master server at {}:{} with the ' 'following details:'.format(self.config['masterserver_ip'], self.config['masterserver_port'])) logger.log_print('*Server name: {}'.format( self.config['masterserver_name'])) logger.log_print('*Server description: {}'.format( self.config['masterserver_description'])) raise await self.error_queue.get()