Expanded the dummy runner, making it more realistically mimicking real Players by spreading out its access usage.
This commit is contained in:
parent
440d403327
commit
fd857f5c86
3 changed files with 49 additions and 15 deletions
|
|
@ -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 random.random() > CHANCE_OF_ACTION:
|
||||||
|
return
|
||||||
if self.istep == 0:
|
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
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue