Compare commits

..

127 Commits

Author SHA1 Message Date
Alex King 73f5fcd6d7 Update exp.cpp 2023-02-13 00:16:55 -06:00
Valorith 407ceac2bb Remove double tabs 2023-02-13 00:16:55 -06:00
Valorith fea571db40 Use tab character setting 2023-02-13 00:16:55 -06:00
Valorith 1f51a2fceb Indenting adjustments 2023-02-13 00:16:55 -06:00
Valorith 724f4ddfb8 Finalizing formatting 2023-02-13 00:16:55 -06:00
Valorith 3d38cd6235 Indenting 2023-02-13 00:16:55 -06:00
Valorith 19519908a6 Formatting 2023-02-13 00:16:54 -06:00
Valorith 5f044c3d43 Update export naming 2023-02-13 00:16:54 -06:00
Valorith 8cce781340 Adjustment 2023-02-13 00:16:54 -06:00
Valorith c9b3facf03 Additional tweak 2023-02-13 00:16:54 -06:00
Valorith d1a16c2ae8 Formatting 2023-02-13 00:16:54 -06:00
Valorith b61ee86ff3 Tweak 2023-02-13 00:16:54 -06:00
Valorith 97c93ca55a Add XP Events 2023-02-13 00:16:54 -06:00
Aeadoin fc7c30977a [Bots & Mercs] Add Support for TrySympatheticProc (#2866)
* [Bots & Merrcs] Add Support for TrySympatheticProc

* [Bots & Merrcs] Add Support for TrySympatheticProc

* [Bots & Merrcs] Add Support for TrySympatheticProc

* Cleanup

* formatting

* auto
2023-02-13 00:16:18 -06:00
Aeadoin b3fd9dd88a [Crash] Fix Crash in FindType (#2867) 2023-02-13 00:15:40 -06:00
Alex King 4df9661903 [Quest API] Add EVENT_DROP_ITEM_CLIENT to Perl/Lua (#2869)
* [Quest API] Add EVENT_DROP_ITEM_CLIENT to Perl/Lua

- Add `EVENT_DROP_ITEM_CLIENT`, exports `$quantity,` $item_name`, `$item_id`, `$spell_id`, `$slot_id`, and `$item`.

- Add `event_drop_item_client`, exports `e.quantity`, `e.item_name`, `e.item_id`, `e.spell_id`, `e.slot_id`, and `e.item`.

* Update inventory.cpp

* Update inventory.cpp

* Update lua_general.cpp

* Update inventory.cpp
2023-02-13 00:15:19 -06:00
Alex King 8c363320d8 [Quest API] Export target to EVENT_TARGET_CHANGE in Perl/Lua. (#2870)
* [Quest API] Export target to EVENT_TARGET_CHANGE in Perl/Lua.

- Export `$target` to `EVENT_TARGET_CHANGE`.

- Export `e.other` to `event_target_change`.

- Allows operators to not have to grab bot, Client, or NPC's target in Perl with `GetTarget()`.
- Allows operators to not have to grab Client's target in Lua with `GetTarget()`.

* Update mob.cpp

* Update mob.cpp

* Update mob.cpp
2023-02-13 00:03:52 -06:00
Alex King d4afc78982 [Quest API] Add EVENT_DESTROY_ITEM_CLIENT to Perl/Lua. (#2871)
* [Quest API] Add EVENT_DESTROY_ITEM_CLIENT to Perl/Lua.

- Add `EVENT_DESTROY_ITEM_CLIENT`, exports `$item_id`, `$item_name`, `$quantity`, and `$item`.

- Add `event_destroy_item_client`, exports `e.item_id`, `e.item_name`, `e.quantity`, and `e.item`.

- Allows operators to use player scripts for item destroys.

* Update lua_parser_events.h

* Update inventory.cpp
2023-02-12 23:58:27 -06:00
Aeadoin 24de1d948a [Crash] Fix crash in Mob::CommonDamage when attacker was null (#2872) 2023-02-12 23:53:29 -06:00
Alex King ca0e85b4bc [Quest API] Export $item to EVENT_PLAYER_PICKUP in Perl. (#2875)
* [Quest API] Export $item to EVENT_PLAYER_PICKUP in Perl.

# Notes
- Exports `$item` to `EVENT_PLAYER_PICKUP` in Perl so that you have access to the item itself.

* Optional parsing.

* Update object.cpp

* Update object.cpp
2023-02-12 23:52:47 -06:00
Alex King 5b24d38d1e [Quest API] Export $item to Fishing and Forage Events in Perl (#2876)
* [Quest API] Export $item to Fishing and Forage Events in Perl

# Notes
- Exports `$item` to `EVENT_FISH_SUCCESS` in Perl.
- Exports `$item` to `EVENT_FORAGE_SUCCESS` in Perl.

* Add optional parsing to fish/forage events.

* Update forage.cpp

* Fix missing event param

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2023-02-12 23:50:21 -06:00
Alex King 4a1d026215 [Quest API] Export $spawned to EVENT_SPAWN_ZONE in Perl (#2877)
* [Quest API] Export $spawned to EVENT_SPAWN_ZONE in Perl

# Notes
- Exports `$spawned` to `EVENT_SPAWN_ZONE` in Perl.
- Allows operators to use the mob reference instead of having to grab it from entity list.

* Optional parsing.
2023-02-12 23:46:01 -06:00
Alex King 5ef8f8c3a8 [Quest API] Export $item and $corpse to EVENT_LOOT and EVENT_LOOT_ZONE in Perl (#2878)
* [Quest API] Export $item and $corpse to EVENT_LOOT and EVENT_LOOT_ZONE in Perl

# Notes
- Exports `$item` and `$corpse` to `EVENT_LOOT` in Perl.
- Exports `$item` and `$corpse` to `EVENT_LOOT_ZONE` in Perl.

* Optional parsing.

* Update corpse.cpp

* Cleanup

* Export changes

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2023-02-12 23:45:26 -06:00
Alex King 384de31989 [Quest API] (Performance) Check event exists before export and execute EVENT_BOT_CREATE (#2886)
* [Quest API] Optionally parse EVENT_BOT_CREATE

- Optionally parse this event instead of always doing so.

* Cleanup

* Cleanup
2023-02-12 23:40:03 -06:00
Alex King 5be3780a54 [Quest API] (Performance) Check event exists before export and execute EVENT_LEVEL_UP and EVENT_LEVEL_DOWN (#2889)
* [Quest API] Optionally parse EVENT_LEVEL_UP and EVENT_LEVEL_DOWN

- Optionally parses these events instead of always doing so.

* [Quest API] Optionally parse EVENT_LEVEL_UP and EVENT_LEVEL_DOWN

- Optionally parses these events instead of always doing so.
2023-02-12 23:36:45 -06:00
Alex King 21e42714eb [Quest API] (Performance) Check merchant events exist before export and execute (#2893)
* [Quest API] Optionally parse merchant events

- Optionally parse these events instead of always doing so.

* Cleanup
2023-02-12 23:33:32 -06:00
Alex King 3474c00e7a [Quest API] (Performance) Check event EVENT_LANGUAGE_SKILL_UP, EVENT_SKILL_UP, or EVENT_USE_SKILL exist before export and execute (#2894)
* [Quest API] Optionally parse EVENT_LANGUAGE_SKILL_UP, EVENT_SKILL_UP, and EVENT_USE_SKILL

- Optionally parse these events instead of always doing so.

* Cleanup
2023-02-12 23:30:48 -06:00
Alex King 3d6b0e5f74 [Quest API] (Performance) Check event EVENT_COMBINE, EVENT_COMBINE_SUCCESS, EVENT_COMBINE_FAILURE, or EVENT_COMBINE_VALIDATE exist before export and execute (#2896)
* [Quest API] Optionally parse EVENT_COMBINE_SUCCESS and EVENT_COMBINE_FAILURE

- Optionally parse these events instead of always doing so.

* Update tradeskills.cpp

* Update tradeskills.cpp
2023-02-12 23:28:27 -06:00
Alex King 9f619859d1 [Quest API] (Performance) Check spell or cast events exist before export and execute (#2897)
* [Quest API] Optionally parse spell/cast events

# Notes
- Optionally parses `EVENT_CAST`, `EVENT_CAST_BEGIN`, `EVENT_CAST_ON`, `EVENT_SPELL_EFFECT_NPC`, `EVENT_SPELL_EFFECT_CLIENT`, `EVENT_SPELL_EFFECT_BOT`, `EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT`, `EVENT_SPELL_EFFECT_BUFF_TIC_NPC`, `EVENT_SPELL_EFFECT_BUFF_TIC_BOT`, `EVENT_SPELL_FADE`, and `EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE`.

* Cleanup

* PR comment fixes

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2023-02-12 23:26:17 -06:00
Alex King 805a9c5f59 [Quest API] (Performance) Check event EVENT_DEATH, EVENT_DEATH_COMPLETE, or EVENT_DEATH_ZONE exist before export and execute (#2909)
* [Quest API] Optionally parse EVENT_DEATH and EVENT_DEATH_COMPLETE

- Optionally parse these events instead of always doing so.

* Update attack.cpp

* Update attack.cpp

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2023-02-12 23:15:57 -06:00
Alex King 43329dc583 [Quest API] (Performance) Check event exists before export and execute EVENT_KILLED_MERIT (#2911)
- Optionally parse this event instead of always doing so.
2023-02-12 23:09:34 -06:00
Alex King 66fee56c47 [Quest API] (Performance) Check event exists before export and execute EVENT_DISCOVER_ITEM (#2912)
- Optionally parse this event instead of always doing so.
2023-02-12 23:07:32 -06:00
Alex King efb2ab57aa [Quest API] Optionally parse EVENT_CONNECT and EVENT_DISCONNECT (#2913)
- Optionally parse these events instead of always doing so.
2023-02-12 23:01:48 -06:00
Alex King 0a7d482299 [Quest API] (Performance) Check event EVENT_ENVIRONMENTAL_DAMAGE exists before export and execute (#2899)
* [Quest API] Optionally parse EVENT_ENVIRONMENTAL_DAMAGE

# Notes
- Optionally parses this event instead of always doing so.

* Update client_packet.cpp
2023-02-12 23:00:04 -06:00
Alex King de047fb851 [Quest API] (Performance) Check event EVENT_FEIGN_DEATH exists before export and execute (#2916)
* [Quest API] Optionally parse EVENT_FEIGN_DEATH

# Notes
- Optionally parse this event instead of always doing so.

* Remove unused reference, fix other PR

* Update task_client_state.cpp

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2023-02-12 22:59:14 -06:00
Natedog2012 9836b5cf67 [Bug Fix] Fix for interrupting item casts to no longer lock the client if cast time of item greater than 0 (#2921) 2023-02-12 22:49:59 -06:00
Alex King c64591b8f7 [Quest API] Export $killed_npc to EVENT_NPC_SLAY to Perl (#2879)
# Notes
- Exports `$killed_npc` to `EVENT_NPC_SLAY` to Perl.
- Allows operators to use NPC reference in event instead of just NPC ID.
2023-02-12 22:49:16 -06:00
Alex King 3813162bac [Quest API] (Performance) Check task events exist before export and execute (#2883)
* [Quest API] Optionally parse task events

- Optionally parses these events instead of always doing so.

* Update task_client_state.cpp

* Cleanup

* Update task_client_state.cpp

* [Quest API] Optionally parse task events

- Optionally parses these events instead of always doing so.

* Update task_client_state.cpp

* Cleanup
2023-02-12 22:48:51 -06:00
Alex King 7099e17c7e [Quest API] Export $hate_entity to EVENT_HATE_LIST in Perl (#2885)
# Notes
- Exports `$hate_entity` to `EVENT_HATE_LIST`.
- Allows operators to see which mob is joining/leaving an NPC's hatelist.
2023-02-12 22:44:31 -06:00
Alex King 2c75e8fcd4 [Quest API] Add $target export to EVENT_INSPECT in Perl (#2891)
* [Quest API] Add $target export to EVENT_INSPECT in Perl

# Notes
- Exports `$target` to `EVENT_INSPECT`.
- Allows operators to get information from target directly instead of using entity list.

* Update client_packet.cpp
2023-02-12 22:43:38 -06:00
Alex King e8f01fb6ac [Quest API] Export $item and $augment to augment events in Perl (#2895)
* [Quest API] Export $item and $augment to augment events in Perl

# Notes
- Exports `$item` and `$augment` to `EVENT_AUGMENT_INSERT_CLIENT` in Perl.
- Exports `$item` and `$augment` to `EVENT_AUGMENT_REMOVE_CLIENT` in Perl.
- Allows operators to use item and augment reference instead of just item IDs.

* Cleanup
2023-02-12 22:42:27 -06:00
Alex King fd0764d4cb [Quest API] (Performance) Check event exists before export and execute EVENT_TIMER (#2903)
# Notes
- Parse this event optionally instead of always doing so.
2023-02-12 22:33:33 -06:00
Alex King 71b2bf6a64 [Quest API] (Performance) Check event exists before export and execute EVENT_ENTER_ZONE and EVENT_ZONE (#2900)
# Notes
- Optionally parse these events instead of always doing so.
2023-02-12 22:32:37 -06:00
Alex King 2dcff247c8 [Quest API] Export targets to EVENT_CONSIDER and EVENT_CONSIDER_CORPSE (#2908)
# Perl
- Export `$target` to `EVENT_CONSIDER`.
- Export `$corpse` to `EVENT_CONSIDER_CORPSE`.

# Lua
- Export `e.other` to `EVENT_CONSIDER`.
- Export `e.corpse` to `EVENT_CONSIDER_CORPSE`.

 # Notes
- Allows operators to grab the target or corpse a player is considering.
2023-02-12 22:32:04 -06:00
Alex King 93f19d3971 [Quest API] (Performance) Check event EVENT_AGGRO, EVENT_ATTACK, or EVENT_COMBAT exist before export and execute (#2901)
* [Quest API] Optionally parse EVENT_COMBAT

# Notes
- Optionally parse this event instead of always doing so.

* Optional EVENT_ATTACK

* Update attack.cpp
2023-02-12 22:30:24 -06:00
Alex King bad44f35e2 [Quest API] (Performance) Check event EVENT_PAYLOAD or EVENT_SIGNAL exist before export and execute (#2902)
* [Quest API] Optionally parse EVENT_PAYLOAD and EVENT_SIGNAL

# Notes
- Optionally parse these events instead of always doing so.

* Update bot.cpp
2023-02-12 22:28:50 -06:00
Alex King 4a339d49df [Quest API] (Performance) Check event exists before export and execute EVENT_HP (#2904)
# Notes
- Optionally parse this event instead of always doing so.
2023-02-12 22:27:57 -06:00
Alex King 57d0420399 [Quest API] (Performance) Check event EVENT_WAYPOINT_ARRIVE or EVENT_WAYPOINT_DEPART exist before export and execute (#2905)
* [Quest API] Optionally parse EVENT_WAYPOINT_ARRIVE and EVENT_WAYPOINT_DEPART

# Notes
- Optionally parse these events instead of always doing so.

* [Quest API] Optionally parse EVENT_WARP

# Notes
- Optionally parse this event instead of always doing so.

* Revert "[Quest API] Optionally parse EVENT_WARP"

This reverts commit d8acb9883d.
2023-02-12 22:27:15 -06:00
Alex King 90def9b882 [Quest API] (Performance) Check event exists before export and execute EVENT_TRADE (#2906)
# Notes
- Optionally parse this event instead of always doing so.
2023-02-12 22:26:21 -06:00
Alex King 84156829a7 [Quest API] (Performance) Check event exists before export and execute EVENT_WARP (#2907)
# Notes
- Parse this event optionally instead of always doing so.
2023-02-12 22:25:42 -06:00
Alex King d210b1e5ff [Quest API] (Performance) Check event EVENT_SLAY exists before export and execute (#2910)
* [Quest API] Optionally parse EVENT_SLAY

# Notes
- Optionally parse this event instead of always doing so.

* Update attack.cpp
2023-02-12 22:24:38 -06:00
Alex King 086538754e [Quest API] (Performance) Check event EVENT_ITEM_TICK or EVENT_WEAPON_PROC exist before export and execute (#2914)
* [Quest API] Optionally parse EVENT_ITEM_TICK

# Notes
- Optionally parse this event instead of always doing so.

* Update mob.cpp
2023-02-12 22:22:22 -06:00
Alex King 241f900dc4 [Quest API] (Performance) Check event exists before export and execute EVENT_DUEL_LOSE and EVENT_DUEL_WIN (#2915)
# Notes
- Optionally parse these events instead of always doing so.
2023-02-12 22:21:21 -06:00
Alex King 604256a223 [Quest API] (Performance) Check event exists before export and execute EVENT_RESPAWN (#2917)
# Notes
- Optionally parse this event instead of always doing so.
2023-02-12 22:19:23 -06:00
Alex King 2dffc66c6f [Quest API] (Performance) Check event exists before export and execute EVENT_UNHANDLED_OPCODE (#2918)
# Notes
- Optionally parse this event instead of always doing so.
2023-02-12 22:18:57 -06:00
Alex King 1bf24273d2 [Quest API] (Performance) Check event exists before export and execute EVENT_TICK (#2919)
# Notes
- Optionally parse this event instead of always doing so.
2023-02-12 22:16:39 -06:00
Alex King c060280417 [Quest API] Optionally parse EVENT_TEST_BUFF (#2920)
# Notes
- Optionally parse this event instead of always doing so.
2023-02-12 22:16:17 -06:00
Alex King bc6efd5f74 [Quest API] (Performance) Check equip or scale item events exist before export and execute (#2898)
* [Quest API] Optional parse equip and scale item events

# Notes
- Optionally parse `EVENT_SCALE_CALC`, `EVENT_ITEM_ENTER_ZONE`, `EVENT_UNEQUIP_ITEM_BOT`, `EVENT_EQUIP_ITEM_BOT`, `EVENT_EQUIP_ITEM`, `EVENT_UNEQUIP_ITEM`, `EVENT_EQUIP_ITEM_CLIENT`, `EVENT_UNEQUIP_ITEM_CLIENT`

* Cleanup
2023-02-12 22:15:54 -06:00
Alex King efd6d2f9b1 [Quest API] (Performance) Check event EVENT_AA_BUY or EVENT_AA_GAIN exist before export and execute (#2892)
* [Quest API] Optionally parse EVENT_AA_BUY and EVENT_AA_GAIN

# Notes
- Optionally parse these events instead of always doing so.

* Cleanup
2023-02-12 22:04:54 -06:00
Alex King 9dd4cf71f1 [Quest API] (Performance) Check event exists before export and execute EVENT_GM_COMMAND (#2890)
# Notes
- Optionally parse this event instead of always doing so.
2023-02-12 22:03:51 -06:00
Alex King cfec31457c [Quest API] (Performance) Check event exists before export and execute area events (#2888)
* [Quest API] Optionally parse area events

# Notes
- Optionally parse these events instead of always doing so.

* Update entity.cpp
2023-02-12 22:01:28 -06:00
Alex King 5ac5beb456 [Quest API] (Performance) Check event exists before export and execute EVENT_DESPAWN and EVENT_DESPAWN_ZONE (#2887)
# Notes
- Optionally parse these events instead of always doing so.
2023-02-12 21:59:01 -06:00
Alex King 0e51131d67 [Quest API] (Performance) Check event exists before export and execute EVENT_GROUP_CHANGE (#2884)
* [Quest API] Optionally parse EVENT_GROUP_CHANGE

# Notes
- Optionally parse this event instead of always doing so.

* Update embparser.cpp
2023-02-12 21:56:25 -06:00
Alex King f9a87e26c9 [Quest API] (Performance) Check event exists before export and execute EVENT_AGGRO_SAY, EVENT_SAY, and EVENT_PROXIMITY_SAY (#2882)
* [Quest API] Add optional parsing to EVENT_AGGRO_SAY and EVENT_SAY

# Notes
- Optionally parse these events instead of always doing so.

* Optionally parse EVENT_PROXIMITY_SAY
2023-02-12 21:54:20 -06:00
Alex King 9e16cd8ae8 [Quest API] (Performance) Check event exists before export and execute EVENT_POPUP_RESPONSE (#2881)
# Notes
- Optionally parses this event instead of always doing so.
2023-02-12 21:48:23 -06:00
Alex King 9644f14746 [Quest API] (Performance) Check event exists before export and execute EVENT_CLICK_DOOR and EVENT_CLICK_OBJECT (#2880)
* [Quest API] Add optional parsing to EVENT_CLICK_DOOR.

# Notes
- Optional parses this event instead of always doing so.

* Update client_packet.cpp
2023-02-12 21:47:17 -06:00
Chris Miles d9f545a5ec [Logging] Implement Player Event Logging system (#2833)
* Plumbing

* Batch processing in world

* Cleanup

* Cleanup

* Update player_event_logs.cpp

* Add player zoning event

* Use generics

* Comments

* Add events

* Add more events

* AA_GAIN, AA_PURCHASE, FORAGE_SUCCESS, FORAGE_FAILURE

* FISH_SUCCESS, FISH_FAILURE, ITEM_DESTROY

* Add charges to ITEM_DESTROY

* WENT_ONLINE, WENT_OFFLINE

* LEVEL_GAIN, LEVEL_LOSS

* LOOT_ITEM

* MERCHANT_PURCHASE

* MERCHANT_SELL

* SKILL_UP

* Add events

* Add more events

* TASK_ACCEPT, TASK_COMPLETE, and TASK_UPDATE

* GROUNDSPAWN_PICKUP

* SAY

* REZ_ACCEPTED

* COMBINE_FAILURE and COMBINE_SUCCESS

* DROPPED_ITEM

* DEATH

* SPLIT_MONEY

* TRADER_PURCHASE and TRADER_SELL

* DISCOVER_ITEM

* Convert GM_COMMAND to use new macro

* Convert ZONING event to use macro

* Revert some code changes

* Revert "Revert some code changes"

This reverts commit d53682f997e89a053a660761085913245db91e9d.

* Add cereal generation support to repositories

* TRADE

* Formatting

* Cleanup

* Relocate discord_manager to discord folder

* Discord sending plumbing

* Rename UCS's Database class to UCSDatabase to be more specific and not collide with base Database class for repository usage

* More discord sending plumbing

* More discord message formatting work

* More discord formatting work

* Discord formatting of events

* Format WENT_ONLINE, WENT_OFFLINE

* Add merchant purchase event

* Handle Discord MERCHANT_SELL formatter

* Update player_event_discord_formatter.cpp

* Tweaks

* Implement retention truncation

* Put mutex locking on batch queue, put processor on its own thread

* Process on initial bootup

* Implement optional QS processing, implement keepalive from world to QS

* Reload player event settings when logs are reloaded in game

* Set settings defaults

* Update player_event_logs.cpp

* Update player_event_logs.cpp

* Set retention days on boot

* Update player_event_logs.cpp

* Player Handin Event Testing.

Testing player handin stuff.

* Cleanup.

* Finish NPC Handin.

* set a reference to the client inside of the trade object as well for plugins to process

* Fix for windows _inline

* Bump to cpp20 default, ignore excessive warnings on windows

* Bump FMT to 6.1.2 for cpp20 compat and swap fmt::join for Strings::Join

* Windows compile fixes

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Create 2022_12_19_player_events_tables.sql

* [Formatters] Work on Discord Formatters

* Handin money.

* Format header

* [Formatters] Work on Discord Formatters

* Format

* Format

* [Formatters] More Formatter work, need to test further.

* [Formatters] More Work on Formatters.

* Add missing #endif

* [Formatters] Work on Formatters, fix Bot formatting in ^create help

* NPC Handin Discord Formatter

* Update player_event_logs.cpp

* Discover Item Discord Formatter

* Dropped Item Discord Formatter

* Split Money Discord Formatter

* Trader Discord Formatters

* Cleanup.

* Trade Event Discord Formatter Groundwork

* SAY don't record GM commands

* GM_Command don't record #help

* Update player_event_logs.cpp

* Fill in more event data

* Post rebase fixes

* Post rebase fix

* Discord formatting adjustments

* Add event deprecation or unimplemented tag support

* Trade events

* Add return money and sanity checks.

* Update schema

* Update ucs.cpp

* Update client.cpp

* Update 2022_12_19_player_events_tables.sql

* Implement archive single line

* Replace hackers table and functions with PossibleHack player event

* Replace very old eventlog table since the same events are covered by player event logs

* Update bot_command.cpp

* Record NPC kill events ALL / Named / Raid

* Add BatchEventProcessIntervalSeconds rule

* Naming

* Update CMakeLists.txt

* Update database_schema.h

* Remove logging function and methods

* DB version

* Cleanup SendPlayerHandinEvent

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
Co-authored-by: Aeadoin <109764533+Aeadoin@users.noreply.github.com>
2023-02-12 21:31:01 -06:00
Aeadoin 1cc32d92cf [Bots] Add Lore Check for Augments. (#2874)
* [Bots] Add Lore Check for Augments.

* Add bot name
2023-02-12 16:43:58 -05:00
Aeadoin 924e91cf64 [Crash] Fix Bot Crash in Bot::Bot Constructor. (#2868)
* [Crash] Fix Bot Crash in Bot::Bot Constructor.

* fix another potential crash in bot contructor.
2023-02-12 14:27:29 -05:00
Aeadoin 9825c61a13 [Bots] Add Pet Power Support for Temp Pets. (#2853) 2023-02-11 10:57:02 -05:00
Alex King 5a0a1b1ffd [Quest API] Export $item to EVENT_DISCOVER_ITEM in Perl (#2863)
# Notes
- Exports `$item` to `EVENT_DISCOVER_ITEM`.
2023-02-10 23:56:50 -05:00
Alex King a3bb7e7741 [Quest API] Export $item to Client/Bot Equip Events in Perl (#2860)
* [Quest API] Add $item Export to Client/Bot Equip Events

# Notes
- Adds `$item` export to `EVENT_ITEM_EQUIP_CLIENT`, `EVENT_ITEM_UNEQUIP_CLIENT`, `EVENT_ITEM_EQUIP_BOT`, and `EVENT_UNEQUIP_ITEM_BOT`.

* Update bot.cpp

* Update embparser.cpp
2023-02-10 23:56:36 -05:00
Alex King a1251bdda8 [Quest API] Export $door to EVENT_CLICKDOOR in Perl (#2861)
# Notes
- Exports `$door` object to `EVENT_CLICKDOOR` in Perl so you don't have to grab it from entity list.
2023-02-10 23:56:24 -05:00
Alex King b90082d694 [Quest API] Export $object to EVENT_CLICK_OBJECT in Perl (#2862)
# Notes
- Exports `$object` to `EVENT_CLICK_OBJECT` in Perl so you don't have to grab it from entity list.
2023-02-10 23:34:52 -05:00
Aeadoin a49fa42f35 [Bots] Update ResistSpell to use temp_level_diff client formula (#2851) 2023-02-09 10:36:15 -05:00
Aeadoin 7064a4156f [Bots] Add IsBot() to methods in attack.cpp where applicable. (#2840)
* [Bots] add IsBot() to methods in attack.cpp where applicable.

* Add mercs where applicable

* Cleanup verbose if statements

* typo

* Fix other spots missed.
2023-02-09 10:36:01 -05:00
Aeadoin 106cb45b57 [Crash] Fix potential crash in Mob::CommonDamage (#2848)
out of bounds memory access.
2023-02-09 10:35:51 -05:00
Aeadoin 9a544650ee [Bots] ST_AreaClientOnly spells to land on Bots (#2849) 2023-02-09 10:35:38 -05:00
Aeadoin 032d423add [Bots] Add TotalDominationBonus modifiers. (#2852) 2023-02-09 10:35:26 -05:00
Akkadius 760b30ca0a [Crash] Fix issue where long short names overflow file_name 2023-02-09 01:03:51 -06:00
Chris Miles 6b08ca51cc [Logging] Add raw opcode when emu translated opcode is not found (OP_Unknown) via (C->S) (#2847) 2023-02-07 21:23:24 -06:00
Alex King 268879b414 [Quest API] Add Recipe-based methods to Perl/Lua. (#2844)
* [Quest API] Add Recipe-based methods to Perl/Lua.

# Perl
- Add `quest::get_recipe_component_item_ids(recipe_id)`.
- Add `quest::get_recipe_container_item_ids(recipe_id)`.
- Add `quest::get_recipe_fail_item_ids(recipe_id)`.
- Add `quest::get_recipe_salvage_item_ids(recipe_id)`.
- Add `quest::get_recipe_success_item_ids(recipe_id)`.
- Add `quest::get_recipe_component_count(recipe_id, item_id)`.
- Add `quest::get_recipe_fail_count(recipe_id, item_id)`.
- Add `quest::get_recipe_salvage_count(recipe_id, item_id)`.
- Add `quest::get_recipe_success_count(recipe_id, item_id)`.

# Lua
- Add `eq.get_recipe_component_item_ids(recipe_id)`.
- Add `eq.get_recipe_container_item_ids(recipe_id)`.
- Add `eq.get_recipe_fail_item_ids(recipe_id)`.
- Add `eq.get_recipe_salvage_item_ids(recipe_id)`.
- Add `eq.get_recipe_success_item_ids(recipe_id)`.
- Add `eq.get_recipe_component_count(recipe_id, item_id)`.
- Add `eq.get_recipe_fail_count(recipe_id, item_id)`.
- Add `eq.get_recipe_salvage_count(recipe_id, item_id)`.
- Add `eq.get_recipe_success_count(recipe_id, item_id)`.

# Notes
- Before these methods, you would have to use DBI from Perl or Lua in order to get the components and their counts, these methods allow easy access to these values via the scripting API.
- These should be used sparingly as they're each an individual database hit and could go crazy in a hot path.

* Update eq_constants.h

* Update zonedb.h

* Update tradeskills.cpp

* Reserve.
2023-02-07 21:42:34 -05:00
Alex King 4c6dc960e4 [Bug Fix] Fix CheckNumHitsRemaining() with 1H Blunt (#2846)
# Notes
- CheckNumHitsRemaining() wasn't working when skill ID was `0` (1H Blunt).
2023-02-06 23:41:32 -05:00
Aeadoin cc46b54f7f [Bots] Add Additional HeroicAgi/Dex Modifiers. (#2838)
* [Bots] Add Additional HeroicAgi/Dex Modifiers.

* Typo
2023-02-06 22:31:02 -05:00
Aeadoin 9e3b363d4a [Code] Add IsOfClientBot() virtual method. (#2845) 2023-02-06 22:30:49 -05:00
Aeadoin b0d1dc5f04 [Bots/Mercs] Add 100% Hit chance if sitting while attacked. (#2839)
* [BOT] Add 100% Hit chance if sitting while attacked.

* [BOT] Add 100% Hit chance if sitting while attacked.

* Add Mercs correctly

* Missed usage of IsSitting() in Mob::RollD20
2023-02-06 22:30:33 -05:00
Chris Miles 158396937a [Release] 22.3.0 (#2842)
* [Release] 22.3.0

* Update version.h

* Redirect stderr

* Update should-release to filter non-master

* Update should-release
2023-02-06 20:12:04 -06:00
Aeadoin fb1467284c [Bots] Add Additional HeroicStr modifiers. (#2837) 2023-02-06 21:07:38 -05:00
Aeadoin 14addd4869 [Feature] Add IsOfClientBotMerc() virtual method. (#2843) 2023-02-06 21:03:48 -05:00
Chris Miles 0a114fae9a [Doors] Have NPCs trigger double doors (#2821) 2023-02-06 17:47:03 -06:00
Chris Miles 2b224d42ad [Rules] Fix rule updates that affected bot booting checks (#2841) 2023-02-06 17:31:50 -06:00
Natedog2012 155ec9ac0d [Quest API] Add rule AlternateAugmentationSealer for using a different bagtype (#2831)
* [Quest API] Add rule AlternateAugmentationSealer for using a different bagtype as an alternate augmentation sealer. Use EVENT_COMBINE with UseAugmentContainer

* Default it to be off or bagtype 53 (BagTypeAugmentationSealer)
2023-02-06 17:30:16 -06:00
Chris Miles 25b4b97c41 [DB Updates] Add Windows MySQL path auto detection for users where the path is not found (#2836) 2023-02-06 17:25:34 -06:00
Joel 839f31b24d [Rule] Added rule to bypass level based haste caps (#2835) 2023-02-06 17:25:17 -06:00
Chris Miles 20728c31c4 [Crash] Fix crash in bot command botdyearmor (#2832)
* [Crash] Fix crash in bot command botdyearmor

* Update bot_command.cpp
2023-02-06 17:24:49 -06:00
Chris Miles c6eb12ac16 [Crash] Fix IsUnderwaterOnly crash where npc data references can be stale (#2830)
* [Crash] Fix IsUnderwaterOnly crash where npc data references can be stale

* m_ prefix
2023-02-06 17:24:38 -06:00
Chris Miles 34d21d4056 [Crash] Fix command crash with #npcedit weapon when second weapon not passed ni (#2829) 2023-02-06 17:24:24 -06:00
Chris Miles d369b47ef4 [Tasks] Implement alternate currency rewards (#2827)
* Reward currency on task completion

* Handle reward window

* Tweaks
2023-02-06 17:24:13 -06:00
Chris Miles 404f7cada8 [Pathing] Improvements to handling tight corridors pathing, clipping detection and recovery (#2826) 2023-02-06 17:24:03 -06:00
Chris Miles 823e73336d [Command] #list now searches without case sensitivity (#2825) 2023-02-06 17:23:50 -06:00
Chris Miles 0da6391be3 [Doors] Remove door dev tools spam on client controlled doors (#2824)
* [Doors] Remove door dev tools spam on client controlled doors

* Update client_packet.cpp

* Update client_packet.cpp

* Update ruletypes.h
2023-02-06 17:23:40 -06:00
Chris Miles 0348cb6b8e [Fix] Fix NPC ghosting at safe coordinates (#2823)
* [Fix] Fix NPC ghosting at safe coordinates

* Tweak order

* Handle another case
2023-02-06 17:23:29 -06:00
Vayle b385a4385f [Rules] Add rule to ignore name filter on chat channel creation. (#2820)
* [Rules] Add rule to ignore name filter on chat channel creation.

* Conditional reorder
2023-02-06 17:22:12 -06:00
Chris Miles 6a9228ed6e [Lua] Resolve stoi Exception (#2736)
* [Lua] Resolve stoi Exception

* Change fallback for wp, not really needed for safety

* Change to Strings::ToInt
2023-02-06 17:22:01 -06:00
Vayle 8031bf0bcb [Quest API] Add EVENT_TASKACCEPTED to Player scope (#2822)
* Add EVENT_TASKACCEPTED to player scope

* Formatting
2023-02-05 22:53:37 -05:00
Alex King c1584da9cc [Quest API] Default ScaleNPC to always scale. (#2818)
* [Quest API] Default ScaleNPC to always scale.

# Notes
- ScaleNPC will now always override stats, with the option to override special abilities.

* Update npc_scale_manager.h
2023-02-05 22:52:52 -05:00
Alex King ee6f6f683c [Commands] Remove extraneous else from #weather (#2819)
# Notes
- Condition falls back to sending message and can't turn weather off.
2023-02-01 06:05:32 -05:00
Akkadius 60707a14db [Hotfix] Post revert build fix for https://github.com/EQEmu/Server/commit/54050924d81d1f83268fe01f9c2b36fe10626601 2023-01-31 20:37:13 -06:00
Akkadius 54050924d8 Revert "[Quest API] Cleanup string copies and push_backs. (#2807)"
This reverts commit bcc2e022dc.
2023-01-31 20:30:34 -06:00
Aeadoin f727c9f75a [Bug Fix] Fix does_augment_fit_slot method. (#2817)
* [Bug Fix] DoesAugmentFit finds if an Aug Slot is free, changed overload method to check if Aug fits slot.

* Tweak/add lua
2023-01-31 21:11:12 -05:00
Alex King f410c89815 [Quest API] Add Override Parameters to ScaleNPC() in Perl/Lua. (#2816)
# Perl
- Add `$npc->ScaleNPC(level, always_scale_stats)`.
- Add `$npc->ScaleNPC(level, always_scale_stats, always_scale_special_abilities)`.

# Lua
- Add `npc:ScaleNPC(level, always_scale_stats)`.
- Add `npc:ScaleNPC(level, always_scale_stats, always_scale_special_abilities)`.

# Notes
- Allows operators to not have to set stats to 0 in order for scaling to kick in when scripting.
- Special ability override is separate in case you don't want to override some inherent special abilities the NPC has from a script or otherwise.
2023-01-31 21:11:05 -05:00
Natedog2012 2e575652f6 [Bug fix]#reload static should now properly fill the entity_lists for… (#2815)
* [Bug fix]#reload static should now properly fill the entity_lists for objects / doors / groundspawns
The individual #reload commands WILL still have issues when trying to use #list afterwards!

* Point ReloadDoors, ReloadGroundSpawns, ReloadObjects all to reload static to avoid entity_list data missing
2023-01-31 16:32:25 -06:00
Natedog2012 8e831dce36 [Bug fix]#reload aa will now refresh the AA table properly for every client when changes are made (#2814) 2023-01-30 20:24:02 -06:00
Alex King 040c092795 [Quest API] Add Augment Slot support to does_augment_fit (#2813)
* [Quest API] Add Augment Slot support to does_augment_fit

# Notes
- Allows you to check if the supplied augment ID fits in the specified augment slot of the item instance provided.

* Update item_instance.cpp
2023-01-30 21:18:16 -05:00
Alex King a25952910a [Quest API] Add EVENT_ITEM_CLICK_CLIENT and EVENT_ITEM_CLICK_CAST_CLIENT to Perl/Lua. (#2810)
* [Quest API] Add EVENT_ITEM_CLICK_CLIENT and EVENT_ITEM_CLICK_CAST_CLIENT to Perl/Lua.

# Perl
- Add `EVENT_ITEM_CLICK_CLIENT`.
- Add `EVENT_ITEM_CLICK_CAST_CLIENT`.
- Both events export `$item_id`, `$item_name`, `$slot_id`, and `$spell_id`.

# Lua
- Add `event_item_click_client`.
- Add `event_item_click_cast_client`.
- Both events export `e.item_id`, `e.item_name`, `e.slot_id`, `e.spell_id`, and `e.item`.

# Notes
- Allows operators to handle item clicks in player scripts instead of item-specific scripts.

* Update lua_parser_events.cpp

* Remove optional bool.
2023-01-30 05:01:12 -06:00
Natedog2012 66896a3121 [Quest API] Add GetItemCooldown to return the time remaining on items… (#2811)
* [Quest API] Add GetItemCooldown to return the time remaining on items in seconds

* Change GetItemCooldown to uint32 for timers up to 130 years
2023-01-30 00:04:06 -06:00
Paul Coene 4d2418af9d [Bug Fix] BuffLevelRestrictions were restricting group buffs if mob targeted (#2809) 2023-01-29 18:52:03 -05:00
Chris Miles 265b32f46f [Readme] Update build badges with Drone 2023-01-29 17:06:48 -06:00
Alex King 1cde55c535 [Quest API] Add LDoN Methods to Perl/Lua (#2799)
# Perl
- Add `$npc->GetLDoNLockedSkill()`.
- Add `$npc->GetLDoNTrapType()`.
- Add `$npc->GetLDoNTrapSpellID()`.
- Add `$npc->IsLDoNLocked()`.
- Add `$npc->IsLDoNTrapped()`.
- Add `$npc->IsLDoNTrapDetected()`.
- Add `$npc->SetLDoNLocked(is_locked)`.
- Add `$npc->SetLDoNLockedSkill(skill_value)`.
- Add `$npc->SetLDoNTrapped(is_trapped)`.
- Add `$npc->SetLDoNTrapDetected(is_detected)`.
- Add `$npc->SetLDoNTrapSpellID(spell_id)`.
- Add `$npc->SetLDoNTrapType(trap_type)`.

# Lua
- Add `npc:GetLDoNLockedSkill()`.
- Add `npc:GetLDoNTrapType()`.
- Add `npc:GetLDoNTrapSpellID()`.
- Add `npc:IsLDoNLocked()`.
- Add `npc:IsLDoNTrapped()`.
- Add `npc:IsLDoNTrapDetected()`.
- Add `npc:SetLDoNLocked(is_locked)`.
- Add `npc:SetLDoNLockedSkill(skill_value)`.
- Add `npc:SetLDoNTrapped(is_trapped)`.
- Add `npc:SetLDoNTrapDetected(is_detected)`.
- Add `npc:SetLDoNTrapSpellID(spell_id)`.
- Add `npc:SetLDoNTrapType(trap_type)`.

# Notes
- Adds these methods to allow LDoN traps to be set by a script.
2023-01-29 14:29:31 -06:00
Michael 369b5c2921 [Bug] Fixing % based mob see invis (#2802)
This was preventing anything other than 0 or 1 to be passed, but per the current design, the see_invis can be anything from 0 to MAX 25499, which can give you level 254 invis. See design notes in Mob::GetSeeInvisibleLevelFromNPCStat.
2023-01-29 14:26:55 -06:00
Alex King bcc2e022dc [Quest API] Cleanup string copies and push_backs. (#2807)
# Notes
- Several places use `push_back` instead of `emplace_back`.
- Several places use `std::string` instead of `const std::string&`.
2023-01-29 15:14:49 -05:00
Alex King 0fef46a6c1 [Feature] Add Min/Max Status to Merchants (#2806)
# Notes
- Allows operators to set a minimum and maximum status that an item will show for players.
- Allows operators to have items on a merchant that only a GM can see.
- Some servers may use status for different things, so having a minimum and a maximum will allow for more functionality.
- Default of `min_status` is `0` (Player) and default of `max_status` is `255` (Max).
2023-01-29 15:03:41 -05:00
Alex King b867d40774 [Quest API] Add EVENT_DAMAGE_GIVEN and EVENT_DAMAGE_TAKEN to Perl/Lua. (#2804)
* [Quest API] Add EVENT_DAMAGE_GIVEN and EVENT_DAMAGE_TAKEN to Perl/Lua.

# Perl
- Add `EVENT_DAMAGE_GIVEN`.
- Add `EVENT_DAMAGE_TAKEN`.
- Both events export `$entity_id`, `$damage`, `$spell_id`, `$skill_id`, `$is_damage_shield`, `$is_avoidable`, `$buff_slot`, `$is_buff_tic`, `$special_attack`.

# Lua
- Add `event_damage_given`.
- Add `event_damage_taken`.
- Both events export `e.entity_id`, `e.damage`, `e.spell_id`, `e.skill_id`, `e.is_damage_shield`, `e.is_avoidable`, `e.buff_slot`, `e.is_buff_tic`, `e.special_attack`, and `e.other`.

# Notes
- These events allow operators to have events fire based on damage given or taken, as well as keep better track of a Bot, Client, or NPC's damage per second or otherwise.
- Special Attack is only useful for NPCs, but allows you to see if the attack is Rampage, AERampage, or Chaotic Stab.

* Cleanup.
2023-01-29 14:35:17 -05:00
Aeadoin a489290eba [Bots] Add GetAugmentIDsBySlotID & AddItem with table ref Methods. (#2805)
* [Bots] Add GetAugmentIDsBySlotID & AddItem with table ref Methods.

* Return invalid slots to owner.
2023-01-29 12:49:44 -05:00
Alex King 549d731849 [Bug Fix] Resolve issue with max buff count being 25 in ROF2. (#2800)
# Notes
- This allows ROF2 to properly utilize their max buff count.
- May cause issues with older clients.
2023-01-28 17:40:11 -06:00
Natedog2012 68a34565f9 [Bugfix] Add SetItemCooldown to tell client exact timer of item when clicked and export to perl and lua (#2795)
Items that are on cooldown but client doesn't show.. clicking item will fix the timer in client
2023-01-27 17:31:14 -06:00
Aeadoin c05f951f81 [Bots] Add Override methods for GetMax Buffs/Songs/Total slots (#2801) 2023-01-27 18:00:50 -05:00
159 changed files with 10715 additions and 3026 deletions
+78
View File
@@ -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
+3 -3
View File
@@ -1,7 +1,7 @@
# EQEmulator Core Server
|Travis CI (Linux)|Appveyor (Windows x86) |Appveyor (Windows x64) |
|:---:|:---:|:---:|
|[![Linux CI](https://travis-ci.org/EQEmu/Server.svg?branch=master)](https://travis-ci.org/EQEmu/Server) |[![Build status](https://ci.appveyor.com/api/projects/status/v3utuu0dttm2cqd0?svg=true)](https://ci.appveyor.com/project/KimLS/server) |[![Build status](https://ci.appveyor.com/api/projects/status/scr25kmntx36c1ub?svg=true)](https://ci.appveyor.com/project/KimLS/server-87crp) |
| Drone (Linux x64) | Drone (Windows x64) |
|:---:|:---:|
|[![Build Status](http://drone.akkadius.com/api/badges/EQEmu/Server/status.svg)](http://drone.akkadius.com/EQEmu/Server) |[![Build Status](http://drone.akkadius.com/api/badges/EQEmu/Server/status.svg)](http://drone.akkadius.com/EQEmu/Server) |
***
+13 -8
View File
@@ -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
-38
View File
@@ -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;
-3
View File
@@ -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,
+3 -2
View File
@@ -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) */
+2 -2
View File
@@ -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
View File
@@ -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;
}
+5
View File
@@ -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
View File
@@ -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
View File
@@ -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
+703
View File
@@ -0,0 +1,703 @@
#include <cereal/archives/json.hpp>
#include "player_event_logs.h"
#include "player_event_discord_formatter.h"
#include "../platform.h"
#include "../rulesys.h"
const uint32 PROCESS_RETENTION_TRUNCATION_TIMER_INTERVAL = 60 * 60 * 1000; // 1 hour
// general initialization routine
void PlayerEventLogs::Init()
{
m_process_batch_events_timer.SetTimer(RuleI(Logging, BatchPlayerEventProcessIntervalSeconds) * 1000);
m_process_retention_truncation_timer.SetTimer(PROCESS_RETENTION_TRUNCATION_TIMER_INTERVAL);
ValidateDatabaseConnection();
// initialize settings array
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
m_settings[i].id = i;
m_settings[i].event_name = PlayerEvent::EventName[i];
m_settings[i].event_enabled = 1;
m_settings[i].retention_days = 0;
m_settings[i].discord_webhook_id = 0;
}
SetSettingsDefaults();
// initialize settings from database
auto s = PlayerEventLogSettingsRepository::All(*m_database);
std::vector<int> db{};
db.reserve(s.size());
for (auto &e: s) {
m_settings[e.id] = e;
db.emplace_back(e.id);
}
// insert entries that don't exist in database
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
bool is_in_database = std::find(db.begin(), db.end(), i) != db.end();
bool is_deprecated = Strings::Contains(PlayerEvent::EventName[i], "Deprecated");
bool is_implemented = !Strings::Contains(PlayerEvent::EventName[i], "Unimplemented");
// remove when deprecated
if (is_deprecated && is_in_database) {
LogInfo("[Deprecated] Removing PlayerEvent [{}] ({})", PlayerEvent::EventName[i], i);
PlayerEventLogSettingsRepository::DeleteWhere(*m_database, fmt::format("id = {}", i));
}
// remove when unimplemented if present
if (!is_implemented && is_in_database) {
LogInfo("[Unimplemented] Removing PlayerEvent [{}] ({})", PlayerEvent::EventName[i], i);
PlayerEventLogSettingsRepository::DeleteWhere(*m_database, fmt::format("id = {}", i));
}
bool is_missing_in_database = std::find(db.begin(), db.end(), i) == db.end();
if (is_missing_in_database && is_implemented && !is_deprecated) {
LogInfo(
"[New] PlayerEvent [{}] ({})",
PlayerEvent::EventName[i],
i
);
auto c = PlayerEventLogSettingsRepository::NewEntity();
c.id = i;
c.event_name = PlayerEvent::EventName[i];
c.event_enabled = m_settings[i].event_enabled;
c.retention_days = m_settings[i].retention_days;
PlayerEventLogSettingsRepository::InsertOne(*m_database, c);
}
}
bool processing_in_world = !RuleB(Logging, PlayerEventsQSProcess) && IsWorld();
bool processing_in_qs = RuleB(Logging, PlayerEventsQSProcess) && IsQueryServ();
// on initial boot process truncation
if (processing_in_world || processing_in_qs) {
ProcessRetentionTruncation();
}
}
// set the database object, during initialization
PlayerEventLogs *PlayerEventLogs::SetDatabase(Database *db)
{
m_database = db;
return this;
}
// validates whether the connection is valid or not, used in initialization
bool PlayerEventLogs::ValidateDatabaseConnection()
{
if (!m_database) {
LogError("No database connection");
return false;
}
return true;
}
// determines if the passed in event is enabled or not
// this is used to gate logic or events from firing off
// this is used prior to building the events, we don't want to
// build the events, send them through the stack in a function call
// only to discard them immediately afterwards, very wasteful on resources
// the quest api currently does this
bool PlayerEventLogs::IsEventEnabled(PlayerEvent::EventType event)
{
return m_settings[event].event_enabled ? m_settings[event].event_enabled : false;
}
// this processes any current player events on the queue
void PlayerEventLogs::ProcessBatchQueue()
{
if (m_record_batch_queue.empty()) {
return;
}
BenchTimer benchmark;
// flush many
PlayerEventLogsRepository::InsertMany(*m_database, m_record_batch_queue);
LogInfo(
"Processing batch player event log queue of [{}] took [{}]",
m_record_batch_queue.size(),
benchmark.elapsed()
);
// empty
m_batch_queue_lock.lock();
m_record_batch_queue = {};
m_batch_queue_lock.unlock();
}
// adds a player event to the queue
void PlayerEventLogs::AddToQueue(const PlayerEventLogsRepository::PlayerEventLogs &log)
{
m_batch_queue_lock.lock();
m_record_batch_queue.emplace_back(log);
m_batch_queue_lock.unlock();
}
// fills common event data in the SendEvent function
void PlayerEventLogs::FillPlayerEvent(
const PlayerEvent::PlayerEvent &p,
PlayerEventLogsRepository::PlayerEventLogs &n
)
{
n.account_id = p.account_id;
n.character_id = p.character_id;
n.zone_id = p.zone_id;
n.instance_id = p.instance_id;
n.x = p.x;
n.y = p.y;
n.z = p.z;
n.heading = p.heading;
}
// builds the dynamic packet used to ship the player event over the wire
// supports serializing the struct so it can be rebuilt on the other end
std::unique_ptr<ServerPacket>
PlayerEventLogs::BuildPlayerEventPacket(const PlayerEvent::PlayerEventContainer &e)
{
EQ::Net::DynamicPacket dyn_pack;
dyn_pack.PutSerialize(0, e);
auto pack_size = sizeof(ServerSendPlayerEvent_Struct) + dyn_pack.Length();
auto pack = std::make_unique<ServerPacket>(ServerOP_PlayerEvent, static_cast<uint32_t>(pack_size));
auto buf = reinterpret_cast<ServerSendPlayerEvent_Struct *>(pack->pBuffer);
buf->cereal_size = static_cast<uint32_t>(dyn_pack.Length());
memcpy(buf->cereal_data, dyn_pack.Data(), dyn_pack.Length());
return pack;
}
const PlayerEventLogSettingsRepository::PlayerEventLogSettings *PlayerEventLogs::GetSettings() const
{
return m_settings;
}
bool PlayerEventLogs::IsEventDiscordEnabled(int32_t event_type_id)
{
// out of bounds check
if (event_type_id >= PlayerEvent::EventType::MAX) {
return false;
}
// make sure webhook id is set
if (m_settings[event_type_id].discord_webhook_id == 0) {
return false;
}
// ensure there is a matching webhook to begin with
if (!LogSys.GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url.empty()) {
return true;
}
return false;
}
std::string PlayerEventLogs::GetDiscordWebhookUrlFromEventType(int32_t event_type_id)
{
// out of bounds check
if (event_type_id >= PlayerEvent::EventType::MAX) {
return "";
}
// make sure webhook id is set
if (m_settings[event_type_id].discord_webhook_id == 0) {
return "";
}
// ensure there is a matching webhook to begin with
if (!LogSys.GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url.empty()) {
return LogSys.GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url;
}
return "";
}
// GM_COMMAND | [x] Implemented Formatter
// ZONING | [x] Implemented Formatter
// AA_GAIN | [x] Implemented Formatter
// AA_PURCHASE | [x] Implemented Formatter
// FORAGE_SUCCESS | [x] Implemented Formatter
// FORAGE_FAILURE | [x] Implemented Formatter
// FISH_SUCCESS | [x] Implemented Formatter
// FISH_FAILURE | [x] Implemented Formatter
// ITEM_DESTROY | [x] Implemented Formatter
// WENT_ONLINE | [x] Implemented Formatter
// WENT_OFFLINE | [x] Implemented Formatter
// LEVEL_GAIN | [x] Implemented Formatter
// LEVEL_LOSS | [x] Implemented Formatter
// LOOT_ITEM | [x] Implemented Formatter
// MERCHANT_PURCHASE | [x] Implemented Formatter
// MERCHANT_SELL | [x] Implemented Formatter
// GROUP_JOIN | [] Implemented Formatter
// GROUP_LEAVE | [] Implemented Formatter
// RAID_JOIN | [] Implemented Formatter
// RAID_LEAVE | [] Implemented Formatter
// GROUNDSPAWN_PICKUP | [x] Implemented Formatter
// NPC_HANDIN | [x] Implemented Formatter
// SKILL_UP | [x] Implemented Formatter
// TASK_ACCEPT | [x] Implemented Formatter
// TASK_UPDATE | [x] Implemented Formatter
// TASK_COMPLETE | [x] Implemented Formatter
// TRADE | [] Implemented Formatter
// GIVE_ITEM | [] Implemented Formatter
// SAY | [x] Implemented Formatter
// REZ_ACCEPTED | [x] Implemented Formatter
// DEATH | [x] Implemented Formatter
// COMBINE_FAILURE | [x] Implemented Formatter
// COMBINE_SUCCESS | [x] Implemented Formatter
// DROPPED_ITEM | [x] Implemented Formatter
// SPLIT_MONEY | [x] Implemented Formatter
// DZ_JOIN | [] Implemented Formatter
// DZ_LEAVE | [] Implemented Formatter
// TRADER_PURCHASE | [x] Implemented Formatter
// TRADER_SELL | [x] Implemented Formatter
// BANDOLIER_CREATE | [] Implemented Formatter
// BANDOLIER_SWAP | [] Implemented Formatter
// DISCOVER_ITEM | [X] Implemented Formatter
std::string PlayerEventLogs::GetDiscordPayloadFromEvent(const PlayerEvent::PlayerEventContainer &e)
{
std::string payload;
switch (e.player_event_log.event_type_id) {
case PlayerEvent::AA_GAIN: {
PlayerEvent::AAGainedEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatAAGainedEvent(e, n);
break;
}
case PlayerEvent::AA_PURCHASE: {
PlayerEvent::AAPurchasedEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatAAPurchasedEvent(e, n);
break;
}
case PlayerEvent::COMBINE_FAILURE:
case PlayerEvent::COMBINE_SUCCESS: {
PlayerEvent::CombineEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatCombineEvent(e, n);
break;
}
case PlayerEvent::DEATH: {
PlayerEvent::DeathEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatDeathEvent(e, n);
break;
}
case PlayerEvent::DISCOVER_ITEM: {
PlayerEvent::DiscoverItemEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatDiscoverItemEvent(e, n);
break;
}
case PlayerEvent::DROPPED_ITEM: {
PlayerEvent::DroppedItemEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatDroppedItemEvent(e, n);
break;
}
case PlayerEvent::FISH_FAILURE: {
payload = PlayerEventDiscordFormatter::FormatWithNodata(e);
break;
}
case PlayerEvent::FISH_SUCCESS: {
PlayerEvent::FishSuccessEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatFishSuccessEvent(e, n);
break;
}
case PlayerEvent::FORAGE_FAILURE: {
payload = PlayerEventDiscordFormatter::FormatWithNodata(e);
break;
}
case PlayerEvent::FORAGE_SUCCESS: {
PlayerEvent::ForageSuccessEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatForageSuccessEvent(e, n);
break;
}
case PlayerEvent::ITEM_DESTROY: {
PlayerEvent::DestroyItemEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatDestroyItemEvent(e, n);
break;
}
case PlayerEvent::LEVEL_GAIN: {
PlayerEvent::LevelGainedEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatLevelGainedEvent(e, n);
break;
}
case PlayerEvent::LEVEL_LOSS: {
PlayerEvent::LevelLostEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatLevelLostEvent(e, n);
break;
}
case PlayerEvent::LOOT_ITEM: {
PlayerEvent::LootItemEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatLootItemEvent(e, n);
break;
}
case PlayerEvent::GROUNDSPAWN_PICKUP: {
PlayerEvent::GroundSpawnPickupEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatGroundSpawnPickupEvent(e, n);
break;
}
case PlayerEvent::NPC_HANDIN: {
PlayerEvent::HandinEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatNPCHandinEvent(e, n);
break;
}
case PlayerEvent::SAY: {
PlayerEvent::SayEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatEventSay(e, n);
break;
}
case PlayerEvent::GM_COMMAND: {
PlayerEvent::GMCommandEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatGMCommand(e, n);
break;
}
case PlayerEvent::SKILL_UP: {
PlayerEvent::SkillUpEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatSkillUpEvent(e, n);
break;
}
case PlayerEvent::SPLIT_MONEY: {
PlayerEvent::SplitMoneyEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatSplitMoneyEvent(e, n);
break;
}
case PlayerEvent::TASK_ACCEPT: {
PlayerEvent::TaskAcceptEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatTaskAcceptEvent(e, n);
break;
}
case PlayerEvent::TASK_COMPLETE: {
PlayerEvent::TaskCompleteEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatTaskCompleteEvent(e, n);
break;
}
case PlayerEvent::TASK_UPDATE: {
PlayerEvent::TaskUpdateEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatTaskUpdateEvent(e, n);
break;
}
case PlayerEvent::TRADE: {
PlayerEvent::TradeEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatTradeEvent(e, n);
break;
}
case PlayerEvent::TRADER_PURCHASE: {
PlayerEvent::TraderPurchaseEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatTraderPurchaseEvent(e, n);
break;
}
case PlayerEvent::TRADER_SELL: {
PlayerEvent::TraderSellEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatTraderSellEvent(e, n);
break;
}
case PlayerEvent::REZ_ACCEPTED: {
PlayerEvent::ResurrectAcceptEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatResurrectAcceptEvent(e, n);
break;
}
case PlayerEvent::WENT_ONLINE:
case PlayerEvent::WENT_OFFLINE: {
payload = PlayerEventDiscordFormatter::FormatWithNodata(e);
break;
}
case PlayerEvent::MERCHANT_PURCHASE: {
PlayerEvent::MerchantPurchaseEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatMerchantPurchaseEvent(e, n);
break;
}
case PlayerEvent::MERCHANT_SELL: {
PlayerEvent::MerchantSellEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatMerchantSellEvent(e, n);
break;
}
case PlayerEvent::ZONING: {
PlayerEvent::ZoningEvent n{};
std::stringstream ss;
{
ss << e.player_event_log.event_data;
cereal::JSONInputArchive ar(ss);
n.serialize(ar);
}
payload = PlayerEventDiscordFormatter::FormatZoningEvent(e, n);
break;
}
default: {
LogInfo(
"Player event [{}] ({}) Discord formatter not implemented",
e.player_event_log.event_type_name,
e.player_event_log.event_type_id
);
}
}
return payload;
}
// general process function, used in world or UCS depending on rule Logging:PlayerEventsQSProcess
void PlayerEventLogs::Process()
{
if (m_process_batch_events_timer.Check()) {
ProcessBatchQueue();
}
if (m_process_retention_truncation_timer.Check()) {
ProcessRetentionTruncation();
}
}
void PlayerEventLogs::ProcessRetentionTruncation()
{
LogInfo("Running truncation");
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
if (m_settings[i].retention_days > 0) {
int deleted_count = PlayerEventLogsRepository::DeleteWhere(
*m_database,
fmt::format(
"event_type_id = {} AND created_at < (NOW() - INTERVAL {} DAY)",
i,
m_settings[i].retention_days
)
);
if (deleted_count > 0) {
LogInfo(
"Truncated [{}] events of type [{}] ({}) older than [{}] days",
deleted_count,
PlayerEvent::EventName[i],
i,
m_settings[i].retention_days
);
}
}
}
}
void PlayerEventLogs::ReloadSettings()
{
for (auto &e: PlayerEventLogSettingsRepository::All(*m_database)) {
m_settings[e.id] = e;
}
}
const int32_t RETENTION_DAYS_DEFAULT = 7;
void PlayerEventLogs::SetSettingsDefaults()
{
m_settings[PlayerEvent::GM_COMMAND].event_enabled = 1;
m_settings[PlayerEvent::ZONING].event_enabled = 1;
m_settings[PlayerEvent::AA_GAIN].event_enabled = 1;
m_settings[PlayerEvent::AA_PURCHASE].event_enabled = 1;
m_settings[PlayerEvent::FORAGE_SUCCESS].event_enabled = 0;
m_settings[PlayerEvent::FORAGE_FAILURE].event_enabled = 0;
m_settings[PlayerEvent::FISH_SUCCESS].event_enabled = 0;
m_settings[PlayerEvent::FISH_FAILURE].event_enabled = 0;
m_settings[PlayerEvent::ITEM_DESTROY].event_enabled = 1;
m_settings[PlayerEvent::WENT_ONLINE].event_enabled = 0;
m_settings[PlayerEvent::WENT_OFFLINE].event_enabled = 0;
m_settings[PlayerEvent::LEVEL_GAIN].event_enabled = 1;
m_settings[PlayerEvent::LEVEL_LOSS].event_enabled = 1;
m_settings[PlayerEvent::LOOT_ITEM].event_enabled = 1;
m_settings[PlayerEvent::MERCHANT_PURCHASE].event_enabled = 1;
m_settings[PlayerEvent::MERCHANT_SELL].event_enabled = 1;
m_settings[PlayerEvent::GROUP_JOIN].event_enabled = 0;
m_settings[PlayerEvent::GROUP_LEAVE].event_enabled = 0;
m_settings[PlayerEvent::RAID_JOIN].event_enabled = 0;
m_settings[PlayerEvent::RAID_LEAVE].event_enabled = 0;
m_settings[PlayerEvent::GROUNDSPAWN_PICKUP].event_enabled = 1;
m_settings[PlayerEvent::NPC_HANDIN].event_enabled = 1;
m_settings[PlayerEvent::SKILL_UP].event_enabled = 0;
m_settings[PlayerEvent::TASK_ACCEPT].event_enabled = 1;
m_settings[PlayerEvent::TASK_UPDATE].event_enabled = 1;
m_settings[PlayerEvent::TASK_COMPLETE].event_enabled = 1;
m_settings[PlayerEvent::TRADE].event_enabled = 1;
m_settings[PlayerEvent::GIVE_ITEM].event_enabled = 1;
m_settings[PlayerEvent::SAY].event_enabled = 0;
m_settings[PlayerEvent::REZ_ACCEPTED].event_enabled = 1;
m_settings[PlayerEvent::DEATH].event_enabled = 1;
m_settings[PlayerEvent::COMBINE_FAILURE].event_enabled = 1;
m_settings[PlayerEvent::COMBINE_SUCCESS].event_enabled = 1;
m_settings[PlayerEvent::DROPPED_ITEM].event_enabled = 1;
m_settings[PlayerEvent::SPLIT_MONEY].event_enabled = 1;
m_settings[PlayerEvent::DZ_JOIN].event_enabled = 1;
m_settings[PlayerEvent::DZ_LEAVE].event_enabled = 1;
m_settings[PlayerEvent::TRADER_PURCHASE].event_enabled = 1;
m_settings[PlayerEvent::TRADER_SELL].event_enabled = 1;
m_settings[PlayerEvent::BANDOLIER_CREATE].event_enabled = 0;
m_settings[PlayerEvent::BANDOLIER_SWAP].event_enabled = 0;
m_settings[PlayerEvent::DISCOVER_ITEM].event_enabled = 1;
m_settings[PlayerEvent::POSSIBLE_HACK].event_enabled = 1;
m_settings[PlayerEvent::KILLED_NPC].event_enabled = 1;
m_settings[PlayerEvent::KILLED_NAMED_NPC].event_enabled = 1;
m_settings[PlayerEvent::KILLED_RAID_NPC].event_enabled = 1;
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
m_settings[i].retention_days = RETENTION_DAYS_DEFAULT;
}
}
+85
View File
@@ -0,0 +1,85 @@
#ifndef EQEMU_PLAYER_EVENT_LOGS_H
#define EQEMU_PLAYER_EVENT_LOGS_H
#include "../repositories/player_event_log_settings_repository.h"
#include "player_events.h"
#include "../servertalk.h"
#include "../repositories/player_event_logs_repository.h"
#include "../timer.h"
#include "../json/json_archive_single_line.h"
#include <cereal/archives/json.hpp>
#include <mutex>
class PlayerEventLogs {
public:
void Init();
void ReloadSettings();
PlayerEventLogs *SetDatabase(Database *db);
bool ValidateDatabaseConnection();
bool IsEventEnabled(PlayerEvent::EventType event);
void Process();
// batch queue
void AddToQueue(const PlayerEventLogsRepository::PlayerEventLogs &logs);
// main event record generic function
// can ingest any struct event types
template<typename T>
std::unique_ptr<ServerPacket> RecordEvent(
PlayerEvent::EventType t,
const PlayerEvent::PlayerEvent &p,
T e
)
{
auto n = PlayerEventLogsRepository::NewEntity();
FillPlayerEvent(p, n);
n.event_type_id = t;
std::stringstream ss;
{
cereal::JSONOutputArchiveSingleLine ar(ss);
e.serialize(ar);
}
n.event_type_name = PlayerEvent::EventName[t];
n.event_data = Strings::Contains(ss.str(), "noop") ? "{}" : ss.str();
n.created_at = std::time(nullptr);
auto c = PlayerEvent::PlayerEventContainer{
.player_event = p,
.player_event_log = n
};
return BuildPlayerEventPacket(c);
}
[[nodiscard]] const PlayerEventLogSettingsRepository::PlayerEventLogSettings *GetSettings() const;
bool IsEventDiscordEnabled(int32_t event_type_id);
std::string GetDiscordWebhookUrlFromEventType(int32_t event_type_id);
static std::string GetDiscordPayloadFromEvent(const PlayerEvent::PlayerEventContainer &e);
private:
Database *m_database; // reference to database
PlayerEventLogSettingsRepository::PlayerEventLogSettings m_settings[PlayerEvent::EventType::MAX]{};
// batch queue is used to record events in batch
std::vector<PlayerEventLogsRepository::PlayerEventLogs> m_record_batch_queue{};
static void FillPlayerEvent(const PlayerEvent::PlayerEvent &p, PlayerEventLogsRepository::PlayerEventLogs &n);
static std::unique_ptr<ServerPacket>
BuildPlayerEventPacket(const PlayerEvent::PlayerEventContainer &e);
// timers
Timer m_process_batch_events_timer; // events processing timer
Timer m_process_retention_truncation_timer; // timer for truncating events based on retention settings
// processing
std::mutex m_batch_queue_lock{};
void ProcessBatchQueue();
void ProcessRetentionTruncation();
void SetSettingsDefaults();
};
extern PlayerEventLogs player_event_logs;
#endif //EQEMU_PLAYER_EVENT_LOGS_H
+935
View File
@@ -0,0 +1,935 @@
#ifndef EQEMU_PLAYER_EVENTS_H
#define EQEMU_PLAYER_EVENTS_H
#include <string>
#include <cereal/cereal.hpp>
#include "../types.h"
#include "../repositories/player_event_logs_repository.h"
namespace PlayerEvent {
enum EventType {
GM_COMMAND = 1,
ZONING,
AA_GAIN,
AA_PURCHASE,
FORAGE_SUCCESS,
FORAGE_FAILURE,
FISH_SUCCESS,
FISH_FAILURE,
ITEM_DESTROY,
WENT_ONLINE,
WENT_OFFLINE,
LEVEL_GAIN,
LEVEL_LOSS,
LOOT_ITEM,
MERCHANT_PURCHASE,
MERCHANT_SELL,
GROUP_JOIN, // unimplemented
GROUP_LEAVE, // unimplemented
RAID_JOIN, // unimplemented
RAID_LEAVE, // unimplemented
GROUNDSPAWN_PICKUP,
NPC_HANDIN,
SKILL_UP,
TASK_ACCEPT,
TASK_UPDATE,
TASK_COMPLETE,
TRADE,
GIVE_ITEM, // unimplemented
SAY,
REZ_ACCEPTED,
DEATH,
COMBINE_FAILURE,
COMBINE_SUCCESS,
DROPPED_ITEM,
SPLIT_MONEY,
DZ_JOIN, // unimplemented
DZ_LEAVE, // unimplemented
TRADER_PURCHASE,
TRADER_SELL,
BANDOLIER_CREATE, // unimplemented
BANDOLIER_SWAP, // unimplemented
DISCOVER_ITEM,
POSSIBLE_HACK,
KILLED_NPC,
KILLED_NAMED_NPC,
KILLED_RAID_NPC,
MAX // dont remove
};
// Don't ever remove items, even if they are deprecated
// If event is deprecated just tag (Deprecated) in the name
// If event is unimplemented just tag (Unimplemented) in the name
// Events don't get saved to the database if unimplemented or deprecated
// Events tagged as deprecated will get automatically removed
static const char *EventName[PlayerEvent::MAX] = {
"None",
"GM Command",
"Zoning",
"AA Gain",
"AA Purchase",
"Forage Success",
"Forage Failure",
"Fish Success",
"Fish Failure",
"Item Destroy",
"Went Online",
"Went Offline",
"Level Gain",
"Level Loss",
"Loot Item",
"Merchant Purchase",
"Merchant Sell",
"Group Join (Unimplemented)",
"Group Leave (Unimplemented)",
"Raid Join (Unimplemented)",
"Raid Leave (Unimplemented)",
"Groundspawn Pickup",
"NPC Handin",
"Skill Up",
"Task Accept",
"Task Update",
"Task Complete",
"Trade",
"Given Item (Unimplemented)",
"Say",
"Rez Accepted",
"Death",
"Combine Failure",
"Combine Success",
"Dropped Item",
"Split Money",
"DZ Join (Unimplemented)",
"DZ Leave (Unimplemented)",
"Trader Purchase",
"Trader Sell",
"Bandolier Create (Unimplemented)",
"Bandolier Swap (Unimplemented)",
"Discover Item",
"Possible Hack",
"Killed NPC",
"Killed Named NPC",
"Killed Raid NPC"
};
// Generic struct used by all events
struct PlayerEvent {
int64 account_id;
std::string account_name;
int64 character_id;
std::string character_name;
int64 guild_id;
std::string guild_name;
int zone_id;
std::string zone_short_name;
std::string zone_long_name;
int instance_id;
float x;
float y;
float z;
float heading;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(account_id),
CEREAL_NVP(account_name),
CEREAL_NVP(character_id),
CEREAL_NVP(character_name),
CEREAL_NVP(guild_id),
CEREAL_NVP(guild_name),
CEREAL_NVP(zone_id),
CEREAL_NVP(zone_short_name),
CEREAL_NVP(zone_long_name),
CEREAL_NVP(instance_id),
CEREAL_NVP(x),
CEREAL_NVP(y),
CEREAL_NVP(z),
CEREAL_NVP(heading)
);
}
};
// contains metadata in use for things like log/discord formatters
// along with the actual event to be persisted
struct PlayerEventContainer {
PlayerEvent player_event;
PlayerEventLogsRepository::PlayerEventLogs player_event_log;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(player_event),
CEREAL_NVP(player_event_log)
);
}
};
// used in events with no extra data
struct EmptyEvent {
std::string noop; // noop, gets discard upstream
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(noop)
);
}
};
// used in Trade event
struct TradeItem {
int64 item_id;
std::string item_name;
int32 slot;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(item_id),
CEREAL_NVP(item_name),
CEREAL_NVP(slot)
);
}
};
// used in Trade event
class TradeItemEntry {
public:
uint16 slot;
uint32 item_id;
std::string item_name;
uint16 charges;
uint32 aug_1_item_id;
std::string aug_1_item_name;
uint32 aug_2_item_id;
std::string aug_2_item_name;
uint32 aug_3_item_id;
std::string aug_3_item_name;
uint32 aug_4_item_id;
std::string aug_4_item_name;
uint32 aug_5_item_id;
std::string aug_5_item_name;
uint32 aug_6_item_id;
std::string aug_6_item_name;
bool in_bag;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(slot),
CEREAL_NVP(item_id),
CEREAL_NVP(charges),
CEREAL_NVP(aug_1_item_id),
CEREAL_NVP(aug_2_item_id),
CEREAL_NVP(aug_3_item_id),
CEREAL_NVP(aug_4_item_id),
CEREAL_NVP(aug_5_item_id),
CEREAL_NVP(in_bag)
);
}
};
/**
* Events
*/
struct Money {
int32 platinum;
int32 gold;
int32 silver;
int32 copper;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(platinum),
CEREAL_NVP(gold),
CEREAL_NVP(silver),
CEREAL_NVP(copper)
);
}
};
struct TradeEvent {
uint32 character_1_id;
std::string character_1_name;
uint32 character_2_id;
std::string character_2_name;
Money character_1_give_money;
Money character_2_give_money;
std::vector<TradeItemEntry> character_1_give_items;
std::vector<TradeItemEntry> character_2_give_items;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(character_1_id),
CEREAL_NVP(character_1_name),
CEREAL_NVP(character_2_id),
CEREAL_NVP(character_2_name),
CEREAL_NVP(character_1_give_money),
CEREAL_NVP(character_2_give_money),
CEREAL_NVP(character_1_give_items),
CEREAL_NVP(character_2_give_items)
);
}
};
struct GMCommandEvent {
std::string message;
std::string target;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(message),
CEREAL_NVP(target)
);
}
};
struct ZoningEvent {
std::string from_zone_long_name;
std::string from_zone_short_name;
int32 from_zone_id;
int32 from_instance_id;
int32 from_instance_version;
std::string to_zone_long_name;
std::string to_zone_short_name;
int32 to_zone_id;
int32 to_instance_id;
int32 to_instance_version;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(from_zone_long_name),
CEREAL_NVP(from_zone_short_name),
CEREAL_NVP(from_zone_id),
CEREAL_NVP(from_instance_id),
CEREAL_NVP(from_instance_version),
CEREAL_NVP(to_zone_long_name),
CEREAL_NVP(to_zone_short_name),
CEREAL_NVP(to_zone_id),
CEREAL_NVP(to_instance_id),
CEREAL_NVP(to_instance_version)
);
}
};
struct AAGainedEvent {
uint32 aa_gained;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(CEREAL_NVP(aa_gained));
}
};
struct AAPurchasedEvent {
int32 aa_id;
int32 aa_cost;
int32 aa_previous_id;
int32 aa_next_id;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(aa_id),
CEREAL_NVP(aa_cost),
CEREAL_NVP(aa_previous_id),
CEREAL_NVP(aa_next_id)
);
}
};
struct ForageSuccessEvent {
uint32 item_id;
std::string item_name;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(item_id),
CEREAL_NVP(item_name)
);
}
};
struct FishSuccessEvent {
uint32 item_id;
std::string item_name;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(item_id),
CEREAL_NVP(item_name)
);
}
};
struct DestroyItemEvent {
uint32 item_id;
std::string item_name;
int16 charges;
std::string reason;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(item_id),
CEREAL_NVP(item_name),
CEREAL_NVP(reason),
CEREAL_NVP(charges)
);
}
};
struct LevelGainedEvent {
uint32 from_level;
uint8 to_level;
int levels_gained;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(from_level),
CEREAL_NVP(to_level),
CEREAL_NVP(levels_gained)
);
}
};
struct LevelLostEvent {
uint32 from_level;
uint8 to_level;
int levels_lost;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(from_level),
CEREAL_NVP(to_level),
CEREAL_NVP(levels_lost)
);
}
};
struct LootItemEvent {
uint32 item_id;
std::string item_name;
int16 charges;
uint32 npc_id;
std::string corpse_name;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(item_id),
CEREAL_NVP(item_name),
CEREAL_NVP(charges),
CEREAL_NVP(npc_id),
CEREAL_NVP(corpse_name)
);
}
};
struct MerchantPurchaseEvent {
uint32 npc_id;
std::string merchant_name;
uint32 merchant_type;
uint32 item_id;
std::string item_name;
int16 charges;
uint32 cost;
uint32 alternate_currency_id;
uint64 player_money_balance;
uint64 player_currency_balance;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(npc_id),
CEREAL_NVP(merchant_name),
CEREAL_NVP(merchant_type),
CEREAL_NVP(item_id),
CEREAL_NVP(item_name),
CEREAL_NVP(charges),
CEREAL_NVP(cost),
CEREAL_NVP(alternate_currency_id),
CEREAL_NVP(player_money_balance),
CEREAL_NVP(player_currency_balance)
);
}
};
struct MerchantSellEvent {
uint32 npc_id;
std::string merchant_name;
uint32 merchant_type;
uint32 item_id;
std::string item_name;
int16 charges;
uint32 cost;
uint32 alternate_currency_id;
uint64 player_money_balance;
uint64 player_currency_balance;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(npc_id),
CEREAL_NVP(merchant_name),
CEREAL_NVP(merchant_type),
CEREAL_NVP(item_id),
CEREAL_NVP(item_name),
CEREAL_NVP(charges),
CEREAL_NVP(cost),
CEREAL_NVP(alternate_currency_id),
CEREAL_NVP(player_money_balance),
CEREAL_NVP(player_currency_balance)
);
}
};
struct SkillUpEvent {
uint32 skill_id;
int value;
int16 max_skill;
std::string against_who;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(skill_id),
CEREAL_NVP(value),
CEREAL_NVP(max_skill),
CEREAL_NVP(against_who)
);
}
};
struct TaskAcceptEvent {
uint32 npc_id;
std::string npc_name;
uint32 task_id;
std::string task_name;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(npc_id),
CEREAL_NVP(npc_name),
CEREAL_NVP(task_id),
CEREAL_NVP(task_name)
);
}
};
struct TaskUpdateEvent {
uint32 task_id;
std::string task_name;
uint32 activity_id;
uint32 done_count;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(task_id),
CEREAL_NVP(task_name),
CEREAL_NVP(activity_id),
CEREAL_NVP(done_count)
);
}
};
struct TaskCompleteEvent {
uint32 task_id;
std::string task_name;
uint32 activity_id;
uint32 done_count;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(task_id),
CEREAL_NVP(task_name),
CEREAL_NVP(activity_id),
CEREAL_NVP(done_count)
);
}
};
struct GroundSpawnPickupEvent {
uint32 item_id;
std::string item_name;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(item_id),
CEREAL_NVP(item_name)
);
}
};
struct SayEvent {
std::string message;
std::string target;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(message),
CEREAL_NVP(target)
);
}
};
struct ResurrectAcceptEvent {
std::string resurrecter_name;
std::string spell_name;
uint32 spell_id;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(resurrecter_name),
CEREAL_NVP(spell_name),
CEREAL_NVP(spell_id)
);
}
};
struct CombineEvent {
uint32 recipe_id;
std::string recipe_name;
uint32 made_count;
uint32 tradeskill_id;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(recipe_id),
CEREAL_NVP(recipe_name),
CEREAL_NVP(made_count),
CEREAL_NVP(tradeskill_id)
);
}
};
struct DroppedItemEvent {
uint32 item_id;
std::string item_name;
int16 slot_id;
uint32 charges;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(item_id),
CEREAL_NVP(item_name),
CEREAL_NVP(slot_id),
CEREAL_NVP(charges)
);
}
};
struct DeathEvent {
uint32 killer_id;
std::string killer_name;
int64 damage;
uint32 spell_id;
std::string spell_name;
int skill_id;
std::string skill_name;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(killer_id),
CEREAL_NVP(killer_name),
CEREAL_NVP(damage),
CEREAL_NVP(spell_id),
CEREAL_NVP(spell_name),
CEREAL_NVP(skill_id),
CEREAL_NVP(skill_name)
);
}
};
struct SplitMoneyEvent {
uint32 copper;
uint32 silver;
uint32 gold;
uint32 platinum;
uint64 player_money_balance;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(copper),
CEREAL_NVP(silver),
CEREAL_NVP(gold),
CEREAL_NVP(platinum),
CEREAL_NVP(player_money_balance)
);
}
};
struct TraderPurchaseEvent {
uint32 item_id;
std::string item_name;
uint32 trader_id;
std::string trader_name;
uint32 price;
uint32 charges;
uint32 total_cost;
uint64 player_money_balance;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(item_id),
CEREAL_NVP(item_name),
CEREAL_NVP(trader_id),
CEREAL_NVP(trader_name),
CEREAL_NVP(price),
CEREAL_NVP(charges),
CEREAL_NVP(total_cost),
CEREAL_NVP(player_money_balance)
);
}
};
struct TraderSellEvent {
uint32 item_id;
std::string item_name;
uint32 buyer_id;
std::string buyer_name;
uint32 price;
uint32 charges;
uint32 total_cost;
uint64 player_money_balance;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(item_id),
CEREAL_NVP(item_name),
CEREAL_NVP(buyer_id),
CEREAL_NVP(buyer_name),
CEREAL_NVP(price),
CEREAL_NVP(charges),
CEREAL_NVP(total_cost),
CEREAL_NVP(player_money_balance)
);
}
};
struct DiscoverItemEvent {
uint32 item_id;
std::string item_name;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(item_id),
CEREAL_NVP(item_name)
);
}
};
class HandinEntry {
public:
uint32 item_id;
std::string item_name;
uint16 charges;
bool attuned;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(item_id),
CEREAL_NVP(item_name),
CEREAL_NVP(charges),
CEREAL_NVP(attuned)
);
}
};
class HandinMoney {
public:
uint32 copper;
uint32 silver;
uint32 gold;
uint32 platinum;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(copper),
CEREAL_NVP(silver),
CEREAL_NVP(gold),
CEREAL_NVP(platinum)
);
}
};
struct HandinEvent {
uint32 npc_id;
std::string npc_name;
std::vector<HandinEntry> handin_items;
HandinMoney handin_money;
std::vector<HandinEntry> return_items;
HandinMoney return_money;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(npc_id),
CEREAL_NVP(npc_name),
CEREAL_NVP(handin_items),
CEREAL_NVP(handin_money),
CEREAL_NVP(return_items),
CEREAL_NVP(return_money)
);
}
};
struct PossibleHackEvent {
std::string message;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(message)
);
}
};
struct KilledNPCEvent {
uint32 npc_id;
std::string npc_name;
uint32 combat_time_seconds;
uint64 total_damage_per_second_taken;
uint64 total_heal_per_second_taken;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(npc_id),
CEREAL_NVP(npc_name),
CEREAL_NVP(combat_time_seconds),
CEREAL_NVP(total_damage_per_second_taken),
CEREAL_NVP(total_heal_per_second_taken)
);
}
};
}
#endif //EQEMU_PLAYER_EVENTS_H
#define RecordPlayerEventLog(event_type, event_data) do {\
if (player_event_logs.IsEventEnabled(event_type)) {\
worldserver.SendPacket(\
player_event_logs.RecordEvent(\
event_type,\
GetPlayerEvent(),\
event_data\
).get()\
);\
}\
} while (0)
#define RecordPlayerEventLogWithClient(c, event_type, event_data) do {\
if (player_event_logs.IsEventEnabled(event_type)) {\
worldserver.SendPacket(\
player_event_logs.RecordEvent(\
event_type,\
(c)->GetPlayerEvent(),\
event_data\
).get()\
);\
}\
} while (0)
+17 -5
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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()) {
+2
View File
@@ -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);
}
@@ -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());
+1 -1
View File
@@ -62,7 +62,7 @@ public:
std::vector<CharacterExpedition> entries;
auto joined_character_names = fmt::format("'{}'", fmt::join(character_names, "','"));
auto joined_character_names = fmt::format("'{}'", Strings::Join(character_names, "','"));
auto results = db.QueryDatabase(fmt::format(SQL(
SELECT
@@ -1,11 +1,11 @@
#ifndef EQEMU_EVENTLOG_REPOSITORY_H
#define EQEMU_EVENTLOG_REPOSITORY_H
#ifndef EQEMU_PLAYER_EVENT_LOG_SETTINGS_REPOSITORY_H
#define EQEMU_PLAYER_EVENT_LOG_SETTINGS_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_eventlog_repository.h"
#include "base/base_player_event_log_settings_repository.h"
class EventlogRepository: public BaseEventlogRepository {
class PlayerEventLogSettingsRepository: public BasePlayerEventLogSettingsRepository {
public:
/**
@@ -32,10 +32,10 @@ public:
*
* Example custom methods in a repository
*
* EventlogRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* EventlogRepository::GetWhereNeverExpires()
* EventlogRepository::GetWhereXAndY()
* EventlogRepository::DeleteWhereXAndY()
* PlayerEventLogSettingsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* PlayerEventLogSettingsRepository::GetWhereNeverExpires()
* PlayerEventLogSettingsRepository::GetWhereXAndY()
* PlayerEventLogSettingsRepository::DeleteWhereXAndY()
*
* Most of the above could be covered by base methods, but if you as a developer
* find yourself re-using logic for other parts of the code, its best to just make a
@@ -47,4 +47,4 @@ public:
};
#endif //EQEMU_EVENTLOG_REPOSITORY_H
#endif //EQEMU_PLAYER_EVENT_LOG_SETTINGS_REPOSITORY_H
@@ -1,11 +1,11 @@
#ifndef EQEMU_HACKERS_REPOSITORY_H
#define EQEMU_HACKERS_REPOSITORY_H
#ifndef EQEMU_PLAYER_EVENT_LOGS_REPOSITORY_H
#define EQEMU_PLAYER_EVENT_LOGS_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_hackers_repository.h"
#include "base/base_player_event_logs_repository.h"
class HackersRepository: public BaseHackersRepository {
class PlayerEventLogsRepository: public BasePlayerEventLogsRepository {
public:
/**
@@ -32,10 +32,10 @@ public:
*
* Example custom methods in a repository
*
* HackersRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* HackersRepository::GetWhereNeverExpires()
* HackersRepository::GetWhereXAndY()
* HackersRepository::DeleteWhereXAndY()
* PlayerEventLogsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* PlayerEventLogsRepository::GetWhereNeverExpires()
* PlayerEventLogsRepository::GetWhereXAndY()
* PlayerEventLogsRepository::DeleteWhereXAndY()
*
* Most of the above could be covered by base methods, but if you as a developer
* find yourself re-using logic for other parts of the code, its best to just make a
@@ -47,4 +47,4 @@ public:
};
#endif //EQEMU_HACKERS_REPOSITORY_H
#endif //EQEMU_PLAYER_EVENT_LOGS_REPOSITORY_H
+10 -1
View File
@@ -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;
}
+7
View File
@@ -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
View File
@@ -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;
+34
View File
@@ -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);
}
+3
View File
@@ -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);
-1
View File
@@ -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
View File
@@ -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
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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()) {
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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");
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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: {
+2 -2
View File
@@ -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 -1
View File
@@ -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.
+2 -2
View File
@@ -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
{
+19
View File
@@ -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`;
+2
View File
@@ -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`;
+5 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}
+1
View File
@@ -121,6 +121,7 @@ private:
EQStreamInterface* eqs;
bool CanTradeFVNoDropItem();
void RecordPossibleHack(const std::string& message);
};
bool CheckCharCreateInfoSoF(CharCreate_Struct *cc);
+6 -6
View File
@@ -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, ",")));
}
}
+2 -2
View File
@@ -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)));
}
+17
View File
@@ -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
View File
@@ -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);
}
+3
View File
@@ -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_*/
+3 -3
View File
@@ -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);
+1 -1
View File
@@ -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});
}
+5 -5
View File
@@ -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);
}
+27
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
-1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+3 -3
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+48 -29
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"
-1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+2
View File
@@ -25,6 +25,8 @@ Eglin
#include <perlbind/perlbind.h>
namespace perl = perlbind;
#undef Null
#ifdef WIN32
#define snprintf _snprintf
#endif
+116 -60
View File
@@ -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