mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Merged script/scripts commands. Resolve #1502
This commit is contained in:
parent
8497dfa933
commit
1196196ed8
5 changed files with 374 additions and 408 deletions
|
|
@ -80,6 +80,8 @@ Up requirements to Django 3.2+
|
|||
`Command.retain_instance` flag for reusing the same command instance.
|
||||
- The `typeclass` command will now correctly search the correct database-table for the target
|
||||
obj (avoids mistakenly assigning an AccountDB-typeclass to a Character etc).
|
||||
- Merged `script` and `scripts` commands into one, for both managing global- and
|
||||
on-object Scripts. Moved `CmdScripts` and `CmdObjects` to `commands/default/building.py`.
|
||||
|
||||
### Evennia 0.9.5 (2019-2020)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
Building and world design commands
|
||||
"""
|
||||
import re
|
||||
from django.core.paginator import Paginator
|
||||
from django.conf import settings
|
||||
from django.db.models import Q, Min, Max
|
||||
from evennia import InterruptCommand
|
||||
from evennia.scripts.models import ScriptDB
|
||||
from evennia.objects.models import ObjectDB
|
||||
from evennia.locks.lockhandler import LockException
|
||||
from evennia.commands.cmdhandler import get_and_merge_cmdsets
|
||||
|
|
@ -14,13 +16,14 @@ from evennia.utils.utils import (
|
|||
class_from_module,
|
||||
get_all_typeclasses,
|
||||
variable_from_module,
|
||||
dbref,
|
||||
dbref, crop,
|
||||
interactive,
|
||||
list_to_string,
|
||||
display_len,
|
||||
)
|
||||
from evennia.utils.eveditor import EvEditor
|
||||
from evennia.utils.evmore import EvMore
|
||||
from evennia.utils.evtable import EvTable
|
||||
from evennia.prototypes import spawner, prototypes as protlib, menus as olc_menus
|
||||
from evennia.utils.ansi import raw as ansi_raw
|
||||
|
||||
|
|
@ -53,7 +56,8 @@ __all__ = (
|
|||
"CmdExamine",
|
||||
"CmdFind",
|
||||
"CmdTeleport",
|
||||
"CmdScript",
|
||||
"CmdScripts",
|
||||
"CmdObjects",
|
||||
"CmdTag",
|
||||
"CmdSpawn",
|
||||
)
|
||||
|
|
@ -2983,6 +2987,330 @@ class CmdFind(COMMAND_DEFAULT_CLASS):
|
|||
caller.msg(string.strip())
|
||||
|
||||
|
||||
class ScriptEvMore(EvMore):
|
||||
"""
|
||||
Listing 1000+ Scripts can be very slow and memory-consuming. So
|
||||
we use this custom EvMore child to build en EvTable only for
|
||||
each page of the list.
|
||||
|
||||
"""
|
||||
|
||||
def init_pages(self, scripts):
|
||||
"""Prepare the script list pagination"""
|
||||
script_pages = Paginator(scripts, max(1, int(self.height / 2)))
|
||||
super().init_pages(script_pages)
|
||||
|
||||
def page_formatter(self, scripts):
|
||||
"""Takes a page of scripts and formats the output
|
||||
into an EvTable."""
|
||||
|
||||
if not scripts:
|
||||
return "<No scripts>"
|
||||
|
||||
table = EvTable(
|
||||
"|wdbref|n",
|
||||
"|wobj|n",
|
||||
"|wkey|n",
|
||||
"|wintval|n",
|
||||
"|wnext|n",
|
||||
"|wrept|n",
|
||||
"|wtypeclass|n",
|
||||
"|wdesc|n",
|
||||
align="r",
|
||||
border="tablecols",
|
||||
width=self.width,
|
||||
)
|
||||
|
||||
for script in scripts:
|
||||
|
||||
nextrep = script.time_until_next_repeat()
|
||||
if nextrep is None:
|
||||
nextrep = script.db._paused_time
|
||||
nextrep = f"PAUSED {int(nextrep)}s" if nextrep else "--"
|
||||
else:
|
||||
nextrep = f"{nextrep}s"
|
||||
|
||||
maxrepeat = script.repeats
|
||||
remaining = script.remaining_repeats() or 0
|
||||
if maxrepeat:
|
||||
rept = "%i/%i" % (maxrepeat - remaining, maxrepeat)
|
||||
else:
|
||||
rept = "-/-"
|
||||
|
||||
table.add_row(
|
||||
f"#{script.id}",
|
||||
f"{script.obj.key}({script.obj.dbref})"
|
||||
if (hasattr(script, "obj") and script.obj)
|
||||
else "<Global>",
|
||||
script.key,
|
||||
script.interval if script.interval > 0 else "--",
|
||||
nextrep,
|
||||
rept,
|
||||
script.typeclass_path.rsplit(".", 1)[-1],
|
||||
crop(script.desc, width=20),
|
||||
)
|
||||
|
||||
return str(table)
|
||||
|
||||
|
||||
class CmdScripts(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
List and manage all running scripts. Allows for creating new global
|
||||
scripts.
|
||||
|
||||
Usage:
|
||||
script[/switches] [script-#dbref, key, script.path or <obj>]
|
||||
script[/start||stop] <obj> = <script.path or script-key>
|
||||
|
||||
Switches:
|
||||
start - start/unpause an existing script's timer.
|
||||
stop - stops an existing script's timer
|
||||
pause - pause a script's timer
|
||||
delete - deletes script. This will also stop the timer as needed
|
||||
|
||||
Examples:
|
||||
script - list scripts
|
||||
script myobj - list all scripts on object
|
||||
script foo.bar.Script - create a new global Script
|
||||
script scriptname - examine named existing global script
|
||||
script myobj = foo.bar.Script - create and assign script to object
|
||||
script/stop myobj = scriptname - stop script on object
|
||||
script/pause foo.Bar.Script - pause global script
|
||||
script/delete myobj - delete ALL scripts on object
|
||||
|
||||
When given with an `<obj>` as left-hand-side, this creates and
|
||||
assigns a new script to that object. Without an `<obj>`, this
|
||||
manages and inspects global scripts
|
||||
|
||||
If no switches are given, this command just views all active
|
||||
scripts. The argument can be either an object, at which point it
|
||||
will be searched for all scripts defined on it, or a script name
|
||||
or #dbref. For using the /stop switch, a unique script #dbref is
|
||||
required since whole classes of scripts often have the same name.
|
||||
|
||||
Use the `script` build-level command for managing scripts attached to
|
||||
objects.
|
||||
|
||||
"""
|
||||
|
||||
key = "scripts"
|
||||
aliases = ["script"]
|
||||
switch_options = ("create", "start", "stop", "pause", "delete")
|
||||
locks = "cmd:perm(scripts) or perm(Builder)"
|
||||
help_category = "System"
|
||||
|
||||
excluded_typeclass_paths = ["evennia.prototypes.prototypes.DbPrototype"]
|
||||
|
||||
switch_mapping = {
|
||||
"create": "|gCreated|n",
|
||||
"start": "|gStarted|n",
|
||||
"stop": "|RStopped|n",
|
||||
"pause": "|Paused|n",
|
||||
"delete": "|rDeleted|n"
|
||||
}
|
||||
|
||||
def _search_script(self, args):
|
||||
# test first if this is a script match
|
||||
scripts = ScriptDB.objects.get_all_scripts(key=args)
|
||||
if scripts:
|
||||
return scripts
|
||||
# try typeclass path
|
||||
scripts = ScriptDB.objects.filter(db_typeclass_path__iendswith=args)
|
||||
if scripts:
|
||||
return scripts
|
||||
|
||||
def func(self):
|
||||
"""implement method"""
|
||||
|
||||
caller = self.caller
|
||||
|
||||
if not self.args:
|
||||
# show all scripts
|
||||
scripts = ScriptDB.objects.all()
|
||||
if not scripts:
|
||||
caller.msg("No scripts found.")
|
||||
return
|
||||
ScriptEvMore(caller, scripts.order_by("id"), session=self.session)
|
||||
return
|
||||
|
||||
# find script or object to operate on
|
||||
scripts, obj = None, None
|
||||
if self.rhs:
|
||||
obj_query = self.lhs
|
||||
script_query = self.rhs
|
||||
else:
|
||||
obj_query = script_query = self.args
|
||||
|
||||
scripts = self._search_script(script_query)
|
||||
objects = ObjectDB.objects.object_search(obj_query)
|
||||
obj = objects[0] if objects else None
|
||||
|
||||
if not self.switches:
|
||||
# creation / view mode
|
||||
if obj:
|
||||
# we have an object
|
||||
if self.rhs:
|
||||
# creation mode
|
||||
if obj.scripts.add(self.rhs, autostart=True):
|
||||
caller.msg(
|
||||
f"Script |w{self.rhs}|n successfully added and "
|
||||
f"started on {obj.get_display_name(caller)}.")
|
||||
else:
|
||||
caller.msg(f"Script {self.rhs} could not be added and/or started "
|
||||
f"on {obj.get_display_name(caller)} (or it started and "
|
||||
"immediately shut down).")
|
||||
else:
|
||||
# just show all scripts on object
|
||||
scripts = ScriptDB.objects.filter(db_obj=obj)
|
||||
if scripts:
|
||||
ScriptEvMore(caller, scripts.order_by("id"), session=self.session)
|
||||
else:
|
||||
caller.msg(f"No scripts defined on {obj}")
|
||||
|
||||
elif scripts:
|
||||
# show found script(s)
|
||||
ScriptEvMore(caller, scripts.order_by("id"), session=self.session)
|
||||
|
||||
else:
|
||||
# create global script
|
||||
try:
|
||||
new_script = create.create_script(self.args)
|
||||
except ImportError:
|
||||
logger.log_trace()
|
||||
new_script = None
|
||||
|
||||
if new_script:
|
||||
caller.msg(f"Global Script Created - "
|
||||
f"{new_script.key} ({new_script.typeclass_path})")
|
||||
ScriptEvMore(caller, [new_script], session=self.session)
|
||||
else:
|
||||
caller.msg(f"Global Script |rNOT|n Created |r(see log)|n - "
|
||||
f"arguments: {self.args}")
|
||||
|
||||
elif scripts or obj:
|
||||
# modification switches - must operate on existing scripts
|
||||
|
||||
if not scripts:
|
||||
scripts = ScriptDB.objects.filter(db_obj=obj)
|
||||
|
||||
if scripts.count() > 1:
|
||||
ret = yield(f"Multiple scripts found: {scripts}. Are you sure you want to "
|
||||
"operate on all of them? [Y]/N? ")
|
||||
if ret.lower() in ('n', 'no'):
|
||||
caller.msg("Aborted.")
|
||||
return
|
||||
|
||||
for script in scripts:
|
||||
script_key = script.key
|
||||
script_typeclass_path = script.typeclass_path
|
||||
scripttype = f"Script on {obj}" if obj else "Global Script"
|
||||
|
||||
for switch in self.switches:
|
||||
verb = self.switch_mapping[switch]
|
||||
msgs = []
|
||||
try:
|
||||
getattr(script, switch)()
|
||||
except Exception:
|
||||
logger.log_trace()
|
||||
msgs.append(f"{scripttype} |rNOT|n {verb} |r(see log)|n - "
|
||||
f"{script_key} ({script_typeclass_path})|n")
|
||||
else:
|
||||
msgs.append(f"{scripttype} {verb} - "
|
||||
f"{script_key} ({script_typeclass_path})")
|
||||
caller.msg("\n".join(msgs))
|
||||
if "delete" not in self.switches:
|
||||
ScriptEvMore(caller, [script], session=self.session)
|
||||
else:
|
||||
caller.msg("No scripts found.")
|
||||
|
||||
|
||||
class CmdObjects(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
statistics on objects in the database
|
||||
|
||||
Usage:
|
||||
objects [<nr>]
|
||||
|
||||
Gives statictics on objects in database as well as
|
||||
a list of <nr> latest objects in database. If not
|
||||
given, <nr> defaults to 10.
|
||||
"""
|
||||
|
||||
key = "objects"
|
||||
aliases = ["listobjects", "listobjs", "stats", "db"]
|
||||
locks = "cmd:perm(listobjects) or perm(Builder)"
|
||||
help_category = "System"
|
||||
|
||||
def func(self):
|
||||
"""Implement the command"""
|
||||
|
||||
caller = self.caller
|
||||
nlim = int(self.args) if self.args and self.args.isdigit() else 10
|
||||
nobjs = ObjectDB.objects.count()
|
||||
Character = class_from_module(settings.BASE_CHARACTER_TYPECLASS)
|
||||
nchars = Character.objects.all_family().count()
|
||||
Room = class_from_module(settings.BASE_ROOM_TYPECLASS)
|
||||
nrooms = Room.objects.all_family().count()
|
||||
Exit = class_from_module(settings.BASE_EXIT_TYPECLASS)
|
||||
nexits = Exit.objects.all_family().count()
|
||||
nother = nobjs - nchars - nrooms - nexits
|
||||
nobjs = nobjs or 1 # fix zero-div error with empty database
|
||||
|
||||
# total object sum table
|
||||
totaltable = self.styled_table(
|
||||
"|wtype|n", "|wcomment|n", "|wcount|n", "|w%|n", border="table", align="l"
|
||||
)
|
||||
totaltable.align = "l"
|
||||
totaltable.add_row(
|
||||
"Characters",
|
||||
"(BASE_CHARACTER_TYPECLASS + children)",
|
||||
nchars,
|
||||
"%.2f" % ((float(nchars) / nobjs) * 100),
|
||||
)
|
||||
totaltable.add_row(
|
||||
"Rooms",
|
||||
"(BASE_ROOM_TYPECLASS + children)",
|
||||
nrooms,
|
||||
"%.2f" % ((float(nrooms) / nobjs) * 100),
|
||||
)
|
||||
totaltable.add_row(
|
||||
"Exits",
|
||||
"(BASE_EXIT_TYPECLASS + children)",
|
||||
nexits,
|
||||
"%.2f" % ((float(nexits) / nobjs) * 100),
|
||||
)
|
||||
totaltable.add_row("Other", "", nother, "%.2f" % ((float(nother) / nobjs) * 100))
|
||||
|
||||
# typeclass table
|
||||
typetable = self.styled_table(
|
||||
"|wtypeclass|n", "|wcount|n", "|w%|n", border="table", align="l"
|
||||
)
|
||||
typetable.align = "l"
|
||||
dbtotals = ObjectDB.objects.get_typeclass_totals()
|
||||
for stat in dbtotals:
|
||||
typetable.add_row(
|
||||
stat.get("typeclass", "<error>"),
|
||||
stat.get("count", -1),
|
||||
"%.2f" % stat.get("percent", -1),
|
||||
)
|
||||
|
||||
# last N table
|
||||
objs = ObjectDB.objects.all().order_by("db_date_created")[max(0, nobjs - nlim) :]
|
||||
latesttable = self.styled_table(
|
||||
"|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)
|
||||
|
||||
|
||||
class CmdTeleport(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
teleport object to another location
|
||||
|
|
@ -3107,113 +3435,6 @@ class CmdTeleport(COMMAND_DEFAULT_CLASS):
|
|||
caller.msg("Teleportation failed.")
|
||||
|
||||
|
||||
class CmdScript(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
attach a script to an object
|
||||
|
||||
Usage:
|
||||
addscript[/switch] <obj> [= script_path or <scriptkey>]
|
||||
|
||||
Switches:
|
||||
start - start all non-running scripts on object, or a given script only
|
||||
stop - stop all scripts on objects, or a given script only
|
||||
|
||||
If no script path/key is given, lists all scripts active on the given
|
||||
object.
|
||||
Script path can be given from the base location for scripts as given in
|
||||
settings. If adding a new script, it will be started automatically
|
||||
(no /start switch is needed). Using the /start or /stop switches on an
|
||||
object without specifying a script key/path will start/stop ALL scripts on
|
||||
the object.
|
||||
"""
|
||||
|
||||
key = "addscript"
|
||||
aliases = ["attachscript"]
|
||||
switch_options = ("start", "stop")
|
||||
locks = "cmd:perm(script) or perm(Builder)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
"""Do stuff"""
|
||||
|
||||
caller = self.caller
|
||||
|
||||
if not self.args:
|
||||
string = "Usage: script[/switch] <obj> [= script_path or <script key>]"
|
||||
caller.msg(string)
|
||||
return
|
||||
|
||||
if not self.lhs:
|
||||
caller.msg("To create a global script you need |wscripts/add <typeclass>|n.")
|
||||
return
|
||||
|
||||
obj = caller.search(self.lhs)
|
||||
if not obj:
|
||||
return
|
||||
|
||||
result = []
|
||||
if not self.rhs:
|
||||
# no rhs means we want to operate on all scripts
|
||||
scripts = obj.scripts.all()
|
||||
if not scripts:
|
||||
result.append("No scripts defined on %s." % obj.get_display_name(caller))
|
||||
elif not self.switches:
|
||||
# view all scripts
|
||||
from evennia.commands.default.system import ScriptEvMore
|
||||
|
||||
ScriptEvMore(self.caller, scripts.order_by("id"), session=self.session)
|
||||
return
|
||||
elif "start" in self.switches:
|
||||
num = sum([obj.scripts.start(script.key) for script in scripts])
|
||||
result.append("%s scripts started on %s." % (num, obj.get_display_name(caller)))
|
||||
elif "stop" in self.switches:
|
||||
for script in scripts:
|
||||
result.append(
|
||||
"Stopping script %s on %s."
|
||||
% (script.get_display_name(caller), obj.get_display_name(caller))
|
||||
)
|
||||
script.stop()
|
||||
else: # rhs exists
|
||||
if not self.switches:
|
||||
# adding a new script, and starting it
|
||||
ok = obj.scripts.add(self.rhs, autostart=True)
|
||||
if not ok:
|
||||
result.append(
|
||||
"\nScript %s could not be added and/or started on %s "
|
||||
"(or it started and immediately shut down)."
|
||||
% (self.rhs, obj.get_display_name(caller))
|
||||
)
|
||||
else:
|
||||
result.append(
|
||||
"Script |w%s|n successfully added and started on %s."
|
||||
% (self.rhs, obj.get_display_name(caller))
|
||||
)
|
||||
|
||||
else:
|
||||
paths = [self.rhs] + [
|
||||
"%s.%s" % (prefix, self.rhs) for prefix in settings.TYPECLASS_PATHS
|
||||
]
|
||||
if "stop" in self.switches:
|
||||
# we are stopping an already existing script
|
||||
for path in paths:
|
||||
ok = obj.scripts.stop(path)
|
||||
if not ok:
|
||||
result.append("\nScript %s could not be stopped. Does it exist?" % path)
|
||||
else:
|
||||
result = ["Script stopped and removed from object."]
|
||||
break
|
||||
if "start" in self.switches:
|
||||
# we are starting an already existing script
|
||||
for path in paths:
|
||||
ok = obj.scripts.start(path)
|
||||
if not ok:
|
||||
result.append("\nScript %s could not be (re)started." % path)
|
||||
else:
|
||||
result = ["Script started successfully."]
|
||||
break
|
||||
|
||||
EvMore(caller, "".join(result).strip())
|
||||
|
||||
|
||||
class CmdTag(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -41,8 +41,6 @@ class CharacterCmdSet(CmdSet):
|
|||
|
||||
# System commands
|
||||
self.add(system.CmdPy())
|
||||
self.add(system.CmdScripts())
|
||||
self.add(system.CmdObjects())
|
||||
self.add(system.CmdAccounts())
|
||||
self.add(system.CmdService())
|
||||
self.add(system.CmdAbout())
|
||||
|
|
@ -83,10 +81,11 @@ class CharacterCmdSet(CmdSet):
|
|||
self.add(building.CmdExamine())
|
||||
self.add(building.CmdTypeclass())
|
||||
self.add(building.CmdLock())
|
||||
self.add(building.CmdScript())
|
||||
self.add(building.CmdSetHome())
|
||||
self.add(building.CmdTag())
|
||||
self.add(building.CmdSpawn())
|
||||
self.add(building.CmdScripts())
|
||||
self.add(building.CmdObjects())
|
||||
|
||||
# Batchprocessor commands
|
||||
self.add(batchprocess.CmdBatchCommands())
|
||||
|
|
|
|||
|
|
@ -15,17 +15,13 @@ import twisted
|
|||
import time
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.paginator import Paginator
|
||||
from evennia.server.sessionhandler import SESSIONS
|
||||
from evennia.scripts.models import ScriptDB
|
||||
from evennia.objects.models import ObjectDB
|
||||
from evennia.accounts.models import AccountDB
|
||||
from evennia.utils import logger, utils, gametime, create, search
|
||||
from evennia.utils import logger, utils, gametime, search
|
||||
from evennia.utils.eveditor import EvEditor
|
||||
from evennia.utils.evtable import EvTable
|
||||
from evennia.utils.evmore import EvMore
|
||||
from evennia.utils.evmenu import ask_yes_no
|
||||
from evennia.utils.utils import crop, class_from_module, iter_to_str
|
||||
from evennia.utils.utils import class_from_module, iter_to_str
|
||||
from evennia.scripts.taskhandler import TaskHandlerTask
|
||||
|
||||
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||
|
|
@ -41,8 +37,6 @@ __all__ = (
|
|||
"CmdReset",
|
||||
"CmdShutdown",
|
||||
"CmdPy",
|
||||
"CmdScripts",
|
||||
"CmdObjects",
|
||||
"CmdService",
|
||||
"CmdAbout",
|
||||
"CmdTime",
|
||||
|
|
@ -412,278 +406,6 @@ class CmdPy(COMMAND_DEFAULT_CLASS):
|
|||
)
|
||||
|
||||
|
||||
class ScriptEvMore(EvMore):
|
||||
"""
|
||||
Listing 1000+ Scripts can be very slow and memory-consuming. So
|
||||
we use this custom EvMore child to build en EvTable only for
|
||||
each page of the list.
|
||||
|
||||
"""
|
||||
|
||||
def init_pages(self, scripts):
|
||||
"""Prepare the script list pagination"""
|
||||
script_pages = Paginator(scripts, max(1, int(self.height / 2)))
|
||||
super().init_pages(script_pages)
|
||||
|
||||
def page_formatter(self, scripts):
|
||||
"""Takes a page of scripts and formats the output
|
||||
into an EvTable."""
|
||||
|
||||
if not scripts:
|
||||
return "<No scripts>"
|
||||
|
||||
table = EvTable(
|
||||
"|wdbref|n",
|
||||
"|wobj|n",
|
||||
"|wkey|n",
|
||||
"|wintval|n",
|
||||
"|wnext|n",
|
||||
"|wrept|n",
|
||||
"|wtypeclass|n",
|
||||
"|wdesc|n",
|
||||
align="r",
|
||||
border="tablecols",
|
||||
width=self.width,
|
||||
)
|
||||
|
||||
for script in scripts:
|
||||
|
||||
nextrep = script.time_until_next_repeat()
|
||||
if nextrep is None:
|
||||
nextrep = script.db._paused_time
|
||||
nextrep = f"PAUSED {int(nextrep)}s" if nextrep else "--"
|
||||
else:
|
||||
nextrep = f"{nextrep}s"
|
||||
|
||||
maxrepeat = script.repeats
|
||||
remaining = script.remaining_repeats() or 0
|
||||
if maxrepeat:
|
||||
rept = "%i/%i" % (maxrepeat - remaining, maxrepeat)
|
||||
else:
|
||||
rept = "-/-"
|
||||
|
||||
table.add_row(
|
||||
f"#{script.id}",
|
||||
f"{script.obj.key}({script.obj.dbref})"
|
||||
if (hasattr(script, "obj") and script.obj)
|
||||
else "<Global>",
|
||||
script.key,
|
||||
script.interval if script.interval > 0 else "--",
|
||||
nextrep,
|
||||
rept,
|
||||
script.typeclass_path.rsplit(".", 1)[-1],
|
||||
crop(script.desc, width=20),
|
||||
)
|
||||
|
||||
return str(table)
|
||||
|
||||
|
||||
class CmdScripts(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
List and manage all running scripts. Allows for creating new global
|
||||
scripts.
|
||||
|
||||
Usage:
|
||||
script[/switches] [#dbref, key, script.path or <obj>]
|
||||
|
||||
Switches:
|
||||
create - create a new global script of given typeclass path. This will
|
||||
auto-start the script's timer if it has one.
|
||||
start - start/unpause an existing script's timer.
|
||||
stop - stops an existing script's timer
|
||||
pause - pause a script's timer
|
||||
delete - deletes script. This will also stop the timer as needed
|
||||
|
||||
If no switches are given, this command just views all active
|
||||
scripts. The argument can be either an object, at which point it
|
||||
will be searched for all scripts defined on it, or a script name
|
||||
or #dbref. For using the /stop switch, a unique script #dbref is
|
||||
required since whole classes of scripts often have the same name.
|
||||
|
||||
Use the `script` build-level command for managing scripts attached to
|
||||
objects.
|
||||
|
||||
"""
|
||||
|
||||
key = "scripts"
|
||||
aliases = ["scripts"]
|
||||
switch_options = ("create", "start", "stop", "pause", "delete")
|
||||
locks = "cmd:perm(listscripts) or perm(Admin)"
|
||||
help_category = "System"
|
||||
|
||||
excluded_typeclass_paths = ["evennia.prototypes.prototypes.DbPrototype"]
|
||||
|
||||
switch_mapping = {
|
||||
"create": "|gCreated|n",
|
||||
"start": "|gStarted|n",
|
||||
"stop": "|RStopped|n",
|
||||
"pause": "|Paused|n",
|
||||
"delete": "|rDeleted|n"
|
||||
}
|
||||
|
||||
def _search_script(self, args):
|
||||
# test first if this is a script match
|
||||
scripts = ScriptDB.objects.get_all_scripts(key=args)
|
||||
if scripts:
|
||||
return scripts
|
||||
# try typeclass path
|
||||
scripts = ScriptDB.objects.filter(db_typeclass_path__iendswith=args)
|
||||
if scripts:
|
||||
return scripts
|
||||
# try to find an object instead.
|
||||
objects = ObjectDB.objects.object_search(args)
|
||||
if objects:
|
||||
scripts = ScriptDB.objects.filter(db_obj__in=objects)
|
||||
return scripts
|
||||
|
||||
def func(self):
|
||||
"""implement method"""
|
||||
|
||||
caller = self.caller
|
||||
args = self.args
|
||||
|
||||
if "create" in self.switches:
|
||||
# global script-start mode
|
||||
verb = self.switch_mapping['create']
|
||||
if not args:
|
||||
caller.msg("Usage script/create <key or typeclass>")
|
||||
return
|
||||
new_script = create.create_script(args)
|
||||
if new_script:
|
||||
caller.msg(f"Global Script {verb} - {new_script.key} ({new_script.typeclass_path})")
|
||||
ScriptEvMore(caller, [new_script], session=self.session)
|
||||
else:
|
||||
caller.msg(f"Global Script |rNOT|n {verb} |r(see log)|n - arguments: {args}")
|
||||
return
|
||||
|
||||
# all other switches require existing scripts
|
||||
if args:
|
||||
scripts = self._search_script(args)
|
||||
if not scripts:
|
||||
caller.msg(f"No scripts found matching '{args}'.")
|
||||
return
|
||||
else:
|
||||
scripts = ScriptDB.objects.all()
|
||||
if not scripts:
|
||||
caller.msg("No scripts found.")
|
||||
return
|
||||
|
||||
if args and self.switches:
|
||||
# global script-modifying mode
|
||||
if scripts.count() > 1:
|
||||
caller.msg("Multiple script matches. Please refine your search.")
|
||||
return
|
||||
script = scripts[0]
|
||||
script_key = script.key
|
||||
script_typeclass_path = script.typeclass_path
|
||||
for switch in self.switches:
|
||||
verb = self.switch_mapping[switch]
|
||||
msgs = []
|
||||
try:
|
||||
getattr(script, switch)()
|
||||
except Exception:
|
||||
logger.log_trace()
|
||||
msgs.append(f"Global Script |rNOT|n {verb} |r(see log)|n - "
|
||||
f"{script_key} ({script_typeclass_path})|n")
|
||||
else:
|
||||
msgs.append(f"Global Script {verb} - "
|
||||
f"{script_key} ({script_typeclass_path})")
|
||||
caller.msg("\n".join(msgs))
|
||||
if "delete" not in self.switches:
|
||||
ScriptEvMore(caller, [script], session=self.session)
|
||||
return
|
||||
else:
|
||||
# simply show the found scripts
|
||||
ScriptEvMore(caller, scripts.order_by("id"), session=self.session)
|
||||
|
||||
|
||||
class CmdObjects(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
statistics on objects in the database
|
||||
|
||||
Usage:
|
||||
objects [<nr>]
|
||||
|
||||
Gives statictics on objects in database as well as
|
||||
a list of <nr> latest objects in database. If not
|
||||
given, <nr> defaults to 10.
|
||||
"""
|
||||
|
||||
key = "objects"
|
||||
aliases = ["listobjects", "listobjs", "stats", "db"]
|
||||
locks = "cmd:perm(listobjects) or perm(Builder)"
|
||||
help_category = "System"
|
||||
|
||||
def func(self):
|
||||
"""Implement the command"""
|
||||
|
||||
caller = self.caller
|
||||
nlim = int(self.args) if self.args and self.args.isdigit() else 10
|
||||
nobjs = ObjectDB.objects.count()
|
||||
Character = class_from_module(settings.BASE_CHARACTER_TYPECLASS)
|
||||
nchars = Character.objects.all_family().count()
|
||||
Room = class_from_module(settings.BASE_ROOM_TYPECLASS)
|
||||
nrooms = Room.objects.all_family().count()
|
||||
Exit = class_from_module(settings.BASE_EXIT_TYPECLASS)
|
||||
nexits = Exit.objects.all_family().count()
|
||||
nother = nobjs - nchars - nrooms - nexits
|
||||
nobjs = nobjs or 1 # fix zero-div error with empty database
|
||||
|
||||
# total object sum table
|
||||
totaltable = self.styled_table(
|
||||
"|wtype|n", "|wcomment|n", "|wcount|n", "|w%|n", border="table", align="l"
|
||||
)
|
||||
totaltable.align = "l"
|
||||
totaltable.add_row(
|
||||
"Characters",
|
||||
"(BASE_CHARACTER_TYPECLASS + children)",
|
||||
nchars,
|
||||
"%.2f" % ((float(nchars) / nobjs) * 100),
|
||||
)
|
||||
totaltable.add_row(
|
||||
"Rooms",
|
||||
"(BASE_ROOM_TYPECLASS + children)",
|
||||
nrooms,
|
||||
"%.2f" % ((float(nrooms) / nobjs) * 100),
|
||||
)
|
||||
totaltable.add_row(
|
||||
"Exits",
|
||||
"(BASE_EXIT_TYPECLASS + children)",
|
||||
nexits,
|
||||
"%.2f" % ((float(nexits) / nobjs) * 100),
|
||||
)
|
||||
totaltable.add_row("Other", "", nother, "%.2f" % ((float(nother) / nobjs) * 100))
|
||||
|
||||
# typeclass table
|
||||
typetable = self.styled_table(
|
||||
"|wtypeclass|n", "|wcount|n", "|w%|n", border="table", align="l"
|
||||
)
|
||||
typetable.align = "l"
|
||||
dbtotals = ObjectDB.objects.get_typeclass_totals()
|
||||
for stat in dbtotals:
|
||||
typetable.add_row(
|
||||
stat.get("typeclass", "<error>"),
|
||||
stat.get("count", -1),
|
||||
"%.2f" % stat.get("percent", -1),
|
||||
)
|
||||
|
||||
# last N table
|
||||
objs = ObjectDB.objects.all().order_by("db_date_created")[max(0, nobjs - nlim) :]
|
||||
latesttable = self.styled_table(
|
||||
"|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)
|
||||
|
||||
|
||||
class CmdAccounts(COMMAND_DEFAULT_CLASS):
|
||||
"""
|
||||
Manage registered accounts
|
||||
|
|
|
|||
|
|
@ -565,10 +565,10 @@ class TestSystem(CommandTest):
|
|||
self.call(system.CmdPy(), "/clientraw 1+2", ">>> 1+2|3")
|
||||
|
||||
def test_scripts(self):
|
||||
self.call(system.CmdScripts(), "", "dbref ")
|
||||
self.call(building.CmdScripts(), "", "dbref ")
|
||||
|
||||
def test_objects(self):
|
||||
self.call(system.CmdObjects(), "", "Object subtype totals")
|
||||
self.call(building.CmdObjects(), "", "Object subtype totals")
|
||||
|
||||
def test_about(self):
|
||||
self.call(system.CmdAbout(), "", None)
|
||||
|
|
@ -1573,36 +1573,58 @@ class TestBuilding(CommandTest):
|
|||
self.call(building.CmdFind(), f"=#{id1}-{id2}", f"{mdiff} Matches(#{id1}-#{id2}):")
|
||||
|
||||
def test_script(self):
|
||||
self.call(building.CmdScript(), "Obj = ", "No scripts defined on Obj")
|
||||
self.call(building.CmdScripts(), "Obj", "No scripts defined on Obj")
|
||||
self.call(
|
||||
building.CmdScript(), "Obj = scripts.Script", "Script scripts.Script successfully added"
|
||||
building.CmdScripts(),
|
||||
"Obj = scripts.Script",
|
||||
"Script scripts.Script successfully added"
|
||||
)
|
||||
self.call(building.CmdScript(), "", "Usage: ")
|
||||
self.call(
|
||||
building.CmdScript(),
|
||||
"= Obj",
|
||||
"To create a global script you need scripts/add <typeclass>.",
|
||||
building.CmdScripts(),
|
||||
"evennia.Dummy",
|
||||
"Global Script NOT Created "
|
||||
)
|
||||
self.call(building.CmdScript(), "Obj ", "dbref ")
|
||||
self.call(
|
||||
building.CmdScripts(),
|
||||
"evennia.scripts.scripts.DoNothing",
|
||||
"Global Script Created - sys_do_nothing "
|
||||
)
|
||||
self.call(building.CmdScripts(), "Obj ", "dbref ")
|
||||
|
||||
self.call(
|
||||
building.CmdScript(), "/start Obj", "1 scripts started on Obj"
|
||||
building.CmdScripts(), "/start Obj", "Script on Obj Started "
|
||||
) # we allow running start again; this should still happen
|
||||
self.call(building.CmdScript(), "/stop Obj", "Stopping script")
|
||||
self.call(building.CmdScripts(), "/stop Obj", "Script on Obj Stopped - ")
|
||||
|
||||
self.call(
|
||||
building.CmdScript(), "Obj = scripts.Script", "Script scripts.Script successfully added"
|
||||
building.CmdScripts(), "Obj = scripts.Script",
|
||||
"Script scripts.Script successfully added",
|
||||
inputs=["Y"]
|
||||
)
|
||||
self.call(
|
||||
building.CmdScript(),
|
||||
building.CmdScripts(),
|
||||
"/start Obj = scripts.Script",
|
||||
"Script scripts.Script could not be (re)started.",
|
||||
"Script on Obj Started ",
|
||||
inputs=["Y"]
|
||||
)
|
||||
self.call(
|
||||
building.CmdScript(),
|
||||
building.CmdScripts(),
|
||||
"/stop Obj = scripts.Script",
|
||||
"Script stopped and removed from object.",
|
||||
"Script on Obj Stopped ",
|
||||
inputs=["Y"]
|
||||
)
|
||||
self.call(
|
||||
building.CmdScripts(),
|
||||
"/delete Obj = scripts.Script",
|
||||
"Script on Obj Deleted ",
|
||||
inputs=["Y"]
|
||||
)
|
||||
self.call(
|
||||
building.CmdScripts(),
|
||||
"/delete evennia.scripts.scripts.DoNothing",
|
||||
"Global Script Deleted -"
|
||||
)
|
||||
|
||||
|
||||
def test_teleport(self):
|
||||
oid = self.obj1.id
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue