mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-22 20:33:01 +00:00
Compare commits
127 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 73f5fcd6d7 | |||
| 407ceac2bb | |||
| fea571db40 | |||
| 1f51a2fceb | |||
| 724f4ddfb8 | |||
| 3d38cd6235 | |||
| 19519908a6 | |||
| 5f044c3d43 | |||
| 8cce781340 | |||
| c9b3facf03 | |||
| d1a16c2ae8 | |||
| b61ee86ff3 | |||
| 97c93ca55a | |||
| fc7c30977a | |||
| b3fd9dd88a | |||
| 4df9661903 | |||
| 8c363320d8 | |||
| d4afc78982 | |||
| 24de1d948a | |||
| ca0e85b4bc | |||
| 5b24d38d1e | |||
| 4a1d026215 | |||
| 5ef8f8c3a8 | |||
| 384de31989 | |||
| 5be3780a54 | |||
| 21e42714eb | |||
| 3474c00e7a | |||
| 3d6b0e5f74 | |||
| 9f619859d1 | |||
| 805a9c5f59 | |||
| 43329dc583 | |||
| 66fee56c47 | |||
| efb2ab57aa | |||
| 0a7d482299 | |||
| de047fb851 | |||
| 9836b5cf67 | |||
| c64591b8f7 | |||
| 3813162bac | |||
| 7099e17c7e | |||
| 2c75e8fcd4 | |||
| e8f01fb6ac | |||
| fd0764d4cb | |||
| 71b2bf6a64 | |||
| 2dcff247c8 | |||
| 93f19d3971 | |||
| bad44f35e2 | |||
| 4a339d49df | |||
| 57d0420399 | |||
| 90def9b882 | |||
| 84156829a7 | |||
| d210b1e5ff | |||
| 086538754e | |||
| 241f900dc4 | |||
| 604256a223 | |||
| 2dffc66c6f | |||
| 1bf24273d2 | |||
| c060280417 | |||
| bc6efd5f74 | |||
| efd6d2f9b1 | |||
| 9dd4cf71f1 | |||
| cfec31457c | |||
| 5ac5beb456 | |||
| 0e51131d67 | |||
| f9a87e26c9 | |||
| 9e16cd8ae8 | |||
| 9644f14746 | |||
| d9f545a5ec | |||
| 1cc32d92cf | |||
| 924e91cf64 | |||
| 9825c61a13 | |||
| 5a0a1b1ffd | |||
| a3bb7e7741 | |||
| a1251bdda8 | |||
| b90082d694 | |||
| a49fa42f35 | |||
| 7064a4156f | |||
| 106cb45b57 | |||
| 9a544650ee | |||
| 032d423add | |||
| 760b30ca0a | |||
| 6b08ca51cc | |||
| 268879b414 | |||
| 4c6dc960e4 | |||
| cc46b54f7f | |||
| 9e3b363d4a | |||
| b0d1dc5f04 | |||
| 158396937a | |||
| fb1467284c | |||
| 14addd4869 | |||
| 0a114fae9a | |||
| 2b224d42ad | |||
| 155ec9ac0d | |||
| 25b4b97c41 | |||
| 839f31b24d | |||
| 20728c31c4 | |||
| c6eb12ac16 | |||
| 34d21d4056 | |||
| d369b47ef4 | |||
| 404f7cada8 | |||
| 823e73336d | |||
| 0da6391be3 | |||
| 0348cb6b8e | |||
| b385a4385f | |||
| 6a9228ed6e | |||
| 8031bf0bcb | |||
| c1584da9cc | |||
| ee6f6f683c | |||
| 60707a14db | |||
| 54050924d8 | |||
| f727c9f75a | |||
| f410c89815 | |||
| 2e575652f6 | |||
| 8e831dce36 | |||
| 040c092795 | |||
| a25952910a | |||
| 66896a3121 | |||
| 4d2418af9d | |||
| 265b32f46f | |||
| 1cde55c535 | |||
| 369b5c2921 | |||
| bcc2e022dc | |||
| 0fef46a6c1 | |||
| b867d40774 | |||
| a489290eba | |||
| 549d731849 | |||
| 68a34565f9 | |||
| c05f951f81 |
@@ -1,3 +1,81 @@
|
||||
## [22.3.0] - 02/06/2023
|
||||
|
||||
### Bots
|
||||
|
||||
* Add GetAugmentIDsBySlotID & AddItem with table ref Methods. ([#2805](https://github.com/EQEmu/Server/pull/2805)) ([Aeadoin](https://github.com/Aeadoin)) 2023-01-29
|
||||
|
||||
### Commands
|
||||
|
||||
* #list now searches without case sensitivity ([#2825](https://github.com/EQEmu/Server/pull/2825)) ([Akkadius](https://github.com/Akkadius)) 2023-02-06
|
||||
* Remove extraneous else from #weather ([#2819](https://github.com/EQEmu/Server/pull/2819)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-01
|
||||
|
||||
### Crash
|
||||
|
||||
* Fix IsUnderwaterOnly crash where npc data references can be stale ([#2830](https://github.com/EQEmu/Server/pull/2830)) ([Akkadius](https://github.com/Akkadius)) 2023-02-06
|
||||
* Fix command crash with #npcedit weapon when second weapon not passed ni ([#2829](https://github.com/EQEmu/Server/pull/2829)) ([Akkadius](https://github.com/Akkadius)) 2023-02-06
|
||||
* Fix crash in bot command botdyearmor ([#2832](https://github.com/EQEmu/Server/pull/2832)) ([Akkadius](https://github.com/Akkadius)) 2023-02-06
|
||||
|
||||
### DB Updates
|
||||
|
||||
* Add Windows MySQL path auto detection for users where the path is not found ([#2836](https://github.com/EQEmu/Server/pull/2836)) ([Akkadius](https://github.com/Akkadius)) 2023-02-06
|
||||
|
||||
### Doors
|
||||
|
||||
* Have NPCs trigger double doors ([#2821](https://github.com/EQEmu/Server/pull/2821)) ([Akkadius](https://github.com/Akkadius)) 2023-02-06
|
||||
* Remove door dev tools spam on client controlled doors ([#2824](https://github.com/EQEmu/Server/pull/2824)) ([Akkadius](https://github.com/Akkadius)) 2023-02-06
|
||||
|
||||
### Feature
|
||||
|
||||
* Add Min/Max Status to Merchants ([#2806](https://github.com/EQEmu/Server/pull/2806)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-01-29
|
||||
|
||||
### Fixes
|
||||
|
||||
* #reload aa will now refresh the AA table properly for every client when changes are made ([#2814](https://github.com/EQEmu/Server/pull/2814)) ([Natedog2012](https://github.com/Natedog2012)) 2023-01-31
|
||||
* #reload static should now properly fill the entity_lists for… ([#2815](https://github.com/EQEmu/Server/pull/2815)) ([Natedog2012](https://github.com/Natedog2012)) 2023-01-31
|
||||
* BuffLevelRestrictions were restricting group buffs if mob targeted ([#2809](https://github.com/EQEmu/Server/pull/2809)) ([noudess](https://github.com/noudess)) 2023-01-29
|
||||
* Fix does_augment_fit_slot method. ([#2817](https://github.com/EQEmu/Server/pull/2817)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-01
|
||||
* Fix NPC ghosting at safe coordinates ([#2823](https://github.com/EQEmu/Server/pull/2823)) ([Akkadius](https://github.com/Akkadius)) 2023-02-06
|
||||
* Fixing % based mob see invis ([#2802](https://github.com/EQEmu/Server/pull/2802)) ([fryguy503](https://github.com/fryguy503)) 2023-01-29
|
||||
* Resolve issue with max buff count being 25 in ROF2. ([#2800](https://github.com/EQEmu/Server/pull/2800)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-01-28
|
||||
|
||||
### Hotfix
|
||||
|
||||
* Post revert build fix for https://github.com/EQEmu/Server/commit/54050924d81d1f83268fe01f9c2b36fe10626601 ([Akkadius](https://github.com/Akkadius)) 2023-02-01
|
||||
|
||||
### Lua
|
||||
|
||||
* Resolve stoi Exception ([#2736](https://github.com/EQEmu/Server/pull/2736)) ([Akkadius](https://github.com/Akkadius)) 2023-02-06
|
||||
|
||||
### Pathing
|
||||
|
||||
* Improvements to handling tight corridors pathing, clipping detection and recovery ([#2826](https://github.com/EQEmu/Server/pull/2826)) ([Akkadius](https://github.com/Akkadius)) 2023-02-06
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add Augment Slot support to does_augment_fit ([#2813](https://github.com/EQEmu/Server/pull/2813)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-01-31
|
||||
* Add EVENT_DAMAGE_GIVEN and EVENT_DAMAGE_TAKEN to Perl/Lua. ([#2804](https://github.com/EQEmu/Server/pull/2804)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-01-29
|
||||
* Add EVENT_ITEM_CLICK_CLIENT and EVENT_ITEM_CLICK_CAST_CLIENT to Perl/Lua. ([#2810](https://github.com/EQEmu/Server/pull/2810)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-01-30
|
||||
* Add EVENT_TASKACCEPTED to Player scope ([#2822](https://github.com/EQEmu/Server/pull/2822)) ([Valorith](https://github.com/Valorith)) 2023-02-06
|
||||
* Add GetItemCooldown to return the time remaining on items… ([#2811](https://github.com/EQEmu/Server/pull/2811)) ([Natedog2012](https://github.com/Natedog2012)) 2023-01-30
|
||||
* Add LDoN Methods to Perl/Lua ([#2799](https://github.com/EQEmu/Server/pull/2799)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-01-29
|
||||
* Add Override Parameters to ScaleNPC() in Perl/Lua. ([#2816](https://github.com/EQEmu/Server/pull/2816)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-01
|
||||
* Add rule AlternateAugmentationSealer for using a different bagtype ([#2831](https://github.com/EQEmu/Server/pull/2831)) ([Natedog2012](https://github.com/Natedog2012)) 2023-02-06
|
||||
* Default ScaleNPC to always scale. ([#2818](https://github.com/EQEmu/Server/pull/2818)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-06
|
||||
|
||||
### Readme
|
||||
|
||||
* Update build badges with Drone ([Akkadius](https://github.com/Akkadius)) 2023-01-29
|
||||
|
||||
### Rules
|
||||
|
||||
* Add rule to ignore name filter on chat channel creation. ([#2820](https://github.com/EQEmu/Server/pull/2820)) ([Valorith](https://github.com/Valorith)) 2023-02-06
|
||||
* Added rule to bypass level based haste caps ([#2835](https://github.com/EQEmu/Server/pull/2835)) ([jcr4990](https://github.com/jcr4990)) 2023-02-06
|
||||
* Fix rule updates that affected bot booting checks ([#2841](https://github.com/EQEmu/Server/pull/2841)) ([Akkadius](https://github.com/Akkadius)) 2023-02-06
|
||||
|
||||
### Tasks
|
||||
|
||||
* Implement alternate currency rewards ([#2827](https://github.com/EQEmu/Server/pull/2827)) ([Akkadius](https://github.com/Akkadius)) 2023-02-06
|
||||
|
||||
## [22.2.0] - 01/27/2023
|
||||
|
||||
### Bots
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# EQEmulator Core Server
|
||||
|Travis CI (Linux)|Appveyor (Windows x86) |Appveyor (Windows x64) |
|
||||
|:---:|:---:|:---:|
|
||||
|[](https://travis-ci.org/EQEmu/Server) |[](https://ci.appveyor.com/project/KimLS/server) |[](https://ci.appveyor.com/project/KimLS/server-87crp) |
|
||||
| Drone (Linux x64) | Drone (Windows x64) |
|
||||
|:---:|:---:|
|
||||
|[](http://drone.akkadius.com/EQEmu/Server) |[](http://drone.akkadius.com/EQEmu/Server) |
|
||||
|
||||
***
|
||||
|
||||
|
||||
+13
-8
@@ -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,
|
||||
|
||||
@@ -476,10 +476,11 @@ bool Database::CheckDatabaseConversions() {
|
||||
CheckDatabaseConvertPPDeblob();
|
||||
CheckDatabaseConvertCorpseDeblob();
|
||||
|
||||
RuleManager::Instance()->LoadRules(this, "default", false);
|
||||
auto *r = RuleManager::Instance();
|
||||
r->LoadRules(this, "default", false);
|
||||
if (!RuleB(Bots, Enabled) && DoesTableExist("bot_data")) {
|
||||
LogInfo("Bot tables found but rule not enabled, enabling");
|
||||
RuleManager::Instance()->SetRule("Bots:Enabled", "true", this, true, true);
|
||||
r->SetRule("Bots:Enabled", "true", this, true, true);
|
||||
}
|
||||
|
||||
/* Run EQEmu Server script (Checks for database updates) */
|
||||
|
||||
@@ -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",
|
||||
|
||||
+92
-12
@@ -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{};
|
||||
+25
-8
@@ -1017,15 +1017,32 @@ 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
|
||||
};
|
||||
|
||||
enum class RecipeCountType : uint8
|
||||
{
|
||||
Component,
|
||||
Container,
|
||||
Fail,
|
||||
Salvage,
|
||||
Success
|
||||
};
|
||||
|
||||
#define ALT_CURRENCY_ID_RADIANT 4
|
||||
#define ALT_CURRENCY_ID_EBON 5
|
||||
|
||||
enum ResurrectionActions
|
||||
{
|
||||
Decline,
|
||||
Accept
|
||||
};
|
||||
|
||||
#endif /*COMMON_EQ_CONSTANTS_H*/
|
||||
|
||||
+12
-10
@@ -29,7 +29,7 @@
|
||||
#include "textures.h"
|
||||
|
||||
|
||||
static const uint32 BUFF_COUNT = 25;
|
||||
static const uint32 BUFF_COUNT = 42;
|
||||
static const uint32 PET_BUFF_COUNT = 30;
|
||||
static const uint32 MAX_MERC = 100;
|
||||
static const uint32 MAX_MERC_GRADES = 10;
|
||||
@@ -3632,17 +3632,19 @@ struct LevelAppearance_Struct { //Sends a little graphic on level up
|
||||
};
|
||||
|
||||
struct MerchantList {
|
||||
uint32 id;
|
||||
uint32 slot;
|
||||
uint32 item;
|
||||
int16 faction_required;
|
||||
int8 level_required;
|
||||
uint16 alt_currency_cost;
|
||||
uint32 classes_required;
|
||||
uint8 probability;
|
||||
uint32 id;
|
||||
uint32 slot;
|
||||
uint32 item;
|
||||
int16 faction_required;
|
||||
int8 level_required;
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
uint16 alt_currency_cost;
|
||||
uint32 classes_required;
|
||||
uint8 probability;
|
||||
std::string bucket_name;
|
||||
std::string bucket_value;
|
||||
uint8 bucket_comparison;
|
||||
uint8 bucket_comparison;
|
||||
};
|
||||
|
||||
struct TempMerchantList {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -358,15 +358,27 @@ int8 EQ::ItemInstance::AvailableAugmentSlot(int32 augment_type) const
|
||||
return (i <= invaug::SOCKET_END) ? i : INVALID_INDEX;
|
||||
}
|
||||
|
||||
bool EQ::ItemInstance::IsAugmentSlotAvailable(int32 augtype, uint8 slot) const
|
||||
bool EQ::ItemInstance::IsAugmentSlotAvailable(int32 augment_type, uint8 slot) const
|
||||
{
|
||||
if (!m_item || !m_item->IsClassCommon())
|
||||
return false;
|
||||
if (!m_item || !m_item->IsClassCommon()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((!GetItem(slot) && m_item->AugSlotVisible[slot]) && augtype == -1 || (m_item->AugSlotType[slot] && ((1 << (m_item->AugSlotType[slot] - 1)) & augtype))) {
|
||||
if (
|
||||
(
|
||||
!GetItem(slot) &&
|
||||
m_item->AugSlotVisible[slot]
|
||||
) &&
|
||||
augment_type == -1 ||
|
||||
(
|
||||
m_item->AugSlotType[slot] &&
|
||||
((1 << (m_item->AugSlotType[slot] - 1)) & augment_type)
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve item inside container
|
||||
|
||||
@@ -101,8 +101,8 @@ namespace EQ
|
||||
//
|
||||
bool IsAugmentable() const;
|
||||
bool AvailableWearSlot(uint32 aug_wear_slots) const;
|
||||
int8 AvailableAugmentSlot(int32 augtype) const;
|
||||
bool IsAugmentSlotAvailable(int32 augtype, uint8 slot) const;
|
||||
int8 AvailableAugmentSlot(int32 augment_type) const;
|
||||
bool IsAugmentSlotAvailable(int32 augment_type, uint8 slot) const;
|
||||
inline int32 GetAugmentType() const { return ((m_item) ? m_item->AugType : 0); }
|
||||
|
||||
inline bool IsExpendable() const { return ((m_item) ? ((m_item->Click.Type == item::ItemEffectExpendable) || (m_item->ItemType == item::ItemTypePotion)) : false); }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+16
-11
@@ -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
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
|
||||
class BaseMerchantlistRepository {
|
||||
public:
|
||||
struct Merchantlist {
|
||||
@@ -24,6 +25,8 @@ public:
|
||||
int32_t item;
|
||||
int16_t faction_required;
|
||||
uint8_t level_required;
|
||||
uint8_t min_status;
|
||||
uint8_t max_status;
|
||||
uint16_t alt_currency_cost;
|
||||
int32_t classes_required;
|
||||
int32_t probability;
|
||||
@@ -49,6 +52,8 @@ public:
|
||||
"item",
|
||||
"faction_required",
|
||||
"level_required",
|
||||
"min_status",
|
||||
"max_status",
|
||||
"alt_currency_cost",
|
||||
"classes_required",
|
||||
"probability",
|
||||
@@ -70,6 +75,8 @@ public:
|
||||
"item",
|
||||
"faction_required",
|
||||
"level_required",
|
||||
"min_status",
|
||||
"max_status",
|
||||
"alt_currency_cost",
|
||||
"classes_required",
|
||||
"probability",
|
||||
@@ -125,6 +132,8 @@ public:
|
||||
e.item = 0;
|
||||
e.faction_required = -100;
|
||||
e.level_required = 0;
|
||||
e.min_status = 0;
|
||||
e.max_status = 255;
|
||||
e.alt_currency_cost = 0;
|
||||
e.classes_required = 65535;
|
||||
e.probability = 100;
|
||||
@@ -160,8 +169,9 @@ public:
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE id = {} LIMIT 1",
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
merchantlist_id
|
||||
)
|
||||
);
|
||||
@@ -175,16 +185,18 @@ public:
|
||||
e.item = static_cast<int32_t>(atoi(row[2]));
|
||||
e.faction_required = static_cast<int16_t>(atoi(row[3]));
|
||||
e.level_required = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
|
||||
e.alt_currency_cost = static_cast<uint16_t>(strtoul(row[5], nullptr, 10));
|
||||
e.classes_required = static_cast<int32_t>(atoi(row[6]));
|
||||
e.probability = static_cast<int32_t>(atoi(row[7]));
|
||||
e.bucket_name = row[8] ? row[8] : "";
|
||||
e.bucket_value = row[9] ? row[9] : "";
|
||||
e.bucket_comparison = static_cast<uint8_t>(strtoul(row[10], nullptr, 10));
|
||||
e.min_expansion = static_cast<int8_t>(atoi(row[11]));
|
||||
e.max_expansion = static_cast<int8_t>(atoi(row[12]));
|
||||
e.content_flags = row[13] ? row[13] : "";
|
||||
e.content_flags_disabled = row[14] ? row[14] : "";
|
||||
e.min_status = static_cast<uint8_t>(strtoul(row[5], nullptr, 10));
|
||||
e.max_status = static_cast<uint8_t>(strtoul(row[6], nullptr, 10));
|
||||
e.alt_currency_cost = static_cast<uint16_t>(strtoul(row[7], nullptr, 10));
|
||||
e.classes_required = static_cast<int32_t>(atoi(row[8]));
|
||||
e.probability = static_cast<int32_t>(atoi(row[9]));
|
||||
e.bucket_name = row[10] ? row[10] : "";
|
||||
e.bucket_value = row[11] ? row[11] : "";
|
||||
e.bucket_comparison = static_cast<uint8_t>(strtoul(row[12], nullptr, 10));
|
||||
e.min_expansion = static_cast<int8_t>(atoi(row[13]));
|
||||
e.max_expansion = static_cast<int8_t>(atoi(row[14]));
|
||||
e.content_flags = row[15] ? row[15] : "";
|
||||
e.content_flags_disabled = row[16] ? row[16] : "";
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -223,16 +235,18 @@ public:
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.item));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.faction_required));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.level_required));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.alt_currency_cost));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.classes_required));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.probability));
|
||||
v.push_back(columns[8] + " = '" + Strings::Escape(e.bucket_name) + "'");
|
||||
v.push_back(columns[9] + " = '" + Strings::Escape(e.bucket_value) + "'");
|
||||
v.push_back(columns[10] + " = " + std::to_string(e.bucket_comparison));
|
||||
v.push_back(columns[11] + " = " + std::to_string(e.min_expansion));
|
||||
v.push_back(columns[12] + " = " + std::to_string(e.max_expansion));
|
||||
v.push_back(columns[13] + " = '" + Strings::Escape(e.content_flags) + "'");
|
||||
v.push_back(columns[14] + " = '" + Strings::Escape(e.content_flags_disabled) + "'");
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.min_status));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.max_status));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.alt_currency_cost));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.classes_required));
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.probability));
|
||||
v.push_back(columns[10] + " = '" + Strings::Escape(e.bucket_name) + "'");
|
||||
v.push_back(columns[11] + " = '" + Strings::Escape(e.bucket_value) + "'");
|
||||
v.push_back(columns[12] + " = " + std::to_string(e.bucket_comparison));
|
||||
v.push_back(columns[13] + " = " + std::to_string(e.min_expansion));
|
||||
v.push_back(columns[14] + " = " + std::to_string(e.max_expansion));
|
||||
v.push_back(columns[15] + " = '" + Strings::Escape(e.content_flags) + "'");
|
||||
v.push_back(columns[16] + " = '" + Strings::Escape(e.content_flags_disabled) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -259,6 +273,8 @@ public:
|
||||
v.push_back(std::to_string(e.item));
|
||||
v.push_back(std::to_string(e.faction_required));
|
||||
v.push_back(std::to_string(e.level_required));
|
||||
v.push_back(std::to_string(e.min_status));
|
||||
v.push_back(std::to_string(e.max_status));
|
||||
v.push_back(std::to_string(e.alt_currency_cost));
|
||||
v.push_back(std::to_string(e.classes_required));
|
||||
v.push_back(std::to_string(e.probability));
|
||||
@@ -303,6 +319,8 @@ public:
|
||||
v.push_back(std::to_string(e.item));
|
||||
v.push_back(std::to_string(e.faction_required));
|
||||
v.push_back(std::to_string(e.level_required));
|
||||
v.push_back(std::to_string(e.min_status));
|
||||
v.push_back(std::to_string(e.max_status));
|
||||
v.push_back(std::to_string(e.alt_currency_cost));
|
||||
v.push_back(std::to_string(e.classes_required));
|
||||
v.push_back(std::to_string(e.probability));
|
||||
@@ -351,16 +369,18 @@ public:
|
||||
e.item = static_cast<int32_t>(atoi(row[2]));
|
||||
e.faction_required = static_cast<int16_t>(atoi(row[3]));
|
||||
e.level_required = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
|
||||
e.alt_currency_cost = static_cast<uint16_t>(strtoul(row[5], nullptr, 10));
|
||||
e.classes_required = static_cast<int32_t>(atoi(row[6]));
|
||||
e.probability = static_cast<int32_t>(atoi(row[7]));
|
||||
e.bucket_name = row[8] ? row[8] : "";
|
||||
e.bucket_value = row[9] ? row[9] : "";
|
||||
e.bucket_comparison = static_cast<uint8_t>(strtoul(row[10], nullptr, 10));
|
||||
e.min_expansion = static_cast<int8_t>(atoi(row[11]));
|
||||
e.max_expansion = static_cast<int8_t>(atoi(row[12]));
|
||||
e.content_flags = row[13] ? row[13] : "";
|
||||
e.content_flags_disabled = row[14] ? row[14] : "";
|
||||
e.min_status = static_cast<uint8_t>(strtoul(row[5], nullptr, 10));
|
||||
e.max_status = static_cast<uint8_t>(strtoul(row[6], nullptr, 10));
|
||||
e.alt_currency_cost = static_cast<uint16_t>(strtoul(row[7], nullptr, 10));
|
||||
e.classes_required = static_cast<int32_t>(atoi(row[8]));
|
||||
e.probability = static_cast<int32_t>(atoi(row[9]));
|
||||
e.bucket_name = row[10] ? row[10] : "";
|
||||
e.bucket_value = row[11] ? row[11] : "";
|
||||
e.bucket_comparison = static_cast<uint8_t>(strtoul(row[12], nullptr, 10));
|
||||
e.min_expansion = static_cast<int8_t>(atoi(row[13]));
|
||||
e.max_expansion = static_cast<int8_t>(atoi(row[14]));
|
||||
e.content_flags = row[15] ? row[15] : "";
|
||||
e.content_flags_disabled = row[16] ? row[16] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -390,16 +410,18 @@ public:
|
||||
e.item = static_cast<int32_t>(atoi(row[2]));
|
||||
e.faction_required = static_cast<int16_t>(atoi(row[3]));
|
||||
e.level_required = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
|
||||
e.alt_currency_cost = static_cast<uint16_t>(strtoul(row[5], nullptr, 10));
|
||||
e.classes_required = static_cast<int32_t>(atoi(row[6]));
|
||||
e.probability = static_cast<int32_t>(atoi(row[7]));
|
||||
e.bucket_name = row[8] ? row[8] : "";
|
||||
e.bucket_value = row[9] ? row[9] : "";
|
||||
e.bucket_comparison = static_cast<uint8_t>(strtoul(row[10], nullptr, 10));
|
||||
e.min_expansion = static_cast<int8_t>(atoi(row[11]));
|
||||
e.max_expansion = static_cast<int8_t>(atoi(row[12]));
|
||||
e.content_flags = row[13] ? row[13] : "";
|
||||
e.content_flags_disabled = row[14] ? row[14] : "";
|
||||
e.min_status = static_cast<uint8_t>(strtoul(row[5], nullptr, 10));
|
||||
e.max_status = static_cast<uint8_t>(strtoul(row[6], nullptr, 10));
|
||||
e.alt_currency_cost = static_cast<uint16_t>(strtoul(row[7], nullptr, 10));
|
||||
e.classes_required = static_cast<int32_t>(atoi(row[8]));
|
||||
e.probability = static_cast<int32_t>(atoi(row[9]));
|
||||
e.bucket_name = row[10] ? row[10] : "";
|
||||
e.bucket_value = row[11] ? row[11] : "";
|
||||
e.bucket_comparison = static_cast<uint8_t>(strtoul(row[12], nullptr, 10));
|
||||
e.min_expansion = static_cast<int8_t>(atoi(row[13]));
|
||||
e.max_expansion = static_cast<int8_t>(atoi(row[14]));
|
||||
e.content_flags = row[15] ? row[15] : "";
|
||||
e.content_flags_disabled = row[16] ? row[16] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
+78
-86
@@ -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
|
||||
@@ -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
|
||||
|
||||
+9
-9
@@ -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
|
||||
+9
-9
@@ -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
|
||||
+10
-1
@@ -385,7 +385,16 @@ void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) {
|
||||
e.rule_value = rule_value;
|
||||
e.notes = rule_notes;
|
||||
|
||||
RuleValuesRepository::UpdateOne(*db, e);
|
||||
db->QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE rule_values SET rule_value = '{}', notes = '{}' WHERE ruleset_id = {} AND rule_name = '{}'",
|
||||
rule_value,
|
||||
Strings::Escape(rule_notes),
|
||||
e.ruleset_id,
|
||||
e.rule_name
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -200,6 +200,7 @@ RULE_INT(Character, ExperiencePercentCapPerKill, -1, "Caps the percentage of exp
|
||||
RULE_BOOL(Character, EnableGroupEXPModifier, true, "Enable or disable the group experience modifier based on number of players in group, default is true")
|
||||
RULE_REAL(Character, GroupMemberEXPModifier, 0.2, "Sets the group experience modifier per members between 2 and 5, default is 0.2")
|
||||
RULE_REAL(Character, FullGroupEXPModifier, 2.16, "Sets the group experience modifier for a full group, default is 2.16")
|
||||
RULE_BOOL(Character, IgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Mercs)
|
||||
@@ -317,6 +318,7 @@ RULE_BOOL(Map, FixPathingZOnSendTo, false, "Try to repair Z coordinates in the S
|
||||
RULE_BOOL(Map, FixZWhenPathing, true, "Automatically fix NPC Z coordinates when moving/pathing/engaged (Far less CPU intensive than its predecessor)")
|
||||
RULE_REAL(Map, DistanceCanTravelBeforeAdjustment, 10.0, "Distance a mob can path before FixZ is called, depends on FixZWhenPathing")
|
||||
RULE_BOOL(Map, MobZVisualDebug, false, "Displays spell effects determining whether or not NPC is hitting Best Z calcs (blue for hit, red for miss)")
|
||||
RULE_BOOL(Map, MobPathingVisualDebug, false, "Displays nodes in pathing points in realtime to help with visual debugging")
|
||||
RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "At runtime in SendTo: maximum change in Z to allow the BestZ code to apply")
|
||||
RULE_INT(Map, FindBestZHeightAdjust, 1, "Adds this to the current Z before seeking the best Z position")
|
||||
RULE_CATEGORY_END()
|
||||
@@ -583,6 +585,7 @@ RULE_INT(Range, SongMessages, 75, "The packet range in which song messages are s
|
||||
RULE_INT(Range, ClientPositionUpdates, 300, "Distance in which the own changed position is communicated to other clients")
|
||||
RULE_INT(Range, CriticalDamage, 80, "The packet range in which critical hit messages are sent")
|
||||
RULE_INT(Range, MobCloseScanDistance, 600, "Close scan distance")
|
||||
RULE_INT(Range, MaxDistanceToClickDoors, 100, "Max distance that a client can click a door from (Client says 'You can't reach that' at roughly 25-50 for most doors)")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Bots)
|
||||
@@ -623,6 +626,7 @@ RULE_BOOL(Chat, EnableVoiceMacros, true, "Enable voice macros")
|
||||
RULE_BOOL(Chat, EnableMailKeyIPVerification, true, "Setting whether the authenticity of the client should be verified via its IP address when accessing the InGame mailbox")
|
||||
RULE_BOOL(Chat, EnableAntiSpam, true, "Enable anti-spam system for chat")
|
||||
RULE_BOOL(Chat, SuppressCommandErrors, false, "Do not suppress command errors by default")
|
||||
RULE_BOOL(Chat, ChannelsIgnoreNameFilter, false, "Ignore name filtering when creating new chat channels")
|
||||
RULE_INT(Chat, MaxPermanentPlayerChannels, 0, "Maximum number of permanent chat channels a player can make. Default 0.")
|
||||
RULE_INT(Chat, MinStatusToBypassAntiSpam, 100, "Minimum status to bypass the anti-spam system")
|
||||
RULE_INT(Chat, MinimumMessagesPerInterval, 4, "Minimum number of chat messages allowed per interval. The karma value is added to this value")
|
||||
@@ -744,6 +748,7 @@ RULE_BOOL(Inventory, DeleteTransformationMold, true, "False if you want mold to
|
||||
RULE_BOOL(Inventory, AllowAnyWeaponTransformation, false, "Weapons can use any weapon transformation")
|
||||
RULE_BOOL(Inventory, TransformSummonedBags, false, "Transforms summoned bags into disenchanted ones instead of deleting")
|
||||
RULE_BOOL(Inventory, AllowMultipleOfSameAugment, false, "Allows multiple of the same augment to be placed in an item via #augmentitem or MQ2, set to true to allow")
|
||||
RULE_INT(Inventory, AlternateAugmentationSealer, 53, "Allows RoF+ clients to augment items from a special container type")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Client)
|
||||
@@ -773,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)
|
||||
|
||||
+20
-12
@@ -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)
|
||||
{
|
||||
@@ -763,3 +777,23 @@ std::string Strings::Random(size_t length)
|
||||
std::generate_n(str.begin(), length, randchar);
|
||||
return str;
|
||||
}
|
||||
|
||||
// a wrapper for stoi which will return a fallback if the string
|
||||
// fails to cast to a number
|
||||
int Strings::ToInt(const std::string &s, int fallback)
|
||||
{
|
||||
return Strings::IsNumber(s) ? std::stoi(s) : fallback;
|
||||
}
|
||||
|
||||
std::string Strings::RemoveNumbers(std::string s)
|
||||
{
|
||||
int current = 0;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (!isdigit(s[i])) {
|
||||
s[current] = s[i];
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
return s.substr(0, current);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,9 @@ class Strings {
|
||||
public:
|
||||
static bool Contains(std::vector<std::string> container, std::string element);
|
||||
static bool Contains(const std::string& subject, const std::string& search);
|
||||
static int ToInt(const std::string &s, int fallback = 0);
|
||||
static bool IsNumber(const std::string &s);
|
||||
static std::string RemoveNumbers(std::string s);
|
||||
static bool IsFloat(const std::string &s);
|
||||
static const std::string ToLower(std::string s);
|
||||
static const std::string ToUpper(std::string s);
|
||||
@@ -106,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>
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@
|
||||
|
||||
// Build variables
|
||||
// these get injected during the build pipeline
|
||||
#define CURRENT_VERSION "22.2.0-dev" // always append -dev to the current version for custom-builds
|
||||
#define CURRENT_VERSION "22.3.0-dev" // always append -dev to the current version for custom-builds
|
||||
#define LOGIN_VERSION "0.8.0"
|
||||
#define COMPILE_DATE __DATE__
|
||||
#define COMPILE_TIME __TIME__
|
||||
@@ -42,7 +42,7 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9218
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9220
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9037
|
||||
|
||||
#endif
|
||||
|
||||
@@ -26,10 +26,11 @@ bool Client::Process()
|
||||
{
|
||||
EQApplicationPacket *app = m_connection->PopPacket();
|
||||
while (app) {
|
||||
auto o = m_connection->GetOpcodeManager();
|
||||
LogPacketClientServer(
|
||||
"[{}] [{:#06x}] Size [{}] {}",
|
||||
OpcodeManager::EmuToName(app->GetOpcode()),
|
||||
m_connection->GetOpcodeManager()->EmuToEQ(app->GetOpcode()),
|
||||
o->EmuToEQ(app->GetOpcode()) == 0 ? app->GetProtocolOpcode() : o->EmuToEQ(app->GetOpcode()),
|
||||
app->Size(),
|
||||
(LogSys.IsLogEnabled(Logs::Detail, Logs::PacketClientServer) ? DumpPacketToString(app) : "")
|
||||
);
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eqemu-server",
|
||||
"version": "22.2.0",
|
||||
"version": "22.3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EQEmu/Server.git"
|
||||
|
||||
+9
-10
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ ChatChannel *ChatChannelList::CreateChannel(
|
||||
{
|
||||
uint8 max_perm_player_channels = RuleI(Chat, MaxPermanentPlayerChannels);
|
||||
|
||||
if (!database.CheckChannelNameFilter(name)) {
|
||||
if (!RuleB(Chat, ChannelsIgnoreNameFilter) && !database.CheckChannelNameFilter(name)) {
|
||||
if (!(owner == SYSTEM_OWNER)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+2
-1
@@ -643,10 +643,11 @@ void Clientlist::Process()
|
||||
while (KeyValid && !(*it)->GetForceDisconnect() && (app = (*it)->ClientStream->PopPacket())) {
|
||||
EmuOpcode opcode = app->GetOpcode();
|
||||
|
||||
auto o = (*it)->ClientStream->GetOpcodeManager();
|
||||
LogPacketClientServer(
|
||||
"[{}] [{:#06x}] Size [{}] {}",
|
||||
OpcodeManager::EmuToName(app->GetOpcode()),
|
||||
(*it)->ClientStream->GetOpcodeManager()->EmuToEQ(app->GetOpcode()),
|
||||
o->EmuToEQ(app->GetOpcode()) == 0 ? app->GetProtocolOpcode() : o->EmuToEQ(app->GetOpcode()),
|
||||
app->Size(),
|
||||
(LogSys.IsLogEnabled(Logs::Detail, Logs::PacketClientServer) ? DumpPacketToString(app) : "")
|
||||
);
|
||||
|
||||
+4
-2
@@ -224,8 +224,10 @@ bool UCSDatabase::GetVariable(const char *varname, char *varvalue, uint16 varval
|
||||
|
||||
|
||||
bool UCSDatabase::LoadChatChannels()
|
||||
{
|
||||
LoadFilteredNamesFromDB();
|
||||
{
|
||||
if (!RuleB(Chat, ChannelsIgnoreNameFilter)) {
|
||||
LoadFilteredNamesFromDB();
|
||||
}
|
||||
LoadReservedNamesFromDB();
|
||||
LogInfo("Loading chat channels from the database");
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "../common/database.h"
|
||||
#include "clientlist.h"
|
||||
#include "chatchannel.h"
|
||||
#include "../common/shareddb.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
+5
-3
@@ -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;
|
||||
|
||||
|
||||
+14
-1
@@ -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: {
|
||||
|
||||
@@ -27,5 +27,5 @@ zip -j eqemu-server-linux-x64.zip ./build/bin/*
|
||||
ls -lsh | grep zip
|
||||
sudo apt-get update && sudo apt-get install -y rclone
|
||||
rclone config create remote ftp env_auth true > /dev/null
|
||||
rclone copy eqemu-server-linux-x64.zip remote:
|
||||
rclone ls remote:
|
||||
rclone copy eqemu-server-linux-x64.zip remote: 2>&1
|
||||
rclone ls remote: 2>&1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module should-release
|
||||
|
||||
go 1.19
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/google/go-github/v41 v41.0.0
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
@@ -59,6 +60,29 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
out, err := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD").Output()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
currentBranch := strings.TrimSpace(string(out))
|
||||
if currentBranch != "master" {
|
||||
fmt.Printf("Not on master, no need to release\n")
|
||||
fmt.Printf("Exiting code 78 to halt pipeline steps gracefully\n")
|
||||
os.Exit(78)
|
||||
}
|
||||
|
||||
if len(os.Getenv("RCLONE_FTP_PASS")) == 0 {
|
||||
fmt.Printf("Missing RCLONE_FTP_PASS no need to deploy\n")
|
||||
fmt.Printf("Exiting code 78 to halt pipeline steps gracefully\n")
|
||||
os.Exit(78)
|
||||
}
|
||||
|
||||
if len(os.Getenv("GH_RELEASE_GITHUB_API_TOKEN")) == 0 {
|
||||
fmt.Printf("Missing GH_RELEASE_GITHUB_API_TOKEN no need to deploy\n")
|
||||
fmt.Printf("Exiting code 78 to halt pipeline steps gracefully\n")
|
||||
os.Exit(78)
|
||||
}
|
||||
|
||||
if len(packageJsonFile) == 0 {
|
||||
fmt.Printf("Could not find package.json\n")
|
||||
os.Exit(1)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -37,8 +37,8 @@ try
|
||||
|
||||
dir *.zip
|
||||
rclone config create remote ftp env_auth true
|
||||
rclone copy eqemu-server-windows-x64.zip remote:
|
||||
rclone ls remote:
|
||||
rclone copy eqemu-server-windows-x64.zip remote: 2>&1
|
||||
rclone ls remote: 2>&1
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -1199,6 +1199,25 @@ sub get_mysql_path
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($path eq "") {
|
||||
my @files;
|
||||
my $start_dir = trim(`echo %programfiles%`);
|
||||
find(
|
||||
sub {
|
||||
if ($#files > 0) {
|
||||
return;
|
||||
}
|
||||
push @files, $File::Find::name unless $File::Find::name!~/mysql.exe/i;
|
||||
},
|
||||
$start_dir
|
||||
);
|
||||
for my $file (@files) {
|
||||
if ($file=~/mysql.exe/i) {
|
||||
$path = $file;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($OS eq "Linux") {
|
||||
$path = `which mysql`;
|
||||
|
||||
@@ -472,6 +472,8 @@
|
||||
9216|2023_01_15_merc_data.sql|SHOW TABLES LIKE 'mercs'|empty|
|
||||
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
|
||||
|
||||
@@ -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`;
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE `merchantlist`
|
||||
ADD COLUMN `min_status` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `level_required`,
|
||||
ADD COLUMN `max_status` tinyint(3) UNSIGNED NOT NULL DEFAULT 255 AFTER `min_status`;
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "../../common/version.h"
|
||||
#include "../../common/json/json.h"
|
||||
#include "../../common/rulesys.h"
|
||||
|
||||
void WorldserverCLI::DatabaseVersion(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||
{
|
||||
@@ -9,13 +10,13 @@ void WorldserverCLI::DatabaseVersion(int argc, char **argv, argh::parser &cmd, s
|
||||
return;
|
||||
}
|
||||
|
||||
Json::Value database_version;
|
||||
Json::Value v;
|
||||
|
||||
database_version["database_version"] = CURRENT_BINARY_DATABASE_VERSION;
|
||||
database_version["bots_database_version"] = CURRENT_BINARY_BOTS_DATABASE_VERSION;
|
||||
v["database_version"] = CURRENT_BINARY_DATABASE_VERSION;
|
||||
v["bots_database_version"] = RuleB(Bots, Enabled) ? CURRENT_BINARY_BOTS_DATABASE_VERSION : 0;
|
||||
|
||||
std::stringstream payload;
|
||||
payload << database_version;
|
||||
payload << v;
|
||||
|
||||
std::cout << payload.str() << std::endl;
|
||||
}
|
||||
|
||||
+4
-11
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+29
-3
@@ -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;
|
||||
}
|
||||
@@ -1010,10 +1014,11 @@ bool Client::HandlePacket(const EQApplicationPacket *app) {
|
||||
|
||||
EmuOpcode opcode = app->GetOpcode();
|
||||
|
||||
auto o = eqs->GetOpcodeManager();
|
||||
LogPacketClientServer(
|
||||
"[{}] [{:#06x}] Size [{}] {}",
|
||||
OpcodeManager::EmuToName(app->GetOpcode()),
|
||||
eqs->GetOpcodeManager()->EmuToEQ(app->GetOpcode()),
|
||||
o->EmuToEQ(app->GetOpcode()) == 0 ? app->GetProtocolOpcode() : o->EmuToEQ(app->GetOpcode()),
|
||||
app->Size(),
|
||||
(LogSys.IsLogEnabled(Logs::Detail, Logs::PacketClientServer) ? DumpPacketToString(app) : "")
|
||||
);
|
||||
@@ -2359,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();
|
||||
|
||||
|
||||
+8
-1
@@ -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});
|
||||
}
|
||||
|
||||
|
||||
@@ -290,16 +290,16 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignore_db) {
|
||||
LogInfo("Checking Database Conversions");
|
||||
database.CheckDatabaseConversions();
|
||||
}
|
||||
|
||||
// logging system init
|
||||
auto logging = LogSys.SetDatabase(&database)
|
||||
->SetLogPath(path.GetLogPath())
|
||||
->LoadLogDatabaseSettings();
|
||||
|
||||
if (!ignore_db) {
|
||||
LogInfo("Checking Database Conversions");
|
||||
database.CheckDatabaseConversions();
|
||||
}
|
||||
|
||||
if (RuleB(Logging, WorldGMSayLogging)) {
|
||||
logging->SetGMSayHandler(&WorldBoot::GMSayHookCallBackProcessWorld);
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
+29
-11
@@ -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) {
|
||||
@@ -53,9 +56,11 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
|
||||
// yep, even these need pet power!
|
||||
int act_power = 0;
|
||||
|
||||
if (IsClient()) {
|
||||
act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);
|
||||
act_power = CastToClient()->mod_pet_power(act_power, spell_id);
|
||||
if (IsOfClientBot()) {
|
||||
act_power = GetFocusEffect(focusPetPower, spell_id);
|
||||
if (IsClient()) {
|
||||
act_power = CastToClient()->mod_pet_power(act_power, spell_id);
|
||||
}
|
||||
}
|
||||
|
||||
PetRecord record;
|
||||
@@ -1178,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,
|
||||
@@ -1223,15 +1239,17 @@ void Client::FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost
|
||||
}
|
||||
}
|
||||
|
||||
const auto export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
cost,
|
||||
rank->id,
|
||||
rank->prev_id,
|
||||
rank->next_id
|
||||
);
|
||||
if (parse->PlayerHasQuestSub(EVENT_AA_BUY)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
cost,
|
||||
rank->id,
|
||||
rank->prev_id,
|
||||
rank->next_id
|
||||
);
|
||||
|
||||
parse->EventPlayer(EVENT_AA_BUY, this, export_string, 0);
|
||||
parse->EventPlayer(EVENT_AA_BUY, this, export_string, 0);
|
||||
}
|
||||
|
||||
CalcBonuses();
|
||||
|
||||
|
||||
+5
-8
@@ -1491,19 +1491,16 @@ bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) {
|
||||
resist_check = ResistSpell(spells[spell_id].resist_type, spell_id, caster, false,0, false, true);
|
||||
|
||||
//2: The mob makes a resistance check against the charm
|
||||
if (resist_check == 100)
|
||||
if (resist_check == 100) {
|
||||
return true;
|
||||
|
||||
else
|
||||
{
|
||||
if (caster->IsClient())
|
||||
{
|
||||
} else {
|
||||
if (caster->IsOfClientBot()) {
|
||||
//3: At maxed ability, Total Domination has a 50% chance of preventing the charm break that otherwise would have occurred.
|
||||
int16 TotalDominationBonus = caster->aabonuses.CharmBreakChance + caster->spellbonuses.CharmBreakChance + caster->itembonuses.CharmBreakChance;
|
||||
|
||||
if (zone->random.Int(0, 99) < TotalDominationBonus)
|
||||
if (zone->random.Int(0, 99) < TotalDominationBonus) {
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+286
-88
@@ -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"
|
||||
@@ -248,8 +249,9 @@ int Mob::compute_defense()
|
||||
{
|
||||
int defense = GetSkill(EQ::skills::SkillDefense) * 400 / 225;
|
||||
defense += (8000 * (GetAGI() - 40)) / 36000;
|
||||
if (IsClient())
|
||||
defense += CastToClient()->GetHeroicAGI() / 10;
|
||||
if (IsOfClientBot()) {
|
||||
defense += GetHeroicAGI() / 10;
|
||||
}
|
||||
|
||||
//516 SE_AC_Mitigation_Max_Percent
|
||||
auto ac_bonus = itembonuses.AC_Mitigation_Max_Percent + aabonuses.AC_Mitigation_Max_Percent + spellbonuses.AC_Mitigation_Max_Percent;
|
||||
@@ -317,8 +319,9 @@ bool Mob::CheckHitChance(Mob* other, DamageHitInfo &hit)
|
||||
Mob *defender = this;
|
||||
Log(Logs::Detail, Logs::Attack, "CheckHitChance(%s) attacked by %s", defender->GetName(), attacker->GetName());
|
||||
|
||||
if (defender->IsClient() && defender->CastToClient()->IsSitting())
|
||||
if (defender->IsOfClientBotMerc() && defender->IsSitting()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto avoidance = defender->GetTotalDefense();
|
||||
if (avoidance == -1) // some sort of auto avoid disc
|
||||
@@ -871,22 +874,23 @@ int Mob::ACSum(bool skip_caps)
|
||||
int ac = 0; // this should be base AC whenever shrouds come around
|
||||
ac += itembonuses.AC; // items + food + tribute
|
||||
int shield_ac = 0;
|
||||
if (HasShieldEquiped() && IsClient()) {
|
||||
auto client = CastToClient();
|
||||
auto inst = client->GetInv().GetItem(EQ::invslot::slotSecondary);
|
||||
if (HasShieldEquiped() && IsOfClientBot()) {
|
||||
auto inst = (IsClient()) ? GetInv().GetItem(EQ::invslot::slotSecondary) : CastToBot()->GetBotItem(EQ::invslot::slotSecondary);
|
||||
if (inst) {
|
||||
if (inst->GetItemRecommendedLevel(true) <= GetLevel())
|
||||
if (inst->GetItemRecommendedLevel(true) <= GetLevel()) {
|
||||
shield_ac = inst->GetItemArmorClass(true);
|
||||
else
|
||||
shield_ac = client->CalcRecommendedLevelBonus(GetLevel(), inst->GetItemRecommendedLevel(true), inst->GetItemArmorClass(true));
|
||||
} else {
|
||||
shield_ac = CalcRecommendedLevelBonus(GetLevel(), inst->GetItemRecommendedLevel(true), inst->GetItemArmorClass(true));
|
||||
}
|
||||
}
|
||||
shield_ac += client->GetHeroicSTR() / 10;
|
||||
shield_ac += GetHeroicSTR() / 10;
|
||||
}
|
||||
// EQ math
|
||||
ac = (ac * 4) / 3;
|
||||
// anti-twink
|
||||
if (!skip_caps && IsClient() && GetLevel() < RuleI(Combat, LevelToStopACTwinkControl))
|
||||
if (!skip_caps && IsOfClientBot() && GetLevel() < RuleI(Combat, LevelToStopACTwinkControl)) {
|
||||
ac = std::min(ac, 25 + 6 * GetLevel());
|
||||
}
|
||||
ac = std::max(0, ac + GetClassRaceACBonus());
|
||||
if (IsNPC()) {
|
||||
// This is the developer tweaked number
|
||||
@@ -915,7 +919,7 @@ int Mob::ACSum(bool skip_caps)
|
||||
if (ac < 0)
|
||||
ac = 0;
|
||||
|
||||
if (!skip_caps && (IsClient() || IsBot())) {
|
||||
if (!skip_caps && IsOfClientBot()) {
|
||||
auto softcap = GetACSoftcap();
|
||||
auto returns = GetSoftcapReturns();
|
||||
int total_aclimitmod = aabonuses.CombatStability + itembonuses.CombatStability + spellbonuses.CombatStability;
|
||||
@@ -1001,8 +1005,9 @@ double Mob::RollD20(int offense, int mitigation)
|
||||
1.6, 1.7, 1.8, 1.9, 2.0
|
||||
};
|
||||
|
||||
if (IsClient() && CastToClient()->IsSitting())
|
||||
if (IsOfClientBotMerc() && IsSitting()) {
|
||||
return mods[19];
|
||||
}
|
||||
|
||||
auto atk_roll = zone->random.Roll0(offense + 5);
|
||||
auto def_roll = zone->random.Roll0(mitigation + 5);
|
||||
@@ -1446,12 +1451,15 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
|
||||
}
|
||||
|
||||
if (IsBot()) {
|
||||
const auto export_string = fmt::format(
|
||||
"{} {}",
|
||||
hit.skill,
|
||||
GetSkill(hit.skill)
|
||||
);
|
||||
parse->EventBot(EVENT_USE_SKILL, CastToBot(), nullptr, export_string, 0);
|
||||
if (parse->BotHasQuestSub(EVENT_USE_SKILL)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {}",
|
||||
hit.skill,
|
||||
GetSkill(hit.skill)
|
||||
);
|
||||
|
||||
parse->EventBot(EVENT_USE_SKILL, CastToBot(), nullptr, export_string, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1730,19 +1738,21 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
|
||||
spell = SPELL_UNKNOWN;
|
||||
}
|
||||
|
||||
auto export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
killerMob ? killerMob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill)
|
||||
);
|
||||
if (parse->PlayerHasQuestSub(EVENT_DEATH)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
killerMob ? killerMob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill)
|
||||
);
|
||||
|
||||
if (parse->EventPlayer(EVENT_DEATH, this, export_string, 0) != 0) {
|
||||
if (GetHP() < 0) {
|
||||
SetHP(0);
|
||||
if (parse->EventPlayer(EVENT_DEATH, this, export_string, 0) != 0) {
|
||||
if (GetHP() < 0) {
|
||||
SetHP(0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (killerMob && (killerMob->IsClient() || killerMob->IsBot()) && (spell != SPELL_UNKNOWN) && damage > 0) {
|
||||
@@ -1810,7 +1820,9 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
|
||||
|
||||
if (killerMob) {
|
||||
if (killerMob->IsNPC()) {
|
||||
parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0);
|
||||
if (parse->HasQuestSub(killerMob->GetNPCTypeID(), EVENT_SLAY)) {
|
||||
parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0);
|
||||
}
|
||||
|
||||
mod_client_death_npc(killerMob);
|
||||
|
||||
@@ -1821,7 +1833,10 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
|
||||
|
||||
killerMob->TrySpellOnKill(killed_level, spell);
|
||||
} else if (killerMob->IsBot()) {
|
||||
parse->EventBot(EVENT_SLAY, killerMob->CastToBot(), this, "", 0);
|
||||
if (parse->BotHasQuestSub(EVENT_SLAY)) {
|
||||
parse->EventBot(EVENT_SLAY, killerMob->CastToBot(), this, "", 0);
|
||||
}
|
||||
|
||||
killerMob->TrySpellOnKill(killed_level, spell);
|
||||
}
|
||||
|
||||
@@ -2060,8 +2075,33 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
|
||||
QServ->PlayerLogEvent(Player_Log_Deaths, CharacterID(), event_desc);
|
||||
}
|
||||
|
||||
std::vector<std::any> args = { new_corpse };
|
||||
parse->EventPlayer(EVENT_DEATH_COMPLETE, this, export_string, 0, &args);
|
||||
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);
|
||||
}
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_DEATH_COMPLETE)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
killerMob ? killerMob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill)
|
||||
);
|
||||
|
||||
std::vector<std::any> args = { new_corpse };
|
||||
|
||||
parse->EventPlayer(EVENT_DEATH_COMPLETE, this, export_string, 0, &args);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//SYNC WITH: tune.cpp, mob.h TuneNPCAttack
|
||||
@@ -2300,8 +2340,11 @@ void NPC::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillTyp
|
||||
//handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds
|
||||
if (attacked_timer.Check())
|
||||
{
|
||||
LogCombat("Triggering EVENT_ATTACK due to attack by [{}]", other ? other->GetName() : "nullptr");
|
||||
parse->EventNPC(EVENT_ATTACK, this, other, "", 0);
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_ATTACK)) {
|
||||
LogCombat("Triggering EVENT_ATTACK due to attack by [{}]", other ? other->GetName() : "nullptr");
|
||||
|
||||
parse->EventNPC(EVENT_ATTACK, this, other, "", 0);
|
||||
}
|
||||
}
|
||||
attacked_timer.Start(CombatEventTimer_expire);
|
||||
|
||||
@@ -2342,29 +2385,41 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
((killer_mob) ? (killer_mob->GetName()) : ("[nullptr]")), damage, spell, attack_skill);
|
||||
|
||||
Mob *oos = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr;
|
||||
auto export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
killer_mob ? killer_mob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill)
|
||||
);
|
||||
|
||||
if (IsNPC()) {
|
||||
if (parse->EventNPC(EVENT_DEATH, this, oos, export_string, 0) != 0) {
|
||||
if (GetHP() < 0) {
|
||||
SetHP(0);
|
||||
}
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
killer_mob ? killer_mob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill)
|
||||
);
|
||||
|
||||
return false;
|
||||
if (parse->EventNPC(EVENT_DEATH, this, oos, export_string, 0) != 0) {
|
||||
if (GetHP() < 0) {
|
||||
SetHP(0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (IsBot()) {
|
||||
if (parse->EventBot(EVENT_DEATH, CastToBot(), oos, export_string, 0) != 0) {
|
||||
if (GetHP() < 0) {
|
||||
SetHP(0);
|
||||
}
|
||||
if (parse->BotHasQuestSub(EVENT_DEATH)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
killer_mob ? killer_mob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill)
|
||||
);
|
||||
if (parse->EventBot(EVENT_DEATH, CastToBot(), oos, export_string, 0) != 0) {
|
||||
if (GetHP() < 0) {
|
||||
SetHP(0);
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2513,7 +2568,11 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
for (int i = 0; i < MAX_RAID_MEMBERS; i++) {
|
||||
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 (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) {
|
||||
parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0);
|
||||
}
|
||||
|
||||
if (RuleB(NPC, EnableMeritBasedFaction))
|
||||
c->SetFactionLevel(c->CharacterID(), GetNPCFactionID(), c->GetBaseClass(), c->GetBaseRace(), c->GetDeity());
|
||||
@@ -2560,7 +2619,12 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
for (int i = 0; i < MAX_GROUP_MEMBERS; i++) {
|
||||
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 (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) {
|
||||
parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0);
|
||||
}
|
||||
|
||||
if (RuleB(NPC, EnableMeritBasedFaction))
|
||||
c->SetFactionLevel(c->CharacterID(), GetNPCFactionID(), c->GetBaseClass(), c->GetBaseRace(), c->GetDeity());
|
||||
@@ -2607,7 +2671,10 @@ 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 (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) {
|
||||
parse->EventNPC(EVENT_KILLED_MERIT, this, give_exp_client, "killed", 0);
|
||||
}
|
||||
|
||||
if (RuleB(NPC, EnableMeritBasedFaction))
|
||||
give_exp_client->SetFactionLevel(give_exp_client->CharacterID(), GetNPCFactionID(), give_exp_client->GetBaseClass(),
|
||||
@@ -2760,7 +2827,10 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
}
|
||||
|
||||
if (oos->IsNPC()) {
|
||||
parse->EventNPC(EVENT_NPC_SLAY, oos->CastToNPC(), this, "", 0);
|
||||
if (parse->HasQuestSub(oos->GetNPCTypeID(), EVENT_NPC_SLAY)) {
|
||||
parse->EventNPC(EVENT_NPC_SLAY, oos->CastToNPC(), this, "", 0);
|
||||
}
|
||||
|
||||
auto emote_id = oos->GetEmoteID();
|
||||
if (emote_id) {
|
||||
oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emote_id);
|
||||
@@ -2771,7 +2841,10 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
}
|
||||
|
||||
if (killer_mob && killer_mob->IsBot()) {
|
||||
parse->EventBot(EVENT_NPC_SLAY, killer_mob->CastToBot(), this, "", 0);
|
||||
if (parse->BotHasQuestSub(EVENT_NPC_SLAY)) {
|
||||
parse->EventBot(EVENT_NPC_SLAY, killer_mob->CastToBot(), this, "", 0);
|
||||
}
|
||||
|
||||
killer_mob->TrySpellOnKill(killed_level, spell);
|
||||
}
|
||||
|
||||
@@ -2783,13 +2856,36 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
|
||||
entity_list.UpdateFindableNPCState(this, true);
|
||||
|
||||
std::vector<std::any> args = { corpse };
|
||||
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, export_string, 0, &args);
|
||||
combat_record.Stop();
|
||||
m_combat_record.Stop();
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH_COMPLETE)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
killer_mob ? killer_mob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill)
|
||||
);
|
||||
|
||||
std::vector<std::any> args = { corpse };
|
||||
|
||||
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, export_string, 0, &args);
|
||||
}
|
||||
|
||||
/* Zone controller process EVENT_DEATH_ZONE (Death events) */
|
||||
args.push_back(this);
|
||||
DispatchZoneControllerEvent(EVENT_DEATH_ZONE, oos, export_string, 0, &args);
|
||||
|
||||
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DEATH_ZONE)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
killer_mob ? killer_mob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill)
|
||||
);
|
||||
|
||||
std::vector<std::any> args = { corpse, this };
|
||||
|
||||
DispatchZoneControllerEvent(EVENT_DEATH_ZONE, oos, export_string, 0, &args);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -2998,8 +3094,12 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
}
|
||||
|
||||
if (!wasengaged) {
|
||||
if (IsNPC() && other->IsClient() && other->CastToClient())
|
||||
parse->EventNPC(EVENT_AGGRO, CastToNPC(), other, "", 0);
|
||||
if (IsNPC() && other->IsClient() && other->CastToClient()) {
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_AGGRO)) {
|
||||
parse->EventNPC(EVENT_AGGRO, CastToNPC(), other, "", 0);
|
||||
}
|
||||
}
|
||||
|
||||
AI_Event_Engaged(other, iYellForHelp);
|
||||
}
|
||||
}
|
||||
@@ -3535,7 +3635,7 @@ bool Mob::HasProcs() const
|
||||
}
|
||||
}
|
||||
|
||||
if (IsClient() || IsBot()) {
|
||||
if (IsOfClientBot()) {
|
||||
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
|
||||
if (aabonuses.SpellProc[i]) {
|
||||
return true;
|
||||
@@ -3553,7 +3653,7 @@ bool Mob::HasDefensiveProcs() const
|
||||
}
|
||||
}
|
||||
|
||||
if (IsClient() || IsBot()) {
|
||||
if (IsOfClientBot()) {
|
||||
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
|
||||
if (aabonuses.DefensiveProc[i]) {
|
||||
return true;
|
||||
@@ -3589,7 +3689,7 @@ bool Mob::HasRangedProcs() const
|
||||
}
|
||||
}
|
||||
|
||||
if (IsClient() || IsBot()) {
|
||||
if (IsOfClientBot()) {
|
||||
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
|
||||
if (aabonuses.RangedProc[i]) {
|
||||
return true;
|
||||
@@ -3685,7 +3785,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
DamageShield(attacker);
|
||||
}
|
||||
|
||||
if (spell_id == SPELL_UNKNOWN && skill_used) {
|
||||
if (spell_id == SPELL_UNKNOWN && skill_used >= EQ::skills::Skill1HBlunt) {
|
||||
CheckNumHitsRemaining(NumHit::IncomingHitAttempts);
|
||||
|
||||
if (attacker)
|
||||
@@ -3820,6 +3920,100 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
//final damage has been determined.
|
||||
SetHP(int64(GetHP() - damage));
|
||||
|
||||
const auto has_bot_given_event = parse->BotHasQuestSub(EVENT_DAMAGE_GIVEN);
|
||||
|
||||
const auto has_bot_taken_event = parse->BotHasQuestSub(EVENT_DAMAGE_TAKEN);
|
||||
|
||||
const auto has_npc_given_event = (
|
||||
(
|
||||
IsNPC() &&
|
||||
parse->HasQuestSub(CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_GIVEN)
|
||||
) ||
|
||||
(
|
||||
attacker &&
|
||||
attacker->IsNPC() &&
|
||||
parse->HasQuestSub(attacker->CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_GIVEN)
|
||||
)
|
||||
);
|
||||
|
||||
const auto has_npc_taken_event = (
|
||||
(
|
||||
IsNPC() &&
|
||||
parse->HasQuestSub(CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_TAKEN)
|
||||
) ||
|
||||
(
|
||||
attacker &&
|
||||
attacker->IsNPC() &&
|
||||
parse->HasQuestSub(attacker->CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_TAKEN)
|
||||
)
|
||||
);
|
||||
|
||||
const auto has_player_given_event = parse->PlayerHasQuestSub(EVENT_DAMAGE_GIVEN);
|
||||
|
||||
const auto has_player_taken_event = parse->PlayerHasQuestSub(EVENT_DAMAGE_TAKEN);
|
||||
|
||||
const auto has_given_event = (
|
||||
has_bot_given_event ||
|
||||
has_npc_given_event ||
|
||||
has_player_given_event
|
||||
);
|
||||
|
||||
const auto has_taken_event = (
|
||||
has_bot_taken_event ||
|
||||
has_npc_taken_event ||
|
||||
has_player_taken_event
|
||||
);
|
||||
|
||||
std::vector<std::any> args;
|
||||
|
||||
if (has_taken_event) {
|
||||
const auto export_string = fmt::format(
|
||||
"{} {} {} {} {} {} {} {} {}",
|
||||
attacker ? attacker->GetID() : 0,
|
||||
damage,
|
||||
spell_id,
|
||||
static_cast<int>(skill_used),
|
||||
FromDamageShield ? 1 : 0,
|
||||
avoidable ? 1 : 0,
|
||||
buffslot,
|
||||
iBuffTic ? 1 : 0,
|
||||
static_cast<int>(special)
|
||||
);
|
||||
|
||||
if (IsBot() && has_bot_taken_event) {
|
||||
parse->EventBot(EVENT_DAMAGE_TAKEN, CastToBot(), attacker ? attacker : nullptr, export_string, 0);
|
||||
} else if (IsClient() && has_player_taken_event) {
|
||||
args.push_back(attacker ? attacker : nullptr);
|
||||
parse->EventPlayer(EVENT_DAMAGE_TAKEN, CastToClient(), export_string, 0, &args);
|
||||
} else if (IsNPC() && has_npc_taken_event) {
|
||||
parse->EventNPC(EVENT_DAMAGE_TAKEN, CastToNPC(), attacker ? attacker : nullptr, export_string, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_given_event && attacker) {
|
||||
const auto export_string = fmt::format(
|
||||
"{} {} {} {} {} {} {} {} {}",
|
||||
GetID(),
|
||||
damage,
|
||||
spell_id,
|
||||
static_cast<int>(skill_used),
|
||||
FromDamageShield ? 1 : 0,
|
||||
avoidable ? 1 : 0,
|
||||
buffslot,
|
||||
iBuffTic ? 1 : 0,
|
||||
static_cast<int>(special)
|
||||
);
|
||||
|
||||
if (attacker->IsBot() && has_bot_given_event) {
|
||||
parse->EventBot(EVENT_DAMAGE_GIVEN, attacker->CastToBot(), this, export_string, 0);
|
||||
} else if (attacker->IsClient() && has_player_given_event) {
|
||||
args.push_back(this);
|
||||
parse->EventPlayer(EVENT_DAMAGE_GIVEN, attacker->CastToClient(), export_string, 0, &args);
|
||||
} else if (attacker->IsNPC() && has_npc_given_event) {
|
||||
parse->EventNPC(EVENT_DAMAGE_GIVEN, attacker->CastToNPC(), this, export_string, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (HasDied()) {
|
||||
bool IsSaved = false;
|
||||
|
||||
@@ -3865,10 +4059,11 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
if (attacker) {
|
||||
if (skill_used == EQ::skills::SkillBash) {
|
||||
can_stun = true;
|
||||
if (attacker->IsClient())
|
||||
if (attacker->IsClient() || attacker->IsBot() || attacker->IsMerc()) {
|
||||
stunbash_chance = attacker->spellbonuses.StunBashChance +
|
||||
attacker->itembonuses.StunBashChance +
|
||||
attacker->aabonuses.StunBashChance;
|
||||
attacker->itembonuses.StunBashChance +
|
||||
attacker->aabonuses.StunBashChance;
|
||||
}
|
||||
}
|
||||
else if (skill_used == EQ::skills::SkillKick &&
|
||||
(attacker->GetLevel() > 55 || attacker->IsNPC()) && GetClass() == WARRIOR) {
|
||||
@@ -3877,7 +4072,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
|
||||
bool is_immune_to_frontal_stun = false;
|
||||
|
||||
if (IsBot() || IsClient() || IsMerc()) {
|
||||
if (IsOfClientBotMerc()) {
|
||||
if (
|
||||
IsPlayerClass(GetClass()) &&
|
||||
RuleI(Combat, FrontalStunImmunityClasses) & GetPlayerClassBit(GetClass())
|
||||
@@ -3998,7 +4193,8 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
a->source = 0;
|
||||
else
|
||||
a->source = attacker->GetID();
|
||||
a->type = SkillDamageTypes[skill_used]; // was 0x1c
|
||||
a->type = (EQ::ValueWithin(skill_used, EQ::skills::Skill1HBlunt, EQ::skills::Skill2HPiercing)) ?
|
||||
SkillDamageTypes[skill_used] : SkillDamageTypes[EQ::skills::SkillHandtoHand]; // was 0x1c
|
||||
a->damage = damage;
|
||||
a->spellid = spell_id;
|
||||
if (special == eSpecialAttacks::AERampage)
|
||||
@@ -4012,10 +4208,11 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
(IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) {
|
||||
a->force = EQ::skills::GetSkillMeleePushForce(skill_used);
|
||||
if (IsNPC()) {
|
||||
if (attacker->IsNPC())
|
||||
if (attacker && attacker->IsNPC()) {
|
||||
a->force = 0.0f; // 2013 change that disabled NPC vs NPC push
|
||||
else
|
||||
} else {
|
||||
a->force *= 0.10f; // force against NPCs is divided by 10 I guess? ex bash is 0.3, parsed 0.03 against an NPC
|
||||
}
|
||||
if (ForcedMovement == 0 && a->force != 0.0f && position_update_melee_push_timer.Check()) {
|
||||
m_Delta.x += a->force * g_Math.FastSin(a->hit_heading);
|
||||
m_Delta.y += a->force * g_Math.FastCos(a->hit_heading);
|
||||
@@ -4358,7 +4555,7 @@ void Mob::TryDefensiveProc(Mob *on, uint16 hand) {
|
||||
}
|
||||
|
||||
//AA Procs
|
||||
if (IsClient() || IsBot()){
|
||||
if (IsOfClientBot()) {
|
||||
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
|
||||
int32 aa_rank_id = aabonuses.DefensiveProc[i + +SBIndex::COMBAT_PROC_ORIGIN_ID];
|
||||
int32 aa_spell_id = aabonuses.DefensiveProc[i + SBIndex::COMBAT_PROC_SPELL_ID];
|
||||
@@ -4616,7 +4813,7 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
|
||||
}
|
||||
|
||||
//AA Melee and Ranged Procs
|
||||
if (IsClient() || IsBot()) {
|
||||
if (IsOfClientBot()) {
|
||||
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
|
||||
|
||||
int32 aa_rank_id = 0;
|
||||
@@ -5066,8 +5263,9 @@ void Mob::ApplyMeleeDamageMods(uint16 skill, int64 &damage, Mob *defender, Extra
|
||||
dmgbonusmod += opts->melee_damage_bonus_flat;
|
||||
|
||||
if (defender) {
|
||||
if (defender->IsClient() && defender->GetClass() == WARRIOR)
|
||||
if (defender->IsOfClientBotMerc() && defender->GetClass() == WARRIOR) {
|
||||
dmgbonusmod -= 5;
|
||||
}
|
||||
// 168 defensive
|
||||
dmgbonusmod += (defender->spellbonuses.MeleeMitigationEffect +
|
||||
defender->itembonuses.MeleeMitigationEffect +
|
||||
@@ -5387,7 +5585,7 @@ void Mob::TrySkillProc(Mob *on, EQ::skills::SkillType skill, uint16 ReuseTime, b
|
||||
}
|
||||
}
|
||||
|
||||
if (IsClient() && aabonuses.LimitToSkill[skill]) {
|
||||
if (IsOfClientBot() && aabonuses.LimitToSkill[skill]) {
|
||||
|
||||
CanProc = true;
|
||||
uint32 effect_id = 0;
|
||||
@@ -5701,16 +5899,16 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
|
||||
TryCriticalHit(defender, hit, opts);
|
||||
|
||||
hit.damage_done += hit.min_damage;
|
||||
if (IsClient()) {
|
||||
if (IsOfClientBot()) {
|
||||
int extra = 0;
|
||||
switch (hit.skill) {
|
||||
case EQ::skills::SkillThrowing:
|
||||
case EQ::skills::SkillArchery:
|
||||
extra = CastToClient()->GetHeroicDEX() / 10;
|
||||
break;
|
||||
default:
|
||||
extra = CastToClient()->GetHeroicSTR() / 10;
|
||||
break;
|
||||
case EQ::skills::SkillThrowing:
|
||||
case EQ::skills::SkillArchery:
|
||||
extra = GetHeroicDEX() / 10;
|
||||
break;
|
||||
default:
|
||||
extra = GetHeroicSTR() / 10;
|
||||
break;
|
||||
}
|
||||
hit.damage_done += extra;
|
||||
}
|
||||
|
||||
+28
-9
@@ -128,7 +128,7 @@ void Client::CalcBonuses()
|
||||
consume_food_timer.SetTimer(timer);
|
||||
}
|
||||
|
||||
int Client::CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat)
|
||||
int Mob::CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat)
|
||||
{
|
||||
if( (reclevel > 0) && (level < reclevel) )
|
||||
{
|
||||
@@ -4009,7 +4009,10 @@ bool Client::CalcItemScale(uint32 slot_x, uint32 slot_y) {
|
||||
if(inst->IsScaling())
|
||||
{
|
||||
uint16 oldexp = inst->GetExp();
|
||||
parse->EventItem(EVENT_SCALE_CALC, this, inst, nullptr, "", 0);
|
||||
|
||||
if (parse->ItemHasQuestSub(inst, EVENT_SCALE_CALC)) {
|
||||
parse->EventItem(EVENT_SCALE_CALC, this, inst, nullptr, "", 0);
|
||||
}
|
||||
|
||||
if (inst->GetExp() != oldexp) { // if the scaling factor changed, rescale the item and update the client
|
||||
inst->ScaleItem();
|
||||
@@ -4028,7 +4031,10 @@ bool Client::CalcItemScale(uint32 slot_x, uint32 slot_y) {
|
||||
if(a_inst->IsScaling())
|
||||
{
|
||||
uint16 oldexp = a_inst->GetExp();
|
||||
parse->EventItem(EVENT_SCALE_CALC, this, a_inst, nullptr, "", 0);
|
||||
|
||||
if (parse->ItemHasQuestSub(a_inst, EVENT_SCALE_CALC)) {
|
||||
parse->EventItem(EVENT_SCALE_CALC, this, a_inst, nullptr, "", 0);
|
||||
}
|
||||
|
||||
if (a_inst->GetExp() != oldexp)
|
||||
{
|
||||
@@ -4096,9 +4102,14 @@ bool Client::DoItemEnterZone(uint32 slot_x, uint32 slot_y) {
|
||||
{
|
||||
uint16 oldexp = inst->GetExp();
|
||||
|
||||
parse->EventItem(EVENT_ITEM_ENTER_ZONE, this, inst, nullptr, "", 0);
|
||||
if (parse->ItemHasQuestSub(inst, EVENT_ITEM_ENTER_ZONE)) {
|
||||
parse->EventItem(EVENT_ITEM_ENTER_ZONE, this, inst, nullptr, "", 0);
|
||||
}
|
||||
|
||||
if (i <= EQ::invslot::EQUIPMENT_END) {
|
||||
parse->EventItem(EVENT_EQUIP_ITEM, this, inst, nullptr, "", i);
|
||||
if (parse->ItemHasQuestSub(inst, EVENT_EQUIP_ITEM)) {
|
||||
parse->EventItem(EVENT_EQUIP_ITEM, this, inst, nullptr, "", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (inst->GetExp() != oldexp) { // if the scaling factor changed, rescale the item and update the client
|
||||
@@ -4108,10 +4119,14 @@ bool Client::DoItemEnterZone(uint32 slot_x, uint32 slot_y) {
|
||||
}
|
||||
} else {
|
||||
if (i <= EQ::invslot::EQUIPMENT_END) {
|
||||
parse->EventItem(EVENT_EQUIP_ITEM, this, inst, nullptr, "", i);
|
||||
if (parse->ItemHasQuestSub(inst, EVENT_EQUIP_ITEM)) {
|
||||
parse->EventItem(EVENT_EQUIP_ITEM, this, inst, nullptr, "", i);
|
||||
}
|
||||
}
|
||||
|
||||
parse->EventItem(EVENT_ITEM_ENTER_ZONE, this, inst, nullptr, "", 0);
|
||||
if (parse->ItemHasQuestSub(inst, EVENT_ITEM_ENTER_ZONE)) {
|
||||
parse->EventItem(EVENT_ITEM_ENTER_ZONE, this, inst, nullptr, "", 0);
|
||||
}
|
||||
}
|
||||
|
||||
//iterate all augments
|
||||
@@ -4125,7 +4140,9 @@ bool Client::DoItemEnterZone(uint32 slot_x, uint32 slot_y) {
|
||||
{
|
||||
uint16 oldexp = a_inst->GetExp();
|
||||
|
||||
parse->EventItem(EVENT_ITEM_ENTER_ZONE, this, a_inst, nullptr, "", 0);
|
||||
if (parse->ItemHasQuestSub(a_inst, EVENT_ITEM_ENTER_ZONE)) {
|
||||
parse->EventItem(EVENT_ITEM_ENTER_ZONE, this, a_inst, nullptr, "", 0);
|
||||
}
|
||||
|
||||
if (a_inst->GetExp() != oldexp)
|
||||
{
|
||||
@@ -4134,7 +4151,9 @@ bool Client::DoItemEnterZone(uint32 slot_x, uint32 slot_y) {
|
||||
update_slot = true;
|
||||
}
|
||||
} else {
|
||||
parse->EventItem(EVENT_ITEM_ENTER_ZONE, this, a_inst, nullptr, "", 0);
|
||||
if (parse->ItemHasQuestSub(a_inst, EVENT_ITEM_ENTER_ZONE)) {
|
||||
parse->EventItem(EVENT_ITEM_ENTER_ZONE, this, a_inst, nullptr, "", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+122
-56
@@ -86,8 +86,6 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm
|
||||
|
||||
m_alt_combat_hate_timer.Start(250);
|
||||
m_auto_defend_timer.Disable();
|
||||
//m_combat_jitter_timer.Disable();
|
||||
//SetCombatJitterFlag(false);
|
||||
SetGuardFlag(false);
|
||||
SetHoldFlag(false);
|
||||
SetAttackFlag(false);
|
||||
@@ -198,8 +196,6 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
|
||||
|
||||
m_alt_combat_hate_timer.Start(250);
|
||||
m_auto_defend_timer.Disable();
|
||||
//m_combat_jitter_timer.Disable();
|
||||
//SetCombatJitterFlag(false);
|
||||
SetGuardFlag(false);
|
||||
SetHoldFlag(false);
|
||||
SetAttackFlag(false);
|
||||
@@ -251,12 +247,13 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
|
||||
|
||||
LoadAAs();
|
||||
|
||||
// copied from client CompleteConnect() handler - watch for problems
|
||||
// (may have to move to post-spawn location if certain buffs still don't process correctly)
|
||||
if (database.botdb.LoadBuffs(this) && bot_owner) {
|
||||
|
||||
if (!database.botdb.LoadBuffs(this)) {
|
||||
if (bot_owner) {
|
||||
bot_owner->Message(Chat::White, "&s for '%s'", BotDatabase::fail::LoadBuffs(), GetCleanName());
|
||||
}
|
||||
} else {
|
||||
//reapply some buffs
|
||||
uint32 buff_count = GetMaxTotalSlots();
|
||||
uint32 buff_count = GetMaxBuffSlots();
|
||||
for (uint32 j1 = 0; j1 < buff_count; j1++) {
|
||||
if (!IsValidSpell(buffs[j1].spellid)) {
|
||||
continue;
|
||||
@@ -326,11 +323,6 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
|
||||
}
|
||||
break;
|
||||
}
|
||||
//case SE_SummonHorse: {
|
||||
// SummonHorse(buffs[j1].spellid);
|
||||
// //hasmount = true; //this was false, is that the correct thing?
|
||||
// break;
|
||||
//}
|
||||
case SE_Silence:
|
||||
{
|
||||
Silence(true);
|
||||
@@ -357,12 +349,8 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
|
||||
{
|
||||
if (!zone->CanLevitate())
|
||||
{
|
||||
//if (!GetGM())
|
||||
//{
|
||||
SendAppearancePacket(AT_Levitate, 0);
|
||||
BuffFadeByEffect(SE_Levitate);
|
||||
//Message(Chat::White, "You can't levitate in this zone.");
|
||||
//}
|
||||
}
|
||||
else {
|
||||
SendAppearancePacket(AT_Levitate, 2);
|
||||
@@ -400,9 +388,6 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
bot_owner->Message(Chat::White, "&s for '%s'", BotDatabase::fail::LoadBuffs(), GetCleanName());
|
||||
}
|
||||
|
||||
CalcBotStats(false);
|
||||
hp_regen = CalcHPRegen();
|
||||
@@ -571,7 +556,7 @@ void Bot::Stand() {
|
||||
SetAppearance(eaStanding);
|
||||
}
|
||||
|
||||
bool Bot::IsSitting() {
|
||||
bool Bot::IsSitting() const {
|
||||
bool result = false;
|
||||
if(GetAppearance() == eaSitting && !IsMoving())
|
||||
result = true;
|
||||
@@ -4758,6 +4743,33 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
|
||||
}
|
||||
}
|
||||
|
||||
for (int m = EQ::invaug::SOCKET_BEGIN; m <= EQ::invaug::SOCKET_END; ++m) {
|
||||
const auto augment = trade_instance->GetAugment(m);
|
||||
if (!augment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CheckLoreConflict(augment->GetItem())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
|
||||
linker.SetItemInst(augment);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
client->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"{} already has {}, the trade has been cancelled!",
|
||||
GetCleanName(),
|
||||
item_link
|
||||
).c_str()
|
||||
);
|
||||
client->ResetTrade();
|
||||
return;
|
||||
}
|
||||
|
||||
if (CheckLoreConflict(trade_instance->GetItem())) {
|
||||
if (trade_event_exists) {
|
||||
event_trade.push_back(ClientTrade(trade_instance, trade_index));
|
||||
@@ -5074,13 +5086,25 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
|
||||
|
||||
BotRemoveEquipItem(return_iterator.from_bot_slot);
|
||||
|
||||
const auto export_string = fmt::format(
|
||||
"{} {}",
|
||||
return_iterator.return_item_instance->IsStackable() ? return_iterator.return_item_instance->GetCharges() : 1,
|
||||
return_iterator.from_bot_slot
|
||||
);
|
||||
if (parse->BotHasQuestSub(EVENT_UNEQUIP_ITEM_BOT)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {}",
|
||||
return_iterator.return_item_instance->IsStackable() ? return_iterator.return_item_instance->GetCharges() : 1,
|
||||
return_iterator.from_bot_slot
|
||||
);
|
||||
|
||||
std::vector<std::any> args = { return_iterator.return_item_instance };
|
||||
|
||||
parse->EventBot(
|
||||
EVENT_UNEQUIP_ITEM_BOT,
|
||||
this,
|
||||
nullptr,
|
||||
export_string,
|
||||
return_iterator.return_item_instance->GetID(),
|
||||
&args
|
||||
);
|
||||
}
|
||||
|
||||
parse->EventBot(EVENT_UNEQUIP_ITEM_BOT, this, nullptr, export_string , return_iterator.return_item_instance->GetID());
|
||||
if (return_instance) {
|
||||
EQ::SayLinkEngine linker;
|
||||
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
|
||||
@@ -5130,13 +5154,24 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
|
||||
m_inv.PutItem(trade_iterator.to_bot_slot, *trade_iterator.trade_item_instance);
|
||||
BotAddEquipItem(trade_iterator.to_bot_slot, (trade_iterator.trade_item_instance ? trade_iterator.trade_item_instance->GetID() : 0));
|
||||
|
||||
const auto export_string = fmt::format(
|
||||
"{} {}",
|
||||
trade_iterator.trade_item_instance->IsStackable() ? trade_iterator.trade_item_instance->GetCharges() : 1,
|
||||
trade_iterator.to_bot_slot
|
||||
);
|
||||
if (parse->BotHasQuestSub(EVENT_EQUIP_ITEM_BOT)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {}",
|
||||
trade_iterator.trade_item_instance->IsStackable() ? trade_iterator.trade_item_instance->GetCharges() : 1,
|
||||
trade_iterator.to_bot_slot
|
||||
);
|
||||
|
||||
parse->EventBot(EVENT_EQUIP_ITEM_BOT, this, nullptr, export_string , trade_iterator.trade_item_instance->GetID());
|
||||
std::vector<std::any> args = { trade_iterator.trade_item_instance };
|
||||
|
||||
parse->EventBot(
|
||||
EVENT_EQUIP_ITEM_BOT,
|
||||
this,
|
||||
nullptr,
|
||||
export_string,
|
||||
trade_iterator.trade_item_instance->GetID(),
|
||||
&args
|
||||
);
|
||||
}
|
||||
|
||||
trade_iterator.trade_item_instance = nullptr; // actual deletion occurs in client delete below
|
||||
|
||||
@@ -5173,8 +5208,11 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
|
||||
std::vector<EQ::ItemInstance*> items(insts, insts + std::size(insts));
|
||||
|
||||
// Check if EVENT_TRADE accepts any items
|
||||
std::vector<std::any> item_list(items.begin(), items.end());
|
||||
parse->EventBot(EVENT_TRADE, this, client, "", 0, &item_list);
|
||||
if (parse->BotHasQuestSub(EVENT_TRADE)) {
|
||||
std::vector<std::any> item_list(items.begin(), items.end());
|
||||
parse->EventBot(EVENT_TRADE, this, client, "", 0, &item_list);
|
||||
}
|
||||
|
||||
CalcBotStats(false);
|
||||
|
||||
} else {
|
||||
@@ -5189,8 +5227,11 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
|
||||
std::vector<EQ::ItemInstance*> items(insts, insts + std::size(insts));
|
||||
|
||||
// Check if EVENT_TRADE accepts any items
|
||||
std::vector<std::any> item_list(items.begin(), items.end());
|
||||
parse->EventBot(EVENT_TRADE, this, client, "", 0, &item_list);
|
||||
if (parse->BotHasQuestSub(EVENT_TRADE)) {
|
||||
std::vector<std::any> item_list(items.begin(), items.end());
|
||||
parse->EventBot(EVENT_TRADE, this, client, "", 0, &item_list);
|
||||
}
|
||||
|
||||
CalcBotStats(false);
|
||||
}
|
||||
}
|
||||
@@ -5280,15 +5321,17 @@ bool Bot::Death(Mob *killerMob, int64 damage, uint16 spell_id, EQ::skills::Skill
|
||||
my_owner->CastToClient()->SetBotPulling(false);
|
||||
}
|
||||
|
||||
const auto export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
killerMob ? killerMob->GetID() : 0,
|
||||
damage,
|
||||
spell_id,
|
||||
static_cast<int>(attack_skill)
|
||||
);
|
||||
if (parse->BotHasQuestSub(EVENT_DEATH_COMPLETE)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
killerMob ? killerMob->GetID() : 0,
|
||||
damage,
|
||||
spell_id,
|
||||
static_cast<int>(attack_skill)
|
||||
);
|
||||
|
||||
parse->EventBot(EVENT_DEATH_COMPLETE, this, killerMob, export_string, 0);
|
||||
parse->EventBot(EVENT_DEATH_COMPLETE, this, killerMob, export_string, 0);
|
||||
}
|
||||
|
||||
entity_list.RemoveBot(GetID());
|
||||
return true;
|
||||
@@ -5299,9 +5342,12 @@ void Bot::Damage(Mob *from, int64 damage, uint16 spell_id, EQ::skills::SkillType
|
||||
spell_id = SPELL_UNKNOWN;
|
||||
|
||||
//handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds
|
||||
if(attacked_timer.Check()) {
|
||||
LogCombat("Triggering EVENT_ATTACK due to attack by [{}]", from->GetName());
|
||||
parse->EventBot(EVENT_ATTACK, this, from, "", 0);
|
||||
if (attacked_timer.Check()) {
|
||||
if (parse->BotHasQuestSub(EVENT_ATTACK)) {
|
||||
LogCombat("Triggering EVENT_ATTACK due to attack by [{}]", from->GetName());
|
||||
|
||||
parse->EventBot(EVENT_ATTACK, this, from, "", 0);
|
||||
}
|
||||
}
|
||||
|
||||
attacked_timer.Start(CombatEventTimer_expire);
|
||||
@@ -6347,7 +6393,17 @@ bool Bot::CastSpell(
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool Bot::SpellOnTarget(uint16 spell_id, Mob* spelltar) {
|
||||
bool Bot::SpellOnTarget(
|
||||
uint16 spell_id,
|
||||
Mob *spelltar,
|
||||
int reflect_effectiveness,
|
||||
bool use_resist_adjust,
|
||||
int16 resist_adjust,
|
||||
bool isproc,
|
||||
int level_override,
|
||||
int duration_override,
|
||||
bool disable_buff_overwrite
|
||||
) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
@@ -8314,7 +8370,11 @@ void EntityList::AddBot(Bot *new_bot, bool send_spawn_packet, bool dont_queue) {
|
||||
new_bot->SetID(GetFreeID());
|
||||
bot_list.push_back(new_bot);
|
||||
mob_list.insert(std::pair<uint16, Mob*>(new_bot->GetID(), new_bot));
|
||||
parse->EventBot(EVENT_SPAWN, new_bot, nullptr, "", 0);
|
||||
|
||||
if (parse->BotHasQuestSub(EVENT_SPAWN)) {
|
||||
parse->EventBot(EVENT_SPAWN, new_bot, nullptr, "", 0);
|
||||
}
|
||||
|
||||
new_bot->SetSpawned();
|
||||
if (send_spawn_packet) {
|
||||
if (dont_queue) {
|
||||
@@ -8332,7 +8392,9 @@ void EntityList::AddBot(Bot *new_bot, bool send_spawn_packet, bool dont_queue) {
|
||||
}
|
||||
}
|
||||
|
||||
new_bot->DispatchZoneControllerEvent(EVENT_SPAWN_ZONE, new_bot, "", 0, nullptr);
|
||||
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_SPAWN_ZONE)) {
|
||||
new_bot->DispatchZoneControllerEvent(EVENT_SPAWN_ZONE, new_bot, "", 0, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9173,14 +9235,18 @@ void Bot::SpawnBotGroupByName(Client* c, std::string botgroup_name, uint32 leade
|
||||
|
||||
void Bot::Signal(int signal_id)
|
||||
{
|
||||
const auto export_string = fmt::format("{}", signal_id);
|
||||
parse->EventBot(EVENT_SIGNAL, this, nullptr, export_string, 0);
|
||||
if (parse->BotHasQuestSub(EVENT_SIGNAL)) {
|
||||
parse->EventBot(EVENT_SIGNAL, this, nullptr, std::to_string(signal_id), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::SendPayload(int payload_id, std::string payload_value)
|
||||
{
|
||||
const auto export_string = fmt::format("{} {}", payload_id, payload_value);
|
||||
parse->EventBot(EVENT_PAYLOAD, this, nullptr, export_string, 0);
|
||||
if (parse->BotHasQuestSub(EVENT_PAYLOAD)) {
|
||||
const auto& export_string = fmt::format("{} {}", payload_id, payload_value);
|
||||
|
||||
parse->EventBot(EVENT_PAYLOAD, this, nullptr, export_string, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::OwnerMessage(std::string message)
|
||||
|
||||
+26
-8
@@ -147,10 +147,10 @@ public:
|
||||
void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1,
|
||||
bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) override;
|
||||
|
||||
bool HasRaid() override { return (GetRaid() ? true : false); }
|
||||
bool HasGroup() override { return (GetGroup() ? true : false); }
|
||||
Raid* GetRaid() override { return entity_list.GetRaidByMob(this); }
|
||||
Group* GetGroup() override { return entity_list.GetGroupByMob(this); }
|
||||
bool HasRaid() final { return (GetRaid() ? true : false); }
|
||||
bool HasGroup() final { return (GetGroup() ? true : false); }
|
||||
Raid* GetRaid() final { return entity_list.GetRaidByMob(this); }
|
||||
Group* GetGroup() final { return entity_list.GetGroupByMob(this); }
|
||||
|
||||
// Common, but informal "interfaces" with Client object
|
||||
uint32 CharacterID() { return GetBotID(); } // Just returns the Bot Id
|
||||
@@ -219,7 +219,7 @@ public:
|
||||
void ChangeBotArcherWeapons(bool isArcher);
|
||||
void Sit();
|
||||
void Stand();
|
||||
bool IsSitting();
|
||||
bool IsSitting() const override;
|
||||
bool IsStanding();
|
||||
int GetWalkspeed() const override { return (int)((float)_GetWalkSpeed() * 1.785714285f); } // 1.25 / 0.7 = 1.7857142857142857142857142857143
|
||||
int GetRunspeed() const override { return (int)((float)_GetRunSpeed() * 1.785714285f); }
|
||||
@@ -354,8 +354,8 @@ public:
|
||||
void AI_Bot_Start(uint32 iMoveDelay = 0);
|
||||
|
||||
// Mob AI Virtual Override Methods
|
||||
void AI_Process() override;
|
||||
void AI_Stop() override;
|
||||
void AI_Process() final;
|
||||
void AI_Stop() final;
|
||||
|
||||
// Mob Spell Virtual Override Methods
|
||||
void SpellProcess() override;
|
||||
@@ -365,7 +365,17 @@ public:
|
||||
void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr) override;
|
||||
virtual bool CastSpell(uint16 spell_id, uint16 target_id, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0,
|
||||
uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr, uint32 aa_id = 0);
|
||||
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar);
|
||||
bool SpellOnTarget(
|
||||
uint16 spell_id,
|
||||
Mob* spelltar,
|
||||
int reflect_effectiveness = 0,
|
||||
bool use_resist_adjust = false,
|
||||
int16 resist_adjust = 0,
|
||||
bool isproc = false,
|
||||
int level_override = -1,
|
||||
int duration_override = 0,
|
||||
bool disable_buff_overwrite = false
|
||||
) final;
|
||||
bool IsImmuneToSpell(uint16 spell_id, Mob *caster) override;
|
||||
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQ::spells::CastingSlot slot);
|
||||
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, int32 casttime = -1, int32 mana_cost = -1,
|
||||
@@ -376,6 +386,11 @@ public:
|
||||
bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr) override
|
||||
{ return Mob::Attack(other, Hand, FromRiposte, IsStrikethrough, IsFromSpell, opts); }
|
||||
|
||||
[[nodiscard]] int GetMaxBuffSlots() const final { return EQ::spells::LONG_BUFFS; }
|
||||
[[nodiscard]] int GetMaxSongSlots() const final { return EQ::spells::SHORT_BUFFS; }
|
||||
[[nodiscard]] int GetMaxDiscSlots() const final { return EQ::spells::DISC_BUFFS; }
|
||||
[[nodiscard]] int GetMaxTotalSlots() const final { return EQ::spells::TOTAL_BUFFS; }
|
||||
|
||||
bool GetBotOwnerDataBuckets();
|
||||
bool GetBotDataBuckets();
|
||||
bool CheckDataBucket(std::string bucket_name, std::string bucket_value, uint8 bucket_comparison);
|
||||
@@ -467,6 +482,9 @@ public:
|
||||
bool IsBotArcher() { return m_bot_archery_setting; }
|
||||
bool IsBotCharmer() { return _botCharmer; }
|
||||
bool IsBot() const override { return true; }
|
||||
bool IsOfClientBot() const override { return true; }
|
||||
bool IsOfClientBotMerc() const override { return true; }
|
||||
|
||||
bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; }
|
||||
BotRoleType GetBotRole() { return _botRole; }
|
||||
EQ::constants::StanceType GetBotStance() { return _botStance; }
|
||||
|
||||
+22
-90
@@ -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
|
||||
*/
|
||||
@@ -5754,7 +5681,7 @@ void bot_subcommand_bot_dye_armor(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
if (helper_is_help_or_usage(sep->arg[1]) || !sep->arg[1] || (sep->arg[1] && !Strings::IsNumber(sep->arg[1]))) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
@@ -9478,13 +9405,17 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep)
|
||||
)
|
||||
);
|
||||
|
||||
const auto export_string = fmt::format(
|
||||
"{} {}",
|
||||
inst->IsStackable() ? inst->GetCharges() : 1,
|
||||
slot_id
|
||||
);
|
||||
if (parse->BotHasQuestSub(EVENT_UNEQUIP_ITEM_BOT)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {}",
|
||||
inst->IsStackable() ? inst->GetCharges() : 1,
|
||||
slot_id
|
||||
);
|
||||
|
||||
parse->EventBot(EVENT_UNEQUIP_ITEM_BOT, my_bot, nullptr, export_string, inst->GetID());
|
||||
std::vector<std::any> args = { inst };
|
||||
|
||||
parse->EventBot(EVENT_UNEQUIP_ITEM_BOT, my_bot, nullptr, export_string, inst->GetID(), &args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10004,17 +9935,18 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas
|
||||
);
|
||||
|
||||
bot_id = my_bot->GetBotID();
|
||||
if (parse->PlayerHasQuestSub(EVENT_BOT_CREATE)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {} {}",
|
||||
bot_name,
|
||||
bot_id,
|
||||
bot_race,
|
||||
bot_class,
|
||||
bot_gender
|
||||
);
|
||||
|
||||
const auto export_string = fmt::format(
|
||||
"{} {} {} {} {}",
|
||||
bot_name,
|
||||
bot_id,
|
||||
bot_race,
|
||||
bot_class,
|
||||
bot_gender
|
||||
);
|
||||
|
||||
parse->EventPlayer(EVENT_BOT_CREATE, bot_owner, export_string, 0);
|
||||
parse->EventPlayer(EVENT_BOT_CREATE, bot_owner, export_string, 0);
|
||||
}
|
||||
|
||||
safe_delete(my_bot);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
+42
-81
@@ -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,20 +40,21 @@ 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(
|
||||
"{} {} {}",
|
||||
position1.x,
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
parse->EventPlayer(EVENT_WARP, m_target, export_string, 0);
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_WARP)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {}",
|
||||
position1.x,
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
|
||||
parse->EventPlayer(EVENT_WARP, m_target, export_string, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MQWarpAbsolute:
|
||||
@@ -65,20 +70,20 @@ 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(
|
||||
"{} {} {}",
|
||||
position1.x,
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
parse->EventPlayer(EVENT_WARP, m_target, export_string, 0);
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_WARP)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {}",
|
||||
position1.x,
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
|
||||
parse->EventPlayer(EVENT_WARP, m_target, export_string, 0);
|
||||
}
|
||||
|
||||
m_time_since_last_warp_detection.Start(2500);
|
||||
}
|
||||
break;
|
||||
@@ -91,12 +96,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 +109,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 +124,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 +139,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 +152,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 +165,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 +174,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 +193,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 +204,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;
|
||||
}
|
||||
|
||||
+448
-99
@@ -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)) {
|
||||
@@ -1185,7 +1199,9 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
|
||||
entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message);
|
||||
}
|
||||
|
||||
parse->EventPlayer(EVENT_SAY, this, message, language);
|
||||
if (parse->PlayerHasQuestSub(EVENT_SAY)) {
|
||||
parse->EventPlayer(EVENT_SAY, this, message, language);
|
||||
}
|
||||
|
||||
if (sender != this) {
|
||||
break;
|
||||
@@ -1206,8 +1222,9 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
|
||||
CheckEmoteHail(t, message);
|
||||
|
||||
if (DistanceNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) {
|
||||
|
||||
parse->EventNPC(EVENT_SAY, t, this, message, language);
|
||||
if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_SAY)) {
|
||||
parse->EventNPC(EVENT_SAY, t, this, message, language);
|
||||
}
|
||||
|
||||
if (RuleB(TaskSystem, EnableTaskSystem)) {
|
||||
if (UpdateTasksOnSpeakWith(t)) {
|
||||
@@ -1216,8 +1233,10 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (DistanceSquaredNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) {
|
||||
parse->EventNPC(EVENT_AGGRO_SAY, t, this, message, language);
|
||||
if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_AGGRO_SAY)) {
|
||||
if (DistanceSquaredNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) {
|
||||
parse->EventNPC(EVENT_AGGRO_SAY, t, this, message, language);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1225,9 +1244,13 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
|
||||
else if (GetTarget() && GetTarget()->IsBot() && !IsInvisible(GetTarget())) {
|
||||
if (DistanceNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) {
|
||||
if (GetTarget()->IsEngaged()) {
|
||||
parse->EventBot(EVENT_AGGRO_SAY, GetTarget()->CastToBot(), this, message, language);
|
||||
if (parse->BotHasQuestSub(EVENT_AGGRO_SAY)) {
|
||||
parse->EventBot(EVENT_AGGRO_SAY, GetTarget()->CastToBot(), this, message, language);
|
||||
}
|
||||
} else {
|
||||
parse->EventBot(EVENT_SAY, GetTarget()->CastToBot(), this, message, language);
|
||||
if (parse->BotHasQuestSub(EVENT_SAY)) {
|
||||
parse->EventBot(EVENT_SAY, GetTarget()->CastToBot(), this, message, language);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2485,22 +2508,35 @@ uint64 Client::GetAllMoney() {
|
||||
}
|
||||
|
||||
bool Client::CheckIncreaseSkill(EQ::skills::SkillType skillid, Mob *against_who, int chancemodi) {
|
||||
if (IsDead() || IsUnconscious())
|
||||
if (IsDead() || IsUnconscious()) {
|
||||
return false;
|
||||
if (IsAIControlled()) // no skillups while chamred =p
|
||||
}
|
||||
|
||||
if (IsAIControlled()) { // no skillups while chamred =p
|
||||
return false;
|
||||
if (against_who != nullptr && against_who->IsCorpse()) // no skillups on corpses
|
||||
}
|
||||
|
||||
if (against_who && against_who->IsCorpse()) { // no skillups on corpses
|
||||
return false;
|
||||
if (skillid > EQ::skills::HIGHEST_SKILL)
|
||||
}
|
||||
|
||||
if (skillid > EQ::skills::HIGHEST_SKILL) {
|
||||
return false;
|
||||
int skillval = GetRawSkill(skillid);
|
||||
int maxskill = GetMaxSkillAfterSpecializationRules(skillid, MaxSkill(skillid));
|
||||
std::string export_string = fmt::format(
|
||||
"{} {}",
|
||||
skillid,
|
||||
skillval
|
||||
);
|
||||
parse->EventPlayer(EVENT_USE_SKILL, this, export_string, 0);
|
||||
}
|
||||
|
||||
auto skillval = GetRawSkill(skillid);
|
||||
auto maxskill = GetMaxSkillAfterSpecializationRules(skillid, MaxSkill(skillid));
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_USE_SKILL)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {}",
|
||||
skillid,
|
||||
skillval
|
||||
);
|
||||
|
||||
parse->EventPlayer(EVENT_USE_SKILL, this, export_string, 0);
|
||||
}
|
||||
|
||||
if (against_who) {
|
||||
if (
|
||||
against_who->GetSpecialAbility(IMMUNE_AGGRO) ||
|
||||
@@ -2534,14 +2570,29 @@ bool Client::CheckIncreaseSkill(EQ::skills::SkillType skillid, Mob *against_who,
|
||||
if(zone->random.Real(0, 99) < Chance)
|
||||
{
|
||||
SetSkill(skillid, GetRawSkill(skillid) + 1);
|
||||
std::string export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
skillid,
|
||||
skillval+1,
|
||||
maxskill,
|
||||
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 = static_cast<int>((skillval + 1)),
|
||||
.max_skill = static_cast<int16>(maxskill),
|
||||
.against_who = (against_who) ? against_who->GetCleanName() : GetCleanName(),
|
||||
};
|
||||
RecordPlayerEventLog(PlayerEvent::SKILL_UP, e);
|
||||
}
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_SKILL_UP)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
skillid,
|
||||
skillval + 1,
|
||||
maxskill,
|
||||
0
|
||||
);
|
||||
|
||||
parse->EventPlayer(EVENT_SKILL_UP, this, export_string, 0);
|
||||
}
|
||||
|
||||
LogSkills("Skill [{}] at value [{}] successfully gain with [{}] chance (mod [{}])", skillid, skillval, Chance, chancemodi);
|
||||
return true;
|
||||
} else {
|
||||
@@ -2569,13 +2620,18 @@ void Client::CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill) {
|
||||
|
||||
if(zone->random.Real(0,100) < Chance) { // if they make the roll
|
||||
IncreaseLanguageSkill(langid); // increase the language skill by 1
|
||||
std::string export_string = fmt::format(
|
||||
"{} {} {}",
|
||||
langid,
|
||||
LangSkill + 1,
|
||||
100
|
||||
);
|
||||
parse->EventPlayer(EVENT_LANGUAGE_SKILL_UP, this, export_string, 0);
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_LANGUAGE_SKILL_UP)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {}",
|
||||
langid,
|
||||
LangSkill + 1,
|
||||
100
|
||||
);
|
||||
|
||||
parse->EventPlayer(EVENT_LANGUAGE_SKILL_UP, this, export_string, 0);
|
||||
}
|
||||
|
||||
LogSkills("Language [{}] at value [{}] successfully gain with [{}] % chance", langid, LangSkill, Chance);
|
||||
}
|
||||
else
|
||||
@@ -2764,35 +2820,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,30 +4071,48 @@ 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;
|
||||
|
||||
parse->EventPlayer(EVENT_DISCOVER_ITEM, this, "", itemid);
|
||||
auto d = DiscoveredItemsRepository::InsertOne(database, e);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::DISCOVER_ITEM)) {
|
||||
const auto* item = database.GetItem(item_id);
|
||||
|
||||
auto e = PlayerEvent::DiscoverItemEvent{
|
||||
.item_id = item_id,
|
||||
.item_name = item->Name,
|
||||
};
|
||||
RecordPlayerEventLog(PlayerEvent::DISCOVER_ITEM, e);
|
||||
|
||||
}
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_DISCOVER_ITEM)) {
|
||||
const auto* item = database.GetItem(item_id);
|
||||
std::vector<std::any> args = {item};
|
||||
|
||||
parse->EventPlayer(EVENT_DISCOVER_ITEM, this, "", item_id, &args);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::UpdateLFP() {
|
||||
@@ -5163,14 +5208,18 @@ void Client::ShowSkillsWindow()
|
||||
|
||||
void Client::Signal(int signal_id)
|
||||
{
|
||||
const auto export_string = fmt::format("{}", signal_id);
|
||||
parse->EventPlayer(EVENT_SIGNAL, this, export_string, 0);
|
||||
if (parse->PlayerHasQuestSub(EVENT_SIGNAL)) {
|
||||
parse->EventPlayer(EVENT_SIGNAL, this, std::to_string(signal_id), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SendPayload(int payload_id, std::string payload_value)
|
||||
{
|
||||
const auto export_string = fmt::format("{} {}", payload_id, payload_value);
|
||||
parse->EventPlayer(EVENT_PAYLOAD, this, export_string, 0);
|
||||
if (parse->PlayerHasQuestSub(EVENT_PAYLOAD)) {
|
||||
const auto& export_string = fmt::format("{} {}", payload_id, payload_value);
|
||||
|
||||
parse->EventPlayer(EVENT_PAYLOAD, this, export_string, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SendRewards()
|
||||
@@ -6864,7 +6913,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 */
|
||||
@@ -6877,12 +6926,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;
|
||||
@@ -6901,6 +6950,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()
|
||||
@@ -8083,7 +8134,10 @@ void Client::TryItemTick(int slot)
|
||||
if (GetLevel() >= zone->tick_items[iid].level && zone->random.Int(0, 100) >= (100 - zone->tick_items[iid].chance) && (zone->tick_items[iid].bagslot || slot <= EQ::invslot::EQUIPMENT_END))
|
||||
{
|
||||
EQ::ItemInstance* e_inst = (EQ::ItemInstance*)inst;
|
||||
parse->EventItem(EVENT_ITEM_TICK, this, e_inst, nullptr, "", slot);
|
||||
|
||||
if (parse->ItemHasQuestSub(e_inst, EVENT_ITEM_TICK)) {
|
||||
parse->EventItem(EVENT_ITEM_TICK, this, e_inst, nullptr, "", slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8101,8 +8155,11 @@ void Client::TryItemTick(int slot)
|
||||
{
|
||||
if( GetLevel() >= zone->tick_items[iid].level && zone->random.Int(0, 100) >= (100 - zone->tick_items[iid].chance) )
|
||||
{
|
||||
EQ::ItemInstance* e_inst = (EQ::ItemInstance*)a_inst;
|
||||
parse->EventItem(EVENT_ITEM_TICK, this, e_inst, nullptr, "", slot);
|
||||
EQ::ItemInstance* e_inst = (EQ::ItemInstance*) a_inst;
|
||||
|
||||
if (parse->ItemHasQuestSub(e_inst, EVENT_ITEM_TICK)) {
|
||||
parse->EventItem(EVENT_ITEM_TICK, this, e_inst, nullptr, "", slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8132,7 +8189,9 @@ void Client::TryItemTimer(int slot)
|
||||
auto it_iter = item_timers.begin();
|
||||
while(it_iter != item_timers.end()) {
|
||||
if(it_iter->second.Check()) {
|
||||
parse->EventItem(EVENT_TIMER, this, inst, nullptr, it_iter->first, 0);
|
||||
if (parse->ItemHasQuestSub(inst, EVENT_TIMER)) {
|
||||
parse->EventItem(EVENT_TIMER, this, inst, nullptr, it_iter->first, 0);
|
||||
}
|
||||
}
|
||||
++it_iter;
|
||||
}
|
||||
@@ -8152,7 +8211,9 @@ void Client::TryItemTimer(int slot)
|
||||
auto it_iter = item_timers.begin();
|
||||
while(it_iter != item_timers.end()) {
|
||||
if(it_iter->second.Check()) {
|
||||
parse->EventItem(EVENT_TIMER, this, a_inst, nullptr, it_iter->first, 0);
|
||||
if (parse->ItemHasQuestSub(a_inst, EVENT_TIMER)) {
|
||||
parse->EventItem(EVENT_TIMER, this, a_inst, nullptr, it_iter->first, 0);
|
||||
}
|
||||
}
|
||||
++it_iter;
|
||||
}
|
||||
@@ -10467,8 +10528,8 @@ void Client::ResetItemCooldown(uint32 item_id)
|
||||
return;
|
||||
}
|
||||
int recast_type = item_d->RecastType;
|
||||
bool found_item = false;
|
||||
|
||||
bool found_item = false;
|
||||
|
||||
static const int16 slots[][2] = {
|
||||
{ EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END },
|
||||
{ EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END },
|
||||
@@ -10484,7 +10545,7 @@ void Client::ResetItemCooldown(uint32 item_id)
|
||||
item = GetInv().GetItem(slot_id);
|
||||
if (item) {
|
||||
item_d = item->GetItem();
|
||||
if (item_d && item->GetID() == item_id || (item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM && item_d->RecastType == recast_type)) {
|
||||
if (item_d && item->GetID() == item_id || (item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM && item_d->RecastType == recast_type)) {
|
||||
item->SetRecastTimestamp(0);
|
||||
DeleteItemRecastTimer(item_d->ID);
|
||||
SendItemPacket(slot_id, item, ItemPacketCharmUpdate);
|
||||
@@ -10498,6 +10559,94 @@ void Client::ResetItemCooldown(uint32 item_id)
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SetItemCooldown(uint32 item_id, bool use_saved_timer, uint32 in_seconds)
|
||||
{
|
||||
EQ::ItemInstance *item = nullptr;
|
||||
const EQ::ItemData* item_d = database.GetItem(item_id);
|
||||
if (!item_d) {
|
||||
return;
|
||||
}
|
||||
int recast_type = item_d->RecastType;
|
||||
auto timestamps = database.GetItemRecastTimestamps(CharacterID());
|
||||
uint32 total_time = 0;
|
||||
uint32 current_time = static_cast<uint32>(std::time(nullptr));
|
||||
uint32 final_time = 0;
|
||||
const auto timer_type = item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM ? item_d->RecastType : item_id;
|
||||
const int timer_id = recast_type != RECAST_TYPE_UNLINKED_ITEM ? (pTimerItemStart + recast_type) : (pTimerNegativeItemReuse * item_id);
|
||||
|
||||
if (use_saved_timer) {
|
||||
if (item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM) {
|
||||
total_time = timestamps.count(item_d->RecastType) ? timestamps.at(item_d->RecastType) : 0;
|
||||
} else {
|
||||
total_time = timestamps.count(item_id) ? timestamps.at(item_id) : 0;
|
||||
}
|
||||
} else {
|
||||
total_time = current_time + in_seconds;
|
||||
}
|
||||
|
||||
if (total_time > current_time) {
|
||||
final_time = total_time - current_time;
|
||||
}
|
||||
|
||||
static const int16 slots[][2] = {
|
||||
{ EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END },
|
||||
{ EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END },
|
||||
{ EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END},
|
||||
{ EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END },
|
||||
{ EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END },
|
||||
{ EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END },
|
||||
{ EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END },
|
||||
};
|
||||
const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]);
|
||||
for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) {
|
||||
for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) {
|
||||
item = GetInv().GetItem(slot_id);
|
||||
if (item) {
|
||||
item_d = item->GetItem();
|
||||
if (item_d && item->GetID() == item_id || (item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM && item_d->RecastType == recast_type)) {
|
||||
item->SetRecastTimestamp(total_time);
|
||||
SendItemPacket(slot_id, item, ItemPacketCharmUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Start timers and update in database only when timer is changed
|
||||
if (!use_saved_timer) {
|
||||
GetPTimers().Clear(&database, timer_id);
|
||||
GetPTimers().Start((timer_id), in_seconds);
|
||||
database.UpdateItemRecast(
|
||||
CharacterID(),
|
||||
timer_type,
|
||||
GetPTimers().Get(timer_id)->GetReadyTimestamp()
|
||||
);
|
||||
}
|
||||
SendItemRecastTimer(recast_type, final_time, true);
|
||||
}
|
||||
|
||||
uint32 Client::GetItemCooldown(uint32 item_id)
|
||||
{
|
||||
const EQ::ItemData* item_d = database.GetItem(item_id);
|
||||
if (!item_d) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int recast_type = item_d->RecastType;
|
||||
auto timestamps = database.GetItemRecastTimestamps(CharacterID());
|
||||
const auto timer_type = recast_type != RECAST_TYPE_UNLINKED_ITEM ? recast_type : item_id;
|
||||
uint32 total_time = 0;
|
||||
uint32 current_time = static_cast<uint32>(std::time(nullptr));
|
||||
uint32 final_time = 0;
|
||||
|
||||
total_time = timestamps.count(timer_type) ? timestamps.at(timer_type) : 0;
|
||||
|
||||
if (total_time > current_time) {
|
||||
final_time = total_time - current_time;
|
||||
}
|
||||
|
||||
return final_time;
|
||||
}
|
||||
|
||||
void Client::RemoveItem(uint32 item_id, uint32 quantity)
|
||||
{
|
||||
EQ::ItemInstance *item = nullptr;
|
||||
@@ -11765,7 +11914,7 @@ void Client::SendPath(Mob* target)
|
||||
target->CastToClient()->Trader ||
|
||||
target->CastToClient()->Buyer
|
||||
)
|
||||
) {
|
||||
) {
|
||||
Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
@@ -11800,7 +11949,8 @@ void Client::SendPath(Mob* target)
|
||||
|
||||
points.push_back(a);
|
||||
points.push_back(b);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
glm::vec3 path_start(
|
||||
GetX(),
|
||||
GetY(),
|
||||
@@ -11813,8 +11963,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) {
|
||||
@@ -11845,7 +11995,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;
|
||||
@@ -11872,3 +12022,202 @@ void Client::SendPath(Mob* target)
|
||||
|
||||
SendPathPacket(points);
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
+15
-8
@@ -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)
|
||||
@@ -277,7 +278,9 @@ public:
|
||||
void KeyRingAdd(uint32 item_id);
|
||||
bool KeyRingCheck(uint32 item_id);
|
||||
void KeyRingList();
|
||||
virtual bool IsClient() const { return true; }
|
||||
bool IsClient() const override { return true; }
|
||||
bool IsOfClientBot() const override { return true; }
|
||||
bool IsOfClientBotMerc() const override { return true; }
|
||||
void CompleteConnect();
|
||||
bool TryStacking(EQ::ItemInstance* item, uint8 type = ItemPacketTrade, bool try_worn = true, bool try_cursor = true);
|
||||
void SendTraderPacket(Client* trader, uint32 Unknown72 = 51);
|
||||
@@ -327,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);
|
||||
@@ -840,9 +842,9 @@ public:
|
||||
|
||||
void Fling(float value, float target_x, float target_y, float target_z, bool ignore_los = false, bool clip_through_walls = false, bool calculate_speed = false);
|
||||
|
||||
inline bool IsStanding() const {return (playeraction == 0);}
|
||||
inline bool IsSitting() const {return (playeraction == 1);}
|
||||
inline bool IsCrouching() const {return (playeraction == 2);}
|
||||
inline bool IsStanding() const { return (playeraction == 0); }
|
||||
inline bool IsSitting() const override { return (playeraction == 1); }
|
||||
inline bool IsCrouching() const { return (playeraction == 2); }
|
||||
inline bool IsBecomeNPC() const { return npcflag; }
|
||||
inline uint8 GetBecomeNPCLevel() const { return npclevel; }
|
||||
inline void SetBecomeNPC(bool flag) { npcflag = flag; }
|
||||
@@ -952,6 +954,7 @@ public:
|
||||
void MemorizeSpell(uint32 slot, uint32 spellid, uint32 scribing, uint32 reduction = 0);
|
||||
|
||||
// Item methods
|
||||
void UseAugmentContainer(int container_slot);
|
||||
void EVENT_ITEM_ScriptStopReturn();
|
||||
uint32 NukeItem(uint32 itemnum, uint8 where_to_check =
|
||||
(invWhereWorn | invWherePersonal | invWhereBank | invWhereSharedBank | invWhereTrading | invWhereCursor));
|
||||
@@ -967,6 +970,8 @@ public:
|
||||
void DeleteItemInInventory(int16 slot_id, int16 quantity = 0, bool client_update = false, bool update_db = true);
|
||||
int CountItem(uint32 item_id);
|
||||
void ResetItemCooldown(uint32 item_id);
|
||||
void SetItemCooldown(uint32 item_id, bool use_saved_timer = false, uint32 in_seconds = 1);
|
||||
uint32 GetItemCooldown(uint32 item_id);
|
||||
void RemoveItem(uint32 item_id, uint32 quantity = 1);
|
||||
bool SwapItem(MoveItem_Struct* move_in);
|
||||
void SwapItemResync(MoveItem_Struct* move_slots);
|
||||
@@ -1482,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;
|
||||
@@ -1674,18 +1679,18 @@ public:
|
||||
|
||||
std::string GetGuildPublicNote();
|
||||
|
||||
PlayerEvent::PlayerEvent GetPlayerEvent();
|
||||
void RecordKilledNPCEvent(NPC *n);
|
||||
protected:
|
||||
friend class Mob;
|
||||
void CalcItemBonuses(StatBonuses* newbon);
|
||||
void AddItemBonuses(const EQ::ItemInstance *inst, StatBonuses* newbon, bool isAug = false, bool isTribute = false, int rec_override = 0, bool ammo_slot_item = false);
|
||||
void AdditiveWornBonuses(const EQ::ItemInstance *inst, StatBonuses* newbon, bool isAug = false);
|
||||
int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat);
|
||||
void CalcEdibleBonuses(StatBonuses* newbon);
|
||||
void ProcessItemCaps();
|
||||
void MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message = true);
|
||||
bool client_data_loaded;
|
||||
|
||||
uint16 GetSympatheticFocusEffect(focusType type, uint16 spell_id);
|
||||
|
||||
void FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost);
|
||||
|
||||
@@ -2081,6 +2086,8 @@ private:
|
||||
bool m_bot_precombat;
|
||||
|
||||
bool CanTradeFVNoDropItem();
|
||||
void SendMobPositions();
|
||||
void PlayerTradeEventLog(Trade *t, Trade *t2);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1031,14 +1031,14 @@ int Client::CalcHaste()
|
||||
h += spellbonuses.hastetype2 > 10 ? 10 : spellbonuses.hastetype2;
|
||||
}
|
||||
// 26+ no cap, 1-25 10
|
||||
if (level > 25) { // 26+
|
||||
if (level > 25 || RuleB(Character, IgnoreLevelBasedHasteCaps)) { // 26+
|
||||
h += itembonuses.haste;
|
||||
}
|
||||
else { // 1-25
|
||||
h += itembonuses.haste > 10 ? 10 : itembonuses.haste;
|
||||
}
|
||||
// 60+ 100, 51-59 85, 1-50 level+25
|
||||
if (level > 59) { // 60+
|
||||
if (level > 59 || RuleB(Character, IgnoreLevelBasedHasteCaps)) { // 60+
|
||||
cap = RuleI(Character, HasteCap);
|
||||
}
|
||||
else if (level > 50) { // 51-59
|
||||
@@ -1052,7 +1052,7 @@ int Client::CalcHaste()
|
||||
h = cap;
|
||||
}
|
||||
// 51+ 25 (despite there being higher spells...), 1-50 10
|
||||
if (level > 50) { // 51+
|
||||
if (level > 50 || RuleB(Character, IgnoreLevelBasedHasteCaps)) { // 51+
|
||||
cap = RuleI(Character, Hastev3Cap);
|
||||
if (spellbonuses.hastetype3 > cap) {
|
||||
h += cap;
|
||||
|
||||
+740
-321
File diff suppressed because it is too large
Load Diff
+48
-29
@@ -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;
|
||||
@@ -183,7 +184,11 @@ bool Client::Process() {
|
||||
|
||||
SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::Offline);
|
||||
|
||||
parse->EventPlayer(EVENT_DISCONNECT, this, "", 0);
|
||||
RecordPlayerEventLog(PlayerEvent::WENT_OFFLINE, PlayerEvent::EmptyEvent{});
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_DISCONNECT)) {
|
||||
parse->EventPlayer(EVENT_DISCONNECT, this, "", 0);
|
||||
}
|
||||
|
||||
return false; //delete client
|
||||
}
|
||||
@@ -542,7 +547,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;
|
||||
}
|
||||
|
||||
@@ -692,7 +697,11 @@ void Client::OnDisconnect(bool hard_disconnect) {
|
||||
if (MyRaid)
|
||||
MyRaid->MemberZoned(this);
|
||||
|
||||
parse->EventPlayer(EVENT_DISCONNECT, this, "", 0);
|
||||
RecordPlayerEventLog(PlayerEvent::WENT_OFFLINE, PlayerEvent::EmptyEvent{});
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_DISCONNECT)) {
|
||||
parse->EventPlayer(EVENT_DISCONNECT, this, "", 0);
|
||||
}
|
||||
|
||||
/* QS: PlayerLogConnectDisconnect */
|
||||
if (RuleB(QueryServ, PlayerLogConnectDisconnect)){
|
||||
@@ -872,6 +881,9 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!EQ::ValueWithin(Admin(), static_cast<int16>(ml.min_status), static_cast<int16>(ml.max_status))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int32 faction_id = npc ? npc->GetPrimaryFaction() : 0;
|
||||
int32 faction_level = (
|
||||
@@ -1152,12 +1164,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;
|
||||
}
|
||||
@@ -1296,10 +1304,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;
|
||||
}
|
||||
|
||||
@@ -1327,11 +1338,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
|
||||
@@ -1383,10 +1396,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)
|
||||
@@ -1426,11 +1442,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
|
||||
@@ -2144,8 +2162,9 @@ void Client::HandleRespawnFromHover(uint32 Option)
|
||||
}
|
||||
|
||||
//After they've respawned into the same zone, trigger EVENT_RESPAWN
|
||||
std::string export_string = fmt::format("{}", Option);
|
||||
parse->EventPlayer(EVENT_RESPAWN, this, export_string, is_rez ? 1 : 0);
|
||||
if (parse->PlayerHasQuestSub(EVENT_RESPAWN)) {
|
||||
parse->EventPlayer(EVENT_RESPAWN, this, std::to_string(Option), is_rez ? 1 : 0);
|
||||
}
|
||||
|
||||
//Pop Rez option from the respawn options list;
|
||||
//easiest way to make sure it stays at the end and
|
||||
|
||||
+39
-16
@@ -4,50 +4,51 @@
|
||||
|
||||
void CombatRecord::Start(std::string in_mob_name)
|
||||
{
|
||||
start_time = std::time(nullptr);
|
||||
end_time = 0;
|
||||
damage_received = 0;
|
||||
heal_received = 0;
|
||||
mob_name = in_mob_name;
|
||||
m_start_time = std::time(nullptr);
|
||||
m_end_time = 0;
|
||||
m_damage_received = 0;
|
||||
m_heal_received = 0;
|
||||
m_mob_name = in_mob_name;
|
||||
}
|
||||
|
||||
|
||||
void CombatRecord::Stop()
|
||||
{
|
||||
end_time = std::time(nullptr);
|
||||
m_end_time = std::time(nullptr);
|
||||
|
||||
double time_in_combat = TimeInCombat();
|
||||
|
||||
LogCombatRecord(
|
||||
"[Summary] Mob [{}] [Received] DPS [{:.0f}] Heal/s [{:.0f}] Duration [{}] ({}s)",
|
||||
mob_name,
|
||||
time_in_combat > 0 ? (damage_received / time_in_combat) : damage_received,
|
||||
time_in_combat > 0 ? (heal_received / time_in_combat) : heal_received,
|
||||
m_mob_name,
|
||||
GetDamageReceivedPerSecond(),
|
||||
GetHealedReceivedPerSecond(),
|
||||
time_in_combat > 0 ? Strings::SecondsToTime(time_in_combat) : "",
|
||||
time_in_combat
|
||||
);
|
||||
}
|
||||
|
||||
bool CombatRecord::InCombat()
|
||||
bool CombatRecord::InCombat() const
|
||||
{
|
||||
return start_time > 0;
|
||||
return m_start_time > 0;
|
||||
}
|
||||
|
||||
void CombatRecord::ProcessHPEvent(int64 hp, int64 current_hp)
|
||||
{
|
||||
// damage
|
||||
if (hp < current_hp) {
|
||||
damage_received = damage_received + std::llabs(current_hp - hp);
|
||||
m_damage_received = m_damage_received + std::llabs(current_hp - hp);
|
||||
}
|
||||
|
||||
// heal
|
||||
if (hp > current_hp && current_hp > 0) {
|
||||
heal_received = heal_received + std::llabs(current_hp - hp);
|
||||
m_heal_received = m_heal_received + std::llabs(current_hp - hp);
|
||||
}
|
||||
|
||||
LogCombatRecordDetail(
|
||||
"damage_received [{}] heal_received [{}] current_hp [{}] hp [{}] calc [{}]",
|
||||
damage_received,
|
||||
heal_received,
|
||||
m_damage_received,
|
||||
m_heal_received,
|
||||
current_hp,
|
||||
hp,
|
||||
std::llabs(current_hp - hp)
|
||||
@@ -56,5 +57,27 @@ void CombatRecord::ProcessHPEvent(int64 hp, int64 current_hp)
|
||||
|
||||
double CombatRecord::TimeInCombat() const
|
||||
{
|
||||
return difftime(end_time, start_time);
|
||||
return m_end_time > m_start_time ? difftime(m_end_time, m_start_time) : 0;
|
||||
}
|
||||
|
||||
float CombatRecord::GetDamageReceivedPerSecond() const
|
||||
{
|
||||
double time_in_combat = TimeInCombat();
|
||||
return time_in_combat > 0 ? (m_damage_received / time_in_combat) : m_damage_received;
|
||||
}
|
||||
|
||||
float CombatRecord::GetHealedReceivedPerSecond() const
|
||||
{
|
||||
double time_in_combat = TimeInCombat();
|
||||
return time_in_combat > 0 ? (m_heal_received / time_in_combat) : m_heal_received;
|
||||
}
|
||||
|
||||
int64 CombatRecord::GetDamageReceived() const
|
||||
{
|
||||
return m_damage_received;
|
||||
}
|
||||
|
||||
int64 CombatRecord::GetHealReceived() const
|
||||
{
|
||||
return m_heal_received;
|
||||
}
|
||||
|
||||
+10
-6
@@ -9,15 +9,19 @@ class CombatRecord {
|
||||
public:
|
||||
void Start(std::string in_mob_name);
|
||||
void Stop();
|
||||
bool InCombat();
|
||||
bool InCombat() const;
|
||||
void ProcessHPEvent(int64 hp, int64 current_hp);
|
||||
double TimeInCombat() const;
|
||||
int64 GetDamageReceived() const;
|
||||
int64 GetHealReceived() const;
|
||||
float GetDamageReceivedPerSecond() const;
|
||||
float GetHealedReceivedPerSecond() const;
|
||||
private:
|
||||
std::string mob_name;
|
||||
time_t start_time = 0;
|
||||
time_t end_time = 0;
|
||||
int64 damage_received = 0;
|
||||
int64 heal_received = 0;
|
||||
std::string m_mob_name;
|
||||
time_t m_start_time = 0;
|
||||
time_t m_end_time = 0;
|
||||
int64 m_damage_received = 0;
|
||||
int64 m_heal_received = 0;
|
||||
};
|
||||
|
||||
#endif //EQEMU_COMBAT_RECORD_H
|
||||
|
||||
+13
-4
@@ -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) {
|
||||
@@ -591,7 +590,18 @@ int command_realdispatch(Client *c, std::string message, bool ignore_status)
|
||||
return -1;
|
||||
}
|
||||
|
||||
parse->EventPlayer(EVENT_GM_COMMAND, c, message, 0);
|
||||
if (parse->PlayerHasQuestSub(EVENT_GM_COMMAND)) {
|
||||
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
|
||||
|
||||
@@ -1035,7 +1045,6 @@ void command_bot(Client *c, const Seperator *sep)
|
||||
#include "gm_commands/listpetition.cpp"
|
||||
#include "gm_commands/lootsim.cpp"
|
||||
#include "gm_commands/loc.cpp"
|
||||
#include "gm_commands/logcommand.cpp"
|
||||
#include "gm_commands/logs.cpp"
|
||||
#include "gm_commands/makepet.cpp"
|
||||
#include "gm_commands/mana.cpp"
|
||||
|
||||
@@ -26,7 +26,6 @@ void command_deinit(void);
|
||||
int command_add(std::string command_name, std::string description, uint8 admin, CmdFuncPtr function);
|
||||
int command_notavail(Client *c, std::string message, bool ignore_status);
|
||||
int command_realdispatch(Client *c, std::string message, bool ignore_status);
|
||||
void command_logcommand(Client *c, std::string message);
|
||||
uint8 GetCommandStatus(Client *c, std::string command_name);
|
||||
void ListModifyNPCStatMap(Client *c);
|
||||
std::map<std::string, std::string> GetModifyNPCStatMap();
|
||||
|
||||
+2
-5
@@ -848,11 +848,6 @@ public:
|
||||
// Add item from cursor slot to trade bucket (automatically does bag data too)
|
||||
void AddEntity(uint16 trade_slot_id, uint32 stack_size);
|
||||
|
||||
// Audit trade
|
||||
void LogTrade();
|
||||
|
||||
void DumpTrade();
|
||||
|
||||
|
||||
public:
|
||||
// Object state
|
||||
@@ -868,6 +863,8 @@ private:
|
||||
|
||||
uint32 with_id;
|
||||
Mob* owner;
|
||||
public:
|
||||
Mob *GetOwner() const;
|
||||
};
|
||||
|
||||
struct ExtraAttackOptions {
|
||||
|
||||
+63
-22
@@ -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>
|
||||
|
||||
|
||||
@@ -1419,28 +1420,53 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app)
|
||||
}
|
||||
}
|
||||
|
||||
std::string export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
inst->GetItem()->ID,
|
||||
inst->GetCharges(),
|
||||
EntityList::RemoveNumbers(corpse_name),
|
||||
GetID()
|
||||
);
|
||||
std::vector<std::any> args;
|
||||
args.push_back(inst);
|
||||
args.push_back(this);
|
||||
bool prevent_loot = false;
|
||||
auto prevent_loot = false;
|
||||
|
||||
if (RuleB(Zone, UseZoneController)) {
|
||||
auto controller = entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID);
|
||||
if (controller){
|
||||
if (parse->EventNPC(EVENT_LOOT_ZONE, controller, client, export_string, 0, &args) != 0) {
|
||||
prevent_loot = true;
|
||||
if (controller) {
|
||||
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_LOOT_ZONE)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
inst->GetItem()->ID,
|
||||
inst->GetCharges(),
|
||||
EntityList::RemoveNumbers(corpse_name),
|
||||
GetID()
|
||||
);
|
||||
|
||||
std::vector<std::any> args = { inst, this };
|
||||
if (parse->EventNPC(EVENT_LOOT_ZONE, controller, client, export_string, 0, &args) != 0) {
|
||||
prevent_loot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parse->EventPlayer(EVENT_LOOT, client, export_string, 0, &args) != 0) {
|
||||
prevent_loot = true;
|
||||
if (parse->PlayerHasQuestSub(EVENT_LOOT)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
inst->GetItem()->ID,
|
||||
inst->GetCharges(),
|
||||
EntityList::RemoveNumbers(corpse_name),
|
||||
GetID()
|
||||
);
|
||||
|
||||
std::vector<std::any> args = { inst, this };
|
||||
if (parse->EventPlayer(EVENT_LOOT, client, export_string, 0, &args) != 0) {
|
||||
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())
|
||||
@@ -1455,9 +1481,19 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app)
|
||||
}
|
||||
}
|
||||
|
||||
// do we want this to have a fail option too? Sure?
|
||||
if (parse->EventItem(EVENT_LOOT, client, inst, this, export_string, 0) != 0) {
|
||||
prevent_loot = true;
|
||||
if (parse->ItemHasQuestSub(inst, EVENT_LOOT)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
inst->GetItem()->ID,
|
||||
inst->GetCharges(),
|
||||
EntityList::RemoveNumbers(corpse_name),
|
||||
GetID()
|
||||
);
|
||||
|
||||
std::vector<std::any> args = { inst, this };
|
||||
if (parse->EventItem(EVENT_LOOT, client, inst, this, export_string, 0, &args) != 0) {
|
||||
prevent_loot = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (prevent_loot) {
|
||||
@@ -1471,9 +1507,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) {
|
||||
|
||||
+5
-1
@@ -121,6 +121,7 @@ Doors::~Doors()
|
||||
bool Doors::Process()
|
||||
{
|
||||
if (m_close_timer.Enabled() && m_close_timer.Check() && IsDoorOpen()) {
|
||||
LogDoorsDetail("door open and timer triggered door_id [{}] open_type [{}]", GetDoorID(), m_open_type);
|
||||
if (m_open_type == 40 || GetTriggerType() == 1) {
|
||||
auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct));
|
||||
MoveDoor_Struct *md = (MoveDoor_Struct *) outapp->pBuffer;
|
||||
@@ -627,11 +628,13 @@ void Doors::ForceOpen(Mob *sender, bool alt_mode)
|
||||
if (!alt_mode) { // original function
|
||||
if (!m_is_open) {
|
||||
if (!m_disable_timer) {
|
||||
LogDoorsDetail("door_id [{}] starting timer", md->doorid);
|
||||
m_close_timer.Start();
|
||||
}
|
||||
m_is_open = true;
|
||||
}
|
||||
else {
|
||||
LogDoorsDetail("door_id [{}] disable timer", md->doorid);
|
||||
m_close_timer.Disable();
|
||||
if (!m_disable_timer) {
|
||||
m_is_open = false;
|
||||
@@ -640,6 +643,7 @@ void Doors::ForceOpen(Mob *sender, bool alt_mode)
|
||||
}
|
||||
else { // alternative function
|
||||
if (!m_disable_timer) {
|
||||
LogDoorsDetail("door_id [{}] alt starting timer", md->doorid);
|
||||
m_close_timer.Start();
|
||||
}
|
||||
m_is_open = true;
|
||||
@@ -840,7 +844,7 @@ void Doors::CreateDatabaseEntry()
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
auto e = DoorsRepository::NewEntity();
|
||||
|
||||
e.id = GetDoorDBID();
|
||||
|
||||
+9
-11
@@ -57,7 +57,7 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
|
||||
value -= (GetLevel() - 40) * 20;
|
||||
|
||||
//This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch.
|
||||
if (spell_id == SPELL_IMP_HARM_TOUCH && (IsClient() || IsBot())) //Improved Harm Touch
|
||||
if (spell_id == SPELL_IMP_HARM_TOUCH && IsOfClientBot()) //Improved Harm Touch
|
||||
value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
|
||||
|
||||
chance = RuleI(Spells, BaseCritChance); //Wizard base critical chance is 2% (Does not scale with level)
|
||||
@@ -65,12 +65,12 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
|
||||
chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation;
|
||||
|
||||
//Crtical Hit Calculation pathway
|
||||
if (chance > 0 || ((IsClient() || IsBot()) && GetClass() == WIZARD && GetLevel() >= RuleI(Spells, WizCritLevel))) {
|
||||
if (chance > 0 || (IsOfClientBot() && GetClass() == WIZARD && GetLevel() >= RuleI(Spells, WizCritLevel))) {
|
||||
|
||||
int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals.
|
||||
|
||||
//Improved Harm Touch is a guaranteed crit if you have at least one level of SCF.
|
||||
if (spell_id == SPELL_IMP_HARM_TOUCH && (IsClient() || IsBot()) && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0))
|
||||
if (spell_id == SPELL_IMP_HARM_TOUCH && IsOfClientBot() && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0))
|
||||
chance = 100;
|
||||
|
||||
if (spells[spell_id].override_crit_chance > 0 && chance > spells[spell_id].override_crit_chance)
|
||||
@@ -82,7 +82,7 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
|
||||
ratio += itembonuses.SpellCritDmgIncNoStack + spellbonuses.SpellCritDmgIncNoStack + aabonuses.SpellCritDmgIncNoStack;
|
||||
}
|
||||
|
||||
else if (((IsClient() || IsBot()) && GetClass() == WIZARD) || (IsMerc() && GetClass() == CASTERDPS)) {
|
||||
else if ((IsOfClientBot() && GetClass() == WIZARD) || (IsMerc() && GetClass() == CASTERDPS)) {
|
||||
if ((GetLevel() >= RuleI(Spells, WizCritLevel)) && zone->random.Roll(RuleI(Spells, WizCritChance))){
|
||||
//Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. (20-70 is parse confirmed)
|
||||
ratio += zone->random.Int(20,70);
|
||||
@@ -90,7 +90,7 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
|
||||
}
|
||||
}
|
||||
|
||||
if ((IsClient() || IsBot()) && GetClass() == WIZARD)
|
||||
if (IsOfClientBot() && GetClass() == WIZARD)
|
||||
ratio += RuleI(Spells, WizCritRatio); //Default is zero
|
||||
|
||||
if (Critical){
|
||||
@@ -1025,7 +1025,7 @@ void EntityList::AESpell(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (spells[spell_id].target_type == ST_AreaClientOnly && !current_mob->IsClient()) {
|
||||
if (spells[spell_id].target_type == ST_AreaClientOnly && !current_mob->IsOfClientBot()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1038,13 +1038,11 @@ void EntityList::AESpell(
|
||||
* 1 = PC
|
||||
* 2 = NPC
|
||||
*/
|
||||
if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsClient() && !current_mob->IsMerc() &&
|
||||
!current_mob->IsBot()) {
|
||||
if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsOfClientBotMerc()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (spells[spell_id].pcnpc_only_flag == 2 &&
|
||||
(current_mob->IsClient() || current_mob->IsMerc() || current_mob->IsBot())) {
|
||||
if (spells[spell_id].pcnpc_only_flag == 2 && current_mob->IsOfClientBotMerc()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1185,7 +1183,7 @@ void EntityList::MassGroupBuff(
|
||||
if (current_mob->IsNPC()) {
|
||||
Mob *owner = current_mob->GetOwner();
|
||||
if (owner) {
|
||||
if (!owner->IsClient()) {
|
||||
if (!owner->IsOfClientBot()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
+152
-5
@@ -161,6 +161,8 @@ const char *QuestEventSubroutines[_LargestEventID] = {
|
||||
"EVENT_TASK_BEFORE_UPDATE",
|
||||
"EVENT_AA_BUY",
|
||||
"EVENT_AA_GAIN",
|
||||
"EVENT_AA_EXP_GAIN",
|
||||
"EVENT_EXP_GAIN",
|
||||
"EVENT_PAYLOAD",
|
||||
"EVENT_LEVEL_DOWN",
|
||||
"EVENT_GM_COMMAND",
|
||||
@@ -171,6 +173,12 @@ const char *QuestEventSubroutines[_LargestEventID] = {
|
||||
"EVENT_AUGMENT_REMOVE_CLIENT",
|
||||
"EVENT_EQUIP_ITEM_BOT",
|
||||
"EVENT_UNEQUIP_ITEM_BOT",
|
||||
"EVENT_DAMAGE_GIVEN",
|
||||
"EVENT_DAMAGE_TAKEN",
|
||||
"EVENT_ITEM_CLICK_CLIENT",
|
||||
"EVENT_ITEM_CLICK_CAST_CLIENT",
|
||||
"EVENT_DESTROY_ITEM_CLIENT",
|
||||
"EVENT_DROP_ITEM_CLIENT",
|
||||
// Add new events before these or Lua crashes
|
||||
"EVENT_SPELL_EFFECT_BOT",
|
||||
"EVENT_SPELL_EFFECT_BUFF_TIC_BOT"
|
||||
@@ -1556,6 +1564,13 @@ void PerlembParser::ExportEventVariables(
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_TARGET_CHANGE: {
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
ExportVar(package_name.c_str(), "target", "Mob", std::any_cast<Mob*>(extra_pointers->at(0)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_WAYPOINT_ARRIVE:
|
||||
case EVENT_WAYPOINT_DEPART: {
|
||||
ExportVar(package_name.c_str(), "wp", data);
|
||||
@@ -1587,6 +1602,7 @@ void PerlembParser::ExportEventVariables(
|
||||
|
||||
case EVENT_NPC_SLAY: {
|
||||
ExportVar(package_name.c_str(), "killed", mob->GetNPCTypeID());
|
||||
ExportVar(package_name.c_str(), "killed_npc", "NPC", mob->CastToNPC());
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1598,6 +1614,9 @@ void PerlembParser::ExportEventVariables(
|
||||
case EVENT_CLICK_DOOR: {
|
||||
ExportVar(package_name.c_str(), "doorid", data);
|
||||
ExportVar(package_name.c_str(), "version", zone->GetInstanceVersion());
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
ExportVar(package_name.c_str(), "door", "Doors", std::any_cast<Doors*>(extra_pointers->at(0)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1606,8 +1625,17 @@ void PerlembParser::ExportEventVariables(
|
||||
Seperator sep(data);
|
||||
ExportVar(package_name.c_str(), "looted_id", sep.arg[0]);
|
||||
ExportVar(package_name.c_str(), "looted_charges", sep.arg[1]);
|
||||
ExportVar(package_name.c_str(), "corpse", sep.arg[2]);
|
||||
ExportVar(package_name.c_str(), "corpse_name", sep.arg[2]);
|
||||
ExportVar(package_name.c_str(), "corpse_id", sep.arg[3]);
|
||||
|
||||
if (extra_pointers && extra_pointers->size() >= 1) {
|
||||
ExportVar(package_name.c_str(), "item", "QuestItem", std::any_cast<EQ::ItemInstance*>(extra_pointers->at(0)));
|
||||
}
|
||||
|
||||
if (extra_pointers && extra_pointers->size() == 2) {
|
||||
ExportVar(package_name.c_str(), "corpse", "Corpse", std::any_cast<Corpse*>(extra_pointers->at(1)));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1663,6 +1691,9 @@ void PerlembParser::ExportEventVariables(
|
||||
case EVENT_PLAYER_PICKUP: {
|
||||
ExportVar(package_name.c_str(), "picked_up_id", data);
|
||||
ExportVar(package_name.c_str(), "picked_up_entity_id", extradata);
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
ExportVar(package_name.c_str(), "item", "QuestItem", std::any_cast<EQ::ItemInstance*>(extra_pointers->at(0)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1711,6 +1742,21 @@ void PerlembParser::ExportEventVariables(
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_ITEM_CLICK_CAST_CLIENT:
|
||||
case EVENT_ITEM_CLICK_CLIENT: {
|
||||
ExportVar(package_name.c_str(), "slot_id", data);
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
auto* item = std::any_cast<EQ::ItemInstance*>(extra_pointers->at(0));
|
||||
if (item) {
|
||||
ExportVar(package_name.c_str(), "item_id", item->GetID());
|
||||
ExportVar(package_name.c_str(), "item_name", item->GetItem()->Name);
|
||||
ExportVar(package_name.c_str(), "spell_id", item->GetItem()->Click.Effect);
|
||||
ExportVar(package_name.c_str(), "item", "QuestItem", item);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_GROUP_CHANGE: {
|
||||
if (mob && mob->IsClient()) {
|
||||
ExportVar(package_name.c_str(), "grouped", mob->IsGrouped());
|
||||
@@ -1721,10 +1767,10 @@ void PerlembParser::ExportEventVariables(
|
||||
|
||||
case EVENT_HATE_LIST: {
|
||||
ExportVar(package_name.c_str(), "hate_state", data);
|
||||
ExportVar(package_name.c_str(), "hate_entity", "Mob", mob);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case EVENT_SPELL_EFFECT_BUFF_TIC_BOT:
|
||||
case EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT:
|
||||
case EVENT_SPELL_EFFECT_BUFF_TIC_NPC:
|
||||
@@ -1751,22 +1797,34 @@ void PerlembParser::ExportEventVariables(
|
||||
|
||||
case EVENT_FORAGE_SUCCESS: {
|
||||
ExportVar(package_name.c_str(), "foraged_item", extradata);
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
ExportVar(package_name.c_str(), "item", "QuestItem", std::any_cast<EQ::ItemInstance*>(extra_pointers->at(0)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_FISH_SUCCESS: {
|
||||
ExportVar(package_name.c_str(), "fished_item", extradata);
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
ExportVar(package_name.c_str(), "item", "QuestItem", std::any_cast<EQ::ItemInstance*>(extra_pointers->at(0)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_CLICK_OBJECT: {
|
||||
ExportVar(package_name.c_str(), "objectid", data);
|
||||
ExportVar(package_name.c_str(), "clicker_id", extradata);
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
ExportVar(package_name.c_str(), "object", "Object", std::any_cast<Object*>(extra_pointers->at(0)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_DISCOVER_ITEM: {
|
||||
ExportVar(package_name.c_str(), "itemid", extradata);
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
ExportVar(package_name.c_str(), "item", "QuestItem", std::any_cast<EQ::ItemInstance*>(extra_pointers->at(0)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1828,10 +1886,24 @@ void PerlembParser::ExportEventVariables(
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_DROP_ITEM_CLIENT: {
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
EQ::ItemInstance* item_instance = std::any_cast<EQ::ItemInstance*>(extra_pointers->at(0));
|
||||
ExportVar(package_name.c_str(), "quantity", item_instance->IsStackable() ? item_instance->GetCharges() : 1);
|
||||
ExportVar(package_name.c_str(), "item_name", item_instance->GetItem()->Name);
|
||||
ExportVar(package_name.c_str(), "item_id", item_instance->GetItem()->ID);
|
||||
ExportVar(package_name.c_str(), "spell_id", item_instance->GetItem()->Click.Effect);
|
||||
ExportVar(package_name.c_str(), "slot_id", extradata);
|
||||
ExportVar(package_name.c_str(), "item", "QuestItem", item_instance);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_SPAWN_ZONE: {
|
||||
ExportVar(package_name.c_str(), "spawned_entity_id", mob->GetID());
|
||||
ExportVar(package_name.c_str(), "spawned_bot_id", mob->IsBot() ? mob->CastToBot()->GetBotID() : 0);
|
||||
ExportVar(package_name.c_str(), "spawned_npc_id", mob->IsNPC() ? mob->GetNPCTypeID() : 0);
|
||||
ExportVar(package_name.c_str(), "spawned", "Mob", mob);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1881,11 +1953,17 @@ void PerlembParser::ExportEventVariables(
|
||||
|
||||
case EVENT_CONSIDER: {
|
||||
ExportVar(package_name.c_str(), "entity_id", std::stoi(data));
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
ExportVar(package_name.c_str(), "target", "Mob", std::any_cast<Mob*>(extra_pointers->at(0)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_CONSIDER_CORPSE: {
|
||||
ExportVar(package_name.c_str(), "corpse_entity_id", std::stoi(data));
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
ExportVar(package_name.c_str(), "corpse", "Corpse", std::any_cast<Corpse*>(extra_pointers->at(0)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1900,6 +1978,9 @@ void PerlembParser::ExportEventVariables(
|
||||
ExportVar(package_name.c_str(), "item_id", extradata);
|
||||
ExportVar(package_name.c_str(), "item_quantity", sep.arg[0]);
|
||||
ExportVar(package_name.c_str(), "slot_id", sep.arg[1]);
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
ExportVar(package_name.c_str(), "item", "QuestItem", std::any_cast<EQ::ItemInstance*>(extra_pointers->at(0)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1909,19 +1990,46 @@ void PerlembParser::ExportEventVariables(
|
||||
ExportVar(package_name.c_str(), "item_id", extradata);
|
||||
ExportVar(package_name.c_str(), "item_quantity", sep.arg[0]);
|
||||
ExportVar(package_name.c_str(), "slot_id", sep.arg[1]);
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
ExportVar(package_name.c_str(), "item", "QuestItem", std::any_cast<EQ::ItemInstance*>(extra_pointers->at(0)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_AUGMENT_INSERT_CLIENT: {
|
||||
Seperator sep(data);
|
||||
ExportVar(package_name.c_str(), "item_id", sep.arg[0]);
|
||||
ExportVar(package_name.c_str(), "item_slot", sep.arg[1]);
|
||||
ExportVar(package_name.c_str(), "augment_id", sep.arg[2]);
|
||||
ExportVar(package_name.c_str(), "augment_slot", sep.arg[3]);
|
||||
|
||||
if (extra_pointers && extra_pointers->size() >= 1) {
|
||||
ExportVar(package_name.c_str(), "item", "QuestItem", std::any_cast<EQ::ItemInstance*>(extra_pointers->at(0)));
|
||||
}
|
||||
|
||||
if (extra_pointers && extra_pointers->size() >= 2) {
|
||||
ExportVar(package_name.c_str(), "augment", "QuestItem", std::any_cast<EQ::ItemInstance*>(extra_pointers->at(1)));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_AUGMENT_INSERT_CLIENT:
|
||||
case EVENT_AUGMENT_REMOVE_CLIENT: {
|
||||
Seperator sep(data);
|
||||
ExportVar(package_name.c_str(), "item_id", sep.arg[0]);
|
||||
ExportVar(package_name.c_str(), "item_slot", sep.arg[1]);
|
||||
ExportVar(package_name.c_str(), "augment_id", sep.arg[2]);
|
||||
ExportVar(package_name.c_str(), "augment_slot", sep.arg[3]);
|
||||
if (sep.argnum >= 4) {
|
||||
ExportVar(package_name.c_str(), "destroyed", sep.arg[4]);
|
||||
ExportVar(package_name.c_str(), "destroyed", sep.arg[4]);
|
||||
|
||||
if (extra_pointers && extra_pointers->size() >= 1) {
|
||||
ExportVar(package_name.c_str(), "item", "QuestItem", std::any_cast<EQ::ItemInstance*>(extra_pointers->at(0)));
|
||||
}
|
||||
|
||||
if (extra_pointers && extra_pointers->size() >= 3) {
|
||||
ExportVar(package_name.c_str(), "augment", "QuestItem", std::any_cast<EQ::ItemInstance*>(extra_pointers->at(2)));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1978,8 +2086,21 @@ void PerlembParser::ExportEventVariables(
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_AA_EXP_GAIN: {
|
||||
ExportVar(package_name.c_str(), "aa_exp_gained", data);
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_EXP_GAIN: {
|
||||
ExportVar(package_name.c_str(), "exp_gained", data);
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_INSPECT: {
|
||||
ExportVar(package_name.c_str(), "target_id", extradata);
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
ExportVar(package_name.c_str(), "target", "Mob", std::any_cast<Mob*>(extra_pointers->at(0)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2031,6 +2152,32 @@ void PerlembParser::ExportEventVariables(
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_DAMAGE_GIVEN:
|
||||
case EVENT_DAMAGE_TAKEN:{
|
||||
Seperator sep(data);
|
||||
ExportVar(package_name.c_str(), "entity_id", sep.arg[0]);
|
||||
ExportVar(package_name.c_str(), "damage", sep.arg[1]);
|
||||
ExportVar(package_name.c_str(), "spell_id", sep.arg[2]);
|
||||
ExportVar(package_name.c_str(), "skill_id", sep.arg[3]);
|
||||
ExportVar(package_name.c_str(), "is_damage_shield", sep.arg[4]);
|
||||
ExportVar(package_name.c_str(), "is_avoidable", sep.arg[5]);
|
||||
ExportVar(package_name.c_str(), "buff_slot", sep.arg[6]);
|
||||
ExportVar(package_name.c_str(), "is_buff_tic", sep.arg[7]);
|
||||
ExportVar(package_name.c_str(), "special_attack", sep.arg[8]);
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_DESTROY_ITEM_CLIENT: {
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
EQ::ItemInstance* inst = std::any_cast<EQ::ItemInstance*>(extra_pointers->at(0));
|
||||
ExportVar(package_name.c_str(), "item_id", inst->GetID());
|
||||
ExportVar(package_name.c_str(), "item_name", inst->GetItem()->Name);
|
||||
ExportVar(package_name.c_str(), "quantity", inst->IsStackable() ? inst->GetCharges() : 1);
|
||||
ExportVar(package_name.c_str(), "item", "QuestItem", inst);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
|
||||
+127
-1
@@ -3981,6 +3981,121 @@ int8 Perl__does_augment_fit(EQ::ItemInstance* inst, uint32 augment_id)
|
||||
return quest_manager.DoesAugmentFit(inst, augment_id);
|
||||
}
|
||||
|
||||
int8 Perl__does_augment_fit_slot(EQ::ItemInstance* inst, uint32 augment_id, uint8 augment_slot)
|
||||
{
|
||||
return quest_manager.DoesAugmentFit(inst, augment_id, augment_slot);
|
||||
}
|
||||
|
||||
perl::array Perl__GetRecipeComponentItemIDs(uint32 recipe_id)
|
||||
{
|
||||
perl::array result;
|
||||
|
||||
const auto& l = content_db.GetRecipeComponentItemIDs(RecipeCountType::Component, recipe_id);
|
||||
|
||||
if (!l.empty()) {
|
||||
result.reserve(l.size());
|
||||
|
||||
for (int i = 0; i < l.size(); i++) {
|
||||
result.push_back(l[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
perl::array Perl__GetRecipeContainerItemIDs(uint32 recipe_id)
|
||||
{
|
||||
perl::array result;
|
||||
|
||||
const auto& l = content_db.GetRecipeComponentItemIDs(RecipeCountType::Container, recipe_id);
|
||||
|
||||
if (!l.empty()) {
|
||||
result.reserve(l.size());
|
||||
|
||||
for (int i = 0; i < l.size(); i++) {
|
||||
result.push_back(l[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
perl::array Perl__GetRecipeFailItemIDs(uint32 recipe_id)
|
||||
{
|
||||
perl::array result;
|
||||
|
||||
const auto &l = content_db.GetRecipeComponentItemIDs(RecipeCountType::Fail, recipe_id);
|
||||
|
||||
if (!l.empty()) {
|
||||
result.reserve(l.size());
|
||||
|
||||
for (int i = 0; i < l.size(); i++) {
|
||||
result.push_back(l[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
perl::array Perl__GetRecipeSalvageItemIDs(uint32 recipe_id)
|
||||
{
|
||||
perl::array result;
|
||||
|
||||
const auto& l = content_db.GetRecipeComponentItemIDs(RecipeCountType::Salvage, recipe_id);
|
||||
|
||||
if (!l.empty()) {
|
||||
result.reserve(l.size());
|
||||
|
||||
for (int i = 0; i < l.size(); i++) {
|
||||
result.push_back(l[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
perl::array Perl__GetRecipeSuccessItemIDs(uint32 recipe_id)
|
||||
{
|
||||
perl::array result;
|
||||
|
||||
const auto& l = content_db.GetRecipeComponentItemIDs(RecipeCountType::Success, recipe_id);
|
||||
|
||||
if (!l.empty()) {
|
||||
result.reserve(l.size());
|
||||
|
||||
for (int i = 0; i < l.size(); i++) {
|
||||
result.push_back(l[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int8 Perl__GetRecipeComponentCount(uint32 recipe_id, uint32 item_id)
|
||||
{
|
||||
return content_db.GetRecipeComponentCount(RecipeCountType::Component, recipe_id, item_id);
|
||||
}
|
||||
|
||||
int8 Perl__GetRecipeFailCount(uint32 recipe_id, uint32 item_id)
|
||||
{
|
||||
return content_db.GetRecipeComponentCount(RecipeCountType::Fail, recipe_id, item_id);
|
||||
}
|
||||
|
||||
int8 Perl__GetRecipeSalvageCount(uint32 recipe_id, uint32 item_id)
|
||||
{
|
||||
return content_db.GetRecipeComponentCount(RecipeCountType::Salvage, recipe_id, item_id);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -4326,7 +4441,8 @@ void perl_register_quest()
|
||||
package.add("doanim", (void(*)(int, int, bool))&Perl__doanim);
|
||||
package.add("doanim", (void(*)(int, int, bool, int))&Perl__doanim);
|
||||
package.add("do_augment_slots_match", &Perl__do_augment_slots_match);
|
||||
package.add("does_augment_fit", &Perl__does_augment_fit);
|
||||
package.add("does_augment_fit", (int8(*)(EQ::ItemInstance*, uint32))&Perl__does_augment_fit);
|
||||
package.add("does_augment_fit_slot", (int8(*)(EQ::ItemInstance*, uint32, uint8))&Perl__does_augment_fit_slot);
|
||||
package.add("echo", &Perl__echo);
|
||||
package.add("emote", &Perl__emote);
|
||||
package.add("enable_proximity_say", &Perl__enable_proximity_say);
|
||||
@@ -4390,6 +4506,15 @@ void perl_register_quest()
|
||||
package.add("getgroupidbycharid", &Perl__getgroupidbycharid);
|
||||
package.add("getinventoryslotname", &Perl__getinventoryslotname);
|
||||
package.add("getraididbycharid", &Perl__getraididbycharid);
|
||||
package.add("get_recipe_component_item_ids", &Perl__GetRecipeComponentItemIDs);
|
||||
package.add("get_recipe_container_item_ids", &Perl__GetRecipeContainerItemIDs);
|
||||
package.add("get_recipe_fail_item_ids", &Perl__GetRecipeFailItemIDs);
|
||||
package.add("get_recipe_salvage_item_ids", &Perl__GetRecipeSalvageItemIDs);
|
||||
package.add("get_recipe_success_item_ids", &Perl__GetRecipeSuccessItemIDs);
|
||||
package.add("get_recipe_component_count", &Perl__GetRecipeComponentCount);
|
||||
package.add("get_recipe_fail_count", &Perl__GetRecipeFailCount);
|
||||
package.add("get_recipe_salvage_count", &Perl__GetRecipeSalvageCount);
|
||||
package.add("get_recipe_success_count", &Perl__GetRecipeSuccessCount);
|
||||
package.add("getracename", &Perl__getracename);
|
||||
package.add("getremainingtimeMS", &Perl__getremainingtimeMS);
|
||||
package.add("getspell", &Perl__getspell);
|
||||
@@ -4512,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
|
||||
|
||||
+116
-60
@@ -683,7 +683,9 @@ void EntityList::AddNPC(NPC *npc, bool send_spawn_packet, bool dont_queue)
|
||||
npc_list.insert(std::pair<uint16, NPC *>(npc->GetID(), npc));
|
||||
mob_list.insert(std::pair<uint16, Mob *>(npc->GetID(), npc));
|
||||
|
||||
parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0);
|
||||
if (parse->HasQuestSub(npc->GetNPCTypeID(), EVENT_SPAWN)) {
|
||||
parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0);
|
||||
}
|
||||
|
||||
const auto emote_id = npc->GetEmoteID();
|
||||
if (emote_id != 0) {
|
||||
@@ -718,9 +720,13 @@ void EntityList::AddNPC(NPC *npc, bool send_spawn_packet, bool dont_queue)
|
||||
}
|
||||
}
|
||||
|
||||
npc->SendPositionToClients();
|
||||
|
||||
entity_list.ScanCloseMobs(npc->close_mobs, npc, true);
|
||||
|
||||
npc->DispatchZoneControllerEvent(EVENT_SPAWN_ZONE, npc, "", 0, nullptr);
|
||||
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_SPAWN_ZONE)) {
|
||||
npc->DispatchZoneControllerEvent(EVENT_SPAWN_ZONE, npc, "", 0, nullptr);
|
||||
}
|
||||
|
||||
if (zone->HasMap() && zone->HasWaterMap()) {
|
||||
npc->SetSpawnedInWater(false);
|
||||
@@ -844,8 +850,10 @@ void EntityList::CheckSpawnQueue()
|
||||
NPC *pnpc = it->second;
|
||||
pnpc->SendArmorAppearance();
|
||||
pnpc->SetAppearance(pnpc->GetGuardPointAnim(), false);
|
||||
if (!pnpc->IsTargetable())
|
||||
if (!pnpc->IsTargetable()) {
|
||||
pnpc->SendTargetable(false);
|
||||
}
|
||||
pnpc->SendPositionToClients();
|
||||
}
|
||||
safe_delete(outapp);
|
||||
iterator.RemoveCurrent();
|
||||
@@ -1783,12 +1791,29 @@ void EntityList::QueueClientsStatus(Mob *sender, const EQApplicationPacket *app,
|
||||
void EntityList::DuelMessage(Mob *winner, Mob *loser, bool flee)
|
||||
{
|
||||
if (winner->GetLevelCon(winner->GetLevel(), loser->GetLevel()) > 2) {
|
||||
std::vector<std::any> args;
|
||||
args.push_back(winner);
|
||||
args.push_back(loser);
|
||||
if (parse->PlayerHasQuestSub(EVENT_DUEL_WIN)) {
|
||||
std::vector<std::any> args = { winner, loser };
|
||||
|
||||
parse->EventPlayer(EVENT_DUEL_WIN, winner->CastToClient(), loser->GetName(), loser->CastToClient()->CharacterID(), &args);
|
||||
parse->EventPlayer(EVENT_DUEL_LOSE, loser->CastToClient(), winner->GetName(), winner->CastToClient()->CharacterID(), &args);
|
||||
parse->EventPlayer(
|
||||
EVENT_DUEL_WIN,
|
||||
winner->CastToClient(),
|
||||
loser->GetName(),
|
||||
loser->CastToClient()->CharacterID(),
|
||||
&args
|
||||
);
|
||||
}
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_DUEL_LOSE)) {
|
||||
std::vector<std::any> args = { winner, loser };
|
||||
|
||||
parse->EventPlayer(
|
||||
EVENT_DUEL_LOSE,
|
||||
loser->CastToClient(),
|
||||
winner->GetName(),
|
||||
winner->CastToClient()->CharacterID(),
|
||||
&args
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
auto it = client_list.begin();
|
||||
@@ -3641,21 +3666,25 @@ void EntityList::ClearFeignAggro(Mob *targ)
|
||||
}
|
||||
|
||||
if (targ->IsClient()) {
|
||||
std::vector<std::any> args;
|
||||
args.push_back(it->second);
|
||||
int i = parse->EventPlayer(EVENT_FEIGN_DEATH, targ->CastToClient(), "", 0, &args);
|
||||
if (i != 0) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
if (parse->PlayerHasQuestSub(EVENT_FEIGN_DEATH)) {
|
||||
std::vector<std::any> args = { it->second };
|
||||
|
||||
if (it->second->IsNPC()) {
|
||||
int i = parse->EventNPC(EVENT_FEIGN_DEATH, it->second->CastToNPC(), targ, "", 0);
|
||||
int i = parse->EventPlayer(EVENT_FEIGN_DEATH, targ->CastToClient(), "", 0, &args);
|
||||
if (i != 0) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (it->second->IsNPC()) {
|
||||
if (parse->HasQuestSub(it->second->GetNPCTypeID(), EVENT_FEIGN_DEATH)) {
|
||||
int i = parse->EventNPC(EVENT_FEIGN_DEATH, it->second->CastToNPC(), targ, "", 0);
|
||||
if (i != 0) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it->second->RemoveFromHateList(targ);
|
||||
@@ -3988,29 +4017,43 @@ void EntityList::ProcessMove(Client *c, const glm::vec3& location)
|
||||
for (auto iter = events.begin(); iter != events.end(); ++iter) {
|
||||
quest_proximity_event& evt = (*iter);
|
||||
|
||||
std::vector<std::any> args;
|
||||
args.push_back(&evt.area_id);
|
||||
args.push_back(&evt.area_type);
|
||||
std::vector<std::any> args = { &evt.area_id, &evt.area_type };
|
||||
|
||||
if (evt.npc) {
|
||||
if (evt.event_id == EVENT_ENTER) {
|
||||
parse->EventNPC(EVENT_ENTER, evt.npc, evt.client, "", 0);
|
||||
if (parse->HasQuestSub(evt.npc->GetNPCTypeID(), EVENT_ENTER)) {
|
||||
parse->EventNPC(EVENT_ENTER, evt.npc, evt.client, "", 0);
|
||||
}
|
||||
} else if (evt.event_id == EVENT_EXIT) {
|
||||
parse->EventNPC(EVENT_EXIT, evt.npc, evt.client, "", 0);
|
||||
if (parse->HasQuestSub(evt.npc->GetNPCTypeID(), EVENT_EXIT)) {
|
||||
parse->EventNPC(EVENT_EXIT, evt.npc, evt.client, "", 0);
|
||||
}
|
||||
} else if (evt.event_id == EVENT_ENTER_AREA) {
|
||||
parse->EventNPC(EVENT_ENTER_AREA, evt.npc, evt.client, "", 0, &args);
|
||||
if (parse->HasQuestSub(evt.npc->GetNPCTypeID(), EVENT_ENTER_AREA)) {
|
||||
parse->EventNPC(EVENT_ENTER_AREA, evt.npc, evt.client, "", 0, &args);
|
||||
}
|
||||
} else if (evt.event_id == EVENT_LEAVE_AREA) {
|
||||
parse->EventNPC(EVENT_LEAVE_AREA, evt.npc, evt.client, "", 0, &args);
|
||||
if (parse->HasQuestSub(evt.npc->GetNPCTypeID(), EVENT_LEAVE_AREA)) {
|
||||
parse->EventNPC(EVENT_LEAVE_AREA, evt.npc, evt.client, "", 0, &args);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (evt.event_id == EVENT_ENTER) {
|
||||
parse->EventPlayer(EVENT_ENTER, evt.client, "", 0);
|
||||
if (parse->PlayerHasQuestSub(EVENT_ENTER)) {
|
||||
parse->EventPlayer(EVENT_ENTER, evt.client, "", 0);
|
||||
}
|
||||
} else if (evt.event_id == EVENT_EXIT) {
|
||||
parse->EventPlayer(EVENT_EXIT, evt.client, "", 0);
|
||||
if (parse->PlayerHasQuestSub(EVENT_EXIT)) {
|
||||
parse->EventPlayer(EVENT_EXIT, evt.client, "", 0);
|
||||
}
|
||||
} else if (evt.event_id == EVENT_ENTER_AREA) {
|
||||
parse->EventPlayer(EVENT_ENTER_AREA, evt.client, "", 0, &args);
|
||||
if (parse->PlayerHasQuestSub(EVENT_ENTER_AREA)) {
|
||||
parse->EventPlayer(EVENT_ENTER_AREA, evt.client, "", 0, &args);
|
||||
}
|
||||
} else if (evt.event_id == EVENT_LEAVE_AREA) {
|
||||
parse->EventPlayer(EVENT_LEAVE_AREA, evt.client, "", 0, &args);
|
||||
if (parse->PlayerHasQuestSub(EVENT_LEAVE_AREA)) {
|
||||
parse->EventPlayer(EVENT_LEAVE_AREA, evt.client, "", 0, &args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4023,20 +4066,22 @@ void EntityList::ProcessMove(NPC *n, float x, float y, float z) {
|
||||
|
||||
std::list<quest_proximity_event> events;
|
||||
|
||||
for (auto iter = area_list.begin(); iter != area_list.end(); ++iter) {
|
||||
|
||||
Area &a = (*iter);
|
||||
for (const auto& a : area_list) {
|
||||
bool old_in = true;
|
||||
bool new_in = true;
|
||||
if (last_x < a.min_x || last_x > a.max_x ||
|
||||
last_y < a.min_y || last_y > a.max_y ||
|
||||
last_z < a.min_z || last_z > a.max_z) {
|
||||
if (
|
||||
!EQ::ValueWithin(last_x, a.min_x, a.max_x) ||
|
||||
!EQ::ValueWithin(last_y, a.min_y, a.max_y) ||
|
||||
!EQ::ValueWithin(last_z, a.min_z, a.max_z)
|
||||
) {
|
||||
old_in = false;
|
||||
}
|
||||
|
||||
if (x < a.min_x || x > a.max_x ||
|
||||
y < a.min_y || y > a.max_y ||
|
||||
z < a.min_z || z > a.max_z) {
|
||||
if (
|
||||
!EQ::ValueWithin(x, a.min_x, a.max_x) ||
|
||||
!EQ::ValueWithin(y, a.min_y, a.max_y) ||
|
||||
!EQ::ValueWithin(z, a.min_z, a.max_z)
|
||||
) {
|
||||
new_in = false;
|
||||
}
|
||||
|
||||
@@ -4048,7 +4093,7 @@ void EntityList::ProcessMove(NPC *n, float x, float y, float z) {
|
||||
evt.npc = n;
|
||||
evt.area_id = a.id;
|
||||
evt.area_type = a.type;
|
||||
events.push_back(evt);
|
||||
events.emplace_back(evt);
|
||||
}
|
||||
else if (!old_in && new_in) {
|
||||
//were not in but now are
|
||||
@@ -4058,25 +4103,29 @@ void EntityList::ProcessMove(NPC *n, float x, float y, float z) {
|
||||
evt.npc = n;
|
||||
evt.area_id = a.id;
|
||||
evt.area_type = a.type;
|
||||
events.push_back(evt);
|
||||
events.emplace_back(evt);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto iter = events.begin(); iter != events.end(); ++iter) {
|
||||
quest_proximity_event &evt = (*iter);
|
||||
|
||||
std::vector<std::any> args;
|
||||
args.push_back(&evt.area_id);
|
||||
args.push_back(&evt.area_type);
|
||||
for (const auto& evt : events) {
|
||||
std::vector<std::any> args = { &evt.area_id, &evt.area_type };
|
||||
|
||||
if (evt.event_id == EVENT_ENTER) {
|
||||
parse->EventNPC(EVENT_ENTER, evt.npc, evt.client, "", 0);
|
||||
if (parse->HasQuestSub(evt.npc->GetNPCTypeID(), EVENT_ENTER)) {
|
||||
parse->EventNPC(EVENT_ENTER, evt.npc, evt.client, "", 0);
|
||||
}
|
||||
} else if (evt.event_id == EVENT_EXIT) {
|
||||
parse->EventNPC(EVENT_EXIT, evt.npc, evt.client, "", 0);
|
||||
if (parse->HasQuestSub(evt.npc->GetNPCTypeID(), EVENT_EXIT)) {
|
||||
parse->EventNPC(EVENT_EXIT, evt.npc, evt.client, "", 0);
|
||||
}
|
||||
} else if (evt.event_id == EVENT_ENTER_AREA) {
|
||||
parse->EventNPC(EVENT_ENTER_AREA, evt.npc, evt.client, "", 0, &args);
|
||||
if (parse->HasQuestSub(evt.npc->GetNPCTypeID(), EVENT_ENTER_AREA)) {
|
||||
parse->EventNPC(EVENT_ENTER_AREA, evt.npc, evt.client, "", 0, &args);
|
||||
}
|
||||
} else if (evt.event_id == EVENT_LEAVE_AREA) {
|
||||
parse->EventNPC(EVENT_LEAVE_AREA, evt.npc, evt.client, "", 0, &args);
|
||||
if (parse->HasQuestSub(evt.npc->GetNPCTypeID(), EVENT_LEAVE_AREA)) {
|
||||
parse->EventNPC(EVENT_LEAVE_AREA, evt.npc, evt.client, "", 0, &args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4130,24 +4179,31 @@ void EntityList::ClearAreas()
|
||||
area_list.clear();
|
||||
}
|
||||
|
||||
void EntityList::ProcessProximitySay(const char *Message, Client *c, uint8 language)
|
||||
void EntityList::ProcessProximitySay(const char *message, Client *c, uint8 language)
|
||||
{
|
||||
if (!Message || !c)
|
||||
if (!message || !c) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto iter = proximity_list.begin();
|
||||
for (; iter != proximity_list.end(); ++iter) {
|
||||
NPC *d = (*iter);
|
||||
NPCProximity *l = d->proximity;
|
||||
if (l == nullptr || !l->say)
|
||||
for (const auto& n : proximity_list) {
|
||||
auto* p = n->proximity;
|
||||
if (!p || !p->say) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c->GetX() < l->min_x || c->GetX() > l->max_x
|
||||
|| c->GetY() < l->min_y || c->GetY() > l->max_y
|
||||
|| c->GetZ() < l->min_z || c->GetZ() > l->max_z)
|
||||
if (!parse->HasQuestSub(n->GetNPCTypeID(), EVENT_PROXIMITY_SAY)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
parse->EventNPC(EVENT_PROXIMITY_SAY, d, c, Message, language);
|
||||
if (
|
||||
!EQ::ValueWithin(c->GetX(), p->min_x, p->max_x) ||
|
||||
!EQ::ValueWithin(c->GetY(), p->min_y, p->max_y) ||
|
||||
!EQ::ValueWithin(c->GetZ(), p->min_z, p->max_z)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
parse->EventNPC(EVENT_PROXIMITY_SAY, n, c, message, language);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user