mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +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:
parent
1cc32d92cf
commit
d9f545a5ec
@ -33,9 +33,11 @@ SET(common_sources
|
||||
eq_stream_proxy.cpp
|
||||
eqtime.cpp
|
||||
event_sub.cpp
|
||||
events/player_event_logs.cpp
|
||||
events/player_event_discord_formatter.cpp
|
||||
expedition_lockout_timer.cpp
|
||||
extprofile.cpp
|
||||
discord_manager.cpp
|
||||
discord/discord_manager.cpp
|
||||
faction.cpp
|
||||
file.cpp
|
||||
guild_base.cpp
|
||||
@ -198,7 +200,6 @@ SET(repositories
|
||||
repositories/base/base_dynamic_zones_repository.h
|
||||
repositories/base/base_dynamic_zone_members_repository.h
|
||||
repositories/base/base_dynamic_zone_templates_repository.h
|
||||
repositories/base/base_eventlog_repository.h
|
||||
repositories/base/base_expeditions_repository.h
|
||||
repositories/base/base_expedition_lockouts_repository.h
|
||||
repositories/base/base_faction_association_repository.h
|
||||
@ -218,7 +219,6 @@ SET(repositories
|
||||
repositories/base/base_guilds_repository.h
|
||||
repositories/base/base_guild_ranks_repository.h
|
||||
repositories/base/base_guild_relations_repository.h
|
||||
repositories/base/base_hackers_repository.h
|
||||
repositories/base/base_horses_repository.h
|
||||
repositories/base/base_instance_list_repository.h
|
||||
repositories/base/base_instance_list_player_repository.h
|
||||
@ -264,6 +264,8 @@ SET(repositories
|
||||
repositories/base/base_pets_equipmentset_repository.h
|
||||
repositories/base/base_pets_equipmentset_entries_repository.h
|
||||
repositories/base/base_player_titlesets_repository.h
|
||||
repositories/base/base_player_event_log_settings_repository.h
|
||||
repositories/base/base_player_event_logs_repository.h
|
||||
repositories/base/base_quest_globals_repository.h
|
||||
repositories/base/base_raid_details_repository.h
|
||||
repositories/base/base_raid_members_repository.h
|
||||
@ -376,7 +378,6 @@ SET(repositories
|
||||
repositories/dynamic_zones_repository.h
|
||||
repositories/dynamic_zone_members_repository.h
|
||||
repositories/dynamic_zone_templates_repository.h
|
||||
repositories/eventlog_repository.h
|
||||
repositories/expeditions_repository.h
|
||||
repositories/expedition_lockouts_repository.h
|
||||
repositories/faction_association_repository.h
|
||||
@ -396,7 +397,6 @@ SET(repositories
|
||||
repositories/guilds_repository.h
|
||||
repositories/guild_ranks_repository.h
|
||||
repositories/guild_relations_repository.h
|
||||
repositories/hackers_repository.h
|
||||
repositories/horses_repository.h
|
||||
repositories/instance_list_repository.h
|
||||
repositories/instance_list_player_repository.h
|
||||
@ -442,6 +442,8 @@ SET(repositories
|
||||
repositories/pets_equipmentset_repository.h
|
||||
repositories/pets_equipmentset_entries_repository.h
|
||||
repositories/player_titlesets_repository.h
|
||||
repositories/player_event_log_settings_repository.h
|
||||
repositories/player_event_logs_repository.h
|
||||
repositories/quest_globals_repository.h
|
||||
repositories/raid_details_repository.h
|
||||
repositories/raid_members_repository.h
|
||||
@ -507,7 +509,7 @@ SET(common_headers
|
||||
dbcore.h
|
||||
deity.h
|
||||
discord/discord.h
|
||||
discord_manager.h
|
||||
discord/discord_manager.h
|
||||
dynamic_zone_base.h
|
||||
emu_constants.h
|
||||
emu_limits.h
|
||||
@ -530,6 +532,9 @@ SET(common_headers
|
||||
eq_stream_locator.h
|
||||
eq_stream_proxy.h
|
||||
eqtime.h
|
||||
events/player_event_logs.h
|
||||
events/player_event_discord_formatter.h
|
||||
events/player_events.h
|
||||
errmsg.h
|
||||
event_sub.h
|
||||
expedition_lockout_timer.h
|
||||
@ -608,6 +613,7 @@ SET(common_headers
|
||||
event/event_loop.h
|
||||
event/task.h
|
||||
event/timer.h
|
||||
json/json_archive_single_line.h
|
||||
json/json.h
|
||||
json/json-forwards.h
|
||||
net/console_server.h
|
||||
@ -661,8 +667,7 @@ SET(common_headers
|
||||
StackWalker/StackWalker.h
|
||||
util/memory_stream.h
|
||||
util/directory.h
|
||||
util/uuid.h
|
||||
)
|
||||
util/uuid.h)
|
||||
|
||||
SOURCE_GROUP(Event FILES
|
||||
event/event_loop.h
|
||||
|
||||
@ -1281,44 +1281,6 @@ bool Database::MoveCharacterToZone(const char *charname, uint32 zone_id)
|
||||
return results.RowsAffected() != 0;
|
||||
}
|
||||
|
||||
bool Database::SetHackerFlag(const char* accountname, const char* charactername, const char* hacked) {
|
||||
std::string query = StringFormat("INSERT INTO `hackers` (account, name, hacked) values('%s','%s','%s')", accountname, charactername, hacked);
|
||||
auto results = QueryDatabase(query);
|
||||
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return results.RowsAffected() != 0;
|
||||
}
|
||||
|
||||
bool Database::SetMQDetectionFlag(const char* accountname, const char* charactername, const char* hacked, const char* zone) {
|
||||
//Utilize the "hacker" table, but also give zone information.
|
||||
std::string query = StringFormat("INSERT INTO hackers(account,name,hacked,zone) values('%s','%s','%s','%s')", accountname, charactername, hacked, zone);
|
||||
auto results = QueryDatabase(query);
|
||||
|
||||
if (!results.Success())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return results.RowsAffected() != 0;
|
||||
}
|
||||
|
||||
bool Database::SetMQDetectionFlag(const char* accountname, const char* charactername, const std::string &hacked, const char* zone) {
|
||||
//Utilize the "hacker" table, but also give zone information.
|
||||
auto query = fmt::format("INSERT INTO hackers(account, name, hacked, zone) values('{}', '{}', '{}', '{}')",
|
||||
accountname, charactername, hacked, zone);
|
||||
auto results = QueryDatabase(query);
|
||||
|
||||
if (!results.Success())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return results.RowsAffected() != 0;
|
||||
}
|
||||
|
||||
uint8 Database::GetRaceSkill(uint8 skillid, uint8 in_race)
|
||||
{
|
||||
uint16 race_cap = 0;
|
||||
|
||||
@ -108,9 +108,6 @@ public:
|
||||
bool MoveCharacterToZone(uint32 character_id, uint32 zone_id);
|
||||
bool ReserveName(uint32 account_id, char *name);
|
||||
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct *pp);
|
||||
bool SetHackerFlag(const char *accountname, const char *charactername, const char *hacked);
|
||||
bool SetMQDetectionFlag(const char *accountname, const char *charactername, const char *hacked, const char *zone);
|
||||
bool SetMQDetectionFlag(const char *accountname, const char *charactername, const std::string &hacked, const char *zone);
|
||||
bool UpdateName(const char *oldname, const char *newname);
|
||||
bool CopyCharacter(
|
||||
const std::string& source_character_name,
|
||||
|
||||
@ -321,13 +321,11 @@ namespace DatabaseSchema {
|
||||
"discord_webhooks",
|
||||
"dynamic_zone_members",
|
||||
"dynamic_zones",
|
||||
"eventlog",
|
||||
"expedition_lockouts",
|
||||
"expeditions",
|
||||
"gm_ips",
|
||||
"group_id",
|
||||
"group_leaders",
|
||||
"hackers",
|
||||
"instance_list",
|
||||
"ip_exemptions",
|
||||
"item_tick",
|
||||
@ -343,6 +341,8 @@ namespace DatabaseSchema {
|
||||
"respawn_times",
|
||||
"saylink",
|
||||
"server_scheduled_events",
|
||||
"player_event_log_settings",
|
||||
"player_event_logs"
|
||||
"shared_task_activity_state",
|
||||
"shared_task_dynamic_zones",
|
||||
"shared_task_members",
|
||||
|
||||
@ -1,22 +1,17 @@
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <cereal/archives/binary.hpp>
|
||||
#include "discord.h"
|
||||
#include "../http/httplib.h"
|
||||
#include "../json/json.h"
|
||||
#include "../strings.h"
|
||||
#include "../eqemu_logsys.h"
|
||||
#include "../events/player_event_logs.h"
|
||||
|
||||
constexpr int MAX_RETRIES = 10;
|
||||
|
||||
void Discord::SendWebhookMessage(const std::string &message, const std::string &webhook_url)
|
||||
{
|
||||
// validate
|
||||
if (webhook_url.empty()) {
|
||||
LogDiscord("[webhook_url] is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
// validate
|
||||
if (webhook_url.find("http://") == std::string::npos && webhook_url.find("https://") == std::string::npos) {
|
||||
LogDiscord("[webhook_url] [{}] does not contain a valid http/s prefix.", webhook_url);
|
||||
if (!ValidateWebhookUrl(webhook_url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -28,7 +23,7 @@ void Discord::SendWebhookMessage(const std::string &message, const std::string &
|
||||
std::string endpoint = Strings::Replace(webhook_url, base_url, "");
|
||||
|
||||
// client
|
||||
httplib::Client cli(base_url.c_str());
|
||||
httplib::Client cli(base_url);
|
||||
cli.set_connection_timeout(0, 15000000); // 15 sec
|
||||
cli.set_read_timeout(15, 0); // 15 seconds
|
||||
cli.set_write_timeout(15, 0); // 15 seconds
|
||||
@ -46,9 +41,9 @@ void Discord::SendWebhookMessage(const std::string &message, const std::string &
|
||||
int retries = 0;
|
||||
int retry_timer = 1000;
|
||||
while (retry) {
|
||||
if (auto res = cli.Post(endpoint.c_str(), payload.str(), "application/json")) {
|
||||
if (auto res = cli.Post(endpoint, payload.str(), "application/json")) {
|
||||
if (res->status != 200 && res->status != 204) {
|
||||
LogError("Code [{}] Error [{}]", res->status, res->body);
|
||||
LogError("[Discord Client] Code [{}] Error [{}]", res->status, res->body);
|
||||
}
|
||||
if (res->status == 429) {
|
||||
if (!res->body.empty()) {
|
||||
@ -81,6 +76,74 @@ void Discord::SendWebhookMessage(const std::string &message, const std::string &
|
||||
}
|
||||
}
|
||||
|
||||
void Discord::SendPlayerEventMessage(
|
||||
const PlayerEvent::PlayerEventContainer &e,
|
||||
const std::string &webhook_url
|
||||
)
|
||||
{
|
||||
if (!ValidateWebhookUrl(webhook_url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto s = Strings::Split(webhook_url, '/');
|
||||
|
||||
// url
|
||||
std::string base_url = fmt::format("{}//{}", s[0], s[2]);
|
||||
std::string endpoint = Strings::Replace(webhook_url, base_url, "");
|
||||
|
||||
// client
|
||||
httplib::Client cli(base_url);
|
||||
cli.set_connection_timeout(0, 15000000); // 15 sec
|
||||
cli.set_read_timeout(15, 0); // 15 seconds
|
||||
cli.set_write_timeout(15, 0); // 15 seconds
|
||||
httplib::Headers headers = {
|
||||
{"Content-Type", "application/json"}
|
||||
};
|
||||
|
||||
std::string payload = PlayerEventLogs::GetDiscordPayloadFromEvent(e);
|
||||
if (payload.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool retry = true;
|
||||
int retries = 0;
|
||||
int retry_timer = 1000;
|
||||
while (retry) {
|
||||
if (auto res = cli.Post(endpoint, payload, "application/json")) {
|
||||
if (res->status != 200 && res->status != 204) {
|
||||
LogError("Code [{}] Error [{}]", res->status, res->body);
|
||||
}
|
||||
if (res->status == 429) {
|
||||
if (!res->body.empty()) {
|
||||
std::stringstream ss(res->body);
|
||||
Json::Value response;
|
||||
|
||||
try {
|
||||
ss >> response;
|
||||
}
|
||||
catch (std::exception const &ex) {
|
||||
LogDiscord("JSON serialization failure [{}] via [{}]", ex.what(), res->body);
|
||||
}
|
||||
|
||||
retry_timer = std::stoi(response["retry_after"].asString()) + 500;
|
||||
}
|
||||
|
||||
LogDiscord("Rate limited... retrying message in [{}ms]", retry_timer);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(retry_timer + 500));
|
||||
}
|
||||
if (res->status == 204) {
|
||||
retry = false;
|
||||
}
|
||||
if (retries > MAX_RETRIES) {
|
||||
LogDiscord("Retries exceeded for player event message");
|
||||
retry = false;
|
||||
}
|
||||
|
||||
retries++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string Discord::FormatDiscordMessage(uint16 category_id, const std::string &message)
|
||||
{
|
||||
if (category_id == Logs::LogCategory::MySQLQuery) {
|
||||
@ -89,3 +152,20 @@ std::string Discord::FormatDiscordMessage(uint16 category_id, const std::string
|
||||
|
||||
return message + "\n";
|
||||
}
|
||||
|
||||
bool Discord::ValidateWebhookUrl(const std::string &webhook_url)
|
||||
{
|
||||
// validate
|
||||
if (webhook_url.empty()) {
|
||||
LogDiscord("[webhook_url] is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate
|
||||
if (!Strings::Contains(webhook_url, "http://") && !Strings::Contains(webhook_url, "https://")) {
|
||||
LogDiscord("[webhook_url] [{}] does not contain a valid http/s prefix.", webhook_url);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4,11 +4,16 @@
|
||||
|
||||
#include <string>
|
||||
#include "../types.h"
|
||||
#include "../http/httplib.h"
|
||||
#include "../repositories/player_event_logs_repository.h"
|
||||
#include "../events/player_events.h"
|
||||
|
||||
class Discord {
|
||||
public:
|
||||
static void SendWebhookMessage(const std::string& message, const std::string& webhook_url);
|
||||
static std::string FormatDiscordMessage(uint16 category_id, const std::string& message);
|
||||
static void SendPlayerEventMessage(const PlayerEvent::PlayerEventContainer& e, const std::string &webhook_url);
|
||||
static bool ValidateWebhookUrl(const std::string &webhook_url);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "discord_manager.h"
|
||||
#include "../common/discord/discord.h"
|
||||
#include "../common/eqemu_logsys.h"
|
||||
#include "../common/strings.h"
|
||||
#include "../../common/discord/discord.h"
|
||||
#include "../events/player_event_logs.h"
|
||||
|
||||
void DiscordManager::QueueWebhookMessage(uint32 webhook_id, const std::string &message)
|
||||
{
|
||||
@ -55,7 +54,6 @@ void DiscordManager::ProcessMessageQueue()
|
||||
message = "";
|
||||
}
|
||||
}
|
||||
|
||||
// final flush
|
||||
if (!message.empty()) {
|
||||
Discord::SendWebhookMessage(
|
||||
@ -67,3 +65,11 @@ void DiscordManager::ProcessMessageQueue()
|
||||
webhook_message_queue.clear();
|
||||
webhook_queue_lock.unlock();
|
||||
}
|
||||
|
||||
void DiscordManager::QueuePlayerEventMessage(const PlayerEvent::PlayerEventContainer& e)
|
||||
{
|
||||
auto w = player_event_logs.GetDiscordWebhookUrlFromEventType(e.player_event_log.event_type_id);
|
||||
if (!w.empty()) {
|
||||
Discord::SendPlayerEventMessage(e, w);
|
||||
}
|
||||
}
|
||||
@ -4,12 +4,15 @@
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "../common/types.h"
|
||||
#include "../../common/types.h"
|
||||
#include "../repositories/player_event_logs_repository.h"
|
||||
#include "../events/player_events.h"
|
||||
|
||||
class DiscordManager {
|
||||
public:
|
||||
void QueueWebhookMessage(uint32 webhook_id, const std::string& message);
|
||||
void ProcessMessageQueue();
|
||||
void QueuePlayerEventMessage(const PlayerEvent::PlayerEventContainer& e);
|
||||
private:
|
||||
std::mutex webhook_queue_lock{};
|
||||
std::map<uint32, std::vector<std::string>> webhook_message_queue{};
|
||||
@ -1017,14 +1017,13 @@ enum Anonymity : uint8
|
||||
Roleplaying
|
||||
};
|
||||
|
||||
enum ZoningMessage : int8
|
||||
{
|
||||
ZoneNoMessage = 0,
|
||||
ZoneSuccess = 1,
|
||||
ZoneNotReady = -1,
|
||||
ZoneValidPC = -2,
|
||||
ZoneStoryZone = -3,
|
||||
ZoneNoExpansion = -6,
|
||||
enum ZoningMessage : int8 {
|
||||
ZoneNoMessage = 0,
|
||||
ZoneSuccess = 1,
|
||||
ZoneNotReady = -1,
|
||||
ZoneValidPC = -2,
|
||||
ZoneStoryZone = -3,
|
||||
ZoneNoExpansion = -6,
|
||||
ZoneNoExperience = -7
|
||||
};
|
||||
|
||||
@ -1040,4 +1039,10 @@ enum class RecipeCountType : uint8
|
||||
#define ALT_CURRENCY_ID_RADIANT 4
|
||||
#define ALT_CURRENCY_ID_EBON 5
|
||||
|
||||
enum ResurrectionActions
|
||||
{
|
||||
Decline,
|
||||
Accept
|
||||
};
|
||||
|
||||
#endif /*COMMON_EQ_CONSTANTS_H*/
|
||||
|
||||
1343
common/events/player_event_discord_formatter.cpp
Normal file
1343
common/events/player_event_discord_formatter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
214
common/events/player_event_discord_formatter.h
Normal file
214
common/events/player_event_discord_formatter.h
Normal file
@ -0,0 +1,214 @@
|
||||
#ifndef EQEMU_PLAYER_EVENT_DISCORD_FORMATTER_H
|
||||
#define EQEMU_PLAYER_EVENT_DISCORD_FORMATTER_H
|
||||
|
||||
#include <string>
|
||||
#include "player_events.h"
|
||||
#include "../repositories/base/base_player_event_logs_repository.h"
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <cereal/types/vector.hpp>
|
||||
|
||||
struct DiscordField {
|
||||
std::string name;
|
||||
std::string value;
|
||||
bool is_inline;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(name),
|
||||
CEREAL_NVP(value),
|
||||
cereal::make_nvp("inline", is_inline)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct DiscordAuthor {
|
||||
std::string name;
|
||||
std::string icon_url;
|
||||
std::string url;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(name),
|
||||
CEREAL_NVP(icon_url),
|
||||
CEREAL_NVP(url)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct DiscordEmbed {
|
||||
std::vector<DiscordField> fields;
|
||||
std::string title;
|
||||
std::string description;
|
||||
std::string timestamp;
|
||||
DiscordAuthor author;
|
||||
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(fields),
|
||||
CEREAL_NVP(title),
|
||||
CEREAL_NVP(description),
|
||||
CEREAL_NVP(timestamp),
|
||||
CEREAL_NVP(author)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct DiscordWebhook {
|
||||
std::vector<DiscordEmbed> embeds;
|
||||
std::string content;
|
||||
std::string avatar_url;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(embeds),
|
||||
CEREAL_NVP(avatar_url),
|
||||
CEREAL_NVP(content)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class PlayerEventDiscordFormatter {
|
||||
public:
|
||||
static std::string GetCurrentTimestamp();
|
||||
static std::string FormatEventSay(const PlayerEvent::PlayerEventContainer &c, const PlayerEvent::SayEvent &e);
|
||||
static std::string
|
||||
FormatGMCommand(const PlayerEvent::PlayerEventContainer &c, const PlayerEvent::GMCommandEvent &e);
|
||||
static void BuildDiscordField(
|
||||
std::vector<DiscordField> *f,
|
||||
const std::string &name,
|
||||
const std::string &value,
|
||||
bool is_inline = true
|
||||
);
|
||||
static void BuildBaseEmbed(
|
||||
std::vector<DiscordEmbed> *e,
|
||||
const std::vector<DiscordField> &f,
|
||||
PlayerEvent::PlayerEventContainer c
|
||||
);
|
||||
static std::string FormatWithNodata(const PlayerEvent::PlayerEventContainer &c);
|
||||
|
||||
static std::string FormatAAGainedEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::AAGainedEvent &e
|
||||
);
|
||||
static std::string FormatAAPurchasedEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::AAPurchasedEvent &e
|
||||
);
|
||||
static std::string FormatDeathEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::DeathEvent &e
|
||||
);
|
||||
static std::string FormatFishSuccessEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::FishSuccessEvent &e
|
||||
);
|
||||
static std::string FormatForageSuccessEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::ForageSuccessEvent &e
|
||||
);
|
||||
static std::string FormatDestroyItemEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::DestroyItemEvent &e
|
||||
);
|
||||
static std::string FormatDiscoverItemEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::DiscoverItemEvent &e
|
||||
);
|
||||
static std::string FormatDroppedItemEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::DroppedItemEvent &e
|
||||
);
|
||||
static std::string FormatLevelGainedEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::LevelGainedEvent &e
|
||||
);
|
||||
static std::string FormatLevelLostEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::LevelLostEvent &e
|
||||
);
|
||||
static std::string FormatLootItemEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::LootItemEvent &e
|
||||
);
|
||||
static std::string FormatGroundSpawnPickupEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::GroundSpawnPickupEvent &e
|
||||
);
|
||||
static std::string FormatMerchantPurchaseEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::MerchantPurchaseEvent &e
|
||||
);
|
||||
static std::string FormatMerchantSellEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::MerchantSellEvent &e
|
||||
);
|
||||
static std::string FormatNPCHandinEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::HandinEvent &e
|
||||
);
|
||||
static std::string FormatSkillUpEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::SkillUpEvent &e
|
||||
);
|
||||
static std::string FormatTaskAcceptEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::TaskAcceptEvent &e
|
||||
);
|
||||
static std::string FormatTaskCompleteEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::TaskCompleteEvent &e
|
||||
);
|
||||
static std::string FormatTaskUpdateEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::TaskUpdateEvent &e
|
||||
);
|
||||
static std::string FormatTradeEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::TradeEvent &e
|
||||
);
|
||||
static std::string FormatTraderPurchaseEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::TraderPurchaseEvent &e
|
||||
);
|
||||
static std::string FormatTraderSellEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::TraderSellEvent &e
|
||||
);
|
||||
static std::string FormatResurrectAcceptEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::ResurrectAcceptEvent &e
|
||||
);
|
||||
static std::string FormatSplitMoneyEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::SplitMoneyEvent &e
|
||||
);
|
||||
static std::string FormatCombineEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::CombineEvent &e
|
||||
);
|
||||
static std::string FormatZoningEvent(
|
||||
const PlayerEvent::PlayerEventContainer &c,
|
||||
const PlayerEvent::ZoningEvent &e
|
||||
);
|
||||
static DiscordWebhook BuildDiscordWebhook(
|
||||
const PlayerEvent::PlayerEventContainer &p,
|
||||
std::vector<DiscordEmbed> &embeds
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
#endif //EQEMU_PLAYER_EVENT_DISCORD_FORMATTER_H
|
||||
703
common/events/player_event_logs.cpp
Normal file
703
common/events/player_event_logs.cpp
Normal file
@ -0,0 +1,703 @@
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include "player_event_logs.h"
|
||||
#include "player_event_discord_formatter.h"
|
||||
#include "../platform.h"
|
||||
#include "../rulesys.h"
|
||||
|
||||
const uint32 PROCESS_RETENTION_TRUNCATION_TIMER_INTERVAL = 60 * 60 * 1000; // 1 hour
|
||||
|
||||
// general initialization routine
|
||||
void PlayerEventLogs::Init()
|
||||
{
|
||||
m_process_batch_events_timer.SetTimer(RuleI(Logging, BatchPlayerEventProcessIntervalSeconds) * 1000);
|
||||
m_process_retention_truncation_timer.SetTimer(PROCESS_RETENTION_TRUNCATION_TIMER_INTERVAL);
|
||||
|
||||
ValidateDatabaseConnection();
|
||||
|
||||
// initialize settings array
|
||||
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
|
||||
m_settings[i].id = i;
|
||||
m_settings[i].event_name = PlayerEvent::EventName[i];
|
||||
m_settings[i].event_enabled = 1;
|
||||
m_settings[i].retention_days = 0;
|
||||
m_settings[i].discord_webhook_id = 0;
|
||||
}
|
||||
|
||||
SetSettingsDefaults();
|
||||
|
||||
// initialize settings from database
|
||||
auto s = PlayerEventLogSettingsRepository::All(*m_database);
|
||||
std::vector<int> db{};
|
||||
db.reserve(s.size());
|
||||
for (auto &e: s) {
|
||||
m_settings[e.id] = e;
|
||||
db.emplace_back(e.id);
|
||||
}
|
||||
|
||||
// insert entries that don't exist in database
|
||||
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
|
||||
bool is_in_database = std::find(db.begin(), db.end(), i) != db.end();
|
||||
bool is_deprecated = Strings::Contains(PlayerEvent::EventName[i], "Deprecated");
|
||||
bool is_implemented = !Strings::Contains(PlayerEvent::EventName[i], "Unimplemented");
|
||||
|
||||
// remove when deprecated
|
||||
if (is_deprecated && is_in_database) {
|
||||
LogInfo("[Deprecated] Removing PlayerEvent [{}] ({})", PlayerEvent::EventName[i], i);
|
||||
PlayerEventLogSettingsRepository::DeleteWhere(*m_database, fmt::format("id = {}", i));
|
||||
}
|
||||
// remove when unimplemented if present
|
||||
if (!is_implemented && is_in_database) {
|
||||
LogInfo("[Unimplemented] Removing PlayerEvent [{}] ({})", PlayerEvent::EventName[i], i);
|
||||
PlayerEventLogSettingsRepository::DeleteWhere(*m_database, fmt::format("id = {}", i));
|
||||
}
|
||||
|
||||
bool is_missing_in_database = std::find(db.begin(), db.end(), i) == db.end();
|
||||
if (is_missing_in_database && is_implemented && !is_deprecated) {
|
||||
LogInfo(
|
||||
"[New] PlayerEvent [{}] ({})",
|
||||
PlayerEvent::EventName[i],
|
||||
i
|
||||
);
|
||||
|
||||
auto c = PlayerEventLogSettingsRepository::NewEntity();
|
||||
c.id = i;
|
||||
c.event_name = PlayerEvent::EventName[i];
|
||||
c.event_enabled = m_settings[i].event_enabled;
|
||||
c.retention_days = m_settings[i].retention_days;
|
||||
PlayerEventLogSettingsRepository::InsertOne(*m_database, c);
|
||||
}
|
||||
}
|
||||
|
||||
bool processing_in_world = !RuleB(Logging, PlayerEventsQSProcess) && IsWorld();
|
||||
bool processing_in_qs = RuleB(Logging, PlayerEventsQSProcess) && IsQueryServ();
|
||||
|
||||
// on initial boot process truncation
|
||||
if (processing_in_world || processing_in_qs) {
|
||||
ProcessRetentionTruncation();
|
||||
}
|
||||
}
|
||||
|
||||
// set the database object, during initialization
|
||||
PlayerEventLogs *PlayerEventLogs::SetDatabase(Database *db)
|
||||
{
|
||||
m_database = db;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// validates whether the connection is valid or not, used in initialization
|
||||
bool PlayerEventLogs::ValidateDatabaseConnection()
|
||||
{
|
||||
if (!m_database) {
|
||||
LogError("No database connection");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// determines if the passed in event is enabled or not
|
||||
// this is used to gate logic or events from firing off
|
||||
// this is used prior to building the events, we don't want to
|
||||
// build the events, send them through the stack in a function call
|
||||
// only to discard them immediately afterwards, very wasteful on resources
|
||||
// the quest api currently does this
|
||||
bool PlayerEventLogs::IsEventEnabled(PlayerEvent::EventType event)
|
||||
{
|
||||
return m_settings[event].event_enabled ? m_settings[event].event_enabled : false;
|
||||
}
|
||||
|
||||
// this processes any current player events on the queue
|
||||
void PlayerEventLogs::ProcessBatchQueue()
|
||||
{
|
||||
if (m_record_batch_queue.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BenchTimer benchmark;
|
||||
|
||||
// flush many
|
||||
PlayerEventLogsRepository::InsertMany(*m_database, m_record_batch_queue);
|
||||
LogInfo(
|
||||
"Processing batch player event log queue of [{}] took [{}]",
|
||||
m_record_batch_queue.size(),
|
||||
benchmark.elapsed()
|
||||
);
|
||||
|
||||
// empty
|
||||
m_batch_queue_lock.lock();
|
||||
m_record_batch_queue = {};
|
||||
m_batch_queue_lock.unlock();
|
||||
}
|
||||
|
||||
// adds a player event to the queue
|
||||
void PlayerEventLogs::AddToQueue(const PlayerEventLogsRepository::PlayerEventLogs &log)
|
||||
{
|
||||
m_batch_queue_lock.lock();
|
||||
m_record_batch_queue.emplace_back(log);
|
||||
m_batch_queue_lock.unlock();
|
||||
}
|
||||
|
||||
// fills common event data in the SendEvent function
|
||||
void PlayerEventLogs::FillPlayerEvent(
|
||||
const PlayerEvent::PlayerEvent &p,
|
||||
PlayerEventLogsRepository::PlayerEventLogs &n
|
||||
)
|
||||
{
|
||||
n.account_id = p.account_id;
|
||||
n.character_id = p.character_id;
|
||||
n.zone_id = p.zone_id;
|
||||
n.instance_id = p.instance_id;
|
||||
n.x = p.x;
|
||||
n.y = p.y;
|
||||
n.z = p.z;
|
||||
n.heading = p.heading;
|
||||
}
|
||||
|
||||
// builds the dynamic packet used to ship the player event over the wire
|
||||
// supports serializing the struct so it can be rebuilt on the other end
|
||||
std::unique_ptr<ServerPacket>
|
||||
PlayerEventLogs::BuildPlayerEventPacket(const PlayerEvent::PlayerEventContainer &e)
|
||||
{
|
||||
EQ::Net::DynamicPacket dyn_pack;
|
||||
dyn_pack.PutSerialize(0, e);
|
||||
auto pack_size = sizeof(ServerSendPlayerEvent_Struct) + dyn_pack.Length();
|
||||
auto pack = std::make_unique<ServerPacket>(ServerOP_PlayerEvent, static_cast<uint32_t>(pack_size));
|
||||
auto buf = reinterpret_cast<ServerSendPlayerEvent_Struct *>(pack->pBuffer);
|
||||
buf->cereal_size = static_cast<uint32_t>(dyn_pack.Length());
|
||||
memcpy(buf->cereal_data, dyn_pack.Data(), dyn_pack.Length());
|
||||
|
||||
return pack;
|
||||
}
|
||||
|
||||
const PlayerEventLogSettingsRepository::PlayerEventLogSettings *PlayerEventLogs::GetSettings() const
|
||||
{
|
||||
return m_settings;
|
||||
}
|
||||
|
||||
bool PlayerEventLogs::IsEventDiscordEnabled(int32_t event_type_id)
|
||||
{
|
||||
// out of bounds check
|
||||
if (event_type_id >= PlayerEvent::EventType::MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure webhook id is set
|
||||
if (m_settings[event_type_id].discord_webhook_id == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ensure there is a matching webhook to begin with
|
||||
if (!LogSys.GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string PlayerEventLogs::GetDiscordWebhookUrlFromEventType(int32_t event_type_id)
|
||||
{
|
||||
// out of bounds check
|
||||
if (event_type_id >= PlayerEvent::EventType::MAX) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// make sure webhook id is set
|
||||
if (m_settings[event_type_id].discord_webhook_id == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// ensure there is a matching webhook to begin with
|
||||
if (!LogSys.GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url.empty()) {
|
||||
return LogSys.GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
// GM_COMMAND | [x] Implemented Formatter
|
||||
// ZONING | [x] Implemented Formatter
|
||||
// AA_GAIN | [x] Implemented Formatter
|
||||
// AA_PURCHASE | [x] Implemented Formatter
|
||||
// FORAGE_SUCCESS | [x] Implemented Formatter
|
||||
// FORAGE_FAILURE | [x] Implemented Formatter
|
||||
// FISH_SUCCESS | [x] Implemented Formatter
|
||||
// FISH_FAILURE | [x] Implemented Formatter
|
||||
// ITEM_DESTROY | [x] Implemented Formatter
|
||||
// WENT_ONLINE | [x] Implemented Formatter
|
||||
// WENT_OFFLINE | [x] Implemented Formatter
|
||||
// LEVEL_GAIN | [x] Implemented Formatter
|
||||
// LEVEL_LOSS | [x] Implemented Formatter
|
||||
// LOOT_ITEM | [x] Implemented Formatter
|
||||
// MERCHANT_PURCHASE | [x] Implemented Formatter
|
||||
// MERCHANT_SELL | [x] Implemented Formatter
|
||||
// GROUP_JOIN | [] Implemented Formatter
|
||||
// GROUP_LEAVE | [] Implemented Formatter
|
||||
// RAID_JOIN | [] Implemented Formatter
|
||||
// RAID_LEAVE | [] Implemented Formatter
|
||||
// GROUNDSPAWN_PICKUP | [x] Implemented Formatter
|
||||
// NPC_HANDIN | [x] Implemented Formatter
|
||||
// SKILL_UP | [x] Implemented Formatter
|
||||
// TASK_ACCEPT | [x] Implemented Formatter
|
||||
// TASK_UPDATE | [x] Implemented Formatter
|
||||
// TASK_COMPLETE | [x] Implemented Formatter
|
||||
// TRADE | [] Implemented Formatter
|
||||
// GIVE_ITEM | [] Implemented Formatter
|
||||
// SAY | [x] Implemented Formatter
|
||||
// REZ_ACCEPTED | [x] Implemented Formatter
|
||||
// DEATH | [x] Implemented Formatter
|
||||
// COMBINE_FAILURE | [x] Implemented Formatter
|
||||
// COMBINE_SUCCESS | [x] Implemented Formatter
|
||||
// DROPPED_ITEM | [x] Implemented Formatter
|
||||
// SPLIT_MONEY | [x] Implemented Formatter
|
||||
// DZ_JOIN | [] Implemented Formatter
|
||||
// DZ_LEAVE | [] Implemented Formatter
|
||||
// TRADER_PURCHASE | [x] Implemented Formatter
|
||||
// TRADER_SELL | [x] Implemented Formatter
|
||||
// BANDOLIER_CREATE | [] Implemented Formatter
|
||||
// BANDOLIER_SWAP | [] Implemented Formatter
|
||||
// DISCOVER_ITEM | [X] Implemented Formatter
|
||||
|
||||
std::string PlayerEventLogs::GetDiscordPayloadFromEvent(const PlayerEvent::PlayerEventContainer &e)
|
||||
{
|
||||
std::string payload;
|
||||
switch (e.player_event_log.event_type_id) {
|
||||
case PlayerEvent::AA_GAIN: {
|
||||
PlayerEvent::AAGainedEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatAAGainedEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::AA_PURCHASE: {
|
||||
PlayerEvent::AAPurchasedEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatAAPurchasedEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::COMBINE_FAILURE:
|
||||
case PlayerEvent::COMBINE_SUCCESS: {
|
||||
PlayerEvent::CombineEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatCombineEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::DEATH: {
|
||||
PlayerEvent::DeathEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatDeathEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::DISCOVER_ITEM: {
|
||||
PlayerEvent::DiscoverItemEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatDiscoverItemEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::DROPPED_ITEM: {
|
||||
PlayerEvent::DroppedItemEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatDroppedItemEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::FISH_FAILURE: {
|
||||
payload = PlayerEventDiscordFormatter::FormatWithNodata(e);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::FISH_SUCCESS: {
|
||||
PlayerEvent::FishSuccessEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatFishSuccessEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::FORAGE_FAILURE: {
|
||||
payload = PlayerEventDiscordFormatter::FormatWithNodata(e);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::FORAGE_SUCCESS: {
|
||||
PlayerEvent::ForageSuccessEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatForageSuccessEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::ITEM_DESTROY: {
|
||||
PlayerEvent::DestroyItemEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatDestroyItemEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::LEVEL_GAIN: {
|
||||
PlayerEvent::LevelGainedEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatLevelGainedEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::LEVEL_LOSS: {
|
||||
PlayerEvent::LevelLostEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatLevelLostEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::LOOT_ITEM: {
|
||||
PlayerEvent::LootItemEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatLootItemEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::GROUNDSPAWN_PICKUP: {
|
||||
PlayerEvent::GroundSpawnPickupEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatGroundSpawnPickupEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::NPC_HANDIN: {
|
||||
PlayerEvent::HandinEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatNPCHandinEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::SAY: {
|
||||
PlayerEvent::SayEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatEventSay(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::GM_COMMAND: {
|
||||
PlayerEvent::GMCommandEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatGMCommand(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::SKILL_UP: {
|
||||
PlayerEvent::SkillUpEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatSkillUpEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::SPLIT_MONEY: {
|
||||
PlayerEvent::SplitMoneyEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatSplitMoneyEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::TASK_ACCEPT: {
|
||||
PlayerEvent::TaskAcceptEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatTaskAcceptEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::TASK_COMPLETE: {
|
||||
PlayerEvent::TaskCompleteEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatTaskCompleteEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::TASK_UPDATE: {
|
||||
PlayerEvent::TaskUpdateEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatTaskUpdateEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::TRADE: {
|
||||
PlayerEvent::TradeEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatTradeEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::TRADER_PURCHASE: {
|
||||
PlayerEvent::TraderPurchaseEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatTraderPurchaseEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::TRADER_SELL: {
|
||||
PlayerEvent::TraderSellEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatTraderSellEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::REZ_ACCEPTED: {
|
||||
PlayerEvent::ResurrectAcceptEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
payload = PlayerEventDiscordFormatter::FormatResurrectAcceptEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::WENT_ONLINE:
|
||||
case PlayerEvent::WENT_OFFLINE: {
|
||||
payload = PlayerEventDiscordFormatter::FormatWithNodata(e);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::MERCHANT_PURCHASE: {
|
||||
PlayerEvent::MerchantPurchaseEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
|
||||
payload = PlayerEventDiscordFormatter::FormatMerchantPurchaseEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::MERCHANT_SELL: {
|
||||
PlayerEvent::MerchantSellEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
|
||||
payload = PlayerEventDiscordFormatter::FormatMerchantSellEvent(e, n);
|
||||
break;
|
||||
}
|
||||
case PlayerEvent::ZONING: {
|
||||
PlayerEvent::ZoningEvent n{};
|
||||
std::stringstream ss;
|
||||
{
|
||||
ss << e.player_event_log.event_data;
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
n.serialize(ar);
|
||||
}
|
||||
|
||||
payload = PlayerEventDiscordFormatter::FormatZoningEvent(e, n);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogInfo(
|
||||
"Player event [{}] ({}) Discord formatter not implemented",
|
||||
e.player_event_log.event_type_name,
|
||||
e.player_event_log.event_type_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
// general process function, used in world or UCS depending on rule Logging:PlayerEventsQSProcess
|
||||
void PlayerEventLogs::Process()
|
||||
{
|
||||
if (m_process_batch_events_timer.Check()) {
|
||||
ProcessBatchQueue();
|
||||
}
|
||||
|
||||
if (m_process_retention_truncation_timer.Check()) {
|
||||
ProcessRetentionTruncation();
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerEventLogs::ProcessRetentionTruncation()
|
||||
{
|
||||
LogInfo("Running truncation");
|
||||
|
||||
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
|
||||
if (m_settings[i].retention_days > 0) {
|
||||
int deleted_count = PlayerEventLogsRepository::DeleteWhere(
|
||||
*m_database,
|
||||
fmt::format(
|
||||
"event_type_id = {} AND created_at < (NOW() - INTERVAL {} DAY)",
|
||||
i,
|
||||
m_settings[i].retention_days
|
||||
)
|
||||
);
|
||||
|
||||
if (deleted_count > 0) {
|
||||
LogInfo(
|
||||
"Truncated [{}] events of type [{}] ({}) older than [{}] days",
|
||||
deleted_count,
|
||||
PlayerEvent::EventName[i],
|
||||
i,
|
||||
m_settings[i].retention_days
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerEventLogs::ReloadSettings()
|
||||
{
|
||||
for (auto &e: PlayerEventLogSettingsRepository::All(*m_database)) {
|
||||
m_settings[e.id] = e;
|
||||
}
|
||||
}
|
||||
|
||||
const int32_t RETENTION_DAYS_DEFAULT = 7;
|
||||
|
||||
void PlayerEventLogs::SetSettingsDefaults()
|
||||
{
|
||||
m_settings[PlayerEvent::GM_COMMAND].event_enabled = 1;
|
||||
m_settings[PlayerEvent::ZONING].event_enabled = 1;
|
||||
m_settings[PlayerEvent::AA_GAIN].event_enabled = 1;
|
||||
m_settings[PlayerEvent::AA_PURCHASE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::FORAGE_SUCCESS].event_enabled = 0;
|
||||
m_settings[PlayerEvent::FORAGE_FAILURE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::FISH_SUCCESS].event_enabled = 0;
|
||||
m_settings[PlayerEvent::FISH_FAILURE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::ITEM_DESTROY].event_enabled = 1;
|
||||
m_settings[PlayerEvent::WENT_ONLINE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::WENT_OFFLINE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::LEVEL_GAIN].event_enabled = 1;
|
||||
m_settings[PlayerEvent::LEVEL_LOSS].event_enabled = 1;
|
||||
m_settings[PlayerEvent::LOOT_ITEM].event_enabled = 1;
|
||||
m_settings[PlayerEvent::MERCHANT_PURCHASE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::MERCHANT_SELL].event_enabled = 1;
|
||||
m_settings[PlayerEvent::GROUP_JOIN].event_enabled = 0;
|
||||
m_settings[PlayerEvent::GROUP_LEAVE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::RAID_JOIN].event_enabled = 0;
|
||||
m_settings[PlayerEvent::RAID_LEAVE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::GROUNDSPAWN_PICKUP].event_enabled = 1;
|
||||
m_settings[PlayerEvent::NPC_HANDIN].event_enabled = 1;
|
||||
m_settings[PlayerEvent::SKILL_UP].event_enabled = 0;
|
||||
m_settings[PlayerEvent::TASK_ACCEPT].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TASK_UPDATE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TASK_COMPLETE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TRADE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::GIVE_ITEM].event_enabled = 1;
|
||||
m_settings[PlayerEvent::SAY].event_enabled = 0;
|
||||
m_settings[PlayerEvent::REZ_ACCEPTED].event_enabled = 1;
|
||||
m_settings[PlayerEvent::DEATH].event_enabled = 1;
|
||||
m_settings[PlayerEvent::COMBINE_FAILURE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::COMBINE_SUCCESS].event_enabled = 1;
|
||||
m_settings[PlayerEvent::DROPPED_ITEM].event_enabled = 1;
|
||||
m_settings[PlayerEvent::SPLIT_MONEY].event_enabled = 1;
|
||||
m_settings[PlayerEvent::DZ_JOIN].event_enabled = 1;
|
||||
m_settings[PlayerEvent::DZ_LEAVE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TRADER_PURCHASE].event_enabled = 1;
|
||||
m_settings[PlayerEvent::TRADER_SELL].event_enabled = 1;
|
||||
m_settings[PlayerEvent::BANDOLIER_CREATE].event_enabled = 0;
|
||||
m_settings[PlayerEvent::BANDOLIER_SWAP].event_enabled = 0;
|
||||
m_settings[PlayerEvent::DISCOVER_ITEM].event_enabled = 1;
|
||||
m_settings[PlayerEvent::POSSIBLE_HACK].event_enabled = 1;
|
||||
m_settings[PlayerEvent::KILLED_NPC].event_enabled = 1;
|
||||
m_settings[PlayerEvent::KILLED_NAMED_NPC].event_enabled = 1;
|
||||
m_settings[PlayerEvent::KILLED_RAID_NPC].event_enabled = 1;
|
||||
|
||||
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
|
||||
m_settings[i].retention_days = RETENTION_DAYS_DEFAULT;
|
||||
}
|
||||
}
|
||||
85
common/events/player_event_logs.h
Normal file
85
common/events/player_event_logs.h
Normal file
@ -0,0 +1,85 @@
|
||||
#ifndef EQEMU_PLAYER_EVENT_LOGS_H
|
||||
#define EQEMU_PLAYER_EVENT_LOGS_H
|
||||
|
||||
#include "../repositories/player_event_log_settings_repository.h"
|
||||
#include "player_events.h"
|
||||
#include "../servertalk.h"
|
||||
#include "../repositories/player_event_logs_repository.h"
|
||||
#include "../timer.h"
|
||||
#include "../json/json_archive_single_line.h"
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <mutex>
|
||||
|
||||
class PlayerEventLogs {
|
||||
public:
|
||||
void Init();
|
||||
void ReloadSettings();
|
||||
PlayerEventLogs *SetDatabase(Database *db);
|
||||
bool ValidateDatabaseConnection();
|
||||
bool IsEventEnabled(PlayerEvent::EventType event);
|
||||
|
||||
void Process();
|
||||
|
||||
// batch queue
|
||||
void AddToQueue(const PlayerEventLogsRepository::PlayerEventLogs &logs);
|
||||
|
||||
// main event record generic function
|
||||
// can ingest any struct event types
|
||||
template<typename T>
|
||||
std::unique_ptr<ServerPacket> RecordEvent(
|
||||
PlayerEvent::EventType t,
|
||||
const PlayerEvent::PlayerEvent &p,
|
||||
T e
|
||||
)
|
||||
{
|
||||
auto n = PlayerEventLogsRepository::NewEntity();
|
||||
FillPlayerEvent(p, n);
|
||||
n.event_type_id = t;
|
||||
|
||||
std::stringstream ss;
|
||||
{
|
||||
cereal::JSONOutputArchiveSingleLine ar(ss);
|
||||
e.serialize(ar);
|
||||
}
|
||||
|
||||
n.event_type_name = PlayerEvent::EventName[t];
|
||||
n.event_data = Strings::Contains(ss.str(), "noop") ? "{}" : ss.str();
|
||||
n.created_at = std::time(nullptr);
|
||||
|
||||
auto c = PlayerEvent::PlayerEventContainer{
|
||||
.player_event = p,
|
||||
.player_event_log = n
|
||||
};
|
||||
|
||||
return BuildPlayerEventPacket(c);
|
||||
}
|
||||
|
||||
[[nodiscard]] const PlayerEventLogSettingsRepository::PlayerEventLogSettings *GetSettings() const;
|
||||
bool IsEventDiscordEnabled(int32_t event_type_id);
|
||||
std::string GetDiscordWebhookUrlFromEventType(int32_t event_type_id);
|
||||
|
||||
static std::string GetDiscordPayloadFromEvent(const PlayerEvent::PlayerEventContainer &e);
|
||||
private:
|
||||
Database *m_database; // reference to database
|
||||
PlayerEventLogSettingsRepository::PlayerEventLogSettings m_settings[PlayerEvent::EventType::MAX]{};
|
||||
|
||||
// batch queue is used to record events in batch
|
||||
std::vector<PlayerEventLogsRepository::PlayerEventLogs> m_record_batch_queue{};
|
||||
static void FillPlayerEvent(const PlayerEvent::PlayerEvent &p, PlayerEventLogsRepository::PlayerEventLogs &n);
|
||||
static std::unique_ptr<ServerPacket>
|
||||
BuildPlayerEventPacket(const PlayerEvent::PlayerEventContainer &e);
|
||||
|
||||
// timers
|
||||
Timer m_process_batch_events_timer; // events processing timer
|
||||
Timer m_process_retention_truncation_timer; // timer for truncating events based on retention settings
|
||||
|
||||
// processing
|
||||
std::mutex m_batch_queue_lock{};
|
||||
void ProcessBatchQueue();
|
||||
void ProcessRetentionTruncation();
|
||||
void SetSettingsDefaults();
|
||||
};
|
||||
|
||||
extern PlayerEventLogs player_event_logs;
|
||||
|
||||
#endif //EQEMU_PLAYER_EVENT_LOGS_H
|
||||
935
common/events/player_events.h
Normal file
935
common/events/player_events.h
Normal file
@ -0,0 +1,935 @@
|
||||
#ifndef EQEMU_PLAYER_EVENTS_H
|
||||
#define EQEMU_PLAYER_EVENTS_H
|
||||
|
||||
#include <string>
|
||||
#include <cereal/cereal.hpp>
|
||||
#include "../types.h"
|
||||
#include "../repositories/player_event_logs_repository.h"
|
||||
|
||||
namespace PlayerEvent {
|
||||
|
||||
enum EventType {
|
||||
GM_COMMAND = 1,
|
||||
ZONING,
|
||||
AA_GAIN,
|
||||
AA_PURCHASE,
|
||||
FORAGE_SUCCESS,
|
||||
FORAGE_FAILURE,
|
||||
FISH_SUCCESS,
|
||||
FISH_FAILURE,
|
||||
ITEM_DESTROY,
|
||||
WENT_ONLINE,
|
||||
WENT_OFFLINE,
|
||||
LEVEL_GAIN,
|
||||
LEVEL_LOSS,
|
||||
LOOT_ITEM,
|
||||
MERCHANT_PURCHASE,
|
||||
MERCHANT_SELL,
|
||||
GROUP_JOIN, // unimplemented
|
||||
GROUP_LEAVE, // unimplemented
|
||||
RAID_JOIN, // unimplemented
|
||||
RAID_LEAVE, // unimplemented
|
||||
GROUNDSPAWN_PICKUP,
|
||||
NPC_HANDIN,
|
||||
SKILL_UP,
|
||||
TASK_ACCEPT,
|
||||
TASK_UPDATE,
|
||||
TASK_COMPLETE,
|
||||
TRADE,
|
||||
GIVE_ITEM, // unimplemented
|
||||
SAY,
|
||||
REZ_ACCEPTED,
|
||||
DEATH,
|
||||
COMBINE_FAILURE,
|
||||
COMBINE_SUCCESS,
|
||||
DROPPED_ITEM,
|
||||
SPLIT_MONEY,
|
||||
DZ_JOIN, // unimplemented
|
||||
DZ_LEAVE, // unimplemented
|
||||
TRADER_PURCHASE,
|
||||
TRADER_SELL,
|
||||
BANDOLIER_CREATE, // unimplemented
|
||||
BANDOLIER_SWAP, // unimplemented
|
||||
DISCOVER_ITEM,
|
||||
POSSIBLE_HACK,
|
||||
KILLED_NPC,
|
||||
KILLED_NAMED_NPC,
|
||||
KILLED_RAID_NPC,
|
||||
MAX // dont remove
|
||||
};
|
||||
|
||||
// Don't ever remove items, even if they are deprecated
|
||||
// If event is deprecated just tag (Deprecated) in the name
|
||||
// If event is unimplemented just tag (Unimplemented) in the name
|
||||
// Events don't get saved to the database if unimplemented or deprecated
|
||||
// Events tagged as deprecated will get automatically removed
|
||||
static const char *EventName[PlayerEvent::MAX] = {
|
||||
"None",
|
||||
"GM Command",
|
||||
"Zoning",
|
||||
"AA Gain",
|
||||
"AA Purchase",
|
||||
"Forage Success",
|
||||
"Forage Failure",
|
||||
"Fish Success",
|
||||
"Fish Failure",
|
||||
"Item Destroy",
|
||||
"Went Online",
|
||||
"Went Offline",
|
||||
"Level Gain",
|
||||
"Level Loss",
|
||||
"Loot Item",
|
||||
"Merchant Purchase",
|
||||
"Merchant Sell",
|
||||
"Group Join (Unimplemented)",
|
||||
"Group Leave (Unimplemented)",
|
||||
"Raid Join (Unimplemented)",
|
||||
"Raid Leave (Unimplemented)",
|
||||
"Groundspawn Pickup",
|
||||
"NPC Handin",
|
||||
"Skill Up",
|
||||
"Task Accept",
|
||||
"Task Update",
|
||||
"Task Complete",
|
||||
"Trade",
|
||||
"Given Item (Unimplemented)",
|
||||
"Say",
|
||||
"Rez Accepted",
|
||||
"Death",
|
||||
"Combine Failure",
|
||||
"Combine Success",
|
||||
"Dropped Item",
|
||||
"Split Money",
|
||||
"DZ Join (Unimplemented)",
|
||||
"DZ Leave (Unimplemented)",
|
||||
"Trader Purchase",
|
||||
"Trader Sell",
|
||||
"Bandolier Create (Unimplemented)",
|
||||
"Bandolier Swap (Unimplemented)",
|
||||
"Discover Item",
|
||||
"Possible Hack",
|
||||
"Killed NPC",
|
||||
"Killed Named NPC",
|
||||
"Killed Raid NPC"
|
||||
};
|
||||
|
||||
// Generic struct used by all events
|
||||
struct PlayerEvent {
|
||||
int64 account_id;
|
||||
std::string account_name;
|
||||
int64 character_id;
|
||||
std::string character_name;
|
||||
int64 guild_id;
|
||||
std::string guild_name;
|
||||
int zone_id;
|
||||
std::string zone_short_name;
|
||||
std::string zone_long_name;
|
||||
int instance_id;
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float heading;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(account_id),
|
||||
CEREAL_NVP(account_name),
|
||||
CEREAL_NVP(character_id),
|
||||
CEREAL_NVP(character_name),
|
||||
CEREAL_NVP(guild_id),
|
||||
CEREAL_NVP(guild_name),
|
||||
CEREAL_NVP(zone_id),
|
||||
CEREAL_NVP(zone_short_name),
|
||||
CEREAL_NVP(zone_long_name),
|
||||
CEREAL_NVP(instance_id),
|
||||
CEREAL_NVP(x),
|
||||
CEREAL_NVP(y),
|
||||
CEREAL_NVP(z),
|
||||
CEREAL_NVP(heading)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// contains metadata in use for things like log/discord formatters
|
||||
// along with the actual event to be persisted
|
||||
struct PlayerEventContainer {
|
||||
PlayerEvent player_event;
|
||||
PlayerEventLogsRepository::PlayerEventLogs player_event_log;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(player_event),
|
||||
CEREAL_NVP(player_event_log)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// used in events with no extra data
|
||||
struct EmptyEvent {
|
||||
std::string noop; // noop, gets discard upstream
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(noop)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// used in Trade event
|
||||
struct TradeItem {
|
||||
int64 item_id;
|
||||
std::string item_name;
|
||||
int32 slot;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(slot)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// used in Trade event
|
||||
class TradeItemEntry {
|
||||
public:
|
||||
uint16 slot;
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
uint16 charges;
|
||||
uint32 aug_1_item_id;
|
||||
std::string aug_1_item_name;
|
||||
uint32 aug_2_item_id;
|
||||
std::string aug_2_item_name;
|
||||
uint32 aug_3_item_id;
|
||||
std::string aug_3_item_name;
|
||||
uint32 aug_4_item_id;
|
||||
std::string aug_4_item_name;
|
||||
uint32 aug_5_item_id;
|
||||
std::string aug_5_item_name;
|
||||
uint32 aug_6_item_id;
|
||||
std::string aug_6_item_name;
|
||||
bool in_bag;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(slot),
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(charges),
|
||||
CEREAL_NVP(aug_1_item_id),
|
||||
CEREAL_NVP(aug_2_item_id),
|
||||
CEREAL_NVP(aug_3_item_id),
|
||||
CEREAL_NVP(aug_4_item_id),
|
||||
CEREAL_NVP(aug_5_item_id),
|
||||
CEREAL_NVP(in_bag)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Events
|
||||
*/
|
||||
struct Money {
|
||||
int32 platinum;
|
||||
int32 gold;
|
||||
int32 silver;
|
||||
int32 copper;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(platinum),
|
||||
CEREAL_NVP(gold),
|
||||
CEREAL_NVP(silver),
|
||||
CEREAL_NVP(copper)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct TradeEvent {
|
||||
uint32 character_1_id;
|
||||
std::string character_1_name;
|
||||
uint32 character_2_id;
|
||||
std::string character_2_name;
|
||||
Money character_1_give_money;
|
||||
Money character_2_give_money;
|
||||
std::vector<TradeItemEntry> character_1_give_items;
|
||||
std::vector<TradeItemEntry> character_2_give_items;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(character_1_id),
|
||||
CEREAL_NVP(character_1_name),
|
||||
CEREAL_NVP(character_2_id),
|
||||
CEREAL_NVP(character_2_name),
|
||||
CEREAL_NVP(character_1_give_money),
|
||||
CEREAL_NVP(character_2_give_money),
|
||||
CEREAL_NVP(character_1_give_items),
|
||||
CEREAL_NVP(character_2_give_items)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct GMCommandEvent {
|
||||
std::string message;
|
||||
std::string target;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(message),
|
||||
CEREAL_NVP(target)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct ZoningEvent {
|
||||
std::string from_zone_long_name;
|
||||
std::string from_zone_short_name;
|
||||
int32 from_zone_id;
|
||||
int32 from_instance_id;
|
||||
int32 from_instance_version;
|
||||
std::string to_zone_long_name;
|
||||
std::string to_zone_short_name;
|
||||
int32 to_zone_id;
|
||||
int32 to_instance_id;
|
||||
int32 to_instance_version;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(from_zone_long_name),
|
||||
CEREAL_NVP(from_zone_short_name),
|
||||
CEREAL_NVP(from_zone_id),
|
||||
CEREAL_NVP(from_instance_id),
|
||||
CEREAL_NVP(from_instance_version),
|
||||
CEREAL_NVP(to_zone_long_name),
|
||||
CEREAL_NVP(to_zone_short_name),
|
||||
CEREAL_NVP(to_zone_id),
|
||||
CEREAL_NVP(to_instance_id),
|
||||
CEREAL_NVP(to_instance_version)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct AAGainedEvent {
|
||||
uint32 aa_gained;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(CEREAL_NVP(aa_gained));
|
||||
}
|
||||
};
|
||||
|
||||
struct AAPurchasedEvent {
|
||||
int32 aa_id;
|
||||
int32 aa_cost;
|
||||
int32 aa_previous_id;
|
||||
int32 aa_next_id;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(aa_id),
|
||||
CEREAL_NVP(aa_cost),
|
||||
CEREAL_NVP(aa_previous_id),
|
||||
CEREAL_NVP(aa_next_id)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct ForageSuccessEvent {
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct FishSuccessEvent {
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct DestroyItemEvent {
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
int16 charges;
|
||||
std::string reason;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(reason),
|
||||
CEREAL_NVP(charges)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct LevelGainedEvent {
|
||||
uint32 from_level;
|
||||
uint8 to_level;
|
||||
int levels_gained;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(from_level),
|
||||
CEREAL_NVP(to_level),
|
||||
CEREAL_NVP(levels_gained)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct LevelLostEvent {
|
||||
uint32 from_level;
|
||||
uint8 to_level;
|
||||
int levels_lost;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(from_level),
|
||||
CEREAL_NVP(to_level),
|
||||
CEREAL_NVP(levels_lost)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct LootItemEvent {
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
int16 charges;
|
||||
uint32 npc_id;
|
||||
std::string corpse_name;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(charges),
|
||||
CEREAL_NVP(npc_id),
|
||||
CEREAL_NVP(corpse_name)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct MerchantPurchaseEvent {
|
||||
uint32 npc_id;
|
||||
std::string merchant_name;
|
||||
uint32 merchant_type;
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
int16 charges;
|
||||
uint32 cost;
|
||||
uint32 alternate_currency_id;
|
||||
uint64 player_money_balance;
|
||||
uint64 player_currency_balance;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(npc_id),
|
||||
CEREAL_NVP(merchant_name),
|
||||
CEREAL_NVP(merchant_type),
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(charges),
|
||||
CEREAL_NVP(cost),
|
||||
CEREAL_NVP(alternate_currency_id),
|
||||
CEREAL_NVP(player_money_balance),
|
||||
CEREAL_NVP(player_currency_balance)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct MerchantSellEvent {
|
||||
uint32 npc_id;
|
||||
std::string merchant_name;
|
||||
uint32 merchant_type;
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
int16 charges;
|
||||
uint32 cost;
|
||||
uint32 alternate_currency_id;
|
||||
uint64 player_money_balance;
|
||||
uint64 player_currency_balance;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(npc_id),
|
||||
CEREAL_NVP(merchant_name),
|
||||
CEREAL_NVP(merchant_type),
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(charges),
|
||||
CEREAL_NVP(cost),
|
||||
CEREAL_NVP(alternate_currency_id),
|
||||
CEREAL_NVP(player_money_balance),
|
||||
CEREAL_NVP(player_currency_balance)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct SkillUpEvent {
|
||||
uint32 skill_id;
|
||||
int value;
|
||||
int16 max_skill;
|
||||
std::string against_who;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(skill_id),
|
||||
CEREAL_NVP(value),
|
||||
CEREAL_NVP(max_skill),
|
||||
CEREAL_NVP(against_who)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct TaskAcceptEvent {
|
||||
uint32 npc_id;
|
||||
std::string npc_name;
|
||||
uint32 task_id;
|
||||
std::string task_name;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(npc_id),
|
||||
CEREAL_NVP(npc_name),
|
||||
CEREAL_NVP(task_id),
|
||||
CEREAL_NVP(task_name)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct TaskUpdateEvent {
|
||||
uint32 task_id;
|
||||
std::string task_name;
|
||||
uint32 activity_id;
|
||||
uint32 done_count;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(task_id),
|
||||
CEREAL_NVP(task_name),
|
||||
CEREAL_NVP(activity_id),
|
||||
CEREAL_NVP(done_count)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct TaskCompleteEvent {
|
||||
uint32 task_id;
|
||||
std::string task_name;
|
||||
uint32 activity_id;
|
||||
uint32 done_count;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(task_id),
|
||||
CEREAL_NVP(task_name),
|
||||
CEREAL_NVP(activity_id),
|
||||
CEREAL_NVP(done_count)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct GroundSpawnPickupEvent {
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct SayEvent {
|
||||
std::string message;
|
||||
std::string target;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(message),
|
||||
CEREAL_NVP(target)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct ResurrectAcceptEvent {
|
||||
std::string resurrecter_name;
|
||||
std::string spell_name;
|
||||
uint32 spell_id;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(resurrecter_name),
|
||||
CEREAL_NVP(spell_name),
|
||||
CEREAL_NVP(spell_id)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct CombineEvent {
|
||||
uint32 recipe_id;
|
||||
std::string recipe_name;
|
||||
uint32 made_count;
|
||||
uint32 tradeskill_id;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(recipe_id),
|
||||
CEREAL_NVP(recipe_name),
|
||||
CEREAL_NVP(made_count),
|
||||
CEREAL_NVP(tradeskill_id)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct DroppedItemEvent {
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
int16 slot_id;
|
||||
uint32 charges;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(slot_id),
|
||||
CEREAL_NVP(charges)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct DeathEvent {
|
||||
uint32 killer_id;
|
||||
std::string killer_name;
|
||||
int64 damage;
|
||||
uint32 spell_id;
|
||||
std::string spell_name;
|
||||
int skill_id;
|
||||
std::string skill_name;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(killer_id),
|
||||
CEREAL_NVP(killer_name),
|
||||
CEREAL_NVP(damage),
|
||||
CEREAL_NVP(spell_id),
|
||||
CEREAL_NVP(spell_name),
|
||||
CEREAL_NVP(skill_id),
|
||||
CEREAL_NVP(skill_name)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct SplitMoneyEvent {
|
||||
uint32 copper;
|
||||
uint32 silver;
|
||||
uint32 gold;
|
||||
uint32 platinum;
|
||||
uint64 player_money_balance;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(copper),
|
||||
CEREAL_NVP(silver),
|
||||
CEREAL_NVP(gold),
|
||||
CEREAL_NVP(platinum),
|
||||
CEREAL_NVP(player_money_balance)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct TraderPurchaseEvent {
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
uint32 trader_id;
|
||||
std::string trader_name;
|
||||
uint32 price;
|
||||
uint32 charges;
|
||||
uint32 total_cost;
|
||||
uint64 player_money_balance;
|
||||
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(trader_id),
|
||||
CEREAL_NVP(trader_name),
|
||||
CEREAL_NVP(price),
|
||||
CEREAL_NVP(charges),
|
||||
CEREAL_NVP(total_cost),
|
||||
CEREAL_NVP(player_money_balance)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct TraderSellEvent {
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
uint32 buyer_id;
|
||||
std::string buyer_name;
|
||||
uint32 price;
|
||||
uint32 charges;
|
||||
uint32 total_cost;
|
||||
uint64 player_money_balance;
|
||||
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(buyer_id),
|
||||
CEREAL_NVP(buyer_name),
|
||||
CEREAL_NVP(price),
|
||||
CEREAL_NVP(charges),
|
||||
CEREAL_NVP(total_cost),
|
||||
CEREAL_NVP(player_money_balance)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct DiscoverItemEvent {
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class HandinEntry {
|
||||
public:
|
||||
uint32 item_id;
|
||||
std::string item_name;
|
||||
uint16 charges;
|
||||
bool attuned;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(charges),
|
||||
CEREAL_NVP(attuned)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class HandinMoney {
|
||||
public:
|
||||
uint32 copper;
|
||||
uint32 silver;
|
||||
uint32 gold;
|
||||
uint32 platinum;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(copper),
|
||||
CEREAL_NVP(silver),
|
||||
CEREAL_NVP(gold),
|
||||
CEREAL_NVP(platinum)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct HandinEvent {
|
||||
uint32 npc_id;
|
||||
std::string npc_name;
|
||||
std::vector<HandinEntry> handin_items;
|
||||
HandinMoney handin_money;
|
||||
std::vector<HandinEntry> return_items;
|
||||
HandinMoney return_money;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(npc_id),
|
||||
CEREAL_NVP(npc_name),
|
||||
CEREAL_NVP(handin_items),
|
||||
CEREAL_NVP(handin_money),
|
||||
CEREAL_NVP(return_items),
|
||||
CEREAL_NVP(return_money)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct PossibleHackEvent {
|
||||
std::string message;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(message)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct KilledNPCEvent {
|
||||
uint32 npc_id;
|
||||
std::string npc_name;
|
||||
uint32 combat_time_seconds;
|
||||
uint64 total_damage_per_second_taken;
|
||||
uint64 total_heal_per_second_taken;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(npc_id),
|
||||
CEREAL_NVP(npc_name),
|
||||
CEREAL_NVP(combat_time_seconds),
|
||||
CEREAL_NVP(total_damage_per_second_taken),
|
||||
CEREAL_NVP(total_heal_per_second_taken)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //EQEMU_PLAYER_EVENTS_H
|
||||
|
||||
#define RecordPlayerEventLog(event_type, event_data) do {\
|
||||
if (player_event_logs.IsEventEnabled(event_type)) {\
|
||||
worldserver.SendPacket(\
|
||||
player_event_logs.RecordEvent(\
|
||||
event_type,\
|
||||
GetPlayerEvent(),\
|
||||
event_data\
|
||||
).get()\
|
||||
);\
|
||||
}\
|
||||
} while (0)
|
||||
|
||||
#define RecordPlayerEventLogWithClient(c, event_type, event_data) do {\
|
||||
if (player_event_logs.IsEventEnabled(event_type)) {\
|
||||
worldserver.SendPacket(\
|
||||
player_event_logs.RecordEvent(\
|
||||
event_type,\
|
||||
(c)->GetPlayerEvent(),\
|
||||
event_data\
|
||||
).get()\
|
||||
);\
|
||||
}\
|
||||
} while (0)
|
||||
1010
common/json/json_archive_single_line.h
Normal file
1010
common/json/json_archive_single_line.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -22,26 +22,31 @@
|
||||
|
||||
EQEmuExePlatform exe_platform = ExePlatformNone;
|
||||
|
||||
void RegisterExecutablePlatform(EQEmuExePlatform p) {
|
||||
void RegisterExecutablePlatform(EQEmuExePlatform p)
|
||||
{
|
||||
exe_platform = p;
|
||||
}
|
||||
|
||||
const EQEmuExePlatform& GetExecutablePlatform() {
|
||||
const EQEmuExePlatform &GetExecutablePlatform()
|
||||
{
|
||||
return exe_platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
int GetExecutablePlatformInt(){
|
||||
int GetExecutablePlatformInt()
|
||||
{
|
||||
return exe_platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns platform name by string
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
bool IsWorld()
|
||||
{
|
||||
return exe_platform == EQEmuExePlatform::ExePlatformWorld;
|
||||
}
|
||||
|
||||
bool IsQueryServ()
|
||||
{
|
||||
return exe_platform == EQEmuExePlatform::ExePlatformQueryServ;
|
||||
}
|
||||
|
||||
std::string GetPlatformName()
|
||||
{
|
||||
switch (GetExecutablePlatformInt()) {
|
||||
|
||||
@ -44,5 +44,7 @@ void RegisterExecutablePlatform(EQEmuExePlatform p);
|
||||
const EQEmuExePlatform& GetExecutablePlatform();
|
||||
int GetExecutablePlatformInt();
|
||||
std::string GetPlatformName();
|
||||
bool IsWorld();
|
||||
bool IsQueryServ();
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,412 +0,0 @@
|
||||
/**
|
||||
* DO NOT MODIFY THIS FILE
|
||||
*
|
||||
* This repository was automatically generated and is NOT to be modified directly.
|
||||
* Any repository modifications are meant to be made to the repository extending the base.
|
||||
* Any modifications to base repositories are to be made by the generator only
|
||||
*
|
||||
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_EVENTLOG_REPOSITORY_H
|
||||
#define EQEMU_BASE_EVENTLOG_REPOSITORY_H
|
||||
|
||||
#include "../../database.h"
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
class BaseEventlogRepository {
|
||||
public:
|
||||
struct Eventlog {
|
||||
uint32_t id;
|
||||
std::string accountname;
|
||||
uint32_t accountid;
|
||||
int32_t status;
|
||||
std::string charname;
|
||||
std::string target;
|
||||
std::string time;
|
||||
std::string descriptiontype;
|
||||
std::string description;
|
||||
int32_t event_nid;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"accountname",
|
||||
"accountid",
|
||||
"status",
|
||||
"charname",
|
||||
"target",
|
||||
"time",
|
||||
"descriptiontype",
|
||||
"description",
|
||||
"event_nid",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"accountname",
|
||||
"accountid",
|
||||
"status",
|
||||
"charname",
|
||||
"target",
|
||||
"time",
|
||||
"descriptiontype",
|
||||
"description",
|
||||
"event_nid",
|
||||
};
|
||||
}
|
||||
|
||||
static std::string ColumnsRaw()
|
||||
{
|
||||
return std::string(Strings::Implode(", ", Columns()));
|
||||
}
|
||||
|
||||
static std::string SelectColumnsRaw()
|
||||
{
|
||||
return std::string(Strings::Implode(", ", SelectColumns()));
|
||||
}
|
||||
|
||||
static std::string TableName()
|
||||
{
|
||||
return std::string("eventlog");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
SelectColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static Eventlog NewEntity()
|
||||
{
|
||||
Eventlog e{};
|
||||
|
||||
e.id = 0;
|
||||
e.accountname = "";
|
||||
e.accountid = 0;
|
||||
e.status = 0;
|
||||
e.charname = "";
|
||||
e.target = "None";
|
||||
e.time = std::time(nullptr);
|
||||
e.descriptiontype = "";
|
||||
e.description = "";
|
||||
e.event_nid = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static Eventlog GetEventlog(
|
||||
const std::vector<Eventlog> &eventlogs,
|
||||
int eventlog_id
|
||||
)
|
||||
{
|
||||
for (auto &eventlog : eventlogs) {
|
||||
if (eventlog.id == eventlog_id) {
|
||||
return eventlog;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static Eventlog FindOne(
|
||||
Database& db,
|
||||
int eventlog_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE id = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
eventlog_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
Eventlog e{};
|
||||
|
||||
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
|
||||
e.accountname = row[1] ? row[1] : "";
|
||||
e.accountid = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e.status = static_cast<int32_t>(atoi(row[3]));
|
||||
e.charname = row[4] ? row[4] : "";
|
||||
e.target = row[5] ? row[5] : "";
|
||||
e.time = row[6] ? row[6] : "";
|
||||
e.descriptiontype = row[7] ? row[7] : "";
|
||||
e.description = row[8] ? row[8] : "";
|
||||
e.event_nid = static_cast<int32_t>(atoi(row[9]));
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
Database& db,
|
||||
int eventlog_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
eventlog_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
Database& db,
|
||||
const Eventlog &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[1] + " = '" + Strings::Escape(e.accountname) + "'");
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.accountid));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.status));
|
||||
v.push_back(columns[4] + " = '" + Strings::Escape(e.charname) + "'");
|
||||
v.push_back(columns[5] + " = '" + Strings::Escape(e.target) + "'");
|
||||
v.push_back(columns[6] + " = '" + Strings::Escape(e.time) + "'");
|
||||
v.push_back(columns[7] + " = '" + Strings::Escape(e.descriptiontype) + "'");
|
||||
v.push_back(columns[8] + " = '" + Strings::Escape(e.description) + "'");
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.event_nid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static Eventlog InsertOne(
|
||||
Database& db,
|
||||
Eventlog e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back("'" + Strings::Escape(e.accountname) + "'");
|
||||
v.push_back(std::to_string(e.accountid));
|
||||
v.push_back(std::to_string(e.status));
|
||||
v.push_back("'" + Strings::Escape(e.charname) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.target) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.time) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.descriptiontype) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.description) + "'");
|
||||
v.push_back(std::to_string(e.event_nid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
e = NewEntity();
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static int InsertMany(
|
||||
Database& db,
|
||||
const std::vector<Eventlog> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back("'" + Strings::Escape(e.accountname) + "'");
|
||||
v.push_back(std::to_string(e.accountid));
|
||||
v.push_back(std::to_string(e.status));
|
||||
v.push_back("'" + Strings::Escape(e.charname) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.target) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.time) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.descriptiontype) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.description) + "'");
|
||||
v.push_back(std::to_string(e.event_nid));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static std::vector<Eventlog> All(Database& db)
|
||||
{
|
||||
std::vector<Eventlog> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Eventlog e{};
|
||||
|
||||
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
|
||||
e.accountname = row[1] ? row[1] : "";
|
||||
e.accountid = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e.status = static_cast<int32_t>(atoi(row[3]));
|
||||
e.charname = row[4] ? row[4] : "";
|
||||
e.target = row[5] ? row[5] : "";
|
||||
e.time = row[6] ? row[6] : "";
|
||||
e.descriptiontype = row[7] ? row[7] : "";
|
||||
e.description = row[8] ? row[8] : "";
|
||||
e.event_nid = static_cast<int32_t>(atoi(row[9]));
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<Eventlog> GetWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
std::vector<Eventlog> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {}",
|
||||
BaseSelect(),
|
||||
where_filter
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Eventlog e{};
|
||||
|
||||
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
|
||||
e.accountname = row[1] ? row[1] : "";
|
||||
e.accountid = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e.status = static_cast<int32_t>(atoi(row[3]));
|
||||
e.charname = row[4] ? row[4] : "";
|
||||
e.target = row[5] ? row[5] : "";
|
||||
e.time = row[6] ? row[6] : "";
|
||||
e.descriptiontype = row[7] ? row[7] : "";
|
||||
e.description = row[8] ? row[8] : "";
|
||||
e.event_nid = static_cast<int32_t>(atoi(row[9]));
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static int DeleteWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {}",
|
||||
TableName(),
|
||||
where_filter
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int Truncate(Database& db)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"TRUNCATE TABLE {}",
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int64 GetMaxId(Database& db)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT COALESCE(MAX({}), 0) FROM {}",
|
||||
PrimaryKey(),
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
static int64 Count(Database& db, const std::string &where_filter = "")
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT COUNT(*) FROM {} {}",
|
||||
TableName(),
|
||||
(where_filter.empty() ? "" : "WHERE " + where_filter)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_EVENTLOG_REPOSITORY_H
|
||||
@ -9,22 +9,21 @@
|
||||
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_HACKERS_REPOSITORY_H
|
||||
#define EQEMU_BASE_HACKERS_REPOSITORY_H
|
||||
#ifndef EQEMU_BASE_PLAYER_EVENT_LOG_SETTINGS_REPOSITORY_H
|
||||
#define EQEMU_BASE_PLAYER_EVENT_LOG_SETTINGS_REPOSITORY_H
|
||||
|
||||
#include "../../database.h"
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
class BaseHackersRepository {
|
||||
class BasePlayerEventLogSettingsRepository {
|
||||
public:
|
||||
struct Hackers {
|
||||
int32_t id;
|
||||
std::string account;
|
||||
std::string name;
|
||||
std::string hacked;
|
||||
std::string zone;
|
||||
std::string date;
|
||||
struct PlayerEventLogSettings {
|
||||
int64_t id;
|
||||
std::string event_name;
|
||||
int8_t event_enabled;
|
||||
int32_t retention_days;
|
||||
int32_t discord_webhook_id;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@ -36,11 +35,10 @@ public:
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"account",
|
||||
"name",
|
||||
"hacked",
|
||||
"zone",
|
||||
"date",
|
||||
"event_name",
|
||||
"event_enabled",
|
||||
"retention_days",
|
||||
"discord_webhook_id",
|
||||
};
|
||||
}
|
||||
|
||||
@ -48,11 +46,10 @@ public:
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"account",
|
||||
"name",
|
||||
"hacked",
|
||||
"zone",
|
||||
"date",
|
||||
"event_name",
|
||||
"event_enabled",
|
||||
"retention_days",
|
||||
"discord_webhook_id",
|
||||
};
|
||||
}
|
||||
|
||||
@ -68,7 +65,7 @@ public:
|
||||
|
||||
static std::string TableName()
|
||||
{
|
||||
return std::string("hackers");
|
||||
return std::string("player_event_log_settings");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
@ -89,57 +86,56 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
static Hackers NewEntity()
|
||||
static PlayerEventLogSettings NewEntity()
|
||||
{
|
||||
Hackers e{};
|
||||
PlayerEventLogSettings e{};
|
||||
|
||||
e.id = 0;
|
||||
e.account = "";
|
||||
e.name = "";
|
||||
e.hacked = "";
|
||||
e.zone = "";
|
||||
e.date = std::time(nullptr);
|
||||
e.id = 0;
|
||||
e.event_name = "";
|
||||
e.event_enabled = 0;
|
||||
e.retention_days = 0;
|
||||
e.discord_webhook_id = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static Hackers GetHackers(
|
||||
const std::vector<Hackers> &hackerss,
|
||||
int hackers_id
|
||||
static PlayerEventLogSettings GetPlayerEventLogSettings(
|
||||
const std::vector<PlayerEventLogSettings> &player_event_log_settingss,
|
||||
int player_event_log_settings_id
|
||||
)
|
||||
{
|
||||
for (auto &hackers : hackerss) {
|
||||
if (hackers.id == hackers_id) {
|
||||
return hackers;
|
||||
for (auto &player_event_log_settings : player_event_log_settingss) {
|
||||
if (player_event_log_settings.id == player_event_log_settings_id) {
|
||||
return player_event_log_settings;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static Hackers FindOne(
|
||||
static PlayerEventLogSettings FindOne(
|
||||
Database& db,
|
||||
int hackers_id
|
||||
int player_event_log_settings_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE id = {} LIMIT 1",
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
hackers_id
|
||||
PrimaryKey(),
|
||||
player_event_log_settings_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
Hackers e{};
|
||||
PlayerEventLogSettings e{};
|
||||
|
||||
e.id = static_cast<int32_t>(atoi(row[0]));
|
||||
e.account = row[1] ? row[1] : "";
|
||||
e.name = row[2] ? row[2] : "";
|
||||
e.hacked = row[3] ? row[3] : "";
|
||||
e.zone = row[4] ? row[4] : "";
|
||||
e.date = row[5] ? row[5] : "";
|
||||
e.id = strtoll(row[0], nullptr, 10);
|
||||
e.event_name = row[1] ? row[1] : "";
|
||||
e.event_enabled = static_cast<int8_t>(atoi(row[2]));
|
||||
e.retention_days = static_cast<int32_t>(atoi(row[3]));
|
||||
e.discord_webhook_id = static_cast<int32_t>(atoi(row[4]));
|
||||
|
||||
return e;
|
||||
}
|
||||
@ -149,7 +145,7 @@ public:
|
||||
|
||||
static int DeleteOne(
|
||||
Database& db,
|
||||
int hackers_id
|
||||
int player_event_log_settings_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
@ -157,7 +153,7 @@ public:
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
hackers_id
|
||||
player_event_log_settings_id
|
||||
)
|
||||
);
|
||||
|
||||
@ -166,18 +162,18 @@ public:
|
||||
|
||||
static int UpdateOne(
|
||||
Database& db,
|
||||
const Hackers &e
|
||||
const PlayerEventLogSettings &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[1] + " = '" + Strings::Escape(e.account) + "'");
|
||||
v.push_back(columns[2] + " = '" + Strings::Escape(e.name) + "'");
|
||||
v.push_back(columns[3] + " = '" + Strings::Escape(e.hacked) + "'");
|
||||
v.push_back(columns[4] + " = '" + Strings::Escape(e.zone) + "'");
|
||||
v.push_back(columns[5] + " = '" + Strings::Escape(e.date) + "'");
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.id));
|
||||
v.push_back(columns[1] + " = '" + Strings::Escape(e.event_name) + "'");
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.event_enabled));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.retention_days));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.discord_webhook_id));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@ -192,19 +188,18 @@ public:
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static Hackers InsertOne(
|
||||
static PlayerEventLogSettings InsertOne(
|
||||
Database& db,
|
||||
Hackers e
|
||||
PlayerEventLogSettings e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back("'" + Strings::Escape(e.account) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.hacked) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.zone) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.date) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.event_name) + "'");
|
||||
v.push_back(std::to_string(e.event_enabled));
|
||||
v.push_back(std::to_string(e.retention_days));
|
||||
v.push_back(std::to_string(e.discord_webhook_id));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@ -226,7 +221,7 @@ public:
|
||||
|
||||
static int InsertMany(
|
||||
Database& db,
|
||||
const std::vector<Hackers> &entries
|
||||
const std::vector<PlayerEventLogSettings> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
@ -235,11 +230,10 @@ public:
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back("'" + Strings::Escape(e.account) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.hacked) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.zone) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.date) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.event_name) + "'");
|
||||
v.push_back(std::to_string(e.event_enabled));
|
||||
v.push_back(std::to_string(e.retention_days));
|
||||
v.push_back(std::to_string(e.discord_webhook_id));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@ -257,9 +251,9 @@ public:
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static std::vector<Hackers> All(Database& db)
|
||||
static std::vector<PlayerEventLogSettings> All(Database& db)
|
||||
{
|
||||
std::vector<Hackers> all_entries;
|
||||
std::vector<PlayerEventLogSettings> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@ -271,14 +265,13 @@ public:
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Hackers e{};
|
||||
PlayerEventLogSettings e{};
|
||||
|
||||
e.id = static_cast<int32_t>(atoi(row[0]));
|
||||
e.account = row[1] ? row[1] : "";
|
||||
e.name = row[2] ? row[2] : "";
|
||||
e.hacked = row[3] ? row[3] : "";
|
||||
e.zone = row[4] ? row[4] : "";
|
||||
e.date = row[5] ? row[5] : "";
|
||||
e.id = strtoll(row[0], nullptr, 10);
|
||||
e.event_name = row[1] ? row[1] : "";
|
||||
e.event_enabled = static_cast<int8_t>(atoi(row[2]));
|
||||
e.retention_days = static_cast<int32_t>(atoi(row[3]));
|
||||
e.discord_webhook_id = static_cast<int32_t>(atoi(row[4]));
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@ -286,9 +279,9 @@ public:
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<Hackers> GetWhere(Database& db, const std::string &where_filter)
|
||||
static std::vector<PlayerEventLogSettings> GetWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
std::vector<Hackers> all_entries;
|
||||
std::vector<PlayerEventLogSettings> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@ -301,14 +294,13 @@ public:
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Hackers e{};
|
||||
PlayerEventLogSettings e{};
|
||||
|
||||
e.id = static_cast<int32_t>(atoi(row[0]));
|
||||
e.account = row[1] ? row[1] : "";
|
||||
e.name = row[2] ? row[2] : "";
|
||||
e.hacked = row[3] ? row[3] : "";
|
||||
e.zone = row[4] ? row[4] : "";
|
||||
e.date = row[5] ? row[5] : "";
|
||||
e.id = strtoll(row[0], nullptr, 10);
|
||||
e.event_name = row[1] ? row[1] : "";
|
||||
e.event_enabled = static_cast<int8_t>(atoi(row[2]));
|
||||
e.retention_days = static_cast<int32_t>(atoi(row[3]));
|
||||
e.discord_webhook_id = static_cast<int32_t>(atoi(row[4]));
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@ -369,4 +361,4 @@ public:
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_HACKERS_REPOSITORY_H
|
||||
#endif //EQEMU_BASE_PLAYER_EVENT_LOG_SETTINGS_REPOSITORY_H
|
||||
465
common/repositories/base/base_player_event_logs_repository.h
Normal file
465
common/repositories/base/base_player_event_logs_repository.h
Normal file
@ -0,0 +1,465 @@
|
||||
/**
|
||||
* DO NOT MODIFY THIS FILE
|
||||
*
|
||||
* This repository was automatically generated and is NOT to be modified directly.
|
||||
* Any repository modifications are meant to be made to the repository extending the base.
|
||||
* Any modifications to base repositories are to be made by the generator only
|
||||
*
|
||||
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_PLAYER_EVENT_LOGS_REPOSITORY_H
|
||||
#define EQEMU_BASE_PLAYER_EVENT_LOGS_REPOSITORY_H
|
||||
|
||||
#include "../../database.h"
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
#include <cereal/cereal.hpp>
|
||||
|
||||
class BasePlayerEventLogsRepository {
|
||||
public:
|
||||
struct PlayerEventLogs {
|
||||
int64_t id;
|
||||
int64_t account_id;
|
||||
int64_t character_id;
|
||||
int32_t zone_id;
|
||||
int32_t instance_id;
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float heading;
|
||||
int32_t event_type_id;
|
||||
std::string event_type_name;
|
||||
std::string event_data;
|
||||
time_t created_at;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(id),
|
||||
CEREAL_NVP(account_id),
|
||||
CEREAL_NVP(character_id),
|
||||
CEREAL_NVP(zone_id),
|
||||
CEREAL_NVP(instance_id),
|
||||
CEREAL_NVP(x),
|
||||
CEREAL_NVP(y),
|
||||
CEREAL_NVP(z),
|
||||
CEREAL_NVP(heading),
|
||||
CEREAL_NVP(event_type_id),
|
||||
CEREAL_NVP(event_type_name),
|
||||
CEREAL_NVP(event_data),
|
||||
CEREAL_NVP(created_at)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"account_id",
|
||||
"character_id",
|
||||
"zone_id",
|
||||
"instance_id",
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"heading",
|
||||
"event_type_id",
|
||||
"event_type_name",
|
||||
"event_data",
|
||||
"created_at",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"account_id",
|
||||
"character_id",
|
||||
"zone_id",
|
||||
"instance_id",
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"heading",
|
||||
"event_type_id",
|
||||
"event_type_name",
|
||||
"event_data",
|
||||
"UNIX_TIMESTAMP(created_at)",
|
||||
};
|
||||
}
|
||||
|
||||
static std::string ColumnsRaw()
|
||||
{
|
||||
return std::string(Strings::Implode(", ", Columns()));
|
||||
}
|
||||
|
||||
static std::string SelectColumnsRaw()
|
||||
{
|
||||
return std::string(Strings::Implode(", ", SelectColumns()));
|
||||
}
|
||||
|
||||
static std::string TableName()
|
||||
{
|
||||
return std::string("player_event_logs");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
SelectColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static PlayerEventLogs NewEntity()
|
||||
{
|
||||
PlayerEventLogs e{};
|
||||
|
||||
e.id = 0;
|
||||
e.account_id = 0;
|
||||
e.character_id = 0;
|
||||
e.zone_id = 0;
|
||||
e.instance_id = 0;
|
||||
e.x = 0;
|
||||
e.y = 0;
|
||||
e.z = 0;
|
||||
e.heading = 0;
|
||||
e.event_type_id = 0;
|
||||
e.event_type_name = "";
|
||||
e.event_data = "";
|
||||
e.created_at = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static PlayerEventLogs GetPlayerEventLogs(
|
||||
const std::vector<PlayerEventLogs> &player_event_logss,
|
||||
int player_event_logs_id
|
||||
)
|
||||
{
|
||||
for (auto &player_event_logs : player_event_logss) {
|
||||
if (player_event_logs.id == player_event_logs_id) {
|
||||
return player_event_logs;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static PlayerEventLogs FindOne(
|
||||
Database& db,
|
||||
int player_event_logs_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
player_event_logs_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
PlayerEventLogs e{};
|
||||
|
||||
e.id = strtoll(row[0], nullptr, 10);
|
||||
e.account_id = strtoll(row[1], nullptr, 10);
|
||||
e.character_id = strtoll(row[2], nullptr, 10);
|
||||
e.zone_id = static_cast<int32_t>(atoi(row[3]));
|
||||
e.instance_id = static_cast<int32_t>(atoi(row[4]));
|
||||
e.x = strtof(row[5], nullptr);
|
||||
e.y = strtof(row[6], nullptr);
|
||||
e.z = strtof(row[7], nullptr);
|
||||
e.heading = strtof(row[8], nullptr);
|
||||
e.event_type_id = static_cast<int32_t>(atoi(row[9]));
|
||||
e.event_type_name = row[10] ? row[10] : "";
|
||||
e.event_data = row[11] ? row[11] : "";
|
||||
e.created_at = strtoll(row[12] ? row[12] : "-1", nullptr, 10);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
Database& db,
|
||||
int player_event_logs_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
player_event_logs_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
Database& db,
|
||||
const PlayerEventLogs &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.account_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.character_id));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.zone_id));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.instance_id));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.x));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.y));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.z));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.heading));
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.event_type_id));
|
||||
v.push_back(columns[10] + " = '" + Strings::Escape(e.event_type_name) + "'");
|
||||
v.push_back(columns[11] + " = '" + Strings::Escape(e.event_data) + "'");
|
||||
v.push_back(columns[12] + " = FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static PlayerEventLogs InsertOne(
|
||||
Database& db,
|
||||
PlayerEventLogs e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.account_id));
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.zone_id));
|
||||
v.push_back(std::to_string(e.instance_id));
|
||||
v.push_back(std::to_string(e.x));
|
||||
v.push_back(std::to_string(e.y));
|
||||
v.push_back(std::to_string(e.z));
|
||||
v.push_back(std::to_string(e.heading));
|
||||
v.push_back(std::to_string(e.event_type_id));
|
||||
v.push_back("'" + Strings::Escape(e.event_type_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.event_data) + "'");
|
||||
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
e = NewEntity();
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static int InsertMany(
|
||||
Database& db,
|
||||
const std::vector<PlayerEventLogs> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.account_id));
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.zone_id));
|
||||
v.push_back(std::to_string(e.instance_id));
|
||||
v.push_back(std::to_string(e.x));
|
||||
v.push_back(std::to_string(e.y));
|
||||
v.push_back(std::to_string(e.z));
|
||||
v.push_back(std::to_string(e.heading));
|
||||
v.push_back(std::to_string(e.event_type_id));
|
||||
v.push_back("'" + Strings::Escape(e.event_type_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.event_data) + "'");
|
||||
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static std::vector<PlayerEventLogs> All(Database& db)
|
||||
{
|
||||
std::vector<PlayerEventLogs> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
PlayerEventLogs e{};
|
||||
|
||||
e.id = strtoll(row[0], nullptr, 10);
|
||||
e.account_id = strtoll(row[1], nullptr, 10);
|
||||
e.character_id = strtoll(row[2], nullptr, 10);
|
||||
e.zone_id = static_cast<int32_t>(atoi(row[3]));
|
||||
e.instance_id = static_cast<int32_t>(atoi(row[4]));
|
||||
e.x = strtof(row[5], nullptr);
|
||||
e.y = strtof(row[6], nullptr);
|
||||
e.z = strtof(row[7], nullptr);
|
||||
e.heading = strtof(row[8], nullptr);
|
||||
e.event_type_id = static_cast<int32_t>(atoi(row[9]));
|
||||
e.event_type_name = row[10] ? row[10] : "";
|
||||
e.event_data = row[11] ? row[11] : "";
|
||||
e.created_at = strtoll(row[12] ? row[12] : "-1", nullptr, 10);
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<PlayerEventLogs> GetWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
std::vector<PlayerEventLogs> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {}",
|
||||
BaseSelect(),
|
||||
where_filter
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
PlayerEventLogs e{};
|
||||
|
||||
e.id = strtoll(row[0], nullptr, 10);
|
||||
e.account_id = strtoll(row[1], nullptr, 10);
|
||||
e.character_id = strtoll(row[2], nullptr, 10);
|
||||
e.zone_id = static_cast<int32_t>(atoi(row[3]));
|
||||
e.instance_id = static_cast<int32_t>(atoi(row[4]));
|
||||
e.x = strtof(row[5], nullptr);
|
||||
e.y = strtof(row[6], nullptr);
|
||||
e.z = strtof(row[7], nullptr);
|
||||
e.heading = strtof(row[8], nullptr);
|
||||
e.event_type_id = static_cast<int32_t>(atoi(row[9]));
|
||||
e.event_type_name = row[10] ? row[10] : "";
|
||||
e.event_data = row[11] ? row[11] : "";
|
||||
e.created_at = strtoll(row[12] ? row[12] : "-1", nullptr, 10);
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static int DeleteWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {}",
|
||||
TableName(),
|
||||
where_filter
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int Truncate(Database& db)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"TRUNCATE TABLE {}",
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int64 GetMaxId(Database& db)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT COALESCE(MAX({}), 0) FROM {}",
|
||||
PrimaryKey(),
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
static int64 Count(Database& db, const std::string &where_filter = "")
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT COUNT(*) FROM {} {}",
|
||||
TableName(),
|
||||
(where_filter.empty() ? "" : "WHERE " + where_filter)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_PLAYER_EVENT_LOGS_REPOSITORY_H
|
||||
@ -5,6 +5,8 @@
|
||||
#include "../strings.h"
|
||||
#include "base/base_character_data_repository.h"
|
||||
|
||||
|
||||
|
||||
class CharacterDataRepository: public BaseCharacterDataRepository {
|
||||
public:
|
||||
|
||||
@ -44,7 +46,6 @@ public:
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_CHARACTER_DATA_REPOSITORY_H
|
||||
|
||||
@ -172,7 +172,7 @@ public:
|
||||
DELETE FROM {}
|
||||
WHERE dynamic_zone_id IN ({});
|
||||
),
|
||||
TableName(), fmt::join(dynamic_zone_ids, ",")
|
||||
TableName(), Strings::Join(dynamic_zone_ids, ",")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ public:
|
||||
FROM expedition_lockouts
|
||||
WHERE expedition_id IN ({})
|
||||
),
|
||||
fmt::join(expedition_ids, ",")
|
||||
Strings::Join(expedition_ids, ",")
|
||||
));
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
@ -62,7 +62,7 @@ public:
|
||||
|
||||
std::vector<CharacterExpedition> entries;
|
||||
|
||||
auto joined_character_names = fmt::format("'{}'", fmt::join(character_names, "','"));
|
||||
auto joined_character_names = fmt::format("'{}'", Strings::Join(character_names, "','"));
|
||||
|
||||
auto results = db.QueryDatabase(fmt::format(SQL(
|
||||
SELECT
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
#ifndef EQEMU_EVENTLOG_REPOSITORY_H
|
||||
#define EQEMU_EVENTLOG_REPOSITORY_H
|
||||
#ifndef EQEMU_PLAYER_EVENT_LOG_SETTINGS_REPOSITORY_H
|
||||
#define EQEMU_PLAYER_EVENT_LOG_SETTINGS_REPOSITORY_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
#include "base/base_eventlog_repository.h"
|
||||
#include "base/base_player_event_log_settings_repository.h"
|
||||
|
||||
class EventlogRepository: public BaseEventlogRepository {
|
||||
class PlayerEventLogSettingsRepository: public BasePlayerEventLogSettingsRepository {
|
||||
public:
|
||||
|
||||
/**
|
||||
@ -32,10 +32,10 @@ public:
|
||||
*
|
||||
* Example custom methods in a repository
|
||||
*
|
||||
* EventlogRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* EventlogRepository::GetWhereNeverExpires()
|
||||
* EventlogRepository::GetWhereXAndY()
|
||||
* EventlogRepository::DeleteWhereXAndY()
|
||||
* PlayerEventLogSettingsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* PlayerEventLogSettingsRepository::GetWhereNeverExpires()
|
||||
* PlayerEventLogSettingsRepository::GetWhereXAndY()
|
||||
* PlayerEventLogSettingsRepository::DeleteWhereXAndY()
|
||||
*
|
||||
* Most of the above could be covered by base methods, but if you as a developer
|
||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
||||
@ -47,4 +47,4 @@ public:
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_EVENTLOG_REPOSITORY_H
|
||||
#endif //EQEMU_PLAYER_EVENT_LOG_SETTINGS_REPOSITORY_H
|
||||
@ -1,11 +1,11 @@
|
||||
#ifndef EQEMU_HACKERS_REPOSITORY_H
|
||||
#define EQEMU_HACKERS_REPOSITORY_H
|
||||
#ifndef EQEMU_PLAYER_EVENT_LOGS_REPOSITORY_H
|
||||
#define EQEMU_PLAYER_EVENT_LOGS_REPOSITORY_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
#include "base/base_hackers_repository.h"
|
||||
#include "base/base_player_event_logs_repository.h"
|
||||
|
||||
class HackersRepository: public BaseHackersRepository {
|
||||
class PlayerEventLogsRepository: public BasePlayerEventLogsRepository {
|
||||
public:
|
||||
|
||||
/**
|
||||
@ -32,10 +32,10 @@ public:
|
||||
*
|
||||
* Example custom methods in a repository
|
||||
*
|
||||
* HackersRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* HackersRepository::GetWhereNeverExpires()
|
||||
* HackersRepository::GetWhereXAndY()
|
||||
* HackersRepository::DeleteWhereXAndY()
|
||||
* PlayerEventLogsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* PlayerEventLogsRepository::GetWhereNeverExpires()
|
||||
* PlayerEventLogsRepository::GetWhereXAndY()
|
||||
* PlayerEventLogsRepository::DeleteWhereXAndY()
|
||||
*
|
||||
* Most of the above could be covered by base methods, but if you as a developer
|
||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
||||
@ -47,4 +47,4 @@ public:
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_HACKERS_REPOSITORY_H
|
||||
#endif //EQEMU_PLAYER_EVENT_LOGS_REPOSITORY_H
|
||||
@ -778,6 +778,8 @@ RULE_CATEGORY_END()
|
||||
RULE_CATEGORY(Logging)
|
||||
RULE_BOOL(Logging, PrintFileFunctionAndLine, false, "Ex: [World Server] [net.cpp::main:309] Loading variables...")
|
||||
RULE_BOOL(Logging, WorldGMSayLogging, true, "Relay worldserver logging to zone processes via GM say output")
|
||||
RULE_BOOL(Logging, PlayerEventsQSProcess, false, "Have query server process player events instead of world. Useful when wanting to use a dedicated server and database for processing player events on separate disk")
|
||||
RULE_INT(Logging, BatchPlayerEventProcessIntervalSeconds, 5, "This is the interval in which player events are processed in world or qs")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(HotReload)
|
||||
|
||||
@ -281,6 +281,9 @@
|
||||
#define ServerOP_QSSendQuery 0x5006
|
||||
#define ServerOP_QSPlayerDropItem 0x5007
|
||||
|
||||
// player events
|
||||
#define ServerOP_PlayerEvent 0x5100
|
||||
|
||||
enum {
|
||||
CZUpdateType_Character,
|
||||
CZUpdateType_Group,
|
||||
@ -1308,10 +1311,10 @@ struct Server_Speech_Struct {
|
||||
char message[0];
|
||||
};
|
||||
|
||||
struct QSTradeItems_Struct {
|
||||
uint32 from_id;
|
||||
struct PlayerLogTradeItemsEntry_Struct {
|
||||
uint32 from_character_id;
|
||||
uint16 from_slot;
|
||||
uint32 to_id;
|
||||
uint32 to_character_id;
|
||||
uint16 to_slot;
|
||||
uint32 item_id;
|
||||
uint16 charges;
|
||||
@ -1322,15 +1325,15 @@ struct QSTradeItems_Struct {
|
||||
uint32 aug_5;
|
||||
};
|
||||
|
||||
struct QSPlayerLogTrade_Struct {
|
||||
uint32 char1_id;
|
||||
MoneyUpdate_Struct char1_money;
|
||||
uint16 char1_count;
|
||||
uint32 char2_id;
|
||||
MoneyUpdate_Struct char2_money;
|
||||
uint16 char2_count;
|
||||
uint16 _detail_count;
|
||||
QSTradeItems_Struct items[0];
|
||||
struct PlayerLogTrade_Struct {
|
||||
uint32 character_1_id;
|
||||
MoneyUpdate_Struct character_1_money;
|
||||
uint16 character_1_item_count;
|
||||
uint32 character_2_id;
|
||||
MoneyUpdate_Struct character_2_money;
|
||||
uint16 character_2_item_count;
|
||||
uint16 _detail_count;
|
||||
PlayerLogTradeItemsEntry_Struct item_entries[0];
|
||||
};
|
||||
|
||||
struct QSDropItems_Struct {
|
||||
@ -1806,6 +1809,11 @@ struct ServerDzCreateSerialized_Struct {
|
||||
char cereal_data[0];
|
||||
};
|
||||
|
||||
struct ServerSendPlayerEvent_Struct {
|
||||
uint32_t cereal_size;
|
||||
char cereal_data[0];
|
||||
};
|
||||
|
||||
struct ServerFlagUpdate_Struct {
|
||||
uint32 account_id;
|
||||
int16 admin;
|
||||
|
||||
@ -225,6 +225,20 @@ std::string Strings::Join(const std::vector<std::string> &ar, const std::string
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string Strings::Join(const std::vector<uint32_t> &ar, const std::string &delim)
|
||||
{
|
||||
std::string ret;
|
||||
for (size_t i = 0; i < ar.size(); ++i) {
|
||||
if (i != 0) {
|
||||
ret += delim;
|
||||
}
|
||||
|
||||
ret += std::to_string(ar[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
Strings::FindReplace(std::string &string_subject, const std::string &search_string, const std::string &replace_string)
|
||||
{
|
||||
|
||||
@ -108,6 +108,7 @@ public:
|
||||
static std::string GetBetween(const std::string &s, std::string start_delim, std::string stop_delim);
|
||||
static std::string Implode(std::string glue, std::vector<std::string> src);
|
||||
static std::string Join(const std::vector<std::string> &ar, const std::string &delim);
|
||||
static std::string Join(const std::vector<uint32_t> &ar, const std::string &delim);
|
||||
static std::string MillisecondsToTime(int duration);
|
||||
static std::string Money(uint32 platinum, uint32 gold = 0, uint32 silver = 0, uint32 copper = 0);
|
||||
static std::string NumberToWords(unsigned long long int n);
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
// Disgrace: for windows compile
|
||||
#ifndef WIN32
|
||||
#include <sys/time.h>
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9219
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9220
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9037
|
||||
|
||||
#endif
|
||||
|
||||
@ -50,7 +50,6 @@
|
||||
#include "../common/strings.h"
|
||||
#include "../common/servertalk.h"
|
||||
|
||||
|
||||
void QSDatabase::AddSpeech(
|
||||
const char *from,
|
||||
const char *to,
|
||||
@ -125,7 +124,7 @@ void QSDatabase::LogPlayerDropItem(QSPlayerDropItem_Struct *QS)
|
||||
}
|
||||
}
|
||||
|
||||
void QSDatabase::LogPlayerTrade(QSPlayerLogTrade_Struct *QS, uint32 detailCount)
|
||||
void QSDatabase::LogPlayerTrade(PlayerLogTrade_Struct *QS, uint32 detailCount)
|
||||
{
|
||||
|
||||
std::string query = StringFormat(
|
||||
@ -134,10 +133,10 @@ void QSDatabase::LogPlayerTrade(QSPlayerLogTrade_Struct *QS, uint32 detailCount)
|
||||
"`char1_sp` = '%i', `char1_cp` = '%i', `char1_items` = '%i', "
|
||||
"`char2_id` = '%i', `char2_pp` = '%i', `char2_gp` = '%i', "
|
||||
"`char2_sp` = '%i', `char2_cp` = '%i', `char2_items` = '%i'",
|
||||
QS->char1_id, QS->char1_money.platinum, QS->char1_money.gold,
|
||||
QS->char1_money.silver, QS->char1_money.copper, QS->char1_count,
|
||||
QS->char2_id, QS->char2_money.platinum, QS->char2_money.gold,
|
||||
QS->char2_money.silver, QS->char2_money.copper, QS->char2_count
|
||||
QS->character_1_id, QS->character_1_money.platinum, QS->character_1_money.gold,
|
||||
QS->character_1_money.silver, QS->character_1_money.copper, QS->character_1_item_count,
|
||||
QS->character_2_id, QS->character_2_money.platinum, QS->character_2_money.gold,
|
||||
QS->character_2_money.silver, QS->character_2_money.copper, QS->character_2_item_count
|
||||
);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
@ -157,10 +156,10 @@ void QSDatabase::LogPlayerTrade(QSPlayerLogTrade_Struct *QS, uint32 detailCount)
|
||||
"`from_id` = '%i', `from_slot` = '%i', `to_id` = '%i', `to_slot` = '%i', "
|
||||
"`item_id` = '%i', `charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', "
|
||||
"`aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'",
|
||||
lastIndex, QS->items[i].from_id, QS->items[i].from_slot,
|
||||
QS->items[i].to_id, QS->items[i].to_slot, QS->items[i].item_id,
|
||||
QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2,
|
||||
QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5
|
||||
lastIndex, QS->item_entries[i].from_character_id, QS->item_entries[i].from_slot,
|
||||
QS->item_entries[i].to_character_id, QS->item_entries[i].to_slot, QS->item_entries[i].item_id,
|
||||
QS->item_entries[i].charges, QS->item_entries[i].aug_1, QS->item_entries[i].aug_2,
|
||||
QS->item_entries[i].aug_3, QS->item_entries[i].aug_4, QS->item_entries[i].aug_5
|
||||
);
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
class QSDatabase : public Database {
|
||||
public:
|
||||
void AddSpeech(const char* from, const char* to, const char* message, uint16 minstatus, uint32 guilddbid, uint8 type);
|
||||
void LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 DetailCount);
|
||||
void LogPlayerTrade(PlayerLogTrade_Struct* QS, uint32 DetailCount);
|
||||
void LogPlayerDropItem(QSPlayerDropItem_Struct* QS);
|
||||
void LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 DetailCount);
|
||||
void LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 Members);
|
||||
|
||||
@ -100,7 +100,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
break;
|
||||
}
|
||||
case ServerOP_QSPlayerLogTrades: {
|
||||
QSPlayerLogTrade_Struct *QS = (QSPlayerLogTrade_Struct *) p.Data();
|
||||
PlayerLogTrade_Struct *QS = (PlayerLogTrade_Struct *) p.Data();
|
||||
database.LogPlayerTrade(QS, QS->_detail_count);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include "../common/database.h"
|
||||
#include "clientlist.h"
|
||||
#include "chatchannel.h"
|
||||
#include "../common/shareddb.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
@ -37,9 +37,10 @@
|
||||
|
||||
#include "../common/net/tcp_server.h"
|
||||
#include "../common/net/servertalk_client_connection.h"
|
||||
#include "../common/discord_manager.h"
|
||||
#include "../common/discord/discord_manager.h"
|
||||
#include "../common/path_manager.h"
|
||||
#include "../common/zone_store.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
ChatChannelList *ChannelList;
|
||||
Clientlist *g_Clientlist;
|
||||
@ -49,6 +50,7 @@ WorldServer *worldserver = nullptr;
|
||||
DiscordManager discord_manager;
|
||||
PathManager path;
|
||||
ZoneStore zone_store;
|
||||
PlayerEventLogs player_event_logs;
|
||||
|
||||
const ucsconfig *Config;
|
||||
|
||||
@ -93,7 +95,7 @@ void CatchSignal(int sig_num) {
|
||||
}
|
||||
}
|
||||
|
||||
void DiscordQueueListener() {
|
||||
void PlayerEventQueueListener() {
|
||||
while (caught_loop == 0) {
|
||||
discord_manager.ProcessMessageQueue();
|
||||
Sleep(100);
|
||||
@ -177,7 +179,7 @@ int main() {
|
||||
std::signal(SIGKILL, CatchSignal);
|
||||
std::signal(SIGSEGV, CatchSignal);
|
||||
|
||||
std::thread(DiscordQueueListener).detach();
|
||||
std::thread(PlayerEventQueueListener).detach();
|
||||
|
||||
worldserver = new WorldServer;
|
||||
|
||||
|
||||
@ -26,7 +26,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "clientlist.h"
|
||||
#include "ucsconfig.h"
|
||||
#include "database.h"
|
||||
#include "../common/discord_manager.h"
|
||||
#include "../common/discord/discord_manager.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
@ -76,6 +77,18 @@ void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p)
|
||||
}
|
||||
case ServerOP_ReloadLogs: {
|
||||
LogSys.LoadLogDatabaseSettings();
|
||||
player_event_logs.ReloadSettings();
|
||||
break;
|
||||
}
|
||||
case ServerOP_PlayerEvent: {
|
||||
auto n = PlayerEvent::PlayerEventContainer{};
|
||||
auto s = (ServerSendPlayerEvent_Struct*) pack->pBuffer;
|
||||
EQ::Util::MemoryStreamReader ss(s->cereal_data, s->cereal_size);
|
||||
cereal::BinaryInputArchive archive(ss);
|
||||
archive(n);
|
||||
|
||||
discord_manager.QueuePlayerEventMessage(n);
|
||||
|
||||
break;
|
||||
}
|
||||
case ServerOP_DiscordWebhookMessage: {
|
||||
|
||||
@ -473,6 +473,7 @@
|
||||
9217|2023_01_15_chatchannel_reserved_names.sql|SHOW TABLES LIKE 'chatchannel_reserved_names'|empty|
|
||||
9218|2023_01_24_item_recast.sql|show columns from character_item_recast like '%recast_type%'|contains|smallint
|
||||
9219|2023_01_29_merchant_status_requirements.sql|SHOW COLUMNS FROM merchantlist LIKE 'min_status'|empty|
|
||||
9220|2022_12_19_player_events_tables.sql|SHOW TABLES LIKE 'player_event_logs'|empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
34
utils/sql/git/required/2022_12_19_player_events_tables.sql
Normal file
34
utils/sql/git/required/2022_12_19_player_events_tables.sql
Normal file
@ -0,0 +1,34 @@
|
||||
CREATE TABLE `player_event_log_settings`
|
||||
(
|
||||
`id` bigint(20) NOT NULL,
|
||||
`event_name` varchar(100) DEFAULT NULL,
|
||||
`event_enabled` tinyint(1) DEFAULT NULL,
|
||||
`retention_days` int(11) DEFAULT 0,
|
||||
`discord_webhook_id` int(11) DEFAULT 0,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `player_event_logs`
|
||||
(
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`account_id` bigint(20) DEFAULT NULL,
|
||||
`character_id` bigint(20) DEFAULT NULL,
|
||||
`zone_id` int(11) DEFAULT NULL,
|
||||
`instance_id` int(11) DEFAULT NULL,
|
||||
`x` float DEFAULT NULL,
|
||||
`y` float DEFAULT NULL,
|
||||
`z` float DEFAULT NULL,
|
||||
`heading` float DEFAULT NULL,
|
||||
`event_type_id` int(11) DEFAULT NULL,
|
||||
`event_type_name` varchar(255) DEFAULT NULL,
|
||||
`event_data` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`event_data`)),
|
||||
`created_at` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `event_created_at` (`event_type_id`,`created_at`),
|
||||
KEY `zone_id` (`zone_id`),
|
||||
KEY `character_id` (`character_id`,`zone_id`) USING BTREE,
|
||||
KEY `created_at` (`created_at`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
DROP TABLE `hackers`;
|
||||
DROP TABLE `eventlog`;
|
||||
@ -1,4 +1,6 @@
|
||||
#include "../../common/zone_store.h"
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <cereal/types/vector.hpp>
|
||||
#include "../../common/events/player_events.h"
|
||||
|
||||
void WorldserverCLI::TestCommand(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||
{
|
||||
@ -8,14 +10,5 @@ void WorldserverCLI::TestCommand(int argc, char **argv, argh::parser &cmd, std::
|
||||
return;
|
||||
}
|
||||
|
||||
zone_store.LoadZones(database);
|
||||
|
||||
const char* zonename = ZoneName(0);
|
||||
if (zonename == 0) {
|
||||
LogInfo("Zone name is 0");
|
||||
}
|
||||
if (zonename == nullptr) {
|
||||
LogInfo("Zone name is nullptr");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -49,6 +49,8 @@
|
||||
#include "sof_char_create_data.h"
|
||||
#include "../common/zone_store.h"
|
||||
#include "../common/repositories/account_repository.h"
|
||||
#include "../common/repositories/player_event_logs_repository.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
@ -818,7 +820,8 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
|
||||
zone_id = database.MoveCharacterToBind(charid, 4);
|
||||
} else {
|
||||
LogInfo("[{}] is trying to go home before they're able.", char_name);
|
||||
database.SetHackerFlag(GetAccountName(), char_name, "MQGoHome: player tried to go home before they were able.");
|
||||
RecordPossibleHack("[MQGoHome] player tried to go home before they were able");
|
||||
|
||||
eqs->Close();
|
||||
return true;
|
||||
}
|
||||
@ -844,7 +847,8 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
|
||||
database.MoveCharacterToZone(charid, zone_id);
|
||||
} else {
|
||||
LogInfo("[{}] is trying to go to the Tutorial but they are not allowed.", char_name);
|
||||
database.SetHackerFlag(GetAccountName(), char_name, "MQTutorial: player tried to enter the tutorial without having tutorial enabled for this character.");
|
||||
RecordPossibleHack("[MQTutorial] player tried to enter the tutorial without having tutorial enabled for this character");
|
||||
|
||||
eqs->Close();
|
||||
return true;
|
||||
}
|
||||
@ -2360,3 +2364,24 @@ bool Client::StoreCharacter(
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Client::RecordPossibleHack(const std::string& message)
|
||||
{
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::POSSIBLE_HACK)) {
|
||||
auto event = PlayerEvent::PossibleHackEvent{.message = message};
|
||||
std::stringstream ss;
|
||||
{
|
||||
cereal::JSONOutputArchiveSingleLine ar(ss);
|
||||
event.serialize(ar);
|
||||
}
|
||||
|
||||
auto e = PlayerEventLogsRepository::NewEntity();
|
||||
e.character_id = charid;
|
||||
e.account_id = GetCLE() ? GetAccountID() : 0;
|
||||
e.event_type_id = PlayerEvent::POSSIBLE_HACK;
|
||||
e.event_type_name = PlayerEvent::EventName[PlayerEvent::POSSIBLE_HACK];
|
||||
e.event_data = ss.str();
|
||||
e.created_at = std::time(nullptr);
|
||||
PlayerEventLogsRepository::InsertOne(database, e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,6 +121,7 @@ private:
|
||||
|
||||
EQStreamInterface* eqs;
|
||||
bool CanTradeFVNoDropItem();
|
||||
void RecordPossibleHack(const std::string& message);
|
||||
};
|
||||
|
||||
bool CheckCharCreateInfoSoF(CharCreate_Struct *cc);
|
||||
|
||||
@ -28,9 +28,9 @@ void DynamicZoneManager::PurgeExpiredDynamicZones()
|
||||
LogDynamicZones("Purging [{}] dynamic zone(s)", dz_ids.size());
|
||||
|
||||
DynamicZoneMembersRepository::DeleteWhere(database,
|
||||
fmt::format("dynamic_zone_id IN ({})", fmt::join(dz_ids, ",")));
|
||||
fmt::format("dynamic_zone_id IN ({})", Strings::Join(dz_ids, ",")));
|
||||
DynamicZonesRepository::DeleteWhere(database,
|
||||
fmt::format("id IN ({})", fmt::join(dz_ids, ",")));
|
||||
fmt::format("id IN ({})", Strings::Join(dz_ids, ",")));
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,7 +145,7 @@ void DynamicZoneManager::Process()
|
||||
// need to look up expedition ids until lockouts are moved to dynamic zones
|
||||
std::vector<uint32_t> expedition_ids;
|
||||
auto expeditions = ExpeditionsRepository::GetWhere(database,
|
||||
fmt::format("dynamic_zone_id IN ({})", fmt::join(dynamic_zone_ids, ",")));
|
||||
fmt::format("dynamic_zone_id IN ({})", Strings::Join(dynamic_zone_ids, ",")));
|
||||
|
||||
if (!expeditions.empty())
|
||||
{
|
||||
@ -154,14 +154,14 @@ void DynamicZoneManager::Process()
|
||||
expedition_ids.emplace_back(expedition.id);
|
||||
}
|
||||
ExpeditionLockoutsRepository::DeleteWhere(database,
|
||||
fmt::format("expedition_id IN ({})", fmt::join(expedition_ids, ",")));
|
||||
fmt::format("expedition_id IN ({})", Strings::Join(expedition_ids, ",")));
|
||||
}
|
||||
|
||||
ExpeditionsRepository::DeleteWhere(database,
|
||||
fmt::format("dynamic_zone_id IN ({})", fmt::join(dynamic_zone_ids, ",")));
|
||||
fmt::format("dynamic_zone_id IN ({})", Strings::Join(dynamic_zone_ids, ",")));
|
||||
DynamicZoneMembersRepository::RemoveAllMembers(database, dynamic_zone_ids);
|
||||
DynamicZonesRepository::DeleteWhere(database,
|
||||
fmt::format("id IN ({})", fmt::join(dynamic_zone_ids, ",")));
|
||||
fmt::format("id IN ({})", Strings::Join(dynamic_zone_ids, ",")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -50,11 +50,11 @@ void ExpeditionDatabase::PurgeExpiredExpeditions()
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (results.Success())
|
||||
{
|
||||
std::vector<uint32_t> expedition_ids;
|
||||
std::vector<std::string> expedition_ids;
|
||||
std::vector<uint32_t> dynamic_zone_ids;
|
||||
for (auto row = results.begin(); row != results.end(); ++row)
|
||||
{
|
||||
expedition_ids.emplace_back(static_cast<uint32_t>(strtoul(row[0], nullptr, 10)));
|
||||
expedition_ids.emplace_back(row[0]);
|
||||
dynamic_zone_ids.emplace_back(static_cast<uint32_t>(strtoul(row[1], nullptr, 10)));
|
||||
}
|
||||
|
||||
|
||||
@ -58,6 +58,7 @@
|
||||
|
||||
#include "../common/unix.h"
|
||||
#include <sys/sem.h>
|
||||
#include <thread>
|
||||
|
||||
#if not defined (FREEBSD) && not defined (DARWIN)
|
||||
union semun {
|
||||
@ -96,6 +97,7 @@ union semun {
|
||||
#include "shared_task_manager.h"
|
||||
#include "world_boot.h"
|
||||
#include "../common/path_manager.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
|
||||
ZoneStore zone_store;
|
||||
@ -118,6 +120,7 @@ EQEmuLogSys LogSys;
|
||||
WorldContentService content_service;
|
||||
WebInterfaceList web_interface;
|
||||
PathManager path;
|
||||
PlayerEventLogs player_event_logs;
|
||||
|
||||
void CatchSignal(int sig_num);
|
||||
|
||||
@ -128,6 +131,13 @@ inline void UpdateWindowTitle(std::string new_title)
|
||||
#endif
|
||||
}
|
||||
|
||||
void PlayerEventQueueListener() {
|
||||
while (RunLoops) {
|
||||
player_event_logs.Process();
|
||||
Sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* World process entrypoint
|
||||
*
|
||||
@ -371,6 +381,13 @@ int main(int argc, char **argv)
|
||||
}
|
||||
);
|
||||
|
||||
player_event_logs.SetDatabase(&database)->Init();
|
||||
|
||||
if (!RuleB(Logging, PlayerEventsQSProcess)) {
|
||||
LogInfo("[PlayerEventQueueListener] Booting queue processor");
|
||||
std::thread(PlayerEventQueueListener).detach();
|
||||
}
|
||||
|
||||
auto loop_fn = [&](EQ::Timer* t) {
|
||||
Timer::SetCurrentTime();
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ void QueryServConnection::AddConnection(std::shared_ptr<EQ::Net::ServertalkServe
|
||||
connection->OnMessage(ServerOP_QueryServGeneric, std::bind(&QueryServConnection::HandleGenericMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
connection->OnMessage(ServerOP_LFGuildUpdate, std::bind(&QueryServConnection::HandleLFGuildUpdateMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
m_streams.insert(std::make_pair(connection->GetUUID(), connection));
|
||||
m_keepalive = std::make_unique<EQ::Timer>(1000, true, std::bind(&QueryServConnection::OnKeepAlive, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void QueryServConnection::RemoveConnection(std::shared_ptr<EQ::Net::ServertalkServerConnection> connection)
|
||||
@ -51,4 +52,10 @@ bool QueryServConnection::SendPacket(ServerPacket* pack)
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void QueryServConnection::OnKeepAlive(EQ::Timer *t)
|
||||
{
|
||||
ServerPacket pack(ServerOP_KeepAlive, 0);
|
||||
SendPacket(&pack);
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "../common/types.h"
|
||||
#include "../common/net/servertalk_server.h"
|
||||
#include "../common/servertalk.h"
|
||||
#include "../common/event/timer.h"
|
||||
|
||||
class QueryServConnection
|
||||
{
|
||||
@ -14,8 +15,10 @@ public:
|
||||
void HandleGenericMessage(uint16_t opcode, EQ::Net::Packet &p);
|
||||
void HandleLFGuildUpdateMessage(uint16_t opcode, EQ::Net::Packet &p);
|
||||
bool SendPacket(ServerPacket* pack);
|
||||
void OnKeepAlive(EQ::Timer *t);
|
||||
private:
|
||||
std::map<std::string, std::shared_ptr<EQ::Net::ServertalkServerConnection>> m_streams;
|
||||
std::unique_ptr<EQ::Timer> m_keepalive;
|
||||
};
|
||||
|
||||
#endif /*QueryServ_H_*/
|
||||
|
||||
@ -312,7 +312,7 @@ void SharedTaskManager::LoadSharedTaskState()
|
||||
|
||||
shared_task_character_data = CharacterDataRepository::GetWhere(
|
||||
*m_database,
|
||||
fmt::format("id IN ({})", fmt::join(character_ids, ","))
|
||||
fmt::format("id IN ({})", Strings::Join(character_ids, ","))
|
||||
);
|
||||
}
|
||||
|
||||
@ -1294,7 +1294,7 @@ std::vector<CharacterTaskTimersRepository::CharacterTaskTimers> SharedTaskManage
|
||||
OR (timer_group > 0 AND timer_type = {} AND timer_group = {}))
|
||||
AND expire_time > NOW() ORDER BY timer_type ASC LIMIT 1
|
||||
),
|
||||
fmt::join(character_ids, ","),
|
||||
Strings::Join(character_ids, ","),
|
||||
task.id,
|
||||
static_cast<int>(TaskTimerType::Replay),
|
||||
task.replay_timer_group,
|
||||
@ -1632,7 +1632,7 @@ void SharedTaskManager::AddReplayTimers(SharedTask *s)
|
||||
s->GetTaskData().id,
|
||||
s->GetTaskData().replay_timer_group,
|
||||
static_cast<int>(TaskTimerType::Replay),
|
||||
fmt::join(s->member_id_history, ",")
|
||||
Strings::Join(s->member_id_history, ",")
|
||||
));
|
||||
|
||||
CharacterTaskTimersRepository::InsertMany(*m_database, task_timers);
|
||||
|
||||
@ -323,7 +323,7 @@ void SharedTaskWorldMessaging::HandleZoneMessage(ServerPacket *pack)
|
||||
}
|
||||
}
|
||||
|
||||
std::string player_list = fmt::format("{}", fmt::join(player_names, ", "));
|
||||
std::string player_list = fmt::format("{}", Strings::Join(player_names, ", "));
|
||||
client_list.SendCharacterMessageID(buf->source_character_id, Chat::Yellow, TaskStr::MEMBERS_PRINT, {player_list});
|
||||
}
|
||||
|
||||
|
||||
@ -43,6 +43,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "../common/shared_tasks.h"
|
||||
#include "shared_task_manager.h"
|
||||
#include "../common/content/world_content_service.h"
|
||||
#include "../common/repositories/player_event_logs_repository.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
|
||||
extern ClientList client_list;
|
||||
extern GroupLFPList LFPGroupList;
|
||||
@ -369,6 +371,30 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
zoneserver_list.SendPacket(pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_PlayerEvent: {
|
||||
auto n = PlayerEvent::PlayerEventContainer{};
|
||||
auto s = (ServerSendPlayerEvent_Struct *) pack->pBuffer;
|
||||
EQ::Util::MemoryStreamReader ss(s->cereal_data, s->cereal_size);
|
||||
cereal::BinaryInputArchive archive(ss);
|
||||
archive(n);
|
||||
|
||||
// by default process events in world
|
||||
// if set, process events in queryserver
|
||||
// if you want to offload event recording to a dedicated QS instance
|
||||
if (!RuleB(Logging, PlayerEventsQSProcess)) {
|
||||
player_event_logs.AddToQueue(n.player_event_log);
|
||||
}
|
||||
else {
|
||||
QSLink.SendPacket(pack);
|
||||
}
|
||||
|
||||
// if discord enabled for event, ship to UCS to process
|
||||
if (player_event_logs.IsEventDiscordEnabled(n.player_event_log.event_type_id)) {
|
||||
UCSLink.SendPacket(pack);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ServerOP_DetailsChange: {
|
||||
if (pack->size != sizeof(ServerRaidGeneralAction_Struct)) {
|
||||
break;
|
||||
@ -1356,6 +1382,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
zoneserver_list.SendPacket(pack);
|
||||
UCSLink.SendPacket(pack);
|
||||
LogSys.LoadLogDatabaseSettings();
|
||||
player_event_logs.ReloadSettings();
|
||||
break;
|
||||
}
|
||||
case ServerOP_ReloadTasks: {
|
||||
|
||||
14
zone/aa.cpp
14
zone/aa.cpp
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
312
zone/client.cpp
312
zone/client.cpp
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
31
zone/exp.cpp
31
zone/exp.cpp
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
284
zone/trading.cpp
284
zone/trading.cpp
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user