Working implementation of User->PlayerDB conversion. Superuser must be created separately. The player-create method was corrected, along with the most obvious places where the user-setup was used. One can log in and look around but it's not heavily debugged yet.

This commit is contained in:
Griatch 2013-07-11 18:03:07 +02:00
parent c94472492a
commit 46d1c48a38
8 changed files with 81 additions and 196 deletions

View file

@ -4,7 +4,6 @@ Commands that are available from the connect screen.
import re import re
import traceback import traceback
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User
from src.players.models import PlayerDB from src.players.models import PlayerDB
from src.objects.models import ObjectDB from src.objects.models import ObjectDB
from src.server.models import ServerConfig from src.server.models import ServerConfig
@ -68,7 +67,7 @@ class CmdUnconnectedConnect(MuxCommand):
player = PlayerDB.objects.get_player_from_name(playername) player = PlayerDB.objects.get_player_from_name(playername)
pswd = None pswd = None
if player: if player:
pswd = player.user.check_password(password) pswd = player.check_password(password)
if not (player and pswd): if not (player and pswd):
# No playername or password match # No playername or password match
@ -142,7 +141,7 @@ class CmdUnconnectedCreate(MuxCommand):
return return
# strip excessive spaces in playername # strip excessive spaces in playername
playername = re.sub(r"\s+", " ", playername).strip() 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) # player already exists (we also ignore capitalization here)
session.msg("Sorry, there is already a player with the name '%s'." % playername) session.msg("Sorry, there is already a player with the name '%s'." % playername)
return return

View file

@ -34,7 +34,6 @@ class ObjectManager(TypedObjectManager):
get_dbref_range get_dbref_range
object_totals object_totals
typeclass_search typeclass_search
get_object_with_user
get_object_with_player get_object_with_player
get_objs_with_key_and_typeclass get_objs_with_key_and_typeclass
get_objs_with_attr get_objs_with_attr
@ -52,29 +51,8 @@ class ObjectManager(TypedObjectManager):
# ObjectManager Get methods # 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 @returns_typeclass
def get_object_with_player(self, ostring, exact=True, candidates=None): def get_object_with_player(self, ostring, exact=True, candidates=None):
""" """
@ -92,9 +70,9 @@ class ObjectManager(TypedObjectManager):
# not a dbref. Search by name. # 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() cand_restriction = candidates and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
if exact: 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 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: if candidates:
index_matches = string_partial_matching(ply_cands, ostring, ret_index=True) 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] return [obj for ind, obj in enumerate(make_iter(candidates)) if ind in index_matches]

View file

@ -141,6 +141,7 @@ class UserAdmin(BaseUserAdmin):
{'fields': ('username', 'password1', 'password2', 'email'), {'fields': ('username', 'password1', 'password2', 'email'),
'description':"<i>These account details are shared by the admin system and the game.</i>"},),) 'description':"<i>These account details are shared by the admin system and the game.</i>"},),)
# TODO! Remove User reference!
def save_formset(self, request, form, formset, change): def save_formset(self, request, form, formset, change):
"Run all hooks on the player object" "Run all hooks on the player object"
super(UserAdmin, self).save_formset(request, form, formset, change) super(UserAdmin, self).save_formset(request, form, formset, change)

View file

@ -3,8 +3,8 @@ The managers for the custom Player object and permissions.
""" """
import datetime import datetime
from functools import update_wrapper
from django.contrib.auth.models import UserManager from django.contrib.auth.models import UserManager
from functools import update_wrapper
from src.typeclasses.managers import returns_typeclass_list, returns_typeclass, TypedObjectManager from src.typeclasses.managers import returns_typeclass_list, returns_typeclass, TypedObjectManager
from src.utils import logger from src.utils import logger
__all__ = ("PlayerManager",) __all__ = ("PlayerManager",)
@ -13,53 +13,6 @@ __all__ = ("PlayerManager",)
# Player Manager # 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, UserManager): class PlayerManager(TypedObjectManager, UserManager):
""" """
This PlayerManager implements methods for searching This PlayerManager implements methods for searching
@ -87,7 +40,7 @@ class PlayerManager(TypedObjectManager, UserManager):
""" """
def num_total_players(self): def num_total_players(self):
""" """
Returns the total number of registered users/players. Returns the total number of registered players.
""" """
return self.count() return self.count()
@ -99,7 +52,6 @@ class PlayerManager(TypedObjectManager, UserManager):
return self.filter(db_is_connected=True) return self.filter(db_is_connected=True)
@returns_typeclass_list @returns_typeclass_list
@returns_player_list
def get_recently_created_players(self, days=7): def get_recently_created_players(self, days=7):
""" """
Returns a QuerySet containing the player User accounts that have been Returns a QuerySet containing the player User accounts that have been
@ -108,13 +60,12 @@ class PlayerManager(TypedObjectManager, UserManager):
end_date = datetime.datetime.now() end_date = datetime.datetime.now()
tdelta = datetime.timedelta(days) tdelta = datetime.timedelta(days)
start_date = end_date - tdelta 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_typeclass_list
@returns_player_list
def get_recently_connected_players(self, days=7): 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. connected within the last <days> days.
days - number of days backwards to check days - number of days backwards to check
@ -122,33 +73,31 @@ class PlayerManager(TypedObjectManager, UserManager):
end_date = datetime.datetime.now() end_date = datetime.datetime.now()
tdelta = datetime.timedelta(days) tdelta = datetime.timedelta(days)
start_date = end_date - tdelta 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') start_date, end_date)).order_by('-last_login')
@returns_typeclass @returns_typeclass
@returns_player
def get_player_from_email(self, uemail): def get_player_from_email(self, uemail):
""" """
Returns a player object when given an email address. 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_typeclass
@returns_player
def get_player_from_uid(self, uid): def get_player_from_uid(self, uid):
""" """
Returns a player object based on User id. Returns a player object based on User id.
""" """
try: try:
return User.objects.get(id=uid) return self.get(id=uid)
except User.model.DoesNotExist: except self.model.DoesNotExist:
return None return None
@returns_typeclass @returns_typeclass
def get_player_from_name(self, uname): def get_player_from_name(self, uname):
"Get player object based on name" "Get player object based on name"
try: try:
return self.get(user__username__iexact=uname) return self.get(username__iexact=uname)
except self.model.DoesNotExist: except self.model.DoesNotExist:
return None return None
@ -165,7 +114,7 @@ class PlayerManager(TypedObjectManager, UserManager):
matches = self.filter(id=dbref) matches = self.filter(id=dbref)
if matches: if matches:
return 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): def swap_character(self, player, new_character, delete_old_character=False):
""" """

View file

@ -25,20 +25,20 @@ def create_config_values():
ServerConfig.objects.conf("site_name", settings.SERVERNAME) ServerConfig.objects.conf("site_name", settings.SERVERNAME)
ServerConfig.objects.conf("idle_timeout", settings.IDLE_TIMEOUT) ServerConfig.objects.conf("idle_timeout", settings.IDLE_TIMEOUT)
def get_god_user(): def get_god_player():
""" """
Creates the god user. Creates the god user.
""" """
PlayerDB = get_user_model() PlayerDB = get_user_model()
try: try:
god_user = PlayerDB.objects.get(id=1) god_player = PlayerDB.objects.get(id=1)
except PlayerDB.DoesNotExist: except PlayerDB.DoesNotExist:
txt = "\n\nNo superuser exists yet. The superuser is the 'owner' account on the" txt = "\n\nNo superuser exists yet. The superuser is the 'owner' account on the"
txt += "\nEvennia server; a good safety fallback. Create a new superuser using the command" txt += "\nEvennia server. Create a new superuser using the command"
txt += "\n\n python manage.py createsuperuser" txt += "\n\n python manage.py createsuperuser"
txt += "\n\nFollow the prompts, then restart the server." txt += "\n\nFollow the prompts, then restart the server."
raise Exception(txt) raise Exception(txt)
return god_user return god_player
def create_objects(): def create_objects():
""" """
@ -49,22 +49,26 @@ def create_objects():
# Set the initial User's account object's username on the #1 object. # 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. # 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 # Create a Player 'user profile' object to hold eventual
# mud-specific settings for the bog standard User object. This is # mud-specific settings for the bog standard User object. This is
# accessed by user.get_profile() and can also store attributes. # accessed by user.get_profile() and can also store attributes.
# It also holds mud permissions, but for a superuser these # It also holds mud permissions, but for a superuser these
# have no effect anyhow. # 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 # run all creation hooks on god_player (we must do so manually since the manage.py command does not)
# for user #1. We can't set location and home yet since nothing 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) # exists. Also, all properties (name, email, password, is_superuser)
# is inherited from the user so we don't specify it again here. # is inherited from the user so we don't specify it again here.
character_typeclass = settings.BASE_CHARACTER_TYPECLASS
god_player = create.create_player(god_user.username, None, None, user=god_user) god_character = create.create_object(character_typeclass, key=god_player.username)
god_character = create.create_object(character_typeclass, key=god_user.username)
god_character.id = 1 god_character.id = 1
god_character.db.desc = _('This is User #1.') god_character.db.desc = _('This is User #1.')
@ -132,7 +136,7 @@ def create_channels():
return return
# connect the god user to all these channels by default. # connect the god user to all these channels by default.
goduser = get_god_user() goduser = get_god_player()
from src.comms.models import PlayerChannelConnection from src.comms.models import PlayerChannelConnection
PlayerChannelConnection.objects.create_connection(goduser, pchan) PlayerChannelConnection.objects.create_connection(goduser, pchan)
PlayerChannelConnection.objects.create_connection(goduser, ichan) PlayerChannelConnection.objects.create_connection(goduser, ichan)

View file

@ -77,17 +77,16 @@ class ServerSession(Session):
player - the player associated with the session player - the player associated with the session
""" """
self.player = player self.player = player
self.user = player.user self.uid = self.player.id
self.uid = self.user.id self.uname = self.player.username
self.uname = self.user.username
self.logged_in = True self.logged_in = True
self.conn_time = time.time() self.conn_time = time.time()
self.puid = None self.puid = None
self.puppet = None self.puppet = None
# Update account's last login time. # Update account's last login time.
self.user.last_login = datetime.now() self.player.last_login = datetime.now()
self.user.save() self.player.save()
def at_disconnect(self): def at_disconnect(self):
""" """
@ -97,7 +96,7 @@ class ServerSession(Session):
sessid = self.sessid sessid = self.sessid
player = self.player player = self.player
_GA(player.dbobj, "unpuppet_object")(sessid) _GA(player.dbobj, "unpuppet_object")(sessid)
uaccount = _GA(player.dbobj, "user") uaccount = player.dbobj
uaccount.last_login = datetime.now() uaccount.last_login = datetime.now()
uaccount.save() uaccount.save()
# calling player hook # calling player hook

View file

@ -22,7 +22,6 @@ Models covered:
Players Players
""" """
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User
from django.db import IntegrityError from django.db import IntegrityError
from src.utils.idmapper.models import SharedMemoryModel from src.utils.idmapper.models import SharedMemoryModel
from src.utils import utils, logger from src.utils import utils, logger
@ -387,49 +386,32 @@ channel = create_channel
# Player creation methods # Player creation methods
# #
def create_player(name, email, password, def create_player(key, email, password,
user=None,
typeclass=None, typeclass=None,
is_superuser=False, is_superuser=False,
locks=None, permissions=None, locks=None, permissions=None,
player_dbobj=None, report_to=None): report_to=None):
""" """
This creates a new player, handling the creation of the User This creates a new player.
object and its associated Player object.
If player_dbobj is given, this player object is used instead of key - the player's name. This should be unique.
creating a new one. This is called by the admin interface since it email - email on valid addr@addr.domain form.
needs to create the player object in order to relate it automatically password - password in cleartext
to the user. 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 Will return the Player-typeclass or None/raise Exception if the
True, a game player object with the same name as the User/Player will Typeclass given failed to load.
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.
Concerning is_superuser: 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 Usually only the server admin should need to be superuser, all
other access levels can be handled with more fine-grained other access levels can be handled with more fine-grained
permissions or groups. permissions or groups. A superuser bypasses all lock checking
Since superuser overrules all permissions, we don't operations and is thus not suitable for play-testing the game.
set any in this case.
""" """
global _PlayerDB, _Player global _PlayerDB, _Player
@ -440,48 +422,28 @@ def create_player(name, email, password,
if not email: if not email:
email = "dummy@dummy.com" email = "dummy@dummy.com"
if user: if _PlayerDB.objects.filter(username__iexact=key):
new_user = user raise ValueError("A Player with this name already exists.")
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)
try: 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: if not typeclass:
typeclass = settings.BASE_PLAYER_TYPECLASS typeclass = settings.BASE_PLAYER_TYPECLASS
elif isinstance(typeclass, _PlayerDB): 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 typeclass = typeclass.typeclass.path
elif isinstance(typeclass, _Player) or utils.inherits_from(typeclass, _Player): 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 typeclass = typeclass.path
if player_dbobj:
try: # assign the typeclass
_GA(player_dbobj, "dbobj") typeclass = utils.to_unicode(typeclass)
new_db_player = player_dbobj.dbobj new_db_player.typeclass_path = typeclass
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
# this will either load the typeclass or the default one # this will either load the typeclass or the default one
new_player = new_db_player.typeclass new_player = new_db_player.typeclass
@ -500,34 +462,27 @@ def create_player(name, email, password,
# call hook method (may override default permissions) # call hook method (may override default permissions)
new_player.at_player_creation() new_player.at_player_creation()
print
# custom given arguments potentially overrides the hook # custom given arguments potentially overrides the hook
if permissions: if permissions:
new_player.permissions = permissions new_player.permissions = permissions
elif not new_player.permissions: elif not new_player.permissions:
new_player.permissions = settings.PERMISSION_PLAYER_DEFAULT new_player.permissions = settings.PERMISSION_PLAYER_DEFAULT
if locks: if locks:
new_player.locks.add(locks) new_player.locks.add(locks)
return new_player return new_player
except Exception: except Exception:
# a failure in creating the character # a failure in creating the player; we try to clean
if not user: # up as much as we can
# in there was a failure we clean up everything we can logger.log_trace()
logger.log_trace() try:
try: new_player.delete()
new_user.delete() except Exception:
except Exception: pass
pass try:
try: del new_player
new_player.delete() except Exception:
except Exception: pass
pass
try:
del new_player
except Exception:
pass
raise raise
# alias # alias

View file

@ -16,11 +16,11 @@ the database model and call its 'objects' property.
Also remember that all commands in this file return lists (also if Also remember that all commands in this file return lists (also if
there is only one match) unless noted otherwise. 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: in src/objects/managers.py:
> from src.objects.models import ObjectDB > from src.objects.models import ObjectDB
> match = Object.objects.get_object_with_user(...) > match = Object.objects.get_object_with_player(...)
""" """