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)
def _on_more_press(self) -> None: our_login_id = _ba.get_public_login_id() # our_login_id = _bs.get_account_misc_read_val_2( # 'resolvedAccountID', None) if not self._can_do_more_button or our_login_id is None: ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr(resource='unavailableText'), color=(1, 0, 0)) return if self._season is None: season_str = '' else: season_str = ( '&season=' + ('all_time' if self._season == 'a' else self._season)) if self._league_url_arg != '': league_str = '&league=' + self._league_url_arg else: league_str = '' ba.open_url(_ba.get_master_server_address() + '/highscores?list=powerRankings&v=2' + league_str + season_str + '&player=' + our_login_id)
def on_app_launch(self) -> None: """Runs after the app finishes bootstrapping. (internal)""" # pylint: disable=too-many-locals # pylint: disable=cyclic-import from ba import _apputils from ba import _appconfig from ba import _achievement from ba import _map 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() 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.specialoffer import show_offer config = self.config if ('pendingSpecialOffer' in config and _ba.get_public_login_id() == config['pendingSpecialOffer']['a']): self.special_offer = config['pendingSpecialOffer']['o'] show_offer() if not self.headless_mode: _ba.timer(3.0, check_special_offer, timetype=TimeType.REAL) self.meta.on_app_launch() self.accounts.on_app_launch() self.plugins.on_app_launch() self.state = self.State.RUNNING
def _on_more_press(self) -> None: ba.open_url(_ba.get_master_server_address() + '/highscores?profile=' + self._account_id)
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)
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
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)
def _run_diagnostics(weakwin: weakref.ref[NetTestingWindow]) -> None: # pylint: disable=too-many-statements from efro.util import utc_now have_error = [False] # We're running in a background thread but UI stuff needs to run # in the logic thread; give ourself a way to pass stuff to it. def _print(text: str, color: tuple[float, float, float] = None) -> None: def _print_in_logic_thread() -> None: win = weakwin() if win is not None: win.print(text, (1.0, 1.0, 1.0) if color is None else color) ba.pushcall(_print_in_logic_thread, from_other_thread=True) def _print_test_results(call: Callable[[], Any]) -> None: """Run the provided call; return success/fail text & color.""" starttime = time.monotonic() try: call() duration = time.monotonic() - starttime _print(f'Succeeded in {duration:.2f}s.', color=(0, 1, 0)) except Exception: import traceback duration = time.monotonic() - starttime _print(traceback.format_exc(), color=(1.0, 1.0, 0.3)) _print(f'Failed in {duration:.2f}s.', color=(1, 0, 0)) have_error[0] = True try: _print(f'Running network diagnostics...\n' f'ua: {_ba.app.user_agent_string}\n' f'time: {utc_now()}.') if bool(False): _print('\nRunning dummy success test...') _print_test_results(_dummy_success) _print('\nRunning dummy fail test...') _print_test_results(_dummy_fail) # V1 ping baseaddr = _ba.get_master_server_address(source=0, version=1) _print(f'\nContacting V1 master-server src0 ({baseaddr})...') _print_test_results(lambda: _test_fetch(baseaddr)) # V1 alternate ping baseaddr = _ba.get_master_server_address(source=1, version=1) _print(f'\nContacting V1 master-server src1 ({baseaddr})...') _print_test_results(lambda: _test_fetch(baseaddr)) _print(f'\nV1-test-log: {ba.app.net.v1_test_log}') for srcid, result in sorted(ba.app.net.v1_ctest_results.items()): _print(f'\nV1 src{srcid} result: {result}') curv1addr = _ba.get_master_server_address(version=1) _print(f'\nUsing V1 address: {curv1addr}') _print('\nRunning V1 transaction...') _print_test_results(_test_v1_transaction) # V2 ping baseaddr = _ba.get_master_server_address(version=2) _print(f'\nContacting V2 master-server ({baseaddr})...') _print_test_results(lambda: _test_fetch(baseaddr)) # Get V2 nearby zone with ba.app.net.zone_pings_lock: zone_pings = copy.deepcopy(ba.app.net.zone_pings) nearest_zone = (None if not zone_pings else sorted( zone_pings.items(), key=lambda i: i[1])[0]) if nearest_zone is not None: nearstr = f'{nearest_zone[0]}: {nearest_zone[1]:.0f}ms' else: nearstr = '-' _print(f'\nChecking nearest V2 zone ping ({nearstr})...') _print_test_results(lambda: _test_nearby_zone_ping(nearest_zone)) if have_error[0]: _print('\nDiagnostics complete. Some diagnostics failed.', color=(10, 0, 0)) else: _print('\nDiagnostics complete. Everything looks good!', color=(0, 1, 0)) except Exception: import traceback _print( f'An unexpected error occurred during testing;' f' please report this.\n' f'{traceback.format_exc()}', color=(1, 0, 0))
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 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, UIScale 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)) # 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_FORCE_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)) # 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) self.ran_on_app_launch = True