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 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

View file

@ -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]

View file

@ -141,6 +141,7 @@ class UserAdmin(BaseUserAdmin):
{'fields': ('username', 'password1', 'password2', 'email'),
'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):
"Run all hooks on the player object"
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
from functools import update_wrapper
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.utils import logger
__all__ = ("PlayerManager",)
@ -13,53 +13,6 @@ __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, UserManager):
"""
This PlayerManager implements methods for searching
@ -87,7 +40,7 @@ class PlayerManager(TypedObjectManager, UserManager):
"""
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, UserManager):
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, UserManager):
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.
days - number of days backwards to check
@ -122,33 +73,31 @@ class PlayerManager(TypedObjectManager, UserManager):
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, UserManager):
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):
"""

View file

@ -25,20 +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():
"""
Creates the god user.
"""
PlayerDB = get_user_model()
try:
god_user = PlayerDB.objects.get(id=1)
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; 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\nFollow the prompts, then restart the server."
raise Exception(txt)
return god_user
return god_player
def create_objects():
"""
@ -49,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.')
@ -132,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)

View file

@ -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

View file

@ -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

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
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(...)
"""