Start Trader with new unique_id

This commit is contained in:
neckkola 2025-04-06 16:14:46 -03:00 committed by Mitch Freeman
parent 69cfa3cdbe
commit 868815e658
6 changed files with 127 additions and 127 deletions

View File

@ -3780,12 +3780,36 @@ struct Trader2_Struct {
std::string serial_number[EQ::invtype::BAZAAR_SIZE]; std::string serial_number[EQ::invtype::BAZAAR_SIZE];
}; };
struct ClickTrader2_Struct { struct BazaarTraderDetails {
uint32 action; uint64 item_id;
uint32 unknown_004; std::string unique_id;
uint64 items[EQ::invtype::BAZAAR_SIZE]; uint64 cost;
uint32 item_cost[EQ::invtype::BAZAAR_SIZE]; uint64 serial_number; // backwards compatibility. Not used for RoF2 as of March 2025
std::string serial_number[EQ::invtype::BAZAAR_SIZE];
template<class Archive>
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<BazaarTraderDetails> items;
template<class Archive>
void serialize(Archive &archive)
{
archive(
CEREAL_NVP(action),
CEREAL_NVP(items)
);
}
}; };
struct GetItems2_Struct { struct GetItems2_Struct {

View File

@ -40,6 +40,7 @@ class EvolveInfo; // Stores information about an evolving item family
#include "../common/deity.h" #include "../common/deity.h"
#include "../common/memory_buffer.h" #include "../common/memory_buffer.h"
#include "../common/repositories/character_evolving_items_repository.h" #include "../common/repositories/character_evolving_items_repository.h"
#include <thread>
#include <map> #include <map>

View File

@ -6167,30 +6167,31 @@ namespace RoF2
switch (action) { switch (action) {
case structs::RoF2BazaarTraderBuyerActions::BeginTraderMode: { case structs::RoF2BazaarTraderBuyerActions::BeginTraderMode: {
DECODE_LENGTH_EXACT(structs::BeginTrader_Struct); 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<uint32>(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 <green>[{}]", action); LogTrading("(RoF2) BeginTraderMode action <green>[{}]", 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; break;
} }
case structs::RoF2BazaarTraderBuyerActions::EndTraderMode: { case structs::RoF2BazaarTraderBuyerActions::EndTraderMode: {

View File

@ -3554,19 +3554,21 @@ struct WhoAllPlayerPart4 {
}; };
struct TraderItemSerial_Struct { struct TraderItemSerial_Struct {
char serial_number[17]; char item_unique_id[17];
uint8 unknown_018; uint8 unknown_018;
void operator=(const char* a) { TraderItemSerial_Struct& operator=(const char* a) {
//auto _tmp = fmt::format("{:016}", a); strn0cpy(this->item_unique_id, a, sizeof(this->item_unique_id));
strn0cpy(this->serial_number, a, sizeof(this->serial_number)); unknown_018 = 0;
return *this;
} }
}; };
struct BeginTrader_Struct { struct BeginTrader_Struct {
/*0000*/ uint32 action; /*0000*/ uint32 action;
/*0004*/ TraderItemSerial_Struct items[200]; /*0004*/ TraderItemSerial_Struct item_unique_ids[RoF2::invtype::BAZAAR_SIZE];
/*3604*/ uint32 item_cost[200]; /*3604*/ uint32 item_cost[RoF2::invtype::BAZAAR_SIZE];
/*4404*/ /*4404*/
}; };

View File

@ -374,6 +374,7 @@ public:
uint16 FindTraderItem(std::string &SerialNumber,uint16 Quantity); uint16 FindTraderItem(std::string &SerialNumber,uint16 Quantity);
uint32 FindTraderItemSerialNumber(int32 ItemID); uint32 FindTraderItemSerialNumber(int32 ItemID);
EQ::ItemInstance* FindTraderItemBySerialNumber(std::string &serial_number); 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 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 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); void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges, uint32 itemid = 0);

View File

@ -782,23 +782,6 @@ void Client::TraderShowItems()
auto outapp = new EQApplicationPacket(OP_Trader, packet_size); auto outapp = new EQApplicationPacket(OP_Trader, packet_size);
memcpy(outapp->pBuffer, ss.str().data(), 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); QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
@ -841,40 +824,20 @@ void Client::Trader_CustomerBrowsing(Client *Customer)
void Client::TraderStartTrader(const EQApplicationPacket *app) void Client::TraderStartTrader(const EQApplicationPacket *app)
{ {
uint32 max_items = GetInv().GetLookup()->InventoryTypeSize.Bazaar; uint32 max_items = GetInv().GetLookup()->InventoryTypeSize.Bazaar;
auto in = (ClickTrader2_Struct *) app->pBuffer;
auto inv = GetTraderItems(); auto inv = GetTraderItems();
bool trade_items_valid = true; bool trade_items_valid = true;
std::vector<TraderRepository::Trader> trader_items{}; std::vector<TraderRepository::Trader> trader_items{};
ClickTraderNew_Struct in;
//Check inventory for no-trade items EQ::Util::MemoryStreamReader ss(reinterpret_cast<char *>(app->pBuffer), app->size);
// for (auto i = 0; i < max_items; i++) { cereal::BinaryInputArchive ar(ss);
// if (inv->items[i] == 0 || inv->serial_number[i].empty()) { {
// continue; ar(in);
// } }
//
// 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;
// }
// }
// }
for (uint32 i = 0; i < max_items; i++) { uint32 slot_id = 0;
if (inv->items[i] == 0 || inv->serial_number[i].empty()) { for (auto &i: in.items) {
continue; auto const inst = FindTraderItemByUniqueID(i.unique_id);
}
auto const inst = FindTraderItemBySerialNumber(inv->serial_number[i]);
if (!inst) { if (!inst) {
trade_items_valid = false; trade_items_valid = false;
break; break;
@ -886,58 +849,41 @@ void Client::TraderStartTrader(const EQApplicationPacket *app)
Chat::Red, Chat::Red,
fmt::format( fmt::format(
"Item: {} is NODROP and found in a Trader's Satchel. Please remove and restart trader mode", "Item: {} is NODROP and found in a Trader's Satchel. Please remove and restart trader mode",
inst->GetItem()->Name inst->GetItem()->Name)
).c_str() .c_str());
);
TraderEndTrader(); TraderEndTrader();
safe_delete(inv); safe_delete(inv);
return; return;
} }
} }
auto it = std::find(std::begin(in->serial_number), std::end(in->serial_number), inv->serial_number[i]); TraderRepository::Trader trader_item{};
if (inst && it != std::end(in->serial_number)) {
inst->SetPrice(in->item_cost[i]);
TraderRepository::Trader trader_item{};
trader_item.id = 0; trader_item.id = 0;
trader_item.char_entity_id = GetID(); trader_item.char_entity_id = GetID();
trader_item.char_id = CharacterID(); trader_item.char_id = CharacterID();
trader_item.char_zone_id = GetZoneID(); trader_item.char_zone_id = GetZoneID();
trader_item.char_zone_instance_id = GetInstanceID(); trader_item.char_zone_instance_id = GetInstanceID();
trader_item.item_charges = inst->GetCharges() == 0 ? 1 : inst->GetCharges(); trader_item.item_charges = inst->GetCharges() == 0 ? 1 : inst->GetCharges();
trader_item.item_cost = inst->GetPrice(); trader_item.item_cost = i.cost;
trader_item.item_id = inst->GetID(); trader_item.item_id = inst->GetID();
trader_item.item_sn = in->serial_number[i]; trader_item.item_sn = i.unique_id;
trader_item.slot_id = i; trader_item.slot_id = slot_id;
trader_item.listing_date = time(nullptr); trader_item.listing_date = time(nullptr);
if (inst->IsAugmented()) { if (inst->IsAugmented()) {
auto augs = inst->GetAugmentIDs(); auto augs = inst->GetAugmentIDs();
trader_item.aug_slot_1 = augs.at(0); trader_item.aug_slot_1 = augs.at(0);
trader_item.aug_slot_2 = augs.at(1); trader_item.aug_slot_2 = augs.at(1);
trader_item.aug_slot_3 = augs.at(2); trader_item.aug_slot_3 = augs.at(2);
trader_item.aug_slot_4 = augs.at(3); trader_item.aug_slot_4 = augs.at(3);
trader_item.aug_slot_5 = augs.at(4); trader_item.aug_slot_5 = augs.at(4);
trader_item.aug_slot_6 = augs.at(5); trader_item.aug_slot_6 = augs.at(5);
} }
trader_items.emplace_back(trader_item); 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;
}
} }
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."); Message(Chat::Red, "You are not able to become a trader at this time. Invalid item found.");
TraderEndTrader(); TraderEndTrader();
safe_delete(inv); safe_delete(inv);
@ -952,7 +898,7 @@ void Client::TraderStartTrader(const EQApplicationPacket *app)
if (ClientVersion() >= EQ::versions::ClientVersion::RoF) { if (ClientVersion() >= EQ::versions::ClientVersion::RoF) {
auto outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderStatus_Struct)); auto outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderStatus_Struct));
auto data = (TraderStatus_Struct *) outapp->pBuffer; auto data = (TraderStatus_Struct *) outapp->pBuffer;
data->Code = TraderAck2; data->Code = TraderAck2;
QueuePacket(outapp); QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
} }
@ -1105,7 +1051,7 @@ uint32 Client::FindTraderItemSerialNumber(int32 ItemID) {
return 0; return 0;
} }
EQ::ItemInstance *Client::FindTraderItemBySerialNumber(std::string &serial_number) EQ::ItemInstance *Client::FindTraderItemBySerialNumber(std::string &unique_id)
{ {
EQ::ItemInstance *item = nullptr; EQ::ItemInstance *item = nullptr;
int16 slot_id = 0; int16 slot_id = 0;
@ -1118,7 +1064,7 @@ EQ::ItemInstance *Client::FindTraderItemBySerialNumber(std::string &serial_numbe
slot_id = EQ::InventoryProfile::CalcSlotId(i, x); slot_id = EQ::InventoryProfile::CalcSlotId(i, x);
item = GetInv().GetItem(slot_id); item = GetInv().GetItem(slot_id);
if (item) { if (item) {
if (item->GetUniqueID().compare(serial_number) == 0) { if (item->GetUniqueID().compare(unique_id) == 0) {
return item; 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; 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() GetItems2_Struct *Client::GetTraderItems()
{ {