mirror of
https://github.com/EQEmu/Server.git
synced 2026-09-02 20:56:37 +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:
+14
@@ -23,6 +23,7 @@ Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
|
||||
#include "../common/races.h"
|
||||
#include "../common/spdat.h"
|
||||
#include "../common/strings.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "aa.h"
|
||||
#include "client.h"
|
||||
#include "corpse.h"
|
||||
@@ -35,9 +36,11 @@ Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
|
||||
#include "titles.h"
|
||||
#include "zonedb.h"
|
||||
#include "../common/zone_store.h"
|
||||
#include "worldserver.h"
|
||||
|
||||
#include "bot.h"
|
||||
|
||||
extern WorldServer worldserver;
|
||||
extern QueryServ* QServ;
|
||||
|
||||
void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, uint32 duration_override, bool followme, bool sticktarg, uint16 *eye_id) {
|
||||
@@ -1180,6 +1183,17 @@ void Client::FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost
|
||||
SendAlternateAdvancementPoints();
|
||||
SendAlternateAdvancementStats();
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::AA_PURCHASE)) {
|
||||
auto e = PlayerEvent::AAPurchasedEvent{
|
||||
.aa_id = rank->id,
|
||||
.aa_cost = cost,
|
||||
.aa_previous_id = rank->prev_id,
|
||||
.aa_next_id = rank->next_id
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::AA_PURCHASE, e);
|
||||
}
|
||||
|
||||
if (rank->prev) {
|
||||
MessageString(
|
||||
Chat::Yellow,
|
||||
|
||||
+19
-1
@@ -25,6 +25,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "../common/strings.h"
|
||||
#include "../common/data_verification.h"
|
||||
#include "../common/misc_functions.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "queryserv.h"
|
||||
#include "quest_parser_collection.h"
|
||||
#include "string_ids.h"
|
||||
@@ -2064,6 +2065,20 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
|
||||
QServ->PlayerLogEvent(Player_Log_Deaths, CharacterID(), event_desc);
|
||||
}
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::DEATH)) {
|
||||
auto e = PlayerEvent::DeathEvent{
|
||||
.killer_id = killerMob ? static_cast<uint32>(killerMob->GetID()) : static_cast<uint32>(0),
|
||||
.killer_name = killerMob ? killerMob->GetCleanName() : "No Killer",
|
||||
.damage = damage,
|
||||
.spell_id = spell,
|
||||
.spell_name = IsValidSpell(spell) ? spells[spell].name : "No Spell",
|
||||
.skill_id = static_cast<int>(attack_skill),
|
||||
.skill_name = !EQ::skills::GetSkillName(attack_skill).empty() ? EQ::skills::GetSkillName(attack_skill) : "No Skill",
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::DEATH, e);
|
||||
}
|
||||
|
||||
std::vector<std::any> args = { new_corpse };
|
||||
parse->EventPlayer(EVENT_DEATH_COMPLETE, this, export_string, 0, &args);
|
||||
return true;
|
||||
@@ -2518,6 +2533,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
if (kr->members[i].member != nullptr && kr->members[i].member->IsClient()) { // If Group Member is Client
|
||||
Client *c = kr->members[i].member;
|
||||
parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0);
|
||||
c->RecordKilledNPCEvent(this);
|
||||
|
||||
if (RuleB(NPC, EnableMeritBasedFaction))
|
||||
c->SetFactionLevel(c->CharacterID(), GetNPCFactionID(), c->GetBaseClass(), c->GetBaseRace(), c->GetDeity());
|
||||
@@ -2565,6 +2581,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
if (kg->members[i] != nullptr && kg->members[i]->IsClient()) { // If Group Member is Client
|
||||
Client *c = kg->members[i]->CastToClient();
|
||||
parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0);
|
||||
c->RecordKilledNPCEvent(this);
|
||||
|
||||
if (RuleB(NPC, EnableMeritBasedFaction))
|
||||
c->SetFactionLevel(c->CharacterID(), GetNPCFactionID(), c->GetBaseClass(), c->GetBaseRace(), c->GetDeity());
|
||||
@@ -2612,6 +2629,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
|
||||
/* Send the EVENT_KILLED_MERIT event */
|
||||
parse->EventNPC(EVENT_KILLED_MERIT, this, give_exp_client, "killed", 0);
|
||||
give_exp_client->RecordKilledNPCEvent(this);
|
||||
|
||||
if (RuleB(NPC, EnableMeritBasedFaction))
|
||||
give_exp_client->SetFactionLevel(give_exp_client->CharacterID(), GetNPCFactionID(), give_exp_client->GetBaseClass(),
|
||||
@@ -2789,7 +2807,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
|
||||
std::vector<std::any> args = { corpse };
|
||||
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, export_string, 0, &args);
|
||||
combat_record.Stop();
|
||||
m_combat_record.Stop();
|
||||
|
||||
/* Zone controller process EVENT_DEATH_ZONE (Death events) */
|
||||
args.push_back(this);
|
||||
|
||||
@@ -1624,8 +1624,6 @@ int bot_command_real_dispatch(Client *c, const char *message)
|
||||
{
|
||||
Seperator sep(message, ' ', 10, 100, true); // "three word argument" should be considered 1 arg
|
||||
|
||||
bot_command_log_command(c, message);
|
||||
|
||||
std::string cstr(sep.arg[0]+1);
|
||||
|
||||
if(bot_command_list.count(cstr) != 1) {
|
||||
@@ -1659,77 +1657,6 @@ int bot_command_real_dispatch(Client *c, const char *message)
|
||||
|
||||
}
|
||||
|
||||
void bot_command_log_command(Client *c, const char *message)
|
||||
{
|
||||
int admin = c->Admin();
|
||||
|
||||
bool continueevents = false;
|
||||
switch (zone->loglevelvar){ //catch failsafe
|
||||
case 9: { // log only LeadGM
|
||||
if (
|
||||
admin >= AccountStatus::GMLeadAdmin &&
|
||||
admin < AccountStatus::GMMgmt
|
||||
) {
|
||||
continueevents = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8: { // log only GM
|
||||
if (
|
||||
admin >= AccountStatus::GMAdmin &&
|
||||
admin < AccountStatus::GMLeadAdmin
|
||||
) {
|
||||
continueevents = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
if (admin >= AccountStatus::GMMgmt) {
|
||||
continueevents = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (admin >= AccountStatus::GMLeadAdmin) {
|
||||
continueevents = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
if (admin >= AccountStatus::GMAdmin) {
|
||||
continueevents = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
if (admin >= AccountStatus::QuestTroupe) {
|
||||
continueevents = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
if (admin >= AccountStatus::ApprenticeGuide) {
|
||||
continueevents = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
if (admin >= AccountStatus::Steward) {
|
||||
continueevents = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
continueevents = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (continueevents)
|
||||
database.logevents(c->AccountName(), c->AccountID(), admin,c->GetName(), c->GetTarget()?c->GetTarget()->GetName():"None", "BotCommand", message, 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* helper functions by use
|
||||
*/
|
||||
|
||||
@@ -543,7 +543,6 @@ void bot_command_deinit(void);
|
||||
int bot_command_add(std::string bot_command_name, const char *desc, int access, BotCmdFuncPtr function);
|
||||
int bot_command_not_avail(Client *c, const char *message);
|
||||
int bot_command_real_dispatch(Client *c, char const *message);
|
||||
void bot_command_log_command(Client *c, const char *message);
|
||||
|
||||
// Bot Commands
|
||||
void bot_command_actionable(Client *c, const Seperator *sep);
|
||||
|
||||
+19
-67
@@ -1,6 +1,10 @@
|
||||
#include "cheat_manager.h"
|
||||
#include "client.h"
|
||||
#include "quest_parser_collection.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "worldserver.h"
|
||||
|
||||
extern WorldServer worldserver;
|
||||
|
||||
void CheatManager::SetClient(Client *cli)
|
||||
{
|
||||
@@ -36,12 +40,9 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3
|
||||
position2.z,
|
||||
Distance(position1, position2)
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
|
||||
RecordPlayerEventLogWithClient(m_target, PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
|
||||
LogCheat(fmt::runtime(message));
|
||||
std::string export_string = fmt::format(
|
||||
"{} {} {}",
|
||||
@@ -65,12 +66,7 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3
|
||||
position2.z,
|
||||
Distance(position1, position2)
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
RecordPlayerEventLogWithClient(m_target, PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
LogCheat(fmt::runtime(message));
|
||||
std::string export_string = fmt::format(
|
||||
"{} {} {}",
|
||||
@@ -91,12 +87,7 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
RecordPlayerEventLogWithClient(m_target, PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
LogCheat(fmt::runtime(message));
|
||||
}
|
||||
break;
|
||||
@@ -109,12 +100,7 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
RecordPlayerEventLogWithClient(m_target, PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
LogCheat(fmt::runtime(message));
|
||||
}
|
||||
break;
|
||||
@@ -129,12 +115,7 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
RecordPlayerEventLogWithClient(m_target, PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
LogCheat(fmt::runtime(message));
|
||||
}
|
||||
}
|
||||
@@ -149,12 +130,7 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
RecordPlayerEventLogWithClient(m_target, PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
LogCheat(fmt::runtime(message));
|
||||
}
|
||||
break;
|
||||
@@ -167,12 +143,7 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
RecordPlayerEventLogWithClient(m_target, PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
LogCheat(fmt::runtime(message));
|
||||
}
|
||||
break;
|
||||
@@ -185,12 +156,7 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
RecordPlayerEventLogWithClient(m_target, PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
LogCheat(fmt::runtime(message));
|
||||
}
|
||||
break;
|
||||
@@ -199,17 +165,13 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3
|
||||
if (RuleB(Cheat, EnableMQGhostDetector) &&
|
||||
((m_target->Admin() < RuleI(Cheat, MQGhostExemptStatus) ||
|
||||
(RuleI(Cheat, MQGhostExemptStatus)) == -1))) {
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
"Packet blocking detected.",
|
||||
zone->GetShortName()
|
||||
);
|
||||
LogCheat(
|
||||
std::string message = fmt::format(
|
||||
"[MQGhost] [{}] [{}] was caught not sending the proper packets as regularly as they were suppose to.",
|
||||
m_target->AccountName(),
|
||||
m_target->GetName()
|
||||
);
|
||||
RecordPlayerEventLogWithClient(m_target, PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
LogCheat("{}", message);
|
||||
}
|
||||
break;
|
||||
case MQFastMem:
|
||||
@@ -222,12 +184,7 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
RecordPlayerEventLogWithClient(m_target, PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
LogCheat(fmt::runtime(message));
|
||||
}
|
||||
break;
|
||||
@@ -238,12 +195,7 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
RecordPlayerEventLogWithClient(m_target, PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
LogCheat(fmt::runtime(message));
|
||||
break;
|
||||
}
|
||||
|
||||
+254
-58
@@ -66,6 +66,9 @@ extern volatile bool RunLoops;
|
||||
#include "../common/repositories/character_spells_repository.h"
|
||||
#include "../common/repositories/character_disciplines_repository.h"
|
||||
#include "../common/repositories/character_data_repository.h"
|
||||
#include "../common/repositories/discovered_items_repository.h"
|
||||
#include "../common/events/player_events.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
|
||||
extern QueryServ* QServ;
|
||||
@@ -1122,6 +1125,17 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
|
||||
break;
|
||||
}
|
||||
case ChatChannel_Say: { /* Say */
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::SAY)) {
|
||||
std::string msg = message;
|
||||
if (!msg.empty() && msg.at(0) != '#' && msg.at(0) != '^') {
|
||||
auto e = PlayerEvent::SayEvent{
|
||||
.message = message,
|
||||
.target = GetTarget() ? GetTarget()->GetCleanName() : ""
|
||||
};
|
||||
RecordPlayerEventLog(PlayerEvent::SAY, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (message[0] == COMMAND_CHAR) {
|
||||
if (command_dispatch(this, message, false) == -2) {
|
||||
if (parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
@@ -2542,6 +2556,17 @@ bool Client::CheckIncreaseSkill(EQ::skills::SkillType skillid, Mob *against_who,
|
||||
0
|
||||
);
|
||||
parse->EventPlayer(EVENT_SKILL_UP, this, export_string, 0);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::SKILL_UP)) {
|
||||
auto e = PlayerEvent::SkillUpEvent{
|
||||
.skill_id = static_cast<uint32>(skillid),
|
||||
.value = (skillval + 1),
|
||||
.max_skill = static_cast<int16>(maxskill),
|
||||
.against_who = (against_who) ? against_who->GetCleanName() : GetCleanName(),
|
||||
};
|
||||
RecordPlayerEventLog(PlayerEvent::SKILL_UP, e);
|
||||
}
|
||||
|
||||
LogSkills("Skill [{}] at value [{}] successfully gain with [{}] chance (mod [{}])", skillid, skillval, Chance, chancemodi);
|
||||
return true;
|
||||
} else {
|
||||
@@ -2764,35 +2789,6 @@ void Client::MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing, uint32 re
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Client::LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 price, const EQ::ItemData* item, bool buying)
|
||||
{
|
||||
if(!player || !merchant || !item)
|
||||
return;
|
||||
|
||||
std::string LogText = "Qty: ";
|
||||
|
||||
char Buffer[255];
|
||||
memset(Buffer, 0, sizeof(Buffer));
|
||||
|
||||
snprintf(Buffer, sizeof(Buffer)-1, "%3i", quantity);
|
||||
LogText += Buffer;
|
||||
snprintf(Buffer, sizeof(Buffer)-1, "%10i", price);
|
||||
LogText += " TotalValue: ";
|
||||
LogText += Buffer;
|
||||
snprintf(Buffer, sizeof(Buffer)-1, " ItemID: %7i", item->ID);
|
||||
LogText += Buffer;
|
||||
LogText += " ";
|
||||
snprintf(Buffer, sizeof(Buffer)-1, " %s", item->Name);
|
||||
LogText += Buffer;
|
||||
|
||||
if (buying==true) {
|
||||
database.logevents(player->AccountName(),player->AccountID(),player->admin,player->GetName(),merchant->GetName(),"Buying from Merchant",LogText.c_str(),2);
|
||||
}
|
||||
else {
|
||||
database.logevents(player->AccountName(),player->AccountID(),player->admin,player->GetName(),merchant->GetName(),"Selling to Merchant",LogText.c_str(),3);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::Disarm(Client* disarmer, int chance) {
|
||||
int16 slot = EQ::invslot::SLOT_INVALID;
|
||||
const EQ::ItemInstance *inst = GetInv().GetItem(EQ::invslot::slotPrimary);
|
||||
@@ -4044,36 +4040,42 @@ void Client::KeyRingList()
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::IsDiscovered(uint32 itemid) {
|
||||
|
||||
std::string query = StringFormat("SELECT count(*) FROM discovered_items WHERE item_id = '%lu'", itemid);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
bool Client::IsDiscovered(uint32 item_id) {
|
||||
const auto& l = DiscoveredItemsRepository::GetWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"item_id = {}",
|
||||
item_id
|
||||
)
|
||||
);
|
||||
if (l.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto row = results.begin();
|
||||
if (!atoi(row[0]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Client::DiscoverItem(uint32 itemid) {
|
||||
void Client::DiscoverItem(uint32 item_id) {
|
||||
auto e = DiscoveredItemsRepository::NewEntity();
|
||||
|
||||
std::string query = StringFormat("INSERT INTO discovered_items "
|
||||
"SET item_id = %lu, char_name = '%s', "
|
||||
"discovered_date = UNIX_TIMESTAMP(), account_status = %i",
|
||||
itemid, GetName(), Admin());
|
||||
auto results = database.QueryDatabase(query);
|
||||
e.account_status = Admin();
|
||||
e.char_name = GetCleanName();
|
||||
e.discovered_date = std::time(nullptr);
|
||||
e.item_id = item_id;
|
||||
|
||||
auto* inst = database.CreateItem(itemid);
|
||||
auto d = DiscoveredItemsRepository::InsertOne(database, e);
|
||||
|
||||
std::vector<std::any> args;
|
||||
parse->EventPlayer(EVENT_DISCOVER_ITEM, this, "", item_id);
|
||||
|
||||
args.emplace_back(inst);
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::DISCOVER_ITEM)) {
|
||||
const auto* item = database.GetItem(item_id);
|
||||
|
||||
parse->EventPlayer(EVENT_DISCOVER_ITEM, this, "", itemid, &args);
|
||||
auto e = PlayerEvent::DiscoverItemEvent{
|
||||
.item_id = item_id,
|
||||
.item_name = item->Name,
|
||||
};
|
||||
RecordPlayerEventLog(PlayerEvent::DISCOVER_ITEM, e);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::UpdateLFP() {
|
||||
@@ -6870,7 +6872,7 @@ void Client::SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount)
|
||||
SendAlternateCurrencyValue(currency_id);
|
||||
}
|
||||
|
||||
void Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method)
|
||||
int Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method)
|
||||
{
|
||||
|
||||
/* Added via Quest, rest of the logging methods may be done inline due to information available in that area of the code */
|
||||
@@ -6883,12 +6885,12 @@ void Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 me
|
||||
}
|
||||
|
||||
if(amount == 0) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!alternate_currency_loaded) {
|
||||
alternate_currency_queued_operations.push(std::make_pair(currency_id, amount));
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int new_value = 0;
|
||||
@@ -6907,6 +6909,8 @@ void Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 me
|
||||
database.UpdateAltCurrencyValue(CharacterID(), currency_id, new_value);
|
||||
}
|
||||
SendAlternateCurrencyValue(currency_id);
|
||||
|
||||
return new_value;
|
||||
}
|
||||
|
||||
void Client::SendAlternateCurrencyValues()
|
||||
@@ -11859,7 +11863,7 @@ void Client::SendPath(Mob* target)
|
||||
target->CastToClient()->Trader ||
|
||||
target->CastToClient()->Buyer
|
||||
)
|
||||
) {
|
||||
) {
|
||||
Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
@@ -11894,7 +11898,8 @@ void Client::SendPath(Mob* target)
|
||||
|
||||
points.push_back(a);
|
||||
points.push_back(b);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
glm::vec3 path_start(
|
||||
GetX(),
|
||||
GetY(),
|
||||
@@ -11907,8 +11912,8 @@ void Client::SendPath(Mob* target)
|
||||
target->GetZ() + (target->GetSize() < 6.0 ? 6 : target->GetSize()) * HEAD_POSITION
|
||||
);
|
||||
|
||||
bool partial = false;
|
||||
bool stuck = false;
|
||||
bool partial = false;
|
||||
bool stuck = false;
|
||||
auto path_list = zone->pathing->FindRoute(path_start, path_end, partial, stuck);
|
||||
|
||||
if (path_list.empty() || partial) {
|
||||
@@ -11939,7 +11944,7 @@ void Client::SendPath(Mob* target)
|
||||
p.z = GetZ();
|
||||
points.push_back(p);
|
||||
|
||||
for (const auto& n : path_list) {
|
||||
for (const auto &n: path_list) {
|
||||
if (n.teleport) {
|
||||
leads_to_teleporter = true;
|
||||
break;
|
||||
@@ -11967,10 +11972,201 @@ void Client::SendPath(Mob* target)
|
||||
SendPathPacket(points);
|
||||
}
|
||||
|
||||
void Client::UseAugmentContainer(int container_slot) {
|
||||
void Client::UseAugmentContainer(int container_slot)
|
||||
{
|
||||
auto in_augment = new AugmentItem_Struct[sizeof(AugmentItem_Struct)];
|
||||
in_augment->container_slot = container_slot;
|
||||
in_augment->augment_slot = -1;
|
||||
in_augment->augment_slot = -1;
|
||||
Object::HandleAugmentation(this, in_augment, nullptr);
|
||||
safe_delete_array(in_augment);
|
||||
}
|
||||
|
||||
PlayerEvent::PlayerEvent Client::GetPlayerEvent()
|
||||
{
|
||||
auto e = PlayerEvent::PlayerEvent{};
|
||||
e.account_id = AccountID();
|
||||
e.character_id = CharacterID();
|
||||
e.character_name = GetCleanName();
|
||||
e.x = GetX();
|
||||
e.y = GetY();
|
||||
e.z = GetZ();
|
||||
e.heading = GetHeading();
|
||||
e.zone_id = GetZoneID();
|
||||
e.zone_short_name = zone ? zone->GetShortName() : "";
|
||||
e.zone_long_name = zone ? zone->GetLongName() : "";
|
||||
e.instance_id = GetInstanceID();
|
||||
e.guild_id = GuildID();
|
||||
e.guild_name = guild_mgr.GetGuildName(GuildID());
|
||||
e.account_name = AccountName();
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void Client::PlayerTradeEventLog(Trade *t, Trade *t2)
|
||||
{
|
||||
Client *trader = t->GetOwner()->CastToClient();
|
||||
Client *trader2 = t2->GetOwner()->CastToClient();
|
||||
uint8 t_item_count = 0;
|
||||
uint8 t2_item_count = 0;
|
||||
|
||||
auto money_t = PlayerEvent::Money{
|
||||
.platinum = t->pp,
|
||||
.gold = t->gp,
|
||||
.silver = t->sp,
|
||||
.copper = t->cp,
|
||||
};
|
||||
auto money_t2 = PlayerEvent::Money{
|
||||
.platinum = t2->pp,
|
||||
.gold = t2->gp,
|
||||
.silver = t2->sp,
|
||||
.copper = t2->cp,
|
||||
};
|
||||
|
||||
// trader 1 item count
|
||||
for (uint16 i = EQ::invslot::TRADE_BEGIN; i <= EQ::invslot::TRADE_END; i++) {
|
||||
if (trader->GetInv().GetItem(i)) {
|
||||
t_item_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// trader 2 item count
|
||||
for (uint16 i = EQ::invslot::TRADE_BEGIN; i <= EQ::invslot::TRADE_END; i++) {
|
||||
if (trader2->GetInv().GetItem(i)) {
|
||||
t2_item_count++;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<PlayerEvent::TradeItemEntry> t_entries = {};
|
||||
t_entries.reserve(t_item_count);
|
||||
if (t_item_count > 0) {
|
||||
for (uint16 i = EQ::invslot::TRADE_BEGIN; i <= EQ::invslot::TRADE_END; i++) {
|
||||
const EQ::ItemInstance *inst = trader->GetInv().GetItem(i);
|
||||
if (inst) {
|
||||
t_entries.emplace_back(
|
||||
PlayerEvent::TradeItemEntry{
|
||||
.slot = i,
|
||||
.item_id = inst->GetItem()->ID,
|
||||
.item_name = inst->GetItem()->Name,
|
||||
.charges = static_cast<uint16>(inst->GetCharges()),
|
||||
.aug_1_item_id = inst->GetAugmentItemID(0),
|
||||
.aug_1_item_name = inst->GetAugment(0) ? inst->GetAugment(0)->GetItem()->Name : "",
|
||||
.aug_2_item_id = inst->GetAugmentItemID(1),
|
||||
.aug_2_item_name = inst->GetAugment(1) ? inst->GetAugment(1)->GetItem()->Name : "",
|
||||
.aug_3_item_id = inst->GetAugmentItemID(2),
|
||||
.aug_3_item_name = inst->GetAugment(2) ? inst->GetAugment(2)->GetItem()->Name : "",
|
||||
.aug_4_item_id = inst->GetAugmentItemID(3),
|
||||
.aug_4_item_name = inst->GetAugment(3) ? inst->GetAugment(3)->GetItem()->Name : "",
|
||||
.aug_5_item_id = inst->GetAugmentItemID(4),
|
||||
.aug_5_item_name = inst->GetAugment(4) ? inst->GetAugment(4)->GetItem()->Name : "",
|
||||
.aug_6_item_id = inst->GetAugmentItemID(5),
|
||||
.aug_6_item_name = inst->GetAugment(5) ? inst->GetAugment(5)->GetItem()->Name : "",
|
||||
.in_bag = 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) {
|
||||
t_entries.emplace_back(
|
||||
PlayerEvent::TradeItemEntry{
|
||||
.slot = j,
|
||||
.item_id = inst->GetItem()->ID,
|
||||
.item_name = inst->GetItem()->Name,
|
||||
.charges = static_cast<uint16>(inst->GetCharges()),
|
||||
.aug_1_item_id = inst->GetAugmentItemID(0),
|
||||
.aug_1_item_name = inst->GetAugment(0) ? inst->GetAugment(0)->GetItem()->Name : "",
|
||||
.aug_2_item_id = inst->GetAugmentItemID(1),
|
||||
.aug_2_item_name = inst->GetAugment(1) ? inst->GetAugment(1)->GetItem()->Name : "",
|
||||
.aug_3_item_id = inst->GetAugmentItemID(2),
|
||||
.aug_3_item_name = inst->GetAugment(2) ? inst->GetAugment(2)->GetItem()->Name : "",
|
||||
.aug_4_item_id = inst->GetAugmentItemID(3),
|
||||
.aug_4_item_name = inst->GetAugment(3) ? inst->GetAugment(3)->GetItem()->Name : "",
|
||||
.aug_5_item_id = inst->GetAugmentItemID(4),
|
||||
.aug_5_item_name = inst->GetAugment(4) ? inst->GetAugment(4)->GetItem()->Name : "",
|
||||
.aug_6_item_id = inst->GetAugmentItemID(5),
|
||||
.aug_6_item_name = inst->GetAugment(5) ? inst->GetAugment(5)->GetItem()->Name : "",
|
||||
.in_bag = true,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<PlayerEvent::TradeItemEntry> t2_entries = {};
|
||||
t_entries.reserve(t2_item_count);
|
||||
if (t2_item_count > 0) {
|
||||
for (uint16 i = EQ::invslot::TRADE_BEGIN; i <= EQ::invslot::TRADE_END; i++) {
|
||||
const EQ::ItemInstance *inst = trader2->GetInv().GetItem(i);
|
||||
if (inst) {
|
||||
t2_entries.emplace_back(
|
||||
PlayerEvent::TradeItemEntry{
|
||||
.slot = i,
|
||||
.item_id = inst->GetItem()->ID,
|
||||
.item_name = inst->GetItem()->Name,
|
||||
.charges = static_cast<uint16>(inst->GetCharges()),
|
||||
.aug_1_item_id = inst->GetAugmentItemID(0),
|
||||
.aug_1_item_name = inst->GetAugment(0) ? inst->GetAugment(0)->GetItem()->Name : "",
|
||||
.aug_2_item_id = inst->GetAugmentItemID(1),
|
||||
.aug_2_item_name = inst->GetAugment(1) ? inst->GetAugment(1)->GetItem()->Name : "",
|
||||
.aug_3_item_id = inst->GetAugmentItemID(2),
|
||||
.aug_3_item_name = inst->GetAugment(2) ? inst->GetAugment(2)->GetItem()->Name : "",
|
||||
.aug_4_item_id = inst->GetAugmentItemID(3),
|
||||
.aug_4_item_name = inst->GetAugment(3) ? inst->GetAugment(3)->GetItem()->Name : "",
|
||||
.aug_5_item_id = inst->GetAugmentItemID(4),
|
||||
.aug_5_item_name = inst->GetAugment(4) ? inst->GetAugment(4)->GetItem()->Name : "",
|
||||
.aug_6_item_id = inst->GetAugmentItemID(5),
|
||||
.aug_6_item_name = inst->GetAugment(5) ? inst->GetAugment(5)->GetItem()->Name : "",
|
||||
.in_bag = false,
|
||||
}
|
||||
);
|
||||
|
||||
if (inst->IsClassBag()) {
|
||||
for (uint8 j = EQ::invbag::SLOT_BEGIN; j <= EQ::invbag::SLOT_END; j++) {
|
||||
inst = trader2->GetInv().GetItem(i, j);
|
||||
if (inst) {
|
||||
t2_entries.emplace_back(
|
||||
PlayerEvent::TradeItemEntry{
|
||||
.slot = j,
|
||||
.item_id = inst->GetItem()->ID,
|
||||
.item_name = inst->GetItem()->Name,
|
||||
.charges = static_cast<uint16>(inst->GetCharges()),
|
||||
.aug_1_item_id = inst->GetAugmentItemID(0),
|
||||
.aug_1_item_name = inst->GetAugment(0) ? inst->GetAugment(0)->GetItem()->Name : "",
|
||||
.aug_2_item_id = inst->GetAugmentItemID(1),
|
||||
.aug_2_item_name = inst->GetAugment(1) ? inst->GetAugment(1)->GetItem()->Name : "",
|
||||
.aug_3_item_id = inst->GetAugmentItemID(2),
|
||||
.aug_3_item_name = inst->GetAugment(2) ? inst->GetAugment(2)->GetItem()->Name : "",
|
||||
.aug_4_item_id = inst->GetAugmentItemID(3),
|
||||
.aug_4_item_name = inst->GetAugment(3) ? inst->GetAugment(3)->GetItem()->Name : "",
|
||||
.aug_5_item_id = inst->GetAugmentItemID(4),
|
||||
.aug_5_item_name = inst->GetAugment(4) ? inst->GetAugment(4)->GetItem()->Name : "",
|
||||
.aug_6_item_id = inst->GetAugmentItemID(5),
|
||||
.aug_6_item_name = inst->GetAugment(5) ? inst->GetAugment(5)->GetItem()->Name : "",
|
||||
.in_bag = true,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto e = PlayerEvent::TradeEvent{
|
||||
.character_1_id = trader->CharacterID(),
|
||||
.character_1_name = trader->GetCleanName(),
|
||||
.character_2_id = trader2->CharacterID(),
|
||||
.character_2_name = trader2->GetCleanName(),
|
||||
.character_1_give_money = money_t,
|
||||
.character_2_give_money = money_t2,
|
||||
.character_1_give_items = t_entries,
|
||||
.character_2_give_items = t2_entries
|
||||
};
|
||||
|
||||
RecordPlayerEventLogWithClient(trader, PlayerEvent::TRADE, e);
|
||||
RecordPlayerEventLogWithClient(trader2, PlayerEvent::TRADE, e);
|
||||
}
|
||||
|
||||
+5
-2
@@ -67,6 +67,7 @@ namespace EQ
|
||||
#include "task_manager.h"
|
||||
#include "task_client_state.h"
|
||||
#include "cheat_manager.h"
|
||||
#include "../common/events/player_events.h"
|
||||
|
||||
#ifdef _WINDOWS
|
||||
// since windows defines these within windef.h (which windows.h include)
|
||||
@@ -329,7 +330,6 @@ public:
|
||||
bool ShouldISpawnFor(Client *c) { return !GMHideMe(c) && !IsHoveringForRespawn(); }
|
||||
virtual bool Process();
|
||||
void ProcessPackets();
|
||||
void LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 price, const EQ::ItemData* item, bool buying);
|
||||
void QueuePacket(const EQApplicationPacket* app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL, eqFilterType filter=FilterNone);
|
||||
void FastQueuePacket(EQApplicationPacket** app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL);
|
||||
void ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_skill, const char* orig_message, const char* targetname = nullptr, bool is_silent = false);
|
||||
@@ -1487,7 +1487,7 @@ public:
|
||||
void ConsentCorpses(std::string consent_name, bool deny = false);
|
||||
void SendAltCurrencies();
|
||||
void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount);
|
||||
void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0);
|
||||
int AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0);
|
||||
void SendAlternateCurrencyValues();
|
||||
void SendAlternateCurrencyValue(uint32 currency_id, bool send_if_null = true);
|
||||
uint32 GetAlternateCurrencyValue(uint32 currency_id) const;
|
||||
@@ -1679,6 +1679,8 @@ public:
|
||||
|
||||
std::string GetGuildPublicNote();
|
||||
|
||||
PlayerEvent::PlayerEvent GetPlayerEvent();
|
||||
void RecordKilledNPCEvent(NPC *n);
|
||||
protected:
|
||||
friend class Mob;
|
||||
void CalcItemBonuses(StatBonuses* newbon);
|
||||
@@ -2086,6 +2088,7 @@ private:
|
||||
|
||||
bool CanTradeFVNoDropItem();
|
||||
void SendMobPositions();
|
||||
void PlayerTradeEventLog(Trade *t, Trade *t2);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+238
-87
@@ -71,6 +71,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "../common/repositories/account_repository.h"
|
||||
|
||||
#include "bot.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
extern QueryServ* QServ;
|
||||
extern Zone* zone;
|
||||
@@ -790,6 +791,7 @@ void Client::CompleteConnect()
|
||||
/* This sub event is for if a player logs in for the first time since entering world. */
|
||||
if (firstlogon == 1) {
|
||||
parse->EventPlayer(EVENT_CONNECT, this, "", 0);
|
||||
RecordPlayerEventLog(PlayerEvent::WENT_ONLINE, PlayerEvent::EmptyEvent{});
|
||||
/* QS: PlayerLogConnectDisconnect */
|
||||
if (RuleB(QueryServ, PlayerLogConnectDisconnect)) {
|
||||
std::string event_desc = StringFormat("Connect :: Logged into zoneid:%i instid:%i", GetZoneID(), GetInstanceID());
|
||||
@@ -2240,9 +2242,6 @@ void Client::Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app)
|
||||
return;
|
||||
}
|
||||
|
||||
if (RuleB(EventLog, RecordSellToMerchant))
|
||||
LogMerchant(this, vendor, ams_in->charges, price, item, false);
|
||||
|
||||
if (!inst->IsStackable())
|
||||
{
|
||||
DeleteItemInInventory(ams_in->slot);
|
||||
@@ -2600,10 +2599,28 @@ void Client::Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app)
|
||||
);
|
||||
parse->EventPlayer(EVENT_ALT_CURRENCY_MERCHANT_BUY, this, export_string, 0);
|
||||
|
||||
AddAlternateCurrencyValue(alt_cur_id, -((int32)cost));
|
||||
int16 charges = 1;
|
||||
if (item->MaxCharges != 0)
|
||||
uint64 current_balance = AddAlternateCurrencyValue(alt_cur_id, -((int32) cost));
|
||||
int16 charges = 1;
|
||||
if (item->MaxCharges != 0) {
|
||||
charges = item->MaxCharges;
|
||||
}
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::MERCHANT_PURCHASE)) {
|
||||
auto e = PlayerEvent::MerchantPurchaseEvent{
|
||||
.npc_id = tar->GetNPCTypeID(),
|
||||
.merchant_name = tar->GetCleanName(),
|
||||
.merchant_type = tar->MerchantType,
|
||||
.item_id = item->ID,
|
||||
.item_name = item->Name,
|
||||
.charges = charges,
|
||||
.cost = cost,
|
||||
.alternate_currency_id = alt_cur_id,
|
||||
.player_money_balance = GetCarriedMoney(),
|
||||
.player_currency_balance = current_balance,
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::MERCHANT_PURCHASE, e);
|
||||
}
|
||||
|
||||
EQ::ItemInstance *inst = database.CreateItem(item, charges);
|
||||
if (!AutoPutLootInInventory(*inst, true, true))
|
||||
@@ -2774,7 +2791,25 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app)
|
||||
parse->EventPlayer(EVENT_ALT_CURRENCY_MERCHANT_SELL, this, export_string, 0);
|
||||
|
||||
FastQueuePacket(&outapp);
|
||||
AddAlternateCurrencyValue(alt_cur_id, cost);
|
||||
uint64 new_balance = AddAlternateCurrencyValue(alt_cur_id, cost);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::MERCHANT_SELL)) {
|
||||
auto e = PlayerEvent::MerchantSellEvent{
|
||||
.npc_id = tar->GetNPCTypeID(),
|
||||
.merchant_name = tar->GetCleanName(),
|
||||
.merchant_type = tar->CastToNPC()->MerchantType,
|
||||
.item_id = item->ID,
|
||||
.item_name = item->Name,
|
||||
.charges = static_cast<int16>(sell->charges),
|
||||
.cost = cost,
|
||||
.alternate_currency_id = 0,
|
||||
.player_money_balance = GetCarriedMoney(),
|
||||
.player_currency_balance = new_balance,
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::MERCHANT_SELL, e);
|
||||
}
|
||||
|
||||
Save(1);
|
||||
}
|
||||
}
|
||||
@@ -3441,10 +3476,10 @@ void Client::Handle_OP_BankerChange(const EQApplicationPacket *app)
|
||||
|
||||
if (!banker || distance > USE_NPC_RANGE2)
|
||||
{
|
||||
auto hacked_string = fmt::format(
|
||||
auto message = fmt::format(
|
||||
"Player tried to make use of a banker(money) but {} is non-existant or too far away ({} units).",
|
||||
banker ? banker->GetName() : "UNKNOWN NPC", distance);
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName());
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4164,7 +4199,8 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
|
||||
const EQ::ItemData* item = inst->GetItem();
|
||||
if (item->Click.Effect != (uint32)castspell->spell_id)
|
||||
{
|
||||
database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, tried to cast a different spell.", zone->GetShortName());
|
||||
std::string message = fmt::format("OP_CastSpell with item, tried to cast a different spell than what was on item - item spell id [{}] attempted [{}]", item->Click.Effect, (uint32)castspell->spell_id);
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
InterruptSpell(castspell->spell_id); //CHEATER!!
|
||||
return;
|
||||
}
|
||||
@@ -4205,7 +4241,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
|
||||
}
|
||||
else
|
||||
{
|
||||
database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, did not meet req level.", zone->GetShortName());
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "OP_CastSpell with item, did not meet req level."});
|
||||
Message(Chat::Red, "Error: level not high enough.", castspell->inventoryslot);
|
||||
InterruptSpell(castspell->spell_id);
|
||||
}
|
||||
@@ -5145,8 +5181,8 @@ void Client::Handle_OP_ControlBoat(const EQApplicationPacket *app)
|
||||
|
||||
if (!boat->IsNPC() || !boat->IsControllableBoat())
|
||||
{
|
||||
auto hacked_string = fmt::format("OP_Control Boat was sent against {} which is of race {}", boat->GetName(), boat->GetRace());
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName());
|
||||
auto message = fmt::format("OP_Control Boat was sent against {} which is of race {}", boat->GetName(), boat->GetRace());
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5415,6 +5451,17 @@ void Client::Handle_OP_DeleteItem(const EQApplicationPacket *app)
|
||||
}
|
||||
DeleteItemInInventory(alc->from_slot, 1);
|
||||
|
||||
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 deleted",
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::ITEM_DESTROY, e);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5466,8 +5513,9 @@ void Client::Handle_OP_Disarm(const EQApplicationPacket *app) {
|
||||
return;
|
||||
if (pmob->GetID() != GetID()) {
|
||||
// Client sent a disarm request with an originator ID not matching their own ID.
|
||||
auto hack_str = fmt::format("Player {} ({}) sent OP_Disarm with source ID of: {}", GetCleanName(), GetID(), pmob->GetID());
|
||||
database.SetMQDetectionFlag(account_name, name, hack_str, zone->GetShortName());
|
||||
auto message = fmt::format("Player {} ({}) sent OP_Disarm with source ID of: {}", GetCleanName(), GetID(), pmob->GetID());
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
|
||||
return;
|
||||
}
|
||||
// No disarm on corpses
|
||||
@@ -6194,7 +6242,7 @@ void Client::Handle_OP_GMBecomeNPC(const EQApplicationPacket *app)
|
||||
{
|
||||
if (Admin() < minStatusToUseGMCommands) {
|
||||
Message(Chat::Red, "Your account has been reported for hacking.");
|
||||
database.SetHackerFlag(account_name, name, "/becomenpc");
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "Used /becomenpc when they shouldn't be able to"});
|
||||
return;
|
||||
}
|
||||
if (app->size != sizeof(BecomeNPC_Struct)) {
|
||||
@@ -6234,7 +6282,7 @@ void Client::Handle_OP_GMDelCorpse(const EQApplicationPacket *app)
|
||||
return;
|
||||
if (Admin() < commandEditPlayerCorpses) {
|
||||
Message(Chat::Red, "Your account has been reported for hacking.");
|
||||
database.SetHackerFlag(account_name, name, "/delcorpse");
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "Used /delcorpse"});
|
||||
return;
|
||||
}
|
||||
GMDelCorpse_Struct* dc = (GMDelCorpse_Struct *)app->pBuffer;
|
||||
@@ -6255,7 +6303,6 @@ void Client::Handle_OP_GMEmoteZone(const EQApplicationPacket *app)
|
||||
{
|
||||
if (Admin() < minStatusToUseGMCommands) {
|
||||
Message(Chat::Red, "Your account has been reported for hacking.");
|
||||
database.SetHackerFlag(account_name, name, "/emote");
|
||||
return;
|
||||
}
|
||||
if (app->size != sizeof(GMEmoteZone_Struct)) {
|
||||
@@ -6288,7 +6335,7 @@ void Client::Handle_OP_GMFind(const EQApplicationPacket *app)
|
||||
{
|
||||
if (Admin() < minStatusToUseGMCommands) {
|
||||
Message(Chat::Red, "Your account has been reported for hacking.");
|
||||
database.SetHackerFlag(account_name, name, "/find");
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "Used /find"});
|
||||
return;
|
||||
}
|
||||
if (app->size != sizeof(GMSummon_Struct)) {
|
||||
@@ -6326,7 +6373,7 @@ void Client::Handle_OP_GMGoto(const EQApplicationPacket *app)
|
||||
}
|
||||
if (Admin() < minStatusToUseGMCommands) {
|
||||
Message(Chat::Red, "Your account has been reported for hacking.");
|
||||
database.SetHackerFlag(account_name, name, "/goto");
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "Used /goto"});
|
||||
return;
|
||||
}
|
||||
GMSummon_Struct* gmg = (GMSummon_Struct*)app->pBuffer;
|
||||
@@ -6353,7 +6400,7 @@ void Client::Handle_OP_GMHideMe(const EQApplicationPacket *app)
|
||||
{
|
||||
if (Admin() < minStatusToUseGMCommands) {
|
||||
Message(Chat::Red, "Your account has been reported for hacking.");
|
||||
database.SetHackerFlag(account_name, name, "/hideme");
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "Used /hideme"});
|
||||
return;
|
||||
}
|
||||
if (app->size != sizeof(SpawnAppearance_Struct)) {
|
||||
@@ -6373,7 +6420,7 @@ void Client::Handle_OP_GMKick(const EQApplicationPacket *app)
|
||||
return;
|
||||
if (Admin() < minStatusToKick) {
|
||||
Message(Chat::Red, "Your account has been reported for hacking.");
|
||||
database.SetHackerFlag(account_name, name, "/kick");
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "Used /kick"});
|
||||
return;
|
||||
}
|
||||
GMKick_Struct* gmk = (GMKick_Struct *)app->pBuffer;
|
||||
@@ -6403,7 +6450,7 @@ void Client::Handle_OP_GMKill(const EQApplicationPacket *app)
|
||||
{
|
||||
if (Admin() < minStatusToUseGMCommands) {
|
||||
Message(Chat::Red, "Your account has been reported for hacking.");
|
||||
database.SetHackerFlag(account_name, name, "/kill");
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "Used /kill"});
|
||||
return;
|
||||
}
|
||||
if (app->size != sizeof(GMKill_Struct)) {
|
||||
@@ -6455,7 +6502,7 @@ void Client::Handle_OP_GMLastName(const EQApplicationPacket *app)
|
||||
else {
|
||||
if (Admin() < minStatusToUseGMCommands) {
|
||||
Message(Chat::Red, "Your account has been reported for hacking.");
|
||||
database.SetHackerFlag(client->account_name, client->name, "/lastname");
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "Used /lastname"});
|
||||
return;
|
||||
}
|
||||
else
|
||||
@@ -6480,7 +6527,7 @@ void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app)
|
||||
const GMName_Struct* gmn = (const GMName_Struct *)app->pBuffer;
|
||||
if (Admin() < minStatusToUseGMCommands) {
|
||||
Message(Chat::Red, "Your account has been reported for hacking.");
|
||||
database.SetHackerFlag(account_name, name, "/name");
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "Used /name"});
|
||||
return;
|
||||
}
|
||||
Client* client = entity_list.GetClientByName(gmn->oldname);
|
||||
@@ -6623,7 +6670,7 @@ void Client::Handle_OP_GMToggle(const EQApplicationPacket *app)
|
||||
}
|
||||
if (Admin() < minStatusToUseGMCommands) {
|
||||
Message(Chat::Red, "Your account has been reported for hacking.");
|
||||
database.SetHackerFlag(account_name, name, "/toggle");
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "Used /toggle"});
|
||||
return;
|
||||
}
|
||||
GMToggle_Struct *ts = (GMToggle_Struct *)app->pBuffer;
|
||||
@@ -6670,7 +6717,7 @@ void Client::Handle_OP_GMZoneRequest(const EQApplicationPacket *app)
|
||||
}
|
||||
if (Admin() < minStatusToBeGM) {
|
||||
Message(Chat::Red, "Your account has been reported for hacking.");
|
||||
database.SetHackerFlag(account_name, name, "/zone");
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "Used /zone"});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6726,7 +6773,7 @@ void Client::Handle_OP_GMZoneRequest2(const EQApplicationPacket *app)
|
||||
{
|
||||
if (Admin() < minStatusToBeGM) {
|
||||
Message(Chat::Red, "Your account has been reported for hacking.");
|
||||
database.SetHackerFlag(account_name, name, "/zone");
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "Used /zone"});
|
||||
return;
|
||||
}
|
||||
if (app->size < sizeof(uint32)) {
|
||||
@@ -8400,9 +8447,12 @@ void Client::Handle_OP_Illusion(const EQApplicationPacket *app)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetGM())
|
||||
{
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), "OP_Illusion sent by non Game Master.", zone->GetShortName());
|
||||
if (!GetGM()) {
|
||||
RecordPlayerEventLog(
|
||||
PlayerEvent::POSSIBLE_HACK,
|
||||
PlayerEvent::PossibleHackEvent{.message = "OP_Illusion sent by non Game Master"}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10199,13 +10249,13 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app)
|
||||
{
|
||||
const EQ::ItemInstance *itm_from = GetInv().GetItem(mi->from_slot);
|
||||
const EQ::ItemInstance *itm_to = GetInv().GetItem(mi->to_slot);
|
||||
auto detect = fmt::format("Player issued a move item from {}(item id {}) to {}(item id {}) while casting {}.",
|
||||
auto message = fmt::format("Player issued a move item from {}(item id {}) to {}(item id {}) while casting {}.",
|
||||
mi->from_slot,
|
||||
itm_from ? itm_from->GetID() : 0,
|
||||
mi->to_slot,
|
||||
itm_to ? itm_to->GetID() : 0,
|
||||
casting_spell_id);
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), detect, zone->GetShortName());
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
Kick("Inventory desync"); // Kick client to prevent client and server from getting out-of-sync inventory slots
|
||||
return;
|
||||
}
|
||||
@@ -11188,7 +11238,7 @@ void Client::Handle_OP_PickPocket(const EQApplicationPacket *app)
|
||||
if (!p_timers.Expired(&database, pTimerBeggingPickPocket, false))
|
||||
{
|
||||
Message(Chat::Red, "Ability recovery time not yet met.");
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), "OP_PickPocket was sent again too quickly.", zone->GetShortName());
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "OP_PickPocket was sent again too quickly."});
|
||||
return;
|
||||
}
|
||||
PickPocket_Struct* pick_in = (PickPocket_Struct*)app->pBuffer;
|
||||
@@ -11210,7 +11260,7 @@ void Client::Handle_OP_PickPocket(const EQApplicationPacket *app)
|
||||
}
|
||||
else if (Distance(GetPosition(), victim->GetPosition()) > 20) {
|
||||
Message(Chat::Red, "Attempt to pickpocket out of range detected.");
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), "OP_PickPocket was sent from outside combat range.", zone->GetShortName());
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "OP_PickPocket was sent from outside combat range"});
|
||||
}
|
||||
else if (victim->IsNPC()) {
|
||||
auto body = victim->GetBodyType();
|
||||
@@ -12696,16 +12746,27 @@ void Client::Handle_OP_RezzAnswer(const EQApplicationPacket *app)
|
||||
{
|
||||
VERIFY_PACKET_LENGTH(OP_RezzAnswer, app, Resurrect_Struct);
|
||||
|
||||
const Resurrect_Struct* ra = (const Resurrect_Struct*)app->pBuffer;
|
||||
const auto* r = (const Resurrect_Struct*) app->pBuffer;
|
||||
|
||||
LogSpells("[Client::Handle_OP_RezzAnswer] Received OP_RezzAnswer from client. Pendingrezzexp is [{}] action is [{}]",
|
||||
PendingRezzXP, ra->action ? "ACCEPT" : "DECLINE");
|
||||
LogSpells(
|
||||
"[Client::Handle_OP_RezzAnswer] Received OP_RezzAnswer from client. Pendingrezzexp is [{}] action is [{}]",
|
||||
PendingRezzXP,
|
||||
r->action ? "ACCEPT" : "DECLINE"
|
||||
);
|
||||
|
||||
|
||||
OPRezzAnswer(ra->action, ra->spellid, ra->zone_id, ra->instance_id, ra->x, ra->y, ra->z);
|
||||
OPRezzAnswer(r->action, r->spellid, r->zone_id, r->instance_id, r->x, r->y, r->z);
|
||||
|
||||
if (r->action == ResurrectionActions::Accept) {
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::REZ_ACCEPTED)) {
|
||||
auto e = PlayerEvent::ResurrectAcceptEvent{
|
||||
.resurrecter_name = r->rezzer_name,
|
||||
.spell_name = spells[r->spellid].name,
|
||||
.spell_id = r->spellid,
|
||||
};
|
||||
RecordPlayerEventLog(PlayerEvent::REZ_ACCEPTED, e);
|
||||
}
|
||||
|
||||
if (ra->action == 1)
|
||||
{
|
||||
EQApplicationPacket* outapp = app->Copy();
|
||||
// Send the OP_RezzComplete to the world server. This finds it's way to the zone that
|
||||
// the rezzed corpse is in to mark the corpse as rezzed.
|
||||
@@ -12713,7 +12774,6 @@ void Client::Handle_OP_RezzAnswer(const EQApplicationPacket *app)
|
||||
worldserver.RezzPlayer(outapp, 0, 0, OP_RezzComplete);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void Client::Handle_OP_Sacrifice(const EQApplicationPacket *app)
|
||||
@@ -13236,10 +13296,19 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
|
||||
|
||||
if (!TakeMoneyFromPP(mpo->price))
|
||||
{
|
||||
auto hacker_str = fmt::format("Vendor Cheat: attempted to buy {} of {}: {} that cost {} cp but only has {} pp {} gp {} sp {} cp",
|
||||
mpo->quantity, item->ID, item->Name,
|
||||
mpo->price, m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper);
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), Strings::Escape(hacker_str), zone->GetShortName());
|
||||
auto message = fmt::format(
|
||||
"Vendor Cheat attempted to buy qty [{}] of item_id [{}] item_name[{}] that cost [{}] copper but only has platinum [{}] gold [{}] silver [{}] copper [{}]",
|
||||
mpo->quantity,
|
||||
item->ID,
|
||||
item->Name,
|
||||
mpo->price,
|
||||
m_pp.platinum,
|
||||
m_pp.gold,
|
||||
m_pp.silver,
|
||||
m_pp.copper
|
||||
);
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
safe_delete(outapp);
|
||||
safe_delete(inst);
|
||||
return;
|
||||
@@ -13304,6 +13373,23 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
|
||||
safe_delete(inst);
|
||||
safe_delete(outapp);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::MERCHANT_PURCHASE)) {
|
||||
auto e = PlayerEvent::MerchantPurchaseEvent{
|
||||
.npc_id = tmp->GetNPCTypeID(),
|
||||
.merchant_name = tmp->GetCleanName(),
|
||||
.merchant_type = tmp->CastToNPC()->MerchantType,
|
||||
.item_id = item->ID,
|
||||
.item_name = item->Name,
|
||||
.charges = static_cast<int16>(mpo->quantity),
|
||||
.cost = mpo->price,
|
||||
.alternate_currency_id = 0,
|
||||
.player_money_balance = GetCarriedMoney(),
|
||||
.player_currency_balance = 0,
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::MERCHANT_PURCHASE, e);
|
||||
}
|
||||
|
||||
// start QS code
|
||||
// stacking purchases not supported at this time - entire process will need some work to catch them properly
|
||||
if (RuleB(QueryServ, PlayerLogMerchantTransactions)) {
|
||||
@@ -13359,9 +13445,6 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
|
||||
}
|
||||
// end QS code
|
||||
|
||||
if (RuleB(EventLog, RecordBuyFromMerchant))
|
||||
LogMerchant(this, tmp, mpo->quantity, mpo->price, item, true);
|
||||
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {} {}",
|
||||
tmp->GetNPCTypeID(),
|
||||
@@ -13372,10 +13455,24 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
|
||||
);
|
||||
parse->EventPlayer(EVENT_MERCHANT_BUY, this, export_string, 0);
|
||||
|
||||
if ((RuleB(Character, EnableDiscoveredItems)))
|
||||
{
|
||||
if (!GetGM() && !IsDiscovered(item_id))
|
||||
DiscoverItem(item_id);
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::MERCHANT_PURCHASE)) {
|
||||
auto e = PlayerEvent::MerchantPurchaseEvent{
|
||||
.npc_id = tmp->GetNPCTypeID(),
|
||||
.merchant_name = tmp->GetCleanName(),
|
||||
.merchant_type = tmp->CastToNPC()->MerchantType,
|
||||
.item_id = item->ID,
|
||||
.item_name = item->Name,
|
||||
.charges = static_cast<int16>(mpo->quantity),
|
||||
.cost = mpo->price,
|
||||
.alternate_currency_id = 0,
|
||||
.player_money_balance = GetCarriedMoney(),
|
||||
.player_currency_balance = 0,
|
||||
};
|
||||
RecordPlayerEventLog(PlayerEvent::MERCHANT_PURCHASE, e);
|
||||
}
|
||||
|
||||
if (RuleB(Character, EnableDiscoveredItems) && !GetGM() && !IsDiscovered(item_id)) {
|
||||
DiscoverItem(item_id);
|
||||
}
|
||||
|
||||
t1.stop();
|
||||
@@ -13460,9 +13557,6 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
else
|
||||
mp->quantity = 1;
|
||||
|
||||
if (RuleB(EventLog, RecordSellToMerchant))
|
||||
LogMerchant(this, vendor, mp->quantity, price, item, false);
|
||||
|
||||
int charges = mp->quantity;
|
||||
|
||||
if (vendor->GetKeepsSoldItems()) {
|
||||
@@ -13475,7 +13569,7 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
charges,
|
||||
true
|
||||
)
|
||||
) > 0) {
|
||||
) > 0) {
|
||||
EQ::ItemInstance *inst2 = inst->Clone();
|
||||
|
||||
while (true) {
|
||||
@@ -13557,6 +13651,22 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
);
|
||||
parse->EventPlayer(EVENT_MERCHANT_SELL, this, export_string, 0);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::MERCHANT_SELL)) {
|
||||
auto e = PlayerEvent::MerchantSellEvent{
|
||||
.npc_id = vendor->GetNPCTypeID(),
|
||||
.merchant_name = vendor->GetCleanName(),
|
||||
.merchant_type = vendor->CastToNPC()->MerchantType,
|
||||
.item_id = item->ID,
|
||||
.item_name = item->Name,
|
||||
.charges = static_cast<int16>(mp->quantity),
|
||||
.cost = price,
|
||||
.alternate_currency_id = 0,
|
||||
.player_money_balance = GetCarriedMoney(),
|
||||
.player_currency_balance = 0,
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::MERCHANT_SELL, e);
|
||||
}
|
||||
|
||||
// Now remove the item from the player, this happens regardless of outcome
|
||||
DeleteItemInInventory(
|
||||
@@ -13747,8 +13857,8 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
|
||||
{
|
||||
if (ClientVersion() < EQ::versions::ClientVersion::SoF)
|
||||
{
|
||||
auto hack_str = fmt::format("Player sent OP_SpawnAppearance with AT_Invis: {}", sa->parameter);
|
||||
database.SetMQDetectionFlag(account_name, name, hack_str, zone->GetShortName());
|
||||
auto message = fmt::format("Player sent OP_SpawnAppearance with AT_Invis [{}]", sa->parameter);
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -13847,8 +13957,8 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
|
||||
{
|
||||
if (!HasSkill(EQ::skills::SkillSneak))
|
||||
{
|
||||
auto hack_str = fmt::format("Player sent OP_SpawnAppearance with AT_Sneak: {}", sa->parameter);
|
||||
database.SetMQDetectionFlag(account_name, name, hack_str, zone->GetShortName());
|
||||
auto message = fmt::format("Player sent OP_SpawnAppearance with AT_Sneak [{}]", sa->parameter);
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -13857,8 +13967,8 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
|
||||
}
|
||||
else if (sa->type == AT_Size)
|
||||
{
|
||||
auto hack_str = fmt::format("Player sent OP_SpawnAppearance with AT_Size: {}", sa->parameter);
|
||||
database.SetMQDetectionFlag(account_name, name, hack_str, zone->GetShortName());
|
||||
auto message = fmt::format("Player sent OP_SpawnAppearance with AT_Size [{}]", sa->parameter);
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
}
|
||||
else if (sa->type == AT_Light) // client emitting light (lightstone, shiny shield)
|
||||
{
|
||||
@@ -14201,9 +14311,14 @@ void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app)
|
||||
else if (GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special
|
||||
|| GetTarget()->GetBodyType() == BT_NoTarget)
|
||||
{
|
||||
auto hacker_str = fmt::format("{} attempting to target something untargetable, {} bodytype: {}",
|
||||
GetName(), GetTarget()->GetName(), (int)GetTarget()->GetBodyType());
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), Strings::Escape(hacker_str), zone->GetShortName());
|
||||
auto message = fmt::format(
|
||||
"[{}] attempting to target something untargetable [{}] bodytype [{}]",
|
||||
GetName(),
|
||||
GetTarget()->GetName(),
|
||||
(int) GetTarget()->GetBodyType()
|
||||
);
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
|
||||
SetTarget((Mob*)nullptr);
|
||||
return;
|
||||
}
|
||||
@@ -14232,15 +14347,16 @@ void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app)
|
||||
{
|
||||
if (DistanceSquared(m_Position, GetTarget()->GetPosition()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip))
|
||||
{
|
||||
auto hacker_str = fmt::format(
|
||||
"{} attempting to target something beyond the clip plane of {:.2f} "
|
||||
auto message = fmt::format(
|
||||
"[{}] attempting to target something beyond the clip plane of {:.2f} "
|
||||
"units, from ({:.2f}, {:.2f}, {:.2f}) to {} ({:.2f}, {:.2f}, "
|
||||
"{:.2f})",
|
||||
GetName(),
|
||||
(zone->newzone_data.maxclip * zone->newzone_data.maxclip), GetX(),
|
||||
GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(),
|
||||
GetTarget()->GetY(), GetTarget()->GetZ());
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), Strings::Escape(hacker_str), zone->GetShortName());
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
SetTarget(nullptr);
|
||||
return;
|
||||
}
|
||||
@@ -14248,13 +14364,21 @@ void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app)
|
||||
}
|
||||
else if (DistanceSquared(m_Position, GetTarget()->GetPosition()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip))
|
||||
{
|
||||
auto hacker_str =
|
||||
fmt::format("{} attempting to target something beyond the clip plane of {:.2f} "
|
||||
"units, from ({:.2f}, {:.2f}, {:.2f}) to {} ({:.2f}, {:.2f}, {:.2f})",
|
||||
GetName(), (zone->newzone_data.maxclip * zone->newzone_data.maxclip),
|
||||
GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(),
|
||||
GetTarget()->GetY(), GetTarget()->GetZ());
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), Strings::Escape(hacker_str), zone->GetShortName());
|
||||
auto message = fmt::format(
|
||||
"{} attempting to target something beyond the clip plane of {:.2f} "
|
||||
"units, from ({:.2f}, {:.2f}, {:.2f}) to {} ({:.2f}, {:.2f}, {:.2f})",
|
||||
GetName(),
|
||||
(zone->newzone_data.maxclip * zone->newzone_data.maxclip),
|
||||
GetX(),
|
||||
GetY(),
|
||||
GetZ(),
|
||||
GetTarget()->GetName(),
|
||||
GetTarget()->GetX(),
|
||||
GetTarget()->GetY(),
|
||||
GetTarget()->GetZ()
|
||||
);
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
SetTarget(nullptr);
|
||||
return;
|
||||
}
|
||||
@@ -14411,16 +14535,14 @@ void Client::Handle_OP_TradeAcceptClick(const EQApplicationPacket *app)
|
||||
// TODO: query (other) as a hacker
|
||||
}
|
||||
else {
|
||||
// Audit trade to database for both trade streams
|
||||
other->trade->LogTrade();
|
||||
trade->LogTrade();
|
||||
other->PlayerTradeEventLog(other->trade, trade);
|
||||
|
||||
// start QS code
|
||||
if (RuleB(QueryServ, PlayerLogTrades)) {
|
||||
QSPlayerLogTrade_Struct event_entry;
|
||||
PlayerLogTrade_Struct event_entry;
|
||||
std::list<void*> event_details;
|
||||
|
||||
memset(&event_entry, 0, sizeof(QSPlayerLogTrade_Struct));
|
||||
memset(&event_entry, 0, sizeof(PlayerLogTrade_Struct));
|
||||
|
||||
// Perform actual trade
|
||||
FinishTrade(other, true, &event_entry, &event_details);
|
||||
@@ -14430,18 +14552,18 @@ void Client::Handle_OP_TradeAcceptClick(const EQApplicationPacket *app)
|
||||
|
||||
auto qs_pack = new ServerPacket(
|
||||
ServerOP_QSPlayerLogTrades,
|
||||
sizeof(QSPlayerLogTrade_Struct) +
|
||||
(sizeof(QSTradeItems_Struct) * event_entry._detail_count));
|
||||
QSPlayerLogTrade_Struct* qs_buf = (QSPlayerLogTrade_Struct*)qs_pack->pBuffer;
|
||||
sizeof(PlayerLogTrade_Struct) +
|
||||
(sizeof(PlayerLogTradeItemsEntry_Struct) * event_entry._detail_count));
|
||||
PlayerLogTrade_Struct* qs_buf = (PlayerLogTrade_Struct*)qs_pack->pBuffer;
|
||||
|
||||
memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogTrade_Struct));
|
||||
memcpy(qs_buf, &event_entry, sizeof(PlayerLogTrade_Struct));
|
||||
|
||||
int offset = 0;
|
||||
|
||||
for (auto iter = event_details.begin(); iter != event_details.end();
|
||||
++iter, ++offset) {
|
||||
QSTradeItems_Struct* detail = reinterpret_cast<QSTradeItems_Struct*>(*iter);
|
||||
qs_buf->items[offset] = *detail;
|
||||
PlayerLogTradeItemsEntry_Struct* detail = reinterpret_cast<PlayerLogTradeItemsEntry_Struct*>(*iter);
|
||||
qs_buf->item_entries[offset] = *detail;
|
||||
safe_delete(detail);
|
||||
}
|
||||
|
||||
@@ -15767,3 +15889,32 @@ void Client::SendMobPositions()
|
||||
}
|
||||
safe_delete(p);
|
||||
}
|
||||
|
||||
struct RecordKillCheck {
|
||||
PlayerEvent::EventType event;
|
||||
bool check;
|
||||
};
|
||||
|
||||
void Client::RecordKilledNPCEvent(NPC *n)
|
||||
{
|
||||
bool is_named = Strings::Contains(n->GetName(), "#") && !n->IsRaidTarget();
|
||||
|
||||
std::vector<RecordKillCheck> checks = {
|
||||
RecordKillCheck{.event = PlayerEvent::KILLED_NPC, .check = true},
|
||||
RecordKillCheck{.event = PlayerEvent::KILLED_NAMED_NPC, .check = is_named},
|
||||
RecordKillCheck{.event = PlayerEvent::KILLED_RAID_NPC, .check = n->IsRaidTarget()},
|
||||
};
|
||||
|
||||
for (auto &c: checks) {
|
||||
if (c.check && player_event_logs.IsEventEnabled(c.event)) {
|
||||
auto e = PlayerEvent::KilledNPCEvent{
|
||||
.npc_id = n->GetNPCTypeID(),
|
||||
.npc_name = n->GetCleanName(),
|
||||
.combat_time_seconds = static_cast<uint32>(n->GetCombatRecord().TimeInCombat()),
|
||||
.total_damage_per_second_taken = static_cast<uint64>(n->GetCombatRecord().GetDamageReceivedPerSecond()),
|
||||
.total_heal_per_second_taken = static_cast<uint64>(n->GetCombatRecord().GetHealedReceivedPerSecond()),
|
||||
};
|
||||
RecordPlayerEventLog(c.event, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+34
-25
@@ -55,6 +55,7 @@
|
||||
#include "zone.h"
|
||||
#include "zonedb.h"
|
||||
#include "../common/zone_store.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
extern QueryServ* QServ;
|
||||
extern Zone* zone;
|
||||
@@ -184,6 +185,7 @@ bool Client::Process() {
|
||||
SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::Offline);
|
||||
|
||||
parse->EventPlayer(EVENT_DISCONNECT, this, "", 0);
|
||||
RecordPlayerEventLog(PlayerEvent::WENT_OFFLINE, PlayerEvent::EmptyEvent{});
|
||||
|
||||
return false; //delete client
|
||||
}
|
||||
@@ -542,7 +544,7 @@ bool Client::Process() {
|
||||
if (client_state == DISCONNECTED) {
|
||||
OnDisconnect(true);
|
||||
std::cout << "Client disconnected (cs=d): " << GetName() << std::endl;
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), "/MQInstantCamp: Possible instant camp disconnect.", zone->GetShortName());
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = "/MQInstantCamp: Possible instant camp disconnect"});
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -693,6 +695,7 @@ void Client::OnDisconnect(bool hard_disconnect) {
|
||||
MyRaid->MemberZoned(this);
|
||||
|
||||
parse->EventPlayer(EVENT_DISCONNECT, this, "", 0);
|
||||
RecordPlayerEventLog(PlayerEvent::WENT_OFFLINE, PlayerEvent::EmptyEvent{});
|
||||
|
||||
/* QS: PlayerLogConnectDisconnect */
|
||||
if (RuleB(QueryServ, PlayerLogConnectDisconnect)){
|
||||
@@ -1155,12 +1158,8 @@ void Client::OPMemorizeSpell(const EQApplicationPacket* app)
|
||||
if (HasSpellScribed(m->spell_id)) {
|
||||
MemSpell(m->spell_id, m->slot);
|
||||
} else {
|
||||
database.SetMQDetectionFlag(
|
||||
AccountName(),
|
||||
GetName(),
|
||||
"OP_MemorizeSpell but we don't have this spell scribed...",
|
||||
zone->GetShortName()
|
||||
);
|
||||
std::string message = fmt::format("OP_MemorizeSpell [{}] but we don't have this spell scribed", m->spell_id);
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1299,10 +1298,13 @@ void Client::OPMoveCoin(const EQApplicationPacket* app)
|
||||
NPC *banker = entity_list.GetClosestBanker(this, distance);
|
||||
if(!banker || distance > USE_NPC_RANGE2)
|
||||
{
|
||||
auto hacked_string = fmt::format("Player tried to make use of a banker(coin move) but "
|
||||
"{} is non-existant or too far away ({} units).",
|
||||
banker ? banker->GetName() : "UNKNOWN NPC", distance);
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName());
|
||||
auto message = fmt::format(
|
||||
"Player tried to make use of a banker (coin move) but "
|
||||
"banker [{}] is non-existent or too far away [{}] units",
|
||||
banker ? banker->GetName() : "UNKNOWN NPC", distance
|
||||
);
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1330,11 +1332,13 @@ void Client::OPMoveCoin(const EQApplicationPacket* app)
|
||||
NPC *banker = entity_list.GetClosestBanker(this, distance);
|
||||
if(!banker || distance > USE_NPC_RANGE2)
|
||||
{
|
||||
auto hacked_string =
|
||||
fmt::format("Player tried to make use of a banker(shared coin move) but {} is "
|
||||
"non-existant or too far away ({} units).",
|
||||
banker ? banker->GetName() : "UNKNOWN NPC", distance);
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName());
|
||||
auto message = fmt::format(
|
||||
"Player tried to make use of a banker (shared coin move) but banker [{}] is "
|
||||
"non-existent or too far away [{}] units",
|
||||
banker ? banker->GetName() : "UNKNOWN NPC", distance
|
||||
);
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
|
||||
return;
|
||||
}
|
||||
if(mc->cointype1 == COINTYPE_PP) // there's only platinum here
|
||||
@@ -1386,10 +1390,13 @@ void Client::OPMoveCoin(const EQApplicationPacket* app)
|
||||
NPC *banker = entity_list.GetClosestBanker(this, distance);
|
||||
if(!banker || distance > USE_NPC_RANGE2)
|
||||
{
|
||||
auto hacked_string = fmt::format("Player tried to make use of a banker(coin move) but "
|
||||
"{} is non-existant or too far away ({} units).",
|
||||
banker ? banker->GetName() : "UNKNOWN NPC", distance);
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName());
|
||||
auto message = fmt::format(
|
||||
"Player tried to make use of a banker(coin move) but "
|
||||
"banker [{}] is non-existent or too far away [{}] units",
|
||||
banker ? banker->GetName() : "UNKNOWN NPC", distance
|
||||
);
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
|
||||
return;
|
||||
}
|
||||
switch(mc->cointype2)
|
||||
@@ -1429,11 +1436,13 @@ void Client::OPMoveCoin(const EQApplicationPacket* app)
|
||||
NPC *banker = entity_list.GetClosestBanker(this, distance);
|
||||
if(!banker || distance > USE_NPC_RANGE2)
|
||||
{
|
||||
auto hacked_string =
|
||||
fmt::format("Player tried to make use of a banker(shared coin move) but {} is "
|
||||
"non-existant or too far away ({} units).",
|
||||
banker ? banker->GetName() : "UNKNOWN NPC", distance);
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName());
|
||||
auto message = fmt::format(
|
||||
"Player tried to make use of a banker (shared coin move) but banker [{}] is "
|
||||
"non-existent or too far away [{}] units",
|
||||
banker ? banker->GetName() : "UNKNOWN NPC", distance
|
||||
);
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
|
||||
return;
|
||||
}
|
||||
if(mc->cointype2 == COINTYPE_PP) // there's only platinum here
|
||||
|
||||
+39
-16
@@ -4,50 +4,51 @@
|
||||
|
||||
void CombatRecord::Start(std::string in_mob_name)
|
||||
{
|
||||
start_time = std::time(nullptr);
|
||||
end_time = 0;
|
||||
damage_received = 0;
|
||||
heal_received = 0;
|
||||
mob_name = in_mob_name;
|
||||
m_start_time = std::time(nullptr);
|
||||
m_end_time = 0;
|
||||
m_damage_received = 0;
|
||||
m_heal_received = 0;
|
||||
m_mob_name = in_mob_name;
|
||||
}
|
||||
|
||||
|
||||
void CombatRecord::Stop()
|
||||
{
|
||||
end_time = std::time(nullptr);
|
||||
m_end_time = std::time(nullptr);
|
||||
|
||||
double time_in_combat = TimeInCombat();
|
||||
|
||||
LogCombatRecord(
|
||||
"[Summary] Mob [{}] [Received] DPS [{:.0f}] Heal/s [{:.0f}] Duration [{}] ({}s)",
|
||||
mob_name,
|
||||
time_in_combat > 0 ? (damage_received / time_in_combat) : damage_received,
|
||||
time_in_combat > 0 ? (heal_received / time_in_combat) : heal_received,
|
||||
m_mob_name,
|
||||
GetDamageReceivedPerSecond(),
|
||||
GetHealedReceivedPerSecond(),
|
||||
time_in_combat > 0 ? Strings::SecondsToTime(time_in_combat) : "",
|
||||
time_in_combat
|
||||
);
|
||||
}
|
||||
|
||||
bool CombatRecord::InCombat()
|
||||
bool CombatRecord::InCombat() const
|
||||
{
|
||||
return start_time > 0;
|
||||
return m_start_time > 0;
|
||||
}
|
||||
|
||||
void CombatRecord::ProcessHPEvent(int64 hp, int64 current_hp)
|
||||
{
|
||||
// damage
|
||||
if (hp < current_hp) {
|
||||
damage_received = damage_received + std::llabs(current_hp - hp);
|
||||
m_damage_received = m_damage_received + std::llabs(current_hp - hp);
|
||||
}
|
||||
|
||||
// heal
|
||||
if (hp > current_hp && current_hp > 0) {
|
||||
heal_received = heal_received + std::llabs(current_hp - hp);
|
||||
m_heal_received = m_heal_received + std::llabs(current_hp - hp);
|
||||
}
|
||||
|
||||
LogCombatRecordDetail(
|
||||
"damage_received [{}] heal_received [{}] current_hp [{}] hp [{}] calc [{}]",
|
||||
damage_received,
|
||||
heal_received,
|
||||
m_damage_received,
|
||||
m_heal_received,
|
||||
current_hp,
|
||||
hp,
|
||||
std::llabs(current_hp - hp)
|
||||
@@ -56,5 +57,27 @@ void CombatRecord::ProcessHPEvent(int64 hp, int64 current_hp)
|
||||
|
||||
double CombatRecord::TimeInCombat() const
|
||||
{
|
||||
return difftime(end_time, start_time);
|
||||
return m_end_time > m_start_time ? difftime(m_end_time, m_start_time) : 0;
|
||||
}
|
||||
|
||||
float CombatRecord::GetDamageReceivedPerSecond() const
|
||||
{
|
||||
double time_in_combat = TimeInCombat();
|
||||
return time_in_combat > 0 ? (m_damage_received / time_in_combat) : m_damage_received;
|
||||
}
|
||||
|
||||
float CombatRecord::GetHealedReceivedPerSecond() const
|
||||
{
|
||||
double time_in_combat = TimeInCombat();
|
||||
return time_in_combat > 0 ? (m_heal_received / time_in_combat) : m_heal_received;
|
||||
}
|
||||
|
||||
int64 CombatRecord::GetDamageReceived() const
|
||||
{
|
||||
return m_damage_received;
|
||||
}
|
||||
|
||||
int64 CombatRecord::GetHealReceived() const
|
||||
{
|
||||
return m_heal_received;
|
||||
}
|
||||
|
||||
+10
-6
@@ -9,15 +9,19 @@ class CombatRecord {
|
||||
public:
|
||||
void Start(std::string in_mob_name);
|
||||
void Stop();
|
||||
bool InCombat();
|
||||
bool InCombat() const;
|
||||
void ProcessHPEvent(int64 hp, int64 current_hp);
|
||||
double TimeInCombat() const;
|
||||
int64 GetDamageReceived() const;
|
||||
int64 GetHealReceived() const;
|
||||
float GetDamageReceivedPerSecond() const;
|
||||
float GetHealedReceivedPerSecond() const;
|
||||
private:
|
||||
std::string mob_name;
|
||||
time_t start_time = 0;
|
||||
time_t end_time = 0;
|
||||
int64 damage_received = 0;
|
||||
int64 heal_received = 0;
|
||||
std::string m_mob_name;
|
||||
time_t m_start_time = 0;
|
||||
time_t m_end_time = 0;
|
||||
int64 m_damage_received = 0;
|
||||
int64 m_heal_received = 0;
|
||||
};
|
||||
|
||||
#endif //EQEMU_COMBAT_RECORD_H
|
||||
|
||||
+10
-3
@@ -36,6 +36,7 @@
|
||||
#include "fastmath.h"
|
||||
#include "mob_movement_manager.h"
|
||||
#include "npc_scale_manager.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
extern QueryServ* QServ;
|
||||
extern WorldServer worldserver;
|
||||
@@ -551,8 +552,6 @@ int command_realdispatch(Client *c, std::string message, bool ignore_status)
|
||||
{
|
||||
Seperator sep(message.c_str(), ' ', 10, 100, true); // "three word argument" should be considered 1 arg
|
||||
|
||||
command_logcommand(c, message.c_str());
|
||||
|
||||
std::string cstr(sep.arg[0] + 1);
|
||||
|
||||
if (commandlist.count(cstr) != 1) {
|
||||
@@ -593,6 +592,15 @@ int command_realdispatch(Client *c, std::string message, bool ignore_status)
|
||||
|
||||
parse->EventPlayer(EVENT_GM_COMMAND, c, message, 0);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::GM_COMMAND) && message != "#help") {
|
||||
auto e = PlayerEvent::GMCommandEvent{
|
||||
.message = message,
|
||||
.target = c->GetTarget() ? c->GetTarget()->GetName() : "NONE"
|
||||
};
|
||||
|
||||
RecordPlayerEventLogWithClient(c, PlayerEvent::GM_COMMAND, e);
|
||||
}
|
||||
|
||||
cur->function(c, &sep); // Dispatch C++ Command
|
||||
|
||||
return 0;
|
||||
@@ -1035,7 +1043,6 @@ void command_bot(Client *c, const Seperator *sep)
|
||||
#include "gm_commands/listpetition.cpp"
|
||||
#include "gm_commands/lootsim.cpp"
|
||||
#include "gm_commands/loc.cpp"
|
||||
#include "gm_commands/logcommand.cpp"
|
||||
#include "gm_commands/logs.cpp"
|
||||
#include "gm_commands/makepet.cpp"
|
||||
#include "gm_commands/mana.cpp"
|
||||
|
||||
@@ -26,7 +26,6 @@ void command_deinit(void);
|
||||
int command_add(std::string command_name, std::string description, uint8 admin, CmdFuncPtr function);
|
||||
int command_notavail(Client *c, std::string message, bool ignore_status);
|
||||
int command_realdispatch(Client *c, std::string message, bool ignore_status);
|
||||
void command_logcommand(Client *c, std::string message);
|
||||
uint8 GetCommandStatus(Client *c, std::string command_name);
|
||||
void ListModifyNPCStatMap(Client *c);
|
||||
std::map<std::string, std::string> GetModifyNPCStatMap();
|
||||
|
||||
+2
-5
@@ -848,11 +848,6 @@ public:
|
||||
// Add item from cursor slot to trade bucket (automatically does bag data too)
|
||||
void AddEntity(uint16 trade_slot_id, uint32 stack_size);
|
||||
|
||||
// Audit trade
|
||||
void LogTrade();
|
||||
|
||||
void DumpTrade();
|
||||
|
||||
|
||||
public:
|
||||
// Object state
|
||||
@@ -868,6 +863,8 @@ private:
|
||||
|
||||
uint32 with_id;
|
||||
Mob* owner;
|
||||
public:
|
||||
Mob *GetOwner() const;
|
||||
};
|
||||
|
||||
struct ExtraAttackOptions {
|
||||
|
||||
+21
-3
@@ -49,6 +49,7 @@ Child of the Mob class.
|
||||
#include "quest_parser_collection.h"
|
||||
#include "string_ids.h"
|
||||
#include "worldserver.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include <iostream>
|
||||
|
||||
|
||||
@@ -1443,6 +1444,18 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app)
|
||||
prevent_loot = true;
|
||||
}
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::LOOT_ITEM) && !IsPlayerCorpse()) {
|
||||
auto e = PlayerEvent::LootItemEvent{
|
||||
.item_id = inst->GetItem()->ID,
|
||||
.item_name = inst->GetItem()->Name,
|
||||
.charges = inst->GetCharges(),
|
||||
.npc_id = GetNPCTypeID(),
|
||||
.corpse_name = EntityList::RemoveNumbers(corpse_name)
|
||||
};
|
||||
|
||||
RecordPlayerEventLogWithClient(client, PlayerEvent::LOOT_ITEM, e);
|
||||
}
|
||||
|
||||
if (!IsPlayerCorpse())
|
||||
{
|
||||
// dynamic zones may prevent looting by non-members or based on lockouts
|
||||
@@ -1471,9 +1484,14 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app)
|
||||
// safe to ACK now
|
||||
client->QueuePacket(app);
|
||||
|
||||
if (!IsPlayerCorpse() && RuleB(Character, EnableDiscoveredItems)) {
|
||||
if (client && !client->GetGM() && !client->IsDiscovered(inst->GetItem()->ID))
|
||||
client->DiscoverItem(inst->GetItem()->ID);
|
||||
if (
|
||||
!IsPlayerCorpse() &&
|
||||
RuleB(Character, EnableDiscoveredItems) &&
|
||||
client &&
|
||||
!client->GetGM() &&
|
||||
!client->IsDiscovered(inst->GetItem()->ID)
|
||||
) {
|
||||
client->DiscoverItem(inst->GetItem()->ID);
|
||||
}
|
||||
|
||||
if (zone->adv_data) {
|
||||
|
||||
@@ -4091,6 +4091,11 @@ int8 Perl__GetRecipeSuccessCount(uint32 recipe_id, uint32 item_id)
|
||||
return content_db.GetRecipeComponentCount(RecipeCountType::Success, recipe_id, item_id);
|
||||
}
|
||||
|
||||
void Perl__send_player_handin_event()
|
||||
{
|
||||
quest_manager.SendPlayerHandinEvent();
|
||||
}
|
||||
|
||||
void perl_register_quest()
|
||||
{
|
||||
perl::interpreter perl(PERL_GET_THX);
|
||||
@@ -4632,6 +4637,7 @@ void perl_register_quest()
|
||||
package.add("scribespells", (int(*)(int, int))&Perl__scribespells);
|
||||
package.add("secondstotime", &Perl__secondstotime);
|
||||
package.add("selfcast", &Perl__selfcast);
|
||||
package.add("send_player_handin_event", &Perl__send_player_handin_event);
|
||||
package.add("setaaexpmodifierbycharid", (void(*)(uint32, uint32, double))&Perl__setaaexpmodifierbycharid);
|
||||
package.add("setaaexpmodifierbycharid", (void(*)(uint32, uint32, double, int16))&Perl__setaaexpmodifierbycharid);
|
||||
package.add("set_proximity", (void(*)(float, float, float, float))&Perl__set_proximity);
|
||||
|
||||
@@ -25,6 +25,8 @@ Eglin
|
||||
#include <perlbind/perlbind.h>
|
||||
namespace perl = perlbind;
|
||||
|
||||
#undef Null
|
||||
|
||||
#ifdef WIN32
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
+29
-2
@@ -34,6 +34,10 @@
|
||||
#include "../common/data_verification.h"
|
||||
|
||||
#include "bot.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "worldserver.h"
|
||||
|
||||
extern WorldServer worldserver;
|
||||
|
||||
extern QueryServ* QServ;
|
||||
|
||||
@@ -728,6 +732,8 @@ void Client::SetEXP(uint64 set_exp, uint64 set_aaxp, bool isrezzexp) {
|
||||
|
||||
parse->EventPlayer(EVENT_AA_GAIN, this, export_string, 0);
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::AA_GAIN, PlayerEvent::AAGainedEvent{gained});
|
||||
|
||||
/* QS: PlayerLogAARate */
|
||||
if (RuleB(QueryServ, PlayerLogAARate)){
|
||||
int add_points = (m_pp.aapoints - last_unspentAA);
|
||||
@@ -867,8 +873,19 @@ void Client::SetLevel(uint8 set_level, bool command)
|
||||
}
|
||||
|
||||
if (set_level > m_pp.level) {
|
||||
const auto export_string = fmt::format("{}", (set_level - m_pp.level));
|
||||
int levels_gained = (set_level - m_pp.level);
|
||||
const auto export_string = fmt::format("{}", levels_gained);
|
||||
parse->EventPlayer(EVENT_LEVEL_UP, this, export_string, 0);
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::LEVEL_GAIN)) {
|
||||
auto e = PlayerEvent::LevelGainedEvent{
|
||||
.from_level = m_pp.level,
|
||||
.to_level = set_level,
|
||||
.levels_gained = levels_gained
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::LEVEL_GAIN, e);
|
||||
}
|
||||
|
||||
|
||||
if (RuleB(QueryServ, PlayerLogLevels)) {
|
||||
const auto event_desc = fmt::format(
|
||||
@@ -881,8 +898,18 @@ void Client::SetLevel(uint8 set_level, bool command)
|
||||
QServ->PlayerLogEvent(Player_Log_Levels, CharacterID(), event_desc);
|
||||
}
|
||||
} else if (set_level < m_pp.level) {
|
||||
const auto export_string = fmt::format("{}", (m_pp.level - set_level));
|
||||
int levels_lost = (m_pp.level - set_level);
|
||||
const auto export_string = fmt::format("{}", levels_lost);
|
||||
parse->EventPlayer(EVENT_LEVEL_DOWN, this, export_string, 0);
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::LEVEL_LOSS)) {
|
||||
auto e = PlayerEvent::LevelLostEvent{
|
||||
.from_level = m_pp.level,
|
||||
.to_level = set_level,
|
||||
.levels_lost = levels_lost
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::LEVEL_LOSS, e);
|
||||
}
|
||||
|
||||
if (RuleB(QueryServ, PlayerLogLevels)) {
|
||||
const auto event_desc = fmt::format(
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
class Client;
|
||||
class EQApplicationPacket;
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
#include "zonedb.h"
|
||||
#include "../common/zone_store.h"
|
||||
#include "../common/repositories/criteria/content_filter_criteria.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "worldserver.h"
|
||||
|
||||
extern WorldServer worldserver;
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@@ -377,6 +381,15 @@ void Client::GoFish()
|
||||
std::vector<std::any> args;
|
||||
args.push_back(inst);
|
||||
parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::FISH_SUCCESS)) {
|
||||
auto e = PlayerEvent::FishSuccessEvent{
|
||||
.item_id = inst->GetItem()->ID,
|
||||
.item_name = inst->GetItem()->Name,
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::FISH_SUCCESS, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,6 +409,7 @@ void Client::GoFish()
|
||||
}
|
||||
|
||||
parse->EventPlayer(EVENT_FISH_FAILURE, this, "", 0);
|
||||
RecordPlayerEventLog(PlayerEvent::FISH_FAILURE, PlayerEvent::EmptyEvent{});
|
||||
}
|
||||
|
||||
//chance to break fishing pole...
|
||||
@@ -497,6 +511,14 @@ void Client::ForageItem(bool guarantee) {
|
||||
std::vector<std::any> args;
|
||||
args.push_back(inst);
|
||||
parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst->GetID(), &args);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::FORAGE_SUCCESS)) {
|
||||
auto e = PlayerEvent::ForageSuccessEvent{
|
||||
.item_id = inst->GetItem()->ID,
|
||||
.item_name = inst->GetItem()->Name
|
||||
};
|
||||
RecordPlayerEventLog(PlayerEvent::FORAGE_SUCCESS, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,6 +530,7 @@ void Client::ForageItem(bool guarantee) {
|
||||
} else {
|
||||
MessageString(Chat::Skills, FORAGE_FAILED);
|
||||
parse->EventPlayer(EVENT_FORAGE_FAILURE, this, "", 0);
|
||||
RecordPlayerEventLog(PlayerEvent::FORAGE_FAILURE, PlayerEvent::EmptyEvent{});
|
||||
}
|
||||
|
||||
CheckIncreaseSkill(EQ::skills::SkillForage, nullptr, 5);
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
#include "../client.h"
|
||||
|
||||
void command_logcommand(Client *c, std::string message)
|
||||
{
|
||||
int admin = c->Admin();
|
||||
|
||||
bool log = false;
|
||||
switch (zone->loglevelvar) { //catch failsafe
|
||||
case 9: { // log only LeadGM
|
||||
if (
|
||||
admin >= AccountStatus::GMLeadAdmin &&
|
||||
admin < AccountStatus::GMMgmt
|
||||
) {
|
||||
log = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 8: { // log only GM
|
||||
if (
|
||||
admin >= AccountStatus::GMAdmin &&
|
||||
admin < AccountStatus::GMLeadAdmin
|
||||
) {
|
||||
log = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
if (admin >= AccountStatus::GMMgmt) {
|
||||
log = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (admin >= AccountStatus::GMLeadAdmin) {
|
||||
log = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
if (admin >= AccountStatus::GMAdmin) {
|
||||
log = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
if (admin >= AccountStatus::QuestTroupe) {
|
||||
log = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
if (admin >= AccountStatus::ApprenticeGuide) {
|
||||
log = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
if (admin >= AccountStatus::Steward) {
|
||||
log = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
log = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (log) {
|
||||
database.logevents(
|
||||
c->AccountName(),
|
||||
c->AccountID(),
|
||||
admin,
|
||||
c->GetName(),
|
||||
c->GetTarget() ? c->GetTarget()->GetName() : "None",
|
||||
"Command",
|
||||
message.c_str(),
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "../common/strings.h"
|
||||
#include "worldserver.h"
|
||||
#include "string_ids.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
extern EntityList entity_list;
|
||||
extern WorldServer worldserver;
|
||||
@@ -177,6 +178,18 @@ void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinu
|
||||
true
|
||||
);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::SPLIT_MONEY)) {
|
||||
auto e = PlayerEvent::SplitMoneyEvent{
|
||||
.copper = copper_split,
|
||||
.silver = silver_split,
|
||||
.gold = gold_split,
|
||||
.platinum = platinum_split,
|
||||
.player_money_balance = members[i]->CastToClient()->GetCarriedMoney(),
|
||||
};
|
||||
|
||||
RecordPlayerEventLogWithClient(members[i]->CastToClient(), PlayerEvent::SPLIT_MONEY, e);
|
||||
}
|
||||
|
||||
members[i]->CastToClient()->MessageString(
|
||||
Chat::MoneySplit,
|
||||
YOU_RECEIVE_AS_SPLIT,
|
||||
|
||||
+97
-35
@@ -24,8 +24,10 @@
|
||||
#include "worldserver.h"
|
||||
#include "zonedb.h"
|
||||
#include "../common/zone_store.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
#include "bot.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
extern WorldServer worldserver;
|
||||
|
||||
@@ -803,17 +805,12 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2,
|
||||
safe_delete(inst);
|
||||
|
||||
// discover item and any augments
|
||||
if((RuleB(Character, EnableDiscoveredItems)) && !GetGM()) {
|
||||
if(!IsDiscovered(item_id)) {
|
||||
DiscoverItem(item_id);
|
||||
}
|
||||
/*
|
||||
// Augments should have been discovered prior to being placed on an item.
|
||||
for (int iter = AUG_BEGIN; iter < EQ::constants::ITEM_COMMON_SIZE; ++iter) {
|
||||
if(augments[iter] && !IsDiscovered(augments[iter]))
|
||||
DiscoverItem(augments[iter]);
|
||||
}
|
||||
*/
|
||||
if (
|
||||
RuleB(Character, EnableDiscoveredItems) &&
|
||||
!GetGM() &&
|
||||
!IsDiscovered(item_id)
|
||||
) {
|
||||
DiscoverItem(item_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -822,11 +819,14 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2,
|
||||
// Drop item from inventory to ground (generally only dropped from SLOT_CURSOR)
|
||||
void Client::DropItem(int16 slot_id, bool recurse)
|
||||
{
|
||||
LogInventory("[{}] (char_id: [{}]) Attempting to drop item from slot [{}] on the ground",
|
||||
GetCleanName(), CharacterID(), slot_id);
|
||||
LogInventory(
|
||||
"[{}] (char_id: [{}]) Attempting to drop item from slot [{}] on the ground",
|
||||
GetCleanName(),
|
||||
CharacterID(),
|
||||
slot_id
|
||||
);
|
||||
|
||||
if(GetInv().CheckNoDrop(slot_id, recurse) && !CanTradeFVNoDropItem())
|
||||
{
|
||||
if (GetInv().CheckNoDrop(slot_id, recurse) && !CanTradeFVNoDropItem()) {
|
||||
auto invalid_drop = m_inv.GetItem(slot_id);
|
||||
if (!invalid_drop) {
|
||||
LogInventory("Error in InventoryProfile::CheckNoDrop() - returned 'true' for empty slot");
|
||||
@@ -850,48 +850,84 @@ void Client::DropItem(int16 slot_id, bool recurse)
|
||||
}
|
||||
invalid_drop = nullptr;
|
||||
|
||||
database.SetHackerFlag(AccountName(), GetCleanName(), "Tried to drop an item on the ground that was nodrop!");
|
||||
std::string message = fmt::format(
|
||||
"Tried to drop an item on the ground that was no-drop! item_name [{}] item_id ({})",
|
||||
invalid_drop->GetItem()->Name,
|
||||
invalid_drop->GetItem()->ID
|
||||
);
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
GetInv().DeleteItem(slot_id);
|
||||
return;
|
||||
}
|
||||
|
||||
// Take control of item in client inventory
|
||||
EQ::ItemInstance *inst = m_inv.PopItem(slot_id);
|
||||
if(inst) {
|
||||
auto* inst = m_inv.PopItem(slot_id);
|
||||
if (inst) {
|
||||
if (LogSys.log_settings[Logs::Inventory].is_category_enabled) {
|
||||
LogInventory("DropItem() Processing - full item parse:");
|
||||
LogInventory("depth: 0, Item: [{}] (id: [{}]), IsDroppable: [{}]",
|
||||
(inst->GetItem() ? inst->GetItem()->Name : "null data"), inst->GetID(), (inst->IsDroppable(false) ? "true" : "false"));
|
||||
LogInventory(
|
||||
"depth: 0, Item: [{}] (id: [{}]), IsDroppable: [{}]",
|
||||
(inst->GetItem() ? inst->GetItem()->Name : "null data"),
|
||||
inst->GetID(),
|
||||
(inst->IsDroppable(false) ? "true" : "false")
|
||||
);
|
||||
|
||||
if (!inst->IsDroppable(false))
|
||||
if (!inst->IsDroppable(false)) {
|
||||
LogError("Non-droppable item being processed for drop by [{}]", GetCleanName());
|
||||
}
|
||||
|
||||
for (auto iter1 : *inst->GetContents()) { // depth 1
|
||||
LogInventory("-depth: 1, Item: [{}] (id: [{}]), IsDroppable: [{}]",
|
||||
(iter1.second->GetItem() ? iter1.second->GetItem()->Name : "null data"), iter1.second->GetID(), (iter1.second->IsDroppable(false) ? "true" : "false"));
|
||||
LogInventory(
|
||||
"-depth: 1, Item: [{}] (id: [{}]), IsDroppable: [{}]",
|
||||
(iter1.second->GetItem() ? iter1.second->GetItem()->Name : "null data"),
|
||||
iter1.second->GetID(),
|
||||
(iter1.second->IsDroppable(false) ? "true" : "false")
|
||||
);
|
||||
|
||||
if (!iter1.second->IsDroppable(false))
|
||||
if (!iter1.second->IsDroppable(false)) {
|
||||
LogError("Non-droppable item being processed for drop by [{}]", GetCleanName());
|
||||
}
|
||||
|
||||
for (auto iter2 : *iter1.second->GetContents()) { // depth 2
|
||||
LogInventory("--depth: 2, Item: [{}] (id: [{}]), IsDroppable: [{}]",
|
||||
(iter2.second->GetItem() ? iter2.second->GetItem()->Name : "null data"), iter2.second->GetID(), (iter2.second->IsDroppable(false) ? "true" : "false"));
|
||||
LogInventory(
|
||||
"--depth: 2, Item: [{}] (id: [{}]), IsDroppable: [{}]",
|
||||
(iter2.second->GetItem() ? iter2.second->GetItem()->Name : "null data"),
|
||||
iter2.second->GetID(),
|
||||
(iter2.second->IsDroppable(false) ? "true" : "false")
|
||||
);
|
||||
|
||||
if (!iter2.second->IsDroppable(false))
|
||||
if (!iter2.second->IsDroppable(false)) {
|
||||
LogError("Non-droppable item being processed for drop by [{}]", GetCleanName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
};
|
||||
RecordPlayerEventLog(PlayerEvent::DROPPED_ITEM, e);
|
||||
}
|
||||
|
||||
int i = parse->EventItem(EVENT_DROP_ITEM, this, inst, nullptr, "", slot_id);
|
||||
if(i != 0) {
|
||||
if (i != 0) {
|
||||
LogInventory("Item drop handled by [EVENT_DROP_ITEM]");
|
||||
safe_delete(inst);
|
||||
}
|
||||
} else {
|
||||
// Item doesn't exist in inventory!
|
||||
LogInventory("DropItem() - No item found in slot [{}]", slot_id);
|
||||
Message(Chat::Red, "Error: Item not found in slot %i", slot_id);
|
||||
Message(
|
||||
Chat::Red,
|
||||
fmt::format(
|
||||
"Error: Item not found in slot {}.",
|
||||
slot_id
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -904,15 +940,16 @@ void Client::DropItem(int16 slot_id, bool recurse)
|
||||
database.SaveInventory(CharacterID(), nullptr, slot_id);
|
||||
}
|
||||
|
||||
if(!inst)
|
||||
if (!inst) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Package as zone object
|
||||
auto object = new Object(this, inst);
|
||||
entity_list.AddObject(object, true);
|
||||
object->StartDecay();
|
||||
|
||||
LogInventory("Item drop handled ut assolet");
|
||||
LogInventory("[{}] dropped [{}] from slot [{}]", GetCleanName(), inst->GetItem()->Name, slot_id);
|
||||
DropItemQS(inst, false);
|
||||
|
||||
safe_delete(inst);
|
||||
@@ -1822,6 +1859,18 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
|
||||
MessageString(Chat::Loot, 290);
|
||||
parse->EventItem(EVENT_DESTROY_ITEM, this, test_inst, nullptr, "", 0);
|
||||
DeleteItemInInventory(EQ::invslot::slotCursor, 0, true);
|
||||
|
||||
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",
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::ITEM_DESTROY, e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -1835,6 +1884,16 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
|
||||
EQ::ItemInstance *inst = m_inv.GetItem(EQ::invslot::slotCursor);
|
||||
if(inst) {
|
||||
parse->EventItem(EVENT_DESTROY_ITEM, this, inst, nullptr, "", 0);
|
||||
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",
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::ITEM_DESTROY, e);
|
||||
}
|
||||
}
|
||||
|
||||
DeleteItemInInventory(move_in->from_slot);
|
||||
@@ -1867,10 +1926,13 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
|
||||
|
||||
if(!banker || distance > USE_NPC_RANGE2)
|
||||
{
|
||||
auto hacked_string = fmt::format("Player tried to make use of a banker(items) but {} is "
|
||||
"non-existant or too far away ({} units).",
|
||||
banker ? banker->GetName() : "UNKNOWN NPC", distance);
|
||||
database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName());
|
||||
auto message = fmt::format(
|
||||
"Player tried to make use of a banker (items) but banker [{}] is "
|
||||
"non-existent or too far away [{}] units",
|
||||
banker ? banker->GetName() : "UNKNOWN NPC", distance
|
||||
);
|
||||
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
|
||||
Kick("Inventory desync"); // Kicking player to avoid item loss do to client and server inventories not being sync'd
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3803,6 +3803,11 @@ int8 lua_get_recipe_success_count(uint32 recipe_id, uint32 item_id)
|
||||
return content_db.GetRecipeComponentCount(RecipeCountType::Success, recipe_id, item_id);
|
||||
}
|
||||
|
||||
void lua_send_player_handin_event()
|
||||
{
|
||||
quest_manager.SendPlayerHandinEvent();
|
||||
}
|
||||
|
||||
#define LuaCreateNPCParse(name, c_type, default_value) do { \
|
||||
cur = table[#name]; \
|
||||
if(luabind::type(cur) != LUA_TNIL) { \
|
||||
@@ -4331,6 +4336,7 @@ luabind::scope lua_register_general() {
|
||||
luabind::def("get_recipe_fail_count", (int8(*)(uint32,uint32))&lua_get_recipe_fail_count),
|
||||
luabind::def("get_recipe_salvage_count", (int8(*)(uint32,uint32))&lua_get_recipe_salvage_count),
|
||||
luabind::def("get_recipe_success_count", (int8(*)(uint32,uint32))&lua_get_recipe_success_count),
|
||||
luabind::def("send_player_handin_event", (void(*)(void))&lua_send_player_handin_event),
|
||||
/*
|
||||
Cross Zone
|
||||
*/
|
||||
|
||||
@@ -59,7 +59,7 @@ void handle_npc_event_trade(
|
||||
uint32 extra_data,
|
||||
std::vector<std::any> *extra_pointers
|
||||
) {
|
||||
Lua_Client l_client(reinterpret_cast<Client*>(init));
|
||||
Lua_Client l_client(reinterpret_cast<Client *>(init));
|
||||
luabind::adl::object l_client_o = luabind::adl::object(L, l_client);
|
||||
l_client_o.push(L);
|
||||
lua_setfield(L, -2, "other");
|
||||
@@ -105,6 +105,10 @@ void handle_npc_event_trade(
|
||||
lua_pushinteger(L, money_value);
|
||||
lua_setfield(L, -2, "copper");
|
||||
|
||||
// set a reference to the client inside of the trade object as well for plugins to process
|
||||
l_client_o.push(L);
|
||||
lua_setfield(L, -2, "other");
|
||||
|
||||
lua_setfield(L, -2, "trade");
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@ extern volatile bool is_zone_loaded;
|
||||
#include "zone_event_scheduler.h"
|
||||
#include "../common/file.h"
|
||||
#include "../common/path_manager.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
EntityList entity_list;
|
||||
WorldServer worldserver;
|
||||
@@ -107,6 +108,7 @@ EQEmuLogSys LogSys;
|
||||
ZoneEventScheduler event_scheduler;
|
||||
WorldContentService content_service;
|
||||
PathManager path;
|
||||
PlayerEventLogs player_event_logs;
|
||||
|
||||
const SPDat_Spell_Struct* spells;
|
||||
int32 SPDAT_RECORDS = -1;
|
||||
@@ -266,6 +268,8 @@ int main(int argc, char** argv) {
|
||||
->SetGMSayHandler(&Zone::GMSayHookCallBackProcess)
|
||||
->StartFileLogs();
|
||||
|
||||
player_event_logs.SetDatabase(&database)->Init();
|
||||
|
||||
/* Guilds */
|
||||
guild_mgr.SetDatabase(&database);
|
||||
GuildBanks = nullptr;
|
||||
|
||||
+3
-1
@@ -1741,7 +1741,9 @@ protected:
|
||||
bool spawned_in_water;
|
||||
bool is_boat;
|
||||
|
||||
CombatRecord combat_record{};
|
||||
CombatRecord m_combat_record{};
|
||||
public:
|
||||
const CombatRecord &GetCombatRecord() const;
|
||||
|
||||
public:
|
||||
bool GetWasSpawnedInWater() const;
|
||||
|
||||
+2
-2
@@ -1929,7 +1929,7 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help)
|
||||
}
|
||||
|
||||
std::string mob_name = GetCleanName();
|
||||
combat_record.Start(mob_name);
|
||||
m_combat_record.Start(mob_name);
|
||||
CastToNPC()->SetCombatEvent(true);
|
||||
}
|
||||
}
|
||||
@@ -1970,7 +1970,7 @@ void Mob::AI_Event_NoLongerEngaged() {
|
||||
CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emoteid);
|
||||
}
|
||||
|
||||
combat_record.Stop();
|
||||
m_combat_record.Stop();
|
||||
CastToNPC()->SetCombatEvent(false);
|
||||
}
|
||||
}
|
||||
|
||||
+18
-6
@@ -25,9 +25,11 @@
|
||||
#include "object.h"
|
||||
|
||||
#include "quest_parser_collection.h"
|
||||
#include "worldserver.h"
|
||||
#include "zonedb.h"
|
||||
#include "../common/zone_store.h"
|
||||
#include "../common/repositories/criteria/content_filter_criteria.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@@ -37,6 +39,7 @@ const char DEFAULT_OBJECT_NAME_SUFFIX[] = "_ACTORDEF";
|
||||
|
||||
extern Zone* zone;
|
||||
extern EntityList entity_list;
|
||||
extern WorldServer worldserver;
|
||||
|
||||
// Loading object from database
|
||||
Object::Object(uint32 id, uint32 type, uint32 icon, const Object_Struct& object, const EQ::ItemInstance* inst)
|
||||
@@ -517,6 +520,14 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object)
|
||||
}
|
||||
}
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::GROUNDSPAWN_PICKUP)) {
|
||||
auto e = PlayerEvent::GroundSpawnPickupEvent{
|
||||
.item_id = item->ID,
|
||||
.item_name = item->Name,
|
||||
};
|
||||
RecordPlayerEventLogWithClient(sender, PlayerEvent::GROUNDSPAWN_PICKUP, e);
|
||||
}
|
||||
|
||||
std::string export_string = fmt::format("{}", item->ID);
|
||||
std::vector<std::any> args;
|
||||
args.push_back(m_inst);
|
||||
@@ -542,12 +553,13 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object)
|
||||
sender->SendItemPacket(EQ::invslot::slotCursor, m_inst, ItemPacketTrade);
|
||||
|
||||
// Could be an undiscovered ground_spawn
|
||||
if (m_ground_spawn && (RuleB(Character, EnableDiscoveredItems)))
|
||||
{
|
||||
if (!sender->GetGM() && !sender->IsDiscovered(item->ID))
|
||||
{
|
||||
sender->DiscoverItem(item->ID);
|
||||
}
|
||||
if (
|
||||
m_ground_spawn &&
|
||||
RuleB(Character, EnableDiscoveredItems) &&
|
||||
!sender->GetGM() &&
|
||||
!sender->IsDiscovered(item->ID)
|
||||
) {
|
||||
sender->DiscoverItem(item->ID);
|
||||
}
|
||||
|
||||
if(cursordelete) // delete the item if it's a duplicate lore. We have to do this because the client expects the item packet
|
||||
|
||||
+170
-1
@@ -23,6 +23,7 @@
|
||||
#include "../common/spdat.h"
|
||||
#include "../common/strings.h"
|
||||
#include "../common/say_link.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
#include "entity.h"
|
||||
#include "event_codes.h"
|
||||
@@ -2431,6 +2432,7 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level
|
||||
);
|
||||
|
||||
parse->EventPlayer(EVENT_BOT_CREATE, initiator, export_string, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2940,7 +2942,6 @@ std::string QuestManager::varlink(
|
||||
|
||||
return linker.GenerateLink();
|
||||
}
|
||||
|
||||
std::string QuestManager::getitemname(uint32 item_id) {
|
||||
const EQ::ItemData* item_data = database.GetItem(item_id);
|
||||
if (!item_data) {
|
||||
@@ -3986,3 +3987,171 @@ int8 QuestManager::DoesAugmentFit(EQ::ItemInstance* inst, uint32 augment_id, uin
|
||||
|
||||
return inst->AvailableAugmentSlot(aug_inst->AugType);
|
||||
}
|
||||
|
||||
void QuestManager::SendPlayerHandinEvent() {
|
||||
QuestManagerCurrentQuestVars();
|
||||
if (!owner || !owner->IsNPC() || !initiator) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!initiator->EntityVariableExists("HANDIN_ITEMS") &&
|
||||
!initiator->EntityVariableExists("HANDIN_MONEY") &&
|
||||
!initiator->EntityVariableExists("RETURN_ITEMS") &&
|
||||
!initiator->EntityVariableExists("RETURN_MONEY")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto handin_items = initiator->GetEntityVariable("HANDIN_ITEMS");
|
||||
auto return_items = initiator->GetEntityVariable("RETURN_ITEMS");
|
||||
auto handin_money = initiator->GetEntityVariable("HANDIN_MONEY");
|
||||
auto return_money = initiator->GetEntityVariable("RETURN_MONEY");
|
||||
|
||||
std::vector<PlayerEvent::HandinEntry> hi = {};
|
||||
std::vector<PlayerEvent::HandinEntry> ri = {};
|
||||
PlayerEvent::HandinMoney hm{};
|
||||
PlayerEvent::HandinMoney rm{};
|
||||
|
||||
// Handin Items
|
||||
if (!handin_items.empty()) {
|
||||
if (Strings::Contains(handin_items, ",")) {
|
||||
const auto handin_data = Strings::Split(handin_items, ",");
|
||||
|
||||
for (const auto &h: handin_data) {
|
||||
const auto item_data = Strings::Split(h, "-");
|
||||
|
||||
if (
|
||||
item_data.size() == 3 &&
|
||||
Strings::IsNumber(item_data[0]) &&
|
||||
Strings::IsNumber(item_data[1]) &&
|
||||
Strings::IsNumber(item_data[2])
|
||||
) {
|
||||
const auto item_id = static_cast<uint32>(std::stoul(item_data[0]));
|
||||
if (item_id != 0) {
|
||||
const auto *item = database.GetItem(item_id);
|
||||
|
||||
hi.emplace_back(
|
||||
PlayerEvent::HandinEntry{
|
||||
.item_id = item_id,
|
||||
.item_name = item->Name,
|
||||
.charges = static_cast<uint16>(std::stoul(item_data[1])),
|
||||
.attuned = std::stoi(item_data[2]) ? true : false
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Strings::Contains(handin_items, "|")) {
|
||||
const auto item_data = Strings::Split(handin_items, "|");
|
||||
|
||||
if (
|
||||
item_data.size() == 3 &&
|
||||
Strings::IsNumber(item_data[0]) &&
|
||||
Strings::IsNumber(item_data[1]) &&
|
||||
Strings::IsNumber(item_data[2])
|
||||
) {
|
||||
const auto item_id = static_cast<uint32>(std::stoul(item_data[0]));
|
||||
const auto *item = database.GetItem(item_id);
|
||||
|
||||
hi.emplace_back(
|
||||
PlayerEvent::HandinEntry{
|
||||
.item_id = item_id,
|
||||
.item_name = item->Name,
|
||||
.charges = static_cast<uint16>(std::stoul(item_data[1])),
|
||||
.attuned = std::stoi(item_data[2]) ? true : false
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handin Money
|
||||
if (!handin_money.empty()) {
|
||||
const auto hms = Strings::Split(handin_money, "|");
|
||||
hm.copper = static_cast<uint32>(std::stoul(hms[0]));
|
||||
hm.silver = static_cast<uint32>(std::stoul(hms[1]));
|
||||
hm.gold = static_cast<uint32>(std::stoul(hms[2]));
|
||||
hm.platinum = static_cast<uint32>(std::stoul(hms[3]));
|
||||
}
|
||||
|
||||
// Return Items
|
||||
if (!return_items.empty()) {
|
||||
if (Strings::Contains(return_items, ",")) {
|
||||
const auto return_data = Strings::Split(return_items, ",");
|
||||
|
||||
for (const auto &r: return_data) {
|
||||
const auto item_data = Strings::Split(r, "|");
|
||||
|
||||
if (
|
||||
item_data.size() == 3 &&
|
||||
Strings::IsNumber(item_data[0]) &&
|
||||
Strings::IsNumber(item_data[1]) &&
|
||||
Strings::IsNumber(item_data[2])
|
||||
) {
|
||||
const auto item_id = static_cast<uint32>(std::stoul(item_data[0]));
|
||||
const auto *item = database.GetItem(item_id);
|
||||
|
||||
ri.emplace_back(
|
||||
PlayerEvent::HandinEntry{
|
||||
.item_id = item_id,
|
||||
.item_name = item->Name,
|
||||
.charges = static_cast<uint16>(std::stoul(item_data[1])),
|
||||
.attuned = std::stoi(item_data[2]) ? true : false
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Strings::Contains(return_items, "|")) {
|
||||
const auto item_data = Strings::Split(return_items, "|");
|
||||
|
||||
if (
|
||||
item_data.size() == 3 &&
|
||||
Strings::IsNumber(item_data[0]) &&
|
||||
Strings::IsNumber(item_data[1]) &&
|
||||
Strings::IsNumber(item_data[2])
|
||||
) {
|
||||
const auto item_id = static_cast<uint32>(std::stoul(item_data[0]));
|
||||
const auto *item = database.GetItem(item_id);
|
||||
|
||||
ri.emplace_back(
|
||||
PlayerEvent::HandinEntry{
|
||||
.item_id = item_id,
|
||||
.item_name = item->Name,
|
||||
.charges = static_cast<uint16>(std::stoul(item_data[1])),
|
||||
.attuned = std::stoi(item_data[2]) ? true : false
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return Money
|
||||
if (!return_money.empty()) {
|
||||
const auto rms = Strings::Split(return_money, "|");
|
||||
rm.copper = static_cast<uint32>(std::stoul(rms[0]));
|
||||
rm.silver = static_cast<uint32>(std::stoul(rms[1]));
|
||||
rm.gold = static_cast<uint32>(std::stoul(rms[2]));
|
||||
rm.platinum = static_cast<uint32>(std::stoul(rms[3]));
|
||||
}
|
||||
|
||||
initiator->DeleteEntityVariable("HANDIN_ITEMS");
|
||||
initiator->DeleteEntityVariable("HANDIN_MONEY");
|
||||
initiator->DeleteEntityVariable("RETURN_ITEMS");
|
||||
initiator->DeleteEntityVariable("RETURN_MONEY");
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::NPC_HANDIN)) {
|
||||
auto e = PlayerEvent::HandinEvent{
|
||||
.npc_id = owner->CastToNPC()->GetNPCTypeID(),
|
||||
.npc_name = owner->GetCleanName(),
|
||||
.handin_items = hi,
|
||||
.handin_money = hm,
|
||||
.return_items = ri,
|
||||
.return_money = rm
|
||||
};
|
||||
|
||||
RecordPlayerEventLogWithClient(initiator, PlayerEvent::NPC_HANDIN, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,7 @@ public:
|
||||
bool HasRecipeLearned(uint32 recipe_id);
|
||||
bool DoAugmentSlotsMatch(uint32 item_one, uint32 item_two);
|
||||
int8 DoesAugmentFit(EQ::ItemInstance* inst, uint32 augment_id, uint8 augment_slot = 255);
|
||||
void SendPlayerHandinEvent();
|
||||
|
||||
Bot *GetBot() const;
|
||||
Client *GetInitiator() const;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
#include "../common/strings.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "entity.h"
|
||||
@@ -814,6 +815,18 @@ void Raid::SplitMoney(uint32 gid, uint32 copper, uint32 silver, uint32 gold, uin
|
||||
true
|
||||
);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::SPLIT_MONEY)) {
|
||||
auto e = PlayerEvent::SplitMoneyEvent{
|
||||
.copper = copper_split,
|
||||
.silver = silver_split,
|
||||
.gold = gold_split,
|
||||
.platinum = platinum_split,
|
||||
.player_money_balance = members[i].member->GetCarriedMoney(),
|
||||
};
|
||||
|
||||
RecordPlayerEventLogWithClient(members[i].member, PlayerEvent::SPLIT_MONEY, e);
|
||||
}
|
||||
|
||||
members[i].member->MessageString(
|
||||
Chat::MoneySplit,
|
||||
YOU_RECEIVE_AS_SPLIT,
|
||||
|
||||
+31
-15
@@ -77,6 +77,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
|
||||
#include "../common/strings.h"
|
||||
#include "../common/data_verification.h"
|
||||
#include "../common/misc_functions.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
#include "data_bucket.h"
|
||||
#include "quest_parser_collection.h"
|
||||
@@ -6914,10 +6915,13 @@ bool Mob::CheckItemRaceClassDietyRestrictionsOnCast(uint32 inventory_slot) {
|
||||
if (itm && itm->GetItem()->Classes != 65535) {
|
||||
if ((itm->GetItem()->Click.Type == EQ::item::ItemEffectEquipClick) && !(itm->GetItem()->Classes & bitmask)) {
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are casting a spell from an item that requires equipping but shouldn't let them equip it
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) which they shouldn't be able to equip!",
|
||||
CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item with an invalid class");
|
||||
std::string message = fmt::format(
|
||||
"Attempted to click an equip-only effect on item_name [{}] item_id [{}] which they shouldn't be able to equip!",
|
||||
itm->GetItem()->Name,
|
||||
itm->GetItem()->ID
|
||||
);
|
||||
|
||||
RecordPlayerEventLogWithClient(CastToClient(), PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
}
|
||||
else {
|
||||
MessageString(Chat::Red, MUST_EQUIP_ITEM);
|
||||
@@ -6926,10 +6930,13 @@ bool Mob::CheckItemRaceClassDietyRestrictionsOnCast(uint32 inventory_slot) {
|
||||
}
|
||||
if ((itm->GetItem()->Click.Type == EQ::item::ItemEffectClick2) && !(itm->GetItem()->Classes & bitmask)) {
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are casting a spell from an item that they don't meet the race/class requirements to cast
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click a race/class restricted effect on item [{}] (id: [{}]) which they shouldn't be able to click!",
|
||||
CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking race/class restricted item with an invalid class");
|
||||
std::string message = fmt::format(
|
||||
"Attempted to click a race/class restricted effect on item_name [{}] item_id [{}] which they shouldn't be able to click!",
|
||||
itm->GetItem()->Name,
|
||||
itm->GetItem()->ID
|
||||
);
|
||||
|
||||
RecordPlayerEventLogWithClient(CastToClient(), PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
}
|
||||
else {
|
||||
if (CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::RoF)
|
||||
@@ -6947,9 +6954,13 @@ bool Mob::CheckItemRaceClassDietyRestrictionsOnCast(uint32 inventory_slot) {
|
||||
}
|
||||
if (itm && (itm->GetItem()->Click.Type == EQ::item::ItemEffectEquipClick) && inventory_slot > EQ::invslot::EQUIPMENT_END) {
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are attempting to cast a must equip clicky without having it equipped
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) without equiping it!", CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item without equiping it");
|
||||
std::string message = fmt::format(
|
||||
"Attempted to click an equip-only effect on item_name [{}] item_id [{}] without equipping it!",
|
||||
itm->GetItem()->Name,
|
||||
itm->GetItem()->ID
|
||||
);
|
||||
|
||||
RecordPlayerEventLogWithClient(CastToClient(), PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
|
||||
}
|
||||
else {
|
||||
MessageString(Chat::Red, MUST_EQUIP_ITEM);
|
||||
@@ -6975,8 +6986,8 @@ void Mob::SetHP(int64 hp)
|
||||
return;
|
||||
}
|
||||
|
||||
if (combat_record.InCombat()) {
|
||||
combat_record.ProcessHPEvent(hp, current_hp);
|
||||
if (m_combat_record.InCombat()) {
|
||||
m_combat_record.ProcessHPEvent(hp, current_hp);
|
||||
}
|
||||
|
||||
current_hp = hp;
|
||||
@@ -6984,8 +6995,8 @@ void Mob::SetHP(int64 hp)
|
||||
|
||||
void Mob::DrawDebugCoordinateNode(std::string node_name, const glm::vec4 vec)
|
||||
{
|
||||
NPC* node = nullptr;
|
||||
for (const auto& n : entity_list.GetNPCList()) {
|
||||
NPC *node = nullptr;
|
||||
for (const auto &n: entity_list.GetNPCList()) {
|
||||
if (n.second->GetCleanName() == node_name) {
|
||||
node = n.second;
|
||||
break;
|
||||
@@ -6995,3 +7006,8 @@ void Mob::DrawDebugCoordinateNode(std::string node_name, const glm::vec4 vec)
|
||||
node = NPC::SpawnNodeNPC(node_name, "", GetPosition());
|
||||
}
|
||||
}
|
||||
|
||||
const CombatRecord &Mob::GetCombatRecord() const
|
||||
{
|
||||
return m_combat_record;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "worldserver.h"
|
||||
#include "dynamic_zone.h"
|
||||
#include "string_ids.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
#define EBON_CRYSTAL 40902
|
||||
#define RADIANT_CRYSTAL 40903
|
||||
@@ -925,6 +926,16 @@ int ClientTaskState::IncrementDoneCount(
|
||||
|
||||
int event_res = DispatchEventTaskComplete(client, *info, activity_id);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::TASK_COMPLETE)) {
|
||||
auto e = PlayerEvent::TaskCompleteEvent{
|
||||
.task_id = static_cast<uint32>(info->task_id),
|
||||
.task_name = task_manager->GetTaskName(static_cast<uint32>(info->task_id)),
|
||||
.activity_id = static_cast<uint32>(info->activity[activity_id].activity_id),
|
||||
.done_count = static_cast<uint32>(info->activity[activity_id].done_count)
|
||||
};
|
||||
RecordPlayerEventLogWithClient(client, PlayerEvent::TASK_COMPLETE, e);
|
||||
}
|
||||
|
||||
/* QS: PlayerLogTaskUpdates :: Complete */
|
||||
if (RuleB(QueryServ, PlayerLogTaskUpdates)) {
|
||||
std::string event_desc = StringFormat(
|
||||
@@ -963,6 +974,16 @@ int ClientTaskState::IncrementDoneCount(
|
||||
activity_id,
|
||||
task_index
|
||||
);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::TASK_UPDATE)) {
|
||||
auto e = PlayerEvent::TaskUpdateEvent{
|
||||
.task_id = static_cast<uint32>(info->task_id),
|
||||
.task_name = task_manager->GetTaskName(static_cast<uint32>(info->task_id)),
|
||||
.activity_id = static_cast<uint32>(info->activity[activity_id].activity_id),
|
||||
.done_count = static_cast<uint32>(info->activity[activity_id].done_count)
|
||||
};
|
||||
RecordPlayerEventLogWithClient(client, PlayerEvent::TASK_UPDATE, e);
|
||||
}
|
||||
}
|
||||
|
||||
task_manager->SaveClientState(client, this);
|
||||
@@ -2134,6 +2155,26 @@ void ClientTaskState::AcceptNewTask(
|
||||
NPC *npc = entity_list.GetID(npc_type_id)->CastToNPC();
|
||||
if (npc) {
|
||||
parse->EventNPC(EVENT_TASK_ACCEPTED, npc, client, export_string, 0);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::TASK_ACCEPT)) {
|
||||
auto e = PlayerEvent::TaskAcceptEvent{
|
||||
.npc_id = static_cast<uint32>(npc_type_id),
|
||||
.npc_name = npc->GetCleanName(),
|
||||
.task_id = static_cast<uint32>(task_id),
|
||||
.task_name = task_manager->GetTaskName(static_cast<uint32>(task_id)),
|
||||
};
|
||||
RecordPlayerEventLogWithClient(client, PlayerEvent::TASK_ACCEPT, e);
|
||||
}
|
||||
} else {
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::TASK_ACCEPT)) {
|
||||
auto e = PlayerEvent::TaskAcceptEvent{
|
||||
.npc_id = 0,
|
||||
.npc_name = "No NPC",
|
||||
.task_id = static_cast<uint32>(task_id),
|
||||
.task_name = task_manager->GetTaskName(static_cast<uint32>(task_id)),
|
||||
};
|
||||
RecordPlayerEventLogWithClient(client, PlayerEvent::TASK_ACCEPT, e);
|
||||
}
|
||||
}
|
||||
parse->EventPlayer(EVENT_TASK_ACCEPTED, client, export_string, 0);
|
||||
}
|
||||
|
||||
@@ -700,7 +700,7 @@ void TaskManager::SharedTaskSelector(Client* client, Mob* mob, const std::vector
|
||||
if (request.group_type != SharedTaskRequestGroupType::Solo) {
|
||||
auto shared_task_members = SharedTaskMembersRepository::GetWhere(
|
||||
database,
|
||||
fmt::format("character_id IN ({}) LIMIT 1", fmt::join(request.character_ids, ",")));
|
||||
fmt::format("character_id IN ({}) LIMIT 1", Strings::Join(request.character_ids, ",")));
|
||||
|
||||
if (!shared_task_members.empty()) {
|
||||
validation_failed = true;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
#include "../common/global_define.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <list>
|
||||
@@ -33,12 +34,14 @@
|
||||
#include "string_ids.h"
|
||||
#include "titles.h"
|
||||
#include "zonedb.h"
|
||||
#include "worldserver.h"
|
||||
#include "../common/repositories/char_recipe_list_repository.h"
|
||||
#include "../common/zone_store.h"
|
||||
#include "../common/repositories/tradeskill_recipe_repository.h"
|
||||
#include "../common/repositories/tradeskill_recipe_entries_repository.h"
|
||||
|
||||
extern QueryServ* QServ;
|
||||
extern WorldServer worldserver;
|
||||
|
||||
static const EQ::skills::SkillType TradeskillUnknown = EQ::skills::Skill1HBlunt; /* an arbitrary non-tradeskill */
|
||||
|
||||
@@ -488,8 +491,28 @@ void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Ob
|
||||
}
|
||||
|
||||
if (success) {
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::COMBINE_SUCCESS)) {
|
||||
auto e = PlayerEvent::CombineEvent{
|
||||
.recipe_id = spec.recipe_id,
|
||||
.recipe_name = spec.name,
|
||||
.made_count = spec.madecount,
|
||||
.tradeskill_id = (uint32)spec.tradeskill
|
||||
};
|
||||
RecordPlayerEventLogWithClient(user, PlayerEvent::COMBINE_SUCCESS, e);
|
||||
}
|
||||
|
||||
parse->EventPlayer(EVENT_COMBINE_SUCCESS, user, spec.name, spec.recipe_id);
|
||||
} else {
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::COMBINE_FAILURE)) {
|
||||
auto e = PlayerEvent::CombineEvent{
|
||||
.recipe_id = spec.recipe_id,
|
||||
.recipe_name = spec.name,
|
||||
.made_count = spec.madecount,
|
||||
.tradeskill_id = (uint32)spec.tradeskill
|
||||
};
|
||||
RecordPlayerEventLogWithClient(user, PlayerEvent::COMBINE_FAILURE, e);
|
||||
}
|
||||
|
||||
parse->EventPlayer(EVENT_COMBINE_FAILURE, user, spec.name, spec.recipe_id);
|
||||
}
|
||||
}
|
||||
|
||||
+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);
|
||||
|
||||
@@ -57,6 +57,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "shared_task_zone_messaging.h"
|
||||
#include "dialogue_window.h"
|
||||
#include "bot_command.h"
|
||||
#include "queryserv.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
extern EntityList entity_list;
|
||||
extern Zone* zone;
|
||||
@@ -1958,8 +1960,9 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
}
|
||||
case ServerOP_ReloadLogs:
|
||||
{
|
||||
zone->SendReloadMessage("Log Settings");
|
||||
LogSys.LoadLogDatabaseSettings();
|
||||
zone->SendReloadMessage("Log Settings");
|
||||
LogSys.LoadLogDatabaseSettings();
|
||||
player_event_logs.ReloadSettings();
|
||||
break;
|
||||
}
|
||||
case ServerOP_ReloadMerchants: {
|
||||
|
||||
@@ -170,34 +170,6 @@ void ZoneDatabase::UpdateSpawn2Status(uint32 id, uint8 new_status)
|
||||
QueryDatabase(query);
|
||||
}
|
||||
|
||||
bool ZoneDatabase::logevents(const char* accountname,uint32 accountid,uint8 status,const char* charname, const char* target,const char* descriptiontype, const char* description,int event_nid){
|
||||
|
||||
uint32 len = strlen(description);
|
||||
uint32 len2 = strlen(target);
|
||||
auto descriptiontext = new char[2 * len + 1];
|
||||
auto targetarr = new char[2 * len2 + 1];
|
||||
memset(descriptiontext, 0, 2*len+1);
|
||||
memset(targetarr, 0, 2*len2+1);
|
||||
DoEscapeString(descriptiontext, description, len);
|
||||
DoEscapeString(targetarr, target, len2);
|
||||
|
||||
std::string query = StringFormat("INSERT INTO eventlog (accountname, accountid, status, "
|
||||
"charname, target, descriptiontype, description, event_nid) "
|
||||
"VALUES('%s', %i, %i, '%s', '%s', '%s', '%s', '%i')",
|
||||
accountname, accountid, status, charname, targetarr,
|
||||
descriptiontype, descriptiontext, event_nid);
|
||||
safe_delete_array(descriptiontext);
|
||||
safe_delete_array(targetarr);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ZoneDatabase::SetSpecialAttkFlag(uint8 id, const char* flag) {
|
||||
|
||||
std::string query = StringFormat("UPDATE npc_types SET npcspecialattks='%s' WHERE id = %i;", flag, id);
|
||||
|
||||
@@ -612,7 +612,6 @@ public:
|
||||
* PLEASE DO NOT ADD TO THIS COLLECTION OF CRAP UNLESS YOUR METHOD
|
||||
* REALLY HAS NO BETTER SECTION
|
||||
*/
|
||||
bool logevents(const char* accountname,uint32 accountid,uint8 status,const char* charname,const char* target, const char* descriptiontype, const char* description,int event_nid);
|
||||
uint32 GetKarma(uint32 acct_id);
|
||||
void UpdateKarma(uint32 acct_id, uint32 amount);
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ extern Zone* zone;
|
||||
|
||||
#include "../common/repositories/character_peqzone_flags_repository.h"
|
||||
#include "../common/repositories/zone_repository.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
|
||||
void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
|
||||
@@ -216,6 +217,22 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::ZONING)) {
|
||||
auto e = PlayerEvent::ZoningEvent{};
|
||||
e.from_zone_long_name = zone->GetLongName();
|
||||
e.from_zone_short_name = zone->GetShortName();
|
||||
e.from_zone_id = zone->GetZoneID();
|
||||
e.from_instance_id = zone->GetInstanceID();
|
||||
e.from_instance_version = zone->GetInstanceVersion();
|
||||
e.to_zone_long_name = ZoneLongName(target_zone_id);
|
||||
e.to_zone_short_name = ZoneName(target_zone_id);
|
||||
e.to_zone_id = target_zone_id;
|
||||
e.to_instance_id = target_instance_id;
|
||||
e.to_instance_version = target_instance_version;
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::ZONING, e);
|
||||
}
|
||||
|
||||
//handle circumvention of zone restrictions
|
||||
//we need the value when creating the outgoing packet as well.
|
||||
uint8 ignore_restrictions = zonesummon_ignorerestrictions;
|
||||
|
||||
Reference in New Issue
Block a user