diff --git a/client.py b/client.py new file mode 100644 index 0000000..782bcf9 --- /dev/null +++ b/client.py @@ -0,0 +1,73 @@ +import logging +import importlib +import subprocess +from pathlib import Path +from typing import List + + +class Client: + """Simple launcher for PokeMMO with plugin support.""" + + def __init__(self, game_path: Path | None = None) -> None: + self.game_path = game_path or self._default_game_path() + self.logger = self._create_logger() + self.plugins: List[object] = [] + + def _create_logger(self) -> logging.Logger: + logger = logging.getLogger("pokemmo.client") + if not logger.handlers: + logger.setLevel(logging.INFO) + handler = logging.StreamHandler() + fmt = "[%(levelname)s] %(name)s: %(message)s" + handler.setFormatter(logging.Formatter(fmt)) + logger.addHandler(handler) + return logger + + def _default_game_path(self) -> Path: + root = Path(__file__).resolve().parent + if (root / "PokeMMO.exe").exists(): + return root / "PokeMMO.exe" + return root / "PokeMMO.sh" + + # Plugins ----------------------------------------------------------------- + def load_plugins(self, directory: Path | None = None) -> None: + """Load all plugins from the provided directory.""" + directory = directory or Path(__file__).resolve().parent / "plugins" + if not directory.exists(): + self.logger.debug("Plugin directory %s does not exist", directory) + return + for file in directory.glob("*.py"): + if file.name == "__init__.py": + continue + module_name = f"plugins.{file.stem}" + try: + module = importlib.import_module(module_name) + except Exception as exc: # pragma: no cover - best effort logging + self.logger.error("Failed to import %s: %s", module_name, exc) + continue + plugin_cls = getattr(module, "Plugin", None) + if plugin_cls is None: + self.logger.warning("No Plugin class in %s", module_name) + continue + plugin = plugin_cls() + self.plugins.append(plugin) + self.logger.info("Loaded plugin %s", module_name) + + # ------------------------------------------------------------------------- + def run(self) -> None: + """Start the PokeMMO game.""" + self.logger.info("Launching PokeMMO from %s", self.game_path) + if self.game_path.suffix == ".sh": + subprocess.Popen(["bash", str(self.game_path)], cwd=self.game_path.parent) + else: + subprocess.Popen([str(self.game_path)], cwd=self.game_path.parent) + + +if __name__ == "__main__": + client = Client() + client.load_plugins() + for plugin in client.plugins: + run = getattr(plugin, "run", None) + if callable(run): + run(client) + client.run() diff --git a/plugins/__init__.py b/plugins/__init__.py new file mode 100644 index 0000000..b5f3921 --- /dev/null +++ b/plugins/__init__.py @@ -0,0 +1 @@ +"""Plugins for the PokeMMO client.""" diff --git a/plugins/sample_plugin.py b/plugins/sample_plugin.py new file mode 100644 index 0000000..2e3f5c8 --- /dev/null +++ b/plugins/sample_plugin.py @@ -0,0 +1,5 @@ +class Plugin: + """Simple example plugin.""" + + def run(self, client): + client.logger.info("Sample plugin ran")