From d5cc91a9bf88be77f6ba449f67c28cfed32edaaa Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sat, 7 Oct 2023 18:17:54 -0700 Subject: [PATCH] handle inventory extension data properly --- TODO.md | 1 - src/ChatCommands.cc | 28 +++--- src/CommandFormats.hh | 4 +- src/Items.cc | 30 +++--- src/Lobby.cc | 10 +- src/Lobby.hh | 6 +- src/Player.cc | 95 ++++++++++++++---- src/Player.hh | 23 ++++- src/PlayerSubordinates.cc | 78 +++++---------- src/PlayerSubordinates.hh | 66 ++++++------ src/ProxyCommands.cc | 24 ++--- src/ProxyServer.hh | 2 +- src/ReceiveSubcommands.cc | 204 ++++++++++++++++++-------------------- src/SendCommands.cc | 15 ++- src/ServerShell.cc | 13 ++- 15 files changed, 309 insertions(+), 290 deletions(-) diff --git a/TODO.md b/TODO.md index 39af3e50..b0dde18f 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,5 @@ ## General -- Extension data in inventories is not handled properly - Test PSOX (blocked on Insignia private server support) - Implement server-side drops on non-BB game versions - Find a way to silence audio in RunDOL.s diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index c754878b..850825f3 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -835,7 +835,7 @@ static void server_command_edit(shared_ptr c, const std::u16string& args uint8_t level = stoul(tokens.at(2)) - 1; if (tokens.at(1) == "all") { for (size_t x = 0; x < 0x14; x++) { - c->game_data.player()->disp.technique_levels.data()[x] = level; + c->game_data.player()->set_technique_level(x, level); } } else { uint8_t tech_id = technique_for_name(decode_sjis(tokens.at(1))); @@ -844,7 +844,7 @@ static void server_command_edit(shared_ptr c, const std::u16string& args return; } try { - c->game_data.player()->disp.technique_levels[tech_id] = level; + c->game_data.player()->set_technique_level(tech_id, level); } catch (const out_of_range&) { send_text_message(c, u"$C6Invalid technique"); return; @@ -1133,7 +1133,7 @@ static void server_command_what(shared_ptr c, const std::u16string&) { 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 = item.inv_item.data.name(true); + string name = item.data.name(true); send_text_message(c, decode_sjis(name)); } } @@ -1226,14 +1226,13 @@ static void server_command_item(shared_ptr c, const std::u16string& args check_is_game(l, true); check_cheats_enabled(s, l); - PlayerInventoryItem item; - item.data = ItemData(encode_sjis(args)); - item.data.id = l->generate_item_id(c->lobby_client_id); + ItemData item(encode_sjis(args)); + item.id = l->generate_item_id(c->lobby_client_id); l->add_item(item, c->area, c->x, c->z); - send_drop_stacked_item(l, item.data, c->area, c->x, c->z); + send_drop_stacked_item(l, item, c->area, c->x, c->z); - string name = item.data.name(true); + string name = item.name(true); send_text_message(c, u"$C7Item created:\n" + decode_sjis(name)); } @@ -1258,21 +1257,20 @@ static void proxy_command_item(shared_ptr ses, const bool set_drop = (!args.empty() && (args[0] == u'!')); - PlayerInventoryItem item; - item.data = ItemData(encode_sjis(set_drop ? args.substr(1) : args)); - item.data.id = random_object(); + ItemData item(encode_sjis(set_drop ? args.substr(1) : args)); + item.id = random_object(); if (set_drop) { ses->next_drop_item = item; - string name = ses->next_drop_item.data.name(true); + string name = ses->next_drop_item.name(true); send_text_message(ses->client_channel, u"$C7Next drop:\n" + decode_sjis(name)); } else { - send_drop_stacked_item(ses->client_channel, item.data, ses->area, ses->x, ses->z); - send_drop_stacked_item(ses->server_channel, item.data, ses->area, ses->x, ses->z); + send_drop_stacked_item(ses->client_channel, item, ses->area, ses->x, ses->z); + send_drop_stacked_item(ses->server_channel, item, ses->area, ses->x, ses->z); - string name = item.data.name(true); + string name = item.name(true); send_text_message(ses->client_channel, u"$C7Item created:\n" + decode_sjis(name)); } } diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index 3f72182d..6b889253 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -4570,7 +4570,7 @@ struct G_SyncPlayerDispAndInventory_V3_6x70 { /* 00A4 */ parray unknown_a9; /* 00B8 */ le_uint32_t unknown_a10; /* 00BC */ le_uint32_t unknown_a11; - /* 00C0 */ parray technique_levels; // Last byte is uninitialized + /* 00C0 */ parray technique_levels_v1; // Last byte is uninitialized /* 00D4 */ PlayerVisualConfig visual; /* 0124 */ PlayerStats stats; /* 0148 */ struct { @@ -5305,7 +5305,7 @@ struct G_CardCounts_GC_Ep3_6xBC { struct G_BankContentsHeader_BB_6xBC { G_ExtendedHeader header; le_uint32_t checksum; // can be random; client won't notice - le_uint32_t numItems; + le_uint32_t num_items; le_uint32_t meseta; // Item data follows } __packed__; diff --git a/src/Items.cc b/src/Items.cc index e5e843a8..f7a5acba 100644 --- a/src/Items.cc +++ b/src/Items.cc @@ -28,45 +28,51 @@ void player_use_item(shared_ptr c, size_t item_index) { if (item.data.data1[2] > max_level) { throw runtime_error("technique level too high"); } - player->disp.technique_levels.data()[item.data.data1[4]] = item.data.data1[2]; + player->set_technique_level(item.data.data1[4], item.data.data1[2]); } else if ((item_identifier & 0xFFFF00) == 0x030A00) { // Grinder if (item.data.data1[2] > 2) { - throw invalid_argument("incorrect grinder value"); + throw runtime_error("incorrect grinder value"); } auto& weapon = player->inventory.items[player->inventory.find_equipped_weapon()]; - auto weapon_def = s->item_parameter_table->get_weapon( - weapon.data.data1[1], weapon.data.data1[2]); + auto weapon_def = s->item_parameter_table->get_weapon(weapon.data.data1[1], weapon.data.data1[2]); if (weapon.data.data1[3] >= weapon_def.max_grind) { throw runtime_error("weapon already at maximum grind"); } weapon.data.data1[3] += (item.data.data1[2] + 1); } else if ((item_identifier & 0xFFFF00) == 0x030B00) { // Material + auto p = c->game_data.player(); + using Type = SavedPlayerDataBB::MaterialType; switch (item.data.data1[2]) { case 0: // Power Material - c->game_data.player()->disp.stats.char_stats.atp += 2; + p->set_material_usage(Type::POWER, p->get_material_usage(Type::POWER) + 1); + p->disp.stats.char_stats.atp += 2; break; case 1: // Mind Material - c->game_data.player()->disp.stats.char_stats.mst += 2; + p->set_material_usage(Type::MIND, p->get_material_usage(Type::MIND) + 1); + p->disp.stats.char_stats.mst += 2; break; case 2: // Evade Material - c->game_data.player()->disp.stats.char_stats.evp += 2; + p->set_material_usage(Type::EVADE, p->get_material_usage(Type::EVADE) + 1); + p->disp.stats.char_stats.evp += 2; break; case 3: // HP Material - c->game_data.player()->inventory.hp_materials_used += 2; + p->set_material_usage(Type::HP, p->get_material_usage(Type::HP) + 1); break; case 4: // TP Material - c->game_data.player()->inventory.tp_materials_used += 2; + p->set_material_usage(Type::TP, p->get_material_usage(Type::TP) + 1); break; case 5: // Def Material - c->game_data.player()->disp.stats.char_stats.dfp += 2; + p->set_material_usage(Type::DEF, p->get_material_usage(Type::DEF) + 1); + p->disp.stats.char_stats.dfp += 2; break; case 6: // Luck Material - c->game_data.player()->disp.stats.char_stats.lck += 2; + p->set_material_usage(Type::LUCK, p->get_material_usage(Type::LUCK) + 1); + p->disp.stats.char_stats.lck += 2; break; default: - throw invalid_argument("unknown material used"); + throw runtime_error("unknown material used"); } } else if ((item_identifier & 0xFFFF00) == 0x030F00) { // AddSlot diff --git a/src/Lobby.cc b/src/Lobby.cc index 9acdedc9..bf5055f6 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -257,20 +257,20 @@ uint8_t Lobby::game_event_for_lobby_event(uint8_t lobby_event) { return lobby_event; } -void Lobby::add_item(const PlayerInventoryItem& item, uint8_t area, float x, float z) { - auto& fi = this->item_id_to_floor_item[item.data.id]; - fi.inv_item = item; +void Lobby::add_item(const ItemData& data, uint8_t area, float x, float z) { + auto& fi = this->item_id_to_floor_item[data.id]; + fi.data = data; fi.area = area; fi.x = x; fi.z = z; } -PlayerInventoryItem Lobby::remove_item(uint32_t item_id) { +ItemData Lobby::remove_item(uint32_t item_id) { auto item_it = this->item_id_to_floor_item.find(item_id); if (item_it == this->item_id_to_floor_item.end()) { throw out_of_range("item not present"); } - PlayerInventoryItem ret = std::move(item_it->second.inv_item); + ItemData ret = item_it->second.data; this->item_id_to_floor_item.erase(item_it); return ret; } diff --git a/src/Lobby.hh b/src/Lobby.hh index 31792d87..46f5d7fe 100644 --- a/src/Lobby.hh +++ b/src/Lobby.hh @@ -57,7 +57,7 @@ struct Lobby : public std::enable_shared_from_this { // Item info struct FloorItem { - PlayerInventoryItem inv_item; + ItemData data; float x; float z; uint8_t area; @@ -143,8 +143,8 @@ struct Lobby : public std::enable_shared_from_this { const std::u16string* identifier = nullptr, uint64_t serial_number = 0); - void add_item(const PlayerInventoryItem& item, uint8_t area, float x, float z); - PlayerInventoryItem remove_item(uint32_t item_id); + void add_item(const ItemData& item, uint8_t area, float x, float z); + ItemData remove_item(uint32_t item_id); size_t find_item(uint32_t item_id); uint32_t generate_item_id(uint8_t client_id); diff --git a/src/Player.cc b/src/Player.cc index 26876ad9..8f76c25a 100644 --- a/src/Player.cc +++ b/src/Player.cc @@ -213,31 +213,31 @@ void SavedPlayerDataBB::update_to_latest_version() { // TODO: Eliminate duplication between this function and the parallel function // in PlayerBank -void SavedPlayerDataBB::add_item(const PlayerInventoryItem& item) { - uint32_t pid = item.data.primary_identifier(); +void SavedPlayerDataBB::add_item(const ItemData& item) { + uint32_t pid = item.primary_identifier(); // Annoyingly, meseta is in the disp data, not in the inventory struct. If the // item is meseta, we have to modify disp instead. if (pid == MESETA_IDENTIFIER) { - this->add_meseta(item.data.data2d); + this->add_meseta(item.data2d); return; } // Handle combinable items - size_t combine_max = item.data.max_stack_size(); + size_t combine_max = item.max_stack_size(); if (combine_max > 1) { // Get the item index if there's already a stack of the same item in the // player's inventory size_t y; for (y = 0; y < this->inventory.num_items; y++) { - if (this->inventory.items[y].data.primary_identifier() == item.data.primary_identifier()) { + if (this->inventory.items[y].data.primary_identifier() == item.primary_identifier()) { break; } } // If we found an existing stack, add it to the total and return if (y < this->inventory.num_items) { - this->inventory.items[y].data.data1[5] += item.data.data1[5]; + this->inventory.items[y].data.data1[5] += item.data1[5]; if (this->inventory.items[y].data.data1[5] > combine_max) { this->inventory.items[y].data.data1[5] = combine_max; } @@ -250,22 +250,24 @@ void SavedPlayerDataBB::add_item(const PlayerInventoryItem& item) { if (this->inventory.num_items >= 30) { throw runtime_error("inventory is full"); } - this->inventory.items[this->inventory.num_items] = item; + auto& inv_item = this->inventory.items[this->inventory.num_items]; + inv_item.present = 1; + inv_item.flags = 0; + inv_item.data = item; this->inventory.num_items++; } // TODO: Eliminate code duplication between this function and the parallel // function in PlayerBank -PlayerInventoryItem SavedPlayerDataBB::remove_item( - uint32_t item_id, uint32_t amount, bool allow_meseta_overdraft) { - PlayerInventoryItem ret; +ItemData SavedPlayerDataBB::remove_item(uint32_t item_id, uint32_t amount, bool allow_meseta_overdraft) { + ItemData ret; // If we're removing meseta (signaled by an invalid item ID), then create a // meseta item. if (item_id == 0xFFFFFFFF) { this->remove_meseta(amount, allow_meseta_overdraft); - ret.data.data1[0] = 0x04; - ret.data.data2d = amount; + ret.data1[0] = 0x04; + ret.data2d = amount; return ret; } @@ -278,9 +280,9 @@ PlayerInventoryItem SavedPlayerDataBB::remove_item( // applies if amount is nonzero. if (amount && (inventory_item.data.stack_size() > 1) && (amount < inventory_item.data.data1[5])) { - ret = inventory_item; - ret.data.data1[5] = amount; - ret.data.id = 0xFFFFFFFF; + ret = inventory_item.data; + ret.data1[5] = amount; + ret.id = 0xFFFFFFFF; inventory_item.data.data1[5] -= amount; return ret; } @@ -288,12 +290,15 @@ PlayerInventoryItem SavedPlayerDataBB::remove_item( // If we get here, then it's not meseta, and either it's not a combine item or // we're removing the entire stack. Delete the item from the inventory slot // and return the deleted item. - ret = inventory_item; + ret = inventory_item.data; this->inventory.num_items--; for (size_t x = index; x < this->inventory.num_items; x++) { this->inventory.items[x] = this->inventory.items[x + 1]; } - this->inventory.items[this->inventory.num_items] = PlayerInventoryItem(); + auto& last_item = this->inventory.items[this->inventory.num_items]; + last_item.present = 0; + last_item.flags = 0; + last_item.data.clear(); return ret; } @@ -310,3 +315,59 @@ void SavedPlayerDataBB::remove_meseta(uint32_t amount, bool allow_overdraft) { throw out_of_range("player does not have enough meseta"); } } + +uint8_t SavedPlayerDataBB::get_technique_level(uint8_t which) const { + return (this->disp.technique_levels_v1[which] == 0xFF) + ? 0xFF + : (this->disp.technique_levels_v1[which] + this->inventory.items[which].extension_data1); +} + +void SavedPlayerDataBB::set_technique_level(uint8_t which, uint8_t level) { + if (level == 0xFF) { + this->disp.technique_levels_v1[which] = 0xFF; + this->inventory.items[which].extension_data1 = 0x00; + } else if (level <= 0x0E) { + this->disp.technique_levels_v1[which] = level; + this->inventory.items[which].extension_data1 = 0x00; + } else { + this->disp.technique_levels_v1[which] = 0x0E; + this->inventory.items[which].extension_data1 = level - 0x0E; + } +} + +uint8_t SavedPlayerDataBB::get_material_usage(MaterialType which) const { + switch (which) { + case MaterialType::HP: + return this->inventory.hp_materials_used; + case MaterialType::TP: + return this->inventory.tp_materials_used; + case MaterialType::POWER: + case MaterialType::MIND: + case MaterialType::EVADE: + case MaterialType::DEF: + case MaterialType::LUCK: + return this->inventory.items[8 + static_cast(which)].extension_data2; + default: + throw logic_error("invalid material type"); + } +} + +void SavedPlayerDataBB::set_material_usage(MaterialType which, uint8_t usage) { + switch (which) { + case MaterialType::HP: + this->inventory.hp_materials_used = usage; + break; + case MaterialType::TP: + this->inventory.tp_materials_used = usage; + break; + case MaterialType::POWER: + case MaterialType::MIND: + case MaterialType::EVADE: + case MaterialType::DEF: + case MaterialType::LUCK: + this->inventory.items[8 + static_cast(which)].extension_data2 = usage; + break; + default: + throw logic_error("invalid material type"); + } +} diff --git a/src/Player.hh b/src/Player.hh index 3dbbff63..ddd5c165 100644 --- a/src/Player.hh +++ b/src/Player.hh @@ -48,12 +48,27 @@ struct SavedPlayerDataBB { // .nsc file format void update_to_latest_version(); - void add_item(const PlayerInventoryItem& item); - PlayerInventoryItem remove_item( - uint32_t item_id, uint32_t amount, bool allow_meseta_overdraft); + void add_item(const ItemData& item); + ItemData remove_item(uint32_t item_id, uint32_t amount, bool allow_meseta_overdraft); void add_meseta(uint32_t amount); void remove_meseta(uint32_t amount, bool allow_overdraft); + uint8_t get_technique_level(uint8_t which) const; // Returns FF or 00-1D + void set_technique_level(uint8_t which, uint8_t level); + + enum class MaterialType : int8_t { + HP = -2, + TP = -1, + POWER = 0, + MIND = 1, + EVADE = 2, + DEF = 3, + LUCK = 4, + }; + + uint8_t get_material_usage(MaterialType which) const; + void set_material_usage(MaterialType which, uint8_t usage); + void print_inventory(FILE* stream) const; } __attribute__((packed)); @@ -95,7 +110,7 @@ public: // These are only used if the client is BB std::string bb_username; size_t bb_player_index; - PlayerInventoryItem identify_result; + ItemData identify_result; std::array, 3> shop_contents; bool should_save; diff --git a/src/PlayerSubordinates.cc b/src/PlayerSubordinates.cc index e9a2a196..2d21edc1 100644 --- a/src/PlayerSubordinates.cc +++ b/src/PlayerSubordinates.cc @@ -43,7 +43,7 @@ PlayerDispDataBB PlayerDispDataDCPCV3::to_bb() const { bb.visual.name = " 0"; bb.name = this->visual.name; bb.config = this->config; - bb.technique_levels = this->v1_technique_levels; + bb.technique_levels_v1 = this->technique_levels_v1; return bb; } @@ -54,7 +54,7 @@ PlayerDispDataDCPCV3 PlayerDispDataBB::to_dcpcv3() const { ret.visual.name = this->name; remove_language_marker_inplace(ret.visual.name); ret.config = this->config; - ret.v1_technique_levels = this->technique_levels; + ret.technique_levels_v1 = this->technique_levels_v1; return ret; } @@ -278,68 +278,34 @@ PlayerRecordsBB_Challenge::operator PlayerRecordsV3_Challenge() const { return ret; } -PlayerInventoryItem::PlayerInventoryItem() { - this->clear(); -} - -PlayerInventoryItem::PlayerInventoryItem(const PlayerBankItem& src) - : present(1), - extension_data1(0), - extension_data2(0), - flags(0), - data(src.data) {} - -void PlayerInventoryItem::clear() { - this->present = 0x0000; - this->extension_data1 = 0x00; - this->extension_data2 = 0x00; - this->flags = 0x00000000; - this->data.clear(); -} - -PlayerBankItem::PlayerBankItem() { - this->clear(); -} - -PlayerBankItem::PlayerBankItem(const PlayerInventoryItem& src) - : data(src.data), - amount(this->data.stack_size()), - show_flags(1) {} - -void PlayerBankItem::clear() { - this->data.clear(); - this->amount = 0; - this->show_flags = 0; -} - PlayerInventory::PlayerInventory() : num_items(0), hp_materials_used(0), tp_materials_used(0), language(0) {} -void PlayerBank::add_item(const PlayerBankItem& item) { - uint32_t pid = item.data.primary_identifier(); +void PlayerBank::add_item(const ItemData& item) { + uint32_t pid = item.primary_identifier(); if (pid == MESETA_IDENTIFIER) { - this->meseta += item.data.data2d; + this->meseta += item.data2d; if (this->meseta > 999999) { this->meseta = 999999; } return; } - size_t combine_max = item.data.max_stack_size(); + size_t combine_max = item.max_stack_size(); if (combine_max > 1) { size_t y; for (y = 0; y < this->num_items; y++) { - if (this->items[y].data.primary_identifier() == item.data.primary_identifier()) { + if (this->items[y].data.primary_identifier() == item.primary_identifier()) { break; } } if (y < this->num_items) { - this->items[y].data.data1[5] += item.data.data1[5]; + this->items[y].data.data1[5] += item.data1[5]; if (this->items[y].data.data1[5] > combine_max) { this->items[y].data.data1[5] = combine_max; } @@ -351,19 +317,22 @@ void PlayerBank::add_item(const PlayerBankItem& item) { if (this->num_items >= 200) { throw runtime_error("bank is full"); } - this->items[this->num_items] = item; + auto& last_item = this->items[this->num_items]; + last_item.data = item; + last_item.amount = (item.max_stack_size() > 1) ? item.data1[5] : 1; + last_item.present = 1; this->num_items++; } -PlayerBankItem PlayerBank::remove_item(uint32_t item_id, uint32_t amount) { - PlayerBankItem ret; +ItemData PlayerBank::remove_item(uint32_t item_id, uint32_t amount) { + ItemData ret; if (item_id == 0xFFFFFFFF) { if (amount > this->meseta) { throw out_of_range("player does not have enough meseta"); } - ret.data.data1[0] = 0x04; - ret.data.data2d = amount; + ret.data1[0] = 0x04; + ret.data2d = amount; this->meseta -= amount; return ret; } @@ -371,22 +340,23 @@ PlayerBankItem PlayerBank::remove_item(uint32_t item_id, uint32_t amount) { size_t index = this->find_item(item_id); auto& bank_item = this->items[index]; - if (amount && (bank_item.data.stack_size() > 1) && - (amount < bank_item.data.data1[5])) { - ret = bank_item; - ret.data.data1[5] = amount; - ret.amount = amount; + if (amount && (bank_item.data.stack_size() > 1) && (amount < bank_item.data.data1[5])) { + ret = bank_item.data; + ret.data1[5] = amount; bank_item.data.data1[5] -= amount; bank_item.amount -= amount; return ret; } - ret = bank_item; + ret = bank_item.data; this->num_items--; for (size_t x = index; x < this->num_items; x++) { this->items[x] = this->items[x + 1]; } - this->items[this->num_items] = PlayerBankItem(); + auto& last_item = this->items[this->num_items]; + last_item.amount = 0; + last_item.present = 0; + last_item.data.clear(); return ret; } diff --git a/src/PlayerSubordinates.hh b/src/PlayerSubordinates.hh index f3f45726..3cf1f421 100644 --- a/src/PlayerSubordinates.hh +++ b/src/PlayerSubordinates.hh @@ -23,10 +23,10 @@ extern FileContentsCache player_files_cache; // stores arrays of bytes striped across these structures. In newserv, we call // those fields extension_data. They contain: // items[0].extension_data1 through items[19].extension_data1: -// Extended technique levels. The values in the v1_technique_levels array +// Extended technique levels. The values in the technique_levels_v1 array // only go up to 14 (tech level 15); if the player has a technique above // level 15, the corresponding extension_data1 field holds the remaining -// levels (so a level 20 tech would have 14 in v1_technique_levels and 5 +// levels (so a level 20 tech would have 14 in technique_levels_v1 and 5 // in the corresponding item's extension_data1 field). // items[0].extension_data2 through items[3].extension_data2: // The value known as unknown_a1 in the PSOGCCharacterFile::Character @@ -40,37 +40,30 @@ extern FileContentsCache player_files_cache; // items[13].extension_data2 through items[15].extension_data2: // Unknown. These are not an array, but do appear to be related. -struct PlayerBankItem; - -struct PlayerInventoryItem { // 0x1C bytes - le_uint16_t present; +struct PlayerInventoryItem { + /* 00 */ le_uint16_t present = 0; // See note above about these fields - uint8_t extension_data1; - uint8_t extension_data2; - le_uint32_t flags; // 8 = equipped - ItemData data; - - PlayerInventoryItem(); - PlayerInventoryItem(const PlayerBankItem&); - void clear(); + /* 02 */ uint8_t extension_data1 = 0; + /* 03 */ uint8_t extension_data2 = 0; + /* 04 */ le_uint32_t flags = 0; // 8 = equipped + /* 08 */ ItemData data; + /* 1C */ } __attribute__((packed)); -struct PlayerBankItem { // 0x18 bytes - ItemData data; - le_uint16_t amount; - le_uint16_t show_flags; - - PlayerBankItem(); - PlayerBankItem(const PlayerInventoryItem&); - void clear(); +struct PlayerBankItem { + /* 00 */ ItemData data; + /* 14 */ le_uint16_t amount = 0; + /* 16 */ le_uint16_t present = 0; + /* 18 */ } __attribute__((packed)); -struct PlayerInventory { // 0x34C bytes - uint8_t num_items; - uint8_t hp_materials_used; - uint8_t tp_materials_used; - uint8_t language; - PlayerInventoryItem items[30]; +struct PlayerInventory { + /* 0000 */ uint8_t num_items = 0; + /* 0001 */ uint8_t hp_materials_used = 0; + /* 0002 */ uint8_t tp_materials_used = 0; + /* 0003 */ uint8_t language = 1; // English + /* 0004 */ parray items; + /* 034C */ PlayerInventory(); @@ -81,10 +74,11 @@ struct PlayerInventory { // 0x34C bytes size_t find_equipped_mag() const; } __attribute__((packed)); -struct PlayerBank { // 0x12C8 bytes - le_uint32_t num_items; - le_uint32_t meseta; - PlayerBankItem items[200]; +struct PlayerBank { + /* 0000 */ le_uint32_t num_items = 0; + /* 0004 */ le_uint32_t meseta = 0; + /* 0008 */ parray items; + /* 12C8 */ void load(const std::string& filename); void save(const std::string& filename, bool save_to_filesystem) const; @@ -92,8 +86,8 @@ struct PlayerBank { // 0x12C8 bytes bool switch_with_file(const std::string& save_filename, const std::string& load_filename); - void add_item(const PlayerBankItem& item); - PlayerBankItem remove_item(uint32_t item_id, uint32_t amount); + void add_item(const ItemData& item); + ItemData remove_item(uint32_t item_id, uint32_t amount); size_t find_item(uint32_t item_id); } __attribute__((packed)); @@ -139,7 +133,7 @@ struct PlayerDispDataDCPCV3 { /* 00 */ PlayerStats stats; /* 24 */ PlayerVisualConfig visual; /* 74 */ parray config; - /* BC */ parray v1_technique_levels; + /* BC */ parray technique_levels_v1; /* D0 */ void enforce_v2_limits(); PlayerDispDataBB to_bb() const; @@ -164,7 +158,7 @@ struct PlayerDispDataBB { /* 008C */ le_uint32_t play_time = 0; /* 0090 */ uint32_t unknown_a3 = 0; /* 0094 */ parray config; - /* 017C */ parray technique_levels; + /* 017C */ parray technique_levels_v1; /* 0190 */ inline void enforce_v2_limits() {} diff --git a/src/ProxyCommands.cc b/src/ProxyCommands.cc index 57c4ee72..682de4aa 100644 --- a/src/ProxyCommands.cc +++ b/src/ProxyCommands.cc @@ -946,16 +946,12 @@ static HandlerResult S_6x(shared_ptr ses, uint16_t, return HandlerResult::Type::SUPPRESS; } - } else if ((data[0] == 0x60) && - ses->next_drop_item.data.data1d[0] && - (ses->version != GameVersion::BB)) { + } else if ((data[0] == 0x60) && ses->next_drop_item.data1d[0] && (ses->version != GameVersion::BB)) { const auto& cmd = check_size_t( data, sizeof(G_StandardDropItemRequest_PC_V3_BB_6x60)); - ses->next_drop_item.data.id = ses->next_item_id++; - send_drop_item(ses->server_channel, ses->next_drop_item.data, - true, cmd.area, cmd.x, cmd.z, cmd.entity_id); - send_drop_item(ses->client_channel, ses->next_drop_item.data, - true, cmd.area, cmd.x, cmd.z, cmd.entity_id); + ses->next_drop_item.id = ses->next_item_id++; + send_drop_item(ses->server_channel, ses->next_drop_item, true, cmd.area, cmd.x, cmd.z, cmd.entity_id); + send_drop_item(ses->client_channel, ses->next_drop_item, true, cmd.area, cmd.x, cmd.z, cmd.entity_id); ses->next_drop_item.clear(); return HandlerResult::Type::SUPPRESS; @@ -963,15 +959,11 @@ static HandlerResult S_6x(shared_ptr ses, uint16_t, // the comparison is always false (which even happens in some environments // if we use -0x5E... apparently char is unsigned on some systems, or // std::string's char_type isn't char??) - } else if ((static_cast(data[0]) == 0xA2) && - ses->next_drop_item.data.data1d[0] && - (ses->version != GameVersion::BB)) { + } else if ((static_cast(data[0]) == 0xA2) && ses->next_drop_item.data1d[0] && (ses->version != GameVersion::BB)) { const auto& cmd = check_size_t(data); - ses->next_drop_item.data.id = ses->next_item_id++; - send_drop_item(ses->server_channel, ses->next_drop_item.data, - false, cmd.area, cmd.x, cmd.z, cmd.entity_id); - send_drop_item(ses->client_channel, ses->next_drop_item.data, - false, cmd.area, cmd.x, cmd.z, cmd.entity_id); + ses->next_drop_item.id = ses->next_item_id++; + send_drop_item(ses->server_channel, ses->next_drop_item, false, cmd.area, cmd.x, cmd.z, cmd.entity_id); + send_drop_item(ses->client_channel, ses->next_drop_item, false, cmd.area, cmd.x, cmd.z, cmd.entity_id); ses->next_drop_item.clear(); return HandlerResult::Type::SUPPRESS; diff --git a/src/ProxyServer.hh b/src/ProxyServer.hh index eea22437..4fbbabd3 100644 --- a/src/ProxyServer.hh +++ b/src/ProxyServer.hh @@ -73,7 +73,7 @@ public: // A null handler in here means to forward the response to the remote server std::deque> function_call_return_handler_queue; G_SwitchStateChanged_6x05 last_switch_enabled_command; - PlayerInventoryItem next_drop_item; + ItemData next_drop_item; uint32_t next_item_id; struct LobbyPlayer { diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 39052ba6..6d56d86a 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -606,12 +606,12 @@ static void on_player_drop_item(shared_ptr c, uint8_t command, uint8_t f auto item = c->game_data.player()->remove_item(cmd.item_id, 0, c->version() != GameVersion::BB); l->add_item(item, cmd.area, cmd.x, cmd.z); - auto name = item.data.name(false); + auto name = item.name(false); 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 = item.data.name(true); + string name = item.name(true); send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s", cmd.item_id.load(), name.c_str()); } @@ -657,22 +657,20 @@ static void on_create_inventory_item_t(shared_ptr c, uint8_t command, ui auto l = c->require_lobby(); if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { - PlayerInventoryItem item; - item.present = 1; - item.flags = 0; - item.data = cmd.item_data; - if (c->version() == GameVersion::GC) { - item.data.bswap_data2_if_mag(); + { + ItemData item = cmd.item_data; + if (c->version() == GameVersion::GC) { + item.bswap_data2_if_mag(); + } + c->game_data.player()->add_item(item); } - c->game_data.player()->add_item(item); - auto name = item.data.name(false); + auto name = cmd.item_data.name(false); l->log.info("Player %hu created inventory item %08" PRIX32 " (%s)", cmd.header.client_id.load(), cmd.item_data.id.load(), name.c_str()); if (c->options.debug) { - string name = item.data.name(true); - send_text_message_printf(c, "$C5CREATE %08" PRIX32 "\n%s", - cmd.item_data.id.load(), name.c_str()); + string name = cmd.item_data.name(true); + send_text_message_printf(c, "$C5CREATE %08" PRIX32 "\n%s", cmd.item_data.id.load(), name.c_str()); } c->game_data.player()->print_inventory(stderr); } @@ -706,23 +704,21 @@ static void on_drop_partial_stack_t(shared_ptr c, uint8_t command, uint8 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.present = 1; - item.flags = 0; - item.data = cmd.item_data; - if (c->version() == GameVersion::GC) { - item.data.bswap_data2_if_mag(); + { + ItemData item = cmd.item_data; + if (c->version() == GameVersion::GC) { + item.bswap_data2_if_mag(); + } + l->add_item(item, cmd.area, cmd.x, cmd.z); } - l->add_item(item, cmd.area, cmd.x, cmd.z); - auto name = item.data.name(false); + auto name = cmd.item_data.name(false); 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.header.client_id.load(), cmd.item_data.id.load(), name.c_str(), cmd.area.load(), cmd.x.load(), cmd.z.load()); if (c->options.debug) { - string name = item.data.name(true); - send_text_message_printf(c, "$C5SPLIT %08" PRIX32 "\n%s", - item.data.id.load(), name.c_str()); + string name = cmd.item_data.name(true); + send_text_message_printf(c, "$C5SPLIT %08" PRIX32 "\n%s", cmd.item_data.id.load(), name.c_str()); } c->game_data.player()->print_inventory(stderr); } @@ -758,8 +754,8 @@ static void on_drop_partial_stack_bb(shared_ptr c, uint8_t command, uint // if a stack was split, the original item still exists, so the dropped item // needs a new ID. remove_item signals this by returning an item with id=-1 - if (item.data.id == 0xFFFFFFFF) { - item.data.id = l->generate_item_id(c->lobby_client_id); + if (item.id == 0xFFFFFFFF) { + item.id = l->generate_item_id(c->lobby_client_id); } // PSOBB sends a 6x29 command after it receives the 6x5D, so we need to add @@ -769,18 +765,18 @@ static void on_drop_partial_stack_bb(shared_ptr c, uint8_t command, uint l->add_item(item, cmd.area, cmd.x, cmd.z); - auto name = item.data.name(false); + auto name = item.name(false); 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 = item.data.name(true); + string name = item.name(true); send_text_message_printf(c, "$C5SPLIT/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); + send_drop_stacked_item(l, item, cmd.area, cmd.x, cmd.z); } else { forward_subcommand(c, command, flag, data, size); @@ -800,25 +796,22 @@ static void on_buy_shop_item(shared_ptr c, uint8_t command, uint8_t flag } if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { - PlayerInventoryItem item; - item.present = 1; - item.flags = 0; - item.data = cmd.item_data; - if (c->version() == GameVersion::GC) { - item.data.bswap_data2_if_mag(); + auto p = c->game_data.player(); + { + ItemData item = cmd.item_data; + if (c->version() == GameVersion::GC) { + item.bswap_data2_if_mag(); + } + p->add_item(item); } - auto p = c->game_data.player(); - p->add_item(item); - - size_t price = s->item_parameter_table->price_for_item(item.data); - auto name = item.data.name(false); + size_t price = s->item_parameter_table->price_for_item(cmd.item_data); + auto name = cmd.item_data.name(false); l->log.info("Player %hu bought item %08" PRIX32 " (%s) from shop (%zu Meseta)", - cmd.header.client_id.load(), item.data.id.load(), name.c_str(), price); + cmd.header.client_id.load(), cmd.item_data.id.load(), name.c_str(), price); if (c->options.debug) { - string name = item.data.name(true); - send_text_message_printf(c, "$C5BUY %08" PRIX32 "\n%s", - item.data.id.load(), name.c_str()); + string name = cmd.item_data.name(true); + send_text_message_printf(c, "$C5BUY %08" PRIX32 "\n%s", cmd.item_data.id.load(), name.c_str()); } p->remove_meseta(price, c->version() != GameVersion::BB); p->print_inventory(stderr); @@ -840,22 +833,20 @@ static void on_box_or_enemy_item_drop_t(shared_ptr c, uint8_t command, u } if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { - PlayerInventoryItem item; - item.present = 1; - item.flags = 0; - item.data = cmd.item_data; - if (c->version() == GameVersion::GC) { - item.data.bswap_data2_if_mag(); + { + ItemData item = cmd.item_data; + if (c->version() == GameVersion::GC) { + item.bswap_data2_if_mag(); + } + l->add_item(item, cmd.area, cmd.x, cmd.z); } - l->add_item(item, cmd.area, cmd.x, cmd.z); - auto name = item.data.name(false); + auto name = cmd.item_data.name(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()); + cmd.item_data.id.load(), name.c_str(), cmd.area, cmd.x.load(), cmd.z.load()); if (c->options.debug) { - string name = item.data.name(true); - send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s", - item.data.id.load(), name.c_str()); + string name = cmd.item_data.name(true); + send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s", cmd.item_data.id.load(), name.c_str()); } } @@ -893,13 +884,12 @@ static void on_pick_up_item(shared_ptr c, uint8_t command, uint8_t flag, auto item = l->remove_item(cmd.item_id); effective_c->game_data.player()->add_item(item); - auto name = item.data.name(false); + auto name = item.name(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 = item.data.name(true); - send_text_message_printf(c, "$C5PICK %08" PRIX32 "\n%s", - cmd.item_id.load(), name.c_str()); + string name = item.name(true); + send_text_message_printf(c, "$C5PICK %08" PRIX32 "\n%s", cmd.item_id.load(), name.c_str()); } effective_c->game_data.player()->print_inventory(stderr); } @@ -924,11 +914,11 @@ static void on_pick_up_item_request(shared_ptr c, uint8_t command, uint8 auto item = l->remove_item(cmd.item_id); c->game_data.player()->add_item(item); - auto name = item.data.name(false); + auto name = item.name(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 = item.data.name(true); + string name = item.name(true); send_text_message_printf(c, "$C5PICK/BB %08" PRIX32 "\n%s", cmd.item_id.load(), name.c_str()); } @@ -1122,8 +1112,7 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr c, uint c->game_data.player()->bank.meseta += cmd.meseta_amount; c->game_data.player()->disp.stats.meseta -= cmd.meseta_amount; } else { // item - auto item = c->game_data.player()->remove_item( - cmd.item_id, cmd.item_amount, c->version() != GameVersion::BB); + auto item = c->game_data.player()->remove_item(cmd.item_id, cmd.item_amount, c->version() != GameVersion::BB); c->game_data.player()->bank.add_item(item); send_destroy_item(c, cmd.item_id, cmd.item_amount); } @@ -1138,11 +1127,10 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr c, uint c->game_data.player()->bank.meseta -= cmd.meseta_amount; c->game_data.player()->disp.stats.meseta += cmd.meseta_amount; } else { // item - auto bank_item = c->game_data.player()->bank.remove_item(cmd.item_id, cmd.item_amount); - PlayerInventoryItem item = bank_item; - item.data.id = l->generate_item_id(0xFF); + auto item = c->game_data.player()->bank.remove_item(cmd.item_id, cmd.item_amount); + item.id = l->generate_item_id(0xFF); c->game_data.player()->add_item(item); - send_create_inventory_item(c, item.data); + send_create_inventory_item(c, item); } } @@ -1211,17 +1199,16 @@ static void on_entity_drop_item_request(shared_ptr c, uint8_t command, u cmd.ignore_def = true; } - PlayerInventoryItem item; if (!l->item_creator.get()) { throw runtime_error("received box drop subcommand without item creator present"); } + ItemData item; if (cmd.rt_index == 0x30) { if (cmd.ignore_def) { - item.data = l->item_creator->on_box_item_drop(cmd.area); + item = l->item_creator->on_box_item_drop(cmd.area); } else { - item.data = l->item_creator->on_specialized_box_item_drop( - cmd.def[0], cmd.def[1], cmd.def[2]); + item = l->item_creator->on_specialized_box_item_drop(cmd.def[0], cmd.def[1], cmd.def[2]); } } else { if (!l->map) { @@ -1233,14 +1220,14 @@ static void on_entity_drop_item_request(shared_ptr c, uint8_t command, u c->log.warning("rt_index %02hhX from command does not match entity\'s expected index %02" PRIX32, cmd.rt_index, expected_rt_index); } - item.data = l->item_creator->on_monster_item_drop(expected_rt_index, cmd.area); + item = l->item_creator->on_monster_item_drop(expected_rt_index, cmd.area); } - item.data.id = l->generate_item_id(0xFF); + item.id = l->generate_item_id(0xFF); if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { l->add_item(item, cmd.area, cmd.x, cmd.z); } - send_drop_item(l, item.data, cmd.rt_index != 0x30, cmd.area, cmd.x, cmd.z, cmd.entity_id); + send_drop_item(l, item, cmd.rt_index != 0x30, cmd.area, cmd.x, cmd.z, cmd.entity_id); } static void on_set_quest_flag(shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size) { @@ -1516,12 +1503,12 @@ void on_meseta_reward_request_bb(shared_ptr c, uint8_t, uint8_t, const v } else if (cmd.amount > 0) { auto l = c->require_lobby(); - PlayerInventoryItem item; - item.data.data1[0] = 0x04; - item.data.data2d = cmd.amount.load(); - item.data.id = l->generate_item_id(0xFF); + ItemData item; + item.data1[0] = 0x04; + item.data2d = cmd.amount.load(); + item.id = l->generate_item_id(0xFF); c->game_data.player()->add_item(item); - send_create_inventory_item(c, item.data); + send_create_inventory_item(c, item); } } @@ -1529,11 +1516,11 @@ void on_item_reward_request_bb(shared_ptr c, uint8_t, uint8_t, const voi const auto& cmd = check_size_t(data, size); auto l = c->require_lobby(); - PlayerInventoryItem item; - item.data = cmd.item_data; - item.data.id = l->generate_item_id(0xFF); + ItemData item; + item = cmd.item_data; + item.id = l->generate_item_id(0xFF); c->game_data.player()->add_item(item); - send_create_inventory_item(c, item.data); + send_create_inventory_item(c, item); } static void on_destroy_inventory_item(shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size) { @@ -1550,11 +1537,11 @@ static void on_destroy_inventory_item(shared_ptr c, uint8_t command, uin if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { auto item = c->game_data.player()->remove_item( cmd.item_id, cmd.amount, c->version() != GameVersion::BB); - auto name = item.data.name(false); + auto name = item.name(false); l->log.info("Inventory item %hu:%08" PRIX32 " destroyed (%s)", cmd.header.client_id.load(), cmd.item_id.load(), name.c_str()); if (c->options.debug) { - string name = item.data.name(true); + string name = item.name(true); send_text_message_printf(c, "$C5DESTROY %08" PRIX32 "\n%s", cmd.item_id.load(), name.c_str()); } @@ -1573,11 +1560,11 @@ static void on_destroy_ground_item(shared_ptr c, uint8_t command, uint8_ if (l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED) { auto item = l->remove_item(cmd.item_id); - auto name = item.data.name(false); + auto name = item.name(false); l->log.info("Ground item %08" PRIX32 " destroyed (%s)", cmd.item_id.load(), name.c_str()); if (c->options.debug) { - string name = item.data.name(true); + string name = item.name(true); send_text_message_printf(c, "$C5DESTROY/GND %08" PRIX32 "\n%s", cmd.item_id.load(), name.c_str()); } @@ -1606,10 +1593,9 @@ static void on_identify_item_bb(shared_ptr c, uint8_t command, uint8_t f auto p = c->game_data.player(); p->disp.stats.meseta -= 100; - c->game_data.identify_result = c->game_data.player()->inventory.items[x]; - c->game_data.identify_result.data.data1[4] &= 0x7F; - l->item_creator->apply_tekker_deltas( - c->game_data.identify_result.data, p->disp.visual.section_id); + c->game_data.identify_result = c->game_data.player()->inventory.items[x].data; + c->game_data.identify_result.data1[4] &= 0x7F; + l->item_creator->apply_tekker_deltas(c->game_data.identify_result, p->disp.visual.section_id); send_item_identify_result(c); } else { @@ -1626,14 +1612,14 @@ static void on_accept_identify_item_bb(shared_ptr c, uint8_t command, ui throw logic_error("item tracking not enabled in BB game"); } - if (!c->game_data.identify_result.data.id) { + if (!c->game_data.identify_result.id || (c->game_data.identify_result.id == 0xFFFFFFFF)) { throw runtime_error("no identify result present"); } - if (c->game_data.identify_result.data.id != cmd.item_id) { + if (c->game_data.identify_result.id != cmd.item_id) { throw runtime_error("accepted item ID does not match previous identify request"); } c->game_data.player()->add_item(c->game_data.identify_result); - send_create_inventory_item(c, c->game_data.identify_result.data); + send_create_inventory_item(c, c->game_data.identify_result); c->game_data.identify_result.clear(); } else { @@ -1653,15 +1639,15 @@ static void on_sell_item_at_shop_bb(shared_ptr c, uint8_t command, uint8 auto p = c->game_data.player(); auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != GameVersion::BB); - size_t price = (s->item_parameter_table->price_for_item(item.data) >> 3) * cmd.amount; + size_t price = (s->item_parameter_table->price_for_item(item) >> 3) * cmd.amount; p->add_meseta(price); - auto name = item.data.name(false); + auto name = item.name(false); l->log.info("Inventory item %hu:%08" PRIX32 " (%s) destroyed via sale (%zu Meseta)", c->lobby_client_id, cmd.item_id.load(), name.c_str(), price); c->game_data.player()->print_inventory(stderr); if (c->options.debug) { - string name = item.data.name(true); + string name = item.name(true); send_text_message_printf(c, "$C5DESTROY/SELL %08" PRIX32 "\n+%zu Meseta\n%s", cmd.item_id.load(), price, name.c_str()); } @@ -1678,29 +1664,29 @@ static void on_buy_shop_item_bb(shared_ptr c, uint8_t, uint8_t, const vo throw logic_error("item tracking not enabled in BB game"); } - PlayerInventoryItem item; - item.data = c->game_data.shop_contents.at(cmd.shop_type).at(cmd.item_index); - if (item.data.is_stackable()) { - item.data.data1[5] = cmd.amount; + ItemData item; + item = c->game_data.shop_contents.at(cmd.shop_type).at(cmd.item_index); + if (item.is_stackable()) { + item.data1[5] = cmd.amount; } else if (cmd.amount != 1) { throw runtime_error("item is not stackable"); } - size_t price = item.data.data2d * cmd.amount; - item.data.data2d = 0; + size_t price = item.data2d * cmd.amount; + item.data2d = 0; auto p = c->game_data.player(); p->remove_meseta(price, false); - item.data.id = cmd.inventory_item_id; + item.id = cmd.inventory_item_id; p->add_item(item); - send_create_inventory_item(c, item.data); + send_create_inventory_item(c, item); - auto name = item.data.name(false); + auto name = item.name(false); l->log.info("Inventory item %hu:%08" PRIX32 " created via purchase (%s) for %zu meseta", c->lobby_client_id, cmd.inventory_item_id.load(), name.c_str(), price); p->print_inventory(stderr); if (c->options.debug) { - string name = item.data.name(true); + string name = item.name(true); send_text_message_printf(c, "$C5CREATE/BUY %08" PRIX32 "\n-%zu Meseta\n%s", cmd.inventory_item_id.load(), price, name.c_str()); } diff --git a/src/SendCommands.cc b/src/SendCommands.cc index cd014f75..bf2c1446 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -2100,7 +2100,7 @@ void send_item_identify_result(shared_ptr c) { res.header.subcommand = 0xB9; res.header.size = sizeof(res) / 4; res.header.client_id = c->lobby_client_id; - res.item_data = c->game_data.identify_result.data; + res.item_data = c->game_data.identify_result; send_command_t(l, 0x60, 0x00, res); } @@ -2109,15 +2109,14 @@ void send_bank(shared_ptr c) { throw logic_error("6xBC can only be sent to BB clients"); } - vector items(c->game_data.player()->bank.items, - &c->game_data.player()->bank.items[c->game_data.player()->bank.num_items]); + const auto* items_it = c->game_data.player()->bank.items.data(); + vector items(items_it, items_it + c->game_data.player()->bank.num_items); - uint32_t checksum = random_object(); G_BankContentsHeader_BB_6xBC cmd = { - {{0xBC, 0, 0}, 0}, checksum, c->game_data.player()->bank.num_items, c->game_data.player()->bank.meseta}; - - size_t size = 8 + sizeof(cmd) + items.size() * sizeof(PlayerBankItem); - cmd.header.size = size; + {{0xBC, 0, 0}, sizeof(G_BankContentsHeader_BB_6xBC) + items.size() * sizeof(PlayerBankItem)}, + random_object(), + c->game_data.player()->bank.num_items, + c->game_data.player()->bank.meseta}; send_command_t_vt(c, 0x6C, 0x00, cmd, items); } diff --git a/src/ServerShell.cc b/src/ServerShell.cc index eed804ed..c320fa9b 100644 --- a/src/ServerShell.cc +++ b/src/ServerShell.cc @@ -819,21 +819,20 @@ Proxy session commands:\n\ throw runtime_error("proxy session is not game leader"); } - PlayerInventoryItem item; - item.data = ItemData(command_args); - item.data.id = random_object(); + ItemData item(command_args); + item.id = random_object(); if (command_name == "set-next-item") { session->next_drop_item = item; - string name = session->next_drop_item.data.name(true); + string name = session->next_drop_item.name(true); send_text_message(session->client_channel, u"$C7Next drop:\n" + decode_sjis(name)); } else { - send_drop_stacked_item(session->client_channel, item.data, session->area, session->x, session->z); - send_drop_stacked_item(session->server_channel, item.data, session->area, session->x, session->z); + send_drop_stacked_item(session->client_channel, item, session->area, session->x, session->z); + send_drop_stacked_item(session->server_channel, item, session->area, session->x, session->z); - string name = item.data.name(true); + string name = item.name(true); send_text_message(session->client_channel, u"$C7Item created:\n" + decode_sjis(name)); }