mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-02-07 16:13:23 +08:00
Cleaned up plugin subsystem
This commit is contained in:
parent
0f9fe41542
commit
05c93cc776
@ -1,6 +1,8 @@
|
|||||||
### 1.5.27 (20218)
|
### 1.5.27 (20218)
|
||||||
- Language functionality has been consolidated into a LanguageSubsystem object at ba.app.lang
|
- Language functionality has been consolidated into a LanguageSubsystem object at ba.app.lang
|
||||||
- ba.get_valid_languages() is now an attr: ba.app.lang.available_languages
|
- ba.get_valid_languages() is now an attr: ba.app.lang.available_languages
|
||||||
|
- Achievement functionality has been consolidated into an AchievementSubsystem object at ba.app.ach
|
||||||
|
- Plugin functionality has been consolidated into a PluginSubsystem obj at ba.app.plugins
|
||||||
|
|
||||||
### 1.5.26 (20217)
|
### 1.5.26 (20217)
|
||||||
- Simplified licensing header on python scripts.
|
- Simplified licensing header on python scripts.
|
||||||
|
|||||||
@ -20,7 +20,7 @@ from _ba import (CollideModel, Context, ContextCall, Data, InputDevice,
|
|||||||
set_analytics_screen, charstr, textwidget, time, timer,
|
set_analytics_screen, charstr, textwidget, time, timer,
|
||||||
open_url, widget)
|
open_url, widget)
|
||||||
from ba._activity import Activity
|
from ba._activity import Activity
|
||||||
from ba._plugin import PotentialPlugin, Plugin
|
from ba._plugin import PotentialPlugin, Plugin, PluginSubsystem
|
||||||
from ba._actor import Actor
|
from ba._actor import Actor
|
||||||
from ba._player import PlayerInfo, Player, EmptyPlayer, StandLocation
|
from ba._player import PlayerInfo, Player, EmptyPlayer, StandLocation
|
||||||
from ba._nodeactor import NodeActor
|
from ba._nodeactor import NodeActor
|
||||||
|
|||||||
@ -175,6 +175,7 @@ class App:
|
|||||||
from ba._language import LanguageSubsystem
|
from ba._language import LanguageSubsystem
|
||||||
from ba._ui import UISubsystem
|
from ba._ui import UISubsystem
|
||||||
from ba._achievement import AchievementSubsystem
|
from ba._achievement import AchievementSubsystem
|
||||||
|
from ba._plugin import PluginSubsystem
|
||||||
|
|
||||||
# Config.
|
# Config.
|
||||||
self.config_file_healthy = False
|
self.config_file_healthy = False
|
||||||
@ -198,10 +199,6 @@ class App:
|
|||||||
self.iircade_mode: bool = self._env['iircade_mode']
|
self.iircade_mode: bool = self._env['iircade_mode']
|
||||||
assert isinstance(self.headless_mode, bool)
|
assert isinstance(self.headless_mode, bool)
|
||||||
|
|
||||||
# Plugins.
|
|
||||||
self.potential_plugins: List[ba.PotentialPlugin] = []
|
|
||||||
self.active_plugins: Dict[str, ba.Plugin] = {}
|
|
||||||
|
|
||||||
# Misc.
|
# Misc.
|
||||||
self.metascan: Optional[_meta.ScanResults] = None
|
self.metascan: Optional[_meta.ScanResults] = None
|
||||||
self.tips: List[str] = []
|
self.tips: List[str] = []
|
||||||
@ -241,6 +238,7 @@ class App:
|
|||||||
self.last_ad_purpose = 'invalid'
|
self.last_ad_purpose = 'invalid'
|
||||||
self.attempted_first_ad = False
|
self.attempted_first_ad = False
|
||||||
|
|
||||||
|
self.plugins = PluginSubsystem()
|
||||||
self.music = MusicSubsystem()
|
self.music = MusicSubsystem()
|
||||||
self.lang = LanguageSubsystem()
|
self.lang = LanguageSubsystem()
|
||||||
self.ach = AchievementSubsystem()
|
self.ach = AchievementSubsystem()
|
||||||
@ -291,7 +289,6 @@ class App:
|
|||||||
(internal)"""
|
(internal)"""
|
||||||
# pylint: disable=too-many-locals
|
# pylint: disable=too-many-locals
|
||||||
# pylint: disable=cyclic-import
|
# pylint: disable=cyclic-import
|
||||||
# pylint: disable=too-many-statements
|
|
||||||
from ba import _apputils
|
from ba import _apputils
|
||||||
from ba import _appconfig
|
from ba import _appconfig
|
||||||
from ba import _achievement
|
from ba import _achievement
|
||||||
@ -401,49 +398,13 @@ class App:
|
|||||||
|
|
||||||
_ba.pushcall(do_auto_sign_in)
|
_ba.pushcall(do_auto_sign_in)
|
||||||
|
|
||||||
# Load up our plugins and go ahead and call their on_app_launch calls.
|
self.plugins.on_app_launch()
|
||||||
self.load_plugins()
|
|
||||||
for plugin in self.active_plugins.values():
|
|
||||||
try:
|
|
||||||
plugin.on_app_launch()
|
|
||||||
except Exception:
|
|
||||||
from ba import _error
|
|
||||||
_error.print_exception('Error in plugin on_app_launch()')
|
|
||||||
|
|
||||||
self.ran_on_app_launch = True
|
self.ran_on_app_launch = True
|
||||||
|
|
||||||
# from ba._dependency import test_depset
|
# from ba._dependency import test_depset
|
||||||
# test_depset()
|
# test_depset()
|
||||||
|
|
||||||
def load_plugins(self) -> None:
|
|
||||||
"""(internal)"""
|
|
||||||
from ba._general import getclass
|
|
||||||
from ba._plugin import Plugin
|
|
||||||
|
|
||||||
# Note: the plugins we load is purely based on what's enabled
|
|
||||||
# in the app config. Our meta-scan gives us a list of available
|
|
||||||
# plugins, but that is only used to give the user a list of plugins
|
|
||||||
# that they can enable. (we wouldn't want to look at meta-scan here
|
|
||||||
# anyway because it may not be done yet at this point in the launch)
|
|
||||||
plugstates: Dict[str, Dict] = self.config.get('Plugins', {})
|
|
||||||
assert isinstance(plugstates, dict)
|
|
||||||
plugkeys: List[str] = sorted(key for key, val in plugstates.items()
|
|
||||||
if val.get('enabled', False))
|
|
||||||
for plugkey in plugkeys:
|
|
||||||
try:
|
|
||||||
cls = getclass(plugkey, Plugin)
|
|
||||||
except Exception as exc:
|
|
||||||
_ba.log(f"Error loading plugin class '{plugkey}': {exc}",
|
|
||||||
to_server=False)
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
plugin = cls()
|
|
||||||
assert plugkey not in self.active_plugins
|
|
||||||
self.active_plugins[plugkey] = plugin
|
|
||||||
except Exception:
|
|
||||||
from ba import _error
|
|
||||||
_error.print_exception(f'Error loading plugin: {plugkey}')
|
|
||||||
|
|
||||||
def read_config(self) -> None:
|
def read_config(self) -> None:
|
||||||
"""(internal)"""
|
"""(internal)"""
|
||||||
from ba import _appconfig
|
from ba import _appconfig
|
||||||
@ -558,7 +519,6 @@ class App:
|
|||||||
"""Run when the app resumes from a suspended state."""
|
"""Run when the app resumes from a suspended state."""
|
||||||
|
|
||||||
self.music.on_app_resume()
|
self.music.on_app_resume()
|
||||||
|
|
||||||
self.fg_state += 1
|
self.fg_state += 1
|
||||||
|
|
||||||
# Mark our cached tourneys as invalid so anyone using them knows
|
# Mark our cached tourneys as invalid so anyone using them knows
|
||||||
@ -657,13 +617,13 @@ class App:
|
|||||||
|
|
||||||
# If we're still not signed in and have pending codes,
|
# If we're still not signed in and have pending codes,
|
||||||
# inform the user that they need to sign in to use them.
|
# inform the user that they need to sign in to use them.
|
||||||
if _ba.app.pending_promo_codes:
|
if self.pending_promo_codes:
|
||||||
_ba.screenmessage(
|
_ba.screenmessage(
|
||||||
Lstr(resource='signInForPromoCodeText'),
|
Lstr(resource='signInForPromoCodeText'),
|
||||||
color=(1, 0, 0))
|
color=(1, 0, 0))
|
||||||
_ba.playsound(_ba.getsound('error'))
|
_ba.playsound(_ba.getsound('error'))
|
||||||
|
|
||||||
_ba.app.pending_promo_codes.append(code)
|
self.pending_promo_codes.append(code)
|
||||||
_ba.timer(6.0, check_pending_codes, timetype=TimeType.REAL)
|
_ba.timer(6.0, check_pending_codes, timetype=TimeType.REAL)
|
||||||
return
|
return
|
||||||
_ba.screenmessage(Lstr(resource='submittingPromoCodeText'),
|
_ba.screenmessage(Lstr(resource='submittingPromoCodeText'),
|
||||||
@ -681,7 +641,7 @@ class App:
|
|||||||
def _test_https(self) -> None:
|
def _test_https(self) -> None:
|
||||||
"""Testing https support.
|
"""Testing https support.
|
||||||
|
|
||||||
(would be nice to get this working on our custom python builds; need
|
(would be nice to get this working on our custom Python builds; need
|
||||||
to wrangle certificates somehow).
|
to wrangle certificates somehow).
|
||||||
"""
|
"""
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
|||||||
@ -68,6 +68,7 @@ def handle_scan_results(results: ScanResults) -> None:
|
|||||||
_ba.log(textwrap.indent(results.errors, 'Error (meta-scan): '))
|
_ba.log(textwrap.indent(results.errors, 'Error (meta-scan): '))
|
||||||
|
|
||||||
# Handle plugins.
|
# Handle plugins.
|
||||||
|
plugs = _ba.app.plugins
|
||||||
config_changed = False
|
config_changed = False
|
||||||
found_new = False
|
found_new = False
|
||||||
plugstates: Dict[str, Dict] = _ba.app.config.setdefault('Plugins', {})
|
plugstates: Dict[str, Dict] = _ba.app.config.setdefault('Plugins', {})
|
||||||
@ -75,7 +76,7 @@ def handle_scan_results(results: ScanResults) -> None:
|
|||||||
|
|
||||||
# Create a potential-plugin for each class we found in the scan.
|
# Create a potential-plugin for each class we found in the scan.
|
||||||
for class_path in results.plugins:
|
for class_path in results.plugins:
|
||||||
_ba.app.potential_plugins.append(
|
plugs.potential_plugins.append(
|
||||||
PotentialPlugin(display_name=Lstr(value=class_path),
|
PotentialPlugin(display_name=Lstr(value=class_path),
|
||||||
class_path=class_path,
|
class_path=class_path,
|
||||||
available=True))
|
available=True))
|
||||||
@ -90,12 +91,12 @@ def handle_scan_results(results: ScanResults) -> None:
|
|||||||
enabled = plugstate.get('enabled', False)
|
enabled = plugstate.get('enabled', False)
|
||||||
assert isinstance(enabled, bool)
|
assert isinstance(enabled, bool)
|
||||||
if enabled and class_path not in results.plugins:
|
if enabled and class_path not in results.plugins:
|
||||||
_ba.app.potential_plugins.append(
|
plugs.potential_plugins.append(
|
||||||
PotentialPlugin(display_name=Lstr(value=class_path),
|
PotentialPlugin(display_name=Lstr(value=class_path),
|
||||||
class_path=class_path,
|
class_path=class_path,
|
||||||
available=False))
|
available=False))
|
||||||
|
|
||||||
_ba.app.potential_plugins.sort(key=lambda p: p.class_path)
|
plugs.potential_plugins.sort(key=lambda p: p.class_path)
|
||||||
|
|
||||||
if found_new:
|
if found_new:
|
||||||
_ba.screenmessage(Lstr(resource='pluginsDetectedText'),
|
_ba.screenmessage(Lstr(resource='pluginsDetectedText'),
|
||||||
|
|||||||
@ -6,11 +6,65 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
import _ba
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from typing import List, Dict
|
||||||
import ba
|
import ba
|
||||||
|
|
||||||
|
|
||||||
|
class PluginSubsystem:
|
||||||
|
"""Subsystem for plugin handling in the app.
|
||||||
|
|
||||||
|
Category: App Classes
|
||||||
|
|
||||||
|
Access the single shared instance of this class at 'ba.app.plugins'.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.potential_plugins: List[ba.PotentialPlugin] = []
|
||||||
|
self.active_plugins: Dict[str, ba.Plugin] = {}
|
||||||
|
|
||||||
|
def on_app_launch(self) -> None:
|
||||||
|
"""Should be called at app launch time."""
|
||||||
|
# Load up our plugins and go ahead and call their on_app_launch calls.
|
||||||
|
self.load_plugins()
|
||||||
|
for plugin in self.active_plugins.values():
|
||||||
|
try:
|
||||||
|
plugin.on_app_launch()
|
||||||
|
except Exception:
|
||||||
|
from ba import _error
|
||||||
|
_error.print_exception('Error in plugin on_app_launch()')
|
||||||
|
|
||||||
|
def load_plugins(self) -> None:
|
||||||
|
"""(internal)"""
|
||||||
|
from ba._general import getclass
|
||||||
|
|
||||||
|
# Note: the plugins we load is purely based on what's enabled
|
||||||
|
# in the app config. Our meta-scan gives us a list of available
|
||||||
|
# plugins, but that is only used to give the user a list of plugins
|
||||||
|
# that they can enable. (we wouldn't want to look at meta-scan here
|
||||||
|
# anyway because it may not be done yet at this point in the launch)
|
||||||
|
plugstates: Dict[str, Dict] = _ba.app.config.get('Plugins', {})
|
||||||
|
assert isinstance(plugstates, dict)
|
||||||
|
plugkeys: List[str] = sorted(key for key, val in plugstates.items()
|
||||||
|
if val.get('enabled', False))
|
||||||
|
for plugkey in plugkeys:
|
||||||
|
try:
|
||||||
|
cls = getclass(plugkey, Plugin)
|
||||||
|
except Exception as exc:
|
||||||
|
_ba.log(f"Error loading plugin class '{plugkey}': {exc}",
|
||||||
|
to_server=False)
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
plugin = cls()
|
||||||
|
assert plugkey not in self.active_plugins
|
||||||
|
self.active_plugins[plugkey] = plugin
|
||||||
|
except Exception:
|
||||||
|
from ba import _error
|
||||||
|
_error.print_exception(f'Error loading plugin: {plugkey}')
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PotentialPlugin:
|
class PotentialPlugin:
|
||||||
"""Represents a ba.Plugin which can potentially be loaded.
|
"""Represents a ba.Plugin which can potentially be loaded.
|
||||||
|
|||||||
@ -97,11 +97,11 @@ class PluginSettingsWindow(ba.Window):
|
|||||||
ba.screenmessage('Still scanning plugins; please try again.',
|
ba.screenmessage('Still scanning plugins; please try again.',
|
||||||
color=(1, 0, 0))
|
color=(1, 0, 0))
|
||||||
ba.playsound(ba.getsound('error'))
|
ba.playsound(ba.getsound('error'))
|
||||||
pluglist = ba.app.potential_plugins
|
pluglist = ba.app.plugins.potential_plugins
|
||||||
plugstates: Dict[str, Dict] = ba.app.config.setdefault('Plugins', {})
|
plugstates: Dict[str, Dict] = ba.app.config.setdefault('Plugins', {})
|
||||||
assert isinstance(plugstates, dict)
|
assert isinstance(plugstates, dict)
|
||||||
for i, availplug in enumerate(pluglist):
|
for i, availplug in enumerate(pluglist):
|
||||||
active = availplug.class_path in ba.app.active_plugins
|
active = availplug.class_path in ba.app.plugins.active_plugins
|
||||||
|
|
||||||
plugstate = plugstates.setdefault(availplug.class_path, {})
|
plugstate = plugstates.setdefault(availplug.class_path, {})
|
||||||
checked = plugstate.get('enabled', False)
|
checked = plugstate.get('enabled', False)
|
||||||
|
|||||||
@ -158,6 +158,7 @@
|
|||||||
<li><a href="#class_ba_MusicPlayer">ba.MusicPlayer</a></li>
|
<li><a href="#class_ba_MusicPlayer">ba.MusicPlayer</a></li>
|
||||||
<li><a href="#class_ba_MusicSubsystem">ba.MusicSubsystem</a></li>
|
<li><a href="#class_ba_MusicSubsystem">ba.MusicSubsystem</a></li>
|
||||||
<li><a href="#class_ba_Plugin">ba.Plugin</a></li>
|
<li><a href="#class_ba_Plugin">ba.Plugin</a></li>
|
||||||
|
<li><a href="#class_ba_PluginSubsystem">ba.PluginSubsystem</a></li>
|
||||||
<li><a href="#class_ba_PotentialPlugin">ba.PotentialPlugin</a></li>
|
<li><a href="#class_ba_PotentialPlugin">ba.PotentialPlugin</a></li>
|
||||||
<li><a href="#class_ba_ServerController">ba.ServerController</a></li>
|
<li><a href="#class_ba_ServerController">ba.ServerController</a></li>
|
||||||
<li><a href="#class_ba_UISubsystem">ba.UISubsystem</a></li>
|
<li><a href="#class_ba_UISubsystem">ba.UISubsystem</a></li>
|
||||||
@ -4734,6 +4735,31 @@ the type-checker properly identifies the returned value as one.</p>
|
|||||||
|
|
||||||
<p>Called when the app is being launched.</p>
|
<p>Called when the app is being launched.</p>
|
||||||
|
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
<hr>
|
||||||
|
<h2><strong><a name="class_ba_PluginSubsystem">ba.PluginSubsystem</a></strong></h3>
|
||||||
|
<p><em><top level class></em>
|
||||||
|
</p>
|
||||||
|
<p>Subsystem for plugin handling in the app.</p>
|
||||||
|
|
||||||
|
<p>Category: <a href="#class_category_App_Classes">App Classes</a></p>
|
||||||
|
|
||||||
|
<p> Access the single shared instance of this class at 'ba.app.plugins'.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Methods:</h3>
|
||||||
|
<h5><a href="#method_ba_PluginSubsystem____init__"><constructor></a>, <a href="#method_ba_PluginSubsystem__on_app_launch">on_app_launch()</a></h5>
|
||||||
|
<dl>
|
||||||
|
<dt><h4><a name="method_ba_PluginSubsystem____init__"><constructor></a></dt></h4><dd>
|
||||||
|
<p><span>ba.PluginSubsystem()</span></p>
|
||||||
|
|
||||||
|
</dd>
|
||||||
|
<dt><h4><a name="method_ba_PluginSubsystem__on_app_launch">on_app_launch()</a></dt></h4><dd>
|
||||||
|
<p><span>on_app_launch(self) -> None</span></p>
|
||||||
|
|
||||||
|
<p>Should be called at app launch time.</p>
|
||||||
|
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<hr>
|
<hr>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user