[Feature] Add RoF2 Bazaar Support (#4315)

* Add RoF2 Bazaar Support

Enable RoF2 bazaar features

* Add augments to Trader Items

* Cleanup

Cleanup of formatting and unused functions

* Update PlayerProfile for correct char_id in trader transactions.  Further cleanup.

* Add parcel delivery price functionality

Add parcel delivery price functionality via rules and new delivery cost struct.

* Add RoF support for bazaar window outside of bazaar with parcel delivery

* Further Testing and ActiveTransaction added

Further testing and a few fixes and messages added.  Add active transaction check to ensure two clients cannot purchase from the bazaar window at the same time

* Cleanup and Formatting updates

Cleanup and Formatting updates

* Update database manifest for the trader table against default peq trader table

* Logs and formatting

* Update bazaarsearch to be content_db aware

* Fix crash

* Simplify search

* Search fixes

* Push up more search logging

* More search fixes

* Formatting

* Update trader_repository.h

* Add Rule for Bazaar Parcel Delivery

Add a rule Bazaar:EnableParcelDelivery to enable/disable bazaar parcel delivery.  Default is True.

* Fix crash

* Update Bazaar Search

Adds/Tested bazaar search with move to content_db
- race, class, money, number of returned items, stats, name, slot, level, traders, local traders, specific trader.
Outstanding
- type, more stats to add (heroic, etc)

* Formatting

* Push

* Update bazaarsearch to include all stats that are available in RoF2

* Update BazaarSearch

Updates the bazaar search for item types.  They should be working as per RoF2+ types.

* Formatting

* Final updates to BazaarSearch

Add search by augmentation slots available on the item.
This enables all but Prestige, which I believe are not implemented yet.

* Add Titanium functionality correct ItemType Search

Add Titanium /trader /bazaar functionality.
Added itemtype=armor bazaar search.  It was missed in the search work

* Close off for loops

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
Mitch Freeman
2024-05-26 17:38:25 -03:00
committed by GitHub
parent d767217461
commit fc79614fac
51 changed files with 4793 additions and 2258 deletions
+558 -246
View File
@@ -398,51 +398,182 @@ namespace RoF2
EQApplicationPacket *in = *p;
*p = nullptr;
char *Buffer = (char *)in->pBuffer;
uint32 action = *(uint32 *) in->pBuffer;
uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer);
switch (action) {
case BazaarSearch: {
LogTrading("(RoF2) BazaarSearch action <green>[{}]", action);
std::vector<BazaarSearchResultsFromDB_Struct> results{};
auto bsms = (BazaarSearchMessaging_Struct *) in->pBuffer;
EQ::Util::MemoryStreamReader ss(
reinterpret_cast<char *>(bsms->payload),
in->size - sizeof(BazaarSearchMessaging_Struct)
);
cereal::BinaryInputArchive ar(ss);
ar(results);
if (SubAction != BazaarSearchResults)
{
dest->FastQueuePacket(&in, ack_req);
return;
auto name_size = 0;
for (auto const &i: results) {
name_size += i.item_name.length() + 1;
}
auto p_size = 41 * results.size() + name_size + 14;
auto buffer = std::make_unique<char[]>(p_size);
auto bufptr = buffer.get();
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 0);
VARSTRUCT_ENCODE_TYPE(uint16, bufptr, results[0].trader_zone_id);
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, results[0].trader_id);
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, results.size());
for (auto i: results) {
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.trader_id); //trader ID
VARSTRUCT_ENCODE_STRING(bufptr, i.serial_number_RoF.c_str()); //serial
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.cost); //cost
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.stackable ? i.charges : i.count); //quantity
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.item_id); //ID
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.icon_id); //icon
VARSTRUCT_ENCODE_STRING(bufptr, i.item_name.c_str()); //name
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.item_stat); //itemstat
}
safe_delete(in->pBuffer);
in->size = p_size;
in->pBuffer = (uchar *) buffer.get();
dest->QueuePacket(in);
break;
}
case BazaarInspect: {
LogTrading("(RoF2) BazaarInspect action <green>[{}]", action);
dest->FastQueuePacket(&in, ack_req);
break;
}
case WelcomeMessage: {
auto buffer = std::make_unique<char[]>(sizeof(structs::BazaarWelcome_Struct));
auto emu = (BazaarWelcome_Struct *) in->pBuffer;
auto eq = (structs::BazaarWelcome_Struct *) buffer.get();
eq->action = structs::RoF2BazaarTraderBuyerActions::WelcomeMessage;
eq->num_of_traders = emu->traders;
eq->num_of_items = emu->items;
safe_delete(in->pBuffer);
in->SetOpcode(OP_TraderShop);
in->size = sizeof(structs::BazaarWelcome_Struct);
in->pBuffer = (uchar *) buffer.get();
LogTrading("(RoF2) WelcomeMessage action <green>[{}]", action);
dest->QueuePacket(in);
break;
}
case DeliveryCostUpdate: {
auto data = (BazaarDeliveryCost_Struct *) in->pBuffer;
LogTrading("(RoF2) Delivery costs updated: vouchers <green>[{}] parcel percentage <green>[{}]",
data->voucher_delivery_cost,
data->parcel_deliver_cost
);
data->action = 0;
dest->FastQueuePacket(&in);
break;
}
default: {
LogTrading("(RoF2) Unhandled action <red>[{}]", action);
dest->FastQueuePacket(&in, ack_req);
}
}
}
unsigned char *__emu_buffer = in->pBuffer;
ENCODE(OP_BecomeTrader)
{
EQApplicationPacket *inapp = *p;
*p = nullptr;
BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer;
unsigned char *__emu_buffer = inapp->pBuffer;
auto in = (BecomeTrader_Struct *) __emu_buffer;
int EntryCount = in->size / sizeof(BazaarSearchResults_Struct);
switch (in->action) {
case TraderOff: {
auto emu = (BecomeTrader_Struct *) __emu_buffer;
if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0)
{
LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct));
delete in;
return;
auto outapp = new EQApplicationPacket(OP_BecomeTrader, sizeof(structs::BecomeTrader_Struct));
auto eq = (structs::BecomeTrader_Struct *) outapp->pBuffer;
eq->action = TraderOff;
eq->entity_id = emu->entity_id;
LogTrading(
"(RoF2) TraderOff action <green>[{}] for entity_id <green>[{}]",
eq->action,
eq->entity_id
);
dest->FastQueuePacket(&outapp);
break;
}
case TraderOn: {
auto emu = (BecomeTrader_Struct *) __emu_buffer;
auto outapp = new EQApplicationPacket(OP_BecomeTrader, sizeof(structs::BecomeTrader_Struct));
auto eq = (structs::BecomeTrader_Struct *) outapp->pBuffer;
eq->action = TraderOn;
eq->entity_id = emu->entity_id;
LogTrading(
"(RoF2) TraderOn action <green>[{}] for entity_id <green>[{}]",
eq->action,
eq->entity_id
);
dest->FastQueuePacket(&outapp);
break;
}
case AddTraderToBazaarWindow: {
auto emu = (BecomeTrader_Struct *) __emu_buffer;
auto outapp = new EQApplicationPacket(OP_TraderShop, sizeof(BecomeTrader_Struct));
auto eq = (BecomeTrader_Struct *) outapp->pBuffer;
eq->action = emu->action;
eq->entity_id = emu->entity_id;
eq->trader_id = emu->trader_id;
eq->zone_id = emu->zone_id;
strn0cpy(eq->trader_name, emu->trader_name, sizeof(eq->trader_name));
LogTrading(
"(RoF2) AddTraderToBazaarWindow action <green>[{}] trader_id <green>[{}] entity_id <green>[{}] zone_id <green>[{}]",
eq->action,
eq->entity_id,
eq->trader_id,
eq->zone_id
);
dest->FastQueuePacket(&outapp);
break;
}
case RemoveTraderFromBazaarWindow: {
auto emu = (BecomeTrader_Struct *) __emu_buffer;
auto outapp = new EQApplicationPacket(OP_TraderShop, sizeof(structs::BazaarWindowRemoveTrader_Struct));
auto eq = (structs::BazaarWindowRemoveTrader_Struct *) outapp->pBuffer;
eq->action = emu->action;
eq->trader_id = emu->trader_id;
LogTrading(
"(RoF2) RemoveTraderFromBazaarWindow action <green>[{}] for entity_id <green>[{}]",
eq->action,
eq->trader_id
);
dest->FastQueuePacket(&outapp);
break;
}
default: {
LogTrading(
"(RoF2) Unhandled action <red>[{}]",
in->action
);
dest->QueuePacket(inapp);
}
}
in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct);
in->pBuffer = new unsigned char[in->size];
memset(in->pBuffer, 0, in->size);
structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer;
for (int i = 0; i < EntryCount; ++i, ++emu, ++eq)
{
OUT(Beginning.Action);
OUT(SellerID);
memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName));
OUT(NumItems);
OUT(ItemID);
OUT(SerialNumber);
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
OUT(Cost);
OUT(ItemStat);
}
delete[] __emu_buffer;
dest->FastQueuePacket(&in, ack_req);
safe_delete(inapp);
}
ENCODE(OP_BeginCast)
@@ -2591,7 +2722,7 @@ namespace RoF2
outapp->WriteUInt8(0); // Unknown
}
outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(emu->char_id); // character_id
outapp->WriteUInt8(emu->leadAAActive);
@@ -3586,54 +3717,146 @@ namespace RoF2
ENCODE(OP_Trader)
{
if ((*p)->size == sizeof(ClickTrader_Struct))
{
ENCODE_LENGTH_EXACT(ClickTrader_Struct);
SETUP_DIRECT_ENCODE(ClickTrader_Struct, structs::ClickTrader_Struct);
uint32 action = *(uint32 *) (*p)->pBuffer;
eq->Code = emu->Code;
// Live actually has 200 items now, but 80 is the most our internal struct supports
for (uint32 i = 0; i < 200; i++)
{
eq->items[i].Unknown18 = 0;
if (i < 80) {
snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016" PRId64, emu->SerialNumber[i]);
eq->ItemCost[i] = emu->ItemCost[i];
}
else {
snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016d", 0);
eq->ItemCost[i] = 0;
}
switch (action) {
case TraderOn: {
ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct);
SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
eq->action = structs::RoF2BazaarTraderBuyerActions::BeginTraderMode;
OUT(entity_id);
LogTrading("(RoF2) TraderOn action <green>[{}] entity_id <green>[{}]", action, eq->entity_id);
FINISH_ENCODE();
break;
}
case TraderOff: {
ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct);
SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
FINISH_ENCODE();
}
else if ((*p)->size == sizeof(Trader_ShowItems_Struct))
{
ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct);
SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
eq->action = structs::RoF2BazaarTraderBuyerActions::EndTraderMode;
OUT(entity_id);
eq->Code = emu->Code;
//strncpy(eq->SerialNumber, "0000000000000000", sizeof(eq->SerialNumber));
//snprintf(eq->SerialNumber, sizeof(eq->SerialNumber), "%016d", 0);
eq->TraderID = emu->TraderID;
//eq->Stacksize = 0;
//eq->Price = 0;
LogTrading("(RoF2) TraderOff action <green>[{}] entity_id <green>[{}]", action, eq->entity_id);
FINISH_ENCODE();
break;
}
case ListTraderItems: {
ENCODE_LENGTH_EXACT(Trader_Struct);
SETUP_DIRECT_ENCODE(Trader_Struct, structs::ClickTrader_Struct);
LogTrading("(RoF2) action <green>[{}]", action);
FINISH_ENCODE();
}
else if ((*p)->size == sizeof(TraderStatus_Struct))
{
ENCODE_LENGTH_EXACT(TraderStatus_Struct);
SETUP_DIRECT_ENCODE(TraderStatus_Struct, structs::TraderStatus_Struct);
eq->action = structs::RoF2BazaarTraderBuyerActions::ListTraderItems;
std::transform(
std::begin(emu->items),
std::end(emu->items),
std::begin(eq->items),
[&](const uint32 x) {
return x;
}
);
std::copy_n(
std::begin(emu->item_cost),
EQ::invtype::BAZAAR_SIZE,
std::begin(eq->item_cost)
);
eq->Code = emu->Code;
FINISH_ENCODE();
break;
}
case TraderAck2: {
LogTrading("(RoF2) TraderAck2 action");
EQApplicationPacket *in = *p;
*p = nullptr;
FINISH_ENCODE();
}
else if ((*p)->size == sizeof(TraderBuy_Struct))
{
ENCODE_FORWARD(OP_TraderBuy);
dest->FastQueuePacket(&in);
break;
}
case PriceUpdate: {
SETUP_DIRECT_ENCODE(TraderPriceUpdate_Struct, structs::TraderPriceUpdate_Struct);
switch (emu->SubAction) {
case BazaarPriceChange_AddItem: {
auto outapp = std::make_unique<EQApplicationPacket>(
OP_Trader,
sizeof(structs::TraderStatus_Struct)
);
auto data = (structs::TraderStatus_Struct *) outapp->pBuffer;
data->action = emu->Action;
data->sub_action = BazaarPriceChange_AddItem;
LogTrading(
"(RoF2) PriceUpdate action <green>[{}] AddItem subaction <yellow>[{}]",
data->action,
data->sub_action
);
dest->QueuePacket(outapp.get());
break;
}
case BazaarPriceChange_RemoveItem: {
auto outapp = std::make_unique<EQApplicationPacket>(
OP_Trader,
sizeof(structs::TraderStatus_Struct)
);
auto data = (structs::TraderStatus_Struct *) outapp->pBuffer;
data->action = emu->Action;
data->sub_action = BazaarPriceChange_RemoveItem;
LogTrading(
"(RoF2) PriceUpdate action <green>[{}] RemoveItem subaction <yellow>[{}]",
data->action,
data->sub_action
);
dest->QueuePacket(outapp.get());
break;
}
case BazaarPriceChange_UpdatePrice: {
auto outapp = std::make_unique<EQApplicationPacket>(
OP_Trader,
sizeof(structs::TraderStatus_Struct)
);
auto data = (structs::TraderStatus_Struct *) outapp->pBuffer;
data->action = emu->Action;
data->sub_action = BazaarPriceChange_UpdatePrice;
LogTrading(
"(RoF2) PriceUpdate action <green>[{}] UpdatePrice subaction <yellow>[{}]",
data->action,
data->sub_action
);
dest->QueuePacket(outapp.get());
break;
}
}
FINISH_ENCODE();
break;
}
case BuyTraderItem: {
EQApplicationPacket *in = *p;
*p = nullptr;
auto eq = (structs::TraderBuy_Struct *) in->pBuffer;
LogTrading(
"(RoF2) BuyTraderItem action <green>[{}] item_id <green>[{}] item_sn <green>[{}] buyer <green>[{}]",
action,
eq->item_id,
eq->serial_number,
eq->buyer_name
);
dest->FastQueuePacket(&in);
break;
}
default: {
LogTrading("(RoF2) action <red>[{}]", action);
EQApplicationPacket *in = *p;
*p = nullptr;
dest->FastQueuePacket(&in);
}
}
}
@@ -3641,14 +3864,26 @@ namespace RoF2
{
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
OUT(Action);
OUT(Price);
OUT(TraderID);
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
OUT(ItemID);
OUT(Quantity);
OUT(AlreadySold);
LogTrading(
"(RoF2) item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
emu->item_id,
emu->price,
emu->quantity,
emu->trader_id
);
__packet->SetOpcode(OP_TraderShop);
OUT(action);
OUT(method);
OUT(sub_action);
OUT(trader_id);
OUT(item_id);
OUT(price);
OUT(already_sold);
OUT(quantity);
OUT_str(buyer_name);
OUT_str(seller_name);
OUT_str(item_name);
OUT_str(serial_number);
FINISH_ENCODE();
}
@@ -3657,71 +3892,74 @@ namespace RoF2
{
ENCODE_LENGTH_EXACT(TraderDelItem_Struct);
SETUP_DIRECT_ENCODE(TraderDelItem_Struct, structs::TraderDelItem_Struct);
LogTrading(
"(RoF2) trader_id <green>[{}] item_id <green>[{}]",
emu->trader_id,
emu->item_id
);
OUT(TraderID);
snprintf(eq->SerialNumber, sizeof(eq->SerialNumber), "%016d", emu->ItemID);
LogTrading("ENCODE(OP_TraderDelItem): TraderID [{}], SerialNumber: [{}]", emu->TraderID, emu->ItemID);
eq->TraderID = emu->trader_id;
auto serial = fmt::format("{:016}\n", emu->item_id);
strn0cpy(eq->SerialNumber, serial.c_str(), sizeof(eq->SerialNumber));
LogTrading("(RoF2) TraderID <green>[{}], SerialNumber: <green>[{}]", emu->trader_id, emu->item_id);
FINISH_ENCODE();
}
ENCODE(OP_TraderShop)
{
uint32 psize = (*p)->size;
if (psize == sizeof(TraderClick_Struct))
{
ENCODE_LENGTH_EXACT(TraderClick_Struct);
SETUP_DIRECT_ENCODE(TraderClick_Struct, structs::TraderClick_Struct);
auto action = *(uint32 *) (*p)->pBuffer;
eq->Code = 28; // Seen on Live
OUT(TraderID);
OUT(Approval);
switch (action) {
case ClickTrader: {
ENCODE_LENGTH_EXACT(TraderClick_Struct);
SETUP_DIRECT_ENCODE(TraderClick_Struct, structs::TraderClick_Struct);
LogTrading(
"(RoF2) ClickTrader action <green>[{}] trader_id <green>[{}]",
action,
emu->TraderID
);
FINISH_ENCODE();
}
else if (psize == sizeof(BazaarWelcome_Struct))
{
ENCODE_LENGTH_EXACT(BazaarWelcome_Struct);
SETUP_DIRECT_ENCODE(BazaarWelcome_Struct, structs::BazaarWelcome_Struct);
eq->action = structs::RoF2BazaarTraderBuyerActions::ClickTrader; // Seen on Live
eq->trader_id = emu->TraderID;
eq->unknown_008 = emu->Approval;
eq->Code = emu->Beginning.Action;
eq->EntityID = emu->Unknown012;
OUT(Traders);
OUT(Items);
eq->Traders2 = emu->Traders;
eq->Items2 = emu->Items;
FINISH_ENCODE();
break;
}
case structs::RoF2BazaarTraderBuyerActions::BuyTraderItem: {
ENCODE_LENGTH_EXACT(structs::TraderBuy_Struct);
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
LogTrading(
"(RoF2) item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
eq->item_id,
eq->price,
eq->quantity,
eq->trader_id
);
LogTrading("ENCODE(OP_TraderShop): BazaarWelcome_Struct Code [{}], Traders [{}], Items [{}]",
eq->Code, eq->Traders, eq->Items);
OUT(action);
OUT(method);
OUT(trader_id);
OUT(item_id);
OUT(price);
OUT(already_sold);
OUT(quantity);
OUT_str(buyer_name);
OUT_str(seller_name);
OUT_str(item_name);
OUT_str(serial_number);
FINISH_ENCODE();
}
else if (psize == sizeof(TraderBuy_Struct))
{
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
FINISH_ENCODE();
break;
}
default: {
LogTrading("(RoF2) Unhandled action <red>[{}]", action);
EQApplicationPacket *in = *p;
*p = nullptr;
OUT(Action);
OUT(TraderID);
//memcpy(eq->BuyerName, emu->BuyerName, sizeof(eq->BuyerName));
//memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName));
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
OUT(ItemID);
OUT(AlreadySold);
OUT(Price);
OUT(Quantity);
snprintf(eq->SerialNumber, sizeof(eq->SerialNumber), "%016d", emu->ItemID);
LogTrading("ENCODE(OP_TraderShop): Buy Action [{}], Price [{}], Trader [{}], ItemID [{}], Quantity [{}], ItemName, [{}]",
eq->Action, eq->Price, eq->TraderID, eq->ItemID, eq->Quantity, emu->ItemName);
FINISH_ENCODE();
}
else
{
LogTrading("ENCODE(OP_TraderShop): Encode Size Unknown ([{}])", psize);
dest->FastQueuePacket(&in);
}
}
}
@@ -4067,20 +4305,20 @@ namespace RoF2
structs::Spawn_Struct_Bitfields *Bitfields = (structs::Spawn_Struct_Bitfields*)Buffer;
Bitfields->gender = emu->gender;
Bitfields->ispet = emu->is_pet;
Bitfields->afk = emu->afk;
Bitfields->anon = emu->anon;
Bitfields->gm = emu->gm;
Bitfields->sneak = 0;
Bitfields->lfg = emu->lfg;
Bitfields->invis = emu->invis;
Bitfields->linkdead = 0;
Bitfields->showhelm = emu->showhelm;
Bitfields->trader = 0;
Bitfields->targetable = 1;
Bitfields->gender = emu->gender;
Bitfields->ispet = emu->is_pet;
Bitfields->afk = emu->afk;
Bitfields->anon = emu->anon;
Bitfields->gm = emu->gm;
Bitfields->sneak = 0;
Bitfields->lfg = emu->lfg;
Bitfields->invis = emu->invis;
Bitfields->linkdead = 0;
Bitfields->showhelm = emu->showhelm;
Bitfields->trader = emu->trader ? 1 : 0;
Bitfields->targetable = 1;
Bitfields->targetable_with_hotkey = emu->targetable_with_hotkey ? 1 : 0;
Bitfields->showname = ShowName;
Bitfields->showname = ShowName;
if (emu->DestructibleObject)
{
@@ -4431,6 +4669,7 @@ namespace RoF2
char *Buffer = (char *)__packet->pBuffer;
uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer);
LogTrading("(RoF2) action <green>[{}]", SubAction);
if ((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct)))
return;
@@ -4440,6 +4679,7 @@ namespace RoF2
IN(Beginning.Action);
memcpy(emu->Name, eq->Name, sizeof(emu->Name));
IN(SerialNumber);
LogTrading("(RoF2) action <green>[{}] serial_number <green>[{}]", eq->Beginning.Action, eq->SerialNumber);
FINISH_DIRECT_DECODE();
}
@@ -5331,40 +5571,61 @@ namespace RoF2
DECODE(OP_Trader)
{
uint32 psize = __packet->size;
if (psize == sizeof(structs::ClickTrader_Struct))
{
DECODE_LENGTH_EXACT(structs::ClickTrader_Struct);
SETUP_DIRECT_DECODE(ClickTrader_Struct, structs::ClickTrader_Struct);
auto action = *(uint32 *)__packet->pBuffer;
emu->Code = eq->Code;
// Live actually has 200 items now, but 80 is the most our internal struct supports
for (uint32 i = 0; i < 80; i++)
{
emu->SerialNumber[i] = 0; // eq->SerialNumber[i];
emu->ItemCost[i] = eq->ItemCost[i];
switch (action) {
case structs::RoF2BazaarTraderBuyerActions::BeginTraderMode: {
DECODE_LENGTH_EXACT(structs::BeginTrader_Struct);
SETUP_DIRECT_DECODE(ClickTrader_Struct, structs::BeginTrader_Struct);
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 Strings::ToUnsignedBigInt(x.serial_number,0);
}
);
FINISH_DIRECT_DECODE();
break;
}
case structs::RoF2BazaarTraderBuyerActions::EndTraderMode: {
DECODE_LENGTH_EXACT(structs::Trader_ShowItems_Struct);
SETUP_DIRECT_DECODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
LogTrading("(RoF2) EndTraderMode action <green>[{}]", action);
FINISH_DIRECT_DECODE();
}
else if (psize == sizeof(structs::Trader_ShowItems_Struct))
{
DECODE_LENGTH_EXACT(structs::Trader_ShowItems_Struct);
SETUP_DIRECT_DECODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
emu->action = TraderOff;
emu->entity_id = eq->entity_id;
emu->Code = eq->Code;
emu->TraderID = eq->TraderID;
FINISH_DIRECT_DECODE();
break;
}
case structs::RoF2BazaarTraderBuyerActions::ListTraderItems: {
LogTrading("(RoF2) ListTraderItems action <green>[{}]", action);
break;
}
case structs::RoF2BazaarTraderBuyerActions::PriceUpdate: {
DECODE_LENGTH_EXACT(structs::TraderPriceUpdate_Struct);
SETUP_DIRECT_DECODE(TraderPriceUpdate_Struct, structs::TraderPriceUpdate_Struct);
LogTrading("(RoF2) PriceUpdate action <green>[{}]", action);
FINISH_DIRECT_DECODE();
}
else if (psize == sizeof(structs::TraderStatus_Struct))
{
DECODE_LENGTH_EXACT(structs::TraderStatus_Struct);
SETUP_DIRECT_DECODE(TraderStatus_Struct, structs::TraderStatus_Struct);
emu->Action = PriceUpdate;
emu->SerialNumber = Strings::ToUnsignedBigInt(eq->serial_number, 0);
if (emu->SerialNumber == 0) {
LogTrading("(RoF2) Price change with invalid serial number <red>[{}]", eq->serial_number);
}
emu->NewPrice = eq->new_price;
emu->Code = eq->Code; // 11 = Start Trader, 2 = End Trader, 22 = ? - Guessing
FINISH_DIRECT_DECODE();
FINISH_DIRECT_DECODE();
break;
}
default: {
LogTrading("(RoF2) Unhandled action <red>[{}]", action);
}
}
}
@@ -5372,73 +5633,136 @@ namespace RoF2
{
DECODE_LENGTH_EXACT(structs::TraderBuy_Struct);
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
LogTrading(
"(RoF2) item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
eq->item_id,
eq->price,
eq->quantity,
eq->trader_id
);
IN(Action);
IN(Price);
IN(TraderID);
memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName));
IN(ItemID);
IN(Quantity);
IN(action);
IN(price);
IN(trader_id);
memcpy(emu->item_name, eq->item_name, sizeof(emu->item_name));
IN(item_id);
IN(quantity);
FINISH_DIRECT_DECODE();
}
DECODE(OP_TraderShop)
{
uint32 psize = __packet->size;
if (psize == sizeof(structs::TraderClick_Struct))
{
DECODE_LENGTH_EXACT(structs::TraderClick_Struct);
SETUP_DIRECT_DECODE(TraderClick_Struct, structs::TraderClick_Struct);
uint32 action = *(uint32 *)__packet->pBuffer;
IN(Code);
IN(TraderID);
IN(Approval);
LogTrading("DECODE(OP_TraderShop): TraderClick_Struct Code [{}], TraderID [{}], Approval [{}]",
eq->Code, eq->TraderID, eq->Approval);
switch (action) {
case structs::RoF2BazaarTraderBuyerActions::BazaarSearch: {
DECODE_LENGTH_EXACT(structs::BazaarSearch_Struct);
SETUP_DIRECT_DECODE(BazaarSearchCriteria_Struct, structs::BazaarSearch_Struct);
LogTrading(
"(RoF2) BazaarSearch action <green>[{}]",
action
);
FINISH_DIRECT_DECODE();
}
else if (psize == sizeof(structs::BazaarWelcome_Struct))
{
// Don't think this size gets used in RoF+ - Leaving for now...
DECODE_LENGTH_EXACT(structs::BazaarWelcome_Struct);
SETUP_DIRECT_DECODE(BazaarWelcome_Struct, structs::BazaarWelcome_Struct);
__packet->SetOpcode(OP_BazaarSearch);
emu->action = BazaarSearch;
emu->type = eq->type == UINT32_MAX ? UINT8_MAX : eq->type;
IN(item_stat);
IN(max_cost);
IN(min_cost);
IN(max_level);
IN(min_level);
IN(race);
IN(slot);
IN(trader_id);
IN(_class);
IN(prestige);
IN(search_scope);
IN(max_results);
IN(augment);
IN_str(item_name);
emu->Beginning.Action = eq->Code;
IN(Traders);
IN(Items);
LogTrading("DECODE(OP_TraderShop): BazaarWelcome_Struct Code [{}], Traders [{}], Items [{}]",
eq->Code, eq->Traders, eq->Items);
FINISH_DIRECT_DECODE();
break;
}
case structs::RoF2BazaarTraderBuyerActions::ClickTrader: {
DECODE_LENGTH_EXACT(structs::TraderClick_Struct);
SETUP_DIRECT_DECODE(TraderClick_Struct, structs::TraderClick_Struct);
FINISH_DIRECT_DECODE();
}
else if (psize == sizeof(structs::TraderBuy_Struct))
{
emu->Code = ClickTrader;
emu->TraderID = eq->trader_id;
emu->Approval = eq->unknown_008;
LogTrading("(RoF2) ClickTrader action <green>[{}], trader_id <green>[{}], approval <green>[{}]",
eq->action,
eq->trader_id,
eq->unknown_008
);
FINISH_DIRECT_DECODE();
break;
}
case structs::RoF2BazaarTraderBuyerActions::BazaarInspect: {
DECODE_LENGTH_EXACT(structs::BazaarInspect_Struct);
SETUP_DIRECT_DECODE(BazaarInspect_Struct, structs::BazaarInspect_Struct);
DECODE_LENGTH_EXACT(structs::TraderBuy_Struct);
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
__packet->SetOpcode(OP_BazaarSearch);
IN(item_id);
IN(trader_id);
emu->action = BazaarInspect;
emu->serial_number = Strings::ToUnsignedInt(eq->serial_number, 0);
if (emu->serial_number == 0) {
LogTrading(
"(RoF2) trader_id = <green>[{}] requested a BazaarInspect with an invalid serial number of <red>[{}]",
eq->trader_id,
eq->serial_number
);
FINISH_DIRECT_DECODE();
return;
}
IN(Action);
IN(Price);
IN(TraderID);
memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName));
IN(ItemID);
IN(Quantity);
LogTrading("DECODE(OP_TraderShop): TraderBuy_Struct (Unknowns) Unknown004 [{}], Unknown008 [{}], Unknown012 [{}], Unknown076 [{}], Unknown276 [{}]",
eq->Unknown004, eq->Unknown008, eq->Unknown012, eq->Unknown076, eq->Unknown276);
LogTrading("DECODE(OP_TraderShop): TraderBuy_Struct Buy Action [{}], Price [{}], Trader [{}], ItemID [{}], Quantity [{}], ItemName, [{}]",
eq->Action, eq->Price, eq->TraderID, eq->ItemID, eq->Quantity, eq->ItemName);
LogTrading("(RoF2) BazaarInspect action <green>[{}] item_id <green>[{}] serial_number <green>[{}]",
action,
eq->item_id,
eq->serial_number
);
FINISH_DIRECT_DECODE();
break;
}
case structs::RoF2BazaarTraderBuyerActions::WelcomeMessage: {
__packet->SetOpcode(OP_BazaarSearch);
LogTrading("(RoF2) WelcomeMessage action <green>[{}]", action);
break;
}
case structs::RoF2BazaarTraderBuyerActions::BuyTraderItem: {
DECODE_LENGTH_EXACT(structs::TraderBuy_Struct);
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
LogTrading(
"(RoF2) item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
eq->item_id,
eq->price,
eq->quantity,
eq->trader_id
);
FINISH_DIRECT_DECODE();
}
else if (psize == 4)
{
LogTrading("DECODE(OP_TraderShop): Forwarding packet as-is with size 4");
}
else
{
LogTrading("DECODE(OP_TraderShop): Decode Size Unknown ([{}])", psize);
__packet->SetOpcode(OP_TraderBuy);
IN(action);
IN(method);
IN(trader_id);
IN(item_id);
IN(price);
IN(already_sold);
IN(quantity);
IN_str(buyer_name);
IN_str(seller_name);
IN_str(item_name);
IN_str(serial_number);
FINISH_DIRECT_DECODE();
break;
}
default: {
LogTrading("(RoF2) Unhandled action <red>[{}]", action);
}
return;
}
}
@@ -5541,19 +5865,7 @@ namespace RoF2
RoF2::structs::ItemSerializationHeader hdr;
//sprintf(hdr.unknown000, "06e0002Y1W00");
snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%016d", item->ID);
if (packet_type == ItemPacketParcel) {
strn0cpy(
hdr.unknown000,
fmt::format(
"{:03}PAR{:010}\0",
inst->GetMerchantSlot(),
item->ID
).c_str(),
sizeof(hdr.unknown000)
);
}
strn0cpy(hdr.unknown000, fmt::format("{:016}\0", inst->GetSerialNumber()).c_str(),sizeof(hdr.unknown000));
hdr.stacksize =
item->ID == PARCEL_MONEY_ITEM_ID ? inst->GetPrice() : (inst->IsStackable() ? ((inst->GetCharges() > 1000)