mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Extract plugins from settings.
There isn't really any need for these two types to interact. The lua plugin manager effectively fully owned its properties, it just delegated all reads and writes to the settings object. Instead, break the plugin settings out into the plugin manager and preserve the manager in the Game. This will make it possible to expose plugin options in the NGW without breaking the game on cancel.
This commit is contained in:
@@ -7,8 +7,6 @@ from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, TYPE_CHECKING
|
||||
|
||||
from game.settings import Settings
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.missiongenerator.luagenerator import LuaGenerator
|
||||
|
||||
@@ -50,26 +48,10 @@ class PluginSettings:
|
||||
|
||||
def __init__(self, identifier: str, enabled_by_default: bool) -> None:
|
||||
self.identifier = identifier
|
||||
self.enabled_by_default = enabled_by_default
|
||||
self.settings = Settings()
|
||||
self.initialize_settings()
|
||||
|
||||
def set_settings(self, settings: Settings) -> None:
|
||||
self.settings = settings
|
||||
self.initialize_settings()
|
||||
|
||||
def initialize_settings(self) -> None:
|
||||
# Plugin options are saved in the game's Settings, but it's possible for
|
||||
# plugins to change across loads. If new plugins are added or new
|
||||
# options added to those plugins, initialize the new settings.
|
||||
self.settings.initialize_plugin_option(self.identifier, self.enabled_by_default)
|
||||
|
||||
@property
|
||||
def enabled(self) -> bool:
|
||||
return self.settings.plugin_option(self.identifier)
|
||||
self.enabled = enabled_by_default
|
||||
|
||||
def set_enabled(self, enabled: bool) -> None:
|
||||
self.settings.set_plugin_option(self.identifier, enabled)
|
||||
self.enabled = enabled
|
||||
|
||||
|
||||
class LuaPluginOption(PluginSettings):
|
||||
@@ -173,6 +155,12 @@ class LuaPlugin(PluginSettings):
|
||||
def options(self) -> List[LuaPluginOption]:
|
||||
return self.definition.options
|
||||
|
||||
def is_option_enabled(self, identifier: str) -> bool:
|
||||
for option in self.options:
|
||||
if option.identifier == identifier:
|
||||
return option.enabled
|
||||
raise KeyError(f"Plugin {self.identifier} has no option {self.identifier}")
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, name: str, path: Path) -> Optional[LuaPlugin]:
|
||||
try:
|
||||
@@ -183,12 +171,6 @@ class LuaPlugin(PluginSettings):
|
||||
|
||||
return cls(definition)
|
||||
|
||||
def set_settings(self, settings: Settings) -> None:
|
||||
"""Attaches the plugin to a settings object."""
|
||||
super().set_settings(settings)
|
||||
for option in self.definition.options:
|
||||
option.set_settings(self.settings)
|
||||
|
||||
def inject_scripts(self, lua_generator: LuaGenerator) -> None:
|
||||
"""Injects the plugin's scripts into the mission."""
|
||||
for work_order in self.definition.work_orders:
|
||||
@@ -231,3 +213,11 @@ class LuaPlugin(PluginSettings):
|
||||
|
||||
for work_order in self.definition.config_work_orders:
|
||||
work_order.work(lua_generator)
|
||||
|
||||
def update_with(self, other: LuaPlugin) -> None:
|
||||
self.enabled = other.enabled
|
||||
for option in self.options:
|
||||
try:
|
||||
option.enabled = other.is_option_enabled(option.identifier)
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from collections.abc import Iterator
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
|
||||
from game.settings import Settings
|
||||
from .luaplugin import LuaPlugin
|
||||
|
||||
|
||||
class LuaPluginManager:
|
||||
"""Manages available and loaded lua plugins."""
|
||||
|
||||
_plugins_loaded = False
|
||||
_plugins: Dict[str, LuaPlugin] = {}
|
||||
def __init__(self, plugins: dict[str, LuaPlugin]) -> None:
|
||||
self._plugins: dict[str, LuaPlugin] = plugins
|
||||
|
||||
@classmethod
|
||||
def _load_plugins(cls) -> None:
|
||||
@staticmethod
|
||||
def load() -> LuaPluginManager:
|
||||
plugins_path = Path("resources/plugins")
|
||||
|
||||
path = plugins_path / "plugins.json"
|
||||
@@ -23,6 +24,7 @@ class LuaPluginManager:
|
||||
|
||||
logging.info(f"Reading plugins list from {path}")
|
||||
|
||||
plugins = {}
|
||||
data = json.loads(path.read_text())
|
||||
for name in data:
|
||||
plugin_path = plugins_path / name / "plugin.json"
|
||||
@@ -34,27 +36,41 @@ class LuaPluginManager:
|
||||
logging.info(f"Loading plugin {name} from {plugin_path}")
|
||||
plugin = LuaPlugin.from_json(name, plugin_path)
|
||||
if plugin is not None:
|
||||
cls._plugins[name] = plugin
|
||||
cls._plugins_loaded = True
|
||||
plugins[name] = plugin
|
||||
return LuaPluginManager(plugins)
|
||||
|
||||
@classmethod
|
||||
def _get_plugins(cls) -> Dict[str, LuaPlugin]:
|
||||
if not cls._plugins_loaded:
|
||||
cls._load_plugins()
|
||||
return cls._plugins
|
||||
def update_with(self, other: LuaPluginManager) -> None:
|
||||
"""Updates all setting values with those in the given plugin manager.
|
||||
|
||||
@classmethod
|
||||
def plugins(cls) -> List[LuaPlugin]:
|
||||
return list(cls._get_plugins().values())
|
||||
When a game is loaded, LuaPluginManager.load() is called to load the latest set
|
||||
of plugins and settings. This is called with the plugin manager that was saved
|
||||
to the Game object to preserve any options that were set, and then the Game is
|
||||
updated with this manager.
|
||||
|
||||
@classmethod
|
||||
def load_settings(cls, settings: Settings) -> None:
|
||||
"""Attaches all loaded plugins to the given settings object.
|
||||
|
||||
The LuaPluginManager singleton can only be attached to a single Settings object
|
||||
at a time, and plugins will update the Settings object directly, so attaching
|
||||
the plugin manager to a detached Settings object (say, during the new game
|
||||
wizard, but then canceling the new game) will break the settings UI.
|
||||
This needs to happen because the set of available plugins (or their options) can
|
||||
change between runs.
|
||||
"""
|
||||
for plugin in cls.plugins():
|
||||
plugin.set_settings(settings)
|
||||
for plugin in self.iter_plugins():
|
||||
try:
|
||||
old_plugin = other.by_id(plugin.identifier)
|
||||
except KeyError:
|
||||
continue
|
||||
plugin.update_with(old_plugin)
|
||||
|
||||
def iter_plugins(self) -> Iterator[LuaPlugin]:
|
||||
yield from self._plugins.values()
|
||||
|
||||
def by_id(self, identifier: str) -> LuaPlugin:
|
||||
return self._plugins[identifier]
|
||||
|
||||
def is_plugin_enabled(self, plugin_id: str) -> bool:
|
||||
try:
|
||||
return self.by_id(plugin_id).enabled
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
def is_option_enabled(self, plugin_id: str, option_id: str) -> bool:
|
||||
try:
|
||||
return self.by_id(plugin_id).is_option_enabled(option_id)
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user