From 8c44766c0a9622557a41cd87b46063af59094882 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 28 Mar 2020 20:34:56 +0100 Subject: [PATCH] Handle going from location=None to unset and back in prototype, as per #2005 --- CHANGELOG.md | 3 +- evennia/commands/default/building.py | 32 +++++++++++++++++---- evennia/prototypes/menus.py | 3 +- evennia/prototypes/prototypes.py | 42 ++++++++++++++-------------- evennia/prototypes/spawner.py | 13 +++++---- 5 files changed, 59 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b22fcd398..ac78239c78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,7 +54,8 @@ without arguments starts a full interactive Python console. bugfixes. - Remove `dummy@example.com` as a default account email when unset, a string is no longer required by Django. -- Fixes to `spawn`, make updating an existing prototype/object work better. +- Fixes to `spawn`, make updating an existing prototype/object work better. Add `/raw` switch + to `spawn` command to extract the raw prototype dict for manual editing. ## Evennia 0.9 (2018-2019) diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index ab931370f8..e0f125079e 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -13,7 +13,8 @@ from evennia.utils.utils import ( class_from_module, get_all_typeclasses, variable_from_module, - dbref, interactive + dbref, interactive, + list_to_string ) from evennia.utils.eveditor import EvEditor from evennia.utils.evmore import EvMore @@ -3253,6 +3254,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): search - search prototype by name or tags. list - list available prototypes, optionally limit by tags. show, examine - inspect prototype by key. If not given, acts like list. + raw - show the raw dict of the prototype as a one-line string for manual editing. save - save a prototype to the database. It will be listable by /list. delete - remove a prototype from database, if allowed to. update - find existing objects with the same prototype_key and update @@ -3301,6 +3303,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): "search", "list", "show", + "raw", "examine", "save", "delete", @@ -3433,9 +3436,12 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): def _list_prototypes(self, key=None, tags=None): """Display prototypes as a list, optionally limited by key/tags. """ + table = protlib.list_prototypes(self.caller, key=key, tags=tags) + if not table: + return True EvMore( self.caller, - str(protlib.list_prototypes(self.caller, key=key, tags=tags)), + str(table), exit_on_lastpage=True, justify_kwargs=False, ) @@ -3492,8 +3498,8 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): Parse ;-separated input list. """ key, desc, tags = "", "", [] - if ";" in self.args: - parts = (part.strip().lower() for part in self.args.split(";")) + if ";" in argstring: + parts = [part.strip().lower() for part in argstring.split(";")] if len(parts) > 1 and desc: key = parts[0] desc = parts[1] @@ -3537,10 +3543,20 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): return # search for key;tag combinations - key, _, tags = self._parse_key_desc_tags(self.args, desc=False) + key, _, tags = self._parse_key_desc_tags(self.args, desc=False) self._list_prototypes(key, tags) return + if "raw" in self.switches: + # query for key match and return the prototype as a safe one-liner string. + if not self.args: + caller.msg("You need to specify a prototype-key to get the raw data for.") + prototype = self._search_prototype(self.args) + if not prototype: + return + caller.msg(str(prototype)) + return + if "show" in self.switches or "examine" in self.switches: # show a specific prot detail. The argument is a search query or empty. if not self.args: @@ -3556,7 +3572,11 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): if "list" in self.switches: # for list, all optional arguments are tags. - self._list_prototypes(tags=self.lhslist) + tags = self.lhslist + err = self._list_prototypes(tags=tags) + if err: + caller.msg("No prototypes found with prototype-tag(s): {}".format( + list_to_string(tags, "or"))) return if "save" in self.switches: diff --git a/evennia/prototypes/menus.py b/evennia/prototypes/menus.py index abaed9ef58..854f9e7ced 100644 --- a/evennia/prototypes/menus.py +++ b/evennia/prototypes/menus.py @@ -2294,7 +2294,8 @@ def node_apply_diff(caller, **kwargs): if not custom_location: diff.pop("location", None) - txt, options = _format_diff_text_and_options(diff, objects=update_objects, base_obj=base_obj) + txt, options = _format_diff_text_and_options(diff, objects=update_objects, + base_obj=base_obj, prototype=prototype) if options: text = [ diff --git a/evennia/prototypes/prototypes.py b/evennia/prototypes/prototypes.py index b80d972e36..7e76ee515e 100644 --- a/evennia/prototypes/prototypes.py +++ b/evennia/prototypes/prototypes.py @@ -272,10 +272,10 @@ def save_prototype(prototype): stored_prototype = create_script( DbPrototype, key=prototype_key, - desc=prototype["prototype_desc"], + desc=in_prototype["prototype_desc"], persistent=True, locks=prototype_locks, - tags=prototype["prototype_tags"], + tags=in_prototype["prototype_tags"], attributes=[("prototype", in_prototype)], ) return stored_prototype.prototype @@ -724,15 +724,15 @@ def prototype_to_str(prototype): prototype_desc=prototype.get("prototype_desc", "|wNone|n"), prototype_parent=prototype.get("prototype_parent", "|wNone|n"), ) - - key = prototype.get("key", "") - if key: + key = aliases = attrs = tags = locks = permissions = location = home = destination = "" + if "key" in prototype: + key = prototype["key"] key = "|ckey:|n {key}".format(key=key) - aliases = prototype.get("aliases", "") - if aliases: + if "aliases" in prototype: + aliases = prototype["aliases"] aliases = "|caliases:|n {aliases}".format(aliases=", ".join(aliases)) - attrs = prototype.get("attrs", "") - if attrs: + if "attrs" in prototype: + attrs = prototype["attrs"] out = [] for (attrkey, value, category, locks) in attrs: locks = ", ".join(lock for lock in locks if lock) @@ -751,8 +751,8 @@ def prototype_to_str(prototype): ) ) attrs = "|cattrs:|n\n {attrs}".format(attrs="\n ".join(out)) - tags = prototype.get("tags", "") - if tags: + if "tags" in prototype: + tags = prototype['tags'] out = [] for (tagkey, category, data) in tags: out.append( @@ -761,20 +761,20 @@ def prototype_to_str(prototype): ) ) tags = "|ctags:|n\n {tags}".format(tags=", ".join(out)) - locks = prototype.get("locks", "") - if locks: + if "locks" in prototype: + locks = prototype["locks"] locks = "|clocks:|n\n {locks}".format(locks=locks) - permissions = prototype.get("permissions", "") - if permissions: + if "permissions" in prototype: + permissions = prototype["permissions"] permissions = "|cpermissions:|n {perms}".format(perms=", ".join(permissions)) - location = prototype.get("location", "") - if location: + if "location" in prototype: + location = prototype["location"] location = "|clocation:|n {location}".format(location=location) - home = prototype.get("home", "") - if home: + if "home" in prototype: + home = prototype["home"] home = "|chome:|n {home}".format(home=home) - destination = prototype.get("destination", "") - if destination: + if "destination" in prototype: + destination = prototype["destination"] destination = "|cdestination:|n {destination}".format(destination=destination) body = "\n".join( diff --git a/evennia/prototypes/spawner.py b/evennia/prototypes/spawner.py index a4625f16f0..5ba3cd4b62 100644 --- a/evennia/prototypes/spawner.py +++ b/evennia/prototypes/spawner.py @@ -354,6 +354,9 @@ def prototype_diff(prototype1, prototype2, maxdepth=2, homogenize=False, implici class Unset: def __bool__(self): return False + def __str__(self): + return "" + _unset = Unset() def _recursive_diff(old, new, depth=0): @@ -361,10 +364,10 @@ def prototype_diff(prototype1, prototype2, maxdepth=2, homogenize=False, implici old_type = type(old) new_type = type(new) - if old_type == new_type and not (old and new): + if old_type == new_type and not (old or new): # both old and new are unset, like [] or None - return (old, new, "KEEP") - elif old_type != new_type: + return (None, None, "KEEP") + if old_type != new_type: if old and not new: if depth < maxdepth and old_type == dict: return {key: (part, None, "REMOVE") for key, part in old.items()} @@ -388,7 +391,7 @@ def prototype_diff(prototype1, prototype2, maxdepth=2, homogenize=False, implici elif depth < maxdepth and new_type == dict: all_keys = set(list(old.keys()) + list(new.keys())) return { - key: _recursive_diff(old.get(key), new.get(key, _unset), depth=depth + 1) + key: _recursive_diff(old.get(key, _unset), new.get(key, _unset), depth=depth + 1) for key in all_keys } elif depth < maxdepth and is_iter(new): @@ -396,7 +399,7 @@ def prototype_diff(prototype1, prototype2, maxdepth=2, homogenize=False, implici new_map = {part[0] if is_iter(part) else part: part for part in new} all_keys = set(list(old_map.keys()) + list(new_map.keys())) return { - key: _recursive_diff(old_map.get(key), new_map.get(key, _unset), depth=depth + 1) + key: _recursive_diff(old_map.get(key, _unset), new_map.get(key, _unset), depth=depth + 1) for key in all_keys } elif old != new: