diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index 850825f3..e1c37443 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -359,11 +359,9 @@ static void server_command_exit(shared_ptr c, const std::u16string&) { send_message_box(c, u""); } - const auto& port_name = version_to_login_port_name.at( - static_cast(c->version())); + const auto& port_name = version_to_login_port_name.at(static_cast(c->version())); auto s = c->require_server_state(); - send_reconnect(c, s->connect_address_for_client(c), - s->name_to_port_config.at(port_name)->port); + send_reconnect(c, s->connect_address_for_client(c), s->name_to_port_config.at(port_name)->port); } } @@ -597,7 +595,7 @@ static void server_command_playrec(shared_ptr c, const std::u16string& a string file_path = file_path_for_recording(args, c->license->serial_number); auto s = c->require_server_state(); - uint32_t flags = Lobby::Flag::NON_V1_ONLY | Lobby::Flag::IS_SPECTATOR_TEAM; + uint32_t flags = Lobby::Flag::IS_SPECTATOR_TEAM; string filename = encode_sjis(args); if (filename[0] == '!') { flags |= Lobby::Flag::START_BATTLE_PLAYER_IMMEDIATELY; @@ -614,8 +612,8 @@ static void server_command_playrec(shared_ptr c, const std::u16string& a shared_ptr record(new Episode3::BattleRecord(data)); shared_ptr battle_player( new Episode3::BattleRecordPlayer(record, s->game_server->get_base())); - auto game = create_game_generic(s, c, args.c_str(), u"", Episode::EP3, GameMode::NORMAL, - 0, flags, nullptr, battle_player); + auto game = create_game_generic( + s, c, args, u"", Episode::EP3, GameMode::NORMAL, 0, flags, false, nullptr, battle_player); if (game) { s->change_client_lobby(c, game); c->flags |= Client::Flag::LOADING; diff --git a/src/Lobby.cc b/src/Lobby.cc index bf5055f6..c14ef073 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -17,7 +17,8 @@ Lobby::Lobby(shared_ptr s, uint32_t id) min_level(0), max_level(0xFFFFFFFF), next_game_item_id(0x00810000), - version(GameVersion::GC), + base_version(GameVersion::GC), + allowed_versions(0xFFFF), section_id(0), episode(Episode::NONE), mode(GameMode::NORMAL), diff --git a/src/Lobby.hh b/src/Lobby.hh index 46f5d7fe..7815d87a 100644 --- a/src/Lobby.hh +++ b/src/Lobby.hh @@ -27,8 +27,7 @@ struct ServerState; struct Lobby : public std::enable_shared_from_this { enum Flag { GAME = 0x00000001, - NON_V1_ONLY = 0x00000002, // DC NTE and DCv1 not allowed - PERSISTENT = 0x00000004, + PERSISTENT = 0x00000002, // Flags used only for games CHEATS_ENABLED = 0x00000100, @@ -45,6 +44,7 @@ struct Lobby : public std::enable_shared_from_this { // Flags used only for lobbies PUBLIC = 0x01000000, DEFAULT = 0x02000000, + V2_AND_LATER = 0x04000000, // Lobby does not appear on v1 }; std::weak_ptr server_state; @@ -69,7 +69,11 @@ struct Lobby : public std::enable_shared_from_this { parray variations; // Game config - GameVersion version; + GameVersion base_version; + // Bits in allowed_versions specify who is allowed to join this game. The + // bits are indexed as (1 << version), where version is a value from the + // QuestScriptVersion enum. + uint16_t allowed_versions; uint8_t section_id; Episode episode; GameMode mode; @@ -127,6 +131,13 @@ struct Lobby : public std::enable_shared_from_this { return this->episode == Episode::EP3; } + inline bool version_is_allowed(QuestScriptVersion v) const { + return this->allowed_versions & (1 << static_cast(v)); + } + inline void allow_version(QuestScriptVersion v) { + this->allowed_versions |= (1 << static_cast(v)); + } + void reassign_leader_on_client_departure(size_t leaving_client_id); size_t count_clients() const; bool any_client_loading() const; diff --git a/src/Main.cc b/src/Main.cc index 494c9d25..cb57f44a 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -1895,7 +1895,7 @@ int main(int argc, char** argv) { shared_ptr dns_server; if (state->dns_server_port && (behavior != Behavior::REPLAY_LOG)) { - config_log.info("Starting DNS server"); + config_log.info("Starting DNS server on port %hu", state->dns_server_port); dns_server.reset(new DNSServer(base, state->local_address, state->external_address)); dns_server->listen("", state->dns_server_port); diff --git a/src/PlayerSubordinates.cc b/src/PlayerSubordinates.cc index 2d21edc1..7490a30e 100644 --- a/src/PlayerSubordinates.cc +++ b/src/PlayerSubordinates.cc @@ -27,6 +27,9 @@ void PlayerDispDataDCPCV3::enforce_v2_limits() { this->visual.char_class = 5; // HUcaseal -> RAcaseal } + // V1/V2 has fewer costumes, so substitute them here too + this->visual.costume %= 9; + // If the player is somehow still not a valid class, make them appear as the // "ninja" NPC if (this->visual.char_class > 8) { diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 4d81088f..5b831a62 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -185,7 +185,7 @@ static void send_main_menu(shared_ptr c) { const auto& l = it.second; if (l->is_game()) { num_games++; - if (l->version == c->version() && + if (l->version_is_allowed(c->quest_version()) && (!l->is_ep3() == !(c->flags & Client::Flag::IS_EPISODE_3))) { num_compatible_games++; } @@ -1119,11 +1119,8 @@ static bool start_ep3_battle_table_game_if_ready(shared_ptr l, int16_t ta auto c = game_clients.begin()->second; auto s = c->require_server_state(); - uint32_t flags = Lobby::Flag::NON_V1_ONLY; u16string name = tourn ? decode_sjis(tourn->get_name()) : u""; - auto game = create_game_generic( - s, c, name, u"", Episode::EP3, - GameMode::NORMAL, 0, flags); + auto game = create_game_generic(s, c, name, u"", Episode::EP3); if (!game) { return false; } @@ -1914,10 +1911,9 @@ static void on_10(shared_ptr c, uint16_t, uint32_t, const string& data) send_lobby_message_box(c, u"$C6You cannot join this\ngame because it is\nfull."); break; } - if ((game->version != c->version()) || + if (!game->version_is_allowed(c->quest_version()) || (!game->is_ep3() != !(c->flags & Client::Flag::IS_EPISODE_3)) || - (!(game->flags & Lobby::Flag::IS_EP3_TRIAL) != !(c->flags & Client::Flag::IS_EP3_TRIAL_EDITION)) || - ((game->flags & Lobby::Flag::NON_V1_ONLY) && (c->flags & Client::Flag::IS_DC_V1))) { + (!(game->flags & Lobby::Flag::IS_EP3_TRIAL) != !(c->flags & Client::Flag::IS_EP3_TRIAL_EDITION))) { send_lobby_message_box(c, u"$C6You cannot join this\ngame because it is\nfor a different\nversion of PSO."); break; } @@ -2375,7 +2371,7 @@ static void on_AC_V3_BB(shared_ptr c, uint16_t, uint32_t, const string& if (c->flags & Client::Flag::LOADING_RUNNING_QUEST) { c->flags &= ~Client::Flag::LOADING_RUNNING_QUEST; - if (l->version != GameVersion::BB) { + if (l->base_version != GameVersion::BB) { throw logic_error("joinable quest started on non-BB version"); } @@ -2395,7 +2391,7 @@ static void on_AC_V3_BB(shared_ptr c, uint16_t, uint32_t, const string& } if (send_quest_barrier_if_all_clients_ready(l) && - (l->version == GameVersion::BB) && + (l->base_version == GameVersion::BB) && l->map && l->quest) { auto dat_contents = prs_decompress(*l->quest->dat_contents()); @@ -3203,6 +3199,7 @@ shared_ptr create_game_generic( GameMode mode, uint8_t difficulty, uint32_t flags, + bool allow_v1, shared_ptr watched_lobby, shared_ptr battle_player) { @@ -3276,7 +3273,54 @@ shared_ptr create_game_generic( (is_ep3_trial ? Lobby::Flag::IS_EP3_TRIAL : 0) | ((s->cheat_mode_behavior == ServerState::CheatModeBehavior::ON_BY_DEFAULT) ? Lobby::Flag::CHEATS_ENABLED : 0); game->password = password; - game->version = c->version(); + + game->allowed_versions = 0; + switch (c->quest_version()) { + case QuestScriptVersion::DC_NTE: + game->allow_version(QuestScriptVersion::DC_NTE); + break; + case QuestScriptVersion::DC_V1: + game->allow_version(QuestScriptVersion::DC_V1); + game->allow_version(QuestScriptVersion::DC_V2); + if (s->allow_dc_pc_games) { + game->allow_version(QuestScriptVersion::PC_V2); + } + break; + case QuestScriptVersion::DC_V2: + case QuestScriptVersion::PC_V2: + if (allow_v1 && (difficulty <= 2)) { + game->allow_version(QuestScriptVersion::DC_V1); + } + game->allow_version(QuestScriptVersion::DC_V2); + if (s->allow_dc_pc_games) { + game->allow_version(QuestScriptVersion::PC_V2); + } + break; + case QuestScriptVersion::GC_NTE: + game->allow_version(QuestScriptVersion::GC_NTE); + break; + case QuestScriptVersion::GC_V3: + game->allow_version(QuestScriptVersion::GC_V3); + if (s->allow_gc_xb_games) { + game->allow_version(QuestScriptVersion::XB_V3); + } + break; + case QuestScriptVersion::XB_V3: + game->allow_version(QuestScriptVersion::XB_V3); + if (s->allow_gc_xb_games) { + game->allow_version(QuestScriptVersion::GC_V3); + } + break; + case QuestScriptVersion::GC_EP3: + game->allow_version(QuestScriptVersion::GC_EP3); + break; + case QuestScriptVersion::BB_V4: + game->allow_version(QuestScriptVersion::BB_V4); + break; + default: + throw logic_error("invalid quest script version"); + } + game->section_id = c->options.override_section_id >= 0 ? c->options.override_section_id : c->game_data.player()->disp.visual.section_id; @@ -3291,7 +3335,7 @@ shared_ptr create_game_generic( game->battle_player = battle_player; battle_player->set_lobby(game); } - if (game->version == GameVersion::BB) { + if (game->base_version == GameVersion::BB) { // TODO: Use appropriate restrictions here if in battle mode game->item_creator.reset(new ItemCreator( s->common_item_set, @@ -3326,7 +3370,7 @@ shared_ptr create_game_generic( generate_variations(game->variations, game->random_crypt, game->episode, is_solo); } - if (game->version == GameVersion::BB) { + if (game->base_version == GameVersion::BB) { for (size_t x = 0; x < 4; x++) { game->next_item_id[x] = (0x00200000 * x) + 0x00010000; } @@ -3382,15 +3426,13 @@ static void on_C1_PC(shared_ptr c, uint16_t, uint32_t, const string& dat const auto& cmd = check_size_t(data); auto s = c->require_server_state(); - uint32_t flags = Lobby::Flag::NON_V1_ONLY; GameMode mode = GameMode::NORMAL; if (cmd.battle_mode) { mode = GameMode::BATTLE; } else if (cmd.challenge_mode) { mode = GameMode::CHALLENGE; } - auto game = create_game_generic( - s, c, cmd.name, cmd.password, Episode::EP1, mode, cmd.difficulty, flags); + auto game = create_game_generic(s, c, cmd.name, cmd.password, Episode::EP1, mode, cmd.difficulty); if (game) { s->change_client_lobby(c, game); c->flags |= Client::Flag::LOADING; @@ -3405,8 +3447,7 @@ static void on_0C_C1_E7_EC(shared_ptr c, uint16_t command, uint32_t, con const auto& cmd = check_size_t>(data); u16string name = decode_sjis(cmd.name); u16string password = decode_sjis(cmd.password); - game = create_game_generic( - s, c, name.c_str(), password.c_str(), Episode::EP1, GameMode::NORMAL, 0, 0); + game = create_game_generic(s, c, name, password); } else { const auto& cmd = check_size_t(data); @@ -3419,16 +3460,13 @@ static void on_0C_C1_E7_EC(shared_ptr c, uint16_t command, uint32_t, con Episode episode = Episode::NONE; uint32_t flags = 0; + bool allow_v1 = false; if (c->version() == GameVersion::DC) { - if (cmd.episode) { - flags |= Lobby::Flag::NON_V1_ONLY; - } + allow_v1 = (cmd.episode == 0); episode = Episode::EP1; } else if (client_is_ep3) { - flags |= Lobby::Flag::NON_V1_ONLY; episode = Episode::EP3; } else { // XB/GC non-Ep3 - flags |= Lobby::Flag::NON_V1_ONLY; episode = cmd.episode == 2 ? Episode::EP2 : Episode::EP1; } @@ -3464,8 +3502,7 @@ static void on_0C_C1_E7_EC(shared_ptr c, uint16_t command, uint32_t, con flags |= Lobby::Flag::IS_SPECTATOR_TEAM; } - game = create_game_generic( - s, c, name.c_str(), password.c_str(), episode, mode, cmd.difficulty, flags, watched_lobby); + game = create_game_generic(s, c, name, password, episode, mode, cmd.difficulty, flags, allow_v1, watched_lobby); if (game && (game->episode == Episode::EP3)) { game->ep3_ex_result_values = s->ep3_default_ex_values; } @@ -3481,7 +3518,6 @@ static void on_C1_BB(shared_ptr c, uint16_t, uint32_t, const string& dat const auto& cmd = check_size_t(data); auto s = c->require_server_state(); - uint32_t flags = Lobby::Flag::NON_V1_ONLY; GameMode mode = GameMode::NORMAL; if (cmd.battle_mode) { mode = GameMode::BATTLE; @@ -3508,8 +3544,7 @@ static void on_C1_BB(shared_ptr c, uint16_t, uint32_t, const string& dat throw runtime_error("invalid episode number"); } - auto game = create_game_generic( - s, c, cmd.name, cmd.password, episode, mode, cmd.difficulty, flags); + auto game = create_game_generic(s, c, cmd.name, cmd.password, episode, mode, cmd.difficulty); if (game) { s->change_client_lobby(c, game); c->flags |= Client::Flag::LOADING; @@ -3539,7 +3574,7 @@ static void on_6F(shared_ptr c, uint16_t, uint32_t, const string& data) c->flags &= (~Client::Flag::LOADING); send_resume_game(l, c); - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { send_set_exp_multiplier(l); } send_server_time(c); diff --git a/src/ReceiveCommands.hh b/src/ReceiveCommands.hh index 981decac..37d9b53f 100644 --- a/src/ReceiveCommands.hh +++ b/src/ReceiveCommands.hh @@ -8,11 +8,12 @@ std::shared_ptr create_game_generic( std::shared_ptr s, std::shared_ptr c, const std::u16string& name, - const std::u16string& password, - Episode episode, - GameMode mode, - uint8_t difficulty, - uint32_t flags, + const std::u16string& password = u"", + Episode episode = Episode::EP1, + GameMode mode = GameMode::NORMAL, + uint8_t difficulty = 0, + uint32_t flags = 0, + bool allow_v1 = false, std::shared_ptr watched_lobby = nullptr, std::shared_ptr battle_player = nullptr); diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 6d56d86a..6bc2821e 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -697,7 +697,7 @@ static void on_drop_partial_stack_t(shared_ptr c, uint8_t command, uint8 if (!l->is_game()) { return; } - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { return; } @@ -738,7 +738,7 @@ static void on_drop_partial_stack(shared_ptr c, uint8_t command, uint8_t static void on_drop_partial_stack_bb(shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size) { auto l = c->require_lobby(); - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { const auto& cmd = check_size_t(data, size); if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) { @@ -791,7 +791,7 @@ static void on_buy_shop_item(shared_ptr c, uint8_t command, uint8_t flag if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) { return; } - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { return; } @@ -828,7 +828,7 @@ static void on_box_or_enemy_item_drop_t(shared_ptr c, uint8_t command, u if (!l->is_game() || (c->lobby_client_id != l->leader_id)) { return; } - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { return; } @@ -870,7 +870,7 @@ static void on_pick_up_item(shared_ptr c, uint8_t command, uint8_t flag, if (!l->is_game()) { return; } - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { // BB clients should never send this; only the server should send this return; } @@ -900,7 +900,7 @@ static void on_pick_up_item(shared_ptr c, uint8_t command, uint8_t flag, static void on_pick_up_item_request(shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size) { // This is handled by the server on BB, and by the leader on other versions auto l = c->require_lobby(); - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { auto& cmd = check_size_t(data, size); if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) { @@ -946,7 +946,7 @@ static void on_equip_unequip_item(shared_ptr c, uint8_t command, uint8_t } else { // Unequip c->game_data.player()->inventory.items[index].flags &= 0xFFFFFFF7; } - } else if (l->version == GameVersion::BB) { + } else if (l->base_version == GameVersion::BB) { throw logic_error("item tracking not enabled in BB game"); } @@ -1021,7 +1021,7 @@ static void on_feed_mag( // a 6x29 immediately after to destroy the fed item. So on BB, we should // remove the fed item here, but on other versions, we allow the following // 6x29 command to do that. - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { c->game_data.player()->remove_item(cmd.fed_item_id, 1, false); } @@ -1049,7 +1049,7 @@ static void on_open_shop_bb_or_ep3_battle_subs(shared_ptr c, uint8_t com } else { const auto& cmd = check_size_t(data, size); - if ((l->version == GameVersion::BB) && l->is_game()) { + if ((l->base_version == GameVersion::BB) && l->is_game()) { if (!l->item_creator) { throw logic_error("item creator missing from BB game"); } @@ -1081,16 +1081,16 @@ static void on_open_shop_bb_or_ep3_battle_subs(shared_ptr c, uint8_t com static void on_open_bank_bb_or_card_trade_counter_ep3(shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size) { auto l = c->require_lobby(); - if ((l->version == GameVersion::BB) && l->is_game()) { + if ((l->base_version == GameVersion::BB) && l->is_game()) { send_bank(c); - } else if ((l->version == GameVersion::GC) && l->is_ep3()) { + } else if ((l->base_version == GameVersion::GC) && l->is_ep3()) { forward_subcommand(c, command, flag, data, size); } } static void on_ep3_private_word_select_bb_bank_action(shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size) { auto l = c->require_lobby(); - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { const auto& cmd = check_size_t(data, size); if (!l->is_game()) { @@ -1141,7 +1141,7 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr c, uint static void on_sort_inventory_bb(shared_ptr c, uint8_t, uint8_t, const void* data, size_t size) { auto l = c->require_lobby(); - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { const auto& cmd = check_size_t(data, size); if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { @@ -1178,7 +1178,7 @@ static void on_entity_drop_item_request(shared_ptr c, uint8_t command, u // If the game is not BB, forward the request to the leader (if drops are // enabled, or just ignore it) instead of generating the item drop command - if (l->version != GameVersion::BB) { + if (l->base_version != GameVersion::BB) { if (l->flags & Lobby::Flag::DROPS_ENABLED) { forward_subcommand(c, command, flag, data, size); } @@ -1304,7 +1304,7 @@ static void on_set_quest_flag(shared_ptr c, uint8_t command, uint8_t fla static void on_enemy_hit(shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size) { auto l = c->require_lobby(); - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { const auto& cmd = check_size_t(data, size); if (!l->is_game()) { @@ -1333,7 +1333,7 @@ static void on_enemy_hit(shared_ptr c, uint8_t command, uint8_t flag, co static void on_charge_attack_bb(shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size) { auto l = c->require_lobby(); - if (l->version != GameVersion::BB) { + if (l->base_version != GameVersion::BB) { throw runtime_error("BB-only command sent in non-BB game"); } @@ -1375,7 +1375,7 @@ static void on_steal_exp_bb(shared_ptr c, uint8_t, uint8_t, const void* auto s = c->require_server_state(); auto l = c->require_lobby(); - if (l->version != GameVersion::BB) { + if (l->base_version != GameVersion::BB) { throw runtime_error("BB-only command sent in non-BB game"); } if (!l->map) { @@ -1413,7 +1413,7 @@ static void on_enemy_killed_bb(shared_ptr c, uint8_t command, uint8_t fl auto s = c->require_server_state(); auto l = c->require_lobby(); - if (l->version != GameVersion::BB) { + if (l->base_version != GameVersion::BB) { throw runtime_error("BB-only command sent in non-BB game"); } @@ -1574,7 +1574,7 @@ static void on_destroy_ground_item(shared_ptr c, uint8_t command, uint8_ static void on_identify_item_bb(shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size) { auto l = c->require_lobby(); - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { const auto& cmd = check_size_t(data, size); if (!l->is_game()) { return; @@ -1605,7 +1605,7 @@ static void on_identify_item_bb(shared_ptr c, uint8_t command, uint8_t f static void on_accept_identify_item_bb(shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size) { auto l = c->require_lobby(); - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { const auto& cmd = check_size_t(data, size); if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { @@ -1630,7 +1630,7 @@ static void on_accept_identify_item_bb(shared_ptr c, uint8_t command, ui static void on_sell_item_at_shop_bb(shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size) { auto s = c->require_server_state(); auto l = c->require_lobby(); - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { const auto& cmd = check_size_t(data, size); if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { @@ -1658,7 +1658,7 @@ static void on_sell_item_at_shop_bb(shared_ptr c, uint8_t command, uint8 static void on_buy_shop_item_bb(shared_ptr c, uint8_t, uint8_t, const void* data, size_t size) { auto l = c->require_lobby(); - if (l->version == GameVersion::BB) { + if (l->base_version == GameVersion::BB) { const auto& cmd = check_size_t(data, size); if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { throw logic_error("item tracking not enabled in BB game"); @@ -1695,7 +1695,7 @@ static void on_buy_shop_item_bb(shared_ptr c, uint8_t, uint8_t, const vo static void on_medical_center_bb(shared_ptr c, uint8_t, uint8_t, const void*, size_t) { auto l = c->require_lobby(); - if (l->is_game() && (l->version == GameVersion::BB)) { + if (l->is_game() && (l->base_version == GameVersion::BB)) { c->game_data.player()->remove_meseta(10, false); } } diff --git a/src/SendCommands.cc b/src/SendCommands.cc index bf2c1446..2ca0f309 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -1188,15 +1188,7 @@ void send_game_menu_t( if (!l->is_game()) { continue; } - if (l->version != c->version()) { - continue; - } - bool l_is_ep3 = l->is_ep3(); - bool c_is_ep3 = !!(c->flags & Client::Flag::IS_EPISODE_3); - if (l_is_ep3 != c_is_ep3) { - continue; - } - if ((c->flags & Client::Flag::IS_DC_V1) && (l->flags & Lobby::Flag::NON_V1_ONLY)) { + if (!l->version_is_allowed(c->quest_version())) { continue; } bool l_is_spectator_team = !!(l->flags & Lobby::Flag::IS_SPECTATOR_TEAM); @@ -1228,10 +1220,10 @@ void send_game_menu_t( auto& e = entries.emplace_back(); e.menu_id = MenuID::GAME; e.game_id = l->lobby_id; - e.difficulty_tag = (l_is_ep3 ? 0x0A : (l->difficulty + 0x22)); + e.difficulty_tag = (l->is_ep3() ? 0x0A : (l->difficulty + 0x22)); e.num_players = l->count_clients(); if (c->version() == GameVersion::DC) { - e.episode = (l->flags & Lobby::Flag::NON_V1_ONLY) ? 1 : 0; + e.episode = l->version_is_allowed(QuestScriptVersion::DC_V1) ? 1 : 0; } else { e.episode = ((c->version() == GameVersion::BB) ? (l->max_clients << 4) : 0) | episode_num; } @@ -1367,7 +1359,7 @@ void send_lobby_list(shared_ptr c) { if (!(l->flags & Lobby::Flag::DEFAULT)) { continue; } - if ((l->flags & Lobby::Flag::NON_V1_ONLY) && (c->flags & Client::Flag::IS_DC_V1)) { + if ((l->flags & Lobby::Flag::V2_AND_LATER) && (c->flags & Client::Flag::IS_DC_V1)) { continue; } if (l->is_ep3() && !(c->flags & Client::Flag::IS_EPISODE_3)) { @@ -2182,7 +2174,7 @@ void send_give_experience(shared_ptr c, uint32_t amount) { } void send_set_exp_multiplier(std::shared_ptr l) { - if (l->version != GameVersion::BB) { + if (l->base_version != GameVersion::BB) { throw logic_error("6xDD can only be sent to BB clients"); } if (!l->is_game()) { diff --git a/src/ServerState.cc b/src/ServerState.cc index bcafe4e9..b5ae7b30 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -24,6 +24,8 @@ ServerState::ServerState(const char* config_filename, bool is_replay) ip_stack_debug(false), allow_unregistered_users(false), allow_saving(true), + allow_dc_pc_games(false), + allow_gc_xb_games(true), item_tracking_enabled(true), drops_enabled(true), ep3_send_function_call_enabled(false), @@ -53,7 +55,7 @@ void ServerState::init() { for (size_t x = 0; x < 20; x++) { auto lobby_name = decode_sjis(string_printf("LOBBY%zu", x + 1)); - bool is_non_v1_only = (x > 9); + bool v2_and_later_only = (x > 9); bool is_ep3_only = (x > 14); shared_ptr l = this->create_lobby(); @@ -61,7 +63,7 @@ void ServerState::init() { Lobby::Flag::PUBLIC | Lobby::Flag::DEFAULT | Lobby::Flag::PERSISTENT | - (is_non_v1_only ? Lobby::Flag::NON_V1_ONLY : 0); + (v2_and_later_only ? Lobby::Flag::V2_AND_LATER : 0); l->block = x + 1; l->name = lobby_name; l->max_clients = 12; @@ -69,7 +71,7 @@ void ServerState::init() { l->episode = Episode::EP3; } - if (!is_non_v1_only) { + if (!v2_and_later_only) { this->public_lobby_search_order_v1.emplace_back(l); } if (!is_ep3_only) { @@ -655,6 +657,9 @@ void ServerState::parse_config(const JSON& json, bool is_reload) { } 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); + try { auto v = json.at("LobbyEvent"); uint8_t event = v.is_int() ? v.as_int() : event_for_name(v.as_string()); diff --git a/src/ServerState.hh b/src/ServerState.hh index 5b1c53b0..86412e4d 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -57,6 +57,8 @@ struct ServerState : public std::enable_shared_from_this { bool ip_stack_debug; bool allow_unregistered_users; bool allow_saving; + bool allow_dc_pc_games; + bool allow_gc_xb_games; bool item_tracking_enabled; bool drops_enabled; bool ep3_send_function_call_enabled; diff --git a/system/config.example.json b/system/config.example.json index a9e02b46..3889f57a 100644 --- a/system/config.example.json +++ b/system/config.example.json @@ -503,6 +503,16 @@ // enable Episode 3 USA patches by default; it only does if this option is on. // "EnableEpisode3SendFunctionCall": true, + // Whether to allow cross-play for various game versions. DCv1 and DCv2 are + // always allowed to join each other's games (though DCv2 can deny permission + // for DCv1 players to join when creating a game); if AllowDCPCGames is + // enabled, then PC players are allowed in DC games and vice versa. Similarly, + // if AllowGCXBGames is enabled, then GameCube and Xbox players are allowed to + // join each other's games. Note that this behavior is experimental; you are + // likely to encounter bugs in cross-play games, especially in the DC/PC case. + "AllowDCPCGames": false, + "AllowGCXBGames": true, + // By default, the server keeps track of items in all games, even for versions // other than Blue Burst. This enables use of the $what command, as well as // protection against item duplication cheats (the cheater is disconnected