From d5ececfa87359f1e11bcd87c86256f0528cad8d6 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Fri, 3 Mar 2023 22:57:28 -0800 Subject: [PATCH] add in-game debug messages --- README.md | 2 +- src/ChatCommands.cc | 10 ++--- src/Client.cc | 2 +- src/Client.hh | 2 +- src/Lobby.cc | 2 +- src/ReceiveSubcommands.cc | 91 ++++++++++++++++++++++++++++++++++----- src/StaticGameData.cc | 5 ++- 7 files changed, 92 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 4a900074..3e7146b7 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ Some commands only work on the game server and not on the proxy server. The chat * `$what` (game server only): Shows the type, name, and stats of the nearest item on the ground. * Debugging commands - * `$dbgid` (game server only): Enable or disable high ID preference. When enabled, you'll be placed into the highest available slot in lobbies and games instead of the lowest. This is useful for finding commands for which newserv doesn't handle client IDs properly. + * `$debug` (game server only): Enable or disable debugging. When enabled, you'll see purple debug messages from the server when you take certain actions. You'll also be placed into the highest available slot in lobbies and games instead of the lowest, which is useful for finding commands for which newserv doesn't handle client IDs properly. * `$gc` (game server only): Send your own Guild Card to yourself. * `$persist` (game server only): Enable or disable persistence for the current lobby or game. This determines whether the lobby/game is deleted when the last player leaves. You need the DEBUG permission in your user license to use this command because there are no game state checks when you do this. For example, if you make a game persistent, start a quest, then leave the game, the game can't be joined by anyone but also can't be deleted. * `$sc `: Send a command to yourself. diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index 2d007ef2..2d85f63e 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -234,11 +234,11 @@ static void proxy_command_arrow(shared_ptr, session.server_channel.send(0x89, stoull(encode_sjis(args), nullptr, 0)); } -static void server_command_dbgid(shared_ptr, shared_ptr, +static void server_command_debug(shared_ptr, shared_ptr, shared_ptr c, const std::u16string&) { - c->options.prefer_high_lobby_client_id = !c->options.prefer_high_lobby_client_id; - send_text_message_printf(c, "ID preference set\nto $C6%s", - c->options.prefer_high_lobby_client_id ? "high" : "low"); + c->options.debug = !c->options.debug; + send_text_message_printf(c, "Debug %s", + c->options.debug ? "enabled" : "disabled"); } static void server_command_auction(shared_ptr, shared_ptr l, @@ -1186,7 +1186,7 @@ static const unordered_map chat_commands({ // TODO: implement this on proxy server {u"$bbchar", {server_command_convert_char_to_bb, nullptr, u"Usage:\nbbchar <1-4>"}}, {u"$cheat", {server_command_cheat, nullptr, u"Usage:\ncheat"}}, - {u"$dbgid", {server_command_dbgid, nullptr, u"Usage:\ndbgid"}}, + {u"$debug", {server_command_debug, nullptr, u"Usage:\ndebug"}}, {u"$edit", {server_command_edit, nullptr , u"Usage:\nedit "}}, {u"$event", {server_command_lobby_event, proxy_command_lobby_event, u"Usage:\nevent "}}, {u"$exit", {server_command_exit, proxy_command_exit, u"Usage:\nexit"}}, diff --git a/src/Client.cc b/src/Client.cc index f2731093..69ccebcc 100644 --- a/src/Client.cc +++ b/src/Client.cc @@ -29,7 +29,7 @@ ClientOptions::ClientOptions() : switch_assist(false), infinite_hp(false), infinite_tp(false), - prefer_high_lobby_client_id(false), + debug(false), override_section_id(-1), override_lobby_event(-1), override_lobby_number(-1), diff --git a/src/Client.hh b/src/Client.hh index e9ca031a..46acf2ad 100644 --- a/src/Client.hh +++ b/src/Client.hh @@ -29,7 +29,7 @@ struct ClientOptions { bool switch_assist; bool infinite_hp; bool infinite_tp; - bool prefer_high_lobby_client_id; + bool debug; int16_t override_section_id; int16_t override_lobby_event; int16_t override_lobby_number; diff --git a/src/Lobby.cc b/src/Lobby.cc index f5fb62e8..88315ee8 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -81,7 +81,7 @@ void Lobby::add_client(shared_ptr c, ssize_t required_client_id) { this->clients[required_client_id] = c; index = required_client_id; - } else if (c->options.prefer_high_lobby_client_id) { + } else if (c->options.debug) { for (index = max_clients - 1; index >= min_client_id; index--) { if (!this->clients[index].get()) { this->clients[index] = c; diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index cb51e39b..665efe45 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -156,6 +156,9 @@ static void on_unimplemented(shared_ptr, } else { c->log.warning("Unknown subcommand: %02hhX (public)", cmd.subcommand); } + if (c->options.debug) { + send_text_message_printf(c, "$C5Sub 6x%02hhX missing", cmd.subcommand); + } } @@ -438,6 +441,9 @@ static void on_switch_state_changed(shared_ptr, if ((l->flags & Lobby::Flag::CHEATS_ENABLED) && c->options.switch_assist && (c->last_switch_enabled_command.header.subcommand == 0x05)) { c->log.info("[Switch assist] Replaying previous enable command"); + if (c->options.debug) { + send_text_message(c, u"$C5Switch assist"); + } forward_subcommand(l, c, command, flag, &c->last_switch_enabled_command, sizeof(c->last_switch_enabled_command)); send_command_t(c, command, flag, c->last_switch_enabled_command); @@ -484,6 +490,11 @@ static void on_player_drop_item(shared_ptr, l->log.info("Player %hu dropped item %08" PRIX32 " (%s) at %hu:(%g, %g)", cmd.header.client_id.load(), cmd.item_id.load(), name.c_str(), cmd.area.load(), cmd.x.load(), cmd.z.load()); + if (c->options.debug) { + string name = name_for_item(item.data, true); + send_text_message_printf(c, "$C5Items: drop %08" PRIX32 "\n%s", + cmd.item_id.load(), name.c_str()); + } c->game_data.player()->print_inventory(stderr); } @@ -515,6 +526,11 @@ static void on_create_inventory_item(shared_ptr, auto name = name_for_item(item.data, false); l->log.info("Player %hu created inventory item %08" PRIX32 " (%s)", cmd.header.client_id.load(), cmd.item.id.load(), name.c_str()); + if (c->options.debug) { + string name = name_for_item(item.data, true); + send_text_message_printf(c, "$C5Items: create %08" PRIX32 "\n%s", + cmd.item.id.load(), name.c_str()); + } c->game_data.player()->print_inventory(stderr); } @@ -548,6 +564,11 @@ static void on_drop_partial_stack(shared_ptr, l->log.info("Player %hu split stack to create ground item %08" PRIX32 " (%s) at %hu:(%g, %g)", cmd.header.client_id.load(), item.data.id.load(), name.c_str(), cmd.area.load(), cmd.x.load(), cmd.z.load()); + if (c->options.debug) { + string name = name_for_item(item.data, true); + send_text_message_printf(c, "$C5Items: split %08" PRIX32 "\n%s", + item.data.id.load(), name.c_str()); + } c->game_data.player()->print_inventory(stderr); } @@ -588,6 +609,11 @@ static void on_drop_partial_stack_bb(shared_ptr, l->log.info("Player %hu split stack %08" PRIX32 " (removed: %s) at %hu:(%g, %g)", cmd.header.client_id.load(), cmd.item_id.load(), name.c_str(), cmd.area.load(), cmd.x.load(), cmd.z.load()); + if (c->options.debug) { + string name = name_for_item(item.data, true); + send_text_message_printf(c, "$C5Items: split/BB %08" PRIX32 "\n%s", + cmd.item_id.load(), name.c_str()); + } c->game_data.player()->print_inventory(stderr); send_drop_stacked_item(l, item.data, cmd.area, cmd.x, cmd.z); @@ -619,6 +645,11 @@ static void on_buy_shop_item(shared_ptr, auto name = name_for_item(item.data, false); l->log.info("Player %hu bought item %08" PRIX32 " (%s) from shop", cmd.header.client_id.load(), item.data.id.load(), name.c_str()); + if (c->options.debug) { + string name = name_for_item(item.data, true); + send_text_message_printf(c, "$C5Items: buy %08" PRIX32 "\n%s", + item.data.id.load(), name.c_str()); + } c->game_data.player()->print_inventory(stderr); } @@ -648,6 +679,11 @@ static void on_box_or_enemy_item_drop(shared_ptr, auto name = name_for_item(item.data, false); l->log.info("Leader created ground item %08" PRIX32 " (%s) at %hhu:(%g, %g)", item.data.id.load(), name.c_str(), cmd.area, cmd.x.load(), cmd.z.load()); + if (c->options.debug) { + string name = name_for_item(item.data, true); + send_text_message_printf(c, "$C5Items: drop %08" PRIX32 "\n%s", + item.data.id.load(), name.c_str()); + } } forward_subcommand(l, c, command, flag, data); @@ -679,6 +715,11 @@ static void on_pick_up_item(shared_ptr, auto name = name_for_item(item.data, false); l->log.info("Player %hu picked up %08" PRIX32 " (%s)", cmd.header.client_id.load(), cmd.item_id.load(), name.c_str()); + if (c->options.debug) { + string name = name_for_item(item.data, true); + send_text_message_printf(c, "$C5Items: pick %08" PRIX32 "\n%s", + cmd.item_id.load(), name.c_str()); + } effective_c->game_data.player()->print_inventory(stderr); } @@ -706,6 +747,11 @@ static void on_pick_up_item_request(shared_ptr, auto name = name_for_item(item.data, false); l->log.info("Player %hu picked up %08" PRIX32 " (%s)", cmd.header.client_id.load(), cmd.item_id.load(), name.c_str()); + if (c->options.debug) { + string name = name_for_item(item.data, true); + send_text_message_printf(c, "$C5Items: pick/BB %08" PRIX32 "\n%s", + cmd.item_id.load(), name.c_str()); + } c->game_data.player()->print_inventory(stderr); send_pick_up_item(l, c, cmd.item_id, cmd.area); @@ -751,11 +797,22 @@ static void on_use_item(shared_ptr, if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { size_t index = c->game_data.player()->inventory.find_item(cmd.item_id); - auto name = name_for_item(c->game_data.player()->inventory.items[index].data, false); + string name, colored_name; + { + // Note: We do this weird scoping thing because player_use_item will + // likely delete the item, which will break the reference here. + const auto& item = c->game_data.player()->inventory.items[index].data; + name = name_for_item(item, false); + colored_name = name_for_item(item, true); + } player_use_item(c, index); l->log.info("Player used item %hu:%08" PRIX32 " (%s)", cmd.header.client_id.load(), cmd.item_id.load(), name.c_str()); + if (c->options.debug) { + send_text_message_printf(c, "$C5Items: use %08" PRIX32 "\n%s", + cmd.item_id.load(), colored_name.c_str()); + } c->game_data.player()->print_inventory(stderr); } @@ -1077,20 +1134,28 @@ static void on_enemy_killed(shared_ptr s, send_text_message(c, u"$C6Missing enemy killed"); return; } - string e_str = l->enemies[cmd.enemy_id].str(); + + auto& e = l->enemies[cmd.enemy_id]; + string e_str = e.str(); c->log.info("Enemy killed: entry %hu => %s", cmd.enemy_id.load(), e_str.c_str()); - if (l->enemies[cmd.enemy_id].hit_flags & 0x80) { + if (e.hit_flags & 0x80) { + if (c->options.debug) { + send_text_message_printf(c, "$C5E-%hX (already dead)", cmd.enemy_id.load()); + } return; // Enemy is already dead } - if (l->enemies[cmd.enemy_id].experience == 0xFFFFFFFF) { - send_text_message(c, u"$C6Unknown enemy type killed"); + if (e.experience == 0xFFFFFFFF) { + if (c->options.debug) { + send_text_message_printf(c, "$C5E-%hX (missing definition)", cmd.enemy_id.load()); + } else { + send_text_message(c, u"$C6Unknown enemy type killed"); + } return; } - auto& enemy = l->enemies[cmd.enemy_id]; - enemy.hit_flags |= 0x80; + e.hit_flags |= 0x80; for (size_t x = 0; x < l->max_clients; x++) { - if (!((enemy.hit_flags >> x) & 1)) { + if (!((e.hit_flags >> x) & 1)) { continue; // Player did not hit this enemy } @@ -1104,14 +1169,18 @@ static void on_enemy_killed(shared_ptr s, // Killer gets full experience, others get 77% uint32_t exp; - if (enemy.last_hit == other_c->lobby_client_id) { - exp = enemy.experience; + if (e.last_hit == other_c->lobby_client_id) { + exp = e.experience; } else { - exp = ((enemy.experience * 77) / 100); + exp = ((e.experience * 77) / 100); } other_c->game_data.player()->disp.experience += exp; send_give_experience(l, other_c, exp); + if (other_c->options.debug) { + send_text_message_printf(other_c, "$C5+%" PRIu32 " E-%hX (%s)", + exp, cmd.enemy_id.load(), e.type_name); + } bool leveled_up = false; do { diff --git a/src/StaticGameData.cc b/src/StaticGameData.cc index 5d9ba1ad..20aecf96 100644 --- a/src/StaticGameData.cc +++ b/src/StaticGameData.cc @@ -1465,7 +1465,8 @@ uint8_t technique_for_name(const u16string& name) { string name_for_item(const ItemData& item, bool include_color_codes) { if (item.data1[0] == 0x04) { - return string_printf("%" PRIu32 " Meseta", item.data2d.load()); + return string_printf("%s%" PRIu32 " Meseta", + include_color_codes ? "$C7" : "", item.data2d.load()); } vector ret_tokens; @@ -1705,7 +1706,7 @@ string name_for_item(const ItemData& item, bool include_color_codes) { } else if (name_info.is_rare) { return "$C6" + ret; } else { - return ret; + return "$C7" + ret; } } else { return ret;