From fc078a5d517de0e3b733a0ab0fdab70814305eb0 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Fri, 24 Jun 2022 22:00:47 -0700 Subject: [PATCH] make it possible to disable item tracking --- src/ChatCommands.cc | 45 ++++++----- src/Lobby.cc | 5 +- src/Lobby.hh | 21 +++-- src/Main.cc | 6 ++ src/ReceiveCommands.cc | 7 +- src/ReceiveSubcommands.cc | 157 ++++++++++++++++++++++++------------- src/ServerState.cc | 1 + src/ServerState.hh | 1 + src/StaticGameData.cc | 2 + system/config.example.json | 8 ++ 10 files changed, 168 insertions(+), 85 deletions(-) diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index 3238ddae..f0fbce85 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -590,28 +590,31 @@ static void command_what(shared_ptr, shared_ptr l, if (!l->episode || (l->episode > 3)) { return; } - - float min_dist2 = 0.0f; - uint32_t nearest_item_id = 0xFFFFFFFF; - for (const auto& it : l->item_id_to_floor_item) { - if (it.second.area != c->area) { - continue; - } - float dx = it.second.x - c->x; - float dz = it.second.z - c->z; - float dist2 = (dx * dx) + (dz * dz); - if ((nearest_item_id == 0xFFFFFFFF) || (dist2 < min_dist2)) { - nearest_item_id = it.first; - min_dist2 = dist2; - } - } - - if (nearest_item_id == 0xFFFFFFFF) { - send_text_message(c, u"No items are near you"); + if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { + send_text_message(c, u"$C4Item tracking is off"); } else { - const auto& item = l->item_id_to_floor_item.at(nearest_item_id); - string name = name_for_item(item.inv_item.data, true); - send_text_message(c, decode_sjis(name)); + float min_dist2 = 0.0f; + uint32_t nearest_item_id = 0xFFFFFFFF; + for (const auto& it : l->item_id_to_floor_item) { + if (it.second.area != c->area) { + continue; + } + float dx = it.second.x - c->x; + float dz = it.second.z - c->z; + float dist2 = (dx * dx) + (dz * dz); + if ((nearest_item_id == 0xFFFFFFFF) || (dist2 < min_dist2)) { + nearest_item_id = it.first; + min_dist2 = dist2; + } + } + + if (nearest_item_id == 0xFFFFFFFF) { + send_text_message(c, u"$C4No items are near you"); + } else { + const auto& item = l->item_id_to_floor_item.at(nearest_item_id); + string name = name_for_item(item.inv_item.data, true); + send_text_message(c, decode_sjis(name)); + } } } diff --git a/src/Lobby.cc b/src/Lobby.cc index cbc8052c..d90cab15 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -96,8 +96,9 @@ void Lobby::add_client(shared_ptr c) { } } - // If the lobby is a game, assign the inventory's item IDs - if (this->is_game()) { + // If the lobby is a game and item tracking is enabled, assign the inventory's + // item IDs + if (this->is_game() && (this->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { auto& inv = c->game_data.player()->inventory; size_t count = min(inv.num_items, 30); for (size_t x = 0; x < count; x++) { diff --git a/src/Lobby.hh b/src/Lobby.hh index 4bf12e91..580053b5 100644 --- a/src/Lobby.hh +++ b/src/Lobby.hh @@ -18,14 +18,19 @@ struct Lobby { enum Flag { - GAME = 0x01, - CHEATS_ENABLED = 0x02, // game only - PUBLIC = 0x04, // lobby only - EPISODE_3_ONLY = 0x08, // lobby & game - QUEST_IN_PROGRESS = 0x10, // game only - JOINABLE_QUEST_IN_PROGRESS = 0x20, // game only - DEFAULT = 0x40, // lobby only; not set for games and private lobbies - PERSISTENT = 0x80, // if not set, lobby is deleted when empty + GAME = 0x00000001, + EPISODE_3_ONLY = 0x00000002, + + // Flags used only for games + CHEATS_ENABLED = 0x00000100, + QUEST_IN_PROGRESS = 0x00000200, + JOINABLE_QUEST_IN_PROGRESS = 0x00000400, + ITEM_TRACKING_ENABLED = 0x00000800, + + // Flags used only for lobbies + PUBLIC = 0x00010000, + DEFAULT = 0x00020000, + PERSISTENT = 0x00040000, }; uint32_t lobby_id; diff --git a/src/Main.cc b/src/Main.cc index d06f187a..8790a184 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -128,6 +128,12 @@ void populate_state_from_config(shared_ptr s, s->allow_unregistered_users = true; } + try { + s->item_tracking_enabled = d.at("EnableItemTracking")->as_bool(); + } catch (const out_of_range&) { + s->item_tracking_enabled = true; + } + for (const string& filename : list_directory("system/blueburst/keys")) { if (!ends_with(filename, ".nsk")) { continue; diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 1ce2411d..5dc99556 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -1767,6 +1767,8 @@ shared_ptr create_game_generic(shared_ptr s, throw invalid_argument("level too low for difficulty"); } + bool item_tracking_enabled = (c->version == GameVersion::BB) | s->item_tracking_enabled; + shared_ptr game(new Lobby()); game->name = name; game->password = password; @@ -1787,7 +1789,10 @@ shared_ptr create_game_generic(shared_ptr s, game->event = Lobby::game_event_for_lobby_event(current_lobby->event); game->block = 0xFF; game->max_clients = 4; - game->flags = (is_ep3 ? Lobby::Flag::EPISODE_3_ONLY : 0) | Lobby::Flag::GAME; + game->flags = + (is_ep3 ? Lobby::Flag::EPISODE_3_ONLY : 0) | + (item_tracking_enabled ? Lobby::Flag::ITEM_TRACKING_ENABLED : 0) | + Lobby::Flag::GAME; game->min_level = min_level; game->max_level = 0xFFFFFFFF; diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 40f7f266..36d532df 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -273,12 +273,14 @@ static void process_subcommand_player_drop_item(shared_ptr, return; } - l->add_item(c->game_data.player()->remove_item(cmd->item_id, 0), - cmd->area, cmd->x, cmd->z); + if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { + l->add_item(c->game_data.player()->remove_item(cmd->item_id, 0), + cmd->area, cmd->x, cmd->z); - log(INFO, "[Items/%08" PRIX32 "] Player %hhu dropped item %08" PRIX32 " at %hu:(%g, %g)", - l->lobby_id, cmd->client_id, cmd->item_id.load(), cmd->area.load(), cmd->x.load(), cmd->z.load()); - c->game_data.player()->print_inventory(stderr); + log(INFO, "[Items/%08" PRIX32 "] Player %hhu dropped item %08" PRIX32 " at %hu:(%g, %g)", + l->lobby_id, cmd->client_id, cmd->item_id.load(), cmd->area.load(), cmd->x.load(), cmd->z.load()); + c->game_data.player()->print_inventory(stderr); + } forward_subcommand(l, c, command, flag, data); } @@ -297,16 +299,18 @@ static void process_subcommand_create_inventory_item(shared_ptr, return; } - PlayerInventoryItem item; - item.equip_flags = 0; // TODO: Use the right default flags here - item.tech_flag = 0; - item.game_flags = 0; - item.data = cmd->item; - c->game_data.player()->add_item(item); + if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { + PlayerInventoryItem item; + item.equip_flags = 0; // TODO: Use the right default flags here + item.tech_flag = 0; + item.game_flags = 0; + item.data = cmd->item; + c->game_data.player()->add_item(item); - log(INFO, "[Items/%08" PRIX32 "] Player %hhu created inventory item %08" PRIX32, - l->lobby_id, cmd->client_id, cmd->item.id.load()); - c->game_data.player()->print_inventory(stderr); + log(INFO, "[Items/%08" PRIX32 "] Player %hhu created inventory item %08" PRIX32, + l->lobby_id, cmd->client_id, cmd->item.id.load()); + c->game_data.player()->print_inventory(stderr); + } forward_subcommand(l, c, command, flag, data); } @@ -324,18 +328,20 @@ static void process_subcommand_drop_partial_stack(shared_ptr, return; } - // TODO: Should we delete anything from the inventory here? Does the client - // send an appropriate 6x29 alongside this? - PlayerInventoryItem item; - item.equip_flags = 0; // TODO: Use the right default flags here - item.tech_flag = 0; - item.game_flags = 0; - item.data = cmd->data; - l->add_item(item, cmd->area, cmd->x, cmd->z); + if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { + // TODO: Should we delete anything from the inventory here? Does the client + // send an appropriate 6x29 alongside this? + PlayerInventoryItem item; + item.equip_flags = 0; // TODO: Use the right default flags here + item.tech_flag = 0; + item.game_flags = 0; + item.data = cmd->data; + l->add_item(item, cmd->area, cmd->x, cmd->z); - log(INFO, "[Items/%08" PRIX32 "] Player %hhu split stack to create ground item %08" PRIX32 " at %hu:(%g, %g)", - l->lobby_id, cmd->client_id, item.data.id.load(), cmd->area.load(), cmd->x.load(), cmd->z.load()); - c->game_data.player()->print_inventory(stderr); + log(INFO, "[Items/%08" PRIX32 "] Player %hhu split stack to create ground item %08" PRIX32 " at %hu:(%g, %g)", + l->lobby_id, cmd->client_id, item.data.id.load(), cmd->area.load(), cmd->x.load(), cmd->z.load()); + c->game_data.player()->print_inventory(stderr); + } forward_subcommand(l, c, command, flag, data); } @@ -350,6 +356,10 @@ static void process_subcommand_drop_partial_stack_bb(shared_ptr, return; } + if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { + throw logic_error("item tracking not enabled in BB game"); + } + auto item = c->game_data.player()->remove_item(cmd->item_id, cmd->amount); // if a stack was split, the original item still exists, so the dropped item @@ -389,16 +399,18 @@ static void process_subcommand_buy_shop_item(shared_ptr, return; } - PlayerInventoryItem item; - item.equip_flags = 0; // TODO: Use the right default flags here - item.tech_flag = 0; - item.game_flags = 0; - item.data = cmd->item; - c->game_data.player()->add_item(item); + if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { + PlayerInventoryItem item; + item.equip_flags = 0; // TODO: Use the right default flags here + item.tech_flag = 0; + item.game_flags = 0; + item.data = cmd->item; + c->game_data.player()->add_item(item); - log(INFO, "[Items/%08" PRIX32 "] Player %hhu bought item %08" PRIX32 " from shop", - l->lobby_id, cmd->client_id, item.data.id.load()); - c->game_data.player()->print_inventory(stderr); + log(INFO, "[Items/%08" PRIX32 "] Player %hhu bought item %08" PRIX32 " from shop", + l->lobby_id, cmd->client_id, item.data.id.load()); + c->game_data.player()->print_inventory(stderr); + } forward_subcommand(l, c, command, flag, data); } @@ -447,10 +459,12 @@ static void process_subcommand_pick_up_item(shared_ptr, return; } - effective_c->game_data.player()->add_item(l->remove_item(cmd->item_id)); - log(INFO, "[Items/%08" PRIX32 "] Player %hu picked up %08" PRIX32, - l->lobby_id, cmd->client_id.load(), cmd->item_id.load()); - effective_c->game_data.player()->print_inventory(stderr); + if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { + effective_c->game_data.player()->add_item(l->remove_item(cmd->item_id)); + log(INFO, "[Items/%08" PRIX32 "] Player %hu picked up %08" PRIX32, + l->lobby_id, cmd->client_id.load(), cmd->item_id.load()); + effective_c->game_data.player()->print_inventory(stderr); + } forward_subcommand(l, c, command, flag, data); } @@ -466,6 +480,10 @@ static void process_subcommand_pick_up_item_request(shared_ptr, return; } + if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { + throw logic_error("item tracking not enabled in BB game"); + } + c->game_data.player()->add_item(l->remove_item(cmd->item_id)); log(INFO, "[Items/%08" PRIX32 "/BB] Player %hhu picked up %08" PRIX32, @@ -482,7 +500,7 @@ static void process_subcommand_pick_up_item_request(shared_ptr, static void process_subcommand_equip_unequip_item(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, const string& data) { - // We don't track equip state on non-BB versions + // TODO: We should track equip state on non-BB versions if (l->version == GameVersion::BB) { const auto* cmd = check_size_sc(data); @@ -490,6 +508,10 @@ static void process_subcommand_equip_unequip_item(shared_ptr, return; } + if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { + throw logic_error("item tracking not enabled in BB game"); + } + size_t index = c->game_data.player()->inventory.find_item(cmd->item_id); if (cmd->command == 0x25) { c->game_data.player()->inventory.items[index].game_flags |= 0x00000008; // equip @@ -511,12 +533,14 @@ static void process_subcommand_use_item(shared_ptr, return; } - size_t index = c->game_data.player()->inventory.find_item(cmd->item_id); - player_use_item(c, index); + if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { + size_t index = c->game_data.player()->inventory.find_item(cmd->item_id); + player_use_item(c, index); - log(INFO, "[Items/%08" PRIX32 "] Player used item %hhu:%08" PRIX32, - l->lobby_id, cmd->client_id, cmd->item_id.load()); - c->game_data.player()->print_inventory(stderr); + log(INFO, "[Items/%08" PRIX32 "] Player used item %hhu:%08" PRIX32, + l->lobby_id, cmd->client_id, cmd->item_id.load()); + c->game_data.player()->print_inventory(stderr); + } forward_subcommand(l, c, command, flag, data); } @@ -572,6 +596,10 @@ static void process_subcommand_bank_action_bb(shared_ptr, return; } + if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { + throw logic_error("item tracking not enabled in BB game"); + } + if (cmd->action == 0) { // deposit if (cmd->item_id == 0xFFFFFFFF) { // meseta if (cmd->meseta_amount > c->game_data.player()->disp.meseta) { @@ -615,6 +643,10 @@ static void process_subcommand_sort_inventory_bb(shared_ptr, if (l->version == GameVersion::BB) { const auto* cmd = check_size_sc(data); + if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { + throw logic_error("item tracking not enabled in BB game"); + } + PlayerInventory sorted; for (size_t x = 0; x < 30; x++) { @@ -647,6 +679,10 @@ static void process_subcommand_enemy_drop_item_request(shared_ptr s return; } + if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { + throw logic_error("item tracking not enabled in BB game"); + } + PlayerInventoryItem item; // TODO: Deduplicate this code with the box drop item request handler @@ -701,6 +737,10 @@ static void process_subcommand_box_drop_item_request(shared_ptr s, return; } + if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { + throw logic_error("item tracking not enabled in BB game"); + } + PlayerInventoryItem item; bool is_rare = false; @@ -891,11 +931,13 @@ static void process_subcommand_destroy_inventory_item(shared_ptr, if (cmd->client_id != c->lobby_client_id) { return; } - c->game_data.player()->remove_item(cmd->item_id, cmd->amount); - log(INFO, "[Items/%08" PRIX32 "] Inventory item %hhu:%08" PRIX32 " destroyed (%" PRIX32 " of them)", - l->lobby_id, cmd->client_id, cmd->item_id.load(), cmd->amount.load()); - c->game_data.player()->print_inventory(stderr); - forward_subcommand(l, c, command, flag, data); + if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { + c->game_data.player()->remove_item(cmd->item_id, cmd->amount); + log(INFO, "[Items/%08" PRIX32 "] Inventory item %hhu:%08" PRIX32 " destroyed (%" PRIX32 " of them)", + l->lobby_id, cmd->client_id, cmd->item_id.load(), cmd->amount.load()); + c->game_data.player()->print_inventory(stderr); + forward_subcommand(l, c, command, flag, data); + } } static void process_subcommand_destroy_ground_item(shared_ptr, @@ -905,10 +947,12 @@ static void process_subcommand_destroy_ground_item(shared_ptr, if (!l->is_game()) { return; } - l->remove_item(cmd->item_id); - log(INFO, "[Items/%08" PRIX32 "] Ground item %08" PRIX32 " destroyed (%" PRIX32 " of them)", - l->lobby_id, cmd->item_id.load(), cmd->amount.load()); - forward_subcommand(l, c, command, flag, data); + if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { + l->remove_item(cmd->item_id); + log(INFO, "[Items/%08" PRIX32 "] Ground item %08" PRIX32 " destroyed (%" PRIX32 " of them)", + l->lobby_id, cmd->item_id.load(), cmd->amount.load()); + forward_subcommand(l, c, command, flag, data); + } } static void process_subcommand_identify_item_bb(shared_ptr, @@ -920,6 +964,10 @@ static void process_subcommand_identify_item_bb(shared_ptr, return; } + if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { + throw logic_error("item tracking not enabled in BB game"); + } + size_t x = c->game_data.player()->inventory.find_item(cmd->item_id); if (c->game_data.player()->inventory.items[x].data.data1[0] != 0) { return; // only weapons can be identified @@ -956,6 +1004,9 @@ static void process_subcommand_identify_item_bb(shared_ptr, // if (cmd->client_id != c->lobby_client_id) { // return; // } +// if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { +// throw logic_error("item tracking not enabled in BB game"); +// } // // size_t x = c->game_data.player()->inventory.find_item(cmd->item_id); // c->game_data.player()->inventory.items[x] = c->game_data.player()->identify_result; diff --git a/src/ServerState.cc b/src/ServerState.cc index 88ea1808..2a3ea937 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -18,6 +18,7 @@ ServerState::ServerState() : dns_server_port(0), ip_stack_debug(false), allow_unregistered_users(false), + item_tracking_enabled(true), run_shell_behavior(RunShellBehavior::DEFAULT), next_lobby_id(1), pre_lobby_event(0), ep3_menu_song(-1) { diff --git a/src/ServerState.hh b/src/ServerState.hh index a50c5e1c..01690063 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -46,6 +46,7 @@ struct ServerState { std::vector ip_stack_addresses; bool ip_stack_debug; bool allow_unregistered_users; + bool item_tracking_enabled; RunShellBehavior run_shell_behavior; std::vector> bb_private_keys; std::shared_ptr function_code_index; diff --git a/src/StaticGameData.cc b/src/StaticGameData.cc index 55cd37be..c068eaa8 100644 --- a/src/StaticGameData.cc +++ b/src/StaticGameData.cc @@ -1468,6 +1468,8 @@ string name_for_item(const ItemData& item, bool include_color_codes) { // For weapons, specials appear before the weapon name if ((item.data1[0] == 0x00) && (item.data1[4] != 0x00)) { + // 0x80 is the unidentified flag, but we always return the identified name + // of the item here, so we ignore it bool is_present = item.data1[4] & 0x40; uint8_t special_id = item.data1[4] & 0x3F; if (is_present) { diff --git a/system/config.example.json b/system/config.example.json index e8193cc0..98567fb0 100644 --- a/system/config.example.json +++ b/system/config.example.json @@ -128,6 +128,14 @@ // BB users. "WelcomeMessage": "", + // By default, the server keeps track of items in all games, even for versions + // other than Blue Burst. This enables use of the $what command, as well as + // protection against item duplication cheats (the cheater is disconnected + // instead of the other players). If item tracking causes any issues, it can + // be turned off here. This option has no effect on Blue Burst games - item + // tracking is always enabled for them. + "EnableItemTracking": true, + // Item drop rates for non-rare items in BB games. For each type (boxes or // enemies), all the categories must add up to a number less than 0x100000000. // Each number is a probability (out of 0x100000000) that the given item type