handle joinable quests
This commit is contained in:
@@ -92,6 +92,8 @@ struct Client {
|
|||||||
LOADING = 0x00000020,
|
LOADING = 0x00000020,
|
||||||
// Client is loading a quest
|
// Client is loading a quest
|
||||||
LOADING_QUEST = 0x00000040,
|
LOADING_QUEST = 0x00000040,
|
||||||
|
// Client is loading a joinable quest that has already started
|
||||||
|
LOADING_RUNNING_QUEST = 0x00100000,
|
||||||
// Client is waiting to join an Episode 3 card auction
|
// Client is waiting to join an Episode 3 card auction
|
||||||
AWAITING_CARD_AUCTION = 0x00010000,
|
AWAITING_CARD_AUCTION = 0x00010000,
|
||||||
// Client is in the information menu (login server only)
|
// Client is in the information menu (login server only)
|
||||||
|
|||||||
+1
-1
@@ -51,7 +51,7 @@ bool Lobby::any_client_loading() const {
|
|||||||
if (!this->clients[x].get()) {
|
if (!this->clients[x].get()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (this->clients[x]->flags & (Client::Flag::LOADING | Client::Flag::LOADING_QUEST)) {
|
if (this->clients[x]->flags & (Client::Flag::LOADING | Client::Flag::LOADING_QUEST | Client::Flag::LOADING_RUNNING_QUEST)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-28
@@ -2331,41 +2331,43 @@ static void on_AC_V3_BB(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
uint16_t, uint32_t, const string& data) {
|
uint16_t, uint32_t, const string& data) {
|
||||||
check_size_v(data.size(), 0);
|
check_size_v(data.size(), 0);
|
||||||
|
|
||||||
// If this client is NOT loading, they should not send an AC. Sending an AC to
|
auto l = s->find_lobby(c->lobby_id);
|
||||||
// a client that isn't waiting to start a quest will crash the client, so we
|
|
||||||
// have to be careful not to do so.
|
if (c->flags & Client::Flag::LOADING_RUNNING_QUEST) {
|
||||||
if (!(c->flags & Client::Flag::LOADING_QUEST)) {
|
c->flags &= ~Client::Flag::LOADING_RUNNING_QUEST;
|
||||||
return;
|
if (l->version != GameVersion::BB) {
|
||||||
|
throw logic_error("joinable quest started on non-BB version");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto leader_c = l->clients.at(l->leader_id);
|
||||||
|
if (!leader_c) {
|
||||||
|
throw logic_error("lobby leader is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
send_command(c, 0xAC, 0x00);
|
||||||
|
send_command(leader_c, 0xDD, c->lobby_client_id);
|
||||||
|
|
||||||
|
} else if (c->flags & Client::Flag::LOADING_QUEST) {
|
||||||
c->flags &= ~Client::Flag::LOADING_QUEST;
|
c->flags &= ~Client::Flag::LOADING_QUEST;
|
||||||
|
|
||||||
auto l = s->find_lobby(c->lobby_id);
|
|
||||||
if (!l->quest.get()) {
|
if (!l->quest.get()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (send_quest_barrier_if_all_clients_ready(l) &&
|
if (send_quest_barrier_if_all_clients_ready(l) &&
|
||||||
(l->version == GameVersion::BB)) {
|
(l->version == GameVersion::BB) &&
|
||||||
// TODO: We should replace the game enemies list here. It'll look something
|
l->map &&
|
||||||
// like this (but the dat format may be different from the existing loader):
|
l->quest) {
|
||||||
// try {
|
auto dat_contents = prs_decompress(*l->quest->dat_contents());
|
||||||
// auto dat_data = prs_decompress(l->quest->dat_contents());
|
l->map->clear();
|
||||||
// game->enemies = parse_map(
|
l->map->add_enemies_from_quest_data(l->episode, l->difficulty, l->event, dat_contents.data(), dat_contents.size());
|
||||||
// s->battle_params,
|
c->log.info("Replaced enemies list with quest layout (%zu entries)",
|
||||||
// l->flags & Lobby::Flag::SOLO_MODE,
|
l->map->enemies.size());
|
||||||
// l->episode,
|
for (size_t z = 0; z < l->map->enemies.size(); z++) {
|
||||||
// l->difficulty,
|
string e_str = l->map->enemies[z].str();
|
||||||
// dat_data,
|
l->log.info("(Entry %zX) %s", z, e_str.c_str());
|
||||||
// false);
|
}
|
||||||
// c->log.info("Replaced enemies list with quest layout (%zu entries)",
|
}
|
||||||
// l->enemies.size());
|
|
||||||
// for (size_t z = 0; z < l->enemies.size(); z++) {
|
|
||||||
// string e_str = l->enemies[z].str();
|
|
||||||
// l->log.info("(Entry %zX) %s", z, e_str.c_str());
|
|
||||||
// }
|
|
||||||
// } catch (const exception& e) {
|
|
||||||
// c->log.info("Failed to load quest layout: %s", e.what());
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3427,9 +3429,24 @@ static void on_6F(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
// unexpectedly (that is, only equipped items are included).
|
// unexpectedly (that is, only equipped items are included).
|
||||||
if (c->version() == GameVersion::BB) {
|
if (c->version() == GameVersion::BB) {
|
||||||
send_get_player_info(c);
|
send_get_player_info(c);
|
||||||
|
if (l->flags & Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS) {
|
||||||
|
if (!l->quest) {
|
||||||
|
throw runtime_error("JOINABLE_QUEST_IN_PROGRESS is set, but lobby has no quest");
|
||||||
}
|
}
|
||||||
|
string bin_basename = l->quest->bin_filename();
|
||||||
|
shared_ptr<const string> bin_contents = l->quest->bin_contents();
|
||||||
|
string dat_basename = l->quest->dat_filename();
|
||||||
|
shared_ptr<const string> dat_contents = l->quest->dat_contents();
|
||||||
|
|
||||||
// TODO (R1): If the game is BB and in a joinable quest, send the quest immediately. If the game is BB and not in a joinable quest, send the rare monster config (DE)
|
send_open_quest_file(c, bin_basename + ".bin",
|
||||||
|
bin_basename, bin_contents, QuestFileType::ONLINE);
|
||||||
|
send_open_quest_file(c, dat_basename + ".dat",
|
||||||
|
dat_basename, dat_contents, QuestFileType::ONLINE);
|
||||||
|
c->flags |= Client::Flag::LOADING_RUNNING_QUEST;
|
||||||
|
} else if (l->map) {
|
||||||
|
send_rare_enemy_index_list(c, l->map->rare_enemy_indexes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle initial commands for spectator teams
|
// Handle initial commands for spectator teams
|
||||||
auto watched_lobby = l->watched_lobby.lock();
|
auto watched_lobby = l->watched_lobby.lock();
|
||||||
|
|||||||
Reference in New Issue
Block a user