mirror of
https://github.com/evennia/evennia.git
synced 2026-04-02 22:17:17 +02:00
Start work on prototype updating
This commit is contained in:
parent
436ad4d8a5
commit
eeeef27283
2 changed files with 105 additions and 43 deletions
|
|
@ -13,10 +13,7 @@ from evennia.utils import create, utils, search
|
|||
from evennia.utils.utils import inherits_from, class_from_module, get_all_typeclasses
|
||||
from evennia.utils.eveditor import EvEditor
|
||||
from evennia.utils.evmore import EvMore
|
||||
from evennia.utils.spawner import (spawn, search_prototype, list_prototypes,
|
||||
save_db_prototype, validate_prototype,
|
||||
delete_db_prototype, PermissionError, start_olc,
|
||||
prototype_to_str)
|
||||
from evennia.utils import spawner
|
||||
from evennia.utils.ansi import raw
|
||||
|
||||
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||
|
|
@ -2792,12 +2789,6 @@ class CmdTag(COMMAND_DEFAULT_CLASS):
|
|||
string = "No tags attached to %s." % obj
|
||||
self.caller.msg(string)
|
||||
|
||||
#
|
||||
# To use the prototypes with the @spawn function set
|
||||
# PROTOTYPE_MODULES = ["commands.prototypes"]
|
||||
# Reload the server and the prototypes should be available.
|
||||
#
|
||||
|
||||
|
||||
class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
|
|
@ -2810,6 +2801,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
@spawn/search [key][;tag[,tag]]
|
||||
@spawn/list [tag, tag]
|
||||
@spawn/show [<key>]
|
||||
@spawn/update <key>
|
||||
|
||||
@spawn/save <key>[;desc[;tag,tag[,...][;lockstring]]] = <prototype_dict>
|
||||
@spawn/menu [<key>]
|
||||
|
|
@ -2823,6 +2815,10 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
show, examine - inspect prototype by key. If not given, acts like list.
|
||||
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
|
||||
them with latest version of given prototype. If given with /save,
|
||||
will auto-update all objects with the old version of the prototype
|
||||
without asking first.
|
||||
menu, olc - create/manipulate prototype in a menu interface.
|
||||
|
||||
Example:
|
||||
|
|
@ -2843,7 +2839,8 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
|waliases |n - string or list of strings.
|
||||
|wndb_|n<name> - value of a nattribute (ndb_ is stripped)
|
||||
|
||||
|wprototype_key|n - name of this prototype. Used to store/retrieve from db
|
||||
|wprototype_key|n - name of this prototype. Unique. Used to store/retrieve from db
|
||||
and update existing prototyped objects if desired.
|
||||
|wprototype_desc|n - desc of this prototype. Used in listings
|
||||
|wprototype_locks|n - locks of this prototype. Limits who may use prototype
|
||||
|wprototype_tags|n - tags of this prototype. Used to find prototype
|
||||
|
|
@ -2858,7 +2855,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
|
||||
key = "@spawn"
|
||||
aliases = ["@olc"]
|
||||
switch_options = ("noloc", "search", "list", "show", "save", "delete", "menu", "olc")
|
||||
switch_options = ("noloc", "search", "list", "show", "save", "delete", "menu", "olc", "update")
|
||||
locks = "cmd:perm(spawn) or perm(Builder)"
|
||||
help_category = "Building"
|
||||
|
||||
|
|
@ -2890,7 +2887,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
"use the 'exec' prototype key.")
|
||||
return None
|
||||
try:
|
||||
validate_prototype(prototype)
|
||||
spawner.validate_prototype(prototype)
|
||||
except RuntimeError as err:
|
||||
self.caller.msg(str(err))
|
||||
return
|
||||
|
|
@ -2899,9 +2896,9 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
def _search_show_prototype(query, prototypes=None):
|
||||
# prototype detail
|
||||
if not prototypes:
|
||||
prototypes = search_prototype(key=query)
|
||||
prototypes = spawner.search_prototype(key=query)
|
||||
if prototypes:
|
||||
return "\n".join(prototype_to_str(prot) for prot in prototypes)
|
||||
return "\n".join(spawner.prototype_to_str(prot) for prot in prototypes)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
|
@ -2912,7 +2909,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
prototype = None
|
||||
if self.lhs:
|
||||
key = self.lhs
|
||||
prototype = search_prototype(key=key, return_meta=True)
|
||||
prototype = spawner.search_prototype(key=key, return_meta=True)
|
||||
if len(prototype) > 1:
|
||||
caller.msg("More than one match for {}:\n{}".format(
|
||||
key, "\n".join(proto.get('prototype_key', '') for proto in prototype)))
|
||||
|
|
@ -2920,7 +2917,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
elif prototype:
|
||||
# one match
|
||||
prototype = prototype[0]
|
||||
start_olc(caller, session=self.session, prototype=prototype)
|
||||
spawner.start_olc(caller, session=self.session, prototype=prototype)
|
||||
return
|
||||
|
||||
if 'search' in self.switches:
|
||||
|
|
@ -2932,7 +2929,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
if ';' in self.args:
|
||||
key, tags = (part.strip().lower() for part in self.args.split(";", 1))
|
||||
tags = [tag.strip() for tag in tags.split(",")] if tags else None
|
||||
EvMore(caller, unicode(list_prototypes(caller, key=key, tags=tags)),
|
||||
EvMore(caller, unicode(spawner.list_prototypes(caller, key=key, tags=tags)),
|
||||
exit_on_lastpage=True)
|
||||
return
|
||||
|
||||
|
|
@ -2950,30 +2947,10 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
|
||||
if 'list' in self.switches:
|
||||
# for list, all optional arguments are tags
|
||||
EvMore(caller, unicode(list_prototypes(caller,
|
||||
EvMore(caller, unicode(spawner.list_prototypes(caller,
|
||||
tags=self.lhslist)), exit_on_lastpage=True)
|
||||
return
|
||||
|
||||
if 'delete' in self.switches:
|
||||
# remove db-based prototype
|
||||
matchstring = _search_show_prototype(self.args)
|
||||
if matchstring:
|
||||
question = "\nDo you want to continue deleting? [Y]/N"
|
||||
string = "|rDeleting prototype:|n\n{}".format(matchstring)
|
||||
answer = yield(string + question)
|
||||
if answer.lower() in ["n", "no"]:
|
||||
caller.msg("|rDeletion cancelled.|n")
|
||||
return
|
||||
try:
|
||||
success = delete_db_prototype(caller, self.args)
|
||||
except PermissionError as err:
|
||||
caller.msg("|rError deleting:|R {}|n".format(err))
|
||||
caller.msg("Deletion {}.".format(
|
||||
'successful' if success else 'failed (does the prototype exist?)'))
|
||||
return
|
||||
else:
|
||||
caller.msg("Could not find prototype '{}'".format(key))
|
||||
|
||||
if 'save' in self.switches:
|
||||
# store a prototype to the database store
|
||||
if not self.args or not self.rhs:
|
||||
|
|
@ -3015,6 +2992,12 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
if not prototype:
|
||||
return
|
||||
|
||||
# inject the prototype_* keys into the prototype to save
|
||||
prototype['prototype_key'] = prototype.get('prototype_key', key)
|
||||
prototype['prototype_desc'] = prototype.get('prototype_desc', desc)
|
||||
prototype['prototype_tags'] = prototype.get('prototype_tags', tags)
|
||||
prototype['prototype_locks'] = prototype.get('prototype_locks', lockstring)
|
||||
|
||||
# present prototype to save
|
||||
new_matchstring = _search_show_prototype("", prototypes=[prototype])
|
||||
string = "|yCreating new prototype:|n\n{}".format(new_matchstring)
|
||||
|
|
@ -3034,7 +3017,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
|
||||
# all seems ok. Try to save.
|
||||
try:
|
||||
prot = save_db_prototype(
|
||||
prot = spawner.save_db_prototype(
|
||||
caller, key, prototype, desc=desc, tags=tags, locks=lockstring)
|
||||
if not prot:
|
||||
caller.msg("|rError saving:|R {}.|n".format(key))
|
||||
|
|
@ -3046,14 +3029,68 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
caller.msg("|rError saving:|R {}|n".format(err))
|
||||
return
|
||||
caller.msg("|gSaved prototype:|n {}".format(key))
|
||||
|
||||
# check if we want to update existing objects
|
||||
existing_objects = spawner.search_objects_with_prototype(key)
|
||||
if existing_objects:
|
||||
if 'update' not in self.switches:
|
||||
n_existing = len(existing_objects)
|
||||
slow = " (note that this may be slow)" if n_existing > 10 else ""
|
||||
string = ("There are {} objects already created with an older version "
|
||||
"of prototype {}. Should it be re-applied to them{}? [Y]/N".format(
|
||||
n_existing, key, slow))
|
||||
answer = yield(string)
|
||||
if answer.lower() in ["n", "no"]:
|
||||
caller.msg("|rNo update was done of existing objects. "
|
||||
"Use @spawn/update <key> to apply later as needed.|n")
|
||||
return
|
||||
n_updated = spawner.batch_update_objects_with_prototype(existing_objects, key)
|
||||
caller.msg("{} objects were updated.".format(n_updated))
|
||||
return
|
||||
|
||||
if not self.args:
|
||||
ncount = len(search_prototype())
|
||||
ncount = len(spawner.search_prototype())
|
||||
caller.msg("Usage: @spawn <prototype-key> or {{key: value, ...}}"
|
||||
"\n ({} existing prototypes. Use /list to inspect)".format(ncount))
|
||||
return
|
||||
|
||||
if 'delete' in self.switches:
|
||||
# remove db-based prototype
|
||||
matchstring = _search_show_prototype(self.args)
|
||||
if matchstring:
|
||||
string = "|rDeleting prototype:|n\n{}".format(matchstring)
|
||||
question = "\nDo you want to continue deleting? [Y]/N"
|
||||
answer = yield(string + question)
|
||||
if answer.lower() in ["n", "no"]:
|
||||
caller.msg("|rDeletion cancelled.|n")
|
||||
return
|
||||
try:
|
||||
success = spawner.delete_db_prototype(caller, self.args)
|
||||
except PermissionError as err:
|
||||
caller.msg("|rError deleting:|R {}|n".format(err))
|
||||
caller.msg("Deletion {}.".format(
|
||||
'successful' if success else 'failed (does the prototype exist?)'))
|
||||
return
|
||||
else:
|
||||
caller.msg("Could not find prototype '{}'".format(key))
|
||||
|
||||
if 'update' in self.switches:
|
||||
# update existing prototypes
|
||||
key = self.args.strip().lower()
|
||||
existing_objects = spawner.search_objects_with_prototype(key)
|
||||
if existing_objects:
|
||||
n_existing = len(existing_objects)
|
||||
slow = " (note that this may be slow)" if n_existing > 10 else ""
|
||||
string = ("There are {} objects already created with an older version "
|
||||
"of prototype {}. Should it be re-applied to them{}? [Y]/N".format(
|
||||
n_existing, key, slow))
|
||||
answer = yield(string)
|
||||
if answer.lower() in ["n", "no"]:
|
||||
caller.msg("|rUpdate cancelled.")
|
||||
return
|
||||
n_updated = spawner.batch_update_objects_with_prototype(existing_objects, key)
|
||||
caller.msg("{} objects were updated.".format(n_updated))
|
||||
|
||||
# A direct creation of an object from a given prototype
|
||||
|
||||
prototype = _parse_prototype(
|
||||
|
|
@ -3066,7 +3103,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
if isinstance(prototype, basestring):
|
||||
# A prototype key we are looking to apply
|
||||
key = prototype
|
||||
prototypes = search_prototype(prototype)
|
||||
prototypes = spawner.search_prototype(prototype)
|
||||
nprots = len(prototypes)
|
||||
if not prototypes:
|
||||
caller.msg("No prototype named '%s'." % prototype)
|
||||
|
|
@ -3087,7 +3124,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
|
||||
# proceed to spawning
|
||||
try:
|
||||
for obj in spawn(prototype):
|
||||
for obj in spawner.spawn(prototype):
|
||||
self.caller.msg("Spawned %s." % obj.get_display_name(self.caller))
|
||||
except RuntimeError as err:
|
||||
caller.msg(err)
|
||||
|
|
|
|||
|
|
@ -270,6 +270,9 @@ class DbPrototype(DefaultScript):
|
|||
self.desc = "A prototype" # prototype_desc
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def save_db_prototype(caller, prototype, key=None, desc=None, tags=None, locks="", delete=False):
|
||||
"""
|
||||
Store a prototype persistently.
|
||||
|
|
@ -662,6 +665,28 @@ def _get_prototype(dic, prot, protparents):
|
|||
return prot
|
||||
|
||||
|
||||
def batch_update_objects_with_prototype(prototype, objects=None):
|
||||
"""
|
||||
Update existing objects with the latest version of the prototype.
|
||||
|
||||
Args:
|
||||
prototype (str or dict): Either the `prototype_key` to use or the
|
||||
prototype dict itself.
|
||||
objects (list): List of objects to update. If not given, query for these
|
||||
objects using the prototype's `prototype_key`.
|
||||
Returns:
|
||||
changed (int): The number of objects that had changes applied to them.
|
||||
"""
|
||||
prototype_key = prototype if isinstance(prototype, basestring) else prototype['prototype_key']
|
||||
prototype_obj = search_db_prototype(prototype_key, return_queryset=True)
|
||||
prototype_obj = prototype_obj[0] if prototype_obj else None
|
||||
new_prototype = prototype_obj.db.prototype
|
||||
|
||||
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def _batch_create_object(*objparams):
|
||||
"""
|
||||
This is a cut-down version of the create_object() function,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue