diff --git a/src/Client.hh b/src/Client.hh index 5ce7030a..e9f44733 100644 --- a/src/Client.hh +++ b/src/Client.hh @@ -156,7 +156,7 @@ public: std::weak_ptr lobby; uint8_t lobby_client_id = 0; uint8_t lobby_arrow_color = 0; - int64_t preferred_lobby_id = -1; // <0 = no preference + int64_t preferred_lobby_id = -1; // <0 = none chosen asio::steady_timer save_game_data_timer; asio::steady_timer send_ping_timer; diff --git a/src/Lobby.cc b/src/Lobby.cc index 3d1417f2..e2de989d 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -835,3 +835,35 @@ bool Lobby::compare_shared(const shared_ptr& a, const shared_ptrname < b->name; } + +template <> +const char* phosg::name_for_enum(Lobby::JoinError value) { + switch (value) { + case Lobby::JoinError::ALLOWED: + return "ALLOWED"; + case Lobby::JoinError::FULL: + return "FULL"; + case Lobby::JoinError::VERSION_CONFLICT: + return "VERSION_CONFLICT"; + case Lobby::JoinError::QUEST_SELECTION_IN_PROGRESS: + return "QUEST_SELECTION_IN_PROGRESS"; + case Lobby::JoinError::QUEST_IN_PROGRESS: + return "QUEST_IN_PROGRESS"; + case Lobby::JoinError::BATTLE_IN_PROGRESS: + return "BATTLE_IN_PROGRESS"; + case Lobby::JoinError::LOADING: + return "LOADING"; + case Lobby::JoinError::SOLO: + return "SOLO"; + case Lobby::JoinError::INCORRECT_PASSWORD: + return "INCORRECT_PASSWORD"; + case Lobby::JoinError::LEVEL_TOO_LOW: + return "LEVEL_TOO_LOW"; + case Lobby::JoinError::LEVEL_TOO_HIGH: + return "LEVEL_TOO_HIGH"; + case Lobby::JoinError::NO_ACCESS_TO_QUEST: + return "NO_ACCESS_TO_QUEST"; + default: + throw runtime_error("invalid drop mode"); + } +} diff --git a/src/Lobby.hh b/src/Lobby.hh index da3419bf..8f9c19e8 100644 --- a/src/Lobby.hh +++ b/src/Lobby.hh @@ -279,6 +279,4 @@ struct Lobby : public std::enable_shared_from_this { }; template <> -ServerDropMode phosg::enum_for_name(const char* name); -template <> -const char* phosg::name_for_enum(ServerDropMode value); +const char* phosg::name_for_enum(Lobby::JoinError value); diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 87cc5c17..fa20086c 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -314,7 +314,13 @@ asio::awaitable start_login_server_procedure(shared_ptr c) { s->ep3_tournament_index->link_client(c); } - if (s->welcome_message.empty() || + if (c->preferred_lobby_id >= 0) { + s->add_client_to_available_lobby(c, true); + if (c->require_lobby()->is_game()) { + c->set_flag(Client::Flag::LOADING); + c->log.info_f("LOADING flag set"); + } + } else if (s->welcome_message.empty() || c->check_flag(Client::Flag::NO_D6) || !c->check_flag(Client::Flag::AT_WELCOME_MESSAGE)) { c->clear_flag(Client::Flag::AT_WELCOME_MESSAGE); @@ -2527,7 +2533,7 @@ static asio::awaitable on_10_main_menu(shared_ptr c, uint32_t item co_await send_get_player_info(c); } if (!c->lobby.lock()) { - s->add_client_to_available_lobby(c); + s->add_client_to_available_lobby(c, false); } break; } @@ -3093,7 +3099,7 @@ static asio::awaitable on_84(shared_ptr c, Channel::Message& msg) // If the client isn't in any lobby, then they just left a game. Add them to the lobby they requested, but fall // back to another lobby if it's full. c->preferred_lobby_id = cmd.item_id; - s->add_client_to_available_lobby(c); + s->add_client_to_available_lobby(c, false); } else { // If the client already is in a lobby, then they're using the lobby teleporter; add them to the lobby they diff --git a/src/ServerState.cc b/src/ServerState.cc index 3d36273a..600f0f1f 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -83,30 +83,45 @@ ServerState::ServerState(const string& config_filename, bool is_replay) bb_system_cache(new FileContentsCache(3600000000ULL)), gba_files_cache(new FileContentsCache(3600000000ULL)) {} -void ServerState::add_client_to_available_lobby(shared_ptr c) { +void ServerState::add_client_to_available_lobby(shared_ptr c, bool allow_games) { shared_ptr added_to_lobby; - if (c->preferred_lobby_id >= 0) { - try { - auto l = this->find_lobby(c->preferred_lobby_id); - if (l && !l->is_game() && l->check_flag(Lobby::Flag::PUBLIC) && l->version_is_allowed(c->version())) { - l->add_client(c); - added_to_lobby = l; - } - } catch (const out_of_range&) { + auto try_join_lobby = [&](uint32_t lobby_id) -> std::shared_ptr { + auto l = this->find_lobby(lobby_id); + if (!l) { + c->log.info_f("Cannot join lobby {:08X}: lobby does not exist", lobby_id); + return nullptr; } + if (!allow_games && l->is_game()) { + c->log.info_f("Cannot join lobby {:08X}: lobby is a game", lobby_id); + return nullptr; + } + static const std::string password = ""; + auto join_error = l->join_error_for_client(c, &password); + if (join_error == Lobby::JoinError::ALLOWED) { + try { + l->add_client(c); + c->log.info_f("Joined lobby {:08X}", lobby_id); + return l; + } catch (const out_of_range& e) { + c->log.info_f("Cannot join lobby {:08X}: {}", lobby_id, e.what()); + return nullptr; + } + } + c->log.info_f("Cannot join lobby {:08X}: {}", lobby_id, phosg::name_for_enum(join_error)); + return nullptr; + }; + + if (c->preferred_lobby_id >= 0) { + added_to_lobby = try_join_lobby(c->preferred_lobby_id); + c->preferred_lobby_id = -1; } - if (!added_to_lobby.get()) { + if (!added_to_lobby) { for (const auto& lobby_id : this->public_lobby_search_order(c)) { - try { - auto l = this->find_lobby(lobby_id); - if (l && !l->is_game() && l->check_flag(Lobby::Flag::PUBLIC) && l->version_is_allowed(c->version())) { - l->add_client(c); - added_to_lobby = l; - break; - } - } catch (const out_of_range&) { + added_to_lobby = try_join_lobby(lobby_id); + if (added_to_lobby) { + break; } } } diff --git a/src/ServerState.hh b/src/ServerState.hh index f49fedf0..6268e30d 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -316,7 +316,7 @@ struct ServerState : public std::enable_shared_from_this { ServerState& operator=(const ServerState&) = delete; ServerState& operator=(ServerState&&) = delete; - void add_client_to_available_lobby(std::shared_ptr c); + void add_client_to_available_lobby(std::shared_ptr c, bool allow_games); void remove_client_from_lobby(std::shared_ptr c); bool change_client_lobby( std::shared_ptr c,