Beispiel #1
0
 def _validate(data):
     "Helper function to convert data to AMP-safe (picketable) values"
     if isinstance(data, dict):
         newdict = {}
         for key, part in data.items():
             newdict[key] = _validate(part)
         return newdict
     elif hasattr(data, "__iter__"):
         return [_validate(part) for part in data]
     elif isinstance(data, basestring):
         # make sure strings are in a valid encoding
         try:
             data = data and to_str(to_unicode(data), encoding=session.protocol_flags["ENCODING"])
         except LookupError:
             # wrong encoding set on the session. Set it to a safe one
             session.protocol_flags["ENCODING"] = "utf-8"
             data = to_str(to_unicode(data), encoding=session.protocol_flags["ENCODING"])
         if _INLINEFUNC_ENABLED and not raw and isinstance(self, ServerSessionHandler):
             # only parse inlinefuncs on the outgoing path (sessionhandler->)
             data = parse_inlinefunc(data, strip=strip_inlinefunc, session=session)
         return data
     elif hasattr(data, "id") and hasattr(data, "db_date_created") \
             and hasattr(data, '__dbclass__'):
         # convert database-object to their string representation.
         return _validate(unicode(data))
     else:
         return data
Beispiel #2
0
 def _validate(data):
     "Helper function to convert data to AMP-safe (picketable) values"
     if isinstance(data, dict):
         newdict = {}
         for key, part in data.items():
             newdict[key] = _validate(part)
         return newdict
     elif hasattr(data, "__iter__"):
         return [_validate(part) for part in data]
     elif isinstance(data, basestring):
         # make sure strings are in a valid encoding
         try:
             data = data and to_str(to_unicode(data), encoding=session.protocol_flags["ENCODING"])
         except LookupError:
             # wrong encoding set on the session. Set it to a safe one
             session.protocol_flags["ENCODING"] = "utf-8"
             data = to_str(to_unicode(data), encoding=session.protocol_flags["ENCODING"])
         if _INLINEFUNC_ENABLED and not raw and isinstance(self, ServerSessionHandler):
             # only parse inlinefuncs on the outgoing path (sessionhandler->)
             data = parse_inlinefunc(data, strip=strip_inlinefunc, session=session)
         # At this point the object is certainly the right encoding, but may still be a unicode object--
         # to_str does not actually force objects to become bytestrings.
         # If the unicode object is a subclass of unicode, such as ANSIString, this can cause a problem,
         # as special behavior for that class will still be in play. Since we're now transferring raw data,
         # we must now force this to be a proper bytestring.
         return str(data)
     elif hasattr(data, "id") and hasattr(data, "db_date_created") \
             and hasattr(data, '__dbclass__'):
         # convert database-object to their string representation.
         return _validate(unicode(data))
     else:
         return data
Beispiel #3
0
    def data_in(self, sessid, text="", **kwargs):
        """
        Data Portal -> Server.
        We also intercept OOB communication here.

        Args:
            sessid (int): Session id.

        Kwargs:
            text (str): Text from protocol.
            kwargs (any): Other data from protocol.

        """
        #from evennia.server.profiling.timetrace import timetrace
        #text = timetrace(text, "ServerSessionHandler.data_in")
        session = self.sessions.get(sessid, None)
        if session:
            text = text and to_unicode(strip_control_sequences(text), encoding=session.encoding)
            if "oob" in kwargs:
                # incoming data is always on the form (cmdname, args, kwargs)
                global _OOB_HANDLER
                if not _OOB_HANDLER:
                    from evennia.server.oobhandler import OOB_HANDLER as _OOB_HANDLER
                funcname, args, kwargs = kwargs.pop("oob")
                #print "OOB session.data_in:", funcname, args, kwargs
                if funcname:
                    _OOB_HANDLER.execute_cmd(session, funcname, *args, **kwargs)

            # pass the rest off to the session
            session.data_in(text=text, **kwargs)
Beispiel #4
0
 def validate_encoding(new_encoding):
     # helper: change encoding
     try:
         utils.to_str(utils.to_unicode("test-string"), encoding=new_encoding)
     except LookupError:
         raise RuntimeError("The encoding '|w%s|n' is invalid. " % new_encoding)
     return val
Beispiel #5
0
    def reload(self, filename=None, form=None, **kwargs):
        """
        Creates the form from a stored file name
        """
        # clean kwargs (these cannot be overridden)
        kwargs.pop("enforce_size", None)
        kwargs.pop("width", None)
        kwargs.pop("height", None)

        if form or self.input_form_dict:
            datadict = form if form else self.input_form_dict
            self.input_form_dict = datadict
        elif filename or self.filename:
            filename = filename if filename else self.filename
            datadict = all_from_module(filename)
            self.filename = filename
        else:
            datadict = {}

        cellchar = to_str(datadict.get("FORMCHAR", "x"))
        self.cellchar = to_str(cellchar[0] if len(cellchar) > 1 else cellchar)
        tablechar = datadict.get("TABLECHAR", "c")
        self.tablechar = tablechar[0] if len(tablechar) > 1 else tablechar

        # split into a list of list of lines. Form can be indexed with form[iy][ix]
        self.raw_form = _to_ansi(to_unicode(datadict.get("FORM", "")).split("\n"))
        # strip first line
        self.raw_form = self.raw_form[1:] if self.raw_form else self.raw_form

        self.options.update(kwargs)

        # parse and replace
        self.mapping = self._parse_rectangles(self.cellchar, self.tablechar, self.raw_form, **kwargs)
        self.form = self._populate_form(self.raw_form, self.mapping)
Beispiel #6
0
    def data_in(self, sessid, text="", **kwargs):
        """
        Data Portal -> Server.
        We also intercept OOB communication here.

        Args:
            sessid (int): Session id.

        Kwargs:
            text (str): Text from protocol.
            kwargs (any): Other data from protocol.

        """
        #from evennia.server.profiling.timetrace import timetrace
        #text = timetrace(text, "ServerSessionHandler.data_in")
        session = self.sessions.get(sessid, None)
        if session:
            text = text and to_unicode(strip_control_sequences(text),
                                       encoding=session.encoding)
            if "oob" in kwargs:
                # incoming data is always on the form (cmdname, args, kwargs)
                global _OOB_HANDLER
                if not _OOB_HANDLER:
                    from evennia.server.oobhandler import OOB_HANDLER as _OOB_HANDLER
                funcname, args, kwargs = kwargs.pop("oob")
                #print "OOB session.data_in:", funcname, args, kwargs
                if funcname:
                    _OOB_HANDLER.execute_cmd(session, funcname, *args,
                                             **kwargs)

            # pass the rest off to the session
            session.data_in(text=text, **kwargs)
Beispiel #7
0
 def validate_encoding(val):
     # helper: change encoding
     try:
         to_str(to_unicode("test-string"), encoding=val)
     except LookupError:
         raise RuntimeError("The encoding '|w%s|n' is invalid. " % val)
     return val
Beispiel #8
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
     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 evennia.utils import logger
         logger.log_errmsg(
             "The property '%s' does not support search criteria of the type %s."
             % (property_name, type(property_value)))
         return []
    def execute_cmd(self, raw_string, session=None, **kwargs):
        """
        Do something as this account. This method is never called normally,
        but only when the account object itself is supposed to execute the
        command. It takes account nicks into account, but not nicks of
        eventual puppets.

        Args:
            raw_string (str): Raw command input coming from the command line.
            session (Session, optional): The session to be responsible
                for the command-send

        Kwargs:
            kwargs (any): Other keyword arguments will be added to the
                found command object instance as variables before it
                executes. This is unused by default Evennia but may be
                used to set flags and change operating paramaters for
                commands at run-time.

        """
        raw_string = to_unicode(raw_string)
        raw_string = self.nicks.nickreplace(raw_string,
                                            categories=("inputline",
                                                        "channel"),
                                            include_account=False)
        if not session and _MULTISESSION_MODE in (0, 1):
            # for these modes we use the first/only session
            sessions = self.sessions.get()
            session = sessions[0] if sessions else None

        return cmdhandler.cmdhandler(self,
                                     raw_string,
                                     callertype="account",
                                     session=session,
                                     **kwargs)
Beispiel #10
0
    def get_objs_with_db_property_value(self, property_name, property_value, candidates=None, typeclasses=None):
        """
        Get objects with a specific field name and value.

        Args:
            property_name (str): Field name to search for.
            property_value (any): Value required for field with `property_name` to have.
            candidates (list, optional): List of objects to limit search to.
            typeclasses (list, optional): 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
        querykwargs = {property_name: property_value}
        cand_restriction = candidates is not 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 evennia.utils import logger
            logger.log_err("The property '%s' does not support search criteria of the type %s." %
                           (property_name, type(property_value)))
            return []
Beispiel #11
0
    def execute_cmd(self, raw_string, sessid=None, **kwargs):
        """
        Do something as this player. This method is never called normally,
        but only when the player object itself is supposed to execute the
        command. It takes player nicks into account, but not nicks of
        eventual puppets.

        raw_string - raw command input coming from the command line.
        sessid - the optional session id to be responsible for the command-send
        **kwargs - other keyword arguments will be added to the found command
                   object instace as variables before it executes. This is
                   unused by default Evennia but may be used to set flags and
                   change operating paramaters for commands at run-time.
        """
        raw_string = to_unicode(raw_string)
        raw_string = self.nicks.nickreplace(raw_string,
                                            categories=("inputline",
                                                        "channel"),
                                            include_player=False)
        if not sessid and _MULTISESSION_MODE in (0, 1):
            # in this case, we should either have only one sessid, or the sessid
            # should not matter (since the return goes to all of them we can
            # just use the first one as the source)
            try:
                sessid = self.get_all_sessions()[0].sessid
            except IndexError:
                # this can happen for bots
                sessid = None
        return cmdhandler.cmdhandler(self,
                                     raw_string,
                                     callertype="player",
                                     sessid=sessid,
                                     **kwargs)
Beispiel #12
0
    def execute_cmd(self, raw_string, session=None, **kwargs):
        """
        Do something as this player. This method is never called normally,
        but only when the player object itself is supposed to execute the
        command. It takes player nicks into account, but not nicks of
        eventual puppets.

        Args:
            raw_string (str): Raw command input coming from the command line.
            session (Session, optional): The session to be responsible
                for the command-send

        Kwargs:
            kwargs (any): Other keyword arguments will be added to the
                found command object instance as variables before it
                executes. This is unused by default Evennia but may be
                used to set flags and change operating paramaters for
                commands at run-time.

        """
        raw_string = to_unicode(raw_string)
        raw_string = self.nicks.nickreplace(raw_string,
                          categories=("inputline", "channel"), include_player=False)
        if not session and _MULTISESSION_MODE in (0, 1):
            # for these modes we use the first/only session
            sessions = self.sessions.get()
            session = sessions[0] if sessions else None

        return cmdhandler.cmdhandler(self, raw_string,
                                     callertype="player", session=session, **kwargs)
Beispiel #13
0
 def get_object_with_player(self, ostring, exact=True, candidates=None):
     """
     Search for an object based on its player's name or dbref.
     This search is sometimes initiated by appending an `*` to the
     beginning of the search criterion (e.g. in local_and_global_search).
     search_string:  (string) The name or dbref to search for.
     """
     ostring = to_unicode(ostring).lstrip('*')
     # simplest case - search by dbref
     dbref = self.dbref(ostring)
     if dbref:
         return dbref
     # not a dbref. Search by name.
     cand_restriction = candidates != None and Q(
         pk__in=[_GA(obj, "id")
                 for obj in make_iter(candidates) if obj]) or Q()
     if exact:
         return self.filter(cand_restriction
                            & Q(db_player__username__iexact=ostring))
     else:  # fuzzy matching
         ply_cands = self.filter(cand_restriction & Q(
             playerdb__username__istartswith=ostring)).values_list(
                 "db_key", flat=True)
         if candidates:
             index_matches = string_partial_matching(ply_cands,
                                                     ostring,
                                                     ret_index=True)
             return [
                 obj for ind, obj in enumerate(make_iter(candidates))
                 if ind in index_matches
             ]
         else:
             return string_partial_matching(ply_cands,
                                            ostring,
                                            ret_index=False)
Beispiel #14
0
 def validate_encoding(val):
     # helper: change encoding
     try:
         utils.to_str(utils.to_unicode("test-string"), encoding=val)
     except LookupError:
         raise RuntimeError("The encoding '|w%s|n' is invalid. " % val)
     return val
Beispiel #15
0
    def get_object_with_player(self, ostring, exact=True, candidates=None):
        """
        Search for an object based on its player's name or dbref.

        Args:
            ostring (str or int): Search criterion or dbref. Searching
                for a player is sometimes initiated by appending an `*` to
                the beginning of the search criterion (e.g. in
                local_and_global_search). This is stripped here.
            exact (bool, optional): Require an exact player match.
            candidates (list, optional): Only search among this list of possible
                object candidates.

        Return:
            match (Object or list): One or more matching results.

        """
        ostring = to_unicode(ostring).lstrip('*')
        # simplest case - search by dbref
        dbref = self.dbref(ostring)
        if dbref:
            return dbref
        # not a dbref. Search by name.
        cand_restriction = candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
        if exact:
            return self.filter(cand_restriction & Q(db_player__username__iexact=ostring))
        else: # fuzzy matching
            ply_cands = self.filter(cand_restriction & Q(playerdb__username__istartswith=ostring)).values_list("db_key", flat=True)
            if candidates:
                index_matches = string_partial_matching(ply_cands, ostring, ret_index=True)
                return [obj for ind, obj in enumerate(make_iter(candidates)) if ind in index_matches]
            else:
                return string_partial_matching(ply_cands, ostring, ret_index=False)
Beispiel #16
0
    def execute_cmd(self, raw_string, sessid=None, **kwargs):
        """
        Do something as this player. This method is never called normally,
        but only when the player object itself is supposed to execute the
        command. It takes player nicks into account, but not nicks of
        eventual puppets.

        raw_string - raw command input coming from the command line.
        sessid - the optional session id to be responsible for the command-send
        **kwargs - other keyword arguments will be added to the found command
                   object instace as variables before it executes. This is
                   unused by default Evennia but may be used to set flags and
                   change operating paramaters for commands at run-time.
        """
        raw_string = to_unicode(raw_string)
        raw_string = self.nicks.nickreplace(raw_string,
                          categories=("inputline", "channel"), include_player=False)
        if not sessid and _MULTISESSION_MODE in (0, 1):
            # in this case, we should either have only one sessid, or the sessid
            # should not matter (since the return goes to all of them we can
            # just use the first one as the source)
            try:
                sessid = self.get_all_sessions()[0].sessid
            except IndexError:
                # this can happen for bots
                sessid = None
        return cmdhandler.cmdhandler(self, raw_string,
                                     callertype="player", sessid=sessid, **kwargs)
Beispiel #17
0
    def get_objs_with_db_property_value(self, property_name, property_value, candidates=None, typeclasses=None):
        """
        Get objects with a specific field name and value.

        Args:
            property_name (str): Field name to search for.
            property_value (any): Value required for field with `property_name` to have.
            candidates (list, optional): List of objects to limit search to.
            typeclasses (list, optional): 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
        querykwargs = {property_name: property_value}
        cand_restriction = candidates is not 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 evennia.utils import logger
            logger.log_err("The property '%s' does not support search criteria of the type %s." %
                           (property_name, type(property_value)))
            return []
Beispiel #18
0
    def __new__(cls, *args, **kwargs):
        """
        When creating a new ANSIString, you may use a custom parser that has
        the same attributes as the standard one, and you may declare the
        string to be handled as already decoded. It is important not to double
        decode strings, as escapes can only be respected once.

        Internally, ANSIString can also passes itself precached code/character
        indexes and clean strings to avoid doing extra work when combining
        ANSIStrings.

        """
        string = args[0]
        if not isinstance(string, basestring):
            string = to_str(string, force_string=True)
        parser = kwargs.get('parser', ANSI_PARSER)
        decoded = kwargs.get('decoded', False) or hasattr(
            string, '_raw_string')
        code_indexes = kwargs.pop('code_indexes', None)
        char_indexes = kwargs.pop('char_indexes', None)
        clean_string = kwargs.pop('clean_string', None)
        # All True, or All False, not just one.
        checks = [
            x is None for x in [code_indexes, char_indexes, clean_string]
        ]
        if not len(set(checks)) == 1:
            raise ValueError("You must specify code_indexes, char_indexes, "
                             "and clean_string together, or not at all.")
        if not all(checks):
            decoded = True
        if not decoded:
            # Completely new ANSI String
            clean_string = to_unicode(
                parser.parse_ansi(string, strip_ansi=True, mxp=True))
            string = parser.parse_ansi(string, xterm256=True, mxp=True)
        elif clean_string is not None:
            # We have an explicit clean string.
            pass
        elif hasattr(string, '_clean_string'):
            # It's already an ANSIString
            clean_string = string._clean_string
            code_indexes = string._code_indexes
            char_indexes = string._char_indexes
            string = string._raw_string
        else:
            # It's a string that has been pre-ansi decoded.
            clean_string = parser.strip_raw_codes(string)

        if not isinstance(string, unicode):
            string = string.decode('utf-8')

        ansi_string = super(ANSIString, cls).__new__(ANSIString,
                                                     to_str(clean_string),
                                                     "utf-8")
        ansi_string._raw_string = string
        ansi_string._clean_string = clean_string
        ansi_string._code_indexes = code_indexes
        ansi_string._char_indexes = char_indexes
        return ansi_string
Beispiel #19
0
def _to_ansi(obj, regexable=False):
    "convert to ANSIString"
    if isinstance(obj, dict):
        return dict((key, _to_ansi(value, regexable=regexable)) for key, value in obj.items())
    elif hasattr(obj, "__iter__"):
        return [_to_ansi(o) for o in obj]
    else:
        return ANSIString(to_unicode(obj), regexable=regexable)
Beispiel #20
0
def _to_ansi(obj, regexable=False):
    "convert to ANSIString"
    if isinstance(obj, dict):
        return dict((key, _to_ansi(value, regexable=regexable)) for key, value in obj.items())
    elif hasattr(obj, "__iter__"):
        return [_to_ansi(o) for o in obj]
    else:
        return ANSIString(to_unicode(obj), regexable=regexable)
Beispiel #21
0
def _to_ansi(obj, regexable=False):
    "convert to ANSIString"
    if isinstance(obj, basestring):
        # since ansi will be parsed twice (here and in the normal ansi send), we have to
        # escape the |-structure twice.
        obj = _ANSI_ESCAPE.sub(r"||||", obj)
    if isinstance(obj, dict):
        return dict((key, _to_ansi(value, regexable=regexable)) for key, value in obj.items())
    elif hasattr(obj, "__iter__"):
        return [_to_ansi(o) for o in obj]
    else:
        return ANSIString(to_unicode(obj), regexable=regexable)
Beispiel #22
0
    def __new__(cls, *args, **kwargs):
        """
        When creating a new ANSIString, you may use a custom parser that has
        the same attributes as the standard one, and you may declare the
        string to be handled as already decoded. It is important not to double
        decode strings, as escapes can only be respected once.

        Internally, ANSIString can also passes itself precached code/character
        indexes and clean strings to avoid doing extra work when combining
        ANSIStrings.

        """
        string = args[0]
        if not isinstance(string, basestring):
            string = to_str(string, force_string=True)
        parser = kwargs.get('parser', ANSI_PARSER)
        decoded = kwargs.get('decoded', False) or hasattr(string, '_raw_string')
        code_indexes = kwargs.pop('code_indexes', None)
        char_indexes = kwargs.pop('char_indexes', None)
        clean_string = kwargs.pop('clean_string', None)
        # All True, or All False, not just one.
        checks = [x is None for x in [code_indexes, char_indexes, clean_string]]
        if not len(set(checks)) == 1:
            raise ValueError("You must specify code_indexes, char_indexes, "
                             "and clean_string together, or not at all.")
        if not all(checks):
            decoded = True
        if not decoded:
            # Completely new ANSI String
            clean_string = to_unicode(parser.parse_ansi(string, strip_ansi=True, mxp=True))
            string = parser.parse_ansi(string, xterm256=True, mxp=True)
        elif clean_string is not None:
            # We have an explicit clean string.
            pass
        elif hasattr(string, '_clean_string'):
            # It's already an ANSIString
            clean_string = string._clean_string
            code_indexes = string._code_indexes
            char_indexes = string._char_indexes
            string = string._raw_string
        else:
            # It's a string that has been pre-ansi decoded.
            clean_string = parser.strip_raw_codes(string)

        if not isinstance(string, unicode):
            string = string.decode('utf-8')

        ansi_string = super(ANSIString, cls).__new__(ANSIString, to_str(clean_string), "utf-8")
        ansi_string._raw_string = string
        ansi_string._clean_string = clean_string
        ansi_string._code_indexes = code_indexes
        ansi_string._char_indexes = char_indexes
        return ansi_string
Beispiel #23
0
def _to_ansi(obj, regexable=False):
    "convert to ANSIString"
    if isinstance(obj, basestring):
        # since ansi will be parsed twice (here and in the normal ansi send), we have to
        # escape the |-structure twice.
        obj = _ANSI_ESCAPE.sub(r"||||", obj)
    if isinstance(obj, dict):
        return dict((key, _to_ansi(value, regexable=regexable)) for key, value in obj.items())
    elif hasattr(obj, "__iter__"):
        return [_to_ansi(o) for o in obj]
    else:
        return ANSIString(to_unicode(obj), regexable=regexable)
Beispiel #24
0
    def data_out(self, session, text="", **kwargs):
        """
        Sending data Server -> Portal

        Args:
            session (Session): Session object
            text (str, optional): text data to return
            _nomulti (bool, optional): if given, only this
                session will receive the rest of the data,
                regardless of MULTISESSION_MODE. This is an
                internal variable that will not be passed on.
                This is ignored for MULTISESSION_MODE = 1,
                since all messages are mirrored everywhere for
                that.
            _forced_nomulti (bool, optional): Like _nomulti,
                but works even when MULTISESSION_MODE = 1.
                Useful for connection handling messages.

        """
        #from evennia.server.profiling.timetrace import timetrace
        #text = timetrace(text, "ServerSessionHandler.data_out")

        sessions = make_iter(session)
        session = sessions[0]
        text = text and to_str(to_unicode(text), encoding=session.encoding)
        multi = not kwargs.pop("_nomulti", None)
        forced_nomulti = kwargs.pop("_forced_nomulti", None)
        # Mode 1 mirrors to all.
        if _MULTISESSION_MODE == 1:
            multi = True
        # ...Unless we're absolutely sure.
        if forced_nomulti:
            multi = False

        if multi:
            if _MULTISESSION_MODE == 1:
                if session.player:
                    sessions = self.sessions_from_player(session.player)
            if _MULTISESSION_MODE == 2:
                if session.player:
                    sessions = self.sessions_from_player(session.player)
            elif _MULTISESSION_MODE == 3:
                if session.puppet:
                    sessions = self.sessions_from_puppet(session.puppet)
                elif session.player:
                    sessions = self.sessions_from_player(session.player)

        # send to all found sessions
        for session in sessions:
            self.server.amp_protocol.send_MsgServer2Portal(sessid=session.sessid,
                                                           msg=text,
                                                           data=kwargs)
Beispiel #25
0
    def data_out(self, session, text="", **kwargs):
        """
        Sending data Server -> Portal

        Args:
            session (Session): Session object
            text (str, optional): text data to return
            _nomulti (bool, optional): if given, only this
                session will receive the rest of the data,
                regardless of MULTISESSION_MODE. This is an
                internal variable that will not be passed on.
                This is ignored for MULTISESSION_MODE = 1,
                since all messages are mirrored everywhere for
                that.
            _forced_nomulti (bool, optional): Like _nomulti,
                but works even when MULTISESSION_MODE = 1.
                Useful for connection handling messages.

        """
        #from evennia.server.profiling.timetrace import timetrace
        #text = timetrace(text, "ServerSessionHandler.data_out")

        sessions = make_iter(session)
        session = sessions[0]
        text = text and to_str(to_unicode(text), encoding=session.encoding)
        multi = not kwargs.pop("_nomulti", None)
        forced_nomulti = kwargs.pop("_forced_nomulti", None)
        # Mode 1 mirrors to all.
        if _MULTISESSION_MODE == 1:
            multi = True
        # ...Unless we're absolutely sure.
        if forced_nomulti:
            multi = False

        if multi:
            if _MULTISESSION_MODE == 1:
                if session.player:
                    sessions = self.sessions_from_player(session.player)
            if _MULTISESSION_MODE == 2:
                if session.player:
                    sessions = self.sessions_from_player(session.player)
            elif _MULTISESSION_MODE == 3:
                if session.puppet:
                    sessions = self.sessions_from_puppet(session.puppet)
                elif session.player:
                    sessions = self.sessions_from_player(session.player)

        # send to all found sessions
        for session in sessions:
            self.server.amp_protocol.send_MsgServer2Portal(
                sessid=session.sessid, msg=text, data=kwargs)
Beispiel #26
0
 def _validate(data):
     "Helper function to convert data to AMP-safe (picketable) values"
     if isinstance(data, dict):
         newdict = {}
         for key, part in data.items():
             newdict[key] = _validate(part)
         return newdict
     elif hasattr(data, "__iter__"):
         return [_validate(part) for part in data]
     elif isinstance(data, basestring):
         # make sure strings are in a valid encoding
         try:
             data = data and to_str(
                 to_unicode(data),
                 encoding=session.protocol_flags["ENCODING"])
         except LookupError:
             # wrong encoding set on the session. Set it to a safe one
             session.protocol_flags["ENCODING"] = "utf-8"
             data = to_str(to_unicode(data),
                           encoding=session.protocol_flags["ENCODING"])
         if _INLINEFUNC_ENABLED and not raw and isinstance(
                 self, ServerSessionHandler):
             # only parse inlinefuncs on the outgoing path (sessionhandler->)
             data = parse_inlinefunc(data,
                                     strip=strip_inlinefunc,
                                     session=session)
         # At this point the object is certainly the right encoding, but may still be a unicode object--
         # to_str does not actually force objects to become bytestrings.
         # If the unicode object is a subclass of unicode, such as ANSIString, this can cause a problem,
         # as special behavior for that class will still be in play. Since we're now transferring raw data,
         # we must now force this to be a proper bytestring.
         return str(data)
     elif hasattr(data, "id") and hasattr(data, "db_date_created") \
             and hasattr(data, '__dbclass__'):
         # convert database-object to their string representation.
         return _validate(unicode(data))
     else:
         return data
Beispiel #27
0
    def smart_search(self, query):
        """
        Search by supplying a string with optional extra search criteria to aid the query.

        Args:
            query (str): A search criteria that accepts extra search criteria on the

                following forms: [key|alias|#dbref...] [tag==<tagstr>[:category]...] [attr==<key>:<value>:category...]
                                          "                !=             "               !=      "
        Returns:
            matches (queryset): A queryset result matching all queries exactly. If wanting to use spaces or
            ==, != in tags or attributes, enclose them in quotes.

        Note:
            The flexibility of this method is limited by the input line format. Tag/attribute
            matching only works for matching primitives.  For even more complex queries, such as
            'in' operations or object field matching, use the full django query language.

        """
        # shlex splits by spaces unless escaped by quotes
        querysplit = shlex.split(to_unicode(query, force_string=True))
        queries, plustags, plusattrs, negtags, negattrs = [], [], [], [], []
        for ipart, part in enumerate(querysplit):
            key, rest = part, ""
            if ":" in part:
                key, rest = part.split(':', 1)
            # tags are on the form tag or tag:category
            if key.startswith('tag=='):
                plustags.append((key[5:], rest))
                continue
            elif key.startswith('tag!='):
                negtags.append((key[5:], rest))
                continue
            # attrs are on the form attr:value or attr:value:category
            elif rest:
                value, category = rest, ""
                if ":" in rest:
                    value, category = rest.split(':', 1)
                if key.startswith('attr=='):
                    plusattrs.append((key[7:], value, category))
                    continue
                elif key.startswith('attr!='):
                    negattrs.append((key[7:], value, category))
                    continue
            # if we get here, we are entering a key search criterion which
            # we assume is one word.
            queries.append(part)
        # build query from components
        query = ' '.join(queries)
Beispiel #28
0
    def get_object_with_account(self, ostring, exact=True, candidates=None):
        """
        Search for an object based on its account's name or dbref.

        Args:
            ostring (str or int): Search criterion or dbref. Searching
                for an account is sometimes initiated by appending an `*` to
                the beginning of the search criterion (e.g. in
                local_and_global_search). This is stripped here.
            exact (bool, optional): Require an exact account match.
            candidates (list, optional): Only search among this list of possible
                object candidates.

        Return:
            match (Object or list): One or more matching results.

        """
        ostring = to_unicode(ostring).lstrip('*')
        # simplest case - search by dbref
        dbref = self.dbref(ostring)
        if dbref:
            try:
                return self.get(id=dbref)
            except self.model.DoesNotExist:
                pass

        # not a dbref. Search by name.
        cand_restriction = candidates is not None and Q(
            pk__in=[_GA(obj, "id")
                    for obj in make_iter(candidates) if obj]) or Q()
        if exact:
            return self.filter(cand_restriction
                               & Q(db_account__username__iexact=ostring))
        else:  # fuzzy matching
            ply_cands = self.filter(cand_restriction & Q(
                accountdb__username__istartswith=ostring)).values_list(
                    "db_key", flat=True)
            if candidates:
                index_matches = string_partial_matching(ply_cands,
                                                        ostring,
                                                        ret_index=True)
                return [
                    obj for ind, obj in enumerate(make_iter(candidates))
                    if ind in index_matches
                ]
            else:
                return string_partial_matching(ply_cands,
                                               ostring,
                                               ret_index=False)
Beispiel #29
0
    def smart_search(self, query):
        """
        Search by supplying a string with optional extra search criteria to aid the query.

        Args:
            query (str): A search criteria that accepts extra search criteria on the

                following forms: [key|alias|#dbref...] [tag==<tagstr>[:category]...] [attr==<key>:<value>:category...]
                                          "                !=             "               !=      "
        Returns:
            matches (queryset): A queryset result matching all queries exactly. If wanting to use spaces or
            ==, != in tags or attributes, enclose them in quotes.

        Note:
            The flexibility of this method is limited by the input line format. Tag/attribute
            matching only works for matching primitives.  For even more complex queries, such as
            'in' operations or object field matching, use the full django query language.

        """
        # shlex splits by spaces unless escaped by quotes
        querysplit = shlex.split(to_unicode(query, force_string=True))
        queries, plustags, plusattrs, negtags, negattrs = [], [], [], [], []
        for ipart, part in enumerate(querysplit):
            key, rest = part, ""
            if ":" in part:
                key, rest = part.split(':', 1)
            # tags are on the form tag or tag:category
            if key.startswith('tag=='):
                plustags.append((key[5:], rest))
                continue
            elif key.startswith('tag!='):
                negtags.append((key[5:], rest))
                continue
            # attrs are on the form attr:value or attr:value:category
            elif rest:
                value, category = rest, ""
                if ":" in rest:
                    value, category = rest.split(':', 1)
                if key.startswith('attr=='):
                    plusattrs.append((key[7:], value, category))
                    continue
                elif key.startswith('attr!='):
                    negattrs.append((key[7:], value, category))
                    continue
            # if we get here, we are entering a key search criterion which
            # we assume is one word.
            queries.append(part)
        # build query from components
        query = ' '.join(queries)
Beispiel #30
0
    def data_out(self, session, text="", **kwargs):
        """
        Sending data Server -> Portal

        Args:
            session (Session): Session to relay to.
            text (str, optional): text data to return

        """
        #from evennia.server.profiling.timetrace import timetrace
        #text = timetrace(text, "ServerSessionHandler.data_out")

        try:
            text = text and to_str(to_unicode(text), encoding=session.encoding)
        except LookupError:
            # wrong encoding set on the session. Set it to a safe one
            session.encoding = "utf-8"
            text = to_str(to_unicode(text), encoding=session.encoding)


        # send across AMP
        self.server.amp_protocol.send_MsgServer2Portal(session,
                                                       text=text,
                                                       **kwargs)
Beispiel #31
0
    def data_out(self, session, text="", **kwargs):
        """
        Sending data Server -> Portal

        Args:
            session (Session): Session to relay to.
            text (str, optional): text data to return

        """
        #from evennia.server.profiling.timetrace import timetrace
        #text = timetrace(text, "ServerSessionHandler.data_out")

        try:
            text = text and to_str(to_unicode(text), encoding=session.encoding)
        except LookupError:
            # wrong encoding set on the session. Set it to a safe one
            session.encoding = "utf-8"
            text = to_str(to_unicode(text), encoding=session.encoding)


        # send across AMP
        self.server.amp_protocol.send_MsgServer2Portal(session,
                                                       text=text,
                                                       **kwargs)
Beispiel #32
0
    def func(self):
        """
        Sets the encoding.
        """

        if self.session is None:
            return

        sync = False
        if 'clear' in self.switches:
            # remove customization
            old_encoding = self.session.protocol_flags.get("ENCODING", None)
            if old_encoding:
                string = "Your custom text encoding ('%s') was cleared." % old_encoding
            else:
                string = "No custom encoding was set."
            self.session.protocol_flags["ENCODING"] = "utf-8"
            sync = True
        elif not self.args:
            # just list the encodings supported
            pencoding = self.session.protocol_flags.get("ENCODING", None)
            string = ""
            if pencoding:
                string += "Default encoding: |g%s|n (change with |w@encoding <encoding>|n)" % pencoding
            encodings = settings.ENCODINGS
            if encodings:
                string += "\nServer's alternative encodings (tested in this order):\n   |g%s|n" % ", ".join(
                    encodings)
            if not string:
                string = "No encodings found."
        else:
            # change encoding
            old_encoding = self.session.protocol_flags.get("ENCODING", None)
            encoding = self.args
            try:
                utils.to_str(utils.to_unicode("test-string"),
                             encoding=encoding)
            except LookupError:
                string = "|rThe encoding '|w%s|r' is invalid. Keeping the previous encoding '|w%s|r'.|n"\
                         % (encoding, old_encoding)
            else:
                self.session.protocol_flags["ENCODING"] = encoding
                string = "Your custom text encoding was changed from '|w%s|n' to '|w%s|n'." % (
                    old_encoding, encoding)
                sync = True
        if sync:
            self.session.sessionhandler.session_portal_sync(self.session)
        self.caller.msg(string.strip())
Beispiel #33
0
    def func(self):
        """
        Sets the encoding.
        """

        if self.session is None:
            return

        sync = False
        if 'clear' in self.switches:
            # remove customization
            old_encoding = self.session.protocol_flags.get("ENCODING", None)
            if old_encoding:
                string = "Your custom text encoding ('%s') was cleared." % old_encoding
            else:
                string = "No custom encoding was set."
            self.session.protocol_flags["ENCODING"] = "utf-8"
            sync = True
        elif not self.args:
            # just list the encodings supported
            pencoding = self.session.protocol_flags.get("ENCODING", None)
            string = ""
            if pencoding:
                string += "Default encoding: |g%s|n (change with |w@encoding <encoding>|n)" % pencoding
            encodings = settings.ENCODINGS
            if encodings:
                string += "\nServer's alternative encodings (tested in this order):\n   |g%s|n" % ", ".join(encodings)
            if not string:
                string = "No encodings found."
        else:
            # change encoding
            old_encoding = self.session.protocol_flags.get("ENCODING", None)
            encoding = self.args
            try:
                utils.to_str(utils.to_unicode("test-string"), encoding=encoding)
            except LookupError:
                string = "|rThe encoding '|w%s|r' is invalid. Keeping the previous encoding '|w%s|r'.|n"\
                         % (encoding, old_encoding)
            else:
                self.session.protocol_flags["ENCODING"] = encoding
                string = "Your custom text encoding was changed from '|w%s|n' to '|w%s|n'." % (old_encoding, encoding)
                sync = True
        if sync:
            self.session.sessionhandler.session_portal_sync(self.session)
        self.caller.msg(string.strip())
Beispiel #34
0
    def data_out(self, session, text="", **kwargs):
        """
        Sending data Server -> Portal

        Args:
            session (Session): Session to relay to.
            text (str, optional): text data to return

        """
        #from evennia.server.profiling.timetrace import timetrace
        #text = timetrace(text, "ServerSessionHandler.data_out")

        text = text and to_str(to_unicode(text), encoding=session.encoding)

        # send across AMP
        self.server.amp_protocol.send_MsgServer2Portal(session,
                                                       text=text,
                                                       **kwargs)
Beispiel #35
0
    def func(self):
        """
        Implements the command
        """
        if self.session is None:
            return

        if not self.args:
            # list the option settings
            string = "{wEncoding{n:\n"
            pencoding = self.session.encoding or "None"
            sencodings = settings.ENCODINGS
            string += " Custom: %s\n Server: %s" % (pencoding,
                                                    ", ".join(sencodings))
            string += "\n{wScreen Reader mode:{n %s" % self.session.screenreader
            self.caller.msg(string)
            return

        if not self.rhs:
            self.caller.msg("Usage: @option [name = [value]]")
            return

        if self.lhs == "encoding":
            # change encoding
            old_encoding = self.session.encoding
            new_encoding = self.rhs.strip() or "utf-8"
            try:
                utils.to_str(utils.to_unicode("test-string"),
                             encoding=new_encoding)
            except LookupError:
                string = "|rThe encoding '|w%s|r' is invalid. Keeping the previous encoding '|w%s|r'.|n" % (
                    new_encoding, old_encoding)
            else:
                self.session.encoding = new_encoding
                string = "Encoding was changed from '|w%s|n' to '|w%s|n'." % (
                    old_encoding, new_encoding)
            self.caller.msg(string)
            return

        if self.lhs == "screenreader":
            onoff = self.rhs.lower() == "on"
            self.session.screenreader = onoff
            self.caller.msg("Screen reader mode was turned {w%s{n." %
                            ("on" if onoff else "off"))
Beispiel #36
0
    def data_in(self, sessid, text="", **kwargs):
        """
        Data Portal -> Server.
        We also intercept OOB communication here.
        """
        session = self.sessions.get(sessid, None)
        if session:
            text = text and to_unicode(strip_control_sequences(text), encoding=session.encoding)
            if "oob" in kwargs:
                # incoming data is always on the form (cmdname, args, kwargs)
                global _OOB_HANDLER
                if not _OOB_HANDLER:
                    from evennia.server.oobhandler import OOB_HANDLER as _OOB_HANDLER
                funcname, args, kwargs = kwargs.pop("oob")
                #print "OOB session.data_in:", funcname, args, kwargs
                if funcname:
                    _OOB_HANDLER.execute_cmd(session, funcname, *args, **kwargs)

            # pass the rest off to the session
            session.data_in(text=text, **kwargs)
Beispiel #37
0
    def data_in(self, sessid, text="", **kwargs):
        """
        Data Portal -> Server.
        We also intercept OOB communication here.
        """
        session = self.sessions.get(sessid, None)
        if session:
            text = text and to_unicode(strip_control_sequences(text),
                                       encoding=session.encoding)
            if "oob" in kwargs:
                # incoming data is always on the form (cmdname, args, kwargs)
                global _OOB_HANDLER
                if not _OOB_HANDLER:
                    from evennia.server.oobhandler import OOB_HANDLER as _OOB_HANDLER
                funcname, args, kwargs = kwargs.pop("oob")
                #print "OOB session.data_in:", funcname, args, kwargs
                if funcname:
                    _OOB_HANDLER.execute_cmd(session, funcname, *args,
                                             **kwargs)

            # pass the rest off to the session
            session.data_in(text=text, **kwargs)
Beispiel #38
0
    def func(self):
        """
        Implements the command
        """
        if self.session is None:
            return

        if not self.args:
            # list the option settings
            string = "{wEncoding{n:\n"
            pencoding = self.session.encoding or "None"
            sencodings = settings.ENCODINGS
            string += " Custom: %s\n Server: %s" % (pencoding, ", ".join(sencodings))
            string += "\n{wScreen Reader mode:{n %s" % self.session.screenreader
            self.msg(string)
            return

        if not self.rhs:
            self.msg("Usage: @option [name = [value]]")
            return

        if self.lhs == "encoding":
            # change encoding
            old_encoding = self.session.encoding
            new_encoding = self.rhs.strip() or "utf-8"
            try:
                utils.to_str(utils.to_unicode("test-string"), encoding=new_encoding)
            except LookupError:
                string = "|rThe encoding '|w%s|r' is invalid. Keeping the previous encoding '|w%s|r'.|n" % (new_encoding, old_encoding)
            else:
                self.session.encoding = new_encoding
                string = "Encoding was changed from '|w%s|n' to '|w%s|n'." % (old_encoding, new_encoding)
            self.msg(string)
            return

        if self.lhs == "screenreader":
            onoff = self.rhs.lower() == "on"
            self.session.screenreader = onoff
            self.msg("Screen reader mode was turned {w%s{n." % ("on" if onoff else "off"))
Beispiel #39
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
     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 evennia.utils import logger
         logger.log_errmsg("The property '%s' does not support search criteria of the type %s." % (property_name, type(property_value)))
         return []
Beispiel #40
0
 def get_object_with_player(self, ostring, exact=True, candidates=None):
     """
     Search for an object based on its player's name or dbref.
     This search is sometimes initiated by appending an `*` to the
     beginning of the search criterion (e.g. in local_and_global_search).
     search_string:  (string) The name or dbref to search for.
     """
     ostring = to_unicode(ostring).lstrip('*')
     # simplest case - search by dbref
     dbref = self.dbref(ostring)
     if dbref:
         return dbref
     # not a dbref. Search by name.
     cand_restriction = candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
     if exact:
         return self.filter(cand_restriction & Q(db_player__username__iexact=ostring))
     else: # fuzzy matching
         ply_cands = self.filter(cand_restriction & Q(playerdb__username__istartswith=ostring)).values_list("db_key", flat=True)
         if candidates:
             index_matches = string_partial_matching(ply_cands, ostring, ret_index=True)
             return [obj for ind, obj in enumerate(make_iter(candidates)) if ind in index_matches]
         else:
             return string_partial_matching(ply_cands, ostring, ret_index=False)
Beispiel #41
0
def build_map(caller, game_map, legend, iterations=1, build_exits=True):
    """
    Receives the fetched map and legend vars provided by the player.

    Args:
        caller (Object): The creator of the map.
        game_map (str): An ASCII map string.
        legend (dict): Mapping of map symbols to object types.
        iterations (int): The number of iteration passes.
        build_exits (bool): Create exits between new rooms.

    Notes:
        The map
        is iterated over character by character, comparing it to the trigger
        characters in the legend var and executing the build instructions on
        finding a match. The map is iterated over according to the `iterations`
        value and exits are optionally generated between adjacent rooms according
        to the `build_exits` value.

    """

    # Split map string to list of rows and create reference list.
    caller.msg("Creating Map...")
    caller.msg(game_map)
    game_map = _map_to_list(game_map)

    # Create a reference dictionary which be passed to build functions and
    # will store obj returned by build functions so objs can be referenced.
    room_dict = {}

    caller.msg("Creating Landmass...")
    for iteration in xrange(iterations):
        for y in xrange(len(game_map)):
            for x in xrange(len(game_map[y])):
                for key in legend:
                    # obs - we must use == for unicode
                    if utils.to_unicode(
                            game_map[y][x]) == utils.to_unicode(key):
                        room = legend[key](x,
                                           y,
                                           iteration=iteration,
                                           room_dict=room_dict,
                                           caller=caller)
                        if iteration == 0:
                            room_dict[(x, y)] = room

    if build_exits:
        # Creating exits. Assumes single room object in dict entry
        caller.msg("Connecting Areas...")
        for loc_key, location in room_dict.iteritems():
            x = loc_key[0]
            y = loc_key[1]

            # north
            if (x, y - 1) in room_dict:
                if room_dict[(x, y - 1)]:
                    create_object(exits.Exit,
                                  key="north",
                                  aliases=["n"],
                                  location=location,
                                  destination=room_dict[(x, y - 1)])

            # east
            if (x + 1, y) in room_dict:
                if room_dict[(x + 1, y)]:
                    create_object(exits.Exit,
                                  key="east",
                                  aliases=["e"],
                                  location=location,
                                  destination=room_dict[(x + 1, y)])

            # south
            if (x, y + 1) in room_dict:
                if room_dict[(x, y + 1)]:
                    create_object(exits.Exit,
                                  key="south",
                                  aliases=["s"],
                                  location=location,
                                  destination=room_dict[(x, y + 1)])

            # west
            if (x - 1, y) in room_dict:
                if room_dict[(x - 1, y)]:
                    create_object(exits.Exit,
                                  key="west",
                                  aliases=["w"],
                                  location=location,
                                  destination=room_dict[(x - 1, y)])

    caller.msg("Map Created.")
Beispiel #42
0
def cmdhandler(called_by, raw_string, _testing=False, callertype="session", session=None, **kwargs):
    """
    This is the main mechanism that handles any string sent to the engine.

    Args:
        called_by (Session, Player or Object): Object from which this
            command was called. which this was called from.  What this is
            depends on the game state.
        raw_string (str): The command string as given on the command line.
        _testing (bool, optional): Used for debug purposes and decides if we
            should actually execute the command or not. If True, the
            command instance will be returned.
        callertype (str, optional): One of "session", "player" or
            "object". These are treated 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.
        session (Session, optional): Relevant if callertype is "player" - the session will help
            retrieve the correct cmdsets from puppeted objects.

    Kwargs:
        kwargs (any): other keyword arguments will be assigned as named variables on the
            retrieved command object *before* it is executed. This is unused
            in default Evennia but may be used by code to set custom flags or
            special operating conditions for a command as it executes.

    Returns:
        deferred (Deferred): This deferred is fired with the return
        value of the command's `func` method.  This is not used in
        default Evennia.

    """

    @inlineCallbacks
    def _run_command(cmd, cmdname, args):
        """
        Helper function: This initializes and runs the Command
        instance once the parser has identified it as either a normal
        command or one of the system commands.

        Args:
            cmd (Command): command object
            cmdname (str): name of command
            args (str): extra text entered after the identified command

        Returns:
            deferred (Deferred): this will fire with the return of the
                command's `func` method.

        Raises:
            RuntimeError: If command recursion limit was reached.

        """
        global _COMMAND_NESTING
        try:
            # Assign useful variables to the instance
            cmd.caller = caller
            cmd.cmdstring = cmdname
            cmd.args = args
            cmd.cmdset = cmdset
            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)

            _COMMAND_NESTING[called_by] += 1
            if _COMMAND_NESTING[called_by] > _COMMAND_RECURSION_LIMIT:
                err = _ERROR_RECURSION_LIMIT.format(recursion_limit=_COMMAND_RECURSION_LIMIT,
                                                    raw_string=unformatted_raw_string,
                                                    cmdclass=cmd.__class__)
                raise RuntimeError(err)

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

            # Parse and execute
            yield cmd.parse()

            # main command code
            # (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

            # return result to the deferred
            returnValue(ret)

        except Exception:
            _msg_err(caller, _ERROR_UNTRAPPED)
            raise ErrorReported
        finally:
            _COMMAND_NESTING[called_by] -= 1


    raw_string = to_unicode(raw_string, force_string=True)

    session, player, obj = session, None, None
    if callertype == "session":
        session = called_by
        player = session.player
        obj = session.puppet
    elif callertype == "player":
        player = called_by
        if session:
            obj = yield session.puppet
    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
    # The error_to is the default recipient for errors. Tries to make sure a player
    # does not get spammed for errors while preserving character mirroring.
    error_to = obj or session or player

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

            cmdset = yield get_and_merge_cmdsets(caller, session, player, obj,
                                                  callertype)
            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 _SEARCH_AT_RESULT([match[2] for match in matches], caller, query=match[0])
                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]

            if not matches:
                # No commands match our entered command
                syscmd = yield cmdset.get(CMD_NOMATCH)
                if syscmd:
                    # use custom CMD_NOMATCH 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.session = session
                sysarg = "%s:%s" % (cmdname, args)
                raise ExecSystemCommand(cmd, sysarg)

            # A normal command.
            ret = yield _run_command(cmd, cmdname, args)
            returnValue(ret)

        except ErrorReported:
            # this error was already reported, so we
            # catch it here and don't pass it on.
            pass

        except ExecSystemCommand as 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:
                ret = yield _run_command(syscmd, syscmd.key, sysarg)
                returnValue(ret)
            elif sysarg:
                # return system arg
                error_to.msg(exc.sysarg)

        except NoCmdSets:
            # Critical error.
            logger.log_err("No cmdsets found: %s" % caller)
            error_to.msg(_ERROR_NOCMDSETS)

        except Exception:
            # We should not end up here. If we do, it's a programming bug.
            _msg_err(error_to, _ERROR_UNTRAPPED)

    except Exception:
        # This catches exceptions in cmdhandler exceptions themselves
        _msg_err(error_to, _ERROR_CMDHANDLER)
Beispiel #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!
    """
    @inlineCallbacks
    def _run_command(cmd, cmdname, args):
        """
        This initializes and runs the Command instance once the parser
        has identified it as either a normal command or one of the
        system commands.

        Args:
            cmd (Command): command object
            cmdname (str): name of command
            args (str): extra text entered after the identified command
        Returns:
            deferred (Deferred): this will fire when the func() method
                returns.
        """
        try:
            # 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()

            # main command code
            # (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
            # return result to the deferred
            returnValue(ret)

        except Exception:
            logger.log_trace()
            _msg_err(caller, _ERROR_UNTRAPPED)
            raise ErrorReported

    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 player.get_puppet(session.sessid)
    elif callertype == "player":
        player = called_by
        if sessid:
            session = player.get_session(sessid)
            obj = yield player.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
    # The error_to is the default recipient for errors. Tries to make sure a player
    # does not get spammed for errors while preserving character mirroring.
    error_to = obj or session or player

    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_NOMATCH 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.
            ret = yield _run_command(cmd, cmdname, args)
            returnValue(ret)

        except ErrorReported:
            # this error was already reported, so we
            # catch it here and don't pass it on.
            pass

        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:
                ret = yield _run_command(syscmd, syscmd.key, sysarg)
                returnValue(ret)
            elif sysarg:
                # return system arg
                error_to.msg(exc.sysarg, _nomulti=True)

        except NoCmdSets:
            # Critical error.
            logger.log_errmsg("No cmdsets found: %s" % caller)
            error_to.msg(_ERROR_NOCMDSETS, _nomulti=True)

        except Exception:
            # We should not end up here. If we do, it's a programming bug.
            logger.log_trace()
            _msg_err(error_to, _ERROR_UNTRAPPED)
Beispiel #44
0
def cmdhandler(called_by,
               raw_string,
               _testing=False,
               callertype="session",
               session=None,
               cmdobj=None,
               cmdobj_key=None,
               **kwargs):
    """
    This is the main mechanism that handles any string sent to the engine.

    Args:
        called_by (Session, Player or Object): Object from which this
            command was called. which this was called from.  What this is
            depends on the game state.
        raw_string (str): The command string as given on the command line.
        _testing (bool, optional): Used for debug purposes and decides if we
            should actually execute the command or not. If True, the
            command instance will be returned.
        callertype (str, optional): One of "session", "player" or
            "object". These are treated 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.
        session (Session, optional): Relevant if callertype is "player" - the session will help
            retrieve the correct cmdsets from puppeted objects.
        cmdobj (Command, optional): If given a command instance, this will be executed using
            `called_by` as the caller, `raw_string` representing its arguments and (optionally)
            `cmdobj_key` as its input command name. No cmdset lookup will be performed but
            all other options apply as normal. This allows for running a specific Command
            within the command system mechanism.
        cmdobj_key (string, optional): Used together with `cmdobj` keyword to specify
            which cmdname should be assigned when calling the specified Command instance. This
            is made available as `self.cmdstring` when the Command runs.
            If not given, the command will be assumed to be called as `cmdobj.key`.

    Kwargs:
        kwargs (any): other keyword arguments will be assigned as named variables on the
            retrieved command object *before* it is executed. This is unused
            in default Evennia but may be used by code to set custom flags or
            special operating conditions for a command as it executes.

    Returns:
        deferred (Deferred): This deferred is fired with the return
        value of the command's `func` method.  This is not used in
        default Evennia.

    """
    @inlineCallbacks
    def _run_command(cmd, cmdname, args, raw_string, cmdset, session, player):
        """
        Helper function: This initializes and runs the Command
        instance once the parser has identified it as either a normal
        command or one of the system commands.

        Args:
            cmd (Command): Command object.
            cmdname (str): Name of command.
            args (str): Extra text entered after the identified command.
            raw_string (str): Full input string.
            cmdset (CmdSet): Command sert the command belongs to (if any)..
            session (Session): Session of caller (if any).
            player (Player): Player of caller (if any).

        Returns:
            deferred (Deferred): this will fire with the return of the
                command's `func` method.

        Raises:
            RuntimeError: If command recursion limit was reached.

        """
        global _COMMAND_NESTING
        try:
            # Assign useful variables to the instance
            cmd.caller = caller
            cmd.cmdstring = cmdname
            cmd.args = args
            cmd.cmdset = cmdset
            cmd.session = session
            cmd.player = player
            cmd.raw_string = 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)

            _COMMAND_NESTING[called_by] += 1
            if _COMMAND_NESTING[called_by] > _COMMAND_RECURSION_LIMIT:
                err = _ERROR_RECURSION_LIMIT.format(
                    recursion_limit=_COMMAND_RECURSION_LIMIT,
                    raw_string=raw_string,
                    cmdclass=cmd.__class__)
                raise RuntimeError(err)

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

            # Parse and execute
            yield cmd.parse()

            # main command code
            # (return value is normally None)
            ret = cmd.func()
            if isinstance(ret, types.GeneratorType):
                # cmd.func() is a generator, execute progressively
                _progressive_cmd_run(cmd, ret)
                yield None
            else:
                ret = yield ret

            # 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

            # return result to the deferred
            returnValue(ret)

        except Exception:
            _msg_err(caller, _ERROR_UNTRAPPED)
            raise ErrorReported(raw_string)
        finally:
            _COMMAND_NESTING[called_by] -= 1

    raw_string = to_unicode(raw_string, force_string=True)

    session, player, obj = session, None, None
    if callertype == "session":
        session = called_by
        player = session.player
        obj = session.puppet
    elif callertype == "player":
        player = called_by
        if session:
            obj = yield session.puppet
    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
    # The error_to is the default recipient for errors. Tries to make sure a player
    # does not get spammed for errors while preserving character mirroring.
    error_to = obj or session or player

    try:  # catch bugs in cmdhandler itself
        try:  # catch special-type commands
            if cmdobj:
                # the command object is already given

                cmd = cmdobj() if callable(cmdobj) else cmdobj
                cmdname = cmdobj_key if cmdobj_key else cmd.key
                args = raw_string
                unformatted_raw_string = "%s%s" % (cmdname, args)
                cmdset = None
                session = session
                player = player

            else:
                # no explicit cmdobject given, figure it out

                cmdset = yield get_and_merge_cmdsets(caller, session, player,
                                                     obj, callertype,
                                                     raw_string)
                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 _SEARCH_AT_RESULT(
                            [match[2] for match in matches],
                            caller,
                            query=matches[0][0])
                    raise ExecSystemCommand(syscmd, sysarg)

                cmdname, args, cmd = "", "", None
                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]

                if not matches:
                    # No commands match our entered command
                    syscmd = yield cmdset.get(CMD_NOMATCH)
                    if syscmd:
                        # use custom CMD_NOMATCH 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.session = session
                    sysarg = "%s:%s" % (cmdname, args)
                    raise ExecSystemCommand(cmd, sysarg)

            # A normal command.
            ret = yield _run_command(cmd, cmdname, args,
                                     unformatted_raw_string, cmdset, session,
                                     player)
            returnValue(ret)

        except ErrorReported as exc:
            # this error was already reported, so we
            # catch it here and don't pass it on.
            logger.log_err("User input was: '%s'." % exc.raw_string)

        except ExecSystemCommand as 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:
                ret = yield _run_command(syscmd, syscmd.key, sysarg,
                                         unformatted_raw_string, cmdset,
                                         session, player)
                returnValue(ret)
            elif sysarg:
                # return system arg
                error_to.msg(exc.sysarg)

        except NoCmdSets:
            # Critical error.
            logger.log_err("No cmdsets found: %s" % caller)
            error_to.msg(_ERROR_NOCMDSETS)

        except Exception:
            # We should not end up here. If we do, it's a programming bug.
            _msg_err(error_to, _ERROR_UNTRAPPED)

    except Exception:
        # This catches exceptions in cmdhandler exceptions themselves
        _msg_err(error_to, _ERROR_CMDHANDLER)
Beispiel #45
0
def build_map(caller, game_map, legend, iterations=1, build_exits=True):
    """
    Receives the fetched map and legend vars provided by the player.

    Args:
        caller (Object): The creator of the map.
        game_map (str): An ASCII map string.
        legend (dict): Mapping of map symbols to object types.
        iterations (int): The number of iteration passes.
        build_exits (bool): Create exits between new rooms.

    Notes:
        The map
        is iterated over character by character, comparing it to the trigger
        characters in the legend var and executing the build instructions on
        finding a match. The map is iterated over according to the `iterations`
        value and exits are optionally generated between adjacent rooms according
        to the `build_exits` value.

    """

    # Split map string to list of rows and create reference list.
    caller.msg("Creating Map...")
    caller.msg(game_map)
    game_map = _map_to_list(game_map)

    # Create a reference dictionary which be passed to build functions and
    # will store obj returned by build functions so objs can be referenced.
    room_dict = {}

    caller.msg("Creating Landmass...")
    for iteration in xrange(iterations):
        for y in xrange(len(game_map)):
            for x in xrange(len(game_map[y])):
                for key in legend:
                    # obs - we must use == for unicode
                    if utils.to_unicode(game_map[y][x]) == utils.to_unicode(key):
                        room = legend[key](x, y, iteration=iteration,
                                           room_dict=room_dict,
                                           caller=caller)
                        if iteration == 0:
                            room_dict[(x, y)] = room

    if build_exits:
        # Creating exits. Assumes single room object in dict entry
        caller.msg("Connecting Areas...")
        for loc_key, location in room_dict.iteritems():
            x = loc_key[0]
            y = loc_key[1]

            # north
            if (x, y-1) in room_dict:
                if room_dict[(x, y-1)]:
                    create_object(exits.Exit, key="north",
                                  aliases=["n"], location=location,
                                  destination=room_dict[(x, y-1)])

            # east
            if (x+1, y) in room_dict:
                if room_dict[(x+1, y)]:
                    create_object(exits.Exit, key="east",
                                  aliases=["e"], location=location,
                                  destination=room_dict[(x+1, y)])

            # south
            if (x, y+1) in room_dict:
                if room_dict[(x, y+1)]:
                    create_object(exits.Exit, key="south",
                                  aliases=["s"], location=location,
                                  destination=room_dict[(x, y+1)])

            # west
            if (x-1, y) in room_dict:
                if room_dict[(x-1, y)]:
                    create_object(exits.Exit, key="west",
                                  aliases=["w"], location=location,
                                  destination=room_dict[(x-1, y)])

    caller.msg("Map Created.")