mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-19 21:37:57 +08:00
various plugin subsystem improvements
This commit is contained in:
parent
3a6a35749a
commit
8ef31acc81
44
.efrocachemap
generated
44
.efrocachemap
generated
@ -430,12 +430,12 @@
|
||||
"build/assets/ba_data/data/languages/czech.json": "https://files.ballistica.net/cache/ba1/54/a2/91da0eec3c0820602d779ef24d10",
|
||||
"build/assets/ba_data/data/languages/danish.json": "https://files.ballistica.net/cache/ba1/3f/d6/9080783d5c9dcc0af737f02b6f1e",
|
||||
"build/assets/ba_data/data/languages/dutch.json": "https://files.ballistica.net/cache/ba1/22/b4/4a33bf81142ba2befad14eb5746e",
|
||||
"build/assets/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/0f/a5/14ed3ec7d80bf0a8751c0d764f64",
|
||||
"build/assets/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/d1/6e/8899211693c20d3b00fc198f58c6",
|
||||
"build/assets/ba_data/data/languages/esperanto.json": "https://files.ballistica.net/cache/ba1/0e/39/7cfa5f3fb8cef5f4a64f21cda880",
|
||||
"build/assets/ba_data/data/languages/filipino.json": "https://files.ballistica.net/cache/ba1/cb/49/1739273c68c82cebca0aee16d6c9",
|
||||
"build/assets/ba_data/data/languages/french.json": "https://files.ballistica.net/cache/ba1/51/89/e01389f8153497b56fbf0fa069c2",
|
||||
"build/assets/ba_data/data/languages/german.json": "https://files.ballistica.net/cache/ba1/22/a4/452043a401252ca66b703ce5d4aa",
|
||||
"build/assets/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/42/75/f30546475d6b7aa6599a9251973a",
|
||||
"build/assets/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/23/6f/8547ba09722b7c7f5b8333986984",
|
||||
"build/assets/ba_data/data/languages/greek.json": "https://files.ballistica.net/cache/ba1/a6/5d/78f912e9a89f98de004405167a6a",
|
||||
"build/assets/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/88/ee/0cda537bab9ac827def5e236fe1a",
|
||||
"build/assets/ba_data/data/languages/hungarian.json": "https://files.ballistica.net/cache/ba1/00/ba/cf1b8bb9f7914f64647d4665b0a8",
|
||||
@ -4068,26 +4068,26 @@
|
||||
"build/assets/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/2d/ef/5335207d41b21b9823f6805997f1",
|
||||
"build/assets/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/b0/8a/55e2e77623fe657bea24f223a3ae",
|
||||
"build/assets/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/86/5b/2af4d1e26a1a8073c89acb06e599",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/d1/5f/aa35974d118ac4a45de5d105edd5",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/85/7b/fa206153e23235bd97eb3abfb45a",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/c8/97/a251315351949cbe40914f3f02a3",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/7f/07/1ba1983b2ad7b1061f0c7624ee87",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/14/3b/984737685ea813a3a8d587fd9083",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/03/ee/59971d68ce248f89175441bc232c",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/69/0a/1a067c77c85e2a57fccdd553fe44",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/d8/af/beea487d4cd9b68e323af8190bdb",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/7f/aa/f77f3330bea584a9710f13485ac0",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/fa/1e/f0c6f0a6edf00bd4975c30b44957",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/eb/ca/e8aa9629a9da31c838e8d7dd0f9d",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/d0/13/08ed924a1032c25b5404f505398b",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/b6/10/dd86496c0e5eac41803e43c552d3",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/89/d2/087efa3672aa053f6456cf5d263f",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/aa/a5/7a4703567c6f754e2248c115fb09",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/71/c1/e52972ac5b26273ad841da7d664b",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/b3/2f/1af856ae52297f7952329efc7617",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/84/5c/b58affdbdae8e334f7dcff14c684",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/4b/e7/5964489b7d22e27a413c9edb9eb3",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/1e/c9/770a9e209a63de714aff7da7e8ee",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/63/44/27519c2a85ba72d2a4e338ed4650",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/3a/33/047dbfc05746818a9dfbc7668314",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/0b/64/e2da499eeba056e64bfce5dab20a",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/76/74/619b268bc7f8609a6f477fc9e6f6",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/8d/36/fbde0a1a43724a97e6478e27b3ab",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/b0/82/021314e49a35124a1d52f8f81c09",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/75/3b/0024994a18df5b10272161903d5f",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/f6/ca/a4db280a528841bccb7aaf3baa63",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/cb/64/d43244656d4310fe0145bef54627",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/44/22/737933ac2d7caaba5cd5fd13d88f",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/6c/77/715843f46cc2c3c1bdc0e21b7b55",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/a4/48/33d74871b0c39e60064d0ce267cf",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/a9/61/26f4c31c7b537e2fb557c4d87c0c",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/19/a2/888b33bcdc7f9d87dd2d1ce4c2fd",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/67/e0/be7233baecdbe2acc133a7e4b596",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/ec/9f/ba7a1e3d7c34d7a3392f31a9fabb",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/22/d7/105197eb744fb4ee35bc99af236e",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/1c/8c/727910c0c52e8f30c6262e57bd61",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/ab/79/6a873cc823621d63f79c2a0256ed",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/1f/5a/7a8a2804f0b49076e01081075a49",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/be/19/b5458933dfc7371d91ecfcd2e06f",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/4e/48/123b806cbe6ddb3d9a8368bbb4f8",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/be/19/b5458933dfc7371d91ecfcd2e06f",
|
||||
|
||||
2
.idea/dictionaries/ericf.xml
generated
2
.idea/dictionaries/ericf.xml
generated
@ -2133,6 +2133,8 @@
|
||||
<w>plugkeys</w>
|
||||
<w>pluglist</w>
|
||||
<w>plugnames</w>
|
||||
<w>plugspec</w>
|
||||
<w>plugspecs</w>
|
||||
<w>plugstate</w>
|
||||
<w>plugstates</w>
|
||||
<w>plusbutton</w>
|
||||
|
||||
23
CHANGELOG.md
23
CHANGELOG.md
@ -1,4 +1,4 @@
|
||||
### 1.7.21 (build 21148, api 8, 2023-06-26)
|
||||
### 1.7.21 (build 21150, api 8, 2023-06-26)
|
||||
|
||||
- Fixed an issue where server builds would not always include collision meshes.
|
||||
- Upgraded Python to 3.11.4 on Android builds.
|
||||
@ -9,6 +9,27 @@
|
||||
code creation will fail. This should keep the images reasonably readable and
|
||||
avoids a crash that could occur when more data was provided than could
|
||||
physically fit in the qr code.
|
||||
- `PotentialPlugin` has been renamed to `PluginSpec` and the list of them
|
||||
renamed from `babase.app.plugins.potential_plugins` to
|
||||
`babase.app.plugins.plugin_specs`.
|
||||
- Added a simpler warning message when plugins are found that need to be updated
|
||||
for the new api version.
|
||||
- Previously, the app would only check api version on plugins when initially
|
||||
registering them. This meant that once a plugin was enabled, the app would
|
||||
always try to load it even if api version stopped matching. This has been
|
||||
corrected; now if the api version doesn't match it will never be loaded.
|
||||
- Fixed an error where plugins nested more than one level such as
|
||||
`mypackage.myplugin.MyPlugin` would fail to load.
|
||||
- Removed the `display_name` attr from the `PluginSpec` class, as it was simply
|
||||
set to `class_path`. It seems that referring to plugins simply by their
|
||||
class-paths is a reasonable system for now.
|
||||
- Added `enabled`, `loadable`, `attempted_load` and `plugin` attrs to the
|
||||
`PluginSpec` class. This should make it easier to interact with the overall
|
||||
app plugin situation without having to do hacky raw config wrangling.
|
||||
- Plugins should now show up more sensibly in the plugins UI in some cases. For
|
||||
example, a plugin which was previously loading but no longer is after an
|
||||
api-version change will still show up in the list as red instead of not
|
||||
showing up at all.
|
||||
|
||||
### 1.7.20 (build 21140, api 8, 2023-06-22)
|
||||
|
||||
|
||||
2
ballisticakit-cmake/.idea/dictionaries/ericf.xml
generated
2
ballisticakit-cmake/.idea/dictionaries/ericf.xml
generated
@ -1246,6 +1246,8 @@
|
||||
<w>plen</w>
|
||||
<w>pluginsettings</w>
|
||||
<w>plugnames</w>
|
||||
<w>plugspec</w>
|
||||
<w>plugspecs</w>
|
||||
<w>plusnet</w>
|
||||
<w>pname</w>
|
||||
<w>pnamel</w>
|
||||
|
||||
@ -148,7 +148,7 @@ from babase._mgen.enums import (
|
||||
from babase._math import normalized_color, is_point_in_box, vec3validate
|
||||
from babase._meta import MetadataSubsystem
|
||||
from babase._net import get_ip_address_type, DEFAULT_REQUEST_TIMEOUT_SECONDS
|
||||
from babase._plugin import PotentialPlugin, Plugin, PluginSubsystem
|
||||
from babase._plugin import PluginSpec, Plugin, PluginSubsystem
|
||||
from babase._text import timestring
|
||||
|
||||
_babase.app = app = App()
|
||||
@ -252,7 +252,7 @@ __all__ = [
|
||||
'PlayerNotFoundError',
|
||||
'Plugin',
|
||||
'PluginSubsystem',
|
||||
'PotentialPlugin',
|
||||
'PluginSpec',
|
||||
'print_error',
|
||||
'print_exception',
|
||||
'print_load_info',
|
||||
|
||||
@ -40,8 +40,8 @@ class ScanResults:
|
||||
"""Final results from a meta-scan."""
|
||||
|
||||
exports: dict[str, list[str]] = field(default_factory=dict)
|
||||
errors: list[str] = field(default_factory=list)
|
||||
warnings: list[str] = field(default_factory=list)
|
||||
incorrect_api_modules: list[str] = field(default_factory=list)
|
||||
announce_errors_occurred: bool = False
|
||||
|
||||
def exports_of_class(self, cls: type) -> list[str]:
|
||||
"""Return exports of a given class."""
|
||||
@ -181,8 +181,9 @@ class MetadataSubsystem:
|
||||
self._scan.run()
|
||||
results = self._scan.results
|
||||
self._scan = None
|
||||
except Exception as exc:
|
||||
results = ScanResults(errors=[f'Scan exception: {exc}'])
|
||||
except Exception:
|
||||
logging.exception('metascan: Error running scan in bg.')
|
||||
results = ScanResults(announce_errors_occurred=True)
|
||||
|
||||
# Place results and tell the logic thread they're ready.
|
||||
self.scanresults = results
|
||||
@ -197,28 +198,44 @@ class MetadataSubsystem:
|
||||
results = self.scanresults
|
||||
assert results is not None
|
||||
|
||||
# Spit out any warnings/errors that happened.
|
||||
# Warnings generally only get printed locally for users' benefit
|
||||
# (things like out-of-date scripts being ignored, etc.)
|
||||
# Errors are more serious and will get included in the regular log.
|
||||
if results.warnings or results.errors:
|
||||
import textwrap
|
||||
do_play_error_sound = False
|
||||
|
||||
# If we found modules needing to be updated to the newer api version,
|
||||
# mention that specifically.
|
||||
if results.incorrect_api_modules:
|
||||
if len(results.incorrect_api_modules) > 1:
|
||||
msg = Lstr(
|
||||
resource='scanScriptsMultipleModulesNeedUpdatesText',
|
||||
subs=[
|
||||
('${PATH}', results.incorrect_api_modules[0]),
|
||||
(
|
||||
'${NUM}',
|
||||
str(len(results.incorrect_api_modules) - 1),
|
||||
),
|
||||
('${API}', str(CURRENT_API_VERSION)),
|
||||
],
|
||||
)
|
||||
else:
|
||||
msg = Lstr(
|
||||
resource='scanScriptsSingleModuleNeedsUpdatesText',
|
||||
subs=[
|
||||
('${PATH}', results.incorrect_api_modules[0]),
|
||||
('${API}', str(CURRENT_API_VERSION)),
|
||||
],
|
||||
)
|
||||
_babase.screenmessage(msg, color=(1, 0, 0))
|
||||
do_play_error_sound = True
|
||||
|
||||
# Let the user know if there's warning/errors in their log
|
||||
# they may want to look at.
|
||||
if results.announce_errors_occurred:
|
||||
_babase.screenmessage(
|
||||
Lstr(resource='scanScriptsErrorText'), color=(1, 0, 0)
|
||||
)
|
||||
_babase.getsimplesound('error').play()
|
||||
do_play_error_sound = True
|
||||
|
||||
if results.warnings:
|
||||
allwarnings = textwrap.indent(
|
||||
'\n'.join(results.warnings), 'Warning (meta-scan): '
|
||||
)
|
||||
logging.warning(allwarnings)
|
||||
if results.errors:
|
||||
allerrors = textwrap.indent(
|
||||
'\n'.join(results.errors), 'Error (meta-scan): '
|
||||
)
|
||||
logging.error(allerrors)
|
||||
if do_play_error_sound:
|
||||
_babase.getsimplesound('error').play()
|
||||
|
||||
# Let the game know we're done.
|
||||
assert self._scan_complete_cb is not None
|
||||
@ -262,11 +279,7 @@ class DirectoryScan:
|
||||
try:
|
||||
self._scan_module(moduledir, subpath)
|
||||
except Exception:
|
||||
import traceback
|
||||
|
||||
self.results.warnings.append(
|
||||
f"Error scanning '{subpath}': " + traceback.format_exc()
|
||||
)
|
||||
logging.exception("metascan: Error scanning '%s'.", subpath)
|
||||
|
||||
# Sort our results
|
||||
for exportlist in self.results.exports.values():
|
||||
@ -289,9 +302,10 @@ class DirectoryScan:
|
||||
except PermissionError:
|
||||
# Expected sometimes.
|
||||
entries = []
|
||||
except Exception as exc:
|
||||
except Exception:
|
||||
# Unexpected; report this.
|
||||
self.results.errors.append(str(exc))
|
||||
logging.exception('metascan: Error in _get_path_module_entries.')
|
||||
self.results.announce_errors_occurred = True
|
||||
entries = []
|
||||
|
||||
# Now identify python packages/modules out of what we found.
|
||||
@ -331,9 +345,15 @@ class DirectoryScan:
|
||||
# If we find a module requiring a different api version, warn
|
||||
# and ignore.
|
||||
if required_api is not None and required_api != CURRENT_API_VERSION:
|
||||
self.results.warnings.append(
|
||||
f'{subpath} requires api {required_api} but'
|
||||
f' we are running {CURRENT_API_VERSION}. Ignoring module.'
|
||||
logging.warning(
|
||||
'metascan: %s requires api %s but we are running'
|
||||
' %s. Ignoring module.',
|
||||
subpath,
|
||||
required_api,
|
||||
CURRENT_API_VERSION,
|
||||
)
|
||||
self.results.incorrect_api_modules.append(
|
||||
self._module_name_for_subpath(subpath)
|
||||
)
|
||||
return
|
||||
|
||||
@ -349,11 +369,13 @@ class DirectoryScan:
|
||||
if submodule[1].name != '__init__.py':
|
||||
self._scan_module(submodule[0], submodule[1])
|
||||
except Exception:
|
||||
import traceback
|
||||
logging.exception('metascan: Error scanning %s.', subpath)
|
||||
|
||||
self.results.warnings.append(
|
||||
f"Error scanning '{subpath}': {traceback.format_exc()}"
|
||||
)
|
||||
def _module_name_for_subpath(self, subpath: Path) -> str:
|
||||
# (should not be getting these)
|
||||
assert '__init__.py' not in str(subpath)
|
||||
|
||||
return '.'.join(subpath.parts).removesuffix('.py')
|
||||
|
||||
def _process_module_meta_tags(
|
||||
self, subpath: Path, flines: list[str], meta_lines: dict[int, list[str]]
|
||||
@ -363,10 +385,12 @@ class DirectoryScan:
|
||||
# meta_lines is just anything containing '# ba_meta '; make sure
|
||||
# the ba_meta is in the right place.
|
||||
if mline[0] != 'ba_meta':
|
||||
self.results.warnings.append(
|
||||
f'Warning: {subpath}:'
|
||||
f' malformed ba_meta statement on line {lindex + 1}.'
|
||||
logging.warning(
|
||||
'metascan: %s:%d: malformed ba_meta statement.',
|
||||
subpath,
|
||||
lindex + 1,
|
||||
)
|
||||
self.results.announce_errors_occurred = True
|
||||
elif (
|
||||
len(mline) == 4 and mline[1] == 'require' and mline[2] == 'api'
|
||||
):
|
||||
@ -375,15 +399,15 @@ class DirectoryScan:
|
||||
elif len(mline) != 3 or mline[1] != 'export':
|
||||
# Currently we only support 'ba_meta export FOO';
|
||||
# complain for anything else we see.
|
||||
self.results.warnings.append(
|
||||
f'Warning: {subpath}'
|
||||
f': unrecognized ba_meta statement on line {lindex + 1}.'
|
||||
logging.warning(
|
||||
'metascan: %s:%d: unrecognized ba_meta statement.',
|
||||
subpath,
|
||||
lindex + 1,
|
||||
)
|
||||
self.results.announce_errors_occurred = True
|
||||
else:
|
||||
# Looks like we've got a valid export line!
|
||||
modulename = '.'.join(subpath.parts)
|
||||
if subpath.name.endswith('.py'):
|
||||
modulename = modulename[:-3]
|
||||
modulename = self._module_name_for_subpath(subpath)
|
||||
exporttypestr = mline[2]
|
||||
export_class_name = self._get_export_class_name(
|
||||
subpath, flines, lindex
|
||||
@ -395,11 +419,14 @@ class DirectoryScan:
|
||||
# classes we need to migrate people to using base
|
||||
# class names for them.
|
||||
if exporttypestr == 'game':
|
||||
self.results.warnings.append(
|
||||
f'{subpath}:'
|
||||
" '# ba_meta export game' tag should be replaced by"
|
||||
f" '# ba_meta export bascenev1.GameActivity'."
|
||||
logging.warning(
|
||||
"metascan: %s:%d: '# ba_meta export"
|
||||
" game' tag should be replaced by '# ba_meta"
|
||||
" export bascenev1.GameActivity'.",
|
||||
subpath,
|
||||
lindex + 1,
|
||||
)
|
||||
self.results.announce_errors_occurred = True
|
||||
else:
|
||||
# If export type is one of our shortcuts, sub in the
|
||||
# actual class path. Otherwise assume its a classpath
|
||||
@ -434,10 +461,13 @@ class DirectoryScan:
|
||||
classname = cbits[0]
|
||||
break # Success!
|
||||
if classname is None:
|
||||
self.results.warnings.append(
|
||||
f'Warning: {subpath}: class definition not found below'
|
||||
f' "ba_meta export" statement on line {lindexorig + 1}.'
|
||||
logging.warning(
|
||||
'metascan: %s:%d: class definition not found below'
|
||||
" 'ba_meta export' statement.",
|
||||
subpath,
|
||||
lindexorig + 1,
|
||||
)
|
||||
self.results.announce_errors_occurred = True
|
||||
return classname
|
||||
|
||||
def _get_api_requirement(
|
||||
@ -460,23 +490,26 @@ class DirectoryScan:
|
||||
and l[3].isdigit()
|
||||
]
|
||||
|
||||
# We're successful if we find exactly one properly formatted line.
|
||||
# We're successful if we find exactly one properly formatted
|
||||
# line.
|
||||
if len(lines) == 1:
|
||||
return int(lines[0][3])
|
||||
|
||||
# Ok; not successful. lets issue warnings for a few error cases.
|
||||
if len(lines) > 1:
|
||||
self.results.warnings.append(
|
||||
f'Warning: {subpath}: multiple'
|
||||
' "# ba_meta require api <NUM>" lines found;'
|
||||
' ignoring module.'
|
||||
logging.warning(
|
||||
"metascan: %s: multiple '# ba_meta require api <NUM>'"
|
||||
' lines found; ignoring module.',
|
||||
subpath,
|
||||
)
|
||||
self.results.announce_errors_occurred = True
|
||||
elif not lines and toplevel and meta_lines:
|
||||
# If we're a top-level module containing meta lines but
|
||||
# no valid "require api" line found, complain.
|
||||
self.results.warnings.append(
|
||||
f'Warning: {subpath}:'
|
||||
' no valid "# ba_meta require api <NUM>" line found;'
|
||||
' ignoring module.'
|
||||
# If we're a top-level module containing meta lines but no
|
||||
# valid "require api" line found, complain.
|
||||
logging.warning(
|
||||
"metascan: %s: no valid '# ba_meta require api <NUM>"
|
||||
' line found; ignoring module.',
|
||||
subpath,
|
||||
)
|
||||
self.results.announce_errors_occurred = True
|
||||
return None
|
||||
|
||||
@ -7,7 +7,6 @@ from __future__ import annotations
|
||||
import logging
|
||||
import importlib.util
|
||||
from typing import TYPE_CHECKING
|
||||
from dataclasses import dataclass
|
||||
|
||||
import _babase
|
||||
from babase._appsubsystem import AppSubsystem
|
||||
@ -31,14 +30,21 @@ class PluginSubsystem(AppSubsystem):
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.potential_plugins: list[babase.PotentialPlugin] = []
|
||||
self.active_plugins: dict[str, babase.Plugin] = {}
|
||||
|
||||
# Info about plugins that we are aware of. This may include
|
||||
# plugins discovered through meta-scanning as well as plugins
|
||||
# registered in the app-config. This may include plugins that
|
||||
# cannot be loaded for various reasons or that have been
|
||||
# intentionally disabled.
|
||||
self.plugin_specs: dict[str, babase.PluginSpec] = {}
|
||||
|
||||
# The set of live active plugin objects.
|
||||
self.active_plugins: list[babase.Plugin] = []
|
||||
|
||||
def on_meta_scan_complete(self) -> None:
|
||||
"""Should be called when meta-scanning is complete."""
|
||||
"""Called when meta-scanning is complete."""
|
||||
from babase._language import Lstr
|
||||
|
||||
plugs = _babase.app.plugins
|
||||
config_changed = False
|
||||
found_new = False
|
||||
plugstates: dict[str, dict] = _babase.app.config.setdefault(
|
||||
@ -56,146 +62,75 @@ class PluginSubsystem(AppSubsystem):
|
||||
)
|
||||
is True
|
||||
)
|
||||
# Create a potential-plugin for each class we found in the scan.
|
||||
|
||||
assert not self.plugin_specs
|
||||
assert not self.active_plugins
|
||||
|
||||
# Create a plugin-spec for each plugin class we found in the
|
||||
# meta-scan.
|
||||
for class_path in results.exports_of_class(Plugin):
|
||||
plugs.potential_plugins.append(
|
||||
PotentialPlugin(
|
||||
display_name=Lstr(value=class_path),
|
||||
class_path=class_path,
|
||||
available=True,
|
||||
)
|
||||
assert class_path not in self.plugin_specs
|
||||
plugspec = self.plugin_specs[class_path] = PluginSpec(
|
||||
class_path=class_path, loadable=True
|
||||
)
|
||||
|
||||
# Auto-enable new ones if desired.
|
||||
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.
|
||||
# they can also disable them in settings so they never load.
|
||||
plugstates[class_path] = {'enabled': True}
|
||||
plugspec.enabled = True
|
||||
config_changed = True
|
||||
found_new = True
|
||||
|
||||
plugs.potential_plugins.sort(key=lambda p: p.class_path)
|
||||
|
||||
# If we're *not* auto-enabling new plugins, at least let the
|
||||
# user know we found something new.
|
||||
# If we're *not* auto-enabling, just let the user know if we
|
||||
# found new ones.
|
||||
if found_new and not auto_enable_new_plugins:
|
||||
_babase.screenmessage(
|
||||
Lstr(resource='pluginsDetectedText'), color=(0, 1, 0)
|
||||
)
|
||||
_babase.getsimplesound('ding').play()
|
||||
|
||||
if config_changed:
|
||||
_babase.app.config.commit()
|
||||
|
||||
def on_app_running(self) -> None:
|
||||
# Load up our plugins and go ahead and call their on_app_running calls.
|
||||
self.load_plugins()
|
||||
for plugin in self.active_plugins.values():
|
||||
try:
|
||||
plugin.on_app_running()
|
||||
except Exception:
|
||||
from babase import _error
|
||||
|
||||
_error.print_exception('Error in plugin on_app_running()')
|
||||
|
||||
def on_app_pause(self) -> None:
|
||||
for plugin in self.active_plugins.values():
|
||||
try:
|
||||
plugin.on_app_pause()
|
||||
except Exception:
|
||||
from babase import _error
|
||||
|
||||
_error.print_exception('Error in plugin on_app_pause()')
|
||||
|
||||
def on_app_resume(self) -> None:
|
||||
for plugin in self.active_plugins.values():
|
||||
try:
|
||||
plugin.on_app_resume()
|
||||
except Exception:
|
||||
from babase import _error
|
||||
|
||||
_error.print_exception('Error in plugin on_app_resume()')
|
||||
|
||||
def on_app_shutdown(self) -> None:
|
||||
for plugin in self.active_plugins.values():
|
||||
try:
|
||||
plugin.on_app_shutdown()
|
||||
except Exception:
|
||||
from babase import _error
|
||||
|
||||
_error.print_exception('Error in plugin on_app_shutdown()')
|
||||
|
||||
def load_plugins(self) -> None:
|
||||
"""(internal)"""
|
||||
from babase._general import getclass
|
||||
from babase._language import Lstr
|
||||
|
||||
# Note: the plugins we load is purely based on what's enabled
|
||||
# in the app config. Its not our job to look at meta stuff here.
|
||||
plugstates: dict[str, dict] = _babase.app.config.get('Plugins', {})
|
||||
# Ok, now go through all plugins registered in the app-config
|
||||
# that weren't covered by the meta stuff above, either creating
|
||||
# plugin-specs for them or clearing them out. This covers
|
||||
# plugins with api versions not matching ours, plugins without
|
||||
# ba_meta tags, and plugins that have since disappeared.
|
||||
assert isinstance(plugstates, dict)
|
||||
plugkeys: list[str] = sorted(
|
||||
key for key, val in plugstates.items() if val.get('enabled', False)
|
||||
)
|
||||
wrong_api_prefixes = [f'{m}.' for m in results.incorrect_api_modules]
|
||||
|
||||
disappeared_plugs: set[str] = set()
|
||||
for plugkey in plugkeys:
|
||||
# Originally I was just catching ModuleNotFoundError on the
|
||||
# getclass() call to detect plugins disappearing. However
|
||||
# this breaks if the module *does* exist but itself imports
|
||||
# something that does not exist; in that case we would
|
||||
# incorrectly show that the plugin had disappeared.
|
||||
#
|
||||
# So now we're first explicitly asking Python if it can
|
||||
# locate the module, and if it can then we treat any further
|
||||
# errors including ModuleNotFound as problems with the
|
||||
# module's code; not ours.
|
||||
|
||||
for class_path in sorted(plugstates.keys()):
|
||||
# Already have a spec for it; nothing to be done.
|
||||
if class_path in self.plugin_specs:
|
||||
continue
|
||||
|
||||
# If this plugin corresponds to any modules that we've
|
||||
# identified as having incorrect api versions, we'll take
|
||||
# note of its existence but we won't try to load it.
|
||||
if any(
|
||||
class_path.startswith(prefix) for prefix in wrong_api_prefixes
|
||||
):
|
||||
plugspec = self.plugin_specs[class_path] = PluginSpec(
|
||||
class_path=class_path, loadable=False
|
||||
)
|
||||
continue
|
||||
|
||||
# Ok, it seems to be a class we have no metadata for. Look
|
||||
# to see if it appears to be an actual class we could
|
||||
# theoretically load. If so, we'll try. If not, we consider
|
||||
# the plugin to have disappeared and inform the user as
|
||||
# such.
|
||||
try:
|
||||
spec = importlib.util.find_spec(plugkey.split('.')[0])
|
||||
spec = importlib.util.find_spec(
|
||||
'.'.join(class_path.split('.')[:-1])
|
||||
)
|
||||
except Exception:
|
||||
spec = None
|
||||
|
||||
if spec is None:
|
||||
disappeared_plugs.add(plugkey)
|
||||
disappeared_plugs.add(class_path)
|
||||
continue
|
||||
|
||||
# Ok; it seems that there's *something* there. Now try to load
|
||||
# it and treat any further errors as the module's fault.
|
||||
try:
|
||||
cls = getclass(plugkey, Plugin)
|
||||
except Exception as exc:
|
||||
_babase.getsimplesound('error').play()
|
||||
_babase.screenmessage(
|
||||
Lstr(
|
||||
resource='pluginClassLoadErrorText',
|
||||
subs=[
|
||||
('${PLUGIN}', plugkey),
|
||||
('${ERROR}', str(exc)),
|
||||
],
|
||||
),
|
||||
color=(1, 0, 0),
|
||||
)
|
||||
logging.exception("Error loading plugin class '%s'.", plugkey)
|
||||
continue
|
||||
try:
|
||||
plugin = cls()
|
||||
assert plugkey not in self.active_plugins
|
||||
self.active_plugins[plugkey] = plugin
|
||||
except Exception as exc:
|
||||
from babase import _error
|
||||
|
||||
_babase.getsimplesound('error').play()
|
||||
_babase.screenmessage(
|
||||
Lstr(
|
||||
resource='pluginInitErrorText',
|
||||
subs=[
|
||||
('${PLUGIN}', plugkey),
|
||||
('${ERROR}', str(exc)),
|
||||
],
|
||||
),
|
||||
color=(1, 0, 0),
|
||||
)
|
||||
_error.print_exception(f"Error initing plugin: '{plugkey}'.")
|
||||
|
||||
# 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
|
||||
@ -220,22 +155,151 @@ class PluginSubsystem(AppSubsystem):
|
||||
del _babase.app.config['Plugins'][goneplug]
|
||||
_babase.app.config.commit()
|
||||
|
||||
if config_changed:
|
||||
_babase.app.config.commit()
|
||||
|
||||
@dataclass
|
||||
class PotentialPlugin:
|
||||
"""Represents a babase.Plugin which can potentially be loaded.
|
||||
def on_app_running(self) -> None:
|
||||
# Load up our plugins and go ahead and call their on_app_running
|
||||
# calls.
|
||||
self.load_plugins()
|
||||
for plugin in self.active_plugins:
|
||||
try:
|
||||
plugin.on_app_running()
|
||||
except Exception:
|
||||
from babase import _error
|
||||
|
||||
_error.print_exception('Error in plugin on_app_running()')
|
||||
|
||||
def on_app_pause(self) -> None:
|
||||
for plugin in self.active_plugins:
|
||||
try:
|
||||
plugin.on_app_pause()
|
||||
except Exception:
|
||||
from babase import _error
|
||||
|
||||
_error.print_exception('Error in plugin on_app_pause()')
|
||||
|
||||
def on_app_resume(self) -> None:
|
||||
for plugin in self.active_plugins:
|
||||
try:
|
||||
plugin.on_app_resume()
|
||||
except Exception:
|
||||
from babase import _error
|
||||
|
||||
_error.print_exception('Error in plugin on_app_resume()')
|
||||
|
||||
def on_app_shutdown(self) -> None:
|
||||
for plugin in self.active_plugins:
|
||||
try:
|
||||
plugin.on_app_shutdown()
|
||||
except Exception:
|
||||
from babase import _error
|
||||
|
||||
_error.print_exception('Error in plugin on_app_shutdown()')
|
||||
|
||||
def load_plugins(self) -> None:
|
||||
"""(internal)"""
|
||||
|
||||
# Load plugins from any specs that are enabled & able to.
|
||||
for _class_path, plug_spec in sorted(self.plugin_specs.items()):
|
||||
plugin = plug_spec.attempt_load_if_enabled()
|
||||
if plugin is not None:
|
||||
self.active_plugins.append(plugin)
|
||||
|
||||
|
||||
class PluginSpec:
|
||||
"""Represents a plugin the engine knows about.
|
||||
|
||||
Category: **App Classes**
|
||||
|
||||
These generally represent plugins which were detected by the
|
||||
meta-tag scan. However they may also represent plugins which
|
||||
were previously set to be loaded but which were unable to be
|
||||
for some reason. In that case, 'available' will be set to False.
|
||||
The 'enabled' attr represents whether this plugin is set to load.
|
||||
Getting or setting that attr affects the corresponding app-config
|
||||
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
|
||||
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,
|
||||
or if the user has explicitly disabled a plugin, the engine will not
|
||||
even attempt to load it.
|
||||
"""
|
||||
|
||||
display_name: babase.Lstr
|
||||
class_path: str
|
||||
available: bool
|
||||
def __init__(self, class_path: str, loadable: bool):
|
||||
self.class_path = class_path
|
||||
self.loadable = loadable
|
||||
self.attempted_load = False
|
||||
self.plugin: Plugin | None = None
|
||||
|
||||
@property
|
||||
def enabled(self) -> bool:
|
||||
"""Whether the user wants this plugin to load."""
|
||||
plugstates: dict[str, dict] = _babase.app.config.get('Plugins', {})
|
||||
assert isinstance(plugstates, dict)
|
||||
val = plugstates.get(self.class_path, {}).get('enabled', False) is True
|
||||
return val
|
||||
|
||||
@enabled.setter
|
||||
def enabled(self, val: bool) -> None:
|
||||
plugstates: dict[str, dict] = _babase.app.config.setdefault(
|
||||
'Plugins', {}
|
||||
)
|
||||
assert isinstance(plugstates, dict)
|
||||
plugstate = plugstates.setdefault(self.class_path, {})
|
||||
plugstate['enabled'] = val
|
||||
|
||||
def attempt_load_if_enabled(self) -> Plugin | None:
|
||||
"""Possibly load the plugin and report errors."""
|
||||
from babase._general import getclass
|
||||
from babase._language import Lstr
|
||||
|
||||
assert not self.attempted_load
|
||||
assert self.plugin is None
|
||||
|
||||
if not self.enabled:
|
||||
return None
|
||||
self.attempted_load = True
|
||||
if not self.loadable:
|
||||
return None
|
||||
try:
|
||||
cls = getclass(self.class_path, Plugin)
|
||||
except Exception as exc:
|
||||
_babase.getsimplesound('error').play()
|
||||
_babase.screenmessage(
|
||||
Lstr(
|
||||
resource='pluginClassLoadErrorText',
|
||||
subs=[
|
||||
('${PLUGIN}', self.class_path),
|
||||
('${ERROR}', str(exc)),
|
||||
],
|
||||
),
|
||||
color=(1, 0, 0),
|
||||
)
|
||||
logging.exception(
|
||||
"Error loading plugin class '%s'.", self.class_path
|
||||
)
|
||||
return None
|
||||
try:
|
||||
self.plugin = cls()
|
||||
return self.plugin
|
||||
except Exception as exc:
|
||||
from babase import _error
|
||||
|
||||
_babase.getsimplesound('error').play()
|
||||
_babase.screenmessage(
|
||||
Lstr(
|
||||
resource='pluginInitErrorText',
|
||||
subs=[
|
||||
('${PLUGIN}', self.class_path),
|
||||
('${ERROR}', str(exc)),
|
||||
],
|
||||
),
|
||||
color=(1, 0, 0),
|
||||
)
|
||||
logging.exception(
|
||||
"Error initing plugin class: '%s'.", self.class_path
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
class Plugin:
|
||||
|
||||
@ -28,7 +28,7 @@ if TYPE_CHECKING:
|
||||
|
||||
# Build number and version of the ballistica binary we expect to be
|
||||
# using.
|
||||
TARGET_BALLISTICA_BUILD = 21148
|
||||
TARGET_BALLISTICA_BUILD = 21150
|
||||
TARGET_BALLISTICA_VERSION = '1.7.21'
|
||||
|
||||
_g_env_config: EnvConfig | None = None
|
||||
|
||||
@ -66,7 +66,7 @@ from babase import (
|
||||
NotFoundError,
|
||||
Permission,
|
||||
Plugin,
|
||||
PotentialPlugin,
|
||||
PluginSpec,
|
||||
pushcall,
|
||||
quit,
|
||||
request_permission,
|
||||
@ -187,7 +187,7 @@ __all__ = [
|
||||
'open_url',
|
||||
'Permission',
|
||||
'Plugin',
|
||||
'PotentialPlugin',
|
||||
'PluginSpec',
|
||||
'pushcall',
|
||||
'quit',
|
||||
'request_permission',
|
||||
|
||||
@ -179,13 +179,13 @@ class PluginWindow(bui.Window):
|
||||
'Still scanning plugins; please try again.', color=(1, 0, 0)
|
||||
)
|
||||
bui.getsound('error').play()
|
||||
pluglist = bui.app.plugins.potential_plugins
|
||||
plugstates: dict[str, dict] = bui.app.config.setdefault('Plugins', {})
|
||||
plugspecs = bui.app.plugins.plugin_specs
|
||||
plugstates: dict[str, dict] = bui.app.config.get('Plugins', {})
|
||||
assert isinstance(plugstates, dict)
|
||||
|
||||
plug_line_height = 50
|
||||
sub_width = self._scroll_width
|
||||
sub_height = len(pluglist) * plug_line_height
|
||||
sub_height = len(plugspecs) * plug_line_height
|
||||
self._subcontainer = bui.containerwidget(
|
||||
parent=self._scrollwidget,
|
||||
size=(sub_width, sub_height),
|
||||
@ -197,9 +197,7 @@ class PluginWindow(bui.Window):
|
||||
)
|
||||
self._restore_state()
|
||||
|
||||
def _check_value_changed(
|
||||
self, plug: bui.PotentialPlugin, value: bool
|
||||
) -> None:
|
||||
def _check_value_changed(self, plug: bui.PluginSpec, value: bool) -> None:
|
||||
bui.screenmessage(
|
||||
bui.Lstr(resource='settingsWindowAdvanced.mustRestartText'),
|
||||
color=(1.0, 0.5, 0.0),
|
||||
@ -266,7 +264,7 @@ class PluginWindow(bui.Window):
|
||||
# pylint: disable=too-many-locals
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-statements
|
||||
pluglist = bui.app.plugins.potential_plugins
|
||||
plugspecs = bui.app.plugins.plugin_specs
|
||||
plugstates: dict[str, dict] = bui.app.config.setdefault('Plugins', {})
|
||||
assert isinstance(plugstates, dict)
|
||||
|
||||
@ -275,17 +273,18 @@ class PluginWindow(bui.Window):
|
||||
num_enabled = 0
|
||||
num_disabled = 0
|
||||
|
||||
for i, availplug in enumerate(pluglist):
|
||||
plugspecs_sorted = sorted(plugspecs.items())
|
||||
|
||||
for _classpath, plugspec in plugspecs_sorted:
|
||||
# counting number of enabled and disabled plugins
|
||||
plugstate = plugstates.setdefault(availplug.class_path, {})
|
||||
enabled = plugstate.get('enabled', False)
|
||||
if enabled:
|
||||
# plugstate = plugstates.setdefault(plugspec[0], {})
|
||||
if plugspec.enabled:
|
||||
num_enabled += 1
|
||||
elif availplug.available and not enabled:
|
||||
else:
|
||||
num_disabled += 1
|
||||
|
||||
if self._category is Category.ALL:
|
||||
sub_height = len(pluglist) * plug_line_height
|
||||
sub_height = len(plugspecs) * plug_line_height
|
||||
bui.containerwidget(
|
||||
edit=self._subcontainer, size=(self._scroll_width, sub_height)
|
||||
)
|
||||
@ -305,20 +304,16 @@ class PluginWindow(bui.Window):
|
||||
sub_height = 0
|
||||
|
||||
num_shown = 0
|
||||
for i, availplug in enumerate(pluglist):
|
||||
plugin = bui.app.plugins.active_plugins.get(availplug.class_path)
|
||||
active = plugin is not None
|
||||
|
||||
plugstate = plugstates.setdefault(availplug.class_path, {})
|
||||
checked = plugstate.get('enabled', False)
|
||||
assert isinstance(checked, bool)
|
||||
for classpath, plugspec in plugspecs_sorted:
|
||||
plugin = plugspec.plugin
|
||||
enabled = plugspec.enabled
|
||||
|
||||
if self._category is Category.ALL:
|
||||
show = True
|
||||
elif self._category is Category.ENABLED:
|
||||
show = checked
|
||||
show = enabled
|
||||
elif self._category is Category.DISABLED:
|
||||
show = availplug.available and not checked
|
||||
show = not enabled
|
||||
else:
|
||||
assert_never(self._category)
|
||||
show = False
|
||||
@ -329,25 +324,24 @@ class PluginWindow(bui.Window):
|
||||
item_y = sub_height - (num_shown + 1) * plug_line_height
|
||||
check = bui.checkboxwidget(
|
||||
parent=self._subcontainer,
|
||||
text=availplug.display_name,
|
||||
text=bui.Lstr(value=classpath),
|
||||
autoselect=True,
|
||||
value=checked,
|
||||
value=enabled,
|
||||
maxwidth=self._scroll_width - 200,
|
||||
position=(10, item_y),
|
||||
size=(self._scroll_width - 40, 50),
|
||||
on_value_change_call=bui.Call(
|
||||
self._check_value_changed, availplug
|
||||
self._check_value_changed, plugspec
|
||||
),
|
||||
textcolor=(
|
||||
(0.8, 0.3, 0.3)
|
||||
if not availplug.available
|
||||
else (0, 1, 0)
|
||||
if active and checked
|
||||
else (0.8, 0.3, 0.3)
|
||||
if checked
|
||||
if (plugspec.attempted_load and plugspec.plugin is None)
|
||||
else (0.6, 0.6, 0.6)
|
||||
if plugspec.plugin is None
|
||||
else (0, 1, 0)
|
||||
),
|
||||
)
|
||||
# noinspection PyUnresolvedReferences
|
||||
if plugin is not None and plugin.has_settings_ui():
|
||||
button = bui.buttonwidget(
|
||||
parent=self._subcontainer,
|
||||
@ -356,6 +350,7 @@ class PluginWindow(bui.Window):
|
||||
size=(100, 40),
|
||||
position=(sub_width - 130, item_y + 6),
|
||||
)
|
||||
# noinspection PyUnresolvedReferences
|
||||
bui.buttonwidget(
|
||||
edit=button,
|
||||
on_activate_call=bui.Call(plugin.show_settings_ui, button),
|
||||
@ -364,7 +359,7 @@ class PluginWindow(bui.Window):
|
||||
button = None
|
||||
|
||||
# Allow getting back to back button.
|
||||
if i == 0:
|
||||
if num_shown == 0:
|
||||
bui.widget(
|
||||
edit=check,
|
||||
up_widget=self._back_button,
|
||||
|
||||
@ -663,19 +663,19 @@ auto CorePlatform::GetTextTextureData(void* tex) -> uint8_t* {
|
||||
void CorePlatform::OnMainThreadStartApp() {}
|
||||
|
||||
void CorePlatform::OnAppStart() {
|
||||
assert(g_base_soft && g_base_soft->InLogicThread());
|
||||
// assert(g_base_soft && g_base_soft->InLogicThread());
|
||||
}
|
||||
|
||||
void CorePlatform::OnAppPause() {
|
||||
assert(g_base_soft && g_base_soft->InLogicThread());
|
||||
// assert(g_base_soft && g_base_soft->InLogicThread());
|
||||
}
|
||||
|
||||
void CorePlatform::OnAppResume() {
|
||||
assert(g_base_soft && g_base_soft->InLogicThread());
|
||||
// assert(g_base_soft && g_base_soft->InLogicThread());
|
||||
}
|
||||
|
||||
void CorePlatform::OnAppShutdown() {
|
||||
assert(g_base_soft && g_base_soft->InLogicThread());
|
||||
// assert(g_base_soft && g_base_soft->InLogicThread());
|
||||
}
|
||||
|
||||
void CorePlatform::OnScreenSizeChange() {
|
||||
|
||||
@ -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 = 21148;
|
||||
const int kEngineBuildNumber = 21150;
|
||||
const char* kEngineVersion = "1.7.21";
|
||||
|
||||
auto MonolithicMain(const core::CoreConfig& core_config) -> int {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user