mirror of
https://github.com/evennia/evennia.git
synced 2026-04-05 15:37:17 +02:00
Changed how the Typeclass system returns errors. Instead of echoing typeclass erros to the MUD-info channel (which is not only not only very spammy for everyone but also very hard to make clean so as to avoid recursion at a stage of typeclass failing), the system instead stores a property on itself called 'typeclass_last_errmsg' that holds eventual errors. This means that the task of reporting errors does not fall on the typeclass system itself but on the calling methods, as it should be. So src.utils.create.create_* functions now takes a new optional keyword "report_to" that holds an object to receive errors. If this keyword is given, the function msg():es that object with the error and returns None as before. If report_to is not set however, the create_* methods now return an Exception containing the error text. All default commands have been changed to accomodate for this behaviour, which allows for much more control over errors.
Also, the default ADMIN_MEDIA static files changed location in Django 1.4. The initial_setup function now accounts for this.
This commit is contained in:
parent
63329f5420
commit
8c3b49e704
15 changed files with 838 additions and 785 deletions
|
|
@ -13,12 +13,12 @@ shorter "create.object()".
|
|||
The respective object managers hold more methods for manipulating and
|
||||
searching objects already existing in the database.
|
||||
|
||||
Models covered:
|
||||
Models covered:
|
||||
Objects
|
||||
Scripts
|
||||
Help
|
||||
Message
|
||||
Channel
|
||||
Channel
|
||||
Players
|
||||
"""
|
||||
|
||||
|
|
@ -26,8 +26,7 @@ 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 logger, utils, idmapper
|
||||
from src.utils.utils import is_iter, has_parent, inherits_from
|
||||
from src.utils import utils, logger
|
||||
|
||||
# limit symbol import from API
|
||||
__all__ = ("create_object", "create_script", "create_help_entry", "create_message", "create_channel", "create_player")
|
||||
|
|
@ -35,23 +34,28 @@ __all__ = ("create_object", "create_script", "create_help_entry", "create_messag
|
|||
GA = object.__getattribute__
|
||||
|
||||
#
|
||||
# Game Object creation
|
||||
# Game Object creation
|
||||
#
|
||||
|
||||
|
||||
def create_object(typeclass, key=None, location=None,
|
||||
home=None, player=None, permissions=None, locks=None,
|
||||
aliases=None, destination=None):
|
||||
home=None, player=None, permissions=None, locks=None,
|
||||
aliases=None, destination=None, report_to=None):
|
||||
"""
|
||||
Create a new in-game object. Any game object is a combination
|
||||
of a database object that stores data persistently to
|
||||
the database, and a typeclass, which on-the-fly 'decorates'
|
||||
the database object into whataver different type of object
|
||||
it is supposed to be in the game.
|
||||
it is supposed to be in the game.
|
||||
|
||||
See src.objects.managers for methods to manipulate existing objects
|
||||
in the database. src.objects.objects holds the base typeclasses
|
||||
and src.objects.models hold the database model.
|
||||
and src.objects.models hold the database model.
|
||||
|
||||
report_to is an optional object for reporting errors to in string form.
|
||||
If report_to is not set, errors will be raised as en Exception
|
||||
containing the error message. If set, this method will return
|
||||
None upon errors.
|
||||
"""
|
||||
# deferred import to avoid loops
|
||||
from src.objects.objects import Object
|
||||
|
|
@ -64,32 +68,36 @@ def create_object(typeclass, key=None, location=None,
|
|||
typeclass = typeclass.typeclass.path
|
||||
elif isinstance(typeclass, Object) or utils.inherits_from(typeclass, Object):
|
||||
# this is already an object typeclass, extract its path
|
||||
typeclass = typeclass.path
|
||||
typeclass = typeclass.path
|
||||
|
||||
# create new database object
|
||||
# create new database object
|
||||
new_db_object = ObjectDB()
|
||||
|
||||
# assign the typeclass
|
||||
|
||||
# assign the typeclass
|
||||
typeclass = utils.to_unicode(typeclass)
|
||||
new_db_object.typeclass_path = typeclass
|
||||
|
||||
# the name/key is often set later in the typeclass. This
|
||||
# is set here as a failsafe.
|
||||
# is set here as a failsafe.
|
||||
if key:
|
||||
new_db_object.key = key
|
||||
new_db_object.key = key
|
||||
else:
|
||||
new_db_object.key = "#%i" % new_db_object.id
|
||||
|
||||
# this will either load the typeclass or the default one
|
||||
new_object = new_db_object.typeclass
|
||||
|
||||
|
||||
if not GA(new_db_object, "is_typeclass")(typeclass, exact=True):
|
||||
if not GA(new_object, "is_typeclass")(typeclass, exact=True):
|
||||
# this will fail if we gave a typeclass as input and it still gave us a default
|
||||
SharedMemoryModel.delete(new_db_object)
|
||||
return None
|
||||
if report_to:
|
||||
GA(report_to, "msg")("Error creating %s (%s):\n%s" % (new_db_object.key, typeclass,
|
||||
GA(new_db_object, "typeclass_last_errmsg")))
|
||||
return None
|
||||
else:
|
||||
raise Exception(GA(new_db_object, "typeclass_last_errmsg"))
|
||||
|
||||
# from now on we can use the typeclass object
|
||||
# from now on we can use the typeclass object
|
||||
# as if it was the database object.
|
||||
|
||||
if player:
|
||||
|
|
@ -97,14 +105,14 @@ def create_object(typeclass, key=None, location=None,
|
|||
new_object.player = player
|
||||
player.obj = new_object
|
||||
|
||||
new_object.destination = destination
|
||||
new_object.destination = destination
|
||||
|
||||
# call the hook method. This is where all at_creation
|
||||
# customization happens as the typeclass stores custom
|
||||
# things on its database object.
|
||||
# things on its database object.
|
||||
new_object.basetype_setup() # setup the basics of Exits, Characters etc.
|
||||
new_object.at_object_creation()
|
||||
|
||||
|
||||
# custom-given perms/locks overwrite hooks
|
||||
if permissions:
|
||||
new_object.permissions = permissions
|
||||
|
|
@ -119,15 +127,15 @@ def create_object(typeclass, key=None, location=None,
|
|||
else:
|
||||
new_object.home = settings.CHARACTER_DEFAULT_HOME
|
||||
|
||||
|
||||
|
||||
if location:
|
||||
new_object.move_to(location, quiet=True)
|
||||
else:
|
||||
# rooms would have location=None.
|
||||
new_object.location = None
|
||||
new_object.location = None
|
||||
|
||||
# post-hook setup (mainly used by Exits)
|
||||
new_object.basetype_posthook_setup()
|
||||
new_object.basetype_posthook_setup()
|
||||
|
||||
new_object.save()
|
||||
return new_object
|
||||
|
|
@ -136,12 +144,12 @@ def create_object(typeclass, key=None, location=None,
|
|||
object = create_object
|
||||
|
||||
#
|
||||
# Script creation
|
||||
# Script creation
|
||||
#
|
||||
|
||||
def create_script(typeclass, key=None, obj=None, locks=None,
|
||||
interval=None, start_delay=None, repeats=None,
|
||||
persistent=None, autostart=True):
|
||||
def create_script(typeclass, key=None, obj=None, locks=None,
|
||||
interval=None, start_delay=None, repeats=None,
|
||||
persistent=None, autostart=True, report_to=None):
|
||||
"""
|
||||
Create a new script. All scripts are a combination
|
||||
of a database object that communicates with the
|
||||
|
|
@ -149,42 +157,47 @@ def create_script(typeclass, key=None, obj=None, locks=None,
|
|||
database object into being different types of scripts.
|
||||
It's behaviour is similar to the game objects except
|
||||
scripts has a time component and are more limited in
|
||||
scope.
|
||||
scope.
|
||||
|
||||
Argument 'typeclass' can be either an actual
|
||||
typeclass object or a python path to such an object.
|
||||
Only set key here if you want a unique name for this
|
||||
particular script (set it in config to give
|
||||
same key to all scripts of the same type). Set obj
|
||||
to tie this script to a particular object.
|
||||
to tie this script to a particular object.
|
||||
|
||||
See src.scripts.manager for methods to manipulate existing
|
||||
scripts in the database.
|
||||
|
||||
report_to is an obtional object to receive error messages.
|
||||
If report_to is not set, an Exception with the
|
||||
error will be raised. If set, this method will
|
||||
return None upon errors.
|
||||
"""
|
||||
|
||||
# deferred import to avoid loops.
|
||||
from src.scripts.scripts import Script
|
||||
#print "in create_script", typeclass
|
||||
from src.scripts.models import ScriptDB
|
||||
#print "in create_script", typeclass
|
||||
from src.scripts.models import ScriptDB
|
||||
|
||||
if not typeclass:
|
||||
typeclass = settings.BASE_SCRIPT_TYPECLASS
|
||||
elif isinstance(typeclass, ScriptDB):
|
||||
# this is already an scriptdb instance, extract its typeclass
|
||||
typeclass = new_db_object.typeclass.path
|
||||
typeclass = typeclass.typeclass.path
|
||||
elif isinstance(typeclass, Script) or utils.inherits_from(typeclass, Script):
|
||||
# this is already an object typeclass, extract its path
|
||||
typeclass = typeclass.path
|
||||
typeclass = typeclass.path
|
||||
|
||||
# create new database script
|
||||
new_db_script = ScriptDB()
|
||||
new_db_script = ScriptDB()
|
||||
|
||||
# assign the typeclass
|
||||
# assign the typeclass
|
||||
typeclass = utils.to_unicode(typeclass)
|
||||
new_db_script.typeclass_path = typeclass
|
||||
|
||||
|
||||
# the name/key is often set later in the typeclass. This
|
||||
# is set here as a failsafe.
|
||||
# is set here as a failsafe.
|
||||
if key:
|
||||
new_db_script.key = key
|
||||
else:
|
||||
|
|
@ -195,15 +208,19 @@ def create_script(typeclass, key=None, obj=None, locks=None,
|
|||
|
||||
if not GA(new_db_script, "is_typeclass")(typeclass, exact=True):
|
||||
# this will fail if we gave a typeclass as input and it still gave us a default
|
||||
print "failure:", new_db_script, typeclass
|
||||
SharedMemoryModel.delete(new_db_script)
|
||||
return None
|
||||
if report_to:
|
||||
GA(report_to, "msg")("Error creating %s (%s): %s" % (new_db_script.key, typeclass,
|
||||
GA(new_db_script, "typeclass_last_errmsg")))
|
||||
return None
|
||||
else:
|
||||
raise Exception(GA(new_db_script, "typeclass_last_errmsg"))
|
||||
|
||||
if obj:
|
||||
try:
|
||||
new_script.obj = obj
|
||||
except ValueError:
|
||||
new_script.obj = obj.dbobj
|
||||
new_script.obj = obj.dbobj
|
||||
|
||||
# call the hook method. This is where all at_creation
|
||||
# customization happens as the typeclass stores custom
|
||||
|
|
@ -212,10 +229,10 @@ def create_script(typeclass, key=None, obj=None, locks=None,
|
|||
|
||||
# custom-given variables override the hook
|
||||
if key:
|
||||
new_script.key = key
|
||||
new_script.key = key
|
||||
if locks:
|
||||
new_script.locks.add(locks)
|
||||
if interval != None:
|
||||
if interval != None:
|
||||
new_script.interval = interval
|
||||
if start_delay != None:
|
||||
new_script.start_delay = start_delay
|
||||
|
|
@ -223,13 +240,13 @@ def create_script(typeclass, key=None, obj=None, locks=None,
|
|||
new_script.repeats = repeats
|
||||
if persistent != None:
|
||||
new_script.persistent = persistent
|
||||
|
||||
|
||||
# a new created script should usually be started.
|
||||
if autostart:
|
||||
new_script.start()
|
||||
|
||||
|
||||
new_db_script.save()
|
||||
return new_script
|
||||
return new_script
|
||||
#alias
|
||||
script = create_script
|
||||
|
||||
|
|
@ -243,7 +260,7 @@ def create_help_entry(key, entrytext, category="General", locks=None):
|
|||
help entries are dynamic and directly taken from the __doc__ entries
|
||||
of the command. The database-stored help entries are intended for more
|
||||
general help on the game, more extensive info, in-game setting information
|
||||
and so on.
|
||||
and so on.
|
||||
"""
|
||||
|
||||
from src.help.models import HelpEntry
|
||||
|
|
@ -255,19 +272,19 @@ def create_help_entry(key, entrytext, category="General", locks=None):
|
|||
if locks:
|
||||
new_help.locks.add(locks)
|
||||
new_help.save()
|
||||
return new_help
|
||||
return new_help
|
||||
except IntegrityError:
|
||||
string = "Could not add help entry: key '%s' already exists." % key
|
||||
logger.log_errmsg(string)
|
||||
return None
|
||||
except Exception:
|
||||
logger.log_trace()
|
||||
return None
|
||||
return None
|
||||
# alias
|
||||
help_entry = create_help_entry
|
||||
|
||||
#
|
||||
# Comm system methods
|
||||
# Comm system methods
|
||||
#
|
||||
|
||||
def create_message(senderobj, message, channels=None,
|
||||
|
|
@ -279,7 +296,7 @@ def create_message(senderobj, message, channels=None,
|
|||
senderobj - the player sending the message. This must be the actual object.
|
||||
message - text with the message. Eventual headers, titles etc
|
||||
should all be included in this text string. Formatting
|
||||
will be retained.
|
||||
will be retained.
|
||||
channels - a channel or a list of channels to send to. The channels
|
||||
may be actual channel objects or their unique key strings.
|
||||
receivers - a player to send to, or a list of them. May be Player objects
|
||||
|
|
@ -289,7 +306,7 @@ def create_message(senderobj, message, channels=None,
|
|||
The Comm system is created very open-ended, so it's fully possible
|
||||
to let a message both go to several channels and to several receivers
|
||||
at the same time, it's up to the command definitions to limit this as
|
||||
desired.
|
||||
desired.
|
||||
"""
|
||||
from src.comms.models import Msg
|
||||
from src.players.models import PlayerDB
|
||||
|
|
@ -304,10 +321,10 @@ def create_message(senderobj, message, channels=None,
|
|||
elif hasattr(obj, 'db_player'):
|
||||
return obj.db_player
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
if not message:
|
||||
# we don't allow empty messages.
|
||||
# we don't allow empty messages.
|
||||
return
|
||||
|
||||
new_message = Msg()
|
||||
|
|
@ -315,19 +332,19 @@ def create_message(senderobj, message, channels=None,
|
|||
new_message.message = message
|
||||
new_message.save()
|
||||
if channels:
|
||||
if not is_iter(channels):
|
||||
if not utils.is_iter(channels):
|
||||
channels = [channels]
|
||||
new_message.channels = [channel for channel in
|
||||
[to_object(channel, objtype='channel')
|
||||
for channel in channels] if channel]
|
||||
for channel in channels] if channel]
|
||||
if receivers:
|
||||
#print "Found receiver:", receivers
|
||||
if not is_iter(receivers):
|
||||
if not utils.is_iter(receivers):
|
||||
receivers = [receivers]
|
||||
#print "to_player: %s" % to_player(receivers[0])
|
||||
new_message.receivers = [to_player(receiver) for receiver in
|
||||
[to_object(receiver) for receiver in receivers]
|
||||
if receiver]
|
||||
if receiver]
|
||||
if locks:
|
||||
new_message.locks.add(locks)
|
||||
new_message.save()
|
||||
|
|
@ -343,20 +360,20 @@ def create_channel(key, aliases=None, desc=None,
|
|||
specifying the receivers explicitly. Instead players may
|
||||
'connect' to the channel and follow the flow of messages. By
|
||||
default the channel allows access to all old messages, but
|
||||
this can be turned off with the keep_log switch.
|
||||
this can be turned off with the keep_log switch.
|
||||
|
||||
key - this must be unique.
|
||||
key - this must be unique.
|
||||
aliases - list of alternative (likely shorter) keynames.
|
||||
locks - lock string definitions
|
||||
"""
|
||||
|
||||
from src.comms.models import Channel
|
||||
from src.comms import channelhandler
|
||||
from src.comms.models import Channel
|
||||
from src.comms import channelhandler
|
||||
try:
|
||||
new_channel = Channel()
|
||||
new_channel.key = key
|
||||
new_channel.key = key
|
||||
if aliases:
|
||||
if not is_iter(aliases):
|
||||
if not utils.is_iter(aliases):
|
||||
aliases = [aliases]
|
||||
new_channel.aliases = ",".join([alias for alias in aliases])
|
||||
new_channel.desc = desc
|
||||
|
|
@ -369,33 +386,33 @@ def create_channel(key, aliases=None, desc=None,
|
|||
new_channel.locks.add(locks)
|
||||
new_channel.save()
|
||||
channelhandler.CHANNELHANDLER.add_channel(new_channel)
|
||||
return new_channel
|
||||
return new_channel
|
||||
|
||||
channel = create_channel
|
||||
|
||||
channel = create_channel
|
||||
|
||||
#
|
||||
# Player creation methods
|
||||
# Player creation methods
|
||||
#
|
||||
|
||||
def create_player(name, email, password,
|
||||
user=None,
|
||||
typeclass=None,
|
||||
is_superuser=False,
|
||||
is_superuser=False,
|
||||
locks=None, permissions=None,
|
||||
create_character=True, character_typeclass=None,
|
||||
character_location=None, character_home=None,
|
||||
player_dbobj=None):
|
||||
player_dbobj=None, report_to=None):
|
||||
|
||||
|
||||
|
||||
"""
|
||||
This creates a new player, handling the creation of the User
|
||||
object and its associated Player object.
|
||||
object and its associated Player object.
|
||||
|
||||
If player_dbobj is given, this player object is used instead of
|
||||
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.
|
||||
|
||||
to the user.
|
||||
|
||||
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.
|
||||
|
|
@ -403,15 +420,15 @@ def create_player(name, email, password,
|
|||
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.
|
||||
|
||||
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
|
||||
as defined in settings.PERMISSION_PLAYER_DEFAULT will be
|
||||
assigned. If permissions are given, no automatic assignment will
|
||||
occur.
|
||||
occur.
|
||||
|
||||
Concerning is_superuser:
|
||||
A superuser should have access to everything
|
||||
|
|
@ -420,18 +437,18 @@ def create_player(name, email, password,
|
|||
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.
|
||||
permissions or groups.
|
||||
Since superuser overrules all permissions, we don't
|
||||
set any in this case.
|
||||
|
||||
|
||||
"""
|
||||
# The system should already have checked so the name/email
|
||||
# isn't already registered, and that the password is ok before
|
||||
# getting here.
|
||||
# getting here.
|
||||
|
||||
from src.players.models import PlayerDB
|
||||
from src.players.player import Player
|
||||
|
||||
|
||||
if not email:
|
||||
email = "dummy@dummy.com"
|
||||
if user:
|
||||
|
|
@ -441,7 +458,7 @@ def create_player(name, email, password,
|
|||
if is_superuser:
|
||||
new_user = User.objects.create_superuser(name, email, password)
|
||||
else:
|
||||
new_user = User.objects.create_user(name, email, password)
|
||||
new_user = User.objects.create_user(name, email, password)
|
||||
try:
|
||||
if not typeclass:
|
||||
typeclass = settings.BASE_PLAYER_TYPECLASS
|
||||
|
|
@ -450,7 +467,7 @@ def create_player(name, email, password,
|
|||
typeclass = typeclass.typeclass.path
|
||||
elif isinstance(typeclass, Player) or utils.inherits_from(typeclass, Player):
|
||||
# this is already an object typeclass, extract its path
|
||||
typeclass = typeclass.path
|
||||
typeclass = typeclass.path
|
||||
|
||||
if player_dbobj:
|
||||
new_db_player = player_dbobj
|
||||
|
|
@ -458,7 +475,7 @@ def create_player(name, email, password,
|
|||
new_db_player = PlayerDB(db_key=name, user=new_user)
|
||||
new_db_player.save()
|
||||
|
||||
# assign the typeclass
|
||||
# assign the typeclass
|
||||
typeclass = utils.to_unicode(typeclass)
|
||||
new_db_player.typeclass_path = typeclass
|
||||
|
||||
|
|
@ -468,13 +485,18 @@ def create_player(name, email, password,
|
|||
if not GA(new_db_player, "is_typeclass")(typeclass, exact=True):
|
||||
# this will fail if we gave a typeclass as input and it still gave us a default
|
||||
SharedMemoryModel.delete(new_db_player)
|
||||
return None
|
||||
if report_to:
|
||||
GA(report_to, "msg")("Error creating %s (%s):\n%s" % (new_db_player.key, typeclass,
|
||||
GA(new_db_player, "typeclass_last_errmsg")))
|
||||
return None
|
||||
else:
|
||||
raise Exception(GA(new_db_player, "typeclass_last_errmsg"))
|
||||
|
||||
new_player.basetype_setup() # setup the basic locks and cmdset
|
||||
# call hook method (may override default permissions)
|
||||
new_player.at_player_creation()
|
||||
|
||||
# custom given arguments potentially overrides the hook
|
||||
# custom given arguments potentially overrides the hook
|
||||
if permissions:
|
||||
new_player.permissions = permissions
|
||||
elif not new_player.permissions:
|
||||
|
|
@ -483,19 +505,19 @@ def create_player(name, email, password,
|
|||
if locks:
|
||||
new_player.locks.add(locks)
|
||||
|
||||
# create *in-game* 'player' object
|
||||
# create *in-game* 'player' object
|
||||
if create_character:
|
||||
if not character_typeclass:
|
||||
character_typeclass = settings.BASE_CHARACTER_TYPECLASS
|
||||
# creating the object automatically links the player
|
||||
# and object together by player.obj <-> obj.player
|
||||
new_character = create_object(character_typeclass, key=name,
|
||||
location=None, home=character_location,
|
||||
location=None, home=character_location,
|
||||
permissions=permissions,
|
||||
player=new_player)
|
||||
player=new_player, report_to=report_to)
|
||||
return new_character
|
||||
return new_player
|
||||
except Exception,e:
|
||||
except Exception:
|
||||
# a failure in creating the character
|
||||
if not user:
|
||||
# in there was a failure we clean up everything we can
|
||||
|
|
@ -507,12 +529,12 @@ def create_player(name, email, password,
|
|||
try:
|
||||
new_player.delete()
|
||||
except Exception:
|
||||
pass
|
||||
pass
|
||||
try:
|
||||
del new_character
|
||||
except Exception:
|
||||
pass
|
||||
raise
|
||||
pass
|
||||
raise
|
||||
|
||||
# alias
|
||||
player = create_player
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue