mirror of
https://github.com/evennia/evennia.git
synced 2026-03-26 17:56:32 +01:00
Refactor, include readonly prototypes
This commit is contained in:
parent
d7deed0b51
commit
3e7dae6a8c
1 changed files with 131 additions and 17 deletions
|
|
@ -6,13 +6,41 @@ available in a repository for buildiers to use. Each prototype is stored
|
|||
in a Script so that it can be tagged for quick sorting/finding and locked for limiting
|
||||
access.
|
||||
|
||||
This system also takes into consideration prototypes defined and stored in modules.
|
||||
Such prototypes are considered 'read-only' to the system and can only be modified
|
||||
in code. To replace a default prototype, add the same-name prototype in a
|
||||
custom module read later in the settings.PROTOTYPE_MODULES list. To remove a default
|
||||
prototype, override its name with an empty dict.
|
||||
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from evennia.scripts.scripts import DefaultScript
|
||||
from evennia.utils.create import create_script
|
||||
from evennia.utils.utils import make_iter
|
||||
from evennia.utils.utils import make_iter, all_from_module
|
||||
from evennia.utils.evtable import EvTable
|
||||
|
||||
# prepare the available prototypes defined in modules
|
||||
|
||||
_READONLY_PROTOTYPES = {}
|
||||
_READONLY_PROTOTYPE_MODULES = {}
|
||||
|
||||
for mod in settings.PROTOTYPE_MODULES:
|
||||
# to remove a default prototype, override it with an empty dict.
|
||||
# internally we store as (key, desc, locks, tags, prototype_dict)
|
||||
prots = [(key, prot) for key, prot in all_from_module(mod).items()
|
||||
if prot and isinstance(prot, dict)]
|
||||
_READONLY_PROTOTYPES.update(
|
||||
{key.lower():
|
||||
(key.lower(),
|
||||
prot['prototype_desc'] if 'prototype_desc' in prot else mod,
|
||||
prot['prototype_lock'] if 'prototype_lock' in prot else "use:all()",
|
||||
set(make_iter(
|
||||
prot['prototype_tags']) if 'prototype_tags' in prot else ["base-prototype"]),
|
||||
prot)
|
||||
for key, prot in prots})
|
||||
_READONLY_PROTOTYPE_MODULES.update({tup[0]: mod for tup in prots})
|
||||
|
||||
|
||||
class PersistentPrototype(DefaultScript):
|
||||
"""
|
||||
|
|
@ -46,17 +74,25 @@ def store_prototype(caller, key, prototype, desc="", tags=None, locks="", delete
|
|||
Raises:
|
||||
PermissionError: If edit lock was not passed by caller.
|
||||
|
||||
|
||||
"""
|
||||
key_orig = key
|
||||
key = key.lower()
|
||||
locks = locks if locks else "use:all();edit:id({}) or edit:perm(Admin)".format(caller.id)
|
||||
tags = [(tag, "persistent_prototype") for tag in make_iter(tags)]
|
||||
|
||||
if key in _READONLY_PROTOTYPES:
|
||||
mod = _READONLY_PROTOTYPE_MODULES.get(key, "N/A")
|
||||
raise PermissionError("{} is a read-only prototype "
|
||||
"(defined as code in {}).".format(key_orig, mod))
|
||||
|
||||
stored_prototype = PersistentPrototype.objects.filter(db_key=key)
|
||||
|
||||
if stored_prototype:
|
||||
stored_prototype = stored_prototype[0]
|
||||
if not stored_prototype.access(caller, 'edit'):
|
||||
PermissionError("{} does not have permission to edit prototype {}".format(caller, key))
|
||||
raise PermissionError("{} does not have permission to "
|
||||
"edit prototype {}".format(caller, key))
|
||||
|
||||
if delete:
|
||||
stored_prototype.delete()
|
||||
|
|
@ -77,9 +113,9 @@ def store_prototype(caller, key, prototype, desc="", tags=None, locks="", delete
|
|||
return stored_prototype
|
||||
|
||||
|
||||
def search_prototype(key=None, tags=None):
|
||||
def search_persistent_prototype(key=None, tags=None):
|
||||
"""
|
||||
Find prototypes based on key and/or tags.
|
||||
Find persistent (database-stored) prototypes based on key and/or tags.
|
||||
|
||||
Kwargs:
|
||||
key (str): An exact or partial key to query for.
|
||||
|
|
@ -87,13 +123,10 @@ def search_prototype(key=None, tags=None):
|
|||
will always be applied with the 'persistent_protototype'
|
||||
tag category.
|
||||
Return:
|
||||
matches (queryset): All found PersistentPrototypes. This will
|
||||
be all prototypes if no arguments are given.
|
||||
matches (queryset): All found PersistentPrototypes
|
||||
|
||||
Note:
|
||||
This will use the tags to make a subselection before attempting
|
||||
to match on the key. So if key/tags don't match up nothing will
|
||||
be found.
|
||||
This will not include read-only prototypes defined in modules.
|
||||
|
||||
"""
|
||||
if tags:
|
||||
|
|
@ -109,6 +142,68 @@ def search_prototype(key=None, tags=None):
|
|||
return matches
|
||||
|
||||
|
||||
def search_readonly_prototype(key=None, tags=None):
|
||||
"""
|
||||
Find read-only prototypes, defined in modules.
|
||||
|
||||
Kwargs:
|
||||
key (str): An exact or partial key to query for.
|
||||
tags (str or list): Tag key to query for.
|
||||
|
||||
Return:
|
||||
matches (list): List of prototype tuples that includes
|
||||
prototype metadata, on the form
|
||||
`(key, desc, lockstring, taglist, prototypedict)`
|
||||
|
||||
"""
|
||||
matches = []
|
||||
if tags:
|
||||
# use tags to limit selection
|
||||
tagset = set(tags)
|
||||
matches = {key: tup for key, tup in _READONLY_PROTOTYPES.items()
|
||||
if tagset.intersection(tup[3])}
|
||||
else:
|
||||
matches = _READONLY_PROTOTYPES
|
||||
|
||||
if key:
|
||||
if key in matches:
|
||||
# exact match
|
||||
return matches[key]
|
||||
else:
|
||||
# fuzzy matching
|
||||
return [tup for pkey, tup in matches.items() if key in pkey]
|
||||
return matches
|
||||
|
||||
|
||||
def search_prototype(key=None, tags=None):
|
||||
"""
|
||||
Find prototypes based on key and/or tags.
|
||||
|
||||
Kwargs:
|
||||
key (str): An exact or partial key to query for.
|
||||
tags (str or list): Tag key or keys to query for. These
|
||||
will always be applied with the 'persistent_protototype'
|
||||
tag category.
|
||||
Return:
|
||||
matches (list): All found prototype dicts.
|
||||
|
||||
Note:
|
||||
The available prototypes is a combination of those supplied in
|
||||
PROTOTYPE_MODULES and those stored from in-game. For the latter,
|
||||
this will use the tags to make a subselection before attempting
|
||||
to match on the key. So if key/tags don't match up nothing will
|
||||
be found.
|
||||
|
||||
"""
|
||||
matches = []
|
||||
if key and key in _READONLY_PROTOTYPES:
|
||||
matches.append(_READONLY_PROTOTYPES[key][3])
|
||||
else:
|
||||
matches.extend([prot.attributes.get("prototype")
|
||||
for prot in search_persistent_prototype(key, tags)])
|
||||
return matches
|
||||
|
||||
|
||||
def get_prototype_list(caller, key=None, tags=None, show_non_use=False, show_non_edit=True):
|
||||
"""
|
||||
Collate a list of found prototypes based on search criteria and access.
|
||||
|
|
@ -124,20 +219,39 @@ def get_prototype_list(caller, key=None, tags=None, show_non_use=False, show_non
|
|||
if no prototypes were found.
|
||||
|
||||
"""
|
||||
prototypes = search_prototype(key, tags)
|
||||
# handle read-only prototypes separately
|
||||
if key and key in _READONLY_PROTOTYPES:
|
||||
readonly_prototypes = _READONLY_PROTOTYPES[key]
|
||||
else:
|
||||
readonly_prototypes = _READONLY_PROTOTYPES.values()
|
||||
|
||||
# get use-permissions of readonly attributes (edit is always False)
|
||||
readonly_prototypes = [
|
||||
(tup[0],
|
||||
tup[1],
|
||||
("{}/N".format('Y'
|
||||
if caller.locks.check_lockstring(caller, tup[2], access_type='use') else 'N')),
|
||||
",".join(tup[3])) for tup in readonly_prototypes]
|
||||
|
||||
# next, handle db-stored prototypes
|
||||
prototypes = search_persistent_prototype(key, tags)
|
||||
|
||||
if not prototypes:
|
||||
return None
|
||||
|
||||
# gather access permissions as (key, desc, can_use, can_edit)
|
||||
# gather access permissions as (key, desc, tags, can_use, can_edit)
|
||||
prototypes = [(prototype.key, prototype.desc,
|
||||
prototype.access(caller, "use"), prototype.access(caller, "edit"))
|
||||
"{}/{}".format('Y' if prototype.access(caller, "use") else 'N',
|
||||
'Y' if prototype.access(caller, "edit") else 'N'),
|
||||
",".join(prototype.tags.get(category="persistent_prototype")))
|
||||
for prototype in prototypes]
|
||||
|
||||
prototypes = prototypes + readonly_prototypes
|
||||
|
||||
if not show_non_use:
|
||||
prototypes = [tup for tup in prototypes if tup[2]]
|
||||
prototypes = [tup for tup in sorted(prototypes, key=lambda o: o[0]) if tup[2]]
|
||||
if not show_non_edit:
|
||||
prototypes = [tup for tup in prototypes if tup[3]]
|
||||
prototypes = [tup for tup in sorted(prototypes, key=lambda o: o[0]) if tup[3]]
|
||||
|
||||
if not prototypes:
|
||||
return None
|
||||
|
|
@ -145,9 +259,9 @@ def get_prototype_list(caller, key=None, tags=None, show_non_use=False, show_non
|
|||
table = []
|
||||
for i in range(len(prototypes[0])):
|
||||
table.append([str(tup[i]) for tup in prototypes])
|
||||
table = EvTable("Key", "Desc", "Use", "Edit", table=table, crop=True, width=78)
|
||||
table = EvTable("Key", "Desc", "Use/Edit", "Tags", table=table, crop=True, width=78)
|
||||
table.reformat_column(0, width=28)
|
||||
table.reformat_column(1, width=40)
|
||||
table.reformat_column(2, width=5)
|
||||
table.reformat_column(3, width=5)
|
||||
table.reformat_column(2, width=11, align='r')
|
||||
table.reformat_column(3, width=20)
|
||||
return table
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue