subsystem cleanup

This commit is contained in:
Eric 2023-06-04 19:45:12 -07:00
parent f1ba22170d
commit 26cc24d345
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
20 changed files with 319 additions and 182 deletions

View File

@ -4072,50 +4072,50 @@
"build/assets/workspace/ninjafightplug.py": "https://files.ballistica.net/cache/ba1/c5/09/4f10b8a21ba87aa5509cff7a164b",
"build/assets/workspace/onslaughtplug.py": "https://files.ballistica.net/cache/ba1/ff/0a/a354984f9c074dab0676ac7e4877",
"build/assets/workspace/runaroundplug.py": "https://files.ballistica.net/cache/ba1/2a/1c/9ee5db6d1bceca7fa6638fb8abde",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/d2/f1/c7ecb66769e793ea1ae2427ec540",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/89/4f/99fb1371ff083d135f3c8251ead1",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/4a/5c/076d47a516a1b9e5d3b7e7930a30",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/ab/63/caf01d35edabba0359e0444bf898",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/25/25/e0a9d955037c5375558b5bef7736",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/b8/6d/ef564300e7189964e133be1c6d7f",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/b4/50/9e1fcab11d5e329750ef64f38a94",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/a6/62/3ae503b2027dad675a8a4dae8d75",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/4c/8b/44c018a2bf5ff79951d28525d230",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/67/d9/1f49882d9f50051e5a77c8b37856",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/2f/26/01c0bb3750c49e8b27032e3506b4",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/c7/90/48bccd35318bbf7693417a50c96d",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/13/92/949362dd74ca933d11b8081c366b",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/20/a0/6f048f2a3b9d64851420de719d3a",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/3a/f8/bca8437e4ac8403d46efdd9b0173",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/d9/3d/5ac299f7f2e7acfdea2a297e1c57",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/82/ff/41b875d2048dee0d47ca5173f8e6",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/18/45/1b8328009e7e7aaf4d840170427a",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/f5/63/2b15e84c989a0f58c8ea6c78c379",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/1f/7f/ed15584c8a49e20a4f081622214f",
"build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/de/fd/21a635bb0a73e252885acb7f6628",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/1b/d0/f94119594604e67652924b434416",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/13/18/48dc17a460c2ee0fa4a22b2d8005",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/c2/2d/bf75444af834501f9e443618786c",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/ec/88/ab5016ebb0ad9bdce10301267793",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/2c/1d/e26f457ba90fdd700dee7cc6252b",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/04/f5/59af762745bef4e8660e95a07226",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/0f/32/d374d10065061ee0a6ca67b6a4af",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/94/30/c61c2114828f360d21bf81d2c39a",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/4c/2b/c6aa202a104e49c82461e6d084af",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/94/ff/ac435f0ade27ad8220c945e77c2b",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/21/b5/7f010c0be5f14b2b14a823afba57",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/12/75/79f8ec088e1c946e9d60db3e91cf",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/d9/e1/4791926365ecf2a19b9528163ae0",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/e0/99/a86cb331308904b24f1a48342d2d",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/94/7d/aa5e4fb93de4681bb4bf002e28cf",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/d1/cf/fa4136e93ded15dbab93fd57d1b8",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/b4/0e/1522c860e33f5001f71cfe12aa7a",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/a5/fb/cb9bb39ac98d0c5b0747094f54c2",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/63/60/cba991152f215d0e6ff1b0801a54",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/2a/29/e323dbaaa208c40a569d85a82527",
"build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/0f/d7/034299d84dde9b8a36933c3d6640",
"build/prefab/lib/linux_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/8c/0c/209d7fef648785021b2442b92a37",
"build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/3e/d6/f3e8c02c82379d78d15bb1bb9d41",
"build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/de/6b/14e766a2d2db860276553b7c3e9d",
"build/prefab/lib/linux_arm64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/db/d6/42e603000c13737e60f693336d4e",
"build/prefab/lib/linux_x86_64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/dd/e3/c1b35888db082abde395c7197d79",
"build/prefab/lib/linux_x86_64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/95/86/052441f8ccaa13006c9d38dbb79d",
"build/prefab/lib/linux_x86_64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/64/40/f06fe4f0d7537e50b6e880c6590c",
"build/prefab/lib/linux_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/b2/fe/09cba839bcfdc19b29f6acf4c129",
"build/prefab/lib/linux_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/ed/bf/45c4771f7d77985b26f3612591e0",
"build/prefab/lib/linux_x86_64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/54/10/a2c3d3a1076eb1b9812b736ca34b",
"build/prefab/lib/mac_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/08/40/aaa523c2752e7128368dcbb9746c",
"build/prefab/lib/mac_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/c3/2d/e1e05f01647e92c65cbcc3599705",
"build/prefab/lib/mac_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/89/cd/db7719b6851e28d7969d807be3d8",
"build/prefab/lib/mac_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/78/c0/3c8e4b03e3d92675a4c255242ccb",
"build/prefab/lib/mac_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/37/43/84a1fc8169db34054378f11fe4c1",
"build/prefab/lib/mac_arm64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/5b/cb/4be9adf2aa44aae442bda6f39112",
"build/prefab/lib/mac_x86_64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/7d/4d/e6f90dbe842358db16b060fd3187",
"build/prefab/lib/mac_x86_64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/3d/92/ea42decffa28fb7540d72248f79e",
"build/prefab/lib/mac_x86_64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/8a/34/5d1b029d1a393139890f600ff91d",
"build/prefab/lib/mac_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/f6/a3/753d976d43b45062184414098306",
"build/prefab/lib/mac_x86_64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/98/e0/0772edc2ae173e7f8ab07d4e1230",
"build/prefab/lib/mac_x86_64_server/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/ba/f0/c7dcdd6472a348663d104d490e77",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/94/a1/3eed726b9bf0dac2e44cca23c1ba",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/09/47/a0d5f2088c43798bc60f72204ba5",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/5f/9a/542f9cb3ddfb702f81756047e7dc",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/c9/57/641e99496162ed453e6a1a8aea2e",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/0c/8b/37e6fdd08cb1dc1df818d550f7a3",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/68/82/a261d09580d572c25982b15efaa2",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/b9/91/34ee2461cf291fd765e9ab1cb09e",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/8f/00/84bd3b34227305d59202195c2032",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/ef/f4/61c64323efa3fd0b1e6ef2042d2e",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/43/58/6797ab58417599aefa2719e9ed70",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/8d/9d/6e683b4a8ff61138041318470352",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/b6/1a/ad49bd349c7c0f59a6dcf2efd70e",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "https://files.ballistica.net/cache/ba1/df/7a/6bdd5096bb65a99d39d922b1743a",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "https://files.ballistica.net/cache/ba1/fb/15/19719ece74758579110612e93adb",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "https://files.ballistica.net/cache/ba1/80/fe/13417c54861d3bc076c173590175",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "https://files.ballistica.net/cache/ba1/2b/32/3d8e3e131fd27908742aac0df030",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "https://files.ballistica.net/cache/ba1/52/c6/c11130af7b10d6c0321add5518fa",
"src/assets/ba_data/python/babase/_mgen/enums.py": "https://files.ballistica.net/cache/ba1/38/c3/1dedd5e74f2508efc5974c8815a1",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "https://files.ballistica.net/cache/ba1/d5/4a/0e480a855ce83709bd7f6761107d",

View File

@ -1,4 +1,4 @@
### 1.7.20 (build 21045, api 8, 2023-06-03)
### 1.7.20 (build 21046, api 8, 2023-06-04)
- This seems like a good time for a `refactoring` release in anticipation of
changes coming in 1.8. Basically this means that a lot of things will be

View File

@ -9,6 +9,7 @@
"ba_data/python/babase/__pycache__/_appintent.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_appmode.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_appmodeselector.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_appsubsystem.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_apputils.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_assetmanager.cpython-311.opt-1.pyc",
"ba_data/python/babase/__pycache__/_asyncio.cpython-311.opt-1.pyc",
@ -34,6 +35,7 @@
"ba_data/python/babase/_appintent.py",
"ba_data/python/babase/_appmode.py",
"ba_data/python/babase/_appmodeselector.py",
"ba_data/python/babase/_appsubsystem.py",
"ba_data/python/babase/_apputils.py",
"ba_data/python/babase/_assetmanager.py",
"ba_data/python/babase/_asyncio.py",

View File

@ -142,6 +142,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
$(BUILD_DIR)/ba_data/python/babase/_appintent.py \
$(BUILD_DIR)/ba_data/python/babase/_appmode.py \
$(BUILD_DIR)/ba_data/python/babase/_appmodeselector.py \
$(BUILD_DIR)/ba_data/python/babase/_appsubsystem.py \
$(BUILD_DIR)/ba_data/python/babase/_apputils.py \
$(BUILD_DIR)/ba_data/python/babase/_assetmanager.py \
$(BUILD_DIR)/ba_data/python/babase/_asyncio.py \
@ -413,6 +414,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_appintent.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_appmode.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_appmodeselector.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_appsubsystem.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_apputils.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_assetmanager.cpython-311.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/babase/__pycache__/_asyncio.cpython-311.opt-1.pyc \

View File

@ -35,6 +35,7 @@ from _babase import (
from babase._appintent import AppIntent, AppIntentDefault, AppIntentExec
from babase._appmode import AppMode
from babase._appsubsystem import AppSubsystem
from babase._accountv2 import AccountV2Handle
from babase._plugin import PotentialPlugin, Plugin, PluginSubsystem
from babase._app import App
@ -161,6 +162,7 @@ __all__ = [
'AppIntentDefault',
'AppIntentExec',
'AppMode',
'AppSubsystem',
]

View File

@ -27,11 +27,9 @@ if TYPE_CHECKING:
from efro.log import LogHandler
import babase
from babase._cloud import CloudSubsystem
from babase import CloudSubsystem, AppIntent, AppMode, AppSubsystem
from babase._accountv2 import AccountV2Subsystem
from babase._apputils import AppHealthMonitor
from babase._appintent import AppIntent
from babase._appmode import AppMode
# __FEATURESET_APP_SUBSYSTEM_IMPORTS_BEGIN__
# This section generated by batools.appmodule; do not edit.
@ -58,6 +56,7 @@ class App:
# Implementations for these will be filled in by internal libs.
accounts: AccountV2Subsystem
cloud: CloudSubsystem
plugins: PluginSubsystem
# log_handler: LogHandler
health_monitor: AppHealthMonitor
@ -237,6 +236,8 @@ class App:
self.state = self.State.INITIAL
self._subsystems: list[AppSubsystem] = []
self._app_bootstrapping_complete = False
self._called_on_app_launching = False
self._launch_completed = False
@ -279,7 +280,6 @@ class App:
self.components = AppComponentSubsystem()
self.meta = MetadataSubsystem()
self.plugins = PluginSubsystem()
self.lang = LanguageSubsystem()
self.net = NetworkSubsystem()
self.workspaces = WorkspaceSubsystem()
@ -299,9 +299,25 @@ class App:
"""Called after we are inited and assigned to babase.app."""
# NOTE: the reason we need a postinit here is that
# some of this stuff might try importing babase.app and that doesn't
# some of this stuff accesses babase.app and that doesn't
# exist yet as of our __init__() call.
self.plugins = PluginSubsystem()
def register_subsystem(self, subsystem: AppSubsystem) -> None:
"""Called by the AppSubsystem class. Do not use directly."""
# We only allow registering new subsystems if we've not yet
# reached the 'running' state. This ensures that all subsystems
# receive a consistent set of callbacks starting with
# on_app_running().
if self._called_on_app_running:
raise RuntimeError(
'Subsystems cannot be registered after the'
' app has reached the running state.'
)
self._subsystems.append(subsystem)
def _threadpool_no_wait_done(self, fut: Future) -> None:
try:
fut.result()
@ -357,8 +373,8 @@ class App:
Intent defines what the app is trying to do at a given time.
This call is asynchronous; the intent switch will happen in the
logic thread in the near future. If set_intent is
called repeatedly before the change takes place, the last intent
logic thread in the near future. If set_intent is called
repeatedly before the change takes place, the final intent to be
set will be used.
"""
@ -467,7 +483,11 @@ class App:
self._update_state()
def on_app_launching(self) -> None:
"""Called when the app is entering the launching state."""
"""Called when the app enters the launching state.
Here we can put together subsystems and other pieces for the
app, but most things should not be doing any work yet.
"""
# pylint: disable=cyclic-import
from babase import _asyncio
from babase import _appconfig
@ -494,6 +514,9 @@ class App:
# Get meta-system scanning built-in stuff in the bg.
self.meta.start_scan(scan_complete_cb=self.on_meta_scan_complete)
if self.plus is not None:
self.plus.on_app_launching()
self.accounts.on_app_launching()
# Make sure this runs after we init our accounts stuff, since
@ -507,27 +530,47 @@ class App:
self._launch_completed = True
self._update_state()
def on_app_loading(self) -> None:
"""Called when the app is entering the loading state."""
assert _babase.in_logic_thread()
def on_meta_scan_complete(self) -> None:
"""Called when meta-scan is done doing its thing."""
assert _babase.in_logic_thread()
# Now that we know what's out there, build our final plugin set.
self.plugins.on_meta_scan_complete()
def on_app_running(self) -> None:
"""Called when the app is entering the running state."""
def on_app_loading(self) -> None:
"""Called when the app enters 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'.
"""
assert _babase.in_logic_thread()
# Let the native layer know.
def on_app_running(self) -> None:
"""Called when the app enters 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.
"""
assert _babase.in_logic_thread()
# Let our native layer know.
_babase.on_app_running()
# Set a default app-mode-selector. Plugins can then override
# this if they want.
# this if they want in the on_app_running callback below.
self.mode_selector = self.DefaultAppModeSelector()
self.plugins.on_app_running()
# Inform all app subsystems in the same order they were inited.
for subsystem in self._subsystems:
try:
subsystem.on_app_running()
except Exception:
logging.exception(
'Error in on_app_running for subsystem %s.', subsystem
)
# If 'exec' code was provided to the app, always kick that off
# here as an intent.
@ -562,7 +605,7 @@ class App:
# This section generated by batools.appmodule; do not edit.
# Hmm; need to think about how we auto-construct this; how
# do we determine which app modes to check and in what
# should we determine which app modes to check and in what
# order?
import bascenev1
@ -627,27 +670,43 @@ class App:
def on_app_pause(self) -> None:
"""Called when the app goes to a paused state."""
self.cloud.on_app_pause()
self.plugins.on_app_pause()
self.health_monitor.on_app_pause()
if self.classic is not None:
self.classic.on_app_pause()
assert _babase.in_logic_thread()
# Pause all app subsystems in the opposite order they were inited.
for subsystem in reversed(self._subsystems):
try:
subsystem.on_app_pause()
except Exception:
logging.exception(
'Error in on_app_pause for subsystem %s.', subsystem
)
def on_app_resume(self) -> None:
"""Called when resuming."""
self.fg_state += 1
self.cloud.on_app_resume()
self.plugins.on_app_resume()
self.health_monitor.on_app_resume()
if self.classic is not None:
self.classic.on_app_resume()
# Resume all app subsystems in the same order they were inited.
for subsystem in self._subsystems:
try:
subsystem.on_app_resume()
except Exception:
logging.exception(
'Error in on_app_resume for subsystem %s.', subsystem
)
def on_app_shutdown(self) -> None:
"""(internal)"""
self.state = self.State.SHUTTING_DOWN
self.plugins.on_app_shutdown()
if self.classic is not None:
self.classic.on_app_shutdown()
# Shutdown all app subsystems in the opposite order they were
# inited.
for subsystem in reversed(self._subsystems):
try:
subsystem.on_app_shutdown()
except Exception:
logging.exception(
'Error in on_app_shutdown for subsystem %s.', subsystem
)
def read_config(self) -> None:
"""(internal)"""

View File

@ -0,0 +1,41 @@
# Released under the MIT License. See LICENSE for details.
#
"""Provides the AppSubsystem base class."""
from __future__ import annotations
from typing import TYPE_CHECKING
import _babase
if TYPE_CHECKING:
from babase._app import App
class AppSubsystem:
"""Base class for an app subsystem.
Category: **App Classes**
An app 'subsystem' is a bit of a vague term, as pieces of the app
can technically be any class and are not required to use this, but
building one out of this base class provides some conveniences such
as predefined callbacks during app state changes.
Subsystems must be registered with the app before it reaches the
'running' state.
"""
def __init__(self) -> None:
_babase.app.register_subsystem(self)
def on_app_running(self) -> None:
"""Called when the app reaches the running state."""
def on_app_pause(self) -> None:
"""Called when the app enters the paused state."""
def on_app_resume(self) -> None:
"""Called when the app exits the paused state."""
def on_app_shutdown(self) -> None:
"""Called when the app is shutting down."""

View File

@ -14,6 +14,7 @@ from efro.call import tpartial
from efro.log import LogLevel
from efro.dataclassio import ioprepped, dataclass_to_json, dataclass_from_json
import _babase
from babase._appsubsystem import AppSubsystem
if TYPE_CHECKING:
from typing import Any, TextIO
@ -78,8 +79,8 @@ def handle_v1_cloud_log() -> None:
if app.classic is None or app.plus is None:
if _babase.do_once():
logging.warning(
'handle_v1_cloud_log should not be used'
' without classic or plus.'
'handle_v1_cloud_log should not be called'
' without classic and plus present.'
)
return
@ -350,11 +351,12 @@ def log_dumped_app_state() -> None:
logging.exception('Error logging dumped app state.')
class AppHealthMonitor:
class AppHealthMonitor(AppSubsystem):
"""Logs things like app-not-responding issues."""
def __init__(self) -> None:
assert _babase.in_logic_thread()
super().__init__()
self._running = True
self._thread = Thread(target=self._app_monitor_thread_main, daemon=True)
self._thread.start()
@ -420,12 +422,10 @@ class AppHealthMonitor:
self._first_check = False
def on_app_pause(self) -> None:
"""Should be called when the app pauses."""
assert _babase.in_logic_thread()
self._running = False
def on_app_resume(self) -> None:
"""Should be called when the app resumes."""
assert _babase.in_logic_thread()
self._running = True

View File

@ -8,6 +8,7 @@ import logging
from typing import TYPE_CHECKING, overload
import _babase
from babase._appsubsystem import AppSubsystem
if TYPE_CHECKING:
from typing import Callable, Any
@ -22,7 +23,7 @@ DEBUG_LOG = False
# internal protocols.
class CloudSubsystem:
class CloudSubsystem(AppSubsystem):
"""Manages communication with cloud components."""
def is_connected(self) -> bool:
@ -33,12 +34,6 @@ class CloudSubsystem:
"""
return False # Needs to be overridden
def on_app_pause(self) -> None:
"""Should be called when the app pauses."""
def on_app_resume(self) -> None:
"""Should be called when the app resumes."""
def on_connectivity_changed(self, connected: bool) -> None:
"""Called when cloud connectivity state changes."""
if DEBUG_LOG:

View File

@ -9,6 +9,7 @@ from typing import TYPE_CHECKING
from dataclasses import dataclass
import _babase
from babase._appsubsystem import AppSubsystem
if TYPE_CHECKING:
from typing import Any
@ -16,7 +17,7 @@ if TYPE_CHECKING:
import babase
class PluginSubsystem:
class PluginSubsystem(AppSubsystem):
"""Subsystem for plugin handling in the app.
Category: **App Classes**
@ -28,6 +29,7 @@ class PluginSubsystem:
AUTO_ENABLE_NEW_PLUGINS_DEFAULT = True
def __init__(self) -> None:
super().__init__()
self.potential_plugins: list[babase.PotentialPlugin] = []
self.active_plugins: dict[str, babase.Plugin] = {}
@ -46,6 +48,13 @@ class PluginSubsystem:
results = _babase.app.meta.scanresults
assert results is not None
auto_enable_new_plugins = (
_babase.app.config.get(
self.AUTO_ENABLE_NEW_PLUGINS_CONFIG_KEY,
self.AUTO_ENABLE_NEW_PLUGINS_DEFAULT,
)
is True
)
# Create a potential-plugin for each class we found in the scan.
for class_path in results.exports_of_class(Plugin):
plugs.potential_plugins.append(
@ -55,13 +64,7 @@ class PluginSubsystem:
available=True,
)
)
if (
_babase.app.config.get(
self.AUTO_ENABLE_NEW_PLUGINS_CONFIG_KEY,
self.AUTO_ENABLE_NEW_PLUGINS_DEFAULT,
)
is True
):
if auto_enable_new_plugins:
if class_path not in plugstates:
# Go ahead and enable new plugins by default, but we'll
# inform the user that they need to restart to pick them up.
@ -72,12 +75,9 @@ class PluginSubsystem:
plugs.potential_plugins.sort(key=lambda p: p.class_path)
# Note: these days we complete meta-scan and immediately activate
# plugins, so we don't need the message about 'restart to activate'
# anymore.
# FIXME: We actually now have an option to NOT activate new plugins
# so we should selectively re-enable this.
if found_new and bool(False):
# If we're *not* auto-enabling new plugins, at least let the
# user know we found something new.
if found_new and not auto_enable_new_plugins:
_babase.screenmessage(
Lstr(resource='pluginsDetectedText'), color=(0, 1, 0)
)
@ -87,7 +87,6 @@ class PluginSubsystem:
_babase.app.config.commit()
def on_app_running(self) -> None:
"""Should be called when the app reaches the running state."""
# Load up our plugins and go ahead and call their on_app_running calls.
self.load_plugins()
for plugin in self.active_plugins.values():
@ -99,7 +98,6 @@ class PluginSubsystem:
_error.print_exception('Error in plugin on_app_running()')
def on_app_pause(self) -> None:
"""Called when the app goes to a suspended state."""
for plugin in self.active_plugins.values():
try:
plugin.on_app_pause()
@ -109,7 +107,6 @@ class PluginSubsystem:
_error.print_exception('Error in plugin on_app_pause()')
def on_app_resume(self) -> None:
"""Run when the app resumes from a suspended state."""
for plugin in self.active_plugins.values():
try:
plugin.on_app_resume()
@ -119,7 +116,6 @@ class PluginSubsystem:
_error.print_exception('Error in plugin on_app_resume()')
def on_app_shutdown(self) -> None:
"""Called when the app is being closed."""
for plugin in self.active_plugins.values():
try:
plugin.on_app_shutdown()

View File

@ -10,6 +10,7 @@ from typing import TYPE_CHECKING
import _babase
import _bauiv1
import bascenev1
from babase._appsubsystem import AppSubsystem
from babase._general import AppTime
import _baclassic
from baclassic._music import MusicSubsystem
@ -34,7 +35,7 @@ if TYPE_CHECKING:
from baclassic._net import MasterServerCallback
class ClassicSubsystem:
class ClassicSubsystem(AppSubsystem):
"""Subsystem for classic functionality in the app.
The single shared instance of this app can be accessed at
@ -55,6 +56,7 @@ class ClassicSubsystem:
from baclassic._music import MusicPlayMode # FIXME move 2 subsys
def __init__(self) -> None:
super().__init__()
self._env = _babase.env()
self.accounts = AccountV1Subsystem()
@ -252,16 +254,13 @@ class ClassicSubsystem:
self.accounts.on_app_launching()
def on_app_pause(self) -> None:
"""Called when the app is getting paused."""
self.accounts.on_app_pause()
def on_app_resume(self) -> None:
"""Called when the app is getting resumed."""
self.accounts.on_app_resume()
self.music.on_app_resume()
def on_app_shutdown(self) -> None:
"""Called when the app is shutting down."""
self.music.on_app_shutdown()
def pause(self) -> None:

View File

@ -30,7 +30,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be
# using.
TARGET_BALLISTICA_BUILD = 21045
TARGET_BALLISTICA_BUILD = 21046
TARGET_BALLISTICA_VERSION = '1.7.20'
_g_env_config: EnvConfig | None = None

View File

@ -6,12 +6,15 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import _baplus
from babase._appsubsystem import AppSubsystem
if TYPE_CHECKING:
from typing import Callable, Any
from babase import App
class PlusSubsystem:
class PlusSubsystem(AppSubsystem):
"""Subsystem for plus functionality in the app.
The single shared instance of this app can be accessed at
@ -26,9 +29,6 @@ class PlusSubsystem:
# type-checking purposes. Maybe there's some smart way we could skip
# the overhead of this wrapper at runtime.
def __init__(self) -> None:
pass
@staticmethod
def add_v1_account_transaction(
transaction: dict, callback: Callable | None = None
@ -157,7 +157,7 @@ class PlusSubsystem:
@staticmethod
def on_app_launching() -> None:
"""(internal)"""
return _baplus.on_app_launching()
_baplus.on_app_launching()
@staticmethod
def power_ranking_query(callback: Callable, season: Any = None) -> None:

View File

@ -460,8 +460,14 @@ void BaseFeatureSet::ScreenMessage(const std::string& s,
void BaseFeatureSet::V1CloudLog(const std::string& msg) {
// If we've got a fully running app environment, let the Python layer
// handle logs. It will group log messages intelligently and ship them
// handle this. It will group log messages intelligently and ship them
// to the master server with various other context info included.
// We currently need both plus and classic for this system to function.
if (!(HavePlus() && HaveClassic())) {
return;
}
if (app_running_) {
python->objs().PushCall(BasePython::ObjID::kHandleV1CloudLogCall);
} else {

View File

@ -53,7 +53,7 @@ void BasePython::AddPythonClasses(PyObject* module) {
// FIXME: should be able to do this in Python bootstrapping
// code.
auto register_call =
PythonRef(PyImport_ImportModule("collections.abc"), PythonRef::kSteal)
PythonRef::Stolen(PyImport_ImportModule("collections.abc"))
.GetAttr("Sequence")
.GetAttr("register");
PythonRef args(Py_BuildValue("(O)", vec3), PythonRef::kSteal);
@ -68,7 +68,7 @@ void BasePython::ImportPythonObjs() {
void BasePython::SoftImportPlus() {
auto gil{Python::ScopedInterpreterLock()};
auto result = PythonRef::Stolen(PyImport_ImportModule("_baplus"));
auto result = PythonRef::StolenSoft(PyImport_ImportModule("_baplus"));
if (!result.Exists()) {
// Ignore any errors here for now. All that will matter is whether plus
// gave us its interface.
@ -78,7 +78,7 @@ void BasePython::SoftImportPlus() {
void BasePython::SoftImportClassic() {
auto gil{Python::ScopedInterpreterLock()};
auto result = PythonRef::Stolen(PyImport_ImportModule("_baclassic"));
auto result = PythonRef::StolenSoft(PyImport_ImportModule("_baclassic"));
if (!result.Exists()) {
// Ignore any errors here for now. All that will matter is whether plus
// gave us its interface.
@ -88,7 +88,7 @@ void BasePython::SoftImportClassic() {
void BasePython::SoftImportUIV1() {
auto gil{Python::ScopedInterpreterLock()};
auto result = PythonRef::Stolen(PyImport_ImportModule("_bauiv1"));
auto result = PythonRef::StolenSoft(PyImport_ImportModule("_bauiv1"));
if (!result.Exists()) {
// Ignore any errors here for now. All that will matter is whether plus
// gave us its interface.

View File

@ -181,7 +181,7 @@ void CorePython::ReleaseMainThreadGIL() {
void CorePython::SoftImportBase() {
auto gil{Python::ScopedInterpreterLock()};
auto result = PythonRef::Stolen(PyImport_ImportModule("_babase"));
auto result = PythonRef::StolenSoft(PyImport_ImportModule("_babase"));
if (!result.Exists()) {
// Ignore any errors here for now. All that will matter is whether base
// gave us its interface.

View File

@ -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 = 21045;
const int kEngineBuildNumber = 21046;
const char* kEngineVersion = "1.7.20";
auto MonolithicMain(const core::CoreConfig& core_config) -> int {

View File

@ -19,56 +19,41 @@ using core::g_core;
#pragma clang diagnostic push
#pragma ide diagnostic ignored "RedundantCast"
PythonRef::PythonRef(PyObject* obj_in, ReferenceBehavior b) {
assert(Python::HaveGIL());
static void ClearPythonExceptionAndWarnIfUnset() {
// We're assuming that a nullptr passed to us means a Python call has
// failed and set an exception. So we're clearing that exception since
// we'll be handling it by converting it to a C++ one. However let's warn
// if we were passed nullptr but *no* Python exception is set. We want to
// avoid that situation because it opens up the possibility of us clearing
// exceptions that aren't related to our nullptr.
if (!PyErr_Occurred()) {
Log(LogLevel::kWarning,
"A PythonRef acquire/steal call was passed nullptr but no Python "
"exception is set. This situation should be avoided; only pass "
"acquire/steal if it is directly due to a Python exception.");
} else {
PyErr_Clear();
}
}
PythonRef::PythonRef(PyObject* obj, ReferenceBehavior b) {
switch (b) {
case kSteal:
Steal(obj_in);
Steal(obj);
break;
case kStealSoft:
if (obj_in) {
Steal(obj_in);
}
StealSoft(obj);
break;
case kAcquire:
Acquire(obj_in);
Acquire(obj);
break;
case kAcquireSoft:
if (obj_in) {
Acquire(obj_in);
break;
}
AcquireSoft(obj);
break;
}
}
void PythonRef::Acquire(PyObject* obj_in) {
BA_PRECONDITION(obj_in);
assert(Python::HaveGIL());
// Assign and increment the new one before decrementing our old
// (in case its the same one or prev gets deallocated and accesses us
// somehow).
PyObject* prev = obj_;
Py_INCREF(obj_in);
obj_ = obj_in;
if (prev) {
Py_DECREF(prev);
}
}
void PythonRef::AcquireSoft(PyObject* obj_in) {
if (!obj_in) {
Release();
return;
}
Acquire(obj_in);
}
void PythonRef::Steal(PyObject* obj_in) {
BA_PRECONDITION(obj_in);
assert(Python::HaveGIL());
void PythonRef::SetObj(PyObject* obj_in) {
// Assign before decrementing the old
// (in case prev gets deallocated and accesses us somehow).
PyObject* prev = obj_;
@ -78,12 +63,46 @@ void PythonRef::Steal(PyObject* obj_in) {
}
}
void PythonRef::StealSoft(PyObject* obj_in) {
void PythonRef::Steal(PyObject* obj_in) {
assert(Python::HaveGIL());
if (!obj_in) {
ClearPythonExceptionAndWarnIfUnset();
throw Exception("nullptr passed to PythonRef::Steal.");
}
SetObj(obj_in);
}
void PythonRef::StealSoft(PyObject* obj_in) {
assert(Python::HaveGIL());
if (!obj_in) {
// 'Soft' versions don't assume nullptr is due to an exception,
// so we don't touch Python exception state here.
Release();
return;
}
Steal(obj_in);
SetObj(obj_in);
}
void PythonRef::Acquire(PyObject* obj_in) {
assert(Python::HaveGIL());
if (!obj_in) {
ClearPythonExceptionAndWarnIfUnset();
throw Exception("nullptr passed to PythonRef::Acquire.");
}
Py_INCREF(obj_in);
SetObj(obj_in);
}
void PythonRef::AcquireSoft(PyObject* obj_in) {
assert(Python::HaveGIL());
if (!obj_in) {
// 'Soft' versions don't assume nullptr is due to an exception,
// so we don't touch Python exception state here.
Release();
return;
}
Py_INCREF(obj_in);
SetObj(obj_in);
}
void PythonRef::Release() {

View File

@ -15,17 +15,24 @@ class PythonRef {
public:
/// Defines referencing behavior when creating new instances.
enum ReferenceBehavior {
/// Steal the provided object reference (and throw an Exception if it is
/// nullptr).
/// Steal the provided object reference. If nullptr is passed, it is
/// assumed to be due to a Python exception occurring. The Python
/// exception is then cleared, a C++ exception is raised, and a warning
/// is logged if no Python exception was set.
kSteal,
/// Steal the provided object reference or set as unreferenced if it is
/// nullptr.
/// Steal the provided object reference. If nullptr is passed, set as
/// unreferenced. Does not touch Python exception state, so be sure to
/// clear that yourself if the nullptr case is due to an Exception.
kStealSoft,
/// Acquire a new reference to the provided object (and throw an Exception
/// if it is nullptr).
/// Acquire a new reference to the provided object. If nullptr is
/// passed, it is assumed to be due to a Python exception occurring. The
/// Python exception is then cleared, a C++ exception is raised, and a
/// warning is logged if no Python exception was set.
kAcquire,
/// Acquire a new reference to the provided object or set as unreferenced if
/// it is nullptr.
/// Acquire a new reference to the provided object. If nullptr is
/// passed, set as unreferenced. Does not touch Python exception state,
/// so be sure to clear that yourself if the nullptr case is due to an
/// exception.
kAcquireSoft
};
@ -35,22 +42,22 @@ class PythonRef {
/// See ReferenceBehavior docs.
PythonRef(PyObject* obj, ReferenceBehavior behavior);
/// Shortcut to create a new PythonRef using ReferenceBehavior::kSteal.
/// Shortcut to create a new PythonRef using ReferenceBehavior::kSteal.
static auto Stolen(PyObject* obj) -> PythonRef {
return PythonRef(obj, ReferenceBehavior::kSteal);
return {obj, ReferenceBehavior::kSteal};
}
static auto StolenSoft(PyObject* obj) -> PythonRef {
return PythonRef(obj, ReferenceBehavior::kStealSoft);
return {obj, ReferenceBehavior::kStealSoft};
}
/// Shortcut to create a new PythonRef using ReferenceBehavior::kAcquire.
/// Shortcut to create a new PythonRef using ReferenceBehavior::kAcquire.
static auto Acquired(PyObject* obj) -> PythonRef {
return PythonRef(obj, ReferenceBehavior::kAcquire);
return {obj, ReferenceBehavior::kAcquire};
}
static auto AcquiredSoft(PyObject* obj) -> PythonRef {
return PythonRef(obj, ReferenceBehavior::kAcquireSoft);
return {obj, ReferenceBehavior::kAcquireSoft};
}
/// Copy constructor acquires a new reference (or sets as unreferenced)
@ -84,20 +91,28 @@ class PythonRef {
return !(*this == other);
}
/// Acquire a new reference to the passed object. Throws an exception if
/// nullptr is passed.
void Acquire(PyObject* obj);
/// Acquire a new reference to the passed object. Sets to null reference
/// if nullptr is passed.
void AcquireSoft(PyObject* obj);
/// Steal the passed reference. Throws an Exception if nullptr is passed.
/// Steal the provided object reference. If nullptr is passed, it is
/// assumed to be due to a Python exception occurring. The Python
/// exception is then cleared, a C++ exception is raised, and a warning is
/// logged if no Python exception was set.
void Steal(PyObject* obj);
/// Steal the passed reference. Sets to null reference if nullptr is passed.
/// Steal the provided object reference. If nullptr is passed, set as
/// unreferenced. Does not touch Python exception state, so be sure to
/// clear that yourself if the nullptr case is due to an Exception.
void StealSoft(PyObject* obj);
/// Acquire a new reference to the provided object. If nullptr is passed,
/// it is assumed to be due to a Python exception occurring. The Python
/// exception is then cleared, a C++ exception is raised, and a warning is
/// logged if no Python exception was set.
void Acquire(PyObject* obj);
/// Acquire a new reference to the provided object. If nullptr is passed,
/// set as unreferenced. Does not touch Python exception state, so be sure
/// to clear that yourself if the nullptr case is due to an exception.
void AcquireSoft(PyObject* obj);
/// Release the held reference (if one is held).
void Release();
@ -162,8 +177,8 @@ class PythonRef {
/// Throws an exception if unset.
auto UnicodeCheck() const -> bool;
/// Call the PyObject. On error, (optionally) prints errors and returns empty
/// ref.
/// Call the PyObject. On error, (optionally) prints errors and returns
/// empty ref.
auto Call(PyObject* args, PyObject* keywds = nullptr,
bool print_errors = true) const -> PythonRef;
auto Call(const PythonRef& args, const PythonRef& keywds = PythonRef(),
@ -177,6 +192,7 @@ class PythonRef {
private:
void ThrowIfUnset() const;
void SetObj(PyObject* obj);
PyObject* obj_{};
};

View File

@ -118,7 +118,7 @@ def generate_app_module(
# Set default app-mode-selection logic.
contents = (
'# Hmm; need to think about how we auto-construct this; how\n'
'# do we determine which app modes to check and in what\n'
'# should we determine which app modes to check and in what\n'
'# order?\n'
)
if 'scene_v1' in fsets: