[Feature] Add Barter/Buyer Features (#4405)

* Add Barter/Buyer Features

Adds barter and buyer features, for ROF2 only at this time including item compensation

* Remove FKs from buyer tables

Remove FKs from buyer tables

* Bug fix for Find Buyer and mutli item selling

Update for quantity purchases not correctly providing multi items.
Update for Find Buyer functionality based on zone instancing.
Update buyer messaging
Update buyer LORE duplicate check

* Revert zone instance comment

* Revert zone_id packet size field

* Add zone instancing to barter/buyer

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
Mitch Freeman 2024-07-30 17:23:37 -03:00 committed by GitHub
parent fc3c691588
commit e49ab924cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 4715 additions and 946 deletions

View File

@ -158,6 +158,7 @@ SET(repositories
repositories/base/base_bugs_repository.h
repositories/base/base_bug_reports_repository.h
repositories/base/base_buyer_repository.h
repositories/base/base_buyer_trade_items_repository.h
repositories/base/base_character_activities_repository.h
repositories/base/base_character_alternate_abilities_repository.h
repositories/base/base_character_alt_currency_repository.h
@ -339,7 +340,8 @@ SET(repositories
repositories/books_repository.h
repositories/bugs_repository.h
repositories/bug_reports_repository.h
repositories/buyer_repository.h
repositories/buyer_buy_lines_repository.h
repositories/buyer_trade_items_repository.h
repositories/character_activities_repository.h
repositories/character_alternate_abilities_repository.h
repositories/character_alt_currency_repository.h

View File

@ -78,6 +78,7 @@
#include "repositories/merchantlist_temp_repository.h"
#include "repositories/bot_data_repository.h"
#include "repositories/trader_repository.h"
#include "repositories/buyer_repository.h"
extern Client client;
@ -2123,3 +2124,8 @@ void Database::ClearTraderDetails()
{
TraderRepository::Truncate(*this);
}
void Database::ClearBuyerDetails()
{
BuyerRepository::DeleteBuyer(*this, 0);
}

View File

@ -245,6 +245,7 @@ public:
void PurgeAllDeletedDataBuckets();
void ClearGuildOnlineStatus();
void ClearTraderDetails();
void ClearBuyerDetails();
/* Database Variables */

View File

@ -5660,6 +5660,63 @@ ALTER TABLE `trader`
DROP PRIMARY KEY,
ADD PRIMARY KEY (`id`),
ADD INDEX `charid_slotid` (`char_id`, `slot_id`);
)"
},
ManifestEntry{
.version = 9281,
.description = "2024_06_24_update_buyer_support.sql",
.check = "SHOW COLUMNS FROM `buyer` LIKE 'id'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `buyer`
ADD COLUMN `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
CHANGE COLUMN `charid` `char_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `id`,
ADD COLUMN `char_entity_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `char_id`,
ADD COLUMN `char_name` VARCHAR(64) NULL DEFAULT NULL AFTER `char_entity_id`,
ADD COLUMN `char_zone_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `char_name`,
ADD COLUMN `char_zone_instance_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `char_zone_id`,
ADD COLUMN `transaction_date` DATETIME NULL DEFAULT NULL AFTER `char_zone_instance_id`,
ADD COLUMN `welcome_message` VARCHAR(256) NULL DEFAULT NULL AFTER `transaction_date`,
DROP COLUMN `buyslot`,
DROP COLUMN `itemid`,
DROP COLUMN `itemname`,
DROP COLUMN `quantity`,
DROP COLUMN `price`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`id`) USING BTREE,
ADD INDEX `charid` (`char_id`);
CREATE TABLE `buyer_buy_lines` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`buyer_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
`char_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
`buy_slot_id` INT(11) NOT NULL DEFAULT '0',
`item_id` INT(11) NOT NULL DEFAULT '0',
`item_qty` INT(11) NOT NULL DEFAULT '0',
`item_price` INT(11) NOT NULL DEFAULT '0',
`item_icon` INT(11) UNSIGNED NOT NULL DEFAULT '0',
`item_name` VARCHAR(64) NOT NULL DEFAULT '' COLLATE 'latin1_swedish_ci',
PRIMARY KEY (`id`) USING BTREE,
INDEX `buyerid_charid_buyslotid` (`buyer_id`, `char_id`, `buy_slot_id`) USING BTREE
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1;
CREATE TABLE `buyer_trade_items` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`buyer_buy_lines_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
`item_id` INT(11) NOT NULL DEFAULT '0',
`item_qty` INT(11) NOT NULL DEFAULT '0',
`item_icon` INT(11) NOT NULL DEFAULT '0',
`item_name` VARCHAR(64) NOT NULL DEFAULT '0' COLLATE 'latin1_swedish_ci',
PRIMARY KEY (`id`) USING BTREE,
INDEX `buyerbuylinesid` (`buyer_buy_lines_id`) USING BTREE
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1;
)"
}
// -- template; copy/paste this when you need to create a new entry

View File

@ -36,7 +36,6 @@ namespace DatabaseSchema {
{
return {
{"adventure_stats", "player_id"},
{"buyer", "charid"},
{"char_recipe_list", "char_id"},
{"character_activities", "charid"},
{"character_alt_currency", "char_id"},
@ -107,6 +106,8 @@ namespace DatabaseSchema {
"adventure_details",
"adventure_stats",
"buyer",
"buyer_buy_lines",
"buyer_trade_items",
"char_recipe_list",
"character_activities",
"character_alt_currency",
@ -325,6 +326,9 @@ namespace DatabaseSchema {
"banned_ips",
"bug_reports",
"bugs",
"buyer",
"buyer_buy_lines",
"buyer_trade_items",
"completed_shared_task_activity_state",
"completed_shared_task_members",
"completed_shared_tasks",

View File

@ -67,6 +67,7 @@ N(OP_Buff),
N(OP_BuffCreate),
N(OP_BuffRemoveRequest),
N(OP_Bug),
N(OP_BuyerItems),
N(OP_CameraEffect),
N(OP_Camp),
N(OP_CancelSneakHide),

View File

@ -1129,4 +1129,7 @@ enum ExpSource
#define PARCEL_LIMIT 5
#define PARCEL_BEGIN_SLOT 1
namespace DoorType {
constexpr uint32 BuyerStall = 155;
}
#endif /*COMMON_EQ_CONSTANTS_H*/

View File

@ -323,6 +323,7 @@ union
bool show_name;
bool guild_show;
bool trader;
bool buyer;
};
struct PlayerState_Struct {
@ -3139,60 +3140,369 @@ struct BazaarSearchResults_Struct {
// Barter/Buyer
//
//
enum {
Barter_BuyerSearch = 0,
Barter_SellerSearch = 1,
Barter_BuyerModeOn = 2,
Barter_BuyerModeOff = 3,
Barter_BuyerItemUpdate = 5,
Barter_BuyerItemRemove = 6,
Barter_SellItem = 7,
#define MAX_BUYER_COMPENSATION_ITEMS 10
enum BarterBuyerActions {
Barter_BuyerSearch = 0,
Barter_SellerSearch = 1,
Barter_BuyerModeOn = 2,
Barter_BuyerModeOff = 3,
Barter_BuyerItemStart = 4,
Barter_BuyerItemUpdate = 5,
Barter_BuyerItemRemove = 6,
Barter_SellItem = 7,
Barter_SellerTransactionComplete = 8,
Barter_BuyerTransactionComplete = 9,
Barter_BuyerInspectBegin = 10,
Barter_BuyerInspectEnd = 11,
Barter_BuyerAppearance = 12,
Barter_BuyerInspectWindow = 13,
Barter_BarterItemInspect = 14,
Barter_SellerBrowsing = 15,
Barter_BuyerSearchResults = 16,
Barter_Welcome = 17,
Barter_WelcomeMessageUpdate = 19,
Barter_BuyerItemInspect = 21,
Barter_Unknown23 = 23
Barter_BuyerTransactionComplete = 9,
Barter_BuyerInspectBegin = 10,
Barter_BuyerInspectEnd = 11,
Barter_BuyerAppearance = 12,
Barter_BuyerInspectWindow = 13,
Barter_BarterItemInspect = 14,
Barter_SellerBrowsing = 15,
Barter_BuyerSearchResults = 16,
Barter_Welcome = 17,
Barter_WelcomeMessageUpdate = 19,
Barter_Greeting = 20,
Barter_BuyerItemInspect = 21,
Barter_OpenBarterWindow = 23,
Barter_AddToBarterWindow = 26,
Barter_RemoveFromBarterWindow = 27,
Barter_RemoveFromMerchantWindow = 50, //Not a client item. Used for internal communications.
Barter_FailedTransaction = 51,
Barter_BuyerCouldNotBeFound = 52,
Barter_FailedBuyerChecks = 53,
Barter_SellerCouldNotBeFound = 54,
Barter_FailedSellerChecks = 55
};
enum BarterBuyerSubActions {
Barter_Success = 0,
Barter_Failure = 1,
Barter_DataOutOfDate = 4,
Barter_SellerDoesNotHaveItem = 6,
Barter_SameZone = 8
};
enum BuyerBarter {
Off = 0,
On = 1
};
struct BuyerRemoveItem_Struct {
uint32 action;
uint32 buy_slot_id;
};
struct BuyerRemoveItemFromMerchantWindow_Struct {
uint32 action;
uint32 unknown_004;
uint32 buy_slot_id;
uint32 unknown_012;
};
struct BuyerGeneric_Struct {
uint32 action;
char payload[];
};
struct BuyerMessaging_Struct {
uint32 action;
uint32 sub_action;
uint32 zone_id;
uint32 buyer_id;
uint32 buyer_entity_id;
char buyer_name[64];
uint32 buy_item_id;
uint32 buy_item_qty;
uint64 buy_item_cost;
uint32 buy_item_icon;
uint32 seller_entity_id;
char seller_name[64];
char item_name[64];
uint32 slot;
uint32 seller_quantity;
};
struct BuyerAddBuyertoBarterWindow_Struct {
uint32 action;
uint32 zone_id;
uint32 buyer_id;
uint32 buyer_entity_id;
char buyer_name[64];
};
struct BuyerRemoveBuyerFromBarterWindow_Struct {
uint32 action;
uint32 buyer_id;
};
struct BuyerBrowsing_Struct {
uint32 action;
char char_name[64];
};
struct BuyerGreeting_Struct {
uint32 action;
uint32 buyer_id;
};
struct BuyerWelcomeMessageUpdate_Struct {
/*000*/ uint32 Action;
/*004*/ char WelcomeMessage[256];
uint32 action;
char welcome_message[256];
};
struct BuyerItemSearch_Struct {
/*000*/ uint32 Unknown000;
/*004*/ char SearchString[64];
struct BuyerLineTradeItems_Struct {
uint32 item_id;
uint32 item_quantity;
uint32 item_icon;
std::string item_name;
void operator*=(uint32 multiplier)
{
this->item_quantity *= multiplier;
}
template<class Archive>
void serialize(Archive &archive)
{
archive(
CEREAL_NVP(item_id),
CEREAL_NVP(item_quantity),
CEREAL_NVP(item_icon),
CEREAL_NVP(item_name)
);
}
};
struct BuyerItemSearchResultEntry_Struct {
/*000*/ char ItemName[64];
/*064*/ uint32 ItemID;
/*068*/ uint32 Unknown068;
/*072*/ uint32 Unknown072;
struct BuyerLineItems_Struct {
uint32 slot;
uint8 enabled;
uint32 item_id;
std::string item_name;
uint32 item_icon;
uint32 item_quantity;
uint8 item_toggle;
uint32 item_cost;
std::vector<BuyerLineTradeItems_Struct> trade_items;
template<class Archive>
void serialize(Archive &archive)
{
archive(
CEREAL_NVP(slot),
CEREAL_NVP(enabled),
CEREAL_NVP(item_id),
CEREAL_NVP(item_name),
CEREAL_NVP(item_icon),
CEREAL_NVP(item_quantity),
CEREAL_NVP(item_toggle),
CEREAL_NVP(item_cost),
CEREAL_NVP(trade_items)
);
}
};
#define MAX_BUYER_ITEMSEARCH_RESULTS 200
struct BuyerBuyLines_Struct {
uint32 action;
union {
uint32 no_items;
uint32 string_length;
};
std::vector<BuyerLineItems_Struct> buy_lines;
struct BuyerItemSearchResults_Struct {
uint32 Action;
uint32 ResultCount;
BuyerItemSearchResultEntry_Struct Results[MAX_BUYER_ITEMSEARCH_RESULTS];
template<class Archive>
void serialize(Archive &archive)
{
archive(
CEREAL_NVP(action),
CEREAL_NVP(no_items),
CEREAL_NVP(buy_lines)
);
}
};
struct BuyerLineSellItem_Struct {
uint32 action;
uint32 sub_action;
uint32 error_code;
uint32 purchase_method; // 0 direct merchant, 1 via /barter window
uint32 buyer_entity_id;
uint32 buyer_id;
std::string buyer_name;
uint32 seller_entity_id;
std::string seller_name;
uint32 slot;
uint8 enabled;
uint32 item_id;
char item_name[64];
uint32 item_icon;
uint32 item_quantity;
uint8 item_toggle;
uint32 item_cost;
uint32 no_trade_items;
std::vector<BuyerLineTradeItems_Struct> trade_items;
uint32 seller_quantity;
uint32 zone_id;
template<class Archive>
void serialize(Archive &archive)
{
archive(
CEREAL_NVP(action),
CEREAL_NVP(sub_action),
CEREAL_NVP(error_code),
CEREAL_NVP(purchase_method),
CEREAL_NVP(buyer_entity_id),
CEREAL_NVP(buyer_id),
CEREAL_NVP(buyer_name),
CEREAL_NVP(seller_entity_id),
CEREAL_NVP(seller_name),
CEREAL_NVP(slot),
CEREAL_NVP(enabled),
CEREAL_NVP(item_id),
CEREAL_NVP(item_name),
CEREAL_NVP(item_icon),
CEREAL_NVP(item_quantity),
CEREAL_NVP(item_toggle),
CEREAL_NVP(item_cost),
CEREAL_NVP(no_trade_items),
CEREAL_NVP(trade_items),
CEREAL_NVP(seller_quantity),
CEREAL_NVP(zone_id)
);
}
};
struct BuyerLineItemsSearch_Struct {
uint32 slot;
uint8 enabled;
uint32 item_id;
char item_name[64];
uint32 item_icon;
uint32 item_quantity;
uint8 item_toggle;
uint32 item_cost;
uint32 buyer_id;
uint32 buyer_entity_id;
uint32 buyer_zone_id;
uint32 buyer_zone_instance_id;
std::string buyer_name;
std::vector<BuyerLineTradeItems_Struct> trade_items;
template<class Archive>
void serialize(Archive &archive)
{
archive(
CEREAL_NVP(slot),
CEREAL_NVP(enabled),
CEREAL_NVP(item_id),
CEREAL_NVP(item_name),
CEREAL_NVP(item_icon),
CEREAL_NVP(item_quantity),
CEREAL_NVP(item_toggle),
CEREAL_NVP(item_cost),
CEREAL_NVP(buyer_id),
CEREAL_NVP(buyer_entity_id),
CEREAL_NVP(buyer_zone_id),
CEREAL_NVP(buyer_zone_instance_id),
CEREAL_NVP(buyer_name),
CEREAL_NVP(trade_items)
);
}
};
struct BuyerLineSearch_Struct {
uint32 action;
uint32 no_items;
std::string search_string;
uint32 transaction_id;
std::vector<BuyerLineItemsSearch_Struct> buy_line;
template<class Archive>
void serialize(Archive &archive)
{
archive(
CEREAL_NVP(action),
CEREAL_NVP(no_items),
CEREAL_NVP(search_string),
CEREAL_NVP(transaction_id),
CEREAL_NVP(buy_line)
);
}
};
struct BuyerSetAppearance_Struct {
uint32 action;
uint32 entity_id;
uint32 status; // 0 off 1 on
char buyer_name[64];
};
struct BarterItemSearchLinkRequest_Struct {
uint32 action;
uint32 searcher_id;
uint32 unknown_008;
uint32 unknown_012;
uint32 item_id;
uint32 unknown_020;
};
struct BuyerInspectRequest_Struct {
uint32 action;
uint32 buyer_id;
uint32 approval;
};
struct BarterSearchRequest_Struct {
uint32 Action;
char SearchString[64];
uint32 SearchID;
uint32 action;
char search_string[64];
uint32 transaction_id;
uint32 unknown_072;
uint32 buyer_id;
uint8 search_scope; //0 All Buyers, 1 Local Buyers
uint16 zone_id;
};
struct BuyerItemSearch_Struct {
uint32 action;
char search_string[64];
};
struct BuyerItemSearchResultEntry_Struct {
char item_name[64];
uint32 item_id;
uint32 item_icon;
uint32 unknown_072;
template<class Archive>
void serialize(Archive &archive)
{
archive(
CEREAL_NVP(item_name),
CEREAL_NVP(item_id),
CEREAL_NVP(item_icon),
CEREAL_NVP(unknown_072)
);
}
};
struct BuyerItemSearchResults_Struct {
uint32 action;
uint32 result_count;
std::vector<BuyerItemSearchResultEntry_Struct> results;
template<class Archive>
void serialize(Archive &archive)
{
archive(
CEREAL_NVP(action),
CEREAL_NVP(result_count),
CEREAL_NVP(results)
);
}
};
//old below here
struct BuyerItemSearchLinkRequest_Struct {
/*000*/ uint32 Action; // 0x00000015
/*004*/ uint32 ItemID;
@ -3200,31 +3510,6 @@ struct BuyerItemSearchLinkRequest_Struct {
/*012*/ uint32 Unknown012;
};
struct BarterItemSearchLinkRequest_Struct {
/*000*/ uint32 Action; // 0x0000000E
/*004*/ uint32 SearcherID;
/*008*/ uint32 Unknown008;
/*012*/ uint32 Unknown012;
/*016*/ uint32 ItemID;
/*020*/ uint32 Unknown020;
};
struct BuyerInspectRequest_Struct {
uint32 Action;
uint32 BuyerID;
uint32 Approval;
};
struct BuyerBrowsing_Struct {
uint32 Action;
char PlayerName[64];
};
struct BuyerRemoveItem_Struct {
uint32 Action;
uint32 BuySlot;
};
struct ServerSideFilters_Struct {
uint8 clientattackfilters; // 0) No, 1) All (players) but self, 2) All (players) but group
uint8 npcattackfilters; // 0) No, 1) Ignore NPC misses (all), 2) Ignore NPC Misses + Attacks (all but self), 3) Ignores NPC Misses + Attacks (all but group)
@ -6066,9 +6351,12 @@ enum BazaarTraderBarterActions {
};
enum BazaarPurchaseActions {
ByVendor = 0,
ByParcel = 1,
ByDirectToInventory = 2
BazaarByVendor = 0,
BazaarByParcel = 1,
BazaarByDirectToInventory = 2,
BarterByVendor = 0,
BarterInBazaar = 1,
BarterOutsideBazaar = 2
};
enum BazaarPurchaseSubActions {
@ -6142,6 +6430,11 @@ struct BazaarSearchMessaging_Struct {
}
};
struct BuylineItemDetails_Struct {
uint64 item_cost;
uint32 item_quantity;
};
// Restore structure packing to default
#pragma pack()

View File

@ -706,6 +706,7 @@ void PlayerEventLogs::SetSettingsDefaults()
m_settings[PlayerEvent::PARCEL_SEND].event_enabled = 1;
m_settings[PlayerEvent::PARCEL_RETRIEVE].event_enabled = 1;
m_settings[PlayerEvent::PARCEL_DELETE].event_enabled = 1;
m_settings[PlayerEvent::BARTER_TRANSACTION].event_enabled = 1;
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
m_settings[i].retention_days = RETENTION_DAYS_DEFAULT;

View File

@ -60,7 +60,8 @@ namespace PlayerEvent {
GUILD_TRIBUTE_DONATE_PLAT,
PARCEL_SEND,
PARCEL_RETRIEVE,
PARCEL_DELETE,
PARCEL_DELETE,
BARTER_TRANSACTION,
MAX // dont remove
};
@ -122,7 +123,8 @@ namespace PlayerEvent {
"Guild Tribute Donate Platinum",
"Parcel Item Sent",
"Parcel Item Retrieved",
"Parcel Prune Routine"
"Parcel Prune Routine",
"Barter Transaction"
};
// Generic struct used by all events
@ -1081,6 +1083,32 @@ namespace PlayerEvent {
);
}
};
struct BarterTransaction {
std::string status;
uint32 item_id;
uint32 item_quantity;
std::string item_name;
std::vector<BuyerLineTradeItems_Struct> trade_items;
std::string buyer_name;
std::string seller_name;
uint64 total_cost;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(status),
CEREAL_NVP(item_id),
CEREAL_NVP(item_quantity),
CEREAL_NVP(item_name),
CEREAL_NVP(trade_items),
CEREAL_NVP(buyer_name),
CEREAL_NVP(seller_name),
CEREAL_NVP(total_cost)
);
}
};
}
#endif //EQEMU_PLAYER_EVENTS_H

View File

@ -1743,3 +1743,68 @@ std::vector<uint32> EQ::InventoryProfile::GetAugmentIDsBySlotID(int16 slot_id)
return augments;
}
std::vector<int16> EQ::InventoryProfile::FindAllFreeSlotsThatFitItem(const EQ::ItemData *item_data)
{
std::vector<int16> free_slots{};
for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) {
if ((((uint64) 1 << i) & GetLookup()->PossessionsBitmask) == 0) {
continue;
}
EQ::ItemInstance *inv_item = GetItem(i);
if (!inv_item) {
// Found available slot in personal inventory
free_slots.push_back(i);
}
if (inv_item->IsClassBag() &&
EQ::InventoryProfile::CanItemFitInContainer(item_data, inv_item->GetItem())) {
int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(i, EQ::invbag::SLOT_BEGIN);
uint8 bag_size = inv_item->GetItem()->BagSlots;
for (uint8 bag_slot = EQ::invbag::SLOT_BEGIN; bag_slot < bag_size; bag_slot++) {
auto bag_item = GetItem(base_slot_id + bag_slot);
if (!bag_item) {
// Found available slot within bag
free_slots.push_back(i);
}
}
}
}
return free_slots;
}
int16 EQ::InventoryProfile::FindFirstFreeSlotThatFitsItem(const EQ::ItemData *item_data)
{
for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) {
if ((((uint64) 1 << i) & GetLookup()->PossessionsBitmask) == 0) {
continue;
}
EQ::ItemInstance *inv_item = GetItem(i);
if (!inv_item) {
// Found available slot in personal inventory
return i;
}
if (inv_item->IsClassBag() &&
EQ::InventoryProfile::CanItemFitInContainer(item_data, inv_item->GetItem())) {
int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(i, EQ::invbag::SLOT_BEGIN);
uint8 bag_size = inv_item->GetItem()->BagSlots;
for (uint8 bag_slot = EQ::invbag::SLOT_BEGIN; bag_slot < bag_size; bag_slot++) {
auto bag_item = GetItem(base_slot_id + bag_slot);
if (!bag_item) {
// Found available slot within bag
return base_slot_id + bag_slot;
}
}
}
}
return 0;
}

View File

@ -176,6 +176,8 @@ namespace EQ
// Locate an available inventory slot
int16 FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size = 0, bool is_arrow = false);
int16 FindFreeSlotForTradeItem(const ItemInstance* inst, int16 general_start = invslot::GENERAL_BEGIN, uint8 bag_start = invbag::SLOT_BEGIN);
std::vector<int16> FindAllFreeSlotsThatFitItem(const EQ::ItemData *inst);
int16 FindFirstFreeSlotThatFitsItem(const EQ::ItemData *inst);
// Calculate slot_id for an item within a bag
static int16 CalcSlotId(int16 slot_id); // Calc parent bag's slot_id

View File

@ -356,41 +356,91 @@ namespace RoF2
ENCODE(OP_Barter)
{
EQApplicationPacket *in = *p;
EQApplicationPacket *in = *p;
*p = nullptr;
char *Buffer = (char *)in->pBuffer;
char *buffer = (char *) in->pBuffer;
uint32 sub_action = VARSTRUCT_DECODE_TYPE(uint32, buffer);
uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer);
switch (sub_action) {
case Barter_BuyerAppearance: {
auto emu = (BuyerInspectRequest_Struct *) in->pBuffer;
if (SubAction != Barter_BuyerAppearance)
{
dest->FastQueuePacket(&in, ack_req);
auto outapp = new EQApplicationPacket(OP_Barter, sizeof(structs::Buyer_SetAppearance_Struct));
auto eq = (structs::Buyer_SetAppearance_Struct *) outapp->pBuffer;
return;
eq->action = structs::RoF2BuyerActions::BuyerAppearance;
eq->entity_id = emu->buyer_id;
eq->enabled = emu->approval;
dest->FastQueuePacket(&outapp);
safe_delete(in);
break;
}
case Barter_BuyerItemRemove: {
auto emu = (BuyerRemoveItem_Struct *) in->pBuffer;
auto outapp = new EQApplicationPacket(OP_BuyerItems, sizeof(structs::BuyerRemoveItem_Struct));
auto eq = (structs::BuyerRemoveItem_Struct *) outapp->pBuffer;
eq->action = structs::RoF2BuyerActions::BuyerModifyBuyLine;
eq->slot_id = emu->buy_slot_id;
eq->toggle = 0;
dest->FastQueuePacket(&outapp);
safe_delete(in);
break;
}
case Barter_BuyerInspectBegin: {
*(uint32 *) in->pBuffer = structs::RoF2BuyerActions::BuyerInspectBegin;
dest->FastQueuePacket(&in);
break;
}
case Barter_BuyerInspectEnd: {
*(uint32 *) in->pBuffer = structs::RoF2BuyerActions::BuyerInspectEnd;
dest->FastQueuePacket(&in);
break;
}
case Barter_SellerBrowsing: {
*(uint32 *) in->pBuffer = structs::RoF2BuyerActions::BuyerBrowsingBuyLine;
dest->FastQueuePacket(&in);
break;
}
case Barter_BuyerSearchResults: {
BuyerItemSearchResults_Struct bisr{};
auto emu = (BuyerGeneric_Struct *) in->pBuffer;
EQ::Util::MemoryStreamReader ss(
reinterpret_cast<char *>(emu->payload),
in->size - sizeof(BuyerGeneric_Struct)
);
cereal::BinaryInputArchive ar(ss);
ar(bisr);
LogTradingDetail("Sending item search results <green>[{}]", bisr.result_count);
uint32 packet_size = bisr.result_count * sizeof(structs::BuyerItemSearchResultEntry_Struct) + 8;
auto outapp = std::make_unique<EQApplicationPacket>(OP_Barter, packet_size);
auto eq = (char *) outapp->pBuffer;
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerSearchResults);
VARSTRUCT_ENCODE_TYPE(uint32, eq, bisr.result_count);
for (auto const &i: bisr.results) {
strn0cpy(eq, i.item_name, 64);
eq += 64;
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_id);
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_icon);
VARSTRUCT_SKIP_TYPE(uint32, eq);
}
dest->QueuePacket(outapp.get());
break;
}
default: {
LogTradingDetail("Unhandled action <red>[{}]", sub_action);
dest->FastQueuePacket(&in);
}
}
unsigned char *__emu_buffer = in->pBuffer;
in->size = 80;
in->pBuffer = new unsigned char[in->size];
char *OutBuffer = (char *)in->pBuffer;
char Name[64];
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction);
uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer);
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID);
uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer);
VARSTRUCT_DECODE_STRING(Name, Buffer);
VARSTRUCT_ENCODE_STRING(OutBuffer, Name);
OutBuffer = (char *)in->pBuffer + 72;
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle);
delete[] __emu_buffer;
dest->FastQueuePacket(&in, ack_req);
}
ENCODE(OP_BazaarSearch)
@ -682,6 +732,243 @@ namespace RoF2
FINISH_ENCODE();
}
ENCODE(OP_BuyerItems)
{
EQApplicationPacket *inapp = *p;
*p = nullptr;
auto action = *(uint32 *) inapp->pBuffer;
switch (action) {
case Barter_BuyerItemUpdate: {
BuyerLineItems_Struct bl{};
auto emu = (BuyerGeneric_Struct *) inapp->pBuffer;
EQ::Util::MemoryStreamReader ss(
reinterpret_cast<char *>(emu->payload),
inapp->size - sizeof(BuyerGeneric_Struct)
);
cereal::BinaryInputArchive ar(ss);
ar(bl);
//packet size
auto packet_size = bl.item_name.length() + 1 + 34;
for (auto const &b: bl.trade_items) {
packet_size += b.item_name.length() + 1;
packet_size += 12;
}
auto outapp = std::make_unique<EQApplicationPacket>(OP_BuyerItems, packet_size);
char *eq = (char *) outapp->pBuffer;
auto no_trade_items = bl.trade_items.size();
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerModifyBuyLine);
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.slot);
VARSTRUCT_ENCODE_TYPE(uint8, eq, bl.enabled ? 1 : 0);
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.item_id);
VARSTRUCT_ENCODE_STRING(eq, bl.item_name.c_str());
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.item_icon);
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.item_quantity);
VARSTRUCT_ENCODE_TYPE(uint8, eq, bl.item_toggle ? 1 : 0);
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.item_cost);
VARSTRUCT_ENCODE_TYPE(uint32, eq, no_trade_items);
for (int i = 0; i < no_trade_items; i++) {
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.trade_items[i].item_id);
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.trade_items[i].item_quantity);
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.trade_items[i].item_icon);
VARSTRUCT_ENCODE_STRING(eq, bl.trade_items[i].item_name.c_str());
}
dest->QueuePacket(outapp.get());
safe_delete(inapp);
break;
}
case Barter_BuyerInspectBegin: {
auto emu = (BuyerGeneric_Struct *) inapp->pBuffer;
BuyerLineItems_Struct bli{};
EQ::Util::MemoryStreamReader ss(
reinterpret_cast<char *>(emu->payload),
inapp->size - sizeof(BuyerGeneric_Struct)
);
cereal::BinaryInputArchive ar(ss);
ar(bli);
//packet size
auto packet_size = bli.item_name.length() + 1 + 34;
for (auto const &b: bli.trade_items) {
packet_size += b.item_name.length() + 1;
packet_size += 12;
}
auto packet = std::make_unique<EQApplicationPacket>(OP_BuyerItems, packet_size);
char *eq = (char *) packet->pBuffer;
auto no_trade_items = bli.trade_items.size();
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerSendBuyLine);
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.slot);
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.slot);
VARSTRUCT_ENCODE_TYPE(uint8, eq, bli.enabled ? 1 : 0);
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.item_id);
VARSTRUCT_ENCODE_STRING(eq, bli.item_name.c_str());
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.item_icon);
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.item_quantity);
VARSTRUCT_ENCODE_TYPE(uint8, eq, bli.item_toggle ? 1 : 0);
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.item_cost);
VARSTRUCT_ENCODE_TYPE(uint32, eq, no_trade_items);
for (auto const &i: bli.trade_items) {
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_id);
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_quantity);
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_icon);
VARSTRUCT_ENCODE_STRING(eq, i.item_name.c_str());
}
dest->QueuePacket(packet.get());
safe_delete(inapp);
break;
}
case Barter_BuyerSearch: {
BuyerLineSearch_Struct bls{};
auto emu = (BuyerGeneric_Struct *) inapp->pBuffer;
EQ::Util::MemoryStreamReader ss(
reinterpret_cast<char *>(emu->payload),
inapp->size - sizeof(BuyerGeneric_Struct)
);
cereal::BinaryInputArchive ar(ss);
ar(bls);
LogTrading("(RoF2) Barter_BuyerSearch action <green>[{}]", emu->action);
//Calculate size of packet
auto p_size = 0;
p_size += 5 * sizeof(uint32) + 1 * sizeof(uint8);
p_size += bls.search_string.length() + 1;
for (auto const &b: bls.buy_line) {
p_size += 6 * sizeof(uint32) + 2 * sizeof(uint8);
p_size += strlen(b.item_name) + 1;
p_size += b.buyer_name.length() + 1;
for (auto const &d: b.trade_items) {
if (d.item_id != 0) {
p_size += d.item_name.length() + 1;
p_size += 3 * sizeof(uint32);
}
}
p_size += 3 * sizeof(uint32);
}
BuyerBuyLines_Struct bl{};
auto outapp = std::make_unique<EQApplicationPacket>(OP_BuyerItems, p_size);
auto eq = (char *) outapp->pBuffer;
VARSTRUCT_ENCODE_TYPE(uint32, eq, 1);
VARSTRUCT_ENCODE_STRING(eq, bls.search_string.c_str());
VARSTRUCT_ENCODE_TYPE(uint32, eq, bls.transaction_id);
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
VARSTRUCT_ENCODE_TYPE(uint8, eq, 1);
VARSTRUCT_ENCODE_TYPE(uint32, eq, bls.no_items);
for (auto const &b: bls.buy_line) {
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.slot);
VARSTRUCT_ENCODE_TYPE(uint8, eq, 1);
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.item_id);
VARSTRUCT_ENCODE_STRING(eq, b.item_name);
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.item_icon);
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.item_quantity);
VARSTRUCT_ENCODE_TYPE(uint8, eq, 1);
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.item_cost);
auto no_sub_items = b.trade_items.size();
VARSTRUCT_ENCODE_TYPE(uint32, eq, no_sub_items);
for (auto const &i: b.trade_items) {
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_id);
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_quantity);
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_icon);
VARSTRUCT_ENCODE_STRING(eq, i.item_name.c_str());
}
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.buyer_entity_id);
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.buyer_id);
VARSTRUCT_ENCODE_TYPE(uint16, eq, b.buyer_zone_id);
VARSTRUCT_ENCODE_TYPE(uint16, eq, b.buyer_zone_instance_id);
VARSTRUCT_ENCODE_STRING(eq, b.buyer_name.c_str());
}
dest->QueuePacket(outapp.get());
break;
}
case Barter_RemoveFromMerchantWindow: {
auto emu = (BuyerRemoveItemFromMerchantWindow_Struct *) inapp->pBuffer;
emu->action = structs::RoF2BuyerActions::BuyerSendBuyLine;
dest->FastQueuePacket(&inapp);
break;
}
case Barter_BuyerTransactionComplete:
case Barter_SellerTransactionComplete: {
BuyerLineSellItem_Struct blsi{};
auto emu = (BuyerGeneric_Struct *) inapp->pBuffer;
EQ::Util::MemoryStreamReader ss(
reinterpret_cast<char *>(emu->payload),
inapp->size - sizeof(BuyerGeneric_Struct)
);
cereal::BinaryInputArchive ar(ss);
ar(blsi);
//packet size
auto packet_size = strlen(blsi.item_name) * 2 + 2 + 48 + 30 + blsi.seller_name.length() + 1 +
blsi.buyer_name.length() + 1;
for (auto const &b: blsi.trade_items) {
packet_size += b.item_name.length() + 1;
packet_size += 12;
}
auto outapp = std::make_unique<EQApplicationPacket>(OP_BuyerItems, packet_size);
auto eq = (char *) outapp->pBuffer;
switch (action) {
case Barter_BuyerTransactionComplete: {
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerBuyItem);
break;
}
case Barter_SellerTransactionComplete: {
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerSellItem);
break;
}
}
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.sub_action);
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.error_code);
eq += 16;
VARSTRUCT_ENCODE_STRING(eq, blsi.buyer_name.c_str());
VARSTRUCT_ENCODE_STRING(eq, blsi.item_name);
VARSTRUCT_ENCODE_STRING(eq, blsi.seller_name.c_str());
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0xFFFFFFFF);
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0xFFFFFFFF);
eq += 1;
VARSTRUCT_ENCODE_STRING(eq, blsi.item_name);
eq += 9;
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.item_cost);
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.trade_items.size());
for (auto const &i: blsi.trade_items) {
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_quantity);
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
VARSTRUCT_ENCODE_STRING(eq, i.item_name.c_str());
}
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0xFFFFFF);
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.seller_quantity);
dest->QueuePacket(outapp.get());
break;
}
default: {
dest->FastQueuePacket(&inapp);
}
}
}
ENCODE(OP_CancelTrade)
{
ENCODE_LENGTH_EXACT(CancelTrade_Struct);
@ -1690,7 +1977,7 @@ namespace RoF2
uchar *__emu_buffer = in->pBuffer;
ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *) __emu_buffer;
switch(old_item_pkt->PacketType)
switch(old_item_pkt->PacketType)
{
case ItemPacketParcel: {
ParcelMessaging_Struct pms{};
@ -4348,6 +4635,9 @@ namespace RoF2
if (emu->DestructibleObject) {
OtherData = OtherData | 0xe1; // Live has 0xe1 for OtherData
}
if (emu->buyer) {
OtherData = OtherData | 0x01;
}
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData);
// float EmitterScalingRadius
@ -4459,7 +4749,7 @@ namespace RoF2
VARSTRUCT_ENCODE_STRING(Buffer, emu->lastName);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // aatitle
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->guild_show);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->guild_show);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // TempPet
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId);
@ -4664,6 +4954,66 @@ namespace RoF2
FINISH_DIRECT_DECODE();
}
DECODE(OP_Barter)
{
auto action = *(uint32 *) __packet->pBuffer;
switch (action) {
case structs::RoF2BuyerActions::BuyerRemoveItem: {
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
emu->action = Barter_BuyerItemRemove;
LogTradingDetail("(RoF2) Buyer Remove Item");
break;
}
case structs::RoF2BuyerActions::BuyerInspectBegin: {
LogTradingDetail("(RoF2) Buyer Inspect Begin Item");
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
emu->action = Barter_BuyerInspectBegin;
break;
}
case structs::RoF2BuyerActions::BuyerInspectEnd: {
LogTradingDetail("(RoF2) Buyer Inspect End Item ");
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
emu->action = Barter_BuyerInspectEnd;
break;
}
case structs::RoF2BuyerActions::BuyerWelcomeMessage: {
LogTradingDetail("(RoF2) Buyer Welcome Message Update");
SETUP_DIRECT_DECODE(BuyerWelcomeMessageUpdate_Struct, structs::BuyerWelcomeMessageUpdate_Struct);
emu->action = Barter_WelcomeMessageUpdate;
strn0cpy(emu->welcome_message, eq->welcome_message, sizeof(emu->welcome_message));
FINISH_DIRECT_DECODE();
break;
}
case structs::RoF2BuyerActions::BuyerItemInspect: {
SETUP_DIRECT_DECODE(BarterItemSearchLinkRequest_Struct, structs::BarterItemSearchLinkRequest_Struct);
LogTradingDetail("(RoF2) Seller ID <green>[{}] Inspecting Item <green>[{}] from Buyer ID <green>[{}] ",
eq->seller_id,
eq->item_id,
eq->buyer_id
);
emu->action = Barter_BarterItemInspect;
emu->item_id = eq->item_id;
emu->searcher_id = eq->seller_id;
FINISH_DIRECT_DECODE();
break;
}
default: {
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
LogTradingDetail("(RoF2) Pass thru OP_Barter packet action <red>[{}]", emu->action);
}
}
}
DECODE(OP_BazaarSearch)
{
char *Buffer = (char *)__packet->pBuffer;
@ -4742,6 +5092,147 @@ namespace RoF2
FINISH_DIRECT_DECODE();
}
DECODE(OP_BuyerItems)
{
auto action = *(uint32 *) __packet->pBuffer;
switch (action) {
case structs::RoF2BuyerActions::BuyerModifyBuyLine:
case structs::RoF2BuyerActions::BuyerBuyLine: {
BuyerBuyLines_Struct buyer_buy_lines{};
auto buffer = (char *) __packet->pBuffer;
buyer_buy_lines.action = VARSTRUCT_DECODE_TYPE(uint32, buffer);
buyer_buy_lines.no_items = 1;
if (action == structs::RoF2BuyerActions::BuyerBuyLine) {
buyer_buy_lines.no_items = VARSTRUCT_DECODE_TYPE(uint16, buffer);
}
buyer_buy_lines.buy_lines.reserve(buyer_buy_lines.no_items);
for (int i = 0; i < buyer_buy_lines.no_items; i++) {
BuyerLineItems_Struct b{};
b.slot = VARSTRUCT_DECODE_TYPE(uint32, buffer);
b.enabled = VARSTRUCT_DECODE_TYPE(uint8, buffer);
b.item_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
b.item_name = std::string(buffer, strlen(buffer));
buffer += strlen(buffer) + 1;
b.item_icon = VARSTRUCT_DECODE_TYPE(uint32, buffer);
b.item_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
b.item_toggle = VARSTRUCT_DECODE_TYPE(uint8, buffer);
b.item_cost = VARSTRUCT_DECODE_TYPE(uint32, buffer);
auto trade_items = VARSTRUCT_DECODE_TYPE(uint32, buffer);
buyer_buy_lines.buy_lines.push_back(b);
if (trade_items > 0) {
buyer_buy_lines.buy_lines[i].trade_items.reserve(trade_items);
for (int x = 0; x < trade_items; x++) {
BuyerLineTradeItems_Struct blti{};
blti.item_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
blti.item_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
blti.item_icon = VARSTRUCT_DECODE_TYPE(uint32, buffer);
blti.item_name = std::string(buffer, strlen(buffer));
buffer += strlen(buffer) + 1;
buyer_buy_lines.buy_lines[i].trade_items.push_back(blti);
}
}
buffer += 13;
}
buffer = nullptr;
std::stringstream ss{};
cereal::BinaryOutputArchive ar(ss);
{
ar(buyer_buy_lines);
}
auto new_size = sizeof(BuyerGeneric_Struct) + ss.str().length();
auto new_packet = new unsigned char[new_size];
__packet->size = new_size;
__packet->pBuffer = new_packet;
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
emu->action = Barter_BuyerItemUpdate;
if (action == structs::RoF2BuyerActions::BuyerBuyLine) {
emu->action = Barter_BuyerItemStart;
}
memcpy(emu->payload, ss.str().data(), ss.str().length());
__packet->SetOpcode(OP_Barter);
break;
}
case structs::RoF2BuyerActions::BuyerSellItem: {
BuyerLineSellItem_Struct sell_item{};
char *buffer = (char *) __packet->pBuffer;
sell_item.action = VARSTRUCT_DECODE_TYPE(uint32, buffer);
sell_item.purchase_method = VARSTRUCT_DECODE_TYPE(uint32, buffer);
buffer += 4;
sell_item.buyer_entity_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
sell_item.buyer_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
buffer += 11;
sell_item.slot = VARSTRUCT_DECODE_TYPE(uint32, buffer);
sell_item.enabled = VARSTRUCT_DECODE_TYPE(uint8, buffer);
sell_item.item_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
VARSTRUCT_DECODE_STRING(sell_item.item_name, buffer);
sell_item.item_icon = VARSTRUCT_DECODE_TYPE(uint32, buffer);
sell_item.item_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
sell_item.item_toggle = VARSTRUCT_DECODE_TYPE(uint8, buffer);
sell_item.item_cost = VARSTRUCT_DECODE_TYPE(uint32, buffer);
sell_item.no_trade_items = VARSTRUCT_DECODE_TYPE(uint32, buffer);
if (sell_item.no_trade_items > 0) {
sell_item.trade_items.reserve(sell_item.no_trade_items);
for (int x = 0; x < sell_item.no_trade_items; x++) {
BuyerLineTradeItems_Struct blti{};
blti.item_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
blti.item_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
blti.item_icon = VARSTRUCT_DECODE_TYPE(uint32, buffer);
blti.item_name = std::string(buffer, strlen(buffer));
buffer += strlen(buffer) + 1;
sell_item.trade_items.push_back(blti);
}
}
if (sell_item.purchase_method) {
sell_item.buyer_entity_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
sell_item.buyer_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
sell_item.zone_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
sell_item.buyer_name = std::string(buffer, strlen(buffer));
buffer += sell_item.buyer_name.length() + 1;
}
else {
buffer += 13;
}
sell_item.seller_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
buffer += 4;
buffer = nullptr;
std::stringstream ss{};
cereal::BinaryOutputArchive ar(ss);
{
ar(sell_item);
}
auto new_size = sizeof(BuyerGeneric_Struct) + ss.str().length();
auto new_packet = new unsigned char[new_size];
__packet->size = new_size;
__packet->pBuffer = new_packet;
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
emu->action = Barter_SellItem;
memcpy(emu->payload, ss.str().data(), ss.str().length());
__packet->SetOpcode(OP_Barter);
break;
}
}
}
DECODE(OP_CastSpell)
{
DECODE_LENGTH_EXACT(structs::CastSpell_Struct);

View File

@ -48,6 +48,7 @@ E(OP_BeginCast)
E(OP_BlockedBuffs)
E(OP_Buff)
E(OP_BuffCreate)
E(OP_BuyerItems)
E(OP_CancelTrade)
E(OP_CastSpell)
E(OP_ChannelMessage)
@ -149,11 +150,13 @@ D(OP_Animation)
D(OP_ApplyPoison)
D(OP_AugmentInfo)
D(OP_AugmentItem)
D(OP_Barter)
D(OP_BazaarSearch)
D(OP_BlockedBuffs)
D(OP_BookButton)
D(OP_Buff)
D(OP_BuffRemoveRequest)
D(OP_BuyerItems)
D(OP_CastSpell)
D(OP_ChannelMessage)
D(OP_CharacterCreate)

View File

@ -354,15 +354,15 @@ struct Spawn_Struct_Bitfields
/*29*/ unsigned showname:1;
/*30*/ unsigned idleanimationsoff:1; // what we called statue?
/*31*/ unsigned untargetable:1; // bClickThrough
/* do these later
32 unsigned buyer:1;
33 unsigned offline:1;
34 unsigned interactiveobject:1;
35 unsigned flung:1; // hmm this vfunc appears to do stuff with leve and flung variables
36 unsigned title:1;
37 unsigned suffix:1;
38 unsigned padding1:1;
39 unsigned padding2:1;
// byte 5
/*32 unsigned buyer:1;
/*33 unsigned offline:1;
/*34 unsigned interactiveobject:1;
/*35 unsigned flung:1; // hmm this vfunc appears to do stuff with leve and flung variables
/*36 unsigned title:1;
/*37 unsigned suffix:1;
/*38 unsigned padding1:1;
/*39 unsigned padding2:1;
40 unsinged padding3:1;
*/
/*
@ -3107,19 +3107,139 @@ struct EnvDamage2_Struct {
//Bazaar Stuff
enum RoF2BazaarTraderBuyerActions {
Zero = 0,
BeginTraderMode = 1,
EndTraderMode = 2,
PriceUpdate = 3,
EndTransaction = 4,
BazaarSearch = 7,
WelcomeMessage = 9,
BuyTraderItem = 10,
ListTraderItems = 11,
BazaarInspect = 18,
ClickTrader = 28,
ItemMove = 19,
ReconcileItems = 20
Zero = 0,
BeginTraderMode = 1,
EndTraderMode = 2,
PriceUpdate = 3,
EndTransaction = 4,
BazaarSearch = 7,
WelcomeMessage = 9,
BuyTraderItem = 10,
ListTraderItems = 11,
BazaarInspect = 18,
ClickTrader = 28,
ItemMove = 19,
ReconcileItems = 20
};
enum RoF2BuyerActions {
BuyerSearchResults = 0x00,
BuyerBuyLine = 0x06,
BuyerModifyBuyLine = 0x07,
BuyerRemoveItem = 0x08,
BuyerSellItem = 0x09,
BuyerBuyItem = 0x0a,
BuyerInspectBegin = 0x0b,
BuyerInspectEnd = 0x0c,
BuyerAppearance = 0x0d,
BuyerSendBuyLine = 0x0e,
BuyerItemInspect = 0x0f,
BuyerBrowsingBuyLine = 0x10,
BarterWelcomeMessage = 0x11,
BuyerWelcomeMessage = 0x13,
BuyerGreeting = 0x14,
BuyerInventoryFull = 0x16
};
struct BarterItemSearchLinkRequest_Struct {
uint32 action;
uint32 unknown_004;
uint32 seller_id;
uint32 buyer_id;
uint32 unknown_016;
uint32 slot_id; // 0xffffffff main buy line 0x0 trade_item_1, 0x1 trade_item_2
uint32 item_id;
uint32 unknown_028;
};
struct BuyerWelcomeMessageUpdate_Struct {
uint32 action;
char unknown_004[64];
uint32 unknown_068;
char welcome_message[256];
};
struct Buyer_SetAppearance_Struct {
uint32 action;
uint32 entity_id;
char unknown[64];
uint32 enabled;
};
struct BuyerRemoveItem_Struct {
uint32 action;
uint32 unknown004;
uint32 slot_id;
uint32 toggle;
};
struct BuyerLineSellItem_Struct {
uint32 action;
uint32 purchase_method; // 0 direct merchant, 1 via /barter window
uint32 unknown008;
uint32 buyer_entity_id;
uint32 seller_entity_id;
char unknown[15];
uint32 slot;
uint8 enabled;
uint32 item_id;
char item_name[64];
uint32 item_icon;
uint32 item_quantity;
uint8 item_toggle;
uint32 item_cost;
uint32 no_trade_items;
BuyerLineTradeItems_Struct trade_items[10];
char unknown2[13];
uint32 seller_quantity;
};
struct BuyerLineItemsSearch_Struct {
uint32 slot;
uint8 enabled;
uint32 item_id;
char item_name[64];
uint32 item_icon;
uint32 item_quantity;
uint8 item_toggle;
uint32 item_cost;
uint32 buyer_id;
BuyerLineTradeItems_Struct trade_items[MAX_BUYER_COMPENSATION_ITEMS];
};
struct BuyerLineSearch_Struct {
uint32 action;
uint32 no_items;
std::vector<BuyerLineItemsSearch_Struct> buy_line;
};
struct BuyerStart_Struct {
uint32 action;
uint16 no_buyer_lines;
uint32 slot;
uint8 enabled;
uint32 item_id;
char item_name[1]; // vary length
uint32 item_icon;
uint32 item_quantity;
uint8 toggle;
uint32 item_cost;
uint32 no_trade_items;
BuyerLineTradeItems_Struct trade_items[1]; // size is actually no_trade_items. If 0, then this is not in packet
char unknown[13];
};
struct BuyerItemSearchResultEntry_Struct {
char item_name[64];
uint32 item_id;
uint32 item_icon;
uint32 unknown_072;
};
struct BuyerItemSearchResults_Struct {
uint32 action;
uint32 result_count;
BuyerItemSearchResultEntry_Struct results[];
};
enum {

View File

@ -0,0 +1,475 @@
/**
* DO NOT MODIFY THIS FILE
*
* This repository was automatically generated and is NOT to be modified directly.
* Any repository modifications are meant to be made to the repository extending the base.
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_BUYER_BUY_LINES_REPOSITORY_H
#define EQEMU_BASE_BUYER_BUY_LINES_REPOSITORY_H
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
class BaseBuyerBuyLinesRepository {
public:
struct BuyerBuyLines {
uint64_t id;
uint64_t buyer_id;
uint32_t char_id;
int32_t buy_slot_id;
int32_t item_id;
int32_t item_qty;
int32_t item_price;
uint32_t item_icon;
std::string item_name;
};
static std::string PrimaryKey()
{
return std::string("id");
}
static std::vector<std::string> Columns()
{
return {
"id",
"buyer_id",
"char_id",
"buy_slot_id",
"item_id",
"item_qty",
"item_price",
"item_icon",
"item_name",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"id",
"buyer_id",
"char_id",
"buy_slot_id",
"item_id",
"item_qty",
"item_price",
"item_icon",
"item_name",
};
}
static std::string ColumnsRaw()
{
return std::string(Strings::Implode(", ", Columns()));
}
static std::string SelectColumnsRaw()
{
return std::string(Strings::Implode(", ", SelectColumns()));
}
static std::string TableName()
{
return std::string("buyer_buy_lines");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static BuyerBuyLines NewEntity()
{
BuyerBuyLines e{};
e.id = 0;
e.buyer_id = 0;
e.char_id = 0;
e.buy_slot_id = 0;
e.item_id = 0;
e.item_qty = 0;
e.item_price = 0;
e.item_icon = 0;
e.item_name = "";
return e;
}
static BuyerBuyLines GetBuyerBuyLines(
const std::vector<BuyerBuyLines> &buyer_buy_liness,
int buyer_buy_lines_id
)
{
for (auto &buyer_buy_lines : buyer_buy_liness) {
if (buyer_buy_lines.id == buyer_buy_lines_id) {
return buyer_buy_lines;
}
}
return NewEntity();
}
static BuyerBuyLines FindOne(
Database& db,
int buyer_buy_lines_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
buyer_buy_lines_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
BuyerBuyLines e{};
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
e.buyer_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
e.char_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.buy_slot_id = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
e.item_id = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
e.item_qty = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
e.item_price = row[6] ? static_cast<int32_t>(atoi(row[6])) : 0;
e.item_icon = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.item_name = row[8] ? row[8] : "";
return e;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int buyer_buy_lines_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
buyer_buy_lines_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
const BuyerBuyLines &e
)
{
std::vector<std::string> v;
auto columns = Columns();
v.push_back(columns[1] + " = " + std::to_string(e.buyer_id));
v.push_back(columns[2] + " = " + std::to_string(e.char_id));
v.push_back(columns[3] + " = " + std::to_string(e.buy_slot_id));
v.push_back(columns[4] + " = " + std::to_string(e.item_id));
v.push_back(columns[5] + " = " + std::to_string(e.item_qty));
v.push_back(columns[6] + " = " + std::to_string(e.item_price));
v.push_back(columns[7] + " = " + std::to_string(e.item_icon));
v.push_back(columns[8] + " = '" + Strings::Escape(e.item_name) + "'");
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static BuyerBuyLines InsertOne(
Database& db,
BuyerBuyLines e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.buyer_id));
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.buy_slot_id));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.item_qty));
v.push_back(std::to_string(e.item_price));
v.push_back(std::to_string(e.item_icon));
v.push_back("'" + Strings::Escape(e.item_name) + "'");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
Strings::Implode(",", v)
)
);
if (results.Success()) {
e.id = results.LastInsertedID();
return e;
}
e = NewEntity();
return e;
}
static int InsertMany(
Database& db,
const std::vector<BuyerBuyLines> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.buyer_id));
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.buy_slot_id));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.item_qty));
v.push_back(std::to_string(e.item_price));
v.push_back(std::to_string(e.item_icon));
v.push_back("'" + Strings::Escape(e.item_name) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<BuyerBuyLines> All(Database& db)
{
std::vector<BuyerBuyLines> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
BuyerBuyLines e{};
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
e.buyer_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
e.char_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.buy_slot_id = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
e.item_id = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
e.item_qty = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
e.item_price = row[6] ? static_cast<int32_t>(atoi(row[6])) : 0;
e.item_icon = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.item_name = row[8] ? row[8] : "";
all_entries.push_back(e);
}
return all_entries;
}
static std::vector<BuyerBuyLines> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<BuyerBuyLines> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {}",
BaseSelect(),
where_filter
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
BuyerBuyLines e{};
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
e.buyer_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
e.char_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.buy_slot_id = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
e.item_id = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
e.item_qty = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
e.item_price = row[6] ? static_cast<int32_t>(atoi(row[6])) : 0;
e.item_icon = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.item_name = row[8] ? row[8] : "";
all_entries.push_back(e);
}
return all_entries;
}
static int DeleteWhere(Database& db, const std::string &where_filter)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {}",
TableName(),
where_filter
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int Truncate(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"TRUNCATE TABLE {}",
TableName()
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int64 GetMaxId(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COALESCE(MAX({}), 0) FROM {}",
PrimaryKey(),
TableName()
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static int64 Count(Database& db, const std::string &where_filter = "")
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COUNT(*) FROM {} {}",
TableName(),
(where_filter.empty() ? "" : "WHERE " + where_filter)
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const BuyerBuyLines &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.buyer_id));
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.buy_slot_id));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.item_qty));
v.push_back(std::to_string(e.item_price));
v.push_back(std::to_string(e.item_icon));
v.push_back("'" + Strings::Escape(e.item_name) + "'");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<BuyerBuyLines> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.buyer_id));
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.buy_slot_id));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.item_qty));
v.push_back(std::to_string(e.item_price));
v.push_back(std::to_string(e.item_icon));
v.push_back("'" + Strings::Escape(e.item_name) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_BUYER_BUY_LINES_REPOSITORY_H

View File

@ -19,40 +19,46 @@
class BaseBuyerRepository {
public:
struct Buyer {
int32_t charid;
int32_t buyslot;
int32_t itemid;
std::string itemname;
int32_t quantity;
int32_t price;
uint64_t id;
uint32_t char_id;
uint32_t char_entity_id;
std::string char_name;
uint32_t char_zone_id;
uint32_t char_zone_instance_id;
time_t transaction_date;
std::string welcome_message;
};
static std::string PrimaryKey()
{
return std::string("charid");
return std::string("id");
}
static std::vector<std::string> Columns()
{
return {
"charid",
"buyslot",
"itemid",
"itemname",
"quantity",
"price",
"id",
"char_id",
"char_entity_id",
"char_name",
"char_zone_id",
"char_zone_instance_id",
"transaction_date",
"welcome_message",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"charid",
"buyslot",
"itemid",
"itemname",
"quantity",
"price",
"id",
"char_id",
"char_entity_id",
"char_name",
"char_zone_id",
"char_zone_instance_id",
"UNIX_TIMESTAMP(transaction_date)",
"welcome_message",
};
}
@ -93,12 +99,14 @@ public:
{
Buyer e{};
e.charid = 0;
e.buyslot = 0;
e.itemid = 0;
e.itemname = "";
e.quantity = 0;
e.price = 0;
e.id = 0;
e.char_id = 0;
e.char_entity_id = 0;
e.char_name = "";
e.char_zone_id = 0;
e.char_zone_instance_id = 0;
e.transaction_date = 0;
e.welcome_message = "";
return e;
}
@ -109,7 +117,7 @@ public:
)
{
for (auto &buyer : buyers) {
if (buyer.charid == buyer_id) {
if (buyer.id == buyer_id) {
return buyer;
}
}
@ -135,12 +143,14 @@ public:
if (results.RowCount() == 1) {
Buyer e{};
e.charid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.buyslot = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
e.itemid = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
e.itemname = row[3] ? row[3] : "";
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
e.price = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.char_entity_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.char_name = row[3] ? row[3] : "";
e.char_zone_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.char_zone_instance_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.transaction_date = strtoll(row[6] ? row[6] : "-1", nullptr, 10);
e.welcome_message = row[7] ? row[7] : "";
return e;
}
@ -174,12 +184,13 @@ public:
auto columns = Columns();
v.push_back(columns[0] + " = " + std::to_string(e.charid));
v.push_back(columns[1] + " = " + std::to_string(e.buyslot));
v.push_back(columns[2] + " = " + std::to_string(e.itemid));
v.push_back(columns[3] + " = '" + Strings::Escape(e.itemname) + "'");
v.push_back(columns[4] + " = " + std::to_string(e.quantity));
v.push_back(columns[5] + " = " + std::to_string(e.price));
v.push_back(columns[1] + " = " + std::to_string(e.char_id));
v.push_back(columns[2] + " = " + std::to_string(e.char_entity_id));
v.push_back(columns[3] + " = '" + Strings::Escape(e.char_name) + "'");
v.push_back(columns[4] + " = " + std::to_string(e.char_zone_id));
v.push_back(columns[5] + " = " + std::to_string(e.char_zone_instance_id));
v.push_back(columns[6] + " = FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
v.push_back(columns[7] + " = '" + Strings::Escape(e.welcome_message) + "'");
auto results = db.QueryDatabase(
fmt::format(
@ -187,7 +198,7 @@ public:
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.charid
e.id
)
);
@ -201,12 +212,14 @@ public:
{
std::vector<std::string> v;
v.push_back(std::to_string(e.charid));
v.push_back(std::to_string(e.buyslot));
v.push_back(std::to_string(e.itemid));
v.push_back("'" + Strings::Escape(e.itemname) + "'");
v.push_back(std::to_string(e.quantity));
v.push_back(std::to_string(e.price));
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.char_entity_id));
v.push_back("'" + Strings::Escape(e.char_name) + "'");
v.push_back(std::to_string(e.char_zone_id));
v.push_back(std::to_string(e.char_zone_instance_id));
v.push_back("FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
v.push_back("'" + Strings::Escape(e.welcome_message) + "'");
auto results = db.QueryDatabase(
fmt::format(
@ -217,7 +230,7 @@ public:
);
if (results.Success()) {
e.charid = results.LastInsertedID();
e.id = results.LastInsertedID();
return e;
}
@ -236,12 +249,14 @@ public:
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.charid));
v.push_back(std::to_string(e.buyslot));
v.push_back(std::to_string(e.itemid));
v.push_back("'" + Strings::Escape(e.itemname) + "'");
v.push_back(std::to_string(e.quantity));
v.push_back(std::to_string(e.price));
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.char_entity_id));
v.push_back("'" + Strings::Escape(e.char_name) + "'");
v.push_back(std::to_string(e.char_zone_id));
v.push_back(std::to_string(e.char_zone_instance_id));
v.push_back("FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
v.push_back("'" + Strings::Escape(e.welcome_message) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@ -275,12 +290,14 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
Buyer e{};
e.charid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.buyslot = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
e.itemid = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
e.itemname = row[3] ? row[3] : "";
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
e.price = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.char_entity_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.char_name = row[3] ? row[3] : "";
e.char_zone_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.char_zone_instance_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.transaction_date = strtoll(row[6] ? row[6] : "-1", nullptr, 10);
e.welcome_message = row[7] ? row[7] : "";
all_entries.push_back(e);
}
@ -305,12 +322,14 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
Buyer e{};
e.charid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.buyslot = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
e.itemid = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
e.itemname = row[3] ? row[3] : "";
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
e.price = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.char_entity_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.char_name = row[3] ? row[3] : "";
e.char_zone_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.char_zone_instance_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.transaction_date = strtoll(row[6] ? row[6] : "-1", nullptr, 10);
e.welcome_message = row[7] ? row[7] : "";
all_entries.push_back(e);
}
@ -385,12 +404,14 @@ public:
{
std::vector<std::string> v;
v.push_back(std::to_string(e.charid));
v.push_back(std::to_string(e.buyslot));
v.push_back(std::to_string(e.itemid));
v.push_back("'" + Strings::Escape(e.itemname) + "'");
v.push_back(std::to_string(e.quantity));
v.push_back(std::to_string(e.price));
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.char_entity_id));
v.push_back("'" + Strings::Escape(e.char_name) + "'");
v.push_back(std::to_string(e.char_zone_id));
v.push_back(std::to_string(e.char_zone_instance_id));
v.push_back("FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
v.push_back("'" + Strings::Escape(e.welcome_message) + "'");
auto results = db.QueryDatabase(
fmt::format(
@ -413,12 +434,14 @@ public:
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.charid));
v.push_back(std::to_string(e.buyslot));
v.push_back(std::to_string(e.itemid));
v.push_back("'" + Strings::Escape(e.itemname) + "'");
v.push_back(std::to_string(e.quantity));
v.push_back(std::to_string(e.price));
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.char_entity_id));
v.push_back("'" + Strings::Escape(e.char_name) + "'");
v.push_back(std::to_string(e.char_zone_id));
v.push_back(std::to_string(e.char_zone_instance_id));
v.push_back("FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
v.push_back("'" + Strings::Escape(e.welcome_message) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}

View File

@ -0,0 +1,439 @@
/**
* DO NOT MODIFY THIS FILE
*
* This repository was automatically generated and is NOT to be modified directly.
* Any repository modifications are meant to be made to the repository extending the base.
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_BUYER_TRADE_ITEMS_REPOSITORY_H
#define EQEMU_BASE_BUYER_TRADE_ITEMS_REPOSITORY_H
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
class BaseBuyerTradeItemsRepository {
public:
struct BuyerTradeItems {
uint64_t id;
uint64_t buyer_buy_lines_id;
int32_t item_id;
int32_t item_qty;
int32_t item_icon;
std::string item_name;
};
static std::string PrimaryKey()
{
return std::string("id");
}
static std::vector<std::string> Columns()
{
return {
"id",
"buyer_buy_lines_id",
"item_id",
"item_qty",
"item_icon",
"item_name",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"id",
"buyer_buy_lines_id",
"item_id",
"item_qty",
"item_icon",
"item_name",
};
}
static std::string ColumnsRaw()
{
return std::string(Strings::Implode(", ", Columns()));
}
static std::string SelectColumnsRaw()
{
return std::string(Strings::Implode(", ", SelectColumns()));
}
static std::string TableName()
{
return std::string("buyer_trade_items");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static BuyerTradeItems NewEntity()
{
BuyerTradeItems e{};
e.id = 0;
e.buyer_buy_lines_id = 0;
e.item_id = 0;
e.item_qty = 0;
e.item_icon = 0;
e.item_name = "0";
return e;
}
static BuyerTradeItems GetBuyerTradeItems(
const std::vector<BuyerTradeItems> &buyer_trade_itemss,
int buyer_trade_items_id
)
{
for (auto &buyer_trade_items : buyer_trade_itemss) {
if (buyer_trade_items.id == buyer_trade_items_id) {
return buyer_trade_items;
}
}
return NewEntity();
}
static BuyerTradeItems FindOne(
Database& db,
int buyer_trade_items_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
buyer_trade_items_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
BuyerTradeItems e{};
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
e.buyer_buy_lines_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
e.item_id = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
e.item_qty = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
e.item_icon = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
e.item_name = row[5] ? row[5] : "0";
return e;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int buyer_trade_items_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
buyer_trade_items_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
const BuyerTradeItems &e
)
{
std::vector<std::string> v;
auto columns = Columns();
v.push_back(columns[1] + " = " + std::to_string(e.buyer_buy_lines_id));
v.push_back(columns[2] + " = " + std::to_string(e.item_id));
v.push_back(columns[3] + " = " + std::to_string(e.item_qty));
v.push_back(columns[4] + " = " + std::to_string(e.item_icon));
v.push_back(columns[5] + " = '" + Strings::Escape(e.item_name) + "'");
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static BuyerTradeItems InsertOne(
Database& db,
BuyerTradeItems e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.buyer_buy_lines_id));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.item_qty));
v.push_back(std::to_string(e.item_icon));
v.push_back("'" + Strings::Escape(e.item_name) + "'");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
Strings::Implode(",", v)
)
);
if (results.Success()) {
e.id = results.LastInsertedID();
return e;
}
e = NewEntity();
return e;
}
static int InsertMany(
Database& db,
const std::vector<BuyerTradeItems> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.buyer_buy_lines_id));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.item_qty));
v.push_back(std::to_string(e.item_icon));
v.push_back("'" + Strings::Escape(e.item_name) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<BuyerTradeItems> All(Database& db)
{
std::vector<BuyerTradeItems> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
BuyerTradeItems e{};
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
e.buyer_buy_lines_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
e.item_id = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
e.item_qty = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
e.item_icon = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
e.item_name = row[5] ? row[5] : "0";
all_entries.push_back(e);
}
return all_entries;
}
static std::vector<BuyerTradeItems> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<BuyerTradeItems> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {}",
BaseSelect(),
where_filter
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
BuyerTradeItems e{};
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
e.buyer_buy_lines_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
e.item_id = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
e.item_qty = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
e.item_icon = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
e.item_name = row[5] ? row[5] : "0";
all_entries.push_back(e);
}
return all_entries;
}
static int DeleteWhere(Database& db, const std::string &where_filter)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {}",
TableName(),
where_filter
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int Truncate(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"TRUNCATE TABLE {}",
TableName()
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int64 GetMaxId(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COALESCE(MAX({}), 0) FROM {}",
PrimaryKey(),
TableName()
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static int64 Count(Database& db, const std::string &where_filter = "")
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COUNT(*) FROM {} {}",
TableName(),
(where_filter.empty() ? "" : "WHERE " + where_filter)
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const BuyerTradeItems &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.buyer_buy_lines_id));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.item_qty));
v.push_back(std::to_string(e.item_icon));
v.push_back("'" + Strings::Escape(e.item_name) + "'");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<BuyerTradeItems> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.buyer_buy_lines_id));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.item_qty));
v.push_back(std::to_string(e.item_icon));
v.push_back("'" + Strings::Escape(e.item_name) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_BUYER_TRADE_ITEMS_REPOSITORY_H

View File

@ -0,0 +1,356 @@
#ifndef EQEMU_BUYER_BUY_LINES_REPOSITORY_H
#define EQEMU_BUYER_BUY_LINES_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_buyer_buy_lines_repository.h"
#include "buyer_trade_items_repository.h"
#include "character_data_repository.h"
#include "buyer_repository.h"
#include "../eq_packet_structs.h"
class BuyerBuyLinesRepository: public BaseBuyerBuyLinesRepository {
public:
/**
* This file was auto generated and can be modified and extended upon
*
* Base repository methods are automatically
* generated in the "base" version of this repository. The base repository
* is immutable and to be left untouched, while methods in this class
* are used as extension methods for more specific persistence-layer
* accessors or mutators.
*
* Base Methods (Subject to be expanded upon in time)
*
* Note: Not all tables are designed appropriately to fit functionality with all base methods
*
* InsertOne
* UpdateOne
* DeleteOne
* FindOne
* GetWhere(std::string where_filter)
* DeleteWhere(std::string where_filter)
* InsertMany
* All
*
* Example custom methods in a repository
*
* BuyerBuyLinesRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* BuyerBuyLinesRepository::GetWhereNeverExpires()
* BuyerBuyLinesRepository::GetWhereXAndY()
* BuyerBuyLinesRepository::DeleteWhereXAndY()
*
* Most of the above could be covered by base methods, but if you as a developer
* find yourself re-using logic for other parts of the code, its best to just make a
* method that can be re-used easily elsewhere especially if it can use a base repository
* method and encapsulate filters there
*/
// Custom extended repository methods here
struct WelcomeData_Struct {
uint32 count_of_buyers;
uint32 count_of_items;
};
static int CreateBuyLine(Database& db, const BuyerLineItems_Struct b, uint32 char_id)
{
auto buyer = BuyerRepository::GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1", char_id));
if (buyer.empty()){
return 0;
}
BuyerBuyLinesRepository::BuyerBuyLines buy_lines{};
buy_lines.id = 0;
buy_lines.buyer_id = buyer.front().id;
buy_lines.char_id = char_id;
buy_lines.buy_slot_id = b.slot;
buy_lines.item_id = b.item_id;
buy_lines.item_name = b.item_name;
buy_lines.item_icon = b.item_icon;
buy_lines.item_qty = b.item_quantity;
buy_lines.item_price = b.item_cost;
auto e = InsertOne(db, buy_lines);
std::vector<BuyerTradeItemsRepository::BuyerTradeItems> queue;
for (auto const &r: b.trade_items) {
BuyerTradeItemsRepository::BuyerTradeItems bti{};
bti.id = 0;
bti.buyer_buy_lines_id = e.id;
bti.item_id = r.item_id;
bti.item_qty = r.item_quantity;
bti.item_icon = r.item_icon;
bti.item_name = r.item_name;
if (bti.item_id) {
queue.push_back(bti);
}
}
if (!queue.empty()) {
BuyerTradeItemsRepository::InsertMany(db, queue);
}
return e.id;
}
static int ModifyBuyLine(Database& db, const BuyerLineItems_Struct b, uint32 char_id)
{
auto b_lines = GetWhere(db, fmt::format("`char_id` = '{}' AND `buy_slot_id` = '{}';", char_id, b.slot));
if (b_lines.empty() || b_lines.size() > 1){
return 0;
}
auto b_line = b_lines.front();
b_line.item_qty = b.item_quantity;
b_line.item_price = b.item_cost;
b_line.item_icon = b.item_icon;
auto e = UpdateOne(db, b_line);
std::vector<BuyerTradeItemsRepository::BuyerTradeItems> queue;
BuyerTradeItemsRepository::DeleteWhere(db, fmt::format("`buyer_buy_lines_id` = '{}';", b_line.id));
for (auto const &r: b.trade_items) {
BuyerTradeItemsRepository::BuyerTradeItems bti{};
bti.id = 0;
bti.buyer_buy_lines_id = b_line.id;
bti.item_id = r.item_id;
bti.item_qty = r.item_quantity;
bti.item_icon = r.item_icon;
bti.item_name = r.item_name;
if (bti.item_id) {
queue.push_back(bti);
}
}
if (!queue.empty()) {
BuyerTradeItemsRepository::InsertMany(db, queue);
}
return 1;
}
static bool DeleteBuyLine(Database &db, uint32 char_id, int32 slot_id = 0xffffffff)
{
std::vector<BuyerBuyLines> buylines{};
if (slot_id == 0xffffffff) {
auto buylines = GetWhere(db, fmt::format("`char_id` = '{}'", char_id));
DeleteWhere(db, fmt::format("`char_id` = '{}'", char_id));
}
else {
auto buylines = GetWhere(db, fmt::format("`char_id` = '{}' AND `buy_slot_id` = '{}'", char_id, slot_id));
DeleteWhere(db, fmt::format("`char_id` = '{}' AND `buy_slot_id` = '{}'", char_id, slot_id));
}
if (buylines.empty()) {
return 0;
}
std::vector<std::string> buyline_ids{};
for (auto const &bl: buylines) {
buyline_ids.push_back((std::to_string(bl.id)));
}
if (!buyline_ids.empty()) {
BuyerTradeItemsRepository::DeleteWhere(
db,
fmt::format(
"`buyer_buy_lines_id` IN({})",
Strings::Implode(", ", buyline_ids)
)
);
}
return 1;
}
static std::vector<BuyerLineItems_Struct> GetBuyLines(Database &db, uint32 char_id)
{
std::vector<BuyerLineItems_Struct> all_entries{};
auto buy_line = GetWhere(db, fmt::format("`char_id` = '{}';", char_id));
if (buy_line.empty()){
return all_entries;
}
auto buy_line_trade_items = BuyerTradeItemsRepository::GetWhere(
db,
fmt::format(
"`buyer_buy_lines_id` IN (SELECT b.id FROM buyer_buy_lines AS b WHERE b.char_id = '{}')",
char_id
)
);
all_entries.reserve(buy_line.size());
for (auto const &l: buy_line) {
BuyerLineItems_Struct bli{};
bli.item_id = l.item_id;
bli.item_cost = l.item_price;
bli.item_quantity = l.item_qty;
bli.slot = l.buy_slot_id;
bli.item_name = l.item_name;
for (auto const &i: GetSubIDs(buy_line_trade_items, l.id)) {
BuyerLineTradeItems_Struct blti{};
blti.item_id = i.item_id;
blti.item_icon = i.item_icon;
blti.item_quantity = i.item_qty;
blti.item_id = i.item_id;
blti.item_name = i.item_name;
bli.trade_items.push_back(blti);
}
all_entries.push_back(bli);
}
return all_entries;
}
static BuyerLineSearch_Struct SearchBuyLines(
Database &db,
std::string &search_string,
uint32 char_id = 0,
uint32 zone_id = 0,
uint32 zone_instance_id = 0
)
{
BuyerLineSearch_Struct all_entries{};
std::string where_clause(fmt::format("`item_name` LIKE \"%{}%\" ", search_string));
if (char_id) {
where_clause += fmt::format("AND `char_id` = '{}' ", char_id);
}
if (zone_id) {
auto buyers = BuyerRepository::GetWhere(
db,
fmt::format(
"`char_zone_id` = '{}' AND char_zone_instance_id = '{}'",
zone_id,
zone_instance_id
)
);
std::vector<std::string> char_ids{};
for (auto const &bl : buyers) {
char_ids.push_back((std::to_string(bl.char_id)));
}
where_clause += fmt::format("AND `char_id` IN ({}) ", Strings::Implode(", ", char_ids));
}
auto buy_line = GetWhere(db, where_clause);
if (buy_line.empty()){
return all_entries;
}
std::vector<std::string> ids{};
std::vector<std::string> char_ids{};
for (auto const &bl : buy_line) {
if (std::find(ids.begin(), ids.end(), std::to_string(bl.id)) == std::end(ids)) {
ids.push_back(std::to_string(bl.id));
}
if (std::find(char_ids.begin(), char_ids.end(), std::to_string(bl.char_id)) == std::end(char_ids)) {
char_ids.push_back((std::to_string(bl.char_id)));
}
}
auto buy_line_trade_items = BuyerTradeItemsRepository::GetWhere(
db,
fmt::format(
"`buyer_buy_lines_id` IN ({});",
Strings::Implode(", ", ids)
)
);
auto char_names = BuyerRepository::GetWhere(
db,
fmt::format(
"`char_id` IN ({});",
Strings::Implode(", ", char_ids)
)
);
all_entries.no_items = buy_line.size();
for (auto const &l: buy_line) {
BuyerLineItemsSearch_Struct blis{};
blis.slot = l.buy_slot_id;
blis.item_id = l.item_id;
blis.item_cost = l.item_price;
blis.item_icon = l.item_icon;
blis.item_quantity = l.item_qty;
blis.buyer_id = l.char_id;
auto it = std::find_if(
char_names.cbegin(),
char_names.cend(),
[&](BuyerRepository::Buyer e) { return e.char_id == l.char_id; }
);
blis.buyer_name = it != char_names.end() ? it->char_name : std::string("");
blis.buyer_entity_id = it != char_names.end() ? it->char_entity_id : 0;
blis.buyer_zone_id = it != char_names.end() ? it->char_zone_id : 0;
blis.buyer_zone_instance_id = it != char_names.end() ? it->char_zone_instance_id : 0;
strn0cpy(blis.item_name, l.item_name.c_str(), sizeof(blis.item_name));
for (auto const &i: GetSubIDs(buy_line_trade_items, l.id)) {
BuyerLineTradeItems_Struct e{};
e.item_id = i.item_id;
e.item_icon = i.item_icon;
e.item_quantity = i.item_qty;
e.item_id = i.item_id;
e.item_name = i.item_name;
blis.trade_items.push_back(e);
}
all_entries.buy_line.push_back(blis);
}
return all_entries;
}
static std::vector<BuyerTradeItemsRepository::BuyerTradeItems>
GetSubIDs(std::vector<BuyerTradeItemsRepository::BuyerTradeItems> &in, uint64 id)
{
std::vector<BuyerTradeItemsRepository::BuyerTradeItems> results{};
std::vector<uint64> indices{};
auto it = in.begin();
while ((it = std::find_if(
it,
in.end(),
[&](BuyerTradeItemsRepository::BuyerTradeItems const &e) {
return e.buyer_buy_lines_id == id;
}
))
!= in.end()
) {
indices.push_back(std::distance(in.begin(), it));
results.push_back(*it);
it++;
}
return results;
}
static WelcomeData_Struct GetWelcomeData(Database &db)
{
WelcomeData_Struct e{};
auto results = db.QueryDatabase("SELECT COUNT(DISTINCT char_id), COUNT(char_id) FROM buyer;");
if (!results.RowCount()) {
return e;
}
auto r = results.begin();
e.count_of_buyers = Strings::ToInt(r[0]);
e.count_of_items = Strings::ToInt(r[1]);
return e;
}
};
#endif //EQEMU_BUYER_BUY_LINES_REPOSITORY_H

View File

@ -4,6 +4,8 @@
#include "../database.h"
#include "../strings.h"
#include "base/base_buyer_repository.h"
#include "base/base_buyer_trade_items_repository.h"
#include "base/base_buyer_buy_lines_repository.h"
class BuyerRepository: public BaseBuyerRepository {
public:
@ -45,6 +47,93 @@ public:
// Custom extended repository methods here
static bool UpdateWelcomeMessage(Database& db, uint32 char_id, const char *message) {
auto const b = GetWhere(db, fmt::format("`char_id` = '{}';", char_id));
if (b.empty()) {
return false;
}
auto buyer = b.front();
buyer.welcome_message = message;
return UpdateOne(db, buyer);
}
static std::string GetWelcomeMessage(Database& db, uint32 char_id) {
auto const b = GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1;", char_id));
if (b.empty()) {
return std::string();
}
return b.front().welcome_message;
}
static int UpdateTransactionDate(Database& db, uint32 char_id, time_t transaction_date) {
auto b = GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1;", char_id));
if (b.empty()) {
return 0;
}
auto e = b.front();
e.transaction_date = transaction_date;
return UpdateOne(db, e);
}
static time_t GetTransactionDate(Database& db, uint32 char_id) {
auto b = GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1;", char_id));
if (b.empty()) {
return 0;
}
auto e = b.front();
return e.transaction_date;
}
static bool DeleteBuyer(Database &db, uint32 char_id)
{
if (char_id == 0) {
Truncate(db);
BaseBuyerBuyLinesRepository::Truncate(db);
BaseBuyerTradeItemsRepository::Truncate(db);
}
else {
auto buyer = GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1;", char_id));
if (buyer.empty()) {
return false;
}
auto buy_lines = BaseBuyerBuyLinesRepository::GetWhere(
db,
fmt::format("`buyer_id` = '{}'", buyer.front().id)
);
if (buy_lines.empty()) {
return false;
}
std::vector<std::string> buy_line_ids{};
for (auto const &bl: buy_lines) {
buy_line_ids.push_back(std::to_string(bl.id));
}
DeleteWhere(db, fmt::format("`char_id` = '{}';", char_id));
BaseBuyerBuyLinesRepository::DeleteWhere(
db,
fmt::format("`id` IN({})", Strings::Implode(", ", buy_line_ids))
);
BaseBuyerTradeItemsRepository::DeleteWhere(
db,
fmt::format(
"`buyer_buy_lines_id` IN({})",
Strings::Implode(", ", buy_line_ids))
);
}
return true;
}
};
#endif //EQEMU_BUYER_REPOSITORY_H

View File

@ -0,0 +1,81 @@
#ifndef EQEMU_BUYER_TRADE_ITEMS_REPOSITORY_H
#define EQEMU_BUYER_TRADE_ITEMS_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_buyer_trade_items_repository.h"
class BuyerTradeItemsRepository: public BaseBuyerTradeItemsRepository {
public:
/**
* This file was auto generated and can be modified and extended upon
*
* Base repository methods are automatically
* generated in the "base" version of this repository. The base repository
* is immutable and to be left untouched, while methods in this class
* are used as extension methods for more specific persistence-layer
* accessors or mutators.
*
* Base Methods (Subject to be expanded upon in time)
*
* Note: Not all tables are designed appropriately to fit functionality with all base methods
*
* InsertOne
* UpdateOne
* DeleteOne
* FindOne
* GetWhere(std::string where_filter)
* DeleteWhere(std::string where_filter)
* InsertMany
* All
*
* Example custom methods in a repository
*
* BuyerTradeItemsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* BuyerTradeItemsRepository::GetWhereNeverExpires()
* BuyerTradeItemsRepository::GetWhereXAndY()
* BuyerTradeItemsRepository::DeleteWhereXAndY()
*
* Most of the above could be covered by base methods, but if you as a developer
* find yourself re-using logic for other parts of the code, its best to just make a
* method that can be re-used easily elsewhere especially if it can use a base repository
* method and encapsulate filters there
*/
// Custom extended repository methods here
static std::vector<BuyerTradeItems> GetTradeItems(Database& db, const uint32 char_id)
{
std::vector<BuyerTradeItems> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"SELECT bti.* "
"FROM buyer_trade_items AS bti "
"INNER JOIN buyer_buy_lines AS bbl ON bti.buyer_buy_lines_id = bbl.id "
"WHERE bbl.char_id = '{}';",
char_id
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
BuyerTradeItems e{};
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
e.buyer_buy_lines_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
e.item_id = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
e.item_qty = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
e.item_icon = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
e.item_name = row[5] ? row[5] : "0";
all_entries.push_back(e);
}
return all_entries;
}
};
#endif //EQEMU_BUYER_TRADE_ITEMS_REPOSITORY_H

View File

@ -814,6 +814,7 @@ RULE_INT(Bazaar, MaxBarterSearchResults, 200, "The maximum results returned in t
RULE_REAL(Bazaar, ParcelDeliveryCostMod, 0.20, "Cost of parcel delivery for a bazaar purchase as a percentage of item cost. Default is 20% of item cost. RoF+ Only.")
RULE_INT(Bazaar, VoucherDeliveryCost, 200, "Number of vouchers for direct delivery for a bazaar purchase. Default is 200 vouchers. RoF+ Only.")
RULE_BOOL(Bazaar, EnableParcelDelivery, true, "Enable bazaar purchases via parcel delivery. Default is True.")
RULE_INT(Bazaar, MaxBuyerInventorySearchResults, 200, "Maximum number of search results when a Buyer searches the global item list. Default is 200. RoF+ Only.")
RULE_CATEGORY_END()
RULE_CATEGORY(Mail)

View File

@ -140,6 +140,7 @@
#define ServerOP_TraderMessaging 0x0120
#define ServerOP_BazaarPurchase 0x0121
#define ServerOP_BuyerMessaging 0x0122
#define ServerOP_InstanceUpdateTime 0x014F
#define ServerOP_AdventureRequest 0x0150

View File

@ -42,7 +42,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9280
#define CURRENT_BINARY_DATABASE_VERSION 9281
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9044
#endif

View File

@ -433,6 +433,7 @@ OP_TraderShop=0x31df
OP_TraderBulkSend=0x6a96
OP_Trader=0x4ef5
OP_Barter=0x243a
OP_BuyerItems=0x1a6a
OP_TraderBuy=0x0000
OP_ShopItem=0x0000
OP_BazaarInspect=0x0000

View File

@ -291,6 +291,8 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
LogInfo("Loading items");
LogInfo("Clearing trader table details");
database.ClearTraderDetails();
database.ClearBuyerDetails();
LogInfo("Clearing buyer table details");
if (!content_db.LoadItems(hotfix_name)) {
LogError("Error: Could not load item data. But ignoring");

View File

@ -1742,7 +1742,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
}
zoneserver_list.SendPacketToBootedZones(pack);
break;
}
case ServerOP_BazaarPurchase: {
@ -1753,9 +1752,40 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
"ServerOP_BazaarPurchase",
in->trader_buy_struct.trader_id
);
return;
}
zoneserver_list.SendPacket(Zones::BAZAAR, pack);
break;
}
case ServerOP_BuyerMessaging: {
auto in = (BuyerMessaging_Struct *)pack->pBuffer;
switch (in->action) {
case Barter_AddToBarterWindow:
case Barter_RemoveFromBarterWindow: {
if (in->buyer_id <= 0) {
LogTrading("World Message <red>[{}] received with invalid buyer_id <red>[{}]",
"ServerOP_BecomeBuyer",
in->buyer_id
);
return;
}
zoneserver_list.SendPacketToBootedZones(pack);
break;
}
case Barter_SellItem: {
zoneserver_list.SendPacket(Zones::BAZAAR, pack);
break;
}
case Barter_FailedTransaction:
case Barter_BuyerTransactionComplete: {
zoneserver_list.SendPacket(in->zone_id, pack);
break;
}
default:
return;
}
}
default: {
LogInfo("Unknown ServerOPcode from zone {:#04x}, size [{}]", pack->opcode, pack->size);

View File

@ -651,7 +651,6 @@ Json::Value ApiGetClientListDetail(EQ::Net::WebsocketServerConnection *connectio
row["base_wis"] = client->GetBaseWIS();
row["become_npc_level"] = client->GetBecomeNPCLevel();
row["boat_id"] = client->GetBoatID();
row["buyer_welcome_message"] = client->GetBuyerWelcomeMessage();
row["calc_atk"] = client->CalcATK();
row["calc_base_mana"] = client->CalcBaseMana();
row["calc_current_weight"] = client->CalcCurrentWeight();

View File

@ -203,7 +203,6 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
port = ntohs(eqs->GetRemotePort());
client_state = CLIENT_CONNECTING;
SetTrader(false);
Buyer = false;
Haste = 0;
SetCustomerID(0);
SetTraderID(0);
@ -386,6 +385,8 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
m_parcel_merchant_engaged = false;
m_parcels.clear();
m_buyer_id = 0;
SetBotPulling(false);
SetBotPrecombat(false);
@ -429,8 +430,9 @@ Client::~Client() {
TraderEndTrader();
}
if(Buyer)
if(IsBuyer()) {
ToggleBuyerMode(false);
}
if(conn_state != ClientConnectFinished) {
LogDebug("Client [{}] was destroyed before reaching the connected state:", GetName());
@ -2163,12 +2165,13 @@ void Client::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
Mob::FillSpawnStruct(ns, ForWho);
// Populate client-specific spawn information
ns->spawn.afk = AFK;
ns->spawn.lfg = LFG; // afk and lfg are cleared on zoning on live
ns->spawn.anon = m_pp.anon;
ns->spawn.gm = GetGM() ? 1 : 0;
ns->spawn.guildID = GuildID();
ns->spawn.trader = IsTrader();
ns->spawn.afk = AFK;
ns->spawn.lfg = LFG; // afk and lfg are cleared on zoning on live
ns->spawn.anon = m_pp.anon;
ns->spawn.gm = GetGM() ? 1 : 0;
ns->spawn.guildID = GuildID();
ns->spawn.trader = IsTrader();
ns->spawn.buyer = IsBuyer();
// ns->spawn.linkdead = IsLD() ? 1 : 0;
// ns->spawn.pvp = GetPVP(false) ? 1 : 0;
ns->spawn.show_name = true;
@ -11993,7 +11996,7 @@ void Client::SendPath(Mob* target)
target->IsClient() &&
(
target->CastToClient()->IsTrader() ||
target->CastToClient()->Buyer
target->CastToClient()->IsBuyer()
)
) {
Message(
@ -12597,3 +12600,104 @@ void Client::RemoveItemBySerialNumber(uint32 serial_number, uint32 quantity)
}
}
}
void Client::AddMoneyToPPWithOverflow(uint64 copper, bool update_client)
{
//I noticed in the ROF2 client that the client auto updates the currency values using overflow
//Therefore, I created this method to ensure that the db matches and clients don't see 10 pp 5 gp
//becoming 9pp 15 gold with the current AddMoneyToPP method.
auto add_pp = copper / 1000;
auto add_gp = (copper - add_pp * 1000) / 100;
auto add_sp = (copper - add_pp * 1000 - add_gp * 100) / 10;
auto add_cp = copper - add_pp * 1000 - add_gp * 100 - add_sp * 10;
m_pp.copper += add_cp;
if (m_pp.copper >= 10) {
m_pp.silver += m_pp.copper / 10;
m_pp.copper = m_pp.copper % 10;
}
m_pp.silver += add_sp;
if (m_pp.silver >= 10) {
m_pp.gold += m_pp.silver / 10;
m_pp.silver = m_pp.silver % 10;
}
m_pp.gold += add_gp;
if (m_pp.gold >= 10) {
m_pp.platinum += m_pp.gold / 10;
m_pp.gold = m_pp.gold % 10;
}
m_pp.platinum += add_pp;
if (update_client) {
SendMoneyUpdate();
}
RecalcWeight();
SaveCurrency();
LogDebug("Client::AddMoneyToPPWithOverflow() [{}] should have: plat:[{}] gold:[{}] silver:[{}] copper:[{}]",
GetName(),
m_pp.platinum,
m_pp.gold,
m_pp.silver,
m_pp.copper
);
}
bool Client::TakeMoneyFromPPWithOverFlow(uint64 copper, bool update_client)
{
int32 remove_pp = copper / 1000;
int32 remove_gp = (copper - remove_pp * 1000) / 100;
int32 remove_sp = (copper - remove_pp * 1000 - remove_gp * 100) / 10;
int32 remove_cp = copper - remove_pp * 1000 - remove_gp * 100 - remove_sp * 10;
uint64 current_money = GetCarriedMoney();
if (copper > current_money) {
return false; //client does not have enough money on them
}
m_pp.copper -= remove_cp;
if (m_pp.copper < 0) {
m_pp.silver -= 1;
m_pp.copper = m_pp.copper + 10;
if (m_pp.copper >= 10) {
m_pp.silver += m_pp.copper / 10;
m_pp.copper = m_pp.copper % 10;
}
}
m_pp.silver -= remove_sp;
if (m_pp.silver < 0) {
m_pp.gold -= 1;
m_pp.silver = m_pp.silver + 10;
if (m_pp.silver >= 10) {
m_pp.gold += m_pp.silver / 10;
m_pp.silver = m_pp.silver % 10;
}
}
m_pp.gold -= remove_gp;
if (m_pp.gold < 0) {
m_pp.platinum -= 1;
m_pp.gold = m_pp.gold + 10;
if (m_pp.gold >= 10) {
m_pp.platinum += m_pp.gold / 10;
m_pp.gold = m_pp.gold % 10;
}
}
m_pp.platinum -= remove_pp;
if (update_client) {
SendMoneyUpdate();
}
SaveCurrency();
RecalcWeight();
return true;
}

View File

@ -71,6 +71,7 @@ namespace EQ
#include "../common/repositories/character_parcels_repository.h"
#include "../common/repositories/trader_repository.h"
#include "../common/guild_base.h"
#include "../common/repositories/buyer_buy_lines_repository.h"
#ifdef _WINDOWS
// since windows defines these within windef.h (which windows.h include)
@ -289,6 +290,7 @@ public:
void TraderPriceUpdate(const EQApplicationPacket *app);
void SendBazaarDone(uint32 trader_id);
void SendBulkBazaarTraders();
void SendBulkBazaarBuyers();
void DoBazaarInspect(const BazaarInspect_Struct &in);
void SendBazaarDeliveryCosts();
static std::string DetermineMoneyString(uint64 copper);
@ -308,8 +310,10 @@ public:
bool TryStacking(EQ::ItemInstance* item, uint8 type = ItemPacketTrade, bool try_worn = true, bool try_cursor = true);
void SendTraderPacket(Client* trader, uint32 Unknown72 = 51);
void SendBuyerPacket(Client* Buyer);
void SendBuyerToBarterWindow(Client* buyer, uint32 action);
GetItems_Struct* GetTraderItems();
void SendBazaarWelcome();
void SendBarterWelcome();
void DyeArmor(EQ::TintProfile* dye);
void DyeArmorBySlot(uint8 slot, uint8 red, uint8 green, uint8 blue, uint8 use_tint = 0x00);
uint8 SlotConvert(uint8 slot,bool bracer=false);
@ -376,14 +380,35 @@ public:
uint32 GetCustomerID() { return customer_id; }
void SetCustomerID(uint32 id) { customer_id = id; }
void SendBuyerResults(char *SearchQuery, uint32 SearchID);
void SetBuyerID(uint32 id) { m_buyer_id = id; }
uint32 GetBuyerID() { return m_buyer_id; }
bool IsBuyer() { return m_buyer_id != 0 ? true : false; }
void SetBarterTime() { m_barter_time = time(nullptr); }
uint32 GetBarterTime() { return m_barter_time; }
void SetBuyerWelcomeMessage(const char* welcome_message);
void SendBuyerGreeting(uint32 char_id);
void SendSellerBrowsing(const std::string &browser);
void SendBuyerMode(bool status);
bool IsInBuyerSpace();
void SendBuyLineUpdate(const BuyerLineItems_Struct &buy_line);
void CheckIfMovedItemIsPartOfBuyLines(uint32 item_id);
void SendBuyerResults(BarterSearchRequest_Struct& bsr);
void ShowBuyLines(const EQApplicationPacket *app);
void SellToBuyer(const EQApplicationPacket *app);
void ToggleBuyerMode(bool TurnOn);
void UpdateBuyLine(const EQApplicationPacket *app);
void ModifyBuyLine(const EQApplicationPacket *app);
void CreateStartingBuyLines(const EQApplicationPacket *app);
void BuyerItemSearch(const EQApplicationPacket *app);
void SetBuyerWelcomeMessage(const char* WelcomeMessage) { BuyerWelcomeMessage = WelcomeMessage; }
const char* GetBuyerWelcomeMessage() { return BuyerWelcomeMessage.c_str(); }
void SendWindowUpdatesToSellerAndBuyer(BuyerLineSellItem_Struct& blsi);
void SendBarterBuyerClientMessage(BuyerLineSellItem_Struct& blsi, BarterBuyerActions action, BarterBuyerSubActions sub_action, BarterBuyerSubActions error_code);
bool BuildBuyLineMap(std::map<uint32, BuylineItemDetails_Struct>& item_map, BuyerBuyLines_Struct& bl);
bool BuildBuyLineMapFromVector(std::map<uint32, BuylineItemDetails_Struct>& item_map, std::vector<BuyerLineItems_Struct>& bl);
void RemoveItemFromBuyLineMap(std::map<uint32, BuylineItemDetails_Struct>& item_map, const BuyerLineItems_Struct& bl);
bool ValidateBuyLineItems(std::map<uint32, BuylineItemDetails_Struct>& item_map);
int64 ValidateBuyLineCost(std::map<uint32, BuylineItemDetails_Struct>& item_map);
bool DoBarterBuyerChecks(BuyerLineSellItem_Struct& sell_line);
bool DoBarterSellerChecks(BuyerLineSellItem_Struct& sell_line);
void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho);
bool ShouldISpawnFor(Client *c) { return !GMHideMe(c) && !IsHoveringForRespawn(); }
@ -827,9 +852,11 @@ public:
void QuestReadBook(const char* text, uint8 type);
void SendMoneyUpdate();
bool TakeMoneyFromPP(uint64 copper, bool update_client = false);
bool TakeMoneyFromPPWithOverFlow(uint64 copper, bool update_client);
bool TakePlatinum(uint32 platinum, bool update_client = false);
void AddMoneyToPP(uint64 copper, bool update_client = false);
void AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool update_client = false);
void AddMoneyToPPWithOverflow(uint64 copper, bool update_client);
void AddPlatinum(uint32 platinu, bool update_client = false);
bool HasMoney(uint64 copper);
uint64 GetCarriedMoney();
@ -1058,6 +1085,8 @@ public:
int32 GetItemIDAt(int16 slot_id);
int32 GetAugmentIDAt(int16 slot_id, uint8 augslot);
bool PutItemInInventory(int16 slot_id, const EQ::ItemInstance& inst, bool client_update = false);
bool PutItemInInventoryWithStacking(EQ::ItemInstance* inst);
bool FindNumberOfFreeInventorySlotsWithSizeCheck(std::vector<BuyerLineTradeItems_Struct> items);
bool PushItemOnCursor(const EQ::ItemInstance& inst, bool client_update = false);
void SendCursorBuffer();
void DeleteItemInInventory(int16 slot_id, int16 quantity = 0, bool client_update = false, bool update_db = true);
@ -1096,7 +1125,6 @@ public:
uint16 GetTraderID() { return trader_id; }
void SetTraderID(uint16 id) { trader_id = id; }
inline bool IsBuyer() const { return(Buyer); }
eqFilterMode GetFilter(eqFilterType filter_id) const { return ClientFilters[filter_id]; }
void SetFilter(eqFilterType filter_id, eqFilterMode filter_mode) { ClientFilters[filter_id] = filter_mode; }
@ -1913,8 +1941,8 @@ private:
uint8 firstlogon;
uint32 mercid; // current merc
uint8 mercSlot; // selected merc slot
bool Buyer;
std::string BuyerWelcomeMessage;
uint32 m_buyer_id;
uint32 m_barter_time;
int32 m_parcel_platinum;
int32 m_parcel_gold;
int32 m_parcel_silver;

View File

@ -65,6 +65,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/repositories/account_repository.h"
#include "../common/repositories/character_corpses_repository.h"
#include "../common/repositories/guild_tributes_repository.h"
#include "../common/repositories/buyer_buy_lines_repository.h"
#include "../common/events/player_event_logs.h"
#include "../common/repositories/character_stats_record_repository.h"
@ -758,8 +759,6 @@ void Client::CompleteConnect()
entity_list.SendIllusionWearChange(this);
entity_list.SendTraders(this);
SendWearChangeAndLighting(EQ::textures::LastTexture);
Mob* pet = GetPet();
if (pet) {
@ -3759,116 +3758,104 @@ void Client::Handle_OP_Barter(const EQApplicationPacket *app)
}
char* Buf = (char *)app->pBuffer;
auto in = (BuyerGeneric_Struct *)app->pBuffer;
// The first 4 bytes of the packet determine the action. A lot of Barter packets require the
// packet the client sent, sent back to it as an acknowledgement.
//
uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buf);
//uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buf);
switch (Action)
{
switch (in->action) {
case Barter_BuyerSearch:
{
BuyerItemSearch(app);
break;
}
case Barter_SellerSearch:
{
BarterSearchRequest_Struct *bsr = (BarterSearchRequest_Struct*)app->pBuffer;
SendBuyerResults(bsr->SearchString, bsr->SearchID);
break;
}
case Barter_BuyerModeOn:
{
if (!IsTrader()) {
ToggleBuyerMode(true);
case Barter_BuyerSearch: {
BuyerItemSearch(app);
break;
}
else {
Buf = (char *)app->pBuffer;
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerModeOff);
Message(Chat::Red, "You cannot be a Trader and Buyer at the same time.");
case Barter_SellerSearch: {
auto bsr = (BarterSearchRequest_Struct *) app->pBuffer;
SendBuyerResults(*bsr);
break;
}
QueuePacket(app);
break;
}
case Barter_BuyerModeOff:
{
QueuePacket(app);
ToggleBuyerMode(false);
break;
}
case Barter_BuyerModeOn: {
if (!IsTrader()) {
ToggleBuyerMode(true);
}
else {
ToggleBuyerMode(false);
Message(Chat::Red, "You cannot be a Trader and Buyer at the same time.");
}
break;
}
case Barter_BuyerItemUpdate:
{
UpdateBuyLine(app);
break;
}
case Barter_BuyerModeOff: {
ToggleBuyerMode(false);
break;
}
case Barter_BuyerItemRemove:
{
BuyerRemoveItem_Struct* bris = (BuyerRemoveItem_Struct*)app->pBuffer;
database.RemoveBuyLine(CharacterID(), bris->BuySlot);
QueuePacket(app);
break;
}
case Barter_BuyerItemUpdate: {
ModifyBuyLine(app);
break;
}
case Barter_SellItem:
{
SellToBuyer(app);
break;
}
case Barter_BuyerItemStart: {
CreateStartingBuyLines(app);
break;
}
case Barter_BuyerInspectBegin:
{
ShowBuyLines(app);
break;
}
case Barter_BuyerItemRemove: {
auto bris = (BuyerRemoveItem_Struct *) app->pBuffer;
BuyerBuyLinesRepository::DeleteBuyLine(database, CharacterID(), bris->buy_slot_id);
QueuePacket(app);
break;
}
case Barter_BuyerInspectEnd:
{
BuyerInspectRequest_Struct* bir = (BuyerInspectRequest_Struct*)app->pBuffer;
Client *Buyer = entity_list.GetClientByID(bir->BuyerID);
if (Buyer)
Buyer->WithCustomer(0);
case Barter_SellItem: {
SellToBuyer(app);
break;
}
break;
}
case Barter_BuyerInspectBegin: {
ShowBuyLines(app);
break;
}
case Barter_BarterItemInspect:
{
BarterItemSearchLinkRequest_Struct* bislr = (BarterItemSearchLinkRequest_Struct*)app->pBuffer;
case Barter_BuyerInspectEnd: {
auto bir = (BuyerInspectRequest_Struct *) app->pBuffer;
auto buyer = entity_list.GetClientByID(bir->buyer_id);
if (buyer) {
buyer->WithCustomer(0);
}
const EQ::ItemData* item = database.GetItem(bislr->ItemID);
break;
}
case Barter_BarterItemInspect: {
auto bislr = (BarterItemSearchLinkRequest_Struct *) app->pBuffer;
const EQ::ItemData *item = database.GetItem(bislr->item_id);
if (!item)
Message(Chat::Red, "Error: This item does not exist!");
else
{
EQ::ItemInstance* inst = database.CreateItem(item);
if (inst)
{
if (!item) {
Message(Chat::Red, "Error: This item does not exist!");
return;
}
EQ::ItemInstance *inst = database.CreateItem(item);
if (inst) {
SendItemPacket(0, inst, ItemPacketViewLink);
safe_delete(inst);
}
}
break;
}
case Barter_Welcome:
{
//SendBazaarWelcome();
SendBarterWelcome();
break;
}
case Barter_WelcomeMessageUpdate:
{
BuyerWelcomeMessageUpdate_Struct* bwmu = (BuyerWelcomeMessageUpdate_Struct*)app->pBuffer;
SetBuyerWelcomeMessage(bwmu->WelcomeMessage);
case Barter_WelcomeMessageUpdate: {
auto bwmu = (BuyerWelcomeMessageUpdate_Struct *) app->pBuffer;
SetBuyerWelcomeMessage(bwmu->welcome_message);
break;
}
@ -3892,15 +3879,20 @@ void Client::Handle_OP_Barter(const EQApplicationPacket *app)
break;
}
case Barter_Unknown23:
case Barter_Greeting:
{
// Sent by SoD client for no discernible reason.
auto data = (BuyerGreeting_Struct *)app->pBuffer;
SendBuyerGreeting(data->buyer_id);
}
case Barter_OpenBarterWindow:
{
SendBulkBazaarBuyers();
break;
}
default:
Message(Chat::Red, "Unrecognised Barter action.");
LogTrading("Unrecognised Barter Action [{}]", Action);
LogTrading("Unrecognised Barter Action [{}]", in->action);
}
}
@ -4982,6 +4974,10 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
TraderEndTrader();
}
if (IsBuyer()) {
ToggleBuyerMode(false);
}
/* Break Hide if moving without sneaking and set rewind timer if moved */
if ((hidden || improved_hidden) && !sneaking) {
hidden = false;
@ -15640,7 +15636,7 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
break;
}
case TraderOn: {
if (Buyer) {
if (IsBuyer()) {
TraderEndTrader();
Message(Chat::Red, "You cannot be a Trader and Buyer at the same time.");
return;
@ -15686,7 +15682,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
auto trader = entity_list.GetClientByID(in->trader_id);
switch (in->method) {
case ByVendor: {
case BazaarByVendor: {
if (trader) {
LogTrading("Buy item directly from vendor id <green>[{}] item_id <green>[{}] quantity <green>[{}] "
"serial_number <green>[{}]",
@ -15699,7 +15695,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
}
break;
}
case ByParcel: {
case BazaarByParcel: {
if (!RuleB(Parcel, EnableParcelMerchants) || !RuleB(Bazaar, EnableParcelDelivery)) {
LogTrading(
"Bazaar purchase attempt by parcel delivery though 'Parcel:EnableParcelMerchants' or "
@ -15709,7 +15705,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
Chat::Yellow,
"The bazaar parcel delivey system is not enabled on this server. Please visit the vendor directly in the Bazaar."
);
in->method = ByParcel;
in->method = BazaarByParcel;
in->sub_action = Failed;
TradeRequestFailed(app);
return;
@ -15724,7 +15720,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
BuyTraderItemOutsideBazaar(in, app);
break;
}
case ByDirectToInventory: {
case BazaarByDirectToInventory: {
if (!RuleB(Parcel, EnableDirectToInventoryDelivery)) {
LogTrading("Bazaar purchase attempt by direct inventory delivery though "
"'Parcel:EnableDirectToInventoryDelivery' not enabled."
@ -15733,7 +15729,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
Chat::Yellow,
"Direct inventory delivey is not enabled on this server. Please visit the vendor directly."
);
in->method = ByDirectToInventory;
in->method = BazaarByDirectToInventory;
in->sub_action = Failed;
TradeRequestFailed(app);
return;
@ -15750,7 +15746,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
Chat::Yellow,
"Direct inventory delivey is not yet implemented. Please visit the vendor directly or purchase via parcel delivery."
);
in->method = ByDirectToInventory;
in->method = BazaarByDirectToInventory;
in->sub_action = Failed;
TradeRequestFailed(app);
break;

View File

@ -1864,6 +1864,10 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
LogInventory("Dest slot [{}] has item [{}] ([{}]) with [{}] charges in it", dst_slot_id, dst_inst->GetItem()->Name, dst_inst->GetItem()->ID, dst_inst->GetCharges());
dstitemid = dst_inst->GetItem()->ID;
}
if (IsBuyer() && srcitemid > 0) {
CheckIfMovedItemIsPartOfBuyLines(srcitemid);
}
if (IsTrader() && srcitemid>0){
EQ::ItemInstance* srcbag;
EQ::ItemInstance* dstbag;
@ -4850,3 +4854,63 @@ bool Client::HasItemOnCorpse(uint32 item_id)
return false;
}
bool Client::PutItemInInventoryWithStacking(EQ::ItemInstance *inst)
{
auto free_id = GetInv().FindFirstFreeSlotThatFitsItem(inst->GetItem());
if (inst->IsStackable()) {
if (TryStacking(inst, ItemPacketTrade, true, false)) {
return true;
}
}
if (free_id != INVALID_INDEX) {
if (PutItemInInventory(free_id, *inst, true)) {
return true;
}
}
return false;
};
bool Client::FindNumberOfFreeInventorySlotsWithSizeCheck(std::vector<BuyerLineTradeItems_Struct> items)
{
uint32 count = 0;
for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) {
if ((((uint64) 1 << i) & GetInv().GetLookup()->PossessionsBitmask) == 0) {
continue;
}
EQ::ItemInstance *inv_item = GetInv().GetItem(i);
if (!inv_item) {
// Found available slot in personal inventory. Fits all sizes
count++;
}
if (count >= items.size()) {
return true;
}
if (inv_item->IsClassBag()) {
for (auto const& item:items) {
auto item_tmp = database.GetItem(item.item_id);
if (EQ::InventoryProfile::CanItemFitInContainer(item_tmp, inv_item->GetItem())) {
int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(i, EQ::invbag::SLOT_BEGIN);
uint8 bag_size = inv_item->GetItem()->BagSlots;
for (uint8 bag_slot = EQ::invbag::SLOT_BEGIN; bag_slot < bag_size; bag_slot++) {
auto bag_item = GetInv().GetItem(base_slot_id + bag_slot);
if (!bag_item) {
// Found a bag slot that fits the item
count++;
}
}
if (count >= items.size()) {
return true;
}
}
}
}
}
return false;
};

View File

@ -4,6 +4,8 @@
#include <cmath>
#include "../common/strings.h"
#include "../common/data_verification.h"
#include <numbers>
#include "../common/types.h"
constexpr float position_eps = 0.0001f;
@ -282,3 +284,54 @@ float CalculateHeadingAngleBetweenPositions(float x1, float y1, float x2, float
return (90.0f - angle + 270.0f) * 511.5f * 0.0027777778f;
}
}
bool IsWithinCircularArc(glm::vec4 arc_center, glm::vec4 point, uint32 arc_offset, uint32 arc_radius, uint32 arc_radius_limit)
{
auto CheckClockwise = [](double v_x, double v_y, double check_x, double check_y) -> bool {
return -v_y * check_x + v_x * check_y >= 0;
};
auto CheckRadiusLimit = [](double check_x, double check_y, uint32 radius, uint32 radius_limit) -> bool {
auto w = check_x * check_x + check_y * check_y;
if (w >= radius_limit * radius_limit && w <= radius * radius) {
return true;
}
return false;
};
auto DegreesToRadians = [](float in) -> double {
return in / 180.0f * std::numbers::pi;
};
auto h = arc_center.w / 512.0f * 360.0f + arc_offset;
auto a = DegreesToRadians(h);
auto vs_x = -arc_radius * cos(a);
auto vs_y = arc_radius * sin(a);
h += 90;
a = DegreesToRadians(h);
auto ve_x = -arc_radius * cos(a);
auto ve_y = arc_radius * sin(a);
double check_x = point.x - arc_center.x;
double check_y = point.y - arc_center.y;
return CheckClockwise(vs_x, vs_y, check_x, check_y) && CheckRadiusLimit(check_x, check_y, arc_radius, arc_radius_limit) && !CheckClockwise(ve_x, ve_y, check_x, check_y);
}
bool IsWithinSquare(glm::vec4 center, uint32 area, glm::vec4 position) {
auto l = std::abs(std::sqrt(area));
if (l <= 0) {
return false;
}
auto x_min = center.x - l;
auto x_max = center.x + l;
auto y_min = center.y - l;
auto y_max = center.y + l;
auto x = position.x;
auto y = position.y;
return x > x_min && x < x_max && y > y_min && y < y_max;
}

View File

@ -23,6 +23,7 @@
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/geometric.hpp>
#include "../common/types.h"
std::string to_string(const glm::vec4 &position);
std::string to_string(const glm::vec3 &position);
@ -62,4 +63,7 @@ bool IsPositionWithinSimpleCylinder(const glm::vec4 &p1, const glm::vec4 &cylind
float CalculateHeadingAngleBetweenPositions(float x1, float y1, float x2, float y2);
bool IsWithinCircularArc(glm::vec4 arc_center, glm::vec4 point, uint32 arc_offset, uint32 arc_radius, uint32 arc_radius_limit);
bool IsWithinSquare(glm::vec4 center, uint32 area, glm::vec4 position);
#endif

View File

@ -413,6 +413,8 @@
#define MAX_ACTIVE_TASKS 6010 //Sorry %3, you already have the maximum number of active tasks.
#define TASK_REQUEST_COOLDOWN_TIMER 6011 //Sorry, %3, but you can't request another task for %4 minutes and %5 seconds.
#define FORAGE_MASTERY 6012 //Your forage mastery has enabled you to find something else!
#define BUYER_WELCOME 6065 //There are %1 Buyers waiting to purchase your loot. Type /barter to search for them, or use /buyer to set up your own Buy Lines.
#define BUYER_GREETING 6070 //%1 greets you, '%2'
#define GUILD_BANK_CANNOT_DEPOSIT 6097 // Cannot deposit this item. Containers must be empty, and only one of each LORE and no NO TRADE or TEMPORARY items may be deposited.
#define GUILD_BANK_FULL 6098 // There is no more room in the Guild Bank.
#define GUILD_BANK_TRANSFERRED 6100 // '%1' transferred to Guild Bank from Deposits.

File diff suppressed because it is too large Load Diff

View File

@ -3997,6 +3997,310 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
break;
}
case ServerOP_BuyerMessaging: {
auto in = (BuyerMessaging_Struct *) pack->pBuffer;
switch (in->action) {
case Barter_AddToBarterWindow: {
auto outapp = std::make_unique<EQApplicationPacket>(
OP_Barter,
sizeof(BuyerAddBuyertoBarterWindow_Struct)
);
auto emu = (BuyerAddBuyertoBarterWindow_Struct *) outapp->pBuffer;
emu->action = Barter_AddToBarterWindow;
emu->buyer_entity_id = in->buyer_entity_id;
emu->buyer_id = in->buyer_id;
emu->zone_id = in->zone_id;
strn0cpy(emu->buyer_name, in->buyer_name, sizeof(emu->buyer_name));
entity_list.QueueClients(nullptr, outapp.get());
break;
}
case Barter_RemoveFromBarterWindow: {
auto outapp = std::make_unique<EQApplicationPacket>(
OP_Barter,
sizeof(BuyerRemoveBuyerFromBarterWindow_Struct)
);
auto emu = (BuyerRemoveBuyerFromBarterWindow_Struct *) outapp->pBuffer;
emu->action = Barter_RemoveFromBarterWindow;
emu->buyer_id = in->buyer_id;
entity_list.QueueClients(nullptr, outapp.get());
break;
}
case Barter_FailedTransaction: {
auto seller = entity_list.GetClientByID(in->seller_entity_id);
auto buyer = entity_list.GetClientByID(in->buyer_entity_id);
BuyerLineSellItem_Struct sell_line{};
sell_line.item_id = in->buy_item_id;
sell_line.item_quantity = in->buy_item_qty;
sell_line.item_cost = in->buy_item_cost;
sell_line.seller_name = in->seller_name;
sell_line.buyer_name = in->buyer_name;
sell_line.seller_quantity = in->seller_quantity;
sell_line.slot = in->slot;
strn0cpy(sell_line.item_name, in->item_name, sizeof(sell_line.item_name));
uint64 total_cost = (uint64) sell_line.item_cost * (uint64) sell_line.seller_quantity;
std::unique_ptr<EQ::ItemInstance> inst(database.CreateItem(in->buy_item_id, in->seller_quantity));
switch (in->sub_action) {
case Barter_FailedBuyerChecks:
case Barter_FailedSellerChecks: {
if (seller) {
LogTradingDetail("Significant barter transaction failure.");
seller->Message(
Chat::Red,
"Significant barter transaction error. Transaction rolled back."
);
seller->SendBarterBuyerClientMessage(
sell_line,
Barter_SellerTransactionComplete,
Barter_Failure,
Barter_Failure
);
if (player_event_logs.IsEventEnabled(PlayerEvent::BARTER_TRANSACTION)) {
PlayerEvent::BarterTransaction e{};
e.status = "Failed Barter Transaction";
e.item_id = sell_line.item_id;
e.item_quantity = sell_line.seller_quantity;
e.item_name = sell_line.item_name;
e.trade_items = sell_line.trade_items;
for (auto &i: e.trade_items) {
i *= sell_line.seller_quantity;
}
e.total_cost = (uint64) sell_line.item_cost * (uint64) in->seller_quantity;
e.buyer_name = sell_line.buyer_name;
e.seller_name = sell_line.seller_name;
RecordPlayerEventLogWithClient(seller, PlayerEvent::BARTER_TRANSACTION, e);
}
}
if (buyer) {
LogError("Significant barter transaction failure. Replacing {} and {} {} to {}",
buyer->DetermineMoneyString(total_cost),
sell_line.seller_quantity,
sell_line.item_name,
buyer->GetCleanName());
buyer->AddMoneyToPPWithOverflow(total_cost, true);
buyer->RemoveItem(sell_line.item_id, sell_line.seller_quantity);
buyer->Message(
Chat::Red,
"Significant barter transaction error. Transaction rolled back."
);
buyer->SendBarterBuyerClientMessage(
sell_line,
Barter_BuyerTransactionComplete,
Barter_Failure,
Barter_Failure
);
if (player_event_logs.IsEventEnabled(PlayerEvent::BARTER_TRANSACTION)) {
PlayerEvent::BarterTransaction e{};
e.status = "Failed Barter Transaction";
e.item_id = sell_line.item_id;
e.item_quantity = sell_line.seller_quantity;
e.item_name = sell_line.item_name;
e.trade_items = sell_line.trade_items;
for (auto &i: e.trade_items) {
i *= sell_line.seller_quantity;
}
e.total_cost = (uint64) sell_line.item_cost * (uint64) in->seller_quantity;
e.buyer_name = sell_line.buyer_name;
e.seller_name = sell_line.seller_name;
RecordPlayerEventLogWithClient(buyer, PlayerEvent::BARTER_TRANSACTION, e);
}
}
break;
}
default: {
if (seller) {
seller->SendBarterBuyerClientMessage(
sell_line,
Barter_SellerTransactionComplete,
Barter_Failure,
Barter_Failure
);
}
if (buyer) {
buyer->SendBarterBuyerClientMessage(
sell_line,
Barter_BuyerTransactionComplete,
Barter_Failure,
Barter_Failure
);
}
}
}
break;
}
case Barter_SellItem: {
auto buyer = entity_list.GetClientByID(in->buyer_entity_id);
if (!buyer) {
in->action = Barter_FailedTransaction;
in->sub_action = Barter_BuyerCouldNotBeFound;
worldserver.SendPacket(pack);
return;
}
BuyerLineSellItem_Struct sell_line{};
sell_line.item_id = in->buy_item_id;
sell_line.item_quantity = in->buy_item_qty;
sell_line.item_cost = in->buy_item_cost;
sell_line.seller_name = in->seller_name;
sell_line.buyer_name = in->buyer_name;
sell_line.buyer_entity_id = in->buyer_entity_id;
sell_line.seller_quantity = in->seller_quantity;
sell_line.slot = in->slot;
strn0cpy(sell_line.item_name, in->item_name, sizeof(sell_line.item_name));
if (!buyer->DoBarterBuyerChecks(sell_line)) {
in->action = Barter_FailedTransaction;
in->sub_action = Barter_FailedBuyerChecks;
worldserver.SendPacket(pack);
break;
}
BuyerLineSellItem_Struct blis{};
blis.enabled = 1;
blis.item_toggle = 1;
blis.item_cost = in->buy_item_cost;
blis.item_id = in->buy_item_id;
blis.item_quantity = in->buy_item_qty;
blis.item_icon = in->buy_item_icon;
blis.slot = in->slot;
blis.seller_quantity = in->seller_quantity;
blis.buyer_entity_id = in->buyer_entity_id;
strn0cpy(blis.item_name, in->item_name, sizeof(blis.item_name));
uint64 total_cost = (uint64) sell_line.item_cost * (uint64) sell_line.seller_quantity;
std::unique_ptr<EQ::ItemInstance> inst(database.CreateItem(in->buy_item_id, in->seller_quantity));
if (inst->IsStackable()) {
if (!buyer->PutItemInInventoryWithStacking(inst.get())) {
buyer->Message(Chat::Red, "Error putting item in your inventory.");
buyer->AddMoneyToPPWithOverflow(total_cost, true);
in->action = Barter_FailedTransaction;
in->sub_action = Barter_FailedBuyerChecks;
worldserver.SendPacket(pack);
break;
}
}
else {
for (int i = 1; i <= sell_line.seller_quantity; i++) {
inst->SetCharges(1);
if (!buyer->PutItemInInventoryWithStacking(inst.get())) {
buyer->Message(Chat::Red, "Error putting item in your inventory.");
buyer->AddMoneyToPPWithOverflow(total_cost, true);
in->action = Barter_FailedTransaction;
in->sub_action = Barter_FailedBuyerChecks;
worldserver.SendPacket(pack);
goto exit_loop;
}
}
}
if (!buyer->TakeMoneyFromPPWithOverFlow(total_cost, false)) {
in->action = Barter_FailedTransaction;
in->sub_action = Barter_FailedBuyerChecks;
worldserver.SendPacket(pack);
break;
}
buyer->SendWindowUpdatesToSellerAndBuyer(blis);
buyer->SendBarterBuyerClientMessage(
sell_line,
Barter_BuyerTransactionComplete,
Barter_Success,
Barter_Success
);
if (player_event_logs.IsEventEnabled(PlayerEvent::BARTER_TRANSACTION)) {
PlayerEvent::BarterTransaction e{};
e.status = "Successful Barter Transaction";
e.item_id = sell_line.item_id;
e.item_quantity = sell_line.seller_quantity;
e.item_name = sell_line.item_name;
e.trade_items = sell_line.trade_items;
for (auto &i: e.trade_items) {
i *= sell_line.seller_quantity;
}
e.total_cost = (uint64) sell_line.item_cost * (uint64) in->seller_quantity;
e.buyer_name = sell_line.buyer_name;
e.seller_name = sell_line.seller_name;
RecordPlayerEventLogWithClient(buyer, PlayerEvent::BARTER_TRANSACTION, e);
}
in->action = Barter_BuyerTransactionComplete;
worldserver.SendPacket(pack);
exit_loop:
break;
}
case Barter_BuyerTransactionComplete: {
auto seller = entity_list.GetClientByID(in->seller_entity_id);
if (!seller) {
in->action = Barter_FailedTransaction;
in->sub_action = Barter_SellerCouldNotBeFound;
worldserver.SendPacket(pack);
return;
}
BuyerLineSellItem_Struct sell_line{};
sell_line.item_id = in->buy_item_id;
sell_line.item_quantity = in->buy_item_qty;
sell_line.item_cost = in->buy_item_cost;
sell_line.seller_name = in->seller_name;
sell_line.buyer_name = in->buyer_name;
sell_line.seller_quantity = in->seller_quantity;
sell_line.slot = in->slot;
strn0cpy(sell_line.item_name, in->item_name, sizeof(sell_line.item_name));
if (!seller->DoBarterSellerChecks(sell_line)) {
in->action = Barter_FailedTransaction;
in->action = Barter_FailedSellerChecks;
worldserver.SendPacket(pack);
return;
}
uint64 total_cost = (uint64) sell_line.item_cost * (uint64) sell_line.seller_quantity;
seller->RemoveItem(in->buy_item_id, in->seller_quantity);
seller->AddMoneyToPPWithOverflow(total_cost, false);
seller->SendBarterBuyerClientMessage(
sell_line,
Barter_SellerTransactionComplete,
Barter_Success,
Barter_Success
);
if (player_event_logs.IsEventEnabled(PlayerEvent::BARTER_TRANSACTION)) {
PlayerEvent::BarterTransaction e{};
e.status = "Successful Barter Transaction";
e.item_id = sell_line.item_id;
e.item_quantity = sell_line.seller_quantity;
e.item_name = sell_line.item_name;
e.trade_items = sell_line.trade_items;
for (auto &i: e.trade_items) {
i *= sell_line.seller_quantity;
}
e.total_cost = (uint64) sell_line.item_cost * (uint64) in->seller_quantity;
e.buyer_name = sell_line.buyer_name;
e.seller_name = sell_line.seller_name;
RecordPlayerEventLogWithClient(seller, PlayerEvent::BARTER_TRANSACTION, e);
}
break;
}
}
}
default: {
LogInfo("Unknown ZS Opcode [{}] size [{}]", (int) pack->opcode, pack->size);
break;

View File

@ -69,6 +69,7 @@
#include "../common/repositories/alternate_currency_repository.h"
#include "../common/repositories/graveyard_repository.h"
#include "../common/repositories/trader_repository.h"
#include "../common/repositories/buyer_repository.h"
#include <time.h>

View File

@ -416,24 +416,6 @@ void ZoneDatabase::UpdateTraderItemPrice(int char_id, uint32 item_id, uint32 cha
}
}
void ZoneDatabase::DeleteBuyLines(uint32 CharID) {
if(CharID==0) {
const std::string query = "DELETE FROM buyer";
auto results = QueryDatabase(query);
if (!results.Success())
LogDebug("[CLIENT] Failed to delete all buyer items data, the error was: [{}]\n",results.ErrorMessage().c_str());
return;
}
std::string query = StringFormat("DELETE FROM buyer WHERE charid = %i", CharID);
auto results = QueryDatabase(query);
if (!results.Success())
LogDebug("[CLIENT] Failed to delete buyer item data for charid: [{}], the error was: [{}]\n",CharID,results.ErrorMessage().c_str());
}
void ZoneDatabase::AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, const char* ItemName, uint32 Quantity, uint32 Price) {
std::string query = StringFormat("REPLACE INTO buyer VALUES(%i, %i, %i, \"%s\", %i, %i)",
CharID, BuySlot, ItemID, ItemName, Quantity, Price);

View File

@ -400,7 +400,6 @@ public:
/* Buyer/Barter */
void AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, const char *ItemName, uint32 Quantity, uint32 Price);
void RemoveBuyLine(uint32 CharID, uint32 BuySlot);
void DeleteBuyLines(uint32 CharID);
void UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity);