diff --git a/evennia/__init__.py b/evennia/__init__.py index 0a5db98372..c26da5f8ef 100644 --- a/evennia/__init__.py +++ b/evennia/__init__.py @@ -390,6 +390,9 @@ def _init(): BASE_GUEST_TYPECLASS = class_from_module(settings.BASE_GUEST_TYPECLASS) del class_from_module + # delayed starts + GLOBAL_SCRIPTS.start() + def set_trace(term_size=(140, 40), debugger="auto"): """ diff --git a/evennia/utils/containers.py b/evennia/utils/containers.py index 615b22ebc1..09fec4c18b 100644 --- a/evennia/utils/containers.py +++ b/evennia/utils/containers.py @@ -16,6 +16,9 @@ from evennia.utils.utils import class_from_module, callables_from_module from evennia.utils import logger +SCRIPTDB = None + + class Container(object): """ Base container class. A container is simply a storage object whose @@ -87,8 +90,8 @@ class OptionContainer(Container): class GlobalScriptContainer(Container): """ Simple Handler object loaded by the Evennia API to contain and manage a - game's Global Scripts. Scripts to start are defined by - `settings.GLOBAL_SCRIPTS`. + game's Global Scripts. This will list global Scripts created on their own + but will also auto-(re)create scripts defined in `settings.GLOBAL_SCRIPTS`. Example: import evennia @@ -101,17 +104,72 @@ class GlobalScriptContainer(Container): """ def __init__(self): """ - Initialize the container by preparing scripts. Lazy-load only when the - script is requested. - Note: We must delay loading of typeclasses since this module may get initialized before Scripts are actually initialized. """ + self.typeclass_storage = None self.loaded_data = {key: {} if data is None else data for key, data in settings.GLOBAL_SCRIPTS.items()} - self.script_storage = {} - self.typeclass_storage = None + + def _get_scripts(self, key=None, default=None): + global SCRIPTDB + if not SCRIPTDB: + from evennia.scripts.models import ScriptDB as SCRIPTDB + if key: + try: + return SCRIPTDB.objects.get(db_key__exact=key, db_obj__isnull=True) + except SCRIPTDB.DoesNotExist: + return default + else: + return SCRIPTDB.objects.filter(db_obj__isnull=True) + + def _load_script(self, key): + self.load_data() + + typeclass = self.typeclass_storage[key] + found = typeclass.objects.filter(db_key=key).first() + interval = self.loaded_data[key].get('interval', None) + start_delay = self.loaded_data[key].get('start_delay', None) + repeats = self.loaded_data[key].get('repeats', 0) + desc = self.loaded_data[key].get('desc', '') + + if not found: + logger.log_info(f"GLOBAL_SCRIPTS: (Re)creating {key} ({typeclass}).") + new_script, errors = typeclass.create(key=key, persistent=True, + interval=interval, + start_delay=start_delay, + repeats=repeats, desc=desc) + if errors: + logger.log_err("\n".join(errors)) + return None + + new_script.start() + return new_script + + if ((found.interval != interval) or + (found.start_delay != start_delay) or + (found.repeats != repeats)): + found.restart(interval=interval, start_delay=start_delay, repeats=repeats) + if found.desc != desc: + found.desc = desc + return found + + def start(self): + """ + Called last in evennia.__init__ to initialize the container late + (after script typeclasses have finished loading). + + We include all global scripts in the handler and + make sure to auto-load time-based scripts. + + """ + # populate self.typeclass_storage + self.load_data() + + # start registered scripts + for key in self.loaded_data: + self._load_script(key) def load_data(self): """ @@ -129,42 +187,11 @@ class GlobalScriptContainer(Container): logger.log_err( f"GlobalScriptContainer could not start global script {key}: {err}") - def _load_script(self, key): - - self.load_data() - - typeclass = self.typeclass_storage[key] - found = typeclass.objects.filter(db_key=key).first() - interval = self.loaded_data[key].get('interval', None) - start_delay = self.loaded_data[key].get('start_delay', None) - repeats = self.loaded_data[key].get('repeats', 0) - desc = self.loaded_data[key].get('desc', '') - - if not found: - new_script, errors = typeclass.create(key=key, persistent=True, - interval=interval, - start_delay=start_delay, - repeats=repeats, desc=desc) - if errors: - logger.log_err("\n".join(errors)) - return None - - new_script.start() - self.script_storage[key] = new_script - return new_script - - if ((found.interval != interval) or - (found.start_delay != start_delay) or - (found.repeats != repeats)): - found.restart(interval=interval, start_delay=start_delay, repeats=repeats) - if found.desc != desc: - found.desc = desc - self.script_storage[key] = found - return found - def get(self, key, default=None): """ - Retrive data by key (in case of not knowing it beforehand). + Retrive data by key (in case of not knowing it beforehand). Any + scripts that are in settings.GLOBAL_SCRIPTS that are not found + will be recreated on-demand. Args: key (str): The name of the script. @@ -174,20 +201,28 @@ class GlobalScriptContainer(Container): Returns: any (any): The data loaded on this container. """ - - if key not in self.loaded_data: + res = self._get_scripts(key) + if not res: + if key in self.loaded_data: + # recreate if we have the info + return self._load_script(key) or default return default - return self.script_storage.get(key) or self._load_script(key) + return res def all(self): """ - Get all scripts. + Get all global scripts. Note that this will not auto-start + scripts defined in settings. Returns: scripts (list): All global script objects stored on the container. """ - return [self.__getattr__(key) for key in self.loaded_data] + self.typeclass_storage = None + self.load_data() + for key in self.loaded_data: + self._load_script(key) + return self._get_scripts(None) # Create all singletons