From d75891e78b5062fa1b7154d05161dcc277ba50b9 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sun, 28 Jan 2024 23:33:49 -0800 Subject: [PATCH] add a few ways to customize lobbies --- src/ReceiveCommands.cc | 63 +++++++------- src/ServerState.cc | 171 ++++++++++++++++++++++--------------- src/ServerState.hh | 4 +- system/config.example.json | 126 +++++++++++++++++++++++++-- tests/config.json | 21 +++++ 5 files changed, 278 insertions(+), 107 deletions(-) diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index b897c0aa..755d8a71 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -30,8 +30,6 @@ const char* BATTLE_TABLE_DISCONNECT_HOOK_NAME = "battle_table_state"; const char* QUEST_BARRIER_DISCONNECT_HOOK_NAME = "quest_barrier"; const char* ADD_NEXT_CLIENT_DISCONNECT_HOOK_NAME = "add_next_game_client"; -void on_login_complete(shared_ptr c); - static shared_ptr proxy_options_menu_for_client(shared_ptr c) { auto s = c->require_server_state(); @@ -265,20 +263,13 @@ void on_login_complete(shared_ptr c) { case ServerBehavior::LOGIN_SERVER: { auto s = c->require_server_state(); - // On the login server, send the events/songs, ep3 updates, and the main - // menu or welcome message - if (is_ep3(c->version())) { - if (s->ep3_menu_song >= 0) { - send_ep3_change_music(c->channel, s->ep3_menu_song); - } else if (s->pre_lobby_event) { - send_change_event(c, s->pre_lobby_event); - } + if (s->pre_lobby_event && (!is_ep3(c->version()) || s->ep3_menu_song < 0)) { + send_change_event(c, s->pre_lobby_event); + } + if (is_ep3(c->version())) { send_ep3_rank_update(c); send_get_player_info(c); - - } else if (s->pre_lobby_event) { - send_change_event(c, s->pre_lobby_event); } if (s->welcome_message.empty() || @@ -1224,6 +1215,18 @@ static void on_B1(shared_ptr c, uint16_t, uint32_t, string& data) { send_server_time(c); } +static void on_B7_Ep3(shared_ptr c, uint16_t, uint32_t, string& data) { + check_size_v(data.size(), 0); + + // If the client is not in any lobby, assume they're at the main menu and + // send the menu song (if any). + auto s = c->require_server_state(); + auto l = c->lobby.lock(); + if (!l && (s->ep3_menu_song >= 0)) { + send_ep3_change_music(c->channel, s->ep3_menu_song); + } +} + static void on_BA_Ep3(shared_ptr c, uint16_t command, uint32_t, string& data) { const auto& in_cmd = check_size_t(data); auto s = c->require_server_state(); @@ -5412,7 +5415,7 @@ static on_command_t handlers[0x100][NUM_VERSIONS] = { /* 9C */ {nullptr, nullptr, on_9C, on_9C, on_9C, on_9C, on_9C, on_9C, on_9C, on_9C, on_9C, on_9C, on_9C, nullptr}, /* 9D */ {nullptr, nullptr, on_9D_9E, on_9D_9E, on_9D_9E, on_9D_9E, on_9D_9E, on_9D_9E, on_9D_9E, on_9D_9E, on_9D_9E, on_9D_9E, on_9D_9E, nullptr}, /* 9E */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_9D_9E, on_9D_9E, on_9D_9E, on_9D_9E, on_9D_9E, on_9D_9E, on_9E_XB, nullptr}, -/* 9F */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_9F, on_9F, on_9F, on_9F, on_9F, on_9F}, +/* 9F */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_9F, on_9F, on_9F, on_9F, on_9F}, // PC_PATCH BB_PATCH DC_NTE DC_PROTO DCV1 DCV2 PC-NTE PC GCNTE GC EP3TE EP3 XB BB /* A0 */ {nullptr, nullptr, on_A0, on_A0, on_A0, on_A0, on_A0, on_A0, on_A0, on_A0, on_A0, on_A0, on_A0, on_A0}, /* A1 */ {nullptr, nullptr, on_A1, on_A1, on_A1, on_A1, on_A1, on_A1, on_A1, on_A1, on_A1, on_A1, on_A1, on_A1}, @@ -5438,10 +5441,10 @@ static on_command_t handlers[0x100][NUM_VERSIONS] = { /* B4 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* B5 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* B6 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, -/* B7 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_ignored, on_ignored, on_ignored, on_ignored, nullptr, nullptr}, -/* B8 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_ignored, on_ignored, on_ignored, on_ignored, nullptr, nullptr}, -/* B9 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_ignored, on_ignored, on_ignored, on_ignored, nullptr, nullptr}, -/* BA */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_BA_Ep3, on_BA_Ep3, on_BA_Ep3, on_BA_Ep3, nullptr, nullptr}, +/* B7 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_B7_Ep3, on_B7_Ep3, nullptr, nullptr}, +/* B8 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_ignored, on_ignored, nullptr, nullptr}, +/* B9 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_ignored, on_ignored, nullptr, nullptr}, +/* BA */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_BA_Ep3, on_BA_Ep3, nullptr, nullptr}, /* BB */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* BC */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* BD */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, @@ -5457,9 +5460,9 @@ static on_command_t handlers[0x100][NUM_VERSIONS] = { /* C6 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_C6, on_C6, on_C6, on_C6, on_C6, on_C6, on_C6, on_C6}, /* C7 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_C7, on_C7, on_C7, on_C7, on_C7, on_C7, on_C7, on_C7}, /* C8 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_C8, on_C8, on_C8, on_C8, on_C8, on_C8, on_C8, on_C8}, -/* C9 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_6x_C9_CB, on_6x_C9_CB, on_6x_C9_CB, on_6x_C9_CB, on_C9_XB, nullptr}, -/* CA */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_CA_Ep3, on_CA_Ep3, on_CA_Ep3, on_CA_Ep3, nullptr, nullptr}, -/* CB */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_6x_C9_CB, on_6x_C9_CB, on_6x_C9_CB, on_6x_C9_CB, nullptr, nullptr}, +/* C9 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_6x_C9_CB, on_6x_C9_CB, on_C9_XB, nullptr}, +/* CA */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_CA_Ep3, on_CA_Ep3, nullptr, nullptr}, +/* CB */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_6x_C9_CB, on_6x_C9_CB, nullptr, nullptr}, /* CC */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* CD */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* CE */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, @@ -5477,27 +5480,27 @@ static on_command_t handlers[0x100][NUM_VERSIONS] = { /* D9 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_D9, on_D9, on_D9, on_D9, on_D9, on_D9, on_D9, on_D9}, /* DA */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* DB */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_DB_V3, on_DB_V3, on_DB_V3, on_DB_V3, on_DB_V3, nullptr}, -/* DC */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_DC_Ep3, on_DC_Ep3, on_DC_Ep3, on_DC_Ep3, nullptr, on_DC_BB}, +/* DC */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_DC_Ep3, on_DC_Ep3, nullptr, on_DC_BB}, /* DD */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* DE */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* DF */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_DF_BB}, // PC_PATCH BB_PATCH DC_NTE DC_PROTO DCV1 DCV2 PC-NTE PC GCNTE GC EP3TE EP3 XB BB /* E0 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_E0_BB}, /* E1 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, -/* E2 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_E2_Ep3, on_E2_Ep3, on_E2_Ep3, on_E2_Ep3, nullptr, on_E2_BB}, +/* E2 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_E2_Ep3, on_E2_Ep3, nullptr, on_E2_BB}, /* E3 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_E3_BB}, -/* E4 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_E4_Ep3, on_E4_Ep3, on_E4_Ep3, on_E4_Ep3, nullptr, nullptr}, -/* E5 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_E5_Ep3, on_E5_Ep3, on_E5_Ep3, on_E5_Ep3, nullptr, on_E5_BB}, -/* E6 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_08_E6, on_08_E6, on_08_E6, on_08_E6, nullptr, nullptr}, -/* E7 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_0C_C1_E7_EC, on_0C_C1_E7_EC, on_0C_C1_E7_EC, on_0C_C1_E7_EC, nullptr, on_E7_BB}, +/* E4 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_E4_Ep3, on_E4_Ep3, nullptr, nullptr}, +/* E5 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_E5_Ep3, on_E5_Ep3, nullptr, on_E5_BB}, +/* E6 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_08_E6, on_08_E6, nullptr, nullptr}, +/* E7 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_0C_C1_E7_EC, on_0C_C1_E7_EC, nullptr, on_E7_BB}, /* E8 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_E8_BB}, /* E9 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* EA */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_EA_BB}, /* EB */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_EB_BB}, -/* EC */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_0C_C1_E7_EC, on_0C_C1_E7_EC, on_0C_C1_E7_EC, on_0C_C1_E7_EC, nullptr, on_EC_BB}, +/* EC */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_0C_C1_E7_EC, on_0C_C1_E7_EC, nullptr, on_EC_BB}, /* ED */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_ED_BB}, -/* EE */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_EE_Ep3, on_EE_Ep3, on_EE_Ep3, on_EE_Ep3, nullptr, nullptr}, -/* EF */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_EF_Ep3, on_EF_Ep3, on_EF_Ep3, on_EF_Ep3, nullptr, nullptr}, +/* EE */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_EE_Ep3, on_EE_Ep3, nullptr, nullptr}, +/* EF */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, on_EF_Ep3, on_EF_Ep3, nullptr, nullptr}, // PC_PATCH BB_PATCH DC_NTE DC_PROTO DCV1 DCV2 PC-NTE PC GCNTE GC EP3TE EP3 XB BB /* F0 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* F1 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, diff --git a/src/ServerState.cc b/src/ServerState.cc index 3cef7200..f546d91a 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -80,8 +80,9 @@ void ServerState::add_client_to_available_lobby(shared_ptr c) { } if (!added_to_lobby.get()) { - for (const auto& l : this->public_lobby_search_order) { + for (const auto& lobby_id : this->public_lobby_search_order(c->version())) { try { + auto l = this->find_lobby(lobby_id); if (l && !l->is_game() && l->check_flag(Lobby::Flag::PUBLIC) && @@ -336,6 +337,11 @@ const vector>& ServerState::proxy_destinations(Version ve } } +const vector ServerState::public_lobby_search_order(Version version) const { + static_assert(NUM_VERSIONS == 14, "Don\'t forget to update the public lobby search orders in config.json"); + return this->public_lobby_search_orders.at(static_cast(version)); +} + shared_ptr> ServerState::information_contents_for_client(shared_ptr c) const { return is_v1_or_v2(c->version()) ? this->information_contents_v2 : this->information_contents_v3; } @@ -664,31 +670,31 @@ void ServerState::load_config() { this->all_addresses.erase(""); this->all_addresses.emplace("", this->external_address); - this->client_ping_interval_usecs = json.get_int("ClientPingInterval", this->client_ping_interval_usecs); - this->client_idle_timeout_usecs = json.get_int("ClientIdleTimeout", this->client_idle_timeout_usecs); + this->client_ping_interval_usecs = json.get_int("ClientPingInterval", 30000000); + this->client_idle_timeout_usecs = json.get_int("ClientIdleTimeout", 60000000); - this->ip_stack_debug = json.get_bool("IPStackDebug", this->ip_stack_debug); - this->allow_unregistered_users = json.get_bool("AllowUnregisteredUsers", this->allow_unregistered_users); - this->allow_pc_nte = json.get_bool("AllowPCNTE", this->allow_pc_nte); - this->use_temp_licenses_for_prototypes = json.get_bool("UseTemporaryLicensesForPrototypes", this->use_temp_licenses_for_prototypes); - this->allowed_drop_modes_v1_v2_normal = json.get_int("AllowedDropModesV1V2Normal", this->allowed_drop_modes_v1_v2_normal); - this->allowed_drop_modes_v1_v2_battle = json.get_int("AllowedDropModesV1V2Battle", this->allowed_drop_modes_v1_v2_battle); - this->allowed_drop_modes_v1_v2_challenge = json.get_int("AllowedDropModesV1V2Challenge", this->allowed_drop_modes_v1_v2_challenge); - this->allowed_drop_modes_v3_normal = json.get_int("AllowedDropModesV3Normal", this->allowed_drop_modes_v3_normal); - this->allowed_drop_modes_v3_battle = json.get_int("AllowedDropModesV3Battle", this->allowed_drop_modes_v3_battle); - this->allowed_drop_modes_v3_challenge = json.get_int("AllowedDropModesV3Challenge", this->allowed_drop_modes_v3_challenge); - this->allowed_drop_modes_v4_normal = json.get_int("AllowedDropModesV4Normal", this->allowed_drop_modes_v4_normal); - this->allowed_drop_modes_v4_battle = json.get_int("AllowedDropModesV4Battle", this->allowed_drop_modes_v4_battle); - this->allowed_drop_modes_v4_challenge = json.get_int("AllowedDropModesV4Challenge", this->allowed_drop_modes_v4_challenge); - this->default_drop_mode_v1_v2_normal = json.get_enum("DefaultDropModeV1V2Normal", this->default_drop_mode_v1_v2_normal); - this->default_drop_mode_v1_v2_battle = json.get_enum("DefaultDropModeV1V2Battle", this->default_drop_mode_v1_v2_battle); - this->default_drop_mode_v1_v2_challenge = json.get_enum("DefaultDropModeV1V2Challenge", this->default_drop_mode_v1_v2_challenge); - this->default_drop_mode_v3_normal = json.get_enum("DefaultDropModeV3Normal", this->default_drop_mode_v3_normal); - this->default_drop_mode_v3_battle = json.get_enum("DefaultDropModeV3Battle", this->default_drop_mode_v3_battle); - this->default_drop_mode_v3_challenge = json.get_enum("DefaultDropModeV3Challenge", this->default_drop_mode_v3_challenge); - this->default_drop_mode_v4_normal = json.get_enum("DefaultDropModeV4Normal", this->default_drop_mode_v4_normal); - this->default_drop_mode_v4_battle = json.get_enum("DefaultDropModeV4Battle", this->default_drop_mode_v4_battle); - this->default_drop_mode_v4_challenge = json.get_enum("DefaultDropModeV4Challenge", this->default_drop_mode_v4_challenge); + this->ip_stack_debug = json.get_bool("IPStackDebug", false); + this->allow_unregistered_users = json.get_bool("AllowUnregisteredUsers", false); + this->allow_pc_nte = json.get_bool("AllowPCNTE", false); + this->use_temp_licenses_for_prototypes = json.get_bool("UseTemporaryLicensesForPrototypes", true); + this->allowed_drop_modes_v1_v2_normal = json.get_int("AllowedDropModesV1V2Normal", 0x1F); + this->allowed_drop_modes_v1_v2_battle = json.get_int("AllowedDropModesV1V2Battle", 0x07); + this->allowed_drop_modes_v1_v2_challenge = json.get_int("AllowedDropModesV1V2Challenge", 0x07); + this->allowed_drop_modes_v3_normal = json.get_int("AllowedDropModesV3Normal", 0x1F); + this->allowed_drop_modes_v3_battle = json.get_int("AllowedDropModesV3Battle", 0x07); + this->allowed_drop_modes_v3_challenge = json.get_int("AllowedDropModesV3Challenge", 0x07); + this->allowed_drop_modes_v4_normal = json.get_int("AllowedDropModesV4Normal", 0x1D); + this->allowed_drop_modes_v4_battle = json.get_int("AllowedDropModesV4Battle", 0x05); + this->allowed_drop_modes_v4_challenge = json.get_int("AllowedDropModesV4Challenge", 0x05); + this->default_drop_mode_v1_v2_normal = json.get_enum("DefaultDropModeV1V2Normal", Lobby::DropMode::CLIENT); + this->default_drop_mode_v1_v2_battle = json.get_enum("DefaultDropModeV1V2Battle", Lobby::DropMode::CLIENT); + this->default_drop_mode_v1_v2_challenge = json.get_enum("DefaultDropModeV1V2Challenge", Lobby::DropMode::CLIENT); + this->default_drop_mode_v3_normal = json.get_enum("DefaultDropModeV3Normal", Lobby::DropMode::CLIENT); + this->default_drop_mode_v3_battle = json.get_enum("DefaultDropModeV3Battle", Lobby::DropMode::CLIENT); + this->default_drop_mode_v3_challenge = json.get_enum("DefaultDropModeV3Challenge", Lobby::DropMode::CLIENT); + this->default_drop_mode_v4_normal = json.get_enum("DefaultDropModeV4Normal", Lobby::DropMode::SERVER_SHARED); + this->default_drop_mode_v4_battle = json.get_enum("DefaultDropModeV4Battle", Lobby::DropMode::SERVER_SHARED); + this->default_drop_mode_v4_challenge = json.get_enum("DefaultDropModeV4Challenge", Lobby::DropMode::SERVER_SHARED); if ((this->default_drop_mode_v4_normal == Lobby::DropMode::CLIENT) || (this->default_drop_mode_v4_battle == Lobby::DropMode::CLIENT) || (this->default_drop_mode_v4_challenge == Lobby::DropMode::CLIENT)) { @@ -707,11 +713,11 @@ void ServerState::load_config() { } catch (const out_of_range&) { } - this->persistent_game_idle_timeout_usecs = json.get_int("PersistentGameIdleTimeout", this->persistent_game_idle_timeout_usecs); - this->cheat_mode_behavior = parse_behavior_switch("CheatModeBehavior", this->cheat_mode_behavior); - this->default_rare_notifs_enabled = json.get_bool("RareNotificationsEnabledByDefault", this->default_rare_notifs_enabled); - this->ep3_send_function_call_enabled = json.get_bool("EnableEpisode3SendFunctionCall", this->ep3_send_function_call_enabled); - this->catch_handler_exceptions = json.get_bool("CatchHandlerExceptions", this->catch_handler_exceptions); + this->persistent_game_idle_timeout_usecs = json.get_int("PersistentGameIdleTimeout", 0); + this->cheat_mode_behavior = parse_behavior_switch("CheatModeBehavior", BehaviorSwitch::OFF_BY_DEFAULT); + this->default_rare_notifs_enabled = json.get_bool("RareNotificationsEnabledByDefault", false); + this->ep3_send_function_call_enabled = json.get_bool("EnableEpisode3SendFunctionCall", false); + this->catch_handler_exceptions = json.get_bool("CatchHandlerExceptions", true); auto parse_int_list = +[](const JSON& json) -> vector { vector ret; @@ -721,16 +727,24 @@ void ServerState::load_config() { return ret; }; - this->ep3_infinite_meseta = json.get_bool("Episode3InfiniteMeseta", this->ep3_infinite_meseta); - this->ep3_defeat_player_meseta_rewards = parse_int_list(json.get("Episode3DefeatPlayerMeseta", JSON::list())); - this->ep3_defeat_com_meseta_rewards = parse_int_list(json.get("Episode3DefeatCOMMeseta", JSON::list())); - this->ep3_final_round_meseta_bonus = json.get_int("Episode3FinalRoundMesetaBonus", this->ep3_final_round_meseta_bonus); - this->ep3_jukebox_is_free = json.get_bool("Episode3JukeboxIsFree", this->ep3_jukebox_is_free); - this->ep3_behavior_flags = json.get_int("Episode3BehaviorFlags", this->ep3_behavior_flags); - this->ep3_card_auction_points = json.get_int("CardAuctionPoints", this->ep3_card_auction_points); - this->hide_download_commands = json.get_bool("HideDownloadCommands", this->hide_download_commands); - this->proxy_allow_save_files = json.get_bool("ProxyAllowSaveFiles", this->proxy_allow_save_files); - this->proxy_enable_login_options = json.get_bool("ProxyEnableLoginOptions", this->proxy_enable_login_options); + this->ep3_infinite_meseta = json.get_bool("Episode3InfiniteMeseta", false); + try { + this->ep3_defeat_player_meseta_rewards = parse_int_list(json.at("Episode3DefeatPlayerMeseta")); + } catch (const out_of_range&) { + this->ep3_defeat_player_meseta_rewards = {300, 400, 500, 600, 700}; + } + try { + this->ep3_defeat_com_meseta_rewards = parse_int_list(json.get("Episode3DefeatCOMMeseta", JSON::list())); + } catch (const out_of_range&) { + this->ep3_defeat_com_meseta_rewards = {100, 200, 300, 400, 500}; + } + this->ep3_final_round_meseta_bonus = json.get_int("Episode3FinalRoundMesetaBonus", 300); + this->ep3_jukebox_is_free = json.get_bool("Episode3JukeboxIsFree", false); + this->ep3_behavior_flags = json.get_int("Episode3BehaviorFlags", false); + this->ep3_card_auction_points = json.get_int("CardAuctionPoints", 0); + this->hide_download_commands = json.get_bool("HideDownloadCommands", true); + this->proxy_allow_save_files = json.get_bool("ProxyAllowSaveFiles", true); + this->proxy_enable_login_options = json.get_bool("ProxyEnableLoginOptions", false); try { const auto& i = json.at("CardAuctionSize"); @@ -746,6 +760,7 @@ void ServerState::load_config() { this->ep3_card_auction_max_size = 0; } + this->ep3_card_auction_pool.clear(); try { for (const auto& it : json.get_dict("CardAuctionPool")) { uint16_t card_id; @@ -763,6 +778,9 @@ void ServerState::load_config() { } catch (const out_of_range&) { } + for (auto& trap_card_ids : this->ep3_trap_card_ids) { + trap_card_ids.clear(); + } try { const auto& ep3_trap_cards_json = json.get_list("Episode3TrapCards"); if (!ep3_trap_cards_json.empty()) { @@ -855,8 +873,8 @@ void ServerState::load_config() { } } + this->quest_F95E_results.clear(); try { - this->quest_F95E_results.clear(); 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()) { @@ -868,8 +886,8 @@ void ServerState::load_config() { } } catch (const out_of_range&) { } + this->quest_F95F_results.clear(); try { - this->quest_F95F_results.clear(); for (const auto& it : json.get_list("QuestF95FResultItems")) { auto& list = it->as_list(); size_t price = list.at(0)->as_int(); @@ -877,23 +895,24 @@ void ServerState::load_config() { } } catch (const out_of_range&) { } + this->quest_F960_success_results.clear(); + this->quest_F960_failure_results = QuestF960Result(); try { - this->quest_F960_success_results.clear(); 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(); try { - this->secret_lottery_results.clear(); 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&) { } - this->bb_global_exp_multiplier = json.get_int("BBGlobalEXPMultiplier", this->bb_global_exp_multiplier); + this->bb_global_exp_multiplier = json.get_int("BBGlobalEXPMultiplier", 1); set_log_levels_from_json(json.get("LogLevels", JSON::dict())); @@ -904,20 +923,51 @@ void ServerState::load_config() { } catch (const out_of_range&) { } - this->allow_dc_pc_games = json.get_bool("AllowDCPCGames", this->allow_dc_pc_games); - this->allow_gc_xb_games = json.get_bool("AllowGCXBGames", this->allow_gc_xb_games); + this->allow_dc_pc_games = json.get_bool("AllowDCPCGames", true); + this->allow_gc_xb_games = json.get_bool("AllowGCXBGames", true); + for (auto& order : this->public_lobby_search_orders) { + order.clear(); + } try { - auto v = json.at("LobbyEvent"); - uint8_t event = v.is_int() ? v.as_int() : event_for_name(v.as_string()); - this->pre_lobby_event = event; - for (const auto& l : this->all_lobbies()) { - l->event = event; + const auto& orders_json = json.get_list("LobbySearchOrders"); + for (size_t v_s = 0; v_s < orders_json.size(); v_s++) { + auto& order = this->public_lobby_search_orders.at(v_s); + const auto& order_json = orders_json.at(v_s); + for (const auto& it : order_json->as_list()) { + order.emplace_back(it->as_int()); + } } } catch (const out_of_range&) { } - this->ep3_menu_song = json.get_int("Episode3MenuSong", this->ep3_menu_song); + for (size_t z = 1; z <= 20; z++) { + auto l = this->find_lobby(z); + if (l) { + l->event = 0; + } + } + try { + const auto& events_json = json.get_list("LobbyEvents"); + for (size_t z = 0; z < events_json.size(); z++) { + const auto& v = events_json.at(z); + uint8_t event = v->is_int() ? v->as_int() : event_for_name(v->as_string()); + const auto& l = this->find_lobby(z + 1); + if (l && l->check_flag(Lobby::Flag::DEFAULT)) { + l->event = event; + } + } + } catch (const out_of_range&) { + } + + this->pre_lobby_event = 0; + try { + auto v = json.at("MenuEvent"); + this->pre_lobby_event = v.is_int() ? v.as_int() : event_for_name(v.as_string()); + } catch (const out_of_range&) { + } + + this->ep3_menu_song = json.get_int("Episode3MenuSong", -1); try { this->quest_category_index = make_shared(json.at("QuestCategories")); @@ -1028,6 +1078,7 @@ void ServerState::load_config() { this->pc_patch_server_message = json.get_string("PCPatchServerMessage", ""); this->bb_patch_server_message = json.get_string("BBPatchServerMessage", ""); + this->team_reward_defs_json = nullptr; try { this->team_reward_defs_json = std::move(json.at("TeamRewards")); } catch (const out_of_range&) { @@ -1480,21 +1531,7 @@ void ServerState::create_default_lobbies() { if (!allow_non_ep3) { l->episode = Episode::EP3; } - - if (allow_non_ep3) { - this->public_lobby_search_order.emplace_back(l); - } else { - ep3_only_lobbies.emplace_back(l); - } } - - // Annoyingly, the CARD lobbies should be searched first, but are sent at the - // end of the lobby list command, so we have to change the search order - // manually here. - this->public_lobby_search_order.insert( - this->public_lobby_search_order.begin(), - ep3_only_lobbies.begin(), - ep3_only_lobbies.end()); } void ServerState::create_load_step_graph() { diff --git a/src/ServerState.hh b/src/ServerState.hh index 8fc4fbab..8e081941 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -222,7 +222,7 @@ struct ServerState : public std::enable_shared_from_this { std::map> id_to_lobby; std::unordered_set> lobbies_to_destroy; std::shared_ptr destroy_lobbies_event; - std::vector> public_lobby_search_order; + std::array, NUM_VERSIONS> public_lobby_search_orders; std::atomic next_lobby_id = 1; uint8_t pre_lobby_event = 0; int32_t ep3_menu_song = -1; @@ -288,6 +288,8 @@ struct ServerState : public std::enable_shared_from_this { std::string describe_item(Version version, const ItemData& item, bool include_color_codes) const; ItemData parse_item_description(Version version, const std::string& description) const; + const std::vector public_lobby_search_order(Version version) const; + std::shared_ptr> information_contents_for_client(std::shared_ptr c) const; std::shared_ptr quest_index(Version version) const; diff --git a/system/config.example.json b/system/config.example.json index 523c0b59..1d1b78d6 100644 --- a/system/config.example.json +++ b/system/config.example.json @@ -323,16 +323,124 @@ "PCPatchServerMessage": "newserv patch server\r\n\r\nThis server is not affiliated with, sponsored by, or in any other way connected to SEGA or Sonic Team, and is owned and operated completely independently.", "BBPatchServerMessage": "$C7newserv patch server\n\nThis server is not affiliated with, sponsored by, or in any\nother way connected to SEGA or Sonic Team, and is owned\nand operated completely independently.", - // Default lobby event. If set, this sets the holiday event in all lobbies at - // server start time, as well as the pre-lobby holiday event. The event can be - // changed in each lobby independently with the $event command, or in all - // lobbies with the $allevent command. When a game is created, it inherits the - // holiday event from the lobby from which it was created. - // The value for this field can be a string like "xmas" (the names used here - // are the same as for the $event command), or an integer. - // "LobbyEvent": "xmas", + // Lobby search orders. When a player joins the lobby from the main menu, they + // are placed into the first lobby in the list that has empty spaces. In these + // lists, CARD lobbies C1-C5 are referenced as lobbies 16-20. + // The number of lobbies is hardcoded in the client and cannot be changed, so + // the server enforces these limits as well. Thus, the server will not add + // DCv1 players to lobbies above 10, for example, even if they are specified + // in these lists. Removing lobbies from these lists also does not prevent + // players from joining those lobbies via the lobby teleporter. + "LobbySearchOrders": [ + [], // PC patch server (unused) + [], // BB patch server (unused) + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], // DC NTE + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], // DC 11/2000 prototype + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], // DC V1 + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // DC V2 + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // PC NTE + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // PC + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // GC NTE + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // GC + [16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // GC Ep3 NTE + [16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // GC Ep3 + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // Xbox + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], // BB + ], + + // Lobby holiday events. The event can be changed in each lobby independently + // with the $event command, or in all lobbies with the $allevent command. When + // a game is created, it inherits the holiday event from the lobby from which + // it was created. + // The values in this list can be strings like "xmas" (the names used here are + // the same as for the $event command), or an integer. + // There are always 20 lobbies; if a player can't be added to any public + // lobby, they are added to a dynamically-created private overflow lobby + // instead, which uses the event specified in MenuEvent below. + // The events are: "none", "xmas", "val", "easter", "hallo", "sonic", + // "newyear", "summer", "white", "wedding", "fall", "s-spring", "s-summer", + // and "spring". + "LobbyEvents": [ + "none", // Lobby 1 + "none", // Lobby 2 + "none", // Lobby 3 + "none", // Lobby 4 + "none", // Lobby 5 + "none", // Lobby 6 + "none", // Lobby 7 + "none", // Lobby 8 + "none", // Lobby 9 + "none", // Lobby 10 (or Lobby 0 on early versions) + "none", // Lobby 11 (DCv2 and later only) + "none", // Lobby 12 (DCv2 and later only) + "none", // Lobby 13 (DCv2 and later only) + "none", // Lobby 14 (DCv2 and later only) + "none", // Lobby 15 (DCv2 and later only) + "none", // Lobby C1 (Episode 3 only) + "none", // Lobby C2 (Episode 3 only) + "none", // Lobby C3 (Episode 3 only) + "none", // Lobby C4 (Episode 3 only) + "none", // Lobby C5 (Episode 3 only) + ], + + // Menu event. This is the holiday event during the lobby overview while at + // the main menu. + "MenuEvent": "none", + // Episode 3 menu song. If set, Episode 3 clients will hear this song when - // they are at the newserv main menu. If set, this value must be an integer. + // they are at the newserv main menu. The values are: + // 0: "Let the winds blow - Theme of PSO Episode3 -" + // 1: "Gate" + // 2: "Tune" + // 3: "Code" + // 4: "NEW LIFES" + // 5: "RIDE ON" + // 6: "ADVICES" + // 7: "Morgue PART1" + // 8: "Unguis lapis" + // 9: "Via Tubus " + // 10: "Tower of Caelum" + // 11: "Mortis Fons" + // 12: "Lupus Silva PART1 from Mother earth of dishonesty" + // 13: "Lupus Silva PART2 from Mother earth of dishonesty" + // 14: "Molae Venti " + // 15: "Tener Sinus" + // 16: "The whole new world - PSO OPENING THEME -" + // 17: "World with me - PSO EP2 ENDING THEME -" + // 18: "Can still see the light - PSO ENDING THEME -" + // 19: "Day dawns" + // 20: "Nebula Montana PART1 from Jungle -A forest cage-" + // 21: "Nebula Montana PART2 from Jungle -A forest cage-" + // 22: "Morgue PART2" + // 23: "Dolor Odor" + // 24: "Ravum Aedes Sacra" + // 25: "IDOLA the strange fruits" + // 26: "Cyber" + // 27: "Special Relaxies" + // 28: "Let the winds blow -Remix Version-" + // 29: "Leavin flow" + // 30: "Rose Confession" + // 31: "Day light" + // 32: "Versus1 -Tricktrack-" + // 33: "Versus2 -A longing to ancient times-" + // 34: "Burning Hearts - Burning Ranger -" + // 35: "Wedding March - SAMBA de AMIGO -" + // 36: "VAMOS A CARNAVAL - SAMBA de AMIGO -" + // 37: "dreams dreams - Nights -" + // 38: "dreams dreams (kids ver) - Nights -" + // 39: "CHANT THIS CHARM - BILLY HATCHER -" + // 40: "Let Mom Sleep - Jet Grind Radio -" + // 41: "THE CONCEPT OF LOVE - Jet Grind Radio Future -" + // 42: "Where is smiley? - NEW ROOMMANIA -" + // 43: "Buggie Running Beeps 01 - Rez -" + // 44: "Skies of Arcadia Opening Theme - Skies of Arcadia -" + // 45: "Shinobi :boutan - Shinobi -" + // 46: "Tsuioku - Panzer Dragoon ZWEI -" + // 47: "Sona mi areru ec sancitu - AZEL Panzer Dragoon RPG -" + // 48: "ANU ORTA VENIYA - Panzer Dragoon ORTA -" + // 49: "LET'S GO AWAY - DAYTONA 53! -" + // 50: "MAIN THEME-SPACE HARRIER - SPACE HARRIER -" + // 51: "OPA-OPA! - Fantasy Zone -" // "Episode3MenuSong": 0, // If this is enabled, all players will have infinite Meseta, effectively diff --git a/tests/config.json b/tests/config.json index 4de5c909..4ed758d0 100644 --- a/tests/config.json +++ b/tests/config.json @@ -96,6 +96,27 @@ "ClientPingInterval": 30000000, "ClientIdleTimeout": 60000000, + "LobbySearchOrders": [ + [], + [], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + ], + "LobbyEvents": [ + "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", + ], + "MenuEvent": "none", + "LogLevels": { "AXMessages": "INFO", "ChannelExceptions": "INFO",