[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:
Chris Miles
2023-02-12 21:31:01 -06:00
committed by GitHub
parent 1cc32d92cf
commit d9f545a5ec
92 changed files with 6480 additions and 1391 deletions
+88 -196
View File
@@ -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);