Exemplo n.º 1
0
def create_connection(channel, irc_network, irc_port, irc_channel, irc_bot_nick):
    """
    This will create a new IRC<->channel connection.
    """
    if not type(channel) == Channel:
        new_channel = Channel.objects.filter(db_key=channel)
        if not new_channel:
            logger.log_errmsg(_("Cannot attach IRC<->Evennia: Evennia Channel '%s' not found") % channel)
            return False
        channel = new_channel[0]
    key = build_connection_key(channel, irc_network, irc_port, irc_channel, irc_bot_nick)

    old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
    if old_conns:
        return False
    config = "%s|%s|%s|%s" % (irc_network, irc_port, irc_channel, irc_bot_nick)
    # how the channel will be able to contact this protocol
    send_code =  "from src.comms.irc import IRC_CHANNELS\n"
    send_code += "matched_ircs = [irc for irc in IRC_CHANNELS if irc.factory.key == '%s']\n" % key
    send_code += "[irc.msg_irc(message, senders=[self]) for irc in matched_ircs]\n"
    conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
                                     db_external_config=config)
    conn.save()

    # connect
    connect_to_irc(conn)
    return True
Exemplo n.º 2
0
    def __delattr__(self, propname):
        """
        Transparently deletes data from the typeclass or dbobj by first
        searching on the typeclass, secondly on the dbobj.db.
        Will not allow deletion of properties stored directly on dbobj.
        """
        if propname in PROTECTED:
            string = "%s: '%s' is a protected attribute name."
            string += " (protected: [%s])" % (", ".join(PROTECTED))
            log_errmsg(string % (self.name, propname))
            return

        try:
            _DA(self, propname)
        except AttributeError:
            # not on typeclass, try to delete on db/ndb
            try:
                dbobj = _GA(self, 'dbobj')
            except AttributeError:
                log_trace("This is probably due to an unsafe reload.")
                return  # ignore delete
            try:
                dbobj.del_attribute(propname, raise_exception=True)
            except AttributeError:
                string = "Object: '%s' not found on %s(#%s), nor on its typeclass %s."
                raise AttributeError(string % (
                    propname,
                    dbobj,
                    dbobj.dbid,
                    dbobj.typeclass_path,
                ))
Exemplo n.º 3
0
    def add(self, scriptclass, key=None, autostart=True):
        """
        Add an script to this object.

        scriptclass - either a class object
             inheriting from Script, an instantiated script object
             or a python path to such a class object.
        key - optional identifier for the script (often set in script
              definition)
        autostart - start the script upon adding it
        """
        if self.obj.dbobj.__class__.__name__ == "PlayerDB":
            # we add to a Player, not an Object
            script = create.create_script(scriptclass,
                                          key=key,
                                          player=self.obj,
                                          autostart=autostart)
        else:
            # the normal - adding to an Object
            script = create.create_script(scriptclass,
                                          key=key,
                                          obj=self.obj,
                                          autostart=autostart)
        if not script:
            logger.log_errmsg(
                "Script %s could not be created and/or started." % scriptclass)
            return False
        return True
Exemplo n.º 4
0
    def get_objs_with_db_property_value(self, property_name, property_value, candidates=None, typeclasses=None):
        """
        Returns all objects having a given db field property.
        candidates - list of objects to search
        typeclasses - list of typeclass-path strings to restrict matches with
        """
        if isinstance(property_value, basestring):
            property_value = to_unicode(property_value)
        if isinstance(property_name, basestring):
            if not property_name.startswith("db_"):
                property_name = "db_%s" % property_name
        if hasattr(property_value, "dbobj"):
            property_value = property_value.dbobj
        querykwargs = {property_name: property_value}
        cand_restriction = (
            candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
        )
        type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
        try:
            return list(self.filter(cand_restriction & type_restriction & Q(**querykwargs)))
        except exceptions.FieldError:
            return []
        except ValueError:
            from src.utils import logger

            logger.log_errmsg(
                "The property '%s' does not support search criteria of the type %s."
                % (property_name, type(property_value))
            )
            return []
Exemplo n.º 5
0
 def get_objs_with_db_property_value(self,
                                     property_name,
                                     property_value,
                                     candidates=None,
                                     typeclasses=None):
     """
     Returns all objects having a given db field property.
     candidates - list of objects to search
     typeclasses - list of typeclass-path strings to restrict matches with
     """
     if isinstance(property_value, basestring):
         property_value = to_unicode(property_value)
     if isinstance(property_name, basestring):
         if not property_name.startswith('db_'):
             property_name = "db_%s" % property_name
     if hasattr(property_value, 'dbobj'):
         property_value = property_value.dbobj
     querykwargs = {property_name: property_value}
     cand_restriction = candidates != None and Q(
         pk__in=[_GA(obj, "id")
                 for obj in make_iter(candidates) if obj]) or Q()
     type_restriction = typeclasses and Q(
         db_typeclass_path__in=make_iter(typeclasses)) or Q()
     try:
         return list(
             self.filter(cand_restriction & type_restriction
                         & Q(**querykwargs)))
     except exceptions.FieldError:
         return []
     except ValueError:
         from src.utils import logger
         logger.log_errmsg(
             "The property '%s' does not support search criteria of the type %s."
             % (property_name, type(property_value)))
         return []
Exemplo n.º 6
0
def create_help_entry(key, entrytext, category="General", locks=None):
    """
    Create a static help entry in the help database. Note that Command
    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.
    """
    global _HelpEntry
    if not _HelpEntry:
        from src.help.models import HelpEntry as _HelpEntry

    try:
        new_help = _HelpEntry()
        new_help.key = key
        new_help.entrytext = entrytext
        new_help.help_category = category
        if locks:
            new_help.locks.add(locks)
        new_help.save()
        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
Exemplo n.º 7
0
    def __setattr__(self, propname, value):
        """
        Transparently save data to the dbobj object in
        all situations. Note that this does not
        necessarily mean storing it to the database
        unless data is stored into a propname
        corresponding to a field on ObjectDB model.
        """
        #print "set %s -> %s" % (propname, value)
        if propname in PROTECTED:
            string = "%s: '%s' is a protected attribute name."
            string += " (protected: [%s])" % (", ".join(PROTECTED))
            log_errmsg(string % (self.name, propname))
            return
        try:
            dbobj = _GA(self, 'dbobj')
        except AttributeError:
            dbobj = None
            log_trace("This is probably due to an unsafe reload.")

        if dbobj:
            try:
                # only set value on propname if propname already exists
                # on dbobj. __getattribute__ will raise attribute error otherwise.
                _GA(dbobj, propname)
                _SA(dbobj, propname, value)
            except AttributeError:
                #XXX deprecated
                dbobj.set_attribute(propname, value)
        else:
            _SA(self, propname, value)
Exemplo n.º 8
0
 def __setattr__(self, propname, value):
     """
     Transparently save data. Use property on Typeclass only if
     that property is already defined, otherwise relegate to the
     dbobj object in all situations. Note that this does not
     necessarily mean storing it to the database.
     """
     #print "set %s -> %s" % (propname, value)
     if propname in PROTECTED:
         string = "%s: '%s' is a protected attribute name."
         string += " (protected: [%s])" % (", ".join(PROTECTED))
         log_errmsg(string % (self.name, propname))
         return
     try:
         _GA(self, propname)
         _SA(self, propname, value)
     except AttributeError:
         try:
             dbobj = _GA(self, 'dbobj')
         except AttributeError:
             dbobj = None
         if dbobj:
             _SA(dbobj, propname, value)
         else:
             # only as a last resort do we save on the typeclass object
             _SA(self, propname, value)
Exemplo n.º 9
0
    def __delattr__(self, propname):
        """
        Transparently deletes data from the typeclass or dbobj by first searching on the typeclass,
        secondly on the dbobj.db.
        Will not allow deletion of properties stored directly on dbobj.
        """
        if propname in PROTECTED:
            string = "%s: '%s' is a protected attribute name."
            string += " (protected: [%s])" % (", ".join(PROTECTED))
            log_errmsg(string % (self.name, propname))
            return

        try:
            _DA(self, propname)
        except AttributeError:
            # not on typeclass, try to delete on db/ndb
            try:
                dbobj = _GA(self, 'dbobj')
            except AttributeError:
                log_trace("This is probably due to an unsafe reload.")
                return # ignore delete
            try:
                dbobj.del_attribute_raise(propname)
            except AttributeError:
                string = "Object: '%s' not found on %s(#%s), nor on its typeclass %s."
                raise AttributeError(string % (propname, dbobj,
                                               dbobj.dbid,
                                               dbobj.typeclass_path,))
Exemplo n.º 10
0
 def __setattr__(self, propname, value):
     """
     Transparently save data. Use property on Typeclass only if
     that property is already defined, otherwise relegate to the
     dbobj object in all situations. Note that this does not
     necessarily mean storing it to the database.
     """
     #print "set %s -> %s" % (propname, value)
     if propname in PROTECTED:
         string = "%s: '%s' is a protected attribute name."
         string += " (protected: [%s])" % (", ".join(PROTECTED))
         log_errmsg(string % (self.name, propname))
         return
     try:
         _GA(self, propname)
         _SA(self, propname, value)
     except AttributeError:
         try:
             dbobj = _GA(self, 'dbobj')
         except AttributeError:
             dbobj = None
         if dbobj:
             _SA(dbobj, propname, value)
         else:
             # only as a last resort do we save on the typeclass object
             _SA(self, propname, value)
Exemplo n.º 11
0
def create_help_entry(key, entrytext, category="General", locks=None):
    """
    Create a static help entry in the help database. Note that Command
    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.
    """
    global _HelpEntry
    if not _HelpEntry:
        from src.help.models import HelpEntry as _HelpEntry

    try:
        new_help = _HelpEntry()
        new_help.key = key
        new_help.entrytext = entrytext
        new_help.help_category = category
        if locks:
            new_help.locks.add(locks)
        new_help.save()
        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
Exemplo n.º 12
0
 def __value_set(self, value):
     "Setter. Allows for self.value = value"
     if utils.has_parent('django.db.models.base.Model', value):
         # we have to protect against storing db objects.
         logger.log_errmsg("ServerConfig cannot store db objects! (%s)" % value)
         return
     self.db_value = pickle.dumps(value)
     self.save()
Exemplo n.º 13
0
 def _log_error(self, message):
     "Try to log errors back to object"
     if self.log_obj and hasattr(self.log_obj, 'msg'):
         self.log_obj.msg(message)
     elif hasattr(self.obj, 'msg'):
         self.obj.msg(message)
     else:
         logger.log_errmsg("%s: %s" % (self.obj, message))
Exemplo n.º 14
0
 def _save_tree(self):
     "recursively traverse back up the tree, save when we reach the root"
     if self._parent:
         self._parent._save_tree()
     elif self._db_obj:
         self._db_obj.value = self
     else:
         logger.log_errmsg("_SaverMutable %s has no root Attribute to save to." % self)
Exemplo n.º 15
0
def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False):
    """
    This helper function is used by the cmdsethandler to load a cmdset
    instance from a python module, given a python_path. It's usually accessed
    through the cmdsethandler's add() and add_default() methods.
    path - This is the full path to the cmdset object on python dot-form
    cmdsetobj - the database object/typeclass on which this cmdset is to be
            assigned (this can be also channels and exits, as well as players
            but there will always be such an object)
    emit_to_obj - if given, error is emitted to this object (in addition
                  to logging)
    no_logging - don't log/send error messages. This can be useful
                if import_cmdset is just used to check if this is a
                valid python path or not.
    function returns None if an error was encountered or path not found.
    """

    python_paths = [path] + ["%s.%s" % (prefix, path)
                                    for prefix in _CMDSET_PATHS]
    errstring = ""
    for python_path in python_paths:
        try:
            #print "importing %s: _CACHED_CMDSETS=%s" % (python_path, _CACHED_CMDSETS)
            wanted_cache_key = python_path
            cmdsetclass = _CACHED_CMDSETS.get(wanted_cache_key, None)
            errstring = ""
            if not cmdsetclass:
                #print "cmdset '%s' not in cache. Reloading %s on %s." % (wanted_cache_key, python_path, cmdsetobj)
                # Not in cache. Reload from disk.
                modulepath, classname = python_path.rsplit('.', 1)
                module = __import__(modulepath, fromlist=[True])
                cmdsetclass = module.__dict__[classname]
                _CACHED_CMDSETS[wanted_cache_key] = cmdsetclass
            #instantiate the cmdset (and catch its errors)
            if callable(cmdsetclass):
                cmdsetclass = cmdsetclass(cmdsetobj)
            return cmdsetclass

        except ImportError:
            errstring += _("Error loading cmdset: Couldn't import module '%s'.")
            errstring = errstring % modulepath
        except KeyError:
            errstring += _("Error in loading cmdset: No cmdset class '%(classname)s' in %(modulepath)s.")
            errstring = errstring % {"classname": classname,
                                     "modulepath": modulepath}
        except Exception:
            errstring += _("Compile/Run error when loading cmdset '%s'. Error was logged.")
            errstring = errstring % (python_path)

    if errstring:
        # returning an empty error cmdset
        if not no_logging:
            logger.log_errmsg(errstring)
            if emit_to_obj and not ServerConfig.objects.conf("server_starting_mode"):
                emit_to_obj.msg(errstring)
        err_cmdset = _ErrorCmdSet()
        err_cmdset.errmessage = errstring
        return err_cmdset
Exemplo n.º 16
0
 def _save_tree(self):
     "recursively traverse back up the tree, save when we reach the root"
     if self._parent:
         self._parent._save_tree()
     elif self._db_obj:
         self._db_obj.value = self
     else:
         logger.log_errmsg(
             "_SaverMutable %s has no root Attribute to save to." % self)
Exemplo n.º 17
0
 def _step_err_callback(self, e):
     "callback for runner errors"
     cname = self.__class__.__name__
     estring = _("Script %(key)s(#%(dbid)i) of type '%(cname)s': at_repeat() error '%(err)s'.") % \
                {"key":self.key, "dbid":self.dbid, "cname":cname, "err":e.getErrorMessage()}
     try:
         self.dbobj.db_obj.msg(estring)
     except Exception:
         pass
     logger.log_errmsg(estring)
Exemplo n.º 18
0
 def func(err, *args, **kwargs):
     err.trap(Exception)
     err = err.getErrorMessage()
     if use_timeout and err == _PROC_ERR:
         err = "Process took longer than %ss and timed out." % use_timeout
     if f:
         return f(err, *args, **kwargs)
     else:
         err = "Error reported from subprocess: '%s'" % err
         logger.log_errmsg(err)
Exemplo n.º 19
0
 def alarm(codestring):
     "store the code of too-long-running scripts"
     global _LOGGER
     if not _LOGGER:
         from src.utils import logger as _LOGGER
     self.timedout_codestrings.append(codestring)
     err = "Evlang code '%s' exceeded allowed execution time (>%ss)." % (codestring, timeout)
     _LOGGER.log_errmsg("EVLANG time exceeded: caller: %s, scripter: %s, code: %s" % (caller, scripter, codestring))
     if not self.msg(err, scripter, caller):
         raise EvlangError(err)
Exemplo n.º 20
0
 def func(err, *args, **kwargs):
     err.trap(Exception)
     err = err.getErrorMessage()
     if use_timeout and err == _PROC_ERR:
         err = "Process took longer than %ss and timed out." % use_timeout
     if f:
         return f(err, *args, **kwargs)
     else:
         err = "Error reported from subprocess: '%s'" % err
         logger.log_errmsg(err)
Exemplo n.º 21
0
 def _step_errback(self, e):
     "callback for runner errors"
     cname = self.__class__.__name__
     estring = _("Script %(key)s(#%(dbid)s) of type '%(cname)s': at_repeat() error '%(err)s'.") % \
                       {"key": self.key, "dbid": self.dbid, "cname": cname,
                        "err": e.getErrorMessage()}
     try:
         self.dbobj.db_obj.msg(estring)
     except Exception:
         pass
     logger.log_errmsg(estring)
Exemplo n.º 22
0
def _cache_lockfuncs():
    "Updates the cache."
    global _LOCKFUNCS
    _LOCKFUNCS = {}
    for modulepath in settings.LOCK_FUNC_MODULES:
        modulepath = utils.pypath_to_realpath(modulepath)
        mod = utils.mod_import(modulepath)
        if mod:
            for tup in (tup for tup in inspect.getmembers(mod) if callable(tup[1])):
                _LOCKFUNCS[tup[0]] = tup[1]
        else:
            logger.log_errmsg("Couldn't load %s from PERMISSION_FUNC_MODULES." % modulepath)
Exemplo n.º 23
0
def _cache_lockfuncs():
    "Updates the cache."
    global _LOCKFUNCS
    _LOCKFUNCS = {}
    for modulepath in settings.LOCK_FUNC_MODULES:
        modulepath = utils.pypath_to_realpath(modulepath)
        mod = utils.mod_import(modulepath)
        if mod:
            for tup in (tup for tup in inspect.getmembers(mod) if callable(tup[1])):
                _LOCKFUNCS[tup[0]] = tup[1]
        else:
            logger.log_errmsg("Couldn't load %s from PERMISSION_FUNC_MODULES." % modulepath)
Exemplo n.º 24
0
 def has_connection(self, player):
     """
     Checks so this player is actually listening
     to this channel.
     """
     # also handle object.player calls
     player, typ = identify_object(player)
     if typ == 'object':
         player = player.player
         player, typ = identify_object(player)
     if player and not typ == "player":
         logger.log_errmsg("Channel.has_connection received object of type '%s'. It only accepts players/characters." % typ)
         return
     # do the check
     return PlayerChannelConnection.objects.has_player_connection(player, self)
Exemplo n.º 25
0
    def add(self, scriptclass, key=None, autostart=True):
        """
        Add an script to this object.

        scriptclass - either a class object
             inheriting from Script, an instantiated script object
             or a python path to such a class object.
        key - optional identifier for the script (often set in script definition)
        autostart - start the script upon adding it
        """
        script = create.create_script(scriptclass, key=key, obj=self.obj, autostart=autostart)
        if not script:
            logger.log_errmsg("Script %s could not be created and/or started." % scriptclass)
            return False
        return True
Exemplo n.º 26
0
def create_channel(key,
                   aliases=None,
                   desc=None,
                   locks=None,
                   keep_log=True,
                   typeclass=None):
    """
    Create A communication Channel. A Channel serves as a central
    hub for distributing Msgs to groups of people without
    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.

    key - this must be unique.
    aliases - list of alternative (likely shorter) keynames.
    locks - lock string definitions
    """
    global _ChannelDB, _channelhandler
    if not _ChannelDB:
        from src.comms.models import ChannelDB as _ChannelDB
    if not _channelhandler:
        from src.comms import channelhandler as _channelhandler
    if not typeclass:
        typeclass = settings.BASE_CHANNEL_TYPECLASS
    try:
        new_channel = _ChannelDB(typeclass=typeclass, db_key=key)
        new_channel.save()
        new_channel = new_channel.typeclass
        if aliases:
            if not utils.is_iter(aliases):
                aliases = [aliases]
            new_channel.aliases.add(aliases)
        new_channel.save()
        new_channel.db.desc = desc
        new_channel.db.keep_log = keep_log
    except IntegrityError:
        string = "Could not add channel: key '%s' already exists." % key
        logger.log_errmsg(string)
        return None
    if locks:
        new_channel.locks.add(locks)
    new_channel.save()
    _channelhandler.CHANNELHANDLER.add_channel(new_channel)
    new_channel.at_channel_create()
    return new_channel
Exemplo n.º 27
0
    def clear_contents(self):
        """
        Moves all objects (players/things) to their home
        location or to default home.
        """
        # Gather up everything that thinks this is its location.
        objs = ObjectDB.objects.filter(db_location=self)
        default_home_id = int(settings.DEFAULT_HOME.lstrip("#"))
        try:
            default_home = ObjectDB.objects.get(id=default_home_id)
            if default_home.dbid == _GA(self, "dbid"):
                # we are deleting default home!
                default_home = None
        except Exception:
            string = _("Could not find default home '(#%d)'.")
            logger.log_errmsg(string % default_home_id)
            default_home = None

        for obj in objs:
            home = obj.home
            # Obviously, we can't send it back to here.
            if not home or (home and home.dbid == _GA(self, "dbid")):
                obj.home = default_home
                home = default_home

            # If for some reason it's still None...
            if not home:
                string = "Missing default home, '%s(#%d)' "
                string += "now has a null location."
                obj.location = None
                obj.msg(
                    _("Something went wrong! You are dumped into nowhere. Contact an admin."
                      ))
                logger.log_errmsg(string % (obj.name, obj.dbid))
                return

            if obj.has_player:
                if home:
                    string = "Your current location has ceased to exist,"
                    string += " moving you to %s(#%d)."
                    obj.msg(_(string) % (home.name, home.dbid))
                else:
                    # Famous last words: The player should never see this.
                    string = "This place should not exist ... contact an admin."
                    obj.msg(_(string))
            obj.move_to(home)
Exemplo n.º 28
0
    def clear_contents(self):
        """
        Moves all objects (players/things) to their home
        location or to default home.
        """
        # Gather up everything that thinks this is its location.
        objs = ObjectDB.objects.filter(db_location=self)
        default_home_id = int(settings.DEFAULT_HOME.lstrip("#"))
        try:
            default_home = ObjectDB.objects.get(id=default_home_id)
            if default_home.dbid == _GA(self, "dbid"):
                # we are deleting default home!
                default_home = None
        except Exception:
            string = _("Could not find default home '(#%d)'.")
            logger.log_errmsg(string % default_home_id)
            default_home = None

        for obj in objs:
            home = obj.home
            # Obviously, we can't send it back to here.
            if not home or (home and home.dbid == _GA(self, "dbid")):
                obj.home = default_home
                home = default_home

            # If for some reason it's still None...
            if not home:
                string = "Missing default home, '%s(#%d)' "
                string += "now has a null location."
                obj.location = None
                obj.msg(_("Something went wrong! You are dumped into nowhere. Contact an admin."))
                logger.log_errmsg(string % (obj.name, obj.dbid))
                return

            if obj.has_player:
                if home:
                    string = "Your current location has ceased to exist,"
                    string += " moving you to %s(#%d)."
                    obj.msg(_(string) % (home.name, home.dbid))
                else:
                    # Famous last words: The player should never see this.
                    string = "This place should not exist ... contact an admin."
                    obj.msg(_(string))
            obj.move_to(home)
Exemplo n.º 29
0
def create_channel(key, aliases=None, desc=None,
                   locks=None, keep_log=True,
                   typeclass=None):
    """
    Create A communication Channel. A Channel serves as a central
    hub for distributing Msgs to groups of people without
    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.

    key - this must be unique.
    aliases - list of alternative (likely shorter) keynames.
    locks - lock string definitions
    """
    global _ChannelDB, _channelhandler
    if not _ChannelDB:
        from src.comms.models import ChannelDB as _ChannelDB
    if not _channelhandler:
        from src.comms import channelhandler as _channelhandler
    if not typeclass:
        typeclass = settings.BASE_CHANNEL_TYPECLASS
    try:
        new_channel = _ChannelDB(typeclass=typeclass, db_key=key)
        new_channel.save()
        new_channel = new_channel.typeclass
        if aliases:
            if not utils.is_iter(aliases):
                aliases = [aliases]
            new_channel.aliases.add(aliases)
        new_channel.save()
        new_channel.db.desc = desc
        new_channel.db.keep_log = keep_log
    except IntegrityError:
        string = "Could not add channel: key '%s' already exists." % key
        logger.log_errmsg(string)
        return None
    if locks:
        new_channel.locks.add(locks)
    new_channel.save()
    _channelhandler.CHANNELHANDLER.add_channel(new_channel)
    new_channel.at_channel_create()
    return new_channel
Exemplo n.º 30
0
    def __location_set(self, location):
        "Set location, checking for loops and allowing dbref"
        if isinstance(location, (basestring, int)):
            # allow setting of #dbref
            dbid = dbref(location, reqhash=False)
            if dbid:
                try:
                    location = ObjectDB.objects.get(id=dbid)
                except ObjectDoesNotExist:
                    # maybe it is just a name that happens to look like a dbid
                    pass
        try:

            def is_loc_loop(loc, depth=0):
                "Recursively traverse target location, trying to catch a loop."
                if depth > 10:
                    return
                elif loc == self:
                    raise RuntimeError
                elif loc == None:
                    raise RuntimeWarning
                return is_loc_loop(_GA(_GA(loc, "dbobj"), "db_location"),
                                   depth + 1)

            try:
                is_loc_loop(location)
            except RuntimeWarning:
                pass
            # actually set the field
            _SA(_GA(self, "dbobj"), "db_location",
                _GA(location, "dbobj") if location else location)
            _GA(_GA(self, "dbobj"), "save")(update_fields=["db_location"])
        except RuntimeError:
            errmsg = "Error: %s.location = %s creates a location loop." % (
                self.key, location)
            logger.log_errmsg(errmsg)
            raise RuntimeError(errmsg)
        except Exception, e:
            errmsg = "Error (%s): %s is not a valid location." % (str(e),
                                                                  location)
            logger.log_errmsg(errmsg)
            raise Exception(errmsg)
Exemplo n.º 31
0
def create_connection(channel, url, interval):
    """
    This will create a new RSS->channel connection
    """
    if not type(channel) == Channel:
        new_channel = Channel.objects.filter(db_key=channel)
        if not new_channel:
            logger.log_errmsg("Cannot attach RSS->Evennia: Evennia Channel '%s' not found." % channel)
            return False
        channel = new_channel[0]
    key = build_connection_key(channel, url)
    old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
    if old_conns:
        return False
    config = "%s|%i" % (url, interval)
    # There is no sendback from evennia to the rss, so we need not define any sendback code.
    conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_config=config)
    conn.save()

    connect_to_rss(conn)
    return True
Exemplo n.º 32
0
 def __setattr__(self, propname, value):
     """
     Transparently save data to the dbobj object in
     all situations. Note that this does not
     necessarily mean storing it to the database.
     """
     #print "set %s -> %s" % (propname, value)
     if propname in PROTECTED:
         string = "%s: '%s' is a protected attribute name."
         string += " (protected: [%s])" % (", ".join(PROTECTED))
         log_errmsg(string % (self.name, propname))
         return
     try:
         dbobj = _GA(self, 'dbobj')
     except AttributeError:
         dbobj = None
         log_trace("This is probably due to an unsafe reload.")
     if dbobj:
         _SA(dbobj, propname, value)
     else:
         # only as a last resort do we save on the typeclass object
         _SA(self, propname, value)
Exemplo n.º 33
0
 def __setattr__(self, propname, value):
     """
     Transparently save data to the dbobj object in
     all situations. Note that this does not
     necessarily mean storing it to the database.
     """
     #print "set %s -> %s" % (propname, value)
     if propname in PROTECTED:
         string = "%s: '%s' is a protected attribute name."
         string += " (protected: [%s])" % (", ".join(PROTECTED))
         log_errmsg(string % (self.name, propname))
         return
     try:
         dbobj = _GA(self, 'dbobj')
     except AttributeError:
         dbobj = None
         log_trace("This is probably due to an unsafe reload.")
     if dbobj:
         _SA(dbobj, propname, value)
     else:
         # only as a last resort do we save on the typeclass object
         _SA(self, propname, value)
Exemplo n.º 34
0
def create_connection(channel, imc2_channel):
    """
    This will create a new IMC2<->channel connection.
    """

    if not type(channel) == ChannelDB:
        new_channel = ChannelDB.objects.filter(db_key=channel)
        if not new_channel:
            logger.log_errmsg(_("Cannot attach IMC2<->Evennia: Evennia Channel '%s' not found") % channel)
            return False
        channel = new_channel[0]
    key = build_connection_key(channel, imc2_channel)

    old_conns = ExternalChannelConnection.objects.filter(db_external_key=key)
    if old_conns:
        # this evennia channel is already connected to imc. Check
        # if imc2_channel is different.
        # connection already exists. We try to only connect a new channel
        old_config = old_conns[0].db_external_config.split(",")
        if imc2_channel in old_config:
            return False # we already listen to this channel
        else:
            # We add a new imc2_channel to listen to
            old_config.append(imc2_channel)
            old_conns[0].db_external_config = ",".join(old_config)
            old_conns[0].save()
            return True
    else:
        # no old connection found; create a new one.
        config = imc2_channel
        # how the evennia channel will be able to contact this protocol in reverse
        send_code = "from src.comms.imc2 import IMC2_CLIENT\n"
        send_code += "data={'channel':from_channel}\n"
        send_code += "IMC2_CLIENT.msg_imc2(message, senders=[self])\n"
        conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code,
                                         db_external_config=config)
        conn.save()
        return True
Exemplo n.º 35
0
    def __location_set(self, location):
        "Set location, checking for loops and allowing dbref"
        if isinstance(location, (basestring, int)):
            # allow setting of #dbref
            dbid = dbref(location, reqhash=False)
            if dbid:
                try:
                    location = ObjectDB.objects.get(id=dbid)
                except ObjectDoesNotExist:
                    # maybe it is just a name that happens to look like a dbid
                    pass
        try:

            def is_loc_loop(loc, depth=0):
                "Recursively traverse target location, trying to catch a loop."
                if depth > 10:
                    return
                elif loc == self:
                    raise RuntimeError
                elif loc == None:
                    raise RuntimeWarning
                return is_loc_loop(_GA(_GA(loc, "dbobj"), "db_location"), depth + 1)

            try:
                is_loc_loop(location)
            except RuntimeWarning:
                pass
            # actually set the field
            _SA(_GA(self, "dbobj"), "db_location", _GA(location, "dbobj") if location else location)
            _GA(_GA(self, "dbobj"), "save")(update_fields=["db_location"])
        except RuntimeError:
            errmsg = "Error: %s.location = %s creates a location loop." % (self.key, location)
            logger.log_errmsg(errmsg)
            raise RuntimeError(errmsg)
        except Exception, e:
            errmsg = "Error (%s): %s is not a valid location." % (str(e), location)
            logger.log_errmsg(errmsg)
            raise Exception(errmsg)
Exemplo n.º 36
0
    def add(self, scriptclass, key=None, autostart=True):
        """
        Add an script to this object.

        scriptclass - either a class object
             inheriting from Script, an instantiated script object
             or a python path to such a class object.
        key - optional identifier for the script (often set in script
              definition)
        autostart - start the script upon adding it
        """
        if self.obj.dbobj.__class__.__name__ == "PlayerDB":
            # we add to a Player, not an Object
            script = create.create_script(scriptclass, key=key, player=self.obj,
                                          autostart=autostart)
        else:
            # the normal - adding to an Object
            script = create.create_script(scriptclass, key=key, obj=self.obj,
                                      autostart=autostart)
        if not script:
            logger.log_errmsg("Script %s could not be created and/or started." % scriptclass)
            return False
        return True
Exemplo n.º 37
0
    def oob_data_in(self, data):
        """
        This receives out-of-band data from the Portal.

        This method parses the data input (a dict) and uses
        it to launch correct methods from those plugged into
        the system.

        data = {funcname: ( [args], {kwargs]),
                funcname: ( [args], {kwargs}), ...}

        example:
           data = {"get_hp": ([], {}),
                   "update_counter", (["counter1"], {"now":True}) }
        """

        outdata = {}

        entity = self.get_character()
        if not entity:
            entity = self.get_player()
        if not entity:
            entity = self

        for funcname, argtuple in data.items():
            # loop through the data, calling available functions.
            func = OOB_FUNC_MODULE.__dict__.get(funcname, None)
            if func:
                try:
                    outdata[funcname] = func(entity, *argtuple[0], **argtuple[1])
                except Exception:
                    logger.log_trace()
            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)
Exemplo n.º 38
0
    def oob_data_in(self, data):
        """
        This receives out-of-band data from the Portal.

        OBS - preliminary. OOB not yet functional in Evennia. Don't use.

        This method parses the data input (a dict) and uses
        it to launch correct methods from those plugged into
        the system.

        data = {oobkey: (funcname, (args), {kwargs}),
                oobkey: (funcname, (args), {kwargs}), ...}

        example:
           data = {"get_hp": ("oob_get_hp, [], {}),
                   "update_counter", ("counter", ["counter1"], {"now":True}) }

        All function names must be defined in settings.OOB_FUNC_MODULE. Each
        function will be called with the oobkey and a back-reference to this session
        as their first two arguments.
        """

        outdata = {}

        for oobkey, functuple in data.items():
            # loop through the data, calling available functions.
            func = OOB_FUNC_MODULE.__dict__.get(functuple[0])
            if func:
                try:
                    outdata[funcname] = func(oobkey, self, *functuple[1], **functuple[2])
                except Exception:
                    logger.log_trace()
            else:
                logger.log_errmsg("oob_data_in error: funcname '%s' not found in OOB_FUNC_MODULE." % funcname)
        if outdata:
            # we have a direct result - send it back right away
            self.oob_data_out(outdata)
Exemplo n.º 39
0
 def _errback(self, fail):
     "Report error"
     logger.log_errmsg("RSS feed error: %s" % fail.value)
Exemplo n.º 40
0
def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sessid=None, **kwargs):
    """
    This is the main function to handle any string sent to the engine.

    called_by - object on which this was called from. This is either a Session, a Player or an Object.
    raw_string - the command string given on the command line
    _testing - if we should actually execute the command or not.
              if True, the command instance will be returned instead.
    callertype - this is one of "session", "player" or "object", in decending
                 order. So when the Session is the caller, it will merge its
                 own cmdset into cmdsets from both Player and eventual puppeted
                 Object (and cmdsets in its room etc). A Player will only
                 include its own cmdset and the Objects and so on. Merge order
                 is the same order, so that Object cmdsets are merged in last,
                 giving them precendence for same-name and same-prio commands.
    sessid - Relevant if callertype is "player" - the session id will help
             retrieve the correct cmdsets from puppeted objects.
    **kwargs - other keyword arguments will be assigned as named variables on the
               retrieved command object *before* it is executed. This is unuesed
               in default Evennia but may be used by code to set custom flags or
               special operating conditions for a command as it executes.

    Note that this function returns a deferred!
    """

    raw_string = to_unicode(raw_string, force_string=True)

    session, player, obj = None, None, None
    if callertype == "session":
        session = called_by
        player = session.player
        if player:
            obj = yield _GA(player.dbobj, "get_puppet")(session.sessid)
    elif callertype == "player":
        player = called_by
        if sessid:
            obj = yield _GA(player.dbobj, "get_puppet")(sessid)
    elif callertype == "object":
        obj = called_by
    else:
        raise RuntimeError("cmdhandler: callertype %s is not valid." % callertype)

    # the caller will be the one to receive messages and excert its permissions.
    # we assign the caller with preference 'bottom up'
    caller = obj or player or session

    try:  # catch bugs in cmdhandler itself
        try:  # catch special-type commands

            cmdset = yield get_and_merge_cmdsets(caller, session, player, obj,
                                                  callertype, sessid)
            if not cmdset:
                # this is bad and shouldn't happen.
                raise NoCmdSets
            unformatted_raw_string = raw_string
            raw_string = raw_string.strip()
            if not raw_string:
                # Empty input. Test for system command instead.
                syscmd = yield cmdset.get(CMD_NOINPUT)
                sysarg = ""
                raise ExecSystemCommand(syscmd, sysarg)
            # Parse the input string and match to available cmdset.
            # This also checks for permissions, so all commands in match
            # are commands the caller is allowed to call.
            matches = yield _COMMAND_PARSER(raw_string, cmdset, caller)

            # Deal with matches

            if len(matches) > 1:
                # We have a multiple-match
                syscmd = yield cmdset.get(CMD_MULTIMATCH)
                sysarg = _("There were multiple matches.")
                if syscmd:
                    # use custom CMD_MULTIMATCH
                    syscmd.matches = matches
                else:
                    # fall back to default error handling
                    sysarg = yield at_multimatch_cmd(caller, matches)
                raise ExecSystemCommand(syscmd, sysarg)

            if len(matches) == 1:
                # We have a unique command match. But it may still be invalid.
                match = matches[0]
                cmdname, args, cmd = match[0], match[1], match[2]

                # check if we allow this type of command
                if cmdset.no_channels and hasattr(cmd, "is_channel") and cmd.is_channel:
                    matches = []
                if cmdset.no_exits and hasattr(cmd, "is_exit") and cmd.is_exit:
                    matches = []

            if not matches:
                # No commands match our entered command
                syscmd = yield cmdset.get(CMD_NOMATCH)
                if syscmd:
                    # use custom CMD_NOMATH command
                    sysarg = raw_string
                else:
                    # fallback to default error text
                    sysarg = _("Command '%s' is not available.") % raw_string
                    suggestions = string_suggestions(raw_string,
                                    cmdset.get_all_cmd_keys_and_aliases(caller),
                                    cutoff=0.7, maxnum=3)
                    if suggestions:
                        sysarg += _(" Maybe you meant %s?") % utils.list_to_string(suggestions, _('or'), addquote=True)
                    else:
                        sysarg += _(" Type \"help\" for help.")
                raise ExecSystemCommand(syscmd, sysarg)

            # Check if this is a Channel-cmd match.
            if hasattr(cmd, 'is_channel') and cmd.is_channel:
                # even if a user-defined syscmd is not defined, the
                # found cmd is already a system command in its own right.
                syscmd = yield cmdset.get(CMD_CHANNEL)
                if syscmd:
                    # replace system command with custom version
                    cmd = syscmd
                cmd.sessid = session.sessid if session else None
                sysarg = "%s:%s" % (cmdname, args)
                raise ExecSystemCommand(cmd, sysarg)

            # A normal command.

            # Assign useful variables to the instance
            cmd.caller = caller
            cmd.cmdstring = cmdname
            cmd.args = args
            cmd.cmdset = cmdset
            cmd.sessid = session.sessid if session else sessid
            cmd.session = session
            cmd.player = player
            cmd.raw_string = unformatted_raw_string
            #cmd.obj  # set via on-object cmdset handler for each command,
                      # since this may be different for every command when
                      # merging multuple cmdsets

            if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'):
                # cmd.obj is automatically made available by the cmdhandler.
                # we make sure to validate its scripts.
                yield cmd.obj.scripts.validate()

            if _testing:
                # only return the command instance
                returnValue(cmd)

            # assign custom kwargs to found cmd object
            for key, val in kwargs.items():
                setattr(cmd, key, val)

            # pre-command hook
            abort = yield cmd.at_pre_cmd()
            if abort:
                # abort sequence
                returnValue(abort)

            # Parse and execute
            yield cmd.parse()
            # (return value is normally None)
            ret = yield cmd.func()

            # post-command hook
            yield cmd.at_post_cmd()

            if cmd.save_for_next:
                # store a reference to this command, possibly
                # accessible by the next command.
                caller.ndb.last_cmd = yield copy(cmd)
            else:
                caller.ndb.last_cmd = None

            # Done! This returns a deferred. By default, Evennia does
            # not use this at all.
            returnValue(ret)

        except ExecSystemCommand, exc:
            # Not a normal command: run a system command, if available,
            # or fall back to a return string.
            syscmd = exc.syscmd
            sysarg = exc.sysarg
            if syscmd:
                syscmd.caller = caller
                syscmd.cmdstring = syscmd.key
                syscmd.args = sysarg
                syscmd.cmdset = cmdset
                syscmd.sessid = session.sessid if session else None
                syscmd.raw_string = unformatted_raw_string

                if hasattr(syscmd, 'obj') and hasattr(syscmd.obj, 'scripts'):
                    # cmd.obj is automatically made available.
                    # we make sure to validate its scripts.
                    yield syscmd.obj.scripts.validate()

                if _testing:
                    # only return the command instance
                    returnValue(syscmd)

                # parse and run the command
                yield syscmd.parse()
                yield syscmd.func()
            elif sysarg:
                # return system arg
                caller.msg(exc.sysarg)

        except NoCmdSets:
            # Critical error.
            string = "No command sets found! This is a sign of a critical bug.\n"
            string += "The error was logged.\n"
            string += "If logging out/in doesn't solve the problem, try to "
            string += "contact the server admin through some other means "
            string += "for assistance."
            caller.msg(_(string))
            logger.log_errmsg("No cmdsets found: %s" % caller)

        except Exception:
            # We should not end up here. If we do, it's a programming bug.
            string = "%s\nAbove traceback is from an untrapped error."
            string += " Please file a bug report."
            logger.log_trace(_(string))
            caller.msg(string % format_exc())
Exemplo n.º 41
0
    def func(self):
        "Do checks and create account"

        session = self.caller
        args = self.args.strip()

        # extract quoted parts
        parts = [part.strip() for part in re.split(r"\"|\'", args) if part.strip()]
        if len(parts) == 1:
            # this was (hopefully) due to no quotes being found
            parts = parts[0].split(None, 1)
        if len(parts) != 2:
            string = "\n Usage (without <>): create <name> <password>"
            string += "\nIf <name> or <password> contains spaces, enclose it in quotes."
            session.msg(string)
            return
        playername, password = parts

        # sanity checks
        if not re.findall('^[\w. @+-]+$', playername) or not (0 < len(playername) <= 30):
            # this echoes the restrictions made by django's auth
            # module (except not allowing spaces, for convenience of
            # logging in).
            string = "\n\r Playername can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only."
            session.msg(string)
            return
        # strip excessive spaces in playername
        playername = re.sub(r"\s+", " ", playername).strip()
        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
        if not re.findall('^[\w. @+-]+$', password) or not (3 < len(password)):
            string = "\n\r Password should be longer than 3 characers. Letters, spaces, digits and @\.\+\-\_ only."
            string += "\nFor best security, make it longer than 8 characters. You can also use a phrase of"
            string += "\nmany words if you enclose the password in quotes."
            session.msg(string)
            return

        # everything's ok. Create the new player account.
        try:
            default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)

            typeclass = settings.BASE_CHARACTER_TYPECLASS
            permissions = settings.PERMISSION_PLAYER_DEFAULT

            try:
                new_player = create.create_player(playername, None, password,
                                                     permissions=permissions)

            except Exception, e:
                session.msg("There was an error creating the default Player/Character:\n%s\n If this problem persists, contact an admin." % e)
                logger.log_trace()
                return

            # This needs to be called so the engine knows this player is
            # logging in for the first time. (so it knows to call the right
            # hooks during login later)
            utils.init_new_player(new_player)

            # join the new player to the public channel
            pchanneldef = settings.CHANNEL_PUBLIC
            if pchanneldef:
                pchannel = ChannelDB.objects.get_channel(pchanneldef[0])
                if not pchannel.connect(new_player):
                    string = "New player '%s' could not connect to public channel!" % new_player.key
                    logger.log_errmsg(string)

            if MULTISESSION_MODE < 2:
                # if we only allow one character, create one with the same name as Player
                # (in mode 2, the character must be created manually once logging in)
                start_location = ObjectDB.objects.get_id(settings.START_LOCATION)
                if not start_location:
                    start_location = default_home # fallback

                new_character = create.create_object(typeclass, key=playername,
                                          location=start_location, home=default_home,
                                          permissions=permissions)
                # set playable character list
                new_player.db._playable_characters.append(new_character)

                # allow only the character itself and the player to puppet this character (and Immortals).
                new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" %
                                        (new_character.id, new_player.id))

                # If no description is set, set a default description
                if not new_character.db.desc:
                    new_character.db.desc = "This is a Player."
                # We need to set this to have @ic auto-connect to this character
                new_player.db._last_puppet = new_character

            # tell the caller everything went well.
            string = "A new account '%s' was created. Welcome!"
            if " " in playername:
                string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'."
            else:
                string += "\n\nYou can now log with the command 'connect %s <your password>'."
            session.msg(string % (playername, playername))
Exemplo n.º 42
0
    def func(self):
        "Do checks and create account"

        session = self.caller
        args = self.args.strip()

        # extract quoted parts
        parts = [
            part.strip() for part in re.split(r"\"|\'", args) if part.strip()
        ]
        if len(parts) == 1:
            # this was (hopefully) due to no quotes being found
            parts = parts[0].split(None, 1)
        if len(parts) != 2:
            string = "\n Usage (without <>): create <name> <password>"
            string += "\nIf <name> or <password> contains spaces, enclose it in quotes."
            session.msg(string)
            return
        playername, password = parts
        print "playername '%s', password: '******'" % (playername, password)

        # sanity checks
        if not re.findall('^[\w. @+-]+$',
                          playername) or not (0 < len(playername) <= 30):
            # this echoes the restrictions made by django's auth module (except not
            # allowing spaces, for convenience of logging in).
            string = "\n\r Playername can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only."
            session.msg(string)
            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):
            # player already exists (we also ignore capitalization here)
            session.msg(
                "Sorry, there is already a player with the name '%s'." %
                playername)
            return
        if not re.findall('^[\w. @+-]+$', password) or not (3 < len(password)):
            string = "\n\r Password should be longer than 3 characers. Letters, spaces, digits and @\.\+\-\_ only."
            string += "\nFor best security, make it longer than 8 characters. You can also use a phrase of"
            string += "\nmany words if you enclose the password in quotes."
            session.msg(string)
            return

        # everything's ok. Create the new player account.
        try:
            default_home = ObjectDB.objects.get_id(
                settings.CHARACTER_DEFAULT_HOME)

            typeclass = settings.BASE_CHARACTER_TYPECLASS
            permissions = settings.PERMISSION_PLAYER_DEFAULT

            try:
                new_character = create.create_player(
                    playername,
                    None,
                    password,
                    permissions=permissions,
                    character_typeclass=typeclass,
                    character_location=default_home,
                    character_home=default_home)
            except Exception:
                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

            # This needs to be called so the engine knows this player is logging in for the first time.
            # (so it knows to call the right hooks during login later)
            utils.init_new_player(new_player)

            # join the new player to the public channel
            pchanneldef = settings.CHANNEL_PUBLIC
            if pchanneldef:
                pchannel = Channel.objects.get_channel(pchanneldef[0])
                if not pchannel.connect_to(new_player):
                    string = "New player '%s' could not connect to public channel!" % new_player.key
                    logger.log_errmsg(string)

            # allow only the character itself and the player to puppet this character (and Immortals).
            new_character.locks.add(
                "puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)"
                % (new_character.id, new_player.id))

            # If no description is set, set a default description
            if not new_character.db.desc:
                new_character.db.desc = "This is a Player."

            # tell the caller everything went well.
            string = "A new account '%s' was created. Welcome!"
            if " " in playername:
                string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'."
            else:
                string += "\n\nYou can now log with the command 'connect %s <your password>'."
            session.msg(string % (playername, playername))

        except Exception:
            # We are in the middle between logged in and -not, so we have to handle tracebacks
            # ourselves at this point. If we don't, we won't see any errors at all.
            string = "%s\nThis is a bug. Please e-mail an admin if the problem persists."
            session.msg(string % (traceback.format_exc()))
            logger.log_errmsg(traceback.format_exc())
Exemplo n.º 43
0
def cmdhandler(called_by,
               raw_string,
               _testing=False,
               callertype="session",
               sessid=None,
               **kwargs):
    """
    This is the main function to handle any string sent to the engine.

    called_by - object on which this was called from. This is either a Session, a Player or an Object.
    raw_string - the command string given on the command line
    _testing - if we should actually execute the command or not.
              if True, the command instance will be returned instead.
    callertype - this is one of "session", "player" or "object", in decending
                 order. So when the Session is the caller, it will merge its
                 own cmdset into cmdsets from both Player and eventual puppeted
                 Object (and cmdsets in its room etc). A Player will only
                 include its own cmdset and the Objects and so on. Merge order
                 is the same order, so that Object cmdsets are merged in last,
                 giving them precendence for same-name and same-prio commands.
    sessid - Relevant if callertype is "player" - the session id will help
             retrieve the correct cmdsets from puppeted objects.
    **kwargs - other keyword arguments will be assigned as named variables on the
               retrieved command object *before* it is executed. This is unuesed
               in default Evennia but may be used by code to set custom flags or
               special operating conditions for a command as it executes.

    Note that this function returns a deferred!
    """

    raw_string = to_unicode(raw_string, force_string=True)

    session, player, obj = None, None, None
    if callertype == "session":
        session = called_by
        player = session.player
        if player:
            obj = yield _GA(player.dbobj, "get_puppet")(session.sessid)
    elif callertype == "player":
        player = called_by
        if sessid:
            obj = yield _GA(player.dbobj, "get_puppet")(sessid)
    elif callertype == "object":
        obj = called_by
    else:
        raise RuntimeError("cmdhandler: callertype %s is not valid." %
                           callertype)

    # the caller will be the one to receive messages and excert its permissions.
    # we assign the caller with preference 'bottom up'
    caller = obj or player or session

    try:  # catch bugs in cmdhandler itself
        try:  # catch special-type commands

            cmdset = yield get_and_merge_cmdsets(caller, session, player, obj,
                                                 callertype, sessid)
            if not cmdset:
                # this is bad and shouldn't happen.
                raise NoCmdSets
            unformatted_raw_string = raw_string
            raw_string = raw_string.strip()
            if not raw_string:
                # Empty input. Test for system command instead.
                syscmd = yield cmdset.get(CMD_NOINPUT)
                sysarg = ""
                raise ExecSystemCommand(syscmd, sysarg)
            # Parse the input string and match to available cmdset.
            # This also checks for permissions, so all commands in match
            # are commands the caller is allowed to call.
            matches = yield _COMMAND_PARSER(raw_string, cmdset, caller)

            # Deal with matches

            if len(matches) > 1:
                # We have a multiple-match
                syscmd = yield cmdset.get(CMD_MULTIMATCH)
                sysarg = _("There were multiple matches.")
                if syscmd:
                    # use custom CMD_MULTIMATCH
                    syscmd.matches = matches
                else:
                    # fall back to default error handling
                    sysarg = yield at_multimatch_cmd(caller, matches)
                raise ExecSystemCommand(syscmd, sysarg)

            if len(matches) == 1:
                # We have a unique command match. But it may still be invalid.
                match = matches[0]
                cmdname, args, cmd = match[0], match[1], match[2]

                # check if we allow this type of command
                if cmdset.no_channels and hasattr(
                        cmd, "is_channel") and cmd.is_channel:
                    matches = []
                if cmdset.no_exits and hasattr(cmd, "is_exit") and cmd.is_exit:
                    matches = []

            if not matches:
                # No commands match our entered command
                syscmd = yield cmdset.get(CMD_NOMATCH)
                if syscmd:
                    # use custom CMD_NOMATH command
                    sysarg = raw_string
                else:
                    # fallback to default error text
                    sysarg = _("Command '%s' is not available.") % raw_string
                    suggestions = string_suggestions(
                        raw_string,
                        cmdset.get_all_cmd_keys_and_aliases(caller),
                        cutoff=0.7,
                        maxnum=3)
                    if suggestions:
                        sysarg += _(
                            " Maybe you meant %s?") % utils.list_to_string(
                                suggestions, _('or'), addquote=True)
                    else:
                        sysarg += _(" Type \"help\" for help.")
                raise ExecSystemCommand(syscmd, sysarg)

            # Check if this is a Channel-cmd match.
            if hasattr(cmd, 'is_channel') and cmd.is_channel:
                # even if a user-defined syscmd is not defined, the
                # found cmd is already a system command in its own right.
                syscmd = yield cmdset.get(CMD_CHANNEL)
                if syscmd:
                    # replace system command with custom version
                    cmd = syscmd
                cmd.sessid = session.sessid if session else None
                sysarg = "%s:%s" % (cmdname, args)
                raise ExecSystemCommand(cmd, sysarg)

            # A normal command.

            # Assign useful variables to the instance
            cmd.caller = caller
            cmd.cmdstring = cmdname
            cmd.args = args
            cmd.cmdset = cmdset
            cmd.sessid = session.sessid if session else sessid
            cmd.session = session
            cmd.player = player
            cmd.raw_string = unformatted_raw_string
            #cmd.obj  # set via on-object cmdset handler for each command,
            # since this may be different for every command when
            # merging multuple cmdsets

            if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'):
                # cmd.obj is automatically made available by the cmdhandler.
                # we make sure to validate its scripts.
                yield cmd.obj.scripts.validate()

            if _testing:
                # only return the command instance
                returnValue(cmd)

            # assign custom kwargs to found cmd object
            for key, val in kwargs.items():
                setattr(cmd, key, val)

            # pre-command hook
            abort = yield cmd.at_pre_cmd()
            if abort:
                # abort sequence
                returnValue(abort)

            # Parse and execute
            yield cmd.parse()
            # (return value is normally None)
            ret = yield cmd.func()

            # post-command hook
            yield cmd.at_post_cmd()

            if cmd.save_for_next:
                # store a reference to this command, possibly
                # accessible by the next command.
                caller.ndb.last_cmd = yield copy(cmd)
            else:
                caller.ndb.last_cmd = None

            # Done! This returns a deferred. By default, Evennia does
            # not use this at all.
            returnValue(ret)

        except ExecSystemCommand, exc:
            # Not a normal command: run a system command, if available,
            # or fall back to a return string.
            syscmd = exc.syscmd
            sysarg = exc.sysarg
            if syscmd:
                syscmd.caller = caller
                syscmd.cmdstring = syscmd.key
                syscmd.args = sysarg
                syscmd.cmdset = cmdset
                syscmd.sessid = session.sessid if session else None
                syscmd.raw_string = unformatted_raw_string

                if hasattr(syscmd, 'obj') and hasattr(syscmd.obj, 'scripts'):
                    # cmd.obj is automatically made available.
                    # we make sure to validate its scripts.
                    yield syscmd.obj.scripts.validate()

                if _testing:
                    # only return the command instance
                    returnValue(syscmd)

                # parse and run the command
                yield syscmd.parse()
                yield syscmd.func()
            elif sysarg:
                # return system arg
                caller.msg(exc.sysarg)

        except NoCmdSets:
            # Critical error.
            string = "No command sets found! This is a sign of a critical bug.\n"
            string += "The error was logged.\n"
            string += "If logging out/in doesn't solve the problem, try to "
            string += "contact the server admin through some other means "
            string += "for assistance."
            caller.msg(_(string))
            logger.log_errmsg("No cmdsets found: %s" % caller)

        except Exception:
            # We should not end up here. If we do, it's a programming bug.
            string = "%s\nAbove traceback is from an untrapped error."
            string += " Please file a bug report."
            logger.log_trace(_(string))
            caller.msg(string % format_exc())
Exemplo n.º 44
0
def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False):
    """
    This helper function is used by the cmdsethandler to load a cmdset
    instance from a python module, given a python_path. It's usually accessed
    through the cmdsethandler's add() and add_default() methods.
    path - This is the full path to the cmdset object on python dot-form
    cmdsetobj - the database object/typeclass on which this cmdset is to be
            assigned (this can be also channels and exits, as well as players
            but there will always be such an object)
    emit_to_obj - if given, error is emitted to this object (in addition
                  to logging)
    no_logging - don't log/send error messages. This can be useful
                if import_cmdset is just used to check if this is a
                valid python path or not.
    function returns None if an error was encountered or path not found.
    """

    python_paths = [path
                    ] + ["%s.%s" % (prefix, path) for prefix in _CMDSET_PATHS]
    errstring = ""
    for python_path in python_paths:
        try:
            #print "importing %s: _CACHED_CMDSETS=%s" % (python_path, _CACHED_CMDSETS)
            wanted_cache_key = python_path
            cmdsetclass = _CACHED_CMDSETS.get(wanted_cache_key, None)
            errstring = ""
            if not cmdsetclass:
                #print "cmdset '%s' not in cache. Reloading %s on %s." % (wanted_cache_key, python_path, cmdsetobj)
                # Not in cache. Reload from disk.
                modulepath, classname = python_path.rsplit('.', 1)
                module = __import__(modulepath, fromlist=[True])
                cmdsetclass = module.__dict__[classname]
                _CACHED_CMDSETS[wanted_cache_key] = cmdsetclass
            #instantiate the cmdset (and catch its errors)
            if callable(cmdsetclass):
                cmdsetclass = cmdsetclass(cmdsetobj)
            return cmdsetclass

        except ImportError:
            errstring += _(
                "Error loading cmdset: Couldn't import module '%s'.")
            errstring = errstring % modulepath
        except KeyError:
            errstring += _(
                "Error in loading cmdset: No cmdset class '%(classname)s' in %(modulepath)s."
            )
            errstring = errstring % {
                "classname": classname,
                "modulepath": modulepath
            }
        except Exception:
            errstring += _(
                "Compile/Run error when loading cmdset '%s'. Error was logged."
            )
            errstring = errstring % (python_path)

    if errstring:
        # returning an empty error cmdset
        if not no_logging:
            logger.log_errmsg(errstring)
            if emit_to_obj and not ServerConfig.objects.conf(
                    "server_starting_mode"):
                emit_to_obj.msg(errstring)
        err_cmdset = _ErrorCmdSet()
        err_cmdset.errmessage = errstring
        return err_cmdset
Exemplo n.º 45
0
 def clientConnectionFailed(self, connector, reason):
     msg = _("Could not connect %(key)s Reason: '%(reason)s'") % {"key":self.pretty_key, "reason":reason}
     msg_info(msg)
     logger.log_errmsg(msg)
Exemplo n.º 46
0
    def func(self):
        "Do checks and create account"

        session = self.caller
        args = self.args.strip()

        # extract quoted parts
        parts = [part.strip() for part in re.split(r"\"|\'", args) if part.strip()]
        if len(parts) == 1:
            # this was (hopefully) due to no quotes being found
            parts = parts[0].split(None, 1)
        if len(parts) != 2:
            string = "\n Usage (without <>): create <name> <password>"
            string += "\nIf <name> or <password> contains spaces, enclose it in quotes."
            session.msg(string)
            return
        playername, password = parts
        print "playername '%s', password: '******'" % (playername, password)

        # sanity checks
        if not re.findall('^[\w. @+-]+$', playername) or not (0 < len(playername) <= 30):
            # this echoes the restrictions made by django's auth module (except not
            # allowing spaces, for convenience of logging in).
            string = "\n\r Playername can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only."
            session.msg(string)
            return
        # strip excessive spaces in playername
        playername = re.sub(r"\s+", " ", playername).strip()
        if PlayerDB.objects.filter(user__username__iexact=playername) or 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
        if not re.findall('^[\w. @+-]+$', password) or not (3 < len(password)):
            string = "\n\r Password should be longer than 3 characers. Letters, spaces, digits and @\.\+\-\_ only."
            string += "\nFor best security, make it longer than 8 characters. You can also use a phrase of"
            string += "\nmany words if you enclose the password in quotes."
            session.msg(string)
            return

        # everything's ok. Create the new player account.
        try:
            default_home = ObjectDB.objects.get_id(settings.CHARACTER_DEFAULT_HOME)

            typeclass = settings.BASE_CHARACTER_TYPECLASS
            permissions = settings.PERMISSION_PLAYER_DEFAULT

            try:
                new_character = create.create_player(playername, None, password,
                                                     permissions=permissions,
                                                     character_typeclass=typeclass,
                                                     character_location=default_home,
                                                     character_home=default_home)
            except Exception:
                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

            # This needs to be called so the engine knows this player is logging in for the first time.
            # (so it knows to call the right hooks during login later)
            utils.init_new_player(new_player)

            # join the new player to the public channel
            pchanneldef = settings.CHANNEL_PUBLIC
            if pchanneldef:
                pchannel = Channel.objects.get_channel(pchanneldef[0])
                if not pchannel.connect_to(new_player):
                    string = "New player '%s' could not connect to public channel!" % new_player.key
                    logger.log_errmsg(string)

            # allow only the character itself and the player to puppet this character (and Immortals).
            new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" %
                                    (new_character.id, new_player.id))


            # If no description is set, set a default description
            if not new_character.db.desc:
                new_character.db.desc = "This is a Player."

            # tell the caller everything went well.
            string = "A new account '%s' was created. Welcome!"
            if " " in playername:
                string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'."
            else:
                string += "\n\nYou can now log with the command 'connect %s <your password>'."
            session.msg(string % (playername, playername))

        except Exception:
            # We are in the middle between logged in and -not, so we have to handle tracebacks
            # ourselves at this point. If we don't, we won't see any errors at all.
            string = "%s\nThis is a bug. Please e-mail an admin if the problem persists."
            session.msg(string % (traceback.format_exc()))
            logger.log_errmsg(traceback.format_exc())
Exemplo n.º 47
0
 def errback(fail):
     logger.log_errmsg(fail.value)
Exemplo n.º 48
0
                "modulepath": modulepath
            }
        except SyntaxError, e:
            logger.log_trace()
            errstring += _(
                "SyntaxError encountered when loading cmdset '%s': %s.")
            errstring = errstring % (modulepath, e)
        except Exception, e:
            logger.log_trace()
            errstring += _("Compile/Run error when loading cmdset '%s': %s.")
            errstring = errstring % (python_path, e)

    if errstring:
        # returning an empty error cmdset
        if not no_logging:
            logger.log_errmsg(errstring)
            if emit_to_obj and not ServerConfig.objects.conf(
                    "server_starting_mode"):
                emit_to_obj.msg(errstring)
        err_cmdset = _ErrorCmdSet()
        err_cmdset.errmessage = errstring + _("\n (See log for details.)")
        return err_cmdset


# classes


class CmdSetHandler(object):
    """
    The CmdSetHandler is always stored on an object, this object is supplied
    as an argument.
Exemplo n.º 49
0
 def errback(fail):
     logger.log_errmsg(fail.value)
Exemplo n.º 50
0
 def clientConnectionLost(self, connector, reason):
     message = _('Connection lost: %s') % reason.getErrorMessage()
     msg_info(message)
     logger.log_errmsg('IMC2: %s' % message)
Exemplo n.º 51
0
    def func(self):
        "Do checks and create account"

        session = self.caller
        args = self.args.strip()

        # extract quoted parts
        parts = [part.strip() for part in re.split(r"\"|\'", args) if part.strip()]
        if len(parts) == 1:
            # this was (hopefully) due to no quotes being found
            parts = parts[0].split(None, 1)
        if len(parts) != 2:
            string = "\n Usage (without <>): create <name> <password>"
            string += "\nIf <name> or <password> contains spaces, enclose it in quotes."
            session.msg(string)
            return
        playername, password = parts

        # sanity checks
        if not re.findall('^[\w. @+-]+$', playername) or not (0 < len(playername) <= 30):
            # this echoes the restrictions made by django's auth
            # module (except not allowing spaces, for convenience of
            # logging in).
            string = "\n\r Playername can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only."
            session.msg(string)
            return
        # strip excessive spaces in playername
        playername = re.sub(r"\s+", " ", playername).strip()
        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
        # Reserve playernames found in GUEST_LIST
        if settings.GUEST_LIST and playername.lower() in map(str.lower, settings.GUEST_LIST):
            string = "\n\r That name is reserved. Please choose another Playername."
            session.msg(string)
            return
        if not re.findall('^[\w. @+-]+$', password) or not (3 < len(password)):
            string = "\n\r Password should be longer than 3 characers. Letters, spaces, digits and @\.\+\-\_ only."
            string += "\nFor best security, make it longer than 8 characters. You can also use a phrase of"
            string += "\nmany words if you enclose the password in quotes."
            session.msg(string)
            return

        # Check IP and/or name bans
        bans = ServerConfig.objects.conf("server_bans")
        if bans and (any(tup[0]==playername.lower() for tup in bans)
                     or
                     any(tup[2].match(session.address) for tup in bans if tup[2])):
            # this is a banned IP or name!
            string = "{rYou have been banned and cannot continue from here."
            string += "\nIf you feel this ban is in error, please email an admin.{x"
            session.msg(string)
            session.execute_cmd("quit")
            return

        # everything's ok. Create the new player account.
        try:
            default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
            permissions = settings.PERMISSION_PLAYER_DEFAULT
            typeclass = settings.BASE_CHARACTER_TYPECLASS
            new_player = _create_player(session, playername, password, default_home, permissions)
            start_location = ObjectDB.objects.get_id(settings.START_LOCATION)
            if new_player:
                if MULTISESSION_MODE < 2:
                    _create_character(session, new_player, typeclass, start_location,
                                    default_home, permissions)
                # tell the caller everything went well.
                string = "A new account '%s' was created. Welcome!"
                if " " in playername:
                    string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'."
                else:
                    string += "\n\nYou can now log with the command 'connect %s <your password>'."
                session.msg(string % (playername, playername))

        except Exception:
            # We are in the middle between logged in and -not, so we have
            # to handle tracebacks ourselves at this point. If we don't,
            # we won't see any errors at all.
            string = "%s\nThis is a bug. Please e-mail an admin if the problem persists."
            session.msg(string % (traceback.format_exc()))
            logger.log_errmsg(traceback.format_exc())
Exemplo n.º 52
0
    def func(self):
        """
        Uses the Django admin api. Note that unlogged-in commands
        have a unique position in that their func() receives
        a session object instead of a source_object like all
        other types of logged-in commands (this is because
        there is no object yet before the player has logged in)
        """

        session = self.caller
        args = self.args
        # extract quoted parts
        parts = [part.strip() for part in re.split(r"\"|\'", args) if part.strip()]
        if len(parts) == 1:
            # this was (hopefully) due to no quotes being found, or a guest login
            parts = parts[0].split(None, 1)
            # Guest login
            if len(parts) == 1 and parts[0].lower() == "guest" and settings.GUEST_ENABLED:
                try:
                    # Find an available guest name.
                    for playername in settings.GUEST_LIST:
                        if not PlayerDB.objects.filter(username__iexact=playername):
                            break
                        playername = None
                    if playername == None:
                        session.msg("All guest accounts are in use. Please try again later.")
                        return

                    password       = "******" % getrandbits(64)
                    home           = ObjectDB.objects.get_id(settings.GUEST_HOME)
                    permissions    = settings.PERMISSION_GUEST_DEFAULT
                    typeclass      = settings.BASE_CHARACTER_TYPECLASS
                    ptypeclass     = settings.BASE_GUEST_TYPECLASS
                    start_location = ObjectDB.objects.get_id(settings.GUEST_START_LOCATION)

                    new_player = _create_player(session, playername, password,
                                              home, permissions, ptypeclass)
                    if new_player:
                        _create_character(session, new_player, typeclass, start_location,
                                        home, permissions)
                        session.sessionhandler.login(session, new_player)

                except Exception:
                # We are in the middle between logged in and -not, so we have
                # to handle tracebacks ourselves at this point. If we don't,
                # we won't see any errors at all.
                    string = "%s\nThis is a bug. Please e-mail an admin if the problem persists."
                    session.msg(string % (traceback.format_exc()))
                    logger.log_errmsg(traceback.format_exc())
                finally:
                    return

        if len(parts) != 2:
            session.msg("\n\r Usage (without <>): connect <name> <password>")
            return
        playername, password = parts

        # Match account name and check password
        player = PlayerDB.objects.get_player_from_name(playername)
        pswd = None
        if player:
            pswd = player.check_password(password)

        if not (player and pswd):
        # No playername or password match
            string = "Wrong login information given.\nIf you have spaces in your name or "
            string += "password, don't forget to enclose it in quotes. Also capitalization matters."
            string += "\nIf you are new you should first create a new account "
            string += "using the 'create' command."
            session.msg(string)
            return

        # Check IP and/or name bans
        bans = ServerConfig.objects.conf("server_bans")
        if bans and (any(tup[0]==player.name.lower() for tup in bans)
                     or
                     any(tup[2].match(session.address) for tup in bans if tup[2])):
            # this is a banned IP or name!
            string = "{rYou have been banned and cannot continue from here."
            string += "\nIf you feel this ban is in error, please email an admin.{x"
            session.msg(string)
            session.execute_cmd("quit")
            return

        # actually do the login. This will call all other hooks:
        #   session.at_login()
        #   player.at_init()  # always called when object is loaded from disk
        #   player.at_pre_login()
        #   player.at_first_login()  # only once
        #   player.at_post_login(sessid=sessid)
        session.sessionhandler.login(session, player)
Exemplo n.º 53
0
        session.msg("There was an error creating the Player:\n%s\n If this problem persists, contact an admin." % e)
        logger.log_trace()
        return False

    # This needs to be called so the engine knows this player is
    # logging in for the first time. (so it knows to call the right
    # hooks during login later)
    utils.init_new_player(new_player)

    # join the new player to the public channel
    pchanneldef = settings.CHANNEL_PUBLIC
    if pchanneldef:
        pchannel = ChannelDB.objects.get_channel(pchanneldef[0])
        if not pchannel.connect(new_player):
            string = "New player '%s' could not connect to public channel!" % new_player.key
            logger.log_errmsg(string)
    return new_player


def _create_character(session, new_player, typeclass, start_location, home, permissions):
    """
    Helper function, creates a character based on a player's name.
    This is meant for Guest and MULTISESSION_MODE < 2 situations.
    """
    try:
        if not start_location:
            start_location = home # fallback
        new_character = create.create_object(typeclass, key=new_player.key,
                                  location=start_location, home=home,
                                  permissions=permissions)
        # set playable character list
Exemplo n.º 54
0
    def func(self):
        "Do checks and create account"

        session = self.caller

        try:
            playername, email, password = self.playerinfo
        except ValueError:
            string = "\n\r Usage (without <>): create \"<playername>\" <email> <password>"
            session.msg(string)
            return
        if not re.findall('^[\w. @+-]+$',
                          playername) or not (0 < len(playername) <= 30):
            session.msg(
                "\n\r Playername can max be 30 characters or fewer. Letters, spaces, dig\
its and @/./+/-/_ only."
            )  # this echoes the restrictions made by django's auth module.
            return
        if not email or not password:
            session.msg(
                "\n\r You have to supply an e-mail address followed by a password."
            )
            return

        if not utils.validate_email_address(email):
            # check so the email at least looks ok.
            session.msg("'%s' is not a valid e-mail address." % email)
            return

        # Run sanity and security checks

        if PlayerDB.objects.filter(username=playername):
            # player already exists
            session.msg(
                "Sorry, there is already a player with the name '%s'." %
                playername)
            return
        if PlayerDB.objects.get_player_from_email(email):
            # email already set on a player
            session.msg(
                "Sorry, there is already a player with that email address.")
            return
        if len(password) < 3:
            # too short password
            string = "Your password must be at least 3 characters or longer."
            string += "\n\rFor best security, make it at least 8 characters long, "
            string += "avoid making it a real word and mix numbers into it."
            session.msg(string)
            return

        # everything's ok. Create the new player account.
        try:
            default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)

            typeclass = settings.BASE_CHARACTER_TYPECLASS
            permissions = settings.PERMISSION_PLAYER_DEFAULT

            try:
                new_player = create.create_player(playername,
                                                  email,
                                                  password,
                                                  permissions=permissions)

            except Exception, e:
                session.msg(
                    "There was an error creating the default Player/Character:\n%s\n If this problem persists, contact an admin."
                    % e)
                logger.log_trace()
                return

            # This needs to be called so the engine knows this player is
            # logging in for the first time. (so it knows to call the right
            # hooks during login later)
            utils.init_new_player(new_player)

            # join the new player to the public channel
            pchanneldef = settings.CHANNEL_PUBLIC
            if pchanneldef:
                pchannel = ChannelDB.objects.get_channel(pchanneldef[0])
                if not pchannel.connect(new_player):
                    string = "New player '%s' could not connect to public channel!" % new_player.key
                    logger.log_errmsg(string)

            if MULTISESSION_MODE < 2:
                # if we only allow one character, create one with the same name as Player
                # (in mode 2, the character must be created manually once logging in)
                new_character = create.create_object(typeclass,
                                                     key=playername,
                                                     location=default_home,
                                                     home=default_home,
                                                     permissions=permissions)
                # set playable character list
                new_player.db._playable_characters.append(new_character)

                # allow only the character itself and the player to puppet this character (and Immortals).
                new_character.locks.add(
                    "puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)"
                    % (new_character.id, new_player.id))

                # If no description is set, set a default description
                if not new_character.db.desc:
                    new_character.db.desc = "This is a Player."
                # We need to set this to have @ic auto-connect to this character
                new_player.db._last_puppet = new_character

            # tell the caller everything went well.
            string = "A new account '%s' was created. Welcome!"
            if " " in playername:
                string += "\n\nYou can now log in with the command 'connect %s <your password>'."
            else:
                string += "\n\nYou can now log with the command 'connect %s <your password>'."
            session.msg(string % (playername, email))