From cce42c4165d03833749e8acb108a65d7fe8bee56 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sat, 11 Nov 2023 23:58:42 -0800 Subject: [PATCH] add $quest command for debugging --- src/ChatCommands.cc | 13 ++++ src/ReceiveCommands.cc | 147 ++++++++++++++++++++++------------------- src/ReceiveCommands.hh | 1 + 3 files changed, 94 insertions(+), 67 deletions(-) diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index 0aa3c223..e2527063 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -257,6 +257,18 @@ static void server_command_debug(shared_ptr c, const std::string&) { send_text_message_printf(c, "Debug %s", (c->config.check_flag(Client::Flag::DEBUG_ENABLED) ? "enabled" : "disabled")); } +static void server_command_quest(shared_ptr c, const std::string& args) { + if (!c->config.check_flag(Client::Flag::DEBUG_ENABLED)) { + send_text_message(c, "$C6This command can only\nbe run in debug mode\n(run %sdebug first)"); + return; + } + + auto s = c->require_server_state(); + auto l = c->require_lobby(); + auto q = s->quest_index_for_client(c)->get(stoul(args)); + set_lobby_quest(c->require_lobby(), q); +} + static void server_command_show_material_counts(shared_ptr c, const std::string&) { auto p = c->game_data.player(); if ((c->version() == GameVersion::DC) || (c->version() == GameVersion::PC)) { @@ -1611,6 +1623,7 @@ static const unordered_map chat_commands({ {"$persist", {server_command_persist, nullptr}}, {"$ping", {server_command_ping, nullptr}}, {"$playrec", {server_command_playrec, nullptr}}, + {"$quest", {server_command_quest, nullptr}}, {"$rand", {server_command_rand, proxy_command_rand}}, {"$saverec", {server_command_saverec, nullptr}}, {"$sc", {server_command_send_client, proxy_command_send_client}}, diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 88096ec4..6fa7c74c 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -1812,6 +1812,85 @@ static void on_09(shared_ptr c, uint16_t, uint32_t, string& data) { } } +void set_lobby_quest(shared_ptr l, shared_ptr q) { + if (!l->is_game()) { + throw logic_error("non-game lobby cannot accept a quest"); + } + if (l->quest) { + throw runtime_error("lobby already has an assigned quest"); + } + + auto s = l->require_server_state(); + + if (q->joinable) { + l->set_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS); + } else { + l->set_flag(Lobby::Flag::QUEST_IN_PROGRESS); + } + + l->quest = q; + l->episode = q->episode; + if (l->item_creator) { + l->item_creator->clear_destroyed_entities(); + if (q->battle_rules) { + l->item_creator->set_restrictions(q->battle_rules); + } + } + + for (size_t client_id = 0; client_id < l->max_clients; client_id++) { + auto lc = l->clients[client_id]; + if (!lc) { + continue; + } + + auto vq = q->version(lc->quest_version(), lc->language()); + if (!vq) { + send_lobby_message_box(lc, "$C6Quest does not exist\nfor this game version."); + lc->should_disconnect = true; + break; + } + + if (vq->battle_rules) { + lc->game_data.create_battle_overlay(vq->battle_rules, s->level_table); + lc->log.info("Created battle overlay"); + } else if (vq->challenge_template_index >= 0) { + lc->game_data.create_challenge_overlay(lc->version(), vq->challenge_template_index, s->level_table); + lc->log.info("Created challenge overlay"); + } + + // If an overlay was created, item IDs need to be assigned + if (lc->game_data.has_overlay()) { + auto overlay = lc->game_data.player(); + for (size_t z = 0; z < overlay->inventory.num_items; z++) { + overlay->inventory.items[z].data.id = l->generate_item_id(client_id); + } + lc->log.info("Assigned overlay item IDs"); + lc->game_data.player()->print_inventory(stderr, lc->version(), s->item_name_index); + } + + string bin_filename = vq->bin_filename(); + string dat_filename = vq->dat_filename(); + string xb_filename = vq->xb_filename(); + send_open_quest_file(lc, bin_filename, bin_filename, xb_filename, vq->quest_number, QuestFileType::ONLINE, vq->bin_contents); + send_open_quest_file(lc, dat_filename, dat_filename, xb_filename, vq->quest_number, QuestFileType::ONLINE, vq->dat_contents); + + // There is no such thing as command AC on PSO V1 and V2 - quests just + // start immediately when they're done downloading. (This is also the + // case on V3 Trial Edition.) There are also no chunk acknowledgements + // (C->S 13 commands) like there are on GC. So, for pre-V3 clients, we + // can just not set the loading flag, since we never need to + // check/clear it later. + if ((lc->version() != GameVersion::DC) && + (lc->version() != GameVersion::PC) && + !lc->config.check_flag(Client::Flag::IS_GC_TRIAL_EDITION)) { + lc->config.set_flag(Client::Flag::LOADING_QUEST); + lc->disconnect_hooks.emplace(QUEST_BARRIER_DISCONNECT_HOOK_NAME, [l]() -> void { + send_quest_barrier_if_all_clients_ready(l); + }); + } + } +} + static void on_10(shared_ptr c, uint16_t, uint32_t, string& data) { bool uses_unicode = ((c->version() == GameVersion::PC) || (c->version() == GameVersion::BB)); @@ -2192,73 +2271,7 @@ static void on_10(shared_ptr c, uint16_t, uint32_t, string& data) { send_lobby_message_box(c, "$C6A quest is already\nin progress."); break; } - if (q->joinable) { - l->set_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS); - } else { - l->set_flag(Lobby::Flag::QUEST_IN_PROGRESS); - } - - l->quest = q; - l->episode = q->episode; - if (l->item_creator) { - l->item_creator->clear_destroyed_entities(); - if (q->battle_rules) { - l->item_creator->set_restrictions(q->battle_rules); - } - } - - for (size_t client_id = 0; client_id < l->max_clients; client_id++) { - auto lc = l->clients[client_id]; - if (!lc) { - continue; - } - - auto vq = q->version(lc->quest_version(), lc->language()); - if (!vq) { - send_lobby_message_box(lc, "$C6Quest does not exist\nfor this game version."); - lc->should_disconnect = true; - break; - } - - if (vq->battle_rules) { - lc->game_data.create_battle_overlay(vq->battle_rules, s->level_table); - lc->log.info("Created battle overlay"); - } else if (vq->challenge_template_index >= 0) { - lc->game_data.create_challenge_overlay(lc->version(), vq->challenge_template_index, s->level_table); - lc->log.info("Created challenge overlay"); - } - - // If an overlay was created, item IDs need to be assigned - if (lc->game_data.has_overlay()) { - auto overlay = lc->game_data.player(); - for (size_t z = 0; z < overlay->inventory.num_items; z++) { - overlay->inventory.items[z].data.id = l->generate_item_id(client_id); - } - lc->log.info("Assigned overlay item IDs"); - lc->game_data.player()->print_inventory(stderr, c->version(), s->item_name_index); - } - - string bin_filename = vq->bin_filename(); - string dat_filename = vq->dat_filename(); - string xb_filename = vq->xb_filename(); - send_open_quest_file(lc, bin_filename, bin_filename, xb_filename, vq->quest_number, QuestFileType::ONLINE, vq->bin_contents); - send_open_quest_file(lc, dat_filename, dat_filename, xb_filename, vq->quest_number, QuestFileType::ONLINE, vq->dat_contents); - - // There is no such thing as command AC on PSO V1 and V2 - quests just - // start immediately when they're done downloading. (This is also the - // case on V3 Trial Edition.) There are also no chunk acknowledgements - // (C->S 13 commands) like there are on GC. So, for pre-V3 clients, we - // can just not set the loading flag, since we never need to - // check/clear it later. - if ((lc->version() != GameVersion::DC) && - (lc->version() != GameVersion::PC) && - !lc->config.check_flag(Client::Flag::IS_GC_TRIAL_EDITION)) { - lc->config.set_flag(Client::Flag::LOADING_QUEST); - lc->disconnect_hooks.emplace(QUEST_BARRIER_DISCONNECT_HOOK_NAME, [l]() -> void { - send_quest_barrier_if_all_clients_ready(l); - }); - } - } + set_lobby_quest(l, q); } else { auto vq = q->version(c->quest_version(), c->language()); diff --git a/src/ReceiveCommands.hh b/src/ReceiveCommands.hh index 04df2458..9101cbf4 100644 --- a/src/ReceiveCommands.hh +++ b/src/ReceiveCommands.hh @@ -15,6 +15,7 @@ std::shared_ptr create_game_generic( bool allow_v1 = false, std::shared_ptr watched_lobby = nullptr, std::shared_ptr battle_player = nullptr); +void set_lobby_quest(std::shared_ptr l, std::shared_ptr q); void on_connect(std::shared_ptr c); void on_disconnect(std::shared_ptr c);