#pragma once #include #include #include #include #include #include #include #include #include "Account.hh" #include "Client.hh" #include "CommonItemSet.hh" #include "DNSServer.hh" #include "Episode3/DataIndexes.hh" #include "Episode3/Tournament.hh" #include "FunctionCompiler.hh" #include "GSLArchive.hh" #include "IPV4RangeSet.hh" #include "ItemNameIndex.hh" #include "ItemParameterTable.hh" #include "ItemTranslationTable.hh" #include "LevelTable.hh" #include "Lobby.hh" #include "Menu.hh" #include "Quest.hh" #include "TeamIndex.hh" #include "WordSelectTable.hh" // Forward declarations due to reference cycles class GameServer; class IPStackSimulator; class HTTPServer; struct PortConfiguration { std::string name; std::string addr; // Blank = listen on all interfaces (default) uint16_t port; Version version; ServerBehavior behavior; }; struct CheatFlags { // This structure describes which behaviors are considered cheating (that is, // require cheat mode to be enabled or the user to have the CHEAT_ANYWHERE // account flag). A false value here means that that particular behavior is // NOT cheating, so cheat mode is NOT required. bool create_items = true; bool edit_section_id = true; bool edit_stats = true; bool ep3_replace_assist = true; bool ep3_unset_field_character = true; bool infinite_hp_tp = true; bool fast_kills = true; bool insufficient_minimum_level = true; bool override_random_seed = true; bool override_section_id = true; bool override_variations = true; bool proxy_override_drops = true; bool reset_materials = false; bool warp = true; CheatFlags() = default; explicit CheatFlags(const phosg::JSON& json); }; struct ServerState : public std::enable_shared_from_this { enum class RunShellBehavior { DEFAULT = 0, ALWAYS, NEVER, }; enum class BehaviorSwitch { OFF = 0, OFF_BY_DEFAULT, ON_BY_DEFAULT, ON, }; static inline bool behavior_enabled(BehaviorSwitch b) { return (b == BehaviorSwitch::ON_BY_DEFAULT) || (b == BehaviorSwitch::ON); } static inline bool behavior_can_be_overridden(BehaviorSwitch b) { return (b == BehaviorSwitch::OFF_BY_DEFAULT) || (b == BehaviorSwitch::ON_BY_DEFAULT); } uint64_t creation_time; std::shared_ptr io_context; std::string config_filename; std::shared_ptr config_json; bool one_time_config_loaded = false; bool default_lobbies_created = false; size_t num_worker_threads = 0; std::unique_ptr thread_pool; std::string name; std::unordered_map> name_to_port_config; std::unordered_map> number_to_port_config; std::string username; std::string dns_server_addr; uint16_t dns_server_port = 0; std::vector ip_stack_addresses; std::vector ppp_stack_addresses; std::vector ppp_raw_addresses; std::vector http_addresses; uint64_t client_ping_interval_usecs = 30000000; uint64_t client_idle_timeout_usecs = 60000000; uint64_t patch_client_idle_timeout_usecs = 300000000; bool is_debug = false; bool ip_stack_debug = false; bool allow_unregistered_users = false; bool allow_pc_nte = false; bool use_temp_accounts_for_prototypes = true; std::array compatibility_groups = {}; bool enable_chat_commands = true; size_t num_backup_character_slots = 16; std::unique_ptr> version_name_colors; uint32_t client_customization_name_color = 0x00000000; uint8_t allowed_drop_modes_v1_v2_normal = 0x1F; uint8_t allowed_drop_modes_v1_v2_battle = 0x07; uint8_t allowed_drop_modes_v1_v2_challenge = 0x07; uint8_t allowed_drop_modes_v3_normal = 0x1F; uint8_t allowed_drop_modes_v3_battle = 0x07; uint8_t allowed_drop_modes_v3_challenge = 0x07; uint8_t allowed_drop_modes_v4_normal = 0x1D; // CLIENT not allowed uint8_t allowed_drop_modes_v4_battle = 0x05; uint8_t allowed_drop_modes_v4_challenge = 0x05; ServerDropMode default_drop_mode_v1_v2_normal = ServerDropMode::CLIENT; ServerDropMode default_drop_mode_v1_v2_battle = ServerDropMode::CLIENT; ServerDropMode default_drop_mode_v1_v2_challenge = ServerDropMode::CLIENT; ServerDropMode default_drop_mode_v3_normal = ServerDropMode::CLIENT; ServerDropMode default_drop_mode_v3_battle = ServerDropMode::CLIENT; ServerDropMode default_drop_mode_v3_challenge = ServerDropMode::CLIENT; ServerDropMode default_drop_mode_v4_normal = ServerDropMode::SERVER_SHARED; ServerDropMode default_drop_mode_v4_battle = ServerDropMode::SERVER_SHARED; ServerDropMode default_drop_mode_v4_challenge = ServerDropMode::SERVER_SHARED; std::unordered_map quest_flag_rewrites_v1_v2; std::unordered_map quest_flag_rewrites_v3; std::unordered_map quest_flag_rewrites_v4; std::unordered_map> quest_counter_fields; // For $qfread command uint64_t persistent_game_idle_timeout_usecs = 0; std::unordered_map enable_send_function_call_quest_numbers; bool enable_v3_v4_protected_subcommands = false; bool ep3_infinite_meseta = false; std::vector ep3_defeat_player_meseta_rewards = {400, 500, 600, 700, 800}; std::vector ep3_defeat_com_meseta_rewards = {100, 200, 300, 400, 500}; uint32_t ep3_final_round_meseta_bonus = 300; bool ep3_jukebox_is_free = false; uint32_t ep3_behavior_flags = 0; bool hide_download_commands = true; RunShellBehavior run_shell_behavior = RunShellBehavior::DEFAULT; BehaviorSwitch cheat_mode_behavior = BehaviorSwitch::OFF_BY_DEFAULT; bool default_switch_assist_enabled = false; bool use_game_creator_section_id = false; bool use_psov2_rand_crypt = false; // Used in some tests bool rare_notifs_enabled_for_client_drops = false; bool default_rare_notifs_enabled_v1_v2 = false; bool default_rare_notifs_enabled_v3_v4 = false; std::unordered_set notify_game_for_item_primary_identifiers_v1_v2; std::unordered_set notify_game_for_item_primary_identifiers_v3; std::unordered_set notify_game_for_item_primary_identifiers_v4; std::unordered_set notify_server_for_item_primary_identifiers_v1_v2; std::unordered_set notify_server_for_item_primary_identifiers_v3; std::unordered_set notify_server_for_item_primary_identifiers_v4; bool notify_server_for_max_level_achieved = false; std::vector> bb_private_keys; std::shared_ptr> bb_default_keyboard_config; std::shared_ptr> bb_default_joystick_config; std::shared_ptr function_code_index; std::shared_ptr pc_patch_file_index; std::shared_ptr bb_patch_file_index; std::unordered_map> map_file_for_source_hash; std::map, NUM_VERSIONS>> map_files_for_free_play_key; std::unordered_map> supermap_for_source_hash_sum; std::unordered_map> supermap_for_free_play_key; std::shared_ptr room_layout_index; std::shared_ptr bb_stream_files_cache; std::shared_ptr bb_system_cache; std::shared_ptr gba_files_cache; std::shared_ptr dol_file_index; std::shared_ptr ep3_card_index; std::shared_ptr ep3_card_index_trial; std::shared_ptr ep3_map_index; std::shared_ptr ep3_com_deck_index; std::shared_ptr ep3_default_ex_values; std::shared_ptr ep3_tournament_ex_values; std::shared_ptr ep3_tournament_final_round_ex_values; std::shared_ptr quest_category_index; std::shared_ptr quest_index; std::shared_ptr level_table_v1_v2; std::shared_ptr level_table_v3; std::shared_ptr level_table_v4; std::shared_ptr battle_params; std::shared_ptr bb_data_gsl; std::unordered_map> common_item_sets; std::unordered_map> rare_item_sets; std::shared_ptr armor_random_set; std::shared_ptr tool_random_set; std::array, 4> weapon_random_sets; // Keyed oin difficulty std::shared_ptr tekker_adjustment_set; std::array, NUM_VERSIONS> item_parameter_tables; std::shared_ptr item_translation_table; std::array, NUM_VERSIONS> item_stack_limits_tables; size_t bb_max_bank_items = 200; size_t bb_max_bank_meseta = 999999; std::shared_ptr mag_evolution_table_v1_v2; std::shared_ptr mag_evolution_table_v3; std::shared_ptr mag_evolution_table_v4; std::shared_ptr text_index; std::array, NUM_VERSIONS> item_name_indexes; std::shared_ptr word_select_table; std::array, NUM_VERSIONS> set_data_tables; std::array, NUM_VERSIONS> set_data_tables_ep1_ult; std::shared_ptr bb_solo_set_data_table; std::shared_ptr bb_solo_set_data_table_ep1_ult; std::array, 4> rare_enemy_rates_by_difficulty; std::shared_ptr rare_enemy_rates_challenge; std::array, 3> min_levels_v1_v2; // Indexed as [episode][difficulty] std::array, 3> min_levels_v3; // Indexed as [episode][difficulty] std::array, 3> min_levels_v4; // Indexed as [episode][difficulty] std::unordered_set bb_required_patches; std::unordered_set auto_patches; CheatFlags cheat_flags; struct QuestF960Result { uint32_t meseta_cost = 0; uint32_t base_probability = 0; uint32_t probability_upgrade = 0; std::array, 7> results; QuestF960Result() = default; QuestF960Result(const phosg::JSON& json, std::shared_ptr name_index); }; // Indexed as [type][difficulty][random_choice] std::vector>> quest_F95E_results; std::vector> quest_F95F_results; // [(num_photon_tickets, item)] std::vector quest_F960_success_results; QuestF960Result quest_F960_failure_results; float bb_global_exp_multiplier = 1.0f; float exp_share_multiplier = 0.5f; float server_global_drop_rate_multiplier = 1.0f; std::shared_ptr ep3_tournament_index; uint16_t ep3_card_auction_points = 0; uint16_t ep3_card_auction_min_size = 0; uint16_t ep3_card_auction_max_size = 0; struct CardAuctionPoolEntry { uint64_t probability; uint16_t card_id; uint16_t min_price; }; std::vector ep3_card_auction_pool; std::array, 5> ep3_trap_card_ids; struct Ep3LobbyBannerEntry { uint32_t type = 1; uint32_t which; // See B9 documentation in CommandFormats.hh std::string data; }; std::vector ep3_lobby_banners; std::shared_ptr account_index; bool allow_saving_accounts = true; std::shared_ptr banned_ipv4_ranges; std::shared_ptr team_index; phosg::JSON team_reward_defs_json; std::shared_ptr information_menu_v2; std::shared_ptr information_menu_v3; std::shared_ptr> information_contents_v2; std::shared_ptr> information_contents_v3; std::shared_ptr proxy_destinations_menu_dc; std::shared_ptr proxy_destinations_menu_pc; std::shared_ptr proxy_destinations_menu_gc; std::shared_ptr proxy_destinations_menu_xb; std::vector> proxy_destinations_dc; std::vector> proxy_destinations_pc; std::vector> proxy_destinations_gc; std::vector> proxy_destinations_xb; std::optional> proxy_destination_patch; std::optional> proxy_destination_bb; std::string welcome_message; std::string pc_patch_server_message; std::string bb_patch_server_message; std::map> id_to_lobby; std::array, NUM_VERSIONS> public_lobby_search_orders; std::vector client_customization_public_lobby_search_order; std::atomic next_lobby_id = 1; uint8_t pre_lobby_event = 0; int32_t ep3_menu_song = -1; std::map all_addresses; uint32_t local_address = 0; uint32_t external_address = 0; bool proxy_allow_save_files = true; std::shared_ptr ip_stack_simulator; std::shared_ptr dns_server; std::shared_ptr game_server; std::shared_ptr http_server; std::unordered_map proxy_persistent_configs; explicit ServerState(const std::string& config_filename = ""); ServerState(const ServerState&) = delete; ServerState(ServerState&&) = delete; ServerState& operator=(const ServerState&) = delete; ServerState& operator=(ServerState&&) = delete; void add_client_to_available_lobby(std::shared_ptr c); void remove_client_from_lobby(std::shared_ptr c); bool change_client_lobby( std::shared_ptr c, std::shared_ptr new_lobby, bool send_join_notification = true, ssize_t required_client_id = -1); void send_lobby_join_notifications(std::shared_ptr l, std::shared_ptr joining_client); std::shared_ptr find_lobby(uint32_t lobby_id); std::vector> all_lobbies(); std::shared_ptr create_lobby(bool is_game); void remove_lobby(std::shared_ptr l); void on_player_left_lobby(std::shared_ptr l, uint8_t leaving_client_id); std::shared_ptr find_client( const std::string* identifier = nullptr, uint64_t account_id = 0, std::shared_ptr l = nullptr); uint32_t connect_address_for_client(std::shared_ptr c) const; uint16_t game_server_port_for_version(Version v) const; std::shared_ptr information_menu(Version version) const; std::shared_ptr proxy_destinations_menu(Version version) const; const std::vector>& proxy_destinations(Version version) const; std::shared_ptr set_data_table(Version version, Episode episode, GameMode mode, Difficulty difficulty) const; inline std::shared_ptr weapon_random_set(Difficulty difficulty) const { return this->weapon_random_sets.at(static_cast(difficulty)); } inline std::shared_ptr rare_enemy_rates(Difficulty difficulty) const { return this->rare_enemy_rates_by_difficulty.at(static_cast(difficulty)); } std::shared_ptr level_table(Version version) const; std::shared_ptr item_parameter_table(Version version) const; std::shared_ptr item_parameter_table_for_encode(Version version) const; std::shared_ptr mag_evolution_table(Version version) const; std::shared_ptr item_stack_limits(Version version) const; std::shared_ptr item_name_index_opt(Version version) const; // Returns null if missing std::shared_ptr item_name_index(Version version) const; // Throws if missing std::string describe_item(Version version, const ItemData& item, uint8_t flags = 0) const; ItemData parse_item_description(Version version, const std::string& description) const; std::shared_ptr common_item_set(Version logic_version, std::shared_ptr q) const; std::shared_ptr rare_item_set(Version logic_version, std::shared_ptr q) const; const std::vector& public_lobby_search_order(Version version, bool is_client_customization) const; inline const std::vector& public_lobby_search_order(std::shared_ptr c) const { return this->public_lobby_search_order(c->version(), c->check_flag(Client::Flag::IS_CLIENT_CUSTOMIZATION)); } inline uint32_t name_color_for_client(Version v, bool is_client_customization) const { if (is_client_customization && this->client_customization_name_color) { return this->client_customization_name_color; } return this->version_name_colors ? this->version_name_colors->at(static_cast(v) - NUM_PATCH_VERSIONS) : 0; } inline uint32_t name_color_for_client(std::shared_ptr c) const { return this->name_color_for_client(c->version(), c->check_flag(Client::Flag::IS_CLIENT_CUSTOMIZATION)); } std::shared_ptr> information_contents_for_client(std::shared_ptr c) const; size_t default_min_level_for_game(Version version, Episode episode, Difficulty difficulty) const; void set_port_configuration(const std::vector& port_configs); std::shared_ptr load_bb_file( const std::string& patch_index_filename, const std::string& gsl_filename = "", const std::string& bb_directory_filename = "") const; std::shared_ptr load_map_file(Version version, const std::string& filename) const; std::shared_ptr load_map_file_uncached(Version version, const std::string& filename) const; std::pair parse_port_spec(const phosg::JSON& json) const; std::vector parse_port_configuration(const phosg::JSON& json) const; static constexpr uint32_t free_play_key( Episode episode, GameMode mode, Difficulty difficulty, uint8_t floor, uint32_t layout, uint32_t entities) { return (static_cast(episode) << 28) | (static_cast(mode) << 26) | (static_cast(difficulty) << 24) | (static_cast(floor) << 16) | (static_cast(layout) << 8) | (static_cast(entities) << 0); } std::shared_ptr get_free_play_supermap( Episode episode, GameMode mode, Difficulty difficulty, uint8_t floor, uint32_t layout, uint32_t entities); std::vector> supermaps_for_variations( Episode episode, GameMode mode, Difficulty difficulty, const Variations& variations); void create_default_lobbies(); void collect_network_addresses(); void load_config_early(); void load_config_late(); void load_bb_private_keys(); void load_bb_system_defaults(); void load_accounts(); void load_teams(); void load_patch_indexes(); void load_maps(); void clear_file_caches(); void load_battle_params(); void load_level_tables(); void load_text_index(); std::shared_ptr create_item_name_index_for_version( std::shared_ptr pmt, std::shared_ptr limits, std::shared_ptr text_index) const; void load_item_name_indexes(); void load_drop_tables(); void load_item_definitions(); void load_set_data_tables(); void load_word_select_table(); void load_ep3_cards(); void load_ep3_maps(bool raise_on_any_failure = false); void load_ep3_tournament_state(); void load_quest_index(bool raise_on_any_failure = false); void compile_functions(bool raise_on_any_failure = false); void load_dol_files(); void load_all(bool enable_thread_pool); void disconnect_all_banned_clients(); };