diff --git a/src/utils/dummyrunner.py b/src/utils/dummyrunner.py index 50e3f198e..2bf4848cc 100644 --- a/src/utils/dummyrunner.py +++ b/src/utils/dummyrunner.py @@ -93,6 +93,19 @@ Setup: from a python prompt (see Python's manual on cProfiler). """ +# number of clients to launch if no input is given on command line +DEFAULT_NCLIENTS = 1 +# time between each 'tick', in seconds, if not set on command +# line. All launched clients will be called upon to possibly do an +# action with this frequency. +DEFAULT_TIMESTEP = 5 +# Port to use, if not specified on command line +DEFAULT_PORT = settings.TELNET_PORTS[0] +# chance of an action happening, per timestep. This helps to +# spread out usage randomly, like it would be in reality. +CHANCE_OF_ACTION = 0.1 + + #------------------------------------------------------------ # Helper functions #------------------------------------------------------------ @@ -134,7 +147,9 @@ class DummyClient(telnet.StatefulTelnetProtocol): self._actions = self.factory.actions self._echo_brief = self.factory.verbose == 1 self._echo_all = self.factory.verbose == 2 - print " ** client %i connected successfully." % self.cid + #print " ** client %i connected." % self.cid + + reactor.addSystemEventTrigger('before', 'shutdown', self.logout) # start client tick d = LoopingCall(self.step) @@ -147,7 +162,7 @@ class DummyClient(telnet.StatefulTelnetProtocol): def connectionLost(self, reason): "loosing the connection" - print " ** client %i lost connection." % self.cid + #print " ** client %i lost connection." % self.cid def error(self, err): "error callback" @@ -157,25 +172,33 @@ class DummyClient(telnet.StatefulTelnetProtocol): "produces a unique id, also between clients" return OID.next() + def logout(self): + "Causes the client to log out of the server. Triggered by ctrl-c signal." + cmd, report = self._actions[1](self) + print "client %i %s (%s actions)" % (self.cid, report, self.istep) + self.sendLine(cmd) + def step(self): """ Perform a step. This is called repeatedly by the runner and causes the client to issue commands to the server. This holds all "intelligence" of the dummy client. """ - if self.istep == 0: + if random.random() > CHANCE_OF_ACTION: + return + if self.istep == 0: cfunc = self._actions[0] else: # random selection using cumulative probabilities rand = random.random() - cfunc = [func for cprob, func in self._actions[1] if cprob >= rand][0] + cfunc = [func for cprob, func in self._actions[2] if cprob >= rand][0] # launch the action (don't hide tracebacks) cmd, report = cfunc(self) # handle the result cmd = "\n".join(makeiter(cmd)) - if self._echo_brief or self._echo_all: + if self.istep == 0 or self._echo_brief or self._echo_all: print "client %i %s" % (self.cid, report) self.sendLine(cmd) - self.istep += 1 + self.istep += 1 # only steps up if an action is taken class DummyFactory(protocol.ClientFactory): protocol = DummyClient @@ -196,12 +219,12 @@ def start_all_dummy_clients(actions, nclients=1, timestep=5, telnet_port=4000, v # validating and preparing the action tuple # make sure the probabilities add up to 1 - pratio = 1.0 / sum(tup[0] for tup in actions[1:]) - flogin, probs, cfuncs = actions[0], [tup[0] * pratio for tup in actions[1:]], [tup[1] for tup in actions[1:]] + pratio = 1.0 / sum(tup[0] for tup in actions[2:]) + flogin, flogout, probs, cfuncs = actions[0], actions[1], [tup[0] * pratio for tup in actions[2:]], [tup[1] for tup in actions[2:]] # create cumulative probabilies for the random actions cprobs = [sum(v for i,v in enumerate(probs) if i<=k) for k in range(len(probs))] # rebuild a new, optimized action structure - actions = (flogin, zip(cprobs, cfuncs)) + actions = (flogin, flogout, zip(cprobs, cfuncs)) # setting up all clients (they are automatically started) factory = DummyFactory(actions, timestep, verbose) @@ -227,9 +250,9 @@ if __name__ == '__main__': options, args = parser.parse_args() nargs = len(args) - nclients = 1 - timestep = 5 - port = 4000 + nclients = DEFAULT_NCLIENTS + timestep = DEFAULT_TIMESTEP + port = DEFAULT_PORT try: if not args : raise Exception if nargs > 0: nclients = max(1, int(args[0])) @@ -248,6 +271,8 @@ if __name__ == '__main__': actions = utils.mod_import(action_modpath, "ACTIONS") print "Connecting %i dummy client(s) to port %i using a %i second timestep ... " % (nclients, port, timestep) + t0 = time.time() start_all_dummy_clients(actions, nclients, timestep, port, verbose=options.verbose) - print "... dummy client runner finished." + ttot = time.time() - t0 + print "... dummy client runner finished after %i seconds." % ttot diff --git a/src/utils/dummyrunner_actions.py b/src/utils/dummyrunner_actions.py index 9dd90bde1..c621b48aa 100644 --- a/src/utils/dummyrunner_actions.py +++ b/src/utils/dummyrunner_actions.py @@ -66,7 +66,11 @@ def c_login(client): '@dig %s' % START_ROOM % client.cid, '@teleport %s' % START_ROOM % client.cid) - return cmd, "logs into game as %s ..." % cname + return cmd, "logs in as %s ..." % cname + +def c_logout(client): + "logouts of the game" + return "@quit", "logs out" def c_looks(client): "looks at various objects" @@ -133,12 +137,14 @@ def c_moves(client): # # This is a tuple of client action functions. The first element is the # function the client should use to log into the game and move to -# STARTROOM . The following elements are 2-tuples of (probability, +# STARTROOM . The second element is the logout command, for cleanly +# exiting the mud. The following elements are 2-tuples of (probability, # action_function). The probablities should normally sum up to 1, # otherwise the system will normalize them. # ACTIONS = ( c_login, + c_logout, (0.2, c_looks), (0.1, c_examines), (0.2, c_help), diff --git a/src/utils/idmapper/base.py b/src/utils/idmapper/base.py index 3d088b0b8..5bb23af84 100755 --- a/src/utils/idmapper/base.py +++ b/src/utils/idmapper/base.py @@ -12,6 +12,7 @@ Evennia changes: """ +from twisted.internet import reactor from django.db.models.base import Model, ModelBase from manager import SharedMemoryManager @@ -134,6 +135,8 @@ class SharedMemoryModel(Model): flush_cached_instance = classmethod(flush_cached_instance) def save(self, *args, **kwargs): + #ssave = super(SharedMemoryModel, self).save + #reactor.callInThread(ssave, *args, **kwargs) super(SharedMemoryModel, self).save(*args, **kwargs) self.__class__.cache_instance(self)