diff --git a/contrib/tutorial_world/objects.py b/contrib/tutorial_world/objects.py
index 08a9f1ab3e..2d033372a3 100644
--- a/contrib/tutorial_world/objects.py
+++ b/contrib/tutorial_world/objects.py
@@ -561,6 +561,7 @@ class CrumblingWall(TutorialObject, Exit):
self.aliases = ["secret passage", "passage", "crack", "opening", "secret door"]
# this is assigned first when pushing button, so assign this at creation time!
+
self.db.destination = 2
# locks on the object directly transfer to the exit "command"
self.locks.add("cmd:not locattr(is_dark)")
diff --git a/src/commands/cmdhandler.py b/src/commands/cmdhandler.py
index 9be05a915e..44e6860a3d 100644
--- a/src/commands/cmdhandler.py
+++ b/src/commands/cmdhandler.py
@@ -139,14 +139,25 @@ def get_and_merge_cmdsets(caller):
cmdsets = yield [caller_cmdset] + [player_cmdset] + [channel_cmdset] + local_objects_cmdsets
# weed out all non-found sets
- cmdsets = yield [cmdset for cmdset in cmdsets if cmdset]
+ cmdsets = yield [cmdset for cmdset in cmdsets if cmdset and cmdset.key!="Empty"]
# report cmdset errors to user (these should already have been logged)
- yield [caller.msg(cmdset.message) for cmdset in cmdsets if cmdset.key == "_CMDSET_ERROR"]
- # sort cmdsets after reverse priority (highest prio are merged in last)
- yield cmdsets.sort(key=lambda x: x.priority)
- #cmdsets = yield sorted(cmdsets, key=lambda x: x.priority)
+ yield [caller.msg(cmdset.errmessage) for cmdset in cmdsets if cmdset.key == "_CMDSET_ERROR"]
if cmdsets:
+ # we group and merge all same-prio cmdsets separately (this avoids order-dependent
+ # clashes in certain cases, such as when duplicates=True)
+ tempmergers = {}
+ for cmdset in cmdsets:
+ prio = cmdset.priority
+ if prio in tempmergers:
+ # merge same-prio cmdset together separately
+ tempmergers[prio] = yield cmdset + tempmergers[prio]
+ else:
+ tempmergers[prio] = cmdset
+
+ # sort cmdsets after reverse priority (highest prio are merged in last)
+ cmdsets = yield sorted(tempmergers.values(), key=lambda x: x.priority)
+
# Merge all command sets into one, beginning with the lowest-prio one
cmdset = cmdsets.pop(0)
for merging_cmdset in cmdsets:
diff --git a/src/commands/cmdset.py b/src/commands/cmdset.py
index c54383ef1b..1346e2f091 100644
--- a/src/commands/cmdset.py
+++ b/src/commands/cmdset.py
@@ -128,9 +128,10 @@ class CmdSet(object):
no_objs = False
no_channels = False
permanent = False
+ errmessage = ""
# pre-store properties to duplicate straight off
to_duplicate = ("key", "cmdsetobj", "no_exits", "no_objs", "no_channels", "permanent",
- "mergetype", "priority", "duplicates")
+ "mergetype", "priority", "duplicates", "errmessage")
def __init__(self, cmdsetobj=None, key=None):
"""
@@ -152,21 +153,21 @@ class CmdSet(object):
# Priority-sensitive merge operations for cmdsets
- def _union(self, cmdset_a, cmdset_b, duplicates=False):
+ def _union(self, cmdset_a, cmdset_b):
"C = A U B. CmdSet A is assumed to have higher priority"
cmdset_c = cmdset_a._duplicate()
# we make copies, not refs by use of [:]
cmdset_c.commands = cmdset_a.commands[:]
- if duplicates and cmdset_a.priority == cmdset_b.priority:
+ if cmdset_a.duplicates and cmdset_a.priority == cmdset_b.priority:
cmdset_c.commands.extend(cmdset_b.commands)
else:
cmdset_c.commands.extend([cmd for cmd in cmdset_b if not cmd in cmdset_a])
return cmdset_c
- def _intersect(self, cmdset_a, cmdset_b, duplicates=False):
+ def _intersect(self, cmdset_a, cmdset_b):
"C = A (intersect) B. A is assumed higher priority"
cmdset_c = cmdset_a._duplicate()
- if duplicates and cmdset_a.priority == cmdset_b.priority:
+ if cmdset_a.duplicates and cmdset_a.priority == cmdset_b.priority:
for cmd in [cmd for cmd in cmdset_a if cmd in cmdset_b]:
cmdset_c.add(cmd)
cmdset_c.add(cmdset_b.get(cmd))
@@ -174,13 +175,13 @@ class CmdSet(object):
cmdset_c.commands = [cmd for cmd in cmdset_a if cmd in cmdset_b]
return cmdset_c
- def _replace(self, cmdset_a, cmdset_b, cmdset_c):
+ def _replace(self, cmdset_a, cmdset_b):
"C = A + B where the result is A."
cmdset_c = cmdset_a._duplicate()
cmdset_c.commands = cmdset_a.commands[:]
return cmdset_c
- def _remove(self, cmdset_a, cmdset_b, cmdset_c):
+ def _remove(self, cmdset_a, cmdset_b):
"C = A + B, where B is filtered by A"
cmdset_c = cmdset_a._duplicate()
cmdset_c.commands = [cmd for cmd in cmdset_b if not cmd in cmdset_a]
@@ -267,13 +268,13 @@ class CmdSet(object):
mergetype = self.key_mergetypes.get(cmdset_b.key, self.mergetype)
if mergetype == "Intersect":
- cmdset_c = self._intersect(self, cmdset_b, cmdset_b.duplicates)
+ cmdset_c = self._intersect(self, cmdset_b)
elif mergetype == "Replace":
- cmdset_c = self._replace(self, cmdset_b, cmdset_b.duplicates)
+ cmdset_c = self._replace(self, cmdset_b)
elif mergetype == "Remove":
- cmdset_c = self._remove(self, cmdset_b, cmdset_b.duplicates)
+ cmdset_c = self._remove(self, cmdset_b)
else: # Union
- cmdset_c = self._union(self, cmdset_b, cmdset_b.duplicates)
+ cmdset_c = self._union(self, cmdset_b)
cmdset_c.no_channels = self.no_channels
cmdset_c.no_exits = self.no_exits
cmdset_c.no_objs = self.no_objs
@@ -286,13 +287,13 @@ class CmdSet(object):
mergetype = cmdset_b.key_mergetypes.get(self.key, cmdset_b.mergetype)
if mergetype == "Intersect":
- cmdset_c = self._intersect(cmdset_b, self, self.duplicates)
+ cmdset_c = self._intersect(cmdset_b, self)
elif mergetype == "Replace":
- cmdset_c = self._replace(cmdset_b, self, self.duplicates)
+ cmdset_c = self._replace(cmdset_b, self)
elif mergetype == "Remove":
- cmdset_c = self._remove(self, cmdset_b, self.duplicates)
+ cmdset_c = self._remove(self, cmdset_b)
else: # Union
- cmdset_c = self._union(cmdset_b, self, self.duplicates)
+ cmdset_c = self._union(cmdset_b, self)
cmdset_c.no_channels = cmdset_b.no_channels
cmdset_c.no_exits = cmdset_b.no_exits
cmdset_c.no_objs = cmdset_b.no_objs
diff --git a/src/commands/cmdsethandler.py b/src/commands/cmdsethandler.py
index 4c19164f31..59a3d658b1 100644
--- a/src/commands/cmdsethandler.py
+++ b/src/commands/cmdsethandler.py
@@ -76,7 +76,7 @@ _CACHED_CMDSETS = {}
class _ErrorCmdSet(CmdSet):
"This is a special cmdset used to report errors"
key = "_CMDSET_ERROR"
- message = "Error when loading cmdset."
+ errmessage = "Error when loading cmdset."
def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
"""
@@ -130,7 +130,7 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
if emit_to_obj and not ServerConfig.objects.conf("server_starting_mode"):
object.__getattribute__(emit_to_obj, "msg")(errstring)
err_cmdset = _ErrorCmdSet()
- err_cmdset.message = errstring
+ err_cmdset.errmessage = errstring
return err_cmdset
# classes
@@ -283,7 +283,7 @@ class CmdSetHandler(object):
elif isinstance(cmdset, basestring):
# this is (maybe) a python path. Try to import from cache.
cmdset = self._import_cmdset(cmdset)
- if cmdset:
+ if cmdset and cmdset.key != '_CMDSET_ERROR':
if permanent and cmdset.key != '_CMDSET_ERROR':
# store the path permanently
cmdset.permanent = True
@@ -315,7 +315,7 @@ class CmdSetHandler(object):
elif isinstance(cmdset, basestring):
# this is (maybe) a python path. Try to import from cache.
cmdset = self._import_cmdset(cmdset)
- if cmdset:
+ if cmdset and cmdset.key != '_CMDSET_ERROR':
if self.cmdset_stack:
self.cmdset_stack[0] = cmdset
self.mergetype_stack[0] = cmdset.mergetype
diff --git a/src/commands/command.py b/src/commands/command.py
index 13988352dd..0c65a90873 100644
--- a/src/commands/command.py
+++ b/src/commands/command.py
@@ -9,58 +9,72 @@ import re
from src.locks.lockhandler import LockHandler
from src.utils.utils import is_iter, fill
+def _init_command(mcs, **kwargs):
+ """
+ Helper command.
+ Makes sure all data are stored as lowercase and
+ do checking on all properties that should be in list form.
+ Sets up locks to be more forgiving. This is used both by the metaclass
+ and (optionally) at instantiation time.
+
+ If kwargs are given, these are set as instance-specific properties on the command.
+ """
+ for i in range(len(kwargs)):
+ # used for dynamic creation of commands
+ key, value = kwargs.popitem()
+ setattr(mcs, key, value)
+
+ mcs.key = mcs.key.lower()
+ if mcs.aliases and not is_iter(mcs.aliases):
+ try:
+ mcs.aliases = [str(alias).strip().lower() for alias in mcs.aliases.split(',')]
+ except Exception:
+ mcs.aliases = []
+ mcs.aliases = list(set(alias for alias in mcs.aliases if alias != mcs.key))
+
+ # optimization - a set is much faster to match against than a list
+ mcs._matchset = set([mcs.key] + mcs.aliases)
+ # optimization for looping over keys+aliases
+ mcs._keyaliases = tuple(mcs._matchset)
+
+ # by default we don't save the command between runs
+ if not hasattr(mcs, "save_for_next"):
+ mcs.save_for_next = False
+
+ # pre-process locks as defined in class definition
+ temp = []
+ if hasattr(mcs, 'permissions'):
+ mcs.locks = mcs.permissions
+ if not hasattr(mcs, 'locks'):
+ # default if one forgets to define completely
+ mcs.locks = "cmd:all()"
+ if not "cmd:" in mcs.locks:
+ mcs.locks = "cmd:all();" + mcs.locks
+ for lockstring in mcs.locks.split(';'):
+ if lockstring and not ':' in lockstring:
+ lockstring = "cmd:%s" % lockstring
+ temp.append(lockstring)
+ mcs.lock_storage = ";".join(temp)
+
+ if hasattr(mcs, 'arg_regex') and isinstance(mcs.arg_regex, basestring):
+ mcs.arg_regex = re.compile(r"%s" % mcs.arg_regex, re.I)
+ else:
+ mcs.arg_regex = None
+ if not hasattr(mcs, "auto_help"):
+ mcs.auto_help = True
+ if not hasattr(mcs, 'is_exit'):
+ mcs.is_exit = False
+ if not hasattr(mcs, "help_category"):
+ mcs.help_category = "general"
+ mcs.help_category = mcs.help_category.lower()
+
+
class CommandMeta(type):
"""
- This metaclass makes some minor on-the-fly convenience fixes to the command
- class in case the admin forgets to put things in lowercase etc.
+ The metaclass cleans up all properties on the class
"""
def __init__(mcs, *args, **kwargs):
- """
- Simply make sure all data are stored as lowercase and
- do checking on all properties that should be in list form.
- Sets up locks to be more forgiving.
- """
- mcs.key = mcs.key.lower()
- if mcs.aliases and not is_iter(mcs.aliases):
- try:
- mcs.aliases = [str(alias).strip().lower() for alias in mcs.aliases.split(',')]
- except Exception:
- mcs.aliases = []
- mcs.aliases = list(set(alias for alias in mcs.aliases if alias != mcs.key))
-
- # optimization - a set is much faster to match against than a list
- mcs._matchset = set([mcs.key] + mcs.aliases)
- # optimization for looping over keys+aliases
- mcs._keyaliases = tuple(mcs._matchset)
-
- # by default we don't save the command between runs
- if not hasattr(mcs, "save_for_next"):
- mcs.save_for_next = False
-
- # pre-process locks as defined in class definition
- temp = []
- if hasattr(mcs, 'permissions'):
- mcs.locks = mcs.permissions
- if not hasattr(mcs, 'locks'):
- # default if one forgets to define completely
- mcs.locks = "cmd:all()"
- for lockstring in mcs.locks.split(';'):
- if lockstring and not ':' in lockstring:
- lockstring = "cmd:%s" % lockstring
- temp.append(lockstring)
- mcs.lock_storage = ";".join(temp)
-
- if hasattr(mcs, 'arg_regex') and isinstance(mcs.arg_regex, basestring):
- mcs.arg_regex = re.compile(r"%s" % mcs.arg_regex, re.I)
- else:
- mcs.arg_regex = None
- if not hasattr(mcs, "auto_help"):
- mcs.auto_help = True
- if not hasattr(mcs, 'is_exit'):
- mcs.is_exit = False
- if not hasattr(mcs, "help_category"):
- mcs.help_category = "general"
- mcs.help_category = mcs.help_category.lower()
+ _init_command(mcs, **kwargs)
super(CommandMeta, mcs).__init__(*args, **kwargs)
# The Command class is the basic unit of an Evennia command; when
@@ -125,8 +139,12 @@ class Command(object):
# sessid - which session-id (if any) is responsible for triggering this command
#
- def __init__(self):
- "the lockhandler works the same as for objects."
+ def __init__(self, **kwargs):
+ """the lockhandler works the same as for objects.
+ optional kwargs will be set as properties on the Command at runtime,
+ overloading evential same-named class properties."""
+ if kwargs:
+ _init_command(self, **kwargs)
self.lockhandler = LockHandler(self)
def __str__(self):
diff --git a/src/commands/default/player.py b/src/commands/default/player.py
index 346c9d1a94..cec03d2cb2 100644
--- a/src/commands/default/player.py
+++ b/src/commands/default/player.py
@@ -606,9 +606,6 @@ class CmdQuell(MuxPlayerCommand):
Hierarchical permission quelling only work downwards, thus a Player cannot
use a higher-permission Character to escalate their permission level.
Use the unquell command to revert back to normal operation.
-
- Note that the superuser character cannot be quelled. Use a separate
- admin account for testing.
"""
key = "@quell"
diff --git a/src/commands/default/unloggedin.py b/src/commands/default/unloggedin.py
index 5485d14132..6ac8e7daeb 100644
--- a/src/commands/default/unloggedin.py
+++ b/src/commands/default/unloggedin.py
@@ -4,7 +4,6 @@ Commands that are available from the connect screen.
import re
import traceback
from django.conf import settings
-from django.contrib.auth.models import User
from src.players.models import PlayerDB
from src.objects.models import ObjectDB
from src.server.models import ServerConfig
@@ -68,7 +67,7 @@ class CmdUnconnectedConnect(MuxCommand):
player = PlayerDB.objects.get_player_from_name(playername)
pswd = None
if player:
- pswd = player.user.check_password(password)
+ pswd = player.check_password(password)
if not (player and pswd):
# No playername or password match
@@ -142,7 +141,7 @@ class CmdUnconnectedCreate(MuxCommand):
return
# strip excessive spaces in playername
playername = re.sub(r"\s+", " ", playername).strip()
- if PlayerDB.objects.filter(user__username__iexact=playername) or User.objects.filter(username__iexact=playername):
+ if PlayerDB.objects.filter(username__iexact=playername):
# player already exists (we also ignore capitalization here)
session.msg("Sorry, there is already a player with the name '%s'." % playername)
return
diff --git a/src/comms/channelhandler.py b/src/comms/channelhandler.py
index 8173aeb5fc..e020e88e95 100644
--- a/src/comms/channelhandler.py
+++ b/src/comms/channelhandler.py
@@ -90,7 +90,7 @@ class ChannelCommand(command.Command):
msgobj.senders = sender
msgobj.channels = channel
# send new message object to channel
- channel.msg(msgobj, senders=sender)
+ channel.msg(msgobj, senders=sender, online=True)
class ChannelHandler(object):
"""
@@ -136,14 +136,11 @@ class ChannelHandler(object):
and run self.update on the handler.
"""
# map the channel to a searchable command
- cmd = ChannelCommand()
- cmd.key = channel.key.strip().lower()
- cmd.obj = channel
+ cmd = ChannelCommand(key=channel.key.strip().lower(),
+ aliases=channel.aliases if channel.aliases else [],
+ locks="cmd:all();%s" % channel.locks,
+ obj=channel)
cmd.__doc__= self._format_help(channel)
- if channel.aliases:
- cmd.aliases = channel.aliases
- cmd.lock_storage = "cmd:all();%s" % channel.locks
- cmd.lockhandler.reset()
self.cached_channel_cmds.append(cmd)
self.cached_cmdsets = {}
diff --git a/src/comms/managers.py b/src/comms/managers.py
index 9439f501da..6362471ae0 100644
--- a/src/comms/managers.py
+++ b/src/comms/managers.py
@@ -11,6 +11,7 @@ _GA = object.__getattribute__
_PlayerDB = None
_ObjectDB = None
_Channel = None
+_SESSIONS = None
_ExternalConnection = None
_User = None
@@ -198,7 +199,7 @@ class MsgManager(models.Manager):
NOTE: This can potentially be slow, so make sure to supply
one of the other arguments to limit the search.
dbref - (int) the exact database id of the message. This will override
- all other search crieteria since it's unique and
+ all other search criteria since it's unique and
always gives a list with only one match.
"""
# unique msg id
@@ -299,19 +300,33 @@ class ChannelManager(models.Manager):
CHANNELHANDLER.update()
return None
- def get_all_connections(self, channel):
+ def get_all_connections(self, channel, online=False):
"""
Return the connections of all players listening
- to this channel
+ to this channel. If Online is true, it only returns
+ connected players.
"""
- # import here to avoid circular imports
- #from src.comms.models import PlayerChannelConnection
+ global _SESSIONS
+ if not _SESSIONS:
+ from src.server.sessionhandler import SESSIONS as _SESSIONS
+
PlayerChannelConnection = ContentType.objects.get(app_label="comms",
model="playerchannelconnection").model_class()
ExternalChannelConnection = ContentType.objects.get(app_label="comms",
model="externalchannelconnection").model_class()
- return itertools.chain(PlayerChannelConnection.objects.get_all_connections(channel),
- ExternalChannelConnection.objects.get_all_connections(channel))
+ players = []
+ if online:
+ session_list = _SESSIONS.get_sessions()
+ unique_online_users = set(sess.uid for sess in session_list if sess.logged_in)
+ online_players = (sess.get_player() for sess in session_list if sess.uid in unique_online_users)
+ for player in online_players:
+ players.extend(PlayerChannelConnection.objects.filter(db_player=player, db_channel=channel))
+ else:
+ players.extend(PlayerChannelConnection.objects.get_all_connections(channel))
+
+ external_connections = ExternalChannelConnection.objects.get_all_connections(channel)
+
+ return itertools.chain(players, external_connections)
def channel_search(self, ostring):
"""
diff --git a/src/comms/models.py b/src/comms/models.py
index 2c039d7283..ecda0db840 100644
--- a/src/comms/models.py
+++ b/src/comms/models.py
@@ -553,7 +553,7 @@ class Channel(SharedMemoryModel):
# do the check
return PlayerChannelConnection.objects.has_player_connection(player, self)
- def msg(self, msgobj, header=None, senders=None, persistent=True):
+ def msg(self, msgobj, header=None, senders=None, persistent=True, online=False):
"""
Send the given message to all players connected to channel. Note that
no permission-checking is done here; it is assumed to have been
@@ -566,7 +566,8 @@ class Channel(SharedMemoryModel):
persistent=False.
persistent (bool) - ignored if msgobj is a Msg or TempMsg. If True, a Msg will be created, using
header and senders keywords. If False, other keywords will be ignored.
-
+ online (bool) - If this is set true, only messages people who are online. Otherwise, messages all players
+ connected. This can make things faster, but may not trigger listeners on players that are offline.
"""
if isinstance(msgobj, basestring):
@@ -588,7 +589,7 @@ class Channel(SharedMemoryModel):
msg = msgobj.message
# get all players connected to this channel and send to them
- for conn in Channel.objects.get_all_connections(self):
+ for conn in Channel.objects.get_all_connections(self, online=online):
try:
conn.player.msg(msg, senders)
except AttributeError:
diff --git a/src/objects/manager.py b/src/objects/manager.py
index 76aa1692f2..6721ea3d87 100644
--- a/src/objects/manager.py
+++ b/src/objects/manager.py
@@ -34,7 +34,6 @@ class ObjectManager(TypedObjectManager):
get_dbref_range
object_totals
typeclass_search
- get_object_with_user
get_object_with_player
get_objs_with_key_and_typeclass
get_objs_with_attr
@@ -52,29 +51,8 @@ class ObjectManager(TypedObjectManager):
# ObjectManager Get methods
#
- # user/player related
+ # player related
- @returns_typeclass
- def get_object_with_user(self, user):
- """
- Matches objects with obj.player.user matching the argument.
- A player<->user is a one-to-relationship, so this always
- returns just one result or None.
-
- user - may be a user object or user id.
- """
- dbref = self.dbref(user)
- if dbref:
- try:
- return self.get(db_player__user__id=dbref)
- except self.model.DoesNotExist:
- pass
- try:
- return self.get(db_player__user=user)
- except self.model.DoesNotExist:
- return None
-
- # This returns typeclass since get_object_with_user and get_dbref does.
@returns_typeclass
def get_object_with_player(self, ostring, exact=True, candidates=None):
"""
@@ -92,9 +70,9 @@ class ObjectManager(TypedObjectManager):
# not a dbref. Search by name.
cand_restriction = candidates and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
if exact:
- return self.filter(cand_restriction & Q(db_player__user__username__iexact=ostring))
+ return self.filter(cand_restriction & Q(db_player__username__iexact=ostring))
else: # fuzzy matching
- ply_cands = self.filter(cand_restriction & Q(playerdb__user__username__istartswith=ostring)).values_list("db_key", flat=True)
+ ply_cands = self.filter(cand_restriction & Q(playerdb__username__istartswith=ostring)).values_list("db_key", flat=True)
if candidates:
index_matches = string_partial_matching(ply_cands, ostring, ret_index=True)
return [obj for ind, obj in enumerate(make_iter(candidates)) if ind in index_matches]
@@ -175,7 +153,8 @@ class ObjectManager(TypedObjectManager):
if isinstance(property_value, basestring):
property_value = to_unicode(property_value)
if isinstance(property_name, basestring):
- property_name = "db_%s" % property_name.lstrip('db_')
+ if not property_name.startswith('db_'):
+ property_name = "db_%s" % property_name
querykwargs = {property_name:property_value}
cand_restriction = candidates and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
@@ -183,6 +162,10 @@ class ObjectManager(TypedObjectManager):
return list(self.filter(cand_restriction & type_restriction & Q(**querykwargs)))
except exceptions.FieldError:
return []
+ except ValueError:
+ from src.utils import logger
+ logger.log_errmsg("The property '%s' does not support search criteria of the type %s." % (property_name, type(property_value)))
+ return []
@returns_typeclass_list
def get_contents(self, location, excludeobj=None):
@@ -253,7 +236,8 @@ class ObjectManager(TypedObjectManager):
By default (if not attribute_name is set), this will search object.key and object.aliases in order. Can also
be on the form #dbref, which will, if exact=True be matched against primary key.
attribute_name: (str): Use this named ObjectAttribute to match searchdata against, instead
- of the defaults.
+ of the defaults. If this is the name of a database field (with or without the db_ prefix), that
+ will be matched too.
typeclass (str or TypeClass): restrict matches to objects having this typeclass. This will help
speed up global searches.
candidates (list obj ObjectDBs): If supplied, search will only be performed among the candidates
diff --git a/src/objects/migrations/0005_add_object_default_locks.py b/src/objects/migrations/0005_add_object_default_locks.py
index 8dccaf25b5..0ae1bd3dd4 100644
--- a/src/objects/migrations/0005_add_object_default_locks.py
+++ b/src/objects/migrations/0005_add_object_default_locks.py
@@ -10,7 +10,7 @@ class Migration(DataMigration):
"Write your forwards methods here."
# we need to add a default lock string to all objects, then a separate set to Characters.
-
+
lockstring1 = 'control:id(1);get:all();edit:perm(Wizards);examine:perm(Builders);call:true();puppet:id(#4) or perm(Immortals) or pperm(Immortals);delete:id(1) or perm(Wizards)'
lockstring2 = 'control:id(#3) or perm(Immortals);get:perm(Wizards);edit:perm(Wizards);examine:perm(Builders);call:false();puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals);delete:perm(Wizards)'
@@ -21,10 +21,10 @@ class Migration(DataMigration):
for obj in orm.ObjectDB.objects.filter(db_player__isnull=False):
obj.db_lock_storage = lockstring2 % (obj.id, obj.db_player.id)
obj.save()
-
+
except utils.DatabaseError:
# running from scatch. In this case we just ignore this.
- pass
+ pass
def backwards(self, orm):
"Write your backwards methods here."
diff --git a/src/objects/models.py b/src/objects/models.py
index bf7e565fcb..b34ee43707 100644
--- a/src/objects/models.py
+++ b/src/objects/models.py
@@ -640,7 +640,7 @@ class ObjectDB(TypedObject):
if searchdata == _HERE:
return self.location
if searchdata in (_ME, _SELF):
- return self
+ return self.typeclass
if use_nicks:
nick = None
diff --git a/src/objects/objects.py b/src/objects/objects.py
index f67491882b..6a748cdbb3 100644
--- a/src/objects/objects.py
+++ b/src/objects/objects.py
@@ -37,14 +37,14 @@ class Object(TypeClass):
# __init__ is only defined here in order to present docstring to API.
def __init__(self, dbobj):
"""
- This is the root typeclass object representing all entities
- that has and actual presence in-game. Objects generally has a
- location, can be manipulated and looked at. Most game entities
- you define should inherit from Object at some distance.
- Important subclasses of Object are that Evennia defines by
- default for you are Characters, Exits and Rooms.
+ This is the root typeclass object, representing all entities
+ that have an actual presence in-game. Objects generally have a
+ location. They can also be manipulated and looked at. Most
+ game entities you define should inherit from Object at some distance.
+ Evennia defines some important subclasses of Object by default, namely
+ Characters, Exits and Rooms (see the bottom of this module).
- Note that all Objects and its subclasses *must* always be
+ Note that all new Objects and their subclasses *must* always be
created using the ev.create_object() function. This is so the
typeclass system can be correctly initiated behind the scenes.
@@ -54,7 +54,7 @@ class Object(TypeClass):
* Available properties (only available on *initiated* typeclass objects)
key (string) - name of object
- name (string)- same as key
+ name (string) - same as key
aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings.
dbref (int, read-only) - unique #id-number. Also "id" can be used.
dbobj (Object, read-only) - link to database model. dbobj.typeclass points back to this class
@@ -465,12 +465,13 @@ class Object(TypeClass):
"""
pass
- def at_pre_puppet(self, player):
+ def at_pre_puppet(self, player, sessid=None):
"""
Called just before a Player connects to this object
to puppet it.
player - connecting player object
+ sessid - session id controlling the connection
"""
pass
@@ -488,13 +489,14 @@ class Object(TypeClass):
"""
pass
- def at_post_unpuppet(self, player):
+ def at_post_unpuppet(self, player, sessid=None):
"""
Called just after the Player successfully disconnected
from this object, severing all connections.
player - the player object that just disconnected from
this object.
+ sessid - session id controlling the connection
"""
pass
@@ -755,7 +757,7 @@ class Object(TypeClass):
return message
#
-# Base Player object
+# Base Character object
#
class Character(Object):
@@ -793,7 +795,7 @@ class Character(Object):
"Default is to look around after a move."
self.execute_cmd('look')
- def at_pre_puppet(self, player):
+ def at_pre_puppet(self, player, sessid=None):
"""
This recovers the character again after having been "stoved away" at the unpuppet
"""
@@ -809,7 +811,7 @@ class Character(Object):
self.location.msg_contents("%s has entered the game." % self.name, exclude=[self])
self.location.at_object_receive(self, self.location)
else:
- player.msg("{r%s has no location and no home is set.{n" % self)
+ player.msg("{r%s has no location and no home is set.{n" % self, sessid=sessid)
def at_post_puppet(self):
"""
@@ -820,7 +822,7 @@ class Character(Object):
if self.location:
self.location.msg_contents("%s has entered the game." % self.name, exclude=[self])
- def at_post_unpuppet(self, player):
+ def at_post_unpuppet(self, player, sessid=None):
"""
We stove away the character when the player goes ooc/logs off, otherwise the character object will
remain in the room also after the player logged off ("headless", so to say).
@@ -836,14 +838,13 @@ class Character(Object):
class Room(Object):
"""
- This is the base room object. It's basically
- like any Object except its location is None.
+ This is the base room object. It's just like any Object except its
+ location is None.
"""
def basetype_setup(self):
"""
Simple setup, shown as an example
(since default is None anyway)
-
"""
super(Room, self).basetype_setup()
@@ -852,19 +853,18 @@ class Room(Object):
self.location = None
#
-# Exits
+# Base Exit object
#
class Exit(Object):
"""
- This is the base exit object - it connects a location to
- another. This is done by the exit assigning a "command" on itself
- with the same name as the exit object (to do this we need to
- remember to re-create the command when the object is cached since it must be
+ This is the base exit object - it connects a location to another.
+ This is done by the exit assigning a "command" on itself with the
+ same name as the exit object (to do this we need to remember to
+ re-create the command when the object is cached since it must be
created dynamically depending on what the exit is called). This
- command (which has a high priority) will thus allow us to traverse exits
- simply by giving the exit-object's name on its own.
-
+ command (which has a high priority) will thus allow us to traverse
+ exits simply by giving the exit-object's name on its own.
"""
# Helper classes and methods to implement the Exit. These need not
@@ -909,13 +909,12 @@ class Exit(Object):
self.obj.at_failed_traverse(self.caller)
# create an exit command.
- cmd = ExitCommand()
- cmd.key = exidbobj.db_key.strip().lower()
- cmd.obj = exidbobj
- cmd.aliases = exidbobj.aliases
- cmd.locks = str(exidbobj.locks)
- cmd.destination = exidbobj.db_destination
- cmd.auto_help = False
+ cmd = ExitCommand(key=exidbobj.db_key.strip().lower(),
+ aliases=exidbobj.aliases,
+ locks=str(exidbobj.locks),
+ auto_help=False,
+ destination=exidbobj.db_destination,
+ obj=exidbobj)
# create a cmdset
exit_cmdset = cmdset.CmdSet(None)
exit_cmdset.key = '_exitset'
diff --git a/src/players/admin.py b/src/players/admin.py
index 6c0142fe4f..5012f557f6 100644
--- a/src/players/admin.py
+++ b/src/players/admin.py
@@ -142,6 +142,7 @@ class UserAdmin(BaseUserAdmin):
{'fields': ('username', 'password1', 'password2', 'email'),
'description':"These account details are shared by the admin system and the game."},),)
+ # TODO! Remove User reference!
def save_formset(self, request, form, formset, change):
"Run all hooks on the player object"
super(UserAdmin, self).save_formset(request, form, formset, change)
diff --git a/src/players/manager.py b/src/players/manager.py
index 9916928f51..4bfc9605b0 100644
--- a/src/players/manager.py
+++ b/src/players/manager.py
@@ -3,8 +3,8 @@ The managers for the custom Player object and permissions.
"""
import datetime
+from django.contrib.auth.models import UserManager
from functools import update_wrapper
-from django.contrib.auth.models import User
from src.typeclasses.managers import returns_typeclass_list, returns_typeclass, TypedObjectManager
from src.utils import logger
__all__ = ("PlayerManager",)
@@ -13,54 +13,7 @@ __all__ = ("PlayerManager",)
# Player Manager
#
-def returns_player_list(method):
- """
- decorator that makes sure that a method
- returns a Player object instead of a User
- one (if you really want the User object, not
- the player, use the player's 'user' property)
- """
- def func(self, *args, **kwargs):
- "This *always* returns a list."
- match = method(self, *args, **kwargs)
- if not match:
- return []
- try:
- match = list(match)
- except TypeError:
- match = [match]
- players = []
- for user in match:
- try:
- players.append(user.get_profile())
- except Exception:
- # there is something wrong with get_profile. But
- # there is a 1-1 relation between Users-Players, so we
- # try to go the other way instead.
- from src.players.models import PlayerDB
- match = PlayerDB.objects.filter(user__id=user.id)
- if match:
- players.append(match[0])
- else:
- logger.log_trace("No connection User<->Player, maybe database was partially reset?")
- return players
- return update_wrapper(func, method)
-
-def returns_player(method):
- """
- Decorator: Always returns a single result or None.
- """
- def func(self, *args, **kwargs):
- "decorator"
- rfunc = returns_player_list(method)
- match = rfunc(self, *args, **kwargs)
- if match:
- return match[0]
- else:
- return None
- return update_wrapper(func, method)
-
-class PlayerManager(TypedObjectManager):
+class PlayerManager(TypedObjectManager, UserManager):
"""
This PlayerManager implements methods for searching
and manipulating Players directly from the database.
@@ -87,7 +40,7 @@ class PlayerManager(TypedObjectManager):
"""
def num_total_players(self):
"""
- Returns the total number of registered users/players.
+ Returns the total number of registered players.
"""
return self.count()
@@ -99,7 +52,6 @@ class PlayerManager(TypedObjectManager):
return self.filter(db_is_connected=True)
@returns_typeclass_list
- @returns_player_list
def get_recently_created_players(self, days=7):
"""
Returns a QuerySet containing the player User accounts that have been
@@ -108,13 +60,12 @@ class PlayerManager(TypedObjectManager):
end_date = datetime.datetime.now()
tdelta = datetime.timedelta(days)
start_date = end_date - tdelta
- return User.objects.filter(date_joined__range=(start_date, end_date))
+ return self.filter(date_joined__range=(start_date, end_date))
@returns_typeclass_list
- @returns_player_list
def get_recently_connected_players(self, days=7):
"""
- Returns a QuerySet containing the player User accounts that have been
+ Returns a QuerySet containing the player accounts that have been
connected within the last days.
days - number of days backwards to check
@@ -122,33 +73,31 @@ class PlayerManager(TypedObjectManager):
end_date = datetime.datetime.now()
tdelta = datetime.timedelta(days)
start_date = end_date - tdelta
- return User.objects.filter(last_login__range=(
+ return self.filter(last_login__range=(
start_date, end_date)).order_by('-last_login')
@returns_typeclass
- @returns_player
def get_player_from_email(self, uemail):
"""
Returns a player object when given an email address.
"""
- return User.objects.filter(email__iexact=uemail)
+ return self.filter(email__iexact=uemail)
@returns_typeclass
- @returns_player
def get_player_from_uid(self, uid):
"""
Returns a player object based on User id.
"""
try:
- return User.objects.get(id=uid)
- except User.model.DoesNotExist:
+ return self.get(id=uid)
+ except self.model.DoesNotExist:
return None
@returns_typeclass
def get_player_from_name(self, uname):
"Get player object based on name"
try:
- return self.get(user__username__iexact=uname)
+ return self.get(username__iexact=uname)
except self.model.DoesNotExist:
return None
@@ -165,7 +114,7 @@ class PlayerManager(TypedObjectManager):
matches = self.filter(id=dbref)
if matches:
return matches
- return self.filter(user__username__iexact=ostring)
+ return self.filter(username__iexact=ostring)
def swap_character(self, player, new_character, delete_old_character=False):
"""
diff --git a/src/players/migrations/0003_auto__add_field_playerdb_db_cmdset_storage.py b/src/players/migrations/0003_auto__add_field_playerdb_db_cmdset_storage.py
index c089e1f8ec..3169188d57 100644
--- a/src/players/migrations/0003_auto__add_field_playerdb_db_cmdset_storage.py
+++ b/src/players/migrations/0003_auto__add_field_playerdb_db_cmdset_storage.py
@@ -7,13 +7,13 @@ from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
-
+
# Adding field 'PlayerDB.db_cmdset_storage'
db.add_column('players_playerdb', 'db_cmdset_storage', self.gf('django.db.models.fields.TextField')(null=True), keep_default=False)
def backwards(self, orm):
-
+
# Deleting field 'PlayerDB.db_cmdset_storage'
db.delete_column('players_playerdb', 'db_cmdset_storage')
diff --git a/src/players/migrations/0021_add_playerdbtmp.py b/src/players/migrations/0021_add_playerdbtmp.py
new file mode 100644
index 0000000000..8a51877312
--- /dev/null
+++ b/src/players/migrations/0021_add_playerdbtmp.py
@@ -0,0 +1,162 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models, connection
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'PlayerDBtmp'
+ if "auth_user" in connection.introspection.table_names():
+ # auth_user exists ffrom before. Use that as a base.
+ db.rename_table('auth_user', 'players_playerdbtmp')
+ else:
+ # from-scratch creation; no auth_user table available. Create vanilla User table
+ db.create_table(u'players_playerdbtmp', (
+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('password', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('last_login', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
+ ('is_superuser', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('username', self.gf('django.db.models.fields.CharField')(unique=True, max_length=30)),
+ ('first_name', self.gf('django.db.models.fields.CharField')(max_length=30, blank=True)),
+ ('last_name', self.gf('django.db.models.fields.CharField')(max_length=30, blank=True)),
+ ('email', self.gf('django.db.models.fields.EmailField')(max_length=75, blank=True)),
+ ('is_staff', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('is_active', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('date_joined', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
+ ))
+ db.send_create_signal(u'players', ['PlayerDBtmp'])
+
+ # Adding M2M table for field groups on 'PlayerDBtmp'
+ db.create_table(u'players_playerdbtmp_groups', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('playerdbtmp', models.ForeignKey(orm[u'players.playerdbtmp'], null=False)),
+ ('group', models.ForeignKey(orm[u'auth.group'], null=False))
+ ))
+ db.create_unique(u'players_playerdbtmp_groups', ['playerdbtmp_id', 'group_id'])
+
+ # Adding M2M table for field user_permissions on 'PlayerDBtmp'
+ db.create_table(u'players_playerdbtmp_user_permissions', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('playerdbtmp', models.ForeignKey(orm[u'players.playerdbtmp'], null=False)),
+ ('permission', models.ForeignKey(orm[u'auth.permission'], null=False))
+ ))
+ db.create_unique(u'players_playerdbtmp_user_permissions', ['playerdbtmp_id', 'permission_id'])
+
+ # Adding field 'PlayerDB.db_is_connected'
+ db.add_column(u'players_playerdb', 'db_is_connected',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+ # Adding field 'PlayerDB.db_cmdset_storage'
+ db.add_column(u'players_playerdb', 'db_cmdset_storage',
+ self.gf('django.db.models.fields.CharField')(max_length=255, null=True),
+ keep_default=False)
+
+ # add Evennia-specific columns
+ db.add_column('players_playerdbtmp', 'db_key', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True, null=True))
+ db.add_column('players_playerdbtmp', 'db_typeclass_path', self.gf('django.db.models.fields.CharField')(max_length=255, null=True))
+ db.add_column('players_playerdbtmp', 'db_date_created', self.gf('django.db.models.fields.DateTimeField')(null=True, auto_now_add=True, blank=True))
+ db.add_column('players_playerdbtmp', 'db_permissions', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True, null=True))
+ db.add_column('players_playerdbtmp', 'db_lock_storage', self.gf('django.db.models.fields.TextField')(blank=True, null=True))
+ db.add_column('players_playerdbtmp', 'db_is_connected', self.gf('django.db.models.fields.BooleanField')(default=False))
+ db.add_column('players_playerdbtmp', 'db_cmdset_storage', self.gf('django.db.models.fields.CharField')(max_length=255, null=True))
+
+ def backwards(self, orm):
+ raise RuntimeError("Cannot revert migration")
+
+
+ models = {
+ u'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ u'auth.permission': {
+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ u'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ u'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ u'players.playerattribute': {
+ 'Meta': {'object_name': 'PlayerAttribute'},
+ '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_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']"}),
+ 'db_value': ('src.utils.picklefield.PickledObjectField', [], {'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ },
+ u'players.playerdb': {
+ 'Meta': {'object_name': 'PlayerDB'},
+ 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'db_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'unique': 'True'})
+ },
+ u'players.playerdbtmp': {
+ 'Meta': {'ordering': "['-db_date_created', 'id', 'db_typeclass_path', 'db_key']", 'object_name': 'PlayerDBtmp'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ '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_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ u'players.playernick': {
+ 'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'PlayerNick'},
+ 'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']"}),
+ 'db_real': ('django.db.models.fields.TextField', [], {}),
+ 'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ }
+ }
+
+ complete_apps = ['players']
diff --git a/src/players/migrations/0022_copy_user_profile_to_tmp.py b/src/players/migrations/0022_copy_user_profile_to_tmp.py
new file mode 100644
index 0000000000..030e6d21e8
--- /dev/null
+++ b/src/players/migrations/0022_copy_user_profile_to_tmp.py
@@ -0,0 +1,118 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ "Write your forwards methods here."
+ # Note: Remember to use orm['appname.ModelName'] rather than "from appname.models..."
+ if not db.dry_run:
+ for profile in orm['players.PlayerDB'].objects.all():
+ plyr = orm['players.PlayerDBtmp'].objects.get(id=profile.user_id)
+ plyr.db_cmdset_storage = profile.db_cmdset_storage
+ plyr.db_date_created = profile.db_date_created
+ plyr.db_is_connected = profile.db_is_connected
+ plyr.db_key = profile.db_key
+ plyr.db_lock_storage = profile.db_lock_storage
+ plyr.db_typeclass_path = profile.db_typeclass_path
+ plyr.db_permissions = profile.db_permissions
+ plyr.save()
+
+ def backwards(self, orm):
+ "Write your backwards methods here."
+ raise RuntimeError("Cannot revert this migration.")
+
+ models = {
+ u'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ u'auth.permission': {
+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ u'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ u'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ u'players.playerattribute': {
+ 'Meta': {'object_name': 'PlayerAttribute'},
+ '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_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']"}),
+ 'db_value': ('src.utils.picklefield.PickledObjectField', [], {'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ },
+ u'players.playerdb': {
+ 'Meta': {'object_name': 'PlayerDB'},
+ 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'db_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'unique': 'True'})
+ },
+ u'players.playerdbtmp': {
+ 'Meta': {'ordering': "['-db_date_created', 'id', 'db_typeclass_path', 'db_key']", 'object_name': 'PlayerDBtmp'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ '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_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ u'players.playernick': {
+ 'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'PlayerNick'},
+ 'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']"}),
+ 'db_real': ('django.db.models.fields.TextField', [], {}),
+ 'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ }
+ }
+
+ complete_apps = ['players']
+ symmetrical = True
diff --git a/src/players/migrations/0023_delete_old_profile.py b/src/players/migrations/0023_delete_old_profile.py
new file mode 100644
index 0000000000..dd1f7c2dd8
--- /dev/null
+++ b/src/players/migrations/0023_delete_old_profile.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.delete_table('players_playerdb')
+
+ def backwards(self, orm):
+ raise RuntimeError("Cannot revert this migration")
+
+ models = {
+ u'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ u'auth.permission': {
+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ u'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ u'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ u'players.playerattribute': {
+ 'Meta': {'object_name': 'PlayerAttribute'},
+ '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_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']"}),
+ 'db_value': ('src.utils.picklefield.PickledObjectField', [], {'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ },
+ u'players.playerdb': {
+ 'Meta': {'object_name': 'PlayerDB'},
+ 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'db_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'unique': 'True'})
+ },
+ u'players.playerdbtmp': {
+ 'Meta': {'ordering': "['-db_date_created', 'id', 'db_typeclass_path', 'db_key']", 'object_name': 'PlayerDBtmp'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ '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_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ u'players.playernick': {
+ 'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'PlayerNick'},
+ 'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']"}),
+ 'db_real': ('django.db.models.fields.TextField', [], {}),
+ 'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ }
+ }
+
+ complete_apps = ['players']
diff --git a/src/players/migrations/0024_rename_tmp_player_to_playerdb.py b/src/players/migrations/0024_rename_tmp_player_to_playerdb.py
new file mode 100644
index 0000000000..1a942b8be1
--- /dev/null
+++ b/src/players/migrations/0024_rename_tmp_player_to_playerdb.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.rename_table('players_PlayerDBtmp', 'players_PlayerDB')
+ db.send_create_signal('players', ['PlayerDB'])
+
+ def backwards(self, orm):
+ raise RuntimeError("Cannot revert this migration.")
+
+ models = {
+ u'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ u'auth.permission': {
+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ u'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ u'players.playerattribute': {
+ 'Meta': {'object_name': 'PlayerAttribute'},
+ '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_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']"}),
+ 'db_value': ('src.utils.picklefield.PickledObjectField', [], {'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ },
+# u'players.playerdbtmp': {
+# 'Meta': {'ordering': "['-db_date_created', 'id', 'db_typeclass_path', 'db_key']", 'object_name': 'PlayerDBtmp'},
+# 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+# '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_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+# 'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+# 'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+# 'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+# 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+# 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+# 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+# 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+# u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+# 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+# 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+# 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+# 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+# 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+# 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+# 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+# 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+# },
+ u'players.playerdb': {
+ 'Meta': {'ordering': "['-db_date_created', 'id', 'db_typeclass_path', 'db_key']", 'object_name': 'PlayerDB'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ '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_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ u'players.playernick': {
+ 'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'PlayerNick'},
+ 'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['players.PlayerDB']"}),
+ 'db_real': ('django.db.models.fields.TextField', [], {}),
+ 'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ }
+ }
+
+ complete_apps = ['players']
diff --git a/src/players/models.py b/src/players/models.py
index 7b7a3f04c9..42af97a888 100644
--- a/src/players/models.py
+++ b/src/players/models.py
@@ -25,7 +25,7 @@ account info and OOC account configuration variables etc.
from django.conf import settings
from django.db import models
-from django.contrib.auth.models import User
+from django.contrib.auth.models import AbstractUser, User
from django.utils.encoding import smart_str
from django.db.models.signals import post_init, pre_delete
@@ -95,7 +95,8 @@ class PlayerNickHandler(TypeNickHandler):
#
#------------------------------------------------------------
-class PlayerDB(TypedObject):
+
+class PlayerDB(TypedObject, AbstractUser):
"""
This is a special model using Django's 'profile' functionality
and extends the default Django User model. It is defined as such
@@ -130,8 +131,8 @@ class PlayerDB(TypedObject):
# this is the one-to-one link between the customized Player object and
# this profile model. It is required by django.
- user = models.ForeignKey(User, unique=True, db_index=True,
- help_text="The User object holds django-specific authentication for each Player. A unique User should be created and tied to each Player, the two should never be switched or changed around. The User will be deleted automatically when the Player is.")
+ #user = models.ForeignKey(User, unique=True, db_index=True,
+ # help_text="The User object holds django-specific authentication for each Player. A unique User should be created and tied to each Player, the two should never be switched or changed around. The User will be deleted automatically when the Player is.")
# store a connected flag here too, not just in sessionhandler.
# This makes it easier to track from various out-of-process locations
db_is_connected = models.BooleanField(default=False, verbose_name="is_connected", help_text="If player is connected to game or not")
@@ -238,17 +239,19 @@ class PlayerDB(TypedObject):
#@property
def __name_get(self):
"Getter. Allows for value = self.name"
- name = get_prop_cache(self, "_name")
- if not name:
- name = _GA(self,"user").username
- set_prop_cache(self, "_name", name)
- return name
+ return self.username
+ #name = get_prop_cache(self, "_name")
+ #if not name:
+ # name = _GA(self,"user").username
+ # set_prop_cache(self, "_name", name)
+ #return name
#@name.setter
def __name_set(self, value):
"Setter. Allows for player.name = newname"
- _GA(self, "user").username = value
- _GA(self, "user").save()
- set_prop_cache(self, "_name", value)
+ self.username = value
+ #_GA(self, "user").username = value
+ #_GA(self, "user").save()
+ #set_prop_cache(self, "_name", value)
#@name.deleter
def __name_del(self):
"Deleter. Allows for del self.name"
@@ -259,11 +262,12 @@ class PlayerDB(TypedObject):
#@property
def __uid_get(self):
"Getter. Retrieves the user id"
- uid = get_prop_cache(self, "_uid")
- if not uid:
- uid = _GA(self, "user").id
- set_prop_cache(self, "_uid", uid)
- return uid
+ return self.id
+ #uid = get_prop_cache(self, "_uid")
+ #if not uid:
+ # uid = _GA(self, "user").id
+ # set_prop_cache(self, "_uid", uid)
+ #return uid
def __uid_set(self, value):
raise Exception("User id cannot be set!")
def __uid_del(self):
@@ -271,14 +275,15 @@ class PlayerDB(TypedObject):
uid = property(__uid_get, __uid_set, __uid_del)
#@property
- def __is_superuser_get(self):
- "Superusers have all permissions."
- is_suser = get_prop_cache(self, "_is_superuser")
- if is_suser == None:
- is_suser = _GA(self, "user").is_superuser
- set_prop_cache(self, "_is_superuser", is_suser)
- return is_suser
- is_superuser = property(__is_superuser_get)
+ #def __is_superuser_get(self):
+ # "Superusers have all permissions."
+ # return self.db_is_superuser
+ # #is_suser = get_prop_cache(self, "_is_superuser")
+ # #if is_suser == None:
+ # # is_suser = _GA(self, "user").is_superuser
+ # # set_prop_cache(self, "_is_superuser", is_suser)
+ # #return is_suser
+ #is_superuser = property(__is_superuser_get)
#
# PlayerDB class access methods
@@ -405,7 +410,7 @@ class PlayerDB(TypedObject):
# with a lingering player/sessid reference from an unclean server kill or similar
if normal_mode:
- _GA(obj.typeclass, "at_pre_puppet")(self.typeclass)
+ _GA(obj.typeclass, "at_pre_puppet")(self.typeclass, sessid=sessid)
# do the connection
obj.sessid = sessid
obj.player = self
@@ -437,7 +442,7 @@ class PlayerDB(TypedObject):
del obj.dbobj.player
session.puppet = None
session.puid = None
- _GA(obj.typeclass, "at_post_unpuppet")(self)
+ _GA(obj.typeclass, "at_post_unpuppet")(self.typeclass, sessid=sessid)
return True
def unpuppet_all(self):
@@ -500,16 +505,12 @@ class PlayerDB(TypedObject):
self.unpuppet_object(session.sessid)
session.sessionhandler.disconnect(session, reason=_("Player being deleted."))
- try:
- if _GA(self, "user"):
- _GA(_GA(self, "user"), "delete")()
- except AssertionError:
- pass
- try:
- super(PlayerDB, self).delete(*args, **kwargs)
- except AssertionError:
- # this means deleting the user already cleared out the Player object.
- pass
+ #try:
+ # if _GA(self, "user"):
+ # _GA(_GA(self, "user"), "delete")()
+ #except AssertionError:
+ # pass
+ super(PlayerDB, self).delete(*args, **kwargs)
def execute_cmd(self, raw_string, sessid=None):
"""
@@ -557,3 +558,6 @@ class PlayerDB(TypedObject):
except:
pass
return matches
+
+class PlayerDBtmp(AbstractUser):
+ pass
diff --git a/src/scripts/manager.py b/src/scripts/manager.py
index 66c739aa16..6941f27b88 100644
--- a/src/scripts/manager.py
+++ b/src/scripts/manager.py
@@ -2,6 +2,7 @@
The custom manager for Scripts.
"""
+from django.db.models import Q
from src.typeclasses.managers import TypedObjectManager
from src.typeclasses.managers import returns_typeclass_list
from src.utils.utils import make_iter
@@ -194,39 +195,24 @@ class ScriptManager(TypedObjectManager):
if dbref or dbref == 0:
# this is a dbref, try to find the script directly
dbref_match = self.dbref_search(dbref)
- if dbref_match:
- ok = True
- if obj and obj != dbref_match.obj:
- ok = False
- if only_timed and dbref_match.interval:
- ok = False
- if ok:
- return [dbref_match]
- if obj:
- # convenience check to make sure obj is really a dbobj
- obj = hasattr(obj, "dbobj") and obj.dbobj or obj
+ if dbref_match and not ((obj and obj != dbref_match.obj)
+ or (only_timed and dbref_match.interval)):
+ return [dbref_match]
# not a dbref; normal search
- scripts = self.filter(db_key__iexact=ostring)
-
- if obj:
- scripts = scripts.exclude(db_obj=None).filter(db_obj__db_key__iexact=ostring)
- if only_timed:
- scripts = scripts.exclude(interval=0)
+ obj_restriction = obj and Q(db_obj=obj.dbobj) or Q()
+ timed_restriction = only_timed and Q(interval__gt=0) or Q()
+ scripts = self.filter(timed_restriction & obj_restriction & Q(db_key__iexact=ostring))
return scripts
def copy_script(self, original_script, new_key=None, new_obj=None, new_locks=None):
"""
Make an identical copy of the original_script
"""
-
typeclass = original_script.typeclass_path
- if not new_key:
- new_key = original_script.key
- if not new_obj:
- new_obj = original_script.obj
- if not new_locks:
- new_locks = original_script.db_lock_storage
+ new_key = new_key if new_key!=None else original_script.key
+ new_obj = new_obj if new_obj!=None else original_script.obj
+ new_locks = new_locks if new_locks!=None else original_script.db_lock_storage
from src.utils import create
new_script = create.create_script(typeclass, key=new_key, obj=new_obj, locks=new_locks, autostart=True)
diff --git a/src/server/initial_setup.py b/src/server/initial_setup.py
index dc0326fe78..feaca4ec33 100644
--- a/src/server/initial_setup.py
+++ b/src/server/initial_setup.py
@@ -7,13 +7,15 @@ Everything starts at handle_setup()
"""
import django
-from django.contrib.auth.models import User
from django.core import management
from django.conf import settings
+from django.core.management import call_command
+from django.contrib.auth import get_user_model
from src.server.models import ServerConfig
from src.help.models import HelpEntry
from src.utils import create
+
from django.utils.translation import ugettext as _
def create_config_values():
@@ -23,11 +25,20 @@ def create_config_values():
ServerConfig.objects.conf("site_name", settings.SERVERNAME)
ServerConfig.objects.conf("idle_timeout", settings.IDLE_TIMEOUT)
-def get_god_user():
+def get_god_player():
"""
- Returns the initially created 'god' User object.
+ Creates the god user.
"""
- return User.objects.get(id=1)
+ PlayerDB = get_user_model()
+ try:
+ god_player = PlayerDB.objects.get(id=1)
+ except PlayerDB.DoesNotExist:
+ txt = "\n\nNo superuser exists yet. The superuser is the 'owner' account on the"
+ txt += "\nEvennia server. Create a new superuser using the command"
+ txt += "\n\n python manage.py createsuperuser"
+ txt += "\n\nFollow the prompts, then restart the server."
+ raise Exception(txt)
+ return god_player
def create_objects():
"""
@@ -38,22 +49,26 @@ def create_objects():
# Set the initial User's account object's username on the #1 object.
# This object is pure django and only holds name, email and password.
- god_user = get_god_user()
+ god_player = get_god_player()
# Create a Player 'user profile' object to hold eventual
# mud-specific settings for the bog standard User object. This is
# accessed by user.get_profile() and can also store attributes.
# It also holds mud permissions, but for a superuser these
# have no effect anyhow.
- character_typeclass = settings.BASE_CHARACTER_TYPECLASS
+ player_typeclass = settings.BASE_PLAYER_TYPECLASS
- # Create the Player object as well as the in-game god-character
- # for user #1. We can't set location and home yet since nothing
+ # run all creation hooks on god_player (we must do so manually since the manage.py command does not)
+ god_player.typeclass_path = player_typeclass
+ god_player.basetype_setup()
+ god_player.at_player_creation()
+ god_player.locks.add("examine:perm(Immortals);edit:false();delete:false();boot:false();msg:all()")
+
+ # Create the in-game god-character for player #1. We can't set location and home yet since nothing
# exists. Also, all properties (name, email, password, is_superuser)
# is inherited from the user so we don't specify it again here.
-
- god_player = create.create_player(god_user.username, None, None, user=god_user)
- god_character = create.create_object(character_typeclass, key=god_user.username)
+ character_typeclass = settings.BASE_CHARACTER_TYPECLASS
+ god_character = create.create_object(character_typeclass, key=god_player.username)
god_character.id = 1
god_character.db.desc = _('This is User #1.')
@@ -121,7 +136,7 @@ def create_channels():
return
# connect the god user to all these channels by default.
- goduser = get_god_user()
+ goduser = get_god_player()
from src.comms.models import PlayerChannelConnection
PlayerChannelConnection.objects.create_connection(goduser, pchan)
PlayerChannelConnection.objects.create_connection(goduser, ichan)
diff --git a/src/server/serversession.py b/src/server/serversession.py
index 49493311f8..24ba73cae1 100644
--- a/src/server/serversession.py
+++ b/src/server/serversession.py
@@ -77,17 +77,16 @@ class ServerSession(Session):
player - the player associated with the session
"""
self.player = player
- self.user = player.user
- self.uid = self.user.id
- self.uname = self.user.username
+ self.uid = self.player.id
+ self.uname = self.player.username
self.logged_in = True
self.conn_time = time.time()
self.puid = None
self.puppet = None
# Update account's last login time.
- self.user.last_login = datetime.now()
- self.user.save()
+ self.player.last_login = datetime.now()
+ self.player.save()
def at_disconnect(self):
"""
@@ -97,7 +96,7 @@ class ServerSession(Session):
sessid = self.sessid
player = self.player
_GA(player.dbobj, "unpuppet_object")(sessid)
- uaccount = _GA(player.dbobj, "user")
+ uaccount = player.dbobj
uaccount.last_login = datetime.now()
uaccount.save()
# calling player hook
diff --git a/src/settings_default.py b/src/settings_default.py
index 86bd81da09..f54b2bc332 100644
--- a/src/settings_default.py
+++ b/src/settings_default.py
@@ -134,7 +134,6 @@ GAME_CACHE_TYPE = "local"
# memory. So every now and then Evennia checks the size of this cache and resets
# it if it's too big. This variable sets the maximum size (in MB).
ATTRIBUTE_CACHE_MAXSIZE = 100
-# OOB (Out-of-band
######################################################################
# Evennia Database config
@@ -487,11 +486,12 @@ INSTALLED_APPS = (
'src.comms',
'src.help',
'src.scripts',
- 'src.web.news',
+ #'src.web.news',
'src.web.website',)
# The user profile extends the User object with more functionality;
# This should usually not be changed.
-AUTH_PROFILE_MODULE = "players.PlayerDB"
+AUTH_USER_MODEL = "players.PlayerDB"
+#AUTH_PROFILE_MODULE = "players.PlayerDB"
# Use a custom test runner that just tests Evennia-specific apps.
TEST_RUNNER = 'src.utils.test_utils.EvenniaTestSuiteRunner'
diff --git a/src/utils/create.py b/src/utils/create.py
index 0eae842304..32e07dfa7c 100644
--- a/src/utils/create.py
+++ b/src/utils/create.py
@@ -22,7 +22,6 @@ Models covered:
Players
"""
from django.conf import settings
-from django.contrib.auth.models import User
from django.db import IntegrityError
from src.utils.idmapper.models import SharedMemoryModel
from src.utils import utils, logger
@@ -387,49 +386,32 @@ channel = create_channel
# Player creation methods
#
-def create_player(name, email, password,
- user=None,
+def create_player(key, email, password,
typeclass=None,
is_superuser=False,
locks=None, permissions=None,
- player_dbobj=None, report_to=None):
+ report_to=None):
"""
- This creates a new player, handling the creation of the User
- object and its associated Player object.
+ This creates a new player.
- If player_dbobj is given, this player object is used instead of
- creating a new one. This is called by the admin interface since it
- needs to create the player object in order to relate it automatically
- to the user.
+ key - the player's name. This should be unique.
+ email - email on valid addr@addr.domain form.
+ password - password in cleartext
+ is_superuser - wether or not this player is to be a superuser
+ locks - lockstring
+ permission - list of permissions
+ report_to - an object with a msg() method to report errors to. If
+ not given, errors will be logged.
- If create_character is
- True, a game player object with the same name as the User/Player will
- also be created. Its typeclass and base properties can also be given.
-
- Returns the new game character, or the Player obj if no
- character is created. For more info about the typeclass argument,
- see create_objects() above.
-
- Note: if user is supplied, it will NOT be modified (args name, email,
- passw and is_superuser will be ignored). Change those properties
- directly on the User instead.
-
- If no permissions are given (None), the default permission group
- as defined in settings.PERMISSION_PLAYER_DEFAULT will be
- assigned. If permissions are given, no automatic assignment will
- occur.
+ Will return the Player-typeclass or None/raise Exception if the
+ Typeclass given failed to load.
Concerning is_superuser:
- A superuser should have access to everything
- in the game and on the server/web interface. The very first user
- created in the database is always a superuser (that's using
- django's own creation, not this one).
Usually only the server admin should need to be superuser, all
other access levels can be handled with more fine-grained
- permissions or groups.
- Since superuser overrules all permissions, we don't
- set any in this case.
+ permissions or groups. A superuser bypasses all lock checking
+ operations and is thus not suitable for play-testing the game.
"""
global _PlayerDB, _Player
@@ -440,48 +422,28 @@ def create_player(name, email, password,
if not email:
email = "dummy@dummy.com"
- if user:
- new_user = user
- email = user.email
-
- if user:
- conflict_check = User.objects.filter(username__iexact=user.username)
- conflict_check = len(conflict_check) > 1
- else:
- conflict_check = User.objects.filter(username__iexact=name)
-
- if conflict_check:
- raise ValueError("A user with this name already exists.")
-
- if not user:
- if is_superuser:
- new_user = User.objects.create_superuser(name, email, password)
- else:
- new_user = User.objects.create_user(name, email, password)
+ if _PlayerDB.objects.filter(username__iexact=key):
+ raise ValueError("A Player with this name already exists.")
try:
+
+ # create the correct Player object
+ if is_superuser:
+ new_db_player = _PlayerDB.objects.create_superuser(key, email, password)
+ else:
+ new_db_player = _PlayerDB.objects.create_user(key, email, password)
+
if not typeclass:
typeclass = settings.BASE_PLAYER_TYPECLASS
elif isinstance(typeclass, _PlayerDB):
- # this is already an objectdb instance, extract its typeclass
+ # this is an PlayerDB instance, extract its typeclass path
typeclass = typeclass.typeclass.path
elif isinstance(typeclass, _Player) or utils.inherits_from(typeclass, _Player):
- # this is already an object typeclass, extract its path
+ # this is Player object typeclass, extract its path
typeclass = typeclass.path
- if player_dbobj:
- try:
- _GA(player_dbobj, "dbobj")
- new_db_player = player_dbobj.dbobj
- except AttributeError:
- new_db_player = player_dbobj
- # use the typeclass from this object
- typeclass = new_db_player.typeclass_path
- else:
- new_user = User.objects.get(username=new_user.username)
- new_db_player = _PlayerDB(db_key=name, user=new_user)
- new_db_player.save()
- # assign the typeclass
- typeclass = utils.to_unicode(typeclass)
- new_db_player.typeclass_path = typeclass
+
+ # assign the typeclass
+ typeclass = utils.to_unicode(typeclass)
+ new_db_player.typeclass_path = typeclass
# this will either load the typeclass or the default one
new_player = new_db_player.typeclass
@@ -500,34 +462,27 @@ def create_player(name, email, password,
# call hook method (may override default permissions)
new_player.at_player_creation()
- print
# custom given arguments potentially overrides the hook
if permissions:
new_player.permissions = permissions
elif not new_player.permissions:
new_player.permissions = settings.PERMISSION_PLAYER_DEFAULT
-
if locks:
new_player.locks.add(locks)
-
return new_player
+
except Exception:
- # a failure in creating the character
- if not user:
- # in there was a failure we clean up everything we can
- logger.log_trace()
- try:
- new_user.delete()
- except Exception:
- pass
- try:
- new_player.delete()
- except Exception:
- pass
- try:
- del new_player
- except Exception:
- pass
+ # a failure in creating the player; we try to clean
+ # up as much as we can
+ logger.log_trace()
+ try:
+ new_player.delete()
+ except Exception:
+ pass
+ try:
+ del new_player
+ except Exception:
+ pass
raise
# alias
diff --git a/src/utils/search.py b/src/utils/search.py
index a7abbc2354..032c6241d1 100644
--- a/src/utils/search.py
+++ b/src/utils/search.py
@@ -16,11 +16,11 @@ the database model and call its 'objects' property.
Also remember that all commands in this file return lists (also if
there is only one match) unless noted otherwise.
-Example: To reach the search method 'get_object_with_user'
+Example: To reach the search method 'get_object_with_player'
in src/objects/managers.py:
> from src.objects.models import ObjectDB
-> match = Object.objects.get_object_with_user(...)
+> match = Object.objects.get_object_with_player(...)
"""
diff --git a/src/web/news/models.py b/src/web/news/models.py
index 02819644f6..9ea8f54953 100755
--- a/src/web/news/models.py
+++ b/src/web/news/models.py
@@ -1,7 +1,7 @@
#
# This module implements a simple news entry system
-# for the evennia website. One needs to use the
-# admin interface to add/edit/delete entries.
+# for the evennia website. One needs to use the
+# admin interface to add/edit/delete entries.
#
from django.db import models
@@ -13,8 +13,8 @@ class NewsTopic(models.Model):
"""
name = models.CharField(max_length=75, unique=True)
description = models.TextField(blank=True)
- icon = models.ImageField(upload_to='newstopic_icons',
- default='newstopic_icons/default.png',
+ icon = models.ImageField(upload_to='newstopic_icons',
+ default='newstopic_icons/default.png',
blank=True, help_text="Image for the news topic.")
def __str__(self):
@@ -35,7 +35,7 @@ class NewsEntry(models.Model):
body = models.TextField()
topic = models.ForeignKey(NewsTopic, related_name='newstopic')
date_posted = models.DateTimeField(auto_now_add=True)
-
+
def __str__(self):
return self.title