support multiple replays in the same session
This commit is contained in:
+13
-11
@@ -154,23 +154,25 @@ add_dependencies(newserv newserv-Revision-cc)
|
||||
|
||||
enable_testing()
|
||||
|
||||
file(GLOB LogTestCases ${CMAKE_SOURCE_DIR}/tests/*.test.txt)
|
||||
file(GLOB LogRDTestCases ${CMAKE_SOURCE_DIR}/tests/*.rdtest.txt)
|
||||
|
||||
foreach(LogTestCase IN ITEMS ${LogTestCases})
|
||||
file(GLOB LOG_TEST_CASES ${CMAKE_SOURCE_DIR}/tests/*.test.txt)
|
||||
foreach(LOG_TEST_CASE IN ITEMS ${LOG_TEST_CASES})
|
||||
add_test(
|
||||
NAME ${LogTestCase}
|
||||
NAME ${LOG_TEST_CASE}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
COMMAND ${CMAKE_BINARY_DIR}/newserv --replay-log=${LogTestCase} --config=${CMAKE_SOURCE_DIR}/tests/config.json)
|
||||
COMMAND ${CMAKE_BINARY_DIR}/newserv --replay-log=${LOG_TEST_CASE} --config=${CMAKE_SOURCE_DIR}/tests/config.json)
|
||||
endforeach()
|
||||
# list(TRANSFORM LOG_TEST_CASES PREPEND "--replay-log=" OUTPUT_VARIABLE LOG_REPLAY_ARGS)
|
||||
# add_test(
|
||||
# NAME "log-replays"
|
||||
# WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
# COMMAND ${CMAKE_BINARY_DIR}/newserv --config=${CMAKE_SOURCE_DIR}/tests/config.json ${LOG_REPLAY_ARGS})
|
||||
|
||||
file(GLOB ScriptTestCases ${CMAKE_SOURCE_DIR}/tests/*.test.sh)
|
||||
|
||||
foreach(ScriptTestCase IN ITEMS ${ScriptTestCases})
|
||||
file(GLOB SCRIPT_TEST_CASES ${CMAKE_SOURCE_DIR}/tests/*.test.sh)
|
||||
foreach(SCRIPT_TEST_CASE IN ITEMS ${SCRIPT_TEST_CASES})
|
||||
add_test(
|
||||
NAME ${ScriptTestCase}
|
||||
NAME ${SCRIPT_TEST_CASE}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
COMMAND ${ScriptTestCase} ${CMAKE_BINARY_DIR}/newserv)
|
||||
COMMAND ${SCRIPT_TEST_CASE} ${CMAKE_BINARY_DIR}/newserv)
|
||||
endforeach()
|
||||
|
||||
|
||||
|
||||
+30
-10
@@ -4041,7 +4041,7 @@ Action a_run_server_replay_log(
|
||||
std::filesystem::create_directories("system/players");
|
||||
}
|
||||
|
||||
const string& replay_log_filename = args.get<string>("replay-log");
|
||||
const auto& replay_log_filenames = args.get_multi<string>("replay-log");
|
||||
|
||||
#ifndef PHOSG_WINDOWS
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
@@ -4050,7 +4050,7 @@ Action a_run_server_replay_log(
|
||||
use_terminal_colors = true;
|
||||
}
|
||||
|
||||
auto state = make_shared<ServerState>(get_config_filename(args), !replay_log_filename.empty());
|
||||
auto state = make_shared<ServerState>(get_config_filename(args), !replay_log_filenames.empty());
|
||||
if (args.get<bool>("debug")) {
|
||||
state->is_debug = true;
|
||||
}
|
||||
@@ -4069,19 +4069,39 @@ Action a_run_server_replay_log(
|
||||
}
|
||||
|
||||
shared_ptr<ServerShell> shell;
|
||||
shared_ptr<ReplaySession> replay_session;
|
||||
shared_ptr<SignalWatcher> signal_watcher;
|
||||
if (!replay_log_filename.empty()) {
|
||||
shared_ptr<ReplaySession> last_running_replay;
|
||||
if (!replay_log_filenames.empty()) {
|
||||
config_log.info_f("Starting game server");
|
||||
state->game_server = make_shared<GameServer>(state);
|
||||
|
||||
// TODO: Do this properly via a config option, you lazy bum
|
||||
state->dol_file_index = make_shared<DOLFileIndex>();
|
||||
|
||||
auto log_f = phosg::fopen_shared(replay_log_filename, "rt");
|
||||
|
||||
replay_session = make_shared<ReplaySession>(state, log_f.get(), false);
|
||||
asio::co_spawn(*state->io_context, replay_session->run(), asio::detached);
|
||||
auto run_replays = [&]() -> asio::awaitable<void> {
|
||||
try {
|
||||
for (const auto& log_filename : replay_log_filenames) {
|
||||
phosg::log_info_f("[Replay] {} ...", log_filename);
|
||||
auto log_f = phosg::fopen_shared(log_filename, "rt");
|
||||
last_running_replay = make_shared<ReplaySession>(state, log_f.get());
|
||||
co_await last_running_replay->run();
|
||||
if (last_running_replay->failed()) {
|
||||
phosg::log_error_f("[Replay] {} failed", log_filename);
|
||||
break;
|
||||
}
|
||||
phosg::log_info_f("[Replay] {} OK", log_filename);
|
||||
state->reset_between_replays();
|
||||
}
|
||||
phosg::log_info_f("[Replay] All replays complete");
|
||||
} catch (const std::exception& e) {
|
||||
phosg::log_info_f("[Replay] Replays failed: {}", e.what());
|
||||
}
|
||||
if (!last_running_replay->failed()) {
|
||||
last_running_replay.reset();
|
||||
}
|
||||
state->io_context->stop();
|
||||
};
|
||||
asio::co_spawn(*state->io_context, run_replays, asio::detached);
|
||||
|
||||
} else {
|
||||
config_log.info_f("Opening sockets");
|
||||
@@ -4174,7 +4194,7 @@ Action a_run_server_replay_log(
|
||||
should_run_shell = false;
|
||||
}
|
||||
if (should_run_shell) {
|
||||
should_run_shell = !replay_session.get();
|
||||
should_run_shell = replay_log_filenames.empty();
|
||||
}
|
||||
|
||||
config_log.info_f("Ready");
|
||||
@@ -4185,7 +4205,7 @@ Action a_run_server_replay_log(
|
||||
state->io_context->run();
|
||||
config_log.info_f("Normal shutdown");
|
||||
|
||||
if (replay_session && replay_session->failed()) {
|
||||
if (last_running_replay) {
|
||||
throw runtime_error("Replay failed");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -321,9 +321,8 @@ void ReplaySession::apply_default_mask(shared_ptr<Event> ev) {
|
||||
}
|
||||
}
|
||||
|
||||
ReplaySession::ReplaySession(shared_ptr<ServerState> state, FILE* input_log, bool is_interactive)
|
||||
ReplaySession::ReplaySession(shared_ptr<ServerState> state, FILE* input_log)
|
||||
: state(state),
|
||||
is_interactive(is_interactive),
|
||||
prev_psov2_crypt_enabled(this->state->use_psov2_rand_crypt),
|
||||
commands_sent(0),
|
||||
bytes_sent(0),
|
||||
@@ -669,9 +668,6 @@ asio::awaitable<void> ReplaySession::run() {
|
||||
replay_log.info_f("Replay complete: {} commands sent ({} bytes), {} commands received ({} bytes)",
|
||||
this->commands_sent, this->bytes_sent, this->commands_received, this->bytes_received);
|
||||
}
|
||||
if (!this->is_interactive) {
|
||||
this->state->io_context->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void ReplaySession::reschedule_idle_timeout() {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
class ReplaySession {
|
||||
public:
|
||||
ReplaySession(std::shared_ptr<ServerState> state, FILE* input_log, bool is_interactive);
|
||||
ReplaySession(std::shared_ptr<ServerState> state, FILE* input_log);
|
||||
ReplaySession(const ReplaySession&) = delete;
|
||||
ReplaySession(ReplaySession&&) = delete;
|
||||
ReplaySession& operator=(const ReplaySession&) = delete;
|
||||
@@ -62,7 +62,6 @@ private:
|
||||
};
|
||||
|
||||
std::shared_ptr<ServerState> state;
|
||||
bool is_interactive;
|
||||
bool prev_psov2_crypt_enabled;
|
||||
|
||||
std::unordered_map<uint64_t, std::shared_ptr<Client>> clients;
|
||||
|
||||
@@ -2361,6 +2361,27 @@ void ServerState::load_all(bool enable_thread_pool) {
|
||||
this->generate_bb_stream_file();
|
||||
}
|
||||
|
||||
void ServerState::reset_between_replays() {
|
||||
if (this->allow_saving_accounts) {
|
||||
throw std::logic_error("Account saving is enabled during replay");
|
||||
}
|
||||
this->account_index = make_shared<AccountIndex>(true);
|
||||
|
||||
this->next_lobby_id = 0;
|
||||
std::vector<std::shared_ptr<Lobby>> lobbies_to_delete;
|
||||
for (const auto& l : this->all_lobbies()) {
|
||||
if (l->is_game()) {
|
||||
lobbies_to_delete.emplace_back(l);
|
||||
} else {
|
||||
this->next_lobby_id = std::max<uint32_t>(this->next_lobby_id, l->lobby_id + 1);
|
||||
}
|
||||
}
|
||||
for (const auto& l : lobbies_to_delete) {
|
||||
phosg::log_warning_f("Previous replay left a game open ({:08X}); destroying it", l->lobby_id);
|
||||
this->remove_lobby(l);
|
||||
}
|
||||
}
|
||||
|
||||
void ServerState::disconnect_all_banned_clients() {
|
||||
uint64_t now_usecs = phosg::now();
|
||||
|
||||
|
||||
@@ -458,5 +458,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
|
||||
void load_all(bool enable_thread_pool);
|
||||
|
||||
void reset_between_replays();
|
||||
|
||||
void disconnect_all_banned_clients();
|
||||
};
|
||||
|
||||
@@ -1095,15 +1095,3 @@ ShellCommand c_create_item(
|
||||
send_text_message(c->channel, "$C7Item created:\n" + name);
|
||||
co_return deque<string>{};
|
||||
});
|
||||
|
||||
ShellCommand c_replay_log(
|
||||
"replay-log", nullptr,
|
||||
+[](ShellCommand::Args& args) -> asio::awaitable<deque<string>> {
|
||||
if (args.s->allow_saving_accounts) {
|
||||
throw runtime_error("Replays cannot be run when account saving is enabled");
|
||||
}
|
||||
auto log_f = phosg::fopen_shared(args.args, "rt");
|
||||
auto replay_session = make_shared<ReplaySession>(args.s, log_f.get(), true);
|
||||
co_await replay_session->run();
|
||||
co_return deque<string>{};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user