rewrite game list filtering logic for BB

This commit is contained in:
Martin Michelsen
2024-01-04 11:49:56 -08:00
parent 649a7c9871
commit 4be431471c
4 changed files with 106 additions and 54 deletions
+45
View File
@@ -674,6 +674,51 @@ shared_ptr<Client> Lobby::find_client(const string* identifier, uint64_t serial_
throw out_of_range("client not found"); throw out_of_range("client not found");
} }
Lobby::JoinError Lobby::join_error_for_client(std::shared_ptr<Client> c, const std::string* password) const {
if (this->count_clients() >= this->max_clients) {
return JoinError::FULL;
}
if (!this->version_is_allowed(c->version()) && !c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
return JoinError::VERSION_CONFLICT;
}
if (this->is_game()) {
if (this->check_flag(Flag::QUEST_IN_PROGRESS)) {
return JoinError::QUEST_IN_PROGRESS;
}
if (this->check_flag(Flag::BATTLE_IN_PROGRESS)) {
return JoinError::BATTLE_IN_PROGRESS;
}
if (this->mode == GameMode::SOLO) {
return JoinError::SOLO;
}
if (!(c->license->flags & License::Flag::FREE_JOIN_GAMES)) {
if (password && !this->password.empty() && (*password != this->password)) {
return JoinError::INCORRECT_PASSWORD;
}
auto p = c->character();
if (p->disp.stats.level < this->min_level) {
return JoinError::LEVEL_TOO_LOW;
}
if (p->disp.stats.level > this->max_level) {
return JoinError::LEVEL_TOO_HIGH;
}
if (this->quest) {
size_t num_clients = this->count_clients() + 1;
if (!c->can_see_quest(this->quest, this->difficulty, num_clients) ||
!c->can_play_quest(this->quest, this->difficulty, num_clients)) {
return JoinError::NO_ACCESS_TO_QUEST;
}
}
}
// Only prevent joining during loading if the client is actually trying to
// join (not just loading the game list)
if (password && this->any_client_loading()) {
return JoinError::LOADING;
}
}
return JoinError::ALLOWED;
}
uint8_t Lobby::game_event_for_lobby_event(uint8_t lobby_event) { uint8_t Lobby::game_event_for_lobby_event(uint8_t lobby_event) {
if (lobby_event > 7) { if (lobby_event > 7) {
return 0; return 0;
+15
View File
@@ -228,6 +228,21 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
const std::string* identifier = nullptr, const std::string* identifier = nullptr,
uint64_t serial_number = 0); uint64_t serial_number = 0);
enum class JoinError {
ALLOWED = 0,
FULL,
VERSION_CONFLICT,
QUEST_IN_PROGRESS,
BATTLE_IN_PROGRESS,
LOADING,
SOLO,
INCORRECT_PASSWORD,
LEVEL_TOO_LOW,
LEVEL_TOO_HIGH,
NO_ACCESS_TO_QUEST,
};
JoinError join_error_for_client(std::shared_ptr<Client> c, const std::string* password) const;
bool item_exists(uint8_t floor, uint32_t item_id) const; bool item_exists(uint8_t floor, uint32_t item_id) const;
std::shared_ptr<FloorItem> find_item(uint8_t floor, uint32_t item_id) const; std::shared_ptr<FloorItem> find_item(uint8_t floor, uint32_t item_id) const;
void add_item(uint8_t floor, const ItemData& item, float x, float z, uint16_t visibility_flags); void add_item(uint8_t floor, const ItemData& item, float x, float z, uint16_t visibility_flags);
+42 -52
View File
@@ -2390,66 +2390,56 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
send_lobby_message_box(c, "$C6You cannot join this\ngame because it no\nlonger exists."); send_lobby_message_box(c, "$C6You cannot join this\ngame because it no\nlonger exists.");
break; break;
} }
if (!game->is_game()) { switch (game->join_error_for_client(c, &password)) {
send_lobby_message_box(c, "$C6You cannot join this\ngame because it is\nnot a game."); case Lobby::JoinError::ALLOWED:
break; if (!s->change_client_lobby(c, game)) {
} throw logic_error("client cannot join game after all preconditions satisfied");
if (game->count_clients() >= game->max_clients) { }
send_lobby_message_box(c, "$C6You cannot join this\ngame because it is\nfull."); if (game->is_game()) {
break; c->config.set_flag(Client::Flag::LOADING);
} // If no one was in the game before, then there's no leader to send the
if (!game->version_is_allowed(c->version()) && !c->config.check_flag(Client::Flag::DEBUG_ENABLED)) { // item state - send it to the joining player (who is now the leader)
send_lobby_message_box(c, "$C6You cannot join this\ngame because it is\nfor a different\nversion of PSO."); if (game->count_clients() == 1) {
break; // No one was in the game before, so the object and enemy state is lost;
} // regenerate it as if the game was just created
if (game->check_flag(Lobby::Flag::QUEST_IN_PROGRESS)) { game->load_maps();
send_lobby_message_box(c, "$C6You cannot join this\ngame because a\nquest is already\nin progress."); c->config.set_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_ITEM_STATE);
break; }
} }
if (game->check_flag(Lobby::Flag::BATTLE_IN_PROGRESS)) { break;
send_lobby_message_box(c, "$C6You cannot join this\ngame because a\nbattle is already\nin progress."); case Lobby::JoinError::FULL:
break; send_lobby_message_box(c, "$C6You cannot join this\ngame because it is\nfull.");
} break;
if (game->any_client_loading()) { case Lobby::JoinError::VERSION_CONFLICT:
send_lobby_message_box(c, "$C6You cannot join this\ngame because\nanother player is\ncurrently loading.\nTry again soon."); send_lobby_message_box(c, "$C6You cannot join this\ngame because it is\nfor a different\nversion of PSO.");
break; break;
} case Lobby::JoinError::QUEST_IN_PROGRESS:
if (game->mode == GameMode::SOLO) { send_lobby_message_box(c, "$C6You cannot join this\ngame because a\nquest is already\nin progress.");
send_lobby_message_box(c, "$C6You cannot join this\n game because it is\na Solo Mode game."); break;
break; case Lobby::JoinError::BATTLE_IN_PROGRESS:
} send_lobby_message_box(c, "$C6You cannot join this\ngame because a\nbattle is already\nin progress.");
break;
if (!(c->license->flags & License::Flag::FREE_JOIN_GAMES)) { case Lobby::JoinError::LOADING:
if (!game->password.empty() && (password != game->password)) { send_lobby_message_box(c, "$C6You cannot join this\ngame because\nanother player is\ncurrently loading.\nTry again soon.");
break;
case Lobby::JoinError::SOLO:
send_lobby_message_box(c, "$C6You cannot join this\ngame because it is\na Solo Mode game.");
break;
case Lobby::JoinError::INCORRECT_PASSWORD:
send_lobby_message_box(c, "$C6Incorrect password."); send_lobby_message_box(c, "$C6Incorrect password.");
break; break;
} case Lobby::JoinError::LEVEL_TOO_LOW:
auto p = c->character();
if (p->disp.stats.level < game->min_level) {
send_lobby_message_box(c, "$C6Your level is too\nlow to join this\ngame."); send_lobby_message_box(c, "$C6Your level is too\nlow to join this\ngame.");
break; break;
} case Lobby::JoinError::LEVEL_TOO_HIGH:
if (p->disp.stats.level > game->max_level) {
send_lobby_message_box(c, "$C6Your level is too\nhigh to join this\ngame."); send_lobby_message_box(c, "$C6Your level is too\nhigh to join this\ngame.");
break; break;
} case Lobby::JoinError::NO_ACCESS_TO_QUEST:
if (game->quest && !c->can_play_quest(game->quest, game->difficulty, game->count_clients() + 1)) {
send_lobby_message_box(c, "$C6You don't have access\nto the quest in progress\nin this game, or there\nis no space for another\nplayer in the quest."); send_lobby_message_box(c, "$C6You don't have access\nto the quest in progress\nin this game, or there\nis no space for another\nplayer in the quest.");
break; break;
} default:
} send_lobby_message_box(c, "$C6You cannot join this\ngame.");
break;
if (!s->change_client_lobby(c, game)) {
throw logic_error("client cannot join game after all preconditions satisfied");
}
c->config.set_flag(Client::Flag::LOADING);
// If no one was in the game before, then there's no leader to send the
// item state - send it to the joining player (who is now the leader)
if (game->count_clients() == 1) {
// No one was in the game before, so the object and enemy state is lost;
// regenerate it as if the game was just created
game->load_maps();
c->config.set_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_ITEM_STATE);
} }
break; break;
} }
+4 -2
View File
@@ -1413,13 +1413,15 @@ void send_game_menu_t(
e.flags |= 0x20; e.flags |= 0x20;
break; break;
case GameMode::SOLO: case GameMode::SOLO:
// These should only be visible to other BB clients
e.flags |= 0x04; // Grayed (but not disabled apparently)
e.episode = 0x10 | episode_num; e.episode = 0x10 | episode_num;
break; break;
default: default:
throw logic_error("invalid game mode"); throw logic_error("invalid game mode");
} }
// On BB, gray out games that can't be joined
if ((c->version() == Version::BB_V4) && (l->join_error_for_client(c, nullptr) != Lobby::JoinError::ALLOWED)) {
e.flags |= 0x04;
}
} }
e.name.encode(l->name, c->language()); e.name.encode(l->name, c->language());
} }