Exemplo n.º 1
0
    def load_plugins(self) -> None:
        """(internal)"""
        from ba._general import getclass
        from ba._plugin import Plugin

        # Note: the plugins we load is purely based on what's enabled
        # in the app config. Our meta-scan gives us a list of available
        # plugins, but that is only used to give the user a list of plugins
        # that they can enable. (we wouldn't want to look at meta-scan here
        # anyway because it may not be done yet at this point in the launch)
        plugstates: Dict[str, Dict] = self.config.get('Plugins', {})
        assert isinstance(plugstates, dict)
        plugkeys: List[str] = sorted(key for key, val in plugstates.items()
                                     if val.get('enabled', False))
        for plugkey in plugkeys:
            try:
                cls = getclass(plugkey, Plugin)
            except Exception as exc:
                _ba.log(f"Error loading plugin class '{plugkey}': {exc}",
                        to_server=False)
                continue
            try:
                plugin = cls()
                assert plugkey not in self.active_plugins
                self.active_plugins[plugkey] = plugin
            except Exception:
                from ba import _error
                _error.print_exception(f'Error loading plugin: {plugkey}')
Exemplo n.º 2
0
    def _launch_server_session(self) -> None:
        """Kick off a host-session based on the current server config."""
        app = _ba.app
        appcfg = app.config
        sessiontype = self._get_session_type()

        if _ba.get_account_state() != 'signed_in':
            print('WARNING: launch_server_session() expects to run '
                  'with a signed in server account')

        if self._first_run:
            curtimestr = time.strftime('%c')
            _ba.log(
                f'{Clr.BLD}{Clr.BLU}{_ba.appnameupper()} {app.version}'
                f' ({app.build_number})'
                f' entering server-mode {curtimestr}{Clr.RST}',
                to_server=False)

        if sessiontype is FreeForAllSession:
            appcfg['Free-for-All Playlist Selection'] = self._playlist_name
            appcfg['Free-for-All Playlist Randomize'] = (
                self._config.playlist_shuffle)
        elif sessiontype is DualTeamSession:
            appcfg['Team Tournament Playlist Selection'] = self._playlist_name
            appcfg['Team Tournament Playlist Randomize'] = (
                self._config.playlist_shuffle)
        else:
            raise RuntimeError(f'Unknown session type {sessiontype}')

        app.teams_series_length = self._config.teams_series_length
        app.ffa_series_length = self._config.ffa_series_length

        _ba.set_authenticate_clients(self._config.authenticate_clients)

        _ba.set_enable_default_kick_voting(
            self._config.enable_default_kick_voting)
        _ba.set_admins(self._config.admins)

        # Call set-enabled last (will push state to the cloud).
        _ba.set_public_party_max_size(self._config.max_party_size)
        _ba.set_public_party_name(self._config.party_name)
        _ba.set_public_party_stats_url(self._config.stats_url)
        _ba.set_public_party_enabled(self._config.party_is_public)

        # And here.. we.. go.
        if self._config.stress_test_players is not None:
            # Special case: run a stress test.
            from ba.internal import run_stress_test
            run_stress_test(playlist_type='Random',
                            playlist_name='__default__',
                            player_count=self._config.stress_test_players,
                            round_duration=30)
        else:
            _ba.new_host_session(sessiontype)

        # Run an access check if we're trying to make a public party.
        if not self._ran_access_check and self._config.party_is_public:
            self._run_access_check()
            self._ran_access_check = True
Exemplo n.º 3
0
def handle_scan_results(results: ScanResults) -> None:
    """Called in the game thread with results of a completed scan."""

    from ba._lang import Lstr
    from ba._plugin import PotentialPlugin

    # Warnings generally only get printed locally for users' benefit
    # (things like out-of-date scripts being ignored, etc.)
    # Errors are more serious and will get included in the regular log
    # warnings = results.get('warnings', '')
    # errors = results.get('errors', '')
    if results.warnings != '' or results.errors != '':
        import textwrap
        _ba.screenmessage(Lstr(resource='scanScriptsErrorText'),
                          color=(1, 0, 0))
        _ba.playsound(_ba.getsound('error'))
        if results.warnings != '':
            _ba.log(textwrap.indent(results.warnings, 'Warning (meta-scan): '),
                    to_server=False)
        if results.errors != '':
            _ba.log(textwrap.indent(results.errors, 'Error (meta-scan): '))

    # Handle plugins.
    config_changed = False
    found_new = False
    plugstates: Dict[str, Dict] = _ba.app.config.setdefault('Plugins', {})
    assert isinstance(plugstates, dict)

    # Create a potential-plugin for each class we found in the scan.
    for class_path in results.plugins:
        _ba.app.potential_plugins.append(
            PotentialPlugin(display_name=Lstr(value=class_path),
                            class_path=class_path,
                            available=True))
        if class_path not in plugstates:
            plugstates[class_path] = {'enabled': False}
            config_changed = True
            found_new = True

    # Also add a special one for any plugins set to load but *not* found
    # in the scan (this way they will show up in the UI so we can disable them)
    for class_path, plugstate in plugstates.items():
        enabled = plugstate.get('enabled', False)
        assert isinstance(enabled, bool)
        if enabled and class_path not in results.plugins:
            _ba.app.potential_plugins.append(
                PotentialPlugin(display_name=Lstr(value=class_path),
                                class_path=class_path,
                                available=False))

    _ba.app.potential_plugins.sort(key=lambda p: p.class_path)

    if found_new:
        _ba.screenmessage(Lstr(resource='pluginsDetectedText'),
                          color=(0, 1, 0))
        _ba.playsound(_ba.getsound('ding'))

    if config_changed:
        _ba.app.config.commit()
Exemplo n.º 4
0
def read_config() -> tuple[AppConfig, bool]:
    """Read the game config."""
    import os
    import json
    from ba._generated.enums import TimeType

    config_file_healthy = False

    # NOTE: it is assumed that this only gets called once and the
    # config object will not change from here on out
    config_file_path = _ba.app.config_file_path
    config_contents = ''
    try:
        if os.path.exists(config_file_path):
            with open(config_file_path, encoding='utf-8') as infile:
                config_contents = infile.read()
            config = AppConfig(json.loads(config_contents))
        else:
            config = AppConfig()
        config_file_healthy = True

    except Exception as exc:
        print(('error reading config file at time ' +
               str(_ba.time(TimeType.REAL)) + ': \'' + config_file_path +
               '\':\n'), exc)

        # Whenever this happens lets back up the broken one just in case it
        # gets overwritten accidentally.
        print(('backing up current config file to \'' + config_file_path +
               ".broken\'"))
        try:
            import shutil
            shutil.copyfile(config_file_path, config_file_path + '.broken')
        except Exception as exc2:
            print('EXC copying broken config:', exc2)
        try:
            _ba.log('broken config contents:\n' +
                    config_contents.replace('\000', '<NULL_BYTE>'),
                    to_stdout=False)
        except Exception as exc2:
            print('EXC logging broken config contents:', exc2)
        config = AppConfig()

        # Now attempt to read one of our 'prev' backup copies.
        prev_path = config_file_path + '.prev'
        try:
            if os.path.exists(prev_path):
                with open(prev_path, encoding='utf-8') as infile:
                    config_contents = infile.read()
                config = AppConfig(json.loads(config_contents))
            else:
                config = AppConfig()
            config_file_healthy = True
            print('successfully read backup config.')
        except Exception as exc2:
            print('EXC reading prev backup config:', exc2)
    return config, config_file_healthy
Exemplo n.º 5
0
    def _shiplog(self) -> None:
        with self._lock:
            line = ''.join(self._linebits)
            if not line:
                return
            self._linebits = []

        # Log messages aren't expected to have trailing newlines.
        if line.endswith('\n'):
            line = line[:-1]
        _ba.log(line, to_stdout=False)
Exemplo n.º 6
0
def handle_scan_results(results: ScanResults) -> None:
    """Called in the game thread with results of a completed scan."""
    from ba import _lang

    # Warnings generally only get printed locally for users' benefit
    # (things like out-of-date scripts being ignored, etc.)
    # Errors are more serious and will get included in the regular log
    # warnings = results.get('warnings', '')
    # errors = results.get('errors', '')
    if results.warnings != '' or results.errors != '':
        _ba.screenmessage(_lang.Lstr(resource='scanScriptsErrorText'),
                          color=(1, 0, 0))
        _ba.playsound(_ba.getsound('error'))
        if results.warnings != '':
            _ba.log(results.warnings, to_server=False)
        if results.errors != '':
            _ba.log(results.errors)
Exemplo n.º 7
0
    def _launch_server_session(self) -> None:
        """Kick off a host-session based on the current server config."""
        # pylint: disable=too-many-branches
        app = _ba.app
        appcfg = app.config
        sessiontype = self._get_session_type()

        if _ba.get_v1_account_state() != 'signed_in':
            print('WARNING: launch_server_session() expects to run '
                  'with a signed in server account')

        # If we didn't fetch a playlist but there's an inline one in the
        # server-config, pull it in to the game config and use it.
        if (self._config.playlist_code is None
                and self._config.playlist_inline is not None):
            self._playlist_name = 'ServerModePlaylist'
            if sessiontype is FreeForAllSession:
                ptypename = 'Free-for-All'
            elif sessiontype is DualTeamSession:
                ptypename = 'Team Tournament'
            elif sessiontype is CoopSession:
                ptypename = 'Coop'
            else:
                raise RuntimeError(f'Unknown session type {sessiontype}')

            # Need to add this in a transaction instead of just setting
            # it directly or it will get overwritten by the master-server.
            _ba.add_transaction({
                'type': 'ADD_PLAYLIST',
                'playlistType': ptypename,
                'playlistName': self._playlist_name,
                'playlist': self._config.playlist_inline
            })
            _ba.run_transactions()

        if self._first_run:
            curtimestr = time.strftime('%c')
            _ba.log(
                f'{Clr.BLD}{Clr.BLU}{_ba.appnameupper()} {app.version}'
                f' ({app.build_number})'
                f' entering server-mode {curtimestr}{Clr.RST}',
                to_server=False)

        if sessiontype is FreeForAllSession:
            appcfg['Free-for-All Playlist Selection'] = self._playlist_name
            appcfg['Free-for-All Playlist Randomize'] = (
                self._config.playlist_shuffle)
        elif sessiontype is DualTeamSession:
            appcfg['Team Tournament Playlist Selection'] = self._playlist_name
            appcfg['Team Tournament Playlist Randomize'] = (
                self._config.playlist_shuffle)
        elif sessiontype is CoopSession:
            app.coop_session_args = {
                'campaign': self._config.coop_campaign,
                'level': self._config.coop_level,
            }
        else:
            raise RuntimeError(f'Unknown session type {sessiontype}')

        app.teams_series_length = self._config.teams_series_length
        app.ffa_series_length = self._config.ffa_series_length

        _ba.set_authenticate_clients(self._config.authenticate_clients)

        _ba.set_enable_default_kick_voting(
            self._config.enable_default_kick_voting)
        _ba.set_admins(self._config.admins)

        # Call set-enabled last (will push state to the cloud).
        _ba.set_public_party_max_size(self._config.max_party_size)
        _ba.set_public_party_name(self._config.party_name)
        _ba.set_public_party_stats_url(self._config.stats_url)
        _ba.set_public_party_enabled(self._config.party_is_public)

        # And here.. we.. go.
        if self._config.stress_test_players is not None:
            # Special case: run a stress test.
            from ba.internal import run_stress_test
            run_stress_test(playlist_type='Random',
                            playlist_name='__default__',
                            player_count=self._config.stress_test_players,
                            round_duration=30)
        else:
            _ba.new_host_session(sessiontype)

        # Run an access check if we're trying to make a public party.
        if not self._ran_access_check and self._config.party_is_public:
            self._run_access_check()
            self._ran_access_check = True