Пример #1
0
    def func(self):
        "Implement the command"

        caller = self.caller

        if self.args and self.args.isdigit():
            nlim = int(self.args)
        else:
            nlim = 10

        nobjs = ObjectDB.objects.count()
        base_char_typeclass = settings.BASE_CHARACTER_TYPECLASS
        nchars = ObjectDB.objects.filter(
            db_typeclass_path=base_char_typeclass).count()
        nrooms = ObjectDB.objects.filter(db_location__isnull=True).exclude(
            db_typeclass_path=base_char_typeclass).count()
        nexits = ObjectDB.objects.filter(db_location__isnull=False,
                                         db_destination__isnull=False).count()
        nother = nobjs - nchars - nrooms - nexits

        nobjs = nobjs or 1  # fix zero-div error with empty database

        # total object sum table
        totaltable = EvTable("{wtype{n",
                             "{wcomment{n",
                             "{wcount{n",
                             "{w%%{n",
                             border="table",
                             align="l")
        totaltable.align = 'l'
        totaltable.add_row("Characters", "(BASE_CHARACTER_TYPECLASS)", nchars,
                           "%.2f" % ((float(nchars) / nobjs) * 100))
        totaltable.add_row("Rooms", "(location=None)", nrooms,
                           "%.2f" % ((float(nrooms) / nobjs) * 100))
        totaltable.add_row("Exits", "(destination!=None)", nexits,
                           "%.2f" % ((float(nexits) / nobjs) * 100))
        totaltable.add_row("Other", "", nother,
                           "%.2f" % ((float(nother) / nobjs) * 100))

        # typeclass table
        typetable = EvTable("{wtypeclass{n",
                            "{wcount{n",
                            "{w%%{n",
                            border="table",
                            align="l")
        typetable.align = 'l'
        dbtotals = ObjectDB.objects.object_totals()
        for path, count in dbtotals.items():
            typetable.add_row(path, count,
                              "%.2f" % ((float(count) / nobjs) * 100))

        # last N table
        objs = ObjectDB.objects.all().order_by(
            "db_date_created")[max(0, nobjs - nlim):]
        latesttable = EvTable("{wcreated{n",
                              "{wdbref{n",
                              "{wname{n",
                              "{wtypeclass{n",
                              align="l",
                              border="table")
        latesttable.align = 'l'
        for obj in objs:
            latesttable.add_row(utils.datetime_format(obj.date_created),
                                obj.dbref, obj.key, obj.path)

        string = "\n{wObject subtype totals (out of %i Objects):{n\n%s" % (
            nobjs, totaltable)
        string += "\n{wObject typeclass distribution:{n\n%s" % typetable
        string += "\n{wLast %s Objects created:{n\n%s" % (min(
            nobjs, nlim), latesttable)
        caller.msg(string)
Пример #2
0
    def count_poses(self):
        """Makes a board post of characters with insufficient pose-counts"""
        qs = ObjectDB.objects.filter(roster__roster__name="Active")
        min_poses = 20
        low_activity = []
        for ob in qs:
            if (ob.posecount < min_poses
                    and (ob.tags.get("rostercg") and ob.player_ob
                         and not ob.player_ob.tags.get("staff_alt"))):
                low_activity.append(ob)
            ob.db.previous_posecount = ob.posecount
            ob.posecount = 0
        board = BBoard.objects.get(db_key__iexact="staff")
        table = EvTable("{wName{n", "{wNum Poses{n", border="cells", width=78)
        for ob in low_activity:
            table.add_row(ob.key, ob.db.previous_posecount)
        board.bb_post(poster_obj=self,
                      msg=str(table),
                      subject="Inactive by Poses List")

        # Various 'Beats' -------------------------------------------------

        #def process_journals(self, player):
        """
        In the journals here, we're processing all the XP gained for
        making journals, comments, or updating relationships.
        """
        char = player.char_ob
        try:
            account = player.roster.current_account
            if account.id in self.ndb.xptypes:
                total = self.ndb.xptypes[account.id].get("journals", 0)
            else:
                self.ndb.xptypes[account.id] = {}
                total = 0
            journal_total = char.messages.num_weekly_journals
            xp = 0
            if journal_total > 0:
                xp += 4
            if journal_total > 1:
                xp += 2
            if journal_total > 2:
                xp += 1
            # XP capped at 7 for all sources
            if xp > 7:
                xp = 7
            if xp + total > 7:
                xp = 7 - total
            if xp <= 0:
                return
        except (ValueError, TypeError):
            return
        except AttributeError:
            return
        except Exception as err:
            print("ERROR in process journals: %s" % err)
            traceback.print_exc()
            return
        if xp:
            msg = "You received %s xp this week for journals/comments/relationship updates." % xp
            self.award_xp(char, xp, player, msg, xptype="journals")

    # -----------------------------------------------------------------

    #def count_votes(self, player):
        """
        Counts the votes for each player. We may log voting patterns later if
        we need to track against abuse, but since voting is stored in each
        player it's fairly trivial to check each week on an individual basis
        anyway.
        """
        votes = player.db.votes or []
        for ob in votes:
            self.ndb.recorded_votes[ob] += 1
        if votes:
            self.ndb.vote_history[player] = votes
Пример #3
0
    def post_top_prestige(self):
        """Makes a board post of the top prestige earners this past week"""
        import random
        from world.dominion.models import PraiseOrCondemn
        changes = PraiseOrCondemn.objects.filter(week=self.db.week).exclude(
            target__organization_owner__secret=True)
        praises = defaultdict(list)
        condemns = defaultdict(list)
        total_values = {}
        for praise in changes.filter(value__gte=0):
            praises[praise.target].append(praise)
        for condemn in changes.filter(value__lt=0):
            condemns[condemn.target].append(condemn)
        for change in changes:
            current = total_values.get(change.target, 0)
            current += change.value
            total_values[change.target] = current

        board = BBoard.objects.get(db_key__iexact=PRESTIGE_BOARD_NAME)

        def get_total_from_list(entry_list):
            """Helper function to get total prestige amount from a list"""
            return sum(praise_ob.value for praise_ob in entry_list)

        sorted_praises = sorted(praises.items(),
                                key=lambda x: get_total_from_list(x[1]),
                                reverse=True)
        sorted_praises = sorted_praises[:20]
        table = EvTable("{wName{n",
                        "{wValue{n",
                        "{wMsg{n",
                        border="cells",
                        width=78)
        for tup in sorted_praises:
            praise_messages = [ob.message for ob in tup[1] if ob.message]
            selected_message = ""
            if praise_messages:
                selected_message = random.choice(praise_messages)
            table.add_row(
                str(tup[0]).capitalize()[:18], get_total_from_list(tup[1]),
                selected_message)
        table.reformat_column(0, width=18)
        table.reformat_column(1, width=10)
        table.reformat_column(2, width=50)
        prestige_msg = "{wMost Praised this week{n".center(72)
        prestige_msg = "%s\n%s" % (prestige_msg, str(table).lstrip())
        prestige_msg += "\n\n"
        try:
            # sort by our prestige change amount
            sorted_changes = sorted(total_values.items(),
                                    key=lambda x: abs(x[1]),
                                    reverse=True)
            sorted_changes = sorted_changes[:20]
            table = EvTable("{wName{n",
                            "{wPrestige Change Amount{n",
                            "{wPrestige Rank{n",
                            border="cells",
                            width=78)
            rank_order = list(
                AssetOwner.objects.filter(
                    player__player__roster__roster__name="Active").distinct())
            rank_order = sorted(rank_order,
                                key=lambda x: x.prestige,
                                reverse=True)
            for tup in sorted_changes:
                # get our prestige ranking compared to others
                owner = tup[0]
                try:
                    rank = rank_order.index(owner) + 1
                except ValueError:
                    # they rostered mid-week or whatever, skip them
                    continue
                # get the amount that our prestige has changed. add + for positive
                amt = tup[1]
                if amt > 0:
                    amt = "+%s" % amt
                table.add_row(owner, amt, rank)
            prestige_msg += "\n\n"
            prestige_msg += "{wTop Prestige Changes{n".center(72)
            prestige_msg = "%s\n%s" % (prestige_msg, str(table).lstrip())
        except (AttributeError, ValueError, TypeError):
            import traceback
            traceback.print_exc()
        board.bb_post(poster_obj=self,
                      msg=prestige_msg,
                      subject="Weekly Praises/Condemns",
                      poster_name="Prestige")
        inform_staff("Praises/condemns tally complete. Posted on %s." % board)
Пример #4
0
    def list_callbacks(self):
        """Display the list of callbacks connected to the object."""
        obj = self.obj
        callback_name = self.callback_name
        parameters = self.parameters
        callbacks = self.handler.get_callbacks(obj)
        types = self.handler.get_events(obj)

        if callback_name:
            # Check that the callback name can be found in this object
            created = callbacks.get(callback_name)
            if created is None:
                self.msg("No callback {} has been set on {}.".format(
                    callback_name, obj))
                return

            if parameters:
                # Check that the parameter points to an existing callback
                try:
                    number = int(parameters) - 1
                    assert number >= 0
                    callback = callbacks[callback_name][number]
                except (ValueError, AssertionError, IndexError):
                    self.msg(
                        "The callback {} {} cannot be found in {}.".format(
                            callback_name, parameters, obj))
                    return

                # Display the callback's details
                author = callback.get("author")
                author = author.key if author else "|gUnknown|n"
                updated_by = callback.get("updated_by")
                updated_by = updated_by.key if updated_by else "|gUnknown|n"
                created_on = callback.get("created_on")
                created_on = created_on.strftime(
                    "%Y-%m-%d %H:%M:%S") if created_on else "|gUnknown|n"
                updated_on = callback.get("updated_on")
                updated_on = updated_on.strftime(
                    "%Y-%m-%d %H:%M:%S") if updated_on else "|gUnknown|n"
                msg = "Callback {} {} of {}:".format(callback_name, parameters,
                                                     obj)
                msg += "\nCreated by {} on {}.".format(author, created_on)
                msg += "\nUpdated by {} on {}".format(updated_by, updated_on)

                if self.is_validator:
                    if callback.get("valid"):
                        msg += "\nThis callback is |rconnected|n and active."
                    else:
                        msg += "\nThis callback |rhasn't been|n accepted yet."

                msg += "\nCallback code:\n"
                msg += raw(callback["code"])
                self.msg(msg)
                return

            # No parameter has been specified, display the table of callbacks
            cols = ["Number", "Author", "Updated", "Param"]
            if self.is_validator:
                cols.append("Valid")

            table = EvTable(*cols, width=78)
            table.reformat_column(0, align="r")
            now = datetime.now()
            for i, callback in enumerate(created):
                author = callback.get("author")
                author = author.key if author else "|gUnknown|n"
                updated_on = callback.get("updated_on")
                if updated_on is None:
                    updated_on = callback.get("created_on")

                if updated_on:
                    updated_on = "{} ago".format(
                        time_format((now - updated_on).total_seconds(),
                                    4).capitalize())
                else:
                    updated_on = "|gUnknown|n"
                parameters = callback.get("parameters", "")

                row = [str(i + 1), author, updated_on, parameters]
                if self.is_validator:
                    row.append("Yes" if callback.get("valid") else "No")
                table.add_row(*row)

            self.msg(unicode(table))
        else:
            names = list(set(list(types.keys()) + list(callbacks.keys())))
            table = EvTable("Callback name",
                            "Number",
                            "Description",
                            valign="t",
                            width=78)
            table.reformat_column(0, width=20)
            table.reformat_column(1, width=10, align="r")
            table.reformat_column(2, width=48)
            for name in sorted(names):
                number = len(callbacks.get(name, []))
                lines = sum(
                    len(e["code"].splitlines())
                    for e in callbacks.get(name, []))
                no = "{} ({})".format(number, lines)
                description = types.get(name, (None, "Chained event."))[1]
                description = description.strip("\n").splitlines()[0]
                table.add_row(name, no, description)

            self.msg(unicode(table))
Пример #5
0
    def func(self):
        caller = self.caller
        args = self.args.strip()
        swap = any(s.startswith('s') for s in self.switches)

        if args:
            if hasattr(self, "item"):
                obj = self.item
                del self.item
            else:
                obj = caller.search(
                    args,
                    candidates=caller.contents,
                    nofound_string=_INVENTORY_ERRMSG.format(args))

            if obj:
                if hasattr(self, "action"):
                    action = self.action
                    del self.action
                else:
                    if any(isinstance(obj, i) for i in (Weapon, Shield)):
                        action = 'wield'
                    elif any(
                            isinstance(obj, i)
                            for i in (Torso, Helm, Boots, Gloves, Necklace,
                                      Bracers, Belt, Ring)):
                        action = 'wear'
                    else:
                        caller.msg("You can't equip {}.".format(
                            obj.get_display_name(caller)))

                if not obj.access(caller, 'equip'):
                    caller.msg("You can't {} {}.".format(
                        action, obj.get_display_name(caller)))
                    return

                if obj in caller.equip:
                    caller.msg("You're already {}ing {}.".format(
                        action, obj.get_display_name(caller)))
                    return

                # check whether slots are occupied
                occupied_slots = [
                    caller.equip.get(s) for s in obj.db.slots
                    if caller.equip.get(s)
                ]
                if obj.db.multi_slot:
                    if len(occupied_slots) > 0:
                        if swap:
                            for item in occupied_slots:
                                caller.equip.remove(item)
                        else:
                            caller.msg("You can't {} {}. ".format(
                                action, obj.get_display_name(caller)) +
                                       "You already have something there.")
                            return
                else:
                    if len(occupied_slots) == len(obj.db.slots):
                        if swap:
                            caller.equip.remove(occupied_slots[0])
                        else:
                            caller.msg("You can't {} {}. ".format(
                                action, obj.get_display_name(caller)) +
                                       "You have no open {} slot{}.".format(
                                           ", or ".join(obj.db.slots), "s"
                                           if len(obj.db.slots) != 1 else ""))
                            return

                if not caller.equip.add(obj):
                    caller.msg("You can't {} {}.".format(
                        action, obj.get_display_name(caller)))
                    return

                # call hook
                if hasattr(obj, "at_equip"):
                    obj.at_equip(caller)

                caller.msg("You {} {}.".format(action,
                                               obj.get_display_name(caller)))
                caller.location.msg_contents("{actor} {action}s {obj}.",
                                             mapping=dict(actor=caller,
                                                          obj=obj,
                                                          action=action),
                                             exclude=caller)
        else:
            # no arguments; display current equip
            data = []
            s_width = max(len(s) for s in caller.equip.slots)
            for slot, item in caller.equip:
                if not item or not item.access(caller, 'view'):
                    continue
                stat = " "
                if item.attributes.has('damage_roll'):
                    stat += "(|rDamage: {:>2d}|n) ".format(item.db.damage_roll)
                if item.attributes.has('physical_bonus'):
                    stat += "(|yPhysical bonus: {:>2d}|n)".format(
                        item.db.physical_bonus)
                if item.attributes.has('magical_bonus'):
                    stat += "(|yMagical bonus: {:>2d}|n)".format(
                        item.db.magical_bonus)
                if item.attributes.has('range'):
                    stat += "(|G{}|n) ".format(item.db.range.capitalize())

                data.append(
                    "  |b{slot:>{swidth}.{swidth}}|n: {item:<20.20} {stat}".
                    format(
                        slot=slot.capitalize(),
                        swidth=s_width,
                        item=item.name,
                        stat=stat,
                    ))
            if len(data) <= 0:
                output = "You have nothing in your equipment."
            else:
                table = EvTable(header=False, border=None, table=[data])
                output = "|YYour equipment:|n\n{}".format(table)

            caller.msg(output)
Пример #6
0
    def func(self):
        """Show server time data in a table."""
        lat, lon, ele = 33.43, -112.07, 24
        here = self.character.location
        if here:
            x = float(here.tags.get(category="coordx", default=0))
            y = float(here.tags.get(category="coordy", default=0))
            # z = here.tags.get(category="coordz")
            if x and y:
                lat, lon = float(y / 10000), float(x / 10000)
                print('Using location coordinates: {}, {}'.format(lat, lon))

        place = Astral.Location(info=('', '', lat, lon, 'UTC', ele))
        place.solar_depression = 'civil'

        def time_dif(at, when):
            diff = abs(at - when)
            return 'now' if diff.total_seconds < 60 else (
                utils.time_format(diff.total_seconds(), 2) +
                (' ago' if at > when else ''))

        def moon_phase(days):
            """
            Summarize the visible portion, given days into cycle
            Args:
                days (int or float): days into current cycle
            Returns:
                phase (str): summary of view of visible portion
            """
            phases = ('new', 'waxing crescent', 'First quarter',
                      'waxing gibbous', 'full', 'waning gibbous',
                      'last quarter', 'waning crescent')
            percent = float((float(days) + 0.5) / 29.53)
            phase = int((percent - int(percent)) * len(phases))
            return phases[phase]

        try:
            sun = place.sun(date=datetime.date.today(), local=True)
        except Exception:
            return
        else:
            past = here.tags.get('past', category='realm')
            moon = place.moon_phase(date=datetime.date.today())
            now = timezone.now()
            moment = ['dawn', 'sunrise', 'noon', 'sunset', 'dusk']
            events = zip([each.capitalize() + ':' for each in moment],
                         [time_dif(now, sun[each]) for each in moment])
            table1 = EvTable("|wServer", '|wtime', align='l', width=75)
            table1.add_row('Current uptime',
                           utils.time_format(gametime.uptime(), 3))
            table1.add_row(
                'First start',
                time_dif(
                    datetime.datetime.now(),
                    datetime.datetime.fromtimestamp(gametime.server_epoch())))
            if here.tags.get('past', category='realm'):
                table1.add_row('Moon phase', moon_phase(moon))
            table1.reformat_column(0, width=20)
            if past:
                table2 = EvTable("|wEvent",
                                 "|wTime until",
                                 align="l",
                                 width=75)
                for entry in events:
                    table2.add_row(entry[0], entry[1])
                table2.reformat_column(0, width=20)
            if self.cmdstring == 'uptime':
                self.msg('Current uptime: ' +
                         utils.time_format(gametime.uptime(), 3))
            else:
                self.msg(unicode(table1))
            if past:
                if self.cmdstring == 'events':
                    self.msg(unicode(table2))
                else:
                    self.msg("\n" + unicode(table2))
Пример #7
0
    def func(self):
        """Show list."""

        global _IDMAPPER
        if not _IDMAPPER:
            from evennia.utils.idmapper import models as _IDMAPPER

        if "flushmem" in self.switches:
            # flush the cache
            prev, _ = _IDMAPPER.cache_size()
            nflushed = _IDMAPPER.flush_cache()
            now, _ = _IDMAPPER.cache_size()
            string = "The Idmapper cache freed |w{idmapper}|n database objects.\n" \
                     "The Python garbage collector freed |w{gc}|n Python instances total."
            self.caller.msg(string.format(idmapper=(prev - now), gc=nflushed))
            return

        # display active processes

        os_windows = os.name == "nt"
        pid = os.getpid()

        if os_windows:
            # Windows requires the psutil module to even get paltry
            # statistics like this (it's pretty much worthless,
            # unfortunately, since it's not specific to the process) /rant
            try:
                import psutil
                has_psutil = True
            except ImportError:
                has_psutil = False

            if has_psutil:
                loadavg = psutil.cpu_percent()
                _mem = psutil.virtual_memory()
                rmem = _mem.used / (1000.0 * 1000)
                pmem = _mem.percent

                if "mem" in self.switches:
                    string = "Total computer memory usage: |w%g|n MB (%g%%)"
                    self.caller.msg(string % (rmem, pmem))
                    return
                # Display table
                loadtable = EvTable("property", "statistic", align="l")
                loadtable.add_row("Total CPU load", "%g %%" % loadavg)
                loadtable.add_row("Total computer memory usage", "%g MB (%g%%)" % (rmem, pmem))
                loadtable.add_row("Process ID", "%g" % pid),
            else:
                loadtable = "Not available on Windows without 'psutil' library " \
                            "(install with |wpip install psutil|n)."

        else:
            # Linux / BSD (OSX) - proper pid-based statistics

            global _RESOURCE
            if not _RESOURCE:
                import resource as _RESOURCE

            loadavg = os.getloadavg()[0]
            rmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "rss")).read()) / 1000.0  # resident memory
            vmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "vsz")).read()) / 1000.0  # virtual memory
            pmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "%mem")).read())  # % of resident memory to total
            rusage = _RESOURCE.getrusage(_RESOURCE.RUSAGE_SELF)

            if "mem" in self.switches:
                string = "Memory usage: RMEM: |w%g|n MB (%g%%), VMEM (res+swap+cache): |w%g|n MB."
                self.caller.msg(string % (rmem, pmem, vmem))
                return

            loadtable = EvTable("property", "statistic", align="l")
            loadtable.add_row("Server load (1 min)", "%g" % loadavg)
            loadtable.add_row("Process ID", "%g" % pid),
            loadtable.add_row("Memory usage", "%g MB (%g%%)" % (rmem, pmem))
            loadtable.add_row("Virtual address space", "")
            loadtable.add_row("|x(resident+swap+caching)|n", "%g MB" % vmem)
            loadtable.add_row("CPU time used (total)", "%s (%gs)"
                              % (utils.time_format(rusage.ru_utime), rusage.ru_utime))
            loadtable.add_row("CPU time used (user)", "%s (%gs)"
                              % (utils.time_format(rusage.ru_stime), rusage.ru_stime))
            loadtable.add_row("Page faults", "%g hard,  %g soft, %g swapouts"
                              % (rusage.ru_majflt, rusage.ru_minflt, rusage.ru_nswap))
            loadtable.add_row("Disk I/O", "%g reads, %g writes" % (rusage.ru_inblock, rusage.ru_oublock))
            loadtable.add_row("Network I/O", "%g in, %g out" % (rusage.ru_msgrcv, rusage.ru_msgsnd))
            loadtable.add_row("Context switching", "%g vol, %g forced, %g signals"
                              % (rusage.ru_nvcsw, rusage.ru_nivcsw, rusage.ru_nsignals))

        # os-generic

        string = "|wServer CPU and Memory load:|n\n%s" % loadtable

        # object cache count (note that sys.getsiseof is not called so this works for pypy too.
        total_num, cachedict = _IDMAPPER.cache_size()
        sorted_cache = sorted([(key, num) for key, num in cachedict.items() if num > 0],
                              key=lambda tup: tup[1], reverse=True)
        memtable = EvTable("entity name", "number", "idmapper %", align="l")
        for tup in sorted_cache:
            memtable.add_row(tup[0], "%i" % tup[1], "%.2f" % (float(tup[1]) / total_num * 100))

        string += "\n|w Entity idmapper cache:|n %i items\n%s" % (total_num, memtable)

        # return to caller
        self.caller.msg(string)
Пример #8
0
    def func(self):
        caller = self.caller

        if not self.args:
            self._usage()
            return

        # split the target off from the first list argument
        targs = tuple(self.lhslist[0].rsplit(' ', 1))
        if len(targs) == 1 or \
                (len(targs) > 1 and targs[1].lower() not in ALL_SKILLS):
            target, self.lhslist[0] = self.lhslist[0], ''
        else:
            target, self.lhslist[0] = targs

        char = caller.search(target,
                             location=caller.location,
                             typeclass='typeclasses.characters.NPC')

        if not char:
            return

        # the inherits_from check below is necessary due to an issue
        # with search() on the ContripRPObject class
        if not inherits_from(char, 'typeclasses.characters.NPC'):
            caller.msg("Could not find NPC: '{}'".format(target))
            return

        if self.rhs:  # handle assignments
            if not all((x.isdigit() for x in self.rhslist)):
                caller.msg('Assignment values must be numeric.')
                return
            if len(self.lhslist) != len(self.rhslist):
                caller.msg('Incorrect number of assignment values.')
                return
            for i in xrange(len(self.lhslist)):
                if self.lhslist[i].lower() in char.skills.all:
                    char.skills[self.lhslist[i].lower()].base = \
                        min(max(int(self.rhslist[i]), 0), 10)  # enforce {0, 10} bounds
                    caller.msg('Skill "{}" set to {} for {}'.format(
                        self.lhslist[i].lower(),
                        min(max(int(self.rhslist[i]), 0), 10),
                        char.sdesc.get()))
                else:
                    caller.msg('Invalid skill: "{}"'.format(self.lhslist[i]))

        # display traits
        data = []
        if any(self.lhslist):
            skills = [s.lower() for s in self.lhslist
                      if s.lower() in char.skills.all]
        else:
            skills = char.skills.all

        if len(skills) < 3:
            [data.append([self._format_skill_3col(char.skills[s])])
             for s in skills]
        else:
            [data.append([self._format_skill_3col(char.skills[s])
                          for s in skills[i::3]])
             for i in xrange(3)]
        table = EvTable(header=False, table=data)
        caller.msg(unicode(table))
Пример #9
0
    def _parse_rectangles(self, cellchar, tablechar, form, **kwargs):
        """
        Parse a form for rectangular formfields identified by formchar
        enclosing an identifier.

        """

        # update options given at creation with new input - this
        # allows e.g. self.map() to add custom settings for individual
        # cells/tables
        custom_options = copy.copy(self.options)
        custom_options.update(kwargs)

        nform = len(form)

        mapping = {}
        cell_coords = {}
        table_coords = {}

        # Locate the identifier tags and the horizontal end coords for all forms
        re_cellchar = re.compile(
            r"%s+([^%s%s])%s+" %
            (cellchar, INVALID_FORMCHARS, cellchar, cellchar))
        re_tablechar = re.compile(
            r"%s+([^%s%s|])%s+" %
            (tablechar, INVALID_FORMCHARS, tablechar, tablechar))
        for iy, line in enumerate(_to_ansi(form, regexable=True)):
            # find cells
            ix0 = 0
            while True:
                match = re_cellchar.search(line, ix0)
                if match:
                    # get the width of the rectangle directly from the match
                    cell_coords[match.group(1)] = [
                        iy, match.start(), match.end()
                    ]
                    ix0 = match.end()
                else:
                    break
            # find tables
            ix0 = 0
            while True:
                match = re_tablechar.search(line, ix0)
                if match:
                    # get the width of the rectangle directly from the match
                    table_coords[match.group(1)] = [
                        iy, match.start(), match.end()
                    ]
                    ix0 = match.end()
                else:
                    break
        #print "cell_coords:", cell_coords
        #print "table_coords:", table_coords

        # get rectangles and assign EvCells
        for key, (iy, leftix, rightix) in cell_coords.items():

            # scan up to find top of rectangle
            dy_up = 0
            if iy > 0:
                for i in range(1, iy):
                    #print "dy_up:", [form[iy-i][ix] for ix in range(leftix, rightix)]
                    if all(form[iy - i][ix] == cellchar
                           for ix in range(leftix, rightix)):
                        dy_up += 1
                    else:
                        break
            # find bottom edge of rectangle
            dy_down = 0
            if iy < nform - 1:
                for i in range(1, nform - iy - 1):
                    #print "dy_down:", [form[iy+i][ix]for ix in range(leftix, rightix)]
                    if all(form[iy + i][ix] == cellchar
                           for ix in range(leftix, rightix)):
                        dy_down += 1
                    else:
                        break

            #  we have our rectangle. Calculate size of EvCell.
            iyup = iy - dy_up
            iydown = iy + dy_down
            width = rightix - leftix
            height = abs(iyup - iydown) + 1

            # we have all the coordinates we need. Create EvCell.
            data = self.cells_mapping.get(key, "")
            #if key == "1":
            #    print "creating cell '%s' (%s):" % (key, data)
            #    print "iy=%s, iyup=%s, iydown=%s, leftix=%s, rightix=%s, width=%s, height=%s" % (iy, iyup, iydown, leftix, rightix, width, height)

            options = {
                "pad_left": 0,
                "pad_right": 0,
                "pad_top": 0,
                "pad_bottom": 0,
                "align": "l",
                "valign": "t",
                "enforce_size": True
            }
            options.update(custom_options)
            #if key=="4":
            #print "options:", options

            mapping[key] = (iyup, leftix, width, height,
                            EvCell(data, width=width, height=height,
                                   **options))

        # get rectangles and assign Tables
        for key, (iy, leftix, rightix) in table_coords.items():

            # scan up to find top of rectangle
            dy_up = 0
            if iy > 0:
                for i in range(1, iy):
                    #print "dy_up:", [form[iy-i][ix] for ix in range(leftix, rightix)]
                    if all(form[iy - i][ix] == tablechar
                           for ix in range(leftix, rightix)):
                        dy_up += 1
                    else:
                        break
            # find bottom edge of rectangle
            dy_down = 0
            if iy < nform - 1:
                for i in range(1, nform - iy - 1):
                    #print "dy_down:", [form[iy+i][ix]for ix in range(leftix, rightix)]
                    if all(form[iy + i][ix] == tablechar
                           for ix in range(leftix, rightix)):
                        dy_down += 1
                    else:
                        break

            #  we have our rectangle. Calculate size of Table.
            iyup = iy - dy_up
            iydown = iy + dy_down
            width = rightix - leftix
            height = abs(iyup - iydown) + 1

            # we have all the coordinates we need. Create Table.
            table = self.tables_mapping.get(key, None)
            #print "creating table '%s' (%s):" % (key, data)
            #print "iy=%s, iyup=%s, iydown=%s, leftix=%s, rightix=%s, width=%s, height=%s" % (iy, iyup, iydown, leftix, rightix, width, height)

            options = {
                "pad_left": 0,
                "pad_right": 0,
                "pad_top": 0,
                "pad_bottom": 0,
                "align": "l",
                "valign": "t",
                "enforce_size": True
            }
            options.update(custom_options)
            #print "options:", options

            if table:
                table.reformat(width=width, height=height, **options)
            else:
                table = EvTable(width=width, height=height, **options)
            mapping[key] = (iyup, leftix, width, height, table)

        return mapping
Пример #10
0
    def func(self):
        """Setup the irc-channel mapping"""

        if not settings.IRC_ENABLED:
            string = "IRC is not enabled. Activate it in game/settings.py."
            self.msg(string)
            return

        # If no args: list bots.
        if not self.args:
            # show all connections
            ircbots = [
                bot for bot in AccountDB.objects.filter(
                    db_is_bot=True, username__startswith="ircbot-")
            ]
            if ircbots:
                from evennia.utils.evtable import EvTable
                table = EvTable("|w#dbref|n",
                                "|wbotname|n",
                                "|wev-channel/location|n",
                                "|wirc-channel|n",
                                "|wSSL|n",
                                maxwidth=_DEFAULT_WIDTH)
                for ircbot in ircbots:
                    ircinfo = "%s (%s:%s)" % (ircbot.db.irc_channel,
                                              ircbot.db.irc_network,
                                              ircbot.db.irc_port)
                    table.add_row(
                        "#%i" % ircbot.id, ircbot.db.irc_botname,
                        ircbot.attributes.get("ev_channel",
                                              ircbot.db.ev_location.key),
                        ircinfo, ircbot.db.irc_ssl)
                self.msg(table)
                self.msg("Use 'help @puppetbot' for more infomation.")
            else:
                self.msg("No irc bots found.")
            return

        # Switch options available only if valid bot is given.
        if self.switches:
            botname = "ircbot-%s" % self.lhs
            matches = AccountDB.objects.filter(db_is_bot=True,
                                               username=botname)
            dbref = utils.dbref(self.lhs)
            if not matches and dbref:
                # try dbref match
                matches = AccountDB.objects.filter(db_is_bot=True, id=dbref)
            if not matches:
                self.msg("No valid bot given. Consult 'help @puppetbot'")
                return

            # Puppetbot/delete <bot> - Delete bot.
            if any(i in ['disconnect', 'remove', 'delete']
                   for i in self.switches):
                matches[0].delete()
                self.msg("IRC link/bot destroyed.")
                return

            # Puppetbot/ping <bot> - ping bot.
            if "ping" in self.switches:
                matches[0].ping(self.caller)
                self.msg("Pinging " + self.lhs)
                return

            # Puppetbot/about <bot> = msg - Set bot about message.
            if "about" in self.switches:
                if self.rhs:
                    matches[0].db.botdesc = self.rhs
                    self.msg("Bot about message changed to: " + self.rhs)
                else:
                    self.msg("No message given. 'About' desc change aborted.")
                return

            # Puppetbot/who <bot> - Get IRC user list..
            if "who" in self.switches:
                # retrieve user list. The bot must handles the echo since it's
                # an asynchronous call.
                self.caller.msg(
                    "Requesting nicklist from %s (%s:%s)." %
                    (matches[0].db.irc_channel, matches[0].db.irc_network,
                     matches[0].db.irc_port))
                matches[0].get_nicklist(self.caller)
                return

            # Puppetbot/reconnect <bot> - reconnect bot.
            if "reconnect" in self.switches:
                matches[0].reconnect()
                self.msg("Reconnecting " + self.lhs)
                return

            # Puppetbot/reload <bot> - Delete all bots, recreates bots from new user list.
            if "reload" in self.switches:
                matches[0].db.ev_location.msg_contents(
                    "Puppet reload in progress.")
                puppetlist = [
                    puppet
                    for puppet in search.search_tag(matches[0].key + "-puppet")
                ]
                for puppet in puppetlist:
                    puppet.delete()
                matches[0].get_nicklist()
                return

            # Puppetbot/ignore <bot> = puppet - Toggle ignore IRC user.
            if "ignore" in self.switches:
                if self.rhs:
                    user = self.rhs.strip()
                    # If already ignored, toggle off.
                    if user in matches[0].db.userignorelist:
                        matches[0].db.userignorelist.remove(user)
                        matches[0].get_nicklist()
                        return

                    # Else ignore user.
                    else:
                        matches[0].db.userignorelist.append(user)
                        if user in matches[0].db.puppetdict:
                            matches[0].db.puppetdict[user].delete()
                            del matches[0].db.puppetdict[user]
                        return
                else:
                    self.msg("Usage: Puppetbot/ignore <bot> = <puppet>")
                    return

            # Puppetbot/entrymsg <bot> = msg - Set default puppet creation message.
            if "entrymsg" in self.switches:
                if self.rhs:
                    matches[0].db.puppetentrymsg = " " + self.rhs
                    self.msg("Bot entry message changed to: " + " " + self.rhs)
                else:
                    self.msg("No message given. Message change aborted.")
                return

            # Puppetbot/exitmsg <bot> = msg - Set default puppet deletion message.
            if "exitmsg" in self.switches:
                if self.rhs:
                    matches[0].db.puppetexitmsg = " " + self.rhs
                    self.msg("Bot exit message changed to: " + " " + self.rhs)
                else:
                    self.msg("No message given. Message change aborted.")
                return

            # Puppetbot/prefix <bot> = msg - Set string put before username in puppet.key
            if "prefix" in self.switches:
                if self.rhs:
                    matches[0].db.puppetprefix = self.rhs
                    self.msg("Puppet prefix changed to: " + self.rhs)
                    self.msg(
                        "Use: '@puppetbot/reload <bot>' to implement changes.")
                else:
                    self.msg("No message given. Prefix change aborted.")
                return

            # Puppetbot/suffix <bot> = msg - Set string put after username in puppet.key
            if "suffix" in self.switches:
                if self.rhs:
                    matches[0].db.puppetsuffix = self.rhs
                    self.msg("Puppet suffix changed to: " + self.rhs)
                    self.msg(
                        "Use: '@puppetbot/reload <bot>' to implement changes.")
                else:
                    self.msg("No message given. Suffix change aborted.")
                return

            # Puppetbot/defaultdesc <bot> = msg - Set default puppet desc message.
            if "defaultdesc" in self.switches:
                if self.rhs:
                    matches[0].db.puppetlastdesc = matches[
                        0].db.puppetdefaultdesc
                    matches[0].db.puppetdefaultdesc = self.rhs
                    self.msg("Default puppet description changed to: " +
                             self.rhs)
                else:
                    self.msg("No message given. Message change aborted.")
                return

            # Puppetbot/softdesc <bot> = msg - Only changes non custom puppet descriptions to new default.
            if "softdesc" in self.switches:
                puppetlist = [
                    puppet
                    for puppet in search.search_tag(matches[0].key + "-puppet")
                ]
                for puppet in puppetlist:
                    if puppet.db.desc == matches[0].db.puppetlastdesc:
                        puppet.db.desc = matches[0].db.puppetdefaultdesc
                self.msg("Puppets description changed to: " +
                         matches[0].db.puppetdefaultdesc)
                return

            # Puppetbot/forcedesc <bot> = msg - Changes all puppet descriptions to new default.
            if "forcedesc" in self.switches:
                puppetlist = [
                    puppet
                    for puppet in search.search_tag(matches[0].key + "-puppet")
                ]
                for puppet in puppetlist:
                    puppet.db.desc = matches[0].db.puppetdefaultdesc
                self.msg("Puppets description changed to: " +
                         matches[0].db.puppetdefaultdesc)
                return

        # Create Bot.
        location = self.caller.location
        self.args = self.args.replace('#', ' ')  # Avoid Python comment issues
        try:
            irc_network, irc_port, irc_channel, irc_botname = \
                       [part.strip() for part in self.args.split(None, 4)]
            irc_channel = "#%s" % irc_channel
        except Exception:
            string = "IRC bot definition '%s' is not valid." % self.args
            self.msg(string)
            return

        botname = "ircbot-%s" % irc_botname
        # create a new bot
        bot = AccountDB.objects.filter(username__iexact=botname)
        if bot:
            # re-use an existing bot
            bot = bot[0]
            if not bot.is_bot:
                self.msg("'%s' already exists and is not a bot." % botname)
                return
        else:
            try:
                bot = create.create_account(botname,
                                            None,
                                            None,
                                            typeclass=ServerBot)
            except Exception as err:
                self.msg("|rError, could not create the bot:|n '%s'." % err)
                return
        bot.start(ev_location=location,
                  irc_botname=irc_botname,
                  irc_channel=irc_channel,
                  irc_network=irc_network,
                  irc_port=irc_port)
        self.msg("Connection created. Starting IRC bot.")
Пример #11
0
    def func(self):
        caller = self.caller

        if not self.args:
            self._usage()
            return

        # split off the target from the first item of lhslist
        targs = tuple(self.lhslist[0].rsplit(' ', 1))
        if len(targs) == 1 or \
                (len(targs) > 1 and targs[1].upper() not in ALL_TRAITS):
            target, self.lhslist[0] = self.lhslist[0], ''
        else:
            target, self.lhslist[0] = targs

        # search for the target NPC
        char = caller.search(target,
                             location=caller.location,
                             typeclass='typeclasses.characters.NPC')

        if not char:
            return

        # the inherits_from check below is necessary due to an issue
        # with search() on the ContripRPObject class
        if not inherits_from(char, 'typeclasses.characters.NPC'):
            caller.msg("Could not find NPC: '{}'".format(target))
            return

        if self.rhs:
            if not all((x.isdigit() for x in self.rhslist)):
                caller.msg('Assignment values must be numeric.')
                return
            if len(self.lhslist) != len(self.rhslist):
                caller.msg('Incorrect number of assignment values.')
                return
            for i in xrange(len(self.lhslist)):
                if self.lhslist[i].upper() in char.traits.all:
                    char.traits[self.lhslist[i].upper()].base = \
                        min(max(int(self.rhslist[i]), 0), 10)
                    caller.msg('Trait "{}" set to {} for {}'.format(
                        self.lhslist[i].upper(),
                        min(max(int(self.rhslist[i]), 0), 10),
                        char.sdesc.get()))
                else:
                    caller.msg('Invalid trait: "{}"'.format(self.lhslist[i]))

        # display traits
        data = []
        if any(self.lhslist):
            traits = [t.upper() for t in self.lhslist
                      if t.upper() in char.traits.all]
        else:
            traits = char.traits.all

        if len(traits) == 0:
            return
        elif 0 < len(traits) < 3:
            [data.append([self._format_trait_3col(char.traits[t])])
             for t in traits]
        else:
            [data.append([self._format_trait_3col(char.traits[t])
                          for t in traits[i::3]])
             for i in xrange(3)]
        table = EvTable(header=False, table=data)
        caller.msg(unicode(table))
Пример #12
0
    def func(self):
        """List the accounts"""

        caller = self.caller
        args = self.args

        if "delete" in self.switches:
            account = getattr(caller, "account")
            if not account or not account.check_permstring("Developer"):
                caller.msg("You are not allowed to delete accounts.")
                return
            if not args:
                caller.msg("Usage: @accounts/delete <name or #id> [: reason]")
                return
            reason = ""
            if ":" in args:
                args, reason = [arg.strip() for arg in args.split(":", 1)]
            # We use account_search since we want to be sure to find also accounts
            # that lack characters.
            accounts = search.account_search(args)
            if not accounts:
                self.msg("Could not find an account by that name.")
                return
            if len(accounts) > 1:
                string = "There were multiple matches:\n"
                string += "\n".join(" %s %s" % (account.id, account.key)
                                    for account in accounts)
                self.msg(string)
                return
            account = accounts.first()
            if not account.access(caller, "delete"):
                self.msg(
                    "You don't have the permissions to delete that account.")
                return
            username = account.username
            # ask for confirmation
            confirm = (
                "It is often better to block access to an account rather than to delete it. "
                "|yAre you sure you want to permanently delete "
                "account '|n{}|y'|n yes/[no]?".format(username))
            answer = yield (confirm)
            if answer.lower() not in ('y', 'yes'):
                caller.msg("Canceled deletion.")
                return

            # Boot the account then delete it.
            self.msg("Informing and disconnecting account ...")
            string = "\nYour account '%s' is being *permanently* deleted.\n" % username
            if reason:
                string += " Reason given:\n  '%s'" % reason
            account.msg(string)
            logger.log_sec(
                "Account Deleted: %s (Reason: %s, Caller: %s, IP: %s)." %
                (account, reason, caller, self.session.address))
            account.delete()
            self.msg("Account %s was successfully deleted." % username)
            return

        # No switches, default to displaying a list of accounts.
        if self.args and self.args.isdigit():
            nlim = int(self.args)
        else:
            nlim = 10

        naccounts = AccountDB.objects.count()

        # typeclass table
        dbtotals = AccountDB.objects.object_totals()
        typetable = EvTable("|wtypeclass|n",
                            "|wcount|n",
                            "|w%%|n",
                            border="cells",
                            align="l")
        for path, count in dbtotals.items():
            typetable.add_row(path, count,
                              "%.2f" % ((float(count) / naccounts) * 100))
        # last N table
        plyrs = AccountDB.objects.all().order_by(
            "db_date_created")[max(0, naccounts - nlim):]
        latesttable = EvTable("|wcreated|n",
                              "|wdbref|n",
                              "|wname|n",
                              "|wtypeclass|n",
                              border="cells",
                              align="l")
        for ply in plyrs:
            latesttable.add_row(utils.datetime_format(ply.date_created),
                                ply.dbref, ply.key, ply.path)

        string = "\n|wAccount typeclass distribution:|n\n%s" % typetable
        string += "\n|wLast %s Accounts created:|n\n%s" % (min(
            naccounts, nlim), latesttable)
        caller.msg(string)
Пример #13
0
def evtable_options_formatter(optionlist):
    if not optionlist:
        return ""

    # column separation distance
    colsep = 4

    nlist = len(optionlist)

    # get the widest option line in the table.
    table_width_max = -1
    table = []
    for key, desc in optionlist:
        if not (key or desc):
            continue
        table_width_max = max(
            table_width_max,
            max(m_len(p) for p in key.split("\n")) +
            max(m_len(p) for p in desc.split("\n")) + colsep)
        raw_key = strip_ansi(key)
        if raw_key != key:
            # already decorations in key definition
            table.append(
                ANSIString(" {lc%s{lt%s{le: %s" % (raw_key, key, desc)))
        else:
            # add a default white color to key
            table.append(
                ANSIString(" {lc%s{lt{w%s{n{le: %s" %
                           (raw_key, raw_key, desc)))

    ncols = (_MAX_TEXT_WIDTH // table_width_max) + 1  # number of ncols
    nlastcol = nlist % ncols  # number of elements left in last row

    # get the amount of rows needed (start with 4 rows)
    nrows = 4
    while nrows * ncols < nlist:
        nrows += 1
    ncols = nlist // nrows  # number of full columns
    nlastcol = nlist % nrows  # number of elements in last column

    # get the final column count
    ncols = ncols + 1 if nlastcol > 0 else ncols
    if ncols > 1:
        # only extend if longer than one column
        table.extend([" " for i in range(nrows - nlastcol)])

    # build the actual table grid
    table = [
        table[icol * nrows:(icol * nrows) + nrows] for icol in range(0, ncols)
    ]

    # adjust the width of each column
    for icol in range(len(table)):
        col_width = max(
            max(m_len(p) for p in part.split("\n"))
            for part in table[icol]) + colsep
        table[icol] = [
            pad(part, width=col_width + colsep, align="l")
            for part in table[icol]
        ]

    # format the table into columns
    return unicode(EvTable(table=table, border="none"))
Пример #14
0
    def _format_node(self, nodetext, optionlist):
        """
        Format the node text + option section

        Args:
            nodetext (str): The node text
            optionlist (list): List of (key, desc) pairs.

        Returns:
            string (str): The options section, including
                all needed spaces.

        Notes:
            This will adjust the columns of the options, first to use
            a maxiumum of 4 rows (expanding in columns), then gradually
            growing to make use of the screen space.

        """
        #
        # handle the node text
        #

        nodetext = dedent(nodetext).strip()

        nodetext_width_max = max(m_len(line) for line in nodetext.split("\n"))

        if not optionlist:
            # return the node text "naked".
            separator1 = "_" * nodetext_width_max + "\n\n" if nodetext_width_max else ""
            separator2 = "\n" if nodetext_width_max else "" + "_" * nodetext_width_max
            return separator1 + nodetext + separator2

        #
        # handle the options
        #

        # column separation distance
        colsep = 4

        nlist = len(optionlist)

        # get the widest option line in the table.
        table_width_max = -1
        table = []
        for key, desc in optionlist:
            table_width_max = max(
                table_width_max,
                max(m_len(p) for p in key.split("\n")) +
                max(m_len(p) for p in desc.split("\n")) + colsep)
            raw_key = strip_ansi(key)
            if raw_key != key:
                # already decorations in key definition
                table.append(
                    ANSIString(" {lc%s{lt%s{le: %s" % (raw_key, key, desc)))
            else:
                # add a default white color to key
                table.append(
                    ANSIString(" {lc%s{lt{w%s{n{le: %s" %
                               (raw_key, raw_key, desc)))

        ncols = (_MAX_TEXT_WIDTH // table_width_max) + 1  # number of ncols
        nlastcol = nlist % ncols  # number of elements left in last row

        # get the amount of rows needed (start with 4 rows)
        nrows = 4
        while nrows * ncols < nlist:
            nrows += 1
        ncols = nlist // nrows  # number of full columns
        nlastcol = nlist % nrows  # number of elements in last column

        # get the final column count
        ncols = ncols + 1 if nlastcol > 0 else ncols
        if ncols > 1:
            # only extend if longer than one column
            table.extend([" " for i in xrange(nrows - nlastcol)])

        # build the actual table grid
        table = [
            table[icol * nrows:(icol * nrows) + nrows]
            for icol in xrange(0, ncols)
        ]

        # adjust the width of each column
        total_width = 0
        for icol in xrange(len(table)):
            col_width = max(
                max(m_len(p) for p in part.split("\n"))
                for part in table[icol]) + colsep
            table[icol] = [
                pad(part, width=col_width + colsep, align="l")
                for part in table[icol]
            ]
            total_width += col_width

        # format the table into columns
        table = EvTable(table=table, border="none")

        # build the page
        total_width = max(total_width, nodetext_width_max)
        separator1 = "_" * total_width + "\n\n" if nodetext_width_max else ""
        separator2 = "\n" + "_" * total_width + "\n\n" if total_width else ""
        return separator1 + nodetext + separator2 + unicode(table)
Пример #15
0
    def options_formatter(self, optionlist):
        """
        Formats the option block.

        Args:
            optionlist (list): List of (key, description) tuples for every
                option related to this node.
            caller (Object, Account or None, optional): The caller of the node.

        Returns:
            options (str): The formatted option display.

        """
        if not optionlist:
            return ""

        # column separation distance
        colsep = 4

        nlist = len(optionlist)

        # get the widest option line in the table.
        table_width_max = -1
        table = []
        for key, desc in optionlist:
            if not (key or desc):
                continue
            table_width_max = max(table_width_max,
                                  max(m_len(p) for p in key.split("\n")) +
                                  max(m_len(p) for p in desc.split("\n")) + colsep)
            raw_key = strip_ansi(key)
            if raw_key != key:
                # already decorations in key definition
                table.append(" |lc%s|lt%s|le: %s" % (raw_key, key, desc))
            else:
                # add a default white color to key
                table.append(" |lc%s|lt|w%s|n|le: %s" % (raw_key, raw_key, desc))

        ncols = (_MAX_TEXT_WIDTH // table_width_max) + 1  # number of ncols

        # get the amount of rows needed (start with 4 rows)
        nrows = 4
        while nrows * ncols < nlist:
            nrows += 1
        ncols = nlist // nrows  # number of full columns
        nlastcol = nlist % nrows  # number of elements in last column

        # get the final column count
        ncols = ncols + 1 if nlastcol > 0 else ncols
        if ncols > 1:
            # only extend if longer than one column
            table.extend([" " for i in range(nrows - nlastcol)])

        # build the actual table grid
        table = [table[icol * nrows: (icol * nrows) + nrows] for icol in range(0, ncols)]

        # adjust the width of each column
        for icol in range(len(table)):
            col_width = max(max(m_len(p) for p in part.split("\n")) for part in table[icol]) + colsep
            table[icol] = [pad(part, width=col_width + colsep, align="l") for part in table[icol]]

        # format the table into columns
        return unicode(EvTable(table=table, border="none"))
Пример #16
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
Пример #17
0
def menunode_allocate_skills(caller, raw_string):
    """Skill -1 counter allocation menu node."""
    char = caller.new_char
    sk = char.skills
    total = 3
    counts = {1: 'one', 2: 'two', 3: 'three'}

    plusses = (ceil(char.traits.INT.actual / 3.0) -
               sum(sk[s].plus for s in skills.ALL_SKILLS))
    minuses = total - sum(sk[s].minus for s in skills.ALL_SKILLS)

    text = ""
    raw_string = raw_string.strip()
    if raw_string.isdigit() and int(raw_string) <= len(skills.ALL_SKILLS):
        skill = sk[skills.ALL_SKILLS[int(raw_string) - 1]]
        if minuses:
            if skill.actual - skill.minus - 1 > 0:
                skill.minus += 1
                minuses -= 1
            else:
                text += "|rSkills cannot be reduced below one.|n\n"
        elif plusses:
            if skill.actual + skill.plus + 1 <= 10:
                skill.plus += 1
                plusses -= 1
            else:
                text += "|rSkills cannot be increased above ten.|n\n"

    if plusses or minuses:
        text += "{}\n\n".format(raw_string) if raw_string and raw_string[0] == 'F' else ""
        text += "Your ability to perform actions in Ainneve is\n"
        text += "tied to your character's skills.\n"

        if minuses:
            text += "Please allocate |w{}|n |m'-1'|n counter{}.".format(
                        counts[minuses],
                        's' if minuses != 1 else '')
        elif plusses:
            text += "Please allocate |w{}|n |m'+1'|n counter{}.".format(
                        counts[plusses],
                        's' if plusses != 1 else '')

        help = "Skill allocation is a two step process. First, you\n"
        help += "distribute three '-1' counters across your skills,\n"
        help += "then a number of '+1' counters equal to your Intelligence\n"
        help += "divided by 3."

        options = [{"desc": _format_skill_opts(sk[s]),
                    "goto": "menunode_allocate_skills"}
                   for s in skills.ALL_SKILLS]

        def clear_skills(s):
            """Reset plus and minus counters on all skills."""
            for skill in skills.ALL_SKILLS:
                sk[skill].plus = 0
                sk[skill].minus = 0

        options.append({"desc": "Start Over",
                        "exec": clear_skills,
                        "goto": "menunode_allocate_skills"})
        return (text, help), options
    else:
        skills.finalize_skills(char.skills)
        data = []
        for i in list(range(3)):
            data.append([_format_trait_opts(sk[s], color='|M')
                         for s in skills.ALL_SKILLS[i::3]])
        table = EvTable(header=False, table=data)
        output = "Final Skills:\n"
        output += "{skills}\n"

        char.db.wallet['SC'] = d_roll('2d6+3')
        output += "You begin with |w{sc} SC|n (Silver Coins)."

        return menunode_equipment_cats(
            caller,
            output.format(skills=table, sc=char.db.wallet['SC'])
        )
Пример #18
0
    def func(self):
        """Setup the irc-channel mapping"""

        if not settings.IRC_ENABLED:
            string = "IRC is not enabled. Activate it in game/settings.py."
            self.msg(string)
            return

        # If no args: list bots.
        if not self.args:
            # show all connections
            ircbots = [
                bot for bot in AccountDB.objects.filter(
                    db_is_bot=True, username__startswith="ircbot-")
            ]
            if ircbots:
                from evennia.utils.evtable import EvTable
                table = EvTable("|w#dbref|n",
                                "|wbotname|n",
                                "|wev-channel/location|n",
                                "|wirc-channel|n",
                                "|wSSL|n",
                                maxwidth=_DEFAULT_WIDTH)
                for ircbot in ircbots:
                    ircinfo = "%s (%s:%s)" % (ircbot.db.irc_channel,
                                              ircbot.db.irc_network,
                                              ircbot.db.irc_port)
                    table.add_row(
                        "#%i" % ircbot.id, ircbot.db.irc_botname,
                        ircbot.attributes.get("ev_channel",
                                              ircbot.db.ev_location.key),
                        ircinfo, ircbot.db.irc_ssl)
                self.msg(table)
                self.msg("Use 'help @puppetbot' for more infomation.")
            else:
                self.msg("No irc bots found.")
            return

        # Switch options available only if valid bot is given.
        if self.switches:
            botname = "ircbot-%s" % self.lhs
            matches = AccountDB.objects.filter(db_is_bot=True,
                                               username=botname)
            dbref = utils.dbref(self.lhs)
            if not matches and dbref:
                # try dbref match
                matches = AccountDB.objects.filter(db_is_bot=True, id=dbref)
            if not matches:
                self.msg("No valid bot given. Consult 'help @puppetbot'")
                return

            # Puppetbot/delete <bot> - Delete bot.
            if any(i in ['disconnect', 'remove', 'delete']
                   for i in self.switches):
                matches[0].delete()
                self.msg("IRC link/bot destroyed.")
                return

            # Puppetbot/ping <bot> - ping bot.
            if "ping" in self.switches:
                matches[0].ping(self.caller)
                self.msg("Pinging " + self.lhs)
                return

            # Puppetbot/who <bot> - Get IRC user list..
            if "who" in self.switches:
                # retrieve user list. The bot must handles the echo since it's
                # an asynchronous call.
                self.caller.msg(
                    "Requesting nicklist from %s (%s:%s)." %
                    (matches[0].db.irc_channel, matches[0].db.irc_network,
                     matches[0].db.irc_port))
                matches[0].get_nicklist(self.caller)
                return

            # Puppetbot/reconnect <bot> - reconnect bot.
            if "reconnect" in self.switches:
                matches[0].reconnect()
                self.msg("Reconnecting " + self.lhs)
                return

            # Puppetbot/reload <bot> - Delete all bots, recreates bots from new user list.
            if "reload" in self.switches:
                matches[0].db.ev_location.msg_contents(
                    "Puppet reload in progress.")
                puppetlist = [
                    puppet
                    for puppet in search.search_tag(matches[0].key + "-puppet")
                ]
                for puppet in puppetlist:
                    puppet.delete()
                matches[0].get_nicklist()
                return

        # Create Bot.
        location = self.caller.location
        self.args = self.args.replace('#', ' ')  # Avoid Python comment issues
        try:
            irc_network, irc_port, irc_channel, irc_botname = \
                       [part.strip() for part in self.args.split(None, 4)]
            irc_channel = "#%s" % irc_channel
        except Exception:
            string = "IRC bot definition '%s' is not valid." % self.args
            self.msg(string)
            return

        botname = "ircbot-%s" % irc_botname
        # create a new bot
        bot = AccountDB.objects.filter(username__iexact=botname)
        if bot:
            self.msg("Account '%s' already exists." % botname)
            return
        else:
            password = "******"
            try:
                bot = create.create_account(botname,
                                            None,
                                            password,
                                            typeclass=AccountBot)
            except Exception as err:
                self.msg("|rError, could not create the bot:|n '%s'." % err)
                return
        bot.start(ev_location=location,
                  irc_botname=irc_botname,
                  irc_channel=irc_channel,
                  irc_network=irc_network,
                  irc_port=irc_port)
        self.msg("Connection created. Starting IRC bot.")
Пример #19
0
    def func(self):
        """Implement command"""

        caller = self.caller
        switches = self.switches

        if switches and switches[0] not in ("list", "start", "stop", "delete"):
            caller.msg("Usage: @service/<list|start|stop|delete> [servicename]")
            return

        # get all services
        service_collection = SESSIONS.server.services

        if not switches or switches[0] == "list":
            # Just display the list of installed services and their
            # status, then exit.
            table = EvTable("|wService|n (use @services/start|stop|delete)", "|wstatus", align="l")
            for service in service_collection.services:
                table.add_row(service.name, service.running and "|gRunning" or "|rNot Running")
            caller.msg(unicode(table))
            return

        # Get the service to start / stop

        try:
            service = service_collection.getServiceNamed(self.args)
        except Exception:
            string = 'Invalid service name. This command is case-sensitive. '
            string += 'See @service/list for valid service name (enter the full name exactly).'
            caller.msg(string)
            return

        if switches[0] in ("stop", "delete"):
            # Stopping/killing a service gracefully closes it and disconnects
            # any connections (if applicable).

            delmode = switches[0] == "delete"
            if not service.running:
                caller.msg('That service is not currently running.')
                return
            if service.name[:7] == 'Evennia':
                if delmode:
                    caller.msg("You cannot remove a core Evennia service (named 'Evennia***').")
                    return
                string = "You seem to be shutting down a core Evennia service (named 'Evennia***'). Note that"
                string += "stopping some TCP port services will *not* disconnect users *already*"
                string += "connected on those ports, but *may* instead cause spurious errors for them. To "
                string += "safely and permanently remove ports, change settings file and restart the server."
                caller.msg(string)

            if delmode:
                service.stopService()
                service_collection.removeService(service)
                caller.msg("Stopped and removed service '%s'." % self.args)
            else:
                service.stopService()
                caller.msg("Stopped service '%s'." % self.args)
            return

        if switches[0] == "start":
            # Attempt to start a service.
            if service.running:
                caller.msg('That service is already running.')
                return
            caller.msg("Starting service '%s'." % self.args)
            service.startService()
Пример #20
0
    def func(self):
        "Setup the irc-channel mapping"

        if not settings.IRC_ENABLED:
            string = """IRC is not enabled. You need to activate it in game/settings.py."""
            self.msg(string)
            return

        if 'list' in self.switches:
            # show all connections
            ircbots = [bot for bot in PlayerDB.objects.filter(db_is_bot=True, username__startswith="ircbot-")]
            if ircbots:
                from evennia.utils.evtable import EvTable
                table = EvTable("{wdbid{n", "{wbotname{n", "{wev-channel{n", "{wirc-channel{n", maxwidth=_DEFAULT_WIDTH)
                for ircbot in ircbots:
                    ircinfo = "%s (%s:%s)" % (ircbot.db.irc_channel, ircbot.db.irc_network, ircbot.db.irc_port)
                    table.add_row(ircbot.id, ircbot.db.irc_botname, ircbot.db.ev_channel, ircinfo)
                self.caller.msg(table)
            else:
                self.msg("No irc bots found.")
            return


        if('disconnect' in self.switches or 'remove' in self.switches or
                                                    'delete' in self.switches):
            botname = "ircbot-%s" % self.lhs
            matches = PlayerDB.objects.filter(db_is_bot=True, username=botname)
            dbref = utils.dbref(self.lhs)
            if not matches and dbref:
                # try dbref match
                matches = PlayerDB.objects.filter(db_is_bot=True, id=dbref)
            if matches:
                matches[0].delete()
                self.msg("IRC connection destroyed.")
            else:
                self.msg("IRC connection/bot could not be removed, does it exist?")
            return

        if not self.args or not self.rhs:
            string = "Usage: @irc2chan[/switches] <evennia_channel> = <ircnetwork> <port> <#irchannel> <botname>"
            self.msg(string)
            return

        channel = self.lhs
        self.rhs = self.rhs.replace('#', ' ') # to avoid Python comment issues
        try:
            irc_network, irc_port, irc_channel, irc_botname = \
                       [part.strip() for part in self.rhs.split(None, 3)]
            irc_channel = "#%s" % irc_channel
        except Exception:
            string = "IRC bot definition '%s' is not valid." % self.rhs
            self.msg(string)
            return

        botname = "ircbot-%s" % irc_botname

        # create a new bot
        bot = PlayerDB.objects.filter(username__iexact=botname)
        if bot:
            # re-use an existing bot
            bot = bot[0]
            if not bot.is_bot:
                self.msg("Player '%s' already exists and is not a bot." % botname)
                return
        else:
            bot = create.create_player(botname, None, None, typeclass=bots.IRCBot)
        bot.start(ev_channel=channel, irc_botname=irc_botname, irc_channel=irc_channel,
                  irc_network=irc_network, irc_port=irc_port)
        self.msg("Connection created. Starting IRC bot.")
Пример #21
0
    def func(self):
        """Setup the rss-channel mapping"""

        # checking we have all we need
        if not settings.RSS_ENABLED:
            string = """RSS is not enabled. You need to activate it in game/settings.py."""
            self.msg(string)
            return
        try:
            import feedparser
            assert feedparser  # to avoid checker error of not being used
        except ImportError:
            string = "RSS requires python-feedparser (https://pypi.python.org/pypi/feedparser)." \
                     " Install before continuing."
            self.msg(string)
            return

        if 'list' in self.switches:
            # show all connections
            rssbots = [bot for bot in AccountDB.objects.filter(db_is_bot=True, username__startswith="rssbot-")]
            if rssbots:
                from evennia.utils.evtable import EvTable
                table = EvTable("|wdbid|n", "|wupdate rate|n", "|wev-channel",
                                "|wRSS feed URL|n", border="cells", maxwidth=_DEFAULT_WIDTH)
                for rssbot in rssbots:
                    table.add_row(rssbot.id, rssbot.db.rss_rate, rssbot.db.ev_channel, rssbot.db.rss_url)
                self.msg(table)
            else:
                self.msg("No rss bots found.")
            return

        if 'disconnect' in self.switches or 'remove' in self.switches or 'delete' in self.switches:
            botname = "rssbot-%s" % self.lhs
            matches = AccountDB.objects.filter(db_is_bot=True, db_key=botname)
            if not matches:
                # try dbref match
                matches = AccountDB.objects.filter(db_is_bot=True, id=self.args.lstrip("#"))
            if matches:
                matches[0].delete()
                self.msg("RSS connection destroyed.")
            else:
                self.msg("RSS connection/bot could not be removed, does it exist?")
            return

        if not self.args or not self.rhs:
            string = "Usage: @rss2chan[/switches] <evennia_channel> = <rss url>"
            self.msg(string)
            return
        channel = self.lhs
        url = self.rhs

        botname = "rssbot-%s" % url
        # create a new bot
        bot = AccountDB.objects.filter(username__iexact=botname)
        if bot:
            # re-use existing bot
            bot = bot[0]
            if not bot.is_bot:
                self.msg("Account '%s' already exists and is not a bot." % botname)
                return
        else:
            bot = create.create_account(botname, None, None, typeclass=bots.RSSBot)
        bot.start(ev_channel=channel, rss_url=url, rss_rate=10)
        self.msg("RSS reporter created. Fetching RSS.")
Пример #22
0
    def options_formatter(self, optionlist, options_format):
        """
        Formats the option block.

        Args:
            optionlist (list): List of (key, description) tuples for every
                option related to this node.
            caller (Object, Account or None, optional): The caller of the node.

        Returns:
            options (str): The formatted option display.

        """
        if not optionlist:
            return ""

        # column separation distance
        colsep = 4

        # parse out options for Quit, Back, Proceed, None, and Finish
        option_list_1 = []
        option_list_2 = []
        for item in optionlist:
            if ('move_keys' in options_format
                    and item[0] in options_format['move_keys']):
                option_list_2.append(item)
            elif ('hide_keys' in options_format
                  and item[0] in options_format['hide_keys']):
                pass
            else:
                option_list_1.append(item)
        nlist = len(option_list_1)

        # get the widest option line in the table.
        table_width_max = -1
        table = []
        for key, desc in option_list_1:
            if key or desc:
                desc_string = ": %s" % desc if desc else ""
                table_width_max = max(
                    table_width_max,
                    max(m_len(p) for p in key.split("\n")) +
                    max(m_len(p) for p in desc_string.split("\n")) + colsep,
                )
                raw_key = strip_ansi(key)
                if raw_key != key:
                    # already decorations in key definition
                    table.append(" |lc%s|lt%s|le%s" %
                                 (raw_key, key, desc_string))
                else:
                    # add a default white color to key
                    table.append(" |lc%s|lt|w%s|n|le%s" %
                                 (raw_key, raw_key, desc_string))
        ncols = _MAX_TEXT_WIDTH // table_width_max  # number of ncols

        if ncols < 0:
            # no visible option at all
            return ""

        ncols = ncols + 1 if ncols == 0 else ncols
        # get the amount of rows needed (start with 4 rows)
        if 'rows' in options_format:
            nrows = options_format['rows']
        else:
            nrows = 4
        while nrows * ncols < nlist:
            nrows += 1
        ncols = nlist // nrows  # number of full columns
        nlastcol = nlist % nrows  # number of elements in last column

        # get the final column count
        ncols = ncols + 1 if nlastcol > 0 else ncols
        if ncols > 1:
            # only extend if longer than one column
            table.extend([" " for i in range(nrows - nlastcol)])

        # build the actual table grid
        table = [
            table[icol * nrows:(icol * nrows) + nrows]
            for icol in range(0, ncols)
        ]

        # adjust the width of each column
        for icol in range(len(table)):
            col_width = (max(
                max(m_len(p) for p in part.split("\n"))
                for part in table[icol]) + colsep)
            table[icol] = [
                pad(part, width=col_width + colsep, align="l")
                for part in table[icol]
            ]
        result = EvTable(table=table, border='none')
        if len(option_list_2) > 0:
            result.add_row(' ')
            for key, desc in option_list_2:
                if key or desc:
                    desc_string = ": %s" % desc if desc else ""
                    table_width_max = max(
                        table_width_max,
                        max(m_len(p) for p in key.split("\n")) +
                        max(m_len(p)
                            for p in desc_string.split("\n")) + colsep,
                    )
                    raw_key = strip_ansi(key)
                    if raw_key != key:
                        # already decorations in key definition
                        result.add_row(" |lc%s|lt%s|le%s" %
                                       (raw_key, key, desc_string))
                    else:
                        # add a default white color to key
                        result.add_row(" |lc%s|lt|w%s|n|le%s" %
                                       (raw_key, raw_key, desc_string))

        # format the table into columns
        return str(result)
Пример #23
0
    def accept_callback(self):
        """Accept a callback."""
        obj = self.obj
        callback_name = self.callback_name
        parameters = self.parameters

        # If no object, display the list of callbacks to be checked
        if obj is None:
            table = EvTable("ID",
                            "Type",
                            "Object",
                            "Name",
                            "Updated by",
                            "On",
                            width=78)
            table.reformat_column(0, align="r")
            now = datetime.now()
            for obj, name, number in self.handler.db.to_valid:
                callbacks = self.handler.get_callbacks(obj).get(name)
                if callbacks is None:
                    continue

                try:
                    callback = callbacks[number]
                except IndexError:
                    continue

                type_name = obj.typeclass_path.split(".")[-1]
                by = callback.get("updated_by")
                by = by.key if by else "|gUnknown|n"
                updated_on = callback.get("updated_on")
                if updated_on is None:
                    updated_on = callback.get("created_on")

                if updated_on:
                    updated_on = "{} ago".format(
                        time_format((now - updated_on).total_seconds(),
                                    4).capitalize())
                else:
                    updated_on = "|gUnknown|n"

                table.add_row(obj.id, type_name, obj, name, by, updated_on)
            self.msg(unicode(table))
            return

        # An object was specified
        callbacks = self.handler.get_callbacks(obj)
        types = self.handler.get_events(obj)

        # If no callback name is specified, display the list of callbacks
        if not callback_name:
            self.list_callbacks()
            return

        # Check that the callback exists
        if callback_name not in callbacks:
            self.msg("The callback name {} can't be found in {}.".format(
                callback_name, obj))
            return

        if not parameters:
            self.msg(
                "Which callback do you wish to accept?  Specify a number.")
            self.list_callbacks()
            return

        # Check that the parameter points to an existing callback
        try:
            number = int(parameters) - 1
            assert number >= 0
            callback = callbacks[callback_name][number]
        except (ValueError, AssertionError, IndexError):
            self.msg("The callback {} {} cannot be found in {}.".format(
                callback_name, parameters, obj))
            return

        # Accept the callback
        if callback["valid"]:
            self.msg("This callback has already been accepted.")
        else:
            self.handler.accept_callback(obj, callback_name, number)
            self.msg("The callback {} {} of {} has been accepted.".format(
                callback_name, parameters, obj))
Пример #24
0
    def func(self):
        if 'all' in self.switches:
            spawners = search_script_tag(SPAWNER_TAG, TAG_CATEGORY_BUILDING)

            if len(spawners) == 0:
                self.caller.msg("No spawners exist.")
                return

            table = EvTable("Scr #",
                            "Spawn Name",
                            "Loc #",
                            "Location",
                            "Respawn",
                            border="cells")
            for spawner in spawners:
                if spawner.obj:
                    location_dbref = spawner.obj.dbref
                    location_key = spawner.obj.key
                else:
                    location_dbref = "N/A"
                    location_key = "No Location"
                respawn_indicator = spawner.respawn_timer()
                table.add_row(spawner.dbref, spawner.db.spawn_name,
                              location_dbref, location_key, respawn_indicator)
            output = "|wSpawners by location:|n\n{}".format(table)
            self.caller.msg(output)

            return

        caller = self.caller
        if not caller.location:
            caller.msg("You need to be in a location to check for spawners.")
            return

        if not self.switches:
            if not self.rhs:
                # List out details for all spawners in the current room
                spawners = []
                for script in caller.location.scripts.all():
                    if isinstance(script, Spawner):
                        spawners.append(script)

                if len(spawners) == 0:
                    self.caller.msg(
                        "No spawners are present in this location.")
                    return

                separator = "\n{}".format("-" * 60)
                output = "|wSpawners in this location:|n"
                for spawner in spawners:
                    output += separator
                    output += "\nScript Name: {} ({})".format(
                        spawner.key, spawner.dbref)
                    output += "\nSpawn Target: {} ({})".format(
                        spawner.db.spawn_name, spawner.db.spawn_type)
                    output += "\nAliases: {}".format(','.join(
                        spawner.db.aliases))
                    output += "\nRespawn Timer: {}".format(
                        spawner.respawn_timer())
                    output += "\nSpawned Attributes:"
                    if not spawner.db.attributes:
                        output += " None"
                    else:
                        for key, value in spawner.db.attributes.items():
                            output += "\n    {}: {}".format(key, value)
                    output += "\nLocks: {}".format(spawner.db.lockstring)
                self.caller.msg(output)

                return

            # Create a new spawner
            try:
                class_from_module(self.rhs)
            except ImportError:
                caller.msg(
                    "{} is not a valid class path.\n"
                    "Usage: @spawner <obj name>[;alias1;alias2] = <class path>"
                    .format(self.rhs))
                return

            aliases = self.lhs.split(';')
            spawn_name = aliases.pop(0)
            # Force all aliases to be in lowercase
            aliases = [alias.lower() for alias in aliases]
            spawner = create_script(
                typeclass=Spawner,
                key=Spawner.get_key(spawn_name),
                obj=caller.location,
                interval=RESPAWN_TICK_RATE,
                start_delay=True,
                persistent=True,
                autostart=False,
                desc="Respawns target on a cadence if target is missing.")
            spawner.tags.add(SPAWNER_TAG, TAG_CATEGORY_BUILDING)
            spawner.db.spawn_name = spawn_name
            spawner.db.aliases = aliases
            spawner.db.spawn_type = self.rhs

            spawner.start()
            caller.msg("Successfully started spawner {}({})".format(
                spawner.db.spawn_name, spawner.dbref))
            return

        # Parse switches off the spawner search params and find it
        split_left = self.lhs.split('/', 1)
        spawner = self.find_spawner(split_left.pop(0))
        if not spawner:
            return

        if "spawn" in self.switches:
            spawned = spawner.spawn_target()
            caller.msg("Force spawned {} from spawner {}".format(
                spawned.get_display_name(caller), spawner.dbref))
            return
        if "del" in self.switches:
            spawner_name = spawner.name
            spawner_dbref = spawner.dbref
            spawner.stop()
            caller.msg("Stopped spawner {}({})".format(spawner_name,
                                                       spawner_dbref))
            return
        elif "name" in self.switches:
            if not self.rhs:
                caller.msg(
                    "@spawner/name <script #> = <obj name>[;alias1;alias2;...]"
                )
                return

            aliases = self.rhs.split(';')
            spawn_name = aliases.pop(0)
            # Force all aliases to be in lowercase
            aliases = [alias.lower().strip() for alias in aliases]
            spawner.db.aliases.extend(aliases)
            # Remove duplicates
            spawner.db.aliases = list(set(spawner.db.aliases))

            spawner.db.spawn_name = spawn_name
            spawner.key = Spawner.get_key(spawn_name)

            caller.msg("Renamed spawn target for spawner {} to be {}".format(
                spawner.dbref, spawn_name))
            return
        elif "alias" in self.switches:
            if not self.rhs:
                spawner.db.aliases = []
                caller.msg("Cleared aliases on spawner {}({})".format(
                    spawner.db.spawn_name, spawner.dbref))
                return

            aliases = self.rhs.split(',')
            # Force all aliases to be in lowercase
            aliases = [alias.lower().strip() for alias in aliases]
            spawner.db.aliases.extend(aliases)
            # Remove duplicates
            spawner.db.aliases = list(set(spawner.db.aliases))

            caller.msg("New alias list for spawner {}({}): {}".format(
                spawner.db.spawn_name, spawner.dbref,
                ','.join(spawner.db.aliases)))
            return
        elif "type" in self.switches:
            if not self.rhs:
                caller.msg("@spawner/type <script #> = <class path>")
                return

            try:
                spawn_class = class_from_module(self.rhs)
            except ImportError:
                caller.msg(
                    "{} is not a valid class path, not changing spawn class.".
                    format(self.rhs))
                return

            spawner.db.spawn_type = self.rhs
            spawner.ndb.spawn_class = spawn_class

            caller.msg("Changed spawner class for {}({}) to be {}".format(
                spawner.db.spawn_name, spawner.dbref, self.rhs))
            return
        elif "rate" in self.switches:
            spawner.respawn_rate = int(self.rhs)
            caller.msg(
                "Changed respawn rate for {}({}) to be {} seconds.".format(
                    spawner.db.spawn_name, spawner.dbref,
                    spawner.respawn_rate))
            return
        elif "set" in self.switches:
            # Check if we chose an attribute; there should be one more element left in split_left
            if len(split_left) != 1:
                caller.msg("Usage: @spawner/set <script #>/<attr>[ = <value>]")
                return
            attr = split_left[0]

            # Trying to clear an attribute if present
            if not self.rhs:
                if attr in spawner.db.attributes:
                    del spawner.db.attributes[attr]
                    caller.msg(
                        "Removed attribute {} from appearing on spawned objects on {}({})"
                        .format(attr, spawner.db.spawn_name, spawner.dbref))
                    return
                else:
                    caller.msg("No attribute {} set for spawner {}({})".format(
                        attr, spawner.db.spawn_name, spawner.dbref))
                    return

            # We are setting or overwriting an attribute to be placed on spawned objects
            value = _convert_from_string(self, self.rhs)
            spawner.db.attributes[attr] = value

            caller.msg(
                "Set attribute {} = {} to appear on objects spawned from {}({})"
                .format(attr, self.rhs, spawner.db.spawn_name, spawner.dbref))
            return
        elif "setclear" in self.switches:
            spawner.db.attributes = {}

            caller.msg(
                "Cleared all attributes from appearing on objects spawned from {}({})"
                .format(spawner.db.spawn_name, spawner.dbref))
            return
        elif "lock" in self.switches:
            spawner.add_lock(self.rhs)
            caller.msg("Added lock {} to spawner {}({})".format(
                self.rhs, spawner.db.spawn_name, spawner.dbref))
            return
        elif "lockdel" in self.switches:
            if self.rhs or len(split_left) != 1:
                caller.msg("Usage: @spawner/lockdel <script #>/<lock type>")
            lock_type = split_left[0]
            spawner.remove_lock(lock_type)
            caller.msg("Deleted lock {} to spawner {}({})".format(
                lock_type, spawner.db.spawn_name, spawner.dbref))
            return
        elif "lockreset" in self.switches:
            spawner.reset_locks()
            caller.msg("Reset locks to default on spawner {}({})".format(
                spawner.db.spawn_name, spawner.dbref))
            return
        else:
            caller.msg(
                "Invalid switch. Type \"help @spawner\" for a list of valid switches."
            )
            return