Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamically loading plugins #233

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pyboy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@

from .pyboy import PyBoy
from .utils import WindowEvent


def get_include():
import os
return os.path.dirname(os.path.abspath(__file__))
8 changes: 5 additions & 3 deletions pyboy/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from pyboy import PyBoy, core
from pyboy.logger import log_level, logger
from pyboy.plugin_manager import parser_arguments
from pyboy.plugin_manager import external_plugin_names, parser_arguments, window_names
from pyboy.pyboy import defaults

INTERNAL_LOADSTATE = "INTERNAL_LOADSTATE_TOKEN"
Expand All @@ -29,8 +29,10 @@ def valid_file_path(path):


parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter, # Don't wrap epilog automatically
description="PyBoy -- Game Boy emulator written in Python",
epilog="Warning: Features marked with (internal use) might be subject to change.",
epilog=(f"External plugins loaded: {external_plugin_names()}\n\n" if external_plugin_names() else "") +
"Warning: Features marked with (internal use) might be subject to change.",
)
parser.add_argument("ROM", type=valid_file_path, help="Path to a Game Boy compatible ROM file")
parser.add_argument("-b", "--bootrom", type=valid_file_path, help="Path to a boot-ROM file")
Expand Down Expand Up @@ -67,7 +69,7 @@ def valid_file_path(path):
"--window",
default=defaults["window_type"],
type=str,
choices=["SDL2", "OpenGL", "headless", "dummy"],
choices=list(window_names()),
help="Specify window-type to use"
)
parser.add_argument("-s", "--scale", default=defaults["scale"], type=int, help="The scaling multiplier for the window")
Expand Down
34 changes: 32 additions & 2 deletions pyboy/plugin_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,26 @@
# License: See LICENSE.md file
# GitHub: https://github.com/Baekalfen/PyBoy
#

import importlib
import inspect
import logging
import os
import sys
import sysconfig
from pathlib import Path
from pkgutil import iter_modules

from pyboy import plugins
from pyboy.plugins.base_plugin import PyBoyDebugPlugin, PyBoyGameWrapper, PyBoyPlugin, PyBoyWindowPlugin

if sys.version_info >= (3, 8):
from importlib import metadata as importlib_metadata
else:
import importlib_metadata

logger = logging.getLogger(__name__)

EXT_SUFFIX = sysconfig.get_config_var("EXT_SUFFIX")

registered_plugins = []
Expand All @@ -22,9 +32,14 @@
enabled_window_plugins = []
enabled_gamewrappers = []

for mod_name in [x.name for x in iter_modules(plugins.__path__)]:
mod = importlib.import_module("pyboy.plugins." + mod_name)
builtin_plugins = [importlib.import_module("pyboy.plugins." + m.name) for m in iter_modules(plugins.__path__)]
external_plugins = []
Comment on lines +35 to +36
Copy link
Owner Author

@Baekalfen Baekalfen Jun 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably want something like Django's list of middlewares to manage loading/execution order.

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
]

for p in importlib_metadata.distributions():
for e in p.entry_points:
if e.group == "pyboy":
external_plugins.append(e.load())

for mod in builtin_plugins + external_plugins:
if hasattr(mod, "_export_plugins"):
plugin_names = getattr(mod, "_export_plugins")
else:
Expand All @@ -48,10 +63,25 @@ def parser_arguments():
yield p.argv


def window_names():
for p in registered_window_plugins:
if p.name:
yield p.name


def external_plugin_names():
return ", ".join([p.__name__ for p in external_plugins])


class PluginManager:
def __init__(self, pyboy, mb, pyboy_argv):
self.pyboy = pyboy

if external_plugins:
logger.info(f"External plugins loaded: {external_plugin_names()}")
else:
logger.info("No external plugins found")

self.enabled_plugins = [p(pyboy, mb, pyboy_argv) for p in registered_plugins if p.enabled(pyboy, pyboy_argv)]
self.enabled_window_plugins = [
p(pyboy, mb, pyboy_argv) for p in registered_window_plugins if p.enabled(pyboy, pyboy_argv)
Expand Down
2 changes: 2 additions & 0 deletions pyboy/plugins/base_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ def enabled(cls, pyboy, pyboy_argv):


class PyBoyWindowPlugin(PyBoyPlugin):
name = "PyBoyWindowPlugin"

def __init__(self, pyboy, mb, pyboy_argv, *args, **kwargs):
super().__init__(pyboy, mb, pyboy_argv, *args, **kwargs)

Expand Down
1 change: 1 addition & 0 deletions pyboy/plugins/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class Debug(PyBoyWindowPlugin):
"type": str,
"help": "Add breakpoints on start-up (internal use)"
})]
name = None

def __init__(self, pyboy, mb, pyboy_argv):
super().__init__(pyboy, mb, pyboy_argv)
Expand Down
2 changes: 2 additions & 0 deletions pyboy/plugins/window_dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@


class WindowDummy(PyBoyWindowPlugin):
name = "dummy"

def __init__(self, pyboy, mb, pyboy_argv):
super().__init__(pyboy, mb, pyboy_argv)

Expand Down
2 changes: 2 additions & 0 deletions pyboy/plugins/window_headless.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@


class WindowHeadless(PyBoyWindowPlugin):
name = "headless"

def __init__(self, pyboy, mb, pyboy_argv):
super().__init__(pyboy, mb, pyboy_argv)

Expand Down
2 changes: 2 additions & 0 deletions pyboy/plugins/window_open_gl.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@


class WindowOpenGL(PyBoyWindowPlugin):
name = "OpenGL"

def __init__(self, pyboy, mb, pyboy_argv):
super().__init__(pyboy, mb, pyboy_argv)

Expand Down
2 changes: 2 additions & 0 deletions pyboy/plugins/window_sdl2.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ def sdl2_event_pump(events):


class WindowSDL2(PyBoyWindowPlugin):
name = "SDL2"

def __init__(self, pyboy, mb, pyboy_argv):
super().__init__(pyboy, mb, pyboy_argv)

Expand Down