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:
Griatch 2012-04-21 16:15:37 +02:00
parent 63329f5420
commit 8c3b49e704
15 changed files with 838 additions and 785 deletions

View file

@ -399,9 +399,8 @@ class CmdCreate(ObjManipCommand):
# object typeclass will automatically be used)
lockstring = "control:id(%s);examine:perm(Builders);delete:id(%s) or perm(Wizards);get:all()" % (caller.id, caller.id)
obj = create.create_object(typeclass, name, caller,
home=caller, aliases=aliases, locks=lockstring)
home=caller, aliases=aliases, locks=lockstring, report_to=caller)
if not obj:
string = "Error when creating object."
continue
if aliases:
string = "You create a new %s: %s (aliases: %s)."
@ -416,8 +415,8 @@ class CmdCreate(ObjManipCommand):
if caller.location:
obj.home = caller.location
obj.move_to(caller.location, quiet=True)
if string:
caller.msg(string)
if string:
caller.msg(string)
class CmdDebug(MuxCommand):
@ -630,7 +629,7 @@ class CmdDig(ObjManipCommand):
lockstring = lockstring % (caller.dbref, caller.dbref, caller.dbref)
new_room = create.create_object(typeclass, room["name"],
aliases=room["aliases"])
aliases=room["aliases"], report_to=caller)
new_room.locks.add(lockstring)
alias_string = ""
if new_room.aliases:
@ -659,7 +658,7 @@ class CmdDig(ObjManipCommand):
new_to_exit = create.create_object(typeclass, to_exit["name"], location,
aliases=to_exit["aliases"],
locks=lockstring, destination=new_room)
locks=lockstring, destination=new_room, report_to=caller)
alias_string = ""
if new_to_exit.aliases:
alias_string = " (%s)" % ", ".join(new_to_exit.aliases)
@ -684,7 +683,7 @@ class CmdDig(ObjManipCommand):
typeclass = settings.BASE_EXIT_TYPECLASS
new_back_exit = create.create_object(typeclass, back_exit["name"],
new_room, aliases=back_exit["aliases"],
locks=lockstring, destination=location)
locks=lockstring, destination=location, report_to=caller)
alias_string = ""
if new_back_exit.aliases:
alias_string = " (%s)" % ", ".join(new_back_exit.aliases)
@ -1083,7 +1082,7 @@ class CmdOpen(ObjManipCommand):
typeclass = settings.BASE_EXIT_TYPECLASS
exit_obj = create.create_object(typeclass, key=exit_name,
location=location,
aliases=exit_aliases)
aliases=exit_aliases, report_to=caller)
if exit_obj:
# storing a destination is what makes it an exit!
exit_obj.destination = destination
@ -1283,9 +1282,9 @@ class CmdTypeclass(MuxCommand):
@typeclass - set object typeclass
Usage:
@typclass[/switch] <object> [= <typeclass path>]
@type ''
@parent ''
@typclass[/switch] <object> [= <typeclass.path>]
@type ''
@parent ''
Switch:
reset - clean out *all* the attributes on the object -
@ -1295,12 +1294,18 @@ class CmdTypeclass(MuxCommand):
Example:
@type button = examples.red_button.RedButton
Sets an object's typeclass. The typeclass must be identified
by its location using python dot-notation pointing to the correct
module and class. If no typeclass is given (or a wrong typeclass
is given), the object will be set to the default typeclass.
The location of the typeclass module is searched from
the default typeclass directory, as defined in the server settings.
View or set an object's typeclass. If setting, the creation hooks
of the new typeclass will be run on the object. If you have
clashing properties on the old class, use /reset. By default you
are protected from changing to a typeclass of the same name as the
one you already have, use /force to override this protection.
The given typeclass must be identified by its location using
python dot-notation pointing to the correct module and class. If
no typeclass is given (or a wrong typeclass is given). Errors in
the path or new typeclass will lead to the old typeclass being
kept. The location of the typeclass module is searched from the
default typeclass directory, as defined in the server settings.
"""
@ -1327,7 +1332,7 @@ class CmdTypeclass(MuxCommand):
# we did not supply a new typeclass, view the
# current one instead.
if hasattr(obj, "typeclass"):
string = "%s's current typeclass is '%s'." % (obj.name, obj.typeclass.typename)
string = "%s's current typeclass is '%s' (%s)." % (obj.name, obj.typeclass.typename, obj.typeclass.path)
else:
string = "%s is not a typed object." % obj.name
caller.msg(string)
@ -1344,25 +1349,31 @@ class CmdTypeclass(MuxCommand):
caller.msg("This object cannot have a type at all!")
return
if obj.is_typeclass(typeclass) and not 'force' in self.switches:
string = "%s already has the typeclass '%s'." % (obj.name, typeclass)
is_same = obj.is_typeclass(typeclass)
if is_same and not 'force' in self.switches:
string = "%s already has the typeclass '%s'. Use /force to override." % (obj.name, typeclass)
else:
reset = "reset" in self.switches
old_typeclass_name = obj.typeclass.typename
old_typeclass_path = obj.typeclass.path
ok = obj.swap_typeclass(typeclass, clean_attributes=reset)
if ok:
string = "%s's type is now %s (instead of %s).\n" % (obj.name,
obj.typeclass.typename,
old_typeclass_name)
if reset:
string += "All attributes where reset."
if is_same:
string = "%s updated its existing typeclass (%s).\n" % (obj.name, obj.typeclass.path)
else:
string += "Note that the new class type could have overwritten "
string += "same-named attributes on the existing object."
string = "%s's changed typeclass from %s to %s.\n" % (obj.name,
old_typeclass_path,
obj.typeclass.path)
string += "Creation hooks were run."
if reset:
string += " All old attributes where deleted before the swap."
else:
string += " Note that the typeclassed object could have ended up with a mixture of old"
string += "\nand new attributes. Use /reset to remove old attributes if you don't want this."
else:
string = "Could not swap '%s' (%s) to typeclass '%s'." % (obj.name,
old_typeclass_name,
typeclass)
string = obj.typeclass_last_errmsg
string += "\nCould not swap '%s' (%s) to typeclass '%s'." % (obj.name,
old_typeclass_path,
typeclass)
caller.msg(string)

View file

@ -588,7 +588,7 @@ class CmdAccess(MuxCommand):
caller = self.caller
hierarchy_full = settings.PERMISSION_HIERARCHY
string = "\n{wPermission Hierarchy{n (climbing):\n %s" % ", ".join(hierarchy_full)
hierarchy = [p.lower() for p in hierarchy_full]
#hierarchy = [p.lower() for p in hierarchy_full]
if self.caller.player.is_superuser:
cperms = "<Superuser>"

View file

@ -8,14 +8,12 @@ import traceback
import os, datetime, time
import django, twisted
from django.contrib.auth.models import User
from django.conf import settings
from src.server.sessionhandler import SESSIONS
from src.scripts.models import ScriptDB
from src.objects.models import ObjectDB
from src.players.models import PlayerDB
from src.server.models import ServerConfig
from src.utils import create, logger, utils, gametime
from src.utils import logger, utils, gametime
from src.commands.default.muxcommand import MuxCommand
# limit symbol import for API

View file

@ -182,13 +182,14 @@ its and @/./+/-/_ only.") # this echoes the restrictions made by django's auth m
typeclass = settings.BASE_CHARACTER_TYPECLASS
permissions = settings.PERMISSION_PLAYER_DEFAULT
new_character = create.create_player(playername, email, password,
permissions=permissions,
character_typeclass=typeclass,
character_location=default_home,
character_home=default_home)
if not new_character:
session.msg("There was an error creating the default Character/Player. This error was logged. Contact an admin.")
try:
new_character = create.create_player(playername, email, password,
permissions=permissions,
character_typeclass=typeclass,
character_location=default_home,
character_home=default_home)
except Exception, e:
session.msg("There was an error creating the default Character/Player:\n%s\n If this problem persists, contact an admin.")
return
new_player = new_character.player

View file

@ -323,7 +323,7 @@ class Object(TypeClass):
"""
self.dbobj.swap_typeclass(new_typeclass, clean_attributes=clean_attributes, no_default=no_default)
return self.dbobj.swap_typeclass(new_typeclass, clean_attributes=clean_attributes, no_default=no_default)
def access(self, accessing_obj, access_type='read', default=False):
"""

View file

@ -58,9 +58,6 @@ def create_objects():
create_character=True,
character_typeclass=character_typeclass)
if not god_character:
raise Exception(_("#1 could not be created. Check the Player/Character typeclass for bugs."))
god_character.id = 1
god_character.db.desc = _('This is User #1.')
god_character.locks.add("examine:perm(Immortals);edit:false();delete:false();boot:false();msg:all();puppet:false()")
@ -164,7 +161,11 @@ def create_admin_media_links():
on system.
"""
import django, os
dpath = os.path.join(django.__path__[0], 'contrib', 'admin', 'media')
if django.get_version() < 1.4:
dpath = os.path.join(django.__path__[0], 'contrib', 'admin', 'media')
else:
dpath = os.path.join(django.__path__[0], 'contrib', 'admin', 'static', 'admin')
apath = os.path.join(settings.ADMIN_MEDIA_ROOT)
if os.path.isdir(apath):
print _(" ADMIN_MEDIA_ROOT already exists. Ignored.")
@ -177,8 +178,8 @@ def create_admin_media_links():
try:
os.symlink(dpath, apath)
print _(" Admin-media symlinked to ADMIN_MEDIA_ROOT.")
except OSError:
print _(" There was an error symlinking Admin-media to ADMIN_MEDIA_ROOT. If you see issues, link manually.")
except OSError, e:
print _(" There was an error symlinking Admin-media to ADMIN_MEDIA_ROOT:\n %s\n -> \n %s\n (%s)\n If you see issues, link manually." % (dpath, apath, e))
else:
print _(" Admin-media files should be copied manually to ADMIN_MEDIA_ROOT.")
@ -194,7 +195,7 @@ def at_initial_setup():
return
try:
mod = __import__(modname, fromlist=[None])
except ImportError, ValueError:
except (ImportError, ValueError):
return
print _(" Running at_initial_setup() hook.")
if mod.__dict__.get("at_initial_setup", None):

View file

@ -12,6 +12,7 @@ etc.
"""
import re
from src.utils.utils import make_iter
from src.utils import logger
# variables
MSDP = chr(69)
@ -24,7 +25,7 @@ MSDP_ARRAY_CLOSE = chr(6)
regex_array = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, MSDP_ARRAY_OPEN, MSDP_ARRAY_CLOSE)) # return 2-tuple
regex_table = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, MSDP_TABLE_OPEN, MSDP_TABLE_CLOSE)) # return 2-tuple (may be nested)
regex_varval = re.compile(r"%s(.*?)%s(.*?)[%s]" % (MSDP_VAR, MSDP_VAL, ENDING)) # return 2-tuple
regex_varval = re.compile(r"%s(.*?)%s(.*?)" % (MSDP_VAR, MSDP_VAL)) # return 2-tuple
class Msdp(object):
"""
@ -111,8 +112,8 @@ class Msdp(object):
tables[table[0]] = dict(regex_varval(table[1]))
for array in regex_array.findall(data):
arrays[array[0]] = dict(regex_varval(array[1]))
variables = dict(regex._varval(regex_array.sub("", regex_table.sub("", data))))
variables = dict(regex_varval(regex_array.sub("", regex_table.sub("", data))))
print variables
# MSDP Commands
@ -125,7 +126,7 @@ class Msdp(object):
The List command allows for retrieving various info about the server/client
"""
if arg == 'COMMANDS':
return self.func_to_msdp(arg, MSDP_COMMANDS.keys())
return self.func_to_msdp(arg, self.MSDP_COMMANDS.keys())
elif arg == 'LISTS':
return self.func_to_msdp(arg, ("COMMANDS", "LISTS",
"CONFIGURABLE_VARIABLES",
@ -133,11 +134,11 @@ class Msdp(object):
elif arg == 'CONFIGURABLE_VARIABLES':
return self.func_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID"))
elif arg == 'REPORTABLE_VARIABLES':
return self.func_to_msdp(arg, MSDP_REPORTABLE.keys())
return self.func_to_msdp(arg, self.MSDP_REPORTABLE.keys())
elif arg == 'REPORTED_VARIABLES':
return self.func_to_msdp(arg, MSDP_REPORTED.keys())
return self.func_to_msdp(arg, self.MSDP_REPORTED.keys())
elif arg == 'SENDABLE_VARIABLES':
return self.func_to_msdp(arg, MSDP_SEND.keys())
return self.func_to_msdp(arg, self.MSDP_SEND.keys())
else:
return self.func_to_msdp("LIST", arg)
@ -147,7 +148,7 @@ class Msdp(object):
reportable variable to the client.
"""
try:
MSDP_REPORTABLE[arg](report=True)
self.MSDP_REPORTABLE[arg](report=True)
except Exception:
logger.log_trace()
@ -156,16 +157,16 @@ class Msdp(object):
Unreport a previously reported variable
"""
try:
MSDP_REPORTABLE[arg](eport=False)
self.MSDP_REPORTABLE[arg](eport=False)
except Exception:
logger.log_trace()
self.logger.log_trace()
def msdp_cmd_reset(self, arg):
"""
The reset command resets a variable to its initial state.
"""
try:
MSDP_REPORTABLE[arg](reset=True)
self.MSDP_REPORTABLE[arg](reset=True)
except Exception:
logger.log_trace()
@ -177,21 +178,22 @@ class Msdp(object):
arg - this is a list of variables the client wants.
"""
ret = []
for var in makeiter(arg):
for var in make_iter(arg):
try:
ret.append(MSDP_REPORTABLE[arg](send=True))
ret.append(self.MSDP_REPORTABLE[arg](send=True))
except Exception:
logger.log_trace()
return ret
MSDP_COMMANDS = {
"LIST": self.msdp_list,
"LIST": "msdp_list",
"REPORT":"mspd_report",
"RESET":"mspd_reset",
"SEND":"mspd_send",
"UNREPORT":"mspd_unreport"
}
# MSDP_MAP is a standard suggestions for making it easy to create generic guis.
# this maps MSDP command names to Evennia commands found in OOB_FUNC_MODULE. It
# is up to these commands to return data on proper form.

View file

@ -244,6 +244,7 @@ class ServerSession(Session):
else:
logger.log_errmsg("oob_data_in error: funcname '%s' not found in OOB_FUNC_MODULE." % funcname)
if outdata:
# we have a result, send it back
self.oob_data_out(outdata)

View file

@ -951,6 +951,7 @@ class TypedObject(SharedMemoryModel):
# If we reach this point we couldn't import any typeclasses. Return default. It's up to the calling
# method to use e.g. self.is_typeclass() to detect that the result is not the one asked for.
_GA(self, "_display_errmsg")(errstring)
_SA(self, "typeclass_lasterrmsg", errstring)
return _GA(self, "_get_default_typeclass")(cache=False, silent=False, save=False)
#@typeclass.deleter
@ -961,6 +962,11 @@ class TypedObject(SharedMemoryModel):
# typeclass property
typeclass = property(__typeclass_get, fdel=__typeclass_del)
# the last error string will be stored here for accessing methods to access.
# It is set by _display_errmsg, which will print to log if error happens
# during server startup.
typeclass_last_errmsg = ""
def _path_import(self, path):
"""
Import a class from a python path of the
@ -973,17 +979,19 @@ class TypedObject(SharedMemoryModel):
return None
try:
modpath, class_name = path.rsplit('.', 1)
module = __import__(modpath, fromlist=[class_name])
module = __import__(modpath, fromlist=["none"])
return module.__dict__[class_name]
except ImportError:
trc = sys.exc_traceback
if not trc.tb_next:
# we separate between not finding the module, and finding a buggy one.
errstring += "(Tried path '%s')." % path
errstring = "Typeclass not found trying path '%s'." % path
else:
# a bug in the module is reported normally.
trc = traceback.format_exc()
errstring += "\n%sError importing '%s'." % (trc, path)
errstring = "\n%sError importing '%s'." % (trc, path)
except (ValueError, TypeError):
errstring = "Malformed typeclass path '%s'." % path
except KeyError:
errstring = "No class '%s' was found in module '%s'."
errstring = errstring % (class_name, modpath)
@ -997,26 +1005,32 @@ class TypedObject(SharedMemoryModel):
"""
Helper function to display error.
"""
infochan = None
cmessage = message
try:
from src.comms.models import Channel
infochan = settings.CHANNEL_MUDINFO
infochan = Channel.objects.get_channel(infochan[0])
if infochan:
cname = infochan.key
cmessage = "\n".join(["[%s]: %s" % (cname, line) for line in message.split('\n') if line])
cmessage = cmessage.strip()
infochan.msg(cmessage)
else:
# no mudinfo channel is found. Log instead.
cmessage = "\n".join(["[NO MUDINFO CHANNEL]: %s" % line for line in message.split('\n')])
logger.log_errmsg(cmessage)
except Exception:
if ServerConfig.objects.conf("server_starting_mode"):
print cmessage
else:
logger.log_trace(cmessage)
if ServerConfig.objects.conf("server_starting_mode"):
print message.strip()
else:
_SA(self, "typeclass_last_errmsg", message.strip())
return
#infochan = None
#cmessage = message
#try:
# from src.comms.models import Channel
# infochan = settings.CHANNEL_MUDINFO
# infochan = Channel.objects.get_channel(infochan[0])
# if infochan:
# cname = infochan.key
# cmessage = "\n".join(["[%s]: %s" % (cname, line) for line in message.split('\n') if line])
# cmessage = cmessage.strip()
# infochan.msg(cmessage)
# else:
# # no mudinfo channel is found. Log instead.
# cmessage = "\n".join(["[NO MUDINFO CHANNEL]: %s" % line for line in message.split('\n')])
# logger.log_errmsg(cmessage)
#except Exception:
# if ServerConfig.objects.conf("server_starting_mode"):
# print cmessage
# else:
# logger.log_trace(cmessage)
def _get_default_typeclass(self, cache=False, silent=False, save=False):
"""
@ -1064,7 +1078,9 @@ class TypedObject(SharedMemoryModel):
"""
Returns true if this object has this type
OR has a typeclass which is an subclass of
the given typeclass.
the given typeclass. This operates on the actually
loaded typeclass (this is important since a failing
typeclass may instead have its default currently loaded)
typeclass - can be a class object or the
python path to such an object to match against.
@ -1079,7 +1095,7 @@ class TypedObject(SharedMemoryModel):
pass
typeclasses = [typeclass] + ["%s.%s" % (path, typeclass) for path in _GA(self, "_typeclass_paths")]
if exact:
current_path = _GA(self, "_cached_db_typeclass_path")
current_path = _GA(self.typeclass, "path") #"_GA(self, "_cached_db_typeclass_path")
return typeclass and any((current_path == typec for typec in typeclasses))
else:
# check parent chain
@ -1158,10 +1174,9 @@ class TypedObject(SharedMemoryModel):
self.nattr(nattr, delete=True)
else:
#print "deleting attrs ..."
self.get_all_attributes()
for attr in self.get_all_attributes():
attr.delete()
for nattr in self.ndb.all():
for nattr in self.ndb.all:
del nattr
# run hooks for this new typeclass
@ -1328,8 +1343,9 @@ class TypedObject(SharedMemoryModel):
_GA(self, 'obj').set_attribute(attrname, value)
def __delattr__(self, attrname):
_GA(self, 'obj').del_attribute(attrname)
def all(self):
def get_all(self):
return _GA(self, 'obj').get_all_attributes()
all = property(get_all)
self._db_holder = DbHolder(self)
return self._db_holder
#@db.setter
@ -1386,9 +1402,10 @@ class TypedObject(SharedMemoryModel):
except AttributeError:
class NdbHolder(object):
"Holder for storing non-persistent attributes."
def all(self):
def get_all(self):
return [val for val in self.__dict__.keys()
if not val.startswith['_']]
if not val.startswith('_')]
all = property(get_all)
def __getattribute__(self, key):
# return None if no matching attribute was found.
try:

View file

@ -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")
@ -41,7 +40,7 @@ GA = object.__getattribute__
def create_object(typeclass, key=None, location=None,
home=None, player=None, permissions=None, locks=None,
aliases=None, destination=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
@ -52,6 +51,11 @@ def create_object(typeclass, key=None, location=None,
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.
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
@ -83,11 +87,15 @@ def create_object(typeclass, key=None, location=None,
# 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
# as if it was the database object.
@ -141,7 +149,7 @@ object = create_object
def create_script(typeclass, key=None, obj=None, locks=None,
interval=None, start_delay=None, repeats=None,
persistent=None, autostart=True):
persistent=None, autostart=True, report_to=None):
"""
Create a new script. All scripts are a combination
of a database object that communicates with the
@ -160,6 +168,11 @@ def create_script(typeclass, key=None, obj=None, locks=None,
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.
@ -171,7 +184,7 @@ def create_script(typeclass, key=None, obj=None, locks=None,
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
@ -195,9 +208,13 @@ 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:
@ -315,14 +332,14 @@ 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]
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
@ -356,7 +373,7 @@ def create_channel(key, aliases=None, desc=None,
new_channel = Channel()
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
@ -384,7 +401,7 @@ def create_player(name, email, password,
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):
"""
@ -468,7 +485,12 @@ 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)
@ -492,10 +514,10 @@ def create_player(name, email, password,
new_character = create_object(character_typeclass, key=name,
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