move shell to separate thread

This commit is contained in:
Martin Michelsen
2024-02-16 22:52:46 -08:00
parent d3d63dd36c
commit 5bfda213c7
18 changed files with 1233 additions and 1249 deletions
+4 -3
View File
@@ -27,10 +27,12 @@ link_directories(${LOCAL_LIB_DIR})
find_path (LIBEVENT_INCLUDE_DIR NAMES event.h) find_path (LIBEVENT_INCLUDE_DIR NAMES event.h)
find_library (LIBEVENT_LIBRARY NAMES event) find_library (LIBEVENT_LIBRARY NAMES event)
find_library (LIBEVENT_CORE NAMES event_core) find_library (LIBEVENT_CORE NAMES event_core)
find_library (LIBEVENT_PTHREADS NAMES event_pthreads)
set (LIBEVENT_INCLUDE_DIRS ${LIBEVENT_INCLUDE_DIR}) set (LIBEVENT_INCLUDE_DIRS ${LIBEVENT_INCLUDE_DIR})
set (LIBEVENT_LIBRARIES set (LIBEVENT_LIBRARIES
${LIBEVENT_LIBRARY} ${LIBEVENT_LIBRARY}
${LIBEVENT_CORE}) ${LIBEVENT_CORE}
${LIBEVENT_PTHREADS})
find_package(phosg REQUIRED) find_package(phosg REQUIRED)
find_package(Iconv REQUIRED) find_package(Iconv REQUIRED)
@@ -81,6 +83,7 @@ set(SOURCES
src/Episode3/RulerServer.cc src/Episode3/RulerServer.cc
src/Episode3/Server.cc src/Episode3/Server.cc
src/Episode3/Tournament.cc src/Episode3/Tournament.cc
src/EventUtils.cc
src/FileContentsCache.cc src/FileContentsCache.cc
src/FunctionCompiler.cc src/FunctionCompiler.cc
src/GSLArchive.cc src/GSLArchive.cc
@@ -121,9 +124,7 @@ set(SOURCES
src/Server.cc src/Server.cc
src/ServerShell.cc src/ServerShell.cc
src/ServerState.cc src/ServerState.cc
src/Shell.cc
src/StaticGameData.cc src/StaticGameData.cc
src/StepGraph.cc
src/TeamIndex.cc src/TeamIndex.cc
src/Text.cc src/Text.cc
src/TextIndex.cc src/TextIndex.cc
+65 -14
View File
@@ -31,21 +31,19 @@
using namespace std; using namespace std;
CatSession::exit_shell::exit_shell() : runtime_error("shell exited") {}
CatSession::CatSession( CatSession::CatSession(
shared_ptr<struct event_base> base, shared_ptr<struct event_base> base,
const struct sockaddr_storage& remote, const struct sockaddr_storage& remote,
Version version, Version version,
shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file) shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file)
: Shell(base), : log(string_printf("[CatSession:%s] ", name_for_enum(version)), proxy_server_log.min_level),
log(string_printf("[CatSession:%s] ", name_for_enum(version)), proxy_server_log.min_level), base(base),
channel( read_event(event_new(this->base.get(), 0, EV_READ | EV_PERSIST, CatSession::dispatch_read_stdin, this), event_free),
version, channel(version, 1, CatSession::dispatch_on_channel_input, CatSession::dispatch_on_channel_error, this, "CatSession"),
1,
CatSession::dispatch_on_channel_input,
CatSession::dispatch_on_channel_error,
this,
"CatSession"),
bb_key_file(bb_key_file) { bb_key_file(bb_key_file) {
if (remote.ss_family != AF_INET) { if (remote.ss_family != AF_INET) {
throw runtime_error("remote is not AF_INET"); throw runtime_error("remote is not AF_INET");
} }
@@ -64,6 +62,14 @@ CatSession::CatSession(
reinterpret_cast<const sockaddr*>(&remote), sizeof(struct sockaddr_in)) != 0) { reinterpret_cast<const sockaddr*>(&remote), sizeof(struct sockaddr_in)) != 0) {
throw runtime_error(string_printf("failed to connect (%d)", EVUTIL_SOCKET_ERROR())); throw runtime_error(string_printf("failed to connect (%d)", EVUTIL_SOCKET_ERROR()));
} }
event_add(this->read_event.get(), nullptr);
this->poll.add(0, POLLIN);
}
void CatSession::execute_command(const std::string& command) {
string full_cmd = parse_data_string(command, nullptr, ParseDataFlags::ALLOW_FILES);
send_command_with_header(this->channel, full_cmd.data(), full_cmd.size());
} }
void CatSession::dispatch_on_channel_input( void CatSession::dispatch_on_channel_input(
@@ -129,9 +135,54 @@ void CatSession::on_channel_error(short events) {
} }
} }
void CatSession::print_prompt() {} void CatSession::dispatch_read_stdin(evutil_socket_t, short, void* ctx) {
reinterpret_cast<CatSession*>(ctx)->read_stdin();
void CatSession::execute_command(const std::string& command) { }
string full_cmd = parse_data_string(command, nullptr, ParseDataFlags::ALLOW_FILES);
send_command_with_header(this->channel, full_cmd.data(), full_cmd.size()); void CatSession::read_stdin() {
bool any_command_read = false;
for (;;) {
auto poll_result = this->poll.poll();
short fd_events = 0;
try {
fd_events = poll_result.at(0);
} catch (const out_of_range&) {
}
if (!(fd_events & POLLIN)) {
break;
}
string command(2048, '\0');
if (!fgets(command.data(), command.size(), stdin)) {
if (!any_command_read) {
// ctrl+d probably; we should exit
fputc('\n', stderr);
event_base_loopexit(this->base.get(), nullptr);
return;
} else {
break; // probably not EOF; just no more commands for now
}
}
// trim the extra data off the string
size_t len = strlen(command.c_str());
if (len == 0) {
break;
}
if (command[len - 1] == '\n') {
len--;
}
command.resize(len);
any_command_read = true;
try {
execute_command(command);
} catch (const exit_shell&) {
event_base_loopexit(this->base.get(), nullptr);
return;
} catch (const exception& e) {
fprintf(stderr, "FAILED: %s\n", e.what());
}
}
} }
+18 -5
View File
@@ -14,28 +14,41 @@
#include "PSOEncryption.hh" #include "PSOEncryption.hh"
#include "PSOProtocol.hh" #include "PSOProtocol.hh"
#include "ServerState.hh" #include "ServerState.hh"
#include "Shell.hh"
class CatSession : public Shell { class CatSession {
public: public:
CatSession( CatSession(
std::shared_ptr<struct event_base> base, std::shared_ptr<struct event_base> base,
const struct sockaddr_storage& remote, const struct sockaddr_storage& remote,
Version version, Version version,
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file); std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file);
CatSession(const CatSession&) = delete;
CatSession(CatSession&&) = delete;
CatSession& operator=(const CatSession&) = delete;
CatSession& operator=(CatSession&&) = delete;
virtual ~CatSession() = default; virtual ~CatSession() = default;
protected: protected:
PrefixedLogger log; PrefixedLogger log;
std::shared_ptr<struct event_base> base;
std::unique_ptr<struct event, void (*)(struct event*)> read_event;
Poll poll;
Channel channel; Channel channel;
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file; std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file;
virtual void print_prompt(); class exit_shell : public std::runtime_error {
public:
exit_shell();
~exit_shell() = default;
};
virtual void execute_command(const std::string& command); virtual void execute_command(const std::string& command);
static void dispatch_on_channel_input( static void dispatch_read_stdin(evutil_socket_t fd, short events, void* ctx);
Channel& ch, uint16_t command, uint32_t flag, std::string& msg); static void dispatch_on_channel_input(Channel& ch, uint16_t command, uint32_t flag, std::string& msg);
static void dispatch_on_channel_error(Channel& ch, short events); static void dispatch_on_channel_error(Channel& ch, short events);
void on_channel_input(uint16_t command, uint32_t flag, std::string& msg); void on_channel_input(uint16_t command, uint32_t flag, std::string& msg);
void on_channel_error(short events); void on_channel_error(short events);
void read_stdin();
}; };
+9
View File
@@ -2004,6 +2004,15 @@ MapDefinitionTrial::MapDefinitionTrial(const MapDefinition& map)
for (size_t z = 0; z < this->dialogue_sets.size(); z++) { for (size_t z = 0; z < this->dialogue_sets.size(); z++) {
this->dialogue_sets[z] = map.dialogue_sets[z].sub<8>(0); this->dialogue_sets[z] = map.dialogue_sets[z].sub<8>(0);
} }
// TODO: It'd be nice to rewrite start_tile_definitions, since it seems NTE
// always expects team A to be represented by 3 and 4, and team B to be
// represented by 7 and 8.
// TODO: NTE also expects team A to always be facing up, and B to always be
// facing down, so it would be nice to automatically rotate the map to make
// that the case. However, we'd also have to fix up the camera zones and
// camera specs, and the spec structure is not (yet) fully understood.
} }
MapDefinitionTrial::operator MapDefinition() const { MapDefinitionTrial::operator MapDefinition() const {
+20
View File
@@ -0,0 +1,20 @@
#include "EventUtils.hh"
#include <event2/event.h>
#include <deque>
#include <functional>
#include <memory>
#include <stdexcept>
static void dispatch_forward_to_event_thread(evutil_socket_t, short, void* ctx) {
auto* fn = reinterpret_cast<std::function<void()>*>(ctx);
(*fn)();
delete fn;
}
void forward_to_event_thread(std::shared_ptr<struct event_base> base, std::function<void()>&& fn) {
struct timeval tv = {0, 0};
std::function<void()>* new_fn = new std::function<void()>(std::move(fn));
event_base_once(base.get(), -1, EV_TIMEOUT, dispatch_forward_to_event_thread, new_fn, &tv);
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include <event2/event.h>
#include <functional>
#include <memory>
void forward_to_event_thread(std::shared_ptr<struct event_base> base, std::function<void()>&& fn);
+53 -22
View File
@@ -1,4 +1,5 @@
#include <event2/event.h> #include <event2/event.h>
#include <event2/thread.h>
#include <pwd.h> #include <pwd.h>
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
@@ -1127,7 +1128,8 @@ Action a_disassemble_set_data_table(
Action a_check_set_data_table( Action a_check_set_data_table(
"check-set-data-tables", nullptr, +[](Arguments&) { "check-set-data-tables", nullptr, +[](Arguments&) {
ServerState s; ServerState s;
s.load_objects_and_upstream_dependents("set_data_tables"); s.load_patch_indexes(false);
s.load_set_data_tables(false);
static_game_data_log.min_level = LogLevel::DISABLED; static_game_data_log.min_level = LogLevel::DISABLED;
auto get_file_data = [&](Version version, const string& filename) -> shared_ptr<const string> { auto get_file_data = [&](Version version, const string& filename) -> shared_ptr<const string> {
@@ -1330,7 +1332,7 @@ Action a_assemble_all_patches(
Edition). The output files are saved in system/client-functions.\n", Edition). The output files are saved in system/client-functions.\n",
+[](Arguments&) { +[](Arguments&) {
ServerState s; ServerState s;
s.load_objects_and_upstream_dependents("functions"); s.compile_functions(false);
auto process_code = +[](shared_ptr<const CompiledFunctionCode> code, auto process_code = +[](shared_ptr<const CompiledFunctionCode> code,
uint32_t checksum_addr, uint32_t checksum_addr,
@@ -1556,7 +1558,9 @@ Action a_print_word_select_table(
option is given, prints the token table sorted by canonical name.\n", option is given, prints the token table sorted by canonical name.\n",
+[](Arguments& args) { +[](Arguments& args) {
ServerState s; ServerState s;
s.load_objects_and_upstream_dependents("word_select_table"); s.load_patch_indexes(false);
s.load_text_index(false);
s.load_word_select_table(false);
Version v; Version v;
try { try {
v = get_cli_version(args); v = get_cli_version(args);
@@ -1613,7 +1617,10 @@ Action a_convert_rare_item_set(
auto version = get_cli_version(args); auto version = get_cli_version(args);
ServerState s; ServerState s;
s.load_objects_and_upstream_dependents("item_name_indexes"); s.load_patch_indexes(false);
s.load_text_index(false);
s.load_item_definitions(false);
s.load_item_name_indexes(false);
string input_filename = args.get<string>(1, false); string input_filename = args.get<string>(1, false);
if (input_filename.empty() || (input_filename == "-")) { if (input_filename.empty() || (input_filename == "-")) {
@@ -1668,7 +1675,10 @@ Action a_describe_item(
auto version = get_cli_version(args); auto version = get_cli_version(args);
ServerState s; ServerState s;
s.load_objects_and_upstream_dependents("item_name_indexes"); s.load_patch_indexes(false);
s.load_text_index(false);
s.load_item_definitions(false);
s.load_item_name_indexes(false);
auto name_index = s.item_name_index(version); auto name_index = s.item_name_index(version);
ItemData item = name_index->parse_item_description(description); ItemData item = name_index->parse_item_description(description);
@@ -1728,7 +1738,10 @@ Action a_describe_item(
Action a_name_all_items( Action a_name_all_items(
"name-all-items", nullptr, +[](Arguments&) { "name-all-items", nullptr, +[](Arguments&) {
ServerState s; ServerState s;
s.load_objects_and_upstream_dependents("item_name_indexes"); s.load_patch_indexes(false);
s.load_text_index(false);
s.load_item_definitions(false);
s.load_item_name_indexes(false);
set<uint32_t> all_primary_identifiers; set<uint32_t> all_primary_identifiers;
for (const auto& index : s.item_name_indexes) { for (const auto& index : s.item_name_indexes) {
@@ -1773,7 +1786,10 @@ Action a_name_all_items(
Action a_print_item_parameter_tables( Action a_print_item_parameter_tables(
"print-item-tables", nullptr, +[](Arguments&) { "print-item-tables", nullptr, +[](Arguments&) {
ServerState s; ServerState s;
s.load_objects_and_upstream_dependents("item_name_indexes"); s.load_patch_indexes(false);
s.load_text_index(false);
s.load_item_definitions(false);
s.load_item_name_indexes(false);
for (size_t v_s = 0; v_s < NUM_VERSIONS; v_s++) { for (size_t v_s = 0; v_s < NUM_VERSIONS; v_s++) {
const auto& index = s.item_name_indexes.at(v_s); const auto& index = s.item_name_indexes.at(v_s);
if (index) { if (index) {
@@ -1793,7 +1809,7 @@ Action a_show_ep3_cards(
bool one_line = args.get<bool>("one-line"); bool one_line = args.get<bool>("one-line");
ServerState s; ServerState s;
s.load_objects_and_upstream_dependents("ep3_data"); s.load_ep3_cards(false);
unique_ptr<BinaryTextSet> text_english; unique_ptr<BinaryTextSet> text_english;
try { try {
@@ -1843,8 +1859,9 @@ Action a_generate_ep3_cards_html(
bool is_nte = (get_cli_version(args, Version::GC_EP3) == Version::GC_EP3_NTE); bool is_nte = (get_cli_version(args, Version::GC_EP3) == Version::GC_EP3_NTE);
ServerState s; ServerState s;
s.load_objects_and_upstream_dependents("ep3_data"); s.load_patch_indexes(false);
s.load_objects_and_upstream_dependents("text_index"); s.load_text_index(false);
s.load_ep3_cards(false);
shared_ptr<const TextSet> text_english; shared_ptr<const TextSet> text_english;
try { try {
@@ -2007,7 +2024,8 @@ Action a_show_ep3_maps(
config_log.info("Collecting Episode 3 data"); config_log.info("Collecting Episode 3 data");
ServerState s; ServerState s;
s.load_objects_and_upstream_dependents("ep3_data"); s.load_ep3_cards(false);
s.load_ep3_maps(false);
auto map_ids = s.ep3_map_index->all_numbers(); auto map_ids = s.ep3_map_index->all_numbers();
log_info("%zu maps", map_ids.size()); log_info("%zu maps", map_ids.size());
@@ -2031,7 +2049,8 @@ Action a_show_battle_params(
in a human-readable format.\n", in a human-readable format.\n",
+[](Arguments&) { +[](Arguments&) {
ServerState s; ServerState s;
s.load_objects_and_upstream_dependents("battle_params"); s.load_patch_indexes(false);
s.load_battle_params(false);
fprintf(stdout, "Episode 1 multi\n"); fprintf(stdout, "Episode 1 multi\n");
s.battle_params->get_table(false, Episode::EP1).print(stdout); s.battle_params->get_table(false, Episode::EP1).print(stdout);
@@ -2073,7 +2092,8 @@ Action a_find_rare_enemy_seeds(
ServerState s("system/config.json"); ServerState s("system/config.json");
shared_ptr<const VersionedQuest> vq; shared_ptr<const VersionedQuest> vq;
if (!quest_name.empty()) { if (!quest_name.empty()) {
s.load_objects_and_upstream_dependents("quest_index"); s.load_config();
s.load_quest_index(false);
auto q = s.quest_index(version)->get(quest_name); auto q = s.quest_index(version)->get(quest_name);
if (!q) { if (!q) {
throw runtime_error("quest does not exist"); throw runtime_error("quest does not exist");
@@ -2083,11 +2103,11 @@ Action a_find_rare_enemy_seeds(
throw runtime_error("quest version does not exist"); throw runtime_error("quest version does not exist");
} }
} else if (version == Version::BB_V4) { } else if (version == Version::BB_V4) {
s.load_objects_and_upstream_dependents("config"); s.load_config();
} else if (version == Version::PC_V2) { } else if (version == Version::PC_V2) {
s.load_objects_and_upstream_dependents("patch_indexes"); s.load_patch_indexes(false);
} else { } else {
s.load_objects_and_upstream_dependents("map_file_caches"); s.clear_map_file_caches();
} }
shared_ptr<const Map::RareEnemyRates> rare_rates; shared_ptr<const Map::RareEnemyRates> rare_rates;
@@ -2265,7 +2285,8 @@ Action a_diff_dol_files(
Action a_replay_ep3_battle_commands( Action a_replay_ep3_battle_commands(
"replay-ep3-battle-commands", nullptr, +[](Arguments& args) { "replay-ep3-battle-commands", nullptr, +[](Arguments& args) {
ServerState s; ServerState s;
s.load_objects_and_upstream_dependents("ep3_data"); s.load_ep3_cards(false);
s.load_ep3_maps(false);
auto random_crypt = make_shared<PSOV2Encryption>(args.get<uint32_t>("seed", 0, Arguments::IntFormat::HEX)); auto random_crypt = make_shared<PSOV2Encryption>(args.get<uint32_t>("seed", 0, Arguments::IntFormat::HEX));
Episode3::Server::Options options = { Episode3::Server::Options options = {
@@ -2296,6 +2317,15 @@ Action a_run_server_replay_log(
config_log.info("newserv %s compiled at %s", GIT_REVISION_HASH, build_date.c_str()); config_log.info("newserv %s compiled at %s", GIT_REVISION_HASH, build_date.c_str());
} }
#ifdef PHOSG_WINDOWS
int evthread_ret = evthread_use_windows_threads();
#else
int evthread_ret = evthread_use_pthreads();
#endif
if (evthread_ret) {
throw runtime_error("failed to setup libevent threads");
}
if (!isdir("system/players")) { if (!isdir("system/players")) {
config_log.info("Players directory does not exist; creating it"); config_log.info("Players directory does not exist; creating it");
mkdir("system/players", 0755); mkdir("system/players", 0755);
@@ -2319,7 +2349,7 @@ Action a_run_server_replay_log(
shared_ptr<struct event_base> base(event_base_new(), event_base_free); shared_ptr<struct event_base> base(event_base_new(), event_base_free);
auto state = make_shared<ServerState>(base, config_filename, is_replay); auto state = make_shared<ServerState>(base, config_filename, is_replay);
state->load_objects_and_downstream_dependents("all"); state->load_all();
shared_ptr<DNSServer> dns_server; shared_ptr<DNSServer> dns_server;
if (state->dns_server_port && !is_replay) { if (state->dns_server_port && !is_replay) {
@@ -2334,7 +2364,7 @@ Action a_run_server_replay_log(
config_log.info("DNS server is disabled"); config_log.info("DNS server is disabled");
} }
shared_ptr<Shell> shell; shared_ptr<ServerShell> shell;
shared_ptr<ReplaySession> replay_session; shared_ptr<ReplaySession> replay_session;
shared_ptr<IPStackSimulator> ip_stack_simulator; shared_ptr<IPStackSimulator> ip_stack_simulator;
if (is_replay) { if (is_replay) {
@@ -2423,11 +2453,12 @@ Action a_run_server_replay_log(
if (should_run_shell) { if (should_run_shell) {
should_run_shell = !replay_session.get(); should_run_shell = !replay_session.get();
} }
if (should_run_shell) {
shell = make_shared<ServerShell>(base, state);
}
config_log.info("Ready"); config_log.info("Ready");
if (should_run_shell) {
shell = make_shared<ServerShell>(state);
}
event_base_dispatch(base.get()); event_base_dispatch(base.get());
if (replay_session) { if (replay_session) {
// If in a replay session, run the event loop for a bit longer to make // If in a replay session, run the event loop for a bit longer to make
+4 -2
View File
@@ -65,8 +65,10 @@ static shared_ptr<const Menu> proxy_options_menu_for_client(shared_ptr<const Cli
"Block events", "Disable seasonal\nevents in the lobby\nand in games"); "Block events", "Disable seasonal\nevents in the lobby\nand in games");
add_flag_option(ProxyOptionsMenuItemID::BLOCK_PATCHES, Client::Flag::PROXY_BLOCK_FUNCTION_CALLS, add_flag_option(ProxyOptionsMenuItemID::BLOCK_PATCHES, Client::Flag::PROXY_BLOCK_FUNCTION_CALLS,
"Block patches", "Disable patches sent\nby the remote server"); "Block patches", "Disable patches sent\nby the remote server");
add_flag_option(ProxyOptionsMenuItemID::SWITCH_ASSIST, Client::Flag::SWITCH_ASSIST_ENABLED, if (!is_ep3(c->version())) {
"Switch assist", "Automatically try\nto unlock 2-player\ndoors when you step\non both switches\nsequentially"); add_flag_option(ProxyOptionsMenuItemID::SWITCH_ASSIST, Client::Flag::SWITCH_ASSIST_ENABLED,
"Switch assist", "Automatically try\nto unlock 2-player\ndoors when you step\non both switches\nsequentially");
}
if ((s->cheat_mode_behavior != ServerState::BehaviorSwitch::OFF) || c->license->check_flag(License::Flag::CHEAT_ANYWHERE)) { if ((s->cheat_mode_behavior != ServerState::BehaviorSwitch::OFF) || c->license->check_flag(License::Flag::CHEAT_ANYWHERE)) {
if (!is_ep3(c->version())) { if (!is_ep3(c->version())) {
add_flag_option(ProxyOptionsMenuItemID::INFINITE_HP, Client::Flag::INFINITE_HP_ENABLED, add_flag_option(ProxyOptionsMenuItemID::INFINITE_HP, Client::Flag::INFINITE_HP_ENABLED,
-4
View File
@@ -6,7 +6,6 @@
#include "Loggers.hh" #include "Loggers.hh"
#include "Server.hh" #include "Server.hh"
#include "Shell.hh"
using namespace std; using namespace std;
@@ -474,9 +473,6 @@ ReplaySession::ReplaySession(
while (!feof(input_log)) { while (!feof(input_log)) {
line_num++; line_num++;
string line = fgets(input_log); string line = fgets(input_log);
if (starts_with(line, Shell::PROMPT)) {
line = line.substr(Shell::PROMPT.size());
}
if (ends_with(line, "\n")) { if (ends_with(line, "\n")) {
line.resize(line.size() - 1); line.resize(line.size() - 1);
} }
+2 -4
View File
@@ -30,8 +30,7 @@ public:
void disconnect_client(std::shared_ptr<Client> c); void disconnect_client(std::shared_ptr<Client> c);
std::shared_ptr<Client> get_client() const; std::shared_ptr<Client> get_client() const;
std::vector<std::shared_ptr<Client>> get_clients_by_identifier( std::vector<std::shared_ptr<Client>> get_clients_by_identifier(const std::string& ident) const;
const std::string& ident) const;
std::shared_ptr<struct event_base> get_base() const; std::shared_ptr<struct event_base> get_base() const;
inline std::shared_ptr<ServerState> get_state() const { inline std::shared_ptr<ServerState> get_state() const {
@@ -69,8 +68,7 @@ private:
evutil_socket_t fd, struct sockaddr* address, int socklen, void* ctx); evutil_socket_t fd, struct sockaddr* address, int socklen, void* ctx);
static void dispatch_on_listen_error(struct evconnlistener* listener, void* ctx); static void dispatch_on_listen_error(struct evconnlistener* listener, void* ctx);
void on_listen_accept(struct evconnlistener* listener, evutil_socket_t fd, void on_listen_accept(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* address, int socklen);
struct sockaddr* address, int socklen);
void on_listen_error(struct evconnlistener* listener); void on_listen_error(struct evconnlistener* listener);
static void on_client_input(Channel& ch, uint16_t command, uint32_t flag, std::string& data); static void on_client_input(Channel& ch, uint16_t command, uint32_t flag, std::string& data);
+718 -687
View File
File diff suppressed because it is too large Load Diff
+14 -13
View File
@@ -6,27 +6,28 @@
#include <event2/event.h> #include <event2/event.h>
#include "ProxyServer.hh" #include "ProxyServer.hh"
#include "Shell.hh" #include "ServerState.hh"
#define SHELL_PROMPT "newserv> " class ServerShell : public std::enable_shared_from_this<ServerShell> {
class ServerShell : public Shell {
public: public:
ServerShell( class exit_shell : public std::runtime_error {
std::shared_ptr<struct event_base> base, public:
std::shared_ptr<ServerState> state); exit_shell();
virtual ~ServerShell() = default; ~exit_shell() = default;
};
explicit ServerShell(std::shared_ptr<ServerState> state);
ServerShell(const ServerShell&) = delete; ServerShell(const ServerShell&) = delete;
ServerShell(ServerShell&&) = delete; ServerShell(ServerShell&&) = delete;
ServerShell& operator=(const ServerShell&) = delete; ServerShell& operator=(const ServerShell&) = delete;
ServerShell& operator=(ServerShell&&) = delete; ServerShell& operator=(ServerShell&&) = delete;
~ServerShell();
std::shared_ptr<ProxyServer::LinkedSession> get_proxy_session(const std::string& name);
protected: protected:
std::shared_ptr<ServerState> state; std::shared_ptr<ServerState> state;
std::thread th;
std::shared_ptr<ProxyServer::LinkedSession> get_proxy_session( void thread_fn();
const std::string& name);
virtual void print_prompt();
virtual void execute_command(const std::string& command);
}; };
+284 -225
View File
@@ -7,6 +7,7 @@
#include <phosg/Network.hh> #include <phosg/Network.hh>
#include "Compression.hh" #include "Compression.hh"
#include "EventUtils.hh"
#include "FileContentsCache.hh" #include "FileContentsCache.hh"
#include "GVMEncoder.hh" #include "GVMEncoder.hh"
#include "IPStackSimulator.hh" #include "IPStackSimulator.hh"
@@ -32,9 +33,7 @@ ServerState::QuestF960Result::QuestF960Result(const JSON& json, std::shared_ptr<
ServerState::ServerState(const string& config_filename) ServerState::ServerState(const string& config_filename)
: creation_time(now()), : creation_time(now()),
config_filename(config_filename) { config_filename(config_filename) {}
this->create_load_step_graph();
}
ServerState::ServerState(shared_ptr<struct event_base> base, const string& config_filename, bool is_replay) ServerState::ServerState(shared_ptr<struct event_base> base, const string& config_filename, bool is_replay)
: creation_time(now()), : creation_time(now()),
@@ -42,25 +41,7 @@ ServerState::ServerState(shared_ptr<struct event_base> base, const string& confi
config_filename(config_filename), config_filename(config_filename),
is_replay(is_replay), is_replay(is_replay),
player_files_manager(this->base ? make_shared<PlayerFilesManager>(base) : nullptr), player_files_manager(this->base ? make_shared<PlayerFilesManager>(base) : nullptr),
destroy_lobbies_event(this->base ? event_new(base.get(), -1, EV_TIMEOUT, &ServerState::dispatch_destroy_lobbies, this) : nullptr, event_free) { destroy_lobbies_event(this->base ? event_new(base.get(), -1, EV_TIMEOUT, &ServerState::dispatch_destroy_lobbies, this) : nullptr, event_free) {}
this->create_load_step_graph();
}
void ServerState::load_objects_and_downstream_dependents(const std::string& what) {
this->load_step_graph.run(what, false);
}
void ServerState::load_objects_and_downstream_dependents(const std::vector<std::string>& what) {
this->load_step_graph.run(what, false);
}
void ServerState::load_objects_and_upstream_dependents(const std::string& what) {
this->load_step_graph.run(what, true);
}
void ServerState::load_objects_and_upstream_dependents(const std::vector<std::string>& what) {
this->load_step_graph.run(what, true);
}
void ServerState::add_client_to_available_lobby(shared_ptr<Client> c) { void ServerState::add_client_to_available_lobby(shared_ptr<Client> c) {
shared_ptr<Lobby> added_to_lobby; shared_ptr<Lobby> added_to_lobby;
@@ -404,10 +385,6 @@ shared_ptr<const ItemParameterTable> ServerState::item_parameter_table_for_encod
return this->item_parameter_table(is_v1(version) ? Version::PC_V2 : version); return this->item_parameter_table(is_v1(version) ? Version::PC_V2 : version);
} }
void ServerState::set_item_parameter_table(Version version, shared_ptr<const ItemParameterTable> table) {
this->item_parameter_tables.at(static_cast<size_t>(version)) = table;
}
shared_ptr<const ItemNameIndex> ServerState::item_name_index(Version version) const { shared_ptr<const ItemNameIndex> ServerState::item_name_index(Version version) const {
auto ret = this->item_name_indexes.at(static_cast<size_t>(version)); auto ret = this->item_name_indexes.at(static_cast<size_t>(version));
if (ret == nullptr) { if (ret == nullptr) {
@@ -416,10 +393,6 @@ shared_ptr<const ItemNameIndex> ServerState::item_name_index(Version version) co
return ret; return ret;
} }
void ServerState::set_item_name_index(Version version, shared_ptr<const ItemNameIndex> new_index) {
this->item_name_indexes.at(static_cast<size_t>(version)) = new_index;
}
string ServerState::describe_item(Version version, const ItemData& item, bool include_color_codes) const { string ServerState::describe_item(Version version, const ItemData& item, bool include_color_codes) const {
return this->item_name_index(version)->describe_item(item, include_color_codes); return this->item_name_index(version)->describe_item(item, include_color_codes);
} }
@@ -784,28 +757,32 @@ void ServerState::load_config() {
for (auto& trap_card_ids : this->ep3_trap_card_ids) { for (auto& trap_card_ids : this->ep3_trap_card_ids) {
trap_card_ids.clear(); trap_card_ids.clear();
} }
try { if (this->ep3_card_index) {
const auto& ep3_trap_cards_json = json.get_list("Episode3TrapCards"); try {
if (!ep3_trap_cards_json.empty()) { const auto& ep3_trap_cards_json = json.get_list("Episode3TrapCards");
if (ep3_trap_cards_json.size() != 5) { if (!ep3_trap_cards_json.empty()) {
throw runtime_error("Episode3TrapCards must be a list of 5 lists"); if (ep3_trap_cards_json.size() != 5) {
} throw runtime_error("Episode3TrapCards must be a list of 5 lists");
for (size_t trap_type = 0; trap_type < 5; trap_type++) { }
auto& trap_card_ids = this->ep3_trap_card_ids[trap_type]; for (size_t trap_type = 0; trap_type < 5; trap_type++) {
for (const auto& card_it : ep3_trap_cards_json.at(trap_type)->as_list()) { auto& trap_card_ids = this->ep3_trap_card_ids[trap_type];
try { for (const auto& card_it : ep3_trap_cards_json.at(trap_type)->as_list()) {
const auto& card = this->ep3_card_index->definition_for_name_normalized(card_it->as_string()); try {
if (card->def.type != Episode3::CardType::ASSIST) { const auto& card = this->ep3_card_index->definition_for_name_normalized(card_it->as_string());
throw runtime_error(string_printf("Ep3 card \"%s\" in trap card list is not an assist card", name.c_str())); if (card->def.type != Episode3::CardType::ASSIST) {
throw runtime_error(string_printf("Ep3 card \"%s\" in trap card list is not an assist card", name.c_str()));
}
trap_card_ids.emplace_back(card->def.card_id);
} catch (const out_of_range&) {
throw runtime_error(string_printf("Ep3 card \"%s\" in trap card list does not exist", name.c_str()));
} }
trap_card_ids.emplace_back(card->def.card_id);
} catch (const out_of_range&) {
throw runtime_error(string_printf("Ep3 card \"%s\" in trap card list does not exist", name.c_str()));
} }
} }
} }
} catch (const out_of_range&) {
} }
} catch (const out_of_range&) { } else {
config_log.warning("Episode 3 card definitions missing; cannot set trap card IDs from config");
} }
if (!this->is_replay) { if (!this->is_replay) {
@@ -877,42 +854,46 @@ void ServerState::load_config() {
} }
this->quest_F95E_results.clear(); this->quest_F95E_results.clear();
try {
for (const auto& type_it : json.get_list("QuestF95EResultItems")) {
auto& type_res = this->quest_F95E_results.emplace_back();
for (const auto& difficulty_it : type_it->as_list()) {
auto& difficulty_res = type_res.emplace_back();
for (const auto& item_it : difficulty_it->as_list()) {
difficulty_res.emplace_back(this->parse_item_description(Version::BB_V4, item_it->as_string()));
}
}
}
} catch (const out_of_range&) {
}
this->quest_F95F_results.clear(); this->quest_F95F_results.clear();
try {
for (const auto& it : json.get_list("QuestF95FResultItems")) {
auto& list = it->as_list();
size_t price = list.at(0)->as_int();
this->quest_F95F_results.emplace_back(make_pair(price, this->parse_item_description(Version::BB_V4, list.at(1)->as_string())));
}
} catch (const out_of_range&) {
}
this->quest_F960_success_results.clear(); this->quest_F960_success_results.clear();
this->quest_F960_failure_results = QuestF960Result(); this->quest_F960_failure_results = QuestF960Result();
try {
this->quest_F960_failure_results = QuestF960Result(json.at("QuestF960FailureResultItems"), this->item_name_index(Version::BB_V4));
for (const auto& it : json.get_list("QuestF960SuccessResultItems")) {
this->quest_F960_success_results.emplace_back(*it, this->item_name_index(Version::BB_V4));
}
} catch (const out_of_range&) {
}
this->secret_lottery_results.clear(); this->secret_lottery_results.clear();
try { if (this->item_name_index(Version::BB_V4)) {
for (const auto& it : json.get_list("SecretLotteryResultItems")) { try {
this->secret_lottery_results.emplace_back(this->parse_item_description(Version::BB_V4, it->as_string())); for (const auto& type_it : json.get_list("QuestF95EResultItems")) {
auto& type_res = this->quest_F95E_results.emplace_back();
for (const auto& difficulty_it : type_it->as_list()) {
auto& difficulty_res = type_res.emplace_back();
for (const auto& item_it : difficulty_it->as_list()) {
difficulty_res.emplace_back(this->parse_item_description(Version::BB_V4, item_it->as_string()));
}
}
}
} catch (const out_of_range&) {
} }
} catch (const out_of_range&) { try {
for (const auto& it : json.get_list("QuestF95FResultItems")) {
auto& list = it->as_list();
size_t price = list.at(0)->as_int();
this->quest_F95F_results.emplace_back(make_pair(price, this->parse_item_description(Version::BB_V4, list.at(1)->as_string())));
}
} catch (const out_of_range&) {
}
try {
this->quest_F960_failure_results = QuestF960Result(json.at("QuestF960FailureResultItems"), this->item_name_index(Version::BB_V4));
for (const auto& it : json.get_list("QuestF960SuccessResultItems")) {
this->quest_F960_success_results.emplace_back(*it, this->item_name_index(Version::BB_V4));
}
} catch (const out_of_range&) {
}
try {
for (const auto& it : json.get_list("SecretLotteryResultItems")) {
this->secret_lottery_results.emplace_back(this->parse_item_description(Version::BB_V4, it->as_string()));
}
} catch (const out_of_range&) {
}
} else {
config_log.warning("BB item name index is missing; cannot load quest reward lists from config");
} }
this->bb_global_exp_multiplier = json.get_int("BBGlobalEXPMultiplier", 1); this->bb_global_exp_multiplier = json.get_int("BBGlobalEXPMultiplier", 1);
@@ -958,6 +939,7 @@ void ServerState::load_config() {
const auto& l = this->find_lobby(z + 1); const auto& l = this->find_lobby(z + 1);
if (l && l->check_flag(Lobby::Flag::DEFAULT)) { if (l && l->check_flag(Lobby::Flag::DEFAULT)) {
l->event = event; l->event = event;
send_change_event(l, l->event);
} }
} }
} catch (const out_of_range&) { } catch (const out_of_range&) {
@@ -1133,41 +1115,61 @@ void ServerState::load_config() {
this->config_loaded = true; this->config_loaded = true;
} }
void ServerState::load_bb_private_keys() { void ServerState::load_bb_private_keys(bool from_non_event_thread) {
std::vector<std::shared_ptr<const PSOBBEncryption::KeyFile>> new_keys;
for (const string& filename : list_directory("system/blueburst/keys")) { for (const string& filename : list_directory("system/blueburst/keys")) {
if (!ends_with(filename, ".nsk")) { if (!ends_with(filename, ".nsk")) {
continue; continue;
} }
this->bb_private_keys.emplace_back(make_shared<PSOBBEncryption::KeyFile>( new_keys.emplace_back(make_shared<PSOBBEncryption::KeyFile>(
load_object_file<PSOBBEncryption::KeyFile>("system/blueburst/keys/" + filename))); load_object_file<PSOBBEncryption::KeyFile>("system/blueburst/keys/" + filename)));
config_log.info("Loaded Blue Burst key file: %s", filename.c_str()); config_log.info("Loaded Blue Burst key file: %s", filename.c_str());
} }
config_log.info("%zu Blue Burst key file(s) loaded", this->bb_private_keys.size()); config_log.info("%zu Blue Burst key file(s) loaded", this->bb_private_keys.size());
auto set = [s = this->shared_from_this(), new_keys = std::move(new_keys)]() {
s->bb_private_keys = std::move(new_keys);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
void ServerState::load_licenses() { void ServerState::load_licenses(bool from_non_event_thread) {
config_log.info("Indexing licenses"); config_log.info("Indexing licenses");
this->license_index = this->is_replay ? make_shared<LicenseIndex>() : make_shared<DiskLicenseIndex>(); shared_ptr<LicenseIndex> new_index = this->is_replay ? make_shared<LicenseIndex>() : make_shared<DiskLicenseIndex>();
auto set = [s = this->shared_from_this(), new_index = std::move(new_index)]() {
s->license_index = std::move(new_index);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
void ServerState::load_teams() { void ServerState::load_teams(bool from_non_event_thread) {
config_log.info("Indexing teams"); config_log.info("Indexing teams");
this->team_index = make_shared<TeamIndex>("system/teams", this->team_reward_defs_json); shared_ptr<TeamIndex> new_index = make_shared<TeamIndex>("system/teams", this->team_reward_defs_json);
auto set = [s = this->shared_from_this(), new_index = std::move(new_index)]() {
s->team_index = std::move(new_index);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
void ServerState::load_patch_indexes() { void ServerState::load_patch_indexes(bool from_non_event_thread) {
shared_ptr<const GSLArchive> bb_data_gsl;
shared_ptr<PatchFileIndex> pc_patch_file_index;
shared_ptr<PatchFileIndex> bb_patch_file_index;
if (isdir("system/patch-pc")) { if (isdir("system/patch-pc")) {
config_log.info("Indexing PSO PC patch files"); config_log.info("Indexing PSO PC patch files");
this->pc_patch_file_index = make_shared<PatchFileIndex>("system/patch-pc"); pc_patch_file_index = make_shared<PatchFileIndex>("system/patch-pc");
} else { } else {
config_log.info("PSO PC patch files not present"); config_log.info("PSO PC patch files not present");
} }
if (isdir("system/patch-bb")) { if (isdir("system/patch-bb")) {
config_log.info("Indexing PSO BB patch files"); config_log.info("Indexing PSO BB patch files");
this->bb_patch_file_index = make_shared<PatchFileIndex>("system/patch-bb"); bb_patch_file_index = make_shared<PatchFileIndex>("system/patch-bb");
try { try {
auto gsl_file = this->bb_patch_file_index->get("./data/data.gsl"); auto gsl_file = bb_patch_file_index->get("./data/data.gsl");
this->bb_data_gsl = make_shared<GSLArchive>(gsl_file->load_data(), false); bb_data_gsl = make_shared<GSLArchive>(gsl_file->load_data(), false);
config_log.info("data.gsl found in BB patch files"); config_log.info("data.gsl found in BB patch files");
} catch (const out_of_range&) { } catch (const out_of_range&) {
config_log.info("data.gsl is not present in BB patch files"); config_log.info("data.gsl is not present in BB patch files");
@@ -1175,6 +1177,16 @@ void ServerState::load_patch_indexes() {
} else { } else {
config_log.info("PSO BB patch files not present"); config_log.info("PSO BB patch files not present");
} }
auto set = [s = this->shared_from_this(),
bb_data_gsl = std::move(bb_data_gsl),
pc_patch_file_index = std::move(pc_patch_file_index),
bb_patch_file_index = std::move(bb_patch_file_index)]() {
s->bb_data_gsl = std::move(bb_data_gsl);
s->pc_patch_file_index = std::move(pc_patch_file_index);
s->bb_patch_file_index = std::move(bb_patch_file_index);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
void ServerState::clear_map_file_caches() { void ServerState::clear_map_file_caches() {
@@ -1184,21 +1196,25 @@ void ServerState::clear_map_file_caches() {
} }
} }
void ServerState::load_set_data_tables() { void ServerState::load_set_data_tables(bool from_non_event_thread) {
config_log.info("Loading set data tables"); config_log.info("Loading set data tables");
std::array<std::shared_ptr<const SetDataTableBase>, NUM_VERSIONS> set_data_tables;
auto load_table = [this](Version version) -> void { std::array<std::shared_ptr<const SetDataTableBase>, NUM_VERSIONS> new_tables;
std::array<std::shared_ptr<const SetDataTableBase>, NUM_VERSIONS> new_tables_ep1_ult;
std::shared_ptr<const SetDataTableBase> new_table_bb_solo;
std::shared_ptr<const SetDataTableBase> new_table_bb_solo_ep1_ult;
auto load_table = [&](Version version) -> void {
auto data = this->load_map_file(version, "SetDataTableOn.rel"); auto data = this->load_map_file(version, "SetDataTableOn.rel");
this->set_data_tables[static_cast<size_t>(version)] = make_shared<SetDataTable>(version, *data); new_tables[static_cast<size_t>(version)] = make_shared<SetDataTable>(version, *data);
if (!is_v1(version) && (version != Version::PC_NTE)) { if (!is_v1(version) && (version != Version::PC_NTE)) {
auto data_ep1_ult = this->load_map_file(version, "SetDataTableOnUlti.rel"); auto data_ep1_ult = this->load_map_file(version, "SetDataTableOnUlti.rel");
this->set_data_tables_ep1_ult[static_cast<size_t>(version)] = make_shared<SetDataTable>(version, *data_ep1_ult); new_tables_ep1_ult[static_cast<size_t>(version)] = make_shared<SetDataTable>(version, *data_ep1_ult);
} }
}; };
this->set_data_tables[static_cast<size_t>(Version::DC_NTE)] = make_shared<SetDataTableDCNTE>(); new_tables[static_cast<size_t>(Version::DC_NTE)] = make_shared<SetDataTableDCNTE>();
this->set_data_tables[static_cast<size_t>(Version::DC_V1_11_2000_PROTOTYPE)] = make_shared<SetDataTableDC112000>(); new_tables[static_cast<size_t>(Version::DC_V1_11_2000_PROTOTYPE)] = make_shared<SetDataTableDC112000>();
load_table(Version::DC_V1); load_table(Version::DC_V1);
load_table(Version::DC_V2); load_table(Version::DC_V2);
load_table(Version::PC_NTE); load_table(Version::PC_NTE);
@@ -1209,29 +1225,51 @@ void ServerState::load_set_data_tables() {
load_table(Version::BB_V4); load_table(Version::BB_V4);
auto bb_solo_data = this->load_map_file(Version::BB_V4, "SetDataTableOff.rel"); auto bb_solo_data = this->load_map_file(Version::BB_V4, "SetDataTableOff.rel");
this->bb_solo_set_data_table = make_shared<SetDataTable>(Version::BB_V4, *bb_solo_data); new_table_bb_solo = make_shared<SetDataTable>(Version::BB_V4, *bb_solo_data);
auto bb_solo_data_ep1_ult = this->load_map_file(Version::BB_V4, "SetDataTableOffUlti.rel"); auto bb_solo_data_ep1_ult = this->load_map_file(Version::BB_V4, "SetDataTableOffUlti.rel");
this->bb_solo_set_data_table_ep1_ult = make_shared<SetDataTable>(Version::BB_V4, *bb_solo_data_ep1_ult); new_table_bb_solo_ep1_ult = make_shared<SetDataTable>(Version::BB_V4, *bb_solo_data_ep1_ult);
auto set = [s = this->shared_from_this(),
new_tables = std::move(new_tables),
new_tables_ep1_ult = std::move(new_tables_ep1_ult),
new_table_bb_solo = std::move(new_table_bb_solo),
new_table_bb_solo_ep1_ult = std::move(new_table_bb_solo_ep1_ult)]() {
s->set_data_tables = std::move(new_tables);
s->set_data_tables_ep1_ult = std::move(new_tables_ep1_ult);
s->bb_solo_set_data_table = std::move(new_table_bb_solo);
s->bb_solo_set_data_table_ep1_ult = std::move(new_table_bb_solo_ep1_ult);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
void ServerState::load_battle_params() { void ServerState::load_battle_params(bool from_non_event_thread) {
config_log.info("Loading battle parameters"); config_log.info("Loading battle parameters");
this->battle_params = make_shared<BattleParamsIndex>( auto new_battle_params = make_shared<BattleParamsIndex>(
this->load_bb_file("BattleParamEntry_on.dat"), this->load_bb_file("BattleParamEntry_on.dat"),
this->load_bb_file("BattleParamEntry_lab_on.dat"), this->load_bb_file("BattleParamEntry_lab_on.dat"),
this->load_bb_file("BattleParamEntry_ep4_on.dat"), this->load_bb_file("BattleParamEntry_ep4_on.dat"),
this->load_bb_file("BattleParamEntry.dat"), this->load_bb_file("BattleParamEntry.dat"),
this->load_bb_file("BattleParamEntry_lab.dat"), this->load_bb_file("BattleParamEntry_lab.dat"),
this->load_bb_file("BattleParamEntry_ep4.dat")); this->load_bb_file("BattleParamEntry_ep4.dat"));
auto set = [s = this->shared_from_this(), new_battle_params = std::move(new_battle_params)]() {
s->battle_params = std::move(new_battle_params);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
void ServerState::load_level_table() { void ServerState::load_level_table(bool from_non_event_thread) {
config_log.info("Loading level table"); config_log.info("Loading level table");
this->level_table = make_shared<LevelTableV4>(*this->load_bb_file("PlyLevelTbl.prs"), true); auto new_table = make_shared<LevelTableV4>(*this->load_bb_file("PlyLevelTbl.prs"), true);
auto set = [s = this->shared_from_this(), new_table = std::move(new_table)]() {
s->level_table = std::move(new_table);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
void ServerState::load_text_index() { void ServerState::load_text_index(bool from_non_event_thread) {
this->text_index = make_shared<TextIndex>("system/text-sets", [&](Version version, const string& filename) -> shared_ptr<const string> { auto new_index = make_shared<TextIndex>("system/text-sets", [&](Version version, const string& filename) -> shared_ptr<const string> {
try { try {
if (version == Version::BB_V4) { if (version == Version::BB_V4) {
return this->load_bb_file(filename); return this->load_bb_file(filename);
@@ -1244,9 +1282,14 @@ void ServerState::load_text_index() {
return nullptr; return nullptr;
} }
}); });
auto set = [s = this->shared_from_this(), new_index = std::move(new_index)]() {
s->text_index = std::move(new_index);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
void ServerState::load_word_select_table() { void ServerState::load_word_select_table(bool from_non_event_thread) {
config_log.info("Loading Word Select table"); config_log.info("Loading Word Select table");
vector<vector<string>> name_alias_lists; vector<vector<string>> name_alias_lists;
@@ -1299,11 +1342,16 @@ void ServerState::load_word_select_table() {
WordSelectSet bb_v4_ws(load_file("system/text-sets/bb-v4/ws_data.bin"), Version::BB_V4, bb_unitxt_collection, false); WordSelectSet bb_v4_ws(load_file("system/text-sets/bb-v4/ws_data.bin"), Version::BB_V4, bb_unitxt_collection, false);
config_log.info("(Word select) Generating table"); config_log.info("(Word select) Generating table");
this->word_select_table = make_shared<WordSelectTable>( auto new_table = make_shared<WordSelectTable>(
dc_nte_ws, dc_112000_ws, dc_v1_ws, dc_v2_ws, dc_nte_ws, dc_112000_ws, dc_v1_ws, dc_v2_ws,
pc_nte_ws, pc_v2_ws, gc_nte_ws, gc_v3_ws, pc_nte_ws, pc_v2_ws, gc_nte_ws, gc_v3_ws,
gc_ep3_nte_ws, gc_ep3_ws, xb_v3_ws, bb_v4_ws, gc_ep3_nte_ws, gc_ep3_ws, xb_v3_ws, bb_v4_ws,
name_alias_lists); name_alias_lists);
auto set = [s = this->shared_from_this(), new_table = std::move(new_table)]() {
s->word_select_table = std::move(new_table);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
shared_ptr<ItemNameIndex> ServerState::create_item_name_index_for_version( shared_ptr<ItemNameIndex> ServerState::create_item_name_index_for_version(
@@ -1334,16 +1382,24 @@ shared_ptr<ItemNameIndex> ServerState::create_item_name_index_for_version(
} }
} }
void ServerState::load_item_name_indexes() { void ServerState::load_item_name_indexes(bool from_non_event_thread) {
std::array<std::shared_ptr<const ItemNameIndex>, NUM_VERSIONS> new_indexes;
for (size_t v_s = NUM_PATCH_VERSIONS; v_s < NUM_VERSIONS; v_s++) { for (size_t v_s = NUM_PATCH_VERSIONS; v_s < NUM_VERSIONS; v_s++) {
Version v = static_cast<Version>(v_s); Version v = static_cast<Version>(v_s);
config_log.info("Generating item name index for %s", name_for_enum(v)); config_log.info("Generating item name index for %s", name_for_enum(v));
this->set_item_name_index(v, this->create_item_name_index_for_version(v, this->item_parameter_table(v), this->text_index)); new_indexes[v_s] = this->create_item_name_index_for_version(v, this->item_parameter_table(v), this->text_index);
} }
auto set = [s = this->shared_from_this(), new_indexes = std::move(new_indexes)]() {
s->item_name_indexes = std::move(new_indexes);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
void ServerState::load_drop_tables() { void ServerState::load_drop_tables(bool from_non_event_thread) {
config_log.info("Loading rare item sets"); config_log.info("Loading rare item sets");
unordered_map<string, shared_ptr<const RareItemSet>> new_rare_item_sets; unordered_map<string, shared_ptr<const RareItemSet>> new_rare_item_sets;
for (const auto& filename : list_directory_sorted("system/item-tables")) { for (const auto& filename : list_directory_sorted("system/item-tables")) {
if (!starts_with(filename, "rare-table-")) { if (!starts_with(filename, "rare-table-")) {
@@ -1387,25 +1443,25 @@ void ServerState::load_drop_tables() {
new_rare_item_sets.emplace(basename, make_shared<RareItemSet>(load_file(path), true)); new_rare_item_sets.emplace(basename, make_shared<RareItemSet>(load_file(path), true));
} }
} }
this->rare_item_sets.swap(new_rare_item_sets);
config_log.info("Loading v2 common item table"); config_log.info("Loading v2 common item table");
auto ct_data_v2 = make_shared<string>(load_file("system/item-tables/ItemCT-pc-v2.afs")); auto ct_data_v2 = make_shared<string>(load_file("system/item-tables/ItemCT-pc-v2.afs"));
auto pt_data_v2 = make_shared<string>(load_file("system/item-tables/ItemPT-pc-v2.afs")); auto pt_data_v2 = make_shared<string>(load_file("system/item-tables/ItemPT-pc-v2.afs"));
this->common_item_set_v2 = make_shared<AFSV2CommonItemSet>(pt_data_v2, ct_data_v2); auto new_common_item_set_v2 = make_shared<AFSV2CommonItemSet>(pt_data_v2, ct_data_v2);
config_log.info("Loading v3+v4 common item table"); config_log.info("Loading v3+v4 common item table");
auto pt_data_v3_v4 = make_shared<string>(load_file("system/item-tables/ItemPT-gc-v3.gsl")); auto pt_data_v3_v4 = make_shared<string>(load_file("system/item-tables/ItemPT-gc-v3.gsl"));
this->common_item_set_v3_v4 = make_shared<GSLV3V4CommonItemSet>(pt_data_v3_v4, true); auto new_common_item_set_v3_v4 = make_shared<GSLV3V4CommonItemSet>(pt_data_v3_v4, true);
config_log.info("Loading armor table"); config_log.info("Loading armor table");
auto armor_data = make_shared<string>(load_file("system/item-tables/ArmorRandom-gc-v3.rel")); auto armor_data = make_shared<string>(load_file("system/item-tables/ArmorRandom-gc-v3.rel"));
this->armor_random_set = make_shared<ArmorRandomSet>(armor_data); auto new_armor_random_set = make_shared<ArmorRandomSet>(armor_data);
config_log.info("Loading tool table"); config_log.info("Loading tool table");
auto tool_data = make_shared<string>(load_file("system/item-tables/ToolRandom-gc-v3.rel")); auto tool_data = make_shared<string>(load_file("system/item-tables/ToolRandom-gc-v3.rel"));
this->tool_random_set = make_shared<ToolRandomSet>(tool_data); auto new_tool_random_set = make_shared<ToolRandomSet>(tool_data);
config_log.info("Loading weapon tables"); config_log.info("Loading weapon tables");
std::array<std::shared_ptr<const WeaponRandomSet>, 4> new_weapon_random_sets;
const char* filenames[4] = { const char* filenames[4] = {
"system/item-tables/WeaponRandomNormal-gc-v3.rel", "system/item-tables/WeaponRandomNormal-gc-v3.rel",
"system/item-tables/WeaponRandomHard-gc-v3.rel", "system/item-tables/WeaponRandomHard-gc-v3.rel",
@@ -1414,34 +1470,59 @@ void ServerState::load_drop_tables() {
}; };
for (size_t z = 0; z < 4; z++) { for (size_t z = 0; z < 4; z++) {
auto weapon_data = make_shared<string>(load_file(filenames[z])); auto weapon_data = make_shared<string>(load_file(filenames[z]));
this->weapon_random_sets[z] = make_shared<WeaponRandomSet>(weapon_data); new_weapon_random_sets[z] = make_shared<WeaponRandomSet>(weapon_data);
} }
config_log.info("Loading tekker adjustment table"); config_log.info("Loading tekker adjustment table");
auto tekker_data = make_shared<string>(load_file("system/item-tables/JudgeItem-gc-v3.rel")); auto tekker_data = make_shared<string>(load_file("system/item-tables/JudgeItem-gc-v3.rel"));
this->tekker_adjustment_set = make_shared<TekkerAdjustmentSet>(tekker_data); auto new_tekker_adjustment_set = make_shared<TekkerAdjustmentSet>(tekker_data);
auto set = [s = this->shared_from_this(),
new_rare_item_sets = std::move(new_rare_item_sets),
new_common_item_set_v2 = std::move(new_common_item_set_v2),
new_common_item_set_v3_v4 = std::move(new_common_item_set_v3_v4),
new_armor_random_set = std::move(new_armor_random_set),
new_tool_random_set = std::move(new_tool_random_set),
new_weapon_random_sets = std::move(new_weapon_random_sets),
new_tekker_adjustment_set = std::move(new_tekker_adjustment_set)]() {
s->rare_item_sets = std::move(new_rare_item_sets);
s->common_item_set_v2 = std::move(new_common_item_set_v2);
s->common_item_set_v3_v4 = std::move(new_common_item_set_v3_v4);
s->armor_random_set = std::move(new_armor_random_set);
s->tool_random_set = std::move(new_tool_random_set);
s->weapon_random_sets = std::move(new_weapon_random_sets);
s->tekker_adjustment_set = std::move(new_tekker_adjustment_set);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
void ServerState::load_item_definitions() { void ServerState::load_item_definitions(bool from_non_event_thread) {
std::array<std::shared_ptr<const ItemParameterTable>, NUM_VERSIONS> new_item_parameter_tables;
for (size_t v_s = NUM_PATCH_VERSIONS; v_s < NUM_VERSIONS; v_s++) { for (size_t v_s = NUM_PATCH_VERSIONS; v_s < NUM_VERSIONS; v_s++) {
Version v = static_cast<Version>(v_s); Version v = static_cast<Version>(v_s);
string path = string_printf("system/item-tables/ItemPMT-%s.prs", file_path_token_for_version(v)); string path = string_printf("system/item-tables/ItemPMT-%s.prs", file_path_token_for_version(v));
config_log.info("Loading item definition table %s", path.c_str()); config_log.info("Loading item definition table %s", path.c_str());
auto data = make_shared<string>(prs_decompress(load_file(path))); auto data = make_shared<string>(prs_decompress(load_file(path)));
this->set_item_parameter_table(v, make_shared<ItemParameterTable>(data, v)); new_item_parameter_tables[v_s] = make_shared<ItemParameterTable>(data, v);
} }
// TODO: We should probably load the tables for other versions too. // TODO: We should probably load the tables for other versions too.
config_log.info("Loading mag evolution table"); config_log.info("Loading mag evolution table");
auto mag_data = make_shared<string>(prs_decompress(load_file("system/item-tables/ItemMagEdit-bb-v4.prs"))); auto mag_data = make_shared<string>(prs_decompress(load_file("system/item-tables/ItemMagEdit-bb-v4.prs")));
this->mag_evolution_table = make_shared<MagEvolutionTable>(mag_data); auto new_mag_evolution_table = make_shared<MagEvolutionTable>(mag_data);
auto set = [s = this->shared_from_this(),
new_item_parameter_tables = std::move(new_item_parameter_tables),
new_mag_evolution_table = std::move(new_mag_evolution_table)]() {
s->item_parameter_tables = std::move(new_item_parameter_tables);
s->mag_evolution_table = std::move(new_mag_evolution_table);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
void ServerState::load_ep3_data() { void ServerState::load_ep3_cards(bool from_non_event_thread) {
config_log.info("Collecting Episode 3 maps");
this->ep3_map_index = make_shared<Episode3::MapIndex>("system/ep3/maps");
config_log.info("Loading Episode 3 card definitions"); config_log.info("Loading Episode 3 card definitions");
this->ep3_card_index = make_shared<Episode3::CardIndex>( auto new_ep3_card_index = make_shared<Episode3::CardIndex>(
"system/ep3/card-definitions.mnr", "system/ep3/card-definitions.mnr",
"system/ep3/card-definitions.mnrd", "system/ep3/card-definitions.mnrd",
"system/ep3/card-text.mnr", "system/ep3/card-text.mnr",
@@ -1449,7 +1530,7 @@ void ServerState::load_ep3_data() {
"system/ep3/card-dice-text.mnr", "system/ep3/card-dice-text.mnr",
"system/ep3/card-dice-text.mnrd"); "system/ep3/card-dice-text.mnrd");
config_log.info("Loading Episode 3 trial card definitions"); config_log.info("Loading Episode 3 trial card definitions");
this->ep3_card_index_trial = make_shared<Episode3::CardIndex>( auto new_ep3_card_index_trial = make_shared<Episode3::CardIndex>(
"system/ep3/card-definitions-trial.mnr", "system/ep3/card-definitions-trial.mnr",
"system/ep3/card-definitions-trial.mnrd", "system/ep3/card-definitions-trial.mnrd",
"system/ep3/card-text-trial.mnr", "system/ep3/card-text-trial.mnr",
@@ -1457,39 +1538,76 @@ void ServerState::load_ep3_data() {
"system/ep3/card-dice-text-trial.mnr", "system/ep3/card-dice-text-trial.mnr",
"system/ep3/card-dice-text-trial.mnrd"); "system/ep3/card-dice-text-trial.mnrd");
config_log.info("Loading Episode 3 COM decks"); config_log.info("Loading Episode 3 COM decks");
this->ep3_com_deck_index = make_shared<Episode3::COMDeckIndex>("system/ep3/com-decks.json"); auto new_ep3_com_deck_index = make_shared<Episode3::COMDeckIndex>("system/ep3/com-decks.json");
auto set = [s = this->shared_from_this(),
new_ep3_card_index = std::move(new_ep3_card_index),
new_ep3_card_index_trial = std::move(new_ep3_card_index_trial),
new_ep3_com_deck_index = std::move(new_ep3_com_deck_index)]() {
s->ep3_card_index = std::move(new_ep3_card_index);
s->ep3_card_index_trial = std::move(new_ep3_card_index_trial);
s->ep3_com_deck_index = std::move(new_ep3_com_deck_index);
};
this->forward_or_call(from_non_event_thread, std::move(set));
}
void ServerState::load_ep3_maps(bool from_non_event_thread) {
config_log.info("Collecting Episode 3 maps");
auto new_ep3_map_index = make_shared<Episode3::MapIndex>("system/ep3/maps");
auto set = [s = this->shared_from_this(), new_ep3_map_index = std::move(new_ep3_map_index)]() {
s->ep3_map_index = std::move(new_ep3_map_index);
};
this->forward_or_call(from_non_event_thread, std::move(set));
}
void ServerState::load_ep3_tournament_state(bool from_non_event_thread) {
config_log.info("Loading Episode 3 tournament state");
const string& tournament_state_filename = "system/ep3/tournament-state.json"; const string& tournament_state_filename = "system/ep3/tournament-state.json";
this->ep3_tournament_index = make_shared<Episode3::TournamentIndex>( auto new_ep3_tournament_index = make_shared<Episode3::TournamentIndex>(
this->ep3_map_index, this->ep3_com_deck_index, tournament_state_filename); this->ep3_map_index, this->ep3_com_deck_index, tournament_state_filename);
shared_ptr<ServerState> s; auto set = [s = this->shared_from_this(),
try { new_ep3_tournament_index = std::move(new_ep3_tournament_index)]() {
s = this->shared_from_this(); s->ep3_tournament_index = std::move(new_ep3_tournament_index);
} catch (const bad_weak_ptr&) { s->ep3_tournament_index->link_all_clients(s);
} };
if (s) { this->forward_or_call(from_non_event_thread, std::move(set));
this->ep3_tournament_index->link_all_clients(s);
}
config_log.info("Loaded Episode 3 tournament state");
} }
void ServerState::load_quest_index() { void ServerState::load_quest_index(bool from_non_event_thread) {
config_log.info("Collecting quests"); config_log.info("Collecting quests");
this->default_quest_index = make_shared<QuestIndex>("system/quests", this->quest_category_index, false); auto new_default_quest_index = make_shared<QuestIndex>("system/quests", this->quest_category_index, false);
config_log.info("Collecting Episode 3 download quests"); config_log.info("Collecting Episode 3 download quests");
this->ep3_download_quest_index = make_shared<QuestIndex>("system/ep3/maps-download", this->quest_category_index, true); auto new_ep3_download_quest_index = make_shared<QuestIndex>("system/ep3/maps-download", this->quest_category_index, true);
auto set = [s = this->shared_from_this(),
new_default_quest_index = std::move(new_default_quest_index),
new_ep3_download_quest_index = std::move(new_ep3_download_quest_index)]() {
s->default_quest_index = std::move(new_default_quest_index);
s->ep3_download_quest_index = std::move(new_ep3_download_quest_index);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
void ServerState::compile_functions() { void ServerState::compile_functions(bool from_non_event_thread) {
config_log.info("Compiling client functions"); config_log.info("Compiling client functions");
this->function_code_index = make_shared<FunctionCodeIndex>("system/client-functions"); auto new_function_code_index = make_shared<FunctionCodeIndex>("system/client-functions");
auto set = [s = this->shared_from_this(), new_function_code_index = std::move(new_function_code_index)]() {
s->function_code_index = std::move(new_function_code_index);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
void ServerState::load_dol_files() { void ServerState::load_dol_files(bool from_non_event_thread) {
config_log.info("Loading DOL files"); config_log.info("Loading DOL files");
this->dol_file_index = make_shared<DOLFileIndex>("system/dol"); auto new_dol_file_index = make_shared<DOLFileIndex>("system/dol");
auto set = [s = this->shared_from_this(), new_dol_file_index = std::move(new_dol_file_index)]() {
s->dol_file_index = std::move(new_dol_file_index);
};
this->forward_or_call(from_non_event_thread, std::move(set));
} }
void ServerState::create_default_lobbies() { void ServerState::create_default_lobbies() {
@@ -1537,86 +1655,27 @@ void ServerState::create_default_lobbies() {
} }
} }
void ServerState::create_load_step_graph() { void ServerState::load_all() {
this->load_step_graph.add_step("all", {}, nullptr); this->collect_network_addresses();
this->load_bb_private_keys(false);
// In: none this->load_licenses(false);
// Out: all_addresses this->clear_map_file_caches();
this->load_step_graph.add_step("network_addresses", {"all"}, bind(&ServerState::collect_network_addresses, this)); this->load_patch_indexes(false);
this->load_ep3_cards(false);
// In: none this->load_ep3_maps(false);
// Out: bb_private_keys this->load_ep3_tournament_state(false);
this->load_step_graph.add_step("bb_private_keys", {"all"}, bind(&ServerState::load_bb_private_keys, this)); this->compile_functions(false);
this->load_dol_files(false);
// In: none this->create_default_lobbies();
// Out: license_index this->load_set_data_tables(false);
this->load_step_graph.add_step("licenses", {"all"}, bind(&ServerState::load_licenses, this)); this->load_battle_params(false);
this->load_level_table(false);
// In: none this->load_text_index(false);
// Out: map_file_caches this->load_word_select_table(false);
this->load_step_graph.add_step("map_file_caches", {"all"}, bind(&ServerState::clear_map_file_caches, this)); this->load_item_definitions(false);
this->load_item_name_indexes(false);
// In: none this->load_drop_tables(false);
// Out: pc_patch_file_index, bb_patch_file_index, bb_data_gsl this->load_config();
this->load_step_graph.add_step("patch_indexes", {"all", "map_file_caches"}, bind(&ServerState::load_patch_indexes, this)); this->load_teams(false);
this->load_quest_index(false);
// In: none
// Out: ep3_map_index, ep3_card_index, ep3_card_index_trial, ep3_com_deck_index, ep3_tournament_index
this->load_step_graph.add_step("ep3_data", {"all"}, bind(&ServerState::load_ep3_data, this));
// In: none
// Out: function_code_index
this->load_step_graph.add_step("functions", {"all"}, bind(&ServerState::compile_functions, this));
// In: none
// Out: dol_file_index
this->load_step_graph.add_step("dol_files", {"all"}, bind(&ServerState::load_dol_files, this));
// In: none
// Out: lobbies
this->load_step_graph.add_step("lobbies", {"all"}, bind(&ServerState::create_default_lobbies, this));
// In: bb_patch_file_index
// Out: set_data_tables
this->load_step_graph.add_step("set_data_tables", {"all", "patch_indexes"}, bind(&ServerState::load_set_data_tables, this));
// In: bb_patch_file_index
// Out: battle_params
this->load_step_graph.add_step("battle_params", {"all", "patch_indexes"}, bind(&ServerState::load_battle_params, this));
// In: bb_patch_file_index
// Out: level_table
this->load_step_graph.add_step("level_table", {"all", "patch_indexes"}, bind(&ServerState::load_level_table, this));
// In: bb_patch_file_index
// Out: text_index
this->load_step_graph.add_step("text_index", {"all", "patch_indexes"}, bind(&ServerState::load_text_index, this));
// In: text_index (optional)
// Out: word_select_table
this->load_step_graph.add_step("word_select_table", {"all"}, bind(&ServerState::load_word_select_table, this));
// In: none
// Out: item_parameter_tables, mag_evolution_table
this->load_step_graph.add_step("item_definitions", {"all"}, bind(&ServerState::load_item_definitions, this));
// In: text_index, item_parameter_tables
// Out: item_name_indexes
this->load_step_graph.add_step("item_name_indexes", {"all", "text_index", "item_definitions"}, bind(&ServerState::load_item_name_indexes, this));
// In: none
// Out: rare_item_sets, common_item_sets, armor_random_set, tool_random_set, weapon_random_sets, tekker_adjustment_set
this->load_step_graph.add_step("drop_tables", {"all", "item_definitions", "item_name_indexes"}, bind(&ServerState::load_drop_tables, this));
// In: all_addresses, ep3_card_index, item_name_indexes
// Out: config, ep3_lobby_banners, quest_category_index, information menus, proxy destinations menus, team_reward_defs_json
this->load_step_graph.add_step("config", {"all", "network_addresses", "ep3_data", "item_name_indexes"}, bind(&ServerState::load_config, this));
// In: team_reward_defs_json
// Out: team_index
this->load_step_graph.add_step("teams", {"all", "config"}, bind(&ServerState::load_teams, this));
// In: quest_category_index
// Out: default_quest_index, ep3_download_quest_index
this->load_step_graph.add_step("quest_index", {"all", "config"}, bind(&ServerState::load_quest_index, this));
} }
+34 -25
View File
@@ -15,6 +15,7 @@
#include "CommonItemSet.hh" #include "CommonItemSet.hh"
#include "Episode3/DataIndexes.hh" #include "Episode3/DataIndexes.hh"
#include "Episode3/Tournament.hh" #include "Episode3/Tournament.hh"
#include "EventUtils.hh"
#include "FunctionCompiler.hh" #include "FunctionCompiler.hh"
#include "GSLArchive.hh" #include "GSLArchive.hh"
#include "ItemNameIndex.hh" #include "ItemNameIndex.hh"
@@ -25,7 +26,6 @@
#include "Menu.hh" #include "Menu.hh"
#include "PlayerFilesManager.hh" #include "PlayerFilesManager.hh"
#include "Quest.hh" #include "Quest.hh"
#include "StepGraph.hh"
#include "TeamIndex.hh" #include "TeamIndex.hh"
#include "WordSelectTable.hh" #include "WordSelectTable.hh"
@@ -69,8 +69,6 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
bool config_loaded = false; bool config_loaded = false;
bool default_lobbies_created = false; bool default_lobbies_created = false;
StepGraph load_step_graph;
std::string name; std::string name;
std::unordered_map<std::string, std::shared_ptr<PortConfiguration>> name_to_port_config; std::unordered_map<std::string, std::shared_ptr<PortConfiguration>> name_to_port_config;
std::unordered_map<uint16_t, std::shared_ptr<PortConfiguration>> number_to_port_config; std::unordered_map<uint16_t, std::shared_ptr<PortConfiguration>> number_to_port_config;
@@ -245,11 +243,6 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
ServerState& operator=(const ServerState&) = delete; ServerState& operator=(const ServerState&) = delete;
ServerState& operator=(ServerState&&) = delete; ServerState& operator=(ServerState&&) = delete;
void load_objects_and_downstream_dependents(const std::string& what);
void load_objects_and_downstream_dependents(const std::vector<std::string>& what);
void load_objects_and_upstream_dependents(const std::string& what);
void load_objects_and_upstream_dependents(const std::vector<std::string>& what);
void add_client_to_available_lobby(std::shared_ptr<Client> c); void add_client_to_available_lobby(std::shared_ptr<Client> c);
void remove_client_from_lobby(std::shared_ptr<Client> c); void remove_client_from_lobby(std::shared_ptr<Client> c);
bool change_client_lobby( bool change_client_lobby(
@@ -308,29 +301,45 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::pair<std::string, uint16_t> parse_port_spec(const JSON& json) const; std::pair<std::string, uint16_t> parse_port_spec(const JSON& json) const;
std::vector<PortConfiguration> parse_port_configuration(const JSON& json) const; std::vector<PortConfiguration> parse_port_configuration(const JSON& json) const;
void create_load_step_graph(); inline void forward_to_event_thread(std::function<void()>&& fn) {
::forward_to_event_thread(this->base, std::move(fn));
}
inline void forward_or_call(bool from_non_event_thread, std::function<void()>&& fn) {
if (from_non_event_thread) {
::forward_to_event_thread(this->base, std::move(fn));
} else {
fn();
}
}
// The following functions may only be called from a non-event thread if they
// take a from_non_event_thread argument; any function that does not have this
// argument must be called only from the event thread.
void create_default_lobbies(); void create_default_lobbies();
void collect_network_addresses(); void collect_network_addresses();
void load_config(); void load_config();
void load_bb_private_keys(); void load_bb_private_keys(bool from_non_event_thread);
void load_licenses(); void load_licenses(bool from_non_event_thread);
void load_teams(); void load_teams(bool from_non_event_thread);
void load_patch_indexes(); void load_patch_indexes(bool from_non_event_thread);
void clear_map_file_caches(); void clear_map_file_caches();
void load_battle_params(); void load_battle_params(bool from_non_event_thread);
void load_level_table(); void load_level_table(bool from_non_event_thread);
void load_text_index(); void load_text_index(bool from_non_event_thread);
static std::shared_ptr<ItemNameIndex> create_item_name_index_for_version( static std::shared_ptr<ItemNameIndex> create_item_name_index_for_version(
Version version, std::shared_ptr<const ItemParameterTable> pmt, std::shared_ptr<const TextIndex> text_index); Version version, std::shared_ptr<const ItemParameterTable> pmt, std::shared_ptr<const TextIndex> text_index);
void load_item_name_indexes(); void load_item_name_indexes(bool from_non_event_thread);
void load_drop_tables(); void load_drop_tables(bool from_non_event_thread);
void load_item_definitions(); void load_item_definitions(bool from_non_event_thread);
void load_set_data_tables(); void load_set_data_tables(bool from_non_event_thread);
void load_word_select_table(); void load_word_select_table(bool from_non_event_thread);
void load_ep3_data(); void load_ep3_cards(bool from_non_event_thread);
void load_quest_index(); void load_ep3_maps(bool from_non_event_thread);
void compile_functions(); void load_ep3_tournament_state(bool from_non_event_thread);
void load_dol_files(); void load_quest_index(bool from_non_event_thread);
void compile_functions(bool from_non_event_thread);
void load_dol_files(bool from_non_event_thread);
void load_all();
void enqueue_destroy_lobbies(); void enqueue_destroy_lobbies();
static void dispatch_destroy_lobbies(evutil_socket_t, short, void* ctx); static void dispatch_destroy_lobbies(evutil_socket_t, short, void* ctx);
-94
View File
@@ -1,94 +0,0 @@
#include "Shell.hh"
#include <event2/event.h>
#include <stdio.h>
#include <string.h>
#include <phosg/Strings.hh>
using namespace std;
const std::string Shell::PROMPT("newserv> ");
Shell::exit_shell::exit_shell() : runtime_error("shell exited") {}
Shell::Shell(std::shared_ptr<struct event_base> base)
: base(base),
read_event(
event_new(this->base.get(), 0, EV_READ | EV_PERSIST, &Shell::dispatch_read_stdin, this),
event_free),
prompt_event(
event_new(this->base.get(), 0, EV_TIMEOUT, &Shell::dispatch_print_prompt, this),
event_free) {
event_add(this->read_event.get(), nullptr);
// Schedule an event to print the prompt as soon as the event loop starts
// running. We do this so the prompt appears after any initialization
// messages that come after starting the shell
struct timeval tv = {0, 0};
event_add(this->prompt_event.get(), &tv);
this->poll.add(0, POLLIN);
}
void Shell::dispatch_print_prompt(evutil_socket_t, short, void* ctx) {
reinterpret_cast<Shell*>(ctx)->print_prompt();
}
void Shell::print_prompt() {
// Default behavior: no prompt
}
void Shell::dispatch_read_stdin(evutil_socket_t, short, void* ctx) {
reinterpret_cast<Shell*>(ctx)->read_stdin();
}
void Shell::read_stdin() {
bool any_command_read = false;
for (;;) {
auto poll_result = this->poll.poll();
short fd_events = 0;
try {
fd_events = poll_result.at(0);
} catch (const out_of_range&) {
}
if (!(fd_events & POLLIN)) {
break;
}
string command(2048, '\0');
if (!fgets(command.data(), command.size(), stdin)) {
if (!any_command_read) {
// ctrl+d probably; we should exit
fputc('\n', stderr);
event_base_loopexit(this->base.get(), nullptr);
return;
} else {
break; // probably not EOF; just no more commands for now
}
}
// trim the extra data off the string
size_t len = strlen(command.c_str());
if (len == 0) {
break;
}
if (command[len - 1] == '\n') {
len--;
}
command.resize(len);
any_command_read = true;
try {
execute_command(command);
} catch (const exit_shell&) {
event_base_loopexit(this->base.get(), nullptr);
return;
} catch (const exception& e) {
fprintf(stderr, "FAILED: %s\n", e.what());
}
}
this->print_prompt();
}
-40
View File
@@ -1,40 +0,0 @@
#pragma once
#include <event2/event.h>
#include <memory>
#include <phosg/Filesystem.hh>
#include <stdexcept>
#include <string>
#include "ServerState.hh"
class Shell {
public:
Shell(std::shared_ptr<struct event_base> base);
virtual ~Shell() = default;
Shell(const Shell&) = delete;
Shell(Shell&&) = delete;
Shell& operator=(const Shell&) = delete;
Shell& operator=(Shell&&) = delete;
static const std::string PROMPT;
protected:
std::shared_ptr<struct event_base> base;
std::unique_ptr<struct event, void (*)(struct event*)> read_event;
std::unique_ptr<struct event, void (*)(struct event*)> prompt_event;
Poll poll;
class exit_shell : public std::runtime_error {
public:
exit_shell();
~exit_shell() = default;
};
static void dispatch_print_prompt(evutil_socket_t fd, short events, void* ctx);
static void dispatch_read_stdin(evutil_socket_t fd, short events, void* ctx);
virtual void print_prompt();
void read_stdin();
virtual void execute_command(const std::string& command) = 0;
};
-83
View File
@@ -1,83 +0,0 @@
#include "StepGraph.hh"
using namespace std;
void StepGraph::add_step(const string& name, const vector<string>& depends_on_names, function<void()>&& execute) {
auto new_step = make_shared<Step>();
new_step->execute = std::move(execute);
this->steps.emplace(name, new_step);
for (const auto& depends_on_name : depends_on_names) {
auto upstream_step = this->steps.at(depends_on_name);
upstream_step->downstream_dependencies.emplace_back(new_step);
new_step->upstream_dependencies.emplace_back(upstream_step);
}
}
void StepGraph::run(const string& start_step_name, bool run_upstreams) {
vector<string> start_step_names({start_step_name});
this->run(start_step_names, run_upstreams);
}
void StepGraph::run(const vector<string>& start_step_names, bool run_upstreams) {
// Collect all steps to run
deque<shared_ptr<Step>> steps_to_visit;
try {
for (const auto& start_step_name : start_step_names) {
steps_to_visit.emplace_back(this->steps.at(start_step_name));
}
} catch (const out_of_range&) {
throw runtime_error("invalid step name");
}
unordered_set<shared_ptr<Step>> steps_to_run;
while (!steps_to_visit.empty()) {
auto step = std::move(steps_to_visit.front());
steps_to_visit.pop_front();
if (steps_to_run.emplace(step).second) {
if (run_upstreams) {
for (const auto& w_other_step : step->upstream_dependencies) {
auto other_step = w_other_step.lock();
if (!other_step) {
throw runtime_error("upstream step is deleted");
}
steps_to_visit.emplace_back(other_step);
}
} else {
for (const auto& other_step : step->downstream_dependencies) {
steps_to_visit.emplace_back(other_step);
}
}
}
}
// Topological sort: repeatedly take all steps that are not a downstream
// dependency of any other step in the set
vector<shared_ptr<Step>> steps_order;
steps_order.reserve(steps_to_run.size());
while (!steps_to_run.empty()) {
unordered_set<shared_ptr<Step>> candidate_steps = steps_to_run;
for (const auto& step : steps_to_run) {
for (const auto& downstream_step : step->downstream_dependencies) {
candidate_steps.erase(downstream_step);
}
}
if (candidate_steps.empty()) {
throw logic_error("dependency graph contains a cycle");
}
for (const auto& step : candidate_steps) {
steps_to_run.erase(step);
steps_order.emplace_back(step);
}
}
// Run the steps in order
uint64_t run_id = ++this->last_run_id;
for (auto step : steps_order) {
if (step->last_run_id < run_id) {
step->last_run_id = run_id;
if (step->execute) {
step->execute();
}
}
}
}
-28
View File
@@ -1,28 +0,0 @@
#pragma once
#include <stdint.h>
#include <functional>
#include <memory>
#include <phosg/Strings.hh>
#include <string>
#include <unordered_map>
#include <vector>
struct StepGraph {
struct Step {
std::vector<std::shared_ptr<Step>> downstream_dependencies;
std::vector<std::weak_ptr<Step>> upstream_dependencies;
std::function<void()> execute;
uint64_t last_run_id = 0;
};
std::unordered_map<std::string, std::shared_ptr<Step>> steps;
uint64_t last_run_id = 0;
StepGraph() = default;
void add_step(const std::string& name, const std::vector<std::string>& depends_on_names, std::function<void()>&& execute);
void run(const std::string& start_step, bool run_upstreams);
void run(const std::vector<std::string>& start_steps, bool run_upstreams);
};