Exemple #1
0
def _config_server() -> None:
    """Apply server config changes that can take effect immediately.

    (party name, etc)
    """
    config = copy.deepcopy(_ba.app.server_config)

    # FIXME: Should make a proper low level config entry for this or
    #  else not store in in app.config. Probably shouldn't be going through
    #  the app config for this anyway since it should just be for this run.
    _ba.app.config['Auto Balance Teams'] = (config.get('auto_balance_teams',
                                                       True))

    _ba.set_public_party_max_size(config.get('max_party_size', 9))
    _ba.set_public_party_name(config.get('party_name', 'party'))
    _ba.set_public_party_stats_url(config.get('stats_url', ''))

    # Call set-enabled last (will push state).
    _ba.set_public_party_enabled(config.get('party_is_public', True))

    if not _ba.app.run_server_first_run:
        print('server config updated.')

    # FIXME: We could avoid setting this as dirty if the only changes have
    #  been ones here we can apply immediately. Could reduce cases where
    #  players have to rejoin.
    _ba.app.server_config_dirty = True
    def _on_start_advertizing_press(self) -> None:
        from bastd.ui.account import show_sign_in_prompt
        if _ba.get_account_state() != 'signed_in':
            show_sign_in_prompt()
            return

        name = cast(str, ba.textwidget(query=self._host_name_text))
        if name == '':
            ba.screenmessage(ba.Lstr(resource='internal.invalidNameErrorText'),
                             color=(1, 0, 0))
            ba.playsound(ba.getsound('error'))
            return
        _ba.set_public_party_name(name)
        cfg = ba.app.config
        cfg['Public Party Name'] = name
        cfg.commit()
        ba.playsound(ba.getsound('shieldUp'))
        _ba.set_public_party_enabled(True)

        # In GUI builds we want to authenticate clients only when hosting
        # public parties.
        _ba.set_authenticate_clients(True)

        self._do_status_check()
        ba.buttonwidget(
            edit=self._host_toggle_button,
            label=ba.Lstr(
                resource='gatherWindow.makePartyPrivateText',
                fallback_resource='gatherWindow.stopAdvertisingText'),
            on_activate_call=self._on_stop_advertising_press)
Exemple #3
0
    def _update(self) -> None:
        """Periodic updating."""

        # Special case: if a party-queue window is up, don't do any of this
        # (keeps things smoother).
        if ba.app.ui.have_party_queue_window:
            return

        # If we've got a party-name text widget, keep its value plugged
        # into our public host name.
        text = self._host_name_text
        if text:
            name = cast(str, ba.textwidget(query=self._host_name_text))
            _ba.set_public_party_name(name)

        if self._sub_tab is SubTabType.JOIN:

            # If our filter value has changed, refresh the list
            # using the new one.
            text = self._filter_text
            if text:
                filter_value = cast(str, ba.textwidget(query=text))
                if filter_value != self._filter_value:
                    self._filter_value = filter_value
                    self._server_list_dirty = True
                    self._update_server_list()

            self._query_party_list_periodically()
            self._ping_parties_periodically()
Exemple #4
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
Exemple #5
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')
            print(f'{Clr.BLD}{Clr.BLU}BallisticaCore {app.version}'
                  f' ({app.build_number})'
                  f' entering server-mode {curtimestr}{Clr.RST}')

        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.
        _ba.new_host_session(sessiontype)

        if not self._ran_access_check:
            self._run_access_check()
            self._ran_access_check = True
    def _update(self) -> None:
        """Periodic updating."""

        # Special case: if a party-queue window is up, don't do any of this
        # (keeps things smoother).
        # if ba.app.ui.have_party_queue_window:
        #     return

        if self._sub_tab is SubTabType.JOIN:

            # Keep our filter-text up to date from the UI.
            text = self._filter_text
            if text:
                filter_value = cast(str, ba.textwidget(query=text))
                if filter_value != self._filter_value:
                    self._filter_value = filter_value
                    self._party_lists_dirty = True

                    # Also wipe out party clean-row states.
                    # (otherwise if a party disappears from a row due to
                    # filtering and then reappears on that same row when
                    # the filter is removed it may not update)
                    for party in self._parties.values():
                        party.clean_display_index = None

            self._query_party_list_periodically()
            self._ping_parties_periodically()

        # If any new party infos have come in, apply some of them.
        self._process_pending_party_infos()

        # Anytime we sign in/out, make sure we refresh our list.
        signed_in = _ba.get_account_state() == 'signed_in'
        if self._signed_in != signed_in:
            self._signed_in = signed_in
            self._party_lists_dirty = True

        # Update sorting to account for ping updates, new parties, etc.
        self._update_party_lists()

        # If we've got a party-name text widget, keep its value plugged
        # into our public host name.
        text = self._host_name_text
        if text:
            name = cast(str, ba.textwidget(query=self._host_name_text))
            _ba.set_public_party_name(name)

        # Update status text.
        status_text = self._join_status_text
        if status_text:
            if not signed_in:
                ba.textwidget(edit=status_text,
                              text=ba.Lstr(resource='notSignedInText'))
            else:
                # If we have a valid list, show no status; just the list.
                # Otherwise show either 'loading...' or 'error' depending
                # on whether this is our first go-round.
                if self._have_valid_server_list:
                    ba.textwidget(edit=status_text, text='')
                else:
                    if self._have_server_list_response:
                        ba.textwidget(edit=status_text,
                                      text=ba.Lstr(resource='errorText'))
                    else:
                        ba.textwidget(
                            edit=status_text,
                            text=ba.Lstr(
                                value='${A}...',
                                subs=[('${A}',
                                       ba.Lstr(resource='store.loadingText'))],
                            ))

        self._update_party_rows()
    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
    def _update_internet_tab(self) -> None:
        global searchText, textModified

        def updatepartylist():
            self._internet_join_last_refresh_time = now
            self._first_public_party_list_rebuild_time = ba.time(
                ba.TimeType.REAL) + 1
            app = ba.app
            _ba.add_transaction(
                {
                    'type': 'PUBLIC_PARTY_QUERY',
                    'proto': app.protocol_version,
                    'lang': app.language
                },
                callback=ba.WeakCall(self._on_public_party_query_result))
            _ba.run_transactions()

        def checkBox(val):
            global checkBoxBool
            checkBoxBool = bool(val)
            updatepartylist()

        if not searchText:
            widgetInstalled = True
            c_width = self._scroll_width
            c_height = self._scroll_height - 20
            v = c_height - 30

            searchText = txt = ba.textwidget(
                parent=self._tab_container,
                position=(c_width * 0.5 + 250, v + 105),
                color=ba.app.ui.title_color,
                scale=1.3,
                size=(150, 30),
                maxwidth=145,
                h_align='left',
                v_align='center',
                click_activate=True,
                selectable=True,
                autoselect=True,
                on_activate_call=lambda: self._set_internet_tab(
                    'host', playsound=True),
                editable=True,
                text='')
            ba.textwidget(parent=self._tab_container,
                          position=(c_width * 0.5 + 125, v + 122),
                          color=ba.app.ui.title_color,
                          scale=1.1,
                          size=(0, 0),
                          h_align='left',
                          v_align='center',
                          on_activate_call=lambda: self._set_internet_tab(
                              'host', playsound=True),
                          text='Search:')
            ba.checkboxwidget(parent=self._tab_container,
                              text="Case-Sensitive",
                              position=(c_width * 0.5 + 125, v + 135),
                              color=ba.app.ui.title_color,
                              textcolor=ba.app.ui.title_color,
                              size=(50, 50),
                              scale=1,
                              value=False,
                              on_value_change_call=checkBox)

        # pylint: disable=too-many-statements

        # Special case: if a party-queue window is up, don't do any of this
        # (keeps things smoother).
        if ba.app.ui.have_party_queue_window:
            return
        # If we've got a party-name text widget, keep its value plugged
        # into our public host name.
        text = self._internet_host_name_text
        if text:
            name = cast(str,
                        ba.textwidget(query=self._internet_host_name_text))
            _ba.set_public_party_name(name)

        # Show/hide the lock icon depending on if we've got pro.
        icon = self._internet_lock_icon
        if icon:
            if self._is_internet_locked():
                ba.imagewidget(edit=icon, opacity=0.5)
            else:
                ba.imagewidget(edit=icon, opacity=0.0)

        if self._internet_tab == 'join':
            now = ba.time(ba.TimeType.REAL)
            if (now - self._internet_join_last_refresh_time > 0.001 *
                    _ba.get_account_misc_read_val('pubPartyRefreshMS', 10000)):
                updatepartylist()
            search_text = cast(str, ba.textwidget(query=searchText))

            if search_text != '':

                textModified = True
                x = []
                if not checkBoxBool:
                    search_text = search_text.lower()
                    for i in self._public_parties:
                        if not search_text in self._public_parties[i][
                                'name'].lower():
                            x.append(i)
                else:
                    for i in self._public_parties:
                        if not search_text in self._public_parties[i]['name']:
                            x.append(i)

                for i in x:
                    del self._public_parties[i]
                self._first_public_party_list_rebuild_time = ba.time(
                    ba.TimeType.REAL) + 1
                self._rebuild_public_party_list()

            else:
                if textModified:
                    updatepartylist()

                    textModified = False
            # Go through our existing public party entries firing off pings
            # for any that have timed out.
            for party in list(self._public_parties.values()):
                if (party['next_ping_time'] <= now
                        and ba.app.ping_thread_count < 15):

                    # Make sure to fully catch up and not to multi-ping if
                    # we're way behind somehow.
                    while party['next_ping_time'] <= now:
                        # Crank the interval up for high-latency parties to
                        # save us some work.
                        mult = 1
                        if party['ping'] is not None:
                            mult = (10 if party['ping'] > 300 else
                                    5 if party['ping'] > 150 else 2)
                        party[
                            'next_ping_time'] += party['ping_interval'] * mult

                    class PingThread(threading.Thread):
                        """Thread for sending out pings."""
                        def __init__(self, address: str, port: int,
                                     call: Callable[[str, int, Optional[int]],
                                                    Optional[int]]):
                            super().__init__()
                            self._address = address
                            self._port = port
                            self._call = call

                        def run(self) -> None:
                            # pylint: disable=too-many-branches
                            ba.app.ping_thread_count += 1
                            try:
                                import socket
                                from ba.internal import get_ip_address_type
                                socket_type = get_ip_address_type(
                                    self._address)
                                sock = socket.socket(socket_type,
                                                     socket.SOCK_DGRAM)
                                sock.connect((self._address, self._port))

                                accessible = False
                                starttime = time.time()

                                # Send a few pings and wait a second for
                                # a response.
                                sock.settimeout(1)
                                for _i in range(3):
                                    sock.send(b'\x0b')
                                    result: Optional[bytes]
                                    try:
                                        # 11: BA_PACKET_SIMPLE_PING
                                        result = sock.recv(10)
                                    except Exception:
                                        result = None
                                    if result == b'\x0c':
                                        # 12: BA_PACKET_SIMPLE_PONG
                                        accessible = True
                                        break
                                    time.sleep(1)
                                sock.close()
                                ping = int((time.time() - starttime) * 1000.0)
                                ba.pushcall(ba.Call(
                                    self._call, self._address, self._port,
                                    ping if accessible else None),
                                            from_other_thread=True)
                            except ConnectionRefusedError:
                                # Fine, server; sorry we pinged you. Hmph.
                                pass
                            except OSError as exc:
                                import errno

                                # Ignore harmless errors.
                                if exc.errno in {
                                        errno.EHOSTUNREACH,
                                        errno.ENETUNREACH,
                                }:
                                    pass
                                elif exc.errno == 10022:
                                    # Windows 'invalid argument' error.
                                    pass
                                elif exc.errno == 10051:
                                    # Windows 'a socket operation was attempted
                                    # to an unreachable network' error.
                                    pass
                                elif exc.errno == errno.EADDRNOTAVAIL:
                                    if self._port == 0:
                                        # This has happened. Ignore.
                                        pass
                                    elif ba.do_once():
                                        print(
                                            f'Got EADDRNOTAVAIL on gather ping'
                                            f' for addr {self._address}'
                                            f' port {self._port}.')
                                else:
                                    ba.print_exception(
                                        f'Error on gather ping '
                                        f'(errno={exc.errno})',
                                        once=True)
                            except Exception:
                                ba.print_exception('Error on gather ping',
                                                   once=True)
                            ba.app.ping_thread_count -= 1

                    PingThread(party['address'], party['port'],
                               ba.WeakCall(self._ping_callback)).start()