diff --git a/contrib/talking_npc.py b/contrib/talking_npc.py index 0128d0b722..61025407f5 100644 --- a/contrib/talking_npc.py +++ b/contrib/talking_npc.py @@ -23,17 +23,15 @@ mob implementation. """ +from ev import Object, CmdSet, default_cmds from contrib import menusystem -from game.gamesrc.objects.baseobjects import Object -from game.gamesrc.commands.basecmdset import CmdSet -from game.gamesrc.commands.basecommand import MuxCommand # # The talk command # -class CmdTalk(MuxCommand): +class CmdTalk(default_cmds.MuxCommand): """ talks to an npc diff --git a/contrib/tutorial_world/mob.py b/contrib/tutorial_world/mob.py index f210e15bcf..87f6611b0c 100644 --- a/contrib/tutorial_world/mob.py +++ b/contrib/tutorial_world/mob.py @@ -241,6 +241,10 @@ class Enemy(Mob): elif not ostring: ostring = "%s falls to the ground!" % target.key self.location.msg_contents(ostring, exclude=[target]) + # Pursue any stragglers after the battle + self.battle_mode = False + self.roam_mode = False + self.pursue_mode = True else: # no players found, this could mean they have fled. Switch to pursue mode. self.battle_mode = False diff --git a/contrib/tutorial_world/objects.py b/contrib/tutorial_world/objects.py index 0a184bf94d..08a9f1ab3e 100644 --- a/contrib/tutorial_world/objects.py +++ b/contrib/tutorial_world/objects.py @@ -773,8 +773,8 @@ class Weapon(TutorialObject): super(Weapon, self).at_object_creation() self.db.hit = 0.4 # hit chance self.db.parry = 0.8 # parry chance - self.damage = 8.0 - self.magic = False + self.db.damage = 8.0 + self.db.magic = False self.cmdset.add_default(CmdSetWeapon, permanent=True) def reset(self): diff --git a/src/commands/cmdhandler.py b/src/commands/cmdhandler.py index 05fcf2d7a0..0931f20d4e 100644 --- a/src/commands/cmdhandler.py +++ b/src/commands/cmdhandler.py @@ -42,7 +42,7 @@ from django.conf import settings from src.comms.channelhandler import CHANNELHANDLER from src.utils import logger, utils from src.commands.cmdparser import at_multimatch_cmd -from src.utils.utils import string_suggestions +from src.utils.utils import string_suggestions, make_iter from django.utils.translation import ugettext as _ @@ -254,7 +254,7 @@ def cmdhandler(caller, raw_string, testing=False, sessid=None): cmd.raw_string = unformatted_raw_string if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'): - # cmd.obj are automatically made available. + # cmd.obj is automatically made available. # we make sure to validate its scripts. yield cmd.obj.scripts.validate() @@ -270,6 +270,13 @@ def cmdhandler(caller, raw_string, testing=False, sessid=None): # (return value is normally None) ret = yield cmd.func() + if hasattr(cmd, "func_parts"): + # yield on command parts (for multi-part delayed commands) + for func_part in make_iter(cmd.func_parts): + err = yield func_part() + # returning anything but a deferred/None will kill the chain + if err: break + # post-command hook yield cmd.at_post_cmd() diff --git a/src/commands/default/system.py b/src/commands/default/system.py index 48f4eed591..323652fb5f 100644 --- a/src/commands/default/system.py +++ b/src/commands/default/system.py @@ -35,7 +35,7 @@ class CmdReload(MuxCommand): Reload the system Usage: - @reload + @reload [reason] This restarts the server. The Portal is not affected. Non-persistent scripts will survive a @reload (use @@ -49,7 +49,10 @@ class CmdReload(MuxCommand): """ Reload the system. """ - SESSIONS.announce_all(" Server restarting ...") + reason = "" + if self.args: + reason = "(Reason: %s) " % self.args.rstrip(".") + SESSIONS.announce_all(" Server restarting %s..." % reason) SESSIONS.server.shutdown(mode='reload') class CmdReset(MuxCommand): @@ -94,7 +97,8 @@ class CmdShutdown(MuxCommand): def func(self): "Define function" try: - session = self.caller.sessions[0] + # Only allow shutdown if caller has session + self.caller.sessions[0] except Exception: return self.caller.msg('Shutting down server ...') @@ -427,12 +431,14 @@ class CmdService(MuxCommand): Switches: list - shows all available services (default) - start - activates a service - stop - stops a service + start - activates or reactivate a service + stop - stops/inactivate a service (can often be restarted) + delete - tries to permanently remove a service Service management system. Allows for the listing, starting, and stopping of services. If no switches - are given, services will be listed. + are given, services will be listed. Note that to operate on the + service you have to supply the full (green or red) name as given in the list. """ key = "@service" @@ -446,8 +452,8 @@ class CmdService(MuxCommand): caller = self.caller switches = self.switches - if switches and switches[0] not in ["list", "start", "stop"]: - caller.msg("Usage: @service/ [service]") + if switches and switches[0] not in ("list", "start", "stop", "delete"): + caller.msg("Usage: @service/ [servicename]") return # get all services @@ -460,7 +466,7 @@ class CmdService(MuxCommand): # Just display the list of installed services and their # status, then exit. string = "-" * 78 - string += "\n{wServices{n (use @services/start|stop):" + string += "\n{wServices{n (use @services/start|stop|delete):" for service in service_collection.services: if service.running: @@ -479,26 +485,35 @@ class CmdService(MuxCommand): service = service_collection.getServiceNamed(self.args) except Exception: string = 'Invalid service name. This command is case-sensitive. ' - string += 'See @service/list for valid services.' + string += 'See @service/list for valid service name (enter the full name exactly).' caller.msg(string) return - if switches[0] == "stop": - # Stopping a service gracefully closes it and disconnects + 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': - string = "You seem to be shutting down a core Evennia* service. Note that" - string += "Stopping some TCP port services will *not* disconnect users *already*" + 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) - service.stopService() - caller.msg("Stopping service '%s'." % self.args) + 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": @@ -536,7 +551,7 @@ class CmdAbout(MuxCommand): {cEvennia{n %s{n MUD/MUX/MU* development system - {wLicence{n Artistic Licence/GPL + {wLicence{n BSD 3-Clause Licence {wWeb{n http://www.evennia.com {wIrc{n #evennia on FreeNode {wForum{n http://www.evennia.com/discussions diff --git a/src/comms/imc2.py b/src/comms/imc2.py index 1f04c0502e..07cfac24d1 100644 --- a/src/comms/imc2.py +++ b/src/comms/imc2.py @@ -434,7 +434,7 @@ def create_connection(channel, imc2_channel): # how the evennia channel will be able to contact this protocol in reverse send_code = "from src.comms.imc2 import IMC2_CLIENT\n" send_code += "data={'channel':from_channel}\n" - send_code += "IMC2_CLIENT.msg_imc2(message, from_obj=from_obj, data=data)\n" + send_code += "IMC2_CLIENT.msg_imc2(message, senders=[self])\n" conn = ExternalChannelConnection(db_channel=channel, db_external_key=key, db_external_send_code=send_code, db_external_config=config) conn.save() diff --git a/src/objects/models.py b/src/objects/models.py index 36955602a0..61ddc38139 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -35,7 +35,7 @@ from django.utils.translation import ugettext as _ #__all__ = ("ObjAttribute", "Alias", "ObjectNick", "ObjectDB") - +_ScriptDB = None _AT_SEARCH_RESULT = variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1)) _GA = object.__getattribute__ @@ -932,6 +932,10 @@ class ObjectDB(TypedObject): objects to their respective home locations, as well as clean up all exits to/from the object. """ + global _ScriptDB + if not _ScriptDB: + from src.scripts.models import ScriptDB as _ScriptDB + if _GA(self, "delete_iter") > 0: # make sure to only call delete once on this object # (avoid recursive loops) @@ -956,8 +960,10 @@ class ObjectDB(TypedObject): _SA(_GA(self, "player"), "character", None) _SA(self, "player", None) - for script in _GA(self, "scripts").all(): + for script in _ScriptDB.objects.get_all_scripts_on_obj(self): script.stop() + #for script in _GA(self, "scripts").all(): + # script.stop() # if self.player: # self.player.user.is_active = False diff --git a/src/utils/utils.py b/src/utils/utils.py index 5d1fef44ac..f3296c2147 100644 --- a/src/utils/utils.py +++ b/src/utils/utils.py @@ -496,7 +496,7 @@ def uses_database(name="sqlite3"): engine = settings.DATABASE_ENGINE return engine == "django.db.backends.%s" % name -def delay(to_return, delay=2, callback=None): +def delay(delay=2, retval=None, callback=None): """ Delay the return of a value. Inputs: @@ -508,7 +508,7 @@ def delay(to_return, delay=2, callback=None): """ d = defer.Deferred() callb = callback or d.callback - reactor.callLater(delay, callb, to_return) + reactor.callLater(delay, callb, retval) return d _FROM_MODEL_MAP = None