Пример #1
0
    def nickreplace(self, raw_string, categories=("inputline", "channel"), include_player=True):
        """
        Apply nick replacement of entries in raw_string with nick replacement.

        Args:
            raw_string (str): The string in which to perform nick
                replacement.
            categories (tuple, optional): Replacement categories in
                which to perform the replacement, such as "inputline",
                "channel" etc.
            include_player (bool, optional): Also include replacement
                with nicks stored on the Player level.
            kwargs (any, optional): Not used.

        Returns:
            string (str): A string with matching keys replaced with
                their nick equivalents.

        """
        raw_string
        obj_nicks, player_nicks = [], []
        for category in make_iter(categories):
            obj_nicks.extend([n for n in make_iter(self.get(category=category, return_obj=True)) if n])
        if include_player and self.obj.has_player:
            for category in make_iter(categories):
                player_nicks.extend([n for n in make_iter(self.obj.player.nicks.get(category=category, return_obj=True)) if n])
        for nick in obj_nicks + player_nicks:
            # make a case-insensitive match here
            match = re.match(re.escape(nick.db_key), raw_string, re.IGNORECASE)
            if match:
                raw_string = raw_string.replace(match.group(), nick.db_strvalue, 1)
                break
        return raw_string
Пример #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 []
Пример #3
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 != 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 []
Пример #4
0
    def func(self):
        "Implement function"

        caller = self.caller

        # all channels we have available to listen to
        channels = [chan for chan in ChannelDB.objects.get_all_channels() if chan.access(caller, "listen")]
        if not channels:
            self.msg("No channels available.")
            return
        # all channel we are already subscribed to
        subs = ChannelDB.objects.get_subscriptions(caller)

        if self.cmdstring == "comlist":
            # just display the subscribed channels with no extra info
            comtable = evtable.EvTable(
                "{wchannel{n", "{wmy aliases{n", "{wdescription{n", align="l", maxwidth=_DEFAULT_WIDTH
            )
            # comtable = prettytable.PrettyTable(["{wchannel", "{wmy aliases", "{wdescription"])
            for chan in subs:
                clower = chan.key.lower()
                nicks = caller.nicks.get(category="channel")
                comtable.add_row(
                    *[
                        "%s%s" % (chan.key, chan.aliases.all() and "(%s)" % ",".join(chan.aliases.all()) or ""),
                        "%s".join(nick for nick in make_iter(nicks) if nick and nick.lower() == clower),
                        chan.db.desc,
                    ]
                )
            caller.msg(
                "\n{wChannel subscriptions{n (use {w@channels{n to list all, {waddcom{n/{wdelcom{n to sub/unsub):{n\n%s"
                % comtable
            )
        else:
            # full listing (of channels caller is able to listen to)
            comtable = evtable.EvTable(
                "{wsub{n", "{wchannel{n", "{wmy aliases{n", "{wlocks{n", "{wdescription{n", maxwidth=_DEFAULT_WIDTH
            )
            # comtable = prettytable.PrettyTable(["{wsub", "{wchannel", "{wmy aliases", "{wlocks", "{wdescription"])
            for chan in channels:
                clower = chan.key.lower()
                nicks = caller.nicks.get(category="channel")
                nicks = nicks or []
                comtable.add_row(
                    *[
                        chan in subs and "{gYes{n" or "{rNo{n",
                        "%s%s" % (chan.key, chan.aliases.all() and "(%s)" % ",".join(chan.aliases.all()) or ""),
                        "%s".join(nick for nick in make_iter(nicks) if nick.lower() == clower),
                        str(chan.locks),
                        chan.db.desc,
                    ]
                )
            caller.msg(
                "\n{wAvailable channels{n (use {wcomlist{n,{waddcom{n and {wdelcom{n to manage subscriptions):\n%s"
                % comtable
            )
Пример #5
0
 def __init__(
     self, senders=None, receivers=None, channels=None, message="", header="", type="", lockstring="", hide_from=None
 ):
     self.senders = senders and make_iter(senders) or []
     self.receivers = receivers and make_iter(receivers) or []
     self.channels = channels and make_iter(channels) or []
     self.type = type
     self.header = header
     self.message = message
     self.lock_storage = lockstring
     self.hide_from = hide_from and make_iter(hide_from) or []
     self.date_sent = timezone.now()
Пример #6
0
    def batch_add(self, key, value, category=None, lockstring="",
            strattr=False, accessing_obj=None, default_access=True):
        """
        Batch-version of `add()`. This is more efficient than
        repeat-calling add.

        `key` and `value` must be sequences of the same length, each
        representing a key-value pair.

        """
        if accessing_obj and not self.obj.access(accessing_obj,
                                      self._attrcreate, default=default_access):
            # check create access
            return
        if self._cache is None:
            self._recache()
        if not key:
            return

        keys, values= make_iter(key), make_iter(value)

        if len(keys) != len(values):
            raise RuntimeError("AttributeHandler.add(): key and value of different length: %s vs %s" % key, value)
        category = category.strip().lower() if category is not None else None
        new_attrobjs = []
        for ikey, keystr in enumerate(keys):
            keystr = keystr.strip().lower()
            new_value = values[ikey]
            cachekey = "%s-%s" % (keystr, category)
            attr_obj = self._cache.get(cachekey)

            if attr_obj:
                # update an existing attribute object
                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_attrtype" : self._attrtype,
                          "db_value" : None if strattr else to_pickle(new_value),
                          "db_strvalue" : value if strattr else None}
                new_attr = Attribute(**kwargs)
                new_attr.save()
                new_attrobjs.append(new_attr)
        if new_attrobjs:
            # Add new objects to m2m field all at once
            getattr(self.obj, self._m2m_fieldname).add(*new_attrobjs)
            self._recache()
Пример #7
0
    def func(self):
        """Implement function"""

        caller = self.caller

        # all channels we have available to listen to
        channels = [chan for chan in ChannelDB.objects.get_all_channels()
                    if chan.access(caller, 'listen')]
        if not channels:
            self.msg("No channels available.")
            return
        # all channel we are already subscribed to
        subs = ChannelDB.objects.get_subscriptions(caller)

        if self.cmdstring == "comlist":
            # just display the subscribed channels with no extra info
            comtable = evtable.EvTable("|wchannel|n", "|wmy aliases|n",
                                       "|wdescription|n", align="l", maxwidth=_DEFAULT_WIDTH)
            for chan in subs:
                clower = chan.key.lower()
                nicks = caller.nicks.get(category="channel", return_obj=True)
                comtable.add_row(*["%s%s" % (chan.key, chan.aliases.all() and
                                             "(%s)" % ",".join(chan.aliases.all()) or ""),
                                   "%s" % ",".join(nick.db_key for nick in make_iter(nicks)
                                                   if nick and nick.value[3].lower() == clower),
                                   chan.db.desc])
            self.msg("\n|wChannel subscriptions|n (use |w@channels|n to list all,"
                     " |waddcom|n/|wdelcom|n to sub/unsub):|n\n%s" % comtable)
        else:
            # full listing (of channels caller is able to listen to)
            comtable = evtable.EvTable("|wsub|n", "|wchannel|n", "|wmy aliases|n",
                                       "|wlocks|n", "|wdescription|n", maxwidth=_DEFAULT_WIDTH)
            for chan in channels:
                clower = chan.key.lower()
                nicks = caller.nicks.get(category="channel", return_obj=True)
                nicks = nicks or []
                if chan not in subs:
                    substatus = "|rNo|n"
                elif caller in chan.mutelist:
                    substatus = "|rMuted|n"
                else:
                    substatus = "|gYes|n"
                comtable.add_row(*[substatus,
                                   "%s%s" % (chan.key, chan.aliases.all() and
                                             "(%s)" % ",".join(chan.aliases.all()) or ""),
                                   "%s" % ",".join(nick.db_key for nick in make_iter(nicks)
                                                   if nick.value[3].lower() == clower),
                                   str(chan.locks),
                                   chan.db.desc])
            comtable.reformat_column(0, width=9)
            comtable.reformat_column(3, width=14)
            self.msg("\n|wAvailable channels|n (use |wcomlist|n,|waddcom|n and |wdelcom|n"
                     " to manage subscriptions):\n%s" % comtable)
Пример #8
0
    def msg(self, text=None, from_obj=None, session=None, options=None, **kwargs):
        """
        Evennia -> User
        This is the main route for sending data back to the user from the
        server.

        Args:
            text (str, optional): text data to send
            from_obj (Object or Account or list, optional): Object sending. If given, its
                at_msg_send() hook will be called. If iterable, call on all entities.
            session (Session or list, optional): Session object or a list of
                Sessions to receive this send. If given, overrules the
                default send behavior for the current
                MULTISESSION_MODE.
            options (list): Protocol-specific options. Passed on to the protocol.
        Kwargs:
            any (dict): All other keywords are passed on to the protocol.

        """
        if from_obj:
            # call hook
            for obj in make_iter(from_obj):
                try:
                    obj.at_msg_send(text=text, to_obj=self, **kwargs)
                except Exception:
                    # this may not be assigned.
                    logger.log_trace()
        try:
            if not self.at_msg_receive(text=text, **kwargs):
                # abort message to this account
                return
        except Exception:
            # this may not be assigned.
            pass

        kwargs["options"] = options

        if text is not None:
            if not (isinstance(text, basestring) or isinstance(text, tuple)):
                # sanitize text before sending across the wire
                try:
                    text = to_str(text, force_string=True)
                except Exception:
                    text = repr(text)
            kwargs['text'] = text

        # session relay
        sessions = make_iter(session) if session else self.sessions.all()
        for session in sessions:
            session.data_out(**kwargs)
Пример #9
0
    def has(self, key, category=None):
        """
        Checks if the given Attribute (or list of Attributes) exists on
        the object.

        If an iterable is given, returns list of booleans.
        """
        if self._cache is None or not _TYPECLASS_AGGRESSIVE_CACHE:
            self._recache()
        key = [k.strip().lower() for k in make_iter(key) if k]
        category = category.strip().lower() if category is not None else None
        searchkeys = ["%s-%s" % (k, category) for k in make_iter(key)]
        ret = [self._cache.get(skey) for skey in searchkeys if skey in self._cache]
        return ret[0] if len(ret) == 1 else ret
Пример #10
0
 def nickreplace(self, raw_string, categories=("inputline", "channel"), include_player=True):
     "Replace entries in raw_string with nick replacement"
     raw_string
     obj_nicks, player_nicks = [], []
     for category in make_iter(categories):
         obj_nicks.extend([n for n in make_iter(self.get(category=category, return_obj=True)) if n])
     if include_player and self.obj.has_player:
         for category in make_iter(categories):
             player_nicks.extend([n for n in make_iter(self.obj.player.nicks.get(category=category, return_obj=True)) if n])
     for nick in obj_nicks + player_nicks:
         # make a case-insensitive match here
         match = re.match(re.escape(nick.db_key), raw_string, re.IGNORECASE)
         if match:
             raw_string = raw_string.replace(match.group(), nick.db_strvalue, 1)
             break
     return raw_string
Пример #11
0
    def get(self, exclude=None):
        """
        Return the contents of the cache.

        Args:
            exclude (Object or list of Object): object(s) to ignore

        Returns:
            objects (list): the Objects inside this location

        """
        if exclude:
            pks = [pk for pk in self._pkcache if pk not in [excl.pk for excl in make_iter(exclude)]]
        else:
            pks = self._pkcache
        try:
            return [self._idcache[pk] for pk in pks]
        except KeyError:
            # this can happen if the idmapper cache was cleared for an object
            # in the contents cache. If so we need to re-initialize and try again.
            self.init()
            try:
                return [self._idcache[pk] for pk in pks]
            except KeyError:
                # this means an actual failure of caching. Return real database match.
                logger.log_err("contents cache failed for %s." % (self.obj.key))
                return list(ObjectDB.objects.filter(db_location=self.obj))
Пример #12
0
    def get(self, key=None, default=None, category=None, return_tagobj=False):
        """
        Get the tag for the given key or list of tags.

        Args:
            key (str or list): The tag or tags to retrieve.
            default (any, optional): The value to return in case of no match.
            category (str, optional): The Tag category to limit the
                request to. Note that `None` is the valid, default
                category.
            return_tagobj (bool, optional): Return the Tag object itself
                instead of a string representation of the Tag.

        Returns:
            tags (str, TagObject or list): The matches, either string
                representations of the tags or the Tag objects themselves
                depending on `return_tagobj`.

        """
        ret = []
        for keystr in make_iter(key):
            # note - the _getcache call removes case sensitivity for us
            ret.extend([tag if return_tagobj else to_str(tag.db_key)
                            for tag in self._getcache(keystr, category)])
        return ret[0] if len(ret) == 1 else (ret if ret else default)
Пример #13
0
    def remove(self, key, raise_exception=False, category=None, accessing_obj=None, default_access=True):
        """
        Remove attribute or a list of attributes from object.

        Args:
            key (str): An Attribute key to remove.
            raise_exception (bool, optional): If set, not finding the
                Attribute to delete will raise an exception instead of
                just quietly failing.
            category (str, optional): The category within which to
                remove the Attribute.
            accessing_obj (object, optional): An object to check
                against the `attredit` lock. If not given, the check will
                be skipped.
            default_access (bool, optional): The fallback access to
                grant if `accessing_obj` is given but there is no
                `attredit` lock set on the Attribute in question.

        Raises:
            AttributeError: If `raise_exception` is set and no matching Attribute
                was found matching `key`.

        """
        if self._cache is None or not _TYPECLASS_AGGRESSIVE_CACHE:
            self._recache()
        key = [k.strip().lower() for k in make_iter(key) if k]
        category = category.strip().lower() if category is not None else None
        for searchstr in ("%s-%s" % (k, category) for k in key):
            attr_obj = self._cache.get(searchstr)
            if attr_obj:
                if not (accessing_obj and not attr_obj.access(accessing_obj, self._attredit, default=default_access)):
                    attr_obj.delete()
            elif not attr_obj and raise_exception:
                raise AttributeError
        self._recache()
Пример #14
0
 def remove_receiver(self, obj):
     "Remove a sender or a list of senders"
     for o in make_iter(obj):
         try:
             self.senders.remove(o)
         except ValueError:
             pass  # nothing to remove
Пример #15
0
def _get_prototype(inprot, protparents, uninherited=None, _workprot=None):
    """
    Recursively traverse a prototype dictionary, including multiple
    inheritance. Use validate_prototype before this, we don't check
    for infinite recursion here.

    Args:
        inprot (dict): Prototype dict (the individual prototype, with no inheritance included).
        protparents (dict): Available protparents, keyed by prototype_key.
        uninherited (dict): Parts of prototype to not inherit.
        _workprot (dict, optional): Work dict for the recursive algorithm.

    """
    _workprot = {} if _workprot is None else _workprot
    if "prototype_parent" in inprot:
        # move backwards through the inheritance
        for prototype in make_iter(inprot["prototype_parent"]):
            # Build the prot dictionary in reverse order, overloading
            new_prot = _get_prototype(protparents.get(prototype.lower(), {}),
                                      protparents, _workprot=_workprot)
            _workprot.update(new_prot)
    # the inprot represents a higher level (a child prot), which should override parents
    _workprot.update(inprot)
    if uninherited:
        # put back the parts that should not be inherited
        _workprot.update(uninherited)
    _workprot.pop("prototype_parent", None)  # we don't need this for spawning
    return _workprot
Пример #16
0
    def add(self, entity):
        """
        Subscribe an entity to this channel.

        Args:
            entity (Player, Object or list): The entity or
                list of entities to subscribe to this channel.

        Note:
            No access-checking is done here, this must have
                been done before calling this method. Also
                no hooks will be called.

        """
        global _CHANNELHANDLER
        if not _CHANNELHANDLER:
            from evennia.comms.channelhandler import CHANNEL_HANDLER as _CHANNELHANDLER
        for subscriber in make_iter(entity):
            if subscriber:
                clsname = subscriber.__dbclass__.__name__
                # chooses the right type
                if clsname == "ObjectDB":
                    self.obj.db_object_subscriptions.add(subscriber)
                elif clsname == "PlayerDB":
                    self.obj.db_subscriptions.add(subscriber)
                _CHANNELHANDLER.cached_cmdsets.pop(subscriber, None)
        self._recache()
Пример #17
0
    def create_tag(self, key=None, category=None, data=None, tagtype=None):
        """
        Create a new Tag of the base type associated with this typedobject.
        This makes sure to create case-insensitive tags. If the exact same
        tag configuration (key+category+tagtype) exists on the model, a
        new tag will not be created, but an old one returned.  A data
        keyword is not part of the uniqueness of the tag and setting one
        on an existing tag will overwrite the old data field.
        """
        data = str(data) if data is not None else None
        # try to get old tag

        tag = self.get_tag(key=key, category=category, tagtype=tagtype, global_search=True)
        if tag and data is not None:
            # overload data on tag
            tag.db_data = data
            tag.save()
        elif not tag:
            # create a new tag
            global _Tag
            if not _Tag:
                from evennia.typeclasses.models import Tag as _Tag
            tag = _Tag.objects.create(
                db_key=key.strip().lower() if key is not None else None,
                db_category=category.strip().lower() if category and key is not None else None,
                db_data=data,
                db_tagtype=tagtype.strip().lower() if tagtype is not None else None)
            tag.save()
        return make_iter(tag)[0]
Пример #18
0
    def get(self, key=None, category="inputline", return_tuple=False, **kwargs):
        """
        Get the replacement value matching the given key and category

        Args:
            key (str or list, optional): the attribute identifier or
                multiple attributes to get. if a list of keys, the
                method will return a list.
            category (str, optional): the category within which to
                retrieve the nick. The "inputline" means replacing data
                sent by the user.
            return_tuple (bool, optional): return the full nick tuple rather
                than just the replacement. For non-template nicks this is just
                a string.
            kwargs (any, optional): These are passed on to `AttributeHandler.get`.

        """
        if return_tuple or "return_obj" in kwargs:
            return super(NickHandler, self).get(key=key, category=category, **kwargs)
        else:
            retval = super(NickHandler, self).get(key=key, category=category, **kwargs)
            if retval:
                return retval[3] if isinstance(retval, tuple) else \
                    [tup[3] for tup in make_iter(retval)]
            return None
Пример #19
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)
Пример #20
0
    def unpuppet_object(self, sessid):
        """
        Disengage control over an object

        Args:
            sessid(int): the session id to disengage

        Raises:
            RuntimeError with message about error.
        """
        if _MULTISESSION_MODE == 1:
            sessions = self.get_all_sessions()
        else:
            sessions = self.get_session(sessid)
        if not sessions:
            raise RuntimeError("No session was found.")
        for session in make_iter(sessions):
            obj = session.puppet or None
            if not obj:
                raise RuntimeError("No puppet was found to disconnect from.")
            elif obj:
                # do the disconnect, but only if we are the last session to puppet
                obj.at_pre_unpuppet()
                obj.sessid.remove(session.sessid)
                if not obj.sessid.count():
                    del obj.player
                    obj.at_post_unpuppet(self, sessid=sessid)
            # Just to be sure we're always clear.
            session.puppet = None
            session.puid = None
Пример #21
0
    def add(self, tag=None, category=None, data=None):
        """
        Add a new tag to the handler.

        Args:
            tag (str or list): The name of the tag to add. If a list,
                add several Tags.
            category (str, optional): Category of Tag. `None` is the default category.
            data (str, optional): Info text about the tag(s) added.
                This can not be used to store object-unique info but only
                eventual info about the text itself.

        Notes:
            If the tag + category combination matches an already
            existing Tag object, this will be re-used and no new Tag
            will be created.

        """
        if not tag:
            return
        for tagstr in make_iter(tag):
            if not tagstr:
                continue
            tagstr = tagstr.strip().lower()
            category = category.strip().lower() if category is not None else None
            data = str(data) if data is not None else None
            # this will only create tag if no matches existed beforehand (it
            # will overload data on an existing tag since that is not
            # considered part of making the tag unique)
            tagobj = self.obj.__class__.objects.create_tag(
                key=tagstr, category=category, data=data, tagtype=self._tagtype
            )
            getattr(self.obj, self._m2m_fieldname).add(tagobj)
            self._setcache(tagstr, category, tagobj)
Пример #22
0
    def get(self, key=None, default=None, category=None, return_tagobj=False, return_list=False):
        """
        Get the tag for the given key, category or combination of the two.

        Args:
            key (str or list, optional): The tag or tags to retrieve.
            default (any, optional): The value to return in case of no match.
            category (str, optional): The Tag category to limit the
                request to. Note that `None` is the valid, default
                category. If no `key` is given, all tags of this category will be
                returned.
            return_tagobj (bool, optional): Return the Tag object itself
                instead of a string representation of the Tag.
            return_list (bool, optional): Always return a list, regardless
                of number of matches.

        Returns:
            tags (list): The matches, either string
                representations of the tags or the Tag objects themselves
                depending on `return_tagobj`. If 'default' is set, this
                will be a list with the default value as its only element.

        """
        ret = []
        for keystr in make_iter(key):
            # note - the _getcache call removes case sensitivity for us
            ret.extend([tag if return_tagobj else to_str(tag.db_key)
                        for tag in self._getcache(keystr, category)])
        if return_list:
            return ret if ret else [default] if default is not None else []
        return ret[0] if len(ret) == 1 else (ret if ret else default)
Пример #23
0
    def remove(self, key, category=None):
        """
        Remove a tag from the handler based ond key and category.

        Args:
            key (str or list): The tag or tags to retrieve.
            category (str, optional): The Tag category to limit the
                request to. Note that `None` is the valid, default
                category.

        """
        for key in make_iter(key):
            if not (key or key.strip()):  # we don't allow empty tags
                continue
            tagstr = key.strip().lower()
            category = category.strip().lower() if category else category

            # This does not delete the tag object itself. Maybe it should do
            # that when no objects reference the tag anymore (but how to check)?
            # For now, tags are never deleted, only their connection to objects.
            tagobj = getattr(self.obj, self._m2m_fieldname).filter(db_key=tagstr, db_category=category,
                                                                   db_model=self._model, db_tagtype=self._tagtype)
            if tagobj:
                getattr(self.obj, self._m2m_fieldname).remove(tagobj[0])
            self._delcache(key, category)
Пример #24
0
    def msg(self, text=None, from_obj=None, session=None, **kwargs):
        """
        Evennia -> User
        This is the main route for sending data back to the user from the
        server.

        Args:
            text (str, optional): text data to send
            from_obj (Object or Player, optional): Object sending. If given,
                its at_msg_send() hook will be called.
            session (Session or list, optional): Session object or a list of
                Sessions to receive this send. If given, overrules the
                default send behavior for the current
                MULTISESSION_MODE.
        Notes:
            All other keywords are passed on to the protocol.

        """
        text = to_str(text, force_string=True) if text else ""

        if from_obj:
            # call hook
            try:
                from_obj.at_msg_send(text=text, to_obj=self, **kwargs)
            except Exception:
                pass

        # session relay
        sessions = make_iter(session) if session else self.sessions.all()
        for session in sessions:
            session.msg(text=text, **kwargs)
Пример #25
0
 def __cmdset_storage_set(self, value):
     """
     Setter. Allows for self.name = value. Stores as a comma-separated
     string.
     """
     _SA(self, "db_cmdset_storage", ",".join(str(val).strip() for val in make_iter(value)))
     _GA(self, "save")()
Пример #26
0
    def get_objs_with_attr_value(self, attribute_name, attribute_value, candidates=None, typeclasses=None):
        """
        Get all objects having the given attrname set to the given value.

        Args:
            attribute_name (str): Attribute key to search for.
            attribute_value (str):  Attribute value to search for.
            candidates (list, optional): Candidate objects to limit search to.
            typeclasses (list, optional): Python pats to restrict matches with.

        Returns:
            matches (list): Objects fullfilling both the `attribute_name` and `attribute_value` criterions.

        Notes:
            This uses the Attribute's PickledField to transparently search the database by matching
            the internal representation. This is reasonably effective but since Attribute values
            cannot be indexed, searching by Attribute key is to be preferred whenever possible.

        """
        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()

        ## This doesn't work if attribute_value is an object. Workaround below

        if isinstance(attribute_value, (basestring, int, float, bool)):
            return self.filter(cand_restriction & type_restriction & Q(db_attributes__db_key=attribute_name, db_attributes__db_value=attribute_value))
        else:
            # We have to loop for safety since the referenced lookup gives deepcopy error if attribute value is an object.
            global _ATTR
            if not _ATTR:
                from evennia.typeclasses.models import Attribute as _ATTR
            cands = list(self.filter(cand_restriction & type_restriction & Q(db_attributes__db_key=attribute_name)))
            results = [attr.objectdb_set.all() for attr in _ATTR.objects.filter(objectdb__in=cands, db_value=attribute_value)]
            return chain(*results)
Пример #27
0
 def __channels_set(self, value):
     """
     Setter. Allows for self.channels = value.
     Requires a channel to be added.
     """
     for val in (v for v in make_iter(value) if v):
         self.db_receivers_channels.add(val)
Пример #28
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)
Пример #29
0
    def batch_add(self, *args):
        """
        Batch-add tags from a list of tuples.

        Args:
            tuples (tuple or str): Any number of `tagstr` keys, `(keystr, category)` or
                `(keystr, category, data)` tuples.

        Notes:
            This will generate a mimimal number of self.add calls,
            based on the number of categories involved (including
            `None`) (data is not unique and may be overwritten by the content
            of a latter tuple with the same category).

        """
        keys = defaultdict(list)
        data = {}
        for tup in args:
            tup = make_iter(tup)
            nlen = len(tup)
            if nlen == 1:  # just a key
                keys[None].append(tup[0])
            elif nlen == 2:
                keys[tup[1]].append(tup[0])
            else:
                keys[tup[1]].append(tup[0])
                data[tup[1]] = tup[2]  # overwrite previous
        for category, key in keys.iteritems():
            self.add(tag=key, category=category, data=data.get(category, None))
Пример #30
0
    def func(self):
        "Create the nickname"

        caller = self.caller
        switches = self.switches
        nicksinputline = caller.nicks.get(category="inputline", return_obj=True)
        nicksobjects = caller.nicks.get(category="object", return_obj=True)
        nicksplayers = caller.nicks.get(category="player", return_obj=True)

        if "list" in switches:
            if not nicksinputline and not nicksobjects and not nicksplayers:
                string = "{wNo nicks defined.{n"
            else:
                table = prettytable.PrettyTable(["{wNickType", "{wNickname", "{wTranslates-to"])
                for nicks in (nicksinputline, nicksobjects, nicksplayers):
                    for nick in utils.make_iter(nicks):
                        table.add_row([nick.db_category, nick.db_key, nick.db_strvalue])
                string = "{wDefined Nicks:{n\n%s" % table
            caller.msg(string)
            return
        if "clearall" in switches:
            caller.nicks.clear()
            caller.msg("Cleared all aliases.")
            return
        if not self.args or not self.lhs:
            caller.msg("Usage: nick[/switches] nickname = [realname]")
            return
        nick = self.lhs
        real = self.rhs

        if real == nick:
            caller.msg("No point in setting nick same as the string to replace...")
            return

        # check so we have a suitable nick type
        if not any(True for switch in switches if switch in ("object", "player", "inputline")):
            switches = ["inputline"]
        string = ""
        for switch in switches:
            oldnick = caller.nicks.get(key=nick, category=switch)
            if not real:
                if "=" in self.args:
                    if oldnick:
                        # clear the alias
                        string += "\nNick cleared: '{w%s{n' (-> '{w%s{n')." % (nick, oldnick)
                        caller.nicks.remove(nick, category=switch)
                    else:
                        string += "\nNo nick '%s' found, so it could not be removed." % nick
                else:
                    string += "\nNick: '{w%s{n'{n -> '{w%s{n'." % (nick, oldnick)

            else:
                # creating new nick
                if oldnick:
                    string += "\nNick '{w%s{n' reset from '{w%s{n' to '{w%s{n'." % (nick, oldnick, real)
                else:
                    string += "\nNick set: '{w%s{n' -> '{w%s{n'." % (nick, real)
                caller.nicks.add(nick, real, category=switch)
        caller.msg(string)
Пример #31
0
    def remove(self, key, raise_exception=False, category=None,
               accessing_obj=None, default_access=True):
        """
        Remove attribute or a list of attributes from object.

        Args:
            key (str): An Attribute key to remove.
            raise_exception (bool, optional): If set, not finding the
                Attribute to delete will raise an exception instead of
                just quietly failing.
            category (str, optional): The category within which to
                remove the Attribute.
            accessing_obj (object, optional): An object to check
                against the `attredit` lock. If not given, the check will
                be skipped.
            default_access (bool, optional): The fallback access to
                grant if `accessing_obj` is given but there is no
                `attredit` lock set on the Attribute in question.

        Raises:
            AttributeError: If `raise_exception` is set and no matching Attribute
                was found matching `key`.

        """
        if self._cache is None or not _TYPECLASS_AGGRESSIVE_CACHE:
            self._recache()
        key = [k.strip().lower() for k in make_iter(key) if k]
        category = category.strip().lower() if category is not None else None
        for searchstr in ("%s-%s" % (k, category) for k in key):
            attr_obj = self._cache.get(searchstr)
            if attr_obj:
                if not (accessing_obj and not attr_obj.access(accessing_obj,
                        self._attredit, default=default_access)):
                    attr_obj.delete()
            elif not attr_obj and raise_exception:
                raise AttributeError
        self._recache()
Пример #32
0
    def add(self, tag=None, category=None, data=None):
        """
        Add a new tag to the handler.

        Args:
            tag (str or list): The name of the tag to add. If a list,
                add several Tags.
            category (str, optional): Category of Tag. `None` is the default category.
            data (str, optional): Info text about the tag(s) added.
                This can not be used to store object-unique info but only
                eventual info about the text itself.

        Notes:
            If the tag + category combination matches an already
            existing Tag object, this will be re-used and no new Tag
            will be created.

        """
        if not tag:
            return
        for tagstr in make_iter(tag):
            if not tagstr:
                continue
            tagstr = tagstr.strip().lower()
            category = category.strip().lower(
            ) if category is not None else None
            data = str(data) if data is not None else None
            # this will only create tag if no matches existed beforehand (it
            # will overload data on an existing tag since that is not
            # considered part of making the tag unique)
            tagobj = self.obj.__class__.objects.create_tag(
                key=tagstr,
                category=category,
                data=data,
                tagtype=self._tagtype)
            getattr(self.obj, self._m2m_fieldname).add(tagobj)
            self._setcache(tagstr, category, tagobj)
Пример #33
0
    def get(self, exclude=None):
        """
        Return the contents of the cache.

        Args:
            exclude (Object or list of Object): object(s) to ignore

        Returns:
            objects (list): the Objects inside this location

        """
        pks = self._pkcache.keys()
        if exclude:
            pks = [
                pk for pk in pks
                if pk not in [excl.pk for excl in make_iter(exclude)]
            ]
        try:
            return [self._idcache[pk] for pk in pks]
        except KeyError:
            # this can happen if the idmapper cache was cleared for an object
            # in the contents cache. If so we need to re-initialize and try again.
            self.init()
            return self.get(exclude=exclude)
Пример #34
0
    def func(self):
        "delete the character"
        player = self.player

        if not self.args:
            self.msg("Usage: @chardelete <charactername>")
            return

        # use the playable_characters list to search
        match = [char for char in utils.make_iter(player.db._playable_characters) if char.key.lower() == self.args.lower()]
        if not match:
            self.msg("You have no such character to delete.")
            return
        elif len(match) > 1:
            self.msg("Aborting - there are two characters with the same name. Ask an admin to delete the right one.")
            return
        else: # one match
            from evennia.utils.evmenu import get_input

            def _callback(caller, prompt, result):
                if result.lower() == "yes":
                    # only take action
                    delobj = caller.ndb._char_to_delete
                    key = delobj.key
                    caller.db._playable_characters = [char for char
                                                        in caller.db._playable_characters if char != delobj]
                    delobj.delete()
                    self.msg("Character '%s' was permanently deleted." % key)
                else:
                    self.msg("Deletion was aborted.")
                del caller.ndb._char_to_delete

            match = match[0]
            player.ndb._char_to_delete = match
            prompt = "|rThis will permanently destroy '%s'. This cannot be undone.|n Continue yes/[no]?"
            get_input(player, prompt % match.key, _callback)
Пример #35
0
 def add(self, tag=None, category=None, data=None):
     "Add a new tag to the handler. Tag is a string or a list of strings."
     if not tag:
         return
     for tagstr in make_iter(tag):
         if not tagstr:
             continue
         tagstr = tagstr.strip().lower()
         category = category.strip().lower(
         ) if category is not None else None
         data = str(data) if data is not None else None
         # this will only create tag if no matches existed beforehand (it
         # will overload data on an existing tag since that is not
         # considered part of making the tag unique)
         tagobj = self.obj.__class__.objects.create_tag(
             key=tagstr,
             category=category,
             data=data,
             tagtype=self._tagtype)
         getattr(self.obj, self._m2m_fieldname).add(tagobj)
         if self._cache is None:
             self._recache()
         cachestring = "%s-%s" % (tagstr, category)
         self._cache[cachestring] = tagobj
Пример #36
0
 def msg_connections(self, text=None, exclude=None, from_obj=None, **kwargs):
         """
         Emit message to all objects inside rooms connecting to this room
         via exits, and 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.
         room = []
         rooms.append(self.objects.filter(db_destination=self))
         rooms.append([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)
Пример #37
0
 def tt_msg(
     self,
     message,
     from_obj,
     exclude=None,
     is_ooc=False,
     msg_type=TT_SAY,
     options=None,
 ):
     """
     Send msg to characters at table. Note that if this method was simply named
     'msg' rather than tt_msg, it would be called by msg_contents in rooms, causing
     characters at the places to receive redundant messages, since they are still
     objects in the room as well.
     """
     # utils.make_iter checks to see if an object is a list, set, etc, and encloses it in a list if not
     # needed so that 'ob not in exclude' can function if we're just passed a character
     exclude = make_iter(exclude)
     for ob in self.item_data.occupants:
         if ob not in exclude:
             place_msg = self.build_tt_msg(from_obj, ob, message, is_ooc,
                                           msg_type)
             ob.msg(place_msg, from_obj=from_obj, options=options)
     from_obj.posecount += 1
Пример #38
0
    def unpuppet_object(self, session):
        """
        Disengage control over an object.

        Args:
            session (Session or list): The session or a list of
                sessions to disengage from their puppets.

        Raises:
            RuntimeError With message about error.

        """
        for session in make_iter(session):
            obj = session.puppet
            if obj:
                # do the disconnect, but only if we are the last session to puppet
                obj.at_pre_unpuppet()
                obj.sessions.remove(session)
                if not obj.sessions.count():
                    del obj.account
                obj.at_post_unpuppet(self, session=session)
            # Just to be sure we're always clear.
            session.puppet = None
            session.puid = None
Пример #39
0
    def remove(self, key=None, category=None):
        """
        Remove a tag from the handler based ond key and/or category.

        Args:
            key (str or list, optional): The tag or tags to retrieve.
            category (str, optional): The Tag category to limit the
                request to. Note that `None` is the valid, default
                category
        Notes:
            If neither key nor category is specified, this acts
            as .clear().

        """
        if not key:
            # only category
            self.clear(category=category)
            return

        for key in make_iter(key):
            if not (key or key.strip()):  # we don't allow empty tags
                continue
            tagstr = key.strip().lower()
            category = category.strip().lower() if category else category

            # This does not delete the tag object itself. Maybe it should do
            # that when no objects reference the tag anymore (but how to check)?
            # For now, tags are never deleted, only their connection to objects.
            tagobj = getattr(self.obj, self._m2m_fieldname).filter(
                db_key=tagstr,
                db_category=category,
                db_model=self._model,
                db_tagtype=self._tagtype)
            if tagobj:
                getattr(self.obj, self._m2m_fieldname).remove(tagobj[0])
            self._delcache(key, category)
Пример #40
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
Пример #41
0
def validate_prototype(prototype, protkey=None, protparents=None,
                       is_prototype_base=True, strict=True, _flags=None):
    """
    Run validation on a prototype, checking for inifinite regress.

    Args:
        prototype (dict): Prototype to validate.
        protkey (str, optional): The name of the prototype definition. If not given, the prototype
            dict needs to have the `prototype_key` field set.
        protpartents (dict, optional): The available prototype parent library. If
            note given this will be determined from settings/database.
        is_prototype_base (bool, optional): We are trying to create a new object *based on this
            object*. This means we can't allow 'mixin'-style prototypes without typeclass/parent
            etc.
        strict (bool, optional): If unset, don't require needed keys, only check against infinite
            recursion etc.
        _flags (dict, optional): Internal work dict that should not be set externally.
    Raises:
        RuntimeError: If prototype has invalid structure.
        RuntimeWarning: If prototype has issues that would make it unsuitable to build an object
            with (it may still be useful as a mix-in prototype).

    """
    assert isinstance(prototype, dict)

    if _flags is None:
        _flags = {"visited": [], "depth": 0, "typeclass": False, "errors": [], "warnings": []}

    if not protparents:
        protparents = {prototype.get('prototype_key', "").lower(): prototype
                       for prototype in search_prototype()}

    protkey = protkey and protkey.lower() or prototype.get('prototype_key', None)

    if strict and not bool(protkey):
        _flags['errors'].append("Prototype lacks a `prototype_key`.")
        protkey = "[UNSET]"

    typeclass = prototype.get('typeclass')
    prototype_parent = prototype.get('prototype_parent', [])

    if strict and not (typeclass or prototype_parent):
        if is_prototype_base:
            _flags['errors'].append("Prototype {} requires `typeclass` "
                                    "or 'prototype_parent'.".format(protkey))
        else:
            _flags['warnings'].append("Prototype {} can only be used as a mixin since it lacks "
                                      "a typeclass or a prototype_parent.".format(protkey))

    if strict and typeclass:
        try:
            class_from_module(typeclass)
        except ImportError as err:
            _flags['errors'].append(
                "{}: Prototype {} is based on typeclass {}, which could not be imported!".format(
                    err, protkey, typeclass))

    # recursively traverese prototype_parent chain

    for protstring in make_iter(prototype_parent):
        protstring = protstring.lower()
        if protkey is not None and protstring == protkey:
            _flags['errors'].append("Prototype {} tries to parent itself.".format(protkey))
        protparent = protparents.get(protstring)
        if not protparent:
            _flags['errors'].append("Prototype {}'s prototype_parent '{}' was not found.".format(
                (protkey, protstring)))
        if id(prototype) in _flags['visited']:
            _flags['errors'].append(
                "{} has infinite nesting of prototypes.".format(protkey or prototype))

        if _flags['errors']:
            raise RuntimeError("Error: " + "\nError: ".join(_flags['errors']))
        _flags['visited'].append(id(prototype))
        _flags['depth'] += 1
        validate_prototype(protparent, protstring, protparents,
                           is_prototype_base=is_prototype_base, _flags=_flags)
        _flags['visited'].pop()
        _flags['depth'] -= 1

    if typeclass and not _flags['typeclass']:
        _flags['typeclass'] = typeclass

    # if we get back to the current level without a typeclass it's an error.
    if strict and is_prototype_base and _flags['depth'] <= 0 and not _flags['typeclass']:
        _flags['errors'].append("Prototype {} has no `typeclass` defined anywhere in its parent\n "
                                "chain. Add `typeclass`, or a `prototype_parent` pointing to a "
                                "prototype with a typeclass.".format(protkey))

    if _flags['depth'] <= 0:
        if _flags['errors']:
            raise RuntimeError("Error: " + "\nError: ".join(_flags['errors']))
        if _flags['warnings']:
            raise RuntimeWarning("Warning: " + "\nWarning: ".join(_flags['warnings']))

    # make sure prototype_locks are set to defaults
    prototype_locks = [lstring.split(":", 1)
                       for lstring in prototype.get("prototype_locks", "").split(';')
                       if ":" in lstring]
    locktypes = [tup[0].strip() for tup in prototype_locks]
    if "spawn" not in locktypes:
        prototype_locks.append(("spawn", "all()"))
    if "edit" not in locktypes:
        prototype_locks.append(("edit", "all()"))
    prototype_locks = ";".join(":".join(tup) for tup in prototype_locks)
    prototype['prototype_locks'] = prototype_locks
Пример #42
0
def save_prototype(**kwargs):
    """
    Create/Store a prototype persistently.

    Kwargs:
        prototype_key (str): This is required for any storage.
        All other kwargs are considered part of the new prototype dict.

    Returns:
        prototype (dict or None): The prototype stored using the given kwargs, None if deleting.

    Raises:
        prototypes.ValidationError: If prototype does not validate.

    Note:
        No edit/spawn locks will be checked here - if this function is called the caller
        is expected to have valid permissions.

    """

    kwargs = homogenize_prototype(kwargs)

    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

    prototype_key = kwargs.get("prototype_key")
    if not prototype_key:
        raise ValidationError("Prototype requires a prototype_key")

    prototype_key = str(prototype_key).lower()

    # we can't edit a prototype defined in a module
    if prototype_key in _MODULE_PROTOTYPES:
        mod = _MODULE_PROTOTYPE_MODULES.get(prototype_key, "N/A")
        raise PermissionError("{} is a read-only prototype "
                              "(defined as code in {}).".format(prototype_key, mod))

    # make sure meta properties are included with defaults
    stored_prototype = DbPrototype.objects.filter(db_key=prototype_key)
    prototype = stored_prototype[0].prototype if stored_prototype else {}

    kwargs['prototype_desc'] = kwargs.get("prototype_desc", prototype.get("prototype_desc", ""))
    prototype_locks = kwargs.get(
        "prototype_locks", prototype.get('prototype_locks', "spawn:all();edit:perm(Admin)"))
    is_valid, err = validate_lockstring(prototype_locks)
    if not is_valid:
        raise ValidationError("Lock error: {}".format(err))
    kwargs['prototype_locks'] = prototype_locks

    prototype_tags = [
        _to_batchtuple(tag, _PROTOTYPE_TAG_META_CATEGORY)
        for tag in make_iter(kwargs.get("prototype_tags",
                             prototype.get('prototype_tags', [])))]
    kwargs["prototype_tags"] = prototype_tags

    prototype.update(kwargs)

    if stored_prototype:
        # edit existing prototype
        stored_prototype = stored_prototype[0]
        stored_prototype.desc = prototype['prototype_desc']
        if prototype_tags:
            stored_prototype.tags.clear(category=_PROTOTYPE_TAG_CATEGORY)
            stored_prototype.tags.batch_add(*prototype['prototype_tags'])
        stored_prototype.locks.add(prototype['prototype_locks'])
        stored_prototype.attributes.add('prototype', prototype)
    else:
        # create a new prototype
        stored_prototype = create_script(
            DbPrototype, key=prototype_key, desc=prototype['prototype_desc'], persistent=True,
            locks=prototype_locks, tags=prototype['prototype_tags'],
            attributes=[("prototype", prototype)])
    return stored_prototype.prototype
Пример #43
0
def search_prototype(key=None, tags=None):
    """
    Find prototypes based on key and/or tags, or all prototypes.

    Kwargs:
        key (str): An exact or partial key to query for.
        tags (str or list): Tag key or keys to query for. These
            will always be applied with the 'db_protototype'
            tag category.

    Return:
        matches (list): All found prototype dicts. If no keys
            or tags are given, all available prototypes will be returned.

    Note:
        The available prototypes is a combination of those supplied in
        PROTOTYPE_MODULES and those stored in the database. Note that if
        tags are given and the prototype has no tags defined, it will not
        be found as a match.

    """
    # search module prototypes

    mod_matches = {}
    if tags:
        # use tags to limit selection
        tagset = set(tags)
        mod_matches = {prototype_key: prototype
                       for prototype_key, prototype in _MODULE_PROTOTYPES.items()
                       if tagset.intersection(prototype.get("prototype_tags", []))}
    else:
        mod_matches = _MODULE_PROTOTYPES
    if key:
        if key in mod_matches:
            # exact match
            module_prototypes = [mod_matches[key]]
        else:
            # fuzzy matching
            module_prototypes = [prototype for prototype_key, prototype in mod_matches.items()
                                 if key in prototype_key]
    else:
        module_prototypes = [match for match in mod_matches.values()]

    # search db-stored prototypes

    if tags:
        # exact match on tag(s)
        tags = make_iter(tags)
        tag_categories = ["db_prototype" for _ in tags]
        db_matches = DbPrototype.objects.get_by_tag(tags, tag_categories)
    else:
        db_matches = DbPrototype.objects.all()
    if key:
        # exact or partial match on key
        db_matches = db_matches.filter(db_key=key) or db_matches.filter(db_key__icontains=key)
        # return prototype
    db_prototypes = [dbprot.prototype for dbprot in db_matches]

    matches = db_prototypes + module_prototypes
    nmatches = len(matches)
    if nmatches > 1 and key:
        key = key.lower()
        # avoid duplicates if an exact match exist between the two types
        filter_matches = [mta for mta in matches
                          if mta.get('prototype_key') and mta['prototype_key'] == key]
        if filter_matches and len(filter_matches) < nmatches:
            matches = filter_matches

    return matches
Пример #44
0
    def search_object(self,
                      searchdata,
                      attribute_name=None,
                      typeclass=None,
                      candidates=None,
                      exact=True,
                      use_dbref=True):
        """
        Search as an object globally or in a list of candidates and
        return results. The result is always an Object. Always returns
        a list.

        Args:
            searchdata (str or Object): The entity to match for. This is
                usually a key string but may also be an object itself.
                By default (if no `attribute_name` is set), this will
                search `object.key` and `object.aliases` in order.
                Can also be on the form #dbref, which will (if
                `exact=True`) be matched against primary key.
            attribute_name (str): Use this named Attribute to
                match searchdata against, instead of the defaults. If
                this is the name of a database field (with or without
                the `db_` prefix), that will be matched too.
            typeclass (str or TypeClass): restrict matches to objects
                having this typeclass. This will help speed up global
                searches.
            candidates (list): If supplied, search will
                only be performed among the candidates in this list. A
                common list of candidates is the contents of the
                current location searched.
            exact (bool): Match names/aliases exactly or partially.
                Partial matching matches the beginning of words in the
                names/aliases, using a matching routine to separate
                multiple matches in names with multiple components (so
                "bi sw" will match "Big sword"). Since this is more
                expensive than exact matching, it is recommended to be
                used together with the `candidates` keyword to limit the
                number of possibilities. This value has no meaning if
                searching for attributes/properties.
            use_dbref (bool): If False, bypass direct lookup of a string
                on the form #dbref and treat it like any string.

        Returns:
            matches (list): Matching objects

        """
        def _searcher(searchdata, candidates, typeclass, exact=False):
            """
            Helper method for searching objects. `typeclass` is only used
            for global searching (no candidates)
            """
            if attribute_name:
                # attribute/property search (always exact).
                matches = self.get_objs_with_db_property_value(
                    attribute_name,
                    searchdata,
                    candidates=candidates,
                    typeclasses=typeclass)
                if matches:
                    return matches
                return self.get_objs_with_attr_value(attribute_name,
                                                     searchdata,
                                                     candidates=candidates,
                                                     typeclasses=typeclass)
            else:
                # normal key/alias search
                return self.get_objs_with_key_or_alias(searchdata,
                                                       exact=exact,
                                                       candidates=candidates,
                                                       typeclasses=typeclass)

        if not searchdata and searchdata != 0:
            return []

        if typeclass:
            # typeclass may also be a list
            typeclasses = make_iter(typeclass)
            for i, typeclass in enumerate(make_iter(typeclasses)):
                if callable(typeclass):
                    typeclasses[i] = u"%s.%s" % (typeclass.__module__,
                                                 typeclass.__name__)
                else:
                    typeclasses[i] = u"%s" % typeclass
            typeclass = typeclasses

        if candidates is not None:
            if not candidates:
                # candidates is the empty list. This should mean no matches can ever be acquired.
                return []
            # Convenience check to make sure candidates are really dbobjs
            candidates = [cand for cand in make_iter(candidates) if cand]
            if typeclass:
                candidates = [
                    cand for cand in candidates
                    if _GA(cand, "db_typeclass_path") in typeclass
                ]

        dbref = not attribute_name and exact and use_dbref and self.dbref(
            searchdata)
        if dbref:
            # Easiest case - dbref matching (always exact)
            dbref_match = self.dbref_search(dbref)
            if dbref_match:
                if not candidates or dbref_match in candidates:
                    return [dbref_match]
                else:
                    return []

        # Search through all possibilities.
        match_number = None
        # always run first check exact - we don't want partial matches
        # if on the form of 1-keyword etc.
        matches = _searcher(searchdata, candidates, typeclass, exact=True)
        if not matches:
            # no matches found - check if we are dealing with N-keyword
            # query - if so, strip it.
            match = _MULTIMATCH_REGEX.match(searchdata)
            match_number = None
            if match:
                # strips the number
                match_number, searchdata = match.group("number"), match.group(
                    "name")
                match_number = int(match_number) - 1
                match_number = match_number if match_number >= 0 else None
            if match_number is not None or not exact:
                # run search again, with the exactness set by call
                matches = _searcher(searchdata,
                                    candidates,
                                    typeclass,
                                    exact=exact)

        # deal with result
        if len(matches) > 1 and match_number is not None:
            # multiple matches, but a number was given to separate them
            try:
                matches = [matches[match_number]]
            except IndexError:
                # match number not matching anything
                pass
        # return a list (possibly empty)
        return matches
Пример #45
0
def search_prototype(key=None,
                     tags=None,
                     require_single=False,
                     return_iterators=False):
    """
    Find prototypes based on key and/or tags, or all prototypes.

    Kwargs:
        key (str): An exact or partial key to query for.
        tags (str or list): Tag key or keys to query for. These
            will always be applied with the 'db_protototype'
            tag category.
        require_single (bool): If set, raise KeyError if the result
            was not found or if there are multiple matches.
        return_iterators (bool): Optimized return for large numbers of db-prototypes.
            If set, separate returns of module based prototypes and paginate
            the db-prototype return.

    Return:
        matches (list): Default return, all found prototype dicts. Empty list if
            no match was found. Note that if neither `key` nor `tags`
            were given, *all* available prototypes will be returned.
        list, queryset: If `return_iterators` are found, this is a list of
            module-based prototypes followed by a *paginated* queryset of
            db-prototypes.

    Raises:
        KeyError: If `require_single` is True and there are 0 or >1 matches.

    Note:
        The available prototypes is a combination of those supplied in
        PROTOTYPE_MODULES and those stored in the database. Note that if
        tags are given and the prototype has no tags defined, it will not
        be found as a match.

    """
    # search module prototypes

    mod_matches = {}
    if tags:
        # use tags to limit selection
        tagset = set(tags)
        mod_matches = {
            prototype_key: prototype
            for prototype_key, prototype in _MODULE_PROTOTYPES.items()
            if tagset.intersection(prototype.get("prototype_tags", []))
        }
    else:
        mod_matches = _MODULE_PROTOTYPES

    if key:
        if key in mod_matches:
            # exact match
            module_prototypes = [mod_matches[key]]
        else:
            # fuzzy matching
            module_prototypes = [
                prototype for prototype_key, prototype in mod_matches.items()
                if key in prototype_key
            ]
    else:
        module_prototypes = [match for match in mod_matches.values()]

    # search db-stored prototypes

    if tags:
        # exact match on tag(s)
        tags = make_iter(tags)
        tag_categories = ["db_prototype" for _ in tags]
        db_matches = DbPrototype.objects.get_by_tag(tags, tag_categories)
    else:
        db_matches = DbPrototype.objects.all().order_by("id")
    if key:
        # exact or partial match on key
        db_matches = (db_matches.filter(
            Q(db_key__iexact=key) | Q(db_key__icontains=key)).order_by("id"))
    # convert to prototype
    db_ids = db_matches.values_list("id", flat=True)
    db_matches = (Attribute.objects.filter(scriptdb__pk__in=db_ids,
                                           db_key="prototype").values_list(
                                               "db_value", flat=True))
    if key:
        matches = list(db_matches) + module_prototypes
        nmatches = len(matches)
        if nmatches > 1:
            key = key.lower()
            # avoid duplicates if an exact match exist between the two types
            filter_matches = [
                mta for mta in matches
                if mta.get("prototype_key") and mta["prototype_key"] == key
            ]
            if filter_matches and len(filter_matches) < nmatches:
                matches = filter_matches
        nmatches = len(matches)
        if nmatches != 1 and require_single:
            raise KeyError("Found {} matching prototypes.".format(nmatches))
        return matches
    elif return_iterators:
        # trying to get the entire set of prototypes - we must paginate
        # we must paginate the result of trying to fetch the entire set
        db_pages = Paginator(db_matches, 500)
        return module_prototypes, db_pages
    else:
        # full fetch, no pagination
        return list(db_matches) + module_prototypes
Пример #46
0
def create_script(
    typeclass=None,
    key=None,
    obj=None,
    account=None,
    locks=None,
    interval=None,
    start_delay=None,
    repeats=None,
    persistent=None,
    autostart=True,
    report_to=None,
    desc=None,
    tags=None,
    attributes=None,
):
    """
    Create a new script. All scripts are a combination of a database
    object that communicates with the database, and an typeclass that
    'decorates' the database object into being different types of
    scripts.  It's behaviour is similar to the game objects except
    scripts has a time component and are more limited in scope.

    Kwargs:
        typeclass (class or str): Class or python path to a typeclass.
        key (str): Name of the new object. If not set, a name of
            #dbref will be set.
        obj (Object): The entity on which this Script sits. If this
            is `None`, we are creating a "global" script.
        account (Account): The account on which this Script sits. It is
            exclusiv to `obj`.
        locks (str): one or more lockstrings, separated by semicolons.
        interval (int): The triggering interval for this Script, in
            seconds. If unset, the Script will not have a timing
            component.
        start_delay (bool): If `True`, will wait `interval` seconds
            before triggering the first time.
        repeats (int): The number of times to trigger before stopping.
            If unset, will repeat indefinitely.
        persistent (bool): If this Script survives a server shutdown
            or not (all Scripts will survive a reload).
        autostart (bool): If this Script will start immediately when
            created or if the `start` method must be called explicitly.
        report_to (Object): The object to return error messages to.
        desc (str): Optional description of script
        tags (list): List of tags or tuples (tag, category).
        attributes (list): List if tuples (key, value) or (key, value, category)
           (key, value, lockstring) or (key, value, lockstring, default_access).

    See evennia.scripts.manager for methods to manipulate existing
    scripts in the database.

    """
    global _ScriptDB
    if not _ScriptDB:
        from evennia.scripts.models import ScriptDB as _ScriptDB

    typeclass = typeclass if typeclass else settings.BASE_SCRIPT_TYPECLASS

    if isinstance(typeclass, str):
        # a path is given. Load the actual typeclass
        typeclass = class_from_module(typeclass, settings.TYPECLASS_PATHS)

    # validate input
    kwarg = {}
    if key:
        kwarg["db_key"] = key
    if account:
        kwarg["db_account"] = dbid_to_obj(account, _AccountDB)
    if obj:
        kwarg["db_obj"] = dbid_to_obj(obj, _ObjectDB)
    if interval:
        kwarg["db_interval"] = max(0, interval)
    if start_delay:
        kwarg["db_start_delay"] = start_delay
    if repeats:
        kwarg["db_repeats"] = max(0, repeats)
    if persistent:
        kwarg["db_persistent"] = persistent
    if desc:
        kwarg["db_desc"] = desc
    tags = make_iter(tags) if tags is not None else None
    attributes = make_iter(attributes) if attributes is not None else None

    # create new instance
    new_script = typeclass(**kwarg)

    # store the call signature for the signal
    new_script._createdict = dict(
        key=key,
        obj=obj,
        account=account,
        locks=locks,
        interval=interval,
        start_delay=start_delay,
        repeats=repeats,
        persistent=persistent,
        autostart=autostart,
        report_to=report_to,
        desc=desc,
        tags=tags,
        attributes=attributes,
    )
    # this will trigger the save signal which in turn calls the
    # at_first_save hook on the typeclass, where the _createdict
    # can be used.
    new_script.save()

    if not new_script.id:
        # this happens in the case of having a repeating script with `repeats=1` and
        # `start_delay=False` - the script will run once and immediately stop before save is over.
        return None

    signals.SIGNAL_SCRIPT_POST_CREATE.send(sender=new_script)

    return new_script
Пример #47
0
    def func(self):
        """Create the nickname"""
        def _cy(string):
            "add color to the special markers"
            return re.sub(r"(\$[0-9]+|\*|\?|\[.+?\])", r"|Y\1|n", string)

        caller = self.caller
        switches = self.switches
        nicktypes = [
            switch for switch in switches
            if switch in ("object", "account", "inputline")
        ]
        specified_nicktype = bool(nicktypes)
        nicktypes = nicktypes if specified_nicktype else ["inputline"]

        nicklist = (
            utils.make_iter(
                caller.nicks.get(category="inputline", return_obj=True) or [])
            + utils.make_iter(
                caller.nicks.get(category="object", return_obj=True) or []) +
            utils.make_iter(
                caller.nicks.get(category="account", return_obj=True) or []))

        if "list" in switches or self.cmdstring in ("nicks", ):

            if not nicklist:
                string = "|wNo nicks defined.|n"
            else:
                table = self.styled_table("#", "Type", "Nick match",
                                          "Replacement")
                for inum, nickobj in enumerate(nicklist):
                    _, _, nickvalue, replacement = nickobj.value
                    table.add_row(str(inum + 1), nickobj.db_category,
                                  _cy(nickvalue), _cy(replacement))
                string = "|wDefined Nicks:|n\n%s" % table
            caller.msg(string)
            return

        if "clearall" in switches:
            caller.nicks.clear()
            caller.account.nicks.clear()
            caller.msg("Cleared all nicks.")
            return

        if "delete" in switches or "del" in switches:
            if not self.args or not self.lhs:
                caller.msg(
                    "usage nick/delete <nick> or <#num> ('nicks' for list)")
                return
            # see if a number was given
            arg = self.args.lstrip("#")
            oldnicks = []
            if arg.isdigit():
                # we are given a index in nicklist
                delindex = int(arg)
                if 0 < delindex <= len(nicklist):
                    oldnicks.append(nicklist[delindex - 1])
                else:
                    caller.msg(
                        "Not a valid nick index. See 'nicks' for a list.")
                    return
            else:
                if not specified_nicktype:
                    nicktypes = ("object", "account", "inputline")
                for nicktype in nicktypes:
                    oldnicks.append(
                        caller.nicks.get(arg,
                                         category=nicktype,
                                         return_obj=True))

            oldnicks = [oldnick for oldnick in oldnicks if oldnick]
            if oldnicks:
                for oldnick in oldnicks:
                    nicktype = oldnick.category
                    nicktypestr = "%s-nick" % nicktype.capitalize()
                    _, _, old_nickstring, old_replstring = oldnick.value
                    caller.nicks.remove(old_nickstring, category=nicktype)
                    caller.msg("%s removed: '|w%s|n' -> |w%s|n." %
                               (nicktypestr, old_nickstring, old_replstring))
            else:
                caller.msg("No matching nicks to remove.")
            return

        if not self.rhs and self.lhs:
            # check what a nick is set to
            strings = []
            if not specified_nicktype:
                nicktypes = ("object", "account", "inputline")
            for nicktype in nicktypes:
                nicks = [
                    nick for nick in utils.make_iter(
                        caller.nicks.get(category=nicktype, return_obj=True))
                    if nick
                ]
                for nick in nicks:
                    _, _, nick, repl = nick.value
                    if nick.startswith(self.lhs):
                        strings.append("{}-nick: '{}' -> '{}'".format(
                            nicktype.capitalize(), nick, repl))
            if strings:
                caller.msg("\n".join(strings))
            else:
                caller.msg("No nicks found matching '{}'".format(self.lhs))
            return

        if not self.rhs and self.lhs:
            # check what a nick is set to
            strings = []
            if not specified_nicktype:
                nicktypes = ("object", "account", "inputline")
            for nicktype in nicktypes:
                if nicktype == "account":
                    obj = account
                else:
                    obj = caller
                nicks = utils.make_iter(
                    obj.nicks.get(category=nicktype, return_obj=True))
                for nick in nicks:
                    _, _, nick, repl = nick.value
                    if nick.startswith(self.lhs):
                        strings.append("{}-nick: '{}' -> '{}'".format(
                            nicktype.capitalize(), nick, repl))
            if strings:
                caller.msg("\n".join(strings))
            else:
                caller.msg("No nicks found matching '{}'".format(self.lhs))
            return

        if not self.rhs and self.lhs:
            # check what a nick is set to
            strings = []
            if not specified_nicktype:
                nicktypes = ("object", "account", "inputline")
            for nicktype in nicktypes:
                if nicktype == "account":
                    obj = account
                else:
                    obj = caller
                nicks = utils.make_iter(
                    obj.nicks.get(category=nicktype, return_obj=True))
                for nick in nicks:
                    _, _, nick, repl = nick.value
                    if nick.startswith(self.lhs):
                        strings.append("{}-nick: '{}' -> '{}'".format(
                            nicktype.capitalize(), nick, repl))
            if strings:
                caller.msg("\n".join(strings))
            else:
                caller.msg("No nicks found matching '{}'".format(self.lhs))
            return

        if not self.args or not self.lhs:
            caller.msg("Usage: nick[/switches] nickname = [realname]")
            return

        # setting new nicks

        nickstring = self.lhs
        replstring = self.rhs

        if replstring == nickstring:
            caller.msg(
                "No point in setting nick same as the string to replace...")
            return

        # check so we have a suitable nick type
        errstring = ""
        string = ""
        for nicktype in nicktypes:
            nicktypestr = "%s-nick" % nicktype.capitalize()
            old_nickstring = None
            old_replstring = None

            oldnick = caller.nicks.get(key=nickstring,
                                       category=nicktype,
                                       return_obj=True)
            if oldnick:
                _, _, old_nickstring, old_replstring = oldnick.value
            if replstring:
                # creating new nick
                errstring = ""
                if oldnick:
                    if replstring == old_replstring:
                        string += "\nIdentical %s already set." % nicktypestr.lower(
                        )
                    else:
                        string += "\n%s '|w%s|n' updated to map to '|w%s|n'." % (
                            nicktypestr,
                            old_nickstring,
                            replstring,
                        )
                else:
                    string += "\n%s '|w%s|n' mapped to '|w%s|n'." % (
                        nicktypestr,
                        nickstring,
                        replstring,
                    )
                try:
                    caller.nicks.add(nickstring, replstring, category=nicktype)
                except NickTemplateInvalid:
                    caller.msg(
                        "You must use the same $-markers both in the nick and in the replacement."
                    )
                    return
            elif old_nickstring and old_replstring:
                # just looking at the nick
                string += "\n%s '|w%s|n' maps to '|w%s|n'." % (
                    nicktypestr,
                    old_nickstring,
                    old_replstring,
                )
                errstring = ""
        string = errstring if errstring else string
        caller.msg(_cy(string))
Пример #48
0
from evennia.server.sessionhandler import SESSIONS

_SA = object.__setattr__

if os.name == 'nt':
    # For Windows we need to handle pid files manually.
    SERVER_PIDFILE = os.path.join(settings.GAME_DIR, "server", 'server.pid')

# a file with a flag telling the server to restart after shutdown or not.
SERVER_RESTART = os.path.join(settings.GAME_DIR, "server", 'server.restart')

# module containing hook methods called during start_stop
SERVER_STARTSTOP_MODULE = mod_import(settings.AT_SERVER_STARTSTOP_MODULE)

# modules containing plugin services
SERVER_SERVICES_PLUGIN_MODULES = [mod_import(module) for module in make_iter(settings.SERVER_SERVICES_PLUGIN_MODULES)]
try:
    WEB_PLUGINS_MODULE = mod_import(settings.WEB_PLUGINS_MODULE)
except ImportError:
    WEB_PLUGINS_MODULE = None
    print ("WARNING: settings.WEB_PLUGINS_MODULE not found - "
           "copy 'evennia/game_template/server/conf/web_plugins.py to mygame/server/conf.")

#------------------------------------------------------------
# Evennia Server settings
#------------------------------------------------------------

SERVERNAME = settings.SERVERNAME
VERSION = get_evennia_version()

AMP_ENABLED = True
Пример #49
0
def spawn(*prototypes, **kwargs):
    """
    Spawn a number of prototyped objects. Each argument should be a
    prototype dictionary.

    keyword args:
        prototype_modules - a python-path to a
            prototype module, or a list of such paths. These will be used
            to build the global protparents dictionary accessible by the
            input prototypes. If not given, it will instead look for modules
            defined by settings.PROTOTYPE_MODULES.
        prototype_parents - a dictionary holding a custom prototype-parent dictionary. Will
                      overload same-named prototypes from prototype_modules.
        return_prototypes - only return a list of the prototype-parents
                            (no object creation happens)
    """

    protparents = {}
    protmodules = make_iter(kwargs.get("prototype_modules", []))
    if not protmodules and hasattr(settings, "PROTOTYPE_MODULES"):
        protmodules = make_iter(settings.PROTOTYPE_MODULES)
    for prototype_module in protmodules:
        protparents.update(
            dict((key, val)
                 for key, val in all_from_module(prototype_module).items()
                 if isinstance(val, dict)))
    #overload module's protparents with specifically given protparents
    protparents.update(kwargs.get("prototype_parents", {}))
    for key, prototype in protparents.items():
        _validate_prototype(key, prototype, protparents, [])

    if "return_prototypes" in kwargs:
        # only return the parents
        return copy.deepcopy(protparents)

    objsparams = []
    for prototype in prototypes:

        _validate_prototype(None, prototype, protparents, [])
        prot = _get_prototype(prototype, {}, protparents)
        if not prot:
            continue

        # extract the keyword args we need to create the object itself
        create_kwargs = {}
        create_kwargs["db_key"] = prot.pop(
            "key", "Spawned Object %06i" % randint(1, 100000))
        create_kwargs["db_location"] = _handle_dbref(prot.pop(
            "location", None))
        create_kwargs["db_home"] = _handle_dbref(
            prot.pop("home", settings.DEFAULT_HOME))
        create_kwargs["db_destination"] = _handle_dbref(
            prot.pop("destination", None))
        create_kwargs["db_typeclass_path"] = prot.pop(
            "typeclass", settings.BASE_OBJECT_TYPECLASS)

        # extract calls to handlers
        permission_string = prot.pop("permissions", "")
        lock_string = prot.pop("locks", "")
        alias_string = prot.pop("aliases", "")
        tags = prot.pop("tags", "")

        # extract ndb assignments
        nattributes = dict(
            (key.split("_", 1)[1], value if callable(value) else value)
            for key, value in prot.items() if key.startswith("ndb_"))

        # the rest are attributes
        attributes = dict(
            (key, value() if callable(value) else value)
            for key, value in prot.items()
            if not (key in _CREATE_OBJECT_KWARGS or key.startswith("ndb_")))

        # pack for call into _batch_create_object
        objsparams.append((create_kwargs, permission_string, lock_string,
                           alias_string, nattributes, attributes, tags))

    return _batch_create_object(*objsparams)
Пример #50
0
    def func(self):
        """Create the nickname"""

        caller = self.caller
        switches = self.switches
        nicktypes = [
            switch for switch in switches
            if switch in ("object", "account", "inputline")
        ] or ["inputline"]

        nicklist = utils.make_iter(caller.nicks.get(return_obj=True) or [])

        if 'list' in switches or self.cmdstring in ("nicks", "@nicks"):

            if not nicklist:
                string = "|wNo nicks defined.|n"
            else:
                table = evtable.EvTable("#", "Type", "Nick match",
                                        "Replacement")
                for inum, nickobj in enumerate(nicklist):
                    _, _, nickvalue, replacement = nickobj.value
                    table.add_row(str(inum + 1), nickobj.db_category,
                                  nickvalue, replacement)
                string = "|wDefined Nicks:|n\n%s" % table
            caller.msg(string)
            return

        if 'clearall' in switches:
            caller.nicks.clear()
            caller.msg("Cleared all nicks.")
            return

        if not self.args or not self.lhs:
            caller.msg("Usage: nick[/switches] nickname = [realname]")
            return

        nickstring = self.lhs
        replstring = self.rhs
        old_nickstring = None
        old_replstring = None

        if replstring == nickstring:
            caller.msg(
                "No point in setting nick same as the string to replace...")
            return

        # check so we have a suitable nick type
        errstring = ""
        string = ""
        for nicktype in nicktypes:
            oldnick = caller.nicks.get(key=nickstring,
                                       category=nicktype,
                                       return_obj=True)
            if oldnick:
                _, _, old_nickstring, old_replstring = oldnick.value
            else:
                # no old nick, see if a number was given
                arg = self.args.lstrip("#")
                if arg.isdigit():
                    # we are given a index in nicklist
                    delindex = int(arg)
                    if 0 < delindex <= len(nicklist):
                        oldnick = nicklist[delindex - 1]
                        _, _, old_nickstring, old_replstring = oldnick.value
                    else:
                        errstring += "Not a valid nick index."
                else:
                    errstring += "Nick not found."
            if "delete" in switches or "del" in switches:
                # clear the nick
                if old_nickstring and caller.nicks.has(old_nickstring,
                                                       category=nicktype):
                    caller.nicks.remove(old_nickstring, category=nicktype)
                    string += "\nNick removed: '|w%s|n' -> |w%s|n." % (
                        old_nickstring, old_replstring)
                else:
                    errstring += "\nNick '|w%s|n' was not deleted." % old_nickstring
            elif replstring:
                # creating new nick
                errstring = ""
                if oldnick:
                    string += "\nNick '|w%s|n' updated to map to '|w%s|n'." % (
                        old_nickstring, replstring)
                else:
                    string += "\nNick '|w%s|n' mapped to '|w%s|n'." % (
                        nickstring, replstring)
                try:
                    caller.nicks.add(nickstring, replstring, category=nicktype)
                except NickTemplateInvalid:
                    caller.msg(
                        "You must use the same $-markers both in the nick and in the replacement."
                    )
                    return
            elif old_nickstring and old_replstring:
                # just looking at the nick
                string += "\nNick '|w%s|n' maps to '|w%s|n'." % (
                    old_nickstring, old_replstring)
                errstring = ""
        string = errstring if errstring else string
        caller.msg(string)
Пример #51
0
    def get(self,
            key=None,
            default=None,
            category=None,
            return_obj=False,
            strattr=False,
            raise_exception=False,
            accessing_obj=None,
            default_access=True,
            return_list=False):
        """
        Get the Attribute.

        Args:
            key (str or list, optional): the attribute identifier or
                multiple attributes to get. if a list of keys, the
                method will return a list.
            category (str, optional): the category within which to
                retrieve attribute(s).
            default (any, optional): The value to return if an
                Attribute was not defined. If set, it will be returned in
                a one-item list.
            return_obj (bool, optional): If set, the return is not the value of the
                Attribute but the Attribute object itself.
            strattr (bool, optional): Return the `strvalue` field of
                the Attribute rather than the usual `value`, this is a
                string-only value for quick database searches.
            raise_exception (bool, optional): When an Attribute is not
                found, the return from this is usually `default`. If this
                is set, an exception is raised instead.
            accessing_obj (object, optional): If set, an `attrread`
                permission lock will be checked before returning each
                looked-after Attribute.
            default_access (bool, optional): If no `attrread` lock is set on
                object, this determines if the lock should then be passed or not.
            return_list (bool, optional):

        Returns:
            result (any or list): One or more matches for keys and/or categories. Each match will be
                the value of the found Attribute(s) unless `return_obj` is True, at which point it
                will be the attribute object itself or None. If `return_list` is True, this will
                always be a list, regardless of the number of elements.

        Raises:
            AttributeError: If `raise_exception` is set and no matching Attribute
                was found matching `key`.

        """
        class RetDefault(object):
            """Holds default values"""
            def __init__(self):
                self.key = None
                self.value = default
                self.category = None
                self.strvalue = str(default) if default is not None else None

        ret = []
        for keystr in make_iter(key):
            # it's okay to send a None key
            attr_objs = self._getcache(keystr, category)
            if attr_objs:
                ret.extend(attr_objs)
            elif raise_exception:
                raise AttributeError
            elif return_obj:
                ret.append(None)
            else:
                ret.append(RetDefault())

        if accessing_obj:
            # check 'attrread' locks
            ret = [
                attr for attr in ret if attr.access(
                    accessing_obj, self._attrread, default=default_access)
            ]
        if strattr:
            ret = ret if return_obj else [
                attr.strvalue for attr in ret if attr
            ]
        else:
            ret = ret if return_obj else [attr.value for attr in ret if attr]

        if return_list:
            return ret if ret else [default] if default is not None else []
        elif not ret:
            return ret if len(key) > 1 else default
        return ret[0] if len(ret) == 1 else ret
Пример #52
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.

    """
    if not prototype or not isinstance(prototype, dict):
        return {}

    reserved = _PROTOTYPE_RESERVED_KEYS + (custom_keys or ())

    # correct cases of setting None for certain values
    for protkey in prototype:
        if prototype[protkey] is None:
            if protkey in ("attrs", "tags", "prototype_tags"):
                prototype[protkey] = []
            elif protkey in ("prototype_key", "prototype_desc"):
                prototype[protkey] = ""

    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

    homogenized["prototype_key"] = homogenized.get(
        "prototype_key",
        # assign a random hash as key
        "prototype-{}".format(
            hashlib.md5(bytes(str(time.time()), "utf-8")).hexdigest()[:7]),
    )
    homogenized["prototype_tags"] = homogenized.get("prototype_tags", [])
    homogenized["prototype_locks"] = homogenized.get("prototype_lock",
                                                     _PROTOTYPE_FALLBACK_LOCK)
    homogenized["prototype_desc"] = homogenized.get("prototype_desc", "")
    if "typeclass" not in prototype and "prototype_parent" not in prototype:
        homogenized["typeclass"] = settings.BASE_OBJECT_TYPECLASS

    return homogenized
Пример #53
0
    # assign module path to each prototype_key for easy reference
    _MODULE_PROTOTYPE_MODULES.update(
        {prototype_key.lower(): mod
         for prototype_key, _ in prots})
    # make sure the prototype contains all meta info
    for prototype_key, prot in prots:
        actual_prot_key = prot.get("prototype_key", prototype_key).lower()
        prot.update({
            "prototype_key":
            actual_prot_key,
            "prototype_desc":
            prot["prototype_desc"] if "prototype_desc" in prot else mod,
            "prototype_locks": (prot["prototype_locks"] if "prototype_locks"
                                in prot else "use:all();edit:false()"),
            "prototype_tags":
            list(set(make_iter(prot.get("prototype_tags", [])) + ["module"])),
        })
        _MODULE_PROTOTYPES[actual_prot_key] = prot

# Db-based prototypes


class DbPrototype(DefaultScript):
    """
    This stores a single prototype, in an Attribute `prototype`.
    """
    def at_script_creation(self):
        self.key = "empty prototype"  # prototype_key
        self.desc = "A prototype"  # prototype_desc (.tags are used for prototype_tags)
        self.db.prototype = {}  # actual prototype
Пример #54
0
def create_account(
    key,
    email,
    password,
    typeclass=None,
    is_superuser=False,
    locks=None,
    permissions=None,
    tags=None,
    attributes=None,
    report_to=None,
):
    """
    This creates a new account.

    Args:
        key (str): The account's name. This should be unique.
        email (str or None): Email on valid [email protected] form. If
            the empty string, will be set to None.
        password (str): Password in cleartext.

    Kwargs:
        typeclass (str): The typeclass to use for the account.
        is_superuser (bool): Wether or not this account is to be a superuser
        locks (str): Lockstring.
        permission (list): List of permission strings.
        tags (list): List of Tags on form `(key, category[, data])`
        attributes (list): List of Attributes on form
             `(key, value [, category, [,lockstring [, default_pass]]])`
        report_to (Object): An object with a msg() method to report
            errors to. If not given, errors will be logged.

    Returns:
        Account: The newly created Account.
    Raises:
        ValueError: If `key` already exists in database.


    Notes:
        Usually only the server admin should need to be superuser, all
        other access levels can be handled with more fine-grained
        permissions or groups. A superuser bypasses all lock checking
        operations and is thus not suitable for play-testing the game.

    """
    global _AccountDB
    if not _AccountDB:
        from evennia.accounts.models import AccountDB as _AccountDB

    typeclass = typeclass if typeclass else settings.BASE_ACCOUNT_TYPECLASS
    locks = make_iter(locks) if locks is not None else None
    permissions = make_iter(permissions) if permissions is not None else None
    tags = make_iter(tags) if tags is not None else None
    attributes = make_iter(attributes) if attributes is not None else None

    if isinstance(typeclass, str):
        # a path is given. Load the actual typeclass.
        typeclass = class_from_module(typeclass, settings.TYPECLASS_PATHS)

    # setup input for the create command. We use AccountDB as baseclass
    # here to give us maximum freedom (the typeclasses will load
    # correctly when each object is recovered).

    if not email:
        email = None
    if _AccountDB.objects.filter(username__iexact=key):
        raise ValueError("An Account with the name '%s' already exists." % key)

    # this handles a given dbref-relocate to an account.
    report_to = dbid_to_obj(report_to, _AccountDB)

    # create the correct account entity, using the setup from
    # base django auth.
    now = timezone.now()
    email = typeclass.objects.normalize_email(email)
    new_account = typeclass(
        username=key,
        email=email,
        is_staff=is_superuser,
        is_superuser=is_superuser,
        last_login=now,
        date_joined=now,
    )
    if password is not None:
        # the password may be None for 'fake' accounts, like bots
        valid, error = new_account.validate_password(password, new_account)
        if not valid:
            raise error

        new_account.set_password(password)

    new_account._createdict = dict(locks=locks,
                                   permissions=permissions,
                                   report_to=report_to,
                                   tags=tags,
                                   attributes=attributes)
    # saving will trigger the signal that calls the
    # at_first_save hook on the typeclass, where the _createdict
    # can be used.
    new_account.save()

    # note that we don't send a signal here, that is sent from the Account.create helper method
    # instead.

    return new_account
Пример #55
0
    def get_by_tag(self, key=None, category=None, tagtype=None, **kwargs):
        """
        Return objects having tags with a given key or category or combination of the two.
        Also accepts multiple tags/category/tagtype

        Args:
            key (str or list, optional): Tag key or list of keys. Not case sensitive.
            category (str or list, optional): Tag category. Not case sensitive.
                If `key` is a list, a single category can either apply to all
                keys in that list or this must be a list matching the `key`
                list element by element. If no `key` is given, all objects with
                tags of this category are returned.
            tagtype (str, optional): 'type' of Tag, by default
                this is either `None` (a normal Tag), `alias` or
                `permission`. This always apply to all queried tags.

        Kwargs:
            match (str): "all" (default) or "any"; determines whether the
                target object must be tagged with ALL of the provided
                tags/categories or ANY single one. ANY will perform a weighted
                sort, so objects with more tag matches will outrank those with
                fewer tag matches.

        Returns:
            objects (list): Objects with matching tag.

        Raises:
            IndexError: If `key` and `category` are both lists and `category` is shorter
                than `key`.

        """
        if not (key or category):
            return []

        global _Tag
        if not _Tag:
            from evennia.typeclasses.models import Tag as _Tag

        anymatch = "any" == kwargs.get("match", "all").lower().strip()

        keys = make_iter(key) if key else []
        categories = make_iter(category) if category else []
        n_keys = len(keys)
        n_categories = len(categories)
        unique_categories = sorted(set(categories))
        n_unique_categories = len(unique_categories)

        dbmodel = self.model.__dbclass__.__name__.lower()
        query = (self.filter(
            db_tags__db_tagtype__iexact=tagtype,
            db_tags__db_model__iexact=dbmodel).distinct().order_by("id"))

        if n_keys > 0:
            # keys and/or categories given
            if n_categories == 0:
                categories = [None for _ in range(n_keys)]
            elif n_categories == 1 and n_keys > 1:
                cat = categories[0]
                categories = [cat for _ in range(n_keys)]
            elif 1 < n_categories < n_keys:
                raise IndexError(
                    "get_by_tag needs a single category or a list of categories "
                    "the same length as the list of tags.")
            clauses = Q()
            for ikey, key in enumerate(keys):
                # ANY mode; must match any one of the given tags/categories
                clauses |= Q(db_key__iexact=key,
                             db_category__iexact=categories[ikey])
        else:
            # only one or more categories given
            clauses = Q()
            # ANY mode; must match any one of them
            for category in unique_categories:
                clauses |= Q(db_category__iexact=category)

        tags = _Tag.objects.filter(clauses)
        query = query.filter(db_tags__in=tags).annotate(matches=Count(
            "db_tags__pk", filter=Q(db_tags__in=tags), distinct=True))

        if anymatch:
            # ANY: Match any single tag, ordered by weight
            query = query.order_by("-matches")
        else:
            # Default ALL: Match all of the tags and optionally more
            n_req_tags = n_keys if n_keys > 0 else n_unique_categories
            query = query.filter(matches__gte=n_req_tags)

        return query
Пример #56
0
def create_object(
    typeclass=None,
    key=None,
    location=None,
    home=None,
    permissions=None,
    locks=None,
    aliases=None,
    tags=None,
    destination=None,
    report_to=None,
    nohome=False,
    attributes=None,
    nattributes=None,
):
    """

    Create a new in-game object.

    Kwargs:
        typeclass (class or str): Class or python path to a typeclass.
        key (str): Name of the new object. If not set, a name of
            #dbref will be set.
        home (Object or str): Obj or #dbref to use as the object's
            home location.
        permissions (list): A list of permission strings or tuples (permstring, category).
        locks (str): one or more lockstrings, separated by semicolons.
        aliases (list): A list of alternative keys or tuples (aliasstring, category).
        tags (list): List of tag keys or tuples (tagkey, category) or (tagkey, category, data).
        destination (Object or str): Obj or #dbref to use as an Exit's
            target.
        report_to (Object): The object to return error messages to.
        nohome (bool): This allows the creation of objects without a
            default home location; only used when creating the default
            location itself or during unittests.
        attributes (list): Tuples on the form (key, value) or (key, value, category),
           (key, value, lockstring) or (key, value, lockstring, default_access).
            to set as Attributes on the new object.
        nattributes (list): Non-persistent tuples on the form (key, value). Note that
            adding this rarely makes sense since this data will not survive a reload.

    Returns:
        object (Object): A newly created object of the given typeclass.

    Raises:
        ObjectDB.DoesNotExist: If trying to create an Object with
            `location` or `home` that can't be found.

    """
    global _ObjectDB
    if not _ObjectDB:
        from evennia.objects.models import ObjectDB as _ObjectDB

    typeclass = typeclass if typeclass else settings.BASE_OBJECT_TYPECLASS

    # convenience converters to avoid common usage mistake
    permissions = make_iter(permissions) if permissions is not None else None
    locks = make_iter(locks) if locks is not None else None
    aliases = make_iter(aliases) if aliases is not None else None
    tags = make_iter(tags) if tags is not None else None
    attributes = make_iter(attributes) if attributes is not None else None

    if isinstance(typeclass, str):
        # a path is given. Load the actual typeclass
        typeclass = class_from_module(typeclass, settings.TYPECLASS_PATHS)

    # Setup input for the create command. We use ObjectDB as baseclass here
    # to give us maximum freedom (the typeclasses will load
    # correctly when each object is recovered).

    location = dbid_to_obj(location, _ObjectDB)
    destination = dbid_to_obj(destination, _ObjectDB)
    home = dbid_to_obj(home, _ObjectDB)
    if not home:
        try:
            home = dbid_to_obj(settings.DEFAULT_HOME,
                               _ObjectDB) if not nohome else None
        except _ObjectDB.DoesNotExist:
            raise _ObjectDB.DoesNotExist(
                "settings.DEFAULT_HOME (= '%s') does not exist, or the setting is malformed."
                % settings.DEFAULT_HOME)

    # create new instance
    new_object = typeclass(
        db_key=key,
        db_location=location,
        db_destination=destination,
        db_home=home,
        db_typeclass_path=typeclass.path,
    )
    # store the call signature for the signal
    new_object._createdict = dict(
        key=key,
        location=location,
        destination=destination,
        home=home,
        typeclass=typeclass.path,
        permissions=permissions,
        locks=locks,
        aliases=aliases,
        tags=tags,
        report_to=report_to,
        nohome=nohome,
        attributes=attributes,
        nattributes=nattributes,
    )
    # this will trigger the save signal which in turn calls the
    # at_first_save hook on the typeclass, where the _createdict can be
    # used.
    new_object.save()

    signals.SIGNAL_OBJECT_POST_CREATE.send(sender=new_object)

    return new_object
Пример #57
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 is not 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 efficiently?
            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 []
Пример #58
0
    for variable_name, prot in all_from_module(mod).items():
        if isinstance(prot, dict):
            if "prototype_key" not in prot:
                prot['prototype_key'] = variable_name.lower()
            prots.append((prot['prototype_key'], homogenize_prototype(prot)))
    # assign module path to each prototype_key for easy reference
    _MODULE_PROTOTYPE_MODULES.update({prototype_key.lower(): mod for prototype_key, _ in prots})
    # make sure the prototype contains all meta info
    for prototype_key, prot in prots:
        actual_prot_key = prot.get('prototype_key', prototype_key).lower()
        prot.update({
          "prototype_key": actual_prot_key,
          "prototype_desc": prot['prototype_desc'] if 'prototype_desc' in prot else mod,
          "prototype_locks": (prot['prototype_locks']
                              if 'prototype_locks' in prot else "use:all();edit:false()"),
          "prototype_tags": list(set(make_iter(prot.get('prototype_tags', [])) + ["module"]))})
        _MODULE_PROTOTYPES[actual_prot_key] = prot


# Db-based prototypes


class DbPrototype(DefaultScript):
    """
    This stores a single prototype, in an Attribute `prototype`.
    """
    def at_script_creation(self):
        self.key = "empty prototype"  # prototype_key
        self.desc = "A prototype"     # prototype_desc (.tags are used for prototype_tags)
        self.db.prototype = {}        # actual prototype
Пример #59
0
# i18n
from django.utils.translation import ugettext as _

_SERVERNAME = settings.SERVERNAME
_MULTISESSION_MODE = settings.MULTISESSION_MODE
_IDLE_TIMEOUT = settings.IDLE_TIMEOUT
_DELAY_CMD_LOGINSTART = settings.DELAY_CMD_LOGINSTART
_MAX_SERVER_COMMANDS_PER_SECOND = 100.0
_MAX_SESSION_COMMANDS_PER_SECOND = 5.0
_MODEL_MAP = None

# input handlers

_INPUT_FUNCS = {}
for modname in make_iter(settings.INPUT_FUNC_MODULES):
    _INPUT_FUNCS.update(callables_from_module(modname))


def delayed_import():
    """
    Helper method for delayed import of all needed entities.

    """
    global _ServerSession, _AccountDB, _ServerConfig, _ScriptDB
    if not _ServerSession:
        # we allow optional arbitrary serversession class for overloading
        modulename, classname = settings.SERVER_SESSION_CLASS.rsplit(".", 1)
        _ServerSession = variable_from_module(modulename, classname)
    if not _AccountDB:
        from evennia.accounts.models import AccountDB as _AccountDB
Пример #60
0
def save_prototype(prototype):
    """
    Create/Store a prototype persistently.

    Args:
        prototype (dict): The prototype to save. A `prototype_key` key is
            required.

    Returns:
        prototype (dict or None): The prototype stored using the given kwargs, None if deleting.

    Raises:
        prototypes.ValidationError: If prototype does not validate.

    Note:
        No edit/spawn locks will be checked here - if this function is called the caller
        is expected to have valid permissions.

    """
    in_prototype = prototype
    in_prototype = homogenize_prototype(in_prototype)

    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

    prototype_key = in_prototype.get("prototype_key")
    if not prototype_key:
        raise ValidationError("Prototype requires a prototype_key")

    prototype_key = str(prototype_key).lower()

    # we can't edit a prototype defined in a module
    if prototype_key in _MODULE_PROTOTYPES:
        mod = _MODULE_PROTOTYPE_MODULES.get(prototype_key, "N/A")
        raise PermissionError("{} is a read-only prototype "
                              "(defined as code in {}).".format(
                                  prototype_key, mod))

    # make sure meta properties are included with defaults
    in_prototype["prototype_desc"] = in_prototype.get(
        "prototype_desc", prototype.get("prototype_desc", ""))
    prototype_locks = in_prototype.get(
        "prototype_locks",
        prototype.get("prototype_locks", _PROTOTYPE_FALLBACK_LOCK))
    is_valid, err = validate_lockstring(prototype_locks)
    if not is_valid:
        raise ValidationError("Lock error: {}".format(err))
    in_prototype["prototype_locks"] = prototype_locks

    prototype_tags = [
        _to_batchtuple(tag, _PROTOTYPE_TAG_META_CATEGORY) for tag in make_iter(
            in_prototype.get("prototype_tags",
                             prototype.get("prototype_tags", [])))
    ]
    in_prototype["prototype_tags"] = prototype_tags

    stored_prototype = DbPrototype.objects.filter(db_key=prototype_key)
    if stored_prototype:
        # edit existing prototype
        stored_prototype = stored_prototype[0]
        stored_prototype.desc = in_prototype["prototype_desc"]
        if prototype_tags:
            stored_prototype.tags.clear(category=PROTOTYPE_TAG_CATEGORY)
            stored_prototype.tags.batch_add(*in_prototype["prototype_tags"])
        stored_prototype.locks.add(in_prototype["prototype_locks"])
        stored_prototype.attributes.add("prototype", in_prototype)
    else:
        # create a new prototype
        stored_prototype = create_script(
            DbPrototype,
            key=prototype_key,
            desc=in_prototype["prototype_desc"],
            persistent=True,
            locks=prototype_locks,
            tags=in_prototype["prototype_tags"],
            attributes=[("prototype", in_prototype)],
        )
    return stored_prototype.prototype