Trunk: Merged the Devel-branch (branches/griatch) into /trunk. This constitutes a major refactoring of Evennia. Development will now continue in trunk. See the wiki and the past posts to the mailing list for info. /Griatch

This commit is contained in:
Griatch 2010-08-29 18:46:58 +00:00
parent df29defbcd
commit f83c2bddf8
222 changed files with 22304 additions and 14371 deletions

0
src/players/__init__.py Normal file
View file

29
src/players/admin.py Normal file
View file

@ -0,0 +1,29 @@
#
# This sets up how models are displayed
# in the web admin interface.
#
from src.players.models import PlayerDB, PlayerAttribute
from django.contrib import admin
class PlayerAttributeAdmin(admin.ModelAdmin):
list_display = ('id', 'db_key', 'db_value', 'db_mode', 'db_obj', 'db_permissions')
list_display_links = ("id", 'db_key')
ordering = ["db_obj", 'db_key']
readonly_fields = ['db_permissions']
search_fields = ['id', 'db_key', 'db_obj']
save_as = True
save_on_top = True
list_select_related = True
admin.site.register(PlayerAttribute, PlayerAttributeAdmin)
class PlayerDBAdmin(admin.ModelAdmin):
list_display = ('id', 'user', 'db_obj', 'db_typeclass_path')
list_display_links = ('id', 'user')
ordering = ['id', 'user']
readonly_fields = ['db_permissions']
search_fields = ['^db_key', 'db_typeclass_path']
save_as = True
save_on_top = True
list_select_related = True
admin.site.register(PlayerDB, PlayerDBAdmin)

147
src/players/manager.py Normal file
View file

@ -0,0 +1,147 @@
"""
The managers for the custom Player object and permissions.
"""
import datetime
from django.db import models
from django.contrib.auth.models import User
from src.typeclasses.managers import returns_typeclass_list, returns_typeclass
#
# 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:
players.append(user)
return players
return func
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 func
class PlayerManager(models.Manager):
"""
Custom manager for the player profile model. We use this
to wrap users in general in evennia, and supply some useful
search/statistics methods.
"""
def num_total_players(self):
"""
Returns the total number of registered users/players.
"""
return self.count()
@returns_typeclass_list
def get_connected_players(self):
"""
Returns a list of player objects with currently connected users/players.
"""
return [player for player in self.all() if player.sessions]
@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
connected within the last <days> days.
"""
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))
@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
connected within the last <days> days.
"""
end_date = datetime.datetime.now()
tdelta = datetime.timedelta(days)
start_date = end_date - tdelta
return User.objects.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)
@returns_typeclass
def get_player_from_name(self, uname):
"Get player object based on name"
players = self.filter(user__username=uname)
if players:
return players[0]
return None
# @returns_typeclass_list
# def get_players_with_perm(self, permstring):
# """
# Returns all players having access according to the given
# permission string.
# """
# return [player for player in self.all()
# if player.has_perm(permstring)]
# @returns_typeclass_list
# def get_players_with_group(self, groupstring):
# """
# Returns all players belonging to the given group.
# """
# return [player.user for player in self.all()
# if player.has_group(groupstring)]
@returns_typeclass_list
def player_search(self, ostring):
"""
Searches for a particular player by name or
database id.
ostring = a string or database id.
"""
players = []
try:
# try dbref match
dbref = int(ostring.strip('#'))
players = self.filter(id=dbref)
except Exception:
pass
if not players:
players = self.filter(user__username=ostring)
return players

278
src/players/models.py Normal file
View file

@ -0,0 +1,278 @@
"""
Player
The Player class is a simple extension of the django User model using
the 'profile' system of django. A profile is a model that tack new
fields to the User model without actually editing the User model
(which would mean hacking into django internals which we want to avoid
for future compatability reasons). The profile, which we call
'Player', is accessed with user.get_profile() by the property 'player'
defined on ObjectDB objects. Since we can customize it, we will try to
abstract as many operations as possible to work on Player rather than
on User.
We use the Player to store a more mud-friendly style of permission
system as well as to allow the admin more flexibility by storing
attributes on the Player. Within the game we should normally use the
Player manager's methods to create users, since that automatically
adds the profile extension.
The default Django permission system is geared towards web-use, and is
defined on a per-application basis permissions. In django terms,
'src/objects' is one application, 'src/scripts' another, indeed all
folders in /src with a model.py inside them is an application. Django
permissions thus have the form
e.g. 'applicationlabel.permissionstring' and django automatically
defines a set of these for editing each application from its automatic
admin interface. These are still available should you want them.
For most in-game mud-use however, like commands and other things, it
does not make sense to tie permissions to the applications in src/ -
To the user these should all just be considered part of the game
engine. So instead we define our own separate permission system here,
borrowing heavily from the django original, but allowing the
permission string to look however we want, making them unrelated to
the applications.
To make the Player model more flexible for your own game, it can also
persistently store attributes of its own. This is ideal for extra
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.utils.encoding import smart_str
from src.server import sessionhandler
from src.players import manager
from src.typeclasses.models import Attribute, TypedObject
from src.permissions import permissions
from src.utils.ansi import parse_ansi
from src.utils import logger
#------------------------------------------------------------
#
# PlayerAttribute
#
#------------------------------------------------------------
class PlayerAttribute(Attribute):
"""
PlayerAttributes work the same way as Attributes on game objects,
but are intended to store OOC information specific to each user
and game (example would be configurations etc).
"""
db_obj = models.ForeignKey("PlayerDB")
class Meta:
"Define Django meta options"
verbose_name = "Player Attribute"
verbose_name_plural = "Player Attributes"
#------------------------------------------------------------
#
# PlayerDB
#
#------------------------------------------------------------
class PlayerDB(TypedObject):
"""
This is a special model using Django's 'profile' functionality
and extends the default Django User model. It is defined as such
by use of the variable AUTH_PROFILE_MODULE in the settings.
One accesses the fields/methods. We try use this model as much
as possible rather than User, since we can customize this to
our liking.
The TypedObject supplies the following (inherited) properties:
key - main name
name - alias for key
typeclass_path - the path to the decorating typeclass
typeclass - auto-linked typeclass
date_created - time stamp of object creation
permissions - perm strings
dbref - #id of object
db - persistent attribute storage
ndb - non-persistent attribute storage
The PlayerDB adds the following properties:
user - Connected User object. django field, needs to be save():d.
obj - game object controlled by player
character - alias for obj
name - alias for user.username
sessions - sessions connected to this player
is_superuser - bool if this player is a superuser
"""
#
# PlayerDB Database model setup
#
# inherited fields (from TypedObject):
# db_key, db_typeclass_path, db_date_created, db_permissions
# 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)
# the in-game object connected to this player (if any).
# Use the property 'obj' to access.
db_obj = models.ForeignKey("objects.ObjectDB", null=True)
# Database manager
objects = manager.PlayerManager()
class Meta:
app_label = 'players'
# Wrapper properties to easily set database fields. These are
# @property decorators that allows to access these fields using
# normal python operations (without having to remember to save()
# etc). So e.g. a property 'attr' has a get/set/del decorator
# defined that allows the user to do self.attr = value,
# value = self.attr and del self.attr respectively (where self
# is the object in question).
# obj property (wraps db_obj)
#@property
def obj_get(self):
"Getter. Allows for value = self.obj"
return self.db_obj
#@obj.setter
def obj_set(self, value):
"Setter. Allows for self.obj = value"
from src.typeclasses.typeclass import TypeClass
if isinstance(value, TypeClass):
value = value.dbobj
try:
self.db_obj = value
self.save()
except Exception:
logger.log_trace()
raise Exception("Cannot assign %s as a player object!" % value)
#@obj.deleter
def obj_del(self):
"Deleter. Allows for del self.obj"
self.db_obj = None
self.save()
obj = property(obj_get, obj_set, obj_del)
# whereas the name 'obj' is consistent with the rest of the code,
# 'character' is a more intuitive property name, so we
# define this too, as an alias to player.obj.
#@property
def character_get(self):
"Getter. Allows for value = self.character"
return self.obj
#@character.setter
def character_set(self, value):
"Setter. Allows for self.character = value"
self.obj = value
#@character.deleter
def character_del(self):
"Deleter. Allows for del self.character"
del self.obj
character = property(character_get, character_set, character_del)
class Meta:
"Define Django meta options"
verbose_name = "Player"
verbose_name_plural = "Players"
#
# PlayerDB main class properties and methods
#
def __str__(self):
return smart_str("%s(player %i)" % (self.name, self.id))
def __unicode__(self):
return u"%s(player#%i)" % (self.name, self.id)
# this is used by all typedobjects as a fallback
try:
default_typeclass_path = settings.BASE_PLAYER_TYPECLASS
except Exception:
default_typeclass_path = "src.players.player.Player"
# this is required to properly handle attributes
attribute_model_path = "src.players.models"
attribute_model_name = "PlayerAttribute"
# name property (wraps self.user.username)
#@property
def name_get(self):
"Getter. Allows for value = self.name"
return self.user.username
#@name.setter
def name_set(self, value):
"Setter. Allows for player.name = newname"
self.user.username = value
self.user.save() # this might be stopped by Django?
#@name.deleter
def name_del(self):
"Deleter. Allows for del self.name"
raise Exception("Player name cannot be deleted!")
name = property(name_get, name_set, name_del)
key = property(name_get, name_set, name_del)
# sessions property (wraps sessionhandler)
#@property
def sessions_get(self):
"Getter. Retrieve sessions related to this player/user"
return sessionhandler.find_sessions_from_username(self.name)
#@sessions.setter
def sessions_set(self, value):
"Setter. Protects the sessions property from adding things"
raise Exception("Cannot set sessions manually!")
#@sessions.deleter
def sessions_del(self):
"Deleter. Protects the sessions property from deletion"
raise Exception("Cannot delete sessions manually!")
sessions = property(sessions_get, sessions_set, sessions_del)
#@property
def is_superuser_get(self):
"Superusers have all permissions."
return self.user.is_superuser
is_superuser = property(is_superuser_get)
def set_perm(self, perm):
"Shortcuts to set permissions, replacing old ones"
return permissions.set_perm(self, perm)
def add_perm(self, perm):
"Add permissions to the old ones"
return permissions.add_perm(self, perm)
def del_perm(self, perm):
"Delete permission from old ones"
return permissions.del_perm(self, perm)
#
# PlayerDB class access methods
#
def msg(self, message, from_obj=None):
"""
This duplicates the same-named method on the Character.
It forwards messages to the character or uses
the session messaging directly.
"""
if self.character:
self.character.msg(message, from_obj)
else:
if from_obj:
try:
from_obj.at_msg_send(message, self)
except Exception:
pass
if self.at_msg_receive(message, from_obj):
for session in self.sessions:
session.msg(parse_ansi(message))
def emit_to(self, message, from_obj=None):
"""
Deprecated. Use msg instead.
"""
self.msg(message, from_obj)

79
src/players/player.py Normal file
View file

@ -0,0 +1,79 @@
"""
Typeclass for Player objects
Note that this object is primarily intended to
store OOC information, not game info! This
object represents the actual user (not their
character) and has NO actual precence in the
game world (this is handled by the associated
character object, so you should customize that
instead for most things).
"""
from src.typeclasses.typeclass import TypeClass
class Player(TypeClass):
"""
Base typeclass for all Players.
"""
def at_player_creation(self):
"""
This is called once, the very first time
the player is created (i.e. first time they
register with the game). It's a good place
to store attributes all players should have,
like configuration values etc.
"""
pass
# Note that the hooks below also exist
# in the character object's typeclass. You
# can often ignore these and rely on the
# character ones instead, unless you
# are implementing a multi-character game
# and have some things that should be done
# regardless of which character is currently
# connected to this player.
def at_first_login(self):
"""
Only called once, the very first
time the user logs in.
"""
pass
def at_pre_login(self):
"""
Called every time the user logs in,
before they are actually logged in.
"""
pass
def at_post_login(self):
"""
Called at the end of the login
process, just before letting
them loose.
"""
pass
def at_disconnect(self):
"""
Called just before user
is disconnected.
"""
pass
def at_message_receive(self, message, from_obj=None):
"""
Called when any text is emitted to this
object. If it returns False, no text
will be sent automatically.
"""
return True
def at_message_send(self, message, to_object):
"""
Called whenever this object tries to send text
to another object. Only called if the object supplied
itself as a sender in the msg() call.
"""
pass