Add a script to simulate heavy lobby traffic
Plus a bunch of changes which were necessary for the script to work: * The "simulate lobby activity" plugin now exits when the server is shut down. * The plugin now uses wesnoth.random() for random number generation. Math.random() uses a fixed seed, which would make all the clients perform the exact same actions. * Exposed wesnoth.random() to plugins to allow the change above. * --nogui no longer implies --wconsole on Windows. With implied --wconsole the clients attached themselves to the standard output of the Python script, which made it impossible to see the output of the script itself.
This commit is contained in:
parent
5d14459594
commit
e7c2105c48
6 changed files with 113 additions and 37 deletions
|
@ -21,10 +21,22 @@ local function create_game(context)
|
|||
context.create({})
|
||||
end
|
||||
|
||||
local function exit_game(context)
|
||||
local events, info
|
||||
|
||||
repeat
|
||||
context.quit({})
|
||||
events, context, info = coroutine.yield()
|
||||
until info.name == "titlescreen"
|
||||
|
||||
context.exit({code = 0})
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
return function()
|
||||
local events, context, info
|
||||
|
||||
wesnoth.preferences.new_lobby = true
|
||||
wesnoth.preferences.new_mp_ui = true
|
||||
|
||||
repeat
|
||||
events, context, info = coroutine.yield()
|
||||
|
@ -42,17 +54,22 @@ return function()
|
|||
-- Reached the lobby. Random delay before we start actually simulating activity.
|
||||
-- This is here to avoid a situation where activity arrives in bursts after a script
|
||||
-- has launched, say, 100 copies of Wesnoth at the same time.
|
||||
wesnoth.delay(math.random(15000))
|
||||
wesnoth.delay(wesnoth.random(15000))
|
||||
|
||||
events, context, info = coroutine.yield()
|
||||
|
||||
local in_staging = false
|
||||
|
||||
while true do
|
||||
if math.random() > 0.1 then
|
||||
if info.name ~= "Multiplayer Lobby" and info.name ~= "Multiplayer Staging" then
|
||||
-- most often, this means that the server was terminated -> stop generating traffic
|
||||
exit_game(context)
|
||||
end
|
||||
|
||||
if wesnoth.random() > 0.1 then
|
||||
-- chat message
|
||||
local messages = {"asdf", "qwerty", "zxc"}
|
||||
context.chat({message = messages[math.random(#messages)]})
|
||||
context.chat({message = messages[wesnoth.random(#messages)]})
|
||||
else
|
||||
-- toggle between creating a game and leaving it
|
||||
if not in_staging then
|
||||
|
|
|
@ -654,36 +654,6 @@ int game_lua_kernel::intf_set_variable(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random numer, same interface as math.random.
|
||||
*/
|
||||
int game_lua_kernel::intf_random(lua_State *L)
|
||||
{
|
||||
if(lua_isnoneornil(L, 1)) {
|
||||
double r = double (random_new::generator->next_random());
|
||||
double r_max = double (std::numeric_limits<uint32_t>::max());
|
||||
lua_push(L, r / (r_max + 1));
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
int32_t min;
|
||||
int32_t max;
|
||||
if(lua_isnumber(L, 2)) {
|
||||
min = lua_check<int32_t>(L, 1);
|
||||
max = lua_check<int32_t>(L, 2);
|
||||
}
|
||||
else {
|
||||
min = 1;
|
||||
max = lua_check<int32_t>(L, 1);
|
||||
}
|
||||
if(min > max) {
|
||||
return luaL_argerror(L, 1, "min > max");
|
||||
}
|
||||
lua_push(L, random_new::generator->get_random_int(min, max));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int game_lua_kernel::intf_set_menu_item(lua_State *L)
|
||||
{
|
||||
game_state_.get_wml_menu_items().set_item(luaL_checkstring(L, 1), luaW_checkvconfig(L,2));
|
||||
|
@ -4160,7 +4130,6 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
{ "print", &dispatch<&game_lua_kernel::intf_print > },
|
||||
{ "put_recall_unit", &dispatch<&game_lua_kernel::intf_put_recall_unit > },
|
||||
{ "put_unit", &dispatch<&game_lua_kernel::intf_put_unit > },
|
||||
{ "random", &dispatch<&game_lua_kernel::intf_random > },
|
||||
{ "redraw", &dispatch<&game_lua_kernel::intf_redraw > },
|
||||
{ "remove_event_handler", &dispatch<&game_lua_kernel::intf_remove_event > },
|
||||
{ "remove_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, true > },
|
||||
|
|
|
@ -84,7 +84,6 @@ class game_lua_kernel : public lua_kernel_base
|
|||
int intf_get_recall_units(lua_State *L);
|
||||
int intf_get_variable(lua_State *L);
|
||||
int intf_get_side_variable(lua_State *L);
|
||||
int intf_random(lua_State *L);
|
||||
int intf_set_variable(lua_State *L);
|
||||
int intf_set_side_variable(lua_State *L);
|
||||
int intf_highlight_hex(lua_State *L);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "game_errors.hpp"
|
||||
#include "log.hpp"
|
||||
#include "lua_jailbreak_exception.hpp" // for tlua_jailbreak_exception
|
||||
#include "random_new.hpp"
|
||||
#include "seed_rng.hpp"
|
||||
|
||||
#ifdef DEBUG_LUA
|
||||
|
@ -213,6 +214,36 @@ static int intf_name_generator(lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random numer, same interface as math.random.
|
||||
*/
|
||||
static int intf_random(lua_State *L)
|
||||
{
|
||||
if (lua_isnoneornil(L, 1)) {
|
||||
double r = double(random_new::generator->next_random());
|
||||
double r_max = double(std::numeric_limits<uint32_t>::max());
|
||||
lua_push(L, r / (r_max + 1));
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
int32_t min;
|
||||
int32_t max;
|
||||
if (lua_isnumber(L, 2)) {
|
||||
min = lua_check<int32_t>(L, 1);
|
||||
max = lua_check<int32_t>(L, 2);
|
||||
}
|
||||
else {
|
||||
min = 1;
|
||||
max = lua_check<int32_t>(L, 1);
|
||||
}
|
||||
if (min > max) {
|
||||
return luaL_argerror(L, 1, "min > max");
|
||||
}
|
||||
lua_push(L, random_new::generator->get_random_int(min, max));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// End Callback implementations
|
||||
|
||||
// Template which allows to push member functions to the lua kernel base into lua as C functions, using a shim
|
||||
|
@ -332,6 +363,7 @@ lua_kernel_base::lua_kernel_base()
|
|||
{ "compile_formula", &lua_formula_bridge::intf_compile_formula},
|
||||
{ "eval_formula", &lua_formula_bridge::intf_eval_formula},
|
||||
{ "name_generator", &intf_name_generator },
|
||||
{ "random", &intf_random },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
|
|
@ -956,7 +956,7 @@ int main(int argc, char** argv)
|
|||
// running before then if requested, so just perform a trivial search
|
||||
// here and let program_options ignore the switch later.
|
||||
for(size_t k = 0; k < args.size(); ++k) {
|
||||
if(args[k] == "--wconsole" || args[k] == "--help" || args[k] == "--nogui" || args[k] == "--logdomains" || args[k] == "--path" || args[k] == "--render-image" || args[k] == "--screenshot" || args[k] == "--data-path" || args[k] == "--userdata-path" || args[k] == "--userconfig-path" || args[k] == "--version") {
|
||||
if(args[k] == "--wconsole" || args[k] == "--help" || args[k] == "--logdomains" || args[k] == "--path" || args[k] == "--render-image" || args[k] == "--screenshot" || args[k] == "--data-path" || args[k] == "--userdata-path" || args[k] == "--userconfig-path" || args[k] == "--version") {
|
||||
lg::enable_native_console_output();
|
||||
break;
|
||||
}
|
||||
|
|
59
utils/simulate_lobby_traffic.py
Normal file
59
utils/simulate_lobby_traffic.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
# This script launches wesnothd, followed by 20 copies of wesnoth running the simulate-lobby-activity.lua plugin.
|
||||
# The idea is to use the script to simulate a high amount of lobby traffic, e.g. for performance testing.
|
||||
|
||||
import subprocess
|
||||
from subprocess import DEVNULL
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import time
|
||||
|
||||
PORT = 56321
|
||||
NUM_CLIENTS = 20
|
||||
EXIT_WAIT_TIME = 20.0
|
||||
|
||||
def is_in_path(filename):
|
||||
for d in os.get_exec_path():
|
||||
if os.path.exists(os.path.join(d, filename)):
|
||||
return True
|
||||
return False
|
||||
|
||||
if (os.name == "nt" and not is_in_path("SDL2.dll")):
|
||||
# Launching Wesnoth is not going to succeed
|
||||
sys.exit("Error: SDL2.dll is not in PATH. This suggests that you haven't added the external\\dll directory to your PATH.")
|
||||
|
||||
print("Launching processes... ", end="")
|
||||
|
||||
# Change the working directory to the parent directory of the directory where this script resides
|
||||
os.chdir(os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
# Wesnoth restarts itself on launch if OMP_WAIT_POLICY isn't set. That's problematic because it makes it impossible to poll
|
||||
# when the client processes terminate. Thus, set OMP_WAIT_POLICY.
|
||||
os.environ["OMP_WAIT_POLICY"] = "PASSIVE"
|
||||
|
||||
# Launch the server
|
||||
server = subprocess.Popen(("wesnothd", "-p", str(PORT)), -1, None, DEVNULL, DEVNULL, DEVNULL)
|
||||
|
||||
# Launch the clients
|
||||
clients = set()
|
||||
for i in range(NUM_CLIENTS):
|
||||
clients.add(subprocess.Popen(("wesnoth", "--plugin=simulate-lobby-activity.lua", "--server=localhost:%d" % PORT, "--username=%d" % i, "--nogui"),
|
||||
-1, None, DEVNULL, DEVNULL, DEVNULL))
|
||||
|
||||
input("done.\nPress Enter when you want to terminate all processes.")
|
||||
|
||||
server.terminate()
|
||||
|
||||
print("Waiting for clients to terminate...")
|
||||
waiting_start_time = time.monotonic()
|
||||
while len(clients) > 0 and time.monotonic() < waiting_start_time + EXIT_WAIT_TIME:
|
||||
time.sleep(1.0)
|
||||
clients_copy = list(clients)
|
||||
for c in clients_copy:
|
||||
if c.poll() != None:
|
||||
# The process has terminated, remove it from the set.
|
||||
clients.remove(c)
|
||||
|
||||
# Make sure that we get rid of the remaining processes
|
||||
for c in clients:
|
||||
c.kill()
|
Loading…
Add table
Reference in a new issue