diff --git a/common/repositories/trader_repository.h b/common/repositories/trader_repository.h index edbe0069b..e3dd79a32 100644 --- a/common/repositories/trader_repository.h +++ b/common/repositories/trader_repository.h @@ -245,6 +245,21 @@ public: return trader_item.at(0); } + static Trader GetItemByItemUniqueNumber(Database &db, const char* item_unique_id) + { + Trader e{}; + const auto trader_item = GetWhere( + db, + fmt::format("`item_unique_id` = '{}' LIMIT 1", item_unique_id) + ); + + if (trader_item.empty()) { + return e; + } + + return trader_item.at(0); + } + static int UpdateActiveTransaction(Database &db, uint32 id, bool status) { auto e = FindOne(db, id); @@ -338,7 +353,6 @@ public: "trader.char_zone_instance_id, trader.active_transaction, c.`name` FROM `trader` " "INNER JOIN character_data AS c ON trader.character_id = c.id " "WHERE {} " - "GROUP BY trader.item_id " "ORDER BY trader.character_id ASC", search_criteria_trader ); diff --git a/common/servertalk.h b/common/servertalk.h index 9985c875c..cfc0d49df 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -368,6 +368,14 @@ enum { UserToWorldStatusAlreadyOnline = -4 }; +enum { + BazaarPurchaseFailed = 0, + BazaarPurchaseSuccess = 1, + BazaarPurchaseSellerFailed = 2, + BazaarPurchaseSellerSuccess = 3, + BazaarPurchaseBuyerFailed = 4, + BazaarPurchaseBuyerSuccess = 5 +}; /************ PACKET RELATED STRUCT ************/ class ServerPacket { @@ -1775,8 +1783,11 @@ struct BazaarPurchaseMessaging_Struct { uint32 item_aug_5; uint32 item_aug_6; uint32 buyer_id; - uint32 item_quantity_available; + uint32 item_charges; uint32 id; + uint32 trader_zone_id; + uint32 trader_zone_instance_id; + uint32 transaction_status; }; diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 30e90cb09..f428aa87b 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1677,7 +1677,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { auto in = (BazaarPurchaseMessaging_Struct *)pack->pBuffer; if (in->trader_buy_struct.trader_id <= 0) { LogTrading( - "World Message [{}] received with invalid trader_id [{}]", + "World Message [{}] received with invalid trader_id [{}]", "ServerOP_BazaarPurchase", in->trader_buy_struct.trader_id ); @@ -1686,7 +1686,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { auto trader = ClientList::Instance()->FindCLEByCharacterID(in->trader_buy_struct.trader_id); if (trader) { - ZSList::Instance()->SendPacket(trader->zone(), trader->instance(), pack); + ZSList::Instance()->SendPacket(in->trader_zone_id, in->trader_zone_instance_id, pack); } break; diff --git a/zone/trading.cpp b/zone/trading.cpp index ae69c52fa..473ebc5cf 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -2773,8 +2773,7 @@ std::string Client::DetermineMoneyString(uint64 cp) void Client::BuyTraderItemFromBazaarWindow(const EQApplicationPacket *app) { auto in = reinterpret_cast(app->pBuffer); - auto item_unique_id = std::string(in->item_unique_id); - auto trader_item = TraderRepository::GetItemByItemUniqueNumber(database, item_unique_id); + auto trader_item = TraderRepository::GetItemByItemUniqueNumber(database, in->item_unique_id); LogTradingDetail( "Packet details: \n" @@ -2836,13 +2835,11 @@ void Client::BuyTraderItemFromBazaarWindow(const EQApplicationPacket *app) return; } - TraderRepository::UpdateActiveTransaction(database, trader_item.id, true); - auto next_slot = FindNextFreeParcelSlot(CharacterID()); if (next_slot == INVALID_INDEX) { LogTrading( - "{} attempted to purchase {} from the bazaar with parcel delivery. Unfortunately their parcel limit was reached. " - "Purchase unsuccessful.", + "{} attempted to purchase {} from the bazaar with parcel delivery. Unfortunately their parcel limit was " + "reached. Purchase unsuccessful.", GetCleanName(), in->item_name ); @@ -2853,11 +2850,24 @@ void Client::BuyTraderItemFromBazaarWindow(const EQApplicationPacket *app) return; } - LogTrading( - "Name: [{}] Requested Quantity: [{}] Charges on Item [{}]", - in->item_name, - in->quantity, - trader_item.item_charges + TraderRepository::UpdateActiveTransaction(database, trader_item.id, true); + + uint32 quantity = in->quantity; + int16 charges = 1; + auto item = database.GetItem(trader_item.item_id); + + if (trader_item.item_charges > 0 || item->Stackable || item->MaxCharges > 0) { + charges = trader_item.item_charges; + } + + LogTrading("Name: [{}] Requested Quantity: [{}] Charges: [{}]", in->item_name, quantity, charges); + LogTradingDetail( + "Step 1:Bazaar Purchase. Buyer [{}] Seller [{}] Quantity [{}] Charges [{}] Item_Unique_ID [{}]", + CharacterID(), + in->trader_id, + quantity, + charges, + in->item_unique_id ); uint64 total_cost = static_cast(in->price) * static_cast(in->quantity); @@ -2872,7 +2882,7 @@ void Client::BuyTraderItemFromBazaarWindow(const EQApplicationPacket *app) return; } - uint64 fee = std::round(total_cost * RuleR(Bazaar, ParcelDeliveryCostMod)); + uint64 fee = std::round(total_cost * RuleR(Bazaar, ParcelDeliveryCostMod)); if (!TakeMoneyFromPP(total_cost + fee, false)) { in->method = BazaarByParcel; in->sub_action = InsufficientFunds; @@ -2882,7 +2892,8 @@ void Client::BuyTraderItemFromBazaarWindow(const EQApplicationPacket *app) } Message(Chat::Red, fmt::format("You paid {} for the parcel delivery.", DetermineMoneyString(fee)).c_str()); - LogTrading("Customer [{}] Paid: [{}] in Copper", CharacterID(), total_cost); + LogTrading("Customer [{}] Paid: [{}] to trader [{}]", CharacterID(), DetermineMoneyString(total_cost), trader_item.character_id); + LogTradingDetail("Step 2:Bazaar Purchase. Took [{}] from Buyer [{}] ", DetermineMoneyString(total_cost), CharacterID()); if (buy_item && PlayerEventLogs::Instance()->IsEventEnabled(PlayerEvent::TRADER_PURCHASE)) { auto e = PlayerEvent::TraderPurchaseEvent{ @@ -2910,7 +2921,7 @@ void Client::BuyTraderItemFromBazaarWindow(const EQApplicationPacket *app) parcel_out.from_name = in->seller_name; parcel_out.note = "Delivered from a Bazaar Purchase"; parcel_out.sent_date = time(nullptr); - parcel_out.quantity = in->quantity; + parcel_out.quantity = charges; parcel_out.item_id = trader_item.item_id; parcel_out.aug_slot_1 = trader_item.augment_one; parcel_out.aug_slot_2 = trader_item.augment_two; @@ -2934,6 +2945,7 @@ void Client::BuyTraderItemFromBazaarWindow(const EQApplicationPacket *app) in->method = BazaarByParcel; in->sub_action = Failed; TraderRepository::UpdateActiveTransaction(database, trader_item.id, false); + AddMoneyToPP(total_cost + fee); TradeRequestFailed(app); return; } @@ -2960,20 +2972,41 @@ void Client::BuyTraderItemFromBazaarWindow(const EQApplicationPacket *app) Parcel_Struct ps{}; ps.item_slot = parcel_out.slot_id; strn0cpy(ps.send_to, GetCleanName(), sizeof(ps.send_to)); - - if (trader_item.item_charges <= static_cast(in->quantity) || !trader_item.item_charges <= 0) { - TraderRepository::DeleteOne(database, trader_item.id); - } - SendParcelDeliveryToWorld(ps); - auto out_server = std::make_unique(ServerOP_BazaarPurchase, static_cast(sizeof(BazaarPurchaseMessaging_Struct))); + LogTradingDetail("Step 3:Bazaar Purchase. Sent parcel to Buyer [{}] Item ID [{}] Quantity [{}] Charges [{}]", + CharacterID(), + trader_item.item_id, + quantity, + charges + ); + + if (item->Stackable && quantity != charges) { + TraderRepository::UpdateQuantity(database, in->item_unique_id, trader_item.item_charges - quantity); + LogTradingDetail( + "Step 4a:Bazaar Purchase. Decreased database id {} from [{}] to [{}] charges", + trader_item.item_id, + trader_item.item_charges, + charges + ); + } + else { + TraderRepository::DeleteOne(database, trader_item.id); + LogTradingDetail( + "Step 4b:Bazaar Purchase. Deleted database id [{}] because database quantity [{}] equals [{}] purchased quantity", + trader_item.id, + trader_item.item_charges, + charges + ); + } + + auto out_server = std::make_unique(ServerOP_BazaarPurchase, sizeof(BazaarPurchaseMessaging_Struct)); auto out_data = (BazaarPurchaseMessaging_Struct *) out_server->pBuffer; out_data->trader_buy_struct.action = in->action; out_data->trader_buy_struct.method = in->method; out_data->trader_buy_struct.already_sold = in->already_sold; - out_data->trader_buy_struct.item_id = in->item_id; + out_data->trader_buy_struct.item_id = item->ID; out_data->trader_buy_struct.price = in->price; out_data->trader_buy_struct.quantity = in->quantity; out_data->trader_buy_struct.sub_action = in->sub_action; @@ -2985,12 +3018,13 @@ void Client::BuyTraderItemFromBazaarWindow(const EQApplicationPacket *app) out_data->item_aug_4 = trader_item.augment_four; out_data->item_aug_5 = trader_item.augment_five; out_data->item_aug_6 = trader_item.augment_six; - out_data->item_quantity_available = trader_item.item_charges; + out_data->item_charges = trader_item.item_charges; out_data->id = trader_item.id; + out_data->trader_zone_id = trader_item.char_zone_id; + out_data->trader_zone_instance_id = trader_item.char_zone_instance_id; strn0cpy(out_data->trader_buy_struct.buyer_name, GetCleanName(), sizeof(out_data->trader_buy_struct.buyer_name)); - strn0cpy(out_data->trader_buy_struct.buyer_name, in->buyer_name, sizeof(out_data->trader_buy_struct.buyer_name)); - strn0cpy(out_data->trader_buy_struct.item_name, in->item_name, sizeof(out_data->trader_buy_struct.item_name)); strn0cpy(out_data->trader_buy_struct.seller_name, in->seller_name, sizeof(out_data->trader_buy_struct.seller_name)); + strn0cpy(out_data->trader_buy_struct.item_name, in->item_name, sizeof(out_data->trader_buy_struct.item_name)); strn0cpy( out_data->trader_buy_struct.item_unique_id, in->item_unique_id, @@ -2998,8 +3032,56 @@ void Client::BuyTraderItemFromBazaarWindow(const EQApplicationPacket *app) ); worldserver.SendPacket(out_server.get()); + LogTradingDetail("Step 5:Bazaar Purchase. Send bazaar messaging data to world.\n" + "Action: {} \n" + "Sub Action: {} \n" + "Method: {} \n" + "Item ID: {} \n" + "Item Unique ID: {} \n" + "Item Name: {} \n" + "Price: {} \n" + "Quantity: {} \n" + "Charges: {} \n" + "Augment One: {} \n" + "Augment Two: {} \n" + "Augment Three: {} \n" + "Augment Four: {} \n" + "Augment Five: {} \n" + "Augment Six: {} \n" + "Already Sold: {} \n" + "DB ID: {} \n" + "Trader ID: {} \n" + "Trader: {} \n" + "Trader Zone ID {} \n" + "Trader Zone Instance ID {} \n" + "Buyer ID: {} \n" + "Buyer: {} \n", + out_data->trader_buy_struct.action, + out_data->trader_buy_struct.sub_action, + out_data->trader_buy_struct.method, + out_data->trader_buy_struct.item_id, + out_data->trader_buy_struct.item_unique_id, + out_data->trader_buy_struct.item_name, + out_data->trader_buy_struct.price, + out_data->trader_buy_struct.quantity, + out_data->item_charges, + out_data->item_aug_1, + out_data->item_aug_2, + out_data->item_aug_3, + out_data->item_aug_4, + out_data->item_aug_5, + out_data->item_aug_6, + out_data->trader_buy_struct.already_sold, + out_data->id, + out_data->trader_buy_struct.trader_id, + out_data->trader_buy_struct.seller_name, + out_data->trader_zone_id, + out_data->trader_zone_instance_id, + out_data->buyer_id, + out_data->trader_buy_struct.buyer_name); SendMoneyUpdate(); + LogTradingDetail("Step 6:Bazaar Purchase. Send money update to client {}. Buyer Actions complete.", CharacterID()); } void Client::SetBuyerWelcomeMessage(const char *welcome_message)