Exemple #1
0
    def shutdown(self):
        # Cleanup operations
        self.shutting_down = True

        # Cancel further polling for district/master server
        if self.local_connection:
            self.local_connection.cancel()
            self.loop.run_until_complete(self.tasker.await_cancellation(self.local_connection))

        if self.district_connection:
            self.district_connection.cancel()
            self.loop.run_until_complete(self.tasker.await_cancellation(self.district_connection))

        if self.masterserver_connection:
            self.masterserver_connection.cancel()
            self.loop.run_until_complete(self.tasker.await_cancellation(self.masterserver_connection))
            self.loop.run_until_complete(self.tasker.await_cancellation(self.ms_client.shutdown()))

        # Cancel pending client tasks and cleanly remove them from the areas
        players = self.get_player_count()
        logger.log_print('Kicking {} remaining client{}.'
                         .format(players, 's' if players != 1 else ''))

        for client in self.client_manager.clients:
            client.disconnect()
Exemple #2
0
def _upcoming_python_version_check():
    current_python_tuple = sys.version_info
    current_python_simple = 'Python {}.{}.{}'.format(*current_python_tuple[:3])
    if current_python_tuple < (3, 9):
        msg = (
            f'WARNING: The upcoming major release of TsuserverDR (4.4.0) will be requiring '
            f'at least Python 3.9. You currently have {current_python_simple}. '
            f'Please consider upgrading to at least Python 3.9 soon. You may find '
            f'additional instructions on updating in README.md')
        logger.log_print(msg)
Exemple #3
0
    def send_error_report(self, client: ClientManager.Client, cmd: str,
                          args: List[str], ex: Exception):
        """
        In case of an error caused by a client packet, send error report to user, notify moderators
        and have full traceback available on console and through /lasterror
        """

        # Send basic logging information to user
        info = (
            '=========\nThe server ran into a Python issue. Please contact the server owner '
            'and send them the following logging information:')
        etype, evalue, etraceback = sys.exc_info()
        tb = traceback.extract_tb(tb=etraceback)
        current_time = Constants.get_time()
        file, line_num, module, func = tb[-1]
        file = file[file.rfind('\\') + 1:]  # Remove unnecessary directories
        version = self.version
        info += '\r\n*Server version: {}'.format(version)
        info += '\r\n*Server time: {}'.format(current_time)
        info += '\r\n*Packet details: {} {}'.format(cmd, args)
        info += '\r\n*Client status: {}'.format(client)
        info += '\r\n*Area status: {}'.format(client.area)
        info += '\r\n*File: {}'.format(file)
        info += '\r\n*Line number: {}'.format(line_num)
        info += '\r\n*Module: {}'.format(module)
        info += '\r\n*Function: {}'.format(func)
        info += '\r\n*Error: {}: {}'.format(type(ex).__name__, ex)
        info += '\r\nYour help would be much appreciated.'
        info += '\r\n========='
        client.send_ooc(info)
        client.send_ooc_others(
            'Client {} triggered a Python error through a client packet. '
            'Do /lasterror to take a look at it.'.format(client.id),
            pred=lambda c: c.is_mod)

        # Print complete traceback to console
        info = 'TSUSERVERDR HAS ENCOUNTERED AN ERROR HANDLING A CLIENT PACKET'
        info += '\r\n*Server time: {}'.format(current_time)
        info += '\r\n*Packet details: {} {}'.format(cmd, args)
        info += '\r\n*Client status: {}'.format(client)
        info += '\r\n*Area status: {}'.format(client.area)
        info += '\r\n\r\n{}'.format("".join(
            traceback.format_exception(etype, evalue, etraceback)))
        logger.log_print(info)
        self.last_error = [info, etype, evalue, etraceback]

        # Log error to file
        logger.log_error(info, server=self, errortype='C')

        if self.in_test:
            raise ex
Exemple #4
0
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')
Exemple #5
0
    def __init__(self):
        self.release = 3
        self.major_version = 'DR'
        self.minor_version = '190622b'
        self.software = 'tsuserver{}'.format(self.get_version_string())
        self.version = 'tsuserver{}dev'.format(self.get_version_string())

        logger.log_print('Launching {}...'.format(self.software))

        logger.log_print('Loading server configurations...')
        self.config = None
        self.global_connection = None
        self.shutting_down = False
        self.loop = None

        self.allowed_iniswaps = None
        self.default_area = 0
        self.load_config()
        self.load_iniswaps()
        self.char_list = list()
        self.load_characters()
        self.client_manager = ClientManager(self)
        self.area_manager = AreaManager(self)
        self.ban_manager = BanManager(self)

        self.ipid_list = {}
        self.hdid_list = {}
        self.char_pages_ao1 = None
        self.music_list = None
        self.music_list_ao2 = None
        self.music_pages_ao1 = None
        self.backgrounds = None
        self.load_music()
        self.load_backgrounds()
        self.load_ids()
        self.district_client = None
        self.ms_client = None
        self.rp_mode = False
        self.user_auth_req = False
        self.client_tasks = dict()
        self.active_timers = dict()
        self.showname_freeze = False
        self.commands = importlib.import_module('server.commands')
        logger.setup_logger(debug=self.config['debug'])
Exemple #6
0
    def shutdown(self):
        # Cleanup operations
        self.shutting_down = True

        # Cancel further polling for district/master server
        if self.global_connection:
            self.global_connection.cancel()
            self.loop.run_until_complete(
                self.await_cancellation(self.global_connection))

        # Cancel pending client tasks and cleanly remove them from the areas
        logger.log_print('Kicking {} remaining clients.'.format(
            self.get_player_count()))

        for area in self.area_manager.areas:
            while area.clients:
                client = next(iter(area.clients))
                area.remove_client(client)
                for task_id in self.client_tasks[client.id].keys():
                    task = self.get_task(client, [task_id])
                    self.loop.run_until_complete(self.await_cancellation(task))
Exemple #7
0
    def __init__(self, server: TsuserverDR):
        super().__init__()
        self.server = server
        self.client = None
        self.buffer = ''
        self.ping_timeout = None
        logger.log_print = logger.log_print2 if self.server.in_test else logger.log_print

        # Determine whether /exec is active or not and warn server owner if so.
        if getattr(self.server.commands, "ooc_cmd_exec")(self.client, "is_exec_active") == 1:
            logger.log_print("""

                  WARNING

                  THE /exec COMMAND IN commands.py IS ACTIVE.

                  UNLESS YOU ABSOLUTELY MEANT IT AND KNOW WHAT YOU ARE DOING,
                  PLEASE STOP YOUR SERVER RIGHT NOW AND DEACTIVATE IT BY GOING TO THE
                  commands.py FILE AND FOLLOWING THE INSTRUCTIONS UNDER ooc_cmd_exec.\n
                  BAD THINGS CAN AND WILL HAPPEN OTHERWISE.

                  """)
Exemple #8
0
    def net_cmd_ct(self, args):
        """ OOC Message

        CT#<name:string>#<message:string>#%

        """
        if self.client.is_ooc_muted:  # Checks to see if the client has been muted by a mod
            self.client.send_ooc("You have been muted by a moderator.")
            return
        if not self.validate_net_cmd(
                args, ArgType.STR, ArgType.STR, needs_auth=False):
            return
        if self.client.name != args[0] and self.client.fake_name != args[0]:
            if self.client.is_valid_name(args[0]):
                self.client.name = args[0]
                self.client.fake_name = args[0]
            else:
                self.client.fake_name = args[0]
                self.client.name = ''
        if self.client.name == '':
            self.client.send_ooc(
                'You must insert a name with at least one letter.')
            return
        if self.client.name.startswith(' '):
            self.client.send_ooc(
                'You must insert a name that starts with a letter.')
            return
        if self.server.config[
                'hostname'] in self.client.name or '<dollar>G' in self.client.name:
            self.client.send_ooc('That name is reserved.')
            return
        if args[1].startswith('/'):
            spl = args[1][1:].split(' ', 1)
            cmd = spl[0]
            arg = ''
            if len(spl) == 2:
                arg = spl[1][:1024]
            try:
                called_function = 'ooc_cmd_{}'.format(cmd)
                function = None  # Double assignment to check if it matched to a function later
                function = getattr(self.server.commands, called_function)
            except AttributeError:
                try:
                    function = getattr(self.server.commands_alt,
                                       called_function)
                except AttributeError:
                    logger.log_print('Attribute error with ' + called_function)
                    self.client.send_ooc('Invalid command.')

            if function:
                try:
                    function(self.client, arg)
                except TsuserverException as ex:
                    self.client.send_ooc(ex)
        else:
            # Censor passwords if accidentally said without a slash in OOC
            for password in self.server.all_passwords:
                for login in ['login ', 'logincm ', 'loginrp ', 'logingm ']:
                    if login + password in args[1]:
                        args[1] = args[1].replace(password, '[CENSORED]')
            if self.client.disemvowel:  #If you are disemvoweled, replace string.
                args[1] = Constants.disemvowel_message(args[1])
            if self.client.disemconsonant:  #If you are disemconsonanted, replace string.
                args[1] = Constants.disemconsonant_message(args[1])
            if self.client.remove_h:  #If h is removed, replace string.
                args[1] = Constants.remove_h_message(args[1])

            self.client.area.send_command('CT', self.client.name, args[1])
            self.client.last_ooc_message = args[1]
            logger.log_server(
                '[OOC][{}][{}][{}]{}'.format(self.client.area.id,
                                             self.client.get_char_name(),
                                             self.client.name, args[1]),
                self.client)
        self.client.last_active = Constants.get_time()
Exemple #9
0
    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
        """
        # try to decode as utf-8, ignore any erroneous characters
        self.buffer += data.decode('utf-8', 'ignore')
        if len(self.buffer) > 8192:
            self.client.disconnect()
        for msg in self.get_messages():
            if len(msg) < 2:
                self.client.disconnect()
                return
            # general netcode structure is not great
            if msg[0] in ('#', '3', '4'):
                if msg[0] == '#':
                    msg = msg[1:]
                spl = msg.split('#', 1)
                msg = '#'.join([fanta_decrypt(spl[0])] + spl[1:])
                logger.log_debug('[INC][RAW]{}'.format(msg), self.client)
            try:
                cmd, *args = msg.split('#')
                self.net_cmd_dispatcher[cmd](self, args)
            except Exception as ex:
                # Send basic logging information to user
                info = '=========\nThe server ran into a Python issue. Please contact the server owner and send them the following logging information:'
                etype, evalue, etraceback = sys.exc_info()
                tb = traceback.extract_tb(tb=etraceback)
                current_time = asctime(localtime(time()))
                file, line_num, module, func = tb[-1]
                file = file[file.rfind('\\') +
                            1:]  # Remove unnecessary directories
                info += '\r\n*Server time: {}'.format(current_time)
                info += '\r\n*Packet details: {} {}'.format(cmd, args)
                info += '\r\n*Client status: {}, {}, {}'.format(
                    self.client.id, self.client.get_char_name(),
                    self.client.is_staff())
                info += '\r\n*Area status: {}, {}'.format(
                    self.client.area.id, len(self.client.area.clients))
                info += '\r\n*File: {}'.format(file)
                info += '\r\n*Line number: {}'.format(line_num)
                info += '\r\n*Module: {}'.format(module)
                info += '\r\n*Function: {}'.format(func)
                info += '\r\n*Error: {}: {}'.format(type(ex).__name__, ex)
                info += '\r\nYour help would be much appreciated.'
                info += '\r\n========='
                self.client.send_host_message(info)

                # Print complete traceback to console
                info = 'TSUSERVER HAS ENCOUNTERED AN ERROR HANDLING A CLIENT PACKET'
                info += '\r\n*Server time: {}'.format(current_time)
                info += '\r\n*Packet details: {} {}'.format(cmd, args)
                info += '\r\n*Client status: {}, {}, {}'.format(
                    self.client.id, self.client.get_char_name(),
                    self.client.is_staff())
                info += '\r\n*Area status: {}, {}'.format(
                    self.client.area.id, len(self.client.area.clients))
                logger.log_print(info)
                traceback.print_exception(etype, evalue, etraceback)
Exemple #10
0
    def net_cmd_ct(self, args):
        """ OOC Message

        CT#<name:string>#<message:string>#%

        """
        if self.client.is_ooc_muted:  # Checks to see if the client has been muted by a mod
            self.client.send_host_message(
                "You have been muted by a moderator.")
            return
        if not self.validate_net_cmd(
                args, self.ArgType.STR, self.ArgType.STR, needs_auth=False):
            return
        if self.client.name != args[0] and self.client.fake_name != args[0]:
            if self.client.is_valid_name(args[0]):
                self.client.name = args[0]
                self.client.fake_name = args[0]
            else:
                self.client.fake_name = args[0]
                self.client.name = ''
        if self.client.name == '':
            self.client.send_host_message(
                'You must insert a name with at least one letter.')
            return
        if self.client.name.startswith(' '):
            self.client.send_host_message(
                'You must insert a name that starts with a letter.')
            return
        if self.server.config[
                'hostname'] in self.client.name or '<dollar>G' in self.client.name:
            self.client.send_host_message('That name is reserved!')
            return
        if args[1].startswith('/'):
            spl = args[1][1:].split(' ', 1)
            cmd = spl[0]
            arg = ''
            if len(spl) == 2:
                arg = spl[1][:256]
            try:
                called_function = 'ooc_cmd_{}'.format(cmd)
                function = getattr(self.server.commands, called_function)
            except AttributeError:
                logger.log_print('Attribute error with ' + called_function)
                self.client.send_host_message('Invalid command.')
            else:
                try:
                    function(self.client, arg)
                except (ClientError, AreaError, ArgumentError,
                        ServerError) as ex:
                    self.client.send_host_message(ex)
                except Exception:
                    raise  # Explicit raising, even though not needed
        else:
            if self.client.disemvowel:  #If you are disemvoweled, replace string.
                args[1] = self.client.disemvowel_message(args[1])
            if self.client.disemconsonant:  #If you are disemconsonanted, replace string.
                args[1] = self.client.disemconsonant_message(args[1])
            if self.client.remove_h:  #If h is removed, replace string.
                args[1] = self.client.remove_h_message(args[1])

            self.client.area.send_command('CT', self.client.name, args[1])
            logger.log_server(
                '[OOC][{}][{}][{}]{}'.format(self.client.area.id,
                                             self.client.get_char_name(),
                                             self.client.name, args[1]),
                self.client)
Exemple #11
0
    def __init__(self, protocol=None, client_manager=None, in_test=False):
        self.release = 4
        self.major_version = 2
        self.minor_version = 5
        self.segment_version = 'post9'
        self.internal_version = '210325a'
        version_string = self.get_version_string()
        self.software = 'TsuserverDR {}'.format(version_string)
        self.version = 'TsuserverDR {} ({})'.format(version_string, self.internal_version)
        self.in_test = in_test

        self.protocol = AOProtocol if protocol is None else protocol
        client_manager = ClientManager if client_manager is None else client_manager
        logger.log_print = logger.log_print2 if self.in_test else logger.log_print
        logger.log_server = logger.log_server2 if self.in_test else logger.log_server
        self.random = importlib.reload(random)

        logger.log_print('Launching {}...'.format(self.version))
        logger.log_print('Loading server configurations...')

        self.config = None
        self.local_connection = None
        self.district_connection = None
        self.masterserver_connection = None
        self.shutting_down = False
        self.loop = None
        self.last_error = None
        self.allowed_iniswaps = None
        self.area_list = None
        self.old_area_list = None
        self.default_area = 0
        self.all_passwords = list()

        self.load_config()
        self.load_iniswaps()
        self.char_list = list()
        self.char_pages_ao1 = None
        self.load_characters()
        self.load_commandhelp()
        self.client_manager = client_manager(self)
        self.zone_manager = ZoneManager(self)
        self.area_manager = AreaManager(self)
        self.ban_manager = BanManager(self)
        self.party_manager = PartyManager(self)

        self.ipid_list = {}
        self.hdid_list = {}
        self.music_list = None
        self._music_list_ao2 = None # Pending deprecation in 4.3
        self.music_pages_ao1 = None
        self.backgrounds = None
        self.load_music()
        self.load_backgrounds()
        self.load_ids()
        self.district_client = None
        self.ms_client = None
        self.rp_mode = False
        self.user_auth_req = False
        # self.client_tasks = dict() # KEPT FOR BACKWARDS COMPATIBILITY
        # self.active_timers = dict() # KEPT FOR BACKWARDS COMPATIBILITY
        self.showname_freeze = False
        self.commands = importlib.import_module('server.commands')
        self.commands_alt = importlib.import_module('server.commands_alt')
        self.logger_handlers = logger.setup_logger(debug=self.config['debug'])

        logger.log_print('Server configurations loaded successfully!')
Exemple #12
0
    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.')
Exemple #13
0
    def start(self):
        self.loop = asyncio.get_event_loop()

        bound_ip = '0.0.0.0'
        if self.config['local']:
            bound_ip = '127.0.0.1'
            logger.log_print(
                'Starting a local server. Ignore outbound connection attempts.'
            )

        ao_server_crt = self.loop.create_server(lambda: AOProtocol(self),
                                                bound_ip, self.config['port'])
        ao_server = self.loop.run_until_complete(ao_server_crt)

        logger.log_pdebug('Server started successfully!\n')

        if self.config['use_district']:
            self.district_client = DistrictClient(self)
            self.global_connection = asyncio.ensure_future(
                self.district_client.connect(), loop=self.loop)
            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.global_connection = asyncio.ensure_future(
                self.ms_client.connect(), loop=self.loop)
            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_print('Server has successfully shut down.')
Exemple #14
0
    def __init__(self,
                 protocol: AOProtocol = None,
                 client_manager: ClientManager = None,
                 in_test: bool = False):
        self.logged_packet_limit = 100  # Arbitrary
        self.logged_packets = []
        self.print_packets = False  # For debugging purposes
        self._server = None  # Internal server object, changed to proper object later

        self.release = 4
        self.major_version = 3
        self.minor_version = 0
        self.segment_version = 'post5'
        self.internal_version = '220304a'
        version_string = self.get_version_string()
        self.software = 'TsuserverDR {}'.format(version_string)
        self.version = 'TsuserverDR {} ({})'.format(version_string,
                                                    self.internal_version)
        self.in_test = in_test

        self.protocol = AOProtocol if protocol is None else protocol
        client_manager = ClientManager if client_manager is None else client_manager
        logger.log_print = logger.log_print2 if self.in_test else logger.log_print
        logger.log_server = logger.log_server2 if self.in_test else logger.log_server
        self.random = importlib.reload(random)

        logger.log_print('Launching {}...'.format(self.version))
        logger.log_print('Loading server configurations...')

        self.config = None
        self.local_connection = None
        self.district_connection = None
        self.masterserver_connection = None
        self.shutting_down = False
        self.loop = None
        self.last_error = None
        self.allowed_iniswaps = None
        self.area_list = None
        self.old_area_list = None
        self.default_area = 0
        self.all_passwords = list()
        self.global_allowed = True
        self.server_select_name = 'SERVER_SELECT'

        self.load_config()
        self.client_manager: ClientManager = client_manager(self)
        self.char_list = list()
        self.load_iniswaps()
        self.load_characters()

        self.game_manager = GameManager(self)
        self.trial_manager = TrialManager(self)
        self.zone_manager = ZoneManager(self)
        self.area_manager = AreaManager(self)
        self.ban_manager = BanManager(self)
        self.party_manager = PartyManager(self)

        self.ipid_list = {}
        self.hdid_list = {}
        self.music_list = None
        self.backgrounds = None
        self.gimp_list = list()
        self.load_commandhelp()
        self.load_music()
        self.load_backgrounds()
        self.load_ids()
        self.load_gimp()

        self.district_client = None
        self.ms_client = None
        self.rp_mode = False
        self.user_auth_req = False
        self.showname_freeze = False
        self.commands = importlib.import_module('server.commands')
        self.commands_alt = importlib.import_module('server.commands_alt')
        self.logger_handlers = logger.setup_logger(debug=self.config['debug'])

        logger.log_print('Server configurations loaded successfully!')

        self.error_queue = None
        self._server = None
Exemple #15
0
    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()