Expanded the dummy runner, making it more realistically mimicking real Players by spreading out its access usage.

This commit is contained in:
Griatch 2012-02-22 12:44:51 +01:00
parent 440d403327
commit fd857f5c86
3 changed files with 49 additions and 15 deletions

View file

@ -93,6 +93,19 @@ Setup:
from a python prompt (see Python's manual on cProfiler). 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 # Helper functions
#------------------------------------------------------------ #------------------------------------------------------------
@ -134,7 +147,9 @@ class DummyClient(telnet.StatefulTelnetProtocol):
self._actions = self.factory.actions self._actions = self.factory.actions
self._echo_brief = self.factory.verbose == 1 self._echo_brief = self.factory.verbose == 1
self._echo_all = self.factory.verbose == 2 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 # start client tick
d = LoopingCall(self.step) d = LoopingCall(self.step)
@ -147,7 +162,7 @@ class DummyClient(telnet.StatefulTelnetProtocol):
def connectionLost(self, reason): def connectionLost(self, reason):
"loosing the connection" "loosing the connection"
print " ** client %i lost connection." % self.cid #print " ** client %i lost connection." % self.cid
def error(self, err): def error(self, err):
"error callback" "error callback"
@ -157,25 +172,33 @@ class DummyClient(telnet.StatefulTelnetProtocol):
"produces a unique id, also between clients" "produces a unique id, also between clients"
return OID.next() 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): def step(self):
""" """
Perform a step. This is called repeatedly by the runner Perform a step. This is called repeatedly by the runner
and causes the client to issue commands to the server. and causes the client to issue commands to the server.
This holds all "intelligence" of the dummy client. 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] cfunc = self._actions[0]
else: # random selection using cumulative probabilities else: # random selection using cumulative probabilities
rand = random.random() 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) # launch the action (don't hide tracebacks)
cmd, report = cfunc(self) cmd, report = cfunc(self)
# handle the result # handle the result
cmd = "\n".join(makeiter(cmd)) 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) print "client %i %s" % (self.cid, report)
self.sendLine(cmd) self.sendLine(cmd)
self.istep += 1 self.istep += 1 # only steps up if an action is taken
class DummyFactory(protocol.ClientFactory): class DummyFactory(protocol.ClientFactory):
protocol = DummyClient 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 # validating and preparing the action tuple
# make sure the probabilities add up to 1 # make sure the probabilities add up to 1
pratio = 1.0 / sum(tup[0] for tup in actions[1:]) pratio = 1.0 / sum(tup[0] for tup in actions[2:])
flogin, probs, cfuncs = actions[0], [tup[0] * pratio for tup in actions[1:]], [tup[1] for tup in actions[1:]] 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 # 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))] cprobs = [sum(v for i,v in enumerate(probs) if i<=k) for k in range(len(probs))]
# rebuild a new, optimized action structure # 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) # setting up all clients (they are automatically started)
factory = DummyFactory(actions, timestep, verbose) factory = DummyFactory(actions, timestep, verbose)
@ -227,9 +250,9 @@ if __name__ == '__main__':
options, args = parser.parse_args() options, args = parser.parse_args()
nargs = len(args) nargs = len(args)
nclients = 1 nclients = DEFAULT_NCLIENTS
timestep = 5 timestep = DEFAULT_TIMESTEP
port = 4000 port = DEFAULT_PORT
try: try:
if not args : raise Exception if not args : raise Exception
if nargs > 0: nclients = max(1, int(args[0])) if nargs > 0: nclients = max(1, int(args[0]))
@ -248,6 +271,8 @@ if __name__ == '__main__':
actions = utils.mod_import(action_modpath, "ACTIONS") actions = utils.mod_import(action_modpath, "ACTIONS")
print "Connecting %i dummy client(s) to port %i using a %i second timestep ... " % (nclients, port, timestep) 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, start_all_dummy_clients(actions, nclients, timestep, port,
verbose=options.verbose) verbose=options.verbose)
print "... dummy client runner finished." ttot = time.time() - t0
print "... dummy client runner finished after %i seconds." % ttot

View file

@ -66,7 +66,11 @@ def c_login(client):
'@dig %s' % START_ROOM % client.cid, '@dig %s' % START_ROOM % client.cid,
'@teleport %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): def c_looks(client):
"looks at various objects" "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 # 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 # 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, # action_function). The probablities should normally sum up to 1,
# otherwise the system will normalize them. # otherwise the system will normalize them.
# #
ACTIONS = ( c_login, ACTIONS = ( c_login,
c_logout,
(0.2, c_looks), (0.2, c_looks),
(0.1, c_examines), (0.1, c_examines),
(0.2, c_help), (0.2, c_help),

View file

@ -12,6 +12,7 @@ Evennia changes:
""" """
from twisted.internet import reactor
from django.db.models.base import Model, ModelBase from django.db.models.base import Model, ModelBase
from manager import SharedMemoryManager from manager import SharedMemoryManager
@ -134,6 +135,8 @@ class SharedMemoryModel(Model):
flush_cached_instance = classmethod(flush_cached_instance) flush_cached_instance = classmethod(flush_cached_instance)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
#ssave = super(SharedMemoryModel, self).save
#reactor.callInThread(ssave, *args, **kwargs)
super(SharedMemoryModel, self).save(*args, **kwargs) super(SharedMemoryModel, self).save(*args, **kwargs)
self.__class__.cache_instance(self) self.__class__.cache_instance(self)