From 7a428b703b5fb24e40e3104f27f440007c2359d3 Mon Sep 17 00:00:00 2001 From: David Estrada Date: Thu, 9 Apr 2020 13:47:17 -0700 Subject: [PATCH 01/11] Manager.py is returning a couple of querysets instead of lists. Fix for bug #2088 --- evennia/objects/manager.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/evennia/objects/manager.py b/evennia/objects/manager.py index 1217d0a91d..f66c1fe253 100644 --- a/evennia/objects/manager.py +++ b/evennia/objects/manager.py @@ -178,11 +178,11 @@ class ObjectDBManager(TypedObjectManager): # This doesn't work if attribute_value is an object. Workaround below if isinstance(attribute_value, (str, int, float, bool)): - return self.filter( + return list(self.filter( cand_restriction & type_restriction & Q(db_attributes__db_key=attribute_name, db_attributes__db_value=attribute_value) - ).order_by("id") + ).order_by("id")) else: # We must loop for safety since the referenced lookup gives deepcopy error if attribute value is an object. global _ATTR @@ -278,7 +278,7 @@ class ObjectDBManager(TypedObjectManager): exclude_restriction = ( Q(pk__in=[_GA(obj, "id") for obj in make_iter(excludeobj)]) if excludeobj else Q() ) - return self.filter(db_location=location).exclude(exclude_restriction).order_by("id") + return list(self.filter(db_location=location).exclude(exclude_restriction).order_by("id")) def get_objs_with_key_or_alias(self, ostring, exact=True, candidates=None, typeclasses=None): """ @@ -309,7 +309,7 @@ class ObjectDBManager(TypedObjectManager): type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q() if exact: # exact match - do direct search - return ( + return list( ( self.filter( cand_restriction From e2b878ff3ced841417b3c178154131ed9e956a85 Mon Sep 17 00:00:00 2001 From: trhr Date: Mon, 20 Apr 2020 00:54:33 -0500 Subject: [PATCH 02/11] EvenniaLocalTest - uses settings-defined typeclasses --- evennia/utils/test_resources.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/evennia/utils/test_resources.py b/evennia/utils/test_resources.py index 053541391f..d9d8967cdf 100644 --- a/evennia/utils/test_resources.py +++ b/evennia/utils/test_resources.py @@ -157,3 +157,11 @@ class EvenniaTest(TestCase): self.account.delete() self.account2.delete() super().tearDown() + +class LocalEvenniaTest(EvenniaTest): + account_typeclass = settings.BASE_ACCOUNT_TYPECLASS + object_typeclass = settings.BASE_OBJECT_TYPECLASS + character_typeclass = settings.BASE_CHARACTER_TYPECLASS + exit_typeclass = settings.BASE_EXIT_TYPECLASS + room_typeclass = settings.BASE_ROOM_TYPECLASS + script_typeclass = settings.BASE_SCRIPT_TYPECLASS From 8512364560ef8e59af068e5a9eb8f77cc289da9a Mon Sep 17 00:00:00 2001 From: David Estrada Date: Mon, 20 Apr 2020 18:36:08 -0700 Subject: [PATCH 03/11] Some of the doc strings were incorrect in manager.py. Manager.py methods should return querySets, not lists --- evennia/objects/manager.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/evennia/objects/manager.py b/evennia/objects/manager.py index f66c1fe253..3e609991ff 100644 --- a/evennia/objects/manager.py +++ b/evennia/objects/manager.py @@ -159,7 +159,7 @@ class ObjectDBManager(TypedObjectManager): typeclasses (list, optional): Python pats to restrict matches with. Returns: - matches (list): Objects fullfilling both the `attribute_name` and + matches (query): Objects fullfilling both the `attribute_name` and `attribute_value` criterions. Notes: @@ -178,11 +178,11 @@ class ObjectDBManager(TypedObjectManager): # This doesn't work if attribute_value is an object. Workaround below if isinstance(attribute_value, (str, int, float, bool)): - return list(self.filter( + return self.filter( cand_restriction & type_restriction & Q(db_attributes__db_key=attribute_name, db_attributes__db_value=attribute_value) - ).order_by("id")) + ).order_by("id") else: # We must loop for safety since the referenced lookup gives deepcopy error if attribute value is an object. global _ATTR @@ -273,12 +273,12 @@ class ObjectDBManager(TypedObjectManager): to exclude from the match. Returns: - contents (list): Matching contents, without excludeobj, if given. + contents (query): Matching contents, without excludeobj, if given. """ exclude_restriction = ( Q(pk__in=[_GA(obj, "id") for obj in make_iter(excludeobj)]) if excludeobj else Q() ) - return list(self.filter(db_location=location).exclude(exclude_restriction).order_by("id")) + return self.filter(db_location=location).exclude(exclude_restriction).order_by("id") def get_objs_with_key_or_alias(self, ostring, exact=True, candidates=None, typeclasses=None): """ @@ -291,7 +291,7 @@ class ObjectDBManager(TypedObjectManager): typeclasses (list): Only match objects with typeclasses having thess path strings. Returns: - matches (list): A list of matches of length 0, 1 or more. + matches (query): A list of matches of length 0, 1 or more. """ if not isinstance(ostring, str): if hasattr(ostring, "key"): @@ -309,7 +309,7 @@ class ObjectDBManager(TypedObjectManager): type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q() if exact: # exact match - do direct search - return list( + return ( ( self.filter( cand_restriction From 508052ed2e63cf5990cbd1001abe47c831c0b339 Mon Sep 17 00:00:00 2001 From: David Estrada Date: Mon, 20 Apr 2020 18:36:49 -0700 Subject: [PATCH 04/11] Send Search Results through _AT_SEARCH_RESULT in DefaultObject class --- evennia/objects/objects.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index de9d09abae..b5ec9a7647 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -498,12 +498,11 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): use_dbref=use_dbref, ) - if quiet: - return results return _AT_SEARCH_RESULT( results, self, query=searchdata, + quiet=quiet, nofound_string=nofound_string, multimatch_string=multimatch_string, ) From 8e294e68ffffa6f5601e13b605a6f261303c5ef0 Mon Sep 17 00:00:00 2001 From: David Estrada Date: Mon, 20 Apr 2020 18:59:55 -0700 Subject: [PATCH 05/11] Seems DefaultObject search should return a list, even if _AT_SEARCH_RESULT returns None --- evennia/objects/objects.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index b5ec9a7647..c51b8079ba 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -498,7 +498,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): use_dbref=use_dbref, ) - return _AT_SEARCH_RESULT( + matches = _AT_SEARCH_RESULT( results, self, query=searchdata, @@ -506,6 +506,9 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): nofound_string=nofound_string, multimatch_string=multimatch_string, ) + if matches: + return matches + return [] def search_account(self, searchdata, quiet=False): """ From 638bb2f4e7cdd27630d2c01d145635d2fd94e695 Mon Sep 17 00:00:00 2001 From: David Estrada Date: Mon, 20 Apr 2020 19:20:50 -0700 Subject: [PATCH 06/11] Misunderstood what the quiet flag was doing. If quiet flag is set, DefaultObject will return a list --- evennia/objects/objects.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index c51b8079ba..d9d355172a 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -498,7 +498,10 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): use_dbref=use_dbref, ) - matches = _AT_SEARCH_RESULT( + if quiet: + return list(results) + + return _AT_SEARCH_RESULT( results, self, query=searchdata, @@ -506,9 +509,6 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): nofound_string=nofound_string, multimatch_string=multimatch_string, ) - if matches: - return matches - return [] def search_account(self, searchdata, quiet=False): """ From 2c8603bdc12fccbe4cee8d20878648af3c4961f2 Mon Sep 17 00:00:00 2001 From: David Estrada Date: Mon, 20 Apr 2020 19:23:04 -0700 Subject: [PATCH 07/11] No longer passing quiet argument to _AT_SEARCH_RESULT. Took out empty line --- evennia/objects/objects.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index d9d355172a..2d093f15ab 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -500,12 +500,10 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): if quiet: return list(results) - return _AT_SEARCH_RESULT( results, self, query=searchdata, - quiet=quiet, nofound_string=nofound_string, multimatch_string=multimatch_string, ) From e270ea86519f85af73c85fe0efdf7ab99cb71394 Mon Sep 17 00:00:00 2001 From: trhr Date: Wed, 22 Apr 2020 19:50:49 -0500 Subject: [PATCH 08/11] added docstring --- evennia/utils/test_resources.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/evennia/utils/test_resources.py b/evennia/utils/test_resources.py index d9d8967cdf..f9809b8c7f 100644 --- a/evennia/utils/test_resources.py +++ b/evennia/utils/test_resources.py @@ -159,6 +159,10 @@ class EvenniaTest(TestCase): super().tearDown() class LocalEvenniaTest(EvenniaTest): + """ + This test class is intended for inheriting in mygame tests. + It helps ensure your tests are run with your own objects. + """ account_typeclass = settings.BASE_ACCOUNT_TYPECLASS object_typeclass = settings.BASE_OBJECT_TYPECLASS character_typeclass = settings.BASE_CHARACTER_TYPECLASS From 586414247f7d61acc676885990a8b199ded5d39e Mon Sep 17 00:00:00 2001 From: Griatch Date: Thu, 23 Apr 2020 19:08:23 +0200 Subject: [PATCH 09/11] Server timekeeper no longer assume 60s between snapshots --- evennia/server/server.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/evennia/server/server.py b/evennia/server/server.py index 2093b5b88c..90c46766ef 100644 --- a/evennia/server/server.py +++ b/evennia/server/server.py @@ -105,6 +105,7 @@ _IDMAPPER_CACHE_MAXSIZE = settings.IDMAPPER_CACHE_MAXSIZE _GAMETIME_MODULE = None _IDLE_TIMEOUT = settings.IDLE_TIMEOUT +_LAST_SERVER_TIME_SNAPSHOT = None def _server_maintenance(): @@ -113,6 +114,8 @@ def _server_maintenance(): the server needs to do. It is called every minute. """ global EVENNIA, _MAINTENANCE_COUNT, _FLUSH_CACHE, _GAMETIME_MODULE + global _LAST_SERVER_TIME_SNAPSHOT + if not _FLUSH_CACHE: from evennia.utils.idmapper.models import conditional_flush as _FLUSH_CACHE if not _GAMETIME_MODULE: @@ -126,7 +129,11 @@ def _server_maintenance(): _GAMETIME_MODULE.SERVER_START_TIME = now _GAMETIME_MODULE.SERVER_RUNTIME = ServerConfig.objects.conf("runtime", default=0.0) else: - _GAMETIME_MODULE.SERVER_RUNTIME += 60.0 + # adjust the runtime not with 60s but with the actual elapsed time + # in case this may varies slightly from 60s. + _GAMETIME_MODULE.SERVER_RUNTIME += (now - _LAST_SERVER_TIME_SNAPSHOT) + _LAST_SERVER_TIME_SNAPSHOT = now + # update game time and save it across reloads _GAMETIME_MODULE.SERVER_RUNTIME_LAST_UPDATED = now ServerConfig.objects.conf("runtime", _GAMETIME_MODULE.SERVER_RUNTIME) From 60ca9f19cfd2a7470524d115fd578d9ff9f6fff7 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Fri, 15 May 2020 22:12:23 -0400 Subject: [PATCH 10/11] Bug: Puzzles do not survive server reload (because their scripts aren't persisted). --- evennia/contrib/puzzles.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/contrib/puzzles.py b/evennia/contrib/puzzles.py index 1ca661cd5d..5eef1f77e2 100644 --- a/evennia/contrib/puzzles.py +++ b/evennia/contrib/puzzles.py @@ -289,7 +289,7 @@ class CmdCreatePuzzleRecipe(MuxCommand): proto_parts = [proto_def(obj) for obj in parts] proto_results = [proto_def(obj) for obj in results] - puzzle = create_script(PuzzleRecipe, key=puzzle_name) + puzzle = create_script(PuzzleRecipe, key=puzzle_name, persistent=True) puzzle.save_recipe(puzzle_name, proto_parts, proto_results) puzzle.locks.add("control:id(%s) or perm(Builder)" % caller.dbref[1:]) @@ -488,7 +488,7 @@ class CmdArmPuzzle(MuxCommand): Notes: Create puzzles with `@puzzle`; get list of - defined puzzles using `@lspuzlerecipies`. + defined puzzles using `@lspuzzlerecipes`. """ From dfea32ec82dc4fe715133df8c9cbb5f5b7f2eb9e Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 16 May 2020 09:56:13 +0200 Subject: [PATCH 11/11] Fix server-time-snapshot unittest patch issue --- evennia/server/server.py | 3 ++- evennia/server/tests/test_server.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/evennia/server/server.py b/evennia/server/server.py index 90c46766ef..880e9a1843 100644 --- a/evennia/server/server.py +++ b/evennia/server/server.py @@ -105,7 +105,7 @@ _IDMAPPER_CACHE_MAXSIZE = settings.IDMAPPER_CACHE_MAXSIZE _GAMETIME_MODULE = None _IDLE_TIMEOUT = settings.IDLE_TIMEOUT -_LAST_SERVER_TIME_SNAPSHOT = None +_LAST_SERVER_TIME_SNAPSHOT = 0 def _server_maintenance(): @@ -128,6 +128,7 @@ def _server_maintenance(): # first call after a reload _GAMETIME_MODULE.SERVER_START_TIME = now _GAMETIME_MODULE.SERVER_RUNTIME = ServerConfig.objects.conf("runtime", default=0.0) + _LAST_SERVER_TIME_SNAPSHOT = now else: # adjust the runtime not with 60s but with the actual elapsed time # in case this may varies slightly from 60s. diff --git a/evennia/server/tests/test_server.py b/evennia/server/tests/test_server.py index 076a03a879..33a9341cae 100644 --- a/evennia/server/tests/test_server.py +++ b/evennia/server/tests/test_server.py @@ -66,6 +66,7 @@ class TestServer(TestCase): connection=DEFAULT, _IDMAPPER_CACHE_MAXSIZE=1000, _MAINTENANCE_COUNT=3600 - 1, + _LAST_SERVER_TIME_SNAPSHOT=0, ServerConfig=DEFAULT, ) as mocks: mocks["connection"].close = MagicMock() @@ -84,6 +85,7 @@ class TestServer(TestCase): connection=DEFAULT, _IDMAPPER_CACHE_MAXSIZE=1000, _MAINTENANCE_COUNT=3700 - 1, + _LAST_SERVER_TIME_SNAPSHOT=0, ServerConfig=DEFAULT, ) as mocks: mocks["connection"].close = MagicMock() @@ -101,6 +103,7 @@ class TestServer(TestCase): connection=DEFAULT, _IDMAPPER_CACHE_MAXSIZE=1000, _MAINTENANCE_COUNT=(3600 * 7) - 1, + _LAST_SERVER_TIME_SNAPSHOT=0, ServerConfig=DEFAULT, ) as mocks: mocks["connection"].close = MagicMock() @@ -117,6 +120,7 @@ class TestServer(TestCase): connection=DEFAULT, _IDMAPPER_CACHE_MAXSIZE=1000, _MAINTENANCE_COUNT=(3600 * 7) - 1, + _LAST_SERVER_TIME_SNAPSHOT=0, SESSIONS=DEFAULT, _IDLE_TIMEOUT=10, time=DEFAULT,