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