From 868815e65855089d7f83af40ff59e19e08db2ac8 Mon Sep 17 00:00:00 2001 From: neckkola <65987027+neckkola@users.noreply.github.com> Date: Sun, 6 Apr 2025 16:14:46 -0300 Subject: [PATCH] Start Trader with new unique_id --- common/eq_packet_structs.h | 36 ++++++-- common/item_instance.h | 1 + common/patches/rof2.cpp | 47 ++++++----- common/patches/rof2_structs.h | 14 +-- zone/client.h | 1 + zone/trading.cpp | 155 ++++++++++++++-------------------- 6 files changed, 127 insertions(+), 127 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index bcd8f1ad7..9c537759b 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -3780,12 +3780,36 @@ struct Trader2_Struct { std::string serial_number[EQ::invtype::BAZAAR_SIZE]; }; -struct ClickTrader2_Struct { - uint32 action; - uint32 unknown_004; - uint64 items[EQ::invtype::BAZAAR_SIZE]; - uint32 item_cost[EQ::invtype::BAZAAR_SIZE]; - std::string serial_number[EQ::invtype::BAZAAR_SIZE]; +struct BazaarTraderDetails { + uint64 item_id; + std::string unique_id; + uint64 cost; + uint64 serial_number; // backwards compatibility. Not used for RoF2 as of March 2025 + + template + void serialize(Archive &archive) + { + archive( + CEREAL_NVP(item_id), + CEREAL_NVP(unique_id), + CEREAL_NVP(cost), + CEREAL_NVP(serial_number) + ); + } +}; + +struct ClickTraderNew_Struct { + uint32 action; + std::vector items; + + template + void serialize(Archive &archive) + { + archive( + CEREAL_NVP(action), + CEREAL_NVP(items) + ); + } }; struct GetItems2_Struct { diff --git a/common/item_instance.h b/common/item_instance.h index e80f1f23c..d11020406 100644 --- a/common/item_instance.h +++ b/common/item_instance.h @@ -40,6 +40,7 @@ class EvolveInfo; // Stores information about an evolving item family #include "../common/deity.h" #include "../common/memory_buffer.h" #include "../common/repositories/character_evolving_items_repository.h" +#include #include diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index b5ede82cb..53da0937c 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -6167,30 +6167,31 @@ namespace RoF2 switch (action) { case structs::RoF2BazaarTraderBuyerActions::BeginTraderMode: { DECODE_LENGTH_EXACT(structs::BeginTrader_Struct); - SETUP_DIRECT_DECODE(ClickTrader2_Struct, structs::BeginTrader_Struct); + + unsigned char *eq_buffer = __packet->pBuffer; + auto eq = (RoF2::structs::BeginTrader_Struct *) eq_buffer; + + ClickTraderNew_Struct out{}; + out.action = TraderOn; + for (auto i = 0; i < RoF2::invtype::BAZAAR_SIZE; i++) { + BazaarTraderDetails btd{}; + btd.unique_id = eq->item_unique_ids[i].item_unique_id; + btd.cost = eq->item_cost[i]; + out.items.push_back(btd); + } + + std::stringstream ss{}; + cereal::BinaryOutputArchive ar(ss); + { + ar(out); + } + + __packet->size = static_cast(ss.str().length()); + __packet->pBuffer = new unsigned char[__packet->size]{}; + memcpy(__packet->pBuffer, ss.str().data(), __packet->size); + safe_delete_array(eq_buffer); + LogTrading("(RoF2) BeginTraderMode action [{}]", action); - - emu->action = TraderOn; - std::copy_n(eq->item_cost, RoF2::invtype::BAZAAR_SIZE, emu->item_cost); - std::transform( - std::begin(eq->items), - std::end(eq->items), - std::begin(emu->serial_number), - [&](const structs::TraderItemSerial_Struct x) { - return std::string(x.serial_number); - }); - //std::ranges::copy(eq->items->serial_number, emu->serial_number); - //std::copy_n(eq->items->serial_number, RoF2::invtype::BAZAAR_SIZE, emu->serial_number); - // std::transform( - // std::begin(eq->items), - // std::end(eq->items), - // std::begin(emu->serial_number), - // [&](const structs::TraderItemSerial_Struct x) { - // return Strings::ToUnsignedBigInt(x.serial_number,0); - // } - // ); - - FINISH_DIRECT_DECODE(); break; } case structs::RoF2BazaarTraderBuyerActions::EndTraderMode: { diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 92f0a1d90..034f97aa7 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -3554,19 +3554,21 @@ struct WhoAllPlayerPart4 { }; struct TraderItemSerial_Struct { - char serial_number[17]; + char item_unique_id[17]; uint8 unknown_018; - void operator=(const char* a) { - //auto _tmp = fmt::format("{:016}", a); - strn0cpy(this->serial_number, a, sizeof(this->serial_number)); + TraderItemSerial_Struct& operator=(const char* a) { + strn0cpy(this->item_unique_id, a, sizeof(this->item_unique_id)); + unknown_018 = 0; + + return *this; } }; struct BeginTrader_Struct { /*0000*/ uint32 action; -/*0004*/ TraderItemSerial_Struct items[200]; -/*3604*/ uint32 item_cost[200]; +/*0004*/ TraderItemSerial_Struct item_unique_ids[RoF2::invtype::BAZAAR_SIZE]; +/*3604*/ uint32 item_cost[RoF2::invtype::BAZAAR_SIZE]; /*4404*/ }; diff --git a/zone/client.h b/zone/client.h index b2ec5483c..3579e05a7 100644 --- a/zone/client.h +++ b/zone/client.h @@ -374,6 +374,7 @@ public: uint16 FindTraderItem(std::string &SerialNumber,uint16 Quantity); uint32 FindTraderItemSerialNumber(int32 ItemID); EQ::ItemInstance* FindTraderItemBySerialNumber(std::string &serial_number); + EQ::ItemInstance* FindTraderItemByUniqueID(std::string &unique_id); void FindAndNukeTraderItem(std::string &serial_number, int16 quantity, Client* customer, uint16 trader_slot); void NukeTraderItem(uint16 slot, int16 charges, int16 quantity, Client* customer, uint16 trader_slot, const std::string &serial_number, int32 item_id = 0); void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges, uint32 itemid = 0); diff --git a/zone/trading.cpp b/zone/trading.cpp index fed062a1a..55a8a0072 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -782,23 +782,6 @@ void Client::TraderShowItems() auto outapp = new EQApplicationPacket(OP_Trader, packet_size); memcpy(outapp->pBuffer, ss.str().data(), packet_size); - // // uint32 item_limit = trader_items.size() >= GetInv().GetLookup()->InventoryTypeSize.Bazaar ? - // // GetInv().GetLookup()->InventoryTypeSize.Bazaar : - // // trader_items.size(); - // uint32 item_limit = GetInv().GetLookup()->InventoryTypeSize.Bazaar; - // //FIX - // - // for (int i = 0; i < trader_items.size(); i++) { - // data->items[i].cost = trader_items.at(i).item_cost; - // strn0cpy(data->items[i].sn, trader_items.at(i).item_sn.data(), sizeof(data->items[i].sn[i])); - // } - // - // for (int i = trader_items.size(); i < item_limit; i++) { - // data->items[i].cost = 0; - // strn0cpy(data->items[i].sn, "0000000000000000", sizeof(data->items[i].sn)); - // } - // - // data->action = ListTraderItems; QueuePacket(outapp); safe_delete(outapp); @@ -841,40 +824,20 @@ void Client::Trader_CustomerBrowsing(Client *Customer) void Client::TraderStartTrader(const EQApplicationPacket *app) { uint32 max_items = GetInv().GetLookup()->InventoryTypeSize.Bazaar; - auto in = (ClickTrader2_Struct *) app->pBuffer; auto inv = GetTraderItems(); bool trade_items_valid = true; std::vector trader_items{}; + ClickTraderNew_Struct in; - //Check inventory for no-trade items - // for (auto i = 0; i < max_items; i++) { - // if (inv->items[i] == 0 || inv->serial_number[i].empty()) { - // continue; - // } - // - // auto inst = FindTraderItemBySerialNumber(inv->serial_number[i]); - // if (inst) { - // if (inst->GetItem() && inst->GetItem()->NoDrop == 0) { - // Message( - // Chat::Red, - // fmt::format( - // "Item: {} is NODROP and found in a Trader's Satchel. Please remove and restart trader mode", - // inst->GetItem()->Name - // ).c_str() - // ); - // TraderEndTrader(); - // safe_delete(inv); - // return; - // } - // } - // } + EQ::Util::MemoryStreamReader ss(reinterpret_cast(app->pBuffer), app->size); + cereal::BinaryInputArchive ar(ss); + { + ar(in); + } - for (uint32 i = 0; i < max_items; i++) { - if (inv->items[i] == 0 || inv->serial_number[i].empty()) { - continue; - } - - auto const inst = FindTraderItemBySerialNumber(inv->serial_number[i]); + uint32 slot_id = 0; + for (auto &i: in.items) { + auto const inst = FindTraderItemByUniqueID(i.unique_id); if (!inst) { trade_items_valid = false; break; @@ -886,58 +849,41 @@ void Client::TraderStartTrader(const EQApplicationPacket *app) Chat::Red, fmt::format( "Item: {} is NODROP and found in a Trader's Satchel. Please remove and restart trader mode", - inst->GetItem()->Name - ).c_str() - ); + inst->GetItem()->Name) + .c_str()); TraderEndTrader(); safe_delete(inv); return; } } - auto it = std::find(std::begin(in->serial_number), std::end(in->serial_number), inv->serial_number[i]); - if (inst && it != std::end(in->serial_number)) { - inst->SetPrice(in->item_cost[i]); - TraderRepository::Trader trader_item{}; + TraderRepository::Trader trader_item{}; - trader_item.id = 0; - trader_item.char_entity_id = GetID(); - trader_item.char_id = CharacterID(); - trader_item.char_zone_id = GetZoneID(); - trader_item.char_zone_instance_id = GetInstanceID(); - trader_item.item_charges = inst->GetCharges() == 0 ? 1 : inst->GetCharges(); - trader_item.item_cost = inst->GetPrice(); - trader_item.item_id = inst->GetID(); - trader_item.item_sn = in->serial_number[i]; - trader_item.slot_id = i; - trader_item.listing_date = time(nullptr); - if (inst->IsAugmented()) { - auto augs = inst->GetAugmentIDs(); - trader_item.aug_slot_1 = augs.at(0); - trader_item.aug_slot_2 = augs.at(1); - trader_item.aug_slot_3 = augs.at(2); - trader_item.aug_slot_4 = augs.at(3); - trader_item.aug_slot_5 = augs.at(4); - trader_item.aug_slot_6 = augs.at(5); - } + trader_item.id = 0; + trader_item.char_entity_id = GetID(); + trader_item.char_id = CharacterID(); + trader_item.char_zone_id = GetZoneID(); + trader_item.char_zone_instance_id = GetInstanceID(); + trader_item.item_charges = inst->GetCharges() == 0 ? 1 : inst->GetCharges(); + trader_item.item_cost = i.cost; + trader_item.item_id = inst->GetID(); + trader_item.item_sn = i.unique_id; + trader_item.slot_id = slot_id; + trader_item.listing_date = time(nullptr); + if (inst->IsAugmented()) { + auto augs = inst->GetAugmentIDs(); + trader_item.aug_slot_1 = augs.at(0); + trader_item.aug_slot_2 = augs.at(1); + trader_item.aug_slot_3 = augs.at(2); + trader_item.aug_slot_4 = augs.at(3); + trader_item.aug_slot_5 = augs.at(4); + trader_item.aug_slot_6 = augs.at(5); + } - trader_items.emplace_back(trader_item); - continue; - } - else if (inst) { - Message( - Chat::Red, - fmt::format( - "Item: {} has no price set. Please set a price and try again.", - inst->GetItem()->Name - ).c_str() - ); - trade_items_valid = false; - continue; - } + trader_items.emplace_back(trader_item); } - if (!trade_items_valid) { + if (!trade_items_valid || trader_items.empty()) { Message(Chat::Red, "You are not able to become a trader at this time. Invalid item found."); TraderEndTrader(); safe_delete(inv); @@ -952,7 +898,7 @@ void Client::TraderStartTrader(const EQApplicationPacket *app) if (ClientVersion() >= EQ::versions::ClientVersion::RoF) { auto outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderStatus_Struct)); auto data = (TraderStatus_Struct *) outapp->pBuffer; - data->Code = TraderAck2; + data->Code = TraderAck2; QueuePacket(outapp); safe_delete(outapp); } @@ -1105,7 +1051,7 @@ uint32 Client::FindTraderItemSerialNumber(int32 ItemID) { return 0; } -EQ::ItemInstance *Client::FindTraderItemBySerialNumber(std::string &serial_number) +EQ::ItemInstance *Client::FindTraderItemBySerialNumber(std::string &unique_id) { EQ::ItemInstance *item = nullptr; int16 slot_id = 0; @@ -1118,7 +1064,7 @@ EQ::ItemInstance *Client::FindTraderItemBySerialNumber(std::string &serial_numbe slot_id = EQ::InventoryProfile::CalcSlotId(i, x); item = GetInv().GetItem(slot_id); if (item) { - if (item->GetUniqueID().compare(serial_number) == 0) { + if (item->GetUniqueID().compare(unique_id) == 0) { return item; } } @@ -1126,11 +1072,36 @@ EQ::ItemInstance *Client::FindTraderItemBySerialNumber(std::string &serial_numbe } } - LogTrading("Couldn't find item! Serial No. was [{}]", serial_number); + LogTrading("Couldn't find item! Serial No. was [{}]", unique_id); return nullptr; } +EQ::ItemInstance *Client::FindTraderItemByUniqueID(std::string &unique_id) +{ + EQ::ItemInstance *item = nullptr; + int16 slot_id = 0; + + for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) { + item = GetInv().GetItem(i); + if (item && item->GetItem()->BagType == EQ::item::BagTypeTradersSatchel) { + for (int16 x = EQ::invbag::SLOT_BEGIN; x <= EQ::invbag::SLOT_END; x++) { + // we already have the parent bag and a contents iterator..why not just iterate the bag!?? + slot_id = EQ::InventoryProfile::CalcSlotId(i, x); + item = GetInv().GetItem(slot_id); + if (item) { + if (item->GetUniqueID().compare(unique_id) == 0) { + return item; + } + } + } + } + } + + LogTrading("Couldn't find item! Serial No. was [{}]", unique_id); + + return nullptr; +} GetItems2_Struct *Client::GetTraderItems() {