Compare commits

...

171 Commits

Author SHA1 Message Date
hg 4a66c3918a Convert perl apis to perlbind 2022-06-12 19:56:46 -04:00
hg a2c6252c58 Add perlbind library 2022-06-12 19:56:45 -04:00
Kinglykrab be00aa1b60 [Loot] Remove unnecessary loot error messages. (#2261)
* [Loot] Remove unnecessary loot error messages.
These messages are only sent when you loot a corpse that is mid-decay (i.e. no items and you go to loot it immediately) or if you try to loot a non-corpse entity.
The ent != 0 shows up a lot if you're trying to loot too quickly and really isn't an error, the server just hasn't caught up to decay the corpse before you try to loot.

* Use preexisting struct.

* Remove newline.

* Update client_packet.cpp
2022-06-12 18:44:16 -04:00
Akkadius b2658a6cbc [Hotfix] Correct database call to point to the content_db connection 2022-06-12 17:34:51 -05:00
Michael 026133f32a [Bug Fix] Tradeskill Item 0 Error (#2256)
When trying to fully script a combine so the tradeskill itself does not return an item (Item 0) and handling all items through quests, the source will provide an error regardless that "Item 0 does not exist". This change will move the item summon after the validity check thus preventing the error to the client.
2022-06-10 13:53:30 -04:00
Michael 6d4f22a1c0 [Bug Fix] Tradeskill Autocombine MinSkill (#2260)
* [Bug] Tradeskill Autocombine MinSkill

Require min tradeskill or above for autocombine.

* Syntax fixes
2022-06-10 13:52:31 -04:00
Michael e75f87a535 [Quest API] Expand SaveGuardSpot (#2258)
* Expand SaveGuardSpot

- Adds
-- mob:saveguardspot()
-- mob:saveguardspot(true)

* Perl Support
Thanks to KinglyKrab for the perl implementation.
2022-06-10 13:52:22 -04:00
Kinglykrab 83e066bffc [Bug Fix] Stop skill ups on Charmed NPCs. (#2249)
- Clients were capable of leveling up their melee skills on Charmed Mobs according to: https://github.com/EQEmu/Server/issues/2182
2022-06-09 21:08:50 -04:00
Chris Miles 4639405fdf [Discord Integration] Native Discord Integration (#2140)
* Start of discord integration work

* more testing

* Discord client work

* More discord work

* Cleanup

* Handle retry timer response and max retries

* Update base retry timer

* Move Discord queue handler to UCS, add queuer to own thread

* Post merge

* Send up Zone::SendDiscordMessage

* Start of discord integration work

* more testing

* Discord client work

* More discord work

* Cleanup

* Move Discord queue handler to UCS, add queuer to own thread

* Post merge

* Push up tables

* Quest API stuff.

* Update 2022_05_07_discord_webhooks.sql

* Post merge fixes

* Push up manifest

* Flip logging signs in logic from copy / paste of inverse logic before

* Make sure we add new line to quest api sourced messages

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2022-06-09 17:22:23 -05:00
hg 8ef3e87370 [Bug Fix] Fix stack leaks in Lua events #2254
EventItem was leaving the package table on the stack in successful
(non-encounter) calls and only popping the return. All events were
also leaking the full stack on lua_pcall errors. Lua mod callbacks
were popping error returns but leaking the table.
2022-06-09 17:20:35 -05:00
Akkadius bec28769aa [Hotfix] Flipped positive / negative values for legacy_combat.lua 2022-06-09 17:19:00 -05:00
Kinglykrab 8586cdc47e [Messages] Convert messages from Spells to FocusEffect where necessary. (#2243)
* [Messages] Convert messages from Spells to FocusEffect where necessary.
https://github.com/EQEmu/Server/issues/837 from 2019 notes a couple places that should use Focus Effect chat type instead of Spells chat type.

* Add rule for item cast messages.
2022-06-08 19:09:33 -04:00
Michael Cook (mackal) 63ba5dc3ab Fix memory leaks found by Quint (#2248) 2022-06-07 23:41:13 -04:00
Michael fcc7725ca3 [Bug Fix] Bazaar Search MYSQL Error (#2252)
Corrected fields that the bazaar search function is using to prevent mysql error due to non-existant field items.spellid.
2022-06-07 21:21:02 -04:00
Michael 5df6a61c96 [Bug Fix] Hacker_Str was causing sql errors - Non Escaped (#2251)
Processing the hacker_str through EscapeString().
2022-06-07 21:19:24 -04:00
Kinglykrab fc5105eed7 [Rules] Add Frontal Stun Immunity Rules. (#2217)
* [Rules] Add Frontal Stun Immunity Rules.
- Add Combat:FrontalStunImmunityClasses rule for determining Frontal Stun Immunity by class bitmasks
- Add Combat:FrontalStunImmunityRaces rule for determining Frontal Stun Immunity by race bitmasks
- Add Combat:NPCsUseFrontalStunImmunityClasses rule for determining if NPCs use Frontal Stun Immunity by class bitmasks
- Add Combat:NPCsUseFrontalStunImmunityRaces rule for determining if NPCs uses Frontal Stun Immunity by race bitmasks
- Cleanup GetDefaultGender() as best as possible with the globalload I have.
- Combat:NPCsUseFrontalStunImmunityRaces defaults to true as NPCs currently use Frontal Stun Immunity.
- Added **all** Ogre races to the check for Frontal Stun Immunity.

* Remove other Ogre races.
2022-06-06 22:29:06 -05:00
Quintinon 243fb781e8 [Bug Fix] Fix two invalid data accesses in zone/client.cpp (#2238)
* Correct potential read out of bounds on array in Client::Doppelganger

* Correct potential read out of bounds in Client::ChannelMessageSend

* Corrected logic to not read out of bounds on the lower end.
2022-06-06 22:26:36 -05:00
Kinglykrab ec4d228dd5 [Bug Fix] Fix issue where #advnpcspawn addspawn does not add spawn sometimes. (#2247)
* [Bug Fix] Fix issue where #advnpcspawn addspawn does not add spawn sometimes.
- Cleanup where std::stoi should be std::stoul.
- Cleanup ShowStats message to show NPC stats at bottom as well.

* Update advnpcspawn.cpp
2022-06-06 22:24:45 -05:00
Michael Cook (mackal) 2910073373 [Memory Leak] Fix leak of CommandRecords in commandlist (#2244)
* Fix leak of CommandRecords in commandlist

Quicker than porting to std::unique_ptr since we do fun stuff with
commandlist ...

* Fix leak so it won't double free

This is fine enough until we rewrite it to be better
2022-06-06 15:21:35 -04:00
Kinglykrab b4d5e807e3 [Commands] Add BestZ and Region Data to #loc (#2245) 2022-06-05 15:38:51 -04:00
Kinglykrab 8f729fe948 [Rules] Add Rule to Disable NPC Last Names. (#2227)
* [Rules] Add Rule to Disable NPC Last Names.
- Add NPC:DisableLastNames to disable NPC Last Names.
- Fix #npcedit lastname to allow you to use an empty string.

* Cleanup of classes in naming.

* Duplicate.

* Update classes.cpp
2022-06-04 14:01:00 -04:00
Kinglykrab 7160aa651e [Cleanup] Cleanup Haste references and Lua API calls for unsigned to signed. (#2240) 2022-06-04 14:00:21 -04:00
Kinglykrab 8414ce02e3 [Cleanup] Add Validation to varchar number item fields. (#2241)
- combateffects and charmfileid were varchar and have no default value, so if you passed '' shared memory would fail, default these to 0 to prevent this.
2022-06-04 14:00:16 -04:00
Kinglykrab 17034a6e47 [Commands] Cleanup #spawneditmass Command. (#2229)
* [Commands] Cleanup #spawneditmass Command.
- Cleanup messages and logic.
- Split command into its own file.

* Save querying database if they're not using the only supported option.

* Condense to one message.
2022-06-04 12:59:46 -05:00
Quintinon be1772d464 [Bug Fix] Correct (probably) unintended bitwise AND instead of logical AND (#2239) 2022-06-01 21:17:39 -04:00
Quintinon a00f086bb8 [Combat] Fix shield calculation (#2234)
* Fix max mitigation calculation

* Fix shield ability not receiving correct arguments from perl script.

* Correct shielder having wrong mitigation set.
2022-06-01 17:17:14 -04:00
Quintinon de830e5535 [Code Cleanup] Resharper Warnings (#2235)
* Remove unused local variable

* Remove another unused variable

* Correct typos and remove unused initialization

* Cleanup some code in OPCharCreate

* Remove unused function in client.cpp and undefined declaration.
Also the function potentially had a null pointer dereference according to Visual Studio.
2022-06-01 17:16:49 -04:00
Kinglykrab d1404a2d95 [Rules] Add Rules to disable various item functionalities and cleanup data types. (#2225)
- Noticed some data types were unsigned when they should be signed based on Live items having these stats as negatives.
- Loregroup was uint32 and should be int32 for Loregroup -1, fixed any references to -1 as 0xFFFFFFFF.
- Attack was uint32 and should be int32.
- DamageShield was uint32 and should be int32.
- DotShielding was uint32 and should be int32.
- Endurance was uint32 and should be int32.
- EnduranceRegen was uint32 and should be int32.
- Haste was uint32 and should be int32.
- ManaRegen was uint32 and should be int32.
- Regen was uint32 and should be int32.
- RULE_BOOL(Items, DisableAttuneable, false, "Enable this to disable Attuneable Items")
- RULE_BOOL(Items, DisableBardFocusEffects, false, "Enable this to disable Bard Focus Effects on Items")
- RULE_BOOL(Items, DisableLore, false, "Enable this to disable Lore Items")
- RULE_BOOL(Items, DisableNoDrop, false, "Enable this to disable No Drop Items")
- RULE_BOOL(Items, DisableNoPet, false, "Enable this to disable No Pet Items")
- RULE_BOOL(Items, DisableNoRent, false, "Enable this to disable No Rent Items")
- RULE_BOOL(Items, DisableNoTransfer, false, "Enable this to disable No Transfer Items")
- RULE_BOOL(Items, DisablePotionBelt, false, "Enable this to disable Potion Belt Items")
- RULE_BOOL(Items, DisableSpellFocusEffects, false, "Enable this to disable Spell Focus Effects on Items")
2022-06-01 17:12:31 -04:00
Kinglykrab 38da37755d [Bug Fix] Fix MovePC in #zone and #zoneinstance Commands. (#2236)
* [Bug Fix] Fix MovePC in #zone and #zoneinstance COmmands.

* #zone 0 Optional Functionality.
2022-06-01 17:05:43 -04:00
Kinglykrab 57ac46d090 [Commands] Cleanup #date Command. (#2228)
* [Commands] Cleanup #date Command.
- Cleanup messages and logic.

* Cleanup.
2022-06-01 15:31:31 -04:00
Chris Miles 59f32b8d34 [Loading] Zone Version Loading Fixes (#2233)
* Zone version loading fixes

* Remove errant code
2022-05-31 22:15:05 -05:00
Chris Miles a45117cd04 [Bug Fix] Adjustment for nullptr crash (#2232) 2022-05-31 22:24:12 -04:00
Chris Miles 291aaea581 [Bug Fix] Fix null pointer crash on zones that have not booted a zone yet with #reload commands or anything that calls GetZoneDescription (#2231) 2022-05-31 21:55:00 -04:00
Chris Miles 162d34e1d9 [Tasks] Fix validation loading (#2230) 2022-05-31 21:47:01 -04:00
titanium-forever 86c9be410d [Database Backup] Enable database dump of bot data (#2221)
* Add option to dump bot data

* Add player_bot_table dump suppor to command handler

* Add tableList getter to the dump_service

* Fix declaration in header file

* Include missed bot tables

* Rename player-bot to bot to be more descriptive

* Fix missed reference to player-bots

Co-authored-by: Kieren Hinch <khinch-github@nylonmoon.com>
2022-05-31 18:25:10 -05:00
Kinglykrab 30e34c67b4 [Quest API] Fix parameters in some Perl worldwide methods. (#2224) 2022-05-31 15:46:14 -04:00
Kinglykrab ce8b8da0d6 [Bug Fix] Fix Legacy Combat Lua Script (#2226) 2022-05-30 22:43:03 -04:00
Kinglykrab 02e8b125a4 [Hot Fix] Fix Linux compile due to missing include. (#2223)
- Not sure how Windows compiles, but Linux fails.
2022-05-30 22:10:49 -04:00
Kinglykrab 11369247b1 [INT64] Further int64 cleanup in Perl SetHP() and GetSpellHPBonuses() in Perl/Lua. (#2222) 2022-05-29 16:36:32 -05:00
Kinglykrab d493a6627b [Commands] Cleanup #nudge Command. (#2220)
* [Commands] Cleanup #nudge Command.
- Cleanup messages and logic.

* Update nudge.cpp

* Update nudge.cpp
2022-05-29 14:33:40 -04:00
Kinglykrab b07945f0be [Commands] Cleanup #emptyinventory Command. (#2219)
- Cleanup messages and logic.
- Breakout #emptyinventory into its own command file.
2022-05-29 14:33:35 -04:00
Kinglykrab 8f3ac74196 [INT64] Fix int64 for OOC Regen and GetHP(), GetMaxHP(), GetItemHPBonuses() in Perl/Lua. (#2218)
* [INT64] Fix int64 for OOC Regen and GetHP(), GetMaxHP(), GetItemHPBonuses() in Perl/Lua.
- These all had int64 values and were overflowing, returning garbage data.

* Update npc.cpp
2022-05-29 14:33:30 -04:00
Kinglykrab bcf7ccefcd [Money Messages] Cleanup quest::givecash(), split, and task reward messages. (#2205)
* [Money Messages] Cleanup quest::givecash(), split, and task reward messages.
- Cleans up all the money messages using ConvertMoneyToString().
- Allows quest::givecash() to have optional parameters other than copper.

* Commification.

* Corpse messages.

* String IDs and cleanup.
2022-05-29 14:33:18 -04:00
Kinglykrab 9e9ef6809b [Cleanup] Cleanup spell and max level bucket logic. (#2181)
* [Cleanup] Cleanup spell and max level bucket logic.
- Spell buckets will now allow new mob->SetBucket() buckets since most people use these now.
- Max level bucket will now allow new mob->SetBucket() bucket since most people use these now.
- Clean up GetScribeableSpells() and GetLearnableDisciplines() logic and magic numbers.
- Make GetClientMaxLevel() uint8 instead of int since it can only be 0-255.

* Fix typo from other commit.

* Lua setter.

* Update client.cpp
2022-05-28 14:35:17 -04:00
Kinglykrab c8f6dbb86d [Commands] Cleanup #npcedit, #lastname, #title, and #titlesuffix Commands. (#2215)
* [Commands] Cleanup #lastname, #npcedit, #title, and #titlesuffix Commands.
- Cleanup messages and logic.

* Update emu_constants.h

* Update command.cpp

* Update command.cpp

* Cleanup of GetXName methods to not define map unnecessarily.

* Update emu_constants.cpp

* Update npcedit.cpp
2022-05-28 14:35:05 -04:00
Kinglykrab 7de50d0e60 [Bug Fix] Fix IP Exemptions. (#2189)
* [Bug Fix] Fix IP Exemptions.
- IP Exemptions were broken due to GetAccountID() returning 0 in logic somehow, resolved this by setting a variable to GetAccountID() at the beginning of the method.
- Fixed weird IP messages where the long form of IP was displayed instead of the string form.
- Fixes edge case where IP rule may be set to -1 and this will make anyone get instantly kicked if IP Exemptions were enabled as their IP Count would always be greater than -1.

* Update client.cpp

* Update client.cpp

* Update clientlist.cpp

* Update clientlist.cpp
2022-05-27 23:57:55 -04:00
Paul Coene aaaee6c6a4 [Bug Fix] IsDamage test for lifetap was not complete. (#2213)
* [Bug Fix] IsDamage test for lifetap was not complete.

* Fix magic # and formatting as per Kingly

* Added #define
2022-05-27 15:30:38 -04:00
Kinglykrab d7f38361f2 [Commands] Cleanup #findaliases and #help Commands. (#2204)
- Cleanup messages and logic.
- Add saylinks to messages for ease of use when searching for commands as most have help messages when using them incorrectly or require no arguments and can be used immediately from the say link.
2022-05-27 15:28:36 -04:00
Kinglykrab 275995a374 [Commands] Cleanup #zone and #zoneinstance Commands. (#2202)
* [Commands] Cleanup #zone and #zoneinstance Commands.
- Cleanup messages and logic.
- Broke these commands out in to their own files.

* Update database.cpp

* Update database.cpp

* Update database.cpp
2022-05-27 15:26:30 -04:00
Kinglykrab 7072b5721b [Rules] Add Spells:BuffsFadeOnDeath. (#2200)
- Allows you to disable buffs fading on death.
2022-05-27 15:00:59 -04:00
Kinglykrab 123bc5f19a [Commands] Cleanup #findclass and #findrace Commands. (#2211)
- Cleanup messages and logic.
- Add bitmasks to player race messages.
2022-05-27 14:46:53 -04:00
Kinglykrab e6db71e31e [Commands] Cleanup #corpsefix Command. (#2197)
* [Commands] Cleanup #corpsefix Command.
- Cleanup messages and logic.

* Update entity.cpp
2022-05-27 14:46:26 -04:00
Kinglykrab 129a738072 [Commands] Cleanup #level Command. (#2203)
- Cleanup messages and logic.
- Breakout #level into its own command file.
2022-05-27 14:45:26 -04:00
Kinglykrab 0de90dc135 [Rules] Add Spells:IllusionsAlwaysPersist. (#2199)
- Allows illusions to always persist beyond death or zoning.
2022-05-27 14:39:35 -04:00
Kinglykrab 5bc4cff7a9 [Regen] Fix possible overflow in CalcHPRegenCap(). (#2185) 2022-05-27 14:39:25 -04:00
Kinglykrab f9191d4ef4 [Commands] Cleanup #oocmute Command. (#2191)
- Cleanup messages and logic.
- Add ServerOOCMute_Struct for cleanliness.
2022-05-27 14:38:40 -04:00
Michael Cook (mackal) 49d751b3d5 Revert "[Aggro] Rooted mobs will add other hated targets to Hate list (#2180)" (#2214)
This reverts commit 14f48fcc93.
2022-05-27 11:40:43 -04:00
Paul Coene 14f48fcc93 [Aggro] Rooted mobs will add other hated targets to Hate list (#2180) 2022-05-27 09:37:55 -04:00
titanium-forever 25addc3bd9 Create user directory during account creation to ensure default files are copied to profile from /etc/skel (#2176)
Co-authored-by: Kieren Hinch <khinch-github@nylonmoon.com>
2022-05-27 00:22:31 -05:00
Kinglykrab 7f12ad325a [Bug Fix] Fix bot compile locking client on server enter. (#2210) 2022-05-26 14:10:19 -04:00
Kinglykrab 6636c64c82 [Commands] Fix typos in #ban and #ipban Commands. (#2209) 2022-05-25 20:05:07 -04:00
Chris e2708af6f2 [Bug Fix] Blocked spells max spell id increased (#2207)
https://github.com/EQEmu/Server/pull/2073 broke blocking spells. There is another location in mob.h that needs to be updated to int32. Tested as fixed on my server.
2022-05-25 17:53:40 -04:00
Kinglykrab 1de0c27629 [Bug Fix] Fix HP Regen Per Second. (#2206)
`hp_regen_rate` was being used for `hp_regen_per_second` incorrectly.
2022-05-25 12:08:28 -05:00
Kinglykrab 9ccdb9eb84 [Bot Commands] Use Account Status Constants. (#2201)
- Convert bot_command_add calls to use constants instead of magic numbers.
2022-05-23 19:56:56 -04:00
Kinglykrab e69f7a6cf1 [Commands] Remove unused/broken #deletegraveyard and #setgraveyard Commands. (#2198)
- These commands don't function in their current state, and they probably haven't ever.
- Removing the commands and putting this in an editor makes more sense, as #setgraveyard uses the current zone XYZ coordinates of a target to set a graveyard for another zone. and they also only allow version 0 graveyards.
- Not sure of a better idea than just deleting, as setting data based on another zone using your current zone's data seems beyond the scope of a command.
2022-05-23 19:56:30 -04:00
Kinglykrab efd04f8324 [Commands] Cleanup #motd Command. (#2190) 2022-05-23 19:56:19 -04:00
Kinglykrab eeacc62a91 [Rules] Cleanup all unused rules. (#2184) 2022-05-23 19:56:03 -04:00
Kinglykrab a7a525ed0b [Commands] #bind Typo. (#2196) 2022-05-22 22:31:14 -04:00
Kinglykrab e43538cf73 [Commands] Cleanup #kill Command. (#2195)
- Cleanup messages and logic.
2022-05-22 22:31:08 -04:00
Kinglykrab 5b90d26a33 [Bug Fix] Fix bot guild removal. (#2194) 2022-05-22 22:31:03 -04:00
Kinglykrab 992e4ac59e [Commands] Consolidate #lock and #unlock Commands into #serverlock. (#2193)
- Convert the two commands into one command.
- Cleanup struct naming.
2022-05-22 22:30:56 -04:00
Chris Miles 6b85c914a5 Schema consistency fixes (#2192) 2022-05-21 23:44:04 -04:00
Kinglykrab 089246db53 [Cleanup] Move Client::Undye() to client.cpp from #path Command. (#2188)
- Client::Undye() was inside the #path command file.
2022-05-21 10:26:45 -04:00
Paul Coene f3e5423677 [Bug Fix] Fix duplicate and missing messages due to innate in spells (#2170)
* [Bug Fix] Fix duplicate and missing messages due to innate skill in spells.

* Seperate spell and melee damage range and skip

* Refine when innate messages are produced.

* Fix magic # (replace with constant)
2022-05-20 11:49:18 -04:00
Kinglykrab 0e96099b3d [Titles] Cleanup titles, title suffix, and last name methods. (#2174)
* [Titles] Cleanup titles, title suffix, and last name methods.
- Use strings instead of const chars*.
- Add optional parameter to SetAATitle in Lua so you can save to the database similar to Perl.
- Cleanup #lastname command.
- Cleanup #title command.
- Cleanup #titlesuffix command.

* Update npc.cpp
2022-05-19 20:15:44 -04:00
Kinglykrab 6398381c44 [Quest API] Add CheckNameFilter to Perl/Lua. (#2175)
- Add quest::checknamefilter(name) to Perl.
- Add eq.check_name_filter(name) to Lua.
- Allows operators to check strings against the name filter for stuff like setting custom pet names, titles, suffixes, etc in scripts.
2022-05-19 20:01:14 -04:00
Kinglykrab 7c1a139991 [Cleanup] Quest API push methods using invalid types. (#2172)
* [Cleanup] Quest API push methods using invalid types.
- Some push methods were pushing integers as unsigned integers or unsigned integer as integers, this fixes all of that.
- Also cleans up some lines that had multiple function calls on them.

* More cleanup of bools and one expansion name was wrong.
2022-05-15 22:14:16 -04:00
Kinglykrab 8554aab2ff [Cleanup] Remove unused methods. (#2171) 2022-05-15 19:12:33 -04:00
hg 77e72ba666 Save eyes in #npcedit featuresave (#2178) 2022-05-15 19:12:22 -04:00
Kinglykrab 8329760632 [Quest API] Add TaskSelector to Perl/Lua. (#2177)
- Add $client->TaskSelector(task_list) to Perl.
- Add client:TaskSelector({task_list}) to Lua.
- Allow $client->AssignTask(task_id) in Perl.
- Allow client:AssignTask(task_id) in Lua.
- Can now assign tasks in scripts without the NPC ID parameter.
2022-05-15 00:49:55 -04:00
hg d8aa8f7e7a [Opcode] Implement SetFace opcode (#2167)
* Implement SetFace opcode

This implements the opcode that's used to update zone clients when a
client updates their face

* Use SetFace in #feature command

Add check for valid number

This adds the #feature eyes command which isn't in the illusion struct
2022-05-11 19:57:20 -04:00
Kinglykrab 29cdd91ca0 [Quest API] Add GetHealAmount() and GetSpellDamage() to Perl/Lua. (#2165)
- Add $client->GetHealAmount() to Perl.
- Add $client->GetSpellDamage() to Perl.
- Add client:GetHealAmount() to Lua
- Add client:GetSpellDamage() to Lua.
2022-05-11 06:35:27 -04:00
Kinglykrab df99d97431 [Cleanup] Cleanup #kick message. (#2164) 2022-05-10 19:52:14 -04:00
Kinglykrab f314e05087 [Hot Fix] Off by on in Merchant Loading. (#2166) 2022-05-10 18:25:32 -04:00
Kinglykrab d120cf8a40 [Commands] #reload Command Overhaul. (#2162)
* [Commands] #reload Command Overhaul.
- Consolidated #reloadaa, #reloadallrules, #reloadcontentflags, #reloademote, #reloadlevelmods, #reloadmerchants, #reloadperlexportsettings, #reloadqst, #reloadstatic, #reloadtitles, #relaodtraps, #reloadworld, and #reloadzps in to one command.
- #reload has 15 different sub commands you may use, including Log Settings and Tasks reloading.
- All the reload commands are a part of the Developer Tools Menu messages now, as well as part of the documentation.
- Fixes the commands that weren't actually sending their packet to zone server to globally reload stuff.
- Added Variables table reloading to command.

* Consistency.

* Hot reload.

* Final big push.
2022-05-10 06:19:07 -04:00
Chris Miles 209b0eb273 [int64] Hate Fixes (#2163)
* Hate fixes

* Update perl_hateentry.cpp

* Update perl_hateentry.cpp
2022-05-09 20:49:43 -05:00
Kinglykrab 763fc82379 [Merchants] Add Merchant Data Bucket capability. (#2160)
* [Merchants] Add Merchant Data Bucket capability.
- Allows server operators to limit merchant items based on data bucket values and comparisons.
- Adds 3 columns, bucket_name, bucket_value, and bucket_comparison to merchantlist table.
- Bucket is checked based on GetBucketKey()-bucket_name.
- Buckets are mass loaded when using the merchant so it's not a database call per item, just a grouping of all their buckets from the start.
- This is a nearly year old pull request redone for master.
- bucket_comparison Values are as follows:
        - bucket_comparison 0: bucket_name == bucket_value
        - bucket_comparison 1: bucket_name != bucket_value
        - bucket_comparison 2: bucket_name >= bucket_value
        - bucket_comparison 3: bucket_name <= bucket_value
        - bucket_comparison 4: bucket_name > bucket_value
        - bucket_comparison 5: bucket_name < bucket_value
        - bucket_comparison 6: bucket_name is any of  pipe(|)-separated bucket_value
        - bucket_comparison 7: bucket_name is not any of  pipe(|)-separated bucket_value
        - bucket_comparison 8: bucket_name is between first and second value of pipe(|)-separated bucket_value
        - bucket_comparison 9: bucket_name is not between first and second value of pipe(|)-separated  bucket_value

* Revert query change.
2022-05-09 21:36:51 -04:00
Kinglykrab 6e0d101457 [Cleanup] Possible issues with variable/parameter name equality. (#2161) 2022-05-09 21:00:37 -04:00
Chris Miles a1a4f91ea6 [Hotfix] Fix regression caused by #2129 2022-05-09 01:18:10 -05:00
Michael Cook (mackal) 6cc845e05e Fix out of bounds issues with SPA 288 (#2157) 2022-05-08 18:18:24 -04:00
Kinglykrab 597e324319 [Bug FIx] Fix #repop Command. (#2159)
If you use no arguments, the command does not repop.
2022-05-08 17:01:50 -04:00
Kinglykrab f370a6048f [Bug Fix] Make Perl TakeMoneyFromPP int64 (#2158) 2022-05-08 13:17:46 -04:00
Kinglykrab 9d784d0d9b [Bug Fix] Fix possible issue where variables have the same name. (#2156)
* [Bug Fix] Fix possible issue where variables have the same name.

* Naming.
2022-05-08 01:43:28 -04:00
Chris Miles fd7b15abb1 [int64] Windows Compile Fixes (#2155)
* int64 windows aftermath

* Perl.

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2022-05-07 23:45:52 -05:00
Kinglykrab b27428a6d8 [Commands] Cleanup #cvs Command. (#2153)
* [Commands] Cleanup #cvs Command.
- Cleanup messages and logic.
- std::find was using .begin() in both comparisons, so it wasn't working and unique IPS always returned 1.

* Update cvs.cpp
2022-05-07 23:45:44 -04:00
Chris Miles f201d4c999 [int64] Support for HP / Mana / End / Damage / Hate (#2091)
* Initial int64 work

* Hate 64 bit

* Update special_attacks.cpp

* Aggro / Damage / Hate int64

* NPC edit adjustments

* Fix bot compile

* More int64 adjustments

* More int64 references

* npcedit references

* aggrozone

* More int64 changes

* More int64 changes for damage

* Many more damage int64 references

* More spell damage int64 conversions

* HealDamage

* Damage fully working

* Remove debug

* Add migration

* More int64 adjustments

* Much wow, many int64

* More int64

* PR adjustments
2022-05-07 22:32:02 -05:00
Kinglykrab d9c41526e8 [Commands] Cleanup #ban, #ipban, #flag, #kick, #setlsinfo, and #setpass Commands. (#2104)
* [Commands] Cleanup #ban, #ipban, #flag, and #kick Commands.
- Cleanup messages and logic.
- Add ServerFlagUpdate_Struct for flag updates.

* Add #setlsinfo and #setpass to cleanup.

* Update setlsinfo.cpp

* Update database.cpp

* Update database.cpp

* Update command.cpp
2022-05-07 23:28:45 -04:00
Chris Miles 07b46ed445 [UCS] Auto Client Reconnection (#2154) 2022-05-07 22:24:11 -05:00
Chris Miles 9f9eaed983 [Hotfix] Fix DB version merge 2022-05-07 21:19:21 -05:00
Chris Miles 862e1e33bf [Tasks] Implement Task Goal Match List (#2097)
* [Tasks] Implement Task Goal Match List

* Migration

* Add npc_type_id to match types for npc kill

* Flip str_tolower
2022-05-07 15:37:06 -05:00
Kinglykrab dca34cc2ff [Commands] Cleanup #time and #timezone Command. (#2147)
* [Commands] Cleanup #time and #timezone Command.
- Cleanup messages and logic.

* Cleanup.
2022-05-07 05:03:58 -05:00
Kinglykrab ccb316b11b [Bug Fix] NPC::GetNPCStat has no default return. (#2150)
```In member function 'float NPC::GetNPCStat(const char*)':
/drone/src/zone/npc.cpp:2810:1: warning: control reaches end of non-void function [-Wreturn-type]```
2022-05-07 05:01:36 -05:00
Kinglykrab 10ae4ea8f6 [Bug Fix] Lua GetBlockNextSpell() no return. (#2151)
- No return value.
2022-05-07 05:01:22 -05:00
Kinglykrab 7df9b2974b [Commands] Cleanup #reloadzps Command. (#2129)
* [Commands] Cleanup #reloadzps Command.
- Cleanup messages and logic.
- Make reloading of zone points global instead of zone specific.

* Further cleanup.
- Add zone->GetZoneDescription().
- Add mob->GetTargetDescription(mob).

* Final cleanup.

* Typo.
2022-05-07 03:23:15 -04:00
Kinglykrab 0b3065d7a9 [Commands] Cleanup #reloadtraps Command. (#2126)
* [Commands] Cleanup #reloadtraps Command.
- Cleanup messages and logic.
- Allow the option to reload/repop traps globally for this command.

* Typos.
2022-05-06 22:39:21 -04:00
Kinglykrab 7549fbbeea [Commands] Cleanup #reloadlevelmods Command. (#2122)
* [Commands] Cleanup #reloadlevelmods Command.
- Cleanup messages and logic.
- Make the reloading of modifiers global instead of zone-specific.

* Remove unnecessary message.

* Update worldserver.cpp

* Update worldserver.cpp

* Update worldserver.cpp
2022-05-06 22:31:17 -04:00
Kinglykrab e26eba8e03 [Commands] Cleanup #npctype_cache Command. (#2109)
* [Commands] Cleanup #npctype_cache Command.
- Cleanup messages and logic.
- Loop was returning as soon as it found first ID, so you couldn't use the spaced ID list functionality at all.

* Remove command.
2022-05-06 22:13:16 -04:00
Kinglykrab 02828a73b8 [Commands] Cleanup #npcspecialattk Command. (#2108)
* [Commands] Cleanup #npcspecialattk Command.
- Cleanup messages and logic.

* Remove command.
2022-05-06 22:06:12 -04:00
Kinglykrab 0e710fe5e7 [Commands] Cleanup #ucs Command. (#2149)
* [Commands] Cleanup #ucs Command.
- Cleanup messages and logic.

* Remove command.
2022-05-06 22:03:13 -04:00
Kinglykrab 86568e9292 [Commands] Cleanup #reloadaa Command. (#2120)
* [Commands] Cleanup #reloadaa Command.
- Cleanup messages.
- Remove unncessary file_exists command from file.
- Make reloading of Alternate Advancement data global instead of zone specific.

* Update worldserver.cpp

* Update worldserver.cpp
2022-05-06 21:57:19 -04:00
Kinglykrab bf1d05d639 [Commands] Cleanup #profanity Command. (#2113)
* [Commands] Cleanup #profanity Command.
- Cleanup messages and logic.

* Update profanity_manager.cpp

* Update profanity_manager.cpp

* Update profanity_manager.cpp

* Update profanity_manager.cpp
2022-05-06 21:36:23 -04:00
Kinglykrab 4eaf44fc33 [Commands] Remove unused #bestz and #pf Commands. (#2112)
- Remove unused commands.
2022-05-06 21:13:26 -04:00
Kinglykrab e62a283a79 [Commands] Cleanup #makepet Command. (#2105)
- Cleanup messages and logic.
2022-05-06 20:58:07 -04:00
Kinglykrab a847e461c1 [Commands] Cleanup #npcemote Command. (#2106)
* [Commands] Cleanup #npcemote Command.
- Cleanup messages and logic.

* Update npcemote.cpp
2022-05-06 20:58:01 -04:00
Kinglykrab 37fefad58e [Commands] Cleanup #npcsay and #npcshout Commands. (#2107)
* [Commands] Cleanup #npcsay and #npcshout Commands.
- Cleanup messages and logic.

* Update npcsay.cpp

* Update npcshout.cpp
2022-05-06 20:57:53 -04:00
Kinglykrab 26b26af8d7 [Commands] Cleanup #npctypespawn Command. (#2110)
- Cleanup messages and logic.
2022-05-06 20:50:02 -04:00
Kinglykrab ee86001132 [Commands] Cleanup #reloadmerchants Command. (#2123)
* [Commands] Cleanup #reloadmerchants Command.
- Cleanup messages and logic.
- Make the reloading of merchants global instead of zone specific.

* Update worldserver.cpp

* Update worldserver.cpp
2022-05-06 20:48:46 -04:00
Kinglykrab 7d89c05a48 [Commmands] Cleanup #questerrors Command. (#2116)
- Cleanup messages and logic.
2022-05-06 20:42:47 -04:00
Kinglykrab 6e15fae6a0 [Commands] Cleanup #randomizefeatures Command. (#2118)
- Cleanup messages and logic.
- Make #randomizefeatures function for Iksar and Vah Shir.
2022-05-06 20:42:34 -04:00
Kinglykrab 2f962c2c8a [Commands] Cleanup #refreshgroup Command. (#2119)
- Cleanup messages and logic.
2022-05-06 20:42:25 -04:00
Kinglykrab 6bbd1e94c3 [Commands] Cleanup #push Command. (#2114)
- Cleanup messages and logic.
2022-05-06 19:38:15 -05:00
Kinglykrab 78d44440eb [Commands] Cleanup #qglobal Command. (#2115)
- Cleanup messages and logic.
2022-05-06 19:37:45 -05:00
Kinglykrab 1e45ffa24d [Bug Fix] Resolve subroutine redefinition due to bot methods. (#2117)
* [Bug Fix] Resolve subroutine definition due to bot methods.

* Update perl_bot.cpp
2022-05-06 19:33:20 -05:00
Kinglykrab 37d5d96871 [Commands] Cleanup #resetaa_timer Command. (#2131)
* [Commands] Cleanup #resetaa_timer Command.
- Cleanup messages and logic.

* Typos.
2022-05-06 20:26:59 -04:00
Kinglykrab 90725f9fd9 [Commands] Cleanup #revoke Command. (#2134)
* [Commands] Cleanup #revoke Command.
- Cleanup messages and logic.

* Update revoke.cpp
2022-05-06 20:26:54 -04:00
Kinglykrab c1aa3e7056 [Commands] Cleanup #reloadallrules Command. (#2121)
* [Commands] Cleanup #reloadallrules Command.
- Cleanup messages.

* Typo.
2022-05-06 20:26:39 -04:00
Kinglykrab 1d59fff2bf [Commands] Cleanup #reloadperlexportsettings Command. (#2124)
* [Commands] Cleanup #reloadperlexportsettings Command.
- Cleanup messages and logic.
- Make reloading of Perl event export settings global instead of zone specific.

* Update worldserver.cpp
2022-05-06 20:22:58 -04:00
Kinglykrab 6beb220e93 [Commands] Cleanup #reloadtitles Command. (#2125)
* [Commands] Cleanup #reloadtitles Command.
- Cleanup messages;

* Update worldserver.cpp
2022-05-06 20:20:42 -04:00
Kinglykrab 3091a84540 [Commands] Cleanup #reloadworld and #repop Command. (#2127)
* [Commands] Cleanup #reloadworld Command.
- Cleanup messages and logic.

* [Commands] Cleanup #reloadworld and #repop Command.
- Cleanup messages and logic.
- Add #reloadworld 2 option to forcefully repop all mobs globally as well as reset quest timers and reload quests.
- Remove delay argument from #repop as it isn't used for anything.

* Typos.
2022-05-06 20:06:51 -04:00
Kinglykrab 9fbab76d40 [Commands] Cleanup #summonburiedplayercorpse Command. (#2146)
* [Commands] Cleanup #summonburiedplayercorpse Command.
- Cleanup messages and logic.

* Update summonburiedplayercorpse.cpp
2022-05-06 20:06:04 -04:00
Kinglykrab b03d47b9cd [Commands] Cleanup #trapinfo Command. (#2148)
* [Commands] Cleanup #trapinfo Command.
- Cleanup messages.
- Does not modify the command file, as it's only one line that calls the method in zones/trap.cpp.

* Cleanup.

* Update trap.cpp

* Update trap.cpp
2022-05-06 20:05:55 -04:00
Kinglykrab b583d95f09 [Commands] Cleanup #reloadrulesworld Command. (#2128)
- Cleanup messages and logic.
2022-05-06 20:05:40 -04:00
Kinglykrab 6846deb9c8 [Commands] Cleanup #reloadstatic Command. (#2130)
- Cleanup messages and logic.
- Make reloading of static zone data global instead of zone specific.
2022-05-06 20:03:22 -04:00
Kinglykrab adfec15893 [Commands] Cleanup #resetaa Command. (#2132)
* [Commands] Cleanup #resetaa Command.
- Cleanup messages and logic.

* Update resetaa.cpp
2022-05-06 20:02:32 -04:00
Kinglykrab 132c936c90 [Commands] Cleanup #sensetrap Command. (#2137)
* [Commands] Cleanup #sensetrap Command.
- Cleanup messages and logic.

* Update sensetrap.cpp
2022-05-06 20:01:29 -04:00
Kinglykrab a0ed0d57c5 [Commands] Cleanup #serverinfo Command. (#2138)
* [Commands] Cleanup #serverinfo Command.
- Cleanup message and logic.
- Use popup instead of messages.

* Update serverinfo.cpp
2022-05-06 20:01:21 -04:00
Kinglykrab 128e8ce08d [Commands] Cleanup #resetdisc_timer Command. (#2133)
- Cleanup messages and logic.
2022-05-06 19:50:58 -04:00
Kinglykrab 55629ce396 [Commands] Cleanup #roambox Command. (#2135)
- Cleanup messages and logic.
2022-05-06 19:50:26 -04:00
Kinglykrab bf7c1252f8 [Commands] Cleanup #save Command. (#2136)
- Cleanup messages and logic.
2022-05-06 19:50:19 -04:00
Kinglykrab e2bfa44df0 [Commands] Cleanup #serverrules Command. (#2139)
- Cleanup messages and logic.
- Change separator from new line to pipe separator, as new line was non-functional.
2022-05-06 19:50:09 -04:00
Kinglykrab e5acc7c322 [Commands] Cleanup #shownpcgloballoot and #showzonegloballoot Command. (#2141)
- Cleanup messages and logic.
2022-05-06 19:50:03 -04:00
Kinglykrab 5aaaaed6f1 [Commands] Add #feature Command. (#2142)
* [Commands] Add #feature Command.
- Removes #beard, #beardcolor, #details, #face, #hair, #haircolor, #helm, #heritage, #size, and #tattoo commands.
- Cleanup messages and logic.
- Consolidate 13 different feature settings to this one command.
- Unique commands including #gender, #race, and #texture were not removed.

* Add missing feature.cpp.

* Update feature.cpp
2022-05-06 19:49:56 -04:00
Kinglykrab 6d31786456 [Commands] Cleanup #spawnfix Command. (#2143)
- Cleanup messages and logic.
2022-05-06 19:49:51 -04:00
Kinglykrab 0aeab11408 [Commands] Cleanup #summon Command. (#2145)
* [Commands] Cleanup #summon Command.
- Cleanup messages and logic.
- Add glm::vec4 overload for GMMove.
- Remove unused parameter from GMMove.
- Remove unnecessary Lua GMMove now that parameter is gone.

* Update summon.cpp

* Cleanup.
2022-05-06 18:45:12 -05:00
Kinglykrab fc484d0b1c [Commands] Cleanup #gassign Command. (#2101)
* [Commands] Cleanup #gassign Command.
- Cleanup messages and logic.

* Update gassign.cpp

* Update gassign.cpp
2022-05-06 19:13:28 -04:00
Kinglykrab 8dcc810b43 [Commands] Cleanup #spawnstatus Command. (#2144)
* [Commands] Cleanup #spawnstatus Command.
- Cleanup messages and logic.

* Further cleanup and consolidation, add inline GetTimer() as timer is protected.
2022-05-06 18:12:29 -05:00
Kinglykrab 04f3d6286c [Commands] Cleanup #attack Command. (#2103)
- Cleanup messages and logic.
2022-05-03 23:05:16 -04:00
Kinglykrab 35044becc1 [Commands] Cleanup #freeze and #unfreeze Commands. (#2102)
- Cleanup messages and logic.
- Remove the ability to #freeze yourself.
2022-05-03 23:05:09 -04:00
Kinglykrab e08afb1234 [Commands] Cleanup #getvariable Command. (#2100)
- Cleanup messages and logic.
2022-05-03 23:05:02 -04:00
Kinglykrab b2b87ea4e0 [Quest API] Expand Bot quest API functionality. (#2096)
* [Quest API] Expand Bot quest API functionality.
- Add $bot->AddItem(slot_id, item_id, charges, attuned, augment_one, augment_two, augment_three, augment_four, augment_five, augment_six) to Perl.
- Add $bot->CountItem(item_id) to Perl.
- Add $bot->HasItem(item_id) to Perl.
- Add $bot->RemoveItem(item_id) to Perl.
- Add bot:AddItem(slot_id, item_id, charges, attuned, augment_one, augment_two, augment_three, augment_four, augment_five, augment_six) to Lua.
- Add bot:CountItem(item_id) to Lua.
- Add bot:GetOwner() to Lua.
- Add bot:HasItem(item_id) to Lua.
- Add bot:RemoveItem(item_id) to Lua.

* Fix possible crash.
2022-05-03 23:04:54 -04:00
Kinglykrab 837c0a4385 [Quest API] Perl Money Fixes. (#2098)
- Fixes `$client->GetAllMoney()` to use `uint64` and not overflow int value.
- Fixes `$client->GetCarriedMoney()` to use `uint64` and not overflow int value.
2022-05-03 22:55:56 -04:00
Kinglykrab 9b075c28b6 [Quest API] Add commify to Perl/Lua. (#2099)
- Add quest::commify(number) to Perl.
- Add eq.commify(number) to Lua.
2022-05-03 19:44:22 -04:00
Paul Coene c4f05c3864 [Combat] Fix Frenzy vs opponents immune to non-magic (#2095)
* [Combat] Fix Frenzy vs opponents immune to non-magic

* Fix naming.

* Use snake case for variable
2022-05-02 23:08:08 -04:00
hg 0c12ca8370 [Repository] Cast floats to avoid grid repository warnings (#2094)
This is in the custom repository method file so the generator doesn't
need modified for it
2022-05-01 21:53:37 -05:00
Chris Miles a450779c91 [Drone] Speed up drone builds (#2092)
* Speed up drone builds

* Add chown for ccache

* Add the cmake compiler launcher flag for ccache

* Update .drone.yml

* Don't optimize

* Don't optimize take 2
2022-05-01 20:17:43 -05:00
hg dc004c2a9d Remove already defined method (#2093)
CastRestrictedSpell is already defined in common/spdat

MSVC Debug builds caught this as an ODR violation, though Release
builds still allowed it (maybe because the methods were identical)
2022-05-01 20:54:50 -04:00
Kinglykrab d59dcb68ca [Commands] Add additional #peqzone functionality. (#2085)
* [Commands] Add additional #peqzone functionality.
- Add #peqzone flagging capabilities so operators don't have to blanket allow #peqzone access to zones.
- Allows you to set a zone's `peqzone` column to `2` and disallow use of `#peqzone` until they have been given the appropriate flag.
- Add #peqzone_flags command to list your #peqzone flags similar to #flags command.
- Add `character_peqzone_flags` table to database and database_schema.h.
- Required SQL update to add the new table.
- Add client:ClearPEQZoneFlag(zone_id) to Lua.
- Add client:HasPEQZoneFlag(zone_id) to Lua.
- Add client:LoadPEQZoneFlags() to Lua.
- Add client:LoadZoneFlags() to Lua.
- Add client:SendPEQZoneFlagInfo(client) to Lua.
- Add client:SetPEQZoneFlag(zone_id) to Lua.
- Add $client->ClearPEQZoneFlag(zone_id) to Perl.
- Add $client->HasPEQZoneFlag(zone_id) to Perl.
- Add $client->LoadPEQZoneFlags() to Perl.
- Add $client->SendPEQZoneFlagInfo(client) to Perl.
- Add $client->SetPEQZoneFlag(zone_id) to Perl.

* Fixes.
2022-05-01 19:39:52 -04:00
Chris Miles c7dbdfae58 [Combat] Basic Combat Recording (#2090)
* Basic combat recording

* Update combat_record.h
2022-05-01 18:08:12 -05:00
Kinglykrab 759f9bd007 [Bots] Bot::PerformTradeWithClient Cleanup. (#2084)
* [Bots] Bot::PerformTradeWithClient Cleanup.
- Cleanups message and logic.
- Initial cleanup to eventually allow easy use with Perl/Lua quest API.

* Duplicated comment.
2022-05-01 19:05:12 -04:00
Chris Miles 8f0b80097e [Refactor] Simplify NPC Loading (#2087)
* Refactor / simplify NPC loading

* Update spacing [skip ci]

* Update base_npc_types_repository.h
2022-05-01 17:04:38 -05:00
Chris Miles 71ae03d5bc [Compile] Decrease build times using unity build strategy (#2089) 2022-05-01 15:53:21 -05:00
KayenEQ 34dc081306 [Spells] Update to target types Beam and Cone to ignore invalid targets. (#2080) 2022-05-01 16:50:55 -04:00
Chris Miles 35b35f85cf [Logging] Update BUILD_LOGGING=false Blank Aliases (#2083) 2022-05-01 14:54:46 -05:00
Chris Miles 90da136b7a [Regen] Implement Per Second HP Regen for NPCs (#2086)
* Implement NPC per second regen

* Add hp_regen_per_second to ModifyNPCStat

* Take per second regen the rest of the way

* Add #npcedit hp_regen_per_second

* Add db migration
2022-05-01 10:26:16 -04:00
Chris Miles 5b4aeaa457 [Code Cleanup] Remove this-> in code where its implied (#2088) 2022-05-01 10:22:09 -04:00
Kinglykrab b02008ec53 [Bots] Remove unused methods. (#2082)
- Remove Bot::BotTradeSwapItem().
- Remove Bot::ApplySpecialAttackMod().
2022-04-30 18:13:34 -04:00
Kinglykrab c709a6aa8e [Quest API] Add multiple inventory method short hands to client. (#2078)
- Allows you to just directly use Client instead of having to grab reference to inventory.
- Add $client->CountAugmentEquippedByID(item_id) to Perl.
- Add $client->HasAugmentEquippedByID(item_id) to Perl.
- Add $client->CountItemEquippedByID(item_id) to Perl.
- Add $client->HasItemEquippedByID(item_id) to Perl.
- Add client:CountAugmentEquippedByID(item_id) to Lua.
- Add client:HasAugmentEquippedByID(item_id) to Lua
- Add client:CountItemEquippedByID(item_id) to Lua.
- Add client:HasItemEquippedByID(item_id) to Lua.
2022-04-30 11:47:05 -04:00
Kinglykrab 9113508269 [Bots] Fix ^dyearmor command math. (#2081)
* [Bots] Fix ^dyearmor command math.

* Typo.
2022-04-30 10:57:22 -04:00
Kinglykrab 1b7c12297d [Quest API] Add AddPlatinum(), GetCarriedPlatinum() and TakePlatinum() to Perl/Lua. (#2079)
* [Quest API] Add AddPlatinum(), GetCarriedPlatinum() and TakePlatinum() to Perl/Lua.
- Allows for easier NPC interactions.
- GetCarriedPlatinum() adds together all currencies in inventory based on conversion amounts so it works easily with removals/checks.
- Add $client->AddPlatinum(platinum, update_client) to Perl.
- Add $client->GetCarriedPlatinum() to Perl.
- Add $client->TakePlatinum(platinum, update_client) to Perl.
- Add client:AddPlatinum(platinum, update_client) to Lua.
- Add client:GetCarriedPlatinum() to Lua.
- Add client:TakePlatinum(platinum, update_client) to Lua.

* Formatting.
2022-04-30 10:57:05 -04:00
nytmyr b1311780a7 [Quest API] Add EVENT_SKILL_UP & EVENT_LANGUAGE_SKILL_UP to Perl/Lua (#2076)
Added EVENT_SKILL_UP to Perl/Lua

Adds sub EVENT_SKILL_UP output for use.
Exports:
$skill_id - ID of the skill being exported. Will export skill or language as the same so check below.
$skill_value - New skill level
$skill_max - Maximum value of skill
$is_tradeskill - 0 for non-tradeskill, 1 for tradeskill

Example usage:
sub EVENT_SKILL_UP {
if($is_tradeskill == 0) {
		quest::shout("Skill Increase! " . $client->GetCleanName() . " has increased their " . quest::getskillname($skill_id) . " to " . $skill_value . " of " . $skill_max . "!"); #deleteme
	}
	if ($is_tradeskill == 1) {
		quest::shout("Tradeskill Increase! " . $client->GetCleanName() . " has increased their " . quest::getskillname($skill_id) . " to " . $skill_value . " of " . $skill_max . "!"); #deleteme
	}
}

Adds sub EVENT_LANGUAGE_SKILL_UP output for use.
Exports:
$skill_id - ID of the skill being exported. Will export skill or language as the same so check below.
$skill_value - New skill level
$skill_max - Maximum value of skill

Example usage:
sub EVENT_LANGUAGE_SKILL_UP  {
		quest::shout("Language Increase! " . $client->GetCleanName() . " has increased their " . quest::getlanguagename($skill_id) . " to " . $skill_value . " of " . $skill_max . "!"); #deleteme
}

Co-authored-by: toxin06 <53322305+toxin06@users.noreply.github.com>
2022-04-25 11:18:52 -05:00
cybernine186 0d734a0837 Bug Fix for WorldServer::HandleMessage, CZUpdateType_NPC (#2074) 2022-04-21 08:17:29 -04:00
420 changed files with 32356 additions and 44482 deletions
+10 -1
View File
@@ -7,10 +7,19 @@ name: EQEmulator Server Linux CI
concurrency:
limit: 1
volumes:
- name: cache
host:
path: /var/lib/cache
steps:
- name: server-build
# Source build script https://github.com/Akkadius/akk-stack/blob/master/containers/eqemu-server/Dockerfile#L20
image: akkadius/eqemu-server:latest
commands:
- sudo chown eqemu:eqemu /drone/src/ * -R
- git submodule init && git submodule update && mkdir -p build && cd build && cmake -DEQEMU_BUILD_LOGIN=ON -DEQEMU_ENABLE_BOTS=ON -DEQEMU_BUILD_LUA=ON -G 'Unix Makefiles' .. && make -j$((`nproc`-4))
- sudo chown eqemu:eqemu /home/eqemu/.ccache/ * -R
- git submodule init && git submodule update && mkdir -p build && cd build && cmake -DEQEMU_BUILD_LOGIN=ON -DEQEMU_ENABLE_BOTS=ON -DEQEMU_BUILD_LUA=ON -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING="-O0 -g -DNDEBUG" -G 'Unix Makefiles' .. && make -j$((`nproc`-4))
volumes:
- name: cache
path: /home/eqemu/.ccache/
+1 -1
View File
@@ -373,7 +373,7 @@ ENDIF()
IF(PERL_LIBRARY_ENABLED)
OPTION(EQEMU_BUILD_PERL "Build Perl parser." ON)
IF(EQEMU_BUILD_PERL)
SET(SERVER_LIBS ${SERVER_LIBS} ${PERL_LIBRARY_LIBS})
SET(SERVER_LIBS ${SERVER_LIBS} ${PERL_LIBRARY_LIBS} perlbind)
INCLUDE_DIRECTORIES(SYSTEM "${PERL_LIBRARY_INCLUDE}")
ADD_DEFINITIONS(-DEMBPERL)
ADD_DEFINITIONS(-DEMBPERL_PLUGIN)
+4
View File
@@ -7,6 +7,7 @@ SET(common_sources
compression.cpp
condition.cpp
content/world_content_service.cpp
discord/discord.cpp
crash.cpp
crc16.cpp
crc32.cpp
@@ -34,6 +35,7 @@ SET(common_sources
event_sub.cpp
expedition_lockout_timer.cpp
extprofile.cpp
discord_manager.cpp
faction.cpp
file_util.cpp
guild_base.cpp
@@ -491,6 +493,8 @@ SET(common_headers
database_schema.h
dbcore.h
deity.h
discord/discord.h
discord_manager.h
dynamic_zone_base.h
emu_constants.h
emu_limits.h
+8 -8
View File
@@ -23,18 +23,18 @@
BasePacket::BasePacket(const unsigned char *buf, uint32 len)
{
this->pBuffer=nullptr;
this->size=0;
this->_wpos = 0;
this->_rpos = 0;
this->timestamp.tv_sec = 0;
pBuffer=nullptr;
size=0;
_wpos = 0;
_rpos = 0;
timestamp.tv_sec = 0;
if (len>0) {
this->size=len;
size=len;
pBuffer= new unsigned char[len];
if (buf) {
memcpy(this->pBuffer,buf,len);
memcpy(pBuffer,buf,len);
} else {
memset(this->pBuffer,0,len);
memset(pBuffer,0,len);
}
}
}
+5 -5
View File
@@ -380,12 +380,12 @@ const char *GetClassIDName(uint8 class_id, uint8 level)
return "Merchant";
case DISCORD_MERCHANT:
return "Discord Merchant";
case ADVENTURERECRUITER:
case ADVENTURE_RECRUITER:
return "Adventure Recruiter";
case ADVENTUREMERCHANT:
case ADVENTURE_MERCHANT:
return "Adventure Merchant";
case CORPSE_CLASS:
return "Corpse Class";
case LDON_TREASURE:
return "LDoN Treasure";
case TRIBUTE_MASTER:
return "Tribute Master";
case GUILD_TRIBUTE_MASTER:
@@ -400,7 +400,7 @@ const char *GetClassIDName(uint8 class_id, uint8 level)
return "Fellowship Master";
case ALT_CURRENCY_MERCHANT:
return "Alternate Currency Merchant";
case MERCERNARY_MASTER:
case MERCENARY_MASTER:
return "Mercenary Liaison";
default:
return "Unknown";
+4 -5
View File
@@ -55,10 +55,9 @@
#define BANKER 40
#define MERCHANT 41
#define DISCORD_MERCHANT 59
#define ADVENTURERECRUITER 60
#define ADVENTUREMERCHANT 61
#define LDON_TREASURE 62 // objects you can use /open on first seen in LDONs
#define CORPSE_CLASS 62 // only seen on Danvi's Corpse in Akheva so far..
#define ADVENTURE_RECRUITER 60
#define ADVENTURE_MERCHANT 61
#define LDON_TREASURE 62 // objects you can use /open on first seen in LDONs, seen on Danvi's Corpse in Akheva
#define TRIBUTE_MASTER 63
#define GUILD_TRIBUTE_MASTER 64 // not sure
#define GUILD_BANKER 66
@@ -66,7 +65,7 @@
#define DARK_REIGN_MERCHANT 68
#define FELLOWSHIP_MASTER 69
#define ALT_CURRENCY_MERCHANT 70
#define MERCERNARY_MASTER 71
#define MERCENARY_MASTER 71
// player class values
+3 -3
View File
@@ -54,7 +54,7 @@ namespace Expansion {
VeilOfAlaris,
RainOfFear,
CallOfTheForsaken,
TheDarkendSea,
TheDarkenedSea,
TheBrokenMirror,
EmpiresOfKunark,
RingOfScale,
@@ -127,7 +127,7 @@ public:
bool IsVeilOfAlarisEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::VeilOfAlaris || GetCurrentExpansion() == Expansion::EXPANSION_ALL; }
bool IsRainOfFearEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::RainOfFear || GetCurrentExpansion() == Expansion::EXPANSION_ALL; }
bool IsCallOfTheForsakenEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::CallOfTheForsaken || GetCurrentExpansion() == Expansion::EXPANSION_ALL; }
bool IsTheDarkendSeaEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::TheDarkendSea || GetCurrentExpansion() == Expansion::EXPANSION_ALL; }
bool IsTheDarkenedSeaEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::TheDarkenedSea || GetCurrentExpansion() == Expansion::EXPANSION_ALL; }
bool IsTheBrokenMirrorEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::TheBrokenMirror || GetCurrentExpansion() == Expansion::EXPANSION_ALL; }
bool IsEmpiresOfKunarkEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::EmpiresOfKunark || GetCurrentExpansion() == Expansion::EXPANSION_ALL; }
bool IsRingOfScaleEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::RingOfScale || GetCurrentExpansion() == Expansion::EXPANSION_ALL; }
@@ -155,7 +155,7 @@ public:
bool IsCurrentExpansionVeilOfAlaris() { return current_expansion == Expansion::ExpansionNumber::VeilOfAlaris; }
bool IsCurrentExpansionRainOfFear() { return current_expansion == Expansion::ExpansionNumber::RainOfFear; }
bool IsCurrentExpansionCallOfTheForsaken() { return current_expansion == Expansion::ExpansionNumber::CallOfTheForsaken; }
bool IsCurrentExpansionTheDarkendSea() { return current_expansion == Expansion::ExpansionNumber::TheDarkendSea; }
bool IsCurrentExpansionTheDarkenedSea() { return current_expansion == Expansion::ExpansionNumber::TheDarkenedSea; }
bool IsCurrentExpansionTheBrokenMirror() { return current_expansion == Expansion::ExpansionNumber::TheBrokenMirror; }
bool IsCurrentExpansionEmpiresOfKunark() { return current_expansion == Expansion::ExpansionNumber::EmpiresOfKunark; }
bool IsCurrentExpansionRingOfScale() { return current_expansion == Expansion::ExpansionNumber::RingOfScale; }
+185 -206
View File
@@ -50,6 +50,8 @@
#include "http/httplib.h"
#include "http/uri.h"
#include "repositories/zone_repository.h"
extern Client client;
Database::Database () {
@@ -92,112 +94,123 @@ Database::~Database()
*/
uint32 Database::CheckLogin(const char* name, const char* password, const char *loginserver, int16* oStatus) {
if(strlen(name) >= 50 || strlen(password) >= 50)
if (strlen(name) >= 50 || strlen(password) >= 50)
return(0);
char tmpUN[100];
char tmpPW[100];
char temporary_username[100];
char temporary_password[100];
DoEscapeString(tmpUN, name, strlen(name));
DoEscapeString(tmpPW, password, strlen(password));
DoEscapeString(temporary_username, name, strlen(name));
DoEscapeString(temporary_password, password, strlen(password));
std::string query = StringFormat("SELECT id, status FROM account WHERE `name`='%s' AND ls_id='%s' AND password is not null "
"and length(password) > 0 and (password='%s' or password=MD5('%s'))",
tmpUN, EscapeString(loginserver).c_str(), tmpPW, tmpPW);
std::string query = fmt::format(
"SELECT id, status FROM account WHERE `name` = '{}' AND ls_id = '{}' AND password is NOT NULL "
"AND length(password) > 0 AND (password = '{}' OR password = MD5('{}'))",
temporary_username,
EscapeString(loginserver),
temporary_password,
temporary_password
);
auto results = QueryDatabase(query);
if (!results.Success())
{
if (!results.Success() || !results.RowCount()) {
return 0;
}
if(results.RowCount() == 0)
return 0;
auto row = results.begin();
uint32 id = atoi(row[0]);
auto id = std::stoul(row[0]);
if (oStatus)
*oStatus = atoi(row[1]);
if (oStatus) {
*oStatus = std::stoi(row[1]);
}
return id;
}
//Get Banned IP Address List - Only return false if the incoming connection's IP address is not present in the banned_ips table.
bool Database::CheckBannedIPs(const char* loginIP)
bool Database::CheckBannedIPs(std::string login_ip)
{
std::string query = StringFormat("SELECT ip_address FROM banned_ips WHERE ip_address='%s'", loginIP);
auto query = fmt::format(
"SELECT ip_address FROM banned_ips WHERE ip_address = '{}'",
login_ip
);
auto results = QueryDatabase(query);
if (!results.Success())
{
if (!results.Success() || results.RowCount() != 0) {
return true;
}
if (results.RowCount() != 0)
return true;
return false;
}
bool Database::AddBannedIP(char* bannedIP, const char* notes) {
std::string query = StringFormat("INSERT into banned_ips SET ip_address='%s', notes='%s'", bannedIP, notes);
bool Database::AddBannedIP(std::string banned_ip, std::string notes) {
auto query = fmt::format(
"INSERT into banned_ips SET ip_address = '{}', notes = '{}'",
EscapeString(banned_ip),
EscapeString(notes)
);
auto results = QueryDatabase(query);
if (!results.Success()) {
return false;
}
return true;
}
bool Database::CheckGMIPs(const char* ip_address, uint32 account_id) {
std::string query = StringFormat("SELECT * FROM `gm_ips` WHERE `ip_address` = '%s' AND `account_id` = %i", ip_address, account_id);
bool Database::CheckGMIPs(std::string login_ip, uint32 account_id) {
auto query = fmt::format(
"SELECT * FROM `gm_ips` WHERE `ip_address` = '{}' AND `account_id` = {}",
login_ip,
account_id
);
auto results = QueryDatabase(query);
if (!results.Success())
if (!results.Success()) {
return false;
}
if (results.RowCount() == 1)
if (results.RowCount() == 1) {
return true;
}
return false;
}
bool Database::AddGMIP(char* ip_address, char* name) {
std::string query = StringFormat("INSERT into `gm_ips` SET `ip_address` = '%s', `name` = '%s'", ip_address, name);
auto results = QueryDatabase(query);
return results.Success();
}
void Database::LoginIP(uint32 AccountID, const char* LoginIP) {
std::string query = StringFormat("INSERT INTO account_ip SET accid=%i, ip='%s' ON DUPLICATE KEY UPDATE count=count+1, lastused=now()", AccountID, LoginIP);
void Database::LoginIP(uint32 account_id, std::string login_ip) {
auto query = fmt::format(
"INSERT INTO account_ip SET accid = {}, ip = '{}' ON DUPLICATE KEY UPDATE count=count+1, lastused=now()",
account_id,
login_ip
);
QueryDatabase(query);
}
int16 Database::CheckStatus(uint32 account_id)
{
std::string query = StringFormat(
"SELECT `status`, TIMESTAMPDIFF(SECOND, NOW(), `suspendeduntil`) FROM `account` WHERE `id` = %i",
account_id);
auto query = fmt::format(
"SELECT `status`, TIMESTAMPDIFF(SECOND, NOW(), `suspendeduntil`) FROM `account` WHERE `id` = {}",
account_id
);
auto results = QueryDatabase(query);
if (!results.Success())
return 0;
if (results.RowCount() != 1)
if (!results.Success() || results.RowCount() != 1) {
return 0;
}
auto row = results.begin();
int16 status = atoi(row[0]);
int16 status = std::stoi(row[0]);
int32 date_diff = 0;
if (row[1] != nullptr)
date_diff = atoi(row[1]);
if (row[1]) {
date_diff = std::stoi(row[1]);
}
if (date_diff > 0)
if (date_diff > 0) {
return -1;
}
return status;
}
@@ -307,9 +320,7 @@ bool Database::SetAccountStatus(const std::string& account_name, int16 status)
LogInfo("Account [{}] is attempting to be set to status [{}]", account_name, status);
std::string query = fmt::format(
SQL(
UPDATE account SET status = {} WHERE name = '{}'
),
"UPDATE account SET status = {} WHERE name = '{}'",
status,
account_name
);
@@ -807,36 +818,34 @@ uint32 Database::GetAccountIDByChar(uint32 char_id) {
return atoi(row[0]);
}
uint32 Database::GetAccountIDByName(const char* accname, const char *loginserver, int16* status, uint32* lsid) {
if (!isAlphaNumeric(accname))
uint32 Database::GetAccountIDByName(std::string account_name, std::string loginserver, int16* status, uint32* lsid) {
if (!isAlphaNumeric(account_name.c_str())) {
return 0;
}
std::string query = StringFormat("SELECT `id`, `status`, `lsaccount_id` FROM `account` WHERE `name` = '%s' AND `ls_id`='%s' LIMIT 1",
EscapeString(accname).c_str(), EscapeString(loginserver).c_str());
auto query = fmt::format(
"SELECT `id`, `status`, `lsaccount_id` FROM `account` WHERE `name` = '{}' AND `ls_id` = '{}' LIMIT 1",
EscapeString(account_name),
EscapeString(loginserver)
);
auto results = QueryDatabase(query);
if (!results.Success()) {
if (!results.Success() || !results.RowCount()) {
return 0;
}
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
auto account_id = std::stoul(row[0]);
uint32 id = atoi(row[0]);
if (status)
*status = atoi(row[1]);
if (lsid) {
if (row[2])
*lsid = atoi(row[2]);
else
*lsid = 0;
if (status) {
*status = static_cast<int16>(std::stoi(row[1]));
}
return id;
if (lsid) {
*lsid = row[2] ? std::stoul(row[2]) : 0;
}
return account_id;
}
void Database::GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID) {
@@ -1100,21 +1109,6 @@ bool Database::GetZoneLongName(const char* short_name, char** long_name, char* f
return true;
}
uint32 Database::GetZoneGraveyardID(uint32 zone_id, uint32 version) {
std::string query = StringFormat("SELECT graveyard_id FROM zone WHERE zoneidnumber='%u' AND (version=%i OR version=0) ORDER BY version DESC", zone_id, version);
auto results = QueryDatabase(query);
if (!results.Success())
return 0;
if (results.RowCount() == 0)
return 0;
auto row = results.begin();
return atoi(row[0]);
}
bool Database::GetZoneGraveyard(const uint32 graveyard_id, uint32* graveyard_zoneid, float* graveyard_x, float* graveyard_y, float* graveyard_z, float* graveyard_heading) {
std::string query = StringFormat("SELECT zone_id, x, y, z, heading FROM graveyard WHERE id=%i", graveyard_id);
@@ -1143,108 +1137,84 @@ bool Database::GetZoneGraveyard(const uint32 graveyard_id, uint32* graveyard_zon
return true;
}
uint8 Database::GetPEQZone(uint32 zoneID, uint32 version){
std::string query = StringFormat("SELECT peqzone from zone where zoneidnumber='%i' AND (version=%i OR version=0) ORDER BY version DESC", zoneID, version);
uint8 Database::GetPEQZone(uint32 zone_id, uint32 version){
std::string query = fmt::format(
"SELECT peqzone FROM zone WHERE zoneidnumber = {} AND (version = {} OR version = 0) ORDER BY version DESC LIMIT 1",
zone_id,
version
);
auto results = QueryDatabase(query);
if (!results.Success()) {
if (!results.Success() || !results.RowCount()) {
return 0;
}
if (results.RowCount() == 0)
return 0;
auto row = results.begin();
return atoi(row[0]);
return static_cast<uint8>(std::stoi(row[0]));
}
bool Database::CheckNameFilter(const char* name, bool surname)
bool Database::CheckNameFilter(std::string name, bool surname)
{
std::string str_name = name;
name = str_tolower(name);
// the minimum 4 is enforced by the client too
if (!name || strlen(name) < 4)
{
if (name.empty() || name.size() < 4) {
return false;
}
// Given name length is enforced by the client too
if (!surname && strlen(name) > 15)
{
if (!surname && name.size() > 15) {
return false;
}
for (size_t i = 0; i < str_name.size(); i++)
{
if(!isalpha(str_name[i]))
{
for (size_t i = 0; i < name.size(); i++) {
if (!isalpha(name[i])) {
return false;
}
}
for(size_t x = 0; x < str_name.size(); ++x)
{
str_name[x] = tolower(str_name[x]);
}
char c = '\0';
uint8 num_c = 0;
for(size_t x = 0; x < str_name.size(); ++x)
{
if(str_name[x] == c)
{
for (size_t x = 0; x < name.size(); ++x) {
if (name[x] == c) {
num_c++;
}
else
{
} else {
num_c = 1;
c = str_name[x];
c = name[x];
}
if(num_c > 2)
{
if (num_c > 2) {
return false;
}
}
std::string query("SELECT name FROM name_filter");
std::string query = "SELECT name FROM name_filter";
auto results = QueryDatabase(query);
if (!results.Success())
{
// false through to true? shouldn't it be falls through to false?
if (!results.Success()) {
return true;
}
for (auto row = results.begin();row != results.end();++row)
{
std::string current_row = row[0];
for(size_t x = 0; x < current_row.size(); ++x)
current_row[x] = tolower(current_row[x]);
if(str_name.find(current_row) != std::string::npos)
for (auto row : results) {
std::string current_row = str_tolower(row[0]);
if (name.find(current_row) != std::string::npos) {
return false;
}
}
return true;
}
bool Database::AddToNameFilter(const char* name) {
std::string query = StringFormat("INSERT INTO name_filter (name) values ('%s')", name);
bool Database::AddToNameFilter(std::string name) {
auto query = fmt::format(
"INSERT INTO name_filter (name) values ('{}')",
name
);
auto results = QueryDatabase(query);
if (!results.Success())
{
if (!results.Success() || !results.RowsAffected()) {
return false;
}
if (results.RowsAffected() == 0)
return false;
return true;
}
@@ -1332,16 +1302,16 @@ bool Database::UpdateName(const char* oldname, const char* newname) {
}
// If the name is used or an error occurs, it returns false, otherwise it returns true
bool Database::CheckUsedName(const char* name) {
std::string query = StringFormat("SELECT `id` FROM `character_data` WHERE `name` = '%s'", name);
bool Database::CheckUsedName(std::string name) {
auto query = fmt::format(
"SELECT `id` FROM `character_data` WHERE `name` = '{}'",
name
);
auto results = QueryDatabase(query);
if (!results.Success()) {
if (!results.Success() || results.RowCount()) {
return false;
}
if (results.RowCount() > 0)
return false;
return true;
}
@@ -1499,41 +1469,25 @@ uint8 Database::GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16
return base_cap;
}
uint32 Database::GetCharacterInfo(
const char *iName,
uint32 *oAccID,
uint32 *oZoneID,
uint32 *oInstanceID,
float *oX,
float *oY,
float *oZ
)
uint32 Database::GetCharacterInfo(std::string character_name, uint32 *account_id, uint32 *zone_id, uint32 *instance_id)
{
std::string query = StringFormat(
"SELECT `id`, `account_id`, `zone_id`, `zone_instance`, `x`, `y`, `z` FROM `character_data` WHERE `name` = '%s'",
EscapeString(iName).c_str()
auto query = fmt::format(
"SELECT `id`, `account_id`, `zone_id`, `zone_instance` FROM `character_data` WHERE `name` = '{}'",
EscapeString(character_name)
);
auto results = QueryDatabase(query);
if (!results.Success()) {
if (!results.Success() || !results.RowCount()) {
return 0;
}
if (results.RowCount() != 1) {
return 0;
}
auto row = results.begin();
auto character_id = std::stoul(row[0]);
*account_id = std::stoul(row[1]);
*zone_id = std::stoul(row[2]);
*instance_id = std::stoul(row[3]);
auto row = results.begin();
uint32 charid = atoi(row[0]);
if (oAccID) { *oAccID = atoi(row[1]); }
if (oZoneID) { *oZoneID = atoi(row[2]); }
if (oInstanceID) { *oInstanceID = atoi(row[3]); }
if (oX) { *oX = atof(row[4]); }
if (oY) { *oY = atof(row[5]); }
if (oZ) { *oZ = atof(row[6]); }
return charid;
return character_id;
}
bool Database::UpdateLiveChar(char* charname, uint32 account_id) {
@@ -1657,29 +1611,36 @@ uint32 Database::GetGroupID(const char* name){
return atoi(row[0]);
}
/* Is this really getting used properly... A half implementation ? Akkadius */
char* Database::GetGroupLeaderForLogin(const char* name, char* leaderbuf) {
strcpy(leaderbuf, "");
std::string Database::GetGroupLeaderForLogin(std::string character_name) {
uint32 group_id = 0;
std::string query = StringFormat("SELECT `groupid` FROM `group_id` WHERE `name` = '%s'", name);
auto query = fmt::format(
"SELECT `groupid` FROM `group_id` WHERE `name` = '{}'",
character_name
);
auto results = QueryDatabase(query);
for (auto row = results.begin(); row != results.end(); ++row)
if (row[0])
group_id = atoi(row[0]);
if (results.Success() && results.RowCount()) {
auto row = results.begin();
group_id = std::stoul(row[0]);
}
if (group_id == 0)
return leaderbuf;
if (!group_id) {
return std::string();
}
query = StringFormat("SELECT `leadername` FROM `group_leaders` WHERE `gid` = '%u' LIMIT 1", group_id);
query = fmt::format(
"SELECT `leadername` FROM `group_leaders` WHERE `gid` = {} LIMIT 1",
group_id
);
results = QueryDatabase(query);
for (auto row = results.begin(); row != results.end(); ++row)
if (row[0])
strcpy(leaderbuf, row[0]);
if (results.Success() && results.RowCount()) {
auto row = results.begin();
return row[0];
}
return leaderbuf;
return std::string();
}
void Database::SetGroupLeaderName(uint32 gid, const char* name) {
@@ -2274,28 +2235,32 @@ bool Database::SaveTime(int8 minute, int8 hour, int8 day, int8 month, int16 year
}
int Database::GetIPExemption(std::string account_ip) {
std::string query = StringFormat("SELECT `exemption_amount` FROM `ip_exemptions` WHERE `exemption_ip` = '%s'", account_ip.c_str());
auto results = QueryDatabase(query);
if (results.Success() && results.RowCount() > 0) {
auto row = results.begin();
return atoi(row[0]);
}
return RuleI(World, MaxClientsPerIP);
}
void Database::SetIPExemption(std::string account_ip, int exemption_amount) {
std::string query = fmt::format(
"SELECT `exemption_id` FROM `ip_exemptions` WHERE `exemption_ip` = '{}'",
auto query = fmt::format(
"SELECT `exemption_amount` FROM `ip_exemptions` WHERE `exemption_ip` = '{}'",
account_ip
);
auto results = QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
return RuleI(World, MaxClientsPerIP);
}
auto row = results.begin();
return std::stoi(row[0]);
}
void Database::SetIPExemption(std::string account_ip, int exemption_amount) {
auto query = fmt::format(
"SELECT `exemption_id` FROM `ip_exemptions` WHERE `exemption_ip` = '{}'",
account_ip
);
uint32 exemption_id = 0;
if (results.Success() && results.RowCount() > 0) {
auto results = QueryDatabase(query);
if (results.Success() && results.RowCount()) {
auto row = results.begin();
exemption_id = atoi(row[0]);
exemption_id = std::stoul(row[0]);
}
query = fmt::format(
@@ -2304,13 +2269,14 @@ void Database::SetIPExemption(std::string account_ip, int exemption_amount) {
exemption_amount
);
if (exemption_id != 0) {
if (exemption_id) {
query = fmt::format(
"UPDATE `ip_exemptions` SET `exemption_amount` = {} WHERE `exemption_ip` = '{}'",
exemption_amount,
account_ip
);
}
QueryDatabase(query);
}
@@ -2528,3 +2494,16 @@ void Database::SourceDatabaseTableFromUrl(std::string table_name, std::string ur
}
}
uint8 Database::GetMinStatus(uint32 zone_id, uint32 instance_version)
{
auto zones = ZoneRepository::GetWhere(
*this,
fmt::format(
"zoneidnumber = {} AND (version = {} OR version = 0) ORDER BY version DESC LIMIT 1",
zone_id,
instance_version
)
);
return !zones.empty() ? zones[0].min_status : 0;
}
+13 -15
View File
@@ -88,7 +88,6 @@ public:
/* Character Creation */
bool AddToNameFilter(const char *name);
bool CreateCharacter(
uint32 account_id,
char *name,
@@ -121,18 +120,18 @@ public:
/* General Information Queries */
bool AddBannedIP(char* bannedIP, const char* notes); //Add IP address to the banned_ips table.
bool AddGMIP(char* ip_address, char* name);
bool CheckBannedIPs(const char* loginIP); //Check incoming connection against banned IP table.
bool CheckGMIPs(const char* loginIP, uint32 account_id);
bool CheckNameFilter(const char* name, bool surname = false);
bool CheckUsedName(const char* name);
bool AddBannedIP(std::string banned_ip, std::string notes); //Add IP address to the banned_ips table.
bool AddToNameFilter(std::string name);
bool CheckBannedIPs(std::string login_ip); //Check incoming connection against banned IP table.
bool CheckGMIPs(std::string login_ip, uint32 account_id);
bool CheckNameFilter(std::string name, bool surname = false);
bool CheckUsedName(std::string name);
uint32 GetAccountIDByChar(const char* charname, uint32* oCharID = 0);
uint32 GetAccountIDByChar(uint32 char_id);
uint32 GetAccountIDByName(const char* accname, const char *loginserver, int16* status = 0, uint32* lsid = 0);
uint32 GetAccountIDByName(std::string account_name, std::string loginserver, int16* status = 0, uint32* lsid = 0);
uint32 GetCharacterID(const char *name);
uint32 GetCharacterInfo(const char* iName, uint32* oAccID = 0, uint32* oZoneID = 0, uint32* oInstanceID = 0, float* oX = 0, float* oY = 0, float* oZ = 0);
uint32 GetCharacterInfo(std::string character_name, uint32 *account_id, uint32 *zone_id, uint32 *instance_id);
uint32 GetGuildIDByCharID(uint32 char_id);
uint32 GetGroupIDByCharID(uint32 char_id);
uint32 GetRaidIDByCharID(uint32 char_id);
@@ -142,7 +141,7 @@ public:
std::string GetCharNameByID(uint32 char_id);
std::string GetNPCNameByID(uint32 npc_id);
std::string GetCleanNPCNameByID(uint32 npc_id);
void LoginIP(uint32 AccountID, const char* LoginIP);
void LoginIP(uint32 account_id, std::string login_ip);
/* Instancing */
@@ -208,8 +207,8 @@ public:
/* Groups */
char* GetGroupLeaderForLogin(const char* name,char* leaderbuf);
char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr);
std::string GetGroupLeaderForLogin(std::string character_name);
char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr);
uint32 GetGroupID(const char* name);
@@ -252,9 +251,8 @@ public:
bool GetZoneLongName(const char* short_name, char** long_name, char* file_name = 0, float* safe_x = 0, float* safe_y = 0, float* safe_z = 0, uint32* graveyard_id = 0, uint32* maxclients = 0);
bool LoadPTimers(uint32 charid, PTimerList &into);
uint32 GetZoneGraveyardID(uint32 zone_id, uint32 version);
uint8 GetPEQZone(uint32 zoneID, uint32 version);
uint8 GetPEQZone(uint32 zone_id, uint32 version);
uint8 GetMinStatus(uint32 zone_id, uint32 instance_version);
uint8 GetRaceSkill(uint8 skillid, uint8 in_race);
uint8 GetServerType();
uint8 GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 in_level);
+30
View File
@@ -163,6 +163,20 @@ std::string DatabaseDumpService::GetPlayerTablesList()
return trim(tables_list);
}
/**
* @return
*/
std::string DatabaseDumpService::GetBotTablesList()
{
std::string tables_list;
std::vector<std::string> tables = DatabaseSchema::GetBotTables();
for (const auto &table : tables) {
tables_list += table + " ";
}
return trim(tables_list);
}
/**
* @return
*/
@@ -317,6 +331,11 @@ void DatabaseDumpService::Dump()
tables_to_dump += GetPlayerTablesList() + " ";
dump_descriptor += "-player";
}
if (IsDumpBotTables()) {
tables_to_dump += GetBotTablesList() + " ";
dump_descriptor += "-bots";
}
if (IsDumpSystemTables()) {
tables_to_dump += GetSystemTablesList() + " ";
@@ -436,6 +455,7 @@ void DatabaseDumpService::Dump()
// LogDebug("[{}] login", (IsDumpLoginServerTables() ? "true" : "false"));
// LogDebug("[{}] player", (IsDumpPlayerTables() ? "true" : "false"));
// LogDebug("[{}] system", (IsDumpSystemTables() ? "true" : "false"));
// LogDebug("[{}] bot", (IsDumpBotTables() ? "true" : "false"));
}
bool DatabaseDumpService::IsDumpSystemTables() const
@@ -577,3 +597,13 @@ void DatabaseDumpService::SetDumpStateTables(bool dump_state_tables)
{
DatabaseDumpService::dump_state_tables = dump_state_tables;
}
bool DatabaseDumpService::IsDumpBotTables() const
{
return dump_bot_tables;
}
void DatabaseDumpService::SetDumpBotTables(bool dump_bot_tables)
{
DatabaseDumpService::dump_bot_tables = dump_bot_tables;
}
+4
View File
@@ -53,6 +53,8 @@ public:
void SetDumpDropTableSyntaxOnly(bool dump_drop_table_syntax_only);
bool IsDumpStateTables() const;
void SetDumpStateTables(bool dump_state_tables);
bool IsDumpBotTables() const;
void SetDumpBotTables(bool dump_bot_tables);
private:
bool dump_all_tables = false;
@@ -67,6 +69,7 @@ private:
bool dump_with_compression = false;
bool dump_output_to_console = false;
bool dump_drop_table_syntax_only = false;
bool dump_bot_tables = false;
std::string dump_path;
std::string dump_file_name;
@@ -75,6 +78,7 @@ private:
std::string GetMySQLVersion();
std::string GetBaseMySQLDumpCommand();
std::string GetPlayerTablesList();
std::string GetBotTablesList();
std::string GetSystemTablesList();
std::string GetStateTablesList();
std::string GetContentTablesList();
+36
View File
@@ -62,6 +62,7 @@ namespace DatabaseSchema {
{"character_pet_buffs", "char_id"},
{"character_pet_info", "char_id"},
{"character_pet_inventory", "char_id"},
{"character_peqzone_flags", "id"},
{"character_potionbelt", "id"},
{"character_skills", "id"},
{"character_spells", "id"},
@@ -129,6 +130,7 @@ namespace DatabaseSchema {
"character_pet_buffs",
"character_pet_info",
"character_pet_inventory",
"character_peqzone_flags",
"character_potionbelt",
"character_skills",
"character_spells",
@@ -373,6 +375,40 @@ namespace DatabaseSchema {
"inventory_versions",
};
}
/**
* @description Gets all player bot tables
* @note These tables have no content in the PEQ daily dump
*
* @return
*/
static std::vector<std::string> GetBotTables()
{
return {
"bot_buffs",
"bot_command_settings",
"bot_create_combinations",
"bot_data",
"bot_group_members",
"bot_groups",
"bot_guild_members",
"bot_heal_rotation_members",
"bot_heal_rotation_targets",
"bot_heal_rotations",
"bot_inspect_messages",
"bot_inventories",
"bot_owner_options",
"bot_pet_buffs",
"bot_pet_inventories",
"bot_pets",
"bot_spell_casting_chances",
"bot_spells_entries",
"bot_stances",
"bot_timers",
"vw_bot_character_mobs",
"vw_bot_groups"
};
}
}
+2 -2
View File
@@ -167,7 +167,7 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
if (LogSys.log_settings[Logs::MySQLQuery].is_category_enabled == 1) {
if ((strncasecmp(query, "select", 6) == 0)) {
LogMySQLQuery(
"{0} ({1} row{2} returned) ({3}s)",
"{0}; -- ({1} row{2} returned) ({3}s)",
query,
requestResult.RowCount(),
requestResult.RowCount() == 1 ? "" : "s",
@@ -176,7 +176,7 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
}
else {
LogMySQLQuery(
"{0} ({1} row{2} affected) ({3}s)",
"{0}; -- ({1} row{2} affected) ({3}s)",
query,
requestResult.RowsAffected(),
requestResult.RowsAffected() == 1 ? "" : "s",
+91
View File
@@ -0,0 +1,91 @@
#include "discord.h"
#include "../http/httplib.h"
#include "../json/json.h"
#include "../string_util.h"
#include "../eqemu_logsys.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);
return;
}
// split
auto s = SplitString(webhook_url, '/');
// url
std::string base_url = fmt::format("{}//{}", s[0], s[2]);
std::string endpoint = replace_string(webhook_url, base_url, "");
// client
httplib::Client cli(base_url.c_str());
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"}
};
// payload
Json::Value p;
p["content"] = message;
std::stringstream payload;
payload << p;
bool retry = true;
int retries = 0;
int retry_timer = 1000;
while (retry) {
if (auto res = cli.Post(endpoint.c_str(), payload.str(), "application/json")) {
if (res->status != 200 && res->status != 204) {
LogError("[Discord Client] 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 message [{}]", message);
retry = false;
}
retries++;
}
}
}
std::string Discord::FormatDiscordMessage(uint16 category_id, const std::string &message)
{
if (category_id == Logs::LogCategory::MySQLQuery) {
return fmt::format("```sql\n{}\n```", message);
}
return message + "\n";
}
+15
View File
@@ -0,0 +1,15 @@
#ifndef EQEMU_DISCORD_H
#define EQEMU_DISCORD_H
#include <string>
#include "../types.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);
};
#endif //EQEMU_DISCORD_H
+65
View File
@@ -0,0 +1,65 @@
#include "discord_manager.h"
#include "../common/discord/discord.h"
#include "../common/eqemu_logsys.h"
#include "../common/string_util.h"
void DiscordManager::QueueWebhookMessage(uint32 webhook_id, const std::string &message)
{
webhook_queue_lock.lock();
webhook_message_queue[webhook_id].emplace_back(message);
webhook_queue_lock.unlock();
}
constexpr int MAX_MESSAGE_LENGTH = 1900;
void DiscordManager::ProcessMessageQueue()
{
if (webhook_message_queue.empty()) {
return;
}
webhook_queue_lock.lock();
for (auto &q: webhook_message_queue) {
LogDiscord("Processing [{}] messages in queue for webhook ID [{}]...", q.second.size(), q.first);
auto webhook = LogSys.discord_webhooks[q.first];
std::string message;
for (auto &m: q.second) {
// next message would become too large
bool next_message_too_large = ((int) m.length() + (int) message.length()) > MAX_MESSAGE_LENGTH;
if (next_message_too_large) {
Discord::SendWebhookMessage(
message,
webhook.webhook_url
);
message = "";
}
message += m;
// one single message was too large
// this should rarely happen but the message will need to be split
if ((int) message.length() > MAX_MESSAGE_LENGTH) {
for (unsigned mi = 0; mi < message.length(); mi += MAX_MESSAGE_LENGTH) {
Discord::SendWebhookMessage(
message.substr(mi, MAX_MESSAGE_LENGTH),
webhook.webhook_url
);
}
message = "";
}
}
// final flush
if (!message.empty()) {
Discord::SendWebhookMessage(
message,
webhook.webhook_url
);
}
webhook_message_queue.erase(q.first);
}
webhook_queue_lock.unlock();
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef EQEMU_DISCORD_MANAGER_H
#define EQEMU_DISCORD_MANAGER_H
#include <mutex>
#include <map>
#include <vector>
#include "../common/types.h"
class DiscordManager {
public:
void QueueWebhookMessage(uint32 webhook_id, const std::string& message);
void ProcessMessageQueue();
private:
std::mutex webhook_queue_lock{};
std::map<uint32, std::vector<std::string>> webhook_message_queue{};
};
#endif
+74 -24
View File
@@ -22,7 +22,6 @@
#include "data_verification.h"
#include "bodytypes.h"
int16 EQ::invtype::GetInvTypeSize(int16 inv_type) {
static const int16 local_array[] = {
POSSESSIONS_SIZE,
@@ -189,15 +188,16 @@ const std::map<int, std::string>& EQ::constants::GetLanguageMap()
{ LANG_HADAL, "Hadal" },
{ LANG_UNKNOWN, "Unknown" }
};
return language_map;
}
std::string EQ::constants::GetLanguageName(int language_id)
{
if (EQ::ValueWithin(language_id, LANG_COMMON_TONGUE, LANG_UNKNOWN)) {
auto languages = EQ::constants::GetLanguageMap();
return languages[language_id];
return EQ::constants::GetLanguageMap().find(language_id)->second;
}
return std::string();
}
@@ -211,21 +211,22 @@ const std::map<uint32, std::string>& EQ::constants::GetLDoNThemeMap()
{ LDoNThemes::RUJ, "Rujarkian Hills" },
{ LDoNThemes::TAK, "Takish-Hiz" },
};
return ldon_theme_map;
}
std::string EQ::constants::GetLDoNThemeName(uint32 theme_id)
{
if (EQ::ValueWithin(theme_id, LDoNThemes::Unused, LDoNThemes::TAK)) {
auto ldon_themes = EQ::constants::GetLDoNThemeMap();
return ldon_themes[theme_id];
return EQ::constants::GetLDoNThemeMap().find(theme_id)->second;
}
return std::string();
}
const std::map<uint8, std::string>& EQ::constants::GetFlyModeMap()
const std::map<int8, std::string>& EQ::constants::GetFlyModeMap()
{
static const std::map<uint8, std::string> flymode_map = {
static const std::map<int8, std::string> flymode_map = {
{ GravityBehavior::Ground, "Ground" },
{ GravityBehavior::Flying, "Flying" },
{ GravityBehavior::Levitating, "Levitating" },
@@ -233,15 +234,16 @@ const std::map<uint8, std::string>& EQ::constants::GetFlyModeMap()
{ GravityBehavior::Floating, "Floating" },
{ GravityBehavior::LevitateWhileRunning, "Levitating While Running" },
};
return flymode_map;
}
std::string EQ::constants::GetFlyModeName(uint8 flymode_id)
std::string EQ::constants::GetFlyModeName(int8 flymode_id)
{
if (EQ::ValueWithin(flymode_id, GravityBehavior::Ground, GravityBehavior::LevitateWhileRunning)) {
auto flymodes = EQ::constants::GetFlyModeMap();
return flymodes[flymode_id];
return EQ::constants::GetFlyModeMap().find(flymode_id)->second;
}
return std::string();
}
@@ -288,15 +290,16 @@ const std::map<bodyType, std::string>& EQ::constants::GetBodyTypeMap()
{ BT_InvisMan, "Invisible Man" },
{ BT_Special, "Special" },
};
return bodytype_map;
}
std::string EQ::constants::GetBodyTypeName(bodyType bodytype_id)
{
auto bodytypes = EQ::constants::GetBodyTypeMap();
if (!bodytypes[bodytype_id].empty()) {
return bodytypes[bodytype_id];
if (EQ::constants::GetBodyTypeMap().find(bodytype_id) != EQ::constants::GetBodyTypeMap().end()) {
return EQ::constants::GetBodyTypeMap().find(bodytype_id)->second;
}
return std::string();
}
@@ -321,21 +324,23 @@ const std::map<uint8, std::string>& EQ::constants::GetAccountStatusMap()
{ AccountStatus::GMImpossible, "GM Impossible" },
{ AccountStatus::Max, "GM Max" }
};
return account_status_map;
}
std::string EQ::constants::GetAccountStatusName(uint8 account_status)
{
auto account_statuses = EQ::constants::GetAccountStatusMap();
std::string status_name;
for (auto status_level = account_statuses.rbegin(); status_level != account_statuses.rend(); ++status_level) {
for (
auto status_level = EQ::constants::GetAccountStatusMap().rbegin();
status_level != EQ::constants::GetAccountStatusMap().rend();
++status_level
) {
if (account_status >= status_level->first) {
status_name = status_level->second;
break;
return status_level->second;
}
}
return status_name;
return std::string();
}
const std::map<uint8, std::string>& EQ::constants::GetConsiderLevelMap()
@@ -351,15 +356,16 @@ const std::map<uint8, std::string>& EQ::constants::GetConsiderLevelMap()
{ ConsiderLevel::Threateningly, "Threateningly" },
{ ConsiderLevel::Scowls, "Scowls" }
};
return consider_level_map;
}
std::string EQ::constants::GetConsiderLevelName(uint8 faction_consider_level)
{
auto consider_levels = EQ::constants::GetConsiderLevelMap();
if (!consider_levels[faction_consider_level].empty()) {
return consider_levels[faction_consider_level];
if (EQ::constants::GetConsiderLevelMap().find(faction_consider_level) != EQ::constants::GetConsiderLevelMap().end()) {
return EQ::constants::GetConsiderLevelMap().find(faction_consider_level)->second;
}
return std::string();
}
@@ -371,14 +377,58 @@ const std::map<uint8, std::string>& EQ::constants::GetEnvironmentalDamageMap()
{ EnvironmentalDamage::Falling, "Falling" },
{ EnvironmentalDamage::Trap, "Trap" }
};
return damage_type_map;
}
std::string EQ::constants::GetEnvironmentalDamageName(uint8 damage_type)
{
if (EQ::ValueWithin(damage_type, EnvironmentalDamage::Lava, EnvironmentalDamage::Trap)) {
auto damage_types = EQ::constants::GetEnvironmentalDamageMap();
return damage_types[damage_type];
return EQ::constants::GetEnvironmentalDamageMap().find(damage_type)->second;
}
return std::string();
}
const std::map<uint8, std::string>& EQ::constants::GetStuckBehaviorMap()
{
static const std::map<uint8, std::string> stuck_behavior_map = {
{ StuckBehavior::RunToTarget, "Run To Target" },
{ StuckBehavior::WarpToTarget, "Warp To Target" },
{ StuckBehavior::TakeNoAction, "Take No Action" },
{ StuckBehavior::EvadeCombat, "Evade Combat" }
};
return stuck_behavior_map;
}
std::string EQ::constants::GetStuckBehaviorName(uint8 behavior_id)
{
if (EQ::ValueWithin(behavior_id, StuckBehavior::RunToTarget, StuckBehavior::EvadeCombat)) {
return EQ::constants::GetStuckBehaviorMap().find(behavior_id)->second;
}
return std::string();
}
const std::map<uint8, std::string>& EQ::constants::GetSpawnAnimationMap()
{
static const std::map<uint8, std::string> spawn_animation_map = {
{ SpawnAnimations::Standing, "Standing" },
{ SpawnAnimations::Sitting, "Sitting" },
{ SpawnAnimations::Crouching, "Crouching" },
{ SpawnAnimations::Laying, "Laying" },
{ SpawnAnimations::Looting, "Looting" }
};
return spawn_animation_map;
}
std::string EQ::constants::GetSpawnAnimationName(uint8 animation_id)
{
if (EQ::ValueWithin(animation_id, SpawnAnimations::Standing, SpawnAnimations::Looting)) {
return EQ::constants::GetSpawnAnimationMap().find(animation_id)->second;
}
return std::string();
}
+52 -3
View File
@@ -221,7 +221,7 @@ namespace EQ
stanceBurnAE
};
enum GravityBehavior : uint8 {
enum GravityBehavior : int8 {
Ground,
Flying,
Levitating,
@@ -237,6 +237,21 @@ namespace EQ
Trap
};
enum StuckBehavior : uint8 {
RunToTarget,
WarpToTarget,
TakeNoAction,
EvadeCombat
};
enum SpawnAnimations : uint8 {
Standing,
Sitting,
Crouching,
Laying,
Looting
};
const char *GetStanceName(StanceType stance_type);
int ConvertStanceTypeToIndex(StanceType stance_type);
@@ -246,8 +261,8 @@ namespace EQ
extern const std::map<uint32, std::string>& GetLDoNThemeMap();
std::string GetLDoNThemeName(uint32 theme_id);
extern const std::map<uint8, std::string>& GetFlyModeMap();
std::string GetFlyModeName(uint8 flymode_id);
extern const std::map<int8, std::string>& GetFlyModeMap();
std::string GetFlyModeName(int8 flymode_id);
extern const std::map<bodyType, std::string>& GetBodyTypeMap();
std::string GetBodyTypeName(bodyType bodytype_id);
@@ -261,6 +276,12 @@ namespace EQ
extern const std::map<uint8, std::string>& GetEnvironmentalDamageMap();
std::string GetEnvironmentalDamageName(uint8 damage_type);
extern const std::map<uint8, std::string>& GetStuckBehaviorMap();
std::string GetStuckBehaviorName(uint8 behavior_id);
extern const std::map<uint8, std::string>& GetSpawnAnimationMap();
std::string GetSpawnAnimationName(uint8 animation_id);
const int STANCE_TYPE_FIRST = stancePassive;
const int STANCE_TYPE_LAST = stanceBurnAE;
const int STANCE_TYPE_COUNT = stanceBurnAE;
@@ -416,4 +437,32 @@ enum ConsiderLevel : uint8 {
Scowls
};
enum TargetDescriptionType : uint8 {
LCSelf,
UCSelf,
LCYou,
UCYou,
LCYour,
UCYour
};
enum ReloadWorld : uint8 {
NoRepop = 0,
Repop,
ForceRepop
};
enum MerchantBucketComparison : uint8 {
BucketEqualTo = 0,
BucketNotEqualTo,
BucketGreaterThanOrEqualTo,
BucketLesserThanOrEqualTo,
BucketGreaterThan,
BucketLesserThan,
BucketIsAny,
BucketIsNotAny,
BucketIsBetween,
BucketIsNotBetween
};
#endif /*COMMON_EMU_CONSTANTS_H*/
+1
View File
@@ -455,6 +455,7 @@ N(OP_ServerListResponse),
N(OP_SessionReady),
N(OP_SetChatServer),
N(OP_SetChatServer2),
N(OP_SetFace),
N(OP_SetGroupTarget),
N(OP_SetGuildMOTD),
N(OP_SetGuildRank),
+17 -11
View File
@@ -2316,9 +2316,12 @@ struct FaceChange_Struct {
/*004*/ uint8 hairstyle;
/*005*/ uint8 beard;
/*006*/ uint8 face;
/*007*/ uint32 drakkin_heritage;
/*011*/ uint32 drakkin_tattoo;
/*015*/ uint32 drakkin_details;
/*007*/ uint8 unused_padding;
/*008*/ uint32 drakkin_heritage;
/*012*/ uint32 drakkin_tattoo;
/*016*/ uint32 drakkin_details;
/*020*/ uint32 entity_id;
/*024*/
//there are only 10 faces for barbs changing woad just
//increase the face value by ten so if there were 8 woad
//designs then there would be 80 barb faces
@@ -3621,14 +3624,17 @@ 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;
uint16 alt_currency_cost;
uint32 classes_required;
uint8 probability;
std::string bucket_name;
std::string bucket_value;
uint8 bucket_comparison;
};
struct TempMerchantList {
+53 -59
View File
@@ -23,6 +23,8 @@
#include "platform.h"
#include "string_util.h"
#include "misc.h"
#include "discord/discord.h"
#include "repositories/discord_webhooks_repository.h"
#include "repositories/logsys_categories_repository.h"
#include <iostream>
@@ -46,6 +48,7 @@ std::ofstream process_log;
#include <unistd.h>
#include <sys/stat.h>
#include <thread>
#endif
@@ -89,7 +92,7 @@ namespace Console {
EQEmuLogSys::EQEmuLogSys()
{
on_log_gmsay_hook = [](uint16 log_type, const std::string &) {};
on_log_console_hook = [](uint16 debug_level, uint16 log_type, const std::string &) {};
on_log_console_hook = [](uint16 log_type, const std::string &) {};
}
/**
@@ -108,6 +111,7 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults()
log_settings[log_category_id].log_to_console = 0;
log_settings[log_category_id].log_to_file = 0;
log_settings[log_category_id].log_to_gmsay = 0;
log_settings[log_category_id].log_to_discord = 0;
log_settings[log_category_id].is_category_enabled = 0;
}
@@ -134,6 +138,8 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults()
log_settings[Logs::HTTP].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::ChecksumVerification].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::ChecksumVerification].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::CombatRecord].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::Discord].log_to_console = static_cast<uint8>(Logs::General);
/**
* RFC 5424
@@ -153,7 +159,8 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults()
const bool log_to_console = log_settings[log_category_id].log_to_console > 0;
const bool log_to_file = log_settings[log_category_id].log_to_file > 0;
const bool log_to_gmsay = log_settings[log_category_id].log_to_gmsay > 0;
const bool is_category_enabled = log_to_console || log_to_file || log_to_gmsay;
const bool log_to_discord = log_settings[log_category_id].log_to_discord > 0;
const bool is_category_enabled = log_to_console || log_to_file || log_to_gmsay || log_to_discord;
if (is_category_enabled) {
log_settings[log_category_id].is_category_enabled = 1;
}
@@ -220,41 +227,12 @@ std::string EQEmuLogSys::FormatOutMessageString(
return return_string + "[" + Logs::LogCategoryName[log_category] + "] " + in_message;
}
/**
* @param debug_level
* @param log_category
* @param message
*/
void EQEmuLogSys::ProcessGMSay(
uint16 debug_level,
uint16 log_category,
const std::string &message
)
{
/**
* Enabling Netcode based GMSay output creates a feedback loop that ultimately ends in a crash
*/
if (log_category == Logs::LogCategory::Netcode) {
return;
}
/**
* Processes that actually support hooks
*/
if (EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformZone ||
EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformWorld
) {
on_log_gmsay_hook(log_category, message);
}
}
/**
* @param debug_level
* @param log_category
* @param message
*/
void EQEmuLogSys::ProcessLogWrite(
uint16 debug_level,
uint16 log_category,
const std::string &message
)
@@ -272,10 +250,9 @@ void EQEmuLogSys::ProcessLogWrite(
crash_log.close();
}
char time_stamp[80];
EQEmuLogSys::SetCurrentTimeStamp(time_stamp);
if (process_log) {
char time_stamp[80];
EQEmuLogSys::SetCurrentTimeStamp(time_stamp);
process_log << time_stamp << " " << message << std::endl;
}
}
@@ -371,7 +348,7 @@ uint16 EQEmuLogSys::GetGMSayColorFromCategory(uint16 log_category)
* @param log_category
* @param message
*/
void EQEmuLogSys::ProcessConsoleMessage(uint16 debug_level, uint16 log_category, const std::string &message)
void EQEmuLogSys::ProcessConsoleMessage(uint16 log_category, const std::string &message)
{
#ifdef _WINDOWS
HANDLE console_handle;
@@ -389,7 +366,7 @@ void EQEmuLogSys::ProcessConsoleMessage(uint16 debug_level, uint16 log_category,
std::cout << EQEmuLogSys::GetLinuxConsoleColorFromCategory(log_category) << message << LC_RESET << std::endl;
#endif
on_log_console_hook(debug_level, log_category, message);
on_log_console_hook(log_category, message);
}
/**
@@ -446,28 +423,28 @@ void EQEmuLogSys::Out(
...
)
{
bool log_to_console = true;
if (log_settings[log_category].log_to_console < debug_level) {
log_to_console = false;
}
bool log_to_console = log_settings[log_category].log_to_console > 0 &&
log_settings[log_category].log_to_console >= debug_level;
bool log_to_file = log_settings[log_category].log_to_file > 0 &&
log_settings[log_category].log_to_file >= debug_level;
bool log_to_gmsay = log_settings[log_category].log_to_gmsay > 0 &&
log_settings[log_category].log_to_gmsay >= debug_level &&
log_category != Logs::LogCategory::Netcode &&
(EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformZone ||
EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformWorld);
bool log_to_discord = EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformZone &&
log_settings[log_category].log_to_discord > 0 &&
log_settings[log_category].log_to_discord >= debug_level &&
log_settings[log_category].discord_webhook_id > 0 &&
log_settings[log_category].discord_webhook_id < MAX_DISCORD_WEBHOOK_ID;
bool log_to_file = true;
if (log_settings[log_category].log_to_file < debug_level) {
log_to_file = false;
}
bool log_to_gmsay = true;
if (log_settings[log_category].log_to_gmsay < debug_level) {
log_to_gmsay = false;
}
const bool nothing_to_log = !log_to_console && !log_to_file && !log_to_gmsay;
// bail out if nothing to log
const bool nothing_to_log = !log_to_console && !log_to_file && !log_to_gmsay && !log_to_discord;
if (nothing_to_log) {
return;
}
std::string prefix;
if (RuleB(Logging, PrintFileFunctionAndLine)) {
prefix = fmt::format("[{0}::{1}:{2}] ", base_file_name(file), func, line);
}
@@ -480,13 +457,16 @@ void EQEmuLogSys::Out(
std::string output_debug_message = EQEmuLogSys::FormatOutMessageString(log_category, prefix + output_message);
if (log_to_console) {
EQEmuLogSys::ProcessConsoleMessage(debug_level, log_category, output_debug_message);
EQEmuLogSys::ProcessConsoleMessage(log_category, output_debug_message);
}
if (log_to_gmsay) {
EQEmuLogSys::ProcessGMSay(debug_level, log_category, output_debug_message);
on_log_gmsay_hook(log_category, message);
}
if (log_to_file) {
EQEmuLogSys::ProcessLogWrite(debug_level, log_category, output_debug_message);
EQEmuLogSys::ProcessLogWrite(log_category, output_debug_message);
}
if (log_to_discord && on_log_discord_hook) {
on_log_discord_hook(log_category, log_settings[log_category].discord_webhook_id, output_message);
}
}
@@ -629,16 +609,20 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
continue;
}
log_settings[c.log_category_id].log_to_console = static_cast<uint8>(c.log_to_console);
log_settings[c.log_category_id].log_to_file = static_cast<uint8>(c.log_to_file);
log_settings[c.log_category_id].log_to_gmsay = static_cast<uint8>(c.log_to_gmsay);
log_settings[c.log_category_id].log_to_console = static_cast<uint8>(c.log_to_console);
log_settings[c.log_category_id].log_to_file = static_cast<uint8>(c.log_to_file);
log_settings[c.log_category_id].log_to_gmsay = static_cast<uint8>(c.log_to_gmsay);
log_settings[c.log_category_id].log_to_discord = static_cast<uint8>(c.log_to_discord);
log_settings[c.log_category_id].discord_webhook_id = c.discord_webhook_id;
// Determine if any output method is enabled for the category
// and set it to 1 so it can used to check if category is enabled
const bool log_to_console = log_settings[c.log_category_id].log_to_console > 0;
const bool log_to_file = log_settings[c.log_category_id].log_to_file > 0;
const bool log_to_gmsay = log_settings[c.log_category_id].log_to_gmsay > 0;
const bool is_category_enabled = log_to_console || log_to_file || log_to_gmsay;
const bool log_to_discord = log_settings[c.log_category_id].log_to_discord > 0 &&
log_settings[c.log_category_id].discord_webhook_id > 0;
const bool is_category_enabled = log_to_console || log_to_file || log_to_gmsay || log_to_discord;
if (is_category_enabled) {
log_settings[c.log_category_id].is_category_enabled = 1;
@@ -668,6 +652,7 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
new_category.log_to_console = log_settings[i].log_to_console;
new_category.log_to_gmsay = log_settings[i].log_to_gmsay;
new_category.log_to_file = log_settings[i].log_to_file;
new_category.log_to_discord = log_settings[i].log_to_discord;
LogsysCategoriesRepository::InsertOne(*m_database, new_category);
}
@@ -675,6 +660,14 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
LogInfo("Loaded [{}] log categories", categories.size());
auto webhooks = DiscordWebhooksRepository::All(*m_database);
if (!webhooks.empty()) {
for (auto &w: webhooks) {
discord_webhooks[w.id] = {w.id, w.webhook_name, w.webhook_url};
}
LogInfo("Loaded [{}] Discord webhooks", webhooks.size());
}
return this;
}
@@ -684,3 +677,4 @@ EQEmuLogSys *EQEmuLogSys::SetDatabase(Database *db)
return this;
}
+42 -11
View File
@@ -128,6 +128,9 @@ namespace Logs {
HTTP,
Saylink,
ChecksumVerification,
CombatRecord,
Hate,
Discord,
MaxCategoryID /* Don't Remove this */
};
@@ -214,6 +217,9 @@ namespace Logs {
"HTTP",
"Saylink",
"ChecksumVerification",
"CombatRecord",
"Hate",
"Discord",
};
}
@@ -221,6 +227,8 @@ namespace Logs {
class Database;
constexpr uint16 MAX_DISCORD_WEBHOOK_ID = 300;
class EQEmuLogSys {
public:
EQEmuLogSys();
@@ -283,9 +291,19 @@ public:
uint8 log_to_file;
uint8 log_to_console;
uint8 log_to_gmsay;
uint8 log_to_discord;
int discord_webhook_id;
uint8 is_category_enabled; /* When any log output in a category > 0, set this to 1 as (Enabled) */
};
struct OriginationInfo {
std::string zone_short_name;
std::string zone_long_name;
int instance_id;
};
OriginationInfo origination_info{};
/**
* Internally used memory reference for all log settings per category
* These are loaded via DB and have defaults loaded in LoadLogSettingsDefaults
@@ -293,24 +311,38 @@ public:
*/
LogSettings log_settings[Logs::LogCategory::MaxCategoryID]{};
struct DiscordWebhooks {
int id;
std::string webhook_name;
std::string webhook_url;
};
DiscordWebhooks discord_webhooks[MAX_DISCORD_WEBHOOK_ID]{};
bool file_logs_enabled = false;
int log_platform = 0;
std::string platform_file_name;
int log_platform = 0;
std::string platform_file_name;
// gmsay
uint16 GetGMSayColorFromCategory(uint16 log_category);
EQEmuLogSys * SetGMSayHandler(std::function<void(uint16 log_type, const std::string &)> f) {
EQEmuLogSys *SetGMSayHandler(std::function<void(uint16 log_type, const std::string &)> f)
{
on_log_gmsay_hook = f;
return this;
}
EQEmuLogSys *SetDiscordHandler(std::function<void(uint16 log_category, int webhook_id, const std::string &)> f)
{
on_log_discord_hook = f;
return this;
}
// console
void SetConsoleHandler(
std::function<void(
uint16 debug_level,
uint16 log_type,
const std::string &
)> f
@@ -324,18 +356,17 @@ public:
private:
// reference to database
Database *m_database;
std::function<void(uint16 log_category, const std::string &)> on_log_gmsay_hook;
std::function<void(uint16 debug_level, uint16 log_category, const std::string &)> on_log_console_hook;
Database *m_database;
std::function<void(uint16 log_category, const std::string &)> on_log_gmsay_hook;
std::function<void(uint16 log_category, int webhook_id, const std::string &)> on_log_discord_hook;
std::function<void(uint16 log_category, const std::string &)> on_log_console_hook;
std::string FormatOutMessageString(uint16 log_category, const std::string &in_message);
std::string GetLinuxConsoleColorFromCategory(uint16 log_category);
uint16 GetWindowsConsoleColorFromCategory(uint16 log_category);
void ProcessConsoleMessage(uint16 debug_level, uint16 log_category, const std::string &message);
void ProcessGMSay(uint16 debug_level, uint16 log_category, const std::string &message);
void ProcessLogWrite(uint16 debug_level, uint16 log_category, const std::string &message);
void ProcessConsoleMessage(uint16 log_category, const std::string &message);
void ProcessLogWrite(uint16 log_category, const std::string &message);
bool IsRfc5424LogCategory(uint16 log_category);
};
+66
View File
@@ -706,6 +706,36 @@
OutF(LogSys, Logs::Detail, Logs::ChecksumVerification, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogCombatRecord(message, ...) do {\
if (LogSys.log_settings[Logs::CombatRecord].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::CombatRecord, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogCombatRecordDetail(message, ...) do {\
if (LogSys.log_settings[Logs::CombatRecord].is_category_enabled == 1)\
OutF(LogSys, Logs::Detail, Logs::CombatRecord, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogHate(message, ...) do {\
if (LogSys.log_settings[Logs::Hate].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Hate, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogHateDetail(message, ...) do {\
if (LogSys.log_settings[Logs::Hate].is_category_enabled == 1)\
OutF(LogSys, Logs::Detail, Logs::Hate, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogDiscord(message, ...) do {\
if (LogSys.log_settings[Logs::Discord].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Discord, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogDiscordDetail(message, ...) do {\
if (LogSys.log_settings[Logs::Discord].is_category_enabled == 1)\
OutF(LogSys, Logs::Detail, Logs::Discord, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define Log(debug_level, log_category, message, ...) do {\
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
@@ -1108,12 +1138,48 @@
#define LogSaylinkDetail(message, ...) do {\
} while (0)
#define LogScheduler(message, ...) do {\
} while (0)
#define LogSchedulerDetail(message, ...) do {\
} while (0)
#define LogCheat(message, ...) do {\
} while (0)
#define LogCheatDetail(message, ...) do {\
} while (0)
#define LogLoot(message, ...) do {\
} while (0)
#define LogLootDetail(message, ...) do {\
} while (0)
#define LogFood(message, ...) do {\
} while (0)
#define LogFoodDetail(message, ...) do {\
} while (0)
#define LogChecksumVerification(message, ...) do {\
} while (0)
#define LogChecksumVerificationDetail(message, ...) do {\
} while (0)
#define LogCombatRecord(message, ...) do {\
} while (0)
#define LogCombatRecordDetail(message, ...) do {\
} while (0)
#define LogHate(message, ...) do {\
} while (0)
#define LogHateDetail(message, ...) do {\
} while (0)
#define Log(debug_level, log_category, message, ...) do {\
} while (0)
+9 -9
View File
@@ -370,7 +370,7 @@ namespace EQ
uint32 Slots; // Bitfield for which slots this item can be used in
uint32 Price; // Item cost (?)
uint32 Icon; // Icon Number
uint32 LoreGroup; // Later items use LoreGroup instead of LoreFlag. we might want to see about changing this to int32 since it is commonly -1 and is constantly being cast from signed (-1) to unsigned (4294967295)
int32 LoreGroup; // Later items use LoreGroup instead of LoreFlag. we might want to see about changing this to int32 since it is commonly -1 and is constantly being cast from signed (-1) to unsigned (4294967295)
bool LoreFlag; // This will be true if LoreGroup is non-zero
bool PendingLoreFlag;
bool ArtifactFlag;
@@ -473,14 +473,14 @@ namespace EQ
uint32 LDoNSold;
uint32 BaneDmgRaceAmt;
uint32 AugRestrict;
uint32 Endur;
uint32 DotShielding;
uint32 Attack;
uint32 Regen;
uint32 ManaRegen;
uint32 EnduranceRegen;
uint32 Haste;
uint32 DamageShield;
int32 Endur;
int32 DotShielding;
int32 Attack;
int32 Regen;
int32 ManaRegen;
int32 EnduranceRegen;
int32 Haste;
int32 DamageShield;
uint32 RecastDelay;
int RecastType;
uint32 AugDistiller;
+21 -21
View File
@@ -214,7 +214,7 @@ EQ::ItemInstance::~ItemInstance()
bool EQ::ItemInstance::IsType(item::ItemClass item_class) const
{
// IsType(<ItemClassTypes>) does not protect against 'm_item = nullptr'
// Check usage type
if ((m_use_type == ItemInstWorldContainer) && (item_class == item::ItemClassBag))
return true;
@@ -245,7 +245,7 @@ bool EQ::ItemInstance::IsStackable() const
{
if (!m_item)
return false;
return m_item->Stackable;
}
@@ -253,7 +253,7 @@ bool EQ::ItemInstance::IsCharged() const
{
if (!m_item)
return false;
if (m_item->MaxCharges > 1)
return true;
else
@@ -381,7 +381,7 @@ EQ::ItemInstance* EQ::ItemInstance::PopItem(uint8 index)
m_contents.erase(index);
return inst; // Return pointer that needs to be deleted (or otherwise managed)
}
return nullptr;
}
@@ -476,7 +476,7 @@ uint8 EQ::ItemInstance::GetTotalItemCount() const
{
if (!m_item)
return 0;
uint8 item_count = 1;
if (m_item && !m_item->IsClassBag()) { return item_count; }
@@ -526,7 +526,7 @@ EQ::ItemInstance* EQ::ItemInstance::GetOrnamentationAug(int32 ornamentationAugty
{
continue;
}
return this->GetAugment(i);
return GetAugment(i);
}
}
@@ -549,7 +549,7 @@ uint32 EQ::ItemInstance::GetOrnamentHeroModel(int32 material_slot) const {
bool EQ::ItemInstance::UpdateOrnamentationInfo() {
if (!m_item || !m_item->IsClassCommon())
return false;
bool ornamentSet = false;
int32 ornamentationAugtype = RuleI(Character, OrnamentationAugmentType);
@@ -642,7 +642,7 @@ void EQ::ItemInstance::PutAugment(uint8 slot, const ItemInstance& augment)
{
if (!m_item || !m_item->IsClassCommon())
return;
PutItem(slot, augment);
}
@@ -655,7 +655,7 @@ void EQ::ItemInstance::PutAugment(SharedDatabase *db, uint8 slot, uint32 item_id
if (aug) {
PutAugment(slot, *aug);
safe_delete(aug);
}
}
}
// Remove augment from item and destroy it
@@ -663,7 +663,7 @@ void EQ::ItemInstance::DeleteAugment(uint8 index)
{
if (!m_item || !m_item->IsClassCommon())
return;
DeleteItem(index);
}
@@ -672,7 +672,7 @@ EQ::ItemInstance* EQ::ItemInstance::RemoveAugment(uint8 index)
{
if (!m_item || !m_item->IsClassCommon())
return nullptr;
return PopItem(index);
}
@@ -680,7 +680,7 @@ bool EQ::ItemInstance::IsAugmented()
{
if (!m_item || !m_item->IsClassCommon())
return false;
for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) {
if (GetAugmentItemID(index))
return true;
@@ -698,7 +698,7 @@ bool EQ::ItemInstance::ContainsAugmentByID(uint32 item_id)
if (!item_id) {
return false;
}
for (uint8 augment_slot = invaug::SOCKET_BEGIN; augment_slot <= invaug::SOCKET_END; ++augment_slot) {
if (GetAugmentItemID(augment_slot) == item_id) {
return true;
@@ -718,7 +718,7 @@ int EQ::ItemInstance::CountAugmentByID(uint32 item_id)
if (!item_id) {
return quantity;
}
for (uint8 augment_slot = invaug::SOCKET_BEGIN; augment_slot <= invaug::SOCKET_END; ++augment_slot) {
if (GetAugmentItemID(augment_slot) == item_id) {
quantity++;
@@ -873,7 +873,7 @@ bool EQ::ItemInstance::IsDroppable(bool recurse) const
return false;
}
}
return true;
}
@@ -1097,7 +1097,7 @@ int EQ::ItemInstance::GetItemElementalFlag(bool augments) const
int EQ::ItemInstance::GetItemElementalDamage(bool augments) const
{
int damage = 0;
int64 damage = 0;
const auto item = GetItem();
if (item) {
damage = item->ElemDmgAmt;
@@ -1162,7 +1162,7 @@ int EQ::ItemInstance::GetItemRequiredLevel(bool augments) const
int EQ::ItemInstance::GetItemWeaponDamage(bool augments) const
{
int damage = 0;
int64 damage = 0;
const auto item = GetItem();
if (item) {
damage = item->Damage;
@@ -1178,7 +1178,7 @@ int EQ::ItemInstance::GetItemWeaponDamage(bool augments) const
int EQ::ItemInstance::GetItemBackstabDamage(bool augments) const
{
int damage = 0;
int64 damage = 0;
const auto item = GetItem();
if (item) {
damage = item->BackstabDmg;
@@ -1236,7 +1236,7 @@ int EQ::ItemInstance::GetItemBaneDamageRace(bool augments) const
int EQ::ItemInstance::GetItemBaneDamageBody(bodyType against, bool augments) const
{
int damage = 0;
int64 damage = 0;
const auto item = GetItem();
if (item) {
if (item->BaneDmgBody == against)
@@ -1253,7 +1253,7 @@ int EQ::ItemInstance::GetItemBaneDamageBody(bodyType against, bool augments) con
int EQ::ItemInstance::GetItemBaneDamageRace(uint16 against, bool augments) const
{
int damage = 0;
int64 damage = 0;
const auto item = GetItem();
if (item) {
if (item->BaneDmgRace == against)
@@ -1745,4 +1745,4 @@ EvolveInfo::EvolveInfo(uint32 first, uint8 max, bool allkills, uint32 L2, uint32
EvolveInfo::~EvolveInfo() {
}
}
+2 -2
View File
@@ -2649,11 +2649,11 @@ struct FaceChange_Struct {
/*004*/ uint8 hairstyle;
/*005*/ uint8 beard;
/*006*/ uint8 face;
/*007*/ uint8 unknown007;
/*007*/ uint8 unused_padding;
/*008*/ uint32 drakkin_heritage;
/*012*/ uint32 drakkin_tattoo;
/*016*/ uint32 drakkin_details;
/*020*/ uint32 unknown020;
/*020*/ uint32 entity_id;
/*024*/
};
//there are only 10 faces for barbs changing woad just
+2 -2
View File
@@ -2622,11 +2622,11 @@ struct FaceChange_Struct {
/*004*/ uint8 hairstyle;
/*005*/ uint8 beard;
/*006*/ uint8 face;
/*007*/ uint8 unknown007;
/*007*/ uint8 unused_padding;
/*008*/ uint32 drakkin_heritage;
/*012*/ uint32 drakkin_tattoo;
/*016*/ uint32 drakkin_details;
/*020*/ uint32 unknown020;
/*020*/ uint32 entity_id;
/*024*/
};
//there are only 10 faces for barbs changing woad just
+2 -2
View File
@@ -2136,11 +2136,11 @@ struct FaceChange_Struct {
/*004*/ uint8 hairstyle;
/*005*/ uint8 beard;
/*006*/ uint8 face;
/*007*/ uint8 unknown007;
/*007*/ uint8 unused_padding;
/*008*/ uint32 drakkin_heritage;
/*012*/ uint32 drakkin_tattoo;
/*016*/ uint32 drakkin_details;
/*020*/ uint32 unknown020;
/*020*/ uint32 entity_id;
/*024*/
};
//there are only 10 faces for barbs changing woad just
+2 -2
View File
@@ -2106,11 +2106,11 @@ struct FaceChange_Struct {
/*004*/ uint8 hairstyle;
/*005*/ uint8 beard;
/*006*/ uint8 face;
/*007*/ uint8 unknown007;
/*007*/ uint8 unused_padding;
/*008*/ uint32 drakkin_heritage;
/*012*/ uint32 drakkin_tattoo;
/*016*/ uint32 drakkin_details;
/*020*/ uint32 unknown020;
/*020*/ uint32 entity_id;
/*024*/
};
//there are only 10 faces for barbs changing woad just
+24
View File
@@ -1464,6 +1464,30 @@ namespace Titanium
FINISH_ENCODE();
}
ENCODE(OP_SetFace)
{
auto emu = reinterpret_cast<FaceChange_Struct*>((*p)->pBuffer);
EQApplicationPacket outapp(OP_Illusion, sizeof(structs::Illusion_Struct));
auto buf = reinterpret_cast<structs::Illusion_Struct*>(outapp.pBuffer);
buf->spawnid = emu->entity_id;
buf->race = -1; // unchanged
buf->gender = -1; // unchanged
buf->texture = -1; // unchanged
buf->helmtexture = -1; // unchanged
buf->face = emu->face;
buf->hairstyle = emu->hairstyle;
buf->haircolor = emu->haircolor;
buf->beard = emu->beard;
buf->beardcolor = emu->beardcolor;
buf->size = 0.0f; // unchanged
safe_delete(*p); // not using the original packet
dest->QueuePacket(&outapp, ack_req);
}
ENCODE(OP_ShopPlayerSell)
{
ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct);
+1
View File
@@ -66,6 +66,7 @@ E(OP_ReadBook)
E(OP_RespondAA)
E(OP_SendCharInfo)
E(OP_SendAATable)
E(OP_SetFace)
E(OP_ShopPlayerSell)
E(OP_SpecialMesg)
E(OP_TaskDescription)
+1 -2
View File
@@ -1761,8 +1761,7 @@ struct AdventureRequestResponse_Struct{
struct Illusion_Struct {
/*000*/ uint32 spawnid;
/*004*/ char charname[64];
/*068*/ uint16 race;
/*070*/ char unknown070[2];
/*068*/ int race;
/*072*/ uint8 gender;
/*073*/ uint8 texture;
/*074*/ uint8 helmtexture;
+2 -2
View File
@@ -2185,11 +2185,11 @@ struct FaceChange_Struct {
/*004*/ uint8 hairstyle;
/*005*/ uint8 beard;
/*006*/ uint8 face;
/*007*/ uint8 unknown007;
/*007*/ uint8 unused_padding;
/*008*/ uint32 drakkin_heritage;
/*012*/ uint32 drakkin_tattoo;
/*016*/ uint32 drakkin_details;
/*020*/ uint32 unknown020;
/*020*/ uint32 entity_id;
/*024*/
};
//there are only 10 faces for barbs changing woad just
+8 -4
View File
@@ -58,7 +58,8 @@ XS(XS_EQDB_field_count)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->field_count();
XSprePUSH; PUSHu((UV)RETVAL);
XSprePUSH;
PUSHu((UV) RETVAL);
}
XSRETURN(1);
}
@@ -84,7 +85,8 @@ XS(XS_EQDB_affected_rows)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->affected_rows();
XSprePUSH; PUSHu((UV)RETVAL);
XSprePUSH;
PUSHu((UV) RETVAL);
}
XSRETURN(1);
}
@@ -110,7 +112,8 @@ XS(XS_EQDB_insert_id)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->insert_id();
XSprePUSH; PUSHu((UV)RETVAL);
XSprePUSH;
PUSHu((UV) RETVAL);
}
XSRETURN(1);
}
@@ -136,7 +139,8 @@ XS(XS_EQDB_get_errno)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->get_errno();
XSprePUSH; PUSHu((UV)RETVAL);
XSprePUSH;
PUSHu((UV) RETVAL);
}
XSRETURN(1);
}
+4 -2
View File
@@ -54,7 +54,8 @@ XS(XS_EQDBRes_num_rows)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->num_rows();
XSprePUSH; PUSHu((UV)RETVAL);
XSprePUSH;
PUSHu((UV) RETVAL);
}
XSRETURN(1);
}
@@ -80,7 +81,8 @@ XS(XS_EQDBRes_num_fields)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->num_fields();
XSprePUSH; PUSHu((UV)RETVAL);
XSprePUSH;
PUSHu((UV) RETVAL);
}
XSRETURN(1);
}
+84 -64
View File
@@ -19,6 +19,7 @@
#include "profanity_manager.h"
#include "dbcore.h"
#include "string_util.h"
#include <ctype.h>
#include <cstring>
@@ -34,15 +35,17 @@ bool EQ::ProfanityManager::LoadProfanityList(DBcore *db) {
return true;
}
if (!load_database_entries(db))
if (!load_database_entries(db)) {
return false;
}
return true;
}
bool EQ::ProfanityManager::UpdateProfanityList(DBcore *db) {
if (!load_database_entries(db))
if (!load_database_entries(db)) {
return false;
}
update_originator_flag = true;
@@ -58,53 +61,60 @@ bool EQ::ProfanityManager::DeleteProfanityList(DBcore *db) {
return true;
}
bool EQ::ProfanityManager::AddProfanity(DBcore *db, const char *profanity) {
if (!db || !profanity)
bool EQ::ProfanityManager::AddProfanity(DBcore *db, std::string profanity) {
if (!db || profanity.empty()) {
return false;
}
std::string entry(profanity);
std::string entry = str_tolower(profanity);
std::transform(entry.begin(), entry.end(), entry.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
if (check_for_existing_entry(entry.c_str()))
if (check_for_existing_entry(entry)) {
return true;
}
if (entry.length() < REDACTION_LENGTH_MIN)
if (entry.length() < REDACTION_LENGTH_MIN) {
return false;
}
profanity_list.push_back(entry);
std::string query = "REPLACE INTO `profanity_list` (`word`) VALUES ('";
query.append(entry);
query.append("')");
auto query = fmt::format(
"REPLACE INTO `profanity_list` (`word`) VALUES ('{}')",
profanity
);
auto results = db->QueryDatabase(query);
if (!results.Success())
if (!results.Success()) {
return false;
}
update_originator_flag = true;
return true;
}
bool EQ::ProfanityManager::RemoveProfanity(DBcore *db, const char *profanity) {
if (!db || !profanity)
bool EQ::ProfanityManager::RemoveProfanity(DBcore *db, std::string profanity) {
if (!db || profanity.empty()) {
return false;
}
std::string entry(profanity);
std::string entry = str_tolower(profanity);
std::transform(entry.begin(), entry.end(), entry.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
if (!check_for_existing_entry(entry.c_str()))
if (!check_for_existing_entry(entry)) {
return true;
}
profanity_list.remove(entry);
std::string query = "DELETE FROM `profanity_list` WHERE `word` LIKE '";
query.append(entry);
query.append("'");
auto query = fmt::format(
"DELETE FROM `profanity_list` WHERE `word` = '{}'",
entry
);
auto results = db->QueryDatabase(query);
if (!results.Success())
if (!results.Success()) {
return false;
}
update_originator_flag = true;
@@ -112,16 +122,16 @@ bool EQ::ProfanityManager::RemoveProfanity(DBcore *db, const char *profanity) {
}
void EQ::ProfanityManager::RedactMessage(char *message) {
if (!message)
if (!message) {
return;
}
std::string test_message(message);
std::string test_message = str_tolower(message);
// hard-coded max length based on channel message buffer size (4096 bytes)..
// ..will need to change or remove if other sources are used for redaction
if (test_message.length() < REDACTION_LENGTH_MIN || test_message.length() >= 4096)
if (test_message.length() < REDACTION_LENGTH_MIN || test_message.length() >= 4096) {
return;
std::transform(test_message.begin(), test_message.end(), test_message.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
}
for (const auto &iter : profanity_list) { // consider adding textlink checks if it becomes an issue
size_t pos = 0;
@@ -129,12 +139,17 @@ void EQ::ProfanityManager::RedactMessage(char *message) {
while (pos != std::string::npos) {
pos = test_message.find(iter, start_pos);
if (pos == std::string::npos)
if (pos == std::string::npos) {
continue;
}
if ((pos + iter.length()) == test_message.length() || !isalpha(test_message.at(pos + iter.length()))) {
if (pos == 0 || !isalpha(test_message.at(pos - 1)))
if (
(pos + iter.length()) == test_message.length() ||
!isalpha(test_message.at(pos + iter.length()))
) {
if (pos == 0 || !isalpha(test_message.at(pos - 1))) {
memset((message + pos), REDACTION_CHARACTER, iter.length());
}
}
start_pos = (pos + iter.length());
@@ -143,25 +158,29 @@ void EQ::ProfanityManager::RedactMessage(char *message) {
}
void EQ::ProfanityManager::RedactMessage(std::string &message) {
if (message.length() < REDACTION_LENGTH_MIN || message.length() >= 4096)
if (message.length() < REDACTION_LENGTH_MIN || message.length() >= 4096) {
return;
}
std::string test_message(const_cast<const std::string&>(message));
std::string test_message = str_tolower(message);
std::transform(test_message.begin(), test_message.end(), test_message.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
for (const auto &iter : profanity_list) { // consider adding textlink checks if it becomes an issue
for (const auto &iter : profanity_list) {
size_t pos = 0;
size_t start_pos = 0;
while (pos != std::string::npos) {
pos = test_message.find(iter, start_pos);
if (pos == std::string::npos)
if (pos == std::string::npos) {
continue;
}
if ((pos + iter.length()) == test_message.length() || !isalpha(test_message.at(pos + iter.length()))) {
if (pos == 0 || !isalpha(test_message.at(pos - 1)))
if (
(pos + iter.length()) == test_message.length() ||
!isalpha(test_message.at(pos + iter.length()))
) {
if (pos == 0 || !isalpha(test_message.at(pos - 1))) {
message.replace(pos, iter.length(), iter.length(), REDACTION_CHARACTER);
}
}
start_pos = (pos + iter.length());
@@ -169,24 +188,18 @@ void EQ::ProfanityManager::RedactMessage(std::string &message) {
}
}
bool EQ::ProfanityManager::ContainsCensoredLanguage(const char *message) {
if (!message)
return false;
return ContainsCensoredLanguage(std::string(message));
}
bool EQ::ProfanityManager::ContainsCensoredLanguage(const std::string &message) {
if (message.length() < REDACTION_LENGTH_MIN || message.length() >= 4096)
if (message.length() < REDACTION_LENGTH_MIN || message.length() >= 4096) {
return false;
}
std::string test_message(message);
std::transform(test_message.begin(), test_message.end(), test_message.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
std::string test_message = str_tolower(message);
for (const auto &iter : profanity_list) {
if (test_message.find(iter) != std::string::npos)
if (test_message.find(iter) != std::string::npos) {
return true;
}
}
return false;
@@ -197,26 +210,28 @@ const std::list<std::string> &EQ::ProfanityManager::GetProfanityList() {
}
bool EQ::ProfanityManager::IsCensorshipActive() {
return (profanity_list.size() != 0);
return profanity_list.size() != 0;
}
bool EQ::ProfanityManager::load_database_entries(DBcore *db) {
if (!db)
if (!db) {
return false;
}
profanity_list.clear();
std::string query = "SELECT `word` FROM `profanity_list`";
auto results = db->QueryDatabase(query);
if (!results.Success())
if (!results.Success()) {
return false;
}
for (auto row = results.begin(); row != results.end(); ++row) {
if (std::strlen(row[0]) >= REDACTION_LENGTH_MIN) {
std::string entry(row[0]);
std::transform(entry.begin(), entry.end(), entry.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
if (!check_for_existing_entry(entry.c_str()))
profanity_list.push_back((std::string)entry);
for (auto row : results) {
std::string entry = str_tolower(row[0]);
if (entry.length() >= REDACTION_LENGTH_MIN) {
if (!check_for_existing_entry(entry)) {
profanity_list.push_back(entry);
}
}
}
@@ -224,26 +239,31 @@ bool EQ::ProfanityManager::load_database_entries(DBcore *db) {
}
bool EQ::ProfanityManager::clear_database_entries(DBcore *db) {
if (!db)
if (!db) {
return false;
}
profanity_list.clear();
std::string query = "DELETE FROM `profanity_list`";
auto results = db->QueryDatabase(query);
if (!results.Success())
if (!results.Success()) {
return false;
}
return true;
}
bool EQ::ProfanityManager::check_for_existing_entry(const char *profanity) {
if (!profanity)
bool EQ::ProfanityManager::check_for_existing_entry(std::string profanity) {
if (profanity.empty()) {
return false;
}
for (const auto &iter : profanity_list) {
if (iter.compare(profanity) == 0)
if (!iter.compare(profanity)) {
return true;
}
}
return false;
+4 -4
View File
@@ -22,6 +22,7 @@
#include <string>
#include <list>
#include <fmt/format.h>
class DBcore;
@@ -34,13 +35,12 @@ namespace EQ
static bool UpdateProfanityList(DBcore *db);
static bool DeleteProfanityList(DBcore *db);
static bool AddProfanity(DBcore *db, const char *profanity);
static bool RemoveProfanity(DBcore *db, const char *profanity);
static bool AddProfanity(DBcore *db, std::string profanity);
static bool RemoveProfanity(DBcore *db, std::string profanity);
static void RedactMessage(char *message);
static void RedactMessage(std::string &message);
static bool ContainsCensoredLanguage(const char *message);
static bool ContainsCensoredLanguage(const std::string &message);
static const std::list<std::string> &GetProfanityList();
@@ -53,7 +53,7 @@ namespace EQ
private:
static bool load_database_entries(DBcore *db);
static bool clear_database_entries(DBcore *db);
static bool check_for_existing_entry(const char *profanity);
static bool check_for_existing_entry(std::string profanity);
};
@@ -0,0 +1,336 @@
/**
* 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_DISCORD_WEBHOOKS_REPOSITORY_H
#define EQEMU_BASE_DISCORD_WEBHOOKS_REPOSITORY_H
#include "../../database.h"
#include "../../string_util.h"
#include <ctime>
class BaseDiscordWebhooksRepository {
public:
struct DiscordWebhooks {
int id;
std::string webhook_name;
std::string webhook_url;
time_t created_at;
time_t deleted_at;
};
static std::string PrimaryKey()
{
return std::string("id");
}
static std::vector<std::string> Columns()
{
return {
"id",
"webhook_name",
"webhook_url",
"created_at",
"deleted_at",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"id",
"webhook_name",
"webhook_url",
"UNIX_TIMESTAMP(created_at)",
"UNIX_TIMESTAMP(deleted_at)",
};
}
static std::string ColumnsRaw()
{
return std::string(implode(", ", Columns()));
}
static std::string SelectColumnsRaw()
{
return std::string(implode(", ", SelectColumns()));
}
static std::string TableName()
{
return std::string("discord_webhooks");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static DiscordWebhooks NewEntity()
{
DiscordWebhooks entry{};
entry.id = 0;
entry.webhook_name = "";
entry.webhook_url = "";
entry.created_at = 0;
entry.deleted_at = 0;
return entry;
}
static DiscordWebhooks GetDiscordWebhooksEntry(
const std::vector<DiscordWebhooks> &discord_webhookss,
int discord_webhooks_id
)
{
for (auto &discord_webhooks : discord_webhookss) {
if (discord_webhooks.id == discord_webhooks_id) {
return discord_webhooks;
}
}
return NewEntity();
}
static DiscordWebhooks FindOne(
Database& db,
int discord_webhooks_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
BaseSelect(),
discord_webhooks_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
DiscordWebhooks entry{};
entry.id = atoi(row[0]);
entry.webhook_name = row[1] ? row[1] : "";
entry.webhook_url = row[2] ? row[2] : "";
entry.created_at = strtoll(row[3] ? row[3] : "-1", nullptr, 10);
entry.deleted_at = strtoll(row[4] ? row[4] : "-1", nullptr, 10);
return entry;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int discord_webhooks_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
discord_webhooks_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
DiscordWebhooks discord_webhooks_entry
)
{
std::vector<std::string> update_values;
auto columns = Columns();
update_values.push_back(columns[1] + " = '" + EscapeString(discord_webhooks_entry.webhook_name) + "'");
update_values.push_back(columns[2] + " = '" + EscapeString(discord_webhooks_entry.webhook_url) + "'");
update_values.push_back(columns[3] + " = FROM_UNIXTIME(" + (discord_webhooks_entry.created_at > 0 ? std::to_string(discord_webhooks_entry.created_at) : "null") + ")");
update_values.push_back(columns[4] + " = FROM_UNIXTIME(" + (discord_webhooks_entry.deleted_at > 0 ? std::to_string(discord_webhooks_entry.deleted_at) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
implode(", ", update_values),
PrimaryKey(),
discord_webhooks_entry.id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static DiscordWebhooks InsertOne(
Database& db,
DiscordWebhooks discord_webhooks_entry
)
{
std::vector<std::string> insert_values;
insert_values.push_back(std::to_string(discord_webhooks_entry.id));
insert_values.push_back("'" + EscapeString(discord_webhooks_entry.webhook_name) + "'");
insert_values.push_back("'" + EscapeString(discord_webhooks_entry.webhook_url) + "'");
insert_values.push_back("FROM_UNIXTIME(" + (discord_webhooks_entry.created_at > 0 ? std::to_string(discord_webhooks_entry.created_at) : "null") + ")");
insert_values.push_back("FROM_UNIXTIME(" + (discord_webhooks_entry.deleted_at > 0 ? std::to_string(discord_webhooks_entry.deleted_at) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
implode(",", insert_values)
)
);
if (results.Success()) {
discord_webhooks_entry.id = results.LastInsertedID();
return discord_webhooks_entry;
}
discord_webhooks_entry = NewEntity();
return discord_webhooks_entry;
}
static int InsertMany(
Database& db,
std::vector<DiscordWebhooks> discord_webhooks_entries
)
{
std::vector<std::string> insert_chunks;
for (auto &discord_webhooks_entry: discord_webhooks_entries) {
std::vector<std::string> insert_values;
insert_values.push_back(std::to_string(discord_webhooks_entry.id));
insert_values.push_back("'" + EscapeString(discord_webhooks_entry.webhook_name) + "'");
insert_values.push_back("'" + EscapeString(discord_webhooks_entry.webhook_url) + "'");
insert_values.push_back("FROM_UNIXTIME(" + (discord_webhooks_entry.created_at > 0 ? std::to_string(discord_webhooks_entry.created_at) : "null") + ")");
insert_values.push_back("FROM_UNIXTIME(" + (discord_webhooks_entry.deleted_at > 0 ? std::to_string(discord_webhooks_entry.deleted_at) : "null") + ")");
insert_chunks.push_back("(" + implode(",", insert_values) + ")");
}
std::vector<std::string> insert_values;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<DiscordWebhooks> All(Database& db)
{
std::vector<DiscordWebhooks> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
DiscordWebhooks entry{};
entry.id = atoi(row[0]);
entry.webhook_name = row[1] ? row[1] : "";
entry.webhook_url = row[2] ? row[2] : "";
entry.created_at = strtoll(row[3] ? row[3] : "-1", nullptr, 10);
entry.deleted_at = strtoll(row[4] ? row[4] : "-1", nullptr, 10);
all_entries.push_back(entry);
}
return all_entries;
}
static std::vector<DiscordWebhooks> GetWhere(Database& db, std::string where_filter)
{
std::vector<DiscordWebhooks> 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) {
DiscordWebhooks entry{};
entry.id = atoi(row[0]);
entry.webhook_name = row[1] ? row[1] : "";
entry.webhook_url = row[2] ? row[2] : "";
entry.created_at = strtoll(row[3] ? row[3] : "-1", nullptr, 10);
entry.deleted_at = strtoll(row[4] ? row[4] : "-1", nullptr, 10);
all_entries.push_back(entry);
}
return all_entries;
}
static int DeleteWhere(Database& db, 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);
}
};
#endif //EQEMU_BASE_DISCORD_WEBHOOKS_REPOSITORY_H
@@ -24,6 +24,8 @@ public:
int log_to_console;
int log_to_file;
int log_to_gmsay;
int log_to_discord;
int discord_webhook_id;
};
static std::string PrimaryKey()
@@ -39,6 +41,8 @@ public:
"log_to_console",
"log_to_file",
"log_to_gmsay",
"log_to_discord",
"discord_webhook_id",
};
}
@@ -50,6 +54,8 @@ public:
"log_to_console",
"log_to_file",
"log_to_gmsay",
"log_to_discord",
"discord_webhook_id",
};
}
@@ -95,6 +101,8 @@ public:
entry.log_to_console = 0;
entry.log_to_file = 0;
entry.log_to_gmsay = 0;
entry.log_to_discord = 0;
entry.discord_webhook_id = 0;
return entry;
}
@@ -135,6 +143,8 @@ public:
entry.log_to_console = atoi(row[2]);
entry.log_to_file = atoi(row[3]);
entry.log_to_gmsay = atoi(row[4]);
entry.log_to_discord = atoi(row[5]);
entry.discord_webhook_id = atoi(row[6]);
return entry;
}
@@ -173,6 +183,8 @@ public:
update_values.push_back(columns[2] + " = " + std::to_string(logsys_categories_entry.log_to_console));
update_values.push_back(columns[3] + " = " + std::to_string(logsys_categories_entry.log_to_file));
update_values.push_back(columns[4] + " = " + std::to_string(logsys_categories_entry.log_to_gmsay));
update_values.push_back(columns[5] + " = " + std::to_string(logsys_categories_entry.log_to_discord));
update_values.push_back(columns[6] + " = " + std::to_string(logsys_categories_entry.discord_webhook_id));
auto results = db.QueryDatabase(
fmt::format(
@@ -199,6 +211,8 @@ public:
insert_values.push_back(std::to_string(logsys_categories_entry.log_to_console));
insert_values.push_back(std::to_string(logsys_categories_entry.log_to_file));
insert_values.push_back(std::to_string(logsys_categories_entry.log_to_gmsay));
insert_values.push_back(std::to_string(logsys_categories_entry.log_to_discord));
insert_values.push_back(std::to_string(logsys_categories_entry.discord_webhook_id));
auto results = db.QueryDatabase(
fmt::format(
@@ -233,6 +247,8 @@ public:
insert_values.push_back(std::to_string(logsys_categories_entry.log_to_console));
insert_values.push_back(std::to_string(logsys_categories_entry.log_to_file));
insert_values.push_back(std::to_string(logsys_categories_entry.log_to_gmsay));
insert_values.push_back(std::to_string(logsys_categories_entry.log_to_discord));
insert_values.push_back(std::to_string(logsys_categories_entry.discord_webhook_id));
insert_chunks.push_back("(" + implode(",", insert_values) + ")");
}
@@ -271,6 +287,8 @@ public:
entry.log_to_console = atoi(row[2]);
entry.log_to_file = atoi(row[3]);
entry.log_to_gmsay = atoi(row[4]);
entry.log_to_discord = atoi(row[5]);
entry.discord_webhook_id = atoi(row[6]);
all_entries.push_back(entry);
}
@@ -300,6 +318,8 @@ public:
entry.log_to_console = atoi(row[2]);
entry.log_to_file = atoi(row[3]);
entry.log_to_gmsay = atoi(row[4]);
entry.log_to_discord = atoi(row[5]);
entry.discord_webhook_id = atoi(row[6]);
all_entries.push_back(entry);
}
@@ -26,15 +26,16 @@ public:
int race;
int class_;
int bodytype;
int hp;
int mana;
int64 hp;
int64 mana;
int gender;
int texture;
int helmtexture;
int herosforgemodel;
float size;
int hp_regen_rate;
int mana_regen_rate;
int64 hp_regen_rate;
int64 hp_regen_per_second;
int64 mana_regen_rate;
int loottable_id;
int merchant_id;
int alt_currency_id;
@@ -167,6 +168,7 @@ public:
"herosforgemodel",
"size",
"hp_regen_rate",
"hp_regen_per_second",
"mana_regen_rate",
"loottable_id",
"merchant_id",
@@ -296,6 +298,7 @@ public:
"herosforgemodel",
"size",
"hp_regen_rate",
"hp_regen_per_second",
"mana_regen_rate",
"loottable_id",
"merchant_id",
@@ -459,6 +462,7 @@ public:
entry.herosforgemodel = 0;
entry.size = 0;
entry.hp_regen_rate = 0;
entry.hp_regen_per_second = 0;
entry.mana_regen_rate = 0;
entry.loottable_id = 0;
entry.merchant_id = 0;
@@ -609,122 +613,123 @@ public:
entry.race = atoi(row[4]);
entry.class_ = atoi(row[5]);
entry.bodytype = atoi(row[6]);
entry.hp = atoi(row[7]);
entry.mana = atoi(row[8]);
entry.hp = strtoll(row[7], nullptr, 10);
entry.mana = strtoll(row[8], nullptr, 10);
entry.gender = atoi(row[9]);
entry.texture = atoi(row[10]);
entry.helmtexture = atoi(row[11]);
entry.herosforgemodel = atoi(row[12]);
entry.size = static_cast<float>(atof(row[13]));
entry.hp_regen_rate = atoi(row[14]);
entry.mana_regen_rate = atoi(row[15]);
entry.loottable_id = atoi(row[16]);
entry.merchant_id = atoi(row[17]);
entry.alt_currency_id = atoi(row[18]);
entry.npc_spells_id = atoi(row[19]);
entry.npc_spells_effects_id = atoi(row[20]);
entry.npc_faction_id = atoi(row[21]);
entry.adventure_template_id = atoi(row[22]);
entry.trap_template = atoi(row[23]);
entry.mindmg = atoi(row[24]);
entry.maxdmg = atoi(row[25]);
entry.attack_count = atoi(row[26]);
entry.npcspecialattks = row[27] ? row[27] : "";
entry.special_abilities = row[28] ? row[28] : "";
entry.aggroradius = atoi(row[29]);
entry.assistradius = atoi(row[30]);
entry.face = atoi(row[31]);
entry.luclin_hairstyle = atoi(row[32]);
entry.luclin_haircolor = atoi(row[33]);
entry.luclin_eyecolor = atoi(row[34]);
entry.luclin_eyecolor2 = atoi(row[35]);
entry.luclin_beardcolor = atoi(row[36]);
entry.luclin_beard = atoi(row[37]);
entry.drakkin_heritage = atoi(row[38]);
entry.drakkin_tattoo = atoi(row[39]);
entry.drakkin_details = atoi(row[40]);
entry.armortint_id = atoi(row[41]);
entry.armortint_red = atoi(row[42]);
entry.armortint_green = atoi(row[43]);
entry.armortint_blue = atoi(row[44]);
entry.d_melee_texture1 = atoi(row[45]);
entry.d_melee_texture2 = atoi(row[46]);
entry.ammo_idfile = row[47] ? row[47] : "";
entry.prim_melee_type = atoi(row[48]);
entry.sec_melee_type = atoi(row[49]);
entry.ranged_type = atoi(row[50]);
entry.runspeed = static_cast<float>(atof(row[51]));
entry.MR = atoi(row[52]);
entry.CR = atoi(row[53]);
entry.DR = atoi(row[54]);
entry.FR = atoi(row[55]);
entry.PR = atoi(row[56]);
entry.Corrup = atoi(row[57]);
entry.PhR = atoi(row[58]);
entry.see_invis = atoi(row[59]);
entry.see_invis_undead = atoi(row[60]);
entry.qglobal = atoi(row[61]);
entry.AC = atoi(row[62]);
entry.npc_aggro = atoi(row[63]);
entry.spawn_limit = atoi(row[64]);
entry.attack_speed = static_cast<float>(atof(row[65]));
entry.attack_delay = atoi(row[66]);
entry.findable = atoi(row[67]);
entry.STR = atoi(row[68]);
entry.STA = atoi(row[69]);
entry.DEX = atoi(row[70]);
entry.AGI = atoi(row[71]);
entry._INT = atoi(row[72]);
entry.WIS = atoi(row[73]);
entry.CHA = atoi(row[74]);
entry.see_hide = atoi(row[75]);
entry.see_improved_hide = atoi(row[76]);
entry.trackable = atoi(row[77]);
entry.isbot = atoi(row[78]);
entry.exclude = atoi(row[79]);
entry.ATK = atoi(row[80]);
entry.Accuracy = atoi(row[81]);
entry.Avoidance = atoi(row[82]);
entry.slow_mitigation = atoi(row[83]);
entry.version = atoi(row[84]);
entry.maxlevel = atoi(row[85]);
entry.scalerate = atoi(row[86]);
entry.private_corpse = atoi(row[87]);
entry.unique_spawn_by_name = atoi(row[88]);
entry.underwater = atoi(row[89]);
entry.isquest = atoi(row[90]);
entry.emoteid = atoi(row[91]);
entry.spellscale = static_cast<float>(atof(row[92]));
entry.healscale = static_cast<float>(atof(row[93]));
entry.no_target_hotkey = atoi(row[94]);
entry.raid_target = atoi(row[95]);
entry.armtexture = atoi(row[96]);
entry.bracertexture = atoi(row[97]);
entry.handtexture = atoi(row[98]);
entry.legtexture = atoi(row[99]);
entry.feettexture = atoi(row[100]);
entry.light = atoi(row[101]);
entry.walkspeed = atoi(row[102]);
entry.peqid = atoi(row[103]);
entry.unique_ = atoi(row[104]);
entry.fixed = atoi(row[105]);
entry.ignore_despawn = atoi(row[106]);
entry.show_name = atoi(row[107]);
entry.untargetable = atoi(row[108]);
entry.charm_ac = atoi(row[109]);
entry.charm_min_dmg = atoi(row[110]);
entry.charm_max_dmg = atoi(row[111]);
entry.charm_attack_delay = atoi(row[112]);
entry.charm_accuracy_rating = atoi(row[113]);
entry.charm_avoidance_rating = atoi(row[114]);
entry.charm_atk = atoi(row[115]);
entry.skip_global_loot = atoi(row[116]);
entry.rare_spawn = atoi(row[117]);
entry.stuck_behavior = atoi(row[118]);
entry.model = atoi(row[119]);
entry.flymode = atoi(row[120]);
entry.always_aggro = atoi(row[121]);
entry.exp_mod = atoi(row[122]);
entry.hp_regen_rate = strtoll(row[14], nullptr, 10);
entry.hp_regen_per_second = strtoll(row[15], nullptr, 10);
entry.mana_regen_rate = strtoll(row[16], nullptr, 10);
entry.loottable_id = atoi(row[17]);
entry.merchant_id = atoi(row[18]);
entry.alt_currency_id = atoi(row[19]);
entry.npc_spells_id = atoi(row[20]);
entry.npc_spells_effects_id = atoi(row[21]);
entry.npc_faction_id = atoi(row[22]);
entry.adventure_template_id = atoi(row[23]);
entry.trap_template = atoi(row[24]);
entry.mindmg = atoi(row[25]);
entry.maxdmg = atoi(row[26]);
entry.attack_count = atoi(row[27]);
entry.npcspecialattks = row[28] ? row[28] : "";
entry.special_abilities = row[29] ? row[29] : "";
entry.aggroradius = atoi(row[30]);
entry.assistradius = atoi(row[31]);
entry.face = atoi(row[32]);
entry.luclin_hairstyle = atoi(row[33]);
entry.luclin_haircolor = atoi(row[34]);
entry.luclin_eyecolor = atoi(row[35]);
entry.luclin_eyecolor2 = atoi(row[36]);
entry.luclin_beardcolor = atoi(row[37]);
entry.luclin_beard = atoi(row[38]);
entry.drakkin_heritage = atoi(row[39]);
entry.drakkin_tattoo = atoi(row[40]);
entry.drakkin_details = atoi(row[41]);
entry.armortint_id = atoi(row[42]);
entry.armortint_red = atoi(row[43]);
entry.armortint_green = atoi(row[44]);
entry.armortint_blue = atoi(row[45]);
entry.d_melee_texture1 = atoi(row[46]);
entry.d_melee_texture2 = atoi(row[47]);
entry.ammo_idfile = row[48] ? row[48] : "";
entry.prim_melee_type = atoi(row[49]);
entry.sec_melee_type = atoi(row[50]);
entry.ranged_type = atoi(row[51]);
entry.runspeed = static_cast<float>(atof(row[52]));
entry.MR = atoi(row[53]);
entry.CR = atoi(row[54]);
entry.DR = atoi(row[55]);
entry.FR = atoi(row[56]);
entry.PR = atoi(row[57]);
entry.Corrup = atoi(row[58]);
entry.PhR = atoi(row[59]);
entry.see_invis = atoi(row[60]);
entry.see_invis_undead = atoi(row[61]);
entry.qglobal = atoi(row[62]);
entry.AC = atoi(row[63]);
entry.npc_aggro = atoi(row[64]);
entry.spawn_limit = atoi(row[65]);
entry.attack_speed = static_cast<float>(atof(row[66]));
entry.attack_delay = atoi(row[67]);
entry.findable = atoi(row[68]);
entry.STR = atoi(row[69]);
entry.STA = atoi(row[70]);
entry.DEX = atoi(row[71]);
entry.AGI = atoi(row[72]);
entry._INT = atoi(row[73]);
entry.WIS = atoi(row[74]);
entry.CHA = atoi(row[75]);
entry.see_hide = atoi(row[76]);
entry.see_improved_hide = atoi(row[77]);
entry.trackable = atoi(row[78]);
entry.isbot = atoi(row[79]);
entry.exclude = atoi(row[80]);
entry.ATK = atoi(row[81]);
entry.Accuracy = atoi(row[82]);
entry.Avoidance = atoi(row[83]);
entry.slow_mitigation = atoi(row[84]);
entry.version = atoi(row[85]);
entry.maxlevel = atoi(row[86]);
entry.scalerate = atoi(row[87]);
entry.private_corpse = atoi(row[88]);
entry.unique_spawn_by_name = atoi(row[89]);
entry.underwater = atoi(row[90]);
entry.isquest = atoi(row[91]);
entry.emoteid = atoi(row[92]);
entry.spellscale = static_cast<float>(atof(row[93]));
entry.healscale = static_cast<float>(atof(row[94]));
entry.no_target_hotkey = atoi(row[95]);
entry.raid_target = atoi(row[96]);
entry.armtexture = atoi(row[97]);
entry.bracertexture = atoi(row[98]);
entry.handtexture = atoi(row[99]);
entry.legtexture = atoi(row[100]);
entry.feettexture = atoi(row[101]);
entry.light = atoi(row[102]);
entry.walkspeed = atoi(row[103]);
entry.peqid = atoi(row[104]);
entry.unique_ = atoi(row[105]);
entry.fixed = atoi(row[106]);
entry.ignore_despawn = atoi(row[107]);
entry.show_name = atoi(row[108]);
entry.untargetable = atoi(row[109]);
entry.charm_ac = atoi(row[110]);
entry.charm_min_dmg = atoi(row[111]);
entry.charm_max_dmg = atoi(row[112]);
entry.charm_attack_delay = atoi(row[113]);
entry.charm_accuracy_rating = atoi(row[114]);
entry.charm_avoidance_rating = atoi(row[115]);
entry.charm_atk = atoi(row[116]);
entry.skip_global_loot = atoi(row[117]);
entry.rare_spawn = atoi(row[118]);
entry.stuck_behavior = atoi(row[119]);
entry.model = atoi(row[120]);
entry.flymode = atoi(row[121]);
entry.always_aggro = atoi(row[122]);
entry.exp_mod = atoi(row[123]);
return entry;
}
@@ -772,114 +777,115 @@ public:
update_values.push_back(columns[12] + " = " + std::to_string(npc_types_entry.herosforgemodel));
update_values.push_back(columns[13] + " = " + std::to_string(npc_types_entry.size));
update_values.push_back(columns[14] + " = " + std::to_string(npc_types_entry.hp_regen_rate));
update_values.push_back(columns[15] + " = " + std::to_string(npc_types_entry.mana_regen_rate));
update_values.push_back(columns[16] + " = " + std::to_string(npc_types_entry.loottable_id));
update_values.push_back(columns[17] + " = " + std::to_string(npc_types_entry.merchant_id));
update_values.push_back(columns[18] + " = " + std::to_string(npc_types_entry.alt_currency_id));
update_values.push_back(columns[19] + " = " + std::to_string(npc_types_entry.npc_spells_id));
update_values.push_back(columns[20] + " = " + std::to_string(npc_types_entry.npc_spells_effects_id));
update_values.push_back(columns[21] + " = " + std::to_string(npc_types_entry.npc_faction_id));
update_values.push_back(columns[22] + " = " + std::to_string(npc_types_entry.adventure_template_id));
update_values.push_back(columns[23] + " = " + std::to_string(npc_types_entry.trap_template));
update_values.push_back(columns[24] + " = " + std::to_string(npc_types_entry.mindmg));
update_values.push_back(columns[25] + " = " + std::to_string(npc_types_entry.maxdmg));
update_values.push_back(columns[26] + " = " + std::to_string(npc_types_entry.attack_count));
update_values.push_back(columns[27] + " = '" + EscapeString(npc_types_entry.npcspecialattks) + "'");
update_values.push_back(columns[28] + " = '" + EscapeString(npc_types_entry.special_abilities) + "'");
update_values.push_back(columns[29] + " = " + std::to_string(npc_types_entry.aggroradius));
update_values.push_back(columns[30] + " = " + std::to_string(npc_types_entry.assistradius));
update_values.push_back(columns[31] + " = " + std::to_string(npc_types_entry.face));
update_values.push_back(columns[32] + " = " + std::to_string(npc_types_entry.luclin_hairstyle));
update_values.push_back(columns[33] + " = " + std::to_string(npc_types_entry.luclin_haircolor));
update_values.push_back(columns[34] + " = " + std::to_string(npc_types_entry.luclin_eyecolor));
update_values.push_back(columns[35] + " = " + std::to_string(npc_types_entry.luclin_eyecolor2));
update_values.push_back(columns[36] + " = " + std::to_string(npc_types_entry.luclin_beardcolor));
update_values.push_back(columns[37] + " = " + std::to_string(npc_types_entry.luclin_beard));
update_values.push_back(columns[38] + " = " + std::to_string(npc_types_entry.drakkin_heritage));
update_values.push_back(columns[39] + " = " + std::to_string(npc_types_entry.drakkin_tattoo));
update_values.push_back(columns[40] + " = " + std::to_string(npc_types_entry.drakkin_details));
update_values.push_back(columns[41] + " = " + std::to_string(npc_types_entry.armortint_id));
update_values.push_back(columns[42] + " = " + std::to_string(npc_types_entry.armortint_red));
update_values.push_back(columns[43] + " = " + std::to_string(npc_types_entry.armortint_green));
update_values.push_back(columns[44] + " = " + std::to_string(npc_types_entry.armortint_blue));
update_values.push_back(columns[45] + " = " + std::to_string(npc_types_entry.d_melee_texture1));
update_values.push_back(columns[46] + " = " + std::to_string(npc_types_entry.d_melee_texture2));
update_values.push_back(columns[47] + " = '" + EscapeString(npc_types_entry.ammo_idfile) + "'");
update_values.push_back(columns[48] + " = " + std::to_string(npc_types_entry.prim_melee_type));
update_values.push_back(columns[49] + " = " + std::to_string(npc_types_entry.sec_melee_type));
update_values.push_back(columns[50] + " = " + std::to_string(npc_types_entry.ranged_type));
update_values.push_back(columns[51] + " = " + std::to_string(npc_types_entry.runspeed));
update_values.push_back(columns[52] + " = " + std::to_string(npc_types_entry.MR));
update_values.push_back(columns[53] + " = " + std::to_string(npc_types_entry.CR));
update_values.push_back(columns[54] + " = " + std::to_string(npc_types_entry.DR));
update_values.push_back(columns[55] + " = " + std::to_string(npc_types_entry.FR));
update_values.push_back(columns[56] + " = " + std::to_string(npc_types_entry.PR));
update_values.push_back(columns[57] + " = " + std::to_string(npc_types_entry.Corrup));
update_values.push_back(columns[58] + " = " + std::to_string(npc_types_entry.PhR));
update_values.push_back(columns[59] + " = " + std::to_string(npc_types_entry.see_invis));
update_values.push_back(columns[60] + " = " + std::to_string(npc_types_entry.see_invis_undead));
update_values.push_back(columns[61] + " = " + std::to_string(npc_types_entry.qglobal));
update_values.push_back(columns[62] + " = " + std::to_string(npc_types_entry.AC));
update_values.push_back(columns[63] + " = " + std::to_string(npc_types_entry.npc_aggro));
update_values.push_back(columns[64] + " = " + std::to_string(npc_types_entry.spawn_limit));
update_values.push_back(columns[65] + " = " + std::to_string(npc_types_entry.attack_speed));
update_values.push_back(columns[66] + " = " + std::to_string(npc_types_entry.attack_delay));
update_values.push_back(columns[67] + " = " + std::to_string(npc_types_entry.findable));
update_values.push_back(columns[68] + " = " + std::to_string(npc_types_entry.STR));
update_values.push_back(columns[69] + " = " + std::to_string(npc_types_entry.STA));
update_values.push_back(columns[70] + " = " + std::to_string(npc_types_entry.DEX));
update_values.push_back(columns[71] + " = " + std::to_string(npc_types_entry.AGI));
update_values.push_back(columns[72] + " = " + std::to_string(npc_types_entry._INT));
update_values.push_back(columns[73] + " = " + std::to_string(npc_types_entry.WIS));
update_values.push_back(columns[74] + " = " + std::to_string(npc_types_entry.CHA));
update_values.push_back(columns[75] + " = " + std::to_string(npc_types_entry.see_hide));
update_values.push_back(columns[76] + " = " + std::to_string(npc_types_entry.see_improved_hide));
update_values.push_back(columns[77] + " = " + std::to_string(npc_types_entry.trackable));
update_values.push_back(columns[78] + " = " + std::to_string(npc_types_entry.isbot));
update_values.push_back(columns[79] + " = " + std::to_string(npc_types_entry.exclude));
update_values.push_back(columns[80] + " = " + std::to_string(npc_types_entry.ATK));
update_values.push_back(columns[81] + " = " + std::to_string(npc_types_entry.Accuracy));
update_values.push_back(columns[82] + " = " + std::to_string(npc_types_entry.Avoidance));
update_values.push_back(columns[83] + " = " + std::to_string(npc_types_entry.slow_mitigation));
update_values.push_back(columns[84] + " = " + std::to_string(npc_types_entry.version));
update_values.push_back(columns[85] + " = " + std::to_string(npc_types_entry.maxlevel));
update_values.push_back(columns[86] + " = " + std::to_string(npc_types_entry.scalerate));
update_values.push_back(columns[87] + " = " + std::to_string(npc_types_entry.private_corpse));
update_values.push_back(columns[88] + " = " + std::to_string(npc_types_entry.unique_spawn_by_name));
update_values.push_back(columns[89] + " = " + std::to_string(npc_types_entry.underwater));
update_values.push_back(columns[90] + " = " + std::to_string(npc_types_entry.isquest));
update_values.push_back(columns[91] + " = " + std::to_string(npc_types_entry.emoteid));
update_values.push_back(columns[92] + " = " + std::to_string(npc_types_entry.spellscale));
update_values.push_back(columns[93] + " = " + std::to_string(npc_types_entry.healscale));
update_values.push_back(columns[94] + " = " + std::to_string(npc_types_entry.no_target_hotkey));
update_values.push_back(columns[95] + " = " + std::to_string(npc_types_entry.raid_target));
update_values.push_back(columns[96] + " = " + std::to_string(npc_types_entry.armtexture));
update_values.push_back(columns[97] + " = " + std::to_string(npc_types_entry.bracertexture));
update_values.push_back(columns[98] + " = " + std::to_string(npc_types_entry.handtexture));
update_values.push_back(columns[99] + " = " + std::to_string(npc_types_entry.legtexture));
update_values.push_back(columns[100] + " = " + std::to_string(npc_types_entry.feettexture));
update_values.push_back(columns[101] + " = " + std::to_string(npc_types_entry.light));
update_values.push_back(columns[102] + " = " + std::to_string(npc_types_entry.walkspeed));
update_values.push_back(columns[103] + " = " + std::to_string(npc_types_entry.peqid));
update_values.push_back(columns[104] + " = " + std::to_string(npc_types_entry.unique_));
update_values.push_back(columns[105] + " = " + std::to_string(npc_types_entry.fixed));
update_values.push_back(columns[106] + " = " + std::to_string(npc_types_entry.ignore_despawn));
update_values.push_back(columns[107] + " = " + std::to_string(npc_types_entry.show_name));
update_values.push_back(columns[108] + " = " + std::to_string(npc_types_entry.untargetable));
update_values.push_back(columns[109] + " = " + std::to_string(npc_types_entry.charm_ac));
update_values.push_back(columns[110] + " = " + std::to_string(npc_types_entry.charm_min_dmg));
update_values.push_back(columns[111] + " = " + std::to_string(npc_types_entry.charm_max_dmg));
update_values.push_back(columns[112] + " = " + std::to_string(npc_types_entry.charm_attack_delay));
update_values.push_back(columns[113] + " = " + std::to_string(npc_types_entry.charm_accuracy_rating));
update_values.push_back(columns[114] + " = " + std::to_string(npc_types_entry.charm_avoidance_rating));
update_values.push_back(columns[115] + " = " + std::to_string(npc_types_entry.charm_atk));
update_values.push_back(columns[116] + " = " + std::to_string(npc_types_entry.skip_global_loot));
update_values.push_back(columns[117] + " = " + std::to_string(npc_types_entry.rare_spawn));
update_values.push_back(columns[118] + " = " + std::to_string(npc_types_entry.stuck_behavior));
update_values.push_back(columns[119] + " = " + std::to_string(npc_types_entry.model));
update_values.push_back(columns[120] + " = " + std::to_string(npc_types_entry.flymode));
update_values.push_back(columns[121] + " = " + std::to_string(npc_types_entry.always_aggro));
update_values.push_back(columns[122] + " = " + std::to_string(npc_types_entry.exp_mod));
update_values.push_back(columns[15] + " = " + std::to_string(npc_types_entry.hp_regen_per_second));
update_values.push_back(columns[16] + " = " + std::to_string(npc_types_entry.mana_regen_rate));
update_values.push_back(columns[17] + " = " + std::to_string(npc_types_entry.loottable_id));
update_values.push_back(columns[18] + " = " + std::to_string(npc_types_entry.merchant_id));
update_values.push_back(columns[19] + " = " + std::to_string(npc_types_entry.alt_currency_id));
update_values.push_back(columns[20] + " = " + std::to_string(npc_types_entry.npc_spells_id));
update_values.push_back(columns[21] + " = " + std::to_string(npc_types_entry.npc_spells_effects_id));
update_values.push_back(columns[22] + " = " + std::to_string(npc_types_entry.npc_faction_id));
update_values.push_back(columns[23] + " = " + std::to_string(npc_types_entry.adventure_template_id));
update_values.push_back(columns[24] + " = " + std::to_string(npc_types_entry.trap_template));
update_values.push_back(columns[25] + " = " + std::to_string(npc_types_entry.mindmg));
update_values.push_back(columns[26] + " = " + std::to_string(npc_types_entry.maxdmg));
update_values.push_back(columns[27] + " = " + std::to_string(npc_types_entry.attack_count));
update_values.push_back(columns[28] + " = '" + EscapeString(npc_types_entry.npcspecialattks) + "'");
update_values.push_back(columns[29] + " = '" + EscapeString(npc_types_entry.special_abilities) + "'");
update_values.push_back(columns[30] + " = " + std::to_string(npc_types_entry.aggroradius));
update_values.push_back(columns[31] + " = " + std::to_string(npc_types_entry.assistradius));
update_values.push_back(columns[32] + " = " + std::to_string(npc_types_entry.face));
update_values.push_back(columns[33] + " = " + std::to_string(npc_types_entry.luclin_hairstyle));
update_values.push_back(columns[34] + " = " + std::to_string(npc_types_entry.luclin_haircolor));
update_values.push_back(columns[35] + " = " + std::to_string(npc_types_entry.luclin_eyecolor));
update_values.push_back(columns[36] + " = " + std::to_string(npc_types_entry.luclin_eyecolor2));
update_values.push_back(columns[37] + " = " + std::to_string(npc_types_entry.luclin_beardcolor));
update_values.push_back(columns[38] + " = " + std::to_string(npc_types_entry.luclin_beard));
update_values.push_back(columns[39] + " = " + std::to_string(npc_types_entry.drakkin_heritage));
update_values.push_back(columns[40] + " = " + std::to_string(npc_types_entry.drakkin_tattoo));
update_values.push_back(columns[41] + " = " + std::to_string(npc_types_entry.drakkin_details));
update_values.push_back(columns[42] + " = " + std::to_string(npc_types_entry.armortint_id));
update_values.push_back(columns[43] + " = " + std::to_string(npc_types_entry.armortint_red));
update_values.push_back(columns[44] + " = " + std::to_string(npc_types_entry.armortint_green));
update_values.push_back(columns[45] + " = " + std::to_string(npc_types_entry.armortint_blue));
update_values.push_back(columns[46] + " = " + std::to_string(npc_types_entry.d_melee_texture1));
update_values.push_back(columns[47] + " = " + std::to_string(npc_types_entry.d_melee_texture2));
update_values.push_back(columns[48] + " = '" + EscapeString(npc_types_entry.ammo_idfile) + "'");
update_values.push_back(columns[49] + " = " + std::to_string(npc_types_entry.prim_melee_type));
update_values.push_back(columns[50] + " = " + std::to_string(npc_types_entry.sec_melee_type));
update_values.push_back(columns[51] + " = " + std::to_string(npc_types_entry.ranged_type));
update_values.push_back(columns[52] + " = " + std::to_string(npc_types_entry.runspeed));
update_values.push_back(columns[53] + " = " + std::to_string(npc_types_entry.MR));
update_values.push_back(columns[54] + " = " + std::to_string(npc_types_entry.CR));
update_values.push_back(columns[55] + " = " + std::to_string(npc_types_entry.DR));
update_values.push_back(columns[56] + " = " + std::to_string(npc_types_entry.FR));
update_values.push_back(columns[57] + " = " + std::to_string(npc_types_entry.PR));
update_values.push_back(columns[58] + " = " + std::to_string(npc_types_entry.Corrup));
update_values.push_back(columns[59] + " = " + std::to_string(npc_types_entry.PhR));
update_values.push_back(columns[60] + " = " + std::to_string(npc_types_entry.see_invis));
update_values.push_back(columns[61] + " = " + std::to_string(npc_types_entry.see_invis_undead));
update_values.push_back(columns[62] + " = " + std::to_string(npc_types_entry.qglobal));
update_values.push_back(columns[63] + " = " + std::to_string(npc_types_entry.AC));
update_values.push_back(columns[64] + " = " + std::to_string(npc_types_entry.npc_aggro));
update_values.push_back(columns[65] + " = " + std::to_string(npc_types_entry.spawn_limit));
update_values.push_back(columns[66] + " = " + std::to_string(npc_types_entry.attack_speed));
update_values.push_back(columns[67] + " = " + std::to_string(npc_types_entry.attack_delay));
update_values.push_back(columns[68] + " = " + std::to_string(npc_types_entry.findable));
update_values.push_back(columns[69] + " = " + std::to_string(npc_types_entry.STR));
update_values.push_back(columns[70] + " = " + std::to_string(npc_types_entry.STA));
update_values.push_back(columns[71] + " = " + std::to_string(npc_types_entry.DEX));
update_values.push_back(columns[72] + " = " + std::to_string(npc_types_entry.AGI));
update_values.push_back(columns[73] + " = " + std::to_string(npc_types_entry._INT));
update_values.push_back(columns[74] + " = " + std::to_string(npc_types_entry.WIS));
update_values.push_back(columns[75] + " = " + std::to_string(npc_types_entry.CHA));
update_values.push_back(columns[76] + " = " + std::to_string(npc_types_entry.see_hide));
update_values.push_back(columns[77] + " = " + std::to_string(npc_types_entry.see_improved_hide));
update_values.push_back(columns[78] + " = " + std::to_string(npc_types_entry.trackable));
update_values.push_back(columns[79] + " = " + std::to_string(npc_types_entry.isbot));
update_values.push_back(columns[80] + " = " + std::to_string(npc_types_entry.exclude));
update_values.push_back(columns[81] + " = " + std::to_string(npc_types_entry.ATK));
update_values.push_back(columns[82] + " = " + std::to_string(npc_types_entry.Accuracy));
update_values.push_back(columns[83] + " = " + std::to_string(npc_types_entry.Avoidance));
update_values.push_back(columns[84] + " = " + std::to_string(npc_types_entry.slow_mitigation));
update_values.push_back(columns[85] + " = " + std::to_string(npc_types_entry.version));
update_values.push_back(columns[86] + " = " + std::to_string(npc_types_entry.maxlevel));
update_values.push_back(columns[87] + " = " + std::to_string(npc_types_entry.scalerate));
update_values.push_back(columns[88] + " = " + std::to_string(npc_types_entry.private_corpse));
update_values.push_back(columns[89] + " = " + std::to_string(npc_types_entry.unique_spawn_by_name));
update_values.push_back(columns[90] + " = " + std::to_string(npc_types_entry.underwater));
update_values.push_back(columns[91] + " = " + std::to_string(npc_types_entry.isquest));
update_values.push_back(columns[92] + " = " + std::to_string(npc_types_entry.emoteid));
update_values.push_back(columns[93] + " = " + std::to_string(npc_types_entry.spellscale));
update_values.push_back(columns[94] + " = " + std::to_string(npc_types_entry.healscale));
update_values.push_back(columns[95] + " = " + std::to_string(npc_types_entry.no_target_hotkey));
update_values.push_back(columns[96] + " = " + std::to_string(npc_types_entry.raid_target));
update_values.push_back(columns[97] + " = " + std::to_string(npc_types_entry.armtexture));
update_values.push_back(columns[98] + " = " + std::to_string(npc_types_entry.bracertexture));
update_values.push_back(columns[99] + " = " + std::to_string(npc_types_entry.handtexture));
update_values.push_back(columns[100] + " = " + std::to_string(npc_types_entry.legtexture));
update_values.push_back(columns[101] + " = " + std::to_string(npc_types_entry.feettexture));
update_values.push_back(columns[102] + " = " + std::to_string(npc_types_entry.light));
update_values.push_back(columns[103] + " = " + std::to_string(npc_types_entry.walkspeed));
update_values.push_back(columns[104] + " = " + std::to_string(npc_types_entry.peqid));
update_values.push_back(columns[105] + " = " + std::to_string(npc_types_entry.unique_));
update_values.push_back(columns[106] + " = " + std::to_string(npc_types_entry.fixed));
update_values.push_back(columns[107] + " = " + std::to_string(npc_types_entry.ignore_despawn));
update_values.push_back(columns[108] + " = " + std::to_string(npc_types_entry.show_name));
update_values.push_back(columns[109] + " = " + std::to_string(npc_types_entry.untargetable));
update_values.push_back(columns[110] + " = " + std::to_string(npc_types_entry.charm_ac));
update_values.push_back(columns[111] + " = " + std::to_string(npc_types_entry.charm_min_dmg));
update_values.push_back(columns[112] + " = " + std::to_string(npc_types_entry.charm_max_dmg));
update_values.push_back(columns[113] + " = " + std::to_string(npc_types_entry.charm_attack_delay));
update_values.push_back(columns[114] + " = " + std::to_string(npc_types_entry.charm_accuracy_rating));
update_values.push_back(columns[115] + " = " + std::to_string(npc_types_entry.charm_avoidance_rating));
update_values.push_back(columns[116] + " = " + std::to_string(npc_types_entry.charm_atk));
update_values.push_back(columns[117] + " = " + std::to_string(npc_types_entry.skip_global_loot));
update_values.push_back(columns[118] + " = " + std::to_string(npc_types_entry.rare_spawn));
update_values.push_back(columns[119] + " = " + std::to_string(npc_types_entry.stuck_behavior));
update_values.push_back(columns[120] + " = " + std::to_string(npc_types_entry.model));
update_values.push_back(columns[121] + " = " + std::to_string(npc_types_entry.flymode));
update_values.push_back(columns[122] + " = " + std::to_string(npc_types_entry.always_aggro));
update_values.push_back(columns[123] + " = " + std::to_string(npc_types_entry.exp_mod));
auto results = db.QueryDatabase(
fmt::format(
@@ -916,6 +922,7 @@ public:
insert_values.push_back(std::to_string(npc_types_entry.herosforgemodel));
insert_values.push_back(std::to_string(npc_types_entry.size));
insert_values.push_back(std::to_string(npc_types_entry.hp_regen_rate));
insert_values.push_back(std::to_string(npc_types_entry.hp_regen_per_second));
insert_values.push_back(std::to_string(npc_types_entry.mana_regen_rate));
insert_values.push_back(std::to_string(npc_types_entry.loottable_id));
insert_values.push_back(std::to_string(npc_types_entry.merchant_id));
@@ -1068,6 +1075,7 @@ public:
insert_values.push_back(std::to_string(npc_types_entry.herosforgemodel));
insert_values.push_back(std::to_string(npc_types_entry.size));
insert_values.push_back(std::to_string(npc_types_entry.hp_regen_rate));
insert_values.push_back(std::to_string(npc_types_entry.hp_regen_per_second));
insert_values.push_back(std::to_string(npc_types_entry.mana_regen_rate));
insert_values.push_back(std::to_string(npc_types_entry.loottable_id));
insert_values.push_back(std::to_string(npc_types_entry.merchant_id));
@@ -1216,122 +1224,123 @@ public:
entry.race = atoi(row[4]);
entry.class_ = atoi(row[5]);
entry.bodytype = atoi(row[6]);
entry.hp = atoi(row[7]);
entry.mana = atoi(row[8]);
entry.hp = strtoll(row[7], nullptr, 10);
entry.mana = strtoll(row[8], nullptr, 10);
entry.gender = atoi(row[9]);
entry.texture = atoi(row[10]);
entry.helmtexture = atoi(row[11]);
entry.herosforgemodel = atoi(row[12]);
entry.size = static_cast<float>(atof(row[13]));
entry.hp_regen_rate = atoi(row[14]);
entry.mana_regen_rate = atoi(row[15]);
entry.loottable_id = atoi(row[16]);
entry.merchant_id = atoi(row[17]);
entry.alt_currency_id = atoi(row[18]);
entry.npc_spells_id = atoi(row[19]);
entry.npc_spells_effects_id = atoi(row[20]);
entry.npc_faction_id = atoi(row[21]);
entry.adventure_template_id = atoi(row[22]);
entry.trap_template = atoi(row[23]);
entry.mindmg = atoi(row[24]);
entry.maxdmg = atoi(row[25]);
entry.attack_count = atoi(row[26]);
entry.npcspecialattks = row[27] ? row[27] : "";
entry.special_abilities = row[28] ? row[28] : "";
entry.aggroradius = atoi(row[29]);
entry.assistradius = atoi(row[30]);
entry.face = atoi(row[31]);
entry.luclin_hairstyle = atoi(row[32]);
entry.luclin_haircolor = atoi(row[33]);
entry.luclin_eyecolor = atoi(row[34]);
entry.luclin_eyecolor2 = atoi(row[35]);
entry.luclin_beardcolor = atoi(row[36]);
entry.luclin_beard = atoi(row[37]);
entry.drakkin_heritage = atoi(row[38]);
entry.drakkin_tattoo = atoi(row[39]);
entry.drakkin_details = atoi(row[40]);
entry.armortint_id = atoi(row[41]);
entry.armortint_red = atoi(row[42]);
entry.armortint_green = atoi(row[43]);
entry.armortint_blue = atoi(row[44]);
entry.d_melee_texture1 = atoi(row[45]);
entry.d_melee_texture2 = atoi(row[46]);
entry.ammo_idfile = row[47] ? row[47] : "";
entry.prim_melee_type = atoi(row[48]);
entry.sec_melee_type = atoi(row[49]);
entry.ranged_type = atoi(row[50]);
entry.runspeed = static_cast<float>(atof(row[51]));
entry.MR = atoi(row[52]);
entry.CR = atoi(row[53]);
entry.DR = atoi(row[54]);
entry.FR = atoi(row[55]);
entry.PR = atoi(row[56]);
entry.Corrup = atoi(row[57]);
entry.PhR = atoi(row[58]);
entry.see_invis = atoi(row[59]);
entry.see_invis_undead = atoi(row[60]);
entry.qglobal = atoi(row[61]);
entry.AC = atoi(row[62]);
entry.npc_aggro = atoi(row[63]);
entry.spawn_limit = atoi(row[64]);
entry.attack_speed = static_cast<float>(atof(row[65]));
entry.attack_delay = atoi(row[66]);
entry.findable = atoi(row[67]);
entry.STR = atoi(row[68]);
entry.STA = atoi(row[69]);
entry.DEX = atoi(row[70]);
entry.AGI = atoi(row[71]);
entry._INT = atoi(row[72]);
entry.WIS = atoi(row[73]);
entry.CHA = atoi(row[74]);
entry.see_hide = atoi(row[75]);
entry.see_improved_hide = atoi(row[76]);
entry.trackable = atoi(row[77]);
entry.isbot = atoi(row[78]);
entry.exclude = atoi(row[79]);
entry.ATK = atoi(row[80]);
entry.Accuracy = atoi(row[81]);
entry.Avoidance = atoi(row[82]);
entry.slow_mitigation = atoi(row[83]);
entry.version = atoi(row[84]);
entry.maxlevel = atoi(row[85]);
entry.scalerate = atoi(row[86]);
entry.private_corpse = atoi(row[87]);
entry.unique_spawn_by_name = atoi(row[88]);
entry.underwater = atoi(row[89]);
entry.isquest = atoi(row[90]);
entry.emoteid = atoi(row[91]);
entry.spellscale = static_cast<float>(atof(row[92]));
entry.healscale = static_cast<float>(atof(row[93]));
entry.no_target_hotkey = atoi(row[94]);
entry.raid_target = atoi(row[95]);
entry.armtexture = atoi(row[96]);
entry.bracertexture = atoi(row[97]);
entry.handtexture = atoi(row[98]);
entry.legtexture = atoi(row[99]);
entry.feettexture = atoi(row[100]);
entry.light = atoi(row[101]);
entry.walkspeed = atoi(row[102]);
entry.peqid = atoi(row[103]);
entry.unique_ = atoi(row[104]);
entry.fixed = atoi(row[105]);
entry.ignore_despawn = atoi(row[106]);
entry.show_name = atoi(row[107]);
entry.untargetable = atoi(row[108]);
entry.charm_ac = atoi(row[109]);
entry.charm_min_dmg = atoi(row[110]);
entry.charm_max_dmg = atoi(row[111]);
entry.charm_attack_delay = atoi(row[112]);
entry.charm_accuracy_rating = atoi(row[113]);
entry.charm_avoidance_rating = atoi(row[114]);
entry.charm_atk = atoi(row[115]);
entry.skip_global_loot = atoi(row[116]);
entry.rare_spawn = atoi(row[117]);
entry.stuck_behavior = atoi(row[118]);
entry.model = atoi(row[119]);
entry.flymode = atoi(row[120]);
entry.always_aggro = atoi(row[121]);
entry.exp_mod = atoi(row[122]);
entry.hp_regen_rate = strtoll(row[14], nullptr, 10);
entry.hp_regen_per_second = strtoll(row[15], nullptr, 10);
entry.mana_regen_rate = strtoll(row[16], nullptr, 10);
entry.loottable_id = atoi(row[17]);
entry.merchant_id = atoi(row[18]);
entry.alt_currency_id = atoi(row[19]);
entry.npc_spells_id = atoi(row[20]);
entry.npc_spells_effects_id = atoi(row[21]);
entry.npc_faction_id = atoi(row[22]);
entry.adventure_template_id = atoi(row[23]);
entry.trap_template = atoi(row[24]);
entry.mindmg = atoi(row[25]);
entry.maxdmg = atoi(row[26]);
entry.attack_count = atoi(row[27]);
entry.npcspecialattks = row[28] ? row[28] : "";
entry.special_abilities = row[29] ? row[29] : "";
entry.aggroradius = atoi(row[30]);
entry.assistradius = atoi(row[31]);
entry.face = atoi(row[32]);
entry.luclin_hairstyle = atoi(row[33]);
entry.luclin_haircolor = atoi(row[34]);
entry.luclin_eyecolor = atoi(row[35]);
entry.luclin_eyecolor2 = atoi(row[36]);
entry.luclin_beardcolor = atoi(row[37]);
entry.luclin_beard = atoi(row[38]);
entry.drakkin_heritage = atoi(row[39]);
entry.drakkin_tattoo = atoi(row[40]);
entry.drakkin_details = atoi(row[41]);
entry.armortint_id = atoi(row[42]);
entry.armortint_red = atoi(row[43]);
entry.armortint_green = atoi(row[44]);
entry.armortint_blue = atoi(row[45]);
entry.d_melee_texture1 = atoi(row[46]);
entry.d_melee_texture2 = atoi(row[47]);
entry.ammo_idfile = row[48] ? row[48] : "";
entry.prim_melee_type = atoi(row[49]);
entry.sec_melee_type = atoi(row[50]);
entry.ranged_type = atoi(row[51]);
entry.runspeed = static_cast<float>(atof(row[52]));
entry.MR = atoi(row[53]);
entry.CR = atoi(row[54]);
entry.DR = atoi(row[55]);
entry.FR = atoi(row[56]);
entry.PR = atoi(row[57]);
entry.Corrup = atoi(row[58]);
entry.PhR = atoi(row[59]);
entry.see_invis = atoi(row[60]);
entry.see_invis_undead = atoi(row[61]);
entry.qglobal = atoi(row[62]);
entry.AC = atoi(row[63]);
entry.npc_aggro = atoi(row[64]);
entry.spawn_limit = atoi(row[65]);
entry.attack_speed = static_cast<float>(atof(row[66]));
entry.attack_delay = atoi(row[67]);
entry.findable = atoi(row[68]);
entry.STR = atoi(row[69]);
entry.STA = atoi(row[70]);
entry.DEX = atoi(row[71]);
entry.AGI = atoi(row[72]);
entry._INT = atoi(row[73]);
entry.WIS = atoi(row[74]);
entry.CHA = atoi(row[75]);
entry.see_hide = atoi(row[76]);
entry.see_improved_hide = atoi(row[77]);
entry.trackable = atoi(row[78]);
entry.isbot = atoi(row[79]);
entry.exclude = atoi(row[80]);
entry.ATK = atoi(row[81]);
entry.Accuracy = atoi(row[82]);
entry.Avoidance = atoi(row[83]);
entry.slow_mitigation = atoi(row[84]);
entry.version = atoi(row[85]);
entry.maxlevel = atoi(row[86]);
entry.scalerate = atoi(row[87]);
entry.private_corpse = atoi(row[88]);
entry.unique_spawn_by_name = atoi(row[89]);
entry.underwater = atoi(row[90]);
entry.isquest = atoi(row[91]);
entry.emoteid = atoi(row[92]);
entry.spellscale = static_cast<float>(atof(row[93]));
entry.healscale = static_cast<float>(atof(row[94]));
entry.no_target_hotkey = atoi(row[95]);
entry.raid_target = atoi(row[96]);
entry.armtexture = atoi(row[97]);
entry.bracertexture = atoi(row[98]);
entry.handtexture = atoi(row[99]);
entry.legtexture = atoi(row[100]);
entry.feettexture = atoi(row[101]);
entry.light = atoi(row[102]);
entry.walkspeed = atoi(row[103]);
entry.peqid = atoi(row[104]);
entry.unique_ = atoi(row[105]);
entry.fixed = atoi(row[106]);
entry.ignore_despawn = atoi(row[107]);
entry.show_name = atoi(row[108]);
entry.untargetable = atoi(row[109]);
entry.charm_ac = atoi(row[110]);
entry.charm_min_dmg = atoi(row[111]);
entry.charm_max_dmg = atoi(row[112]);
entry.charm_attack_delay = atoi(row[113]);
entry.charm_accuracy_rating = atoi(row[114]);
entry.charm_avoidance_rating = atoi(row[115]);
entry.charm_atk = atoi(row[116]);
entry.skip_global_loot = atoi(row[117]);
entry.rare_spawn = atoi(row[118]);
entry.stuck_behavior = atoi(row[119]);
entry.model = atoi(row[120]);
entry.flymode = atoi(row[121]);
entry.always_aggro = atoi(row[122]);
entry.exp_mod = atoi(row[123]);
all_entries.push_back(entry);
}
@@ -1363,122 +1372,123 @@ public:
entry.race = atoi(row[4]);
entry.class_ = atoi(row[5]);
entry.bodytype = atoi(row[6]);
entry.hp = atoi(row[7]);
entry.mana = atoi(row[8]);
entry.hp = strtoll(row[7], nullptr, 10);
entry.mana = strtoll(row[8], nullptr, 10);
entry.gender = atoi(row[9]);
entry.texture = atoi(row[10]);
entry.helmtexture = atoi(row[11]);
entry.herosforgemodel = atoi(row[12]);
entry.size = static_cast<float>(atof(row[13]));
entry.hp_regen_rate = atoi(row[14]);
entry.mana_regen_rate = atoi(row[15]);
entry.loottable_id = atoi(row[16]);
entry.merchant_id = atoi(row[17]);
entry.alt_currency_id = atoi(row[18]);
entry.npc_spells_id = atoi(row[19]);
entry.npc_spells_effects_id = atoi(row[20]);
entry.npc_faction_id = atoi(row[21]);
entry.adventure_template_id = atoi(row[22]);
entry.trap_template = atoi(row[23]);
entry.mindmg = atoi(row[24]);
entry.maxdmg = atoi(row[25]);
entry.attack_count = atoi(row[26]);
entry.npcspecialattks = row[27] ? row[27] : "";
entry.special_abilities = row[28] ? row[28] : "";
entry.aggroradius = atoi(row[29]);
entry.assistradius = atoi(row[30]);
entry.face = atoi(row[31]);
entry.luclin_hairstyle = atoi(row[32]);
entry.luclin_haircolor = atoi(row[33]);
entry.luclin_eyecolor = atoi(row[34]);
entry.luclin_eyecolor2 = atoi(row[35]);
entry.luclin_beardcolor = atoi(row[36]);
entry.luclin_beard = atoi(row[37]);
entry.drakkin_heritage = atoi(row[38]);
entry.drakkin_tattoo = atoi(row[39]);
entry.drakkin_details = atoi(row[40]);
entry.armortint_id = atoi(row[41]);
entry.armortint_red = atoi(row[42]);
entry.armortint_green = atoi(row[43]);
entry.armortint_blue = atoi(row[44]);
entry.d_melee_texture1 = atoi(row[45]);
entry.d_melee_texture2 = atoi(row[46]);
entry.ammo_idfile = row[47] ? row[47] : "";
entry.prim_melee_type = atoi(row[48]);
entry.sec_melee_type = atoi(row[49]);
entry.ranged_type = atoi(row[50]);
entry.runspeed = static_cast<float>(atof(row[51]));
entry.MR = atoi(row[52]);
entry.CR = atoi(row[53]);
entry.DR = atoi(row[54]);
entry.FR = atoi(row[55]);
entry.PR = atoi(row[56]);
entry.Corrup = atoi(row[57]);
entry.PhR = atoi(row[58]);
entry.see_invis = atoi(row[59]);
entry.see_invis_undead = atoi(row[60]);
entry.qglobal = atoi(row[61]);
entry.AC = atoi(row[62]);
entry.npc_aggro = atoi(row[63]);
entry.spawn_limit = atoi(row[64]);
entry.attack_speed = static_cast<float>(atof(row[65]));
entry.attack_delay = atoi(row[66]);
entry.findable = atoi(row[67]);
entry.STR = atoi(row[68]);
entry.STA = atoi(row[69]);
entry.DEX = atoi(row[70]);
entry.AGI = atoi(row[71]);
entry._INT = atoi(row[72]);
entry.WIS = atoi(row[73]);
entry.CHA = atoi(row[74]);
entry.see_hide = atoi(row[75]);
entry.see_improved_hide = atoi(row[76]);
entry.trackable = atoi(row[77]);
entry.isbot = atoi(row[78]);
entry.exclude = atoi(row[79]);
entry.ATK = atoi(row[80]);
entry.Accuracy = atoi(row[81]);
entry.Avoidance = atoi(row[82]);
entry.slow_mitigation = atoi(row[83]);
entry.version = atoi(row[84]);
entry.maxlevel = atoi(row[85]);
entry.scalerate = atoi(row[86]);
entry.private_corpse = atoi(row[87]);
entry.unique_spawn_by_name = atoi(row[88]);
entry.underwater = atoi(row[89]);
entry.isquest = atoi(row[90]);
entry.emoteid = atoi(row[91]);
entry.spellscale = static_cast<float>(atof(row[92]));
entry.healscale = static_cast<float>(atof(row[93]));
entry.no_target_hotkey = atoi(row[94]);
entry.raid_target = atoi(row[95]);
entry.armtexture = atoi(row[96]);
entry.bracertexture = atoi(row[97]);
entry.handtexture = atoi(row[98]);
entry.legtexture = atoi(row[99]);
entry.feettexture = atoi(row[100]);
entry.light = atoi(row[101]);
entry.walkspeed = atoi(row[102]);
entry.peqid = atoi(row[103]);
entry.unique_ = atoi(row[104]);
entry.fixed = atoi(row[105]);
entry.ignore_despawn = atoi(row[106]);
entry.show_name = atoi(row[107]);
entry.untargetable = atoi(row[108]);
entry.charm_ac = atoi(row[109]);
entry.charm_min_dmg = atoi(row[110]);
entry.charm_max_dmg = atoi(row[111]);
entry.charm_attack_delay = atoi(row[112]);
entry.charm_accuracy_rating = atoi(row[113]);
entry.charm_avoidance_rating = atoi(row[114]);
entry.charm_atk = atoi(row[115]);
entry.skip_global_loot = atoi(row[116]);
entry.rare_spawn = atoi(row[117]);
entry.stuck_behavior = atoi(row[118]);
entry.model = atoi(row[119]);
entry.flymode = atoi(row[120]);
entry.always_aggro = atoi(row[121]);
entry.exp_mod = atoi(row[122]);
entry.hp_regen_rate = strtoll(row[14], nullptr, 10);
entry.hp_regen_per_second = strtoll(row[15], nullptr, 10);
entry.mana_regen_rate = strtoll(row[16], nullptr, 10);
entry.loottable_id = atoi(row[17]);
entry.merchant_id = atoi(row[18]);
entry.alt_currency_id = atoi(row[19]);
entry.npc_spells_id = atoi(row[20]);
entry.npc_spells_effects_id = atoi(row[21]);
entry.npc_faction_id = atoi(row[22]);
entry.adventure_template_id = atoi(row[23]);
entry.trap_template = atoi(row[24]);
entry.mindmg = atoi(row[25]);
entry.maxdmg = atoi(row[26]);
entry.attack_count = atoi(row[27]);
entry.npcspecialattks = row[28] ? row[28] : "";
entry.special_abilities = row[29] ? row[29] : "";
entry.aggroradius = atoi(row[30]);
entry.assistradius = atoi(row[31]);
entry.face = atoi(row[32]);
entry.luclin_hairstyle = atoi(row[33]);
entry.luclin_haircolor = atoi(row[34]);
entry.luclin_eyecolor = atoi(row[35]);
entry.luclin_eyecolor2 = atoi(row[36]);
entry.luclin_beardcolor = atoi(row[37]);
entry.luclin_beard = atoi(row[38]);
entry.drakkin_heritage = atoi(row[39]);
entry.drakkin_tattoo = atoi(row[40]);
entry.drakkin_details = atoi(row[41]);
entry.armortint_id = atoi(row[42]);
entry.armortint_red = atoi(row[43]);
entry.armortint_green = atoi(row[44]);
entry.armortint_blue = atoi(row[45]);
entry.d_melee_texture1 = atoi(row[46]);
entry.d_melee_texture2 = atoi(row[47]);
entry.ammo_idfile = row[48] ? row[48] : "";
entry.prim_melee_type = atoi(row[49]);
entry.sec_melee_type = atoi(row[50]);
entry.ranged_type = atoi(row[51]);
entry.runspeed = static_cast<float>(atof(row[52]));
entry.MR = atoi(row[53]);
entry.CR = atoi(row[54]);
entry.DR = atoi(row[55]);
entry.FR = atoi(row[56]);
entry.PR = atoi(row[57]);
entry.Corrup = atoi(row[58]);
entry.PhR = atoi(row[59]);
entry.see_invis = atoi(row[60]);
entry.see_invis_undead = atoi(row[61]);
entry.qglobal = atoi(row[62]);
entry.AC = atoi(row[63]);
entry.npc_aggro = atoi(row[64]);
entry.spawn_limit = atoi(row[65]);
entry.attack_speed = static_cast<float>(atof(row[66]));
entry.attack_delay = atoi(row[67]);
entry.findable = atoi(row[68]);
entry.STR = atoi(row[69]);
entry.STA = atoi(row[70]);
entry.DEX = atoi(row[71]);
entry.AGI = atoi(row[72]);
entry._INT = atoi(row[73]);
entry.WIS = atoi(row[74]);
entry.CHA = atoi(row[75]);
entry.see_hide = atoi(row[76]);
entry.see_improved_hide = atoi(row[77]);
entry.trackable = atoi(row[78]);
entry.isbot = atoi(row[79]);
entry.exclude = atoi(row[80]);
entry.ATK = atoi(row[81]);
entry.Accuracy = atoi(row[82]);
entry.Avoidance = atoi(row[83]);
entry.slow_mitigation = atoi(row[84]);
entry.version = atoi(row[85]);
entry.maxlevel = atoi(row[86]);
entry.scalerate = atoi(row[87]);
entry.private_corpse = atoi(row[88]);
entry.unique_spawn_by_name = atoi(row[89]);
entry.underwater = atoi(row[90]);
entry.isquest = atoi(row[91]);
entry.emoteid = atoi(row[92]);
entry.spellscale = static_cast<float>(atof(row[93]));
entry.healscale = static_cast<float>(atof(row[94]));
entry.no_target_hotkey = atoi(row[95]);
entry.raid_target = atoi(row[96]);
entry.armtexture = atoi(row[97]);
entry.bracertexture = atoi(row[98]);
entry.handtexture = atoi(row[99]);
entry.legtexture = atoi(row[100]);
entry.feettexture = atoi(row[101]);
entry.light = atoi(row[102]);
entry.walkspeed = atoi(row[103]);
entry.peqid = atoi(row[104]);
entry.unique_ = atoi(row[105]);
entry.fixed = atoi(row[106]);
entry.ignore_despawn = atoi(row[107]);
entry.show_name = atoi(row[108]);
entry.untargetable = atoi(row[109]);
entry.charm_ac = atoi(row[110]);
entry.charm_min_dmg = atoi(row[111]);
entry.charm_max_dmg = atoi(row[112]);
entry.charm_attack_delay = atoi(row[113]);
entry.charm_accuracy_rating = atoi(row[114]);
entry.charm_avoidance_rating = atoi(row[115]);
entry.charm_atk = atoi(row[116]);
entry.skip_global_loot = atoi(row[117]);
entry.rare_spawn = atoi(row[118]);
entry.stuck_behavior = atoi(row[119]);
entry.model = atoi(row[120]);
entry.flymode = atoi(row[121]);
entry.always_aggro = atoi(row[122]);
entry.exp_mod = atoi(row[123]);
all_entries.push_back(entry);
}
@@ -29,6 +29,7 @@ public:
std::string spell_list;
std::string description_override;
int goalid;
std::string goal_match_list;
int goalmethod;
int goalcount;
int delivertonpc;
@@ -54,6 +55,7 @@ public:
"spell_list",
"description_override",
"goalid",
"goal_match_list",
"goalmethod",
"goalcount",
"delivertonpc",
@@ -75,6 +77,7 @@ public:
"spell_list",
"description_override",
"goalid",
"goal_match_list",
"goalmethod",
"goalcount",
"delivertonpc",
@@ -130,6 +133,7 @@ public:
entry.spell_list = "0";
entry.description_override = "";
entry.goalid = 0;
entry.goal_match_list = "";
entry.goalmethod = 0;
entry.goalcount = 1;
entry.delivertonpc = 0;
@@ -180,11 +184,12 @@ public:
entry.spell_list = row[7] ? row[7] : "";
entry.description_override = row[8] ? row[8] : "";
entry.goalid = atoi(row[9]);
entry.goalmethod = atoi(row[10]);
entry.goalcount = atoi(row[11]);
entry.delivertonpc = atoi(row[12]);
entry.zones = row[13] ? row[13] : "";
entry.optional = atoi(row[14]);
entry.goal_match_list = row[10] ? row[10] : "";
entry.goalmethod = atoi(row[11]);
entry.goalcount = atoi(row[12]);
entry.delivertonpc = atoi(row[13]);
entry.zones = row[14] ? row[14] : "";
entry.optional = atoi(row[15]);
return entry;
}
@@ -228,11 +233,12 @@ public:
update_values.push_back(columns[7] + " = '" + EscapeString(task_activities_entry.spell_list) + "'");
update_values.push_back(columns[8] + " = '" + EscapeString(task_activities_entry.description_override) + "'");
update_values.push_back(columns[9] + " = " + std::to_string(task_activities_entry.goalid));
update_values.push_back(columns[10] + " = " + std::to_string(task_activities_entry.goalmethod));
update_values.push_back(columns[11] + " = " + std::to_string(task_activities_entry.goalcount));
update_values.push_back(columns[12] + " = " + std::to_string(task_activities_entry.delivertonpc));
update_values.push_back(columns[13] + " = '" + EscapeString(task_activities_entry.zones) + "'");
update_values.push_back(columns[14] + " = " + std::to_string(task_activities_entry.optional));
update_values.push_back(columns[10] + " = '" + EscapeString(task_activities_entry.goal_match_list) + "'");
update_values.push_back(columns[11] + " = " + std::to_string(task_activities_entry.goalmethod));
update_values.push_back(columns[12] + " = " + std::to_string(task_activities_entry.goalcount));
update_values.push_back(columns[13] + " = " + std::to_string(task_activities_entry.delivertonpc));
update_values.push_back(columns[14] + " = '" + EscapeString(task_activities_entry.zones) + "'");
update_values.push_back(columns[15] + " = " + std::to_string(task_activities_entry.optional));
auto results = db.QueryDatabase(
fmt::format(
@@ -264,6 +270,7 @@ public:
insert_values.push_back("'" + EscapeString(task_activities_entry.spell_list) + "'");
insert_values.push_back("'" + EscapeString(task_activities_entry.description_override) + "'");
insert_values.push_back(std::to_string(task_activities_entry.goalid));
insert_values.push_back("'" + EscapeString(task_activities_entry.goal_match_list) + "'");
insert_values.push_back(std::to_string(task_activities_entry.goalmethod));
insert_values.push_back(std::to_string(task_activities_entry.goalcount));
insert_values.push_back(std::to_string(task_activities_entry.delivertonpc));
@@ -308,6 +315,7 @@ public:
insert_values.push_back("'" + EscapeString(task_activities_entry.spell_list) + "'");
insert_values.push_back("'" + EscapeString(task_activities_entry.description_override) + "'");
insert_values.push_back(std::to_string(task_activities_entry.goalid));
insert_values.push_back("'" + EscapeString(task_activities_entry.goal_match_list) + "'");
insert_values.push_back(std::to_string(task_activities_entry.goalmethod));
insert_values.push_back(std::to_string(task_activities_entry.goalcount));
insert_values.push_back(std::to_string(task_activities_entry.delivertonpc));
@@ -356,11 +364,12 @@ public:
entry.spell_list = row[7] ? row[7] : "";
entry.description_override = row[8] ? row[8] : "";
entry.goalid = atoi(row[9]);
entry.goalmethod = atoi(row[10]);
entry.goalcount = atoi(row[11]);
entry.delivertonpc = atoi(row[12]);
entry.zones = row[13] ? row[13] : "";
entry.optional = atoi(row[14]);
entry.goal_match_list = row[10] ? row[10] : "";
entry.goalmethod = atoi(row[11]);
entry.goalcount = atoi(row[12]);
entry.delivertonpc = atoi(row[13]);
entry.zones = row[14] ? row[14] : "";
entry.optional = atoi(row[15]);
all_entries.push_back(entry);
}
@@ -395,11 +404,12 @@ public:
entry.spell_list = row[7] ? row[7] : "";
entry.description_override = row[8] ? row[8] : "";
entry.goalid = atoi(row[9]);
entry.goalmethod = atoi(row[10]);
entry.goalcount = atoi(row[11]);
entry.delivertonpc = atoi(row[12]);
entry.zones = row[13] ? row[13] : "";
entry.optional = atoi(row[14]);
entry.goal_match_list = row[10] ? row[10] : "";
entry.goalmethod = atoi(row[11]);
entry.goalcount = atoi(row[12]);
entry.delivertonpc = atoi(row[13]);
entry.zones = row[14] ? row[14] : "";
entry.optional = atoi(row[15]);
all_entries.push_back(entry);
}
@@ -0,0 +1,70 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_DISCORD_WEBHOOKS_REPOSITORY_H
#define EQEMU_DISCORD_WEBHOOKS_REPOSITORY_H
#include "../database.h"
#include "../string_util.h"
#include "base/base_discord_webhooks_repository.h"
class DiscordWebhooksRepository: public BaseDiscordWebhooksRepository {
public:
/**
* This file was auto generated and can be modified and extended upon
*
* Base repository methods are automatically
* generated in the "base" version of this repository. The base repository
* is immutable and to be left untouched, while methods in this class
* are used as extension methods for more specific persistence-layer
* accessors or mutators.
*
* Base Methods (Subject to be expanded upon in time)
*
* Note: Not all tables are designed appropriately to fit functionality with all base methods
*
* InsertOne
* UpdateOne
* DeleteOne
* FindOne
* GetWhere(std::string where_filter)
* DeleteWhere(std::string where_filter)
* InsertMany
* All
*
* Example custom methods in a repository
*
* DiscordWebhooksRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* DiscordWebhooksRepository::GetWhereNeverExpires()
* DiscordWebhooksRepository::GetWhereXAndY()
* DiscordWebhooksRepository::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
* method that can be re-used easily elsewhere especially if it can use a base repository
* method and encapsulate filters there
*/
// Custom extended repository methods here
};
#endif //EQEMU_DISCORD_WEBHOOKS_REPOSITORY_H
@@ -109,10 +109,10 @@ public:
entry.gridid = atoi(row[0]);
entry.zoneid = atoi(row[1]);
entry.number = atoi(row[2]);
entry.x = atof(row[3]);
entry.y = atof(row[4]);
entry.z = atof(row[5]);
entry.heading = atof(row[6]);
entry.x = static_cast<float>(atof(row[3]));
entry.y = static_cast<float>(atof(row[4]));
entry.z = static_cast<float>(atof(row[5]));
entry.heading = static_cast<float>(atof(row[6]));
entry.pause = atoi(row[7]);
entry.centerpoint = atoi(row[8]);
+9 -9
View File
@@ -246,7 +246,7 @@ const std::string &RuleManager::_GetRuleNotes(RuleType type, uint16 index) {
bool RuleManager::LoadRules(Database *database, const char *ruleset_name, bool reload) {
int ruleset_id = this->GetRulesetID(database, ruleset_name);
int ruleset_id = GetRulesetID(database, ruleset_name);
if (ruleset_id < 0) {
Log(Logs::Detail, Logs::Rules, "Failed to find ruleset '%s' for load operation. Canceling.", ruleset_name);
return (false);
@@ -309,7 +309,7 @@ bool RuleManager::LoadRules(Database *database, const char *ruleset_name, bool r
}
void RuleManager::SaveRules(Database *database, const char *ruleset_name) {
if (ruleset_name != nullptr) {
//saving to a specific name
if (m_activeName != ruleset_name) {
@@ -347,7 +347,7 @@ void RuleManager::_SaveRule(Database *database, RuleType type, uint16 index) {
return;
if (type == BoolRule && strcasecmp(_GetRuleName(type, index), "World:UseClientBasedExpansionSettings") == 0)
return;
switch (type) {
case IntRule:
sprintf(value_string, "%d", m_RuleIntValues[index]);
@@ -432,7 +432,7 @@ bool RuleManager::UpdateInjectedRules(Database *db, const char *ruleset_name, bo
// build injected entries
for (const auto &rd_iter : rule_data) {
const auto &dd_iter = std::find(database_data.begin(), database_data.end(), rd_iter.first);
if (dd_iter == database_data.end()) {
@@ -440,7 +440,7 @@ bool RuleManager::UpdateInjectedRules(Database *db, const char *ruleset_name, bo
std::tuple<int, std::string, std::string, std::string>(
ruleset_id, // `ruleset_id`
rd_iter.first, // `rule_name`
rd_iter.second.first, // `rule_value`
rd_iter.second.first, // `rule_value`
EscapeString(*rd_iter.second.second) // `notes`
)
);
@@ -481,7 +481,7 @@ bool RuleManager::UpdateInjectedRules(Database *db, const char *ruleset_name, bo
ruleset_id
);
}
return true;
}
@@ -509,7 +509,7 @@ bool RuleManager::UpdateOrphanedRules(Database *db, bool quiet_update)
// build orphaned entries
for (auto row : results) {
const auto &rd_iter = std::find(rule_data.begin(), rule_data.end(), row[0]);
if (rd_iter == rule_data.end()) {
@@ -539,7 +539,7 @@ bool RuleManager::UpdateOrphanedRules(Database *db, bool quiet_update)
LogInfo("[{}] Orphaned Rule(s) Deleted from [All Rulesets] (-1)", orphaned_rule_entries.size());
}
return true;
}
@@ -586,7 +586,7 @@ bool RuleManager::RestoreRuleNotes(Database *db)
if (!db->QueryDatabase(query).Success()) {
continue;
}
++update_count;
}
+20 -41
View File
@@ -150,8 +150,6 @@ RULE_INT(Character, InvSnapshotMinRetryM, 30, "Time to re-attempt an inventory s
RULE_INT(Character, InvSnapshotHistoryD, 30, "Time to keep snapshot entries (days)")
RULE_BOOL(Character, RestrictSpellScribing, false, "Setting whether to restrict spell scribing to allowable races/classes of spell scroll")
RULE_BOOL(Character, UseStackablePickPocketing, true, "Allows stackable pickpocketed items to stack instead of only being allowed in empty inventory slots")
RULE_BOOL(Character, EnableAvoidanceCap, false, "Setting whether the avoidance cap should be activated")
RULE_INT(Character, AvoidanceCap, 750, "750 Is a pretty good value, seen people dodge all attacks beyond 1,000 Avoidance")
RULE_BOOL(Character, AllowMQTarget, false, "Disables putting players in the 'hackers' list for targeting beyond the clip plane or attempting to target something untargetable")
RULE_BOOL(Character, UseOldBindWound, false, "Uses the original bind wound behavior")
RULE_BOOL(Character, GrantHoTTOnCreate, false, "Grant Health of Target's Target leadership AA on character creation")
@@ -350,7 +348,6 @@ RULE_INT(Spells, MaxTotalSlotsPET, 30, "Maximum total of pet slots. The default
RULE_BOOL (Spells, EnableBlockedBuffs, true, "Allow blocked spells")
RULE_INT(Spells, ReflectType, 4, "Reflect type. 0=disabled, 1=single target player spells only, 2=all player spells, 3=all single target spells, 4=all spells")
RULE_BOOL(Spells, ReflectMessagesClose, true, "True (Live functionality) is for Reflect messages to show to players within close proximity. False shows just player reflecting")
RULE_INT(Spells, VirusSpreadDistance, 30, "The distance a viral spell will jump to its next victim")
RULE_BOOL(Spells, LiveLikeFocusEffects, true, "Determines whether specific healing, dmg and mana reduction focuses are randomized")
RULE_INT(Spells, BaseImmunityLevel, 55, "The level that targets start to be immune to stun, fear and mez spells with a maximum level of 0")
RULE_BOOL(Spells, NPCIgnoreBaseImmunity, true, "Whether or not NPC get to ignore the BaseImmunityLevel for their spells")
@@ -417,6 +414,9 @@ RULE_BOOL(Spells, CompoundLifetapHeals, true, "True: Lifetap heals calculate dam
RULE_BOOL(Spells, UseFadingMemoriesMaxLevel, false, "Enables to limit field in spell data to set the max level that over which an NPC will ignore fading memories effect and not lose aggro.")
RULE_BOOL(Spells, FixBeaconHeading, false, "Beacon spells use casters heading to fix live bug. False: Live like heading always 0.")
RULE_BOOL(Spells, UseSpellImpliedTargeting, false, "Replicates EQ2-style targeting behavior for spells. Spells will 'pass through' inappropriate targets to target's target if it is appropriate.")
RULE_BOOL(Spells, BuffsFadeOnDeath, true, "Disable to keep buffs from fading on death")
RULE_BOOL(Spells, IllusionsAlwaysPersist, false, "Allows Illusions to persist beyond death and zoning always.")
RULE_BOOL(Spells, UseItemCastMessage, false, "Enable to use the \"item begins to glow\" messages when casting from an item.")
RULE_CATEGORY_END()
RULE_CATEGORY(Combat)
@@ -441,17 +441,6 @@ RULE_REAL(Combat, AvgProcsPerMinute, 2.0, "Average proc rate per minute")
RULE_REAL(Combat, ProcPerMinDexContrib, 0.075, "Increases the probability of a proc increased by DEX by the value indicated")
RULE_REAL(Combat, BaseProcChance, 0.035, "Base chance for procs")
RULE_REAL(Combat, ProcDexDivideBy, 11000, "Divisor for the probability of a proc increased by dexterity")
RULE_REAL(Combat, BaseHitChance, 69.0, "Base chance to hit")
RULE_REAL(Combat, NPCBonusHitChance, 26.0, "Bonus chance to hit for NPC")
RULE_REAL(Combat, HitFalloffMinor, 5.0, "Hit will fall off up to value over the initial level range (percent)")
RULE_REAL(Combat, HitFalloffModerate, 7.0, "Hit will fall off up to value over the three levels after the initial level range (percent)")
RULE_REAL(Combat, HitFalloffMajor, 50.0, "Hit will fall off sharply if we're outside the minor and moderate range")
RULE_REAL(Combat, HitBonusPerLevel, 1.2, "You gain this percentage of hit for every level you are above your target")
RULE_REAL(Combat, WeaponSkillFalloff, 0.33, "For every weapon skill point that's not maxed you lose this percentage of hit")
RULE_REAL(Combat, ArcheryHitPenalty, 0.25, "Archery has a hit penalty to try to help balance it with the plethora of long term +hit modifiers for it")
RULE_REAL(Combat, AgiHitFactor, 0.01, "Factor with which agility is taken into account in the hit probability. Higher is better")
RULE_REAL(Combat, MinChancetoHit, 5.0, "Minimum percentage chance to hit with regular melee/ranged")
RULE_REAL(Combat, MaxChancetoHit, 95.0, "Maximum percentage chance to hit with regular melee/ranged")
RULE_INT(Combat, MinRangedAttackDist, 25, "Minimum Distance to use Ranged Attacks")
RULE_BOOL(Combat, ArcheryBonusRequiresStationary, true, "does the 2x archery bonus chance require a stationary npc")
RULE_REAL(Combat, ArcheryNPCMultiplier, 1.0, "Value is multiplied by the regular dmg to get the archery dmg")
@@ -460,40 +449,13 @@ RULE_INT(Combat, MaxRampageTargets, 3, "Maximum number of people hit with rampag
RULE_INT(Combat, DefaultRampageTargets, 1, "Default number of people to hit with rampage")
RULE_BOOL(Combat, RampageHitsTarget, false, "Rampage will hit the target if it still has targets left")
RULE_INT(Combat, MaxFlurryHits, 2, "Maximum number of extra hits from flurry")
RULE_REAL(Combat, NPCACFactor, 2.25, "If UseIntervalAC is enabled, the armor class for NPC is divided by this value")
RULE_INT(Combat, ClothACSoftcap, 75, "If OldACSoftcapRules is true: armorclass softcap for cloth armor")
RULE_INT(Combat, LeatherACSoftcap, 100, "If OldACSoftcapRules is true: armorclass softcap for leather armor")
RULE_INT(Combat, MonkACSoftcap, 120, "If OldACSoftcapRules is true: armorclass softcap for monks")
RULE_INT(Combat, ChainACSoftcap, 200, "If OldACSoftcapRules is true: armorclass softcap for chain armor")
RULE_INT(Combat, PlateACSoftcap, 300, "If OldACSoftcapRules is true: armorclass softcap for plate armor")
RULE_REAL(Combat, AAMitigationACFactor, 3.0, "If OldACSoftcapRules: AA mitgation armorclass factor")
RULE_REAL(Combat, WarriorACSoftcapReturn, 0.45, "If OldACSoftcapRules: warrior armorclass softcap increase-factor")
RULE_REAL(Combat, KnightACSoftcapReturn, 0.33, "If OldACSoftcapRules: SHD/PAL/MNK armorclass softcap increase-factor")
RULE_REAL(Combat, LowPlateChainACSoftcapReturn, 0.23, "If OldACSoftcapRules: CLR/BRD/BSK/ROG/SHA/MNK armorclass softcap increase-factor")
RULE_REAL(Combat, LowChainLeatherACSoftcapReturn, 0.17, "If OldACSoftcapRules: RNG/BST armorclass softcap increase-factor")
RULE_REAL(Combat, CasterACSoftcapReturn, 0.06, "If OldACSoftcapRules: WIZ/MAG/NEC/ENC/DRU armorclass softcap increase-factor")
RULE_REAL(Combat, MiscACSoftcapReturn, 0.3, "If OldACSoftcapRules true/false: unspecified classes armorclass softcap increase-factor")
RULE_BOOL(Combat, OldACSoftcapRules, false, "Setting if the old softcap values should be used")
RULE_BOOL(Combat, UseOldDamageIntervalRules, false, "Use old damage formulas for everything")
RULE_REAL(Combat, WarACSoftcapReturn, 0.3448, "WAR armorclass softcap increase-factor")
RULE_REAL(Combat, ClrRngMnkBrdACSoftcapReturn, 0.3030, "CLR/RNG/MNK/BRD armorclass softcap increase-factor")
RULE_REAL(Combat, PalShdACSoftcapReturn, 0.3226, "SHD/PAL armorclass softcap increase-factor")
RULE_REAL(Combat, DruNecWizEncMagACSoftcapReturn, 0.2000, "DRU/NEC/WIZ/ENC/MAG softcap increase-factor")
RULE_REAL(Combat, RogShmBstBerACSoftcapReturn, 0.2500, "ROG/SHM/BST/BER softcap increase-factor")
RULE_REAL(Combat, SoftcapFactor, 1.88, "When UseIntervalAC is enabled, the softcap for mitigation capability is multiplied by this value")
RULE_REAL(Combat, ACthac0Factor, 0.55, "If a mob is attacked and the attack roll is greater than his defense roll, the attack rating is multiplied by this value")
RULE_REAL(Combat, ACthac20Factor, 0.55, "If a mob is attacked and his defense roll is greater than the attack roll, the attack rating is multiplied by this value")
RULE_INT(Combat, HitCapPre20, 40, "Hit cap before level 20. Live has it capped at 40")
RULE_INT(Combat, HitCapPre10, 20, "Hit cap before level 10. Live has it capped at 20")
RULE_INT(Combat, MinHastedDelay, 400, "Minimum hasted combat delay")
RULE_REAL(Combat, AvgDefProcsPerMinute, 2.0, "Average defense procs per minute")
RULE_REAL(Combat, DefProcPerMinAgiContrib, 0.075, "How much agility contributes to defensive proc rate")
RULE_INT(Combat, SpecialAttackACBonus, 15, "Percent amount of damage per AC gained for certain special attacks (damage = AC*SpecialAttackACBonus/100)")
RULE_INT(Combat, NPCFlurryChance, 20, "Chance for NPC to flurry")
RULE_BOOL(Combat, TauntOverLevel, 1, "Allows you to taunt NPC's over warriors level")
RULE_REAL(Combat, TauntSkillFalloff, 0.33, "For every taunt skill point that's not maxed you lose this percentage chance to taunt")
RULE_BOOL(Combat, EXPFromDmgShield, false, "Determine if damage from a damage shield counts for experience gain")
RULE_INT(Combat, MonkACBonusWeight, 15, "Usually, a monk under this weight threshold gets an AC bonus")
RULE_INT(Combat, QuiverHasteCap, 1000, "Quiver haste cap 1000 on live for a while, currently 700 on live")
RULE_INT(Combat, BerserkerFrenzyStart, 35, "Percentage Health Points below which Warrior and Berserker start frenzy")
RULE_INT(Combat, BerserkerFrenzyEnd, 45, "Percentage Health Points above which Warrior and Berserker end frenzy")
@@ -518,6 +480,10 @@ RULE_BOOL(Combat, Classic2HBAnimation, false, "2HB will use the 2 hand piercing
RULE_BOOL(Combat, ArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption")
RULE_BOOL(Combat, ThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption")
RULE_BOOL(Combat, UseLiveRiposteMechanics, false, "Set to true to disable SPA 173 SE_RiposteChance from making those with the effect on them immune to enrage, can longer riposte from a riposte.")
RULE_INT(Combat, FrontalStunImmunityClasses, 0, "Bitmask for Classes than have frontal stun immunity, No Races (0) by default.")
RULE_BOOL(Combat, NPCsUseFrontalStunImmunityClasses, false, "Enable or disable NPCs using frontal stun immunity Classes from Combat:FrontalStunImmunityClasses, false by default.")
RULE_INT(Combat, FrontalStunImmunityRaces, 512, "Bitmask for Races than have frontal stun immunity, Ogre (512) only by default.")
RULE_BOOL(Combat, NPCsUseFrontalStunImmunityRaces, true, "Enable or disable NPCs using frontal stun immunity Races from Combat:FrontalStunImmunityRaces, true by default.")
RULE_CATEGORY_END()
RULE_CATEGORY(NPC)
@@ -550,6 +516,7 @@ RULE_BOOL(NPC, UseMeditateBasedManaRegen, false, "Based NPC ooc regen on Meditat
RULE_REAL(NPC, NPCHealOnGateAmount, 25, "How much the NPC will heal on gate if enabled")
RULE_BOOL(NPC, AnimalsOpenDoors, true, "Determines or not whether animals open doors or not when they approach them")
RULE_INT(NPC, MaxRaceID, 732, "Maximum Race ID, RoF2 by default supports up to 732")
RULE_BOOL(NPC, DisableLastNames, false, "Enable to disable NPC Last Names")
RULE_CATEGORY_END()
RULE_CATEGORY(Aggro)
@@ -839,6 +806,18 @@ RULE_CATEGORY(Doors)
RULE_BOOL(Doors, RequireKeyOnCursor, false, "Enable this to require pre-keyring keys to be on player cursor to open doors.")
RULE_CATEGORY_END()
RULE_CATEGORY(Items)
RULE_BOOL(Items, DisableAttuneable, false, "Enable this to disable Attuneable Items")
RULE_BOOL(Items, DisableBardFocusEffects, false, "Enable this to disable Bard Focus Effects on Items")
RULE_BOOL(Items, DisableLore, false, "Enable this to disable Lore Items")
RULE_BOOL(Items, DisableNoDrop, false, "Enable this to disable No Drop Items")
RULE_BOOL(Items, DisableNoPet, false, "Enable this to disable No Pet Items")
RULE_BOOL(Items, DisableNoRent, false, "Enable this to disable No Rent Items")
RULE_BOOL(Items, DisableNoTransfer, false, "Enable this to disable No Transfer Items")
RULE_BOOL(Items, DisablePotionBelt, false, "Enable this to disable Potion Belt Items")
RULE_BOOL(Items, DisableSpellFocusEffects, false, "Enable this to disable Spell Focus Effects on Items")
RULE_CATEGORY_END()
#undef RULE_CATEGORY
#undef RULE_INT
#undef RULE_REAL
+59 -31
View File
@@ -83,17 +83,15 @@
#define ServerOP_UpdateSpawn 0x003f
#define ServerOP_SpawnStatusChange 0x0040
#define ServerOP_DropClient 0x0041 // DropClient
#define ServerOP_ReloadTasks 0x0060
#define ServerOP_DepopAllPlayersCorpses 0x0061
#define ServerOP_ReloadTitles 0x0062
#define ServerOP_QGlobalUpdate 0x0063
#define ServerOP_QGlobalDelete 0x0064
#define ServerOP_DepopPlayerCorpse 0x0065
#define ServerOP_RequestTellQueue 0x0066 // client asks for it's tell queues
#define ServerOP_ChangeSharedMem 0x0067
#define ServerOP_WebInterfaceEvent 0x0068
#define ServerOP_WebInterfaceSubscribe 0x0069
#define ServerOP_WebInterfaceUnsubscribe 0x0070
#define ServerOP_DepopAllPlayersCorpses 0x0060
#define ServerOP_QGlobalUpdate 0x0061
#define ServerOP_QGlobalDelete 0x0062
#define ServerOP_DepopPlayerCorpse 0x0063
#define ServerOP_RequestTellQueue 0x0064 // client asks for it's tell queues
#define ServerOP_ChangeSharedMem 0x0065
#define ServerOP_WebInterfaceEvent 0x0066
#define ServerOP_WebInterfaceSubscribe 0x0067
#define ServerOP_WebInterfaceUnsubscribe 0x0068
#define ServerOP_RaidAdd 0x0100 //in use
#define ServerOP_RaidRemove 0x0101 //in use
@@ -213,21 +211,37 @@
#define ServerOP_LauncherZoneStatus 0x3002
#define ServerOP_DoZoneCommand 0x3003
#define ServerOP_UCSMessage 0x4000
#define ServerOP_UCSMailMessage 0x4001
#define ServerOP_ReloadRules 0x4002
#define ServerOP_ReloadRulesWorld 0x4003
#define ServerOP_CameraShake 0x4004
#define ServerOP_QueryServGeneric 0x4005
#define ServerOP_ReloadWorld 0x4006
#define ServerOP_ReloadLogs 0x4007
#define ServerOP_ReloadPerlExportSettings 0x4008
#define ServerOP_UCSServerStatusRequest 0x4009
#define ServerOP_UCSServerStatusReply 0x4010
#define ServerOP_HotReloadQuests 0x4011
#define ServerOP_UpdateSchedulerEvents 0x4012
#define ServerOP_ReloadContentFlags 0x4013
#define ServerOP_ReloadVariablesWorld 0x4014
#define ServerOP_CameraShake 0x4000
#define ServerOP_HotReloadQuests 0x4001
#define ServerOP_QueryServGeneric 0x4002
#define ServerOP_UCSMailMessage 0x4003
#define ServerOP_UCSMessage 0x4004
#define ServerOP_UCSServerStatusReply 0x4005
#define ServerOP_UCSServerStatusRequest 0x4006
#define ServerOP_UpdateSchedulerEvents 0x4007
#define ServerOP_DiscordWebhookMessage 0x4008
#define ServerOP_ReloadAAData 0x4100
#define ServerOP_ReloadAlternateCurrencies 0x4101
#define ServerOP_ReloadBlockedSpells 0x4102
#define ServerOP_ReloadContentFlags 0x4103
#define ServerOP_ReloadDoors 0x4104
#define ServerOP_ReloadGroundSpawns 0x4105
#define ServerOP_ReloadLevelEXPMods 0x4106
#define ServerOP_ReloadLogs 0x4107
#define ServerOP_ReloadMerchants 0x4108
#define ServerOP_ReloadNPCEmotes 0x4109
#define ServerOP_ReloadObjects 0x4110
#define ServerOP_ReloadPerlExportSettings 0x4111
#define ServerOP_ReloadRules 0x4112
#define ServerOP_ReloadStaticZoneData 0x4113
#define ServerOP_ReloadTasks 0x4114
#define ServerOP_ReloadTitles 0x4115
#define ServerOP_ReloadTraps 0x4116
#define ServerOP_ReloadVariables 0x4117
#define ServerOP_ReloadVeteranRewards 0x4118
#define ServerOP_ReloadWorld 0x4119
#define ServerOP_ReloadZonePoints 0x4120
#define ServerOP_CZDialogueWindow 0x4500
#define ServerOP_CZLDoNUpdate 0x4501
@@ -727,8 +741,8 @@ struct ServerMultiLineMsg_Struct {
};
struct ServerLock_Struct {
char myname[64]; // User that did it
uint8 mode; // 0 = Unlocked ; 1 = Locked
char character_name[64];
bool is_locked;
};
struct ServerMotd_Struct {
@@ -1006,8 +1020,8 @@ struct ServerOP_Consent_Struct {
};
struct ReloadTasks_Struct {
uint32 Command;
uint32 Parameter;
uint8 reload_type;
uint32 task_id;
};
struct ServerDepopAllPlayersCorpses_Struct
@@ -1436,6 +1450,11 @@ struct QSMerchantLogTransaction_Struct {
QSTransactionItems_Struct items[0];
};
struct DiscordWebhookMessage_Struct {
uint32 webhook_id;
char message[2000];
};
struct QSGeneralQuery_Struct {
char QueryString[0];
};
@@ -1600,7 +1619,7 @@ struct WWTaskUpdate_Struct {
};
struct ReloadWorld_Struct {
uint32 Option;
uint8 global_repop;
};
struct HotReloadQuestsStruct {
@@ -1770,6 +1789,15 @@ struct ServerDzCreateSerialized_Struct {
char cereal_data[0];
};
struct ServerFlagUpdate_Struct {
uint32 account_id;
int16 admin;
};
struct ServerOOCMute_Struct {
bool is_muted;
};
#pragma pack()
#endif
+295 -225
View File
@@ -38,6 +38,7 @@
#include "shareddb.h"
#include "string_util.h"
#include "eqemu_config.h"
#include "data_verification.h"
#include "repositories/criteria/content_filter_criteria.h"
namespace ItemField
@@ -634,18 +635,18 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
uint32 aug[EQ::invaug::SOCKET_COUNT];
aug[0] = (uint32)atoul(row[4]);
aug[1] = (uint32)atoul(row[5]);
aug[2] = (uint32)atoul(row[6]);
aug[3] = (uint32)atoul(row[7]);
aug[4] = (uint32)atoul(row[8]);
aug[5] = (uint32)atoul(row[9]);
aug[0] = std::stoul(row[4]);
aug[1] = std::stoul(row[5]);
aug[2] = std::stoul(row[6]);
aug[3] = std::stoul(row[7]);
aug[4] = std::stoul(row[8]);
aug[5] = std::stoul(row[9]);
bool instnodrop = (row[10] && (uint16)atoi(row[10])) ? true : false;
uint32 ornament_icon = (uint32)atoul(row[12]);
uint32 ornament_idfile = (uint32)atoul(row[13]);
uint32 ornament_hero_model = (uint32)atoul(row[14]);
uint32 ornament_icon = std::stoul(row[12]);
uint32 ornament_idfile = std::stoul(row[13]);
uint32 ornament_hero_model = std::stoul(row[14]);
const EQ::ItemData *item = GetItem(item_id);
@@ -788,9 +789,9 @@ bool SharedDatabase::GetInventory(uint32 account_id, char *name, EQ::InventoryPr
aug[5] = (uint32)atoi(row[9]);
bool instnodrop = (row[10] && (uint16)atoi(row[10])) ? true : false;
uint32 ornament_icon = (uint32)atoul(row[12]);
uint32 ornament_idfile = (uint32)atoul(row[13]);
uint32 ornament_hero_model = (uint32)atoul(row[14]);
uint32 ornament_icon = std::stoul(row[12]);
uint32 ornament_idfile = std::stoul(row[13]);
uint32 ornament_hero_model = std::stoul(row[14]);
const EQ::ItemData *item = GetItem(item_id);
int16 put_slot_id = INVALID_INDEX;
@@ -943,29 +944,41 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_
{
EQ::FixedMemoryHashSet<EQ::ItemData> hash(reinterpret_cast<uint8 *>(data), size, items, max_item_id);
std::string ndbuffer;
bool disableNoRent = false;
if (GetVariable("disablenorent", ndbuffer)) {
if (ndbuffer[0] == '1' && ndbuffer[1] == '\0') {
disableNoRent = true;
std::string variable_buffer;
bool disable_attuneable = RuleB(Items, DisableAttuneable);
bool disable_bard_focus_effects = RuleB(Items, DisableBardFocusEffects);
bool disable_lore = RuleB(Items, DisableLore);
bool disable_no_drop = RuleB(Items, DisableNoDrop);
bool disable_no_pet = RuleB(Items, DisableNoPet);
bool disable_no_rent = RuleB(Items, DisableNoRent);
bool disable_no_transfer = RuleB(Items, DisableNoTransfer);
bool disable_potion_belt = RuleB(Items, DisablePotionBelt);
bool disable_spell_focus_effects = RuleB(Items, DisableSpellFocusEffects);
// Old Variable Code
if (GetVariable("disablelore", variable_buffer)) {
if (variable_buffer == "1") {
disable_lore = true;
}
}
bool disableNoDrop = false;
if (GetVariable("disablenodrop", ndbuffer)) {
if (ndbuffer[0] == '1' && ndbuffer[1] == '\0') {
disableNoDrop = true;
if (GetVariable("disablenodrop", variable_buffer)) {
if (variable_buffer == "1") {
disable_no_drop = true;
}
}
bool disableLoreGroup = false;
if (GetVariable("disablelore", ndbuffer)) {
if (ndbuffer[0] == '1' && ndbuffer[1] == '\0') {
disableLoreGroup = true;
if (GetVariable("disablenorent", variable_buffer)) {
if (variable_buffer == "1") {
disable_no_rent = true;
}
}
bool disableNoTransfer = false;
if (GetVariable("disablenotransfer", ndbuffer)) {
if (ndbuffer[0] == '1' && ndbuffer[1] == '\0') {
disableNoTransfer = true;
if (GetVariable("disablenotransfer", variable_buffer)) {
if (variable_buffer == "1") {
disable_no_transfer = true;
}
}
@@ -984,212 +997,269 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_
for (auto row = results.begin(); row != results.end(); ++row) {
memset(&item, 0, sizeof(EQ::ItemData));
item.ItemClass = (uint8)atoi(row[ItemField::itemclass]);
strcpy(item.Name, row[ItemField::name]);
strcpy(item.Lore, row[ItemField::lore]);
strcpy(item.IDFile, row[ItemField::idfile]);
// Unique Identifier
item.ID = std::stoul(row[ItemField::id]);
item.ID = (uint32)atoul(row[ItemField::id]);
item.Weight = (int32)atoi(row[ItemField::weight]);
item.NoRent = disableNoRent ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::norent]);
item.NoDrop = disableNoDrop ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::nodrop]);
item.Size = (uint8)atoi(row[ItemField::size]);
item.Slots = (uint32)atoul(row[ItemField::slots]);
item.Price = (uint32)atoul(row[ItemField::price]);
item.Icon = (uint32)atoul(row[ItemField::icon]);
item.BenefitFlag = (atoul(row[ItemField::benefitflag]) != 0);
item.Tradeskills = (atoi(row[ItemField::tradeskills]) == 0) ? false : true;
item.CR = (int8)atoi(row[ItemField::cr]);
item.DR = (int8)atoi(row[ItemField::dr]);
item.PR = (int8)atoi(row[ItemField::pr]);
item.MR = (int8)atoi(row[ItemField::mr]);
item.FR = (int8)atoi(row[ItemField::fr]);
item.AStr = (int8)atoi(row[ItemField::astr]);
item.ASta = (int8)atoi(row[ItemField::asta]);
item.AAgi = (int8)atoi(row[ItemField::aagi]);
item.ADex = (int8)atoi(row[ItemField::adex]);
item.ACha = (int8)atoi(row[ItemField::acha]);
item.AInt = (int8)atoi(row[ItemField::aint]);
item.AWis = (int8)atoi(row[ItemField::awis]);
item.HP = (int32)atoul(row[ItemField::hp]);
item.Mana = (int32)atoul(row[ItemField::mana]);
item.AC = (int32)atoul(row[ItemField::ac]);
item.Deity = (uint32)atoul(row[ItemField::deity]);
item.SkillModValue = (int32)atoul(row[ItemField::skillmodvalue]);
item.SkillModMax = (int32)atoul(row[ItemField::skillmodmax]);
item.SkillModType = (uint32)atoul(row[ItemField::skillmodtype]);
item.BaneDmgRace = (uint32)atoul(row[ItemField::banedmgrace]);
item.BaneDmgAmt = (int32)atoul(row[ItemField::banedmgamt]);
item.BaneDmgBody = (uint32)atoul(row[ItemField::banedmgbody]);
item.Magic = (atoi(row[ItemField::magic]) == 0) ? false : true;
item.CastTime_ = (int32)atoul(row[ItemField::casttime_]);
item.ReqLevel = (uint8)atoi(row[ItemField::reqlevel]);
item.BardType = (uint32)atoul(row[ItemField::bardtype]);
item.BardValue = (int32)atoul(row[ItemField::bardvalue]);
item.Light = (int8)atoi(row[ItemField::light]);
item.Delay = (uint8)atoi(row[ItemField::delay]);
item.RecLevel = (uint8)atoi(row[ItemField::reclevel]);
item.RecSkill = (uint8)atoi(row[ItemField::recskill]);
item.ElemDmgType = (uint8)atoi(row[ItemField::elemdmgtype]);
item.ElemDmgAmt = (uint8)atoi(row[ItemField::elemdmgamt]);
item.Range = (uint8)atoi(row[ItemField::range]);
item.Damage = (uint32)atoi(row[ItemField::damage]);
item.Color = (uint32)atoul(row[ItemField::color]);
item.Classes = (uint32)atoul(row[ItemField::classes]);
item.Races = (uint32)atoul(row[ItemField::races]);
// Name and Lore
strn0cpy(item.Name, row[ItemField::name], sizeof(item.Name));
strn0cpy(item.Lore, row[ItemField::lore], sizeof(item.Lore));
item.MaxCharges = (int16)atoi(row[ItemField::maxcharges]);
item.ItemType = (uint8)atoi(row[ItemField::itemtype]);
item.SubType = atoi(row[ItemField::subtype]);
item.Material = (uint8)atoi(row[ItemField::material]);
item.HerosForgeModel = (uint32)atoi(row[ItemField::herosforgemodel]);
item.SellRate = (float)atof(row[ItemField::sellrate]);
item.CastTime = (uint32)atoul(row[ItemField::casttime]);
item.EliteMaterial = (uint32)atoul(row[ItemField::elitematerial]);
item.ProcRate = (int32)atoi(row[ItemField::procrate]);
item.CombatEffects = (int8)atoi(row[ItemField::combateffects]);
item.Shielding = (int8)atoi(row[ItemField::shielding]);
item.StunResist = (int8)atoi(row[ItemField::stunresist]);
item.StrikeThrough = (int8)atoi(row[ItemField::strikethrough]);
item.ExtraDmgSkill = (uint32)atoul(row[ItemField::extradmgskill]);
item.ExtraDmgAmt = (uint32)atoul(row[ItemField::extradmgamt]);
item.SpellShield = (int8)atoi(row[ItemField::spellshield]);
item.Avoidance = (int8)atoi(row[ItemField::avoidance]);
item.Accuracy = (int8)atoi(row[ItemField::accuracy]);
item.CharmFileID = (uint32)atoul(row[ItemField::charmfileid]);
item.FactionMod1 = (int32)atoul(row[ItemField::factionmod1]);
item.FactionMod2 = (int32)atoul(row[ItemField::factionmod2]);
item.FactionMod3 = (int32)atoul(row[ItemField::factionmod3]);
item.FactionMod4 = (int32)atoul(row[ItemField::factionmod4]);
item.FactionAmt1 = (int32)atoul(row[ItemField::factionamt1]);
item.FactionAmt2 = (int32)atoul(row[ItemField::factionamt2]);
item.FactionAmt3 = (int32)atoul(row[ItemField::factionamt3]);
item.FactionAmt4 = (int32)atoul(row[ItemField::factionamt4]);
// Flags
item.ArtifactFlag = std::stoi(row[ItemField::artifactflag]) ? true : false;
item.Attuneable = disable_attuneable ? false : std::stoi(row[ItemField::attuneable]) ? true : false;
item.BenefitFlag = std::stoi(row[ItemField::benefitflag]) ? true : false;
item.FVNoDrop = std::stoi(row[ItemField::fvnodrop]) ? true : false;
item.Magic = std::stoi(row[ItemField::magic]) ? true : false;
item.NoDrop = disable_no_drop ? static_cast<uint8>(255) : static_cast<uint8>(std::stoul(row[ItemField::nodrop]));
item.NoPet = disable_no_pet ? false : std::stoi(row[ItemField::nopet]) ? true : false;
item.NoRent = disable_no_rent ? static_cast<uint8>(255) : static_cast<uint8>(std::stoul(row[ItemField::norent]));
item.NoTransfer = disable_no_transfer ? false : std::stoi(row[ItemField::notransfer]) ? true : false;
item.PendingLoreFlag = std::stoi(row[ItemField::pendingloreflag]) ? true : false;
item.QuestItemFlag = std::stoi(row[ItemField::questitemflag]) ? true : false;
item.Stackable = std::stoi(row[ItemField::stackable]) ? true : false;
item.Tradeskills = std::stoi(row[ItemField::tradeskills]) ? true : false;
item.SummonedFlag = std::stoi(row[ItemField::summonedflag]) ? true : false;
strcpy(item.CharmFile, row[ItemField::charmfile]);
// Lore
item.LoreGroup = disable_lore ? 0 : std::stoi(row[ItemField::loregroup]);
item.LoreFlag = disable_lore ? false : item.LoreGroup != 0;
item.AugType = (uint32)atoul(row[ItemField::augtype]);
item.AugSlotType[0] = (uint8)atoi(row[ItemField::augslot1type]);
item.AugSlotVisible[0] = (uint8)atoi(row[ItemField::augslot1visible]);
item.AugSlotUnk2[0] = 0;
item.AugSlotType[1] = (uint8)atoi(row[ItemField::augslot2type]);
item.AugSlotVisible[1] = (uint8)atoi(row[ItemField::augslot2visible]);
item.AugSlotUnk2[1] = 0;
item.AugSlotType[2] = (uint8)atoi(row[ItemField::augslot3type]);
item.AugSlotVisible[2] = (uint8)atoi(row[ItemField::augslot3visible]);
item.AugSlotUnk2[2] = 0;
item.AugSlotType[3] = (uint8)atoi(row[ItemField::augslot4type]);
item.AugSlotVisible[3] = (uint8)atoi(row[ItemField::augslot4visible]);
item.AugSlotUnk2[3] = 0;
item.AugSlotType[4] = (uint8)atoi(row[ItemField::augslot5type]);
item.AugSlotVisible[4] = (uint8)atoi(row[ItemField::augslot5visible]);
item.AugSlotUnk2[4] = 0;
item.AugSlotType[5] = (uint8)atoi(row[ItemField::augslot6type]);
item.AugSlotVisible[5] = (uint8)atoi(row[ItemField::augslot6visible]);
item.AugSlotUnk2[5] = 0;
// Type
item.AugType = std::stoul(row[ItemField::augtype]);
item.ItemType = static_cast<uint8>(std::stoul(row[ItemField::itemtype]));
item.SubType = std::stoi(row[ItemField::subtype]);
item.LDoNTheme = (uint32)atoul(row[ItemField::ldontheme]);
item.LDoNPrice = (uint32)atoul(row[ItemField::ldonprice]);
item.LDoNSold = (uint32)atoul(row[ItemField::ldonsold]);
item.BagType = (uint8)atoi(row[ItemField::bagtype]);
item.BagSlots = (uint8)std::min(atoi(row[ItemField::bagslots]), 10); // FIXME: remove when big bags supported
item.BagSize = (uint8)atoi(row[ItemField::bagsize]);
item.BagWR = (uint8)atoi(row[ItemField::bagwr]);
item.Book = (uint8)atoi(row[ItemField::book]);
item.BookType = (uint32)atoul(row[ItemField::booktype]);
// Miscellaneous
item.ExpendableArrow = static_cast<uint16>(std::stoul(row[ItemField::expendablearrow]));
item.Light = static_cast<int8>(std::stoi(row[ItemField::light]));
item.MaxCharges = static_cast<int16>(std::stoi(row[ItemField::maxcharges]));
item.Size = static_cast<uint8>(std::stoul(row[ItemField::size]));
item.StackSize = static_cast<int16>(std::stoi(row[ItemField::stacksize]));
item.Weight = std::stoi(row[ItemField::weight]);
strcpy(item.Filename, row[ItemField::filename]);
// Potion Belt
item.PotionBelt = disable_potion_belt ? false : std::stoi(row[ItemField::potionbelt]) ? true : false;
item.PotionBeltSlots = disable_potion_belt ? 0 : static_cast<uint8>(std::stoul(row[ItemField::potionbeltslots]));
item.BaneDmgRaceAmt = (uint32)atoul(row[ItemField::banedmgraceamt]);
item.AugRestrict = (uint32)atoul(row[ItemField::augrestrict]);
item.LoreGroup = disableLoreGroup ? (uint8)atoi("0") : atoi(row[ItemField::loregroup]);
item.LoreFlag = item.LoreGroup != 0;
item.PendingLoreFlag = (atoi(row[ItemField::pendingloreflag]) == 0) ? false : true;
item.ArtifactFlag = (atoi(row[ItemField::artifactflag]) == 0) ? false : true;
item.SummonedFlag = (atoi(row[ItemField::summonedflag]) == 0) ? false : true;
item.Favor = (uint32)atoul(row[ItemField::favor]);
item.FVNoDrop = (atoi(row[ItemField::fvnodrop]) == 0) ? false : true;
item.Endur = (uint32)atoul(row[ItemField::endur]);
item.DotShielding = (uint32)atoul(row[ItemField::dotshielding]);
item.Attack = (uint32)atoul(row[ItemField::attack]);
item.Regen = (uint32)atoul(row[ItemField::regen]);
item.ManaRegen = (uint32)atoul(row[ItemField::manaregen]);
item.EnduranceRegen = (uint32)atoul(row[ItemField::enduranceregen]);
item.Haste = (uint32)atoul(row[ItemField::haste]);
item.DamageShield = (uint32)atoul(row[ItemField::damageshield]);
item.RecastDelay = (uint32)atoul(row[ItemField::recastdelay]);
item.RecastType = (int)atoi(row[ItemField::recasttype]);
item.GuildFavor = (uint32)atoul(row[ItemField::guildfavor]);
item.AugDistiller = (uint32)atoul(row[ItemField::augdistiller]);
item.Attuneable = (atoi(row[ItemField::attuneable]) == 0) ? false : true;
item.NoPet = (atoi(row[ItemField::nopet]) == 0) ? false : true;
item.PointType = (uint32)atoul(row[ItemField::pointtype]);
item.PotionBelt = (atoi(row[ItemField::potionbelt]) == 0) ? false : true;
item.PotionBeltSlots = (atoi(row[ItemField::potionbeltslots]) == 0) ? false : true;
item.StackSize = (uint16)atoi(row[ItemField::stacksize]);
item.NoTransfer = disableNoTransfer ? false : (atoi(row[ItemField::notransfer]) == 0) ? false : true;
item.Stackable = (atoi(row[ItemField::stackable]) == 0) ? false : true;
item.Click.Effect = (uint32)atoul(row[ItemField::clickeffect]);
item.Click.Type = (uint8)atoul(row[ItemField::clicktype]);
item.Click.Level = (uint8)atoul(row[ItemField::clicklevel]);
item.Click.Level2 = (uint8)atoul(row[ItemField::clicklevel2]);
// Merchant
item.Favor = std::stoul(row[ItemField::favor]);
item.GuildFavor = std::stoul(row[ItemField::guildfavor]);
item.Price = std::stoul(row[ItemField::price]);
item.SellRate = std::stof(row[ItemField::sellrate]);
// Display
item.Color = std::stoul(row[ItemField::color]);
item.EliteMaterial = std::stoul(row[ItemField::elitematerial]);
item.HerosForgeModel = std::stoul(row[ItemField::herosforgemodel]);
item.Icon = std::stoul(row[ItemField::icon]);
strn0cpy(item.IDFile, row[ItemField::idfile], sizeof(item.IDFile));
item.Material = static_cast<uint8>(std::stoul(row[ItemField::material]));
strcpy(item.CharmFile, row[ItemField::charmfile]);
// Resists
item.CR = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::cr]), -128, 127));
item.DR = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::dr]), -128, 127));
item.FR = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::fr]), -128, 127));
item.MR = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::mr]), -128, 127));
item.PR = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::pr]), -128, 127));
item.SVCorruption = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::svcorruption]), -128, 127));
item.Proc.Effect = (int32)atoul(row[ItemField::proceffect]);
item.Proc.Type = (uint8)atoul(row[ItemField::proctype]);
item.Proc.Level = (uint8)atoul(row[ItemField::proclevel]);
item.Proc.Level2 = (uint8)atoul(row[ItemField::proclevel2]);
item.Worn.Effect = (int32)atoul(row[ItemField::worneffect]);
item.Worn.Type = (uint8)atoul(row[ItemField::worntype]);
item.Worn.Level = (uint8)atoul(row[ItemField::wornlevel]);
item.Worn.Level2 = (uint8)atoul(row[ItemField::wornlevel2]);
item.Focus.Effect = (int32)atoul(row[ItemField::focuseffect]);
item.Focus.Type = (uint8)atoul(row[ItemField::focustype]);
item.Focus.Level = (uint8)atoul(row[ItemField::focuslevel]);
item.Focus.Level2 = (uint8)atoul(row[ItemField::focuslevel2]);
item.Scroll.Effect = (int32)atoul(row[ItemField::scrolleffect]);
item.Scroll.Type = (uint8)atoul(row[ItemField::scrolltype]);
item.Scroll.Level = (uint8)atoul(row[ItemField::scrolllevel]);
item.Scroll.Level2 = (uint8)atoul(row[ItemField::scrolllevel2]);
item.Bard.Effect = (int32)atoul(row[ItemField::bardeffect]);
item.Bard.Type = (uint8)atoul(row[ItemField::bardtype]);
item.Bard.Level = (uint8)atoul(row[ItemField::bardlevel]);
item.Bard.Level2 = (uint8)atoul(row[ItemField::bardlevel2]);
item.QuestItemFlag = (atoi(row[ItemField::questitemflag]) == 0) ? false : true;
item.SVCorruption = (int32)atoi(row[ItemField::svcorruption]);
item.Purity = (uint32)atoul(row[ItemField::purity]);
item.EvolvingItem = (uint8)atoul(row[ItemField::evoitem]);
item.EvolvingID = (uint8)atoul(row[ItemField::evoid]);
item.EvolvingLevel = (uint8)atoul(row[ItemField::evolvinglevel]);
item.EvolvingMax = (uint8)atoul(row[ItemField::evomax]);
item.BackstabDmg = (uint32)atoul(row[ItemField::backstabdmg]);
item.DSMitigation = (uint32)atoul(row[ItemField::dsmitigation]);
item.HeroicStr = (int32)atoi(row[ItemField::heroic_str]);
item.HeroicInt = (int32)atoi(row[ItemField::heroic_int]);
item.HeroicWis = (int32)atoi(row[ItemField::heroic_wis]);
item.HeroicAgi = (int32)atoi(row[ItemField::heroic_agi]);
item.HeroicDex = (int32)atoi(row[ItemField::heroic_dex]);
item.HeroicSta = (int32)atoi(row[ItemField::heroic_sta]);
item.HeroicCha = (int32)atoi(row[ItemField::heroic_cha]);
item.HeroicMR = (int32)atoi(row[ItemField::heroic_mr]);
item.HeroicFR = (int32)atoi(row[ItemField::heroic_fr]);
item.HeroicCR = (int32)atoi(row[ItemField::heroic_cr]);
item.HeroicDR = (int32)atoi(row[ItemField::heroic_dr]);
item.HeroicPR = (int32)atoi(row[ItemField::heroic_pr]);
item.HeroicSVCorrup = (int32)atoi(row[ItemField::heroic_svcorrup]);
item.HealAmt = (int32)atoi(row[ItemField::healamt]);
item.SpellDmg = (int32)atoi(row[ItemField::spelldmg]);
item.LDoNSellBackRate = (uint32)atoul(row[ItemField::ldonsellbackrate]);
item.ScriptFileID = (uint32)atoul(row[ItemField::scriptfileid]);
item.ExpendableArrow = (uint16)atoul(row[ItemField::expendablearrow]);
item.Clairvoyance = (uint32)atoul(row[ItemField::clairvoyance]);
// Heroic Resists
item.HeroicCR = std::stoi(row[ItemField::heroic_cr]);
item.HeroicDR = std::stoi(row[ItemField::heroic_dr]);
item.HeroicFR = std::stoi(row[ItemField::heroic_fr]);
item.HeroicMR = std::stoi(row[ItemField::heroic_mr]);
item.HeroicPR = std::stoi(row[ItemField::heroic_pr]);
item.HeroicSVCorrup = std::stoi(row[ItemField::heroic_svcorrup]);
strcpy(item.ClickName, row[ItemField::clickname]);
strcpy(item.ProcName, row[ItemField::procname]);
strcpy(item.WornName, row[ItemField::wornname]);
strcpy(item.FocusName, row[ItemField::focusname]);
strcpy(item.ScrollName, row[ItemField::scrollname]);
// Stats
item.AAgi = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::aagi]), -128, 127));
item.ACha = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::acha]), -128, 127));
item.ADex = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::adex]), -128, 127));
item.AInt = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::aint]), -128, 127));
item.ASta = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::asta]), -128, 127));
item.AStr = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::astr]), -128, 127));
item.AWis = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::awis]), -128, 127));
// Heroic Stats
item.HeroicAgi = std::stoi(row[ItemField::heroic_agi]);
item.HeroicCha = std::stoi(row[ItemField::heroic_cha]);
item.HeroicDex = std::stoi(row[ItemField::heroic_dex]);
item.HeroicInt = std::stoi(row[ItemField::heroic_int]);
item.HeroicSta = std::stoi(row[ItemField::heroic_sta]);
item.HeroicStr = std::stoi(row[ItemField::heroic_str]);
item.HeroicWis = std::stoi(row[ItemField::heroic_wis]);
// Health, Mana, and Endurance
item.HP = std::stoi(row[ItemField::hp]);
item.Regen = std::stoul(row[ItemField::regen]);
item.Mana = std::stoi(row[ItemField::mana]);
item.ManaRegen = std::stoul(row[ItemField::manaregen]);
item.Endur = std::stoul(row[ItemField::endur]);
item.EnduranceRegen = std::stoul(row[ItemField::enduranceregen]);
// Bane Damage
item.BaneDmgAmt = std::stoi(row[ItemField::banedmgamt]);
item.BaneDmgBody = std::stoul(row[ItemField::banedmgbody]);
item.BaneDmgRace = std::stoul(row[ItemField::banedmgrace]);
item.BaneDmgRaceAmt = std::stoul(row[ItemField::banedmgraceamt]);
// Elemental Damage
item.ElemDmgType = static_cast<uint8>(std::stoul(row[ItemField::elemdmgtype]));
item.ElemDmgAmt = static_cast<uint8>(std::stoul(row[ItemField::elemdmgamt]));
// Combat
item.BackstabDmg = std::stoul(row[ItemField::backstabdmg]);
item.Damage = std::stoul(row[ItemField::damage]);
item.Delay = static_cast<uint8>(std::stoul(row[ItemField::delay]));
item.Range = static_cast<uint8>(std::stoul(row[ItemField::range]));
// Combat Stats
item.AC = std::stoi(row[ItemField::ac]);
item.Accuracy = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::accuracy]), -128, 127));
item.Attack = std::stoi(row[ItemField::attack]);
item.Avoidance = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::avoidance]), -128, 127));
item.Clairvoyance = std::stoul(row[ItemField::clairvoyance]);
item.CombatEffects = StringIsNumber(row[ItemField::combateffects]) ? static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::combateffects]), -128, 127)) : 0;
item.DamageShield = std::stoi(row[ItemField::damageshield]);
item.DotShielding = std::stoi(row[ItemField::dotshielding]);
item.DSMitigation = std::stoul(row[ItemField::dsmitigation]);
item.Haste = std::stoi(row[ItemField::haste]);
item.HealAmt = std::stoi(row[ItemField::healamt]);
item.Purity = std::stoul(row[ItemField::purity]);
item.Shielding = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::shielding]), -128, 127));
item.SpellDmg = std::stoi(row[ItemField::spelldmg]);
item.SpellShield = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::spellshield]), -128, 127));
item.StrikeThrough = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::strikethrough]), -128, 127));
item.StunResist = static_cast<int8>(EQ::Clamp(std::stoi(row[ItemField::stunresist]), -128, 127));
// Restrictions
item.AugRestrict = std::stoul(row[ItemField::augrestrict]);
item.Classes = std::stoul(row[ItemField::classes]);
item.Deity = std::stoul(row[ItemField::deity]);
item.ItemClass = static_cast<uint8>(std::stoul(row[ItemField::itemclass]));
item.Races = std::stoul(row[ItemField::races]);
item.RecLevel = static_cast<uint8>(std::stoul(row[ItemField::reclevel]));
item.RecSkill = static_cast<uint8>(std::stoul(row[ItemField::recskill]));
item.ReqLevel = static_cast<uint8>(std::stoul(row[ItemField::reqlevel]));
item.Slots = std::stoul(row[ItemField::slots]);
// Skill Modifier
item.SkillModValue = std::stoi(row[ItemField::skillmodvalue]);
item.SkillModMax = std::stoi(row[ItemField::skillmodmax]);
item.SkillModType = std::stoul(row[ItemField::skillmodtype]);
// Extra Damage Skill
item.ExtraDmgSkill = std::stoul(row[ItemField::extradmgskill]);
item.ExtraDmgAmt = std::stoul(row[ItemField::extradmgamt]);
// Bard
item.BardType = std::stoul(row[ItemField::bardtype]);
item.BardValue = std::stoi(row[ItemField::bardvalue]);
// Faction
item.FactionAmt1 = std::stoi(row[ItemField::factionamt1]);
item.FactionMod1 = std::stoi(row[ItemField::factionmod1]);
item.FactionAmt2 = std::stoi(row[ItemField::factionamt2]);
item.FactionMod2 = std::stoi(row[ItemField::factionmod2]);
item.FactionAmt3 = std::stoi(row[ItemField::factionamt3]);
item.FactionMod3 = std::stoi(row[ItemField::factionmod3]);
item.FactionAmt4 = std::stoi(row[ItemField::factionamt4]);
item.FactionMod4 = std::stoi(row[ItemField::factionmod4]);
// Augment
item.AugDistiller = std::stoul(row[ItemField::augdistiller]);
item.AugSlotType[0] = static_cast<uint8>(std::stoul(row[ItemField::augslot1type]));
item.AugSlotVisible[0] = static_cast<uint8>(std::stoul(row[ItemField::augslot1visible]));
item.AugSlotType[1] = static_cast<uint8>(std::stoul(row[ItemField::augslot2type]));
item.AugSlotVisible[1] = static_cast<uint8>(std::stoul(row[ItemField::augslot2visible]));
item.AugSlotType[2] = static_cast<uint8>(std::stoul(row[ItemField::augslot3type]));
item.AugSlotVisible[2] = static_cast<uint8>(std::stoul(row[ItemField::augslot3visible]));
item.AugSlotType[3] = static_cast<uint8>(std::stoul(row[ItemField::augslot4type]));
item.AugSlotVisible[3] = static_cast<uint8>(std::stoul(row[ItemField::augslot4visible]));
item.AugSlotType[4] = static_cast<uint8>(std::stoul(row[ItemField::augslot5type]));
item.AugSlotVisible[4] = static_cast<uint8>(std::stoul(row[ItemField::augslot5visible]));
item.AugSlotType[5] = static_cast<uint8>(std::stoul(row[ItemField::augslot6type]));
item.AugSlotVisible[5] = static_cast<uint8>(std::stoul(row[ItemField::augslot6visible]));
// Augment Unknowns
for (uint8 i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
item.AugSlotUnk2[i] = 0;
}
// LDoN
item.LDoNTheme = std::stoul(row[ItemField::ldontheme]);
item.LDoNPrice = std::stoul(row[ItemField::ldonprice]);
item.LDoNSellBackRate = std::stoul(row[ItemField::ldonsellbackrate]);
item.LDoNSold = std::stoul(row[ItemField::ldonsold]);
item.PointType = std::stoul(row[ItemField::pointtype]);
// Bag
item.BagSize = static_cast<uint8>(std::stoul(row[ItemField::bagsize]));
item.BagSlots = static_cast<uint8>(EQ::Clamp(std::stoi(row[ItemField::bagslots]), 0, 10)); // Will need to be changed from std::min to just use database value when bag slots are increased
item.BagType = static_cast<uint8>(std::stoul(row[ItemField::bagtype]));
item.BagWR = static_cast<uint8>(EQ::Clamp(std::stoi(row[ItemField::bagwr]), 0, 100));
// Bard Effect
item.Bard.Effect = disable_bard_focus_effects ? 0 : std::stoi(row[ItemField::bardeffect]);
item.Bard.Type = disable_bard_focus_effects ? 0 : static_cast<uint8>(std::stoul(row[ItemField::bardtype]));
item.Bard.Level = disable_bard_focus_effects ? 0 : static_cast<uint8>(std::stoul(row[ItemField::bardlevel]));
item.Bard.Level2 = disable_bard_focus_effects ? 0 : static_cast<uint8>(std::stoul(row[ItemField::bardlevel2]));
// Book
item.Book = static_cast<uint8>(std::stoul(row[ItemField::book]));
item.BookType = std::stoul(row[ItemField::booktype]);
// Click Effect
item.CastTime = std::stoul(row[ItemField::casttime]);
item.CastTime_ = std::stoi(row[ItemField::casttime_]);
item.Click.Effect = std::stoul(row[ItemField::clickeffect]);
item.Click.Type = static_cast<uint8>(std::stoul(row[ItemField::clicktype]));
item.Click.Level = static_cast<uint8>(std::stoul(row[ItemField::clicklevel]));
item.Click.Level2 = static_cast<uint8>(std::stoul(row[ItemField::clicklevel2]));
strn0cpy(item.ClickName, row[ItemField::clickname], sizeof(item.ClickName));
item.RecastDelay = std::stoul(row[ItemField::recastdelay]);
item.RecastType = std::stoi(row[ItemField::recasttype]);
// Focus Effect
item.Focus.Effect = disable_spell_focus_effects ? 0 : std::stoi(row[ItemField::focuseffect]);
item.Focus.Type = disable_spell_focus_effects ? 0 : static_cast<uint8>(std::stoul(row[ItemField::focustype]));
item.Focus.Level = disable_spell_focus_effects ? 0 : static_cast<uint8>(std::stoul(row[ItemField::focuslevel]));
item.Focus.Level2 = disable_spell_focus_effects ? 0 : static_cast<uint8>(std::stoul(row[ItemField::focuslevel2]));
strn0cpy(item.FocusName, disable_spell_focus_effects ? "" : row[ItemField::focusname], sizeof(item.FocusName));
// Proc Effect
item.Proc.Effect = std::stoi(row[ItemField::proceffect]);
item.Proc.Type = static_cast<uint8>(std::stoul(row[ItemField::proctype]));
item.Proc.Level = static_cast<uint8>(std::stoul(row[ItemField::proclevel]));
item.Proc.Level2 = static_cast<uint8>(std::stoul(row[ItemField::proclevel2]));
strn0cpy(item.ProcName, row[ItemField::procname], sizeof(item.ProcName));
item.ProcRate = std::stoi(row[ItemField::procrate]);
// Scroll Effect
item.Scroll.Effect = std::stoi(row[ItemField::scrolleffect]);
item.Scroll.Type = static_cast<uint8>(std::stoul(row[ItemField::scrolltype]));
item.Scroll.Level = static_cast<uint8>(std::stoul(row[ItemField::scrolllevel]));
item.Scroll.Level2 = static_cast<uint8>(std::stoul(row[ItemField::scrolllevel2]));
strn0cpy(item.ScrollName, row[ItemField::scrollname], sizeof(item.ScrollName));
// Worn Effect
item.Worn.Effect = std::stoi(row[ItemField::worneffect]);
item.Worn.Type = static_cast<uint8>(std::stoul(row[ItemField::worntype]));
item.Worn.Level = static_cast<uint8>(std::stoul(row[ItemField::wornlevel]));
item.Worn.Level2 = static_cast<uint8>(std::stoul(row[ItemField::wornlevel2]));
strn0cpy(item.WornName, row[ItemField::wornname], sizeof(item.WornName));
// Evolving Item
item.EvolvingID = std::stoul(row[ItemField::evoid]);
item.EvolvingItem = static_cast<uint8>(std::stoul(row[ItemField::evoitem]));
item.EvolvingLevel = static_cast<uint8>(std::stoul(row[ItemField::evolvinglevel]));
item.EvolvingMax = static_cast<uint8>(std::stoul(row[ItemField::evomax]));
// Scripting
item.CharmFileID = StringIsNumber(row[ItemField::charmfileid]) ? std::stoul(row[ItemField::charmfileid]) : 0;
strn0cpy(item.CharmFile, row[ItemField::charmfile], sizeof(item.CharmFile));
strn0cpy(item.Filename, row[ItemField::filename], sizeof(item.Filename));
item.ScriptFileID = std::stoul(row[ItemField::scriptfileid]);
try {
hash.insert(item.ID, item);
+12 -8
View File
@@ -101,14 +101,18 @@ bool IsSacrificeSpell(uint16 spell_id)
bool IsLifetapSpell(uint16 spell_id)
{
// Ancient Lifebane: 2115
if (IsValidSpell(spell_id) &&
(spells[spell_id].target_type == ST_Tap ||
spells[spell_id].target_type == ST_TargetAETap ||
spell_id == 2115))
return true;
if (
IsValidSpell(spell_id) &&
(
spells[spell_id].target_type == ST_Tap ||
spells[spell_id].target_type == ST_TargetAETap ||
spell_id == SPELL_ANCIENT_LIFEBANE
)
) {
return true;
}
return false;
return false;
}
bool IsMezSpell(uint16 spell_id)
@@ -139,7 +143,7 @@ bool IsEvacSpell(uint16 spellid)
bool IsDamageSpell(uint16 spellid)
{
if (spells[spellid].target_type == ST_Tap)
if (IsLifetapSpell(spellid))
return false;
for (int o = 0; o < EFFECT_COUNT; o++) {
+1
View File
@@ -169,6 +169,7 @@
#define SPELL_ILLUSION_FEMALE 1731
#define SPELL_ILLUSION_MALE 1732
#define SPELL_UNSUMMON_SELF 892
#define SPELL_ANCIENT_LIFEBANE 2115
//spellgroup ids
#define SPELLGROUP_FRENZIED_BURNOUT 2754
+48 -27
View File
@@ -575,9 +575,9 @@ char *RemoveApostrophes(const char *s)
return NewString;
}
const char *ConvertArray(int input, char *returnchar)
const char *ConvertArray(int64 input, char *returnchar)
{
sprintf(returnchar, "%i", input);
sprintf(returnchar, "%lld", input);
return returnchar;
}
@@ -1192,76 +1192,97 @@ std::string ConvertMoneyToString(uint32 platinum, uint32 gold, uint32 silver, ui
if (copper && silver && gold && platinum) { // CSGP
money_string = fmt::format(
"{} Platinum, {} Gold, {} Silver, and {} Copper",
platinum,
gold,
silver,
copper
commify(std::to_string(platinum)),
commify(std::to_string(gold)),
commify(std::to_string(silver)),
commify(std::to_string(copper))
);
} else if (copper && silver && gold && !platinum) { // CSG
money_string = fmt::format(
"{} Gold, {} Silver, and {} Copper",
gold,
silver,
copper
commify(std::to_string(gold)),
commify(std::to_string(silver)),
commify(std::to_string(copper))
);
} else if (copper && silver && !gold && !platinum) { // CS
money_string = fmt::format(
"{} Silver and {} Copper",
silver,
copper
commify(std::to_string(silver)),
commify(std::to_string(copper))
);
} else if (!copper && silver && gold && platinum) { // SGP
money_string = fmt::format(
"{} Platinum, {} Gold, and {} Silver",
platinum,
gold,
silver
commify(std::to_string(platinum)),
commify(std::to_string(gold)),
commify(std::to_string(silver))
);
} else if (!copper && silver && gold && !platinum) { // SG
money_string = fmt::format(
"{} Gold and {} Silver",
gold,
silver
commify(std::to_string(gold)),
commify(std::to_string(silver))
);
} else if (copper && !silver && gold && platinum) { // CGP
money_string = fmt::format(
"{} Platinum, {} Gold, and {} Copper",
platinum,
gold,
copper
commify(std::to_string(platinum)),
commify(std::to_string(gold)),
commify(std::to_string(copper))
);
} else if (copper && !silver && gold && !platinum) { // CG
money_string = fmt::format(
"{} Gold and {} Copper",
gold,
copper
commify(std::to_string(gold)),
commify(std::to_string(copper))
);
} else if (!copper && !silver && gold && platinum) { // GP
money_string = fmt::format(
"{} Platinum and {} Gold",
platinum,
gold
commify(std::to_string(platinum)),
commify(std::to_string(gold))
);
} else if (!copper && !silver && !gold && platinum) { // P
money_string = fmt::format(
"{} Platinum",
platinum
commify(std::to_string(platinum))
);
} else if (!copper && !silver && gold && !platinum) { // G
money_string = fmt::format(
"{} Gold",
gold
commify(std::to_string(gold))
);
} else if (!copper && silver && !gold && !platinum) { // S
money_string = fmt::format(
"{} Silver",
silver
commify(std::to_string(silver))
);
} else if (copper && !silver && !gold && !platinum) { // C
money_string = fmt::format(
"{} Copper",
copper
commify(std::to_string(copper))
);
}
return money_string;
}
std::string commify(const std::string &number) {
std::string temp_string;
auto string_length = static_cast<int>(number.length());
int i = 0;
for (i = string_length - 3; i >= 0; i -= 3) {
if (i > 0) {
temp_string = "," + number.substr(static_cast<unsigned long>(i), 3) + temp_string;
} else {
temp_string = number.substr(static_cast<unsigned long>(i), 3) + temp_string;
}
}
if (i < 0) {
temp_string = number.substr(0, static_cast<unsigned long>(3 + i)) + temp_string;
}
return temp_string;
}
+2 -1
View File
@@ -194,6 +194,7 @@ std::string JoinString(const std::vector<std::string>& ar, const std::string &de
void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string);
std::string replace_string(std::string subject, const std::string &search, const std::string &replace);
void ParseAccountString(const std::string &s, std::string &account, std::string &loginserver);
std::string commify(const std::string &number);
//const char based
@@ -203,7 +204,7 @@ bool strn0cpyt(char* dest, const char* source, uint32 size);
char *CleanMobName(const char *in, char *out);
char *RemoveApostrophes(const char *s);
char* strn0cpy(char* dest, const char* source, uint32 size);
const char *ConvertArray(int input, char *returnchar);
const char *ConvertArray(int64 input, char *returnchar);
const char *ConvertArrayF(float input, char *returnchar);
const char *MakeLowerString(const char *source);
uint32 hextoi(const char* num);
+1
View File
@@ -61,6 +61,7 @@ struct ActivityInformation {
int skill_id; // older clients, first id from above
int spell_id; // older clients, first id from above
int goal_id;
std::string goal_match_list;
TaskMethodType goal_method;
int goal_count;
int deliver_to_npc;
+1 -1
View File
@@ -34,7 +34,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9178
#define CURRENT_BINARY_DATABASE_VERSION 9185
#ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9028
+4
View File
@@ -1,3 +1,7 @@
IF(EQEMU_BUILD_LUA)
ADD_SUBDIRECTORY(luabind)
ENDIF(EQEMU_BUILD_LUA)
IF(EQEMU_BUILD_PERL)
ADD_SUBDIRECTORY(perlbind)
ENDIF(EQEMU_BUILD_PERL)
+21
View File
@@ -0,0 +1,21 @@
*
!.gitignore
!.editorconfig
!CMakeLists.txt
!LICENSE
!README.md
!.github/
!.github/**
!doc/
!doc/**
!include/
!include/**
!src/
!src/*
!test/
!test/*
+64
View File
@@ -0,0 +1,64 @@
cmake_minimum_required(VERSION 3.7)
project(perlbind LANGUAGES CXX)
set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".so" ".a")
find_package(PerlLibs)
set(PERLBIND_HEADERS
include/perlbind/array.h
include/perlbind/forward.h
include/perlbind/function.h
include/perlbind/hash.h
include/perlbind/interpreter.h
include/perlbind/iterator.h
include/perlbind/package.h
include/perlbind/perlbind.h
include/perlbind/scalar.h
include/perlbind/stack.h
include/perlbind/stack_push.h
include/perlbind/stack_read.h
include/perlbind/subcaller.h
include/perlbind/traits.h
include/perlbind/typemap.h
include/perlbind/types.h
include/perlbind/util.h
include/perlbind/version.h
)
set(PERLBIND_SOURCES
src/function.cpp
src/hash.cpp
src/interpreter.cpp
src/package.cpp
)
if(MSVC)
set(PERLBIND_SOURCES ${PERLBIND_SOURCES} src/perlbind.natvis)
endif()
add_library(perlbind ${PERLBIND_SOURCES} ${PERLBIND_HEADERS})
target_include_directories(perlbind PUBLIC
${PERL_INCLUDE_PATH}
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
option(PERLBIND_BUILD_TESTS "Build tests" OFF)
option(PERLBIND_ENABLE_ASAN "Build with address sanitizer" OFF)
if(PERLBIND_ENABLE_ASAN)
target_compile_options(perlbind PRIVATE -fsanitize=address -fno-omit-frame-pointer)
target_link_options(perlbind PRIVATE -fsanitize=address -fno-omit-frame-pointer)
endif()
if(PERLBIND_BUILD_TESTS)
enable_testing()
add_subdirectory(test)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT tests)
target_include_directories(tests PRIVATE
${PERL_INCLUDE_PATH}
${CMAKE_CURRENT_SOURCE_DIR}/include)
endif()
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2022 hg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+119
View File
@@ -0,0 +1,119 @@
#pragma once
#include "types.h"
#include "iterator.h"
#include <stdexcept>
namespace perlbind {
struct array : public type_base
{
using iterator = detail::array_iterator;
~array() noexcept
{
SvREFCNT_dec(m_av);
}
array() noexcept
: type_base(), m_av(newAV()) {}
array(PerlInterpreter* interp) noexcept
: type_base(interp), m_av(newAV()) {}
array(const array& other) noexcept
: type_base(other.my_perl), m_av(copy_array(other.m_av)) {}
array(array&& other) noexcept
: type_base(other.my_perl), m_av(other.m_av)
{
other.m_av = newAV();
}
array(AV*& value) noexcept
: type_base(), m_av(copy_array(value)) {}
array(AV*&& value) noexcept
: type_base(), m_av(value) {} // take ownership
array(scalar ref)
: type_base(ref.my_perl)
{
if (!ref.is_array_ref())
throw std::runtime_error("cannot construct array from non-array reference");
reset(reinterpret_cast<AV*>(SvREFCNT_inc(*ref)));
}
array(scalar_proxy proxy)
: array(scalar(SvREFCNT_inc(proxy.sv()))) {}
array& operator=(const array& other) noexcept
{
if (this != &other)
m_av = copy_array(other.m_av);
return *this;
}
array& operator=(array&& other) noexcept
{
if (this != &other)
std::swap(m_av, other.m_av);
return *this;
}
array& operator=(AV*& value) noexcept
{
if (m_av != value)
m_av = copy_array(value);
return *this;
}
array& operator=(AV*&& value) noexcept
{
reset(value);
return *this;
}
operator AV*() const { return m_av; }
operator SV*() const { return reinterpret_cast<SV*>(m_av); }
AV* release() noexcept
{
AV* tmp = m_av;
m_av = newAV();
return tmp;
}
void reset(AV* value) noexcept
{
SvREFCNT_dec(m_av);
m_av = value;
}
void clear() noexcept { av_clear(m_av); } // decreases refcnt of all SV elements
scalar pop_back() noexcept { return av_pop(m_av); }
scalar pop_front() noexcept { return av_shift(m_av); }
void push_back(const scalar& value) { av_push(m_av, newSVsv(value)); }
void push_back(scalar&& value) { av_push(m_av, value.release()); }
void reserve(size_t count) { av_extend(m_av, count > 0 ? count - 1 : 0); }
size_t size() const { return av_len(m_av) + 1; }
SV* sv() const { return reinterpret_cast<SV*>(m_av); }
// returns a proxy that takes ownership of one reference to the SV element
// extends the array and creates an undef SV if index out of range
scalar_proxy operator[](size_t index)
{
SV** sv = av_fetch(m_av, index, 1);
return scalar_proxy(my_perl, SvREFCNT_inc(*sv));
}
iterator begin() const noexcept { return { my_perl, m_av, 0 }; }
iterator end() const noexcept { return { my_perl, m_av, size() }; }
private:
AV* copy_array(AV* other)
{
return av_make(av_len(other)+1, AvARRAY(other));
}
AV* m_av = nullptr;
};
} // namespace perlbind
+22
View File
@@ -0,0 +1,22 @@
#pragma once
namespace perlbind {
namespace detail {
class xsub_stack;
struct function_base;
struct array_iterator;
struct hash_iterator;
} // namespace detail
class interpreter;
class package;
struct scalar;
struct scalar_proxy;
struct reference;
struct array;
struct hash;
} // namespace perlbind
+144
View File
@@ -0,0 +1,144 @@
#pragma once
namespace perlbind { namespace detail {
// traits for function and class method exports
template <typename Ret, typename Class, typename... Args>
struct base_traits
{
using return_t = Ret;
using sig_t = util::type_name<Args...>;
using stack_tuple = std::conditional_t<std::is_void<Class>::value,
std::tuple<Args...>,
std::tuple<Class*, Args...>>;
static constexpr int arity = sizeof...(Args);
static constexpr int stack_arity = sizeof...(Args) + (std::is_void<Class>::value ? 0 : 1);
static constexpr int vararg_count = count_of<array, Args...>::value +
count_of<hash, Args...>::value;
static constexpr bool is_vararg = vararg_count > 0;
static constexpr bool is_vararg_last = is_last<array, Args...>::value ||
is_last<hash, Args...>::value;
static_assert(!is_vararg || (vararg_count == 1 && is_vararg_last),
"A function may only accept a single array or hash and it must be "
"be the last parameter. Prefer using reference parameters instead.");
};
template <typename T, bool = std::is_class<T>::value>
struct function_traits : public function_traits<decltype(&T::operator()), true> {};
template <typename Ret, typename... Args>
struct function_traits<Ret(*)(Args...), false> : base_traits<Ret, void, Args...>
{
using type = Ret(*)(Args...);
};
template <typename Ret, typename Class, typename... Args>
struct function_traits<Ret(Class::*)(Args...), false> : base_traits<Ret, Class, Args...>
{
using type = Ret(Class::*)(Args...);
};
template <typename Ret, typename Class, typename... Args>
struct function_traits<Ret(Class::*)(Args...) const, false> : base_traits<Ret, Class, Args...>
{
using type = Ret(Class::*)(Args...) const;
};
template <typename Ret, typename Class, typename... Args>
struct function_traits<Ret(Class::*)(Args...) const, true> : base_traits<Ret, void, Args...>
{
using type = Ret(*)(Args...);
};
// represents a bound native function
struct function_base
{
virtual ~function_base() = default;
virtual std::string get_signature() const = 0;
virtual bool is_compatible(xsub_stack&) const = 0;
virtual void call(xsub_stack&) const = 0;
static const MGVTBL mgvtbl;
};
template <typename T>
struct function : public function_base, function_traits<T>
{
using target_t = typename function::type;
using return_t = typename function::return_t;
function() = delete;
function(PerlInterpreter* interp, T func)
: my_perl(interp), m_func(func) {}
std::string get_signature() const override
{
return util::type_name<target_t>::str();
};
bool is_compatible(xsub_stack& stack) const override
{
return function::is_vararg || stack.check_types(typename function::stack_tuple{});
}
void call(xsub_stack& stack) const override
{
if (!function::is_vararg && stack.size() != function::stack_arity)
{
using sig = typename function::sig_t;
int count = std::is_member_function_pointer<T>::value ? stack.size() - 1 : stack.size();
SV* err = newSVpvf("'%s(%s)' called with %d argument(s), expected %d\n argument(s): (%s)\n",
stack.name().c_str(), sig::str().c_str(), count, function::arity, stack.types().c_str());
err = sv_2mortal(err);
throw std::runtime_error(SvPV_nolen(err));
}
call_impl(stack, std::is_void<function::return_t>());
}
private:
void call_impl(xsub_stack& stack, std::false_type) const
{
return_t result = apply(m_func, stack.convert_stack(typename function::stack_tuple{}));
stack.push_return(std::move(result));
}
void call_impl(xsub_stack& stack, std::true_type) const
{
apply(m_func, stack.convert_stack(typename function::stack_tuple{}));
}
// c++14 call function template with tuple arg unpacking (c++17 can use std::apply())
template <typename F, typename Tuple, size_t... I>
auto call_func(F func, Tuple&& t, std::index_sequence<I...>) const
{
return func(std::get<I>(std::forward<Tuple>(t))...);
}
template <typename F, typename Tuple, size_t... I>
auto call_member(F method, Tuple&& t, std::index_sequence<I...>) const
{
return (std::get<0>(t)->*method)(std::get<I + 1>(std::forward<Tuple>(t))...);
}
template <typename F, typename Tuple, std::enable_if_t<!std::is_member_function_pointer<F>::value, bool> = true>
auto apply(F func, Tuple&& t) const
{
using make_sequence = std::make_index_sequence<std::tuple_size<Tuple>::value>;
return call_func(func, std::forward<Tuple>(t), make_sequence{});
}
template <typename F, typename Tuple, std::enable_if_t<std::is_member_function_pointer<F>::value, bool> = true>
auto apply(F func, Tuple&& t) const
{
using make_sequence = std::make_index_sequence<std::tuple_size<Tuple>::value - 1>;
return call_member(func, std::forward<Tuple>(t), make_sequence{});
}
PerlInterpreter* my_perl = nullptr;
T m_func;
};
} // namespace detail
} // namespace perlbind
+124
View File
@@ -0,0 +1,124 @@
#pragma once
#include "types.h"
#include <string>
namespace perlbind {
struct hash : public type_base
{
using iterator = detail::hash_iterator;
~hash() noexcept
{
SvREFCNT_dec(m_hv);
}
hash() noexcept
: type_base(), m_hv(newHV()) {}
hash(PerlInterpreter* interp) noexcept
: type_base(interp), m_hv(newHV()) {}
hash(const hash& other) noexcept
: type_base(other.my_perl), m_hv(copy_hash(other.m_hv)) {}
hash(hash&& other) noexcept
: type_base(other.my_perl), m_hv(other.m_hv)
{
other.m_hv = newHV();
}
hash(HV*& value) noexcept
: type_base(), m_hv(copy_hash(value)) {}
hash(HV*&& value) noexcept
: type_base(), m_hv(value) {} // take ownership
hash(scalar ref);
hash(scalar_proxy proxy);
hash& operator=(const hash& other) noexcept
{
if (this != &other)
m_hv = copy_hash(other.m_hv);
return *this;
}
hash& operator=(hash&& other) noexcept
{
if (this != &other)
std::swap(m_hv, other.m_hv);
return *this;
}
hash& operator=(HV*& value) noexcept
{
if (m_hv != value)
m_hv = copy_hash(value);
return *this;
}
hash& operator=(HV*&& value) noexcept
{
reset(value);
return *this;
}
operator HV*() const { return m_hv; }
operator SV*() const { return reinterpret_cast<SV*>(m_hv); }
HV* release() noexcept
{
HV* tmp = m_hv;
m_hv = newHV();
return tmp;
}
void reset(HV* value) noexcept
{
SvREFCNT_dec(m_hv);
m_hv = value;
}
scalar at(const char* key);
scalar at(const std::string& key);
void clear() noexcept { hv_clear(m_hv); }
bool exists(const char* key) const
{
return hv_exists(m_hv, key, static_cast<I32>(strlen(key)));
}
bool exists(const std::string& key) const
{
return hv_exists(m_hv, key.c_str(), static_cast<I32>(key.size()));
}
void insert(const char* key, scalar value);
void insert(const std::string& key, scalar value);
void remove(const char* key)
{
hv_delete(m_hv, key, static_cast<I32>(strlen(key)), 0);
}
void remove(const std::string& key)
{
hv_delete(m_hv, key.c_str(), static_cast<I32>(key.size()), 0);
}
size_t size() const { return HvTOTALKEYS(m_hv); }
SV* sv() const { return reinterpret_cast<SV*>(m_hv); }
// returns a proxy that takes ownership of one reference to the SV value
// creates an undef SV entry for the key if it doesn't exist
scalar_proxy operator[](const std::string& key);
iterator begin() const noexcept;
iterator end() const noexcept;
iterator find(const char* key);
iterator find(const std::string& key);
private:
scalar at(const char* key, size_t size);
iterator find(const char* key, size_t size);
void insert(const char* key, size_t size, scalar value);
HV* copy_hash(HV* other) noexcept;
HV* m_hv = nullptr;
};
} // namespace perlbind
@@ -0,0 +1,63 @@
#pragma once
namespace perlbind {
class interpreter
{
public:
interpreter();
interpreter(PerlInterpreter* interp) : my_perl(interp) {}
interpreter(int argc, const char** argv);
interpreter(const interpreter& other) = delete;
interpreter(interpreter&& other) = delete;
interpreter& operator=(const interpreter& other) = delete;
interpreter& operator=(interpreter&& other) = delete;
~interpreter();
PerlInterpreter* get() const { return my_perl; }
void load_script(std::string packagename, std::string filename);
void eval(const char* str);
template <typename T, typename... Args>
T call_sub(const char* subname, Args&&... args) const
{
detail::sub_caller caller(my_perl);
return caller.call_sub<T>(subname, std::forward<Args>(args)...);
}
// returns interface to add bindings to package name
package new_package(const char* name)
{
return package(my_perl, name);
}
// registers type for blessing objects, returns interface
template <typename T>
class_<T> new_class(const char* name)
{
static_assert(!std::is_pointer<T>::value && !std::is_reference<T>::value,
"new_class<T> 'T' should not be a pointer or reference");
auto typemap = detail::typemap::get(my_perl);
auto type_id = detail::usertype<T*>::id();
typemap[type_id] = name;
return class_<T>(my_perl, name);
}
// helper to bind functions in default main:: package
template <typename T>
void add(const char* name, T&& func)
{
new_package("main").add(name, std::forward<T>(func));
}
private:
void init(int argc, const char** argv);
bool m_is_owner = false;
PerlInterpreter* my_perl = nullptr;
};
} // namespace perlbind
+100
View File
@@ -0,0 +1,100 @@
#pragma once
namespace perlbind { namespace detail {
struct array_iterator
{
array_iterator() = default;
array_iterator(PerlInterpreter* interp, AV* av, size_t index)
: my_perl(interp), m_av(av), m_index(index), m_scalar(interp)
{
fetch();
}
bool operator!=(const array_iterator& other) const
{
return m_index != other.m_index;
}
array_iterator& operator++()
{
++m_index;
fetch();
return *this;
}
scalar* operator->()
{
return &m_scalar;
}
scalar& operator*()
{
return m_scalar;
}
private:
void fetch()
{
SV** sv = av_fetch(m_av, m_index, 0);
if (sv)
m_scalar = SvREFCNT_inc(*sv);
}
PerlInterpreter* my_perl;
AV* m_av;
size_t m_index;
scalar m_scalar;
};
struct hash_iterator
{
hash_iterator() = default;
hash_iterator(PerlInterpreter* interp, HV* hv, HE* he)
: my_perl(interp), m_hv(hv), m_he(he)
{
fetch();
}
bool operator==(const hash_iterator& other) const
{
return m_he == other.m_he;
}
bool operator!=(const hash_iterator& other) const
{
return !(*this == other);
}
hash_iterator& operator++()
{
m_he = hv_iternext(m_hv);
fetch();
return *this;
}
std::pair<const char*, scalar>* operator->()
{
return &m_pair;
}
std::pair<const char*, scalar>& operator*()
{
return m_pair;
}
private:
void fetch()
{
if (m_he)
m_pair = { HePV(m_he, PL_na), scalar(my_perl, SvREFCNT_inc(HeVAL(m_he))) };
}
PerlInterpreter* my_perl;
HV* m_hv;
HE* m_he;
std::pair<const char*, scalar> m_pair;
};
} // namespace detail
} // namespace perlbind
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#include <string>
namespace perlbind {
class package
{
public:
virtual ~package() = default;
package() = delete;
package(PerlInterpreter* interp, const char* name)
: my_perl(interp), m_name(name), m_stash(gv_stashpv(name, GV_ADD))
{}
// bind a function pointer to a function name in the package
// overloads with same name must be explicit (default parameters not supported)
// overloads have a runtime lookup cost and chooses the first compatible overload
template <typename T>
void add(const char* name, T func)
{
// ownership of function object is given to perl
auto function = new detail::function<T>(my_perl, func);
add_impl(name, static_cast<detail::function_base*>(function));
}
// specify a base class name for object inheritance (must be registered)
// calling object methods missing from the package will search parent classes
// base classes are searched in registered order and include any grandparents
void add_base_class(const char* name)
{
std::string package_isa = m_name + "::ISA";
AV* av = get_av(package_isa.c_str(), GV_ADD);
array isa_array = reinterpret_cast<AV*>(SvREFCNT_inc(av));
isa_array.push_back(name);
}
// add a constant value to this package namespace
template <typename T>
void add_const(const char* name, T&& value)
{
newCONSTSUB(m_stash, name, scalar(value).release());
}
private:
void add_impl(const char* name, detail::function_base* function);
std::string m_name;
PerlInterpreter* my_perl = nullptr;
HV* m_stash = nullptr;
};
template <typename T>
struct class_ : public package
{
using package::package;
};
} // namespace perlbind
+55
View File
@@ -0,0 +1,55 @@
#pragma once
// Defining PERLBIND_STRICT_NUMERIC_TYPES will enable strict type checks
// for integers and floats.This is required for overloads that depend on
// int and float type differences.
// #define PERLBIND_STRICT_NUMERIC_TYPES
// Defining PERLBIND_NO_STRICT_SCALAR_TYPES will disable strict type checks
// for all int, float, and string function arguments. These types will only
// be checked for scalar validity and converted to the function's expected
// paramter type. This will break overloads that depend on distinct types.
// This option overrides PERLBIND_STRICT_NUMERIC_TYPES.
//#define PERLBIND_NO_STRICT_SCALAR_TYPES
// defining PERL_NO_GET_CONTEXT gets context from local variable "my_perl"
// instead of calling Perl_get_context() in macros
#define PERL_NO_GET_CONTEXT
#define WIN32IO_IS_STDIO
#if _MSC_VER
#define __inline__ __inline
// perl 5.30+ defines HAS_BUILTIN_EXPECT for msvc which breaks builds
#define __builtin_expect(expr,val) (expr)
// avoid INT64_C and UINT64_C redefinition warnings
#if PERL_VERSION < 28
#include <cstdint>
#endif
#endif
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>
// short name perl macros that cause issues
#undef Move
#undef Copy
#undef Zero
#undef list
#undef seed
#undef do_open
#undef do_close
#include <perlbind/version.h>
#include <perlbind/forward.h>
#include <perlbind/util.h>
#include <perlbind/traits.h>
#include <perlbind/hash.h>
#include <perlbind/typemap.h>
#include <perlbind/scalar.h>
#include <perlbind/array.h>
#include <perlbind/stack.h>
#include <perlbind/subcaller.h>
#include <perlbind/function.h>
#include <perlbind/package.h>
#include <perlbind/interpreter.h>
+254
View File
@@ -0,0 +1,254 @@
#pragma once
#include "types.h"
#include <string>
#include <type_traits>
namespace perlbind {
struct scalar : type_base
{
virtual ~scalar() noexcept
{
SvREFCNT_dec(m_sv);
}
scalar() noexcept
: type_base(), m_sv(newSV(0)) {} // nothing allocated
scalar(PerlInterpreter* interp) noexcept
: type_base(interp), m_sv(newSV(0)) {}
scalar(PerlInterpreter* interp, SV*&& sv) noexcept
: type_base(interp), m_sv(sv) {}
scalar(const scalar& other) noexcept
: type_base(other.my_perl), m_sv(newSVsv(other.m_sv)) {}
scalar(scalar&& other) noexcept
: type_base(other.my_perl), m_sv(other.m_sv)
{
other.m_sv = newSV(0);
}
scalar(SV*& value) noexcept
: type_base(), m_sv(newSVsv(value)) {}
scalar(SV*&& value) noexcept
: type_base(), m_sv(value) {}
scalar(const char* value) noexcept
: type_base(), m_sv(newSVpv(value, 0)) {}
scalar(const std::string& value) noexcept
: type_base(), m_sv(newSVpvn(value.c_str(), value.size())) {}
template <typename T, std::enable_if_t<detail::is_signed_integral_or_enum<T>::value, bool> = true>
scalar(T value) noexcept : type_base(), m_sv(newSViv(static_cast<IV>(value))) {}
template <typename T, std::enable_if_t<std::is_unsigned<T>::value, bool> = true>
scalar(T value) noexcept : type_base(), m_sv(newSVuv(value)) {}
template <typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
scalar(T value) noexcept : type_base(), m_sv(newSVnv(value)) {}
template <typename T, std::enable_if_t<std::is_pointer<T>::value, bool> = true>
scalar(T value) noexcept : type_base(), m_sv(newSV(0))
{
*this = std::move(value);
}
scalar& operator=(const scalar& other) noexcept
{
if (this != &other)
sv_setsv(m_sv, other.m_sv);
return *this;
}
scalar& operator=(scalar&& other) noexcept
{
if (this != &other)
std::swap(m_sv, other.m_sv);
return *this;
}
scalar& operator=(SV*& value) noexcept
{
sv_setsv(m_sv, value);
return *this;
}
scalar& operator=(SV*&& value) noexcept
{
reset(value);
return *this;
}
scalar& operator=(const char* value) noexcept
{
sv_setpv(m_sv, value);
return *this;
}
scalar& operator=(const std::string& value) noexcept
{
sv_setpvn(m_sv, value.c_str(), value.size());
return *this;
}
template <typename T, std::enable_if_t<detail::is_signed_integral_or_enum<T>::value, bool> = true>
scalar& operator=(T value) noexcept
{
sv_setiv(m_sv, static_cast<IV>(value));
return *this;
}
template <typename T, std::enable_if_t<std::is_unsigned<T>::value, bool> = true>
scalar& operator=(T value) noexcept
{
sv_setuv(m_sv, value);
return *this;
}
template <typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
scalar& operator=(T value) noexcept
{
sv_setnv(m_sv, value);
return *this;
}
template <typename T, std::enable_if_t<std::is_pointer<T>::value, bool> = true>
scalar& operator=(T value) noexcept
{
// bless if it's in the typemap
const char* type_name = detail::typemap::template get_name<T>(my_perl);
sv_setref_pv(m_sv, type_name, static_cast<void*>(value));
return *this;
}
operator SV*() const { return m_sv; }
operator void*() const { return m_sv; }
operator const char*() const { return SvPV_nolen(m_sv); }
operator std::string() const { return SvPV_nolen(m_sv); }
template <typename T, std::enable_if_t<detail::is_signed_integral_or_enum<T>::value, bool> = true>
operator T() const { return static_cast<T>(SvIV(m_sv)); }
template <typename T, std::enable_if_t<std::is_unsigned<T>::value, bool> = true>
operator T() const { return static_cast<T>(SvUV(m_sv)); }
template <typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
operator T() const { return static_cast<T>(SvNV(m_sv)); }
template <typename T, std::enable_if_t<std::is_pointer<T>::value, bool> = true>
operator T() const
{
const char* type_name = detail::typemap::template get_name<T>(my_perl);
if (type_name && sv_isobject(m_sv) && sv_derived_from(m_sv, type_name))
{
IV tmp = SvIV(SvRV(m_sv));
return INT2PTR(T, tmp);
}
return nullptr;
}
template <typename T>
T as() const { return static_cast<T>(*this); }
// release ownership of SV
SV* release() noexcept
{
SV* tmp = m_sv;
m_sv = newSV(0);
return tmp;
}
// take ownership of an SV
void reset(SV* value) noexcept
{
SvREFCNT_dec(m_sv);
m_sv = value;
}
SV* sv() const { return m_sv; }
SV* deref() const { return SvRV(m_sv); }
size_t size() const { return SvPOK(m_sv) ? sv_len(m_sv) : 0; }
svtype type() const { return SvTYPE(m_sv); }
const char* c_str() const { return SvPV_nolen(m_sv); }
SV* operator*() { return SvRV(m_sv); }
bool is_null() const { return type() == SVt_NULL; } //SvOK(m_sv)
bool is_integer() const { return SvIOK(m_sv); }
bool is_float() const { return SvNOK(m_sv); }
bool is_string() const { return SvPOK(m_sv); }
bool is_reference() const { return SvROK(m_sv); }
bool is_scalar_ref() const { return SvROK(m_sv) && SvTYPE(SvRV(m_sv)) < SVt_PVAV; }
bool is_array_ref() const { return SvROK(m_sv) && SvTYPE(SvRV(m_sv)) == SVt_PVAV; }
bool is_hash_ref() const { return SvROK(m_sv) && SvTYPE(SvRV(m_sv)) == SVt_PVHV; }
protected:
SV* m_sv = nullptr;
};
// references are scalars that take ownership of one new reference to a value
// use reset() to take ownership of an existing RV
struct reference : public scalar
{
reference() = default;
template <typename T, std::enable_if_t<std::is_base_of<type_base, T>::value, bool> = true>
reference(T& value) noexcept : scalar(value.my_perl, nullptr) { m_sv = newRV_inc(value); }
// increments referent for rvalues of scalar objects (not raw SVs) since they dec on destruct
template <typename T, std::enable_if_t<std::is_base_of<type_base, T>::value, bool> = true>
reference(T&& value) noexcept : scalar(value.my_perl, nullptr) { m_sv = newRV_inc(value); }
template <typename T, std::enable_if_t<detail::is_any<T, SV*, AV*, HV*>::value, bool> = true>
reference(T& value) noexcept { reset(newRV_inc(reinterpret_cast<SV*>(value))); }
template <typename T, std::enable_if_t<detail::is_any<T, SV*, AV*, HV*>::value, bool> = true>
reference(T&& value) noexcept { reset(newRV_noinc(reinterpret_cast<SV*>(value))); }
SV* operator*() { return SvRV(m_sv); }
};
// scalar proxy reference is used for array and hash index operator[] overloads
struct scalar_proxy
{
scalar_proxy() = delete;
scalar_proxy(PerlInterpreter* interp, scalar&& value) noexcept
: my_perl(interp), m_value(std::move(value)) {}
SV* sv() const { return m_value; }
const char* c_str() const { return static_cast<const char*>(m_value); }
template <typename T>
T as() const { return m_value.as<T>(); }
operator std::string() const { return m_value; }
// copying value to supported conversion types (e.g. int val = arr[i])
template <typename T, std::enable_if_t<!std::is_base_of<type_base, T>::value, bool> = true>
operator T() const
{
return static_cast<T>(m_value);
}
// taking a reference to the source SV (e.g. scalar val = arr[i])
template <typename T, std::enable_if_t<std::is_same<T, scalar>::value, bool> = true>
operator T() const
{
return SvREFCNT_inc(m_value);
}
// assigning scalar to proxy, the source SV is modified (arr[i] = "new value")
scalar_proxy& operator=(scalar value)
{
m_value = value;
return *this;
}
scalar_proxy& operator=(const scalar_proxy& other)
{
m_value = other.m_value;
return *this;
}
// todo: nested proxy[]
private:
PerlInterpreter* my_perl = nullptr;
scalar m_value;
};
} // namespace perlbind
+137
View File
@@ -0,0 +1,137 @@
#pragma once
#include "stack_push.h"
#include "stack_read.h"
#include <algorithm>
#include <string>
#include <tuple>
namespace perlbind { namespace detail {
// handles xsub call stack from perl, inherits stack::pusher to push return values
class xsub_stack : public stack::pusher
{
public:
xsub_stack() = delete;
xsub_stack(PerlInterpreter* my_perl, CV* cv)
: stack::pusher(my_perl)
{
GV* gv = CvGV(cv);
m_sub_name = GvNAME(gv);
m_pkg_name = HvNAME(GvSTASH(gv));
dXSARGS;
this->sp = sp;
this->ax = ax;
this->mark = mark;
this->items = items;
}
~xsub_stack() { XSRETURN(m_pushed); }
int size() const { return items; }
std::string name() const { return std::string(pkg_name()) + "::" + sub_name(); }
const char* pkg_name() const { return m_pkg_name; }
const char* sub_name() const { return m_sub_name; }
template <typename T>
void push_return(T&& value)
{
XSprePUSH;
push(std::forward<T>(value));
}
// returns true if all perl stack arguments are compatible with expected native arg types
template <typename Tuple>
bool check_types(Tuple&& types)
{
static constexpr int count = std::tuple_size<Tuple>::value;
if (items != count)
return false;
else if (count == 0)
return true;
using make_sequence = std::make_index_sequence<count>;
return check_stack(std::forward<Tuple>(types), make_sequence());
}
// returns tuple of converted perl stack arguments, throws on an incompatible type
template <typename Tuple>
auto convert_stack(Tuple&& types)
{
using make_sequence = std::make_index_sequence<std::tuple_size<Tuple>::value>;
return get_stack(std::forward<Tuple>(types), make_sequence());
}
std::string types()
{
std::string args;
for (int i = 0; i < items; ++i)
{
args += get_type_name(ST(i));
if (i < (items - 1))
args += ", ";
}
return args.empty() ? "void" : args;
}
protected:
int ax = 0;
int items = 0;
SV** mark = nullptr;
const char* m_pkg_name = nullptr;
const char* m_sub_name = nullptr;
std::string get_type_name(SV* item)
{
switch (SvTYPE(item))
{
case SVt_NULL: return "<undefined>";
case SVt_NV: return "double";
case SVt_PV: return "string";
case SVt_PVAV: return "array";
case SVt_PVHV: return "hash";
case SVt_IV:
if (sv_isobject(item))
return std::string(sv_reftype(SvRV(item), true)) + "*";
else if (SvROK(item))
return "ref";
else
return "int";
default:
return sv_reftype(item, true);
}
}
private:
template <typename T>
bool check_index(T t, size_t index)
{
return stack::read_as<T>::check(my_perl, static_cast<int>(index), ax, items);
}
// return true if perl stack matches all expected argument types in tuple
template <typename Tuple, size_t... I>
bool check_stack(Tuple&& t, std::index_sequence<I...>)
{
// lists compatibility of each expected arg type (no short-circuit)
std::initializer_list<bool> res = {
check_index(std::get<I>(std::forward<Tuple>(t)), I)... };
return std::all_of(res.begin(), res.end(), [](bool same) { return same; });
}
template <typename T>
T get_stack_index(T t, size_t index)
{
return stack::read_as<T>::get(my_perl, static_cast<int>(index), ax, items);
}
template <typename Tuple, size_t... I>
auto get_stack(Tuple&& t, std::index_sequence<I...>)
{
return Tuple{ get_stack_index(std::get<I>(std::forward<Tuple>(t)), I)... };
}
};
} // namespace detail
} // namespace perlbind
+118
View File
@@ -0,0 +1,118 @@
#pragma once
#include <string>
namespace perlbind { namespace stack {
// base class for pushing value types to perl stack
// methods use macros that push new mortalized SVs but do not extend the stack
// the stack is only extended when pushing an array, hash, or using push_args().
// this is because for xsubs the "stack is always large enough to take one return value"
struct pusher
{
virtual ~pusher() = default;
pusher() = delete;
pusher(PerlInterpreter* interp) : my_perl(interp), sp(PL_stack_sp) {}
SV* pop() { return POPs; }
void push(bool value) { PUSHs(boolSV(value)); ++m_pushed; }
void push(const char* value)
{
if (!value)
PUSHs(&PL_sv_undef);
else
mPUSHp(value, strlen(value));
++m_pushed;
}
void push(const std::string& value) { mPUSHp(value.c_str(), value.size()); ++m_pushed; }
void push(scalar value) { mPUSHs(value.release()); ++m_pushed; };
void push(reference value) { mPUSHs(value.release()); ++m_pushed; };
void push(array value)
{
int count = static_cast<int>(value.size());
EXTEND(sp, count);
for (int i = 0; i < count; ++i)
{
// mortalizes one reference to array element to avoid copying
PUSHs(sv_2mortal(SvREFCNT_inc(value[i].sv())));
}
m_pushed += count;
}
void push(hash value)
{
// hashes are pushed to the perl stack as alternating keys and values
// this is less efficient than pushing a reference to the hash
auto count = hv_iterinit(value) * 2;
EXTEND(sp, count);
while (HE* entry = hv_iternext(value))
{
auto val = HeVAL(entry);
PUSHs(hv_iterkeysv(entry)); // mortalizes new key sv (keys are not stored as sv)
PUSHs(sv_2mortal(SvREFCNT_inc(val)));
}
m_pushed += count;
}
template <typename T, std::enable_if_t<detail::is_signed_integral_or_enum<T>::value, bool> = true>
void push(T value) { mPUSHi(static_cast<IV>(value)); ++m_pushed; }
template <typename T, std::enable_if_t<std::is_unsigned<T>::value, bool> = true>
void push(T value) { mPUSHu(value); ++m_pushed; }
template <typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
void push(T value) { mPUSHn(value); ++m_pushed; }
template <typename T, std::enable_if_t<std::is_pointer<T>::value, bool> = true>
void push(T value)
{
const char* type_name = detail::typemap::get_name<T>(my_perl);
if (!type_name)
{
throw std::runtime_error("cannot push unregistered pointer of type '" + util::type_name<T>::str() + "'");
}
SV* sv = sv_newmortal();
sv_setref_pv(sv, type_name, static_cast<void*>(value));
PUSHs(sv);
++m_pushed;
};
void push(void* value)
{
SV* sv = sv_newmortal();
sv_setref_pv(sv, nullptr, value); // unblessed
PUSHs(sv);
++m_pushed;
}
template <typename... Args>
void push_args(Args&&... args)
{
EXTEND(sp, sizeof...(Args));
push_args_impl(std::forward<Args>(args)...);
};
protected:
PerlInterpreter* my_perl = nullptr;
SV** sp = nullptr;
int m_pushed = 0;
private:
template <typename... Args>
void push_args_impl(Args&&... args) {}
template <typename T, typename... Args>
void push_args_impl(T&& value, Args&&... args)
{
push(std::forward<T>(value));
push_args(std::forward<Args>(args)...);
}
};
} // namespace stack
} // namespace perlbind
+266
View File
@@ -0,0 +1,266 @@
#pragma once
#include <string>
namespace perlbind { namespace stack {
// perl stack reader to convert types, throws if perl stack value isn't type compatible
template <typename T, typename = void>
struct read_as;
template <typename T>
struct read_as<T, std::enable_if_t<std::is_integral<T>::value || std::is_enum<T>::value>>
{
static bool check(PerlInterpreter* my_perl, int i, int ax, int items)
{
#ifdef PERLBIND_NO_STRICT_SCALAR_TYPES
return SvTYPE(ST(i)) < SVt_PVAV;
#elif !defined PERLBIND_STRICT_NUMERIC_TYPES
return SvNIOK(ST(i));
#else
return SvIOK(ST(i));
#endif
}
static T get(PerlInterpreter* my_perl, int i, int ax, int items)
{
if (!check(my_perl, i, ax, items))
{
throw std::runtime_error("expected argument " + std::to_string(i+1) + " to be an integer");
}
return static_cast<T>(SvIV(ST(i))); // unsigned and bools casted
}
};
template <typename T>
struct read_as<T, std::enable_if_t<std::is_floating_point<T>::value>>
{
static bool check(PerlInterpreter* my_perl, int i, int ax, int items)
{
#ifdef PERLBIND_NO_STRICT_SCALAR_TYPES
return SvTYPE(ST(i)) < SVt_PVAV;
#elif !defined PERLBIND_STRICT_NUMERIC_TYPES
return SvNIOK(ST(i));
#else
return SvNOK(ST(i));
#endif
}
static T get(PerlInterpreter* my_perl, int i, int ax, int items)
{
if (!check(my_perl, i, ax, items))
{
throw std::runtime_error("expected argument " + std::to_string(i+1) + " to be a floating point");
}
return static_cast<T>(SvNV(ST(i)));
}
};
template <>
struct read_as<const char*>
{
static bool check(PerlInterpreter* my_perl, int i, int ax, int items)
{
#ifdef PERLBIND_NO_STRICT_SCALAR_TYPES
return SvTYPE(ST(i)) < SVt_PVAV;
#else
return SvPOK(ST(i));
#endif
}
static const char* get(PerlInterpreter* my_perl, int i, int ax, int items)
{
if (!check(my_perl, i, ax, items))
{
throw std::runtime_error("expected argument " + std::to_string(i+1) + " to be a string");
}
return static_cast<const char*>(SvPV_nolen(ST(i)));
}
};
template <>
struct read_as<std::string> : read_as<const char*>
{
};
template <>
struct read_as<void*>
{
static bool check(PerlInterpreter* my_perl, int i, int ax, int items)
{
return sv_isobject(ST(i));
}
static void* get(PerlInterpreter* my_perl, int i, int ax, int items)
{
if (!check(my_perl, i, ax, items))
{
throw std::runtime_error("expected argument " + std::to_string(i+1) + " to be a reference to an object");
}
IV tmp = SvIV(SvRV(ST(i)));
return INT2PTR(void*, tmp);
}
};
template <typename T>
struct read_as<T, std::enable_if_t<std::is_pointer<T>::value>>
{
static bool check(PerlInterpreter* my_perl, int i, int ax, int items)
{
const char* type_name = detail::typemap::get_name<T>(my_perl);
return type_name && sv_isobject(ST(i)) && sv_derived_from(ST(i), type_name);
}
static T get(PerlInterpreter* my_perl, int i, int ax, int items)
{
if (!check(my_perl, i, ax, items))
{
// would prefer to check for unregistered types at compile time (not possible?)
const char* type_name = detail::typemap::get_name<T>(my_perl);
if (!type_name)
{
throw std::runtime_error("expected argument " + std::to_string(i+1) + " to be a reference to an unregistered type (method unusable)");
}
throw std::runtime_error("expected argument " + std::to_string(i+1) + " to be a reference to an object of type '" + type_name + "'");
}
IV tmp = SvIV(SvRV(ST(i)));
return INT2PTR(T, tmp);
}
};
template <typename T>
struct read_as<nullable<T>>
{
static bool check(PerlInterpreter* my_perl, int i, int ax, int items)
{
return true;
}
static nullable<T> get(PerlInterpreter* my_perl, int i, int ax, int items)
{
if (sv_isobject(ST(i)))
{
const char* type_name = detail::typemap::get_name<T>(my_perl);
if (type_name && sv_derived_from(ST(i), type_name))
{
IV tmp = SvIV(SvRV(ST(i)));
return INT2PTR(T, tmp);
}
}
return nullptr;
}
};
template <>
struct read_as<SV*>
{
static bool check(PerlInterpreter* my_perl, int i, int ax, int items)
{
return i < items;
}
static SV* get(PerlInterpreter* my_perl, int i, int ax, int items)
{
if (!check(my_perl, i, ax, items))
{
throw std::runtime_error("expected argument " + std::to_string(i+1) + " to be valid scalar value");
}
return ST(i);
}
};
// scalar, array, and hash readers return reference to stack items (not copies)
template <>
struct read_as<scalar>
{
static bool check(PerlInterpreter* my_perl, int i, int ax, int items)
{
return (SvROK(ST(i)) && SvTYPE(SvRV(ST(i))) < SVt_PVAV) || SvTYPE(ST(i)) < SVt_PVAV;
}
static scalar get(PerlInterpreter* my_perl, int i, int ax, int items)
{
if (!check(my_perl, i, ax, items))
{
throw std::runtime_error("expected argument " + std::to_string(i+1) + " to be a scalar or reference to a scalar");
}
return SvROK(ST(i)) ? SvREFCNT_inc(SvRV(ST(i))) : SvREFCNT_inc(ST(i));
}
};
template <>
struct read_as<reference>
{
static bool check(PerlInterpreter* my_perl, int i, int ax, int items)
{
return SvROK(ST(i));
}
static reference get(PerlInterpreter* my_perl, int i, int ax, int items)
{
if (!check(my_perl, i, ax, items))
{
throw std::runtime_error("expected argument " + std::to_string(i+1) + " to be a reference");
}
// take ownership of a reference to the RV itself (avoid reference to a reference)
reference result;
result.reset(SvREFCNT_inc(ST(i)));
return result;
}
};
template <>
struct read_as<array>
{
static bool check(PerlInterpreter* my_perl, int i, int ax, int items)
{
return items > i;
}
static array get(PerlInterpreter* my_perl, int i, int ax, int items)
{
if (!check(my_perl, i, ax, items))
{
throw std::runtime_error("expected argument " + std::to_string(i+1) + " to be start of a perl array");
}
array result;
result.reserve(items - i);
for (int index = i; index < items; ++index)
{
result.push_back(SvREFCNT_inc(ST(index)));
}
return result;
}
};
template <>
struct read_as<hash>
{
static bool check(PerlInterpreter* my_perl, int i, int ax, int items)
{
int remaining = items - i;
return remaining > 0 && remaining % 2 == 0 && SvTYPE(ST(i)) == SVt_PV;
}
static hash get(PerlInterpreter* my_perl, int i, int ax, int items)
{
if (!check(my_perl, i, ax, items))
{
throw std::runtime_error("expected argument " + std::to_string(i+1) + " to be start of a perl hash");
}
hash result;
for (int index = i; index < items; index += 2)
{
const char* key = SvPV_nolen(ST(index));
result[key] = SvREFCNT_inc(ST(index + 1));
}
return result;
}
};
} // namespace stack
} // namespace perlbind
@@ -0,0 +1,78 @@
#pragma once
#include <stdexcept>
namespace perlbind { namespace detail {
// handles calls to perl, inherits stack::pusher to push args to perl sub
class sub_caller : public stack::pusher
{
public:
sub_caller() = delete;
sub_caller(PerlInterpreter* my_perl) : stack::pusher(my_perl)
{
ENTER; // enter scope boundary for any mortals we create
SAVETMPS;
}
~sub_caller()
{
PUTBACK; // set global sp back to local for any popped return values
FREETMPS;
LEAVE; // leave scope, decref mortals and values returned by perl
}
template <typename T, typename... Args, std::enable_if_t<std::is_void<T>::value, bool> = true>
auto call_sub(const char* subname, Args&&... args)
{
call_sub_impl(subname, G_EVAL|G_VOID, std::forward<Args>(args)...);
}
template <typename T, typename... Args, std::enable_if_t<std::is_integral<T>::value, bool> = true>
auto call_sub(const char* subname, Args&&... args)
{
T result = 0;
try
{
int count = call_sub_impl(subname, G_EVAL|G_SCALAR, std::forward<Args>(args)...);
if (count == 1)
{
SV* sv_result = pop();
result = static_cast<T>(SvIV(sv_result));
}
}
catch (...)
{
pop(); // top of stack holds undef on error when called with these flags
throw;
}
return result;
}
private:
template <typename... Args>
int call_sub_impl(const char* subname, int flags, Args&&... args)
{
PUSHMARK(SP); // notify perl of local sp (required even if not pushing args)
push_args(std::forward<Args>(args)...);
PUTBACK; // set global sp back to local so call will know pushed arg count
int result_count = call_pv(subname, flags);
SPAGAIN; // refresh local sp since call may reallocate stack for scalar returns
// ERRSV doesn't work in perl 5.28+ here for unknown reasons
SV* err = get_sv("@", 0);
if (SvTRUE(err))
{
throw std::runtime_error("Perl error: " + std::string(SvPV_nolen(err)));
}
return result_count;
}
};
} //namespace detail
} // namespace perlbind
+33
View File
@@ -0,0 +1,33 @@
#pragma once
namespace perlbind { namespace detail {
template<typename T, typename... Rest>
struct is_any : std::false_type {};
template<typename T, typename Last>
struct is_any<T, Last> : std::is_same<T, Last> {};
template<typename T, typename First, typename... Rest>
struct is_any<T, First, Rest...> : std::integral_constant<bool, std::is_same<T, First>::value || is_any<T, Rest...>::value> {};
template <typename T>
struct is_signed_integral : std::integral_constant<bool, std::is_integral<T>::value && std::is_signed<T>::value> {};
template <typename T>
struct is_signed_integral_or_enum : std::integral_constant<bool, is_signed_integral<T>::value || std::is_enum<T>::value> {};
template <typename T, typename... Rest>
struct count_of : std::integral_constant<size_t, 0> {};
template <typename T, typename Last>
struct count_of<T, Last> : std::integral_constant<size_t, std::is_same<T, Last>::value ? 1 : 0> {};
template <typename T, typename Next, typename... Rest>
struct count_of<T, Next, Rest...> : std::integral_constant<size_t, count_of<T, Next>::value + count_of<T, Rest...>::value> {};
template <typename T, typename... Args>
struct is_last : std::false_type {};
template <typename T, typename Last>
struct is_last<T, Last> : std::is_same<T, Last> {};
template <typename T, typename Next, typename... Args>
struct is_last<T, Next, Args...> : std::integral_constant<bool, is_last<T, Args...>::value> {};
} // namespace detail
} // namespace perlbind
+45
View File
@@ -0,0 +1,45 @@
#pragma once
namespace perlbind { namespace detail {
struct usertype_counter
{
static std::size_t next_id()
{
static std::size_t counter = 0;
return counter++;
}
};
template <typename T>
struct usertype
{
static std::string id()
{
static std::size_t id = usertype_counter::next_id();
return std::to_string(id);
}
};
namespace typemap
{
// type names are stored in a hash on interpreter when registered with
// unique id keys generated by usertype counter
inline hash get(PerlInterpreter* my_perl)
{
HV* hv = get_hv("__perlbind::typemap", GV_ADD);
return reinterpret_cast<HV*>(SvREFCNT_inc(hv));
}
template <typename T>
const char* get_name(PerlInterpreter* my_perl)
{
auto typemap = detail::typemap::get(my_perl);
auto type_id = detail::template usertype<T>::id();
return typemap.exists(type_id) ? typemap[type_id].c_str() : nullptr;
}
} // namespace typemap
} // namespace detail
} // namespace perlbind
+25
View File
@@ -0,0 +1,25 @@
#pragma once
namespace perlbind {
struct type_base
{
type_base() : my_perl(PERL_GET_THX) {}
type_base(PerlInterpreter* interp) : my_perl(interp) {}
PerlInterpreter* my_perl = nullptr;
};
// helper type to allow null object reference arguments in bindings
template <typename T>
struct nullable
{
static_assert(std::is_pointer<T>::value, "nullable<T> 'T' must be pointer");
nullable() = default;
nullable(T ptr) : m_ptr(ptr) {}
T get() { return m_ptr; }
private:
T m_ptr = nullptr;
};
} // namespace perlbind
+50
View File
@@ -0,0 +1,50 @@
#pragma once
#include <string>
#include <typeinfo>
#ifndef _MSC_VER
#include <cxxabi.h>
#endif
namespace perlbind { namespace util {
inline std::string demangle(const char* name)
{
#ifndef _MSC_VER
int status = 0;
char* res = abi::__cxa_demangle(name, nullptr, nullptr, &status);
if (res)
{
std::string demangled = res;
free(res);
return demangled;
}
return "<unknown>";
#else
return name;
#endif
}
template <typename... Args>
struct type_name;
template <>
struct type_name<>
{
static std::string str() { return "void"; }
};
template <typename T>
struct type_name<T>
{
static std::string str() { return demangle(typeid(T).name()); }
};
template <typename T, typename... Args>
struct type_name<T, Args...>
{
static std::string str() { return type_name<T>::str() + "," + type_name<Args...>::str(); }
};
} // namespace util
} // namespace perlbind
+10
View File
@@ -0,0 +1,10 @@
#pragma once
constexpr int perlbind_version_major = 1;
constexpr int perlbind_version_minor = 0;
constexpr int perlbind_version_patch = 0;
constexpr int perlbind_version()
{
return perlbind_version_major * 10000 + perlbind_version_minor * 100 + perlbind_version_patch;
}
+15
View File
@@ -0,0 +1,15 @@
#include <perlbind/perlbind.h>
namespace perlbind { namespace detail {
extern "C" int gc(pTHX_ SV* sv, MAGIC* mg)
{
auto pfunc = INT2PTR(perlbind::detail::function_base*, SvIV(sv));
delete pfunc;
return 1;
}
const MGVTBL function_base::mgvtbl = { 0, 0, 0, 0, gc, 0, 0, 0 };
} // namespace detail
} // namespace perlbind
+107
View File
@@ -0,0 +1,107 @@
#include <perlbind/perlbind.h>
#include <perlbind/iterator.h>
#include <stdexcept>
namespace perlbind {
hash::hash(scalar ref)
: type_base(ref.my_perl)
{
if (!ref.is_hash_ref())
throw std::runtime_error("cannot construct hash from non-hash reference");
reset(reinterpret_cast<HV*>(SvREFCNT_inc(*ref)));
}
hash::hash(scalar_proxy proxy)
: hash(scalar(SvREFCNT_inc(proxy.sv())))
{}
scalar hash::at(const char* key)
{
return at(key, strlen(key));
}
scalar hash::at(const std::string& key)
{
return at(key.c_str(), key.size());
}
scalar hash::at(const char* key, size_t size)
{
SV** sv = hv_fetch(m_hv, key, static_cast<I32>(size), 1);
return SvREFCNT_inc(*sv);
}
void hash::insert(const char* key, scalar value)
{
insert(key, strlen(key), value);
}
void hash::insert(const std::string& key, scalar value)
{
insert(key.c_str(), key.size(), value);
}
scalar_proxy hash::operator[](const std::string& key)
{
return scalar_proxy(my_perl, at(key.c_str(), key.size()));
}
hash::iterator hash::begin() const noexcept
{
hv_iterinit(m_hv);
return { my_perl, m_hv, hv_iternext(m_hv) };
}
hash::iterator hash::end() const noexcept
{
return { my_perl, m_hv, nullptr };
}
hash::iterator hash::find(const char* key)
{
return find(key, static_cast<I32>(strlen(key)));
}
hash::iterator hash::find(const std::string& key)
{
return find(key.c_str(), static_cast<I32>(key.size()));
}
hash::iterator hash::find(const char* key, size_t size)
{
// key sv made mortal with SVs_TEMP flag
SV* keysv = newSVpvn_flags(key, static_cast<I32>(size), SVs_TEMP);
HE* he = hv_fetch_ent(m_hv, keysv, 0, 0);
return { my_perl, m_hv, he };
}
void hash::insert(const char* key, size_t size, scalar value)
{
if (!hv_store(m_hv, key, static_cast<I32>(size), SvREFCNT_inc(value), 0))
{
SvREFCNT_dec(value);
}
}
HV* hash::copy_hash(HV* other) noexcept
{
HV* hv = newHV();
hv_iterinit(other);
while (HE* entry = hv_iternext(other))
{
size_t key_size;
auto key = HePV(entry, key_size);
auto value = newSVsv(HeVAL(entry));
if (!hv_store(hv, key, static_cast<I32>(key_size), value, HeHASH(entry)))
{
SvREFCNT_dec(value);
}
}
return hv;
}
} // namespace perlbind
+98
View File
@@ -0,0 +1,98 @@
#include <perlbind/perlbind.h>
#include <fstream>
#include <sstream>
#include <stdexcept>
EXTERN_C
{
void boot_DynaLoader(pTHX_ CV* cv);
static void xs_init(pTHX)
{
newXS(const_cast<char*>("DynaLoader::boot_DynaLoader"), boot_DynaLoader, const_cast<char*>(__FILE__));
}
}
namespace perlbind {
interpreter::interpreter()
: m_is_owner(true)
{
const char* argv[] = { "", "-ew", "0", nullptr };
constexpr int argc = (sizeof(argv) / sizeof(*argv)) - 1;
init(argc, argv);
}
interpreter::interpreter(int argc, const char** argv)
: m_is_owner(true)
{
init(argc, argv);
}
void interpreter::init(int argc, const char** argv)
{
char** argvs = const_cast<char**>(argv);
char** env = { nullptr };
// PERL_SYS_INIT3 and PERL_SYS_TERM should only be called once per program
PERL_SYS_INIT3(&argc, &argvs, &env);
my_perl = perl_alloc();
PERL_SET_CONTEXT(my_perl);
PL_perl_destruct_level = 1;
perl_construct(my_perl);
perl_parse(my_perl, xs_init, argc, argvs, nullptr);
perl_run(my_perl);
}
interpreter::~interpreter()
{
if (m_is_owner)
{
PL_perl_destruct_level = 1;
perl_destruct(my_perl);
perl_free(my_perl);
PERL_SYS_TERM();
}
}
void interpreter::load_script(std::string packagename, std::string filename)
{
struct stat st{};
if (stat(filename.c_str(), &st) != 0)
{
throw std::runtime_error("Unable to read perl file '" + filename + "'");
}
std::ifstream ifs(filename);
std::stringstream buffer;
buffer << "package " << packagename << "; " << ifs.rdbuf();
try
{
eval(buffer.str().c_str());
}
catch (std::exception& e)
{
throw std::runtime_error("Error loading script '" + filename + "':\n " + e.what());
}
}
void interpreter::eval(const char* str)
{
SV* sv = eval_pv(str, 0);
if (sv == &PL_sv_undef)
{
SV* err = get_sv("@", 0);
if (err && err->sv_u.svu_pv[0])
{
throw std::runtime_error(err->sv_u.svu_pv);
}
throw std::runtime_error("unknown error in eval()");
}
}
} // namespace perlbind
+88
View File
@@ -0,0 +1,88 @@
#include <perlbind/perlbind.h>
namespace perlbind {
namespace detail {
extern "C" void xsub(PerlInterpreter* my_perl, CV* cv);
} // namespace detail
void package::add_impl(const char* name, detail::function_base* function)
{
std::string export_name = m_name + "::" + name;
// the sv is assigned a magic metamethod table to delete the function
// object when perl frees the sv
SV* sv = newSViv(PTR2IV(function));
sv_magicext(sv, nullptr, PERL_MAGIC_ext, &detail::function_base::mgvtbl, nullptr, 0);
CV* cv = get_cv(export_name.c_str(), 0);
if (!cv)
{
cv = newXS(export_name.c_str(), &detail::xsub, __FILE__);
CvXSUBANY(cv).any_ptr = function;
}
else // function exists, remove target to search overloads when called
{
CvXSUBANY(cv).any_ptr = nullptr;
}
// create an array with same name to store overloads in the CV's GV
AV* av = GvAV(CvGV(cv));
if (!av)
{
av = get_av(export_name.c_str(), GV_ADD);
}
array overloads = reinterpret_cast<AV*>(SvREFCNT_inc(av));
overloads.push_back(sv); // giving only ref to GV array
}
extern "C" void detail::xsub(PerlInterpreter* my_perl, CV* cv)
{
// croak does not unwind so inner calls throw exceptions to prevent leaks
try
{
detail::xsub_stack stack(my_perl, cv);
auto target = static_cast<detail::function_base*>(CvXSUBANY(cv).any_ptr);
if (target)
{
return target->call(stack);
}
// find first compatible overload
AV* av = GvAV(CvGV(cv));
array functions = reinterpret_cast<AV*>(SvREFCNT_inc(av));
for (const auto& function : functions)
{
auto func = INT2PTR(detail::function_base*, SvIV(function.sv()));
if (func->is_compatible(stack))
{
return func->call(stack);
}
}
SV* err = newSVpvf("no overload of '%s' matched the %d argument(s):\n (%s)\ncandidates:\n ",
stack.name().c_str(), stack.size(), stack.types().c_str());
for (const auto& function : functions)
{
auto func = INT2PTR(detail::function_base*, SvIV(function.sv()));
Perl_sv_catpvf(aTHX_ err, "%s\n ", func->get_signature().c_str());
}
err = sv_2mortal(err);
throw std::runtime_error(SvPV_nolen(err));
}
catch (std::exception& e)
{
Perl_croak(aTHX_ "%s", e.what());
}
catch (...)
{
Perl_croak(aTHX_ "unhandled exception");
}
}
} // namespace perlbind
+112
View File
@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="perlbind::scalar">
<AlternativeType Name="perlbind::reference" />
<DisplayString>{{ m_sv={(void*)m_sv} refcnt={m_sv->sv_refcnt,d} type={(svtype)(m_sv->sv_flags &amp; 0xff),d} }}</DisplayString>
<Expand>
<ExpandedItem>m_sv</ExpandedItem>
</Expand>
</Type>
<Type Name="perlbind::array">
<DisplayString Condition="m_av != nullptr">{{ size={(m_av->sv_any)->xav_fill + 1,d} refcnt={m_av->sv_refcnt,d} }</DisplayString>
<Expand>
<ExpandedItem>m_av</ExpandedItem>
</Expand>
</Type>
<Type Name="perlbind::hash">
<DisplayString Condition="m_hv != nullptr">{{ size={(m_hv->sv_any)->xhv_keys,d} refcnt={m_hv->sv_refcnt,d} }}</DisplayString>
<Expand>
<ExpandedItem>m_hv</ExpandedItem>
</Expand>
</Type>
<Type Name="sv">
<AlternativeType Name="cv" />
<DisplayString>{{ refcnt={sv_refcnt,d} type={(svtype)(sv_flags &amp; 0xff),d} }}</DisplayString>
<Expand>
<Item Name="[refcnt]">sv_refcnt,d</Item>
<Item Name="[type]">(svtype)(sv_flags &amp; 0xff),d</Item>
<Item Name="[reference]" Condition="(sv_flags &amp; 0x00000800)">sv_u.svu_rv</Item>
<!-- SVt_PVAV -->
<Item Name="[array]" Condition="((svtype)(sv_flags &amp; 0xff)) == 11">(av*)this</Item>
<!-- SVt_PVHV -->
<Item Name="[hash]" Condition="((svtype)(sv_flags &amp; 0xff)) == 12">(hv*)this</Item>
<!-- SVt_PVGV -->
<Item Name="[glob]" Condition="((svtype)(sv_flags &amp; 0xff)) == 9">(gv*)this</Item>
<!-- SVt_PVMG -->
<Item Name="[magic]" Condition="((svtype)(sv_flags &amp; 0xff)) == 7">((XPVMG*)(sv_any))</Item>
<!--<ExpandedItem>sv_u</ExpandedItem>-->
<Item Name="svu_pv">sv_u.svu_pv,na</Item>
<Item Name="svu_iv">sv_u.svu_iv,i</Item>
<Item Name="svu_uv">sv_u.svu_uv</Item>
<Item Name="svu_nv">sv_u.svu_nv,f</Item>
<Item Name="svu_rv">sv_u.svu_rv</Item>
</Expand>
</Type>
<Type Name="av">
<!--
These might be dependent on perl version
#define AvARRAY(av) ((av)->sv_u.svu_array)
#define AvALLOC(av) ((XPVAV*) SvANY(av))->xav_alloc
#define AvMAX(av) ((XPVAV*) SvANY(av))->xav_max
#define AvFILLp(av) ((XPVAV*) SvANY(av))->xav_fill
-->
<DisplayString>{{ size={(sv_any)->xav_fill + 1,d} refcnt={sv_refcnt,d} type={(svtype)(sv_flags &amp; 0xff),d} }</DisplayString>
<Expand>
<Item Name="[refcnt]">sv_refcnt,d</Item>
<Item Name="[size]">(sv_any)->xav_fill + 1</Item>
<Item Name="[capacity]">(sv_any)->xav_max</Item>
<ArrayItems>
<Size>(sv_any)->xav_fill + 1</Size>
<ValuePointer>(sv_u).svu_array</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="hv">
<!--
These might be dependent on perl version
SvANY(hv): (m_hv->sv_any)
HvMAX(hv): ((m_hv->sv_any)->xhv_max
HvARRAY(hv): ((m_hv->sv_u).svu_hash
HvAUX(hv): (xpvhv_aux*)&(((m_hv->sv_u)->svu_hash)[((m_hv->sv_any)->xhv_max + 1]
-->
<DisplayString>{{ size={(sv_any)->xhv_keys,d} refcnt={sv_refcnt,d} type={(svtype)(sv_flags &amp; 0xff),d} }}</DisplayString>
<Expand>
<Item Name="[refcnt]">sv_refcnt,d</Item>
<Item Name="[size]">(sv_any)->xhv_keys</Item>
<Item Name="[capacity]">(sv_any)->xhv_max</Item>
<CustomListItems MaxItemsPerView="5000">
<Variable Name="index" InitialValue="0"/>
<Variable Name="bucket_inc" InitialValue="0"/>
<Variable Name="max_index" InitialValue="(sv_any)->xhv_max"/>
<Variable Name="bucket_array" InitialValue="(sv_u).svu_hash"/>
<Variable Name="entry" InitialValue="(sv_u).svu_hash[0]"/>
<Loop>
<If Condition="entry == nullptr">
<Exec>index++</Exec>
<Exec>bucket_inc = __findnonnull(bucket_array + index, max_index - index)</Exec>
<Break Condition="bucket_inc == -1" />
<Exec>index += bucket_inc</Exec>
<Exec>entry = bucket_array[index]</Exec>
</If>
<Item Name="[{ (entry->hent_hek)->hek_key,na }]">(entry->he_valu).hent_val</Item>
<Exec>entry = entry->hent_next</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
<Type Name="gv">
<DisplayString>{{ refcnt={sv_refcnt,d} type={(svtype)(sv_flags &amp; 0xff),d} }}</DisplayString>
<Expand>
<Item Name="[refcnt]">sv_refcnt,d</Item>
<Item Name="[type]">(svtype)(sv_flags &amp; 0xff),d</Item>
<Item Name="[sv]" Condition="(sv_u.svu_gp)->gp_sv != nullptr">(sv_u.svu_gp)->gp_sv</Item>
<Item Name="[cv]" Condition="(sv_u.svu_gp)->gp_cv != nullptr">(sv_u.svu_gp)->gp_cv</Item>
<Item Name="[array]" Condition="(sv_u.svu_gp)->gp_av != nullptr">(sv_u.svu_gp)->gp_av</Item>
<Item Name="[hash]" Condition="(sv_u.svu_gp)->gp_hv != nullptr">(sv_u.svu_gp)->gp_hv</Item>
<Item Name="svu_gp">(sv_u.svu_gp)</Item>
</Expand>
</Type>
</AutoVisualizer>
+9 -9
View File
@@ -1,18 +1,18 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.2)
SET(qserv_sources
database.cpp
lfguild.cpp
queryserv.cpp
queryservconfig.cpp
worldserver.cpp
database.cpp
lfguild.cpp
queryserv.cpp
queryservconfig.cpp
worldserver.cpp
)
SET(qserv_headers
database.h
lfguild.h
queryservconfig.h
worldserver.h
database.h
lfguild.h
queryservconfig.h
worldserver.h
)
ADD_EXECUTABLE(queryserv ${qserv_sources} ${qserv_headers})
+15 -15
View File
@@ -12,25 +12,25 @@ extern Database database;
PlayerLookingForGuild::PlayerLookingForGuild(char *Name, char *Comments, uint32 Level, uint32 Class, uint32 AACount, uint32 Timezone, uint32 TimePosted)
{
this->Name = Name;
this->Comments = Comments;
this->Level = Level;
this->Class = Class;
this->AACount = AACount;
this->TimeZone = Timezone;
this->TimePosted = TimePosted;
Name = Name;
Comments = Comments;
Level = Level;
Class = Class;
AACount = AACount;
TimeZone = Timezone;
TimePosted = TimePosted;
}
GuildLookingForPlayers::GuildLookingForPlayers(char *Name, char *Comments, uint32 FromLevel, uint32 ToLevel, uint32 Classes, uint32 AACount, uint32 Timezone, uint32 TimePosted)
{
this->Name = Name;
this->Comments = Comments;
this->FromLevel = FromLevel;
this->ToLevel = ToLevel;
this->Classes = Classes;
this->AACount = AACount;
this->TimeZone = Timezone;
this->TimePosted = TimePosted;
Name = Name;
Comments = Comments;
FromLevel = FromLevel;
ToLevel = ToLevel;
Classes = Classes;
AACount = AACount;
TimeZone = Timezone;
TimePosted = TimePosted;
}
bool LFGuildManager::LoadDatabase()
+21 -14
View File
@@ -24,6 +24,7 @@
#include "../common/servertalk.h"
#include "../common/platform.h"
#include "../common/crash.h"
#include "../common/string_util.h"
#include "../common/event/event_loop.h"
#include "../common/timer.h"
#include "database.h"
@@ -32,21 +33,24 @@
#include "worldserver.h"
#include <list>
#include <signal.h>
#include <thread>
volatile bool RunLoops = true;
Database database;
LFGuildManager lfguildmanager;
std::string WorldShortName;
Database database;
LFGuildManager lfguildmanager;
std::string WorldShortName;
const queryservconfig *Config;
WorldServer *worldserver = 0;
EQEmuLogSys LogSys;
WorldServer *worldserver = 0;
EQEmuLogSys LogSys;
void CatchSignal(int sig_num) {
void CatchSignal(int sig_num)
{
RunLoops = false;
}
int main() {
int main()
{
RegisterExecutablePlatform(ExePlatformQueryServ);
LogSys.LoadLogSettingsDefaults();
set_exception_handler();
@@ -58,7 +62,7 @@ int main() {
return 1;
}
Config = queryservconfig::get();
Config = queryservconfig::get();
WorldShortName = Config->ShortName;
LogInfo("Connecting to MySQL");
@@ -69,7 +73,8 @@ int main() {
Config->QSDatabaseUsername.c_str(),
Config->QSDatabasePassword.c_str(),
Config->QSDatabaseDB.c_str(),
Config->QSDatabasePort)) {
Config->QSDatabasePort
)) {
LogInfo("Cannot continue without a database connection");
return 1;
}
@@ -78,11 +83,11 @@ int main() {
->LoadLogDatabaseSettings()
->StartFileLogs();
if (signal(SIGINT, CatchSignal) == SIG_ERR) {
if (signal(SIGINT, CatchSignal) == SIG_ERR) {
LogInfo("Could not set signal handler");
return 1;
}
if (signal(SIGTERM, CatchSignal) == SIG_ERR) {
if (signal(SIGTERM, CatchSignal) == SIG_ERR) {
LogInfo("Could not set signal handler");
return 1;
}
@@ -94,10 +99,11 @@ int main() {
/* Load Looking For Guild Manager */
lfguildmanager.LoadDatabase();
while(RunLoops) {
while (RunLoops) {
Timer::SetCurrentTime();
if(LFGuildExpireTimer.Check())
if (LFGuildExpireTimer.Check()) {
lfguildmanager.ExpireEntries();
}
EQ::EventLoop::Get().Process();
Sleep(5);
@@ -105,7 +111,8 @@ int main() {
LogSys.CloseFileLogs();
}
void UpdateWindowTitle(char* iNewTitle) {
void UpdateWindowTitle(char *iNewTitle)
{
#ifdef _WINDOWS
char tmp[500];
if (iNewTitle) {
+104 -98
View File
@@ -37,10 +37,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include <string.h>
#include <time.h>
extern WorldServer worldserver;
extern WorldServer worldserver;
extern const queryservconfig *Config;
extern Database database;
extern LFGuildManager lfguildmanager;
extern Database database;
extern LFGuildManager lfguildmanager;
WorldServer::WorldServer()
{
@@ -52,7 +52,13 @@ WorldServer::~WorldServer()
void WorldServer::Connect()
{
m_connection = std::make_unique<EQ::Net::ServertalkClient>(Config->WorldIP, Config->WorldTCPPort, false, "QueryServ", Config->SharedKey);
m_connection = std::make_unique<EQ::Net::ServertalkClient>(
Config->WorldIP,
Config->WorldTCPPort,
false,
"QueryServ",
Config->SharedKey
);
m_connection->OnMessage(std::bind(&WorldServer::HandleMessage, this, std::placeholders::_1, std::placeholders::_2));
}
@@ -80,109 +86,109 @@ bool WorldServer::Connected() const
void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
{
switch (opcode) {
case 0: {
break;
}
case ServerOP_KeepAlive: {
break;
}
case ServerOP_Speech: {
Server_Speech_Struct *SSS = (Server_Speech_Struct*)p.Data();
std::string tmp1 = SSS->from;
std::string tmp2 = SSS->to;
database.AddSpeech(tmp1.c_str(), tmp2.c_str(), SSS->message, SSS->minstatus, SSS->guilddbid, SSS->type);
break;
}
case ServerOP_QSPlayerLogTrades: {
QSPlayerLogTrade_Struct *QS = (QSPlayerLogTrade_Struct*)p.Data();
database.LogPlayerTrade(QS, QS->_detail_count);
break;
}
case ServerOP_QSPlayerDropItem: {
QSPlayerDropItem_Struct *QS = (QSPlayerDropItem_Struct *) p.Data();
database.LogPlayerDropItem(QS);
break;
}
case ServerOP_QSPlayerLogHandins: {
QSPlayerLogHandin_Struct *QS = (QSPlayerLogHandin_Struct*)p.Data();
database.LogPlayerHandin(QS, QS->_detail_count);
break;
}
case ServerOP_QSPlayerLogNPCKills: {
QSPlayerLogNPCKill_Struct *QS = (QSPlayerLogNPCKill_Struct*)p.Data();
uint32 Members = (uint32)(p.Length() - sizeof(QSPlayerLogNPCKill_Struct));
if (Members > 0) Members = Members / sizeof(QSPlayerLogNPCKillsPlayers_Struct);
database.LogPlayerNPCKill(QS, Members);
break;
}
case ServerOP_QSPlayerLogDeletes: {
QSPlayerLogDelete_Struct *QS = (QSPlayerLogDelete_Struct*)p.Data();
uint32 Items = QS->char_count;
database.LogPlayerDelete(QS, Items);
break;
}
case ServerOP_QSPlayerLogMoves: {
QSPlayerLogMove_Struct *QS = (QSPlayerLogMove_Struct*)p.Data();
uint32 Items = QS->char_count;
database.LogPlayerMove(QS, Items);
break;
}
case ServerOP_QSPlayerLogMerchantTransactions: {
QSMerchantLogTransaction_Struct *QS = (QSMerchantLogTransaction_Struct*)p.Data();
uint32 Items = QS->char_count + QS->merchant_count;
database.LogMerchantTransaction(QS, Items);
break;
}
case ServerOP_QueryServGeneric: {
/*
The purpose of ServerOP_QueryServerGeneric is so that we don't have to add code to world just to relay packets
each time we add functionality to queryserv.
case 0: {
break;
}
case ServerOP_KeepAlive: {
break;
}
case ServerOP_Speech: {
Server_Speech_Struct *SSS = (Server_Speech_Struct *) p.Data();
std::string tmp1 = SSS->from;
std::string tmp2 = SSS->to;
database.AddSpeech(tmp1.c_str(), tmp2.c_str(), SSS->message, SSS->minstatus, SSS->guilddbid, SSS->type);
break;
}
case ServerOP_QSPlayerLogTrades: {
QSPlayerLogTrade_Struct *QS = (QSPlayerLogTrade_Struct *) p.Data();
database.LogPlayerTrade(QS, QS->_detail_count);
break;
}
case ServerOP_QSPlayerDropItem: {
QSPlayerDropItem_Struct *QS = (QSPlayerDropItem_Struct *) p.Data();
database.LogPlayerDropItem(QS);
break;
}
case ServerOP_QSPlayerLogHandins: {
QSPlayerLogHandin_Struct *QS = (QSPlayerLogHandin_Struct *) p.Data();
database.LogPlayerHandin(QS, QS->_detail_count);
break;
}
case ServerOP_QSPlayerLogNPCKills: {
QSPlayerLogNPCKill_Struct *QS = (QSPlayerLogNPCKill_Struct *) p.Data();
uint32 Members = (uint32) (p.Length() - sizeof(QSPlayerLogNPCKill_Struct));
if (Members > 0) { Members = Members / sizeof(QSPlayerLogNPCKillsPlayers_Struct); }
database.LogPlayerNPCKill(QS, Members);
break;
}
case ServerOP_QSPlayerLogDeletes: {
QSPlayerLogDelete_Struct *QS = (QSPlayerLogDelete_Struct *) p.Data();
uint32 Items = QS->char_count;
database.LogPlayerDelete(QS, Items);
break;
}
case ServerOP_QSPlayerLogMoves: {
QSPlayerLogMove_Struct *QS = (QSPlayerLogMove_Struct *) p.Data();
uint32 Items = QS->char_count;
database.LogPlayerMove(QS, Items);
break;
}
case ServerOP_QSPlayerLogMerchantTransactions: {
QSMerchantLogTransaction_Struct *QS = (QSMerchantLogTransaction_Struct *) p.Data();
uint32 Items = QS->char_count + QS->merchant_count;
database.LogMerchantTransaction(QS, Items);
break;
}
case ServerOP_QueryServGeneric: {
/*
The purpose of ServerOP_QueryServerGeneric is so that we don't have to add code to world just to relay packets
each time we add functionality to queryserv.
A ServerOP_QueryServGeneric packet has the following format:
A ServerOP_QueryServGeneric packet has the following format:
uint32 SourceZoneID
uint32 SourceInstanceID
char OriginatingCharacterName[0]
- Null terminated name of the character this packet came from. This could be just
- an empty string if it has no meaning in the context of a particular packet.
uint32 Type
uint32 SourceZoneID
uint32 SourceInstanceID
char OriginatingCharacterName[0]
- Null terminated name of the character this packet came from. This could be just
- an empty string if it has no meaning in the context of a particular packet.
uint32 Type
The 'Type' field is a 'sub-opcode'. A value of 0 is used for the LFGuild packets. The next feature to be added
to queryserv would use 1, etc.
The 'Type' field is a 'sub-opcode'. A value of 0 is used for the LFGuild packets. The next feature to be added
to queryserv would use 1, etc.
Obviously, any fields in the packet following the 'Type' will be unique to the particular type of packet. The
'Generic' in the name of this ServerOP code relates to the four header fields.
*/
Obviously, any fields in the packet following the 'Type' will be unique to the particular type of packet. The
'Generic' in the name of this ServerOP code relates to the four header fields.
*/
auto from = p.GetCString(8);
uint32 Type = p.GetUInt32(8 + from.length() + 1);
auto from = p.GetCString(8);
uint32 Type = p.GetUInt32(8 + from.length() + 1);
switch (Type) {
case QSG_LFGuild: {
switch (Type) {
case QSG_LFGuild: {
ServerPacket pack;
pack.pBuffer = (uchar *) p.Data();
pack.opcode = opcode;
pack.size = (uint32) p.Length();
lfguildmanager.HandlePacket(&pack);
pack.pBuffer = nullptr;
break;
}
default:
LogInfo("Received unhandled ServerOP_QueryServGeneric", Type);
break;
}
break;
}
case ServerOP_QSSendQuery: {
/* Process all packets here */
ServerPacket pack;
pack.pBuffer = (uchar*)p.Data();
pack.opcode = opcode;
pack.size = (uint32)p.Length();
lfguildmanager.HandlePacket(&pack);
pack.pBuffer = (uchar *) p.Data();
pack.opcode = opcode;
pack.size = (uint32) p.Length();
database.GeneralQueryReceive(&pack);
pack.pBuffer = nullptr;
break;
}
default:
LogInfo("Received unhandled ServerOP_QueryServGeneric", Type);
break;
}
break;
}
case ServerOP_QSSendQuery: {
/* Process all packets here */
ServerPacket pack;
pack.pBuffer = (uchar*)p.Data();
pack.opcode = opcode;
pack.size = (uint32)p.Length();
database.GeneralQueryReceive(&pack);
pack.pBuffer = nullptr;
break;
}
}
}
+14 -13
View File
@@ -18,24 +18,25 @@
#ifndef WORLDSERVER_H
#define WORLDSERVER_H
#include <mutex>
#include "../common/eq_packet_structs.h"
#include "../common/net/servertalk_client_connection.h"
class WorldServer
{
public:
WorldServer();
~WorldServer();
class WorldServer {
public:
WorldServer();
~WorldServer();
void Connect();
bool SendPacket(ServerPacket* pack);
std::string GetIP() const;
uint16 GetPort() const;
bool Connected() const;
void Connect();
bool SendPacket(ServerPacket *pack);
std::string GetIP() const;
uint16 GetPort() const;
bool Connected() const;
void HandleMessage(uint16 opcode, const EQ::Net::Packet &p);
private:
std::unique_ptr<EQ::Net::ServertalkClient> m_connection;
void HandleMessage(uint16 opcode, const EQ::Net::Packet &p);
private:
std::unique_ptr<EQ::Net::ServertalkClient> m_connection;
};
#endif
+52 -21
View File
@@ -32,15 +32,19 @@
#include "worldserver.h"
#include <list>
#include <signal.h>
#include <csignal>
#include <thread>
#include "../common/net/tcp_server.h"
#include "../common/net/servertalk_client_connection.h"
#include "../common/discord_manager.h"
ChatChannelList *ChannelList;
Clientlist *g_Clientlist;
EQEmuLogSys LogSys;
Database database;
WorldServer *worldserver = nullptr;
DiscordManager discord_manager;
const ucsconfig *Config;
@@ -49,19 +53,49 @@ std::string WorldShortName;
uint32 ChatMessagesSent = 0;
uint32 MailMessagesSent = 0;
volatile bool RunLoops = true;
void CatchSignal(int sig_num) {
RunLoops = false;
}
std::string GetMailPrefix() {
return "SOE.EQ." + WorldShortName + ".";
}
void crash_func() {
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
int* p=0;
*p=0;
}
void Shutdown() {
LogInfo("Shutting down...");
ChannelList->RemoveAllChannels();
g_Clientlist->CloseAllConnections();
LogSys.CloseFileLogs();
}
int caught_loop = 0;
void CatchSignal(int sig_num) {
LogInfo("Caught signal [{}]", sig_num);
EQ::EventLoop::Get().Shutdown();
caught_loop++;
// when signal handler is incapable of exiting properly
if (caught_loop > 1) {
LogInfo("In a signal handler loop and process is incapable of exiting properly, forcefully cleaning up");
ChannelList->RemoveAllChannels();
g_Clientlist->CloseAllConnections();
LogSys.CloseFileLogs();
std::exit(0);
}
}
void DiscordQueueListener() {
while (caught_loop == 0) {
discord_manager.ProcessMessageQueue();
Sleep(100);
}
}
int main() {
RegisterExecutablePlatform(ExePlatformUCS);
LogSys.LoadLogSettingsDefaults();
@@ -134,17 +168,19 @@ int main() {
database.LoadChatChannels();
if (signal(SIGINT, CatchSignal) == SIG_ERR) {
LogInfo("Could not set signal handler");
return 1;
}
if (signal(SIGTERM, CatchSignal) == SIG_ERR) {
LogInfo("Could not set signal handler");
return 1;
}
std::signal(SIGINT, CatchSignal);
std::signal(SIGTERM, CatchSignal);
std::signal(SIGKILL, CatchSignal);
std::signal(SIGSEGV, CatchSignal);
std::thread(DiscordQueueListener).detach();
worldserver = new WorldServer;
// uncomment to simulate timed crash for catching SIGSEV
// std::thread crash_test(crash_func);
// crash_test.detach();
auto loop_fn = [&](EQ::Timer* t) {
Timer::SetCurrentTime();
@@ -166,12 +202,7 @@ int main() {
EQ::EventLoop::Get().Run();
ChannelList->RemoveAllChannels();
g_Clientlist->CloseAllConnections();
LogSys.CloseFileLogs();
Shutdown();
}
void UpdateWindowTitle(char* iNewTitle) {
+15 -3
View File
@@ -26,6 +26,7 @@ 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 <iostream>
#include <string.h>
@@ -35,10 +36,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdlib.h>
#include <stdarg.h>
extern WorldServer worldserver;
extern Clientlist *g_Clientlist;
extern WorldServer worldserver;
extern Clientlist *g_Clientlist;
extern const ucsconfig *Config;
extern Database database;
extern Database database;
extern DiscordManager discord_manager;
void ProcessMailTo(Client *c, std::string from, std::string subject, std::string message);
@@ -72,6 +74,16 @@ void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p)
{
break;
}
case ServerOP_DiscordWebhookMessage: {
auto *q = (DiscordWebhookMessage_Struct *) p.Data();
discord_manager.QueueWebhookMessage(
q->webhook_id,
q->message
);
break;
}
case ServerOP_UCSMessage:
{
char *Buffer = (char *)pack->pBuffer;
+41 -35
View File
@@ -8,40 +8,6 @@
*
]]
MonkACBonusWeight = RuleI.Get(Rule.MonkACBonusWeight);
NPCACFactor = RuleR.Get(Rule.NPCACFactor);
OldACSoftcapRules = RuleB.Get(Rule.OldACSoftcapRules);
ClothACSoftcap = RuleI.Get(Rule.ClothACSoftcap);
LeatherACSoftcap = RuleI.Get(Rule.LeatherACSoftcap);
MonkACSoftcap = RuleI.Get(Rule.MonkACSoftcap);
ChainACSoftcap = RuleI.Get(Rule.ChainACSoftcap);
PlateACSoftcap = RuleI.Get(Rule.PlateACSoftcap);
AAMitigationACFactor = RuleR.Get(Rule.AAMitigationACFactor);
WarriorACSoftcapReturn = RuleR.Get(Rule.WarriorACSoftcapReturn);
KnightACSoftcapReturn = RuleR.Get(Rule.KnightACSoftcapReturn);
LowPlateChainACSoftcapReturn = RuleR.Get(Rule.LowPlateChainACSoftcapReturn);
LowChainLeatherACSoftcapReturn = RuleR.Get(Rule.LowChainLeatherACSoftcapReturn);
CasterACSoftcapReturn = RuleR.Get(Rule.CasterACSoftcapReturn);
MiscACSoftcapReturn = RuleR.Get(Rule.MiscACSoftcapReturn);
WarACSoftcapReturn = RuleR.Get(Rule.WarACSoftcapReturn);
ClrRngMnkBrdACSoftcapReturn = RuleR.Get(Rule.ClrRngMnkBrdACSoftcapReturn);
PalShdACSoftcapReturn = RuleR.Get(Rule.PalShdACSoftcapReturn);
DruNecWizEncMagACSoftcapReturn = RuleR.Get(Rule.DruNecWizEncMagACSoftcapReturn);
RogShmBstBerACSoftcapReturn = RuleR.Get(Rule.RogShmBstBerACSoftcapReturn);
SoftcapFactor = RuleR.Get(Rule.SoftcapFactor);
ACthac0Factor = RuleR.Get(Rule.ACthac0Factor);
ACthac20Factor = RuleR.Get(Rule.ACthac20Factor);
BaseHitChance = RuleR.Get(Rule.BaseHitChance);
NPCBonusHitChance = RuleR.Get(Rule.NPCBonusHitChance);
HitFalloffMinor = RuleR.Get(Rule.HitFalloffMinor);
HitFalloffModerate = RuleR.Get(Rule.HitFalloffModerate);
HitFalloffMajor = RuleR.Get(Rule.HitFalloffMajor);
HitBonusPerLevel = RuleR.Get(Rule.HitBonusPerLevel);
AgiHitFactor = RuleR.Get(Rule.AgiHitFactor);
WeaponSkillFalloff = RuleR.Get(Rule.WeaponSkillFalloff);
ArcheryHitPenalty = RuleR.Get(Rule.ArcheryHitPenalty);
UseOldDamageIntervalRules = RuleB.Get(Rule.UseOldDamageIntervalRules);
CriticalMessageRange = RuleI.Get(Rule.CriticalDamage);
--[[
*
@@ -51,6 +17,41 @@ CriticalMessageRange = RuleI.Get(Rule.CriticalDamage);
*
]]
MonkACBonusWeight = 15;
NPCACFactor = 2.25;
OldACSoftcapRules = false;
ClothACSoftcap = 75;
LeatherACSoftcap = 100;
MonkACSoftcap = 120;
ChainACSoftcap = 200;
PlateACSoftcap = 300;
AAMitigationACFactor = 3.0;
WarriorACSoftcapReturn = 0.45;
KnightACSoftcapReturn = 0.33;
LowPlateChainACSoftcapReturn = 0.23;
LowChainLeatherACSoftcapReturn = 0.17;
CasterACSoftcapReturn = 0.06;
MiscACSoftcapReturn = 0.3;
WarACSoftcapReturn = 0.3448;
ClrRngMnkBrdACSoftcapReturn = 0.3030;
PalShdACSoftcapReturn = 0.3226;
DruNecWizEncMagACSoftcapReturn = 0.2;
RogShmBstBerACSoftcapReturn = 0.25;
SoftcapFactor = 1.88;
ACthac0Factor = 0.55;
ACthac20Factor = 0.55;
BaseHitChance = 69.0;
NPCBonusHitChance = 26.0;
HitFalloffMinor = 5.0;
HitFalloffModerate = 7.0;
HitFalloffMajor = 50.0;
HitBonusPerLevel = 1.2;
AgiHitFactor = 0.01;
WeaponSkillFalloff = 0.33;
ArcheryHitPenalty = 0.25;
UseOldDamageIntervalRules = false;
CriticalMessageRange = RuleI.Get(Rule.CriticalDamage);
MeleeBaseCritChance = 0.0;
ClientBaseCritChance = 0.0;
BerserkBaseCritChance = 6.0;
@@ -851,7 +852,12 @@ function MobGetMeleeMitDmg(defender, attacker, damage, min_damage, mitigation_ra
);
-- Changed from positive to negative per original
damage = damage - (damage * (defender:GetSpellBonuses():MeleeMitigationEffect() + defender:GetItemBonuses():MeleeMitigationEffect() + defender:GetAABonuses():MeleeMitigationEffect()) / 100);
local spell_based_mitigation = defender:GetSpellBonuses():MeleeMitigationEffect() * -1;
local item_based_mitigation = defender:GetItemBonuses():MeleeMitigationEffect() * -1;
local aa_based_mitigation = defender:GetAABonuses():MeleeMitigationEffect() * -1;
-- Changed from positive to negative per original
damage = damage - (damage * (spell_based_mitigation + item_based_mitigation + aa_based_mitigation) / 100);
eq.log_combat(
string.format("[%s] [Mob::GetMeleeMitDmg] Damage [%02f] SpellMit [%i] ItemMit [%i] AAMit [%i] Post All Mit Bonuses",
+1
View File
@@ -203,6 +203,7 @@ OP_DeleteSpell=0x3358
OP_Surname=0x0423
OP_ClearSurname=0x3fb0
OP_FaceChange=0x5578
OP_SetFace=0x1af3
OP_SenseHeading=0x260a
OP_Action=0x744c
OP_ConsiderCorpse=0x5204
+1
View File
@@ -201,6 +201,7 @@ OP_DeleteSpell=0x0736 # C
OP_Surname=0x7547 # C
OP_ClearSurname=0x2edd # C
OP_FaceChange=0x5658 # C
OP_SetFace=0x210a
OP_SenseHeading=0x3887 # C
OP_Action=0x2c27 # C
OP_ConsiderCorpse=0x37a7 # C
+1
View File
@@ -198,6 +198,7 @@ OP_DeleteSpell=0x6D7E #Xinu 02/20/09
OP_Surname=0x683E #Xinu 02/21/09
OP_ClearSurname=0x2613
OP_FaceChange=0x482D #Trevius 01/16/09
OP_SetFace=0x49dc
OP_SenseHeading=0x1237 #Trevius 01/16/09
OP_Action=0x5285 #Trevius 01/16/09
OP_ConsiderCorpse=0x4CBB #Xinu 02/20/09
+1
View File
@@ -205,6 +205,7 @@ OP_DeleteSpell=0x0698 # C
OP_Surname=0x44ae # C
OP_ClearSurname=0x6705 # C
OP_FaceChange=0x37a7 # C
OP_SetFace=0x6cfa
OP_SenseHeading=0x1b8a # C
OP_Action=0x0f14 # C
OP_ConsiderCorpse=0x0a18 # C
+2 -3
View File
@@ -64,11 +64,10 @@ if [ ! -f ./install_variables.txt ]; then
echo ""
echo ""
groupadd eqemu
useradd -g eqemu -d $eqemu_server_directory eqemu
useradd -g eqemu -m -d $eqemu_server_directory eqemu
passwd eqemu
#::: Make server directory and go to it
mkdir $eqemu_server_directory
#::: Go to server directory
cd $eqemu_server_directory
#::: Setup MySQL root user PW
+7
View File
@@ -432,6 +432,13 @@
9176|2022_01_10_checksum_verification.sql|SHOW COLUMNS FROM `account` LIKE 'crc_eqgame'|empty|
9177|2022_03_06_table_structure_changes.sql|SHOW COLUMNS FROM `pets` LIKE 'id'|empty|
9178|2022_03_07_saylink_collation.sql|SELECT * FROM db_version WHERE version >= 9178|empty|
9179|2022_04_30_hp_regen_per_second.sql|SHOW COLUMNS FROM `npc_types` LIKE 'hp_regen_per_second'|empty|
9180|2022_05_01_character_peqzone_flags.sql|SHOW TABLES LIKE 'character_peqzone_flags'|empty|
9181|2022_05_03_task_activity_goal_match_list.sql|SHOW COLUMNS FROM `task_activities` LIKE 'goal_match_list'|empty|
9182|2022_05_02_npc_types_int64.sql|SHOW COLUMNS FROM `npc_types` LIKE 'hp'|missing|bigint
9183|2022_05_07_merchant_data_buckets.sql|SHOW COLUMNS FROM `merchantlist` LIKE 'bucket_comparison'|empty
9184|2022_05_21_schema_consistency.sql|SELECT * FROM db_version WHERE version >= 9184|empty|
9185|2022_05_07_discord_webhooks.sql|SHOW TABLES LIKE 'discord_webhooks'|empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not
@@ -0,0 +1 @@
ALTER TABLE npc_types ADD COLUMN hp_regen_per_second bigint DEFAULT 0 AFTER hp_regen_rate;
@@ -0,0 +1,14 @@
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for character_peqzone_flags
-- ----------------------------
DROP TABLE IF EXISTS `character_peqzone_flags`;
CREATE TABLE `character_peqzone_flags` (
`id` int NOT NULL DEFAULT 0,
`zone_id` int NOT NULL DEFAULT 0,
PRIMARY KEY (`id`, `zone_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;
SET FOREIGN_KEY_CHECKS = 1;
@@ -0,0 +1,4 @@
ALTER TABLE npc_types MODIFY COLUMN hp BIGINT NOT NULL DEFAULT 0;
ALTER TABLE npc_types MODIFY COLUMN mana BIGINT NOT NULL DEFAULT 0;
ALTER TABLE npc_types MODIFY COLUMN hp_regen_rate BIGINT NOT NULL DEFAULT 0;
ALTER TABLE npc_types MODIFY COLUMN mana_regen_rate BIGINT NOT NULL DEFAULT 0;
@@ -0,0 +1 @@
ALTER TABLE task_activities ADD goal_match_list text AFTER goalid;
@@ -0,0 +1,15 @@
CREATE TABLE discord_webhooks
(
id INT auto_increment primary key NULL,
webhook_name varchar(100) NULL,
webhook_url varchar(255) NULL,
created_at DATETIME NULL,
deleted_at DATETIME NULL
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_general_ci;
ALTER TABLE logsys_categories
ADD log_to_discord smallint(11) default 0 AFTER log_to_gmsay;
ALTER TABLE logsys_categories
ADD discord_webhook_id int(11) default 0 AFTER log_to_discord;

Some files were not shown because too many files have changed in this diff Show More