mirror of
https://github.com/solero/houdini.git
synced 2024-11-25 15:07:24 +00:00
Move plugins, commands and listeners into their own "manager" objects
Works just like before, only the logic is stored in the objects themselves instead of being scattered around houdini.py
This commit is contained in:
parent
3c09434012
commit
8d6e726f97
@ -1,8 +1,12 @@
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from aiocache import cached
|
from aiocache import cached
|
||||||
from types import FunctionType
|
from types import FunctionType
|
||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import enum
|
import enum
|
||||||
|
import logging
|
||||||
|
import copy
|
||||||
|
|
||||||
|
|
||||||
class ConflictResolution(enum.Enum):
|
class ConflictResolution(enum.Enum):
|
||||||
@ -18,6 +22,33 @@ class Language(enum.Enum):
|
|||||||
Es = 8
|
Es = 8
|
||||||
De = 32
|
De = 32
|
||||||
Ru = 64
|
Ru = 64
|
||||||
|
|
||||||
|
|
||||||
|
class _AbstractManager(dict):
|
||||||
|
def __init__(self, server):
|
||||||
|
self.server = server
|
||||||
|
self.logger = logging.getLogger('houdini')
|
||||||
|
|
||||||
|
self.__backup = None
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def load(self, module):
|
||||||
|
"""Loads entries from module"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def remove(self, module):
|
||||||
|
"""Removes all entries by module"""
|
||||||
|
|
||||||
|
def backup(self):
|
||||||
|
self.__backup = copy.copy(self)
|
||||||
|
|
||||||
|
def restore(self):
|
||||||
|
if self.__backup is not None:
|
||||||
|
self.update(self.__backup)
|
||||||
|
self.__backup = None
|
||||||
|
|
||||||
|
|
||||||
class PenguinStringCompiler(OrderedDict):
|
class PenguinStringCompiler(OrderedDict):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import inspect
|
import inspect
|
||||||
import config
|
import config
|
||||||
|
import logging
|
||||||
|
import copy
|
||||||
|
|
||||||
from houdini import handlers
|
from houdini import handlers
|
||||||
from houdini import plugins
|
from houdini import plugins
|
||||||
from houdini import ConflictResolution
|
from houdini import ConflictResolution, _AbstractManager
|
||||||
|
|
||||||
from houdini.converters import _ArgumentDeserializer, _listener
|
from houdini.converters import _ArgumentDeserializer, _listener
|
||||||
|
|
||||||
@ -63,42 +64,49 @@ player_data_attribute = handlers.player_data_attribute
|
|||||||
player_in_room = handlers.player_in_room
|
player_in_room = handlers.player_in_room
|
||||||
|
|
||||||
|
|
||||||
|
class CommandManager(_AbstractManager):
|
||||||
|
def load(self, module):
|
||||||
|
command_objects = inspect.getmembers(module, is_command)
|
||||||
|
if not isinstance(module, plugins.IPlugin):
|
||||||
|
raise TypeError('Commands can only be loaded from plugins')
|
||||||
|
|
||||||
|
for command_name, command_object in command_objects:
|
||||||
|
command_object.instance = module
|
||||||
|
|
||||||
|
if type(command_object.alias) == str:
|
||||||
|
command_object.alias = [command_object.alias]
|
||||||
|
command_object.alias.append(command_object.name)
|
||||||
|
|
||||||
|
parent_commands = self if command_object.parent is None else command_object.parent.commands
|
||||||
|
|
||||||
|
for name in command_object.alias:
|
||||||
|
if name in parent_commands and len(parent_commands[name]):
|
||||||
|
conflict_command = parent_commands[name][0]
|
||||||
|
if config.commands['ConflictMode'] == ConflictResolution.Exception:
|
||||||
|
raise NameError('Command name conflict: \'{}\' from plugin \'{}\' '
|
||||||
|
'conflicts with \'{}\' from module \'{}\''
|
||||||
|
.format(name, module.__class__.__name__, conflict_command.name,
|
||||||
|
conflict_command.instance.__class__.__name__))
|
||||||
|
elif config.commands['ConflictMode'] == ConflictResolution.Append:
|
||||||
|
parent_commands[name].append(command_object)
|
||||||
|
elif config.commands['ConflictMode'] == ConflictResolution.Silent:
|
||||||
|
module.server.logger.warning(
|
||||||
|
'Command \'{}\' from module \'{}\' disabled due to conflict with \'{}\''.format(
|
||||||
|
name, module.__class__.__name__, conflict_command.instance.__class__.__name__))
|
||||||
|
else:
|
||||||
|
parent_commands[name] = [command_object]
|
||||||
|
|
||||||
|
def remove(self, module):
|
||||||
|
for command_name, command_handlers in self.items():
|
||||||
|
for command_handler in command_handlers:
|
||||||
|
if module.__name__ == command_handler.callback.__module__:
|
||||||
|
command_handlers.remove(command_handler)
|
||||||
|
|
||||||
|
|
||||||
def is_command(command_object):
|
def is_command(command_object):
|
||||||
return issubclass(type(command_object), _Command)
|
return issubclass(type(command_object), _Command)
|
||||||
|
|
||||||
|
|
||||||
def commands_from_plugin(commands, plugin):
|
|
||||||
command_objects = inspect.getmembers(plugin, is_command)
|
|
||||||
if not isinstance(plugin, plugins.IPlugin):
|
|
||||||
raise TypeError('Commands can only be loaded from plugins')
|
|
||||||
|
|
||||||
for command_name, command_object in command_objects:
|
|
||||||
command_object.instance = plugin
|
|
||||||
|
|
||||||
if type(command_object.alias) == str:
|
|
||||||
command_object.alias = [command_object.alias]
|
|
||||||
command_object.alias.append(command_object.name)
|
|
||||||
|
|
||||||
parent_commands = commands if command_object.parent is None else command_object.parent.commands
|
|
||||||
|
|
||||||
for name in command_object.alias:
|
|
||||||
if name in parent_commands:
|
|
||||||
conflict_command = parent_commands[name][0]
|
|
||||||
if config.commands['ConflictMode'] == ConflictResolution.Exception:
|
|
||||||
raise NameError('Command name conflict: \'{}\' from plugin \'{}\' '
|
|
||||||
'conflicts with \'{}\' from module \'{}\''
|
|
||||||
.format(name, plugin.__class__.__name__, conflict_command.name,
|
|
||||||
conflict_command.plugin.__class__.__name__))
|
|
||||||
elif config.commands['ConflictMode'] == ConflictResolution.Append:
|
|
||||||
parent_commands[name].append(command_object)
|
|
||||||
elif config.commands['ConflictMode'] == ConflictResolution.Silent:
|
|
||||||
plugin.server.logger.warn('Command \'{}\' from plugin \'{}\' disabled due to conflict with \'{}\''
|
|
||||||
.format(name, plugin.__class__.__name__,
|
|
||||||
conflict_command.plugin.__class__.__name__))
|
|
||||||
else:
|
|
||||||
parent_commands[name] = [command_object]
|
|
||||||
|
|
||||||
|
|
||||||
if type(config.commands['Prefix']) == str:
|
if type(config.commands['Prefix']) == str:
|
||||||
config.commands['Prefix'] = [config.commands['Prefix']]
|
config.commands['Prefix'] = [config.commands['Prefix']]
|
||||||
|
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
import sys
|
|
||||||
import importlib
|
|
||||||
import copy
|
|
||||||
from watchdog.events import FileSystemEventHandler
|
|
||||||
|
|
||||||
from houdini.handlers import listeners_from_module, remove_handlers_by_module
|
|
||||||
from houdini.events import evaluate_handler_file_event
|
|
||||||
|
|
||||||
|
|
||||||
class HandlerFileEventHandler(FileSystemEventHandler):
|
|
||||||
|
|
||||||
def __init__(self, server):
|
|
||||||
self.logger = server.logger
|
|
||||||
self.server = server
|
|
||||||
|
|
||||||
def on_created(self, event):
|
|
||||||
handler_module_details = evaluate_handler_file_event(event)
|
|
||||||
|
|
||||||
if not handler_module_details:
|
|
||||||
return
|
|
||||||
|
|
||||||
handler_module_path, handler_module = handler_module_details
|
|
||||||
|
|
||||||
if '__init__.py' in handler_module_path:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.logger.debug('New handler module detected %s', handler_module)
|
|
||||||
|
|
||||||
try:
|
|
||||||
module = importlib.import_module(handler_module)
|
|
||||||
listeners_from_module(self.server.xt_listeners, self.server.xml_listeners, module)
|
|
||||||
except Exception as import_error:
|
|
||||||
self.logger.error('%s detected in %s, not importing.', import_error.__class__.__name__, handler_module)
|
|
||||||
|
|
||||||
def on_deleted(self, event):
|
|
||||||
handler_module_details = evaluate_handler_file_event(event)
|
|
||||||
|
|
||||||
if not handler_module_details:
|
|
||||||
return
|
|
||||||
|
|
||||||
handler_module_path, handler_module = handler_module_details
|
|
||||||
|
|
||||||
if handler_module not in sys.modules:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.logger.debug('Deleting listeners registered by %s...', handler_module)
|
|
||||||
|
|
||||||
remove_handlers_by_module(self.server.xt_listeners, self.server.xml_listeners, handler_module_path)
|
|
||||||
|
|
||||||
def on_modified(self, event):
|
|
||||||
handler_module_details = evaluate_handler_file_event(event)
|
|
||||||
|
|
||||||
if not handler_module_details:
|
|
||||||
return
|
|
||||||
|
|
||||||
handler_module_path, handler_module = handler_module_details
|
|
||||||
|
|
||||||
if handler_module not in sys.modules:
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.logger.info('Reloading %s', handler_module)
|
|
||||||
|
|
||||||
xt_listeners, xml_listeners = copy.copy(self.server.xt_listeners), copy.copy(self.server.xml_listeners)
|
|
||||||
|
|
||||||
remove_handlers_by_module(self.server.xt_listeners, self.server.xml_listeners, handler_module_path)
|
|
||||||
|
|
||||||
handler_module_object = sys.modules[handler_module]
|
|
||||||
|
|
||||||
try:
|
|
||||||
module = importlib.reload(handler_module_object)
|
|
||||||
listeners_from_module(self.server.xt_listeners, self.server.xml_listeners, module)
|
|
||||||
|
|
||||||
self.logger.info('Successfully reloaded %s!', handler_module)
|
|
||||||
except Exception as rebuild_error:
|
|
||||||
self.logger.error('%s detected in %s, not reloading.', rebuild_error.__class__.__name__, handler_module)
|
|
||||||
self.logger.info('Restoring handler references...')
|
|
||||||
|
|
||||||
self.server.xt_listeners = xt_listeners
|
|
||||||
self.server.xml_listeners = xml_listeners
|
|
||||||
|
|
||||||
self.logger.info('Handler references restored. Phew!')
|
|
89
houdini/events/listener_file_event.py
Normal file
89
houdini/events/listener_file_event.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import sys
|
||||||
|
import importlib
|
||||||
|
import logging
|
||||||
|
from watchdog.events import FileSystemEventHandler
|
||||||
|
|
||||||
|
from houdini.events import evaluate_listener_file_event
|
||||||
|
|
||||||
|
|
||||||
|
class ListenerFileEventHandler(FileSystemEventHandler):
|
||||||
|
|
||||||
|
def __init__(self, server):
|
||||||
|
self.server = server
|
||||||
|
self.logger = logging.getLogger('houdini')
|
||||||
|
|
||||||
|
def on_created(self, event):
|
||||||
|
listener_module_details = evaluate_listener_file_event(event)
|
||||||
|
|
||||||
|
if not listener_module_details:
|
||||||
|
return
|
||||||
|
|
||||||
|
listener_module_path, listener_module = listener_module_details
|
||||||
|
|
||||||
|
if '__init__.py' in listener_module_path:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.logger.debug('New handler module detected %s', listener_module)
|
||||||
|
|
||||||
|
try:
|
||||||
|
module = importlib.import_module(listener_module)
|
||||||
|
self.server.xt_listeners.load(module)
|
||||||
|
self.server.xml_listeners.load(module)
|
||||||
|
except Exception as import_error:
|
||||||
|
self.logger.error('%s detected in %s, not importing.', import_error.__class__.__name__, listener_module)
|
||||||
|
|
||||||
|
def on_deleted(self, event):
|
||||||
|
listener_module_details = evaluate_listener_file_event(event)
|
||||||
|
|
||||||
|
if not listener_module_details:
|
||||||
|
return
|
||||||
|
|
||||||
|
listener_module_path, listener_module = listener_module_details
|
||||||
|
|
||||||
|
if listener_module not in sys.modules:
|
||||||
|
return
|
||||||
|
|
||||||
|
listener_module_object = sys.modules[listener_module]
|
||||||
|
|
||||||
|
self.logger.debug('Deleting listeners registered by %s...', listener_module)
|
||||||
|
|
||||||
|
self.server.xt_listeners.remove(listener_module_object)
|
||||||
|
self.server.xml_listeners.remove(listener_module_object)
|
||||||
|
|
||||||
|
def on_modified(self, event):
|
||||||
|
listener_module_details = evaluate_listener_file_event(event)
|
||||||
|
|
||||||
|
if not listener_module_details:
|
||||||
|
return
|
||||||
|
|
||||||
|
listener_module_path, listener_module = listener_module_details
|
||||||
|
|
||||||
|
if listener_module not in sys.modules:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.logger.info('Reloading %s', listener_module)
|
||||||
|
|
||||||
|
self.server.xt_listeners.backup()
|
||||||
|
self.server.xml_listeners.backup()
|
||||||
|
self.server.commands.backup()
|
||||||
|
|
||||||
|
listener_module_object = sys.modules[listener_module]
|
||||||
|
|
||||||
|
self.server.xt_listeners.remove(listener_module_object)
|
||||||
|
self.server.xml_listeners.remove(listener_module_object)
|
||||||
|
|
||||||
|
try:
|
||||||
|
module = importlib.reload(listener_module_object)
|
||||||
|
self.server.xt_listeners.load(module)
|
||||||
|
self.server.xml_listeners.load(module)
|
||||||
|
|
||||||
|
self.logger.info('Successfully reloaded %s!', listener_module)
|
||||||
|
except Exception as rebuild_error:
|
||||||
|
self.logger.error('%s detected in %s, not reloading.', rebuild_error.__class__.__name__, listener_module)
|
||||||
|
self.logger.info('Restoring listeners...')
|
||||||
|
|
||||||
|
self.server.xt_listeners.restore()
|
||||||
|
self.server.xml_listeners.restore()
|
||||||
|
self.server.commands.restore()
|
||||||
|
|
||||||
|
self.logger.info('Listeners restored. Phew!')
|
@ -1,36 +1,35 @@
|
|||||||
import sys
|
import sys
|
||||||
import importlib
|
import importlib
|
||||||
import os.path
|
import os.path
|
||||||
import copy
|
import logging
|
||||||
import asyncio
|
import asyncio
|
||||||
from watchdog.events import FileSystemEventHandler
|
from watchdog.events import FileSystemEventHandler
|
||||||
|
|
||||||
from houdini.events import evaluate_handler_file_event
|
from houdini.events import evaluate_plugin_file_event
|
||||||
|
|
||||||
|
|
||||||
class PluginFileEventHandler(FileSystemEventHandler):
|
class PluginFileEventHandler(FileSystemEventHandler):
|
||||||
|
|
||||||
def __init__(self, server):
|
def __init__(self, server):
|
||||||
self.logger = server.logger
|
|
||||||
self.server = server
|
self.server = server
|
||||||
|
self.logger = logging.getLogger('houdini')
|
||||||
|
|
||||||
def on_created(self, event):
|
def on_created(self, event):
|
||||||
plugin_module_details = evaluate_handler_file_event(event)
|
plugin_module_details = evaluate_plugin_file_event(event)
|
||||||
|
|
||||||
if not plugin_module_details:
|
if not plugin_module_details:
|
||||||
return
|
return
|
||||||
|
|
||||||
plugin_module_path, plugin_module = plugin_module_details
|
plugin_module_path, plugin_module = plugin_module_details
|
||||||
|
|
||||||
self.logger.debug('New handler module detected %s', plugin_module)
|
self.logger.debug('New plugin detected %s', plugin_module)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
plugin_module_object = importlib.import_module(plugin_module)
|
plugin_module_object = importlib.import_module(plugin_module)
|
||||||
plugin_class = plugin_module_object.__name__.split(".")[2]
|
|
||||||
|
|
||||||
asyncio.run(self.server.load_plugin((plugin_module_object, plugin_class)))
|
self.server.plugin.load(plugin_module_object)
|
||||||
|
|
||||||
self.logger.info('New plugin \'%s\' has been loaded.' % plugin_class)
|
self.logger.info('New plugin \'%s\' has been loaded.' % plugin_module)
|
||||||
except Exception as import_error:
|
except Exception as import_error:
|
||||||
self.logger.error('%s detected in %s, not importing.', import_error.__class__.__name__, plugin_module)
|
self.logger.error('%s detected in %s, not importing.', import_error.__class__.__name__, plugin_module)
|
||||||
|
|
||||||
@ -45,13 +44,11 @@ class PluginFileEventHandler(FileSystemEventHandler):
|
|||||||
self.logger.debug('Deleting listeners registered by %s.', plugin_module)
|
self.logger.debug('Deleting listeners registered by %s.', plugin_module)
|
||||||
|
|
||||||
plugin_module_object = sys.modules[plugin_module]
|
plugin_module_object = sys.modules[plugin_module]
|
||||||
plugin_class = plugin_module_object.__name__.split(".")[2]
|
|
||||||
|
|
||||||
self.server.unload_plugin((plugin_module_object, plugin_class))
|
self.server.plugins.remove(plugin_module_object)
|
||||||
|
|
||||||
def on_modified(self, event):
|
def on_modified(self, event):
|
||||||
plugin_module_details = evaluate_handler_file_event(event)
|
plugin_module_details = evaluate_plugin_file_event(event)
|
||||||
|
|
||||||
if not plugin_module_details:
|
if not plugin_module_details:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -63,24 +60,27 @@ class PluginFileEventHandler(FileSystemEventHandler):
|
|||||||
self.logger.info('Reloading %s', plugin_module)
|
self.logger.info('Reloading %s', plugin_module)
|
||||||
|
|
||||||
plugin_module_object = sys.modules[plugin_module]
|
plugin_module_object = sys.modules[plugin_module]
|
||||||
plugin_class = plugin_module_object.__name__.split(".")[2]
|
|
||||||
|
|
||||||
xt_listeners, xml_listeners = copy.copy(self.server.xt_listeners), copy.copy(self.server.xml_listeners)
|
self.server.xt_listeners.backup()
|
||||||
|
self.server.xml_listeners.backup()
|
||||||
|
self.server.commands.backup()
|
||||||
|
|
||||||
self.server.unload_plugin((plugin_module_object, plugin_class))
|
self.server.plugins.remove(plugin_module_object)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
new_plugin_module = importlib.reload(plugin_module_object)
|
new_plugin_module = importlib.reload(plugin_module_object)
|
||||||
asyncio.run(self.server.load_plugin((new_plugin_module, plugin_class)))
|
|
||||||
|
self.server.plugins.load(new_plugin_module)
|
||||||
|
|
||||||
self.logger.info('Successfully reloaded %s!', plugin_module)
|
self.logger.info('Successfully reloaded %s!', plugin_module)
|
||||||
except LookupError as lookup_error:
|
except LookupError as lookup_error:
|
||||||
self.logger.warn('Did not reload plugin \'%s\': %s.', plugin_class, lookup_error)
|
self.logger.warning('Did not reload plugin \'%s\': %s.', plugin_module, lookup_error)
|
||||||
except Exception as rebuild_error:
|
except Exception as rebuild_error:
|
||||||
self.logger.error('%s detected in %s, not reloading.', rebuild_error.__class__.__name__, plugin_module)
|
self.logger.error('%s detected in %s, not reloading.', rebuild_error.__class__.__name__, plugin_module)
|
||||||
self.logger.info('Restoring handler references...')
|
self.logger.info('Restoring handler references...')
|
||||||
|
|
||||||
self.server.xt_handlers = xt_listeners
|
self.server.xt_listeners.restore()
|
||||||
self.server.xml_handlers = xml_listeners
|
self.server.xml_listeners.restore()
|
||||||
|
self.server.commands.restore()
|
||||||
|
|
||||||
self.logger.info('Restored handler references. Phew!')
|
self.logger.info('Restored handler references. Phew!')
|
||||||
|
@ -2,12 +2,16 @@ import inspect
|
|||||||
import enum
|
import enum
|
||||||
import os
|
import os
|
||||||
import itertools
|
import itertools
|
||||||
|
import importlib
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
import copy
|
||||||
from types import FunctionType
|
from types import FunctionType
|
||||||
|
|
||||||
from houdini.converters import _listener, _ArgumentDeserializer, get_converter, do_conversion, _ConverterContext
|
from houdini.converters import _listener, _ArgumentDeserializer, get_converter, do_conversion, _ConverterContext
|
||||||
|
|
||||||
from houdini.cooldown import _Cooldown, _CooldownMapping, BucketType
|
from houdini.cooldown import _Cooldown, _CooldownMapping, BucketType
|
||||||
from houdini import plugins
|
from houdini import plugins, _AbstractManager
|
||||||
|
|
||||||
|
|
||||||
class AuthorityError(Exception):
|
class AuthorityError(Exception):
|
||||||
@ -105,11 +109,59 @@ class _XMLListener(_Listener):
|
|||||||
return await self.callback(*handler_call_arguments)
|
return await self.callback(*handler_call_arguments)
|
||||||
|
|
||||||
|
|
||||||
def get_relative_function_path(function_obj):
|
class _ListenerManager(_AbstractManager):
|
||||||
abs_function_file = inspect.getfile(function_obj)
|
def setup(self, module, strict_load=None, exclude_load=None):
|
||||||
rel_function_file = os.path.relpath(abs_function_file)
|
for handler_module in self.server.get_package_modules(module):
|
||||||
|
if not (strict_load and handler_module not in strict_load or exclude_load
|
||||||
|
and handler_module in exclude_load):
|
||||||
|
module = sys.modules[handler_module] if handler_module in sys.modules.keys() \
|
||||||
|
else importlib.import_module(handler_module)
|
||||||
|
self.load(module)
|
||||||
|
|
||||||
return rel_function_file
|
self.logger.info('Handler modules loaded')
|
||||||
|
|
||||||
|
def load(self, module):
|
||||||
|
listener_objects = inspect.getmembers(module, self.is_listener)
|
||||||
|
for listener_name, listener_object in listener_objects:
|
||||||
|
if isinstance(module, plugins.IPlugin):
|
||||||
|
listener_object.instance = module
|
||||||
|
|
||||||
|
if listener_object.packet not in self:
|
||||||
|
self[listener_object.packet] = []
|
||||||
|
|
||||||
|
if listener_object not in self[listener_object.packet]:
|
||||||
|
if listener_object.priority == Priority.High:
|
||||||
|
self[listener_object.packet].insert(0, listener_object)
|
||||||
|
elif listener_object.priority == Priority.Override:
|
||||||
|
self[listener_object.packet] = [listener_object]
|
||||||
|
else:
|
||||||
|
self[listener_object.packet].append(listener_object)
|
||||||
|
|
||||||
|
for listener_name, listener_object in listener_objects:
|
||||||
|
for override in listener_object.overrides:
|
||||||
|
self[override.packet].remove(override)
|
||||||
|
|
||||||
|
def remove(self, module):
|
||||||
|
for handler_id, handler_listeners in self.items():
|
||||||
|
for handler_listener in handler_listeners:
|
||||||
|
if module.__name__ == handler_listener.callback.__module__:
|
||||||
|
handler_listeners.remove(handler_listener)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_listener(cls, listener):
|
||||||
|
return issubclass(type(listener), _Listener)
|
||||||
|
|
||||||
|
|
||||||
|
class XTListenerManager(_ListenerManager):
|
||||||
|
@classmethod
|
||||||
|
def is_listener(cls, listener):
|
||||||
|
return issubclass(type(listener), _XTListener)
|
||||||
|
|
||||||
|
|
||||||
|
class XMLListenerManager(_ListenerManager):
|
||||||
|
@classmethod
|
||||||
|
def is_listener(cls, listener):
|
||||||
|
return issubclass(type(listener), _XMLListener)
|
||||||
|
|
||||||
|
|
||||||
def handler(packet, **kwargs):
|
def handler(packet, **kwargs):
|
||||||
@ -120,50 +172,6 @@ def handler(packet, **kwargs):
|
|||||||
return _listener(listener_class, packet, **kwargs)
|
return _listener(listener_class, packet, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def listener_exists(xt_listeners, xml_listeners, packet):
|
|
||||||
listener_collection = xt_listeners if isinstance(packet, XTPacket) else xml_listeners
|
|
||||||
return packet in listener_collection
|
|
||||||
|
|
||||||
|
|
||||||
def is_listener(listener):
|
|
||||||
return issubclass(type(listener), _Listener)
|
|
||||||
|
|
||||||
|
|
||||||
def listeners_from_module(xt_listeners, xml_listeners, module):
|
|
||||||
listener_objects = inspect.getmembers(module, is_listener)
|
|
||||||
for listener_name, listener_object in listener_objects:
|
|
||||||
if isinstance(module, plugins.IPlugin):
|
|
||||||
listener_object.instance = module
|
|
||||||
|
|
||||||
listener_collection = xt_listeners if type(listener_object) == _XTListener else xml_listeners
|
|
||||||
if listener_object.packet not in listener_collection:
|
|
||||||
listener_collection[listener_object.packet] = []
|
|
||||||
|
|
||||||
if listener_object not in listener_collection[listener_object.packet]:
|
|
||||||
if listener_object.priority == Priority.High:
|
|
||||||
listener_collection[listener_object.packet].insert(0, listener_object)
|
|
||||||
elif listener_object.priority == Priority.Override:
|
|
||||||
listener_collection[listener_object.packet] = [listener_object]
|
|
||||||
else:
|
|
||||||
listener_collection[listener_object.packet].append(listener_object)
|
|
||||||
|
|
||||||
for listener_name, listener_object in listener_objects:
|
|
||||||
listener_collection = xt_listeners if type(listener_object) == _XTListener else xml_listeners
|
|
||||||
for override in listener_object.overrides:
|
|
||||||
listener_collection[override.packet].remove(override)
|
|
||||||
|
|
||||||
|
|
||||||
def remove_handlers_by_module(xt_listeners, xml_listeners, handler_module_path):
|
|
||||||
def remove_handlers(remove_handler_items):
|
|
||||||
for handler_id, handler_listeners in remove_handler_items:
|
|
||||||
for handler_listener in handler_listeners:
|
|
||||||
handler_file = get_relative_function_path(handler_listener.callback)
|
|
||||||
if handler_file == handler_module_path:
|
|
||||||
handler_listeners.remove(handler_listener)
|
|
||||||
remove_handlers(xt_listeners.items())
|
|
||||||
remove_handlers(xml_listeners.items())
|
|
||||||
|
|
||||||
|
|
||||||
def cooldown(per=1.0, rate=1, bucket_type=BucketType.Default, callback=None):
|
def cooldown(per=1.0, rate=1, bucket_type=BucketType.Default, callback=None):
|
||||||
def decorator(handler_function):
|
def decorator(handler_function):
|
||||||
handler_function.__cooldown = _CooldownMapping(callback, _Cooldown(per, rate, bucket_type))
|
handler_function.__cooldown = _CooldownMapping(callback, _Cooldown(per, rate, bucket_type))
|
||||||
|
@ -39,13 +39,13 @@ except ImportError:
|
|||||||
uvloop = None
|
uvloop = None
|
||||||
|
|
||||||
import houdini.handlers
|
import houdini.handlers
|
||||||
from houdini.handlers import listeners_from_module, remove_handlers_by_module
|
import houdini.plugins
|
||||||
from houdini.events.handler_file_event import HandlerFileEventHandler
|
from houdini.events.listener_file_event import ListenerFileEventHandler
|
||||||
from houdini.events.plugin_file_event import PluginFileEventHandler
|
from houdini.events.plugin_file_event import PluginFileEventHandler
|
||||||
|
|
||||||
from houdini.commands import commands_from_plugin
|
from houdini.handlers import XTListenerManager, XMLListenerManager
|
||||||
|
from houdini.plugins import PluginManager
|
||||||
import houdini.plugins as plugins
|
from houdini.commands import CommandManager
|
||||||
|
|
||||||
|
|
||||||
class HoudiniFactory:
|
class HoudiniFactory:
|
||||||
@ -72,9 +72,11 @@ class HoudiniFactory:
|
|||||||
|
|
||||||
self.login_attempts = {}
|
self.login_attempts = {}
|
||||||
|
|
||||||
self.xt_listeners, self.xml_listeners = {}, {}
|
self.xt_listeners = XTListenerManager(self)
|
||||||
self.commands = {}
|
self.xml_listeners = XMLListenerManager(self)
|
||||||
self.plugins = {}
|
self.commands = CommandManager(self)
|
||||||
|
self.plugins = PluginManager(self)
|
||||||
|
|
||||||
|
|
||||||
self.items = None
|
self.items = None
|
||||||
self.igloos = None
|
self.igloos = None
|
||||||
@ -144,7 +146,7 @@ class HoudiniFactory:
|
|||||||
self.config.database['Address'],
|
self.config.database['Address'],
|
||||||
self.config.database['Name']))
|
self.config.database['Name']))
|
||||||
|
|
||||||
self.logger.info('houdini module instantiated')
|
self.logger.info('Houdini module instantiated')
|
||||||
|
|
||||||
self.redis = await aioredis.create_redis_pool('redis://{}:{}'.format(
|
self.redis = await aioredis.create_redis_pool('redis://{}:{}'.format(
|
||||||
self.config.redis['Address'], self.config.redis['Port']),
|
self.config.redis['Address'], self.config.redis['Port']),
|
||||||
@ -163,10 +165,11 @@ class HoudiniFactory:
|
|||||||
PenguinStringCompiler.setup_default_builder(self.penguin_string_compiler)
|
PenguinStringCompiler.setup_default_builder(self.penguin_string_compiler)
|
||||||
PenguinStringCompiler.setup_anonymous_default_builder(self.anonymous_penguin_string_compiler)
|
PenguinStringCompiler.setup_anonymous_default_builder(self.anonymous_penguin_string_compiler)
|
||||||
|
|
||||||
self.load_handler_modules(exclude_load='houdini.Handlers.Login.Login')
|
self.xml_listeners.setup(houdini.handlers, exclude_load='houdini.handlers.login.login')
|
||||||
|
self.xt_listeners.setup(houdini.handlers)
|
||||||
self.logger.info('World server started')
|
self.logger.info('World server started')
|
||||||
else:
|
else:
|
||||||
self.load_handler_modules('houdini.Handlers.Login.Login')
|
self.xml_listeners.setup(houdini.handlers, 'houdini.handlers.login.login')
|
||||||
self.logger.info('Login server started')
|
self.logger.info('Login server started')
|
||||||
|
|
||||||
self.items = await ItemCrumbsCollection.get_collection()
|
self.items = await ItemCrumbsCollection.get_collection()
|
||||||
@ -207,60 +210,20 @@ class HoudiniFactory:
|
|||||||
|
|
||||||
handlers_path = './houdini{}handlers'.format(os.path.sep)
|
handlers_path = './houdini{}handlers'.format(os.path.sep)
|
||||||
plugins_path = './houdini{}plugins'.format(os.path.sep)
|
plugins_path = './houdini{}plugins'.format(os.path.sep)
|
||||||
self.configure_observers([handlers_path, HandlerFileEventHandler],
|
self.configure_observers([handlers_path, ListenerFileEventHandler],
|
||||||
[plugins_path, PluginFileEventHandler])
|
[plugins_path, PluginFileEventHandler])
|
||||||
|
|
||||||
self.logger.info('Listening on {}:{}'.format(self.server_config['Address'], self.server_config['Port']))
|
self.logger.info('Listening on {}:{}'.format(self.server_config['Address'], self.server_config['Port']))
|
||||||
|
|
||||||
await self.load_plugins()
|
self.plugins.setup(houdini.plugins)
|
||||||
|
|
||||||
async with self.server:
|
async with self.server:
|
||||||
await self.server.serve_forever()
|
await self.server.serve_forever()
|
||||||
|
|
||||||
async def load_plugins(self):
|
|
||||||
for plugin_package in self.get_package_modules(plugins):
|
|
||||||
await self.load_plugin(plugin_package)
|
|
||||||
|
|
||||||
async def load_plugin(self, plugin):
|
|
||||||
plugin_module, plugin_class = plugin
|
|
||||||
|
|
||||||
if plugin_class not in self.server_config['Plugins']:
|
|
||||||
return
|
|
||||||
|
|
||||||
plugin_object = getattr(plugin_module, plugin_class)(self)
|
|
||||||
|
|
||||||
if isinstance(plugin_object, plugins.IPlugin):
|
|
||||||
self.plugins[plugin_class] = plugin_object
|
|
||||||
|
|
||||||
listeners_from_module(self.xt_listeners, self.xml_listeners, plugin_object)
|
|
||||||
commands_from_plugin(self.commands, plugin_object)
|
|
||||||
|
|
||||||
await plugin_object.ready()
|
|
||||||
else:
|
|
||||||
self.logger.warn('{0} plugin object doesn\'t provide the plugin interface'.format(plugin_class))
|
|
||||||
|
|
||||||
def unload_plugin(self, plugin):
|
|
||||||
plugin_module, plugin_class = plugin
|
|
||||||
|
|
||||||
if plugin_class in self.plugins:
|
|
||||||
plugin_module_path = plugin_module.__file__
|
|
||||||
del self.plugins[plugin_class]
|
|
||||||
|
|
||||||
remove_handlers_by_module(self.xt_listeners, self.xml_listeners, plugin_module_path)
|
|
||||||
|
|
||||||
async def client_connected(self, reader, writer):
|
async def client_connected(self, reader, writer):
|
||||||
client_object = self.client_class(self, reader, writer)
|
client_object = self.client_class(self, reader, writer)
|
||||||
await client_object.run()
|
await client_object.run()
|
||||||
|
|
||||||
def load_handler_modules(self, strict_load=None, exclude_load=None):
|
|
||||||
for handler_module in self.get_package_modules(houdini.handlers):
|
|
||||||
if not (strict_load and handler_module not in strict_load or exclude_load and handler_module in exclude_load):
|
|
||||||
if handler_module not in sys.modules.keys():
|
|
||||||
module = importlib.import_module(handler_module)
|
|
||||||
listeners_from_module(self.xt_listeners, self.xml_listeners, module)
|
|
||||||
|
|
||||||
self.logger.info('Handler modules loaded')
|
|
||||||
|
|
||||||
def get_package_modules(self, package):
|
def get_package_modules(self, package):
|
||||||
package_modules = []
|
package_modules = []
|
||||||
|
|
||||||
@ -271,8 +234,8 @@ class HoudiniFactory:
|
|||||||
subpackage_object = importlib.import_module(full_module_name, package=package.__path__)
|
subpackage_object = importlib.import_module(full_module_name, package=package.__path__)
|
||||||
subpackage_object_directory = dir(subpackage_object)
|
subpackage_object_directory = dir(subpackage_object)
|
||||||
|
|
||||||
if plugins.IPlugin.__name__ in subpackage_object_directory:
|
if houdini.plugins.IPlugin.__name__ in subpackage_object_directory:
|
||||||
package_modules.append((subpackage_object, module_name))
|
package_modules.append(subpackage_object)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
sub_package_modules = self.get_package_modules(subpackage_object)
|
sub_package_modules = self.get_package_modules(subpackage_object)
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
from abc import ABC
|
from abc import ABC
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from houdini import _AbstractManager
|
||||||
|
|
||||||
|
|
||||||
class IPlugin(ABC):
|
class IPlugin(ABC):
|
||||||
"""
|
"""
|
||||||
@ -24,10 +29,42 @@ class IPlugin(ABC):
|
|||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def ready(self):
|
async def ready(self):
|
||||||
"""
|
"""Called when the plugin is ready to function."""
|
||||||
Called when the plugin is ready to function.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def __init__(self, server):
|
def __init__(self, server):
|
||||||
self.server = server
|
self.server = server
|
||||||
|
|
||||||
|
|
||||||
|
class PluginManager(_AbstractManager):
|
||||||
|
def setup(self, module):
|
||||||
|
for plugin_package in self.server.get_package_modules(module):
|
||||||
|
self.load(plugin_package)
|
||||||
|
|
||||||
|
def load(self, module):
|
||||||
|
plugin_class, plugin_type = inspect.getmembers(module, is_plugin).pop()
|
||||||
|
|
||||||
|
if self.server.server_config['Plugins'] is not True and \
|
||||||
|
plugin_class not in self.server.server_config['Plugins']:
|
||||||
|
return
|
||||||
|
|
||||||
|
plugin_object = plugin_type(self.server)
|
||||||
|
self[module.__name__] = plugin_object
|
||||||
|
|
||||||
|
self.server.commands.load(plugin_object)
|
||||||
|
self.server.xt_listeners.load(plugin_object)
|
||||||
|
self.server.xml_listeners.load(plugin_object)
|
||||||
|
|
||||||
|
asyncio.run_coroutine_threadsafe(plugin_object.ready(), self.server.server.get_loop())
|
||||||
|
|
||||||
|
def remove(self, module):
|
||||||
|
if module.__name__ in self:
|
||||||
|
del self[module.__name__]
|
||||||
|
|
||||||
|
self.server.commands.remove(module)
|
||||||
|
self.server.xt_listeners.remove(module)
|
||||||
|
self.server.xml_listeners.remove(module)
|
||||||
|
|
||||||
|
|
||||||
|
def is_plugin(plugin_class):
|
||||||
|
return inspect.isclass(plugin_class) and issubclass(plugin_class, IPlugin) and plugin_class != IPlugin
|
||||||
|
@ -85,7 +85,7 @@ class Spheniscidae:
|
|||||||
packet_id = parsed_data[2]
|
packet_id = parsed_data[2]
|
||||||
packet = XTPacket(packet_id)
|
packet = XTPacket(packet_id)
|
||||||
|
|
||||||
if handlers.listener_exists(self.server.xt_listeners, self.server.xml_listeners, packet):
|
if packet in self.server.xt_listeners:
|
||||||
xt_listeners = self.server.xt_listeners[packet]
|
xt_listeners = self.server.xt_listeners[packet]
|
||||||
packet_data = parsed_data[4:]
|
packet_data = parsed_data[4:]
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ class Spheniscidae:
|
|||||||
action = body_tag.get('action')
|
action = body_tag.get('action')
|
||||||
packet = XMLPacket(action)
|
packet = XMLPacket(action)
|
||||||
|
|
||||||
if handlers.listener_exists(self.server.xt_listeners, self.server.xml_listeners, packet):
|
if packet in self.server.xml_listeners:
|
||||||
xml_listeners = self.server.xml_listeners[packet]
|
xml_listeners = self.server.xml_listeners[packet]
|
||||||
|
|
||||||
for listener in xml_listeners:
|
for listener in xml_listeners:
|
||||||
|
Loading…
Reference in New Issue
Block a user