From 5e2372f79ddcc9bbbb76bb9e16739974d400d531 Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 7 Feb 2022 20:45:48 +0100 Subject: [PATCH] Make sure Typeclass.at_init() is always called on cache initialization for any typeclass. Resolve #2641 --- evennia/scripts/models.py | 2 -- evennia/scripts/scripts.py | 8 ++++++++ evennia/scripts/tests.py | 8 +++++--- evennia/server/server.py | 8 +++++--- evennia/typeclasses/models.py | 8 ++++++++ evennia/utils/idmapper/models.py | 1 + 6 files changed, 27 insertions(+), 8 deletions(-) diff --git a/evennia/scripts/models.py b/evennia/scripts/models.py index a91046198d..29ad388b15 100644 --- a/evennia/scripts/models.py +++ b/evennia/scripts/models.py @@ -157,8 +157,6 @@ class ScriptDB(TypedObject): # deprecated ... pass if isinstance(value, (str, int)): - from evennia.objects.models import ObjectDB - value = to_str(value) if value.isdigit() or value.startswith("#"): dbid = dbref(value, reqhash=False) diff --git a/evennia/scripts/scripts.py b/evennia/scripts/scripts.py index 43f06e7830..eeed1473c9 100644 --- a/evennia/scripts/scripts.py +++ b/evennia/scripts/scripts.py @@ -473,6 +473,14 @@ class ScriptBase(ScriptDB, metaclass=TypeclassBase): super().delete() return True + def at_init(self): + """ + Called when the Script is cached in the idmapper. This is usually more reliable + than overriding `__init__` since the latter can be called at unexpected times. + + """ + pass + def at_script_creation(self): """ Should be overridden in child. diff --git a/evennia/scripts/tests.py b/evennia/scripts/tests.py index 305826fa2e..d639c5ca1d 100644 --- a/evennia/scripts/tests.py +++ b/evennia/scripts/tests.py @@ -11,9 +11,11 @@ from evennia.scripts.scripts import DoNothing, ExtendedLoopingCall class TestScript(BaseEvenniaTest): def test_create(self): "Check the script can be created via the convenience method." - obj, errors = DefaultScript.create("useless-machine") - self.assertTrue(obj, errors) - self.assertFalse(errors, errors) + with mock.patch("evennia.scripts.scripts.DefaultScript.at_init") as mockinit: + obj, errors = DefaultScript.create("useless-machine") + self.assertTrue(obj, errors) + self.assertFalse(errors, errors) + mockinit.assert_called() class TestScriptDB(TestCase): diff --git a/evennia/server/server.py b/evennia/server/server.py index 9b749f2f63..af7aeea343 100644 --- a/evennia/server/server.py +++ b/evennia/server/server.py @@ -395,7 +395,7 @@ class Evennia: mode (str): One of shutdown, reload or reset """ - from evennia.objects.models import ObjectDB + from evennia.typeclasses.models import TypedObject # start server time and maintenance task self.maintenance_task = LoopingCall(_server_maintenance) @@ -404,8 +404,10 @@ class Evennia: # update eventual changed defaults self.update_defaults() - [o.at_init() for o in ObjectDB.get_all_cached_instances()] - [p.at_init() for p in AccountDB.get_all_cached_instances()] + # run at_init() on all cached entities on reconnect + [[entity.at_init() + for entity in typeclass_db.get_all_cached_instances()] + for typeclass_db in TypedObject.__subclasses__()] # call correct server hook based on start file value if mode == "reload": diff --git a/evennia/typeclasses/models.py b/evennia/typeclasses/models.py index f47f9b267e..6512d30527 100644 --- a/evennia/typeclasses/models.py +++ b/evennia/typeclasses/models.py @@ -485,6 +485,14 @@ class TypedObject(SharedMemoryModel): # Object manipulation methods # + def at_init(self): + """ + Called when this object is loaded into cache. This is more reliable + than to override `__init__`. + + """ + pass + @classmethod def search(cls, query, **kwargs): """ diff --git a/evennia/utils/idmapper/models.py b/evennia/utils/idmapper/models.py index c62f3798b6..f9c187f98c 100644 --- a/evennia/utils/idmapper/models.py +++ b/evennia/utils/idmapper/models.py @@ -313,6 +313,7 @@ class SharedMemoryModel(Model, metaclass=SharedMemoryModelBase): """ pk = instance._get_pk_val() if pk is not None: + new = new or pk not in cls.__dbclass__.__instance_cache__ cls.__dbclass__.__instance_cache__[pk] = instance if new: try: