commit
16b1aeffc2
31 changed files with 953 additions and 353 deletions
1
ev.py
1
ev.py
|
|
@ -132,6 +132,7 @@ from src.utils import logger
|
||||||
from src.utils import utils
|
from src.utils import utils
|
||||||
from src.utils import gametime
|
from src.utils import gametime
|
||||||
from src.utils import ansi
|
from src.utils import ansi
|
||||||
|
from src.utils.spawner import spawn
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
|
||||||
4
game/gamesrc/web/template_overrides/README.md
Normal file
4
game/gamesrc/web/template_overrides/README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
Place your own version of templates into this file to override the default ones.
|
||||||
|
For instance, if there's a template at: `src/web/templates/evennia_general/index.html`
|
||||||
|
and you want to replace it, create the file `game/gamesrc/web/template_overrides/evennia_general/index.html`
|
||||||
|
and it will be loaded instead.
|
||||||
56
game/gamesrc/world/examples/prototypes.py
Normal file
56
game/gamesrc/world/examples/prototypes.py
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
"""
|
||||||
|
Example prototypes read by the @spawn command but is also easily
|
||||||
|
available to use from code. Each prototype should be a dictionary. Use
|
||||||
|
the same name as the variable to refer to other prototypes.
|
||||||
|
|
||||||
|
Possible keywords are:
|
||||||
|
prototype - string pointing to parent prototype of this structure
|
||||||
|
key - string, the main object identifier
|
||||||
|
typeclass - string, if not set, will use settings.BASE_OBJECT_TYPECLASS
|
||||||
|
location - this should be a valid object or #dbref
|
||||||
|
home - valid object or #dbref
|
||||||
|
destination - only valid for exits (object or dbref)
|
||||||
|
|
||||||
|
permissions - string or list of permission strings
|
||||||
|
locks - a lock-string
|
||||||
|
aliases - string or list of strings
|
||||||
|
|
||||||
|
ndb_<name> - value of a nattribute (ndb_ is stripped)
|
||||||
|
any other keywords are interpreted as Attributes and their values.
|
||||||
|
|
||||||
|
See the @spawn command and src.utils.spawner for more info.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from random import randint
|
||||||
|
|
||||||
|
NOBODY = {}
|
||||||
|
|
||||||
|
GOBLIN = {
|
||||||
|
"key": "goblin grunt",
|
||||||
|
"health": lambda: randint(20,30),
|
||||||
|
"resists": ["cold", "poison"],
|
||||||
|
"attacks": ["fists"],
|
||||||
|
"weaknesses": ["fire", "light"]
|
||||||
|
}
|
||||||
|
|
||||||
|
GOBLIN_WIZARD = {
|
||||||
|
"prototype": "GOBLIN",
|
||||||
|
"key": "goblin wizard",
|
||||||
|
"spells": ["fire ball", "lighting bolt"]
|
||||||
|
}
|
||||||
|
|
||||||
|
GOBLIN_ARCHER = {
|
||||||
|
"prototype": "GOBLIN",
|
||||||
|
"key": "goblin archer",
|
||||||
|
"attacks": ["short bow"]
|
||||||
|
}
|
||||||
|
|
||||||
|
ARCHWIZARD = {
|
||||||
|
"attacks": ["archwizard staff"],
|
||||||
|
}
|
||||||
|
|
||||||
|
GOBLIN_ARCHWIZARD = {
|
||||||
|
"key": "goblin archwizard",
|
||||||
|
"prototype" : ("GOBLIN_WIZARD", "ARCHWIZARD")
|
||||||
|
}
|
||||||
|
|
@ -262,7 +262,6 @@ def at_multimatch_cmd(caller, matches):
|
||||||
|
|
||||||
id1 = ""
|
id1 = ""
|
||||||
id2 = ""
|
id2 = ""
|
||||||
print "cmd.obj:", cmd, cmd.obj
|
|
||||||
if (not (is_channel or is_exit) and
|
if (not (is_channel or is_exit) and
|
||||||
(hasattr(cmd, 'obj') and cmd.obj != caller) and
|
(hasattr(cmd, 'obj') and cmd.obj != caller) and
|
||||||
hasattr(cmd.obj, "key")):
|
hasattr(cmd.obj, "key")):
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
python_paths = [path] + ["%s.%s" % (prefix, path)
|
python_paths = [path] + ["%s.%s" % (prefix, path)
|
||||||
for prefix in _CMDSET_PATHS]
|
for prefix in _CMDSET_PATHS if not path.startswith(prefix)]
|
||||||
errstring = ""
|
errstring = ""
|
||||||
for python_path in python_paths:
|
for python_path in python_paths:
|
||||||
try:
|
try:
|
||||||
|
|
@ -123,16 +123,19 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False):
|
||||||
cmdsetclass = cmdsetclass(cmdsetobj)
|
cmdsetclass = cmdsetclass(cmdsetobj)
|
||||||
return cmdsetclass
|
return cmdsetclass
|
||||||
|
|
||||||
except ImportError:
|
except ImportError, e:
|
||||||
errstring += _("Error loading cmdset: Couldn't import module '%s'.")
|
errstring += _("Error loading cmdset: Couldn't import module '%s': %s.")
|
||||||
errstring = errstring % modulepath
|
errstring = errstring % (modulepath, e)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
errstring += _("Error in loading cmdset: No cmdset class '%(classname)s' in %(modulepath)s.")
|
errstring += _("Error in loading cmdset: No cmdset class '%(classname)s' in %(modulepath)s.")
|
||||||
errstring = errstring % {"classname": classname,
|
errstring = errstring % {"classname": classname,
|
||||||
"modulepath": modulepath}
|
"modulepath": modulepath}
|
||||||
|
except SyntaxError, e:
|
||||||
|
errstring += _("SyntaxError encountered when loading cmdset '%s': %s.")
|
||||||
|
errstring = errstring % (modulepath, e)
|
||||||
except Exception:
|
except Exception:
|
||||||
errstring += _("Compile/Run error when loading cmdset '%s'. Error was logged.")
|
errstring += _("Compile/Run error when loading cmdset '%s': %s.")
|
||||||
errstring = errstring % (python_path)
|
errstring = errstring % (python_path, e)
|
||||||
|
|
||||||
if errstring:
|
if errstring:
|
||||||
# returning an empty error cmdset
|
# returning an empty error cmdset
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ All commands in Evennia inherit from the 'Command' class in this module.
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from src.locks.lockhandler import LockHandler
|
from src.locks.lockhandler import LockHandler
|
||||||
from src.utils.utils import is_iter, fill, LazyLoadHandler
|
from src.utils.utils import is_iter, fill, lazy_property
|
||||||
|
|
||||||
|
|
||||||
def _init_command(mcs, **kwargs):
|
def _init_command(mcs, **kwargs):
|
||||||
|
|
@ -155,7 +155,10 @@ class Command(object):
|
||||||
overloading evential same-named class properties."""
|
overloading evential same-named class properties."""
|
||||||
if kwargs:
|
if kwargs:
|
||||||
_init_command(self, **kwargs)
|
_init_command(self, **kwargs)
|
||||||
self.lockhandler = LazyLoadHandler(self, "lockhandler", LockHandler)
|
|
||||||
|
@lazy_property
|
||||||
|
def lockhandler(self):
|
||||||
|
return LockHandler(self)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"Print the command"
|
"Print the command"
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,12 @@ Building and world design commands
|
||||||
"""
|
"""
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from src.objects.models import ObjectDB
|
from src.objects.models import ObjectDB
|
||||||
from src.utils import create, utils, search
|
|
||||||
from src.utils.ansi import raw
|
|
||||||
from src.locks.lockhandler import LockException
|
from src.locks.lockhandler import LockException
|
||||||
from src.commands.default.muxcommand import MuxCommand
|
from src.commands.default.muxcommand import MuxCommand
|
||||||
from src.commands.cmdhandler import get_and_merge_cmdsets
|
from src.commands.cmdhandler import get_and_merge_cmdsets
|
||||||
|
from src.utils import create, utils, search
|
||||||
|
from src.utils.spawner import spawn
|
||||||
|
from src.utils.ansi import raw
|
||||||
|
|
||||||
# limit symbol import for API
|
# limit symbol import for API
|
||||||
__all__ = ("ObjManipCommand", "CmdSetObjAlias", "CmdCopy",
|
__all__ = ("ObjManipCommand", "CmdSetObjAlias", "CmdCopy",
|
||||||
|
|
@ -19,7 +20,7 @@ __all__ = ("ObjManipCommand", "CmdSetObjAlias", "CmdCopy",
|
||||||
"CmdUnLink", "CmdSetHome", "CmdListCmdSets", "CmdName",
|
"CmdUnLink", "CmdSetHome", "CmdListCmdSets", "CmdName",
|
||||||
"CmdOpen", "CmdSetAttribute", "CmdTypeclass", "CmdWipe",
|
"CmdOpen", "CmdSetAttribute", "CmdTypeclass", "CmdWipe",
|
||||||
"CmdLock", "CmdExamine", "CmdFind", "CmdTeleport",
|
"CmdLock", "CmdExamine", "CmdFind", "CmdTeleport",
|
||||||
"CmdScript", "CmdTag")
|
"CmdScript", "CmdTag", "CmdSpawn")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# used by @set
|
# used by @set
|
||||||
|
|
@ -30,7 +31,7 @@ except ImportError:
|
||||||
|
|
||||||
# used by @find
|
# used by @find
|
||||||
CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
|
CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
|
||||||
|
_PROTOTYPE_PARENTS = None
|
||||||
|
|
||||||
class ObjManipCommand(MuxCommand):
|
class ObjManipCommand(MuxCommand):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1169,40 +1170,7 @@ class CmdOpen(ObjManipCommand):
|
||||||
back_exit_typeclass)
|
back_exit_typeclass)
|
||||||
|
|
||||||
|
|
||||||
class CmdSetAttribute(ObjManipCommand):
|
def _convert_from_string(cmd, strobj):
|
||||||
"""
|
|
||||||
set attribute on an object or player
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
@set <obj>/<attr> = <value>
|
|
||||||
@set <obj>/<attr> =
|
|
||||||
@set <obj>/<attr>
|
|
||||||
@set *<player>/attr = <value>
|
|
||||||
|
|
||||||
Sets attributes on objects. The second form clears
|
|
||||||
a previously set attribute while the last form
|
|
||||||
inspects the current value of the attribute
|
|
||||||
(if any).
|
|
||||||
|
|
||||||
The most common data to save with this command are strings and
|
|
||||||
numbers. You can however also set Python primities such as lists,
|
|
||||||
dictionaries and tuples on objects (this might be important for
|
|
||||||
the functionality of certain custom objects). This is indicated
|
|
||||||
by you starting your value with one of {c'{n, {c"{n, {c({n, {c[{n
|
|
||||||
or {c{ {n.
|
|
||||||
Note that you should leave a space after starting a dictionary ('{ ')
|
|
||||||
so as to not confuse the dictionary start with a colour code like \{g.
|
|
||||||
Remember that if you use Python primitives like this, you must
|
|
||||||
write proper Python syntax too - notably you must include quotes
|
|
||||||
around your strings or you will get an error.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
key = "@set"
|
|
||||||
locks = "cmd:perm(set) or perm(Builders)"
|
|
||||||
help_category = "Building"
|
|
||||||
|
|
||||||
def convert_from_string(self, strobj):
|
|
||||||
"""
|
"""
|
||||||
Converts a single object in *string form* to its equivalent python
|
Converts a single object in *string form* to its equivalent python
|
||||||
type.
|
type.
|
||||||
|
|
@ -1263,13 +1231,47 @@ class CmdSetAttribute(ObjManipCommand):
|
||||||
# treat as string
|
# treat as string
|
||||||
string = "{RNote: Value was converted to string. If you don't want this, "
|
string = "{RNote: Value was converted to string. If you don't want this, "
|
||||||
string += "use proper Python syntax, like enclosing strings in quotes.{n"
|
string += "use proper Python syntax, like enclosing strings in quotes.{n"
|
||||||
self.caller.msg(string)
|
cmd.caller.msg(string)
|
||||||
return utils.to_str(strobj)
|
return utils.to_str(strobj)
|
||||||
else:
|
else:
|
||||||
# fall back to old recursive solution (does not support
|
# fall back to old recursive solution (does not support
|
||||||
# nested lists/dicts)
|
# nested lists/dicts)
|
||||||
return rec_convert(strobj.strip())
|
return rec_convert(strobj.strip())
|
||||||
|
|
||||||
|
class CmdSetAttribute(ObjManipCommand):
|
||||||
|
"""
|
||||||
|
set attribute on an object or player
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
@set <obj>/<attr> = <value>
|
||||||
|
@set <obj>/<attr> =
|
||||||
|
@set <obj>/<attr>
|
||||||
|
@set *<player>/attr = <value>
|
||||||
|
|
||||||
|
Sets attributes on objects. The second form clears
|
||||||
|
a previously set attribute while the last form
|
||||||
|
inspects the current value of the attribute
|
||||||
|
(if any).
|
||||||
|
|
||||||
|
The most common data to save with this command are strings and
|
||||||
|
numbers. You can however also set Python primities such as lists,
|
||||||
|
dictionaries and tuples on objects (this might be important for
|
||||||
|
the functionality of certain custom objects). This is indicated
|
||||||
|
by you starting your value with one of {c'{n, {c"{n, {c({n, {c[{n
|
||||||
|
or {c{ {n.
|
||||||
|
Note that you should leave a space after starting a dictionary ('{ ')
|
||||||
|
so as to not confuse the dictionary start with a colour code like \{g.
|
||||||
|
Remember that if you use Python primitives like this, you must
|
||||||
|
write proper Python syntax too - notably you must include quotes
|
||||||
|
around your strings or you will get an error.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
key = "@set"
|
||||||
|
locks = "cmd:perm(set) or perm(Builders)"
|
||||||
|
help_category = "Building"
|
||||||
|
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implement the set attribute - a limited form of @py."
|
"Implement the set attribute - a limited form of @py."
|
||||||
|
|
||||||
|
|
@ -1295,7 +1297,7 @@ class CmdSetAttribute(ObjManipCommand):
|
||||||
if self.rhs is None:
|
if self.rhs is None:
|
||||||
# no = means we inspect the attribute(s)
|
# no = means we inspect the attribute(s)
|
||||||
if not attrs:
|
if not attrs:
|
||||||
attrs = [attr.key for attr in obj.get_all_attributes()]
|
attrs = [attr.key for attr in obj.attributes.all()]
|
||||||
for attr in attrs:
|
for attr in attrs:
|
||||||
if obj.attributes.has(attr):
|
if obj.attributes.has(attr):
|
||||||
string += "\nAttribute %s/%s = %s" % (obj.name, attr,
|
string += "\nAttribute %s/%s = %s" % (obj.name, attr,
|
||||||
|
|
@ -1303,7 +1305,7 @@ class CmdSetAttribute(ObjManipCommand):
|
||||||
else:
|
else:
|
||||||
string += "\n%s has no attribute '%s'." % (obj.name, attr)
|
string += "\n%s has no attribute '%s'." % (obj.name, attr)
|
||||||
# we view it without parsing markup.
|
# we view it without parsing markup.
|
||||||
self.caller.msg(string.strip(), data={"raw": True})
|
self.caller.msg(string.strip(), raw=True)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# deleting the attribute(s)
|
# deleting the attribute(s)
|
||||||
|
|
@ -1318,7 +1320,7 @@ class CmdSetAttribute(ObjManipCommand):
|
||||||
# setting attribute(s). Make sure to convert to real Python type before saving.
|
# setting attribute(s). Make sure to convert to real Python type before saving.
|
||||||
for attr in attrs:
|
for attr in attrs:
|
||||||
try:
|
try:
|
||||||
obj.attributes.add(attr, self.convert_from_string(value))
|
obj.attributes.add(attr, _convert_from_string(self, value))
|
||||||
string += "\nCreated attribute %s/%s = %s" % (obj.name, attr, value)
|
string += "\nCreated attribute %s/%s = %s" % (obj.name, attr, value)
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
# this means literal_eval tried to parse a faulty string
|
# this means literal_eval tried to parse a faulty string
|
||||||
|
|
@ -2242,3 +2244,100 @@ class CmdTag(MuxCommand):
|
||||||
else:
|
else:
|
||||||
string = "No tags attached to %s." % obj
|
string = "No tags attached to %s." % obj
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
#
|
||||||
|
# To use the prototypes with the @spawn function, copy
|
||||||
|
# game/gamesrc/world/examples/prototypes.py up one level
|
||||||
|
# to game/gamesrc/world. Then add to game/settings.py the
|
||||||
|
# line
|
||||||
|
# PROTOTYPE_MODULES = ["game.gamesrc.commands.prototypes"]
|
||||||
|
# Reload the server and the prototypes should be available.
|
||||||
|
#
|
||||||
|
|
||||||
|
class CmdSpawn(MuxCommand):
|
||||||
|
"""
|
||||||
|
spawn objects from prototype
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
@spawn
|
||||||
|
@spawn[/switch] prototype_name
|
||||||
|
@spawn[/switch] {prototype dictionary}
|
||||||
|
|
||||||
|
Switch:
|
||||||
|
noloc - allow location to be None if not specified explicitly. Otherwise,
|
||||||
|
location will default to caller's current location.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
@spawn GOBLIN
|
||||||
|
@spawn {"key":"goblin", "typeclass":"monster.Monster", "location":"#2"}
|
||||||
|
|
||||||
|
Dictionary keys:
|
||||||
|
{wprototype {n - name of parent prototype to use. Can be a list for
|
||||||
|
multiple inheritance (inherits left to right)
|
||||||
|
{wkey {n - string, the main object identifier
|
||||||
|
{wtypeclass {n - string, if not set, will use settings.BASE_OBJECT_TYPECLASS
|
||||||
|
{wlocation {n - this should be a valid object or #dbref
|
||||||
|
{whome {n - valid object or #dbref
|
||||||
|
{wdestination{n - only valid for exits (object or dbref)
|
||||||
|
{wpermissions{n - string or list of permission strings
|
||||||
|
{wlocks {n - a lock-string
|
||||||
|
{waliases {n - string or list of strings
|
||||||
|
{wndb_{n<name> - value of a nattribute (ndb_ is stripped)
|
||||||
|
any other keywords are interpreted as Attributes and their values.
|
||||||
|
|
||||||
|
The available prototypes are defined globally in modules set in
|
||||||
|
settings.PROTOTYPE_MODULES. If @spawn is used without arguments it
|
||||||
|
displays a list of available prototypes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
key = "@spawn"
|
||||||
|
aliases = ["spawn"]
|
||||||
|
locks = "cmd:perm(spawn) or perm(Builders)"
|
||||||
|
help_category = "Building"
|
||||||
|
|
||||||
|
def func(self):
|
||||||
|
"Implements the spawner"
|
||||||
|
|
||||||
|
def _show_prototypes(prototypes):
|
||||||
|
"Helper to show a list of available prototypes"
|
||||||
|
string = "\nAvailable prototypes:\n %s"
|
||||||
|
string = string % utils.fill(", ".join(sorted(prototypes.keys())))
|
||||||
|
return string
|
||||||
|
|
||||||
|
prototypes = spawn(return_prototypes=True)
|
||||||
|
if not self.args:
|
||||||
|
string = "Usage: @spawn {key:value, key, value, ... }"
|
||||||
|
self.caller.msg(string + _show_prototypes(prototypes))
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
# make use of _convert_from_string from the SetAttribute command
|
||||||
|
prototype = _convert_from_string(self, self.args)
|
||||||
|
except SyntaxError:
|
||||||
|
# this means literal_eval tried to parse a faulty string
|
||||||
|
string = "{RCritical Python syntax error in argument. "
|
||||||
|
string += "Only primitive Python structures are allowed. "
|
||||||
|
string += "\nYou also need to use correct Python syntax. "
|
||||||
|
string += "Remember especially to put quotes around all "
|
||||||
|
string += "strings inside lists and dicts.{n"
|
||||||
|
self.caller.msg(string)
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(prototype, basestring):
|
||||||
|
# A prototype key
|
||||||
|
keystr = prototype
|
||||||
|
prototype = prototypes.get(prototype, None)
|
||||||
|
if not prototype:
|
||||||
|
string = "No prototype named '%s'." % keystr
|
||||||
|
self.caller.msg(string + _show_prototypes(prototypes))
|
||||||
|
return
|
||||||
|
elif not isinstance(prototype, dict):
|
||||||
|
self.caller.msg("The prototype must be a prototype key or a Python dictionary.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not "noloc" in self.switches and not "location" in prototype:
|
||||||
|
prototype["location"] = self.caller.location
|
||||||
|
|
||||||
|
for obj in spawn(prototype):
|
||||||
|
self.caller.msg("Spawned %s." % obj.key)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ class CharacterCmdSet(CmdSet):
|
||||||
self.add(building.CmdScript())
|
self.add(building.CmdScript())
|
||||||
self.add(building.CmdSetHome())
|
self.add(building.CmdSetHome())
|
||||||
self.add(building.CmdTag())
|
self.add(building.CmdTag())
|
||||||
|
self.add(building.CmdSpawn())
|
||||||
|
|
||||||
# Batchprocessor commands
|
# Batchprocessor commands
|
||||||
self.add(batchprocess.CmdBatchCommands())
|
self.add(batchprocess.CmdBatchCommands())
|
||||||
|
|
|
||||||
|
|
@ -378,9 +378,11 @@ class CmdWho(MuxPlayerCommand):
|
||||||
|
|
||||||
nplayers = (SESSIONS.player_count())
|
nplayers = (SESSIONS.player_count())
|
||||||
if show_session_data:
|
if show_session_data:
|
||||||
|
# privileged info
|
||||||
table = prettytable.PrettyTable(["{wPlayer Name",
|
table = prettytable.PrettyTable(["{wPlayer Name",
|
||||||
"{wOn for",
|
"{wOn for",
|
||||||
"{wIdle",
|
"{wIdle",
|
||||||
|
"{wPuppeting",
|
||||||
"{wRoom",
|
"{wRoom",
|
||||||
"{wCmds",
|
"{wCmds",
|
||||||
"{wProtocol",
|
"{wProtocol",
|
||||||
|
|
@ -389,25 +391,27 @@ class CmdWho(MuxPlayerCommand):
|
||||||
if not session.logged_in: continue
|
if not session.logged_in: continue
|
||||||
delta_cmd = time.time() - session.cmd_last_visible
|
delta_cmd = time.time() - session.cmd_last_visible
|
||||||
delta_conn = time.time() - session.conn_time
|
delta_conn = time.time() - session.conn_time
|
||||||
plr_pobject = session.get_puppet()
|
player = session.get_player()
|
||||||
plr_pobject = plr_pobject or session.get_player()
|
puppet = session.get_puppet()
|
||||||
table.add_row([utils.crop(plr_pobject.name, width=25),
|
location = puppet.location.key if puppet else "None"
|
||||||
|
table.add_row([utils.crop(player.name, width=25),
|
||||||
utils.time_format(delta_conn, 0),
|
utils.time_format(delta_conn, 0),
|
||||||
utils.time_format(delta_cmd, 1),
|
utils.time_format(delta_cmd, 1),
|
||||||
hasattr(plr_pobject, "location") and plr_pobject.location and plr_pobject.location.key or "None",
|
utils.crop(puppet.key if puppet else "None", width=25),
|
||||||
|
utils.crop(location, width=25),
|
||||||
session.cmd_total,
|
session.cmd_total,
|
||||||
session.protocol_key,
|
session.protocol_key,
|
||||||
isinstance(session.address, tuple) and session.address[0] or session.address])
|
isinstance(session.address, tuple) and session.address[0] or session.address])
|
||||||
else:
|
else:
|
||||||
|
# unprivileged
|
||||||
table = prettytable.PrettyTable(["{wPlayer name", "{wOn for", "{wIdle"])
|
table = prettytable.PrettyTable(["{wPlayer name", "{wOn for", "{wIdle"])
|
||||||
for session in session_list:
|
for session in session_list:
|
||||||
if not session.logged_in:
|
if not session.logged_in:
|
||||||
continue
|
continue
|
||||||
delta_cmd = time.time() - session.cmd_last_visible
|
delta_cmd = time.time() - session.cmd_last_visible
|
||||||
delta_conn = time.time() - session.conn_time
|
delta_conn = time.time() - session.conn_time
|
||||||
plr_pobject = session.get_puppet()
|
player = session.get_player()
|
||||||
plr_pobject = plr_pobject or session.get_player()
|
table.add_row([utils.crop(player.key, width=25),
|
||||||
table.add_row([utils.crop(plr_pobject.name, width=25),
|
|
||||||
utils.time_format(delta_conn, 0),
|
utils.time_format(delta_conn, 0),
|
||||||
utils.time_format(delta_cmd, 1)])
|
utils.time_format(delta_cmd, 1)])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,9 @@ from src.players.player import Player
|
||||||
from src.utils import create, ansi
|
from src.utils import create, ansi
|
||||||
from src.server.sessionhandler import SESSIONS
|
from src.server.sessionhandler import SESSIONS
|
||||||
|
|
||||||
from django.db.models.signals import pre_save
|
from django.db.models.signals import post_save
|
||||||
from src.server.caches import field_pre_save
|
from src.server.caches import field_post_save
|
||||||
pre_save.connect(field_pre_save, dispatch_uid="fieldcache")
|
post_save.connect(field_post_save, dispatch_uid="fieldcache")
|
||||||
|
|
||||||
# set up signal here since we are not starting the server
|
# set up signal here since we are not starting the server
|
||||||
|
|
||||||
|
|
@ -78,12 +78,12 @@ class CommandTest(TestCase):
|
||||||
CID = 0 # we must set a different CID in every test to avoid unique-name collisions creating the objects
|
CID = 0 # we must set a different CID in every test to avoid unique-name collisions creating the objects
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"sets up testing environment"
|
"sets up testing environment"
|
||||||
settings.DEFAULT_HOME = "#2"
|
|
||||||
#print "creating player %i: %s" % (self.CID, self.__class__.__name__)
|
#print "creating player %i: %s" % (self.CID, self.__class__.__name__)
|
||||||
self.player = create.create_player("TestPlayer%i" % self.CID, "test@test.com", "testpassword", typeclass=TestPlayerClass)
|
self.player = create.create_player("TestPlayer%i" % self.CID, "test@test.com", "testpassword", typeclass=TestPlayerClass)
|
||||||
self.player2 = create.create_player("TestPlayer%ib" % self.CID, "test@test.com", "testpassword", typeclass=TestPlayerClass)
|
self.player2 = create.create_player("TestPlayer%ib" % self.CID, "test@test.com", "testpassword", typeclass=TestPlayerClass)
|
||||||
self.room1 = create.create_object("src.objects.objects.Room", key="Room%i"%self.CID, nohome=True)
|
self.room1 = create.create_object("src.objects.objects.Room", key="Room%i"%self.CID, nohome=True)
|
||||||
self.room1.db.desc = "room_desc"
|
self.room1.db.desc = "room_desc"
|
||||||
|
settings.DEFAULT_HOME = "#%i" % self.room1.id # we must have a default home
|
||||||
self.room2 = create.create_object("src.objects.objects.Room", key="Room%ib" % self.CID)
|
self.room2 = create.create_object("src.objects.objects.Room", key="Room%ib" % self.CID)
|
||||||
self.obj1 = create.create_object(TestObjectClass, key="Obj%i" % self.CID, location=self.room1, home=self.room1)
|
self.obj1 = create.create_object(TestObjectClass, key="Obj%i" % self.CID, location=self.room1, home=self.room1)
|
||||||
self.obj2 = create.create_object(TestObjectClass, key="Obj%ib" % self.CID, location=self.room1, home=self.room1)
|
self.obj2 = create.create_object(TestObjectClass, key="Obj%ib" % self.CID, location=self.room1, home=self.room1)
|
||||||
|
|
@ -272,7 +272,7 @@ class TestComms(CommandTest):
|
||||||
self.call(comms.CmdCdesc(), "testchan = Test Channel", "Description of channel 'testchan' set to 'Test Channel'.")
|
self.call(comms.CmdCdesc(), "testchan = Test Channel", "Description of channel 'testchan' set to 'Test Channel'.")
|
||||||
self.call(comms.CmdCemit(), "testchan = Test Message", "[testchan] Test Message|Sent to channel testchan: Test Message")
|
self.call(comms.CmdCemit(), "testchan = Test Message", "[testchan] Test Message|Sent to channel testchan: Test Message")
|
||||||
self.call(comms.CmdCWho(), "testchan", "Channel subscriptions\ntestchan:\n TestPlayer7")
|
self.call(comms.CmdCWho(), "testchan", "Channel subscriptions\ntestchan:\n TestPlayer7")
|
||||||
self.call(comms.CmdPage(), "TestPlayer7b = Test", "You paged TestPlayer7b with: 'Test'.")
|
self.call(comms.CmdPage(), "TestPlayer7b = Test", "TestPlayer7b is offline. They will see your message if they list their pages later.|You paged TestPlayer7b with: 'Test'.")
|
||||||
self.call(comms.CmdCBoot(), "", "Usage: @cboot[/quiet] <channel> = <player> [:reason]") # noone else connected to boot
|
self.call(comms.CmdCBoot(), "", "Usage: @cboot[/quiet] <channel> = <player> [:reason]") # noone else connected to boot
|
||||||
self.call(comms.CmdCdestroy(), "testchan" ,"[testchan] TestPlayer7: testchan is being destroyed. Make sure to change your aliases.|Channel 'testchan' was destroyed.")
|
self.call(comms.CmdCdestroy(), "testchan" ,"[testchan] TestPlayer7: testchan is being destroyed. Make sure to change your aliases.|Channel 'testchan' was destroyed.")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ from src.utils.idmapper.models import SharedMemoryModel
|
||||||
from src.comms import managers
|
from src.comms import managers
|
||||||
from src.comms.managers import identify_object
|
from src.comms.managers import identify_object
|
||||||
from src.locks.lockhandler import LockHandler
|
from src.locks.lockhandler import LockHandler
|
||||||
from src.utils.utils import crop, make_iter, LazyLoadHandler
|
from src.utils.utils import crop, make_iter, lazy_property
|
||||||
|
|
||||||
__all__ = ("Msg", "TempMsg", "ChannelDB")
|
__all__ = ("Msg", "TempMsg", "ChannelDB")
|
||||||
|
|
||||||
|
|
@ -103,7 +103,6 @@ class Msg(SharedMemoryModel):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
SharedMemoryModel.__init__(self, *args, **kwargs)
|
SharedMemoryModel.__init__(self, *args, **kwargs)
|
||||||
#_SA(self, "locks", LazyLoadHandler(self, "locks", LockHandler))
|
|
||||||
self.extra_senders = []
|
self.extra_senders = []
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -299,10 +298,13 @@ class TempMsg(object):
|
||||||
self.header = header
|
self.header = header
|
||||||
self.message = message
|
self.message = message
|
||||||
self.lock_storage = lockstring
|
self.lock_storage = lockstring
|
||||||
self.locks = LazyLoadHandler(self, "locks", LockHandler)
|
|
||||||
self.hide_from = hide_from and make_iter(hide_from) or []
|
self.hide_from = hide_from and make_iter(hide_from) or []
|
||||||
self.date_sent = datetime.now()
|
self.date_sent = datetime.now()
|
||||||
|
|
||||||
|
@lazy_property
|
||||||
|
def locks(self):
|
||||||
|
return LockHandler(self)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"This handles what is shown when e.g. printing the message"
|
"This handles what is shown when e.g. printing the message"
|
||||||
senders = ",".join(obj.key for obj in self.senders)
|
senders = ",".join(obj.key for obj in self.senders)
|
||||||
|
|
@ -359,12 +361,6 @@ class ChannelDB(TypedObject):
|
||||||
_typeclass_paths = settings.CHANNEL_TYPECLASS_PATHS
|
_typeclass_paths = settings.CHANNEL_TYPECLASS_PATHS
|
||||||
_default_typeclass_path = settings.BASE_CHANNEL_TYPECLASS or "src.comms.comms.Channel"
|
_default_typeclass_path = settings.BASE_CHANNEL_TYPECLASS or "src.comms.comms.Channel"
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
TypedObject.__init__(self, *args, **kwargs)
|
|
||||||
_SA(self, "tags", LazyLoadHandler(self, "tags", TagHandler))
|
|
||||||
_SA(self, "attributes", LazyLoadHandler(self, "attributes", AttributeHandler))
|
|
||||||
_SA(self, "aliases", LazyLoadHandler(self, "aliases", AliasHandler))
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"Define Django meta options"
|
"Define Django meta options"
|
||||||
verbose_name = "Channel"
|
verbose_name = "Channel"
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ from src.utils.idmapper.models import SharedMemoryModel
|
||||||
from src.help.manager import HelpEntryManager
|
from src.help.manager import HelpEntryManager
|
||||||
from src.typeclasses.models import Tag, TagHandler
|
from src.typeclasses.models import Tag, TagHandler
|
||||||
from src.locks.lockhandler import LockHandler
|
from src.locks.lockhandler import LockHandler
|
||||||
from src.utils.utils import LazyLoadHandler
|
from src.utils.utils import lazy_property
|
||||||
__all__ = ("HelpEntry",)
|
__all__ = ("HelpEntry",)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -66,10 +66,16 @@ class HelpEntry(SharedMemoryModel):
|
||||||
objects = HelpEntryManager()
|
objects = HelpEntryManager()
|
||||||
_is_deleted = False
|
_is_deleted = False
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
# lazy-loaded handlers
|
||||||
SharedMemoryModel.__init__(self, *args, **kwargs)
|
|
||||||
self.locks = LazyLoadHandler(self, "locks", LockHandler)
|
@lazy_property
|
||||||
self.tags = LazyLoadHandler(self, "tags", TagHandler)
|
def locks(self):
|
||||||
|
return LockHandler(self)
|
||||||
|
|
||||||
|
@lazy_property
|
||||||
|
def tags(self):
|
||||||
|
return TagHandler(self)
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"Define Django meta options"
|
"Define Django meta options"
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,15 @@ from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
from src.typeclasses.models import (TypedObject, TagHandler, NickHandler,
|
from src.typeclasses.models import TypedObject, NickHandler
|
||||||
AliasHandler, AttributeHandler)
|
|
||||||
from src.objects.manager import ObjectManager
|
from src.objects.manager import ObjectManager
|
||||||
from src.players.models import PlayerDB
|
from src.players.models import PlayerDB
|
||||||
from src.commands.cmdsethandler import CmdSetHandler
|
from src.commands.cmdsethandler import CmdSetHandler
|
||||||
from src.commands import cmdhandler
|
from src.commands import cmdhandler
|
||||||
from src.scripts.scripthandler import ScriptHandler
|
from src.scripts.scripthandler import ScriptHandler
|
||||||
from src.utils import logger
|
from src.utils import logger
|
||||||
from src.utils.utils import (make_iter, to_str, to_unicode,
|
from src.utils.utils import (make_iter, to_str, to_unicode, lazy_property,
|
||||||
variable_from_module, dbref, LazyLoadHandler)
|
variable_from_module, dbref)
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
|
@ -130,19 +129,18 @@ class ObjectDB(TypedObject):
|
||||||
_typeclass_paths = settings.OBJECT_TYPECLASS_PATHS
|
_typeclass_paths = settings.OBJECT_TYPECLASS_PATHS
|
||||||
_default_typeclass_path = settings.BASE_OBJECT_TYPECLASS or "src.objects.objects.Object"
|
_default_typeclass_path = settings.BASE_OBJECT_TYPECLASS or "src.objects.objects.Object"
|
||||||
|
|
||||||
# Add the object-specific handlers
|
# lazy-load handlers
|
||||||
def __init__(self, *args, **kwargs):
|
@lazy_property
|
||||||
"Parent must be initialized first."
|
def cmdset(self):
|
||||||
TypedObject.__init__(self, *args, **kwargs)
|
return CmdSetHandler(self, True)
|
||||||
# handlers
|
|
||||||
_SA(self, "cmdset", LazyLoadHandler(self, "cmdset", CmdSetHandler, True))
|
@lazy_property
|
||||||
_SA(self, "scripts", LazyLoadHandler(self, "scripts", ScriptHandler))
|
def scripts(self):
|
||||||
_SA(self, "nicks", LazyLoadHandler(self, "nicks", NickHandler))
|
return ScriptHandler(self)
|
||||||
#_SA(self, "attributes", LazyLoadHandler(self, "attributes", AttributeHandler))
|
|
||||||
#_SA(self, "tags", LazyLoadHandler(self, "tags", TagHandler))
|
@lazy_property
|
||||||
#_SA(self, "aliases", LazyLoadHandler(self, "aliases", AliasHandler))
|
def nicks(self):
|
||||||
# make sure to sync the contents cache when initializing
|
return NickHandler(self)
|
||||||
#_GA(self, "contents_update")()
|
|
||||||
|
|
||||||
def _at_db_player_postsave(self):
|
def _at_db_player_postsave(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -197,23 +197,31 @@ class PlayerDBAdmin(BaseUserAdmin):
|
||||||
'description': "<i>These account details are shared by the admin "
|
'description': "<i>These account details are shared by the admin "
|
||||||
"system and the game.</i>"},),)
|
"system and the game.</i>"},),)
|
||||||
|
|
||||||
# TODO! Remove User reference!
|
def save_model(self, request, obj, form, change):
|
||||||
def save_formset(self, request, form, formset, change):
|
obj.save()
|
||||||
"""
|
|
||||||
Run all hooks on the player object
|
|
||||||
"""
|
|
||||||
super(PlayerDBAdmin, self).save_formset(request, form, formset, change)
|
|
||||||
userobj = form.instance
|
|
||||||
userobj.name = userobj.username
|
|
||||||
if not change:
|
if not change:
|
||||||
# uname, passwd, email = str(request.POST.get(u"username")), \
|
#calling hooks for new player
|
||||||
# str(request.POST.get(u"password1")), \
|
ply = obj.typeclass
|
||||||
# str(request.POST.get(u"email"))
|
ply.basetype_setup()
|
||||||
typeclass = str(request.POST.get(
|
ply.at_player_creation()
|
||||||
u"playerdb_set-0-db_typeclass_path"))
|
|
||||||
create.create_player("", "", "",
|
## TODO! Remove User reference!
|
||||||
user=userobj,
|
#def save_formset(self, request, form, formset, change):
|
||||||
typeclass=typeclass,
|
# """
|
||||||
player_dbobj=userobj)
|
# Run all hooks on the player object
|
||||||
|
# """
|
||||||
|
# super(PlayerDBAdmin, self).save_formset(request, form, formset, change)
|
||||||
|
# userobj = form.instance
|
||||||
|
# userobj.name = userobj.username
|
||||||
|
# if not change:
|
||||||
|
# # uname, passwd, email = str(request.POST.get(u"username")), \
|
||||||
|
# # str(request.POST.get(u"password1")), \
|
||||||
|
# # str(request.POST.get(u"email"))
|
||||||
|
# typeclass = str(request.POST.get(
|
||||||
|
# u"playerdb_set-0-db_typeclass_path"))
|
||||||
|
# create.create_player("", "", "",
|
||||||
|
# user=userobj,
|
||||||
|
# typeclass=typeclass,
|
||||||
|
# player_dbobj=userobj)
|
||||||
|
|
||||||
admin.site.register(PlayerDB, PlayerDBAdmin)
|
admin.site.register(PlayerDB, PlayerDBAdmin)
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,12 @@ from django.utils.encoding import smart_str
|
||||||
|
|
||||||
from src.players import manager
|
from src.players import manager
|
||||||
from src.scripts.models import ScriptDB
|
from src.scripts.models import ScriptDB
|
||||||
from src.typeclasses.models import (TypedObject, TagHandler, NickHandler,
|
from src.typeclasses.models import (TypedObject, NickHandler)
|
||||||
AliasHandler, AttributeHandler)
|
|
||||||
from src.scripts.scripthandler import ScriptHandler
|
from src.scripts.scripthandler import ScriptHandler
|
||||||
from src.commands.cmdsethandler import CmdSetHandler
|
from src.commands.cmdsethandler import CmdSetHandler
|
||||||
from src.commands import cmdhandler
|
from src.commands import cmdhandler
|
||||||
from src.utils import utils, logger
|
from src.utils import utils, logger
|
||||||
from src.utils.utils import to_str, make_iter, LazyLoadHandler
|
from src.utils.utils import to_str, make_iter, lazy_property
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
|
@ -111,15 +110,19 @@ class PlayerDB(TypedObject, AbstractUser):
|
||||||
app_label = 'players'
|
app_label = 'players'
|
||||||
verbose_name = 'Player'
|
verbose_name = 'Player'
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
# lazy-loading of handlers
|
||||||
"Parent must be initiated first"
|
@lazy_property
|
||||||
TypedObject.__init__(self, *args, **kwargs)
|
def cmdset(self):
|
||||||
# handlers
|
return CmdSetHandler(self, True)
|
||||||
_SA(self, "cmdset", LazyLoadHandler(self, "cmdset", CmdSetHandler, True))
|
|
||||||
_SA(self, "scripts", LazyLoadHandler(self, "scripts", ScriptHandler))
|
@lazy_property
|
||||||
_SA(self, "nicks", LazyLoadHandler(self, "nicks", NickHandler))
|
def scripts(self):
|
||||||
#_SA(self, "tags", LazyLoadHandler(self, "tags", TagHandler))
|
return ScriptHandler(self)
|
||||||
#_SA(self, "aliases", LazyLoadHandler(self, "aliases", AliasHandler))
|
|
||||||
|
@lazy_property
|
||||||
|
def nicks(self):
|
||||||
|
return NickHandler(self)
|
||||||
|
|
||||||
|
|
||||||
# alias to the objs property
|
# alias to the objs property
|
||||||
def __characters_get(self):
|
def __characters_get(self):
|
||||||
|
|
|
||||||
|
|
@ -383,6 +383,16 @@ class Player(TypeClass):
|
||||||
reason = reason and "(%s)" % reason or ""
|
reason = reason and "(%s)" % reason or ""
|
||||||
self._send_to_connect_channel("{R%s disconnected %s{n" % (self.key, reason))
|
self._send_to_connect_channel("{R%s disconnected %s{n" % (self.key, reason))
|
||||||
|
|
||||||
|
def at_post_disconnect(self):
|
||||||
|
"""
|
||||||
|
This is called after disconnection is complete. No messages
|
||||||
|
can be relayed to the player from here. After this call, the
|
||||||
|
player should not be accessed any more, making this a good
|
||||||
|
spot for deleting it (in the case of a guest player account,
|
||||||
|
for example).
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
def at_message_receive(self, message, from_obj=None):
|
def at_message_receive(self, message, from_obj=None):
|
||||||
"""
|
"""
|
||||||
Called when any text is emitted to this
|
Called when any text is emitted to this
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ Common examples of uses of Scripts:
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from src.typeclasses.models import TypedObject, TagHandler, AttributeHandler
|
from src.typeclasses.models import TypedObject
|
||||||
from src.scripts.manager import ScriptManager
|
from src.scripts.manager import ScriptManager
|
||||||
from src.utils.utils import dbref, to_str, LazyLoadHandler
|
from src.utils.utils import dbref, to_str
|
||||||
|
|
||||||
__all__ = ("ScriptDB",)
|
__all__ = ("ScriptDB",)
|
||||||
_GA = object.__getattribute__
|
_GA = object.__getattribute__
|
||||||
|
|
@ -108,13 +108,6 @@ class ScriptDB(TypedObject):
|
||||||
"Define Django meta options"
|
"Define Django meta options"
|
||||||
verbose_name = "Script"
|
verbose_name = "Script"
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(ScriptDB, self).__init__(*args, **kwargs)
|
|
||||||
_SA(self, "attributes", LazyLoadHandler(self, "attributes", AttributeHandler))
|
|
||||||
_SA(self, "tags", LazyLoadHandler(self, "tags", TagHandler))
|
|
||||||
#_SA(self, "aliases", AliasHandler(self))
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# ScriptDB class properties
|
# ScriptDB class properties
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,9 @@ from django.conf import settings
|
||||||
#from src.scripts.models import ScriptDB
|
#from src.scripts.models import ScriptDB
|
||||||
from src.comms.models import ChannelDB
|
from src.comms.models import ChannelDB
|
||||||
from src.utils import logger, utils
|
from src.utils import logger, utils
|
||||||
from src.utils.utils import make_iter, to_unicode, LazyLoadHandler
|
from src.utils.utils import make_iter, to_unicode
|
||||||
from src.commands import cmdhandler, cmdsethandler
|
from src.commands.cmdhandler import cmdhandler
|
||||||
|
from src.commands.cmdsethandler import CmdSetHandler
|
||||||
from src.server.session import Session
|
from src.server.session import Session
|
||||||
|
|
||||||
IDLE_COMMAND = settings.IDLE_COMMAND
|
IDLE_COMMAND = settings.IDLE_COMMAND
|
||||||
|
|
@ -49,7 +50,7 @@ class ServerSession(Session):
|
||||||
self.puppet = None
|
self.puppet = None
|
||||||
self.player = None
|
self.player = None
|
||||||
self.cmdset_storage_string = ""
|
self.cmdset_storage_string = ""
|
||||||
self.cmdset = LazyLoadHandler(self, "cmdset", cmdsethandler.CmdSetHandler, True)
|
self.cmdset = CmdSetHandler(self, True)
|
||||||
|
|
||||||
def __cmdset_storage_get(self):
|
def __cmdset_storage_get(self):
|
||||||
return [path.strip() for path in self.cmdset_storage_string.split(',')]
|
return [path.strip() for path in self.cmdset_storage_string.split(',')]
|
||||||
|
|
@ -103,7 +104,7 @@ class ServerSession(Session):
|
||||||
self.player.save()
|
self.player.save()
|
||||||
|
|
||||||
# add the session-level cmdset
|
# add the session-level cmdset
|
||||||
self.cmdset = LazyLoadHandler(self, "cmdset", cmdsethandler.CmdSetHandler, True)
|
self.cmdset = CmdSetHandler(self, True)
|
||||||
|
|
||||||
def at_disconnect(self):
|
def at_disconnect(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -122,6 +123,8 @@ class ServerSession(Session):
|
||||||
if not self.sessionhandler.sessions_from_player(player):
|
if not self.sessionhandler.sessions_from_player(player):
|
||||||
# no more sessions connected to this player
|
# no more sessions connected to this player
|
||||||
player.is_connected = False
|
player.is_connected = False
|
||||||
|
# this may be used to e.g. delete player after disconnection etc
|
||||||
|
_GA(player.typeclass, "at_post_disconnect")()
|
||||||
|
|
||||||
def get_player(self):
|
def get_player(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -198,7 +201,7 @@ class ServerSession(Session):
|
||||||
else:
|
else:
|
||||||
text = self.player.nicks.nickreplace(text,
|
text = self.player.nicks.nickreplace(text,
|
||||||
categories=("inputline", "channels"), include_player=False)
|
categories=("inputline", "channels"), include_player=False)
|
||||||
cmdhandler.cmdhandler(self, text, callertype="session", sessid=self.sessid)
|
cmdhandler(self, text, callertype="session", sessid=self.sessid)
|
||||||
self.update_session_counters()
|
self.update_session_counters()
|
||||||
if "oob" in kwargs:
|
if "oob" in kwargs:
|
||||||
# handle oob instructions
|
# handle oob instructions
|
||||||
|
|
|
||||||
|
|
@ -514,7 +514,7 @@ STATICFILES_IGNORE_PATTERNS = ('README.md',)
|
||||||
ACTIVE_TEMPLATE = 'prosimii'
|
ACTIVE_TEMPLATE = 'prosimii'
|
||||||
# We setup the location of the website template as well as the admin site.
|
# We setup the location of the website template as well as the admin site.
|
||||||
TEMPLATE_DIRS = (
|
TEMPLATE_DIRS = (
|
||||||
os.path.join(GAME_DIR, "gamesrc", "web", "templates"),
|
os.path.join(GAME_DIR, "gamesrc", "web", "template_overrides"),
|
||||||
os.path.join(SRC_DIR, "web", "templates", ACTIVE_TEMPLATE),
|
os.path.join(SRC_DIR, "web", "templates", ACTIVE_TEMPLATE),
|
||||||
os.path.join(SRC_DIR, "web", "templates"),)
|
os.path.join(SRC_DIR, "web", "templates"),)
|
||||||
# List of callables that know how to import templates from various sources.
|
# List of callables that know how to import templates from various sources.
|
||||||
|
|
|
||||||
|
|
@ -70,24 +70,24 @@ class AttributeManager(models.Manager):
|
||||||
@_attr_pickled
|
@_attr_pickled
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
return super(AttributeManager, self).get(*args, **kwargs)
|
return super(AttributeManager, self).get(*args, **kwargs)
|
||||||
@_attr_pickled
|
|
||||||
|
|
||||||
|
@_attr_pickled
|
||||||
def filter(self,*args, **kwargs):
|
def filter(self,*args, **kwargs):
|
||||||
return super(AttributeManager, self).filter(*args, **kwargs)
|
return super(AttributeManager, self).filter(*args, **kwargs)
|
||||||
@_attr_pickled
|
|
||||||
|
|
||||||
|
@_attr_pickled
|
||||||
def exclude(self,*args, **kwargs):
|
def exclude(self,*args, **kwargs):
|
||||||
return super(AttributeManager, self).exclude(*args, **kwargs)
|
return super(AttributeManager, self).exclude(*args, **kwargs)
|
||||||
@_attr_pickled
|
|
||||||
|
|
||||||
|
@_attr_pickled
|
||||||
def values(self,*args, **kwargs):
|
def values(self,*args, **kwargs):
|
||||||
return super(AttributeManager, self).values(*args, **kwargs)
|
return super(AttributeManager, self).values(*args, **kwargs)
|
||||||
@_attr_pickled
|
|
||||||
|
|
||||||
|
@_attr_pickled
|
||||||
def values_list(self,*args, **kwargs):
|
def values_list(self,*args, **kwargs):
|
||||||
return super(AttributeManager, self).values_list(*args, **kwargs)
|
return super(AttributeManager, self).values_list(*args, **kwargs)
|
||||||
@_attr_pickled
|
|
||||||
|
|
||||||
|
@_attr_pickled
|
||||||
def exists(self,*args, **kwargs):
|
def exists(self,*args, **kwargs):
|
||||||
return super(AttributeManager, self).exists(*args, **kwargs)
|
return super(AttributeManager, self).exists(*args, **kwargs)
|
||||||
|
|
||||||
|
|
@ -217,6 +217,56 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
|
||||||
Common ObjectManager for all dbobjects.
|
Common ObjectManager for all dbobjects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Attribute manager methods
|
||||||
|
|
||||||
|
# Tag manager methods
|
||||||
|
|
||||||
|
def get_tag(self, key=None, category=None, obj=None, tagtype=None):
|
||||||
|
"""
|
||||||
|
Return Tag objects by key, by category, by object or
|
||||||
|
with a combination of those criteria.
|
||||||
|
|
||||||
|
tagtype - one of None (normal tags), "alias" or "permission"
|
||||||
|
"""
|
||||||
|
query = [("tag__db_tagtype", tagtype)]
|
||||||
|
if obj:
|
||||||
|
query.append(("%s__id" % self.model.__name__.lower(), obj.id))
|
||||||
|
if key:
|
||||||
|
query.append(("tag__db_key", key))
|
||||||
|
if category:
|
||||||
|
query.append(("tag__db_category", category))
|
||||||
|
return self.model.db_tags.through.objects.filter(**dict(query))
|
||||||
|
|
||||||
|
def get_permission(self, key=None, category=None, obj=None):
|
||||||
|
return self.get_tag(key=key, category=category, obj=obj, tagtype="permission")
|
||||||
|
|
||||||
|
def get_alias(self, key=None, category=None, obj=None):
|
||||||
|
return self.get_tag(key=key, category=category, obj=obj, tagtype="alias")
|
||||||
|
|
||||||
|
@returns_typeclass
|
||||||
|
def get_by_tag(self, key=None, category=None, tagtype=None):
|
||||||
|
"""
|
||||||
|
Return objects having tags with a given key or category or
|
||||||
|
combination of the two.
|
||||||
|
|
||||||
|
tagtype = None, alias or permission
|
||||||
|
"""
|
||||||
|
query = [("db_tags__db_tagtype", tagtype)]
|
||||||
|
if key:
|
||||||
|
query.append(("db_tags__db_key", key))
|
||||||
|
if category:
|
||||||
|
query.append(("db_tags__db_category", category))
|
||||||
|
return self.filter(**dict(query))
|
||||||
|
|
||||||
|
def get_by_permission(self, key=None, category=None):
|
||||||
|
return self.get_by_tag(key=key, category=category, tagtype="permission")
|
||||||
|
|
||||||
|
def get_by_alias(self, key=None, category=None):
|
||||||
|
return self.get_by_tag(key=key, category=category, tagtype="alias")
|
||||||
|
|
||||||
|
|
||||||
|
# object-manager methods
|
||||||
|
|
||||||
def dbref(self, dbref, reqhash=True):
|
def dbref(self, dbref, reqhash=True):
|
||||||
"""
|
"""
|
||||||
Valid forms of dbref (database reference number)
|
Valid forms of dbref (database reference number)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from south.utils import datetime_utils as datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
# Removing unique constraint on 'Tag', fields ['db_key', 'db_category']
|
||||||
|
db.delete_unique(u'typeclasses_tag', ['db_key', 'db_category'])
|
||||||
|
|
||||||
|
# Adding unique constraint on 'Tag', fields ['db_key', 'db_category', 'db_tagtype']
|
||||||
|
db.create_unique(u'typeclasses_tag', ['db_key', 'db_category', 'db_tagtype'])
|
||||||
|
|
||||||
|
# Removing index on 'Tag', fields ['db_key', 'db_category']
|
||||||
|
db.delete_index(u'typeclasses_tag', ['db_key', 'db_category'])
|
||||||
|
|
||||||
|
# Adding index on 'Tag', fields ['db_key', 'db_category', 'db_tagtype']
|
||||||
|
db.create_index(u'typeclasses_tag', ['db_key', 'db_category', 'db_tagtype'])
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
# Removing index on 'Tag', fields ['db_key', 'db_category', 'db_tagtype']
|
||||||
|
db.delete_index(u'typeclasses_tag', ['db_key', 'db_category', 'db_tagtype'])
|
||||||
|
|
||||||
|
# Adding index on 'Tag', fields ['db_key', 'db_category']
|
||||||
|
db.create_index(u'typeclasses_tag', ['db_key', 'db_category'])
|
||||||
|
|
||||||
|
# Removing unique constraint on 'Tag', fields ['db_key', 'db_category', 'db_tagtype']
|
||||||
|
db.delete_unique(u'typeclasses_tag', ['db_key', 'db_category', 'db_tagtype'])
|
||||||
|
|
||||||
|
# Adding unique constraint on 'Tag', fields ['db_key', 'db_category']
|
||||||
|
db.create_unique(u'typeclasses_tag', ['db_key', 'db_category'])
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
u'typeclasses.attribute': {
|
||||||
|
'Meta': {'object_name': 'Attribute'},
|
||||||
|
'db_attrtype': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '16', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'db_category': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||||
|
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||||
|
'db_model': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '32', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'db_strvalue': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'db_value': ('src.utils.picklefield.PickledObjectField', [], {'null': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||||
|
},
|
||||||
|
u'typeclasses.tag': {
|
||||||
|
'Meta': {'unique_together': "(('db_key', 'db_category', 'db_tagtype'),)", 'object_name': 'Tag', 'index_together': "(('db_key', 'db_category', 'db_tagtype'),)"},
|
||||||
|
'db_category': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'db_index': 'True'}),
|
||||||
|
'db_data': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}),
|
||||||
|
'db_model': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'db_index': 'True'}),
|
||||||
|
'db_tagtype': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True', 'db_index': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['typeclasses']
|
||||||
|
|
@ -34,11 +34,9 @@ import weakref
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import Q
|
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_str
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
|
|
||||||
from src.utils.idmapper.models import SharedMemoryModel, WeakSharedMemoryModel
|
from src.utils.idmapper.models import SharedMemoryModel
|
||||||
from src.server.caches import get_prop_cache, set_prop_cache
|
from src.server.caches import get_prop_cache, set_prop_cache
|
||||||
#from src.server.caches import set_attr_cache
|
#from src.server.caches import set_attr_cache
|
||||||
|
|
||||||
|
|
@ -48,7 +46,7 @@ from src.typeclasses import managers
|
||||||
from src.locks.lockhandler import LockHandler
|
from src.locks.lockhandler import LockHandler
|
||||||
from src.utils import logger
|
from src.utils import logger
|
||||||
from src.utils.utils import (
|
from src.utils.utils import (
|
||||||
make_iter, is_iter, to_str, inherits_from, LazyLoadHandler)
|
make_iter, is_iter, to_str, inherits_from, lazy_property)
|
||||||
from src.utils.dbserialize import to_pickle, from_pickle
|
from src.utils.dbserialize import to_pickle, from_pickle
|
||||||
from src.utils.picklefield import PickledObjectField
|
from src.utils.picklefield import PickledObjectField
|
||||||
|
|
||||||
|
|
@ -59,7 +57,6 @@ TICKER_HANDLER = None
|
||||||
_PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
_PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
||||||
_TYPECLASS_AGGRESSIVE_CACHE = settings.TYPECLASS_AGGRESSIVE_CACHE
|
_TYPECLASS_AGGRESSIVE_CACHE = settings.TYPECLASS_AGGRESSIVE_CACHE
|
||||||
|
|
||||||
_CTYPEGET = ContentType.objects.get
|
|
||||||
_GA = object.__getattribute__
|
_GA = object.__getattribute__
|
||||||
_SA = object.__setattr__
|
_SA = object.__setattr__
|
||||||
_DA = object.__delattr__
|
_DA = object.__delattr__
|
||||||
|
|
@ -132,12 +129,9 @@ class Attribute(SharedMemoryModel):
|
||||||
# Database manager
|
# Database manager
|
||||||
objects = managers.AttributeManager()
|
objects = managers.AttributeManager()
|
||||||
|
|
||||||
# Lock handler self.locks
|
@lazy_property
|
||||||
def __init__(self, *args, **kwargs):
|
def locks(self):
|
||||||
"Initializes the parent first -important!"
|
return LockHandler(self)
|
||||||
#SharedMemoryModel.__init__(self, *args, **kwargs)
|
|
||||||
super(Attribute, self).__init__(*args, **kwargs)
|
|
||||||
self.locks = LazyLoadHandler(self, "locks", LockHandler)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"Define Django meta options"
|
"Define Django meta options"
|
||||||
|
|
@ -237,19 +231,18 @@ class AttributeHandler(object):
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
"Initialize handler"
|
"Initialize handler"
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
self._model = "%s.%s" % ContentType.objects.get_for_model(obj).natural_key()
|
self._objid = obj.id
|
||||||
|
self._model = to_str(obj.__class__.__name__.lower())
|
||||||
self._cache = None
|
self._cache = None
|
||||||
|
|
||||||
def _recache(self):
|
def _recache(self):
|
||||||
if not self._attrtype:
|
"Cache all attributes of this object"
|
||||||
attrtype = Q(db_attrtype=None) | Q(db_attrtype='')
|
query = {"%s__id" % self._model : self._objid,
|
||||||
else:
|
"attribute__db_attrtype" : self._attrtype}
|
||||||
attrtype = Q(db_attrtype=self._attrtype)
|
attrs = [conn.attribute for conn in getattr(self.obj, self._m2m_fieldname).through.objects.filter(**query)]
|
||||||
self._cache = dict(("%s-%s" % (to_str(attr.db_key).lower(),
|
self._cache = dict(("%s-%s" % (to_str(attr.db_key).lower(),
|
||||||
attr.db_category.lower() if attr.db_category else None), attr)
|
attr.db_category.lower() if conn.attribute.db_category else None),
|
||||||
for attr in getattr(self.obj, self._m2m_fieldname).filter(
|
attr) for attr in attrs)
|
||||||
db_model=self._model).filter(attrtype))
|
|
||||||
#set_attr_cache(self.obj, self._cache) # currently only for testing
|
|
||||||
|
|
||||||
def has(self, key, category=None):
|
def has(self, key, category=None):
|
||||||
"""
|
"""
|
||||||
|
|
@ -321,6 +314,7 @@ class AttributeHandler(object):
|
||||||
return ret if len(key) > 1 else default
|
return ret if len(key) > 1 else default
|
||||||
return ret[0] if len(ret)==1 else ret
|
return ret[0] if len(ret)==1 else ret
|
||||||
|
|
||||||
|
|
||||||
def add(self, key, value, category=None, lockstring="",
|
def add(self, key, value, category=None, lockstring="",
|
||||||
strattr=False, accessing_obj=None, default_access=True):
|
strattr=False, accessing_obj=None, default_access=True):
|
||||||
"""
|
"""
|
||||||
|
|
@ -341,28 +335,87 @@ class AttributeHandler(object):
|
||||||
self._recache()
|
self._recache()
|
||||||
if not key:
|
if not key:
|
||||||
return
|
return
|
||||||
key = key.strip().lower()
|
|
||||||
category = category.strip().lower() if category is not None else None
|
category = category.strip().lower() if category is not None else None
|
||||||
cachekey = "%s-%s" % (key, category)
|
keystr = key.strip().lower()
|
||||||
|
cachekey = "%s-%s" % (keystr, category)
|
||||||
attr_obj = self._cache.get(cachekey)
|
attr_obj = self._cache.get(cachekey)
|
||||||
if not attr_obj:
|
|
||||||
# no old attr available; create new.
|
if attr_obj:
|
||||||
attr_obj = Attribute(db_key=key, db_category=category,
|
# update an existing attribute object
|
||||||
db_model=self._model, db_attrtype=self._attrtype)
|
|
||||||
attr_obj.save() # important
|
|
||||||
getattr(self.obj, self._m2m_fieldname).add(attr_obj)
|
|
||||||
self._cache[cachekey] = attr_obj
|
|
||||||
if lockstring:
|
|
||||||
attr_obj.locks.add(lockstring)
|
|
||||||
# we shouldn't need to fear stale objects, the field signalling
|
|
||||||
# should catch all cases
|
|
||||||
if strattr:
|
if strattr:
|
||||||
# store as a simple string
|
# store as a simple string (will not notify OOB handlers)
|
||||||
attr_obj.db_strvalue = value
|
attr_obj.db_strvalue = value
|
||||||
attr_obj.save(update_fields=["db_strvalue"])
|
attr_obj.save(update_fields=["db_strvalue"])
|
||||||
else:
|
else:
|
||||||
# pickle arbitrary data
|
# store normally (this will also notify OOB handlers)
|
||||||
attr_obj.value = value
|
attr_obj.value = value
|
||||||
|
else:
|
||||||
|
# create a new Attribute (no OOB handlers can be notified)
|
||||||
|
kwargs = {"db_key" : keystr, "db_category" : category,
|
||||||
|
"db_model" : self._model, "db_attrtype" : self._attrtype,
|
||||||
|
"db_value" : None if strattr else to_pickle(value),
|
||||||
|
"db_strvalue" : value if strattr else None}
|
||||||
|
new_attr = Attribute(**kwargs)
|
||||||
|
new_attr.save()
|
||||||
|
getattr(self.obj, self._m2m_fieldname).add(new_attr)
|
||||||
|
self._cache[cachekey] = new_attr
|
||||||
|
|
||||||
|
|
||||||
|
def batch_add(self, key, value, category=None, lockstring="",
|
||||||
|
strattr=False, accessing_obj=None, default_access=True):
|
||||||
|
"""
|
||||||
|
Batch-version of add(). This is more efficient than
|
||||||
|
repeat-calling add.
|
||||||
|
|
||||||
|
key and value must be sequences of the same length, each
|
||||||
|
representing a key-value pair.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if accessing_obj and not self.obj.access(accessing_obj,
|
||||||
|
self._attrcreate, default=default_access):
|
||||||
|
# check create access
|
||||||
|
return
|
||||||
|
if self._cache is None:
|
||||||
|
self._recache()
|
||||||
|
if not key:
|
||||||
|
return
|
||||||
|
|
||||||
|
keys, values= make_iter(key), make_iter(value)
|
||||||
|
|
||||||
|
if len(keys) != len(values):
|
||||||
|
raise RuntimeError("AttributeHandler.add(): key and value of different length: %s vs %s" % key, value)
|
||||||
|
category = category.strip().lower() if category is not None else None
|
||||||
|
new_attrobjs = []
|
||||||
|
for ikey, keystr in enumerate(keys):
|
||||||
|
keystr = keystr.strip().lower()
|
||||||
|
new_value = values[ikey]
|
||||||
|
cachekey = "%s-%s" % (keystr, category)
|
||||||
|
attr_obj = self._cache.get(cachekey)
|
||||||
|
|
||||||
|
if attr_obj:
|
||||||
|
# update an existing attribute object
|
||||||
|
if strattr:
|
||||||
|
# store as a simple string (will not notify OOB handlers)
|
||||||
|
attr_obj.db_strvalue = new_value
|
||||||
|
attr_obj.save(update_fields=["db_strvalue"])
|
||||||
|
else:
|
||||||
|
# store normally (this will also notify OOB handlers)
|
||||||
|
attr_obj.value = new_value
|
||||||
|
else:
|
||||||
|
# create a new Attribute (no OOB handlers can be notified)
|
||||||
|
kwargs = {"db_key" : keystr, "db_category" : category,
|
||||||
|
"db_attrtype" : self._attrtype,
|
||||||
|
"db_value" : None if strattr else to_pickle(new_value),
|
||||||
|
"db_strvalue" : value if strattr else None}
|
||||||
|
new_attr = Attribute(**kwargs)
|
||||||
|
new_attr.save()
|
||||||
|
new_attrobjs.append(new_attr)
|
||||||
|
if new_attrobjs:
|
||||||
|
# Add new objects to m2m field all at once
|
||||||
|
getattr(self.obj, self._m2m_fieldname).add(*new_attrobjs)
|
||||||
|
self._recache()
|
||||||
|
|
||||||
|
|
||||||
def remove(self, key, raise_exception=False, category=None,
|
def remove(self, key, raise_exception=False, category=None,
|
||||||
accessing_obj=None, default_access=True):
|
accessing_obj=None, default_access=True):
|
||||||
|
|
@ -416,6 +469,7 @@ class AttributeHandler(object):
|
||||||
else:
|
else:
|
||||||
return self._cache.values()
|
return self._cache.values()
|
||||||
|
|
||||||
|
|
||||||
class NickHandler(AttributeHandler):
|
class NickHandler(AttributeHandler):
|
||||||
"""
|
"""
|
||||||
Handles the addition and removal of Nicks
|
Handles the addition and removal of Nicks
|
||||||
|
|
@ -539,8 +593,8 @@ class Tag(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
"Define Django meta options"
|
"Define Django meta options"
|
||||||
verbose_name = "Tag"
|
verbose_name = "Tag"
|
||||||
unique_together = (('db_key', 'db_category'),)
|
unique_together = (('db_key', 'db_category', 'db_tagtype'),)
|
||||||
index_together = (('db_key', 'db_category'),)
|
index_together = (('db_key', 'db_category', 'db_tagtype'),)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return u"%s" % self.db_key
|
return u"%s" % self.db_key
|
||||||
|
|
@ -567,22 +621,23 @@ class TagHandler(object):
|
||||||
and with a tagtype given by self.handlertype
|
and with a tagtype given by self.handlertype
|
||||||
"""
|
"""
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
self._model = "%s.%s" % ContentType.objects.get_for_model(obj).natural_key()
|
self._objid = obj.id
|
||||||
|
self._model = obj.__class__.__name__.lower()
|
||||||
self._cache = None
|
self._cache = None
|
||||||
|
|
||||||
def _recache(self):
|
def _recache(self):
|
||||||
"Update cache from database field"
|
"Cache all tags of this object"
|
||||||
if not self._tagtype:
|
query = {"%s__id" % self._model : self._objid,
|
||||||
tagtype = Q(db_tagtype='') | Q(db_tagtype__isnull=True)
|
"tag__db_tagtype" : self._tagtype}
|
||||||
else:
|
tagobjs = [conn.tag for conn in getattr(self.obj, self._m2m_fieldname).through.objects.filter(**query)]
|
||||||
tagtype = Q(db_tagtype=self._tagtype)
|
self._cache = dict(("%s-%s" % (to_str(tagobj.db_key).lower(),
|
||||||
self._cache = dict(("%s-%s" % (tag.db_key, tag.db_category), tag)
|
tagobj.db_category.lower() if tagobj.db_category else None),
|
||||||
for tag in getattr(
|
tagobj) for tagobj in tagobjs)
|
||||||
self.obj, self._m2m_fieldname).filter(
|
|
||||||
db_model=self._model).filter(tagtype))
|
|
||||||
|
|
||||||
def add(self, tag, category=None, data=None):
|
def add(self, tag=None, category=None, data=None):
|
||||||
"Add a new tag to the handler. Tag is a string or a list of strings."
|
"Add a new tag to the handler. Tag is a string or a list of strings."
|
||||||
|
if not tag:
|
||||||
|
return
|
||||||
for tagstr in make_iter(tag):
|
for tagstr in make_iter(tag):
|
||||||
if not tagstr:
|
if not tagstr:
|
||||||
continue
|
continue
|
||||||
|
|
@ -593,7 +648,7 @@ class TagHandler(object):
|
||||||
# will overload data on an existing tag since that is not
|
# will overload data on an existing tag since that is not
|
||||||
# considered part of making the tag unique)
|
# considered part of making the tag unique)
|
||||||
tagobj = Tag.objects.create_tag(key=tagstr, category=category, data=data,
|
tagobj = Tag.objects.create_tag(key=tagstr, category=category, data=data,
|
||||||
model=self._model, tagtype=self._tagtype)
|
tagtype=self._tagtype)
|
||||||
getattr(self.obj, self._m2m_fieldname).add(tagobj)
|
getattr(self.obj, self._m2m_fieldname).add(tagobj)
|
||||||
if self._cache is None:
|
if self._cache is None:
|
||||||
self._recache()
|
self._recache()
|
||||||
|
|
@ -646,11 +701,10 @@ class TagHandler(object):
|
||||||
self._recache()
|
self._recache()
|
||||||
if category:
|
if category:
|
||||||
category = category.strip().lower() if category is not None else None
|
category = category.strip().lower() if category is not None else None
|
||||||
matches = getattr(self.obj, self._m2m_fieldname).filter(db_category=category,
|
matches = [tag for tag in self._cache.values() if tag.db_category == category]
|
||||||
db_tagtype=self._tagtype,
|
|
||||||
db_model=self._model)
|
|
||||||
else:
|
else:
|
||||||
matches = self._cache.values()
|
matches = self._cache.values()
|
||||||
|
|
||||||
if matches:
|
if matches:
|
||||||
if return_key_and_category:
|
if return_key_and_category:
|
||||||
# return tuple (key, category)
|
# return tuple (key, category)
|
||||||
|
|
@ -741,15 +795,33 @@ class TypedObject(SharedMemoryModel):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"We must initialize the parent first - important!"
|
"We must initialize the parent first - important!"
|
||||||
super(TypedObject, self).__init__(*args, **kwargs)
|
super(TypedObject, self).__init__(*args, **kwargs)
|
||||||
#SharedMemoryModel.__init__(self, *args, **kwargs)
|
|
||||||
_SA(self, "dbobj", self) # this allows for self-reference
|
_SA(self, "dbobj", self) # this allows for self-reference
|
||||||
_SA(self, "locks", LazyLoadHandler(self, "locks", LockHandler))
|
|
||||||
_SA(self, "tags", LazyLoadHandler(self, "tags", TagHandler))
|
# initialize all handlers in a lazy fashion
|
||||||
_SA(self, "aliases", LazyLoadHandler(self, "aliases", AliasHandler))
|
@lazy_property
|
||||||
_SA(self, "permissions", LazyLoadHandler(self, "permissions", PermissionHandler))
|
def attributes(self):
|
||||||
_SA(self, "attributes", LazyLoadHandler(self, "attributes", AttributeHandler))
|
return AttributeHandler(self)
|
||||||
_SA(self, "nattributes", NAttributeHandler(self))
|
|
||||||
#_SA(self, "nattributes", LazyLoadHandler(self, "nattributes", NAttributeHandler))
|
@lazy_property
|
||||||
|
def locks(self):
|
||||||
|
return LockHandler(self)
|
||||||
|
|
||||||
|
@lazy_property
|
||||||
|
def tags(self):
|
||||||
|
return TagHandler(self)
|
||||||
|
|
||||||
|
@lazy_property
|
||||||
|
def aliases(self):
|
||||||
|
return AliasHandler(self)
|
||||||
|
|
||||||
|
@lazy_property
|
||||||
|
def permissions(self):
|
||||||
|
return PermissionHandler(self)
|
||||||
|
|
||||||
|
@lazy_property
|
||||||
|
def nattributes(self):
|
||||||
|
return NAttributeHandler(self)
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
|
|
@ -1216,13 +1288,10 @@ class TypedObject(SharedMemoryModel):
|
||||||
if not TICKER_HANDLER:
|
if not TICKER_HANDLER:
|
||||||
from src.scripts.tickerhandler import TICKER_HANDLER
|
from src.scripts.tickerhandler import TICKER_HANDLER
|
||||||
TICKER_HANDLER.remove(self) # removes objects' all ticker subscriptions
|
TICKER_HANDLER.remove(self) # removes objects' all ticker subscriptions
|
||||||
if not isinstance(_GA(self, "permissions"), LazyLoadHandler):
|
|
||||||
_GA(self, "permissions").clear()
|
_GA(self, "permissions").clear()
|
||||||
if not isinstance(_GA(self, "attributes"), LazyLoadHandler):
|
|
||||||
_GA(self, "attributes").clear()
|
_GA(self, "attributes").clear()
|
||||||
if not isinstance(_GA(self, "aliases"), LazyLoadHandler):
|
|
||||||
_GA(self, "aliases").clear()
|
_GA(self, "aliases").clear()
|
||||||
if hasattr(self, "nicks") and not isinstance(_GA(self, "nicks"), LazyLoadHandler):
|
if hasattr(self, "nicks"):
|
||||||
_GA(self, "nicks").clear()
|
_GA(self, "nicks").clear()
|
||||||
_SA(self, "_cached_typeclass", None)
|
_SA(self, "_cached_typeclass", None)
|
||||||
_GA(self, "flush_from_cache")()
|
_GA(self, "flush_from_cache")()
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,12 @@ def handle_dbref(inp, objclass, raise_errors=True):
|
||||||
objects.
|
objects.
|
||||||
"""
|
"""
|
||||||
if not (isinstance(inp, basestring) and inp.startswith("#")):
|
if not (isinstance(inp, basestring) and inp.startswith("#")):
|
||||||
|
try:
|
||||||
|
return inp.dbobj
|
||||||
|
except AttributeError:
|
||||||
return inp
|
return inp
|
||||||
|
|
||||||
|
# a string, analyze it
|
||||||
inp = inp.lstrip('#')
|
inp = inp.lstrip('#')
|
||||||
try:
|
try:
|
||||||
if int(inp) < 0:
|
if int(inp) < 0:
|
||||||
|
|
@ -67,7 +72,7 @@ def handle_dbref(inp, objclass, raise_errors=True):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# if we get to this point, inp is an integer dbref
|
# if we get to this point, inp is an integer dbref; get the matching object
|
||||||
try:
|
try:
|
||||||
return objclass.objects.get(id=inp)
|
return objclass.objects.get(id=inp)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -98,7 +103,7 @@ def create_object(typeclass=None, key=None, location=None,
|
||||||
containing the error message. If set, this method will return
|
containing the error message. If set, this method will return
|
||||||
None upon errors.
|
None upon errors.
|
||||||
nohome - this allows the creation of objects without a default home location;
|
nohome - this allows the creation of objects without a default home location;
|
||||||
this only used when creating default location itself or during unittests
|
this only used when creating the default location itself or during unittests
|
||||||
"""
|
"""
|
||||||
global _Object, _ObjectDB
|
global _Object, _ObjectDB
|
||||||
if not _Object:
|
if not _Object:
|
||||||
|
|
@ -116,28 +121,30 @@ def create_object(typeclass=None, key=None, location=None,
|
||||||
elif isinstance(typeclass, _Object) or utils.inherits_from(typeclass, _Object):
|
elif isinstance(typeclass, _Object) or utils.inherits_from(typeclass, _Object):
|
||||||
# this is already an object typeclass, extract its path
|
# this is already an object typeclass, extract its path
|
||||||
typeclass = typeclass.path
|
typeclass = typeclass.path
|
||||||
|
|
||||||
# handle eventual #dbref input
|
|
||||||
location = handle_dbref(location, _ObjectDB)
|
|
||||||
home = handle_dbref(home, _ObjectDB)
|
|
||||||
destination = handle_dbref(destination, _ObjectDB)
|
|
||||||
report_to = handle_dbref(report_to, _ObjectDB)
|
|
||||||
|
|
||||||
# create new database object
|
|
||||||
new_db_object = _ObjectDB()
|
|
||||||
|
|
||||||
# assign the typeclass
|
|
||||||
typeclass = utils.to_unicode(typeclass)
|
typeclass = utils.to_unicode(typeclass)
|
||||||
new_db_object.typeclass_path = typeclass
|
|
||||||
|
|
||||||
# the name/key is often set later in the typeclass. This
|
# Setup input for the create command
|
||||||
# is set here as a failsafe.
|
|
||||||
if key:
|
location = handle_dbref(location, _ObjectDB)
|
||||||
new_db_object.key = key
|
destination = handle_dbref(destination, _ObjectDB)
|
||||||
else:
|
home = handle_dbref(home, _ObjectDB)
|
||||||
|
if not home:
|
||||||
|
try:
|
||||||
|
home = handle_dbref(settings.DEFAULT_HOME, _ObjectDB) if not nohome else None
|
||||||
|
except _ObjectDB.DoesNotExist:
|
||||||
|
raise _ObjectDB.DoesNotExist("settings.DEFAULT_HOME (= '%s') does not exist, or the setting is malformed." %
|
||||||
|
settings.DEFAULT_HOME)
|
||||||
|
|
||||||
|
# create new database object all in one go
|
||||||
|
new_db_object = _ObjectDB(db_key=key, db_location=location,
|
||||||
|
db_destination=destination, db_home=home,
|
||||||
|
db_typeclass_path=typeclass)
|
||||||
|
|
||||||
|
if not key:
|
||||||
|
# the object should always have a key, so if not set we give a default
|
||||||
new_db_object.key = "#%i" % new_db_object.dbid
|
new_db_object.key = "#%i" % new_db_object.dbid
|
||||||
|
|
||||||
# this will either load the typeclass or the default one
|
# this will either load the typeclass or the default one (will also save object)
|
||||||
new_object = new_db_object.typeclass
|
new_object = new_db_object.typeclass
|
||||||
|
|
||||||
if not _GA(new_object, "is_typeclass")(typeclass, exact=True):
|
if not _GA(new_object, "is_typeclass")(typeclass, exact=True):
|
||||||
|
|
@ -145,6 +152,7 @@ def create_object(typeclass=None, key=None, location=None,
|
||||||
# gave us a default
|
# gave us a default
|
||||||
SharedMemoryModel.delete(new_db_object)
|
SharedMemoryModel.delete(new_db_object)
|
||||||
if report_to:
|
if report_to:
|
||||||
|
report_to = handle_dbref(report_to, _ObjectDB)
|
||||||
_GA(report_to, "msg")("Error creating %s (%s).\n%s" % (new_db_object.key, typeclass,
|
_GA(report_to, "msg")("Error creating %s (%s).\n%s" % (new_db_object.key, typeclass,
|
||||||
_GA(new_db_object, "typeclass_last_errmsg")))
|
_GA(new_db_object, "typeclass_last_errmsg")))
|
||||||
return None
|
return None
|
||||||
|
|
@ -154,15 +162,26 @@ def create_object(typeclass=None, key=None, location=None,
|
||||||
# from now on we can use the typeclass object
|
# from now on we can use the typeclass object
|
||||||
# as if it was the database object.
|
# as if it was the database object.
|
||||||
|
|
||||||
new_object.destination = destination
|
# call the hook methods. This is where all at_creation
|
||||||
|
|
||||||
# call the hook method. This is where all at_creation
|
|
||||||
# customization happens as the typeclass stores custom
|
# customization happens as the typeclass stores custom
|
||||||
# things on its database object.
|
# things on its database object.
|
||||||
|
|
||||||
|
# note - this may override input keys, locations etc!
|
||||||
new_object.basetype_setup() # setup the basics of Exits, Characters etc.
|
new_object.basetype_setup() # setup the basics of Exits, Characters etc.
|
||||||
new_object.at_object_creation()
|
new_object.at_object_creation()
|
||||||
|
|
||||||
# custom-given perms/locks overwrite hooks
|
# we want the input to override that set in the hooks, so
|
||||||
|
# we re-apply those if needed
|
||||||
|
if new_object.key != key:
|
||||||
|
new_object.key = key
|
||||||
|
if new_object.location != location:
|
||||||
|
new_object.location = location
|
||||||
|
if new_object.home != home:
|
||||||
|
new_object.home = home
|
||||||
|
if new_object.destination != destination:
|
||||||
|
new_object.destination = destination
|
||||||
|
|
||||||
|
# custom-given perms/locks do overwrite hooks
|
||||||
if permissions:
|
if permissions:
|
||||||
new_object.permissions.add(permissions)
|
new_object.permissions.add(permissions)
|
||||||
if locks:
|
if locks:
|
||||||
|
|
@ -170,28 +189,14 @@ def create_object(typeclass=None, key=None, location=None,
|
||||||
if aliases:
|
if aliases:
|
||||||
new_object.aliases.add(aliases)
|
new_object.aliases.add(aliases)
|
||||||
|
|
||||||
if home:
|
# trigger relevant move_to hooks in order to display messages.
|
||||||
new_object.home = home
|
|
||||||
else:
|
|
||||||
# we shouldn't need to handle dbref here (home handler should fix it), but some have
|
|
||||||
# reported issues here (issue 446).
|
|
||||||
try:
|
|
||||||
new_object.home = handle_dbref(settings.DEFAULT_HOME, _ObjectDB) if not nohome else None
|
|
||||||
except _ObjectDB.DoesNotExist:
|
|
||||||
raise _ObjectDB.DoesNotExist("settings.DEFAULT_HOME (= '%s') does not exist, or the setting is malformed." %
|
|
||||||
settings.DEFAULT_HOME)
|
|
||||||
|
|
||||||
# perform a move_to in order to display eventual messages.
|
|
||||||
if location:
|
if location:
|
||||||
new_object.move_to(location, quiet=True)
|
new_object.at_object_receive(new_object, None)
|
||||||
else:
|
new_object.at_after_move(new_object)
|
||||||
# rooms would have location=None.
|
|
||||||
new_object.location = None
|
|
||||||
|
|
||||||
# post-hook setup (mainly used by Exits)
|
# post-hook setup (mainly used by Exits)
|
||||||
new_object.basetype_posthook_setup()
|
new_object.basetype_posthook_setup()
|
||||||
|
|
||||||
new_object.save()
|
|
||||||
return new_object
|
return new_object
|
||||||
|
|
||||||
#alias for create_object
|
#alias for create_object
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ from django.forms import CharField, Textarea
|
||||||
from django.forms.util import flatatt
|
from django.forms.util import flatatt
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
|
|
||||||
|
from src.utils.dbserialize import from_pickle, to_pickle
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
|
||||||
253
src/utils/spawner.py
Normal file
253
src/utils/spawner.py
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
"""
|
||||||
|
Spawner
|
||||||
|
|
||||||
|
The spawner takes input files containing object definitions in
|
||||||
|
dictionary forms. These use a prototype architechture to define
|
||||||
|
unique objects without having to make a Typeclass for each.
|
||||||
|
|
||||||
|
The main function is spawn(*prototype), where the prototype
|
||||||
|
is a dictionary like this:
|
||||||
|
|
||||||
|
GOBLIN = {
|
||||||
|
"typeclass": "game.gamesrc.objects.objects.Monster",
|
||||||
|
"key": "goblin grunt",
|
||||||
|
"health": lambda: randint(20,30),
|
||||||
|
"resists": ["cold", "poison"],
|
||||||
|
"attacks": ["fists"],
|
||||||
|
"weaknesses": ["fire", "light"]
|
||||||
|
}
|
||||||
|
|
||||||
|
Possible keywords are:
|
||||||
|
prototype - string parent prototype
|
||||||
|
key - string, the main object identifier
|
||||||
|
typeclass - string, if not set, will use settings.BASE_OBJECT_TYPECLASS
|
||||||
|
location - this should be a valid object or #dbref
|
||||||
|
home - valid object or #dbref
|
||||||
|
destination - only valid for exits (object or dbref)
|
||||||
|
|
||||||
|
permissions - string or list of permission strings
|
||||||
|
locks - a lock-string
|
||||||
|
aliases - string or list of strings
|
||||||
|
|
||||||
|
ndb_<name> - value of a nattribute (ndb_ is stripped)
|
||||||
|
any other keywords are interpreted as Attributes and their values.
|
||||||
|
|
||||||
|
Each value can also be a callable that takes no arguments. It should
|
||||||
|
return the value to enter into the field and will be called every time
|
||||||
|
the prototype is used to spawn an object.
|
||||||
|
|
||||||
|
By specifying a prototype, the child will inherit all prototype slots
|
||||||
|
it does not explicitly define itself, while overloading those that it
|
||||||
|
does specify.
|
||||||
|
|
||||||
|
GOBLIN_WIZARD = {
|
||||||
|
"prototype": GOBLIN,
|
||||||
|
"key": "goblin wizard",
|
||||||
|
"spells": ["fire ball", "lighting bolt"]
|
||||||
|
}
|
||||||
|
|
||||||
|
GOBLIN_ARCHER = {
|
||||||
|
"prototype": GOBLIN,
|
||||||
|
"key": "goblin archer",
|
||||||
|
"attacks": ["short bow"]
|
||||||
|
}
|
||||||
|
|
||||||
|
One can also have multiple prototypes. These are inherited from the
|
||||||
|
left, with the ones further to the right taking precedence.
|
||||||
|
|
||||||
|
ARCHWIZARD = {
|
||||||
|
"attack": ["archwizard staff", "eye of doom"]
|
||||||
|
|
||||||
|
GOBLIN_ARCHWIZARD = {
|
||||||
|
"key" : "goblin archwizard"
|
||||||
|
"prototype": (GOBLIN_WIZARD, ARCHWIZARD),
|
||||||
|
}
|
||||||
|
|
||||||
|
The goblin archwizard will have some different attacks, but will
|
||||||
|
otherwise have the same spells as a goblin wizard who in turn shares
|
||||||
|
many traits with a normal goblin.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, sys
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||||
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'game.settings'
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from random import randint
|
||||||
|
from src.objects.models import ObjectDB
|
||||||
|
from src.utils.create import handle_dbref
|
||||||
|
from src.utils.utils import make_iter, all_from_module
|
||||||
|
|
||||||
|
_CREATE_OBJECT_KWARGS = ("key", "location", "home", "destination")
|
||||||
|
|
||||||
|
_handle_dbref = lambda inp: handle_dbref(inp, ObjectDB)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_prototype(dic, prot, protparents, visited):
|
||||||
|
"""
|
||||||
|
Recursively traverse a prototype dictionary,
|
||||||
|
including multiple inheritance and self-reference
|
||||||
|
detection
|
||||||
|
"""
|
||||||
|
visited.append(id(dic))
|
||||||
|
if "prototype" in dic:
|
||||||
|
# move backwards through the inheritance
|
||||||
|
for prototype in make_iter(dic["prototype"]):
|
||||||
|
if id(prototype) in visited:
|
||||||
|
# a loop was detected. Don't self-reference.
|
||||||
|
continue
|
||||||
|
# Build the prot dictionary in reverse order, overloading
|
||||||
|
new_prot = _get_prototype(protparents.get(prototype, {}), prot, protparents, visited)
|
||||||
|
prot.update(new_prot)
|
||||||
|
prot.update(dic)
|
||||||
|
prot.pop("prototype", None) # we don't need this anymore
|
||||||
|
return prot
|
||||||
|
|
||||||
|
|
||||||
|
def _batch_create_object(*objparams):
|
||||||
|
"""
|
||||||
|
This is a cut-down version of the create_object() function,
|
||||||
|
optimized for speed. It does NOT check and convert various input
|
||||||
|
so make sure the spawned Typeclass works before using this!
|
||||||
|
|
||||||
|
Input:
|
||||||
|
objsparams - each argument should be a tuple of arguments for the respective
|
||||||
|
creation/add handlers in the following order:
|
||||||
|
(create, permissions, locks, aliases, nattributes, attributes)
|
||||||
|
Returns:
|
||||||
|
A list of created objects
|
||||||
|
"""
|
||||||
|
|
||||||
|
# bulk create all objects in one go
|
||||||
|
dbobjs = [ObjectDB(**objparam[0]) for objparam in objparams]
|
||||||
|
# unfortunately this doesn't work since bulk_create don't creates pks;
|
||||||
|
# the result are double objects at the next stage
|
||||||
|
#dbobjs = _ObjectDB.objects.bulk_create(dbobjs)
|
||||||
|
|
||||||
|
objs = []
|
||||||
|
for iobj, dbobj in enumerate(dbobjs):
|
||||||
|
# call all setup hooks on each object
|
||||||
|
objparam = objparams[iobj]
|
||||||
|
obj = dbobj.typeclass # this saves dbobj if not done already
|
||||||
|
obj.basetype_setup()
|
||||||
|
obj.at_object_creation()
|
||||||
|
|
||||||
|
if objparam[1]:
|
||||||
|
# permissions
|
||||||
|
obj.permissions.add(objparam[1])
|
||||||
|
if objparam[2]:
|
||||||
|
# locks
|
||||||
|
obj.locks.add(objparam[2])
|
||||||
|
if objparam[3]:
|
||||||
|
# aliases
|
||||||
|
obj.aliases.add(objparam[3])
|
||||||
|
if objparam[4]:
|
||||||
|
# nattributes
|
||||||
|
for key, value in objparam[4].items():
|
||||||
|
obj.nattributes.add(key, value)
|
||||||
|
if objparam[5]:
|
||||||
|
# attributes
|
||||||
|
keys, values = objparam[5].keys(), objparam[5].values()
|
||||||
|
obj.attributes.batch_add(keys, values)
|
||||||
|
|
||||||
|
obj.basetype_posthook_setup()
|
||||||
|
objs.append(obj)
|
||||||
|
return objs
|
||||||
|
|
||||||
|
|
||||||
|
def spawn(*prototypes, **kwargs):
|
||||||
|
"""
|
||||||
|
Spawn a number of prototyped objects. Each argument should be a
|
||||||
|
prototype dictionary.
|
||||||
|
|
||||||
|
keyword args:
|
||||||
|
prototype_modules - a python-path to a
|
||||||
|
prototype module, or a list of such paths. These will be used
|
||||||
|
to build the global protparents dictionary accessible by the
|
||||||
|
input prototypes. If not given, it will instead look for modules
|
||||||
|
defined by settings.PROTOTYPE_MODULES.
|
||||||
|
return_prototypes - only return a list of the prototype-parents
|
||||||
|
(no object creation happens)
|
||||||
|
"""
|
||||||
|
|
||||||
|
protparents = {}
|
||||||
|
protmodules = make_iter(kwargs.get("prototype_modules", []))
|
||||||
|
if not protmodules and hasattr(settings, "PROTOTYPE_MODULES"):
|
||||||
|
protmodules = make_iter(settings.PROTOTYPE_MODULES)
|
||||||
|
for prototype_module in protmodules:
|
||||||
|
protparents.update(dict((key, val)
|
||||||
|
for key, val in all_from_module(prototype_module).items() if isinstance(val, dict)))
|
||||||
|
|
||||||
|
if "return_prototypes" in kwargs:
|
||||||
|
# only return the parents
|
||||||
|
return protparents
|
||||||
|
|
||||||
|
objsparams = []
|
||||||
|
for prototype in prototypes:
|
||||||
|
|
||||||
|
prot = _get_prototype(prototype, {}, protparents, [])
|
||||||
|
if not prot:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# extract the keyword args we need to create the object itself
|
||||||
|
create_kwargs = {}
|
||||||
|
create_kwargs["db_key"] = prot.pop("key", "Spawned Object %06i" % randint(1,100000))
|
||||||
|
create_kwargs["db_location"] = _handle_dbref(prot.pop("location", None))
|
||||||
|
create_kwargs["db_home"] = _handle_dbref(prot.pop("home", settings.DEFAULT_HOME))
|
||||||
|
create_kwargs["db_destination"] = _handle_dbref(prot.pop("destination", None))
|
||||||
|
create_kwargs["db_typeclass_path"] = prot.pop("typeclass", settings.BASE_OBJECT_TYPECLASS)
|
||||||
|
|
||||||
|
# extract calls to handlers
|
||||||
|
permission_string = prot.pop("permissions", "")
|
||||||
|
lock_string = prot.pop("locks", "")
|
||||||
|
alias_string = prot.pop("aliases", "")
|
||||||
|
|
||||||
|
# extract ndb assignments
|
||||||
|
nattributes = dict((key.split("_", 1)[1], value if callable(value) else value)
|
||||||
|
for key, value in prot.items() if key.startswith("ndb_"))
|
||||||
|
|
||||||
|
# the rest are attributes
|
||||||
|
attributes = dict((key, value() if callable(value) else value)
|
||||||
|
for key, value in prot.items()
|
||||||
|
if not (key in _CREATE_OBJECT_KWARGS or key in nattributes))
|
||||||
|
|
||||||
|
# pack for call into _batch_create_object
|
||||||
|
objsparams.append( (create_kwargs, permission_string, lock_string,
|
||||||
|
alias_string, nattributes, attributes) )
|
||||||
|
|
||||||
|
return _batch_create_object(*objsparams)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# testing
|
||||||
|
|
||||||
|
protparents = {
|
||||||
|
"NOBODY": {},
|
||||||
|
"GOBLIN" : {
|
||||||
|
"key": "goblin grunt",
|
||||||
|
"health": lambda: randint(20,30),
|
||||||
|
"resists": ["cold", "poison"],
|
||||||
|
"attacks": ["fists"],
|
||||||
|
"weaknesses": ["fire", "light"]
|
||||||
|
},
|
||||||
|
"GOBLIN_WIZARD" : {
|
||||||
|
"prototype": "GOBLIN",
|
||||||
|
"key": "goblin wizard",
|
||||||
|
"spells": ["fire ball", "lighting bolt"]
|
||||||
|
},
|
||||||
|
"GOBLIN_ARCHER" : {
|
||||||
|
"prototype": "GOBLIN",
|
||||||
|
"key": "goblin archer",
|
||||||
|
"attacks": ["short bow"]
|
||||||
|
},
|
||||||
|
"ARCHWIZARD" : {
|
||||||
|
"attacks": ["archwizard staff"],
|
||||||
|
},
|
||||||
|
"GOBLIN_ARCHWIZARD" : {
|
||||||
|
"key": "goblin archwizard",
|
||||||
|
"prototype" : ("GOBLIN_WIZARD", "ARCHWIZARD")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# test
|
||||||
|
print [o.key for o in spawn(protparents["GOBLIN"], protparents["GOBLIN_ARCHWIZARD"], prototype_parents=protparents)]
|
||||||
|
|
@ -792,6 +792,8 @@ def all_from_module(module):
|
||||||
Return all global-level variables from a module as a dict
|
Return all global-level variables from a module as a dict
|
||||||
"""
|
"""
|
||||||
mod = mod_import(module)
|
mod = mod_import(module)
|
||||||
|
if not mod:
|
||||||
|
return {}
|
||||||
return dict((key, val) for key, val in mod.__dict__.items()
|
return dict((key, val) for key, val in mod.__dict__.items()
|
||||||
if not (key.startswith("_") or ismodule(val)))
|
if not (key.startswith("_") or ismodule(val)))
|
||||||
|
|
||||||
|
|
@ -1058,67 +1060,38 @@ def deepsize(obj, max_depth=4):
|
||||||
size = getsizeof(obj) + sum([p[1] for p in sizedict.values()])
|
size = getsizeof(obj) + sum([p[1] for p in sizedict.values()])
|
||||||
return size
|
return size
|
||||||
|
|
||||||
# lazy load handlers
|
# lazy load handler
|
||||||
|
_missing = object()
|
||||||
|
class lazy_property(object):
|
||||||
|
"""
|
||||||
|
Delays loading of property until first access. Credit goes to
|
||||||
|
the Implementation in the werkzeug suite:
|
||||||
|
http://werkzeug.pocoo.org/docs/utils/#werkzeug.utils.cached_property
|
||||||
|
|
||||||
import weakref
|
This should be used as a decorator in a class and is in Evennia
|
||||||
class LazyLoadHandler(object):
|
mainly used to lazy-load handlers:
|
||||||
"""
|
|
||||||
Load handlers only when they are actually accessed
|
|
||||||
"""
|
|
||||||
def __init__(self, obj, name, cls, *args):
|
|
||||||
"""
|
|
||||||
Set up a delayed load of a class. The 'name' must be named the
|
|
||||||
same as the variable to which the LazyLoadHandler is assigned.
|
|
||||||
"""
|
|
||||||
_SA(self, "obj", weakref.ref(obj))
|
|
||||||
_SA(self, "name", name)
|
|
||||||
_SA(self, "cls", cls)
|
|
||||||
_SA(self, "args", args)
|
|
||||||
|
|
||||||
def _instantiate(self):
|
@lazy_property
|
||||||
"""
|
def attributes(self):
|
||||||
Initialize handler as cls(obj, *args)
|
return AttributeHandler(self)
|
||||||
"""
|
|
||||||
obj = _GA(self, "obj")()
|
|
||||||
instance = _GA(self, "cls")(weakref.proxy(obj), *_GA(self, "args"))
|
|
||||||
_SA(obj, _GA(self, "name"), instance)
|
|
||||||
return instance
|
|
||||||
|
|
||||||
def __getattribute__(self, name):
|
Once initialized, the AttributeHandler will be available
|
||||||
"""
|
as a property "attributes" on the object.
|
||||||
Access means loading the handler
|
|
||||||
"""
|
|
||||||
return getattr(_GA(self, "_instantiate")(), name)
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
|
||||||
"""
|
"""
|
||||||
Setting means loading the handler
|
def __init__(self, func, name=None, doc=None):
|
||||||
"""
|
"Store all properties for now"
|
||||||
setattr(_GA(self, "_instantiate")(), name, value)
|
self.__name__ = name or func.__name__
|
||||||
|
self.__module__ = func.__module__
|
||||||
|
self.__doc__ = doc or func.__doc__
|
||||||
|
self.func = func
|
||||||
|
|
||||||
def __delattr__(self, name):
|
def __get__(self, obj, type=None):
|
||||||
"""
|
"Triggers initialization"
|
||||||
Deleting also triggers loading of handler
|
if obj is None:
|
||||||
"""
|
return self
|
||||||
delattr(_GA(self, "_instantiate")(), name)
|
value = obj.__dict__.get(self.__name__, _missing)
|
||||||
|
if value is _missing:
|
||||||
def __repr__(self):
|
value = self.func(obj)
|
||||||
return repr(_GA(self, "_instantiate")())
|
obj.__dict__[self.__name__] = value
|
||||||
def __str__(self):
|
return value
|
||||||
return str(_GA(self, "_instantiate")())
|
|
||||||
def __unicode__(self):
|
|
||||||
return str(_GA(self, "_instantiate")())
|
|
||||||
|
|
||||||
class NonWeakLazyLoadHandler(LazyLoadHandler):
|
|
||||||
"""
|
|
||||||
Variation of LazyLoadHandler that does not
|
|
||||||
create a weak reference when initiating.
|
|
||||||
"""
|
|
||||||
def _instantiate(self):
|
|
||||||
"""
|
|
||||||
Initialize handler as cls(obj, *args)
|
|
||||||
"""
|
|
||||||
obj = _GA(self, "obj")()
|
|
||||||
instance = _GA(self, "cls")(obj, *_GA(self, "args"))
|
|
||||||
_SA(obj, _GA(self, "name"), instance)
|
|
||||||
return instance
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ def page_index(request):
|
||||||
# Some misc. configurable stuff.
|
# Some misc. configurable stuff.
|
||||||
# TODO: Move this to either SQL or settings.py based configuration.
|
# TODO: Move this to either SQL or settings.py based configuration.
|
||||||
fpage_player_limit = 4
|
fpage_player_limit = 4
|
||||||
fpage_news_entries = 2
|
|
||||||
|
|
||||||
# A QuerySet of the most recently connected players.
|
# A QuerySet of the most recently connected players.
|
||||||
recent_users = PlayerDB.objects.get_recently_connected_players()[:fpage_player_limit]
|
recent_users = PlayerDB.objects.get_recently_connected_players()[:fpage_player_limit]
|
||||||
|
|
@ -52,7 +51,7 @@ def page_index(request):
|
||||||
"num_others": nothers or "no"
|
"num_others": nothers or "no"
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, 'index.html', pagevars)
|
return render(request, 'evennia_general/index.html', pagevars)
|
||||||
|
|
||||||
|
|
||||||
def to_be_implemented(request):
|
def to_be_implemented(request):
|
||||||
|
|
@ -65,7 +64,7 @@ def to_be_implemented(request):
|
||||||
"page_title": "To Be Implemented...",
|
"page_title": "To Be Implemented...",
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, 'tbi.html', pagevars)
|
return render(request, 'evennia_general/tbi.html', pagevars)
|
||||||
|
|
||||||
|
|
||||||
@staff_member_required
|
@staff_member_required
|
||||||
|
|
@ -74,7 +73,7 @@ def evennia_admin(request):
|
||||||
Helpful Evennia-specific admin page.
|
Helpful Evennia-specific admin page.
|
||||||
"""
|
"""
|
||||||
return render(
|
return render(
|
||||||
request, 'evennia_admin.html', {
|
request, 'evennia_general/evennia_admin.html', {
|
||||||
'playerdb': PlayerDB})
|
'playerdb': PlayerDB})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue