[Player Event Logs] Migrate and Deprecate QS Legacy Logging (#4542)

* First pass of player_event_loot_items

* Second pass of player_event_loot_items

* Third pass of player_event_loot_items

* Example without RecordDetailEvent template

* Cleanup the removal of the template

* Fourth Pass

Add retention for etl tables
Rename tables/fields to etl nomenclature
Combine database work to one atomic load

* Reposition to reduce db tasks

* Refactor etl processing for easier additions

* Add merchant purchase event
testing passed though appears that the event itself has a few bugs.  Will fix them in another commit

* Fix PlayerEventMerchantPurchase in client_packet.cpp

* WIP - Handin

* Handin Event added

* Cleanup

* All a rentention period of 0 days which deletes all current records.

* Updates

Cleanup and refactor a few items.

* Cleanup and Formatting

Cleanup and Formatting

* Add etl for
Playerevent::Trade
PlayerEvent::Speech (new event to mirror functionality of qs_speech

* Add etl for
Playerevent::KilledNPC, KilledNamedNPC and KilledRaidNPC

* Add etl for Playerevent::AA_purchase

Add etl for Playerevent::AA_purchase

* Cleanup before PR

* Review comment updates.

* Add world cli etl:settings to output a json on all player event details.

* Add reserve for all etl_queues
Correct a failed test case for improper next id for etl tables when table is first created.

* Potential solution for a dedicated database connection for player events.

* Simple thread for player_events.  Likely there is a better way to do this.

* Add zone to qs communications for recordplayerevents

First pass of enabling zone to qs direct transport to allow for PlayerEvents to bypass world.

* Cleanup a linux compile issue

* Add augments to LOOT ITEM and DESTROY ITEM

* Add augments to ITEMCREATION, FORAGESUCCESS, FISHSUCCESS, DESTROYITEM, LOOTITEM, DROPPEDITEM, TRADERPURCHASE, TRADERSELL, GUILDTRIBUTEDONATE and cleaned up the naming convention of augments

* Formatting fixes

* Swap out GetNextTableId

* Statically load counter

* Add counter.clear() since the counter is static

* Upload optional QS conversion scripts

* Remove all qs_tables and code referencing them

* Update database.cpp

* Simplify ProcessBatchQueue

* Simplify PorcessBatchQueue

* Simplify event truncation

* Build event truncation to bulk query by retention groups

* Post rebase

* Update player_events.h

* Fix build

* Update npc.cpp

* First pass of direct zone to qs sending for player events

* Remove keepalive logic

* Fix event ordering

* Cleanup

* Update player_event_logs.cpp

* Wipe event data after ETL processed

* Split up database connections, hot reload logs for QS

* Load rules from database vs qs_database

* Update player_event_logs.cpp

* Hot toggle queryserv connect

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
Mitch Freeman
2025-02-05 04:02:16 -04:00
committed by GitHub
parent 21d27a1122
commit 8f4f8368df
99 changed files with 8156 additions and 2451 deletions
+45 -254
View File
@@ -27,8 +27,10 @@
#include "bot.h"
#include "../common/evolving_items.h"
#include "../common/repositories/character_corpse_items_repository.h"
#include "queryserv.h"
extern WorldServer worldserver;
extern QueryServ *QServ;
// @merth: this needs to be touched up
uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) {
@@ -631,17 +633,17 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2,
if (player_event_logs.IsEventEnabled(PlayerEvent::ITEM_CREATION)) {
auto e = PlayerEvent::ItemCreationEvent{};
e.item_id = item->ID;
e.item_name = item->Name;
e.to_slot = to_slot;
e.charges = charges;
e.aug1 = aug1;
e.aug2 = aug2;
e.aug3 = aug3;
e.aug4 = aug4;
e.aug5 = aug5;
e.aug6 = aug6;
e.attuned = attuned;
e.item_id = item->ID;
e.item_name = item->Name;
e.to_slot = to_slot;
e.charges = charges;
e.augment_1_id = aug1;
e.augment_2_id = aug2;
e.augment_3_id = aug3;
e.augment_4_id = aug4;
e.augment_5_id = aug5;
e.augment_6_id = aug6;
e.attuned = attuned;
RecordPlayerEventLog(PlayerEvent::ITEM_CREATION, e);
}
@@ -763,10 +765,16 @@ void Client::DropItem(int16 slot_id, bool recurse)
if (player_event_logs.IsEventEnabled(PlayerEvent::DROPPED_ITEM)) {
auto e = PlayerEvent::DroppedItemEvent{
.item_id = inst->GetID(),
.item_name = inst->GetItem()->Name,
.slot_id = slot_id,
.charges = (uint32) inst->GetCharges()
.item_id = inst->GetID(),
.augment_1_id = inst->GetAugmentItemID(0),
.augment_2_id = inst->GetAugmentItemID(1),
.augment_3_id = inst->GetAugmentItemID(2),
.augment_4_id = inst->GetAugmentItemID(3),
.augment_5_id = inst->GetAugmentItemID(4),
.augment_6_id = inst->GetAugmentItemID(5),
.item_name = inst->GetItem()->Name,
.slot_id = slot_id,
.charges = (uint32) inst->GetCharges()
};
RecordPlayerEventLog(PlayerEvent::DROPPED_ITEM, e);
}
@@ -820,79 +828,10 @@ void Client::DropItem(int16 slot_id, bool recurse)
object->StartDecay();
LogInventory("[{}] dropped [{}] from slot [{}]", GetCleanName(), inst->GetItem()->Name, slot_id);
DropItemQS(inst, false);
safe_delete(inst);
}
void Client::DropItemQS(EQ::ItemInstance* inst, bool pickup) {
if (RuleB(QueryServ, PlayerDropItems)) {
QSPlayerDropItem_Struct qs_audit;
std::list<void*> event_details;
memset(&qs_audit, 0, sizeof(QSPlayerDropItem_Struct));
qs_audit.char_id = character_id;
qs_audit.pickup = pickup;
qs_audit.zone_id = GetZoneID();
qs_audit.x = (int) GetX();
qs_audit.y = (int) GetY();
qs_audit.z = (int) GetZ();
if (inst) {
auto detail = new QSDropItems_Struct;
detail->item_id = inst->GetID();
detail->charges = inst->IsClassBag() ? 1 : inst->GetCharges();
detail->aug_1 = inst->GetAugmentItemID(1);
detail->aug_2 = inst->GetAugmentItemID(2);
detail->aug_3 = inst->GetAugmentItemID(3);
detail->aug_4 = inst->GetAugmentItemID(4);
detail->aug_5 = inst->GetAugmentItemID(5);
event_details.push_back(detail);
if (inst->IsClassBag()) {
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 QSDropItems_Struct;
detail->item_id = bag_inst->GetID();
detail->charges = (!bag_inst->IsStackable() ? 1 : bag_inst->GetCharges());
detail->aug_1 = bag_inst->GetAugmentItemID(1);
detail->aug_2 = bag_inst->GetAugmentItemID(2);
detail->aug_3 = bag_inst->GetAugmentItemID(3);
detail->aug_4 = bag_inst->GetAugmentItemID(4);
detail->aug_5 = bag_inst->GetAugmentItemID(5);
event_details.push_back(detail);
}
}
}
}
qs_audit._detail_count = event_details.size();
auto qs_pack = new ServerPacket(
ServerOP_QSPlayerDropItem,
sizeof(QSPlayerDropItem_Struct) +
(sizeof(QSDropItems_Struct) * qs_audit._detail_count));
QSPlayerDropItem_Struct* qs_buf = (QSPlayerDropItem_Struct*) qs_pack->pBuffer;
memcpy(qs_buf, &qs_audit, sizeof(QSPlayerDropItem_Struct));
int offset = 0;
for (auto iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) {
QSDropItems_Struct* detail = reinterpret_cast<QSDropItems_Struct*>(*iter);
qs_buf->items[offset] = *detail;
safe_delete(detail);
}
event_details.clear();
if (worldserver.Connected())
worldserver.SendPacket(qs_pack);
safe_delete(qs_pack);
}
}
// Drop inst
void Client::DropInst(const EQ::ItemInstance* inst)
{
@@ -1041,55 +980,6 @@ void Client::DeleteItemInInventory(int16 slot_id, int16 quantity, bool client_up
return;
}
// start QS code
if(RuleB(QueryServ, PlayerLogDeletes)) {
uint16 delete_count = 0;
if(m_inv[slot_id]) { delete_count += m_inv.GetItem(slot_id)->GetTotalItemCount(); }
auto qspack =
new ServerPacket(ServerOP_QSPlayerLogDeletes,
sizeof(QSPlayerLogDelete_Struct) + (sizeof(QSDeleteItems_Struct) * delete_count));
QSPlayerLogDelete_Struct* qsaudit = (QSPlayerLogDelete_Struct*)qspack->pBuffer;
uint16 parent_offset = 0;
qsaudit->char_id = character_id;
qsaudit->stack_size = quantity;
qsaudit->char_count = delete_count;
qsaudit->items[parent_offset].char_slot = slot_id;
qsaudit->items[parent_offset].item_id = m_inv[slot_id]->GetID();
qsaudit->items[parent_offset].charges = m_inv[slot_id]->GetCharges();
qsaudit->items[parent_offset].aug_1 = m_inv[slot_id]->GetAugmentItemID(1);
qsaudit->items[parent_offset].aug_2 = m_inv[slot_id]->GetAugmentItemID(2);
qsaudit->items[parent_offset].aug_3 = m_inv[slot_id]->GetAugmentItemID(3);
qsaudit->items[parent_offset].aug_4 = m_inv[slot_id]->GetAugmentItemID(4);
qsaudit->items[parent_offset].aug_5 = m_inv[slot_id]->GetAugmentItemID(5);
if (m_inv[slot_id]->IsClassBag()) {
for (uint8 bag_idx = EQ::invbag::SLOT_BEGIN; bag_idx < m_inv[slot_id]->GetItem()->BagSlots; bag_idx++) {
EQ::ItemInstance* bagitem = m_inv[slot_id]->GetItem(bag_idx);
if(bagitem) {
int16 bagslot_id = EQ::InventoryProfile::CalcSlotId(slot_id, bag_idx);
qsaudit->items[++parent_offset].char_slot = bagslot_id;
qsaudit->items[parent_offset].item_id = bagitem->GetID();
qsaudit->items[parent_offset].charges = bagitem->GetCharges();
qsaudit->items[parent_offset].aug_1 = bagitem->GetAugmentItemID(1);
qsaudit->items[parent_offset].aug_2 = bagitem->GetAugmentItemID(2);
qsaudit->items[parent_offset].aug_3 = bagitem->GetAugmentItemID(3);
qsaudit->items[parent_offset].aug_4 = bagitem->GetAugmentItemID(4);
qsaudit->items[parent_offset].aug_5 = bagitem->GetAugmentItemID(5);
}
}
}
if(worldserver.Connected()) { worldserver.SendPacket(qspack); }
safe_delete(qspack);
}
// end QS code
uint64 evolve_id = m_inv[slot_id]->GetEvolveUniqueID();
bool isDeleted = m_inv.DeleteItem(slot_id, quantity);
if (isDeleted && evolve_id && (slot_id > EQ::invslot::TRADE_END || slot_id < EQ::invslot::TRADE_BEGIN)) {
@@ -1732,7 +1622,6 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
}
if (move_in->from_slot == move_in->to_slot) { // Item summon, no further processing needed
if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit
if (ClientVersion() >= EQ::versions::ClientVersion::RoF) { return true; } // Can't do RoF+
if (move_in->to_slot == EQ::invslot::slotCursor) {
@@ -1768,10 +1657,17 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
if (player_event_logs.IsEventEnabled(PlayerEvent::ITEM_DESTROY)) {
auto e = PlayerEvent::DestroyItemEvent{
.item_id = test_inst->GetItem()->ID,
.item_name = test_inst->GetItem()->Name,
.charges = test_inst->GetCharges(),
.reason = "Duplicate lore item",
.item_id = test_inst->GetItem()->ID,
.item_name = test_inst->GetItem()->Name,
.charges = test_inst->GetCharges(),
.augment_1_id = test_inst->GetAugmentItemID(0),
.augment_2_id = test_inst->GetAugmentItemID(1),
.augment_3_id = test_inst->GetAugmentItemID(2),
.augment_4_id = test_inst->GetAugmentItemID(3),
.augment_5_id = test_inst->GetAugmentItemID(4),
.augment_6_id = test_inst->GetAugmentItemID(5),
.attuned = test_inst->IsAttuned(),
.reason = "Duplicate lore item"
};
RecordPlayerEventLog(PlayerEvent::ITEM_DESTROY, e);
@@ -1785,17 +1681,23 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
if (move_in->to_slot == (uint32)INVALID_INDEX) {
if (move_in->from_slot == (uint32)EQ::invslot::slotCursor) {
LogInventory("Client destroyed item from cursor slot [{}]", move_in->from_slot);
if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit
EQ::ItemInstance *inst = m_inv.GetItem(EQ::invslot::slotCursor);
if (inst) {
if (player_event_logs.IsEventEnabled(PlayerEvent::ITEM_DESTROY)) {
auto e = PlayerEvent::DestroyItemEvent{
.item_id = inst->GetItem()->ID,
.item_name = inst->GetItem()->Name,
.charges = inst->GetCharges(),
.reason = "Client destroy cursor",
.item_id = inst->GetItem()->ID,
.item_name = inst->GetItem()->Name,
.charges = inst->GetCharges(),
.augment_1_id = inst->GetAugmentItemID(0),
.augment_2_id = inst->GetAugmentItemID(1),
.augment_3_id = inst->GetAugmentItemID(2),
.augment_4_id = inst->GetAugmentItemID(3),
.augment_5_id = inst->GetAugmentItemID(4),
.augment_6_id = inst->GetAugmentItemID(5),
.attuned = inst->IsAttuned(),
.reason = "Client destroy cursor"
};
RecordPlayerEventLog(PlayerEvent::ITEM_DESTROY, e);
@@ -1818,7 +1720,6 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
}
else {
LogInventory("Deleted item from slot [{}] as a result of an inventory container tradeskill combine", move_in->from_slot);
if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit
DeleteItemInInventory(move_in->from_slot);
return true; // Item deletion
}
@@ -2015,8 +1916,6 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
safe_delete(inst);
}
if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in, true); } // QS Audit
return true;
}
else if (dst_slot_id >= EQ::invslot::WORLD_BEGIN && dst_slot_id <= EQ::invslot::WORLD_END) {
@@ -2085,8 +1984,6 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
database.SaveInventory(character_id, m_inv[src_slot_id], src_slot_id);
}
if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in, true); } // QS Audit
return true;
}
}
@@ -2105,10 +2002,6 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
return false;
}
// Add cursor item to trade bucket
// Also sends trade information to other client of trade session
if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit
trade->AddEntity(dst_slot_id, move_in->number_in_stack);
if (dstitemid == 0)
{
@@ -2117,8 +2010,6 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
return true;
} else {
if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit
SummonItem(src_inst->GetID(), src_inst->GetCharges());
DeleteItemInInventory(EQ::invslot::slotCursor);
@@ -2339,8 +2230,6 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
database.SaveInventory(character_id, m_inv.GetItem(dst_slot_id), dst_slot_id);
}
if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in, true); } // QS Audit
// Step 8: Re-calc stats
CalcBonuses();
ApplyWeaponsStance();
@@ -2442,104 +2331,6 @@ void Client::SwapItemResync(MoveItem_Struct* move_slots) {
}
}
void Client::QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call) {
int16 from_slot_id = static_cast<int16>(move_in->from_slot);
int16 to_slot_id = static_cast<int16>(move_in->to_slot);
int16 move_amount = static_cast<int16>(move_in->number_in_stack);
if(!m_inv[from_slot_id] && !m_inv[to_slot_id]) { return; }
uint16 move_count = 0;
if(m_inv[from_slot_id]) { move_count += m_inv[from_slot_id]->GetTotalItemCount(); }
if(to_slot_id != from_slot_id) { if(m_inv[to_slot_id]) { move_count += m_inv[to_slot_id]->GetTotalItemCount(); } }
auto qspack = new ServerPacket(ServerOP_QSPlayerLogMoves,
sizeof(QSPlayerLogMove_Struct) + (sizeof(QSMoveItems_Struct) * move_count));
QSPlayerLogMove_Struct* qsaudit = (QSPlayerLogMove_Struct*)qspack->pBuffer;
qsaudit->char_id = character_id;
qsaudit->stack_size = move_amount;
qsaudit->char_count = move_count;
qsaudit->postaction = postaction_call;
qsaudit->from_slot = from_slot_id;
qsaudit->to_slot = to_slot_id;
move_count = 0;
const EQ::ItemInstance* from_inst = m_inv[postaction_call?to_slot_id:from_slot_id];
if(from_inst) {
qsaudit->items[move_count].from_slot = from_slot_id;
qsaudit->items[move_count].to_slot = to_slot_id;
qsaudit->items[move_count].item_id = from_inst->GetID();
qsaudit->items[move_count].charges = from_inst->GetCharges();
qsaudit->items[move_count].aug_1 = from_inst->GetAugmentItemID(1);
qsaudit->items[move_count].aug_2 = from_inst->GetAugmentItemID(2);
qsaudit->items[move_count].aug_3 = from_inst->GetAugmentItemID(3);
qsaudit->items[move_count].aug_4 = from_inst->GetAugmentItemID(4);
qsaudit->items[move_count++].aug_5 = from_inst->GetAugmentItemID(5);
if (from_inst->IsType(EQ::item::ItemClassBag)) {
for (uint8 bag_idx = EQ::invbag::SLOT_BEGIN; bag_idx < from_inst->GetItem()->BagSlots; bag_idx++) {
const EQ::ItemInstance* from_baginst = from_inst->GetItem(bag_idx);
if(from_baginst) {
qsaudit->items[move_count].from_slot = EQ::InventoryProfile::CalcSlotId(from_slot_id, bag_idx);
qsaudit->items[move_count].to_slot = EQ::InventoryProfile::CalcSlotId(to_slot_id, bag_idx);
qsaudit->items[move_count].item_id = from_baginst->GetID();
qsaudit->items[move_count].charges = from_baginst->GetCharges();
qsaudit->items[move_count].aug_1 = from_baginst->GetAugmentItemID(1);
qsaudit->items[move_count].aug_2 = from_baginst->GetAugmentItemID(2);
qsaudit->items[move_count].aug_3 = from_baginst->GetAugmentItemID(3);
qsaudit->items[move_count].aug_4 = from_baginst->GetAugmentItemID(4);
qsaudit->items[move_count++].aug_5 = from_baginst->GetAugmentItemID(5);
}
}
}
}
if(to_slot_id != from_slot_id) {
const EQ::ItemInstance* to_inst = m_inv[postaction_call?from_slot_id:to_slot_id];
if(to_inst) {
qsaudit->items[move_count].from_slot = to_slot_id;
qsaudit->items[move_count].to_slot = from_slot_id;
qsaudit->items[move_count].item_id = to_inst->GetID();
qsaudit->items[move_count].charges = to_inst->GetCharges();
qsaudit->items[move_count].aug_1 = to_inst->GetAugmentItemID(1);
qsaudit->items[move_count].aug_2 = to_inst->GetAugmentItemID(2);
qsaudit->items[move_count].aug_3 = to_inst->GetAugmentItemID(3);
qsaudit->items[move_count].aug_4 = to_inst->GetAugmentItemID(4);
qsaudit->items[move_count++].aug_5 = to_inst->GetAugmentItemID(5);
if (to_inst->IsType(EQ::item::ItemClassBag)) {
for (uint8 bag_idx = EQ::invbag::SLOT_BEGIN; bag_idx < to_inst->GetItem()->BagSlots; bag_idx++) {
const EQ::ItemInstance* to_baginst = to_inst->GetItem(bag_idx);
if(to_baginst) {
qsaudit->items[move_count].from_slot = EQ::InventoryProfile::CalcSlotId(to_slot_id, bag_idx);
qsaudit->items[move_count].to_slot = EQ::InventoryProfile::CalcSlotId(from_slot_id, bag_idx);
qsaudit->items[move_count].item_id = to_baginst->GetID();
qsaudit->items[move_count].charges = to_baginst->GetCharges();
qsaudit->items[move_count].aug_1 = to_baginst->GetAugmentItemID(1);
qsaudit->items[move_count].aug_2 = to_baginst->GetAugmentItemID(2);
qsaudit->items[move_count].aug_3 = to_baginst->GetAugmentItemID(3);
qsaudit->items[move_count].aug_4 = to_baginst->GetAugmentItemID(4);
qsaudit->items[move_count++].aug_5 = to_baginst->GetAugmentItemID(5);
}
}
}
}
}
if(move_count && worldserver.Connected()) {
worldserver.SendPacket(qspack);
}
safe_delete(qspack);
}
void Client::DyeArmor(EQ::TintProfile* dye){
int16 slot=0;
for (int i = EQ::textures::textureBegin; i <= EQ::textures::LastTintableTexture; i++) {