diff --git a/.efrocachemap b/.efrocachemap index 896bcf30..a4b58656 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -4064,54 +4064,54 @@ "build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1", "build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae", "build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599", - "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "2ce43921f1917e4face2bd89de255c89", - "build/prefab/full/linux_arm64_gui/release/ballisticakit": "cfad36d58003989bd3fb758d72900914", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "11a8f7317dafd295f43e5b8573b21caa", - "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "dca6448b3d8f3acd46632f103d270b38", - "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "dcf48e1ee93b7a747b0c33efca58057a", - "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "b568da36b6a2ee329e31ff321df54810", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "b9e6b511c5fea84b0a81335fd3000592", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "ec94eec1e4c1084535c65e9923de87d6", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "d53c169951686dd96a85668def6dbbfc", - "build/prefab/full/mac_arm64_gui/release/ballisticakit": "b46e409cb87de94f6cf6f7d3e10e032f", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "4692a22e7ddaf15e34157325252b5fb3", - "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "d0ab73deaa89dd9fa1b75c65d463d293", - "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "3db746bb01e002fee667c70c6f57f928", - "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "10c06bd2c02a9b2ac488993e2ffc6700", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "01a466465933b84f3772e9d274ee9c40", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "3d3233606e03eb3b8ffe04169a5f2fce", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "c59aeaf8686b4008d0b11728a0523397", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "92dcffd261cfe8bf80806ebc765bb323", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "57639e6862d409cf39ad2fd609aafb20", - "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "fa7a86c50be523dcb7ff6aadb9d79d0e", - "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "d16860bc4d3ff0abae745e2fd5638149", - "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "620ed03a89ad8053656a466da6053676", - "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "d16860bc4d3ff0abae745e2fd5638149", - "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "620ed03a89ad8053656a466da6053676", - "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "642d380b38d5fd4a5f50a331579e6e9c", - "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "a767b4b9d20e61e0a6fb7289660959e9", - "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "642d380b38d5fd4a5f50a331579e6e9c", - "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "a767b4b9d20e61e0a6fb7289660959e9", - "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "fecd29b4aff2924b2403f36996cdca93", - "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "9aea5093bb559d64e28edc3c112b06b2", - "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "fecd29b4aff2924b2403f36996cdca93", - "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "9aea5093bb559d64e28edc3c112b06b2", - "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "8ce1ea0d1d64dac282100599159ad2a3", - "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "adf78a2ab5fb271d83e80fb72b68784f", - "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "864807f34140eed4dada8832878a1173", - "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "adf78a2ab5fb271d83e80fb72b68784f", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "8d271eb87a692db53219288d762a5194", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "3a2e88e7788e47cc86ac87df0ea46ce3", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "d0db9d64f7ff618cb6a62f59a70a00f0", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "308f59dbc2f3193b1adb321db9a09ff1", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "d66fe8cbfcd4d472ff3536c274f2a8d4", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "89d7812f28754a00ad797bb994c81846", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "ea8906f5c66ba5606bff77e8e57f3ade", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "d117fce0b21244310ce6771a29c237be", + "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "bb5e0df17efe96476c3c2b8c41c7979b", + "build/prefab/full/linux_arm64_gui/release/ballisticakit": "056115be35ac1afc1f62b58dcc8f217a", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "cf2b052caaa06d512ef379687b3aff86", + "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "97b8d5f261f84b8047d43df1ca81777a", + "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "f304ee46675e7552c31e174d81199307", + "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "70b54361b557f5e8d4271a0c976b28b6", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "06c778dc4c2772acf6dbaf67eb7321c9", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "212a5c7206c1aa9a8427b61471e9e89b", + "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "f3a1028602c7bbd3f8d62bd843af628d", + "build/prefab/full/mac_arm64_gui/release/ballisticakit": "9c4c6d1c50e225dc61cfbab4a82a37a6", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "4db4b600478f1767fdd0462a137912de", + "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "628bc102cf6ef17b38804c4c9baa5265", + "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "fb9d165ab24084e8beaa6d7c51c81a77", + "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "ae88283e96a9238aab7561d2afcd9a5f", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "e5fe958377b8dcf5d5203dbd17aaab72", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "f22e8af184b19b4929162e901a056454", + "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "5fa2cb24b9e78bddb1bf9efb197d0c51", + "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "d36f11acfa5e68f303d347c3895979d0", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "570a7e325c15ecebcc419d48a046dd24", + "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "3155f665993e5f850db5b87c9296abe7", + "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "d1bfae5e75824ba89338892bc0f84c6b", + "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "b1466048e319c0d60139c46751f3eb79", + "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "d1bfae5e75824ba89338892bc0f84c6b", + "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "b1466048e319c0d60139c46751f3eb79", + "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "656176631037184b6e22b0b68c3cd1fa", + "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "12b633db4dad37bbb5a5c398db0c10dd", + "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "656176631037184b6e22b0b68c3cd1fa", + "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "12b633db4dad37bbb5a5c398db0c10dd", + "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "7e014214c6cb9ddaa0e95f5186ba9df6", + "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "45ddc559dd5ef563d2df5c5db9c9fbc0", + "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "7e014214c6cb9ddaa0e95f5186ba9df6", + "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "45ddc559dd5ef563d2df5c5db9c9fbc0", + "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "1a15bf7d809addab4992827da9d89895", + "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "a1e03b7d13482beab8852691b5698974", + "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "807e3629d9d4611cd93feb87face4e51", + "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "a1e03b7d13482beab8852691b5698974", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "7f7bc04993982b164f6e86ad6ce350ef", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "d24102dd35c29b6a77cdf3d9921695da", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "8edef08a22594617d2b4273e0e4cba40", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "cb6c0c6efad034b53fe1a8f930f2ce81", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "3ec4aadf128132116fc5479a46bd1f71", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "14c2cf0812e3022391caffd9409d0650", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "67c207425afc5023cea9740e3bd459c3", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "7219b9034f14c5b769818b80135ea61b", "src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/enums.py": "f8cd3af311ac63147882590123b78318", - "src/ballistica/base/mgen/pyembed/binding_base.inc": "ad5475b871dadc69bc2cf8ccac550878", - "src/ballistica/base/mgen/pyembed/binding_base_app.inc": "ef931ec35861d4ad37850b7278588922", + "src/ballistica/base/mgen/pyembed/binding_base.inc": "ad347097a38e0d7ede9eb6dec6a80ee9", + "src/ballistica/base/mgen/pyembed/binding_base_app.inc": "077bf63282b23d1880287200c9f6035f", "src/ballistica/classic/mgen/pyembed/binding_classic.inc": "3ceb412513963f0818ab39c58bf292e3", "src/ballistica/core/mgen/pyembed/binding_core.inc": "9d0a3c9636138e35284923e0c8311c69", "src/ballistica/core/mgen/pyembed/env.inc": "8be46e5818f360d10b7b0224a9e91d07", diff --git a/CHANGELOG.md b/CHANGELOG.md index 66b44dcf..4565e808 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### 1.7.26 (build 21250, api 8, 2023-08-23) +### 1.7.26 (build 21256, api 8, 2023-08-25) - Android should now be better at detecting hardware keyboards (you will see 'Configure Keyboard' and 'Configure Keyboard P2' buttons under @@ -24,14 +24,19 @@ functionality, but rather adapt the app to a particular paradigm or api (VR, Headless, SDL GUI, etc.). Also am trying to move any functionality out of those classes that does not fit that definition. -- Started cleaning up the app exit process. This will allow the app to +- Started cleaning up the app shutdown process. This will allow the app to gracefully run tasks such as syncing account data to the cloud or disk or properly closing the audio system when shutting down. It also means there should be more consistent use of the 'Quit?' confirm window. Please holler if you see any odd behavior when trying to quit the app. +- Unix TERM signal now triggers graceful app shutdown. - Added `ba.app.add_shutdown_task()` to register coroutines to be run as part of shutdown. - Removed `babase.app.iircade_mode`. RIP iiRcade :(. +- Changed `AppState.INITIAL` to `AppState.NOT_RUNNING`, added a + `AppState.NATIVE_BOOTSTRAPPING`, and changed `AppState.LAUNCHING` to + `AppState.INITING`. These better describe what the app is actually doing while + in those states. ### 1.7.25 (build 21211, api 8, 2023-08-03) diff --git a/src/assets/ba_data/python/babase/_accountv2.py b/src/assets/ba_data/python/babase/_accountv2.py index e0efc839..89be37fd 100644 --- a/src/assets/ba_data/python/babase/_accountv2.py +++ b/src/assets/ba_data/python/babase/_accountv2.py @@ -128,7 +128,7 @@ class AccountV2Subsystem: # Ok; no workspace to worry about; carry on. if not self._initial_sign_in_completed: self._initial_sign_in_completed = True - _babase.app.on_initial_sign_in_completed() + _babase.app.on_initial_sign_in_complete() def on_active_logins_changed(self, logins: dict[LoginType, str]) -> None: """Should be called when logins for the active account change.""" @@ -163,7 +163,7 @@ class AccountV2Subsystem: """ if not self._initial_sign_in_completed: self._initial_sign_in_completed = True - _babase.app.on_initial_sign_in_completed() + _babase.app.on_initial_sign_in_complete() @staticmethod def _hashstr(val: str) -> str: @@ -409,7 +409,7 @@ class AccountV2Subsystem: def _on_set_active_workspace_completed(self) -> None: if not self._initial_sign_in_completed: self._initial_sign_in_completed = True - _babase.app.on_initial_sign_in_completed() + _babase.app.on_initial_sign_in_complete() class AccountV2Handle: diff --git a/src/assets/ba_data/python/babase/_app.py b/src/assets/ba_data/python/babase/_app.py index 81754925..feb4ea01 100644 --- a/src/assets/ba_data/python/babase/_app.py +++ b/src/assets/ba_data/python/babase/_app.py @@ -4,8 +4,8 @@ from __future__ import annotations import os -from enum import Enum import logging +from enum import Enum from typing import TYPE_CHECKING from concurrent.futures import ThreadPoolExecutor from functools import cached_property @@ -58,31 +58,41 @@ class App: health_monitor: AppHealthMonitor + # How long we allow shutdown tasks to run before killing them. + # Currently the entire app hard-exits if shutdown takes 10 seconds, + # so we need to keep it under that. + SHUTDOWN_TASK_TIMEOUT_SECONDS = 5 + class State(Enum): """High level state the app can be in.""" - # Waiting on the native layer to finish spinning up; our launch - # process here has not yet started. - BOOTSTRAPPING = 0 + # The app has not yet begun starting and should not be used in + # any way. + NOT_RUNNING = 'not_running' - # Our app subsystems are being inited but should not yet - # interact. - LAUNCHING = 1 + # The native layer is spinning up its machinery (screens, + # renderers, etc.). Nothing should happen in the Python layer + # until this completes. + NATIVE_BOOTSTRAPPING = 'native_bootstrapping' - # App subsystems are inited and interacting, but the app has not - # yet embarked on a high level course of action. It is doing - # initial account logins, workspace & asset downloads, etc. in - # order to prepare for this. - LOADING = 2 + # Python app subsystems are being inited but should not yet + # interact or do any work. + INITING = 'initing' + + # Python app subsystems are inited and interacting, but the app + # has not yet embarked on a high level course of action. It is + # doing initial account logins, workspace & asset downloads, + # etc. + LOADING = 'loading' # All pieces are in place and the app is now doing its thing. - RUNNING = 3 + RUNNING = 'running' # The app is backgrounded or otherwise suspended. - PAUSED = 4 + PAUSED = 'paused' # The app is shutting down. - SHUTTING_DOWN = 5 + SHUTTING_DOWN = 'shutting_down' @property def aioloop(self) -> asyncio.AbstractEventLoop: @@ -236,19 +246,20 @@ class App: if os.environ.get('BA_RUNNING_WITH_DUMMY_MODULES') == '1': return - self.state = self.State.BOOTSTRAPPING + self.state = self.State.NOT_RUNNING self._subsystems: list[AppSubsystem] = [] - self._native_bootstrapped = False + self._native_bootstrapping_completed = False + self._init_completed = False + self._meta_scan_completed = False + self._native_start_called = False self._native_paused = False self._native_shutdown_called = False - self._launch_completed = False self._initial_sign_in_completed = False - self._meta_scan_completed = False - self._called_on_app_launching = False - self._called_on_app_loading = False - self._called_on_app_running = False + self._called_on_initing = False + self._called_on_loading = False + self._called_on_running = False self._subsystem_registration_ended = False self._pending_apply_app_config = False @@ -484,13 +495,13 @@ class App: def run(self) -> None: """Run the app to completion. - Note that this only works on platforms where ballistica + Note that this only works on platforms where Ballistica manages its own event loop. """ _babase.run_app() - def _on_app_launching(self) -> None: - """Called when the app enters the launching state. + def _on_initing(self) -> None: + """Called when the app enters the initing state. Here we can put together subsystems and other pieces for the app, but most things should not be doing any work yet. @@ -503,7 +514,7 @@ class App: assert _babase.in_logic_thread() - _env.on_app_launching() + _env.on_app_state_initing() self._aioloop = _asyncio.setup_asyncio() self.health_monitor = AppHealthMonitor() @@ -531,28 +542,25 @@ class App: # __FEATURESET_APP_SUBSYSTEM_CREATE_END__ - self._launch_completed = True + # We're a pretty short-lived state. This should flip us to + # 'loading'. + self._init_completed = True self._update_state() - def _on_app_loading(self) -> None: - """Called when the app enters the loading state. + def _on_loading(self) -> None: + """Called when we enter the loading state. At this point, all built-in pieces of the app should be in place - and can start doing 'work'. Though at a high level, the goal of - the app at this point is only to sign in to initial accounts, - download workspaces, and otherwise prepare itself to really - 'run'. + and can start talking to each other and doing work. Though at a + high level, the goal of the app at this point is only to sign in + to initial accounts, download workspaces, and otherwise prepare + itself to really 'run'. """ - from babase._apputils import log_dumped_app_state - assert _babase.in_logic_thread() # Get meta-system scanning built-in stuff in the bg. self.meta.start_scan(scan_complete_cb=self._on_meta_scan_complete) - # If any traceback dumps happened last run, log and clear them. - log_dumped_app_state() - # Inform all app subsystems in the same order they were inited. # Operate on a copy here because subsystems can still be added # at this point. @@ -568,7 +576,7 @@ class App: # is not present, however, we just do it ourself so we can # proceed on to the running state. if self.plus is None: - _babase.pushcall(self.on_initial_sign_in_completed) + _babase.pushcall(self.on_initial_sign_in_complete) def _on_meta_scan_complete(self) -> None: """Called when meta-scan is done doing its thing.""" @@ -581,8 +589,8 @@ class App: self._meta_scan_completed = True self._update_state() - def _on_app_running(self) -> None: - """Called when the app enters the running state. + def _on_running(self) -> None: + """Called when we enter the running state. At this point, all workspaces, initial accounts, etc. are in place and we can actually get started doing whatever we're gonna do. @@ -704,51 +712,61 @@ class App: # pylint: disable=too-many-branches assert _babase.in_logic_thread() - # Can't shut down until we've got our asyncio machinery spun up. - if self._native_shutdown_called and self._aioloop is not None: + # Shutdown trumps all. Though we can't shut down until init is + # completed since we need our asyncio stuff to exist for the + # shutdown process. + if self._native_shutdown_called and self._init_completed: # Entering shutdown state: if self.state is not self.State.SHUTTING_DOWN: self.state = self.State.SHUTTING_DOWN - self._on_app_shutdown() + self._on_shutting_down() elif self._native_paused: # Entering paused state: if self.state is not self.State.PAUSED: self.state = self.State.PAUSED - self._on_app_pause() + self._on_pause() else: # Leaving paused state: if self.state is self.State.PAUSED: - self._on_app_resume() + self._on_resume() # Handle initially entering or returning to other states. if self._initial_sign_in_completed and self._meta_scan_completed: if self.state != self.State.RUNNING: self.state = self.State.RUNNING _babase.lifecyclelog('app state running') - if not self._called_on_app_running: - self._called_on_app_running = True - self._on_app_running() - elif self._launch_completed: + if not self._called_on_running: + self._called_on_running = True + self._on_running() + elif self._init_completed: if self.state is not self.State.LOADING: self.state = self.State.LOADING _babase.lifecyclelog('app state loading') - if not self._called_on_app_loading: - self._called_on_app_loading = True - self._on_app_loading() + if not self._called_on_loading: + self._called_on_loading = True + self._on_loading() + elif self._native_bootstrapping_completed: + if self.state is not self.State.INITING: + self.state = self.State.INITING + _babase.lifecyclelog('app state initing') + if not self._called_on_initing: + self._called_on_initing = True + self._on_initing() else: - # Only thing left is launching. We shouldn't be getting - # called before at least that is complete. - assert self._native_bootstrapped - if self.state is not self.State.LAUNCHING: - self.state = self.State.LAUNCHING - _babase.lifecyclelog('app state launching') - if not self._called_on_app_launching: - self._called_on_app_launching = True - self._on_app_launching() + # Only possibility left is app-start. We shouldn't be + # getting called before at least that happens. + assert self._native_start_called + assert self.state is self.State.NOT_RUNNING + if bool(True): + self.state = self.State.NATIVE_BOOTSTRAPPING def add_shutdown_task(self, coro: Coroutine[None, None, None]) -> None: - """Add a task to be run on app shutdown.""" + """Add a task to be run on app shutdown. + + Note that tasks will be killed after + App.SHUTDOWN_TASK_TIMEOUT_SECONDS if they are still running. + """ if self.state is self.State.SHUTTING_DOWN: raise RuntimeError( 'Cannot add shutdown tasks with state SHUTTING_DOWN.' @@ -780,15 +798,22 @@ class App: task = asyncio.create_task(coro) try: - await asyncio.wait_for(task, 5.0) + await asyncio.wait_for(task, self.SHUTDOWN_TASK_TIMEOUT_SECONDS) except Exception: logging.exception('Error in shutdown task.') - def on_native_bootstrapped(self) -> None: + def on_native_start(self) -> None: + """Called by the native layer when the app is being started.""" + assert _babase.in_logic_thread() + assert not self._native_start_called + self._native_start_called = True + self._update_state() + + def on_native_bootstrapping_complete(self) -> None: """Called by the native layer once its ready to rock.""" assert _babase.in_logic_thread() - assert not self._native_bootstrapped - self._native_bootstrapped = True + assert not self._native_bootstrapping_completed + self._native_bootstrapping_completed = True self._update_state() def on_native_pause(self) -> None: @@ -811,7 +836,7 @@ class App: self._native_shutdown_called = True self._update_state() - def _on_app_pause(self) -> None: + def _on_pause(self) -> None: """Called when the app goes to a paused state.""" assert _babase.in_logic_thread() @@ -824,7 +849,7 @@ class App: 'Error in on_app_pause for subsystem %s.', subsystem ) - def _on_app_resume(self) -> None: + def _on_resume(self) -> None: """Called when resuming.""" assert _babase.in_logic_thread() self.fg_state += 1 @@ -838,7 +863,7 @@ class App: 'Error in on_app_resume for subsystem %s.', subsystem ) - def _on_app_shutdown(self) -> None: + def _on_shutting_down(self) -> None: """(internal)""" assert _babase.in_logic_thread() @@ -882,7 +907,7 @@ class App: except ImportError: pass - def on_initial_sign_in_completed(self) -> None: + def on_initial_sign_in_complete(self) -> None: """Called when initial sign-in (or lack thereof) completes. This normally gets called by the plus subsystem. The diff --git a/src/assets/ba_data/python/babase/_apputils.py b/src/assets/ba_data/python/babase/_apputils.py index 67aa9cf0..70fc4e32 100644 --- a/src/assets/ba_data/python/babase/_apputils.py +++ b/src/assets/ba_data/python/babase/_apputils.py @@ -378,6 +378,10 @@ class AppHealthMonitor(AppSubsystem): self._response = False self._first_check = True + def on_app_loading(self) -> None: + # If any traceback dumps happened last run, log and clear them. + log_dumped_app_state() + def _app_monitor_thread_main(self) -> None: try: self._monitor_app() diff --git a/src/assets/ba_data/python/babase/_env.py b/src/assets/ba_data/python/babase/_env.py index fd426fcd..4ee0b12c 100644 --- a/src/assets/ba_data/python/babase/_env.py +++ b/src/assets/ba_data/python/babase/_env.py @@ -149,14 +149,15 @@ def on_main_thread_start_app() -> None: asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) -def on_app_launching() -> None: - """Called when the app reaches the launching state.""" +def on_app_state_initing() -> None: + """Called when the app reaches the initing state.""" import _babase import baenv assert _babase.in_logic_thread() - # Let the user know if the app Python dir is a 'user' one. + # Let the user know if the app Python dir is a 'user' one. This is a + # risky thing to be doing so don't let them forget they're doing it. envconfig = baenv.get_config() if envconfig.is_user_app_python_dir: _babase.screenmessage( @@ -192,12 +193,13 @@ def _feed_logs_to_babase(log_handler: LogHandler) -> None: # cache. This will feed the engine any logs that happened between # baenv.configure() and now. - # FIXME: while this works for now, the downside is that these + # FIXME: while this setup works for now, the downside is that these # callbacks fire in a bg thread so certain things like android - # logging will be delayed compared to code that uses native logging - # calls directly. Perhaps we should add some sort of 'immediate' - # callback option to better handle such cases (similar to the - # immediate echofile stderr print that already occurs). + # logging will be delayed relative to code that uses native logging + # calls directly. Ideally we should add some sort of 'immediate' + # callback option to better handle such cases (analogous to the + # immediate echofile stderr print that LogHandler already + # supports). log_handler.add_callback(_on_log, feed_existing_logs=True) diff --git a/src/assets/ba_data/python/babase/_plugin.py b/src/assets/ba_data/python/babase/_plugin.py index 3b3f07e1..23550900 100644 --- a/src/assets/ba_data/python/babase/_plugin.py +++ b/src/assets/ba_data/python/babase/_plugin.py @@ -81,7 +81,7 @@ class PluginSubsystem(AppSubsystem): config_changed = True found_new = True - # If we're *not* auto-enabling, just let the user know if we + # If we're *not* auto-enabling, simply let the user know if we # found new ones. if found_new and not auto_enable_new_plugins: _babase.screenmessage( @@ -131,10 +131,10 @@ class PluginSubsystem(AppSubsystem): disappeared_plugs.add(class_path) continue - # If plugins disappeared, let the user know gently and remove them - # from the config so we'll again let the user know if they later - # reappear. This makes it much smoother to switch between users - # or workspaces. + # If plugins disappeared, let the user know gently and remove + # them from the config so we'll again let the user know if they + # later reappear. This makes it much smoother to switch between + # users or workspaces. if disappeared_plugs: _babase.getsimplesound('shieldDown').play() _babase.screenmessage( @@ -217,7 +217,7 @@ class PluginSpec: key. Remember to commit the app-config after making any changes. The 'attempted_load' attr will be True if the engine has attempted - to load the plugin. If 'attempted_load' is True for a plugin-spec + to load the plugin. If 'attempted_load' is True for a PluginSpec but the 'plugin' attr is None, it means there was an error loading the plugin. If a plugin's api-version does not match the running app, if a new plugin is detected with auto-enable-plugins disabled, @@ -249,7 +249,7 @@ class PluginSpec: plugstate['enabled'] = val def attempt_load_if_enabled(self) -> Plugin | None: - """Possibly load the plugin and report errors.""" + """Possibly load the plugin and log any errors.""" from babase._general import getclass from babase._language import Lstr @@ -308,8 +308,8 @@ class Plugin: Category: **App Classes** Plugins are discoverable by the meta-tag system - and the user can select which ones they want to activate. - Active plugins are then called at specific times as the + and the user can select which ones they want to enable. + Enabled plugins are then called at specific times as the app is running in order to modify its behavior in some way. """ diff --git a/src/assets/ba_data/python/baclassic/_net.py b/src/assets/ba_data/python/baclassic/_net.py index d109acdc..3c899900 100644 --- a/src/assets/ba_data/python/baclassic/_net.py +++ b/src/assets/ba_data/python/baclassic/_net.py @@ -4,8 +4,9 @@ from __future__ import annotations import copy -import threading +import logging import weakref +import threading from enum import Enum from typing import TYPE_CHECKING @@ -36,7 +37,9 @@ class MasterServerV1CallThread(threading.Thread): callback: MasterServerCallback | None, response_type: MasterServerResponseType, ): - super().__init__() + # Set daemon=True so long-running requests don't keep us from + # quitting the app. + super().__init__(daemon=True) self._request = request self._request_type = request_type if not isinstance(response_type, MasterServerResponseType): @@ -76,6 +79,16 @@ class MasterServerV1CallThread(threading.Thread): from efro.error import is_urllib_communication_error + # If the app is going down, this is a no-op. Trying to avoid the + # rare odd crash I see from (presumably) SSL stuff getting used + # while the app is being torn down. + if babase.app.state is babase.app.State.SHUTTING_DOWN: + logging.warning( + 'MasterServerV1CallThread.run() during app' + ' shutdown is a no-op.' + ) + return + plus = babase.app.plus assert plus is not None response_data: Any = None diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py index 75c0b9f6..c806cbf9 100644 --- a/src/assets/ba_data/python/baenv.py +++ b/src/assets/ba_data/python/baenv.py @@ -52,7 +52,7 @@ if TYPE_CHECKING: # Build number and version of the ballistica binary we expect to be # using. -TARGET_BALLISTICA_BUILD = 21250 +TARGET_BALLISTICA_BUILD = 21256 TARGET_BALLISTICA_VERSION = '1.7.26' diff --git a/src/assets/ba_data/python/bauiv1lib/confirm.py b/src/assets/ba_data/python/bauiv1lib/confirm.py index 256d3b7b..dfa9a697 100644 --- a/src/assets/ba_data/python/bauiv1lib/confirm.py +++ b/src/assets/ba_data/python/bauiv1lib/confirm.py @@ -199,7 +199,11 @@ class QuitWindow: ) bui.lock_all_input() - # Unlock and fade back in shortly.. just in case something goes wrong - # (or on android where quit just backs out of our activity and - # we may come back) - bui.apptimer(0.3, bui.unlock_all_input) + # Unlock and fade back in shortly. Just in case something goes + # wrong (or on Android where quit just backs out of our activity + # and we may come back after). + def _come_back() -> None: + bui.unlock_all_input() + bui.fade_screen(True, time=0.1) + + bui.apptimer(0.3, _come_back) diff --git a/src/assets/ba_data/python/bauiv1lib/store/browser.py b/src/assets/ba_data/python/bauiv1lib/store/browser.py index 6d5489b2..43ce844a 100644 --- a/src/assets/ba_data/python/bauiv1lib/store/browser.py +++ b/src/assets/ba_data/python/bauiv1lib/store/browser.py @@ -1406,11 +1406,11 @@ def _check_merch_availability_in_bg_thread() -> None: time.sleep(1.1934) # A bit randomized to avoid aliasing. -# Slight hack; start checking merch availability in the bg -# (but only if it looks like we're part of a running app; don't want to -# do this during docs generation/etc.) +# Slight hack; start checking merch availability in the bg (but only if +# it looks like we've been imported for use in a running app; don't want +# to do this during docs generation/etc.) if ( os.environ.get('BA_RUNNING_WITH_DUMMY_MODULES') != '1' - and bui.app.state is not bui.app.State.BOOTSTRAPPING + and bui.app.state is not bui.app.State.NOT_RUNNING ): Thread(target=_check_merch_availability_in_bg_thread, daemon=True).start() diff --git a/src/ballistica/base/app_adapter/app_adapter.cc b/src/ballistica/base/app_adapter/app_adapter.cc index e555cbff..824ea797 100644 --- a/src/ballistica/base/app_adapter/app_adapter.cc +++ b/src/ballistica/base/app_adapter/app_adapter.cc @@ -140,7 +140,6 @@ void AppAdapter::OnAppPause_() { g_base->network_reader->OnAppPause(); } g_base->networking->OnAppPause(); - g_core->platform->OnAppPause(); } void AppAdapter::OnAppResume_() { @@ -151,7 +150,6 @@ void AppAdapter::OnAppResume_() { EventLoop::SetEventLoopsPaused(false); // Run resumes that expect to happen in the main thread. - g_core->platform->OnAppResume(); g_base->network_reader->OnAppResume(); g_base->networking->OnAppResume(); diff --git a/src/ballistica/base/base.cc b/src/ballistica/base/base.cc index cdb5cf2a..3ab54f02 100644 --- a/src/ballistica/base/base.cc +++ b/src/ballistica/base/base.cc @@ -75,12 +75,12 @@ BaseFeatureSet::BaseFeatureSet() } void BaseFeatureSet::OnModuleExec(PyObject* module) { - // Ok, our feature-set's Python module is getting imported. Like any - // normal Python module, we take this opportunity to import/create the + // Ok, our feature-set's Python module is getting imported. Just like a + // pure Python module would, we take this opportunity to import/create the // stuff we use. // Importing core should always be the first thing we do. Various - // ballistica functionality will fail if this has not been done. + // Ballistica functionality will fail if this has not been done. assert(g_core == nullptr); g_core = core::CoreFeatureSet::Import(); @@ -106,17 +106,18 @@ void BaseFeatureSet::OnModuleExec(PyObject* module) { g_base->python->AddPythonClasses(module); // Store our C++ front-end with our Python module. This is what allows - // others to 'import' our C++ front end. + // other C++ code to 'import' our C++ front end and talk to us directly. g_base->StoreOnPythonModule(module); // Import all the Python stuff we use. g_base->python->ImportPythonObjs(); // Run some sanity checks, wire up our log handler, etc. - auto result = g_base->python->objs() - .Get(BasePython::ObjID::kOnNativeModuleImportCall) - .Call(); - if (!result.Exists()) { + bool success = g_base->python->objs() + .Get(BasePython::ObjID::kEnvOnNativeModuleImportCall) + .Call() + .Exists(); + if (!success) { FatalError("babase._env.on_native_module_import() call failed."); } diff --git a/src/ballistica/base/logic/logic.cc b/src/ballistica/base/logic/logic.cc index f16026ed..dfc4f3cf 100644 --- a/src/ballistica/base/logic/logic.cc +++ b/src/ballistica/base/logic/logic.cc @@ -62,7 +62,6 @@ void Logic::OnAppStart() { g_base->audio->OnAppStart(); g_base->input->OnAppStart(); g_base->ui->OnAppStart(); - g_core->platform->OnAppStart(); g_base->app_mode()->OnAppStart(); if (g_base->HavePlus()) { g_base->plus()->OnAppStart(); @@ -80,7 +79,7 @@ void Logic::OnInitialScreenCreated() { // business logic. // Let the Python layer know the native layer is now fully functional. - // This will probably result in the Python layer flipping to the LAUNCHING + // This will probably result in the Python layer flipping to the INITING // state. CompleteAppBootstrapping(); @@ -109,7 +108,7 @@ void Logic::CompleteAppBootstrapping() { assert(!app_bootstrapping_complete_); app_bootstrapping_complete_ = true; - g_core->LifecycleLog("app bootstrapping complete"); + g_core->LifecycleLog("app native bootstrapping complete"); // Let the assets system know it can start loading stuff now that // we have a screen and thus know texture formats/etc. @@ -143,7 +142,7 @@ void Logic::CompleteAppBootstrapping() { // Let Python know we're done bootstrapping so it can flip the app // into the 'launching' state. g_base->python->objs() - .Get(BasePython::ObjID::kAppOnNativeBootstrappedCall) + .Get(BasePython::ObjID::kAppOnNativeBootstrappingCompleteCall) .Call(); UpdatePendingWorkTimer(); @@ -181,7 +180,6 @@ void Logic::OnAppPause() { g_base->plus()->OnAppPause(); } g_base->app_mode()->OnAppPause(); - g_core->platform->OnAppPause(); g_base->ui->OnAppPause(); g_base->input->OnAppPause(); g_base->audio->OnAppPause(); @@ -197,7 +195,6 @@ void Logic::OnAppResume() { g_base->audio->OnAppResume(); g_base->input->OnAppResume(); g_base->ui->OnAppResume(); - g_core->platform->OnAppResume(); g_base->app_mode()->OnAppResume(); if (g_base->HavePlus()) { g_base->plus()->OnAppResume(); @@ -234,7 +231,6 @@ void Logic::OnAppShutdown() { g_base->plus()->OnAppShutdown(); } g_base->app_mode()->OnAppShutdown(); - g_core->platform->OnAppShutdown(); g_base->ui->OnAppShutdown(); g_base->input->OnAppShutdown(); g_base->audio->OnAppShutdown(); @@ -269,7 +265,6 @@ void Logic::DoApplyAppConfig() { g_base->audio->DoApplyAppConfig(); g_base->input->DoApplyAppConfig(); g_base->ui->DoApplyAppConfig(); - g_core->platform->DoApplyAppConfig(); g_base->app_mode()->DoApplyAppConfig(); if (g_base->HavePlus()) { g_base->plus()->DoApplyAppConfig(); @@ -585,15 +580,20 @@ void Logic::UpdatePendingWorkTimer() { void Logic::HandleInterruptSignal() { assert(g_base->InLogicThread()); + // Interrupt signals are 'gentle' requests to shut down. + // Special case; when running under the server-wrapper, we completely // ignore interrupt signals (the wrapper acts on them). if (g_base->server_wrapper_managed()) { return; } + Shutdown(); +} - // Go with a low level process shutdown here. In situations where we're - // getting interrupt signals I don't think we'd ever want high level - // 'soft' quits. +void Logic::HandleTerminateSignal() { + // Interrupt signals are slightly more stern requests to shut down. + // We always respond to these. + assert(g_base->InLogicThread()); Shutdown(); } diff --git a/src/ballistica/base/logic/logic.h b/src/ballistica/base/logic/logic.h index 2dd3c27b..bbdb26c7 100644 --- a/src/ballistica/base/logic/logic.h +++ b/src/ballistica/base/logic/logic.h @@ -69,7 +69,9 @@ class Logic { return app_bootstrapping_complete_; } void NotifyOfPendingAssetLoads(); + void HandleInterruptSignal(); + void HandleTerminateSignal(); auto NewAppTimer(millisecs_t length, bool repeat, const Object::Ref& runnable) -> int; diff --git a/src/ballistica/base/platform/base_platform.cc b/src/ballistica/base/platform/base_platform.cc index 79d360b7..d3e5ccc7 100644 --- a/src/ballistica/base/platform/base_platform.cc +++ b/src/ballistica/base/platform/base_platform.cc @@ -257,11 +257,21 @@ void BasePlatform::DoOpenURL(const std::string& url) { #if !BA_OSTYPE_WINDOWS static void HandleSIGINT(int s) { - if (g_base->logic) { + if (g_base && g_base->logic->event_loop()) { g_base->logic->event_loop()->PushCall( [] { g_base->logic->HandleInterruptSignal(); }); } else { - Log(LogLevel::kError, "SigInt handler called before g_logic exists."); + Log(LogLevel::kError, + "SigInt handler called before g_base->logic->event_loop exists."); + } +} +static void HandleSIGTERM(int s) { + if (g_base && g_base->logic->event_loop()) { + g_base->logic->event_loop()->PushCall( + [] { g_base->logic->HandleTerminateSignal(); }); + } else { + Log(LogLevel::kError, + "SigInt handler called before g_base->logic->event_loop exists."); } } #endif @@ -271,11 +281,20 @@ void BasePlatform::SetupInterruptHandling() { #if BA_OSTYPE_WINDOWS throw Exception(); #else - struct sigaction handler {}; - handler.sa_handler = HandleSIGINT; - sigemptyset(&handler.sa_mask); - handler.sa_flags = 0; - sigaction(SIGINT, &handler, nullptr); + { + struct sigaction handler {}; + handler.sa_handler = HandleSIGINT; + sigemptyset(&handler.sa_mask); + handler.sa_flags = 0; + sigaction(SIGINT, &handler, nullptr); + } + { + struct sigaction handler {}; + handler.sa_handler = HandleSIGTERM; + sigemptyset(&handler.sa_mask); + handler.sa_flags = 0; + sigaction(SIGTERM, &handler, nullptr); + } #endif } diff --git a/src/ballistica/base/python/base_python.cc b/src/ballistica/base/python/base_python.cc index 5bc76669..2960f3b2 100644 --- a/src/ballistica/base/python/base_python.cc +++ b/src/ballistica/base/python/base_python.cc @@ -142,7 +142,10 @@ void BasePython::OnMainThreadStartApp() { } } -void BasePython::OnAppStart() { assert(g_base->InLogicThread()); } +void BasePython::OnAppStart() { + assert(g_base->InLogicThread()); + objs().Get(BasePython::ObjID::kAppOnNativeStartCall).Call(); +} void BasePython::OnAppPause() { assert(g_base->InLogicThread()); diff --git a/src/ballistica/base/python/base_python.h b/src/ballistica/base/python/base_python.h index 3e841434..dce5cc7c 100644 --- a/src/ballistica/base/python/base_python.h +++ b/src/ballistica/base/python/base_python.h @@ -35,7 +35,7 @@ class BasePython { kCallClass, kGarbageCollectSessionEndCall, kConfig, - kAppOnNativeBootstrappedCall, + kAppOnNativeBootstrappingCompleteCall, kResetToMainMenuCall, kSetConfigFullscreenOnCall, kSetConfigFullscreenOffCall, @@ -69,10 +69,11 @@ class BasePython { kAppReadConfigCall, kUIRemotePressCall, kRemoveInGameAdsMessageCall, + kAppOnNativeStartCall, kAppOnNativePauseCall, kAppOnNativeResumeCall, - kQuitCall, kAppOnNativeShutdownCall, + kQuitCall, kShowPostPurchaseMessageCall, kContextError, kNotFoundError, @@ -98,7 +99,7 @@ class BasePython { kLoginAdapterGetSignInTokenResponseCall, kPreEnv, kOpenURLWithWebBrowserModuleCall, - kOnNativeModuleImportCall, + kEnvOnNativeModuleImportCall, kOnMainThreadStartAppCall, kAppPushApplyAppConfigCall, kLast // Sentinel; must be at end. diff --git a/src/ballistica/base/python/methods/python_methods_app.cc b/src/ballistica/base/python/methods/python_methods_app.cc index c13b7a36..0e7ba225 100644 --- a/src/ballistica/base/python/methods/python_methods_app.cc +++ b/src/ballistica/base/python/methods/python_methods_app.cc @@ -516,6 +516,9 @@ static auto PyQuit(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } + // Log(LogLevel::kDebug, + // "QUIT soft=" + std::to_string(soft) + " back=" + std::to_string(back)); + // FIXME this should all just go through platform and/or app-adapter. if (g_buildconfig.ostype_ios_tvos()) { diff --git a/src/ballistica/classic/classic.cc b/src/ballistica/classic/classic.cc index b98d4570..11abf286 100644 --- a/src/ballistica/classic/classic.cc +++ b/src/ballistica/classic/classic.cc @@ -32,8 +32,8 @@ void ClassicFeatureSet::OnModuleExec(PyObject* module) { assert(g_classic == nullptr); g_classic = new ClassicFeatureSet(); - // Store our C++ front-end with our Python module. - // This is what allows others to 'import' our C++ front end. + // Store our C++ front-end with our Python module. This is what allows + // other C++ code to 'import' our C++ front end and talk to us directly. g_classic->StoreOnPythonModule(module); // Import any Python stuff we use into objs_. diff --git a/src/ballistica/core/platform/core_platform.cc b/src/ballistica/core/platform/core_platform.cc index efcb80bd..963045ff 100644 --- a/src/ballistica/core/platform/core_platform.cc +++ b/src/ballistica/core/platform/core_platform.cc @@ -644,22 +644,6 @@ auto CorePlatform::GetTextTextureData(void* tex) -> uint8_t* { throw Exception(); } -void CorePlatform::OnAppStart() { - assert(g_base_soft && g_base_soft->InLogicThread()); -} - -void CorePlatform::OnAppPause() { - assert(g_base_soft && g_base_soft->InLogicThread()); -} - -void CorePlatform::OnAppResume() { - assert(g_base_soft && g_base_soft->InLogicThread()); -} - -void CorePlatform::OnAppShutdown() { - assert(g_base_soft && g_base_soft->InLogicThread()); -} - void CorePlatform::OnScreenSizeChange() { assert(g_base_soft && g_base_soft->InLogicThread()); } @@ -700,8 +684,6 @@ void CorePlatform::AndroidSetResString(const std::string& res) { throw Exception(); } -void CorePlatform::DoApplyAppConfig() {} - void CorePlatform::AndroidSynthesizeBackPress() { Log(LogLevel::kError, "AndroidSynthesizeBackPress() unimplemented"); } diff --git a/src/ballistica/core/platform/core_platform.h b/src/ballistica/core/platform/core_platform.h index b7efa227..c72efd0a 100644 --- a/src/ballistica/core/platform/core_platform.h +++ b/src/ballistica/core/platform/core_platform.h @@ -51,11 +51,6 @@ class CorePlatform { virtual void WillExitMain(bool errored); - virtual void OnAppStart(); - virtual void OnAppPause(); - virtual void OnAppResume(); - virtual void OnAppShutdown(); - virtual void DoApplyAppConfig(); virtual void OnScreenSizeChange(); virtual void StepDisplayTime(); diff --git a/src/ballistica/scene_v1/scene_v1.cc b/src/ballistica/scene_v1/scene_v1.cc index a6a29551..fd086a8e 100644 --- a/src/ballistica/scene_v1/scene_v1.cc +++ b/src/ballistica/scene_v1/scene_v1.cc @@ -50,8 +50,8 @@ void SceneV1FeatureSet::OnModuleExec(PyObject* module) { assert(g_scene_v1 == nullptr); g_scene_v1 = new SceneV1FeatureSet(); - // Store our C++ front-end with our Python module. - // This is what allows others to 'import' our C++ front end. + // Store our C++ front-end with our Python module. This is what allows + // other C++ code to 'import' our C++ front end and talk to us directly. g_scene_v1->StoreOnPythonModule(module); // Define our classes. diff --git a/src/ballistica/scene_v1/support/scene_v1_app_mode.cc b/src/ballistica/scene_v1/support/scene_v1_app_mode.cc index 4754b498..efeaed8f 100644 --- a/src/ballistica/scene_v1/support/scene_v1_app_mode.cc +++ b/src/ballistica/scene_v1/support/scene_v1_app_mode.cc @@ -1376,10 +1376,7 @@ void SceneV1AppMode::HandleQuitOnIdle() { idle_exiting_ = true; Log(LogLevel::kInfo, "Quitting due to reaching idle-exit-minutes."); - g_base->logic->event_loop()->PushCall([] { - assert(g_base->InLogicThread()); - g_base->python->objs().Get(base::BasePython::ObjID::kQuitCall).Call(); - }); + g_base->logic->event_loop()->PushCall([] { g_base->logic->Shutdown(); }); } } } diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc index 23a18a5f..4a6ea18f 100644 --- a/src/ballistica/shared/ballistica.cc +++ b/src/ballistica/shared/ballistica.cc @@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int { namespace ballistica { // These are set automatically via script; don't modify them here. -const int kEngineBuildNumber = 21250; +const int kEngineBuildNumber = 21256; const char* kEngineVersion = "1.7.26"; #if BA_MONOLITHIC_BUILD diff --git a/src/ballistica/shared/foundation/event_loop.cc b/src/ballistica/shared/foundation/event_loop.cc index 9b6ebc63..2f3b4d41 100644 --- a/src/ballistica/shared/foundation/event_loop.cc +++ b/src/ballistica/shared/foundation/event_loop.cc @@ -86,16 +86,8 @@ EventLoop::EventLoop(EventLoopID identifier_in, ThreadSource source) // Block until the thread is bootstrapped. // (maybe not necessary, but let's be cautious in case we'd // try to use things like thread_id before they're known). - if (identifier_ == EventLoopID::kLogic) { - g_core->LifecycleLog("logic thread bootstrap wait begin"); - } - client_listener_cv_.wait(lock, [this] { return bootstrapped_; }); - if (identifier_ == EventLoopID::kLogic) { - g_core->LifecycleLog("logic thread bootstrap wait end"); - } - break; } case ThreadSource::kWrapMain: { @@ -758,20 +750,12 @@ void EventLoop::PushRunnableSynchronous(Runnable* runnable) { PushCrossThreadRunnable_(runnable, &complete); } - if (identifier_ == EventLoopID::kLogic) { - g_core->LifecycleLog("logic thread sync run push begin"); - } - // Now listen until our completion flag gets set. client_listener_cv_.wait(lock, [complete_ptr] { // Go back to sleep on spurious wakeups // (if we're not actually complete yet). return *complete_ptr; }); - - if (identifier_ == EventLoopID::kLogic) { - g_core->LifecycleLog("logic thread sync run push end"); - } } auto EventLoop::CheckPushSafety() -> bool { diff --git a/src/ballistica/template_fs/template_fs.cc b/src/ballistica/template_fs/template_fs.cc index f81ce4d5..8c7a0632 100644 --- a/src/ballistica/template_fs/template_fs.cc +++ b/src/ballistica/template_fs/template_fs.cc @@ -23,8 +23,8 @@ void TemplateFsFeatureSet::OnModuleExec(PyObject* module) { // Create our feature-set's C++ front-end. g_template_fs = new TemplateFsFeatureSet(); - // Store our C++ front-end on our Python module. - // This is what allows others to 'import' our C++ front end. + // Store our C++ front-end with our Python module. This is what allows + // other C++ code to 'import' our C++ front end and talk to us directly. g_template_fs->StoreOnPythonModule(module); // Import any Python stuff we use into objs_. diff --git a/src/meta/babasemeta/pyembed/binding_base.py b/src/meta/babasemeta/pyembed/binding_base.py index b180e93f..f1351065 100644 --- a/src/meta/babasemeta/pyembed/binding_base.py +++ b/src/meta/babasemeta/pyembed/binding_base.py @@ -78,6 +78,6 @@ values = [ _hooks.implicit_sign_out, # kImplicitSignOutCall _hooks.login_adapter_get_sign_in_token_response, # kLoginAdapterGetSignInTokenResponseCall _hooks.open_url_with_webbrowser_module, # kOpenURLWithWebBrowserModuleCall - _env.on_native_module_import, # kOnNativeModuleImportCall + _env.on_native_module_import, # kEnvOnNativeModuleImportCall _env.on_main_thread_start_app, # kOnMainThreadStartAppCall ] diff --git a/src/meta/babasemeta/pyembed/binding_base_app.py b/src/meta/babasemeta/pyembed/binding_base_app.py index d4b8757f..3b63ff60 100644 --- a/src/meta/babasemeta/pyembed/binding_base_app.py +++ b/src/meta/babasemeta/pyembed/binding_base_app.py @@ -12,7 +12,8 @@ values = [ app.lang.get_resource, # kGetResourceCall app.lang.translate, # kTranslateCall app.push_apply_app_config, # kAppPushApplyAppConfigCall - app.on_native_bootstrapped, # kAppOnNativeBootstrappedCall + app.on_native_start, # kAppOnNativeStartCall + app.on_native_bootstrapping_complete, # kAppOnNativeBootstrappingCompleteCall app.on_native_pause, # kAppOnNativePauseCall app.on_native_resume, # kAppOnNativeResumeCall app.on_native_shutdown, # kAppOnNativeShutdownCall