mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
337 lines
9.3 KiB
Python
337 lines
9.3 KiB
Python
"""
|
|
Settings and actions for the dummyrunner
|
|
|
|
This module defines dummyrunner settings and sets up
|
|
the actions available to dummy accounts.
|
|
|
|
The settings are global variables:
|
|
|
|
- TIMESTEP - time in seconds between each 'tick'. 1 is a good start.
|
|
- CHANCE_OF_ACTION - chance 0-1 of action happening. Default is 0.5.
|
|
- CHANCE_OF_LOGIN - chance 0-1 of login happening. 0.01 is a good number.
|
|
- TELNET_PORT - port to use, defaults to settings.TELNET_PORT
|
|
- ACTIONS - see below
|
|
|
|
ACTIONS is a tuple
|
|
|
|
```python
|
|
(login_func, logout_func, (0.3, func1), (0.1, func2) ... )
|
|
|
|
```
|
|
|
|
where the first entry is the function to call on first connect, with a
|
|
chance of occurring given by CHANCE_OF_LOGIN. This function is usually
|
|
responsible for logging in the account. The second entry is always
|
|
called when the dummyrunner disconnects from the server and should
|
|
thus issue a logout command. The other entries are tuples (chance,
|
|
func). They are picked randomly, their commonality based on the
|
|
cumulative chance given (the chance is normalized between all options
|
|
so if will still work also if the given chances don't add up to 1).
|
|
|
|
The PROFILE variable define pre-made ACTION tuples for convenience.
|
|
|
|
Each function should return an iterable of one or more command-call
|
|
strings (like "look here"), so each can group multiple command operations.
|
|
|
|
An action-function is called with a "client" argument which is a
|
|
reference to the dummy client currently performing the action.
|
|
|
|
The client object has the following relevant properties and methods:
|
|
|
|
- key - an optional client key. This is only used for dummyrunner output.
|
|
Default is "Dummy-<cid>"
|
|
- cid - client id
|
|
- gid - globally unique id, hashed with time stamp
|
|
- istep - the current step
|
|
- exits - an empty list. Can be used to store exit names
|
|
- objs - an empty list. Can be used to store object names
|
|
- counter() - returns a unique increasing id, hashed with time stamp
|
|
to make it unique also between dummyrunner instances.
|
|
|
|
The return should either be a single command string or a tuple of
|
|
command strings. This list of commands will always be executed every
|
|
TIMESTEP with a chance given by CHANCE_OF_ACTION by in the order given
|
|
(no randomness) and allows for setting up a more complex chain of
|
|
commands (such as creating an account and logging in).
|
|
|
|
----
|
|
|
|
"""
|
|
import random
|
|
import string
|
|
|
|
# Dummy runner settings
|
|
|
|
# Time between each dummyrunner "tick", in seconds. Each dummy
|
|
# will be called with this frequency.
|
|
TIMESTEP = 1
|
|
# TIMESTEP = 0.025 # 40/s
|
|
|
|
# Chance of a dummy actually performing an action on a given tick.
|
|
# This spreads out usage randomly, like it would be in reality.
|
|
CHANCE_OF_ACTION = 0.5
|
|
|
|
# Chance of a currently unlogged-in dummy performing its login
|
|
# action every tick. This emulates not all accounts logging in
|
|
# at exactly the same time.
|
|
CHANCE_OF_LOGIN = 0.01
|
|
|
|
# Which telnet port to connect to. If set to None, uses the first
|
|
# default telnet port of the running server.
|
|
TELNET_PORT = None
|
|
|
|
|
|
# Setup actions tuple
|
|
|
|
# some convenient templates
|
|
|
|
DUMMY_NAME = "Dummy_{gid}"
|
|
DUMMY_PWD = (''.join(random.choice(string.ascii_letters + string.digits)
|
|
for _ in range(20)) + "-{gid}")
|
|
START_ROOM = "testing_room_start_{gid}"
|
|
ROOM_TEMPLATE = "testing_room_%s"
|
|
EXIT_TEMPLATE = "exit_%s"
|
|
OBJ_TEMPLATE = "testing_obj_%s"
|
|
TOBJ_TEMPLATE = "testing_button_%s"
|
|
TOBJ_TYPECLASS = "contrib.tutorial_examples.red_button.RedButton"
|
|
|
|
|
|
# action function definitions (pick and choose from
|
|
# these to build a client "usage profile"
|
|
|
|
# login/logout
|
|
|
|
def c_login(client):
|
|
"logins to the game"
|
|
# we always use a new client name
|
|
cname = DUMMY_NAME.format(gid=client.gid)
|
|
cpwd = DUMMY_PWD.format(gid=client.gid)
|
|
room_name = START_ROOM.format(gid=client.gid)
|
|
|
|
# we assign the dummyrunner cmdsert to ourselves so # we can use special commands
|
|
add_cmdset = (
|
|
"py from evennia.server.profiling.dummyrunner import DummyRunnerCmdSet;"
|
|
"self.cmdset.add(DummyRunnerCmdSet, persistent=False)"
|
|
)
|
|
|
|
# create character, log in, then immediately dig a new location and
|
|
# teleport it (to keep the login room clean)
|
|
cmds = (
|
|
f"create {cname} {cpwd}",
|
|
f"yes", # to confirm creation
|
|
f"connect {cname} {cpwd}",
|
|
f"dig {room_name}",
|
|
f"teleport {room_name}",
|
|
add_cmdset,
|
|
)
|
|
return cmds
|
|
|
|
|
|
def c_login_nodig(client):
|
|
"logins, don't dig its own room"
|
|
cname = DUMMY_NAME.format(gid=client.gid)
|
|
cpwd = DUMMY_PWD.format(gid=client.gid)
|
|
cmds = (
|
|
f"create {cname} {cpwd}",
|
|
f"connect {cname} {cpwd}"
|
|
)
|
|
return cmds
|
|
|
|
|
|
def c_logout(client):
|
|
"logouts of the game"
|
|
return ("quit",)
|
|
|
|
|
|
# random commands
|
|
|
|
|
|
def c_looks(client):
|
|
"looks at various objects"
|
|
cmds = ["look %s" % obj for obj in client.objs]
|
|
if not cmds:
|
|
cmds = ["look %s" % exi for exi in client.exits]
|
|
if not cmds:
|
|
cmds = ("look",)
|
|
return cmds
|
|
|
|
|
|
def c_examines(client):
|
|
"examines various objects"
|
|
cmds = ["examine %s" % obj for obj in client.objs]
|
|
if not cmds:
|
|
cmds = ["examine %s" % exi for exi in client.exits]
|
|
if not cmds:
|
|
cmds = ("examine me",)
|
|
return cmds
|
|
|
|
|
|
def c_idles(client):
|
|
"idles"
|
|
cmds = ("idle", "idle")
|
|
return cmds
|
|
|
|
|
|
def c_help(client):
|
|
"reads help files"
|
|
cmds = ("help", "dummyrunner_echo_response",)
|
|
return cmds
|
|
|
|
|
|
def c_digs(client):
|
|
"digs a new room, storing exit names on client"
|
|
roomname = ROOM_TEMPLATE % client.counter()
|
|
exitname1 = EXIT_TEMPLATE % client.counter()
|
|
exitname2 = EXIT_TEMPLATE % client.counter()
|
|
client.exits.extend([exitname1, exitname2])
|
|
return ("dig/tel %s = %s, %s" % (roomname, exitname1, exitname2),)
|
|
|
|
|
|
def c_creates_obj(client):
|
|
"creates normal objects, storing their name on client"
|
|
objname = OBJ_TEMPLATE % client.counter()
|
|
client.objs.append(objname)
|
|
cmds = (
|
|
"create %s" % objname,
|
|
'desc %s = "this is a test object' % objname,
|
|
"set %s/testattr = this is a test attribute value." % objname,
|
|
"set %s/testattr2 = this is a second test attribute." % objname,
|
|
)
|
|
return cmds
|
|
|
|
|
|
def c_creates_button(client):
|
|
"creates example button, storing name on client"
|
|
objname = TOBJ_TEMPLATE % client.counter()
|
|
client.objs.append(objname)
|
|
cmds = ("create %s:%s" % (objname, TOBJ_TYPECLASS), "desc %s = test red button!" % objname)
|
|
return cmds
|
|
|
|
|
|
def c_socialize(client):
|
|
"socializechats on channel"
|
|
cmds = (
|
|
"pub Hello!",
|
|
"say Yo!",
|
|
"emote stands looking around.",
|
|
)
|
|
return cmds
|
|
|
|
|
|
def c_moves(client):
|
|
"moves to a previously created room, using the stored exits"
|
|
cmds = client.exits # try all exits - finally one will work
|
|
return ("look",) if not cmds else cmds
|
|
|
|
|
|
def c_moves_n(client):
|
|
"move through north exit if available"
|
|
return ("north",)
|
|
|
|
|
|
def c_moves_s(client):
|
|
"move through south exit if available"
|
|
return ("south",)
|
|
|
|
|
|
def c_measure_lag(client):
|
|
"""
|
|
Special dummyrunner command, injected in c_login. It measures
|
|
response time. Including this in the ACTION tuple will give more
|
|
dummyrunner output about just how fast commands are being processed.
|
|
|
|
The dummyrunner will treat this special and inject the
|
|
{timestamp} just before sending.
|
|
|
|
"""
|
|
return ("dummyrunner_echo_response {timestamp}",)
|
|
|
|
|
|
# Action profile (required)
|
|
|
|
# Some pre-made profiles to test. To make your own, just assign a tuple to ACTIONS.
|
|
#
|
|
# idler - does nothing after logging in
|
|
# looker - just looks around
|
|
# normal_player - moves around, reads help, looks around (digs rarely) (spammy)
|
|
# normal_builder - digs now and then, examines, creates objects, moves
|
|
# heavy_builder - digs and creates a lot, moves and examines
|
|
# socializing_builder - builds a lot, creates help entries, moves, chat (spammy)
|
|
# only_digger - extreme builder that only digs room after room
|
|
|
|
PROFILE = "looker"
|
|
|
|
|
|
if PROFILE == 'idler':
|
|
ACTIONS = (
|
|
c_login,
|
|
c_logout,
|
|
(0.9, c_idles),
|
|
(0.1, c_measure_lag),
|
|
)
|
|
elif PROFILE == 'looker':
|
|
ACTIONS = (
|
|
c_login,
|
|
c_logout,
|
|
(0.8, c_looks),
|
|
(0.2, c_measure_lag)
|
|
)
|
|
elif PROFILE == 'normal_player':
|
|
ACTIONS = (
|
|
c_login,
|
|
c_logout,
|
|
(0.01, c_digs),
|
|
(0.29, c_looks),
|
|
(0.2, c_help),
|
|
(0.3, c_moves),
|
|
(0.05, c_socialize),
|
|
(0.1, c_measure_lag)
|
|
)
|
|
elif PROFILE == 'normal_builder':
|
|
ACTIONS = (
|
|
c_login,
|
|
c_logout,
|
|
(0.5, c_looks),
|
|
(0.08, c_examines),
|
|
(0.1, c_help),
|
|
(0.01, c_digs),
|
|
(0.01, c_creates_obj),
|
|
(0.2, c_moves),
|
|
(0.1, c_measure_lag)
|
|
)
|
|
elif PROFILE == 'heavy_builder':
|
|
ACTIONS = (
|
|
c_login,
|
|
c_logout,
|
|
(0.1, c_looks),
|
|
(0.1, c_examines),
|
|
(0.2, c_help),
|
|
(0.1, c_digs),
|
|
(0.1, c_creates_obj),
|
|
(0.2, c_moves),
|
|
(0.1, c_measure_lag)
|
|
)
|
|
elif PROFILE == 'socializing_builder':
|
|
ACTIONS = (
|
|
c_login,
|
|
c_logout,
|
|
(0.1, c_socialize),
|
|
(0.1, c_looks),
|
|
(0.1, c_help),
|
|
(0.1, c_creates_obj),
|
|
(0.2, c_digs),
|
|
(0.3, c_moves),
|
|
(0.1, c_measure_lag)
|
|
)
|
|
elif PROFILE == 'only_digger':
|
|
ACTIONS = (
|
|
c_login,
|
|
c_logout,
|
|
(0.9, c_digs),
|
|
(0.1, c_measure_lag)
|
|
)
|
|
|
|
else:
|
|
print("No dummyrunner ACTION profile defined.")
|
|
import sys
|
|
sys.exit()
|