""" System commands """ import traceback import os, datetime import django, twisted from django.contrib.auth.models import User from src.server.sessionhandler import SESSIONS from src.scripts.models import ScriptDB from src.objects.models import ObjectDB from src.config.models import ConfigValue from src.utils import reloads, create, logger, utils, gametime from src.commands.default.muxcommand import MuxCommand class CmdReload(MuxCommand): """ Reload the system Usage: @reload This reloads the system modules and re-validates all scripts. """ key = "@reload" permissions = "cmd:reload" help_category = "System" def func(self): """ Reload the system. """ caller = self.caller reloads.reload_modules() max_attempts = 4 for attempt in range(max_attempts): # if reload modules take a long time, # we might end up in a situation where # the subsequent commands fail since they # can't find the reloads module (due to it # not yet fully loaded). So we retry a few # times before giving up. try: reloads.reload_scripts() reloads.reload_commands() break except AttributeError: if attempt < max_attempts-1: caller.msg(" Waiting for modules(s) to finish (%s) ..." % attempt) else: string = " ... The module(s) took too long to reload, " string += "\n so the remaining reloads where skipped." string += "\n Re-run @reload again when modules have fully " string += "\n re-initialized." caller.msg(string) class CmdPy(MuxCommand): """ Execute a snippet of python code Usage: @py In this limited python environment. available_vars: 'self','me' : caller 'here' : caller.location 'obj' : dummy obj instance 'script': dummy script instance 'config': dummy conf instance 'ObjectDB' : ObjectDB class 'ScriptDB' : ScriptDB class 'ConfigValue' ConfigValue class only two variables are defined: 'self'/'me' which refers to one's own object, and 'here' which refers to the current location. """ key = "@py" aliases = ["!"] permissions = "cmd:py" help_category = "System" def func(self): "hook function" caller = self.caller pycode = self.args if not pycode: string = "Usage: @py " caller.msg(string) return # create temporary test objects for playing with script = create.create_script("src.scripts.scripts.DoNothing", 'testscript') obj = create.create_object("src.objects.objects.Object", 'testobject') conf = ConfigValue() # used to access conf values available_vars = {'self':caller, 'me':caller, 'here':caller.location, 'obj':obj, 'script':script, 'config':conf, 'ObjectDB':ObjectDB, 'ScriptDB':ScriptDB, 'ConfigValue':ConfigValue} caller.msg(">>> %s" % pycode) try: ret = eval(pycode, {}, available_vars) ret = "<<< %s" % str(ret) except Exception: try: exec(pycode, {}, available_vars) ret = "<<< Done." except Exception: errlist = traceback.format_exc().split('\n') if len(errlist) > 4: errlist = errlist[4:] ret = "\n".join("<<< %s" % line for line in errlist if line) caller.msg(ret) obj.delete() script.delete() class CmdListScripts(MuxCommand): """ Operate on scripts. Usage: @scripts[/switches] [] Switches: stop - stops an existing script validate - run a validation on the script(s) 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 an 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. """ key = "@scripts" aliases = "@listscripts" permissions = "cmd:listscripts" help_category = "System" def format_script_list(self, scripts): "Takes a list of scripts and formats the output." if not scripts: return "" table = [["id"], ["obj"], ["key"],["intval"],["next"],["rept"], ["db"],["typeclass"],["desc"]] for script in scripts: table[0].append(script.id) if not hasattr(script, 'obj') or not script.obj: table[1].append("") else: table[1].append(script.obj.key) table[2].append(script.key) if not hasattr(script, 'interval') or script.interval < 0: table[3].append("--") else: table[3].append("%ss" % script.interval) next = script.time_until_next_repeat() if not next: table[4].append("--") else: table[4].append("%ss" % next) if not hasattr(script, 'repeats') or not script.repeats: table[5].append("--") else: table[5].append("%ss" % script.repeats) if script.persistent: table[6].append("*") else: table[6].append("-") typeclass_path = script.typeclass_path.rsplit('.', 1) table[7].append("%s" % typeclass_path[-1]) table[8].append(script.desc) ftable = utils.format_table(table) string = "" for irow, row in enumerate(ftable): if irow == 0: srow = "\n" + "".join(row) srow = "{w%s{n" % srow.rstrip() else: srow = "\n" + "{w%s{n" % row[0] + "".join(row[1:]) string += srow.rstrip() return string.strip() def func(self): "implement method" caller = self.caller args = self.args string = "" if args: # test first if this is a script match scripts = ScriptDB.objects.get_all_scripts(key=args) if not scripts: # try to find an object instead. objects = ObjectDB.objects.object_search(caller, args, global_search=True) if objects: scripts = [] for obj in objects: # get all scripts on the object(s) scripts.extend(ScriptDB.objects.get_all_scripts_on_obj(obj)) else: # we want all scripts. scripts = ScriptDB.objects.get_all_scripts() if not scripts: string = "No scripts found with a key '%s', or on an object named '%s'." % (args, args) caller.msg(string) return if self.switches and self.switches[0] in ('stop', 'del', 'delete'): # we want to delete something if not scripts: string = "No scripts/objects matching '%s'. " % args string += "Be more specific." elif len(scripts) == 1: # we have a unique match! string = "Stopping script '%s'." % scripts[0].key scripts[0].stop() ScriptDB.objects.validate() #just to be sure all is synced else: # multiple matches. string = "Multiple script matches. Please refine your search:\n" string += self.format_script_list(scripts) elif self.switches and self.switches[0] in ("validate", "valid", "val"): # run validation on all found scripts nr_started, nr_stopped = ScriptDB.objects.validate(scripts=scripts) string = "Validated %s scripts. " % ScriptDB.objects.all().count() string += "Started %s and stopped %s scripts." % (nr_started, nr_stopped) else: # No stopping or validation. We just want to view things. string = self.format_script_list(scripts) caller.msg(string) class CmdListObjects(MuxCommand): """ Give a summary of object types in database Usage: @objects [] Gives statictics on objects in database as well as a list of latest objects in database. If not given, defaults to 10. """ key = "@objects" aliases = ["@listobjects", "@listobjs"] permissions = "cmd:listobjects" help_category = "System" def func(self): "Implement the command" caller = self.caller if self.args and self.args.isdigit(): nlim = int(self.args) else: nlim = 10 dbtotals = ObjectDB.objects.object_totals() #print dbtotals string = "\n{wDatase Object totals:{n" table = [["Count"], ["Typeclass"]] for path, count in dbtotals.items(): table[0].append(count) table[1].append(path) ftable = utils.format_table(table, 3) for irow, row in enumerate(ftable): srow = "\n" + "".join(row) srow = srow.rstrip() if irow == 0: srow = "{w%s{n" % srow string += srow string += "\n\n{wLast %s Objects created:{n" % nlim objs = list(ObjectDB.objects.all())[-nlim:] table = [["Created"], ["dbref"], ["name"], ["typeclass"]] for i, obj in enumerate(objs): table[0].append(utils.datetime_format(obj.date_created)) table[1].append(obj.dbref) table[2].append(obj.key) table[3].append(str(obj.typeclass)) ftable = utils.format_table(table, 5) for irow, row in enumerate(ftable): srow = "\n" + "".join(row) srow = srow.rstrip() if irow == 0: srow = "{w%s{n" % srow string += srow caller.msg(string) class CmdService(MuxCommand): """ @service - manage services Usage: @service[/switch] Switches: start - activates a service stop - stops a service list - shows all available services Service management system. Allows for the listing, starting, and stopping of services. """ key = "@service" permissions = "cmd:service" help_category = "System" def func(self): "Implement command" caller = self.caller switches = self.switches if not switches or \ switches[0] not in ["list","start","stop"]: caller.msg("Usage: @service/ [service]") return switch = switches[0] # get all services sessions = caller.sessions if not sessions: return service_collection = sessions[0].server.service_collection if switch == "list": # Just display the list of installed services and their # status, then exit. string = "-" * 40 string += "\nService Listing" for service in service_collection.services: if service.running: status = 'Running' else: status = 'Inactive' string += '\n * %s (%s)' % (service.name, status) string += "\n" + "-" * 40 caller.msg(string) 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.' caller.msg(string) return if switch == "stop": # Stopping a service gracefully closes it and disconnects # any connections (if applicable). if not service.running: caller.msg('That service is not currently running.') return # We don't want to kill the main Evennia TCPServer services # here. If wanting to kill a listening port, one needs to # do it through settings.py and a restart. if service.name[:7] == 'Evennia': string = "You can not stop Evennia TCPServer services this way." string += "\nTo e.g. remove a listening port, change settings file and restart." caller.msg(string) return #comsys.cemit_mudinfo("%s is *Stopping* the service '%s'." % (sname, service.name)) #TODO! service.stopService() return if switch == "start": #Starts a service. if service.running: caller.msg('That service is already running.') return #comsys.cemit_mudinfo("%s is *Starting* the service '%s'." % (sname,service.name)) #TODO! service.startService() class CmdShutdown(MuxCommand): """ @shutdown Usage: @shutdown [announcement] Shut the game server down gracefully. """ key = "@shutdown" permissions = "cmd:shutdown" help_category = "System" def func(self): "Define function" try: session = self.caller.sessions[0] except Exception: return self.caller.msg('Shutting down server ...') announcement = "\nServer is being SHUT DOWN!\n" if self.args: announcement += "%s\n" % self.args logger.log_infomsg('Server shutdown by %s.' % self.caller.name) SESSIONS.announce_all(announcement) SESSIONS.server.shutdown() class CmdVersion(MuxCommand): """ @version - game version Usage: @version Display the game version info. """ key = "@version" help_category = "System" def func(self): "Show the version" version = utils.get_evennia_version() string = "-"*50 +"\n\r" string += " {cEvennia{n %s\n\r" % version string += " (Django %s, " % (django.get_version()) string += " Twisted %s)\n\r" % (twisted.version.short()) string += "-"*50 self.caller.msg(string) class CmdTime(MuxCommand): """ @time Usage: @time Server local time. """ key = "@time" aliases = "@uptime" permissions = "cmd:time" help_category = "System" def func(self): "Show times." string1 = "\nCurrent server uptime: \t" string1 += "{w%s{n" % (utils.time_format(gametime.uptime(format=False), 2)) string2 = "\nTotal server running time: \t" string2 += "{w%s{n" % (utils.time_format(gametime.runtime(format=False), 2)) string3 = "\nTotal in-game time (realtime x %g):\t" % (gametime.TIMEFACTOR) string3 += "{w%s{n" % (utils.time_format(gametime.gametime(format=False), 2)) string4 = "\nServer time stamp: \t" string4 += "{w%s{n" % (str(datetime.datetime.now())) string5 = "" if not utils.host_os_is('nt'): # os.getloadavg() is not available on Windows. loadavg = os.getloadavg() string5 += "\nServer load (per minute): \t" string5 += "{w%g%%{n" % (100 * loadavg[0]) string = "%s%s%s%s%s" % (string1, string2, string3, string4, string5) self.caller.msg(string) class CmdList(MuxCommand): """ @list - list info Usage: @list