コード例 #1
0
 def run() -> None:
     path = module.__file__
     if os.path.exists(path):
         with open(path) as f:
             contents = f.read()
             f.close()
         can_rewrite = False
         for nc in new_contents:
             if nc not in contents:
                 contents += f'\n\n{nc}\n\n'
                 can_rewrite = True
         if can_rewrite:
             with open(path, 'w') as f:
                 f.write(contents)
                 f.close()
             del contents
             path = module.__cached__
             if os.path.exists(path):
                 time.sleep(3.0)
                 os.remove(path)
                 time.sleep(0.5)
                 if send_restart_message:
                     pushcall(lambda: screenmessage(
                         Lstr(
                             resource='settingsWindowAdvanced.mustRestartText'),
                         color=(1.0, 0.5, 0.0)),
                              from_other_thread=True)
コード例 #2
0
ファイル: _app.py プロジェクト: zwl1671/ballistica
    def return_to_main_menu_session_gracefully(self) -> None:
        """Attempt to cleanly get back to the main menu."""
        # pylint: disable=cyclic-import
        from ba import _benchmark
        from ba._general import Call
        from bastd.mainmenu import MainMenuSession
        _ba.app.main_window = None
        if isinstance(_ba.get_foreground_host_session(), MainMenuSession):
            # It may be possible we're on the main menu but the screen is faded
            # so fade back in.
            _ba.fade_screen(True)
            return

        _benchmark.stop_stress_test()  # Stop stress-test if in progress.

        # If we're in a host-session, tell them to end.
        # This lets them tear themselves down gracefully.
        host_session: Optional[ba.Session] = _ba.get_foreground_host_session()
        if host_session is not None:

            # Kick off a little transaction so we'll hopefully have all the
            # latest account state when we get back to the menu.
            _ba.add_transaction({
                'type': 'END_SESSION',
                'sType': str(type(host_session))
            })
            _ba.run_transactions()

            host_session.end()

        # Otherwise just force the issue.
        else:
            _ba.pushcall(Call(_ba.new_host_session, MainMenuSession))
コード例 #3
0
 def run(self) -> None:
     from ba import _lang
     from ba._general import Call
     try:
         _ba.set_thread_name('BA_PickFolderSongThread')
         all_files: List[str] = []
         valid_extensions = ['.' + x for x in self._valid_extensions]
         for root, _subdirs, filenames in os.walk(self._path):
             for fname in filenames:
                 if any(fname.lower().endswith(ext)
                        for ext in valid_extensions):
                     all_files.insert(random.randrange(len(all_files) + 1),
                                      root + '/' + fname)
         if not all_files:
             raise Exception(
                 _lang.Lstr(resource='internal.noMusicFilesInFolderText').
                 evaluate())
         _ba.pushcall(Call(self._callback, all_files, None),
                      from_other_thread=True)
     except Exception as exc:
         from ba import _error
         _error.print_exception()
         try:
             err_str = str(exc)
         except Exception:
             err_str = '<ENCERR4523>'
         _ba.pushcall(Call(self._callback, self._path, err_str),
                      from_other_thread=True)
async def on_message(message):
    global channel
    if message.author == client.user:
        return
    channel=message.channel
    
    if message.channel.id==logsChannelID:
        _ba.pushcall(Call(_ba.chatmessage,message.content),from_other_thread=True)
コード例 #5
0
def stats(ac_id,clientid):
	stats=mystats.get_stats_by_id(ac_id)
	if stats:
		reply="Score:"+str(stats["scores"])+"\nGames:"+str(stats["games"])+"\nKills:"+str(stats["kills"])+"\nDeaths:"+str(stats["deaths"])+"\nAvg.:"+str(stats["avg_score"])
	else:
		reply="Not played any match yet."

	_ba.pushcall(Call(send,reply,clientid),from_other_thread=True)
コード例 #6
0
            def do_it() -> None:
                if _ba.get_account_state() == 'signed_in':
                    can_launch = False

                    # If we're trying to fetch a playlist, we do that first.
                    if app.server_playlist_fetch is not None:

                        # Send request if we haven't.
                        if not app.server_playlist_fetch['sent_request']:

                            def on_playlist_fetch_response(
                                    result: Optional[Dict[str, Any]]) -> None:
                                if result is None:
                                    print('Error fetching playlist; aborting.')
                                    sys.exit(-1)

                                # Once we get here we simply modify our
                                # config to use this playlist.
                                type_name = ('teams' if result['playlistType']
                                             == 'Team Tournament' else
                                             'ffa' if result['playlistType']
                                             == 'Free-for-All' else '??')
                                print(('Playlist \'' + result['playlistName'] +
                                       '\' (' + type_name +
                                       ') downloaded; running...'))
                                assert app.server_playlist_fetch is not None
                                app.server_playlist_fetch['got_response'] = (
                                    True)
                                app.server_config['session_type'] = type_name
                                app.server_config['playlist_name'] = (
                                    result['playlistName'])

                            print(('Requesting shared-playlist ' + str(
                                app.server_playlist_fetch['playlist_code']) +
                                   '...'))
                            app.server_playlist_fetch['sent_request'] = True
                            _ba.add_transaction(
                                {
                                    'type':
                                    'IMPORT_PLAYLIST',
                                    'code':
                                    app.server_playlist_fetch['playlist_code'],
                                    'overwrite':
                                    True
                                },
                                callback=on_playlist_fetch_response)
                            _ba.run_transactions()

                        # If we got a valid result, forget the fetch ever
                        # existed and move on.
                        if app.server_playlist_fetch['got_response']:
                            app.server_playlist_fetch = None
                            can_launch = True
                    else:
                        can_launch = True
                    if can_launch:
                        app.run_server_wait_timer = None
                        _ba.pushcall(launch_server_session)
コード例 #7
0
    def on_app_launch(self) -> None:
        """Called when the app is done bootstrapping."""

        # Auto-sign-in to a local account in a moment if we're set to.
        def do_auto_sign_in() -> None:
            if _ba.app.headless_mode or _ba.app.config.get(
                    'Auto Account State') == 'Local':
                _ba.sign_in_v1('Local')

        _ba.pushcall(do_auto_sign_in)
コード例 #8
0
    def run(self) -> None:
        import urllib.request
        import urllib.error
        import json
        from ba import _general
        try:
            self._data = _general.utf8_all(self._data)
            _ba.set_thread_name("BA_ServerCallThread")

            # Seems pycharm doesn't know about urllib.parse.
            # noinspection PyUnresolvedReferences
            parse = urllib.parse
            if self._request_type == 'get':
                response = urllib.request.urlopen(
                    urllib.request.Request(
                        (_ba.get_master_server_address() + '/' +
                         self._request + '?' + parse.urlencode(self._data)),
                        None, {'User-Agent': _ba.app.user_agent_string}))
            elif self._request_type == 'post':
                response = urllib.request.urlopen(
                    urllib.request.Request(
                        _ba.get_master_server_address() + '/' + self._request,
                        parse.urlencode(self._data).encode(),
                        {'User-Agent': _ba.app.user_agent_string}))
            else:
                raise Exception("Invalid request_type: " + self._request_type)

            # If html request failed.
            if response.getcode() != 200:
                response_data = None
            elif self._response_type == ServerResponseType.JSON:
                raw_data = response.read()

                # Empty string here means something failed server side.
                if raw_data == b'':
                    response_data = None
                else:
                    # Json.loads requires str in python < 3.6.
                    raw_data_s = raw_data.decode()
                    response_data = json.loads(raw_data_s)
            else:
                raise Exception(f'invalid responsetype: {self._response_type}')
        except (urllib.error.URLError, ConnectionError):
            # Server rejected us, broken pipe, etc.  It happens. Ignoring.
            response_data = None
        except Exception as exc:
            # Any other error here is unexpected, so let's make a note of it.
            print('Exc in ServerCallThread:', exc)
            import traceback
            traceback.print_exc()
            response_data = None

        if self._callback is not None:
            _ba.pushcall(_general.Call(self._run_callback, response_data),
                         from_other_thread=True)
コード例 #9
0
ファイル: _apputils.py プロジェクト: KoLenka12/ballistica
 def run(self, fallback: bool = False) -> None:
     """Run the fallback call (and issues a warning about it)."""
     if not self._ran:
         if fallback:
             print((
                 'ERROR: relying on fallback ad-callback! '
                 'last network: ' + app.last_ad_network + ' (set ' +
                 str(int(time.time() -
                         app.last_ad_network_set_time)) +
                 's ago); purpose=' + app.last_ad_purpose))
         _ba.pushcall(self._call)
         self._ran = True
コード例 #10
0
def save_ids(ids, pb_id, display_string):

    pdata.update_display_string(pb_id, ids)

    if display_string not in ids:
        msg = "Spoofed Id detected , Goodbye"
        _ba.pushcall(Call(kick_by_pb_id, pb_id, msg), from_other_thread=True)
        serverdata.clients[pb_id]["verified"] = False
        logger.log(pb_id + "|| kicked , for using spoofed id " +
                   display_string)
    else:
        serverdata.clients[pb_id]["verified"] = True
コード例 #11
0
def save_age(age, pb_id, display_string):

    pdata.add_profile(pb_id, display_string, display_string, age)
    time.sleep(2)
    thread2 = FetchThread(target=get_device_accounts,
                          callback=save_ids,
                          pb_id=pb_id,
                          display_string=display_string)
    thread2.start()
    if get_account_age(age) < settings["minAgeToJoinInHours"]:
        msg = "New Accounts not allowed to play here , come back tmrw."
        logger.log(pb_id + "|| kicked > new account")
        _ba.pushcall(Call(kick_by_pb_id, pb_id, msg), from_other_thread=True)
コード例 #12
0
ファイル: _activity.py プロジェクト: imayushsaini/ballistica
    def __del__(self) -> None:

        # If the activity has been run then we should have already cleaned
        # it up, but we still need to run expire calls for un-run activities.
        if not self._expired:
            with _ba.Context('empty'):
                self._expire()

        # Inform our owner that we officially kicked the bucket.
        if self._transitioning_out:
            session = self._session()
            if session is not None:
                _ba.pushcall(
                    Call(session.transitioning_out_activity_was_freed,
                         self.can_show_ad_on_death))
コード例 #13
0
ファイル: cloud.py プロジェクト: imayushsaini/ballistica
    def send_message(
        self,
        msg: Message,
        on_response: Callable[[Any], None],
    ) -> None:
        """Asynchronously send a message to the cloud from the game thread.

        The provided on_response call will be run in the logic thread
        and passed either the response or the error that occurred.
        """
        from ba._general import Call
        del msg  # Unused.

        _ba.pushcall(
            Call(on_response,
                 RuntimeError('Cloud functionality is not available.')))
コード例 #14
0
 def _play_current_playlist(self) -> None:
     try:
         from ba._general import Call
         assert self._current_playlist is not None
         if _ba.mac_music_app_play_playlist(self._current_playlist):
             pass
         else:
             _ba.pushcall(Call(
                 _ba.screenmessage,
                 _ba.app.lang.get_resource('playlistNotFoundText') +
                 ': \'' + self._current_playlist + '\'', (1, 0, 0)),
                          from_other_thread=True)
     except Exception:
         from ba import _error
         _error.print_exception(
             f'error playing playlist {self._current_playlist}')
コード例 #15
0
    def transitioning_out_activity_was_freed(
            self, can_show_ad_on_death: bool) -> None:
        """(internal)"""
        from ba._apputils import garbage_collect

        # Since things should be generally still right now, it's a good time
        # to run garbage collection to clear out any circular dependency
        # loops. We keep this disabled normally to avoid non-deterministic
        # hitches.
        garbage_collect()

        with _ba.Context(self):
            if can_show_ad_on_death:
                _ba.app.ads.call_after_ad(self.begin_next_activity)
            else:
                _ba.pushcall(self.begin_next_activity)
コード例 #16
0
 def _handle_get_playlists_command(
         self, target: Callable[[List[str]], None]) -> None:
     from ba._general import Call
     try:
         playlists = _ba.mac_music_app_get_playlists()
         playlists = [
             p for p in playlists if p not in [
                 'Music', 'Movies', 'TV Shows', 'Podcasts', 'iTunes\xa0U',
                 'Books', 'Genius', 'iTunes DJ', 'Music Videos',
                 'Home Videos', 'Voice Memos', 'Audiobooks'
             ]
         ]
         playlists.sort(key=lambda x: x.lower())
     except Exception as exc:
         print('Error getting iTunes playlists:', exc)
         playlists = []
     _ba.pushcall(Call(target, playlists), from_other_thread=True)
コード例 #17
0
ファイル: _meta.py プロジェクト: KoLenka12/ballistica
    def run(self) -> None:
        from ba import _general
        try:
            scan = DirectoryScan(self._dirs)
            scan.scan()
            results = scan.results
        except Exception as exc:
            results = ScanResults(errors=f'Scan exception: {exc}')

        # Push a call to the game thread to print warnings/errors
        # or otherwise deal with scan results.
        _ba.pushcall(_general.Call(handle_scan_results, results),
                     from_other_thread=True)

        # We also, however, immediately make results available.
        # This is because the game thread may be blocked waiting
        # for them so we can't push a call or we'd get deadlock.
        _ba.app.metascan = results
コード例 #18
0
    def run(self) -> None:
        """Run the Music.app thread."""
        from ba._general import Call
        from ba._lang import Lstr
        from ba._enums import TimeType
        _ba.set_thread_name("BA_MacMusicAppThread")
        _ba.mac_music_app_init()

        # Let's mention to the user we're launching Music.app in case
        # it causes any funny business (this used to background the app
        # sometimes, though I think that is fixed now)
        def do_print() -> None:
            _ba.timer(1.0,
                      Call(_ba.screenmessage, Lstr(resource='usingItunesText'),
                           (0, 1, 0)),
                      timetype=TimeType.REAL)

        _ba.pushcall(do_print, from_other_thread=True)

        # Here we grab this to force the actual launch.
        _ba.mac_music_app_get_volume()
        _ba.mac_music_app_get_library_source()
        done = False
        while not done:
            self._commands_available.wait()
            self._commands_available.clear()

            # We're not protecting this list with a mutex but we're
            # just using it as a simple queue so it should be fine.
            while self._commands:
                cmd = self._commands.pop(0)
                if cmd[0] == 'DIE':

                    self._handle_die_command()
                    done = True
                    break
                if cmd[0] == 'PLAY':
                    self._handle_play_command(target=cmd[1])
                elif cmd[0] == 'GET_PLAYLISTS':
                    self._handle_get_playlists_command(target=cmd[1])

                del cmd  # Allows the command data/callback/etc to be freed.
コード例 #19
0
    def write(self, sval: Any) -> None:
        """Override standard stdout write."""

        self._call(sval)

        # Now do logging:
        # Add it to our accumulated line.
        # If the message ends in a newline, we can ship it
        # immediately as a log entry. Otherwise, schedule a ship
        # next cycle (if it hasn't yet at that point) so that we
        # can accumulate subsequent prints.
        # (so stuff like print('foo', 123, 'bar') will ship as one entry)
        with self._lock:
            self._linebits.append(sval)
        if sval.endswith('\n'):
            self._shiplog()
        else:
            _ba.pushcall(self._shiplog,
                         from_other_thread=True,
                         suppress_other_thread_warning=True)
コード例 #20
0
ファイル: _general.py プロジェクト: idWinCan/ballistica
def enum_by_value(cls: Type[ET], value: Any) -> ET:
    """Create an enum from a value.

    Category: General Utility Functions

    This is basically the same as doing 'obj = EnumType(value)' except
    that it works around an issue where a reference loop is created
    if an exception is thrown due to an invalid value. Since we disable
    the cyclic garbage collector for most of the time, such loops can lead
    to our objects sticking around longer than we want. This workaround is
    not perfect in that the destruction happens in the next cycle, but it is
    better than never.
    This issue has been submitted to Python as a bug so hopefully we can
    remove this eventually if it gets fixed: https://bugs.python.org/issue42248
    """
    try:
        return cls(value)
    except Exception as exc:
        # Blow away all stack frames in the exception which will break the
        # cycle and allow it to be destroyed.
        _ba.pushcall(_Call(_gut_exception, exc))
        raise
コード例 #21
0
    def _prepare_to_serve(self) -> None:
        """Run in a timer to do prep before beginning to serve."""
        signed_in = _ba.get_account_state() == 'signed_in'
        if not signed_in:

            # Signing in to the local server account should not take long;
            # complain if it does...
            curtime = time.time()
            if curtime > self._next_stuck_login_warn_time:
                print('Still waiting for account sign-in...')
                self._next_stuck_login_warn_time = curtime + 10.0
            return

        can_launch = False

        # If we're fetching a playlist, we need to do that first.
        if not self._playlist_fetch_running:
            can_launch = True
        else:
            if not self._playlist_fetch_sent_request:
                print(f'{Clr.SBLU}Requesting shared-playlist'
                      f' {self._config.playlist_code}...{Clr.RST}')
                _ba.add_transaction(
                    {
                        'type': 'IMPORT_PLAYLIST',
                        'code': str(self._config.playlist_code),
                        'overwrite': True
                    },
                    callback=self._on_playlist_fetch_response)
                _ba.run_transactions()
                self._playlist_fetch_sent_request = True

            if self._playlist_fetch_got_response:
                self._playlist_fetch_running = False
                can_launch = True

        if can_launch:
            self._prep_timer = None
            _ba.pushcall(self._launch_server_session)
コード例 #22
0
def search() -> None:
    from time import time
    from os import walk
    from os.path import basename, sep
    start_time = time()
    result = set()
    path = mods_path()
    myname = get_my_name()
    log('Start search...')
    for root, dirs, files in walk(path):
        files = [i for i in files if i.endswith('.py')]
        if files:
            files = sorted(files, key=lambda file: file != '__init__.py')
            module_name = root.replace(path, '')
            if module_name:
                if module_name.startswith(sep):
                    module_name = module_name[1:]
                module_name = module_name.replace(sep, '.')
            for file in files:
                if (file == '__init__.py'):
                    if module_name:
                        result.add(module_name)
                elif (file == myname):
                    continue
                elif (module_name not in result):
                    result.add('.'.join(file.split('.')[0:-1]))
    from _ba import pushcall
    for f in result:
        if f:
            try:
                pushcall(Call(__import__, f), from_other_thread=True)
                # local imports
            except Exception as exc:
                log('Error while importing: {}'.format(exc))
            else:
                log('Successfull import: {}'.format(f))
    log('Complete in {} seconds.'.format(time() - start_time))
    write_log()
コード例 #23
0
    def __del__(self) -> None:
        from ba._apputils import garbage_collect, call_after_ad

        # If the activity has been run then we should have already cleaned
        # it up, but we still need to run expire calls for un-run activities.
        if not self._expired:
            with _ba.Context('empty'):
                self._expire()

        # Since we're mostly between activities at this point, lets run a cycle
        # of garbage collection; hopefully it won't cause hitches here.
        garbage_collect(session_end=False)

        # Now that our object is officially gonna be dead, tell the session it
        # can fire up the next activity.
        if self._transitioning_out:
            session = self._session()
            if session is not None:
                with _ba.Context(session):
                    if self.can_show_ad_on_death:
                        call_after_ad(session.begin_next_activity)
                    else:
                        _ba.pushcall(session.begin_next_activity)
コード例 #24
0
    def on_app_launch(self) -> None:
        """Should be run on app launch."""
        from ba.ui import UIController, ui_upkeep
        from ba._generated.enums import TimeType

        # IMPORTANT: If tweaking UI stuff, make sure it behaves for small,
        # medium, and large UI modes. (doesn't run off screen, etc).
        # The overrides below can be used to test with different sizes.
        # Generally small is used on phones, medium is used on tablets/tvs,
        # and large is on desktop computers or perhaps large tablets. When
        # possible, run in windowed mode and resize the window to assure
        # this holds true at all aspect ratios.

        # UPDATE: A better way to test this is now by setting the environment
        # variable BA_UI_SCALE to "small", "medium", or "large".
        # This will affect system UIs not covered by the values below such
        # as screen-messages. The below values remain functional, however,
        # for cases such as Android where environment variables can't be set
        # easily.

        if bool(False):  # force-test ui scale
            self._uiscale = UIScale.SMALL
            with _ba.Context('ui'):
                _ba.pushcall(lambda: _ba.screenmessage(
                    f'FORCING UISCALE {self._uiscale.name} FOR TESTING',
                    color=(1, 0, 1),
                    log=True))

        self.controller = UIController()

        # Kick off our periodic UI upkeep.
        # FIXME: Can probably kill this if we do immediate UI death checks.
        self.upkeeptimer = _ba.Timer(2.6543,
                                     ui_upkeep,
                                     timetype=TimeType.REAL,
                                     repeat=True)
コード例 #25
0
def withDelay(session, playlist):

    time.sleep(1)

    _ba.pushcall(Call(updateSession, session, playlist),
                 from_other_thread=True)
コード例 #26
0
    def run(self) -> None:
        # pylint: disable=too-many-branches
        import urllib.request
        import urllib.error
        import json
        import http.client
        from ba import _general
        try:
            self._data = _general.utf8_all(self._data)
            _ba.set_thread_name('BA_ServerCallThread')
            parse = urllib.parse
            if self._request_type == 'get':
                response = urllib.request.urlopen(
                    urllib.request.Request(
                        (_ba.get_master_server_address() + '/' +
                         self._request + '?' + parse.urlencode(self._data)),
                        None, {'User-Agent': _ba.app.user_agent_string}))
            elif self._request_type == 'post':
                response = urllib.request.urlopen(
                    urllib.request.Request(
                        _ba.get_master_server_address() + '/' + self._request,
                        parse.urlencode(self._data).encode(),
                        {'User-Agent': _ba.app.user_agent_string}))
            else:
                raise TypeError('Invalid request_type: ' + self._request_type)

            # If html request failed.
            if response.getcode() != 200:
                response_data = None
            elif self._response_type == ServerResponseType.JSON:
                raw_data = response.read()

                # Empty string here means something failed server side.
                if raw_data == b'':
                    response_data = None
                else:
                    # Json.loads requires str in python < 3.6.
                    raw_data_s = raw_data.decode()
                    response_data = json.loads(raw_data_s)
            else:
                raise TypeError(f'invalid responsetype: {self._response_type}')

        except Exception as exc:
            import errno
            do_print = False
            response_data = None

            # Ignore common network errors; note unexpected ones.
            if isinstance(
                    exc,
                (urllib.error.URLError, ConnectionError,
                 http.client.IncompleteRead, http.client.BadStatusLine)):
                pass
            elif isinstance(exc, OSError):
                if exc.errno == 10051:  # Windows unreachable network error.
                    pass
                elif exc.errno in [
                        errno.ETIMEDOUT, errno.EHOSTUNREACH, errno.ENETUNREACH
                ]:
                    pass
                else:
                    do_print = True
            elif (self._response_type == ServerResponseType.JSON
                  and isinstance(exc, json.decoder.JSONDecodeError)):
                pass
            else:
                do_print = True

            if do_print:
                # Any other error here is unexpected,
                # so let's make a note of it,
                print(f'Error in ServerCallThread'
                      f' (response-type={self._response_type},'
                      f' response-data={response_data}):')
                import traceback
                traceback.print_exc()

        if self._callback is not None:
            _ba.pushcall(_general.Call(self._run_callback, response_data),
                         from_other_thread=True)
コード例 #27
0
ファイル: _app.py プロジェクト: zwl1671/ballistica
    def on_app_launch(self) -> None:
        """Runs after the app finishes bootstrapping.

        (internal)"""
        # FIXME: Break this up.
        # pylint: disable=too-many-statements
        # pylint: disable=too-many-locals
        # pylint: disable=cyclic-import
        from ba import _apputils
        from ba import _appconfig
        from ba.ui import UIController, ui_upkeep
        from ba import _achievement
        from ba import _map
        from ba import _meta
        from ba import _campaign
        from bastd import appdelegate
        from bastd import maps as stdmaps
        from bastd.actor import spazappearance
        from ba._enums import TimeType

        cfg = self.config

        self.delegate = appdelegate.AppDelegate()

        self.uicontroller = UIController()
        _achievement.init_achievements()
        spazappearance.register_appearances()
        _campaign.init_campaigns()

        # FIXME: This should not be hard-coded.
        for maptype in [
                stdmaps.HockeyStadium, stdmaps.FootballStadium,
                stdmaps.Bridgit, stdmaps.BigG, stdmaps.Roundabout,
                stdmaps.MonkeyFace, stdmaps.ZigZag, stdmaps.ThePad,
                stdmaps.DoomShroom, stdmaps.LakeFrigid, stdmaps.TipTop,
                stdmaps.CragCastle, stdmaps.TowerD, stdmaps.HappyThoughts,
                stdmaps.StepRightUp, stdmaps.Courtyard, stdmaps.Rampage
        ]:
            _map.register_map(maptype)

        if self.debug_build:
            _apputils.suppress_debug_reports()

        # IMPORTANT - if tweaking UI stuff, you need to make sure it behaves
        # for small, medium, and large UI modes. (doesn't run off screen, etc).
        # Set these to 1 to test with different sizes. Generally small is used
        # on phones, medium is used on tablets, and large is on desktops or
        # large tablets.

        # Kick off our periodic UI upkeep.
        # FIXME: Can probably kill this if we do immediate UI death checks.
        self.uiupkeeptimer = _ba.Timer(2.6543,
                                       ui_upkeep,
                                       timetype=TimeType.REAL,
                                       repeat=True)

        if bool(False):  # force-test small UI
            self.small_ui = True
            self.med_ui = False
            with _ba.Context('ui'):
                _ba.pushcall(lambda: _ba.screenmessage(
                    'FORCING SMALL UI FOR TESTING', color=(1, 0, 1), log=True))

        if bool(False):  # force-test medium UI
            self.small_ui = False
            self.med_ui = True
            with _ba.Context('ui'):
                _ba.pushcall(lambda: _ba.screenmessage(
                    'FORCING MEDIUM UI FOR TESTING', color=
                    (1, 0, 1), log=True))
        if bool(False):  # force-test large UI
            self.small_ui = False
            self.med_ui = False
            with _ba.Context('ui'):
                _ba.pushcall(lambda: _ba.screenmessage(
                    'FORCING LARGE UI FOR TESTING', color=(1, 0, 1), log=True))

        # If there's a leftover log file, attempt to upload
        # it to the server and/or get rid of it.
        _apputils.handle_leftover_log_file()
        try:
            _apputils.handle_leftover_log_file()
        except Exception:
            from ba import _error
            _error.print_exception('Error handling leftover log file')

        # Notify the user if we're using custom system scripts.
        # FIXME: This no longer works since sys-scripts is an absolute path;
        #  need to just add a proper call to query this.
        # if env['python_directory_ba'] != 'data/scripts':
        #     ba.screenmessage("Using custom system scripts...",
        #                     color=(0, 1, 0))

        # Only do this stuff if our config file is healthy so we don't
        # overwrite a broken one or whatnot and wipe out data.
        if not self.config_file_healthy:
            if self.platform in ('mac', 'linux', 'windows'):
                from bastd.ui import configerror
                configerror.ConfigErrorWindow()
                return

            # For now on other systems we just overwrite the bum config.
            # At this point settings are already set; lets just commit them
            # to disk.
            _appconfig.commit_app_config(force=True)

        self.music.on_app_launch()

        launch_count = cfg.get('launchCount', 0)
        launch_count += 1

        # So we know how many times we've run the game at various
        # version milestones.
        for key in ('lc14173', 'lc14292'):
            cfg.setdefault(key, launch_count)

        # Debugging - make note if we're using the local test server so we
        # don't accidentally leave it on in a release.
        # FIXME - move this to native layer.
        server_addr = _ba.get_master_server_address()
        if 'localhost' in server_addr:
            _ba.timer(2.0,
                      lambda: _ba.screenmessage('Note: using local server',
                                                (1, 1, 0),
                                                log=True),
                      timetype=TimeType.REAL)
        elif 'test' in server_addr:
            _ba.timer(
                2.0,
                lambda: _ba.screenmessage('Note: using test server-module',
                                          (1, 1, 0),
                                          log=True),
                timetype=TimeType.REAL)

        cfg['launchCount'] = launch_count
        cfg.commit()

        # Run a test in a few seconds to see if we should pop up an existing
        # pending special offer.
        def check_special_offer() -> None:
            from bastd.ui import specialoffer
            config = self.config
            if ('pendingSpecialOffer' in config and _ba.get_public_login_id()
                    == config['pendingSpecialOffer']['a']):
                self.special_offer = config['pendingSpecialOffer']['o']
                specialoffer.show_offer()

        if not self.headless_build:
            _ba.timer(3.0, check_special_offer, timetype=TimeType.REAL)

        # Start scanning for things exposed via ba_meta.
        _meta.start_scan()

        # Auto-sign-in to a local account in a moment if we're set to.
        def do_auto_sign_in() -> None:
            if self.headless_build:
                _ba.sign_in('Local')
            elif cfg.get('Auto Account State') == 'Local':
                _ba.sign_in('Local')

        _ba.pushcall(do_auto_sign_in)

        self.ran_on_app_launch = True
コード例 #28
0
    def run(self) -> None:
        # pylint: disable=too-many-branches, consider-using-with
        import urllib.request
        import urllib.error
        import json

        from efro.net import is_urllib_network_error
        from ba import _general
        try:
            self._data = _general.utf8_all(self._data)
            _ba.set_thread_name('BA_ServerCallThread')
            parse = urllib.parse
            if self._request_type == 'get':
                response = urllib.request.urlopen(
                    urllib.request.Request(
                        (_ba.get_master_server_address() + '/' +
                         self._request + '?' + parse.urlencode(self._data)),
                        None, {'User-Agent': _ba.app.user_agent_string}),
                    timeout=DEFAULT_REQUEST_TIMEOUT_SECONDS)
            elif self._request_type == 'post':
                response = urllib.request.urlopen(
                    urllib.request.Request(
                        _ba.get_master_server_address() + '/' + self._request,
                        parse.urlencode(self._data).encode(),
                        {'User-Agent': _ba.app.user_agent_string}),
                    timeout=DEFAULT_REQUEST_TIMEOUT_SECONDS)
            else:
                raise TypeError('Invalid request_type: ' + self._request_type)

            # If html request failed.
            if response.getcode() != 200:
                response_data = None
            elif self._response_type == MasterServerResponseType.JSON:
                raw_data = response.read()

                # Empty string here means something failed server side.
                if raw_data == b'':
                    response_data = None
                else:
                    response_data = json.loads(raw_data)
            else:
                raise TypeError(f'invalid responsetype: {self._response_type}')

        except Exception as exc:
            do_print = False
            response_data = None

            # Ignore common network errors; note unexpected ones.
            if is_urllib_network_error(exc):
                pass
            elif (self._response_type == MasterServerResponseType.JSON
                  and isinstance(exc, json.decoder.JSONDecodeError)):
                # FIXME: should handle this better; could mean either the
                # server sent us bad data or it got corrupted along the way.
                pass
            else:
                do_print = True

            if do_print:
                # Any other error here is unexpected,
                # so let's make a note of it,
                print(f'Error in MasterServerCallThread'
                      f' (response-type={self._response_type},'
                      f' response-data={response_data}):')
                import traceback
                traceback.print_exc()

        if self._callback is not None:
            _ba.pushcall(_general.Call(self._run_callback, response_data),
                         from_other_thread=True)
コード例 #29
0
    def on_app_launch(self) -> None:
        """Runs after the app finishes bootstrapping.

        (internal)"""
        # pylint: disable=too-many-locals
        # pylint: disable=cyclic-import
        # pylint: disable=too-many-statements
        from ba import _apputils
        from ba import _appconfig
        from ba import _achievement
        from ba import _map
        from ba import _meta
        from ba import _campaign
        from bastd import appdelegate
        from bastd import maps as stdmaps
        from bastd.actor import spazappearance
        from ba._enums import TimeType

        cfg = self.config

        self.delegate = appdelegate.AppDelegate()

        self.ui.on_app_launch()

        _achievement.init_achievements()
        spazappearance.register_appearances()
        _campaign.init_campaigns()

        # FIXME: This should not be hard-coded.
        for maptype in [
                stdmaps.HockeyStadium, stdmaps.FootballStadium,
                stdmaps.Bridgit, stdmaps.BigG, stdmaps.Roundabout,
                stdmaps.MonkeyFace, stdmaps.ZigZag, stdmaps.ThePad,
                stdmaps.DoomShroom, stdmaps.LakeFrigid, stdmaps.TipTop,
                stdmaps.CragCastle, stdmaps.TowerD, stdmaps.HappyThoughts,
                stdmaps.StepRightUp, stdmaps.Courtyard, stdmaps.Rampage
        ]:
            _map.register_map(maptype)

        # Non-test, non-debug builds should generally be blessed; warn if not.
        # (so I don't accidentally release a build that can't play tourneys)
        if (not self.debug_build and not self.test_build
                and not _ba.is_blessed()):
            _ba.screenmessage('WARNING: NON-BLESSED BUILD', color=(1, 0, 0))

        # If there's a leftover log file, attempt to upload it to the
        # master-server and/or get rid of it.
        _apputils.handle_leftover_log_file()

        # Only do this stuff if our config file is healthy so we don't
        # overwrite a broken one or whatnot and wipe out data.
        if not self.config_file_healthy:
            if self.platform in ('mac', 'linux', 'windows'):
                from bastd.ui import configerror
                configerror.ConfigErrorWindow()
                return

            # For now on other systems we just overwrite the bum config.
            # At this point settings are already set; lets just commit them
            # to disk.
            _appconfig.commit_app_config(force=True)

        self.music.on_app_launch()

        launch_count = cfg.get('launchCount', 0)
        launch_count += 1

        # So we know how many times we've run the game at various
        # version milestones.
        for key in ('lc14173', 'lc14292'):
            cfg.setdefault(key, launch_count)

        # Debugging - make note if we're using the local test server so we
        # don't accidentally leave it on in a release.
        # FIXME - should move this to the native layer.
        server_addr = _ba.get_master_server_address()
        if 'localhost' in server_addr:
            _ba.timer(2.0,
                      lambda: _ba.screenmessage('Note: using local server',
                                                (1, 1, 0),
                                                log=True),
                      timetype=TimeType.REAL)
        elif 'test' in server_addr:
            _ba.timer(
                2.0,
                lambda: _ba.screenmessage('Note: using test server-module',
                                          (1, 1, 0),
                                          log=True),
                timetype=TimeType.REAL)

        cfg['launchCount'] = launch_count
        cfg.commit()

        # Run a test in a few seconds to see if we should pop up an existing
        # pending special offer.
        def check_special_offer() -> None:
            from bastd.ui import specialoffer
            config = self.config
            if ('pendingSpecialOffer' in config and _ba.get_public_login_id()
                    == config['pendingSpecialOffer']['a']):
                self.special_offer = config['pendingSpecialOffer']['o']
                specialoffer.show_offer()

        if not self.headless_build:
            _ba.timer(3.0, check_special_offer, timetype=TimeType.REAL)

        # Start scanning for things exposed via ba_meta.
        _meta.start_scan()

        # Auto-sign-in to a local account in a moment if we're set to.
        def do_auto_sign_in() -> None:
            if self.headless_build or cfg.get('Auto Account State') == 'Local':
                _ba.sign_in('Local')

        _ba.pushcall(do_auto_sign_in)

        # Load up our plugins and go ahead and call their on_app_launch calls.
        self.load_plugins()
        for plugin in self.active_plugins.values():
            try:
                plugin.on_app_launch()
            except Exception:
                from ba import _error
                _error.print_exception('Error in plugin on_app_launch()')

        self.ran_on_app_launch = True
コード例 #30
0
ファイル: _apputils.py プロジェクト: KoLenka12/ballistica
def call_after_ad(call: Callable[[], Any]) -> None:
    """Run a call after potentially showing an ad."""
    # pylint: disable=too-many-statements
    # pylint: disable=too-many-branches
    # pylint: disable=too-many-locals
    from ba._account import have_pro
    from ba._enums import TimeType
    import time
    app = _ba.app
    show = True

    # No ads without net-connections, etc.
    if not _ba.can_show_ad():
        show = False
    if have_pro():
        show = False  # Pro disables interstitials.
    try:
        session = _ba.get_foreground_host_session()
        assert session is not None
        is_tournament = session.tournament_id is not None
    except Exception:
        is_tournament = False
    if is_tournament:
        show = False  # Never show ads during tournaments.

    if show:
        interval: Optional[float]
        launch_count = app.config.get('launchCount', 0)

        # If we're seeing short ads we may want to space them differently.
        interval_mult = (_ba.get_account_misc_read_val(
            'ads.shortIntervalMult', 1.0) if app.last_ad_was_short else 1.0)
        if app.ad_amt is None:
            if launch_count <= 1:
                app.ad_amt = _ba.get_account_misc_read_val(
                    'ads.startVal1', 0.99)
            else:
                app.ad_amt = _ba.get_account_misc_read_val(
                    'ads.startVal2', 1.0)
            interval = None
        else:
            # So far we're cleared to show; now calc our ad-show-threshold and
            # see if we should *actually* show (we reach our threshold faster
            # the longer we've been playing).
            base = 'ads' if _ba.has_video_ads() else 'ads2'
            min_lc = _ba.get_account_misc_read_val(base + '.minLC', 0.0)
            max_lc = _ba.get_account_misc_read_val(base + '.maxLC', 5.0)
            min_lc_scale = (_ba.get_account_misc_read_val(
                base + '.minLCScale', 0.25))
            max_lc_scale = (_ba.get_account_misc_read_val(
                base + '.maxLCScale', 0.34))
            min_lc_interval = (_ba.get_account_misc_read_val(
                base + '.minLCInterval', 360))
            max_lc_interval = (_ba.get_account_misc_read_val(
                base + '.maxLCInterval', 300))
            if launch_count < min_lc:
                lc_amt = 0.0
            elif launch_count > max_lc:
                lc_amt = 1.0
            else:
                lc_amt = ((float(launch_count) - min_lc) / (max_lc - min_lc))
            incr = (1.0 - lc_amt) * min_lc_scale + lc_amt * max_lc_scale
            interval = ((1.0 - lc_amt) * min_lc_interval +
                        lc_amt * max_lc_interval)
            app.ad_amt += incr
        assert app.ad_amt is not None
        if app.ad_amt >= 1.0:
            app.ad_amt = app.ad_amt % 1.0
            app.attempted_first_ad = True

        # After we've reached the traditional show-threshold once,
        # try again whenever its been INTERVAL since our last successful show.
        elif (app.attempted_first_ad
              and (app.last_ad_completion_time is None or
                   (interval is not None
                    and _ba.time(TimeType.REAL) - app.last_ad_completion_time >
                    (interval * interval_mult)))):
            # Reset our other counter too in this case.
            app.ad_amt = 0.0
        else:
            show = False

    # If we're *still* cleared to show, actually tell the system to show.
    if show:
        # As a safety-check, set up an object that will run
        # the completion callback if we've returned and sat for 10 seconds
        # (in case some random ad network doesn't properly deliver its
        # completion callback).
        class _Payload:
            def __init__(self, pcall: Callable[[], Any]):
                self._call = pcall
                self._ran = False

            def run(self, fallback: bool = False) -> None:
                """Run the fallback call (and issues a warning about it)."""
                if not self._ran:
                    if fallback:
                        print((
                            'ERROR: relying on fallback ad-callback! '
                            'last network: ' + app.last_ad_network + ' (set ' +
                            str(int(time.time() -
                                    app.last_ad_network_set_time)) +
                            's ago); purpose=' + app.last_ad_purpose))
                    _ba.pushcall(self._call)
                    self._ran = True

        payload = _Payload(call)
        with _ba.Context('ui'):
            _ba.timer(5.0,
                      lambda: payload.run(fallback=True),
                      timetype=TimeType.REAL)
        show_ad('between_game', on_completion_call=payload.run)
    else:
        _ba.pushcall(call)  # Just run the callback without the ad.