mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-16 18:52:22 +00:00
[Logging] Implement Player Event Logging system (#2833)
* Plumbing * Batch processing in world * Cleanup * Cleanup * Update player_event_logs.cpp * Add player zoning event * Use generics * Comments * Add events * Add more events * AA_GAIN, AA_PURCHASE, FORAGE_SUCCESS, FORAGE_FAILURE * FISH_SUCCESS, FISH_FAILURE, ITEM_DESTROY * Add charges to ITEM_DESTROY * WENT_ONLINE, WENT_OFFLINE * LEVEL_GAIN, LEVEL_LOSS * LOOT_ITEM * MERCHANT_PURCHASE * MERCHANT_SELL * SKILL_UP * Add events * Add more events * TASK_ACCEPT, TASK_COMPLETE, and TASK_UPDATE * GROUNDSPAWN_PICKUP * SAY * REZ_ACCEPTED * COMBINE_FAILURE and COMBINE_SUCCESS * DROPPED_ITEM * DEATH * SPLIT_MONEY * TRADER_PURCHASE and TRADER_SELL * DISCOVER_ITEM * Convert GM_COMMAND to use new macro * Convert ZONING event to use macro * Revert some code changes * Revert "Revert some code changes" This reverts commit d53682f997e89a053a660761085913245db91e9d. * Add cereal generation support to repositories * TRADE * Formatting * Cleanup * Relocate discord_manager to discord folder * Discord sending plumbing * Rename UCS's Database class to UCSDatabase to be more specific and not collide with base Database class for repository usage * More discord sending plumbing * More discord message formatting work * More discord formatting work * Discord formatting of events * Format WENT_ONLINE, WENT_OFFLINE * Add merchant purchase event * Handle Discord MERCHANT_SELL formatter * Update player_event_discord_formatter.cpp * Tweaks * Implement retention truncation * Put mutex locking on batch queue, put processor on its own thread * Process on initial bootup * Implement optional QS processing, implement keepalive from world to QS * Reload player event settings when logs are reloaded in game * Set settings defaults * Update player_event_logs.cpp * Update player_event_logs.cpp * Set retention days on boot * Update player_event_logs.cpp * Player Handin Event Testing. Testing player handin stuff. * Cleanup. * Finish NPC Handin. * set a reference to the client inside of the trade object as well for plugins to process * Fix for windows _inline * Bump to cpp20 default, ignore excessive warnings on windows * Bump FMT to 6.1.2 for cpp20 compat and swap fmt::join for Strings::Join * Windows compile fixes * Update CMakeLists.txt * Update CMakeLists.txt * Update CMakeLists.txt * Create 2022_12_19_player_events_tables.sql * [Formatters] Work on Discord Formatters * Handin money. * Format header * [Formatters] Work on Discord Formatters * Format * Format * [Formatters] More Formatter work, need to test further. * [Formatters] More Work on Formatters. * Add missing #endif * [Formatters] Work on Formatters, fix Bot formatting in ^create help * NPC Handin Discord Formatter * Update player_event_logs.cpp * Discover Item Discord Formatter * Dropped Item Discord Formatter * Split Money Discord Formatter * Trader Discord Formatters * Cleanup. * Trade Event Discord Formatter Groundwork * SAY don't record GM commands * GM_Command don't record #help * Update player_event_logs.cpp * Fill in more event data * Post rebase fixes * Post rebase fix * Discord formatting adjustments * Add event deprecation or unimplemented tag support * Trade events * Add return money and sanity checks. * Update schema * Update ucs.cpp * Update client.cpp * Update 2022_12_19_player_events_tables.sql * Implement archive single line * Replace hackers table and functions with PossibleHack player event * Replace very old eventlog table since the same events are covered by player event logs * Update bot_command.cpp * Record NPC kill events ALL / Named / Raid * Add BatchEventProcessIntervalSeconds rule * Naming * Update CMakeLists.txt * Update database_schema.h * Remove logging function and methods * DB version * Cleanup SendPlayerHandinEvent --------- Co-authored-by: Kinglykrab <kinglykrab@gmail.com> Co-authored-by: Aeadoin <109764533+Aeadoin@users.noreply.github.com>
This commit is contained in:
+88
-196
@@ -21,6 +21,7 @@
|
||||
#include "../common/rulesys.h"
|
||||
#include "../common/strings.h"
|
||||
#include "../common/misc_functions.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "entity.h"
|
||||
@@ -187,149 +188,9 @@ void Trade::SendItemData(const EQ::ItemInstance* inst, int16 dest_slot_id)
|
||||
}
|
||||
}
|
||||
|
||||
// Audit trade: The part logged is what travels owner -> with
|
||||
void Trade::LogTrade()
|
||||
Mob *Trade::GetOwner() const
|
||||
{
|
||||
Mob* with = With();
|
||||
if (!owner->IsClient() || !with)
|
||||
return; // Should never happen
|
||||
|
||||
Client* trader = owner->CastToClient();
|
||||
bool logtrade = false;
|
||||
int admin_level = 0;
|
||||
uint8 item_count = 0;
|
||||
|
||||
if (zone->tradevar != 0) {
|
||||
for (uint16 i = EQ::invslot::TRADE_BEGIN; i <= EQ::invslot::TRADE_END; i++) {
|
||||
if (trader->GetInv().GetItem(i))
|
||||
item_count++;
|
||||
}
|
||||
|
||||
if ((cp + sp + gp + pp) || item_count) {
|
||||
admin_level = trader->Admin();
|
||||
} else {
|
||||
admin_level = (AccountStatus::Max + 1);
|
||||
}
|
||||
|
||||
if (zone->tradevar == 7) {
|
||||
logtrade = true;
|
||||
} else if (
|
||||
admin_level >= AccountStatus::Steward &&
|
||||
admin_level < AccountStatus::ApprenticeGuide
|
||||
) {
|
||||
if (zone->tradevar < 8 && zone->tradevar > 5) {
|
||||
logtrade = true;
|
||||
}
|
||||
} else if (admin_level <= AccountStatus::ApprenticeGuide) {
|
||||
if (zone->tradevar < 8 && zone->tradevar > 4) {
|
||||
logtrade = true;
|
||||
}
|
||||
} else if (admin_level <= AccountStatus::QuestTroupe) {
|
||||
if (zone->tradevar < 8 && zone->tradevar > 3) {
|
||||
logtrade = true;
|
||||
}
|
||||
} else if (admin_level <= AccountStatus::GMAdmin) {
|
||||
if (zone->tradevar < 9 && zone->tradevar > 2) {
|
||||
logtrade = true;
|
||||
}
|
||||
} else if (admin_level <= AccountStatus::GMLeadAdmin) {
|
||||
if ((zone->tradevar < 8 && zone->tradevar > 1) || zone->tradevar == 9) {
|
||||
logtrade = true;
|
||||
}
|
||||
} else if (admin_level <= AccountStatus::Max){
|
||||
if (zone->tradevar < 8 && zone->tradevar > 0) {
|
||||
logtrade = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (logtrade) {
|
||||
char logtext[1000] = {0};
|
||||
uint32 cash = 0;
|
||||
bool comma = false;
|
||||
|
||||
// Log items offered by owner
|
||||
cash = cp + sp + gp + pp;
|
||||
if ((cash>0) || (item_count>0)) {
|
||||
sprintf(logtext, "%s gave %s ", trader->GetName(), with->GetName());
|
||||
|
||||
if (item_count > 0) {
|
||||
strcat(logtext, "items {");
|
||||
|
||||
for (uint16 i = EQ::invslot::TRADE_BEGIN; i <= EQ::invslot::TRADE_END; i++) {
|
||||
const EQ::ItemInstance* inst = trader->GetInv().GetItem(i);
|
||||
|
||||
if (!comma)
|
||||
comma = true;
|
||||
else {
|
||||
if (inst)
|
||||
strcat(logtext, ",");
|
||||
}
|
||||
|
||||
if (inst) {
|
||||
char item_num[15] = {0};
|
||||
sprintf(item_num, "%i", inst->GetItem()->ID);
|
||||
strcat(logtext, item_num);
|
||||
|
||||
if (inst->IsClassBag()) {
|
||||
for (uint8 j = EQ::invbag::SLOT_BEGIN; j <= EQ::invbag::SLOT_END; j++) {
|
||||
inst = trader->GetInv().GetItem(i, j);
|
||||
if (inst) {
|
||||
strcat(logtext, ",");
|
||||
sprintf(item_num, "%i", inst->GetItem()->ID);
|
||||
strcat(logtext, item_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cash > 0) {
|
||||
char money[100] = {0};
|
||||
sprintf(money, " %ipp, %igp, %isp, %icp", trader->trade->pp, trader->trade->gp, trader->trade->sp, trader->trade->cp);
|
||||
strcat(logtext, money);
|
||||
}
|
||||
|
||||
database.logevents(trader->AccountName(), trader->AccountID(),
|
||||
trader->Admin(), trader->GetName(), with->GetName(), "Trade", logtext, 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Trade::DumpTrade()
|
||||
{
|
||||
Mob* with = With();
|
||||
LogTrading("Dumping trade data: [{}] in TradeState [{}] with [{}]",
|
||||
owner->GetName(), state, ((with==nullptr)?"(null)":with->GetName()));
|
||||
|
||||
if (!owner->IsClient())
|
||||
return;
|
||||
|
||||
Client* trader = owner->CastToClient();
|
||||
for (uint16 i = EQ::invslot::TRADE_BEGIN; i <= EQ::invslot::TRADE_END; i++) {
|
||||
const EQ::ItemInstance* inst = trader->GetInv().GetItem(i);
|
||||
|
||||
if (inst) {
|
||||
LogTrading("Item [{}] (Charges=[{}], Slot=[{}], IsBag=[{}])",
|
||||
inst->GetItem()->ID, inst->GetCharges(),
|
||||
i, ((inst->IsClassBag()) ? "True" : "False"));
|
||||
|
||||
if (inst->IsClassBag()) {
|
||||
for (uint8 j = EQ::invbag::SLOT_BEGIN; j <= EQ::invbag::SLOT_END; j++) {
|
||||
inst = trader->GetInv().GetItem(i, j);
|
||||
if (inst) {
|
||||
LogTrading("\tBagItem [{}] (Charges=[{}], Slot=[{}])",
|
||||
inst->GetItem()->ID, inst->GetCharges(),
|
||||
EQ::InventoryProfile::CalcSlotId(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogTrading("\tpp:[{}], gp:[{}], sp:[{}], cp:[{}]", pp, gp, sp, cp);
|
||||
return owner;
|
||||
}
|
||||
|
||||
|
||||
@@ -458,8 +319,8 @@ void Client::ResetTrade() {
|
||||
|
||||
void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, std::list<void*>* event_details) {
|
||||
if(tradingWith && tradingWith->IsClient()) {
|
||||
Client* other = tradingWith->CastToClient();
|
||||
QSPlayerLogTrade_Struct* qs_audit = nullptr;
|
||||
Client * other = tradingWith->CastToClient();
|
||||
PlayerLogTrade_Struct * qs_audit = nullptr;
|
||||
bool qs_log = false;
|
||||
|
||||
if(other) {
|
||||
@@ -470,24 +331,24 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
// step 0: pre-processing
|
||||
// QS code
|
||||
if (RuleB(QueryServ, PlayerLogTrades) && event_entry && event_details) {
|
||||
qs_audit = (QSPlayerLogTrade_Struct*)event_entry;
|
||||
qs_audit = (PlayerLogTrade_Struct*)event_entry;
|
||||
qs_log = true;
|
||||
|
||||
if (finalizer) {
|
||||
qs_audit->char2_id = character_id;
|
||||
qs_audit->character_2_id = character_id;
|
||||
|
||||
qs_audit->char2_money.platinum = trade->pp;
|
||||
qs_audit->char2_money.gold = trade->gp;
|
||||
qs_audit->char2_money.silver = trade->sp;
|
||||
qs_audit->char2_money.copper = trade->cp;
|
||||
qs_audit->character_2_money.platinum = trade->pp;
|
||||
qs_audit->character_2_money.gold = trade->gp;
|
||||
qs_audit->character_2_money.silver = trade->sp;
|
||||
qs_audit->character_2_money.copper = trade->cp;
|
||||
}
|
||||
else {
|
||||
qs_audit->char1_id = character_id;
|
||||
qs_audit->character_1_id = character_id;
|
||||
|
||||
qs_audit->char1_money.platinum = trade->pp;
|
||||
qs_audit->char1_money.gold = trade->gp;
|
||||
qs_audit->char1_money.silver = trade->sp;
|
||||
qs_audit->char1_money.copper = trade->cp;
|
||||
qs_audit->character_1_money.platinum = trade->pp;
|
||||
qs_audit->character_1_money.gold = trade->gp;
|
||||
qs_audit->character_1_money.silver = trade->sp;
|
||||
qs_audit->character_1_money.copper = trade->cp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,12 +371,12 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
if (other->PutItemInInventory(free_slot, *inst, true)) {
|
||||
LogTrading("Container [{}] ([{}]) successfully transferred, deleting from trade slot", inst->GetItem()->Name, inst->GetItem()->ID);
|
||||
if (qs_log) {
|
||||
auto detail = new QSTradeItems_Struct;
|
||||
auto detail = new PlayerLogTradeItemsEntry_Struct;
|
||||
|
||||
detail->from_id = character_id;
|
||||
detail->from_slot = trade_slot;
|
||||
detail->to_id = other->CharacterID();
|
||||
detail->to_slot = free_slot;
|
||||
detail->from_character_id = character_id;
|
||||
detail->from_slot = trade_slot;
|
||||
detail->to_character_id = other->CharacterID();
|
||||
detail->to_slot = free_slot;
|
||||
detail->item_id = inst->GetID();
|
||||
detail->charges = 1;
|
||||
detail->aug_1 = inst->GetAugmentItemID(1);
|
||||
@@ -527,20 +388,20 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
event_details->push_back(detail);
|
||||
|
||||
if (finalizer)
|
||||
qs_audit->char2_count += detail->charges;
|
||||
qs_audit->character_2_item_count += detail->charges;
|
||||
else
|
||||
qs_audit->char1_count += detail->charges;
|
||||
qs_audit->character_1_item_count += detail->charges;
|
||||
|
||||
for (uint8 sub_slot = EQ::invbag::SLOT_BEGIN; (sub_slot <= EQ::invbag::SLOT_END); ++sub_slot) { // this is to catch ALL items
|
||||
const EQ::ItemInstance* bag_inst = inst->GetItem(sub_slot);
|
||||
|
||||
if (bag_inst) {
|
||||
detail = new QSTradeItems_Struct;
|
||||
detail = new PlayerLogTradeItemsEntry_Struct;
|
||||
|
||||
detail->from_id = character_id;
|
||||
detail->from_slot = EQ::InventoryProfile::CalcSlotId(trade_slot, sub_slot);
|
||||
detail->to_id = other->CharacterID();
|
||||
detail->to_slot = EQ::InventoryProfile::CalcSlotId(free_slot, sub_slot);
|
||||
detail->from_character_id = character_id;
|
||||
detail->from_slot = EQ::InventoryProfile::CalcSlotId(trade_slot, sub_slot);
|
||||
detail->to_character_id = other->CharacterID();
|
||||
detail->to_slot = EQ::InventoryProfile::CalcSlotId(free_slot, sub_slot);
|
||||
detail->item_id = bag_inst->GetID();
|
||||
detail->charges = (!bag_inst->IsStackable() ? 1 : bag_inst->GetCharges());
|
||||
detail->aug_1 = bag_inst->GetAugmentItemID(1);
|
||||
@@ -552,9 +413,9 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
event_details->push_back(detail);
|
||||
|
||||
if (finalizer)
|
||||
qs_audit->char2_count += detail->charges;
|
||||
qs_audit->character_2_item_count += detail->charges;
|
||||
else
|
||||
qs_audit->char1_count += detail->charges;
|
||||
qs_audit->character_1_item_count += detail->charges;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -620,12 +481,12 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
LogTrading("Partial stack [{}] ([{}]) successfully transferred, deleting [{}] charges from trade slot",
|
||||
inst->GetItem()->Name, inst->GetItem()->ID, (old_charges - inst->GetCharges()));
|
||||
if (qs_log) {
|
||||
auto detail = new QSTradeItems_Struct;
|
||||
auto detail = new PlayerLogTradeItemsEntry_Struct;
|
||||
|
||||
detail->from_id = character_id;
|
||||
detail->from_slot = trade_slot;
|
||||
detail->to_id = other->CharacterID();
|
||||
detail->to_slot = partial_slot;
|
||||
detail->from_character_id = character_id;
|
||||
detail->from_slot = trade_slot;
|
||||
detail->to_character_id = other->CharacterID();
|
||||
detail->to_slot = partial_slot;
|
||||
detail->item_id = inst->GetID();
|
||||
detail->charges = (old_charges - inst->GetCharges());
|
||||
detail->aug_1 = 0;
|
||||
@@ -637,9 +498,9 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
event_details->push_back(detail);
|
||||
|
||||
if (finalizer)
|
||||
qs_audit->char2_count += detail->charges;
|
||||
qs_audit->character_2_item_count += detail->charges;
|
||||
else
|
||||
qs_audit->char1_count += detail->charges;
|
||||
qs_audit->character_1_item_count += detail->charges;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -688,12 +549,12 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
}
|
||||
|
||||
if (qs_log) {
|
||||
auto detail = new QSTradeItems_Struct;
|
||||
auto detail = new PlayerLogTradeItemsEntry_Struct;
|
||||
|
||||
detail->from_id = character_id;
|
||||
detail->from_slot = trade_slot;
|
||||
detail->to_id = character_id;
|
||||
detail->to_slot = bias_slot;
|
||||
detail->from_character_id = character_id;
|
||||
detail->from_slot = trade_slot;
|
||||
detail->to_character_id = character_id;
|
||||
detail->to_slot = bias_slot;
|
||||
detail->item_id = inst->GetID();
|
||||
detail->charges = (old_charges - inst->GetCharges());
|
||||
detail->aug_1 = 0;
|
||||
@@ -728,12 +589,12 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
if (other->PutItemInInventory(free_slot, *inst, true)) {
|
||||
LogTrading("Item [{}] ([{}]) successfully transferred, deleting from trade slot", inst->GetItem()->Name, inst->GetItem()->ID);
|
||||
if (qs_log) {
|
||||
auto detail = new QSTradeItems_Struct;
|
||||
auto detail = new PlayerLogTradeItemsEntry_Struct;
|
||||
|
||||
detail->from_id = character_id;
|
||||
detail->from_slot = trade_slot;
|
||||
detail->to_id = other->CharacterID();
|
||||
detail->to_slot = free_slot;
|
||||
detail->from_character_id = character_id;
|
||||
detail->from_slot = trade_slot;
|
||||
detail->to_character_id = other->CharacterID();
|
||||
detail->to_slot = free_slot;
|
||||
detail->item_id = inst->GetID();
|
||||
detail->charges = (!inst->IsStackable() ? 1 : inst->GetCharges());
|
||||
detail->aug_1 = inst->GetAugmentItemID(1);
|
||||
@@ -745,21 +606,21 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
event_details->push_back(detail);
|
||||
|
||||
if (finalizer)
|
||||
qs_audit->char2_count += detail->charges;
|
||||
qs_audit->character_2_item_count += detail->charges;
|
||||
else
|
||||
qs_audit->char1_count += detail->charges;
|
||||
qs_audit->character_1_item_count += detail->charges;
|
||||
|
||||
// 'step 3' should never really see containers..but, just in case...
|
||||
for (uint8 sub_slot = EQ::invbag::SLOT_BEGIN; (sub_slot <= EQ::invbag::SLOT_END); ++sub_slot) { // this is to catch ALL items
|
||||
const EQ::ItemInstance* bag_inst = inst->GetItem(sub_slot);
|
||||
|
||||
if (bag_inst) {
|
||||
detail = new QSTradeItems_Struct;
|
||||
detail = new PlayerLogTradeItemsEntry_Struct;
|
||||
|
||||
detail->from_id = character_id;
|
||||
detail->from_slot = trade_slot;
|
||||
detail->to_id = other->CharacterID();
|
||||
detail->to_slot = free_slot;
|
||||
detail->from_character_id = character_id;
|
||||
detail->from_slot = trade_slot;
|
||||
detail->to_character_id = other->CharacterID();
|
||||
detail->to_slot = free_slot;
|
||||
detail->item_id = bag_inst->GetID();
|
||||
detail->charges = (!bag_inst->IsStackable() ? 1 : bag_inst->GetCharges());
|
||||
detail->aug_1 = bag_inst->GetAugmentItemID(1);
|
||||
@@ -771,9 +632,9 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
event_details->push_back(detail);
|
||||
|
||||
if (finalizer)
|
||||
qs_audit->char2_count += detail->charges;
|
||||
qs_audit->character_2_item_count += detail->charges;
|
||||
else
|
||||
qs_audit->char1_count += detail->charges;
|
||||
qs_audit->character_1_item_count += detail->charges;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1697,7 +1558,7 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs, Client* Trader, const EQApplic
|
||||
}
|
||||
|
||||
if(!TakeMoneyFromPP(TotalCost)) {
|
||||
database.SetHackerFlag(account_name, name, "Attempted to buy something in bazaar but did not have enough money.");
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "Attempted to buy something in bazaar but did not have enough money."});
|
||||
TradeRequestFailed(app);
|
||||
safe_delete(outapp);
|
||||
return;
|
||||
@@ -1715,6 +1576,37 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs, Client* Trader, const EQApplic
|
||||
|
||||
Trader->AddMoneyToPP(copper, silver, gold, platinum, true);
|
||||
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::TRADER_PURCHASE)) {
|
||||
auto e = PlayerEvent::TraderPurchaseEvent{
|
||||
.item_id = BuyItem->GetID(),
|
||||
.item_name = BuyItem->GetItem()->Name,
|
||||
.trader_id = Trader->CharacterID(),
|
||||
.trader_name = Trader->GetCleanName(),
|
||||
.price = tbs->Price,
|
||||
.charges = outtbs->Quantity,
|
||||
.total_cost = (tbs->Price * outtbs->Quantity),
|
||||
.player_money_balance = GetCarriedMoney(),
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::TRADER_PURCHASE, e);
|
||||
}
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::TRADER_SELL)) {
|
||||
auto e = PlayerEvent::TraderSellEvent{
|
||||
.item_id = BuyItem->GetID(),
|
||||
.item_name = BuyItem->GetItem()->Name,
|
||||
.buyer_id = CharacterID(),
|
||||
.buyer_name = GetCleanName(),
|
||||
.price = tbs->Price,
|
||||
.charges = outtbs->Quantity,
|
||||
.total_cost = (tbs->Price * outtbs->Quantity),
|
||||
.player_money_balance = Trader->GetCarriedMoney(),
|
||||
};
|
||||
|
||||
RecordPlayerEventLogWithClient(Trader, PlayerEvent::TRADER_SELL, e);
|
||||
}
|
||||
|
||||
LogTrading("Trader Received: [{}] Platinum, [{}] Gold, [{}] Silver, [{}] Copper", platinum, gold, silver, copper);
|
||||
|
||||
ReturnTraderReq(app, outtbs->Quantity, ItemID);
|
||||
|
||||
Reference in New Issue
Block a user