Exemple #1
0
def _init_command(cls, **kwargs):
    """
    Helper command.
    Makes sure all data are stored as lowercase and
    do checking on all properties that should be in list form.
    Sets up locks to be more forgiving. This is used both by the metaclass
    and (optionally) at instantiation time.

    If kwargs are given, these are set as instance-specific properties
    on the command - but note that the Command instance is *re-used* on a given
    host object, so a kwarg value set on the instance will *remain* on the instance
    for subsequent uses of that Command on that particular object.

    """
    for i in range(len(kwargs)):
        # used for dynamic creation of commands
        key, value = kwargs.popitem()
        setattr(cls, key, value)

    cls.key = cls.key.lower()
    if cls.aliases and not is_iter(cls.aliases):
        try:
            cls.aliases = [str(alias).strip().lower() for alias in cls.aliases.split(",")]
        except Exception:
            cls.aliases = []
    cls.aliases = list(set(alias for alias in cls.aliases if alias and alias != cls.key))

    # optimization - a set is much faster to match against than a list
    cls._matchset = set([cls.key] + cls.aliases)
    # optimization for looping over keys+aliases
    cls._keyaliases = tuple(cls._matchset)

    # by default we don't save the command between runs
    if not hasattr(cls, "save_for_next"):
        cls.save_for_next = False

    # pre-process locks as defined in class definition
    temp = []
    if hasattr(cls, "permissions"):
        cls.locks = cls.permissions
    if not hasattr(cls, "locks"):
        # default if one forgets to define completely
        cls.locks = "cmd:all()"
    if "cmd:" not in cls.locks:
        cls.locks = "cmd:all();" + cls.locks
    for lockstring in cls.locks.split(";"):
        if lockstring and ":" not in lockstring:
            lockstring = "cmd:%s" % lockstring
        temp.append(lockstring)
    cls.lock_storage = ";".join(temp)

    if hasattr(cls, "arg_regex") and isinstance(cls.arg_regex, str):
        cls.arg_regex = re.compile(r"%s" % cls.arg_regex, re.I + re.UNICODE)
    if not hasattr(cls, "auto_help"):
        cls.auto_help = True
    if not hasattr(cls, "is_exit"):
        cls.is_exit = False
    if not hasattr(cls, "help_category"):
        cls.help_category = "general"
    cls.help_category = cls.help_category.lower()
Exemple #2
0
    def get_objs_with_key_or_alias(self, ostring, exact=True,
                                         candidates=None, typeclasses=None):
        """
        Args:
            ostring (str): A search criterion.
            exact (bool, optional): Require exact match of ostring
                (still case-insensitive). If `False`, will do fuzzy matching
                using `evennia.utils.utils.string_partial_matching` algorithm.
            candidates (list): Only match among these candidates.
            typeclasses (list): Only match objects with typeclasses having thess path strings.

        Returns:
            matches (list): A list of matches of length 0, 1 or more.
        """
        if not isinstance(ostring, basestring):
            if hasattr(ostring, "key"):
                ostring = ostring.key
            else:
                return []
        if is_iter(candidates) and not len(candidates):
            # if candidates is an empty iterable there can be no matches
            # Exit early.
            return []

        # build query objects
        candidates_id = [_GA(obj, "id") for obj in make_iter(candidates) if obj]
        cand_restriction = candidates != None and Q(pk__in=candidates_id) or Q()
        type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
        if exact:
            # exact match - do direct search
            return self.filter(cand_restriction & type_restriction & (Q(db_key__iexact=ostring) |
                               Q(db_tags__db_key__iexact=ostring) & Q(db_tags__db_tagtype__iexact="alias"))).distinct()
        elif candidates:
            # fuzzy with candidates
            search_candidates = self.filter(cand_restriction & type_restriction)
        else:
            # fuzzy without supplied candidates - we select our own candidates
            search_candidates = self.filter(type_restriction & (Q(db_key__istartswith=ostring) | Q(db_tags__db_key__istartswith=ostring))).distinct()
        # fuzzy matching
        key_strings = search_candidates.values_list("db_key", flat=True).order_by("id")

        index_matches = string_partial_matching(key_strings, ostring, ret_index=True)
        if index_matches:
            # a match by key
            return [obj for ind, obj in enumerate(search_candidates) if ind in index_matches]
        else:
            # match by alias rather than by key
            search_candidates = search_candidates.filter(db_tags__db_tagtype__iexact="alias",
                                                        db_tags__db_key__icontains=ostring)
            alias_strings = []
            alias_candidates = []
            #TODO create the alias_strings and alias_candidates lists more effiently?
            for candidate in search_candidates:
                for alias in candidate.aliases.all():
                    alias_strings.append(alias)
                    alias_candidates.append(candidate)
            index_matches = string_partial_matching(alias_strings, ostring, ret_index=True)
            if index_matches:
                return [alias_candidates[ind] for ind in index_matches]
            return []
Exemple #3
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 is_iter(data):
                return [_validate(part) for part in data]
            elif isinstance(data, (str, bytes)):
                data = _utf8(data)

                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 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(str(data))
            else:
                return data
Exemple #4
0
def _init_command(mcs, **kwargs):
    """
    Helper command.
    Makes sure all data are stored as lowercase and
    do checking on all properties that should be in list form.
    Sets up locks to be more forgiving. This is used both by the metaclass
    and (optionally) at instantiation time.

    If kwargs are given, these are set as instance-specific properties
    on the command.
    """
    for i in range(len(kwargs)):
        # used for dynamic creation of commands
        key, value = kwargs.popitem()
        setattr(mcs, key, value)

    mcs.key = mcs.key.lower()
    if mcs.aliases and not is_iter(mcs.aliases):
        try:
            mcs.aliases = [
                str(alias).strip().lower() for alias in mcs.aliases.split(',')
            ]
        except Exception:
            mcs.aliases = []
    mcs.aliases = list(
        set(alias for alias in mcs.aliases if alias and alias != mcs.key))

    # optimization - a set is much faster to match against than a list
    mcs._matchset = set([mcs.key] + mcs.aliases)
    # optimization for looping over keys+aliases
    mcs._keyaliases = tuple(mcs._matchset)

    # by default we don't save the command between runs
    if not hasattr(mcs, "save_for_next"):
        mcs.save_for_next = False

    # pre-process locks as defined in class definition
    temp = []
    if hasattr(mcs, 'permissions'):
        mcs.locks = mcs.permissions
    if not hasattr(mcs, 'locks'):
        # default if one forgets to define completely
        mcs.locks = "cmd:all()"
    if not "cmd:" in mcs.locks:
        mcs.locks = "cmd:all();" + mcs.locks
    for lockstring in mcs.locks.split(';'):
        if lockstring and not ':' in lockstring:
            lockstring = "cmd:%s" % lockstring
        temp.append(lockstring)
    mcs.lock_storage = ";".join(temp)

    if hasattr(mcs, 'arg_regex') and isinstance(mcs.arg_regex, basestring):
        mcs.arg_regex = re.compile(r"%s" % mcs.arg_regex, re.I)
    if not hasattr(mcs, "auto_help"):
        mcs.auto_help = True
    if not hasattr(mcs, 'is_exit'):
        mcs.is_exit = False
    if not hasattr(mcs, "help_category"):
        mcs.help_category = "general"
    mcs.help_category = mcs.help_category.lower()
Exemple #5
0
def init_spawn_value(value, validator=None):
    """
    Analyze the prototype value and produce a value useful at the point of spawning.

    Args:
        value (any): This can be:
            callable - will be called as callable()
            (callable, (args,)) - will be called as callable(*args)
            other - will be assigned depending on the variable type
            validator (callable, optional): If given, this will be called with the value to
                check and guarantee the outcome is of a given type.

    Returns:
        any (any): The (potentially pre-processed value to use for this prototype key)

    """
    value = protfunc_parser(value)
    validator = validator if validator else lambda o: o
    if callable(value):
        return validator(value())
    elif value and is_iter(value) and callable(value[0]):
        # a structure (callable, (args, ))
        args = value[1:]
        return validator(value[0](*make_iter(args)))
    else:
        return validator(value)
Exemple #6
0
    def get_objs_with_key_or_alias(self, ostring, exact=True,
                                         candidates=None, typeclasses=None):
        """
        Args:
            ostring (str): A search criterion.
            exact (bool, optional): Require exact match of ostring
                (still case-insensitive). If `False`, will do fuzzy matching
                using `evennia.utils.utils.string_partial_matching` algorithm.
            candidates (list): Only match among these candidates.
            typeclasses (list): Only match objects with typeclasses having thess path strings.

        Returns:
            matches (list): A list of matches of length 0, 1 or more.
        """
        if not isinstance(ostring, basestring):
            if hasattr(ostring, "key"):
                ostring = ostring.key
            else:
                return []
        if is_iter(candidates) and not len(candidates):
            # if candidates is an empty iterable there can be no matches
            # Exit early.
            return []

        # build query objects
        candidates_id = [_GA(obj, "id") for obj in make_iter(candidates) if obj]
        cand_restriction = candidates != None and Q(pk__in=candidates_id) or Q()
        type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
        if exact:
            # exact match - do direct search
            return self.filter(cand_restriction & type_restriction & (Q(db_key__iexact=ostring) |
                               Q(db_tags__db_key__iexact=ostring) & Q(db_tags__db_tagtype__iexact="alias"))).distinct()
        elif candidates:
            # fuzzy with candidates
            search_candidates = self.filter(cand_restriction & type_restriction)
        else:
            # fuzzy without supplied candidates - we select our own candidates
            search_candidates = self.filter(type_restriction & (Q(db_key__istartswith=ostring) | Q(db_tags__db_key__istartswith=ostring))).distinct()
        # fuzzy matching
        key_strings = search_candidates.values_list("db_key", flat=True).order_by("id")

        index_matches = string_partial_matching(key_strings, ostring, ret_index=True)
        if index_matches:
            # a match by key
            return [obj for ind, obj in enumerate(search_candidates) if ind in index_matches]
        else:
            # match by alias rather than by key
            search_candidates = search_candidates.filter(db_tags__db_tagtype__iexact="alias",
                                                        db_tags__db_key__icontains=ostring)
            alias_strings = []
            alias_candidates = []
            #TODO create the alias_strings and alias_candidates lists more effiently?
            for candidate in search_candidates:
                for alias in candidate.aliases.all():
                    alias_strings.append(alias)
                    alias_candidates.append(candidate)
            index_matches = string_partial_matching(alias_strings, ostring, ret_index=True)
            if index_matches:
                return [alias_candidates[ind] for ind in index_matches]
            return []
Exemple #7
0
 def session_from_sessid(self, sessid):
     """
     Return session based on sessid, or None if not found
     """
     if is_iter(sessid):
         return [self.sessions.get(sid) for sid in sessid if sid in self.sessions]
     return self.sessions.get(sessid)
Exemple #8
0
 def msg_exits(self, text=None, exclude=None, from_obj=None, **kwargs):
         """
         Emit message to all objects in rooms this room connects to via 
         exits.
         
         Args:
             text (str or tuple): Message to send. If a tuple, this should be
                 on the valid OOB outmessage form `(message, {kwargs})`,
                 where kwargs are optional data passed to the `text`
                 outputfunc.
             exclude (list, optional): A list of objects not to send to.
             from_obj (Object, optional): An object designated as the
                 "sender" of the message. See `DefaultObject.msg()` for
                 more info.
         Kwargs:
             Keyword arguments will be passed on to `obj.msg()` for all
             messaged objects.
         """
         # we also accept an outcommand on the form (message, {kwargs})
         is_outcmd = text and is_iter(text)
         message = text[0] if is_outcmd else text
         outkwargs = text[1] if is_outcmd and len(text) > 1 else {}
 
         # Collect exit and entrance locations with no repeats.
         rooms = [exit.destination for exit in self.exits]
         rooms = list(set(rooms))
         if self in rooms: rooms.remove(self)
 
         if exclude:
             exclude = make_iter(exclude)
             rooms = [room for room in rooms if room not in exclude]
         
         for room in rooms:
             room.msg_contents(text=(message, outkwargs), from_obj=from_obj, **kwargs)
Exemple #9
0
    def add(self, cmd):
        """
        Add a command, a list of commands or a cmdset to this cmdset.

        Note that if cmd already exists in set,
        it will replace the old one (no priority checking etc
        at this point; this is often used to overload
        default commands).

        If cmd is another cmdset class or -instance, the commands
        of that command set is added to this one, as if they were part
        of the original cmdset definition. No merging or priority checks
        are made, rather later added commands will simply replace
        existing ones to make a unique set.
        """

        if inherits_from(cmd, "evennia.commands.cmdset.CmdSet"):
            # cmd is a command set so merge all commands in that set
            # to this one. We raise a visible error if we created
            # an infinite loop (adding cmdset to itself somehow)
            try:
                cmd = self._instantiate(cmd)
            except RuntimeError:
                string = "Adding cmdset %(cmd)s to %(class)s lead to an "
                string += "infinite loop. When adding a cmdset to another, "
                string += "make sure they are not themself cyclically added to "
                string += "the new cmdset somewhere in the chain."
                raise RuntimeError(
                    _(string) % {
                        "cmd": cmd,
                        "class": self.__class__
                    })
            cmds = cmd.commands
        elif is_iter(cmd):
            cmds = [self._instantiate(c) for c in cmd]
        else:
            cmds = [self._instantiate(cmd)]
        commands = self.commands
        system_commands = self.system_commands
        for cmd in cmds:
            # add all commands
            if not hasattr(cmd, 'obj'):
                cmd.obj = self.cmdsetobj
            try:
                ic = commands.index(cmd)
                commands[ic] = cmd  # replace
            except ValueError:
                commands.append(cmd)
            # extra run to make sure to avoid doublets
            self.commands = list(set(commands))
            #print "In cmdset.add(cmd):", self.key, cmd
            # add system_command to separate list as well,
            # for quick look-up
            if cmd.key.startswith("__"):
                try:
                    ic = system_commands.index(cmd)
                    system_commands[ic] = cmd  # replace
                except ValueError:
                    system_commands.append(cmd)
Exemple #10
0
def _init_command(mcs, **kwargs):
    """
    Helper command.
    Makes sure all data are stored as lowercase and
    do checking on all properties that should be in list form.
    Sets up locks to be more forgiving. This is used both by the metaclass
    and (optionally) at instantiation time.

    If kwargs are given, these are set as instance-specific properties
    on the command.
    """
    for i in range(len(kwargs)):
        # used for dynamic creation of commands
        key, value = kwargs.popitem()
        setattr(mcs, key, value)

    mcs.key = mcs.key.lower()
    if mcs.aliases and not is_iter(mcs.aliases):
        try:
            mcs.aliases = [str(alias).strip().lower()
                          for alias in mcs.aliases.split(',')]
        except Exception:
            mcs.aliases = []
    mcs.aliases = list(set(alias for alias in mcs.aliases
                           if alias and alias != mcs.key))

    # optimization - a set is much faster to match against than a list
    mcs._matchset = set([mcs.key] + mcs.aliases)
    # optimization for looping over keys+aliases
    mcs._keyaliases = tuple(mcs._matchset)

    # by default we don't save the command between runs
    if not hasattr(mcs, "save_for_next"):
        mcs.save_for_next = False

    # pre-process locks as defined in class definition
    temp = []
    if hasattr(mcs, 'permissions'):
        mcs.locks = mcs.permissions
    if not hasattr(mcs, 'locks'):
        # default if one forgets to define completely
        mcs.locks = "cmd:all()"
    if not "cmd:" in mcs.locks:
        mcs.locks = "cmd:all();" + mcs.locks
    for lockstring in mcs.locks.split(';'):
        if lockstring and not ':' in lockstring:
            lockstring = "cmd:%s" % lockstring
        temp.append(lockstring)
    mcs.lock_storage = ";".join(temp)

    if hasattr(mcs, 'arg_regex') and isinstance(mcs.arg_regex, basestring):
        mcs.arg_regex = re.compile(r"%s" % mcs.arg_regex, re.I + re.UNICODE)
    if not hasattr(mcs, "auto_help"):
        mcs.auto_help = True
    if not hasattr(mcs, 'is_exit'):
        mcs.is_exit = False
    if not hasattr(mcs, "help_category"):
        mcs.help_category = "general"
    mcs.help_category = mcs.help_category.lower()
Exemple #11
0
 def sessions_from_puppet(self, puppet):
     """
     Given a puppeted object, return all controlling sessions.
     """
     sessid = puppet.sessid.get()
     if is_iter(sessid):
         return [self.sessions.get(sid) for sid in sessid if sid in self.sessions]
     return self.sessions.get(sessid)
Exemple #12
0
def value_to_obj(value, force=True):
    "Always convert value(s) to Object, or None"
    stype = type(value)
    if is_iter(value):
        if stype == dict:
            return {value_to_obj_or_any(key): value_to_obj_or_any(val) for key, val in value.iter()}
        else:
            return stype([value_to_obj_or_any(val) for val in value])
    return dbid_to_obj(value, ObjectDB)
Exemple #13
0
 def drill(obj, bucket):
     if isinstance(obj, dict):
         return bucket
     elif utils.is_iter(obj):
         for sub_obj in obj:
             bucket.extend(drill(sub_obj, []))
     else:
         bucket.append(obj)
     return bucket
Exemple #14
0
 def drill(obj, bucket):
     if isinstance(obj, dict):
         return bucket
     elif utils.is_iter(obj):
         for sub_obj in obj:
             bucket.extend(drill(sub_obj, []))
     else:
         bucket.append(obj)
     return bucket
Exemple #15
0
    def do_mssp(self, option):
        """
        Negotiate all the information.

        Args:
            option (Option): Not used.

        """

        self.mssp_table = {

            # Required fields
            "NAME": settings.SERVERNAME,
            "PLAYERS": self.get_player_count,
            "UPTIME": self.get_uptime,
            "PORT": list(reversed(settings.TELNET_PORTS)
                         ),  # most important port should be last in list

            # Evennia auto-filled
            "CRAWL DELAY": "-1",
            "CODEBASE": utils.get_evennia_version(mode='pretty'),
            "FAMILY": "Custom",
            "ANSI": "1",
            "GMCP": "1" if settings.TELNET_OOB_ENABLED else "0",
            "ATCP": "0",
            "MCCP": "1",
            "MCP": "0",
            "MSDP": "1" if settings.TELNET_OOB_ENABLED else "0",
            "MSP": "0",
            "MXP": "1",
            "PUEBLO": "0",
            "SSL": "1" if settings.SSL_ENABLED else "0",
            "UTF-8": "1",
            "ZMP": "0",
            "VT100": "1",
            "XTERM 256 COLORS": "1",
        }

        # update the static table with the custom one
        if MSSPTable_CUSTOM:
            self.mssp_table.update(MSSPTable_CUSTOM)

        varlist = b''
        for variable, value in self.mssp_table.items():
            if callable(value):
                value = value()
            if utils.is_iter(value):
                for partval in value:
                    varlist += (MSSP_VAR + bytes(variable, 'utf-8') +
                                MSSP_VAL + bytes(partval, 'utf-8'))
            else:
                varlist += MSSP_VAR + bytes(
                    variable, 'utf-8') + MSSP_VAL + bytes(value, 'utf-8')

        # send to crawler by subnegotiation
        self.protocol.requestNegotiation(MSSP, varlist)
        self.protocol.handshake_done()
Exemple #16
0
    def add(self, cmd):
        """
        Add a command, a list of commands or a cmdset to this cmdset.

        Note that if cmd already exists in set,
        it will replace the old one (no priority checking etc
        at this point; this is often used to overload
        default commands).

        If cmd is another cmdset class or -instance, the commands
        of that command set is added to this one, as if they were part
        of the original cmdset definition. No merging or priority checks
        are made, rather later added commands will simply replace
        existing ones to make a unique set.
        """

        if inherits_from(cmd, "evennia.commands.cmdset.CmdSet"):
            # cmd is a command set so merge all commands in that set
            # to this one. We raise a visible error if we created
            # an infinite loop (adding cmdset to itself somehow)
            try:
                cmd = self._instantiate(cmd)
            except RuntimeError:
                string = "Adding cmdset %(cmd)s to %(class)s lead to an "
                string += "infinite loop. When adding a cmdset to another, "
                string += "make sure they are not themself cyclically added to "
                string += "the new cmdset somewhere in the chain."
                raise RuntimeError(_(string) % {"cmd": cmd,
                                                "class": self.__class__})
            cmds = cmd.commands
        elif is_iter(cmd):
            cmds = [self._instantiate(c) for c in cmd]
        else:
            cmds = [self._instantiate(cmd)]
        commands = self.commands
        system_commands = self.system_commands
        for cmd in cmds:
            # add all commands
            if not hasattr(cmd, 'obj'):
                cmd.obj = self.cmdsetobj
            try:
                ic = commands.index(cmd)
                commands[ic] = cmd  # replace
            except ValueError:
                commands.append(cmd)
            # extra run to make sure to avoid doublets
            self.commands = list(set(commands))
            #print "In cmdset.add(cmd):", self.key, cmd
            # add system_command to separate list as well,
            # for quick look-up
            if cmd.key.startswith("__"):
                try:
                    ic = system_commands.index(cmd)
                    system_commands[ic] = cmd  # replace
                except ValueError:
                    system_commands.append(cmd)
Exemple #17
0
 def session_from_sessid(self, sessid):
     """
     Return session based on sessid, or None if not found
     """
     if is_iter(sessid):
         return [
             self.sessions.get(sid) for sid in sessid
             if sid in self.sessions
         ]
     return self.sessions.get(sessid)
Exemple #18
0
 def _iter(obj):
     typ = type(obj)
     tname = typ.__name__
     if tname in ("_SaverDict", "dict"):
         return {_iter(key): _iter(val) for key, val in obj.items()}
     elif tname in _DESERIALIZE_MAPPING:
         return _DESERIALIZE_MAPPING[tname](_iter(val) for val in obj)
     elif is_iter(obj):
         return typ(_iter(val) for val in obj)
     return obj
Exemple #19
0
 def session_from_player(self, player, sessid):
     """
     Given a player and a session id, return the actual session object
     """
     if is_iter(sessid):
         sessions = [self.sessions.get(sid) for sid in sessid]
         s = [sess for sess in sessions if sess and sess.logged_in and player.uid == sess.uid]
         return s
     session = self.sessions.get(sessid)
     return session and session.logged_in and player.uid == session.uid and session or None
Exemple #20
0
    def page_formatter(self, page):
        """Input is a queryset page from django.Paginator"""
        caller = self._caller

        # get use-permissions of readonly attributes (edit is always False)
        display_tuples = []

        table = EvTable(
            "|wKey|n",
            "|wSpawn/Edit|n",
            "|wTags|n",
            "|wDesc|n",
            border="tablecols",
            crop=True,
            width=self.width,
        )

        for prototype in page:
            lock_use = caller.locks.check_lockstring(caller,
                                                     prototype.get(
                                                         "prototype_locks",
                                                         ""),
                                                     access_type="spawn",
                                                     default=True)
            if not self.show_non_use and not lock_use:
                continue
            if prototype.get("prototype_key", "") in _MODULE_PROTOTYPES:
                lock_edit = False
            else:
                lock_edit = caller.locks.check_lockstring(
                    caller,
                    prototype.get("prototype_locks", ""),
                    access_type="edit",
                    default=True)
            if not self.show_non_edit and not lock_edit:
                continue
            ptags = []
            for ptag in prototype.get("prototype_tags", []):
                if is_iter(ptag):
                    if len(ptag) > 1:
                        ptags.append("{}".format(ptag[0]))
                    else:
                        ptags.append(ptag[0])
                else:
                    ptags.append(str(ptag))

            table.add_row(
                prototype.get("prototype_key", "<unset>"),
                "{}/{}".format("Y" if lock_use else "N",
                               "Y" if lock_edit else "N"),
                ", ".join(list(set(ptags))),
                prototype.get("prototype_desc", "<unset>"),
            )

        return str(table)
Exemple #21
0
 def sessions_from_puppet(self, puppet):
     """
     Given a puppeted object, return all controlling sessions.
     """
     sessid = puppet.sessid.get()
     if is_iter(sessid):
         return [
             self.sessions.get(sid) for sid in sessid
             if sid in self.sessions
         ]
     return self.sessions.get(sessid)
Exemple #22
0
def value_to_obj_or_any(value):
    "Convert value(s) to Object if possible, otherwise keep original value"
    stype = type(value)
    if is_iter(value):
        if stype == dict:
            return {value_to_obj_or_any(key):
                    value_to_obj_or_any(val) for key, val in value.items()}
        else:
            return stype([value_to_obj_or_any(val) for val in value])
    obj = dbid_to_obj(value, ObjectDB)
    return obj if obj is not None else value
Exemple #23
0
 def _visualize(obj, rootname, get_name=False):
     if is_iter(obj):
         if not obj:
             return str(obj)
         if get_name:
             return obj[0] if obj[0] else "<unset>"
         if rootname == "attrs":
             return "{} |w=|n {} |w(category:|n |n{}|w, locks:|n {}|w)|n".format(*obj)
         elif rootname == "tags":
             return "{} |w(category:|n {}|w)|n".format(obj[0], obj[1])
     return "{}".format(obj)
Exemple #24
0
def homogenize_prototype(prototype, custom_keys=None):
    """
    Homogenize the more free-form prototype supported pre Evennia 0.7 into the stricter form.


    Args:
        prototype (dict): Prototype.
        custom_keys (list, optional): Custom keys which should not be interpreted as attrs, beyond
            the default reserved keys.

    Returns:
        homogenized (dict): Prototype where all non-identified keys grouped as attributes and other
            homogenizations like adding missing prototype_keys and setting a default typeclass.

    """
    reserved = _PROTOTYPE_RESERVED_KEYS + (custom_keys or ())

    attrs = list(prototype.get("attrs", []))  # break reference
    tags = make_iter(prototype.get("tags", []))
    homogenized_tags = []

    homogenized = {}
    for key, val in prototype.items():
        if key in reserved:
            if key == "tags":
                for tag in tags:
                    if not is_iter(tag):
                        homogenized_tags.append((tag, None, None))
                    else:
                        homogenized_tags.append(tag)
            else:
                homogenized[key] = val
        else:
            # unassigned keys -> attrs
            attrs.append((key, val, None, ""))
    if attrs:
        homogenized["attrs"] = attrs
    if homogenized_tags:
        homogenized["tags"] = homogenized_tags

    # add required missing parts that had defaults before

    if "prototype_key" not in prototype:
        # assign a random hash as key
        homogenized["prototype_key"] = "prototype-{}".format(
            hashlib.md5(bytes(str(time.time()), "utf-8")).hexdigest()[:7]
        )

    if "typeclass" not in prototype and "prototype_parent" not in prototype:
        homogenized["typeclass"] = settings.BASE_OBJECT_TYPECLASS

    return homogenized
Exemple #25
0
    def decode_gmcp(self, data):
        """
        Decodes incoming GMCP data on the form 'varname <structure>'.

        Args:
            data (str or list): GMCP data.

        Notes:
            Clients send data on the form "Module.Submodule.Cmdname <structure>".
            We assume the structure is valid JSON.

            The following is parsed into Evennia's formal structure:

            ```
            Core.Name                         -> [name, [], {}]
            Core.Name string                  -> [name, [string], {}]
            Core.Name [arg, arg,...]          -> [name, [args], {}]
            Core.Name {key:arg, key:arg, ...} -> [name, [], {kwargs}]
            Core.Name [[args], {kwargs}]      -> [name, [args], {kwargs}]
            ```

        """
        if isinstance(data, list):
            data = b"".join(data)

        # print("decode_gmcp in:", data)  # DEBUG
        if data:
            try:
                cmdname, structure = data.split(None, 1)
            except ValueError:
                cmdname, structure = data, b""
            cmdname = cmdname.replace(b".", b"_")
            try:
                structure = json.loads(structure)
            except ValueError:
                # maybe the structure is not json-serialized at all
                pass
            args, kwargs = [], {}
            if is_iter(structure):
                if isinstance(structure, dict):
                    kwargs = {
                        key: value
                        for key, value in structure.items() if key
                    }
                else:
                    args = list(structure)
            else:
                args = (structure, )
            if cmdname.lower().startswith(b"core_"):
                # if Core.cmdname, then use cmdname
                cmdname = cmdname[5:]
            self.protocol.data_in(**{cmdname.lower().decode(): [args, kwargs]})
Exemple #26
0
def _to_ansi(obj, regexable=False):
    "convert to ANSIString"
    if isinstance(obj, str):
        # 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 is_iter(obj):
        return [_to_ansi(o) for o in obj]
    else:
        return ANSIString(obj, regexable=regexable)
Exemple #27
0
 def session_from_player(self, player, sessid):
     """
     Given a player and a session id, return the actual session object
     """
     if is_iter(sessid):
         sessions = [self.sessions.get(sid) for sid in sessid]
         s = [
             sess for sess in sessions
             if sess and sess.logged_in and player.uid == sess.uid
         ]
         return s
     session = self.sessions.get(sessid)
     return session and session.logged_in and player.uid == session.uid and session or None
Exemple #28
0
def homogenize_prototype(prototype, custom_keys=None):
    """
    Homogenize the more free-form prototype supported pre Evennia 0.7 into the stricter form.


    Args:
        prototype (dict): Prototype.
        custom_keys (list, optional): Custom keys which should not be interpreted as attrs, beyond
            the default reserved keys.

    Returns:
        homogenized (dict): Prototype where all non-identified keys grouped as attributes and other
            homogenizations like adding missing prototype_keys and setting a default typeclass.

    """
    reserved = _PROTOTYPE_RESERVED_KEYS + (custom_keys or ())

    attrs = list(prototype.get('attrs', []))  # break reference
    tags = make_iter(prototype.get('tags', []))
    homogenized_tags = []

    homogenized = {}
    for key, val in prototype.items():
        if key in reserved:
            if key == 'tags':
                for tag in tags:
                    if not is_iter(tag):
                        homogenized_tags.append((tag, None, None))
                    else:
                        homogenized_tags.append(tag)
            else:
                homogenized[key] = val
        else:
            # unassigned keys -> attrs
            attrs.append((key, val, None, ''))
    if attrs:
        homogenized['attrs'] = attrs
    if homogenized_tags:
        homogenized['tags'] = homogenized_tags

    # add required missing parts that had defaults before

    if "prototype_key" not in prototype:
        # assign a random hash as key
        homogenized["prototype_key"] = "prototype-{}".format(
            hashlib.md5(str(time.time())).hexdigest()[:7])

    if "typeclass" not in prototype and "prototype_parent" not in prototype:
        homogenized["typeclass"] = settings.BASE_OBJECT_TYPECLASS

    return homogenized
Exemple #29
0
    def session_from_sessid(self, sessid):
        """
        Get session based on sessid, or None if not found

        Args:
            sessid (int or list): Session id(s)

        Return:
            sessions (Session or list): Session(s) found.

        """
        if is_iter(sessid):
            return [self.sessions.get(sid) for sid in sessid if sid in self.sessions]
        return self.sessions.get(sessid)
Exemple #30
0
    def session_from_sessid(self, sessid):
        """
        Get session based on sessid, or None if not found

        Args:
            sessid (int or list): Session id(s).

        Return:
            sessions (Session or list): Session(s) found. This
                is a list if input was a list.

        """
        if is_iter(sessid):
            return [self.get(sid) for sid in sessid if sid in self]
        return self.get(sessid)
Exemple #31
0
    def session_from_sessid(self, sessid):
        """
        Get session based on sessid, or None if not found

        Args:
            sessid (int or list): Session id(s).

        Return:
            sessions (Session or list): Session(s) found. This
                is a list if input was a list.

        """
        if is_iter(sessid):
            return [self.get(sid) for sid in sessid if sid in self]
        return self.get(sessid)
Exemple #32
0
    def sessions_from_puppet(self, puppet):
        """
        Given a puppeted object, return all controlling sessions.

        Args:
            puppet (Object): Object puppeted

        Returns.
            sessions (Session or list): Can be more than one of Object is controlled by
                more than one Session (MULTISESSION_MODE > 1).

        """
        sessid = puppet.sessid.get()
        if is_iter(sessid):
            return [self.sessions.get(sid) for sid in sessid if sid in self.sessions]
        return self.sessions.get(sessid)
Exemple #33
0
    def _recursive_diff(old, new, depth=0):

        old_type = type(old)
        new_type = type(new)

        if old_type == new_type and not (old or new):
            # both old and new are unset, like [] or None
            return (None, None, "KEEP")
        if old_type != new_type:
            if old and not new:
                if depth < maxdepth and old_type == dict:
                    return {key: (part, None, "REMOVE") for key, part in old.items()}
                elif depth < maxdepth and is_iter(old):
                    return {
                        part[0] if is_iter(part) else part: (part, None, "REMOVE") for part in old
                    }
                if isinstance(new, Unset) and implicit_keep:
                    # the new does not define any change, use implicit-keep
                    return (old, None, "KEEP")
                return (old, new, "REMOVE")
            elif not old and new:
                if depth < maxdepth and new_type == dict:
                    return {key: (None, part, "ADD") for key, part in new.items()}
                elif depth < maxdepth and is_iter(new):
                    return {part[0] if is_iter(part) else part: (None, part, "ADD") for part in new}
                return (old, new, "ADD")
            else:
                # this condition should not occur in a standard diff
                return (old, new, "UPDATE")
        elif depth < maxdepth and new_type == dict:
            all_keys = set(list(old.keys()) + list(new.keys()))
            return {
                key: _recursive_diff(old.get(key, _unset), new.get(key, _unset), depth=depth + 1)
                for key in all_keys
            }
        elif depth < maxdepth and is_iter(new):
            old_map = {part[0] if is_iter(part) else part: part for part in old}
            new_map = {part[0] if is_iter(part) else part: part for part in new}
            all_keys = set(list(old_map.keys()) + list(new_map.keys()))
            return {
                key: _recursive_diff(
                    old_map.get(key, _unset), new_map.get(key, _unset), depth=depth + 1
                )
                for key in all_keys
            }
        elif old != new:
            return (old, new, "UPDATE")
        else:
            return (old, new, "KEEP")
Exemple #34
0
    def get_objs_with_key_or_alias(self, ostring, exact=True,
                                         candidates=None, typeclasses=None):
        """
        Returns objects based on key or alias match. Will also do fuzzy
        matching based on the `utils.string_partial_matching` function.
        candidates - list of candidate objects to restrict on
        typeclasses - list of typeclass path strings to restrict on
        """
        if not isinstance(ostring, basestring):
            if hasattr(ostring, "key"):
                ostring = ostring.key
            else:
                return []
        if is_iter(candidates) and not len(candidates):
            # if candidates is an empty iterable there can be no matches
            # Exit early.
            return []

        # build query objects
        candidates_id = [_GA(obj, "id") for obj in make_iter(candidates) if obj]
        cand_restriction = candidates != None and Q(pk__in=make_iter(candidates_id)) or Q()
        type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
        if exact:
            # exact match - do direct search
            return self.filter(cand_restriction & type_restriction & (Q(db_key__iexact=ostring) |
                               Q(db_tags__db_key__iexact=ostring) & Q(db_tags__db_tagtype__iexact="alias"))).distinct()
        elif candidates:
            # fuzzy with candidates
            key_candidates = self.filter(cand_restriction & type_restriction)
        else:
            # fuzzy without supplied candidates - we select our own candidates
            key_candidates = self.filter(type_restriction & (Q(db_key__istartswith=ostring) | Q(db_tags__db_key__istartswith=ostring))).distinct()
            candidates_id = [_GA(obj, "id") for obj in key_candidates]
        # fuzzy matching
        key_strings = key_candidates.values_list("db_key", flat=True).order_by("id")

        index_matches = string_partial_matching(key_strings, ostring, ret_index=True)
        if index_matches:
            return [obj for ind, obj in enumerate(key_candidates) if ind in index_matches]
        else:
            alias_candidates = self.filter(id__in=candidates_id, db_tags__db_tagtype__iexact="alias")
            alias_strings = alias_candidates.values_list("db_key", flat=True)
            index_matches = string_partial_matching(alias_strings, ostring, ret_index=True)
            if index_matches:
                return [alias.db_obj for ind, alias in enumerate(alias_candidates) if ind in index_matches]
            return []
Exemple #35
0
    def session_from_sessid(self, sessid):
        """
        Get session based on sessid, or None if not found

        Args:
            sessid (int or list): Session id(s)

        Return:
            sessions (Session or list): Session(s) found.

        """
        if is_iter(sessid):
            return [
                self.sessions.get(sid) for sid in sessid
                if sid in self.sessions
            ]
        return self.sessions.get(sessid)
Exemple #36
0
    def sessions_from_puppet(self, puppet):
        """
        Given a puppeted object, return all controlling sessions.

        Args:
            puppet (Object): Object puppeted

        Returns.
            sessions (Session or list): Can be more than one of Object is controlled by
                more than one Session (MULTISESSION_MODE > 1).

        """
        sessid = puppet.sessid.get()
        if is_iter(sessid):
            return [
                self.sessions.get(sid) for sid in sessid
                if sid in self.sessions
            ]
        return self.sessions.get(sessid)
Exemple #37
0
    def session_from_player(self, player, sessid):
        """
        Given a player and a session id, return the actual session
        object.

        Args:
            player (Player): The Player to get the Session from.
            sessid (int or list): Session id(s).

        Returns:
            sessions (Session or list): Session(s) found.

        """
        if is_iter(sessid):
            sessions = [self.sessions.get(sid) for sid in sessid]
            s = [sess for sess in sessions if sess and sess.logged_in and player.uid == sess.uid]
            return s
        session = self.sessions.get(sessid)
        return session and session.logged_in and player.uid == session.uid and session or None
Exemple #38
0
    def set_aliases(self, new_aliases):
        """
        Update aliases.

        Args:
            new_aliases (list):

        Notes:
            This is necessary to use to make sure the optimization
            caches are properly updated as well.

        """
        if not is_iter(new_aliases):
            try:
                self.aliases = [str(alias).strip().lower() for alias in self.aliases.split(",")]
            except Exception:
                self.aliases = []
        self.aliases = list(set(alias for alias in self.aliases if alias and alias != self.key))
        self._optimize()
Exemple #39
0
    def at_look(self, target=None, session=None):
        """
        Called when this object executes a look. It allows to customize
        just what this means.

        Args:
            target (Object or list, optional): An object or a list
                objects to inspect.
            session (Session, optional): The session doing this look.

        Returns:
            look_string (str): A prepared look string, ready to send
                off to any recipient (usually to ourselves)

        """

        if target and not is_iter(target):
            # single target - just show it
            return target.return_appearance()
        else:
            self.render.render_login(session)
Exemple #40
0
    def set_aliases(self, new_aliases):
        """
        Update aliases.

        Args:
            new_aliases (list):

        Notes:
            This is necessary to use to make sure the optimization
            caches are properly updated as well.

        """
        if not is_iter(new_aliases):
            try:
                self.aliases = [str(alias).strip().lower()
                                for alias in self.aliases.split(',')]
            except Exception:
                self.aliases = []
        self.aliases = list(set(alias for alias in self.aliases
                            if alias and alias != self.key))
        self._optimize()
Exemple #41
0
    def search_dbref(self, dbref, location=None):
        """
        Search as an object by its dbref.

        Args:
            dbref: (string)dbref.

        Returns:
            The object or None.
        """
        match = ObjectDB.objects.dbref_search(dbref)

        if match and location:
            # match the location
            if is_iter(location):
                if not [l for l in location if match.location == l]:
                    match = None
            elif match.location != location:
                match = None

        return match
Exemple #42
0
    def search_dbref(self, dbref, location=None):
        """
        Search as an object by its dbref.

        Args:
            dbref: (string)dbref.

        Returns:
            The object or None.
        """
        match = ObjectDB.objects.dbref_search(dbref)

        if match and location:
            # match the location
            if is_iter(location):
                if not [l for l in location if match.location == l]:
                    match = None
            elif match.location != location:
                match = None

        return match
Exemple #43
0
    def session_from_player(self, player, sessid):
        """
        Given a player and a session id, return the actual session
        object.

        Args:
            player (Player): The Player to get the Session from.
            sessid (int or list): Session id(s).

        Returns:
            sessions (Session or list): Session(s) found.

        """
        if is_iter(sessid):
            sessions = [self.sessions.get(sid) for sid in sessid]
            s = [
                sess for sess in sessions
                if sess and sess.logged_in and player.uid == sess.uid
            ]
            return s
        session = self.sessions.get(sessid)
        return session and session.logged_in and player.uid == session.uid and session or None
Exemple #44
0
    def _recursive_diff(old, new, depth=0):

        old_type = type(old)
        new_type = type(new)

        if old_type != new_type:
            if old and not new:
                if depth < maxdepth and old_type == dict:
                    return {key: (part, None, "REMOVE") for key, part in old.items()}
                elif depth < maxdepth and is_iter(old):
                    return {part[0] if is_iter(part) else part:
                            (part, None, "REMOVE") for part in old}
                return (old, new, "REMOVE")
            elif not old and new:
                if depth < maxdepth and new_type == dict:
                    return {key: (None, part, "ADD") for key, part in new.items()}
                elif depth < maxdepth and is_iter(new):
                    return {part[0] if is_iter(part) else part: (None, part, "ADD") for part in new}
                return (old, new, "ADD")
            else:
                # this condition should not occur in a standard diff
                return (old, new, "UPDATE")
        elif depth < maxdepth and new_type == dict:
            all_keys = set(old.keys() + new.keys())
            return {key: _recursive_diff(old.get(key), new.get(key), depth=depth + 1)
                    for key in all_keys}
        elif depth < maxdepth and is_iter(new):
            old_map = {part[0] if is_iter(part) else part: part for part in old}
            new_map = {part[0] if is_iter(part) else part: part for part in new}
            all_keys = set(old_map.keys() + new_map.keys())
            return {key: _recursive_diff(old_map.get(key), new_map.get(key), depth=depth + 1)
                    for key in all_keys}
        elif old != new:
            return (old, new, "UPDATE")
        else:
            return (old, new, "KEEP")
Exemple #45
0
    def at_look(self, target=None, session=None):
        """
        Called when this object executes a look. It allows to customize
        just what this means.

        Args:
            target (Object or list, optional): An object or a list
                objects to inspect.
            session (Session, optional): The session doing this look.

        Returns:
            look_string (str): A prepared look string, ready to send
                off to any recipient (usually to ourselves)

        """

        if target and not is_iter(target):
            # single target - just show it
            return target.return_appearance()
        else:
            # list of targets - make list
            characters = target
            sessions = self.sessions.all()
            is_su = self.is_superuser

            # text shown when looking in the ooc area
            string = "Account {g%s{n (you are Out-of-Character)" % (self.key)

            nsess = len(sessions)
            string += nsess == 1 and "\n\n{wConnected session:{n" or "\n\n{wConnected sessions (%i):{n" % nsess
            for isess, sess in enumerate(sessions):
                csessid = sess.sessid
                addr = "%s (%s)" % (sess.protocol_key, isinstance(sess.address, tuple) and str(sess.address[0]) or str(sess.address))
                string += "\n %s %s" % (session.sessid == csessid and "{w* %s{n" % (isess + 1) or "  %s" % (isess + 1), addr)
            string += "\n\n {whelp{n - more commands"
            string += "\n {wooc <Text>{n - talk on public channel"

            charmax = _MAX_NR_CHARACTERS if _MULTISESSION_MODE > 1 else 1

            if is_su or len(characters) < charmax:
                if not characters:
                    string += "\n\n You don't have any characters yet. See {whelp @charcreate{n for creating one."
                else:
                    string += "\n {w@charcreate <name> [=description]{n - create new character"

            if characters:
                string_s_ending = len(characters) > 1 and "s" or ""
                string += "\n {w@ic <character>{n - enter the game ({w@ooc{n to get back here)"
                if is_su:
                    string += "\n\nAvailable character%s (%i/unlimited):" % (string_s_ending, len(characters))
                else:
                    string += "\n\nAvailable character%s%s:"  % (string_s_ending,
                             charmax > 1 and " (%i/%i)" % (len(characters), charmax) or "")

                for char in characters:
                    csessions = char.sessions.all()
                    if csessions:
                        for sess in csessions:
                            # character is already puppeted
                            sid = sess in sessions and sessions.index(sess) + 1
                            if sess and sid:
                                string += "\n - {G%s{n [%s] (played by you in session %i)" % (char.key, ", ".join(char.permissions.all()), sid)
                            else:
                                string += "\n - {R%s{n [%s] (played by someone else)" % (char.key, ", ".join(char.permissions.all()))
                    else:
                        # character is "free to puppet"
                        string += "\n - %s [%s]" % (char.key, ", ".join(char.permissions.all()))
            string = ("-" * 68) + "\n" + string + "\n" + ("-" * 68)
            return string
Exemple #46
0
    def batch_add(self, *args, **kwargs):
        """
        Batch-version of `add()`. This is more efficient than
        repeat-calling add when having many Attributes to add.

        Args:
            indata (tuple): Tuples of varying length representing the
                Attribute to add to this object.
                    - `(key, value)`
                    - `(key, value, category)`
                    - `(key, value, category, lockstring)`
                    - `(key, value, category, lockstring, default_access)`

        Kwargs:
            strattr (bool): If `True`, value must be a string. This
                will save the value without pickling which is less
                flexible but faster to search (not often used except
                internally).

        Raises:
            RuntimeError: If trying to pass a non-iterable as argument.

        Notes:
            The indata tuple order matters, so if you want a lockstring
            but no category, set the category to `None`. This method
            does not have the ability to check editing permissions like
            normal .add does, and is mainly used internally. It does not
            use the normal self.add but apply the Attributes directly
            to the database.

        """
        new_attrobjs = []
        strattr = kwargs.get('strattr', False)
        for tup in args:
            if not is_iter(tup) or len(tup) < 2:
                raise RuntimeError("batch_add requires iterables as arguments (got %r)." % tup)
            ntup = len(tup)
            keystr = str(tup[0]).strip().lower()
            new_value = tup[1]
            category = str(tup[2]).strip().lower() if ntup > 2 else None
            lockstring = tup[3] if ntup > 3 else ""

            attr_objs = self._getcache(keystr, category)

            if attr_objs:
                attr_obj = attr_objs[0]
                # update an existing attribute object
                attr_obj.db_category = category
                attr_obj.db_lock_storage = lockstring
                attr_obj.save(update_fields=["db_category", "db_lock_storage"])
                if strattr:
                    # store as a simple string (will not notify OOB handlers)
                    attr_obj.db_strvalue = new_value
                    attr_obj.save(update_fields=["db_strvalue"])
                else:
                    # store normally (this will also notify OOB handlers)
                    attr_obj.value = new_value
            else:
                # create a new Attribute (no OOB handlers can be notified)
                kwargs = {"db_key": keystr,
                          "db_category": category,
                          "db_model": self._model,
                          "db_attrtype": self._attrtype,
                          "db_value": None if strattr else to_pickle(new_value),
                          "db_strvalue": new_value if strattr else None,
                          "db_lock_storage": lockstring}
                new_attr = Attribute(**kwargs)
                new_attr.save()
                new_attrobjs.append(new_attr)
                self._setcache(keystr, category, new_attr)
        if new_attrobjs:
            # Add new objects to m2m field all at once
            getattr(self.obj, self._m2m_fieldname).add(*new_attrobjs)
Exemple #47
0
    def swap_typeclass(self, new_typeclass, clean_attributes=False,
                       run_start_hooks=True, no_default=True):
        """
        This performs an in-situ swap of the typeclass. This means
        that in-game, this object will suddenly be something else.
        Player will not be affected. To 'move' a player to a different
        object entirely (while retaining this object's type), use
        self.player.swap_object().

        Note that this might be an error prone operation if the
        old/new typeclass was heavily customized - your code
        might expect one and not the other, so be careful to
        bug test your code if using this feature! Often its easiest
        to create a new object and just swap the player over to
        that one instead.

        Args:
            new_typeclass (str or classobj): Type to switch to.
            clean_attributes (bool or list, optional): Will delete all
                attributes stored on this object (but not any of the
                database fields such as name or location). You can't get
                attributes back, but this is often the safest bet to make
                sure nothing in the new typeclass clashes with the old
                one. If you supply a list, only those named attributes
                will be cleared.
            run_start_hooks (bool, optional): Trigger the start hooks
                of the object, as if it was created for the first time.
            no_default (bool, optiona): If set, the swapper will not
                allow for swapping to a default typeclass in case the
                given one fails for some reason. Instead the old one will
                be preserved.
        Returns:
            result (bool): True/False depending on if the swap worked
                or not.

        """

        if not callable(new_typeclass):
            # this is an actual class object - build the path
            new_typeclass = class_from_module(new_typeclass, defaultpaths=settings.TYPECLASS_PATHS)

        # if we get to this point, the class is ok.


        if inherits_from(self, "evennia.scripts.models.ScriptDB"):
            if self.interval > 0:
                raise RuntimeError("Cannot use swap_typeclass on time-dependent " \
                                   "Script '%s'.\nStop and start a new Script of the " \
                                   "right type instead." % self.key)

        self.typeclass_path = new_typeclass.path
        self.__class__ = new_typeclass

        if clean_attributes:
            # Clean out old attributes
            if is_iter(clean_attributes):
                for attr in clean_attributes:
                    self.attributes.remove(attr)
                for nattr in clean_attributes:
                    if hasattr(self.ndb, nattr):
                        self.nattributes.remove(nattr)
            else:
                self.attributes.clear()
                self.nattributes.clear()

        if run_start_hooks:
            # fake this call to mimic the first save
            self.at_first_save()
Exemple #48
0
    def audit(self, **kwargs):
        """
        Extracts messages and system data from a Session object upon message
        send or receive.

        Kwargs:
            src (str): Source of data; 'client' or 'server'. Indicates direction.
            text (str or list): Client sends messages to server in the form of
                lists. Server sends messages to client as string.

        Returns:
            log (dict): Dictionary object containing parsed system and user data
                related to this message.

        """
        # Get time at start of processing
        time_obj = timezone.now()
        time_str = str(time_obj)

        session = self
        src = kwargs.pop('src', '?')
        bytecount = 0

        # Do not log empty lines
        if not kwargs:
            return {}

        # Get current session's IP address
        client_ip = session.address

        # Capture Account name and dbref together
        account = session.get_account()
        account_token = ''
        if account:
            account_token = '%s%s' % (account.key, account.dbref)

        # Capture Character name and dbref together
        char = session.get_puppet()
        char_token = ''
        if char:
            char_token = '%s%s' % (char.key, char.dbref)

        # Capture Room name and dbref together
        room = None
        room_token = ''
        if char:
            room = char.location
            room_token = '%s%s' % (room.key, room.dbref)

        # Try to compile an input/output string
        def drill(obj, bucket):
            if isinstance(obj, dict):
                return bucket
            elif utils.is_iter(obj):
                for sub_obj in obj:
                    bucket.extend(drill(sub_obj, []))
            else:
                bucket.append(obj)
            return bucket

        text = kwargs.pop('text', '')
        if utils.is_iter(text):
            text = '|'.join(drill(text, []))

        # Mask any PII in message, where possible
        bytecount = len(text.encode('utf-8'))
        text = self.mask(text)

        # Compile the IP, Account, Character, Room, and the message.
        log = {
            'time': time_str,
            'hostname': socket.getfqdn(),
            'application': '%s' % ev_settings.SERVERNAME,
            'version': get_evennia_version(),
            'pid': os.getpid(),
            'direction': 'SND' if src == 'server' else 'RCV',
            'protocol': self.protocol_key,
            'ip': client_ip,
            'session': 'session#%s' % self.sessid,
            'account': account_token,
            'character': char_token,
            'room': room_token,
            'text': text.strip(),
            'bytes': bytecount,
            'data': kwargs,
            'objects': {
                'time': time_obj,
                'session': self,
                'account': account,
                'character': char,
                'room': room,
            }
        }

        # Remove any keys with blank values
        if AUDIT_ALLOW_SPARSE is False:
            log['data'] = {k: v for k, v in log['data'].iteritems() if v}
            log['objects'] = {k: v for k, v in log['objects'].iteritems() if v}
            log = {k: v for k, v in log.iteritems() if v}

        return log
Exemple #49
0
    def do_mssp(self, option):
        """
        Negotiate all the information.

        Args:
            option (Option): Not used.

        """

        self.mssp_table =  {

        # Required fields

        "NAME":               "Evennia",
        "PLAYERS":            self.get_player_count,
        "UPTIME" :            self.get_uptime,

        # Generic

        "CRAWL DELAY":        "-1",

        "HOSTNAME":           "",       # current or new hostname
        "PORT":               ["4000"], # most important port should be last in list
        "CODEBASE":           "Evennia",
        "CONTACT":            "",       # email for contacting the mud
        "CREATED":            "",       # year MUD was created
        "ICON":               "",       # url to icon 32x32 or larger; <32kb.
        "IP":                 "",       # current or new IP address
        "LANGUAGE":           "",       # name of language used, e.g. English
        "LOCATION":           "",       # full English name of server country
        "MINIMUM AGE":        "0",      # set to 0 if not applicable
        "WEBSITE":            "www.evennia.com",

        # Categorisation

        "FAMILY":             "Custom", # evennia goes under 'Custom'
        "GENRE":              "None",   # Adult, Fantasy, Historical, Horror, Modern, None, or Science Fiction
        "GAMEPLAY":           "None",   # Adventure, Educational, Hack and Slash, None,
                                        # Player versus Player, Player versus Environment,
                                        # Roleplaying, Simulation, Social or Strategy
        "STATUS":             "Open Beta",  # Alpha, Closed Beta, Open Beta, Live
        "GAMESYSTEM":         "Custom", # D&D, d20 System, World of Darkness, etc. Use Custom if homebrew
        "INTERMUD":           "IMC2",   # evennia supports IMC2.
        "SUBGENRE":           "None",   # LASG, Medieval Fantasy, World War II, Frankenstein,
                                        # Cyberpunk, Dragonlance, etc. Or None if not available.

        # World

        "AREAS":              "0",
        "HELPFILES":          "0",
        "MOBILES":            "0",
        "OBJECTS":            "0",
        "ROOMS":              "0",      # use 0 if room-less
        "CLASSES":            "0",      # use 0 if class-less
        "LEVELS":             "0",      # use 0 if level-less
        "RACES":              "0",      # use 0 if race-less
        "SKILLS":             "0",      # use 0 if skill-less

        # Protocols set to 1 or 0)

        "ANSI":               "1",
        "GMCP":               "0",
        "MCCP":               "0",
        "MCP":                "0",
        "MSDP":               "0",
        "MSP":                "0",
        "MXP":                "0",
        "PUEBLO":             "0",
        "UTF-8":              "1",
        "VT100":              "0",
        "XTERM 256 COLORS":   "0",

        # Commercial set to 1 or 0)

        "PAY TO PLAY":        "0",
        "PAY FOR PERKS":      "0",

        # Hiring  set to 1 or 0)

        "HIRING BUILDERS":    "0",
        "HIRING CODERS":      "0",

        # Extended variables

        # World

        "DBSIZE":             "0",
        "EXITS":              "0",
        "EXTRA DESCRIPTIONS": "0",
        "MUDPROGS":           "0",
        "MUDTRIGS":           "0",
        "RESETS":             "0",

        # Game (set to 1, 0 or one of the given alternatives)

        "ADULT MATERIAL":     "0",
        "MULTICLASSING":      "0",
        "NEWBIE FRIENDLY":    "0",
        "PLAYER CITIES":      "0",
        "PLAYER CLANS":       "0",
        "PLAYER CRAFTING":    "0",
        "PLAYER GUILDS":      "0",
        "EQUIPMENT SYSTEM":   "None",  # "None", "Level", "Skill", "Both"
        "MULTIPLAYING":       "None",  # "None", "Restricted", "Full"
        "PLAYERKILLING":      "None",  # "None", "Restricted", "Full"
        "QUEST SYSTEM":       "None",  # "None", "Immortal Run", "Automated", "Integrated"
        "ROLEPLAYING":        "None",  # "None", "Accepted", "Encouraged", "Enforced"
        "TRAINING SYSTEM":    "None",  # "None", "Level", "Skill", "Both"
        "WORLD ORIGINALITY":  "None",  # "All Stock", "Mostly Stock", "Mostly Original", "All Original"

        # Protocols (only change if you added/removed something manually)

        "ATCP":               "0",
        "MSDP":               "0",
        "MCCP":               "1",
        "SSL":                "1",
        "UTF-8":              "1",
        "ZMP":                "0",
        "XTERM 256 COLORS":   "0"}

        # update the static table with the custom one
        if MSSPTable_CUSTOM:
            self.mssp_table.update(MSSPTable_CUSTOM)

        varlist = ''
        for variable, value in self.mssp_table.items():
            if callable(value):
                value = value()
            if utils.is_iter(value):
                for partval in value:
                    varlist += MSSP_VAR + str(variable) + MSSP_VAL + str(partval)
            else:
                varlist += MSSP_VAR + str(variable) + MSSP_VAL + str(value)

        # send to crawler by subnegotiation
        self.protocol.requestNegotiation(MSSP, varlist)
        self.protocol.handshake_done()
Exemple #50
0
def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_edit=True):
    """
    Collate a list of found prototypes based on search criteria and access.

    Args:
        caller (Account or Object): The object requesting the list.
        key (str, optional): Exact or partial prototype key to query for.
        tags (str or list, optional): Tag key or keys to query for.
        show_non_use (bool, optional): Show also prototypes the caller may not use.
        show_non_edit (bool, optional): Show also prototypes the caller may not edit.
    Returns:
        table (EvTable or None): An EvTable representation of the prototypes. None
            if no prototypes were found.

    """
    # this allows us to pass lists of empty strings
    tags = [tag for tag in make_iter(tags) if tag]

    # get prototypes for readonly and db-based prototypes
    prototypes = search_prototype(key, tags)

    # get use-permissions of readonly attributes (edit is always False)
    display_tuples = []
    for prototype in sorted(prototypes, key=lambda d: d.get('prototype_key', '')):
        lock_use = caller.locks.check_lockstring(
            caller, prototype.get('prototype_locks', ''), access_type='spawn', default=True)
        if not show_non_use and not lock_use:
            continue
        if prototype.get('prototype_key', '') in _MODULE_PROTOTYPES:
            lock_edit = False
        else:
            lock_edit = caller.locks.check_lockstring(
                caller, prototype.get('prototype_locks', ''), access_type='edit', default=True)
        if not show_non_edit and not lock_edit:
            continue
        ptags = []
        for ptag in prototype.get('prototype_tags', []):
            if is_iter(ptag):
                if len(ptag) > 1:
                    ptags.append("{} (category: {}".format(ptag[0], ptag[1]))
                else:
                    ptags.append(ptag[0])
            else:
                ptags.append(str(ptag))

        display_tuples.append(
            (prototype.get('prototype_key', '<unset>'),
             prototype.get('prototype_desc', '<unset>'),
             "{}/{}".format('Y' if lock_use else 'N', 'Y' if lock_edit else 'N'),
             ",".join(ptags)))

    if not display_tuples:
        return ""

    table = []
    width = 78
    for i in range(len(display_tuples[0])):
        table.append([str(display_tuple[i]) for display_tuple in display_tuples])
    table = EvTable("Key", "Desc", "Spawn/Edit", "Tags", table=table, crop=True, width=width)
    table.reformat_column(0, width=22)
    table.reformat_column(1, width=29)
    table.reformat_column(2, width=11, align='c')
    table.reformat_column(3, width=16)
    return table
Exemple #51
0
 def _to_batchtuple(inp, *args):
     "build tuple suitable for batch-creation"
     if is_iter(inp):
         # already a tuple/list, use as-is
         return inp
     return (inp, ) + args
Exemple #52
0
    def batch_add(self, *args, **kwargs):
        """
        Batch-version of `add()`. This is more efficient than
        repeat-calling add when having many Attributes to add.

        Args:
            indata (list): List of tuples of varying length representing the
                Attribute to add to this object. Supported tuples are
                    - `(key, value)`
                    - `(key, value, category)`
                    - `(key, value, category, lockstring)`
                    - `(key, value, category, lockstring, default_access)`

        Kwargs:
            strattr (bool): If `True`, value must be a string. This
                will save the value without pickling which is less
                flexible but faster to search (not often used except
                internally).

        Raises:
            RuntimeError: If trying to pass a non-iterable as argument.

        Notes:
            The indata tuple order matters, so if you want a lockstring
            but no category, set the category to `None`. This method
            does not have the ability to check editing permissions like
            normal .add does, and is mainly used internally. It does not
            use the normal self.add but apply the Attributes directly
            to the database.

        """
        new_attrobjs = []
        strattr = kwargs.get('strattr', False)
        for tup in args:
            if not is_iter(tup) or len(tup) < 2:
                raise RuntimeError(
                    "batch_add requires iterables as arguments (got %r)." %
                    tup)
            ntup = len(tup)
            keystr = str(tup[0]).strip().lower()
            new_value = tup[1]
            category = str(tup[2]).strip().lower(
            ) if ntup > 2 and tup[2] is not None else None
            lockstring = tup[3] if ntup > 3 else ""

            attr_objs = self._getcache(keystr, category)

            if attr_objs:
                attr_obj = attr_objs[0]
                # update an existing attribute object
                attr_obj.db_category = category
                attr_obj.db_lock_storage = lockstring or ''
                attr_obj.save(update_fields=["db_category", "db_lock_storage"])
                if strattr:
                    # store as a simple string (will not notify OOB handlers)
                    attr_obj.db_strvalue = new_value
                    attr_obj.save(update_fields=["db_strvalue"])
                else:
                    # store normally (this will also notify OOB handlers)
                    attr_obj.value = new_value
            else:
                # create a new Attribute (no OOB handlers can be notified)
                kwargs = {
                    "db_key": keystr,
                    "db_category": category,
                    "db_model": self._model,
                    "db_attrtype": self._attrtype,
                    "db_value": None if strattr else to_pickle(new_value),
                    "db_strvalue": new_value if strattr else None,
                    "db_lock_storage": lockstring or ''
                }
                new_attr = Attribute(**kwargs)
                new_attr.save()
                new_attrobjs.append(new_attr)
                self._setcache(keystr, category, new_attr)
        if new_attrobjs:
            # Add new objects to m2m field all at once
            getattr(self.obj, self._m2m_fieldname).add(*new_attrobjs)
Exemple #53
0
    def at_look(self, target=None, session=None, **kwargs):
        """
        Called when this object executes a look. It allows to customize
        just what this means.

        Args:
            target (Object or list, optional): An object or a list
                objects to inspect.
            session (Session, optional): The session doing this look.
            **kwargs (dict): Arbitrary, optional arguments for users
                overriding the call (unused by default).

        Returns:
            look_string (str): A prepared look string, ready to send
                off to any recipient (usually to ourselves)

        """

        if target and not is_iter(target):
            # single target - just show it
            if hasattr(target, "return_appearance"):
                return target.return_appearance(self)
            else:
                return "{} has no in-game appearance.".format(target)
        else:
            # list of targets - make list to disconnect from db
            characters = list(tar for tar in target if tar) if target else []
            sessions = self.sessions.all()
            is_su = self.is_superuser

            # text shown when looking in the ooc area
            result = ["Account |g%s|n (you are Out-of-Character)" % self.key]

            nsess = len(sessions)
            result.append(nsess == 1 and "\n\n|wConnected session:|n" or "\n\n|wConnected sessions (%i):|n" % nsess)
            for isess, sess in enumerate(sessions):
                csessid = sess.sessid
                addr = "%s (%s)" % (sess.protocol_key, isinstance(sess.address, tuple) and
                                    str(sess.address[0]) or str(sess.address))
                result.append("\n %s %s" % (session.sessid == csessid and "|w* %s|n" % (isess + 1) or
                                            "  %s" % (isess + 1), addr))
            result.append("\n\n |whelp|n - more commands")
            result.append("\n |wooc <Text>|n - talk on public channel")

            charmax = _MAX_NR_CHARACTERS

            if is_su or len(characters) < charmax:
                if not characters:
                    result.append("\n\n You don't have any characters yet. See |whelp @charcreate|n for creating one.")
                else:
                    result.append("\n |w@charcreate <name> [=description]|n - create new character")
                    result.append("\n |w@chardelete <name>|n - delete a character (cannot be undone!)")

            if characters:
                string_s_ending = len(characters) > 1 and "s" or ""
                result.append("\n |w@ic <character>|n - enter the game (|w@ooc|n to get back here)")
                if is_su:
                    result.append("\n\nAvailable character%s (%i/unlimited):" % (string_s_ending, len(characters)))
                else:
                    result.append("\n\nAvailable character%s%s:"
                                  % (string_s_ending, charmax > 1 and " (%i/%i)" % (len(characters), charmax) or ""))

                for char in characters:
                    csessions = char.sessions.all()
                    if csessions:
                        for sess in csessions:
                            # character is already puppeted
                            sid = sess in sessions and sessions.index(sess) + 1
                            if sess and sid:
                                result.append("\n - |G%s|n [%s] (played by you in session %i)"
                                              % (char.key, ", ".join(char.permissions.all()), sid))
                            else:
                                result.append("\n - |R%s|n [%s] (played by someone else)"
                                              % (char.key, ", ".join(char.permissions.all())))
                    else:
                        # character is "free to puppet"
                        result.append("\n - %s [%s]" % (char.key, ", ".join(char.permissions.all())))
            look_string = ("-" * 68) + "\n" + "".join(result) + "\n" + ("-" * 68)
            return look_string