diff --git a/.gitignore b/.gitignore index f155c36bb..daba92762 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,7 @@ vcpkg/ perl/ .idea/* -*cbp \ No newline at end of file +*cbp + +submodules/* +cmake-build-debug/ \ No newline at end of file diff --git a/changelog.txt b/changelog.txt index 84d38df4a..955a0a2e8 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,14 @@ +############################################ +# Deprecated +############################################ +# +# New changelog can be found here +# https://eqemu.gitbook.io/changelog/ +# +############################################ +# Deprecated +############################################ + EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 8/16/2019 == @@ -73,48 +84,48 @@ Uleat: Reworked BotDatabase into a functional add-on for ZoneDatabase == 3/1/2019 == Noudess: Major faction conversion to use client data. - Pull request #802 New min/max personal faction per faction. Use of actual + Pull request #802 New min/max personal faction per faction. Use of actual client mods for race/class/deity. This PR involves major changes to your database and your quests. - The clients recently exposed raw data included + The clients recently exposed raw data included - the min/max personal faction for each faction - the actual faction id the client uses for each faction - - the actual mods that come into play when a PC cons an opponent that + - the actual mods that come into play when a PC cons an opponent that determine your overall con to that faction. - The approach I took resulted in minimal change to the code base. I did - alter the code to enforce the new validated min/max from the client. This - min/max applies to personally earned faction. So if a faction has a min - of 0 and a max of 2000, that means your personally earned value can never - go below 0 or over 2000. The actual con, will, however often do so because - of class/race/deity modifications. I also changed the con ranges, per + The approach I took resulted in minimal change to the code base. I did + alter the code to enforce the new validated min/max from the client. This + min/max applies to personally earned faction. So if a faction has a min + of 0 and a max of 2000, that means your personally earned value can never + go below 0 or over 2000. The actual con, will, however often do so because + of class/race/deity modifications. I also changed the con ranges, per Mackal's data that was proven to be accurate: - Ally = 1100+ - Warmly = 750 to 1099 - Kindly = 500 to 749 - Amiable = 100 to 499 - Indifferent = 0 to 99 - Apprehensive = -1 to -100 - Dubious = -101 to -500 - Threateningly = -501 to -750 - Ready to Attack = -751 + Ally = 1100+ + Warmly = 750 to 1099 + Kindly = 500 to 749 + Amiable = 100 to 499 + Indifferent = 0 to 99 + Apprehensive = -1 to -100 + Dubious = -101 to -500 + Threateningly = -501 to -750 + Ready to Attack = -751 - The above means that dubious is a much smaller range now. For that reason - the scripts modify any custom faction base values to put them in the same + The above means that dubious is a much smaller range now. For that reason + the scripts modify any custom faction base values to put them in the same range, hopefully as the creators of the custom factions intended. - Also to be noted as characters that have a faction between -501 and -700 - wont be dubious anymore, they will be threateningly. This is expected with - the new ranges, but might take players by suprise as the old ranges we used + Also to be noted as characters that have a faction between -501 and -700 + wont be dubious anymore, they will be threateningly. This is expected with + the new ranges, but might take players by suprise as the old ranges we used were more liberal but were incorrect. - The database is changed extensively, but really only content. We're - translating faction_list to use the clients ids. As such every place a + The database is changed extensively, but really only content. We're + translating faction_list to use the clients ids. As such every place a faction_is is used, namely (see below) are being converted. - faction_list @@ -123,41 +134,41 @@ Noudess: Major faction conversion to use client data. - npc_faction_entries (faction_id field only) - faction_values - Quests will also automatically be adjusted. This MUST be done after the - PR sql and before starting the server. This is automated by + Quests will also automatically be adjusted. This MUST be done after the + PR sql and before starting the server. This is automated by eqemu_server.pl (or starting world) - Be assured, custom factions that you may have created, or obsolete or - duplicate factions in our original faction_list, that you may have used, - will be preserved. Anything that does not map directly is being moved to - the 5000 range in faction_list and any references are corrected to point + Be assured, custom factions that you may have created, or obsolete or + duplicate factions in our original faction_list, that you may have used, + will be preserved. Anything that does not map directly is being moved to + the 5000 range in faction_list and any references are corrected to point there. - A great example of this is Ebon Mask and Hall of the Ebon Mask. Many peqdb - style servers have both of these. Some have used one, some the other. We - map Ebon Mask to the clients Ebon mask and the Hall of the Ebon Mask gets - moved to the 5000 range, and all its references are preserved. However, - if you would like to make proper use of client mobs to Ebon mask, or other - factions that have duplicitous entries, I recommend you manually move to - using the correct one. In that way all of the new raw data mapped in from - the client into faction_list_mod will get used instead of what your db had + A great example of this is Ebon Mask and Hall of the Ebon Mask. Many peqdb + style servers have both of these. Some have used one, some the other. We + map Ebon Mask to the clients Ebon mask and the Hall of the Ebon Mask gets + moved to the 5000 range, and all its references are preserved. However, + if you would like to make proper use of client mobs to Ebon mask, or other + factions that have duplicitous entries, I recommend you manually move to + using the correct one. In that way all of the new raw data mapped in from + the client into faction_list_mod will get used instead of what your db had before these values were known. - In my experience converting 4 different server's data, there are only + In my experience converting 4 different server's data, there are only about 20 factions moved into the 5000 range. - This PR has only 1 new, permanent table faction_base_data, which is taken - right from the client. The base field is left in case you want to mod your - server, but we are very sure that the client doesn't use a base. It uses - global mods to race or class for this as you'll see in the + This PR has only 1 new, permanent table faction_base_data, which is taken + right from the client. The base field is left in case you want to mod your + server, but we are very sure that the client doesn't use a base. It uses + global mods to race or class for this as you'll see in the new faction_list_mod. The PR makes many backup tables, and two mapping tables that are used during - the conversion process to fix quests. This table was hand created by - analysis. This table serves no purpose after conversion except an audit + the conversion process to fix quests. This table was hand created by + analysis. This table serves no purpose after conversion except an audit trail if we see any issues. - I will release a new PR that will clean up all these backups and temporary + I will release a new PR that will clean up all these backups and temporary tables in about a month. == 2/7/2019 == @@ -327,7 +338,7 @@ Kinglykrab: Added multiple new instance related quest functions. Added spell buckets, similar to spell globals. - Uses a new spell_buckets table and the Spells:EnableSpellBuckets rule. - + Added max level by data bucket. - Uses data bucket char_id-CharMaxLevel and Character:PerCharacterBucketMaxLevel rule. @@ -337,7 +348,7 @@ Uleat: Added bot owner options - options are saved in the database and therefore, persistent - Implemented option 'deathmarquee' -- toggles client owner flag to show marquee message when a bot dies (default: disabled) - + == 10/07/2018 == Uleat: Fixed a few bot issues.. - Fix for bot item trades not attuning @@ -353,27 +364,27 @@ Uleat: Fixed a few bot issues.. Uleat: Notes for manual conversion of quest script inventory slot values - You should use reference/lookup values provided by the lua and perl apis to avoid skirting safety checks and to ensure that the values used are the correct ones for your needs - + [perl api examples] old: 1) my $charmitem = $client->GetItemIDAt(0); 2) for($for_x = 22; $for_x < 30; $for_x++) {...} 3) for($slot1 = 0; $slot1 <= 30; $slot1++) {...} - + new: 1) my $charmitem = $client->GetItemIDAt(quest::getinventoryslotid("charm")); 2) for($for_x = quest::getinventoryslotid("general.begin"); $for_x <= quest::getinventoryslotid("general.end"); $for_x++) {...} ** notice change of conditional 3) for($slot1 = quest::getinventoryslotid("possessions.begin"); $slot1 <= quest::getinventoryslotid("possessions.end"); $slot1++) {...} - + [lua api examples] old: 1) if(e.self:GetItemIDAt(30) == 31599) then ... 2) for i = 0, 30, 1 do ... - + new: 1) if(e.self:GetItemIDAt(Slot.Cursor) == 31599) then ... 2) for i = Slot.PossessionsBegin, Slot.PossessionsEnd, 1 do ... - + - If you need to manually assign bag slot ranges to individual 'general' slots, use this assignment for now: -- General1 (23) = 251 .. 260 -- General2 (24) = 261 .. 270 @@ -386,13 +397,13 @@ Uleat: Notes for manual conversion of quest script inventory slot values -- General9 (31) = 331 .. 340 -- General10 (32) = 341 .. 350 -- Cursor (33) = 351 .. 360 - + - If you need to manually assign ammo or powersource slots, use these values: -- PowerSource = 21 -- Ammo = 22 - + - All slot values not addressed above remain the same - + - Additional information can be found at: -- https://github.com/EQEmu/Server/wiki/Inventory-Slots -- https://github.com/EQEmu/Server/wiki/Perl-API @@ -425,11 +436,11 @@ Uleat: Activation of RoF+ clients' two additional general slots and integration - Database will have existing inventory slot values modified to the new standard and table `items` entries will also be updated to the 'correct' equipable slot bitmask - Script (quest) updates are required with this change - + Note: The proper way to reference inventory slots is to use either instrinsic lookups (c/c++ & perl) or to use valid const ref declarations (c/c++ & lua). Any other method is not guaranteed to be accurate and may result in item loss and/or unexpected/undefined behavior. - + == 07/10/2018 == Akkadius: Adjusted DataBuckets to use other acceptable time formats @@ -533,7 +544,7 @@ Uleat: Re-work of Bot::AI_Process(). Overall behavior is much improved. - Added combat 'jitter' movement to complement the existing rogue movement - Attack can now be aborted if target contains no leash owner nor bot hate and leash owner turns off auto-attack - Please report any issues with the bot AI code - + Added a work-around for heal rotations crashing the server - under certain conditions. == 01/28/2018 == @@ -576,7 +587,7 @@ Values stored in the database are 0-6000, previously we capped it at 6000 but pr == 7/14/2017 == Akkadius: HP Update tuning - HP Updates are now forced when a client is targeted Akkadius: Client position updates should be smoother (granted the client has a good connection) - - Clients should also no longer randomly disappear + - Clients should also no longer randomly disappear == 7/11/2017 == Akkadius: Raid/Group/XTarget HP/Mana/Endurance updates now only send when percentage changes @@ -613,7 +624,7 @@ Akkadius: Fixed issues with Z correctness when NPCs are engaged with players fol Akkadius: NPC corpses should fall into the ground far less == 6/25/2017 == -Akkadius: New rules made by developers are now automatically created when world boots up, this keeps +Akkadius: New rules made by developers are now automatically created when world boots up, this keeps from having to issue schema SQL updates every time rules are added. - Whenever a rule isn't present in the database, it will be automatically created Akkadius: Sped up saylink retrieval x1000 helpful for dialogues, plugins with many saylinks @@ -630,11 +641,11 @@ KLS: Merge eqstream branch - Because of changes to the TCP stack, lsreconnect and echo have been disabled. - The server tic rate has been changed to be approx 30 fps from 500+ fps. - Changed how missiles and movement were calculated slightly to account for this (Missiles in particular are not perfect but close enough). - + - Breaking changes: - Users who use the cmake install feature should be aware that the install directory is now %cmake_install_dir%/bin instead of just %cmake_install_dir%/ - To support new features such as encryption the underlying protocol had to change... however some servers such as the public login server will be slow to change so we've included a compatibility layer for legacy login connections: - - You should add 1 to the login section of your configuration file when connecting to a server that is using the old protocol. + - You should add 1 to the login section of your configuration file when connecting to a server that is using the old protocol. - The central eqemu login server uses the old protocol and probably will for the forseeable future so if your server is connecting to it be sure to add that tag to your configuration file in that section. - Telnet no longer uses the same port as the Server to Server connection and because of this the tcp tag no longer has any effect on telnet connections. - To enable telnet you need to add a telnet tag in the world section of configuration such as: @@ -646,11 +657,11 @@ Akkadius: [Performance] Reworked how all log calls are made in the source - Before we used Log.Out, we will now use a macro Log( - Before: Log.Out(Logs::General, Logs::Status, "Importing Spells..."); - After: Log(Logs::General, Logs::Status, "Importing Spells..."); - - The difference is + - The difference is 1) It's 200-300x faster especially when log statements are inside very hot code paths. We already - had most hot paths checked before we logged them, but this blankets all existing logging calls now and not just the + had most hot paths checked before we logged them, but this blankets all existing logging calls now and not just the select few we had picked out in the source. - 2) Strings don't get copied to the stack, popped and pushed constantly even when we hit a log statement that + 2) Strings don't get copied to the stack, popped and pushed constantly even when we hit a log statement that actually isn't going to log anything. - We do an 'if (LogSys.log_settings[log_category].is_category_enabled == 1)' before we call a log function in the log macro so the log function doesn't get called at all if we're not logging the category @@ -660,16 +671,16 @@ Akkadius: [Performance] Reworked how all log calls are made in the source == 03/30/2017 == Akkadius: [Performance] Fixed an overhead issue where many hot paths would trigger quest subroutines and beneath that the code would - try to see if a quest existed perpetually (checking if file exists) even though it should have determined the quest + try to see if a quest existed perpetually (checking if file exists) even though it should have determined the quest didn't exist the first time. - This caused a lot of overhead in an instance where an entire zone of NPC's is pathing, triggering EVENT_WAYPOINT_ARRIVE - and EVENT_WAYPOINT_DEPART when there is no global_npc.pl/lua, or all NPC's pathing don't have a quest assigned, similar + and EVENT_WAYPOINT_DEPART when there is no global_npc.pl/lua, or all NPC's pathing don't have a quest assigned, similar behavior would occur. This goes for any other type of quests: spells, items, encounters etc. == 03/28/2017 == Akkadius: [Performance] Fixed a large overhead issue where every single NPC in a zone was checking to depop themselves as a swarm pet every 3ms regardless of being a swarm pet or not. Swarm pets now check to depop only when their timer is up -Akkadius: [Performance] Removed a timer where clients would constantly calculate light amount on equipment every 600ms, instead +Akkadius: [Performance] Removed a timer where clients would constantly calculate light amount on equipment every 600ms, instead clients will update light when changing equipment or entering a zone Akkadius: [Performance] Disabled enraged timer checks for NPC's that do not actually have enrage as a special attack Akkadius: [Performance] Don't process ProjectileAttack checks for NPC's that are not engaged in any combat @@ -680,7 +691,7 @@ Akkadius: [Performance] Reworked how client to NPC aggro checks are made check an entire entity list with distance calcs and other checks for aggro, with many clients in a zone and many NPC's this would add a lot of unecessary overhead. A temporary adjustment on 3/25 was made and upped the check to 6 seconds. - Now, there is a new methodology to scanning. The client will build a cache list of NPC's within close range as defined in new rule: - RULE_INT(Range, ClientNPCScan, 300) and will also get any NPC that has an aggro range beyond that defined range to use in + RULE_INT(Range, ClientNPCScan, 300) and will also get any NPC that has an aggro range beyond that defined range to use in the frequent checks for aggro, the result is far less overhead - Client scanning changes when moving versus not moving, the client will scan aggro every 500 milliseconds while moving, and 3000 millseconds aggro check when not moving, with a 6000ms re-fetch for close NPC's @@ -689,7 +700,7 @@ Akkadius: [Performance] Reworked how client to NPC aggro checks are made == 03/25/2017 == Akkadius: [Performance] Reduced CPU footprint in non-combat zones doing constant checks for combat related activities -Akkadius: [Performance] Reduced CPU footprint in cases where a client is checking for aggro excessively every 750 millseconds. This has +Akkadius: [Performance] Reduced CPU footprint in cases where a client is checking for aggro excessively every 750 millseconds. This has been adjusted to 6 seconds per new rule RULE_INT(Aggro, ClientAggroCheckInterval) - When zones have many players, with many NPC's, this adds up quickly @@ -706,7 +717,7 @@ Akkadius: [Performance] RULE_INT ( Range, SongMessages, 75) RULE_INT ( Range, MobPositionUpdates, 600) RULE_INT ( Range, CriticalDamage, 80) - + - (Readability) Also cleaned up some formatting in messaging and packets so it is easier to understand what is going on with the code == 03/09/2017 == @@ -7728,13 +7739,13 @@ Trevius: Food and Drink will now actually give stats. Required SQL: utils/sql/svn/229_spells_table.sql -==11/22/2008 +==11/22/2008== Trevius/AndMetal: Corrected the output of #showstats to provide accurate Attack Rating calculations. Derision: Initialise animation to zero in the Mob constructor to prevent players sometimes appearing to run off when they first spawn. Derision: /buyer /barter fix. Trevius: Added Account Limiting to allow limiting how many characters can be logged in per account at once - See Optional SQL. -==11/20/2008 +==11/20/2008== Derision: Implemented /buyer (cash compensation only) and /barter. Update your patch_Titanium.conf from the utils directory. REQUIRED SQL: @@ -7753,19 +7764,19 @@ CREATE TABLE `buyer` ( ALTER TABLE `trader_audit` ADD `trantype` TINYINT NOT NULL DEFAULT '0'; -==11/20/2008 +==11/20/2008== cavedude00: (aza77) Added quest::collectitems. cavedude00: (Rocker8956) Zones with IDs higher than 255 can now be instanced. -==11/19/2008 +==11/19/2008== seveianrex: (Varkalas) WAR Sturdiness AA Fix AndMetal: zone-*.log files on Linux will allow read access to group members. You may need to erase any existing logs to see the change. AndMetal: Added some logging for spell crits (SPELLS__CRITS) -==11/18/2008 +==11/18/2008== Derision: Bazaar: Added support for changing prices without ending Trader mode. -==11/16/2008 +==11/16/2008== Derision: Added command 'undyeme' to restore all a player's armor slots to their natural undyed state. Trevius: (Denivia) Changed Expansive Mind AA so that it raises the Worn Mana Regen Cap (as it should) instead of just increasing mana regen. Derision: Bazaar bug fix relating to items with -1 max charges. @@ -7774,7 +7785,7 @@ Optional SQL: INSERT INTO commands (command, access, description) VALUES ('undyeme', 0, 'Remove dye from all of your armor slots'); -==11/15/2008 +==11/15/2008== seveianrex: Implemented AA use of SE_Accuracy (Ranger GoD AA "Precision of the Pathfinder" will have an effect now) seveianrex: Implemented Enchanter GoD AA "Mesmerization Mastery" seveianrex: Adjusted Monk Kick Mastery damage modifiers slightly. @@ -7784,7 +7795,7 @@ Derision: Added Rule World:TutorialZoneID, default 189 (tutorialb). Derision: MinPrice/MaxPrice in the Bazaar search window are now interpreted correctly as values in Platinum. Derision: Bazaar bug fix. -==11/14/2008 +==11/14/2008== seveianrex: Adjusted Coat of Thistles AA modifier. seveianrex: Implemented Pet Crits for NEC/MAG/BST (Deaths Fury, Elemental Fury, Warders Fury GoD era AAs) Derision: Bazaar Trader mode (not Barter). Please test it well. @@ -7815,20 +7826,20 @@ CREATE TABLE `trader_audit` ( UPDATE doors set opentype=155 where opentype=154 and zone='bazaar'; -==11/12/2008 +==11/12/2008== Congdar: Bots: add command #bot lore - Casts Identify on the item on your mouse pointer seveianrex: Tweaked the Slay Undead damage to once again. Damage should be in-line with live numbers now. Angelox: (Kobaz): Fix for 'PIC register bx clobbered in asm' error Angelox: Improved filtering on [bot track rare] option for Ranger Angelox: Added option [bot track near] for Ranger -==11/11/2008 +==11/11/2008== Congdar: Bots: fix ghosting when runnning up to targets -==11/10/2008 +==11/10/2008== Trevius/Derision: Fix for GM Training Point exploit when de-leveling and leveling up again -==11/10/2008 +==11/10/2008== Derision: Implemented Looking For Group/Looking For Players Window. patch_Titanium_conf has been updated. Derision: Visual Studio users should add world/lfplist.h and world/lfplist.cpp to their project. @@ -7837,7 +7848,7 @@ REQUIRED SQL: ALTER TABLE `character_` ADD `lfp` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '0'; ALTER TABLE `character_` ADD `lfg` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '0'; -==11/08/2008 +==11/08/2008== Congdar: Bots: add command #bot resurrectme Congdar: Bots: modify heal ai, more mana calcs Congdar: Added Rule to allow melee to bind anywhere casters can bind, default is false @@ -7846,7 +7857,7 @@ optional sql: seveianrex: Figured out what was crashing ranged procs. Is fixed and fully implemented now. Rangers rejoice.. seveianrex: Modified the way #melody works to improve the delay between songs being casted. Bards rejoice.. -==11/07/2008 +==11/07/2008== seveianrex: GoD Rogue AA's: Seized Opportunity, Triple Backstab implemented seveianrex: (LeftRoad) Provide a damage message to client when mob is killed by a spell Congdar: Bots: reduce spam, fix crash when fighting Dain, change mana/hp from npc to client like calcs @@ -7857,16 +7868,16 @@ INSERT INTO commands (command, access, description) VALUES ('melody', 0, 'A supp seveianrex: Added support for defensive and ranged spell-effect based procs. Having some zone-crash issues with the ranged proc code for some reason, so it's commented out until I can fix it. -==11/05/2008 +==11/05/2008== Derision: Dyed armor color should now show up correctly to other players. Derision: Implemented the Potion Belt. -==11/03/2008 +==11/03/2008== seveianrex: Added some scaling to the way NPC hitboxes are calculated. This corrects some of the issues with mobs like Wurms/Dragons who you could hit from miles away. seveianrex: NPCs who are 5% HP or lower, are fleeing, and have a decent snare on will no longer cover so much distance. seveianrex: Implemented Mob::GetSnaredAmount() function, utilized in the above. Returns the ABS() value of the snare. -==11/01/2008 +==11/01/2008== seveianrex: GoD AA Implementations: [CLR] Touch of the Divine, [BRD] Internal Metronome, [SHD] Improved Consumption of the Soul Derision: Allow backtick through the CleanName filter. Derision: Pickpocket should no longer occasionally bug the UI. @@ -7874,23 +7885,23 @@ Derision: Pickpocketing money should no longer show twice the amount taken in th Derision: Looting stackable PVPItem now works. Derision: Client now updates correctly when a charge on an item with a right click effect is used. Update your patch_Titanium.conf -==10/31/2008 +==10/31/2008== seveianrex: Monk updates: Strikethrough AA, Mend Worsen Frequency Tweak, Kick Mastery AA Fix seveianrex: Implemented Pet Flurries for MAG/BST/NEC Derision: Extended quest::popup to take optional PopupID and buttontype (OK or YES/NO) fields. -==10/30/2008 +==10/30/2008== KLS: Spell crits should work reasonably well once again. Derision: Fixed inability to jump introduced in Rev161. -==10/29/2008 +==10/29/2008== Derision: Mobs in water should no longer sink to the bottom. cavedude00: Added rule to enable DeathExpLossMultiplier, or to use server code default. Optional SQL: Insert into rule_values values (0, 'Character:UseDeathExpLossMult', 'true'); -==10/27/2008 +==10/27/2008== seveianrex: Added server-side code for WAR sturdiness AA Angelox: (Cbodmer) Experience loss based on cbodmers formula with added rule to regulate loss amount Trevius: (Denivia) Added Critical Spell Damage for Lifetaps with the proper AAs @@ -7898,7 +7909,7 @@ Trevius: (Denivia) Added Critical Spell Damage for Lifetaps with the proper AAs Optional SQL (options are 0-10, defaults to 3): Insert into rule_values values (0, 'Character:DeathExpLossMultiplier', 3); -==10/26/2008 +==10/26/2008== Derision: Implemented Bandolier. Test thoroughly before trusting it with your Epic. Derision: Minor change to enable the spell Tiny Companion to work. @@ -7906,7 +7917,7 @@ Derision: Minor change to enable the spell Tiny Companion to work. seveianrex: Slay Undead tweak + message gender fix, Tradeskill message gender fix Trevius: Updated SQL file for R142 to correct some altadv_vars AA table issues with AA requirements -==10/24/2008 +==10/24/2008== seveianrex: Implemented various GoD AAs: Coat of Thistles, Rapid Strikes, Elemental Durability, Subtlety2, Strengthened Strike, Vicious Smash seveianrex: Implemented GoD Monk AA "Kick Mastery" Trevius: Adjusted the new Frenzy AAs code to fix a crash @@ -7919,7 +7930,7 @@ Derision: 'Killing' LDoN chest type objects(e.g. Vermin Nests) will now update k KLS: Revert Lag fixes that were causing movement and facing abnormalities. KLS: Fix to a few problems with quest::creategroundobject that was causing it to not work in some situations. -==10/23/2008 +==10/23/2008== Congdar: Clone NoDrop removal code to NoRent, Lore, NoTrade. Optionally enabled in the db Variables table DisasbleNoDrop=1 DisableNoRent=true DisableLore=1 DisableNoTrade=1 Congdar: Bot code cleanup, method call reduction Congdar: Bot DoubleAttack method is more like clients @@ -7946,7 +7957,7 @@ Required SQL: ALTER TABLE `tasks` ADD `repeatable` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '1'; -==10/22/2008 +==10/22/2008== AndMetal: Fix for item links to prevent occasional zone crashes AndMetal: Critical DOTs should now work correctly AndMetal: Fixed sql/svn/125_aggrozone.sql to work with current command table schema @@ -7958,7 +7969,7 @@ AndMetal: Support for +Stat Cap (Wunshi's Focusing, etc) AndMetal: New rules for tweaking spell crits: Spells:BaseCritChance(0), Spells:BaseCritRatio(0), Spells:WizCritLevel(12), Spells:WizCritChance(7), Spells:WizCritRatio(15) AndMetal: Loot messages will now have item links instead of just the item name -==10/21/2008 +==10/21/2008== KLS: Some changes to make compiling with profiler enabled more error free. KLS: Added player quest event EVENT_TASK_STAGE_COMPLETE exports $task_id and $activity_id. KLS: Added player quest event EVENT_PICK_UP exports $picked_up_id for when a player picks up a ground spawn or dropped item. @@ -7966,7 +7977,7 @@ KLS: Added quest::CreateGroundObject(itemid, x, y, z, heading) which lets the qu KLS: Objects should decay after they're loaded back into the world. Might want to backup your objects table till I can confirm this works 100%. KLS: Lowered ground object decay time from 30 min to 5 min. -==10/20/2008 +==10/20/2008== AndMetal: (erde) Moved SVN SQL updates to utils/sql/svn. File name will start with the revision they were introduced & optional updates have optional somewhere at the beginning of the name Derision/Trevius: Implemented sub EVENT_AGGRO_SAY AndMetal: Implemented Reverse Damage Shield @@ -7974,7 +7985,7 @@ AndMetal: Fixed +Damage Shield on items so that it doesn't work without having a AndMetal: (seveianrex) Hate w[h]iped on CoH AndMetal: (via Yeahlight) New command: #aggrozone. Requires 100 status by default -==10/19/2008 +==10/19/2008== Angelox: Added a start to Bot tracking - Thanks Derision for all the help and know-how. Derision: Altered damage shield message processing to be more like (the same?) as live. Derision: If it exists, damage shield types are read from a new table (damageshieldtypes) @@ -7982,7 +7993,7 @@ Derision: If no entry exists in the table, a default based on resist type is use Optional SQL: See utils/sql/damageshieldtypes.sql -==10/18/2008 +==10/18/2008== KLS: Reworked classic style traps. KLS: Debuff style traps should function perfectly, the level field in the DB gives what level the caster is for resists in this case. KLS: Added respawn and respawn variance to traps. @@ -8004,12 +8015,12 @@ ALTER TABLE `traps` ADD `respawn_time` INT(11) UNSIGNED DEFAULT '60' NOT NULL AF ALTER TABLE `traps` ADD `level` MEDIUMINT(4) UNSIGNED DEFAULT '1' NOT NULL AFTER `skill`; ALTER TABLE `traps` ADD `respawn_var` INT(11) UNSIGNED DEFAULT '0' NOT NULL AFTER `respawn_time`; -==10/17/2008 +==10/17/2008== KLS: Tweaks to /pet attack KLS: Renamed WhipeHateList to WipeHateList so I never have to view that spelling monster ever again. cavedude00: (Rocker8956) Added shutdowndelay to zone query. -==10/16/2008 +==10/16/2008== Derision: Cosmetic change to foraging to correctly identify food/drink items. cavedude00: Added rules to determine if /ooc and /auction should be server wide (true) or zone wide (false - Live Like). cavedude00: #peqzone can no longer be used if invulnerable. @@ -8021,14 +8032,14 @@ Optional SQL: insert into rule_values values (0, 'Chat:ServerWideOOC', 'true'); insert into rule_values values (0, 'Chat:ServerWideAuction', 'true'); -==10/15/2008 +==10/15/2008== Derision: Added OP_LevelAppearance to utils/patch_Titanium.conf and it is now sent to nearby clients (eye candy when you level) Derision: (seveianrex) Alcohol Drinking / Skillup Fix KLS: Change to proc code to correct some oddities with % chance to proc. KLS: Changed how avoidance bonuses are calculated to be like other melee bonuses. KLS: (seveianrex) Basic Pet Focus Implementation. -==10/14/2008 +==10/14/2008== Trevius: Charm Spells set to max level of 0 now have unlimited max level as they should Derision: Player pets should no longer be fearable if npcspecialattks contains 'D'. cavedude00: (Rocker8956) Changes to quest::getlevel and quest::setinstflag/setinstflagmanually to reduce DB access @@ -8036,21 +8047,21 @@ cavedude00: (Rocker8956) Added quest::getinstflag KLS: Fixed crash in shutdown delay, please test things for crashes before adding them, this was pretty obvious. Derision: Removed requirement to be grouped for Translocate spells. -==10/13/2008 +==10/13/2008== Derision: Looting your corpse while liched/wolf form etc should now auto-equip items -==10/12/2008 +==10/12/2008== Derision: No more binding wounds while feigning death. Derision: (Rocker8956) Movegrp quest command fix. Derision: (LeftRoad) TEMPORARY(NORENT) items in a character inventory will be retained if you log back into that character within 30 minutes. KLS: Implemented Raid::TeleportGroup() and Raid::TeleportRaid(), untested but should work. KLS: (seveianrex) Critical DoT and Sinister Strikes AA. -==10/11/2008 +==10/11/2008== Derision: Resurrection fix. KLS: Rebalanced skill ups. -==10/10/2008 +==10/10/2008== AndMetal: Added functions to calculate AA bonuses just like item & spell bonuses (not turned on yet) Condgar: Fix for Bots wont Attack Congdar: Fix low level bots hitting too hard @@ -8058,7 +8069,7 @@ Derision: Prevent two instances of possible zone crashes due to null pointers. Trevius: (Striat) Added quest commands for zone and world emotes - quest::ze() & quest::we() Derision: Recalculate Pet bonuses on zoning before setting current HP/Mana. -==10/09/2008 +==10/09/2008== Congdar: Replace Client IsEngaged() checks in Bot Source with bot aggro check Trevius: (AndMetal) Added quest Object SetOOCRegen() to adjust NPC Out of Combat Regen on the fly. Trevius: Relocated the Empty Corpse check code so that the Decay Time rule for it will now work. @@ -8069,18 +8080,18 @@ Derision: Reworked code to stop mobs fleeing if they have buddies to account for Optional SQL: Insert into rule_values values (0, 'Character:MaxExpLevel', 0); -==10/08/2008 +==10/08/2008== Congdar: Fix orphaned bots when camping/zoning Congdar: Link bot assist with client auto attack Derision: Added #titlesuffix command to add a title after a player's name. -==10/07/2008 +==10/07/2008== Congdar: Allow bot pets to be the main tank in a bot raid Congdar: Remove old Sense Heading skill check AndMetal: Web interface will now list open petitions. Derision: #title should now prefix the target player's name, e.g. #title Lord_Protector (underscores are replaced with spaces, max 31 chars) -==10/06/2008 +==10/06/2008== Angelox: Fix Bot names in group window (now is 'Mybot' instead of 'Mybot000') cavedude00: (erde) Fix for world and zone crashing when compiled on VS 2008 in release mode. cavedude00: (Rocker8956) Zone shutdown timer rule. @@ -8094,7 +8105,7 @@ INSERT INTO rule_values VALUES (1,'Zone:AutoShutdownDelay', 5000); Derision: Total time playing in /played now maintained across sessions. Derision: Corrected OP_DuelResponse/OP_DuelResponse2/OP_Shielding opcodes in utils/patch_Titanium.conf -==10/04/2008 +==10/04/2008== Congdar: Double Attack redo, tested working Congdar: Fix Bot follow bug when a bot gets killed Derision: Added minlevel and maxlevel fields to tasks table and new quest function istaskappropriate(task). @@ -8107,7 +8118,7 @@ Required SQL: ALTER TABLE `tasks` ADD `minlevel` TINYINT UNSIGNED NOT NULL DEFAULT '0', ADD `maxlevel` TINYINT UNSIGNED NOT NULL DEFAULT '0'; -==10/03/2008 +==10/03/2008== Congdar: Add EQOffline Bot Source Derision: Removed 'stepped' column from task table. See http://eqemulator.net/forums/showthread.php?p=157613 Derision: Added display of Task Description to #task show to aid in debugging a reported problem. @@ -8119,25 +8130,25 @@ KLS: Fix to crash in monk special attack function. Required SQL: ALTER TABLE `tasks` DROP `stepped` ; -==10/02/2008 +==10/02/2008== KLS: (Congdar) Update to Tech. of Master Wu AA. KLS: Revert of double attack change, people reporting non warriors were no longer double attacking properly. KLS: Compile Warning in Database::GetZoneName() KLS: Change to IsPlayerIllusionSpell() to make it more consistant, illusion spells can be more than 49 so we work off behavior and effect now. KLS: Added AA__Message logs to project illusion code to try to track down an error. -==10/01/2008 +==10/01/2008== Derision: (erde) VS2008 Compile Fix (#if (_MSC_VER < 1500) #define vsnprintf _vsnprintf #endif Derision: Fix to stop zone crashing when looting PVPItem and PVPReward is set to 3. cavedude00: Group members will no longer recieve a split when a player corpse is looted. -==9/30/2008 +==9/30/2008== Derision: Task activities with an activitytype of <= 0 in the activities table will be sent with an activitytype of 9 to the client. Derision: The reward field in the task table will now be displayed even if rewardid=0 cavedude00: (Rocker8956) Added quest function to get average group/raid level. cavedude00: (Rocker8956) Changes to setinstflagmanually to allow deletion of instance flags and to allow manual flagging of raids, groups, and individuals. -==9/29/2008 +==9/29/2008== cavedude00: Added rule World:ClearTempMerchantlist to control whether world clears temp merchant items when started or not. AndMetal: (Congdar) New Double Attack logic Angelox: Keyring code fix, so clicky portals that require keys will work too. @@ -8147,11 +8158,11 @@ AndMetal: Ayonae's Tutelage should now calculate bonus for Singing Optional SQL: insert into rule_values values (0,'World:ClearTempMerchantlist','true'); -==9/28/2008 +==9/28/2008== AndMetal: Fixed exploit for pets ignoring Fear by using /pet commands AndMetal: #wp add will now use the highest value in the grid_entries table + 1 if you don't use a Waypoint # or use 0 -==09/26/2008 +==09/26/2008== cavedude00: (AndMetal) Deathblow AA cavedude00: (AndMetal) Swift Journey AA cavedude00: (AndMetal) Convalescence and Healthy Aura AAs @@ -8166,7 +8177,7 @@ Optional SQL: Insert into rule_values values (0, 'World:AddMaxClientsPerIP', -1 ); Insert into rule_values values (0, 'World:AddMaxClientsStatus', -1 ); -==09/25/2008 +==09/25/2008== cavedude00: (trevius) IP Limiting Minor Fix cavedude00: (trevius) New Quest Command quest::clearspawntimers() cavedude00: (trevius) New Quest Command quest::traindiscs() @@ -8195,7 +8206,7 @@ cavedude00: Several minor forage/fishing fixes Required SQL is in utils/sql/09252008.sql Make sure you update your .conf files, also found in utils! -==09/01/2008 +==09/01/2008== KLS: (derision) Crash Fix. KLS: (haecz) Non-Melee damage filter. KLS: (LordKahel) Lore Slotted Augment. @@ -8225,12 +8236,12 @@ CREATE TABLE `blocked_spells` ( PRIMARY KEY (`id`) ) -==08/15/2008 +==08/15/2008== KLS: Fix for groups clearing on new group create. KLS: Fix for null row access in Database::GetLeaderName() KLS: CountDispellableBuffs now will validate buffs spells. -==08/14/2008 +==08/14/2008== KLS: Very basic work on raids. KLS: Moved group id calls to their own database as to not be blocked by long character_ selects and such. @@ -8243,7 +8254,7 @@ CREATE TABLE `group_id` ( ) ENGINE = InnoDB; -==08/13/2008 +==08/13/2008== KLS: Added dispel field for npc spells as 512. KLS: NPCs should be smarter about when they choose to cast certain spells; namely dots, lifetaps and dispels. KLS: NPCs should continue to cast nuke spells for a longer period of time. @@ -8263,7 +8274,7 @@ UPDATE npc_spells_entries SET type='512' WHERE spellid='49'; UPDATE npc_spells_entries SET type='512' WHERE spellid='1526'; UPDATE npc_spells_entries SET type='512' WHERE spellid='1697'; -==08/11/2008 +==08/11/2008== KLS: Group leader info should transfer to a new zone when the leader transfers to that zone. KLS: Will now force a group update to players not in zone, text not correct. KLS: Added #refreshgroup command - will refresh group visually from DB @@ -8282,15 +8293,15 @@ CREATE TABLE `group_leaders` ( ) ENGINE = InnoDB; -==08/09/2008 +==08/09/2008== KLS: Minor tweaks to heal aggro. -==08/07/2008 +==08/07/2008== KLS: Bald char fix revert, will try something else. KLS: Wake The Dead initial implementation. KLS: Hopefully better bald character fix base on kraeger's findings. -==08/04/2008 +==08/04/2008== KLS: (Derision) Pet Buff Window Implemented. KLS: Charmed pets should now function with the pet window, as well as appear correctly in group. KLS: Added rule Character:SkillUpModifier (100) to govern how fasts skill ups happen on a server. 100% = normal chance, 200% = double normal chance. @@ -8311,26 +8322,26 @@ KLS: Roughly implemented Skill Attack spell effect. Required: patch_6.2.conf and patch_Titanium.conf files have changed be sure to update them. -==07/21/2008 +==07/21/2008== KLS: (irv0) Fix for out of order ack not being sent in some situations. KLS: (Derision) Pet bar OOC update fix. KLS: Should have made client pets unable to give experience, untested but should work. KLS: Healing aggro should function better for people not on the hate list. KLS: Some work on public tradeskill objects saving their content between uses. -==06/22/2008 +==06/22/2008== KLS: Changed world/clientlist.cpp's line endings back to unix style line endings KLS: Fixed up ipban based on updated code from TheLieka. -==06/21/2008 +==06/21/2008== KLS: Belated updates to azone including (derision)EQG fixes and some changes to make it easier to compile under windows. KLS: Fixed inconsistant line endings in ruletypes.h; there are probably more line ending changes I didn't catch, please try to keep your line endings consistant with what's in the repo. -==06/19/2008 +==06/19/2008== Scorpious2k (Knightly): Correction of divine intervention text Scorpious2k (LordKahel): Support for defensive Instinct and Reflexive Mastery AA -==06/18/2008 +==06/18/2008== Scorpious2k (Derision): Fix for flee runspeed - linear flee speed reduction as HP drops Scorpious2k (Derision): Rule to prevent mobs from fleeing if they are being helped by other NPCs Scorpious2k (haecz): Distance check for corpse dragging @@ -8340,7 +8351,7 @@ Scorpious2k (greggg230): Faction will now show on /con for agnostic players Scorpious2k (BatCountry): Correction of a zone crash caused by reloading rules Scorpious2k (Congdar): Eliminated array index error/zone crash in spells -==06/17/2008 +==06/17/2008== Scorpious2k (TheLieka): Ban by IP Scorpious2k (cavedude/TheLieka): Ability to limit melee guys from being bound in certain zones. This changes the canbind column of the zone table. Value 0 means noone can bind, value 1 means only casters can bind, value 2 means @@ -8357,7 +8368,7 @@ Optional SQL: Insert into rule_values values (0, 'World:UseBannedIPsTable', 0); Update zone set canbind = 2 where zoneidnumber in (1,2,3,8,9,10,19,23,24,29,40,41,42,45,49,52,54,55,60,61,62,67,75,82,83,106,155); -==06/14/2008 +==06/14/2008== Scorpious2k(Trevius): Door names can now go beyond the 16 char limit to allow doors and other objects from later expansions to be used. The new max is 32 characters. Scorpious2k(Derision): New fear adjustment to cause mobs to flee at the correct rates instead of running very fast at certain @@ -8374,7 +8385,7 @@ Scorpious2k(Striat): Quest Commands for Temp Race, Texture, Size and Gender Chan Required sql: ALTER TABLE `doors` MODIFY COLUMN `name` VARCHAR(32) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL; -==05/30/2008 +==05/30/2008== Scorpious2k: (Derision/Wiz) Added code to implement fear. KLS: (Leika) Implemented detection of various MQ activities. KLS: EVENT_CAST_ON should now export $spell_id properly. @@ -8393,7 +8404,7 @@ CREATE TABLE `hackers` ( PRIMARY KEY (`id`) ) AUTO_INCREMENT=8; -==04/26/2008 +==04/26/2008== KLS: Should have fixed discipline and combat ability timer overlap. KLS: Added a rule for partial hits on fear, seperate from normal resist partial hits. KLS: Fixed some quirks in the aggro system. @@ -8408,10 +8419,10 @@ Required SQL: alter table `zone` add column `canlevitate` tinyint (4) DEFAULT '1' NOT NULL after `cancombat` alter table `zone` add column `castoutdoor` tinyint (4) DEFAULT '1' NOT NULL after `canlevitate`; -==04/22/2008 +==04/22/2008== Rogean: Server-side check for removing detrimental spells. -==04/13/2008 +==04/13/2008== Rogean: Fixed a hack to sell no-drop items to merchants. (Reported by KingMort) Rogean: Fixed #hideme, it won't show you zone in and then disappear anymore. (Reported by KingMort) Rogean: Fixed /summon. @@ -8420,7 +8431,7 @@ Rogean: Changes to /who all and GM's: You will not show up to players of lower status if your /anon and #gm on, if your #gm off and /anon you will show up as a normal player, regardless of statuses. -==04/09/2008 +==04/09/2008== KLS: (AiliaMorisato)Fixed death packet bindzoneid and attack_skill fields being switched. KLS: Added rule based caps for all PoP item abilities. KLS: Agility will now affect a defenders chance to be missed slightly. @@ -8447,20 +8458,20 @@ Required SQL: ALTER TABLE npc_types ADD ATK MEDIUMINT NOT NULL DEFAULT '0'; ALTER TABLE npc_types ADD Accuracy MEDIUMINT NOT NULL DEFAULT '0'; -==04/01/2008 +==04/01/2008== Rogean: Fixed a merchant purchase packet exploit. -==02/28/2008 +==02/28/2008== WildcardX: Tweaked the code for the Divine Intervention spell line code and the Unfailing Divinity AA ability. -==02/27/2008 +==02/27/2008== WildcardX: Implemented HeadShot AA Ability. WildcardX: Archery and throwing attacks will not cause you to suffer injury from your target's damage shield. WildcardX: Implemented Spell: Death Pact. WildcardX: Implemented Spell: Divine Intervention. WildcardX: Implemented Unfailing Divinity AA Ability. -==02/25/2008 +==02/25/2008== WildcardX: Enchanters can now control their pets if they purchased the Animation Empathy AA. WildcardX: Mobs now have a chance to resist fear spell line each tic. WildcardX: Mobs now have a chance to resist charm spell line each tic. @@ -8468,21 +8479,21 @@ WildcardX: Implemented Total Domination AA. WildcardX: Reworked new charisma test. This test now evaluates factors like MR, CHA, mob and caster levels. It yields better live-like results. WildcardX: Enchanter pets now get the /pet report and /pet health commands by default. -==02/23/2008 +==02/23/2008== WildcardX: Only characters with Pet Affinity AA can have group buffs casted on their pets. WildcardX: Fixed a bug that would not allow a rune buff to be used in some situations. -==02/22/2008 +==02/22/2008== WildcardX: Characters can now have only one caster specialization skill above 50. If more than one specialization skill is detected above 50, then all specialization skills are reset to 1. WildcardX: Reworked mana reduction calculations. WildcardX: Implemented spell casting specilization checks. -==02/21/2008 +==02/21/2008== WildcardX: Removed the possible fix for the Call of Hero spell as it didnt actually fix the issue after extensive testing. WildcardX: Added new command #scribespell. This will scribe a specified spell into the target's spell book. WildcardX: Added new command #unscribespell. This will unscribe a specified spell from the target's spell book. -==02/20/2008 +==02/20/2008== KLS: Zone crash fix caused by calling the TryWeaponProc() method when dead. WildcardX: Changed the quest function depopzone() to accept a parameter to specify if the spawn timers should resume or become disabled. 0 = Disable, 1 = Enable. WildcardX: Added the quest function repopzone(). This function will cause a zone to repop it's spawns normally. @@ -8490,28 +8501,28 @@ WildcardX: Possible fix for a zoning bug caused by the Call Of Hero spell. WildcardX: Harmony/Pacify line of spells will now cause aggro when resisted. WildcardX: Added a check against CHA to avoid aggro from a resisted Harmony/Pacify spell. -==02/19/2008 +==02/19/2008== WildcardX: Found a small efficiency for the code that determines if an item is equipable. WildcardX: Re-worked the rune spell buff code to make it more efficient and reduce CPU utilization. WildcardX: Added the quest function depopall(int npctype_id). This will remove all mobs from the zone with the specified npctype_id. WildcardX: Added the quest function depopzone(). This will remove all mobs from the zone and NOT cause a repop. -==02/10/2008 +==02/10/2008== WildcardX: Mesmerize line of spells will now cause aggro when casted on a mesmerize immune mob. WildcardX: Fixed enchanter spell Theft of Thought. This spell will now work as described. WildcardX: The pacify/harmony line of spells will no longer require a line of sight check to complete a cast. -==01/28/2008 +==01/28/2008== WildcardX: Regenerated perl_mob.cpp due to deprecating the following methods: GetFamiliar(), SetFamiliar(), GetRune(), SetRune(), GetMagicRune(), SetMagicRune(). WildcardX: Enchanters, this is your patch! Characters will now benefit from all rune and spell rune spell effects all spell buffs will provide, consistent with spell rules. WildcardX: Rune and spell rune spell buffs will now persist zoning and camping. WildcardX: Fixed a zone crash caused by TryWeaponProc() method. -==01/27/2008 +==01/27/2008== WildcardX: Subtle changes to zoning code to allow both the 6.2 and Titanium client to perform all zoning operations similiar to live. WildcardX: Fixed #zone and #goto commands for both the 6.2 and Titanium client. -==01/25/2008 +==01/25/2008== WildcardX: Regenerated perl_client.cpp and perl_groups.cpp due to parameter changes for Group::TeleportGroup and Client::MovePC methods. Changes to some quest files may be necessary. WildcardX: void MovePC(int32 zoneID, float x, float y, float z, float heading) WildcardX: void TeleportGroup(Mob* sender, int32 zoneID, float x, float y, float z, float heading) @@ -8533,7 +8544,7 @@ EVENT_ENTERZONE with no special var EVENT_LEVEL_UP with no special var -==01/24/2008 +==01/24/2008== WildcardX: Removed a 64 character cap length on merchant names sent during a merchant greeting. WildcardX: Fixed a bug that allowed players looting their corpse at just the right time, to duplicate their items. WildcardX: Fixed a bug I caused in group portals when I corrected the Succor/Evac line. @@ -8546,7 +8557,7 @@ WildcardX: (cavedude) Created Character:DeathItemLossLevel rule to define when a Required SQL: insert into rule_values values(0, 'Character:DeathItemLossLevel', 10); -==01/22/2008 +==01/22/2008== KLS: Clients will be immune to proximity aggro until they are finished loading now. KLS: Slightly reduced the melee accuracy of clients. KLS: Melee mitigation should now properly enforce minimum damage. @@ -8559,7 +8570,7 @@ KLS: Figured in a work around for corpses between the server and client becoming KLS: AA Consumption of the soul should now function correctly. KLS: AA Soul Abrasion should now function correctly -==01/19/2008 +==01/19/2008== KLS: Slightly reduced the effectivness of the Flurry AA KLS: Speed of the Knight AA implemented KLS: Evade will now reduce hate by a static amount, the static amount of hate increased slightly. @@ -8596,11 +8607,11 @@ Changed Spells:SpellAggroModifier to Aggro:SpellAggroMod ( 100 ) Changed Spells:BardSpellAggroMod to Aggro:SongAggroMod ( 33 ) Changed Spells:PetSpellAggroMod to Aggro:PetSpellAggroMod ( 10 ) -==01/16/2008 +==01/16/2008== KLS: Updated AA_Data.sql with various small fixes. KLS: Changed HasPet() to check for the existance of the pet as mob as well as the petid, should sync up with GetPet() nicely now. -==01/15/2008 +==01/15/2008== KLS: LDoN /open will only check for class now, client does not enforce bodytype so neither will the server. KLS: Numhits in disciplines should work correctly now KLS: Changed many instances where HasPet() was being checked to verify that we have a valid pet pointer from GetPet(), GetPet() will be used instead, (it is possible to have a valid pet ID and an invalid GetPet() pointer) @@ -8610,10 +8621,10 @@ KLS: PickPocket should now correctly skill up on it's own through normal use. KLS: GetMinLevel(int16 spell_id) will now return 0 if the level for the class was 255 instead of 255, many buff formulas do not work well with 255 which can cause some issues when a class can use a clicky item with a spell they cannot scribe normally. KLS: GetProcID() should no longer have hard coded values.. whatever these represented are no longer valid in the current spell data. -==01/14/2008 +==01/14/2008== WildcardX: Replaced the corpse consent system with one that allows cross zone player consents. -==01/13/2008 +==01/13/2008== FatherNitwit: (Derision) Added tool (awater) to extract BSP tree with region type info from .s3d files into .wtr files. FatherNitwit: (Derision) Added support for zone to load .wtr files. FatherNitwit: (Derision) Employ water file to prevent under water mobs from sinking. @@ -8627,10 +8638,10 @@ Watermap:CheckForWaterWhenFishing (Default: false) Watermap:FishingRodLength (Default: 30) Watermap:FishingLineLength (Default: 40) -==01/12/2008 +==01/12/2008== WildcardX: (cavedude) Fixed beastlord pet sizes. -==01/09/2008 +==01/09/2008== KLS: Starting items will now be saved if they are placed in slots other than the primary 8, this includes inside bags and on the character's inventory and bank slots KLS: Kick at level 55 or higher now has a chance to act as a spell interrupt as bash does. KLS: (TheLieka) Stun Immunity for Frontal Stuns on Ogres @@ -8648,24 +8659,24 @@ Combat:PetAttackMagicLevel (Default: 30) NPC:SayPauseTimeInSec (Default: 5) NPC:OOCRegen (Default: 0) -==12/06/2007 +==12/06/2007== KLS: Fixed logic on duration 7 formula.. again. -==12/02/2007 +==12/02/2007== KLS: Reworked buff duration formula 7 calculations. KLS: (Cripp)Added (Missing?) pathing z rules. -==11/30/2007 +==11/30/2007== FatherNitwit: Reworked pathing z code to be rules based instead of built time options. -==11/29/2007 +==11/29/2007== FatherNitwit: (Derision) Fix BestZ pathing cleanup code top stop hopping. FatherNitwit: (Derision) New fix-z-on-load feature. -==11/28/2007 +==11/28/2007== Rogean: Fixed another no-drop trade hack. -==11/26/2007 +==11/26/2007== KLS: Dynamic zones will now not attempt to boot up more than one instance of the zone if two or more clients request it in quick succession. KLS: Fixed: qglobals will now not be exported for npcs that do not have the qglobal flag set in the perl parser. KLS: Added a new npc special attack 'H' for immune to aggro, mobs with this set should not aggro no matter what a person does to them. @@ -8676,32 +8687,32 @@ KLS: Players will now be able to target themselves for group spells if they are KLS: Fixed some buff duration oddities. KLS: Level should have somewhat less of an impact for players in the resist code. -==11/14/2007 +==11/14/2007== KLS: Swarm pets should be forced to correctly depop after their owner disappears. KLS: NPCs should be able to use swarm pets correctly now. KLS: #traindisc should no longer learn disciplines over already known disciplines. KLS: Added a few more known spell effects to spdat.h, not implemented any yet KLS: Rez experience should now only goto regular experience, not AA experience. -==11/07/2007 +==11/07/2007== KLS: Reworked swarm pets a bit KLS: Swarm pets should no longer crash zones(I hope) KLS: Swarm pets will now gain all their information from the normal pet tables and the spell data, as a result the aa_swarmpets table is now obsolete. -==11/06/2007 +==11/06/2007== KLS: Reverted client timeouts. KLS: Fixed NPC HP and Mana Regen not loading from DB for NPCs -==11/05/2007 +==11/05/2007== WildcardX: Fixed a bug that prevented player corpse summoning spells from working on Linux platforms. -==11/04/2007 +==11/04/2007== WildcardX: The spell "Reanimation" will no longer restore any player experience. This is a 0% experience resurrection. WildcardX: Corpses moved by a consented player will now remember where it was moved to, even after a zone restart. WildcardX: Necromancers and Shadowknights can now summon corpses belonging to other players. WildcardX: Corrected a bug that would have allowed Necromancers and Shadownights to summon corpses belonging to non-grouped party members. -==11/03/2007 +==11/03/2007== WildcardX: (Cavedude) Implemented perl wrappers for new quest methods supporting the Shadowrest zone implementation. WildcardX: Fixed a bug that prevented a player from experiencing resurrection effects in designated no combat zones. WildcardX: Fixed a bug in graveyard system that could leave a player corpse in the database, but without a location. @@ -8717,7 +8728,7 @@ alter table player_corpses add column IsBurried TINYINT(3) NOT NULL default 0; alter table player_corpses add column WasAtGraveyard TINYINT(3) NOT NULL default 0; insert into rule_values values(0, 'Zone:EnableShadowrest', 0); -==11/01/2007 +==11/01/2007== WildcardX: (TheLieka) Bug fix to discontinue player invisibility when the player loots. WildcardX: (TheLieka) Bug fix to remove a type from the #npcedit loottable command. WildcardX: Implemented corpse graveyard support for all zones. Once a zone is configured for a graveyard, a zone restart is necessary. @@ -8742,7 +8753,7 @@ CREATE TABLE `graveyard` ( insert into rule_values values(0, 'Zone:GraveyardTimeMS', 1200000); -==10/19/2007 +==10/19/2007== WildcardX: (Bleh) Enhancement to avoid a zone crash by avoiding a null pointer. WildcardX: (UrbeIT) Bug fix to allow pet spells to effect players in PVP. WildcardX: (UrbeIT) Bug fix to allow players to remain invisible while in PVP. @@ -8753,26 +8764,26 @@ WildcardX: (zydria) Bug fix to allow players to meditate while mounted. WildcardX: (Cavedude) Enhancement to allow berserkers and rangers the chance to triple attack. WildcardX: (Cavedude) Enhancement to quest::movepc method to accept a heading value. -==10/17/2007 +==10/17/2007== KLS: Quick fix to quest::movegrp -==10/11/2007 +==10/11/2007== KLS: Familiars should fall under the pet system instead of their own system now, familiars should act in a much more behaved fashion when it comes to functionality shared with normal pets. KLS: Added a network timeout for clients in zone, clients should linkdead from zone when their connection is suddenly severed. KLS: (AiliaMorisato) Added some checks for item bonuses, bonuses should not calculate if the item is not equipable. KLS: NPCs should now accept signals while engaged in combat. -==10/10/2007 +==10/10/2007== FatherNitwit: Quick fix for Divine Aura on pets exploit (KingMort) -==10/09/2007 +==10/09/2007== KLS: Tweaked my AC system a bit, should get less default defense and the system should be much more lienent overall, goal is to allow people to see more of a benefit from having AC. -==09/24/2007 +==09/24/2007== FatherNitwit: (sfisque) Quick adjustment to item fishing probability. -==09/07/2007 +==09/07/2007== KLS: Fixed melee mitigation and tweaks and a small fix to AC. KLS: Small changes to the order in the getweapondamage code to make it a little more efficient KLS: Added a field that allows you to override a factions innate desire to assist their own faction in battle to the npc_faction table @@ -8787,7 +8798,7 @@ KLS: Lockpicking should work once again KLS: Should hopefully no longer be possible to use teleport doors when they are in the closed position. KLS: Added basic AC mitigation, not on by default uncomment #define USE_INT_AC in features.h to use it. -==08/31/2007 +==08/31/2007== KLS: (gernblan) Added spawn group info to #npcstats KLS: (TheLieka) Fix for zone exp modifiers not being used KLS: (inkubus) Lull and harmony spells should land on enemy targets and not cause any aggro, still some work to be done on the AE portion. @@ -8796,7 +8807,7 @@ KLS: (cbodmer) Support for LDoN style boxes (class 62 + body type 33) implemente KLS: The error in acmod() should no longer complain as much. Required: Opcode files have changed, be sure to update to the latest .conf files. -==08/30/2007 +==08/30/2007== KLS: Reworked Mob::GetWeaponDamage(), it will now return damage done by any item and NULL and will return a value zero or less if we can't hit a mob with said item KLS: Applied new GetWeaponDamage() to Attack and Special Attack code @@ -8810,58 +8821,58 @@ KLS: Changed NPC_DW_CHANCE in features.h to 100% as default as opposed to the pr KLS: Archery will be affected by various crit AA effects and spell bonuses KLS: Applied haste and slow modifiers to basic NPC special attacks such as kick and bash. -==08/26/2007 +==08/26/2007== KLS: Fixed a misplaced field in the titanium spawn structure that was causing some spawn display issues on titanium. -==08/16/2007 +==08/16/2007== KLS: Some cleaning up on autofire code KLS: Reintroduction of item animation for archery -==08/15/2007 +==08/15/2007== KLS: Fixed an /autofire crash KLS: Added some conditions to RangedAttack() for now. Should keep from using ranged attacks when we shouldn't be allowed to attack. -==08/14/2007 +==08/14/2007== KLS: Added /autofire KLS: Fixed up SetAttackTimer() calculations a bit, especially when it comes to ranged attacks. Hopefully fewer instances where ranged attacks will be wasted because of the attack timer. KLS: Ranged attack crits will now work no matter your chance to hit, also changed the appearances a tiny bit and added a min range check for /autofire. KLS: Removed ReadBook packet size checking for now.. it was checking a wrong size and canceling out legitimate book requests Make sure to update your .conf files to get /autoattack to work. -==08/12/2007 +==08/12/2007== KLS: A little bit more tinkering with how potions and stacks work. -==08/11/2007 +==08/11/2007== KLS: Fixed stackable potions in the item packet, they should now appear to have 1 charge instead of 0. KLS: Added support for stackable items above 20. KLS: Fixed forage, it will no longer overwrite slot 30 to create it's item, instead it will push the item onto the cursor like it should. -==08/10/2007 +==08/10/2007== KLS: Fixed a stacking issue and made res effects not count to normal stacking rules. KLS: Implemented a few more passive AA effects. KLS: Added CanThisClassBlock() for the avoid code KLS: Addressed some inconsistancies in the CanThisClass line of functions, GM classes should be able to do everything their base class can now. -==08/01/2007 +==08/01/2007== FatherNitwit: (cavedude) Fix Iksar BL pet appearance. -==07/30/2007 +==07/30/2007== Rogean: Fixed an item dupe exploit. -==07/28/2007 +==07/28/2007== Rogean: Put in checks for No Drop Trading Hacks -==07/26/2007 +==07/26/2007== KLS: Updated AA_Data.sql, still work to be done but should now be more complete than older AA sources. -==07/22/2007 +==07/22/2007== FatherNitwit: Fixed another memory leak in world and zone. KLS: Fixed up some message types so they match the client correctly. -==07/21/2007 +==07/21/2007== Rogean: Fixed LDoN Merchant Item Inspect -==07/20/2007 +==07/20/2007== FatherNitwit: (Striat) Fix for typos in perlparser.cpp to fix quest::me and quest::echo. FatherNitwit: (Striat) Fix argument type in quest::setguild. FatherNitwit: Fixed quest::settime @@ -8874,28 +8885,28 @@ Changes to opcode files: patch_Titanium.conf: OP_TargetHoTT=0x6a12 patch_6.2.conf: OP_TargetHoTT=0x3ef6 -==07/16/2007 +==07/16/2007== FatherNitwit: (Glather) Fix for mobs showing weapons FatherNitwit: (Cbodmer) Notify owner when buffs wear off pets. -==6/06/2007 +==6/06/2007== KLS: (Darkonig) Change to the way tradeskill containers are handled. KLS: (Darkonig) Change to item->isStackable() implementation, should let certain items that the client lets stack also stack server-side. -==6/01/2007 +==6/01/2007== KLS: Addressed a memory leak in spell code and a memory leak in NPC destruction code. -==4/13/2007 +==4/13/2007== KLS: Fixed an issue with buff duration inc. that was causing buffs to lose their effects (I hope) -==4/08/2007 +==4/08/2007== KLS: Added support for the skill Frenzy. KLS: Various tweaks and changes to some passive AA abilities. KLS: Added #traindisc to train disciplines on the target player, #scribespells should no longer memorize disciplines to the spellbook. KLS: Fixed an issue with spells that require components that use item ids higher than 32k KLS: Fixed an issue with AAs and focus effects that increase buff duration not appearing correctly for clients until they zone. -==4/02/2007 +==4/02/2007== KLS: Implemented number6 inspired item cooldown timers. KLS: Added rules: NPC:MinorNPCCorpseDecayTimeMS & NPC:MajorNPCCorpseDecayTimeMS to set the decay time in millisecond of mobs below 55 and greater or equal to 55 respectively KLS: Addressed an issue that caused some beneficial spells to generate incorrect aggro amounts. @@ -8909,14 +8920,14 @@ KLS: Made mend skill checks more lienent, shouldn't ever fail after 200 skill, s KLS: Initial implementation of hp balance spell effect. KLS: Focus effects that limit max level should now correctly reduce the effect by a percentage if the spell is over the level cap -==3/26/2007 +==3/26/2007== FatherNitwit: Optimized the merchantlist_temp query on boot. -==3/14/2007 +==3/14/2007== KLS: Put in FNW's requested changes to tradeskill combines from the other day that I almost forgot about KLS: Tradeskill combines should check for container type now. -==3/11/2007 +==3/11/2007== KLS: Attack() will not set our target unless we have no target now. KLS: Tweaked some invis and hide stuff to hopefully catch more situations where it should break correctly. KLS: Fixed the spell critical hit ratio to be accurate. @@ -8925,13 +8936,13 @@ KLS: NPCs will no longer be restricted by the number of targets on their AE spel KLS: Spells base_1 will load as a 32 bit signed int instead of a 16 bit signed int, should correct some oddities with certain spells. For example certain summon spells using large item ids. KLS: Fixed a tradeskill exploit with tradeskill containers and experimentation. -==3/10/2007 +==3/10/2007== Rogean: Fixed #si and minstatus -==3/3/2007 +==3/3/2007== FatherNitwit: Finally rewrote 'make depend' to properly track dependencies in linux builds -==2/22/2007 +==2/22/2007== KLS: Some more changes to AA system: KLS: altadv_vars.cost_inc will now be unsigned to allow your skills to cost less per level KLS: Client side aa effects are now loaded from the database once again @@ -8956,7 +8967,7 @@ CREATE TABLE aa_effects ( Optional SQL: Resource AA_Data.sql -==2/18/2007 +==2/18/2007== WildcardX: Player casted area of effect detrimental spells will not longer have an affect on players not engaged in PvP. WildcardX: Fixed spawnlimit setting to control the maximum times a given npc_type can spawn in the same zone. WildcardX: (Bleh) Fixed player emote animations. @@ -8964,7 +8975,7 @@ WildcardX: Implemented a server-side check of a Client object's target to avoid WildcardX: Fixed a zone crash that could occurr during a monk special attack or a rogue's backstab attack. WildcardX: Area of effect beneficial spells will no longer affect non-player characters. -==2/16/2007 +==2/16/2007== KLS: Updating Changelog AA work to be more clear as well as the required SQL. Required SQL: @@ -8973,7 +8984,7 @@ alter table altadv_vars add column cost_inc tinyint(4) not null default 0; Optionally: Source in AA_data.sql -==2/15/2007 +==2/15/2007== WildcardX: Commented out the code supporting the deprecated OP_CloseContainer opcode. WildcardX: Fixed world containers so when one player has closed a world container, another player can open it without the need for the first player to leave the zone. KLS: Initial release of ongoing AA work @@ -8997,7 +9008,7 @@ KLS: Fixed a int to float conversion issue in CalcRecommendedLevelBonus() (as pe KLS: Fixed a crash issue related to resists, should help those crashing after zoning with a spell that has a heartbeat resist such as root. KLS: Removed an unused spell function that didn't make much sense. -==2/14/2007 +==2/14/2007== WildcardX: Implemented a cap of 4 mobs affected by targeted area of effect spells. This is consistent with EQ live. WildcardX: Implemented the succor line of spells. WildcardX: Specific mobs can now be toggled as findable or not findable by the track skill. Mobs which are not findable will not appear on any track lists. @@ -9005,10 +9016,10 @@ WildcardX: Specific mobs can now be toggled as findable or not findable by the t Required SQL: alter table npc_types add column trackable tinyint(4) not null default 1; -==2/13/2007 +==2/13/2007== WildcardX: Fixed bug that broke #zone command. -==2/11/2007 +==2/11/2007== WildcardX: Further enhancements to zone weather system to reduce the amount of work necessary to perform weather and to make it more logical. WildcardX: Added a new opcode for both 6.2 and Titanium clients, OP_ZonePlayerToBind=0x385e. Please be sure to update your .conf files. WildcardX: Implemented the beginnings of what will become a new "system" to deal with zoning. @@ -9022,21 +9033,21 @@ WildcardX: (Cavedude) Implemented yet another container type to perform even mor WildcardX: Added additional logging to zone "debug" and "error" to support zoning and intra-zone movement. WildcardX: Group members disbanding in another zone will still fail to appear as removed from group from other group members, but disbanding this group member when he/she rejoins group in zone will no longer crash the zone and you will be able to re-add this former group member without having to destroy the whole group. -==2/10/2007 +==2/10/2007== FatherNitwit: Reworked NPC Idle spell casting to avoid extra LOS checks (based on KLS's observations) FatherNitwit: Reworked NPC spell casting timers to be more consistent. -==2/9/2007 +==2/9/2007== Doodman: Fixed AdventurePoints_Update_Struct to be the correct struct. Same structure for both 6.2 and Ti. Changed core structure. -==2/1/2007 +==2/1/2007== WildcardX: (Bleh) Fixed quest globals. WildcardX: Fixed the common error message MakeNameUnique() has been recording in logs about being unable to make a unique name for mobs greater than 100 in a zone. Required SQL: ALTER TABLE quest_globals CHANGE expdate expdate INT; -==1/29/2007 +==1/29/2007== WildcardX: Cleaned up zone weather code for better efficiency. Weather checks will be more frequent when their is snow or rain and less frequent when the sun is out. WildcardX: Made weather log messages more consistent and informative. WildcardX: Druids are now replace bards has the second best tracker in the game. This is consistent with live. @@ -9045,11 +9056,11 @@ WildcardX: Clients will now see their coins update from quests or player trades. WildcardX: The server is now using the OP_MoneyUpdate opcode, instead of OP_MoneyOnCorpse opcode for coin updates. WildcardX: Clients entering a zone will now receive a weather packet. This will ensure all clients in the same zone will experience the same weather. -==1/23/2007 +==1/23/2007== KLS: Fixed the change log so the current entries are correct KLS: Fixed a small but significant crash. -==1/15/2007 +==1/15/2007== KLS: Fixed an issue with storing timers. KLS: Added in hide and basic support of hide improving AAs later KLS: Some more changes to how procs work, perma procs will not be influenced by mob stats, should make them easier to balance. @@ -9066,7 +9077,7 @@ Optional SQL: UPDATE `npc_types` SET `see_hide` = 1 WHERE see_invis = '1'; UPDATE `npc_types` SET `see_hide` = 1 WHERE see_invis_undead = '1'; -==1/07/2007 +==1/07/2007== KLS: action_struct unknown06 renamed to instrument_mod to reflect it's behavior KLS: WildCardX inspired weapon proc rate changes, proc rates should now load as a signed int instead of an unsigned int and should correctly calculate. KLS: Some changes to the expired function of ptimers, it should return if the timer is expired whether or not the timer is enabled. This avoids certain situations where timers are disabled and then run down without ever being reenabled and essentially being locked down forever unless the database admin resets it manually. @@ -9084,7 +9095,7 @@ KLS: Spin effects are now correctly capped at a max of level 55, all targets abo KLS: SE_LimitEffect should now correctly deal with any effect type the spell has and not just the ones hard coded into the server. KLS: (Cripp)Fix for zone heading -==12/24/2006 +==12/24/2006== KLS: (WildcardX) Fix for generic tradeskill container combines KLS: (WildcardX) Fix for quest::depop and multipul mobs in the same zone that share a NPCType ID KLS: (WildcardX) Zones can now be flagged to disallow offensive actions. @@ -9102,7 +9113,7 @@ UPDATE `zone` SET `cancombat` = 0 WHERE short_name = 'nexus'; UPDATE `zone` SET `cancombat` = 0 WHERE short_name = 'poknowledge'; UPDATE `zone` SET `cancombat` = 0 WHERE short_name = 'potranquility'; -==12/19/2006 +==12/19/2006== KLS: Fixed up Illusions a bit KLS: Fixed a bug where the base2[0] field of a spell was not getting loaded correctly KLS: Implemented Sacrifice Spells @@ -9117,38 +9128,38 @@ KLS: If a player is at max level the experience he has will cap at the max exper KLS: The max level you can group with and gain experience is now YourLevel*1.5 instead of YourLevel+8. KLS: You'll only get an experience loss message if you actually lose exp now, no more You lost experience messages when you take a 0% rez. -==12/15/2006 +==12/15/2006== FatherNitwit: Fix for possible crash when NPCs are immune to damage. KLS: Small fix for newly created player corpses, hopefully they will no longer act like they are npc corpses. KLS: Changed how recourse works a bit, it is now done after the resist check. KLS: Group spells should be able to land on the caster's pet now. KLS: Fixed crazy Bash/Kick damage. -==12/09/2006 +==12/09/2006== KLS: Stun immune should apply to spin effects such as those found in One Hundred Blows. KLS: Some cleanups in special attack and resist code. KLS: Added a define SKILL_MAX_LEVEL in features.h that lets you define how high level you can get skills from the database, defaults to 75. KLS: Damage shields wont proc on yourself any longer to avoid some wierdness. They only ever procced on yourself under special conditions so it shouldn't be noticable. -==12/03/2006 +==12/03/2006== FatherNitwit: Fixed a few issues related to quest global expiration. -==12/01/2006 +==12/01/2006== KLS: NPC ghosting fix with rule support. FatherNitwit: Fixing up spell set loading crash. KLS: More skill fixes KLS: Moved the old class_skills system over to the new skills system. -==11/28/2006 +==11/28/2006== FatherNitwit: (bleh) Added #hatelist command. FatherNitwit: (bleh) Work on windows build issues. FatherNitwit: Optimized merchant list query during zone boot. FatherNitwit: Fix a few race related issues in eqbuilder. -==11/26/2006 +==11/26/2006== FatherNitwit: Fixed berserker skill cap issue that KLS found. -==11/25/2006 +==11/25/2006== KLS: The way resists are calculate have been changed and are now somewhat tweakable with rules, and partial hits should work correctly. KLS: Ripostes have been seperated from the attack code and will now apply to all attacks. KLS: Skills and Spells that directly modify melee damage should now work and be applied to all attacks. @@ -9161,16 +9172,16 @@ KLS: Stun Immunity will now only apply to the stun portion of the spell instead KLS: Instrument Mods should send correctly on the reapplication of bard pulses. KLS: Spells should no longer check instant heals/damage portions of the spell for stacking purposes, several spells that should have stacked before but didn't now should properly. -=11/24/2006 +=11/24/2006== FatherNitwit: (number6) Fix for loading saved spell sets. FatherNitwit: Added #giveitem as inspired from the forums. FatherNitwit: Changed default status required for #summonitem to 200. -==11/20/2006 +==11/20/2006== KLS: Skill system tweaks and bug fixes. KLS: Bard songs will now skill up while they are being sung instead of just when first cast. -=11/19/2006 +=11/19/2006== FatherNitwit: Completely redid skills in the code to support >252 FatherNitwit: Skill caps are now database driven (shared mem) Create this table, and source in SkillCaps.sql @@ -9182,7 +9193,7 @@ CREATE TABLE skill_caps ( PRIMARY KEY(skillID,class,level) ); -==11/07/2006 +==11/07/2006== KLS: Changed how critical hits work, as a result things that could not crit before like special attacks, now can. KLS: Cleave like effects should be fixed to do an increase of your already standing chance to critical instead of a flat increase. KLS: Reworked much of the special attack code. @@ -9191,19 +9202,19 @@ KLS: Monk skill damage will be more random but the max damage they can do has no KLS: Skills should be avoided less often, they were being avoided twice in the code instead of once. KLS: Implemented basic strikethrough. -==11/06/2006 +==11/06/2006== FatherNitwit: (Cripp) periodic auto save. FatherNitwit: LostZaphod's possible win32 azone fix. (untested) FatherNitwit: (cbodmer) Regen multiplier rules. -==11/04/2006 +==11/04/2006== Rogean: Haste Fixes: Fixed Haste/Slow as well as Bard Songs (Including Overcap). -==11/03/2006 +==11/03/2006== KLS: Added Magoth's stun opcodes for both Titanium and 0.6.2 clients KLS: Changed Object Clear opcode for Titanium -==10/30/2006 +==10/30/2006== FatherNitwit: (KLS) Fixes for max endurance calc on zone in FatherNitwit: (KLS) Fix for rulesystem category issue. FatherNitwit: Added rule support to world (no runtime manipulation yet) @@ -9218,17 +9229,17 @@ KLS: GroupInvite2 opcodes found for both Titanium and 0.6.2, make sure to get th KLS: Glow messages should only go off for instant spells. KLS: How haste calculations have been calculated has been reworked somewhat, haste shouldn't break any more, nor should all hastes stack. -==10/28/2006 +==10/28/2006== FatherNitwit: Fix for NPC weapon appearance due to loot items. aza77: Fixed SendAppearancePacket aza77: Fixed a PvP bug that caused the zone to crash on death -==10/26/2006 +==10/26/2006== FatherNitwit: WildcardX's name cleanup. FatherNitwit: Tweak for 64 bit pointer support in the item code of the struct strategy. FatherNitwit: More const cleanup in npc.h -==10/22/2006 +==10/22/2006== FatherNitwit: added EVENT_COMBAT triggered when an NPC engages any form of FatherNitwit: combat and when combat is over. $combat_state signals which. FatherNitwit: Hopefully fixed d_meele_texture @@ -9238,14 +9249,14 @@ FatherNitwit: Reworked special attacks a bit (monk, kick, backstab): FatherNitwit: they now use the standard combat hit chance forumals based on offense/defense instead of always hitting FatherNitwit: they are now subject to damage avoidance (parry, riposte, etc.) like normal attacks -==10/21/2006 +==10/21/2006== FatherNitwit: Hopefully fixed spawn timer variance. -==10/18/2006 +==10/18/2006== FatherNitwit: Maybe fixed issue with losing items when zoning in mysql 5. FatherNitwit: Fixed issue with color saving properly. -==10/15/2006 +==10/15/2006== FatherNitwit: (KLS) Implemented Endurance FatherNitwit: (KLS) Implemented support for seperate discipline timers FatherNitwit: (KLS) Fix for haste & slow issue on NPCs @@ -9266,66 +9277,66 @@ FatherNitwit: (KLS) let dots of the same resist type stack so long as they aren' FatherNitwit: cleanup windows logging a bit FatherNitwit: (eq4me) GCC 4+ build fixes -==10/05/2006 +==10/05/2006== FatherNitwit: (KLS) Fix for explicit spell stacking rule problem. -==10/02/2006 +==10/02/2006== FatherNitwit: (InsaneWallaby) Tradeskill skill gain fix. FatherNitwit: (WildcardX) Improved spawnstatus command. FatherNitwit: (number6) Work around for lay hands/harm touch recovery time hang. FatherNitwit: Possible fix for faction sticking at min/max. -==09/21/2006 +==09/21/2006== FatherNitwit: Revert part of bard mod fix, and fix it right. -==09/20/2006 +==09/20/2006== FatherNitwit: Prevent stats from going negative. FatherNitwit: Try to prevent skills from wrapping above 252 (still no support for higher) FatherNitwit: Minor shop close tweak. FatherNitwit: (Zuesrooster) Potential bard modifier fixes. -=09/07/2006 +=09/07/2006== FatherNitwit: (eq4me) Reworked tradeskill success and gain rates FatherNitwit: Potentially addressed trade coin display issue. FatherNitwit: Undid clickie spell component change until dicussion finishes -=09/07/2006 +=09/07/2006== FatherNitwit: (Aramid) Fixed ability to bind. FatherNitwit: (Somebody, forget who) Fixed issue with reagents being consumed by clickies. FatherNitwit: (eq4me) Fixed tradeskill stat bonus calculation for success rate. FatherNitwit: Maybe fixed up zone solution file to only build perl versions, for debug and release. -==08/20/2006 +==08/20/2006== FatherNitwit: Zero out buff counters when we occupy a new slot. -==08/16/2006 +==08/16/2006== FatherNitwit: Glorandwarf's updated AA.h -==08/14/2006 +==08/14/2006== FatherNitwit: More work on titanium AAs by Glorandwarf. FatherNitwit: (Zuesrooster) Fix for pet self buffing. -==08/10/2006 +==08/10/2006== FatherNitwit: Changed logic in quest parser to potentially avoid windows crash (Zuesrooster) FatherNitwit: (Zuesrooster) fixed aggro quest related crash. FatherNitwit: Untested update of OP_AugmentItem (John Adams) FatherNitwit: A little bit further on AAs (Glorandwarf) -==07/27/2006 +==07/27/2006== FatherNitwit: Added door info dump on click (DOORS__INFO) -==07/23/2006 +==07/23/2006== FatherNitwit: Enforce some zone requirements on zone in too. FatherNitwit: Fixed minor tradeskill crash. -==07/23/2006 +==07/23/2006== FatherNitwit: (EverHood) Archery/self PvP mitigation changes FatherNitwit: (EverHood) Regen tweaks FatherNitwit: (EverHood) Fix DoT damage and messages FatherNitwit: Killed erronious double-effects on regen/HoT buffs FatherNitwit: Fixed replace-container client side appearance issue. -==07/17/2006 +==07/17/2006== FatherNitwit: Added new rules subsystem to allow game rules to be changed at runtime. more about this will come as time goes on. FatherNitwit: Added #rules command to manage rules data from in game. @@ -9352,27 +9363,27 @@ CREATE TABLE rule_values ( PRIMARY KEY(ruleset_id,rule_name) ); -==07/16/2006 +==07/16/2006== FatherNitwit: (EverHood) Implemented Eye of Zomm and Bind Sight FatherNitwit: Reworked Eye of Zomm code -==07/16/2006 +==07/16/2006== aza77: Fixed the lift issue + keyitems at doors aza77: Fixed + modified traps and added a new type of trap ALTER TABLE `traps` ADD `message` VARCHAR( 200 ) NOT NULL AFTER `effectvalue2` ; -==07/13/2006 +==07/13/2006== FatherNitwit: Fixed zone crash when removing a guild member. aza77: Added in game guild creation variables.GuildCreation + commands #guildcreate #guildapprove #guildlist FatherNitwit: Fixed feign aggro crash. -==07/12/2006 +==07/12/2006== aza77: Added triggered only doors. -==07/10/2006 +==07/10/2006== FatherNitwit: Adding EverHood's lull fixes. -==07/09/2006 +==07/09/2006== FatherNitwit: Wizardanim has dicovered the opcode to partiall fix AAs for Ti!! FatherNitwit: EverHood's Mob ghost running on (feign)death fix. FatherNitwit: Redid walkspeed code to be caclualted from runspeed (EverHood inspired) @@ -9381,24 +9392,24 @@ FatherNitwit: Added EVENT_NPC_SLAY when NPCs kill another NPC. (fanman55 inspire FatherNitwit: Fixed up a long running memory issue in quest::ChooseRandom ALTER TABLE `npc_types` DROP walkspeed; -==07/05/2006 +==07/05/2006== aza77: Added variables.Rules + commands #rules #acceptrules ALTER TABLE `account` ADD `rulesflag` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '0'; -==07/04/2006 +==07/04/2006== FatherNitwit: Maybe fixing feign memory. FatherNitwit: EverHood's temp pets and group recourse fixes. FatherNitwit: EverHood's NoLongerEngaged AI fix. FatherNitwit: bufgix in log.ini reading. -==06/29/2006 +==06/29/2006== aza77: Get the SQL right: ALTER TABLE `npc_types` CHANGE `hp_regen_rate` `hp_regen_rate` INT( 11 ) NOT NULL DEFAULT '0'; ALTER TABLE `npc_types` CHANGE `mana_regen_rate` `mana_regen_rate` INT( 11 ) NOT NULL DEFAULT '0'; -==06/28/2006 +==06/28/2006== FatherNitwit: Added EverHood's feign memory fixes FatherNitwit: Reverted regen of 0 to mean "auto config", and negative to mean "no regen" aza77: Changed hp_regen + mana_regen such that negative values indicate no regen @@ -9409,18 +9420,18 @@ NULL DEFAULT '0'; UPDATE `npc_types` SET `hp_regen_rate` = '0' WHERE `hp_regen_rate` = '-1'; UPDATE `npc_types` SET `mana_regen_rate` = '0' WHERE `mana_regen_rate` = '-1'; -==06/27/2006 +==06/27/2006== aza77: Added quest::setnextinchpevent + $inchpevent aza77: Added quest::sethp -==06/25/2006 +==06/25/2006== aza77: Fixed PVP appearance issues FatherNitwit: Fixed the #logsql command -==06/20/2006 +==06/20/2006== FatherNitwit: tweaked some ability timers and fixed up ranger crit damage -==06/18/2006 +==06/18/2006== FatherNitwit: necro DOT feign death fix (unicorn97211) FatherNitwit: Disable kill-pet-on-feigndeath by default (features.h FEIGN_KILLS_PET) MySQL 5 compatibility increases (WildcardX): @@ -9430,19 +9441,19 @@ alter table player_corpses change data data blob NULL; alter table player_corpses_backup change data data blob NULL; -==06/15/2006 +==06/15/2006== aza: Added MySQL5 support (FOR CUSTOM COMPILE ONLY) ALTER TABLE spawn2 CHANGE `condition` `_condition` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0'; -==06/07/2006 +==06/07/2006== FatherNitwit: Fixed SetAATitle (#title), and experted it to perl. FatherNitwit: Fixed quest money reward printing. (WildcardX) -==06/03/2006 +==06/03/2006== FatherNitwit: Fixed a client-side inventory appearance issue, may help with trade bugs. -==05/31/2006 +==05/31/2006== FatherNitwit: (aza) Added quest::unscribespells FatherNitwit: fixed ranger critical hit chance. FatherNitwit: Implemented conditions on windows. @@ -9455,7 +9466,7 @@ ALTER TABLE spawnentry DROP spawn_limit; ALTER TABLE spawngroup ADD spawn_limit tinyint(4) NOT NULL default '0'; ALTER TABLE altadv_vars ADD class_type int unsigned NOT NULL DEFAULT 0; -==05/06/2006 +==05/06/2006== FatherNitwit: (aza) Enabled newer froglok classes. FatherNitwit: (aza) Enabled several berserker combat skills. FatherNitwit: Fixed possibly infinite recursion in map code. @@ -9463,53 +9474,53 @@ FatherNitwit: Holding EQStreams open until they send all their data. FatherNitwit: Trying to fix death disconnect (and failing) FatherNitwit: Maybe fixed tinkering searching. -==04/24/2006 +==04/24/2006== FatherNitwit: Added new HTTP functionality for moving chars between accounts. -==04/21/2006 +==04/21/2006== FatherNitwit: Fixed bane damage loading problem. -==04/20/2006 +==04/20/2006== FatherNitwit: Hopefully fixed the unidentified stream crash and some memory leaks. FatherNitwit: Fixed weapon affinity huge proc bonuses FatherNitwit: Fixed spell-granted procs not getting removed. FatherNitwit: Implemented the 'procrate' field on items. -==04/13/2006 +==04/13/2006== FatherNitwit: Fixed the issue of charmed mobs getting assists from their old friends. FatherNitwit: Changed version number to 0.7.0 -==04/13/2006 +==04/13/2006== FatherNitwit: fixed a crash related to client destructors and tradeskills FatherNitwit: Adding the incstats canges from aza on the forums. -==04/01/2006 +==04/01/2006== FatherNitwit: FINALLY found and fixed the netcod crash I introduced a while back. FatherNitwit: Fixed bard group buff tics. FatherNitwit: Fixed the global guild chat going to everybody problem. -==03/27/2006 +==03/27/2006== Doodman: Added queuing for future packets, fixing lag reported by Richardo -==03/25/2006 +==03/25/2006== FatherNitwit: Fixed character deletion to remove guild member entries. (Aeris1) -==03/24/2006 +==03/24/2006== Doodman: Added live struct strategy. Can get to char select -==03/22/2006 +==03/22/2006== Doodman: Fixed Ti items Doodman: Added some field name mappings to load_13thfloor_items script. UPDATE items SET stackable=1 WHERE itemtype IN (14, 15, 17, 18, 19, 27, 37, 38, 55, 56); -==03/22/2006 +==03/22/2006== FatherNitwit: Refactored the netcode to move the opcode manager back into the stream. -==03/21/2006 +==03/21/2006== Doodman: Merge up from Source branch Doodman: Fixed CreateItem -==03/20/2006 +==03/20/2006== FatherNitwit: Fixed invisibility vs. undead to only apply to undead mobs. FatherNitwit: Added `slot` column to starting items to support much more robust configurations. FatherNitwit: Updated char backups. (noted by typhoon) @@ -9521,11 +9532,11 @@ ALTER TABLE character_backup DROP guild; ALTER TABLE character_backup DROP guildrank; ALTER TABLE character_backup DROP publicnote; -==03/19/2006 +==03/19/2006== FatherNitwit: A bunch of minor changes, should help some bugs in 0.6.6. FatherNitwit: Wrote basic guild management pages, much more needed. -==03/18/2006 +==03/18/2006== FatherNitwit: Queue fix in netcode, should help crashing problem. Doodman: Redid world port allocation not start looking for a port at the begining each time Doodman: Made zone send it's port to world on connect, in case it is reconnecting to a restarted world @@ -9603,10 +9614,10 @@ ALTER TABLE character_ DROP guildrank; ALTER TABLE character_ DROP publicnote; -==03/11/2006 +==03/11/2006== FatherNitwit: Redid netcode outbound queue to properly handle sequence wrapping. -==03/10/2006 +==03/10/2006== Doodman: Worked up serialization for Ti and Live (/snicker) Doodman: Converted the item table to match live (13th-floor) changes below (apply both) Doodman: Worked items to be compatible for both (kept loreflag and loregroup) @@ -9637,37 +9648,37 @@ ALTER TABLE items ADD UNK134 varchar(255) not null AFTER UNK133; ALTER TABLE items CHANGE UNK130 potionbeltslots int not null; ALTER TABLE items CHANGE UNK133 stackable int not null; -==03/08/2006 +==03/08/2006== FatherNitwit: (BatCountry) Fixed throwing item crash. FatherNitwit: (Hvitrev) Implemented Primsatic and Chromatic resists. -==03/07/2006 +==03/07/2006== FatherNitwit: Fixed up adding a launcher to not falsely report an error. Doodman: Fixed sending old style info from world->ls (fixing minilogin) Doodman: Fixed TCPConnection cleanup on socket closed by the remote -==03/06/2006 +==03/06/2006== Doodman: Fixed deadlock when zones are shutting down. Doodman: Fixed MakeUpperString and MakeLowerString to function correctly on a NULL string Doodman: Made HTTPSocket inherit from TCPConnection instead of using Doodman: Fixed HTTP POST processing (needs a good workout still) -==03/05/2006 +==03/05/2006== FatherNitwit: Developed stream proxy, stream identifier, and struct strategy to support dynamic structures. FatherNitwit: Developed initial patch support for 0.6.2 and Titanium. -==03/04/2006 +==03/04/2006== FatherNitwit: Fixed some broken logging in world. FatherNitwit: Fixed the potential crash when zone connects to world. -==03/04/2006 +==03/04/2006== FatherNitwit: Significantly refactored the old TCPConnection/TCPServer code. FatherNitwit: Rewrote almost the entire web server to get rid of most of the socket lib. FatherNitwit: Removed everything but the HTTP framework from the socket lib. FatherNitwit: Rewrite better have fixed the windows crash. -==02/28/2006 +==02/28/2006== Changed version number to 0.6.5-DR1 Doodman: Put in new XML configuration object and made zone/world use them Doodman: Added auto ip configuration to world/LS @@ -9710,7 +9721,7 @@ CREATE TABLE commands ( ); -==01/20/06 +==01/20/2006== FatherNitwit: Reworked melee and ranged attack code significantly. FatherNitwit: Properly support recomended level on weapon DMG now. FatherNitwit: Re-implemented racial/body bane damage and elemental damage. @@ -9720,17 +9731,17 @@ FatherNitwit: Added canbind flag to zone table to completely prevent binding in Required SQL: ALTER TABLE zone ADD canbind TINYINT NOT NULL DEFAULT '1'; -==01/14/06 +==01/14/2006== FatherNitwit: Fixed ranged misses not generating hate. FatherNitwit: Find is as working as good as it ever was (straight line) -==01/13/06 +==01/13/2006== FatherNitwit: Made some changes to buff stacking rules reguarding DoTs and beneficial overwrite. -==01/12/06 +==01/12/2006== FatherNitwit: Fixed the "Buffs do not apply effects in client after zoning" issue. -==01/09/06 +==01/09/2006== FatherNitwit: Made NPCs actually load: resists, attack_speed, findable FatherNitwit: npc_types cleanup day (they dont work or are not used): ALTER TABLE npc_types ADD attack_speed float NOT NULL default '0'; @@ -9738,16 +9749,16 @@ ALTER TABLE npc_types DROP ipc; ALTER TABLE npc_types DROP banish; ALTER TABLE npc_types DROP social; -==01/08/06 +==01/08/2006== FatherNitwit: Fixed more windows compile errors on .net 2003+... -==01/07/06 +==01/07/2006== FatherNitwit: Fixed VS.net projects and compiling. (.net 2002) -==01/04/06 +==01/04/2006== FatherNitwit: Fixed the quad special attack to not require triple in order to work. -==01/03/06 +==01/03/2006== FatherNitwit: Added new debug logging facility. Will be implemented further in the time to come FatherNitwit: Added command #mlog to manipulate the new logging facility. FatherNitwit: Load log settings from log.ini if present. @@ -9766,7 +9777,7 @@ FatherNitwit: Fixed NPC buffing faction checks. FatherNitwit: Added LOS checks for detrimental AOE spells. FatherNitwit: Changed version number to 0.6.4DR1 -==12/30/05 +==12/30/2005== FatherNitwit: Fixed event_waypoint in quests FatherNitwit: Refactored a lot of NPC pathing code (no functional changes) FatherNitwit: Added zone flagging concepts/enforcement @@ -9781,7 +9792,7 @@ CREATE TABLE zone_flags ( ); ALTER TABLE zone ADD flag_needed VARCHAR(128) NOT NULL DEFAULT ''; -==12/23/05 +==12/23/2005== FatherNitwit: Added #reloadstatic to reload doors, objects, zone points, etc... (zone in/out required) FatherNitwit: Fixed mobs to buff their friends, not their enemies. FatherNitwit: Allow support for quests on charmed NPCs. @@ -9789,14 +9800,14 @@ FatherNitwit: Changed quest timers to be tied to the specific NPC, instead of th FatherNitwit: Fixed quest global expiration times... durations should be accurate now. FatherNitwit: Re-enabled Y (year) expiration times, and added a 'F' time to say it dosent expire. -==12/18/05 +==12/18/2005== FatherNitwit: Removed doors from shared memory. FatherNitwit: Hopefully fixed pottery firing in kilns. -==12/15/05 +==12/15/2005== FatherNitwit: Fixed Levitate. -==12/11/05 +==12/11/2005== FatherNitwit: Removed some worthless fields. FatherNitwit: Rewrote all the pet creation code to be based on npc_types FatherNitwit: Completely changed the pets table. @@ -9806,68 +9817,68 @@ You must load up the SQL in Sql-Files/pets.sql the new pets sql assumes NPC IDs 500-700 are free, so edit them as needed. ALTER TABLE npc_types DROP fixedz; -==12/09/2005 +==12/09/2005== FatherNitwit: Fixed the issue where maxed out factions cannot be raised/lowered FatherNitwit: Changed faction hits to properly ignore illusions. FatherNitwit: Fixed an issue with converted chars and corrupt AAs. (not fully resolved) FatherNitwit: Fixed the issue with mobs not pathing after respawning. -==12/01/2005 +==12/01/2005== FatherNitwit: added USE_RACE_CLASS_XP_MODS (disabled by default) to features.h FatherNitwit: These fields are not used: ALTER TABLE zone_points DROP keep_x; ALTER TABLE zone_points DROP keep_y; -==11/28/2005 (LiveCompat) +==11/28/2005== Doodman: Found additional world opcodes Doodman: Updated char select struct -==11/24/2005 +==11/24/2005== FatherNitwit: just some SQL updates you should run to make the recent aggro changes less annoying. UPDATE npc_types SET _INT=80 WHERE _INT=75; UPDATE npc_types SET _INT=60 WHERE _INT=80 AND ( bodytype=3 OR bodytype=8); ALTER TABLE npc_types CHANGE _INT _INT mediumint(8) unsigned NOT NULL default '80'; -==11/17/2005 +==11/17/2005== FatherNitwit: Fixed a crash in the map code FatherNitwit: Tweaked mob usage of d_meele_texture* -==11/11/2005 +==11/11/2005== FatherNitwit: Changed most double prescision operations to single prescision. -==11/10/2005 +==11/10/2005== FatherNitwit: Fixed waypoint ID limit (was 65535). FatherNitwit: Implemented the #wpinfo command -==11/05/2005 +==11/05/2005== FatherNitwit: Fixed the broken server side filters (reworked entirely). FatherNitwit: Refactored damage code to be more consistent between PCs and NPCs. FatherNitwit: Changed spell damage mechanism slightly to better utilize runes. FatherNitwit: Tweaked a bit of illusion code to be more versitile. -==11/03/2005 +==11/03/2005== FatherNitwit: Fixed AA storage in the player profile. -==10/28/2005 +==10/28/2005== FatherNitwit: Fixed a lot of GCC 4.0 and 64 bit problems. Should build under both better now. -==10/24/2005 +==10/24/2005== FatherNitwit: Fixed mob int loading. -==10/23/2005 +==10/23/2005== FatherNitwit: Fixed Client->NPC->Client riposte. -==09/28/2005 +==09/28/2005== FatherNitwit: added quest::attacknpc and quest::attacknpctype FatherNitwit: Cleaned up usage of Appearance in the code FatherNitwit: Fixed faction ally checking for npc->npc faction FatherNitwit: Removed some stupid rules reguarding npc aggro FatherNitwit: Fixed some faction issues with aggro, guards should work reliably now. -==09/25/2005 +==09/25/2005== FatherNitwit: Fixed incorrect usage of EXPMod variable. -==09/24/2005 +==09/24/2005== FatherNitwit: Hopefully fixed bard instruments. FatherNitwit: Fixed crash when loading DoD+ spells_us.txt file FatherNitwit: Rewrote a ton of the perl parser internals to suck less. @@ -9882,7 +9893,7 @@ ALTER TABLE npc_types ADD _INT MEDIUMINT UNSIGNED NOT NULL DEFAULT '75'; ALTER TABLE npc_types ADD WIS MEDIUMINT UNSIGNED NOT NULL DEFAULT '75'; ALTER TABLE npc_types ADD CHA MEDIUMINT UNSIGNED NOT NULL DEFAULT '75'; -==09/23/2005 +==09/23/2005== FatherNitwit: Inverted XY coordinates on in zone objects and ground spawns. FatherNitwit: Maybe fixed quests setting a waypoint in EVENT_WAYPOINT ALTER TABLE ground_spawns ADD temp_x float NOT NULL; @@ -9900,40 +9911,40 @@ UPDATE object SET ypos=temp_x; ALTER TABLE object DROP temp_x; -==09/22/2005 +==09/22/2005== FatherNitwit: Reworked quest signaling implementation to be less dumb. Also exported SignalNPC to perl. FatherNitwit: Implemented a missing bard AE DOT spell type. -==09/21/2005 +==09/21/2005== FatherNitwit: Fixing a few issues with windows compiles and corpses. -==09/19/2005 +==09/19/2005== FatherNitwit: Added missing packet for player death to create a corpse. -==09/16/2005 +==09/16/2005== FatherNitwit: Fixed loot error case client hang, reported by Yablargo -==09/15/2005 +==09/15/2005== FatherNitwit: Fixed coin trading bug reported by Windcatcher -==09/12/2005 +==09/12/2005== FatherNitwit: Fixed adventure merchants -==09/11/2005 +==09/11/2005== FatherNitwit: Made temp merchant lists clear on world boot. FatherNitwit: Fixed some shared memory issues on windows. FatherNitwit: exported some more mob methods to perl. -==08/26/2005 +==08/26/2005== Doodman: Found most of the missing guild opcodes, except the manage ones. Doodman: Moved guildlist to shared mem (ick) since zone now sends it too. Doodman: Need to implement a way to recover the functionality of dynamicly adding/removing guilds with the shared mem solution -==08/23/2005 +==08/23/2005== FatherNitwit: Added range check for all combat abilities (reported by LoOsEr) -==08/21/2005 +==08/21/2005== FatherNitwit: Fixed gaining AA exp (buying still broken) FatherNitwit: Fixed weapon procs FatherNitwit: Added looting messages (links to come later) @@ -9941,28 +9952,28 @@ FatherNitwit: Fixed instant spells acting like DoTs --- Version Changed to 0.6.2DR1 --- -==08/05/2005 +==08/05/2005== FatherNitwit: Adding database support for both eye colors and beard type. Required SQL: ALTER TABLE npc_types ADD luclin_eyecolor2 int(10) unsigned NOT NULL default '1' AFTER luclin_eyecolor; ALTER TABLE npc_types ADD luclin_beard int(10) unsigned NOT NULL default '0' AFTER luclin_beardcolor; -==08/03/2005 +==08/03/2005== FatherNitwit: Work on eqextractor to support the newest patch. FatherNitwit: Worked on spawn struct to identify the remaining missing fields. -==08/02/2005 +==08/02/2005== FatherNitwit: Added initial fix for the shared bank dupe. -==07/28/2005 +==07/28/2005== FatherNitwit: Fixed $wp in EVENT_WAYPOINT FatherNitwit: added #aggro to query aggro checking info for mobs. FatherNitwit: fixed a couple strange things in the aggro code. -==07/07/2005 +==07/07/2005== FatherNitwit: Untested fix for zone point wildcards in GetClosestZone* -==05/09/2005 +==05/09/2005== FatherNitwit: Fix some stuff related to zoning process. update zone set min_status=0 where min_status is null; update zone set min_level=0 where min_level is null; @@ -9970,12 +9981,12 @@ alter table zone change min_status min_status tinyint(3) unsigned default 0 NOT alter table zone change min_level min_level tinyint(3) unsigned default 0 NOT NULL; alter table zone change long_name long_name text NOT NULL DEFAULT ''; -==05/05/2005 +==05/05/2005== FatherNitwit: Theoretically fixed a no-drop trading bug. ... Lots of work on 5/12/05 patch ... -==05/05/2005 +==05/05/2005== FatherNitwit: A bunch of work on ranged attacks and attack timers. FatherNitwit: Fixed issue with the first time a new char gains exp. FatherNitwit: Fixed delete character at char select. @@ -9983,22 +9994,22 @@ FatherNitwit: Fixed fizzle messages. FatherNitwit: Fixed /goto and other cross-zone player teleporting. FatherNitwit: Initial work on making arrows show up when shot. -==04/23/2005 +==04/23/2005== LethalEncounter: Fixed compile errors on Windows LethalEncounter: Fixed guild crash bug while logging in LethalEncounter: Fixed most of the guild commands -==04/13/2005 +==04/13/2005== Cofruben: Fixed possible crashes using #npscpawn command. Cofruben: Added a few more cheat locators. -==04/10/2005 +==04/10/2005== FatherNitwit: Reworked zone in code to make it more closely match live's ordering. FatherNitwit: Fixed up stream factory problem with dynamic zones. FatherNitwit: Added detailed connecting debugging. FatherNitwit: Work on custom titles. -==04/08/2005 +==04/08/2005== FatherNitwit: Completely rewrote zoning code to make sense. FatherNitwit: Fixed some discipline related effects FatherNitwit: Fixed archery hit chance cap (was 47%) @@ -10018,14 +10029,14 @@ FatherNitwit: Finally fixed no drop items in tradeskill containers for real this Required SQL (added to upgrade.sql): ALTER TABLE account ADD hideme TINYINT NOT NULL DEFAULT 0; -==04/04/2005 +==04/04/2005== FatherNitwit: Fixed guild member list struct, identified more fields of it (showeq) FatherNitwit: (bUri) Fixed attack code with non-weapons and h2h weapons. FatherNitwit: Split zone fog types into seperate DB fields. FatherNitwit: Differentiated between guild and player tributes. Required SQL: utils/0.6.1-upgrade.sql (will be updated until release) -==01/15/2005 +==01/15/2005== Doodman: Fixed charges/quantity Doodman: Fixed IsStackable() Doodman: Fixed some empty Handle() functons. @@ -10033,7 +10044,7 @@ Doodman: Added instance level nodrop. Required SQL: alter table inventory add instnodrop tinyint(1) unsigned default 0 not null; -==01/13/2005 +==01/13/2005== Doodman: Merged source (FNW changes) in to LiveCompat Doodman: SendItemPacket's are now sent deflated (speeds up merchants) Doodman: Combining is now turned on in makefile.perl for zone @@ -10046,7 +10057,7 @@ Doodman: New item structure is in: eqitems.13th-floor.org Item table convert script: utils/items-0.6.0-DR2-0.6.1-DR1-convert.sql -==01/10/2005 +==01/10/2005== FatherNitwit: Disable sleep() in perl, it is bad news. FatherNitwit: Fixed guild MOTD at login (hopefully). FatherNitwit: Fixed minor security problem in SQL logging. @@ -10054,7 +10065,7 @@ FatherNitwit: Added error reporting to waypoint editing commands. FatherNitwit: Reworked client packet handling to use opcode dispatch routines, which is paving the way for dynamic opcodes. -==01/07/2005 +==01/07/2005== FatherNitwit: Fixed rogue-like skill training (sneak, etc..) FatherNitwit: Added spawn_limit field to spawnentry and npc_types which represent the maximum number of that npc_type which can spawn @@ -10074,13 +10085,13 @@ Required Queries: ALTER TABLE spawnentry ADD spawn_limit TINYINT NOT NULL DEFAULT '0'; ALTER TABLE npc_types ADD spawn_limit TINYINT NOT NULL DEFAULT '0'; -==01/04/2004 +==01/04/2004== Doodman: opcode/struct changes to allw zoning in on live. -==12/29/2204 +==12/29/2004== Doodman: Fixed leak in Separator -==12/21/2004 +==12/21/2004== FatherNitwit: fixed item handing to non-quest NPCs FatherNitwit: delete nodrop or norent items left in world containers FatherNitwit: changed quest::spawn commands to return the NPC ID of the spawned mob (in XS) @@ -10109,27 +10120,27 @@ FatherNitwit: Cleaned up eq_packet_structs.h a bit FatherNitwit: Required DB update: ALTER TABLE character_ ADD extprofile BLOB NOT NULL; -==11/20/2004 +==11/20/2004== Doodman: Fixed merchant selling bug on merchant slot -==11/18/2004 +==11/18/2004== Doodman: Fixed the initialization of augs on NPC loot that was causing a zone crash when looting NPC's -==11/16/2004 +==11/16/2004== Doodman: Implemented Augmentation inserting, removal and distilling. Doodman: Augment saves in inventory, shared bank, player_corpse and object_contents. Doodman: Fixed world to detect and cleanup a zone disconnect immediately instead of when it tries to write to it. Doodman: Reimplmented cursor queue using an actually queue. Should be unlimited depth now. Doodman: Updated items for new fields and new names. -==11/10/2004 +==11/10/2004== LethalEncounter: Fixed a couple of corpse related bugs. LethalEncounter: Added in various functions for the new login system. -==11/09/2004 +==11/09/2004== FatherNitwit: Fixed minor crash with new grid delay thing. -==11/08/2004 +==11/08/2004== LethalEncounter: Fixed loot bug with stacked items. LethalEncounter: Added a 10 second timer to grid assignments to speed up zone in time. FatherNitwit: Fixed invalid bind zone crash. @@ -10151,22 +10162,22 @@ FatherNitwit: Rewrote NPC spell casting code to be a ton more effecient FatherNitwit: Run this query (value of 1 == NPC might aggro other NPCs): ALTER TABLE npc_types ADD npc_aggro TINYINT NOT NULL DEFAULT '0'; -==11/07/2004 +==11/07/2004== LethalEncounter: Added in three variables per feature request. -==11/06/2004 +==11/06/2004== LethalEncounter: Fixed Merchant bug that wouldnt let you buy anything. LethalEncounter: Fixed stack bug that wouldnt automatically stack any items you bought. -==11/05/2004 +==11/05/2004== LethalEncounter: Client Freeze/Black Screen of Death bugs have been eliminated. LethalEncounter: Added a simple count to our linked list so we can process a few things faster and more efficiently. LethalEncounter: Fixed a small compile bug in regards to corpses. -==11/04/2004 +==11/04/2004== FatherNitwit: Fixed exploit with split that kathgar found. -==11/01/2004 +==11/01/2004== LethalEncounter: Inventory items on zone in are now sent in one packet like live instead of individual packets. This will save on bandwidth and processing time. Cofruben: Added support for /makeleader command. FatherNitwit: Increased max buffs from 15 to 20 @@ -10189,28 +10200,28 @@ FatherNitwit: Fixed NPC -> NPC aggro, need to run these queries onan older DB: ALTER TABLE npc_faction_entries ADD npc_value TINYINT UNSIGNED DEFAULT '0' NOT NULL; UPDATE npc_faction_entries SET npc_value=1 WHERE value<0; -==10/31/2004 +==10/31/2004== LethalEncounter: Aside from a few tweeks, merchants are finished. They will now keep your items after you sell them like live. LethalEncounter: Fixed bug that caused falling damage to be calculated twice. LethalEncounter: Changed Sense Heading to start at 200 for new characters. LethalEncounter: Black screen bug should be fixed now. LethalEncounter: Fixed bash/slam. -==10/30/2004 +==10/30/2004== LethalEncounter: Fixed a few issues with item charges. LethalEncounter: Fixed illusions. LethalEncounter: Rewrote the merchant code. Its now much more efficient and instead of 81 database queries per shop request, a typical request doesnt pull anything from the db. It loads the information at load time. LethalEncounter: Added in the beginning parts of temporary merchant items. (you can sell items to merchants and purchase them back) Download the merchantlist_temp table from: http://www.eqemulator.net/sql/merchantlist_temp.sql -==10/27/2004 +==10/27/2004== Cofruben: Added LDoN adventure system.Please,source the new sql file(thanks to skorch for testing). LethalEncounter: Mob deaths should be sent to all players now. LethalEncounter: ServerType 1 will make you pvp on zone in now. Still trying figure out why the client doesnt send any pvp special combat abilities unless your dueling. -==10/26/2004 +==10/26/2004== Scorpious2k: Fixed guild doors -==10/25/2004 +==10/25/2004== FatherNitwit: Fixed zone crash related to character_ query. FatherNitwit: Fixed book reading to give proper book window. FatherNitwit: At least partialy fixed pet dual weild/double attack problem. @@ -10219,11 +10230,11 @@ FatherNitwit: Fixed archery ammo issue. LethalEncounter: Added ability to /consent (and /deny) other players so they can drag your corpse. LethalEncounter: Fixed bug where other people were able to loot your corpse. -==10/24/2004 +==10/24/2004== LethalEncounter: Fixed bug where you lost air supply on zoning in underwater. LethalEncounter: Fixed GM damage bug. -==10/23/2004 +==10/23/2004== FatherNitwit: Enable named quests by default (in features.h) FatherNitwit: Fixed decaying world containers problem FatherNitwit: Fixed undead nuke spells @@ -10237,16 +10248,16 @@ LethalEncounter: Fixed Bazaar trader bug. LethalEncounter: Zones now display the proper fog when you update your zone table with the latest. LethalEncounter: Fixed zone crash and merchant item charge bug. -==10/22/2004 +==10/22/2004== LethalEncounter: Fixed item deletion bug not updating client. -==10/21/2004 +==10/21/2004== LethalEncounter: Fixed various AA related bugs. -==10/20/2004 +==10/20/2004== LethalEncounter: Reworked zoning in, solved a couple of issues and should speed the process up a tad. -==10/19/2004 +==10/19/2004== FatherNitwit: Fixed Disciplines and Abilities (lay hands & harm touch) FatherNitwit: Added new server variable: DisableNoDrop (set to 1 takes nodrop off items when loaded) FatherNitwit: Fixed NPC buffing through walls @@ -10255,28 +10266,28 @@ LethalEncounter: Fixed equip bug when using two handed weapons. LethalEncounter: Fixed resurrect bug that let you regain xp more than once. LethalEncounter: Fixed bug that was making players corpses disappear on death when they shouldnt have. -==10/18/2004 +==10/18/2004== LethalEncounter: Fixed pet only spells. LethalEncounter: Fixed bug that wouldnt let you loot a corpse if someone got an error looting it. (such as a dupe lore error) LethalEncounter: Fixed mana regen bug. -==10/17/2004 +==10/17/2004== LethalEncounter: Added some debug code to make it easier for people that cant get minilogin working. LethalEncounter: You will now eat/drink like eqlive if you arent a GM. LethalEncounter: Fixed infinite recursion bug I made a few days ago :/ -==10/16/2004 +==10/16/2004== LethalEncounter: Fixed mana bug that was causing people not to regen their full mana. LethalEncounter: HP Adjustments, will now send your hp update every tic and only the mob you have targeted (not including group hp updates) to reduce bandwidth. LethalEncounter: Fixed a crash bug on character creation. Thanks to blahblah for the help :P LethalEncounter: Fixed item charge bug, need to update your database. -==10/15/2004 +==10/15/2004== LethalEncounter: Changes necessary for the new MiniLogin :) LethalEncounter: Z loc fix, it was placing you ten times the z loc it was supposed to :P -==10/14/2004 +==10/14/2004== Rogean: omg we are so teh 0.6.0DR1 now~ FatherNitWit: @@ -10393,11 +10404,11 @@ FatherNitwit Improvements: - If a player logs/zones in to a Z below the world, they are placed at a valid Z if possible. - labeled new item field as attuneable, updated DB code for it -==10/10/2004 +==10/10/2004== LethalEncounter: Fixed some issues with hp and mana. LethalEncounter: Fixed bug with zoning z coord. -==10/09/2004 +==10/09/2004== LethalEncounter: Fixed spell scribing and meming. LethalEncounter: Merged in Xabob's expansion fixes (except regen which FNW is working on). LethalEncounter: Merged in RangerDown's fixed version of #spawnfix. @@ -10405,259 +10416,259 @@ LethalEncounter: Fix for #zcolor that was brought up by rmanders on the bug foru LethalEncounter: Character conversions from 5.7 to 5.9 should be fixed now. LethalEncounter: Implicit length fix and mana struct fixed. -==10/05/2004 +==10/05/2004== FatherNitwit: Fix name generator opcode FatherNitwit: Added loot table drop chance fix FatherNitwit: Cleaned up some map code and other thing -==10/02/2004 +==10/02/2004== LethalEncounter: Updated AAs so they are sent based on class now. -==10/02/2004 +==10/02/2004== LethalEncounter: Updated to version 5.9-DR2 (EQLive Compatible). -==10/02/2004 +==10/02/2004== LethalEncounter: Updated all the structs/opcodes necessary to login using the latest client. -==10/02/2004 +==10/02/2004== LethalEncounter: Misc AA fixes. -==09/29/2004 +==09/29/2004== LethalEncounter: Fixed AAs, you can now buy them correctly. Big thanks to Xabob for PacketCollecting the information for me!! -==09/26/2004 +==09/26/2004== LethalEncounter: Fixed the displaying of AAs, moving them into two tables. Be SURE to source aa.sql for these updates. (Will fix the buying aspect soon) -==09/25/2004 +==09/25/2004== Doodman: Fixed linux kernel 2.6.x shared memory bug. IPC_NOWAIT is not allowd on shared memory segment operations. -==09/20/2004 +==09/20/2004== Scorpious2k: Allow multiple Perl plug-ins -==09/17/2004 +==09/17/2004== Doodman: (un)fixed world to hand the LS->world hand off like it did in the past -==/09/12/2004 +==/09/12/2004== FatherNitwit: Added my LOS code and map converter. Thanks to wiz for help with testing. -==/09/09/2004 +==/09/09/2004== FatherNitwit: Adding basics of spell specialization. -==/09/07/2004 +==/09/07/2004== FatherNitwit: Fixed #heal on pets. -==/09/05/2004 +==/09/05/2004== FatherNitwit: Fixed minor flaw in player profile with skills. FatherNitwit: Merged My Tradeskill Rewrite LethalEncounter: Moved zone configurations from the old flat file method to the database. BE SURE TO SOURCE ZONECFG.SQL!!! -==/09/04/2004 +==/09/04/2004== FatherNitwit: Merged in ProjectEQ's 2grid system FatherNitwit: Fixed new style hate/regen/mana regen on items FatherNitwit: Collected PoP item features from equiped, not used yet LethalEncounter: Fixed /bug LethalEncounter: Fixed merchant messages/issues -==/09/03/2004 +==/09/03/2004== FatherNitwit: Fixed almost ALL compile warnings on UNIX FatherNitwit: Added my named quest files patch as optional #ifdef FatherNitwit: Added persistent timers (client->p_timers) FatherNitwit: Added 508->588 byte .cfg file converter -==/08/31/2004 +==/08/31/2004== FatherNitwit: Fixed MOB point-blank AE spells to cast (from PEQ folks) FatherNitwit: Fixed Spawned MOB's being permarooted. -==/08/29/2004 +==/08/29/2004== LethalEncounter/Cofruben: LOY Guild Management tool updated. Cofruben: Added new perl function: quest::addloot(itemid,charges). -==/08/28/2004 +==/08/28/2004== Cofruben: Implemented inspect code. -==/08/26/2004 +==/08/26/2004== Wiz: Fixed a bug that was displaying unknown spells on login. Wiz: Fixed a bug that was causing hp to go wonky when you join a group. Wiz: Fixed a bug that was preventing you from attacking when invulnerable. Wiz: Fixed a bug that would cause invulnerability spells to stop working if you zoned. -==/08/25/2004 +==/08/25/2004== FatherNitwit: Redid split, implemented autosplit. -==/08/23/2004 +==/08/23/2004== Draupner: Fixed spell haste focuses -==08/21/2004 +==08/21/2004== LethalEncounter: Finished updating few structs, can now login with latest client. Merchants, combat, loot, etc work fine. -==08/20/2004 +==08/20/2004== LethalEncounter: Fixed player profile. -==08/12/2004 +==08/12/2004== Cofruben:Added basics of begging. Cofruben:Added group splitting. -==08/10/2004 +==08/10/2004== Wiz: Severely cleaned up position update code and made it more efficent. Should drastically reduce update packets generated by other clients. Wiz: Improved the LOS calculation. It should use less CPU and work better now. -==08/02/2004 +==08/02/2004== Scorpious2k: Changed Quest command settimer to reuse timers with same name instead of creating new -==07/26/2004 +==07/26/2004== Cofruben: Added some perl functions,including: ding,addldonpoint,surname,permaclass/race/gender,scribespells. -==07/20/2004 +==07/20/2004== Draupner: Added #npcedit -==07/19/2004 +==07/19/2004== Doodman(WizeOne): Added beacon.o and embxs.o to makefile.perl Doodman: Fixed #serverinfo for linux. -==07/12/2004 +==07/12/2004== Doodman(RangerDown): Added "You cannot attack while invulnerable" message. -==07/10/2004 +==07/10/2004== Scorpious2k: Fixed AA related exploit Scorpious2k: Fixed AA problem where % remained if player lost levels to below 51 -==06/27/2004 +==06/27/2004== Scorpious2k: Added quest command spawn2 - same as spawn except also requires heading Scorpious2k: Modified Quest Globals. It now always creates a unique var if char specific (not ALL players) Scorpious2k: Enabled traders Sandy: Changed mobs so they must have SPECATK_SUMMON to summon players -==06/23/2004 +==06/23/2004== Rogean: Mobs no longer Dual Wield with Shields -==06/22/2004 +==06/22/2004== Sandy: Event Aggro added Sandy: Permaroot added Sandy: x,y,z variables for quests corrected -==06/17/2004 +==06/17/2004== Dspirit: More patch 6/16 opcodes corrected -==06/16/2004 +==06/16/2004== Dspirit: Patch 6/16 opcodes corrected -==06/14/2004 +==06/14/2004== Dspirit: More opcodes corrected -==06/13/2004 +==06/13/2004== Dspirit: Multiple opcodes corrected -==06/12/2004 +==06/12/2004== LethalEncounter: #zone fixed. Rogean: Fixed inspecting items in adventure window. Rogean/Scorpious2k: AA Fixes Implimented -==06/11/2004 +==06/11/2004== Doodman: Fixed OP_HPUpdate and SendHPUpate. Client now gets proper HP updates. -==06/11/2004 +==06/11/2004== Scorpious2k/Image: #reloadqst now works for perl (Reloads the entire zones quest files). -==05/29/2004 +==05/29/2004== Scorpious2k: Changed opcode to fix trade windows >> THANKS Dspirit -==05/29/2004 +==05/29/2004== Scorpious2k: Merged in Sandy's EVENT_HP code Scorpious2k: Merged in Bleh's Perl/XS enhancement Scorpious2k: Added quest commands for mob movement stop(),start(),pause(),resume() and moveto() -==05/28/2004 +==05/28/2004== Doodman/Bleh: Move NPCTypes out of shared mem in to zone specific STL maps. Doodman/Bleh: Removed NPCTypes.o from makefile in EMuShareMem Doodman/Bleh: Fixed null pointer dereference in selling -==5/15/2004 +==5/15/2004== Doodman: Upped MMF_MAX_NPCTYPE_ID to 400k to support tcsmyworlds db Doodman: Fixed EQNetwork.cpp to be C++ standard compliant, -not- MS compliant -==5/11/2004 +==5/11/2004== Image: Better netcode, rawr. Scorpious2k: Added AC to mobs - be sure to check MobACupdate.sql Scorpious2k: Changed AC so it can now be controlled with serverOp variables ACreduction, ACrandom & ACfail Scorpious2k: Added check so items with req level can't be auto equipped if player not >= req level Scorpious2k: Added generic (theme = 0) LDoN point processing -==5/6/2004 +==5/6/2004== Image: Fixed tradeskill places like smiths -==4/28/2004 +==4/28/2004== Image: EQNetwork changes to get packets to send in order Image: Fixed zones getting bugged (unable to attack, see others, etc.) -==4/23/2004 +==4/23/2004== LethalEncounter: Emu works with the latest patch now. Sony did some crazy crap and added 4608 bytes to player profile :/ -==4/20/2004 +==4/20/2004== Image: Lifetap now works on PVP Image: Fixed the stacks of snare/root/speed, snare breaks speed spells (SOW,JBoots,etc.), root breaks snare, root breaks speed spells also. -==4/17/2004 +==4/17/2004== LethalEncounter: Fixed merchants that were broken in the last patch. Image: Inventory item combining added to decrease the traffic sent on zoning. -==4/16/2004 +==4/16/2004== LethalEncounter: Fixed item struct that was changed in the last patch, items now work again. -==4/15/2004 +==4/15/2004== LethalEncounter: Fixed Player Profile that was changed in the last patch, you can now login correctly. -==4/08/2004 +==4/08/2004== Scorpious2k & Tark: Fixed incompatability problem with Perl and combined packets -==4/02/2004 +==4/02/2004== Image: Improvements made on the combination code. -==3/29/2004 +==3/29/2004== Image: Combined packets now work to full extent. Set app priority to 6 if you don't want it combined, all packets should no longer be deflated,encrypted unless priority 6. Scorpious2k: Fixed mob movement -==3/28/2004 +==3/28/2004== Scorpious2k: Added quest command signal() and EVENT_SIGNAL Scorpious2k: Made mobs face player for EVENT_SAY and EVENT_ITEM Scorpious2k: Added smogo's $hasitem for perl quests Scorpious2k: Added selfcast() command to perl quests -==3/23/2004 +==3/23/2004== Scorpious2k: Added AC. Probably needs major adjusting of values to match live, but its a start. -==3/22/2004 +==3/22/2004== image: Group inviting is fixed. -==3/21/2004 +==3/21/2004== solar: corrected an issue with channeling skill/regaining concentration solar: changed spell resist so that only detrimental spells are resist checked. will have to flag beneficial flags that need resist checking later. -==3/20/2004 +==3/20/2004== solar: spell resists implemented solar: added/fixed up some spell effects. notable ones are the stackable hastes like melody of ervaj solar: added new version of Wiz's CheckLos routine solar: fixed a problem with detrimental buffs (root, mez) not working in pvp -==3/15/2004 +==3/15/2004== solar: door updates, find sql file in release dir - doorupdate-03-15-04.sql -==3/13/2004 +==3/13/2004== solar: fixed zone to zone movement occasionally not working -==3/10/2004 +==3/10/2004== solar: spells that are group only should only work on the group now. -==3/07/2004 +==3/07/2004== solar: moving coin around should work properly now -==3/06/2004 +==3/06/2004== solar: looted stackable items should distribute properly now. -==3/05/2004 +==3/05/2004== solar: player armor will appear the proper color now solar: LoY Dye works. You need a vial of prismatic dye for each slot you want to change the color of. @@ -10666,40 +10677,40 @@ solar: looting differentiates between left and right clicks. left clicking auto equip it. solar: containers can now be auto equip looted -==3/02/2004 +==3/02/2004== solar: implemented AE location spells. ex: rain of lava, sentinel. small issue with this still; the caster continues to execute the casting animation for the duration of the spell. -==3/01/2004 +==3/01/2004== solar: started implementing AE rain type spells, still some work left to do. There are 2 new files for a Beacon class that you have to add to the build. -==2/29/2004 +==2/29/2004== solar: revised and corrected a ton of spell effect stuff solar: corrected an hp regen issue, but need to get the proper calculations for regen rates. until corrected, clients will see their hp regen and then immediately change to what the server thinks it should be solar: probably broke something -==2/28/2004 +==2/28/2004== solar: spells will now consume reagents properly solar: lifts work again solar: corrected a mistake that caused spells to not finish scribing properly solar: fixed some memory leak issues regarding hp updates -==2/25/2004 +==2/25/2004== Scorpious2k: Added vesuvias changes for 5.5 - luclin appearence should work now -==2/22/2004 +==2/22/2004== solar: spells should be in working order again, more work to be done on this. -==2/20/2004 +==2/20/2004== solar: merged vesuvias' appearance (face, beard etc) and zone change changes from the old code. -==2/19/2004 +==2/19/2004== kathgar: Fixed, SE_Fear, SE_SpinStun, SE_Charm for fixed duration spells and not double checking breaking Range check on Archery Skill checks on Hide and Sneak @@ -10709,44 +10720,44 @@ kathgar: Fixed, SE_Fear, SE_SpinStun, SE_Charm for fixed duration spells and no No EQLive fixes in this change solar: fixed a bug that was causing people to be set as gm when they shouldn't -==2/15/2004 +==2/15/2004== solar: characters being created are checked for validity now. thanks to kathgar for the tables. -==2/14/2004 +==2/14/2004== Zaphod: Added Door ZonePoint information. -==2/13/2004 +==2/13/2004== solar: Doors should work now solar: adventure merchant stuff fully working (check me) solar: petition interface stuff believed to be working but not fully tested LethalEncounter: (entered by solar) fixed item opcode, items fully working -==2/12/2004 +==2/12/2004== solar: Opcodes updated from Zaphod's list, also a few others. All the ops that are tabbed in are unconfirmed, and I marked unused ones also. -==2/10/2004 +==2/10/2004== Image: NewSource directory created for 0.5.5 (EQLive Compatability Developement) Image: Few opcodes updated, also implicitlen updated. -==2/8/2004 +==2/8/2004== solar: fixed a world crash regarding deleting an empty character slot -==2/7/2004 +==2/7/2004== solar: AE spells working properly now solar: wrote all new buff duration formulas solar: buffs being applied will properly displace multiple buffs if needed solar: bard spells should keep casting while sitting now solar: mobs should now have a proper casting animation when they cast -==2/6/2004 +==2/6/2004== solar: some buff stacking changes, misc spell stuff -==2/5/2004 +==2/5/2004== solar: more spell work, bards should be in a working state, but not done. -==2/3/2004 +==2/3/2004== Image: Divine Aura now works cross zone. Image: Invisibility now works cross zone. Image: Added a zone(""); command to quest (Zones them to the specified short zone name) @@ -10755,43 +10766,43 @@ Image: Tons of my personal GW stuff changed, you can't see bwuhahahhah Image: Fixed platinum duping Image: Levitate now works cross zone (others see you levitate and not warping) -==2/2/2004 +==2/2/2004== Scorpious2k: added quest global variables (requires DB change see quest_globals.txt in EQEmu Release)) Scorpious2k: fixed faction command in parser -==2/2/2004 +==2/2/2004== solar: worked on buff stacking and some misc spell stuff. -==2/1/2004 +==2/1/2004== solar: group spell fixes solar: implemented /targetgroupbuff -==1/31/2004 +==1/31/2004== solar: revised the StringID based messaging. -==1/30/2004 +==1/30/2004== solar: Merged in Wiz's CheckLos() mob method solar: Spell casting fixes. solar: Channeling skill should work very similar to live now. If you end your casting where you started you have a chance to finish casting. solar: Line of sight is checked for spells. -==1/29/2004 +==1/29/2004== LethalEncounter: Fixed an issue with HT and LoH not repopping, apparently fatigue in player profile was moved into the timer hours slot. solar: Updated death. You shouldn't receive an extra message about being punched when you die to a spell anymore. -==1/28/2004 +==1/28/2004== solar: fixed an item dupe bug. -==1/26/2004 +==1/26/2004== solar: various fixes to spells, more coming. -==1/25/2004 +==1/25/2004== LethalEncounter: Fixed some issues with the guilds. LethalEncounter: No Rent items will now be deleted if your offline for more than 30 minutes. -==1/24/2004 +==1/24/2004== LethalEncounter: The guild management tool has been updated and works again. LethalEncounter: Guilds will now work almost 100% like live, only a few of the #guild commands are left over, mainly for gm management. kathgar: Added #revoke and #oocmute @@ -10806,28 +10817,28 @@ solar: /surname command corrects capitalization and rejects non alpha now. solar: #name fixed - renames player target solar: made some fixes to kathgar's revoke patch - it now works as intended. -==1/23/2004 +==1/23/2004== solar: LD clients should now be kicked when the LD timer is up, even if engaged. solar: Buffs should be working correctly after zoning now. solar: #gassign fixes -==1/22/2004 +==1/22/2004== solar: Corpses are now sent to clients zoning in. -==1/21/2004 +==1/21/2004== Trumpcard: Merged in mongrels fix for undead/invis. Use update sql provided in release to update. solar: Buff fading should work for slots besides the first one now. This will fix the 'mez bug' that was due to the icon not being stripped. -==1/20/2004= +==1/20/2004== solar: sense heading skill will now improve as you move around -==1/19/2004= +==1/19/2004== Scorpious2k: fixed faction command for quests LethalEncounter: Fixed a problem with queued cursor items. solar: fixes to #gassign -==1/18/2004= +==1/18/2004== LethalEncounter: Added item and spell bonuses to the mana regen formulas, they will now help. LethalEncounter: Fixed a bug that was killing players even when they had hitpoints remaining. LethalEncounter: Added server side filters @@ -10877,7 +10888,7 @@ LethalEncounter: You will now get hit for max damage when you are sitting and an LethalEncounter: Fixed loot messages. LethalEncounter: Fixed loot so right click autoequips correctly now. -==1/4/04== +==1/4/2004== MYRA: changed to new opcode for follow MYRA: changed to new opcode for taunt MYRA: use new (5.x) Status labels in who for telnet connection @@ -10898,14 +10909,14 @@ MYRA: fixed comma bug for shout command MYRA: added itemlink(ItemNumber) command -==1/2/04== +==1/2/2004== LethalEncounter: Added further support for eqstr_us.txt, this should cut down on lag. Image: Fixed client lockup related to spells. aka 'spell sticking'. -==1/1/04== +==1/1/2004== LethalEncounter: Updated spell structure to new spells_us.txt format. NOTE: You must have the latest eqlive spells_us.txt in your EQEmu directory. -==12/21/03 through 1/1/04== +==12/21/03 Image/LethalEncounter/kathgar/devn00b/Shawn319: Updated/added several opcodes to match eqlive. @@ -10933,100 +10944,102 @@ Server no longer counts item stats in the ammo slot (as it should). Fixed bug with eventlog that would cause certain entries to return a MySQL syntax error. -==12/21/03== +==12/21/2003== LethalEncounter: Ive been busy the last few days updating all the opcodes and structs for the recent patch. Nearly everything works again please post any remaining unknown opcodes to the development forum. -==12/16/03== +==12/16/2003== Image: Fixed #zsave and added #zunderworld so you can set the zcoord for underworld. -==12/15/03== + +==12/15/2003== Image: Took out an HP update in NPC::Damage() reduces HP updates big time and kills network traffic. Image: All HP Updates are done only to targets now, TODO: Queue group clients with HP updates (once they work again) -==12/14/03== + +==12/14/2003== LethalEncounter: Fixed a bug in tradeskills that was clearing the container before it was telling the client to. LethalEncounter: Fixed the opcode for mend. LethalEncounter: Fixed the guild trainer messages. LethalEncounter: Fixed char create so it creates the character with a new aa struct. LethalEncounter: Important fix for mob movement via waypoints and such. Mobs now move nearly exactly like live and it uses 20 times less bandwidth than before. -==12/13/03== +==12/13/2003== LethalEncounter: Fixed Environmental Damage LethalEncounter: Added support for a new database variable called startzone. To use this, add a record called startzone to the variables table and make the value the zone shortname you want players to start in. This will force all players to start in that zone. LethalEncounter: Fixed a bug that Shawn found where /zone wouldnt work correctly in zones with similiar names. -==12/12/03== +==12/12/2003== Shawn319: (Bud9weiser) Fixed start_zones bug. Players should now start in correct location as long as db is up to date. LethalEncounter: Item tint fix. LethalEncounter: Tradeskill items being deleted without correct recipe fix. LethalEncounter: Problem with the ' character inserting into the bug table fixed. -==12/11/03== +==12/11/2003== LethalEncounter: Added Archery in for pvp. LethalEncounter: Fixed a throw bug. LethalEncounter: Fixed char select colors not matching your colors in game. -==12/10/03== +==12/10/2003== LethalEncounter: Fixed the weapon delay bug. Special thanks to haecz for bring this to our attention. LethalEncounter: Fixed dup money bug. Special thanks to afrospy also for bring this to our attention. LethalEncounter: Fixed Bazaar Traders, they should work correctly. -==12/09/03== +==12/09/2003== LethalEncounter: Fixes for item tints and dyes. LethalEncounter: Fixed NPC textures finally. -==12/08/03== +==12/08/2003== LethalEncounter: Fixed Horses/Drogmors. -==12/07/03== +==12/07/2003== LethalEncounter: Fixed a bug with pets causing lag. LethalEncounter: Fixed a crash bug/1017 bug related to login in, people on slow/lagged connections should be able to login fairly consistently now. -==12/02/03== +==12/02/2003== LethalEncounter: Trumpcard and I completely rewrote the linked lists to improve lag, they are about 4 times more efficient now. LethalEncounter: Fixed a small bug with zone points. BE SURE YOUR USING THE MOST UP-TO-DATE TABLE!! -==11/30/03== +==11/30/2003== LethalEncounter: Fixed various loot bugs, including the duplicate lore item, blah blah blah bug.. LethalEncounter: Fixed #zonestatus and /servers to be on multiple lines LethalEncounter: Fixed /who all guild tags to display the right guild LethalEncounter: Added the ability to send multi-lined emotes with the
newline. LethalEncounter: Fixed the bug that gave the message: "Multiplier == 0 in Client::CalcBaseHP" -==11/29/03== +==11/29/2003== Image: Fixed last names for clients (When a new client zoned in, didn't send lastname). -==11/28/03== +==11/28/2003== LethalEncounter: Added a default instance for pets that arent created via legit spells. LethalEncounter: Fixed the spell scribe issues. -==11/25/03== +==11/25/2003== LethalEncounter: Added a new table called ground_spawns. This will allow you to spawn items randomly throughout the zone. Use this for only random locations, use the object table for static ones. LethalEncounter: Added Timers for ground spawns so they will respawn based on the time you set. -==11/24/03== +==11/24/2003== Image: Fixed zoning and deaths so you no longer drop from the server. Image: Properly enabled LDoN on EQEmu. Image: Basic Adventure Merchant code implemented, throwing it to the STL guys to properly optimize. LethalEncounter: Fixed Tradeskill objects. If the objects show up as bags, thats a db issue -==11/23/03== +==11/23/2003== Trumpcard: Several zone performance enhancements, as well as changes to decrease combat lag. LethalEncounter: Reworked pets, they should now work properly. -==11/21/03== +==11/21/2003== LethalEncounter: Merchants wont trade with people while they are fighting now. LethalEncounter: Players can no longer attack while invulnerable. LethalEncounter: Fixed an AA bug that gave the player 199 aa points. -==11/18/03== +==11/18/2003== LethalEncounter: Lotsa cool fixes like crash fixes and guild fixes and cool stuff like that -==11/17/03== +==11/17/2003== LethalEncounter: Finally got /who all done and *hopefully* free of bugs. LethalEncounter: Fixed a crash bug with guilds and added some of the Guildmanagement features. -==11/16/03== +==11/16/2003== Trumpcard: Several deallocate and mem leak errors corrected. solar: Fixed bug with mobs assisting entities they shouldn't. Often this show itself with other mobs helping the player when the player @@ -11034,16 +11047,16 @@ solar: Fixed bug with mobs assisting entities they shouldn't. Often this LethalEncounter: Added in the GuildManagement tool. You will need 2 new columns in your character_ table for this. LethalEncounter: Fixed a couple crash bugs with guilds and spells. -==11/15/03== +==11/15/2003== solar: Fixed a bug in commands where if you didn't define an accesslevel for a command in the addon.ini it would default to accesslevel 0 instead of the hardcoded default. solar: Modified #help command to search for a partial command specified as argument. solar: HP wasn't being updated to client properly and would fall out of sync -==11/14/03== +==11/14/2003== LethalEncounter: Fixed a bug in doors that would cause triggered doors to go into an endless loop and crash. -==11/13/03== +==11/13/2003== kathgar: Fixed a crash when calling ZSList::FindByZoneID() when sending an invalid zone number. Stack was corrupt in the backtrace, so I am not sure what called it in this way. LethalEncounter: Fixed animation bug with attack. @@ -11052,44 +11065,44 @@ LethalEncounter: Character Select will now show your equipment (Existing charact LethalEncounter: NPCs will now equip weapons and items from the db. LethalEncounter: You can now give weapons and items to NPCs to equip. -==11/12/03== +==11/12/2003== LethalEncounter: Fixed skills, we had the struct one int32 off. LethalEncounter: Fixed GM Trainers, they should now work as intended. -==11/11/03== +==11/11/2003== Doodman: Fixed zone crash in CheckCloseArrgo() during assist checking based on flawed if logic. -==11/11/03== +==11/11/2003== LethalEncounter: Fixed Stamina issues. -==11/10/03== +==11/10/2003== LethalEncounter: Fixed Quests. LethalEncounter: A quest fix that makes them use far less memory, up to 25 MB per computer -==11/09/03== +==11/09/2003== LethalEncounter: Fixed zoning, zone_points should now work correctly, but make sure you have the latest ones in zone_points! LethalEncounter: Fixed problem with spells not scribing. LethalEncounter: Fixed zoning bug where you zoned back into the same zone, but it looked like you were in the new zone. -==11/06/03== +==11/06/2003== Image: Guildwars code and fixed a bug in MobAI for NPCs assisting clients, clients do not use IsEngaged(), so it always returned false, now it uses AutoAttackEnabled() in its place for clients. Image: Reversed the AICheckCloseSpells if statements, now sanity checks before distance check, uses less CPU usage. -==11/05/03== +==11/05/2003== LethalEncounter: Updated all of the opcodes that were changed in the patch today. LethalEncounter: Refined AA's some, added table to hold the timers for AA's so users can exploit them. Look in db.sql for the table. -==11/04/03== +==11/04/2003== LethalEncounter: Added in the basic structure for AA's and got some of them working. -==11/03/03== +==11/03/2003== LethalEncounter: Fixed factions. LethalEncounter: Fixed issues with mobs of different factions or no faction assisting. -==10/29/03 to 11/02/03== +==10/29/2003== LethalEncounter: Fixed X, Y coords. LethalEncounter: Added in groups. LethalEncounter: Added in trades. @@ -11105,16 +11118,16 @@ LethalEncounter: Fixed lifts. LethalEncounter: Fixed automatic weather changes. Change the default in the zone table to suit your needs. 1: normal 2: Rainy most of the time 3: Snowing most of the time LethalEncounter: Fixed lotsa other important stuff that I cant remember :) -==10/29/03== +==10/29/2003== devn00b: Yet again removed Faulty pet code. LEAVE THEM ALONE -==10/17/03== +==10/17/2003== Image: Zone points code put in, need some testing done on it please! -==10/15/03== +==10/15/2003== Image: More optimization to the aggro code and an infinite for loop fixed. -==10/14/03== +==10/14/2003== kathgar: Removed deprecated guildwars code Added #ifdefs for IPC code, no more IsInteractive checks wasting cycles Fixed a memleak in NPC with attacked_timer @@ -11124,33 +11137,31 @@ merth: Updated zone objects: * Fully supports imported data from packet collector * Objects dropped to ground persist zone sessions -==10/13/03== +==10/13/2003== Image: Optimizations to Aggro code, ~10% more efficient according to Trumpcard. -==10/11/03== +==10/11/2003== Trumpcard: Lots of performance improvements to combat code/AI code. Streamlined several calls, and found and removed unneeded calls in MobAI that were causing serious bottlenecks. Went through and caught a few memory leaks, and converted many deletes to safe deletes. -==10/10/03== +==10/10/2003== LethalEncounter: Somewhere around this date I added in bazaar traders and cool features like that. -==10/8/03== +==10/8/2003== Trumpcard: Starting items are now in. LE also fixed the combat damage struct and mob hp bar updates. -==10/8/03== +==10/8/2003== scruffy: Inverted the XY coordinates system to work like the EQLive system. Run invertxy.sql to update existing databases. -==10/6/03== +==10/6/2003== kathgar: Added opcodes for things such as the LFG tool from SEQ, replaced some instances of static opcode use -==10/5/03== +==10/5/2003== devn00b: Removed Faulty pet code, replaced with working code. -==09/24/03== +==09/24/2003== Image: Added the functions for the GuildWars point system. -==8/10/03== +==8/10/2003== devn00b: Moved pets from hardcoded to DB based. need to source pets.sql - -============ diff --git a/client_files/export/main.cpp b/client_files/export/main.cpp index db424846d..ebe91353c 100644 --- a/client_files/export/main.cpp +++ b/client_files/export/main.cpp @@ -39,20 +39,19 @@ int main(int argc, char **argv) { LogSys.LoadLogSettingsDefaults(); set_exception_handler(); - Log(Logs::General, Logs::Status, "Client Files Export Utility"); + LogInfo("Client Files Export Utility"); if(!EQEmuConfig::LoadConfig()) { - Log(Logs::General, Logs::Error, "Unable to load configuration file."); + LogError("Unable to load configuration file"); return 1; } auto Config = EQEmuConfig::get(); SharedDatabase database; - Log(Logs::General, Logs::Status, "Connecting to database..."); + LogInfo("Connecting to database"); if(!database.Connect(Config->DatabaseHost.c_str(), Config->DatabaseUsername.c_str(), Config->DatabasePassword.c_str(), Config->DatabaseDB.c_str(), Config->DatabasePort)) { - Log(Logs::General, Logs::Error, "Unable to connect to the database, cannot continue without a " - "database connection"); + LogError("Unable to connect to the database, cannot continue without a database connection"); return 1; } @@ -94,11 +93,11 @@ int main(int argc, char **argv) { } void ExportSpells(SharedDatabase *db) { - Log(Logs::General, Logs::Status, "Exporting Spells..."); + LogInfo("Exporting Spells"); FILE *f = fopen("export/spells_us.txt", "w"); if(!f) { - Log(Logs::General, Logs::Error, "Unable to open export/spells_us.txt to write, skipping."); + LogError("Unable to open export/spells_us.txt to write, skipping."); return; } @@ -165,11 +164,11 @@ int GetSkill(SharedDatabase *db, int skill_id, int class_id, int level) { } void ExportSkillCaps(SharedDatabase *db) { - Log(Logs::General, Logs::Status, "Exporting Skill Caps..."); + LogInfo("Exporting Skill Caps"); FILE *f = fopen("export/SkillCaps.txt", "w"); if(!f) { - Log(Logs::General, Logs::Error, "Unable to open export/SkillCaps.txt to write, skipping."); + LogError("Unable to open export/SkillCaps.txt to write, skipping."); return; } @@ -194,11 +193,11 @@ void ExportSkillCaps(SharedDatabase *db) { } void ExportBaseData(SharedDatabase *db) { - Log(Logs::General, Logs::Status, "Exporting Base Data..."); + LogInfo("Exporting Base Data"); FILE *f = fopen("export/BaseData.txt", "w"); if(!f) { - Log(Logs::General, Logs::Error, "Unable to open export/BaseData.txt to write, skipping."); + LogError("Unable to open export/BaseData.txt to write, skipping."); return; } @@ -225,11 +224,11 @@ void ExportBaseData(SharedDatabase *db) { } void ExportDBStrings(SharedDatabase *db) { - Log(Logs::General, Logs::Status, "Exporting DB Strings..."); + LogInfo("Exporting DB Strings"); FILE *f = fopen("export/dbstr_us.txt", "w"); if(!f) { - Log(Logs::General, Logs::Error, "Unable to open export/dbstr_us.txt to write, skipping."); + LogError("Unable to open export/dbstr_us.txt to write, skipping."); return; } diff --git a/client_files/import/main.cpp b/client_files/import/main.cpp index a8e2ad0fe..109ff4859 100644 --- a/client_files/import/main.cpp +++ b/client_files/import/main.cpp @@ -37,19 +37,19 @@ int main(int argc, char **argv) { LogSys.LoadLogSettingsDefaults(); set_exception_handler(); - Log(Logs::General, Logs::Status, "Client Files Import Utility"); + LogInfo("Client Files Import Utility"); if(!EQEmuConfig::LoadConfig()) { - Log(Logs::General, Logs::Error, "Unable to load configuration file."); + LogError("Unable to load configuration file."); return 1; } auto Config = EQEmuConfig::get(); SharedDatabase database; - Log(Logs::General, Logs::Status, "Connecting to database..."); + LogInfo("Connecting to database"); if(!database.Connect(Config->DatabaseHost.c_str(), Config->DatabaseUsername.c_str(), Config->DatabasePassword.c_str(), Config->DatabaseDB.c_str(), Config->DatabasePort)) { - Log(Logs::General, Logs::Error, "Unable to connect to the database, cannot continue without a " + LogError("Unable to connect to the database, cannot continue without a " "database connection"); return 1; } @@ -97,10 +97,10 @@ bool IsStringField(int i) { } void ImportSpells(SharedDatabase *db) { - Log(Logs::General, Logs::Status, "Importing Spells..."); + LogInfo("Importing Spells"); FILE *f = fopen("import/spells_us.txt", "r"); if(!f) { - Log(Logs::General, Logs::Error, "Unable to open import/spells_us.txt to read, skipping."); + LogError("Unable to open import/spells_us.txt to read, skipping."); return; } @@ -173,23 +173,23 @@ void ImportSpells(SharedDatabase *db) { spells_imported++; if(spells_imported % 1000 == 0) { - Log(Logs::General, Logs::Status, "%d spells imported.", spells_imported); + LogInfo("[{}] spells imported", spells_imported); } } if(spells_imported % 1000 != 0) { - Log(Logs::General, Logs::Status, "%d spells imported.", spells_imported); + LogInfo("[{}] spells imported", spells_imported); } fclose(f); } void ImportSkillCaps(SharedDatabase *db) { - Log(Logs::General, Logs::Status, "Importing Skill Caps..."); + LogInfo("Importing Skill Caps"); FILE *f = fopen("import/SkillCaps.txt", "r"); if(!f) { - Log(Logs::General, Logs::Error, "Unable to open import/SkillCaps.txt to read, skipping."); + LogError("Unable to open import/SkillCaps.txt to read, skipping."); return; } @@ -220,11 +220,11 @@ void ImportSkillCaps(SharedDatabase *db) { } void ImportBaseData(SharedDatabase *db) { - Log(Logs::General, Logs::Status, "Importing Base Data..."); + LogInfo("Importing Base Data"); FILE *f = fopen("import/BaseData.txt", "r"); if(!f) { - Log(Logs::General, Logs::Error, "Unable to open import/BaseData.txt to read, skipping."); + LogError("Unable to open import/BaseData.txt to read, skipping."); return; } @@ -265,11 +265,11 @@ void ImportBaseData(SharedDatabase *db) { } void ImportDBStrings(SharedDatabase *db) { - Log(Logs::General, Logs::Status, "Importing DB Strings..."); + LogInfo("Importing DB Strings"); FILE *f = fopen("import/dbstr_us.txt", "r"); if(!f) { - Log(Logs::General, Logs::Error, "Unable to open import/dbstr_us.txt to read, skipping."); + LogError("Unable to open import/dbstr_us.txt to read, skipping."); return; } diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index b803bb357..c4dec4074 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -3,6 +3,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.2) SET(common_sources base_packet.cpp classes.cpp + cli/eqemu_command_handler.cpp compression.cpp condition.cpp crash.cpp @@ -35,6 +36,7 @@ SET(common_sources inventory_profile.cpp inventory_slot.cpp ipc_mutex.cpp + ip_util.cpp item_data.cpp item_instance.cpp json_config.cpp @@ -71,7 +73,6 @@ SET(common_sources textures.cpp timer.cpp unix.cpp - xml_parser.cpp platform.cpp json/jsoncpp.cpp net/console_server.cpp @@ -102,13 +103,8 @@ SET(common_sources patches/uf.cpp patches/uf_limits.cpp StackWalker/StackWalker.cpp - tinyxml/tinystr.cpp - tinyxml/tinyxml.cpp - tinyxml/tinyxmlerror.cpp - tinyxml/tinyxmlparser.cpp util/directory.cpp - util/uuid.cpp -) + util/uuid.cpp) SET(common_headers any.h @@ -121,6 +117,9 @@ SET(common_headers crash.h crc16.h crc32.h + cli/argh.h + cli/eqemu_command_handler.h + cli/terminal_color.hpp data_verification.h database.h dbcore.h @@ -138,7 +137,7 @@ SET(common_headers eqemu_config.h eqemu_config_elements.h eqemu_logsys.h - eqemu_logsys_fmt.h + eqemu_logsys_log_aliases.h eq_limits.h eq_packet.h eq_stream_ident.h @@ -156,9 +155,11 @@ SET(common_headers global_define.h guild_base.h guilds.h + http/httplib.h inventory_profile.h inventory_slot.h ipc_mutex.h + ip_util.h item_data.h item_fieldlist.h item_instance.h @@ -209,7 +210,6 @@ SET(common_headers unix.h useperl.h version.h - xml_parser.h zone_numbers.h event/event_loop.h event/task.h @@ -263,12 +263,9 @@ SET(common_headers patches/uf_ops.h patches/uf_structs.h StackWalker/StackWalker.h - tinyxml/tinystr.h - tinyxml/tinyxml.h util/memory_stream.h util/directory.h - util/uuid.h -) + util/uuid.h) SOURCE_GROUP(Event FILES event/event_loop.h @@ -368,15 +365,6 @@ SOURCE_GROUP(StackWalker FILES StackWalker/StackWalker.cpp ) -SOURCE_GROUP(TinyXML FILES - tinyxml/tinystr.h - tinyxml/tinyxml.h - tinyxml/tinystr.cpp - tinyxml/tinyxml.cpp - tinyxml/tinyxmlerror.cpp - tinyxml/tinyxmlparser.cpp -) - SOURCE_GROUP(Util FILES util/memory_stream.h util/directory.cpp @@ -385,7 +373,7 @@ SOURCE_GROUP(Util FILES util/uuid.h ) -INCLUDE_DIRECTORIES(Patches SocketLib StackWalker TinyXML) +INCLUDE_DIRECTORIES(Patches SocketLib StackWalker) ADD_LIBRARY(common ${common_sources} ${common_headers}) diff --git a/common/cli/argh.h b/common/cli/argh.h new file mode 100644 index 000000000..047fa190f --- /dev/null +++ b/common/cli/argh.h @@ -0,0 +1,434 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace argh +{ + // Terminology: + // A command line is composed of 2 types of args: + // 1. Positional args, i.e. free standing values + // 2. Options: args beginning with '-'. We identify two kinds: + // 2.1: Flags: boolean options => (exist ? true : false) + // 2.2: Parameters: a name followed by a non-option value + +#if !defined(__GNUC__) || (__GNUC__ >= 5) + using string_stream = std::istringstream; +#else + // Until GCC 5, istringstream did not have a move constructor. + // stringstream_proxy is used instead, as a workaround. + class stringstream_proxy + { + public: + stringstream_proxy() = default; + + // Construct with a value. + stringstream_proxy(std::string const& value) : + stream_(value) + {} + + // Copy constructor. + stringstream_proxy(const stringstream_proxy& other) : + stream_(other.stream_.str()) + { + stream_.setstate(other.stream_.rdstate()); + } + + void setstate(std::ios_base::iostate state) { stream_.setstate(state); } + + // Stream out the value of the parameter. + // If the conversion was not possible, the stream will enter the fail state, + // and operator bool will return false. + template + stringstream_proxy& operator >> (T& thing) + { + stream_ >> thing; + return *this; + } + + + // Get the string value. + std::string str() const { return stream_.str(); } + + std::stringbuf* rdbuf() const { return stream_.rdbuf(); } + + // Check the state of the stream. + // False when the most recent stream operation failed + operator bool() const { return !!stream_; } + + ~stringstream_proxy() = default; + private: + std::istringstream stream_; + }; + using string_stream = stringstream_proxy; +#endif + + class parser + { + public: + enum Mode { PREFER_FLAG_FOR_UNREG_OPTION = 1 << 0, + PREFER_PARAM_FOR_UNREG_OPTION = 1 << 1, + NO_SPLIT_ON_EQUALSIGN = 1 << 2, + SINGLE_DASH_IS_MULTIFLAG = 1 << 3, + }; + + parser() = default; + + parser(std::initializer_list pre_reg_names) + { add_params(pre_reg_names); } + + parser(const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION) + { parse(argv, mode); } + + parser(int argc, const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION) + { parse(argc, argv, mode); } + + void add_param(std::string const& name); + void add_params(std::initializer_list init_list); + + void parse(const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION); + void parse(int argc, const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION); + + std::multiset const& flags() const { return flags_; } + std::map const& params() const { return params_; } + std::vector const& pos_args() const { return pos_args_; } + + // begin() and end() for using range-for over positional args. + std::vector::const_iterator begin() const { return pos_args_.cbegin(); } + std::vector::const_iterator end() const { return pos_args_.cend(); } + size_t size() const { return pos_args_.size(); } + + ////////////////////////////////////////////////////////////////////////// + // Accessors + + // flag (boolean) accessors: return true if the flag appeared, otherwise false. + bool operator[](std::string const& name) const; + + // multiple flag (boolean) accessors: return true if at least one of the flag appeared, otherwise false. + bool operator[](std::initializer_list init_list) const; + + // returns positional arg string by order. Like argv[] but without the options + std::string const& operator[](size_t ind) const; + + // returns a std::istream that can be used to convert a positional arg to a typed value. + string_stream operator()(size_t ind) const; + + // same as above, but with a default value in case the arg is missing (index out of range). + template + string_stream operator()(size_t ind, T&& def_val) const; + + // parameter accessors, give a name get an std::istream that can be used to convert to a typed value. + // call .str() on result to get as string + string_stream operator()(std::string const& name) const; + + // accessor for a parameter with multiple names, give a list of names, get an std::istream that can be used to convert to a typed value. + // call .str() on result to get as string + // returns the first value in the list to be found. + string_stream operator()(std::initializer_list init_list) const; + + // same as above, but with a default value in case the param was missing. + // Non-string def_val types must have an operator<<() (output stream operator) + // If T only has an input stream operator, pass the string version of the type as in "3" instead of 3. + template + string_stream operator()(std::string const& name, T&& def_val) const; + + // same as above but for a list of names. returns the first value to be found. + template + string_stream operator()(std::initializer_list init_list, T&& def_val) const; + + private: + string_stream bad_stream() const; + std::string trim_leading_dashes(std::string const& name) const; + bool is_number(std::string const& arg) const; + bool is_option(std::string const& arg) const; + bool got_flag(std::string const& name) const; + bool is_param(std::string const& name) const; + + private: + std::vector args_; + std::map params_; + std::vector pos_args_; + std::multiset flags_; + std::set registeredParams_; + std::string empty_; + }; + + + ////////////////////////////////////////////////////////////////////////// + + inline void parser::parse(const char * const argv[], int mode) + { + int argc = 0; + for (auto argvp = argv; *argvp; ++argc, ++argvp); + parse(argc, argv, mode); + } + + ////////////////////////////////////////////////////////////////////////// + + inline void parser::parse(int argc, const char* const argv[], int mode /*= PREFER_FLAG_FOR_UNREG_OPTION*/) + { + // convert to strings + args_.resize(argc); + std::transform(argv, argv + argc, args_.begin(), [](const char* const arg) { return arg; }); + + // parse line + for (auto i = 0u; i < args_.size(); ++i) + { + if (!is_option(args_[i])) + { + pos_args_.emplace_back(args_[i]); + continue; + } + + auto name = trim_leading_dashes(args_[i]); + + if (!(mode & NO_SPLIT_ON_EQUALSIGN)) + { + auto equalPos = name.find('='); + if (equalPos != std::string::npos) + { + params_.insert({ name.substr(0, equalPos), name.substr(equalPos + 1) }); + continue; + } + } + + // if the option is unregistered and should be a multi-flag + if (1 == (args_[i].size() - name.size()) && // single dash + argh::parser::SINGLE_DASH_IS_MULTIFLAG & mode && // multi-flag mode + !is_param(name)) // unregistered + { + std::string keep_param; + + if (!name.empty() && is_param(std::string(1ul, name.back()))) // last char is param + { + keep_param += name.back(); + name.resize(name.size() - 1); + } + + for (auto const& c : name) + { + flags_.emplace(std::string{ c }); + } + + if (!keep_param.empty()) + { + name = keep_param; + } + else + { + continue; // do not consider other options for this arg + } + } + + // any potential option will get as its value the next arg, unless that arg is an option too + // in that case it will be determined a flag. + if (i == args_.size() - 1 || is_option(args_[i + 1])) + { + flags_.emplace(name); + continue; + } + + // if 'name' is a pre-registered option, then the next arg cannot be a free parameter to it is skipped + // otherwise we have 2 modes: + // PREFER_FLAG_FOR_UNREG_OPTION: a non-registered 'name' is determined a flag. + // The following value (the next arg) will be a free parameter. + // + // PREFER_PARAM_FOR_UNREG_OPTION: a non-registered 'name' is determined a parameter, the next arg + // will be the value of that option. + + assert(!(mode & argh::parser::PREFER_FLAG_FOR_UNREG_OPTION) + || !(mode & argh::parser::PREFER_PARAM_FOR_UNREG_OPTION)); + + bool preferParam = mode & argh::parser::PREFER_PARAM_FOR_UNREG_OPTION; + + if (is_param(name) || preferParam) + { + params_.insert({ name, args_[i + 1] }); + ++i; // skip next value, it is not a free parameter + continue; + } + else + { + flags_.emplace(name); + } + }; + } + + ////////////////////////////////////////////////////////////////////////// + + inline string_stream parser::bad_stream() const + { + string_stream bad; + bad.setstate(std::ios_base::failbit); + return bad; + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool parser::is_number(std::string const& arg) const + { + // inefficient but simple way to determine if a string is a number (which can start with a '-') + std::istringstream istr(arg); + double number; + istr >> number; + return !(istr.fail() || istr.bad()); + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool parser::is_option(std::string const& arg) const + { + assert(0 != arg.size()); + if (is_number(arg)) + return false; + return '-' == arg[0]; + } + + ////////////////////////////////////////////////////////////////////////// + + inline std::string parser::trim_leading_dashes(std::string const& name) const + { + auto pos = name.find_first_not_of('-'); + return std::string::npos != pos ? name.substr(pos) : name; + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool argh::parser::got_flag(std::string const& name) const + { + return flags_.end() != flags_.find(trim_leading_dashes(name)); + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool argh::parser::is_param(std::string const& name) const + { + return registeredParams_.count(name); + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool parser::operator[](std::string const& name) const + { + return got_flag(name); + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool parser::operator[](std::initializer_list init_list) const + { + return std::any_of(init_list.begin(), init_list.end(), [&](char const* const name) { return got_flag(name); }); + } + + ////////////////////////////////////////////////////////////////////////// + + inline std::string const& parser::operator[](size_t ind) const + { + if (ind < pos_args_.size()) + return pos_args_[ind]; + return empty_; + } + + ////////////////////////////////////////////////////////////////////////// + + inline string_stream parser::operator()(std::string const& name) const + { + auto optIt = params_.find(trim_leading_dashes(name)); + if (params_.end() != optIt) + return string_stream(optIt->second); + return bad_stream(); + } + + ////////////////////////////////////////////////////////////////////////// + + inline string_stream parser::operator()(std::initializer_list init_list) const + { + for (auto& name : init_list) + { + auto optIt = params_.find(trim_leading_dashes(name)); + if (params_.end() != optIt) + return string_stream(optIt->second); + } + return bad_stream(); + } + + ////////////////////////////////////////////////////////////////////////// + + template + string_stream parser::operator()(std::string const& name, T&& def_val) const + { + auto optIt = params_.find(trim_leading_dashes(name)); + if (params_.end() != optIt) + return string_stream(optIt->second); + + std::ostringstream ostr; + ostr.precision(std::numeric_limits::max_digits10); + ostr << def_val; + return string_stream(ostr.str()); // use default + } + + ////////////////////////////////////////////////////////////////////////// + + // same as above but for a list of names. returns the first value to be found. + template + string_stream parser::operator()(std::initializer_list init_list, T&& def_val) const + { + for (auto& name : init_list) + { + auto optIt = params_.find(trim_leading_dashes(name)); + if (params_.end() != optIt) + return string_stream(optIt->second); + } + std::ostringstream ostr; + ostr.precision(std::numeric_limits::max_digits10); + ostr << def_val; + return string_stream(ostr.str()); // use default + } + + ////////////////////////////////////////////////////////////////////////// + + inline string_stream parser::operator()(size_t ind) const + { + if (pos_args_.size() <= ind) + return bad_stream(); + + return string_stream(pos_args_[ind]); + } + + ////////////////////////////////////////////////////////////////////////// + + template + string_stream parser::operator()(size_t ind, T&& def_val) const + { + if (pos_args_.size() <= ind) + { + std::ostringstream ostr; + ostr.precision(std::numeric_limits::max_digits10); + ostr << def_val; + return string_stream(ostr.str()); + } + + return string_stream(pos_args_[ind]); + } + + ////////////////////////////////////////////////////////////////////////// + + inline void parser::add_param(std::string const& name) + { + registeredParams_.insert(trim_leading_dashes(name)); + } + + ////////////////////////////////////////////////////////////////////////// + + inline void parser::add_params(std::initializer_list init_list) + { + for (auto& name : init_list) + registeredParams_.insert(trim_leading_dashes(name)); + } +} \ No newline at end of file diff --git a/common/cli/eqemu_command_handler.cpp b/common/cli/eqemu_command_handler.cpp new file mode 100644 index 000000000..ef46e0d9f --- /dev/null +++ b/common/cli/eqemu_command_handler.cpp @@ -0,0 +1,193 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ + +#include +#include "eqemu_command_handler.h" +#include "terminal_color.hpp" +#include "../platform.h" + +namespace EQEmuCommand { + + std::map function_map; + + /** + * @param cmd + */ + void DisplayDebug(argh::parser &cmd) + { + if (cmd[{"-d", "--debug"}]) { + std::cout << "Positional args:\n"; + for (auto &pos_arg : cmd) + std::cout << '\t' << pos_arg << std::endl; + + std::cout << "Positional args:\n"; + for (auto &pos_arg : cmd.pos_args()) + std::cout << '\t' << pos_arg << std::endl; + + std::cout << "\nFlags:\n"; + for (auto &flag : cmd.flags()) + std::cout << '\t' << flag << std::endl; + + std::cout << "\nParameters:\n"; + for (auto ¶m : cmd.params()) + std::cout << '\t' << param.first << " : " << param.second << std::endl; + } + } + + /** + * @param arguments + * @param options + * @param cmd + * @param argc + * @param argv + */ + void ValidateCmdInput( + std::vector &arguments, + std::vector &options, + argh::parser &cmd, + int argc, + char **argv + ) + { + bool arguments_filled = true; + + for (auto &arg : arguments) { + if (cmd(arg).str().empty()) { + arguments_filled = false; + } + } + + if (!arguments_filled || argc == 2) { + std::string arguments_string; + for (auto &arg : arguments) { + arguments_string += " " + arg + "=*\n"; + } + + std::string options_string; + for (auto &opt : options) { + options_string += " " + opt + "\n"; + } + + std::cout << fmt::format( + "Command\n\n{0} \n\nArgs\n{1}\nOptions\n{2}", + argv[1], + arguments_string, + options_string + ) << std::endl; + + exit(1); + } + } + + /** + * @param in_function_map + * @param cmd + * @param argc + * @param argv + */ + void HandleMenu( + std::map &in_function_map, + argh::parser &cmd, + int argc, + char **argv + ) + { + std::string description; + bool ran_command = false; + for (auto &it: in_function_map) { + if (it.first == argv[1]) { + std::cout << std::endl; + std::cout << "> " << termcolor::cyan << "Executing CLI Command" << termcolor::reset << std::endl; + std::cout << std::endl; + + (it.second)(argc, argv, cmd, description); + ran_command = true; + } + } + + if (cmd[{"-h", "--help"}]) { + std::cout << std::endl; + std::cout << + "> " << + termcolor::yellow << + "EQEmulator [" + GetPlatformName() + "] CLI Menu" << + termcolor::reset + << std::endl + << std::endl; + + /** + * Get max command length for padding length + */ + int max_command_length = 0; + + for (auto &it: in_function_map) { + std::stringstream command; + command << termcolor::colorize << termcolor::yellow << it.first << termcolor::reset; + if (command.str().length() > max_command_length) { + max_command_length = command.str().length() + 1; + } + } + + /** + * Display command menu + */ + std::string command_section; + for (auto &it: in_function_map) { + description = ""; + + (it.second)(argc, argv, cmd, description); + + /** + * Print section header + */ + std::string command_prefix = it.first.substr(0, it.first.find(":")); + if (command_section != command_prefix) { + command_section = command_prefix; + std::cout << termcolor::reset << command_prefix << std::endl; + } + + /** + * Print commands + */ + std::stringstream command; + command << termcolor::colorize << termcolor::yellow << it.first << termcolor::reset; + printf(" %-*s %s\n", max_command_length, command.str().c_str(), description.c_str()); + } + + std::cout << std::endl; + } + else if (!ran_command) { + std::cerr << "Unknown command [" << argv[1] << "] ! Try --help" << std::endl; + } + + exit(1); + } + +} diff --git a/common/cli/eqemu_command_handler.h b/common/cli/eqemu_command_handler.h new file mode 100644 index 000000000..d0b0b5b5e --- /dev/null +++ b/common/cli/eqemu_command_handler.h @@ -0,0 +1,75 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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_EQEMU_COMMAND_HANDLER_H +#define EQEMU_EQEMU_COMMAND_HANDLER_H + +#include "argh.h" + +namespace EQEmuCommand { + + extern std::map function_map; + + /** + * @param arguments + * @param options + * @param cmd + * @param argc + * @param argv + */ + void ValidateCmdInput( + std::vector &arguments, + std::vector &options, + argh::parser &cmd, + int argc, + char **argv + ); + + /** + * @param cmd + */ + void DisplayDebug(argh::parser &cmd); + + /** + * @param in_function_map + * @param cmd + * @param argc + * @param argv + */ + void HandleMenu( + std::map &in_function_map, + argh::parser &cmd, + int argc, + char **argv + ); +}; + + +#endif //EQEMU_EQEMU_COMMAND_HANDLER_H diff --git a/common/cli/terminal_color.hpp b/common/cli/terminal_color.hpp new file mode 100644 index 000000000..a98f2483b --- /dev/null +++ b/common/cli/terminal_color.hpp @@ -0,0 +1,557 @@ +//! +//! termcolor +//! ~~~~~~~~~ +//! +//! termcolor is a header-only c++ library for printing colored messages +//! to the terminal. Written just for fun with a help of the Force. +//! +//! :copyright: (c) 2013 by Ihor Kalnytskyi +//! :license: BSD, see LICENSE for details +//! + +#ifndef TERMCOLOR_HPP_ +#define TERMCOLOR_HPP_ + +// the following snippet of code detects the current OS and +// defines the appropriate macro that is used to wrap some +// platform specific things +#if defined(_WIN32) || defined(_WIN64) +# define TERMCOLOR_OS_WINDOWS +#elif defined(__APPLE__) +# define TERMCOLOR_OS_MACOS +#elif defined(__unix__) || defined(__unix) +# define TERMCOLOR_OS_LINUX +#else +# error unsupported platform +#endif + + +// This headers provides the `isatty()`/`fileno()` functions, +// which are used for testing whether a standart stream refers +// to the terminal. As for Windows, we also need WinApi funcs +// for changing colors attributes of the terminal. +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) +# include +#elif defined(TERMCOLOR_OS_WINDOWS) +# include +# include +#endif + + +#include +#include + + + +namespace termcolor +{ + // Forward declaration of the `_internal` namespace. + // All comments are below. + namespace _internal + { + // An index to be used to access a private storage of I/O streams. See + // colorize / nocolorize I/O manipulators for details. + static int colorize_index = std::ios_base::xalloc(); + + inline FILE* get_standard_stream(const std::ostream& stream); + inline bool is_colorized(std::ostream& stream); + inline bool is_atty(const std::ostream& stream); + +#if defined(TERMCOLOR_OS_WINDOWS) + inline void win_change_attributes(std::ostream& stream, int foreground, int background=-1); +#endif + } + + inline + std::ostream& colorize(std::ostream& stream) + { + stream.iword(_internal::colorize_index) = 1L; + return stream; + } + + inline + std::ostream& nocolorize(std::ostream& stream) + { + stream.iword(_internal::colorize_index) = 0L; + return stream; + } + + inline + std::ostream& reset(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;00m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, -1); +#endif + } + return stream; + } + + + inline + std::ostream& bold(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;1m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; + } + + + inline + std::ostream& dark(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;2m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; + } + + + inline + std::ostream& underline(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;4m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; + } + + + inline + std::ostream& blink(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;5m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; + } + + + inline + std::ostream& reverse(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;7m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; + } + + + inline + std::ostream& concealed(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;8m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; + } + + + inline + std::ostream& grey(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;30m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + 0 // grey (black) + ); +#endif + } + return stream; + } + + inline + std::ostream& red(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;31m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + FOREGROUND_RED + ); +#endif + } + return stream; + } + + inline + std::ostream& green(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;32m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + FOREGROUND_GREEN + ); +#endif + } + return stream; + } + + inline + std::ostream& yellow(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;33m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + FOREGROUND_GREEN | FOREGROUND_RED + ); +#endif + } + return stream; + } + + inline + std::ostream& blue(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;34m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + FOREGROUND_BLUE + ); +#endif + } + return stream; + } + + inline + std::ostream& magenta(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;35m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + FOREGROUND_BLUE | FOREGROUND_RED + ); +#endif + } + return stream; + } + + inline + std::ostream& cyan(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;36m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + FOREGROUND_BLUE | FOREGROUND_GREEN + ); +#endif + } + return stream; + } + + inline + std::ostream& white(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;37m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED + ); +#endif + } + return stream; + } + + + + inline + std::ostream& on_grey(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;40m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + 0 // grey (black) + ); +#endif + } + return stream; + } + + inline + std::ostream& on_red(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;41m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_RED + ); +#endif + } + return stream; + } + + inline + std::ostream& on_green(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;42m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_GREEN + ); +#endif + } + return stream; + } + + inline + std::ostream& on_yellow(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;43m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_GREEN | BACKGROUND_RED + ); +#endif + } + return stream; + } + + inline + std::ostream& on_blue(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;44m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_BLUE + ); +#endif + } + return stream; + } + + inline + std::ostream& on_magenta(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;45m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_BLUE | BACKGROUND_RED + ); +#endif + } + return stream; + } + + inline + std::ostream& on_cyan(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;46m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_GREEN | BACKGROUND_BLUE + ); +#endif + } + return stream; + } + + inline + std::ostream& on_white(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;47m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED + ); +#endif + } + + return stream; + } + + + + //! Since C++ hasn't a way to hide something in the header from + //! the outer access, I have to introduce this namespace which + //! is used for internal purpose and should't be access from + //! the user code. + namespace _internal + { + //! Since C++ hasn't a true way to extract stream handler + //! from the a given `std::ostream` object, I have to write + //! this kind of hack. + inline + FILE* get_standard_stream(const std::ostream& stream) + { + if (&stream == &std::cout) + return stdout; + else if ((&stream == &std::cerr) || (&stream == &std::clog)) + return stderr; + + return 0; + } + + // Say whether a given stream should be colorized or not. It's always + // true for ATTY streams and may be true for streams marked with + // colorize flag. + inline + bool is_colorized(std::ostream& stream) + { + return is_atty(stream) || static_cast(stream.iword(colorize_index)); + } + + //! Test whether a given `std::ostream` object refers to + //! a terminal. + inline + bool is_atty(const std::ostream& stream) + { + FILE* std_stream = get_standard_stream(stream); + + // Unfortunately, fileno() ends with segmentation fault + // if invalid file descriptor is passed. So we need to + // handle this case gracefully and assume it's not a tty + // if standard stream is not detected, and 0 is returned. + if (!std_stream) + return false; + +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + return ::isatty(fileno(std_stream)); +#elif defined(TERMCOLOR_OS_WINDOWS) + return ::_isatty(_fileno(std_stream)); +#endif + } + +#if defined(TERMCOLOR_OS_WINDOWS) + //! Change Windows Terminal colors attribute. If some + //! parameter is `-1` then attribute won't changed. + inline void win_change_attributes(std::ostream& stream, int foreground, int background) + { + // yeah, i know.. it's ugly, it's windows. + static WORD defaultAttributes = 0; + + // Windows doesn't have ANSI escape sequences and so we use special + // API to change Terminal output color. That means we can't + // manipulate colors by means of "std::stringstream" and hence + // should do nothing in this case. + if (!_internal::is_atty(stream)) + return; + + // get terminal handle + HANDLE hTerminal = INVALID_HANDLE_VALUE; + if (&stream == &std::cout) + hTerminal = GetStdHandle(STD_OUTPUT_HANDLE); + else if (&stream == &std::cerr) + hTerminal = GetStdHandle(STD_ERROR_HANDLE); + + // save default terminal attributes if it unsaved + if (!defaultAttributes) + { + CONSOLE_SCREEN_BUFFER_INFO info; + if (!GetConsoleScreenBufferInfo(hTerminal, &info)) + return; + defaultAttributes = info.wAttributes; + } + + // restore all default settings + if (foreground == -1 && background == -1) + { + SetConsoleTextAttribute(hTerminal, defaultAttributes); + return; + } + + // get current settings + CONSOLE_SCREEN_BUFFER_INFO info; + if (!GetConsoleScreenBufferInfo(hTerminal, &info)) + return; + + if (foreground != -1) + { + info.wAttributes &= ~(info.wAttributes & 0x0F); + info.wAttributes |= static_cast(foreground); + } + + if (background != -1) + { + info.wAttributes &= ~(info.wAttributes & 0xF0); + info.wAttributes |= static_cast(background); + } + + SetConsoleTextAttribute(hTerminal, info.wAttributes); + } +#endif // TERMCOLOR_OS_WINDOWS + + } // namespace _internal + +} // namespace termcolor + + +#undef TERMCOLOR_OS_WINDOWS +#undef TERMCOLOR_OS_MACOS +#undef TERMCOLOR_OS_LINUX + +#endif // TERMCOLOR_HPP_ diff --git a/common/data_verification.h b/common/data_verification.h index 27ad017a9..c56f3ce04 100644 --- a/common/data_verification.h +++ b/common/data_verification.h @@ -1,53 +1,57 @@ -/* EQEMu: Everquest Server Emulator - - Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) - - 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 -*/ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ #pragma once #include #include -namespace EQEmu -{ - template - T Clamp(const T& value, const T& lower, const T& upper) { +namespace EQEmu { + template + T Clamp(const T &value, const T &lower, const T &upper) + { return std::max(lower, std::min(value, upper)); } - template - T ClampLower(const T& value, const T& lower) { + template + T ClampLower(const T &value, const T &lower) + { return std::max(lower, value); } - template - T ClampUpper(const T& value, const T& upper) { + template + T ClampUpper(const T &value, const T &upper) + { return std::min(value, upper); } - template - bool ValueWithin(const T& value, const T& lower, const T& upper) { + template + bool ValueWithin(const T &value, const T &lower, const T &upper) + { return value >= lower && value <= upper; } - template - bool ValueWithin(const T1& value, const T2& lower, const T3& upper) { - return value >= (T1)lower && value <= (T1)upper; + template + bool ValueWithin(const T1 &value, const T2 &lower, const T3 &upper) + { + return value >= (T1) lower && value <= (T1) upper; } - } /*EQEmu*/ diff --git a/common/database.cpp b/common/database.cpp index 2c234aa64..6de980adb 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -64,11 +64,11 @@ bool Database::Connect(const char* host, const char* user, const char* passwd, c uint32 errnum= 0; char errbuf[MYSQL_ERRMSG_SIZE]; if (!Open(host, user, passwd, database, port, &errnum, errbuf)) { - Log(Logs::General, Logs::Error, "Failed to connect to database: Error: %s", errbuf); - return false; + LogError("Failed to connect to database: Error: {}", errbuf); + return false; } else { - Log(Logs::General, Logs::Status, "Using database '%s' at %s:%d", database, host,port); + LogInfo("Using database [{}] at [{}]:[{}]", database, host,port); return true; } } @@ -86,7 +86,7 @@ Database::~Database() Return the account id or zero if no account matches. Zero will also be returned if there is a database error. */ -uint32 Database::CheckLogin(const char* name, const char* password, int16* oStatus) { +uint32 Database::CheckLogin(const char* name, const char* password, const char *loginserver, int16* oStatus) { if(strlen(name) >= 50 || strlen(password) >= 50) return(0); @@ -97,9 +97,10 @@ uint32 Database::CheckLogin(const char* name, const char* password, int16* oStat DoEscapeString(tmpUN, name, strlen(name)); DoEscapeString(tmpPW, password, strlen(password)); - std::string query = StringFormat("SELECT id, status FROM account WHERE name='%s' AND password is not null " + 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, tmpPW, tmpPW); + tmpUN, EscapeString(loginserver).c_str(), tmpPW, tmpPW); + auto results = QueryDatabase(query); if (!results.Success()) @@ -139,16 +140,16 @@ bool Database::CheckBannedIPs(const char* loginIP) } bool Database::AddBannedIP(char* bannedIP, const char* notes) { - std::string query = StringFormat("INSERT into Banned_IPs SET ip_address='%s', notes='%s'", bannedIP, notes); - auto results = QueryDatabase(query); + std::string query = StringFormat("INSERT into Banned_IPs SET ip_address='%s', notes='%s'", bannedIP, 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); + std::string query = StringFormat("SELECT * FROM `gm_ips` WHERE `ip_address` = '%s' AND `account_id` = %i", ip_address, account_id); auto results = QueryDatabase(query); if (!results.Success()) @@ -161,14 +162,14 @@ bool Database::AddBannedIP(char* bannedIP, const char* notes) { } 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); + 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); - QueryDatabase(query); + std::string query = StringFormat("INSERT INTO account_ip SET accid=%i, ip='%s' ON DUPLICATE KEY UPDATE count=count+1, lastused=now()", AccountID, LoginIP); + QueryDatabase(query); } int16 Database::CheckStatus(uint32 account_id) @@ -197,34 +198,63 @@ int16 Database::CheckStatus(uint32 account_id) return status; } -uint32 Database::CreateAccount(const char* name, const char* password, int16 status, uint32 lsaccount_id) { +/** + * @param name + * @param password + * @param status + * @param loginserver + * @param lsaccount_id + * @return + */ +uint32 Database::CreateAccount( + const char *name, + const char *password, + int16 status, + const char *loginserver, + uint32 lsaccount_id +) +{ std::string query; - if (password) - query = StringFormat("INSERT INTO account SET name='%s', password='%s', status=%i, lsaccount_id=%i, time_creation=UNIX_TIMESTAMP();",name,password,status, lsaccount_id); - else - query = StringFormat("INSERT INTO account SET name='%s', status=%i, lsaccount_id=%i, time_creation=UNIX_TIMESTAMP();",name, status, lsaccount_id); + if (password) { + query = StringFormat( + "INSERT INTO account SET name='%s', password='%s', status=%i, ls_id='%s', lsaccount_id=%i, time_creation=UNIX_TIMESTAMP();", + name, + password, + status, + loginserver, + lsaccount_id + ); + } + else { + query = StringFormat( + "INSERT INTO account SET name='%s', status=%i, ls_id='%s', lsaccount_id=%i, time_creation=UNIX_TIMESTAMP();", + name, + status, + loginserver, + lsaccount_id + ); + } - Log(Logs::General, Logs::World_Server, "Account Attempting to be created: '%s' status: %i", name, status); + LogInfo("Account Attempting to be created: [{0}:{1}] status: {2}", loginserver, name, status); auto results = QueryDatabase(query); if (!results.Success()) { return 0; } - if (results.LastInsertedID() == 0) - { + if (results.LastInsertedID() == 0) { return 0; } return results.LastInsertedID(); } -bool Database::DeleteAccount(const char* name) { - std::string query = StringFormat("DELETE FROM account WHERE name='%s';",name); - Log(Logs::General, Logs::World_Server, "Account Attempting to be deleted:'%s'", name); +bool Database::DeleteAccount(const char* name, const char *loginserver) { + std::string query = StringFormat("DELETE FROM account WHERE name='%s' AND ls_id='%s'", name, loginserver); + LogInfo("Account Attempting to be deleted:'[{}]:[{}]'", loginserver, name); - auto results = QueryDatabase(query); + auto results = QueryDatabase(query); if (!results.Success()) { return false; } @@ -257,7 +287,7 @@ bool Database::SetAccountStatus(const char* name, int16 status) { if (results.RowsAffected() == 0) { std::cout << "Account: " << name << " does not exist, therefore it cannot be flagged\n"; - return false; + return false; } return true; @@ -269,14 +299,14 @@ bool Database::ReserveName(uint32 account_id, char* name) { auto results = QueryDatabase(query); for (auto row = results.begin(); row != results.end(); ++row) { if (row[0] && atoi(row[0]) > 0){ - Log(Logs::General, Logs::World_Server, "Account: %i tried to request name: %s, but it is already taken...", account_id, name); + LogInfo("Account: [{}] tried to request name: [{}], but it is already taken", account_id, name); return false; } } - query = StringFormat("INSERT INTO `character_data` SET `account_id` = %i, `name` = '%s'", account_id, name); + query = StringFormat("INSERT INTO `character_data` SET `account_id` = %i, `name` = '%s'", account_id, name); results = QueryDatabase(query); - if (!results.Success() || results.ErrorMessage() != ""){ return false; } + if (!results.Success() || results.ErrorMessage() != ""){ return false; } return true; } @@ -287,18 +317,18 @@ bool Database::ReserveName(uint32 account_id, char* name) { bool Database::DeleteCharacter(char *name) { uint32 charid = 0; if(!name || !strlen(name)) { - Log(Logs::General, Logs::World_Server, "DeleteCharacter: request to delete without a name (empty char slot)"); + LogInfo("DeleteCharacter: request to delete without a name (empty char slot)"); return false; } - Log(Logs::General, Logs::World_Server, "Database::DeleteCharacter name : '%s'", name); + LogInfo("Database::DeleteCharacter name : [{}]", name); /* Get id from character_data before deleting record so we can clean up the rest of the tables */ std::string query = StringFormat("SELECT `id` from `character_data` WHERE `name` = '%s'", name); auto results = QueryDatabase(query); for (auto row = results.begin(); row != results.end(); ++row) { charid = atoi(row[0]); } - if (charid <= 0){ - Log(Logs::General, Logs::Error, "Database::DeleteCharacter :: Character (%s) not found, stopping delete...", name); - return false; + if (charid <= 0){ + LogError("Database::DeleteCharacter :: Character ({}) not found, stopping delete...", name); + return false; } query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", charid); QueryDatabase(query); @@ -341,7 +371,7 @@ bool Database::DeleteCharacter(char *name) { query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d'", charid); #endif QueryDatabase(query); - + return true; } @@ -656,7 +686,7 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe } } } - results = QueryDatabase(query); + results = QueryDatabase(query); /* Save Language */ firstquery = 0; @@ -671,20 +701,20 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe } } } - results = QueryDatabase(query); + results = QueryDatabase(query); return true; } /* This only for new Character creation storing */ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, EQEmu::InventoryProfile* inv) { - uint32 charid = 0; - char zone[50]; - float x, y, z; + uint32 charid = 0; + char zone[50]; + float x, y, z; charid = GetCharacterID(pp->name); if(!charid) { - Log(Logs::General, Logs::Error, "StoreCharacter: no character id"); + LogError("StoreCharacter: no character id"); return false; } @@ -701,7 +731,7 @@ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, EQEmu z = pp->z; /* Saves Player Profile Data */ - SaveCharacterCreate(charid, account_id, pp); + SaveCharacterCreate(charid, account_id, pp); /* Insert starting inventory... */ std::string invquery; @@ -709,23 +739,23 @@ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, EQEmu const EQEmu::ItemInstance* newinv = inv->GetItem(i); if (newinv) { invquery = StringFormat("INSERT INTO `inventory` (charid, slotid, itemid, charges, color) VALUES (%u, %i, %u, %i, %u)", - charid, i, newinv->GetItem()->ID, newinv->GetCharges(), newinv->GetColor()); - - auto results = QueryDatabase(invquery); + charid, i, newinv->GetItem()->ID, newinv->GetCharges(), newinv->GetColor()); + + auto results = QueryDatabase(invquery); } if (i == EQEmu::invslot::slotCursor) { - i = EQEmu::invbag::GENERAL_BAGS_BEGIN; + i = EQEmu::invbag::GENERAL_BAGS_BEGIN; continue; } - else if (i == EQEmu::invbag::CURSOR_BAG_END) { - i = EQEmu::invslot::BANK_BEGIN; - continue; + else if (i == EQEmu::invbag::CURSOR_BAG_END) { + i = EQEmu::invslot::BANK_BEGIN; + continue; + } + else if (i == EQEmu::invslot::BANK_END) { + i = EQEmu::invbag::BANK_BAGS_BEGIN; + continue; } - else if (i == EQEmu::invslot::BANK_END) { - i = EQEmu::invbag::BANK_BAGS_BEGIN; - continue; - } i++; } return true; @@ -739,7 +769,7 @@ uint32 Database::GetCharacterID(const char *name) { { return atoi(row[0]); } - return 0; + return 0; } /* @@ -758,7 +788,7 @@ uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) { } if (results.RowCount() != 1) - return 0; + return 0; auto row = results.begin(); @@ -772,8 +802,8 @@ uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) { // Retrieve account_id for a given char_id uint32 Database::GetAccountIDByChar(uint32 char_id) { - std::string query = StringFormat("SELECT `account_id` FROM `character_data` WHERE `id` = %i LIMIT 1", char_id); - auto results = QueryDatabase(query); + std::string query = StringFormat("SELECT `account_id` FROM `character_data` WHERE `id` = %i LIMIT 1", char_id); + auto results = QueryDatabase(query); if (!results.Success()) { return 0; } @@ -781,15 +811,16 @@ uint32 Database::GetAccountIDByChar(uint32 char_id) { if (results.RowCount() != 1) return 0; - auto row = results.begin(); + auto row = results.begin(); return atoi(row[0]); } -uint32 Database::GetAccountIDByName(const char* accname, int16* status, uint32* lsid) { +uint32 Database::GetAccountIDByName(const char* accname, const char *loginserver, int16* status, uint32* lsid) { if (!isAlphaNumeric(accname)) return 0; - std::string query = StringFormat("SELECT `id`, `status`, `lsaccount_id` FROM `account` WHERE `name` = '%s' LIMIT 1", accname); + 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 results = QueryDatabase(query); if (!results.Success()) { @@ -817,7 +848,7 @@ uint32 Database::GetAccountIDByName(const char* accname, int16* status, uint32* } void Database::GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID) { - std::string query = StringFormat("SELECT `name`, `lsaccount_id` FROM `account` WHERE `id` = '%i'", accountid); + std::string query = StringFormat("SELECT `name`, `lsaccount_id` FROM `account` WHERE `id` = '%i'", accountid); auto results = QueryDatabase(query); if (!results.Success()) { @@ -841,7 +872,7 @@ void Database::GetCharName(uint32 char_id, char* name) { auto results = QueryDatabase(query); if (!results.Success()) { - return; + return; } auto row = results.begin(); @@ -918,21 +949,9 @@ bool Database::SetVariable(const std::string varname, const std::string &varvalu return true; } -uint32 Database::GetMiniLoginAccount(char* ip) -{ - std::string query = StringFormat("SELECT `id` FROM `account` WHERE `minilogin_ip` = '%s'", ip); - auto results = QueryDatabase(query); - - if (!results.Success()) - return 0; - - auto row = results.begin(); - return atoi(row[0]); -} - // Get zone starting points from DB bool Database::GetSafePoints(const char* short_name, uint32 version, float* safe_x, float* safe_y, float* safe_z, int16* minstatus, uint8* minlevel, char *flag_needed) { - + std::string query = StringFormat("SELECT safe_x, safe_y, safe_z, min_status, min_level, flag_needed FROM zone " " WHERE short_name='%s' AND (version=%i OR version=0) ORDER BY version DESC", short_name, version); auto results = QueryDatabase(query); @@ -962,7 +981,7 @@ bool Database::GetSafePoints(const char* short_name, uint32 version, float* safe } bool Database::GetZoneLongName(const char* short_name, char** long_name, char* file_name, float* safe_x, float* safe_y, float* safe_z, uint32* graveyard_id, uint32* maxclients) { - + std::string query = StringFormat("SELECT long_name, file_name, safe_x, safe_y, safe_z, graveyard_id, maxclients FROM zone WHERE short_name='%s' AND version=0", short_name); auto results = QueryDatabase(query); @@ -1015,7 +1034,7 @@ uint32 Database::GetZoneGraveyardID(uint32 zone_id, uint32 version) { } 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); auto results = QueryDatabase(query); @@ -1087,7 +1106,7 @@ const char* Database::GetZoneName(uint32 zoneID, bool ErrorUnknown) { } 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); auto results = QueryDatabase(query); @@ -1151,7 +1170,7 @@ bool Database::CheckNameFilter(const char* name, bool surname) } } - + std::string query("SELECT name FROM name_filter"); auto results = QueryDatabase(query); @@ -1176,7 +1195,7 @@ bool Database::CheckNameFilter(const char* name, bool surname) } bool Database::AddToNameFilter(const char* name) { - + std::string query = StringFormat("INSERT INTO name_filter (name) values ('%s')", name); auto results = QueryDatabase(query); @@ -1191,32 +1210,53 @@ bool Database::AddToNameFilter(const char* name) { return true; } -uint32 Database::GetAccountIDFromLSID(uint32 iLSID, char* oAccountName, int16* oStatus) { +/** + * @param in_loginserver_id + * @param in_loginserver_account_id + * @param in_account_name + * @param in_status + * @return + */ +uint32 Database::GetAccountIDFromLSID( + const std::string &in_loginserver_id, + uint32 in_loginserver_account_id, + char *in_account_name, + int16 *in_status +) +{ uint32 account_id = 0; - std::string query = StringFormat("SELECT id, name, status FROM account WHERE lsaccount_id=%i", iLSID); + auto query = fmt::format( + "SELECT id, name, status FROM account WHERE lsaccount_id = {0} AND ls_id = '{1}'", + in_loginserver_account_id, + in_loginserver_id + ); + auto results = QueryDatabase(query); if (!results.Success()) { return 0; } - if (results.RowCount() != 1) + if (results.RowCount() != 1) { return 0; + } for (auto row = results.begin(); row != results.end(); ++row) { - account_id = atoi(row[0]); + account_id = std::stoi(row[0]); - if (oAccountName) - strcpy(oAccountName, row[1]); - if (oStatus) - *oStatus = atoi(row[2]); + if (in_account_name) { + strcpy(in_account_name, row[1]); + } + if (in_status) { + *in_status = std::stoi(row[2]); + } } return account_id; } void Database::GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus) { - + std::string query = StringFormat("SELECT name, status FROM account WHERE id=%i", id); auto results = QueryDatabase(query); @@ -1239,8 +1279,8 @@ void Database::ClearMerchantTemp(){ QueryDatabase("DELETE FROM merchantlist_temp"); } -bool Database::UpdateName(const char* oldname, const char* newname) { - std::cout << "Renaming " << oldname << " to " << newname << "..." << std::endl; +bool Database::UpdateName(const char* oldname, const char* newname) { + std::cout << "Renaming " << oldname << " to " << newname << "..." << std::endl; std::string query = StringFormat("UPDATE `character_data` SET `name` = '%s' WHERE `name` = '%s';", newname, oldname); auto results = QueryDatabase(query); @@ -1256,7 +1296,7 @@ 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); - auto results = QueryDatabase(query); + auto results = QueryDatabase(query); if (!results.Success()) { return false; } @@ -1269,7 +1309,7 @@ bool Database::CheckUsedName(const char* name) { uint8 Database::GetServerType() { std::string query("SELECT `value` FROM `variables` WHERE `varname` = 'ServerType' LIMIT 1"); - auto results = QueryDatabase(query); + auto results = QueryDatabase(query); if (!results.Success()) { return 0; } @@ -1302,7 +1342,7 @@ bool Database::MoveCharacterToZone(const char* charname, const char* zonename) { return MoveCharacterToZone(charname, zonename, GetZoneID(zonename)); } -bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) { +bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) { std::string query = StringFormat("UPDATE `character_data` SET `zone_id` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `id` = %i", GetZoneID(iZonename), iCharID); auto results = QueryDatabase(query); @@ -1313,7 +1353,7 @@ bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) { return results.RowsAffected() != 0; } -bool Database::SetHackerFlag(const char* accountname, const char* charactername, const char* hacked) { +bool Database::SetHackerFlag(const char* accountname, const char* charactername, const char* hacked) { std::string query = StringFormat("INSERT INTO `hackers` (account, name, hacked) values('%s','%s','%s')", accountname, charactername, hacked); auto results = QueryDatabase(query); @@ -1324,7 +1364,7 @@ bool Database::SetHackerFlag(const char* accountname, const char* charactername, return results.RowsAffected() != 0; } -bool Database::SetMQDetectionFlag(const char* accountname, const char* charactername, const char* hacked, const char* zone) { +bool Database::SetMQDetectionFlag(const char* accountname, const char* charactername, const char* hacked, const char* zone) { //Utilize the "hacker" table, but also give zone information. std::string query = StringFormat("INSERT INTO hackers(account,name,hacked,zone) values('%s','%s','%s','%s')", accountname, charactername, hacked, zone); auto results = QueryDatabase(query); @@ -1340,7 +1380,7 @@ bool Database::SetMQDetectionFlag(const char* accountname, const char* character uint8 Database::GetRaceSkill(uint8 skillid, uint8 in_race) { uint16 race_cap = 0; - + //Check for a racial cap! std::string query = StringFormat("SELECT skillcap from race_skillcaps where skill = %i && race = %i", skillid, in_race); auto results = QueryDatabase(query); @@ -1359,7 +1399,7 @@ uint8 Database::GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 { uint8 skill_level = 0, skill_formula = 0; uint16 base_cap = 0, skill_cap = 0, skill_cap2 = 0, skill_cap3 = 0; - + //Fetch the data from DB. std::string query = StringFormat("SELECT level, formula, pre50cap, post50cap, post60cap from skillcaps where skill = %i && class = %i", skillid, in_class); @@ -1442,9 +1482,9 @@ uint32 Database::GetCharacterInfo( return charid; } -bool Database::UpdateLiveChar(char* charname,uint32 lsaccount_id) { +bool Database::UpdateLiveChar(char* charname, uint32 account_id) { - std::string query = StringFormat("UPDATE account SET charname='%s' WHERE id=%i;",charname, lsaccount_id); + std::string query = StringFormat("UPDATE account SET charname='%s' WHERE id=%i;", charname, account_id); auto results = QueryDatabase(query); if (!results.Success()){ @@ -1473,24 +1513,24 @@ bool Database::GetLiveChar(uint32 account_id, char* cname) { return true; } -void Database::SetLFP(uint32 CharID, bool LFP) { +void Database::SetLFP(uint32 CharID, bool LFP) { std::string query = StringFormat("UPDATE `character_data` SET `lfp` = %i WHERE `id` = %i",LFP, CharID); - QueryDatabase(query); + QueryDatabase(query); } -void Database::SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon) { +void Database::SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon) { std::string query = StringFormat("update `character_data` SET `lfp` = %i, `lfg` = %i, `firstlogon` = %i WHERE `id` = %i",LFP, LFG, firstlogon, CharID); - QueryDatabase(query); + QueryDatabase(query); } -void Database::SetLFG(uint32 CharID, bool LFG) { +void Database::SetLFG(uint32 CharID, bool LFG) { std::string query = StringFormat("update `character_data` SET `lfg` = %i WHERE `id` = %i",LFG, CharID); - QueryDatabase(query); + QueryDatabase(query); } -void Database::SetFirstLogon(uint32 CharID, uint8 firstlogon) { +void Database::SetFirstLogon(uint32 CharID, uint8 firstlogon) { std::string query = StringFormat( "UPDATE `character_data` SET `firstlogon` = %i WHERE `id` = %i",firstlogon, CharID); - QueryDatabase(query); + QueryDatabase(query); } void Database::AddReport(std::string who, std::string against, std::string lines) @@ -1511,7 +1551,7 @@ void Database::SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ism auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Error deleting character from group id: %s", results.ErrorMessage().c_str()); + LogError("Error deleting character from group id: {}", results.ErrorMessage().c_str()); return; } @@ -1530,7 +1570,7 @@ void Database::ClearAllGroups(void) void Database::ClearGroup(uint32 gid) { ClearGroupLeader(gid); - + if(gid == 0) { //clear all groups @@ -1543,7 +1583,7 @@ void Database::ClearGroup(uint32 gid) { QueryDatabase(query); } -uint32 Database::GetGroupID(const char* name){ +uint32 Database::GetGroupID(const char* name){ std::string query = StringFormat("SELECT groupid from group_id where name='%s'", name); auto results = QueryDatabase(query); @@ -1554,7 +1594,7 @@ uint32 Database::GetGroupID(const char* name){ if (results.RowCount() == 0) { // Commenting this out until logging levels can prevent this from going to console - //Log(Logs::General, Logs::None,, "Character not in a group: %s", name); + //LogDebug(, "Character not in a group: [{}]", name); return 0; } @@ -1588,7 +1628,7 @@ char* Database::GetGroupLeaderForLogin(const char* name, char* leaderbuf) { return leaderbuf; } -void Database::SetGroupLeaderName(uint32 gid, const char* name) { +void Database::SetGroupLeaderName(uint32 gid, const char* name) { std::string query = StringFormat("UPDATE group_leaders SET leadername = '%s' WHERE gid = %u", EscapeString(name).c_str(), gid); auto result = QueryDatabase(query); @@ -1601,7 +1641,7 @@ void Database::SetGroupLeaderName(uint32 gid, const char* name) { result = QueryDatabase(query); if(!result.Success()) { - Log(Logs::General, Logs::None, "Error in Database::SetGroupLeaderName: %s", result.ErrorMessage().c_str()); + LogDebug("Error in Database::SetGroupLeaderName: [{}]", result.ErrorMessage().c_str()); } } @@ -1676,7 +1716,7 @@ void Database::ClearAllGroupLeaders(void) { } void Database::ClearGroupLeader(uint32 gid) { - + if(gid == 0) { ClearAllGroupLeaders(); @@ -1747,7 +1787,7 @@ void Database::ClearAllRaidDetails(void) } void Database::ClearRaidDetails(uint32 rid) { - + if(rid == 0) { //clear all raids @@ -1775,7 +1815,7 @@ void Database::PurgeAllDeletedDataBuckets() { // returns 0 on error or no raid for that character, or // the raid id that the character is a member of. uint32 Database::GetRaidID(const char* name) -{ +{ std::string query = StringFormat("SELECT `raidid` FROM `raid_members` WHERE `name` = '%s'", name); auto results = QueryDatabase(query); @@ -1783,7 +1823,7 @@ uint32 Database::GetRaidID(const char* name) return 0; } - auto row = results.begin(); + auto row = results.begin(); if (row == results.end()) { return 0; } @@ -1802,12 +1842,12 @@ const char* Database::GetRaidLeaderName(uint32 raid_id) // variable). C++0x standard states this should be thread safe // but may not be fully supported in some compilers. static char name[128]; - + std::string query = StringFormat("SELECT `name` FROM `raid_members` WHERE `raidid` = %u AND `israidleader` = 1", raid_id); auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Debug, "Unable to get Raid Leader Name for Raid ID: %u", raid_id); + LogDebug("Unable to get Raid Leader Name for Raid ID: [{}]", raid_id); return "UNKNOWN"; } @@ -2030,7 +2070,7 @@ bool Database::GetAdventureStats(uint32 char_id, AdventureStats_Struct *as) "FROM " "`adventure_stats` " "WHERE " - "player_id = %u ", + "player_id = %u ", char_id ); auto results = QueryDatabase(query); @@ -2059,7 +2099,7 @@ bool Database::GetAdventureStats(uint32 char_id, AdventureStats_Struct *as) return true; } -uint32 Database::GetGuildIDByCharID(uint32 character_id) +uint32 Database::GetGuildIDByCharID(uint32 character_id) { std::string query = StringFormat("SELECT guild_id FROM guild_members WHERE char_id='%i'", character_id); auto results = QueryDatabase(query); @@ -2074,9 +2114,11 @@ uint32 Database::GetGuildIDByCharID(uint32 character_id) return atoi(row[0]); } -void Database::LoadLogSettings(EQEmuLogSys::LogSettings* log_settings) { - // log_settings previously initialized to '0' by EQEmuLogSys::LoadLogSettingsDefaults() - +/** + * @param log_settings + */ +void Database::LoadLogSettings(EQEmuLogSys::LogSettings *log_settings) +{ std::string query = "SELECT " "log_category_id, " @@ -2088,11 +2130,10 @@ void Database::LoadLogSettings(EQEmuLogSys::LogSettings* log_settings) { "logsys_categories " "ORDER BY log_category_id"; - auto results = QueryDatabase(query); + auto results = QueryDatabase(query); + int log_category_id = 0; - int log_category_id = 0; - - int categories_in_database[1000] = {}; + int *categories_in_database = new int[1000]; for (auto row = results.begin(); row != results.end(); ++row) { log_category_id = atoi(row[0]); @@ -2132,15 +2173,14 @@ void Database::LoadLogSettings(EQEmuLogSys::LogSettings* log_settings) { * Auto inject categories that don't exist in the database... */ for (int log_index = Logs::AA; log_index != Logs::MaxCategoryID; log_index++) { - if (!categories_in_database[log_index]) { + if (categories_in_database[log_index] != 1) { - Log(Logs::General, - Logs::Status, - "New Log Category '%s' doesn't exist... Automatically adding to `logsys_categories` table...", + LogInfo( + "New Log Category [{0}] doesn't exist... Automatically adding to [logsys_categories] table...", Logs::LogCategoryName[log_index] ); - std::string inject_query = StringFormat( + auto inject_query = fmt::format( "INSERT INTO logsys_categories " "(log_category_id, " "log_category_description, " @@ -2148,17 +2188,19 @@ void Database::LoadLogSettings(EQEmuLogSys::LogSettings* log_settings) { "log_to_file, " "log_to_gmsay) " "VALUES " - "(%i, '%s', %i, %i, %i)", + "({0}, '{1}', {2}, {3}, {4})", log_index, - EscapeString(Logs::LogCategoryName[log_index]).c_str(), - log_settings[log_category_id].log_to_console, - log_settings[log_category_id].log_to_file, - log_settings[log_category_id].log_to_gmsay + EscapeString(Logs::LogCategoryName[log_index]), + std::to_string(log_settings[log_index].log_to_console), + std::to_string(log_settings[log_index].log_to_file), + std::to_string(log_settings[log_index].log_to_gmsay) ); QueryDatabase(inject_query); } } + + delete[] categories_in_database; } int Database::CountInvSnapshots() { @@ -2195,7 +2237,7 @@ struct TimeOfDay_Struct Database::LoadTime(time_t &realtime) auto results = QueryDatabase(query); if (!results.Success() || results.RowCount() == 0){ - Log(Logs::Detail, Logs::World_Server, "Loading EQ time of day failed. Using defaults."); + LogInfo("Loading EQ time of day failed. Using defaults"); eqTime.minute = 0; eqTime.hour = 9; eqTime.day = 1; @@ -2229,12 +2271,12 @@ 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); } diff --git a/common/database.h b/common/database.h index 71ebe3315..0dce5318d 100644 --- a/common/database.h +++ b/common/database.h @@ -129,7 +129,7 @@ public: uint32 GetAccountIDByChar(const char* charname, uint32* oCharID = 0); uint32 GetAccountIDByChar(uint32 char_id); - uint32 GetAccountIDByName(const char* accname, int16* status = 0, uint32* lsid = 0); + uint32 GetAccountIDByName(const char* accname, const char *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 GetGuildIDByCharID(uint32 char_id); @@ -176,18 +176,17 @@ public: /* Account Related */ - bool DeleteAccount(const char* name); + bool DeleteAccount(const char *name, const char* loginserver); bool GetLiveChar(uint32 account_id, char* cname); bool SetAccountStatus(const char* name, int16 status); bool SetLocalPassword(uint32 accid, const char* password); - bool UpdateLiveChar(char* charname, uint32 lsaccount_id); + bool UpdateLiveChar(char* charname, uint32 account_id); int16 CheckStatus(uint32 account_id); - uint32 CheckLogin(const char* name, const char* password, int16* oStatus = 0); - uint32 CreateAccount(const char* name, const char* password, int16 status, uint32 lsaccount_id = 0); - uint32 GetAccountIDFromLSID(uint32 iLSID, char* oAccountName = 0, int16* oStatus = 0); - uint32 GetMiniLoginAccount(char* ip); + uint32 CheckLogin(const char* name, const char* password, const char *loginserver, int16* oStatus = 0); + uint32 CreateAccount(const char* name, const char* password, int16 status, const char* loginserver, uint32 lsaccount_id); + uint32 GetAccountIDFromLSID(const std::string& in_loginserver_id, uint32 in_loginserver_account_id, char* in_account_name = 0, int16* in_status = 0); uint8 GetAgreementFlag(uint32 acctid); void GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus); diff --git a/common/dbcore.cpp b/common/dbcore.cpp index 0468bdc77..2c46f298a 100644 --- a/common/dbcore.cpp +++ b/common/dbcore.cpp @@ -2,8 +2,9 @@ #include #endif -#include "../common/misc_functions.h" -#include "../common/eqemu_logsys.h" +#include "misc_functions.h" +#include "eqemu_logsys.h" +#include "timer.h" #include "dbcore.h" @@ -14,33 +15,37 @@ #include #ifdef _WINDOWS - #define snprintf _snprintf - #define strncasecmp _strnicmp - #define strcasecmp _stricmp - #include +#define snprintf _snprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#include #else - #include "unix.h" - #include + +#include "unix.h" +#include + #endif #ifdef _EQDEBUG - #define DEBUG_MYSQL_QUERIES 0 +#define DEBUG_MYSQL_QUERIES 0 #else - #define DEBUG_MYSQL_QUERIES 0 +#define DEBUG_MYSQL_QUERIES 0 #endif -DBcore::DBcore() { +DBcore::DBcore() +{ mysql_init(&mysql); - pHost = 0; - pUser = 0; + pHost = 0; + pUser = 0; pPassword = 0; pDatabase = 0; pCompress = false; - pSSL = false; - pStatus = Closed; + pSSL = false; + pStatus = Closed; } -DBcore::~DBcore() { +DBcore::~DBcore() +{ mysql_close(&mysql); safe_delete_array(pHost); safe_delete_array(pUser); @@ -49,7 +54,8 @@ DBcore::~DBcore() { } // Sends the MySQL server a keepalive -void DBcore::ping() { +void DBcore::ping() +{ if (!MDatabase.trylock()) { // well, if's it's locked, someone's using it. If someone's using it, it doesnt need a keepalive return; @@ -63,34 +69,35 @@ MySQLRequestResult DBcore::QueryDatabase(std::string query, bool retryOnFailureO return QueryDatabase(query.c_str(), query.length(), retryOnFailureOnce); } -MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, bool retryOnFailureOnce) +MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, bool retryOnFailureOnce) { + BenchTimer timer; + timer.reset(); + LockMutex lock(&MDatabase); // Reconnect if we are not connected before hand. - if (pStatus != Connected) + if (pStatus != Connected) { Open(); + } // request query. != 0 indicates some kind of error. - if (mysql_real_query(&mysql, query, querylen) != 0) - { + if (mysql_real_query(&mysql, query, querylen) != 0) { unsigned int errorNumber = mysql_errno(&mysql); - if (errorNumber == CR_SERVER_GONE_ERROR) + if (errorNumber == CR_SERVER_GONE_ERROR) { pStatus = Error; + } // error appears to be a disconnect error, may need to try again. - if (errorNumber == CR_SERVER_LOST || errorNumber == CR_SERVER_GONE_ERROR) - { + if (errorNumber == CR_SERVER_LOST || errorNumber == CR_SERVER_GONE_ERROR) { - if (retryOnFailureOnce) - { - std::cout << "Database Error: Lost connection, attempting to recover...." << std::endl; + if (retryOnFailureOnce) { + LogInfo("Database Error: Lost connection, attempting to recover"); MySQLRequestResult requestResult = QueryDatabase(query, querylen, false); - if (requestResult.Success()) - { - std::cout << "Reconnection to database successful." << std::endl; + if (requestResult.Success()) { + LogInfo("Reconnection to database successful"); return requestResult; } @@ -102,109 +109,154 @@ MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, boo snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); - return MySQLRequestResult(nullptr, 0, 0, 0, 0, (uint32)mysql_errno(&mysql), errorBuffer); + return MySQLRequestResult(nullptr, 0, 0, 0, 0, (uint32) mysql_errno(&mysql), errorBuffer); } auto errorBuffer = new char[MYSQL_ERRMSG_SIZE]; snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); /* Implement Logging at the Root */ - if (mysql_errno(&mysql) > 0 && strlen(query) > 0){ + if (mysql_errno(&mysql) > 0 && strlen(query) > 0) { if (LogSys.log_settings[Logs::MySQLError].is_category_enabled == 1) Log(Logs::General, Logs::MySQLError, "%i: %s \n %s", mysql_errno(&mysql), mysql_error(&mysql), query); } - return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(&mysql),errorBuffer); + return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(&mysql), errorBuffer); } // successful query. get results. - MYSQL_RES* res = mysql_store_result(&mysql); - uint32 rowCount = 0; + MYSQL_RES *res = mysql_store_result(&mysql); + uint32 rowCount = 0; - if (res != nullptr) - rowCount = (uint32)mysql_num_rows(res); + if (res != nullptr) { + rowCount = (uint32) mysql_num_rows(res); + } - MySQLRequestResult requestResult(res, (uint32)mysql_affected_rows(&mysql), rowCount, (uint32)mysql_field_count(&mysql), (uint32)mysql_insert_id(&mysql)); - - if (LogSys.log_settings[Logs::MySQLQuery].is_category_enabled == 1) - { + MySQLRequestResult requestResult( + res, + (uint32) mysql_affected_rows(&mysql), + rowCount, + (uint32) mysql_field_count(&mysql), + (uint32) mysql_insert_id(&mysql) + ); + + if (LogSys.log_settings[Logs::MySQLQuery].is_category_enabled == 1) { if ((strncasecmp(query, "select", 6) == 0)) { - Log(Logs::General, Logs::MySQLQuery, "%s (%u row%s returned)", query, requestResult.RowCount(), requestResult.RowCount() == 1 ? "" : "s"); + LogF( + Logs::General, + Logs::MySQLQuery, + "{0} ({1} row{2} returned) ({3}ms)", + query, + requestResult.RowCount(), + requestResult.RowCount() == 1 ? "" : "s", + std::to_string(timer.elapsed()) + ); } else { - Log(Logs::General, Logs::MySQLQuery, "%s (%u row%s affected)", query, requestResult.RowsAffected(), requestResult.RowsAffected() == 1 ? "" : "s"); + LogF( + Logs::General, + Logs::MySQLQuery, + "{0} ({1} row{2} affected) ({3}ms)", + query, + requestResult.RowsAffected(), + requestResult.RowsAffected() == 1 ? "" : "s", + std::to_string(timer.elapsed()) + ); } } return requestResult; } -void DBcore::TransactionBegin() { +void DBcore::TransactionBegin() +{ QueryDatabase("START TRANSACTION"); } -void DBcore::TransactionCommit() { +void DBcore::TransactionCommit() +{ QueryDatabase("COMMIT"); } -void DBcore::TransactionRollback() { +void DBcore::TransactionRollback() +{ QueryDatabase("ROLLBACK"); } -uint32 DBcore::DoEscapeString(char* tobuf, const char* frombuf, uint32 fromlen) { +uint32 DBcore::DoEscapeString(char *tobuf, const char *frombuf, uint32 fromlen) +{ // No good reason to lock the DB, we only need it in the first place to check char encoding. // LockMutex lock(&MDatabase); return mysql_real_escape_string(&mysql, tobuf, frombuf, fromlen); } -bool DBcore::Open(const char* iHost, const char* iUser, const char* iPassword, const char* iDatabase,uint32 iPort, uint32* errnum, char* errbuf, bool iCompress, bool iSSL) { +bool DBcore::Open( + const char *iHost, + const char *iUser, + const char *iPassword, + const char *iDatabase, + uint32 iPort, + uint32 *errnum, + char *errbuf, + bool iCompress, + bool iSSL +) +{ LockMutex lock(&MDatabase); safe_delete(pHost); safe_delete(pUser); safe_delete(pPassword); safe_delete(pDatabase); - pHost = strcpy(new char[strlen(iHost) + 1], iHost); - pUser = strcpy(new char[strlen(iUser) + 1], iUser); + pHost = strcpy(new char[strlen(iHost) + 1], iHost); + pUser = strcpy(new char[strlen(iUser) + 1], iUser); pPassword = strcpy(new char[strlen(iPassword) + 1], iPassword); pDatabase = strcpy(new char[strlen(iDatabase) + 1], iDatabase); pCompress = iCompress; - pPort = iPort; - pSSL = iSSL; + pPort = iPort; + pSSL = iSSL; return Open(errnum, errbuf); } -bool DBcore::Open(uint32* errnum, char* errbuf) { - if (errbuf) +bool DBcore::Open(uint32 *errnum, char *errbuf) +{ + if (errbuf) { errbuf[0] = 0; + } LockMutex lock(&MDatabase); - if (GetStatus() == Connected) + if (GetStatus() == Connected) { return true; + } if (GetStatus() == Error) { mysql_close(&mysql); - mysql_init(&mysql); // Initialize structure again + mysql_init(&mysql); // Initialize structure again } - if (!pHost) + if (!pHost) { return false; + } /* Added CLIENT_FOUND_ROWS flag to the connect otherwise DB update calls would say 0 rows affected when the value already equalled what the function was tring to set it to, therefore the function would think it failed */ uint32 flags = CLIENT_FOUND_ROWS; - if (pCompress) + if (pCompress) { flags |= CLIENT_COMPRESS; - if (pSSL) + } + if (pSSL) { flags |= CLIENT_SSL; + } if (mysql_real_connect(&mysql, pHost, pUser, pPassword, pDatabase, pPort, 0, flags)) { pStatus = Connected; return true; } else { - if (errnum) + if (errnum) { *errnum = mysql_errno(&mysql); - if (errbuf) + } + if (errbuf) { snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); + } pStatus = Error; return false; } diff --git a/common/eq_constants.h b/common/eq_constants.h index 6c3e28da0..4cc61c0b4 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -459,4 +459,9 @@ enum ChatChannelNames : uint16 ChatChannel_Emotes = 22 }; +namespace ZoneBlockedSpellTypes { + const uint8 ZoneWide = 1; + const uint8 Region = 2; +}; + #endif /*COMMON_EQ_CONSTANTS_H*/ diff --git a/common/eq_stream.cpp b/common/eq_stream.cpp index 96b816b1f..61162580a 100644 --- a/common/eq_stream.cpp +++ b/common/eq_stream.cpp @@ -84,14 +84,14 @@ void EQStream::init(bool resetSession) { OpMgr = nullptr; if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { - Log(Logs::Detail, Logs::Netcode, _L "init Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); + LogNetcode(_L "init Invalid Sequenced queue: BS [{}] + SQ [{}] != NOS [{}]" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); } } EQRawApplicationPacket *EQStream::MakeApplicationPacket(EQProtocolPacket *p) { EQRawApplicationPacket *ap=nullptr; - Log(Logs::Detail, Logs::Netcode, _L "Creating new application packet, length %d" __L, p->size); + LogNetcode(_L "Creating new application packet, length [{}]" __L, p->size); // _raw(NET__APP_CREATE_HEX, 0xFFFF, p); ap = p->MakeAppPacket(); return ap; @@ -100,7 +100,7 @@ EQRawApplicationPacket *EQStream::MakeApplicationPacket(EQProtocolPacket *p) EQRawApplicationPacket *EQStream::MakeApplicationPacket(const unsigned char *buf, uint32 len) { EQRawApplicationPacket *ap=nullptr; - Log(Logs::Detail, Logs::Netcode, _L "Creating new application packet, length %d" __L, len); + LogNetcode(_L "Creating new application packet, length [{}]" __L, len); ap = new EQRawApplicationPacket(buf, len); return ap; } @@ -130,7 +130,7 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) } if (!Session && p->opcode!=OP_SessionRequest && p->opcode!=OP_SessionResponse) { - Log(Logs::Detail, Logs::Netcode, _L "Session not initialized, packet ignored" __L); + LogNetcode(_L "Session not initialized, packet ignored" __L); // _raw(NET__DEBUG, 0xFFFF, p); return; } @@ -141,7 +141,7 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) while(processed < p->size) { subpacket_length=*(p->pBuffer+processed); EQProtocolPacket *subp=MakeProtocolPacket(p->pBuffer+processed+1,subpacket_length); - Log(Logs::Detail, Logs::Netcode, _L "Extracting combined packet of length %d" __L, subpacket_length); + LogNetcode(_L "Extracting combined packet of length [{}]" __L, subpacket_length); // _raw(NET__NET_CREATE_HEX, 0xFFFF, subp); subp->copyInfo(p); ProcessPacket(subp); @@ -156,12 +156,12 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) while(processedsize) { EQRawApplicationPacket *ap=nullptr; if ((subpacket_length=(unsigned char)*(p->pBuffer+processed))!=0xff) { - Log(Logs::Detail, Logs::Netcode, _L "Extracting combined app packet of length %d, short len" __L, subpacket_length); + LogNetcode(_L "Extracting combined app packet of length [{}], short len" __L, subpacket_length); ap=MakeApplicationPacket(p->pBuffer+processed+1,subpacket_length); processed+=subpacket_length+1; } else { subpacket_length=ntohs(*(uint16 *)(p->pBuffer+processed+1)); - Log(Logs::Detail, Logs::Netcode, _L "Extracting combined app packet of length %d, short len" __L, subpacket_length); + LogNetcode(_L "Extracting combined app packet of length [{}], short len" __L, subpacket_length); ap=MakeApplicationPacket(p->pBuffer+processed+3,subpacket_length); processed+=subpacket_length+3; } @@ -176,29 +176,29 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) case OP_Packet: { if(!p->pBuffer || (p->Size() < 4)) { - Log(Logs::Detail, Logs::Netcode, _L "Received OP_Packet that was of malformed size" __L); + LogNetcode(_L "Received OP_Packet that was of malformed size" __L); break; } uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); SeqOrder check=CompareSequence(NextInSeq,seq); if (check == SeqFuture) { - Log(Logs::Detail, Logs::Netcode, _L "Future OP_Packet: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); + LogNetcode(_L "Future OP_Packet: Expecting Seq=[{}], but got Seq=[{}]" __L, NextInSeq, seq); // _raw(NET__DEBUG, seq, p); PacketQueue[seq]=p->Copy(); - Log(Logs::Detail, Logs::Netcode, _L "OP_Packet Queue size=%d" __L, PacketQueue.size()); + LogNetcode(_L "OP_Packet Queue size=[{}]" __L, PacketQueue.size()); //SendOutOfOrderAck(seq); } else if (check == SeqPast) { - Log(Logs::Detail, Logs::Netcode, _L "Duplicate OP_Packet: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); + LogNetcode(_L "Duplicate OP_Packet: Expecting Seq=[{}], but got Seq=[{}]" __L, NextInSeq, seq); // _raw(NET__DEBUG, seq, p); SendOutOfOrderAck(seq); //we already got this packet but it was out of order } else { // In case we did queue one before as well. EQProtocolPacket *qp=RemoveQueue(seq); if (qp) { - Log(Logs::General, Logs::Netcode, "[NET_TRACE] OP_Packet: Removing older queued packet with sequence %d", seq); + LogNetcode("[NET_TRACE] OP_Packet: Removing older queued packet with sequence [{}]", seq); delete qp; } @@ -207,7 +207,7 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) // Check for an embedded OP_AppCombinded (protocol level 0x19) if (*(p->pBuffer+2)==0x00 && *(p->pBuffer+3)==0x19) { EQProtocolPacket *subp=MakeProtocolPacket(p->pBuffer+2,p->size-2); - Log(Logs::Detail, Logs::Netcode, _L "seq %d, Extracting combined packet of length %d" __L, seq, subp->size); + LogNetcode(_L "seq [{}], Extracting combined packet of length [{}]" __L, seq, subp->size); // _raw(NET__NET_CREATE_HEX, seq, subp); subp->copyInfo(p); ProcessPacket(subp); @@ -226,29 +226,29 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) case OP_Fragment: { if(!p->pBuffer || (p->Size() < 4)) { - Log(Logs::Detail, Logs::Netcode, _L "Received OP_Fragment that was of malformed size" __L); + LogNetcode(_L "Received OP_Fragment that was of malformed size" __L); break; } uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); SeqOrder check=CompareSequence(NextInSeq,seq); if (check == SeqFuture) { - Log(Logs::Detail, Logs::Netcode, _L "Future OP_Fragment: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); + LogNetcode(_L "Future OP_Fragment: Expecting Seq=[{}], but got Seq=[{}]" __L, NextInSeq, seq); // _raw(NET__DEBUG, seq, p); PacketQueue[seq]=p->Copy(); - Log(Logs::Detail, Logs::Netcode, _L "OP_Fragment Queue size=%d" __L, PacketQueue.size()); + LogNetcode(_L "OP_Fragment Queue size=[{}]" __L, PacketQueue.size()); //SendOutOfOrderAck(seq); } else if (check == SeqPast) { - Log(Logs::Detail, Logs::Netcode, _L "Duplicate OP_Fragment: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); + LogNetcode(_L "Duplicate OP_Fragment: Expecting Seq=[{}], but got Seq=[{}]" __L, NextInSeq, seq); // _raw(NET__DEBUG, seq, p); SendOutOfOrderAck(seq); } else { // In case we did queue one before as well. EQProtocolPacket *qp=RemoveQueue(seq); if (qp) { - Log(Logs::General, Logs::Netcode, "[NET_TRACE] OP_Fragment: Removing older queued packet with sequence %d", seq); + LogNetcode("[NET_TRACE] OP_Fragment: Removing older queued packet with sequence [{}]", seq); delete qp; } SetNextAckToSend(seq); @@ -256,18 +256,18 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) if (oversize_buffer) { memcpy(oversize_buffer+oversize_offset,p->pBuffer+2,p->size-2); oversize_offset+=p->size-2; - Log(Logs::Detail, Logs::Netcode, _L "Fragment of oversized of length %d, seq %d: now at %d/%d" __L, p->size-2, seq, oversize_offset, oversize_length); + LogNetcode(_L "Fragment of oversized of length [{}], seq [{}]: now at [{}]/[{}]" __L, p->size-2, seq, oversize_offset, oversize_length); if (oversize_offset==oversize_length) { if (*(p->pBuffer+2)==0x00 && *(p->pBuffer+3)==0x19) { EQProtocolPacket *subp=MakeProtocolPacket(oversize_buffer,oversize_offset); - Log(Logs::Detail, Logs::Netcode, _L "seq %d, Extracting combined oversize packet of length %d" __L, seq, subp->size); + LogNetcode(_L "seq [{}], Extracting combined oversize packet of length [{}]" __L, seq, subp->size); //// _raw(NET__NET_CREATE_HEX, subp); subp->copyInfo(p); ProcessPacket(subp); delete subp; } else { EQRawApplicationPacket *ap=MakeApplicationPacket(oversize_buffer,oversize_offset); - Log(Logs::Detail, Logs::Netcode, _L "seq %d, completed combined oversize packet of length %d" __L, seq, ap->size); + LogNetcode(_L "seq [{}], completed combined oversize packet of length [{}]" __L, seq, ap->size); if (ap) { ap->copyInfo(p); InboundQueuePush(ap); @@ -282,20 +282,20 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) oversize_buffer=new unsigned char[oversize_length]; memcpy(oversize_buffer,p->pBuffer+6,p->size-6); oversize_offset=p->size-6; - Log(Logs::Detail, Logs::Netcode, _L "First fragment of oversized of seq %d: now at %d/%d" __L, seq, oversize_offset, oversize_length); + LogNetcode(_L "First fragment of oversized of seq [{}]: now at [{}]/[{}]" __L, seq, oversize_offset, oversize_length); } } } break; case OP_KeepAlive: { NonSequencedPush(new EQProtocolPacket(p->opcode,p->pBuffer,p->size)); - Log(Logs::Detail, Logs::Netcode, _L "Received and queued reply to keep alive" __L); + LogNetcode(_L "Received and queued reply to keep alive" __L); } break; case OP_Ack: { if(!p->pBuffer || (p->Size() < 4)) { - Log(Logs::Detail, Logs::Netcode, _L "Received OP_Ack that was of malformed size" __L); + LogNetcode(_L "Received OP_Ack that was of malformed size" __L); break; } uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); @@ -309,11 +309,11 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) case OP_SessionRequest: { if(p->Size() < sizeof(SessionRequest)) { - Log(Logs::Detail, Logs::Netcode, _L "Received OP_SessionRequest that was of malformed size" __L); + LogNetcode(_L "Received OP_SessionRequest that was of malformed size" __L); break; } if (GetState()==ESTABLISHED) { - Log(Logs::Detail, Logs::Netcode, _L "Received OP_SessionRequest in ESTABLISHED state (%d) streamactive (%i) attempt (%i)" __L, GetState(),streamactive,sessionAttempts); + LogNetcode(_L "Received OP_SessionRequest in ESTABLISHED state ([{}]) streamactive ([{}]) attempt ([{}])" __L, GetState(),streamactive,sessionAttempts); // client seems to try a max of 30 times (initial+3 retries) then gives up, giving it a few more attempts just in case // streamactive means we identified the opcode for the stream, we cannot re-establish this connection @@ -331,7 +331,7 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) SessionRequest *Request=(SessionRequest *)p->pBuffer; Session=ntohl(Request->Session); SetMaxLen(ntohl(Request->MaxLength)); - Log(Logs::Detail, Logs::Netcode, _L "Received OP_SessionRequest: session %lu, maxlen %d" __L, (unsigned long)Session, MaxLen); + LogNetcode(_L "Received OP_SessionRequest: session [{}], maxlen [{}]" __L, (unsigned long)Session, MaxLen); SetState(ESTABLISHED); Key=0x11223344; SendSessionResponse(); @@ -340,7 +340,7 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) case OP_SessionResponse: { if(p->Size() < sizeof(SessionResponse)) { - Log(Logs::Detail, Logs::Netcode, _L "Received OP_SessionResponse that was of malformed size" __L); + LogNetcode(_L "Received OP_SessionResponse that was of malformed size" __L); break; } @@ -356,7 +356,7 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) compressed=(Response->Format&FLAG_COMPRESSED); encoded=(Response->Format&FLAG_ENCODED); - Log(Logs::Detail, Logs::Netcode, _L "Received OP_SessionResponse: session %lu, maxlen %d, key %lu, compressed? %s, encoded? %s" __L, (unsigned long)Session, MaxLen, (unsigned long)Key, compressed?"yes":"no", encoded?"yes":"no"); + LogNetcode(_L "Received OP_SessionResponse: session [{}], maxlen [{}], key [{}], compressed? [{}], encoded? [{}]" __L, (unsigned long)Session, MaxLen, (unsigned long)Key, compressed?"yes":"no", encoded?"yes":"no"); // Kinda kludgy, but trie for now if (StreamType==UnknownStream) { @@ -379,17 +379,17 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) EQStreamState state = GetState(); if(state == ESTABLISHED) { //client initiated disconnect? - Log(Logs::Detail, Logs::Netcode, _L "Received unsolicited OP_SessionDisconnect. Treating like a client-initiated disconnect." __L); + LogNetcode(_L "Received unsolicited OP_SessionDisconnect. Treating like a client-initiated disconnect" __L); _SendDisconnect(); SetState(CLOSED); } else if(state == CLOSING) { //we were waiting for this anyways, ignore pending messages, send the reply and be closed. - Log(Logs::Detail, Logs::Netcode, _L "Received OP_SessionDisconnect when we have a pending close, they beat us to it. Were happy though." __L); + LogNetcode(_L "Received OP_SessionDisconnect when we have a pending close, they beat us to it. Were happy though" __L); _SendDisconnect(); SetState(CLOSED); } else { //we are expecting this (or have already gotten it, but dont care either way) - Log(Logs::Detail, Logs::Netcode, _L "Received expected OP_SessionDisconnect. Moving to closed state." __L); + LogNetcode(_L "Received expected OP_SessionDisconnect. Moving to closed state" __L); SetState(CLOSED); } } @@ -397,14 +397,14 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) case OP_OutOfOrderAck: { if(!p->pBuffer || (p->Size() < 4)) { - Log(Logs::Detail, Logs::Netcode, _L "Received OP_OutOfOrderAck that was of malformed size" __L); + LogNetcode(_L "Received OP_OutOfOrderAck that was of malformed size" __L); break; } uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); MOutboundQueue.lock(); if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { - Log(Logs::Detail, Logs::Netcode, _L "Pre-OOA Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); + LogNetcode(_L "Pre-OOA Invalid Sequenced queue: BS [{}] + SQ [{}] != NOS [{}]" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); } //if the packet they got out of order is between our last acked packet and the last sent packet, then its valid. @@ -414,7 +414,7 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) uint16 sqsize = SequencedQueue.size(); uint16 index = seq - SequencedBase; - Log(Logs::Detail, Logs::Netcode, _L "OP_OutOfOrderAck marking packet acked in queue (queue index = %d, queue size = %d)." __L, index, sqsize); + LogNetcode(_L "OP_OutOfOrderAck marking packet acked in queue (queue index = [{}], queue size = [{}])" __L, index, sqsize); if (index < sqsize) { SequencedQueue[index]->acked = true; // flag packets for a resend @@ -423,7 +423,7 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) for (auto sitr = SequencedQueue.begin(); sitr != SequencedQueue.end() && count < index; ++sitr, ++count) { if (!(*sitr)->acked && (*sitr)->sent_time > 0 && (((*sitr)->sent_time + timeout) < Timer::GetCurrentTime())) { (*sitr)->sent_time = 0; - Log(Logs::Detail, Logs::Netcode, _L "OP_OutOfOrderAck Flagging packet %d for retransmission" __L, SequencedBase + count); + LogNetcode(_L "OP_OutOfOrderAck Flagging packet [{}] for retransmission" __L, SequencedBase + count); } } } @@ -432,11 +432,11 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) retransmittimer = Timer::GetCurrentTime(); } } else { - Log(Logs::Detail, Logs::Netcode, _L "Received OP_OutOfOrderAck for out-of-window %d. Window (%d->%d)." __L, seq, SequencedBase, NextOutSeq); + LogNetcode(_L "Received OP_OutOfOrderAck for out-of-window [{}]. Window ([{}]->[{}])" __L, seq, SequencedBase, NextOutSeq); } if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { - Log(Logs::Detail, Logs::Netcode, _L "Post-OOA Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); + LogNetcode(_L "Post-OOA Invalid Sequenced queue: BS [{}] + SQ [{}] != NOS [{}]" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); } MOutboundQueue.unlock(); @@ -445,7 +445,7 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) case OP_SessionStatRequest: { if(p->Size() < sizeof(ClientSessionStats)) { - Log(Logs::Detail, Logs::Netcode, _L "Received OP_SessionStatRequest that was of malformed size" __L); + LogNetcode(_L "Received OP_SessionStatRequest that was of malformed size" __L); break; } ClientSessionStats *ClientStats=(ClientSessionStats *)p->pBuffer; @@ -468,7 +468,7 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) retransmittimeout += 300; if(retransmittimeout > RETRANSMIT_TIMEOUT_MAX) retransmittimeout = RETRANSMIT_TIMEOUT_MAX; - Log(Logs::Detail, Logs::Netcode, _L "Retransmit timeout recalculated to %dms" __L, retransmittimeout); + LogNetcode(_L "Retransmit timeout recalculated to [{}]ms" __L, retransmittimeout); } } @@ -485,11 +485,11 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) } break; case OP_SessionStatResponse: { - Log(Logs::Detail, Logs::Netcode, _L "Received OP_SessionStatResponse. Ignoring." __L); + LogNetcode(_L "Received OP_SessionStatResponse. Ignoring" __L); } break; case OP_OutOfSession: { - Log(Logs::Detail, Logs::Netcode, _L "Received OP_OutOfSession. Ignoring." __L); + LogNetcode(_L "Received OP_OutOfSession. Ignoring" __L); } break; default: @@ -520,7 +520,7 @@ void EQStream::FastQueuePacket(EQApplicationPacket **p, bool ack_req) return; if(OpMgr == nullptr || *OpMgr == nullptr) { - Log(Logs::Detail, Logs::Netcode, _L "Packet enqueued into a stream with no opcode manager, dropping." __L); + LogNetcode(_L "Packet enqueued into a stream with no opcode manager, dropping" __L); delete pack; return; } @@ -559,18 +559,18 @@ void EQStream::SendPacket(uint16 opcode, EQApplicationPacket *p) // Convert the EQApplicationPacket to 1 or more EQProtocolPackets if (p->size>(MaxLen-8)) { // proto-op(2), seq(2), app-op(2) ... data ... crc(2) - Log(Logs::Detail, Logs::Netcode, _L "Making oversized packet, len %d" __L, p->Size()); + LogNetcode(_L "Making oversized packet, len [{}]" __L, p->Size()); auto tmpbuff = new unsigned char[p->size + 3]; length=p->serialize(opcode, tmpbuff); if (length != p->Size()) - Log(Logs::Detail, Logs::Netcode, _L "Packet adjustment, len %d to %d" __L, p->Size(), length); + LogNetcode(_L "Packet adjustment, len [{}] to [{}]" __L, p->Size(), length); auto out = new EQProtocolPacket(OP_Fragment, nullptr, MaxLen - 4); *(uint32 *)(out->pBuffer+2)=htonl(length); used=MaxLen-10; memcpy(out->pBuffer+6,tmpbuff,used); - Log(Logs::Detail, Logs::Netcode, _L "First fragment: used %d/%d. Payload size %d in the packet" __L, used, length, p->size); + LogNetcode(_L "First fragment: used [{}]/[{}]. Payload size [{}] in the packet" __L, used, length, p->size); SequencedPush(out); @@ -581,7 +581,7 @@ void EQStream::SendPacket(uint16 opcode, EQApplicationPacket *p) out->size=chunksize+2; SequencedPush(out); used+=chunksize; - Log(Logs::Detail, Logs::Netcode, _L "Subsequent fragment: len %d, used %d/%d." __L, chunksize, used, length); + LogNetcode(_L "Subsequent fragment: len [{}], used [{}]/[{}]" __L, chunksize, used, length); } delete p; delete[] tmpbuff; @@ -623,7 +623,7 @@ void EQStream::SequencedPush(EQProtocolPacket *p) void EQStream::NonSequencedPush(EQProtocolPacket *p) { MOutboundQueue.lock(); - Log(Logs::Detail, Logs::Netcode, _L "Pushing non-sequenced packet of length %d" __L, p->size); + LogNetcode(_L "Pushing non-sequenced packet of length [{}]" __L, p->size); NonSequencedQueue.push(p); MOutboundQueue.unlock(); } @@ -631,14 +631,14 @@ void EQStream::NonSequencedPush(EQProtocolPacket *p) void EQStream::SendAck(uint16 seq) { uint16 Seq=htons(seq); - Log(Logs::Detail, Logs::Netcode, _L "Sending ack with sequence %d" __L, seq); + LogNetcode(_L "Sending ack with sequence [{}]" __L, seq); SetLastAckSent(seq); NonSequencedPush(new EQProtocolPacket(OP_Ack,(unsigned char *)&Seq,sizeof(uint16))); } void EQStream::SendOutOfOrderAck(uint16 seq) { - Log(Logs::Detail, Logs::Netcode, _L "Sending out of order ack with sequence %d" __L, seq); + LogNetcode(_L "Sending out of order ack with sequence [{}]" __L, seq); uint16 Seq=htons(seq); NonSequencedPush(new EQProtocolPacket(OP_OutOfOrderAck,(unsigned char *)&Seq,sizeof(uint16))); } @@ -688,24 +688,24 @@ void EQStream::Write(int eq_fd) // If we don't have a packet to try to combine into, use this one as the base // And remove it form the queue p = NonSequencedQueue.front(); - Log(Logs::Detail, Logs::Netcode, _L "Starting combined packet with non-seq packet of len %d" __L, p->size); + LogNetcode(_L "Starting combined packet with non-seq packet of len [{}]" __L, p->size); NonSequencedQueue.pop(); } else if (!p->combine(NonSequencedQueue.front())) { // Trying to combine this packet with the base didn't work (too big maybe) // So just send the base packet (we'll try this packet again later) - Log(Logs::Detail, Logs::Netcode, _L "Combined packet full at len %d, next non-seq packet is len %d" __L, p->size, (NonSequencedQueue.front())->size); + LogNetcode(_L "Combined packet full at len [{}], next non-seq packet is len [{}]" __L, p->size, (NonSequencedQueue.front())->size); ReadyToSend.push(p); BytesWritten+=p->size; p=nullptr; if (BytesWritten > threshold) { // Sent enough this round, lets stop to be fair - Log(Logs::Detail, Logs::Netcode, _L "Exceeded write threshold in nonseq (%d > %d)" __L, BytesWritten, threshold); + LogNetcode(_L "Exceeded write threshold in nonseq ([{}] > [{}])" __L, BytesWritten, threshold); break; } } else { // Combine worked, so just remove this packet and it's spot in the queue - Log(Logs::Detail, Logs::Netcode, _L "Combined non-seq packet of len %d, yeilding %d combined." __L, (NonSequencedQueue.front())->size, p->size); + LogNetcode(_L "Combined non-seq packet of len [{}], yeilding [{}] combined" __L, (NonSequencedQueue.front())->size, p->size); delete NonSequencedQueue.front(); NonSequencedQueue.pop(); } @@ -718,7 +718,7 @@ void EQStream::Write(int eq_fd) uint16 seq_send = SequencedBase + count; //just for logging... if(SequencedQueue.empty()) { - Log(Logs::Detail, Logs::Netcode, _L "Tried to write a packet with an empty queue (%d is past next out %d)" __L, seq_send, NextOutSeq); + LogNetcode(_L "Tried to write a packet with an empty queue ([{}] is past next out [{}])" __L, seq_send, NextOutSeq); SeqEmpty=true; continue; } @@ -728,35 +728,35 @@ void EQStream::Write(int eq_fd) ++sitr; ++count; if (p) { - Log(Logs::Detail, Logs::Netcode, _L "Final combined packet not full, len %d" __L, p->size); + LogNetcode(_L "Final combined packet not full, len [{}]" __L, p->size); ReadyToSend.push(p); BytesWritten += p->size; p = nullptr; } - Log(Logs::Detail, Logs::Netcode, _L "Not retransmitting seq packet %d because already marked as acked" __L, seq_send); + LogNetcode(_L "Not retransmitting seq packet [{}] because already marked as acked" __L, seq_send); } else if (!p) { // If we don't have a packet to try to combine into, use this one as the base // Copy it first as it will still live until it is acked p=(*sitr)->Copy(); - Log(Logs::Detail, Logs::Netcode, _L "Starting combined packet with seq packet %d of len %d" __L, seq_send, p->size); + LogNetcode(_L "Starting combined packet with seq packet [{}] of len [{}]" __L, seq_send, p->size); (*sitr)->sent_time = Timer::GetCurrentTime(); ++sitr; ++count; } else if (!p->combine(*sitr)) { // Trying to combine this packet with the base didn't work (too big maybe) // So just send the base packet (we'll try this packet again later) - Log(Logs::Detail, Logs::Netcode, _L "Combined packet full at len %d, next seq packet %d is len %d" __L, p->size, seq_send + 1, (*sitr)->size); + LogNetcode(_L "Combined packet full at len [{}], next seq packet [{}] is len [{}]" __L, p->size, seq_send + 1, (*sitr)->size); ReadyToSend.push(p); BytesWritten+=p->size; p=nullptr; if ((*sitr)->opcode != OP_Fragment && BytesWritten > threshold) { // Sent enough this round, lets stop to be fair - Log(Logs::Detail, Logs::Netcode, _L "Exceeded write threshold in seq (%d > %d)" __L, BytesWritten, threshold); + LogNetcode(_L "Exceeded write threshold in seq ([{}] > [{}])" __L, BytesWritten, threshold); break; } } else { // Combine worked - Log(Logs::Detail, Logs::Netcode, _L "Combined seq packet %d of len %d, yeilding %d combined." __L, seq_send, (*sitr)->size, p->size); + LogNetcode(_L "Combined seq packet [{}] of len [{}], yeilding [{}] combined" __L, seq_send, (*sitr)->size, p->size); (*sitr)->sent_time = Timer::GetCurrentTime(); ++sitr; ++count; @@ -766,7 +766,7 @@ void EQStream::Write(int eq_fd) ++sitr; ++count; if (p) { - Log(Logs::Detail, Logs::Netcode, _L "Final combined packet not full, len %d" __L, p->size); + LogNetcode(_L "Final combined packet not full, len [{}]" __L, p->size); ReadyToSend.push(p); BytesWritten += p->size; p = nullptr; @@ -776,25 +776,25 @@ void EQStream::Write(int eq_fd) // Copy it first as it will still live until it is acked p=(*sitr)->Copy(); (*sitr)->sent_time = Timer::GetCurrentTime(); - Log(Logs::Detail, Logs::Netcode, _L "Starting combined packet with seq packet %d of len %d" __L, seq_send, p->size); + LogNetcode(_L "Starting combined packet with seq packet [{}] of len [{}]" __L, seq_send, p->size); ++sitr; ++count; } else if (!p->combine(*sitr)) { // Trying to combine this packet with the base didn't work (too big maybe) // So just send the base packet (we'll try this packet again later) - Log(Logs::Detail, Logs::Netcode, _L "Combined packet full at len %d, next seq packet %d is len %d" __L, p->size, seq_send, (*sitr)->size); + LogNetcode(_L "Combined packet full at len [{}], next seq packet [{}] is len [{}]" __L, p->size, seq_send, (*sitr)->size); ReadyToSend.push(p); BytesWritten+=p->size; p=nullptr; if (BytesWritten > threshold) { // Sent enough this round, lets stop to be fair - Log(Logs::Detail, Logs::Netcode, _L "Exceeded write threshold in seq (%d > %d)" __L, BytesWritten, threshold); + LogNetcode(_L "Exceeded write threshold in seq ([{}] > [{}])" __L, BytesWritten, threshold); break; } } else { // Combine worked - Log(Logs::Detail, Logs::Netcode, _L "Combined seq packet %d of len %d, yielding %d combined." __L, seq_send, (*sitr)->size, p->size); + LogNetcode(_L "Combined seq packet [{}] of len [{}], yielding [{}] combined" __L, seq_send, (*sitr)->size, p->size); (*sitr)->sent_time = Timer::GetCurrentTime(); ++sitr; ++count; @@ -802,7 +802,7 @@ void EQStream::Write(int eq_fd) } if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { - Log(Logs::Detail, Logs::Netcode, _L "Post send Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); + LogNetcode(_L "Post send Invalid Sequenced queue: BS [{}] + SQ [{}] != NOS [{}]" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); } } else { // No more sequenced packets @@ -814,7 +814,7 @@ void EQStream::Write(int eq_fd) // We have a packet still, must have run out of both seq and non-seq, so send it if (p) { - Log(Logs::Detail, Logs::Netcode, _L "Final combined packet not full, len %d" __L, p->size); + LogNetcode(_L "Final combined packet not full, len [{}]" __L, p->size); ReadyToSend.push(p); BytesWritten+=p->size; } @@ -831,7 +831,7 @@ void EQStream::Write(int eq_fd) if(SeqEmpty && NonSeqEmpty) { //no more data to send if(CheckState(CLOSING)) { - Log(Logs::Detail, Logs::Netcode, _L "All outgoing data flushed, closing stream." __L ); + LogNetcode(_L "All outgoing data flushed, closing stream" __L ); //we are waiting for the queues to empty, now we can do our disconnect. //this packet will not actually go out until the next call to Write(). _SendDisconnect(); @@ -910,7 +910,7 @@ void EQStream::SendSessionRequest() Request->Session=htonl(time(nullptr)); Request->MaxLength=htonl(512); - Log(Logs::Detail, Logs::Netcode, _L "Sending OP_SessionRequest: session %lu, maxlen=%d" __L, (unsigned long)ntohl(Request->Session), ntohl(Request->MaxLength)); + LogNetcode(_L "Sending OP_SessionRequest: session [{}], maxlen=[{}]" __L, (unsigned long)ntohl(Request->Session), ntohl(Request->MaxLength)); NonSequencedPush(out); } @@ -924,7 +924,7 @@ void EQStream::_SendDisconnect() *(uint32 *)out->pBuffer=htonl(Session); NonSequencedPush(out); - Log(Logs::Detail, Logs::Netcode, _L "Sending OP_SessionDisconnect: session %lu" __L, (unsigned long)Session); + LogNetcode(_L "Sending OP_SessionDisconnect: session [{}]" __L, (unsigned long)Session); } void EQStream::InboundQueuePush(EQRawApplicationPacket *p) @@ -976,7 +976,7 @@ EQRawApplicationPacket *p=nullptr; if(OpMgr != nullptr && *OpMgr != nullptr) { EmuOpcode emu_op = (*OpMgr)->EQToEmu(p->opcode); if(emu_op == OP_Unknown) { - Log(Logs::General, Logs::Netcode, "Unable to convert EQ opcode 0x%.4x to an Application opcode.", p->opcode); + LogNetcode("Unable to convert EQ opcode {:#04x} to an Application opcode", p->opcode); } p->SetOpcode(emu_op); @@ -1004,7 +1004,7 @@ void EQStream::InboundQueueClear() { EQApplicationPacket *p=nullptr; - Log(Logs::Detail, Logs::Netcode, _L "Clearing inbound queue" __L); + LogNetcode(_L "Clearing inbound queue" __L); MInboundQueue.lock(); if (!InboundQueue.empty()) { @@ -1047,7 +1047,7 @@ void EQStream::OutboundQueueClear() { EQProtocolPacket *p=nullptr; - Log(Logs::Detail, Logs::Netcode, _L "Clearing outbound queue" __L); + LogNetcode(_L "Clearing outbound queue" __L); MOutboundQueue.lock(); while(!NonSequencedQueue.empty()) { @@ -1069,7 +1069,7 @@ void EQStream::PacketQueueClear() { EQProtocolPacket *p=nullptr; - Log(Logs::Detail, Logs::Netcode, _L "Clearing future packet queue" __L); + LogNetcode(_L "Clearing future packet queue" __L); if(!PacketQueue.empty()) { std::map::iterator itr; @@ -1101,7 +1101,7 @@ void EQStream::Process(const unsigned char *buffer, const uint32 length) delete p; ProcessQueue(); } else { - Log(Logs::Detail, Logs::Netcode, _L "Incoming packet failed checksum" __L); + LogNetcode(_L "Incoming packet failed checksum" __L); } } @@ -1132,23 +1132,23 @@ std::deque::iterator itr, tmp; SeqOrder ord = CompareSequence(SequencedBase, seq); if(ord == SeqInOrder) { //they are not acking anything new... - Log(Logs::Detail, Logs::Netcode, _L "Received an ack with no window advancement (seq %d)." __L, seq); + LogNetcode(_L "Received an ack with no window advancement (seq [{}])" __L, seq); } else if(ord == SeqPast) { //they are nacking blocks going back before our buffer, wtf? - Log(Logs::Detail, Logs::Netcode, _L "Received an ack with backward window advancement (they gave %d, our window starts at %d). This is bad." __L, seq, SequencedBase); + LogNetcode(_L "Received an ack with backward window advancement (they gave [{}], our window starts at [{}]). This is bad" __L, seq, SequencedBase); } else { - Log(Logs::Detail, Logs::Netcode, _L "Received an ack up through sequence %d. Our base is %d." __L, seq, SequencedBase); + LogNetcode(_L "Received an ack up through sequence [{}]. Our base is [{}]" __L, seq, SequencedBase); //this is a good ack, we get to ack some blocks. seq++; //we stop at the block right after their ack, counting on the wrap of both numbers. while(SequencedBase != seq) { if(SequencedQueue.empty()) { - Log(Logs::Detail, Logs::Netcode, _L "OUT OF PACKETS acked packet with sequence %lu. Next send is %d before this." __L, (unsigned long)SequencedBase, SequencedQueue.size()); + LogNetcode(_L "OUT OF PACKETS acked packet with sequence [{}]. Next send is [{}] before this" __L, (unsigned long)SequencedBase, SequencedQueue.size()); SequencedBase = NextOutSeq; break; } - Log(Logs::Detail, Logs::Netcode, _L "Removing acked packet with sequence %lu." __L, (unsigned long)SequencedBase); + LogNetcode(_L "Removing acked packet with sequence [{}]" __L, (unsigned long)SequencedBase); //clean out the acked packet delete SequencedQueue.front(); SequencedQueue.pop_front(); @@ -1156,7 +1156,7 @@ std::deque::iterator itr, tmp; SequencedBase++; } if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { - Log(Logs::Detail, Logs::Netcode, _L "Post-Ack on %d Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, seq, SequencedBase, SequencedQueue.size(), NextOutSeq); + LogNetcode(_L "Post-Ack on [{}] Invalid Sequenced queue: BS [{}] + SQ [{}] != NOS [{}]" __L, seq, SequencedBase, SequencedQueue.size(), NextOutSeq); } } @@ -1166,7 +1166,7 @@ std::deque::iterator itr, tmp; void EQStream::SetNextAckToSend(uint32 seq) { MAcks.lock(); - Log(Logs::Detail, Logs::Netcode, _L "Set Next Ack To Send to %lu" __L, (unsigned long)seq); + LogNetcode(_L "Set Next Ack To Send to [{}]" __L, (unsigned long)seq); NextAckToSend=seq; MAcks.unlock(); } @@ -1174,7 +1174,7 @@ void EQStream::SetNextAckToSend(uint32 seq) void EQStream::SetLastAckSent(uint32 seq) { MAcks.lock(); - Log(Logs::Detail, Logs::Netcode, _L "Set Last Ack Sent to %lu" __L, (unsigned long)seq); + LogNetcode(_L "Set Last Ack Sent to [{}]" __L, (unsigned long)seq); LastAckSent=seq; MAcks.unlock(); } @@ -1187,10 +1187,10 @@ void EQStream::ProcessQueue() EQProtocolPacket *qp=nullptr; while((qp=RemoveQueue(NextInSeq))!=nullptr) { - Log(Logs::Detail, Logs::Netcode, _L "Processing Queued Packet: Seq=%d" __L, NextInSeq); + LogNetcode(_L "Processing Queued Packet: Seq=[{}]" __L, NextInSeq); ProcessPacket(qp); delete qp; - Log(Logs::Detail, Logs::Netcode, _L "OP_Packet Queue size=%d" __L, PacketQueue.size()); + LogNetcode(_L "OP_Packet Queue size=[{}]" __L, PacketQueue.size()); } } @@ -1201,21 +1201,21 @@ EQProtocolPacket *qp=nullptr; if ((itr=PacketQueue.find(seq))!=PacketQueue.end()) { qp=itr->second; PacketQueue.erase(itr); - Log(Logs::Detail, Logs::Netcode, _L "OP_Packet Queue size=%d" __L, PacketQueue.size()); + LogNetcode(_L "OP_Packet Queue size=[{}]" __L, PacketQueue.size()); } return qp; } void EQStream::SetStreamType(EQStreamType type) { - Log(Logs::Detail, Logs::Netcode, _L "Changing stream type from %s to %s" __L, StreamTypeString(StreamType), StreamTypeString(type)); + LogNetcode(_L "Changing stream type from [{}] to [{}]" __L, StreamTypeString(StreamType), StreamTypeString(type)); StreamType=type; switch (StreamType) { case LoginStream: app_opcode_size=1; compressed=false; encoded=false; - Log(Logs::Detail, Logs::Netcode, _L "Login stream has app opcode size %d, is not compressed or encoded." __L, app_opcode_size); + LogNetcode(_L "Login stream has app opcode size [{}], is not compressed or encoded" __L, app_opcode_size); break; case ChatOrMailStream: case ChatStream: @@ -1223,7 +1223,7 @@ void EQStream::SetStreamType(EQStreamType type) app_opcode_size=1; compressed=false; encoded=true; - Log(Logs::Detail, Logs::Netcode, _L "Chat/Mail stream has app opcode size %d, is not compressed, and is encoded." __L, app_opcode_size); + LogNetcode(_L "Chat/Mail stream has app opcode size [{}], is not compressed, and is encoded" __L, app_opcode_size); break; case ZoneStream: case WorldStream: @@ -1231,7 +1231,7 @@ void EQStream::SetStreamType(EQStreamType type) app_opcode_size=2; compressed=true; encoded=false; - Log(Logs::Detail, Logs::Netcode, _L "World/Zone stream has app opcode size %d, is compressed, and is not encoded." __L, app_opcode_size); + LogNetcode(_L "World/Zone stream has app opcode size [{}], is compressed, and is not encoded" __L, app_opcode_size); break; } } @@ -1281,7 +1281,7 @@ EQStream::SeqOrder EQStream::CompareSequence(uint16 expected_seq , uint16 seq) void EQStream::SetState(EQStreamState state) { MState.lock(); - Log(Logs::Detail, Logs::Netcode, _L "Changing state from %d to %d" __L, State, state); + LogNetcode(_L "Changing state from [{}] to [{}]" __L, State, state); State=state; MState.unlock(); } @@ -1293,29 +1293,29 @@ void EQStream::CheckTimeout(uint32 now, uint32 timeout) { EQStreamState orig_state = GetState(); if (orig_state == CLOSING && !outgoing_data) { - Log(Logs::Detail, Logs::Netcode, _L "Out of data in closing state, disconnecting." __L); + LogNetcode(_L "Out of data in closing state, disconnecting" __L); _SendDisconnect(); SetState(DISCONNECTING); } else if (LastPacket && (now-LastPacket) > timeout) { switch(orig_state) { case CLOSING: //if we time out in the closing state, they are not acking us, just give up - Log(Logs::Detail, Logs::Netcode, _L "Timeout expired in closing state. Moving to closed state." __L); + LogNetcode(_L "Timeout expired in closing state. Moving to closed state" __L); _SendDisconnect(); SetState(CLOSED); break; case DISCONNECTING: //we timed out waiting for them to send us the disconnect reply, just give up. - Log(Logs::Detail, Logs::Netcode, _L "Timeout expired in disconnecting state. Moving to closed state." __L); + LogNetcode(_L "Timeout expired in disconnecting state. Moving to closed state" __L); SetState(CLOSED); break; case CLOSED: - Log(Logs::Detail, Logs::Netcode, _L "Timeout expired in closed state??" __L); + LogNetcode(_L "Timeout expired in closed state??" __L); break; case ESTABLISHED: //we timed out during normal operation. Try to be nice about it. //we will almost certainly time out again waiting for the disconnect reply, but oh well. - Log(Logs::Detail, Logs::Netcode, _L "Timeout expired in established state. Closing connection." __L); + LogNetcode(_L "Timeout expired in established state. Closing connection" __L); _SendDisconnect(); SetState(DISCONNECTING); break; @@ -1342,7 +1342,7 @@ void EQStream::Decay() for (auto sitr = SequencedQueue.begin(); sitr != SequencedQueue.end(); ++sitr, count++) { if (!(*sitr)->acked && (*sitr)->sent_time > 0 && ((*sitr)->sent_time + retransmittimeout) < Timer::GetCurrentTime()) { (*sitr)->sent_time = 0; - Log(Logs::Detail, Logs::Netcode, _L "Timeout exceeded for seq %d. Flagging packet for retransmission" __L, SequencedBase + count); + LogNetcode(_L "Timeout exceeded for seq [{}]. Flagging packet for retransmission" __L, SequencedBase + count); } } MOutboundQueue.unlock(); @@ -1384,12 +1384,12 @@ void EQStream::AdjustRates(uint32 average_delta) void EQStream::Close() { if(HasOutgoingData()) { //there is pending data, wait for it to go out. - Log(Logs::Detail, Logs::Netcode, _L "Stream requested to Close(), but there is pending data, waiting for it." __L); + LogNetcode(_L "Stream requested to Close(), but there is pending data, waiting for it" __L); SetState(CLOSING); } else { //otherwise, we are done, we can drop immediately. _SendDisconnect(); - Log(Logs::Detail, Logs::Netcode, _L "Stream closing immediate due to Close()" __L); + LogNetcode(_L "Stream closing immediate due to Close()" __L); SetState(DISCONNECTING); } } @@ -1417,19 +1417,19 @@ EQStream::MatchState EQStream::CheckSignature(const Signature *sig) { } else if(p->opcode == sig->first_eq_opcode) { //opcode matches, check length.. if(p->size == sig->first_length) { - Log(Logs::General, Logs::Netcode, "[IDENT_TRACE] %s:%d: First opcode matched 0x%x and length matched %d", long2ip(GetRemoteIP()).c_str(), ntohs(GetRemotePort()), sig->first_eq_opcode, p->size); + LogNetcode("[StreamIdentify] [{}]:[{}]: First opcode matched {:#04x} and length matched [{}]", long2ip(GetRemoteIP()).c_str(), ntohs(GetRemotePort()), sig->first_eq_opcode, p->size); res = MatchSuccessful; } else if(sig->first_length == 0) { - Log(Logs::General, Logs::Netcode, "[IDENT_TRACE] %s:%d: First opcode matched 0x%x and length (%d) is ignored", long2ip(GetRemoteIP()).c_str(), ntohs(GetRemotePort()), sig->first_eq_opcode, p->size); + LogNetcode("[StreamIdentify] [{}]:[{}]: First opcode matched {:#04x} and length ([{}]) is ignored", long2ip(GetRemoteIP()).c_str(), ntohs(GetRemotePort()), sig->first_eq_opcode, p->size); res = MatchSuccessful; } else { //opcode matched but length did not. - Log(Logs::General, Logs::Netcode, "[IDENT_TRACE] %s:%d: First opcode matched 0x%x, but length %d did not match expected %d", long2ip(GetRemoteIP()).c_str(), ntohs(GetRemotePort()), sig->first_eq_opcode, p->size, sig->first_length); + LogNetcode("[StreamIdentify] [{}]:[{}]: First opcode matched {:#04x}, but length [{}] did not match expected [{}]", long2ip(GetRemoteIP()).c_str(), ntohs(GetRemotePort()), sig->first_eq_opcode, p->size, sig->first_length); res = MatchFailed; } } else { //first opcode did not match.. - Log(Logs::General, Logs::Netcode, "[IDENT_TRACE] %s:%d: First opcode 0x%x did not match expected 0x%x", long2ip(GetRemoteIP()).c_str(), ntohs(GetRemotePort()), p->opcode, sig->first_eq_opcode); + LogNetcode("[StreamIdentify] [{}]:[{}]: First opcode {:#04x} did not match expected {:#04x}", long2ip(GetRemoteIP()).c_str(), ntohs(GetRemotePort()), p->opcode, sig->first_eq_opcode); res = MatchFailed; } } diff --git a/common/eq_stream_ident.cpp b/common/eq_stream_ident.cpp index 5706bbced..8222e471a 100644 --- a/common/eq_stream_ident.cpp +++ b/common/eq_stream_ident.cpp @@ -46,7 +46,7 @@ void EQStreamIdentifier::Process() { //first see if this stream has expired if(r.expire.Check(false)) { - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Unable to identify stream from %s:%d before timeout.", r.stream->GetRemoteAddr().c_str(), ntohs(r.stream->GetRemotePort())); + LogNetcode("[StreamIdentify] Unable to identify stream from [{}:{}] before timeout", r.stream->GetRemoteAddr().c_str(), ntohs(r.stream->GetRemotePort())); r.stream->Close(); cur = m_streams.erase(cur); @@ -62,23 +62,23 @@ void EQStreamIdentifier::Process() { } if(r.stream->GetState() != ESTABLISHED) { //the stream closed before it was identified. - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Unable to identify stream from %s:%d before it closed.", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort())); + LogNetcode("[StreamIdentify] Unable to identify stream from [{}:{}] before it closed", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort())); switch(r.stream->GetState()) { case ESTABLISHED: - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Stream state was Established"); + LogNetcode("[StreamIdentify] Stream state was Established"); break; case CLOSING: - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Stream state was Closing"); + LogNetcode("[StreamIdentify] Stream state was Closing"); break; case DISCONNECTING: - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Stream state was Disconnecting"); + LogNetcode("[StreamIdentify] Stream state was Disconnecting"); break; case CLOSED: - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Stream state was Closed"); + LogNetcode("[StreamIdentify] Stream state was Closed"); break; default: - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Stream state was Unestablished or unknown"); + LogNetcode("[StreamIdentify] Stream state was Unestablished or unknown"); break; } r.stream->ReleaseFromUse(); @@ -102,13 +102,13 @@ void EQStreamIdentifier::Process() { switch(res) { case EQStreamInterface::MatchNotReady: //the stream has not received enough packets to compare with this signature -// Log.LogDebugType(Logs::General, Logs::Netcode, "[IDENT_TRACE] %s:%d: Tried patch %s, but stream is not ready for it.", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort()), p->name.c_str()); +// Log.LogDebugType(Logs::General, Logs::Netcode, "[StreamIdentify] %s:%d: Tried patch %s, but stream is not ready for it.", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort()), p->name.c_str()); all_ready = false; break; case EQStreamInterface::MatchSuccessful: { //yay, a match. - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Identified stream %s:%d with signature %s", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort()), p->name.c_str()); + LogNetcode("[StreamIdentify] Identified stream [{}:{}] with signature [{}]", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort()), p->name.c_str()); // before we assign the eqstream to an interface, let the stream recognize it is in use and the session should not be reset any further r.stream->SetActive(true); @@ -122,7 +122,7 @@ void EQStreamIdentifier::Process() { } case EQStreamInterface::MatchFailed: //do nothing... - Log(Logs::General, Logs::Netcode, "[IDENT_TRACE] %s:%d: Tried patch %s, and it did not match.", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort()), p->name.c_str()); + LogNetcode("[StreamIdentify] [{}:{}] Tried patch [{}] and it did not match", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort()), p->name.c_str()); break; } } @@ -130,7 +130,7 @@ void EQStreamIdentifier::Process() { //if we checked all patches and did not find a match. if(all_ready && !found_one) { //the stream cannot be identified. - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Unable to identify stream from %s:%d, no match found.", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort())); + LogNetcode("[StreamIdentify] Unable to identify stream from [{}:{}], no match found", long2ip(r.stream->GetRemoteIP()).c_str(), ntohs(r.stream->GetRemotePort())); r.stream->ReleaseFromUse(); } diff --git a/common/eq_stream_proxy.cpp b/common/eq_stream_proxy.cpp index 402deaa34..587e55b64 100644 --- a/common/eq_stream_proxy.cpp +++ b/common/eq_stream_proxy.cpp @@ -42,8 +42,8 @@ void EQStreamProxy::QueuePacket(const EQApplicationPacket *p, bool ack_req) { return; if (p->GetOpcode() != OP_SpecialMesg) { - Log(Logs::General, Logs::Server_Client_Packet, "[%s - 0x%04x] [Size: %u]", OpcodeManager::EmuToName(p->GetOpcode()), p->GetOpcode(), p->Size()); - Log(Logs::General, Logs::Server_Client_Packet_With_Dump, "[%s - 0x%04x] [Size: %u] %s", OpcodeManager::EmuToName(p->GetOpcode()), p->GetOpcode(), p->Size(), DumpPacketToString(p).c_str()); + Log(Logs::General, Logs::PacketServerClient, "[%s - 0x%04x] [Size: %u]", OpcodeManager::EmuToName(p->GetOpcode()), p->GetOpcode(), p->Size()); + Log(Logs::General, Logs::PacketServerClientWithDump, "[%s - 0x%04x] [Size: %u] %s", OpcodeManager::EmuToName(p->GetOpcode()), p->GetOpcode(), p->Size(), DumpPacketToString(p).c_str()); } EQApplicationPacket *newp = p->Copy(); diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index f84e0839c..c2d786d94 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -19,6 +19,7 @@ */ #include "eqemu_logsys.h" +#include "rulesys.h" #include "platform.h" #include "string_util.h" #include "database.h" @@ -86,18 +87,14 @@ 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 &) {}; - bool file_logs_enabled = false; - int log_platform = 0; + on_log_gmsay_hook = [](uint16 log_type, const std::string &) {}; + on_log_console_hook = [](uint16 debug_level, uint16 log_type, const std::string &) {}; } /** * EQEmuLogSys Deconstructor */ -EQEmuLogSys::~EQEmuLogSys() -{ -} +EQEmuLogSys::~EQEmuLogSys() = default; void EQEmuLogSys::LoadLogSettingsDefaults() { @@ -105,7 +102,7 @@ void EQEmuLogSys::LoadLogSettingsDefaults() * Get Executable platform currently running this code (Zone/World/etc) */ log_platform = GetExecutablePlatformInt(); - + for (int log_category_id = Logs::AA; log_category_id != Logs::MaxCategoryID; log_category_id++) { log_settings[log_category_id].log_to_console = 0; log_settings[log_category_id].log_to_file = 0; @@ -118,15 +115,26 @@ void EQEmuLogSys::LoadLogSettingsDefaults() /** * Set Defaults */ - log_settings[Logs::World_Server].log_to_console = Logs::General; - log_settings[Logs::Zone_Server].log_to_console = Logs::General; - log_settings[Logs::QS_Server].log_to_console = Logs::General; - log_settings[Logs::UCS_Server].log_to_console = Logs::General; - log_settings[Logs::Crash].log_to_console = Logs::General; - log_settings[Logs::MySQLError].log_to_console = Logs::General; - log_settings[Logs::Login_Server].log_to_console = Logs::General; - log_settings[Logs::Headless_Client].log_to_console = Logs::General; - log_settings[Logs::NPCScaling].log_to_gmsay = Logs::General; + log_settings[Logs::WorldServer].log_to_console = static_cast(Logs::General); + log_settings[Logs::ZoneServer].log_to_console = static_cast(Logs::General); + log_settings[Logs::QSServer].log_to_console = static_cast(Logs::General); + log_settings[Logs::UCSServer].log_to_console = static_cast(Logs::General); + log_settings[Logs::Crash].log_to_console = static_cast(Logs::General); + log_settings[Logs::MySQLError].log_to_console = static_cast(Logs::General); + log_settings[Logs::Loginserver].log_to_console = static_cast(Logs::General); + log_settings[Logs::HeadlessClient].log_to_console = static_cast(Logs::General); + log_settings[Logs::NPCScaling].log_to_gmsay = static_cast(Logs::General); + + /** + * RFC 5424 + */ + log_settings[Logs::Emergency].log_to_console = static_cast(Logs::General); + log_settings[Logs::Alert].log_to_console = static_cast(Logs::General); + log_settings[Logs::Critical].log_to_console = static_cast(Logs::General); + log_settings[Logs::Error].log_to_console = static_cast(Logs::General); + log_settings[Logs::Warning].log_to_console = static_cast(Logs::General); + log_settings[Logs::Notice].log_to_console = static_cast(Logs::General); + log_settings[Logs::Info].log_to_console = static_cast(Logs::General); /** * Set Category enabled status on defaults @@ -167,20 +175,41 @@ void EQEmuLogSys::LoadLogSettingsDefaults() } } +/** + * @param log_category + * @return + */ +bool EQEmuLogSys::IsRfc5424LogCategory(uint16 log_category) +{ + return ( + log_category == Logs::Emergency || + log_category == Logs::Alert || + log_category == Logs::Critical || + log_category == Logs::Error || + log_category == Logs::Warning || + log_category == Logs::Notice || + log_category == Logs::Info || + log_category == Logs::Debug + ); +} + /** * @param log_category * @param in_message * @return */ -std::string EQEmuLogSys::FormatOutMessageString(uint16 log_category, const std::string &in_message) +std::string EQEmuLogSys::FormatOutMessageString( + uint16 log_category, + const std::string &in_message +) { - std::string ret; - ret.push_back('['); - ret.append(Logs::LogCategoryName[log_category]); - ret.push_back(']'); - ret.push_back(' '); - ret.append(in_message); - return ret; + std::string return_string; + + if (IsRfc5424LogCategory(log_category)) { + return_string = "[" + GetPlatformName() + "] "; + } + + return return_string + "[" + Logs::LogCategoryName[log_category] + "] " + in_message; } /** @@ -188,7 +217,11 @@ std::string EQEmuLogSys::FormatOutMessageString(uint16 log_category, const std:: * @param log_category * @param message */ -void EQEmuLogSys::ProcessGMSay(uint16 debug_level, uint16 log_category, const std::string &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 @@ -210,7 +243,11 @@ void EQEmuLogSys::ProcessGMSay(uint16 debug_level, uint16 log_category, const st * @param log_category * @param message */ -void EQEmuLogSys::ProcessLogWrite(uint16 debug_level, uint16 log_category, const std::string &message) +void EQEmuLogSys::ProcessLogWrite( + uint16 debug_level, + uint16 log_category, + const std::string &message +) { if (log_category == Logs::Crash) { char time_stamp[80]; @@ -272,6 +309,8 @@ std::string EQEmuLogSys::GetLinuxConsoleColorFromCategory(uint16 log_category) case Logs::Normal: return LC_YELLOW; case Logs::MySQLError: + case Logs::Warning: + case Logs::Critical: case Logs::Error: return LC_RED; case Logs::MySQLQuery: @@ -343,6 +382,42 @@ void EQEmuLogSys::ProcessConsoleMessage(uint16 debug_level, uint16 log_category, on_log_console_hook(debug_level, log_category, message); } +/** + * @param str + * @return + */ +constexpr const char *str_end(const char *str) +{ + return *str ? str_end(str + 1) : str; +} + +/** + * @param str + * @return + */ +constexpr bool str_slant(const char *str) +{ + return *str == '/' ? true : (*str ? str_slant(str + 1) : false); +} + +/** + * @param str + * @return + */ +constexpr const char *r_slant(const char *str) +{ + return *str == '/' ? (str + 1) : r_slant(str - 1); +} + +/** + * @param str + * @return + */ +constexpr const char *base_file_name(const char *str) +{ + return str_slant(str) ? r_slant(str_end(str)) : str; +} + /** * Core logging function * @@ -351,7 +426,15 @@ void EQEmuLogSys::ProcessConsoleMessage(uint16 debug_level, uint16 log_category, * @param message * @param ... */ -void EQEmuLogSys::Out(Logs::DebugLevel debug_level, uint16 log_category, std::string message, ...) +void EQEmuLogSys::Out( + Logs::DebugLevel debug_level, + uint16 log_category, + const char *file, + const char *func, + int line, + const char *message, + ... +) { bool log_to_console = true; if (log_settings[log_category].log_to_console < debug_level) { @@ -373,12 +456,18 @@ void EQEmuLogSys::Out(Logs::DebugLevel debug_level, uint16 log_category, std::st return; } + std::string prefix; + + if (RuleB(Logging, PrintFileFunctionAndLine)) { + prefix = fmt::format("[{0}::{1}:{2}] ", base_file_name(file), func, line); + } + va_list args; va_start(args, message); - std::string output_message = vStringFormat(message.c_str(), args); + std::string output_message = vStringFormat(message, args); va_end(args); - std::string output_debug_message = EQEmuLogSys::FormatOutMessageString(log_category, output_message); + 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); @@ -414,7 +503,7 @@ void EQEmuLogSys::MakeDirectory(const std::string &directory_name) return; _mkdir(directory_name.c_str()); #else - struct stat st; + struct stat st{}; if (stat(directory_name.c_str(), &st) == 0) { // exists return; } @@ -455,12 +544,7 @@ void EQEmuLogSys::StartFileLogs(const std::string &log_name) return; } - EQEmuLogSys::Out( - Logs::General, - Logs::Status, - "Starting File Log 'logs/%s_%i.log'", - platform_file_name.c_str(), - getpid()); + LogInfo("Starting File Log [logs/{}_{}.log]", platform_file_name.c_str(), getpid()); /** * Make directory if not exists @@ -480,17 +564,11 @@ void EQEmuLogSys::StartFileLogs(const std::string &log_name) /** * All other processes */ - if (platform_file_name.empty()) { return; } - EQEmuLogSys::Out( - Logs::General, - Logs::Status, - "Starting File Log 'logs/%s_%i.log'", - platform_file_name.c_str(), - getpid()); + LogInfo("Starting File Log [logs/{}_{}.log]", platform_file_name.c_str(), getpid()); /** * Open file pointer diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 05f4a5fee..11645c6fa 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -25,6 +25,14 @@ #include #include #include + +#ifdef _WIN32 +#ifdef utf16_to_utf8 +#undef utf16_to_utf8 +#endif +#endif + +#include #include "types.h" namespace Logs { @@ -45,7 +53,7 @@ namespace Logs { AI, Aggro, Attack, - Client_Server_Packet, + PacketClientServer, Combat, Commands, Crash, @@ -59,40 +67,46 @@ namespace Logs { Normal, Object, Pathing, - QS_Server, + QSServer, Quests, Rules, Skills, Spawns, Spells, Status, - TCP_Connection, + TCPConnection, Tasks, Tradeskills, Trading, Tribute, - UCS_Server, - WebInterface_Server, - World_Server, - Zone_Server, + UCSServer, + WebInterfaceServer, + WorldServer, + ZoneServer, MySQLError, MySQLQuery, Mercenaries, QuestDebug, - Server_Client_Packet, - Client_Server_Packet_Unhandled, - Server_Client_Packet_With_Dump, - Client_Server_Packet_With_Dump, - Login_Server, - Client_Login, - Headless_Client, - HP_Update, + PacketServerClient, + PacketClientServerUnhandled, + PacketServerClientWithDump, + PacketClientServerWithDump, + Loginserver, + ClientLogin, + HeadlessClient, + HPUpdate, FixZ, Food, Traps, NPCRoamBox, NPCScaling, MobAppearance, + Info, + Warning, + Critical, + Emergency, + Alert, + Notice, MaxCategoryID /* Don't Remove this */ }; @@ -152,19 +166,17 @@ namespace Logs { "Traps", "NPC Roam Box", "NPC Scaling", - "Mob Appearance" + "Mob Appearance", + "Info", + "Warning", + "Critical", + "Emergency", + "Alert", + "Notice" }; } -#define Log(debug_level, log_category, message, ...) do {\ - if (LogSys.log_settings[log_category].is_category_enabled == 1)\ - LogSys.Out(debug_level, log_category, message, ##__VA_ARGS__);\ -} while (0) - -#define LogF(debug_level, log_category, message, ...) do {\ - if (LogSys.log_settings[log_category].is_category_enabled == 1)\ - OutF(LogSys, debug_level, log_category, message, ##__VA_ARGS__);\ -} while (0) +#include "eqemu_logsys_log_aliases.h" class EQEmuLogSys { public: @@ -177,6 +189,10 @@ public: */ void CloseFileLogs(); void LoadLogSettingsDefaults(); + + /** + * @param directory_name + */ void MakeDirectory(const std::string &directory_name); /** @@ -188,12 +204,25 @@ public: * - This would pipe the same category and debug level to all output formats, but the internal memory reference of log_settings would * be checked against to see if that piped output is set to actually process it for the category and debug level */ - void Out(Logs::DebugLevel debug_level, uint16 log_category, std::string message, ...); + void Out( + Logs::DebugLevel debug_level, + uint16 log_category, + const char *file, + const char *func, + int line, + const char *message, + ... + ); /** * Used in file logs to prepend a timestamp entry for logs + * @param time_stamp */ void SetCurrentTimeStamp(char* time_stamp); + + /** + * @param log_name + */ void StartFileLogs(const std::string &log_name = ""); /** @@ -218,14 +247,14 @@ public: * These are loaded via DB and have defaults loaded in LoadLogSettingsDefaults * Database loaded via Database::LoadLogSettings(log_settings) */ - LogSettings log_settings[Logs::LogCategory::MaxCategoryID]; + LogSettings log_settings[Logs::LogCategory::MaxCategoryID]{}; - bool file_logs_enabled; + bool file_logs_enabled = false; /** * Sets Executable platform (Zone/World/UCS) etc. */ - int log_platform; + int log_platform = 0; /** * File name used in writing logs @@ -240,7 +269,14 @@ public: */ uint16 GetGMSayColorFromCategory(uint16 log_category); + /** + * @param f + */ void SetGMSayHandler(std::function f) { on_log_gmsay_hook = f; } + + /** + * @param f + */ void SetConsoleHandler(std::function f) { on_log_console_hook = f; } private: @@ -258,6 +294,7 @@ private: /** * Linux console color messages mapped by category + * * @param log_category * @return */ @@ -268,11 +305,57 @@ private: */ uint16 GetWindowsConsoleColorFromCategory(uint16 log_category); + /** + * @param debug_level + * @param log_category + * @param message + */ void ProcessConsoleMessage(uint16 debug_level, uint16 log_category, const std::string &message); + + /** + * @param debug_level + * @param log_category + * @param message + */ void ProcessGMSay(uint16 debug_level, uint16 log_category, const std::string &message); + + /** + * @param debug_level + * @param log_category + * @param message + */ void ProcessLogWrite(uint16 debug_level, uint16 log_category, const std::string &message); + + /** + * @param log_category + * @return + */ + bool IsRfc5424LogCategory(uint16 log_category); }; extern EQEmuLogSys LogSys; +/** +template +void OutF( + EQEmuLogSys &ls, + Logs::DebugLevel debug_level, + uint16 log_category, + const char *file, + const char *func, + int line, + const char *fmt, + const Args &... args +) +{ + std::string log_str = fmt::format(fmt, args...); + ls.Out(debug_level, log_category, file, func, line, log_str.c_str()); +} + **/ + +#define OutF(ls, debug_level, log_category, file, func, line, formatStr, ...) \ +do { \ + ls.Out(debug_level, log_category, file, func, line, fmt::format(formatStr, ##__VA_ARGS__).c_str()); \ +} while(0) + #endif diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h new file mode 100644 index 000000000..a57c6cd07 --- /dev/null +++ b/common/eqemu_logsys_log_aliases.h @@ -0,0 +1,507 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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_EQEMU_LOGSYS_LOG_ALIASES_H +#define EQEMU_EQEMU_LOGSYS_LOG_ALIASES_H + +/** + * RFC 5424 + */ + +#define LogEmergency(message, ...) do {\ + if (LogSys.log_settings[Logs::Emergency].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Emergency, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAlert(message, ...) do {\ + if (LogSys.log_settings[Logs::Alert].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Alert, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogCritical(message, ...) do {\ + if (LogSys.log_settings[Logs::Critical].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Critical, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogError(message, ...) do {\ + if (LogSys.log_settings[Logs::Error].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Error, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogWarning(message, ...) do {\ + if (LogSys.log_settings[Logs::Warning].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Warning, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogNotice(message, ...) do {\ + if (LogSys.log_settings[Logs::Notice].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Notice, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogInfo(message, ...) do {\ + if (LogSys.log_settings[Logs::Info].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Info, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogDebug(message, ...) do {\ + if (LogSys.log_settings[Logs::Debug].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Debug, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +/** + * Category + */ + +#define LogAA(message, ...) do {\ + if (LogSys.log_settings[Logs::AA].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::AA, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAADetail(message, ...) do {\ + if (LogSys.log_settings[Logs::AA].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::AA, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAI(message, ...) do {\ + if (LogSys.log_settings[Logs::AI].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::AI, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAIDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::AI].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::AI, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAggro(message, ...) do {\ + if (LogSys.log_settings[Logs::Aggro].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Aggro, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAggroDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Aggro].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Aggro, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAttack(message, ...) do {\ + if (LogSys.log_settings[Logs::Attack].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Attack, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAttackDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Attack].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Attack, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogPacketClientServer(message, ...) do {\ + if (LogSys.log_settings[Logs::PacketClientServer].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::PacketClientServer, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogPacketClientServerDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::PacketClientServer].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::PacketClientServer, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogCombat(message, ...) do {\ + if (LogSys.log_settings[Logs::Combat].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Combat, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogCombatDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Combat].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Combat, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogCommands(message, ...) do {\ + if (LogSys.log_settings[Logs::Commands].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Commands, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogCommandsDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Commands].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Commands, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogCrash(message, ...) do {\ + if (LogSys.log_settings[Logs::Crash].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Crash, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogCrashDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Crash].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Crash, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogDoors(message, ...) do {\ + if (LogSys.log_settings[Logs::Doors].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Doors, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogDoorsDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Doors].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Doors, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogGuilds(message, ...) do {\ + if (LogSys.log_settings[Logs::Guilds].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Guilds, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogGuildsDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Guilds].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Guilds, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogInventory(message, ...) do {\ + if (LogSys.log_settings[Logs::Inventory].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Inventory, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogInventoryDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Inventory].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Inventory, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogLauncher(message, ...) do {\ + if (LogSys.log_settings[Logs::Launcher].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Launcher, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogLauncherDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Launcher].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Launcher, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogNetcode(message, ...) do {\ + if (LogSys.log_settings[Logs::Netcode].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Netcode, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogNetcodeDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Netcode].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Netcode, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogNormal(message, ...) do {\ + if (LogSys.log_settings[Logs::Normal].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Normal, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogNormalDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Normal].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Normal, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogObject(message, ...) do {\ + if (LogSys.log_settings[Logs::Object].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Object, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogObjectDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Object].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Object, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogPathing(message, ...) do {\ + if (LogSys.log_settings[Logs::Pathing].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Pathing, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogPathingDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Pathing].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Pathing, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogQSServer(message, ...) do {\ + if (LogSys.log_settings[Logs::QSServer].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::QSServer, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogQSServerDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::QSServer].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::QSServer, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogQuests(message, ...) do {\ + if (LogSys.log_settings[Logs::Quests].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Quests, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogQuestsDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Quests].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Quests, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogRules(message, ...) do {\ + if (LogSys.log_settings[Logs::Rules].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Rules, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogRulesDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Rules].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Rules, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogSkills(message, ...) do {\ + if (LogSys.log_settings[Logs::Skills].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Skills, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogSkillsDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Skills].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Skills, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogSpawns(message, ...) do {\ + if (LogSys.log_settings[Logs::Spawns].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Spawns, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogSpawnsDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Spawns].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Spawns, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogSpells(message, ...) do {\ + if (LogSys.log_settings[Logs::Spells].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Spells, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogSpellsDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Spells].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Spells, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTCPConnection(message, ...) do {\ + if (LogSys.log_settings[Logs::TCPConnection].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::TCPConnection, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTCPConnectionDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::TCPConnection].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::TCPConnection, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTasks(message, ...) do {\ + if (LogSys.log_settings[Logs::Tasks].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Tasks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTasksDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Tasks].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Tasks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTradeskills(message, ...) do {\ + if (LogSys.log_settings[Logs::Tradeskills].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Tradeskills, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTradeskillsDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Tradeskills].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Tradeskills, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTrading(message, ...) do {\ + if (LogSys.log_settings[Logs::Trading].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Trading, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTradingDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Trading].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Trading, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTribute(message, ...) do {\ + if (LogSys.log_settings[Logs::Tribute].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Tribute, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTributeDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Tribute].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Tribute, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogMySQLError(message, ...) do {\ + if (LogSys.log_settings[Logs::MySQLError].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::MySQLError, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogMySQLErrorDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::MySQLError].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::MySQLError, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogMySQLQuery(message, ...) do {\ + if (LogSys.log_settings[Logs::MySQLQuery].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::MySQLQuery, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogMySQLQueryDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::MySQLQuery].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::MySQLQuery, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogMercenaries(message, ...) do {\ + if (LogSys.log_settings[Logs::Mercenaries].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Mercenaries, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogMercenariesDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Mercenaries].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Mercenaries, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogQuestDebug(message, ...) do {\ + if (LogSys.log_settings[Logs::QuestDebug].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::QuestDebug, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogQuestDebugDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::QuestDebug].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::QuestDebug, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogLoginserver(message, ...) do {\ + if (LogSys.log_settings[Logs::Loginserver].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Loginserver, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogLoginserverDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Loginserver].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Loginserver, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogClientLogin(message, ...) do {\ + if (LogSys.log_settings[Logs::ClientLogin].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::ClientLogin, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogClientLoginDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::ClientLogin].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::ClientLogin, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogHeadlessClient(message, ...) do {\ + if (LogSys.log_settings[Logs::HeadlessClient].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::HeadlessClient, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogHeadlessClientDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::HeadlessClient].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::HeadlessClient, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogHPUpdate(message, ...) do {\ + if (LogSys.log_settings[Logs::HPUpdate].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::HPUpdate, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogHPUpdateDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::HPUpdate].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::HPUpdate, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogFixZ(message, ...) do {\ + if (LogSys.log_settings[Logs::FixZ].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::FixZ, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogFixZDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::FixZ].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::FixZ, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogFood(message, ...) do {\ + if (LogSys.log_settings[Logs::Food].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Food, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogFoodDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Food].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Food, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTraps(message, ...) do {\ + if (LogSys.log_settings[Logs::Traps].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Traps, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTrapsDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Traps].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Traps, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogNPCRoamBox(message, ...) do {\ + if (LogSys.log_settings[Logs::NPCRoamBox].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::NPCRoamBox, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogNPCRoamBoxDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::NPCRoamBox].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::NPCRoamBox, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogNPCScaling(message, ...) do {\ + if (LogSys.log_settings[Logs::NPCScaling].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::NPCScaling, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogNPCScalingDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::NPCScaling].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::NPCScaling, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogMobAppearance(message, ...) do {\ + if (LogSys.log_settings[Logs::MobAppearance].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::MobAppearance, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogMobAppearanceDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::MobAppearance].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::MobAppearance, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogStatus(message, ...) do {\ + if (LogSys.log_settings[Logs::Status].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Status, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogStatusDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Status].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Status, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +/** +* Misc +*/ + +#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__);\ +} while (0) + +#define LogF(debug_level, log_category, message, ...) do {\ + if (LogSys.log_settings[log_category].is_category_enabled == 1)\ + OutF(LogSys, debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + + +#endif //EQEMU_EQEMU_LOGSYS_LOG_ALIASES_H \ No newline at end of file diff --git a/common/event/task_scheduler.h b/common/event/task_scheduler.h new file mode 100644 index 000000000..6cfe15f00 --- /dev/null +++ b/common/event/task_scheduler.h @@ -0,0 +1,114 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace EQ +{ + namespace Event + { + class TaskScheduler + { + public: + static const int DefaultThreadCount = 4; + + TaskScheduler() : _running(false) + { + Start(DefaultThreadCount); + } + + TaskScheduler(size_t threads) : _running(false) + { + Start(threads); + } + + ~TaskScheduler() { + Stop(); + } + + void Start(size_t threads) { + if (true == _running) { + return; + } + + _running = true; + + for (size_t i = 0; i < threads; ++i) { + _threads.push_back(std::thread(std::bind(&TaskScheduler::ProcessWork, this))); + } + } + + void Stop() { + if (false == _running) { + return; + } + + { + std::unique_lock lock(_lock); + _running = false; + } + + _cv.notify_all(); + + for (auto &t : _threads) { + t.join(); + } + } + + template + auto Enqueue(Fn&& fn, Args&&... args) -> std::future::type> { + using return_type = typename std::result_of::type; + + auto task = std::make_shared>( + std::bind(std::forward(fn), std::forward(args)...) + ); + + std::future res = task->get_future(); + { + std::unique_lock lock(_lock); + + if (false == _running) { + throw std::runtime_error("Enqueue on stopped scheduler."); + } + + _tasks.emplace([task]() { (*task)(); }); + } + + _cv.notify_one(); + return res; + } + + private: + void ProcessWork() { + for (;;) { + std::function work; + + { + std::unique_lock lock(_lock); + _cv.wait(lock, [this] { return !_running || !_tasks.empty(); }); + + if (false == _running) { + return; + } + + work = std::move(_tasks.front()); + _tasks.pop(); + } + + work(); + } + + } + + bool _running = true; + std::vector _threads; + std::mutex _lock; + std::condition_variable _cv; + std::queue> _tasks; + }; + } +} diff --git a/common/guild_base.cpp b/common/guild_base.cpp index 8cf1b0fa8..642db7cbc 100644 --- a/common/guild_base.cpp +++ b/common/guild_base.cpp @@ -46,7 +46,7 @@ bool BaseGuildManager::LoadGuilds() { ClearGuilds(); if(m_db == nullptr) { - Log(Logs::Detail, Logs::Guilds, "Requested to load guilds when we have no database object."); + LogGuilds("Requested to load guilds when we have no database object"); return(false); } @@ -77,13 +77,13 @@ bool BaseGuildManager::LoadGuilds() { uint8 rankn = atoi(row[1]); if(rankn > GUILD_MAX_RANK) { - Log(Logs::Detail, Logs::Guilds, "Found invalid (too high) rank %d for guild %d, skipping.", rankn, guild_id); + LogGuilds("Found invalid (too high) rank [{}] for guild [{}], skipping", rankn, guild_id); continue; } res = m_guilds.find(guild_id); if(res == m_guilds.end()) { - Log(Logs::Detail, Logs::Guilds, "Found rank %d for non-existent guild %d, skipping.", rankn, guild_id); + LogGuilds("Found rank [{}] for non-existent guild [{}], skipping", rankn, guild_id); continue; } @@ -105,7 +105,7 @@ bool BaseGuildManager::LoadGuilds() { bool BaseGuildManager::RefreshGuild(uint32 guild_id) { if(m_db == nullptr) { - Log(Logs::Detail, Logs::Guilds, "Requested to refresh guild %d when we have no database object.", guild_id); + LogGuilds("Requested to refresh guild [{}] when we have no database object", guild_id); return(false); } @@ -123,7 +123,7 @@ bool BaseGuildManager::RefreshGuild(uint32 guild_id) { if (results.RowCount() == 0) { - Log(Logs::Detail, Logs::Guilds, "Unable to find guild %d in the database.", guild_id); + LogGuilds("Unable to find guild [{}] in the database", guild_id); return false; } @@ -145,7 +145,7 @@ bool BaseGuildManager::RefreshGuild(uint32 guild_id) { uint8 rankn = atoi(row[1]); if(rankn > GUILD_MAX_RANK) { - Log(Logs::Detail, Logs::Guilds, "Found invalid (too high) rank %d for guild %d, skipping.", rankn, guild_id); + LogGuilds("Found invalid (too high) rank [{}] for guild [{}], skipping", rankn, guild_id); continue; } @@ -162,7 +162,7 @@ bool BaseGuildManager::RefreshGuild(uint32 guild_id) { rank.permissions[GUILD_WARPEACE] = (row[10][0] == '1') ? true: false; } - Log(Logs::Detail, Logs::Guilds, "Successfully refreshed guild %d from the database.", guild_id); + LogGuilds("Successfully refreshed guild [{}] from the database", guild_id); return true; } @@ -214,14 +214,14 @@ BaseGuildManager::GuildInfo *BaseGuildManager::_CreateGuild(uint32 guild_id, con bool BaseGuildManager::_StoreGuildDB(uint32 guild_id) { if(m_db == nullptr) { - Log(Logs::Detail, Logs::Guilds, "Requested to store guild %d when we have no database object.", guild_id); + LogGuilds("Requested to store guild [{}] when we have no database object", guild_id); return(false); } std::map::const_iterator res; res = m_guilds.find(guild_id); if(res == m_guilds.end()) { - Log(Logs::Detail, Logs::Guilds, "Requested to store non-existent guild %d", guild_id); + LogGuilds("Requested to store non-existent guild [{}]", guild_id); return(false); } GuildInfo *info = res->second; @@ -289,14 +289,14 @@ bool BaseGuildManager::_StoreGuildDB(uint32 guild_id) { safe_delete_array(title_esc); } - Log(Logs::Detail, Logs::Guilds, "Stored guild %d in the database", guild_id); + LogGuilds("Stored guild [{}] in the database", guild_id); return true; } uint32 BaseGuildManager::_GetFreeGuildID() { if(m_db == nullptr) { - Log(Logs::Detail, Logs::Guilds, "Requested find a free guild ID when we have no database object."); + LogGuilds("Requested find a free guild ID when we have no database object"); return(GUILD_NONE); } @@ -330,12 +330,12 @@ uint32 BaseGuildManager::_GetFreeGuildID() { if (results.RowCount() == 0) { - Log(Logs::Detail, Logs::Guilds, "Located free guild ID %d in the database", index); + LogGuilds("Located free guild ID [{}] in the database", index); return index; } } - Log(Logs::Detail, Logs::Guilds, "Unable to find a free guild ID when requested."); + LogGuilds("Unable to find a free guild ID when requested"); return GUILD_NONE; } @@ -505,11 +505,11 @@ uint32 BaseGuildManager::DBCreateGuild(const char* name, uint32 leader) { //now store the resulting guild setup into the DB. if(!_StoreGuildDB(new_id)) { - Log(Logs::Detail, Logs::Guilds, "Error storing new guild. It may have been partially created which may need manual removal."); + LogGuilds("Error storing new guild. It may have been partially created which may need manual removal"); return(GUILD_NONE); } - Log(Logs::Detail, Logs::Guilds, "Created guild %d in the database.", new_id); + LogGuilds("Created guild [{}] in the database", new_id); return(new_id); } @@ -525,7 +525,7 @@ bool BaseGuildManager::DBDeleteGuild(uint32 guild_id) { } if(m_db == nullptr) { - Log(Logs::Detail, Logs::Guilds, "Requested to delete guild %d when we have no database object.", guild_id); + LogGuilds("Requested to delete guild [{}] when we have no database object", guild_id); return(false); } @@ -545,14 +545,14 @@ bool BaseGuildManager::DBDeleteGuild(uint32 guild_id) { query = StringFormat("DELETE FROM guild_bank WHERE guildid=%lu", (unsigned long)guild_id); QueryWithLogging(query, "deleting guild bank"); - Log(Logs::Detail, Logs::Guilds, "Deleted guild %d from the database.", guild_id); + LogGuilds("Deleted guild [{}] from the database", guild_id); return(true); } bool BaseGuildManager::DBRenameGuild(uint32 guild_id, const char* name) { if(m_db == nullptr) { - Log(Logs::Detail, Logs::Guilds, "Requested to rename guild %d when we have no database object.", guild_id); + LogGuilds("Requested to rename guild [{}] when we have no database object", guild_id); return false; } @@ -573,13 +573,13 @@ bool BaseGuildManager::DBRenameGuild(uint32 guild_id, const char* name) { if (!results.Success()) { - Log(Logs::Detail, Logs::Guilds, "Error renaming guild %d '%s': %s", guild_id, query.c_str(), results.Success()); + LogGuilds("Error renaming guild [{}] [{}]: [{}]", guild_id, query.c_str(), results.Success()); safe_delete_array(esc); return false; } safe_delete_array(esc); - Log(Logs::Detail, Logs::Guilds, "Renamed guild %s (%d) to %s in database.", info->name.c_str(), guild_id, name); + LogGuilds("Renamed guild [{}] ([{}]) to [{}] in database", info->name.c_str(), guild_id, name); info->name = name; //update our local record. @@ -588,7 +588,7 @@ bool BaseGuildManager::DBRenameGuild(uint32 guild_id, const char* name) { bool BaseGuildManager::DBSetGuildLeader(uint32 guild_id, uint32 leader) { if(m_db == nullptr) { - Log(Logs::Detail, Logs::Guilds, "Requested to set the leader for guild %d when we have no database object.", guild_id); + LogGuilds("Requested to set the leader for guild [{}] when we have no database object", guild_id); return false; } @@ -614,7 +614,7 @@ bool BaseGuildManager::DBSetGuildLeader(uint32 guild_id, uint32 leader) { if(!DBSetGuildRank(leader, GUILD_LEADER)) return false; - Log(Logs::Detail, Logs::Guilds, "Set guild leader for guild %d to %d in the database", guild_id, leader); + LogGuilds("Set guild leader for guild [{}] to [{}] in the database", guild_id, leader); info->leader_char_id = leader; //update our local record. @@ -623,7 +623,7 @@ bool BaseGuildManager::DBSetGuildLeader(uint32 guild_id, uint32 leader) { bool BaseGuildManager::DBSetGuildMOTD(uint32 guild_id, const char* motd, const char *setter) { if(m_db == nullptr) { - Log(Logs::Detail, Logs::Guilds, "Requested to set the MOTD for guild %d when we have no database object.", guild_id); + LogGuilds("Requested to set the MOTD for guild [{}] when we have no database object", guild_id); return(false); } @@ -654,7 +654,7 @@ bool BaseGuildManager::DBSetGuildMOTD(uint32 guild_id, const char* motd, const c safe_delete_array(esc); safe_delete_array(esc_set); - Log(Logs::Detail, Logs::Guilds, "Set MOTD for guild %d in the database", guild_id); + LogGuilds("Set MOTD for guild [{}] in the database", guild_id); info->motd = motd; //update our local record. info->motd_setter = setter; //update our local record. @@ -688,7 +688,7 @@ bool BaseGuildManager::DBSetGuildURL(uint32 GuildID, const char* URL) } safe_delete_array(esc); - Log(Logs::Detail, Logs::Guilds, "Set URL for guild %d in the database", GuildID); + LogGuilds("Set URL for guild [{}] in the database", GuildID); info->url = URL; //update our local record. @@ -722,7 +722,7 @@ bool BaseGuildManager::DBSetGuildChannel(uint32 GuildID, const char* Channel) } safe_delete_array(esc); - Log(Logs::Detail, Logs::Guilds, "Set Channel for guild %d in the database", GuildID); + LogGuilds("Set Channel for guild [{}] in the database", GuildID); info->channel = Channel; //update our local record. @@ -731,7 +731,7 @@ bool BaseGuildManager::DBSetGuildChannel(uint32 GuildID, const char* Channel) bool BaseGuildManager::DBSetGuild(uint32 charid, uint32 guild_id, uint8 rank) { if(m_db == nullptr) { - Log(Logs::Detail, Logs::Guilds, "Requested to set char to guild %d when we have no database object.", guild_id); + LogGuilds("Requested to set char to guild [{}] when we have no database object", guild_id); return(false); } @@ -753,7 +753,7 @@ bool BaseGuildManager::DBSetGuild(uint32 charid, uint32 guild_id, uint8 rank) { return false; } } - Log(Logs::Detail, Logs::Guilds, "Set char %d to guild %d and rank %d in the database.", charid, guild_id, rank); + LogGuilds("Set char [{}] to guild [{}] and rank [{}] in the database", charid, guild_id, rank); return true; } @@ -845,7 +845,7 @@ bool BaseGuildManager::DBSetPublicNote(uint32 charid, const char* note) { return false; } - Log(Logs::Detail, Logs::Guilds, "Set public not for char %d", charid); + LogGuilds("Set public not for char [{}]", charid); return true; } @@ -924,14 +924,14 @@ bool BaseGuildManager::GetEntireGuild(uint32 guild_id, std::vector::const_iterator res; res = m_guilds.find(guild_id); if(res == m_guilds.end()) { - Log(Logs::Detail, Logs::Guilds, "Check leader for char %d: invalid guild.", char_id); + LogGuilds("Check leader for char [{}]: invalid guild", char_id); return(false); //invalid guild } - Log(Logs::Detail, Logs::Guilds, "Check leader for guild %d, char %d: leader id=%d", guild_id, char_id, res->second->leader_char_id); + LogGuilds("Check leader for guild [{}], char [{}]: leader id=[{}]", guild_id, char_id, res->second->leader_char_id); return(char_id == res->second->leader_char_id); } @@ -1137,20 +1137,20 @@ uint8 BaseGuildManager::GetDisplayedRank(uint32 guild_id, uint8 rank, uint32 cha bool BaseGuildManager::CheckGMStatus(uint32 guild_id, uint8 status) const { if(status >= 250) { - Log(Logs::Detail, Logs::Guilds, "Check permission on guild %d with user status %d > 250, granted.", guild_id, status); + LogGuilds("Check permission on guild [{}] with user status [{}] > 250, granted", guild_id, status); return(true); //250+ as allowed anything } std::map::const_iterator res; res = m_guilds.find(guild_id); if(res == m_guilds.end()) { - Log(Logs::Detail, Logs::Guilds, "Check permission on guild %d with user status %d, no such guild, denied.", guild_id, status); + LogGuilds("Check permission on guild [{}] with user status [{}], no such guild, denied", guild_id, status); return(false); //invalid guild } bool granted = (res->second->minstatus <= status); - Log(Logs::Detail, Logs::Guilds, "Check permission on guild %s (%d) with user status %d. Min status %d: %s", + LogGuilds("Check permission on guild [{}] ([{}]) with user status [{}]. Min status [{}]: [{}]", res->second->name.c_str(), guild_id, status, res->second->minstatus, granted?"granted":"denied"); return(granted); @@ -1158,21 +1158,21 @@ bool BaseGuildManager::CheckGMStatus(uint32 guild_id, uint8 status) const { bool BaseGuildManager::CheckPermission(uint32 guild_id, uint8 rank, GuildAction act) const { if(rank > GUILD_MAX_RANK) { - Log(Logs::Detail, Logs::Guilds, "Check permission on guild %d and rank %d for action %s (%d): Invalid rank, denied.", + LogGuilds("Check permission on guild [{}] and rank [{}] for action [{}] ([{}]): Invalid rank, denied", guild_id, rank, GuildActionNames[act], act); return(false); //invalid rank } std::map::const_iterator res; res = m_guilds.find(guild_id); if(res == m_guilds.end()) { - Log(Logs::Detail, Logs::Guilds, "Check permission on guild %d and rank %d for action %s (%d): Invalid guild, denied.", + LogGuilds("Check permission on guild [{}] and rank [{}] for action [{}] ([{}]): Invalid guild, denied", guild_id, rank, GuildActionNames[act], act); return(false); //invalid guild } bool granted = res->second->ranks[rank].permissions[act]; - Log(Logs::Detail, Logs::Guilds, "Check permission on guild %s (%d) and rank %s (%d) for action %s (%d): %s", + LogGuilds("Check permission on guild [{}] ([{}]) and rank [{}] ([{}]) for action [{}] ([{}]): [{}]", res->second->name.c_str(), guild_id, res->second->ranks[rank].name.c_str(), rank, GuildActionNames[act], act, diff --git a/common/http/httplib.h b/common/http/httplib.h new file mode 100644 index 000000000..827d5f1e0 --- /dev/null +++ b/common/http/httplib.h @@ -0,0 +1,2694 @@ +// +// httplib.h +// +// Copyright (c) 2019 Yuji Hirose. All rights reserved. +// MIT License +// + +#ifndef CPPHTTPLIB_HTTPLIB_H +#define CPPHTTPLIB_HTTPLIB_H + +#ifdef _WIN32 +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif //_CRT_SECURE_NO_WARNINGS + +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif //_CRT_NONSTDC_NO_DEPRECATE + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf_s +#endif // _MSC_VER + +#ifndef S_ISREG +#define S_ISREG(m) (((m)&S_IFREG) == S_IFREG) +#endif // S_ISREG + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR) +#endif // S_ISDIR + +#ifndef NOMINMAX +#define NOMINMAX +#endif // NOMINMAX + +#include +#include +#include + +#pragma comment(lib, "ws2_32.lib") + +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif // strcasecmp + +typedef SOCKET socket_t; +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef int socket_t; +#define INVALID_SOCKET (-1) +#endif //_WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +#include +#include +#include + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) { + return M_ASN1_STRING_data(asn1); +} +#endif +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +#include +#endif + +/* + * Configuration + */ +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0 +#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 +#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 +#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 +#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 +#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits::max)() +#define CPPHTTPLIB_RECV_BUFSIZ 4096 + +namespace httplib { + + namespace detail { + + struct ci { + bool operator()(const std::string &s1, const std::string &s2) const { + return std::lexicographical_compare( + s1.begin(), s1.end(), s2.begin(), s2.end(), + [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); }); + } + }; + + } // namespace detail + + enum class HttpVersion { v1_0 = 0, v1_1 }; + + typedef std::multimap Headers; + + template + std::pair make_range_header(uint64_t value, + Args... args); + + typedef std::multimap Params; + typedef std::smatch Match; + typedef std::function Progress; + + struct MultipartFile { + std::string filename; + std::string content_type; + size_t offset = 0; + size_t length = 0; + }; + typedef std::multimap MultipartFiles; + + struct Request { + std::string version; + std::string method; + std::string target; + std::string path; + Headers headers; + std::string body; + Params params; + MultipartFiles files; + Match matches; + + Progress progress; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + const SSL *ssl; +#endif + + bool has_header(const char *key) const; + std::string get_header_value(const char *key, size_t id = 0) const; + size_t get_header_value_count(const char *key) const; + void set_header(const char *key, const char *val); + + bool has_param(const char *key) const; + std::string get_param_value(const char *key, size_t id = 0) const; + size_t get_param_value_count(const char *key) const; + + bool has_file(const char *key) const; + MultipartFile get_file_value(const char *key) const; + }; + + struct Response { + std::string version; + int status; + Headers headers; + std::string body; + std::function streamcb; + + bool has_header(const char *key) const; + std::string get_header_value(const char *key, size_t id = 0) const; + size_t get_header_value_count(const char *key) const; + void set_header(const char *key, const char *val); + + void set_redirect(const char *uri); + void set_content(const char *s, size_t n, const char *content_type); + void set_content(const std::string &s, const char *content_type); + + Response() : status(-1) {} + }; + + class Stream { + public: + virtual ~Stream() {} + virtual int read(char *ptr, size_t size) = 0; + virtual int write(const char *ptr, size_t size1) = 0; + virtual int write(const char *ptr) = 0; + virtual std::string get_remote_addr() const = 0; + + template + void write_format(const char *fmt, const Args &... args); + }; + + class SocketStream : public Stream { + public: + SocketStream(socket_t sock); + virtual ~SocketStream(); + + virtual int read(char *ptr, size_t size); + virtual int write(const char *ptr, size_t size); + virtual int write(const char *ptr); + virtual std::string get_remote_addr() const; + + private: + socket_t sock_; + }; + + class BufferStream : public Stream { + public: + BufferStream() {} + virtual ~BufferStream() {} + + virtual int read(char *ptr, size_t size); + virtual int write(const char *ptr, size_t size); + virtual int write(const char *ptr); + virtual std::string get_remote_addr() const; + + const std::string &get_buffer() const; + + private: + std::string buffer; + }; + + class Server { + public: + typedef std::function Handler; + typedef std::function Logger; + + Server(); + + virtual ~Server(); + + virtual bool is_valid() const; + + Server &Get(const char *pattern, Handler handler); + Server &Post(const char *pattern, Handler handler); + + Server &Put(const char *pattern, Handler handler); + Server &Patch(const char *pattern, Handler handler); + Server &Delete(const char *pattern, Handler handler); + Server &Options(const char *pattern, Handler handler); + + bool set_base_dir(const char *path); + + void set_error_handler(Handler handler); + void set_logger(Logger logger); + + void set_keep_alive_max_count(size_t count); + void set_payload_max_length(uint64_t length); + + int bind_to_any_port(const char *host, int socket_flags = 0); + + bool bind(const char *host, int port, int socket_flags = 0) + { + if (bind_internal(host, port, socket_flags) < 0) return false; + + return true; + } + + bool poll(); + + bool listen_after_bind(); + + bool listen(const char *host, int port, int socket_flags = 0); + + bool is_running() const; + void stop(); + + protected: + bool process_request(Stream &strm, bool last_connection, + bool &connection_close, + std::function setup_request = nullptr); + + size_t keep_alive_max_count_; + size_t payload_max_length_; + + private: + typedef std::vector> Handlers; + + socket_t create_server_socket(const char *host, int port, + int socket_flags) const; + int bind_internal(const char *host, int port, int socket_flags); + bool listen_internal(); + + bool routing(Request &req, Response &res); + bool handle_file_request(Request &req, Response &res); + bool dispatch_request(Request &req, Response &res, Handlers &handlers); + + bool parse_request_line(const char *s, Request &req); + void write_response(Stream &strm, bool last_connection, const Request &req, + Response &res); + + virtual bool read_and_close_socket(socket_t sock); + + std::atomic is_running_; + std::atomic svr_sock_; + std::string base_dir_; + Handlers get_handlers_; + Handlers post_handlers_; + Handlers put_handlers_; + Handlers patch_handlers_; + Handlers delete_handlers_; + Handlers options_handlers_; + Handler error_handler_; + Logger logger_; + + // TODO: Use thread pool... + std::mutex running_threads_mutex_; + int running_threads_; + }; + + class Client { + public: + Client(const char *host, int port = 80, time_t timeout_sec = 300); + + virtual ~Client(); + + virtual bool is_valid() const; + + std::shared_ptr Get(const char *path, Progress progress = nullptr); + std::shared_ptr Get(const char *path, const Headers &headers, + Progress progress = nullptr); + + std::shared_ptr Head(const char *path); + std::shared_ptr Head(const char *path, const Headers &headers); + + std::shared_ptr Post(const char *path, const std::string &body, + const char *content_type); + std::shared_ptr Post(const char *path, const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Post(const char *path, const Params ¶ms); + std::shared_ptr Post(const char *path, const Headers &headers, + const Params ¶ms); + + std::shared_ptr Put(const char *path, const std::string &body, + const char *content_type); + std::shared_ptr Put(const char *path, const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Patch(const char *path, const std::string &body, + const char *content_type); + std::shared_ptr Patch(const char *path, const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Delete(const char *path, + const std::string &body = std::string(), + const char *content_type = nullptr); + std::shared_ptr Delete(const char *path, const Headers &headers, + const std::string &body = std::string(), + const char *content_type = nullptr); + + std::shared_ptr Options(const char *path); + std::shared_ptr Options(const char *path, const Headers &headers); + + bool send(Request &req, Response &res); + + protected: + bool process_request(Stream &strm, Request &req, Response &res, + bool &connection_close); + + const std::string host_; + const int port_; + time_t timeout_sec_; + const std::string host_and_port_; + + private: + socket_t create_client_socket() const; + bool read_response_line(Stream &strm, Response &res); + void write_request(Stream &strm, Request &req); + + virtual bool read_and_close_socket(socket_t sock, Request &req, + Response &res); + virtual bool is_ssl() const; + }; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + class SSLSocketStream : public Stream { +public: + SSLSocketStream(socket_t sock, SSL *ssl); + virtual ~SSLSocketStream(); + + virtual int read(char *ptr, size_t size); + virtual int write(const char *ptr, size_t size); + virtual int write(const char *ptr); + virtual std::string get_remote_addr() const; + +private: + socket_t sock_; + SSL *ssl_; +}; + +class SSLServer : public Server { +public: + SSLServer(const char *cert_path, const char *private_key_path, + const char *client_ca_cert_file_path = nullptr, + const char *client_ca_cert_dir_path = nullptr); + + virtual ~SSLServer(); + + virtual bool is_valid() const; + +private: + virtual bool read_and_close_socket(socket_t sock); + + SSL_CTX *ctx_; + std::mutex ctx_mutex_; +}; + +class SSLClient : public Client { +public: + SSLClient(const char *host, int port = 443, time_t timeout_sec = 300, + const char *client_cert_path = nullptr, + const char *client_key_path = nullptr); + + virtual ~SSLClient(); + + virtual bool is_valid() const; + + void set_ca_cert_path(const char *ca_ceert_file_path, + const char *ca_cert_dir_path = nullptr); + void enable_server_certificate_verification(bool enabled); + + long get_openssl_verify_result() const; + +private: + virtual bool read_and_close_socket(socket_t sock, Request &req, + Response &res); + virtual bool is_ssl() const; + + bool verify_host(X509 *server_cert) const; + bool verify_host_with_subject_alt_name(X509 *server_cert) const; + bool verify_host_with_common_name(X509 *server_cert) const; + bool check_host_name(const char *pattern, size_t pattern_len) const; + + SSL_CTX *ctx_; + std::mutex ctx_mutex_; + std::vector host_components_; + std::string ca_cert_file_path_; + std::string ca_cert_dir_path_; + bool server_certificate_verification_ = false; + long verify_result_ = 0; +}; +#endif + +/* + * Implementation + */ + namespace detail { + + template void split(const char *b, const char *e, char d, Fn fn) { + int i = 0; + int beg = 0; + + while (e ? (b + i != e) : (b[i] != '\0')) { + if (b[i] == d) { + fn(&b[beg], &b[i]); + beg = i + 1; + } + i++; + } + + if (i) { fn(&b[beg], &b[i]); } + } + +// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` +// to store data. The call can set memory on stack for performance. + class stream_line_reader { + public: + stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size) + : strm_(strm), fixed_buffer_(fixed_buffer), + fixed_buffer_size_(fixed_buffer_size) {} + + const char *ptr() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_; + } else { + return glowable_buffer_.data(); + } + } + + size_t size() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_used_size_; + } else { + return glowable_buffer_.size(); + } + } + + bool getline() { + fixed_buffer_used_size_ = 0; + glowable_buffer_.clear(); + + for (size_t i = 0;; i++) { + char byte; + auto n = strm_.read(&byte, 1); + + if (n < 0) { + return false; + } else if (n == 0) { + if (i == 0) { + return false; + } else { + break; + } + } + + append(byte); + + if (byte == '\n') { break; } + } + + return true; + } + + private: + void append(char c) { + if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { + fixed_buffer_[fixed_buffer_used_size_++] = c; + fixed_buffer_[fixed_buffer_used_size_] = '\0'; + } else { + if (glowable_buffer_.empty()) { + assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); + glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); + } + glowable_buffer_ += c; + } + } + + Stream &strm_; + char *fixed_buffer_; + const size_t fixed_buffer_size_; + size_t fixed_buffer_used_size_; + std::string glowable_buffer_; + }; + + inline int close_socket(socket_t sock) { +#ifdef _WIN32 + return closesocket(sock); +#else + return close(sock); +#endif + } + + inline int select_read(socket_t sock, time_t sec, time_t usec) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); + } + + inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { + fd_set fdsr; + FD_ZERO(&fdsr); + FD_SET(sock, &fdsr); + + auto fdsw = fdsr; + auto fdse = fdsr; + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + if (select(static_cast(sock + 1), &fdsr, &fdsw, &fdse, &tv) < 0) { + return false; + } else if (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw)) { + int error = 0; + socklen_t len = sizeof(error); + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, &len) < 0 || + error) { + return false; + } + } else { + return false; + } + + return true; + } + + template + inline bool read_and_close_socket(socket_t sock, size_t keep_alive_max_count, + T callback) { + bool ret = false; + + if (keep_alive_max_count > 0) { + auto count = keep_alive_max_count; + while (count > 0 && + detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { + SocketStream strm(sock); + auto last_connection = count == 1; + auto connection_close = false; + + ret = callback(strm, last_connection, connection_close); + if (!ret || connection_close) { break; } + + count--; + } + } else { + SocketStream strm(sock); + auto dummy_connection_close = false; + ret = callback(strm, true, dummy_connection_close); + } + + close_socket(sock); + return ret; + } + + inline int shutdown_socket(socket_t sock) { +#ifdef _WIN32 + return shutdown(sock, SD_BOTH); +#else + return shutdown(sock, SHUT_RDWR); +#endif + } + + template + socket_t create_socket(const char *host, int port, Fn fn, + int socket_flags = 0) { +#ifdef _WIN32 + #define SO_SYNCHRONOUS_NONALERT 0x20 +#define SO_OPENTYPE 0x7008 + + int opt = SO_SYNCHRONOUS_NONALERT; + setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&opt, + sizeof(opt)); +#endif + + // Get address info + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = socket_flags; + hints.ai_protocol = 0; + + auto service = std::to_string(port); + + if (getaddrinfo(host, service.c_str(), &hints, &result)) { + return INVALID_SOCKET; + } + + for (auto rp = result; rp; rp = rp->ai_next) { + // Create a socket +#ifdef _WIN32 + auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, + nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT); +#else + auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); +#endif + if (sock == INVALID_SOCKET) { continue; } + +#ifndef _WIN32 + if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; } +#endif + + // Make 'reuse address' option available + int yes = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)); +#ifdef SO_REUSEPORT + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char *)&yes, sizeof(yes)); +#endif + + // bind or connect + if (fn(sock, *rp)) { + freeaddrinfo(result); + return sock; + } + + close_socket(sock); + } + + freeaddrinfo(result); + return INVALID_SOCKET; + } + + inline void set_nonblocking(socket_t sock, bool nonblocking) { +#ifdef _WIN32 + auto flags = nonblocking ? 1UL : 0UL; + ioctlsocket(sock, FIONBIO, &flags); +#else + auto flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, + nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); +#endif + } + + inline bool is_connection_error() { +#ifdef _WIN32 + return WSAGetLastError() != WSAEWOULDBLOCK; +#else + return errno != EINPROGRESS; +#endif + } + + inline std::string get_remote_addr(socket_t sock) { + struct sockaddr_storage addr; + socklen_t len = sizeof(addr); + + if (!getpeername(sock, (struct sockaddr *)&addr, &len)) { + char ipstr[NI_MAXHOST]; + + if (!getnameinfo((struct sockaddr *)&addr, len, ipstr, sizeof(ipstr), + nullptr, 0, NI_NUMERICHOST)) { + return ipstr; + } + } + + return std::string(); + } + + inline bool is_file(const std::string &path) { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode); + } + + inline bool is_dir(const std::string &path) { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode); + } + + inline bool is_valid_path(const std::string &path) { + size_t level = 0; + size_t i = 0; + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + + while (i < path.size()) { + // Read component + auto beg = i; + while (i < path.size() && path[i] != '/') { + i++; + } + + auto len = i - beg; + assert(len > 0); + + if (!path.compare(beg, len, ".")) { + ; + } else if (!path.compare(beg, len, "..")) { + if (level == 0) { return false; } + level--; + } else { + level++; + } + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + } + + return true; + } + + inline void read_file(const std::string &path, std::string &out) { + std::ifstream fs(path, std::ios_base::binary); + fs.seekg(0, std::ios_base::end); + auto size = fs.tellg(); + fs.seekg(0); + out.resize(static_cast(size)); + fs.read(&out[0], size); + } + + inline std::string file_extension(const std::string &path) { + std::smatch m; + auto pat = std::regex("\\.([a-zA-Z0-9]+)$"); + if (std::regex_search(path, m, pat)) { return m[1].str(); } + return std::string(); + } + + inline const char *find_content_type(const std::string &path) { + auto ext = file_extension(path); + if (ext == "txt") { + return "text/plain"; + } else if (ext == "html") { + return "text/html"; + } else if (ext == "css") { + return "text/css"; + } else if (ext == "jpeg" || ext == "jpg") { + return "image/jpg"; + } else if (ext == "png") { + return "image/png"; + } else if (ext == "gif") { + return "image/gif"; + } else if (ext == "svg") { + return "image/svg+xml"; + } else if (ext == "ico") { + return "image/x-icon"; + } else if (ext == "json") { + return "application/json"; + } else if (ext == "pdf") { + return "application/pdf"; + } else if (ext == "js") { + return "application/javascript"; + } else if (ext == "xml") { + return "application/xml"; + } else if (ext == "xhtml") { + return "application/xhtml+xml"; + } + return nullptr; + } + + inline const char *status_message(int status) { + switch (status) { + case 200: return "OK"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 400: return "Bad Request"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 413: return "Payload Too Large"; + case 414: return "Request-URI Too Long"; + case 415: return "Unsupported Media Type"; + default: + case 500: return "Internal Server Error"; + } + } + + inline bool has_header(const Headers &headers, const char *key) { + return headers.find(key) != headers.end(); + } + + inline const char *get_header_value(const Headers &headers, const char *key, + size_t id = 0, const char *def = nullptr) { + auto it = headers.find(key); + std::advance(it, id); + if (it != headers.end()) { return it->second.c_str(); } + return def; + } + + inline uint64_t get_header_value_uint64(const Headers &headers, const char *key, + int def = 0) { + auto it = headers.find(key); + if (it != headers.end()) { + return std::strtoull(it->second.data(), nullptr, 10); + } + return def; + } + + inline bool read_headers(Stream &strm, Headers &headers) { + static std::regex re(R"((.+?):\s*(.+?)\s*\r\n)"); + + const auto bufsiz = 2048; + char buf[bufsiz]; + + stream_line_reader reader(strm, buf, bufsiz); + + for (;;) { + if (!reader.getline()) { return false; } + if (!strcmp(reader.ptr(), "\r\n")) { break; } + std::cmatch m; + if (std::regex_match(reader.ptr(), m, re)) { + auto key = std::string(m[1]); + auto val = std::string(m[2]); + headers.emplace(key, val); + } + } + + return true; + } + + inline bool read_content_with_length(Stream &strm, std::string &out, size_t len, + Progress progress) { + out.assign(len, 0); + size_t r = 0; + while (r < len) { + auto n = strm.read(&out[r], len - r); + if (n <= 0) { return false; } + + r += n; + + if (progress) { + if (!progress(r, len)) { return false; } + } + } + + return true; + } + + inline void skip_content_with_length(Stream &strm, size_t len) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + size_t r = 0; + while (r < len) { + auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); + if (n <= 0) { return; } + r += n; + } + } + + inline bool read_content_without_length(Stream &strm, std::string &out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + for (;;) { + auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); + if (n < 0) { + return false; + } else if (n == 0) { + return true; + } + out.append(buf, n); + } + + return true; + } + + inline bool read_content_chunked(Stream &strm, std::string &out) { + const auto bufsiz = 16; + char buf[bufsiz]; + + stream_line_reader reader(strm, buf, bufsiz); + + if (!reader.getline()) { return false; } + + auto chunk_len = std::stoi(reader.ptr(), 0, 16); + + while (chunk_len > 0) { + std::string chunk; + if (!read_content_with_length(strm, chunk, chunk_len, nullptr)) { + return false; + } + + if (!reader.getline()) { return false; } + + if (strcmp(reader.ptr(), "\r\n")) { break; } + + out += chunk; + + if (!reader.getline()) { return false; } + + chunk_len = std::stoi(reader.ptr(), 0, 16); + } + + if (chunk_len == 0) { + // Reader terminator after chunks + if (!reader.getline() || strcmp(reader.ptr(), "\r\n")) return false; + } + + return true; + } + + template + bool read_content(Stream &strm, T &x, uint64_t payload_max_length, + bool &exceed_payload_max_length, + Progress progress = Progress()) { + if (has_header(x.headers, "Content-Length")) { + auto len = get_header_value_uint64(x.headers, "Content-Length", 0); + if (len == 0) { + const auto &encoding = + get_header_value(x.headers, "Transfer-Encoding", 0, ""); + if (!strcasecmp(encoding, "chunked")) { + return read_content_chunked(strm, x.body); + } + } + + if ((len > payload_max_length) || + // For 32-bit platform + (sizeof(size_t) < sizeof(uint64_t) && + len > std::numeric_limits::max())) { + exceed_payload_max_length = true; + skip_content_with_length(strm, len); + return false; + } + + return read_content_with_length(strm, x.body, len, progress); + } else { + const auto &encoding = + get_header_value(x.headers, "Transfer-Encoding", 0, ""); + if (!strcasecmp(encoding, "chunked")) { + return read_content_chunked(strm, x.body); + } + return read_content_without_length(strm, x.body); + } + return true; + } + + template inline void write_headers(Stream &strm, const T &info) { + for (const auto &x : info.headers) { + strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + } + strm.write("\r\n"); + } + + inline std::string encode_url(const std::string &s) { + std::string result; + + for (auto i = 0; s[i]; i++) { + switch (s[i]) { + case ' ': result += "%20"; break; + case '+': result += "%2B"; break; + case '\r': result += "%0D"; break; + case '\n': result += "%0A"; break; + case '\'': result += "%27"; break; + case ',': result += "%2C"; break; + case ':': result += "%3A"; break; + case ';': result += "%3B"; break; + default: + auto c = static_cast(s[i]); + if (c >= 0x80) { + result += '%'; + char hex[4]; + size_t len = snprintf(hex, sizeof(hex) - 1, "%02X", c); + assert(len == 2); + result.append(hex, len); + } else { + result += s[i]; + } + break; + } + } + + return result; + } + + inline bool is_hex(char c, int &v) { + if (0x20 <= c && isdigit(c)) { + v = c - '0'; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; + return true; + } + return false; + } + + inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, + int &val) { + if (i >= s.size()) { return false; } + + val = 0; + for (; cnt; i++, cnt--) { + if (!s[i]) { return false; } + int v = 0; + if (is_hex(s[i], v)) { + val = val * 16 + v; + } else { + return false; + } + } + return true; + } + + inline std::string from_i_to_hex(uint64_t n) { + const char *charset = "0123456789abcdef"; + std::string ret; + do { + ret = charset[n & 15] + ret; + n >>= 4; + } while (n > 0); + return ret; + } + + inline size_t to_utf8(int code, char *buff) { + if (code < 0x0080) { + buff[0] = (code & 0x7F); + return 1; + } else if (code < 0x0800) { + buff[0] = (0xC0 | ((code >> 6) & 0x1F)); + buff[1] = (0x80 | (code & 0x3F)); + return 2; + } else if (code < 0xD800) { + buff[0] = (0xE0 | ((code >> 12) & 0xF)); + buff[1] = (0x80 | ((code >> 6) & 0x3F)); + buff[2] = (0x80 | (code & 0x3F)); + return 3; + } else if (code < 0xE000) { // D800 - DFFF is invalid... + return 0; + } else if (code < 0x10000) { + buff[0] = (0xE0 | ((code >> 12) & 0xF)); + buff[1] = (0x80 | ((code >> 6) & 0x3F)); + buff[2] = (0x80 | (code & 0x3F)); + return 3; + } else if (code < 0x110000) { + buff[0] = (0xF0 | ((code >> 18) & 0x7)); + buff[1] = (0x80 | ((code >> 12) & 0x3F)); + buff[2] = (0x80 | ((code >> 6) & 0x3F)); + buff[3] = (0x80 | (code & 0x3F)); + return 4; + } + + // NOTREACHED + return 0; + } + + inline std::string decode_url(const std::string &s) { + std::string result; + + for (size_t i = 0; i < s.size(); i++) { + if (s[i] == '%' && i + 1 < s.size()) { + if (s[i + 1] == 'u') { + int val = 0; + if (from_hex_to_i(s, i + 2, 4, val)) { + // 4 digits Unicode codes + char buff[4]; + size_t len = to_utf8(val, buff); + if (len > 0) { result.append(buff, len); } + i += 5; // 'u0000' + } else { + result += s[i]; + } + } else { + int val = 0; + if (from_hex_to_i(s, i + 1, 2, val)) { + // 2 digits hex codes + result += val; + i += 2; // '00' + } else { + result += s[i]; + } + } + } else if (s[i] == '+') { + result += ' '; + } else { + result += s[i]; + } + } + + return result; + } + + inline void parse_query_text(const std::string &s, Params ¶ms) { + split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) { + std::string key; + std::string val; + split(b, e, '=', [&](const char *b, const char *e) { + if (key.empty()) { + key.assign(b, e); + } else { + val.assign(b, e); + } + }); + params.emplace(key, decode_url(val)); + }); + } + + inline bool parse_multipart_boundary(const std::string &content_type, + std::string &boundary) { + auto pos = content_type.find("boundary="); + if (pos == std::string::npos) { return false; } + + boundary = content_type.substr(pos + 9); + return true; + } + + inline bool parse_multipart_formdata(const std::string &boundary, + const std::string &body, + MultipartFiles &files) { + static std::string dash = "--"; + static std::string crlf = "\r\n"; + + static std::regex re_content_type("Content-Type: (.*?)", + std::regex_constants::icase); + + static std::regex re_content_disposition( + "Content-Disposition: form-data; name=\"(.*?)\"(?:; filename=\"(.*?)\")?", + std::regex_constants::icase); + + auto dash_boundary = dash + boundary; + + auto pos = body.find(dash_boundary); + if (pos != 0) { return false; } + + pos += dash_boundary.size(); + + auto next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { return false; } + + pos = next_pos + crlf.size(); + + while (pos < body.size()) { + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { return false; } + + std::string name; + MultipartFile file; + + auto header = body.substr(pos, (next_pos - pos)); + + while (pos != next_pos) { + std::smatch m; + if (std::regex_match(header, m, re_content_type)) { + file.content_type = m[1]; + } else if (std::regex_match(header, m, re_content_disposition)) { + name = m[1]; + file.filename = m[2]; + } + + pos = next_pos + crlf.size(); + + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { return false; } + + header = body.substr(pos, (next_pos - pos)); + } + + pos = next_pos + crlf.size(); + + next_pos = body.find(crlf + dash_boundary, pos); + + if (next_pos == std::string::npos) { return false; } + + file.offset = pos; + file.length = next_pos - pos; + + pos = next_pos + crlf.size() + dash_boundary.size(); + + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { return false; } + + files.emplace(name, file); + + pos = next_pos + crlf.size(); + } + + return true; + } + + inline std::string to_lower(const char *beg, const char *end) { + std::string out; + auto it = beg; + while (it != end) { + out += ::tolower(*it); + it++; + } + return out; + } + + inline void make_range_header_core(std::string &) {} + + template + inline void make_range_header_core(std::string &field, uint64_t value) { + if (!field.empty()) { field += ", "; } + field += std::to_string(value) + "-"; + } + + template + inline void make_range_header_core(std::string &field, uint64_t value1, + uint64_t value2, Args... args) { + if (!field.empty()) { field += ", "; } + field += std::to_string(value1) + "-" + std::to_string(value2); + make_range_header_core(field, args...); + } + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + inline bool can_compress(const std::string &content_type) { + return !content_type.find("text/") || content_type == "image/svg+xml" || + content_type == "application/javascript" || + content_type == "application/json" || + content_type == "application/xml" || + content_type == "application/xhtml+xml"; +} + +inline void compress(std::string &content) { + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, + Z_DEFAULT_STRATEGY); + if (ret != Z_OK) { return; } + + strm.avail_in = content.size(); + strm.next_in = (Bytef *)content.data(); + + std::string compressed; + + const auto bufsiz = 16384; + char buff[bufsiz]; + do { + strm.avail_out = bufsiz; + strm.next_out = (Bytef *)buff; + deflate(&strm, Z_FINISH); + compressed.append(buff, bufsiz - strm.avail_out); + } while (strm.avail_out == 0); + + content.swap(compressed); + + deflateEnd(&strm); +} + +inline void decompress(std::string &content) { + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + // 15 is the value of wbits, which should be at the maximum possible value to + // ensure that any gzip stream can be decoded. The offset of 16 specifies that + // the stream to decompress will be formatted with a gzip wrapper. + auto ret = inflateInit2(&strm, 16 + 15); + if (ret != Z_OK) { return; } + + strm.avail_in = content.size(); + strm.next_in = (Bytef *)content.data(); + + std::string decompressed; + + const auto bufsiz = 16384; + char buff[bufsiz]; + do { + strm.avail_out = bufsiz; + strm.next_out = (Bytef *)buff; + inflate(&strm, Z_NO_FLUSH); + decompressed.append(buff, bufsiz - strm.avail_out); + } while (strm.avail_out == 0); + + content.swap(decompressed); + + inflateEnd(&strm); +} +#endif + +#ifdef _WIN32 + class WSInit { +public: + WSInit() { + WSADATA wsaData; + WSAStartup(0x0002, &wsaData); + } + + ~WSInit() { WSACleanup(); } +}; + +static WSInit wsinit_; +#endif + + } // namespace detail + +// Header utilities + template + inline std::pair make_range_header(uint64_t value, + Args... args) { + std::string field; + detail::make_range_header_core(field, value, args...); + field.insert(0, "bytes="); + return std::make_pair("Range", field); + } + +// Request implementation + inline bool Request::has_header(const char *key) const { + return detail::has_header(headers, key); + } + + inline std::string Request::get_header_value(const char *key, size_t id) const { + return detail::get_header_value(headers, key, id, ""); + } + + inline size_t Request::get_header_value_count(const char *key) const { + auto r = headers.equal_range(key); + return std::distance(r.first, r.second); + } + + inline void Request::set_header(const char *key, const char *val) { + headers.emplace(key, val); + } + + inline bool Request::has_param(const char *key) const { + return params.find(key) != params.end(); + } + + inline std::string Request::get_param_value(const char *key, size_t id) const { + auto it = params.find(key); + std::advance(it, id); + if (it != params.end()) { return it->second; } + return std::string(); + } + + inline size_t Request::get_param_value_count(const char *key) const { + auto r = params.equal_range(key); + return std::distance(r.first, r.second); + } + + inline bool Request::has_file(const char *key) const { + return files.find(key) != files.end(); + } + + inline MultipartFile Request::get_file_value(const char *key) const { + auto it = files.find(key); + if (it != files.end()) { return it->second; } + return MultipartFile(); + } + +// Response implementation + inline bool Response::has_header(const char *key) const { + return headers.find(key) != headers.end(); + } + + inline std::string Response::get_header_value(const char *key, + size_t id) const { + return detail::get_header_value(headers, key, id, ""); + } + + inline size_t Response::get_header_value_count(const char *key) const { + auto r = headers.equal_range(key); + return std::distance(r.first, r.second); + } + + inline void Response::set_header(const char *key, const char *val) { + headers.emplace(key, val); + } + + inline void Response::set_redirect(const char *url) { + set_header("Location", url); + status = 302; + } + + inline void Response::set_content(const char *s, size_t n, + const char *content_type) { + body.assign(s, n); + set_header("Content-Type", content_type); + } + + inline void Response::set_content(const std::string &s, + const char *content_type) { + body = s; + set_header("Content-Type", content_type); + } + +// Rstream implementation + template + inline void Stream::write_format(const char *fmt, const Args &... args) { + const auto bufsiz = 2048; + char buf[bufsiz]; + +#if defined(_MSC_VER) && _MSC_VER < 1900 + auto n = _snprintf_s(buf, bufsiz, bufsiz - 1, fmt, args...); +#else + auto n = snprintf(buf, bufsiz - 1, fmt, args...); +#endif + if (n > 0) { + if (n >= bufsiz - 1) { + std::vector glowable_buf(bufsiz); + + while (n >= static_cast(glowable_buf.size() - 1)) { + glowable_buf.resize(glowable_buf.size() * 2); +#if defined(_MSC_VER) && _MSC_VER < 1900 + n = _snprintf_s(&glowable_buf[0], glowable_buf.size(), + glowable_buf.size() - 1, fmt, args...); +#else + n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...); +#endif + } + write(&glowable_buf[0], n); + } else { + write(buf, n); + } + } + } + +// Socket stream implementation + inline SocketStream::SocketStream(socket_t sock) : sock_(sock) {} + + inline SocketStream::~SocketStream() {} + + inline int SocketStream::read(char *ptr, size_t size) { + if (detail::select_read(sock_, CPPHTTPLIB_READ_TIMEOUT_SECOND, + CPPHTTPLIB_READ_TIMEOUT_USECOND) > 0) { + return recv(sock_, ptr, static_cast(size), 0); + } + return -1; + } + + inline int SocketStream::write(const char *ptr, size_t size) { + return send(sock_, ptr, static_cast(size), 0); + } + + inline int SocketStream::write(const char *ptr) { + return write(ptr, strlen(ptr)); + } + + inline std::string SocketStream::get_remote_addr() const { + return detail::get_remote_addr(sock_); + } + +// Buffer stream implementation + inline int BufferStream::read(char *ptr, size_t size) { +#if defined(_MSC_VER) && _MSC_VER < 1900 + return static_cast(buffer._Copy_s(ptr, size, size)); +#else + return static_cast(buffer.copy(ptr, size)); +#endif + } + + inline int BufferStream::write(const char *ptr, size_t size) { + buffer.append(ptr, size); + return static_cast(size); + } + + inline int BufferStream::write(const char *ptr) { + size_t size = strlen(ptr); + buffer.append(ptr, size); + return static_cast(size); + } + + inline std::string BufferStream::get_remote_addr() const { return ""; } + + inline const std::string &BufferStream::get_buffer() const { return buffer; } + +// HTTP server implementation + inline Server::Server() + : keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), + payload_max_length_(CPPHTTPLIB_PAYLOAD_MAX_LENGTH), is_running_(false), + svr_sock_(INVALID_SOCKET), running_threads_(0) { +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif + } + + inline Server::~Server() {} + + inline Server &Server::Get(const char *pattern, Handler handler) { + get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; + } + + inline Server &Server::Post(const char *pattern, Handler handler) { + post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; + } + + inline Server &Server::Put(const char *pattern, Handler handler) { + put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; + } + + inline Server &Server::Patch(const char *pattern, Handler handler) { + patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; + } + + inline Server &Server::Delete(const char *pattern, Handler handler) { + delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; + } + + inline Server &Server::Options(const char *pattern, Handler handler) { + options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; + } + + inline bool Server::set_base_dir(const char *path) { + if (detail::is_dir(path)) { + base_dir_ = path; + return true; + } + return false; + } + + inline void Server::set_error_handler(Handler handler) { + error_handler_ = handler; + } + + inline void Server::set_logger(Logger logger) { logger_ = logger; } + + inline void Server::set_keep_alive_max_count(size_t count) { + keep_alive_max_count_ = count; + } + + inline void Server::set_payload_max_length(uint64_t length) { + payload_max_length_ = length; + } + + inline int Server::bind_to_any_port(const char *host, int socket_flags) { + return bind_internal(host, 0, socket_flags); + } + + inline bool Server::listen_after_bind() { return listen_internal(); } + + inline bool Server::listen(const char *host, int port, int socket_flags) { + if (bind_internal(host, port, socket_flags) < 0) return false; + return listen_internal(); + } + + inline bool Server::is_running() const { return is_running_; } + + inline void Server::stop() { + if (is_running_) { + assert(svr_sock_ != INVALID_SOCKET); + std::atomic sock (svr_sock_.exchange(INVALID_SOCKET)); + detail::shutdown_socket(sock); + detail::close_socket(sock); + } + } + + inline bool Server::parse_request_line(const char *s, Request &req) { + static std::regex re("(GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS) " + "(([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n"); + + std::cmatch m; + if (std::regex_match(s, m, re)) { + req.version = std::string(m[5]); + req.method = std::string(m[1]); + req.target = std::string(m[2]); + req.path = detail::decode_url(m[3]); + + // Parse query text + auto len = std::distance(m[4].first, m[4].second); + if (len > 0) { detail::parse_query_text(m[4], req.params); } + + return true; + } + + return false; + } + + inline void Server::write_response(Stream &strm, bool last_connection, + const Request &req, Response &res) { + assert(res.status != -1); + + if (400 <= res.status && error_handler_) { error_handler_(req, res); } + + // Response line + strm.write_format("HTTP/1.1 %d %s\r\n", res.status, + detail::status_message(res.status)); + + // Headers + if (last_connection || req.get_header_value("Connection") == "close") { + res.set_header("Connection", "close"); + } + + if (!last_connection && req.get_header_value("Connection") == "Keep-Alive") { + res.set_header("Connection", "Keep-Alive"); + } + + if (res.body.empty()) { + if (!res.has_header("Content-Length")) { + if (res.streamcb) { + // Streamed response + res.set_header("Transfer-Encoding", "chunked"); + } else { + res.set_header("Content-Length", "0"); + } + } + } else { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + // TODO: 'Accpet-Encoding' has gzip, not gzip;q=0 + const auto &encodings = req.get_header_value("Accept-Encoding"); + if (encodings.find("gzip") != std::string::npos && + detail::can_compress(res.get_header_value("Content-Type"))) { + detail::compress(res.body); + res.set_header("Content-Encoding", "gzip"); + } +#endif + + if (!res.has_header("Content-Type")) { + res.set_header("Content-Type", "text/plain"); + } + + auto length = std::to_string(res.body.size()); + res.set_header("Content-Length", length.c_str()); + } + + detail::write_headers(strm, res); + + // Body + if (req.method != "HEAD") { + if (!res.body.empty()) { + strm.write(res.body.c_str(), res.body.size()); + } else if (res.streamcb) { + bool chunked_response = !res.has_header("Content-Length"); + uint64_t offset = 0; + bool data_available = true; + while (data_available) { + std::string chunk = res.streamcb(offset); + offset += chunk.size(); + data_available = !chunk.empty(); + // Emit chunked response header and footer for each chunk + if (chunked_response) + chunk = detail::from_i_to_hex(chunk.size()) + "\r\n" + chunk + "\r\n"; + if (strm.write(chunk.c_str(), chunk.size()) < 0) break; // Stop on error + } + } + } + + // Log + if (logger_) { logger_(req, res); } + } + + inline bool Server::handle_file_request(Request &req, Response &res) { + if (!base_dir_.empty() && detail::is_valid_path(req.path)) { + std::string path = base_dir_ + req.path; + + if (!path.empty() && path.back() == '/') { path += "index.html"; } + + if (detail::is_file(path)) { + detail::read_file(path, res.body); + auto type = detail::find_content_type(path); + if (type) { res.set_header("Content-Type", type); } + res.status = 200; + return true; + } + } + + return false; + } + + inline socket_t Server::create_server_socket(const char *host, int port, + int socket_flags) const { + return detail::create_socket( + host, port, + [](socket_t sock, struct addrinfo &ai) -> bool { + if (::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + return false; + } + if (::listen(sock, 5)) { // Listen through 5 channels + return false; + } + return true; + }, + socket_flags); + } + + inline int Server::bind_internal(const char *host, int port, int socket_flags) { + if (!is_valid()) { return -1; } + + svr_sock_ = create_server_socket(host, port, socket_flags); + if (svr_sock_ == INVALID_SOCKET) { return -1; } + + if (port == 0) { + struct sockaddr_storage address; + socklen_t len = sizeof(address); + if (getsockname(svr_sock_, reinterpret_cast(&address), + &len) == -1) { + return -1; + } + if (address.ss_family == AF_INET) { + return ntohs(reinterpret_cast(&address)->sin_port); + } else if (address.ss_family == AF_INET6) { + return ntohs( + reinterpret_cast(&address)->sin6_port); + } else { + return -1; + } + } else { + return port; + } + } + + inline bool Server::poll() { + auto ret = true; + + is_running_ = true; + + if (svr_sock_ == INVALID_SOCKET) { + // The server socket was closed by 'stop' method. + return false; + } + + auto val = detail::select_read(svr_sock_, 0, 1000); + + if (val == 0) { // Timeout + return false; + } + + socket_t sock = accept(svr_sock_, nullptr, nullptr); + + if (sock == INVALID_SOCKET) { + if (svr_sock_ != INVALID_SOCKET) { + detail::close_socket(svr_sock_); + ret = false; + } else { + ; // The server socket was closed by user. + } + return false; + } + + read_and_close_socket(sock); + + is_running_ = false; + + return ret; + } + + inline bool Server::listen_internal() { + auto ret = true; + + is_running_ = true; + + for (;;) { + if (svr_sock_ == INVALID_SOCKET) { + // The server socket was closed by 'stop' method. + break; + } + + auto val = detail::select_read(svr_sock_, 0, 100000); + + if (val == 0) { // Timeout + continue; + } + + socket_t sock = accept(svr_sock_, nullptr, nullptr); + + if (sock == INVALID_SOCKET) { + if (svr_sock_ != INVALID_SOCKET) { + detail::close_socket(svr_sock_); + ret = false; + } else { + ; // The server socket was closed by user. + } + break; + } + + // TODO: Use thread pool... + std::thread([=]() { + { + std::lock_guard guard(running_threads_mutex_); + running_threads_++; + } + + read_and_close_socket(sock); + + { + std::lock_guard guard(running_threads_mutex_); + running_threads_--; + } + }).detach(); + } + + // TODO: Use thread pool... + for (;;) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + std::lock_guard guard(running_threads_mutex_); + if (!running_threads_) { break; } + } + + is_running_ = false; + + return ret; + } + + inline bool Server::routing(Request &req, Response &res) { + if (req.method == "GET" && handle_file_request(req, res)) { return true; } + + if (req.method == "GET" || req.method == "HEAD") { + return dispatch_request(req, res, get_handlers_); + } else if (req.method == "POST") { + return dispatch_request(req, res, post_handlers_); + } else if (req.method == "PUT") { + return dispatch_request(req, res, put_handlers_); + } else if (req.method == "PATCH") { + return dispatch_request(req, res, patch_handlers_); + } else if (req.method == "DELETE") { + return dispatch_request(req, res, delete_handlers_); + } else if (req.method == "OPTIONS") { + return dispatch_request(req, res, options_handlers_); + } + return false; + } + + inline bool Server::dispatch_request(Request &req, Response &res, + Handlers &handlers) { + for (const auto &x : handlers) { + const auto &pattern = x.first; + const auto &handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res); + return true; + } + } + return false; + } + + inline bool + Server::process_request(Stream &strm, bool last_connection, + bool &connection_close, + std::function setup_request) { + const auto bufsiz = 2048; + char buf[bufsiz]; + + detail::stream_line_reader reader(strm, buf, bufsiz); + + // Connection has been closed on client + if (!reader.getline()) { return false; } + + Request req; + Response res; + + res.version = "HTTP/1.1"; + + // Check if the request URI doesn't exceed the limit + if (reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { + res.status = 414; + write_response(strm, last_connection, req, res); + return true; + } + + // Request line and headers + if (!parse_request_line(reader.ptr(), req) || + !detail::read_headers(strm, req.headers)) { + res.status = 400; + write_response(strm, last_connection, req, res); + return true; + } + + if (req.get_header_value("Connection") == "close") { + connection_close = true; + } + + req.set_header("REMOTE_ADDR", strm.get_remote_addr().c_str()); + + // Body + if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") { + bool exceed_payload_max_length = false; + if (!detail::read_content(strm, req, payload_max_length_, + exceed_payload_max_length)) { + res.status = exceed_payload_max_length ? 413 : 400; + write_response(strm, last_connection, req, res); + return !exceed_payload_max_length; + } + + const auto &content_type = req.get_header_value("Content-Type"); + + if (req.get_header_value("Content-Encoding") == "gzip") { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + detail::decompress(req.body); +#else + res.status = 415; + write_response(strm, last_connection, req, res); + return true; +#endif + } + + if (!content_type.find("application/x-www-form-urlencoded")) { + detail::parse_query_text(req.body, req.params); + } else if (!content_type.find("multipart/form-data")) { + std::string boundary; + if (!detail::parse_multipart_boundary(content_type, boundary) || + !detail::parse_multipart_formdata(boundary, req.body, req.files)) { + res.status = 400; + write_response(strm, last_connection, req, res); + return true; + } + } + } + + // TODO: Add additional request info + if (setup_request) { setup_request(req); } + + if (routing(req, res)) { + if (res.status == -1) { res.status = 200; } + } else { + res.status = 404; + } + + write_response(strm, last_connection, req, res); + return true; + } + + inline bool Server::is_valid() const { return true; } + + inline bool Server::read_and_close_socket(socket_t sock) { + return detail::read_and_close_socket( + sock, keep_alive_max_count_, + [this](Stream &strm, bool last_connection, bool &connection_close) { + return process_request(strm, last_connection, connection_close); + }); + } + +// HTTP client implementation + inline Client::Client(const char *host, int port, time_t timeout_sec) + : host_(host), port_(port), timeout_sec_(timeout_sec), + host_and_port_(host_ + ":" + std::to_string(port_)) {} + + inline Client::~Client() {} + + inline bool Client::is_valid() const { return true; } + + inline socket_t Client::create_client_socket() const { + return detail::create_socket( + host_.c_str(), port_, [=](socket_t sock, struct addrinfo &ai) -> bool { + detail::set_nonblocking(sock, true); + + auto ret = connect(sock, ai.ai_addr, static_cast(ai.ai_addrlen)); + if (ret < 0) { + if (detail::is_connection_error() || + !detail::wait_until_socket_is_ready(sock, timeout_sec_, 0)) { + detail::close_socket(sock); + return false; + } + } + + detail::set_nonblocking(sock, false); + return true; + }); + } + + inline bool Client::read_response_line(Stream &strm, Response &res) { + const auto bufsiz = 2048; + char buf[bufsiz]; + + detail::stream_line_reader reader(strm, buf, bufsiz); + + if (!reader.getline()) { return false; } + + const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n"); + + std::cmatch m; + if (std::regex_match(reader.ptr(), m, re)) { + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + } + + return true; + } + + inline bool Client::send(Request &req, Response &res) { + if (req.path.empty()) { return false; } + + auto sock = create_client_socket(); + if (sock == INVALID_SOCKET) { return false; } + + return read_and_close_socket(sock, req, res); + } + + inline void Client::write_request(Stream &strm, Request &req) { + BufferStream bstrm; + + // Request line + auto path = detail::encode_url(req.path); + + bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); + + // Headers + if (!req.has_header("Host")) { + if (is_ssl()) { + if (port_ == 443) { + req.set_header("Host", host_.c_str()); + } else { + req.set_header("Host", host_and_port_.c_str()); + } + } else { + if (port_ == 80) { + req.set_header("Host", host_.c_str()); + } else { + req.set_header("Host", host_and_port_.c_str()); + } + } + } + + if (!req.has_header("Accept")) { req.set_header("Accept", "*/*"); } + + if (!req.has_header("User-Agent")) { + req.set_header("User-Agent", "cpp-httplib/0.2"); + } + + // TODO: Support KeepAlive connection + // if (!req.has_header("Connection")) { + req.set_header("Connection", "close"); + // } + + if (req.body.empty()) { + if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") { + req.set_header("Content-Length", "0"); + } + } else { + if (!req.has_header("Content-Type")) { + req.set_header("Content-Type", "text/plain"); + } + + if (!req.has_header("Content-Length")) { + auto length = std::to_string(req.body.size()); + req.set_header("Content-Length", length.c_str()); + } + } + + detail::write_headers(bstrm, req); + + // Body + if (!req.body.empty()) { bstrm.write(req.body.c_str(), req.body.size()); } + + // Flush buffer + auto &data = bstrm.get_buffer(); + strm.write(data.data(), data.size()); + } + + inline bool Client::process_request(Stream &strm, Request &req, Response &res, + bool &connection_close) { + // Send request + write_request(strm, req); + + // Receive response and headers + if (!read_response_line(strm, res) || + !detail::read_headers(strm, res.headers)) { + return false; + } + + if (res.get_header_value("Connection") == "close" || + res.version == "HTTP/1.0") { + connection_close = true; + } + + // Body + if (req.method != "HEAD") { + bool exceed_payload_max_length = false; + if (!detail::read_content(strm, res, std::numeric_limits::max(), + exceed_payload_max_length, req.progress)) { + return false; + } + + if (res.get_header_value("Content-Encoding") == "gzip") { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + detail::decompress(res.body); +#else + return false; +#endif + } + } + + return true; + } + + inline bool Client::read_and_close_socket(socket_t sock, Request &req, + Response &res) { + return detail::read_and_close_socket( + sock, 0, + [&](Stream &strm, bool /*last_connection*/, bool &connection_close) { + return process_request(strm, req, res, connection_close); + }); + } + + inline bool Client::is_ssl() const { return false; } + + inline std::shared_ptr Client::Get(const char *path, + Progress progress) { + return Get(path, Headers(), progress); + } + + inline std::shared_ptr + Client::Get(const char *path, const Headers &headers, Progress progress) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.progress = progress; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; + } + + inline std::shared_ptr Client::Head(const char *path) { + return Head(path, Headers()); + } + + inline std::shared_ptr Client::Head(const char *path, + const Headers &headers) { + Request req; + req.method = "HEAD"; + req.headers = headers; + req.path = path; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; + } + + inline std::shared_ptr Client::Post(const char *path, + const std::string &body, + const char *content_type) { + return Post(path, Headers(), body, content_type); + } + + inline std::shared_ptr Client::Post(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + Request req; + req.method = "POST"; + req.headers = headers; + req.path = path; + + req.headers.emplace("Content-Type", content_type); + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; + } + + inline std::shared_ptr Client::Post(const char *path, + const Params ¶ms) { + return Post(path, Headers(), params); + } + + inline std::shared_ptr + Client::Post(const char *path, const Headers &headers, const Params ¶ms) { + std::string query; + for (auto it = params.begin(); it != params.end(); ++it) { + if (it != params.begin()) { query += "&"; } + query += it->first; + query += "="; + query += detail::encode_url(it->second); + } + + return Post(path, headers, query, "application/x-www-form-urlencoded"); + } + + inline std::shared_ptr Client::Put(const char *path, + const std::string &body, + const char *content_type) { + return Put(path, Headers(), body, content_type); + } + + inline std::shared_ptr Client::Put(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + Request req; + req.method = "PUT"; + req.headers = headers; + req.path = path; + + req.headers.emplace("Content-Type", content_type); + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; + } + + inline std::shared_ptr Client::Patch(const char *path, + const std::string &body, + const char *content_type) { + return Patch(path, Headers(), body, content_type); + } + + inline std::shared_ptr Client::Patch(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + Request req; + req.method = "PATCH"; + req.headers = headers; + req.path = path; + + req.headers.emplace("Content-Type", content_type); + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; + } + + inline std::shared_ptr Client::Delete(const char *path, + const std::string &body, + const char *content_type) { + return Delete(path, Headers(), body, content_type); + } + + inline std::shared_ptr Client::Delete(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + Request req; + req.method = "DELETE"; + req.headers = headers; + req.path = path; + + if (content_type) { req.headers.emplace("Content-Type", content_type); } + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; + } + + inline std::shared_ptr Client::Options(const char *path) { + return Options(path, Headers()); + } + + inline std::shared_ptr Client::Options(const char *path, + const Headers &headers) { + Request req; + req.method = "OPTIONS"; + req.path = path; + req.headers = headers; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; + } + +/* + * SSL Implementation + */ +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + namespace detail { + +template +inline bool +read_and_close_socket_ssl(socket_t sock, size_t keep_alive_max_count, + // TODO: OpenSSL 1.0.2 occasionally crashes... + // The upcoming 1.1.0 is going to be thread safe. + SSL_CTX *ctx, std::mutex &ctx_mutex, + U SSL_connect_or_accept, V setup, T callback) { + SSL *ssl = nullptr; + { + std::lock_guard guard(ctx_mutex); + ssl = SSL_new(ctx); + } + + if (!ssl) { + close_socket(sock); + return false; + } + + auto bio = BIO_new_socket(sock, BIO_NOCLOSE); + SSL_set_bio(ssl, bio, bio); + + if (!setup(ssl)) { + SSL_shutdown(ssl); + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } + + close_socket(sock); + return false; + } + + bool ret = false; + + if (SSL_connect_or_accept(ssl) == 1) { + if (keep_alive_max_count > 0) { + auto count = keep_alive_max_count; + while (count > 0 && + detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { + SSLSocketStream strm(sock, ssl); + auto last_connection = count == 1; + auto connection_close = false; + + ret = callback(ssl, strm, last_connection, connection_close); + if (!ret || connection_close) { break; } + + count--; + } + } else { + SSLSocketStream strm(sock, ssl); + auto dummy_connection_close = false; + ret = callback(ssl, strm, true, dummy_connection_close); + } + } + + SSL_shutdown(ssl); + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } + + close_socket(sock); + + return ret; +} + +class SSLInit { +public: + SSLInit() { + SSL_load_error_strings(); + SSL_library_init(); + } + + ~SSLInit() { ERR_free_strings(); } +}; + +static SSLInit sslinit_; + +} // namespace detail + +// SSL socket stream implementation +inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl) + : sock_(sock), ssl_(ssl) {} + +inline SSLSocketStream::~SSLSocketStream() {} + +inline int SSLSocketStream::read(char *ptr, size_t size) { + if (SSL_pending(ssl_) > 0 || + detail::select_read(sock_, CPPHTTPLIB_READ_TIMEOUT_SECOND, + CPPHTTPLIB_READ_TIMEOUT_USECOND) > 0) { + return SSL_read(ssl_, ptr, size); + } + return -1; +} + +inline int SSLSocketStream::write(const char *ptr, size_t size) { + return SSL_write(ssl_, ptr, size); +} + +inline int SSLSocketStream::write(const char *ptr) { + return write(ptr, strlen(ptr)); +} + +inline std::string SSLSocketStream::get_remote_addr() const { + return detail::get_remote_addr(sock_); +} + +// SSL HTTP server implementation +inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, + const char *client_ca_cert_file_path, + const char *client_ca_cert_dir_path) { + ctx_ = SSL_CTX_new(SSLv23_server_method()); + + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + // auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + // SSL_CTX_set_tmp_ecdh(ctx_, ecdh); + // EC_KEY_free(ecdh); + + if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != + 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } else if (client_ca_cert_file_path || client_ca_cert_dir_path) { + // if (client_ca_cert_file_path) { + // auto list = SSL_load_client_CA_file(client_ca_cert_file_path); + // SSL_CTX_set_client_CA_list(ctx_, list); + // } + + SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path, + client_ca_cert_dir_path); + + SSL_CTX_set_verify( + ctx_, + SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE, + nullptr); + } + } +} + +inline SSLServer::~SSLServer() { + if (ctx_) { SSL_CTX_free(ctx_); } +} + +inline bool SSLServer::is_valid() const { return ctx_; } + +inline bool SSLServer::read_and_close_socket(socket_t sock) { + return detail::read_and_close_socket_ssl( + sock, keep_alive_max_count_, ctx_, ctx_mutex_, SSL_accept, + [](SSL * /*ssl*/) { return true; }, + [this](SSL *ssl, Stream &strm, bool last_connection, + bool &connection_close) { + return process_request(strm, last_connection, connection_close, + [&](Request &req) { req.ssl = ssl; }); + }); +} + +// SSL HTTP client implementation +inline SSLClient::SSLClient(const char *host, int port, time_t timeout_sec, + const char *client_cert_path, + const char *client_key_path) + : Client(host, port, timeout_sec) { + ctx_ = SSL_CTX_new(SSLv23_client_method()); + + detail::split(&host_[0], &host_[host_.size()], '.', + [&](const char *b, const char *e) { + host_components_.emplace_back(std::string(b, e)); + }); + if (client_cert_path && client_key_path) { + if (SSL_CTX_use_certificate_file(ctx_, client_cert_path, + SSL_FILETYPE_PEM) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, client_key_path, SSL_FILETYPE_PEM) != + 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLClient::~SSLClient() { + if (ctx_) { SSL_CTX_free(ctx_); } +} + +inline bool SSLClient::is_valid() const { return ctx_; } + +inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path, + const char *ca_cert_dir_path) { + if (ca_cert_file_path) { ca_cert_file_path_ = ca_cert_file_path; } + if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; } +} + +inline void SSLClient::enable_server_certificate_verification(bool enabled) { + server_certificate_verification_ = enabled; +} + +inline long SSLClient::get_openssl_verify_result() const { + return verify_result_; +} + +inline bool SSLClient::read_and_close_socket(socket_t sock, Request &req, + Response &res) { + + return is_valid() && + detail::read_and_close_socket_ssl( + sock, 0, ctx_, ctx_mutex_, + [&](SSL *ssl) { + if (ca_cert_file_path_.empty()) { + SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr); + } else { + if (!SSL_CTX_load_verify_locations( + ctx_, ca_cert_file_path_.c_str(), nullptr)) { + return false; + } + SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr); + } + + if (SSL_connect(ssl) != 1) { return false; } + + if (server_certificate_verification_) { + verify_result_ = SSL_get_verify_result(ssl); + + if (verify_result_ != X509_V_OK) { return false; } + + auto server_cert = SSL_get_peer_certificate(ssl); + + if (server_cert == nullptr) { return false; } + + if (!verify_host(server_cert)) { + X509_free(server_cert); + return false; + } + X509_free(server_cert); + } + + return true; + }, + [&](SSL *ssl) { + SSL_set_tlsext_host_name(ssl, host_.c_str()); + return true; + }, + [&](SSL * /*ssl*/, Stream &strm, bool /*last_connection*/, + bool &connection_close) { + return process_request(strm, req, res, connection_close); + }); +} + +inline bool SSLClient::is_ssl() const { return true; } + +inline bool SSLClient::verify_host(X509 *server_cert) const { + /* Quote from RFC2818 section 3.1 "Server Identity" + + If a subjectAltName extension of type dNSName is present, that MUST + be used as the identity. Otherwise, the (most specific) Common Name + field in the Subject field of the certificate MUST be used. Although + the use of the Common Name is existing practice, it is deprecated and + Certification Authorities are encouraged to use the dNSName instead. + + Matching is performed using the matching rules specified by + [RFC2459]. If more than one identity of a given type is present in + the certificate (e.g., more than one dNSName name, a match in any one + of the set is considered acceptable.) Names may contain the wildcard + character * which is considered to match any single domain name + component or component fragment. E.g., *.a.com matches foo.a.com but + not bar.foo.a.com. f*.com matches foo.com but not bar.com. + + In some cases, the URI is specified as an IP address rather than a + hostname. In this case, the iPAddress subjectAltName must be present + in the certificate and must exactly match the IP in the URI. + + */ + return verify_host_with_subject_alt_name(server_cert) || + verify_host_with_common_name(server_cert); +} + +inline bool +SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { + auto ret = false; + + auto type = GEN_DNS; + + struct in6_addr addr6; + struct in_addr addr; + size_t addr_len = 0; + + if (inet_pton(AF_INET6, host_.c_str(), &addr6)) { + type = GEN_IPADD; + addr_len = sizeof(struct in6_addr); + } else if (inet_pton(AF_INET, host_.c_str(), &addr)) { + type = GEN_IPADD; + addr_len = sizeof(struct in_addr); + } + + auto alt_names = static_cast( + X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr)); + + if (alt_names) { + auto dsn_matched = false; + auto ip_mached = false; + + auto count = sk_GENERAL_NAME_num(alt_names); + + for (auto i = 0; i < count && !dsn_matched; i++) { + auto val = sk_GENERAL_NAME_value(alt_names, i); + if (val->type == type) { + auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5); + auto name_len = (size_t)ASN1_STRING_length(val->d.ia5); + + if (strlen(name) == name_len) { + switch (type) { + case GEN_DNS: dsn_matched = check_host_name(name, name_len); break; + + case GEN_IPADD: + if (!memcmp(&addr6, name, addr_len) || + !memcmp(&addr, name, addr_len)) { + ip_mached = true; + } + break; + } + } + } + } + + if (dsn_matched || ip_mached) { ret = true; } + } + + GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names); + + return ret; +} + +inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const { + const auto subject_name = X509_get_subject_name(server_cert); + + if (subject_name != nullptr) { + char name[BUFSIZ]; + auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, + name, sizeof(name)); + + if (name_len != -1) { return check_host_name(name, name_len); } + } + + return false; +} + +inline bool SSLClient::check_host_name(const char *pattern, + size_t pattern_len) const { + if (host_.size() == pattern_len && host_ == pattern) { return true; } + + // Wildcard match + // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484 + std::vector pattern_components; + detail::split(&pattern[0], &pattern[pattern_len], '.', + [&](const char *b, const char *e) { + pattern_components.emplace_back(std::string(b, e)); + }); + + if (host_components_.size() != pattern_components.size()) { return false; } + + auto itr = pattern_components.begin(); + for (const auto &h : host_components_) { + auto &p = *itr; + if (p != h && p != "*") { + auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' && + !p.compare(0, p.size() - 1, h)); + if (!partial_match) { return false; } + } + ++itr; + } + + return true; +} +#endif + +} // namespace httplib + +#endif // CPPHTTPLIB_HTTPLIB_H \ No newline at end of file diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index 826c84fb4..a51b04998 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -1381,7 +1381,7 @@ int16 EQEmu::InventoryProfile::_PutItem(int16 slot_id, ItemInstance* inst) } if (result == INVALID_INDEX) { - Log(Logs::General, Logs::Error, "InventoryProfile::_PutItem: Invalid slot_id specified (%i) with parent slot id (%i)", slot_id, parentSlot); + LogError("InventoryProfile::_PutItem: Invalid slot_id specified ({}) with parent slot id ({})", slot_id, parentSlot); InventoryProfile::MarkDirty(inst); // Slot not found, clean up } diff --git a/common/ip_util.cpp b/common/ip_util.cpp new file mode 100644 index 000000000..e1f5d0794 --- /dev/null +++ b/common/ip_util.cpp @@ -0,0 +1,72 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ + +#include "ip_util.h" + +/** + * @param ip + * @return + */ +uint32_t IpUtil::IPToUInt(const std::string &ip) +{ + int a, b, c, d; + uint32_t addr = 0; + + if (sscanf(ip.c_str(), "%d.%d.%d.%d", &a, &b, &c, &d) != 4) { + return 0; + } + + addr = a << 24; + addr |= b << 16; + addr |= c << 8; + addr |= d; + return addr; +} + +/** + * @param ip + * @param network + * @param mask + * @return + */ +bool IpUtil::IsIpInRange(const std::string &ip, const std::string &network, const std::string &mask) +{ + uint32_t ip_addr = IpUtil::IPToUInt(ip); + uint32_t network_addr = IpUtil::IPToUInt(network); + uint32_t mask_addr = IpUtil::IPToUInt(mask); + + uint32_t net_lower = (network_addr & mask_addr); + uint32_t net_upper = (net_lower | (~mask_addr)); + + return ip_addr >= net_lower && ip_addr <= net_upper; +} + +/** + * @param ip + * @return + */ +bool IpUtil::IsIpInPrivateRfc1918(const std::string &ip) +{ + return ( + IpUtil::IsIpInRange(ip, "10.0.0.0", "255.0.0.0") || + IpUtil::IsIpInRange(ip, "172.16.0.0", "255.240.0.0") || + IpUtil::IsIpInRange(ip, "192.168.0.0", "255.255.0.0") + ); +} diff --git a/common/eqemu_logsys_fmt.h b/common/ip_util.h similarity index 72% rename from common/eqemu_logsys_fmt.h rename to common/ip_util.h index 3a6a9f67d..76ea33fdb 100644 --- a/common/eqemu_logsys_fmt.h +++ b/common/ip_util.h @@ -16,15 +16,21 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * -*/ + */ -#pragma once +#ifndef EQEMU_IP_UTIL_H +#define EQEMU_IP_UTIL_H -#include +#include "types.h" +#include "iostream" -template -void OutF(EQEmuLogSys &ls, Logs::DebugLevel debug_level, uint16 log_category, const char *fmt, const Args&... args) -{ - std::string log_str = fmt::format(fmt, args...); - ls.Out(debug_level, log_category, log_str); -} +class IpUtil { +public: + + static uint32_t IPToUInt(const std::string &ip); + static bool IsIpInRange(const std::string &ip, const std::string &network, const std::string &mask); + static bool IsIpInPrivateRfc1918(const std::string &ip); + +}; + +#endif //EQEMU_IP_UTIL_H \ No newline at end of file diff --git a/common/json_config.cpp b/common/json_config.cpp index d677fc452..d262038f6 100644 --- a/common/json_config.cpp +++ b/common/json_config.cpp @@ -1,26 +1,29 @@ #include "json_config.h" #include +#include -EQ::JsonConfigFile::JsonConfigFile() -{ -} +EQ::JsonConfigFile::JsonConfigFile() = default; EQ::JsonConfigFile::JsonConfigFile(const Json::Value &value) { m_root = value; } -EQ::JsonConfigFile::~JsonConfigFile() -{ -} +EQ::JsonConfigFile::~JsonConfigFile() = default; -EQ::JsonConfigFile EQ::JsonConfigFile::Load(const std::string &filename) +/** + * @param file_name + * @return + */ +EQ::JsonConfigFile EQ::JsonConfigFile::Load( + const std::string &file_name +) { JsonConfigFile ret; ret.m_root = Json::Value(); std::ifstream ifs; - ifs.open(filename, std::ifstream::in); + ifs.open(file_name, std::ifstream::in); if (!ifs.good()) { return ret; @@ -36,7 +39,43 @@ EQ::JsonConfigFile EQ::JsonConfigFile::Load(const std::string &filename) return ret; } -std::string EQ::JsonConfigFile::GetVariableString(const std::string &title, const std::string ¶meter, const std::string &default_value) { +/** + * @param file_name + * @return + */ +void EQ::JsonConfigFile::Save( + const std::string &file_name +) +{ + std::ofstream opened_config_file; + opened_config_file.open(file_name); + + /** + * Grab and build config contents + */ + Json::StreamWriterBuilder write_builder; + write_builder["indentation"] = " "; + std::string document = Json::writeString(write_builder, m_root); + + /** + * Write current contents and close + */ + opened_config_file << document; + opened_config_file.close(); +} + +/** + * @param title + * @param parameter + * @param default_value + * @return + */ +std::string EQ::JsonConfigFile::GetVariableString( + const std::string &title, + const std::string ¶meter, + const std::string &default_value +) +{ try { if (m_root.isMember(title) && m_root[title].isMember(parameter)) { return m_root[title][parameter].asString(); @@ -49,7 +88,18 @@ std::string EQ::JsonConfigFile::GetVariableString(const std::string &title, cons return default_value; } -int EQ::JsonConfigFile::GetVariableInt(const std::string &title, const std::string ¶meter, const int default_value) { +/** + * @param title + * @param parameter + * @param default_value + * @return + */ +int EQ::JsonConfigFile::GetVariableInt( + const std::string &title, + const std::string ¶meter, + const int default_value +) +{ try { if (m_root.isMember(title) && m_root[title].isMember(parameter)) { return m_root[title][parameter].asInt(); @@ -62,7 +112,18 @@ int EQ::JsonConfigFile::GetVariableInt(const std::string &title, const std::stri return default_value; } -bool EQ::JsonConfigFile::GetVariableBool(const std::string &title, const std::string ¶meter, const bool default_value) { +/** + * @param title + * @param parameter + * @param default_value + * @return + */ +bool EQ::JsonConfigFile::GetVariableBool( + const std::string &title, + const std::string ¶meter, + const bool default_value +) +{ try { if (m_root.isMember(title) && m_root[title].isMember(parameter)) { return m_root[title][parameter].asBool(); @@ -75,7 +136,18 @@ bool EQ::JsonConfigFile::GetVariableBool(const std::string &title, const std::st return default_value; } -double EQ::JsonConfigFile::GetVariableDouble(const std::string &title, const std::string ¶meter, const double default_value) { +/** + * @param title + * @param parameter + * @param default_value + * @return + */ +double EQ::JsonConfigFile::GetVariableDouble( + const std::string &title, + const std::string ¶meter, + const double default_value +) +{ try { if (m_root.isMember(title) && m_root[title].isMember(parameter)) { return m_root[title][parameter].asDouble(); diff --git a/common/json_config.h b/common/json_config.h index 279fb64d9..aa4d2d8f6 100644 --- a/common/json_config.h +++ b/common/json_config.h @@ -7,10 +7,12 @@ namespace EQ class JsonConfigFile { public: + JsonConfigFile(); JsonConfigFile(const Json::Value &value); ~JsonConfigFile(); - static JsonConfigFile Load(const std::string &filename); + static JsonConfigFile Load(const std::string &file_name); + void Save(const std::string &file_name); std::string GetVariableString(const std::string &title, const std::string ¶meter, const std::string &default_value); int GetVariableInt(const std::string &title, const std::string ¶meter, const int default_value); @@ -19,7 +21,6 @@ namespace EQ Json::Value& RawHandle() { return m_root; } private: - JsonConfigFile(); Json::Value m_root; }; diff --git a/common/misc_functions.h b/common/misc_functions.h index 4f295c68a..62a6745c1 100644 --- a/common/misc_functions.h +++ b/common/misc_functions.h @@ -40,7 +40,7 @@ #define VERIFY_PACKET_LENGTH(OPCode, Packet, StructName) \ if(Packet->size != sizeof(StructName)) \ { \ - Log(Logs::Detail, Logs::Netcode, "Size mismatch in " #OPCode " expected %i got %i", sizeof(StructName), Packet->size); \ + LogNetcode("Size mismatch in " #OPCode " expected [{}] got [{}]", sizeof(StructName), Packet->size); \ DumpPacket(Packet); \ return; \ } diff --git a/common/net/eqstream.cpp b/common/net/eqstream.cpp index 378b0d919..368a23931 100644 --- a/common/net/eqstream.cpp +++ b/common/net/eqstream.cpp @@ -1,6 +1,5 @@ #include "eqstream.h" #include "../eqemu_logsys.h" -#include "../eqemu_logsys_fmt.h" EQ::Net::EQStreamManager::EQStreamManager(const EQStreamManagerInterfaceOptions &options) : EQStreamManagerInterface(options), m_daybreak(options.daybreak_options) { @@ -185,23 +184,23 @@ EQStreamInterface::MatchState EQ::Net::EQStream::CheckSignature(const Signature if (opcode == sig->first_eq_opcode) { if (length == sig->first_length) { - LogF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode matched {2:#x} and length matched {3}", + LogF(Logs::General, Logs::Netcode, "[StreamIdentify] {0}:{1}: First opcode matched {2:#x} and length matched {3}", m_connection->RemoteEndpoint(), m_connection->RemotePort(), sig->first_eq_opcode, length); return MatchSuccessful; } else if (length == 0) { - LogF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode matched {2:#x} and length is ignored.", + LogF(Logs::General, Logs::Netcode, "[StreamIdentify] {0}:{1}: First opcode matched {2:#x} and length is ignored.", m_connection->RemoteEndpoint(), m_connection->RemotePort(), sig->first_eq_opcode); return MatchSuccessful; } else { - LogF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode matched {2:#x} but length {3} did not match expected {4}", + LogF(Logs::General, Logs::Netcode, "[StreamIdentify] {0}:{1}: First opcode matched {2:#x} but length {3} did not match expected {4}", m_connection->RemoteEndpoint(), m_connection->RemotePort(), sig->first_eq_opcode, length, sig->first_length); return MatchFailed; } } else { - LogF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode {1:#x} did not match expected {2:#x}", + LogF(Logs::General, Logs::Netcode, "[StreamIdentify] {0}:{1}: First opcode {1:#x} did not match expected {2:#x}", m_connection->RemoteEndpoint(), m_connection->RemotePort(), opcode, sig->first_eq_opcode); return MatchFailed; } diff --git a/common/net/servertalk_client_connection.cpp b/common/net/servertalk_client_connection.cpp index 0b77fc073..0a5dceac6 100644 --- a/common/net/servertalk_client_connection.cpp +++ b/common/net/servertalk_client_connection.cpp @@ -1,7 +1,6 @@ #include "servertalk_client_connection.h" #include "dns.h" #include "../eqemu_logsys.h" -#include "../eqemu_logsys_fmt.h" EQ::Net::ServertalkClient::ServertalkClient(const std::string &addr, int port, bool ipv6, const std::string &identifier, const std::string &credentials) : m_timer(std::unique_ptr(new EQ::Timer(100, true, std::bind(&EQ::Net::ServertalkClient::Connect, this)))) @@ -79,15 +78,15 @@ void EQ::Net::ServertalkClient::Connect() m_connecting = true; EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr connection) { if (connection == nullptr) { - LogF(Logs::General, Logs::TCP_Connection, "Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port); + LogF(Logs::General, Logs::TCPConnection, "Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port); m_connecting = false; return; } - LogF(Logs::General, Logs::TCP_Connection, "Connected to {0}:{1}", m_addr, m_port); + LogF(Logs::General, Logs::TCPConnection, "Connected to {0}:{1}", m_addr, m_port); m_connection = connection; m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) { - LogF(Logs::General, Logs::TCP_Connection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port); + LogF(Logs::General, Logs::TCPConnection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port); m_encrypted = false; m_connection.reset(); }); @@ -214,7 +213,7 @@ void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p) } } else { - LogF(Logs::General, Logs::Error, "Could not process hello, size != {0}", 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES); + LogError("Could not process hello, size != {0}", 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES); } } else { @@ -226,7 +225,7 @@ void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p) } } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing hello from server: {0}", ex.what()); + LogError("Error parsing hello from server: {0}", ex.what()); m_connection->Disconnect(); if (m_on_connect_cb) { @@ -253,7 +252,7 @@ void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p) } } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing hello from server: {0}", ex.what()); + LogError("Error parsing hello from server: {0}", ex.what()); m_connection->Disconnect(); if (m_on_connect_cb) { @@ -276,7 +275,7 @@ void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p) std::unique_ptr decrypted_text(new unsigned char[message_len]); if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)&data[0], length, m_nonce_theirs, m_shared_key)) { - LogF(Logs::General, Logs::Error, "Error decrypting message from server"); + LogError("Error decrypting message from server"); (*(uint64_t*)&m_nonce_theirs[0])++; return; } @@ -324,7 +323,7 @@ void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p) } } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing message from server: {0}", ex.what()); + LogError("Error parsing message from server: {0}", ex.what()); } } diff --git a/common/net/servertalk_legacy_client_connection.cpp b/common/net/servertalk_legacy_client_connection.cpp index 851ca3541..3cbe1c85a 100644 --- a/common/net/servertalk_legacy_client_connection.cpp +++ b/common/net/servertalk_legacy_client_connection.cpp @@ -1,7 +1,6 @@ #include "servertalk_legacy_client_connection.h" #include "dns.h" #include "../eqemu_logsys.h" -#include "../eqemu_logsys_fmt.h" EQ::Net::ServertalkLegacyClient::ServertalkLegacyClient(const std::string &addr, int port, bool ipv6) : m_timer(std::unique_ptr(new EQ::Timer(100, true, std::bind(&EQ::Net::ServertalkLegacyClient::Connect, this)))) @@ -59,15 +58,15 @@ void EQ::Net::ServertalkLegacyClient::Connect() m_connecting = true; EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr connection) { if (connection == nullptr) { - LogF(Logs::General, Logs::TCP_Connection, "Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port); + LogF(Logs::General, Logs::TCPConnection, "Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port); m_connecting = false; return; } - LogF(Logs::General, Logs::TCP_Connection, "Connected to {0}:{1}", m_addr, m_port); + LogF(Logs::General, Logs::TCPConnection, "Connected to {0}:{1}", m_addr, m_port); m_connection = connection; m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) { - LogF(Logs::General, Logs::TCP_Connection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port); + LogF(Logs::General, Logs::TCPConnection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port); m_connection.reset(); }); diff --git a/common/net/servertalk_server_connection.cpp b/common/net/servertalk_server_connection.cpp index fa52d8f9e..6bfae6ea1 100644 --- a/common/net/servertalk_server_connection.cpp +++ b/common/net/servertalk_server_connection.cpp @@ -1,7 +1,6 @@ #include "servertalk_server_connection.h" #include "servertalk_server.h" #include "../eqemu_logsys.h" -#include "../eqemu_logsys_fmt.h" #include "../util/uuid.h" EQ::Net::ServertalkServerConnection::ServertalkServerConnection(std::shared_ptr c, EQ::Net::ServertalkServer *parent, bool encrypted, bool allow_downgrade) @@ -202,8 +201,8 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b { #ifdef ENABLE_SECURITY if (downgrade_security && m_allow_downgrade && m_encrypted) { - LogF(Logs::General, Logs::TCP_Connection, "Downgraded encrypted connection to plaintext because otherside didn't support encryption {0}:{1}", - m_connection->RemoteIP(), m_connection->RemotePort()); + LogF(Logs::General, Logs::TCPConnection, "Downgraded encrypted connection to plaintext because otherside didn't support encryption {0}:{1}", + m_connection->RemoteIP(), m_connection->RemotePort()); m_encrypted = false; } @@ -221,7 +220,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)p.Data() + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, cipher_len, m_nonce_theirs, m_shared_key)) { - LogF(Logs::General, Logs::Error, "Error decrypting handshake from client, dropping connection."); + LogError("Error decrypting handshake from client, dropping connection."); m_connection->Disconnect(); return; } @@ -230,7 +229,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b std::string credentials = (const char*)&decrypted_text[0] + (m_identifier.length() + 1); if (!m_parent->CheckCredentials(credentials)) { - LogF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection."); + LogError("Got incoming connection with invalid credentials during handshake, dropping connection."); m_connection->Disconnect(); return; } @@ -240,7 +239,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b } } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what()); + LogError("Error parsing handshake from client: {0}", ex.what()); m_connection->Disconnect(); } } @@ -250,7 +249,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b auto credentials = p.GetCString(m_identifier.length() + 1); if (!m_parent->CheckCredentials(credentials)) { - LogF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection."); + LogError("Got incoming connection with invalid credentials during handshake, dropping connection."); m_connection->Disconnect(); return; } @@ -258,7 +257,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b m_parent->ConnectionIdentified(this); } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what()); + LogError("Error parsing handshake from client: {0}", ex.what()); m_connection->Disconnect(); } } @@ -268,7 +267,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b auto credentials = p.GetCString(m_identifier.length() + 1); if (!m_parent->CheckCredentials(credentials)) { - LogF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection."); + LogError("Got incoming connection with invalid credentials during handshake, dropping connection."); m_connection->Disconnect(); return; } @@ -276,7 +275,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b m_parent->ConnectionIdentified(this); } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what()); + LogError("Error parsing handshake from client: {0}", ex.what()); m_connection->Disconnect(); } #endif @@ -296,7 +295,7 @@ void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p) if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)&data[0], length, m_nonce_theirs, m_shared_key)) { - LogF(Logs::General, Logs::Error, "Error decrypting message from client"); + LogError("Error decrypting message from client"); (*(uint64_t*)&m_nonce_theirs[0])++; return; } @@ -344,6 +343,6 @@ void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p) } } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing message from client: {0}", ex.what()); + LogError("Error parsing message from client: {0}", ex.what()); } } diff --git a/common/opcodemgr.cpp b/common/opcodemgr.cpp index 2190db4a7..11d1b4811 100644 --- a/common/opcodemgr.cpp +++ b/common/opcodemgr.cpp @@ -32,7 +32,7 @@ OpcodeManager::OpcodeManager() { bool OpcodeManager::LoadOpcodesFile(const char *filename, OpcodeSetStrategy *s, bool report_errors) { FILE *opf = fopen(filename, "r"); if(opf == nullptr) { - Log(Logs::General, Logs::Error, "Unable to open opcodes file '%s'", filename); + LogError("Unable to open opcodes file [{}]", filename); return(false); } diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 322e187e4..cfc3458c3 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -85,7 +85,7 @@ namespace RoF //TODO: figure out how to support shared memory with multiple patches... opcodes = new RegularOpcodeManager(); if (!opcodes->LoadOpcodes(opfile.c_str())) { - Log(Logs::General, Logs::Netcode, "[OPCODES] Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + LogNetcode("[OPCODES] Error loading opcodes file [{}]. Not registering patch [{}]", opfile.c_str(), name); return; } } @@ -108,7 +108,7 @@ namespace RoF signature.first_length = sizeof(structs::ClientZoneEntry_Struct); signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Registered patch %s", name); + LogNetcode("[StreamIdentify] Registered patch [{}]", name); } void Reload() @@ -125,10 +125,10 @@ namespace RoF opfile += name; opfile += ".conf"; if (!opcodes->ReloadOpcodes(opfile.c_str())) { - Log(Logs::General, Logs::Netcode, "[OPCODES] Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + LogNetcode("[OPCODES] Error reloading opcodes file [{}] for patch [{}]", opfile.c_str(), name); return; } - Log(Logs::General, Logs::Netcode, "[OPCODES] Reloaded opcodes for patch %s", name); + LogNetcode("[OPCODES] Reloaded opcodes for patch [{}]", name); } } @@ -350,7 +350,7 @@ namespace RoF if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); delete in; return; } @@ -589,7 +589,7 @@ namespace RoF for (int index = 0; index < item_count; ++index, ++eq) { SerializeItem(ob, (const EQEmu::ItemInstance*)eq->inst, eq->slot_id, 0, ItemPacketCharInventory); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Netcode, "RoF::ENCODE(OP_CharInventory) Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + LogNetcode("RoF::ENCODE(OP_CharInventory) Serialization failed on item slot [{}] during OP_CharInventory. Item skipped", eq->slot_id); last_pos = ob.tellp(); } @@ -1521,7 +1521,7 @@ namespace RoF SerializeItem(ob, (const EQEmu::ItemInstance*)int_struct->inst, int_struct->slot_id, 0, old_item_pkt->PacketType); if (ob.tellp() == last_pos) { - Log(Logs::General, Logs::Netcode, "RoF::ENCODE(OP_ItemPacket) Serialization failed on item slot %d.", int_struct->slot_id); + LogNetcode("RoF::ENCODE(OP_ItemPacket) Serialization failed on item slot [{}]", int_struct->slot_id); delete in; return; } @@ -2602,7 +2602,7 @@ namespace RoF outapp->WriteUInt8(0); // Unknown - Log(Logs::General, Logs::Netcode, "[STRUCTS] Player Profile Packet is %i bytes", outapp->GetWritePosition()); + LogNetcode("[STRUCTS] Player Profile Packet is [{}] bytes", outapp->GetWritePosition()); auto NewBuffer = new unsigned char[outapp->GetWritePosition()]; memcpy(NewBuffer, outapp->pBuffer, outapp->GetWritePosition()); @@ -3453,7 +3453,7 @@ namespace RoF if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); delete in; return; } @@ -3804,7 +3804,7 @@ namespace RoF //determine and verify length int entrycount = in->size / sizeof(Spawn_Struct); if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); delete in; return; } @@ -4052,7 +4052,7 @@ namespace RoF Buffer += 29; if (Buffer != (BufferStart + PacketSize)) { - Log(Logs::General, Logs::Netcode, "[ERROR] SPAWN ENCODE LOGIC PROBLEM: Buffer pointer is now %i from end", Buffer - (BufferStart + PacketSize)); + LogNetcode("[ERROR] SPAWN ENCODE LOGIC PROBLEM: Buffer pointer is now [{}] from end", Buffer - (BufferStart + PacketSize)); } //Log.LogDebugType(Logs::General, Logs::Netcode, "[ERROR] Sending zone spawn for %s packet is %i bytes", emu->name, outapp->size); //Log.Hex(Logs::Netcode, outapp->pBuffer, outapp->size); @@ -4621,7 +4621,7 @@ namespace RoF return; } default: - Log(Logs::Detail, Logs::Netcode, "Unhandled OP_GuildBank action"); + LogNetcode("Unhandled OP_GuildBank action"); __packet->SetOpcode(OP_Unknown); /* invalidate the packet */ return; } @@ -5678,7 +5678,7 @@ namespace RoF RoFSlot = server_corpse_slot; } - Log(Logs::Detail, Logs::Netcode, "Convert Server Corpse Slot %i to RoF Corpse Main Slot %i", server_corpse_slot, RoFSlot); + LogNetcode("Convert Server Corpse Slot [{}] to RoF Corpse Main Slot [{}]", server_corpse_slot, RoFSlot); return RoFSlot; } diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index a6cf7a134..2dc361b26 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -85,7 +85,7 @@ namespace RoF2 //TODO: figure out how to support shared memory with multiple patches... opcodes = new RegularOpcodeManager(); if (!opcodes->LoadOpcodes(opfile.c_str())) { - Log(Logs::General, Logs::Netcode, "[OPCODES] Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + LogNetcode("[OPCODES] Error loading opcodes file [{}]. Not registering patch [{}]", opfile.c_str(), name); return; } } @@ -111,7 +111,7 @@ namespace RoF2 - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Registered patch %s", name); + LogNetcode("[StreamIdentify] Registered patch [{}]", name); } void Reload() @@ -128,10 +128,10 @@ namespace RoF2 opfile += name; opfile += ".conf"; if (!opcodes->ReloadOpcodes(opfile.c_str())) { - Log(Logs::General, Logs::Netcode, "[OPCODES] Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + LogNetcode("[OPCODES] Error reloading opcodes file [{}] for patch [{}]", opfile.c_str(), name); return; } - Log(Logs::General, Logs::Netcode, "[OPCODES] Reloaded opcodes for patch %s", name); + LogNetcode("[OPCODES] Reloaded opcodes for patch [{}]", name); } } @@ -419,7 +419,7 @@ namespace RoF2 if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); delete in; return; } @@ -657,7 +657,7 @@ namespace RoF2 for (int index = 0; index < item_count; ++index, ++eq) { SerializeItem(ob, (const EQEmu::ItemInstance*)eq->inst, eq->slot_id, 0, ItemPacketCharInventory); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Netcode, "RoF2::ENCODE(OP_CharInventory) Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + LogNetcode("RoF2::ENCODE(OP_CharInventory) Serialization failed on item slot [{}] during OP_CharInventory. Item skipped", eq->slot_id); last_pos = ob.tellp(); } @@ -1589,7 +1589,7 @@ namespace RoF2 SerializeItem(ob, (const EQEmu::ItemInstance*)int_struct->inst, int_struct->slot_id, 0, old_item_pkt->PacketType); if (ob.tellp() == last_pos) { - Log(Logs::General, Logs::Netcode, "RoF2::ENCODE(OP_ItemPacket) Serialization failed on item slot %d.", int_struct->slot_id); + LogNetcode("RoF2::ENCODE(OP_ItemPacket) Serialization failed on item slot [{}]", int_struct->slot_id); delete in; return; } @@ -2688,7 +2688,7 @@ namespace RoF2 // Think we need 1 byte of padding at the end outapp->WriteUInt8(0); // Unknown - Log(Logs::General, Logs::Netcode, "[STRUCTS] Player Profile Packet is %i bytes", outapp->GetWritePosition()); + LogNetcode("[STRUCTS] Player Profile Packet is [{}] bytes", outapp->GetWritePosition()); auto NewBuffer = new unsigned char[outapp->GetWritePosition()]; memcpy(NewBuffer, outapp->pBuffer, outapp->GetWritePosition()); @@ -3520,7 +3520,7 @@ namespace RoF2 if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); delete in; return; } @@ -3630,7 +3630,7 @@ namespace RoF2 OUT(TraderID); snprintf(eq->SerialNumber, sizeof(eq->SerialNumber), "%016d", emu->ItemID); - Log(Logs::Detail, Logs::Trading, "ENCODE(OP_TraderDelItem): TraderID %d, SerialNumber: %d", emu->TraderID, emu->ItemID); + LogTrading("ENCODE(OP_TraderDelItem): TraderID [{}], SerialNumber: [{}]", emu->TraderID, emu->ItemID); FINISH_ENCODE(); } @@ -3661,7 +3661,7 @@ namespace RoF2 eq->Traders2 = emu->Traders; eq->Items2 = emu->Items; - Log(Logs::Detail, Logs::Trading, "ENCODE(OP_TraderShop): BazaarWelcome_Struct Code %d, Traders %d, Items %d", + LogTrading("ENCODE(OP_TraderShop): BazaarWelcome_Struct Code [{}], Traders [{}], Items [{}]", eq->Code, eq->Traders, eq->Items); FINISH_ENCODE(); @@ -3684,14 +3684,14 @@ namespace RoF2 OUT(Quantity); snprintf(eq->SerialNumber, sizeof(eq->SerialNumber), "%016d", emu->ItemID); - Log(Logs::Detail, Logs::Trading, "ENCODE(OP_TraderShop): Buy Action %d, Price %d, Trader %d, ItemID %d, Quantity %d, ItemName, %s", + LogTrading("ENCODE(OP_TraderShop): Buy Action [{}], Price [{}], Trader [{}], ItemID [{}], Quantity [{}], ItemName, [{}]", eq->Action, eq->Price, eq->TraderID, eq->ItemID, eq->Quantity, emu->ItemName); FINISH_ENCODE(); } else { - Log(Logs::Detail, Logs::Trading, "ENCODE(OP_TraderShop): Encode Size Unknown (%d)", psize); + LogTrading("ENCODE(OP_TraderShop): Encode Size Unknown ([{}])", psize); } } @@ -3946,7 +3946,7 @@ namespace RoF2 //determine and verify length int entrycount = in->size / sizeof(Spawn_Struct); if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); delete in; return; } @@ -4278,7 +4278,7 @@ namespace RoF2 Buffer += 29; if (Buffer != (BufferStart + PacketSize)) { - Log(Logs::General, Logs::Netcode, "[ERROR] SPAWN ENCODE LOGIC PROBLEM: Buffer pointer is now %i from end", Buffer - (BufferStart + PacketSize)); + LogNetcode("[ERROR] SPAWN ENCODE LOGIC PROBLEM: Buffer pointer is now [{}] from end", Buffer - (BufferStart + PacketSize)); } //Log.LogDebugType(Logs::General, Logs::Netcode, "[ERROR] Sending zone spawn for %s packet is %i bytes", emu->name, outapp->size); //Log.Hex(Logs::Netcode, outapp->pBuffer, outapp->size); @@ -4859,7 +4859,7 @@ namespace RoF2 return; } default: - Log(Logs::Detail, Logs::Netcode, "Unhandled OP_GuildBank action"); + LogNetcode("Unhandled OP_GuildBank action"); __packet->SetOpcode(OP_Unknown); /* invalidate the packet */ return; } @@ -5012,7 +5012,7 @@ namespace RoF2 emu->to_slot = RoF2ToServerSlot(eq->to_slot); IN(number_in_stack); - //Log(Logs::General, Logs::Netcode, "[RoF2] MoveItem Slot from %u to %u, Number %u", emu->from_slot, emu->to_slot, emu->number_in_stack); + //LogNetcode("[RoF2] MoveItem Slot from [{}] to [{}], Number [{}]", emu->from_slot, emu->to_slot, emu->number_in_stack); FINISH_DIRECT_DECODE(); } @@ -5245,7 +5245,7 @@ namespace RoF2 IN(Code); IN(TraderID); IN(Approval); - Log(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): TraderClick_Struct Code %d, TraderID %d, Approval %d", + LogTrading("DECODE(OP_TraderShop): TraderClick_Struct Code [{}], TraderID [{}], Approval [{}]", eq->Code, eq->TraderID, eq->Approval); FINISH_DIRECT_DECODE(); @@ -5259,7 +5259,7 @@ namespace RoF2 emu->Beginning.Action = eq->Code; IN(Traders); IN(Items); - Log(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): BazaarWelcome_Struct Code %d, Traders %d, Items %d", + LogTrading("DECODE(OP_TraderShop): BazaarWelcome_Struct Code [{}], Traders [{}], Items [{}]", eq->Code, eq->Traders, eq->Items); FINISH_DIRECT_DECODE(); @@ -5276,20 +5276,20 @@ namespace RoF2 memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); IN(ItemID); IN(Quantity); - Log(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): TraderBuy_Struct (Unknowns) Unknown004 %d, Unknown008 %d, Unknown012 %d, Unknown076 %d, Unknown276 %d", + LogTrading("DECODE(OP_TraderShop): TraderBuy_Struct (Unknowns) Unknown004 [{}], Unknown008 [{}], Unknown012 [{}], Unknown076 [{}], Unknown276 [{}]", eq->Unknown004, eq->Unknown008, eq->Unknown012, eq->Unknown076, eq->Unknown276); - Log(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): TraderBuy_Struct Buy Action %d, Price %d, Trader %d, ItemID %d, Quantity %d, ItemName, %s", + LogTrading("DECODE(OP_TraderShop): TraderBuy_Struct Buy Action [{}], Price [{}], Trader [{}], ItemID [{}], Quantity [{}], ItemName, [{}]", eq->Action, eq->Price, eq->TraderID, eq->ItemID, eq->Quantity, eq->ItemName); FINISH_DIRECT_DECODE(); } else if (psize == 4) { - Log(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): Forwarding packet as-is with size 4"); + LogTrading("DECODE(OP_TraderShop): Forwarding packet as-is with size 4"); } else { - Log(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): Decode Size Unknown (%d)", psize); + LogTrading("DECODE(OP_TraderShop): Decode Size Unknown ([{}])", psize); } } @@ -5966,7 +5966,7 @@ namespace RoF2 RoF2Slot = server_corpse_slot; } - Log(Logs::Detail, Logs::Netcode, "Convert Server Corpse Slot %i to RoF2 Corpse Main Slot %i", server_corpse_slot, RoF2Slot); + LogNetcode("Convert Server Corpse Slot [{}] to RoF2 Corpse Main Slot [{}]", server_corpse_slot, RoF2Slot); return RoF2Slot; } @@ -6149,7 +6149,7 @@ namespace RoF2 ServerSlot = rof2_corpse_slot; } - Log(Logs::Detail, Logs::Netcode, "Convert RoF2 Corpse Main Slot %i to Server Corpse Slot %i", rof2_corpse_slot, ServerSlot); + LogNetcode("Convert RoF2 Corpse Main Slot [{}] to Server Corpse Slot [{}]", rof2_corpse_slot, ServerSlot); return ServerSlot; } diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 11aae5cc9..2c05c1425 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -79,7 +79,7 @@ namespace SoD //TODO: figure out how to support shared memory with multiple patches... opcodes = new RegularOpcodeManager(); if (!opcodes->LoadOpcodes(opfile.c_str())) { - Log(Logs::General, Logs::Netcode, "[OPCODES] Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + LogNetcode("[OPCODES] Error loading opcodes file [{}]. Not registering patch [{}]", opfile.c_str(), name); return; } } @@ -105,7 +105,7 @@ namespace SoD - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Registered patch %s", name); + LogNetcode("[StreamIdentify] Registered patch [{}]", name); } void Reload() @@ -122,10 +122,10 @@ namespace SoD opfile += name; opfile += ".conf"; if (!opcodes->ReloadOpcodes(opfile.c_str())) { - Log(Logs::General, Logs::Netcode, "[OPCODES] Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + LogNetcode("[OPCODES] Error reloading opcodes file [{}] for patch [{}]", opfile.c_str(), name); return; } - Log(Logs::General, Logs::Netcode, "[OPCODES] Reloaded opcodes for patch %s", name); + LogNetcode("[OPCODES] Reloaded opcodes for patch [{}]", name); } } @@ -277,7 +277,7 @@ namespace SoD if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); delete in; return; } @@ -400,7 +400,7 @@ namespace SoD for (int index = 0; index < item_count; ++index, ++eq) { SerializeItem(ob, (const EQEmu::ItemInstance*)eq->inst, eq->slot_id, 0); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Netcode, "SoD::ENCODE(OP_CharInventory) Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + LogNetcode("SoD::ENCODE(OP_CharInventory) Serialization failed on item slot [{}] during OP_CharInventory. Item skipped", eq->slot_id); last_pos = ob.tellp(); } @@ -1069,7 +1069,7 @@ namespace SoD SerializeItem(ob, (const EQEmu::ItemInstance*)int_struct->inst, int_struct->slot_id, 0); if (ob.tellp() == last_pos) { - Log(Logs::General, Logs::Netcode, "SoD::ENCODE(OP_ItemPacket) Serialization failed on item slot %d.", int_struct->slot_id); + LogNetcode("SoD::ENCODE(OP_ItemPacket) Serialization failed on item slot [{}]", int_struct->slot_id); delete in; return; } @@ -2198,7 +2198,7 @@ namespace SoD if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); delete in; return; } @@ -2435,7 +2435,7 @@ namespace SoD //determine and verify length int entrycount = in->size / sizeof(Spawn_Struct); if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); delete in; return; } @@ -3848,7 +3848,7 @@ namespace SoD SoDSlot = serverSlot; } - Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to SoD Slot %i", serverSlot, SoDSlot); + LogNetcode("Convert Server Slot [{}] to SoD Slot [{}]", serverSlot, SoDSlot); return SoDSlot; } @@ -3865,7 +3865,7 @@ namespace SoD SoDSlot = server_corpse_slot - 2; } - Log(Logs::Detail, Logs::Netcode, "Convert Server Corpse Slot %i to SoD Corpse Slot %i", server_corpse_slot, SoDSlot); + LogNetcode("Convert Server Corpse Slot [{}] to SoD Corpse Slot [{}]", server_corpse_slot, SoDSlot); return SoDSlot; } @@ -3930,7 +3930,7 @@ namespace SoD server_slot = sod_slot; } - Log(Logs::Detail, Logs::Netcode, "Convert SoD Slot %i to Server Slot %i", sod_slot, server_slot); + LogNetcode("Convert SoD Slot [{}] to Server Slot [{}]", sod_slot, server_slot); return server_slot; } @@ -3947,7 +3947,7 @@ namespace SoD server_slot = sod_corpse_slot + 2; } - Log(Logs::Detail, Logs::Netcode, "Convert SoD Corpse Slot %i to Server Corpse Slot %i", sod_corpse_slot, server_slot); + LogNetcode("Convert SoD Corpse Slot [{}] to Server Corpse Slot [{}]", sod_corpse_slot, server_slot); return server_slot; } diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index bb310a76c..0d2189740 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -79,7 +79,7 @@ namespace SoF //TODO: figure out how to support shared memory with multiple patches... opcodes = new RegularOpcodeManager(); if (!opcodes->LoadOpcodes(opfile.c_str())) { - Log(Logs::General, Logs::Netcode, "[OPCODES] Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + LogNetcode("[OPCODES] Error loading opcodes file [{}]. Not registering patch [{}]", opfile.c_str(), name); return; } } @@ -103,7 +103,7 @@ namespace SoF signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Registered patch %s", name); + LogNetcode("[StreamIdentify] Registered patch [{}]", name); } void Reload() @@ -120,10 +120,10 @@ namespace SoF opfile += name; opfile += ".conf"; if (!opcodes->ReloadOpcodes(opfile.c_str())) { - Log(Logs::General, Logs::Netcode, "[OPCODES] Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + LogNetcode("[OPCODES] Error reloading opcodes file [{}] for patch [{}]", opfile.c_str(), name); return; } - Log(Logs::General, Logs::Netcode, "[OPCODES] Reloaded opcodes for patch %s", name); + LogNetcode("[OPCODES] Reloaded opcodes for patch [{}]", name); } } @@ -380,7 +380,7 @@ namespace SoF for (int index = 0; index < item_count; ++index, ++eq) { SerializeItem(ob, (const EQEmu::ItemInstance*)eq->inst, eq->slot_id, 0); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Netcode, "SoF::ENCODE(OP_CharInventory) Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + LogNetcode("SoF::ENCODE(OP_CharInventory) Serialization failed on item slot [{}] during OP_CharInventory. Item skipped", eq->slot_id); last_pos = ob.tellp(); } @@ -864,7 +864,7 @@ namespace SoF SerializeItem(ob, (const EQEmu::ItemInstance*)int_struct->inst, int_struct->slot_id, 0); if (ob.tellp() == last_pos) { - Log(Logs::General, Logs::Netcode, "SoF::ENCODE(OP_ItemPacket) Serialization failed on item slot %d.", int_struct->slot_id); + LogNetcode("SoF::ENCODE(OP_ItemPacket) Serialization failed on item slot [{}]", int_struct->slot_id); delete in; return; } @@ -1827,7 +1827,7 @@ namespace SoF if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); delete in; return; } @@ -1989,7 +1989,7 @@ namespace SoF //determine and verify length int entrycount = in->size / sizeof(Spawn_Struct); if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); delete in; return; } @@ -3244,7 +3244,7 @@ namespace SoF sof_slot = server_slot; } - Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to SoF Slot %i", server_slot, sof_slot); + LogNetcode("Convert Server Slot [{}] to SoF Slot [{}]", server_slot, sof_slot); return sof_slot; } @@ -3330,7 +3330,7 @@ namespace SoF server_slot = sof_slot; } - Log(Logs::Detail, Logs::Netcode, "Convert SoF Slot %i to Server Slot %i", sof_slot, server_slot); + LogNetcode("Convert SoF Slot [{}] to Server Slot [{}]", sof_slot, server_slot); return server_slot; } diff --git a/common/patches/ss_define.h b/common/patches/ss_define.h index bd5c065c5..57bf6b1b4 100644 --- a/common/patches/ss_define.h +++ b/common/patches/ss_define.h @@ -87,14 +87,14 @@ //check length of packet before decoding. Call before setup. #define ENCODE_LENGTH_EXACT(struct_) \ if((*p)->size != sizeof(struct_)) { \ - Log(Logs::Detail, Logs::Netcode, "Wrong size on outbound %s (" #struct_ "): Got %d, expected %d", opcodes->EmuToName((*p)->GetOpcode()), (*p)->size, sizeof(struct_)); \ + LogNetcode("Wrong size on outbound [{}] (" #struct_ "): Got [{}], expected [{}]", opcodes->EmuToName((*p)->GetOpcode()), (*p)->size, sizeof(struct_)); \ delete *p; \ *p = nullptr; \ return; \ } #define ENCODE_LENGTH_ATLEAST(struct_) \ if((*p)->size < sizeof(struct_)) { \ - Log(Logs::Detail, Logs::Netcode, "Wrong size on outbound %s (" #struct_ "): Got %d, expected at least %d", opcodes->EmuToName((*p)->GetOpcode()), (*p)->size, sizeof(struct_)); \ + LogNetcode("Wrong size on outbound [{}] (" #struct_ "): Got [{}], expected at least [{}]", opcodes->EmuToName((*p)->GetOpcode()), (*p)->size, sizeof(struct_)); \ delete *p; \ *p = nullptr; \ return; \ @@ -153,13 +153,13 @@ //check length of packet before decoding. Call before setup. #define DECODE_LENGTH_EXACT(struct_) \ if(__packet->size != sizeof(struct_)) { \ - Log(Logs::Detail, Logs::Netcode, "Wrong size on incoming %s (" #struct_ "): Got %d, expected %d", opcodes->EmuToName(__packet->GetOpcode()), __packet->size, sizeof(struct_)); \ + LogNetcode("Wrong size on incoming [{}] (" #struct_ "): Got [{}], expected [{}]", opcodes->EmuToName(__packet->GetOpcode()), __packet->size, sizeof(struct_)); \ __packet->SetOpcode(OP_Unknown); /* invalidate the packet */ \ return; \ } #define DECODE_LENGTH_ATLEAST(struct_) \ if(__packet->size < sizeof(struct_)) { \ - Log(Logs::Detail, Logs::Netcode, "Wrong size on incoming %s (" #struct_ "): Got %d, expected at least %d", opcodes->EmuToName(__packet->GetOpcode()), __packet->size, sizeof(struct_)); \ + LogNetcode("Wrong size on incoming [{}] (" #struct_ "): Got [{}], expected at least [{}]", opcodes->EmuToName(__packet->GetOpcode()), __packet->size, sizeof(struct_)); \ __packet->SetOpcode(OP_Unknown); /* invalidate the packet */ \ return; \ } diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index b50df595c..5cb8e5897 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -78,7 +78,7 @@ namespace Titanium //TODO: figure out how to support shared memory with multiple patches... opcodes = new RegularOpcodeManager(); if (!opcodes->LoadOpcodes(opfile.c_str())) { - Log(Logs::General, Logs::Netcode, "[OPCODES] Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + LogNetcode("[OPCODES] Error loading opcodes file [{}]. Not registering patch [{}]", opfile.c_str(), name); return; } } @@ -104,7 +104,7 @@ namespace Titanium - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Registered patch %s", name); + LogNetcode("[StreamIdentify] Registered patch [{}]", name); } void Reload() @@ -121,10 +121,10 @@ namespace Titanium opfile += name; opfile += ".conf"; if (!opcodes->ReloadOpcodes(opfile.c_str())) { - Log(Logs::General, Logs::Netcode, "[OPCODES] Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + LogNetcode("[OPCODES] Error reloading opcodes file [{}] for patch [{}]", opfile.c_str(), name); return; } - Log(Logs::General, Logs::Netcode, "[OPCODES] Reloaded opcodes for patch %s", name); + LogNetcode("[OPCODES] Reloaded opcodes for patch [{}]", name); } } @@ -337,7 +337,7 @@ namespace Titanium for (int r = 0; r < itemcount; r++, eq++) { SerializeItem(ob, (const EQEmu::ItemInstance*)eq->inst, ServerToTitaniumSlot(eq->slot_id), 0); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Netcode, "Titanium::ENCODE(OP_CharInventory) Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + LogNetcode("Titanium::ENCODE(OP_CharInventory) Serialization failed on item slot [{}] during OP_CharInventory. Item skipped", eq->slot_id); last_pos = ob.tellp(); } @@ -822,7 +822,7 @@ namespace Titanium SerializeItem(ob, (const EQEmu::ItemInstance*)int_struct->inst, ServerToTitaniumSlot(int_struct->slot_id), 0); if (ob.tellp() == last_pos) { - Log(Logs::General, Logs::Netcode, "Titanium::ENCODE(OP_ItemPacket) Serialization failed on item slot %d.", int_struct->slot_id); + LogNetcode("Titanium::ENCODE(OP_ItemPacket) Serialization failed on item slot [{}]", int_struct->slot_id); delete in; return; } @@ -1510,7 +1510,7 @@ namespace Titanium if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); delete in; return; } @@ -1628,7 +1628,7 @@ namespace Titanium //determine and verify length int entrycount = in->size / sizeof(Spawn_Struct); if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); delete in; return; } @@ -2540,7 +2540,7 @@ namespace Titanium titanium_slot = server_slot; } - Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to Titanium Slot %i", server_slot, titanium_slot); + LogNetcode("Convert Server Slot [{}] to Titanium Slot [{}]", server_slot, titanium_slot); return titanium_slot; } @@ -2627,7 +2627,7 @@ namespace Titanium server_slot = titanium_slot; } - Log(Logs::Detail, Logs::Netcode, "Convert Titanium Slot %i to Server Slot %i", titanium_slot, server_slot); + LogNetcode("Convert Titanium Slot [{}] to Server Slot [{}]", titanium_slot, server_slot); return server_slot; } @@ -2648,7 +2648,7 @@ namespace Titanium server_slot = titanium_corpse_slot + 4; } - Log(Logs::Detail, Logs::Netcode, "Convert Titanium Corpse Slot %i to Server Corpse Slot %i", titanium_corpse_slot, server_slot); + LogNetcode("Convert Titanium Corpse Slot [{}] to Server Corpse Slot [{}]", titanium_corpse_slot, server_slot); return server_slot; } diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index b644c9554..0d44d7d1a 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -79,7 +79,7 @@ namespace UF //TODO: figure out how to support shared memory with multiple patches... opcodes = new RegularOpcodeManager(); if (!opcodes->LoadOpcodes(opfile.c_str())) { - Log(Logs::General, Logs::Netcode, "[OPCODES] Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + LogNetcode("[OPCODES] Error loading opcodes file [{}]. Not registering patch [{}]", opfile.c_str(), name); return; } } @@ -105,7 +105,7 @@ namespace UF - Log(Logs::General, Logs::Netcode, "[IDENTIFY] Registered patch %s", name); + LogNetcode("[StreamIdentify] Registered patch [{}]", name); } void Reload() @@ -122,10 +122,10 @@ namespace UF opfile += name; opfile += ".conf"; if (!opcodes->ReloadOpcodes(opfile.c_str())) { - Log(Logs::General, Logs::Netcode, "[OPCODES] Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + LogNetcode("[OPCODES] Error reloading opcodes file [{}] for patch [{}]", opfile.c_str(), name); return; } - Log(Logs::General, Logs::Netcode, "[OPCODES] Reloaded opcodes for patch %s", name); + LogNetcode("[OPCODES] Reloaded opcodes for patch [{}]", name); } } @@ -330,7 +330,7 @@ namespace UF if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); delete in; return; } @@ -520,7 +520,7 @@ namespace UF for (int index = 0; index < item_count; ++index, ++eq) { SerializeItem(ob, (const EQEmu::ItemInstance*)eq->inst, eq->slot_id, 0); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Netcode, "UF::ENCODE(OP_CharInventory) Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + LogNetcode("UF::ENCODE(OP_CharInventory) Serialization failed on item slot [{}] during OP_CharInventory. Item skipped", eq->slot_id); last_pos = ob.tellp(); } @@ -1277,7 +1277,7 @@ namespace UF SerializeItem(ob, (const EQEmu::ItemInstance*)int_struct->inst, int_struct->slot_id, 0); if (ob.tellp() == last_pos) { - Log(Logs::General, Logs::Netcode, "UF::ENCODE(OP_ItemPacket) Serialization failed on item slot %d.", int_struct->slot_id); + LogNetcode("UF::ENCODE(OP_ItemPacket) Serialization failed on item slot [{}]", int_struct->slot_id); delete in; return; } @@ -2472,7 +2472,7 @@ namespace UF if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); delete in; return; } @@ -2704,7 +2704,7 @@ namespace UF //determine and verify length int entrycount = in->size / sizeof(Spawn_Struct); if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); delete in; return; } @@ -4206,7 +4206,7 @@ namespace UF UFSlot = serverSlot; } - Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to UF Slot %i", serverSlot, UFSlot); + LogNetcode("Convert Server Slot [{}] to UF Slot [{}]", serverSlot, UFSlot); return UFSlot; } @@ -4223,7 +4223,7 @@ namespace UF UFSlot = serverCorpseSlot - 2; } - Log(Logs::Detail, Logs::Netcode, "Convert Server Corpse Slot %i to UF Corpse Slot %i", serverCorpseSlot, UFSlot); + LogNetcode("Convert Server Corpse Slot [{}] to UF Corpse Slot [{}]", serverCorpseSlot, UFSlot); return UFSlot; } @@ -4288,7 +4288,7 @@ namespace UF ServerSlot = ufSlot; } - Log(Logs::Detail, Logs::Netcode, "Convert UF Slot %i to Server Slot %i", ufSlot, ServerSlot); + LogNetcode("Convert UF Slot [{}] to Server Slot [{}]", ufSlot, ServerSlot); return ServerSlot; } @@ -4305,7 +4305,7 @@ namespace UF ServerSlot = ufCorpseSlot + 2; } - Log(Logs::Detail, Logs::Netcode, "Convert UF Corpse Slot %i to Server Corpse Slot %i", ufCorpseSlot, ServerSlot); + LogNetcode("Convert UF Corpse Slot [{}] to Server Corpse Slot [{}]", ufCorpseSlot, ServerSlot); return ServerSlot; } diff --git a/common/platform.cpp b/common/platform.cpp index 4955d6639..b67267155 100644 --- a/common/platform.cpp +++ b/common/platform.cpp @@ -1,3 +1,23 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ + #include "platform.h" EQEmuExePlatform exe_platform = ExePlatformNone; @@ -10,6 +30,44 @@ const EQEmuExePlatform& GetExecutablePlatform() { return exe_platform; } +/** + * @return + */ int GetExecutablePlatformInt(){ return exe_platform; +} + +/** + * Returns platform name by string + * + * @return + */ +std::string GetPlatformName() +{ + switch (GetExecutablePlatformInt()) { + case EQEmuExePlatform::ExePlatformWorld: + return "WorldServer"; + case EQEmuExePlatform::ExePlatformQueryServ: + return "QueryServer"; + case EQEmuExePlatform::ExePlatformZone: + return "ZoneServer"; + case EQEmuExePlatform::ExePlatformUCS: + return "UCS"; + case EQEmuExePlatform::ExePlatformLogin: + return "LoginServer"; + case EQEmuExePlatform::ExePlatformSocket_Server: + return "SocketServer"; + case EQEmuExePlatform::ExePlatformSharedMemory: + return "SharedMemory"; + case EQEmuExePlatform::ExePlatformClientImport: + return "ClientImport"; + case EQEmuExePlatform::ExePlatformClientExport: + return "ClientExport"; + case EQEmuExePlatform::ExePlatformLaunch: + return "Launch"; + case EQEmuExePlatform::ExePlatformHC: + return "HC"; + default: + return ""; + } } \ No newline at end of file diff --git a/common/platform.h b/common/platform.h index 8eb765257..bd24a1493 100644 --- a/common/platform.h +++ b/common/platform.h @@ -1,6 +1,28 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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_PLATFORM_H #define EQEMU_PLATFORM_H +#include "iostream" + enum EQEmuExePlatform { ExePlatformNone = 0, @@ -20,5 +42,6 @@ enum EQEmuExePlatform void RegisterExecutablePlatform(EQEmuExePlatform p); const EQEmuExePlatform& GetExecutablePlatform(); int GetExecutablePlatformInt(); +std::string GetPlatformName(); #endif diff --git a/common/ptimer.cpp b/common/ptimer.cpp index 5a3d9617f..032ac5945 100644 --- a/common/ptimer.cpp +++ b/common/ptimer.cpp @@ -134,7 +134,6 @@ bool PersistentTimer::Load(Database *db) { (unsigned long)_char_id, _type); auto results = db->QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error in PersistentTimer::Load, error: %s", results.ErrorMessage().c_str()); return false; } @@ -165,9 +164,6 @@ bool PersistentTimer::Store(Database *db) { #endif auto results = db->QueryDatabase(query); if (!results.Success()) { -#if EQDEBUG > 5 - Log(Logs::General, Logs::Error, "Error in PersistentTimer::Store, error: %s", results.ErrorMessage().c_str()); -#endif return false; } @@ -185,9 +181,6 @@ bool PersistentTimer::Clear(Database *db) { auto results = db->QueryDatabase(query); if (!results.Success()) { -#if EQDEBUG > 5 - Log(Logs::General, Logs::Error, "Error in PersistentTimer::Clear, error: %s", results.ErrorMessage().c_str()); -#endif return false; } @@ -198,7 +191,7 @@ bool PersistentTimer::Clear(Database *db) { /* This function checks if the timer triggered */ bool PersistentTimer::Expired(Database *db, bool iReset) { if (this == nullptr) { - Log(Logs::General, Logs::Error, "Null timer during ->Check()!?\n"); + LogError("Null timer during ->Check()!?\n"); return(true); } uint32 current_time = get_current_time(); @@ -289,9 +282,6 @@ bool PTimerList::Load(Database *db) { (unsigned long)_char_id); auto results = db->QueryDatabase(query); if (!results.Success()) { -#if EQDEBUG > 5 - Log(Logs::General, Logs::Error, "Error in PersistentTimer::Load, error: %s", results.ErrorMessage().c_str()); -#endif return false; } @@ -348,9 +338,6 @@ bool PTimerList::Clear(Database *db) { #endif auto results = db->QueryDatabase(query); if (!results.Success()) { -#if EQDEBUG > 5 - Log(Logs::General, Logs::Error, "Error in PersistentTimer::Clear, error: %s", results.ErrorMessage().c_str()); -#endif return false; } @@ -440,9 +427,6 @@ bool PTimerList::ClearOffline(Database *db, uint32 char_id, pTimerType type) { #endif auto results = db->QueryDatabase(query); if (!results.Success()) { -#if EQDEBUG > 5 - Log(Logs::General, Logs::Error, "Error in PTimerList::ClearOffline, error: %s", results.ErrorMessage().c_str()); -#endif return false; } diff --git a/common/rulesys.cpp b/common/rulesys.cpp index d4ff3d4d9..fed83d097 100644 --- a/common/rulesys.cpp +++ b/common/rulesys.cpp @@ -21,6 +21,7 @@ #include "string_util.h" #include #include +#include /* Commands: @@ -45,14 +46,14 @@ const char *RuleManager::s_categoryNames[_CatCount+1] = { const RuleManager::RuleInfo RuleManager::s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount+1] = { /* this is done in three steps so we can reliably get to them by index*/ - #define RULE_INT(cat, rule, default_value) \ - { #cat ":" #rule, Category__##cat, IntRule, Int__##rule }, + #define RULE_INT(cat, rule, default_value, notes) \ + { #cat ":" #rule, Category__##cat, IntRule, Int__##rule, notes }, #include "ruletypes.h" - #define RULE_REAL(cat, rule, default_value) \ - { #cat ":" #rule, Category__##cat, RealRule, Real__##rule }, + #define RULE_REAL(cat, rule, default_value, notes) \ + { #cat ":" #rule, Category__##cat, RealRule, Real__##rule, notes }, #include "ruletypes.h" - #define RULE_BOOL(cat, rule, default_value) \ - { #cat ":" #rule, Category__##cat, BoolRule, Bool__##rule }, + #define RULE_BOOL(cat, rule, default_value, notes) \ + { #cat ":" #rule, Category__##cat, BoolRule, Bool__##rule, notes }, #include "ruletypes.h" { "Invalid Rule", _CatCount, IntRule } }; @@ -145,11 +146,11 @@ bool RuleManager::SetRule(const char *rule_name, const char *rule_value, Databas switch(type) { case IntRule: m_RuleIntValues[index] = atoi(rule_value); - Log(Logs::Detail, Logs::Rules, "Set rule %s to value %d", rule_name, m_RuleIntValues[index]); + LogRules("Set rule [{}] to value [{}]", rule_name, m_RuleIntValues[index]); break; case RealRule: m_RuleRealValues[index] = atof(rule_value); - Log(Logs::Detail, Logs::Rules, "Set rule %s to value %.13f", rule_name, m_RuleRealValues[index]); + LogRules("Set rule [{}] to value [{}]", rule_name, m_RuleRealValues[index]); break; case BoolRule: uint32 val = 0; @@ -157,7 +158,7 @@ bool RuleManager::SetRule(const char *rule_name, const char *rule_value, Databas val = 1; m_RuleBoolValues[index] = val; - Log(Logs::Detail, Logs::Rules, "Set rule %s to value %s", rule_name, m_RuleBoolValues[index] == 1 ? "true" : "false"); + LogRules("Set rule [{}] to value [{}]", rule_name, m_RuleBoolValues[index] == 1 ? "true" : "false"); break; } @@ -178,11 +179,11 @@ void RuleManager::ResetRules(bool reload) { } Log(Logs::Detail, Logs::Rules, "Resetting running rules to default values"); - #define RULE_INT(cat, rule, default_value) \ + #define RULE_INT(cat, rule, default_value, notes) \ m_RuleIntValues[ Int__##rule ] = default_value; - #define RULE_REAL(cat, rule, default_value) \ + #define RULE_REAL(cat, rule, default_value, notes) \ m_RuleRealValues[ Real__##rule ] = default_value; - #define RULE_BOOL(cat, rule, default_value) \ + #define RULE_BOOL(cat, rule, default_value, notes) \ m_RuleBoolValues[ Bool__##rule ] = default_value; #include "ruletypes.h" @@ -214,19 +215,101 @@ bool RuleManager::_FindRule(const char *rule_name, RuleType &type_into, uint16 & //assumes index is valid! const char *RuleManager::_GetRuleName(RuleType type, uint16 index) { switch (type) { - case IntRule: - return(s_RuleInfo[index].name); - case RealRule: - return(s_RuleInfo[index + _IntRuleCount].name); - case BoolRule: - return(s_RuleInfo[index + _IntRuleCount + _RealRuleCount].name); + case IntRule: + return(s_RuleInfo[index].name); + case RealRule: + return(s_RuleInfo[index+_IntRuleCount].name); + case BoolRule: + return(s_RuleInfo[index+_IntRuleCount+_RealRuleCount].name); + default: + break; } //should never happen - return("InvalidRule??"); + return(s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount].name); // no need to create a string when one already exists... +} + +//assumes index is valid! +const std::string &RuleManager::_GetRuleNotes(RuleType type, uint16 index) { + switch (type) { + case IntRule: + return(s_RuleInfo[index].notes); + case RealRule: + return(s_RuleInfo[index+_IntRuleCount].notes); + case BoolRule: + return(s_RuleInfo[index+_IntRuleCount+_RealRuleCount].notes); + default: + break; + } + //should never happen + return(s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount].notes); +} + +bool RuleManager::LoadRules(Database *database, const char *ruleset_name, bool reload) { + + int ruleset_id = this->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); + } + + m_activeRuleset = ruleset_id; + m_activeName = ruleset_name; + + /* Load default ruleset values first if we're loading something other than default */ + if (strcasecmp(ruleset_name, "default") != 0) { + + std::string default_ruleset_name = "default"; + int default_ruleset_id = GetRulesetID(database, default_ruleset_name.c_str()); + + if (default_ruleset_id < 0) { + Log(Logs::Detail, + Logs::Rules, + "Failed to find default ruleset '%s' for load operation. Canceling.", + default_ruleset_name.c_str() + ); + + return (false); + } + + Log(Logs::Detail, Logs::Rules, "Processing rule set '%s' (%d) load...", default_ruleset_name.c_str(), default_ruleset_id); + + std::string query = StringFormat( + "SELECT `rule_name`, `rule_value` FROM `rule_values` WHERE `ruleset_id` = '%d'", + default_ruleset_id + ); + + auto results = database->QueryDatabase(query); + if (!results.Success()) { + return false; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + if (!SetRule(row[0], row[1], nullptr, false, reload)) { + Log(Logs::Detail, Logs::Rules, "Unable to interpret rule record for '%s'", row[0]); + } + } + } + + Log(Logs::Detail, Logs::Rules, "Processing rule set '%s' (%d) load...", ruleset_name, ruleset_id); + + std::string query = StringFormat("SELECT `rule_name`, `rule_value` FROM `rule_values` WHERE `ruleset_id` = '%d'", ruleset_id); + + auto results = database->QueryDatabase(query); + if (!results.Success()) { + return false; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + if (!SetRule(row[0], row[1], nullptr, false, reload)) { + Log(Logs::Detail, Logs::Rules, "Unable to interpret rule record for '%s'", row[0]); + } + } + + return true; } void RuleManager::SaveRules(Database *database, const char *ruleset_name) { - + if (ruleset_name != nullptr) { //saving to a specific name if (m_activeName != ruleset_name) { @@ -257,56 +340,6 @@ void RuleManager::SaveRules(Database *database, const char *ruleset_name) { } } -bool RuleManager::LoadRules(Database *database, const char *ruleset_name, bool reload) { - - int ruleset_id = this->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); - } - - Log(Logs::Detail, Logs::Rules, "Loading rule set '%s' (%d)", ruleset_name, ruleset_id); - - m_activeRuleset = ruleset_id; - m_activeName = ruleset_name; - - /* Load default ruleset values first if we're loading something other than default */ - if (strcasecmp(ruleset_name, "default") != 0) { - std::string default_ruleset_name = "default"; - int default_ruleset_id = GetRulesetID(database, default_ruleset_name.c_str()); - if (default_ruleset_id < 0) { - Log(Logs::Detail, Logs::Rules, "Failed to find default ruleset '%s' for load operation. Canceling.", - default_ruleset_name.c_str()); - return (false); - } - Log(Logs::Detail, Logs::Rules, "Loading rule set '%s' (%d)", default_ruleset_name.c_str(), default_ruleset_id); - - std::string query = StringFormat( - "SELECT rule_name, rule_value FROM rule_values WHERE ruleset_id = %d", - default_ruleset_id - ); - - auto results = database->QueryDatabase(query); - if (!results.Success()) - return false; - - for (auto row = results.begin(); row != results.end(); ++row) - if (!SetRule(row[0], row[1], nullptr, false, reload)) - Log(Logs::Detail, Logs::Rules, "Unable to interpret rule record for %s", row[0]); - } - - std::string query = StringFormat("SELECT rule_name, rule_value FROM rule_values WHERE ruleset_id=%d", ruleset_id); - auto results = database->QueryDatabase(query); - if (!results.Success()) - return false; - - for (auto row = results.begin(); row != results.end(); ++row) - if (!SetRule(row[0], row[1], nullptr, false, reload)) - Log(Logs::Detail, Logs::Rules, "Unable to interpret rule record for %s", row[0]); - - return true; -} - void RuleManager::_SaveRule(Database *database, RuleType type, uint16 index) { char value_string[100]; @@ -328,17 +361,241 @@ void RuleManager::_SaveRule(Database *database, RuleType type, uint16 index) { } std::string query = StringFormat( - "REPLACE INTO rule_values " - "(ruleset_id, rule_name, rule_value) " - " VALUES(%d, '%s', '%s')", + "REPLACE INTO `rule_values`" + "(`ruleset_id`, `rule_name`, `rule_value`, `notes`)" + " VALUES('%d', '%s', '%s', '%s')", m_activeRuleset, _GetRuleName(type, index), - value_string + value_string, + EscapeString(_GetRuleNotes(type, index)).c_str() ); database->QueryDatabase(query); } +bool RuleManager::UpdateInjectedRules(Database *db, const char *ruleset_name, bool quiet_update) +{ + std::vector database_data; + std::map> rule_data; + std::vector> injected_rule_entries; + + if (ruleset_name == nullptr) { + return false; + } + + int ruleset_id = GetRulesetID(db, ruleset_name); + if (ruleset_id < 0) { + return false; + } + + // load database rule names + std::string query(StringFormat("SELECT `rule_name` FROM `rule_values` WHERE `ruleset_id` = '%i'", ruleset_id)); + + auto results = db->QueryDatabase(query); + if (!results.Success()) { + return false; + } + + // build database data entries + for (auto row : results) { + database_data.push_back(std::string(row[0])); + } + + // build rule data entries + for (const auto &ri_iter : s_RuleInfo) { + if (strcasecmp(ri_iter.name, "Invalid Rule") == 0) { + continue; + } + + char buffer[100]; + + switch (ri_iter.type) { + case IntRule: + sprintf(buffer, "%d", m_RuleIntValues[ri_iter.rule_index]); + rule_data[ri_iter.name].first = buffer; + rule_data[ri_iter.name].second = &ri_iter.notes; + break; + case RealRule: + sprintf(buffer, "%.13f", m_RuleRealValues[ri_iter.rule_index]); + rule_data[ri_iter.name].first = buffer; + rule_data[ri_iter.name].second = &ri_iter.notes; + break; + case BoolRule: + sprintf(buffer, "%s", (m_RuleBoolValues[ri_iter.rule_index] ? "true" : "false")); + rule_data[ri_iter.name].first = buffer; + rule_data[ri_iter.name].second = &ri_iter.notes; + break; + default: + break; + } + } + + // 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()) { + + injected_rule_entries.push_back( + std::tuple( + ruleset_id, // `ruleset_id` + rd_iter.first, // `rule_name` + rd_iter.second.first, // `rule_value` + EscapeString(*rd_iter.second.second) // `notes` + ) + ); + + if (!quiet_update) { + LogInfo( + "Adding new rule [{}] ruleset [{}] ({}) value [{}]", + rd_iter.first.c_str(), + ruleset_name, + ruleset_id, + rd_iter.second.first.c_str() + ); + } + } + } + + if (injected_rule_entries.size()) { + + std::string query( + fmt::format( + "REPLACE INTO `rule_values`(`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES {}", + implode( + ",", + std::pair('(', ')'), + join_tuple(",", std::pair('\'', '\''), injected_rule_entries) + ) + ) + ); + + if (!db->QueryDatabase(query).Success()) { + return false; + } + + LogInfo( + "[{}] New rule(s) added to ruleset [{}] [{}]", + injected_rule_entries.size(), + ruleset_name, + ruleset_id + ); + } + + return true; +} + +bool RuleManager::UpdateOrphanedRules(Database *db, bool quiet_update) +{ + std::vector rule_data; + std::vector orphaned_rule_entries; + + // load database rule names + std::string query("SELECT `rule_name` FROM `rule_values` GROUP BY `rule_name`"); + + auto results = db->QueryDatabase(query); + if (!results.Success()) { + return false; + } + + // build rule data entries + for (const auto &ri_iter : s_RuleInfo) { + if (strcasecmp(ri_iter.name, "Invalid Rule") == 0) { + continue; + } + + rule_data.push_back(ri_iter.name); + } + + // 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()) { + + orphaned_rule_entries.push_back(std::string(row[0])); + + if (!quiet_update) { + LogInfo( + "Rule [{}] no longer exists... Deleting orphaned entry from `rule_values` table...", + row[0] + ); + } + } + } + + if (orphaned_rule_entries.size()) { + + std::string query ( + fmt::format( + "DELETE FROM `rule_values` WHERE `rule_name` IN ({})", + implode(",", std::pair('\'', '\''), orphaned_rule_entries) + ) + ); + + if (!db->QueryDatabase(query).Success()) { + return false; + } + + LogInfo("[{}] Orphaned Rule(s) Deleted from [All Rulesets] (-1)", orphaned_rule_entries.size()); + } + + return true; +} + +bool RuleManager::RestoreRuleNotes(Database *db) +{ + std::string query("SELECT `ruleset_id`, `rule_name`, `notes` FROM `rule_values`"); + + auto results = db->QueryDatabase(query); + if (!results.Success()) { + return false; + } + + int update_count = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + + auto rule = [](const char *rule_name) { + + for (auto rule_iter : s_RuleInfo) { + if (strcasecmp(rule_iter.name, rule_name) == 0) { + return rule_iter; + } + } + + return s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount]; + }(row[1]); + + if (strcasecmp(rule.name, row[1]) != 0) { + continue; + } + + if (row[2] != nullptr && rule.notes.compare(row[2]) == 0) { + continue; + } + + std::string query( + fmt::format( + "UPDATE `rule_values` SET `notes` = '{}' WHERE `ruleset_id` = '{}' AND `rule_name` = '{}'", + EscapeString(rule.notes), + row[0], + row[1] + ) + ); + + if (!db->QueryDatabase(query).Success()) { + continue; + } + + ++update_count; + } + + if (update_count > 0) { + LogInfo("[{}] Rule Note [{}] Restored", update_count, (update_count == 1 ? "" : "s")); + } + + return true; +} int RuleManager::GetRulesetID(Database *database, const char *ruleset_name) { diff --git a/common/rulesys.h b/common/rulesys.h index 710c6d612..e9d3365fe 100644 --- a/common/rulesys.h +++ b/common/rulesys.h @@ -49,21 +49,21 @@ class RuleManager { public: //generate our rule enums: typedef enum { - #define RULE_INT(cat, rule, default_value) \ + #define RULE_INT(cat, rule, default_value, notes) \ Int__##rule, #include "ruletypes.h" _IntRuleCount } IntType; typedef enum { - #define RULE_REAL(cat, rule, default_value) \ + #define RULE_REAL(cat, rule, default_value, notes) \ Real__##rule, #include "ruletypes.h" _RealRuleCount } RealType; typedef enum { - #define RULE_BOOL(cat, rule, default_value) \ + #define RULE_BOOL(cat, rule, default_value, notes) \ Bool__##rule, #include "ruletypes.h" _BoolRuleCount @@ -97,6 +97,9 @@ public: static const char *GetRuleName(IntType t) { return(s_RuleInfo[t].name); } static const char *GetRuleName(RealType t) { return(s_RuleInfo[t+_IntRuleCount].name); } static const char *GetRuleName(BoolType t) { return(s_RuleInfo[t+_IntRuleCount+_RealRuleCount].name); } + static const std::string &GetRuleNotes(IntType t) { return(s_RuleInfo[t].notes); } + static const std::string &GetRuleNotes(RealType t) { return(s_RuleInfo[t+_IntRuleCount].notes); } + static const std::string &GetRuleNotes(BoolType t) { return(s_RuleInfo[t+_IntRuleCount+_RealRuleCount].notes); } static uint32 CountRules() { return(_RulesCount); } static CategoryType FindCategory(const char *catname); bool ListRules(const char *catname, std::vector &into); @@ -113,6 +116,9 @@ public: void ResetRules(bool reload = false); bool LoadRules(Database *db, const char *ruleset = nullptr, bool reload = false); void SaveRules(Database *db, const char *ruleset = nullptr); + bool UpdateInjectedRules(Database *db, const char *ruleset_name, bool quiet_update = false); + bool UpdateOrphanedRules(Database *db, bool quiet_update = false); + bool RestoreRuleNotes(Database *db); private: RuleManager(); @@ -137,15 +143,17 @@ private: static bool _FindRule(const char *rule_name, RuleType &type_into, uint16 &index_into); static const char *_GetRuleName(RuleType type, uint16 index); + static const std::string &_GetRuleNotes(RuleType type, uint16 index); static int _FindOrCreateRuleset(Database *db, const char *ruleset); void _SaveRule(Database *db, RuleType type, uint16 index); - + static const char *s_categoryNames[]; typedef struct { const char *name; CategoryType category; RuleType type; uint16 rule_index; //index into its 'type' array + const std::string notes; } RuleInfo; static const RuleInfo s_RuleInfo[]; diff --git a/common/ruletypes.h b/common/ruletypes.h index cad8f455f..1a4a7ea67 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -20,13 +20,13 @@ #define RULE_CATEGORY(name) #endif #ifndef RULE_INT -#define RULE_INT(cat, rule, default_value) +#define RULE_INT(cat, rule, default_value, notes) #endif #ifndef RULE_REAL -#define RULE_REAL(cat, rule, default_value) +#define RULE_REAL(cat, rule, default_value, notes) #endif #ifndef RULE_BOOL -#define RULE_BOOL(cat, rule, default_value) +#define RULE_BOOL(cat, rule, default_value, notes) #endif #ifndef RULE_CATEGORY_END #define RULE_CATEGORY_END() @@ -36,714 +36,720 @@ RULE_CATEGORY(Character) -RULE_INT(Character, MaxLevel, 65) -RULE_BOOL(Character, PerCharacterQglobalMaxLevel, false) // This will check for qglobal 'CharMaxLevel' character qglobal (Type 5), if player tries to level beyond that point, it will not go beyond that level -RULE_BOOL(Character, PerCharacterBucketMaxLevel, false) // This will check for data bucket 'CharMaxLevel', if player tries to level beyond that point, it will not go beyond that level -RULE_INT(Character, MaxExpLevel, 0) //Sets the Max Level attainable via Experience -RULE_INT(Character, DeathExpLossLevel, 10) // Any level greater than this will lose exp on death -RULE_INT(Character, DeathExpLossMaxLevel, 255) // Any level greater than this will no longer lose exp on death -RULE_INT(Character, DeathItemLossLevel, 10) -RULE_INT(Character, DeathExpLossMultiplier, 3) //Adjust how much exp is lost -RULE_BOOL(Character, UseDeathExpLossMult, false) //Adjust to use the above multiplier or to use code default. -RULE_BOOL(Character, UseOldRaceRezEffects, false) // older clients had ID 757 for races with high starting STR, but it doesn't seem used anymore -RULE_INT(Character, CorpseDecayTimeMS, 10800000) -RULE_INT(Character, CorpseResTimeMS, 10800000) // time before cant res corpse(3 hours) -RULE_BOOL(Character, LeaveCorpses, true) -RULE_BOOL(Character, LeaveNakedCorpses, false) -RULE_INT(Character, MaxDraggedCorpses, 2) -RULE_REAL(Character, DragCorpseDistance, 400) // If the corpse is <= this distance from the player, it won't move -RULE_REAL(Character, ExpMultiplier, 0.5) -RULE_REAL(Character, AAExpMultiplier, 0.5) -RULE_REAL(Character, GroupExpMultiplier, 0.5) -RULE_REAL(Character, RaidExpMultiplier, 0.2) -RULE_BOOL(Character, UseXPConScaling, true) -RULE_INT(Character, ShowExpValues, 0) //0 - normal, 1 - Show raw experience values, 2 - Show raw experience values AND percent. -RULE_INT(Character, GreenModifier, 20) -RULE_INT(Character, LightBlueModifier, 40) -RULE_INT(Character, BlueModifier, 90) -RULE_INT(Character, WhiteModifier, 100) -RULE_INT(Character, YellowModifier, 125) -RULE_INT(Character, RedModifier, 150) -RULE_INT(Character, AutosaveIntervalS, 300) //0=disabled -RULE_INT(Character, HPRegenMultiplier, 100) -RULE_INT(Character, ManaRegenMultiplier, 100) -RULE_INT(Character, EnduranceRegenMultiplier, 100) -RULE_BOOL(Character, OldMinMana, false) // this is used for servers that want to follow older skill cap formulas so they can still have some regen w/o mediate -RULE_INT(Character, ConsumptionMultiplier, 100) //item's hunger restored = this value * item's food level, 100 = normal, 50 = people eat 2x as fast, 200 = people eat 2x as slow -RULE_BOOL(Character, HealOnLevel, false) -RULE_BOOL(Character, FeignKillsPet, false) -RULE_INT(Character, ItemManaRegenCap, 15) -RULE_INT(Character, ItemHealthRegenCap, 30) -RULE_INT(Character, ItemDamageShieldCap, 30) -RULE_INT(Character, ItemAccuracyCap, 150) -RULE_INT(Character, ItemAvoidanceCap, 100) -RULE_INT(Character, ItemCombatEffectsCap, 100) -RULE_INT(Character, ItemShieldingCap, 35) -RULE_INT(Character, ItemSpellShieldingCap, 35) -RULE_INT(Character, ItemDoTShieldingCap, 35) -RULE_INT(Character, ItemStunResistCap, 35) -RULE_INT(Character, ItemStrikethroughCap, 35) -RULE_INT(Character, ItemATKCap, 250) -RULE_INT(Character, ItemHealAmtCap, 250) -RULE_INT(Character, ItemSpellDmgCap, 250) -RULE_INT(Character, ItemClairvoyanceCap, 250) -RULE_INT(Character, ItemDSMitigationCap, 50) -RULE_INT(Character, ItemEnduranceRegenCap, 15) -RULE_INT(Character, ItemExtraDmgCap, 150) // Cap for bonuses to melee skills like Bash, Frenzy, etc -RULE_INT(Character, HasteCap, 100) // Haste cap for non-v3(overhaste) haste. -RULE_INT(Character, SkillUpModifier, 100) //skill ups are at 100% -RULE_BOOL(Character, SharedBankPlat, false) //off by default to prevent duping for now -RULE_BOOL(Character, BindAnywhere, false) -RULE_BOOL(Character, RestRegenEnabled, true) // Enable OOC Regen -RULE_INT(Character, RestRegenTimeToActivate, 30) // Time in seconds for rest state regen to kick in. -RULE_INT(Character, RestRegenRaidTimeToActivate, 300) // Time in seconds for rest state regen to kick in with a raid target. -RULE_INT(Character, KillsPerGroupLeadershipAA, 250) // Number of dark blues or above per Group Leadership AA -RULE_INT(Character, KillsPerRaidLeadershipAA, 250) // Number of dark blues or above per Raid Leadership AA -RULE_INT(Character, MaxFearDurationForPlayerCharacter, 4) //4 tics, each tic calculates every 6 seconds. -RULE_INT(Character, MaxCharmDurationForPlayerCharacter, 15) -RULE_INT(Character, BaseHPRegenBonusRaces, 4352) //a bitmask of race(s) that receive the regen bonus. Iksar (4096) & Troll (256) = 4352. see common/races.h for the bitmask values -RULE_BOOL(Character, SoDClientUseSoDHPManaEnd, false) // Setting this to true will allow SoD clients to use the SoD HP/Mana/End formulas and previous clients will use the old formulas -RULE_BOOL(Character, UseRaceClassExpBonuses, true) // Setting this to true will enable Class and Racial experience rate bonuses -RULE_BOOL(Character, UseOldRaceExpPenalties, false) // Setting this to true will enable racial exp penalties for Iksar, Troll, Ogre, and Barbarian, as well as the bonus for Halflings -RULE_BOOL(Character, UseOldClassExpPenalties, false) // Setting this to true will enable old class exp penalties for Paladin, SK, Ranger, Bard, Monk, Wizard, Enchanter, Magician, and Necromancer, as well as the bonus for Rogues and Warriors -RULE_BOOL(Character, RespawnFromHover, false) // Use Respawn window, or not. -RULE_INT(Character, RespawnFromHoverTimer, 300) // Respawn Window countdown timer, in SECONDS -RULE_BOOL(Character, UseNewStatsWindow, true) // New stats window shows everything -RULE_BOOL(Character, ItemCastsUseFocus, false) // If true, this allows item clickies to use focuses that have limited max levels on them -RULE_INT(Character, MinStatusForNoDropExemptions, 80) // This allows status x and higher to trade no drop items. -RULE_INT(Character, SkillCapMaxLevel, 75) // Sets the Max Level used for Skill Caps (from skill_caps table). -1 makes it use MaxLevel rule value. It is set to 75 because PEQ only has skillcaps up to that level, and grabbing the players' skill past 75 will return 0, breaking all skills past that level. This helps servers with obsurd level caps (75+ level cap) function without any modifications. -RULE_INT(Character, StatCap, 0) -RULE_BOOL(Character, CheckCursorEmptyWhenLooting, true) // If true, a player cannot loot a corpse (player or NPC) with an item on their cursor -RULE_BOOL(Character, MaintainIntoxicationAcrossZones, true) // If true, alcohol effects are maintained across zoning and logging out/in. -RULE_BOOL(Character, EnableDiscoveredItems, true) // If enabled, it enables EVENT_DISCOVER_ITEM and also saves character names and timestamps for the first time an item is discovered. -RULE_BOOL(Character, EnableXTargetting, true) // Enable Extended Targetting Window, for users with UF and later clients. -RULE_BOOL(Character, EnableAggroMeter, true) // Enable Aggro Meter, for users with RoF and later clients. -RULE_BOOL(Character, KeepLevelOverMax, false) // Don't delevel a character that has somehow gone over the level cap -RULE_INT(Character, FoodLossPerUpdate, 32) // How much food/water you lose per stamina update -RULE_BOOL(Character, EnableHungerPenalties, false) // being hungry/thirsty has negative effects -- it does appear normal live servers do not have penalties -RULE_INT(Character, BaseInstrumentSoftCap, 36) // Softcap for instrument mods, 36 commonly referred to as "3.6" as well. -RULE_BOOL(Character, UseSpellFileSongCap, true) // When they removed the AA that increased the cap they removed the above and just use the spell field -RULE_INT(Character, BaseRunSpeedCap, 158) // Base Run Speed Cap, on live it's 158% which will give you a runspeed of 1.580 hard capped to 225. -RULE_INT(Character, OrnamentationAugmentType, 20) //Ornamentation Augment Type -RULE_REAL(Character, EnvironmentDamageMulipliter, 1) -RULE_BOOL(Character, UnmemSpellsOnDeath, true) -RULE_INT(Character, TradeskillUpAlchemy, 2) // Alchemy skillup rate adjust. Lower is faster. -RULE_INT(Character, TradeskillUpBaking, 2) // Baking skillup rate adjust. Lower is faster. -RULE_INT(Character, TradeskillUpBlacksmithing, 2) // Blacksmithing skillup rate adjust. Lower is faster. -RULE_INT(Character, TradeskillUpBrewing, 3) // Brewing skillup rate adjust. Lower is faster. -RULE_INT(Character, TradeskillUpFletching, 2) // Fletching skillup rate adjust. Lower is faster. -RULE_INT(Character, TradeskillUpJewelcrafting, 2) // Jewelcrafting skillup rate adjust. Lower is faster. -RULE_INT(Character, TradeskillUpMakePoison, 2) // Make Poison skillup rate adjust. Lower is faster. -RULE_INT(Character, TradeskillUpPottery, 4) // Pottery skillup rate adjust. Lower is faster. -RULE_INT(Character, TradeskillUpResearch, 1) // Research skillup rate adjust. Lower is faster. -RULE_INT(Character, TradeskillUpTinkering, 2) // Tinkering skillup rate adjust. Lower is faster. -RULE_BOOL(Character, MarqueeHPUpdates, false) // Will show Health % in center of screen < 100% -RULE_INT(Character, IksarCommonTongue, 95) // 95 By default (live-like?) -RULE_INT(Character, OgreCommonTongue, 95) // 95 By default (live-like?) -RULE_INT(Character, TrollCommonTongue, 95) // 95 By default (live-like?) -RULE_BOOL(Character, ActiveInvSnapshots, false) // Takes a periodic snapshot of inventory contents from online players -RULE_INT(Character, InvSnapshotMinIntervalM, 180) // Minimum time (in minutes) between inventory snapshots -RULE_INT(Character, InvSnapshotMinRetryM, 30) // Time (in minutes) to re-attempt an inventory snapshot after a failure -RULE_INT(Character, InvSnapshotHistoryD, 30) // Time (in days) to keep snapshot entries -RULE_BOOL(Character, RestrictSpellScribing, false) // Restricts spell scribing to allowable races/classes of spell scroll, if true -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) -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 -RULE_BOOL(Character, UseOldConSystem, false) // Grant Health of Target's Target leadership AA on character creation -RULE_BOOL(Character, OPClientUpdateVisualDebug, false) // Shows a pulse and forward directional particle each time the client sends its position to server -RULE_BOOL(Character, AllowCrossClassTrainers, false) -RULE_BOOL(Character, PetsUseReagents, true) //Pets use reagent on spells -RULE_BOOL(Character, DismountWater, true) // Dismount horses when entering water +RULE_INT(Character, MaxLevel, 65, "") +RULE_BOOL(Character, PerCharacterQglobalMaxLevel, false, "This will check for qglobal 'CharMaxLevel' character qglobal (Type 5, \"\"), if player tries to level beyond that point, it will not go beyond that level") +RULE_BOOL(Character, PerCharacterBucketMaxLevel, false, "This will check for data bucket 'CharMaxLevel', if player tries to level beyond that point, it will not go beyond that level") +RULE_INT(Character, MaxExpLevel, 0, "Sets the Max Level attainable via Experience") +RULE_INT(Character, DeathExpLossLevel, 10, "Any level greater than this will lose exp on death") +RULE_INT(Character, DeathExpLossMaxLevel, 255, "Any level greater than this will no longer lose exp on death") +RULE_INT(Character, DeathItemLossLevel, 10, "") +RULE_INT(Character, DeathExpLossMultiplier, 3, "Adjust how much exp is lost") +RULE_BOOL(Character, UseDeathExpLossMult, false, "Adjust to use the above multiplier or to use code default") +RULE_BOOL(Character, UseOldRaceRezEffects, false, "older clients had ID 757 for races with high starting STR, but it doesn't seem used anymore") +RULE_INT(Character, CorpseDecayTimeMS, 10800000, "") +RULE_INT(Character, CorpseResTimeMS, 10800000, "time before cant res corpse(3 hours)") +RULE_BOOL(Character, LeaveCorpses, true, "") +RULE_BOOL(Character, LeaveNakedCorpses, false, "") +RULE_INT(Character, MaxDraggedCorpses, 2, "") +RULE_REAL(Character, DragCorpseDistance, 400, "If the corpse is <= this distance from the player, it won't move") +RULE_REAL(Character, ExpMultiplier, 0.5, "") +RULE_REAL(Character, AAExpMultiplier, 0.5, "") +RULE_REAL(Character, GroupExpMultiplier, 0.5, "") +RULE_REAL(Character, RaidExpMultiplier, 0.2, "") +RULE_BOOL(Character, UseXPConScaling, true, "") +RULE_INT(Character, ShowExpValues, 0, "0 - normal, 1 - Show raw experience values, 2 - Show raw experience values AND percent") +RULE_INT(Character, GreenModifier, 20, "") +RULE_INT(Character, LightBlueModifier, 40, "") +RULE_INT(Character, BlueModifier, 90, "") +RULE_INT(Character, WhiteModifier, 100, "") +RULE_INT(Character, YellowModifier, 125, "") +RULE_INT(Character, RedModifier, 150, "") +RULE_INT(Character, AutosaveIntervalS, 300, "0 = disabled") +RULE_INT(Character, HPRegenMultiplier, 100, "") +RULE_INT(Character, ManaRegenMultiplier, 100, "") +RULE_INT(Character, EnduranceRegenMultiplier, 100, "") +RULE_BOOL(Character, OldMinMana, false, "this is used for servers that want to follow older skill cap formulas so they can still have some regen w/o mediate") +RULE_INT(Character, ConsumptionMultiplier, 100, "item's hunger restored = this value * item's food level, 100 = normal, 50 = people eat 2x as fast, 200 = people eat 2x as slow") +RULE_BOOL(Character, HealOnLevel, false, "") +RULE_BOOL(Character, FeignKillsPet, false, "") +RULE_INT(Character, ItemManaRegenCap, 15, "") +RULE_INT(Character, ItemHealthRegenCap, 30, "") +RULE_INT(Character, ItemDamageShieldCap, 30, "") +RULE_INT(Character, ItemAccuracyCap, 150, "") +RULE_INT(Character, ItemAvoidanceCap, 100, "") +RULE_INT(Character, ItemCombatEffectsCap, 100, "") +RULE_INT(Character, ItemShieldingCap, 35, "") +RULE_INT(Character, ItemSpellShieldingCap, 35, "") +RULE_INT(Character, ItemDoTShieldingCap, 35, "") +RULE_INT(Character, ItemStunResistCap, 35, "") +RULE_INT(Character, ItemStrikethroughCap, 35, "") +RULE_INT(Character, ItemATKCap, 250, "") +RULE_INT(Character, ItemHealAmtCap, 250, "") +RULE_INT(Character, ItemSpellDmgCap, 250, "") +RULE_INT(Character, ItemClairvoyanceCap, 250, "") +RULE_INT(Character, ItemDSMitigationCap, 50, "") +RULE_INT(Character, ItemEnduranceRegenCap, 15, "") +RULE_INT(Character, ItemExtraDmgCap, 150, "Cap for bonuses to melee skills like Bash, Frenzy, etc") +RULE_INT(Character, HasteCap, 100, "Haste cap for non-v3(overhaste) haste") +RULE_INT(Character, SkillUpModifier, 100, "skill ups are at 100%") +RULE_BOOL(Character, SharedBankPlat, false, "off by default to prevent duping for now") +RULE_BOOL(Character, BindAnywhere, false, "") +RULE_BOOL(Character, RestRegenEnabled, true, "Enable OOC Regen") +RULE_INT(Character, RestRegenTimeToActivate, 30, "Time in seconds for rest state regen to kick in") +RULE_INT(Character, RestRegenRaidTimeToActivate, 300, "Time in seconds for rest state regen to kick in with a raid target") +RULE_INT(Character, KillsPerGroupLeadershipAA, 250, "Number of dark blues or above per Group Leadership AA") +RULE_INT(Character, KillsPerRaidLeadershipAA, 250, "Number of dark blues or above per Raid Leadership AA") +RULE_INT(Character, MaxFearDurationForPlayerCharacter, 4, "4 tics, each tic calculates every 6 seconds") +RULE_INT(Character, MaxCharmDurationForPlayerCharacter, 15, "") +RULE_INT(Character, BaseHPRegenBonusRaces, 4352, "a bitmask of race(s) that receive the regen bonus. Iksar (4096) & Troll (256) = 4352. see common/races.h for the bitmask values") +RULE_BOOL(Character, SoDClientUseSoDHPManaEnd, false, "Setting this to true will allow SoD clients to use the SoD HP/Mana/End formulas and previous clients will use the old formulas") +RULE_BOOL(Character, UseRaceClassExpBonuses, true, "Setting this to true will enable Class and Racial experience rate bonuses") +RULE_BOOL(Character, UseOldRaceExpPenalties, false, "Setting this to true will enable racial exp penalties for Iksar, Troll, Ogre, and Barbarian, as well as the bonus for Halflings") +RULE_BOOL(Character, UseOldClassExpPenalties, false, "Setting this to true will enable old class exp penalties for Paladin, SK, Ranger, Bard, Monk, Wizard, Enchanter, Magician, and Necromancer, as well as the bonus for Rogues and Warriors") +RULE_BOOL(Character, RespawnFromHover, false, "Use Respawn window, or not") +RULE_INT(Character, RespawnFromHoverTimer, 300, "Respawn Window countdown timer, in SECONDS") +RULE_BOOL(Character, UseNewStatsWindow, true, "New stats window shows everything") +RULE_BOOL(Character, ItemCastsUseFocus, false, "If true, this allows item clickies to use focuses that have limited max levels on them") +RULE_INT(Character, MinStatusForNoDropExemptions, 80, "This allows status x and higher to trade no drop items") +RULE_INT(Character, SkillCapMaxLevel, 75, "Sets the Max Level used for Skill Caps (from skill_caps table). -1 makes it use MaxLevel rule value. It is set to 75 because PEQ only has skillcaps up to that level, and grabbing the players' skill past 75 will return 0, breaking all skills past that level. This helps servers with obsurd level caps (75+ level cap) function without any modifications") +RULE_INT(Character, StatCap, 0, "") +RULE_BOOL(Character, CheckCursorEmptyWhenLooting, true, "If true, a player cannot loot a corpse (player or NPC) with an item on their cursor") +RULE_BOOL(Character, MaintainIntoxicationAcrossZones, true, "If true, alcohol effects are maintained across zoning and logging out/in") +RULE_BOOL(Character, EnableDiscoveredItems, true, "If enabled, it enables EVENT_DISCOVER_ITEM and also saves character names and timestamps for the first time an item is discovered") +RULE_BOOL(Character, EnableXTargetting, true, "Enable Extended Targetting Window, for users with UF and later clients") +RULE_BOOL(Character, EnableAggroMeter, true, "Enable Aggro Meter, for users with RoF and later clients") +RULE_BOOL(Character, KeepLevelOverMax, false, "Don't delevel a character that has somehow gone over the level cap") +RULE_INT(Character, FoodLossPerUpdate, 32, "How much food/water you lose per stamina update") +RULE_BOOL(Character, EnableHungerPenalties, false, "being hungry/thirsty has negative effects -- it does appear normal live servers do not have penalties") +RULE_INT(Character, BaseInstrumentSoftCap, 36, "Softcap for instrument mods, 36 commonly referred to as \"3.6\" as well") +RULE_BOOL(Character, UseSpellFileSongCap, true, "When they removed the AA that increased the cap they removed the above and just use the spell field") +RULE_INT(Character, BaseRunSpeedCap, 158, "Base Run Speed Cap, on live it's 158% which will give you a runspeed of 1.580 hard capped to 225") +RULE_INT(Character, OrnamentationAugmentType, 20, "Ornamentation Augment Type") +RULE_REAL(Character, EnvironmentDamageMulipliter, 1, "") +RULE_BOOL(Character, UnmemSpellsOnDeath, true, "") +RULE_INT(Character, TradeskillUpAlchemy, 2, "Alchemy skillup rate adjust. Lower is faster") +RULE_INT(Character, TradeskillUpBaking, 2, "Baking skillup rate adjust. Lower is faster") +RULE_INT(Character, TradeskillUpBlacksmithing, 2, "Blacksmithing skillup rate adjust. Lower is faster") +RULE_INT(Character, TradeskillUpBrewing, 3, "Brewing skillup rate adjust. Lower is faster") +RULE_INT(Character, TradeskillUpFletching, 2, "Fletching skillup rate adjust. Lower is faster") +RULE_INT(Character, TradeskillUpJewelcrafting, 2, "Jewelcrafting skillup rate adjust. Lower is faster") +RULE_INT(Character, TradeskillUpMakePoison, 2, "Make Poison skillup rate adjust. Lower is faster") +RULE_INT(Character, TradeskillUpPottery, 4, "Pottery skillup rate adjust. Lower is faster") +RULE_INT(Character, TradeskillUpResearch, 1, "Research skillup rate adjust. Lower is faster") +RULE_INT(Character, TradeskillUpTinkering, 2, "Tinkering skillup rate adjust. Lower is faster") +RULE_BOOL(Character, MarqueeHPUpdates, false, "Will show Health % in center of screen < 100%") +RULE_INT(Character, IksarCommonTongue, 95, "95 By default (live-like?)") +RULE_INT(Character, OgreCommonTongue, 95, "95 By default (live-like?)") +RULE_INT(Character, TrollCommonTongue, 95, "95 By default (live-like?)") +RULE_BOOL(Character, ActiveInvSnapshots, false, "Takes a periodic snapshot of inventory contents from online players") +RULE_INT(Character, InvSnapshotMinIntervalM, 180, "Minimum time (in minutes) between inventory snapshots") +RULE_INT(Character, InvSnapshotMinRetryM, 30, "Time (in minutes) to re-attempt an inventory snapshot after a failure") +RULE_INT(Character, InvSnapshotHistoryD, 30, "Time (in days) to keep snapshot entries") +RULE_BOOL(Character, RestrictSpellScribing, false, "Restricts spell scribing to allowable races/classes of spell scroll, if true") +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, "") +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") +RULE_BOOL(Character, UseOldConSystem, false, "Grant Health of Target's Target leadership AA on character creation") +RULE_BOOL(Character, OPClientUpdateVisualDebug, false, "Shows a pulse and forward directional particle each time the client sends its position to server") +RULE_BOOL(Character, AllowCrossClassTrainers, false, "") +RULE_BOOL(Character, PetsUseReagents, true, "Pets use reagent on spells") +RULE_BOOL(Character, DismountWater, true, "Dismount horses when entering water") RULE_CATEGORY_END() RULE_CATEGORY(Mercs) -RULE_INT(Mercs, SuspendIntervalMS, 10000) -RULE_INT(Mercs, UpkeepIntervalMS, 180000) -RULE_INT(Mercs, SuspendIntervalS, 10) -RULE_INT(Mercs, UpkeepIntervalS, 180) -RULE_BOOL(Mercs, AllowMercs, false) -RULE_BOOL(Mercs, ChargeMercPurchaseCost, false) -RULE_BOOL(Mercs, ChargeMercUpkeepCost, false) -RULE_INT(Mercs, AggroRadius, 100) // Determines the distance from which a merc will aggro group member's target(also used to determine the distance at which a healer merc will begin healing a group member) -RULE_INT(Mercs, AggroRadiusPuller, 25) // Determines the distance from which a merc will aggro group member's target, if they have the group role of puller (also used to determine the distance at which a healer merc will begin healing a group member, if they have the group role of puller) -RULE_INT(Mercs, ResurrectRadius, 50) // Determines the distance from which a healer merc will attempt to resurrect a group member's corpse -RULE_INT(Mercs, ScaleRate, 100) -RULE_BOOL(Mercs, MercsUsePathing, true) // Mercs will use node pathing when moving -RULE_BOOL(Mercs, AllowMercSuspendInCombat, true) +RULE_INT(Mercs, SuspendIntervalMS, 10000, "") +RULE_INT(Mercs, UpkeepIntervalMS, 180000, "") +RULE_INT(Mercs, SuspendIntervalS, 10, "") +RULE_INT(Mercs, UpkeepIntervalS, 180, "") +RULE_BOOL(Mercs, AllowMercs, false, "") +RULE_BOOL(Mercs, ChargeMercPurchaseCost, false, "") +RULE_BOOL(Mercs, ChargeMercUpkeepCost, false, "") +RULE_INT(Mercs, AggroRadius, 100, "Determines the distance from which a merc will aggro group member's target(also used to determine the distance at which a healer merc will begin healing a group member)") +RULE_INT(Mercs, AggroRadiusPuller, 25, "Determines the distance from which a merc will aggro group member's target, if they have the group role of puller (also used to determine the distance at which a healer merc will begin healing a group member, if they have the group role of puller)") +RULE_INT(Mercs, ResurrectRadius, 50, "Determines the distance from which a healer merc will attempt to resurrect a group member's corpse") +RULE_INT(Mercs, ScaleRate, 100, "") +RULE_BOOL(Mercs, MercsUsePathing, true, "Mercs will use node pathing when moving") +RULE_BOOL(Mercs, AllowMercSuspendInCombat, true, "") RULE_CATEGORY_END() RULE_CATEGORY(Guild) -RULE_INT(Guild, MaxMembers, 2048) -RULE_BOOL(Guild, PlayerCreationAllowed, false) // Allow players to create a guild using the window in Underfoot+ -RULE_INT(Guild, PlayerCreationLimit, 1) // Only allow use of the UF+ window if the account has < than this number of guild leaders on it -RULE_INT(Guild, PlayerCreationRequiredStatus, 0) // Required admin status. -RULE_INT(Guild, PlayerCreationRequiredLevel, 0) // Required Level of the player attempting to create the guild. -RULE_INT(Guild, PlayerCreationRequiredTime, 0) // Required Time Entitled On Account (in Minutes) to create the guild. +RULE_INT(Guild, MaxMembers, 2048, "") +RULE_BOOL(Guild, PlayerCreationAllowed, false, "Allow players to create a guild using the window in Underfoot+") +RULE_INT(Guild, PlayerCreationLimit, 1, "Only allow use of the UF+ window if the account has < than this number of guild leaders on it") +RULE_INT(Guild, PlayerCreationRequiredStatus, 0, "Required admin status") +RULE_INT(Guild, PlayerCreationRequiredLevel, 0, "Required Level of the player attempting to create the guild") +RULE_INT(Guild, PlayerCreationRequiredTime, 0, "Required Time Entitled On Account (in Minutes) to create the guild") RULE_CATEGORY_END() RULE_CATEGORY(Skills) -RULE_INT(Skills, MaxTrainTradeskills, 21) -RULE_BOOL(Skills, UseLimitTradeskillSearchSkillDiff, true) -RULE_INT(Skills, MaxTradeskillSearchSkillDiff, 50) -RULE_INT(Skills, MaxTrainSpecializations, 50) // Max level a GM trainer will train casting specializations -RULE_INT(Skills, SwimmingStartValue, 100) -RULE_BOOL(Skills, TrainSenseHeading, false) -RULE_INT(Skills, SenseHeadingStartValue, 200) -RULE_BOOL(Skills, SelfLanguageLearning, true) +RULE_INT(Skills, MaxTrainTradeskills, 21, "") +RULE_BOOL(Skills, UseLimitTradeskillSearchSkillDiff, true, "") +RULE_INT(Skills, MaxTradeskillSearchSkillDiff, 50, "") +RULE_INT(Skills, MaxTrainSpecializations, 50, "Max level a GM trainer will train casting specializations") +RULE_INT(Skills, SwimmingStartValue, 100, "") +RULE_BOOL(Skills, TrainSenseHeading, false, "") +RULE_INT(Skills, SenseHeadingStartValue, 200, "") +RULE_BOOL(Skills, SelfLanguageLearning, true, "") RULE_CATEGORY_END() RULE_CATEGORY(Pets) -RULE_REAL(Pets, AttackCommandRange, 150) -RULE_BOOL(Pets, UnTargetableSwarmPet, false) -RULE_REAL(Pets, PetPowerLevelCap, 10) // Max number of levels your pet can go up with pet power -RULE_BOOL(Pets, CanTakeNoDrop, false) // Can everyone trade nodrop gear to pets +RULE_REAL(Pets, AttackCommandRange, 150, "") +RULE_BOOL(Pets, UnTargetableSwarmPet, false, "") +RULE_REAL(Pets, PetPowerLevelCap, 10, "Max number of levels your pet can go up with pet power") +RULE_BOOL(Pets, CanTakeNoDrop, false, "Can everyone trade nodrop gear to pets") RULE_CATEGORY_END() RULE_CATEGORY(GM) -RULE_INT(GM, MinStatusToSummonItem, 250) -RULE_INT(GM, MinStatusToZoneAnywhere, 250) -RULE_INT(GM, MinStatusToLevelTarget, 100) +RULE_INT(GM, MinStatusToSummonItem, 250, "") +RULE_INT(GM, MinStatusToZoneAnywhere, 250, "") +RULE_INT(GM, MinStatusToLevelTarget, 100, "") RULE_CATEGORY_END() RULE_CATEGORY(World) -RULE_INT(World, ZoneAutobootTimeoutMS, 60000) -RULE_INT(World, ClientKeepaliveTimeoutMS, 65000) -RULE_BOOL(World, UseBannedIPsTable, false) // Toggle whether or not to check incoming client connections against the Banned_IPs table. Set this value to false to disable this feature. -RULE_BOOL(World, EnableTutorialButton, true) -RULE_BOOL(World, EnableReturnHomeButton, true) -RULE_INT(World, MaxLevelForTutorial, 10) -RULE_INT(World, TutorialZoneID, 189) -RULE_INT(World, GuildBankZoneID, 345) -RULE_INT(World, MinOfflineTimeToReturnHome, 21600) // 21600 seconds is 6 Hours -RULE_INT(World, MaxClientsPerIP, -1) // Maximum number of clients allowed to connect per IP address if account status is < AddMaxClientsStatus. Default value: -1 (feature disabled) -RULE_INT(World, ExemptMaxClientsStatus, -1) // Exempt accounts from the MaxClientsPerIP and AddMaxClientsStatus rules, if their status is >= this value. Default value: -1 (feature disabled) -RULE_INT(World, AddMaxClientsPerIP, -1) // Maximum number of clients allowed to connect per IP address if account status is < ExemptMaxClientsStatus. Default value: -1 (feature disabled) -RULE_INT(World, AddMaxClientsStatus, -1) // Accounts with status >= this rule will be allowed to use the amount of accounts defined in the AddMaxClientsPerIP. Default value: -1 (feature disabled) -RULE_BOOL(World, MaxClientsSetByStatus, false) // If True, IP Limiting will be set to the status on the account as long as the status is > MaxClientsPerIP -RULE_BOOL(World, EnableIPExemptions, false) // If True, ip_exemptions table is used, if there is no entry for the IP it will default to RuleI(World, MaxClientsPerIP) -RULE_BOOL(World, ClearTempMerchantlist, true) // Clears temp merchant items when world boots. -RULE_BOOL(World, DeleteStaleCorpeBackups, true) // Deletes stale corpse backups older than 2 weeks. -RULE_BOOL(World, GMAccountIPList, false) // Check ip list against GM Accounts, AntiHack GM Accounts. -RULE_INT(World, MinGMAntiHackStatus, 1) //Minimum GM status to check against AntiHack list -RULE_INT(World, SoFStartZoneID, -1) //Sets the Starting Zone for SoF Clients separate from Titanium Clients (-1 is disabled) -RULE_INT(World, TitaniumStartZoneID, -1) //Sets the Starting Zone for Titanium Clients (-1 is disabled). Replaces the old method. -RULE_INT(World, ExpansionSettings, 16383) // Sets the expansion settings for the server, This is sent on login to world and affects client expansion settings. Defaults to all expansions enabled up to TSS. -RULE_BOOL(World, UseClientBasedExpansionSettings, true) // if true it will overrule World, ExpansionSettings and set someone's expansion based on the client they're using -RULE_INT(World, PVPSettings, 0) // Sets the PVP settings for the server, 1 = Rallos Zek RuleSet, 2 = Tallon/Vallon Zek Ruleset, 4 = Sullon Zek Ruleset, 6 = Discord Ruleset, anything above 6 is the Discord Ruleset without the no-drop restrictions removed. TODO: Edit IsAttackAllowed in Zone to accomodate for these rules. -RULE_INT(World, PVPMinLevel, 0) // minimum level to pvp -RULE_BOOL (World, IsGMPetitionWindowEnabled, false) -RULE_INT (World, FVNoDropFlag, 0) // Sets the Firiona Vie settings on the client. If set to 2, the flag will be set for GMs only, allowing trading of no-drop items. -RULE_BOOL (World, IPLimitDisconnectAll, false) -RULE_BOOL(World, MaxClientsSimplifiedLogic, false) // New logic that only uses ExemptMaxClientsStatus and MaxClientsPerIP. Done on the loginserver. This mimics the P99-style special IP rules. -RULE_INT (World, TellQueueSize, 20) -RULE_BOOL(World, StartZoneSameAsBindOnCreation, true) //Should the start zone ALWAYS be the same location as your bind? -RULE_BOOL(World, EnforceCharacterLimitAtLogin, false) +RULE_INT(World, ZoneAutobootTimeoutMS, 60000, "") +RULE_INT(World, ClientKeepaliveTimeoutMS, 65000, "") +RULE_BOOL(World, UseBannedIPsTable, false, "Toggle whether or not to check incoming client connections against the Banned_IPs table. Set this value to false to disable this feature") +RULE_BOOL(World, EnableTutorialButton, true, "") +RULE_BOOL(World, EnableReturnHomeButton, true, "") +RULE_INT(World, MaxLevelForTutorial, 10, "") +RULE_INT(World, TutorialZoneID, 189, "") +RULE_INT(World, GuildBankZoneID, 345, "") +RULE_INT(World, MinOfflineTimeToReturnHome, 21600, "21600 seconds is 6 Hours") +RULE_INT(World, MaxClientsPerIP, -1, "Maximum number of clients allowed to connect per IP address if account status is < AddMaxClientsStatus. Default value: -1 (feature disabled)") +RULE_INT(World, ExemptMaxClientsStatus, -1, "Exempt accounts from the MaxClientsPerIP and AddMaxClientsStatus rules, if their status is >= this value. Default value: -1 (feature disabled)") +RULE_INT(World, AddMaxClientsPerIP, -1, "Maximum number of clients allowed to connect per IP address if account status is < ExemptMaxClientsStatus. Default value: -1 (feature disabled)") +RULE_INT(World, AddMaxClientsStatus, -1, "Accounts with status >= this rule will be allowed to use the amount of accounts defined in the AddMaxClientsPerIP. Default value: -1 (feature disabled)") +RULE_BOOL(World, MaxClientsSetByStatus, false, "If True, IP Limiting will be set to the status on the account as long as the status is > MaxClientsPerIP") +RULE_BOOL(World, EnableIPExemptions, false, "If True, ip_exemptions table is used, if there is no entry for the IP it will default to RuleI(World, MaxClientsPerIP)") +RULE_BOOL(World, ClearTempMerchantlist, true, "Clears temp merchant items when world boots") +RULE_BOOL(World, DeleteStaleCorpeBackups, true, "Deletes stale corpse backups older than 2 weeks") +RULE_BOOL(World, GMAccountIPList, false, "Check ip list against GM Accounts, AntiHack GM Accounts") +RULE_INT(World, MinGMAntiHackStatus, 1, "Minimum GM status to check against AntiHack list") +RULE_INT(World, SoFStartZoneID, -1, "Sets the Starting Zone for SoF Clients separate from Titanium Clients (-1 is disabled)") +RULE_INT(World, TitaniumStartZoneID, -1, "Sets the Starting Zone for Titanium Clients (-1 is disabled). Replaces the old method") +RULE_INT(World, ExpansionSettings, 16383, "Sets the expansion settings for the server, This is sent on login to world and affects client expansion settings. Defaults to all expansions enabled up to TSS") +RULE_BOOL(World, UseClientBasedExpansionSettings, true, "if true it will overrule World, ExpansionSettings and set someone's expansion based on the client they're using") +RULE_INT(World, PVPSettings, 0, "Sets the PVP settings for the server, 1 = Rallos Zek RuleSet, 2 = Tallon/Vallon Zek Ruleset, 4 = Sullon Zek Ruleset, 6 = Discord Ruleset, anything above 6 is the Discord Ruleset without the no-drop restrictions removed. TODO: Edit IsAttackAllowed in Zone to accomodate for these rules") +RULE_INT(World, PVPMinLevel, 0, "minimum level to pvp") +RULE_BOOL (World, IsGMPetitionWindowEnabled, false, "") +RULE_INT (World, FVNoDropFlag, 0, "Sets the Firiona Vie settings on the client. If set to 2, the flag will be set for GMs only, allowing trading of no-drop items") +RULE_BOOL (World, IPLimitDisconnectAll, false, "") +RULE_BOOL(World, MaxClientsSimplifiedLogic, false, "New logic that only uses ExemptMaxClientsStatus and MaxClientsPerIP. Done on the loginserver. This mimics the P99-style special IP rules") +RULE_INT (World, TellQueueSize, 20, "") +RULE_BOOL(World, StartZoneSameAsBindOnCreation, true, "Should the start zone ALWAYS be the same location as your bind?") +RULE_BOOL(World, EnforceCharacterLimitAtLogin, false, "") RULE_CATEGORY_END() RULE_CATEGORY(Zone) -RULE_INT(Zone, ClientLinkdeadMS, 90000) //the time a client remains link dead on the server after a sudden disconnection -RULE_INT(Zone, GraveyardTimeMS, 1200000) //ms time until a player corpse is moved to a zone's graveyard, if one is specified for the zone -RULE_BOOL(Zone, EnableShadowrest, 1) // enables or disables the shadowrest zone feature for player corpses. Default is turned on. -RULE_BOOL(Zone, UsePlayerCorpseBackups, true) // Keeps backups of player corpses. -RULE_INT(Zone, MQWarpExemptStatus, -1) // Required status level to exempt the MQWarpDetector. Set to -1 to disable this feature. -RULE_INT(Zone, MQZoneExemptStatus, -1) // Required status level to exempt the MQZoneDetector. Set to -1 to disable this feature. -RULE_INT(Zone, MQGateExemptStatus, -1) // Required status level to exempt the MQGateDetector. Set to -1 to disable this feature. -RULE_INT(Zone, MQGhostExemptStatus, -1) // Required status level to exempt the MGhostDetector. Set to -1 to disable this feature. -RULE_BOOL(Zone, EnableMQWarpDetector, true) // Enable the MQWarp Detector. Set to False to disable this feature. -RULE_BOOL(Zone, EnableMQZoneDetector, true) // Enable the MQZone Detector. Set to False to disable this feature. -RULE_BOOL(Zone, EnableMQGateDetector, true) // Enable the MQGate Detector. Set to False to disable this feature. -RULE_BOOL(Zone, EnableMQGhostDetector, true) // Enable the MQGhost Detector. Set to False to disable this feature. -RULE_REAL(Zone, MQWarpDetectionDistanceFactor, 9.0) //clients move at 4.4 about if in a straight line but with movement and to acct for lag we raise it a bit -RULE_BOOL(Zone, MarkMQWarpLT, false) -RULE_INT(Zone, AutoShutdownDelay, 5000) //How long a dynamic zone stays loaded while empty -RULE_INT(Zone, PEQZoneReuseTime, 900) //How long, in seconds, until you can reuse the #peqzone command. -RULE_INT(Zone, PEQZoneDebuff1, 4454) //First debuff casted by #peqzone Default is Cursed Keeper's Blight. -RULE_INT(Zone, PEQZoneDebuff2, 2209) //Second debuff casted by #peqzone Default is Tendrils of Apathy. -RULE_BOOL(Zone, UsePEQZoneDebuffs, true) //Will determine if #peqzone will debuff players or not when used. -RULE_REAL(Zone, HotZoneBonus, 0.75) -RULE_INT(Zone, ReservedInstances, 30) //Will reserve this many instance ids for globals... probably not a good idea to change this while a server is running. -RULE_INT(Zone, EbonCrystalItemID, 40902) -RULE_INT(Zone, RadiantCrystalItemID, 40903) -RULE_BOOL(Zone, LevelBasedEXPMods, false) // Allows you to use the level_exp_mods table in consideration to your players EXP hits -RULE_INT(Zone, WeatherTimer, 600) // Weather timer when no duration is available -RULE_BOOL(Zone, EnableLoggedOffReplenishments, true) -RULE_INT(Zone, MinOfflineTimeToReplenishments, 21600) // 21600 seconds is 6 Hours -RULE_BOOL(Zone, UseZoneController, true) // Enables the ability to use persistent quest based zone controllers (zone_controller.pl/lua) -RULE_BOOL(Zone, EnableZoneControllerGlobals, false) // Enables the ability to use quest globals with the zone controller NPC -RULE_INT(Zone, GlobalLootMultiplier, 1) // Sets Global Loot drop multiplier for database based drops, useful for double, triple loot etc. +RULE_INT(Zone, ClientLinkdeadMS, 90000, "the time a client remains link dead on the server after a sudden disconnection") +RULE_INT(Zone, GraveyardTimeMS, 1200000, "ms time until a player corpse is moved to a zone's graveyard, if one is specified for the zone") +RULE_BOOL(Zone, EnableShadowrest, 1, "enables or disables the shadowrest zone feature for player corpses. Default is turned on") +RULE_BOOL(Zone, UsePlayerCorpseBackups, true, "Keeps backups of player corpses") +RULE_INT(Zone, MQWarpExemptStatus, -1, "Required status level to exempt the MQWarpDetector. Set to -1 to disable this feature") +RULE_INT(Zone, MQZoneExemptStatus, -1, "Required status level to exempt the MQZoneDetector. Set to -1 to disable this feature") +RULE_INT(Zone, MQGateExemptStatus, -1, "Required status level to exempt the MQGateDetector. Set to -1 to disable this feature") +RULE_INT(Zone, MQGhostExemptStatus, -1, "Required status level to exempt the MGhostDetector. Set to -1 to disable this feature") +RULE_BOOL(Zone, EnableMQWarpDetector, true, "Enable the MQWarp Detector. Set to False to disable this feature") +RULE_BOOL(Zone, EnableMQZoneDetector, true, "Enable the MQZone Detector. Set to False to disable this feature") +RULE_BOOL(Zone, EnableMQGateDetector, true, "Enable the MQGate Detector. Set to False to disable this feature") +RULE_BOOL(Zone, EnableMQGhostDetector, true, "Enable the MQGhost Detector. Set to False to disable this feature") +RULE_REAL(Zone, MQWarpDetectionDistanceFactor, 9.0, "clients move at 4.4 about if in a straight line but with movement and to acct for lag we raise it a bit") +RULE_BOOL(Zone, MarkMQWarpLT, false, "") +RULE_INT(Zone, AutoShutdownDelay, 5000, "How long a dynamic zone stays loaded while empty") +RULE_INT(Zone, PEQZoneReuseTime, 900, "How long, in seconds, until you can reuse the #peqzone command") +RULE_INT(Zone, PEQZoneDebuff1, 4454, "First debuff casted by #peqzone Default is Cursed Keeper's Blight") +RULE_INT(Zone, PEQZoneDebuff2, 2209, "Second debuff casted by #peqzone Default is Tendrils of Apathy") +RULE_BOOL(Zone, UsePEQZoneDebuffs, true, "Will determine if #peqzone will debuff players or not when used") +RULE_REAL(Zone, HotZoneBonus, 0.75, "") +RULE_INT(Zone, ReservedInstances, 30, "Will reserve this many instance ids for globals... probably not a good idea to change this while a server is running") +RULE_INT(Zone, EbonCrystalItemID, 40902, "") +RULE_INT(Zone, RadiantCrystalItemID, 40903, "") +RULE_BOOL(Zone, LevelBasedEXPMods, false, "Allows you to use the level_exp_mods table in consideration to your players EXP hits") +RULE_INT(Zone, WeatherTimer, 600, "Weather timer when no duration is available") +RULE_BOOL(Zone, EnableLoggedOffReplenishments, true, "") +RULE_INT(Zone, MinOfflineTimeToReplenishments, 21600, "21600 seconds is 6 Hours") +RULE_BOOL(Zone, UseZoneController, true, "Enables the ability to use persistent quest based zone controllers (zone_controller.pl/lua)") +RULE_BOOL(Zone, EnableZoneControllerGlobals, false, "Enables the ability to use quest globals with the zone controller NPC") +RULE_INT(Zone, GlobalLootMultiplier, 1, "Sets Global Loot drop multiplier for database based drops, useful for double, triple loot etc") +RULE_BOOL(Zone, KillProcessOnDynamicShutdown, true, "When process has booted a zone and has hit its zone shut down timer, it will hard kill the process to free memory back to the OS") RULE_CATEGORY_END() RULE_CATEGORY(Map) -RULE_BOOL(Map, FixPathingZOnSendTo, false) //try to repair Z coords in the SendTo routine as well. -RULE_BOOL(Map, FixZWhenPathing, true) // Automatically fix NPC Z coordinates when moving/pathing/engaged (Far less CPU intensive than its predecessor) -RULE_REAL(Map, DistanceCanTravelBeforeAdjustment, 10.0) // distance a mob can path before FixZ is called, depends on FixZWhenPathing -RULE_BOOL(Map, MobZVisualDebug, false) // Displays spell effects determining whether or not NPC is hitting Best Z calcs (blue for hit, red for miss) -RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20) //at runtime in SendTo: max change in Z to allow the BestZ code to apply. -RULE_INT(Map, FindBestZHeightAdjust, 1) // Adds this to the current Z before seeking the best Z position +RULE_BOOL(Map, FixPathingZOnSendTo, false, "try to repair Z coords in the SendTo routine as well") +RULE_BOOL(Map, FixZWhenPathing, true, "Automatically fix NPC Z coordinates when moving/pathing/engaged (Far less CPU intensive than its predecessor)") +RULE_REAL(Map, DistanceCanTravelBeforeAdjustment, 10.0, "distance a mob can path before FixZ is called, depends on FixZWhenPathing") +RULE_BOOL(Map, MobZVisualDebug, false, "Displays spell effects determining whether or not NPC is hitting Best Z calcs (blue for hit, red for miss)") +RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "at runtime in SendTo: max change in Z to allow the BestZ code to apply") +RULE_INT(Map, FindBestZHeightAdjust, 1, "Adds this to the current Z before seeking the best Z position") RULE_CATEGORY_END() RULE_CATEGORY(Pathing) -RULE_BOOL(Pathing, Guard, true) // Enable pathing for mobs moving to their guard point. -RULE_BOOL(Pathing, Find, true) // Enable pathing for FindPerson requests from the client. -RULE_BOOL(Pathing, Fear, true) // Enable pathing for fear -RULE_REAL(Pathing, NavmeshStepSize, 100.0f) -RULE_REAL(Pathing, ShortMovementUpdateRange, 130.0f) +RULE_BOOL(Pathing, Guard, true, "Enable pathing for mobs moving to their guard point") +RULE_BOOL(Pathing, Find, true, "Enable pathing for FindPerson requests from the client") +RULE_BOOL(Pathing, Fear, true, "Enable pathing for fear") +RULE_REAL(Pathing, NavmeshStepSize, 100.0f, "") +RULE_REAL(Pathing, ShortMovementUpdateRange, 130.0f, "") RULE_CATEGORY_END() RULE_CATEGORY(Watermap) // enable these to use the water detection code. Requires Water Maps generated by awater utility -RULE_BOOL(Watermap, CheckWaypointsInWaterWhenLoading, false) // Does not apply BestZ as waypoints are loaded if they are in water -RULE_BOOL(Watermap, CheckForWaterAtWaypoints, false) // Check if a mob has moved into/out of water when at waypoints and sets flymode -RULE_BOOL(Watermap, CheckForWaterWhenMoving, false) // Checks if a mob has moved into/out of water each time it's loc is recalculated -RULE_BOOL(Watermap, CheckForWaterOnSendTo, false) // Checks if a mob has moved into/out of water on SendTo -RULE_BOOL(Watermap, CheckForWaterWhenFishing, false) // Only lets a player fish near water (if a water map exists for the zone) -RULE_REAL(Watermap, FishingRodLength, 30) // How far in front of player water must be for fishing to work -RULE_REAL(Watermap, FishingLineLength, 100) // If water is more than this far below the player, it is considered too far to fish -RULE_REAL(Watermap, FishingLineStepSize, 1) // Basic step size for fishing calc, too small and it will eat cpu, too large and it will miss potential water +RULE_BOOL(Watermap, CheckWaypointsInWaterWhenLoading, false, "Does not apply BestZ as waypoints are loaded if they are in water") +RULE_BOOL(Watermap, CheckForWaterAtWaypoints, false, "Check if a mob has moved into/out of water when at waypoints and sets flymode") +RULE_BOOL(Watermap, CheckForWaterWhenMoving, false, "Checks if a mob has moved into/out of water each time it's loc is recalculated") +RULE_BOOL(Watermap, CheckForWaterOnSendTo, false, "Checks if a mob has moved into/out of water on SendTo") +RULE_BOOL(Watermap, CheckForWaterWhenFishing, false, "Only lets a player fish near water (if a water map exists for the zone)") +RULE_REAL(Watermap, FishingRodLength, 30, "How far in front of player water must be for fishing to work") +RULE_REAL(Watermap, FishingLineLength, 100, "If water is more than this far below the player, it is considered too far to fish") +RULE_REAL(Watermap, FishingLineStepSize, 1, "Basic step size for fishing calc, too small and it will eat cpu, too large and it will miss potential water") RULE_CATEGORY_END() RULE_CATEGORY(Spells) -RULE_INT(Spells, AutoResistDiff, 15) -RULE_REAL(Spells, ResistChance, 2.0) //chance to resist given no resists and same level -RULE_REAL(Spells, ResistMod, 0.40) //multiplier, chance to resist = this * ResistAmount -RULE_REAL(Spells, PartialHitChance, 0.7) //The chance when a spell is resisted that it will partial hit. -RULE_REAL(Spells, PartialHitChanceFear, 0.25) //The chance when a fear spell is resisted that it will partial hit. -RULE_INT(Spells, BaseCritChance, 0) //base % chance that everyone has to crit a spell -RULE_INT(Spells, BaseCritRatio, 100) //base % bonus to damage on a successful spell crit. 100 = 2x damage -RULE_INT(Spells, WizCritLevel, 12) //level wizards first get spell crits -RULE_INT(Spells, WizCritChance, 7) //wiz's crit chance, on top of BaseCritChance -RULE_INT(Spells, WizCritRatio, 0) //wiz's crit bonus, on top of BaseCritRatio (should be 0 for Live-like) -RULE_INT(Spells, ResistPerLevelDiff, 85) //8.5 resist per level difference. -RULE_INT(Spells, TranslocateTimeLimit, 0) // If not zero, time in seconds to accept a Translocate. -RULE_INT(Spells, SacrificeMinLevel, 46) //first level Sacrifice will work on -RULE_INT(Spells, SacrificeMaxLevel, 69) //last level Sacrifice will work on -RULE_INT(Spells, SacrificeItemID, 9963) //Item ID of the item Sacrifice will return (defaults to an EE) -RULE_BOOL(Spells, EnableSpellGlobals, false) // If Enabled, spells check the spell_globals table and compare character data from their quest globals before allowing the spell to scribe with scribespells/traindiscs -RULE_BOOL(Spells, EnableSpellBuckets, false) // If Enabled, spells check the spell_buckets table and compare character data from their data buckets before allowing the spell to scribe with scribespells/traindiscs -RULE_INT(Spells, MaxBuffSlotsNPC, 60) // default to Tit's limit -RULE_INT(Spells, MaxSongSlotsNPC, 0) // NPCs don't have songs ... -RULE_INT(Spells, MaxDiscSlotsNPC, 0) // NPCs don't have discs ... -RULE_INT(Spells, MaxTotalSlotsNPC, 60) // default to Tit's limit -RULE_INT(Spells, MaxTotalSlotsPET, 30) // default to Tit's limit -RULE_BOOL (Spells, EnableBlockedBuffs, true) -RULE_INT(Spells, ReflectType, 3) //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) // 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 max level of 0. -RULE_BOOL(Spells, NPCIgnoreBaseImmunity, true) // Whether or not NPCs get to ignore the BaseImmunityLevel for their spells. -RULE_REAL(Spells, AvgSpellProcsPerMinute, 6.0) //Adjust rate for sympathetic spell procs -RULE_INT(Spells, ResistFalloff, 67) //Max that level that will adjust our resist chance based on level modifiers -RULE_INT(Spells, CharismaEffectiveness, 10) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. -RULE_INT(Spells, CharismaEffectivenessCap, 255) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. -RULE_BOOL(Spells, CharismaCharmDuration, false) // Allow CHA resist mod to extend charm duration. -RULE_INT(Spells, CharmBreakCheckChance, 25) //Determines chance for a charm break check to occur each buff tick. -RULE_INT(Spells, MaxCastTimeReduction, 50) //Max percent your spell cast time can be reduced by spell haste -RULE_INT(Spells, RootBreakFromSpells, 55) //Chance for root to break when cast on. -RULE_INT(Spells, DeathSaveCharismaMod, 3) //Determines how much charisma effects chance of death save firing. -RULE_INT(Spells, DivineInterventionHeal, 8000) //Divine intervention heal amount. -RULE_INT(Spells, AdditiveBonusWornType, 0) //Calc worn bonuses to add together (instead of taking highest) if set to THIS worn type. (2=Will covert live items automatically) -RULE_BOOL(Spells, UseCHAScribeHack, false) //ScribeSpells and TrainDiscs quest functions will ignore entries where field 12 is CHA. What's the best way to do this? -RULE_BOOL(Spells, BuffLevelRestrictions, true) //Buffs will not land on low level toons like live -RULE_INT(Spells, RootBreakCheckChance, 70) //Determines chance for a root break check to occur each buff tick. -RULE_INT(Spells, FearBreakCheckChance, 70) //Determines chance for a fear break check to occur each buff tick. -RULE_INT(Spells, SuccorFailChance, 2) //Determines chance for a succor spell not to teleport an invidual player -RULE_INT(Spells, FRProjectileItem_Titanium, 1113) // Item id for Titanium clients for Fire 'spell projectile'. -RULE_INT(Spells, FRProjectileItem_SOF, 80684) // Item id for SOF clients for Fire 'spell projectile'. -RULE_INT(Spells, FRProjectileItem_NPC, 80684) // Item id for NPC Fire 'spell projectile'. -RULE_BOOL(Spells, UseLiveSpellProjectileGFX, false) // Use spell projectile graphics set in the spells_new table (player_1). Server must be using UF+ spell file. -RULE_BOOL(Spells, FocusCombatProcs, false) //Allow all combat procs to receive focus effects. -RULE_BOOL(Spells, PreNerfBardAEDoT, false) //Allow bard AOE dots to damage targets when moving. -RULE_INT(Spells, AI_SpellCastFinishedFailRecast, 800) // AI spell recast time(MS) when an spell is cast but fails (ie stunned). -RULE_INT(Spells, AI_EngagedNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while engaged. (min time in random) -RULE_INT(Spells, AI_EngagedNoSpellMaxRecast, 1000) // AI spell recast time(MS) check when no spell is cast engaged.(max time in random) -RULE_INT(Spells, AI_EngagedBeneficialSelfChance, 100) // Chance during first AI Cast check to do a beneficial spell on self. -RULE_INT(Spells, AI_EngagedBeneficialOtherChance, 25) // Chance during second AI Cast check to do a beneficial spell on others. -RULE_INT(Spells, AI_EngagedDetrimentalChance, 20) // Chance during third AI Cast check to do a determental spell on others. -RULE_INT(Spells, AI_PursueNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while chasing target. (min time in random) -RULE_INT(Spells, AI_PursueNoSpellMaxRecast, 2000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random) -RULE_INT(Spells, AI_PursueDetrimentalChance, 90) // Chance while chasing target to cast a detrimental spell. -RULE_INT(Spells, AI_IdleNoSpellMinRecast, 6000) // AI spell recast time(MS) check when no spell is cast while idle. (min time in random) -RULE_INT(Spells, AI_IdleNoSpellMaxRecast, 60000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random) -RULE_INT(Spells, AI_IdleBeneficialChance, 100) // Chance while idle to do a beneficial spell on self or others. -RULE_INT(Spells, AI_HealHPPct, 50) // HP Pct NPCs will start heals at (in and out of combat) if spell's max_hp is not set -RULE_BOOL(Spells, SHDProcIDOffByOne, true) // pre June 2009 SHD spell procs were off by 1, they stopped doing this in June 2009 (so UF+ spell files need this false) -RULE_BOOL(Spells, Jun182014HundredHandsRevamp, false) // this should be true for if you import a spell file newer than June 18, 2014 -RULE_BOOL(Spells, SwarmPetTargetLock, false) // Use old method of swarm pets target locking till target dies then despawning. -RULE_BOOL(Spells, NPC_UseFocusFromSpells, true) // Allow npcs to use most spell derived focus effects. -RULE_BOOL(Spells, NPC_UseFocusFromItems, false) // Allow npcs to use most item derived focus effects. -RULE_BOOL(Spells, UseAdditiveFocusFromWornSlot, false) // Allows an additive focus effect to be calculated from worn slot. -RULE_BOOL(Spells, AlwaysSendTargetsBuffs, false) // ignore LAA level if true -RULE_BOOL(Spells, FlatItemExtraSpellAmt, false) // allow SpellDmg stat to affect all spells, regardless of cast time/cooldown/etc -RULE_BOOL(Spells, IgnoreSpellDmgLvlRestriction, false) // ignore the 5 level spread on applying SpellDmg -RULE_BOOL(Spells, AllowItemTGB, false) // TGB doesn't work with items on live, custom servers want it though -RULE_BOOL(Spells, NPCInnateProcOverride, true) // NPC innate procs override the target type to single target. -RULE_BOOL(Spells, OldRainTargets, false) // use old incorrectly implemented max targets for rains -RULE_BOOL(Spells, NPCSpellPush, false) // enable spell push on NPCs +RULE_INT(Spells, AutoResistDiff, 15, "") +RULE_REAL(Spells, ResistChance, 2.0, "chance to resist given no resists and same level") +RULE_REAL(Spells, ResistMod, 0.40, "multiplier, chance to resist = this * ResistAmount") +RULE_REAL(Spells, PartialHitChance, 0.7, "The chance when a spell is resisted that it will partial hit") +RULE_REAL(Spells, PartialHitChanceFear, 0.25, "The chance when a fear spell is resisted that it will partial hit") +RULE_INT(Spells, BaseCritChance, 0, "base % chance that everyone has to crit a spell") +RULE_INT(Spells, BaseCritRatio, 100, "base % bonus to damage on a successful spell crit. 100 = 2x damage") +RULE_INT(Spells, WizCritLevel, 12, "level wizards first get spell crits") +RULE_INT(Spells, WizCritChance, 7, "wiz's crit chance, on top of BaseCritChance") +RULE_INT(Spells, WizCritRatio, 0, "wiz's crit bonus, on top of BaseCritRatio (should be 0 for Live-like)") +RULE_INT(Spells, ResistPerLevelDiff, 85, "8.5 resist per level difference") +RULE_INT(Spells, TranslocateTimeLimit, 0, "If not zero, time in seconds to accept a Translocate") +RULE_INT(Spells, SacrificeMinLevel, 46, "first level Sacrifice will work on") +RULE_INT(Spells, SacrificeMaxLevel, 69, "last level Sacrifice will work on") +RULE_INT(Spells, SacrificeItemID, 9963, "Item ID of the item Sacrifice will return (defaults to an EE)") +RULE_BOOL(Spells, EnableSpellGlobals, false, "If Enabled, spells check the spell_globals table and compare character data from their quest globals before allowing the spell to scribe with scribespells/traindiscs") +RULE_BOOL(Spells, EnableSpellBuckets, false, "If Enabled, spells check the spell_buckets table and compare character data from their data buckets before allowing the spell to scribe with scribespells/traindiscs") +RULE_INT(Spells, MaxBuffSlotsNPC, 60, "default to Tit's limit") +RULE_INT(Spells, MaxSongSlotsNPC, 0, "NPCs don't have songs ...") +RULE_INT(Spells, MaxDiscSlotsNPC, 0, "NPCs don't have discs ...") +RULE_INT(Spells, MaxTotalSlotsNPC, 60, "default to Tit's limit") +RULE_INT(Spells, MaxTotalSlotsPET, 30, "default to Tit's limit") +RULE_BOOL (Spells, EnableBlockedBuffs, true, "") +RULE_INT(Spells, ReflectType, 3, "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, "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 max level of 0") +RULE_BOOL(Spells, NPCIgnoreBaseImmunity, true, "Whether or not NPCs get to ignore the BaseImmunityLevel for their spells") +RULE_REAL(Spells, AvgSpellProcsPerMinute, 6.0, "Adjust rate for sympathetic spell procs") +RULE_INT(Spells, ResistFalloff, 67, "Max that level that will adjust our resist chance based on level modifiers") +RULE_INT(Spells, CharismaEffectiveness, 10, "Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod") +RULE_INT(Spells, CharismaEffectivenessCap, 255, "Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod") +RULE_BOOL(Spells, CharismaCharmDuration, false, "Allow CHA resist mod to extend charm duration") +RULE_INT(Spells, CharmBreakCheckChance, 25, "Determines chance for a charm break check to occur each buff tick") +RULE_BOOL(Spells, CharmDisablesSpecialAbilities, false, "When charm is cast on an NPC, strip their special abilities") +RULE_INT(Spells, MaxCastTimeReduction, 50, "Max percent your spell cast time can be reduced by spell haste") +RULE_INT(Spells, RootBreakFromSpells, 55, "Chance for root to break when cast on") +RULE_INT(Spells, DeathSaveCharismaMod, 3, "Determines how much charisma effects chance of death save firing") +RULE_INT(Spells, DivineInterventionHeal, 8000, "Divine intervention heal amount") +RULE_INT(Spells, AdditiveBonusWornType, 0, "Calc worn bonuses to add together (instead of taking highest) if set to THIS worn type. (2=Will covert live items automatically)") +RULE_BOOL(Spells, UseCHAScribeHack, false, "ScribeSpells and TrainDiscs quest functions will ignore entries where field 12 is CHA. What's the best way to do this?") +RULE_BOOL(Spells, BuffLevelRestrictions, true, "Buffs will not land on low level toons like live") +RULE_INT(Spells, RootBreakCheckChance, 70, "Determines chance for a root break check to occur each buff tick") +RULE_INT(Spells, FearBreakCheckChance, 70, "Determines chance for a fear break check to occur each buff tick") +RULE_INT(Spells, SuccorFailChance, 2, "Determines chance for a succor spell not to teleport an invidual player") +RULE_INT(Spells, FRProjectileItem_Titanium, 1113, "Item id for Titanium clients for Fire 'spell projectile'") +RULE_INT(Spells, FRProjectileItem_SOF, 80684, "Item id for SOF clients for Fire 'spell projectile'") +RULE_INT(Spells, FRProjectileItem_NPC, 80684, "Item id for NPC Fire 'spell projectile'") +RULE_BOOL(Spells, UseLiveSpellProjectileGFX, false, "Use spell projectile graphics set in the spells_new table (player_1). Server must be using UF+ spell file") +RULE_BOOL(Spells, FocusCombatProcs, false, "Allow all combat procs to receive focus effects") +RULE_BOOL(Spells, PreNerfBardAEDoT, false, "Allow bard AOE dots to damage targets when moving") +RULE_INT(Spells, AI_SpellCastFinishedFailRecast, 800, "AI spell recast time(MS) when an spell is cast but fails (ie stunned)") +RULE_INT(Spells, AI_EngagedNoSpellMinRecast, 500, "AI spell recast time(MS) check when no spell is cast while engaged. (min time in random)") +RULE_INT(Spells, AI_EngagedNoSpellMaxRecast, 1000, "AI spell recast time(MS) check when no spell is cast engaged.(max time in random)") +RULE_INT(Spells, AI_EngagedBeneficialSelfChance, 100, "Chance during first AI Cast check to do a beneficial spell on self") +RULE_INT(Spells, AI_EngagedBeneficialOtherChance, 25, "Chance during second AI Cast check to do a beneficial spell on others") +RULE_INT(Spells, AI_EngagedDetrimentalChance, 20, "Chance during third AI Cast check to do a determental spell on others") +RULE_INT(Spells, AI_PursueNoSpellMinRecast, 500, "AI spell recast time(MS) check when no spell is cast while chasing target. (min time in random)") +RULE_INT(Spells, AI_PursueNoSpellMaxRecast, 2000, "AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random)") +RULE_INT(Spells, AI_PursueDetrimentalChance, 90, "Chance while chasing target to cast a detrimental spell") +RULE_INT(Spells, AI_IdleNoSpellMinRecast, 6000, "AI spell recast time(MS) check when no spell is cast while idle. (min time in random)") +RULE_INT(Spells, AI_IdleNoSpellMaxRecast, 60000, "AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random)") +RULE_INT(Spells, AI_IdleBeneficialChance, 100, "Chance while idle to do a beneficial spell on self or others") +RULE_INT(Spells, AI_HealHPPct, 50, "HP Pct NPCs will start heals at (in and out of combat) if spell's max_hp is not set") +RULE_BOOL(Spells, SHDProcIDOffByOne, true, "pre June 2009 SHD spell procs were off by 1, they stopped doing this in June 2009 (so UF+ spell files need this false)") +RULE_BOOL(Spells, Jun182014HundredHandsRevamp, false, "this should be true for if you import a spell file newer than June 18, 2014") +RULE_BOOL(Spells, SwarmPetTargetLock, false, "Use old method of swarm pets target locking till target dies then despawning") +RULE_BOOL(Spells, NPC_UseFocusFromSpells, true, "Allow npcs to use most spell derived focus effects") +RULE_BOOL(Spells, NPC_UseFocusFromItems, false, "Allow npcs to use most item derived focus effects") +RULE_BOOL(Spells, UseAdditiveFocusFromWornSlot, false, "Allows an additive focus effect to be calculated from worn slot") +RULE_BOOL(Spells, AlwaysSendTargetsBuffs, false, "ignore LAA level if true") +RULE_BOOL(Spells, FlatItemExtraSpellAmt, false, "allow SpellDmg stat to affect all spells, regardless of cast time/cooldown/etc") +RULE_BOOL(Spells, IgnoreSpellDmgLvlRestriction, false, "ignore the 5 level spread on applying SpellDmg") +RULE_BOOL(Spells, AllowItemTGB, false, "TGB doesn't work with items on live, custom servers want it though") +RULE_BOOL(Spells, NPCInnateProcOverride, true, "NPC innate procs override the target type to single target") +RULE_BOOL(Spells, OldRainTargets, false, "use old incorrectly implemented max targets for rains") +RULE_BOOL(Spells, NPCSpellPush, false, "enable spell push on NPCs") RULE_CATEGORY_END() RULE_CATEGORY(Combat) -RULE_INT(Combat, PetBaseCritChance, 0) // Pet Base crit chance -RULE_INT(Combat, NPCBashKickLevel, 6) //The level that npcs can KICK/BASH -RULE_INT(Combat, NPCBashKickStunChance, 15) //Percent chance that a bash/kick will stun -RULE_INT(Combat, MeleeCritDifficulty, 8900) // lower is easier -RULE_INT(Combat, ArcheryCritDifficulty, 3400) // lower is easier -RULE_INT(Combat, ThrowingCritDifficulty, 1100) // lower is easier -RULE_BOOL(Combat, NPCCanCrit, false) // true allows non PC pet NPCs to crit -RULE_BOOL(Combat, UseIntervalAC, true) -RULE_INT(Combat, PetAttackMagicLevel, 30) -RULE_BOOL(Combat, EnableFearPathing, true) -RULE_REAL(Combat, FleeMultiplier, 2.0) // Determines how quickly a NPC will slow down while fleeing. Decrease multiplier to slow NPC down quicker. -RULE_BOOL(Combat, FleeGray, true) // If true FleeGrayHPRatio will be used. -RULE_INT(Combat, FleeGrayHPRatio, 50) //HP % when a Gray NPC begins to flee. -RULE_INT(Combat, FleeGrayMaxLevel, 18) // NPC's above this level won't do gray/green con flee -RULE_INT(Combat, FleeHPRatio, 25) //HP % when a NPC begins to flee. -RULE_BOOL(Combat, FleeIfNotAlone, false) // If false, mobs won't flee if other mobs are in combat with it. -RULE_BOOL(Combat, AdjustProcPerMinute, true) -RULE_REAL(Combat, AvgProcsPerMinute, 2.0) -RULE_REAL(Combat, ProcPerMinDexContrib, 0.075) -RULE_REAL(Combat, BaseProcChance, 0.035) -RULE_REAL(Combat, ProcDexDivideBy, 11000) -RULE_BOOL(Combat, AdjustSpecialProcPerMinute, true) //Set PPM for special abilities like HeadShot (Live does this as of 4-14) -RULE_REAL(Combat, AvgSpecialProcsPerMinute, 2.0) //Unclear what best value is atm. -RULE_REAL(Combat, BaseHitChance, 69.0) -RULE_REAL(Combat, NPCBonusHitChance, 26.0) -RULE_REAL(Combat, HitFalloffMinor, 5.0) //hit will fall off up to 5% over the initial level range -RULE_REAL(Combat, HitFalloffModerate, 7.0) //hit will fall off up to 7% over the three levels after the initial level range -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 % 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 % 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) -RULE_REAL(Combat, MinChancetoHit, 5.0) //Minimum % chance to hit with regular melee/ranged -RULE_REAL(Combat, MaxChancetoHit, 95.0) //Maximum % 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, ArcheryBaseDamageBonus, 1) // % Modifier to Base Archery Damage (.5 = 50% base damage, 1 = 100%, 2 = 200%) -RULE_REAL(Combat, ArcheryNPCMultiplier, 1.0) // this is multiplied by the regular dmg to get the archery dmg -RULE_BOOL(Combat, AssistNoTargetSelf, true) //when assisting a target that does not have a target: true = target self, false = leave target as was before assist (false = live like) -RULE_INT(Combat, MaxRampageTargets, 3) //max number of people hit with rampage -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) //max number of extra hits from flurry -RULE_INT(Combat, MonkDamageTableBonus, 5) //% bonus monks get to their damage table calcs -RULE_INT(Combat, FlyingKickBonus, 25) //% Modifier that this skill gets to str and skill bonuses -RULE_INT(Combat, DragonPunchBonus, 20) //% Modifier that this skill gets to str and skill bonuses -RULE_INT(Combat, EagleStrikeBonus, 15) //% Modifier that this skill gets to str and skill bonuses -RULE_INT(Combat, TigerClawBonus, 10) //% Modifier that this skill gets to str and skill bonuses -RULE_INT(Combat, RoundKickBonus, 5) //% Modifier that this skill gets to str and skill bonuses -RULE_INT(Combat, FrenzyBonus, 0) //% Modifier to damage -RULE_INT(Combat, BackstabBonus, 0) //% Modifier to damage -RULE_BOOL(Combat, ProcTargetOnly, true) //true = procs will only affect our target, false = procs will affect all of our targets -RULE_REAL(Combat, NPCACFactor, 2.25) -RULE_INT(Combat, ClothACSoftcap, 75) -RULE_INT(Combat, LeatherACSoftcap, 100) -RULE_INT(Combat, MonkACSoftcap, 120) -RULE_INT(Combat, ChainACSoftcap, 200) -RULE_INT(Combat, PlateACSoftcap, 300) -RULE_REAL(Combat, AAMitigationACFactor, 3.0) -RULE_REAL(Combat, WarriorACSoftcapReturn, 0.45) -RULE_REAL(Combat, KnightACSoftcapReturn, 0.33) -RULE_REAL(Combat, LowPlateChainACSoftcapReturn, 0.23) -RULE_REAL(Combat, LowChainLeatherACSoftcapReturn, 0.17) -RULE_REAL(Combat, CasterACSoftcapReturn, 0.06) -RULE_REAL(Combat, MiscACSoftcapReturn, 0.3) -RULE_BOOL(Combat, OldACSoftcapRules, false) // use old softcaps -RULE_BOOL(Combat, UseOldDamageIntervalRules, false) // use old damage formulas for everything -RULE_REAL(Combat, WarACSoftcapReturn, 0.3448) // new AC returns -RULE_REAL(Combat, ClrRngMnkBrdACSoftcapReturn, 0.3030) -RULE_REAL(Combat, PalShdACSoftcapReturn, 0.3226) -RULE_REAL(Combat, DruNecWizEncMagACSoftcapReturn, 0.2000) -RULE_REAL(Combat, RogShmBstBerACSoftcapReturn, 0.2500) -RULE_REAL(Combat, SoftcapFactor, 1.88) -RULE_REAL(Combat, ACthac0Factor, 0.55) -RULE_REAL(Combat, ACthac20Factor, 0.55) -RULE_INT(Combat, HitCapPre20, 40) // live has it capped at 40 for whatever dumb reason... this is mainly for custom servers -RULE_INT(Combat, HitCapPre10, 20) // live has it capped at 20, see above :p -RULE_INT(Combat, MinHastedDelay, 400) // how fast we can get with haste. -RULE_REAL(Combat, AvgDefProcsPerMinute, 2.0) -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 % chance to taunt. -RULE_BOOL (Combat,EXPFromDmgShield, false) //Determine if damage from a damage shield counts for EXP gain. -RULE_INT(Combat, MonkACBonusWeight, 15) -RULE_INT(Combat, ClientStunLevel, 55) //This is the level where client kicks and bashes can stun the target -RULE_INT(Combat, QuiverHasteCap, 1000) //Quiver haste cap 1000 on live for a while, currently 700 on live -RULE_BOOL(Combat, UseArcheryBonusRoll, false) //Make the 51+ archery bonus require an actual roll -RULE_INT(Combat, ArcheryBonusChance, 50) -RULE_INT(Combat, BerserkerFrenzyStart, 35) -RULE_INT(Combat, BerserkerFrenzyEnd, 45) -RULE_BOOL(Combat, OneProcPerWeapon, true) //If enabled, One proc per weapon per round -RULE_BOOL(Combat, ProjectileDmgOnImpact, true) //If enabled, projectiles (ie arrows) will hit on impact, instead of instantly. -RULE_BOOL(Combat, MeleePush, true) // enable melee push -RULE_INT(Combat, MeleePushChance, 50) // (NPCs) chance the target will be pushed. Made up, 100 actually isn't that bad -RULE_BOOL(Combat, UseLiveCombatRounds, true) // turn this false if you don't want to worry about fixing up combat rounds for NPCs -RULE_INT(Combat, NPCAssistCap, 5) // Maxiumium number of NPCs that will assist another NPC at once -RULE_INT(Combat, NPCAssistCapTimer, 6000) // Time in milliseconds a NPC will take to clear assist aggro cap space -RULE_BOOL(Combat, UseRevampHandToHand, false) // use h2h revamped dmg/delays I believe this was implemented during SoF -RULE_BOOL(Combat, ClassicMasterWu, false) // classic master wu uses a random special, modern doesn't -RULE_INT(Combat, LevelToStopDamageCaps, 0) // 1 will effectively disable them, 20 should give basically same results as old incorrect system -RULE_BOOL(Combat, ClassicNPCBackstab, false) // true disables npc facestab - npcs get normal attack if not behind -RULE_BOOL(Combat, UseNPCDamageClassLevelMods, true) // Uses GetClassLevelDamageMod calc in npc_scale_manager +RULE_INT(Combat, PetBaseCritChance, 0, "Pet Base crit chance") +RULE_INT(Combat, NPCBashKickLevel, 6, "The level that npcs can KICK/BASH") +RULE_INT(Combat, NPCBashKickStunChance, 15, "Percent chance that a bash/kick will stun") +RULE_INT(Combat, MeleeCritDifficulty, 8900, "lower is easier") +RULE_INT(Combat, ArcheryCritDifficulty, 3400, "lower is easier") +RULE_INT(Combat, ThrowingCritDifficulty, 1100, "lower is easier") +RULE_BOOL(Combat, NPCCanCrit, false, "true allows non PC pet NPCs to crit") +RULE_BOOL(Combat, UseIntervalAC, true, "") +RULE_INT(Combat, PetAttackMagicLevel, 30, "") +RULE_BOOL(Combat, EnableFearPathing, true, "") +RULE_REAL(Combat, FleeMultiplier, 2.0, "Determines how quickly a NPC will slow down while fleeing. Decrease multiplier to slow NPC down quicker") +RULE_BOOL(Combat, FleeGray, true, "If true FleeGrayHPRatio will be used") +RULE_INT(Combat, FleeGrayHPRatio, 50, "HP % when a Gray NPC begins to flee") +RULE_INT(Combat, FleeGrayMaxLevel, 18, "NPC's above this level won't do gray/green con flee") +RULE_INT(Combat, FleeHPRatio, 25, "HP % when a NPC begins to flee") +RULE_BOOL(Combat, FleeIfNotAlone, false, "If false, mobs won't flee if other mobs are in combat with it") +RULE_BOOL(Combat, AdjustProcPerMinute, true, "") +RULE_REAL(Combat, AvgProcsPerMinute, 2.0, "") +RULE_REAL(Combat, ProcPerMinDexContrib, 0.075, "") +RULE_REAL(Combat, BaseProcChance, 0.035, "") +RULE_REAL(Combat, ProcDexDivideBy, 11000, "") +RULE_BOOL(Combat, AdjustSpecialProcPerMinute, true, "Set PPM for special abilities like HeadShot (Live does this as of 4-14)") +RULE_REAL(Combat, AvgSpecialProcsPerMinute, 2.0, "Unclear what best value is atm") +RULE_REAL(Combat, BaseHitChance, 69.0, "") +RULE_REAL(Combat, NPCBonusHitChance, 26.0, "") +RULE_REAL(Combat, HitFalloffMinor, 5.0, "hit will fall off up to 5% over the initial level range") +RULE_REAL(Combat, HitFalloffModerate, 7.0, "hit will fall off up to 7% over the three levels after the initial level range") +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 % 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 % 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, "") +RULE_REAL(Combat, MinChancetoHit, 5.0, "Minimum % chance to hit with regular melee/ranged") +RULE_REAL(Combat, MaxChancetoHit, 95.0, "Maximum % 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, ArcheryBaseDamageBonus, 1, "% Modifier to Base Archery Damage (.5 = 50% base damage, 1 = 100%, 2 = 200%)") +RULE_REAL(Combat, ArcheryNPCMultiplier, 1.0, "this is multiplied by the regular dmg to get the archery dmg") +RULE_BOOL(Combat, AssistNoTargetSelf, true, "when assisting a target that does not have a target: true = target self, false = leave target as was before assist (false = live like)") +RULE_INT(Combat, MaxRampageTargets, 3, "max number of people hit with rampage") +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, "max number of extra hits from flurry") +RULE_INT(Combat, MonkDamageTableBonus, 5, "% bonus monks get to their damage table calcs") +RULE_INT(Combat, FlyingKickBonus, 25, "% Modifier that this skill gets to str and skill bonuses") +RULE_INT(Combat, DragonPunchBonus, 20, "% Modifier that this skill gets to str and skill bonuses") +RULE_INT(Combat, EagleStrikeBonus, 15, "% Modifier that this skill gets to str and skill bonuses") +RULE_INT(Combat, TigerClawBonus, 10, "% Modifier that this skill gets to str and skill bonuses") +RULE_INT(Combat, RoundKickBonus, 5, "% Modifier that this skill gets to str and skill bonuses") +RULE_INT(Combat, FrenzyBonus, 0, "% Modifier to damage") +RULE_INT(Combat, BackstabBonus, 0, "% Modifier to damage") +RULE_BOOL(Combat, ProcTargetOnly, true, "true = procs will only affect our target, false = procs will affect all of our targets") +RULE_REAL(Combat, NPCACFactor, 2.25, "") +RULE_INT(Combat, ClothACSoftcap, 75, "") +RULE_INT(Combat, LeatherACSoftcap, 100, "") +RULE_INT(Combat, MonkACSoftcap, 120, "") +RULE_INT(Combat, ChainACSoftcap, 200, "") +RULE_INT(Combat, PlateACSoftcap, 300, "") +RULE_REAL(Combat, AAMitigationACFactor, 3.0, "") +RULE_REAL(Combat, WarriorACSoftcapReturn, 0.45, "") +RULE_REAL(Combat, KnightACSoftcapReturn, 0.33, "") +RULE_REAL(Combat, LowPlateChainACSoftcapReturn, 0.23, "") +RULE_REAL(Combat, LowChainLeatherACSoftcapReturn, 0.17, "") +RULE_REAL(Combat, CasterACSoftcapReturn, 0.06, "") +RULE_REAL(Combat, MiscACSoftcapReturn, 0.3, "") +RULE_BOOL(Combat, OldACSoftcapRules, false, "use old softcaps") +RULE_BOOL(Combat, UseOldDamageIntervalRules, false, "use old damage formulas for everything") +RULE_REAL(Combat, WarACSoftcapReturn, 0.3448, "new AC returns") +RULE_REAL(Combat, ClrRngMnkBrdACSoftcapReturn, 0.3030, "") +RULE_REAL(Combat, PalShdACSoftcapReturn, 0.3226, "") +RULE_REAL(Combat, DruNecWizEncMagACSoftcapReturn, 0.2000, "") +RULE_REAL(Combat, RogShmBstBerACSoftcapReturn, 0.2500, "") +RULE_REAL(Combat, SoftcapFactor, 1.88, "") +RULE_REAL(Combat, ACthac0Factor, 0.55, "") +RULE_REAL(Combat, ACthac20Factor, 0.55, "") +RULE_INT(Combat, HitCapPre20, 40, "live has it capped at 40 for whatever dumb reason... this is mainly for custom servers") +RULE_INT(Combat, HitCapPre10, 20, "live has it capped at 20, see above") +RULE_INT(Combat, MinHastedDelay, 400, "how fast we can get with haste") +RULE_REAL(Combat, AvgDefProcsPerMinute, 2.0, "") +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 % chance to taunt") +RULE_BOOL (Combat,EXPFromDmgShield, false, "Determine if damage from a damage shield counts for EXP gain") +RULE_INT(Combat, MonkACBonusWeight, 15, "") +RULE_INT(Combat, ClientStunLevel, 55, "This is the level where client kicks and bashes can stun the target") +RULE_INT(Combat, QuiverHasteCap, 1000, "Quiver haste cap 1000 on live for a while, currently 700 on live") +RULE_BOOL(Combat, UseArcheryBonusRoll, false, "Make the 51+ archery bonus require an actual roll") +RULE_INT(Combat, ArcheryBonusChance, 50, "") +RULE_INT(Combat, BerserkerFrenzyStart, 35, "") +RULE_INT(Combat, BerserkerFrenzyEnd, 45, "") +RULE_BOOL(Combat, OneProcPerWeapon, true, "If enabled, One proc per weapon per round") +RULE_BOOL(Combat, ProjectileDmgOnImpact, true, "If enabled, projectiles (ie arrows) will hit on impact, instead of instantly") +RULE_BOOL(Combat, MeleePush, true, "enable melee push") +RULE_INT(Combat, MeleePushChance, 50, "(NPCs) chance the target will be pushed. Made up, 100 actually isn't that bad") +RULE_BOOL(Combat, UseLiveCombatRounds, true, "turn this false if you don't want to worry about fixing up combat rounds for NPCs") +RULE_INT(Combat, NPCAssistCap, 5, "Maxiumium number of NPCs that will assist another NPC at once") +RULE_INT(Combat, NPCAssistCapTimer, 6000, "Time in milliseconds a NPC will take to clear assist aggro cap space") +RULE_BOOL(Combat, UseRevampHandToHand, false, "use h2h revamped dmg/delays I believe this was implemented during SoF") +RULE_BOOL(Combat, ClassicMasterWu, false, "classic master wu uses a random special, modern doesn't") +RULE_INT(Combat, LevelToStopDamageCaps, 0, "1 will effectively disable them, 20 should give basically same results as old incorrect system") +RULE_BOOL(Combat, ClassicNPCBackstab, false, "true disables npc facestab - npcs get normal attack if not behind") +RULE_BOOL(Combat, UseNPCDamageClassLevelMods, true, "Uses GetClassLevelDamageMod calc in npc_scale_manager") RULE_CATEGORY_END() RULE_CATEGORY(NPC) -RULE_INT(NPC, MinorNPCCorpseDecayTimeMS, 450000) //level<55 -RULE_INT(NPC, MajorNPCCorpseDecayTimeMS, 1500000) //level>=55 -RULE_INT(NPC, CorpseUnlockTimer, 150000) -RULE_INT(NPC, EmptyNPCCorpseDecayTimeMS, 0) -RULE_BOOL(NPC, UseItemBonusesForNonPets, true) -RULE_BOOL(NPC, UseBaneDamage, false) -RULE_INT(NPC, SayPauseTimeInSec, 5) -RULE_INT(NPC, OOCRegen, 0) -RULE_BOOL(NPC, BuffFriends, false) -RULE_BOOL(NPC, EnableNPCQuestJournal, false) -RULE_INT(NPC, LastFightingDelayMovingMin, 10000) -RULE_INT(NPC, LastFightingDelayMovingMax, 20000) -RULE_BOOL(NPC, SmartLastFightingDelayMoving, true) -RULE_BOOL(NPC, ReturnNonQuestNoDropItems, false) // Returns NO DROP items on NPCs that don't have an EVENT_TRADE sub in their script -RULE_INT(NPC, StartEnrageValue, 9) // % HP that an NPC will begin to enrage -RULE_BOOL(NPC, LiveLikeEnrage, false) // If set to true then only player controlled pets will enrage -RULE_BOOL(NPC, EnableMeritBasedFaction, false) // If set to true, faction will given in the same way as experience (solo/group/raid) -RULE_INT(NPC, NPCToNPCAggroTimerMin, 500) -RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000) -RULE_BOOL(NPC, UseClassAsLastName, true) // Uses class archetype as LastName for npcs with none -RULE_BOOL(NPC, NewLevelScaling, true) // Better level scaling, use old if new formulas would break your server -RULE_INT(NPC, NPCGatePercent, 5) // % at which the NPC Will attempt to gate at. -RULE_BOOL(NPC, NPCGateNearBind, false) // Will NPC attempt to gate when near bind location? -RULE_INT(NPC, NPCGateDistanceBind, 75) // Distance from bind before NPC will attempt to gate -RULE_BOOL(NPC, NPCHealOnGate, true) // Will the NPC Heal on Gate. -RULE_REAL(NPC, NPCHealOnGateAmount, 25) // How much the npc will heal on gate if enabled. +RULE_INT(NPC, MinorNPCCorpseDecayTimeMS, 450000, "level < 55") +RULE_INT(NPC, MajorNPCCorpseDecayTimeMS, 1500000, "level >= 55") +RULE_INT(NPC, CorpseUnlockTimer, 150000, "") +RULE_INT(NPC, EmptyNPCCorpseDecayTimeMS, 0, "") +RULE_BOOL(NPC, UseItemBonusesForNonPets, true, "") +RULE_BOOL(NPC, UseBaneDamage, false, "") +RULE_INT(NPC, SayPauseTimeInSec, 5, "") +RULE_INT(NPC, OOCRegen, 0, "") +RULE_BOOL(NPC, BuffFriends, false, "") +RULE_BOOL(NPC, EnableNPCQuestJournal, false, "") +RULE_INT(NPC, LastFightingDelayMovingMin, 10000, "") +RULE_INT(NPC, LastFightingDelayMovingMax, 20000, "") +RULE_BOOL(NPC, SmartLastFightingDelayMoving, true, "") +RULE_BOOL(NPC, ReturnNonQuestNoDropItems, false, "Returns NO DROP items on NPCs that don't have an EVENT_TRADE sub in their script") +RULE_INT(NPC, StartEnrageValue, 9, "% HP that an NPC will begin to enrage") +RULE_BOOL(NPC, LiveLikeEnrage, false, "If set to true then only player controlled pets will enrage") +RULE_BOOL(NPC, EnableMeritBasedFaction, false, "If set to true, faction will given in the same way as experience (solo/group/raid)") +RULE_INT(NPC, NPCToNPCAggroTimerMin, 500, "") +RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000, "") +RULE_BOOL(NPC, UseClassAsLastName, true, "Uses class archetype as LastName for npcs with none") +RULE_BOOL(NPC, NewLevelScaling, true, "Better level scaling, use old if new formulas would break your server") +RULE_INT(NPC, NPCGatePercent, 5, "% at which the NPC Will attempt to gate at") +RULE_BOOL(NPC, NPCGateNearBind, false, "Will NPC attempt to gate when near bind location?") +RULE_INT(NPC, NPCGateDistanceBind, 75, "Distance from bind before NPC will attempt to gate") +RULE_BOOL(NPC, NPCHealOnGate, true, "Will the NPC Heal on Gate") +RULE_REAL(NPC, NPCHealOnGateAmount, 25, "How much the npc will heal on gate if enabled") RULE_CATEGORY_END() RULE_CATEGORY(Aggro) -RULE_BOOL(Aggro, SmartAggroList, true) -RULE_INT(Aggro, SittingAggroMod, 35) //35% -RULE_INT(Aggro, MeleeRangeAggroMod, 10) //10% -RULE_INT(Aggro, CurrentTargetAggroMod, 0) //0% -- will prefer our current target to any other; makes it harder for our npcs to switch targets. -RULE_INT(Aggro, CriticallyWoundedAggroMod, 100) //100% -RULE_INT(Aggro, SpellAggroMod, 100) -RULE_INT(Aggro, SongAggroMod, 33) -RULE_INT(Aggro, PetSpellAggroMod, 10) -RULE_REAL(Aggro, TunnelVisionAggroMod, 0.75) //people not currently the top hate generate this much hate on a Tunnel Vision mob -RULE_INT(Aggro, MaxScalingProcAggro, 400) // Set to -1 for no limit. Maxmimum amount of aggro that HP scaling SPA effect in a proc will add. -RULE_INT(Aggro, IntAggroThreshold, 75) // Int <= this will aggro regardless of level difference. -RULE_BOOL(Aggro, AllowTickPulling, false) // tick pulling is an exploit in an NPC's call for help fixed sometime in 2006 on live -RULE_INT(Aggro, MinAggroLevel, 18) // For use with UseLevelAggro -RULE_BOOL(Aggro, UseLevelAggro, true) // MinAggroLevel rule value+ and Undead will aggro regardless of level difference. (this will disabled Rule:IntAggroThreshold if set to true) -RULE_INT(Aggro, ClientAggroCheckInterval, 6) // Interval in which clients actually check for aggro - in seconds -RULE_REAL(Aggro, PetAttackRange, 40000.0) // max squared range /pet attack works at default is 200 -RULE_BOOL(Aggro, NPCAggroMaxDistanceEnabled, true) /* If enabled, NPC's will drop aggro beyond 600 units or what is defined at the zone level */ +RULE_BOOL(Aggro, SmartAggroList, true, "") +RULE_INT(Aggro, SittingAggroMod, 35, "35%") +RULE_INT(Aggro, MeleeRangeAggroMod, 10, "10%") +RULE_INT(Aggro, CurrentTargetAggroMod, 0, "0% -- will prefer our current target to any other; makes it harder for our npcs to switch targets") +RULE_INT(Aggro, CriticallyWoundedAggroMod, 100, "100%") +RULE_INT(Aggro, SpellAggroMod, 100, "") +RULE_INT(Aggro, SongAggroMod, 33, "") +RULE_INT(Aggro, PetSpellAggroMod, 10, "") +RULE_REAL(Aggro, TunnelVisionAggroMod, 0.75, "people not currently the top hate generate this much hate on a Tunnel Vision mob") +RULE_INT(Aggro, MaxScalingProcAggro, 400, "Set to -1 for no limit. Maxmimum amount of aggro that HP scaling SPA effect in a proc will add") +RULE_INT(Aggro, IntAggroThreshold, 75, "Int <= this will aggro regardless of level difference") +RULE_BOOL(Aggro, AllowTickPulling, false, "tick pulling is an exploit in an NPC's call for help fixed sometime in 2006 on live") +RULE_INT(Aggro, MinAggroLevel, 18, "For use with UseLevelAggro") +RULE_BOOL(Aggro, UseLevelAggro, true, "MinAggroLevel rule value+ and Undead will aggro regardless of level difference. (this will disabled Rule:IntAggroThreshold if set to true)") +RULE_INT(Aggro, ClientAggroCheckInterval, 6, "Interval in which clients actually check for aggro - in seconds") +RULE_REAL(Aggro, PetAttackRange, 40000.0, "max squared range /pet attack works at default is 200") +RULE_BOOL(Aggro, NPCAggroMaxDistanceEnabled, true, "If enabled, NPC's will drop aggro beyond 600 units or what is defined at the zone level") RULE_CATEGORY_END() RULE_CATEGORY(TaskSystem) -RULE_BOOL(TaskSystem, EnableTaskSystem, true) // Globally enable or disable the Task system -RULE_INT(TaskSystem, PeriodicCheckTimer, 5) // Seconds between checks for failed tasks. Also used by the 'Touch' activity -RULE_BOOL(TaskSystem, RecordCompletedTasks, true) -RULE_BOOL(TaskSystem, RecordCompletedOptionalActivities, false) -RULE_BOOL(TaskSystem, KeepOneRecordPerCompletedTask, true) -RULE_BOOL(TaskSystem, EnableTaskProximity, true) +RULE_BOOL(TaskSystem, EnableTaskSystem, true, "Globally enable or disable the Task system") +RULE_INT(TaskSystem, PeriodicCheckTimer, 5, "Seconds between checks for failed tasks. Also used by the 'Touch' activity") +RULE_BOOL(TaskSystem, RecordCompletedTasks, true, "") +RULE_BOOL(TaskSystem, RecordCompletedOptionalActivities, false, "") +RULE_BOOL(TaskSystem, KeepOneRecordPerCompletedTask, true, "") +RULE_BOOL(TaskSystem, EnableTaskProximity, true, "") RULE_CATEGORY_END() RULE_CATEGORY(Range) -RULE_INT(Range, Say, 15) -RULE_INT(Range, Emote, 135) -RULE_INT(Range, BeginCast, 200) -RULE_INT(Range, Anims, 135) -RULE_INT(Range, SpellParticles, 135) -RULE_INT(Range, DamageMessages, 50) -RULE_INT(Range, SpellMessages, 75) -RULE_INT(Range, SongMessages, 75) -RULE_INT(Range, MobPositionUpdates, 600) -RULE_INT(Range, ClientPositionUpdates, 300) -RULE_INT(Range, ClientForceSpawnUpdateRange, 1000) -RULE_INT(Range, CriticalDamage, 80) -RULE_INT(Range, ClientNPCScan, 300) +RULE_INT(Range, Say, 15, "") +RULE_INT(Range, Emote, 135, "") +RULE_INT(Range, BeginCast, 200, "") +RULE_INT(Range, Anims, 135, "") +RULE_INT(Range, SpellParticles, 135, "") +RULE_INT(Range, DamageMessages, 50, "") +RULE_INT(Range, SpellMessages, 75, "") +RULE_INT(Range, SongMessages, 75, "") +RULE_INT(Range, MobPositionUpdates, 600, "") +RULE_INT(Range, ClientPositionUpdates, 300, "") +RULE_INT(Range, ClientForceSpawnUpdateRange, 1000, "") +RULE_INT(Range, CriticalDamage, 80, "") +RULE_INT(Range, ClientNPCScan, 300, "") RULE_CATEGORY_END() #ifdef BOTS RULE_CATEGORY(Bots) -RULE_INT(Bots, AAExpansion, 8) // Bots get AAs through this expansion -RULE_BOOL(Bots, AllowCamelCaseNames, false) // Allows the use of 'MyBot' type names -RULE_INT(Bots, CommandSpellRank, 1) // Filters bot command spells by rank (1, 2 and 3 are valid filters - any other number allows all ranks) -RULE_INT(Bots, CreationLimit, 150) // Number of bots that each account can create -RULE_BOOL(Bots, FinishBuffing, false) // Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat. -RULE_BOOL(Bots, GroupBuffing, false) // Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB. -RULE_INT(Bots, HealRotationMaxMembers, 24) // Maximum number of heal rotation members -RULE_INT(Bots, HealRotationMaxTargets, 12) // Maximum number of heal rotation targets -RULE_REAL(Bots, ManaRegen, 2.0) // Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players. -RULE_BOOL(Bots, PreferNoManaCommandSpells, true) // Give sorting priority to newer no-mana spells (i.e., 'Bind Affinity') -RULE_BOOL(Bots, QuestableSpawnLimit, false) // Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl -RULE_BOOL(Bots, QuestableSpells, false) // Anita Thrall's (Anita_Thrall.pl) Bot Spell Scriber quests. -RULE_INT(Bots, SpawnLimit, 71) // Number of bots a character can have spawned at one time, You + 71 bots is a 12 group pseudo-raid (bots are not raidable at this time) -RULE_BOOL(Bots, UpdatePositionWithTimer, false) // Sends a position update with every positive movement timer check -RULE_BOOL(Bots, UsePathing, true) // Bots will use node pathing when moving -RULE_BOOL(Bots, BotGroupXP, false) // Determines whether client gets xp for bots outside their group. -RULE_BOOL(Bots, BotBardUseOutOfCombatSongs, true) // Determines whether bard bots use additional out of combat songs (optional script) -RULE_BOOL(Bots, BotLevelsWithOwner, false) // Auto-updates spawned bots as owner levels/de-levels (false is original behavior) -RULE_BOOL(Bots, BotCharacterLevelEnabled, false) // Enables required level to spawn bots -RULE_INT(Bots, BotCharacterLevel, 0) // 0 as default (if level > this value you can spawn bots if BotCharacterLevelEnabled is true) -RULE_INT(Bots, CasterStopMeleeLevel, 13) // Level at which caster bots stop melee attacks -RULE_INT(Bots, AllowedClasses, 0xFFFFFFFF) // Bitmask of allowed bot classes -RULE_INT(Bots, AllowedRaces, 0xFFFFFFFF) // Bitmask of allowed bot races -RULE_INT(Bots, AllowedGenders, 0x3) // Bitmask of allowed bot genders +RULE_INT(Bots, AAExpansion, 8, "Bots get AAs through this expansion") +RULE_BOOL(Bots, AllowCamelCaseNames, false, "Allows the use of 'MyBot' type names") +RULE_INT(Bots, CommandSpellRank, 1, "Filters bot command spells by rank (1, 2 and 3 are valid filters - any other number allows all ranks)") +RULE_INT(Bots, CreationLimit, 150, "Number of bots that each account can create") +RULE_BOOL(Bots, FinishBuffing, false, "Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat") +RULE_BOOL(Bots, GroupBuffing, false, "Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB") +RULE_INT(Bots, HealRotationMaxMembers, 24, "Maximum number of heal rotation members") +RULE_INT(Bots, HealRotationMaxTargets, 12, "Maximum number of heal rotation targets") +RULE_REAL(Bots, ManaRegen, 2.0, "Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players") +RULE_BOOL(Bots, PreferNoManaCommandSpells, true, "Give sorting priority to newer no-mana spells (i.e., 'Bind Affinity')") +RULE_BOOL(Bots, QuestableSpawnLimit, false, "Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl") +RULE_BOOL(Bots, QuestableSpells, false, "Anita Thrall's (Anita_Thrall.pl) Bot Spell Scriber quests") +RULE_INT(Bots, SpawnLimit, 71, "Number of bots a character can have spawned at one time, You + 71 bots is a 12 group pseudo-raid (bots are not raidable at this time)") +RULE_BOOL(Bots, UpdatePositionWithTimer, false, "Sends a position update with every positive movement timer check") +RULE_BOOL(Bots, UsePathing, true, "Bots will use node pathing when moving") +RULE_BOOL(Bots, BotGroupXP, false, "Determines whether client gets xp for bots outside their group") +RULE_BOOL(Bots, BotBardUseOutOfCombatSongs, true, "Determines whether bard bots use additional out of combat songs (optional script)") +RULE_BOOL(Bots, BotLevelsWithOwner, false, "Auto-updates spawned bots as owner levels/de-levels (false is original behavior)") +RULE_BOOL(Bots, BotCharacterLevelEnabled, false, "Enables required level to spawn bots") +RULE_INT(Bots, BotCharacterLevel, 0, "0 as default (if level > this value you can spawn bots if BotCharacterLevelEnabled is true)") +RULE_INT(Bots, CasterStopMeleeLevel, 13, "Level at which caster bots stop melee attacks") +RULE_INT(Bots, AllowedClasses, 0xFFFFFFFF, "Bitmask of allowed bot classes") +RULE_INT(Bots, AllowedRaces, 0xFFFFFFFF, "Bitmask of allowed bot races") +RULE_INT(Bots, AllowedGenders, 0x3, "Bitmask of allowed bot genders") RULE_CATEGORY_END() #endif RULE_CATEGORY(Chat) -RULE_BOOL(Chat, ServerWideOOC, true) -RULE_BOOL(Chat, ServerWideAuction, true) -RULE_BOOL(Chat, EnableVoiceMacros, true) -RULE_BOOL(Chat, EnableMailKeyIPVerification, true) -RULE_BOOL(Chat, EnableAntiSpam, true) -RULE_BOOL(Chat, SuppressCommandErrors, false) // Do not suppress by default -RULE_INT(Chat, MinStatusToBypassAntiSpam, 100) -RULE_INT(Chat, MinimumMessagesPerInterval, 4) -RULE_INT(Chat, MaximumMessagesPerInterval, 12) -RULE_INT(Chat, MaxMessagesBeforeKick, 20) -RULE_INT(Chat, IntervalDurationMS, 60000) -RULE_INT(Chat, KarmaUpdateIntervalMS, 1200000) -RULE_INT(Chat, KarmaGlobalChatLimit, 72) //amount of karma you need to be able to talk in ooc/auction/chat below the level limit -RULE_INT(Chat, GlobalChatLevelLimit, 8) //level limit you need to of reached to talk in ooc/auction/chat if your karma is too low. +RULE_BOOL(Chat, ServerWideOOC, true, "") +RULE_BOOL(Chat, ServerWideAuction, true, "") +RULE_BOOL(Chat, EnableVoiceMacros, true, "") +RULE_BOOL(Chat, EnableMailKeyIPVerification, true, "") +RULE_BOOL(Chat, EnableAntiSpam, true, "") +RULE_BOOL(Chat, SuppressCommandErrors, false, "Do not suppress by default") +RULE_INT(Chat, MinStatusToBypassAntiSpam, 100, "") +RULE_INT(Chat, MinimumMessagesPerInterval, 4, "") +RULE_INT(Chat, MaximumMessagesPerInterval, 12, "") +RULE_INT(Chat, MaxMessagesBeforeKick, 20, "") +RULE_INT(Chat, IntervalDurationMS, 60000, "") +RULE_INT(Chat, KarmaUpdateIntervalMS, 1200000, "") +RULE_INT(Chat, KarmaGlobalChatLimit, 72, "amount of karma you need to be able to talk in ooc/auction/chat below the level limit") +RULE_INT(Chat, GlobalChatLevelLimit, 8, "level limit you need to of reached to talk in ooc/auction/chat if your karma is too low") RULE_CATEGORY_END() RULE_CATEGORY(Merchant) -RULE_BOOL(Merchant, UsePriceMod, true) // Use faction/charisma price modifiers. -RULE_REAL(Merchant, SellCostMod, 1.05) // Modifier for NPC sell price. -RULE_REAL(Merchant, BuyCostMod, 0.95) // Modifier for NPC buy price. -RULE_INT(Merchant, PriceBonusPct, 4) // Determines maximum price bonus from having good faction/CHA. Value is a percent. -RULE_INT(Merchant, PricePenaltyPct, 4) // Determines maximum price penalty from having bad faction/CHA. Value is a percent. -RULE_REAL(Merchant, ChaBonusMod, 3.45) // Determines CHA cap, from 104 CHA. 3.45 is 132 CHA at apprehensive. 0.34 is 400 CHA at apprehensive. -RULE_REAL(Merchant, ChaPenaltyMod, 1.52) // Determines CHA bottom, up to 102 CHA. 1.52 is 37 CHA at apprehensive. 0.98 is 0 CHA at apprehensive. -RULE_BOOL(Merchant, EnableAltCurrencySell, true) // Enables the ability to resell items to alternate currency merchants +RULE_BOOL(Merchant, UsePriceMod, true, "Use faction/charisma price modifiers") +RULE_REAL(Merchant, SellCostMod, 1.05, "Modifier for NPC sell price") +RULE_REAL(Merchant, BuyCostMod, 0.95, "Modifier for NPC buy price") +RULE_INT(Merchant, PriceBonusPct, 4, "Determines maximum price bonus from having good faction/CHA. Value is a percent") +RULE_INT(Merchant, PricePenaltyPct, 4, "Determines maximum price penalty from having bad faction/CHA. Value is a percent") +RULE_REAL(Merchant, ChaBonusMod, 3.45, "Determines CHA cap, from 104 CHA. 3.45 is 132 CHA at apprehensive. 0.34 is 400 CHA at apprehensive") +RULE_REAL(Merchant, ChaPenaltyMod, 1.52, "Determines CHA bottom, up to 102 CHA. 1.52 is 37 CHA at apprehensive. 0.98 is 0 CHA at apprehensive") +RULE_BOOL(Merchant, EnableAltCurrencySell, true, "Enables the ability to resell items to alternate currency merchants") RULE_CATEGORY_END() RULE_CATEGORY(Bazaar) -RULE_BOOL(Bazaar, AuditTrail, false) -RULE_INT(Bazaar, MaxSearchResults, 50) -RULE_BOOL(Bazaar, EnableWarpToTrader, true) -RULE_INT(Bazaar, MaxBarterSearchResults, 200) // The max results returned in the /barter search +RULE_BOOL(Bazaar, AuditTrail, false, "") +RULE_INT(Bazaar, MaxSearchResults, 50, "") +RULE_BOOL(Bazaar, EnableWarpToTrader, true, "") +RULE_INT(Bazaar, MaxBarterSearchResults, 200, "The max results returned in the /barter search") RULE_CATEGORY_END() RULE_CATEGORY(Mail) -RULE_BOOL(Mail, EnableMailSystem, true) // If false, client won't bring up the Mail window. -RULE_INT(Mail, ExpireTrash, 0) // Time in seconds. 0 will delete all messages in the trash when the mailserver starts -RULE_INT(Mail, ExpireRead, 31536000) // 1 Year. Set to -1 for never -RULE_INT(Mail, ExpireUnread, 31536000) // 1 Year. Set to -1 for never +RULE_BOOL(Mail, EnableMailSystem, true, "If false, client won't bring up the Mail window") +RULE_INT(Mail, ExpireTrash, 0, "Time in seconds. 0 will delete all messages in the trash when the mailserver starts") +RULE_INT(Mail, ExpireRead, 31536000, "1 Year. Set to -1 for never") +RULE_INT(Mail, ExpireUnread, 31536000, "1 Year. Set to -1 for never") RULE_CATEGORY_END() RULE_CATEGORY(Channels) -RULE_INT(Channels, RequiredStatusAdmin, 251) // Required status to administer chat channels -RULE_INT(Channels, RequiredStatusListAll, 251) // Required status to list all chat channels -RULE_INT(Channels, DeleteTimer, 1440) // Empty password protected channels will be deleted after this many minutes +RULE_INT(Channels, RequiredStatusAdmin, 251, "Required status to administer chat channels") +RULE_INT(Channels, RequiredStatusListAll, 251, "Required status to list all chat channels") +RULE_INT(Channels, DeleteTimer, 1440, "Empty password protected channels will be deleted after this many minutes") RULE_CATEGORY_END() RULE_CATEGORY(EventLog) -RULE_BOOL(EventLog, RecordSellToMerchant, false) // Record sales from a player to an NPC merchant in eventlog table -RULE_BOOL(EventLog, RecordBuyFromMerchant, false) // Record purchases by a player from an NPC merchant in eventlog table +RULE_BOOL(EventLog, RecordSellToMerchant, false, "Record sales from a player to an NPC merchant in eventlog table") +RULE_BOOL(EventLog, RecordBuyFromMerchant, false, "Record purchases by a player from an NPC merchant in eventlog table") RULE_CATEGORY_END() RULE_CATEGORY(Adventure) -RULE_INT(Adventure, MinNumberForGroup, 2) -RULE_INT(Adventure, MaxNumberForGroup, 6) -RULE_INT(Adventure, MinNumberForRaid, 18) -RULE_INT(Adventure, MaxNumberForRaid, 36) -RULE_INT(Adventure, MaxLevelRange, 9) -RULE_INT(Adventure, NumberKillsForBossSpawn, 45) -RULE_REAL(Adventure, DistanceForRescueAccept, 10000.0) -RULE_REAL(Adventure, DistanceForRescueComplete, 2500.0) -RULE_INT(Adventure, ItemIDToEnablePorts, 41000) //0 to disable, otherwise using a LDoN portal will require the user to have this item. -RULE_INT(Adventure, LDoNTrapDistanceUse, 625) -RULE_REAL(Adventure, LDoNBaseTrapDifficulty, 15.0) -RULE_REAL(Adventure, LDoNCriticalFailTrapThreshold, 10.0) -RULE_INT(Adventure, LDoNAdventureExpireTime, 1800) //30 minutes to expire +RULE_INT(Adventure, MinNumberForGroup, 2, "") +RULE_INT(Adventure, MaxNumberForGroup, 6, "") +RULE_INT(Adventure, MinNumberForRaid, 18, "") +RULE_INT(Adventure, MaxNumberForRaid, 36, "") +RULE_INT(Adventure, MaxLevelRange, 9, "") +RULE_INT(Adventure, NumberKillsForBossSpawn, 45, "") +RULE_REAL(Adventure, DistanceForRescueAccept, 10000.0, "") +RULE_REAL(Adventure, DistanceForRescueComplete, 2500.0, "") +RULE_INT(Adventure, ItemIDToEnablePorts, 41000, "0 to disable, otherwise using a LDoN portal will require the user to have this item") +RULE_INT(Adventure, LDoNTrapDistanceUse, 625, "") +RULE_REAL(Adventure, LDoNBaseTrapDifficulty, 15.0, "") +RULE_REAL(Adventure, LDoNCriticalFailTrapThreshold, 10.0, "") +RULE_INT(Adventure, LDoNAdventureExpireTime, 1800, "30 minutes to expire") RULE_CATEGORY_END() RULE_CATEGORY(AA) -RULE_INT(AA, ExpPerPoint, 23976503) //Amount of exp per AA. Is the same as the amount of exp to go from level 51 to level 52. -RULE_BOOL(AA, Stacking, true) //Allow AA that belong to the same group to stack on SOF+ clients. -RULE_BOOL(AA, NormalizedAAEnabled, false) // TSS+ change to AA that normalizes AA XP to a fixed # of white con kills independent of level. -RULE_INT(AA, NormalizedAANumberOfWhiteConPerAA, 25) // The number of white con kills per AA point. -RULE_BOOL(AA, ModernAAScalingEnabled, false) // Are we linearly scaling AA XP based on total # of earned AA? -RULE_REAL(AA, ModernAAScalingStartPercent, 1000) // 1000% or 10x AA XP at the start of the scaling range -RULE_INT(AA, ModernAAScalingAAMinimum, 0) // The minimum number of earned AA before AA XP scaling begins. -RULE_INT(AA, ModernAAScalingAALimit, 4000) // The number of earned AA when AA XP scaling ends +RULE_INT(AA, ExpPerPoint, 23976503, "Amount of exp per AA. Is the same as the amount of exp to go from level 51 to level 52") +RULE_BOOL(AA, Stacking, true, "Allow AA that belong to the same group to stack on SOF+ clients") +RULE_BOOL(AA, NormalizedAAEnabled, false, "TSS+ change to AA that normalizes AA XP to a fixed # of white con kills independent of level") +RULE_INT(AA, NormalizedAANumberOfWhiteConPerAA, 25, "The number of white con kills per AA point") +RULE_BOOL(AA, ModernAAScalingEnabled, false, "Are we linearly scaling AA XP based on total # of earned AA?") +RULE_REAL(AA, ModernAAScalingStartPercent, 1000, "1000% or 10x AA XP at the start of the scaling range") +RULE_INT(AA, ModernAAScalingAAMinimum, 0, "The minimum number of earned AA before AA XP scaling begins") +RULE_INT(AA, ModernAAScalingAALimit, 4000, "The number of earned AA when AA XP scaling ends") RULE_CATEGORY_END() RULE_CATEGORY(Console) -RULE_INT(Console, SessionTimeOut, 600000) // Amount of time in ms for the console session to time out +RULE_INT(Console, SessionTimeOut, 600000, "Amount of time in ms for the console session to time out") RULE_CATEGORY_END() RULE_CATEGORY(Network) -RULE_INT(Network, ResendDelayBaseMS, 100) -RULE_REAL(Network, ResendDelayFactor, 1.5) -RULE_INT(Network, ResendDelayMinMS, 300) -RULE_INT(Network, ResendDelayMaxMS, 5000) -RULE_REAL(Network, ClientDataRate, 0.0) // KB / sec, 0.0 disabled -RULE_BOOL(Network, CompressZoneStream, true) +RULE_INT(Network, ResendDelayBaseMS, 100, "") +RULE_REAL(Network, ResendDelayFactor, 1.5, "") +RULE_INT(Network, ResendDelayMinMS, 300, "") +RULE_INT(Network, ResendDelayMaxMS, 5000, "") +RULE_REAL(Network, ClientDataRate, 0.0, "KB / sec, 0.0 disabled") +RULE_BOOL(Network, CompressZoneStream, true, "") RULE_CATEGORY_END() RULE_CATEGORY(QueryServ) -RULE_BOOL(QueryServ, PlayerLogChat, false) // Logs Player Chat -RULE_BOOL(QueryServ, PlayerLogTrades, false) // Logs Player Trades -RULE_BOOL(QueryServ, PlayerDropItems, false) // Logs Player dropping items -RULE_BOOL(QueryServ, PlayerLogHandins, false) // Logs Player Handins -RULE_BOOL(QueryServ, PlayerLogNPCKills, false) // Logs Player NPC Kills -RULE_BOOL(QueryServ, PlayerLogDeletes, false) // Logs Player Deletes -RULE_BOOL(QueryServ, PlayerLogMoves, false) // Logs Player Moves -RULE_BOOL(QueryServ, PlayerLogMerchantTransactions, false) // Logs Merchant Transactions -RULE_BOOL(QueryServ, PlayerLogPCCoordinates, false) // Logs Player Coordinates with certain events -RULE_BOOL(QueryServ, PlayerLogDropItem, false) // Logs Player Drop Item -RULE_BOOL(QueryServ, PlayerLogZone, false) // Logs Player Zone Events -RULE_BOOL(QueryServ, PlayerLogDeaths, false) // Logs Player Deaths -RULE_BOOL(QueryServ, PlayerLogConnectDisconnect, false) // Logs Player Connect Disconnect State -RULE_BOOL(QueryServ, PlayerLogLevels, false) // Logs Player Leveling/Deleveling -RULE_BOOL(QueryServ, PlayerLogAARate, false) // Logs Player AA Experience Rates -RULE_BOOL(QueryServ, PlayerLogQGlobalUpdate, false) // Logs Player QGlobal Updates -RULE_BOOL(QueryServ, PlayerLogTaskUpdates, false) // Logs Player Task Updates -RULE_BOOL(QueryServ, PlayerLogKeyringAddition, false) // Log PLayer Keyring additions -RULE_BOOL(QueryServ, PlayerLogAAPurchases, false) // Log Player AA Purchases -RULE_BOOL(QueryServ, PlayerLogTradeSkillEvents, false) // Log Player Tradeskill Transactions -RULE_BOOL(QueryServ, PlayerLogIssuedCommandes, false) // Log Player Issued Commands -RULE_BOOL(QueryServ, PlayerLogMoneyTransactions, false) // Log Player Money Transaction/Splits -RULE_BOOL(QueryServ, PlayerLogAlternateCurrencyTransactions, false) // Log Ploayer Alternate Currency Transactions +RULE_BOOL(QueryServ, PlayerLogChat, false, "Logs Player Chat") +RULE_BOOL(QueryServ, PlayerLogTrades, false, "Logs Player Trades") +RULE_BOOL(QueryServ, PlayerDropItems, false, "Logs Player dropping items") +RULE_BOOL(QueryServ, PlayerLogHandins, false, "Logs Player Handins") +RULE_BOOL(QueryServ, PlayerLogNPCKills, false, "Logs Player NPC Kills") +RULE_BOOL(QueryServ, PlayerLogDeletes, false, "Logs Player Deletes") +RULE_BOOL(QueryServ, PlayerLogMoves, false, "Logs Player Moves") +RULE_BOOL(QueryServ, PlayerLogMerchantTransactions, false, "Logs Merchant Transactions") +RULE_BOOL(QueryServ, PlayerLogPCCoordinates, false, "Logs Player Coordinates with certain events") +RULE_BOOL(QueryServ, PlayerLogDropItem, false, "Logs Player Drop Item") +RULE_BOOL(QueryServ, PlayerLogZone, false, "Logs Player Zone Events") +RULE_BOOL(QueryServ, PlayerLogDeaths, false, "Logs Player Deaths") +RULE_BOOL(QueryServ, PlayerLogConnectDisconnect, false, "Logs Player Connect Disconnect State") +RULE_BOOL(QueryServ, PlayerLogLevels, false, "Logs Player Leveling/Deleveling") +RULE_BOOL(QueryServ, PlayerLogAARate, false, "Logs Player AA Experience Rates") +RULE_BOOL(QueryServ, PlayerLogQGlobalUpdate, false, "Logs Player QGlobal Updates") +RULE_BOOL(QueryServ, PlayerLogTaskUpdates, false, "Logs Player Task Updates") +RULE_BOOL(QueryServ, PlayerLogKeyringAddition, false, "Log PLayer Keyring additions") +RULE_BOOL(QueryServ, PlayerLogAAPurchases, false, "Log Player AA Purchases") +RULE_BOOL(QueryServ, PlayerLogTradeSkillEvents, false, "Log Player Tradeskill Transactions") +RULE_BOOL(QueryServ, PlayerLogIssuedCommandes, false, "Log Player Issued Commands") +RULE_BOOL(QueryServ, PlayerLogMoneyTransactions, false, "Log Player Money Transaction/Splits") +RULE_BOOL(QueryServ, PlayerLogAlternateCurrencyTransactions, false, "Log Player Alternate Currency Transactions") RULE_CATEGORY_END() RULE_CATEGORY(Inventory) -RULE_BOOL(Inventory, EnforceAugmentRestriction, true) // Forces augment slot restrictions -RULE_BOOL(Inventory, EnforceAugmentUsability, true) // Forces augmented item usability -RULE_BOOL(Inventory, EnforceAugmentWear, true) // Forces augment wear slot validation -RULE_BOOL(Inventory, DeleteTransformationMold, true) //False if you want mold to last forever -RULE_BOOL(Inventory, AllowAnyWeaponTransformation, false) //Weapons can use any weapon transformation -RULE_BOOL(Inventory, TransformSummonedBags, false) //Transforms summoned bags into disenchanted ones instead of deleting +RULE_BOOL(Inventory, EnforceAugmentRestriction, true, "Forces augment slot restrictions") +RULE_BOOL(Inventory, EnforceAugmentUsability, true, "Forces augmented item usability") +RULE_BOOL(Inventory, EnforceAugmentWear, true, "Forces augment wear slot validation") +RULE_BOOL(Inventory, DeleteTransformationMold, true, "False if you want mold to last forever") +RULE_BOOL(Inventory, AllowAnyWeaponTransformation, false, "Weapons can use any weapon transformation") +RULE_BOOL(Inventory, TransformSummonedBags, false, "Transforms summoned bags into disenchanted ones instead of deleting") RULE_CATEGORY_END() RULE_CATEGORY(Client) -RULE_BOOL(Client, UseLiveFactionMessage, false) // Allows players to see faction adjustments like Live -RULE_BOOL(Client, UseLiveBlockedMessage, false) // Allows players to see faction adjustments like Live +RULE_BOOL(Client, UseLiveFactionMessage, false, "Allows players to see faction adjustments like Live") +RULE_BOOL(Client, UseLiveBlockedMessage, false, "Allows players to see faction adjustments like Live") RULE_CATEGORY_END() RULE_CATEGORY(Bugs) -RULE_BOOL(Bugs, ReportingSystemActive, true) // Activates bug reporting -RULE_BOOL(Bugs, UseOldReportingMethod, true) // Forces the use of the old bug reporting system -RULE_BOOL(Bugs, DumpTargetEntity, false) // Dumps the target entity, if one is provided +RULE_BOOL(Bugs, ReportingSystemActive, true, "Activates bug reporting") +RULE_BOOL(Bugs, UseOldReportingMethod, true, "Forces the use of the old bug reporting system") +RULE_BOOL(Bugs, DumpTargetEntity, false, "Dumps the target entity, if one is provided") RULE_CATEGORY_END() RULE_CATEGORY(Faction) -RULE_INT(Faction, AllyFactionMinimum, 1100) -RULE_INT(Faction, WarmlyFactionMinimum, 750) -RULE_INT(Faction, KindlyFactionMinimum, 500) -RULE_INT(Faction, AmiablyFactionMinimum, 100) -RULE_INT(Faction, IndifferentlyFactionMinimum, 0) -RULE_INT(Faction, ApprehensivelyFactionMinimum, -100) -RULE_INT(Faction, DubiouslyFactionMinimum, -500) -RULE_INT(Faction, ThreateninglyFactionMinimum, -750) +RULE_INT(Faction, AllyFactionMinimum, 1100, "") +RULE_INT(Faction, WarmlyFactionMinimum, 750, "") +RULE_INT(Faction, KindlyFactionMinimum, 500, "") +RULE_INT(Faction, AmiablyFactionMinimum, 100, "") +RULE_INT(Faction, IndifferentlyFactionMinimum, 0, "") +RULE_INT(Faction, ApprehensivelyFactionMinimum, -100, "") +RULE_INT(Faction, DubiouslyFactionMinimum, -500, "") +RULE_INT(Faction, ThreateninglyFactionMinimum, -750, "") +RULE_CATEGORY_END() + +RULE_CATEGORY(Logging) +RULE_BOOL(Logging, PrintFileFunctionAndLine, false, "Ex: [World Server] [net.cpp::main:309] Loading variables...") RULE_CATEGORY_END() #undef RULE_CATEGORY diff --git a/common/say_link.cpp b/common/say_link.cpp index 4b8ddc009..f4937fe88 100644 --- a/common/say_link.cpp +++ b/common/say_link.cpp @@ -100,8 +100,8 @@ const std::string &EQEmu::SayLinkEngine::GenerateLink() if ((m_Link.length() == 0) || (m_Link.length() > (EQEmu::constants::SAY_LINK_MAXIMUM_SIZE))) { m_Error = true; m_Link = ""; - Log(Logs::General, Logs::Error, "SayLinkEngine::GenerateLink() failed to generate a useable say link"); - Log(Logs::General, Logs::Error, ">> LinkType: %i, Lengths: {link: %u(%u), body: %u(%u), text: %u(%u)}", + LogError("SayLinkEngine::GenerateLink() failed to generate a useable say link"); + LogError(">> LinkType: {}, Lengths: {link: {}({}), body: {}({}), text: {}({})}", m_LinkType, m_Link.length(), EQEmu::constants::SAY_LINK_MAXIMUM_SIZE, @@ -110,8 +110,8 @@ const std::string &EQEmu::SayLinkEngine::GenerateLink() m_LinkText.length(), EQEmu::constants::SAY_LINK_TEXT_SIZE ); - Log(Logs::General, Logs::Error, ">> LinkBody: %s", m_LinkBody.c_str()); - Log(Logs::General, Logs::Error, ">> LinkText: %s", m_LinkText.c_str()); + LogError(">> LinkBody: {}", m_LinkBody.c_str()); + LogError(">> LinkText: {}", m_LinkText.c_str()); } return m_Link; @@ -316,7 +316,7 @@ std::string EQEmu::SayLinkEngine::GenerateQuestSaylink(std::string saylink_text, results = database.QueryDatabase(insert_query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error in saylink phrase queries %s", results.ErrorMessage().c_str()); + LogError("Error in saylink phrase queries {}", results.ErrorMessage().c_str()); } else { saylink_id = results.LastInsertedID(); diff --git a/common/servertalk.h b/common/servertalk.h index 3ad7c0cc2..3aef636ca 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -143,14 +143,15 @@ #define ServerOP_ClientVersionSummary 0x0215 #define ServerOP_LSInfo 0x1000 #define ServerOP_LSStatus 0x1001 -#define ServerOP_LSClientAuth 0x1002 +#define ServerOP_LSClientAuthLeg 0x1002 #define ServerOP_LSFatalError 0x1003 #define ServerOP_SystemwideMessage 0x1005 #define ServerOP_ListWorlds 0x1006 #define ServerOP_PeerConnect 0x1007 #define ServerOP_NewLSInfo 0x1008 #define ServerOP_LSRemoteAddr 0x1009 -#define ServerOP_LSAccountUpdate 0x100A +#define ServerOP_LSAccountUpdate 0x100A +#define ServerOP_LSClientAuth 0x100B #define ServerOP_EncapPacket 0x2007 // Packet within a packet #define ServerOP_WorldListUpdate 0x2008 @@ -171,8 +172,10 @@ #define ServerOP_LSPlayerJoinWorld 0x3007 #define ServerOP_LSPlayerZoneChange 0x3008 -#define ServerOP_UsertoWorldReq 0xAB00 -#define ServerOP_UsertoWorldResp 0xAB01 +#define ServerOP_UsertoWorldReqLeg 0xAB00 +#define ServerOP_UsertoWorldRespLeg 0xAB01 +#define ServerOP_UsertoWorldReq 0xAB02 +#define ServerOP_UsertoWorldResp 0xAB03 #define ServerOP_LauncherConnectInfo 0x3000 #define ServerOP_LauncherZoneRequest 0x3001 @@ -469,15 +472,15 @@ struct ServerLSInfo_Struct { }; struct ServerNewLSInfo_Struct { - char name[201]; // name the worldserver wants - char shortname[50]; // shortname the worldserver wants - char remote_address[125]; // DNS address of the server - char local_address[125]; // DNS address of the server - char account[31]; // account name for the worldserver - char password[31]; // password for the name - char protocolversion[25]; // Major protocol version number - char serverversion[64]; // minor server software version number - uint8 servertype; // 0=world, 1=chat, 2=login, 3=MeshLogin + char server_long_name[201]; // name the worldserver wants + char server_short_name[50]; // shortname the worldserver wants + char remote_ip_address[125]; // DNS address of the server + char local_ip_address[125]; // DNS address of the server + char account_name[31]; // account name for the worldserver + char account_password[31]; // password for the name + char protocol_version[25]; // Major protocol version number + char server_version[64]; // minor server software version number + uint8 server_process_type; // 0=world, 1=chat, 2=login, 3=MeshLogin }; struct ServerLSAccountUpdate_Struct { // for updating info on login server @@ -486,7 +489,7 @@ struct ServerLSAccountUpdate_Struct { // for updating info on login server uint32 useraccountid; // player account ID char useraccount[31]; // player account name char userpassword[51]; // player account password - char useremail[101]; // player account email address + char user_email[101]; // player account email address }; struct ServerLSStatus_Struct { @@ -533,18 +536,35 @@ struct ServerLSPlayerZoneChange_Struct { }; struct ClientAuth_Struct { - uint32 lsaccount_id; // ID# in login server's db - char name[30]; // username in login server's db + uint32 loginserver_account_id; // ID# in login server's db + char loginserver_name[64]; + char account_name[30]; // username in login server's db char key[30]; // the Key the client will present uint8 lsadmin; // login server admin level - int16 worldadmin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it + int16 is_world_admin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it uint32 ip; - uint8 local; // 1 if the client is from the local network + uint8 is_client_from_local_network; // 1 if the client is from the local network template void serialize(Archive &ar) { - ar(lsaccount_id, name, key, lsadmin, worldadmin, ip, local); + ar(loginserver_account_id, loginserver_name, account_name, key, lsadmin, is_world_admin, ip, is_client_from_local_network); + } +}; + +struct ClientAuthLegacy_Struct { + uint32 loginserver_account_id; // ID# in login server's db + char loginserver_account_name[30]; // username in login server's db + char key[30]; // the Key the client will present + uint8 loginserver_admin_level; // login server admin level + int16 is_world_admin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it + uint32 ip; + uint8 is_client_from_local_network; // 1 if the client is from the local network + + template + void serialize(Archive &ar) + { + ar(loginserver_account_id, loginserver_account_name, key, loginserver_admin_level, is_world_admin, ip, is_client_from_local_network); } }; @@ -672,7 +692,7 @@ struct ServerSyncWorldList_Struct { bool placeholder; }; -struct UsertoWorldRequest_Struct { +struct UsertoWorldRequestLegacy_Struct { uint32 lsaccountid; uint32 worldid; uint32 FromID; @@ -680,12 +700,30 @@ struct UsertoWorldRequest_Struct { char IPAddr[64]; }; -struct UsertoWorldResponse_Struct { +struct UsertoWorldRequest_Struct { uint32 lsaccountid; uint32 worldid; - int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed uint32 FromID; uint32 ToID; + char IPAddr[64]; + char login[64]; +}; + +struct UsertoWorldResponseLegacy_Struct { + uint32 lsaccountid; + uint32 worldid; + int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed + uint32 FromID; + uint32 ToID; +}; + +struct UsertoWorldResponse_Struct { + uint32 lsaccountid; + uint32 worldid; + int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed + uint32 FromID; + uint32 ToID; + char login[64]; }; // generic struct to be used for alot of simple zone->world questions diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 97585670c..b6cd81029 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -18,6 +18,7 @@ #include #include +#include #if defined(_MSC_VER) && _MSC_VER >= 1800 #include @@ -123,7 +124,7 @@ void SharedDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) MailKeyString, CharID); auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "SharedDatabase::SetMailKey(%i, %s) : %s", CharID, MailKeyString, results.ErrorMessage().c_str()); + LogError("SharedDatabase::SetMailKey({}, {}) : {}", CharID, MailKeyString, results.ErrorMessage().c_str()); } @@ -466,8 +467,6 @@ bool SharedDatabase::GetSharedBank(uint32 id, EQEmu::InventoryProfile *inv, bool id); auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Database::GetSharedBank(uint32 account_id): %s", - results.ErrorMessage().c_str()); return false; } @@ -487,8 +486,7 @@ bool SharedDatabase::GetSharedBank(uint32 id, EQEmu::InventoryProfile *inv, bool const EQEmu::ItemData *item = GetItem(item_id); if (!item) { - Log(Logs::General, Logs::Error, - "Warning: %s %i has an invalid item_id %i in inventory slot %i", + LogError("Warning: [{}] [{}] has an invalid item_id [{}] in inventory slot [{}]", ((is_charid == true) ? "charid" : "acctid"), id, item_id, slot_id); continue; } @@ -536,8 +534,7 @@ bool SharedDatabase::GetSharedBank(uint32 id, EQEmu::InventoryProfile *inv, bool if (put_slot_id != INVALID_INDEX) continue; - Log(Logs::General, Logs::Error, - "Warning: Invalid slot_id for item in shared bank inventory: %s=%i, item_id=%i, slot_id=%i", + LogError("Warning: Invalid slot_id for item in shared bank inventory: [{}]=[{}], item_id=[{}], slot_id=[{}]", ((is_charid == true) ? "charid" : "acctid"), id, item_id, slot_id); if (is_charid) @@ -561,7 +558,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQEmu::InventoryProfile *inv) char_id); auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "If you got an error related to the 'instnodrop' field, run the " + LogError("If you got an error related to the 'instnodrop' field, run the " "following SQL Queries:\nalter table inventory add instnodrop " "tinyint(1) unsigned default 0 not null;\n"); return false; @@ -625,8 +622,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQEmu::InventoryProfile *inv) const EQEmu::ItemData *item = GetItem(item_id); if (!item) { - Log(Logs::General, Logs::Error, - "Warning: charid %i has an invalid item_id %i in inventory slot %i", char_id, item_id, + LogError("Warning: charid [{}] has an invalid item_id [{}] in inventory slot [{}]", char_id, item_id, slot_id); continue; } @@ -699,8 +695,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQEmu::InventoryProfile *inv) put_slot_id = inv->PushCursor(*inst); } else if (slot_id >= 3111 && slot_id <= 3179) { // Admins: please report any occurrences of this error - Log(Logs::General, Logs::Error, "Warning: Defunct location for item in inventory: " - "charid=%i, item_id=%i, slot_id=%i .. pushing to cursor...", + LogError("Warning: Defunct location for item in inventory: charid={}, item_id={}, slot_id={} .. pushing to cursor...", char_id, item_id, slot_id); put_slot_id = inv->PushCursor(*inst); } else { @@ -711,8 +706,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQEmu::InventoryProfile *inv) // Save ptr to item in inventory if (put_slot_id == INVALID_INDEX) { - Log(Logs::General, Logs::Error, - "Warning: Invalid slot_id for item in inventory: charid=%i, item_id=%i, slot_id=%i", + LogError("Warning: Invalid slot_id for item in inventory: charid=[{}], item_id=[{}], slot_id=[{}]", char_id, item_id, slot_id); } } @@ -720,8 +714,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQEmu::InventoryProfile *inv) if (cv_conflict) { char char_name[64] = ""; GetCharName(char_id, char_name); - Log(Logs::General, Logs::Error, - "ClientVersion/Expansion conflict during inventory load at zone entry for '%s' (charid: %u, inver: %s, gmi: %s)", + LogError("ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])", char_name, char_id, EQEmu::versions::MobVersionName(inv->InventoryVersion()), @@ -746,7 +739,7 @@ bool SharedDatabase::GetInventory(uint32 account_id, char *name, EQEmu::Inventor name, account_id); auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "If you got an error related to the 'instnodrop' field, run the " + LogError("If you got an error related to the 'instnodrop' field, run the " "following SQL Queries:\nalter table inventory add instnodrop " "tinyint(1) unsigned default 0 not null;\n"); return false; @@ -834,8 +827,7 @@ bool SharedDatabase::GetInventory(uint32 account_id, char *name, EQEmu::Inventor // Save ptr to item in inventory if (put_slot_id == INVALID_INDEX) - Log(Logs::General, Logs::Error, "Warning: Invalid slot_id for item in inventory: name=%s, " - "acctid=%i, item_id=%i, slot_id=%i", + LogError("Warning: Invalid slot_id for item in inventory: name={}, acctid={}, item_id={}, slot_id={}", name, account_id, item_id, slot_id); } @@ -911,7 +903,7 @@ bool SharedDatabase::LoadItems(const std::string &prefix) { items_hash = std::unique_ptr>(new EQEmu::FixedMemoryHashSet(reinterpret_cast(items_mmf->Get()), items_mmf->Size())); mutex.Unlock(); } catch(std::exception& ex) { - Log(Logs::General, Logs::Error, "Error Loading Items: %s", ex.what()); + LogError("Error Loading Items: {}", ex.what()); return false; } @@ -1172,7 +1164,7 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_ try { hash.insert(item.ID, item); } catch (std::exception &ex) { - Log(Logs::General, Logs::Error, "Database::LoadItems: %s", ex.what()); + LogError("Database::LoadItems: {}", ex.what()); break; } } @@ -1229,7 +1221,7 @@ std::string SharedDatabase::GetBook(const char *txtfile, int16 *language) } if (results.RowCount() == 0) { - Log(Logs::General, Logs::Error, "No book to send, (%s)", txtfile); + LogError("No book to send, ({})", txtfile); txtout.assign(" ",1); return txtout; } @@ -1334,7 +1326,7 @@ bool SharedDatabase::LoadNPCFactionLists(const std::string &prefix) { faction_hash = std::unique_ptr>(new EQEmu::FixedMemoryHashSet(reinterpret_cast(faction_mmf->Get()), faction_mmf->Size())); mutex.Unlock(); } catch(std::exception& ex) { - Log(Logs::General, Logs::Error, "Error Loading npc factions: %s", ex.what()); + LogError("Error Loading npc factions: {}", ex.what()); return false; } @@ -1352,8 +1344,8 @@ EQEmu::ItemInstance* SharedDatabase::CreateItem(uint32 item_id, int16 charges, u inst = CreateBaseItem(item, charges); if (inst == nullptr) { - Log(Logs::General, Logs::Error, "Error: valid item data returned a null reference for EQEmu::ItemInstance creation in SharedDatabase::CreateItem()"); - Log(Logs::General, Logs::Error, "Item Data = ID: %u, Name: %s, Charges: %i", item->ID, item->Name, charges); + LogError("Error: valid item data returned a null reference for EQEmu::ItemInstance creation in SharedDatabase::CreateItem()"); + LogError("Item Data = ID: {}, Name: {}, Charges: {}", item->ID, item->Name, charges); return nullptr; } @@ -1378,8 +1370,8 @@ EQEmu::ItemInstance* SharedDatabase::CreateItem(const EQEmu::ItemData* item, int inst = CreateBaseItem(item, charges); if (inst == nullptr) { - Log(Logs::General, Logs::Error, "Error: valid item data returned a null reference for EQEmu::ItemInstance creation in SharedDatabase::CreateItem()"); - Log(Logs::General, Logs::Error, "Item Data = ID: %u, Name: %s, Charges: %i", item->ID, item->Name, charges); + LogError("Error: valid item data returned a null reference for EQEmu::ItemInstance creation in SharedDatabase::CreateItem()"); + LogError("Item Data = ID: {}, Name: {}, Charges: {}", item->ID, item->Name, charges); return nullptr; } @@ -1409,8 +1401,8 @@ EQEmu::ItemInstance* SharedDatabase::CreateBaseItem(const EQEmu::ItemData* item, inst = new EQEmu::ItemInstance(item, charges); if (inst == nullptr) { - Log(Logs::General, Logs::Error, "Error: valid item data returned a null reference for EQEmu::ItemInstance creation in SharedDatabase::CreateBaseItem()"); - Log(Logs::General, Logs::Error, "Item Data = ID: %u, Name: %s, Charges: %i", item->ID, item->Name, charges); + LogError("Error: valid item data returned a null reference for EQEmu::ItemInstance creation in SharedDatabase::CreateBaseItem()"); + LogError("Item Data = ID: {}, Name: {}, Charges: {}", item->ID, item->Name, charges); return nullptr; } @@ -1469,6 +1461,55 @@ bool SharedDatabase::GetCommandSettings(std::map> &injected) +{ + if (injected.size()) { + + std::string query = fmt::format( + "REPLACE INTO `command_settings`(`command`, `access`) VALUES {}", + implode( + ",", + std::pair('(', ')'), + join_pair(",", std::pair('\'', '\''), injected) + ) + ); + + if (!QueryDatabase(query).Success()) { + return false; + } + + LogInfo( + "[{0}] New Command(s) Added", + injected.size() + ); + } + + return true; +} + +bool SharedDatabase::UpdateOrphanedCommandSettings(const std::vector &orphaned) +{ + if (orphaned.size()) { + + std::string query = fmt::format( + "DELETE FROM `command_settings` WHERE `command` IN ({})", + implode(",", std::pair('\'', '\''), orphaned) + ); + + if (!QueryDatabase(query).Success()) { + return false; + } + + LogInfo( + "{} Orphaned Command{} Deleted", + orphaned.size(), + (orphaned.size() == 1 ? "" : "s") + ); + } + + return true; +} + bool SharedDatabase::LoadSkillCaps(const std::string &prefix) { skill_caps_mmf.reset(nullptr); @@ -1485,7 +1526,7 @@ bool SharedDatabase::LoadSkillCaps(const std::string &prefix) { skill_caps_mmf = std::unique_ptr(new EQEmu::MemoryMappedFile(file_name)); mutex.Unlock(); } catch(std::exception &ex) { - Log(Logs::General, Logs::Error, "Error loading skill caps: %s", ex.what()); + LogError("Error loading skill caps: {}", ex.what()); return false; } @@ -1501,7 +1542,7 @@ void SharedDatabase::LoadSkillCaps(void *data) { const std::string query = "SELECT skillID, class, level, cap FROM skill_caps ORDER BY skillID, class, level"; auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error loading skill caps from database: %s", results.ErrorMessage().c_str()); + LogError("Error loading skill caps from database: {}", results.ErrorMessage().c_str()); return; } @@ -1645,7 +1686,7 @@ bool SharedDatabase::LoadSpells(const std::string &prefix, int32 *records, const mutex.Unlock(); } catch(std::exception& ex) { - Log(Logs::General, Logs::Error, "Error Loading Spells: %s", ex.what()); + LogError("Error Loading Spells: {}", ex.what()); return false; } return true; @@ -1662,7 +1703,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { } if(results.ColumnCount() <= SPELL_LOAD_FIELD_COUNT) { - Log(Logs::Detail, Logs::Spells, "Fatal error loading spells: Spell field count < SPELL_LOAD_FIELD_COUNT(%u)", SPELL_LOAD_FIELD_COUNT); + LogSpells("Fatal error loading spells: Spell field count < SPELL_LOAD_FIELD_COUNT([{}])", SPELL_LOAD_FIELD_COUNT); return; } @@ -1672,7 +1713,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { for (auto row = results.begin(); row != results.end(); ++row) { tempid = atoi(row[0]); if(tempid >= max_spells) { - Log(Logs::Detail, Logs::Spells, "Non fatal error: spell.id >= max_spells, ignoring."); + LogSpells("Non fatal error: spell.id >= max_spells, ignoring"); continue; } @@ -1849,7 +1890,7 @@ bool SharedDatabase::LoadBaseData(const std::string &prefix) { base_data_mmf = std::unique_ptr(new EQEmu::MemoryMappedFile(file_name)); mutex.Unlock(); } catch(std::exception& ex) { - Log(Logs::General, Logs::Error, "Error Loading Base Data: %s", ex.what()); + LogError("Error Loading Base Data: {}", ex.what()); return false; } @@ -1873,22 +1914,22 @@ void SharedDatabase::LoadBaseData(void *data, int max_level) { cl = atoi(row[1]); if(lvl <= 0) { - Log(Logs::General, Logs::Error, "Non fatal error: base_data.level <= 0, ignoring."); + LogError("Non fatal error: base_data.level <= 0, ignoring."); continue; } if(lvl >= max_level) { - Log(Logs::General, Logs::Error, "Non fatal error: base_data.level >= max_level, ignoring."); + LogError("Non fatal error: base_data.level >= max_level, ignoring."); continue; } if(cl <= 0) { - Log(Logs::General, Logs::Error, "Non fatal error: base_data.cl <= 0, ignoring."); + LogError("Non fatal error: base_data.cl <= 0, ignoring."); continue; } if(cl > 16) { - Log(Logs::General, Logs::Error, "Non fatal error: base_data.class > 16, ignoring."); + LogError("Non fatal error: base_data.class > 16, ignoring."); continue; } @@ -2096,7 +2137,7 @@ bool SharedDatabase::LoadLoot(const std::string &prefix) { loot_drop_mmf->Size())); mutex.Unlock(); } catch(std::exception &ex) { - Log(Logs::General, Logs::Error, "Error loading loot: %s", ex.what()); + LogError("Error loading loot: {}", ex.what()); return false; } @@ -2112,7 +2153,7 @@ const LootTable_Struct* SharedDatabase::GetLootTable(uint32 loottable_id) { return &loot_table_hash->at(loottable_id); } } catch(std::exception &ex) { - Log(Logs::General, Logs::Error, "Could not get loot table: %s", ex.what()); + LogError("Could not get loot table: {}", ex.what()); } return nullptr; } @@ -2126,7 +2167,7 @@ const LootDrop_Struct* SharedDatabase::GetLootDrop(uint32 lootdrop_id) { return &loot_drop_hash->at(lootdrop_id); } } catch(std::exception &ex) { - Log(Logs::General, Logs::Error, "Could not get loot drop: %s", ex.what()); + LogError("Could not get loot drop: {}", ex.what()); } return nullptr; } diff --git a/common/shareddb.h b/common/shareddb.h index f6b1879df..8deb65126 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -71,6 +71,8 @@ class SharedDatabase : public Database void LoadCharacterInspectMessage(uint32 character_id, InspectMessage_Struct* message); void SaveCharacterInspectMessage(uint32 character_id, const InspectMessage_Struct* message); bool GetCommandSettings(std::map>> &command_settings); + bool UpdateInjectedCommandSettings(const std::vector> &injected); + bool UpdateOrphanedCommandSettings(const std::vector &orphaned); uint32 GetTotalTimeEntitledOnAccount(uint32 AccountID); void SetMailKey(int CharID, int IPAddress, int MailKey); std::string GetMailKey(int CharID, bool key_only = false); diff --git a/common/spdat.cpp b/common/spdat.cpp index 8006e377d..3fd6d19e4 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -850,7 +850,7 @@ DmgShieldType GetDamageShieldType(uint16 spell_id, int32 DSType) // If we have a DamageShieldType for this spell from the damageshieldtypes table, return that, // else, make a guess, based on the resist type. Default return value is DS_THORNS if (IsValidSpell(spell_id)) { - Log(Logs::Detail, Logs::Spells, "DamageShieldType for spell %i (%s) is %X\n", spell_id, + LogSpells("DamageShieldType for spell [{}] ([{}]) is [{}]", spell_id, spells[spell_id].name, spells[spell_id].DamageShieldType); if (spells[spell_id].DamageShieldType) diff --git a/common/string_util.cpp b/common/string_util.cpp index ec53663cc..b48bee1b3 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -262,6 +262,18 @@ void find_replace(std::string &string_subject, const std::string &search_string, } +void ParseAccountString(const std::string &s, std::string &account, std::string &loginserver) +{ + auto split = SplitString(s, ':'); + if (split.size() == 2) { + loginserver = split[0]; + account = split[1]; + } + else if(split.size() == 1) { + account = split[0]; + } +} + //Const char based // normal strncpy doesnt put a null term on copied strings, this one does @@ -514,4 +526,4 @@ bool isAlphaNumeric(const char *text) } return true; -} \ No newline at end of file +} diff --git a/common/string_util.h b/common/string_util.h index 23a8af05b..0727b98e2 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -20,6 +20,12 @@ #include #include #include +#include + +#ifndef _WIN32 +// this doesn't appear to affect linux-based systems..need feedback for _WIN64 +#include +#endif #include "types.h" @@ -31,6 +37,93 @@ std::vector split(std::string str_to_split, char delimiter); const std::string StringFormat(const char* format, ...); const std::string vStringFormat(const char* format, va_list args); std::string implode(std::string glue, std::vector src); + +template +std::string implode(const std::string &glue, const std::pair &encapsulation, const std::vector &src) +{ + if (src.empty()) { + return {}; + } + + std::ostringstream oss; + + for (const T &src_iter : src) { + oss << encapsulation.first << src_iter << encapsulation.second << glue; + } + + std::string output(oss.str()); + output.resize(output.size() - glue.size()); + + return output; +} + +// _WIN32 builds require that #include be included in whatever code file the invocation is made from (no header files) +template +std::vector join_pair(const std::string &glue, const std::pair &encapsulation, const std::vector> &src) +{ + if (src.empty()) { + return {}; + } + + std::vector output; + + for (const std::pair &src_iter : src) { + output.push_back( + + fmt::format( + "{}{}{}{}{}{}{}", + encapsulation.first, + src_iter.first, + encapsulation.second, + glue, + encapsulation.first, + src_iter.second, + encapsulation.second + ) + ); + } + + return output; +} + +// _WIN32 builds require that #include be included in whatever code file the invocation is made from (no header files) +template +std::vector join_tuple(const std::string &glue, const std::pair &encapsulation, const std::vector> &src) +{ + if (src.empty()) { + return {}; + } + + std::vector output; + + for (const std::tuple &src_iter : src) { + + output.push_back( + + fmt::format( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + encapsulation.first, + std::get<0>(src_iter), + encapsulation.second, + glue, + encapsulation.first, + std::get<1>(src_iter), + encapsulation.second, + glue, + encapsulation.first, + std::get<2>(src_iter), + encapsulation.second, + glue, + encapsulation.first, + std::get<3>(src_iter), + encapsulation.second + ) + ); + } + + return output; +} + std::vector SplitString(const std::string &s, char delim); std::string EscapeString(const char *src, size_t sz); std::string EscapeString(const std::string &s); @@ -39,6 +132,7 @@ void ToLowerString(std::string &s); void ToUpperString(std::string &s); std::string JoinString(const std::vector& ar, const std::string &delim); void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string); +void ParseAccountString(const std::string &s, std::string &account, std::string &loginserver); //const char based diff --git a/common/struct_strategy.cpp b/common/struct_strategy.cpp index 58a7f9e76..52df20410 100644 --- a/common/struct_strategy.cpp +++ b/common/struct_strategy.cpp @@ -41,13 +41,13 @@ void StructStrategy::ErrorEncoder(EQApplicationPacket **in_p, std::shared_ptrGetOpcode())); + LogNetcode("[STRUCTS] Error encoding opcode [{}]: no encoder provided. Dropping", OpcodeManager::EmuToName(p->GetOpcode())); delete p; } void StructStrategy::ErrorDecoder(EQApplicationPacket *p) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Error decoding opcode %s: no decoder provided. Invalidating.", OpcodeManager::EmuToName(p->GetOpcode())); + LogNetcode("[STRUCTS] Error decoding opcode [{}]: no decoder provided. Invalidating", OpcodeManager::EmuToName(p->GetOpcode())); p->SetOpcode(OP_Unknown); } diff --git a/common/tcp_server.cpp b/common/tcp_server.cpp index d036e7626..0009475fd 100644 --- a/common/tcp_server.cpp +++ b/common/tcp_server.cpp @@ -68,7 +68,7 @@ ThreadReturnType BaseTCPServer::TCPServerLoop(void* tmp) { BaseTCPServer* tcps = (BaseTCPServer*) tmp; #ifndef WIN32 - Log(Logs::Detail, Logs::None, "Starting TCPServerLoop with thread ID %d", pthread_self()); + LogDebug( "Starting TCPServerLoop with thread ID [{}]", pthread_self()); #endif tcps->MLoopRunning.lock(); @@ -79,7 +79,7 @@ ThreadReturnType BaseTCPServer::TCPServerLoop(void* tmp) { tcps->MLoopRunning.unlock(); #ifndef WIN32 - Log(Logs::Detail, Logs::None, "Ending TCPServerLoop with thread ID %d", pthread_self()); + LogDebug( "Ending TCPServerLoop with thread ID [{}]", pthread_self()); #endif THREAD_RETURN(nullptr); diff --git a/common/timeoutmgr.cpp b/common/timeoutmgr.cpp index 71e639f31..f8ee62869 100644 --- a/common/timeoutmgr.cpp +++ b/common/timeoutmgr.cpp @@ -42,9 +42,6 @@ void TimeoutManager::CheckTimeouts() { for(; cur != end; ++cur) { Timeoutable *it = *cur; if(it->next_check.Check()) { -#ifdef TIMEOUT_DEBUG - Log(Logs::General, Logs::None,, "Checking timeout on 0x%x\n", it); -#endif it->CheckTimeout(); } } @@ -57,15 +54,9 @@ void TimeoutManager::AddMember(Timeoutable *who) { DeleteMember(who); //just in case... prolly not needed. members.push_back(who); -#ifdef TIMEOUT_DEBUG - Log(Logs::General, Logs::None,, "Adding timeoutable 0x%x\n", who); -#endif } void TimeoutManager::DeleteMember(Timeoutable *who) { -#ifdef TIMEOUT_DEBUG - Log(Logs::General, Logs::None,, "Removing timeoutable 0x%x\n", who); -#endif std::vector::iterator cur,end; cur = members.begin(); end = members.end(); diff --git a/common/tinyxml/tinystr.cpp b/common/tinyxml/tinystr.cpp deleted file mode 100644 index 57aa56792..000000000 --- a/common/tinyxml/tinystr.cpp +++ /dev/null @@ -1,318 +0,0 @@ -/* -www.sourceforge.net/projects/tinyxml -Original file by Yves Berquin. - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#include "tinyxml.h" - -#ifndef TIXML_USE_STL - - -#include -#include -#include - -#include "tinystr.h" - -// TiXmlString constructor, based on a C string -TiXmlString::TiXmlString (const char* instring) -{ - size_t newlen; - char * newstring; - - if (!instring) - { - allocated = 0; - cstring = nullptr; - current_length = 0; - return; - } - newlen = strlen (instring) + 1; - newstring = new char [newlen]; - memcpy (newstring, instring, newlen); - // strcpy (newstring, instring); - allocated = newlen; - cstring = newstring; - current_length = newlen - 1; -} - -// TiXmlString copy constructor -TiXmlString::TiXmlString (const TiXmlString& copy) -{ - size_t newlen; - char * newstring; - - // Prevent copy to self! - if ( © == this ) - return; - - if (! copy . allocated) - { - allocated = 0; - cstring = nullptr; - current_length = 0; - return; - } - newlen = copy . length () + 1; - newstring = new char [newlen]; - // strcpy (newstring, copy . cstring); - memcpy (newstring, copy . cstring, newlen); - allocated = newlen; - cstring = newstring; - current_length = newlen - 1; -} - -// TiXmlString = operator. Safe when assign own content -void TiXmlString ::operator = (const char * content) -{ - size_t newlen; - char * newstring; - - if (! content) - { - empty_it (); - return; - } - newlen = strlen (content) + 1; - newstring = new char [newlen]; - // strcpy (newstring, content); - memcpy (newstring, content, newlen); - empty_it (); - allocated = newlen; - cstring = newstring; - current_length = newlen - 1; -} - -// = operator. Safe when assign own content -void TiXmlString ::operator = (const TiXmlString & copy) -{ - size_t newlen; - char * newstring; - - if (! copy . length ()) - { - empty_it (); - return; - } - newlen = copy . length () + 1; - newstring = new char [newlen]; - // strcpy (newstring, copy . c_str ()); - memcpy (newstring, copy . c_str (), newlen); - empty_it (); - allocated = newlen; - cstring = newstring; - current_length = newlen - 1; -} - - -// append a const char * to an existing TiXmlString -void TiXmlString::append( const char* str, size_t len ) -{ - char * new_string; - size_t new_alloc, new_size, size_suffix; - - // don't use strlen - it can overrun the len passed in! - const char* p = str; - size_suffix = 0; - - while ( *p && size_suffix < (unsigned)len ) - { - ++p; - ++size_suffix; - } - if ( !size_suffix) - return; - - new_size = length () + size_suffix + 1; - // check if we need to expand - if (new_size > allocated) - { - // compute new size - new_alloc = assign_new_size (new_size); - - // allocate new buffer - new_string = new char [new_alloc]; - new_string [0] = 0; - - // copy the previous allocated buffer into this one - if (allocated && cstring) - // strcpy (new_string, cstring); - memcpy (new_string, cstring, length ()); - - // append the suffix. It does exist, otherwize we wouldn't be expanding - // strncat (new_string, str, len); - memcpy (new_string + length (), - str, - size_suffix); - - // return previsously allocated buffer if any - if (allocated && cstring) - delete [] cstring; - - // update member variables - cstring = new_string; - allocated = new_alloc; - } - else - { - // we know we can safely append the new string - // strncat (cstring, str, len); - memcpy (cstring + length (), - str, - size_suffix); - } - current_length = new_size - 1; - cstring [current_length] = 0; -} - - -// append a const char * to an existing TiXmlString -void TiXmlString::append( const char * suffix ) -{ - char * new_string; - size_t new_alloc, new_size; - - new_size = length () + strlen (suffix) + 1; - // check if we need to expand - if (new_size > allocated) - { - // compute new size - new_alloc = assign_new_size (new_size); - - // allocate new buffer - new_string = new char [new_alloc]; - new_string [0] = 0; - - // copy the previous allocated buffer into this one - if (allocated && cstring) - memcpy (new_string, cstring, 1 + length ()); - // strcpy (new_string, cstring); - - // append the suffix. It does exist, otherwize we wouldn't be expanding - // strcat (new_string, suffix); - memcpy (new_string + length (), - suffix, - strlen (suffix) + 1); - - // return previsously allocated buffer if any - if (allocated && cstring) - delete [] cstring; - - // update member variables - cstring = new_string; - allocated = new_alloc; - } - else - { - // we know we can safely append the new string - // strcat (cstring, suffix); - memcpy (cstring + length (), - suffix, - strlen (suffix) + 1); - } - current_length = new_size - 1; -} - -// Check for TiXmlString equuivalence -//bool TiXmlString::operator == (const TiXmlString & compare) const -//{ -// return (! strcmp (c_str (), compare . c_str ())); -//} - -//unsigned TiXmlString::length () const -//{ -// if (allocated) -// // return strlen (cstring); -// return current_length; -// return 0; -//} - - -unsigned TiXmlString::find (char tofind, unsigned offset) const -{ - char * lookup; - - if (offset >= length ()) - return (unsigned) notfound; - for (lookup = cstring + offset; * lookup; lookup++) - if (* lookup == tofind) - return (unsigned)(lookup - cstring); - return (unsigned) notfound; -} - - -bool TiXmlString::operator == (const TiXmlString & compare) const -{ - if ( allocated && compare.allocated ) - { - assert( cstring ); - assert( compare.cstring ); - return ( strcmp( cstring, compare.cstring ) == 0 ); - } - else if ( length() == 0 && compare.length() == 0 ) - { - return true; - } - return false; -} - - -bool TiXmlString::operator == (const char* compare) const -{ - if ( allocated && compare && *compare ) - { - assert( cstring ); - return ( strcmp( cstring, compare ) == 0 ); - } - else if ( length() == 0 && (!compare || !*compare ) ) // this is a little dubious, but try to duplicate behavior in other operator== - { - return true; - } - return false; -} - - -bool TiXmlString::operator < (const TiXmlString & compare) const -{ - if ( allocated && compare.allocated ) - { - assert( cstring ); - assert( compare.cstring ); - return ( strcmp( cstring, compare.cstring ) > 0 ); - } - return false; -} - - -bool TiXmlString::operator > (const TiXmlString & compare) const -{ - if ( allocated && compare.allocated ) - { - assert( cstring ); - assert( compare.cstring ); - return ( strcmp( cstring, compare.cstring ) < 0 ); - } - return false; -} - - -#endif // TIXML_USE_STL diff --git a/common/tinyxml/tinystr.h b/common/tinyxml/tinystr.h deleted file mode 100644 index 6f1edca9b..000000000 --- a/common/tinyxml/tinystr.h +++ /dev/null @@ -1,250 +0,0 @@ -/* -www.sourceforge.net/projects/tinyxml -Original file by Yves Berquin. - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#include "tinyxml.h" - - -#ifndef TIXML_USE_STL - -#ifndef TIXML_STRING_INCLUDED -#define TIXML_STRING_INCLUDED - -#ifdef _MSC_VER -#pragma warning( disable : 4530 ) -#pragma warning( disable : 4786 ) -#endif - -#include - -/* - TiXmlString is an emulation of the std::string template. - Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. - Only the member functions relevant to the TinyXML project have been implemented. - The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase - a string and there's no more room, we allocate a buffer twice as big as we need. -*/ -class TiXmlString -{ - public : - // TiXmlString constructor, based on a string, mark explicit to force - // us to find unnecessary casting. - explicit TiXmlString (const char * instring); - - // TiXmlString empty constructor - TiXmlString () - { - allocated = 0; - cstring = nullptr; - current_length = 0; - } - - // TiXmlString copy constructor - explicit TiXmlString (const TiXmlString& copy); - - // TiXmlString destructor - ~ TiXmlString () - { - empty_it (); - } - - // Convert a TiXmlString into a classical char * - const char * c_str () const - { - if (allocated) - return cstring; - return ""; - } - - // Return the length of a TiXmlString - size_t length () const - { - return ( allocated ) ? current_length : 0; - } - - // TiXmlString = operator - void operator = (const char * content); - - // = operator - void operator = (const TiXmlString & copy); - - // += operator. Maps to append - TiXmlString& operator += (const char * suffix) - { - append (suffix); - return *this; - } - - // += operator. Maps to append - TiXmlString& operator += (char single) - { - append (single); - return *this; - } - - // += operator. Maps to append - TiXmlString& operator += (TiXmlString & suffix) - { - append (suffix); - return *this; - } - bool operator == (const TiXmlString & compare) const; - bool operator == (const char* compare) const; - bool operator < (const TiXmlString & compare) const; - bool operator > (const TiXmlString & compare) const; - - // Checks if a TiXmlString is empty - bool empty () const - { - return length () ? false : true; - } - - // single char extraction - const char& at (unsigned index) const - { - assert( index < length ()); - return cstring [index]; - } - - // find a char in a string. Return TiXmlString::notfound if not found - unsigned find (char lookup) const - { - return find (lookup, 0); - } - - // find a char in a string from an offset. Return TiXmlString::notfound if not found - unsigned find (char tofind, unsigned offset) const; - - /* Function to reserve a big amount of data when we know we'll need it. Be aware that this - function clears the content of the TiXmlString if any exists. - */ - void reserve (unsigned size) - { - empty_it (); - if (size) - { - allocated = size; - cstring = new char [size]; - cstring [0] = 0; - current_length = 0; - } - } - - // [] operator - char& operator [] (unsigned index) const - { - assert( index < length ()); - return cstring [index]; - } - - // Error value for find primitive - enum { notfound = 0xffffffff, - npos = notfound }; - - void append (const char *str, size_t len ); - - protected : - - // The base string - char * cstring; - // Number of chars allocated - size_t allocated; - // Current string size - size_t current_length; - - // New size computation. It is simplistic right now : it returns twice the amount - // we need - size_t assign_new_size (size_t minimum_to_allocate) - { - return minimum_to_allocate * 2; - } - - // Internal function that clears the content of a TiXmlString - void empty_it () - { - if (cstring) - delete [] cstring; - cstring = nullptr; - allocated = 0; - current_length = 0; - } - - void append (const char *suffix ); - - // append function for another TiXmlString - void append (const TiXmlString & suffix) - { - append (suffix . c_str ()); - } - - // append for a single char. - void append (char single) - { - if ( cstring && current_length < (allocated-1) ) - { - cstring[ current_length ] = single; - ++current_length; - cstring[ current_length ] = 0; - } - else - { - char smallstr [2]; - smallstr [0] = single; - smallstr [1] = 0; - append (smallstr); - } - } - -} ; - -/* - TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. - Only the operators that we need for TinyXML have been developped. -*/ -class TiXmlOutStream : public TiXmlString -{ -public : - TiXmlOutStream () : TiXmlString () {} - - // TiXmlOutStream << operator. Maps to TiXmlString::append - TiXmlOutStream & operator << (const char * in) - { - append (in); - return (* this); - } - - // TiXmlOutStream << operator. Maps to TiXmlString::append - TiXmlOutStream & operator << (const TiXmlString & in) - { - append (in . c_str ()); - return (* this); - } -} ; - -#ifdef _MSC_VER -#pragma warning( default : 4530 ) -#pragma warning( default : 4786 ) -#endif - -#endif // TIXML_STRING_INCLUDED -#endif // TIXML_USE_STL diff --git a/common/tinyxml/tinyxml.cpp b/common/tinyxml/tinyxml.cpp deleted file mode 100644 index c652cc1c9..000000000 --- a/common/tinyxml/tinyxml.cpp +++ /dev/null @@ -1,1592 +0,0 @@ -/* -www.sourceforge.net/projects/tinyxml -Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#include -#include "tinyxml.h" - -#ifdef TIXML_USE_STL -#include -#endif - - -bool TiXmlBase::condenseWhiteSpace = true; - -void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_OSTREAM* stream ) -{ - TIXML_STRING buffer; - PutString( str, &buffer ); - (*stream) << buffer; -} - -void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_STRING* outString ) -{ - int i=0; - - while( i<(int)str.length() ) - { - unsigned char c = (unsigned char) str[i]; - - if ( c == '&' - && i < ( (int)str.length() - 2 ) - && str[i+1] == '#' - && str[i+2] == 'x' ) - { - // Hexadecimal character reference. - // Pass through unchanged. - // © -- copyright symbol, for example. - // - // The -1 is a bug fix from Rob Laveaux. It keeps - // an overflow from happening if there is no ';'. - // There are actually 2 ways to exit this loop - - // while fails (error case) and break (semicolon found). - // However, there is no mechanism (currently) for - // this function to return an error. - while ( i<(int)str.length()-1 ) - { - outString->append( str.c_str() + i, 1 ); - ++i; - if ( str[i] == ';' ) - break; - } - } - else if ( c == '&' ) - { - outString->append( entity[0].str, entity[0].strLength ); - ++i; - } - else if ( c == '<' ) - { - outString->append( entity[1].str, entity[1].strLength ); - ++i; - } - else if ( c == '>' ) - { - outString->append( entity[2].str, entity[2].strLength ); - ++i; - } - else if ( c == '\"' ) - { - outString->append( entity[3].str, entity[3].strLength ); - ++i; - } - else if ( c == '\'' ) - { - outString->append( entity[4].str, entity[4].strLength ); - ++i; - } - else if ( c < 32 ) - { - // Easy pass at non-alpha/numeric/symbol - // Below 32 is symbolic. - char buf[ 32 ]; - sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); - //*ME: warning C4267: convert 'size_t' to 'int' - //*ME: Int-Cast to make compiler happy ... - outString->append( buf, (int)strlen( buf ) ); - ++i; - } - else - { - //char realc = (char) c; - //outString->append( &realc, 1 ); - *outString += (char) c; // somewhat more efficient function call. - ++i; - } - } -} - - -// <-- Strange class for a bug fix. Search for STL_STRING_BUG -TiXmlBase::StringToBuffer::StringToBuffer( const TIXML_STRING& str ) -{ - buffer = new char[ str.length()+1 ]; - if ( buffer ) - { - strcpy( buffer, str.c_str() ); - } -} - - -TiXmlBase::StringToBuffer::~StringToBuffer() -{ - delete [] buffer; -} -// End strange bug fix. --> - - -TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() -{ - parent = 0; - type = _type; - firstChild = 0; - lastChild = 0; - prev = 0; - next = 0; -} - - -TiXmlNode::~TiXmlNode() -{ - TiXmlNode* node = firstChild; - TiXmlNode* temp = 0; - - while ( node ) - { - temp = node; - node = node->next; - delete temp; - } -} - - -void TiXmlNode::CopyTo( TiXmlNode* target ) const -{ - target->SetValue (value.c_str() ); - target->userData = userData; -} - - -void TiXmlNode::Clear() -{ - TiXmlNode* node = firstChild; - TiXmlNode* temp = 0; - - while ( node ) - { - temp = node; - node = node->next; - delete temp; - } - - firstChild = 0; - lastChild = 0; -} - - -TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) -{ - node->parent = this; - - node->prev = lastChild; - node->next = 0; - - if ( lastChild ) - lastChild->next = node; - else - firstChild = node; // it was an empty list. - - lastChild = node; - return node; -} - - -TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) -{ - TiXmlNode* node = addThis.Clone(); - if ( !node ) - return 0; - - return LinkEndChild( node ); -} - - -TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) -{ - if ( !beforeThis || beforeThis->parent != this ) - return 0; - - TiXmlNode* node = addThis.Clone(); - if ( !node ) - return 0; - node->parent = this; - - node->next = beforeThis; - node->prev = beforeThis->prev; - if ( beforeThis->prev ) - { - beforeThis->prev->next = node; - } - else - { - assert( firstChild == beforeThis ); - firstChild = node; - } - beforeThis->prev = node; - return node; -} - - -TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) -{ - if ( !afterThis || afterThis->parent != this ) - return 0; - - TiXmlNode* node = addThis.Clone(); - if ( !node ) - return 0; - node->parent = this; - - node->prev = afterThis; - node->next = afterThis->next; - if ( afterThis->next ) - { - afterThis->next->prev = node; - } - else - { - assert( lastChild == afterThis ); - lastChild = node; - } - afterThis->next = node; - return node; -} - - -TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) -{ - if ( replaceThis->parent != this ) - return 0; - - TiXmlNode* node = withThis.Clone(); - if ( !node ) - return 0; - - node->next = replaceThis->next; - node->prev = replaceThis->prev; - - if ( replaceThis->next ) - replaceThis->next->prev = node; - else - lastChild = node; - - if ( replaceThis->prev ) - replaceThis->prev->next = node; - else - firstChild = node; - - delete replaceThis; - node->parent = this; - return node; -} - - -bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) -{ - if ( removeThis->parent != this ) - { - assert( 0 ); - return false; - } - - if ( removeThis->next ) - removeThis->next->prev = removeThis->prev; - else - lastChild = removeThis->prev; - - if ( removeThis->prev ) - removeThis->prev->next = removeThis->next; - else - firstChild = removeThis->next; - - delete removeThis; - return true; -} - -const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const -{ - const TiXmlNode* node; - for ( node = firstChild; node; node = node->next ) - { - if ( node->SValue() == _value ) - return node; - } - return 0; -} - - -TiXmlNode* TiXmlNode::FirstChild( const char * _value ) -{ - TiXmlNode* node; - for ( node = firstChild; node; node = node->next ) - { - if ( node->SValue() == _value ) - return node; - } - return 0; -} - - -const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const -{ - const TiXmlNode* node; - for ( node = lastChild; node; node = node->prev ) - { - if ( node->SValue() == _value ) - return node; - } - return 0; -} - -TiXmlNode* TiXmlNode::LastChild( const char * _value ) -{ - TiXmlNode* node; - for ( node = lastChild; node; node = node->prev ) - { - if ( node->SValue() == _value ) - return node; - } - return 0; -} - -const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const -{ - if ( !previous ) - { - return FirstChild(); - } - else - { - assert( previous->parent == this ); - return previous->NextSibling(); - } -} - -TiXmlNode* TiXmlNode::IterateChildren( TiXmlNode* previous ) -{ - if ( !previous ) - { - return FirstChild(); - } - else - { - assert( previous->parent == this ); - return previous->NextSibling(); - } -} - -const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const -{ - if ( !previous ) - { - return FirstChild( val ); - } - else - { - assert( previous->parent == this ); - return previous->NextSibling( val ); - } -} - -TiXmlNode* TiXmlNode::IterateChildren( const char * val, TiXmlNode* previous ) -{ - if ( !previous ) - { - return FirstChild( val ); - } - else - { - assert( previous->parent == this ); - return previous->NextSibling( val ); - } -} - -const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const -{ - const TiXmlNode* node; - for ( node = next; node; node = node->next ) - { - if ( node->SValue() == _value ) - return node; - } - return 0; -} - -TiXmlNode* TiXmlNode::NextSibling( const char * _value ) -{ - TiXmlNode* node; - for ( node = next; node; node = node->next ) - { - if ( node->SValue() == _value ) - return node; - } - return 0; -} - -const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const -{ - const TiXmlNode* node; - for ( node = prev; node; node = node->prev ) - { - if ( node->SValue() == _value ) - return node; - } - return 0; -} - -TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) -{ - TiXmlNode* node; - for ( node = prev; node; node = node->prev ) - { - if ( node->SValue() == _value ) - return node; - } - return 0; -} - -void TiXmlElement::RemoveAttribute( const char * name ) -{ - TiXmlAttribute* node = attributeSet.Find( name ); - if ( node ) - { - attributeSet.Remove( node ); - delete node; - } -} - -const TiXmlElement* TiXmlNode::FirstChildElement() const -{ - const TiXmlNode* node; - - for ( node = FirstChild(); - node; - node = node->NextSibling() ) - { - if ( node->ToElement() ) - return node->ToElement(); - } - return 0; -} - -TiXmlElement* TiXmlNode::FirstChildElement() -{ - TiXmlNode* node; - - for ( node = FirstChild(); - node; - node = node->NextSibling() ) - { - if ( node->ToElement() ) - return node->ToElement(); - } - return 0; -} - -const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const -{ - const TiXmlNode* node; - - for ( node = FirstChild( _value ); - node; - node = node->NextSibling( _value ) ) - { - if ( node->ToElement() ) - return node->ToElement(); - } - return 0; -} - -TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) -{ - TiXmlNode* node; - - for ( node = FirstChild( _value ); - node; - node = node->NextSibling( _value ) ) - { - if ( node->ToElement() ) - return node->ToElement(); - } - return 0; -} - -const TiXmlElement* TiXmlNode::NextSiblingElement() const -{ - const TiXmlNode* node; - - for ( node = NextSibling(); - node; - node = node->NextSibling() ) - { - if ( node->ToElement() ) - return node->ToElement(); - } - return 0; -} - -TiXmlElement* TiXmlNode::NextSiblingElement() -{ - TiXmlNode* node; - - for ( node = NextSibling(); - node; - node = node->NextSibling() ) - { - if ( node->ToElement() ) - return node->ToElement(); - } - return 0; -} - -const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const -{ - const TiXmlNode* node; - - for ( node = NextSibling( _value ); - node; - node = node->NextSibling( _value ) ) - { - if ( node->ToElement() ) - return node->ToElement(); - } - return 0; -} - -TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) -{ - TiXmlNode* node; - - for ( node = NextSibling( _value ); - node; - node = node->NextSibling( _value ) ) - { - if ( node->ToElement() ) - return node->ToElement(); - } - return 0; -} - - -const TiXmlDocument* TiXmlNode::GetDocument() const -{ - const TiXmlNode* node; - - for( node = this; node; node = node->parent ) - { - if ( node->ToDocument() ) - return node->ToDocument(); - } - return 0; -} - -TiXmlDocument* TiXmlNode::GetDocument() -{ - TiXmlNode* node; - - for( node = this; node; node = node->parent ) - { - if ( node->ToDocument() ) - return node->ToDocument(); - } - return 0; -} - -TiXmlElement::TiXmlElement (const char * _value) - : TiXmlNode( TiXmlNode::ELEMENT ) -{ - firstChild = lastChild = 0; - value = _value; -} - - -#ifdef TIXML_USE_STL -TiXmlElement::TiXmlElement( const std::string& _value ) - : TiXmlNode( TiXmlNode::ELEMENT ) -{ - firstChild = lastChild = 0; - value = _value; -} -#endif - - -TiXmlElement::TiXmlElement( const TiXmlElement& copy) - : TiXmlNode( TiXmlNode::ELEMENT ) -{ - firstChild = lastChild = 0; - copy.CopyTo( this ); -} - - -void TiXmlElement::operator=( const TiXmlElement& base ) -{ - ClearThis(); - base.CopyTo( this ); -} - - -TiXmlElement::~TiXmlElement() -{ - ClearThis(); -} - - -void TiXmlElement::ClearThis() -{ - Clear(); - while( attributeSet.First() ) - { - TiXmlAttribute* node = attributeSet.First(); - attributeSet.Remove( node ); - delete node; - } -} - - -const char * TiXmlElement::Attribute( const char * name ) const -{ - const TiXmlAttribute* node = attributeSet.Find( name ); - - if ( node ) - return node->Value(); - - return 0; -} - - -const char * TiXmlElement::Attribute( const char * name, int* i ) const -{ - const char * s = Attribute( name ); - if ( i ) - { - if ( s ) - *i = atoi( s ); - else - *i = 0; - } - return s; -} - - -const char * TiXmlElement::Attribute( const char * name, double* d ) const -{ - const char * s = Attribute( name ); - if ( d ) - { - if ( s ) - *d = atof( s ); - else - *d = 0; - } - return s; -} - - -int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const -{ - const TiXmlAttribute* node = attributeSet.Find( name ); - if ( !node ) - return TIXML_NO_ATTRIBUTE; - - return node->QueryIntValue( ival ); -} - - -int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const -{ - const TiXmlAttribute* node = attributeSet.Find( name ); - if ( !node ) - return TIXML_NO_ATTRIBUTE; - - return node->QueryDoubleValue( dval ); -} - - -void TiXmlElement::SetAttribute( const char * name, int val ) -{ - char buf[64]; - sprintf( buf, "%d", val ); - SetAttribute( name, buf ); -} - - -void TiXmlElement::SetDoubleAttribute( const char * name, double val ) -{ - char buf[256]; - sprintf( buf, "%f", val ); - SetAttribute( name, buf ); -} - - -void TiXmlElement::SetAttribute( const char * name, const char * _value ) -{ - TiXmlAttribute* node = attributeSet.Find( name ); - if ( node ) - { - node->SetValue( _value ); - return; - } - - TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); - if ( attrib ) - { - attributeSet.Add( attrib ); - } - else - { - TiXmlDocument* document = GetDocument(); - if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); - } -} - -void TiXmlElement::Print( FILE* cfile, int depth ) const -{ - int i; - for ( i=0; iNext() ) - { - fprintf( cfile, " " ); - attrib->Print( cfile, depth ); - } - - // There are 3 different formatting approaches: - // 1) An element without children is printed as a node - // 2) An element with only a text child is printed as text - // 3) An element with children is printed on multiple lines. - TiXmlNode* node; - if ( !firstChild ) - { - fprintf( cfile, " />" ); - } - else if ( firstChild == lastChild && firstChild->ToText() ) - { - fprintf( cfile, ">" ); - firstChild->Print( cfile, depth + 1 ); - fprintf( cfile, "", value.c_str() ); - } - else - { - fprintf( cfile, ">" ); - - for ( node = firstChild; node; node=node->NextSibling() ) - { - if ( !node->ToText() ) - { - fprintf( cfile, "\n" ); - } - node->Print( cfile, depth+1 ); - } - fprintf( cfile, "\n" ); - for( i=0; i", value.c_str() ); - } -} - -void TiXmlElement::StreamOut( TIXML_OSTREAM * stream ) const -{ - (*stream) << "<" << value; - - const TiXmlAttribute* attrib; - for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) - { - (*stream) << " "; - attrib->StreamOut( stream ); - } - - // If this node has children, give it a closing tag. Else - // make it an empty tag. - TiXmlNode* node; - if ( firstChild ) - { - (*stream) << ">"; - - for ( node = firstChild; node; node=node->NextSibling() ) - { - node->StreamOut( stream ); - } - (*stream) << ""; - } - else - { - (*stream) << " />"; - } -} - - -void TiXmlElement::CopyTo( TiXmlElement* target ) const -{ - // superclass: - TiXmlNode::CopyTo( target ); - - // Element class: - // Clone the attributes, then clone the children. - const TiXmlAttribute* attribute = 0; - for( attribute = attributeSet.First(); - attribute; - attribute = attribute->Next() ) - { - target->SetAttribute( attribute->Name(), attribute->Value() ); - } - - TiXmlNode* node = 0; - for ( node = firstChild; node; node = node->NextSibling() ) - { - target->LinkEndChild( node->Clone() ); - } -} - - -TiXmlNode* TiXmlElement::Clone() const -{ - TiXmlElement* clone = new TiXmlElement( Value() ); - if ( !clone ) - return 0; - - CopyTo( clone ); - return clone; -} - - -TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) -{ - tabsize = 4; - ClearError(); -} - -TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) -{ - tabsize = 4; - value = documentName; - ClearError(); -} - - -#ifdef TIXML_USE_STL -TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) -{ - tabsize = 4; - value = documentName; - ClearError(); -} -#endif - - -TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) -{ - copy.CopyTo( this ); -} - - -void TiXmlDocument::operator=( const TiXmlDocument& copy ) -{ - Clear(); - copy.CopyTo( this ); -} - - -bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) -{ - // See STL_STRING_BUG below. - StringToBuffer buf( value ); - - if ( buf.buffer && LoadFile( buf.buffer, encoding ) ) - return true; - - return false; -} - - -bool TiXmlDocument::SaveFile() const -{ - // See STL_STRING_BUG below. - StringToBuffer buf( value ); - - if ( buf.buffer && SaveFile( buf.buffer ) ) - return true; - - return false; -} - -bool TiXmlDocument::LoadFile( const char* filename, TiXmlEncoding encoding ) -{ - // Delete the existing data: - Clear(); - location.Clear(); - - // There was a really terrifying little bug here. The code: - // value = filename - // in the STL case, cause the assignment method of the std::string to - // be called. What is strange, is that the std::string had the same - // address as it's c_str() method, and so bad things happen. Looks - // like a bug in the Microsoft STL implementation. - // See STL_STRING_BUG above. - // Fixed with the StringToBuffer class. - value = filename; - - FILE* file = fopen( value.c_str (), "r" ); - - if ( file ) - { - // Get the file size, so we can pre-allocate the string. HUGE speed impact. - long length = 0; - fseek( file, 0, SEEK_END ); - length = ftell( file ); - fseek( file, 0, SEEK_SET ); - - // Strange case, but good to handle up front. - if ( length == 0 ) - { - fclose( file ); - return false; - } - - // If we have a file, assume it is all one big XML file, and read it in. - // The document parser may decide the document ends sooner than the entire file, however. - TIXML_STRING data; - data.reserve( length ); - - const int BUF_SIZE = 2048; - char buf[BUF_SIZE]; - - while( fgets( buf, BUF_SIZE, file ) ) - { - data += buf; - } - fclose( file ); - - Parse( data.c_str(), 0, encoding ); - - if ( Error() ) - return false; - else - return true; - } - SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); - return false; -} - -bool TiXmlDocument::SaveFile( const char * filename ) const -{ - // The old c stuff lives on... - FILE* fp = fopen( filename, "w" ); - if ( fp ) - { - Print( fp, 0 ); - fclose( fp ); - return true; - } - return false; -} - - -void TiXmlDocument::CopyTo( TiXmlDocument* target ) const -{ - TiXmlNode::CopyTo( target ); - - target->error = error; - target->errorDesc = errorDesc.c_str (); - - TiXmlNode* node = 0; - for ( node = firstChild; node; node = node->NextSibling() ) - { - target->LinkEndChild( node->Clone() ); - } -} - - -TiXmlNode* TiXmlDocument::Clone() const -{ - TiXmlDocument* clone = new TiXmlDocument(); - if ( !clone ) - return 0; - - CopyTo( clone ); - return clone; -} - - -void TiXmlDocument::Print( FILE* cfile, int depth ) const -{ - const TiXmlNode* node; - for ( node=FirstChild(); node; node=node->NextSibling() ) - { - node->Print( cfile, depth ); - fprintf( cfile, "\n" ); - } -} - -void TiXmlDocument::StreamOut( TIXML_OSTREAM * out ) const -{ - const TiXmlNode* node; - for ( node=FirstChild(); node; node=node->NextSibling() ) - { - node->StreamOut( out ); - - // Special rule for streams: stop after the root element. - // The stream in code will only read one element, so don't - // write more than one. - if ( node->ToElement() ) - break; - } -} - - -const TiXmlAttribute* TiXmlAttribute::Next() const -{ - // We are using knowledge of the sentinel. The sentinel - // have a value or name. - if ( next->value.empty() && next->name.empty() ) - return 0; - return next; -} - -TiXmlAttribute* TiXmlAttribute::Next() -{ - // We are using knowledge of the sentinel. The sentinel - // have a value or name. - if ( next->value.empty() && next->name.empty() ) - return 0; - return next; -} - -const TiXmlAttribute* TiXmlAttribute::Previous() const -{ - // We are using knowledge of the sentinel. The sentinel - // have a value or name. - if ( prev->value.empty() && prev->name.empty() ) - return 0; - return prev; -} - -TiXmlAttribute* TiXmlAttribute::Previous() -{ - // We are using knowledge of the sentinel. The sentinel - // have a value or name. - if ( prev->value.empty() && prev->name.empty() ) - return 0; - return prev; -} - -void TiXmlAttribute::Print( FILE* cfile, int /*depth*/ ) const -{ - TIXML_STRING n, v; - - PutString( name, &n ); - PutString( value, &v ); - - if (value.find ('\"') == TIXML_STRING::npos) - fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); - else - fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); -} - - -void TiXmlAttribute::StreamOut( TIXML_OSTREAM * stream ) const -{ - if (value.find( '\"' ) != TIXML_STRING::npos) - { - PutString( name, stream ); - (*stream) << "=" << "'"; - PutString( value, stream ); - (*stream) << "'"; - } - else - { - PutString( name, stream ); - (*stream) << "=" << "\""; - PutString( value, stream ); - (*stream) << "\""; - } -} - -int TiXmlAttribute::QueryIntValue( int* ival ) const -{ - if ( sscanf( value.c_str(), "%d", ival ) == 1 ) - return TIXML_SUCCESS; - return TIXML_WRONG_TYPE; -} - -int TiXmlAttribute::QueryDoubleValue( double* dval ) const -{ - if ( sscanf( value.c_str(), "%lf", dval ) == 1 ) - return TIXML_SUCCESS; - return TIXML_WRONG_TYPE; -} - -void TiXmlAttribute::SetIntValue( int _value ) -{ - char buf [64]; - sprintf (buf, "%d", _value); - SetValue (buf); -} - -void TiXmlAttribute::SetDoubleValue( double _value ) -{ - char buf [256]; - sprintf (buf, "%lf", _value); - SetValue (buf); -} - -const int TiXmlAttribute::IntValue() const -{ - return atoi (value.c_str ()); -} - -const double TiXmlAttribute::DoubleValue() const -{ - return atof (value.c_str ()); -} - - -TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) -{ - copy.CopyTo( this ); -} - - -void TiXmlComment::operator=( const TiXmlComment& base ) -{ - Clear(); - base.CopyTo( this ); -} - - -void TiXmlComment::Print( FILE* cfile, int depth ) const -{ - for ( int i=0; i", value.c_str() ); -} - -void TiXmlComment::StreamOut( TIXML_OSTREAM * stream ) const -{ - (*stream) << ""; -} - - -void TiXmlComment::CopyTo( TiXmlComment* target ) const -{ - TiXmlNode::CopyTo( target ); -} - - -TiXmlNode* TiXmlComment::Clone() const -{ - TiXmlComment* clone = new TiXmlComment(); - - if ( !clone ) - return 0; - - CopyTo( clone ); - return clone; -} - - -void TiXmlText::Print( FILE* cfile, int /*depth*/ ) const -{ - TIXML_STRING buffer; - PutString( value, &buffer ); - fprintf( cfile, "%s", buffer.c_str() ); -} - - -void TiXmlText::StreamOut( TIXML_OSTREAM * stream ) const -{ - PutString( value, stream ); -} - - -void TiXmlText::CopyTo( TiXmlText* target ) const -{ - TiXmlNode::CopyTo( target ); -} - - -TiXmlNode* TiXmlText::Clone() const -{ - TiXmlText* clone = 0; - clone = new TiXmlText( "" ); - - if ( !clone ) - return 0; - - CopyTo( clone ); - return clone; -} - - -TiXmlDeclaration::TiXmlDeclaration( const char * _version, - const char * _encoding, - const char * _standalone ) - : TiXmlNode( TiXmlNode::DECLARATION ) -{ - version = _version; - encoding = _encoding; - standalone = _standalone; -} - - -#ifdef TIXML_USE_STL -TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, - const std::string& _encoding, - const std::string& _standalone ) - : TiXmlNode( TiXmlNode::DECLARATION ) -{ - version = _version; - encoding = _encoding; - standalone = _standalone; -} -#endif - - -TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) - : TiXmlNode( TiXmlNode::DECLARATION ) -{ - copy.CopyTo( this ); -} - - -void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) -{ - Clear(); - copy.CopyTo( this ); -} - - -void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/ ) const -{ - fprintf (cfile, ""); -} - -void TiXmlDeclaration::StreamOut( TIXML_OSTREAM * stream ) const -{ - (*stream) << ""; -} - - -void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const -{ - TiXmlNode::CopyTo( target ); - - target->version = version; - target->encoding = encoding; - target->standalone = standalone; -} - - -TiXmlNode* TiXmlDeclaration::Clone() const -{ - TiXmlDeclaration* clone = new TiXmlDeclaration(); - - if ( !clone ) - return 0; - - CopyTo( clone ); - return clone; -} - - -void TiXmlUnknown::Print( FILE* cfile, int depth ) const -{ - for ( int i=0; i", value.c_str() ); -} - - -void TiXmlUnknown::StreamOut( TIXML_OSTREAM * stream ) const -{ - (*stream) << "<" << value << ">"; // Don't use entities here! It is unknown. -} - - -void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const -{ - TiXmlNode::CopyTo( target ); -} - - -TiXmlNode* TiXmlUnknown::Clone() const -{ - TiXmlUnknown* clone = new TiXmlUnknown(); - - if ( !clone ) - return 0; - - CopyTo( clone ); - return clone; -} - - -TiXmlAttributeSet::TiXmlAttributeSet() -{ - sentinel.next = &sentinel; - sentinel.prev = &sentinel; -} - - -TiXmlAttributeSet::~TiXmlAttributeSet() -{ - assert( sentinel.next == &sentinel ); - assert( sentinel.prev == &sentinel ); -} - - -void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) -{ - assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. - - addMe->next = &sentinel; - addMe->prev = sentinel.prev; - - sentinel.prev->next = addMe; - sentinel.prev = addMe; -} - -void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) -{ - TiXmlAttribute* node; - - for( node = sentinel.next; node != &sentinel; node = node->next ) - { - if ( node == removeMe ) - { - node->prev->next = node->next; - node->next->prev = node->prev; - node->next = 0; - node->prev = 0; - return; - } - } - assert( 0 ); // we tried to remove a non-linked attribute. -} - -const TiXmlAttribute* TiXmlAttributeSet::Find( const char * name ) const -{ - const TiXmlAttribute* node; - - for( node = sentinel.next; node != &sentinel; node = node->next ) - { - if ( node->name == name ) - return node; - } - return 0; -} - -TiXmlAttribute* TiXmlAttributeSet::Find( const char * name ) -{ - TiXmlAttribute* node; - - for( node = sentinel.next; node != &sentinel; node = node->next ) - { - if ( node->name == name ) - return node; - } - return 0; -} - -#ifdef TIXML_USE_STL -TIXML_ISTREAM & operator >> (TIXML_ISTREAM & in, TiXmlNode & base) -{ - TIXML_STRING tag; - tag.reserve( 8 * 1000 ); - base.StreamIn( &in, &tag ); - - base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); - return in; -} -#endif - - -TIXML_OSTREAM & operator<< (TIXML_OSTREAM & out, const TiXmlNode & base) -{ - base.StreamOut (& out); - return out; -} - - -#ifdef TIXML_USE_STL -std::string & operator<< (std::string& out, const TiXmlNode& base ) -{ - std::ostringstream os_stream( std::ostringstream::out ); - base.StreamOut( &os_stream ); - - out.append( os_stream.str() ); - return out; -} -#endif - - -TiXmlHandle TiXmlHandle::FirstChild() const -{ - if ( node ) - { - TiXmlNode* child = node->FirstChild(); - if ( child ) - return TiXmlHandle( child ); - } - return TiXmlHandle( 0 ); -} - - -TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const -{ - if ( node ) - { - TiXmlNode* child = node->FirstChild( value ); - if ( child ) - return TiXmlHandle( child ); - } - return TiXmlHandle( 0 ); -} - - -TiXmlHandle TiXmlHandle::FirstChildElement() const -{ - if ( node ) - { - TiXmlElement* child = node->FirstChildElement(); - if ( child ) - return TiXmlHandle( child ); - } - return TiXmlHandle( 0 ); -} - - -TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const -{ - if ( node ) - { - TiXmlElement* child = node->FirstChildElement( value ); - if ( child ) - return TiXmlHandle( child ); - } - return TiXmlHandle( 0 ); -} - - -TiXmlHandle TiXmlHandle::Child( int count ) const -{ - if ( node ) - { - int i; - TiXmlNode* child = node->FirstChild(); - for ( i=0; - child && iNextSibling(), ++i ) - { - // nothing - } - if ( child ) - return TiXmlHandle( child ); - } - return TiXmlHandle( 0 ); -} - - -TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const -{ - if ( node ) - { - int i; - TiXmlNode* child = node->FirstChild( value ); - for ( i=0; - child && iNextSibling( value ), ++i ) - { - // nothing - } - if ( child ) - return TiXmlHandle( child ); - } - return TiXmlHandle( 0 ); -} - - -TiXmlHandle TiXmlHandle::ChildElement( int count ) const -{ - if ( node ) - { - int i; - TiXmlElement* child = node->FirstChildElement(); - for ( i=0; - child && iNextSiblingElement(), ++i ) - { - // nothing - } - if ( child ) - return TiXmlHandle( child ); - } - return TiXmlHandle( 0 ); -} - - -TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const -{ - if ( node ) - { - int i; - TiXmlElement* child = node->FirstChildElement( value ); - for ( i=0; - child && iNextSiblingElement( value ), ++i ) - { - // nothing - } - if ( child ) - return TiXmlHandle( child ); - } - return TiXmlHandle( 0 ); -} diff --git a/common/tinyxml/tinyxml.h b/common/tinyxml/tinyxml.h deleted file mode 100644 index cea3bebfd..000000000 --- a/common/tinyxml/tinyxml.h +++ /dev/null @@ -1,1426 +0,0 @@ -/* -www.sourceforge.net/projects/tinyxml -Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - - -#ifndef TINYXML_INCLUDED -#define TINYXML_INCLUDED - -#ifdef _MSC_VER -#pragma warning( disable : 4530 ) -#pragma warning( disable : 4786 ) -#endif - -#include -#include -#include -#include -#include - -// Help out windows: -#if defined( _DEBUG ) && !defined( DEBUG ) -#define DEBUG -#endif - -#if defined( DEBUG ) && defined( _MSC_VER ) -#include -#define TIXML_LOG OutputDebugString -#else -#define TIXML_LOG printf -#endif - -#ifdef TIXML_USE_STL - #include - #include - #define TIXML_STRING std::string - #define TIXML_ISTREAM std::istream - #define TIXML_OSTREAM std::ostream -#else - #include "tinystr.h" - #define TIXML_STRING TiXmlString - #define TIXML_OSTREAM TiXmlOutStream -#endif - -class TiXmlDocument; -class TiXmlElement; -class TiXmlComment; -class TiXmlUnknown; -class TiXmlAttribute; -class TiXmlText; -class TiXmlDeclaration; -class TiXmlParsingData; - -const int TIXML_MAJOR_VERSION = 2; -const int TIXML_MINOR_VERSION = 3; -const int TIXML_PATCH_VERSION = 4; - -/* Internal structure for tracking location of items - in the XML file. -*/ -struct TiXmlCursor -{ - TiXmlCursor() { Clear(); } - void Clear() { row = col = -1; } - - int row; // 0 based. - int col; // 0 based. -}; - - -// Only used by Attribute::Query functions -enum -{ - TIXML_SUCCESS, - TIXML_NO_ATTRIBUTE, - TIXML_WRONG_TYPE -}; - - -// Used by the parsing routines. -enum TiXmlEncoding -{ - TIXML_ENCODING_UNKNOWN, - TIXML_ENCODING_UTF8, - TIXML_ENCODING_LEGACY -}; - -const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; - -/** TiXmlBase is a base class for every class in TinyXml. - It does little except to establish that TinyXml classes - can be printed and provide some utility functions. - - In XML, the document and elements can contain - other elements and other types of nodes. - - @verbatim - A Document can contain: Element (container or leaf) - Comment (leaf) - Unknown (leaf) - Declaration( leaf ) - - An Element can contain: Element (container or leaf) - Text (leaf) - Attributes (not on tree) - Comment (leaf) - Unknown (leaf) - - A Decleration contains: Attributes (not on tree) - @endverbatim -*/ -class TiXmlBase -{ - friend class TiXmlNode; - friend class TiXmlElement; - friend class TiXmlDocument; - -public: - TiXmlBase() : userData(0) {} - virtual ~TiXmlBase() {} - - /** All TinyXml classes can print themselves to a filestream. - This is a formatted print, and will insert tabs and newlines. - - (For an unformatted stream, use the << operator.) - */ - virtual void Print( FILE* cfile, int depth ) const = 0; - - /** The world does not agree on whether white space should be kept or - not. In order to make everyone happy, these global, static functions - are provided to set whether or not TinyXml will condense all white space - into a single space or not. The default is to condense. Note changing this - values is not thread safe. - */ - static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } - - /// Return the current white space setting. - static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } - - /** Return the position, in the original source file, of this node or attribute. - The row and column are 1-based. (That is the first row and first column is - 1,1). If the returns values are 0 or less, then the parser does not have - a row and column value. - - Generally, the row and column value will be set when the TiXmlDocument::Load(), - TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set - when the DOM was created from operator>>. - - The values reflect the initial load. Once the DOM is modified programmatically - (by adding or changing nodes and attributes) the new values will NOT update to - reflect changes in the document. - - There is a minor performance cost to computing the row and column. Computation - can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. - - @sa TiXmlDocument::SetTabSize() - */ - int Row() const { return location.row + 1; } - int Column() const { return location.col + 1; } ///< See Row() - - void SetUserData( void* user ) { userData = user; } - void* GetUserData() { return userData; } - - // Table that returs, for a given lead byte, the total number of bytes - // in the UTF-8 sequence. - static const int utf8ByteTable[256]; - - virtual const char* Parse( const char* p, - TiXmlParsingData* data, - TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; - - enum - { - TIXML_NO_ERROR = 0, - TIXML_ERROR, - TIXML_ERROR_OPENING_FILE, - TIXML_ERROR_OUT_OF_MEMORY, - TIXML_ERROR_PARSING_ELEMENT, - TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, - TIXML_ERROR_READING_ELEMENT_VALUE, - TIXML_ERROR_READING_ATTRIBUTES, - TIXML_ERROR_PARSING_EMPTY, - TIXML_ERROR_READING_END_TAG, - TIXML_ERROR_PARSING_UNKNOWN, - TIXML_ERROR_PARSING_COMMENT, - TIXML_ERROR_PARSING_DECLARATION, - TIXML_ERROR_DOCUMENT_EMPTY, - TIXML_ERROR_EMBEDDED_NULL, - - TIXML_ERROR_STRING_COUNT - }; - -protected: - - // See STL_STRING_BUG - // Utility class to overcome a bug. - class StringToBuffer - { - public: - StringToBuffer( const TIXML_STRING& str ); - ~StringToBuffer(); - char* buffer; - }; - - static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); - inline static bool IsWhiteSpace( char c ) - { - return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); - } - - virtual void StreamOut (TIXML_OSTREAM *) const = 0; - - #ifdef TIXML_USE_STL - static bool StreamWhiteSpace( TIXML_ISTREAM * in, TIXML_STRING * tag ); - static bool StreamTo( TIXML_ISTREAM * in, int character, TIXML_STRING * tag ); - #endif - - /* Reads an XML name into the string provided. Returns - a pointer just past the last character of the name, - or 0 if the function has an error. - */ - static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); - - /* Reads text. Returns a pointer past the given end tag. - Wickedly complex options, but it keeps the (sensitive) code in one place. - */ - static const char* ReadText( const char* in, // where to start - TIXML_STRING* text, // the string read - bool ignoreWhiteSpace, // whether to keep the white space - const char* endTag, // what ends this text - bool ignoreCase, // whether to ignore case in the end tag - TiXmlEncoding encoding ); // the current encoding - - // If an entity has been found, transform it into a character. - static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); - - // Get a character, while interpreting entities. - // The length can be from 0 to 4 bytes. - inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) - { - assert( p ); - if ( encoding == TIXML_ENCODING_UTF8 ) - { - *length = utf8ByteTable[ *((const unsigned char*)p) ]; - assert( *length >= 0 && *length < 5 ); - } - else - { - *length = 1; - } - - if ( *length == 1 ) - { - if ( *p == '&' ) - return GetEntity( p, _value, length, encoding ); - *_value = *p; - return p+1; - } - else if ( *length ) - { - strncpy( _value, p, *length ); - return p + (*length); - } - else - { - // Not valid text. - return 0; - } - } - - // Puts a string to a stream, expanding entities as it goes. - // Note this should not contian the '<', '>', etc, or they will be transformed into entities! - static void PutString( const TIXML_STRING& str, TIXML_OSTREAM* out ); - - static void PutString( const TIXML_STRING& str, TIXML_STRING* out ); - - // Return true if the next characters in the stream are any of the endTag sequences. - // Ignore case only works for english, and should only be relied on when comparing - // to Engilish words: StringEqual( p, "version", true ) is fine. - static bool StringEqual( const char* p, - const char* endTag, - bool ignoreCase, - TiXmlEncoding encoding ); - - static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; - - TiXmlCursor location; - - /// Field containing a generic user pointer - void* userData; - - // None of these methods are reliable for any language except English. - // Good for approximation, not great for accuracy. - static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); - static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); - inline static int ToLower( int v, TiXmlEncoding encoding ) - { - if ( encoding == TIXML_ENCODING_UTF8 ) - { - if ( v < 128 ) return tolower( v ); - return v; - } - else - { - return tolower( v ); - } - } - static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); - -private: - TiXmlBase( const TiXmlBase& ); // not implemented. - void operator=( const TiXmlBase& base ); // not allowed. - - struct Entity - { - const char* str; - unsigned int strLength; - char chr; - }; - enum - { - NUM_ENTITY = 5, - MAX_ENTITY_LENGTH = 6 - - }; - static Entity entity[ NUM_ENTITY ]; - static bool condenseWhiteSpace; -}; - - -/** The parent class for everything in the Document Object Model. - (Except for attributes). - Nodes have siblings, a parent, and children. A node can be - in a document, or stand on its own. The type of a TiXmlNode - can be queried, and it can be cast to its more defined type. -*/ -class TiXmlNode : public TiXmlBase -{ - friend class TiXmlDocument; - friend class TiXmlElement; - -public: - #ifdef TIXML_USE_STL - - /** An input stream operator, for every class. Tolerant of newlines and - formatting, but doesn't expect them. - */ - friend std::istream& operator >> (std::istream& in, TiXmlNode& base); - - /** An output stream operator, for every class. Note that this outputs - without any newlines or formatting, as opposed to Print(), which - includes tabs and new lines. - - The operator<< and operator>> are not completely symmetric. Writing - a node to a stream is very well defined. You'll get a nice stream - of output, without any extra whitespace or newlines. - - But reading is not as well defined. (As it always is.) If you create - a TiXmlElement (for example) and read that from an input stream, - the text needs to define an element or junk will result. This is - true of all input streams, but it's worth keeping in mind. - - A TiXmlDocument will read nodes until it reads a root element, and - all the children of that root element. - */ - friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); - - /// Appends the XML node or attribute to a std::string. - friend std::string& operator<< (std::string& out, const TiXmlNode& base ); - - #else - // Used internally, not part of the public API. - friend TIXML_OSTREAM& operator<< (TIXML_OSTREAM& out, const TiXmlNode& base); - #endif - - /** The types of XML nodes supported by TinyXml. (All the - unsupported types are picked up by UNKNOWN.) - */ - enum NodeType - { - DOCUMENT, - ELEMENT, - COMMENT, - UNKNOWN, - TEXT, - DECLARATION, - TYPECOUNT - }; - - virtual ~TiXmlNode(); - - /** The meaning of 'value' changes for the specific type of - TiXmlNode. - @verbatim - Document: filename of the xml file - Element: name of the element - Comment: the comment text - Unknown: the tag contents - Text: the text string - @endverbatim - - The subclasses will wrap this function. - */ - const char * Value() const { return value.c_str (); } - - /** Changes the value of the node. Defined as: - @verbatim - Document: filename of the xml file - Element: name of the element - Comment: the comment text - Unknown: the tag contents - Text: the text string - @endverbatim - */ - void SetValue(const char * _value) { value = _value;} - - #ifdef TIXML_USE_STL - /// STL std::string form. - void SetValue( const std::string& _value ) - { - StringToBuffer buf( _value ); - SetValue( buf.buffer ? buf.buffer : "" ); - } - #endif - - /// Delete all the children of this node. Does not affect 'this'. - void Clear(); - - /// One step up the DOM. - TiXmlNode* Parent() { return parent; } - const TiXmlNode* Parent() const { return parent; } - - const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. - TiXmlNode* FirstChild() { return firstChild; } - const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. - TiXmlNode* FirstChild( const char * value ); ///< The first child of this node with the matching 'value'. Will be null if none found. - - const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. - TiXmlNode* LastChild() { return lastChild; } - const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. - TiXmlNode* LastChild( const char * value ); - - #ifdef TIXML_USE_STL - const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. - TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. - const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. - TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. - #endif - - /** An alternate way to walk the children of a node. - One way to iterate over nodes is: - @verbatim - for( child = parent->FirstChild(); child; child = child->NextSibling() ) - @endverbatim - - IterateChildren does the same thing with the syntax: - @verbatim - child = 0; - while( child = parent->IterateChildren( child ) ) - @endverbatim - - IterateChildren takes the previous child as input and finds - the next one. If the previous child is null, it returns the - first. IterateChildren will return null when done. - */ - const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; - TiXmlNode* IterateChildren( TiXmlNode* previous ); - - /// This flavor of IterateChildren searches for children with a particular 'value' - const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; - TiXmlNode* IterateChildren( const char * value, TiXmlNode* previous ); - - #ifdef TIXML_USE_STL - const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. - TiXmlNode* IterateChildren( const std::string& _value, TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. - #endif - - /** Add a new node related to this. Adds a child past the LastChild. - Returns a pointer to the new object or nullptr if an error occured. - */ - TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); - - - /** Add a new node related to this. Adds a child past the LastChild. - - NOTE: the node to be added is passed by pointer, and will be - henceforth owned (and deleted) by tinyXml. This method is efficient - and avoids an extra copy, but should be used with care as it - uses a different memory model than the other insert functions. - - @sa InsertEndChild - */ - TiXmlNode* LinkEndChild( TiXmlNode* addThis ); - - /** Add a new node related to this. Adds a child before the specified child. - Returns a pointer to the new object or nullptr if an error occured. - */ - TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); - - /** Add a new node related to this. Adds a child after the specified child. - Returns a pointer to the new object or nullptr if an error occured. - */ - TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); - - /** Replace a child of this node. - Returns a pointer to the new object or nullptr if an error occured. - */ - TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); - - /// Delete a child of this node. - bool RemoveChild( TiXmlNode* removeThis ); - - /// Navigate to a sibling node. - const TiXmlNode* PreviousSibling() const { return prev; } - TiXmlNode* PreviousSibling() { return prev; } - - /// Navigate to a sibling node. - const TiXmlNode* PreviousSibling( const char * ) const; - TiXmlNode* PreviousSibling( const char * ); - - #ifdef TIXML_USE_STL - const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. - TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. - const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. - TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. - #endif - - /// Navigate to a sibling node. - const TiXmlNode* NextSibling() const { return next; } - TiXmlNode* NextSibling() { return next; } - - /// Navigate to a sibling node with the given 'value'. - const TiXmlNode* NextSibling( const char * ) const; - TiXmlNode* NextSibling( const char * ); - - /** Convenience function to get through elements. - Calls NextSibling and ToElement. Will skip all non-Element - nodes. Returns 0 if there is not another element. - */ - const TiXmlElement* NextSiblingElement() const; - TiXmlElement* NextSiblingElement(); - - /** Convenience function to get through elements. - Calls NextSibling and ToElement. Will skip all non-Element - nodes. Returns 0 if there is not another element. - */ - const TiXmlElement* NextSiblingElement( const char * ) const; - TiXmlElement* NextSiblingElement( const char * ); - - #ifdef TIXML_USE_STL - const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. - TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. - #endif - - /// Convenience function to get through elements. - const TiXmlElement* FirstChildElement() const; - TiXmlElement* FirstChildElement(); - - /// Convenience function to get through elements. - const TiXmlElement* FirstChildElement( const char * value ) const; - TiXmlElement* FirstChildElement( const char * value ); - - #ifdef TIXML_USE_STL - const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. - TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. - #endif - - /** Query the type (as an enumerated value, above) of this node. - The possible types are: DOCUMENT, ELEMENT, COMMENT, - UNKNOWN, TEXT, and DECLARATION. - */ - virtual int Type() const { return type; } - - /** Return a pointer to the Document this node lives in. - Returns null if not in a document. - */ - const TiXmlDocument* GetDocument() const; - TiXmlDocument* GetDocument(); - - /// Returns true if this node has no children. - bool NoChildren() const { return !firstChild; } - - const TiXmlDocument* ToDocument() const { return ( this && type == DOCUMENT ) ? (const TiXmlDocument*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. - const TiXmlElement* ToElement() const { return ( this && type == ELEMENT ) ? (const TiXmlElement*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. - const TiXmlComment* ToComment() const { return ( this && type == COMMENT ) ? (const TiXmlComment*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. - const TiXmlUnknown* ToUnknown() const { return ( this && type == UNKNOWN ) ? (const TiXmlUnknown*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. - const TiXmlText* ToText() const { return ( this && type == TEXT ) ? (const TiXmlText*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. - const TiXmlDeclaration* ToDeclaration() const { return ( this && type == DECLARATION ) ? (const TiXmlDeclaration*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. - - TiXmlDocument* ToDocument() { return ( this && type == DOCUMENT ) ? (TiXmlDocument*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. - TiXmlElement* ToElement() { return ( this && type == ELEMENT ) ? (TiXmlElement*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. - TiXmlComment* ToComment() { return ( this && type == COMMENT ) ? (TiXmlComment*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. - TiXmlUnknown* ToUnknown() { return ( this && type == UNKNOWN ) ? (TiXmlUnknown*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. - TiXmlText* ToText() { return ( this && type == TEXT ) ? (TiXmlText*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. - TiXmlDeclaration* ToDeclaration() { return ( this && type == DECLARATION ) ? (TiXmlDeclaration*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. - - /** Create an exact duplicate of this node and return it. The memory must be deleted - by the caller. - */ - virtual TiXmlNode* Clone() const = 0; - -protected: - TiXmlNode( NodeType _type ); - - // Copy to the allocated object. Shared functionality between Clone, Copy constructor, - // and the assignment operator. - void CopyTo( TiXmlNode* target ) const; - - #ifdef TIXML_USE_STL - // The real work of the input operator. - virtual void StreamIn( TIXML_ISTREAM* in, TIXML_STRING* tag ) = 0; - #endif - - // Figure out what is at *p, and parse it. Returns null if it is not an xml node. - TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); - - // Internal Value function returning a TIXML_STRING - const TIXML_STRING& SValue() const { return value ; } - - TiXmlNode* parent; - NodeType type; - - TiXmlNode* firstChild; - TiXmlNode* lastChild; - - TIXML_STRING value; - - TiXmlNode* prev; - TiXmlNode* next; - -private: - TiXmlNode( const TiXmlNode& ); // not implemented. - void operator=( const TiXmlNode& base ); // not allowed. -}; - - -/** An attribute is a name-value pair. Elements have an arbitrary - number of attributes, each with a unique name. - - @note The attributes are not TiXmlNodes, since they are not - part of the tinyXML document object model. There are other - suggested ways to look at this problem. -*/ -class TiXmlAttribute : public TiXmlBase -{ - friend class TiXmlAttributeSet; - -public: - /// Construct an empty attribute. - TiXmlAttribute() : TiXmlBase() - { - document = 0; - prev = next = 0; - } - - #ifdef TIXML_USE_STL - /// std::string constructor. - TiXmlAttribute( const std::string& _name, const std::string& _value ) - { - name = _name; - value = _value; - document = 0; - prev = next = 0; - } - #endif - - /// Construct an attribute with a name and value. - TiXmlAttribute( const char * _name, const char * _value ) - { - name = _name; - value = _value; - document = 0; - prev = next = 0; - } - - const char* Name() const { return name.c_str (); } ///< Return the name of this attribute. - const char* Value() const { return value.c_str (); } ///< Return the value of this attribute. - const int IntValue() const; ///< Return the value of this attribute, converted to an integer. - const double DoubleValue() const; ///< Return the value of this attribute, converted to a double. - - /** QueryIntValue examines the value string. It is an alternative to the - IntValue() method with richer error checking. - If the value is an integer, it is stored in 'value' and - the call returns TIXML_SUCCESS. If it is not - an integer, it returns TIXML_WRONG_TYPE. - - A specialized but useful call. Note that for success it returns 0, - which is the opposite of almost all other TinyXml calls. - */ - int QueryIntValue( int* value ) const; - /// QueryDoubleValue examines the value string. See QueryIntValue(). - int QueryDoubleValue( double* value ) const; - - void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. - void SetValue( const char* _value ) { value = _value; } ///< Set the value. - - void SetIntValue( int value ); ///< Set the value from an integer. - void SetDoubleValue( double value ); ///< Set the value from a double. - - #ifdef TIXML_USE_STL - /// STL std::string form. - void SetName( const std::string& _name ) - { - StringToBuffer buf( _name ); - SetName ( buf.buffer ? buf.buffer : "error" ); - } - /// STL std::string form. - void SetValue( const std::string& _value ) - { - StringToBuffer buf( _value ); - SetValue( buf.buffer ? buf.buffer : "error" ); - } - #endif - - /// Get the next sibling attribute in the DOM. Returns null at end. - const TiXmlAttribute* Next() const; - TiXmlAttribute* Next(); - /// Get the previous sibling attribute in the DOM. Returns null at beginning. - const TiXmlAttribute* Previous() const; - TiXmlAttribute* Previous(); - - bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } - bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } - bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } - - /* Attribute parsing starts: first letter of the name - returns: the next char after the value end quote - */ - virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); - - // Prints this Attribute to a FILE stream. - virtual void Print( FILE* cfile, int depth ) const; - - virtual void StreamOut( TIXML_OSTREAM * out ) const; - // [internal use] - // Set the document pointer so the attribute can report errors. - void SetDocument( TiXmlDocument* doc ) { document = doc; } - -private: - TiXmlAttribute( const TiXmlAttribute& ); // not implemented. - void operator=( const TiXmlAttribute& base ); // not allowed. - - TiXmlDocument* document; // A pointer back to a document, for error reporting. - TIXML_STRING name; - TIXML_STRING value; - TiXmlAttribute* prev; - TiXmlAttribute* next; -}; - - -/* A class used to manage a group of attributes. - It is only used internally, both by the ELEMENT and the DECLARATION. - - The set can be changed transparent to the Element and Declaration - classes that use it, but NOT transparent to the Attribute - which has to implement a next() and previous() method. Which makes - it a bit problematic and prevents the use of STL. - - This version is implemented with circular lists because: - - I like circular lists - - it demonstrates some independence from the (typical) doubly linked list. -*/ -class TiXmlAttributeSet -{ -public: - TiXmlAttributeSet(); - ~TiXmlAttributeSet(); - - void Add( TiXmlAttribute* attribute ); - void Remove( TiXmlAttribute* attribute ); - - const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } - TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } - const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } - TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } - - const TiXmlAttribute* Find( const char * name ) const; - TiXmlAttribute* Find( const char * name ); - -private: - //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), - //*ME: this class must be also use a hidden/disabled copy-constructor !!! - TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed - void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) - - TiXmlAttribute sentinel; -}; - - -/** The element is a container class. It has a value, the element name, - and can contain other elements, text, comments, and unknowns. - Elements also contain an arbitrary number of attributes. -*/ -class TiXmlElement : public TiXmlNode -{ -public: - /// Construct an element. - TiXmlElement (const char * in_value); - - #ifdef TIXML_USE_STL - /// std::string constructor. - TiXmlElement( const std::string& _value ); - #endif - - TiXmlElement( const TiXmlElement& ); - - void operator=( const TiXmlElement& base ); - - virtual ~TiXmlElement(); - - /** Given an attribute name, Attribute() returns the value - for the attribute of that name, or null if none exists. - */ - const char* Attribute( const char* name ) const; - - /** Given an attribute name, Attribute() returns the value - for the attribute of that name, or null if none exists. - If the attribute exists and can be converted to an integer, - the integer value will be put in the return 'i', if 'i' - is non-null. - */ - const char* Attribute( const char* name, int* i ) const; - - /** Given an attribute name, Attribute() returns the value - for the attribute of that name, or null if none exists. - If the attribute exists and can be converted to an double, - the double value will be put in the return 'd', if 'd' - is non-null. - */ - const char* Attribute( const char* name, double* d ) const; - - /** QueryIntAttribute examines the attribute - it is an alternative to the - Attribute() method with richer error checking. - If the attribute is an integer, it is stored in 'value' and - the call returns TIXML_SUCCESS. If it is not - an integer, it returns TIXML_WRONG_TYPE. If the attribute - does not exist, then TIXML_NO_ATTRIBUTE is returned. - */ - int QueryIntAttribute( const char* name, int* value ) const; - /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). - int QueryDoubleAttribute( const char* name, double* value ) const; - /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). - int QueryDoubleAttribute( const char* name, float* value ) const { - double d; - int result = QueryDoubleAttribute( name, &d ); - *value = (float)d; - return result; - } - - /** Sets an attribute of name to a given value. The attribute - will be created if it does not exist, or changed if it does. - */ - void SetAttribute( const char* name, const char * value ); - - #ifdef TIXML_USE_STL - const char* Attribute( const std::string& name ) const { return Attribute( name.c_str() ); } - const char* Attribute( const std::string& name, int* i ) const { return Attribute( name.c_str(), i ); } - const char* Attribute( const std::string& name, double* d ) const { return Attribute( name.c_str(), d ); } - int QueryIntAttribute( const std::string& name, int* value ) const { return QueryIntAttribute( name.c_str(), value ); } - int QueryDoubleAttribute( const std::string& name, double* value ) const { return QueryDoubleAttribute( name.c_str(), value ); } - - /// STL std::string form. - void SetAttribute( const std::string& name, const std::string& _value ) - { - StringToBuffer n( name ); - StringToBuffer v( _value ); - if ( n.buffer && v.buffer ) - SetAttribute (n.buffer, v.buffer ); - } - ///< STL std::string form. - void SetAttribute( const std::string& name, int _value ) - { - StringToBuffer n( name ); - if ( n.buffer ) - SetAttribute (n.buffer, _value); - } - #endif - - /** Sets an attribute of name to a given value. The attribute - will be created if it does not exist, or changed if it does. - */ - void SetAttribute( const char * name, int value ); - - /** Sets an attribute of name to a given value. The attribute - will be created if it does not exist, or changed if it does. - */ - void SetDoubleAttribute( const char * name, double value ); - - /** Deletes an attribute with the given name. - */ - void RemoveAttribute( const char * name ); - #ifdef TIXML_USE_STL - void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. - #endif - - const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. - TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } - const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. - TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } - - /// Creates a new Element and returns it - the returned element is a copy. - virtual TiXmlNode* Clone() const; - // Print the Element to a FILE stream. - virtual void Print( FILE* cfile, int depth ) const; - - /* Attribtue parsing starts: next char past '<' - returns: next char past '>' - */ - virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); - -protected: - - void CopyTo( TiXmlElement* target ) const; - void ClearThis(); // like clear, but initializes 'this' object as well - - // Used to be public [internal use] - #ifdef TIXML_USE_STL - virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); - #endif - virtual void StreamOut( TIXML_OSTREAM * out ) const; - - /* [internal use] - Reads the "value" of the element -- another element, or text. - This should terminate with the current end tag. - */ - const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); - -private: - - TiXmlAttributeSet attributeSet; -}; - - -/** An XML comment. -*/ -class TiXmlComment : public TiXmlNode -{ -public: - /// Constructs an empty comment. - TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {} - TiXmlComment( const TiXmlComment& ); - void operator=( const TiXmlComment& base ); - - virtual ~TiXmlComment() {} - - /// Returns a copy of this Comment. - virtual TiXmlNode* Clone() const; - /// Write this Comment to a FILE stream. - virtual void Print( FILE* cfile, int depth ) const; - - /* Attribtue parsing starts: at the ! of the !-- - returns: next char past '>' - */ - virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); - -protected: - void CopyTo( TiXmlComment* target ) const; - - // used to be public - #ifdef TIXML_USE_STL - virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); - #endif - virtual void StreamOut( TIXML_OSTREAM * out ) const; - -private: - -}; - - -/** XML text. Contained in an element. -*/ -class TiXmlText : public TiXmlNode -{ - friend class TiXmlElement; -public: - /// Constructor. - TiXmlText (const char * initValue) : TiXmlNode (TiXmlNode::TEXT) - { - SetValue( initValue ); - } - virtual ~TiXmlText() {} - - #ifdef TIXML_USE_STL - /// Constructor. - TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) - { - SetValue( initValue ); - } - #endif - - TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); } - void operator=( const TiXmlText& base ) { base.CopyTo( this ); } - - /// Write this text object to a FILE stream. - virtual void Print( FILE* cfile, int depth ) const; - - virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); - -protected : - /// [internal use] Creates a new Element and returns it. - virtual TiXmlNode* Clone() const; - void CopyTo( TiXmlText* target ) const; - - virtual void StreamOut ( TIXML_OSTREAM * out ) const; - bool Blank() const; // returns true if all white space and new lines - // [internal use] - #ifdef TIXML_USE_STL - virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); - #endif - -private: -}; - - -/** In correct XML the declaration is the first entry in the file. - @verbatim - - @endverbatim - - TinyXml will happily read or write files without a declaration, - however. There are 3 possible attributes to the declaration: - version, encoding, and standalone. - - Note: In this version of the code, the attributes are - handled as special cases, not generic attributes, simply - because there can only be at most 3 and they are always the same. -*/ -class TiXmlDeclaration : public TiXmlNode -{ -public: - /// Construct an empty declaration. - TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {} - -#ifdef TIXML_USE_STL - /// Constructor. - TiXmlDeclaration( const std::string& _version, - const std::string& _encoding, - const std::string& _standalone ); -#endif - - /// Construct. - TiXmlDeclaration( const char* _version, - const char* _encoding, - const char* _standalone ); - - TiXmlDeclaration( const TiXmlDeclaration& copy ); - void operator=( const TiXmlDeclaration& copy ); - - virtual ~TiXmlDeclaration() {} - - /// Version. Will return an empty string if none was found. - const char *Version() const { return version.c_str (); } - /// Encoding. Will return an empty string if none was found. - const char *Encoding() const { return encoding.c_str (); } - /// Is this a standalone document? - const char *Standalone() const { return standalone.c_str (); } - - /// Creates a copy of this Declaration and returns it. - virtual TiXmlNode* Clone() const; - /// Print this declaration to a FILE stream. - virtual void Print( FILE* cfile, int depth ) const; - - virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); - -protected: - void CopyTo( TiXmlDeclaration* target ) const; - // used to be public - #ifdef TIXML_USE_STL - virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); - #endif - virtual void StreamOut ( TIXML_OSTREAM * out) const; - -private: - - TIXML_STRING version; - TIXML_STRING encoding; - TIXML_STRING standalone; -}; - - -/** Any tag that tinyXml doesn't recognize is saved as an - unknown. It is a tag of text, but should not be modified. - It will be written back to the XML, unchanged, when the file - is saved. - - DTD tags get thrown into TiXmlUnknowns. -*/ -class TiXmlUnknown : public TiXmlNode -{ -public: - TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} - virtual ~TiXmlUnknown() {} - - TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); } - void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } - - /// Creates a copy of this Unknown and returns it. - virtual TiXmlNode* Clone() const; - /// Print this Unknown to a FILE stream. - virtual void Print( FILE* cfile, int depth ) const; - - virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); - -protected: - void CopyTo( TiXmlUnknown* target ) const; - - #ifdef TIXML_USE_STL - virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); - #endif - virtual void StreamOut ( TIXML_OSTREAM * out ) const; - -private: - -}; - - -/** Always the top level node. A document binds together all the - XML pieces. It can be saved, loaded, and printed to the screen. - The 'value' of a document node is the xml file name. -*/ -class TiXmlDocument : public TiXmlNode -{ -public: - /// Create an empty document, that has no name. - TiXmlDocument(); - /// Create a document with a name. The name of the document is also the filename of the xml. - TiXmlDocument( const char * documentName ); - - #ifdef TIXML_USE_STL - /// Constructor. - TiXmlDocument( const std::string& documentName ); - #endif - - TiXmlDocument( const TiXmlDocument& copy ); - void operator=( const TiXmlDocument& copy ); - - virtual ~TiXmlDocument() {} - - /** Load a file using the current document value. - Returns true if successful. Will delete any existing - document data before loading. - */ - bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); - /// Save a file using the current document value. Returns true if successful. - bool SaveFile() const; - /// Load a file using the given filename. Returns true if successful. - bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); - /// Save a file using the given filename. Returns true if successful. - bool SaveFile( const char * filename ) const; - - #ifdef TIXML_USE_STL - bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. - { - StringToBuffer f( filename ); - return ( f.buffer && LoadFile( f.buffer, encoding )); - } - bool SaveFile( const std::string& filename ) const ///< STL std::string version. - { - StringToBuffer f( filename ); - return ( f.buffer && SaveFile( f.buffer )); - } - #endif - - /** Parse the given null terminated block of xml data. Passing in an encoding to this - method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml - to use that encoding, regardless of what TinyXml might otherwise try to detect. - */ - virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); - - /** Get the root element -- the only top level element -- of the document. - In well formed XML, there should only be one. TinyXml is tolerant of - multiple elements at the document level. - */ - const TiXmlElement* RootElement() const { return FirstChildElement(); } - TiXmlElement* RootElement() { return FirstChildElement(); } - - /** If an error occurs, Error will be set to true. Also, - - The ErrorId() will contain the integer identifier of the error (not generally useful) - - The ErrorDesc() method will return the name of the error. (very useful) - - The ErrorRow() and ErrorCol() will return the location of the error (if known) - */ - bool Error() const { return error; } - - /// Contains a textual (english) description of the error if one occurs. - const char * ErrorDesc() const { return errorDesc.c_str (); } - - /** Generally, you probably want the error string ( ErrorDesc() ). But if you - prefer the ErrorId, this function will fetch it. - */ - const int ErrorId() const { return errorId; } - - /** Returns the location (if known) of the error. The first column is column 1, - and the first row is row 1. A value of 0 means the row and column wasn't applicable - (memory errors, for example, have no row/column) or the parser lost the error. (An - error in the error reporting, in that case.) - - @sa SetTabSize, Row, Column - */ - int ErrorRow() { return errorLocation.row+1; } - int ErrorCol() { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() - - /** By calling this method, with a tab size - greater than 0, the row and column of each node and attribute is stored - when the file is loaded. Very useful for tracking the DOM back in to - the source file. - - The tab size is required for calculating the location of nodes. If not - set, the default of 4 is used. The tabsize is set per document. Setting - the tabsize to 0 disables row/column tracking. - - Note that row and column tracking is not supported when using operator>>. - - The tab size needs to be enabled before the parse or load. Correct usage: - @verbatim - TiXmlDocument doc; - doc.SetTabSize( 8 ); - doc.Load( "myfile.xml" ); - @endverbatim - - @sa Row, Column - */ - void SetTabSize( int _tabsize ) { tabsize = _tabsize; } - - int TabSize() const { return tabsize; } - - /** If you have handled the error, it can be reset with this call. The error - state is automatically cleared if you Parse a new XML block. - */ - void ClearError() { error = false; - errorId = 0; - errorDesc = ""; - errorLocation.row = errorLocation.col = 0; - //errorLocation.last = 0; - } - - /** Dump the document to standard out. */ - void Print() const { Print( stdout, 0 ); } - - /// Print this Document to a FILE stream. - virtual void Print( FILE* cfile, int depth = 0 ) const; - // [internal use] - void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); - -protected : - virtual void StreamOut ( TIXML_OSTREAM * out) const; - // [internal use] - virtual TiXmlNode* Clone() const; - #ifdef TIXML_USE_STL - virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); - #endif - -private: - void CopyTo( TiXmlDocument* target ) const; - - bool error; - int errorId; - TIXML_STRING errorDesc; - int tabsize; - TiXmlCursor errorLocation; -}; - - -/** - A TiXmlHandle is a class that wraps a node pointer with null checks; this is - an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml - DOM structure. It is a separate utility class. - - Take an example: - @verbatim - - - - - - - @endverbatim - - Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very - easy to write a *lot* of code that looks like: - - @verbatim - TiXmlElement* root = document.FirstChildElement( "Document" ); - if ( root ) - { - TiXmlElement* element = root->FirstChildElement( "Element" ); - if ( element ) - { - TiXmlElement* child = element->FirstChildElement( "Child" ); - if ( child ) - { - TiXmlElement* child2 = child->NextSiblingElement( "Child" ); - if ( child2 ) - { - // Finally do something useful. - @endverbatim - - And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity - of such code. A TiXmlHandle checks for null pointers so it is perfectly safe - and correct to use: - - @verbatim - TiXmlHandle docHandle( &document ); - TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).Element(); - if ( child2 ) - { - // do something useful - @endverbatim - - Which is MUCH more concise and useful. - - It is also safe to copy handles - internally they are nothing more than node pointers. - @verbatim - TiXmlHandle handleCopy = handle; - @endverbatim - - What they should not be used for is iteration: - - @verbatim - int i=0; - while ( true ) - { - TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).Element(); - if ( !child ) - break; - // do something - ++i; - } - @endverbatim - - It seems reasonable, but it is in fact two embedded while loops. The Child method is - a linear walk to find the element, so this code would iterate much more than it needs - to. Instead, prefer: - - @verbatim - TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).Element(); - - for( child; child; child=child->NextSiblingElement() ) - { - // do something - } - @endverbatim -*/ -class TiXmlHandle -{ -public: - /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. - TiXmlHandle( TiXmlNode* node ) { this->node = node; } - /// Copy constructor - TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } - TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; } - - /// Return a handle to the first child node. - TiXmlHandle FirstChild() const; - /// Return a handle to the first child node with the given name. - TiXmlHandle FirstChild( const char * value ) const; - /// Return a handle to the first child element. - TiXmlHandle FirstChildElement() const; - /// Return a handle to the first child element with the given name. - TiXmlHandle FirstChildElement( const char * value ) const; - - /** Return a handle to the "index" child with the given name. - The first child is 0, the second 1, etc. - */ - TiXmlHandle Child( const char* value, int index ) const; - /** Return a handle to the "index" child. - The first child is 0, the second 1, etc. - */ - TiXmlHandle Child( int index ) const; - /** Return a handle to the "index" child element with the given name. - The first child element is 0, the second 1, etc. Note that only TiXmlElements - are indexed: other types are not counted. - */ - TiXmlHandle ChildElement( const char* value, int index ) const; - /** Return a handle to the "index" child element. - The first child element is 0, the second 1, etc. Note that only TiXmlElements - are indexed: other types are not counted. - */ - TiXmlHandle ChildElement( int index ) const; - - #ifdef TIXML_USE_STL - TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } - TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } - - TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } - TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } - #endif - - /// Return the handle as a TiXmlNode. This may return null. - TiXmlNode* Node() const { return node; } - /// Return the handle as a TiXmlElement. This may return null. - TiXmlElement* Element() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } - /// Return the handle as a TiXmlText. This may return null. - TiXmlText* Text() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } - /// Return the handle as a TiXmlUnknown. This may return null; - TiXmlUnknown* Unknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } - -private: - TiXmlNode* node; -}; - -#ifdef _MSC_VER -#pragma warning( default : 4530 ) -#pragma warning( default : 4786 ) -#endif - -#endif - diff --git a/common/tinyxml/tinyxmlerror.cpp b/common/tinyxml/tinyxmlerror.cpp deleted file mode 100644 index b04add7f9..000000000 --- a/common/tinyxml/tinyxmlerror.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* -www.sourceforge.net/projects/tinyxml -Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#include "tinyxml.h" - -// The goal of the seperate error file is to make the first -// step towards localization. tinyxml (currently) only supports -// latin-1, but at least the error messages could now be translated. -// -// It also cleans up the code a bit. -// - -const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] = -{ - "No error", - "Error", - "Failed to open file", - "Memory allocation failed.", - "Error parsing Element.", - "Failed to read Element name", - "Error reading Element value.", - "Error reading Attributes.", - "Error: empty tag.", - "Error reading end tag.", - "Error parsing Unknown.", - "Error parsing Comment.", - "Error parsing Declaration.", - "Error document empty.", - "Error null (0) or unexpected EOF found in input stream.", -}; diff --git a/common/tinyxml/tinyxmlparser.cpp b/common/tinyxml/tinyxmlparser.cpp deleted file mode 100644 index 3e6b174f7..000000000 --- a/common/tinyxml/tinyxmlparser.cpp +++ /dev/null @@ -1,1508 +0,0 @@ -/* -www.sourceforge.net/projects/tinyxml -Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#include "tinyxml.h" -#include -#include - -//#define DEBUG_PARSER - -// Note tha "PutString" hardcodes the same list. This -// is less flexible than it appears. Changing the entries -// or order will break putstring. -TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = -{ - { "&", 5, '&' }, - { "<", 4, '<' }, - { ">", 4, '>' }, - { """, 6, '\"' }, - { "'", 6, '\'' } -}; - -// Bunch of unicode info at: -// http://www.unicode.org/faq/utf_bom.html -// Including the basic of this table, which determines the #bytes in the -// sequence from the lead byte. 1 placed for invalid sequences -- -// although the result will be junk, pass it through as much as possible. -// Beware of the non-characters in UTF-8: -// ef bb bf (Microsoft "lead bytes") -// ef bf be -// ef bf bf - -const unsigned char TIXML_UTF_LEAD_0 = 0xefU; -const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; -const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; - -const int TiXmlBase::utf8ByteTable[256] = -{ - // 0 1 2 3 4 5 6 7 8 9 a b c d e f - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 - 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte - 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid -}; - - -void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) -{ - const unsigned long BYTE_MASK = 0xBF; - const unsigned long BYTE_MARK = 0x80; - const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; - - if (input < 0x80) - *length = 1; - else if ( input < 0x800 ) - *length = 2; - else if ( input < 0x10000 ) - *length = 3; - else if ( input < 0x200000 ) - *length = 4; - else - { *length = 0; return; } // This code won't covert this correctly anyway. - - output += *length; - - // Scary scary fall throughs. - switch (*length) - { - case 4: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 3: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 2: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 1: - --output; - *output = (char)(input | FIRST_BYTE_MARK[*length]); - } -} - - -/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) -{ - // This will only work for low-ascii, everything else is assumed to be a valid - // letter. I'm not sure this is the best approach, but it is quite tricky trying - // to figure out alhabetical vs. not across encoding. So take a very - // conservative approach. - -// if ( encoding == TIXML_ENCODING_UTF8 ) -// { - if ( anyByte < 127 ) - return isalpha( anyByte ); - else - return 1; // What else to do? The unicode set is huge...get the english ones right. -// } -// else -// { -// return isalpha( anyByte ); -// } -} - - -/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) -{ - // This will only work for low-ascii, everything else is assumed to be a valid - // letter. I'm not sure this is the best approach, but it is quite tricky trying - // to figure out alhabetical vs. not across encoding. So take a very - // conservative approach. - -// if ( encoding == TIXML_ENCODING_UTF8 ) -// { - if ( anyByte < 127 ) - return isalnum( anyByte ); - else - return 1; // What else to do? The unicode set is huge...get the english ones right. -// } -// else -// { -// return isalnum( anyByte ); -// } -} - - -class TiXmlParsingData -{ - friend class TiXmlDocument; - public: - void Stamp( const char* now, TiXmlEncoding encoding ); - - const TiXmlCursor& Cursor() { return cursor; } - - private: - // Only used by the document! - TiXmlParsingData( const char* start, int _tabsize, int row, int col ) - { - assert( start ); - stamp = start; - tabsize = _tabsize; - cursor.row = row; - cursor.col = col; - } - - TiXmlCursor cursor; - const char* stamp; - int tabsize; -}; - - -void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) -{ - assert( now ); - - // Do nothing if the tabsize is 0. - if ( tabsize < 1 ) - { - return; - } - - // Get the current row, column. - int row = cursor.row; - int col = cursor.col; - const char* p = stamp; - assert( p ); - - while ( p < now ) - { - // Treat p as unsigned, so we have a happy compiler. - const unsigned char* pU = (const unsigned char*)p; - - // Code contributed by Fletcher Dunn: (modified by lee) - switch (*pU) { - case 0: - // We *should* never get here, but in case we do, don't - // advance past the terminating null character, ever - return; - - case '\r': - // bump down to the next line - ++row; - col = 0; - // Eat the character - ++p; - - // Check for \r\n sequence, and treat this as a single character - if (*p == '\n') { - ++p; - } - break; - - case '\n': - // bump down to the next line - ++row; - col = 0; - - // Eat the character - ++p; - - // Check for \n\r sequence, and treat this as a single - // character. (Yes, this bizarre thing does occur still - // on some arcane platforms...) - if (*p == '\r') { - ++p; - } - break; - - case '\t': - // Eat the character - ++p; - - // Skip to next tab stop - col = (col / tabsize + 1) * tabsize; - break; - - case TIXML_UTF_LEAD_0: - if ( encoding == TIXML_ENCODING_UTF8 ) - { - if ( *(p+1) && *(p+2) ) - { - // In these cases, don't advance the column. These are - // 0-width spaces. - if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) - p += 3; - else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) - p += 3; - else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) - p += 3; - else - { p +=3; ++col; } // A normal character. - } - } - else - { - ++p; - ++col; - } - break; - - default: - if ( encoding == TIXML_ENCODING_UTF8 ) - { - // Eat the 1 to 4 byte utf8 character. - int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; - if ( step == 0 ) - step = 1; // Error case from bad encoding, but handle gracefully. - p += step; - - // Just advance one column, of course. - ++col; - } - else - { - ++p; - ++col; - } - break; - } - } - cursor.row = row; - cursor.col = col; - assert( cursor.row >= -1 ); - assert( cursor.col >= -1 ); - stamp = p; - assert( stamp ); -} - - -const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) -{ - if ( !p || !*p ) - { - return 0; - } - if ( encoding == TIXML_ENCODING_UTF8 ) - { - while ( *p ) - { - const unsigned char* pU = (const unsigned char*)p; - - // Skip the stupid Microsoft UTF-8 Byte order marks - if ( *(pU+0)==TIXML_UTF_LEAD_0 - && *(pU+1)==TIXML_UTF_LEAD_1 - && *(pU+2)==TIXML_UTF_LEAD_2 ) - { - p += 3; - continue; - } - else if(*(pU+0)==TIXML_UTF_LEAD_0 - && *(pU+1)==0xbfU - && *(pU+2)==0xbeU ) - { - p += 3; - continue; - } - else if(*(pU+0)==TIXML_UTF_LEAD_0 - && *(pU+1)==0xbfU - && *(pU+2)==0xbfU ) - { - p += 3; - continue; - } - - if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. - ++p; - else - break; - } - } - else - { - while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) - ++p; - } - - return p; -} - -#ifdef TIXML_USE_STL -/*static*/ bool TiXmlBase::StreamWhiteSpace( TIXML_ISTREAM * in, TIXML_STRING * tag ) -{ - for( ;; ) - { - if ( !in->good() ) return false; - - int c = in->peek(); - // At this scope, we can't get to a document. So fail silently. - if ( !IsWhiteSpace( c ) || c <= 0 ) - return true; - - *tag += (char) in->get(); - } -} - -/*static*/ bool TiXmlBase::StreamTo( TIXML_ISTREAM * in, int character, TIXML_STRING * tag ) -{ - //assert( character > 0 && character < 128 ); // else it won't work in utf-8 - while ( in->good() ) - { - int c = in->peek(); - if ( c == character ) - return true; - if ( c <= 0 ) // Silent failure: can't get document at this scope - return false; - - in->get(); - *tag += (char) c; - } - return false; -} -#endif - -const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) -{ - *name = ""; - assert( p ); - - // Names start with letters or underscores. - // Of course, in unicode, tinyxml has no idea what a letter *is*. The - // algorithm is generous. - // - // After that, they can be letters, underscores, numbers, - // hyphens, or colons. (Colons are valid ony for namespaces, - // but tinyxml can't tell namespaces from names.) - if ( p && *p - && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) - { - while( p && *p - && ( IsAlphaNum( (unsigned char ) *p, encoding ) - || *p == '_' - || *p == '-' - || *p == '.' - || *p == ':' ) ) - { - (*name) += *p; - ++p; - } - return p; - } - return 0; -} - -const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) -{ - // Presume an entity, and pull it out. - TIXML_STRING ent; - int i; - *length = 0; - - if ( *(p+1) && *(p+1) == '#' && *(p+2) ) - { - unsigned long ucs = 0; - ptrdiff_t delta = 0; - unsigned mult = 1; - - if ( *(p+2) == 'x' ) - { - // Hexadecimal. - if ( !*(p+3) ) return 0; - - const char* q = p+3; - q = strchr( q, ';' ); - - if ( !q || !*q ) return 0; - - delta = q-p; - --q; - - while ( *q != 'x' ) - { - if ( *q >= '0' && *q <= '9' ) - ucs += mult * (*q - '0'); - else if ( *q >= 'a' && *q <= 'f' ) - ucs += mult * (*q - 'a' + 10); - else if ( *q >= 'A' && *q <= 'F' ) - ucs += mult * (*q - 'A' + 10 ); - else - return 0; - mult *= 16; - --q; - } - } - else - { - // Decimal. - if ( !*(p+2) ) return 0; - - const char* q = p+2; - q = strchr( q, ';' ); - - if ( !q || !*q ) return 0; - - delta = q-p; - --q; - - while ( *q != '#' ) - { - if ( *q >= '0' && *q <= '9' ) - ucs += mult * (*q - '0'); - else - return 0; - mult *= 10; - --q; - } - } - if ( encoding == TIXML_ENCODING_UTF8 ) - { - // convert the UCS to UTF-8 - ConvertUTF32ToUTF8( ucs, value, length ); - } - else - { - *value = (char)ucs; - *length = 1; - } - return p + delta + 1; - } - - // Now try to match it. - for( i=0; iappend( cArr, len ); - } - } - else - { - bool whitespace = false; - - // Remove leading white space: - p = SkipWhiteSpace( p, encoding ); - while ( p && *p - && !StringEqual( p, endTag, caseInsensitive, encoding ) ) - { - if ( *p == '\r' || *p == '\n' ) - { - whitespace = true; - ++p; - } - else if ( IsWhiteSpace( *p ) ) - { - whitespace = true; - ++p; - } - else - { - // If we've found whitespace, add it before the - // new character. Any whitespace just becomes a space. - if ( whitespace ) - { - (*text) += ' '; - whitespace = false; - } - int len; - char cArr[4] = { 0, 0, 0, 0 }; - p = GetChar( p, cArr, &len, encoding ); - if ( len == 1 ) - (*text) += cArr[0]; // more efficient - else - text->append( cArr, len ); - } - } - } - return p + strlen( endTag ); -} - -#ifdef TIXML_USE_STL - -void TiXmlDocument::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ) -{ - // The basic issue with a document is that we don't know what we're - // streaming. Read something presumed to be a tag (and hope), then - // identify it, and call the appropriate stream method on the tag. - // - // This "pre-streaming" will never read the closing ">" so the - // sub-tag can orient itself. - - if ( !StreamTo( in, '<', tag ) ) - { - SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); - return; - } - - while ( in->good() ) - { - int tagIndex = (int) tag->length(); - while ( in->good() && in->peek() != '>' ) - { - int c = in->get(); - if ( c <= 0 ) - { - SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); - break; - } - (*tag) += (char) c; - } - - if ( in->good() ) - { - // We now have something we presume to be a node of - // some sort. Identify it, and call the node to - // continue streaming. - TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); - - if ( node ) - { - node->StreamIn( in, tag ); - bool isElement = node->ToElement() != 0; - delete node; - node = 0; - - // If this is the root element, we're done. Parsing will be - // done by the >> operator. - if ( isElement ) - { - return; - } - } - else - { - SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); - return; - } - } - } - // We should have returned sooner. - SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); -} - -#endif - -const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) -{ - ClearError(); - - // Parse away, at the document level. Since a document - // contains nothing but other tags, most of what happens - // here is skipping white space. - if ( !p || !*p ) - { - SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); - return 0; - } - - // Note that, for a document, this needs to come - // before the while space skip, so that parsing - // starts from the pointer we are given. - location.Clear(); - if ( prevData ) - { - location.row = prevData->cursor.row; - location.col = prevData->cursor.col; - } - else - { - location.row = 0; - location.col = 0; - } - TiXmlParsingData data( p, TabSize(), location.row, location.col ); - location = data.Cursor(); - - if ( encoding == TIXML_ENCODING_UNKNOWN ) - { - // Check for the Microsoft UTF-8 lead bytes. - const unsigned char* pU = (const unsigned char*)p; - if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 - && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 - && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) - { - encoding = TIXML_ENCODING_UTF8; - } - } - - p = SkipWhiteSpace( p, encoding ); - if ( !p ) - { - SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); - return 0; - } - - while ( p && *p ) - { - TiXmlNode* node = Identify( p, encoding ); - if ( node ) - { - p = node->Parse( p, &data, encoding ); - LinkEndChild( node ); - } - else - { - break; - } - - // Did we get encoding info? - if ( encoding == TIXML_ENCODING_UNKNOWN - && node->ToDeclaration() ) - { - TiXmlDeclaration* dec = node->ToDeclaration(); - const char* enc = dec->Encoding(); - assert( enc ); - - if ( *enc == 0 ) - encoding = TIXML_ENCODING_UTF8; - else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) - encoding = TIXML_ENCODING_UTF8; - else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) - encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice - else - encoding = TIXML_ENCODING_LEGACY; - } - - p = SkipWhiteSpace( p, encoding ); - } - - // Was this empty? - if ( !firstChild ) { - SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); - return 0; - } - - // All is well. - return p; -} - -void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) -{ - // The first error in a chain is more accurate - don't set again! - if ( error ) - return; - - assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); - error = true; - errorId = err; - errorDesc = errorString[ errorId ]; - - errorLocation.Clear(); - if ( pError && data ) - { - //TiXmlParsingData data( pError, prevData ); - data->Stamp( pError, encoding ); - errorLocation = data->Cursor(); - } -} - - -TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) -{ - TiXmlNode* returnNode = 0; - - p = SkipWhiteSpace( p, encoding ); - if( !p || !*p || *p != '<' ) - { - return 0; - } - - TiXmlDocument* doc = GetDocument(); - p = SkipWhiteSpace( p, encoding ); - - if ( !p || !*p ) - { - return 0; - } - - // What is this thing? - // - Elements start with a letter or underscore, but xml is reserved. - // - Comments: "; - - if ( !StringEqual( p, startTag, false, encoding ) ) - { - document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); - return 0; - } - p += strlen( startTag ); - p = ReadText( p, &value, false, endTag, false, encoding ); - return p; -} - - -const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) -{ - p = SkipWhiteSpace( p, encoding ); - if ( !p || !*p ) return 0; - - int tabsize = 4; - if ( document ) - tabsize = document->TabSize(); - -// TiXmlParsingData data( p, prevData ); - if ( data ) - { - data->Stamp( p, encoding ); - location = data->Cursor(); - } - // Read the name, the '=' and the value. - const char* pErr = p; - p = ReadName( p, &name, encoding ); - if ( !p || !*p ) - { - if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); - return 0; - } - p = SkipWhiteSpace( p, encoding ); - if ( !p || !*p || *p != '=' ) - { - if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); - return 0; - } - - ++p; // skip '=' - p = SkipWhiteSpace( p, encoding ); - if ( !p || !*p ) - { - if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); - return 0; - } - - const char* end; - - if ( *p == '\'' ) - { - ++p; - end = "\'"; - p = ReadText( p, &value, false, end, false, encoding ); - } - else if ( *p == '"' ) - { - ++p; - end = "\""; - p = ReadText( p, &value, false, end, false, encoding ); - } - else - { - // All attribute values should be in single or double quotes. - // But this is such a common error that the parser will try - // its best, even without them. - value = ""; - while ( p && *p // existence - && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace - && *p != '/' && *p != '>' ) // tag end - { - value += *p; - ++p; - } - } - return p; -} - -#ifdef TIXML_USE_STL -void TiXmlText::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ) -{ - while ( in->good() ) - { - int c = in->peek(); - if ( c == '<' ) - return; - if ( c <= 0 ) - { - TiXmlDocument* document = GetDocument(); - if ( document ) - document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); - return; - } - - (*tag) += (char) c; - in->get(); - } -} -#endif - -const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) -{ - value = ""; -// TiXmlParsingData data( p, prevData ); - if ( data ) - { - data->Stamp( p, encoding ); - location = data->Cursor(); - } - bool ignoreWhite = true; - - const char* end = "<"; - p = ReadText( p, &value, ignoreWhite, end, false, encoding ); - if ( p ) - return p-1; // don't truncate the '<' - return 0; -} - -#ifdef TIXML_USE_STL -void TiXmlDeclaration::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ) -{ - while ( in->good() ) - { - int c = in->get(); - if ( c <= 0 ) - { - TiXmlDocument* document = GetDocument(); - if ( document ) - document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); - return; - } - (*tag) += (char) c; - - if ( c == '>' ) - { - // All is well. - return; - } - } -} -#endif - -const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) -{ - p = SkipWhiteSpace( p, _encoding ); - // Find the beginning, find the end, and look for - // the stuff in-between. - TiXmlDocument* document = GetDocument(); - if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); - return 0; - } -// TiXmlParsingData data( p, prevData ); - if ( data ) - { - data->Stamp( p, _encoding ); - location = data->Cursor(); - } - p += 5; - - version = ""; - encoding = ""; - standalone = ""; - - while ( p && *p ) - { - if ( *p == '>' ) - { - ++p; - return p; - } - - p = SkipWhiteSpace( p, _encoding ); - if ( StringEqual( p, "version", true, _encoding ) ) - { - TiXmlAttribute attrib; - p = attrib.Parse( p, data, _encoding ); - version = attrib.Value(); - } - else if ( StringEqual( p, "encoding", true, _encoding ) ) - { - TiXmlAttribute attrib; - p = attrib.Parse( p, data, _encoding ); - encoding = attrib.Value(); - } - else if ( StringEqual( p, "standalone", true, _encoding ) ) - { - TiXmlAttribute attrib; - p = attrib.Parse( p, data, _encoding ); - standalone = attrib.Value(); - } - else - { - // Read over whatever it is. - while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) - ++p; - } - } - return 0; -} - -bool TiXmlText::Blank() const -{ - for ( unsigned i=0; iWorldIP.c_str(), Config->WorldTCPPort); + LogNetcode("[WORLD] Connected to World: [{}]:[{}]", Config->WorldIP.c_str(), Config->WorldTCPPort); auto pack = new ServerPacket(ServerOP_ZAAuth, 16); MD5::Generate((const uchar*) m_password.c_str(), m_password.length(), pack->pBuffer); @@ -76,7 +76,7 @@ bool WorldConnection::Connect() { if (tcpc.Connect(Config->WorldIP.c_str(), Config->WorldTCPPort, errbuf)) { return true; } else { - Log(Logs::General, Logs::Netcode, "[WORLD] WorldConnection connect: Connecting to the server %s:%d failed: %s", Config->WorldIP.c_str(), Config->WorldTCPPort, errbuf); + LogNetcode("[WORLD] WorldConnection connect: Connecting to the server [{}]:[{}] failed: [{}]", Config->WorldIP.c_str(), Config->WorldTCPPort, errbuf); } return false; } diff --git a/common/xml_parser.cpp b/common/xml_parser.cpp deleted file mode 100644 index f252ce177..000000000 --- a/common/xml_parser.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net) - - 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 -*/ -#include "global_define.h" -#include "xml_parser.h" - -XMLParser::XMLParser() { - ParseOkay = false; -} - -bool XMLParser::ParseFile(const char *file, const char *root_ele) { - std::map::iterator handler; - TiXmlDocument doc( file ); - if(!doc.LoadFile()) { - printf("Unable to load '%s': %s\n", file, doc.ErrorDesc()); - return(false); - } - - TiXmlElement *root = doc.FirstChildElement( root_ele ); - if(root == nullptr) { - printf("Unable to find root '%s' in %s\n",root_ele, file); - return(false); - } - - ParseOkay=true; - - TiXmlNode *main_element = nullptr; - while( (main_element = root->IterateChildren( main_element )) ) { - if(main_element->Type() != TiXmlNode::ELEMENT) - continue; //skip crap we dont care about - TiXmlElement *ele = (TiXmlElement *) main_element; - - handler=Handlers.find(ele->Value()); - if (handler!=Handlers.end() && handler->second) { - ElementHandler h=handler->second; - - /* - * - * This is kinda a sketchy operation here, since all of these - * element handler methods will be functions in child classes. - * This essentially causes us to do an un-checkable (and hence - * un-handle-properly-able) cast down to the child class. This - * WILL BREAK if any children classes do multiple inheritance. - * - * - */ - - (this->*h)(ele); - } else { - //unhandled element.... do nothing for now - } - - } - - return(ParseOkay); -} - -const char *XMLParser::ParseTextBlock(TiXmlNode *within, const char *name, bool optional) { - TiXmlElement * txt = within->FirstChildElement(name); - if(txt == nullptr) { - if(!optional) { - printf("Unable to find a '%s' element on %s element at line %d\n", name, within->Value(), within->Row()); - ParseOkay=false; - } - return(nullptr); - } - TiXmlNode *contents = txt->FirstChild(); - if(contents == nullptr || contents->Type() != TiXmlNode::TEXT) { - if(!optional) - printf("Node '%s' was expected to be a text element in %s element at line %d\n", name, txt->Value(), txt->Row()); - return(nullptr); - } - return(contents->Value()); -} - -const char *XMLParser::GetText(TiXmlNode *within, bool optional) { - TiXmlNode *contents = within->FirstChild(); - if(contents == nullptr || contents->Type() != TiXmlNode::TEXT) { - if(!optional) { - printf("Node was expected to be a text element in %s element at line %d\n", within->Value(), within->Row()); - ParseOkay=false; - } - return(nullptr); - } - return(contents->Value()); -} - diff --git a/common/xml_parser.h b/common/xml_parser.h deleted file mode 100644 index 38ed6d254..000000000 --- a/common/xml_parser.h +++ /dev/null @@ -1,52 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net) - - 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 XMLParser_H -#define XMLParser_H - -#include "global_define.h" -#include "tinyxml/tinyxml.h" -#include "../common/types.h" - -#include -#include - -/* -* See note in XMLParser::ParseFile() before inheriting this class. -*/ -class XMLParser { -public: - typedef void (XMLParser::*ElementHandler)(TiXmlElement *ele); - - XMLParser(); - virtual ~XMLParser() {} - - bool ParseFile(const char *file, const char *root_ele); - bool ParseStatus() const { return ParseOkay; } - -protected: - const char *ParseTextBlock(TiXmlNode *within, const char *name, bool optional = false); - const char *GetText(TiXmlNode *within, bool optional = false); - - std::map Handlers; - - bool ParseOkay; - -}; - -#endif - diff --git a/loginserver/CMakeLists.txt b/loginserver/CMakeLists.txt index 898af45cb..7d60655fc 100644 --- a/loginserver/CMakeLists.txt +++ b/loginserver/CMakeLists.txt @@ -1,25 +1,26 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.2) SET(eqlogin_sources + account_management.cpp client.cpp client_manager.cpp - config.cpp - database_mysql.cpp - database_postgresql.cpp + database.cpp encryption.cpp + loginserver_command_handler.cpp + loginserver_webserver.cpp main.cpp server_manager.cpp world_server.cpp ) SET(eqlogin_headers + account_management.h client.h client_manager.h - config.h database.h - database_mysql.h - database_postgresql.h encryption.h + loginserver_command_handler.h + loginserver_webserver.h login_server.h login_structures.h options.h diff --git a/loginserver/account_management.cpp b/loginserver/account_management.cpp new file mode 100644 index 000000000..6094a72f3 --- /dev/null +++ b/loginserver/account_management.cpp @@ -0,0 +1,365 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ + +#include "account_management.h" +#include "login_server.h" +#include "../common/event/task_scheduler.h" +#include "../common/event/event_loop.h" +#include "../common/net/dns.h" + +extern LoginServer server; +EQ::Event::TaskScheduler task_runner; + +/** + * @param username + * @param password + * @param email + * @param source_loginserver + * @param login_account_id + * @return + */ +int32 AccountManagement::CreateLoginServerAccount( + std::string username, + std::string password, + std::string email, + const std::string &source_loginserver, + uint32 login_account_id +) +{ + auto mode = server.options.GetEncryptionMode(); + auto hash = eqcrypt_hash(username, password, mode); + + LogInfo( + "Attempting to create local login account for user [{0}] encryption algorithm [{1}] ({2})", + username, + GetEncryptionByModeId(mode), + mode + ); + + unsigned int db_id = 0; + if (server.db->DoesLoginServerAccountExist(username, hash, source_loginserver, 1)) { + LogWarning( + "Attempting to create local login account for user [{0}] login [{1}] but already exists!", + username, + source_loginserver + ); + + return -1; + } + + uint32 created_account_id = 0; + if (login_account_id > 0) { + created_account_id = server.db->CreateLoginDataWithID(username, hash, source_loginserver, login_account_id); + } else { + created_account_id = server.db->CreateLoginAccount(username, hash, source_loginserver, email); + } + + if (created_account_id > 0) { + LogInfo( + "Account creation success for user [{0}] encryption algorithm [{1}] ({2}) id: [{3}]", + username, + GetEncryptionByModeId(mode), + mode, + created_account_id + ); + + return (int32) created_account_id; + } + + LogError("Failed to create local login account for user [{0}]!", username); + + return 0; +} + +/** + * @param username + * @param password + * @param email + * @return + */ +bool AccountManagement::CreateLoginserverWorldAdminAccount( + const std::string &username, + const std::string &password, + const std::string &email, + const std::string &first_name, + const std::string &last_name, + const std::string &ip_address +) +{ + auto mode = server.options.GetEncryptionMode(); + auto hash = eqcrypt_hash(username, password, mode); + + LogInfo( + "Attempting to create world admin account | username [{0}] encryption algorithm [{1}] ({2})", + username, + GetEncryptionByModeId(mode), + mode + ); + + if (server.db->DoesLoginserverWorldAdminAccountExist(username)) { + LogWarning( + "Attempting to create world admin account for user [{0}] but already exists!", + username + ); + + return false; + } + + uint32 created_world_admin_id = server.db->CreateLoginserverWorldAdminAccount( + username, + hash, + first_name, + last_name, + email, + ip_address + ); + + if (created_world_admin_id > 0) { + LogInfo( + "Account creation success for user [{0}] encryption algorithm [{1}] ({2}) new admin id [{3}]", + username, + GetEncryptionByModeId(mode), + mode, + created_world_admin_id + ); + return true; + } + + LogError("Failed to create world admin account account for user [{0}]!", username); + + return false; +} + +/** + * @param in_account_username + * @param in_account_password + * @return + */ +uint32 AccountManagement::CheckLoginserverUserCredentials( + const std::string &in_account_username, + const std::string &in_account_password, + const std::string &source_loginserver +) +{ + auto mode = server.options.GetEncryptionMode(); + + Database::DbLoginServerAccount + login_server_admin = server.db->GetLoginServerAccountByAccountName( + in_account_username, + source_loginserver + ); + + if (!login_server_admin.loaded) { + LogError( + "CheckLoginUserCredentials account [{0}] source_loginserver [{1}] not found!", + in_account_username, + source_loginserver + ); + + return false; + } + + bool validated_credentials = eqcrypt_verify_hash( + in_account_username, + in_account_password, + login_server_admin.account_password, + mode + ); + + if (!validated_credentials) { + LogError( + "CheckLoginUserCredentials account [{0}] source_loginserver [{1}] invalid credentials!", + in_account_username, + source_loginserver + ); + + return 0; + } + + LogInfo( + "CheckLoginUserCredentials account [{0}] source_loginserver [{1}] credentials validated success!", + in_account_username, + source_loginserver + ); + + return login_server_admin.id; +} + + +/** + * @param in_account_username + * @param in_account_password + * @return + */ +bool AccountManagement::UpdateLoginserverUserCredentials( + const std::string &in_account_username, + const std::string &in_account_password, + const std::string &source_loginserver +) +{ + auto mode = server.options.GetEncryptionMode(); + + Database::DbLoginServerAccount + login_server_account = server.db->GetLoginServerAccountByAccountName( + in_account_username, + source_loginserver + ); + + if (!login_server_account.loaded) { + LogError( + "ChangeLoginserverUserCredentials account [{0}] source_loginserver [{1}] not found!", + in_account_username, + source_loginserver + ); + + return false; + } + + server.db->UpdateLoginserverAccountPasswordHash( + in_account_username, + source_loginserver, + eqcrypt_hash( + in_account_username, + in_account_password, + mode + ) + ); + + LogInfo( + "ChangeLoginserverUserCredentials account [{0}] source_loginserver [{1}] credentials updated!", + in_account_username, + source_loginserver + ); + + return true; +} + +uint32 AccountManagement::CheckExternalLoginserverUserCredentials( + const std::string &in_account_username, + const std::string &in_account_password +) +{ + auto res = task_runner.Enqueue( + [&]() -> uint32 { + bool running = true; + uint32 ret = 0; + + EQ::Net::DaybreakConnectionManager mgr; + std::shared_ptr c; + + mgr.OnNewConnection( + [&](std::shared_ptr connection) { + c = connection; + } + ); + + mgr.OnConnectionStateChange( + [&]( + std::shared_ptr conn, + EQ::Net::DbProtocolStatus from, + EQ::Net::DbProtocolStatus to + ) { + if (EQ::Net::StatusConnected == to) { + EQ::Net::DynamicPacket p; + p.PutUInt16(0, 1); //OP_SessionReady + p.PutUInt32(2, 2); + c->QueuePacket(p); + } + else if (EQ::Net::StatusDisconnected == to) { + running = false; + } + } + ); + + mgr.OnPacketRecv( + [&](std::shared_ptr conn, const EQ::Net::Packet &p) { + auto opcode = p.GetUInt16(0); + switch (opcode) { + case 0x0017: //OP_ChatMessage + { + size_t buffer_len = + in_account_username.length() + in_account_password.length() + 2; + + std::unique_ptr buffer(new char[buffer_len]); + + strcpy(&buffer[0], in_account_username.c_str()); + strcpy(&buffer[in_account_username.length() + 1], in_account_password.c_str()); + + size_t encrypted_len = buffer_len; + + if (encrypted_len % 8 > 0) { + encrypted_len = ((encrypted_len / 8) + 1) * 8; + } + + EQ::Net::DynamicPacket p; + p.Resize(12 + encrypted_len); + p.PutUInt16(0, 2); //OP_Login + p.PutUInt32(2, 3); + + eqcrypt_block(&buffer[0], buffer_len, (char *) p.Data() + 12, true); + c->QueuePacket(p); + break; + } + case 0x0018: { + auto encrypt_size = p.Length() - 12; + if (encrypt_size % 8 > 0) { + encrypt_size = (encrypt_size / 8) * 8; + } + + std::unique_ptr decrypted(new char[encrypt_size]); + + eqcrypt_block((char *) p.Data() + 12, encrypt_size, &decrypted[0], false); + + EQ::Net::StaticPacket sp(&decrypted[0], encrypt_size); + auto response_error = sp.GetUInt16(1); + auto m_dbid = sp.GetUInt32(8); + + { + ret = (response_error <= 101 ? m_dbid : 0); + running = false; + } + break; + } + } + } + ); + + EQ::Net::DNSLookup( + "login.eqemulator.net", 5999, false, [&](const std::string &addr) { + if (addr.empty()) { + ret = 0; + running = false; + } + + mgr.Connect(addr, 5999); + } + ); + + auto &loop = EQ::EventLoop::Get(); + while (running) { + loop.Process(); + } + + return ret; + } + ); + + return res.get(); +} diff --git a/loginserver/account_management.h b/loginserver/account_management.h new file mode 100644 index 000000000..d88cdfdab --- /dev/null +++ b/loginserver/account_management.h @@ -0,0 +1,94 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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_ACCOUNT_MANAGEMENT_H +#define EQEMU_ACCOUNT_MANAGEMENT_H + +#include "iostream" +#include "../common/types.h" + +class AccountManagement { +public: + + /** + * @param username + * @param password + * @param email + * @param source_loginserver + * @param login_account_id + * @return + */ + static int32 CreateLoginServerAccount( + std::string username, + std::string password, + std::string email = "", + const std::string &source_loginserver = "local", + uint32 login_account_id = 0 + ); + + /** + * @param username + * @param password + * @param email + * @return + */ + static bool CreateLoginserverWorldAdminAccount( + const std::string &username, + const std::string &password, + const std::string &email, + const std::string &first_name = "", + const std::string &last_name = "", + const std::string &ip_address = "" + ); + + /** + * @param in_account_username + * @param in_account_password + * @return + */ + static uint32 CheckLoginserverUserCredentials( + const std::string &in_account_username, + const std::string &in_account_password, + const std::string &source_loginserver = "local" + ); + + /** + * @param in_account_username + * @param in_account_password + * @return + */ + static bool UpdateLoginserverUserCredentials( + const std::string &in_account_username, + const std::string &in_account_password, + const std::string &source_loginserver = "local" + ); + + /** + * @param in_account_username + * @param in_account_password + * @return + */ + static uint32 CheckExternalLoginserverUserCredentials( + const std::string &in_account_username, + const std::string &in_account_password + ); +}; + + +#endif //EQEMU_ACCOUNT_MANAGEMENT_H diff --git a/loginserver/client.cpp b/loginserver/client.cpp index 6ca13a4c1..a1adfc934 100644 --- a/loginserver/client.cpp +++ b/loginserver/client.cpp @@ -1,51 +1,55 @@ -/* EQEMu: Everquest Server Emulator -Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ -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 -*/ #include "client.h" #include "login_server.h" -#include "login_structures.h" #include "../common/misc_functions.h" #include "../common/eqemu_logsys.h" -#include "../common/eqemu_logsys_fmt.h" +#include "../common/string_util.h" +#include "encryption.h" extern LoginServer server; +/** + * @param c + * @param v + */ Client::Client(std::shared_ptr c, LSClientVersion v) { - connection = c; - version = v; - status = cs_not_sent_session_ready; - account_id = 0; - play_server_id = 0; + connection = c; + version = v; + status = cs_not_sent_session_ready; + account_id = 0; + play_server_id = 0; play_sequence_id = 0; } bool Client::Process() { EQApplicationPacket *app = connection->PopPacket(); - while (app) - { - if (server.options.IsTraceOn()) - { - Log(Logs::General, Logs::Login_Server, "Application packet received from client (size %u)", app->Size()); + while (app) { + if (server.options.IsTraceOn()) { + LogDebug("Application packet received from client (size {0})", app->Size()); } - if (server.options.IsDumpInPacketsOn()) - { + if (server.options.IsDumpInPacketsOn()) { DumpPacket(app); } @@ -55,67 +59,56 @@ bool Client::Process() continue; } - switch (app->GetOpcode()) - { - case OP_SessionReady: - { - if (server.options.IsTraceOn()) - { - Log(Logs::General, Logs::Login_Server, "Session ready received from client."); - } - Handle_SessionReady((const char*)app->pBuffer, app->Size()); - break; - } - case OP_Login: - { - if (app->Size() < 20) - { - Log(Logs::General, Logs::Error, "Login received but it is too small, discarding."); + switch (app->GetOpcode()) { + case OP_SessionReady: { + if (server.options.IsTraceOn()) { + LogInfo("Session ready received from client"); + } + Handle_SessionReady((const char *) app->pBuffer, app->Size()); break; } + case OP_Login: { + if (app->Size() < 20) { + LogError("Login received but it is too small, discarding"); + break; + } - if (server.options.IsTraceOn()) - { - Log(Logs::General, Logs::Login_Server, "Login received from client."); - } + if (server.options.IsTraceOn()) { + LogInfo("Login received from client"); + } - Handle_Login((const char*)app->pBuffer, app->Size()); - break; - } - case OP_ServerListRequest: - { - if (app->Size() < 4) { - Log(Logs::General, Logs::Error, "Server List Request received but it is too small, discarding."); + Handle_Login((const char *) app->pBuffer, app->Size()); break; } + case OP_ServerListRequest: { + if (app->Size() < 4) { + LogError("Server List Request received but it is too small, discarding"); + break; + } - if (server.options.IsTraceOn()) - { - Log(Logs::General, Logs::Login_Server, "Server list request received from client."); - } + if (server.options.IsTraceOn()) { + LogDebug("Server list request received from client"); + } - SendServerListPacket(*(uint32_t*)app->pBuffer); - break; - } - case OP_PlayEverquestRequest: - { - if (app->Size() < sizeof(PlayEverquestRequest_Struct)) - { - Log(Logs::General, Logs::Error, "Play received but it is too small, discarding."); + SendServerListPacket(*(uint32_t *) app->pBuffer); break; } + case OP_PlayEverquestRequest: { + if (app->Size() < sizeof(PlayEverquestRequest_Struct)) { + LogError("Play received but it is too small, discarding"); + break; + } - Handle_Play((const char*)app->pBuffer); - break; - } - default: - { - if (LogSys.log_settings[Logs::Client_Server_Packet_Unhandled].is_category_enabled == 1) { - char dump[64]; - app->build_header_dump(dump); - Log(Logs::General, Logs::Error, "Recieved unhandled application packet from the client: %s.", dump); + Handle_Play((const char *) app->pBuffer); + break; + } + default: { + if (LogSys.log_settings[Logs::PacketClientServerUnhandled].is_category_enabled == 1) { + char dump[64]; + app->build_header_dump(dump); + LogError("Recieved unhandled application packet from the client: [{}]", dump); + } } - } } delete app; @@ -125,51 +118,51 @@ bool Client::Process() return true; } -void Client::Handle_SessionReady(const char* data, unsigned int size) +/** + * Sends our reply to session ready packet + * + * @param data + * @param size + */ +void Client::Handle_SessionReady(const char *data, unsigned int size) { - if (status != cs_not_sent_session_ready) - { - Log(Logs::General, Logs::Error, "Session ready received again after already being received."); + if (status != cs_not_sent_session_ready) { + LogError("Session ready received again after already being received"); return; } - if (size < sizeof(unsigned int)) - { - Log(Logs::General, Logs::Error, "Session ready was too small."); + if (size < sizeof(unsigned int)) { + LogError("Session ready was too small"); return; } status = cs_waiting_for_login; /** - * The packets are mostly the same but slightly different between the two versions. - */ - if (version == cv_sod) - { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChatMessage, 17); - outapp->pBuffer[0] = 0x02; + * The packets are mostly the same but slightly different between the two versions + */ + if (version == cv_sod) { + auto *outapp = new EQApplicationPacket(OP_ChatMessage, 17); + outapp->pBuffer[0] = 0x02; outapp->pBuffer[10] = 0x01; outapp->pBuffer[11] = 0x65; - if (server.options.IsDumpOutPacketsOn()) - { + if (server.options.IsDumpOutPacketsOn()) { DumpPacket(outapp); } connection->QueuePacket(outapp); delete outapp; } - else - { - const char *msg = "ChatMessage"; - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChatMessage, 16 + strlen(msg)); - outapp->pBuffer[0] = 0x02; + else { + const char *msg = "ChatMessage"; + auto *outapp = new EQApplicationPacket(OP_ChatMessage, 16 + strlen(msg)); + outapp->pBuffer[0] = 0x02; outapp->pBuffer[10] = 0x01; outapp->pBuffer[11] = 0x65; - strcpy((char*)(outapp->pBuffer + 15), msg); + strcpy((char *) (outapp->pBuffer + 15), msg); - if (server.options.IsDumpOutPacketsOn()) - { + if (server.options.IsDumpOutPacketsOn()) { DumpPacket(outapp); } @@ -178,35 +171,52 @@ void Client::Handle_SessionReady(const char* data, unsigned int size) } } -void Client::Handle_Login(const char* data, unsigned int size) +/** + * Verifies login and send a reply + * + * @param data + * @param size + */ +void Client::Handle_Login(const char *data, unsigned int size) { - auto mode = server.options.GetEncryptionMode(); - if (status != cs_waiting_for_login) { - Log(Logs::General, Logs::Error, "Login received after already having logged in."); + LogError("Login received after already having logged in"); return; } if ((size - 12) % 8 != 0) { - Log(Logs::General, Logs::Error, "Login received packet of size: %u, this would cause a block corruption, discarding.", size); + LogError("Login received packet of size: {0}, this would cause a block corruption, discarding", size); + + return; + } + + if (size < sizeof(LoginLoginRequest_Struct)) { + LogError("Login received packet of size: {0}, this would cause a buffer overflow, discarding", size); + return; } char *login_packet_buffer = nullptr; unsigned int db_account_id = 0; + + std::string db_loginserver = "local"; + if (server.options.CanAutoLinkAccounts()) { + db_loginserver = "eqemu"; + } + std::string db_account_password_hash; std::string outbuffer; outbuffer.resize(size - 12); if (outbuffer.length() == 0) { - LogF(Logs::General, Logs::Debug, "Corrupt buffer sent to server, no length."); + LogError("Corrupt buffer sent to server, no length"); return; } auto r = eqcrypt_block(data + 10, size - 12, &outbuffer[0], 0); if (r == nullptr) { - LogF(Logs::General, Logs::Debug, "Failed to decrypt eqcrypt block"); + LogError("Failed to decrypt eqcrypt block"); return; } @@ -214,159 +224,119 @@ void Client::Handle_Login(const char* data, unsigned int size) std::string user(&outbuffer[0]); if (user.length() >= outbuffer.length()) { - LogF(Logs::General, Logs::Debug, "Corrupt buffer sent to server, preventing buffer overflow."); + LogError("Corrupt buffer sent to server, preventing buffer overflow"); return; } + memcpy(&llrs, data, sizeof(LoginLoginRequest_Struct)); + bool result = false; if (outbuffer[0] == 0 && outbuffer[1] == 0) { if (server.options.IsTokenLoginAllowed()) { - cred = (&outbuffer[2 + user.length()]); - result = server.db->GetLoginTokenDataFromToken(cred, connection->GetRemoteAddr(), db_account_id, user); + cred = (&outbuffer[2 + user.length()]); + result = server.db->GetLoginTokenDataFromToken( + cred, + connection->GetRemoteAddr(), + db_account_id, + db_loginserver, + user + ); } } else { if (server.options.IsPasswordLoginAllowed()) { - cred = (&outbuffer[1 + user.length()]); - if (server.db->GetLoginDataFromAccountName(user, db_account_password_hash, db_account_id) == false) { - /* If we have auto_create_accounts enabled in the login.ini, we will process the creation of an account on our own*/ - if ( - server.options.CanAutoCreateAccounts() && - server.db->CreateLoginData(user, eqcrypt_hash(user, cred, mode), db_account_id) == true - ) { - LogF(Logs::General, Logs::Error, "User {0} does not exist in the database, so we created it...", user); - result = true; - } - else { - LogF(Logs::General, Logs::Error, "Error logging in, user {0} does not exist in the database.", user); - result = false; - } + cred = (&outbuffer[1 + user.length()]); + auto components = SplitString(user, ':'); + if (components.size() == 2) { + db_loginserver = components[0]; + user = components[1]; + } + + LogInfo( + "Attempting password based login [{0}] login [{1}] user [{2}]", + user, + db_loginserver, + user + ); + + ParseAccountString(user, user, db_loginserver); + + if (server.db->GetLoginDataFromAccountInfo(user, db_loginserver, db_account_password_hash, db_account_id)) { + result = VerifyLoginHash(user, db_loginserver, cred, db_account_password_hash); + + LogDebug("[VerifyLoginHash] Success [{0}]", (result ? "true" : "false")); } else { - if (eqcrypt_verify_hash(user, cred, db_account_password_hash, mode)) { - result = true; - } - else { - result = false; - } + status = cs_creating_account; + AttemptLoginAccountCreation(user, cred, db_loginserver); + + return; } } } - /* Login Accepted */ + /** + * Login accepted + */ if (result) { + LogInfo( + "login [{0}] user [{1}] Login succeeded", + db_loginserver, + user + ); - server.client_manager->RemoveExistingClient(db_account_id); - - in_addr in; - in.s_addr = connection->GetRemoteIP(); - - server.db->UpdateLSAccountData(db_account_id, std::string(inet_ntoa(in))); - GenerateKey(); - - account_id = db_account_id; - account_name = user; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LoginAccepted, 10 + 80); - const LoginLoginRequest_Struct* llrs = (const LoginLoginRequest_Struct *)data; - LoginAccepted_Struct* login_accepted = (LoginAccepted_Struct *)outapp->pBuffer; - login_accepted->unknown1 = llrs->unknown1; - login_accepted->unknown2 = llrs->unknown2; - login_accepted->unknown3 = llrs->unknown3; - login_accepted->unknown4 = llrs->unknown4; - login_accepted->unknown5 = llrs->unknown5; - - LoginFailedAttempts_Struct * login_failed_attempts = new LoginFailedAttempts_Struct; - memset(login_failed_attempts, 0, sizeof(LoginFailedAttempts_Struct)); - - login_failed_attempts->failed_attempts = 0; - login_failed_attempts->message = 0x01; - login_failed_attempts->lsid = db_account_id; - login_failed_attempts->unknown3[3] = 0x03; - login_failed_attempts->unknown4[3] = 0x02; - login_failed_attempts->unknown5[0] = 0xe7; - login_failed_attempts->unknown5[1] = 0x03; - login_failed_attempts->unknown6[0] = 0xff; - login_failed_attempts->unknown6[1] = 0xff; - login_failed_attempts->unknown6[2] = 0xff; - login_failed_attempts->unknown6[3] = 0xff; - login_failed_attempts->unknown7[0] = 0xa0; - login_failed_attempts->unknown7[1] = 0x05; - login_failed_attempts->unknown8[3] = 0x02; - login_failed_attempts->unknown9[0] = 0xff; - login_failed_attempts->unknown9[1] = 0x03; - login_failed_attempts->unknown11[0] = 0x63; - login_failed_attempts->unknown12[0] = 0x01; - memcpy(login_failed_attempts->key, key.c_str(), key.size()); - - char encrypted_buffer[80] = { 0 }; - auto rc = eqcrypt_block((const char*)login_failed_attempts, 75, encrypted_buffer, 1); - if (rc == nullptr) { - LogF(Logs::General, Logs::Debug, "Failed to encrypt eqcrypt block"); - } - - memcpy(login_accepted->encrypt, encrypted_buffer, 80); - - if (server.options.IsDumpOutPacketsOn()) { - DumpPacket(outapp); - } - - connection->QueuePacket(outapp); - delete outapp; - - status = cs_logged_in; + DoSuccessfulLogin(user, db_account_id, db_loginserver); } else { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LoginAccepted, sizeof(LoginLoginFailed_Struct)); - const LoginLoginRequest_Struct* llrs = (const LoginLoginRequest_Struct *)data; - LoginLoginFailed_Struct* llas = (LoginLoginFailed_Struct *)outapp->pBuffer; - llas->unknown1 = llrs->unknown1; - llas->unknown2 = llrs->unknown2; - llas->unknown3 = llrs->unknown3; - llas->unknown4 = llrs->unknown4; - llas->unknown5 = llrs->unknown5; - memcpy(llas->unknown6, FailedLoginResponseData, sizeof(FailedLoginResponseData)); + LogInfo( + "login [{0}] user [{1}] Login failed", + db_loginserver, + user + ); - if (server.options.IsDumpOutPacketsOn()) { - DumpPacket(outapp); - } - - connection->QueuePacket(outapp); - delete outapp; - - status = cs_failed_to_login; + DoFailedLogin(); } } -void Client::Handle_Play(const char* data) +/** + * Sends a packet to the requested server to see if the client is allowed or not + * + * @param data + */ +void Client::Handle_Play(const char *data) { - if (status != cs_logged_in) - { - Log(Logs::General, Logs::Error, "Client sent a play request when they were not logged in, discarding."); + if (status != cs_logged_in) { + LogError("Client sent a play request when they were not logged in, discarding"); return; } - const PlayEverquestRequest_Struct *play = (const PlayEverquestRequest_Struct*)data; - unsigned int server_id_in = (unsigned int)play->ServerNumber; - unsigned int sequence_in = (unsigned int)play->Sequence; + const auto *play = (const PlayEverquestRequest_Struct *) data; + auto server_id_in = (unsigned int) play->ServerNumber; + auto sequence_in = (unsigned int) play->Sequence; - if (server.options.IsTraceOn()) - { - Log(Logs::General, Logs::Login_Server, "Play received from client, server number %u sequence %u.", server_id_in, sequence_in); + if (server.options.IsTraceOn()) { + LogInfo( + "Play received from client [{0}] server number {1} sequence {2}", + GetAccountName(), + server_id_in, + sequence_in + ); } - this->play_server_id = (unsigned int)play->ServerNumber; + this->play_server_id = (unsigned int) play->ServerNumber; play_sequence_id = sequence_in; - play_server_id = server_id_in; - server.server_manager->SendUserToWorldRequest(server_id_in, account_id); + play_server_id = server_id_in; + server.server_manager->SendUserToWorldRequest(server_id_in, account_id, loginserver_name); } +/** + * @param seq + */ void Client::SendServerListPacket(uint32 seq) { EQApplicationPacket *outapp = server.server_manager->CreateServerListPacket(this, seq); - if (server.options.IsDumpOutPacketsOn()) - { + if (server.options.IsDumpOutPacketsOn()) { DumpPacket(outapp); } @@ -376,9 +346,8 @@ void Client::SendServerListPacket(uint32 seq) void Client::SendPlayResponse(EQApplicationPacket *outapp) { - if (server.options.IsTraceOn()) - { - Log(Logs::General, Logs::Netcode, "Sending play response for %s.", GetAccountName().c_str()); + if (server.options.IsTraceOn()) { + LogDebug("Sending play response for {0}", GetAccountName()); // server_log->LogPacket(log_network_trace, (const char*)outapp->pBuffer, outapp->size); } connection->QueuePacket(outapp); @@ -388,18 +357,487 @@ void Client::GenerateKey() { key.clear(); int count = 0; - while (count < 10) - { + while (count < 10) { static const char key_selection[] = - { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', '0', '1', '2', '3', '4', '5', - '6', '7', '8', '9' - }; + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9' + }; - key.append((const char*)&key_selection[random.Int(0, 35)], 1); + key.append((const char *) &key_selection[random.Int(0, 35)], 1); count++; } } + +/** + * @param user + * @param pass + * @param loginserver + */ +void Client::AttemptLoginAccountCreation( + const std::string &user, + const std::string &pass, + const std::string &loginserver +) +{ +#ifdef LSPX + if (loginserver == "eqemu") { + LogInfo("Attempting login account creation via '{0}'", loginserver); + + if (!server.options.CanAutoLinkAccounts()) { + LogInfo("CanAutoLinkAccounts disabled - sending failed login"); + DoFailedLogin(); + return; + } + + if (server.options.GetEQEmuLoginServerAddress().length() == 0) { + DoFailedLogin(); + return; + } + + auto addr_components = SplitString(server.options.GetEQEmuLoginServerAddress(), ':'); + if (addr_components.size() != 2) { + DoFailedLogin(); + return; + } + + stored_user = user; + stored_pass = pass; + + auto address = addr_components[0]; + auto port = std::stoi(addr_components[1]); + EQ::Net::DNSLookup( + address, port, false, [=](const std::string &addr) { + if (addr.empty()) { + DoFailedLogin(); + return; + } + + login_connection_manager.reset(new EQ::Net::DaybreakConnectionManager()); + login_connection_manager->OnNewConnection( + std::bind( + &Client::LoginOnNewConnection, + this, + std::placeholders::_1 + ) + ); + login_connection_manager->OnConnectionStateChange( + std::bind( + &Client::LoginOnStatusChange, + this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3 + ) + ); + login_connection_manager->OnPacketRecv( + std::bind( + &Client::LoginOnPacketRecv, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + + login_connection_manager->Connect(addr, port); + } + ); + + return; + } +#endif + + if (server.options.CanAutoCreateAccounts() && loginserver == "local") { + LogInfo("CanAutoCreateAccounts enabled, attempting to creating account [{0}]", user); + CreateLocalAccount(user, pass); + return; + } + + DoFailedLogin(); +} + +void Client::DoFailedLogin() +{ + stored_user.clear(); + stored_pass.clear(); + + EQApplicationPacket outapp(OP_LoginAccepted, sizeof(LoginLoginFailed_Struct)); + auto *login_failed = (LoginLoginFailed_Struct *) outapp.pBuffer; + + login_failed->unknown1 = llrs.unknown1; + login_failed->unknown2 = llrs.unknown2; + login_failed->unknown3 = llrs.unknown3; + login_failed->unknown4 = llrs.unknown4; + login_failed->unknown5 = llrs.unknown5; + + memcpy(login_failed->unknown6, FailedLoginResponseData, sizeof(FailedLoginResponseData)); + + if (server.options.IsDumpOutPacketsOn()) { + DumpPacket(&outapp); + } + + connection->QueuePacket(&outapp); + status = cs_failed_to_login; +} + +/** + * Verifies a login hash, will also attempt to update a login hash if needed + * + * @param account_username + * @param source_loginserver + * @param account_password + * @param password_hash + * @return + */ +bool Client::VerifyLoginHash( + const std::string &account_username, + const std::string &source_loginserver, + const std::string &account_password, + const std::string &password_hash +) +{ + auto mode = server.options.GetEncryptionMode(); + if (eqcrypt_verify_hash(account_username, account_password, password_hash, mode)) { + return true; + } + else { + if (server.options.IsUpdatingInsecurePasswords()) { + if (mode < EncryptionModeArgon2) { + mode = EncryptionModeArgon2; + } + + if (password_hash.length() == 32) { //md5 is insecure + for (int i = EncryptionModeMD5; i <= EncryptionModeMD5Triple; ++i) { + if (i != mode && eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { + LogDebug( + "user [{0}] loginserver [{1}] mode [{2}]", + account_username, + source_loginserver, + mode + ); + server.db->UpdateLoginserverAccountPasswordHash( + account_username, + source_loginserver, + eqcrypt_hash( + account_username, + account_password, + mode + )); + return true; + } + } + } + else if (password_hash.length() == 40) { //sha1 is insecure + for (int i = EncryptionModeSHA; i <= EncryptionModeSHATriple; ++i) { + if (i != mode && eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { + LogDebug( + "user [{0}] loginserver [{1}] mode [{2}]", + account_username, + source_loginserver, + mode + ); + + server.db->UpdateLoginserverAccountPasswordHash( + account_username, + source_loginserver, + eqcrypt_hash( + account_username, + account_password, + mode + )); + return true; + } + } + } + else if (password_hash.length() == 128) { //sha2-512 is insecure + for (int i = EncryptionModeSHA512; i <= EncryptionModeSHA512Triple; ++i) { + if (i != mode && eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { + LogDebug( + "user [{0}] loginserver [{1}] mode [{2}]", + account_username, + source_loginserver, + mode + ); + + server.db->UpdateLoginserverAccountPasswordHash( + account_username, + source_loginserver, + eqcrypt_hash( + account_username, + account_password, + mode + )); + return true; + } + } + } + //argon2 is still secure + //scrypt is still secure + } + } + + return false; +} + +/** + * @param in_account_name + * @param db_account_id + * @param db_loginserver + */ +void Client::DoSuccessfulLogin( + const std::string in_account_name, + int db_account_id, + const std::string &db_loginserver +) +{ + stored_user.clear(); + stored_pass.clear(); + + server.client_manager->RemoveExistingClient(db_account_id, db_loginserver); + + in_addr in{}; + in.s_addr = connection->GetRemoteIP(); + + server.db->UpdateLSAccountData(db_account_id, std::string(inet_ntoa(in))); + GenerateKey(); + + account_id = db_account_id; + account_name = in_account_name; + loginserver_name = db_loginserver; + + auto *outapp = new EQApplicationPacket(OP_LoginAccepted, 10 + 80); + auto *login_accepted = (LoginAccepted_Struct *) outapp->pBuffer; + login_accepted->unknown1 = llrs.unknown1; + login_accepted->unknown2 = llrs.unknown2; + login_accepted->unknown3 = llrs.unknown3; + login_accepted->unknown4 = llrs.unknown4; + login_accepted->unknown5 = llrs.unknown5; + + auto *login_failed_attempts = new LoginFailedAttempts_Struct; + memset(login_failed_attempts, 0, sizeof(LoginFailedAttempts_Struct)); + + login_failed_attempts->failed_attempts = 0; + login_failed_attempts->message = 0x01; + login_failed_attempts->lsid = db_account_id; + login_failed_attempts->unknown3[3] = 0x03; + login_failed_attempts->unknown4[3] = 0x02; + login_failed_attempts->unknown5[0] = 0xe7; + login_failed_attempts->unknown5[1] = 0x03; + login_failed_attempts->unknown6[0] = 0xff; + login_failed_attempts->unknown6[1] = 0xff; + login_failed_attempts->unknown6[2] = 0xff; + login_failed_attempts->unknown6[3] = 0xff; + login_failed_attempts->unknown7[0] = 0xa0; + login_failed_attempts->unknown7[1] = 0x05; + login_failed_attempts->unknown8[3] = 0x02; + login_failed_attempts->unknown9[0] = 0xff; + login_failed_attempts->unknown9[1] = 0x03; + login_failed_attempts->unknown11[0] = 0x63; + login_failed_attempts->unknown12[0] = 0x01; + memcpy(login_failed_attempts->key, key.c_str(), key.size()); + + char encrypted_buffer[80] = {0}; + auto rc = eqcrypt_block((const char *) login_failed_attempts, 75, encrypted_buffer, 1); + if (rc == nullptr) { + LogDebug("Failed to encrypt eqcrypt block"); + } + + memcpy(login_accepted->encrypt, encrypted_buffer, 80); + + if (server.options.IsDumpOutPacketsOn()) { + DumpPacket(outapp); + } + + connection->QueuePacket(outapp); + delete outapp; + + status = cs_logged_in; +} + +/** + * @param username + * @param password + */ +void Client::CreateLocalAccount(const std::string &username, const std::string &password) +{ + auto mode = server.options.GetEncryptionMode(); + auto hash = eqcrypt_hash(username, password, mode); + unsigned int db_id = 0; + if (!server.db->CreateLoginData(username, hash, "local", db_id)) { + DoFailedLogin(); + } + else { + DoSuccessfulLogin(username, db_id, "local"); + } +} + +/** + * @param in_account_name + * @param in_account_password + * @param loginserver_account_id + */ +void Client::CreateEQEmuAccount( + const std::string &in_account_name, + const std::string &in_account_password, + unsigned int loginserver_account_id +) +{ + auto mode = server.options.GetEncryptionMode(); + auto hash = eqcrypt_hash(in_account_name, in_account_password, mode); + + if (server.db->DoesLoginServerAccountExist(in_account_name, hash, "eqemu", loginserver_account_id)) { + DoSuccessfulLogin(in_account_name, loginserver_account_id, "eqemu"); + return; + } + + if (!server.db->CreateLoginDataWithID(in_account_name, hash, "eqemu", loginserver_account_id)) { + DoFailedLogin(); + } + else { + DoSuccessfulLogin(in_account_name, loginserver_account_id, "eqemu"); + } +} + +/** + * @param connection + */ +void Client::LoginOnNewConnection(std::shared_ptr connection) +{ + login_connection = connection; +} + +/** + * @param conn + * @param from + * @param to + */ +void Client::LoginOnStatusChange( + std::shared_ptr conn, + EQ::Net::DbProtocolStatus from, + EQ::Net::DbProtocolStatus to +) +{ + if (to == EQ::Net::StatusConnected) { + LogDebug("EQ::Net::StatusConnected"); + LoginSendSessionReady(); + } + + if (to == EQ::Net::StatusDisconnecting || to == EQ::Net::StatusDisconnected) { + LogDebug("EQ::Net::StatusDisconnecting || EQ::Net::StatusDisconnected"); + + DoFailedLogin(); + } +} + +/** + * @param conn + * @param from + * @param to + */ +void Client::LoginOnStatusChangeIgnored( + std::shared_ptr conn, + EQ::Net::DbProtocolStatus from, + EQ::Net::DbProtocolStatus to +) +{ +} + +/** + * @param conn + * @param p + */ +void Client::LoginOnPacketRecv(std::shared_ptr conn, const EQ::Net::Packet &p) +{ + auto opcode = p.GetUInt16(0); + switch (opcode) { + case 0x0017: //OP_ChatMessage + LoginSendLogin(); + break; + case 0x0018: + LoginProcessLoginResponse(p); + break; + } +} + +void Client::LoginSendSessionReady() +{ + EQ::Net::DynamicPacket p; + p.PutUInt16(0, 1); //OP_SessionReady + p.PutUInt32(2, 2); + + login_connection->QueuePacket(p); +} + +void Client::LoginSendLogin() +{ + size_t buffer_len = stored_user.length() + stored_pass.length() + 2; + std::unique_ptr buffer(new char[buffer_len]); + + strcpy(&buffer[0], stored_user.c_str()); + strcpy(&buffer[stored_user.length() + 1], stored_pass.c_str()); + + size_t encrypted_len = buffer_len; + + if (encrypted_len % 8 > 0) { + encrypted_len = ((encrypted_len / 8) + 1) * 8; + } + + EQ::Net::DynamicPacket p; + p.Resize(12 + encrypted_len); + p.PutUInt16(0, 2); //OP_Login + p.PutUInt32(2, 3); + + eqcrypt_block(&buffer[0], buffer_len, (char *) p.Data() + 12, true); + + login_connection->QueuePacket(p); +} + +/** + * @param p + */ +void Client::LoginProcessLoginResponse(const EQ::Net::Packet &p) +{ + auto encrypt_size = p.Length() - 12; + + if (encrypt_size % 8 > 0) { + encrypt_size = (encrypt_size / 8) * 8; + } + + std::unique_ptr decrypted(new char[encrypt_size]); + + eqcrypt_block((char *) p.Data() + 12, encrypt_size, &decrypted[0], false); + + EQ::Net::StaticPacket sp(&decrypted[0], encrypt_size); + auto response_error = sp.GetUInt16(1); + + login_connection_manager->OnConnectionStateChange( + std::bind( + &Client::LoginOnStatusChangeIgnored, + this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3 + ) + ); + + if (response_error > 101) { + LogDebug("response [{0}] failed login", response_error); + DoFailedLogin(); + login_connection->Close(); + } + else { + LogDebug( + "response [{0}] login succeeded user [{1}]", + response_error, + stored_user + ); + + auto m_dbid = sp.GetUInt32(8); + + CreateEQEmuAccount(stored_user, stored_pass, m_dbid); + login_connection->Close(); + } +} diff --git a/loginserver/client.h b/loginserver/client.h index b9bfe0494..b8f092db3 100644 --- a/loginserver/client.h +++ b/loginserver/client.h @@ -1,137 +1,233 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ - 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_CLIENT_H #define EQEMU_CLIENT_H #include "../common/global_define.h" #include "../common/opcodemgr.h" #include "../common/random.h" - -#include #include "../common/eq_stream_intf.h" +#include "../common/net/dns.h" +#include "../common/net/daybreak_connection.h" +#include "login_structures.h" +#include -enum LSClientVersion -{ +enum LSClientVersion { cv_titanium, cv_sod }; -enum LSClientStatus -{ +enum LSClientStatus { cs_not_sent_session_ready, cs_waiting_for_login, + cs_creating_account, cs_failed_to_login, cs_logged_in }; /** -* Client class, controls a single client and it's -* connection to the login server. -*/ -class Client -{ + * Client class, controls a single client and it's connection to the login server + */ +class Client { public: + /** - * Constructor, sets our connection to c and version to v - */ + * Constructor, sets our connection to c and version to v + * + * @param c + * @param v + */ Client(std::shared_ptr c, LSClientVersion v); /** - * Destructor. - */ - ~Client() { } + * Destructor + */ + ~Client() {} /** - * Processes the client's connection and does various actions. - */ + * Processes the client's connection and does various actions + * + * @return + */ bool Process(); /** - * Sends our reply to session ready packet. - */ - void Handle_SessionReady(const char* data, unsigned int size); + * Sends our reply to session ready packet + * + * @param data + * @param size + */ + void Handle_SessionReady(const char *data, unsigned int size); /** - * Verifies login and send a reply. - */ - void Handle_Login(const char* data, unsigned int size); + * Verifies login and send a reply + * + * @param data + * @param size + */ + void Handle_Login(const char *data, unsigned int size); /** - * Sends a packet to the requested server to see if the client is allowed or not. - */ - void Handle_Play(const char* data); + * Sends a packet to the requested server to see if the client is allowed or not + * + * @param data + */ + void Handle_Play(const char *data); /** - * Sends a server list packet to the client. - */ + * Sends a server list packet to the client + * + * @param seq + */ void SendServerListPacket(uint32 seq); /** - * Sends the input packet to the client and clears our play response states. - */ + * Sends the input packet to the client and clears our play response states + * + * @param outapp + */ void SendPlayResponse(EQApplicationPacket *outapp); /** - * Generates a random login key for the client during login. - */ + * Generates a random login key for the client during login + */ void GenerateKey(); /** - * Gets the account id of this client. - */ + * Gets the account id of this client + * + * @return + */ unsigned int GetAccountID() const { return account_id; } /** - * Gets the account name of this client. - */ + * Gets the loginserver name of this client + * + * @return + */ + std::string GetLoginServerName() const { return loginserver_name; } + + /** + * Gets the account name of this client + * + * @return + */ std::string GetAccountName() const { return account_name; } /** - * Gets the key generated at login for this client. - */ + * Gets the key generated at login for this client + * + * @return + */ std::string GetKey() const { return key; } /** - * Gets the server selected to be played on for this client. - */ + * Gets the server selected to be played on for this client + * + * @return + */ unsigned int GetPlayServerID() const { return play_server_id; } /** - * Gets the play sequence state for this client. - */ + * Gets the play sequence state for this client + * + * @return + */ unsigned int GetPlaySequence() const { return play_sequence_id; } /** - * Gets the connection for this client. - */ + * Gets the connection for this client + * + * @return + */ std::shared_ptr GetConnection() { return connection; } - EQEmu::Random random; -private: - std::shared_ptr connection; - LSClientVersion version; - LSClientStatus status; + /** + * Attempts to create a login account + * + * @param user + * @param pass + * @param loginserver + */ + void AttemptLoginAccountCreation(const std::string &user, const std::string &pass, const std::string &loginserver); - std::string account_name; + /** + * Does a failed login + */ + void DoFailedLogin(); + + /** + * Verifies a login hash, will also attempt to update a login hash if needed + * + * @param account_username + * @param source_loginserver + * @param account_password + * @param password_hash + * @return + */ + bool VerifyLoginHash( + const std::string &account_username, + const std::string &source_loginserver, + const std::string &account_password, + const std::string &password_hash + ); + + void DoSuccessfulLogin(const std::string in_account_name, int db_account_id, const std::string &db_loginserver); + void CreateLocalAccount(const std::string &username, const std::string &password); + void CreateEQEmuAccount(const std::string &in_account_name, const std::string &in_account_password, unsigned int loginserver_account_id); + +private: + EQEmu::Random random; + std::shared_ptr connection; + LSClientVersion version; + LSClientStatus status; + + std::string account_name; unsigned int account_id; + std::string loginserver_name; unsigned int play_server_id; unsigned int play_sequence_id; - std::string key; + std::string key; + + std::unique_ptr login_connection_manager; + std::shared_ptr login_connection; + LoginLoginRequest_Struct llrs; + + std::string stored_user; + std::string stored_pass; + void LoginOnNewConnection(std::shared_ptr connection); + void LoginOnStatusChange( + std::shared_ptr conn, + EQ::Net::DbProtocolStatus from, + EQ::Net::DbProtocolStatus to + ); + void LoginOnStatusChangeIgnored( + std::shared_ptr conn, + EQ::Net::DbProtocolStatus from, + EQ::Net::DbProtocolStatus to + ); + void LoginOnPacketRecv(std::shared_ptr conn, const EQ::Net::Packet &p); + void LoginSendSessionReady(); + void LoginSendLogin(); + void LoginProcessLoginResponse(const EQ::Net::Packet &p); }; #endif diff --git a/loginserver/client_manager.cpp b/loginserver/client_manager.cpp index 4117430a7..d37ec545f 100644 --- a/loginserver/client_manager.cpp +++ b/loginserver/client_manager.cpp @@ -1,87 +1,119 @@ -/* EQEMu: Everquest Server Emulator -Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ -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 -*/ #include "client_manager.h" #include "login_server.h" extern LoginServer server; -extern bool run_server; +extern bool run_server; #include "../common/eqemu_logsys.h" -#include "../common/eqemu_logsys_fmt.h" ClientManager::ClientManager() { - int titanium_port = atoi(server.config->GetVariable("Titanium", "port").c_str()); + int titanium_port = server.config.GetVariableInt("client_configuration", "titanium_port", 5998); + EQStreamManagerInterfaceOptions titanium_opts(titanium_port, false, false); + titanium_stream = new EQ::Net::EQStreamManager(titanium_opts); - titanium_ops = new RegularOpcodeManager; - if (!titanium_ops->LoadOpcodes(server.config->GetVariable("Titanium", "opcodes").c_str())) - { - Log(Logs::General, Logs::Error, "ClientManager fatal error: couldn't load opcodes for Titanium file %s.", - server.config->GetVariable("Titanium", "opcodes").c_str()); + titanium_ops = new RegularOpcodeManager; + if (!titanium_ops->LoadOpcodes( + server.config.GetVariableString( + "client_configuration", + "titanium_opcodes", + "login_opcodes.conf" + ).c_str())) { + + LogError( + "ClientManager fatal error: couldn't load opcodes for Titanium file [{0}]", + server.config.GetVariableString("client_configuration", "titanium_opcodes", "login_opcodes.conf") + ); + run_server = false; } - titanium_stream->OnNewConnection([this](std::shared_ptr stream) { - LogF(Logs::General, Logs::Login_Server, "New Titanium client connection from {0}:{1}", stream->GetRemoteIP(), stream->GetRemotePort()); - stream->SetOpcodeManager(&titanium_ops); - Client *c = new Client(stream, cv_titanium); - clients.push_back(c); - }); + titanium_stream->OnNewConnection( + [this](std::shared_ptr stream) { + LogInfo( + "New Titanium client connection from {0}:{1}", + stream->GetRemoteIP(), + stream->GetRemotePort() + ); + + stream->SetOpcodeManager(&titanium_ops); + Client *c = new Client(stream, cv_titanium); + clients.push_back(c); + } + ); + + int sod_port = server.config.GetVariableInt("client_configuration", "sod_port", 5999); - int sod_port = atoi(server.config->GetVariable("SoD", "port").c_str()); EQStreamManagerInterfaceOptions sod_opts(sod_port, false, false); sod_stream = new EQ::Net::EQStreamManager(sod_opts); - sod_ops = new RegularOpcodeManager; - if (!sod_ops->LoadOpcodes(server.config->GetVariable("SoD", "opcodes").c_str())) - { - Log(Logs::General, Logs::Error, "ClientManager fatal error: couldn't load opcodes for SoD file %s.", - server.config->GetVariable("SoD", "opcodes").c_str()); + sod_ops = new RegularOpcodeManager; + if ( + !sod_ops->LoadOpcodes( + server.config.GetVariableString( + "client_configuration", + "sod_opcodes", + "login_opcodes.conf" + ).c_str() + )) { + LogError( + "ClientManager fatal error: couldn't load opcodes for SoD file {0}", + server.config.GetVariableString("client_configuration", "sod_opcodes", "login_opcodes.conf").c_str() + ); + run_server = false; } - sod_stream->OnNewConnection([this](std::shared_ptr stream) { - LogF(Logs::General, Logs::Login_Server, "New SoD client connection from {0}:{1}", stream->GetRemoteIP(), stream->GetRemotePort()); - stream->SetOpcodeManager(&sod_ops); - Client *c = new Client(stream, cv_sod); - clients.push_back(c); - }); + sod_stream->OnNewConnection( + [this](std::shared_ptr stream) { + LogInfo( + "New SoD client connection from {0}:{1}", + stream->GetRemoteIP(), + stream->GetRemotePort() + ); + + stream->SetOpcodeManager(&sod_ops); + Client *c = new Client(stream, cv_sod); + clients.push_back(c); + } + ); } ClientManager::~ClientManager() { - if (titanium_stream) - { + if (titanium_stream) { delete titanium_stream; } - if (titanium_ops) - { + if (titanium_ops) { delete titanium_ops; } - if (sod_stream) - { + if (sod_stream) { delete sod_stream; } - if (sod_ops) - { + if (sod_ops) { delete sod_ops; } } @@ -91,16 +123,13 @@ void ClientManager::Process() ProcessDisconnect(); auto iter = clients.begin(); - while (iter != clients.end()) - { - if ((*iter)->Process() == false) - { - Log(Logs::General, Logs::Debug, "Client had a fatal error and had to be removed from the login."); + while (iter != clients.end()) { + if ((*iter)->Process() == false) { + LogWarning("Client had a fatal error and had to be removed from the login"); delete (*iter); iter = clients.erase(iter); } - else - { + else { ++iter; } } @@ -109,58 +138,52 @@ void ClientManager::Process() void ClientManager::ProcessDisconnect() { auto iter = clients.begin(); - while (iter != clients.end()) - { + while (iter != clients.end()) { std::shared_ptr c = (*iter)->GetConnection(); - if (c->CheckState(CLOSED)) - { - Log(Logs::General, Logs::Login_Server, "Client disconnected from the server, removing client."); + if (c->CheckState(CLOSED)) { + LogInfo("Client disconnected from the server, removing client"); delete (*iter); iter = clients.erase(iter); } - else - { + else { ++iter; } } } -void ClientManager::RemoveExistingClient(unsigned int account_id) +/** + * @param account_id + * @param loginserver + */ +void ClientManager::RemoveExistingClient(unsigned int account_id, const std::string &loginserver) { auto iter = clients.begin(); - while (iter != clients.end()) - { - if ((*iter)->GetAccountID() == account_id) - { - Log(Logs::General, Logs::Login_Server, "Client attempting to log in and existing client already logged in, removing existing client."); + while (iter != clients.end()) { + if ((*iter)->GetAccountID() == account_id && (*iter)->GetLoginServerName().compare(loginserver) == 0) { + LogInfo("Client attempting to log in existing client already logged in, removing existing client"); delete (*iter); iter = clients.erase(iter); } - else - { + else { ++iter; } } } -Client *ClientManager::GetClient(unsigned int account_id) +/** + * @param account_id + * @param loginserver + * @return + */ +Client *ClientManager::GetClient(unsigned int account_id, const std::string &loginserver) { - Client *cur = nullptr; - int count = 0; auto iter = clients.begin(); - while (iter != clients.end()) - { - if ((*iter)->GetAccountID() == account_id) - { - cur = (*iter); - count++; + while (iter != clients.end()) { + if ((*iter)->GetAccountID() == account_id && (*iter)->GetLoginServerName().compare(loginserver) == 0) { + return (*iter); } ++iter; } - if (count > 1) - { - Log(Logs::General, Logs::Error, "More than one client with a given account_id existed in the client list."); - } - return cur; + return nullptr; } diff --git a/loginserver/client_manager.h b/loginserver/client_manager.h index 7e05737a2..4de290210 100644 --- a/loginserver/client_manager.h +++ b/loginserver/client_manager.h @@ -1,20 +1,23 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ - 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_CLIENTMANAGER_H #define EQEMU_CLIENTMANAGER_H @@ -27,44 +30,50 @@ /** * Client manager class, holds all the client objects and does basic processing. */ -class ClientManager -{ +class ClientManager { public: /** - * Constructor, sets up the stream factories and opcode managers. - */ + * Constructor: sets up the stream factories and opcode managers + */ ClientManager(); /** - * Destructor, shuts down the streams and opcode managers. - */ + * Destructor: shuts down the streams and opcode managers + */ ~ClientManager(); /** - * Processes every client in the internal list, removes them if necessary. - */ + * Processes every client in the internal list, removes them if necessary. + */ void Process(); /** - * Removes a client with a certain account id. - */ - void RemoveExistingClient(unsigned int account_id); + * Removes a client with a certain account id + * + * @param account_id + * @param loginserver + */ + void RemoveExistingClient(unsigned int account_id, const std::string &loginserver); /** - * Gets a client (if exists) by their account id. - */ - Client *GetClient(unsigned int account_id); + * Gets a client (if exists) by their account id + * + * @param account_id + * @param loginserver + * @return + */ + Client *GetClient(unsigned int account_id, const std::string &loginserver); private: /** - * Processes disconnected clients, removes them if necessary. - */ + * Processes disconnected clients, removes them if necessary + */ void ProcessDisconnect(); - std::list clients; - OpcodeManager *titanium_ops; + std::list clients; + OpcodeManager *titanium_ops; EQ::Net::EQStreamManager *titanium_stream; - OpcodeManager *sod_ops; + OpcodeManager *sod_ops; EQ::Net::EQStreamManager *sod_stream; }; diff --git a/loginserver/config.cpp b/loginserver/config.cpp deleted file mode 100644 index 72d44efa0..000000000 --- a/loginserver/config.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) - - 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 -*/ -#include "../common/global_define.h" -#include "../common/eqemu_logsys.h" -#include "config.h" - -/** -* Retrieves the variable we want from our title or theme -* First gets the map from the title -* Then gets the argument from the map we got from title -*/ -std::string Config::GetVariable(std::string title, std::string parameter) -{ - std::map >::iterator iter = vars.find(title); - if(iter != vars.end()) - { - std::map::iterator arg_iter = iter->second.find(parameter); - if(arg_iter != iter->second.end()) - { - return arg_iter->second; - } - } - - return std::string(""); -} - -/** -* Opens a file and passes it to the tokenizer -* Then it parses the tokens returned and puts them into titles and variables. -*/ -void Config::Parse(const char *file_name) -{ - if(file_name == nullptr) - { - Log(Logs::General, Logs::Error, "Config::Parse(), file_name passed was null."); - return; - } - - vars.clear(); - FILE *input = fopen(file_name, "r"); - if(input) - { - std::list tokens; - Tokenize(input, tokens); - - char mode = 0; - std::string title, param, arg; - std::list::iterator iter = tokens.begin(); - while(iter != tokens.end()) - { - if((*iter).compare("[") == 0) - { - title.clear(); - bool first = true; - ++iter; - if(iter == tokens.end()) - { - Log(Logs::General, Logs::Error, "Config::Parse(), EOF before title done parsing."); - fclose(input); - vars.clear(); - return; - } - - while((*iter).compare("]") != 0 && iter != tokens.end()) - { - if(!first) - { - title += " "; - } - else - { - first = false; - } - - title += (*iter); - ++iter; - } - ++iter; - } - - if(mode == 0) - { - param = (*iter); - mode++; - } - else if(mode == 1) - { - mode++; - if((*iter).compare("=") != 0) - { - Log(Logs::General, Logs::Error, "Config::Parse(), invalid parse token where = should be."); - fclose(input); - vars.clear(); - return; - } - } - else - { - arg = (*iter); - mode = 0; - std::map >::iterator map_iter = vars.find(title); - if(map_iter != vars.end()) - { - map_iter->second[param] = arg; - vars[title] = map_iter->second; - } - else - { - std::map var_map; - var_map[param] = arg; - vars[title] = var_map; - } - } - ++iter; - } - fclose(input); - } - else - { - Log(Logs::General, Logs::Error, "Config::Parse(), file was unable to be opened for parsing."); - } -} - -/** -* Pretty basic lexical analyzer -* Breaks up the input character stream into tokens and puts them into the list provided. -* Ignores # as a line comment -*/ -void Config::Tokenize(FILE *input, std::list &tokens) -{ - auto c = fgetc(input); - std::string lexeme; - - while(c != EOF) - { - if(isspace(c)) - { - if(lexeme.size() > 0) - { - tokens.push_back(lexeme); - lexeme.clear(); - } - c = fgetc(input); - continue; - } - - if(isalnum(c)) - { - lexeme += c; - c = fgetc(input); - continue; - } - - switch(c) - { - case '#': - { - if(lexeme.size() > 0) - { - tokens.push_back(lexeme); - lexeme.clear(); - } - - while(c != '\n' && c != EOF) - { - c = fgetc(input); - } - break; - } - case '[': - case ']': - case '=': - { - if(lexeme.size() > 0) - { - tokens.push_back(lexeme); - lexeme.clear(); - } - - lexeme += c; - tokens.push_back(lexeme); - lexeme.clear(); - break; - } - default: - { - lexeme += c; - } - } - - c = fgetc(input); - } - - if(lexeme.size() > 0) - { - tokens.push_back(lexeme); - } -} - diff --git a/loginserver/config.h b/loginserver/config.h deleted file mode 100644 index 36d1feebe..000000000 --- a/loginserver/config.h +++ /dev/null @@ -1,61 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) - - 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_CONFIG_H -#define EQEMU_CONFIG_H - -#include -#include -#include -#include - -/** - * Keeps track of all the configuration for the application with a small parser. - * Note: This is not a thread safe class, but only parse writes to variables in the class. - * Thus making it mostly safe so long as you're careful with where you call Parse() - */ -class Config -{ -public: - Config() { } - ~Config() { } - - /** - * Parses the selected file for variables, will clear current variables if selected. - */ - virtual void Parse(const char *file_name); - - /** - * Gets a variable if it exists. - */ - std::string GetVariable(std::string title, std::string parameter); - -protected: - std::map > vars; - -private: - /** - * Breaks our input up into tokens for Parse(). - * This is private because it's not intended to be overloaded by a derived class which - * may get it's input from other places than a C file pointer. (a http get request for example). - * The programmer of a derived class would be expected to make their own Tokenize function for their own Parse(). - */ - void Tokenize(FILE* input, std::list &tokens); -}; - -#endif - diff --git a/loginserver/database.cpp b/loginserver/database.cpp new file mode 100644 index 000000000..660641e29 --- /dev/null +++ b/loginserver/database.cpp @@ -0,0 +1,768 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ + +#include "../common/global_define.h" + +#include "database.h" +#include "login_server.h" +#include "../common/eqemu_logsys.h" +#include "../common/string_util.h" +#include "../common/util/uuid.h" + +extern LoginServer server; + +/** + * Initial connect + * + * @param user + * @param pass + * @param host + * @param port + * @param name + */ +Database::Database( + std::string user, + std::string pass, + std::string host, + std::string port, + std::string name +) +{ + this->user = user; + this->pass = pass; + this->host = host; + this->name = name; + + uint32 errnum = 0; + char errbuf[MYSQL_ERRMSG_SIZE]; + if (!Open( + host.c_str(), + user.c_str(), + pass.c_str(), + name.c_str(), + std::stoi(port), + &errnum, + errbuf + ) + ) { + LogError("Failed to connect to database: Error: [{0}]", errbuf); + exit(1); + } + else { + LogStatus("Using database [{0}] at [{1}:{2}]", name, host, port); + } +} + +/** + * Deconstructor + */ +Database::~Database() +{ + if (database) { + mysql_close(database); + } +} + +/** + * @param name + * @param loginserver + * @param password + * @param id + * @return + */ +bool Database::GetLoginDataFromAccountInfo( + const std::string &name, + const std::string &loginserver, + std::string &password, + unsigned int &id +) +{ + auto query = fmt::format( + "SELECT id, account_password FROM login_accounts WHERE account_name = '{0}' AND source_loginserver = '{1}' LIMIT 1", + EscapeString(name), + EscapeString(loginserver) + ); + + auto results = QueryDatabase(query); + + if (results.RowCount() != 1) { + LogDebug( + "Could not find account for name [{0}] login [{1}]", + name, + loginserver + ); + + return false; + } + + if (!results.Success()) { + return false; + } + + auto row = results.begin(); + + id = atoi(row[0]); + password = row[1]; + + LogDebug( + "Found account for name [{0}] login [{1}]", + name, + loginserver + ); + + return true; +} + +/** + * @param token + * @param ip + * @param db_account_id + * @param db_loginserver + * @param user + * @return + */ +bool Database::GetLoginTokenDataFromToken( + const std::string &token, + const std::string &ip, + unsigned int &db_account_id, + std::string &db_loginserver, + std::string &user +) +{ + auto query = fmt::format( + "SELECT tbllogintokens.Id, tbllogintokens.IpAddress, tbllogintokenclaims.Name, tbllogintokenclaims.Value FROM tbllogintokens " + "JOIN tbllogintokenclaims ON tbllogintokens.Id = tbllogintokenclaims.TokenId WHERE tbllogintokens.Expires > NOW() " + "AND tbllogintokens.Id='{0}' AND tbllogintokens.IpAddress='{1}'", + EscapeString(token), + EscapeString(ip) + ); + + auto results = QueryDatabase(query); + if (results.RowCount() == 0 || !results.Success()) { + return false; + } + + bool found_username = false; + bool found_login_id = false; + bool found_login_server_name = false; + for (auto row = results.begin(); row != results.end(); ++row) { + if (strcmp(row[2], "username") == 0) { + user = row[3]; + found_username = true; + continue; + } + + if (strcmp(row[2], "login_server_id") == 0) { + db_account_id = atoi(row[3]); + found_login_id = true; + continue; + } + + if (strcmp(row[2], "login_server_name") == 0) { + db_loginserver = row[3]; + found_login_server_name = true; + continue; + } + } + + return found_username && found_login_id && found_login_server_name; +} + +/** + * @param loginserver + * @return + */ +unsigned int Database::GetFreeID(const std::string &loginserver) +{ + auto query = fmt::format( + "SELECT IFNULL(MAX(id), 0) + 1 FROM login_accounts WHERE source_loginserver = '{0}'", + EscapeString(loginserver) + ); + + auto results = QueryDatabase(query); + if (!results.Success() || results.RowCount() != 1) { + return 0; + } + + auto row = results.begin(); + + return std::stoi(row[0]); +} + +/** + * @param name + * @param password + * @param loginserver + * @param id + * @return + */ +bool Database::CreateLoginData( + const std::string &name, + const std::string &password, + const std::string &loginserver, + unsigned int &id +) +{ + uint32 free_id = GetFreeID(loginserver); + id = free_id; + + return CreateLoginDataWithID(name, password, loginserver, free_id); +} + +/** + * @param name + * @param password + * @param loginserver + * @param email + * @return + */ +uint32 Database::CreateLoginAccount( + const std::string &name, + const std::string &password, + const std::string &loginserver, + const std::string &email +) +{ + uint32 free_id = GetFreeID(loginserver); + + if (free_id <= 0) { + return 0; + } + + auto query = fmt::format( + "INSERT INTO login_accounts (id, source_loginserver, account_name, account_password, account_email, last_login_date, last_ip_address, created_at) " + "VALUES ({0}, '{1}', '{2}', '{3}', '{4}', NOW(), '127.0.0.1', NOW())", + free_id, + EscapeString(loginserver), + EscapeString(name), + EscapeString(password), + EscapeString(email) + ); + + auto results = QueryDatabase(query); + + return (results.Success() ? free_id : 0); +} + +/** + * @param in_account_name + * @param in_account_password + * @param loginserver + * @param id + * @return + */ +bool Database::CreateLoginDataWithID( + const std::string &in_account_name, + const std::string &in_account_password, + const std::string &loginserver, + unsigned int id +) +{ + if (id == 0) { + return false; + } + + auto query = fmt::format( + "INSERT INTO login_accounts (id, source_loginserver, account_name, account_password, account_email, last_login_date, last_ip_address, created_at) " + "VALUES ({0}, '{1}', '{2}', '{3}', 'local_creation', NOW(), '127.0.0.1', NOW())", + id, + EscapeString(loginserver), + EscapeString(in_account_name), + EscapeString(in_account_password) + ); + + auto results = QueryDatabase(query); + + return results.Success(); +} + +/** + * @param name + * @param password + * @param loginserver + * @param id + * @return + */ +bool Database::DoesLoginServerAccountExist( + const std::string &name, + const std::string &password, + const std::string &loginserver, + unsigned int id +) +{ + if (id == 0) { + return false; + } + + auto query = fmt::format( + "SELECT account_name FROM login_accounts WHERE account_name = '{0}' AND source_loginserver = '{1}'", + EscapeString(name), + EscapeString(loginserver) + ); + + auto results = QueryDatabase(query); + if (!results.Success() || results.RowCount() != 1) { + return false; + } + + return true; +} + +/** + * @param name + * @param loginserver + * @param hash + */ +void Database::UpdateLoginserverAccountPasswordHash( + const std::string &name, + const std::string &loginserver, + const std::string &hash +) +{ + LogDebug( + "name [{0}] loginserver [{1}] hash [{2}]", + name, + loginserver, + hash + ); + + auto query = fmt::format( + "UPDATE login_accounts SET account_password = '{0}' WHERE account_name = '{1}' AND source_loginserver = '{2}'", + hash, + EscapeString(name), + EscapeString(loginserver) + ); + + QueryDatabase(query); +} + +/** + * @param short_name + * @param login_world_server_admin_id + * @return + */ +Database::DbWorldRegistration Database::GetWorldRegistration( + const std::string &short_name, + uint32 login_world_server_admin_id +) +{ + auto query = fmt::format( + "SELECT\n" + " WSR.id,\n" + " WSR.tag_description,\n" + " WSR.is_server_trusted,\n" + " SLT.id,\n" + " SLT.description,\n" + " ifnull(WSR.login_server_admin_id, 0) AS login_server_admin_id\n" + "FROM\n" + " login_world_servers AS WSR\n" + " JOIN login_server_list_types AS SLT ON WSR.login_server_list_type_id = SLT.id\n" + "WHERE\n" + " WSR.short_name = '{0}' AND WSR.login_server_admin_id = {1} LIMIT 1", + EscapeString(short_name), + login_world_server_admin_id + ); + + Database::DbWorldRegistration world_registration{}; + + auto results = QueryDatabase(query); + if (!results.Success() || results.RowCount() != 1) { + return world_registration; + } + + auto row = results.begin(); + + world_registration.loaded = true; + world_registration.server_id = std::stoi(row[0]); + world_registration.server_description = row[1]; + world_registration.server_list_type = std::stoi(row[3]); + world_registration.is_server_trusted = std::stoi(row[2]) > 0; + world_registration.server_list_description = row[4]; + + int db_account_id = std::stoi(row[5]); + if (db_account_id <= 0) { + return world_registration; + } + + auto world_registration_query = fmt::format( + "SELECT account_name, account_password FROM login_server_admins WHERE id = {0} LIMIT 1", + db_account_id + ); + + auto world_registration_results = QueryDatabase(world_registration_query); + if (world_registration_results.Success() && world_registration_results.RowCount() == 1) { + auto world_registration_row = world_registration_results.begin(); + world_registration.server_admin_account_name = world_registration_row[0]; + world_registration.server_admin_account_password = world_registration_row[1]; + } + + return world_registration; +} + +/** + * @param id + * @param ip_address + */ +void Database::UpdateLSAccountData(unsigned int id, std::string ip_address) +{ + auto query = fmt::format( + "UPDATE login_accounts SET last_ip_address = '{0}', last_login_date = NOW() where id = {1}", + ip_address, + id + ); + + QueryDatabase(query); +} + +/** + * @param id + * @param name + * @param password + * @param email + */ +void Database::UpdateLSAccountInfo( + unsigned int id, + std::string name, + std::string password, + std::string email +) +{ + auto query = fmt::format( + "REPLACE login_accounts SET id = {0}, account_name = '{1}', account_password = sha('{2}'), " + "account_email = '{3}', last_ip_address = '0.0.0.0', last_login_date = now()", + id, + EscapeString(name), + EscapeString(password), + EscapeString(email) + ); + + QueryDatabase(query); +} + +/** + * @param id + * @param long_name + * @param ip_address + */ +void Database::UpdateWorldRegistration(unsigned int id, std::string long_name, std::string ip_address) +{ + auto query = fmt::format( + "UPDATE login_world_servers SET last_login_date = NOW(), last_ip_address = '{0}', long_name = '{1}' WHERE id = {2}", + ip_address, + EscapeString(long_name), + id + ); + + QueryDatabase(query); +} + +/** + * @param server_long_name + * @param server_short_name + * @param id + * @return + */ +bool Database::CreateWorldRegistration( + std::string server_long_name, + std::string server_short_name, + std::string server_remote_ip, + unsigned int &id, + unsigned int &server_admin_id +) +{ + auto results = QueryDatabase("SELECT IFNULL(max(id), 0) + 1 FROM login_world_servers"); + if (!results.Success() || results.RowCount() != 1) { + return false; + } + + auto row = results.begin(); + + id = std::stoi(row[0]); + auto insert_query = fmt::format( + "INSERT INTO login_world_servers SET id = {0}, long_name = '{1}', short_name = '{2}', last_ip_address = '{3}', \n" + "login_server_list_type_id = 3, login_server_admin_id = {4}, is_server_trusted = 0, tag_description = ''", + id, + EscapeString(server_long_name), + EscapeString(server_short_name), + server_remote_ip, + server_admin_id + ); + + auto insert_results = QueryDatabase(insert_query); + if (!insert_results.Success()) { + LogError( + "Failed to register world server {0} - {1}", + server_long_name, + server_short_name + ); + + return false; + } + + return true; +} + +/** + * @param long_name + * @param short_name + * @param id + * @return + */ +std::string Database::CreateLoginserverApiToken( + bool write_mode, + bool read_mode +) +{ + std::string token = EQ::Util::UUID::Generate().ToString(); + auto query = fmt::format( + "INSERT INTO login_api_tokens (token, can_write, can_read, created_at) VALUES ('{0}', {1}, {2}, NOW())", + token, + (write_mode ? "1" : "0"), + (read_mode ? "1" : "0") + ); + + auto results = QueryDatabase(query); + if (!results.Success()) { + return ""; + } + + return token; +} + +/** + * @param long_name + * @param short_name + * @param id + * @return + */ +MySQLRequestResult Database::GetLoginserverApiTokens() +{ + return QueryDatabase("SELECT token, can_write, can_read FROM login_api_tokens"); +} + +/** + * @param log_settings + */ +void Database::LoadLogSettings(EQEmuLogSys::LogSettings *log_settings) +{ + std::string query = + "SELECT " + "log_category_id, " + "log_category_description, " + "log_to_console, " + "log_to_file, " + "log_to_gmsay " + "FROM " + "logsys_categories " + "ORDER BY log_category_id"; + + auto results = QueryDatabase(query); + int log_category_id = 0; + + int *categories_in_database = new int[1000]; + + for (auto row = results.begin(); row != results.end(); ++row) { + log_category_id = atoi(row[0]); + if (log_category_id <= Logs::None || log_category_id >= Logs::MaxCategoryID) { + continue; + } + + log_settings[log_category_id].log_to_console = static_cast(atoi(row[2])); + log_settings[log_category_id].log_to_file = static_cast(atoi(row[3])); + log_settings[log_category_id].log_to_gmsay = static_cast(atoi(row[4])); + + /** + * 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[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; + + if (is_category_enabled) { + log_settings[log_category_id].is_category_enabled = 1; + } + + /** + * This determines whether or not the process needs to actually file log anything. + * If we go through this whole loop and nothing is set to any debug level, there is no point to create a file or keep anything open + */ + if (log_settings[log_category_id].log_to_file > 0) { + LogSys.file_logs_enabled = true; + } + + categories_in_database[log_category_id] = 1; + } + + /** + * Auto inject categories that don't exist in the database... + */ + for (int log_index = Logs::AA; log_index != Logs::MaxCategoryID; log_index++) { + if (categories_in_database[log_index] != 1) { + + LogInfo( + "New Log Category [{0}] doesn't exist... Automatically adding to [logsys_categories] table...", + Logs::LogCategoryName[log_index] + ); + + auto inject_query = fmt::format( + "INSERT INTO logsys_categories " + "(log_category_id, " + "log_category_description, " + "log_to_console, " + "log_to_file, " + "log_to_gmsay) " + "VALUES " + "({0}, '{1}', {2}, {3}, {4})", + log_index, + EscapeString(Logs::LogCategoryName[log_index]), + std::to_string(log_settings[log_index].log_to_console), + std::to_string(log_settings[log_index].log_to_file), + std::to_string(log_settings[log_index].log_to_gmsay) + ); + + QueryDatabase(inject_query); + } + } + + delete[] categories_in_database; +} + +/** + * @param account_name + * @param account_password + * @param first_name + * @param last_name + * @param email + * @param ip_address + * @return + */ +uint32 Database::CreateLoginserverWorldAdminAccount( + const std::string &account_name, + const std::string &account_password, + const std::string &first_name, + const std::string &last_name, + const std::string &email, + const std::string &ip_address +) +{ + auto query = fmt::format( + "INSERT INTO login_server_admins (account_name, account_password, first_name, last_name, email, registration_date, " + "registration_ip_address) " + "VALUES ('{0}', '{1}', '{2}', '{3}', '{4}', NOW(), '{5}')", + EscapeString(account_name), + EscapeString(account_password), + EscapeString(first_name), + EscapeString(last_name), + EscapeString(email), + ip_address + ); + + auto results = QueryDatabase(query); + + return (results.Success() ? results.LastInsertedID() : 0); +} + +/** + * @param account_name + * @return + */ +bool Database::DoesLoginserverWorldAdminAccountExist( + const std::string &account_name +) +{ + auto query = fmt::format( + "SELECT account_name FROM login_server_admins WHERE account_name = '{0}' LIMIT 1", + EscapeString(account_name) + ); + + auto results = QueryDatabase(query); + + return (results.RowCount() == 1); +} + +/** + * @param account_name + * @return + */ +Database::DbLoginServerAdmin Database::GetLoginServerAdmin(const std::string &account_name) +{ + auto query = fmt::format( + "SELECT id, account_name, account_password, first_name, last_name, email, registration_date, registration_ip_address" + " FROM login_server_admins WHERE account_name = '{0}' LIMIT 1", + EscapeString(account_name) + ); + + auto results = QueryDatabase(query); + + Database::DbLoginServerAdmin login_server_admin{}; + if (results.RowCount() == 1) { + auto row = results.begin(); + login_server_admin.loaded = true; + login_server_admin.id = std::stoi(row[0]); + login_server_admin.account_name = row[1]; + login_server_admin.account_password = row[2]; + login_server_admin.first_name = row[3]; + login_server_admin.last_name = row[4]; + login_server_admin.email = row[5]; + login_server_admin.registration_date = row[7]; + login_server_admin.registration_ip_address = row[8]; + } + + return login_server_admin; +} + +/** + * @param account_name + * @return + */ +Database::DbLoginServerAccount Database::GetLoginServerAccountByAccountName( + const std::string &account_name, + const std::string &source_loginserver +) +{ + auto query = fmt::format( + "SELECT id, account_name, account_password, account_email, source_loginserver, last_ip_address, last_login_date, " + "created_at, updated_at" + " FROM login_accounts WHERE account_name = '{0}' and source_loginserver = '{1}' LIMIT 1", + EscapeString(account_name), + EscapeString(source_loginserver) + ); + + auto results = QueryDatabase(query); + + Database::DbLoginServerAccount login_server_account{}; + if (results.RowCount() == 1) { + auto row = results.begin(); + login_server_account.loaded = true; + login_server_account.id = std::stoi(row[0]); + login_server_account.account_name = row[1]; + login_server_account.account_password = row[2]; + login_server_account.account_email = row[3]; + login_server_account.source_loginserver = row[4]; + login_server_account.last_ip_address = row[5]; + login_server_account.last_login_date = row[6]; + login_server_account.created_at = row[7]; + login_server_account.updated_at = row[8]; + } + + return login_server_account; +} \ No newline at end of file diff --git a/loginserver/database.h b/loginserver/database.h index 5dd7f7037..ef1d7bfe0 100644 --- a/loginserver/database.h +++ b/loginserver/database.h @@ -1,82 +1,291 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ - 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. +#ifndef EQEMU_DATABASEMYSQL_H +#define EQEMU_DATABASEMYSQL_H - 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_DATABASE_H -#define EQEMU_DATABASE_H +#include "../common/dbcore.h" +#include "../common/eqemu_logsys.h" #include +#include +#include +#include -#define EQEMU_MYSQL_ENABLED -//#define EQEMU_POSTGRESQL_ENABLED - -/** -* Base database class, intended to be extended. -*/ -class Database -{ +class Database : public DBcore { public: - Database() : user(""), pass(""), host(""), port(""), name("") { } - virtual ~Database() { } + + Database() { database = nullptr; } /** - * Returns true if the database successfully connected. - */ - virtual bool IsConnected() { return false; } + * Constructor, tries to set our database to connect to the supplied options. + * + * @param user + * @param pass + * @param host + * @param port + * @param name + */ + Database(std::string user, std::string pass, std::string host, std::string port, std::string name); /** - * Retrieves the login data (password hash and account id) from the account name provided - * Needed for client login procedure. - * Returns true if the record was found, false otherwise. - */ - virtual bool GetLoginDataFromAccountName(std::string name, std::string &password, unsigned int &id) { return false; } - - virtual bool GetLoginTokenDataFromToken(const std::string &token, const std::string &ip, unsigned int &db_account_id, std::string &user) { return false; } - - virtual bool CreateLoginData(const std::string &name, const std::string &password, unsigned int &id) { return false; } + * Destructor, frees our database if needed. + */ + ~Database(); + bool IsConnected() { return (database != nullptr); } /** - * Retrieves the world registration from the long and short names provided. - * Needed for world login procedure. - * Returns true if the record was found, false otherwise. - */ - virtual bool GetWorldRegistration(std::string long_name, std::string short_name, unsigned int &id, std::string &desc, unsigned int &list_id, - unsigned int &trusted, std::string &list_desc, std::string &account, std::string &password) { return false; } + * Retrieves the login data (password hash and account id) from the account name provided needed for client login procedure. + * @param name + * @param loginserver + * @param password + * @param id + * @return + */ + bool GetLoginDataFromAccountInfo( + const std::string &name, + const std::string &loginserver, + std::string &password, + unsigned int &id + ); /** - * Updates the ip address of the client with account id = id - */ - virtual void UpdateLSAccountData(unsigned int id, std::string ip_address) { } + * @param token + * @param ip + * @param db_account_id + * @param db_loginserver + * @param user + * @return + */ + bool GetLoginTokenDataFromToken( + const std::string &token, + const std::string &ip, + unsigned int &db_account_id, + std::string &db_loginserver, + std::string &user + ); /** - * Updates or creates the login server account with info from world server - */ - virtual void UpdateLSAccountInfo(unsigned int id, std::string name, std::string password, std::string email) { } + * @param loginserver + * @return + */ + unsigned int GetFreeID(const std::string &loginserver); /** - * Updates the ip address of the world with account id = id - */ - virtual void UpdateWorldRegistration(unsigned int id, std::string long_name, std::string ip_address) { } + * @param name + * @param password + * @param loginserver + * @param id + * @return + */ + bool CreateLoginData( + const std::string &name, + const std::string &password, + const std::string &loginserver, + unsigned int &id + ); /** - * Creates new world registration for unregistered servers and returns new id - */ - virtual bool CreateWorldRegistration(std::string long_name, std::string short_name, unsigned int &id) { return false; } + * @param in_account_name + * @param in_account_password + * @param loginserver + * @param id + * @return + */ + bool CreateLoginDataWithID( + const std::string &in_account_name, + const std::string &in_account_password, + const std::string &loginserver, + unsigned int id + ); + + /** + * @param name + * @param loginserver + * @param hash + */ + void UpdateLoginserverAccountPasswordHash( + const std::string &name, + const std::string &loginserver, + const std::string &hash); + + /** + * @param name + * @param password + * @param loginserver + * @param id + * @return + */ + bool DoesLoginServerAccountExist( + const std::string &name, + const std::string &password, + const std::string &loginserver, + unsigned int id + ); + + struct DbWorldRegistration { + bool loaded = false; + int32 server_id = 0; + int8 server_list_type = 3; + bool is_server_trusted = false; + std::string server_description; + std::string server_list_description; + std::string server_admin_account_name; + std::string server_admin_account_password; + }; + + /** + * Retrieves the world registration from the long and short names provided + * Needed for world login procedure + * Returns true if the record was found, false otherwise + * + * @param short_name + * @param login_world_server_admin_id + * @return + */ + Database::DbWorldRegistration GetWorldRegistration( + const std::string &short_name, + uint32 login_world_server_admin_id + ); + + /** + * @param id + * @param ip_address + */ + void UpdateLSAccountData(unsigned int id, std::string ip_address); + + /** + * @param id + * @param name + * @param password + * @param email + */ + void UpdateLSAccountInfo(unsigned int id, std::string name, std::string password, std::string email); + + /** + * @param id + * @param long_name + * @param ip_address + */ + void UpdateWorldRegistration(unsigned int id, std::string long_name, std::string ip_address); + + /** + * @param server_long_name + * @param server_short_name + * @param id + * @return + */ + bool CreateWorldRegistration( + std::string server_long_name, + std::string server_short_name, + std::string server_remote_ip, + unsigned int &id, + unsigned int &server_admin_id + ); + + /** + * @param log_settings + */ + void LoadLogSettings(EQEmuLogSys::LogSettings *log_settings); + + /** + * @param write_mode + * @param read_mode + * @return + */ + std::string CreateLoginserverApiToken(bool write_mode, bool read_mode); + MySQLRequestResult GetLoginserverApiTokens(); + + /** + * @param account_name + * @param account_password + * @param first_name + * @param last_name + * @param email + * @param ip_address + * @return + */ + uint32 CreateLoginserverWorldAdminAccount( + const std::string &account_name, + const std::string &account_password, + const std::string &first_name, + const std::string &last_name, + const std::string &email, + const std::string &ip_address + ); + + /** + * @param account_name + * @return + */ + bool DoesLoginserverWorldAdminAccountExist(const std::string &account_name); + + struct DbLoginServerAdmin { + bool loaded = false; + uint32 id; + std::string account_name; + std::string account_password; + std::string first_name; + std::string last_name; + std::string email; + std::string registration_date; + std::string registration_ip_address; + }; + + Database::DbLoginServerAdmin GetLoginServerAdmin(const std::string &account_name); + + struct DbLoginServerAccount { + bool loaded = false; + uint32 id; + std::string account_name; + std::string account_password; + std::string account_email; + std::string source_loginserver; + std::string last_login_date; + std::string last_ip_address; + std::string created_at; + std::string updated_at; + }; + + Database::DbLoginServerAccount GetLoginServerAccountByAccountName( + const std::string &account_name, + const std::string &source_loginserver = "local" + ); + + /** + * @param name + * @param password + * @param loginserver + * @param email + * @return + */ + uint32 CreateLoginAccount( + const std::string &name, + const std::string &password, + const std::string &loginserver = "local", + const std::string &email = "local_creation" + ); + protected: std::string user, pass, host, port, name; + MYSQL *database{}; }; #endif diff --git a/loginserver/database_mysql.cpp b/loginserver/database_mysql.cpp deleted file mode 100644 index af5010925..000000000 --- a/loginserver/database_mysql.cpp +++ /dev/null @@ -1,362 +0,0 @@ -/* EQEMu: Everquest Server Emulator -Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) - -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 -*/ -#include "../common/global_define.h" -#include "database.h" - -#ifdef EQEMU_MYSQL_ENABLED -#include "database_mysql.h" -#include "login_server.h" -#include "../common/eqemu_logsys.h" -#include "../common/eqemu_logsys_fmt.h" -#include "../common/string_util.h" - -extern LoginServer server; - -DatabaseMySQL::DatabaseMySQL(std::string user, std::string pass, std::string host, std::string port, std::string name) -{ - this->user = user; - this->pass = pass; - this->host = host; - this->name = name; - - database = mysql_init(nullptr); - if (database) - { - char r = 1; - mysql_options(database, MYSQL_OPT_RECONNECT, &r); - if (!mysql_real_connect(database, host.c_str(), user.c_str(), pass.c_str(), name.c_str(), atoi(port.c_str()), nullptr, 0)) - { - mysql_close(database); - Log(Logs::General, Logs::Error, "Failed to connect to MySQL database. Error: %s", mysql_error(database)); - exit(1); - } - } - else - { - Log(Logs::General, Logs::Error, "Failed to create db object in MySQL database."); - } -} - -DatabaseMySQL::~DatabaseMySQL() -{ - if (database) - { - mysql_close(database); - } -} - -bool DatabaseMySQL::GetLoginDataFromAccountName(std::string name, std::string &password, unsigned int &id) -{ - if (!database) - { - return false; - } - - MYSQL_RES *res; - MYSQL_ROW row; - std::stringstream query(std::stringstream::in | std::stringstream::out); - query << "SELECT LoginServerID, AccountPassword FROM " << server.options.GetAccountTable() << " WHERE AccountName = '"; - query << name; - query << "'"; - - if (mysql_query(database, query.str().c_str()) != 0) - { - LogF(Logs::General, Logs::Error, "Mysql query failed: {0}", query.str()); - return false; - } - - res = mysql_use_result(database); - - if (res) - { - while ((row = mysql_fetch_row(res)) != nullptr) - { - id = atoi(row[0]); - password = row[1]; - mysql_free_result(res); - return true; - } - } - - Log(Logs::General, Logs::Error, "Mysql query returned no result: %s", query.str().c_str()); - return false; -} - -bool DatabaseMySQL::GetLoginTokenDataFromToken(const std::string &token, const std::string &ip, unsigned int &db_account_id, std::string &user) -{ - if (!database) - { - return false; - } - - MYSQL_RES *res; - MYSQL_ROW row; - std::stringstream query(std::stringstream::in | std::stringstream::out); - query << "SELECT tbllogintokens.Id, tbllogintokens.IpAddress, tbllogintokenclaims.Name, tbllogintokenclaims.Value FROM tbllogintokens "; - query << "JOIN tbllogintokenclaims ON tbllogintokens.Id = tbllogintokenclaims.TokenId WHERE tbllogintokens.Expires > NOW() AND tbllogintokens.Id='"; - query << EscapeString(token) << "' AND tbllogintokens.IpAddress='" << EscapeString(ip) << "'"; - - if (mysql_query(database, query.str().c_str()) != 0) - { - Log(Logs::General, Logs::Error, "Mysql query failed: %s", query.str().c_str()); - return false; - } - - res = mysql_use_result(database); - - bool found_username = false; - bool found_login_id = false; - if (res) - { - while ((row = mysql_fetch_row(res)) != nullptr) - { - if (strcmp(row[2], "username") == 0) { - user = row[3]; - found_username = true; - continue; - } - - if (strcmp(row[2], "login_server_id") == 0) { - db_account_id = atoi(row[3]); - found_login_id = true; - continue; - } - } - - mysql_free_result(res); - } - - return found_username && found_login_id; -} - -bool DatabaseMySQL::CreateLoginData(const std::string &name, const std::string &password, unsigned int &id) -{ - if (!database) { - return false; - } - - MYSQL_RES *result; - MYSQL_ROW row; - std::stringstream query(std::stringstream::in | std::stringstream::out); - - query << "INSERT INTO " << server.options.GetAccountTable() << " (AccountName, AccountPassword, AccountEmail, LastLoginDate, LastIPAddress) "; - query << " VALUES('" << name << "', '" << password << "', 'local_creation', NOW(), '127.0.0.1'); "; - - if (mysql_query(database, query.str().c_str()) != 0) { - Log(Logs::General, Logs::Error, "Mysql query failed: %s", query.str().c_str()); - return false; - } - else { - id = mysql_insert_id(database); - return true; - } - - Log(Logs::General, Logs::Error, "Mysql query returned no result: %s", query.str().c_str()); - return false; -} - -bool DatabaseMySQL::GetWorldRegistration(std::string long_name, std::string short_name, unsigned int &id, std::string &desc, unsigned int &list_id, - unsigned int &trusted, std::string &list_desc, std::string &account, std::string &password) -{ - if (!database) - { - return false; - } - - MYSQL_RES *res; - MYSQL_ROW row; - char escaped_short_name[101]; - unsigned long length; - length = mysql_real_escape_string(database, escaped_short_name, short_name.substr(0, 100).c_str(), short_name.substr(0, 100).length()); - escaped_short_name[length + 1] = 0; - std::stringstream query(std::stringstream::in | std::stringstream::out); - query << "SELECT ifnull(WSR.ServerID,999999) AS ServerID, WSR.ServerTagDescription, ifnull(WSR.ServerTrusted,0) AS ServerTrusted, ifnull(SLT.ServerListTypeID,3) AS ServerListTypeID, "; - query << "SLT.ServerListTypeDescription, ifnull(WSR.ServerAdminID,0) AS ServerAdminID FROM " << server.options.GetWorldRegistrationTable(); - query << " AS WSR JOIN " << server.options.GetWorldServerTypeTable() << " AS SLT ON WSR.ServerListTypeID = SLT.ServerListTypeID"; - query << " WHERE WSR.ServerShortName = '"; - query << escaped_short_name; - query << "'"; - - if (mysql_query(database, query.str().c_str()) != 0) - { - Log(Logs::General, Logs::Error, "Mysql query failed: %s", query.str().c_str()); - return false; - } - - res = mysql_use_result(database); - if (res) - { - if ((row = mysql_fetch_row(res)) != nullptr) - { - id = atoi(row[0]); - desc = row[1]; - trusted = atoi(row[2]); - list_id = atoi(row[3]); - list_desc = row[4]; - int db_account_id = atoi(row[5]); - mysql_free_result(res); - - if (db_account_id > 0) - { - std::stringstream query(std::stringstream::in | std::stringstream::out); - query << "SELECT AccountName, AccountPassword FROM " << server.options.GetWorldAdminRegistrationTable(); - query << " WHERE ServerAdminID = " << db_account_id; - - if (mysql_query(database, query.str().c_str()) != 0) - { - Log(Logs::General, Logs::Error, "Mysql query failed: %s", query.str().c_str()); - return false; - } - - res = mysql_use_result(database); - if (res) - { - if ((row = mysql_fetch_row(res)) != nullptr) - { - account = row[0]; - password = row[1]; - mysql_free_result(res); - return true; - } - } - - Log(Logs::General, Logs::Error, "Mysql query returned no result: %s", query.str().c_str()); - return false; - } - return true; - } - } - - Log(Logs::General, Logs::Error, "Mysql query returned no result: %s", query.str().c_str()); - return false; -} - -void DatabaseMySQL::UpdateLSAccountData(unsigned int id, std::string ip_address) -{ - if (!database) - { - return; - } - - std::stringstream query(std::stringstream::in | std::stringstream::out); - query << "UPDATE " << server.options.GetAccountTable() << " SET LastIPAddress = '"; - query << ip_address; - query << "', LastLoginDate = now() where LoginServerID = "; - query << id; - - if (mysql_query(database, query.str().c_str()) != 0) - { - Log(Logs::General, Logs::Error, "Mysql query failed: %s", query.str().c_str()); - } -} - -void DatabaseMySQL::UpdateLSAccountInfo(unsigned int id, std::string name, std::string password, std::string email) -{ - if (!database) - { - return; - } - - std::stringstream query(std::stringstream::in | std::stringstream::out); - query << "REPLACE " << server.options.GetAccountTable() << " SET LoginServerID = "; - query << id << ", AccountName = '" << name << "', AccountPassword = sha('"; - query << password << "'), AccountCreateDate = now(), AccountEmail = '" << email; - query << "', LastIPAddress = '0.0.0.0', LastLoginDate = now()"; - - if (mysql_query(database, query.str().c_str()) != 0) - { - Log(Logs::General, Logs::Error, "Mysql query failed: %s", query.str().c_str()); - } -} - -void DatabaseMySQL::UpdateWorldRegistration(unsigned int id, std::string long_name, std::string ip_address) -{ - if (!database) - { - return; - } - - char escaped_long_name[101]; - unsigned long length; - length = mysql_real_escape_string(database, escaped_long_name, long_name.substr(0, 100).c_str(), long_name.substr(0, 100).length()); - escaped_long_name[length + 1] = 0; - std::stringstream query(std::stringstream::in | std::stringstream::out); - query << "UPDATE " << server.options.GetWorldRegistrationTable() << " SET ServerLastLoginDate = now(), ServerLastIPAddr = '"; - query << ip_address; - query << "', ServerLongName = '"; - query << escaped_long_name; - query << "' WHERE ServerID = "; - query << id; - - if (mysql_query(database, query.str().c_str()) != 0) - { - Log(Logs::General, Logs::Error, "Mysql query failed: %s", query.str().c_str()); - } -} - -bool DatabaseMySQL::CreateWorldRegistration(std::string long_name, std::string short_name, unsigned int &id) -{ - if (!database) - { - return false; - } - - MYSQL_RES *res; - MYSQL_ROW row; - char escaped_long_name[201]; - char escaped_short_name[101]; - unsigned long length; - length = mysql_real_escape_string(database, escaped_long_name, long_name.substr(0, 100).c_str(), long_name.substr(0, 100).length()); - escaped_long_name[length + 1] = 0; - length = mysql_real_escape_string(database, escaped_short_name, short_name.substr(0, 100).c_str(), short_name.substr(0, 100).length()); - escaped_short_name[length + 1] = 0; - std::stringstream query(std::stringstream::in | std::stringstream::out); - query << "SELECT ifnull(max(ServerID),0) FROM " << server.options.GetWorldRegistrationTable(); - - if (mysql_query(database, query.str().c_str()) != 0) - { - Log(Logs::General, Logs::Error, "Mysql query failed: %s", query.str().c_str()); - return false; - } - - res = mysql_use_result(database); - if (res) - { - if ((row = mysql_fetch_row(res)) != nullptr) - { - id = atoi(row[0]) + 1; - mysql_free_result(res); - - std::stringstream query(std::stringstream::in | std::stringstream::out); - query << "INSERT INTO " << server.options.GetWorldRegistrationTable() << " SET ServerID = " << id; - query << ", ServerLongName = '" << escaped_long_name << "', ServerShortName = '" << escaped_short_name; - query << "', ServerListTypeID = 3, ServerAdminID = 0, ServerTrusted = 0, ServerTagDescription = ''"; - - if (mysql_query(database, query.str().c_str()) != 0) - { - Log(Logs::General, Logs::Error, "Mysql query failed: %s", query.str().c_str()); - return false; - } - return true; - } - } - Log(Logs::General, Logs::Error, "World registration did not exist in the database for %s %s", long_name.c_str(), short_name.c_str()); - return false; -} - -#endif diff --git a/loginserver/database_mysql.h b/loginserver/database_mysql.h deleted file mode 100644 index fc791b229..000000000 --- a/loginserver/database_mysql.h +++ /dev/null @@ -1,100 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) - - 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_DATABASEMYSQL_H -#define EQEMU_DATABASEMYSQL_H - -#include "database.h" -#ifdef EQEMU_MYSQL_ENABLED - -#include -#include -#include -#include - -/** -* Mysql Database class -*/ -class DatabaseMySQL : public Database -{ -public: - /** - * Constructor, sets our database to null. - */ - DatabaseMySQL() { database = nullptr; } - - /** - * Constructor, tries to set our database to connect to the supplied options. - */ - DatabaseMySQL(std::string user, std::string pass, std::string host, std::string port, std::string name); - - /** - * Destructor, frees our database if needed. - */ - virtual ~DatabaseMySQL(); - - /** - * @return Returns true if the database successfully connected. - */ - virtual bool IsConnected() { return (database != nullptr); } - - /** - * Retrieves the login data (password hash and account id) from the account name provided - * Needed for client login procedure. - * Returns true if the record was found, false otherwise. - */ - virtual bool GetLoginDataFromAccountName(std::string name, std::string &password, unsigned int &id); - - virtual bool GetLoginTokenDataFromToken(const std::string &token, const std::string &ip, unsigned int &db_account_id, std::string &user); - - virtual bool CreateLoginData(const std::string &name, const std::string &password, unsigned int &id); - - /** - * Retrieves the world registration from the long and short names provided. - * Needed for world login procedure. - * Returns true if the record was found, false otherwise. - */ - virtual bool GetWorldRegistration(std::string long_name, std::string short_name, unsigned int &id, std::string &desc, unsigned int &list_id, - unsigned int &trusted, std::string &list_desc, std::string &account, std::string &password); - - /** - * Updates the ip address of the client with account id = id - */ - virtual void UpdateLSAccountData(unsigned int id, std::string ip_address); - - /** - * Updates or creates the login server account with info from world server - */ - virtual void UpdateLSAccountInfo(unsigned int id, std::string name, std::string password, std::string email); - - /** - * Updates the ip address of the world with account id = id - */ - virtual void UpdateWorldRegistration(unsigned int id, std::string long_name, std::string ip_address); - - /** - * Creates new world registration for unregistered servers and returns new id - */ - virtual bool CreateWorldRegistration(std::string long_name, std::string short_name, unsigned int &id); -protected: - std::string user, pass, host, port, name; - MYSQL *database; -}; - -#endif -#endif - diff --git a/loginserver/database_postgresql.cpp b/loginserver/database_postgresql.cpp deleted file mode 100644 index 4b6242e63..000000000 --- a/loginserver/database_postgresql.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) - - 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 -*/ -#include "../common/global_define.h" -#include "database.h" - -#ifdef EQEMU_POSTGRESQL_ENABLED -#include "database_postgresql.h" -#include "error_log.h" -#include "login_server.h" - - -extern LoginServer server; - -#pragma comment(lib, "libpq.lib") - -DatabasePostgreSQL::DatabasePostgreSQL(string user, string pass, string host, string port, string name) -{ - db = nullptr; - db = PQsetdbLogin(host.c_str(), port.c_str(), nullptr, nullptr, name.c_str(), user.c_str(), pass.c_str()); - if(!db) - { - Log(Logs::General, Logs::Error, "Failed to connect to PostgreSQL Database."); - } - - if(PQstatus(db) != CONNECTION_OK) - { - Log(Logs::General, Logs::Error, "Failed to connect to PostgreSQL Database."); - PQfinish(db); - db = nullptr; - } -} - -DatabasePostgreSQL::~DatabasePostgreSQL() -{ - if(db) - { - PQfinish(db); - } -} - -bool DatabasePostgreSQL::GetLoginDataFromAccountName(string name, string &password, unsigned int &id) -{ - if(!db) - { - return false; - } - - /** - * PostgreSQL doesn't have automatic reconnection option like mysql - * but it's easy to check and reconnect - */ - if(PQstatus(db) != CONNECTION_OK) - { - PQreset(db); - if(PQstatus(db) != CONNECTION_OK) - { - return false; - } - } - - stringstream query(stringstream::in | stringstream::out); - query << "SELECT LoginServerID, AccountPassword FROM " << server.options.GetAccountTable() << " WHERE AccountName = '"; - query << name; - query << "'"; - - PGresult *res = PQexec(db, query.str().c_str()); - - char *error = PQresultErrorMessage(res); - if(strlen(error) > 0) - { - Log(Logs::General, Logs::Error, "Database error in DatabasePostgreSQL::GetLoginDataFromAccountName(): %s", error); - PQclear(res); - return false; - } - - if(PQntuples(res) > 0) - { - id = atoi(PQgetvalue(res, 0, 0)); - password = PQgetvalue(res, 0, 1); - PQclear(res); - return true; - } - - PQclear(res); - return false; -} - -bool DatabasePostgreSQL::GetWorldRegistration(string long_name, string short_name, unsigned int &id, string &desc, unsigned int &list_id, - unsigned int &trusted, string &list_desc, string &account, string &password) -{ - if(!db) - { - return false; - } - - /** - * PostgreSQL doesn't have automatic reconnection option like mysql - * but it's easy to check and reconnect - */ - if(PQstatus(db) != CONNECTION_OK) - { - PQreset(db); - if(PQstatus(db) != CONNECTION_OK) - { - return false; - } - } - - stringstream query(stringstream::in | stringstream::out); - query << "SELECT WSR.ServerID, WSR.ServerTagDescription, WSR.ServerTrusted, SLT.ServerListTypeID, "; - query << "SLT.ServerListTypeDescription, SAR.AccountName, SAR.AccountPassword FROM " << server.options.GetWorldRegistrationTable(); - query << " AS WSR JOIN " << server.options.GetWorldServerTypeTable() << " AS SLT ON WSR.ServerListTypeID = SLT.ServerListTypeID JOIN "; - query << server.options.GetWorldAdminRegistrationTable() << " AS SAR ON WSR.ServerAdminID = SAR.ServerAdminID WHERE WSR.ServerShortName"; - query << " = '"; - query << short_name; - query << "'"; - - PGresult *res = PQexec(db, query.str().c_str()); - - char *error = PQresultErrorMessage(res); - if(strlen(error) > 0) - { - Log(Logs::General, Logs::Error, "Database error in DatabasePostgreSQL::GetWorldRegistration(): %s", error); - PQclear(res); - return false; - } - - if(PQntuples(res) > 0) - { - id = atoi(PQgetvalue(res, 0, 0)); - desc = PQgetvalue(res, 0, 1); - trusted = atoi(PQgetvalue(res, 0, 2)); - list_id = atoi(PQgetvalue(res, 0, 3)); - list_desc = PQgetvalue(res, 0, 4); - account = PQgetvalue(res, 0, 5); - password = PQgetvalue(res, 0, 6); - - PQclear(res); - return true; - } - - PQclear(res); - return false; -} - -void DatabasePostgreSQL::UpdateLSAccountData(unsigned int id, string ip_address) -{ - if(!db) - { - return; - } - - /** - * PostgreSQL doesn't have automatic reconnection option like mysql - * but it's easy to check and reconnect - */ - if(PQstatus(db) != CONNECTION_OK) - { - PQreset(db); - if(PQstatus(db) != CONNECTION_OK) - { - return; - } - } - - stringstream query(stringstream::in | stringstream::out); - query << "UPDATE " << server.options.GetAccountTable() << " SET LastIPAddress = '"; - query << ip_address; - query << "', LastLoginDate = current_date where LoginServerID = "; - query << id; - PGresult *res = PQexec(db, query.str().c_str()); - - char *error = PQresultErrorMessage(res); - if(strlen(error) > 0) - { - Log(Logs::General, Logs::Error, "Database error in DatabasePostgreSQL::GetLoginDataFromAccountName(): %s", error); - } - PQclear(res); -} - -void DatabasePostgreSQL::UpdateWorldRegistration(unsigned int id, string long_name, string ip_address) -{ - if(!db) - { - return; - } - - /** - * PostgreSQL doesn't have automatic reconnection option like mysql - * but it's easy to check and reconnect - */ - if(PQstatus(db) != CONNECTION_OK) - { - PQreset(db); - if(PQstatus(db) != CONNECTION_OK) - { - return; - } - } - - stringstream query(stringstream::in | stringstream::out); - query << "UPDATE " << server.options.GetWorldRegistrationTable() << " SET ServerLastLoginDate = current_date, ServerLastIPAddr = '"; - query << ip_address; - query << "', ServerLongName = '"; - query << long_name; - query << "' where ServerID = "; - query << id; - PGresult *res = PQexec(db, query.str().c_str()); - - char *error = PQresultErrorMessage(res); - if(strlen(error) > 0) - { - Log(Logs::General, Logs::Error, "Database error in DatabasePostgreSQL::GetLoginDataFromAccountName(): %s", error); - } - PQclear(res); -} - -#endif - diff --git a/loginserver/database_postgresql.h b/loginserver/database_postgresql.h deleted file mode 100644 index 0cd5c3ee8..000000000 --- a/loginserver/database_postgresql.h +++ /dev/null @@ -1,91 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) - - 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_DATABASEPOSTGRESQL_H -#define EQEMU_DATABASEPOSTGRESQL_H - -#include "database.h" -#ifdef EQEMU_POSTGRESQL_ENABLED - -#include -#include -#include -#include - -/** - * PostgreSQL Database class - */ -class DatabasePostgreSQL : public Database -{ -public: - /** - * Constructor, sets our database to null. - */ - DatabasePostgreSQL() { db = nullptr; } - - /** - * Constructor, tries to set our database to connect to the supplied options. - */ - DatabasePostgreSQL(std::string user, std::string pass, std::string host, std::string port, std::string name); - - /** - * Destructor, frees our database if needed. - */ - virtual ~DatabasePostgreSQL(); - - /** - * Returns true if the database successfully connected. - */ - virtual bool IsConnected() { return (db != nullptr); } - - /** - * Retrieves the login data (password hash and account id) from the account name provided - * Needed for client login procedure. - * Returns true if the record was found, false otherwise. - */ - virtual bool GetLoginDataFromAccountName(std::string name, std::string &password, unsigned int &id); - - /** - * Retrieves the world registration from the long and short names provided. - * Needed for world login procedure. - * Returns true if the record was found, false otherwise. - */ - virtual bool GetWorldRegistration(std::string long_name, std::string short_name, unsigned int &id, std::string &desc, unsigned int &list_id, - unsigned int &trusted, std::string &list_desc, std::string &account, std::string &password); - - /** - * Updates the ip address of the client with account id = id - */ - virtual void UpdateLSAccountData(unsigned int id, std::string ip_address); - - /** - * Updates the ip address of the world with account id = id - */ - virtual void UpdateWorldRegistration(unsigned int id, std::string long_name, std::string ip_address); - - /** - * Creates new world registration for unregistered servers and returns new id - */ - virtual bool CreateWorldRegistration(std::string long_name, std::string short_name, unsigned int &id); -protected: - std::string user, pass, host, port, name; - PGconn *db; -}; - -#endif -#endif - diff --git a/loginserver/encryption.h b/loginserver/encryption.h index fe27ce28a..000a3d0b4 100644 --- a/loginserver/encryption.h +++ b/loginserver/encryption.h @@ -1,7 +1,51 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ + #pragma once #include +#include "../common/types.h" +enum EncryptionMode +{ + EncryptionModeMD5 = 1, + EncryptionModeMD5PassUser = 2, + EncryptionModeMD5UserPass = 3, + EncryptionModeMD5Triple = 4, + EncryptionModeSHA = 5, + EncryptionModeSHAPassUser = 6, + EncryptionModeSHAUserPass = 7, + EncryptionModeSHATriple = 8, + EncryptionModeSHA512 = 9, + EncryptionModeSHA512PassUser = 10, + EncryptionModeSHA512UserPass = 11, + EncryptionModeSHA512Triple = 12, + EncryptionModeArgon2 = 13, + EncryptionModeSCrypt = 14 +}; + +/** + * @param mode + * @return + */ +std::string GetEncryptionByModeId(uint32 mode); const char* eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char* buffer_out, bool enc); std::string eqcrypt_hash(const std::string &username, const std::string &password, int mode); bool eqcrypt_verify_hash(const std::string &username, const std::string &password, const std::string &pwhash, int mode); diff --git a/loginserver/eq_crypto_api.h b/loginserver/eq_crypto_api.h index d722bcb53..bff7b8444 100644 --- a/loginserver/eq_crypto_api.h +++ b/loginserver/eq_crypto_api.h @@ -1,20 +1,23 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ - 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 EQEMUCAPI__H #define EQEMUCAPI__H diff --git a/loginserver/login_server.h b/loginserver/login_server.h index 7760b7efb..e71cc7da0 100644 --- a/loginserver/login_server.h +++ b/loginserver/login_server.h @@ -1,46 +1,53 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) +#include - 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. +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ - 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_LOGINSERVER_H #define EQEMU_LOGINSERVER_H -#include "config.h" +#include "../common/json_config.h" #include "database.h" -#include "database_mysql.h" -#include "database_postgresql.h" #include "encryption.h" #include "options.h" #include "server_manager.h" #include "client_manager.h" +#include "loginserver_webserver.h" /** -* Login server struct, contains every variable for the server that needs to exist -* outside the scope of main(). -*/ + * Login server struct, contains every variable for the server that needs to exist outside the scope of main() + */ struct LoginServer { public: - LoginServer() : config(nullptr), db(nullptr), server_manager(nullptr) { } + LoginServer() : db(nullptr), server_manager(nullptr) { - Config *config; - Database *db; - Options options; - ServerManager *server_manager; - ClientManager *client_manager; + } + + EQ::JsonConfigFile config; + Database *db; + LoginserverWebserver::TokenManager *token_manager{}; + Options options; + ServerManager *server_manager; + ClientManager *client_manager{}; }; #endif diff --git a/loginserver/login_structures.h b/loginserver/login_structures.h index 6e3f290e9..099b4b4f0 100644 --- a/loginserver/login_structures.h +++ b/loginserver/login_structures.h @@ -1,32 +1,35 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ - 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_LOGINSTRUCTURES_H #define EQEMU_LOGINSTRUCTURES_H #pragma pack(1) struct LoginChatMessage_Struct { - short Unknown0; + short Unknown0; uint32 Unknown1; uint32 Unknown2; uint32 Unknown3; - uint8 Unknown4; - char ChatMessage[1]; + uint8 Unknown4; + char ChatMessage[1]; }; struct LoginLoginRequest_Struct { @@ -35,7 +38,7 @@ struct LoginLoginRequest_Struct { short unknown3; short unknown4; short unknown5; - char unknown6[16]; + char unknown6[16]; }; struct LoginAccepted_Struct { @@ -44,28 +47,27 @@ struct LoginAccepted_Struct { short unknown3; short unknown4; short unknown5; - char encrypt[80]; + char encrypt[80]; }; -struct LoginFailedAttempts_Struct -{ - char message; //0x01 - char unknown2[7]; //0x00 +struct LoginFailedAttempts_Struct { + char message; //0x01 + char unknown2[7]; //0x00 uint32 lsid; - char key[11]; //10 char + null term; + char key[11]; //10 char + null term; uint32 failed_attempts; - char unknown3[4]; //0x00, 0x00, 0x00, 0x03 - char unknown4[4]; //0x00, 0x00, 0x00, 0x02 - char unknown5[4]; //0xe7, 0x03, 0x00, 0x00 - char unknown6[4]; //0xff, 0xff, 0xff, 0xff - char unknown7[4]; //0xa0, 0x05, 0x00, 0x00 - char unknown8[4]; //0x00, 0x00, 0x00, 0x02 - char unknown9[4]; //0xff, 0x03, 0x00, 0x00 - char unknown10[4]; //0x00, 0x00, 0x00, 0x00 - char unknown11[4]; //0x63, 0x00, 0x00, 0x00 - char unknown12[4]; //0x01, 0x00, 0x00, 0x00 - char unknown13[4]; //0x00, 0x00, 0x00, 0x00 - char unknown14[4]; //0x00, 0x00, 0x00, 0x00 + char unknown3[4]; //0x00, 0x00, 0x00, 0x03 + char unknown4[4]; //0x00, 0x00, 0x00, 0x02 + char unknown5[4]; //0xe7, 0x03, 0x00, 0x00 + char unknown6[4]; //0xff, 0xff, 0xff, 0xff + char unknown7[4]; //0xa0, 0x05, 0x00, 0x00 + char unknown8[4]; //0x00, 0x00, 0x00, 0x02 + char unknown9[4]; //0xff, 0x03, 0x00, 0x00 + char unknown10[4]; //0x00, 0x00, 0x00, 0x00 + char unknown11[4]; //0x63, 0x00, 0x00, 0x00 + char unknown12[4]; //0x01, 0x00, 0x00, 0x00 + char unknown13[4]; //0x00, 0x00, 0x00, 0x00 + char unknown14[4]; //0x00, 0x00, 0x00, 0x00 }; struct LoginLoginFailed_Struct { @@ -74,7 +76,7 @@ struct LoginLoginFailed_Struct { short unknown3; short unknown4; short unknown5; - char unknown6[74]; + char unknown6[74]; }; struct ServerListHeader_Struct { @@ -86,8 +88,7 @@ struct ServerListHeader_Struct { uint32 NumberOfServers; }; -struct PlayEverquestRequest_Struct -{ +struct PlayEverquestRequest_Struct { uint16 Sequence; uint32 Unknown1; uint32 Unknown2; @@ -95,18 +96,19 @@ struct PlayEverquestRequest_Struct }; struct PlayEverquestResponse_Struct { - uint8 Sequence; - uint8 Unknown1[9]; - uint8 Allowed; + uint8 Sequence; + uint8 Unknown1[9]; + uint8 Allowed; uint16 Message; - uint8 Unknown2[3]; + uint8 Unknown2[3]; uint32 ServerNumber; }; static const unsigned char FailedLoginResponseData[] = { 0xf6, 0x85, 0x9c, 0x23, 0x57, 0x7e, 0x3e, 0x55, 0xb3, 0x4c, 0xf8, 0xc8, 0xcb, 0x77, 0xd5, 0x16, 0x09, 0x7a, 0x63, 0xdc, 0x57, 0x7e, 0x3e, 0x55, 0xb3, 0x4c, 0xf8, 0xc8, 0xcb, 0x77, 0xd5, 0x16, - 0x09, 0x7a, 0x63, 0xdc, 0x57, 0x7e, 0x3e, 0x55, 0xb3 }; + 0x09, 0x7a, 0x63, 0xdc, 0x57, 0x7e, 0x3e, 0x55, 0xb3 +}; #pragma pack() diff --git a/loginserver/login_util/EQEmuLoginServerDBInstall.sql b/loginserver/login_util/EQEmuLoginServerDBInstall.sql deleted file mode 100644 index ae8f6e5e8..000000000 --- a/loginserver/login_util/EQEmuLoginServerDBInstall.sql +++ /dev/null @@ -1,57 +0,0 @@ -DROP TABLE IF EXISTS tblLoginServerAccounts; -CREATE TABLE IF NOT EXISTS tblLoginServerAccounts ( - LoginServerID integer unsigned NOT NULL auto_increment, - AccountName varchar(30) NOT NULL, - AccountPassword varchar(50) NOT NULL, - AccountCreateDate timestamp default CURRENT_TIMESTAMP NOT NULL, - AccountEmail varchar(100) NOT NULL, - LastLoginDate datetime NOT NULL, - LastIPAddress varchar(15) NOT NULL, - PRIMARY KEY (LoginServerID, AccountName) -) ENGINE=InnoDB; - -insert into tblLoginServerAccounts (AccountName, AccountPassword, AccountEmail, LastLoginDate, LastIPAddress) values('Admin', sha('password'), 'admin@somewhere.com', now(), '127.0.0.1'); - -DROP TABLE IF EXISTS tblServerListType; -CREATE TABLE IF NOT EXISTS tblServerListType ( - ServerListTypeID integer unsigned NOT NULL, - ServerListTypeDescription varchar(20) NOT NULL, - PRIMARY KEY (ServerListTypeID) -) ENGINE=MyISAM; - -INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (1, 'Legends'); -INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (2, 'Preferred'); -INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (3, 'Standard'); - -DROP TABLE IF EXISTS tblServerAdminRegistration; -CREATE TABLE IF NOT EXISTS tblServerAdminRegistration ( - ServerAdminID integer unsigned NOT NULL auto_increment, - AccountName varchar(30) NOT NULL, - AccountPassword varchar(30) NOT NULL, - FirstName varchar(40) NOT NULL, - LastName varchar(50) NOT NULL, - Email varchar(100) NULL, - RegistrationDate datetime NOT NULL, - RegistrationIPAddr varchar(15) NOT NULL, - PRIMARY KEY (ServerAdminID, Email) -) ENGINE=MyISAM; - -INSERT INTO tblServerAdminRegistration (AccountName, AccountPassword, FirstName, LastName, Email, RegistrationDate, RegistrationIPAddr) VALUES ('Admin', 'Password', 'Tom', 'Wilson', 'Tom.Wilson@gmail.com', now(), '0.0.0.0'); - -DROP TABLE IF EXISTS tblWorldServerRegistration; -CREATE TABLE IF NOT EXISTS tblWorldServerRegistration ( - ServerID integer unsigned NOT NULL auto_increment, - ServerLongName varchar(100) NOT NULL, - ServerTagDescription varchar(50) NOT NULL DEFAULT '', - ServerShortName varchar(25) NOT NULL, - ServerListTypeID integer NOT NULL, - ServerLastLoginDate datetime NULL, - ServerLastIPAddr varchar(15) NULL, - ServerAdminID integer NOT NULL, - ServerTrusted integer NOT NULL, - Note varchar(300) NULL, - PRIMARY KEY (ServerID, ServerLongName) -) ENGINE=InnoDB; - - -INSERT INTO tblWorldServerRegistration (ServerLongName, ServerTagDescription, ServerShortName, ServerListTypeID, ServerLastLoginDate, ServerLastIPAddr, ServerAdminID, ServerTrusted, Note) VALUES ('My Test Server', 'A test server', 'MTST', 1, now(), '0.0.0.0', 1, 0, 'This is a note for the test server'); diff --git a/loginserver/login_util/EQEmuLoginServerPostgreDBInstall.sql b/loginserver/login_util/EQEmuLoginServerPostgreDBInstall.sql deleted file mode 100644 index bedfbe20d..000000000 --- a/loginserver/login_util/EQEmuLoginServerPostgreDBInstall.sql +++ /dev/null @@ -1,57 +0,0 @@ -DROP TABLE IF EXISTS tblLoginServerAccounts; -CREATE TABLE tblLoginServerAccounts ( - LoginServerID SERIAL, - AccountName text NOT NULL, - AccountPassword text NOT NULL, - AccountCreateDate date NOT NULL, - AccountEmail text NOT NULL, - LastLoginDate date NOT NULL, - LastIPAddress text NOT NULL, - PRIMARY KEY(LoginServerID, AccountName) -); - -insert into tblLoginServerAccounts (AccountName, AccountPassword, AccountEmail, AccountCreateDate, LastLoginDate, LastIPAddress) values('Admin', '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8', 'admin@somewhere.com', current_date, current_date, '127.0.0.1'); - -DROP TABLE IF EXISTS tblServerListType; -CREATE TABLE tblServerListType ( - ServerListTypeID integer NOT NULL, - CHECK (ServerListTypeID >= 0), - ServerListTypeDescription text NOT NULL, - PRIMARY KEY (ServerListTypeID) -); - -INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (1, 'Legends'); -INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (2, 'Preferred'); -INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (3, 'Standard'); - -DROP TABLE IF EXISTS tblServerAdminRegistration; -CREATE TABLE tblServerAdminRegistration ( - ServerAdminID SERIAL, - AccountName text NOT NULL, - AccountPassword text NOT NULL, - FirstName text NOT NULL, - LastName text NOT NULL, - Email text NOT NULL, - RegistrationDate date NOT NULL, - RegistrationIPAddr text NOT NULL, - PRIMARY KEY (ServerAdminID, Email) -); - -INSERT INTO tblServerAdminRegistration (AccountName, AccountPassword, FirstName, LastName, Email, RegistrationDate, RegistrationIPAddr) VALUES ('Admin', 'Password', 'Tom', 'Wilson', 'Tom.Wilson@gmail.com', current_date, '0.0.0.0'); - -DROP TABLE IF EXISTS tblWorldServerRegistration; -CREATE TABLE tblWorldServerRegistration ( - ServerID SERIAL, - ServerLongName text NOT NULL, - ServerTagDescription text NOT NULL, - ServerShortName text NOT NULL, - ServerListTypeID integer NOT NULL, - ServerLastLoginDate date NULL, - ServerLastIPAddr text NOT NULL, - ServerAdminID integer NOT NULL, - ServerTrusted integer NOT NULL, - Note text NOT NULL, - PRIMARY KEY (ServerID, ServerLongName) -); - -INSERT INTO tblWorldServerRegistration (ServerLongName, ServerTagDescription, ServerShortName, ServerListTypeID, ServerLastLoginDate, ServerLastIPAddr, ServerAdminID, ServerTrusted, Note) VALUES ('My Test Server', 'A test server', 'MTST', 1, current_date, '0.0.0.0', 1, 0, 'This is a note for the test server'); \ No newline at end of file diff --git a/loginserver/login_util/login.ini b/loginserver/login_util/login.ini deleted file mode 100644 index bde7616f1..000000000 --- a/loginserver/login_util/login.ini +++ /dev/null @@ -1,35 +0,0 @@ -[database] -host = localhost -port = 3306 -db = eqemu -user = user -password = password -subsystem = MySQL - -[options] -unregistered_allowed = TRUE -reject_duplicate_servers = FALSE -trace = TRUE -world_trace = FALSE -dump_packets_in = FALSE -dump_packets_out = FALSE -listen_port = 5998 -local_network = 192.168.1. - -[security] -plugin = EQEmuAuthCrypto -mode = 5 - -[Titanium] -port = 5998 -opcodes = login_opcodes.conf - -[SoD] -port = 5999 -opcodes = login_opcodes_sod.conf - -[schema] -account_table = tblLoginServerAccounts -world_registration_table = tblWorldServerRegistration -world_admin_registration_table = tblServerAdminRegistration -world_server_type_table = tblServerListType \ No newline at end of file diff --git a/loginserver/login_util/login.json b/loginserver/login_util/login.json new file mode 100644 index 000000000..f6c81ffcc --- /dev/null +++ b/loginserver/login_util/login.json @@ -0,0 +1,39 @@ +{ + "database": { + "host": "mariadb", // database host + "port": "3306", // database port + "db": "peq", // database name + "user": "eqemu", // database user + "password": "eqemu" // database password + }, + "account": { + // ideal for local LAN setups, if you want a login attempt to automatically create an account + // this will automatically create the account using the username and password if it doesn't exist + "auto_create_accounts": true + }, + "worldservers": { + "unregistered_allowed": true, // allows worldservers to connect to your loginserver without server admin authentication + "reject_duplicate_servers": false // if enabled, rejects duplicate worldservers + }, + "web_api": { + "enabled": true, // enable/disable embedded webserver api + "port": 6000 // the port you want the web api to serve on (recommended not to change) + }, + "security": { + "mode": 14, // encryption mode (dont touch) (14=scrypt) + "allow_password_login": true, // allows users to login via password, most cases, leave this on + "allow_token_login": true // allows token based login directly from launching game + }, + "logging": { + "trace": false, // For debugging general packet messaging + "world_trace": false, // For debugging world to loginserver messaging + "dump_packets_in": false, // for debugging inbound packets + "dump_packets_out": false // for debugging outbound packets + }, + "client_configuration": { + "titanium_port": 5998, // don't change + "titanium_opcodes": "login_opcodes.conf", // opcodes for the titanium era clients + "sod_port": 5999, // don't change + "sod_opcodes": "login_opcodes_sod.conf" // opcodes for sod and higher era clients + } +} \ No newline at end of file diff --git a/loginserver/login_util/login_old_to_new_schema_convert.sql b/loginserver/login_util/login_old_to_new_schema_convert.sql new file mode 100644 index 000000000..18af32b25 --- /dev/null +++ b/loginserver/login_util/login_old_to_new_schema_convert.sql @@ -0,0 +1,87 @@ +-- Because the old / legacy schema was mostly inconsistent with naming and overall data structure, we have +-- migrated to a schema that follows our modern conventions and meanwhile fixes quite a few bugs that +-- were present as well + +-- Login Accounts + +INSERT INTO + login_accounts ( + id, + account_name, + account_password, + account_email, + source_loginserver, + last_ip_address, + last_login_date, + created_at + ) +SELECT + LoginServerID, + AccountName, + AccountPassword, + AccountEmail, + 'local', + LastIPAddress, + LastLoginDate, + AccountCreateDate +FROM + tblLoginServerAccounts; + +-- Server Admins + +INSERT INTO + login_server_admins ( + id, + account_name, + account_password, + first_name, + last_name, + email, + registration_date, + registration_ip_address + ) +SELECT + ServerAdminID, + AccountName, + AccountPassword, + FirstName, + LastName, + Email, + RegistrationDate, + RegistrationIPAddr +FROM + tblServerAdminRegistration; + +-- World Servers + +INSERT INTO + login_world_servers ( + id, + long_name, + short_name, + tag_description, + login_server_list_type_id, + last_login_date, + last_ip_address, + login_server_admin_id, + is_server_trusted, + note + ) +SELECT + `ServerID`, + `ServerLongName`, + `ServerShortName`, + `ServerTagDescription`, + `ServerListTypeID`, + `ServerLastLoginDate`, + `ServerLastIPAddr`, + `ServerAdminID`, + `ServerTrusted`, + `Note` +FROM + tblWorldServerRegistration; + +DROP TABLE tblLoginServerAccounts; +DROP TABLE tblServerAdminRegistration; +DROP TABLE tblWorldServerRegistration; +DROP TABLE tblServerListType; diff --git a/loginserver/login_util/login_schema.sql b/loginserver/login_util/login_schema.sql new file mode 100644 index 000000000..b07362830 --- /dev/null +++ b/loginserver/login_util/login_schema.sql @@ -0,0 +1,64 @@ +DROP TABLE IF EXISTS `login_accounts`; +CREATE TABLE `login_accounts` ( + `id` int(11) unsigned NOT NULL, + `account_name` varchar(50) NOT NULL, + `account_password` text NOT NULL, + `account_email` varchar(100) NOT NULL, + `source_loginserver` varchar(64) DEFAULT NULL, + `last_ip_address` varchar(15) NOT NULL, + `last_login_date` datetime NOT NULL, + `created_at` datetime DEFAULT NULL, + `updated_at` datetime DEFAULT current_timestamp(), + PRIMARY KEY (`id`), + UNIQUE KEY `source_loginserver_account_name` (`source_loginserver`,`account_name`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +DROP TABLE IF EXISTS `login_server_admins`; +CREATE TABLE `login_server_admins` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `account_name` varchar(30) NOT NULL, + `account_password` varchar(255) NOT NULL, + `first_name` varchar(50) NOT NULL, + `last_name` varchar(50) NOT NULL, + `email` varchar(100) NOT NULL, + `registration_date` datetime NOT NULL, + `registration_ip_address` varchar(15) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + +DROP TABLE IF EXISTS `login_server_list_types`; +CREATE TABLE `login_server_list_types` ( + `id` int(10) unsigned NOT NULL, + `description` varchar(60) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +INSERT INTO `login_server_list_types` (`id`, `description`) VALUES ('1', 'Legends'), +('2', 'Preferred'), +('3', 'Standard'); + +DROP TABLE IF EXISTS `login_world_servers`; +CREATE TABLE `login_world_servers` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `long_name` varchar(100) NOT NULL, + `short_name` varchar(100) NOT NULL, + `tag_description` varchar(50) NOT NULL DEFAULT '', + `login_server_list_type_id` int(11) NOT NULL, + `last_login_date` datetime DEFAULT NULL, + `last_ip_address` varchar(15) DEFAULT NULL, + `login_server_admin_id` int(11) NOT NULL, + `is_server_trusted` int(11) NOT NULL, + `note` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + +DROP TABLE IF EXISTS `login_api_tokens`; +CREATE TABLE `login_api_tokens` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `token` varchar(200) DEFAULT NULL, + `can_write` int(11) DEFAULT 0, + `can_read` int(11) DEFAULT 0, + `created_at` datetime DEFAULT NULL, + `updated_at` datetime DEFAULT current_timestamp(), + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; \ No newline at end of file diff --git a/loginserver/login_util/tblLoginServerAccounts.sql b/loginserver/login_util/tblLoginServerAccounts.sql deleted file mode 100644 index 65143b38b..000000000 --- a/loginserver/login_util/tblLoginServerAccounts.sql +++ /dev/null @@ -1,11 +0,0 @@ -DROP TABLE IF EXISTS tblLoginServerAccounts; -CREATE TABLE IF NOT EXISTS tblLoginServerAccounts ( - LoginServerID integer unsigned NOT NULL auto_increment, - AccountName varchar(30) NOT NULL, - AccountPassword varchar(50) NOT NULL, - AccountCreateDate timestamp default CURRENT_TIMESTAMP NOT NULL, - AccountEmail varchar(100) NOT NULL, - LastLoginDate datetime NOT NULL, - LastIPAddress varchar(15) NOT NULL, - PRIMARY KEY (LoginServerID, AccountName) -) ENGINE=InnoDB; diff --git a/loginserver/login_util/tblServerAdminRegistration.sql b/loginserver/login_util/tblServerAdminRegistration.sql deleted file mode 100644 index 8e91a5a77..000000000 --- a/loginserver/login_util/tblServerAdminRegistration.sql +++ /dev/null @@ -1,12 +0,0 @@ -DROP TABLE IF EXISTS tblServerAdminRegistration; -CREATE TABLE IF NOT EXISTS tblServerAdminRegistration ( - ServerAdminID integer unsigned NOT NULL auto_increment, - AccountName varchar(30) NOT NULL, - AccountPassword varchar(30) NOT NULL, - FirstName varchar(40) NOT NULL, - LastName varchar(50) NOT NULL, - Email varchar(100) NULL, - RegistrationDate datetime NOT NULL, - RegistrationIPAddr varchar(15) NOT NULL, - PRIMARY KEY (ServerAdminID, Email) -) ENGINE=MyISAM; \ No newline at end of file diff --git a/loginserver/login_util/tblServerListType.sql b/loginserver/login_util/tblServerListType.sql deleted file mode 100644 index fafaffe45..000000000 --- a/loginserver/login_util/tblServerListType.sql +++ /dev/null @@ -1,10 +0,0 @@ -DROP TABLE IF EXISTS tblServerListType; -CREATE TABLE IF NOT EXISTS tblServerListType ( - ServerListTypeID integer unsigned NOT NULL, - ServerListTypeDescription varchar(20) NOT NULL, - PRIMARY KEY (ServerListTypeID) -) ENGINE=MyISAM; - -INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (1, 'Legends'); -INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (2, 'Preferred'); -INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (3, 'Standard'); \ No newline at end of file diff --git a/loginserver/login_util/tblWorldServerRegistration.sql b/loginserver/login_util/tblWorldServerRegistration.sql deleted file mode 100644 index e0ee4dc9f..000000000 --- a/loginserver/login_util/tblWorldServerRegistration.sql +++ /dev/null @@ -1,14 +0,0 @@ -DROP TABLE IF EXISTS tblWorldServerRegistration; -CREATE TABLE IF NOT EXISTS tblWorldServerRegistration ( - ServerID integer unsigned NOT NULL auto_increment, - ServerLongName varchar(100) NOT NULL, - ServerTagDescription varchar(50) NOT NULL DEFAULT '', - ServerShortName varchar(25) NOT NULL, - ServerListTypeID integer NOT NULL, - ServerLastLoginDate datetime NULL, - ServerLastIPAddr varchar(15) NULL, - ServerAdminID integer NOT NULL, - Note varchar(300) NULL, - ServerTrusted int(11), - PRIMARY KEY (ServerID, ServerLongName) -) ENGINE=InnoDB; diff --git a/loginserver/loginserver_command_handler.cpp b/loginserver/loginserver_command_handler.cpp new file mode 100644 index 000000000..d297d519d --- /dev/null +++ b/loginserver/loginserver_command_handler.cpp @@ -0,0 +1,272 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ + +#include +#include +#include "loginserver_command_handler.h" +#include "../common/util/uuid.h" +#include "login_server.h" +#include "loginserver_webserver.h" +#include "account_management.h" + +extern LoginServer server; + +namespace LoginserverCommandHandler { + + /** + * @param argc + * @param argv + */ + void CommandHandler(int argc, char **argv) + { + if (argc == 1) { return; } + + argh::parser cmd; + cmd.parse(argc, argv, argh::parser::PREFER_PARAM_FOR_UNREG_OPTION); + EQEmuCommand::DisplayDebug(cmd); + + /** + * Declare command mapping + */ + auto function_map = EQEmuCommand::function_map; + + /** + * Register commands + */ + function_map["login-user:check-credentials"] = &LoginserverCommandHandler::CheckLoginserverUserCredentials; + function_map["login-user:check-external-credentials"] = &LoginserverCommandHandler::CheckExternalLoginserverUserCredentials; + function_map["login-user:create"] = &LoginserverCommandHandler::CreateLocalLoginserverAccount; + function_map["login-user:update-credentials"] = &LoginserverCommandHandler::UpdateLoginserverUserCredentials; + function_map["web-api-token:create"] = &LoginserverCommandHandler::CreateLoginserverApiToken; + function_map["web-api-token:list"] = &LoginserverCommandHandler::ListLoginserverApiTokens; + function_map["world-admin:create"] = &LoginserverCommandHandler::CreateLoginserverWorldAdminAccount; + + EQEmuCommand::HandleMenu(function_map, cmd, argc, argv); + } + + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void CreateLoginserverApiToken(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Creates Loginserver API Token"; + + if (cmd[{"-h", "--help"}]) { + return; + } + + std::vector arguments = {}; + std::vector options = { + "--read", + "--write" + }; + + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + + bool can_read = cmd[{"-r", "--read"}]; + bool can_write = cmd[{"-w", "--write"}]; + + if (!can_read || !can_write) { + LogInfo("[{0}] --read or --write must be set or both!", __func__); + exit(1); + } + + std::string token = server.db->CreateLoginserverApiToken(can_write, can_read); + if (!token.empty()) { + LogInfo("[{0}] Created Loginserver API token [{1}]", __func__, token); + } + } + + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void ListLoginserverApiTokens(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Lists Loginserver API Tokens"; + + if (cmd[{"-h", "--help"}]) { + return; + } + + for (auto &it : server.token_manager->loaded_api_tokens) { + LogInfo( + "token [{0}] can_write [{1}] can_read [{2}]", + it.second.token, + it.second.can_write, + it.second.can_read + ); + } + } + + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void CreateLocalLoginserverAccount(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Creates Local Loginserver Account"; + + std::vector arguments = { + "--username", + "--password" + }; + std::vector options = { + "--email=*" + }; + + if (cmd[{"-h", "--help"}]) { + return; + } + + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + + AccountManagement::CreateLoginServerAccount( + cmd("--username").str(), + cmd("--password").str(), + cmd("--email").str() + ); + } + + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void CreateLoginserverWorldAdminAccount(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Creates Loginserver World Administrator Account"; + + std::vector arguments = { + "--username", + "--password", + "--email" + }; + std::vector options = {}; + + if (cmd[{"-h", "--help"}]) { + return; + } + + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + + AccountManagement::CreateLoginserverWorldAdminAccount( + cmd("--username").str(), + cmd("--password").str(), + cmd("--email").str() + ); + } + + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void CheckLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Check user login credentials"; + + std::vector arguments = { + "--username", + "--password" + }; + std::vector options = {}; + + if (cmd[{"-h", "--help"}]) { + return; + } + + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + + auto res = AccountManagement::CheckLoginserverUserCredentials( + cmd("--username").str(), + cmd("--password").str() + ); + + LogInfo("Credentials were {0}", res == true ? "accepted" : "not accepted"); + } + + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void UpdateLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Change user login credentials"; + + std::vector arguments = { + "--username", + "--password" + }; + std::vector options = {}; + + if (cmd[{"-h", "--help"}]) { + return; + } + + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + + AccountManagement::UpdateLoginserverUserCredentials( + cmd("--username").str(), + cmd("--password").str() + ); + } + + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void CheckExternalLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Check user external login credentials"; + + std::vector arguments = { + "--username", + "--password" + }; + std::vector options = {}; + + if (cmd[{"-h", "--help"}]) { + return; + } + + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + + auto res = AccountManagement::CheckExternalLoginserverUserCredentials( + cmd("--username").str(), + cmd("--password").str() + ); + + LogInfo("Credentials were {0}", res ? "accepted" : "not accepted"); + } +} diff --git a/loginserver/loginserver_command_handler.h b/loginserver/loginserver_command_handler.h new file mode 100644 index 000000000..c7abc50a6 --- /dev/null +++ b/loginserver/loginserver_command_handler.h @@ -0,0 +1,39 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ + +#include "iostream" +#include "../common/cli/eqemu_command_handler.h" + +#ifndef EQEMU_LOGINSERVER_COMMAND_HANDLER_H +#define EQEMU_LOGINSERVER_COMMAND_HANDLER_H + +namespace LoginserverCommandHandler { + void CommandHandler(int argc, char **argv); + void CreateLoginserverApiToken(int argc, char **argv, argh::parser &cmd, std::string &description); + void ListLoginserverApiTokens(int argc, char **argv, argh::parser &cmd, std::string &description); + void CreateLocalLoginserverAccount(int argc, char **argv, argh::parser &cmd, std::string &description); + void CreateLoginserverWorldAdminAccount(int argc, char **argv, argh::parser &cmd, std::string &description); + void CheckLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); + void UpdateLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); + void CheckExternalLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); +}; + + +#endif //EQEMU_LOGINSERVER_COMMAND_HANDLER_H diff --git a/loginserver/loginserver_webserver.cpp b/loginserver/loginserver_webserver.cpp new file mode 100644 index 000000000..38a3508f6 --- /dev/null +++ b/loginserver/loginserver_webserver.cpp @@ -0,0 +1,533 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ + +#include "loginserver_webserver.h" +#include "server_manager.h" +#include "login_server.h" +#include "../common/json/json.h" +#include "../common/string_util.h" +#include "account_management.h" + +extern LoginServer server; + +namespace LoginserverWebserver { + + constexpr static int HTTP_RESPONSE_OK = 200; + constexpr static int HTTP_RESPONSE_BAD_REQUEST = 400; + constexpr static int HTTP_RESPONSE_UNAUTHORIZED = 401; + + /** + * @param api + */ + void RegisterRoutes(httplib::Server &api) + { + server.token_manager = new LoginserverWebserver::TokenManager; + server.token_manager->LoadApiTokens(); + + api.Get( + "/v1/servers/list", [](const httplib::Request &request, httplib::Response &res) { + if (!LoginserverWebserver::TokenManager::AuthCanRead(request, res)) { + return; + } + + Json::Value response; + auto iter = server.server_manager->getWorldServers().begin(); + while (iter != server.server_manager->getWorldServers().end()) { + Json::Value row; + row["server_long_name"] = (*iter)->GetServerLongName(); + row["server_short_name"] = (*iter)->GetServerLongName(); + row["server_list_id"] = (*iter)->GetServerListID(); + row["server_status"] = (*iter)->GetStatus(); + row["zones_booted"] = (*iter)->GetZonesBooted(); + row["local_ip"] = (*iter)->GetLocalIP(); + row["remote_ip"] = (*iter)->GetRemoteIP(); + row["players_online"] = (*iter)->GetPlayersOnline(); + response.append(row); + ++iter; + } + + LoginserverWebserver::SendResponse(response, res); + } + ); + + api.Post( + "/v1/account/create", [](const httplib::Request &request, httplib::Response &res) { + if (!LoginserverWebserver::TokenManager::AuthCanWrite(request, res)) { + return; + } + + Json::Value request_body = LoginserverWebserver::ParseRequestBody(request); + std::string username = request_body.get("username", "").asString(); + std::string password = request_body.get("password", "").asString(); + std::string email = request_body.get("email", "").asString(); + + Json::Value response; + if (username.empty() || password.empty()) { + response["error"] = "Username or password not set"; + res.status = HTTP_RESPONSE_BAD_REQUEST; + LoginserverWebserver::SendResponse(response, res); + return; + } + + int32 account_created_id = AccountManagement::CreateLoginServerAccount(username, password, email); + if (account_created_id > 0) { + response["message"] = "Account created successfully!"; + response["data"]["account_id"] = account_created_id; + } + else if (account_created_id == -1) { + res.status = HTTP_RESPONSE_BAD_REQUEST; + response["error"] = "Account already exists!"; + } + else { + res.status = HTTP_RESPONSE_BAD_REQUEST; + response["error"] = "Account failed to create!"; + } + + LoginserverWebserver::SendResponse(response, res); + } + ); + + api.Post( + "/v1/account/create/external", [](const httplib::Request &request, httplib::Response &res) { + if (!LoginserverWebserver::TokenManager::AuthCanWrite(request, res)) { + return; + } + + Json::Value request_body = LoginserverWebserver::ParseRequestBody(request); + std::string username = request_body.get("username", "").asString(); + std::string password = request_body.get("password", "").asString(); + std::string email = request_body.get("email", "").asString(); + uint32 login_account_id = request_body.get("login_account_id", "").asInt(); + + Json::Value response; + if (username.empty() || password.empty()) { + res.status = HTTP_RESPONSE_BAD_REQUEST; + response["error"] = "Username or password not set"; + LoginserverWebserver::SendResponse(response, res); + return; + } + + std::string source_loginserver = "eqemu"; + int32 account_created_id = AccountManagement::CreateLoginServerAccount( + username, + password, + email, + source_loginserver, + login_account_id + ); + + if (account_created_id > 0) { + response["message"] = "Account created successfully!"; + response["data"]["account_id"] = account_created_id; + } + else if (account_created_id == -1) { + res.status = HTTP_RESPONSE_BAD_REQUEST; + response["error"] = "Account already exists!"; + } + else { + res.status = HTTP_RESPONSE_BAD_REQUEST; + response["error"] = "Account failed to create!"; + } + + LoginserverWebserver::SendResponse(response, res); + } + ); + + api.Post( + "/v1/account/credentials/validate/local", [](const httplib::Request &request, httplib::Response &res) { + if (!LoginserverWebserver::TokenManager::AuthCanRead(request, res)) { + return; + } + + Json::Value request_body = LoginserverWebserver::ParseRequestBody(request); + std::string username = request_body.get("username", "").asString(); + std::string password = request_body.get("password", "").asString(); + + Json::Value response; + if (username.empty() || password.empty()) { + res.status = HTTP_RESPONSE_BAD_REQUEST; + response["error"] = "Username or password not set"; + LoginserverWebserver::SendResponse(response, res); + return; + } + + uint32 login_account_id = AccountManagement::CheckLoginserverUserCredentials( + username, + password + ); + + if (login_account_id > 0) { + response["message"] = "Credentials valid!"; + response["data"]["account_id"] = login_account_id; + } + else { + res.status = HTTP_RESPONSE_BAD_REQUEST; + response["error"] = "Credentials invalid!"; + } + + LoginserverWebserver::SendResponse(response, res); + } + ); + + api.Post( + "/v1/account/credentials/update/local", [](const httplib::Request &request, httplib::Response &res) { + if (!LoginserverWebserver::TokenManager::AuthCanWrite(request, res)) { + return; + } + + Json::Value request_body = LoginserverWebserver::ParseRequestBody(request); + std::string username = request_body.get("username", "").asString(); + std::string password = request_body.get("password", "").asString(); + + Json::Value response; + if (username.empty() || password.empty()) { + res.status = HTTP_RESPONSE_BAD_REQUEST; + response["error"] = "Username or password not set"; + LoginserverWebserver::SendResponse(response, res); + return; + } + + Database::DbLoginServerAccount + login_server_account = server.db->GetLoginServerAccountByAccountName( + username + ); + + if (!login_server_account.loaded) { + res.status = HTTP_RESPONSE_BAD_REQUEST; + response["error"] = "Failed to find associated loginserver account!"; + LoginserverWebserver::SendResponse(response, res); + return; + } + + bool credentials_valid = AccountManagement::UpdateLoginserverUserCredentials( + username, + password + ); + + if (credentials_valid) { + response["message"] = "Loginserver account credentials updated!"; + } + else { + res.status = HTTP_RESPONSE_BAD_REQUEST; + response["error"] = "Failed to update loginserver account credentials!"; + } + + LoginserverWebserver::SendResponse(response, res); + } + ); + + + api.Post( + "/v1/account/credentials/update/external", [](const httplib::Request &request, httplib::Response &res) { + if (!LoginserverWebserver::TokenManager::AuthCanWrite(request, res)) { + return; + } + + Json::Value request_body = LoginserverWebserver::ParseRequestBody(request); + std::string username = request_body.get("username", "").asString(); + std::string password = request_body.get("password", "").asString(); + + Json::Value response; + if (username.empty() || password.empty()) { + response["error"] = "Username or password not set"; + LoginserverWebserver::SendResponse(response, res); + return; + } + + std::string source_loginserver = "eqemu"; + + Database::DbLoginServerAccount + login_server_account = server.db->GetLoginServerAccountByAccountName( + username, + source_loginserver + ); + + if (!login_server_account.loaded) { + response["error"] = "Failed to find associated loginserver account!"; + LoginserverWebserver::SendResponse(response, res); + return; + } + + bool credentials_valid = AccountManagement::UpdateLoginserverUserCredentials( + username, + password, + source_loginserver + ); + + if (credentials_valid) { + response["message"] = "Loginserver account credentials updated!"; + } + else { + response["error"] = "Failed to update loginserver account credentials!"; + } + + LoginserverWebserver::SendResponse(response, res); + } + ); + + api.Post( + "/v1/account/credentials/validate/external", [](const httplib::Request &request, httplib::Response &res) { + if (!LoginserverWebserver::TokenManager::AuthCanRead(request, res)) { + return; + } + + Json::Value request_body = LoginserverWebserver::ParseRequestBody(request); + std::string username = request_body.get("username", "").asString(); + std::string password = request_body.get("password", "").asString(); + + Json::Value response; + if (username.empty() || password.empty()) { + response["error"] = "Username or password not set"; + LoginserverWebserver::SendResponse(response, res); + return; + } + + uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials( + username, + password + ); + + if (account_id > 0) { + response["message"] = "Credentials valid!"; + response["data"]["account_id"] = account_id; + } + else { + response["error"] = "Credentials invalid!"; + res.status = HTTP_RESPONSE_BAD_REQUEST; + } + + LoginserverWebserver::SendResponse(response, res); + } + ); + } + + /** + * @param payload + * @param res + */ + void SendResponse(const Json::Value &payload, httplib::Response &res) + { + if (res.get_header_value("response_set") == "true") { + res.set_header("response_set", ""); + return; + } + + std::stringstream response_payload; + + if (payload.empty()) { + Json::Value response; + response["message"] = "There were no results found"; + response_payload << response; + res.set_content(response_payload.str(), "application/json"); + return; + } + + response_payload << payload; + res.set_content(response_payload.str(), "application/json"); + } + + /** + * @param payload + * @param res + */ + Json::Value ParseRequestBody(const httplib::Request &request) + { + Json::Value request_body; + + try { + std::stringstream ss; + ss.str(request.body); + ss >> request_body; + } + catch (std::exception &) { + request_body["error"] = "Payload invalid"; + + return request_body; + } + + return request_body; + } + + /** + * @param request + * @param res + */ + bool LoginserverWebserver::TokenManager::AuthCanRead(const httplib::Request &request, httplib::Response &res) + { + LoginserverWebserver::TokenManager::token_data + user_token = LoginserverWebserver::TokenManager::CheckApiAuthorizationHeaders(request); + + if (!user_token.can_read) { + Json::Value response; + std::stringstream response_payload; + response["message"] = "Authorization token is either invalid or cannot read!"; + response_payload << response; + + res.status = HTTP_RESPONSE_UNAUTHORIZED; + res.set_content(response_payload.str(), "application/json"); + res.set_header("response_set", "true"); + + LogWarning( + "AuthCanRead access failure remote_address [{0}] user_agent [{1}]", + user_token.remote_address, + user_token.user_agent + ); + + return false; + } + + return true; + } + + /** + * @param request + * @param res + */ + bool LoginserverWebserver::TokenManager::AuthCanWrite(const httplib::Request &request, httplib::Response &res) + { + LoginserverWebserver::TokenManager::token_data + user_token = LoginserverWebserver::TokenManager::CheckApiAuthorizationHeaders(request); + + if (!user_token.can_write) { + Json::Value response; + std::stringstream response_payload; + response["message"] = "Authorization token is either invalid or cannot write!"; + response_payload << response; + + res.status = HTTP_RESPONSE_UNAUTHORIZED; + res.set_content(response_payload.str(), "application/json"); + res.set_header("response_set", "true"); + + LogWarning( + "AuthCanWrite access failure remote_address [{0}] user_agent [{1}]", + user_token.remote_address, + user_token.user_agent + ); + + return false; + } + + return true; + } + + /** + * @param request + * @return + */ + LoginserverWebserver::TokenManager::token_data + LoginserverWebserver::TokenManager::CheckApiAuthorizationHeaders( + const httplib::Request &request + ) + { + std::string authorization_key; + + LoginserverWebserver::TokenManager::token_data user_token{}; + + for (const auto &header : request.headers) { + auto header_key = header.first; + auto header_value = header.second; + if (header_key == "Authorization") { + authorization_key = header.second; + find_replace(authorization_key, "Bearer ", ""); + if (LoginserverWebserver::TokenManager::TokenExists(authorization_key)) { + user_token = server.token_manager->GetToken(authorization_key); + } + } + + if (header_key == "REMOTE_ADDR") { + user_token.remote_address = header.second; + } + + if (header_key == "User-Agent") { + user_token.user_agent = header.second; + } + } + + LogDebug( + "Authentication Request | remote_address [{0}] user_agent [{1}] authorization_key [{2}] request_path [{3}]", + user_token.remote_address, + user_token.user_agent, + authorization_key, + request.path + ); + + return user_token; + } + + /** + * Loads API Tokens + */ + void TokenManager::LoadApiTokens() + { + auto results = server.db->GetLoginserverApiTokens(); + int token_count = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + LoginserverWebserver::TokenManager::token_data token_data; + token_data.token = row[0]; + token_data.can_write = std::stoi(row[1]) > 0; + token_data.can_read = std::stoi(row[2]) > 0; + + LogDebug( + "Inserting api token to internal list [{0}] write {1} read {2}", + token_data.token, + token_data.can_read, + token_data.can_write + ); + + server.token_manager->loaded_api_tokens.insert( + std::make_pair( + token_data.token, + token_data + ) + ); + + token_count++; + } + + LogInfo("Loaded [{}] API token(s)", token_count); + } + + /** + * @param token + * @return + */ + bool TokenManager::TokenExists(const std::string &token) + { + auto it = server.token_manager->loaded_api_tokens.find(token); + + return !(it == server.token_manager->loaded_api_tokens.end()); + } + + /** + * @param token + * @return + */ + LoginserverWebserver::TokenManager::token_data TokenManager::GetToken( + const std::string &token + ) + { + auto iter = server.token_manager->loaded_api_tokens.find(token); + if (iter != server.token_manager->loaded_api_tokens.end()) { + return iter->second; + } + + return {}; + } +} diff --git a/loginserver/loginserver_webserver.h b/loginserver/loginserver_webserver.h new file mode 100644 index 000000000..b0cdb88c2 --- /dev/null +++ b/loginserver/loginserver_webserver.h @@ -0,0 +1,58 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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_LOGINSERVER_WEBSERVER_H +#define EQEMU_LOGINSERVER_WEBSERVER_H + +#include "../common/http/httplib.h" +#include "../common/json/json.h" +#include "../common/types.h" + +namespace LoginserverWebserver { + + class TokenManager { + + public: + TokenManager() = default; + + struct token_data { + std::string token; + bool can_read; + bool can_write; + std::string user_agent; + std::string remote_address; + }; + + std::map loaded_api_tokens{}; + + void LoadApiTokens(); + static bool TokenExists(const std::string &token); + token_data GetToken(const std::string &token); + static token_data CheckApiAuthorizationHeaders(const httplib::Request &request); + static bool AuthCanRead(const httplib::Request &request, httplib::Response &res); + static bool AuthCanWrite(const httplib::Request &request, httplib::Response &res); + }; + + void RegisterRoutes(httplib::Server &api); + void SendResponse(const Json::Value &payload, httplib::Response &res); + static Json::Value ParseRequestBody(const httplib::Request &request); +}; + +#endif //EQEMU_LOGINSERVER_WEBSERVER_H diff --git a/loginserver/main.cpp b/loginserver/main.cpp index a77ec395f..6941ad7e6 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -1,20 +1,23 @@ -/* EQEMu: Everquest Server Emulator -Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ -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 -*/ #include "../common/global_define.h" #include "../common/types.h" #include "../common/opcodemgr.h" @@ -23,7 +26,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/platform.h" #include "../common/crash.h" #include "../common/eqemu_logsys.h" +#include "../common/http/httplib.h" #include "login_server.h" +#include "loginserver_webserver.h" +#include "loginserver_command_handler.h" #include #include #include @@ -31,167 +37,209 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA LoginServer server; EQEmuLogSys LogSys; -bool run_server = true; +bool run_server = true; void CatchSignal(int sig_num) { } -int main() +int main(int argc, char **argv) { RegisterExecutablePlatform(ExePlatformLogin); set_exception_handler(); - LogSys.LoadLogSettingsDefaults(); - LogSys.log_settings[Logs::Error].log_to_console = Logs::General; - LogSys.log_settings[Logs::Error].is_category_enabled = 1; + LogInfo("Logging System Init"); - Log(Logs::General, Logs::Login_Server, "Logging System Init."); - - /* Parse out login.ini */ - server.config = new Config(); - Log(Logs::General, Logs::Login_Server, "Config System Init."); - server.config->Parse("login.ini"); - - if (server.config->GetVariable("options", "unregistered_allowed").compare("FALSE") == 0) - server.options.AllowUnregistered(false); - - if (server.config->GetVariable("options", "trace").compare("TRUE") == 0) - server.options.Trace(true); - - if (server.config->GetVariable("options", "world_trace").compare("TRUE") == 0) - server.options.WorldTrace(true); - - if (server.config->GetVariable("options", "dump_packets_in").compare("TRUE") == 0) - server.options.DumpInPackets(true); - - if (server.config->GetVariable("options", "dump_packets_out").compare("TRUE") == 0) - server.options.DumpOutPackets(true); - - if (server.config->GetVariable("security", "allow_token_login").compare("TRUE") == 0) - server.options.AllowTokenLogin(true); - - if (server.config->GetVariable("security", "allow_password_login").compare("FALSE") == 0) - server.options.AllowPasswordLogin(false); - - if (server.config->GetVariable("options", "auto_create_accounts").compare("TRUE") == 0) - server.options.AutoCreateAccounts(true); - - std::string mode = server.config->GetVariable("security", "mode"); - if (mode.size() > 0) - server.options.EncryptionMode(atoi(mode.c_str())); - - std::string local_network = server.config->GetVariable("options", "local_network"); - if (local_network.size() > 0) - server.options.LocalNetwork(local_network); - - if (server.config->GetVariable("options", "reject_duplicate_servers").compare("TRUE") == 0) - server.options.RejectDuplicateServers(true); - - local_network = server.config->GetVariable("schema", "account_table"); - if (local_network.size() > 0) - server.options.AccountTable(local_network); - - local_network = server.config->GetVariable("schema", "world_registration_table"); - if (local_network.size() > 0) - server.options.WorldRegistrationTable(local_network); - - local_network = server.config->GetVariable("schema", "world_admin_registration_table"); - if (local_network.size() > 0) - server.options.WorldAdminRegistrationTable(local_network); - - local_network = server.config->GetVariable("schema", "world_server_type_table"); - if (local_network.size() > 0) - server.options.WorldServerTypeTable(local_network); - - /* Create database connection */ - if (server.config->GetVariable("database", "subsystem").compare("MySQL") == 0) { -#ifdef EQEMU_MYSQL_ENABLED - Log(Logs::General, Logs::Login_Server, "MySQL Database Init."); - server.db = (Database*)new DatabaseMySQL( - server.config->GetVariable("database", "user"), - server.config->GetVariable("database", "password"), - server.config->GetVariable("database", "host"), - server.config->GetVariable("database", "port"), - server.config->GetVariable("database", "db")); -#endif - } - else if (server.config->GetVariable("database", "subsystem").compare("PostgreSQL") == 0) { -#ifdef EQEMU_POSTGRESQL_ENABLED - Log(Logs::General, Logs::Login_Server, "PostgreSQL Database Init."); - server.db = (Database*)new DatabasePostgreSQL( - server.config->GetVariable("database", "user"), - server.config->GetVariable("database", "password"), - server.config->GetVariable("database", "host"), - server.config->GetVariable("database", "port"), - server.config->GetVariable("database", "db")); -#endif + if (argc == 1) { + LogSys.LoadLogSettingsDefaults(); } - /* Make sure our database got created okay, otherwise cleanup and exit. */ + server.config = EQ::JsonConfigFile::Load("login.json"); + LogInfo("Config System Init"); + + /** + * options: logging + */ + server.options.Trace(server.config.GetVariableBool("logging", "trace", false)); + server.options.WorldTrace(server.config.GetVariableBool("logging", "world_trace", false)); + server.options.DumpInPackets(server.config.GetVariableBool("logging", "dump_packets_in", false)); + server.options.DumpOutPackets(server.config.GetVariableBool("logging", "dump_packets_out", false)); + + /** + * options: worldservers + */ + server.options.RejectDuplicateServers( + server.config.GetVariableBool( + "worldservers", + "reject_duplicate_servers", + false + )); + server.options.AllowUnregistered(server.config.GetVariableBool("worldservers", "unregistered_allowed", true)); + + /** + * options: account + */ + server.options.AutoCreateAccounts(server.config.GetVariableBool("account", "auto_create_accounts", true)); + server.options.AutoLinkAccounts(server.config.GetVariableBool("account", "auto_link_accounts", false)); + +#ifdef LSPX + server.options.EQEmuLoginServerAddress( + server.config.GetVariableString( + "general", + "eqemu_loginserver_address", + "login.eqemulator.net:5999" + ) + ); +#endif + + server.options.DefaultLoginServerName( + server.config.GetVariableString( + "general", + "default_loginserver_name", + "local" + ) + ); + +#ifdef ENABLE_SECURITY + server.options.EncryptionMode(server.config.GetVariableInt("security", "mode", 13)); +#else + server.options.EncryptionMode(server.config.GetVariableInt("security", "mode", 6)); +#endif + + server.options.AllowTokenLogin(server.config.GetVariableBool("security", "allow_token_login", false)); + server.options.AllowPasswordLogin(server.config.GetVariableBool("security", "allow_password_login", true)); + server.options.UpdateInsecurePasswords( + server.config.GetVariableBool( + "security", + "update_insecure_passwords", + true + ) + ); + + /** + * mysql connect + */ + LogInfo("MySQL Database Init"); + + server.db = new Database( + server.config.GetVariableString("database", "user", "root"), + server.config.GetVariableString("database", "password", ""), + server.config.GetVariableString("database", "host", "localhost"), + server.config.GetVariableString("database", "port", "3306"), + server.config.GetVariableString("database", "db", "peq") + ); + + if (argc == 1) { + server.db->LoadLogSettings(LogSys.log_settings); + LogSys.StartFileLogs(); + } + + /** + * make sure our database got created okay, otherwise cleanup and exit + */ if (!server.db) { - Log(Logs::General, Logs::Error, "Database Initialization Failure."); - Log(Logs::General, Logs::Login_Server, "Config System Shutdown."); - delete server.config; - Log(Logs::General, Logs::Login_Server, "Log System Shutdown."); + LogError("Database Initialization Failure"); + LogInfo("Log System Shutdown"); return 1; } - //create our server manager. - Log(Logs::General, Logs::Login_Server, "Server Manager Initialize."); + /** + * create server manager + */ + LogInfo("Server Manager Init"); server.server_manager = new ServerManager(); if (!server.server_manager) { - //We can't run without a server manager, cleanup and exit. - Log(Logs::General, Logs::Error, "Server Manager Failed to Start."); - - Log(Logs::General, Logs::Login_Server, "Database System Shutdown."); + LogError("Server Manager Failed to Start"); + LogInfo("Database System Shutdown"); delete server.db; - Log(Logs::General, Logs::Login_Server, "Config System Shutdown."); - delete server.config; return 1; } - //create our client manager. - Log(Logs::General, Logs::Login_Server, "Client Manager Initialize."); - server.client_manager = new ClientManager(); + /** + * create client manager + */ + LogInfo("Client Manager Init"); + server.client_manager = new ClientManager(); if (!server.client_manager) { - //We can't run without a client manager, cleanup and exit. - Log(Logs::General, Logs::Error, "Client Manager Failed to Start."); - Log(Logs::General, Logs::Login_Server, "Server Manager Shutdown."); + LogError("Client Manager Failed to Start"); + LogInfo("Server Manager Shutdown"); delete server.server_manager; - Log(Logs::General, Logs::Login_Server, "Database System Shutdown."); + LogInfo("Database System Shutdown"); delete server.db; - Log(Logs::General, Logs::Login_Server, "Config System Shutdown."); - delete server.config; return 1; } #ifdef WIN32 #ifdef UNICODE - SetConsoleTitle(L"EQEmu Login Server"); + SetConsoleTitle(L"EQEmu Login Server"); #else - SetConsoleTitle("EQEmu Login Server"); + SetConsoleTitle("EQEmu Login Server"); #endif #endif - Log(Logs::General, Logs::Login_Server, "Server Started."); + LogInfo("Server Started"); + if (LogSys.log_settings[Logs::Loginserver].log_to_console == 1) { + LogInfo("Loginserver logging set to level [1] for more debugging, enable detail [3]"); + } + + /** + * Web API + */ + httplib::Server api; + int web_api_port = server.config.GetVariableInt("web_api", "port", 6000); + bool web_api_enabled = server.config.GetVariableBool("web_api", "enabled", true); + if (web_api_enabled) { + api.bind("0.0.0.0", web_api_port); + LogInfo("Webserver API now listening on port [{0}]", web_api_port); + LoginserverWebserver::RegisterRoutes(api); + } + + if (argc > 1) { + LogSys.LoadLogSettingsDefaults(); + LogSys.log_settings[Logs::Debug].log_to_console = static_cast(Logs::General); + LogSys.log_settings[Logs::Debug].is_category_enabled = 1; + + LoginserverCommandHandler::CommandHandler(argc, argv); + } + + LogInfo("[Config] [Logging] IsTraceOn [{0}]", server.options.IsTraceOn()); + LogInfo("[Config] [Logging] IsWorldTraceOn [{0}]", server.options.IsWorldTraceOn()); + LogInfo("[Config] [Logging] IsDumpInPacketsOn [{0}]", server.options.IsDumpInPacketsOn()); + LogInfo("[Config] [Logging] IsDumpOutPacketsOn [{0}]", server.options.IsDumpOutPacketsOn()); + LogInfo("[Config] [Account] CanAutoCreateAccounts [{0}]", server.options.CanAutoCreateAccounts()); +#ifdef LSPX + LogInfo("[Config] [Account] CanAutoLinkAccounts [{0}]", server.options.CanAutoLinkAccounts()); +#endif + LogInfo("[Config] [WorldServer] IsRejectingDuplicateServers [{0}]", server.options.IsRejectingDuplicateServers()); + LogInfo("[Config] [WorldServer] IsUnregisteredAllowed [{0}]", server.options.IsUnregisteredAllowed()); + LogInfo("[Config] [Security] GetEncryptionMode [{0}]", server.options.GetEncryptionMode()); + LogInfo("[Config] [Security] IsTokenLoginAllowed [{0}]", server.options.IsTokenLoginAllowed()); + LogInfo("[Config] [Security] IsPasswordLoginAllowed [{0}]", server.options.IsPasswordLoginAllowed()); + LogInfo("[Config] [Security] IsUpdatingInsecurePasswords [{0}]", server.options.IsUpdatingInsecurePasswords()); + while (run_server) { Timer::SetCurrentTime(); server.client_manager->Process(); EQ::EventLoop::Get().Process(); + + if (web_api_enabled) { + api.poll(); + } + Sleep(5); } - Log(Logs::General, Logs::Login_Server, "Server Shutdown."); - Log(Logs::General, Logs::Login_Server, "Client Manager Shutdown."); + LogInfo("Server Shutdown"); + + LogInfo("Client Manager Shutdown"); delete server.client_manager; - Log(Logs::General, Logs::Login_Server, "Server Manager Shutdown."); + + LogInfo("Server Manager Shutdown"); delete server.server_manager; - Log(Logs::General, Logs::Login_Server, "Database System Shutdown."); + LogInfo("Database System Shutdown"); delete server.db; - Log(Logs::General, Logs::Login_Server, "Config System Shutdown."); - delete server.config; + return 0; } diff --git a/loginserver/options.h b/loginserver/options.h index 5aecbad0a..b774a88a0 100644 --- a/loginserver/options.h +++ b/loginserver/options.h @@ -1,20 +1,23 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ - 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_OPTIONS_H #define EQEMU_OPTIONS_H @@ -22,23 +25,22 @@ * Collects options on one object, because having a bunch of global variables floating around is * really ugly and just a little dangerous. */ -class Options -{ +class Options { public: + /** - * Constructor, sets the default options. - */ + * Constructor: Default options + */ Options() : allow_unregistered(true), trace(false), dump_in_packets(false), dump_out_packets(false), encryption_mode(5), - local_network("127.0.0.1"), reject_duplicate_servers(false), allow_password_login(true), allow_token_login(false), - auto_create_accounts(false) { } + auto_create_accounts(false) {} /** * Sets allow_unregistered. @@ -100,56 +102,6 @@ public: */ inline int GetEncryptionMode() const { return encryption_mode; } - /** - * Sets local_network. - */ - inline void LocalNetwork(std::string n) { local_network = n; } - - /** - * Return the value of local_network. - */ - inline std::string GetLocalNetwork() const { return local_network; } - - /** - * Sets account table. - */ - inline void AccountTable(std::string t) { account_table = t; } - - /** - * Return the value of world account table. - */ - inline std::string GetAccountTable() const { return account_table; } - - /** - * Sets world registration table. - */ - inline void WorldRegistrationTable(std::string t) { world_registration_table = t; } - - /** - * Return the value of world registration table. - */ - inline std::string GetWorldRegistrationTable() const { return world_registration_table; } - - /** - * Sets world admin account table. - */ - inline void WorldAdminRegistrationTable(std::string t) { world_admin_registration_table = t; } - - /** - * Return the value of world admin account table. - */ - inline std::string GetWorldAdminRegistrationTable() const { return world_admin_registration_table; } - - /** - * Sets world server type table. - */ - inline void WorldServerTypeTable(std::string t) { world_server_type_table = t; } - - /** - * Return the value of world server type table. - */ - inline std::string GetWorldServerTypeTable() const { return world_server_type_table; } - /** * Sets whether we are rejecting duplicate servers or not. */ @@ -169,22 +121,33 @@ public: inline void AutoCreateAccounts(bool b) { auto_create_accounts = b; } inline bool CanAutoCreateAccounts() const { return auto_create_accounts; } + inline void AutoLinkAccounts(bool b) { auto_link_accounts = b; } + inline bool CanAutoLinkAccounts() const { return auto_link_accounts; } + + inline void EQEmuLoginServerAddress(std::string v) { eqemu_loginserver_address = v; } + inline std::string GetEQEmuLoginServerAddress() const { return eqemu_loginserver_address; } + + inline void DefaultLoginServerName(std::string v) { default_loginserver_name = v; } + inline std::string GetDefaultLoginServerName() const { return default_loginserver_name; } + + inline void UpdateInsecurePasswords(bool b) { update_insecure_passwords = b; } + inline bool IsUpdatingInsecurePasswords() const { return update_insecure_passwords; } + private: - bool allow_unregistered; - bool trace; - bool world_trace; - bool dump_in_packets; - bool dump_out_packets; - bool reject_duplicate_servers; - bool allow_token_login; - bool allow_password_login; - bool auto_create_accounts; - int encryption_mode; - std::string local_network; - std::string account_table; - std::string world_registration_table; - std::string world_admin_registration_table; - std::string world_server_type_table; + bool allow_unregistered; + bool trace; + bool world_trace; + bool dump_in_packets; + bool dump_out_packets; + bool reject_duplicate_servers; + bool allow_token_login; + bool allow_password_login; + bool auto_create_accounts; + bool auto_link_accounts; + bool update_insecure_passwords; + int encryption_mode; + std::string eqemu_loginserver_address; + std::string default_loginserver_name; }; #endif diff --git a/loginserver/server_manager.cpp b/loginserver/server_manager.cpp index 58e351c47..5cd01a8f9 100644 --- a/loginserver/server_manager.cpp +++ b/loginserver/server_manager.cpp @@ -1,34 +1,37 @@ -/* EQEMu: Everquest Server Emulator -Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ -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 -*/ #include "server_manager.h" #include "login_server.h" #include "login_structures.h" #include #include "../common/eqemu_logsys.h" -#include "../common/eqemu_logsys_fmt.h" +#include "../common/ip_util.h" extern LoginServer server; -extern bool run_server; +extern bool run_server; ServerManager::ServerManager() { - int listen_port = atoi(server.config->GetVariable("options", "listen_port").c_str()); + int listen_port = server.config.GetVariableInt("general", "listen_port", 5998); server_connection.reset(new EQ::Net::ServertalkServer()); EQ::Net::ServertalkServerOptions opts; @@ -36,50 +39,71 @@ ServerManager::ServerManager() opts.ipv6 = false; server_connection->Listen(opts); - server_connection->OnConnectionIdentified("World", [this](std::shared_ptr c) { - LogF(Logs::General, Logs::Login_Server, "New world server connection from {0}:{1}", c->Handle()->RemoteIP(), c->Handle()->RemotePort()); + LogInfo("Loginserver now listening on port [{0}]", listen_port); - auto iter = world_servers.begin(); - while (iter != world_servers.end()) { - if ((*iter)->GetConnection()->Handle()->RemoteIP().compare(c->Handle()->RemoteIP()) == 0 && - (*iter)->GetConnection()->Handle()->RemotePort() == c->Handle()->RemotePort()) { - LogF(Logs::General, Logs::Login_Server, "World server already existed for {0}:{1}, removing existing connection.", - c->Handle()->RemoteIP(), c->Handle()->RemotePort()); + server_connection->OnConnectionIdentified( + "World", [this](std::shared_ptr world_connection) { + LogInfo( + "New World Server connection from {0}:{1}", + world_connection->Handle()->RemoteIP(), + world_connection->Handle()->RemotePort() + ); - world_servers.erase(iter); - break; + auto iter = world_servers.begin(); + while (iter != world_servers.end()) { + if ((*iter)->GetConnection()->Handle()->RemoteIP().compare(world_connection->Handle()->RemoteIP()) == + 0 && + (*iter)->GetConnection()->Handle()->RemotePort() == world_connection->Handle()->RemotePort()) { + + LogInfo( + "World server already existed for {0}:{1}, removing existing connection.", + world_connection->Handle()->RemoteIP(), + world_connection->Handle()->RemotePort() + ); + + world_servers.erase(iter); + break; + } + + ++iter; } - ++iter; + world_servers.push_back(std::unique_ptr(new WorldServer(world_connection))); } + ); - world_servers.push_back(std::unique_ptr(new WorldServer(c))); - }); + server_connection->OnConnectionRemoved( + "World", [this](std::shared_ptr c) { + auto iter = world_servers.begin(); + while (iter != world_servers.end()) { + if ((*iter)->GetConnection()->GetUUID() == c->GetUUID()) { + LogInfo( + "World server {0} has been disconnected, removing.", + (*iter)->GetServerLongName() + ); + world_servers.erase(iter); + return; + } - server_connection->OnConnectionRemoved("World", [this](std::shared_ptr c) { - auto iter = world_servers.begin(); - while (iter != world_servers.end()) { - if ((*iter)->GetConnection()->GetUUID() == c->GetUUID()) { - LogF(Logs::General, Logs::World_Server, "World server {0} has been disconnected, removing.", (*iter)->GetLongName().c_str()); - world_servers.erase(iter); - return; + ++iter; } - - ++iter; } - }); + ); } -ServerManager::~ServerManager() -{ +ServerManager::~ServerManager() = default; -} - -WorldServer* ServerManager::GetServerByAddress(const std::string &addr, int port) +/** + * @param ip_address + * @param port + * @return + */ +WorldServer *ServerManager::GetServerByAddress(const std::string &ip_address, int port) { auto iter = world_servers.begin(); while (iter != world_servers.end()) { - if ((*iter)->GetConnection()->Handle()->RemoteIP() == addr && (*iter)->GetConnection()->Handle()->RemotePort()) { + if ((*iter)->GetConnection()->Handle()->RemoteIP() == ip_address && + (*iter)->GetConnection()->Handle()->RemotePort()) { return (*iter).get(); } ++iter; @@ -88,40 +112,76 @@ WorldServer* ServerManager::GetServerByAddress(const std::string &addr, int port return nullptr; } -EQApplicationPacket *ServerManager::CreateServerListPacket(Client *c, uint32 seq) +/** + * @param client + * @param sequence + * @return + */ +EQApplicationPacket *ServerManager::CreateServerListPacket(Client *client, uint32 sequence) { - unsigned int packet_size = sizeof(ServerListHeader_Struct); + unsigned int packet_size = sizeof(ServerListHeader_Struct); unsigned int server_count = 0; - in_addr in; - in.s_addr = c->GetConnection()->GetRemoteIP(); + in_addr in{}; + in.s_addr = client->GetConnection()->GetRemoteIP(); std::string client_ip = inet_ntoa(in); + LogDebug("ServerManager::CreateServerListPacket via client address [{0}]", client_ip); + auto iter = world_servers.begin(); while (iter != world_servers.end()) { - if ((*iter)->IsAuthorized() == false) { + if (!(*iter)->IsAuthorized()) { + LogDebug( + "ServerManager::CreateServerListPacket | Server [{0}] via IP [{1}] is not authorized to be listed", + (*iter)->GetServerLongName(), + (*iter)->GetConnection()->Handle()->RemoteIP() + ); ++iter; continue; } std::string world_ip = (*iter)->GetConnection()->Handle()->RemoteIP(); + if (world_ip == client_ip) { + packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetLocalIP().size() + 24; - if (world_ip.compare(client_ip) == 0) { - packet_size += (*iter)->GetLongName().size() + (*iter)->GetLocalIP().size() + 24; + LogDebug( + "CreateServerListPacket | Building list entry | Client [{0}] IP [{1}] Server Long Name [{2}] Server IP [{3}] (Local)", + client->GetAccountName(), + client_ip, + (*iter)->GetServerLongName(), + (*iter)->GetLocalIP() + ); } - else if (client_ip.find(server.options.GetLocalNetwork()) != std::string::npos) { - packet_size += (*iter)->GetLongName().size() + (*iter)->GetLocalIP().size() + 24; + else if (IpUtil::IsIpInPrivateRfc1918(client_ip)) { + packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetLocalIP().size() + 24; + + LogDebug( + "CreateServerListPacket | Building list entry | Client [{0}] IP [{1}] Server Long Name [{2}] Server IP [{3}] (Local)", + client->GetAccountName(), + client_ip, + (*iter)->GetServerLongName(), + (*iter)->GetLocalIP() + ); } else { - packet_size += (*iter)->GetLongName().size() + (*iter)->GetRemoteIP().size() + 24; + packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetRemoteIP().size() + 24; + + LogDebug( + "CreateServerListPacket | Building list entry | Client [{0}] IP [{1}] Server Long Name [{2}] Server IP [{3}] (Remote)", + client->GetAccountName(), + client_ip, + (*iter)->GetServerLongName(), + (*iter)->GetRemoteIP() + ); } server_count++; ++iter; } - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ServerListResponse, packet_size); - ServerListHeader_Struct *server_list = (ServerListHeader_Struct*)outapp->pBuffer; - server_list->Unknown1 = seq; + auto *outapp = new EQApplicationPacket(OP_ServerListResponse, packet_size); + auto *server_list = (ServerListHeader_Struct *) outapp->pBuffer; + + server_list->Unknown1 = sequence; server_list->Unknown2 = 0x00000000; server_list->Unknown3 = 0x01650000; @@ -129,7 +189,7 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *c, uint32 seq * Not sure what this is but it should be noted setting it to * 0xFFFFFFFF crashes the client so: don't do that. */ - server_list->Unknown4 = 0x00000000; + server_list->Unknown4 = 0x00000000; server_list->NumberOfServers = server_count; unsigned char *data_pointer = outapp->pBuffer; @@ -137,17 +197,17 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *c, uint32 seq iter = world_servers.begin(); while (iter != world_servers.end()) { - if ((*iter)->IsAuthorized() == false) { + if (!(*iter)->IsAuthorized()) { ++iter; continue; } std::string world_ip = (*iter)->GetConnection()->Handle()->RemoteIP(); - if (world_ip.compare(client_ip) == 0) { + if (world_ip == client_ip) { memcpy(data_pointer, (*iter)->GetLocalIP().c_str(), (*iter)->GetLocalIP().size()); data_pointer += ((*iter)->GetLocalIP().size() + 1); } - else if (client_ip.find(server.options.GetLocalNetwork()) != std::string::npos) { + else if (IpUtil::IsIpInPrivateRfc1918(client_ip)) { memcpy(data_pointer, (*iter)->GetLocalIP().c_str(), (*iter)->GetLocalIP().size()); data_pointer += ((*iter)->GetLocalIP().size() + 1); } @@ -157,26 +217,26 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *c, uint32 seq } switch ((*iter)->GetServerListID()) { - case 1: { - *(unsigned int*)data_pointer = 0x00000030; - break; - } - case 2: { - *(unsigned int*)data_pointer = 0x00000009; - break; - } - default: { - *(unsigned int*)data_pointer = 0x00000001; - } + case 1: { + *(unsigned int *) data_pointer = 0x00000030; + break; + } + case 2: { + *(unsigned int *) data_pointer = 0x00000009; + break; + } + default: { + *(unsigned int *) data_pointer = 0x00000001; + } } data_pointer += 4; - *(unsigned int*)data_pointer = (*iter)->GetRuntimeID(); + *(unsigned int *) data_pointer = (*iter)->GetServerId(); data_pointer += 4; - memcpy(data_pointer, (*iter)->GetLongName().c_str(), (*iter)->GetLongName().size()); - data_pointer += ((*iter)->GetLongName().size() + 1); + memcpy(data_pointer, (*iter)->GetServerLongName().c_str(), (*iter)->GetServerLongName().size()); + data_pointer += ((*iter)->GetServerLongName().size() + 1); memcpy(data_pointer, "EN", 2); data_pointer += 3; @@ -187,18 +247,18 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *c, uint32 seq // 0 = Up, 1 = Down, 2 = Up, 3 = down, 4 = locked, 5 = locked(down) if ((*iter)->GetStatus() < 0) { if ((*iter)->GetZonesBooted() == 0) { - *(uint32*)data_pointer = 0x01; + *(uint32 *) data_pointer = 0x01; } else { - *(uint32*)data_pointer = 0x04; + *(uint32 *) data_pointer = 0x04; } } else { - *(uint32*)data_pointer = 0x02; + *(uint32 *) data_pointer = 0x02; } data_pointer += 4; - *(uint32*)data_pointer = (*iter)->GetPlayersOnline(); + *(uint32 *) data_pointer = (*iter)->GetPlayersOnline(); data_pointer += 4; ++iter; @@ -207,33 +267,54 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *c, uint32 seq return outapp; } -void ServerManager::SendUserToWorldRequest(unsigned int server_id, unsigned int client_account_id) +/** + * @param server_id + * @param client_account_id + * @param client_loginserver + */ +void ServerManager::SendUserToWorldRequest( + unsigned int server_id, + unsigned int client_account_id, + const std::string &client_loginserver +) { - auto iter = world_servers.begin(); + auto iter = world_servers.begin(); bool found = false; while (iter != world_servers.end()) { - if ((*iter)->GetRuntimeID() == server_id) { + if ((*iter)->GetServerId() == server_id) { EQ::Net::DynamicPacket outapp; outapp.Resize(sizeof(UsertoWorldRequest_Struct)); - UsertoWorldRequest_Struct *utwr = (UsertoWorldRequest_Struct*)outapp.Data(); - utwr->worldid = server_id; - utwr->lsaccountid = client_account_id; + + auto *user_to_world_request = (UsertoWorldRequest_Struct *) outapp.Data(); + user_to_world_request->worldid = server_id; + user_to_world_request->lsaccountid = client_account_id; + strncpy(user_to_world_request->login, &client_loginserver[0], 64); (*iter)->GetConnection()->Send(ServerOP_UsertoWorldReq, outapp); found = true; if (server.options.IsDumpInPacketsOn()) { - LogF(Logs::General, Logs::Login_Server, "{0}", outapp.ToString()); + LogInfo("{0}", outapp.ToString()); } } ++iter; } if (!found && server.options.IsTraceOn()) { - Log(Logs::General, Logs::Error, "Client requested a user to world but supplied an invalid id of %u.", server_id); + LogError("Client requested a user to world but supplied an invalid id of {0}", server_id); } } -bool ServerManager::ServerExists(std::string l_name, std::string s_name, WorldServer *ignore) +/** + * @param server_long_name + * @param server_short_name + * @param ignore + * @return + */ +bool ServerManager::ServerExists( + std::string server_long_name, + std::string server_short_name, + WorldServer *ignore +) { auto iter = world_servers.begin(); while (iter != world_servers.end()) { @@ -242,7 +323,7 @@ bool ServerManager::ServerExists(std::string l_name, std::string s_name, WorldSe continue; } - if ((*iter)->GetLongName().compare(l_name) == 0 && (*iter)->GetShortName().compare(s_name) == 0) { + if ((*iter)->GetServerLongName() == server_long_name && (*iter)->GetServerShortName() == server_short_name) { return true; } @@ -251,7 +332,16 @@ bool ServerManager::ServerExists(std::string l_name, std::string s_name, WorldSe return false; } -void ServerManager::DestroyServerByName(std::string l_name, std::string s_name, WorldServer *ignore) +/** + * @param server_long_name + * @param server_short_name + * @param ignore + */ +void ServerManager::DestroyServerByName( + std::string server_long_name, + std::string server_short_name, + WorldServer *ignore +) { auto iter = world_servers.begin(); while (iter != world_servers.end()) { @@ -260,7 +350,8 @@ void ServerManager::DestroyServerByName(std::string l_name, std::string s_name, continue; } - if ((*iter)->GetLongName().compare(l_name) == 0 && (*iter)->GetShortName().compare(s_name) == 0) { + if ((*iter)->GetServerLongName().compare(server_long_name) == 0 && + (*iter)->GetServerShortName().compare(server_short_name) == 0) { (*iter)->GetConnection()->Handle()->Disconnect(); iter = world_servers.erase(iter); continue; @@ -269,3 +360,11 @@ void ServerManager::DestroyServerByName(std::string l_name, std::string s_name, ++iter; } } + +/** + * @return + */ +const std::list> &ServerManager::getWorldServers() const +{ + return world_servers; +} diff --git a/loginserver/server_manager.h b/loginserver/server_manager.h index f9a1f66e1..8babf8a56 100644 --- a/loginserver/server_manager.h +++ b/loginserver/server_manager.h @@ -1,20 +1,23 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ - 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_SERVERMANAGER_H #define EQEMU_SERVERMANAGER_H @@ -27,14 +30,14 @@ #include /** -* Server manager class, deals with management of the world servers. -*/ -class ServerManager -{ + * Server manager class, deals with management of the world servers + */ +class ServerManager { public: + /** - * Constructor, sets up the TCP server and starts listening. - */ + * Constructor, sets up the TCP server and starts listening + */ ServerManager(); /** @@ -43,34 +46,66 @@ public: ~ServerManager(); /** - * Sends a request to world to see if the client is banned or suspended. - */ - void SendUserToWorldRequest(unsigned int server_id, unsigned int client_account_id); + * Sends a request to world to see if the client is banned or suspended + * + * @param server_id + * @param client_account_id + * @param client_loginserver + */ + void SendUserToWorldRequest( + unsigned int server_id, + unsigned int client_account_id, + const std::string &client_loginserver + ); /** - * Creates a server list packet for the client. - */ - EQApplicationPacket *CreateServerListPacket(Client *c, uint32 seq); + * Creates a server list packet for the client + * + * @param client + * @param sequence + * @return + */ + EQApplicationPacket *CreateServerListPacket(Client *client, uint32 sequence); /** - * Checks to see if there is a server exists with this name, ignoring option. - */ - bool ServerExists(std::string l_name, std::string s_name, WorldServer *ignore = nullptr); + * Checks to see if there is a server exists with this name, ignoring option + * + * @param server_long_name + * @param server_short_name + * @param ignore + * @return + */ + bool ServerExists(std::string server_long_name, std::string server_short_name, WorldServer *ignore = nullptr); /** - * Destroys a server with this name, ignoring option. - */ - void DestroyServerByName(std::string l_name, std::string s_name, WorldServer *ignore = nullptr); + * Destroys a server with this name, ignoring option + * + * @param server_long_name + * @param server_short_name + * @param ignore + */ + void DestroyServerByName(std::string server_long_name, std::string server_short_name, WorldServer *ignore = nullptr); + + /** + * @return + */ + const std::list> &getWorldServers() const; private: + /** - * Retrieves a server(if exists) by ip address - * Useful utility for the reconnect process. - */ - WorldServer* GetServerByAddress(const std::string &address, int port); + * Retrieves a server(if exists) by ip address + * Useful utility for the reconnect process + * + * @param ip_address + * @param port + * @return + */ + WorldServer *GetServerByAddress(const std::string &ip_address, int port); std::unique_ptr server_connection; - std::list> world_servers; + std::list> world_servers; + }; #endif diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index d953ccc3a..c2c99abd8 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -1,537 +1,1243 @@ -/* EQEMu: Everquest Server Emulator -Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ -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 -*/ #include "world_server.h" #include "login_server.h" #include "login_structures.h" -#include "config.h" - #include "../common/eqemu_logsys.h" +#include "../common/ip_util.h" extern LoginServer server; -WorldServer::WorldServer(std::shared_ptr c) +/** + * @param worldserver_connection + */ +WorldServer::WorldServer(std::shared_ptr worldserver_connection) { - connection = c; - zones_booted = 0; - players_online = 0; - server_status = 0; - runtime_id = 0; - server_list_id = 0; - server_type = 0; + connection = worldserver_connection; + zones_booted = 0; + players_online = 0; + server_status = 0; + server_id = 0; + server_list_type_id = 0; + server_process_type = 0; is_server_authorized = false; - is_server_trusted = false; - is_server_logged_in = false; + is_server_trusted = false; + is_server_logged_in = false; - c->OnMessage(ServerOP_NewLSInfo, std::bind(&WorldServer::ProcessNewLSInfo, this, std::placeholders::_1, std::placeholders::_2)); - c->OnMessage(ServerOP_LSStatus, std::bind(&WorldServer::ProcessLSStatus, this, std::placeholders::_1, std::placeholders::_2)); - c->OnMessage(ServerOP_UsertoWorldResp, std::bind(&WorldServer::ProcessUsertoWorldResp, this, std::placeholders::_1, std::placeholders::_2)); - c->OnMessage(ServerOP_LSAccountUpdate, std::bind(&WorldServer::ProcessLSAccountUpdate, this, std::placeholders::_1, std::placeholders::_2)); + worldserver_connection->OnMessage( + ServerOP_NewLSInfo, + std::bind(&WorldServer::ProcessNewLSInfo, this, std::placeholders::_1, std::placeholders::_2) + ); + + worldserver_connection->OnMessage( + ServerOP_LSStatus, + std::bind(&WorldServer::ProcessLSStatus, this, std::placeholders::_1, std::placeholders::_2) + ); + + worldserver_connection->OnMessage( + ServerOP_UsertoWorldRespLeg, + std::bind( + &WorldServer::ProcessUserToWorldResponseLegacy, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + + worldserver_connection->OnMessage( + ServerOP_UsertoWorldResp, + std::bind(&WorldServer::ProcessUserToWorldResponse, this, std::placeholders::_1, std::placeholders::_2) + ); + + worldserver_connection->OnMessage( + ServerOP_LSAccountUpdate, + std::bind(&WorldServer::ProcessLSAccountUpdate, this, std::placeholders::_1, std::placeholders::_2) + ); + + m_keepalive.reset(new EQ::Timer(5000, true, std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1))); } -WorldServer::~WorldServer() -{ - -} +WorldServer::~WorldServer() = default; void WorldServer::Reset() { - zones_booted = 0; - players_online = 0; - server_status = 0; - runtime_id; - server_list_id = 0; - server_type = 0; + server_id; + zones_booted = 0; + players_online = 0; + server_status = 0; + server_list_type_id = 0; + server_process_type = 0; is_server_authorized = false; - is_server_logged_in = false; + is_server_logged_in = false; } -void WorldServer::ProcessNewLSInfo(uint16_t opcode, const EQ::Net::Packet &p) +/** + * @param opcode + * @param packet + */ +void WorldServer::ProcessNewLSInfo(uint16_t opcode, const EQ::Net::Packet &packet) { - if (server.options.IsWorldTraceOn()) - { - Log(Logs::General, Logs::Netcode, "Application packet received from server: 0x%.4X, (size %u)", opcode, p.Length()); + if (server.options.IsWorldTraceOn()) { + LogDebug( + "Application packet received from server: {0}, (size {1})", + opcode, + packet.Length() + ); } - if (server.options.IsDumpInPacketsOn()) - { - DumpPacket(opcode, p); + if (server.options.IsDumpInPacketsOn()) { + DumpPacket(opcode, packet); } - if (p.Length() < sizeof(ServerNewLSInfo_Struct)) - { - Log(Logs::General, Logs::Error, "Received application packet from server that had opcode ServerOP_NewLSInfo, " - "but was too small. Discarded to avoid buffer overrun."); + if (packet.Length() < sizeof(ServerNewLSInfo_Struct)) { + LogError( + "Received application packet from server that had opcode ServerOP_NewLSInfo, " + "but was too small. Discarded to avoid buffer overrun" + ); + return; } - if (server.options.IsWorldTraceOn()) - { - Log(Logs::General, Logs::Netcode, "New Login Info Recieved."); - } - ServerNewLSInfo_Struct *info = (ServerNewLSInfo_Struct*)p.Data(); + auto *info = (ServerNewLSInfo_Struct *) packet.Data(); + + LogInfo( + "New World Server Info | name [{0}] shortname [{1}] remote_address [{2}] local_address [{3}] account [{4}] password [{5}] server_type [{6}]", + info->server_long_name, + info->server_short_name, + info->remote_ip_address, + info->local_ip_address, + info->account_name, + info->account_password, + info->server_process_type + ); + Handle_NewLSInfo(info); } -void WorldServer::ProcessLSStatus(uint16_t opcode, const EQ::Net::Packet &p) +/** + * @param opcode + * @param packet + */ +void WorldServer::ProcessLSStatus(uint16_t opcode, const EQ::Net::Packet &packet) { - if (server.options.IsWorldTraceOn()) - { - Log(Logs::General, Logs::Netcode, "Application packet received from server: 0x%.4X, (size %u)", opcode, p.Length()); + Log( + Logs::Detail, + Logs::Netcode, + "Application packet received from server: 0x%.4X, (size %u)", + opcode, + packet.Length() + ); + + if (server.options.IsDumpInPacketsOn()) { + DumpPacket(opcode, packet); } - if (server.options.IsDumpInPacketsOn()) - { - DumpPacket(opcode, p); - } + if (packet.Length() < sizeof(ServerLSStatus_Struct)) { + LogError( + "Received application packet from server that had opcode ServerOP_LSStatus, but was too small. Discarded to avoid buffer overrun" + ); - if (p.Length() < sizeof(ServerLSStatus_Struct)) - { - Log(Logs::General, Logs::Error, "Recieved application packet from server that had opcode ServerOP_LSStatus, " - "but was too small. Discarded to avoid buffer overrun."); return; } - if (server.options.IsWorldTraceOn()) - { - Log(Logs::General, Logs::Netcode, "World Server Status Recieved."); + auto *ls_status = (ServerLSStatus_Struct *) packet.Data(); + + if (server.options.IsWorldTraceOn()) { + LogDebug( + "World Server Status Update Received | Server [{0}] Status [{1}] Players [{2}] Zones [{3}]", + this->GetServerLongName(), + ls_status->status, + ls_status->num_players, + ls_status->num_zones + ); } - ServerLSStatus_Struct *ls_status = (ServerLSStatus_Struct*)p.Data(); Handle_LSStatus(ls_status); } -void WorldServer::ProcessUsertoWorldResp(uint16_t opcode, const EQ::Net::Packet &p) +/** + * @param opcode + * @param packet + */ +void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Net::Packet &packet) { - if (server.options.IsWorldTraceOn()) - { - Log(Logs::General, Logs::Netcode, "Application packet received from server: 0x%.4X, (size %u)", opcode, p.Length()); + if (server.options.IsWorldTraceOn()) { + LogDebug( + "Application packet received from server: {0}, (size {1})", + opcode, + packet.Length() + ); } - if (server.options.IsDumpInPacketsOn()) - { - DumpPacket(opcode, p); + if (server.options.IsDumpInPacketsOn()) { + DumpPacket(opcode, packet); } - if (p.Length() < sizeof(UsertoWorldResponse_Struct)) - { - Log(Logs::General, Logs::Error, "Recieved application packet from server that had opcode ServerOP_UsertoWorldResp, " - "but was too small. Discarded to avoid buffer overrun."); + if (packet.Length() < sizeof(UsertoWorldResponseLegacy_Struct)) { + LogError( + "Received application packet from server that had opcode ServerOP_UsertoWorldResp, " + "but was too small. Discarded to avoid buffer overrun" + ); + return; } //I don't use world trace for this and here is why: //Because this is a part of the client login procedure it makes tracking client errors //While keeping world server spam with multiple servers connected almost impossible. - if (server.options.IsTraceOn()) - { - Log(Logs::General, Logs::Netcode, "User-To-World Response received."); + if (server.options.IsTraceOn()) { + LogDebug("User-To-World Response received"); } - UsertoWorldResponse_Struct *utwr = (UsertoWorldResponse_Struct*)p.Data(); - Log(Logs::General, Logs::Debug, "Trying to find client with user id of %u.", utwr->lsaccountid); - Client *c = server.client_manager->GetClient(utwr->lsaccountid); - if (c) - { - Log(Logs::General, Logs::Debug, "Found client with user id of %u and account name of %s.", utwr->lsaccountid, c->GetAccountName().c_str()); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_PlayEverquestResponse, sizeof(PlayEverquestResponse_Struct)); - PlayEverquestResponse_Struct *per = (PlayEverquestResponse_Struct*)outapp->pBuffer; - per->Sequence = c->GetPlaySequence(); - per->ServerNumber = c->GetPlayServerID(); - Log(Logs::General, Logs::Debug, "Found sequence and play of %u %u", c->GetPlaySequence(), c->GetPlayServerID()); + auto *user_to_world_response = (UsertoWorldResponseLegacy_Struct *) packet.Data(); - Log(Logs::General, Logs::Netcode, "[Size: %u] %s", outapp->size, DumpPacketToString(outapp).c_str()); + LogDebug("Trying to find client with user id of [{0}]", user_to_world_response->lsaccountid); + Client *client = server.client_manager->GetClient(user_to_world_response->lsaccountid, "eqemu"); + if (client) { - if (utwr->response > 0) - { + LogDebug( + "Found client with user id of [{0}] and account name of [{1}]", + user_to_world_response->lsaccountid, + client->GetAccountName() + ); + + auto *outapp = new EQApplicationPacket( + OP_PlayEverquestResponse, + sizeof(PlayEverquestResponse_Struct) + ); + + auto *per = (PlayEverquestResponse_Struct *) outapp->pBuffer; + per->Sequence = client->GetPlaySequence(); + per->ServerNumber = client->GetPlayServerID(); + + if (user_to_world_response->response > 0) { per->Allowed = 1; - SendClientAuth(c->GetConnection()->GetRemoteAddr(), c->GetAccountName(), c->GetKey(), c->GetAccountID()); + SendClientAuth( + client->GetConnection()->GetRemoteAddr(), + client->GetAccountName(), + client->GetKey(), + client->GetAccountID(), + client->GetLoginServerName() + ); } - switch (utwr->response) - { - case UserToWorldStatusSuccess: - per->Message = 101; - break; - case UserToWorldStatusWorldUnavail: - per->Message = 326; - break; - case UserToWorldStatusSuspended: - per->Message = 337; - break; - case UserToWorldStatusBanned: - per->Message = 338; - break; - case UserToWorldStatusWorldAtCapacity: - per->Message = 339; - break; - case UserToWorldStatusAlreadyOnline: - per->Message = 111; - break; - default: - per->Message = 102; + switch (user_to_world_response->response) { + case UserToWorldStatusSuccess: + per->Message = 101; + break; + case UserToWorldStatusWorldUnavail: + per->Message = 326; + break; + case UserToWorldStatusSuspended: + per->Message = 337; + break; + case UserToWorldStatusBanned: + per->Message = 338; + break; + case UserToWorldStatusWorldAtCapacity: + per->Message = 339; + break; + case UserToWorldStatusAlreadyOnline: + per->Message = 111; + break; + default: + per->Message = 102; } - if (server.options.IsTraceOn()) - { - Log(Logs::General, Logs::Netcode, "Sending play response with following data, allowed %u, sequence %u, server number %u, message %u", - per->Allowed, per->Sequence, per->ServerNumber, per->Message); - Log(Logs::General, Logs::Netcode, "[Size: %u] %s", outapp->size, DumpPacketToString(outapp).c_str()); + if (server.options.IsWorldTraceOn()) { + LogDebug( + "Sending play response: allowed [{0}] sequence [{1}] server number [{2}] message [{3}]", + per->Allowed, + per->Sequence, + per->ServerNumber, + per->Message + ); + + LogDebug("[Size: {0}] {1}", outapp->size, DumpPacketToString(outapp)); } - if (server.options.IsDumpOutPacketsOn()) - { + if (server.options.IsDumpOutPacketsOn()) { DumpPacket(outapp); } - c->SendPlayResponse(outapp); + client->SendPlayResponse(outapp); delete outapp; } - else - { - Log(Logs::General, Logs::Error, "Recieved User-To-World Response for %u but could not find the client referenced!.", utwr->lsaccountid); + else { + LogError( + "Received User-To-World Response for {0} but could not find the client referenced!", + user_to_world_response->lsaccountid + ); } } -void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &p) +/** + * @param opcode + * @param packet + */ +void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Packet &packet) { - if (server.options.IsWorldTraceOn()) - { - Log(Logs::General, Logs::Netcode, "Application packet received from server: 0x%.4X, (size %u)", opcode, p.Length()); + if (server.options.IsWorldTraceOn()) { + LogDebug( + "Application packet received from server: 0x%.4X, (size %u)", + opcode, + packet.Length() + ); } - if (server.options.IsDumpInPacketsOn()) - { - DumpPacket(opcode, p); + if (server.options.IsDumpInPacketsOn()) { + DumpPacket(opcode, packet); } - if (p.Length() < sizeof(ServerLSAccountUpdate_Struct)) - { - Log(Logs::General, Logs::Error, "Recieved application packet from server that had opcode ServerLSAccountUpdate_Struct, " - "but was too small. Discarded to avoid buffer overrun."); + if (packet.Length() < sizeof(UsertoWorldResponse_Struct)) { + LogError( + "Received application packet from server that had opcode ServerOP_UsertoWorldResp, " + "but was too small. Discarded to avoid buffer overrun" + ); + return; } - Log(Logs::General, Logs::Netcode, "ServerOP_LSAccountUpdate packet received from: %s", short_name.c_str()); - ServerLSAccountUpdate_Struct *lsau = (ServerLSAccountUpdate_Struct*)p.Data(); - if (is_server_trusted) - { - Log(Logs::General, Logs::Netcode, "ServerOP_LSAccountUpdate update processed for: %s", lsau->useraccount); + //I don't use world trace for this and here is why: + //Because this is a part of the client login procedure it makes tracking client errors + //While keeping world server spam with multiple servers connected almost impossible. + if (server.options.IsTraceOn()) { + LogDebug("User-To-World Response received"); + } + + auto user_to_world_response = (UsertoWorldResponse_Struct *) packet.Data(); + LogDebug("Trying to find client with user id of [{0}]", user_to_world_response->lsaccountid); + + Client *client = server.client_manager->GetClient( + user_to_world_response->lsaccountid, + user_to_world_response->login + ); + + if (client) { + LogDebug("Found client with user id of {0} and account name of {1}", + user_to_world_response->lsaccountid, + client->GetAccountName().c_str() + ); + + auto *outapp = new EQApplicationPacket( + OP_PlayEverquestResponse, + sizeof(PlayEverquestResponse_Struct) + ); + + auto *per = (PlayEverquestResponse_Struct *) outapp->pBuffer; + per->Sequence = client->GetPlaySequence(); + per->ServerNumber = client->GetPlayServerID(); + + LogDebug( + "Found sequence and play of [{0}] [{1}]", + client->GetPlaySequence(), + client->GetPlayServerID() + ); + + LogDebug("[Size: {0}] {1}", outapp->size, DumpPacketToString(outapp)); + + if (user_to_world_response->response > 0) { + per->Allowed = 1; + SendClientAuth( + client->GetConnection()->GetRemoteAddr(), + client->GetAccountName(), + client->GetKey(), + client->GetAccountID(), + client->GetLoginServerName() + ); + } + + switch (user_to_world_response->response) { + case UserToWorldStatusSuccess: + per->Message = 101; + break; + case UserToWorldStatusWorldUnavail: + per->Message = 326; + break; + case UserToWorldStatusSuspended: + per->Message = 337; + break; + case UserToWorldStatusBanned: + per->Message = 338; + break; + case UserToWorldStatusWorldAtCapacity: + per->Message = 339; + break; + case UserToWorldStatusAlreadyOnline: + per->Message = 111; + break; + default: + per->Message = 102; + } + + if (server.options.IsTraceOn()) { + LogDebug( + "Sending play response with following data, allowed {0}, sequence {1}, server number {2}, message {3}", + per->Allowed, + per->Sequence, + per->ServerNumber, + per->Message + ); + LogDebug("[Size: {0}] {1}", outapp->size, DumpPacketToString(outapp)); + } + + if (server.options.IsDumpOutPacketsOn()) { + DumpPacket(outapp); + } + + client->SendPlayResponse(outapp); + delete outapp; + } + else { + LogError( + "Received User-To-World Response for {0} but could not find the client referenced!.", + user_to_world_response->lsaccountid + ); + } +} + +/** + * @param opcode + * @param packet + */ +void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &packet) +{ + if (server.options.IsWorldTraceOn()) { + LogDebug( + "Application packet received from server: {0}, (size {1})", + opcode, + packet.Length() + ); + } + + if (server.options.IsDumpInPacketsOn()) { + DumpPacket(opcode, packet); + } + + if (packet.Length() < sizeof(ServerLSAccountUpdate_Struct)) { + LogError( + "Received application packet from server that had opcode ServerLSAccountUpdate_Struct, " + "but was too small. Discarded to avoid buffer overrun" + ); + + return; + } + + if (server.options.IsWorldTraceOn()) { + LogDebug("ServerOP_LSAccountUpdate packet received from [{0}]", short_name); + } + + auto *loginserver_update = (ServerLSAccountUpdate_Struct *) packet.Data(); + if (IsServerTrusted()) { + LogDebug("ServerOP_LSAccountUpdate update processed for: [{0}]", loginserver_update->useraccount); std::string name; std::string password; std::string email; - name.assign(lsau->useraccount); - password.assign(lsau->userpassword); - email.assign(lsau->useremail); - server.db->UpdateLSAccountInfo(lsau->useraccountid, name, password, email); + + name.assign(loginserver_update->useraccount); + password.assign(loginserver_update->userpassword); + + if (loginserver_update->user_email) { + email.assign(loginserver_update->user_email); + } + + server.db->UpdateLSAccountInfo( + loginserver_update->useraccountid, + name, + password, + email + ); } } -void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct* i) +/** + * When a worldserver first messages the loginserver telling them who they are + * + * @param new_world_server_info_packet + */ +void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info_packet) { - if (is_server_logged_in) - { - Log(Logs::General, Logs::Error, "WorldServer::Handle_NewLSInfo called but the login server was already marked as logged in, aborting."); + if (IsServerLoggedIn()) { + LogError("WorldServer::Handle_NewLSInfo called but the login server was already marked as logged in, aborting"); return; } - if (strlen(i->account) <= 30) - { - account_name = i->account; - } - else - { - Log(Logs::General, Logs::Error, "Handle_NewLSInfo error, account name was too long."); + if (!this->HandleNewLoginserverInfoValidation(new_world_server_info_packet)) { + LogError("WorldServer::Handle_NewLSInfo failed validation rules"); return; } - if (strlen(i->password) <= 30) - { - account_password = i->password; - } - else - { - Log(Logs::General, Logs::Error, "Handle_NewLSInfo error, account password was too long."); - return; - } + this->SetAccountPassword(new_world_server_info_packet->account_password) + ->SetLongName(new_world_server_info_packet->server_long_name) + ->SetShortName(new_world_server_info_packet->server_short_name) + ->SetLocalIp(new_world_server_info_packet->local_ip_address) + ->SetRemoteIp(new_world_server_info_packet->remote_ip_address) + ->SetVersion(new_world_server_info_packet->server_version) + ->SetProtocol(new_world_server_info_packet->protocol_version) + ->SetServerProcessType(new_world_server_info_packet->server_process_type) + ->SetIsServerLoggedIn(true) + ->SetAccountName(new_world_server_info_packet->account_name); - if (strlen(i->name) <= 200) - { - long_name = i->name; - } - else - { - Log(Logs::General, Logs::Error, "Handle_NewLSInfo error, long name was too long."); - return; - } - - if (strlen(i->shortname) <= 50) - { - short_name = i->shortname; - } - else - { - Log(Logs::General, Logs::Error, "Handle_NewLSInfo error, short name was too long."); - return; - } - - if (strlen(i->local_address) <= 125) - { - if (strlen(i->local_address) == 0) - { - Log(Logs::General, Logs::Error, "Handle_NewLSInfo error, local address was null, defaulting to localhost"); - local_ip = "127.0.0.1"; - } - else - { - local_ip = i->local_address; - } - } - else - { - Log(Logs::General, Logs::Error, "Handle_NewLSInfo error, local address was too long."); - return; - } - - if (strlen(i->remote_address) <= 125) - { - if (strlen(i->remote_address) == 0) - { - remote_ip = GetConnection()->Handle()->RemoteIP(); - Log(Logs::General, Logs::Error, "Handle_NewLSInfo error, remote address was null, defaulting to stream address %s.", remote_ip.c_str()); - } - else - { - remote_ip = i->remote_address; - } - } - else - { - remote_ip = GetConnection()->Handle()->RemoteIP(); - Log(Logs::General, Logs::Error, "Handle_NewLSInfo error, remote address was too long, defaulting to stream address %s.", remote_ip.c_str()); - } - - if (strlen(i->serverversion) <= 64) - { - version = i->serverversion; - } - else - { - Log(Logs::General, Logs::Error, "Handle_NewLSInfo error, server version was too long."); - return; - } - - if (strlen(i->protocolversion) <= 25) - { - protocol = i->protocolversion; - } - else - { - Log(Logs::General, Logs::Error, "Handle_NewLSInfo error, protocol version was too long."); - return; - } - - server_type = i->servertype; - is_server_logged_in = true; - - if (server.options.IsRejectingDuplicateServers()) - { - if (server.server_manager->ServerExists(long_name, short_name, this)) - { - Log(Logs::General, Logs::Error, "World tried to login but there already exists a server that has that name."); + if (server.options.IsRejectingDuplicateServers()) { + if (server.server_manager->ServerExists(GetServerLongName(), GetServerShortName(), this)) { + LogError("World tried to login but there already exists a server that has that name"); return; } } - else - { - if (server.server_manager->ServerExists(long_name, short_name, this)) - { - Log(Logs::General, Logs::Error, "World tried to login but there already exists a server that has that name."); + else { + if (server.server_manager->ServerExists(GetServerLongName(), GetServerShortName(), this)) { + LogInfo("World tried to login but there already exists a server that has that name"); server.server_manager->DestroyServerByName(long_name, short_name, this); } } - if (!server.options.IsUnregisteredAllowed()) - { - if (account_name.size() > 0 && account_password.size() > 0) - { - unsigned int s_id = 0; - unsigned int s_list_type = 0; - unsigned int s_trusted = 0; - std::string s_desc; - std::string s_list_desc; - std::string s_acct_name; - std::string s_acct_pass; - if (server.db->GetWorldRegistration(long_name, short_name, s_id, s_desc, s_list_type, s_trusted, s_list_desc, s_acct_name, s_acct_pass)) - { - if (s_acct_name.size() == 0 || s_acct_pass.size() == 0) - { - Log(Logs::General, Logs::World_Server, "Server %s(%s) successfully logged into account that had no user/password requirement.", - long_name.c_str(), short_name.c_str()); - is_server_authorized = true; - SetRuntimeID(s_id); - server_list_id = s_list_type; - desc = s_desc; - } - else if (s_acct_name.compare(account_name) == 0 && s_acct_pass.compare(account_password) == 0) - { - Log(Logs::General, Logs::World_Server, "Server %s(%s) successfully logged in.", - long_name.c_str(), short_name.c_str()); - is_server_authorized = true; - SetRuntimeID(s_id); - server_list_id = s_list_type; - desc = s_desc; - if (s_trusted) { - Log(Logs::General, Logs::Netcode, "ServerOP_LSAccountUpdate sent to world"); - is_server_trusted = true; + uint32 world_server_admin_id = 0; - EQ::Net::DynamicPacket outapp; - connection->Send(ServerOP_LSAccountUpdate, outapp); - } - } - else { - Log(Logs::General, Logs::World_Server, "Server %s(%s) attempted to log in but account and password did not match the entry in the database, and only" - " registered servers are allowed.", long_name.c_str(), short_name.c_str()); - return; - } - } - else { - Log(Logs::General, Logs::World_Server, "Server %s(%s) attempted to log in but database couldn't find an entry and only registered servers are allowed.", - long_name.c_str(), short_name.c_str()); - return; + /** + * If our world is trying to authenticate, let's try and pull the owner first to try associating + * with a world short_name + */ + if (!GetAccountName().empty() && !GetAccountPassword().empty()) { + Database::DbLoginServerAdmin + login_server_admin = server.db->GetLoginServerAdmin(GetAccountName()); + + if (login_server_admin.loaded) { + LogDebug( + "WorldServer::Handle_NewLSInfo | Attempting to authenticate world admin... [{0}] ({1}) against worldserver [{2}]", + GetAccountName(), + login_server_admin.id, + GetServerShortName() + ); + + /** + * Validate password hash + */ + auto mode = server.options.GetEncryptionMode(); + if (eqcrypt_verify_hash( + GetAccountName(), + GetAccountPassword(), + login_server_admin.account_password, + mode + )) { + LogDebug( + "WorldServer::Handle_NewLSInfo | Authenticating world admin... [{0}] ({1}) success! World ({2})", + GetAccountName(), + login_server_admin.id, + GetServerShortName() + ); + world_server_admin_id = login_server_admin.id; + + this->SetIsServerAuthorized(true); } } - else { - Log(Logs::General, Logs::World_Server, "Server %s(%s) did not attempt to log in but only registered servers are allowed.", - long_name.c_str(), short_name.c_str()); + } + + Database::DbWorldRegistration + world_registration = server.db->GetWorldRegistration( + GetServerShortName(), + world_server_admin_id + ); + + if (!server.options.IsUnregisteredAllowed()) { + if (!this->HandleNewLoginserverRegisteredOnly(world_registration)) { + LogError( + "WorldServer::HandleNewLoginserverRegisteredOnly checks failed with server [{0}]", + this->GetServerLongName() + ); return; } } else { - unsigned int server_id = 0; - unsigned int server_list_type = 0; - unsigned int is_server_trusted = 0; - std::string server_description; - std::string server_list_description; - std::string server_account_name; - std::string server_account_password; - - - if (server.db->GetWorldRegistration( - long_name, - short_name, - server_id, - server_description, - server_list_type, - is_server_trusted, - server_list_description, - server_account_name, - server_account_password)) - { - - if (account_name.size() > 0 && account_password.size() > 0) { - if (server_account_name.compare(account_name) == 0 && server_account_password.compare(account_password) == 0) { - Log(Logs::General, Logs::World_Server, "Server %s(%s) successfully logged in.", - long_name.c_str(), short_name.c_str()); - is_server_authorized = true; - SetRuntimeID(server_id); - server_list_id = server_list_type; - desc = server_description; - - if (is_server_trusted) { - Log(Logs::General, Logs::Netcode, "ServerOP_LSAccountUpdate sent to world"); - is_server_trusted = true; - EQ::Net::DynamicPacket outapp; - connection->Send(ServerOP_LSAccountUpdate, outapp); - } - } - else { - // this is the first of two cases where we should deny access even if unregistered is allowed - Log(Logs::General, Logs::World_Server, "Server %s(%s) attempted to log in but account and password did not match the entry in the database.", - long_name.c_str(), short_name.c_str()); - } - } - else { - if (server_account_name.size() > 0 || server_account_password.size() > 0) { - // this is the second of two cases where we should deny access even if unregistered is allowed - Log(Logs::General, Logs::World_Server, "Server %s(%s) did not attempt to log in but this server requires a password.", - long_name.c_str(), short_name.c_str()); - } - else { - Log(Logs::General, Logs::World_Server, "Server %s(%s) did not attempt to log in but unregistered servers are allowed.", - long_name.c_str(), short_name.c_str()); - is_server_authorized = true; - SetRuntimeID(server_id); - server_list_id = 3; - } - } - } - else - { - Log(Logs::General, Logs::World_Server, "Server %s(%s) attempted to log in but database couldn't find an entry but unregistered servers are allowed.", - long_name.c_str(), short_name.c_str()); - if (server.db->CreateWorldRegistration(long_name, short_name, server_id)) { - is_server_authorized = true; - SetRuntimeID(server_id); - server_list_id = 3; - } + if (!this->HandleNewLoginserverInfoUnregisteredAllowed(world_registration)) { + LogError( + "WorldServer::HandleNewLoginserverInfoUnregisteredAllowed checks failed with server [{0}]", + this->GetServerLongName() + ); + return; } } - server.db->UpdateWorldRegistration(GetRuntimeID(), long_name, GetConnection()->Handle()->RemoteIP()); + server.db->UpdateWorldRegistration( + GetServerId(), + GetServerLongName(), + GetRemoteIp() + ); } -void WorldServer::Handle_LSStatus(ServerLSStatus_Struct *s) +/** + * @param server_login_status + */ +void WorldServer::Handle_LSStatus(ServerLSStatus_Struct *server_login_status) { - players_online = s->num_players; - zones_booted = s->num_zones; - server_status = s->status; + SetPlayersOnline(server_login_status->num_players); + SetZonesBooted(server_login_status->num_zones); + SetServerStatus(server_login_status->status); } -void WorldServer::SendClientAuth(std::string ip, std::string account, std::string key, unsigned int account_id) +/** + * @param ip + * @param account + * @param key + * @param account_id + * @param loginserver_name + */ +void WorldServer::SendClientAuth( + std::string ip, + std::string account, + std::string key, + unsigned int account_id, + const std::string &loginserver_name +) { EQ::Net::DynamicPacket outapp; - ClientAuth_Struct client_auth; - client_auth.lsaccount_id = account_id; - strncpy(client_auth.name, account.c_str(), 30); + ClientAuth_Struct client_auth{}; + + client_auth.loginserver_account_id = account_id; + + strncpy(client_auth.account_name, account.c_str(), 30); strncpy(client_auth.key, key.c_str(), 30); - client_auth.lsadmin = 0; - client_auth.worldadmin = 0; - client_auth.ip = inet_addr(ip.c_str()); - std::string client_address(ip); - std::string world_address(connection->Handle()->RemoteIP()); + client_auth.lsadmin = 0; + client_auth.is_world_admin = 0; + client_auth.ip = inet_addr(ip.c_str()); + strncpy(client_auth.loginserver_name, &loginserver_name[0], 64); - if (client_address.compare(world_address) == 0) { - client_auth.local = 1; + const std::string &client_address(ip); + std::string world_address(connection->Handle()->RemoteIP()); + + if (client_address == world_address) { + client_auth.is_client_from_local_network = 1; } - else if (client_address.find(server.options.GetLocalNetwork()) != std::string::npos) { - client_auth.local = 1; + else if (IpUtil::IsIpInPrivateRfc1918(client_address)) { + LogInfo("Client is authenticating from a local address [{0}]", client_address); + client_auth.is_client_from_local_network = 1; } else { - client_auth.local = 0; + client_auth.is_client_from_local_network = 0; } + struct in_addr ip_addr{}; + ip_addr.s_addr = client_auth.ip; + + LogInfo( + "Client authentication response: world_address [{0}] client_address [{1}]", + world_address, + client_address + ); + + LogInfo( + "Sending Client Authentication Response ls_account_id [{0}] ls_name [{1}] name [{2}] key [{3}] ls_admin [{4}] " + "world_admin [{5}] ip [{6}] local [{7}]", + client_auth.loginserver_account_id, + client_auth.loginserver_name, + client_auth.account_name, + client_auth.key, + client_auth.lsadmin, + client_auth.is_world_admin, + inet_ntoa(ip_addr), + client_auth.is_client_from_local_network + ); + outapp.PutSerialize(0, client_auth); connection->Send(ServerOP_LSClientAuth, outapp); - if (server.options.IsDumpInPacketsOn()) - { + if (server.options.IsDumpInPacketsOn()) { DumpPacket(ServerOP_LSClientAuth, outapp); } } + +/** + * @param new_world_server_info_packet + * @return + */ +bool WorldServer::HandleNewLoginserverInfoValidation( + ServerNewLSInfo_Struct *new_world_server_info_packet +) +{ + const int max_account_name_length = 30; + const int max_account_password_length = 30; + const int max_server_long_name_length = 200; + const int max_server_short_name_length = 50; + const int max_server_local_address_length = 125; + const int max_server_remote_address_length = 125; + const int max_server_version_length = 64; + const int max_server_protocol_version = 25; + + if (strlen(new_world_server_info_packet->account_name) >= max_account_name_length) { + LogError("Handle_NewLSInfo error [account_name] was too long | max [{0}]", max_account_name_length); + return false; + } + else if (strlen(new_world_server_info_packet->account_password) >= max_account_password_length) { + LogError("Handle_NewLSInfo error [account_password] was too long | max [{0}]", max_account_password_length); + return false; + } + else if (strlen(new_world_server_info_packet->server_long_name) >= max_server_long_name_length) { + LogError("Handle_NewLSInfo error [server_long_name] was too long | max [{0}]", max_server_long_name_length); + return false; + } + else if (strlen(new_world_server_info_packet->server_short_name) >= max_server_short_name_length) { + LogError("Handle_NewLSInfo error [server_short_name] was too long | max [{0}]", max_server_short_name_length); + return false; + } + else if (strlen(new_world_server_info_packet->server_version) >= max_server_short_name_length) { + LogError("Handle_NewLSInfo error [server_version] was too long | max [{0}]", max_server_version_length); + return false; + } + else if (strlen(new_world_server_info_packet->protocol_version) >= max_server_protocol_version) { + LogError("Handle_NewLSInfo error [protocol_version] was too long | max [{0}]", max_server_protocol_version); + return false; + } + + if (strlen(new_world_server_info_packet->local_ip_address) <= max_server_local_address_length) { + if (strlen(new_world_server_info_packet->local_ip_address) == 0) { + LogError("Handle_NewLSInfo error, local address was null, defaulting to localhost"); + this->SetLocalIp("127.0.0.1"); + } + else { + this->SetLocalIp(new_world_server_info_packet->local_ip_address); + } + } + else { + LogError("Handle_NewLSInfo error, local address was too long | max [{0}]", max_server_local_address_length); + return false; + } + + if (strlen(new_world_server_info_packet->remote_ip_address) <= max_server_remote_address_length) { + if (strlen(new_world_server_info_packet->remote_ip_address) == 0) { + this->SetRemoteIp(GetConnection()->Handle()->RemoteIP()); + + LogWarning( + "Remote address was null, defaulting to stream address {0}", + remote_ip_address + ); + } + else { + this->SetRemoteIp(new_world_server_info_packet->remote_ip_address); + } + } + else { + this->SetRemoteIp(GetConnection()->Handle()->RemoteIP()); + + LogWarning( + "Handle_NewLSInfo remote address was too long, defaulting to stream address [{0}]", + remote_ip_address + ); + } + + return true; +} + +/** + * @param world_registration + * @return + */ +bool WorldServer::HandleNewLoginserverRegisteredOnly( + Database::DbWorldRegistration &world_registration +) +{ + if (!this->GetAccountName().empty() && !this->GetAccountPassword().empty()) { + if (world_registration.loaded) { + bool does_world_server_not_require_authentication = ( + world_registration.server_admin_account_name.empty() || + world_registration.server_admin_account_password.empty() + ); + + bool does_world_server_pass_authentication_check = ( + world_registration.server_admin_account_name == this->GetAccountName() && + eqcrypt_verify_hash( + GetAccountName(), + GetAccountPassword(), + world_registration.server_admin_account_password, + server.options.GetEncryptionMode() + ) + ); + + this + ->SetServerDescription(world_registration.server_description) + ->SetServerId(world_registration.server_id) + ->SetIsServerTrusted(world_registration.is_server_trusted) + ->SetServerListTypeId(world_registration.server_list_type); + + if (does_world_server_not_require_authentication) { + + this->SetIsServerAuthorized(true); + + LogInfo( + "Server long_name {0} short_name [{1}] successfully logged into account that had no user/password requirement", + this->GetServerLongName(), + this->GetServerShortName() + ); + } + else if (does_world_server_pass_authentication_check) { + + this->SetIsServerAuthorized(true); + + LogInfo( + "Server long_name {0} short_name [{1}] successfully logged in", + this->GetServerLongName(), + this->GetServerShortName() + ); + + if (IsServerTrusted()) { + LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world"); + EQ::Net::DynamicPacket outapp; + connection->Send(ServerOP_LSAccountUpdate, outapp); + } + } + else { + LogInfo( + "Server long_name {0} short_name [{1}] attempted to log in but account and password did not " + "match the entry in the database, and only registered servers are allowed", + this->GetServerLongName(), + this->GetServerShortName() + ); + + return false; + } + } + else { + LogInfo( + "Server long_name {0} short_name [{1}] attempted to log in but database couldn't find an entry and only registered servers are allowed", + this->GetServerLongName(), + this->GetServerShortName() + ); + + return false; + } + } + else { + LogInfo( + "Server long_name {0} short_name [{1}] did not attempt to log in but only registered servers are allowed", + this->GetServerLongName(), + this->GetServerShortName() + ); + + return false; + } + + return true; +} + +/** + * @param world_registration + * @return + */ +bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( + Database::DbWorldRegistration &world_registration +) +{ + if (world_registration.loaded) { + this + ->SetServerDescription(world_registration.server_description) + ->SetServerId(world_registration.server_id) + ->SetIsServerTrusted(world_registration.is_server_trusted) + ->SetServerListTypeId(world_registration.server_list_type); + + bool does_world_server_pass_authentication_check = ( + world_registration.server_admin_account_name == this->GetAccountName() && + eqcrypt_verify_hash( + GetAccountName(), + GetAccountPassword(), + world_registration.server_admin_account_password, + server.options.GetEncryptionMode() + ) + ); + + bool does_world_server_have_non_empty_credentials = ( + !this->GetAccountName().empty() && + !this->GetAccountPassword().empty() + ); + + if (does_world_server_have_non_empty_credentials) { + if (does_world_server_pass_authentication_check) { + this->SetIsServerAuthorized(true); + + LogInfo( + "Server long_name {0} short_name [{1}] successfully logged in", + this->GetServerLongName(), + this->GetServerShortName() + ); + + if (this->IsServerTrusted()) { + LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world"); + EQ::Net::DynamicPacket outapp; + connection->Send(ServerOP_LSAccountUpdate, outapp); + } + } + else { + + /** + * this is the first of two cases where we should deny access even if unregistered is allowed + */ + LogInfo( + "Server long_name {0} short_name [{1}] attempted to log in but account and password did not match the entry in the database.", + this->GetServerLongName(), + this->GetServerShortName() + ); + } + } + else { + + /** + * this is the second of two cases where we should deny access even if unregistered is allowed + */ + if (!this->GetAccountName().empty() || !this->GetAccountPassword().empty()) { + LogInfo( + "Server [{0}] [{1}] did not login but this server required a password to login", + this->GetServerLongName(), + this->GetServerShortName() + ); + } + else { + this->SetIsServerAuthorized(true); + LogInfo( + "Server [{0}] [{1}] did not login but unregistered servers are allowed", + this->GetServerLongName(), + this->GetServerShortName() + ); + } + } + } + else { + LogInfo( + "Server [{0}] ({1}) is not registered but unregistered servers are allowed", + this->GetServerLongName(), + this->GetServerShortName() + ); + + if (world_registration.loaded) { + this->SetIsServerAuthorized(true); + return true; + } + + Database::DbLoginServerAdmin login_server_admin = server.db->GetLoginServerAdmin(GetAccountName()); + + uint32 server_admin_id = 0; + if (login_server_admin.loaded) { + auto mode = server.options.GetEncryptionMode(); + if (eqcrypt_verify_hash( + GetAccountName(), + GetAccountPassword(), + login_server_admin.account_password, + mode + )) { + server_admin_id = login_server_admin.id; + } + } + + /** + * Auto create a registration + */ + if (!server.db->CreateWorldRegistration( + GetServerLongName(), + GetServerShortName(), + GetRemoteIp(), + server_id, + server_admin_id + )) { + return false; + } + } + + return true; +} + +/** + * @param in_server_list_id + * @return + */ +WorldServer *WorldServer::SetServerListTypeId(unsigned int in_server_list_id) +{ + server_list_type_id = in_server_list_id; + + return this; +} + +/** + * @return + */ +const std::string &WorldServer::GetServerDescription() const +{ + return server_description; +} + +/** + * @param in_server_description + */ +WorldServer *WorldServer::SetServerDescription(const std::string &in_server_description) +{ + WorldServer::server_description = in_server_description; + + return this; +} + +/** + * @return + */ +bool WorldServer::IsServerAuthorized() const +{ + return is_server_authorized; +} + +/** + * @param in_is_server_authorized + */ +WorldServer *WorldServer::SetIsServerAuthorized(bool in_is_server_authorized) +{ + WorldServer::is_server_authorized = in_is_server_authorized; + + return this; +} + +/** + * @return + */ +bool WorldServer::IsServerLoggedIn() const +{ + return is_server_logged_in; +} + +/** + * @param in_is_server_logged_in + */ +WorldServer *WorldServer::SetIsServerLoggedIn(bool in_is_server_logged_in) +{ + WorldServer::is_server_logged_in = in_is_server_logged_in; + + return this; +} + +/** + * @return + */ +bool WorldServer::IsServerTrusted() const +{ + return is_server_trusted; +} + +/** + * @param in_is_server_trusted + */ +WorldServer *WorldServer::SetIsServerTrusted(bool in_is_server_trusted) +{ + WorldServer::is_server_trusted = in_is_server_trusted; + + return this; +} + +/** + * @param in_zones_booted + */ +WorldServer *WorldServer::SetZonesBooted(unsigned int in_zones_booted) +{ + WorldServer::zones_booted = in_zones_booted; + + return this; +} + +/** + * @param in_players_online + */ +WorldServer *WorldServer::SetPlayersOnline(unsigned int in_players_online) +{ + WorldServer::players_online = in_players_online; + + return this; +} + +/** + * @param in_server_status + */ +WorldServer *WorldServer::SetServerStatus(int in_server_status) +{ + WorldServer::server_status = in_server_status; + + return this; +} + +/** + * @param in_server_process_type + */ +WorldServer *WorldServer::SetServerProcessType(unsigned int in_server_process_type) +{ + WorldServer::server_process_type = in_server_process_type; + + return this; +} + +/** + * @param in_long_name + */ +WorldServer *WorldServer::SetLongName(const std::string &in_long_name) +{ + WorldServer::long_name = in_long_name; + + return this; +} + +/** + * @param in_short_name + */ +WorldServer *WorldServer::SetShortName(const std::string &in_short_name) +{ + WorldServer::short_name = in_short_name; + + return this; +} + +/** + * @param in_account_name + */ +WorldServer *WorldServer::SetAccountName(const std::string &in_account_name) +{ + WorldServer::account_name = in_account_name; + + return this; +} + +/** + * @param in_account_password + */ +WorldServer *WorldServer::SetAccountPassword(const std::string &in_account_password) +{ + WorldServer::account_password = in_account_password; + + return this; +} + +/** + * @param in_remote_ip + */ +WorldServer *WorldServer::SetRemoteIp(const std::string &in_remote_ip) +{ + WorldServer::remote_ip_address = in_remote_ip; + + return this; +} + +/** + * @param in_local_ip + */ +WorldServer *WorldServer::SetLocalIp(const std::string &in_local_ip) +{ + WorldServer::local_ip = in_local_ip; + + return this; +} + +/** + * @param in_protocol + */ +WorldServer *WorldServer::SetProtocol(const std::string &in_protocol) +{ + WorldServer::protocol = in_protocol; + + return this; +} + +/** + * @param in_version + */ +WorldServer *WorldServer::SetVersion(const std::string &in_version) +{ + WorldServer::version = in_version; + + return this; +} + +/** + * @return + */ +int WorldServer::GetServerStatus() const +{ + return server_status; +} + +/** + * @return + */ +unsigned int WorldServer::GetServerListTypeId() const +{ + return server_list_type_id; +} + +/** + * @return + */ +unsigned int WorldServer::GetServerProcessType() const +{ + return server_process_type; +} + +/** + * @return + */ +const std::string &WorldServer::GetAccountName() const +{ + return account_name; +} + +/** + * @return + */ +const std::string &WorldServer::GetAccountPassword() const +{ + return account_password; +} + +/** + * @return + */ +const std::string &WorldServer::GetRemoteIp() const +{ + return remote_ip_address; +} + +/** + * @return + */ +const std::string &WorldServer::GetLocalIp() const +{ + return local_ip; +} + +/** + * @return + */ +const std::string &WorldServer::GetProtocol() const +{ + return protocol; +} + +/** + * @return + */ +const std::string &WorldServer::GetVersion() const +{ + return version; +} + +void WorldServer::OnKeepAlive(EQ::Timer *t) +{ + ServerPacket pack(ServerOP_KeepAlive, 0); + connection->SendPacket(&pack); +} \ No newline at end of file diff --git a/loginserver/world_server.h b/loginserver/world_server.h index bce7b27eb..60697bc3d 100644 --- a/loginserver/world_server.h +++ b/loginserver/world_server.h @@ -1,20 +1,23 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net) +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 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 + * + */ - 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_WORLDSERVER_H #define EQEMU_WORLDSERVER_H @@ -22,6 +25,8 @@ #include "../common/net/servertalk_server_connection.h" #include "../common/servertalk.h" #include "../common/packet_dump.h" +#include "database.h" +#include "../common/event/timer.h" #include #include @@ -31,130 +36,158 @@ class WorldServer { public: - /** - * Constructor, sets our connection to c. - */ - WorldServer(std::shared_ptr c); + WorldServer(std::shared_ptr worldserver_connection); /** - * Destructor, frees our connection if it exists. - */ + * Destructor, frees our connection if it exists + */ ~WorldServer(); /** - * Resets the basic stats of this server. - */ + * Resets the basic stats of this server. + */ void Reset(); /** * Accesses connection, it is intentional that this is not const (trust me). */ std::shared_ptr GetConnection() { return connection; } - - /** - * Sets the connection to c. - */ void SetConnection(std::shared_ptr c) { connection = c; } /** - * Gets the runtime id of this server. - */ - unsigned int GetRuntimeID() const { return runtime_id; } + * @return + */ + unsigned int GetServerId() const { return server_id; } + WorldServer * SetServerId(unsigned int id) { server_id = id; return this; } /** - * Sets the runtime id of this server. - */ - void SetRuntimeID(unsigned int id) { runtime_id = id; } + * @return + */ + std::string GetServerLongName() const { return long_name; } + std::string GetServerShortName() const { return short_name; } /** - * Gets the long name of the server. - */ - std::string GetLongName() const { return long_name; } - - /** - * Gets the short name of the server. - */ - std::string GetShortName() const { return short_name; } - - /** - * Gets whether the server is authorized to show up on the server list or not. - */ + * Gets whether the server is authorized to show up on the server list or not + * @return + */ bool IsAuthorized() const { return is_server_authorized; } - - /** - * Gets the local ip of the server. - */ std::string GetLocalIP() const { return local_ip; } + std::string GetRemoteIP() const { return remote_ip_address; } /** - * Gets the remote ip of the server. - */ - std::string GetRemoteIP() const { return remote_ip; } + * Gets what kind of server this server is (legends, preferred, normal) + * + * @return + */ + unsigned int GetServerListID() const { return server_list_type_id; } + WorldServer * SetServerListTypeId(unsigned int in_server_list_id); - /** - * Gets what kind of server this server is (legends, preferred, normal) - */ - unsigned int GetServerListID() const { return server_list_id; } - - /** - * Gets the status of the server. - */ int GetStatus() const { return server_status; } - - /** - * Gets the number of zones online on the server. - */ unsigned int GetZonesBooted() const { return zones_booted; } - - /** - * Gets the number of players on the server. - */ unsigned int GetPlayersOnline() const { return players_online; } /** - * Takes the info struct we received from world and processes it. - */ - void Handle_NewLSInfo(ServerNewLSInfo_Struct* i); + * Takes the info struct we received from world and processes it + * + * @param new_world_server_info_packet + */ + void Handle_NewLSInfo(ServerNewLSInfo_Struct* new_world_server_info_packet); /** - * Takes the status struct we received from world and processes it. - */ - void Handle_LSStatus(ServerLSStatus_Struct *s); + * Takes the status struct we received from world and processes it + * + * @param server_login_status + */ + void Handle_LSStatus(ServerLSStatus_Struct *server_login_status); + + bool HandleNewLoginserverInfoValidation(ServerNewLSInfo_Struct *new_world_server_info_packet); /** - * Informs world that there is a client incoming with the following data. - */ - void SendClientAuth(std::string ip, std::string account, std::string key, unsigned int account_id); + * Informs world that there is a client incoming with the following data. + * + * @param ip + * @param account + * @param key + * @param account_id + * @param loginserver_name + */ + void SendClientAuth(std::string ip, std::string account, std::string key, unsigned int account_id, const std::string &loginserver_name); + + WorldServer * SetZonesBooted(unsigned int in_zones_booted); + WorldServer * SetPlayersOnline(unsigned int in_players_online); + WorldServer * SetServerStatus(int in_server_status); + WorldServer * SetServerProcessType(unsigned int in_server_process_type); + WorldServer * SetLongName(const std::string &in_long_name); + WorldServer * SetShortName(const std::string &in_short_name); + WorldServer * SetAccountName(const std::string &in_account_name); + WorldServer * SetAccountPassword(const std::string &in_account_password); + WorldServer * SetRemoteIp(const std::string &in_remote_ip); + WorldServer * SetLocalIp(const std::string &in_local_ip); + WorldServer * SetProtocol(const std::string &in_protocol); + WorldServer * SetVersion(const std::string &in_version); + WorldServer * SetServerDescription(const std::string &in_server_description); + WorldServer * SetIsServerAuthorized(bool in_is_server_authorized); + WorldServer * SetIsServerLoggedIn(bool in_is_server_logged_in); + WorldServer * SetIsServerTrusted(bool in_is_server_trusted); + + bool IsServerAuthorized() const; + bool IsServerLoggedIn() const; + bool IsServerTrusted() const; + const std::string &GetAccountName() const; + const std::string &GetAccountPassword() const; + const std::string &GetLocalIp() const; + const std::string &GetProtocol() const; + const std::string &GetRemoteIp() const; + const std::string &GetServerDescription() const; + const std::string &GetVersion() const; + int GetServerStatus() const; + unsigned int GetServerListTypeId() const; + unsigned int GetServerProcessType() const; + + bool HandleNewLoginserverRegisteredOnly(Database::DbWorldRegistration &world_registration); + bool HandleNewLoginserverInfoUnregisteredAllowed(Database::DbWorldRegistration &world_registration); private: /** - * Packet processing functions: - */ - void ProcessNewLSInfo(uint16_t opcode, const EQ::Net::Packet &p); - void ProcessLSStatus(uint16_t opcode, const EQ::Net::Packet &p); - void ProcessUsertoWorldResp(uint16_t opcode, const EQ::Net::Packet &p); - void ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &p); + * Packet processing functions + * + * @param opcode + * @param packet + */ + void ProcessNewLSInfo(uint16_t opcode, const EQ::Net::Packet &packet); + void ProcessLSStatus(uint16_t opcode, const EQ::Net::Packet &packet); + void ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Net::Packet &packet); + void ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Packet &packet); + void ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &packet); std::shared_ptr connection; unsigned int zones_booted; unsigned int players_online; int server_status; - unsigned int runtime_id; - unsigned int server_list_id; - unsigned int server_type; - std::string desc; + unsigned int server_id; + unsigned int server_list_type_id; + unsigned int server_process_type; + std::string server_description; std::string long_name; std::string short_name; std::string account_name; std::string account_password; - std::string remote_ip; + std::string remote_ip_address; std::string local_ip; std::string protocol; std::string version; bool is_server_authorized; bool is_server_logged_in; bool is_server_trusted; + + /** + * Keepalive + * @param t + */ + void OnKeepAlive(EQ::Timer *t); + std::unique_ptr m_keepalive; + }; #endif diff --git a/queryserv/database.cpp b/queryserv/database.cpp index 1e06f229e..20465e6b5 100644 --- a/queryserv/database.cpp +++ b/queryserv/database.cpp @@ -39,8 +39,10 @@ #define strncasecmp _strnicmp #define strcasecmp _stricmp #else + #include "../common/unix.h" #include + #endif #include "database.h" @@ -48,7 +50,7 @@ #include "../common/string_util.h" #include "../common/servertalk.h" -Database::Database () +Database::Database() { DBInitVars(); } @@ -57,37 +59,36 @@ Database::Database () Establish a connection to a mysql database with the supplied parameters */ -Database::Database(const char* host, const char* user, const char* passwd, const char* database, uint32 port) +Database::Database(const char *host, const char *user, const char *passwd, const char *database, uint32 port) { DBInitVars(); Connect(host, user, passwd, database, port); } -bool Database::Connect(const char* host, const char* user, const char* passwd, const char* database, uint32 port) +bool Database::Connect(const char *host, const char *user, const char *passwd, const char *database, uint32 port) { - uint32 errnum= 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - if (!Open(host, user, passwd, database, port, &errnum, errbuf)) - { - Log(Logs::General, Logs::Error, "Failed to connect to database: Error: %s", errbuf); + uint32 errnum = 0; + char errbuf[MYSQL_ERRMSG_SIZE]; + if (!Open(host, user, passwd, database, port, &errnum, errbuf)) { + LogError("Failed to connect to database: Error: {}", errbuf); HandleMysqlError(errnum); return false; } - else - { - Log(Logs::General, Logs::Status, "Using database '%s' at %s:%d",database,host,port); + else { + LogInfo("Using database [{}] at [{}]:[{}]", database, host, port); return true; } } -void Database::DBInitVars() { +void Database::DBInitVars() +{ } - -void Database::HandleMysqlError(uint32 errnum) { +void Database::HandleMysqlError(uint32 errnum) +{ } /* @@ -98,343 +99,438 @@ Database::~Database() { } -void Database::AddSpeech(const char* from, const char* to, const char* message, uint16 minstatus, uint32 guilddbid, uint8 type) { +void Database::AddSpeech( + const char *from, + const char *to, + const char *message, + uint16 minstatus, + uint32 guilddbid, + uint8 type +) +{ - auto escapedFrom = new char[strlen(from) * 2 + 1]; - auto escapedTo = new char[strlen(to) * 2 + 1]; + auto escapedFrom = new char[strlen(from) * 2 + 1]; + auto escapedTo = new char[strlen(to) * 2 + 1]; auto escapedMessage = new char[strlen(message) * 2 + 1]; DoEscapeString(escapedFrom, from, strlen(from)); DoEscapeString(escapedTo, to, strlen(to)); DoEscapeString(escapedMessage, message, strlen(message)); - std::string query = StringFormat("INSERT INTO `qs_player_speech` " - "SET `from` = '%s', `to` = '%s', `message`='%s', " - "`minstatus`='%i', `guilddbid`='%i', `type`='%i'", - escapedFrom, escapedTo, escapedMessage, minstatus, guilddbid, type); - safe_delete_array(escapedFrom); + std::string query = StringFormat( + "INSERT INTO `qs_player_speech` " + "SET `from` = '%s', `to` = '%s', `message`='%s', " + "`minstatus`='%i', `guilddbid`='%i', `type`='%i'", + escapedFrom, escapedTo, escapedMessage, minstatus, guilddbid, type + ); + safe_delete_array(escapedFrom); safe_delete_array(escapedTo); safe_delete_array(escapedMessage); auto results = QueryDatabase(query); - if(!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed Speech Entry Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); + if (!results.Success()) { + LogInfo("Failed Speech Entry Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); } } -void Database::LogPlayerDropItem(QSPlayerDropItem_Struct* QS) { +void Database::LogPlayerDropItem(QSPlayerDropItem_Struct *QS) +{ - std::string query = StringFormat("INSERT INTO `qs_player_drop_record` SET `time` = NOW(), " - "`char_id` = '%i', `pickup` = '%i', " - "`zone_id` = '%i', `x` = '%i', `y` = '%i', `z` = '%i' ", - QS->char_id, QS->pickup, QS->zone_id, QS->x, QS->y, QS->z); + std::string query = StringFormat( + "INSERT INTO `qs_player_drop_record` SET `time` = NOW(), " + "`char_id` = '%i', `pickup` = '%i', " + "`zone_id` = '%i', `x` = '%i', `y` = '%i', `z` = '%i' ", + QS->char_id, QS->pickup, QS->zone_id, QS->x, QS->y, QS->z + ); auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed Drop Record Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); + LogInfo("Failed Drop Record Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); } - if (QS->_detail_count == 0) + if (QS->_detail_count == 0) { return; + } int lastIndex = results.LastInsertedID(); for (int i = 0; i < QS->_detail_count; i++) { - query = StringFormat("INSERT INTO `qs_player_drop_record_entries` SET `event_id` = '%i', " - "`item_id` = '%i', `charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', " - "`aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", - lastIndex, QS->items[i].item_id, - QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, - QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5); + query = StringFormat( + "INSERT INTO `qs_player_drop_record_entries` SET `event_id` = '%i', " + "`item_id` = '%i', `charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', " + "`aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].item_id, + QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, + QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5 + ); results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed Drop Record Entry Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); + LogInfo("Failed Drop Record Entry Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); } } } -void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 detailCount) { +void Database::LogPlayerTrade(QSPlayerLogTrade_Struct *QS, uint32 detailCount) +{ - std::string query = StringFormat("INSERT INTO `qs_player_trade_record` SET `time` = NOW(), " - "`char1_id` = '%i', `char1_pp` = '%i', `char1_gp` = '%i', " - "`char1_sp` = '%i', `char1_cp` = '%i', `char1_items` = '%i', " - "`char2_id` = '%i', `char2_pp` = '%i', `char2_gp` = '%i', " - "`char2_sp` = '%i', `char2_cp` = '%i', `char2_items` = '%i'", - QS->char1_id, QS->char1_money.platinum, QS->char1_money.gold, - QS->char1_money.silver, QS->char1_money.copper, QS->char1_count, - QS->char2_id, QS->char2_money.platinum, QS->char2_money.gold, - QS->char2_money.silver, QS->char2_money.copper, QS->char2_count); - auto results = QueryDatabase(query); - if(!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed Trade Log Record Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); + std::string query = StringFormat( + "INSERT INTO `qs_player_trade_record` SET `time` = NOW(), " + "`char1_id` = '%i', `char1_pp` = '%i', `char1_gp` = '%i', " + "`char1_sp` = '%i', `char1_cp` = '%i', `char1_items` = '%i', " + "`char2_id` = '%i', `char2_pp` = '%i', `char2_gp` = '%i', " + "`char2_sp` = '%i', `char2_cp` = '%i', `char2_items` = '%i'", + QS->char1_id, QS->char1_money.platinum, QS->char1_money.gold, + QS->char1_money.silver, QS->char1_money.copper, QS->char1_count, + QS->char2_id, QS->char2_money.platinum, QS->char2_money.gold, + QS->char2_money.silver, QS->char2_money.copper, QS->char2_count + ); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogInfo("Failed Trade Log Record Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); } - if(detailCount == 0) - return; + if (detailCount == 0) { + return; + } int lastIndex = results.LastInsertedID(); - for(int i = 0; i < detailCount; i++) { - query = StringFormat("INSERT INTO `qs_player_trade_record_entries` SET `event_id` = '%i', " - "`from_id` = '%i', `from_slot` = '%i', `to_id` = '%i', `to_slot` = '%i', " - "`item_id` = '%i', `charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', " - "`aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", - lastIndex, QS->items[i].from_id, QS->items[i].from_slot, - QS->items[i].to_id, QS->items[i].to_slot, QS->items[i].item_id, - QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, - QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5); - results = QueryDatabase(query); - if(!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed Trade Log Record Entry Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); - } + for (int i = 0; i < detailCount; i++) { + query = StringFormat( + "INSERT INTO `qs_player_trade_record_entries` SET `event_id` = '%i', " + "`from_id` = '%i', `from_slot` = '%i', `to_id` = '%i', `to_slot` = '%i', " + "`item_id` = '%i', `charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', " + "`aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].from_id, QS->items[i].from_slot, + QS->items[i].to_id, QS->items[i].to_slot, QS->items[i].item_id, + QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, + QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5 + ); + results = QueryDatabase(query); + if (!results.Success()) { + LogInfo("Failed Trade Log Record Entry Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); + } - } + } } -void Database::LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 detailCount) { +void Database::LogPlayerHandin(QSPlayerLogHandin_Struct *QS, uint32 detailCount) +{ - std::string query = StringFormat("INSERT INTO `qs_player_handin_record` SET `time` = NOW(), " - "`quest_id` = '%i', `char_id` = '%i', `char_pp` = '%i', " - "`char_gp` = '%i', `char_sp` = '%i', `char_cp` = '%i', " - "`char_items` = '%i', `npc_id` = '%i', `npc_pp` = '%i', " - "`npc_gp` = '%i', `npc_sp` = '%i', `npc_cp` = '%i', " - "`npc_items`='%i'", - QS->quest_id, QS->char_id, QS->char_money.platinum, - QS->char_money.gold, QS->char_money.silver, QS->char_money.copper, - QS->char_count, QS->npc_id, QS->npc_money.platinum, - QS->npc_money.gold, QS->npc_money.silver, QS->npc_money.copper, - QS->npc_count); - auto results = QueryDatabase(query); - if(!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed Handin Log Record Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); + std::string query = StringFormat( + "INSERT INTO `qs_player_handin_record` SET `time` = NOW(), " + "`quest_id` = '%i', `char_id` = '%i', `char_pp` = '%i', " + "`char_gp` = '%i', `char_sp` = '%i', `char_cp` = '%i', " + "`char_items` = '%i', `npc_id` = '%i', `npc_pp` = '%i', " + "`npc_gp` = '%i', `npc_sp` = '%i', `npc_cp` = '%i', " + "`npc_items`='%i'", + QS->quest_id, QS->char_id, QS->char_money.platinum, + QS->char_money.gold, QS->char_money.silver, QS->char_money.copper, + QS->char_count, QS->npc_id, QS->npc_money.platinum, + QS->npc_money.gold, QS->npc_money.silver, QS->npc_money.copper, + QS->npc_count + ); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogInfo("Failed Handin Log Record Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); } - if(detailCount == 0) - return; + if (detailCount == 0) { + return; + } int lastIndex = results.LastInsertedID(); - for(int i = 0; i < detailCount; i++) { - query = StringFormat("INSERT INTO `qs_player_handin_record_entries` SET `event_id` = '%i', " - "`action_type` = '%s', `char_slot` = '%i', `item_id` = '%i', " - "`charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', `aug_3` = '%i', " - "`aug_4` = '%i', `aug_5` = '%i'", - lastIndex, QS->items[i].action_type, QS->items[i].char_slot, - QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1, - QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, - QS->items[i].aug_5); - if(!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed Handin Log Record Entry Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); - } - } + for (int i = 0; i < detailCount; i++) { + query = StringFormat( + "INSERT INTO `qs_player_handin_record_entries` SET `event_id` = '%i', " + "`action_type` = '%s', `char_slot` = '%i', `item_id` = '%i', " + "`charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', `aug_3` = '%i', " + "`aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].action_type, QS->items[i].char_slot, + QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1, + QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, + QS->items[i].aug_5 + ); + if (!results.Success()) { + LogInfo("Failed Handin Log Record Entry Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); + } + } } -void Database::LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 members){ +void Database::LogPlayerNPCKill(QSPlayerLogNPCKill_Struct *QS, uint32 members) +{ - std::string query = StringFormat("INSERT INTO `qs_player_npc_kill_record` " - "SET `npc_id` = '%i', `type` = '%i', " - "`zone_id` = '%i', `time` = NOW()", - QS->s1.NPCID, QS->s1.Type, QS->s1.ZoneID); - auto results = QueryDatabase(query); - if(!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed NPC Kill Log Record Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); + std::string query = StringFormat( + "INSERT INTO `qs_player_npc_kill_record` " + "SET `npc_id` = '%i', `type` = '%i', " + "`zone_id` = '%i', `time` = NOW()", + QS->s1.NPCID, QS->s1.Type, QS->s1.ZoneID + ); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogInfo("Failed NPC Kill Log Record Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); } - if(members == 0) - return; + if (members == 0) { + return; + } int lastIndex = results.LastInsertedID(); for (int i = 0; i < members; i++) { - query = StringFormat("INSERT INTO `qs_player_npc_kill_record_entries` " - "SET `event_id` = '%i', `char_id` = '%i'", - lastIndex, QS->Chars[i].char_id); + query = StringFormat( + "INSERT INTO `qs_player_npc_kill_record_entries` " + "SET `event_id` = '%i', `char_id` = '%i'", + lastIndex, QS->Chars[i].char_id + ); auto results = QueryDatabase(query); - if(!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed NPC Kill Log Entry Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); + if (!results.Success()) { + LogInfo("Failed NPC Kill Log Entry Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); } } } -void Database::LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 items) { +void Database::LogPlayerDelete(QSPlayerLogDelete_Struct *QS, uint32 items) +{ - std::string query = StringFormat("INSERT INTO `qs_player_delete_record` SET `time` = NOW(), " - "`char_id` = '%i', `stack_size` = '%i', `char_items` = '%i'", - QS->char_id, QS->stack_size, QS->char_count, QS->char_count); - auto results = QueryDatabase(query); - if(!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed Delete Log Record Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); + std::string query = StringFormat( + "INSERT INTO `qs_player_delete_record` SET `time` = NOW(), " + "`char_id` = '%i', `stack_size` = '%i', `char_items` = '%i'", + QS->char_id, QS->stack_size, QS->char_count, QS->char_count + ); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogInfo("Failed Delete Log Record Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); } - if(items == 0) - return; + if (items == 0) { + return; + } - int lastIndex = results.LastInsertedID(); + int lastIndex = results.LastInsertedID(); - for(int i = 0; i < items; i++) { - query = StringFormat("INSERT INTO `qs_player_delete_record_entries` SET `event_id` = '%i', " - "`char_slot` = '%i', `item_id` = '%i', `charges` = '%i', `aug_1` = '%i', " - "`aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", - lastIndex, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, - QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, - QS->items[i].aug_5); - results = QueryDatabase(query); - if(!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed Delete Log Record Entry Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); - } + for (int i = 0; i < items; i++) { + query = StringFormat( + "INSERT INTO `qs_player_delete_record_entries` SET `event_id` = '%i', " + "`char_slot` = '%i', `item_id` = '%i', `charges` = '%i', `aug_1` = '%i', " + "`aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, + QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, + QS->items[i].aug_5 + ); + results = QueryDatabase(query); + if (!results.Success()) { + LogInfo("Failed Delete Log Record Entry Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); + } - } + } } -void Database::LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 items) { +void Database::LogPlayerMove(QSPlayerLogMove_Struct *QS, uint32 items) +{ /* These are item moves */ - std::string query = StringFormat("INSERT INTO `qs_player_move_record` SET `time` = NOW(), " - "`char_id` = '%i', `from_slot` = '%i', `to_slot` = '%i', " - "`stack_size` = '%i', `char_items` = '%i', `postaction` = '%i'", - QS->char_id, QS->from_slot, QS->to_slot, QS->stack_size, - QS->char_count, QS->postaction); - auto results = QueryDatabase(query); - if(!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed Move Log Record Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); + std::string query = StringFormat( + "INSERT INTO `qs_player_move_record` SET `time` = NOW(), " + "`char_id` = '%i', `from_slot` = '%i', `to_slot` = '%i', " + "`stack_size` = '%i', `char_items` = '%i', `postaction` = '%i'", + QS->char_id, QS->from_slot, QS->to_slot, QS->stack_size, + QS->char_count, QS->postaction + ); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogInfo("Failed Move Log Record Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); } - if(items == 0) - return; + if (items == 0) { + return; + } - int lastIndex = results.LastInsertedID(); - - for(int i = 0; i < items; i++) { - query = StringFormat("INSERT INTO `qs_player_move_record_entries` SET `event_id` = '%i', " - "`from_slot` = '%i', `to_slot` = '%i', `item_id` = '%i', `charges` = '%i', " - "`aug_1` = '%i', `aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", - lastIndex, QS->items[i].from_slot, QS->items[i].to_slot, QS->items[i].item_id, - QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, - QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5); - results = QueryDatabase(query); - if(!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed Move Log Record Entry Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); - } - - } + int lastIndex = results.LastInsertedID(); + for (int i = 0; i < items; i++) { + query = StringFormat( + "INSERT INTO `qs_player_move_record_entries` SET `event_id` = '%i', " + "`from_slot` = '%i', `to_slot` = '%i', `item_id` = '%i', `charges` = '%i', " + "`aug_1` = '%i', `aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].from_slot, QS->items[i].to_slot, QS->items[i].item_id, + QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, + QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5 + ); + results = QueryDatabase(query); + if (!results.Success()) { + LogInfo("Failed Move Log Record Entry Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); + } + } } -void Database::LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint32 items) { +void Database::LogMerchantTransaction(QSMerchantLogTransaction_Struct *QS, uint32 items) +{ /* Merchant transactions are from the perspective of the merchant, not the player */ - std::string query = StringFormat("INSERT INTO `qs_merchant_transaction_record` SET `time` = NOW(), " - "`zone_id` = '%i', `merchant_id` = '%i', `merchant_pp` = '%i', " - "`merchant_gp` = '%i', `merchant_sp` = '%i', `merchant_cp` = '%i', " - "`merchant_items` = '%i', `char_id` = '%i', `char_pp` = '%i', " - "`char_gp` = '%i', `char_sp` = '%i', `char_cp` = '%i', " - "`char_items` = '%i'", - QS->zone_id, QS->merchant_id, QS->merchant_money.platinum, - QS->merchant_money.gold, QS->merchant_money.silver, - QS->merchant_money.copper, QS->merchant_count, QS->char_id, - QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver, - QS->char_money.copper, QS->char_count); - auto results = QueryDatabase(query); - if(!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed Transaction Log Record Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); + std::string query = StringFormat( + "INSERT INTO `qs_merchant_transaction_record` SET `time` = NOW(), " + "`zone_id` = '%i', `merchant_id` = '%i', `merchant_pp` = '%i', " + "`merchant_gp` = '%i', `merchant_sp` = '%i', `merchant_cp` = '%i', " + "`merchant_items` = '%i', `char_id` = '%i', `char_pp` = '%i', " + "`char_gp` = '%i', `char_sp` = '%i', `char_cp` = '%i', " + "`char_items` = '%i'", + QS->zone_id, QS->merchant_id, QS->merchant_money.platinum, + QS->merchant_money.gold, QS->merchant_money.silver, + QS->merchant_money.copper, QS->merchant_count, QS->char_id, + QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver, + QS->char_money.copper, QS->char_count + ); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogInfo("Failed Transaction Log Record Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); } - if(items == 0) - return; + if (items == 0) { + return; + } - int lastIndex = results.LastInsertedID(); + int lastIndex = results.LastInsertedID(); - for(int i = 0; i < items; i++) { - query = StringFormat("INSERT INTO `qs_merchant_transaction_record_entries` SET `event_id` = '%i', " - "`char_slot` = '%i', `item_id` = '%i', `charges` = '%i', `aug_1` = '%i', " - "`aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", - lastIndex, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, - QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, - QS->items[i].aug_5); - results = QueryDatabase(query); - if(!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed Transaction Log Record Entry Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); - } + for (int i = 0; i < items; i++) { + query = StringFormat( + "INSERT INTO `qs_merchant_transaction_record_entries` SET `event_id` = '%i', " + "`char_slot` = '%i', `item_id` = '%i', `charges` = '%i', `aug_1` = '%i', " + "`aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, + QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, + QS->items[i].aug_5 + ); + results = QueryDatabase(query); + if (!results.Success()) { + LogInfo("Failed Transaction Log Record Entry Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); + } - } + } } // this function does not delete the ServerPacket, so it must be handled at call site -void Database::GeneralQueryReceive(ServerPacket *pack) { +void Database::GeneralQueryReceive(ServerPacket *pack) +{ /* These are general queries passed from anywhere in zone instead of packing structures and breaking them down again and again */ - auto queryBuffer = new char[pack->ReadUInt32() + 1]; + auto queryBuffer = new char[pack->ReadUInt32() + 1]; pack->ReadString(queryBuffer); std::string query(queryBuffer); - auto results = QueryDatabase(query); + auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::Detail, Logs::QS_Server, "Failed Delete Log Record Insert: %s", results.ErrorMessage().c_str()); - Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); + LogInfo("Failed Delete Log Record Insert: [{}]", results.ErrorMessage().c_str()); + LogInfo("[{}]", query.c_str()); } safe_delete_array(queryBuffer); } -void Database::LoadLogSettings(EQEmuLogSys::LogSettings* log_settings){ +void Database::LoadLogSettings(EQEmuLogSys::LogSettings *log_settings) +{ std::string query = - "SELECT " - "log_category_id, " - "log_category_description, " - "log_to_console, " - "log_to_file, " - "log_to_gmsay " - "FROM " - "logsys_categories " - "ORDER BY log_category_id"; - auto results = QueryDatabase(query); + "SELECT " + "log_category_id, " + "log_category_description, " + "log_to_console, " + "log_to_file, " + "log_to_gmsay " + "FROM " + "logsys_categories " + "ORDER BY log_category_id"; - int log_category = 0; - LogSys.file_logs_enabled = false; + auto results = QueryDatabase(query); + int log_category_id = 0; + + int *categories_in_database = new int[1000]; for (auto row = results.begin(); row != results.end(); ++row) { - log_category = atoi(row[0]); - log_settings[log_category].log_to_console = atoi(row[2]); - log_settings[log_category].log_to_file = atoi(row[3]); - log_settings[log_category].log_to_gmsay = atoi(row[4]); + log_category_id = atoi(row[0]); + if (log_category_id <= Logs::None || log_category_id >= Logs::MaxCategoryID) { + continue; + } - /* 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[log_category].log_to_console > 0; - const bool log_to_file = log_settings[log_category].log_to_file > 0; - const bool log_to_gmsay = log_settings[log_category].log_to_gmsay > 0; + log_settings[log_category_id].log_to_console = static_cast(atoi(row[2])); + log_settings[log_category_id].log_to_file = static_cast(atoi(row[3])); + log_settings[log_category_id].log_to_gmsay = static_cast(atoi(row[4])); + + /** + * 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[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; - if (is_category_enabled) - log_settings[log_category].is_category_enabled = 1; + if (is_category_enabled) { + log_settings[log_category_id].is_category_enabled = 1; + } - /* - This determines whether or not the process needs to actually file log anything. - If we go through this whole loop and nothing is set to any debug level, there is no point to create a file or keep anything open - */ - if (log_settings[log_category].log_to_file > 0){ + /** + * This determines whether or not the process needs to actually file log anything. + * If we go through this whole loop and nothing is set to any debug level, there is no point to create a file or keep anything open + */ + if (log_settings[log_category_id].log_to_file > 0) { LogSys.file_logs_enabled = true; } + + categories_in_database[log_category_id] = 1; } -} + + /** + * Auto inject categories that don't exist in the database... + */ + for (int log_index = Logs::AA; log_index != Logs::MaxCategoryID; log_index++) { + if (categories_in_database[log_index] != 1) { + + LogInfo( + "New Log Category [{0}] doesn't exist... Automatically adding to [logsys_categories] table...", + Logs::LogCategoryName[log_index] + ); + + auto inject_query = fmt::format( + "INSERT INTO logsys_categories " + "(log_category_id, " + "log_category_description, " + "log_to_console, " + "log_to_file, " + "log_to_gmsay) " + "VALUES " + "({0}, '{1}', {2}, {3}, {4})", + log_index, + EscapeString(Logs::LogCategoryName[log_index]), + std::to_string(log_settings[log_index].log_to_console), + std::to_string(log_settings[log_index].log_to_file), + std::to_string(log_settings[log_index].log_to_gmsay) + ); + + QueryDatabase(inject_query); + } + } + + delete[] categories_in_database; +} \ No newline at end of file diff --git a/queryserv/queryserv.cpp b/queryserv/queryserv.cpp index cef6e40e3..67a371a8e 100644 --- a/queryserv/queryserv.cpp +++ b/queryserv/queryserv.cpp @@ -52,16 +52,16 @@ int main() { set_exception_handler(); Timer LFGuildExpireTimer(60000); - Log(Logs::General, Logs::QS_Server, "Starting EQEmu QueryServ."); + LogInfo("Starting EQEmu QueryServ"); if (!queryservconfig::LoadConfig()) { - Log(Logs::General, Logs::QS_Server, "Loading server configuration failed."); + LogInfo("Loading server configuration failed"); return 1; } Config = queryservconfig::get(); WorldShortName = Config->ShortName; - Log(Logs::General, Logs::QS_Server, "Connecting to MySQL..."); + LogInfo("Connecting to MySQL"); /* MySQL Connection */ if (!database.Connect( @@ -70,7 +70,7 @@ int main() { Config->QSDatabasePassword.c_str(), Config->QSDatabaseDB.c_str(), Config->QSDatabasePort)) { - Log(Logs::General, Logs::QS_Server, "Cannot continue without a database connection."); + LogInfo("Cannot continue without a database connection"); return 1; } @@ -79,11 +79,11 @@ int main() { LogSys.StartFileLogs(); if (signal(SIGINT, CatchSignal) == SIG_ERR) { - Log(Logs::General, Logs::QS_Server, "Could not set signal handler"); + LogInfo("Could not set signal handler"); return 1; } if (signal(SIGTERM, CatchSignal) == SIG_ERR) { - Log(Logs::General, Logs::QS_Server, "Could not set signal handler"); + LogInfo("Could not set signal handler"); return 1; } diff --git a/queryserv/worldserver.cpp b/queryserv/worldserver.cpp index a03d23b1e..ec34ce7bb 100644 --- a/queryserv/worldserver.cpp +++ b/queryserv/worldserver.cpp @@ -168,7 +168,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } default: - Log(Logs::Detail, Logs::QS_Server, "Received unhandled ServerOP_QueryServGeneric", Type); + LogInfo("Received unhandled ServerOP_QueryServGeneric", Type); break; } break; diff --git a/shared_memory/main.cpp b/shared_memory/main.cpp index 63280adc0..69b46d896 100644 --- a/shared_memory/main.cpp +++ b/shared_memory/main.cpp @@ -74,20 +74,19 @@ int main(int argc, char **argv) { LogSys.LoadLogSettingsDefaults(); set_exception_handler(); - Log(Logs::General, Logs::Status, "Shared Memory Loader Program"); + LogInfo("Shared Memory Loader Program"); if(!EQEmuConfig::LoadConfig()) { - Log(Logs::General, Logs::Error, "Unable to load configuration file."); + LogError("Unable to load configuration file"); return 1; } auto Config = EQEmuConfig::get(); SharedDatabase database; - Log(Logs::General, Logs::Status, "Connecting to database..."); + LogInfo("Connecting to database"); if(!database.Connect(Config->DatabaseHost.c_str(), Config->DatabaseUsername.c_str(), Config->DatabasePassword.c_str(), Config->DatabaseDB.c_str(), Config->DatabasePort)) { - Log(Logs::General, Logs::Error, "Unable to connect to the database, cannot continue without a " - "database connection"); + LogError("Unable to connect to the database, cannot continue without a database connection"); return 1; } @@ -97,7 +96,7 @@ int main(int argc, char **argv) { std::string shared_mem_directory = Config->SharedMemDir; if (MakeDirectory(shared_mem_directory)) { - Log(Logs::General, Logs::Status, "Shared Memory folder doesn't exist, so we created it... '%s'", shared_mem_directory.c_str()); + LogInfo("Shared Memory folder doesn't exist, so we created it [{}]", shared_mem_directory.c_str()); } database.LoadVariables(); @@ -106,7 +105,7 @@ int main(int argc, char **argv) { std::string db_hotfix_name; if (database.GetVariable("hotfix_name", db_hotfix_name)) { if (!db_hotfix_name.empty() && strcasecmp("hotfix_", db_hotfix_name.c_str()) == 0) { - Log(Logs::General, Logs::Status, "Current hotfix in variables is the default %s, clearing out variable", db_hotfix_name.c_str()); + LogInfo("Current hotfix in variables is the default [{}], clearing out variable", db_hotfix_name.c_str()); std::string query = StringFormat("UPDATE `variables` SET `value`='' WHERE (`varname`='hotfix_name')"); database.QueryDatabase(query); } @@ -177,65 +176,65 @@ int main(int argc, char **argv) { } if(hotfix_name.length() > 0) { - Log(Logs::General, Logs::Status, "Writing data for hotfix '%s'", hotfix_name.c_str()); + LogInfo("Writing data for hotfix [{}]", hotfix_name.c_str()); } if(load_all || load_items) { - Log(Logs::General, Logs::Status, "Loading items..."); + LogInfo("Loading items"); try { LoadItems(&database, hotfix_name); } catch(std::exception &ex) { - Log(Logs::General, Logs::Error, "%s", ex.what()); + LogError("{}", ex.what()); return 1; } } if(load_all || load_factions) { - Log(Logs::General, Logs::Status, "Loading factions..."); + LogInfo("Loading factions"); try { LoadFactions(&database, hotfix_name); } catch(std::exception &ex) { - Log(Logs::General, Logs::Error, "%s", ex.what()); + LogError("{}", ex.what()); return 1; } } if(load_all || load_loot) { - Log(Logs::General, Logs::Status, "Loading loot..."); + LogInfo("Loading loot"); try { LoadLoot(&database, hotfix_name); } catch(std::exception &ex) { - Log(Logs::General, Logs::Error, "%s", ex.what()); + LogError("{}", ex.what()); return 1; } } if(load_all || load_skill_caps) { - Log(Logs::General, Logs::Status, "Loading skill caps..."); + LogInfo("Loading skill caps"); try { LoadSkillCaps(&database, hotfix_name); } catch(std::exception &ex) { - Log(Logs::General, Logs::Error, "%s", ex.what()); + LogError("{}", ex.what()); return 1; } } if(load_all || load_spells) { - Log(Logs::General, Logs::Status, "Loading spells..."); + LogInfo("Loading spells"); try { LoadSpells(&database, hotfix_name); } catch(std::exception &ex) { - Log(Logs::General, Logs::Error, "%s", ex.what()); + LogError("{}", ex.what()); return 1; } } if(load_all || load_bd) { - Log(Logs::General, Logs::Status, "Loading base data..."); + LogInfo("Loading base data"); try { LoadBaseData(&database, hotfix_name); } catch(std::exception &ex) { - Log(Logs::General, Logs::Error, "%s", ex.what()); + LogError("{}", ex.what()); return 1; } } diff --git a/ucs/chatchannel.cpp b/ucs/chatchannel.cpp index 11a000712..001f7b1e7 100644 --- a/ucs/chatchannel.cpp +++ b/ucs/chatchannel.cpp @@ -47,8 +47,8 @@ ChatChannel::ChatChannel(std::string inName, std::string inOwner, std::string in Moderated = false; - Log(Logs::Detail, Logs::UCS_Server, "New ChatChannel created: Name: [%s], Owner: [%s], Password: [%s], MinStatus: %i", - Name.c_str(), Owner.c_str(), Password.c_str(), MinimumStatus); + LogInfo("New ChatChannel created: Name: [[{}]], Owner: [[{}]], Password: [[{}]], MinStatus: [{}]", + Name.c_str(), Owner.c_str(), Password.c_str(), MinimumStatus); } @@ -154,7 +154,7 @@ void ChatChannelList::SendAllChannels(Client *c) { void ChatChannelList::RemoveChannel(ChatChannel *Channel) { - Log(Logs::Detail, Logs::UCS_Server, "RemoveChannel(%s)", Channel->GetName().c_str()); + LogInfo("RemoveChannel([{}])", Channel->GetName().c_str()); LinkedListIterator iterator(ChatChannels); @@ -175,7 +175,7 @@ void ChatChannelList::RemoveChannel(ChatChannel *Channel) { void ChatChannelList::RemoveAllChannels() { - Log(Logs::Detail, Logs::UCS_Server, "RemoveAllChannels"); + LogInfo("RemoveAllChannels"); LinkedListIterator iterator(ChatChannels); @@ -233,7 +233,7 @@ void ChatChannel::AddClient(Client *c) { if(IsClientInChannel(c)) { - Log(Logs::Detail, Logs::UCS_Server, "Client %s already in channel %s", c->GetName().c_str(), GetName().c_str()); + LogInfo("Client [{}] already in channel [{}]", c->GetName().c_str(), GetName().c_str()); return; } @@ -242,7 +242,7 @@ void ChatChannel::AddClient(Client *c) { int AccountStatus = c->GetAccountStatus(); - Log(Logs::Detail, Logs::UCS_Server, "Adding %s to channel %s", c->GetName().c_str(), Name.c_str()); + LogInfo("Adding [{}] to channel [{}]", c->GetName().c_str(), Name.c_str()); LinkedListIterator iterator(ClientsInChannel); @@ -267,7 +267,7 @@ bool ChatChannel::RemoveClient(Client *c) { if(!c) return false; - Log(Logs::Detail, Logs::UCS_Server, "RemoveClient %s from channel %s", c->GetName().c_str(), GetName().c_str()); + LogInfo("RemoveClient [{}] from channel [{}]", c->GetName().c_str(), GetName().c_str()); bool HideMe = c->GetHideMe(); @@ -304,7 +304,7 @@ bool ChatChannel::RemoveClient(Client *c) { if((Password.length() == 0) || (RuleI(Channels, DeleteTimer) == 0)) return false; - Log(Logs::Detail, Logs::UCS_Server, "Starting delete timer for empty password protected channel %s", Name.c_str()); + LogInfo("Starting delete timer for empty password protected channel [{}]", Name.c_str()); DeleteTimer.Start(RuleI(Channels, DeleteTimer) * 60000); } @@ -402,8 +402,8 @@ void ChatChannel::SendMessageToChannel(std::string Message, Client* Sender) { if(ChannelClient) { - Log(Logs::Detail, Logs::UCS_Server, "Sending message to %s from %s", - ChannelClient->GetName().c_str(), Sender->GetName().c_str()); + LogInfo("Sending message to [{}] from [{}]", + ChannelClient->GetName().c_str(), Sender->GetName().c_str()); if (cv_messages[static_cast(ChannelClient->GetClientVersion())].length() == 0) { switch (ChannelClient->GetClientVersion()) { @@ -505,7 +505,7 @@ ChatChannel *ChatChannelList::AddClientToChannel(std::string ChannelName, Client return nullptr; } - Log(Logs::Detail, Logs::UCS_Server, "AddClient to channel [%s] with password [%s]", NormalisedName.c_str(), Password.c_str()); + LogInfo("AddClient to channel [[{}]] with password [[{}]]", NormalisedName.c_str(), Password.c_str()); ChatChannel *RequiredChannel = FindChannel(NormalisedName); @@ -581,7 +581,7 @@ void ChatChannelList::Process() { if(CurrentChannel && CurrentChannel->ReadyToDelete()) { - Log(Logs::Detail, Logs::UCS_Server, "Empty temporary password protected channel %s being destroyed.", + LogInfo("Empty temporary password protected channel [{}] being destroyed", CurrentChannel->GetName().c_str()); RemoveChannel(CurrentChannel); @@ -597,7 +597,7 @@ void ChatChannel::AddInvitee(const std::string &Invitee) if (!IsInvitee(Invitee)) { Invitees.push_back(Invitee); - Log(Logs::Detail, Logs::UCS_Server, "Added %s as invitee to channel %s", Invitee.c_str(), Name.c_str()); + LogInfo("Added [{}] as invitee to channel [{}]", Invitee.c_str(), Name.c_str()); } } @@ -608,7 +608,7 @@ void ChatChannel::RemoveInvitee(std::string Invitee) if(it != std::end(Invitees)) { Invitees.erase(it); - Log(Logs::Detail, Logs::UCS_Server, "Removed %s as invitee to channel %s", Invitee.c_str(), Name.c_str()); + LogInfo("Removed [{}] as invitee to channel [{}]", Invitee.c_str(), Name.c_str()); } } @@ -622,7 +622,7 @@ void ChatChannel::AddModerator(const std::string &Moderator) if (!IsModerator(Moderator)) { Moderators.push_back(Moderator); - Log(Logs::Detail, Logs::UCS_Server, "Added %s as moderator to channel %s", Moderator.c_str(), Name.c_str()); + LogInfo("Added [{}] as moderator to channel [{}]", Moderator.c_str(), Name.c_str()); } } @@ -633,7 +633,7 @@ void ChatChannel::RemoveModerator(const std::string &Moderator) if (it != std::end(Moderators)) { Moderators.erase(it); - Log(Logs::Detail, Logs::UCS_Server, "Removed %s as moderator to channel %s", Moderator.c_str(), Name.c_str()); + LogInfo("Removed [{}] as moderator to channel [{}]", Moderator.c_str(), Name.c_str()); } } @@ -647,7 +647,7 @@ void ChatChannel::AddVoice(const std::string &inVoiced) if (!HasVoice(inVoiced)) { Voiced.push_back(inVoiced); - Log(Logs::Detail, Logs::UCS_Server, "Added %s as voiced to channel %s", inVoiced.c_str(), Name.c_str()); + LogInfo("Added [{}] as voiced to channel [{}]", inVoiced.c_str(), Name.c_str()); } } @@ -658,7 +658,7 @@ void ChatChannel::RemoveVoice(const std::string &inVoiced) if (it != std::end(Voiced)) { Voiced.erase(it); - Log(Logs::Detail, Logs::UCS_Server, "Removed %s as voiced to channel %s", inVoiced.c_str(), Name.c_str()); + LogInfo("Removed [{}] as voiced to channel [{}]", inVoiced.c_str(), Name.c_str()); } } diff --git a/ucs/clientlist.cpp b/ucs/clientlist.cpp index 26ef62b82..429f28fbf 100644 --- a/ucs/clientlist.cpp +++ b/ucs/clientlist.cpp @@ -20,7 +20,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/global_define.h" #include "../common/string_util.h" #include "../common/eqemu_logsys.h" -#include "../common/eqemu_logsys_fmt.h" #include "../common/misc_functions.h" #include "ucsconfig.h" @@ -236,7 +235,7 @@ std::vector ParseRecipients(std::string RecipientString) { static void ProcessMailTo(Client *c, std::string MailMessage) { - Log(Logs::Detail, Logs::UCS_Server, "MAILTO: From %s, %s", c->MailBoxName().c_str(), MailMessage.c_str()); + LogInfo("MAILTO: From [{}], [{}]", c->MailBoxName().c_str(), MailMessage.c_str()); std::vector Recipients; @@ -305,7 +304,7 @@ static void ProcessMailTo(Client *c, std::string MailMessage) { if (!database.SendMail(Recipient, c->MailBoxName(), Subject, Body, RecipientsString)) { - Log(Logs::Detail, Logs::UCS_Server, "Failed in SendMail(%s, %s, %s, %s)", Recipient.c_str(), + LogInfo("Failed in SendMail([{}], [{}], [{}], [{}])", Recipient.c_str(), c->MailBoxName().c_str(), Subject.c_str(), RecipientsString.c_str()); int PacketLength = 10 + Recipient.length() + Subject.length(); @@ -398,7 +397,7 @@ static void ProcessSetMessageStatus(std::string SetMessageCommand) { static void ProcessCommandBuddy(Client *c, std::string Buddy) { - Log(Logs::Detail, Logs::UCS_Server, "Received buddy command with parameters %s", Buddy.c_str()); + LogInfo("Received buddy command with parameters [{}]", Buddy.c_str()); c->GeneralChannelMessage("Buddy list modified"); uint8 SubAction = 1; @@ -427,7 +426,7 @@ static void ProcessCommandBuddy(Client *c, std::string Buddy) { static void ProcessCommandIgnore(Client *c, std::string Ignoree) { - Log(Logs::Detail, Logs::UCS_Server, "Received ignore command with parameters %s", Ignoree.c_str()); + LogInfo("Received ignore command with parameters [{}]", Ignoree.c_str()); c->GeneralChannelMessage("Ignore list modified"); uint8 SubAction = 0; @@ -483,12 +482,12 @@ Clientlist::Clientlist(int ChatPort) { const ucsconfig *Config = ucsconfig::get(); - Log(Logs::General, Logs::UCS_Server, "Loading '%s'", Config->MailOpCodesFile.c_str()); + LogInfo("Loading [{}]", Config->MailOpCodesFile.c_str()); if (!ChatOpMgr->LoadOpcodes(Config->MailOpCodesFile.c_str())) exit(1); chatsf->OnNewConnection([this](std::shared_ptr stream) { - LogF(Logs::General, Logs::Login_Server, "New Client UDP connection from {0}:{1}", stream->GetRemoteIP(), stream->GetRemotePort()); + LogF(Logs::General, Logs::Loginserver, "New Client UDP connection from {0}:{1}", stream->GetRemoteIP(), stream->GetRemotePort()); stream->SetOpcodeManager(&ChatOpMgr); auto c = new Client(stream); @@ -568,13 +567,13 @@ void Clientlist::CheckForStaleConnections(Client *c) { if (((*Iterator) != c) && ((c->GetName() == (*Iterator)->GetName()) && (c->GetConnectionType() == (*Iterator)->GetConnectionType()))) { - Log(Logs::Detail, Logs::UCS_Server, "Removing old connection for %s", c->GetName().c_str()); + LogInfo("Removing old connection for [{}]", c->GetName().c_str()); struct in_addr in; in.s_addr = (*Iterator)->ClientStream->GetRemoteIP(); - Log(Logs::Detail, Logs::UCS_Server, "Client connection from %s:%d closed.", inet_ntoa(in), + LogInfo("Client connection from [{}]:[{}] closed", inet_ntoa(in), ntohs((*Iterator)->ClientStream->GetRemotePort())); safe_delete((*Iterator)); @@ -593,7 +592,7 @@ void Clientlist::Process() struct in_addr in; in.s_addr = (*it)->ClientStream->GetRemoteIP(); - Log(Logs::Detail, Logs::UCS_Server, "Client connection from %s:%d closed.", inet_ntoa(in), + LogInfo("Client connection from [{}]:[{}] closed", inet_ntoa(in), ntohs((*it)->ClientStream->GetRemotePort())); safe_delete((*it)); @@ -619,8 +618,7 @@ void Clientlist::Process() VARSTRUCT_DECODE_STRING(MailBox, PacketBuffer); if (strlen(PacketBuffer) != 9) { - Log(Logs::Detail, Logs::UCS_Server, - "Mail key is the wrong size. Version of world incompatible with UCS."); + LogInfo("Mail key is the wrong size. Version of world incompatible with UCS."); KeyValid = false; break; } @@ -641,12 +639,11 @@ void Clientlist::Process() else CharacterName = MailBoxString.substr(LastPeriod + 1); - Log(Logs::Detail, Logs::UCS_Server, "Received login for user %s with key %s", + LogInfo("Received login for user [{}] with key [{}]", MailBox, Key); if (!database.VerifyMailKey(CharacterName, (*it)->ClientStream->GetRemoteIP(), Key)) { - Log(Logs::Detail, Logs::UCS_Server, - "Chat Key for %s does not match, closing connection.", MailBox); + LogError("Chat Key for [{}] does not match, closing connection.", MailBox); KeyValid = false; break; } @@ -671,7 +668,7 @@ void Clientlist::Process() } default: { - Log(Logs::Detail, Logs::UCS_Server, "Unhandled chat opcode %8X", opcode); + LogInfo("Unhandled chat opcode {:#04x}", opcode); break; } } @@ -681,10 +678,9 @@ void Clientlist::Process() struct in_addr in; in.s_addr = (*it)->ClientStream->GetRemoteIP(); - Log(Logs::Detail, Logs::UCS_Server, - "Force disconnecting client: %s:%d, KeyValid=%i, GetForceDisconnect()=%i", - inet_ntoa(in), ntohs((*it)->ClientStream->GetRemotePort()), KeyValid, - (*it)->GetForceDisconnect()); + LogInfo("Force disconnecting client: [{}]:[{}], KeyValid=[{}], GetForceDisconnect()=[{}]", + inet_ntoa(in), ntohs((*it)->ClientStream->GetRemotePort()), KeyValid, + (*it)->GetForceDisconnect()); (*it)->ClientStream->Close(); @@ -824,7 +820,7 @@ void Clientlist::ProcessOPMailCommand(Client *c, std::string CommandString) break; case CommandSetMessageStatus: - Log(Logs::Detail, Logs::UCS_Server, "Set Message Status, Params: %s", Parameters.c_str()); + LogInfo("Set Message Status, Params: [{}]", Parameters.c_str()); ProcessSetMessageStatus(Parameters); break; @@ -849,7 +845,7 @@ void Clientlist::ProcessOPMailCommand(Client *c, std::string CommandString) default: c->SendHelp(); - Log(Logs::Detail, Logs::UCS_Server, "Unhandled OP_Mail command: %s", CommandString.c_str()); + LogInfo("Unhandled OP_Mail command: [{}]", CommandString.c_str()); } } @@ -860,7 +856,7 @@ void Clientlist::CloseAllConnections() { for (Iterator = ClientChatConnections.begin(); Iterator != ClientChatConnections.end(); ++Iterator) { - Log(Logs::Detail, Logs::UCS_Server, "Removing client %s", (*Iterator)->GetName().c_str()); + LogInfo("Removing client [{}]", (*Iterator)->GetName().c_str()); (*Iterator)->CloseConnection(); } @@ -869,7 +865,7 @@ void Clientlist::CloseAllConnections() { void Client::AddCharacter(int CharID, const char *CharacterName, int Level) { if (!CharacterName) return; - Log(Logs::Detail, Logs::UCS_Server, "Adding character %s with ID %i for %s", CharacterName, CharID, GetName().c_str()); + LogInfo("Adding character [{}] with ID [{}] for [{}]", CharacterName, CharID, GetName().c_str()); CharacterEntry NewCharacter; NewCharacter.CharID = CharID; NewCharacter.Name = CharacterName; @@ -934,7 +930,7 @@ void Client::AddToChannelList(ChatChannel *JoinedChannel) { for (int i = 0; i < MAX_JOINED_CHANNELS; i++) if (JoinedChannels[i] == nullptr) { JoinedChannels[i] = JoinedChannel; - Log(Logs::Detail, Logs::UCS_Server, "Added Channel %s to slot %i for %s", JoinedChannel->GetName().c_str(), i + 1, GetName().c_str()); + LogInfo("Added Channel [{}] to slot [{}] for [{}]", JoinedChannel->GetName().c_str(), i + 1, GetName().c_str()); return; } } @@ -975,7 +971,7 @@ void Client::JoinChannels(std::string ChannelNameList) { } } - Log(Logs::Detail, Logs::UCS_Server, "Client: %s joining channels %s", GetName().c_str(), ChannelNameList.c_str()); + LogInfo("Client: [{}] joining channels [{}]", GetName().c_str(), ChannelNameList.c_str()); int NumberOfChannels = ChannelCount(); @@ -1074,7 +1070,7 @@ void Client::JoinChannels(std::string ChannelNameList) { void Client::LeaveChannels(std::string ChannelNameList) { - Log(Logs::Detail, Logs::UCS_Server, "Client: %s leaving channels %s", GetName().c_str(), ChannelNameList.c_str()); + LogInfo("Client: [{}] leaving channels [{}]", GetName().c_str(), ChannelNameList.c_str()); std::string::size_type CurrentPos = 0; @@ -1250,7 +1246,7 @@ void Client::SendChannelMessage(std::string Message) std::string ChannelName = Message.substr(1, MessageStart - 1); - Log(Logs::Detail, Logs::UCS_Server, "%s tells %s, [%s]", GetName().c_str(), ChannelName.c_str(), Message.substr(MessageStart + 1).c_str()); + LogInfo("[{}] tells [{}], [[{}]]", GetName().c_str(), ChannelName.c_str(), Message.substr(MessageStart + 1).c_str()); ChatChannel *RequiredChannel = ChannelList->FindChannel(ChannelName); @@ -1393,7 +1389,7 @@ void Client::SendChannelMessageByNumber(std::string Message) { } } - Log(Logs::Detail, Logs::UCS_Server, "%s tells %s, [%s]", GetName().c_str(), RequiredChannel->GetName().c_str(), + LogInfo("[{}] tells [{}], [[{}]]", GetName().c_str(), RequiredChannel->GetName().c_str(), Message.substr(MessageStart + 1).c_str()); if (RuleB(Chat, EnableAntiSpam)) @@ -1601,7 +1597,7 @@ void Client::SetChannelPassword(std::string ChannelPassword) { else Message = "Password change on channel " + ChannelName; - Log(Logs::Detail, Logs::UCS_Server, "Set password of channel [%s] to [%s] by %s", ChannelName.c_str(), Password.c_str(), GetName().c_str()); + LogInfo("Set password of channel [[{}]] to [[{}]] by [{}]", ChannelName.c_str(), Password.c_str(), GetName().c_str()); ChatChannel *RequiredChannel = ChannelList->FindChannel(ChannelName); @@ -1656,7 +1652,7 @@ void Client::SetChannelOwner(std::string CommandString) { if ((ChannelName.length() > 0) && isdigit(ChannelName[0])) ChannelName = ChannelSlotName(atoi(ChannelName.c_str())); - Log(Logs::Detail, Logs::UCS_Server, "Set owner of channel [%s] to [%s]", ChannelName.c_str(), NewOwner.c_str()); + LogInfo("Set owner of channel [[{}]] to [[{}]]", ChannelName.c_str(), NewOwner.c_str()); ChatChannel *RequiredChannel = ChannelList->FindChannel(ChannelName); @@ -1744,7 +1740,7 @@ void Client::ChannelInvite(std::string CommandString) { if ((ChannelName.length() > 0) && isdigit(ChannelName[0])) ChannelName = ChannelSlotName(atoi(ChannelName.c_str())); - Log(Logs::Detail, Logs::UCS_Server, "[%s] invites [%s] to channel [%s]", GetName().c_str(), Invitee.c_str(), ChannelName.c_str()); + LogInfo("[[{}]] invites [[{}]] to channel [[{}]]", GetName().c_str(), Invitee.c_str(), ChannelName.c_str()); Client *RequiredClient = g_Clientlist->FindCharacter(Invitee); @@ -1872,7 +1868,7 @@ void Client::ChannelGrantModerator(std::string CommandString) { if ((ChannelName.length() > 0) && isdigit(ChannelName[0])) ChannelName = ChannelSlotName(atoi(ChannelName.c_str())); - Log(Logs::Detail, Logs::UCS_Server, "[%s] gives [%s] moderator rights to channel [%s]", GetName().c_str(), Moderator.c_str(), ChannelName.c_str()); + LogInfo("[[{}]] gives [[{}]] moderator rights to channel [[{}]]", GetName().c_str(), Moderator.c_str(), ChannelName.c_str()); Client *RequiredClient = g_Clientlist->FindCharacter(Moderator); @@ -1953,7 +1949,7 @@ void Client::ChannelGrantVoice(std::string CommandString) { if ((ChannelName.length() > 0) && isdigit(ChannelName[0])) ChannelName = ChannelSlotName(atoi(ChannelName.c_str())); - Log(Logs::Detail, Logs::UCS_Server, "[%s] gives [%s] voice to channel [%s]", GetName().c_str(), Voicee.c_str(), ChannelName.c_str()); + LogInfo("[[{}]] gives [[{}]] voice to channel [[{}]]", GetName().c_str(), Voicee.c_str(), ChannelName.c_str()); Client *RequiredClient = g_Clientlist->FindCharacter(Voicee); @@ -2041,7 +2037,7 @@ void Client::ChannelKick(std::string CommandString) { if ((ChannelName.length() > 0) && isdigit(ChannelName[0])) ChannelName = ChannelSlotName(atoi(ChannelName.c_str())); - Log(Logs::Detail, Logs::UCS_Server, "[%s] kicks [%s] from channel [%s]", GetName().c_str(), Kickee.c_str(), ChannelName.c_str()); + LogInfo("[[{}]] kicks [[{}]] from channel [[{}]]", GetName().c_str(), Kickee.c_str(), ChannelName.c_str()); Client *RequiredClient = g_Clientlist->FindCharacter(Kickee); @@ -2151,28 +2147,28 @@ void Client::SetConnectionType(char c) { { TypeOfConnection = ConnectionTypeChat; ClientVersion_ = EQEmu::versions::ClientVersion::Titanium; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Chat (Titanium)"); + LogInfo("Connection type is Chat (Titanium)"); break; } case EQEmu::versions::ucsTitaniumMail: { TypeOfConnection = ConnectionTypeMail; ClientVersion_ = EQEmu::versions::ClientVersion::Titanium; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Mail (Titanium)"); + LogInfo("Connection type is Mail (Titanium)"); break; } case EQEmu::versions::ucsSoFCombined: { TypeOfConnection = ConnectionTypeCombined; ClientVersion_ = EQEmu::versions::ClientVersion::SoF; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (SoF)"); + LogInfo("Connection type is Combined (SoF)"); break; } case EQEmu::versions::ucsSoDCombined: { TypeOfConnection = ConnectionTypeCombined; ClientVersion_ = EQEmu::versions::ClientVersion::SoD; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (SoD)"); + LogInfo("Connection type is Combined (SoD)"); break; } case EQEmu::versions::ucsUFCombined: @@ -2180,7 +2176,7 @@ void Client::SetConnectionType(char c) { TypeOfConnection = ConnectionTypeCombined; ClientVersion_ = EQEmu::versions::ClientVersion::UF; UnderfootOrLater = true; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (Underfoot)"); + LogInfo("Connection type is Combined (Underfoot)"); break; } case EQEmu::versions::ucsRoFCombined: @@ -2188,7 +2184,7 @@ void Client::SetConnectionType(char c) { TypeOfConnection = ConnectionTypeCombined; ClientVersion_ = EQEmu::versions::ClientVersion::RoF; UnderfootOrLater = true; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (RoF)"); + LogInfo("Connection type is Combined (RoF)"); break; } case EQEmu::versions::ucsRoF2Combined: @@ -2196,14 +2192,14 @@ void Client::SetConnectionType(char c) { TypeOfConnection = ConnectionTypeCombined; ClientVersion_ = EQEmu::versions::ClientVersion::RoF2; UnderfootOrLater = true; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is Combined (RoF2)"); + LogInfo("Connection type is Combined (RoF2)"); break; } default: { TypeOfConnection = ConnectionTypeUnknown; ClientVersion_ = EQEmu::versions::ClientVersion::Unknown; - Log(Logs::Detail, Logs::UCS_Server, "Connection type is unknown."); + LogInfo("Connection type is unknown"); } } } @@ -2280,12 +2276,12 @@ void Client::SendNotification(int MailBoxNumber, std::string Subject, std::strin void Client::ChangeMailBox(int NewMailBox) { - Log(Logs::Detail, Logs::UCS_Server, "%s Change to mailbox %i", MailBoxName().c_str(), NewMailBox); + LogInfo("[{}] Change to mailbox [{}]", MailBoxName().c_str(), NewMailBox); SetMailBox(NewMailBox); auto id = std::to_string(NewMailBox); - Log(Logs::Detail, Logs::UCS_Server, "New mailbox is %s", MailBoxName().c_str()); + LogInfo("New mailbox is [{}]", MailBoxName().c_str()); auto outapp = new EQApplicationPacket(OP_MailboxChange, id.length() + 1); @@ -2354,13 +2350,13 @@ std::string Client::MailBoxName() { if ((Characters.empty()) || (CurrentMailBox > (Characters.size() - 1))) { - Log(Logs::Detail, Logs::UCS_Server, "MailBoxName() called with CurrentMailBox set to %i and Characters.size() is %i", + LogInfo("MailBoxName() called with CurrentMailBox set to [{}] and Characters.size() is [{}]", CurrentMailBox, Characters.size()); return ""; } - Log(Logs::Detail, Logs::UCS_Server, "MailBoxName() called with CurrentMailBox set to %i and Characters.size() is %i", + LogInfo("MailBoxName() called with CurrentMailBox set to [{}] and Characters.size() is [{}]", CurrentMailBox, Characters.size()); return Characters[CurrentMailBox].Name; diff --git a/ucs/database.cpp b/ucs/database.cpp index 5104c851d..d756f1ff4 100644 --- a/ucs/database.cpp +++ b/ucs/database.cpp @@ -38,8 +38,10 @@ #define strncasecmp _strnicmp #define strcasecmp _stricmp #else + #include "../common/unix.h" #include + #endif #include "database.h" @@ -48,12 +50,12 @@ #include "../common/string_util.h" #include "chatchannel.h" -extern Clientlist *g_Clientlist; +extern Clientlist *g_Clientlist; extern std::string GetMailPrefix(); extern ChatChannelList *ChannelList; -extern uint32 MailMessagesSent; +extern uint32 MailMessagesSent; -Database::Database () +Database::Database() { DBInitVars(); } @@ -62,37 +64,36 @@ Database::Database () Establish a connection to a mysql database with the supplied parameters */ -Database::Database(const char* host, const char* user, const char* passwd, const char* database, uint32 port) +Database::Database(const char *host, const char *user, const char *passwd, const char *database, uint32 port) { DBInitVars(); Connect(host, user, passwd, database, port); } -bool Database::Connect(const char* host, const char* user, const char* passwd, const char* database, uint32 port) +bool Database::Connect(const char *host, const char *user, const char *passwd, const char *database, uint32 port) { - uint32 errnum= 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - if (!Open(host, user, passwd, database, port, &errnum, errbuf)) - { - Log(Logs::General, Logs::Error, "Failed to connect to database: Error: %s", errbuf); + uint32 errnum = 0; + char errbuf[MYSQL_ERRMSG_SIZE]; + if (!Open(host, user, passwd, database, port, &errnum, errbuf)) { + LogError("Failed to connect to database: Error: {}", errbuf); HandleMysqlError(errnum); return false; } - else - { - Log(Logs::General, Logs::Status, "Using database '%s' at %s:%d",database,host,port); + else { + LogInfo("Using database [{}] at [{}]:[{}]", database, host, port); return true; } } -void Database::DBInitVars() { +void Database::DBInitVars() +{ } - -void Database::HandleMysqlError(uint32 errnum) { +void Database::HandleMysqlError(uint32 errnum) +{ } /* @@ -103,22 +104,27 @@ Database::~Database() { } -void Database::GetAccountStatus(Client *client) { +void Database::GetAccountStatus(Client *client) +{ + + std::string query = StringFormat( + "SELECT `status`, `hideme`, `karma`, `revoked` FROM `account` WHERE `id` = '%i' LIMIT 1", + client->GetAccountID() + ); + + auto results = QueryDatabase(query); + if (!results.Success()) { + LogInfo( + "Unable to get account status for character [{}], error [{}]", + client->GetName().c_str(), + results.ErrorMessage().c_str() + ); - std::string query = StringFormat("SELECT `status`, `hideme`, `karma`, `revoked` " - "FROM `account` WHERE `id` = '%i' LIMIT 1", - client->GetAccountID()); - auto results = QueryDatabase(query); - if (!results.Success()) { - Log(Logs::Detail, Logs::UCS_Server, "Unable to get account status for character %s, error %s", client->GetName().c_str(), results.ErrorMessage().c_str()); return; } - Log(Logs::Detail, Logs::UCS_Server, "GetAccountStatus Query: %s", query.c_str()); - - if(results.RowCount() != 1) - { - Log(Logs::Detail, Logs::UCS_Server, "Error in GetAccountStatus"); + if (results.RowCount() != 1) { + LogInfo("Error in GetAccountStatus"); return; } @@ -127,29 +133,34 @@ void Database::GetAccountStatus(Client *client) { client->SetAccountStatus(atoi(row[0])); client->SetHideMe(atoi(row[1]) != 0); client->SetKarma(atoi(row[2])); - client->SetRevoked((atoi(row[3])==1?true:false)); - - Log(Logs::Detail, Logs::UCS_Server, "Set account status to %i, hideme to %i and karma to %i for %s", client->GetAccountStatus(), client->GetHideMe(), client->GetKarma(), client->GetName().c_str()); + client->SetRevoked((atoi(row[3]) == 1 ? true : false)); + LogDebug( + "Set account status to [{}], hideme to [{}] and karma to [{}] for [{}]", + client->GetAccountStatus(), + client->GetHideMe(), + client->GetKarma(), + client->GetName().c_str() + ); } -int Database::FindAccount(const char *characterName, Client *client) { - - Log(Logs::Detail, Logs::UCS_Server, "FindAccount for character %s", characterName); +int Database::FindAccount(const char *characterName, Client *client) +{ + LogInfo("FindAccount for character [{}]", characterName); client->ClearCharacters(); - std::string query = StringFormat("SELECT `id`, `account_id`, `level` " - "FROM `character_data` WHERE `name` = '%s' LIMIT 1", - characterName); - auto results = QueryDatabase(query); + std::string query = StringFormat( + "SELECT `id`, `account_id`, `level` FROM `character_data` WHERE `name` = '%s' LIMIT 1", + characterName + ); + auto results = QueryDatabase(query); + if (!results.Success()) { - Log(Logs::Detail, Logs::UCS_Server, "FindAccount query failed: %s", query.c_str()); return -1; } if (results.RowCount() != 1) { - Log(Logs::Detail, Logs::UCS_Server, "Bad result from query"); return -1; } @@ -158,14 +169,18 @@ int Database::FindAccount(const char *characterName, Client *client) { int accountID = atoi(row[1]); - Log(Logs::Detail, Logs::UCS_Server, "Account ID for %s is %i", characterName, accountID); + LogInfo("Account ID for [{}] is [{}]", characterName, accountID); - query = StringFormat("SELECT `id`, `name`, `level` FROM `character_data` " - "WHERE `account_id` = %i AND `name` != '%s'", - accountID, characterName); - results = QueryDatabase(query); - if (!results.Success()) + query = StringFormat( + "SELECT `id`, `name`, `level` FROM `character_data` " + "WHERE `account_id` = %i AND `name` != '%s'", + accountID, characterName + ); + + results = QueryDatabase(query); + if (!results.Success()) { return accountID; + } for (auto row = results.begin(); row != results.end(); ++row) client->AddCharacter(atoi(row[0]), row[1], atoi(row[2])); @@ -173,13 +188,17 @@ int Database::FindAccount(const char *characterName, Client *client) { return accountID; } -bool Database::VerifyMailKey(std::string characterName, int IPAddress, std::string MailKey) { +bool Database::VerifyMailKey(std::string characterName, int IPAddress, std::string MailKey) +{ - std::string query = StringFormat("SELECT `mailkey` FROM `character_data` WHERE `name`='%s' LIMIT 1", - characterName.c_str()); - auto results = QueryDatabase(query); + std::string query = StringFormat( + "SELECT `mailkey` FROM `character_data` WHERE `name`='%s' LIMIT 1", + characterName.c_str() + ); + + auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::Detail, Logs::UCS_Server, "Error retrieving mailkey from database: %s", results.ErrorMessage().c_str()); + LogInfo("Error retrieving mailkey from database: [{}]", results.ErrorMessage().c_str()); return false; } @@ -190,21 +209,26 @@ bool Database::VerifyMailKey(std::string characterName, int IPAddress, std::stri // char combinedKey[17]; - if(RuleB(Chat, EnableMailKeyIPVerification) == true) + if (RuleB(Chat, EnableMailKeyIPVerification) == true) { sprintf(combinedKey, "%08X%s", IPAddress, MailKey.c_str()); - else + } + else { sprintf(combinedKey, "%s", MailKey.c_str()); + } - Log(Logs::Detail, Logs::UCS_Server, "DB key is [%s], Client key is [%s]", (row[0] ? row[0] : ""), combinedKey); + LogInfo("DB key is [[{}]], Client key is [[{}]]", (row[0] ? row[0] : ""), combinedKey); return !strcmp(row[0], combinedKey); } int Database::FindCharacter(const char *characterName) { - char *safeCharName = RemoveApostrophes(characterName); - std::string query = StringFormat("SELECT `id` FROM `character_data` WHERE `name`='%s' LIMIT 1", safeCharName); - auto results = QueryDatabase(query); + char *safeCharName = RemoveApostrophes(characterName); + std::string query = StringFormat( + "SELECT `id` FROM `character_data` WHERE `name`='%s' LIMIT 1", + safeCharName + ); + auto results = QueryDatabase(query); if (!results.Success()) { safe_delete_array(safeCharName); return -1; @@ -213,8 +237,7 @@ int Database::FindCharacter(const char *characterName) safe_delete_array(safeCharName); if (results.RowCount() != 1) { - Log(Logs::Detail, Logs::UCS_Server, "Bad result from FindCharacter query for character %s", - characterName); + LogInfo("Bad result from FindCharacter query for character [{}]", characterName); return -1; } @@ -225,16 +248,18 @@ int Database::FindCharacter(const char *characterName) return characterID; } -bool Database::GetVariable(const char* varname, char* varvalue, uint16 varvalue_len) { +bool Database::GetVariable(const char *varname, char *varvalue, uint16 varvalue_len) +{ - std::string query = StringFormat("SELECT `value` FROM `variables` WHERE `varname` = '%s'", varname); - auto results = QueryDatabase(query); + std::string query = StringFormat("SELECT `value` FROM `variables` WHERE `varname` = '%s'", varname); + auto results = QueryDatabase(query); if (!results.Success()) { return false; } - if (results.RowCount() != 1) + if (results.RowCount() != 1) { return false; + } auto row = results.begin(); @@ -243,19 +268,20 @@ bool Database::GetVariable(const char* varname, char* varvalue, uint16 varvalue_ return true; } -bool Database::LoadChatChannels() { +bool Database::LoadChatChannels() +{ - Log(Logs::Detail, Logs::UCS_Server, "Loading chat channels from the database."); + LogInfo("Loading chat channels from the database"); - const std::string query = "SELECT `name`, `owner`, `password`, `minstatus` FROM `chatchannels`"; - auto results = QueryDatabase(query); + const std::string query = "SELECT `name`, `owner`, `password`, `minstatus` FROM `chatchannels`"; + auto results = QueryDatabase(query); if (!results.Success()) { return false; } - for (auto row = results.begin();row != results.end(); ++row) { - std::string channelName = row[0]; - std::string channelOwner = row[1]; + for (auto row = results.begin(); row != results.end(); ++row) { + std::string channelName = row[0]; + std::string channelOwner = row[1]; std::string channelPassword = row[2]; ChannelList->CreateChannel(channelName, channelOwner, channelPassword, true, atoi(row[3])); @@ -264,40 +290,50 @@ bool Database::LoadChatChannels() { return true; } -void Database::SetChannelPassword(std::string channelName, std::string password) { +void Database::SetChannelPassword(std::string channelName, std::string password) +{ + LogInfo("Database::SetChannelPassword([{}], [{}])", channelName.c_str(), password.c_str()); - Log(Logs::Detail, Logs::UCS_Server, "Database::SetChannelPassword(%s, %s)", channelName.c_str(), password.c_str()); - - std::string query = StringFormat("UPDATE `chatchannels` SET `password` = '%s' WHERE `name` = '%s'", - password.c_str(), channelName.c_str()); - QueryDatabase(query); + std::string query = StringFormat( + "UPDATE `chatchannels` SET `password` = '%s' WHERE `name` = '%s'", + password.c_str(), channelName.c_str()); + QueryDatabase(query); } -void Database::SetChannelOwner(std::string channelName, std::string owner) { +void Database::SetChannelOwner(std::string channelName, std::string owner) +{ + LogInfo("Database::SetChannelOwner([{}], [{}])", channelName.c_str(), owner.c_str()); - Log(Logs::Detail, Logs::UCS_Server, "Database::SetChannelOwner(%s, %s)", channelName.c_str(), owner.c_str()); + std::string query = StringFormat( + "UPDATE `chatchannels` SET `owner` = '%s' WHERE `name` = '%s'", + owner.c_str(), + channelName.c_str() + ); - std::string query = StringFormat("UPDATE `chatchannels` SET `owner` = '%s' WHERE `name` = '%s'", - owner.c_str(), channelName.c_str()); - QueryDatabase(query); + QueryDatabase(query); } -void Database::SendHeaders(Client *client) { +void Database::SendHeaders(Client *client) +{ int unknownField2 = 25015275; int unknownField3 = 1; - int characterID = FindCharacter(client->MailBoxName().c_str()); + int characterID = FindCharacter(client->MailBoxName().c_str()); - Log(Logs::Detail, Logs::UCS_Server, "Sendheaders for %s, CharID is %i", client->MailBoxName().c_str(), characterID); + LogInfo("Sendheaders for [{}], CharID is [{}]", client->MailBoxName().c_str(), characterID); - if(characterID <= 0) + if (characterID <= 0) { return; + } - std::string query = StringFormat("SELECT `msgid`,`timestamp`, `from`, `subject`, `status` " - "FROM `mail` WHERE `charid`=%i", characterID); - auto results = QueryDatabase(query); - if (!results.Success()) + std::string query = StringFormat( + "SELECT `msgid`,`timestamp`, `from`, `subject`, `status` " + "FROM `mail` WHERE `charid`=%i", characterID + ); + auto results = QueryDatabase(query); + if (!results.Success()) { return; + } char buffer[100]; @@ -317,7 +353,7 @@ void Database::SendHeaders(Client *client) { auto outapp = new EQApplicationPacket(OP_MailHeaderCount, headerCountPacketLength); - char *packetBuffer = (char *)outapp->pBuffer; + char *packetBuffer = (char *) outapp->pBuffer; VARSTRUCT_ENCODE_INTSTRING(packetBuffer, client->GetMailBoxNumber()); VARSTRUCT_ENCODE_INTSTRING(packetBuffer, unknownField2); @@ -329,8 +365,8 @@ void Database::SendHeaders(Client *client) { safe_delete(outapp); - int rowIndex = 0; - for(auto row = results.begin(); row != results.end(); ++row, ++rowIndex) { + int rowIndex = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++rowIndex) { int headerPacketLength = 0; sprintf(buffer, "%i", client->GetMailBoxNumber()); @@ -348,7 +384,7 @@ void Database::SendHeaders(Client *client) { outapp = new EQApplicationPacket(OP_MailHeader, headerPacketLength); - packetBuffer = (char *)outapp->pBuffer; + packetBuffer = (char *) outapp->pBuffer; VARSTRUCT_ENCODE_INTSTRING(packetBuffer, client->GetMailBoxNumber()); VARSTRUCT_ENCODE_INTSTRING(packetBuffer, unknownField2); @@ -369,39 +405,48 @@ void Database::SendHeaders(Client *client) { } -void Database::SendBody(Client *client, int messageNumber) { +void Database::SendBody(Client *client, int messageNumber) +{ int characterID = FindCharacter(client->MailBoxName().c_str()); - Log(Logs::Detail, Logs::UCS_Server, "SendBody: MsgID %i, to %s, CharID is %i", messageNumber, client->MailBoxName().c_str(), characterID); + LogInfo("SendBody: MsgID [{}], to [{}], CharID is [{}]", messageNumber, client->MailBoxName().c_str(), characterID); - if(characterID <= 0) + if (characterID <= 0) { return; + } - std::string query = StringFormat("SELECT `msgid`, `body`, `to` FROM `mail` " - "WHERE `charid`=%i AND `msgid`=%i", characterID, messageNumber); - auto results = QueryDatabase(query); - if (!results.Success()) + std::string query = StringFormat( + "SELECT `msgid`, `body`, `to` FROM `mail` " + "WHERE `charid`=%i AND `msgid`=%i", + characterID, + messageNumber + ); + + auto results = QueryDatabase(query); + if (!results.Success()) { return; + } - if (results.RowCount() != 1) + if (results.RowCount() != 1) { return; + } auto row = results.begin(); - Log(Logs::Detail, Logs::UCS_Server, "Message: %i body (%i bytes)", messageNumber, strlen(row[1])); + LogInfo("Message: [{}] body ([{}] bytes)", messageNumber, strlen(row[1])); int packetLength = 12 + strlen(row[0]) + strlen(row[1]) + strlen(row[2]); auto outapp = new EQApplicationPacket(OP_MailSendBody, packetLength); - char *packetBuffer = (char *)outapp->pBuffer; + char *packetBuffer = (char *) outapp->pBuffer; VARSTRUCT_ENCODE_INTSTRING(packetBuffer, client->GetMailBoxNumber()); - VARSTRUCT_ENCODE_STRING(packetBuffer,row[0]); - VARSTRUCT_ENCODE_STRING(packetBuffer,row[1]); - VARSTRUCT_ENCODE_STRING(packetBuffer,"1"); + VARSTRUCT_ENCODE_STRING(packetBuffer, row[0]); + VARSTRUCT_ENCODE_STRING(packetBuffer, row[1]); + VARSTRUCT_ENCODE_STRING(packetBuffer, "1"); VARSTRUCT_ENCODE_TYPE(uint8, packetBuffer, 0); VARSTRUCT_ENCODE_TYPE(uint8, packetBuffer, 0x0a); VARSTRUCT_ENCODE_STRING(packetBuffer, "TO:"); @@ -410,62 +455,75 @@ void Database::SendBody(Client *client, int messageNumber) { packetBuffer--; // Overwrite the null terminator VARSTRUCT_ENCODE_TYPE(uint8, packetBuffer, 0x0a); - client->QueuePacket(outapp); safe_delete(outapp); } -bool Database::SendMail(std::string recipient, std::string from, std::string subject, std::string body, std::string recipientsString) { +bool Database::SendMail( + std::string recipient, + std::string from, + std::string subject, + std::string body, + std::string recipientsString +) +{ - int characterID; + int characterID; std::string characterName; auto lastPeriod = recipient.find_last_of("."); - if(lastPeriod == std::string::npos) + if (lastPeriod == std::string::npos) { characterName = recipient; - else - characterName = recipient.substr(lastPeriod+1); + } + else { + characterName = recipient.substr(lastPeriod + 1); + } characterName[0] = toupper(characterName[0]); - for(unsigned int i = 1; i < characterName.length(); i++) + for (unsigned int i = 1; i < characterName.length(); i++) characterName[i] = tolower(characterName[i]); characterID = FindCharacter(characterName.c_str()); - Log(Logs::Detail, Logs::UCS_Server, "SendMail: CharacterID for recipient %s is %i", characterName.c_str(), characterID); + LogInfo("SendMail: CharacterID for recipient [{}] is [{}]", characterName.c_str(), characterID); - if(characterID <= 0) - return false; + if (characterID <= 0) { + return false; + } auto escSubject = new char[subject.length() * 2 + 1]; - auto escBody = new char[body.length() * 2 + 1]; + auto escBody = new char[body.length() * 2 + 1]; DoEscapeString(escSubject, subject.c_str(), subject.length()); DoEscapeString(escBody, body.c_str(), body.length()); int now = time(nullptr); // time returns a 64 bit int on Windows at least, which vsnprintf doesn't like. - std::string query = StringFormat("INSERT INTO `mail` " - "(`charid`, `timestamp`, `from`, `subject`, `body`, `to`, `status`) " - "VALUES ('%i', %i, '%s', '%s', '%s', '%s', %i)", - characterID, now, from.c_str(), escSubject, escBody, - recipientsString.c_str(), 1); - safe_delete_array(escSubject); - safe_delete_array(escBody); - auto results = QueryDatabase(query); - if(!results.Success()) { + std::string query = StringFormat( + "INSERT INTO `mail` (`charid`, `timestamp`, `from`, `subject`, `body`, `to`, `status`) VALUES ('%i', %i, '%s', '%s', '%s', '%s', %i)", + characterID, + now, + from.c_str(), + escSubject, + escBody, + recipientsString.c_str(), + 1 + ); + safe_delete_array(escSubject); + safe_delete_array(escBody); + auto results = QueryDatabase(query); + if (!results.Success()) { return false; } - Log(Logs::Detail, Logs::UCS_Server, "MessageID %i generated, from %s, to %s", results.LastInsertedID(), from.c_str(), recipient.c_str()); - + LogInfo("MessageID [{}] generated, from [{}], to [{}]", results.LastInsertedID(), from.c_str(), recipient.c_str()); Client *client = g_Clientlist->IsCharacterOnline(characterName); - if(client) { + if (client) { std::string FQN = GetMailPrefix() + from; client->SendNotification(client->GetMailBoxNumber(characterName), subject, FQN, results.LastInsertedID()); } @@ -475,91 +533,127 @@ bool Database::SendMail(std::string recipient, std::string from, std::string sub return true; } -void Database::SetMessageStatus(int messageNumber, int status) { +void Database::SetMessageStatus(int messageNumber, int status) +{ - Log(Logs::Detail, Logs::UCS_Server, "SetMessageStatus %i %i", messageNumber, status); + LogInfo("SetMessageStatus [{}] [{}]", messageNumber, status); - if(status == 0) { - std::string query = StringFormat("DELETE FROM `mail` WHERE `msgid` = %i", messageNumber); - auto results = QueryDatabase(query); - return; - } + if (status == 0) { + std::string query = StringFormat("DELETE FROM `mail` WHERE `msgid` = %i", messageNumber); + auto results = QueryDatabase(query); + return; + } - std::string query = StringFormat("UPDATE `mail` SET `status` = %i WHERE `msgid`=%i", status, messageNumber); - QueryDatabase(query); + std::string query = StringFormat("UPDATE `mail` SET `status` = %i WHERE `msgid`=%i", status, messageNumber); + QueryDatabase(query); } -void Database::ExpireMail() { +void Database::ExpireMail() +{ - Log(Logs::Detail, Logs::UCS_Server, "Expiring mail..."); + LogInfo("Expiring mail"); - std::string query = "SELECT COUNT(*) FROM `mail`"; - auto results = QueryDatabase(query); - if (!results.Success()) { + std::string query = "SELECT COUNT(*) FROM `mail`"; + auto results = QueryDatabase(query); + if (!results.Success()) { return; } auto row = results.begin(); - Log(Logs::Detail, Logs::UCS_Server, "There are %s messages in the database.", row[0]); + LogInfo("There are [{}] messages in the database", row[0]); - // Expire Trash - if(RuleI(Mail, ExpireTrash) >= 0) { - query = StringFormat("DELETE FROM `mail` WHERE `status`=4 AND `timestamp` < %i", - time(nullptr) - RuleI(Mail, ExpireTrash)); - results = QueryDatabase(query); - if(results.Success()) - Log(Logs::Detail, Logs::UCS_Server, "Expired %i trash messages.", results.RowsAffected()); + /** + * Expire trash + */ + if (RuleI(Mail, ExpireTrash) >= 0) { + query = StringFormat( + "DELETE FROM `mail` WHERE `status`=4 AND `timestamp` < %i", + time(nullptr) - RuleI(Mail, ExpireTrash)); + results = QueryDatabase(query); + if (results.Success()) { + LogInfo("Expired [{}] trash messages", results.RowsAffected()); + } } - // Expire Read - if(RuleI(Mail, ExpireRead) >= 0) { - query = StringFormat("DELETE FROM `mail` WHERE `status` = 3 AND `timestamp` < %i", - time(nullptr) - RuleI(Mail, ExpireRead)); - results = QueryDatabase(query); - if(results.Success()) - Log(Logs::Detail, Logs::UCS_Server, "Expired %i read messages.", results.RowsAffected()); + /** + * Expire read + */ + if (RuleI(Mail, ExpireRead) >= 0) { + query = StringFormat( + "DELETE FROM `mail` WHERE `status` = 3 AND `timestamp` < %i", + time(nullptr) - RuleI(Mail, ExpireRead) + ); + + results = QueryDatabase(query); + if (results.Success()) + LogInfo("Expired [{}] read messages", results.RowsAffected()); } - // Expire Unread - if(RuleI(Mail, ExpireUnread) >= 0) { - query = StringFormat("DELETE FROM `mail` WHERE `status`=1 AND `timestamp` < %i", - time(nullptr) - RuleI(Mail, ExpireUnread)); - results = QueryDatabase(query); - if(results.Success()) - Log(Logs::Detail, Logs::UCS_Server, "Expired %i unread messages.", results.RowsAffected()); + /** + * Expire unread + */ + if (RuleI(Mail, ExpireUnread) >= 0) { + query = StringFormat( + "DELETE FROM `mail` WHERE `status`=1 AND `timestamp` < %i", + time(nullptr) - RuleI(Mail, ExpireUnread) + ); + + results = QueryDatabase(query); + if (results.Success()) { + LogInfo("Expired [{}] unread messages", results.RowsAffected()); + } } } -void Database::AddFriendOrIgnore(int charID, int type, std::string name) { +void Database::AddFriendOrIgnore(int charID, int type, std::string name) +{ + std::string query = StringFormat( + "INSERT INTO `friends` (`charid`, `type`, `name`) " + "VALUES('%i', %i, '%s')", + charID, + type, + CapitaliseName(name).c_str() + ); - std::string query = StringFormat("INSERT INTO `friends` (`charid`, `type`, `name`) " - "VALUES('%i', %i, '%s')", - charID, type, CapitaliseName(name).c_str()); - auto results = QueryDatabase(query); - if(results.Success()) - Log(Logs::Detail, Logs::UCS_Server, "Wrote Friend/Ignore entry for charid %i, type %i, name %s to database.", charID, type, name.c_str()); + auto results = QueryDatabase(query); -} - -void Database::RemoveFriendOrIgnore(int charID, int type, std::string name) { - - std::string query = StringFormat("DELETE FROM `friends` WHERE `charid` = %i " - "AND `type` = %i AND `name` = '%s'", - charID, type, CapitaliseName(name).c_str()); - auto results = QueryDatabase(query); - if (!results.Success()) { - Log(Logs::Detail, Logs::UCS_Server, "Error removing friend/ignore, query was %s", query.c_str()); - } - else { - Log(Logs::Detail, Logs::UCS_Server, "Removed Friend/Ignore entry for charid %i, type %i, name %s from database.", charID, type, name.c_str()); + if (results.Success()) { + LogInfo( + "Wrote Friend/Ignore entry for charid [{}], type [{}], name [{}] to database", + charID, + type, + name.c_str() + ); } } -void Database::GetFriendsAndIgnore(int charID, std::vector &friends, std::vector &ignorees) { +void Database::RemoveFriendOrIgnore(int charID, int type, std::string name) +{ + std::string query = StringFormat( + "DELETE FROM `friends` WHERE `charid` = %i AND `type` = %i AND `name` = '%s'", + charID, + type, + CapitaliseName(name).c_str() + ); - std::string query = StringFormat("select `type`, `name` FROM `friends` WHERE `charid`=%i", charID); - auto results = QueryDatabase(query); + auto results = QueryDatabase(query); + + if (results.Success()) { + LogInfo( + "Removed Friend/Ignore entry for charid [{}], type [{}], name [{}] from database", + charID, + type, + name.c_str() + ); + } +} + +void Database::GetFriendsAndIgnore(int charID, std::vector &friends, std::vector &ignorees) +{ + + std::string query = StringFormat("select `type`, `name` FROM `friends` WHERE `charid`=%i", charID); + auto results = QueryDatabase(query); if (!results.Success()) { return; } @@ -568,57 +662,100 @@ void Database::GetFriendsAndIgnore(int charID, std::vector &friends for (auto row = results.begin(); row != results.end(); ++row) { std::string name = row[1]; - if(atoi(row[0]) == 0) - { + if (atoi(row[0]) == 0) { ignorees.push_back(name); - Log(Logs::Detail, Logs::UCS_Server, "Added Ignoree from DB %s", name.c_str()); + LogInfo("Added Ignoree from DB [{}]", name.c_str()); continue; } - friends.push_back(name); - Log(Logs::Detail, Logs::UCS_Server, "Added Friend from DB %s", name.c_str()); + friends.push_back(name); + LogInfo("Added Friend from DB [{}]", name.c_str()); } } -void Database::LoadLogSettings(EQEmuLogSys::LogSettings* log_settings){ +void Database::LoadLogSettings(EQEmuLogSys::LogSettings *log_settings) +{ std::string query = - "SELECT " - "log_category_id, " - "log_category_description, " - "log_to_console, " - "log_to_file, " - "log_to_gmsay " - "FROM " - "logsys_categories " - "ORDER BY log_category_id"; - auto results = QueryDatabase(query); + "SELECT " + "log_category_id, " + "log_category_description, " + "log_to_console, " + "log_to_file, " + "log_to_gmsay " + "FROM " + "logsys_categories " + "ORDER BY log_category_id"; - int log_category = 0; - LogSys.file_logs_enabled = false; + auto results = QueryDatabase(query); + int log_category_id = 0; + + int *categories_in_database = new int[1000]; for (auto row = results.begin(); row != results.end(); ++row) { - log_category = atoi(row[0]); - log_settings[log_category].log_to_console = atoi(row[2]); - log_settings[log_category].log_to_file = atoi(row[3]); - log_settings[log_category].log_to_gmsay = atoi(row[4]); + log_category_id = atoi(row[0]); + if (log_category_id <= Logs::None || log_category_id >= Logs::MaxCategoryID) { + continue; + } - /* 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[log_category].log_to_console > 0; - const bool log_to_file = log_settings[log_category].log_to_file > 0; - const bool log_to_gmsay = log_settings[log_category].log_to_gmsay > 0; + log_settings[log_category_id].log_to_console = static_cast(atoi(row[2])); + log_settings[log_category_id].log_to_file = static_cast(atoi(row[3])); + log_settings[log_category_id].log_to_gmsay = static_cast(atoi(row[4])); + + /** + * 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[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; - if (is_category_enabled) - log_settings[log_category].is_category_enabled = 1; + if (is_category_enabled) { + log_settings[log_category_id].is_category_enabled = 1; + } - /* - This determines whether or not the process needs to actually file log anything. - If we go through this whole loop and nothing is set to any debug level, there is no point to create a file or keep anything open - */ - if (log_settings[log_category].log_to_file > 0){ + /** + * This determines whether or not the process needs to actually file log anything. + * If we go through this whole loop and nothing is set to any debug level, there is no point to create a file or keep anything open + */ + if (log_settings[log_category_id].log_to_file > 0) { LogSys.file_logs_enabled = true; } + + categories_in_database[log_category_id] = 1; } + + /** + * Auto inject categories that don't exist in the database... + */ + for (int log_index = Logs::AA; log_index != Logs::MaxCategoryID; log_index++) { + if (categories_in_database[log_index] != 1) { + + LogInfo( + "New Log Category [{0}] doesn't exist... Automatically adding to [logsys_categories] table...", + Logs::LogCategoryName[log_index] + ); + + auto inject_query = fmt::format( + "INSERT INTO logsys_categories " + "(log_category_id, " + "log_category_description, " + "log_to_console, " + "log_to_file, " + "log_to_gmsay) " + "VALUES " + "({0}, '{1}', {2}, {3}, {4})", + log_index, + EscapeString(Logs::LogCategoryName[log_index]), + std::to_string(log_settings[log_index].log_to_console), + std::to_string(log_settings[log_index].log_to_file), + std::to_string(log_settings[log_index].log_to_gmsay) + ); + + QueryDatabase(inject_query); + } + } + + delete[] categories_in_database; } diff --git a/ucs/ucs.cpp b/ucs/ucs.cpp index bd36c47e6..0f396252b 100644 --- a/ucs/ucs.cpp +++ b/ucs/ucs.cpp @@ -73,10 +73,10 @@ int main() { Timer InterserverTimer(INTERSERVER_TIMER); // does auto-reconnect - Log(Logs::General, Logs::UCS_Server, "Starting EQEmu Universal Chat Server."); + LogInfo("Starting EQEmu Universal Chat Server"); if (!ucsconfig::LoadConfig()) { - Log(Logs::General, Logs::UCS_Server, "Loading server configuration failed."); + LogInfo("Loading server configuration failed"); return 1; } @@ -84,7 +84,7 @@ int main() { WorldShortName = Config->ShortName; - Log(Logs::General, Logs::UCS_Server, "Connecting to MySQL..."); + LogInfo("Connecting to MySQL"); if (!database.Connect( Config->DatabaseHost.c_str(), @@ -92,7 +92,7 @@ int main() { Config->DatabasePassword.c_str(), Config->DatabaseDB.c_str(), Config->DatabasePort)) { - Log(Logs::General, Logs::UCS_Server, "Cannot continue without a database connection."); + LogInfo("Cannot continue without a database connection"); return 1; } @@ -104,26 +104,26 @@ int main() { // ucs has no 'reload rules' handler if (database.GetVariable("RuleSet", tmp, sizeof(tmp)-1)) { - Log(Logs::General, Logs::UCS_Server, "Loading rule set '%s'", tmp); + LogInfo("Loading rule set [{}]", tmp); if(!RuleManager::Instance()->LoadRules(&database, tmp, false)) { - Log(Logs::General, Logs::UCS_Server, "Failed to load ruleset '%s', falling back to defaults.", tmp); + LogInfo("Failed to load ruleset [{}], falling back to defaults", tmp); } } else { if(!RuleManager::Instance()->LoadRules(&database, "default", false)) { - Log(Logs::General, Logs::UCS_Server, "No rule set configured, using default rules"); + LogInfo("No rule set configured, using default rules"); } else { - Log(Logs::General, Logs::UCS_Server, "Loaded default rule set 'default'", tmp); + LogInfo("Loaded default rule set 'default'", tmp); } } EQEmu::InitializeDynamicLookups(); - Log(Logs::General, Logs::UCS_Server, "Initialized dynamic dictionary entries"); + LogInfo("Initialized dynamic dictionary entries"); database.ExpireMail(); if(Config->ChatPort != Config->MailPort) { - Log(Logs::General, Logs::UCS_Server, "MailPort and CharPort must be the same in eqemu_config.xml for UCS."); + LogInfo("MailPort and CharPort must be the same in eqemu_config.json for UCS"); exit(1); } @@ -134,11 +134,11 @@ int main() { database.LoadChatChannels(); if (signal(SIGINT, CatchSignal) == SIG_ERR) { - Log(Logs::General, Logs::UCS_Server, "Could not set signal handler"); + LogInfo("Could not set signal handler"); return 1; } if (signal(SIGTERM, CatchSignal) == SIG_ERR) { - Log(Logs::General, Logs::UCS_Server, "Could not set signal handler"); + LogInfo("Could not set signal handler"); return 1; } diff --git a/ucs/worldserver.cpp b/ucs/worldserver.cpp index b3df5d99d..72865e416 100644 --- a/ucs/worldserver.cpp +++ b/ucs/worldserver.cpp @@ -61,7 +61,7 @@ void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p) ServerPacket tpack(opcode, p); ServerPacket *pack = &tpack; - Log(Logs::Detail, Logs::UCS_Server, "Received Opcode: %4X", opcode); + LogInfo("Received Opcode: {:#04x}", opcode); switch (opcode) { @@ -82,7 +82,7 @@ void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p) std::string Message = Buffer; - Log(Logs::Detail, Logs::UCS_Server, "Player: %s, Sent Message: %s", From, Message.c_str()); + LogInfo("Player: [{}], Sent Message: [{}]", From, Message.c_str()); Client *c = g_Clientlist->FindCharacter(From); @@ -93,7 +93,7 @@ void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p) if (!c) { - Log(Logs::Detail, Logs::UCS_Server, "Client not found."); + LogInfo("Client not found"); break; } diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index ef8b0babd..e3a051ef7 100644 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -399,11 +399,13 @@ sub build_linux_source { print `git clone https://github.com/EQEmu/Server.git`; mkdir($source_dir . "/Server/build") if (!-e $source_dir . "/Server/build"); - chdir($source_dir . "/Server/build"); + chdir($source_dir . "/Server"); print `git submodule init`; print `git submodule update`; + chdir($source_dir . "/Server/build"); + print "Generating CMake build files...\n"; if ($os_flavor eq "fedora_core") { print `cmake $cmake_options -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -DLUA_INCLUDE_DIR=/usr/include/lua-5.1/ -G "Unix Makefiles" ..`; @@ -470,9 +472,19 @@ sub do_installer_routines { print `"$path" --host $host --user $user --password="$pass" -N -B -e "DROP DATABASE IF EXISTS $db_name;"`; print `"$path" --host $host --user $user --password="$pass" -N -B -e "CREATE DATABASE $db_name"`; + my $world_path = "world"; + if (-e "bin/world") { + $world_path = "bin/world"; + } + #::: Get Binary DB version - if ($OS eq "Windows") { @db_version = split(': ', `world db_version`); } - if ($OS eq "Linux") { @db_version = split(': ', `./world db_version`); } + if ($OS eq "Windows") { + @db_version = split(': ', `$world_path db_version`); + } + if ($OS eq "Linux") { + @db_version = split(': ', `./$world_path db_version`); + } + $binary_database_version = trim($db_version[1]); #::: Local DB Version diff --git a/utils/scripts/linux_installer/install.sh b/utils/scripts/linux_installer/install.sh index 6af4a9df3..5cef4ab8b 100644 --- a/utils/scripts/linux_installer/install.sh +++ b/utils/scripts/linux_installer/install.sh @@ -153,7 +153,7 @@ elif [[ "$OS" == "red_hat" ]]; then yum -y install \ open-vm-tools \ vim \ - cmake \ + cmake3 \ boost-* \ zlib-devel \ mariadb \ @@ -178,6 +178,12 @@ elif [[ "$OS" == "red_hat" ]]; then "Development Tools" \ "Basic Web Server" \ "Compatibility Libraries" + # Deal with the cmake 3 prerequisite on RHEL/CentOS 6/7 Note: Might break with RHEL/CentOS 8 + alternatives --install /usr/local/bin/cmake cmake /usr/bin/cmake3 20 \ + --slave /usr/local/bin/ctest ctest /usr/bin/ctest3 \ + --slave /usr/local/bin/cpack cpack /usr/bin/cpack3 \ + --slave /usr/local/bin/ccmake ccmake /usr/bin/ccmake3 \ + --family cmake elif [[ "$OS" == "fedora_core" ]]; then # Do Fedora stuff diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 4addfe6ce..86ed07b07 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -395,6 +395,8 @@ 9139|2019_03_25_optional_npc_model.sql|SHOW COLUMNS FROM `npc_types` LIKE 'model'|empty| 9140|2019_07_03_update_range.sql|SHOW COLUMNS FROM `zone` LIKE 'max_movement_update_range'|empty| 9141|2019_07_10_npc_flymode.sql|SHOW COLUMNS FROM `npc_types` LIKE 'flymode'|empty| +9142|2019_09_02_required_spawn_filter.sql|SHOW COLUMNS FROM `spawnentry` LIKE 'condition_value_filter'|empty| +9143|2019_09_16_account_table_changes.sql|SHOW COLUMNS FROM `account` LIKE 'ls_id'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/bots/bots_db_update_manifest.txt b/utils/sql/git/bots/bots_db_update_manifest.txt index 405cf3f5c..0a1e4466a 100644 --- a/utils/sql/git/bots/bots_db_update_manifest.txt +++ b/utils/sql/git/bots/bots_db_update_manifest.txt @@ -24,6 +24,7 @@ 9023|2019_06_22_bots_owner_option_stats_update.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'stats_update'|empty| 9024|2019_06_27_bots_pet_get_lost.sql|SELECT `bot_command` FROM `bot_command_settings` WHERE `bot_command` LIKE 'petgetlost'|empty| 9025|2019_08_26_bots_owner_option_spawn_message.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'spawn_message_enabled'|empty| +9026|2019_09_09_bots_owner_options_rework.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'option_type'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/bots/required/2019_09_09_bots_owner_options_rework.sql b/utils/sql/git/bots/required/2019_09_09_bots_owner_options_rework.sql new file mode 100644 index 000000000..6240e9366 --- /dev/null +++ b/utils/sql/git/bots/required/2019_09_09_bots_owner_options_rework.sql @@ -0,0 +1,10 @@ +DROP TABLE IF EXISTS `bot_owner_options`; + +CREATE TABLE `bot_owner_options` ( + `owner_id` INT(11) UNSIGNED NOT NULL, + `option_type` SMALLINT(3) UNSIGNED NOT NULL, + `option_value` SMALLINT(3) UNSIGNED NULL DEFAULT '0', + PRIMARY KEY (`owner_id`, `option_type`) +) +COLLATE='latin1_swedish_ci' +ENGINE=InnoDB; diff --git a/utils/sql/git/optional/2017_12_25_LoginServer.sql b/utils/sql/git/optional/2017_12_25_LoginServer.sql new file mode 100644 index 000000000..3853e5c21 --- /dev/null +++ b/utils/sql/git/optional/2017_12_25_LoginServer.sql @@ -0,0 +1,21 @@ +ALTER TABLE `tblloginserveraccounts` + ADD COLUMN `AccountLoginserver` VARCHAR(64) NULL AFTER `LastIPAddress`; + +ALTER TABLE `tblloginserveraccounts` + CHANGE COLUMN `AccountLoginserver` `AccountLoginserver` VARCHAR(64) NOT NULL DEFAULT 'eqemu' AFTER `LoginServerID`, + DROP PRIMARY KEY, + ADD PRIMARY KEY (`LoginServerID`, `AccountLoginserver`); + +ALTER TABLE `tblloginserveraccounts` + ADD UNIQUE INDEX `AccountLoginserver_AccountName` (`AccountLoginserver`, `AccountName`); + +ALTER TABLE `tblloginserveraccounts` + ALTER `LoginServerID` DROP DEFAULT; +ALTER TABLE `tblloginserveraccounts` + CHANGE COLUMN `LoginServerID` `LoginServerID` INT(10) UNSIGNED NOT NULL FIRST; + +ALTER TABLE `tblloginserveraccounts` + ALTER `AccountPassword` DROP DEFAULT; +ALTER TABLE `tblloginserveraccounts` + CHANGE COLUMN `AccountPassword` `AccountPassword` TEXT NOT NULL AFTER `AccountName`; + \ No newline at end of file diff --git a/utils/sql/git/required/2019_09_02_required_spawn_filter.sql b/utils/sql/git/required/2019_09_02_required_spawn_filter.sql new file mode 100644 index 000000000..d89cdfa4a --- /dev/null +++ b/utils/sql/git/required/2019_09_02_required_spawn_filter.sql @@ -0,0 +1 @@ +ALTER TABLE `spawnentry` ADD COLUMN `condition_value_filter` MEDIUMINT(9) NOT NULL DEFAULT '1' AFTER `chance`; diff --git a/utils/sql/git/required/2019_09_16_account_table_changes.sql b/utils/sql/git/required/2019_09_16_account_table_changes.sql new file mode 100644 index 000000000..e4393d29d --- /dev/null +++ b/utils/sql/git/required/2019_09_16_account_table_changes.sql @@ -0,0 +1,11 @@ +ALTER TABLE `account` + DROP INDEX `name`, + DROP INDEX `lsaccount_id`; + +ALTER TABLE `account` + ADD COLUMN `ls_id` VARCHAR(64) NULL DEFAULT 'eqemu' AFTER `status`; + +ALTER TABLE `account` + ADD UNIQUE INDEX `name_ls_id` (`name`, `ls_id`), + ADD UNIQUE INDEX `ls_id_lsaccount_id` (`ls_id`, `lsaccount_id`); + \ No newline at end of file diff --git a/world/client.cpp b/world/client.cpp index c29b7d698..2d4b65e71 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -158,7 +158,7 @@ void Client::SendEnterWorld(std::string name) eqs->Close(); return; } else { - Log(Logs::Detail, Logs::World_Server,"Telling client to continue session."); + LogInfo("Telling client to continue session"); } } @@ -204,7 +204,7 @@ void Client::SendCharInfo() { QueuePacket(outapp); } else { - Log(Logs::General, Logs::World_Server, "[Error] Database did not return an OP_SendCharInfo packet for account %u", GetAccountID()); + LogInfo("[Error] Database did not return an OP_SendCharInfo packet for account [{}]", GetAccountID()); } safe_delete(outapp); } @@ -394,70 +394,40 @@ void Client::SendPostEnterWorld() { safe_delete(outapp); } -bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) { +bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) +{ if (app->size != sizeof(LoginInfo_Struct)) { return false; } - LoginInfo_Struct *li=(LoginInfo_Struct *)app->pBuffer; + auto *login_info = (LoginInfo_Struct *) app->pBuffer; // Quagmire - max len for name is 18, pass 15 - char name[19] = {0}; + char name[19] = {0}; char password[16] = {0}; - strn0cpy(name, (char*)li->login_info,18); - strn0cpy(password, (char*)&(li->login_info[strlen(name)+1]), 15); + strn0cpy(name, (char *) login_info->login_info, 18); + strn0cpy(password, (char *) &(login_info->login_info[strlen(name) + 1]), 15); + + LogDebug("Receiving Login Info Packet from Client | name [{0}] password [{1}]", name, password); if (strlen(password) <= 1) { - // TODO: Find out how to tell the client wrong username/password - Log(Logs::Detail, Logs::World_Server,"Login without a password"); + LogInfo("Login without a password"); return false; } - is_player_zoning=(li->zoning==1); + is_player_zoning = (login_info->zoning == 1); -#ifdef IPBASED_AUTH_HACK - struct in_addr tmpip; - tmpip.s_addr = ip; -#endif - uint32 id=0; - bool minilogin = loginserverlist.MiniLogin(); - if(minilogin){ - struct in_addr miniip; - miniip.s_addr = ip; - id = database.GetMiniLoginAccount(inet_ntoa(miniip)); - } - else if(strncasecmp(name, "LS#", 3) == 0) - id=atoi(&name[3]); - else if(database.GetAccountIDByName(name)){ - int16 status = 0; - uint32 lsid = 0; - id = database.GetAccountIDByName(name, &status, &lsid); - } - else - id=atoi(name); - if (loginserverlist.Connected() == false && !is_player_zoning) { - Log(Logs::General, Logs::World_Server,"Error: Login server login while not connected to login server."); + uint32 id = std::stoi(name); + if (id == 0) { + LogWarning("Receiving Login Info Packet from Client | account_id is 0 - disconnecting"); return false; } - if (((cle = client_list.CheckAuth(name, password)) || (cle = client_list.CheckAuth(id, password)))) - { - if (cle->AccountID() == 0 || (!minilogin && cle->LSID()==0)) { - Log(Logs::General, Logs::World_Server,"ID is 0. Is this server connected to minilogin?"); - if (!minilogin) { - Log(Logs::General, Logs::World_Server, "If so you forget the minilogin variable..."); - } - else { - Log(Logs::General, Logs::World_Server, "Could not find a minilogin account, verify ip address logging into minilogin is the same that is in your account table."); - } - return false; - } - if(minilogin){ - cle->SetOnline(); - WorldConfig::DisableStats(); - Log(Logs::General, Logs::World_Server, "MiniLogin Account #%d",cle->AccountID()); - } - else if (!is_player_zoning) { + LogClientLogin("[HandleSendLoginInfoPacket] Checking Auth id [{}]", id); + + if ((cle = client_list.CheckAuth(id, password))) { + LogClientLogin("[HandleSendLoginInfoPacket] Checking Auth id [{}] passed", id); + if (!is_player_zoning) { // Track who is in and who is out of the game char *inout= (char *) ""; @@ -469,24 +439,22 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) { // Game -> Char Select inout=(char *) "Out"; } - + // Always at Char select at this point. // Either from a fresh client launch or coming back from the game. // Exiting the game entirely does not come through here. // Could use a Logging Out Completely message somewhere. cle->SetOnline(CLE_Status::CharSelect); - Log(Logs::General, Logs::World_Server, - "Account (%s) Logging(%s) to character select :: LSID: %d ", - cle->AccountName(), inout, cle->LSID()); + LogInfo("Account ({}) Logging({}) to character select :: LSID [{}] ", cle->AccountName(), inout, cle->LSID()); } else { cle->SetOnline(); } - + const WorldConfig *Config=WorldConfig::get(); - - if(Config->UpdateStats){ + + if(Config->UpdateStats) { auto pack = new ServerPacket; pack->opcode = ServerOP_LSPlayerJoinWorld; pack->size = sizeof(ServerLSPlayerJoinWorld_Struct); @@ -498,10 +466,10 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) { loginserverlist.SendPacket(pack); safe_delete(pack); } - + if (!is_player_zoning) SendGuildList(); - + SendLogServer(); SendApproveWorld(); SendEnterWorld(cle->name()); @@ -512,24 +480,19 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) { database.LoginIP(cle->AccountID(), long2ip(GetIP()).c_str()); } + cle->SetIP(GetIP()); + return true; } else { - // TODO: Find out how to tell the client wrong username/password - Log(Logs::Detail, Logs::World_Server,"Bad/Expired session key '%s'",name); + LogInfo("Bad/Expired session key [{}]", name); return false; } - - if (!cle) - return true; - - cle->SetIP(GetIP()); - return true; } bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { if (GetAccountID() == 0) { - Log(Logs::Detail, Logs::World_Server,"Name approval request with no logged in account"); + LogInfo("Name approval request with no logged in account"); return false; } @@ -537,7 +500,7 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) uchar race = app->pBuffer[64]; uchar clas = app->pBuffer[68]; - Log(Logs::Detail, Logs::World_Server, "Name approval request. Name=%s, race=%s, class=%s", char_name, GetRaceIDName(race), GetClassIDName(clas)); + LogInfo("Name approval request. Name=[{}], race=[{}], class=[{}]", char_name, GetRaceIDName(race), GetClassIDName(clas)); EQApplicationPacket *outapp; outapp = new EQApplicationPacket; @@ -698,11 +661,11 @@ bool Client::HandleCharacterCreateRequestPacket(const EQApplicationPacket *app) bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) { if (GetAccountID() == 0) { - Log(Logs::Detail, Logs::World_Server,"Account ID not set; unable to create character."); + LogInfo("Account ID not set; unable to create character"); return false; } else if (app->size != sizeof(CharCreate_Struct)) { - Log(Logs::Detail, Logs::World_Server,"Wrong size on OP_CharacterCreate. Got: %d, Expected: %d",app->size,sizeof(CharCreate_Struct)); + LogInfo("Wrong size on OP_CharacterCreate. Got: [{}], Expected: [{}]", app->size, sizeof(CharCreate_Struct)); DumpPacket(app); // the previous behavior was essentially returning true here // but that seems a bit odd to me. @@ -729,14 +692,14 @@ bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) { bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { if (GetAccountID() == 0) { - Log(Logs::Detail, Logs::World_Server,"Enter world with no logged in account"); + LogInfo("Enter world with no logged in account"); eqs->Close(); return true; } if(GetAdmin() < 0) { - Log(Logs::Detail, Logs::World_Server,"Account banned or suspended."); + LogInfo("Account banned or suspended"); eqs->Close(); return true; } @@ -752,14 +715,14 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { uint32 tmpaccid = 0; charid = database.GetCharacterInfo(char_name, &tmpaccid, &zone_id, &instance_id); if (charid == 0 || tmpaccid != GetAccountID()) { - Log(Logs::Detail, Logs::World_Server,"Could not get CharInfo for '%s'",char_name); + LogInfo("Could not get CharInfo for [{}]", char_name); eqs->Close(); return true; } // Make sure this account owns this character if (tmpaccid != GetAccountID()) { - Log(Logs::Detail, Logs::World_Server,"This account does not own the character named '%s'",char_name); + LogInfo("This account does not own the character named [{}]", char_name); eqs->Close(); return true; } @@ -801,7 +764,7 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { zone_id = database.MoveCharacterToBind(charid, 4); } else { - Log(Logs::Detail, Logs::World_Server, "'%s' is trying to go home before they're able...", char_name); + LogInfo("[{}] is trying to go home before they're able", char_name); database.SetHackerFlag(GetAccountName(), char_name, "MQGoHome: player tried to go home before they were able."); eqs->Close(); return true; @@ -825,7 +788,7 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { database.MoveCharacterToZone(charid, database.GetZoneName(zone_id)); } else { - Log(Logs::Detail, Logs::World_Server, "'%s' is trying to go to tutorial but are not allowed...", char_name); + LogInfo("[{}] is trying to go to tutorial but are not allowed", char_name); database.SetHackerFlag(GetAccountName(), char_name, "MQTutorial: player tried to enter the tutorial without having tutorial enabled for this character."); eqs->Close(); return true; @@ -836,7 +799,7 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { if (zone_id == 0 || !database.GetZoneName(zone_id)) { // This is to save people in an invalid zone, once it's removed from the DB database.MoveCharacterToZone(charid, "arena"); - Log(Logs::Detail, Logs::World_Server, "Zone not found in database zone_id=%i, moveing char to arena character:%s", zone_id, char_name); + LogInfo("Zone not found in database zone_id=[{}], moveing char to arena character:[{}]", zone_id, char_name); } if(instance_id > 0) @@ -981,7 +944,7 @@ bool Client::HandleDeleteCharacterPacket(const EQApplicationPacket *app) { uint32 char_acct_id = database.GetAccountIDByChar((char*)app->pBuffer); if(char_acct_id == GetAccountID()) { - Log(Logs::Detail, Logs::World_Server,"Delete character: %s",app->pBuffer); + LogInfo("Delete character: [{}]", app->pBuffer); database.DeleteCharacter((char *)app->pBuffer); SendCharInfo(); } @@ -1002,24 +965,24 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { EmuOpcode opcode = app->GetOpcode(); - Log(Logs::Detail, Logs::World_Server,"Recevied EQApplicationPacket"); + LogNetcode("Received EQApplicationPacket [{:#04x}]", opcode); if (!eqs->CheckState(ESTABLISHED)) { - Log(Logs::Detail, Logs::World_Server,"Client disconnected (net inactive on send)"); + LogInfo("Client disconnected (net inactive on send)"); return false; } // Voidd: Anti-GM Account hack, Checks source ip against valid GM Account IP Addresses if (RuleB(World, GMAccountIPList) && this->GetAdmin() >= (RuleI(World, MinGMAntiHackStatus))) { if(!database.CheckGMIPs(long2ip(this->GetIP()).c_str(), this->GetAccountID())) { - Log(Logs::Detail, Logs::World_Server,"GM Account not permited from source address %s and accountid %i", long2ip(this->GetIP()).c_str(), this->GetAccountID()); + LogInfo("GM Account not permited from source address [{}] and accountid [{}]", long2ip(this->GetIP()).c_str(), this->GetAccountID()); eqs->Close(); } } if (GetAccountID() == 0 && opcode != OP_SendLoginInfo) { // Got a packet other than OP_SendLoginInfo when not logged in - Log(Logs::Detail, Logs::World_Server,"Expecting OP_SendLoginInfo, got %s", OpcodeNames[opcode]); + LogInfo("Expecting OP_SendLoginInfo, got [{}]", OpcodeNames[opcode]); return false; } else if (opcode == OP_AckPacket) { @@ -1098,7 +1061,7 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { } default: { - Log(Logs::Detail, Logs::World_Server,"Received unknown EQApplicationPacket"); + LogNetcode("Received unknown EQApplicationPacket"); return true; } } @@ -1116,7 +1079,7 @@ bool Client::Process() { to.sin_addr.s_addr = ip; if (autobootup_timeout.Check()) { - Log(Logs::General, Logs::World_Server, "Zone bootup timer expired, bootup failed or too slow."); + LogInfo("Zone bootup timer expired, bootup failed or too slow"); TellClientZoneUnavailable(); } @@ -1150,7 +1113,7 @@ bool Client::Process() { loginserverlist.SendPacket(pack); safe_delete(pack); } - Log(Logs::Detail, Logs::World_Server,"Client disconnected (not active in process)"); + LogInfo("Client disconnected (not active in process)"); return false; } @@ -1206,17 +1169,17 @@ void Client::EnterWorld(bool TryBootup) { } else { if (TryBootup) { - Log(Logs::General, Logs::World_Server, "Attempting autobootup of %s (%d:%d)", zone_name, zone_id, instance_id); + LogInfo("Attempting autobootup of [{}] ([{}]:[{}])", zone_name, zone_id, instance_id); autobootup_timeout.Start(); zone_waiting_for_bootup = zoneserver_list.TriggerBootup(zone_id, instance_id); if (zone_waiting_for_bootup == 0) { - Log(Logs::General, Logs::World_Server, "No zoneserver available to boot up."); + LogInfo("No zoneserver available to boot up"); TellClientZoneUnavailable(); } return; } else { - Log(Logs::General, Logs::World_Server, "Requested zone %s is not running.", zone_name); + LogInfo("Requested zone [{}] is not running", zone_name); TellClientZoneUnavailable(); return; } @@ -1225,7 +1188,7 @@ void Client::EnterWorld(bool TryBootup) { zone_waiting_for_bootup = 0; if (GetAdmin() < 80 && zoneserver_list.IsZoneLocked(zone_id)) { - Log(Logs::General, Logs::World_Server, "Enter world failed. Zone is locked."); + LogInfo("Enter world failed. Zone is locked"); TellClientZoneUnavailable(); return; } @@ -1238,8 +1201,8 @@ void Client::EnterWorld(bool TryBootup) { cle->SetChar(charid, char_name); database.UpdateLiveChar(char_name, GetAccountID()); - Log(Logs::General, Logs::World_Server, - "(%s) %s %s (Zone ID %d: Instance ID: %d) ", + LogInfo( + "({}) [{}] [{}] (Zone ID [{}]: Instance ID: [{}]) ", char_name, (seen_character_select ? "Zoning from character select" : "Zoning to"), zone_name, @@ -1281,9 +1244,9 @@ void Client::Clearance(int8 response) { if (zs == 0) { - Log(Logs::Detail, Logs::World_Server,"Unable to find zoneserver in Client::Clearance!!"); + LogInfo("Unable to find zoneserver in Client::Clearance!!"); } else { - Log(Logs::Detail, Logs::World_Server, "Invalid response %d in Client::Clearance", response); + LogInfo("Invalid response [{}] in Client::Clearance", response); } TellClientZoneUnavailable(); @@ -1293,20 +1256,20 @@ void Client::Clearance(int8 response) EQApplicationPacket* outapp; if (zs->GetCAddress() == nullptr) { - Log(Logs::Detail, Logs::World_Server, "Unable to do zs->GetCAddress() in Client::Clearance!!"); + LogInfo("Unable to do zs->GetCAddress() in Client::Clearance!!"); TellClientZoneUnavailable(); return; } if (zone_id == 0) { - Log(Logs::Detail, Logs::World_Server, "zoneID is nullptr in Client::Clearance!!"); + LogInfo("zoneID is nullptr in Client::Clearance!!"); TellClientZoneUnavailable(); return; } const char* zonename = database.GetZoneName(zone_id); if (zonename == 0) { - Log(Logs::Detail, Logs::World_Server, "zonename is nullptr in Client::Clearance!!"); + LogInfo("zonename is nullptr in Client::Clearance!!"); TellClientZoneUnavailable(); return; } @@ -1330,10 +1293,10 @@ void Client::Clearance(int8 response) if(strcmp(zs_addr, "127.0.0.1") == 0) { - Log(Logs::Detail, Logs::World_Server, "Local zone address was %s, setting local address to: %s", zs_addr, WorldConfig::get()->LocalAddress.c_str()); + LogInfo("Local zone address was [{}], setting local address to: [{}]", zs_addr, WorldConfig::get()->LocalAddress.c_str()); zs_addr = WorldConfig::get()->LocalAddress.c_str(); } else { - Log(Logs::Detail, Logs::World_Server, "Local zone address %s", zs_addr); + LogInfo("Local zone address [{}]", zs_addr); } } @@ -1348,7 +1311,7 @@ void Client::Clearance(int8 response) strcpy(zsi->ip, zs_addr); zsi->port =zs->GetCPort(); - Log(Logs::Detail, Logs::World_Server,"Sending client to zone %s (%d:%d) at %s:%d",zonename,zone_id,instance_id,zsi->ip,zsi->port); + LogInfo("Sending client to zone [{}] ([{}]:[{}]) at [{}]:[{}]", zonename, zone_id, instance_id, zsi->ip, zsi->port); QueuePacket(outapp); safe_delete(outapp); @@ -1380,7 +1343,7 @@ bool Client::GenPassKey(char* key) { } void Client::QueuePacket(const EQApplicationPacket* app, bool ack_req) { - Log(Logs::Detail, Logs::World_Server, "Sending EQApplicationPacket OpCode 0x%04x",app->GetOpcode()); + LogNetcode("Sending EQApplicationPacket OpCode {:#04x}", app->GetOpcode()); ack_req = true; // It's broke right now, dont delete this line till fix it. =P eqs->QueuePacket(app, ack_req); @@ -1468,27 +1431,27 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) in.s_addr = GetIP(); - Log(Logs::Detail, Logs::World_Server, "Character creation request from %s LS#%d (%s:%d) : ", GetCLE()->LSName(), GetCLE()->LSID(), inet_ntoa(in), GetPort()); - Log(Logs::Detail, Logs::World_Server, "Name: %s", name); - Log(Logs::Detail, Logs::World_Server, "Race: %d Class: %d Gender: %d Deity: %d Start zone: %d Tutorial: %s", + LogInfo("Character creation request from [{}] LS#[{}] ([{}]:[{}]) : ", GetCLE()->LSName(), GetCLE()->LSID(), inet_ntoa(in), GetPort()); + LogInfo("Name: [{}]", name); + Log(Logs::Detail, Logs::WorldServer, "Race: %d Class: %d Gender: %d Deity: %d Start zone: %d Tutorial: %s", cc->race, cc->class_, cc->gender, cc->deity, cc->start_zone, cc->tutorial ? "true" : "false"); - Log(Logs::Detail, Logs::World_Server, "STR STA AGI DEX WIS INT CHA Total"); - Log(Logs::Detail, Logs::World_Server, "%3d %3d %3d %3d %3d %3d %3d %3d", + LogInfo("STR STA AGI DEX WIS INT CHA Total"); + Log(Logs::Detail, Logs::WorldServer, "%3d %3d %3d %3d %3d %3d %3d %3d", cc->STR, cc->STA, cc->AGI, cc->DEX, cc->WIS, cc->INT, cc->CHA, stats_sum); - Log(Logs::Detail, Logs::World_Server, "Face: %d Eye colors: %d %d", cc->face, cc->eyecolor1, cc->eyecolor2); - Log(Logs::Detail, Logs::World_Server, "Hairstyle: %d Haircolor: %d", cc->hairstyle, cc->haircolor); - Log(Logs::Detail, Logs::World_Server, "Beard: %d Beardcolor: %d", cc->beard, cc->beardcolor); + LogInfo("Face: [{}] Eye colors: [{}] [{}]", cc->face, cc->eyecolor1, cc->eyecolor2); + LogInfo("Hairstyle: [{}] Haircolor: [{}]", cc->hairstyle, cc->haircolor); + LogInfo("Beard: [{}] Beardcolor: [{}]", cc->beard, cc->beardcolor); /* Validate the char creation struct */ if (m_ClientVersionBit & EQEmu::versions::maskSoFAndLater) { if (!CheckCharCreateInfoSoF(cc)) { - Log(Logs::Detail, Logs::World_Server,"CheckCharCreateInfo did not validate the request (bad race/class/stats)"); + LogInfo("CheckCharCreateInfo did not validate the request (bad race/class/stats)"); return false; } } else { if (!CheckCharCreateInfoTitanium(cc)) { - Log(Logs::Detail, Logs::World_Server,"CheckCharCreateInfo did not validate the request (bad race/class/stats)"); + LogInfo("CheckCharCreateInfo did not validate the request (bad race/class/stats)"); return false; } } @@ -1550,14 +1513,14 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) /* If it is an SoF Client and the SoF Start Zone rule is set, send new chars there */ if (m_ClientVersionBit & EQEmu::versions::maskSoFAndLater) { - Log(Logs::Detail, Logs::World_Server,"Found 'SoFStartZoneID' rule setting: %i", RuleI(World, SoFStartZoneID)); + LogInfo("Found 'SoFStartZoneID' rule setting: [{}]", RuleI(World, SoFStartZoneID)); if (RuleI(World, SoFStartZoneID) > 0) { pp.zone_id = RuleI(World, SoFStartZoneID); cc->start_zone = pp.zone_id; } } else { - Log(Logs::General, Logs::World_Server, "Found 'TitaniumStartZoneID' rule setting: %i", RuleI(World, TitaniumStartZoneID)); + LogInfo("Found 'TitaniumStartZoneID' rule setting: [{}]", RuleI(World, TitaniumStartZoneID)); if (RuleI(World, TitaniumStartZoneID) > 0) { /* if there's a startzone variable put them in there */ pp.zone_id = RuleI(World, TitaniumStartZoneID); @@ -1617,12 +1580,12 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) pp.binds[0].heading = pp.heading; } - Log(Logs::Detail, Logs::World_Server,"Current location: %s (%d) %0.2f, %0.2f, %0.2f, %0.2f", + Log(Logs::Detail, Logs::WorldServer, "Current location: %s (%d) %0.2f, %0.2f, %0.2f, %0.2f", database.GetZoneName(pp.zone_id), pp.zone_id, pp.x, pp.y, pp.z, pp.heading); - Log(Logs::Detail, Logs::World_Server,"Bind location: %s (%d) %0.2f, %0.2f, %0.2f", - database.GetZoneName(pp.binds[0].zoneId), pp.binds[0].zoneId, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z); - Log(Logs::Detail, Logs::World_Server,"Home location: %s (%d) %0.2f, %0.2f, %0.2f", - database.GetZoneName(pp.binds[4].zoneId), pp.binds[4].zoneId, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z); + Log(Logs::Detail, Logs::WorldServer, "Bind location: %s (%d) %0.2f, %0.2f, %0.2f", + database.GetZoneName(pp.binds[0].zoneId), pp.binds[0].zoneId, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z); + Log(Logs::Detail, Logs::WorldServer, "Home location: %s (%d) %0.2f, %0.2f, %0.2f", + database.GetZoneName(pp.binds[4].zoneId), pp.binds[4].zoneId, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z); /* Starting Items inventory */ database.SetStartingItems(&pp, &inv, pp.race, pp.class_, pp.deity, pp.zone_id, pp.name, GetAdmin()); @@ -1630,10 +1593,10 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) // now we give the pp and the inv we made to StoreCharacter // to see if we can store it if (!database.StoreCharacter(GetAccountID(), &pp, &inv)) { - Log(Logs::Detail, Logs::World_Server,"Character creation failed: %s", pp.name); + LogInfo("Character creation failed: [{}]", pp.name); return false; } - Log(Logs::Detail, Logs::World_Server,"Character creation successful: %s", pp.name); + LogInfo("Character creation successful: [{}]", pp.name); return true; } @@ -1643,7 +1606,7 @@ bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) if (!cc) return false; - Log(Logs::Detail, Logs::World_Server, "Validating char creation info..."); + LogInfo("Validating char creation info"); RaceClassCombos class_combo; bool found = false; @@ -1660,7 +1623,7 @@ bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) } if (!found) { - Log(Logs::Detail, Logs::World_Server, "Could not find class/race/deity/start_zone combination"); + LogInfo("Could not find class/race/deity/start_zone combination"); return false; } @@ -1677,7 +1640,7 @@ bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) } if (!found) { - Log(Logs::Detail, Logs::World_Server, "Could not find starting stats for selected character combo, cannot verify stats"); + LogInfo("Could not find starting stats for selected character combo, cannot verify stats"); return false; } @@ -1690,37 +1653,37 @@ bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) allocation.DefaultPointAllocation[6]; if (cc->STR > allocation.BaseStats[0] + max_stats || cc->STR < allocation.BaseStats[0]) { - Log(Logs::Detail, Logs::World_Server, "Strength out of range"); + LogInfo("Strength out of range"); return false; } if (cc->DEX > allocation.BaseStats[1] + max_stats || cc->DEX < allocation.BaseStats[1]) { - Log(Logs::Detail, Logs::World_Server, "Dexterity out of range"); + LogInfo("Dexterity out of range"); return false; } if (cc->AGI > allocation.BaseStats[2] + max_stats || cc->AGI < allocation.BaseStats[2]) { - Log(Logs::Detail, Logs::World_Server, "Agility out of range"); + LogInfo("Agility out of range"); return false; } if (cc->STA > allocation.BaseStats[3] + max_stats || cc->STA < allocation.BaseStats[3]) { - Log(Logs::Detail, Logs::World_Server, "Stamina out of range"); + LogInfo("Stamina out of range"); return false; } if (cc->INT > allocation.BaseStats[4] + max_stats || cc->INT < allocation.BaseStats[4]) { - Log(Logs::Detail, Logs::World_Server, "Intelligence out of range"); + LogInfo("Intelligence out of range"); return false; } if (cc->WIS > allocation.BaseStats[5] + max_stats || cc->WIS < allocation.BaseStats[5]) { - Log(Logs::Detail, Logs::World_Server, "Wisdom out of range"); + LogInfo("Wisdom out of range"); return false; } if (cc->CHA > allocation.BaseStats[6] + max_stats || cc->CHA < allocation.BaseStats[6]) { - Log(Logs::Detail, Logs::World_Server, "Charisma out of range"); + LogInfo("Charisma out of range"); return false; } @@ -1733,7 +1696,7 @@ bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) current_stats += cc->WIS - allocation.BaseStats[5]; current_stats += cc->CHA - allocation.BaseStats[6]; if (current_stats > max_stats) { - Log(Logs::Detail, Logs::World_Server, "Current Stats > Maximum Stats"); + LogInfo("Current Stats > Maximum Stats"); return false; } @@ -1814,7 +1777,7 @@ bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc) if (!cc) return false; - Log(Logs::Detail, Logs::World_Server,"Validating char creation info..."); + LogInfo("Validating char creation info"); classtemp = cc->class_ - 1; racetemp = cc->race - 1; @@ -1827,16 +1790,16 @@ bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc) // if out of range looking it up in the table would crash stuff // so we return from these if (classtemp >= PLAYER_CLASS_COUNT) { - Log(Logs::Detail, Logs::World_Server," class is out of range"); + LogInfo(" class is out of range"); return false; } if (racetemp >= _TABLE_RACES) { - Log(Logs::Detail, Logs::World_Server," race is out of range"); + LogInfo(" race is out of range"); return false; } if (!ClassRaceLookupTable[classtemp][racetemp]) { //Lookup table better than a bunch of ifs? - Log(Logs::Detail, Logs::World_Server," invalid race/class combination"); + LogInfo(" invalid race/class combination"); // we return from this one, since if it's an invalid combination our table // doesn't have meaningful values for the stats return false; @@ -1864,43 +1827,43 @@ bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc) // that are messed up not just the first hit if (bTOTAL + stat_points != cTOTAL) { - Log(Logs::Detail, Logs::World_Server," stat points total doesn't match expected value: expecting %d got %d", bTOTAL + stat_points, cTOTAL); + LogInfo(" stat points total doesn't match expected value: expecting [{}] got [{}]", bTOTAL + stat_points, cTOTAL); Charerrors++; } if (cc->STR > bSTR + stat_points || cc->STR < bSTR) { - Log(Logs::Detail, Logs::World_Server," stat STR is out of range"); + LogInfo(" stat STR is out of range"); Charerrors++; } if (cc->STA > bSTA + stat_points || cc->STA < bSTA) { - Log(Logs::Detail, Logs::World_Server," stat STA is out of range"); + LogInfo(" stat STA is out of range"); Charerrors++; } if (cc->AGI > bAGI + stat_points || cc->AGI < bAGI) { - Log(Logs::Detail, Logs::World_Server," stat AGI is out of range"); + LogInfo(" stat AGI is out of range"); Charerrors++; } if (cc->DEX > bDEX + stat_points || cc->DEX < bDEX) { - Log(Logs::Detail, Logs::World_Server," stat DEX is out of range"); + LogInfo(" stat DEX is out of range"); Charerrors++; } if (cc->WIS > bWIS + stat_points || cc->WIS < bWIS) { - Log(Logs::Detail, Logs::World_Server," stat WIS is out of range"); + LogInfo(" stat WIS is out of range"); Charerrors++; } if (cc->INT > bINT + stat_points || cc->INT < bINT) { - Log(Logs::Detail, Logs::World_Server," stat INT is out of range"); + LogInfo(" stat INT is out of range"); Charerrors++; } if (cc->CHA > bCHA + stat_points || cc->CHA < bCHA) { - Log(Logs::Detail, Logs::World_Server," stat CHA is out of range"); + LogInfo(" stat CHA is out of range"); Charerrors++; } /*TODO: Check for deity/class/race.. it'd be nice, but probably of any real use to hack(faction, deity based items are all I can think of) I am NOT writing those tables - kathgar*/ - Log(Logs::Detail, Logs::World_Server,"Found %d errors in character creation request", Charerrors); + LogInfo("Found [{}] errors in character creation request", Charerrors); return Charerrors == 0; } diff --git a/world/cliententry.cpp b/world/cliententry.cpp index d9ae26730..9aa2b9ae0 100644 --- a/world/cliententry.cpp +++ b/world/cliententry.cpp @@ -26,61 +26,71 @@ #include "../common/guilds.h" #include "../common/string_util.h" -extern uint32 numplayers; +extern uint32 numplayers; extern LoginServerList loginserverlist; -extern ClientList client_list; -extern volatile bool RunLoops; +extern ClientList client_list; +extern volatile bool RunLoops; -ClientListEntry::ClientListEntry(uint32 in_id, uint32 iLSID, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin, uint32 ip, uint8 local) -: id(in_id) +/** + * @param in_id + * @param in_loginserver_id + * @param in_loginserver_name + * @param in_login_name + * @param in_login_key + * @param in_is_world_admin + * @param ip + * @param local + */ +ClientListEntry::ClientListEntry( + uint32 in_id, + uint32 in_loginserver_id, + const char *in_loginserver_name, + const char *in_login_name, + const char *in_login_key, + int16 in_is_world_admin, + uint32 ip, + uint8 local +) + : id(in_id) { ClearVars(true); - pIP = ip; - pLSID = iLSID; - if(iLSID > 0) - paccountid = database.GetAccountIDFromLSID(iLSID, paccountname, &padmin); - strn0cpy(plsname, iLoginName, sizeof(plsname)); - strn0cpy(plskey, iLoginKey, sizeof(plskey)); - pworldadmin = iWorldAdmin; - plocal=(local==1); + LogDebug( + "ClientListEntry in_id [{0}] in_loginserver_id [{1}] in_loginserver_name [{2}] in_login_name [{3}] in_login_key [{4}] " + " in_is_world_admin [{5}] ip [{6}] local [{7}]", + in_id, + in_loginserver_id, + in_loginserver_name, + in_login_name, + in_login_key, + in_is_world_admin, + ip, + local + ); + + pIP = ip; + pLSID = in_loginserver_id; + if (in_loginserver_id > 0) { + paccountid = database.GetAccountIDFromLSID(in_loginserver_name, in_loginserver_id, paccountname, &padmin); + } + + strn0cpy(loginserver_account_name, in_login_name, sizeof(loginserver_account_name)); + strn0cpy(plskey, in_login_key, sizeof(plskey)); + strn0cpy(source_loginserver, in_loginserver_name, sizeof(source_loginserver)); + pworldadmin = in_is_world_admin; + plocal = (local == 1); - pinstance = 0; - pLFGFromLevel = 0; - pLFGToLevel = 0; - pLFGMatchFilter = false; memset(pLFGComments, 0, 64); } -ClientListEntry::ClientListEntry(uint32 in_id, uint32 iAccID, const char* iAccName, MD5& iMD5Pass, int16 iAdmin) -: id(in_id) +ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer *iZS, ServerClientList_Struct *scl, CLE_Status iOnline) + : id(in_id) { ClearVars(true); - pIP = 0; - pLSID = 0; - pworldadmin = 0; - - paccountid = iAccID; - strn0cpy(paccountname, iAccName, sizeof(paccountname)); - pMD5Pass = iMD5Pass; - padmin = iAdmin; - - pinstance = 0; - pLFGFromLevel = 0; - pLFGToLevel = 0; - pLFGMatchFilter = false; - memset(pLFGComments, 0, 64); -} - -ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer* iZS, ServerClientList_Struct* scl, CLE_Status iOnline) -: id(in_id) -{ - ClearVars(true); - - pIP = 0; + pIP = 0; pLSID = scl->LSAccountID; - strn0cpy(plsname, scl->name, sizeof(plsname)); + strn0cpy(loginserver_account_name, scl->name, sizeof(loginserver_account_name)); strn0cpy(plskey, scl->lskey, sizeof(plskey)); pworldadmin = 0; @@ -88,19 +98,22 @@ ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer* iZS, ServerClientList strn0cpy(paccountname, scl->AccountName, sizeof(paccountname)); padmin = scl->Admin; - pinstance = 0; - pLFGFromLevel = 0; - pLFGToLevel = 0; + pinstance = 0; + pLFGFromLevel = 0; + pLFGToLevel = 0; pLFGMatchFilter = false; memset(pLFGComments, 0, 64); - if (iOnline >= CLE_Status::Zoning) + if (iOnline >= CLE_Status::Zoning) { Update(iZS, scl, iOnline); - else + } + else { SetOnline(iOnline); + } } -ClientListEntry::~ClientListEntry() { +ClientListEntry::~ClientListEntry() +{ if (RunLoops) { Camp(); // updates zoneserver's numplayers client_list.RemoveCLEReferances(this); @@ -110,99 +123,118 @@ ClientListEntry::~ClientListEntry() { tell_queue.clear(); } -void ClientListEntry::SetChar(uint32 iCharID, const char* iCharName) { +void ClientListEntry::SetChar(uint32 iCharID, const char *iCharName) +{ pcharid = iCharID; strn0cpy(pname, iCharName, sizeof(pname)); } -void ClientListEntry::SetOnline(ZoneServer* iZS, CLE_Status iOnline) { - if (iZS == this->Server()) +void ClientListEntry::SetOnline(ZoneServer *iZS, CLE_Status iOnline) +{ + if (iZS == this->Server()) { SetOnline(iOnline); + } } -void ClientListEntry::SetOnline(CLE_Status iOnline) { - Log(Logs::General, Logs::World_Server, "ClientListEntry::SetOnline for %s(%i) = %i", AccountName(), AccountID(), iOnline); +void ClientListEntry::SetOnline(CLE_Status iOnline) +{ + LogClientLogin( + "ClientListEntry::SetOnline for [{}] ({}) = [{}] ({})", + AccountName(), + AccountID(), + CLEStatusString[CLE_Status::Online], + iOnline + ); - if (iOnline >= CLE_Status::Online && pOnline < CLE_Status::Online) + if (iOnline >= CLE_Status::Online && pOnline < CLE_Status::Online) { numplayers++; + } else if (iOnline < CLE_Status::Online && pOnline >= CLE_Status::Online) { numplayers--; } - if (iOnline != CLE_Status::Online || pOnline < CLE_Status::Online) + if (iOnline != CLE_Status::Online || pOnline < CLE_Status::Online) { pOnline = iOnline; - if (iOnline < CLE_Status::Zoning) + } + if (iOnline < CLE_Status::Zoning) { Camp(); - if (pOnline >= CLE_Status::Online) + } + if (pOnline >= CLE_Status::Online) { stale = 0; + } } -void ClientListEntry::LSUpdate(ZoneServer* iZS){ - if(WorldConfig::get()->UpdateStats){ + +void ClientListEntry::LSUpdate(ZoneServer *iZS) +{ + if (WorldConfig::get()->UpdateStats) { auto pack = new ServerPacket; - pack->opcode = ServerOP_LSZoneInfo; - pack->size = sizeof(ZoneInfo_Struct); + pack->opcode = ServerOP_LSZoneInfo; + pack->size = sizeof(ZoneInfo_Struct); pack->pBuffer = new uchar[pack->size]; - ZoneInfo_Struct* zone =(ZoneInfo_Struct*)pack->pBuffer; - zone->count=iZS->NumPlayers(); - zone->zone = iZS->GetZoneID(); + ZoneInfo_Struct *zone = (ZoneInfo_Struct *) pack->pBuffer; + zone->count = iZS->NumPlayers(); + zone->zone = iZS->GetZoneID(); zone->zone_wid = iZS->GetID(); loginserverlist.SendPacket(pack); safe_delete(pack); } } -void ClientListEntry::LSZoneChange(ZoneToZone_Struct* ztz){ - if(WorldConfig::get()->UpdateStats){ +void ClientListEntry::LSZoneChange(ZoneToZone_Struct *ztz) +{ + if (WorldConfig::get()->UpdateStats) { auto pack = new ServerPacket; - pack->opcode = ServerOP_LSPlayerZoneChange; - pack->size = sizeof(ServerLSPlayerZoneChange_Struct); + pack->opcode = ServerOP_LSPlayerZoneChange; + pack->size = sizeof(ServerLSPlayerZoneChange_Struct); pack->pBuffer = new uchar[pack->size]; - ServerLSPlayerZoneChange_Struct* zonechange =(ServerLSPlayerZoneChange_Struct*)pack->pBuffer; + ServerLSPlayerZoneChange_Struct *zonechange = (ServerLSPlayerZoneChange_Struct *) pack->pBuffer; zonechange->lsaccount_id = LSID(); - zonechange->from = ztz->current_zone_id; - zonechange->to = ztz->requested_zone_id; + zonechange->from = ztz->current_zone_id; + zonechange->to = ztz->requested_zone_id; loginserverlist.SendPacket(pack); safe_delete(pack); } } -void ClientListEntry::Update(ZoneServer* iZS, ServerClientList_Struct* scl, CLE_Status iOnline) { + +void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, CLE_Status iOnline) +{ if (pzoneserver != iZS) { - if (pzoneserver){ + if (pzoneserver) { pzoneserver->RemovePlayer(); LSUpdate(pzoneserver); } - if (iZS){ + if (iZS) { iZS->AddPlayer(); LSUpdate(iZS); } } - pzoneserver = iZS; - pzone = scl->zone; - pinstance = scl->instance_id; - pcharid = scl->charid; + pzoneserver = iZS; + pzone = scl->zone; + pinstance = scl->instance_id; + pcharid = scl->charid; strcpy(pname, scl->name); if (paccountid == 0) { paccountid = scl->AccountID; strcpy(paccountname, scl->AccountName); - strcpy(plsname, scl->AccountName); - pIP = scl->IP; + strcpy(loginserver_account_name, scl->AccountName); + pIP = scl->IP; pLSID = scl->LSAccountID; strn0cpy(plskey, scl->lskey, sizeof(plskey)); } - padmin = scl->Admin; - plevel = scl->level; - pclass_ = scl->class_; - prace = scl->race; - panon = scl->anon; - ptellsoff = scl->tellsoff; - pguild_id = scl->guild_id; - pLFG = scl->LFG; - gm = scl->gm; + padmin = scl->Admin; + plevel = scl->level; + pclass_ = scl->class_; + prace = scl->race; + panon = scl->anon; + ptellsoff = scl->tellsoff; + pguild_id = scl->guild_id; + pLFG = scl->LFG; + gm = scl->gm; pClientVersion = scl->ClientVersion; // Fields from the LFG Window - if((scl->LFGFromLevel != 0) && (scl->LFGToLevel != 0)) { - pLFGFromLevel = scl->LFGFromLevel; - pLFGToLevel = scl->LFGToLevel; + if ((scl->LFGFromLevel != 0) && (scl->LFGToLevel != 0)) { + pLFGFromLevel = scl->LFGFromLevel; + pLFGToLevel = scl->LFGToLevel; pLFGMatchFilter = scl->LFGMatchFilter; memcpy(pLFGComments, scl->LFGComments, sizeof(pLFGComments)); } @@ -210,26 +242,29 @@ void ClientListEntry::Update(ZoneServer* iZS, ServerClientList_Struct* scl, CLE_ SetOnline(iOnline); } -void ClientListEntry::LeavingZone(ZoneServer* iZS, CLE_Status iOnline) { - if (iZS != 0 && iZS != pzoneserver) +void ClientListEntry::LeavingZone(ZoneServer *iZS, CLE_Status iOnline) +{ + if (iZS != 0 && iZS != pzoneserver) { return; + } SetOnline(iOnline); - if (pzoneserver){ + if (pzoneserver) { pzoneserver->RemovePlayer(); LSUpdate(pzoneserver); } pzoneserver = 0; - pzone = 0; + pzone = 0; } -void ClientListEntry::ClearVars(bool iAll) { +void ClientListEntry::ClearVars(bool iAll) +{ if (iAll) { pOnline = CLE_Status::Never; - stale = 0; + stale = 0; pLSID = 0; - memset(plsname, 0, sizeof(plsname)); + memset(loginserver_account_name, 0, sizeof(loginserver_account_name)); memset(plskey, 0, sizeof(plskey)); pworldadmin = 0; @@ -238,27 +273,29 @@ void ClientListEntry::ClearVars(bool iAll) { padmin = 0; } pzoneserver = 0; - pzone = 0; - pcharid = 0; + pzone = 0; + pcharid = 0; memset(pname, 0, sizeof(pname)); - plevel = 0; - pclass_ = 0; - prace = 0; - panon = 0; - ptellsoff = 0; - pguild_id = GUILD_NONE; - pLFG = 0; - gm = 0; + plevel = 0; + pclass_ = 0; + prace = 0; + panon = 0; + ptellsoff = 0; + pguild_id = GUILD_NONE; + pLFG = 0; + gm = 0; pClientVersion = 0; for (auto &elem : tell_queue) safe_delete_array(elem); tell_queue.clear(); } -void ClientListEntry::Camp(ZoneServer* iZS) { - if (iZS != 0 && iZS != pzoneserver) +void ClientListEntry::Camp(ZoneServer *iZS) +{ + if (iZS != 0 && iZS != pzoneserver) { return; - if (pzoneserver){ + } + if (pzoneserver) { pzoneserver->RemovePlayer(); LSUpdate(pzoneserver); } @@ -268,48 +305,66 @@ void ClientListEntry::Camp(ZoneServer* iZS) { stale = 0; } -bool ClientListEntry::CheckStale() { +bool ClientListEntry::CheckStale() +{ stale++; if (stale > 20) { - if (pOnline > CLE_Status::Offline) + if (pOnline > CLE_Status::Offline) { SetOnline(CLE_Status::Offline); + } return true; } return false; } -bool ClientListEntry::CheckAuth(uint32 iLSID, const char* iKey) { - if (strncmp(plskey, iKey,10) == 0) { - if (paccountid == 0 && LSID()>0) { - int16 tmpStatus = WorldConfig::get()->DefaultStatus; - paccountid = database.CreateAccount(plsname, 0, tmpStatus, LSID()); +bool ClientListEntry::CheckAuth(uint32 loginserver_account_id, const char *key_password) +{ + LogDebug( + "ClientListEntry::CheckAuth ls_account_id [{0}] key_password [{1}] plskey [{2}]", + loginserver_account_id, + key_password, + plskey + ); + if (pLSID == loginserver_account_id && strncmp(plskey, key_password, 10) == 0) { + + LogDebug( + "ClientListEntry::CheckAuth ls_account_id [{0}] key_password [{1}] plskey [{2}] lsid [{3}] paccountid [{4}]", + loginserver_account_id, + key_password, + plskey, + LSID(), + paccountid + ); + + if (paccountid == 0 && LSID() > 0) { + int16 default_account_status = WorldConfig::get()->DefaultStatus; + + paccountid = database.CreateAccount( + loginserver_account_name, + 0, + default_account_status, + source_loginserver, + LSID() + ); + if (!paccountid) { - Log(Logs::Detail, Logs::World_Server,"Error adding local account for LS login: '%s', duplicate name?" ,plsname); + LogInfo( + "Error adding local account for LS login: [{0}:{1}], duplicate name", + source_loginserver, + loginserver_account_name + ); return false; } - strn0cpy(paccountname, plsname, sizeof(paccountname)); - padmin = tmpStatus; + strn0cpy(paccountname, loginserver_account_name, sizeof(paccountname)); + padmin = default_account_status; } std::string lsworldadmin; - if (database.GetVariable("honorlsworldadmin", lsworldadmin)) - if (atoi(lsworldadmin.c_str()) == 1 && pworldadmin != 0 && (padmin < pworldadmin || padmin == 0)) + if (database.GetVariable("honorlsworldadmin", lsworldadmin)) { + if (atoi(lsworldadmin.c_str()) == 1 && pworldadmin != 0 && (padmin < pworldadmin || padmin == 0)) { padmin = pworldadmin; - return true; - } - return false; -} - -bool ClientListEntry::CheckAuth(const char* iName, MD5& iMD5Password) { - if (LSAccountID() == 0 && strcmp(paccountname, iName) == 0 && pMD5Pass == iMD5Password) - return true; - return false; -} - -bool ClientListEntry::CheckAuth(uint32 id, const char* iKey, uint32 ip) { - if (pIP==ip && strncmp(plskey, iKey,10) == 0){ - paccountid = id; - database.GetAccountFromID(id,paccountname,&padmin); + } + } return true; } return false; @@ -317,13 +372,17 @@ bool ClientListEntry::CheckAuth(uint32 id, const char* iKey, uint32 ip) { void ClientListEntry::ProcessTellQueue() { - if (!Server()) + if (!Server()) { return; + } ServerPacket *pack; - auto it = tell_queue.begin(); + auto it = tell_queue.begin(); while (it != tell_queue.end()) { - pack = new ServerPacket(ServerOP_ChannelMessage, sizeof(ServerChannelMessage_Struct) + strlen((*it)->message) + 1); + pack = new ServerPacket( + ServerOP_ChannelMessage, + sizeof(ServerChannelMessage_Struct) + strlen((*it)->message) + 1 + ); memcpy(pack->pBuffer, *it, pack->size); Server()->SendPacket(pack); safe_delete(pack); diff --git a/world/cliententry.h b/world/cliententry.h index 725b7df0d..b4d449c58 100644 --- a/world/cliententry.h +++ b/world/cliententry.h @@ -18,12 +18,48 @@ typedef enum InZone } CLE_Status; +static const char * CLEStatusString[] = { + "Never", + "Offline", + "Online", + "CharSelect", + "Zoning", + "InZone" +}; + class ZoneServer; struct ServerClientList_Struct; class ClientListEntry { public: - ClientListEntry(uint32 id, uint32 iLSID, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = 0, uint32 ip = 0, uint8 local=0); + + /** + * @param id + * @param in_loginserver_id + * @param in_loginserver_name + * @param in_login_name + * @param in_login_key + * @param in_is_world_admin + * @param ip + * @param local + */ + ClientListEntry( + uint32 id, + uint32 in_loginserver_id, + const char *in_loginserver_name, + const char *in_login_name, + const char *in_login_key, + int16 in_is_world_admin = 0, + uint32 ip = 0, + uint8 local = 0 + ); + + /** + * @param id + * @param iZS + * @param scl + * @param iOnline + */ ClientListEntry(uint32 id, uint32 iAccID, const char* iAccName, MD5& iMD5Pass, int16 iAdmin = 0); ClientListEntry(uint32 id, ZoneServer* iZS, ServerClientList_Struct* scl, CLE_Status iOnline); ~ClientListEntry(); @@ -31,7 +67,7 @@ public: void Update(ZoneServer* zoneserver, ServerClientList_Struct* scl, CLE_Status iOnline = CLE_Status::InZone); void LSUpdate(ZoneServer* zoneserver); void LSZoneChange(ZoneToZone_Struct* ztz); - bool CheckAuth(uint32 iLSID, const char* key); + bool CheckAuth(uint32 loginserver_account_id, const char* key_password); bool CheckAuth(const char* iName, MD5& iMD5Password); bool CheckAuth(uint32 id, const char* key, uint32 ip); void SetOnline(ZoneServer* iZS, CLE_Status iOnline); @@ -47,9 +83,10 @@ public: void Camp(ZoneServer* iZS = 0); // Login Server stuff + inline const char* LoginServer() const { return source_loginserver; } inline uint32 LSID() const { return pLSID; } inline uint32 LSAccountID() const { return pLSID; } - inline const char* LSName() const { return plsname; } + inline const char* LSName() const { return loginserver_account_name; } inline int16 WorldAdmin() const { return pworldadmin; } inline const char* GetLSKey() const { return plskey; } inline const CLE_Status GetOnline() const { return pOnline; } @@ -99,37 +136,38 @@ private: uint8 stale; // Login Server stuff + char source_loginserver[64]{}; //Loginserver we came from. uint32 pLSID; - char plsname[32]; - char plskey[16]; + char loginserver_account_name[32]{}; + char plskey[16]{}; int16 pworldadmin; // Login server's suggested admin status setting bool plocal; // Account stuff uint32 paccountid; - char paccountname[32]; + char paccountname[32]{}; MD5 pMD5Pass; - int16 padmin; + int16 padmin{}; // Character info - ZoneServer* pzoneserver; - uint32 pzone; - uint16 pinstance; - uint32 pcharid; - char pname[64]; - uint8 plevel; - uint8 pclass_; - uint16 prace; - uint8 panon; - uint8 ptellsoff; - uint32 pguild_id; - bool pLFG; - uint8 gm; - uint8 pClientVersion; - uint8 pLFGFromLevel; - uint8 pLFGToLevel; - bool pLFGMatchFilter; - char pLFGComments[64]; + ZoneServer* pzoneserver{}; + uint32 pzone{}; + uint16 pinstance{}; + uint32 pcharid{}; + char pname[64]{}; + uint8 plevel{}; + uint8 pclass_{}; + uint16 prace{}; + uint8 panon{}; + uint8 ptellsoff{}; + uint32 pguild_id{}; + bool pLFG{}; + uint8 gm{}; + uint8 pClientVersion{}; + uint8 pLFGFromLevel{}; + uint8 pLFGToLevel{}; + bool pLFGMatchFilter{}; + char pLFGComments[64]{}; // Tell Queue -- really a vector :D std::vector tell_queue; diff --git a/world/clientlist.cpp b/world/clientlist.cpp index cb05e707d..92a02b6ec 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -63,7 +63,7 @@ void ClientList::Process() { if (!iterator.GetData()->Process()) { struct in_addr in; in.s_addr = iterator.GetData()->GetIP(); - Log(Logs::Detail, Logs::World_Server,"Removing client from %s:%d", inet_ntoa(in), iterator.GetData()->GetPort()); + LogInfo("Removing client from [{}]:[{}]", inet_ntoa(in), iterator.GetData()->GetPort()); iterator.RemoveCurrent(); } else @@ -110,16 +110,16 @@ void ClientList::GetCLEIP(uint32 iIP) { countCLEIPs = iterator.GetData(); if ((countCLEIPs->GetIP() == iIP) && ((countCLEIPs->Admin() < (RuleI(World, ExemptMaxClientsStatus))) || (RuleI(World, ExemptMaxClientsStatus) < 0))) { // If the IP matches, and the connection admin status is below the exempt status, or exempt status is less than 0 (no-one is exempt) IPInstances++; // Increment the occurences of this IP address - Log(Logs::General, Logs::Client_Login, "Account ID: %i Account Name: %s IP: %s.", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); + LogClientLogin("Account ID: [{}] Account Name: [{}] IP: [{}]", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); if (RuleB(World, EnableIPExemptions)) { - Log(Logs::General, Logs::Client_Login, "Account ID: %i Account Name: %s IP: %s IP Instances: %i Max IP Instances: %i", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str(), IPInstances, database.GetIPExemption(long2ip(countCLEIPs->GetIP()).c_str())); + LogClientLogin("Account ID: [{}] Account Name: [{}] IP: [{}] IP Instances: [{}] Max IP Instances: [{}]", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str(), IPInstances, database.GetIPExemption(long2ip(countCLEIPs->GetIP()).c_str())); if (IPInstances > database.GetIPExemption(long2ip(countCLEIPs->GetIP()).c_str())) { if(RuleB(World, IPLimitDisconnectAll)) { - Log(Logs::General, Logs::Client_Login, "Disconnect: All accounts on IP %s", long2ip(countCLEIPs->GetIP()).c_str()); + LogClientLogin("Disconnect: All accounts on IP [{}]", long2ip(countCLEIPs->GetIP()).c_str()); DisconnectByIP(iIP); return; } else { - Log(Logs::General, Logs::Client_Login, "Disconnect: Account %s on IP %s.", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); + LogClientLogin("Disconnect: Account [{}] on IP [{}]", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); countCLEIPs->SetOnline(CLE_Status::Offline); iterator.RemoveCurrent(); continue; @@ -128,14 +128,14 @@ void ClientList::GetCLEIP(uint32 iIP) { } else { if (IPInstances > (RuleI(World, MaxClientsPerIP))) { // If the number of connections exceeds the lower limit if (RuleB(World, MaxClientsSetByStatus)) { // If MaxClientsSetByStatus is set to True, override other IP Limit Rules - Log(Logs::General, Logs::Client_Login, "Account ID: %i Account Name: %s IP: %s IP Instances: %i Max IP Instances: %i", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str(), IPInstances, countCLEIPs->Admin()); + LogClientLogin("Account ID: [{}] Account Name: [{}] IP: [{}] IP Instances: [{}] Max IP Instances: [{}]", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str(), IPInstances, countCLEIPs->Admin()); if (IPInstances > countCLEIPs->Admin()) { // The IP Limit is set by the status of the account if status > MaxClientsPerIP if(RuleB(World, IPLimitDisconnectAll)) { - Log(Logs::General, Logs::Client_Login, "Disconnect: All accounts on IP %s", long2ip(countCLEIPs->GetIP()).c_str()); + LogClientLogin("Disconnect: All accounts on IP [{}]", long2ip(countCLEIPs->GetIP()).c_str()); DisconnectByIP(iIP); return; } else { - Log(Logs::General, Logs::Client_Login, "Disconnect: Account %s on IP %s.", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); + LogClientLogin("Disconnect: Account [{}] on IP [{}]", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); countCLEIPs->SetOnline(CLE_Status::Offline); // Remove the connection iterator.RemoveCurrent(); continue; @@ -143,22 +143,22 @@ void ClientList::GetCLEIP(uint32 iIP) { } } else if ((countCLEIPs->Admin() < RuleI(World, AddMaxClientsStatus)) || (RuleI(World, AddMaxClientsStatus) < 0)) { // Else if the Admin status of the connection is not eligible for the higher limit, or there is no higher limit (AddMaxClientStatus < 0) if(RuleB(World, IPLimitDisconnectAll)) { - Log(Logs::General, Logs::Client_Login, "Disconnect: All accounts on IP %s", long2ip(countCLEIPs->GetIP()).c_str()); + LogClientLogin("Disconnect: All accounts on IP [{}]", long2ip(countCLEIPs->GetIP()).c_str()); DisconnectByIP(iIP); return; } else { - Log(Logs::General, Logs::Client_Login, "Disconnect: Account %s on IP %s.", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); + LogClientLogin("Disconnect: Account [{}] on IP [{}]", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); countCLEIPs->SetOnline(CLE_Status::Offline); // Remove the connection iterator.RemoveCurrent(); continue; } } else if (IPInstances > RuleI(World, AddMaxClientsPerIP)) { // else they are eligible for the higher limit, but if they exceed that if(RuleB(World, IPLimitDisconnectAll)) { - Log(Logs::General, Logs::Client_Login, "Disconnect: All accounts on IP %s", long2ip(countCLEIPs->GetIP()).c_str()); + LogClientLogin("Disconnect: All accounts on IP [{}]", long2ip(countCLEIPs->GetIP()).c_str()); DisconnectByIP(iIP); return; } else { - Log(Logs::General, Logs::Client_Login, "Disconnect: Account %s on IP %s.", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); + LogClientLogin("Disconnect: Account [{}] on IP [{}]", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); countCLEIPs->SetOnline(CLE_Status::Offline); // Remove the connection iterator.RemoveCurrent(); continue; @@ -253,6 +253,19 @@ ClientListEntry* ClientList::FindCLEByCharacterID(uint32 iCharID) { return nullptr; } +ClientListEntry* ClientList::FindCLEByLSID(uint32 iLSID) { + LinkedListIterator iterator(clientlist); + + iterator.Reset(); + while (iterator.MoreElements()) { + if (iterator.GetData()->LSID() == iLSID) { + return iterator.GetData(); + } + iterator.Advance(); + } + return nullptr; +} + void ClientList::SendCLEList(const int16& admin, const char* to, WorldTCPConnection* connection, const char* iName) { LinkedListIterator iterator(clientlist); char* output = 0; @@ -301,8 +314,8 @@ void ClientList::SendCLEList(const int16& admin, const char* to, WorldTCPConnect } -void ClientList::CLEAdd(uint32 iLSID, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin, uint32 ip, uint8 local) { - auto tmp = new ClientListEntry(GetNextCLEID(), iLSID, iLoginName, iLoginKey, iWorldAdmin, ip, local); +void ClientList::CLEAdd(uint32 iLSID, const char *iLoginServerName, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin, uint32 ip, uint8 local) { + auto tmp = new ClientListEntry(GetNextCLEID(), iLSID, iLoginServerName, iLoginName, iLoginKey, iWorldAdmin, ip, local); clientlist.Append(tmp); } @@ -362,52 +375,18 @@ void ClientList::CLEKeepAlive(uint32 numupdates, uint32* wid) { } } - -ClientListEntry* ClientList::CheckAuth(uint32 id, const char* iKey, uint32 ip ) { - LinkedListIterator iterator(clientlist); +ClientListEntry *ClientList::CheckAuth(uint32 iLSID, const char *iKey) +{ + LinkedListIterator iterator(clientlist); iterator.Reset(); - while(iterator.MoreElements()) { - if (iterator.GetData()->CheckAuth(id, iKey, ip)) + while (iterator.MoreElements()) { + if (iterator.GetData()->CheckAuth(iLSID, iKey)) { return iterator.GetData(); + } iterator.Advance(); } - return 0; -} -ClientListEntry* ClientList::CheckAuth(uint32 iLSID, const char* iKey) { - LinkedListIterator iterator(clientlist); - iterator.Reset(); - while(iterator.MoreElements()) { - if (iterator.GetData()->CheckAuth(iLSID, iKey)) - return iterator.GetData(); - iterator.Advance(); - } - return 0; -} - -ClientListEntry* ClientList::CheckAuth(const char* iName, const char* iPassword) { - LinkedListIterator iterator(clientlist); - MD5 tmpMD5(iPassword); - - iterator.Reset(); - while(iterator.MoreElements()) { - if (iterator.GetData()->CheckAuth(iName, tmpMD5)) - return iterator.GetData(); - iterator.Advance(); - } - int16 tmpadmin; - - //Log.LogDebugType(Logs::Detail, Logs::World_Server,"Login with '%s' and '%s'", iName, iPassword); - - uint32 accid = database.CheckLogin(iName, iPassword, &tmpadmin); - if (accid) { - uint32 lsid = 0; - database.GetAccountIDByName(iName, &tmpadmin, &lsid); - auto tmp = new ClientListEntry(GetNextCLEID(), lsid, iName, tmpMD5, tmpadmin, 0, 0); - clientlist.Append(tmp); - return tmp; - } return 0; } @@ -420,7 +399,7 @@ void ClientList::SendOnlineGuildMembers(uint32 FromID, uint32 GuildID) if(!from) { - Log(Logs::Detail, Logs::World_Server,"Invalid client. FromID=%i GuildID=%i", FromID, GuildID); + LogInfo("Invalid client. FromID=[{}] GuildID=[{}]", FromID, GuildID); return; } @@ -745,7 +724,7 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S safe_delete_array(output); } catch(...){ - Log(Logs::Detail, Logs::World_Server,"Unknown error in world's SendWhoAll (probably mem error), ignoring..."); + LogInfo("Unknown error in world's SendWhoAll (probably mem error), ignoring"); return; } } @@ -888,7 +867,7 @@ void ClientList::SendFriendsWho(ServerFriendsWho_Struct *FriendsWho, WorldTCPCon safe_delete(pack2); } catch(...){ - Log(Logs::Detail, Logs::World_Server,"Unknown error in world's SendFriendsWho (probably mem error), ignoring..."); + LogInfo("Unknown error in world's SendFriendsWho (probably mem error), ignoring"); return; } } @@ -1124,7 +1103,6 @@ Client* ClientList::FindByAccountID(uint32 account_id) { iterator.Reset(); while(iterator.MoreElements()) { - Log(Logs::Detail, Logs::World_Server, "ClientList[0x%08x]::FindByAccountID(%p) iterator.GetData()[%p]", this, account_id, iterator.GetData()); if (iterator.GetData()->GetAccountID() == account_id) { Client* tmp = iterator.GetData(); return tmp; @@ -1139,7 +1117,6 @@ Client* ClientList::FindByName(char* charname) { iterator.Reset(); while(iterator.MoreElements()) { - Log(Logs::Detail, Logs::World_Server, "ClientList[0x%08x]::FindByName(\"%s\") iterator.GetData()[%p]", this, charname, iterator.GetData()); if (iterator.GetData()->GetCharName() == charname) { Client* tmp = iterator.GetData(); return tmp; @@ -1264,15 +1241,16 @@ void ClientList::UpdateClientGuild(uint32 char_id, uint32 guild_id) { void ClientList::RemoveCLEByLSID(uint32 iLSID) { - LinkedListIterator iterator(clientlist); + LinkedListIterator iterator(clientlist); iterator.Reset(); while (iterator.MoreElements()) { if (iterator.GetData()->LSAccountID() == iLSID) { iterator.RemoveCurrent(); } - else + else { iterator.Advance(); + } } } diff --git a/world/clientlist.h b/world/clientlist.h index 2ed9c3971..3386a6f14 100644 --- a/world/clientlist.h +++ b/world/clientlist.h @@ -52,18 +52,17 @@ public: void ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl); void CLERemoveZSRef(ZoneServer* iZS); ClientListEntry* CheckAuth(uint32 iLSID, const char* iKey); - ClientListEntry* CheckAuth(const char* iName, const char* iPassword); - ClientListEntry* CheckAuth(uint32 id, const char* iKey, uint32 ip); ClientListEntry* FindCharacter(const char* name); ClientListEntry* FindCLEByAccountID(uint32 iAccID); ClientListEntry* FindCLEByCharacterID(uint32 iCharID); + ClientListEntry* FindCLEByLSID(uint32 iLSID); ClientListEntry* GetCLE(uint32 iID); void GetCLEIP(uint32 iIP); uint32 GetCLEIPCount(uint32 iLSAccountID); void DisconnectByIP(uint32 iIP); void CLCheckStale(); void CLEKeepAlive(uint32 numupdates, uint32* wid); - void CLEAdd(uint32 iLSID, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = 0, uint32 ip = 0, uint8 local=0); + void CLEAdd(uint32 iLSID, const char* iLoginServerName, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = 0, uint32 ip = 0, uint8 local=0); void UpdateClientGuild(uint32 char_id, uint32 guild_id); void RemoveCLEByLSID(uint32 iLSID); bool IsAccountInGame(uint32 iLSID); diff --git a/world/console.cpp b/world/console.cpp index 981074884..90e5f7c71 100644 --- a/world/console.cpp +++ b/world/console.cpp @@ -44,7 +44,14 @@ extern LoginServerList loginserverlist; struct EQ::Net::ConsoleLoginStatus CheckLogin(const std::string &username, const std::string &password) { struct EQ::Net::ConsoleLoginStatus ret; - ret.account_id = database.CheckLogin(username.c_str(), password.c_str()); + + std::string prefix = "eqemu"; + std::string raw_user = ""; + + ParseAccountString(username, raw_user, prefix); + + ret.account_id = database.CheckLogin(raw_user.c_str(), password.c_str(), prefix.c_str()); + if (ret.account_id == 0) { return ret; } @@ -229,6 +236,7 @@ void ConsoleMd5( uint8 md5[16]; MD5::Generate((const uchar *) args[0].c_str(), strlen(args[0].c_str()), md5); + connection->SendLine( StringFormat( "MD5: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", @@ -248,7 +256,8 @@ void ConsoleMd5( md5[13], md5[14], md5[15] - )); + ) + ); } /** @@ -581,7 +590,7 @@ void ConsoleZoneBootup( strcpy(&tmpname[1], connection->UserName().c_str()); Log(Logs::Detail, - Logs::World_Server, + Logs::WorldServer, "Console ZoneBootup: %s, %s, %s", tmpname, args[1].c_str(), @@ -711,20 +720,17 @@ void ConsoleSetPass( connection->SendLine("Format: setpass accountname password"); } else { + std::string prefix = "eqemu"; + std::string raw_user = ""; + + ParseAccountString(args[0], raw_user, prefix); + int16 tmpstatus = 0; - uint32 tmpid = database.GetAccountIDByName(args[0].c_str(), &tmpstatus); + uint32 tmpid = database.GetAccountIDByName(raw_user.c_str(), prefix.c_str(), &tmpstatus); + if (!tmpid) { connection->SendLine("Error: Account not found"); } - else if (tmpstatus > connection->Admin()) { - connection->SendLine("Cannot change password: Account's status is higher than yours"); - } - else if (database.SetLocalPassword(tmpid, args[1].c_str())) { - connection->SendLine("Password changed."); - } - else { - connection->SendLine("Error changing password."); - } } } @@ -901,4 +907,4 @@ void RegisterConsoleFunctions(std::unique_ptr& console) console->RegisterCall("zonestatus", 50, "zonestatus", std::bind(ConsoleZoneStatus, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));console->RegisterCall("ping", 50, "ping", std::bind(ConsoleNull, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("quit", 50, "quit", std::bind(ConsoleQuit, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); console->RegisterCall("exit", 50, "exit", std::bind(ConsoleQuit, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); -} +} \ No newline at end of file diff --git a/world/console.old.cpp b/world/console.old.cpp index fa5ca0ffe..185b95751 100644 --- a/world/console.old.cpp +++ b/world/console.old.cpp @@ -88,7 +88,7 @@ void Console::Die() { state = CONSOLE_STATE_CLOSED; struct in_addr in; in.s_addr = GetIP(); - Log(Logs::Detail, Logs::World_Server,"Removing console from %s:%d",inet_ntoa(in),GetPort()); + LogInfo("Removing console from [{}]:[{}]",inet_ntoa(in),GetPort()); tcpc->Disconnect(); } @@ -219,7 +219,7 @@ bool Console::Process() { if (!tcpc->Connected()) { struct in_addr in; in.s_addr = GetIP(); - Log(Logs::Detail, Logs::World_Server,"Removing console (!tcpc->Connected) from %s:%d",inet_ntoa(in),GetPort()); + LogInfo("Removing console (!tcpc->Connected) from [{}]:[{}]",inet_ntoa(in),GetPort()); return false; } //if we have not gotten the special markers after this timer, send login prompt @@ -252,7 +252,7 @@ bool Console::Process() { SendMessage(1, "Timeout, disconnecting..."); struct in_addr in; in.s_addr = GetIP(); - Log(Logs::Detail, Logs::World_Server,"TCP connection timeout from %s:%d",inet_ntoa(in),GetPort()); + LogInfo("TCP connection timeout from [{}]:[{}]",inet_ntoa(in),GetPort()); return false; } @@ -261,29 +261,29 @@ bool Console::Process() { in.s_addr = GetIP(); if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeZone) { auto zs = new ZoneServer(tcpc); - Log(Logs::Detail, Logs::World_Server,"New zoneserver #%d from %s:%d", zs->GetID(), inet_ntoa(in), GetPort()); + LogInfo("New zoneserver #[{}] from [{}]:[{}]", zs->GetID(), inet_ntoa(in), GetPort()); zoneserver_list.Add(zs); numzones++; tcpc = 0; } else if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeLauncher) { - Log(Logs::Detail, Logs::World_Server,"New launcher from %s:%d", inet_ntoa(in), GetPort()); + LogInfo("New launcher from [{}]:[{}]", inet_ntoa(in), GetPort()); launcher_list.Add(tcpc); tcpc = 0; } else if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeUCS) { - Log(Logs::Detail, Logs::World_Server,"New UCS Connection from %s:%d", inet_ntoa(in), GetPort()); + LogInfo("New UCS Connection from [{}]:[{}]", inet_ntoa(in), GetPort()); UCSLink.SetConnection(tcpc); tcpc = 0; } else if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeQueryServ) { - Log(Logs::Detail, Logs::World_Server,"New QS Connection from %s:%d", inet_ntoa(in), GetPort()); + LogInfo("New QS Connection from [{}]:[{}]", inet_ntoa(in), GetPort()); QSLink.SetConnection(tcpc); tcpc = 0; } else { - Log(Logs::Detail, Logs::World_Server,"Unsupported packet mode from %s:%d", inet_ntoa(in), GetPort()); + LogInfo("Unsupported packet mode from [{}]:[{}]", inet_ntoa(in), GetPort()); } return false; } @@ -440,7 +440,7 @@ void Console::ProcessCommand(const char* command) { state = CONSOLE_STATE_CLOSED; return; } - Log(Logs::Detail, Logs::World_Server,"TCP console authenticated: Username=%s, Admin=%d",paccountname,admin); + LogInfo("TCP console authenticated: Username=[{}], Admin=[{}]",paccountname,admin); SendMessage(1, 0); SendMessage(2, "Login accepted."); state = CONSOLE_STATE_CONNECTED; @@ -737,7 +737,7 @@ void Console::ProcessCommand(const char* command) { tmpname[0] = '*'; strcpy(&tmpname[1], paccountname); - Log(Logs::Detail, Logs::World_Server,"Console ZoneBootup: %s, %s, %s",tmpname,sep.arg[2],sep.arg[1]); + LogInfo("Console ZoneBootup: [{}], [{}], [{}]",tmpname,sep.arg[2],sep.arg[1]); zoneserver_list.SOPZoneBootup(tmpname, atoi(sep.arg[1]), sep.arg[2], (bool) (strcasecmp(sep.arg[3], "static") == 0)); } } @@ -821,7 +821,7 @@ void Console::ProcessCommand(const char* command) { #endif RunLoops = true; SendMessage(1, " Login Server Reconnect manually restarted by Console"); - Log(Logs::Detail, Logs::World_Server,"Login Server Reconnect manually restarted by Console"); + LogInfo("Login Server Reconnect manually restarted by Console"); } else if (strcasecmp(sep.arg[0], "zonelock") == 0 && admin >= consoleZoneStatus) { if (strcasecmp(sep.arg[1], "list") == 0) { diff --git a/world/eql_config.cpp b/world/eql_config.cpp index 1a1536591..9c065acb5 100644 --- a/world/eql_config.cpp +++ b/world/eql_config.cpp @@ -42,7 +42,7 @@ void EQLConfig::LoadSettings() { std::string query = StringFormat("SELECT dynamics FROM launcher WHERE name = '%s'", namebuf); auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "EQLConfig::LoadSettings: %s", results.ErrorMessage().c_str()); + LogError("EQLConfig::LoadSettings: {}", results.ErrorMessage().c_str()); } else { auto row = results.begin(); @@ -52,7 +52,7 @@ void EQLConfig::LoadSettings() { query = StringFormat("SELECT zone, port FROM launcher_zones WHERE launcher = '%s'", namebuf); results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "EQLConfig::LoadSettings: %s", results.ErrorMessage().c_str()); + LogError("EQLConfig::LoadSettings: {}", results.ErrorMessage().c_str()); return; } @@ -199,7 +199,7 @@ bool EQLConfig::ChangeStaticZone(Const_char *short_name, uint16 port) { res = m_zones.find(short_name); if(res == m_zones.end()) { //not found. - Log(Logs::General, Logs::Error, "Update for unknown zone %s", short_name); + LogError("Update for unknown zone {}", short_name); return false; } @@ -235,7 +235,7 @@ bool EQLConfig::DeleteStaticZone(Const_char *short_name) { res = m_zones.find(short_name); if(res == m_zones.end()) { //not found. - Log(Logs::General, Logs::Error, "Update for unknown zone %s", short_name); + LogError("Update for unknown zone {}", short_name); return false; } diff --git a/world/eqw_http_handler.cpp b/world/eqw_http_handler.cpp index 9e0effb1e..5ae1b8b60 100644 --- a/world/eqw_http_handler.cpp +++ b/world/eqw_http_handler.cpp @@ -139,11 +139,11 @@ bool EQWHTTPHandler::CheckAuth() const { int16 status = 0; uint32 acctid = database.CheckLogin(m_username.c_str(), m_password.c_str(), &status); if(acctid == 0) { - Log(Logs::Detail, Logs::World_Server, "Login autentication failed for %s with '%s'", m_username.c_str(), m_password.c_str()); + LogInfo("Login autentication failed for [{}] with [{}]", m_username.c_str(), m_password.c_str()); return(false); } if(status < httpLoginStatus) { - Log(Logs::Detail, Logs::World_Server, "Login of %s failed: status too low.", m_username.c_str()); + LogInfo("Login of [{}] failed: status too low", m_username.c_str()); return(false); } @@ -278,29 +278,29 @@ void EQWHTTPServer::CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP } void EQWHTTPServer::Stop() { - Log(Logs::Detail, Logs::World_Server, "Requesting that HTTP Service stop."); + LogInfo("Requesting that HTTP Service stop"); m_running = false; Close(); } bool EQWHTTPServer::Start(uint16 port, const char *mime_file) { if(m_running) { - Log(Logs::Detail, Logs::World_Server, "HTTP Service is already running on port %d", m_port); + LogInfo("HTTP Service is already running on port [{}]", m_port); return(false); } //load up our nice mime types if(!EQWHTTPHandler::LoadMimeTypes(mime_file)) { - Log(Logs::Detail, Logs::World_Server, "Failed to load mime types from '%s'", mime_file); + LogInfo("Failed to load mime types from [{}]", mime_file); return(false); } else { - Log(Logs::Detail, Logs::World_Server, "Loaded mime types from %s", mime_file); + LogInfo("Loaded mime types from [{}]", mime_file); } //fire up the server thread char errbuf[TCPServer_ErrorBufferSize]; if(!Open(port, errbuf)) { - Log(Logs::Detail, Logs::World_Server, "Unable to bind to port %d for HTTP service: %s", port, errbuf); + LogInfo("Unable to bind to port [{}] for HTTP service: [{}]", port, errbuf); return(false); } diff --git a/world/eqw_parser.cpp b/world/eqw_parser.cpp index b0604924e..46cc4465c 100644 --- a/world/eqw_parser.cpp +++ b/world/eqw_parser.cpp @@ -65,7 +65,7 @@ EQWParser::EQWParser() { my_perl = perl_alloc(); _empty_sv = newSV(0); if (!my_perl) { - Log(Logs::Detail, Logs::World_Server, "Error: perl_alloc failed!"); + LogInfo("Error: perl_alloc failed!"); } else { DoInit(); @@ -184,10 +184,10 @@ void EQWParser::DoInit() { #ifdef EMBPERL_PLUGIN - Log(Logs::Detail, Logs::World_Server, "Loading worldui perl plugins."); + LogInfo("Loading worldui perl plugins"); std::string err; if(!eval_file("world", "worldui.pl", err)) { - Log(Logs::Detail, Logs::World_Server, "Warning - world.pl: %s", err.c_str()); + LogInfo("Warning - world.pl: [{}]", err.c_str()); } eval_pv( diff --git a/world/launcher_link.cpp b/world/launcher_link.cpp index 83c51947b..d60933b41 100644 --- a/world/launcher_link.cpp +++ b/world/launcher_link.cpp @@ -79,25 +79,25 @@ void LauncherLink::ProcessMessage(uint16 opcode, EQ::Net::Packet &p) break; } case ServerOP_ZAAuth: { - Log(Logs::Detail, Logs::World_Server, "Got authentication from %s when they are already authenticated.", m_name.c_str()); + LogInfo("Got authentication from [{}] when they are already authenticated", m_name.c_str()); break; } case ServerOP_LauncherConnectInfo: { const LauncherConnectInfo *it = (const LauncherConnectInfo *)pack->pBuffer; if (HasName()) { - Log(Logs::Detail, Logs::World_Server, "Launcher '%s' received an additional connect packet with name '%s'. Ignoring.", m_name.c_str(), it->name); + LogInfo("Launcher [{}] received an additional connect packet with name [{}]. Ignoring", m_name.c_str(), it->name); break; } m_name = it->name; EQLConfig *config = launcher_list.GetConfig(m_name.c_str()); if (config == nullptr) { - Log(Logs::Detail, Logs::World_Server, "Unknown launcher '%s' connected. Disconnecting.", it->name); + LogInfo("Unknown launcher [{}] connected. Disconnecting", it->name); Disconnect(); break; } - Log(Logs::Detail, Logs::World_Server, "Launcher Identified itself as '%s'. Loading zone list.", it->name); + LogInfo("Launcher Identified itself as [{}]. Loading zone list", it->name); std::vector result; //database.GetLauncherZones(it->name, result); @@ -111,7 +111,7 @@ void LauncherLink::ProcessMessage(uint16 opcode, EQ::Net::Packet &p) zs.port = cur->port; zs.up = false; zs.starts = 0; - Log(Logs::Detail, Logs::World_Server, "%s: Loaded zone '%s' on port %d", m_name.c_str(), cur->name.c_str(), zs.port); + LogInfo("[{}]: Loaded zone [{}] on port [{}]", m_name.c_str(), cur->name.c_str(), zs.port); m_states[cur->name] = zs; } @@ -127,17 +127,17 @@ void LauncherLink::ProcessMessage(uint16 opcode, EQ::Net::Packet &p) std::map::iterator res; res = m_states.find(it->short_name); if (res == m_states.end()) { - Log(Logs::Detail, Logs::World_Server, "%s: reported state for zone %s which it does not have.", m_name.c_str(), it->short_name); + LogInfo("[{}]: reported state for zone [{}] which it does not have", m_name.c_str(), it->short_name); break; } - Log(Logs::Detail, Logs::World_Server, "%s: %s reported state %s (%d starts)", m_name.c_str(), it->short_name, it->running ? "STARTED" : "STOPPED", it->start_count); + LogInfo("[{}]: [{}] reported state [{}] ([{}] starts)", m_name.c_str(), it->short_name, it->running ? "STARTED" : "STOPPED", it->start_count); res->second.up = it->running; res->second.starts = it->start_count; break; } default: { - Log(Logs::Detail, Logs::World_Server, "Unknown ServerOPcode from launcher 0x%04x, size %d", pack->opcode, pack->size); + LogInfo("Unknown ServerOPcode from launcher {:#04x}, size [{}]", pack->opcode, pack->size); DumpPacket(pack->pBuffer, pack->size); break; } @@ -153,7 +153,7 @@ void LauncherLink::BootZone(const char *short_name, uint16 port) { zs.port = port; zs.up = false; zs.starts = 0; - Log(Logs::Detail, Logs::World_Server, "%s: Loaded zone '%s' on port %d", m_name.c_str(), short_name, zs.port); + LogInfo("[{}]: Loaded zone [{}] on port [{}]", m_name.c_str(), short_name, zs.port); m_states[short_name] = zs; StartZone(short_name, port); diff --git a/world/launcher_list.cpp b/world/launcher_list.cpp index c13276384..b38b38589 100644 --- a/world/launcher_list.cpp +++ b/world/launcher_list.cpp @@ -65,10 +65,10 @@ void LauncherList::Process() { std::map::iterator res; res = m_launchers.find(name); if (res != m_launchers.end()) { - Log(Logs::Detail, Logs::World_Server, "Ghosting launcher %s", name.c_str()); + LogInfo("Ghosting launcher [{}]", name.c_str()); delete res->second; } - Log(Logs::Detail, Logs::World_Server, "Removing pending launcher %d. Adding %s to active list.", l->GetID(), name.c_str()); + LogInfo("Removing pending launcher [{}]. Adding [{}] to active list", l->GetID(), name.c_str()); //put the launcher in the list. m_launchers[name] = l; } @@ -99,7 +99,7 @@ LauncherLink *LauncherList::FindByZone(const char *short_name) { void LauncherList::Add(std::shared_ptr conn) { auto it = new LauncherLink(nextID++, conn); - Log(Logs::Detail, Logs::World_Server, "Adding pending launcher %d", it->GetID()); + LogInfo("Adding pending launcher [{}]", it->GetID()); m_pendingLaunchers.push_back(it); } diff --git a/world/login_server.cpp b/world/login_server.cpp index 02e9fdbab..2533a9bad 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -28,7 +28,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/packet_dump.h" #include "../common/string_util.h" #include "../common/eqemu_logsys.h" -#include "../common/eqemu_logsys_fmt.h" #include "login_server.h" #include "login_server_list.h" #include "zoneserver.h" @@ -38,48 +37,63 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "cliententry.h" #include "world_config.h" -extern ZSList zoneserver_list; -extern ClientList client_list; -extern uint32 numzones; -extern uint32 numplayers; -extern volatile bool RunLoops; -LoginServer::LoginServer(const char* iAddress, uint16 iPort, const char* Account, const char* Password, bool legacy) +extern ZSList zoneserver_list; +extern ClientList client_list; +extern uint32 numzones; +extern uint32 numplayers; +extern volatile bool RunLoops; + +LoginServer::LoginServer(const char *iAddress, uint16 iPort, const char *Account, const char *Password, bool legacy) { strn0cpy(LoginServerAddress, iAddress, 256); - LoginServerPort = iPort; - strn0cpy(LoginAccount, Account, 31); - strn0cpy(LoginPassword, Password, 31); + LoginServerPort = iPort; + LoginAccount = Account; + LoginPassword = Password; CanAccountUpdate = false; - IsLegacy = legacy; + IsLegacy = legacy; Connect(); } -LoginServer::~LoginServer() { +LoginServer::~LoginServer() +{ } -void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p) { +void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p) +{ const WorldConfig *Config = WorldConfig::get(); - Log(Logs::Detail, Logs::World_Server, "Recevied ServerPacket from LS OpCode 0x04x", opcode); + LogNetcode("[ProcessUsertoWorldReqLeg] Received ServerPacket from LS OpCode {:#04x}", opcode); - UsertoWorldRequest_Struct* utwr = (UsertoWorldRequest_Struct*)p.Data(); - uint32 id = database.GetAccountIDFromLSID(utwr->lsaccountid); - int16 status = database.CheckStatus(id); + UsertoWorldRequestLegacy_Struct *utwr = (UsertoWorldRequestLegacy_Struct *) p.Data(); + uint32 id = database.GetAccountIDFromLSID("eqemu", utwr->lsaccountid); + int16 status = database.CheckStatus(id); + + LogDebug( + "[ProcessUsertoWorldReqLeg] id [{}] status [{}] account_id [{}] world_id [{}] from_id [{}] to_id [{}] ip [{}]", + id, + status, + utwr->lsaccountid, + utwr->worldid, + utwr->FromID, + utwr->ToID, + utwr->IPAddr + ); ServerPacket outpack; - outpack.opcode = ServerOP_UsertoWorldResp; - outpack.size = sizeof(UsertoWorldResponse_Struct); + outpack.opcode = ServerOP_UsertoWorldRespLeg; + outpack.size = sizeof(UsertoWorldResponseLegacy_Struct); outpack.pBuffer = new uchar[outpack.size]; memset(outpack.pBuffer, 0, outpack.size); - UsertoWorldResponse_Struct* utwrs = (UsertoWorldResponse_Struct*)outpack.pBuffer; - utwrs->lsaccountid = utwr->lsaccountid; - utwrs->ToID = utwr->FromID; - utwrs->worldid = utwr->worldid; - utwrs->response = UserToWorldStatusSuccess; - if (Config->Locked == true) - { + UsertoWorldResponseLegacy_Struct *utwrs = (UsertoWorldResponseLegacy_Struct *) outpack.pBuffer; + utwrs->lsaccountid = utwr->lsaccountid; + utwrs->ToID = utwr->FromID; + utwrs->worldid = utwr->worldid; + utwrs->response = UserToWorldStatusSuccess; + + if (Config->Locked) { if (status < 100) { + LogDebug("[ProcessUsertoWorldReqLeg] Server locked and status is not high enough for account_id [{0}]", utwr->lsaccountid); utwrs->response = UserToWorldStatusWorldUnavail; SendPacket(&outpack); return; @@ -87,19 +101,22 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p) { } int32 x = Config->MaxClients; - if ((int32)numplayers >= x && x != -1 && x != 255 && status < 80) { + if ((int32) numplayers >= x && x != -1 && x != 255 && status < 80) { + LogDebug("[ProcessUsertoWorldReqLeg] World at capacity account_id [{0}]", utwr->lsaccountid); utwrs->response = UserToWorldStatusWorldAtCapacity; SendPacket(&outpack); return; } if (status == -1) { + LogDebug("[ProcessUsertoWorldReqLeg] User suspended account_id [{0}]", utwr->lsaccountid); utwrs->response = UserToWorldStatusSuspended; SendPacket(&outpack); return; } if (status == -2) { + LogDebug("[ProcessUsertoWorldReqLeg] User banned account_id [{0}]", utwr->lsaccountid); utwrs->response = UserToWorldStatusBanned; SendPacket(&outpack); return; @@ -107,217 +124,498 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p) { if (RuleB(World, EnforceCharacterLimitAtLogin)) { if (client_list.IsAccountInGame(utwr->lsaccountid)) { + LogDebug("[ProcessUsertoWorldReqLeg] User already online account_id [{0}]", utwr->lsaccountid); utwrs->response = UserToWorldStatusAlreadyOnline; SendPacket(&outpack); return; } } + LogDebug("[ProcessUsertoWorldReqLeg] Sent response to account_id [{0}]", utwr->lsaccountid); + SendPacket(&outpack); } -void LoginServer::ProcessLSClientAuth(uint16_t opcode, EQ::Net::Packet &p) { +void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p) +{ const WorldConfig *Config = WorldConfig::get(); - Log(Logs::Detail, Logs::World_Server, "Recevied ServerPacket from LS OpCode 0x04x", opcode); + LogNetcode("[ProcessUsertoWorldReq] Received ServerPacket from LS OpCode {:#04x}", opcode); + + UsertoWorldRequest_Struct *utwr = (UsertoWorldRequest_Struct *) p.Data(); + uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid); + int16 status = database.CheckStatus(id); + + LogDebug( + "[ProcessUsertoWorldReq] id [{}] status [{}] account_id [{}] world_id [{}] from_id [{}] to_id [{}] ip [{}]", + id, + status, + utwr->lsaccountid, + utwr->worldid, + utwr->FromID, + utwr->ToID, + utwr->IPAddr + ); + + ServerPacket outpack; + outpack.opcode = ServerOP_UsertoWorldResp; + outpack.size = sizeof(UsertoWorldResponse_Struct); + outpack.pBuffer = new uchar[outpack.size]; + memset(outpack.pBuffer, 0, outpack.size); + + UsertoWorldResponse_Struct *utwrs = (UsertoWorldResponse_Struct *) outpack.pBuffer; + utwrs->lsaccountid = utwr->lsaccountid; + utwrs->ToID = utwr->FromID; + strn0cpy(utwrs->login, utwr->login, 64); + utwrs->worldid = utwr->worldid; + utwrs->response = UserToWorldStatusSuccess; + + if (Config->Locked == true) { + if (status < 100) { + LogDebug("[ProcessUsertoWorldReq] Server locked and status is not high enough for account_id [{0}]", utwr->lsaccountid); + utwrs->response = UserToWorldStatusWorldUnavail; + SendPacket(&outpack); + return; + } + } + + int32 x = Config->MaxClients; + if ((int32) numplayers >= x && x != -1 && x != 255 && status < 80) { + LogDebug("[ProcessUsertoWorldReq] World at capacity account_id [{0}]", utwr->lsaccountid); + utwrs->response = UserToWorldStatusWorldAtCapacity; + SendPacket(&outpack); + return; + } + + if (status == -1) { + LogDebug("[ProcessUsertoWorldReq] User suspended account_id [{0}]", utwr->lsaccountid); + utwrs->response = UserToWorldStatusSuspended; + SendPacket(&outpack); + return; + } + + if (status == -2) { + LogDebug("[ProcessUsertoWorldReq] User banned account_id [{0}]", utwr->lsaccountid); + utwrs->response = UserToWorldStatusBanned; + SendPacket(&outpack); + return; + } + + if (RuleB(World, EnforceCharacterLimitAtLogin)) { + if (client_list.IsAccountInGame(utwr->lsaccountid)) { + LogDebug("[ProcessUsertoWorldReq] User already online account_id [{0}]", utwr->lsaccountid); + utwrs->response = UserToWorldStatusAlreadyOnline; + SendPacket(&outpack); + return; + } + } + + LogDebug("[ProcessUsertoWorldReq] Sent response to account_id [{0}]", utwr->lsaccountid); + + SendPacket(&outpack); +} + +void LoginServer::ProcessLSClientAuthLegacy(uint16_t opcode, EQ::Net::Packet &p) +{ + const WorldConfig *Config = WorldConfig::get(); + LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode); try { - auto slsca = p.GetSerialize(0); - client_list.CLEAdd(slsca.lsaccount_id, slsca.name, slsca.key, slsca.worldadmin, slsca.ip, slsca.local); + auto client_authentication_request = p.GetSerialize(0); + + LogDebug( + "Processing Loginserver Auth Legacy | account_id [{0}] account_name [{1}] key [{2}] admin [{3}] ip [{4}] " + "local_network [{5}]", + client_authentication_request.loginserver_account_id, + client_authentication_request.loginserver_account_name, + client_authentication_request.key, + client_authentication_request.is_world_admin, + client_authentication_request.ip, + client_authentication_request.is_client_from_local_network + ); + + client_list.CLEAdd( + client_authentication_request.loginserver_account_id, + "eqemu", + client_authentication_request.loginserver_account_name, + client_authentication_request.key, + client_authentication_request.is_world_admin, + client_authentication_request.ip, + client_authentication_request.is_client_from_local_network + ); } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing LSClientAuth packet from world.\n{0}", ex.what()); + LogError("Error parsing ClientAuthLegacy packet from world\nReason [{0}]", ex.what()); } } -void LoginServer::ProcessLSFatalError(uint16_t opcode, EQ::Net::Packet &p) { +void LoginServer::ProcessLSClientAuth(uint16_t opcode, EQ::Net::Packet &p) +{ const WorldConfig *Config = WorldConfig::get(); - Log(Logs::Detail, Logs::World_Server, "Recevied ServerPacket from LS OpCode 0x04x", opcode); + LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode); - Log(Logs::Detail, Logs::World_Server, "Login server responded with FatalError."); + try { + auto client_authentication_request = p.GetSerialize(0); + + LogDebug( + "Processing Loginserver Auth | account_id [{0}] account_name [{1}] loginserver_name [{2}] key [{3}] " + "admin [{4}] ip [{5}] local_network [{6}]", + client_authentication_request.loginserver_account_id, + client_authentication_request.account_name, + client_authentication_request.loginserver_name, + client_authentication_request.key, + client_authentication_request.is_world_admin, + client_authentication_request.ip, + client_authentication_request.is_client_from_local_network + ); + + client_list.CLEAdd( + client_authentication_request.loginserver_account_id, + client_authentication_request.loginserver_name, + client_authentication_request.account_name, + client_authentication_request.key, + client_authentication_request.is_world_admin, + client_authentication_request.ip, + client_authentication_request.is_client_from_local_network + ); + } + catch (std::exception &ex) { + LogError("Error parsing ClientAuth packet from world\nReason [{0}]", ex.what()); + } +} + +void LoginServer::ProcessLSFatalError(uint16_t opcode, EQ::Net::Packet &p) +{ + const WorldConfig *Config = WorldConfig::get(); + LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode); + + LogInfo("Login server responded with FatalError"); if (p.Length() > 1) { - Log(Logs::Detail, Logs::World_Server, " %s", (const char*)p.Data()); + LogError("Error [{}]", (const char *) p.Data()); } } -void LoginServer::ProcessSystemwideMessage(uint16_t opcode, EQ::Net::Packet &p) { +void LoginServer::ProcessSystemwideMessage(uint16_t opcode, EQ::Net::Packet &p) +{ const WorldConfig *Config = WorldConfig::get(); - Log(Logs::Detail, Logs::World_Server, "Recevied ServerPacket from LS OpCode 0x04x", opcode); + LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode); - ServerSystemwideMessage* swm = (ServerSystemwideMessage*)p.Data(); + ServerSystemwideMessage *swm = (ServerSystemwideMessage *) p.Data(); zoneserver_list.SendEmoteMessageRaw(0, 0, 0, swm->type, swm->message); } -void LoginServer::ProcessLSRemoteAddr(uint16_t opcode, EQ::Net::Packet &p) { +void LoginServer::ProcessLSRemoteAddr(uint16_t opcode, EQ::Net::Packet &p) +{ const WorldConfig *Config = WorldConfig::get(); - Log(Logs::Detail, Logs::World_Server, "Recevied ServerPacket from LS OpCode 0x04x", opcode); + LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode); if (!Config->WorldAddress.length()) { - WorldConfig::SetWorldAddress((char *)p.Data()); - Log(Logs::Detail, Logs::World_Server, "Loginserver provided %s as world address", (const char*)p.Data()); + WorldConfig::SetWorldAddress((char *) p.Data()); + LogInfo("Loginserver provided [{}] as world address", (const char *) p.Data()); } } -void LoginServer::ProcessLSAccountUpdate(uint16_t opcode, EQ::Net::Packet &p) { +void LoginServer::ProcessLSAccountUpdate(uint16_t opcode, EQ::Net::Packet &p) +{ const WorldConfig *Config = WorldConfig::get(); - Log(Logs::Detail, Logs::World_Server, "Recevied ServerPacket from LS OpCode 0x04x", opcode); + LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode); - Log(Logs::Detail, Logs::World_Server, "Received ServerOP_LSAccountUpdate packet from loginserver"); + LogNetcode("Received ServerOP_LSAccountUpdate packet from loginserver"); CanAccountUpdate = true; } -bool LoginServer::Connect() { - std::string tmp; - if (database.GetVariable("loginType", tmp) && strcasecmp(tmp.c_str(), "MinILogin") == 0) { - minilogin = true; - Log(Logs::Detail, Logs::World_Server, "Setting World to MiniLogin Server type"); - } - else - minilogin = false; - - if (minilogin && WorldConfig::get()->WorldAddress.length() == 0) { - Log(Logs::Detail, Logs::World_Server, "**** For minilogin to work, you need to set the
element in the section."); - return false; - } - +bool LoginServer::Connect() +{ char errbuf[1024]; if ((LoginServerIP = ResolveIP(LoginServerAddress, errbuf)) == 0) { - Log(Logs::Detail, Logs::World_Server, "Unable to resolve '%s' to an IP.", LoginServerAddress); + LogInfo("Unable to resolve [{}] to an IP", LoginServerAddress); return false; } if (LoginServerIP == 0 || LoginServerPort == 0) { - Log(Logs::Detail, Logs::World_Server, "Connect info incomplete, cannot connect: %s:%d", LoginServerAddress, LoginServerPort); + LogInfo( + "Connect info incomplete, cannot connect: [{0}:{1}]", + LoginServerAddress, + LoginServerPort + ); + return false; } if (IsLegacy) { legacy_client.reset(new EQ::Net::ServertalkLegacyClient(LoginServerAddress, LoginServerPort, false)); - legacy_client->OnConnect([this](EQ::Net::ServertalkLegacyClient *client) { - if (client) { - Log(Logs::Detail, Logs::World_Server, "Connected to Legacy Loginserver: %s:%d", LoginServerAddress, LoginServerPort); - if (minilogin) + legacy_client->OnConnect( + [this](EQ::Net::ServertalkLegacyClient *client) { + if (client) { + LogInfo( + "Connected to Legacy Loginserver: [{0}:{1}]", + LoginServerAddress, + LoginServerPort + ); + SendInfo(); - else - SendNewInfo(); - SendStatus(); - zoneserver_list.SendLSZones(); - - statusupdate_timer.reset(new EQ::Timer(LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) { SendStatus(); - })); - } - else { - Log(Logs::Detail, Logs::World_Server, "Could not connect to Legacy Loginserver: %s:%d", LoginServerAddress, LoginServerPort); - } - }); + zoneserver_list.SendLSZones(); - legacy_client->OnMessage(ServerOP_UsertoWorldReq, std::bind(&LoginServer::ProcessUsertoWorldReq, this, std::placeholders::_1, std::placeholders::_2)); - legacy_client->OnMessage(ServerOP_LSClientAuth, std::bind(&LoginServer::ProcessLSClientAuth, this, std::placeholders::_1, std::placeholders::_2)); - legacy_client->OnMessage(ServerOP_LSFatalError, std::bind(&LoginServer::ProcessLSFatalError, this, std::placeholders::_1, std::placeholders::_2)); - legacy_client->OnMessage(ServerOP_SystemwideMessage, std::bind(&LoginServer::ProcessSystemwideMessage, this, std::placeholders::_1, std::placeholders::_2)); - legacy_client->OnMessage(ServerOP_LSRemoteAddr, std::bind(&LoginServer::ProcessLSRemoteAddr, this, std::placeholders::_1, std::placeholders::_2)); - legacy_client->OnMessage(ServerOP_LSAccountUpdate, std::bind(&LoginServer::ProcessLSAccountUpdate, this, std::placeholders::_1, std::placeholders::_2)); + statusupdate_timer.reset( + new EQ::Timer( + LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) { + SendStatus(); + } + ) + ); + } + else { + LogInfo( + "Could not connect to Legacy Loginserver: [{0}:{1}]", + LoginServerAddress, + LoginServerPort + ); + } + } + ); + legacy_client->OnMessage( + ServerOP_UsertoWorldReqLeg, + std::bind( + &LoginServer::ProcessUsertoWorldReqLeg, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + legacy_client->OnMessage( + ServerOP_UsertoWorldReq, + std::bind( + &LoginServer::ProcessUsertoWorldReq, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + legacy_client->OnMessage( + ServerOP_LSClientAuthLeg, + std::bind( + &LoginServer::ProcessLSClientAuthLegacy, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + legacy_client->OnMessage( + ServerOP_LSClientAuth, + std::bind( + &LoginServer::ProcessLSClientAuth, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + legacy_client->OnMessage( + ServerOP_LSFatalError, + std::bind( + &LoginServer::ProcessLSFatalError, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + legacy_client->OnMessage( + ServerOP_SystemwideMessage, + std::bind( + &LoginServer::ProcessSystemwideMessage, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + legacy_client->OnMessage( + ServerOP_LSRemoteAddr, + std::bind( + &LoginServer::ProcessLSRemoteAddr, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + legacy_client->OnMessage( + ServerOP_LSAccountUpdate, + std::bind( + &LoginServer::ProcessLSAccountUpdate, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); } else { client.reset(new EQ::Net::ServertalkClient(LoginServerAddress, LoginServerPort, false, "World", "")); - client->OnConnect([this](EQ::Net::ServertalkClient *client) { - if (client) { - Log(Logs::Detail, Logs::World_Server, "Connected to Loginserver: %s:%d", LoginServerAddress, LoginServerPort); - if (minilogin) + client->OnConnect( + [this](EQ::Net::ServertalkClient *client) { + if (client) { + LogInfo( + "Connected to Loginserver: [{0}:{1}]", + LoginServerAddress, + LoginServerPort + ); SendInfo(); - else - SendNewInfo(); - SendStatus(); - zoneserver_list.SendLSZones(); - - statusupdate_timer.reset(new EQ::Timer(LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) { SendStatus(); - })); - } - else { - Log(Logs::Detail, Logs::World_Server, "Could not connect to Loginserver: %s:%d", LoginServerAddress, LoginServerPort); - } - }); + zoneserver_list.SendLSZones(); - client->OnMessage(ServerOP_UsertoWorldReq, std::bind(&LoginServer::ProcessUsertoWorldReq, this, std::placeholders::_1, std::placeholders::_2)); - client->OnMessage(ServerOP_LSClientAuth, std::bind(&LoginServer::ProcessLSClientAuth, this, std::placeholders::_1, std::placeholders::_2)); - client->OnMessage(ServerOP_LSFatalError, std::bind(&LoginServer::ProcessLSFatalError, this, std::placeholders::_1, std::placeholders::_2)); - client->OnMessage(ServerOP_SystemwideMessage, std::bind(&LoginServer::ProcessSystemwideMessage, this, std::placeholders::_1, std::placeholders::_2)); - client->OnMessage(ServerOP_LSRemoteAddr, std::bind(&LoginServer::ProcessLSRemoteAddr, this, std::placeholders::_1, std::placeholders::_2)); - client->OnMessage(ServerOP_LSAccountUpdate, std::bind(&LoginServer::ProcessLSAccountUpdate, this, std::placeholders::_1, std::placeholders::_2)); + statusupdate_timer.reset( + new EQ::Timer( + LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) { + SendStatus(); + } + )); + } + else { + LogInfo( + "Could not connect to Loginserver: [{0}:{1}]", + LoginServerAddress, + LoginServerPort + ); + } + } + ); + + client->OnMessage( + ServerOP_UsertoWorldReqLeg, + std::bind( + &LoginServer::ProcessUsertoWorldReqLeg, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + client->OnMessage( + ServerOP_UsertoWorldReq, + std::bind( + &LoginServer::ProcessUsertoWorldReq, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + client->OnMessage( + ServerOP_LSClientAuthLeg, + std::bind( + &LoginServer::ProcessLSClientAuthLegacy, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + client->OnMessage( + ServerOP_LSClientAuth, + std::bind( + &LoginServer::ProcessLSClientAuth, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + client->OnMessage( + ServerOP_LSFatalError, + std::bind( + &LoginServer::ProcessLSFatalError, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + client->OnMessage( + ServerOP_SystemwideMessage, + std::bind( + &LoginServer::ProcessSystemwideMessage, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + client->OnMessage( + ServerOP_LSRemoteAddr, + std::bind( + &LoginServer::ProcessLSRemoteAddr, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); + client->OnMessage( + ServerOP_LSAccountUpdate, + std::bind( + &LoginServer::ProcessLSAccountUpdate, + this, + std::placeholders::_1, + std::placeholders::_2 + ) + ); } + m_keepalive.reset(new EQ::Timer(5000, true, std::bind(&LoginServer::OnKeepAlive, this, std::placeholders::_1))); + return true; } -void LoginServer::SendInfo() { + +void LoginServer::SendInfo() +{ const WorldConfig *Config = WorldConfig::get(); auto pack = new ServerPacket; - pack->opcode = ServerOP_LSInfo; - pack->size = sizeof(ServerLSInfo_Struct); + pack->opcode = ServerOP_NewLSInfo; + pack->size = sizeof(ServerNewLSInfo_Struct); pack->pBuffer = new uchar[pack->size]; memset(pack->pBuffer, 0, pack->size); - ServerLSInfo_Struct* lsi = (ServerLSInfo_Struct*)pack->pBuffer; - strcpy(lsi->protocolversion, EQEMU_PROTOCOL_VERSION); - strcpy(lsi->serverversion, LOGIN_VERSION); - strcpy(lsi->name, Config->LongName.c_str()); - strcpy(lsi->account, LoginAccount); - strcpy(lsi->password, LoginPassword); - strcpy(lsi->address, Config->WorldAddress.c_str()); - SendPacket(pack); - delete pack; -} - -void LoginServer::SendNewInfo() { - const WorldConfig *Config = WorldConfig::get(); - - auto pack = new ServerPacket; - pack->opcode = ServerOP_NewLSInfo; - pack->size = sizeof(ServerNewLSInfo_Struct); - pack->pBuffer = new uchar[pack->size]; - memset(pack->pBuffer, 0, pack->size); - ServerNewLSInfo_Struct* lsi = (ServerNewLSInfo_Struct*)pack->pBuffer; - strcpy(lsi->protocolversion, EQEMU_PROTOCOL_VERSION); - strcpy(lsi->serverversion, LOGIN_VERSION); - strcpy(lsi->name, Config->LongName.c_str()); - strcpy(lsi->shortname, Config->ShortName.c_str()); - strcpy(lsi->account, LoginAccount); - strcpy(lsi->password, LoginPassword); - if (Config->WorldAddress.length()) - strcpy(lsi->remote_address, Config->WorldAddress.c_str()); - if (Config->LocalAddress.length()) - strcpy(lsi->local_address, Config->LocalAddress.c_str()); + ServerNewLSInfo_Struct *lsi = (ServerNewLSInfo_Struct *) pack->pBuffer; + strcpy(lsi->protocol_version, EQEMU_PROTOCOL_VERSION); + strcpy(lsi->server_version, LOGIN_VERSION); + strcpy(lsi->server_long_name, Config->LongName.c_str()); + strcpy(lsi->server_short_name, Config->ShortName.c_str()); + strn0cpy(lsi->account_name, LoginAccount.c_str(), 30); + strn0cpy(lsi->account_password, LoginPassword.c_str(), 30); + if (Config->WorldAddress.length()) { + strcpy(lsi->remote_ip_address, Config->WorldAddress.c_str()); + } + if (Config->LocalAddress.length()) { + strcpy(lsi->local_ip_address, Config->LocalAddress.c_str()); + } else { auto local_addr = IsLegacy ? legacy_client->Handle()->LocalIP() : client->Handle()->LocalIP(); - strcpy(lsi->local_address, local_addr.c_str()); - WorldConfig::SetLocalAddress(lsi->local_address); + strcpy(lsi->local_ip_address, local_addr.c_str()); + WorldConfig::SetLocalAddress(lsi->local_ip_address); } SendPacket(pack); delete pack; } -void LoginServer::SendStatus() { +void LoginServer::SendStatus() +{ auto pack = new ServerPacket; - pack->opcode = ServerOP_LSStatus; - pack->size = sizeof(ServerLSStatus_Struct); + pack->opcode = ServerOP_LSStatus; + pack->size = sizeof(ServerLSStatus_Struct); pack->pBuffer = new uchar[pack->size]; memset(pack->pBuffer, 0, pack->size); - ServerLSStatus_Struct* lss = (ServerLSStatus_Struct*)pack->pBuffer; + auto loginserver_status = (ServerLSStatus_Struct *) pack->pBuffer; - if (WorldConfig::get()->Locked) - lss->status = -2; - else if (numzones <= 0) - lss->status = -2; - else - lss->status = numplayers; + if (WorldConfig::get()->Locked) { + loginserver_status->status = -2; + } + else if (numzones <= 0) { + loginserver_status->status = -2; + } + else { + loginserver_status->status = numplayers; + } - lss->num_zones = numzones; - lss->num_players = numplayers; + loginserver_status->num_zones = numzones; + loginserver_status->num_players = numplayers; SendPacket(pack); delete pack; } +/** + * @param pack + */ void LoginServer::SendPacket(ServerPacket *pack) { if (IsLegacy) { @@ -332,28 +630,23 @@ void LoginServer::SendPacket(ServerPacket *pack) } } -void LoginServer::SendAccountUpdate(ServerPacket* pack) { - ServerLSAccountUpdate_Struct* s = (ServerLSAccountUpdate_Struct *)pack->pBuffer; +void LoginServer::SendAccountUpdate(ServerPacket *pack) +{ + auto *ls_account_update = (ServerLSAccountUpdate_Struct *) pack->pBuffer; if (CanUpdate()) { - Log(Logs::Detail, Logs::World_Server, "Sending ServerOP_LSAccountUpdate packet to loginserver: %s:%d", LoginServerAddress, LoginServerPort); - strn0cpy(s->worldaccount, LoginAccount, 30); - strn0cpy(s->worldpassword, LoginPassword, 30); + LogInfo( + "Sending ServerOP_LSAccountUpdate packet to loginserver: [{0}]:[{1}]", + LoginServerAddress, + LoginServerPort + ); + strn0cpy(ls_account_update->worldaccount, LoginAccount.c_str(), 30); + strn0cpy(ls_account_update->worldpassword, LoginPassword.c_str(), 30); SendPacket(pack); } } -bool LoginServer::Connected() +void LoginServer::OnKeepAlive(EQ::Timer *t) { - if (IsLegacy) { - if (legacy_client) { - return legacy_client->Connected(); - } - } - else { - if (client) { - return client->Connected(); - } - } - - return false; -} + ServerPacket pack(ServerOP_KeepAlive, 0); + SendPacket(&pack); +} \ No newline at end of file diff --git a/world/login_server.h b/world/login_server.h index 03f80afe0..a1bb35d7b 100644 --- a/world/login_server.h +++ b/world/login_server.h @@ -35,34 +35,35 @@ public: ~LoginServer(); bool Connect(); - void SendInfo(); - void SendNewInfo(); void SendStatus(); void SendPacket(ServerPacket* pack); void SendAccountUpdate(ServerPacket* pack); - bool Connected(); - bool MiniLogin() { return minilogin; } + bool Connected() { return IsLegacy ? legacy_client->Connected() : client->Connected(); } bool CanUpdate() { return CanAccountUpdate; } private: + void ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p); void ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p); void ProcessLSClientAuth(uint16_t opcode, EQ::Net::Packet &p); + void ProcessLSClientAuthLegacy(uint16_t opcode, EQ::Net::Packet &p); void ProcessLSFatalError(uint16_t opcode, EQ::Net::Packet &p); void ProcessSystemwideMessage(uint16_t opcode, EQ::Net::Packet &p); void ProcessLSRemoteAddr(uint16_t opcode, EQ::Net::Packet &p); void ProcessLSAccountUpdate(uint16_t opcode, EQ::Net::Packet &p); - bool minilogin; + void OnKeepAlive(EQ::Timer *t); + std::unique_ptr m_keepalive; + std::unique_ptr client; std::unique_ptr legacy_client; std::unique_ptr statusupdate_timer; char LoginServerAddress[256]; uint32 LoginServerIP; uint16 LoginServerPort; - char LoginAccount[32]; - char LoginPassword[32]; + std::string LoginAccount; + std::string LoginPassword; bool CanAccountUpdate; bool IsLegacy; }; diff --git a/world/login_server_list.cpp b/world/login_server_list.cpp index 92d67fb82..64081a3fe 100644 --- a/world/login_server_list.cpp +++ b/world/login_server_list.cpp @@ -63,14 +63,6 @@ bool LoginServerList::SendInfo() { return true; } -bool LoginServerList::SendNewInfo() { - for (auto &iter : m_list) { - (*iter).SendNewInfo(); - } - - return true; -} - bool LoginServerList::SendStatus() { for (auto &iter : m_list) { (*iter).SendStatus(); @@ -88,7 +80,7 @@ bool LoginServerList::SendPacket(ServerPacket* pack) { } bool LoginServerList::SendAccountUpdate(ServerPacket* pack) { - Log(Logs::Detail, Logs::World_Server, "Requested to send ServerOP_LSAccountUpdate packet to all loginservers"); + LogInfo("Requested to send ServerOP_LSAccountUpdate packet to all loginservers"); for (auto &iter : m_list) { if ((*iter).CanUpdate()) { (*iter).SendAccountUpdate(pack); @@ -118,16 +110,6 @@ bool LoginServerList::AllConnected() { return true; } -bool LoginServerList::MiniLogin() { - for (auto &iter : m_list) { - if ((*iter).MiniLogin()) { - return true; - } - } - - return false; -} - bool LoginServerList::CanUpdate() { for (auto &iter : m_list) { if ((*iter).CanUpdate()) { diff --git a/world/login_server_list.h b/world/login_server_list.h index 56806f704..d1b29a8cf 100644 --- a/world/login_server_list.h +++ b/world/login_server_list.h @@ -18,14 +18,12 @@ public: void Add(const char*, uint16, const char*, const char*, bool); bool SendInfo(); - bool SendNewInfo(); bool SendStatus(); bool SendPacket(ServerPacket *pack); bool SendAccountUpdate(ServerPacket *pack); bool Connected(); bool AllConnected(); - bool MiniLogin(); bool CanUpdate(); size_t GetServerCount() const { return m_list.size(); } diff --git a/world/net.cpp b/world/net.cpp index 8278f9437..aeb9d8b4d 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/string_util.h" #include "../common/eqemu_logsys.h" -#include "../common/eqemu_logsys_fmt.h" #include "../common/queue.h" #include "../common/timer.h" #include "../common/eq_packet.h" @@ -119,18 +118,20 @@ int main(int argc, char** argv) { LogSys.LoadLogSettingsDefaults(); set_exception_handler(); - /* If eqemu_config.json does not exist - create it from conversion... */ - if (!std::ifstream("eqemu_config.json")) { + /** + * Auto convert json config from xml + */ + if (!std::ifstream("eqemu_config.json") && std::ifstream("eqemu_config.xml")) { CheckForServerScript(true); - /* Run EQEmu Server script (Checks for database updates) */ if(system("perl eqemu_server.pl convert_xml")); } else { - /* Download EQEmu Server Maintenance Script if doesn't exist */ CheckForServerScript(); } - /* Database Version Check */ + /** + * Database version + */ uint32 Database_Version = CURRENT_BINARY_DATABASE_VERSION; uint32 Bots_Database_Version = CURRENT_BINARY_BOTS_DATABASE_VERSION; if (argc >= 2) { @@ -141,60 +142,74 @@ int main(int argc, char** argv) { } // Load server configuration - Log(Logs::General, Logs::World_Server, "Loading server configuration.."); + LogInfo("Loading server configuration"); if (!WorldConfig::LoadConfig()) { - Log(Logs::General, Logs::World_Server, "Loading server configuration failed."); + LogError("Loading server configuration failed"); return 1; } Config = WorldConfig::get(); - Log(Logs::General, Logs::World_Server, "CURRENT_VERSION: %s", CURRENT_VERSION); + LogInfo("CURRENT_VERSION: [{}]", CURRENT_VERSION); if (signal(SIGINT, CatchSignal) == SIG_ERR) { - Log(Logs::General, Logs::World_Server, "Could not set signal handler"); + LogError("Could not set signal handler"); return 1; } if (signal(SIGTERM, CatchSignal) == SIG_ERR) { - Log(Logs::General, Logs::World_Server, "Could not set signal handler"); + LogError("Could not set signal handler"); return 1; } #ifndef WIN32 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { - Log(Logs::General, Logs::World_Server, "Could not set signal handler"); + LogError("Could not set signal handler"); return 1; } #endif - // add login server config to list + /** + * Add Loginserver + */ if (Config->LoginCount == 0) { if (Config->LoginHost.length()) { - loginserverlist.Add(Config->LoginHost.c_str(), Config->LoginPort, Config->LoginAccount.c_str(), Config->LoginPassword.c_str(), Config->LoginLegacy); - Log(Logs::General, Logs::World_Server, "Added loginserver %s:%i", Config->LoginHost.c_str(), Config->LoginPort); + loginserverlist.Add( + Config->LoginHost.c_str(), + Config->LoginPort, + Config->LoginAccount.c_str(), + Config->LoginPassword.c_str(), + Config->LoginLegacy + ); + LogInfo("Added loginserver [{}]:[{}]", Config->LoginHost.c_str(), Config->LoginPort); } } else { - LinkedList loginlist = Config->loginlist; - LinkedListIterator iterator(loginlist); + LinkedList loginlist = Config->loginlist; + LinkedListIterator iterator(loginlist); iterator.Reset(); while (iterator.MoreElements()) { - loginserverlist.Add(iterator.GetData()->LoginHost.c_str(), iterator.GetData()->LoginPort, iterator.GetData()->LoginAccount.c_str(), iterator.GetData()->LoginPassword.c_str(), - iterator.GetData()->LoginLegacy); - Log(Logs::General, Logs::World_Server, "Added loginserver %s:%i", iterator.GetData()->LoginHost.c_str(), iterator.GetData()->LoginPort); + loginserverlist.Add( + iterator.GetData()->LoginHost.c_str(), + iterator.GetData()->LoginPort, + iterator.GetData()->LoginAccount.c_str(), + iterator.GetData()->LoginPassword.c_str(), + iterator.GetData()->LoginLegacy + ); + + LogInfo("Added loginserver [{}]:[{}]", iterator.GetData()->LoginHost.c_str(), iterator.GetData()->LoginPort); iterator.Advance(); } } - Log(Logs::General, Logs::World_Server, "Connecting to MySQL %s@%s:%i...", Config->DatabaseUsername.c_str(), Config->DatabaseHost.c_str(), Config->DatabasePort); + LogInfo("Connecting to MySQL [{}]@[{}]:[{}]", Config->DatabaseUsername.c_str(), Config->DatabaseHost.c_str(), Config->DatabasePort); if (!database.Connect( Config->DatabaseHost.c_str(), Config->DatabaseUsername.c_str(), Config->DatabasePassword.c_str(), Config->DatabaseDB.c_str(), Config->DatabasePort)) { - Log(Logs::General, Logs::World_Server, "Cannot continue without a database connection."); + LogError("Cannot continue without a database connection"); return 1; } guild_mgr.SetDatabase(&database); @@ -230,7 +245,12 @@ int main(int argc, char** argv) { if (argc == 5) { if (Seperator::IsNumber(argv[4])) { if (atoi(argv[4]) >= 0 && atoi(argv[4]) <= 255) { - if (database.CreateAccount(argv[2], argv[3], atoi(argv[4])) == 0) { + std::string user; + std::string loginserver; + + ParseAccountString(argv[2], user, loginserver); + + if (database.CreateAccount(argv[2], argv[3], atoi(argv[4]), loginserver.c_str(), 0) == 0) { std::cerr << "database.CreateAccount failed." << std::endl; return 1; } @@ -298,70 +318,85 @@ int main(int argc, char** argv) { } if (!ignore_db) { - Log(Logs::General, Logs::World_Server, "Checking Database Conversions.."); + LogInfo("Checking Database Conversions"); database.CheckDatabaseConversions(); } - Log(Logs::General, Logs::World_Server, "Loading variables.."); + LogInfo("Loading variables"); database.LoadVariables(); std::string hotfix_name; if (database.GetVariable("hotfix_name", hotfix_name)) { if (!hotfix_name.empty()) { - Log(Logs::General, Logs::Zone_Server, "Current hotfix in use: '%s'", hotfix_name.c_str()); + LogInfo("Current hotfix in use: [{}]", hotfix_name.c_str()); } } - Log(Logs::General, Logs::World_Server, "Purging expired data buckets..."); + LogInfo("Purging expired data buckets"); database.PurgeAllDeletedDataBuckets(); - Log(Logs::General, Logs::World_Server, "Loading zones.."); + LogInfo("Loading zones"); database.LoadZoneNames(); - Log(Logs::General, Logs::World_Server, "Clearing groups.."); + LogInfo("Clearing groups"); database.ClearGroup(); - Log(Logs::General, Logs::World_Server, "Clearing raids.."); + LogInfo("Clearing raids"); database.ClearRaid(); database.ClearRaidDetails(); database.ClearRaidLeader(); - Log(Logs::General, Logs::World_Server, "Clearing inventory snapshots.."); + LogInfo("Clearing inventory snapshots"); database.ClearInvSnapshots(); - Log(Logs::General, Logs::World_Server, "Loading items.."); + LogInfo("Loading items"); if (!database.LoadItems(hotfix_name)) - Log(Logs::General, Logs::World_Server, "Error: Could not load item data. But ignoring"); - Log(Logs::General, Logs::World_Server, "Loading skill caps.."); + LogError("Error: Could not load item data. But ignoring"); + LogInfo("Loading skill caps"); if (!database.LoadSkillCaps(std::string(hotfix_name))) - Log(Logs::General, Logs::World_Server, "Error: Could not load skill cap data. But ignoring"); - Log(Logs::General, Logs::World_Server, "Loading guilds.."); + LogError("Error: Could not load skill cap data. But ignoring"); + + + LogInfo("Loading guilds"); guild_mgr.LoadGuilds(); + //rules: { + if (!RuleManager::Instance()->UpdateOrphanedRules(&database)) { + LogInfo("Failed to process 'Orphaned Rules' update operation."); + } + + if (!RuleManager::Instance()->UpdateInjectedRules(&database, "default")) { + LogInfo("Failed to process 'Injected Rules' for ruleset 'default' update operation."); + } + std::string tmp; if (database.GetVariable("RuleSet", tmp)) { - Log(Logs::General, Logs::World_Server, "Loading rule set '%s'", tmp.c_str()); + LogInfo("Loading rule set [{}]", tmp.c_str()); + if (!RuleManager::Instance()->LoadRules(&database, tmp.c_str(), false)) { - Log(Logs::General, Logs::World_Server, "Failed to load ruleset '%s', falling back to defaults.", tmp.c_str()); + LogInfo("Failed to load ruleset [{}], falling back to defaults", tmp.c_str()); } } else { + if (!RuleManager::Instance()->LoadRules(&database, "default", false)) { - Log(Logs::General, Logs::World_Server, "No rule set configured, using default rules"); + LogInfo("No rule set configured, using default rules"); } else { - Log(Logs::General, Logs::World_Server, "Loaded default rule set 'default'", tmp.c_str()); + LogInfo("Loaded default rule set [default]", tmp.c_str()); } } - EQEmu::InitializeDynamicLookups(); - Log(Logs::General, Logs::World_Server, "Initialized dynamic dictionary entries"); + if (!RuleManager::Instance()->RestoreRuleNotes(&database)) { + LogInfo("Failed to process 'Restore Rule Notes' update operation."); + } } + EQEmu::InitializeDynamicLookups(); + LogInfo("Initialized dynamic dictionary entries"); + if (RuleB(World, ClearTempMerchantlist)) { - Log(Logs::General, Logs::World_Server, "Clearing temporary merchant lists.."); + LogInfo("Clearing temporary merchant lists"); database.ClearMerchantTemp(); } - RuleManager::Instance()->SaveRules(&database); - - Log(Logs::General, Logs::World_Server, "Loading EQ time of day.."); + LogInfo("Loading EQ time of day"); TimeOfDay_Struct eqTime; time_t realtime; eqTime = database.LoadTime(realtime); @@ -369,7 +404,7 @@ int main(int argc, char** argv) { Timer EQTimeTimer(600000); EQTimeTimer.Start(600000); - Log(Logs::General, Logs::World_Server, "Loading launcher list.."); + LogInfo("Loading launcher list"); launcher_list.LoadList(); std::string tmp; @@ -377,37 +412,37 @@ int main(int argc, char** argv) { if (tmp.length() == 1 && tmp[0] == '1') { holdzones = true; } - Log(Logs::General, Logs::World_Server, "Reboot zone modes %s", holdzones ? "ON" : "OFF"); + LogInfo("Reboot zone modes [{}]", holdzones ? "ON" : "OFF"); - Log(Logs::General, Logs::World_Server, "Deleted %i stale player corpses from database", database.DeleteStalePlayerCorpses()); + LogInfo("Deleted [{}] stale player corpses from database", database.DeleteStalePlayerCorpses()); - Log(Logs::General, Logs::World_Server, "Loading adventures..."); + LogInfo("Loading adventures"); if (!adventure_manager.LoadAdventureTemplates()) { - Log(Logs::General, Logs::World_Server, "Unable to load adventure templates."); + LogInfo("Unable to load adventure templates"); } if (!adventure_manager.LoadAdventureEntries()) { - Log(Logs::General, Logs::World_Server, "Unable to load adventure templates."); + LogInfo("Unable to load adventure templates"); } adventure_manager.Load(); adventure_manager.LoadLeaderboardInfo(); - Log(Logs::General, Logs::World_Server, "Purging expired instances"); + LogInfo("Purging expired instances"); database.PurgeExpiredInstances(); Timer PurgeInstanceTimer(450000); PurgeInstanceTimer.Start(450000); - Log(Logs::General, Logs::World_Server, "Loading char create info..."); + LogInfo("Loading char create info"); database.LoadCharacterCreateAllocations(); database.LoadCharacterCreateCombos(); std::unique_ptr console; if (Config->TelnetEnabled) { - Log(Logs::General, Logs::World_Server, "Console (TCP) listener started."); + LogInfo("Console (TCP) listener started"); console.reset(new EQ::Net::ConsoleServer(Config->TelnetIP, Config->TelnetTCPPort)); RegisterConsoleFunctions(console); } @@ -420,18 +455,20 @@ int main(int argc, char** argv) { server_opts.ipv6 = false; server_opts.credentials = Config->SharedKey; server_connection->Listen(server_opts); - Log(Logs::General, Logs::World_Server, "Server (TCP) listener started."); + LogInfo("Server (TCP) listener started"); - server_connection->OnConnectionIdentified("Zone", [&console](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "New Zone Server connection from {2} at {0}:{1}", - connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); + server_connection->OnConnectionIdentified( + "Zone", [&console](std::shared_ptr connection) { + LogInfo("New Zone Server connection from [{2}] at [{0}:{1}]", + connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); - numzones++; - zoneserver_list.Add(new ZoneServer(connection, console.get())); - }); + numzones++; + zoneserver_list.Add(new ZoneServer(connection, console.get())); + } + ); server_connection->OnConnectionRemoved("Zone", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "Removed Zone Server connection from {0}", + LogInfo("Removed Zone Server connection from [{0}]", connection->GetUUID()); numzones--; @@ -439,35 +476,35 @@ int main(int argc, char** argv) { }); server_connection->OnConnectionIdentified("Launcher", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "New Launcher connection from {2} at {0}:{1}", + LogInfo("New Launcher connection from [{2}] at [{0}:{1}]", connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); launcher_list.Add(connection); }); server_connection->OnConnectionRemoved("Launcher", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "Removed Launcher connection from {0}", + LogInfo("Removed Launcher connection from [{0}]", connection->GetUUID()); launcher_list.Remove(connection); }); server_connection->OnConnectionIdentified("QueryServ", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "New Query Server connection from {2} at {0}:{1}", + LogInfo("New Query Server connection from [{2}] at [{0}:{1}]", connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); QSLink.AddConnection(connection); }); server_connection->OnConnectionRemoved("QueryServ", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "Removed Query Server connection from {0}", + LogInfo("Removed Query Server connection from [{0}]", connection->GetUUID()); QSLink.RemoveConnection(connection); }); server_connection->OnConnectionIdentified("UCS", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "New UCS Server connection from {2} at {0}:{1}", + LogInfo("New UCS Server connection from [{2}] at [{0}:{1}]", connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); UCSLink.SetConnection(connection); @@ -476,7 +513,7 @@ int main(int argc, char** argv) { }); server_connection->OnConnectionRemoved("UCS", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "Removed Query Server connection from {0}", + LogInfo("Removed UCS Server connection from [{0}]", connection->GetUUID()); UCSLink.SetConnection(nullptr); @@ -485,14 +522,14 @@ int main(int argc, char** argv) { }); server_connection->OnConnectionIdentified("WebInterface", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "New WebInterface Server connection from {2} at {0}:{1}", + LogInfo("New WebInterface Server connection from [{2}] at [{0}:{1}]", connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); web_interface.AddConnection(connection); }); server_connection->OnConnectionRemoved("WebInterface", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "Removed WebInterface Server connection from {0}", + LogInfo("Removed WebInterface Server connection from [{0}]", connection->GetUUID()); web_interface.RemoveConnection(connection); @@ -522,7 +559,7 @@ int main(int argc, char** argv) { eqsm.OnNewConnection([&stream_identifier](std::shared_ptr stream) { stream_identifier.AddStream(stream); - LogF(Logs::Detail, Logs::World_Server, "New connection from IP {0}:{1}", stream->GetRemoteIP(), ntohs(stream->GetRemotePort())); + LogInfo("New connection from IP {0}:{1}", stream->GetRemoteIP(), ntohs(stream->GetRemotePort())); }); while (RunLoops) { @@ -535,23 +572,23 @@ int main(int argc, char** argv) { //check the stream identifier for any now-identified streams while ((eqsi = stream_identifier.PopIdentified())) { //now that we know what patch they are running, start up their client object - struct in_addr in; + struct in_addr in{}; in.s_addr = eqsi->GetRemoteIP(); if (RuleB(World, UseBannedIPsTable)) { //Lieka: Check to see if we have the responsibility for blocking IPs. - Log(Logs::Detail, Logs::World_Server, "Checking inbound connection %s against BannedIPs table", inet_ntoa(in)); + LogInfo("Checking inbound connection [{}] against BannedIPs table", inet_ntoa(in)); if (!database.CheckBannedIPs(inet_ntoa(in))) { //Lieka: Check inbound IP against banned IP table. - Log(Logs::Detail, Logs::World_Server, "Connection %s PASSED banned IPs check. Processing connection.", inet_ntoa(in)); + LogInfo("Connection [{}] PASSED banned IPs check. Processing connection", inet_ntoa(in)); auto client = new Client(eqsi); // @merth: client->zoneattempt=0; client_list.Add(client); } else { - Log(Logs::General, Logs::World_Server, "Connection from %s FAILED banned IPs check. Closing connection.", inet_ntoa(in)); + LogInfo("Connection from [{}] failed banned IPs check. Closing connection", inet_ntoa(in)); eqsi->Close(); //Lieka: If the inbound IP is on the banned table, close the EQStream. } } if (!RuleB(World, UseBannedIPsTable)) { - Log(Logs::Detail, Logs::World_Server, "New connection from %s:%d, processing connection", inet_ntoa(in), ntohs(eqsi->GetRemotePort())); + LogInfo("New connection from [{}]:[{}], processing connection", inet_ntoa(in), ntohs(eqsi->GetRemotePort())); auto client = new Client(eqsi); // @merth: client->zoneattempt=0; client_list.Add(client); @@ -569,9 +606,9 @@ int main(int argc, char** argv) { TimeOfDay_Struct tod; zoneserver_list.worldclock.GetCurrentEQTimeOfDay(time(0), &tod); if (!database.SaveTime(tod.minute, tod.hour, tod.day, tod.month, tod.year)) - Log(Logs::General, Logs::World_Server, "Failed to save eqtime."); + LogError("Failed to save eqtime"); else - Log(Logs::Detail, Logs::World_Server, "EQTime successfully saved."); + LogDebug("EQTime successfully saved"); } zoneserver_list.Process(); @@ -590,18 +627,18 @@ int main(int argc, char** argv) { EQ::EventLoop::Get().Process(); Sleep(5); } - Log(Logs::General, Logs::World_Server, "World main loop completed."); - Log(Logs::General, Logs::World_Server, "Shutting down zone connections (if any)."); + LogInfo("World main loop completed"); + LogInfo("Shutting down zone connections (if any)"); zoneserver_list.KillAll(); - Log(Logs::General, Logs::World_Server, "Zone (TCP) listener stopped."); - Log(Logs::General, Logs::World_Server, "Signaling HTTP service to stop..."); + LogInfo("Zone (TCP) listener stopped"); + LogInfo("Signaling HTTP service to stop"); LogSys.CloseFileLogs(); return 0; } void CatchSignal(int sig_num) { - Log(Logs::General, Logs::World_Server, "Caught signal %d", sig_num); + LogInfo("Caught signal [{}]", sig_num); RunLoops = false; } diff --git a/world/ucs.cpp b/world/ucs.cpp index b745005d4..f5910da80 100644 --- a/world/ucs.cpp +++ b/world/ucs.cpp @@ -16,7 +16,7 @@ void UCSConnection::SetConnection(std::shared_ptrHandle()) { - Log(Logs::Detail, Logs::UCS_Server, "Incoming UCS Connection while we were already connected to a UCS."); + LogInfo("Incoming UCS Connection while we were already connected to a UCS"); Stream->Handle()->Disconnect(); } @@ -46,12 +46,12 @@ void UCSConnection::ProcessPacket(uint16 opcode, EQ::Net::Packet &p) } case ServerOP_ZAAuth: { - Log(Logs::Detail, Logs::UCS_Server, "Got authentication from UCS when they are already authenticated."); + LogInfo("Got authentication from UCS when they are already authenticated"); break; } default: { - Log(Logs::Detail, Logs::UCS_Server, "Unknown ServerOPcode from UCS 0x%04x, size %d", opcode, pack->size); + LogInfo("Unknown ServerOPcode from UCS {:#04x}, size [{}]", opcode, pack->size); DumpPacket(pack->pBuffer, pack->size); break; } diff --git a/world/wguild_mgr.cpp b/world/wguild_mgr.cpp index dbc20fba6..2768a4dd8 100644 --- a/world/wguild_mgr.cpp +++ b/world/wguild_mgr.cpp @@ -34,7 +34,7 @@ WorldGuildManager guild_mgr; void WorldGuildManager::SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation) { - Log(Logs::Detail, Logs::Guilds, "Broadcasting guild refresh for %d, changes: name=%d, motd=%d, rank=d, relation=%d", guild_id, name, motd, rank, relation); + LogGuilds("Broadcasting guild refresh for [{}], changes: name=[{}], motd=[{}], rank=d, relation=[{}]", guild_id, name, motd, rank, relation); auto pack = new ServerPacket(ServerOP_RefreshGuild, sizeof(ServerGuildRefresh_Struct)); ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer; s->guild_id = guild_id; @@ -47,7 +47,7 @@ void WorldGuildManager::SendGuildRefresh(uint32 guild_id, bool name, bool motd, } void WorldGuildManager::SendCharRefresh(uint32 old_guild_id, uint32 guild_id, uint32 charid) { - Log(Logs::Detail, Logs::Guilds, "Broadcasting char refresh for %d from guild %d to world", charid, guild_id); + LogGuilds("Broadcasting char refresh for [{}] from guild [{}] to world", charid, guild_id); auto pack = new ServerPacket(ServerOP_GuildCharRefresh, sizeof(ServerGuildCharRefresh_Struct)); ServerGuildCharRefresh_Struct *s = (ServerGuildCharRefresh_Struct *) pack->pBuffer; s->guild_id = guild_id; @@ -58,7 +58,7 @@ void WorldGuildManager::SendCharRefresh(uint32 old_guild_id, uint32 guild_id, ui } void WorldGuildManager::SendGuildDelete(uint32 guild_id) { - Log(Logs::Detail, Logs::Guilds, "Broadcasting guild delete for guild %d to world", guild_id); + LogGuilds("Broadcasting guild delete for guild [{}] to world", guild_id); auto pack = new ServerPacket(ServerOP_DeleteGuild, sizeof(ServerGuildID_Struct)); ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer; s->guild_id = guild_id; @@ -71,18 +71,18 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) { case ServerOP_RefreshGuild: { if(pack->size != sizeof(ServerGuildRefresh_Struct)) { - Log(Logs::Detail, Logs::Guilds, "Received ServerOP_RefreshGuild of incorrect size %d, expected %d", pack->size, sizeof(ServerGuildRefresh_Struct)); + LogGuilds("Received ServerOP_RefreshGuild of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildRefresh_Struct)); return; } ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer; - Log(Logs::Detail, Logs::Guilds, "Received and broadcasting guild refresh for %d, changes: name=%d, motd=%d, rank=d, relation=%d", s->guild_id, s->name_change, s->motd_change, s->rank_change, s->relation_change); + LogGuilds("Received and broadcasting guild refresh for [{}], changes: name=[{}], motd=[{}], rank=d, relation=[{}]", s->guild_id, s->name_change, s->motd_change, s->rank_change, s->relation_change); //broadcast this packet to all zones. zoneserver_list.SendPacket(pack); //preform a local refresh. if(!RefreshGuild(s->guild_id)) { - Log(Logs::Detail, Logs::Guilds, "Unable to preform local refresh on guild %d", s->guild_id); + LogGuilds("Unable to preform local refresh on guild [{}]", s->guild_id); //can we do anything? } @@ -91,11 +91,11 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) { case ServerOP_GuildCharRefresh: { if(pack->size != sizeof(ServerGuildCharRefresh_Struct)) { - Log(Logs::Detail, Logs::Guilds, "Received ServerOP_RefreshGuild of incorrect size %d, expected %d", pack->size, sizeof(ServerGuildCharRefresh_Struct)); + LogGuilds("Received ServerOP_RefreshGuild of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildCharRefresh_Struct)); return; } ServerGuildCharRefresh_Struct *s = (ServerGuildCharRefresh_Struct *) pack->pBuffer; - Log(Logs::Detail, Logs::Guilds, "Received and broadcasting guild member refresh for char %d to all zones with members of guild %d", s->char_id, s->guild_id); + LogGuilds("Received and broadcasting guild member refresh for char [{}] to all zones with members of guild [{}]", s->char_id, s->guild_id); //preform the local update client_list.UpdateClientGuild(s->char_id, s->guild_id); @@ -110,18 +110,18 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) { case ServerOP_DeleteGuild: { if(pack->size != sizeof(ServerGuildID_Struct)) { - Log(Logs::Detail, Logs::Guilds, "Received ServerOP_DeleteGuild of incorrect size %d, expected %d", pack->size, sizeof(ServerGuildID_Struct)); + LogGuilds("Received ServerOP_DeleteGuild of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildID_Struct)); return; } ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer; - Log(Logs::Detail, Logs::Guilds, "Received and broadcasting guild delete for guild %d", s->guild_id); + LogGuilds("Received and broadcasting guild delete for guild [{}]", s->guild_id); //broadcast this packet to all zones. zoneserver_list.SendPacket(pack); //preform a local refresh. if(!LocalDeleteGuild(s->guild_id)) { - Log(Logs::Detail, Logs::Guilds, "Unable to preform local delete on guild %d", s->guild_id); + LogGuilds("Unable to preform local delete on guild [{}]", s->guild_id); //can we do anything? } @@ -131,7 +131,7 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) { case ServerOP_GuildMemberUpdate: { if(pack->size != sizeof(ServerGuildMemberUpdate_Struct)) { - Log(Logs::Detail, Logs::Guilds, "Received ServerOP_GuildMemberUpdate of incorrect size %d, expected %d", pack->size, sizeof(ServerGuildMemberUpdate_Struct)); + LogGuilds("Received ServerOP_GuildMemberUpdate of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildMemberUpdate_Struct)); return; } @@ -141,7 +141,7 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) { } default: - Log(Logs::Detail, Logs::Guilds, "Unknown packet 0x%x received from zone??", pack->opcode); + LogGuilds("Unknown packet {:#04x} received from zone??", pack->opcode); break; } } diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 47354cc60..fbd4cdf94 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -366,14 +366,14 @@ bool WorldDatabase::GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* return false; } - Log(Logs::General, Logs::Status, "SoF Start zone query: %s\n", query.c_str()); + LogInfo("SoF Start zone query: [{}]\n", query.c_str()); if (results.RowCount() == 0) { printf("No start_zones entry in database, using defaults\n"); isTitanium ? SetTitaniumDefaultStartZone(in_pp, in_cc) : SetSoFDefaultStartZone(in_pp, in_cc); } else { - Log(Logs::General, Logs::Status, "Found starting location in start_zones"); + LogInfo("Found starting location in start_zones"); auto row = results.begin(); in_pp->x = atof(row[0]); in_pp->y = atof(row[1]); @@ -512,7 +512,7 @@ void WorldDatabase::GetLauncherList(std::vector &rl) { const std::string query = "SELECT name FROM launcher"; auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "WorldDatabase::GetLauncherList: %s", results.ErrorMessage().c_str()); + LogError("WorldDatabase::GetLauncherList: {}", results.ErrorMessage().c_str()); return; } @@ -526,7 +526,7 @@ bool WorldDatabase::GetCharacterLevel(const char *name, int &level) std::string query = StringFormat("SELECT level FROM character_data WHERE name = '%s'", name); auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "WorldDatabase::GetCharacterLevel: %s", results.ErrorMessage().c_str()); + LogError("WorldDatabase::GetCharacterLevel: {}", results.ErrorMessage().c_str()); return false; } diff --git a/world/zonelist.cpp b/world/zonelist.cpp index 77782ee9b..abb2a5f6f 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -94,7 +94,7 @@ void ZSList::KillAll() { void ZSList::Process() { if (shutdowntimer && shutdowntimer->Check()) { - Log(Logs::Detail, Logs::World_Server, "Shutdown timer has expired. Telling all zones to shut down and exiting. (fake sigint)"); + LogInfo("Shutdown timer has expired. Telling all zones to shut down and exiting. (fake sigint)"); auto pack2 = new ServerPacket; pack2->opcode = ServerOP_ShutdownAll; pack2->size = 0; @@ -432,6 +432,7 @@ void ZSList::SendChannelMessageRaw(const char* from, const char* to, uint8 chan_ } scm->language = language; + scm->lang_skill = 100; scm->chan_num = chan_num; strcpy(&scm->message[0], message); @@ -570,7 +571,7 @@ void ZSList::RebootZone(const char* ip1, uint16 port, const char* ip2, uint32 sk s->port = port; s->zoneid = zoneid; if (zoneid != 0) - Log(Logs::Detail, Logs::World_Server, "Rebooting static zone with the ID of: %i", zoneid); + LogInfo("Rebooting static zone with the ID of: [{}]", zoneid); tmp[z]->SendPacket(pack); delete pack; safe_delete_array(tmp); diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 21c5d8e65..dd55fac39 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -90,7 +90,7 @@ bool ZoneServer::SetZone(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { char* longname; if (iZoneID) - Log(Logs::Detail, Logs::World_Server, "Setting to '%s' (%d:%d)%s", (zn) ? zn : "", iZoneID, iInstanceID, + Log(Logs::Detail, Logs::WorldServer, "Setting to '%s' (%d:%d)%s", (zn) ? zn : "", iZoneID, iInstanceID, iStaticZone ? " (Static)" : ""); zone_server_zone_id = iZoneID; @@ -541,10 +541,10 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { RezzPlayer_Struct* sRezz = (RezzPlayer_Struct*)pack->pBuffer; if (zoneserver_list.SendPacket(pack)) { - Log(Logs::Detail, Logs::World_Server, "Sent Rez packet for %s", sRezz->rez.your_name); + LogInfo("Sent Rez packet for [{}]", sRezz->rez.your_name); } else { - Log(Logs::Detail, Logs::World_Server, "Could not send Rez packet for %s", sRezz->rez.your_name); + LogInfo("Could not send Rez packet for [{}]", sRezz->rez.your_name); } break; } @@ -589,21 +589,21 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { ServerConnectInfo* sci = (ServerConnectInfo*)p.pBuffer; sci->port = client_port; SendPacket(&p); - Log(Logs::Detail, Logs::World_Server, "Auto zone port configuration. Telling zone to use port %d", client_port); + LogInfo("Auto zone port configuration. Telling zone to use port [{}]", client_port); } else { client_port = sci->port; - Log(Logs::Detail, Logs::World_Server, "Zone specified port %d.", client_port); + LogInfo("Zone specified port [{}]", client_port); } if (sci->address[0]) { strn0cpy(client_address, sci->address, 250); - Log(Logs::Detail, Logs::World_Server, "Zone specified address %s.", sci->address); + LogInfo("Zone specified address [{}]", sci->address); } if (sci->local_address[0]) { strn0cpy(client_local_address, sci->local_address, 250); - Log(Logs::Detail, Logs::World_Server, "Zone specified local address %s.", sci->address); + LogInfo("Zone specified local address [{}]", sci->address); } if (sci->process_id) { @@ -617,7 +617,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { const LaunchName_Struct* ln = (const LaunchName_Struct*)pack->pBuffer; launcher_name = ln->launcher_name; launched_name = ln->zone_name; - Log(Logs::Detail, Logs::World_Server, "Zone started with name %s by launcher %s", launched_name.c_str(), launcher_name.c_str()); + LogInfo("Zone started with name [{}] by launcher [{}]", launched_name.c_str(), launcher_name.c_str()); break; } case ServerOP_ShutdownAll: { @@ -700,12 +700,11 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { if (WorldConfig::get()->UpdateStats) client = client_list.FindCharacter(ztz->name); - Log(Logs::Detail, Logs::World_Server, "ZoneToZone request for %s current zone %d req zone %d\n", - ztz->name, ztz->current_zone_id, ztz->requested_zone_id); + LogInfo("ZoneToZone request for [{}] current zone [{}] req zone [{}]", ztz->name, ztz->current_zone_id, ztz->requested_zone_id); /* This is a request from the egress zone */ if (GetZoneID() == ztz->current_zone_id && GetInstanceID() == ztz->current_instance_id) { - Log(Logs::Detail, Logs::World_Server, "Processing ZTZ for egress from zone for client %s\n", ztz->name); + LogInfo("Processing ZTZ for egress from zone for client [{}]", ztz->name); if (ztz->admin < 80 && ztz->ignorerestrictions < 2 && zoneserver_list.IsZoneLocked(ztz->requested_zone_id)) { ztz->response = 0; @@ -723,20 +722,20 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { /* Zone was already running*/ if (ingress_server) { - Log(Logs::Detail, Logs::World_Server, "Found a zone already booted for %s\n", ztz->name); + LogInfo("Found a zone already booted for [{}]", ztz->name); ztz->response = 1; } /* Boot the Zone*/ else { int server_id; if ((server_id = zoneserver_list.TriggerBootup(ztz->requested_zone_id, ztz->requested_instance_id))) { - Log(Logs::Detail, Logs::World_Server, "Successfully booted a zone for %s\n", ztz->name); + LogInfo("Successfully booted a zone for [{}]", ztz->name); // bootup successful, ready to rock ztz->response = 1; ingress_server = zoneserver_list.FindByID(server_id); } else { - Log(Logs::Detail, Logs::World_Server, "FAILED to boot a zone for %s\n", ztz->name); + LogError("failed to boot a zone for [{}]", ztz->name); // bootup failed, send back error code 0 ztz->response = 0; } @@ -751,7 +750,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { /* Response from Ingress server, route back to egress */ else { - Log(Logs::Detail, Logs::World_Server, "Processing ZTZ for ingress to zone for client %s\n", ztz->name); + LogInfo("Processing ZTZ for ingress to zone for client [{}]", ztz->name); ZoneServer *egress_server = nullptr; if (ztz->current_instance_id > 0) { egress_server = zoneserver_list.FindByInstanceID(ztz->current_instance_id); @@ -769,7 +768,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { } case ServerOP_ClientList: { if (pack->size != sizeof(ServerClientList_Struct)) { - Log(Logs::Detail, Logs::World_Server, "Wrong size on ServerOP_ClientList. Got: %d, Expected: %d", pack->size, sizeof(ServerClientList_Struct)); + LogInfo("Wrong size on ServerOP_ClientList. Got: [{}], Expected: [{}]", pack->size, sizeof(ServerClientList_Struct)); break; } client_list.ClientUpdate(this, (ServerClientList_Struct*)pack->pBuffer); @@ -778,7 +777,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_ClientListKA: { ServerClientListKeepAlive_Struct* sclka = (ServerClientListKeepAlive_Struct*)pack->pBuffer; if (pack->size < 4 || pack->size != 4 + (4 * sclka->numupdates)) { - Log(Logs::Detail, Logs::World_Server, "Wrong size on ServerOP_ClientListKA. Got: %d, Expected: %d", pack->size, (4 + (4 * sclka->numupdates))); + LogInfo("Wrong size on ServerOP_ClientListKA. Got: [{}], Expected: [{}]", pack->size, (4 + (4 * sclka->numupdates))); break; } client_list.CLEKeepAlive(sclka->numupdates, sclka->wid); @@ -893,7 +892,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { } case ServerOP_GMGoto: { if (pack->size != sizeof(ServerGMGoto_Struct)) { - Log(Logs::Detail, Logs::World_Server, "Wrong size on ServerOP_GMGoto. Got: %d, Expected: %d", pack->size, sizeof(ServerGMGoto_Struct)); + LogInfo("Wrong size on ServerOP_GMGoto. Got: [{}], Expected: [{}]", pack->size, sizeof(ServerGMGoto_Struct)); break; } ServerGMGoto_Struct* gmg = (ServerGMGoto_Struct*)pack->pBuffer; @@ -913,7 +912,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { } case ServerOP_Lock: { if (pack->size != sizeof(ServerLock_Struct)) { - Log(Logs::Detail, Logs::World_Server, "Wrong size on ServerOP_Lock. Got: %d, Expected: %d", pack->size, sizeof(ServerLock_Struct)); + LogInfo("Wrong size on ServerOP_Lock. Got: [{}], Expected: [{}]", pack->size, sizeof(ServerLock_Struct)); break; } ServerLock_Struct* slock = (ServerLock_Struct*)pack->pBuffer; @@ -938,7 +937,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { } case ServerOP_Motd: { if (pack->size != sizeof(ServerMotd_Struct)) { - Log(Logs::Detail, Logs::World_Server, "Wrong size on ServerOP_Motd. Got: %d, Expected: %d", pack->size, sizeof(ServerMotd_Struct)); + LogInfo("Wrong size on ServerOP_Motd. Got: [{}], Expected: [{}]", pack->size, sizeof(ServerMotd_Struct)); break; } ServerMotd_Struct* smotd = (ServerMotd_Struct*)pack->pBuffer; @@ -949,7 +948,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { } case ServerOP_Uptime: { if (pack->size != sizeof(ServerUptime_Struct)) { - Log(Logs::Detail, Logs::World_Server, "Wrong size on ServerOP_Uptime. Got: %d, Expected: %d", pack->size, sizeof(ServerUptime_Struct)); + LogInfo("Wrong size on ServerOP_Uptime. Got: [{}], Expected: [{}]", pack->size, sizeof(ServerUptime_Struct)); break; } ServerUptime_Struct* sus = (ServerUptime_Struct*)pack->pBuffer; @@ -968,7 +967,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; } case ServerOP_GetWorldTime: { - Log(Logs::Detail, Logs::World_Server, "Broadcasting a world time update"); + LogInfo("Broadcasting a world time update"); auto pack = new ServerPacket; pack->opcode = ServerOP_SyncWorldTime; @@ -987,17 +986,17 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; } case ServerOP_SetWorldTime: { - Log(Logs::Detail, Logs::World_Server, "Received SetWorldTime"); + LogNetcode("Received SetWorldTime"); eqTimeOfDay* newtime = (eqTimeOfDay*)pack->pBuffer; zoneserver_list.worldclock.SetCurrentEQTimeOfDay(newtime->start_eqtime, newtime->start_realtime); - Log(Logs::Detail, Logs::World_Server, "New time = %d-%d-%d %d:%d (%d)\n", newtime->start_eqtime.year, newtime->start_eqtime.month, (int)newtime->start_eqtime.day, (int)newtime->start_eqtime.hour, (int)newtime->start_eqtime.minute, (int)newtime->start_realtime); + LogInfo("New time = [{}]-[{}]-[{}] [{}]:[{}] ([{}])\n", newtime->start_eqtime.year, newtime->start_eqtime.month, (int)newtime->start_eqtime.day, (int)newtime->start_eqtime.hour, (int)newtime->start_eqtime.minute, (int)newtime->start_realtime); database.SaveTime((int)newtime->start_eqtime.minute, (int)newtime->start_eqtime.hour, (int)newtime->start_eqtime.day, newtime->start_eqtime.month, newtime->start_eqtime.year); zoneserver_list.SendTimeSync(); break; } case ServerOP_IPLookup: { if (pack->size < sizeof(ServerGenericWorldQuery_Struct)) { - Log(Logs::Detail, Logs::World_Server, "Wrong size on ServerOP_IPLookup. Got: %d, Expected (at least): %d", pack->size, sizeof(ServerGenericWorldQuery_Struct)); + LogInfo("Wrong size on ServerOP_IPLookup. Got: [{}], Expected (at least): [{}]", pack->size, sizeof(ServerGenericWorldQuery_Struct)); break; } ServerGenericWorldQuery_Struct* sgwq = (ServerGenericWorldQuery_Struct*)pack->pBuffer; @@ -1009,7 +1008,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { } case ServerOP_LockZone: { if (pack->size < sizeof(ServerLockZone_Struct)) { - Log(Logs::Detail, Logs::World_Server, "Wrong size on ServerOP_LockZone. Got: %d, Expected: %d", pack->size, sizeof(ServerLockZone_Struct)); + LogInfo("Wrong size on ServerOP_LockZone. Got: [{}], Expected: [{}]", pack->size, sizeof(ServerLockZone_Struct)); break; } ServerLockZone_Struct* s = (ServerLockZone_Struct*)pack->pBuffer; @@ -1087,7 +1086,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { zs->SendPacket(pack); } else { - Log(Logs::Detail, Logs::World_Server, "Unable to locate zone record for instance id %u in zoneserver list for ServerOP_Consent_Response operation.", s->instance_id); + LogInfo("Unable to locate zone record for instance id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->instance_id); } safe_delete(pack); } @@ -1112,7 +1111,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { zs->SendPacket(pack); } else { - Log(Logs::Detail, Logs::World_Server, "Unable to locate zone record for zone id %u in zoneserver list for ServerOP_Consent_Response operation.", s->zone_id); + LogInfo("Unable to locate zone record for zone id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id); } safe_delete(pack); } @@ -1132,7 +1131,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { zs->SendPacket(pack); } else { - Log(Logs::Detail, Logs::World_Server, "Unable to locate zone record for zone id %u in zoneserver list for ServerOP_Consent_Response operation.", s->zone_id); + LogInfo("Unable to locate zone record for zone id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id); } safe_delete(pack); } @@ -1151,7 +1150,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { zs->SendPacket(pack); } else { - Log(Logs::Detail, Logs::World_Server, "Unable to locate zone record for instance id %u in zoneserver list for ServerOP_Consent_Response operation.", s->instance_id); + LogInfo("Unable to locate zone record for instance id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->instance_id); } } else @@ -1161,7 +1160,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { zs->SendPacket(pack); } else { - Log(Logs::Detail, Logs::World_Server, "Unable to locate zone record for zone id %u in zoneserver list for ServerOP_Consent_Response operation.", s->zone_id); + LogInfo("Unable to locate zone record for zone id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id); } } break; @@ -1263,7 +1262,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_LSAccountUpdate: { - Log(Logs::Detail, Logs::World_Server, "Received ServerOP_LSAccountUpdate packet from zone"); + LogNetcode("Received ServerOP_LSAccountUpdate packet from zone"); loginserverlist.SendAccountUpdate(pack); break; } @@ -1326,14 +1325,14 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_ChangeSharedMem: { std::string hotfix_name = std::string((char*)pack->pBuffer); - Log(Logs::General, Logs::World_Server, "Loading items..."); + LogInfo("Loading items"); if (!database.LoadItems(hotfix_name)) { - Log(Logs::General, Logs::World_Server, "Error: Could not load item data. But ignoring"); + LogInfo("Error: Could not load item data. But ignoring"); } - Log(Logs::General, Logs::World_Server, "Loading skill caps..."); + LogInfo("Loading skill caps"); if (!database.LoadSkillCaps(hotfix_name)) { - Log(Logs::General, Logs::World_Server, "Error: Could not load skill cap data. But ignoring"); + LogInfo("Error: Could not load skill cap data. But ignoring"); } zoneserver_list.SendPacket(pack); @@ -1352,7 +1351,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { } default: { - Log(Logs::Detail, Logs::World_Server, "Unknown ServerOPcode from zone 0x%04x, size %d", pack->opcode, pack->size); + LogInfo("Unknown ServerOPcode from zone {:#04x}, size [{}]", pack->opcode, pack->size); DumpPacket(pack->pBuffer, pack->size); break; } diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 865d1dc8c..f4632b9dc 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -250,10 +250,6 @@ SET(zone_headers zonedb.h zonedump.h) -IF(EQEMU_DEPOP_INVALIDATES_CACHE) - ADD_DEFINITIONS(-DDEPOP_INVALIDATES_NPC_TYPES_CACHE) -ENDIF(EQEMU_DEPOP_INVALIDATES_CACHE) - ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers}) INSTALL(TARGETS zone RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) diff --git a/zone/aa.cpp b/zone/aa.cpp index 1712dd7a0..b4977a5b9 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -57,7 +57,7 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u PetRecord record; if (!database.GetPoweredPetEntry(spells[spell_id].teleport_zone, act_power, &record)) { - Log(Logs::General, Logs::Error, "Unknown swarm pet spell id: %d, check pets table", spell_id); + LogError("Unknown swarm pet spell id: {}, check pets table", spell_id); Message(Chat::Red, "Unable to find data for pet %s", spells[spell_id].teleport_zone); return; } @@ -84,7 +84,7 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u const NPCType *npc_type = database.LoadNPCTypesData(pet.npc_id); if (npc_type == nullptr) { //log write - Log(Logs::General, Logs::Error, "Unknown npc type for swarm pet spell id: %d", spell_id); + LogError("Unknown npc type for swarm pet spell id: [{}]", spell_id); Message(0, "Unable to find pet!"); return; } @@ -183,7 +183,7 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid const NPCType *npc_type = database.LoadNPCTypesData(typesid); if(npc_type == nullptr) { //log write - Log(Logs::General, Logs::Error, "Unknown npc type for swarm pet type id: %d", typesid); + LogError("Unknown npc type for swarm pet type id: [{}]", typesid); Message(0,"Unable to find pet!"); return; } @@ -1562,17 +1562,17 @@ bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price, } void Zone::LoadAlternateAdvancement() { - Log(Logs::General, Logs::Status, "Loading Alternate Advancement Data..."); + LogInfo("Loading Alternate Advancement Data"); if(!database.LoadAlternateAdvancementAbilities(aa_abilities, aa_ranks)) { aa_abilities.clear(); aa_ranks.clear(); - Log(Logs::General, Logs::Status, "Failed to load Alternate Advancement Data"); + LogInfo("Failed to load Alternate Advancement Data"); return; } - Log(Logs::General, Logs::Status, "Processing Alternate Advancement Data..."); + LogInfo("Processing Alternate Advancement Data"); for(const auto &ability : aa_abilities) { ability.second->first = GetAlternateAdvancementRank(ability.second->first_rank_id); @@ -1623,13 +1623,13 @@ void Zone::LoadAlternateAdvancement() { } } - Log(Logs::General, Logs::Status, "Loaded Alternate Advancement Data"); + LogInfo("Loaded Alternate Advancement Data"); } bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_map> &abilities, std::unordered_map> &ranks) { - Log(Logs::General, Logs::Status, "Loading Alternate Advancement Abilities..."); + LogInfo("Loading Alternate Advancement Abilities"); abilities.clear(); std::string query = "SELECT id, name, category, classes, races, deities, drakkin_heritage, status, type, charges, " "grant_only, first_rank_id FROM aa_ability WHERE enabled = 1"; @@ -1655,13 +1655,13 @@ bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_mapid] = std::unique_ptr(ability); } } else { - Log(Logs::General, Logs::Error, "Failed to load Alternate Advancement Abilities"); + LogError("Failed to load Alternate Advancement Abilities"); return false; } - Log(Logs::General, Logs::Status, "Loaded %d Alternate Advancement Abilities", (int)abilities.size()); + LogInfo("Loaded [{}] Alternate Advancement Abilities", (int)abilities.size()); - Log(Logs::General, Logs::Status, "Loading Alternate Advancement Ability Ranks..."); + LogInfo("Loading Alternate Advancement Ability Ranks"); ranks.clear(); query = "SELECT id, upper_hotkey_sid, lower_hotkey_sid, title_sid, desc_sid, cost, level_req, spell, spell_type, recast_time, " "next_id, expansion FROM aa_ranks"; @@ -1690,13 +1690,13 @@ bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_mapid] = std::unique_ptr(rank); } } else { - Log(Logs::General, Logs::Error, "Failed to load Alternate Advancement Ability Ranks"); + LogError("Failed to load Alternate Advancement Ability Ranks"); return false; } - Log(Logs::General, Logs::Status, "Loaded %d Alternate Advancement Ability Ranks", (int)ranks.size()); + LogInfo("Loaded [{}] Alternate Advancement Ability Ranks", (int)ranks.size()); - Log(Logs::General, Logs::Status, "Loading Alternate Advancement Ability Rank Effects..."); + LogInfo("Loading Alternate Advancement Ability Rank Effects"); query = "SELECT rank_id, slot, effect_id, base1, base2 FROM aa_rank_effects"; results = QueryDatabase(query); if(results.Success()) { @@ -1717,13 +1717,13 @@ bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_mapGetName()); + LogAggro("[{}] is in combat, and does not have prox_aggro, or does and is out of combat range with [{}]", GetName(), mob->GetName()); return false; } @@ -356,7 +354,7 @@ bool Mob::CheckWillAggro(Mob *mob) { { //FatherNiwtit: make sure we can see them. last since it is very expensive if(CheckLosFN(mob)) { - Log(Logs::Detail, Logs::Aggro, "Check aggro for %s target %s.", GetName(), mob->GetName()); + LogAggro("Check aggro for [{}] target [{}]", GetName(), mob->GetName()); return( mod_will_aggro(mob, this) ); } } @@ -388,18 +386,18 @@ bool Mob::CheckWillAggro(Mob *mob) { { //FatherNiwtit: make sure we can see them. last since it is very expensive if(CheckLosFN(mob)) { - Log(Logs::Detail, Logs::Aggro, "Check aggro for %s target %s.", GetName(), mob->GetName()); + LogAggro("Check aggro for [{}] target [{}]", GetName(), mob->GetName()); return( mod_will_aggro(mob, this) ); } } } - Log(Logs::Detail, Logs::Aggro, "Is In zone?:%d\n", mob->InZone()); - Log(Logs::Detail, Logs::Aggro, "Dist^2: %f\n", dist2); - Log(Logs::Detail, Logs::Aggro, "Range^2: %f\n", iAggroRange2); - Log(Logs::Detail, Logs::Aggro, "Faction: %d\n", fv); - Log(Logs::Detail, Logs::Aggro, "Int: %d\n", GetINT()); - Log(Logs::Detail, Logs::Aggro, "Con: %d\n", GetLevelCon(mob->GetLevel())); + LogAggro("Is In zone?:[{}]\n", mob->InZone()); + LogAggro("Dist^2: [{}]\n", dist2); + LogAggro("Range^2: [{}]\n", iAggroRange2); + LogAggro("Faction: [{}]\n", fv); + LogAggro("Int: [{}]\n", GetINT()); + LogAggro("Con: [{}]\n", GetLevelCon(mob->GetLevel())); return(false); } @@ -519,7 +517,7 @@ void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { //Father Nitwit: make sure we can see them. if(mob->CheckLosFN(sender)) { #if (EQDEBUG>=5) - Log(Logs::General, Logs::None, "AIYellForHelp(\"%s\",\"%s\") %s attacking %s Dist %f Z %f", + LogDebug("AIYellForHelp(\"[{}]\",\"[{}]\") [{}] attacking [{}] Dist [{}] Z [{}]", sender->GetName(), attacker->GetName(), mob->GetName(), attacker->GetName(), DistanceSquared(mob->GetPosition(), sender->GetPosition()), std::abs(sender->GetZ()+mob->GetZ())); @@ -750,7 +748,7 @@ type', in which case, the answer is yes. } while( reverse++ == 0 ); - Log(Logs::General, Logs::None, "Mob::IsAttackAllowed: don't have a rule for this - %s vs %s\n", this->GetName(), target->GetName()); + LogDebug("Mob::IsAttackAllowed: don't have a rule for this - [{}] vs [{}]\n", this->GetName(), target->GetName()); return false; } @@ -890,7 +888,7 @@ bool Mob::IsBeneficialAllowed(Mob *target) } while( reverse++ == 0 ); - Log(Logs::General, Logs::None, "Mob::IsBeneficialAllowed: don't have a rule for this - %s to %s\n", this->GetName(), target->GetName()); + LogDebug("Mob::IsBeneficialAllowed: don't have a rule for this - [{}] to [{}]\n", this->GetName(), target->GetName()); return false; } @@ -1011,7 +1009,7 @@ bool Mob::CheckLosFN(float posX, float posY, float posZ, float mobSize) { oloc.z = posZ + (mobSize==0.0?LOS_DEFAULT_HEIGHT:mobSize)/2 * SEE_POSITION; #if LOSDEBUG>=5 - Log(Logs::General, Logs::None, "LOS from (%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f) sizes: (%.2f, %.2f)", myloc.x, myloc.y, myloc.z, oloc.x, oloc.y, oloc.z, GetSize(), mobSize); + LogDebug("LOS from ([{}], [{}], [{}]) to ([{}], [{}], [{}]) sizes: ([{}], [{}])", myloc.x, myloc.y, myloc.z, oloc.x, oloc.y, oloc.z, GetSize(), mobSize); #endif return zone->zonemap->CheckLoS(myloc, oloc); } @@ -1033,7 +1031,7 @@ bool Mob::CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarge posTarget.z += (sizeTarget == 0.0f ? LOS_DEFAULT_HEIGHT : sizeTarget) / 2 * SEE_POSITION; #if LOSDEBUG>=5 - Log(Logs::General, Logs::None, "LOS from (%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f) sizes: (%.2f, %.2f) [static]", posWatcher.x, posWatcher.y, posWatcher.z, posTarget.x, posTarget.y, posTarget.z, sizeWatcher, sizeTarget); + LogDebug("LOS from ([{}], [{}], [{}]) to ([{}], [{}], [{}]) sizes: ([{}], [{}]) [static]", posWatcher.x, posWatcher.y, posWatcher.z, posTarget.x, posTarget.y, posTarget.z, sizeWatcher, sizeTarget); #endif return zone->zonemap->CheckLoS(posWatcher, posTarget); } diff --git a/zone/api_service.cpp b/zone/api_service.cpp index b8ae1149e..b038da1a6 100644 --- a/zone/api_service.cpp +++ b/zone/api_service.cpp @@ -34,13 +34,25 @@ extern Zone *zone; -EQ::Net::WebsocketLoginStatus -CheckLogin(EQ::Net::WebsocketServerConnection *connection, const std::string &username, const std::string &password) +/** + * @param connection + * @param username + * @param password + * @return + */ +EQ::Net::WebsocketLoginStatus CheckLogin( + EQ::Net::WebsocketServerConnection *connection, + const std::string &username, + const std::string &password +) { EQ::Net::WebsocketLoginStatus ret; ret.logged_in = false; - ret.account_id = database.CheckLogin(username.c_str(), password.c_str()); + + std::string prefix = "eqemu"; + + ret.account_id = database.CheckLogin(username.c_str(), password.c_str(), prefix.c_str()); if (ret.account_id == 0) { return ret; @@ -54,6 +66,11 @@ CheckLogin(EQ::Net::WebsocketServerConnection *connection, const std::string &us return ret; } +/** + * @param connection + * @param params + * @return + */ Json::Value ApiGetPacketStatistics(EQ::Net::WebsocketServerConnection *connection, Json::Value params) { if (zone->GetZoneID() == 0) { @@ -803,7 +820,7 @@ Json::Value ApiGetZoneAttributes(EQ::Net::WebsocketServerConnection *connection, row["mobs_aggro_count"] = zone->MobsAggroCount(); row["save_zone_cfg"] = zone->SaveZoneCFG(); row["short_name"] = zone->GetShortName(); - row["total_blocked_spells"] = zone->GetTotalBlockedSpells(); + row["total_blocked_spells"] = zone->GetZoneTotalBlockedSpells(); row["zone_id"] = zone->GetZoneID(); row["zone_type"] = zone->GetZoneType(); @@ -847,7 +864,7 @@ Json::Value ApiSetLoggingLevel(EQ::Net::WebsocketServerConnection *connection, J response["status"] = "Category doesn't exist"; - Log(Logs::General, Logs::Status, "Logging category is %i and level is %i", + LogInfo("Logging category is [{}] and level is [{}]", logging_category, logging_level ); diff --git a/zone/attack.cpp b/zone/attack.cpp index 3dca5f8c3..b53b247b7 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -393,7 +393,7 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit) if (hit.hand != EQEmu::invslot::slotRange && (CanThisClassRiposte() || IsEnraged()) && InFront && !ImmuneRipo) { if (IsEnraged()) { hit.damage_done = DMG_RIPOSTED; - Log(Logs::Detail, Logs::Combat, "I am enraged, riposting frontal attack."); + LogCombat("I am enraged, riposting frontal attack"); return true; } if (IsClient()) @@ -850,10 +850,10 @@ int Mob::ACSum() auto over_cap = ac - softcap; ac = softcap + (over_cap * returns); } - Log(Logs::Detail, Logs::Combat, "ACSum ac %d softcap %d returns %f", ac, softcap, returns); + LogCombat("ACSum ac [{}] softcap [{}] returns [{}]", ac, softcap, returns); } else { - Log(Logs::Detail, Logs::Combat, "ACSum ac %d", ac); + LogCombat("ACSum ac [{}]", ac); } return ac; } @@ -1313,7 +1313,7 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts) { if (!other) return; - Log(Logs::Detail, Logs::Combat, "%s::DoAttack vs %s base %d min %d offense %d tohit %d skill %d", GetName(), + LogCombat("[{}]::DoAttack vs [{}] base [{}] min [{}] offense [{}] tohit [{}] skill [{}]", GetName(), other->GetName(), hit.base_damage, hit.min_damage, hit.offense, hit.tohit, hit.skill); // check to see if we hit.. @@ -1330,7 +1330,7 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts) //if (IsDead()) return; } - Log(Logs::Detail, Logs::Combat, "Avoided/strikethrough damage with code %d", hit.damage_done); + LogCombat("Avoided/strikethrough damage with code [{}]", hit.damage_done); } if (hit.damage_done >= 0) { @@ -1354,10 +1354,10 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts) ApplyDamageTable(hit); CommonOutgoingHitSuccess(other, hit, opts); } - Log(Logs::Detail, Logs::Combat, "Final damage after all reductions: %d", hit.damage_done); + LogCombat("Final damage after all reductions: [{}]", hit.damage_done); } else { - Log(Logs::Detail, Logs::Combat, "Attack missed. Damage set to 0."); + LogCombat("Attack missed. Damage set to 0"); hit.damage_done = 0; } } @@ -1370,14 +1370,14 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b { if (!other) { SetTarget(nullptr); - Log(Logs::General, Logs::Error, "A null Mob object was passed to Client::Attack() for evaluation!"); + LogError("A null Mob object was passed to Client::Attack() for evaluation!"); return false; } if (!GetTarget()) SetTarget(other); - Log(Logs::Detail, Logs::Combat, "Attacking %s with hand %d %s", other ? other->GetName() : "(nullptr)", Hand, bRiposte ? "(this is a riposte)" : ""); + LogCombat("Attacking [{}] with hand [{}] [{}]", other ? other->GetName() : "(nullptr)", Hand, bRiposte ? "(this is a riposte)" : ""); //SetAttackTimer(); if ( @@ -1387,13 +1387,13 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b || (GetHP() < 0) || (!IsAttackAllowed(other)) ) { - Log(Logs::Detail, Logs::Combat, "Attack canceled, invalid circumstances."); + LogCombat("Attack cancelled, invalid circumstances"); return false; // Only bards can attack while casting } if (DivineAura() && !GetGM()) {//cant attack while invulnerable unless your a gm - Log(Logs::Detail, Logs::Combat, "Attack canceled, Divine Aura is in effect."); - MessageString(Chat::DefaultText, DIVINE_AURA_NO_ATK); //You can't attack while invulnerable! + LogCombat("Attack cancelled, Divine Aura is in effect"); + MessageString(Chat::DefaultText, DIVINE_AURA_NO_ATK); //You can't attack while invulnerable return false; } @@ -1412,20 +1412,20 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b if (weapon != nullptr) { if (!weapon->IsWeapon()) { - Log(Logs::Detail, Logs::Combat, "Attack canceled, Item %s (%d) is not a weapon.", weapon->GetItem()->Name, weapon->GetID()); + LogCombat("Attack cancelled, Item [{}] ([{}]) is not a weapon", weapon->GetItem()->Name, weapon->GetID()); return(false); } - Log(Logs::Detail, Logs::Combat, "Attacking with weapon: %s (%d)", weapon->GetItem()->Name, weapon->GetID()); + LogCombat("Attacking with weapon: [{}] ([{}])", weapon->GetItem()->Name, weapon->GetID()); } else { - Log(Logs::Detail, Logs::Combat, "Attacking without a weapon."); + LogCombat("Attacking without a weapon"); } DamageHitInfo my_hit; // calculate attack_skill and skillinuse depending on hand and weapon // also send Packet to near clients my_hit.skill = AttackAnimation(Hand, weapon); - Log(Logs::Detail, Logs::Combat, "Attacking with %s in slot %d using skill %d", weapon ? weapon->GetItem()->Name : "Fist", Hand, my_hit.skill); + LogCombat("Attacking with [{}] in slot [{}] using skill [{}]", weapon ? weapon->GetItem()->Name : "Fist", Hand, my_hit.skill); // Now figure out damage my_hit.damage_done = 1; @@ -1493,7 +1493,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b // damage = mod_client_damage(damage, skillinuse, Hand, weapon, other); - Log(Logs::Detail, Logs::Combat, "Damage calculated: base %d min damage %d skill %d", my_hit.base_damage, my_hit.min_damage, my_hit.skill); + LogCombat("Damage calculated: base [{}] min damage [{}] skill [{}]", my_hit.base_damage, my_hit.min_damage, my_hit.skill); int hit_chance_bonus = 0; my_hit.offense = offense(my_hit.skill); // we need this a few times @@ -1629,7 +1629,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQEmu::skills::Sk } int exploss = 0; - Log(Logs::Detail, Logs::Combat, "Fatal blow dealt by %s with %d damage, spell %d, skill %d", killerMob ? killerMob->GetName() : "Unknown", damage, spell, attack_skill); + LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", killerMob ? killerMob->GetName() : "Unknown", damage, spell, attack_skill); /* #1: Send death packet to everyone @@ -1902,7 +1902,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool { if (!other) { SetTarget(nullptr); - Log(Logs::General, Logs::Error, "A null Mob object was passed to NPC::Attack() for evaluation!"); + LogError("A null Mob object was passed to NPC::Attack() for evaluation!"); return false; } @@ -1920,7 +1920,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool if (other->IsClient()) other->CastToClient()->RemoveXTarget(this, false); RemoveFromHateList(other); - Log(Logs::Detail, Logs::Combat, "I am not allowed to attack %s", other->GetName()); + LogCombat("I am not allowed to attack [{}]", other->GetName()); } return false; } @@ -1950,10 +1950,10 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool //We dont factor much from the weapon into the attack. //Just the skill type so it doesn't look silly using punching animations and stuff while wielding weapons if (weapon) { - Log(Logs::Detail, Logs::Combat, "Attacking with weapon: %s (%d) (too bad im not using it for much)", weapon->Name, weapon->ID); + LogCombat("Attacking with weapon: [{}] ([{}]) (too bad im not using it for much)", weapon->Name, weapon->ID); - if (Hand == EQEmu::invslot::slotSecondary && weapon->ItemType == EQEmu::item::ItemTypeShield) { - Log(Logs::Detail, Logs::Combat, "Attack with shield canceled."); + if (Hand == EQEmu::invslot::slotSecondary && !weapon->IsType1HWeapon()) { + LogCombat("Attack with non-weapon cancelled"); return false; } @@ -2055,7 +2055,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool other->AddToHateList(this, hate); - Log(Logs::Detail, Logs::Combat, "Final damage against %s: %d", other->GetName(), my_hit.damage_done); + LogCombat("Final damage against [{}]: [{}]", other->GetName(), my_hit.damage_done); if (other->IsClient() && IsPet() && GetOwner()->IsClient()) { //pets do half damage to clients in pvp @@ -2112,7 +2112,7 @@ void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, EQEmu::skills::Skill //handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds if (attacked_timer.Check()) { - Log(Logs::Detail, Logs::Combat, "Triggering EVENT_ATTACK due to attack by %s", other ? other->GetName() : "nullptr"); + LogCombat("Triggering EVENT_ATTACK due to attack by [{}]", other ? other->GetName() : "nullptr"); parse->EventNPC(EVENT_ATTACK, this, other, "", 0); } attacked_timer.Start(CombatEventTimer_expire); @@ -2150,7 +2150,7 @@ void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, EQEmu::skills::Skill bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::SkillType attack_skill) { - Log(Logs::Detail, Logs::Combat, "Fatal blow dealt by %s with %d damage, spell %d, skill %d", + LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", ((killer_mob) ? (killer_mob->GetName()) : ("[nullptr]")), damage, spell, attack_skill); Mob *oos = nullptr; @@ -2798,7 +2798,7 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) { if (DS == 0 && rev_ds == 0) return; - Log(Logs::Detail, Logs::Combat, "Applying Damage Shield of value %d to %s", DS, attacker->GetName()); + LogCombat("Applying Damage Shield of value [{}] to [{}]", DS, attacker->GetName()); //invert DS... spells yield negative values for a true damage shield if (DS < 0) { @@ -2844,7 +2844,7 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) { rev_ds_spell_id = spellbonuses.ReverseDamageShieldSpellID; if (rev_ds < 0) { - Log(Logs::Detail, Logs::Combat, "Applying Reverse Damage Shield of value %d to %s", rev_ds, attacker->GetName()); + LogCombat("Applying Reverse Damage Shield of value [{}] to [{}]", rev_ds, attacker->GetName()); attacker->Damage(this, -rev_ds, rev_ds_spell_id, EQEmu::skills::SkillAbjuration/*hackish*/, false); //"this" (us) will get the hate, etc. not sure how this works on Live, but it'll works for now, and tanks will love us for this //do we need to send a damage packet here also? } @@ -3054,16 +3054,14 @@ int32 Mob::ReduceDamage(int32 damage) int damage_to_reduce = damage * spellbonuses.MeleeThresholdGuard[0] / 100; if (damage_to_reduce >= buffs[slot].melee_rune) { - Log(Logs::Detail, Logs::Spells, "Mob::ReduceDamage SE_MeleeThresholdGuard %d damage negated, %d" - " damage remaining, fading buff.", damage_to_reduce, buffs[slot].melee_rune); + LogSpells("Mob::ReduceDamage SE_MeleeThresholdGuard [{}] damage negated, [{}] damage remaining, fading buff", damage_to_reduce, buffs[slot].melee_rune); damage -= buffs[slot].melee_rune; if (!TryFadeEffect(slot)) BuffFadeBySlot(slot); } else { - Log(Logs::Detail, Logs::Spells, "Mob::ReduceDamage SE_MeleeThresholdGuard %d damage negated, %d" - " damage remaining.", damage_to_reduce, buffs[slot].melee_rune); + LogSpells("Mob::ReduceDamage SE_MeleeThresholdGuard [{}] damage negated, [{}] damage remaining", damage_to_reduce, buffs[slot].melee_rune); buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce); damage -= damage_to_reduce; } @@ -3081,16 +3079,14 @@ int32 Mob::ReduceDamage(int32 damage) if (spellbonuses.MitigateMeleeRune[3] && (damage_to_reduce >= buffs[slot].melee_rune)) { - Log(Logs::Detail, Logs::Spells, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" - " damage remaining, fading buff.", damage_to_reduce, buffs[slot].melee_rune); + LogSpells("Mob::ReduceDamage SE_MitigateMeleeDamage [{}] damage negated, [{}] damage remaining, fading buff", damage_to_reduce, buffs[slot].melee_rune); damage -= buffs[slot].melee_rune; if (!TryFadeEffect(slot)) BuffFadeBySlot(slot); } else { - Log(Logs::Detail, Logs::Spells, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" - " damage remaining.", damage_to_reduce, buffs[slot].melee_rune); + LogSpells("Mob::ReduceDamage SE_MitigateMeleeDamage [{}] damage negated, [{}] damage remaining", damage_to_reduce, buffs[slot].melee_rune); if (spellbonuses.MitigateMeleeRune[3]) buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce); @@ -3207,16 +3203,14 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi if (spellbonuses.MitigateSpellRune[3] && (damage_to_reduce >= buffs[slot].magic_rune)) { - Log(Logs::Detail, Logs::Spells, "Mob::ReduceDamage SE_MitigateSpellDamage %d damage negated, %d" - " damage remaining, fading buff.", damage_to_reduce, buffs[slot].magic_rune); + LogSpells("Mob::ReduceDamage SE_MitigateSpellDamage [{}] damage negated, [{}] damage remaining, fading buff", damage_to_reduce, buffs[slot].magic_rune); damage -= buffs[slot].magic_rune; if (!TryFadeEffect(slot)) BuffFadeBySlot(slot); } else { - Log(Logs::Detail, Logs::Spells, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" - " damage remaining.", damage_to_reduce, buffs[slot].magic_rune); + LogSpells("Mob::ReduceDamage SE_MitigateMeleeDamage [{}] damage negated, [{}] damage remaining", damage_to_reduce, buffs[slot].magic_rune); if (spellbonuses.MitigateSpellRune[3]) buffs[slot].magic_rune = (buffs[slot].magic_rune - damage_to_reduce); @@ -3371,13 +3365,13 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const ignore_invul = spell_id == 982 || spells[spell_id].cast_not_standing; // cazic touch if (!ignore_invul && (GetInvul() || DivineAura())) { - Log(Logs::Detail, Logs::Combat, "Avoiding %d damage due to invulnerability.", damage); + LogCombat("Avoiding [{}] damage due to invulnerability", damage); damage = DMG_INVULNERABLE; } // this should actually happen MUCH sooner, need to investigate though -- good enough for now if ((skill_used == EQEmu::skills::SkillArchery || skill_used == EQEmu::skills::SkillThrowing) && GetSpecialAbility(IMMUNE_RANGED_ATTACKS)) { - Log(Logs::Detail, Logs::Combat, "Avoiding %d damage due to IMMUNE_RANGED_ATTACKS.", damage); + LogCombat("Avoiding [{}] damage due to IMMUNE_RANGED_ATTACKS", damage); damage = DMG_INVULNERABLE; } @@ -3421,7 +3415,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const int healed = damage; healed = attacker->GetActSpellHealing(spell_id, healed); - Log(Logs::Detail, Logs::Combat, "Applying lifetap heal of %d to %s", healed, attacker->GetName()); + LogCombat("Applying lifetap heal of [{}] to [{}]", healed, attacker->GetName()); attacker->HealDamage(healed); //we used to do a message to the client, but its gone now. @@ -3437,7 +3431,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const if (pet && !pet->IsFamiliar() && !pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse() && !pet->IsGHeld() && !attacker->IsTrap()) { if (!pet->IsHeld()) { - Log(Logs::Detail, Logs::Aggro, "Sending pet %s into battle due to attack.", pet->GetName()); + LogAggro("Sending pet [{}] into battle due to attack", pet->GetName()); pet->AddToHateList(attacker, 1, 0, true, false, false, spell_id); pet->SetTarget(attacker); MessageString(Chat::NPCQuestSay, PET_ATTACKING, pet->GetCleanName(), attacker->GetCleanName()); @@ -3447,7 +3441,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const //see if any runes want to reduce this damage if (spell_id == SPELL_UNKNOWN) { damage = ReduceDamage(damage); - Log(Logs::Detail, Logs::Combat, "Melee Damage reduced to %d", damage); + LogCombat("Melee Damage reduced to [{}]", damage); damage = ReduceAllDamage(damage); TryTriggerThreshHold(damage, SE_TriggerMeleeThreshold, attacker); @@ -3507,7 +3501,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const //fade mez if we are mezzed if (IsMezzed() && attacker) { - Log(Logs::Detail, Logs::Combat, "Breaking mez due to attack."); + LogCombat("Breaking mez due to attack"); entity_list.MessageCloseString( this, /* Sender */ true, /* Skip Sender */ @@ -3594,7 +3588,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const //increment chances of interrupting if (IsCasting()) { //shouldnt interrupt on regular spell damage attacked_count++; - Log(Logs::Detail, Logs::Combat, "Melee attack while casting. Attack count %d", attacked_count); + LogCombat("Melee attack while casting. Attack count [{}]", attacked_count); } } @@ -3863,7 +3857,7 @@ float Mob::GetProcChances(float ProcBonus, uint16 hand) ProcChance += ProcChance * ProcBonus / 100.0f; } - Log(Logs::Detail, Logs::Combat, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); + LogCombat("Proc chance [{}] ([{}] from bonuses)", ProcChance, ProcBonus); return ProcChance; } @@ -3882,7 +3876,7 @@ float Mob::GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 h ProcBonus += static_cast(myagi) * RuleR(Combat, DefProcPerMinAgiContrib) / 100.0f; ProcChance = ProcChance + (ProcChance * ProcBonus); - Log(Logs::Detail, Logs::Combat, "Defensive Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); + LogCombat("Defensive Proc chance [{}] ([{}] from bonuses)", ProcChance, ProcBonus); return ProcChance; } @@ -3891,7 +3885,7 @@ void Mob::TryDefensiveProc(Mob *on, uint16 hand) { if (!on) { SetTarget(nullptr); - Log(Logs::General, Logs::Error, "A null Mob object was passed to Mob::TryDefensiveProc for evaluation!"); + LogError("A null Mob object was passed to Mob::TryDefensiveProc for evaluation!"); return; } @@ -3931,17 +3925,17 @@ void Mob::TryDefensiveProc(Mob *on, uint16 hand) { void Mob::TryWeaponProc(const EQEmu::ItemInstance* weapon_g, Mob *on, uint16 hand) { if (!on) { SetTarget(nullptr); - Log(Logs::General, Logs::Error, "A null Mob object was passed to Mob::TryWeaponProc for evaluation!"); + LogError("A null Mob object was passed to Mob::TryWeaponProc for evaluation!"); return; } if (!IsAttackAllowed(on)) { - Log(Logs::Detail, Logs::Combat, "Preventing procing off of unattackable things."); + LogCombat("Preventing procing off of unattackable things"); return; } if (DivineAura()) { - Log(Logs::Detail, Logs::Combat, "Procs canceled, Divine Aura is in effect."); + LogCombat("Procs cancelled, Divine Aura is in effect"); return; } @@ -3988,8 +3982,7 @@ void Mob::TryWeaponProc(const EQEmu::ItemInstance *inst, const EQEmu::ItemData * static_cast(weapon->ProcRate)) / 100.0f; if (zone->random.Roll(WPC)) { // 255 dex = 0.084 chance of proc. No idea what this number should be really. if (weapon->Proc.Level2 > ourlevel) { - Log(Logs::Detail, Logs::Combat, - "Tried to proc (%s), but our level (%d) is lower than required (%d)", + LogCombat("Tried to proc ([{}]), but our level ([{}]) is lower than required ([{}])", weapon->Name, ourlevel, weapon->Proc.Level2); if (IsPet()) { Mob *own = GetOwner(); @@ -4001,9 +3994,7 @@ void Mob::TryWeaponProc(const EQEmu::ItemInstance *inst, const EQEmu::ItemData * } } else { - Log(Logs::Detail, Logs::Combat, - "Attacking weapon (%s) successfully procing spell %d (%.2f percent chance)", - weapon->Name, weapon->Proc.Effect, WPC * 100); + LogCombat("Attacking weapon ([{}]) successfully procing spell [{}] ([{}] percent chance)", weapon->Name, weapon->Proc.Effect, WPC * 100); ExecWeaponProc(inst, weapon->Proc.Effect, on); proced = true; } @@ -4083,15 +4074,11 @@ void Mob::TrySpellProc(const EQEmu::ItemInstance *inst, const EQEmu::ItemData *w // Perma procs (AAs) if (PermaProcs[i].spellID != SPELL_UNKNOWN) { if (zone->random.Roll(PermaProcs[i].chance)) { // TODO: Do these get spell bonus? - Log(Logs::Detail, Logs::Combat, - "Permanent proc %d procing spell %d (%d percent chance)", - i, PermaProcs[i].spellID, PermaProcs[i].chance); + LogCombat("Permanent proc [{}] procing spell [{}] ([{}] percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance); ExecWeaponProc(nullptr, PermaProcs[i].spellID, on); } else { - Log(Logs::Detail, Logs::Combat, - "Permanent proc %d failed to proc %d (%d percent chance)", - i, PermaProcs[i].spellID, PermaProcs[i].chance); + LogCombat("Permanent proc [{}] failed to proc [{}] ([{}] percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance); } } @@ -4099,18 +4086,14 @@ void Mob::TrySpellProc(const EQEmu::ItemInstance *inst, const EQEmu::ItemData *w if (SpellProcs[i].spellID != SPELL_UNKNOWN) { float chance = ProcChance * (static_cast(SpellProcs[i].chance) / 100.0f); if (zone->random.Roll(chance)) { - Log(Logs::Detail, Logs::Combat, - "Spell proc %d procing spell %d (%.2f percent chance)", - i, SpellProcs[i].spellID, chance); + LogCombat("Spell proc [{}] procing spell [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance); SendBeginCast(SpellProcs[i].spellID, 0); ExecWeaponProc(nullptr, SpellProcs[i].spellID, on, SpellProcs[i].level_override); CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, SpellProcs[i].base_spellID); } else { - Log(Logs::Detail, Logs::Combat, - "Spell proc %d failed to proc %d (%.2f percent chance)", - i, SpellProcs[i].spellID, chance); + LogCombat("Spell proc [{}] failed to proc [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance); } } } @@ -4119,17 +4102,13 @@ void Mob::TrySpellProc(const EQEmu::ItemInstance *inst, const EQEmu::ItemData *w if (RangedProcs[i].spellID != SPELL_UNKNOWN) { float chance = ProcChance * (static_cast(RangedProcs[i].chance) / 100.0f); if (zone->random.Roll(chance)) { - Log(Logs::Detail, Logs::Combat, - "Ranged proc %d procing spell %d (%.2f percent chance)", - i, RangedProcs[i].spellID, chance); + LogCombat("Ranged proc [{}] procing spell [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance); ExecWeaponProc(nullptr, RangedProcs[i].spellID, on); CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, RangedProcs[i].base_spellID); } else { - Log(Logs::Detail, Logs::Combat, - "Ranged proc %d failed to proc %d (%.2f percent chance)", - i, RangedProcs[i].spellID, chance); + LogCombat("Ranged proc [{}] failed to proc [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance); } } } @@ -4324,9 +4303,7 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions * } hit.damage_done = hit.damage_done * crit_mod / 100; - Log(Logs::Detail, Logs::Combat, - "Crit success roll %d dex chance %d og dmg %d crit_mod %d new dmg %d", roll, dex_bonus, - og_damage, crit_mod, hit.damage_done); + LogCombat("Crit success roll [{}] dex chance [{}] og dmg [{}] crit_mod [{}] new dmg [{}]", roll, dex_bonus, og_damage, crit_mod, hit.damage_done); // step 3: check deadly strike if (GetClass() == ROGUE && hit.skill == EQEmu::skills::SkillThrowing) { @@ -4367,7 +4344,7 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions * if (IsBerserk() || berserk) { hit.damage_done += og_damage * 119 / 100; - Log(Logs::Detail, Logs::Combat, "Crip damage %d", hit.damage_done); + LogCombat("Crip damage [{}]", hit.damage_done); entity_list.FilteredMessageCloseString( this, /* Sender */ @@ -4447,7 +4424,7 @@ bool Mob::TryFinishingBlow(Mob *defender, int &damage) void Mob::DoRiposte(Mob *defender) { - Log(Logs::Detail, Logs::Combat, "Preforming a riposte"); + LogCombat("Preforming a riposte"); if (!defender) return; @@ -4467,8 +4444,7 @@ void Mob::DoRiposte(Mob *defender) defender->itembonuses.DoubleRiposte; if (DoubleRipChance && zone->random.Roll(DoubleRipChance)) { - Log(Logs::Detail, Logs::Combat, - "Preforming a double riposted from SE_DoubleRiposte (%d percent chance)", DoubleRipChance); + LogCombat("Preforming a double riposted from SE_DoubleRiposte ([{}] percent chance)", DoubleRipChance); defender->Attack(this, EQEmu::invslot::slotPrimary, true); if (HasDied()) return; @@ -4479,9 +4455,7 @@ void Mob::DoRiposte(Mob *defender) // Live AA - Double Riposte if (DoubleRipChance && zone->random.Roll(DoubleRipChance)) { - Log(Logs::Detail, Logs::Combat, - "Preforming a double riposted from SE_GiveDoubleRiposte base1 == 0 (%d percent chance)", - DoubleRipChance); + LogCombat("Preforming a double riposted from SE_GiveDoubleRiposte base1 == 0 ([{}] percent chance)", DoubleRipChance); defender->Attack(this, EQEmu::invslot::slotPrimary, true); if (HasDied()) return; @@ -4493,8 +4467,7 @@ void Mob::DoRiposte(Mob *defender) DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[1]; if (DoubleRipChance && zone->random.Roll(DoubleRipChance)) { - Log(Logs::Detail, Logs::Combat, "Preforming a return SPECIAL ATTACK (%d percent chance)", - DoubleRipChance); + LogCombat("Preforming a return SPECIAL ATTACK ([{}] percent chance)", DoubleRipChance); if (defender->GetClass() == MONK) defender->MonkSpecialAttack(this, defender->aabonuses.GiveDoubleRiposte[2]); @@ -4717,7 +4690,7 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui if (!on) { SetTarget(nullptr); - Log(Logs::General, Logs::Error, "A null Mob object was passed to Mob::TrySkillProc for evaluation!"); + LogError("A null Mob object was passed to Mob::TrySkillProc for evaluation!"); return; } @@ -4948,13 +4921,13 @@ bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) { if (!TryFadeEffect(spellbonuses.Root[1])) { BuffFadeBySlot(spellbonuses.Root[1]); - Log(Logs::Detail, Logs::Combat, "Spell broke root! BreakChance percent chance"); + LogCombat("Spell broke root! BreakChance percent chance"); return true; } } } - Log(Logs::Detail, Logs::Combat, "Spell did not break root. BreakChance percent chance"); + LogCombat("Spell did not break root. BreakChance percent chance"); return false; } @@ -5130,19 +5103,19 @@ void Mob::CommonBreakInvisibleFromCombat() { //break invis when you attack if (invisible) { - Log(Logs::Detail, Logs::Combat, "Removing invisibility due to melee attack."); + LogCombat("Removing invisibility due to melee attack"); BuffFadeByEffect(SE_Invisibility); BuffFadeByEffect(SE_Invisibility2); invisible = false; } if (invisible_undead) { - Log(Logs::Detail, Logs::Combat, "Removing invisibility vs. undead due to melee attack."); + LogCombat("Removing invisibility vs. undead due to melee attack"); BuffFadeByEffect(SE_InvisVsUndead); BuffFadeByEffect(SE_InvisVsUndead2); invisible_undead = false; } if (invisible_animals) { - Log(Logs::Detail, Logs::Combat, "Removing invisibility vs. animals due to melee attack."); + LogCombat("Removing invisibility vs. animals due to melee attack"); BuffFadeByEffect(SE_InvisVsAnimals); invisible_animals = false; } diff --git a/zone/aura.cpp b/zone/aura.cpp index adedb3f56..0830816c1 100644 --- a/zone/aura.cpp +++ b/zone/aura.cpp @@ -711,13 +711,13 @@ void Mob::MakeAura(uint16 spell_id) AuraRecord record; if (!database.GetAuraEntry(spell_id, record)) { Message(Chat::Red, "Unable to find data for aura %s", spells[spell_id].name); - Log(Logs::General, Logs::Error, "Unable to find data for aura %d, check auras table.", spell_id); + LogError("Unable to find data for aura [{}], check auras table", spell_id); return; } if (!IsValidSpell(record.spell_id)) { Message(Chat::Red, "Casted spell (%d) is not valid for aura %s", record.spell_id, spells[spell_id].name); - Log(Logs::General, Logs::Error, "Casted spell (%d) is not valid for aura %d, check auras table.", + LogError("Casted spell ([{}]) is not valid for aura [{}], check auras table", record.spell_id, spell_id); return; } @@ -745,9 +745,7 @@ void Mob::MakeAura(uint16 spell_id) const auto base = database.LoadNPCTypesData(record.npc_type); if (base == nullptr) { Message(Chat::Red, "Unable to load NPC data for aura %s", spells[spell_id].teleport_zone); - Log(Logs::General, Logs::Error, - "Unable to load NPC data for aura %s (NPC ID %d), check auras and npc_types tables.", - spells[spell_id].teleport_zone, record.npc_type); + LogError("Unable to load NPC data for aura [{}] (NPC ID [{}]), check auras and npc_types tables", spells[spell_id].teleport_zone, record.npc_type); return; } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 9b7354190..ad314d214 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -663,7 +663,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) effect == SE_StackingCommand_Overwrite) continue; - Log(Logs::Detail, Logs::AA, "Applying Effect %d from AA %u in slot %d (base1: %d, base2: %d) on %s", + LogAA("Applying Effect [{}] from AA [{}] in slot [{}] (base1: [{}], base2: [{}]) on [{}]", effect, rank.id, slot, base1, base2, GetCleanName()); uint8 focus = IsFocusEffect(0, 0, true, effect); @@ -1205,8 +1205,12 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; } - case SE_CastingLevel2: case SE_CastingLevel: { + newbon->adjusted_casting_skill += base1; + break; + } + + case SE_CastingLevel2: { newbon->effective_casting_level += base1; break; } @@ -1533,7 +1537,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; default: - Log(Logs::Detail, Logs::AA, "SPA %d not accounted for in AA %s (%d)", effect, rank.base_ability->name.c_str(), rank.id); + LogAA("SPA [{}] not accounted for in AA [{}] ([{}])", effect, rank.base_ability->name.c_str(), rank.id); break; } @@ -1914,8 +1918,13 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } - case SE_CastingLevel2: case SE_CastingLevel: // Brilliance of Ro + { + new_bonus->adjusted_casting_skill += effect_value; + break; + } + + case SE_CastingLevel2: { new_bonus->effective_casting_level += effect_value; break; @@ -3850,8 +3859,13 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) aabonuses.Corrup = effect_value; break; - case SE_CastingLevel2: case SE_CastingLevel: // Brilliance of Ro + spellbonuses.adjusted_casting_skill = effect_value; + aabonuses.adjusted_casting_skill = effect_value; + itembonuses.adjusted_casting_skill = effect_value; + break; + + case SE_CastingLevel2: spellbonuses.effective_casting_level = effect_value; aabonuses.effective_casting_level = effect_value; itembonuses.effective_casting_level = effect_value; diff --git a/zone/bot.cpp b/zone/bot.cpp index 971c1d1b6..238fb06da 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -255,6 +255,18 @@ void Bot::SetBotSpellID(uint32 newSpellID) { this->npc_spells_id = newSpellID; } +void Bot::SetSurname(std::string bot_surname) { + _surname = bot_surname.substr(0, 31); +} + +void Bot::SetTitle(std::string bot_title) { + _title = bot_title.substr(0, 31); +} + +void Bot::SetSuffix(std::string bot_suffix) { + _suffix = bot_suffix.substr(0, 31); +} + uint32 Bot::GetBotArcheryRange() { const EQEmu::ItemInstance *range_inst = GetBotItem(EQEmu::invslot::slotRange); const EQEmu::ItemInstance *ammo_inst = GetBotItem(EQEmu::invslot::slotAmmo); @@ -1312,7 +1324,7 @@ int32 Bot::acmod() { else return (65 + ((agility - 300) / 21)); #if EQDEBUG >= 11 - Log(Logs::General, Logs::Error, "Error in Bot::acmod(): Agility: %i, Level: %i",agility,level); + LogError("Error in Bot::acmod(): Agility: [{}], Level: [{}]",agility,level); #endif return 0; } @@ -2069,7 +2081,7 @@ void Bot::BotRangedAttack(Mob* other) { //make sure the attack and ranged timers are up //if the ranged timer is disabled, then they have no ranged weapon and shouldent be attacking anyhow if((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check())) { - Log(Logs::Detail, Logs::Combat, "Bot Archery attack canceled. Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); + LogCombat("Bot Archery attack canceled. Timer not up. Attack [{}], ranged [{}]", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); Message(0, "Error: Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); return; } @@ -2087,7 +2099,7 @@ void Bot::BotRangedAttack(Mob* other) { if(!RangeWeapon || !Ammo) return; - Log(Logs::Detail, Logs::Combat, "Shooting %s with bow %s (%d) and arrow %s (%d)", other->GetCleanName(), RangeWeapon->Name, RangeWeapon->ID, Ammo->Name, Ammo->ID); + LogCombat("Shooting [{}] with bow [{}] ([{}]) and arrow [{}] ([{}])", other->GetCleanName(), RangeWeapon->Name, RangeWeapon->ID, Ammo->Name, Ammo->ID); if(!IsAttackAllowed(other) || IsCasting() || DivineAura() || IsStunned() || IsMezzed() || (GetAppearance() == eaDead)) return; @@ -2097,21 +2109,21 @@ void Bot::BotRangedAttack(Mob* other) { //break invis when you attack if(invisible) { - Log(Logs::Detail, Logs::Combat, "Removing invisibility due to melee attack."); + LogCombat("Removing invisibility due to melee attack"); BuffFadeByEffect(SE_Invisibility); BuffFadeByEffect(SE_Invisibility2); invisible = false; } if(invisible_undead) { - Log(Logs::Detail, Logs::Combat, "Removing invisibility vs. undead due to melee attack."); + LogCombat("Removing invisibility vs. undead due to melee attack"); BuffFadeByEffect(SE_InvisVsUndead); BuffFadeByEffect(SE_InvisVsUndead2); invisible_undead = false; } if(invisible_animals) { - Log(Logs::Detail, Logs::Combat, "Removing invisibility vs. animals due to melee attack."); + LogCombat("Removing invisibility vs. animals due to melee attack"); BuffFadeByEffect(SE_InvisVsAnimals); invisible_animals = false; } @@ -2351,7 +2363,7 @@ void Bot::AI_Process() { Mob* delete_me = HealRotationTarget(); if (AIHealRotation(HealRotationTarget(), UseHealRotationFastHeals())) { #if (EQDEBUG >= 12) - Log(Logs::General, Logs::Error, "Bot::AI_Process() - Casting succeeded (m: %s, t: %s) : AdvHR(true)", GetCleanName(), ((delete_me) ? (delete_me->GetCleanName()) : ("nullptr"))); + LogError("Bot::AI_Process() - Casting succeeded (m: [{}], t: [{}]) : AdvHR(true)", GetCleanName(), ((delete_me) ? (delete_me->GetCleanName()) : ("nullptr"))); #endif m_member_of_heal_rotation->SetMemberIsCasting(this); m_member_of_heal_rotation->UpdateTargetHealingStats(HealRotationTarget()); @@ -2359,7 +2371,7 @@ void Bot::AI_Process() { } else { #if (EQDEBUG >= 12) - Log(Logs::General, Logs::Error, "Bot::AI_Process() - Casting failed (m: %s, t: %s) : AdvHR(false)", GetCleanName(), ((delete_me) ? (delete_me->GetCleanName()) : ("nullptr"))); + LogError("Bot::AI_Process() - Casting failed (m: [{}], t: [{}]) : AdvHR(false)", GetCleanName(), ((delete_me) ? (delete_me->GetCleanName()) : ("nullptr"))); #endif m_member_of_heal_rotation->SetMemberIsCasting(this, false); AdvanceHealRotation(false); @@ -2872,7 +2884,7 @@ void Bot::AI_Process() { else { // To far away to fight (GetTarget() validity can be iffy below this point - including outer scopes) if (AI_movement_timer->Check() && (!spellend_timer.Enabled() || GetClass() == BARD)) { // Pursue processing if (GetTarget() && !IsRooted()) { - Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", GetTarget()->GetCleanName()); + LogAI("Pursuing [{}] while engaged", GetTarget()->GetCleanName()); Goal = GetTarget()->GetPosition(); @@ -3177,7 +3189,7 @@ void Bot::PetAIProcess() { else if (botPet->GetTarget() && botPet->GetAIMovementTimer()->Check()) { botPet->SetRunAnimSpeed(0); if(!botPet->IsRooted()) { - Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", botPet->GetTarget()->GetCleanName()); + LogAI("Pursuing [{}] while engaged", botPet->GetTarget()->GetCleanName()); botPet->RunTo(botPet->GetTarget()->GetX(), botPet->GetTarget()->GetY(), botPet->GetTarget()->GetZ()); return; } else { @@ -3260,7 +3272,7 @@ bool Bot::Spawn(Client* botCharacterOwner) { else this->GetBotOwner()->CastToClient()->Message(Chat::Red, "%s save failed!", this->GetCleanName()); - // Spawn the bot at the bow owner's loc + // Spawn the bot at the bot owner's loc this->m_Position.x = botCharacterOwner->GetX(); this->m_Position.y = botCharacterOwner->GetY(); this->m_Position.z = botCharacterOwner->GetZ(); @@ -3365,6 +3377,9 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { ns->spawn.helm = helmtexture; //(GetShowHelm() ? helmtexture : 0); //0xFF; ns->spawn.equip_chest2 = texture; //0xFF; ns->spawn.show_name = true; + strcpy(ns->spawn.lastName, GetSurname().c_str()); + strcpy(ns->spawn.title, GetTitle().c_str()); + strcpy(ns->spawn.suffix, GetSuffix().c_str()); const EQEmu::ItemData* item = nullptr; const EQEmu::ItemInstance* inst = nullptr; uint32 spawnedbotid = 0; @@ -3499,7 +3514,7 @@ void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) { Bot* bot = *biter; if(bot && (bot->GetLevel() != client->GetLevel())) { bot->SetPetChooser(false); // not sure what this does, but was in bot 'update' code - bot->CalcBotStats(client->GetBotOptionStatsUpdate()); + bot->CalcBotStats(client->GetBotOption(Client::booStatsUpdate)); if(sendlvlapp) bot->SendLevelAppearance(); // modified from Client::SetLevel() @@ -4178,7 +4193,7 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli client->Message(Chat::Lime, "Trade with '%s' resulted in %i accepted item%s, %i returned item%s.", GetCleanName(), accepted_count, ((accepted_count == 1) ? "" : "s"), returned_count, ((returned_count == 1) ? "" : "s")); if (accepted_count) - CalcBotStats(client->GetBotOptionStatsUpdate()); + CalcBotStats(client->GetBotOption(Client::booStatsUpdate)); } bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill) { @@ -4188,7 +4203,7 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, EQEmu::skills::Sk Save(); Mob *my_owner = GetBotOwner(); - if (my_owner && my_owner->IsClient() && my_owner->CastToClient()->GetBotOptionDeathMarquee()) { + if (my_owner && my_owner->IsClient() && my_owner->CastToClient()->GetBotOption(Client::booDeathMarquee)) { if (killerMob) my_owner->CastToClient()->SendMarqueeMessage(Chat::Yellow, 510, 0, 1000, 3000, StringFormat("%s has been slain by %s", GetCleanName(), killerMob->GetCleanName())); else @@ -4271,7 +4286,7 @@ void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, EQEmu::skills::SkillT //handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds if(attacked_timer.Check()) { - Log(Logs::Detail, Logs::Combat, "Triggering EVENT_ATTACK due to attack by %s", from->GetName()); + LogCombat("Triggering EVENT_ATTACK due to attack by [{}]", from->GetName()); parse->EventNPC(EVENT_ATTACK, this, from, "", 0); } @@ -4279,7 +4294,7 @@ void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, EQEmu::skills::SkillT // if spell is lifetap add hp to the caster if (spell_id != SPELL_UNKNOWN && IsLifetapSpell(spell_id)) { int healed = GetActSpellHealing(spell_id, damage); - Log(Logs::Detail, Logs::Combat, "Applying lifetap heal of %d to %s", healed, GetCleanName()); + LogCombat("Applying lifetap heal of [{}] to [{}]", healed, GetCleanName()); HealDamage(healed); entity_list.MessageClose(this, true, 300, Chat::Spells, "%s beams a smile at %s", GetCleanName(), from->GetCleanName() ); } @@ -4315,13 +4330,13 @@ void Bot::AddToHateList(Mob* other, uint32 hate, int32 damage, bool iYellForHelp bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) { if (!other) { SetTarget(nullptr); - Log(Logs::General, Logs::Error, "A null Mob object was passed to Bot::Attack for evaluation!"); + LogError("A null Mob object was passed to Bot::Attack for evaluation!"); return false; } if ((GetHP() <= 0) || (GetAppearance() == eaDead)) { SetTarget(nullptr); - Log(Logs::Detail, Logs::Combat, "Attempted to attack %s while unconscious or, otherwise, appearing dead", other->GetCleanName()); + LogCombat("Attempted to attack [{}] while unconscious or, otherwise, appearing dead", other->GetCleanName()); return false; } @@ -4333,20 +4348,20 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b // takes more to compare a call result, load for a call, load a compare to address and compare, and finally // push a value to an address than to just load for a call and push a value to an address. - Log(Logs::Detail, Logs::Combat, "Attacking %s with hand %d %s", other->GetCleanName(), Hand, (FromRiposte ? "(this is a riposte)" : "")); + LogCombat("Attacking [{}] with hand [{}] [{}]", other->GetCleanName(), Hand, (FromRiposte ? "(this is a riposte)" : "")); if ((IsCasting() && (GetClass() != BARD) && !IsFromSpell) || (!IsAttackAllowed(other))) { if(this->GetOwnerID()) entity_list.MessageClose(this, 1, 200, 10, "%s says, '%s is not a legal target master.'", this->GetCleanName(), this->GetTarget()->GetCleanName()); if(other) { RemoveFromHateList(other); - Log(Logs::Detail, Logs::Combat, "I am not allowed to attack %s", other->GetCleanName()); + LogCombat("I am not allowed to attack [{}]", other->GetCleanName()); } return false; } if(DivineAura()) {//cant attack while invulnerable - Log(Logs::Detail, Logs::Combat, "Attack canceled, Divine Aura is in effect."); + LogCombat("Attack canceled, Divine Aura is in effect"); return false; } @@ -4364,19 +4379,19 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b if(weapon != nullptr) { if (!weapon->IsWeapon()) { - Log(Logs::Detail, Logs::Combat, "Attack canceled, Item %s (%d) is not a weapon.", weapon->GetItem()->Name, weapon->GetID()); + LogCombat("Attack canceled, Item [{}] ([{}]) is not a weapon", weapon->GetItem()->Name, weapon->GetID()); return false; } - Log(Logs::Detail, Logs::Combat, "Attacking with weapon: %s (%d)", weapon->GetItem()->Name, weapon->GetID()); + LogCombat("Attacking with weapon: [{}] ([{}])", weapon->GetItem()->Name, weapon->GetID()); } else - Log(Logs::Detail, Logs::Combat, "Attacking without a weapon."); + LogCombat("Attacking without a weapon"); // calculate attack_skill and skillinuse depending on hand and weapon // also send Packet to near clients DamageHitInfo my_hit; my_hit.skill = AttackAnimation(Hand, weapon); - Log(Logs::Detail, Logs::Combat, "Attacking with %s in slot %d using skill %d", weapon?weapon->GetItem()->Name:"Fist", Hand, my_hit.skill); + LogCombat("Attacking with [{}] in slot [{}] using skill [{}]", weapon?weapon->GetItem()->Name:"Fist", Hand, my_hit.skill); // Now figure out damage my_hit.damage_done = 1; @@ -4424,7 +4439,7 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b } } - Log(Logs::Detail, Logs::Combat, "Damage calculated: base %d min damage %d skill %d", my_hit.base_damage, my_hit.min_damage, my_hit.skill); + LogCombat("Damage calculated: base [{}] min damage [{}] skill [{}]", my_hit.base_damage, my_hit.min_damage, my_hit.skill); int hit_chance_bonus = 0; my_hit.offense = offense(my_hit.skill); @@ -4442,7 +4457,7 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b DoAttack(other, my_hit, opts); - Log(Logs::Detail, Logs::Combat, "Final damage after all reductions: %d", my_hit.damage_done); + LogCombat("Final damage after all reductions: [{}]", my_hit.damage_done); } else { my_hit.damage_done = DMG_INVULNERABLE; } @@ -5054,7 +5069,7 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel return 0; break; default: - Log(Logs::General, Logs::Normal, "CalcFocusEffect: unknown limit spelltype %d", focus_spell.base[i]); + LogInfo("CalcFocusEffect: unknown limit spelltype [{}]", focus_spell.base[i]); } break; @@ -5268,7 +5283,7 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel break; } default: - Log(Logs::General, Logs::Spells, "CalcFocusEffect: unknown effectid %d", focus_spell.effectid[i]); + LogSpells("CalcFocusEffect: unknown effectid [{}]", focus_spell.effectid[i]); break; } } @@ -5308,7 +5323,7 @@ float Bot::GetProcChances(float ProcBonus, uint16 hand) { ProcChance += (ProcChance * ProcBonus / 100.0f); } - Log(Logs::Detail, Logs::Combat, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); + LogCombat("Proc chance [{}] ([{}] from bonuses)", ProcChance, ProcBonus); return ProcChance; } @@ -5362,13 +5377,13 @@ bool Bot::TryFinishingBlow(Mob *defender, int &damage) int fb_damage = aabonuses.FinishingBlow[1]; int levelreq = aabonuses.FinishingBlowLvl[0]; if (defender->GetLevel() <= levelreq && (chance >= zone->random.Int(1, 1000))) { - Log(Logs::Detail, Logs::Combat, "Landed a finishing blow: levelreq at %d, other level %d", + LogCombat("Landed a finishing blow: levelreq at [{}], other level [{}]", levelreq, defender->GetLevel()); entity_list.MessageCloseString(this, false, 200, Chat::MeleeCrit, FINISHING_BLOW, GetName()); damage = fb_damage; return true; } else { - Log(Logs::Detail, Logs::Combat, "FAILED a finishing blow: levelreq at %d, other level %d", + LogCombat("failed a finishing blow: levelreq at [{}], other level [{}]", levelreq, defender->GetLevel()); return false; } @@ -5377,14 +5392,14 @@ bool Bot::TryFinishingBlow(Mob *defender, int &damage) } void Bot::DoRiposte(Mob* defender) { - Log(Logs::Detail, Logs::Combat, "Preforming a riposte"); + LogCombat("Preforming a riposte"); if (!defender) return; defender->Attack(this, EQEmu::invslot::slotPrimary, true); int32 DoubleRipChance = (defender->GetAABonuses().GiveDoubleRiposte[0] + defender->GetSpellBonuses().GiveDoubleRiposte[0] + defender->GetItemBonuses().GiveDoubleRiposte[0]); if(DoubleRipChance && (DoubleRipChance >= zone->random.Int(0, 100))) { - Log(Logs::Detail, Logs::Combat, "Preforming a double riposte (%d percent chance)", DoubleRipChance); + LogCombat("Preforming a double riposte ([{}] percent chance)", DoubleRipChance); defender->Attack(this, EQEmu::invslot::slotPrimary, true); } @@ -6007,7 +6022,7 @@ int32 Bot::CalcMaxMana() { break; } default: { - Log(Logs::General, Logs::None, "Invalid Class '%c' in CalcMaxMana", GetCasterClass()); + LogDebug("Invalid Class [{}] in CalcMaxMana", GetCasterClass()); max_mana = 0; break; } @@ -6435,14 +6450,14 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, EQEmu::spells::CastingSlo uint32* oSpellWillFinish, uint32 item_slot, int16 *resist_adjust, uint32 aa_id) { bool Result = false; if(zone && !zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { - Log(Logs::Detail, Logs::Spells, "CastSpell called for spell %s (%d) on entity %d, slot %d, time %d, mana %d, from item slot %d", spells[spell_id].name, spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot); + // LogSpells("CastSpell called for spell [{}] ([{}]) on entity [{}], slot [{}], time [{}], mana [{}], from item slot [{}]", spells[spell_id].name, spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot); if(casting_spell_id == spell_id) ZeroCastingVars(); if(GetClass() != BARD) { if(!IsValidSpell(spell_id) || casting_spell_id || delaytimer || spellend_timer.Enabled() || IsStunned() || IsFeared() || IsMezzed() || (IsSilenced() && !IsDiscipline(spell_id)) || (IsAmnesiad() && IsDiscipline(spell_id))) { - Log(Logs::Detail, Logs::Spells, "Spell casting canceled: not able to cast now. Valid? %d, casting %d, waiting? %d, spellend? %d, stunned? %d, feared? %d, mezed? %d, silenced? %d", IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled(), IsStunned(), IsFeared(), IsMezzed(), IsSilenced() ); + LogSpells("Spell casting canceled: not able to cast now. Valid? [{}], casting [{}], waiting? [{}], spellend? [{}], stunned? [{}], feared? [{}], mezed? [{}], silenced? [{}]", IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled(), IsStunned(), IsFeared(), IsMezzed(), IsSilenced() ); if(IsSilenced() && !IsDiscipline(spell_id)) MessageString(Chat::Red, SILENCED_STRING); @@ -6466,7 +6481,7 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, EQEmu::spells::CastingSlo } if(DivineAura()) { - Log(Logs::Detail, Logs::Spells, "Spell casting canceled: cannot cast while Divine Aura is in effect."); + LogSpells("Spell casting canceled: cannot cast while Divine Aura is in effect"); InterruptSpell(173, 0x121, false); return false; } @@ -6476,13 +6491,13 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, EQEmu::spells::CastingSlo InterruptSpell(fizzle_msg, 0x121, spell_id); uint32 use_mana = ((spells[spell_id].mana) / 4); - Log(Logs::Detail, Logs::Spells, "Spell casting canceled: fizzled. %d mana has been consumed", use_mana); + LogSpells("Spell casting canceled: fizzled. [{}] mana has been consumed", use_mana); SetMana(GetMana() - use_mana); return false; } if (HasActiveSong()) { - Log(Logs::Detail, Logs::Spells, "Casting a new spell/song while singing a song. Killing old song %d.", bardsong); + LogSpells("Casting a new spell/song while singing a song. Killing old song [{}]", bardsong); bardsong = 0; bardsong_target_id = 0; bardsong_slot = EQEmu::spells::CastingSlot::Gem1; @@ -6596,19 +6611,19 @@ bool Bot::IsImmuneToSpell(uint16 spell_id, Mob *caster) { if(caster->IsBot()) { if(spells[spell_id].targettype == ST_Undead) { if((GetBodyType() != BT_SummonedUndead) && (GetBodyType() != BT_Undead) && (GetBodyType() != BT_Vampire)) { - Log(Logs::Detail, Logs::Spells, "Bot's target is not an undead."); + LogSpells("Bot's target is not an undead"); return true; } } if(spells[spell_id].targettype == ST_Summoned) { if((GetBodyType() != BT_SummonedUndead) && (GetBodyType() != BT_Summoned) && (GetBodyType() != BT_Summoned2) && (GetBodyType() != BT_Summoned3)) { - Log(Logs::Detail, Logs::Spells, "Bot's target is not a summoned creature."); + LogSpells("Bot's target is not a summoned creature"); return true; } } } - Log(Logs::Detail, Logs::Spells, "No bot immunities to spell %d found.", spell_id); + LogSpells("No bot immunities to spell [{}] found", spell_id); } } @@ -6759,7 +6774,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQEmu:: if((spelltypeequal || spelltypetargetequal) || spelltypeclassequal || slotequal) { if(((spells[thespell].effectid[0] == 0) && (spells[thespell].base[0] < 0)) && (spellTarget->GetHP() < ((spells[thespell].base[0] * (-1)) + 100))) { - Log(Logs::General, Logs::Spells, "Bot::DoFinishedSpellSingleTarget - GroupBuffing failure"); + LogSpells("Bot::DoFinishedSpellSingleTarget - GroupBuffing failure"); return false; } @@ -8223,7 +8238,7 @@ bool Bot::CheckLoreConflict(const EQEmu::ItemData* item) { bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes) { if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) { - Log(Logs::General, Logs::Error, "Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); + LogError("Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); return false; } diff --git a/zone/bot.h b/zone/bot.h index 3b363748a..20348181e 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -270,7 +270,12 @@ public: bool GetNeedsHateRedux(Mob *tar); bool HasOrMayGetAggro(); void SetDefaultBotStance(); - + void SetSurname(std::string bot_surname); + void SetTitle(std::string bot_title); + void SetSuffix(std::string bot_suffix); + std::string GetSurname() { return _surname; } + std::string GetTitle() { return _title; } + std::string GetSuffix() { return _suffix; } inline virtual int32 GetMaxStat(); inline virtual int32 GetMaxResist(); inline virtual int32 GetMaxSTR(); @@ -650,6 +655,9 @@ private: uint32 _guildId; uint8 _guildRank; std::string _guildName; + std::string _surname; + std::string _title; + std::string _suffix; uint32 _lastZoneId; bool _rangerAutoWeaponSelect; BotRoleType _botRole; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 31270cc0f..3ca3ab169 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -454,7 +454,7 @@ public: if (target_type == BCEnum::TT_Self && (entry_prototype->BCST() != BCEnum::SpT_Stance && entry_prototype->BCST() != BCEnum::SpT_SummonCorpse)) { #ifdef BCSTSPELLDUMP - Log(Logs::General, Logs::Error, "DELETING entry_prototype (primary clause) - name: %s, target_type: %s, BCST: %s", + LogError("DELETING entry_prototype (primary clause) - name: [{}], target_type: [{}], BCST: [{}]", spells[spell_id].name, BCEnum::TargetTypeEnumToString(target_type).c_str(), BCEnum::SpellTypeEnumToString(entry_prototype->BCST()).c_str()); #endif safe_delete(entry_prototype); @@ -462,7 +462,7 @@ public: } if (entry_prototype->BCST() == BCEnum::SpT_Stance && target_type != BCEnum::TT_Self) { #ifdef BCSTSPELLDUMP - Log(Logs::General, Logs::Error, "DELETING entry_prototype (secondary clause) - name: %s, BCST: %s, target_type: %s", + LogError("DELETING entry_prototype (secondary clause) - name: [{}], BCST: [{}], target_type: [{}]", spells[spell_id].name, BCEnum::SpellTypeEnumToString(entry_prototype->BCST()).c_str(), BCEnum::TargetTypeEnumToString(target_type).c_str()); #endif safe_delete(entry_prototype); @@ -1061,7 +1061,7 @@ private: std::string query = "SELECT `short_name`, `long_name` FROM `zone` WHERE '' NOT IN (`short_name`, `long_name`)"; auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "load_teleport_zone_names() - Error in zone names query: %s", results.ErrorMessage().c_str()); + LogError("load_teleport_zone_names() - Error in zone names query: [{}]", results.ErrorMessage().c_str()); return; } @@ -1088,14 +1088,14 @@ private: } static void status_report() { - Log(Logs::General, Logs::Commands, "load_bot_command_spells(): - 'RuleI(Bots, CommandSpellRank)' set to %i.", RuleI(Bots, CommandSpellRank)); + LogCommands("load_bot_command_spells(): - 'RuleI(Bots, CommandSpellRank)' set to [{}]", RuleI(Bots, CommandSpellRank)); if (bot_command_spells.empty()) { - Log(Logs::General, Logs::Error, "load_bot_command_spells() - 'bot_command_spells' is empty."); + LogError("load_bot_command_spells() - 'bot_command_spells' is empty"); return; } for (int i = BCEnum::SpellTypeFirst; i <= BCEnum::SpellTypeLast; ++i) - Log(Logs::General, Logs::Commands, "load_bot_command_spells(): - '%s' returned %u spell entries.", + LogCommands("load_bot_command_spells(): - [{}] returned [{}] spell entries", BCEnum::SpellTypeEnumToString(static_cast(i)).c_str(), bot_command_spells[static_cast(i)].size()); } @@ -1351,10 +1351,13 @@ int bot_command_init(void) bot_command_add("botspawn", "Spawns a created bot", 0, bot_subcommand_bot_spawn) || bot_command_add("botstance", "Changes the stance of a bot", 0, bot_subcommand_bot_stance) || bot_command_add("botstopmeleelevel", "Sets the level a caster or spell-casting fighter bot will stop melee combat", 0, bot_subcommand_bot_stop_melee_level) || + bot_command_add("botsuffix", "Sets a bots suffix", 0, bot_subcommand_bot_suffix) || bot_command_add("botsummon", "Summons bot(s) to your location", 0, bot_subcommand_bot_summon) || + bot_command_add("botsurname", "Sets a bots surname (last name)", 0, bot_subcommand_bot_surname) || bot_command_add("bottattoo", "Changes the Drakkin tattoo of a bot", 0, bot_subcommand_bot_tattoo) || bot_command_add("bottogglearcher", "Toggles a archer bot between melee and ranged weapon use", 0, bot_subcommand_bot_toggle_archer) || bot_command_add("bottogglehelm", "Toggles the helm visibility of a bot between shown and hidden", 0, bot_subcommand_bot_toggle_helm) || + bot_command_add("bottitle", "Sets a bots title", 0, bot_subcommand_bot_title) || bot_command_add("botupdate", "Updates a bot to reflect any level changes that you have experienced", 0, bot_subcommand_bot_update) || bot_command_add("botwoad", "Changes the Barbarian woad of a bot", 0, bot_subcommand_bot_woad) || bot_command_add("charm", "Attempts to have a bot charm your target", 0, bot_command_charm) || @@ -1426,35 +1429,93 @@ int bot_command_init(void) std::map>> bot_command_settings; database.botdb.LoadBotCommandSettings(bot_command_settings); + std::vector> injected_bot_command_settings; + std::vector orphaned_bot_command_settings; + + for (auto bcs_iter : bot_command_settings) { + + auto bcl_iter = bot_command_list.find(bcs_iter.first); + if (bcl_iter == bot_command_list.end()) { + + orphaned_bot_command_settings.push_back(bcs_iter.first); + LogInfo( + "Bot Command [{}] no longer exists... Deleting orphaned entry from `bot_command_settings` table", + bcs_iter.first.c_str() + ); + } + } + + if (orphaned_bot_command_settings.size()) { + if (!database.botdb.UpdateOrphanedBotCommandSettings(orphaned_bot_command_settings)) { + LogInfo("Failed to process 'Orphaned Bot Commands' update operation."); + } + } + auto working_bcl = bot_command_list; for (auto working_bcl_iter : working_bcl) { - auto bot_command_settings_iter = bot_command_settings.find(working_bcl_iter.first); - if (bot_command_settings_iter == bot_command_settings.end()) { - if (working_bcl_iter.second->access == 0) - Log(Logs::General, Logs::Commands, "bot_command_init(): Warning: Bot Command '%s' defaulting to access level 0!", working_bcl_iter.first.c_str()); + + auto bcs_iter = bot_command_settings.find(working_bcl_iter.first); + if (bcs_iter == bot_command_settings.end()) { + + injected_bot_command_settings.push_back(std::pair(working_bcl_iter.first, working_bcl_iter.second->access)); + LogInfo( + "New Bot Command [{}] found... Adding to `bot_command_settings` table with access [{}]", + working_bcl_iter.first.c_str(), + working_bcl_iter.second->access + ); + + if (working_bcl_iter.second->access == 0) { + LogCommands( + "bot_command_init(): Warning: Bot Command [{}] defaulting to access level 0!", + working_bcl_iter.first.c_str() + ); + } + continue; } - working_bcl_iter.second->access = bot_command_settings_iter->second.first; - Log(Logs::General, Logs::Commands, "bot_command_init(): - Bot Command '%s' set to access level %d.", working_bcl_iter.first.c_str(), bot_command_settings_iter->second.first); - if (bot_command_settings_iter->second.second.empty()) + working_bcl_iter.second->access = bcs_iter->second.first; + LogCommands( + "bot_command_init(): - Bot Command [{}] set to access level [{}]", + working_bcl_iter.first.c_str(), + bcs_iter->second.first + ); + + if (bcs_iter->second.second.empty()) { continue; + } - for (auto alias_iter : bot_command_settings_iter->second.second) { - if (alias_iter.empty()) + for (auto alias_iter : bcs_iter->second.second) { + if (alias_iter.empty()) { continue; + } + if (bot_command_list.find(alias_iter) != bot_command_list.end()) { - Log(Logs::General, Logs::Commands, "bot_command_init(): Warning: Alias '%s' already exists as a bot command - skipping!", alias_iter.c_str()); + LogCommands( + "bot_command_init(): Warning: Alias [{}] already exists as a bot command - skipping!", + alias_iter.c_str() + ); + continue; } bot_command_list[alias_iter] = working_bcl_iter.second; bot_command_aliases[alias_iter] = working_bcl_iter.first; - Log(Logs::General, Logs::Commands, "bot_command_init(): - Alias '%s' added to bot command '%s'.", alias_iter.c_str(), bot_command_aliases[alias_iter].c_str()); + LogCommands( + "bot_command_init(): - Alias [{}] added to bot command [{}]", + alias_iter.c_str(), + bot_command_aliases[alias_iter].c_str() + ); } } + if (injected_bot_command_settings.size()) { + if (!database.botdb.UpdateInjectedBotCommandSettings(injected_bot_command_settings)) { + LogInfo("Failed to process 'Injected Bot Commands' update operation."); + } + } + bot_command_dispatch = bot_command_real_dispatch; BCSpells::Load(); @@ -1497,21 +1558,21 @@ void bot_command_deinit(void) int bot_command_add(std::string bot_command_name, const char *desc, int access, BotCmdFuncPtr function) { if (bot_command_name.empty()) { - Log(Logs::General, Logs::Error, "bot_command_add() - Bot command added with empty name string - check bot_command.cpp."); + LogError("bot_command_add() - Bot command added with empty name string - check bot_command.cpp"); return -1; } if (function == nullptr) { - Log(Logs::General, Logs::Error, "bot_command_add() - Bot command '%s' added without a valid function pointer - check bot_command.cpp.", bot_command_name.c_str()); + LogError("bot_command_add() - Bot command [{}] added without a valid function pointer - check bot_command.cpp", bot_command_name.c_str()); return -1; } if (bot_command_list.count(bot_command_name) != 0) { - Log(Logs::General, Logs::Error, "bot_command_add() - Bot command '%s' is a duplicate bot command name - check bot_command.cpp.", bot_command_name.c_str()); + LogError("bot_command_add() - Bot command [{}] is a duplicate bot command name - check bot_command.cpp", bot_command_name.c_str()); return -1; } for (auto iter : bot_command_list) { if (iter.second->function != function) continue; - Log(Logs::General, Logs::Error, "bot_command_add() - Bot command '%s' equates to an alias of '%s' - check bot_command.cpp.", bot_command_name.c_str(), iter.first.c_str()); + LogError("bot_command_add() - Bot command [{}] equates to an alias of [{}] - check bot_command.cpp", bot_command_name.c_str(), iter.first.c_str()); return -1; } @@ -1566,11 +1627,11 @@ int bot_command_real_dispatch(Client *c, const char *message) } if(cur->access >= COMMANDS_LOGGING_MIN_STATUS) { - Log(Logs::General, Logs::Commands, "%s (%s) used bot command: %s (target=%s)", c->GetName(), c->AccountName(), message, c->GetTarget()?c->GetTarget()->GetName():"NONE"); + LogCommands("[{}] ([{}]) used bot command: [{}] (target=[{}])", c->GetName(), c->AccountName(), message, c->GetTarget()?c->GetTarget()->GetName():"NONE"); } if(cur->function == nullptr) { - Log(Logs::General, Logs::Error, "Bot command '%s' has a null function\n", cstr.c_str()); + LogError("Bot command [{}] has a null function\n", cstr.c_str()); return(-1); } else { //dispatch C++ bot command @@ -3443,63 +3504,180 @@ void bot_command_movement_speed(Client *c, const Seperator *sep) void bot_command_owner_option(Client *c, const Seperator *sep) { if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(m_usage, "usage: %s [deathmarquee | statsupdate] (argument: enable | disable | null (toggles))", sep->arg[0]); - c->Message(m_usage, "usage: %s [spawnmessage] [argument: say | tell | silent | class | default]", sep->arg[0]); + + c->Message(m_usage, "usage: %s [option] [argument | null]", sep->arg[0]); + + std::string window_title = "Bot Owner Options"; + std::string window_text = + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
OptionArgumentNotes
deathmarqueeenable
disable
null(toggles)
statsupdateenable
disable
null(toggles)
spawnmessagesay
tell
silent
class
default
"; + + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + return; } - std::string owner_option = sep->arg[1]; - std::string argument = sep->arg[2]; + std::string owner_option(sep->arg[1]); + std::string argument(sep->arg[2]); if (!owner_option.compare("deathmarquee")) { - if (!argument.compare("enable")) - c->SetBotOptionDeathMarquee(true); - else if (!argument.compare("disable")) - c->SetBotOptionDeathMarquee(false); - else - c->SetBotOptionDeathMarquee(!c->GetBotOptionDeathMarquee()); - - database.botdb.SaveOwnerOptionDeathMarquee(c->CharacterID(), c->GetBotOptionDeathMarquee()); - c->Message(m_action, "Bot 'death marquee' is now %s.", (c->GetBotOptionDeathMarquee() == true ? "enabled" : "disabled")); - } - else if (!owner_option.compare("statsupdate")) { - if (!argument.compare("enable")) - c->SetBotOptionStatsUpdate(true); - else if (!argument.compare("disable")) - c->SetBotOptionStatsUpdate(false); - else - c->SetBotOptionStatsUpdate(!c->GetBotOptionStatsUpdate()); - database.botdb.SaveOwnerOptionStatsUpdate(c->CharacterID(), c->GetBotOptionStatsUpdate()); - c->Message(m_action, "Bot 'stats update' is now %s.", (c->GetBotOptionStatsUpdate() == true ? "enabled" : "disabled")); - } - else if (!owner_option.compare("spawnmessage")) { - if (!argument.compare("say")) { - c->SetBotOptionSpawnMessageSay(); + if (!argument.compare("enable")) { + c->SetBotOption(Client::booDeathMarquee, true); } - else if (!argument.compare("tell")) { - c->SetBotOptionSpawnMessageTell(); - } - else if (!argument.compare("silent")) { - c->SetBotOptionSpawnMessageSilent(); - } - else if (!argument.compare("class")) { - c->SetBotOptionSpawnMessageClassSpecific(true); - } - else if (!argument.compare("default")) { - c->SetBotOptionSpawnMessageClassSpecific(false); + else if (!argument.compare("disable")) { + c->SetBotOption(Client::booDeathMarquee, false); } else { + c->SetBotOption(Client::booDeathMarquee, !c->GetBotOption(Client::booDeathMarquee)); + } + + database.botdb.SaveOwnerOption(c->CharacterID(), Client::booDeathMarquee, c->GetBotOption(Client::booDeathMarquee)); + + c->Message(m_action, "Bot 'death marquee' is now %s.", (c->GetBotOption(Client::booDeathMarquee) == true ? "enabled" : "disabled")); + } + else if (!owner_option.compare("statsupdate")) { + + if (!argument.compare("enable")) { + c->SetBotOption(Client::booStatsUpdate, true); + } + else if (!argument.compare("disable")) { + c->SetBotOption(Client::booStatsUpdate, false); + } + else { + c->SetBotOption(Client::booStatsUpdate, !c->GetBotOption(Client::booStatsUpdate)); + } + + database.botdb.SaveOwnerOption(c->CharacterID(), Client::booStatsUpdate, c->GetBotOption(Client::booStatsUpdate)); + + c->Message(m_action, "Bot 'stats update' is now %s.", (c->GetBotOption(Client::booStatsUpdate) == true ? "enabled" : "disabled")); + } + else if (!owner_option.compare("spawnmessage")) { + + Client::BotOwnerOption boo = Client::_booCount; + + if (!argument.compare("say")) { + + boo = Client::booSpawnMessageSay; + c->SetBotOption(Client::booSpawnMessageSay, true); + c->SetBotOption(Client::booSpawnMessageTell, false); + } + else if (!argument.compare("tell")) { + + boo = Client::booSpawnMessageSay; + c->SetBotOption(Client::booSpawnMessageSay, false); + c->SetBotOption(Client::booSpawnMessageTell, true); + } + else if (!argument.compare("silent")) { + + boo = Client::booSpawnMessageSay; + c->SetBotOption(Client::booSpawnMessageSay, false); + c->SetBotOption(Client::booSpawnMessageTell, false); + } + else if (!argument.compare("class")) { + + boo = Client::booSpawnMessageClassSpecific; + c->SetBotOption(Client::booSpawnMessageClassSpecific, true); + } + else if (!argument.compare("default")) { + + boo = Client::booSpawnMessageClassSpecific; + c->SetBotOption(Client::booSpawnMessageClassSpecific, false); + } + else { + c->Message(m_fail, "Owner option '%s' argument '%s' is not recognized.", owner_option.c_str(), argument.c_str()); return; } - database.botdb.SaveOwnerOptionSpawnMessage( - c->CharacterID(), - c->GetBotOptionSpawnMessageSay(), - c->GetBotOptionSpawnMessageTell(), - c->GetBotOptionSpawnMessageClassSpecific() - ); + if (boo == Client::booSpawnMessageSay) { + + database.botdb.SaveOwnerOption( + c->CharacterID(), + std::pair( + Client::booSpawnMessageSay, + Client::booSpawnMessageTell + ), + std::pair( + c->GetBotOption(Client::booSpawnMessageSay), + c->GetBotOption(Client::booSpawnMessageTell) + ) + ); + } + else if (boo == Client::booSpawnMessageClassSpecific) { + + database.botdb.SaveOwnerOption( + c->CharacterID(), + Client::booSpawnMessageClassSpecific, + c->GetBotOption(Client::booSpawnMessageClassSpecific) + ); + } + else { + + c->Message(m_action, "Bot 'spawn message' is now ERROR."); + return; + } + c->Message(m_action, "Bot 'spawn message' is now %s.", argument.c_str()); } else { @@ -4254,7 +4432,7 @@ void bot_subcommand_bot_clone(Client *c, const Seperator *sep) } if (!my_bot->GetBotID()) { c->Message(m_unknown, "An unknown error has occured - BotName: %s, BotID: %u", my_bot->GetCleanName(), my_bot->GetBotID()); - Log(Logs::General, Logs::Commands, "bot_command_clone(): - Error: Active bot reported invalid ID (BotName: %s, BotID: %u, OwnerName: %s, OwnerID: %u, AcctName: %s, AcctID: %u)", + LogCommands("bot_command_clone(): - Error: Active bot reported invalid ID (BotName: [{}], BotID: [{}], OwnerName: [{}], OwnerID: [{}], AcctName: [{}], AcctID: [{}])", my_bot->GetCleanName(), my_bot->GetBotID(), c->GetCleanName(), c->CharacterID(), c->AccountName(), c->AccountID()); return; } @@ -4408,7 +4586,7 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep) return; } std::string bot_name = sep->arg[1]; - + bot_name = ucfirst(bot_name); if (sep->arg[2][0] == '\0' || !sep->IsNumber(2)) { c->Message(m_fail, "Invalid Class!"); return; @@ -4936,17 +5114,22 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_subcommand_bot_list", sep->arg[0], "botlist")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(m_usage, "usage: %s ([class] [value]) ([race] [value]) ([name] [partial-full])", sep->arg[0]); + c->Message(m_usage, "usage: %s (account) ([class] [value]) ([race] [value]) ([name] [partial-full])", sep->arg[0]); c->Message(m_note, "note: filter criteria is orderless and optional"); return; } - + bool Account = false; + int seps = 1; uint32 filter_value[FilterCount]; int name_criteria_arg = 0; memset(&filter_value, 0, sizeof(uint32) * FilterCount); int filter_mask = 0; - for (int i = 1; i < (FilterCount * 2); i += 2) { + if (strcasecmp(sep->arg[1], "account") == 0) { + Account = true; + seps = 2; + } + for (int i = seps; i < (FilterCount * 2); i += 2) { if (sep->arg[i][0] == '\0') break; @@ -4971,7 +5154,7 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep) } std::list bots_list; - if (!database.botdb.LoadBotsList(c->CharacterID(), bots_list)) { + if (!database.botdb.LoadBotsList(c->CharacterID(), bots_list, Account)) { c->Message(m_fail, "%s", BotDatabase::fail::LoadBotsList()); return; } @@ -4981,6 +5164,7 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep) } int bot_count = 0; + int bots_owned = 0; for (auto bots_iter : bots_list) { if (filter_mask) { if ((filter_mask & MaskClass) && filter_value[FilterClass] != bots_iter.Class) @@ -4996,23 +5180,26 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep) continue; } } - - c->Message(m_message, "%s is a level %u %s %s %s", - bots_iter.Name, + Bot * botCheckNotOnline = entity_list.GetBotByBotName(bots_iter.Name); + std::string botspawn_saylink = StringFormat("^botspawn %s", bots_iter.Name); + c->Message(Chat::White, "%s is a level %u %s %s %s who is owned by %s", + ((c->CharacterID() == bots_iter.Owner_ID) && (!botCheckNotOnline) ? (EQEmu::SayLinkEngine::GenerateQuestSaylink(botspawn_saylink, false, bots_iter.Name).c_str()) : (bots_iter.Name)), bots_iter.Level, Bot::RaceIdToString(bots_iter.Race).c_str(), ((bots_iter.Gender == FEMALE) ? ("Female") : ((bots_iter.Gender == MALE) ? ("Male") : ("Neuter"))), - Bot::ClassIdToString(bots_iter.Class).c_str() + Bot::ClassIdToString(bots_iter.Class).c_str(), + bots_iter.Owner ); - + if (c->CharacterID() == bots_iter.Owner_ID) { ++bots_owned; } ++bot_count; } if (!bot_count) { - c->Message(m_fail, "You have no bots meeting this criteria"); + c->Message(Chat::Red, "You have no bots meeting this criteria"); } else { - c->Message(m_action, "%i of %i bot%s shown", bot_count, bots_list.size(), ((bot_count != 1) ? ("s") : (""))); - c->Message(m_message, "Your limit is %i bot%s", RuleI(Bots, CreationLimit), ((RuleI(Bots, CreationLimit) != 1) ? ("s") : (""))); + c->Message(Chat::Yellow, "%i of %i bot%s shown.", bot_count, bots_list.size(), ((bot_count != 1) ? ("s") : (""))); + c->Message(Chat::Yellow, "%i of %i bot%s are owned by you. (You may spawn any available by clicking name)", bots_owned, bot_count, ((bot_count != 1) ? ("s") : (""))); + c->Message(Chat::White, "Your limit is %i bot%s", RuleI(Bots, CreationLimit), ((RuleI(Bots, CreationLimit) != 1) ? ("s") : (""))); } } @@ -5058,6 +5245,103 @@ void bot_subcommand_bot_out_of_combat(Client *c, const Seperator *sep) } } +void bot_subcommand_bot_surname(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == '\0' || sep->IsNumber(1)) { + c->Message(Chat::Red, "You must specify a [surname] to use this command (use _ to define spaces or -remove to clear.)"); + return; + } + auto my_bot = ActionableBots::AsTarget_ByBot(c); + if (!my_bot) { + c->Message(Chat::Red, "You must a bot that you own to use this command"); + return; + } + if (strlen(sep->arg[1]) > 31) { + c->Message(Chat::Red, "Surname must be 31 characters or less."); + return; + } + std::string bot_surname = sep->arg[1]; + bot_surname = (bot_surname == "-remove") ? "" : bot_surname; + std::replace(bot_surname.begin(), bot_surname.end(), '_', ' '); + my_bot->SetSurname(bot_surname); + if (!database.botdb.SaveBot(my_bot)) { + c->Message(Chat::Red, BotDatabase::fail::SaveBot()); + return; + } + else { + auto outapp = new EQApplicationPacket(OP_GMLastName, sizeof(GMLastName_Struct)); + GMLastName_Struct * gmn = (GMLastName_Struct*)outapp->pBuffer; + strcpy(gmn->name, my_bot->GetCleanName()); + strcpy(gmn->gmname, my_bot->GetCleanName()); + strcpy(gmn->lastname, my_bot->GetSurname().c_str()); + gmn->unknown[0] = 1; + gmn->unknown[1] = 1; + gmn->unknown[2] = 1; + gmn->unknown[3] = 1; + entity_list.QueueClients(my_bot->CastToClient(), outapp); + safe_delete(outapp); + c->Message(Chat::Yellow, "Bot Surname Saved."); + } +} + +void bot_subcommand_bot_title(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == '\0' || sep->IsNumber(1)) { + c->Message(Chat::Red, "You must specify a [title] to use this command. (use _ to define spaces or -remove to clear.)"); + return; + } + auto my_bot = ActionableBots::AsTarget_ByBot(c); + if (!my_bot) { + c->Message(Chat::Red, "You must a bot that you own to use this command"); + return; + } + if (strlen(sep->arg[1]) > 31) { + c->Message(Chat::Red, "Title must be 31 characters or less."); + return; + } + std::string bot_title = sep->arg[1]; + bot_title = (bot_title == "-remove") ? "" : bot_title; + std::replace(bot_title.begin(), bot_title.end(), '_', ' '); + my_bot->SetTitle(bot_title); + if (!database.botdb.SaveBot(my_bot)) { + c->Message(Chat::Red, BotDatabase::fail::SaveBot()); + return; + } + else { + my_bot->CastToClient()->SetAATitle(my_bot->GetTitle().c_str()); + c->Message(Chat::Yellow, "Bot Title Saved."); + } +} + +void bot_subcommand_bot_suffix(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == '\0' || sep->IsNumber(1)) { + c->Message(Chat::Red, "You must specify a [suffix] to use this command. (use _ to define spaces or -remove to clear.)"); + return; + } + auto my_bot = ActionableBots::AsTarget_ByBot(c); + if (!my_bot) { + c->Message(Chat::Red, "You must a bot that you own to use this command"); + return; + } + if (strlen(sep->arg[1]) > 31) { + c->Message(Chat::Red, "Suffix must be 31 characters or less."); + return; + } + std::string bot_suffix = sep->arg[1]; + bot_suffix = (bot_suffix == "-remove") ? "" : bot_suffix; + std::replace(bot_suffix.begin(), bot_suffix.end(), '_', ' '); + my_bot->SetSuffix(bot_suffix); + if (!database.botdb.SaveBot(my_bot)) { + c->Message(Chat::Red, BotDatabase::fail::SaveBot()); + return; + } + else { + my_bot->CastToClient()->SetTitleSuffix(my_bot->GetSuffix().c_str()); + c->Message(Chat::Yellow, "Bot Suffix Saved."); + } +} + void bot_subcommand_bot_report(Client *c, const Seperator *sep) { if (helper_command_alias_fail(c, "bot_subcommand_bot_report", sep->arg[0], "botreport")) @@ -5221,13 +5505,16 @@ void bot_subcommand_bot_spawn(Client *c, const Seperator *sep) }; uint8 message_index = 0; - if (c->GetBotOptionSpawnMessageClassSpecific()) + if (c->GetBotOption(Client::booSpawnMessageClassSpecific)) { message_index = VALIDATECLASSID(my_bot->GetClass()); + } - if (c->GetBotOptionSpawnMessageSay()) + if (c->GetBotOption(Client::booSpawnMessageSay)) { Bot::BotGroupSay(my_bot, "%s", bot_spawn_message[message_index]); - else if (c->GetBotOptionSpawnMessageTell()) + } + else if (c->GetBotOption(Client::booSpawnMessageTell)) { c->Message(Chat::Tell, "%s tells you, \"%s\"", my_bot->GetCleanName(), bot_spawn_message[message_index]); + } } void bot_subcommand_bot_stance(Client *c, const Seperator *sep) @@ -5607,7 +5894,7 @@ void bot_subcommand_bot_update(Client *c, const Seperator *sep) continue; bot_iter->SetPetChooser(false); - bot_iter->CalcBotStats(c->GetBotOptionStatsUpdate()); + bot_iter->CalcBotStats(c->GetBotOption(Client::booStatsUpdate)); bot_iter->SendAppearancePacket(AT_WhoLevel, bot_iter->GetLevel(), true, true); ++bot_count; } @@ -7396,7 +7683,7 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep) } my_bot->BotRemoveEquipItem(slotId); - my_bot->CalcBotStats(c->GetBotOptionStatsUpdate()); + my_bot->CalcBotStats(c->GetBotOption(Client::booStatsUpdate)); } switch (slotId) { diff --git a/zone/bot_command.h b/zone/bot_command.h index d60238f23..c8bad1e42 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -614,8 +614,11 @@ void bot_subcommand_bot_report(Client *c, const Seperator *sep); void bot_subcommand_bot_spawn(Client *c, const Seperator *sep); void bot_subcommand_bot_stance(Client *c, const Seperator *sep); void bot_subcommand_bot_stop_melee_level(Client *c, const Seperator *sep); +void bot_subcommand_bot_suffix(Client *c, const Seperator *sep); void bot_subcommand_bot_summon(Client *c, const Seperator *sep); +void bot_subcommand_bot_surname(Client *c, const Seperator *sep); void bot_subcommand_bot_tattoo(Client *c, const Seperator *sep); +void bot_subcommand_bot_title(Client *c, const Seperator *sep); void bot_subcommand_bot_toggle_archer(Client *c, const Seperator *sep); void bot_subcommand_bot_toggle_helm(Client *c, const Seperator *sep); void bot_subcommand_bot_update(Client *c, const Seperator *sep); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 29d0f37e9..974bbf565 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -27,6 +27,8 @@ #include "bot.h" #include "client.h" +#include + bool BotDatabase::LoadBotCommandSettings(std::map>> &bot_command_settings) { @@ -52,6 +54,58 @@ bool BotDatabase::LoadBotCommandSettings(std::map> &injected) +{ + if (injected.size()) { + + query = fmt::format( + "REPLACE INTO `bot_command_settings`(`bot_command`, `access`) VALUES {}", + implode( + ",", + std::pair('(', ')'), + join_pair(",", std::pair('\'', '\''), injected) + ) + ); + + if (!database.QueryDatabase(query).Success()) { + return false; + } + + Log(Logs::General, + Logs::Status, + "%u New Bot Command%s Added", + injected.size(), + (injected.size() == 1 ? "" : "s") + ); + } + + return true; +} + +bool BotDatabase::UpdateOrphanedBotCommandSettings(const std::vector &orphaned) +{ + if (orphaned.size()) { + + query = fmt::format( + "DELETE FROM `bot_command_settings` WHERE `bot_command` IN ({})", + implode(",", std::pair('\'', '\''), orphaned) + ); + + if (!database.QueryDatabase(query).Success()) { + return false; + } + + Log(Logs::General, + Logs::Status, + "%u Orphaned Bot Command%s Deleted", + orphaned.size(), + (orphaned.size() == 1 ? "" : "s") + ); + } + + return true; +} + bool BotDatabase::LoadBotSpellCastingChances() { query = @@ -163,12 +217,19 @@ bool BotDatabase::LoadQuestableSpawnCount(const uint32 owner_id, int& spawn_coun return true; } -bool BotDatabase::LoadBotsList(const uint32 owner_id, std::list& bots_list) +bool BotDatabase::LoadBotsList(const uint32 owner_id, std::list& bots_list, bool ByAccount) { if (!owner_id) return false; - query = StringFormat("SELECT `bot_id`, `name`, `class`, `level`, `race`, `gender` FROM `bot_data` WHERE `owner_id` = '%u'", owner_id); + if (ByAccount == true) + query = StringFormat("SELECT bot_id, bd.`name`, bd.class, bd.`level`, bd.race, bd.gender, cd.`name` as owner, bd.owner_id, cd.account_id, cd.id" + " FROM bot_data as bd inner join character_data as cd on bd.owner_id = cd.id" + " WHERE cd.account_id = (select account_id from bot_data bd inner join character_data as cd on bd.owner_id = cd.id where bd.owner_id = '%u' LIMIT 1)" + " ORDER BY bd.owner_id", owner_id); + else + query = StringFormat("SELECT `bot_id`, `name`, `class`, `level`, `race`, `gender`, 'You' as owner, owner_id FROM `bot_data` WHERE `owner_id` = '%u'", owner_id); + auto results = database.QueryDatabase(query); if (!results.Success()) return false; @@ -186,12 +247,17 @@ bool BotDatabase::LoadBotsList(const uint32 owner_id, std::list 63) + bot_owner = bot_owner.substr(0, 63); + if (!bot_owner.empty()) + strcpy(bot_entry.Owner, bot_owner.c_str()); bot_entry.Class = atoi(row[2]); bot_entry.Level = atoi(row[3]); bot_entry.Race = atoi(row[4]); bot_entry.Gender = atoi(row[5]); - + bot_entry.Owner_ID = atoi(row[7]); bots_list.push_back(bot_entry); } @@ -266,8 +332,8 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot) " `spells_id`," " `name`," " `last_name`," - " `title`," /* planned use[4] */ - " `suffix`," /* planned use[5] */ + " `title`," + " `suffix`," " `zone_id`," " `gender`," " `race`," @@ -364,7 +430,9 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot) loaded_bot = new Bot(bot_id, atoi(row[0]), atoi(row[1]), atof(row[14]), atoi(row[6]), tempNPCStruct); if (loaded_bot) { loaded_bot->SetShowHelm((atoi(row[43]) > 0 ? true : false)); - + loaded_bot->SetSurname(row[3]);//maintaining outside mob::lastname to cater to spaces + loaded_bot->SetTitle(row[4]); + loaded_bot->SetSuffix(row[5]); uint32 bfd = atoi(row[44]); if (bfd < 1) bfd = 1; @@ -573,12 +641,14 @@ bool BotDatabase::SaveBot(Bot* bot_inst) " `corruption` = '%i'," " `show_helm` = '%i'," " `follow_distance` = '%i'," - " `stop_melee_level` = '%u'" + " `stop_melee_level` = '%u'," + " `title` = '%s'," + " `suffix` = '%s'" " WHERE `bot_id` = '%u'", bot_inst->GetBotOwnerCharacterID(), bot_inst->GetBotSpellID(), bot_inst->GetCleanName(), - bot_inst->GetLastName(), + bot_inst->GetSurname().c_str(), bot_inst->GetLastZoneID(), bot_inst->GetBaseGender(), bot_inst->GetBaseRace(), @@ -616,6 +686,8 @@ bool BotDatabase::SaveBot(Bot* bot_inst) ((bot_inst->GetShowHelm()) ? (1) : (0)), bot_inst->GetFollowDistance(), bot_inst->GetStopMeleeLevel(), + bot_inst->GetTitle().c_str(), + bot_inst->GetSuffix().c_str(), bot_inst->GetBotID() ); auto results = database.QueryDatabase(query); @@ -1121,7 +1193,7 @@ bool BotDatabase::LoadItems(const uint32 bot_id, EQEmu::InventoryProfile& invent (uint32)atoul(row[14]) ); if (!item_inst) { - Log(Logs::General, Logs::Error, "Warning: bot_id '%i' has an invalid item_id '%i' in inventory slot '%i'", bot_id, item_id, slot_id); + LogError("Warning: bot_id [{}] has an invalid item_id [{}] in inventory slot [{}]", bot_id, item_id, slot_id); continue; } @@ -1174,7 +1246,7 @@ bool BotDatabase::LoadItems(const uint32 bot_id, EQEmu::InventoryProfile& invent item_inst->SetOrnamentHeroModel((uint32)atoul(row[8])); if (inventory_inst.PutItem(slot_id, *item_inst) == INVALID_INDEX) - Log(Logs::General, Logs::Error, "Warning: Invalid slot_id for item in inventory: bot_id = '%i', item_id = '%i', slot_id = '%i'", bot_id, item_id, slot_id); + LogError("Warning: Invalid slot_id for item in inventory: bot_id = [{}], item_id = [{}], slot_id = [{}]", bot_id, item_id, slot_id); safe_delete(item_inst); } @@ -2153,111 +2225,92 @@ bool BotDatabase::SaveStopMeleeLevel(const uint32 owner_id, const uint32 bot_id, bool BotDatabase::LoadOwnerOptions(Client *owner) { - if (!owner || !owner->CharacterID()) - return false; - - query = StringFormat( - "SELECT `death_marquee`, `stats_update`, `spawn_message_enabled`, `spawn_message_type` FROM `bot_owner_options`" - " WHERE `owner_id` = '%u'", - owner->CharacterID() - ); - auto results = database.QueryDatabase(query); - if (!results.Success()) - return false; - if (!results.RowCount()) { - query = StringFormat("REPLACE INTO `bot_owner_options` (`owner_id`) VALUES ('%u')", owner->CharacterID()); - results = database.QueryDatabase(query); - + if (!owner || !owner->CharacterID()) { return false; } - auto row = results.begin(); - owner->SetBotOptionDeathMarquee((atoi(row[0]) != 0)); - owner->SetBotOptionStatsUpdate((atoi(row[1]) != 0)); - switch (atoi(row[2])) { - case 2: - owner->SetBotOptionSpawnMessageSay(); - break; - case 1: - owner->SetBotOptionSpawnMessageTell(); - break; + query = fmt::format("SELECT `option_type`, `option_value` FROM `bot_owner_options` WHERE `owner_id` = '{}'", owner->CharacterID()); + + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return false; + } + + for (auto row : results) { + + owner->SetBotOption(static_cast(atoul(row[0])), (atoul(row[1]) != 0)); + } + + return true; +} + +bool BotDatabase::SaveOwnerOption(const uint32 owner_id, size_t type, const bool flag) +{ + if (!owner_id) { + return false; + } + + switch (static_cast(type)) { + case Client::booDeathMarquee: + case Client::booStatsUpdate: + case Client::booSpawnMessageClassSpecific: { + + query = fmt::format( + "REPLACE INTO `bot_owner_options`(`owner_id`, `option_type`, `option_value`) VALUES ('{}', '{}', '{}')", + owner_id, + type, + (flag == true ? 1 : 0) + ); + + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return false; + } + + return true; + } default: - owner->SetBotOptionSpawnMessageSilent(); - break; + return false; } - owner->SetBotOptionSpawnMessageClassSpecific((atoi(row[3]) != 0)); - - return true; } -bool BotDatabase::SaveOwnerOptionDeathMarquee(const uint32 owner_id, const bool flag) +bool BotDatabase::SaveOwnerOption(const uint32 owner_id, const std::pair type, const std::pair flag) { - if (!owner_id) + if (!owner_id) { return false; + } - query = StringFormat( - "UPDATE `bot_owner_options`" - " SET `death_marquee` = '%u'" - " WHERE `owner_id` = '%u'", - (flag == true ? 1 : 0), - owner_id - ); - auto results = database.QueryDatabase(query); - if (!results.Success()) + switch (static_cast(type.first)) { + case Client::booSpawnMessageSay: + case Client::booSpawnMessageTell: { + switch (static_cast(type.second)) { + case Client::booSpawnMessageSay: + case Client::booSpawnMessageTell: { + + query = fmt::format( + "REPLACE INTO `bot_owner_options`(`owner_id`, `option_type`, `option_value`) VALUES ('{}', '{}', '{}'), ('{}', '{}', '{}')", + owner_id, + type.first, + (flag.first == true ? 1 : 0), + owner_id, + type.second, + (flag.second == true ? 1 : 0) + ); + + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return false; + } + + return true; + } + default: + return false; + } + } + default: return false; - - return true; -} - -bool BotDatabase::SaveOwnerOptionStatsUpdate(const uint32 owner_id, const bool flag) -{ - if (!owner_id) - return false; - - query = StringFormat( - "UPDATE `bot_owner_options`" - " SET `stats_update` = '%u'" - " WHERE `owner_id` = '%u'", - (flag == true ? 1 : 0), - owner_id - ); - auto results = database.QueryDatabase(query); - if (!results.Success()) - return false; - - return true; -} - -bool BotDatabase::SaveOwnerOptionSpawnMessage(const uint32 owner_id, const bool say, const bool tell, const bool class_specific) -{ - if (!owner_id) - return false; - - uint8 enabled_value = 0; - if (say) - enabled_value = 2; - else if (tell) - enabled_value = 1; - - uint8 type_value = 0; - if (class_specific) - type_value = 1; - - query = StringFormat( - "UPDATE `bot_owner_options`" - " SET" - " `spawn_message_enabled` = '%u'," - " `spawn_message_type` = '%u'" - " WHERE `owner_id` = '%u'", - enabled_value, - type_value, - owner_id - ); - auto results = database.QueryDatabase(query); - if (!results.Success()) - return false; - - return true; + } } diff --git a/zone/bot_database.h b/zone/bot_database.h index 0c18eade5..82cf68f09 100644 --- a/zone/bot_database.h +++ b/zone/bot_database.h @@ -43,6 +43,8 @@ class BotDatabase { public: bool LoadBotCommandSettings(std::map>> &bot_command_settings); + bool UpdateInjectedBotCommandSettings(const std::vector> &injected); + bool UpdateOrphanedBotCommandSettings(const std::vector &orphaned); bool LoadBotSpellCastingChances(); @@ -50,7 +52,7 @@ public: bool QueryNameAvailablity(const std::string& bot_name, bool& available_flag); bool QueryBotCount(const uint32 owner_id, uint32& bot_count); bool LoadQuestableSpawnCount(const uint32 owner_id, int& spawn_count); - bool LoadBotsList(const uint32 owner_id, std::list& bots_list); + bool LoadBotsList(const uint32 owner_id, std::list& bots_list, bool ByAccount = false); bool LoadOwnerID(const std::string& bot_name, uint32& owner_id); bool LoadOwnerID(const uint32 bot_id, uint32& owner_id); @@ -139,10 +141,9 @@ public: bool SaveStopMeleeLevel(const uint32 owner_id, const uint32 bot_id, const uint8 sml_value); bool LoadOwnerOptions(Client *owner); - bool SaveOwnerOptionDeathMarquee(const uint32 owner_id, const bool flag); - bool SaveOwnerOptionStatsUpdate(const uint32 owner_id, const bool flag); - bool SaveOwnerOptionSpawnMessage(const uint32 owner_id, const bool say, const bool tell, const bool class_specific); - + bool SaveOwnerOption(const uint32 owner_id, size_t type, const bool flag); + bool SaveOwnerOption(const uint32 owner_id, const std::pair type, const std::pair flag); + /* Bot bot-group functions */ bool QueryBotGroupExistence(const std::string& botgroup_name, bool& extant_flag); diff --git a/zone/bot_structs.h b/zone/bot_structs.h index d54c0c042..7ba32349b 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -32,6 +32,8 @@ struct BotsAvailableList { uint8 Level; uint16 Race; uint8 Gender; + char Owner[64]; + uint32 Owner_ID; }; struct BotGroup { diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 3ec7e7202..1839e91e4 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -1134,7 +1134,7 @@ bool Bot::AI_PursueCastCheck() { AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - Log(Logs::Detail, Logs::AI, "Bot Engaged (pursuing) autocast check triggered. Trying to cast offensive spells."); + LogAI("Bot Engaged (pursuing) autocast check triggered. Trying to cast offensive spells"); if(!AICastSpell(GetTarget(), 100, SpellType_Snare)) { if(!AICastSpell(GetTarget(), 100, SpellType_Lifetap)) { @@ -1162,7 +1162,7 @@ bool Bot::AI_IdleCastCheck() { if (AIautocastspell_timer->Check(false)) { #if BotAI_DEBUG_Spells >= 25 - Log(Logs::Detail, Logs::AI, "Bot Non-Engaged autocast check triggered: %s", this->GetCleanName()); + LogAI("Bot Non-Engaged autocast check triggered: [{}]", this->GetCleanName()); #endif AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. @@ -1310,7 +1310,7 @@ bool Bot::AI_EngagedCastCheck() { EQEmu::constants::StanceType botStance = GetBotStance(); bool mayGetAggro = HasOrMayGetAggro(); - Log(Logs::Detail, Logs::AI, "Engaged autocast check triggered (BOTS). Trying to cast healing spells then maybe offensive spells."); + LogAI("Engaged autocast check triggered (BOTS). Trying to cast healing spells then maybe offensive spells"); if(botClass == CLERIC) { if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { @@ -1560,11 +1560,11 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) { } #if BotAI_DEBUG_Spells >= 10 - Log(Logs::Detail, Logs::AI, "Bot::AIHealRotation: heal spellid = %u, fastheals = %c, casterlevel = %u", + LogAI("Bot::AIHealRotation: heal spellid = [{}], fastheals = [{}], casterlevel = [{}]", botSpell.SpellId, ((useFastHeals) ? ('T') : ('F')), GetLevel()); #endif #if BotAI_DEBUG_Spells >= 25 - Log(Logs::Detail, Logs::AI, "Bot::AIHealRotation: target = %s, current_time = %u, donthealmebefore = %u", tar->GetCleanName(), Timer::GetCurrentTime(), tar->DontHealMeBefore()); + LogAI("Bot::AIHealRotation: target = [{}], current_time = [{}], donthealmebefore = [{}]", tar->GetCleanName(), Timer::GetCurrentTime(), tar->DontHealMeBefore()); #endif // If there is still no spell id, then there isn't going to be one so we are done diff --git a/zone/client.cpp b/zone/client.cpp index c1293be3f..6a0a10f5d 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -347,7 +347,11 @@ Client::Client(EQStreamInterface* ieqs) dev_tools_window_enabled = true; #ifdef BOTS - bot_owner_options = DefaultBotOwnerOptions; + bot_owner_options[booDeathMarquee] = false; + bot_owner_options[booStatsUpdate] = false; + bot_owner_options[booSpawnMessageSay] = false; + bot_owner_options[booSpawnMessageTell] = true; + bot_owner_options[booSpawnMessageClassSpecific] = true; #endif AI_Init(); @@ -377,7 +381,7 @@ Client::~Client() { ToggleBuyerMode(false); if(conn_state != ClientConnectFinished) { - Log(Logs::General, Logs::None, "Client '%s' was destroyed before reaching the connected state:", GetName()); + LogDebug("Client [{}] was destroyed before reaching the connected state:", GetName()); ReportConnectingState(); } @@ -538,31 +542,31 @@ void Client::SendLogoutPackets() { void Client::ReportConnectingState() { switch(conn_state) { case NoPacketsReceived: //havent gotten anything - Log(Logs::General, Logs::None, "Client has not sent us an initial zone entry packet."); + LogDebug("Client has not sent us an initial zone entry packet"); break; case ReceivedZoneEntry: //got the first packet, loading up PP - Log(Logs::General, Logs::None, "Client sent initial zone packet, but we never got their player info from the database."); + LogDebug("Client sent initial zone packet, but we never got their player info from the database"); break; case PlayerProfileLoaded: //our DB work is done, sending it - Log(Logs::General, Logs::None, "We were sending the player profile, tributes, tasks, spawns, time and weather, but never finished."); + LogDebug("We were sending the player profile, tributes, tasks, spawns, time and weather, but never finished"); break; case ZoneInfoSent: //includes PP, tributes, tasks, spawns, time and weather - Log(Logs::General, Logs::None, "We successfully sent player info and spawns, waiting for client to request new zone."); + LogDebug("We successfully sent player info and spawns, waiting for client to request new zone"); break; case NewZoneRequested: //received and sent new zone request - Log(Logs::General, Logs::None, "We received client's new zone request, waiting for client spawn request."); + LogDebug("We received client's new zone request, waiting for client spawn request"); break; case ClientSpawnRequested: //client sent ReqClientSpawn - Log(Logs::General, Logs::None, "We received the client spawn request, and were sending objects, doors, zone points and some other stuff, but never finished."); + LogDebug("We received the client spawn request, and were sending objects, doors, zone points and some other stuff, but never finished"); break; case ZoneContentsSent: //objects, doors, zone points - Log(Logs::General, Logs::None, "The rest of the zone contents were successfully sent, waiting for client ready notification."); + LogDebug("The rest of the zone contents were successfully sent, waiting for client ready notification"); break; case ClientReadyReceived: //client told us its ready, send them a bunch of crap like guild MOTD, etc - Log(Logs::General, Logs::None, "We received client ready notification, but never finished Client::CompleteConnect"); + LogDebug("We received client ready notification, but never finished Client::CompleteConnect"); break; case ClientConnectFinished: //client finally moved to finished state, were done here - Log(Logs::General, Logs::None, "Client is successfully connected."); + LogDebug("Client is successfully connected"); break; }; } @@ -692,7 +696,7 @@ bool Client::Save(uint8 iCommitNow) { database.SaveCharacterTribute(this->CharacterID(), &m_pp); SaveTaskState(); /* Save Character Task */ - Log(Logs::General, Logs::Food, "Client::Save - hunger_level: %i thirst_level: %i", m_pp.hunger_level, m_pp.thirst_level); + LogFood("Client::Save - hunger_level: [{}] thirst_level: [{}]", m_pp.hunger_level, m_pp.thirst_level); // perform snapshot before SaveCharacterData() so that m_epp will contain the updated time if (RuleB(Character, ActiveInvSnapshots) && time(nullptr) >= GetNextInvSnapshotTime()) { @@ -766,7 +770,7 @@ bool Client::SendAllPackets() { if(eqs) eqs->FastQueuePacket((EQApplicationPacket **)&cp->app, cp->ack_req); clientpackets.pop_front(); - Log(Logs::Moderate, Logs::Client_Server_Packet, "Transmitting a packet"); + Log(Logs::Moderate, Logs::PacketClientServer, "Transmitting a packet"); } return true; } @@ -814,7 +818,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s char message[4096]; strn0cpy(message, orig_message, sizeof(message)); - Log(Logs::Detail, Logs::Zone_Server, "Client::ChannelMessageReceived() Channel:%i message:'%s'", chan_num, message); + LogDebug("Client::ChannelMessageReceived() Channel:[{}] message:[{}]", chan_num, message); if (targetname == nullptr) { targetname = (!GetTarget()) ? "" : GetTarget()->GetName(); @@ -1636,7 +1640,7 @@ void Client::UpdateAdmin(bool iFromDB) { if(m_pp.gm) { - Log(Logs::Moderate, Logs::Zone_Server, "%s - %s is a GM", __FUNCTION__ , GetName()); + LogInfo("[{}] - [{}] is a GM", __FUNCTION__ , GetName()); // no need for this, having it set in pp you already start as gm // and it's also set in your spawn packet so other people see it too // SendAppearancePacket(AT_GM, 1, false); @@ -2067,7 +2071,7 @@ void Client::ReadBook(BookRequest_Struct *book) { if (booktxt2[0] != '\0') { #if EQDEBUG >= 6 - Log(Logs::General, Logs::Normal, "Client::ReadBook() textfile:%s Text:%s", txtfile, booktxt2.c_str()); + LogInfo("Client::ReadBook() textfile:[{}] Text:[{}]", txtfile, booktxt2.c_str()); #endif auto outapp = new EQApplicationPacket(OP_ReadBook, length + sizeof(BookText_Struct)); @@ -2287,7 +2291,7 @@ void Client::AddMoneyToPP(uint64 copper, bool updateclient){ SaveCurrency(); - Log(Logs::General, Logs::None, "Client::AddMoneyToPP() %s should have: plat:%i gold:%i silver:%i copper:%i", GetName(), m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); + LogDebug("Client::AddMoneyToPP() [{}] should have: plat:[{}] gold:[{}] silver:[{}] copper:[{}]", GetName(), m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); } void Client::EVENT_ITEM_ScriptStopReturn(){ @@ -2327,7 +2331,7 @@ void Client::AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 plat SaveCurrency(); #if (EQDEBUG>=5) - Log(Logs::General, Logs::None, "Client::AddMoneyToPP() %s should have: plat:%i gold:%i silver:%i copper:%i", + LogDebug("Client::AddMoneyToPP() [{}] should have: plat:[{}] gold:[{}] silver:[{}] copper:[{}]", GetName(), m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); #endif } @@ -2419,13 +2423,13 @@ bool Client::CheckIncreaseSkill(EQEmu::skills::SkillType skillid, Mob *against_w if(zone->random.Real(0, 99) < Chance) { SetSkill(skillid, GetRawSkill(skillid) + 1); - Log(Logs::Detail, Logs::Skills, "Skill %d at value %d successfully gain with %d%%chance (mod %d)", skillid, skillval, Chance, chancemodi); + LogSkills("Skill [{}] at value [{}] successfully gain with [{}]% chance (mod [{}])", skillid, skillval, Chance, chancemodi); return true; } else { - Log(Logs::Detail, Logs::Skills, "Skill %d at value %d failed to gain with %d%%chance (mod %d)", skillid, skillval, Chance, chancemodi); + LogSkills("Skill [{}] at value [{}] failed to gain with [{}]% chance (mod [{}])", skillid, skillval, Chance, chancemodi); } } else { - Log(Logs::Detail, Logs::Skills, "Skill %d at value %d cannot increase due to maxmum %d", skillid, skillval, maxskill); + LogSkills("Skill [{}] at value [{}] cannot increase due to maxmum [{}]", skillid, skillval, maxskill); } return false; } @@ -2446,10 +2450,10 @@ void Client::CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill) { if(zone->random.Real(0,100) < Chance) { // if they make the roll IncreaseLanguageSkill(langid); // increase the language skill by 1 - Log(Logs::Detail, Logs::Skills, "Language %d at value %d successfully gain with %.4f%%chance", langid, LangSkill, Chance); + LogSkills("Language [{}] at value [{}] successfully gain with [{}] % chance", langid, LangSkill, Chance); } else - Log(Logs::Detail, Logs::Skills, "Language %d at value %d failed to gain with %.4f%%chance", langid, LangSkill, Chance); + LogSkills("Language [{}] at value [{}] failed to gain with [{}] % chance", langid, LangSkill, Chance); } } @@ -2558,7 +2562,7 @@ uint16 Client::GetMaxSkillAfterSpecializationRules(EQEmu::skills::SkillType skil Save(); - Log(Logs::General, Logs::Normal, "Reset %s's caster specialization skills to 1. " + LogInfo("Reset [{}]'s caster specialization skills to 1" "Too many specializations skills were above 50.", GetCleanName()); } @@ -2590,7 +2594,7 @@ void Client::SetPVP(bool toggle, bool message) { void Client::Kick(const std::string &reason) { client_state = CLIENT_KICKED; - Log(Logs::General, Logs::Client_Login, "Client [%s] kicked, reason [%s]", GetCleanName(), reason.c_str()); + LogClientLogin("Client [{}] kicked, reason [{}]", GetCleanName(), reason.c_str()); } void Client::WorldKick() { @@ -4915,14 +4919,14 @@ void Client::HandleLDoNOpen(NPC *target) { if(target->GetClass() != LDON_TREASURE) { - Log(Logs::General, Logs::None, "%s tried to open %s but %s was not a treasure chest.", + LogDebug("[{}] tried to open [{}] but [{}] was not a treasure chest", GetName(), target->GetName(), target->GetName()); return; } if(DistanceSquaredNoZ(m_Position, target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) { - Log(Logs::General, Logs::None, "%s tried to open %s but %s was out of range", + LogDebug("[{}] tried to open [{}] but [{}] was out of range", GetName(), target->GetName(), target->GetName()); Message(Chat::Red, "Treasure chest out of range."); return; @@ -6234,7 +6238,7 @@ void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_overrid PetRecord record; if(!database.GetPetEntry(spells[spell_id].teleport_zone, &record)) { - Log(Logs::General, Logs::Error, "Unknown doppelganger spell id: %d, check pets table", spell_id); + LogError("Unknown doppelganger spell id: [{}], check pets table", spell_id); Message(Chat::Red, "Unable to find data for pet %s", spells[spell_id].teleport_zone); return; } @@ -6248,7 +6252,7 @@ void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_overrid const NPCType *npc_type = database.LoadNPCTypesData(pet.npc_id); if(npc_type == nullptr) { - Log(Logs::General, Logs::Error, "Unknown npc type for doppelganger spell id: %d", spell_id); + LogError("Unknown npc type for doppelganger spell id: [{}]", spell_id); Message(0,"Unable to find pet!"); return; } @@ -8359,15 +8363,14 @@ void Client::Consume(const EQEmu::ItemData *item, uint8 type, int16 slot, bool a m_pp.hunger_level += increase; - Log(Logs::General, Logs::Food, "Consuming food, points added to hunger_level: %i - current_hunger: %i", - increase, m_pp.hunger_level); + LogFood("Consuming food, points added to hunger_level: [{}] - current_hunger: [{}]", increase, m_pp.hunger_level); DeleteItemInInventory(slot, 1, false); if (!auto_consume) // no message if the client consumed for us entity_list.MessageCloseString(this, true, 50, 0, EATING_MESSAGE, GetName(), item->Name); - Log(Logs::General, Logs::Food, "Eating from slot: %i", (int)slot); + LogFood("Eating from slot: [{}]", (int)slot); } else { increase = mod_drink_value(item, increase); @@ -8379,13 +8382,12 @@ void Client::Consume(const EQEmu::ItemData *item, uint8 type, int16 slot, bool a DeleteItemInInventory(slot, 1, false); - Log(Logs::General, Logs::Food, "Consuming drink, points added to thirst_level: %i current_thirst: %i", - increase, m_pp.thirst_level); + LogFood("Consuming drink, points added to thirst_level: [{}] current_thirst: [{}]", increase, m_pp.thirst_level); if (!auto_consume) // no message if the client consumed for us entity_list.MessageCloseString(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name); - Log(Logs::General, Logs::Food, "Drinking from slot: %i", (int)slot); + LogFood("Drinking from slot: [{}]", (int)slot); } } @@ -9134,4 +9136,24 @@ glm::vec4 &Client::GetLastPositionBeforeBulkUpdate() void Client::SetLastPositionBeforeBulkUpdate(glm::vec4 in_last_position_before_bulk_update) { Client::last_position_before_bulk_update = in_last_position_before_bulk_update; -} \ No newline at end of file +} + +#ifdef BOTS + +bool Client::GetBotOption(BotOwnerOption boo) const { + + if (boo < _booCount) { + return bot_owner_options[boo]; + } + + return false; +} + +void Client::SetBotOption(BotOwnerOption boo, bool flag) { + + if (boo < _booCount) { + bot_owner_options[boo] = flag; + } +} + +#endif diff --git a/zone/client.h b/zone/client.h index 88bee0b03..cbbf3c40e 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1399,6 +1399,7 @@ private: uint32 WID; uint32 account_id; char account_name[30]; + char loginserver[64]; uint32 lsaccountid; char lskey[30]; int16 admin; @@ -1626,39 +1627,26 @@ private: int client_max_level; #ifdef BOTS - struct BotOwnerOptions { - bool death_marquee; - bool stats_update; - bool spawn_message_say; - bool spawn_message_tell; - bool spawn_message_class_specific; - }; + - BotOwnerOptions bot_owner_options; - - const BotOwnerOptions DefaultBotOwnerOptions = { - false, // death_marquee - false, // stats_update - false, // spawn_message_say - true, // spawn_message_tell - true // spawn_message_class_specific - }; + public: - void SetBotOptionDeathMarquee(bool flag) { bot_owner_options.death_marquee = flag; } - void SetBotOptionStatsUpdate(bool flag) { bot_owner_options.stats_update = flag; } - void SetBotOptionSpawnMessageSay() { bot_owner_options.spawn_message_say = true; bot_owner_options.spawn_message_tell = false; } - void SetBotOptionSpawnMessageTell() { bot_owner_options.spawn_message_say = false; bot_owner_options.spawn_message_tell = true; } - void SetBotOptionSpawnMessageSilent() { bot_owner_options.spawn_message_say = false; bot_owner_options.spawn_message_tell = false; } - void SetBotOptionSpawnMessageClassSpecific(bool flag) { bot_owner_options.spawn_message_class_specific = flag; } + enum BotOwnerOption : size_t { + booDeathMarquee, + booStatsUpdate, + booSpawnMessageSay, + booSpawnMessageTell, + booSpawnMessageClassSpecific, + _booCount + }; - bool GetBotOptionDeathMarquee() const { return bot_owner_options.death_marquee; } - bool GetBotOptionStatsUpdate() const { return bot_owner_options.stats_update; } - bool GetBotOptionSpawnMessageSay() const { return bot_owner_options.spawn_message_say; } - bool GetBotOptionSpawnMessageTell() const { return bot_owner_options.spawn_message_tell; } - bool GetBotOptionSpawnMessageClassSpecific() const { return bot_owner_options.spawn_message_class_specific; } + bool GetBotOption(BotOwnerOption boo) const; + void SetBotOption(BotOwnerOption boo, bool flag = true); + +private: + bool bot_owner_options[_booCount]; -private: #endif }; diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index 1f9049262..1b8735d7d 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -576,7 +576,7 @@ int32 Client::CalcMaxMana() break; } default: { - Log(Logs::Detail, Logs::Spells, "Invalid Class '%c' in CalcMaxMana", GetCasterClass()); + LogSpells("Invalid Class [{}] in CalcMaxMana", GetCasterClass()); max_mana = 0; break; } @@ -594,7 +594,7 @@ int32 Client::CalcMaxMana() current_mana = curMana_cap; } } - Log(Logs::Detail, Logs::Spells, "Client::CalcMaxMana() called for %s - returning %d", GetName(), max_mana); + LogSpells("Client::CalcMaxMana() called for [{}] - returning [{}]", GetName(), max_mana); return max_mana; } @@ -678,13 +678,13 @@ int32 Client::CalcBaseMana() break; } default: { - Log(Logs::General, Logs::None, "Invalid Class '%c' in CalcMaxMana", GetCasterClass()); + LogDebug("Invalid Class [{}] in CalcMaxMana", GetCasterClass()); max_m = 0; break; } } #if EQDEBUG >= 11 - Log(Logs::General, Logs::None, "Client::CalcBaseMana() called for %s - returning %d", GetName(), max_m); + LogDebug("Client::CalcBaseMana() called for [{}] - returning [{}]", GetName(), max_m); #endif return max_m; } @@ -1597,8 +1597,9 @@ uint32 Mob::GetInstrumentMod(uint16 spell_id) const effectmod = 10; if (!nocap && effectmod > effectmodcap) // if the cap is calculated to be 0 using new rules, no cap. effectmod = effectmodcap; - Log(Logs::Detail, Logs::Spells, "%s::GetInstrumentMod() spell=%d mod=%d modcap=%d\n", GetName(), spell_id, - effectmod, effectmodcap); + + LogSpells("[{}]::GetInstrumentMod() spell=[{}] mod=[{}] modcap=[{}]\n", GetName(), spell_id, effectmod, effectmodcap); + return effectmod; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ccb4ee96a..2b5702af4 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -420,14 +420,14 @@ int Client::HandlePacket(const EQApplicationPacket *app) if (LogSys.log_settings[Logs::LogCategory::Netcode].is_category_enabled == 1) { char buffer[64]; app->build_header_dump(buffer); - Log(Logs::Detail, Logs::Client_Server_Packet, "Dispatch opcode: %s", buffer); + Log(Logs::Detail, Logs::PacketClientServer, "Dispatch opcode: %s", buffer); } - if (LogSys.log_settings[Logs::Client_Server_Packet].is_category_enabled == 1) - Log(Logs::General, Logs::Client_Server_Packet, "[%s - 0x%04x] [Size: %u]", OpcodeManager::EmuToName(app->GetOpcode()), app->GetOpcode(), app->Size()); + if (LogSys.log_settings[Logs::PacketClientServer].is_category_enabled == 1) + Log(Logs::General, Logs::PacketClientServer, "[%s - 0x%04x] [Size: %u]", OpcodeManager::EmuToName(app->GetOpcode()), app->GetOpcode(), app->Size()); - if (LogSys.log_settings[Logs::Client_Server_Packet_With_Dump].is_category_enabled == 1) - Log(Logs::General, Logs::Client_Server_Packet_With_Dump, "[%s - 0x%04x] [Size: %u] %s", OpcodeManager::EmuToName(app->GetOpcode()), app->GetOpcode(), app->Size(), DumpPacketToString(app).c_str()); + if (LogSys.log_settings[Logs::PacketClientServerWithDump].is_category_enabled == 1) + Log(Logs::General, Logs::PacketClientServerWithDump, "[%s - 0x%04x] [Size: %u] %s", OpcodeManager::EmuToName(app->GetOpcode()), app->GetOpcode(), app->Size(), DumpPacketToString(app).c_str()); EmuOpcode opcode = app->GetOpcode(); if (opcode == OP_AckPacket) { @@ -446,11 +446,6 @@ int Client::HandlePacket(const EQApplicationPacket *app) args.push_back(const_cast(app)); parse->EventPlayer(EVENT_UNHANDLED_OPCODE, this, "", 1, &args); -#if EQDEBUG >= 10 - Log(Logs::General, Logs::Error, "HandlePacket() Opcode error: Unexpected packet during CLIENT_CONNECTING: opcode:" - " %s (#%d eq=0x%04x), size: %i", OpcodeNames[opcode], opcode, 0, app->size); - DumpPacket(app); -#endif break; } @@ -475,10 +470,10 @@ int Client::HandlePacket(const EQApplicationPacket *app) args.push_back(const_cast(app)); parse->EventPlayer(EVENT_UNHANDLED_OPCODE, this, "", 0, &args); - if (LogSys.log_settings[Logs::Client_Server_Packet_Unhandled].is_category_enabled == 1) { + if (LogSys.log_settings[Logs::PacketClientServerUnhandled].is_category_enabled == 1) { char buffer[64]; app->build_header_dump(buffer); - Log(Logs::General, Logs::Client_Server_Packet_Unhandled, "%s %s", buffer, DumpPacketToString(app).c_str()); + Log(Logs::General, Logs::PacketClientServerUnhandled, "%s %s", buffer, DumpPacketToString(app).c_str()); } break; } @@ -492,7 +487,7 @@ int Client::HandlePacket(const EQApplicationPacket *app) case CLIENT_LINKDEAD: break; default: - Log(Logs::General, Logs::None, "Unknown client_state: %d\n", client_state); + LogDebug("Unknown client_state: [{}]\n", client_state); break; } @@ -793,7 +788,7 @@ void Client::CompleteConnect() //enforce some rules.. if (!CanBeInZone()) { - Log(Logs::Detail, Logs::None, "[CLIENT] Kicking char from zone, not allowed here"); + LogDebug("[CLIENT] Kicking char from zone, not allowed here"); GoToSafeCoords(database.GetZoneID("arena"), 0); return; } @@ -927,7 +922,7 @@ return; void Client::Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app) { if (app->size != sizeof(ApproveZone_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size on OP_ApproveZone: Expected %i, Got %i", + LogError("Invalid size on OP_ApproveZone: Expected [{}], Got [{}]", sizeof(ApproveZone_Struct), app->size); return; } @@ -940,14 +935,14 @@ void Client::Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app) void Client::Handle_Connect_OP_ClientError(const EQApplicationPacket *app) { if (app->size != sizeof(ClientError_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size on OP_ClientError: Expected %i, Got %i", + LogError("Invalid size on OP_ClientError: Expected [{}], Got [{}]", sizeof(ClientError_Struct), app->size); return; } // Client reporting error to server ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; - Log(Logs::General, Logs::Error, "Client error: %s", error->character_name); - Log(Logs::General, Logs::Error, "Error message: %s", error->message); + LogError("Client error: [{}]", error->character_name); + LogError("Error message: [{}]", error->message); Message(Chat::Red, error->message); #if (EQDEBUG>=5) DumpPacket(app); @@ -1085,7 +1080,7 @@ void Client::Handle_Connect_OP_SendTributes(const EQApplicationPacket *app) void Client::Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app) { if (app->size != sizeof(SetServerFilter_Struct)) { - Log(Logs::General, Logs::Error, "Received invalid sized OP_SetServerFilter"); + LogError("Received invalid sized OP_SetServerFilter"); DumpPacket(app); return; } @@ -1102,7 +1097,7 @@ void Client::Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app) void Client::Handle_Connect_OP_TGB(const EQApplicationPacket *app) { if (app->size != sizeof(uint32)) { - Log(Logs::General, Logs::Error, "Invalid size on OP_TGB: Expected %i, Got %i", + LogError("Invalid size on OP_TGB: Expected [{}], Got [{}]", sizeof(uint32), app->size); return; } @@ -1169,7 +1164,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) */ Client* client = entity_list.GetClientByName(cze->char_name); if (!zone->GetAuth(ip, cze->char_name, &WID, &account_id, &character_id, &admin, lskey, &tellsoff)) { - Log(Logs::General, Logs::Client_Login, "%s failed zone auth check.", cze->char_name); + LogClientLogin("[{}] failed zone auth check", cze->char_name); if (nullptr != client) { client->Save(); client->Kick("Failed auth check"); @@ -1183,7 +1178,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) struct in_addr ghost_addr; ghost_addr.s_addr = eqs->GetRemoteIP(); - Log(Logs::General, Logs::Error, "Ghosting client: Account ID:%i Name:%s Character:%s IP:%s", + LogError("Ghosting client: Account ID:[{}] Name:[{}] Character:[{}] IP:[{}]", client->AccountID(), client->AccountName(), client->GetName(), inet_ntoa(ghost_addr)); client->Save(); client->Disconnect(); @@ -1205,16 +1200,17 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) database.LoadCharacterFactionValues(cid, factionvalues); /* Load Character Account Data: Temp until I move */ - query = StringFormat("SELECT `status`, `name`, `lsaccount_id`, `gmspeed`, `revoked`, `hideme`, `time_creation` FROM `account` WHERE `id` = %u", this->AccountID()); + query = StringFormat("SELECT `status`, `name`, `ls_id`, `lsaccount_id`, `gmspeed`, `revoked`, `hideme`, `time_creation` FROM `account` WHERE `id` = %u", this->AccountID()); auto results = database.QueryDatabase(query); for (auto row = results.begin(); row != results.end(); ++row) { admin = atoi(row[0]); - strncpy(account_name, row[1], 30); - lsaccountid = atoi(row[2]); - gmspeed = atoi(row[3]); - revoked = atoi(row[4]); - gm_hide_me = atoi(row[5]); - account_creation = atoul(row[6]); + strn0cpy(account_name, row[1], sizeof(account_name)); + strn0cpy(loginserver, row[2], sizeof(loginserver)); + lsaccountid = atoi(row[3]); + gmspeed = atoi(row[4]); + revoked = atoi(row[5]); + gm_hide_me = atoi(row[6]); + account_creation = atoul(row[7]); } /* Load Character Data */ @@ -1423,7 +1419,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) } if (!database.LoadAlternateAdvancement(this)) { - Log(Logs::General, Logs::Error, "Error loading AA points for %s", GetName()); + LogError("Error loading AA points for [{}]", GetName()); } if (SPDAT_RECORDS > 0) { @@ -1555,7 +1551,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) p_timers.SetCharID(CharacterID()); if (!p_timers.Load(&database)) { - Log(Logs::General, Logs::Error, "Unable to load ability timers from the database for %s (%i)!", GetCleanName(), CharacterID()); + LogError("Unable to load ability timers from the database for [{}] ([{}])!", GetCleanName(), CharacterID()); } /* Load Spell Slot Refresh from Currently Memoried Spells */ @@ -1735,16 +1731,16 @@ void Client::Handle_0x0193(const EQApplicationPacket *app) void Client::Handle_OP_AAAction(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::AA, "Received OP_AAAction"); + LogAA("Received OP_AAAction"); if (app->size != sizeof(AA_Action)) { - Log(Logs::General, Logs::AA, "Error! OP_AAAction size didnt match!"); + LogAA("Error! OP_AAAction size didnt match!"); return; } AA_Action* action = (AA_Action*)app->pBuffer; if (action->action == aaActionActivate) {//AA Hotkey - Log(Logs::Detail, Logs::AA, "Activating AA %d", action->ability); + LogAA("Activating AA [{}]", action->ability); ActivateAlternateAdvancementAbility(action->ability, action->target_id); } else if (action->action == aaActionBuy) { @@ -1769,7 +1765,7 @@ void Client::Handle_OP_AAAction(const EQApplicationPacket *app) SendAlternateAdvancementTable(); } else { - Log(Logs::General, Logs::AA, "Unknown AA action : %u %u %u %d", action->action, action->ability, action->target_id, action->exp_value); + LogAA("Unknown AA action : [{}] [{}] [{}] [{}]", action->action, action->ability, action->target_id, action->exp_value); } } @@ -1777,8 +1773,7 @@ void Client::Handle_OP_AcceptNewTask(const EQApplicationPacket *app) { if (app->size != sizeof(AcceptNewTask_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_AcceptNewTask expected %i got %i", - sizeof(AcceptNewTask_Struct), app->size); + LogDebug("Size mismatch in OP_AcceptNewTask expected [{}] got [{}]", sizeof(AcceptNewTask_Struct), app->size); DumpPacket(app); return; } @@ -1792,7 +1787,7 @@ void Client::Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app) { if (app->size < sizeof(EntityId_Struct)) { - Log(Logs::General, Logs::Error, "Handle_OP_AdventureInfoRequest had a packet that was too small."); + LogError("Handle_OP_AdventureInfoRequest had a packet that was too small"); return; } EntityId_Struct* ent = (EntityId_Struct*)app->pBuffer; @@ -1847,7 +1842,7 @@ void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) { if (app->size != sizeof(Adventure_Purchase_Struct)) { - Log(Logs::General, Logs::Error, "OP size error: OP_AdventureMerchantPurchase expected:%i got:%i", sizeof(Adventure_Purchase_Struct), app->size); + LogError("OP size error: OP_AdventureMerchantPurchase expected:[{}] got:[{}]", sizeof(Adventure_Purchase_Struct), app->size); return; } @@ -2027,7 +2022,7 @@ void Client::Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app) { if (app->size != sizeof(AdventureMerchant_Struct)) { - Log(Logs::General, Logs::Error, "OP size error: OP_AdventureMerchantRequest expected:%i got:%i", sizeof(AdventureMerchant_Struct), app->size); + LogError("OP size error: OP_AdventureMerchantRequest expected:[{}] got:[{}]", sizeof(AdventureMerchant_Struct), app->size); return; } std::stringstream ss(std::stringstream::in | std::stringstream::out); @@ -2116,8 +2111,7 @@ void Client::Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app) { if (app->size != sizeof(Adventure_Sell_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch on OP_AdventureMerchantSell: got %u expected %u", - app->size, sizeof(Adventure_Sell_Struct)); + LogDebug("Size mismatch on OP_AdventureMerchantSell: got [{}] expected [{}]", app->size, sizeof(Adventure_Sell_Struct)); DumpPacket(app); return; } @@ -2249,7 +2243,7 @@ void Client::Handle_OP_AdventureRequest(const EQApplicationPacket *app) { if (app->size < sizeof(AdventureRequest_Struct)) { - Log(Logs::General, Logs::Error, "Handle_OP_AdventureRequest had a packet that was too small."); + LogError("Handle_OP_AdventureRequest had a packet that was too small"); return; } @@ -2388,7 +2382,7 @@ void Client::Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app) void Client::Handle_OP_AggroMeterLockTarget(const EQApplicationPacket *app) { if (app->size < sizeof(uint32)) { - Log(Logs::General, Logs::Error, "Handle_OP_AggroMeterLockTarget had a packet that was too small."); + LogError("Handle_OP_AggroMeterLockTarget had a packet that was too small"); return; } @@ -2790,9 +2784,7 @@ void Client::Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app) void Client::Handle_OP_Animation(const EQApplicationPacket *app) { if (app->size != sizeof(Animation_Struct)) { - Log(Logs::General, Logs::Error, "Received invalid sized " - "OP_Animation: got %d, expected %d", app->size, - sizeof(Animation_Struct)); + LogError("Received invalid sized OP_Animation: got [{}], expected [{}]", app->size, sizeof(Animation_Struct)); DumpPacket(app); return; } @@ -2808,7 +2800,7 @@ void Client::Handle_OP_Animation(const EQApplicationPacket *app) void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) { if (app->size != sizeof(ApplyPoison_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_ApplyPoison, size=%i, expected %i", app->size, sizeof(ApplyPoison_Struct)); + LogError("Wrong size: OP_ApplyPoison, size=[{}], expected [{}]", app->size, sizeof(ApplyPoison_Struct)); DumpPacket(app); return; } @@ -2852,12 +2844,16 @@ void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) // Poisons that don't proc until a level higher than the // rogue simply won't apply at all, no skill check done. - if (ChanceRoll < (.9 + GetLevel()/1000)) { + uint16 poison_skill = GetSkill(EQEmu::skills::SkillApplyPoison); + + if (ChanceRoll < (.75 + poison_skill / 1000)) { ApplyPoisonSuccessResult = 1; - AddProcToWeapon(poison->Proc.Effect, false, - (GetDEX() / 100) + 103); + AddProcToWeapon(poison->Proc.Effect, false, (GetDEX() / 100) + 103); } } + else { + Message(Chat::Red, "A piercing weapon must be wielded to apply poison."); + } // Live always deletes the item, success or failure. Even if too high. DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true); @@ -2874,7 +2870,7 @@ void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) void Client::Handle_OP_Assist(const EQApplicationPacket *app) { if (app->size != sizeof(EntityId_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_Assist expected %i got %i", sizeof(EntityId_Struct), app->size); + LogDebug("Size mismatch in OP_Assist expected [{}] got [{}]", sizeof(EntityId_Struct), app->size); return; } @@ -2907,7 +2903,7 @@ void Client::Handle_OP_Assist(const EQApplicationPacket *app) void Client::Handle_OP_AssistGroup(const EQApplicationPacket *app) { if (app->size != sizeof(EntityId_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_AssistGroup expected %i got %i", sizeof(EntityId_Struct), app->size); + LogDebug("Size mismatch in OP_AssistGroup expected [{}] got [{}]", sizeof(EntityId_Struct), app->size); return; } QueuePacket(app); @@ -2920,8 +2916,7 @@ void Client::Handle_OP_AugmentInfo(const EQApplicationPacket *app) // Some clients this seems to nuke the charm text (ex. Adventurer's Stone) if (app->size != sizeof(AugmentInfo_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_AugmentInfo expected %i got %i", - sizeof(AugmentInfo_Struct), app->size); + LogDebug("Size mismatch in OP_AugmentInfo expected [{}] got [{}]", sizeof(AugmentInfo_Struct), app->size); DumpPacket(app); return; } @@ -2939,7 +2934,7 @@ void Client::Handle_OP_AugmentInfo(const EQApplicationPacket *app) void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) { if (app->size != sizeof(AugmentItem_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size for AugmentItem_Struct: Expected: %i, Got: %i", + LogError("Invalid size for AugmentItem_Struct: Expected: [{}], Got: [{}]", sizeof(AugmentItem_Struct), app->size); return; } @@ -2962,7 +2957,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) EQEmu::ItemInstance *itemOneToPush = nullptr, *itemTwoToPush = nullptr; - //Log(Logs::DebugLevel::Moderate, Logs::Debug, "cslot: %i aslot: %i cidx: %i aidx: %i act: %i dest: %i", + //Log(Logs::DebugLevel::Moderate, Logs::Debug, "cslot: [{}] aslot: [{}] cidx: [{}] aidx: [{}] act: [{}] dest: [{}]", // in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id); EQEmu::ItemInstance *tobe_auged = nullptr, *old_aug = nullptr, *new_aug = nullptr, *aug = nullptr, *solvent = nullptr; @@ -2995,7 +2990,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) { old_aug = tobe_auged->GetAugment(in_augment->augment_index); if (!old_aug || old_aug->GetItem()->AugDistiller != 0) { - Log(Logs::General, Logs::Error, "Player tried to safely remove an augment without a distiller."); + LogError("Player tried to safely remove an augment without a distiller"); Message(Chat::Red, "Error: Missing an augmentation distiller for safely removing this augment."); return; } @@ -3006,20 +3001,20 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) if (!old_aug) { - Log(Logs::General, Logs::Error, "Player tried to safely remove a nonexistent augment."); + LogError("Player tried to safely remove a nonexistent augment"); Message(Chat::Red, "Error: No augment found in slot %i for safely removing.", in_augment->augment_index); return; } else if (solvent->GetItem()->ID != old_aug->GetItem()->AugDistiller) { - Log(Logs::General, Logs::Error, "Player tried to safely remove an augment with the wrong distiller (item %u vs expected %u).", solvent->GetItem()->ID, old_aug->GetItem()->AugDistiller); + LogError("Player tried to safely remove an augment with the wrong distiller (item [{}] vs expected [{}])", solvent->GetItem()->ID, old_aug->GetItem()->AugDistiller); Message(Chat::Red, "Error: Wrong augmentation distiller for safely removing this augment."); return; } } else if (solvent->GetItem()->ItemType != EQEmu::item::ItemTypePerfectedAugmentationDistiller) { - Log(Logs::General, Logs::Error, "Player tried to safely remove an augment with a non-distiller item."); + LogError("Player tried to safely remove an augment with a non-distiller item"); Message(Chat::Red, "Error: Invalid augmentation distiller for safely removing this augment."); return; } @@ -3033,7 +3028,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) if (!new_aug) // Shouldn't get the OP code without the augment on the user's cursor, but maybe it's h4x. { - Log(Logs::General, Logs::Error, "AugmentItem OpCode with 'Insert' or 'Swap' action received, but no augment on client's cursor."); + LogError("AugmentItem OpCode with 'Insert' or 'Swap' action received, but no augment on client's cursor"); Message(Chat::Red, "Error: No augment found on cursor for inserting."); return; } @@ -3100,7 +3095,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) // This is a swap. Return the old aug to the player's cursor. if (!PutItemInInventory(EQEmu::invslot::slotCursor, *itemTwoToPush, true)) { - Log(Logs::General, Logs::Error, "Problem returning old augment to player's cursor after augmentation swap."); + LogError("Problem returning old augment to player's cursor after augmentation swap"); Message(Chat::Yellow, "Error: Failed to retrieve old augment after augmentation swap!"); } } @@ -3171,7 +3166,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) // Replace it with the unaugmented item if (!PutItemInInventory(item_slot, *itemOneToPush, true)) { - Log(Logs::General, Logs::Error, "Problem returning equipment item to player's inventory after safe augment removal."); + LogError("Problem returning equipment item to player's inventory after safe augment removal"); Message(Chat::Yellow, "Error: Failed to return item after de-augmentation!"); } @@ -3185,7 +3180,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) // Drop the removed augment on the player's cursor if (!PutItemInInventory(EQEmu::invslot::slotCursor, *itemTwoToPush, true)) { - Log(Logs::General, Logs::Error, "Problem returning augment to player's cursor after safe removal."); + LogError("Problem returning augment to player's cursor after safe removal"); Message(Chat::Yellow, "Error: Failed to return augment after removal from item!"); return; } @@ -3225,7 +3220,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) if (!PutItemInInventory(item_slot, *itemOneToPush, true)) { - Log(Logs::General, Logs::Error, "Problem returning equipment item to player's inventory after augment deletion."); + LogError("Problem returning equipment item to player's inventory after augment deletion"); Message(Chat::Yellow, "Error: Failed to return item after destroying augment!"); } } @@ -3238,7 +3233,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) } break; default: // Unknown - Log(Logs::General, Logs::Inventory, "Unrecognized augmentation action - cslot: %i aslot: %i cidx: %i aidx: %i act: %i dest: %i", + LogInventory("Unrecognized augmentation action - cslot: [{}] aslot: [{}] cidx: [{}] aidx: [{}] act: [{}] dest: [{}]", in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id); break; } @@ -3254,7 +3249,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) void Client::Handle_OP_AutoAttack(const EQApplicationPacket *app) { if (app->size != 4) { - Log(Logs::General, Logs::Error, "OP size error: OP_AutoAttack expected:4 got:%i", app->size); + LogError("OP size error: OP_AutoAttack expected:4 got:[{}]", app->size); return; } @@ -3304,7 +3299,7 @@ void Client::Handle_OP_AutoAttack2(const EQApplicationPacket *app) void Client::Handle_OP_AutoFire(const EQApplicationPacket *app) { if (app->size != sizeof(bool)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_AutoFire expected %i got %i", sizeof(bool), app->size); + LogDebug("Size mismatch in OP_AutoFire expected [{}] got [{}]", sizeof(bool), app->size); DumpPacket(app); return; } @@ -3319,8 +3314,7 @@ void Client::Handle_OP_Bandolier(const EQApplicationPacket *app) // Although there are three different structs for OP_Bandolier, they are all the same size. // if (app->size != sizeof(BandolierCreate_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_Bandolier expected %i got %i", - sizeof(BandolierCreate_Struct), app->size); + LogDebug("Size mismatch in OP_Bandolier expected [{}] got [{}]", sizeof(BandolierCreate_Struct), app->size); DumpPacket(app); return; } @@ -3339,7 +3333,7 @@ void Client::Handle_OP_Bandolier(const EQApplicationPacket *app) SetBandolier(app); break; default: - Log(Logs::General, Logs::None, "Unknown Bandolier action %i", bs->Action); + LogDebug("Unknown Bandolier action [{}]", bs->Action); break; } } @@ -3348,7 +3342,7 @@ void Client::Handle_OP_BankerChange(const EQApplicationPacket *app) { if (app->size != sizeof(BankerChange_Struct) && app->size != 4) //Titanium only sends 4 Bytes for this { - Log(Logs::General, Logs::None, "Size mismatch in OP_BankerChange expected %i got %i", sizeof(BankerChange_Struct), app->size); + LogDebug("Size mismatch in OP_BankerChange expected [{}] got [{}]", sizeof(BankerChange_Struct), app->size); DumpPacket(app); return; } @@ -3433,7 +3427,7 @@ void Client::Handle_OP_Barter(const EQApplicationPacket *app) if (app->size < 4) { - Log(Logs::General, Logs::None, "OP_Barter packet below minimum expected size. The packet was %i bytes.", app->size); + LogDebug("OP_Barter packet below minimum expected size. The packet was [{}] bytes", app->size); DumpPacket(app); return; } @@ -3580,7 +3574,7 @@ void Client::Handle_OP_Barter(const EQApplicationPacket *app) default: Message(Chat::Red, "Unrecognised Barter action."); - Log(Logs::Detail, Logs::Trading, "Unrecognised Barter Action %i", Action); + LogTrading("Unrecognised Barter Action [{}]", Action); } } @@ -3588,7 +3582,7 @@ void Client::Handle_OP_Barter(const EQApplicationPacket *app) void Client::Handle_OP_BazaarInspect(const EQApplicationPacket *app) { if (app->size != sizeof(BazaarInspect_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size for BazaarInspect_Struct: Expected %i, Got %i", + LogError("Invalid size for BazaarInspect_Struct: Expected [{}], Got [{}]", sizeof(BazaarInspect_Struct), app->size); return; } @@ -3642,8 +3636,8 @@ void Client::Handle_OP_BazaarSearch(const EQApplicationPacket *app) return; } else { - Log(Logs::Detail, Logs::Trading, "Malformed BazaarSearch_Struct packe, Action %it received, ignoring..."); - Log(Logs::General, Logs::Error, "Malformed BazaarSearch_Struct packet received, ignoring...\n"); + LogTrading("Malformed BazaarSearch_Struct packe, Action [{}]t received, ignoring"); + LogError("Malformed BazaarSearch_Struct packet received, ignoring\n"); } return; @@ -3728,16 +3722,16 @@ void Client::Handle_OP_Begging(const EQApplicationPacket *app) void Client::Handle_OP_Bind_Wound(const EQApplicationPacket *app) { if (app->size != sizeof(BindWound_Struct)) { - Log(Logs::General, Logs::Error, "Size mismatch for Bind wound packet"); + LogError("Size mismatch for Bind wound packet"); DumpPacket(app); } BindWound_Struct* bind_in = (BindWound_Struct*)app->pBuffer; Mob* bindmob = entity_list.GetMob(bind_in->to); if (!bindmob) { - Log(Logs::General, Logs::Error, "Bindwound on non-exsistant mob from %s", this->GetName()); + LogError("Bindwound on non-exsistant mob from [{}]", this->GetName()); } else { - Log(Logs::General, Logs::None, "BindWound in: to:\'%s\' from=\'%s\'", bindmob->GetName(), GetName()); + LogDebug("BindWound in: to:\'[{}]\' from=\'[{}]\'", bindmob->GetName(), GetName()); BindWound(bindmob, true); } return; @@ -3750,8 +3744,7 @@ void Client::Handle_OP_BlockedBuffs(const EQApplicationPacket *app) if (app->size != sizeof(BlockedBuffs_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_BlockedBuffs expected %i got %i", - sizeof(BlockedBuffs_Struct), app->size); + LogDebug("Size mismatch in OP_BlockedBuffs expected [{}] got [{}]", sizeof(BlockedBuffs_Struct), app->size); DumpPacket(app); @@ -3845,7 +3838,7 @@ void Client::Handle_OP_BoardBoat(const EQApplicationPacket *app) // this sends unclean mob name, so capped at 64 // a_boat006 if (app->size <= 5 || app->size > 64) { - Log(Logs::General, Logs::Error, "Size mismatch in OP_BoardBoad. Expected greater than 5 less than 64, got %i", app->size); + LogError("Size mismatch in OP_BoardBoad. Expected greater than 5 less than 64, got [{}]", app->size); DumpPacket(app); return; } @@ -3868,14 +3861,14 @@ void Client::Handle_OP_Buff(const EQApplicationPacket *app) { if (app->size != sizeof(SpellBuffPacket_Struct)) { - Log(Logs::General, Logs::Error, "Size mismatch in OP_Buff. expected %i got %i", sizeof(SpellBuffPacket_Struct), app->size); + LogError("Size mismatch in OP_Buff. expected [{}] got [{}]", sizeof(SpellBuffPacket_Struct), app->size); DumpPacket(app); return; } SpellBuffPacket_Struct* sbf = (SpellBuffPacket_Struct*)app->pBuffer; uint32 spid = sbf->buff.spellid; - Log(Logs::Detail, Logs::Spells, "Client requested that buff with spell id %d be canceled.", spid); + LogSpells("Client requested that buff with spell id [{}] be canceled", spid); //something about IsDetrimentalSpell() crashes this portion of code.. //tbh we shouldn't use it anyway since this is a simple red vs blue buff check and @@ -3952,9 +3945,11 @@ void Client::Handle_OP_Camp(const EQApplicationPacket *app) #ifdef BOTS // This block is necessary to clean up any bot objects owned by a Client Bot::BotOrderCampAll(this); - auto group = GetGroup(); - if (group && group->GroupCount() < 2) - group->DisbandGroup(); + // Evidently, this is bad under certain conditions and causes crashes... + // Group and Raid code really needs to be overhauled to account for non-client types (mercs and bots) + //auto group = GetGroup(); + //if (group && group->GroupCount() < 2) + // group->DisbandGroup(); #endif if (IsLFP()) worldserver.StopLFP(CharacterID()); @@ -3972,8 +3967,7 @@ void Client::Handle_OP_CancelTask(const EQApplicationPacket *app) { if (app->size != sizeof(CancelTask_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_CancelTask expected %i got %i", - sizeof(CancelTask_Struct), app->size); + LogDebug("Size mismatch in OP_CancelTask expected [{}] got [{}]", sizeof(CancelTask_Struct), app->size); DumpPacket(app); return; } @@ -3986,7 +3980,7 @@ void Client::Handle_OP_CancelTask(const EQApplicationPacket *app) void Client::Handle_OP_CancelTrade(const EQApplicationPacket *app) { if (app->size != sizeof(CancelTrade_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_CancelTrade, size=%i, expected %i", app->size, sizeof(CancelTrade_Struct)); + LogError("Wrong size: OP_CancelTrade, size=[{}], expected [{}]", app->size, sizeof(CancelTrade_Struct)); return; } Mob* with = trade->With(); @@ -4035,7 +4029,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) m_TargetRing = glm::vec3(castspell->x_pos, castspell->y_pos, castspell->z_pos); - Log(Logs::General, Logs::Spells, "OP CastSpell: slot=%d, spell=%d, target=%d, inv=%lx", castspell->slot, castspell->spell_id, castspell->target_id, (unsigned long)castspell->inventoryslot); + LogSpells("OP CastSpell: slot [{}] spell [{}] target [{}] inv [{}]", castspell->slot, castspell->spell_id, castspell->target_id, (unsigned long)castspell->inventoryslot); CastingSlot slot = static_cast(castspell->slot); /* Memorized Spell */ @@ -4131,7 +4125,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) /* Discipline -- older clients use the same slot as items, but we translate to it's own */ else if (slot == CastingSlot::Discipline) { if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { - Log(Logs::General, Logs::Spells, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); + LogSpells("Unknown ability being used by [{}], spell being cast is: [{}]\n", GetName(), castspell->spell_id); InterruptSpell(castspell->spell_id); return; } @@ -4201,7 +4195,7 @@ void Client::Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app) if (app->size != 1) { - Log(Logs::General, Logs::None, "Size mismatch in OP_ClearBlockedBuffs expected 1 got %i", app->size); + LogDebug("Size mismatch in OP_ClearBlockedBuffs expected 1 got [{}]", app->size); DumpPacket(app); @@ -4223,8 +4217,7 @@ void Client::Handle_OP_ClearNPCMarks(const EQApplicationPacket *app) if (app->size != 0) { - Log(Logs::General, Logs::None, "Size mismatch in OP_ClearNPCMarks expected 0 got %i", - app->size); + LogDebug("Size mismatch in OP_ClearNPCMarks expected 0 got [{}]", app->size); DumpPacket(app); @@ -4245,7 +4238,7 @@ void Client::Handle_OP_ClearSurname(const EQApplicationPacket *app) void Client::Handle_OP_ClickDoor(const EQApplicationPacket *app) { if (app->size != sizeof(ClickDoor_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_ClickDoor, size=%i, expected %i", app->size, sizeof(ClickDoor_Struct)); + LogError("Wrong size: OP_ClickDoor, size=[{}], expected [{}]", app->size, sizeof(ClickDoor_Struct)); return; } ClickDoor_Struct* cd = (ClickDoor_Struct*)app->pBuffer; @@ -4270,7 +4263,7 @@ void Client::Handle_OP_ClickDoor(const EQApplicationPacket *app) void Client::Handle_OP_ClickObject(const EQApplicationPacket *app) { if (app->size != sizeof(ClickObject_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size on ClickObject_Struct: Expected %i, Got %i", + LogError("Invalid size on ClickObject_Struct: Expected [{}], Got [{}]", sizeof(ClickObject_Struct), app->size); return; } @@ -4317,7 +4310,7 @@ void Client::Handle_OP_ClickObjectAction(const EQApplicationPacket *app) else { if (app->size != sizeof(ClickObjectAction_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size on OP_ClickObjectAction: Expected %i, Got %i", + LogError("Invalid size on OP_ClickObjectAction: Expected [{}], Got [{}]", sizeof(ClickObjectAction_Struct), app->size); return; } @@ -4330,11 +4323,11 @@ void Client::Handle_OP_ClickObjectAction(const EQApplicationPacket *app) object->Close(); } else { - Log(Logs::General, Logs::Error, "Unsupported action %d in OP_ClickObjectAction", oos->open); + LogError("Unsupported action [{}] in OP_ClickObjectAction", oos->open); } } else { - Log(Logs::General, Logs::Error, "Invalid object %d in OP_ClickObjectAction", oos->drop_id); + LogError("Invalid object [{}] in OP_ClickObjectAction", oos->drop_id); } } @@ -4351,8 +4344,8 @@ void Client::Handle_OP_ClickObjectAction(const EQApplicationPacket *app) void Client::Handle_OP_ClientError(const EQApplicationPacket *app) { ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; - Log(Logs::General, Logs::Error, "Client error: %s", error->character_name); - Log(Logs::General, Logs::Error, "Error message:%s", error->message); + LogError("Client error: [{}]", error->character_name); + LogError("Error message:[{}]", error->message); return; } @@ -4372,7 +4365,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { if (app->size != sizeof(PlayerPositionUpdateClient_Struct) && app->size != (sizeof(PlayerPositionUpdateClient_Struct) + 1) ) { - Log(Logs::General, Logs::Error, "OP size error: OP_ClientUpdate expected:%i got:%i", + LogError("OP size error: OP_ClientUpdate expected:[{}] got:[{}]", sizeof(PlayerPositionUpdateClient_Struct), app->size); return; } @@ -4472,16 +4465,14 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { is_client_moving = (ppu->y_pos == m_Position.y && ppu->x_pos == m_Position.x) ? false : true; if (is_client_moving) { - Log(Logs::Detail, Logs::Normal, "ClientUpdate: Client is moving - scan timer is: %u", - client_scan_npc_aggro_timer.GetDuration()); + LogDebug("ClientUpdate: Client is moving - scan timer is: [{}]", client_scan_npc_aggro_timer.GetDuration()); if (client_scan_npc_aggro_timer.GetDuration() > 1000) { client_scan_npc_aggro_timer.Disable(); client_scan_npc_aggro_timer.Start(500); } } else { - Log(Logs::Detail, Logs::Normal, "ClientUpdate: Client is NOT moving - scan timer is: %u", - client_scan_npc_aggro_timer.GetDuration()); + LogDebug("ClientUpdate: Client is NOT moving - scan timer is: [{}]", client_scan_npc_aggro_timer.GetDuration()); if (client_scan_npc_aggro_timer.GetDuration() < 1000) { client_scan_npc_aggro_timer.Disable(); client_scan_npc_aggro_timer.Start(3000); @@ -4505,7 +4496,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { ); if (is_client_moving && is_ready_to_update) { - Log(Logs::Detail, Logs::Normal, "[%s] Client Zone Wide Position Update NPCs", GetCleanName()); + LogDebug("[[{}]] Client Zone Wide Position Update NPCs", GetCleanName()); auto &mob_movement_manager = MobMovementManager::Get(); auto &mob_list = entity_list.GetMobList(); @@ -4542,8 +4533,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { /* Visual Debugging */ if (RuleB(Character, OPClientUpdateVisualDebug)) { - Log(Logs::General, Logs::Debug, "ClientUpdate: ppu x: %f y: %f z: %f h: %u", ppu->x_pos, ppu->y_pos, ppu->z_pos, - ppu->heading); + LogDebug("ClientUpdate: ppu x: [{}] y: [{}] z: [{}] h: [{}]", ppu->x_pos, ppu->y_pos, ppu->z_pos, ppu->heading); this->SendAppearanceEffect(78, 0, 0, 0, 0); this->SendAppearanceEffect(41, 0, 0, 0, 0); } @@ -4657,7 +4647,7 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app) { if (app->size != sizeof(Consider_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in Consider expected %i got %i", sizeof(Consider_Struct), app->size); + LogDebug("Size mismatch in Consider expected [{}] got [{}]", sizeof(Consider_Struct), app->size); return; } Consider_Struct* conin = (Consider_Struct*)app->pBuffer; @@ -4782,7 +4772,7 @@ void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) { if (app->size != sizeof(Consider_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in Consider corpse expected %i got %i", sizeof(Consider_Struct), app->size); + LogDebug("Size mismatch in Consider corpse expected [{}] got [{}]", sizeof(Consider_Struct), app->size); return; } Consider_Struct* conin = (Consider_Struct*)app->pBuffer; @@ -4842,7 +4832,7 @@ void Client::Handle_OP_Consume(const EQApplicationPacket *app) { if (app->size != sizeof(Consume_Struct)) { - Log(Logs::General, Logs::Error, "OP size error: OP_Consume expected:%i got:%i", sizeof(Consume_Struct), app->size); + LogError("OP size error: OP_Consume expected:[{}] got:[{}]", sizeof(Consume_Struct), app->size); return; } Consume_Struct* pcs = (Consume_Struct*)app->pBuffer; @@ -4879,7 +4869,7 @@ void Client::Handle_OP_Consume(const EQApplicationPacket *app) EQEmu::ItemInstance *myitem = GetInv().GetItem(pcs->slot); if (myitem == nullptr) { - Log(Logs::General, Logs::Error, "Consuming from empty slot %d", pcs->slot); + LogError("Consuming from empty slot [{}]", pcs->slot); return; } @@ -4891,7 +4881,7 @@ void Client::Handle_OP_Consume(const EQApplicationPacket *app) Consume(eat_item, EQEmu::item::ItemTypeDrink, pcs->slot, (pcs->auto_consumed == 0xffffffff)); } else { - Log(Logs::General, Logs::Error, "OP_Consume: unknown type, type:%i", (int)pcs->type); + LogError("OP_Consume: unknown type, type:[{}]", (int)pcs->type); return; } if (m_pp.hunger_level > 50000) @@ -4912,7 +4902,7 @@ void Client::Handle_OP_Consume(const EQApplicationPacket *app) void Client::Handle_OP_ControlBoat(const EQApplicationPacket *app) { if (app->size != sizeof(ControlBoat_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_ControlBoat, size=%i, expected %i", app->size, sizeof(ControlBoat_Struct)); + LogError("Wrong size: OP_ControlBoat, size=[{}], expected [{}]", app->size, sizeof(ControlBoat_Struct)); return; } ControlBoat_Struct* cbs = (ControlBoat_Struct*)app->pBuffer; @@ -5014,7 +5004,7 @@ void Client::Handle_OP_CrashDump(const EQApplicationPacket *app) void Client::Handle_OP_CreateObject(const EQApplicationPacket *app) { if (LogSys.log_settings[Logs::Inventory].is_category_enabled) - Log(Logs::Detail, Logs::Inventory, "Handle_OP_CreateObject() [psize: %u] %s", app->size, DumpPacketToString(app).c_str()); + LogInventory("Handle_OP_CreateObject() [psize: [{}]] [{}]", app->size, DumpPacketToString(app).c_str()); DropItem(EQEmu::invslot::slotCursor); return; @@ -5082,8 +5072,7 @@ void Client::Handle_OP_CrystalReclaim(const EQApplicationPacket *app) void Client::Handle_OP_Damage(const EQApplicationPacket *app) { if (app->size != sizeof(CombatDamage_Struct)) { - Log(Logs::General, Logs::Error, "Received invalid sized OP_Damage: got %d, expected %d", app->size, - sizeof(CombatDamage_Struct)); + LogError("Received invalid sized OP_Damage: got [{}], expected [{}]", app->size, sizeof(CombatDamage_Struct)); DumpPacket(app); return; } @@ -5120,8 +5109,7 @@ void Client::Handle_OP_DelegateAbility(const EQApplicationPacket *app) if (app->size != sizeof(DelegateAbility_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_DelegateAbility expected %i got %i", - sizeof(DelegateAbility_Struct), app->size); + LogDebug("Size mismatch in OP_DelegateAbility expected [{}] got [{}]", sizeof(DelegateAbility_Struct), app->size); DumpPacket(app); @@ -5223,7 +5211,7 @@ void Client::Handle_OP_Disarm(const EQApplicationPacket *app) { return; if (app->size != sizeof(Disarm_Struct)) { - Log(Logs::General, Logs::Skills, "Size mismatch for Disarm_Struct packet"); + LogSkills("Size mismatch for Disarm_Struct packet"); return; } @@ -5394,8 +5382,7 @@ void Client::Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app) if (app->size != sizeof(DoGroupLeadershipAbility_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_DoGroupLeadershipAbility expected %i got %i", - sizeof(DoGroupLeadershipAbility_Struct), app->size); + LogDebug("Size mismatch in OP_DoGroupLeadershipAbility expected [{}] got [{}]", sizeof(DoGroupLeadershipAbility_Struct), app->size); DumpPacket(app); @@ -5446,8 +5433,7 @@ void Client::Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app) } default: - Log(Logs::General, Logs::None, "Got unhandled OP_DoGroupLeadershipAbility Ability: %d Parameter: %d", - dglas->Ability, dglas->Parameter); + LogDebug("Got unhandled OP_DoGroupLeadershipAbility Ability: [{}] Parameter: [{}]", dglas->Ability, dglas->Parameter); break; } } @@ -5528,9 +5514,7 @@ void Client::Handle_OP_Dye(const EQApplicationPacket *app) void Client::Handle_OP_Emote(const EQApplicationPacket *app) { if (app->size != sizeof(Emote_Struct)) { - Log(Logs::General, Logs::Error, "Received invalid sized " - "OP_Emote: got %d, expected %d", app->size, - sizeof(Emote_Struct)); + LogError("Received invalid sized OP_Emote: got [{}], expected [{}]", app->size, sizeof(Emote_Struct)); DumpPacket(app); return; } @@ -5619,8 +5603,7 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app) } if (app->size != sizeof(EnvDamage2_Struct)) { - Log(Logs::General, Logs::Error, "Received invalid sized OP_EnvDamage: got %d, expected %d", app->size, - sizeof(EnvDamage2_Struct)); + LogError("Received invalid sized OP_EnvDamage: got [{}], expected [{}]", app->size, sizeof(EnvDamage2_Struct)); DumpPacket(app); return; } @@ -5673,7 +5656,7 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app) void Client::Handle_OP_FaceChange(const EQApplicationPacket *app) { if (app->size != sizeof(FaceChange_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size for OP_FaceChange: Expected: %i, Got: %i", + LogError("Invalid size for OP_FaceChange: Expected: [{}], Got: [{}]", sizeof(FaceChange_Struct), app->size); return; } @@ -5891,7 +5874,7 @@ void Client::Handle_OP_FriendsWho(const EQApplicationPacket *app) void Client::Handle_OP_GetGuildMOTD(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Guilds, "Received OP_GetGuildMOTD"); + LogGuilds("Received OP_GetGuildMOTD"); SendGuildMOTD(true); @@ -5904,7 +5887,7 @@ void Client::Handle_OP_GetGuildMOTD(const EQApplicationPacket *app) void Client::Handle_OP_GetGuildsList(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Guilds, "Received OP_GetGuildsList"); + LogGuilds("Received OP_GetGuildsList"); SendGuildList(); } @@ -5917,7 +5900,7 @@ void Client::Handle_OP_GMBecomeNPC(const EQApplicationPacket *app) return; } if (app->size != sizeof(BecomeNPC_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_GMBecomeNPC, size=%i, expected %i", app->size, sizeof(BecomeNPC_Struct)); + LogError("Wrong size: OP_GMBecomeNPC, size=[{}], expected [{}]", app->size, sizeof(BecomeNPC_Struct)); return; } //entity_list.QueueClients(this, app, false); @@ -5969,7 +5952,7 @@ void Client::Handle_OP_GMEmoteZone(const EQApplicationPacket *app) return; } if (app->size != sizeof(GMEmoteZone_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_GMEmoteZone, size=%i, expected %i", app->size, sizeof(GMEmoteZone_Struct)); + LogError("Wrong size: OP_GMEmoteZone, size=[{}], expected [{}]", app->size, sizeof(GMEmoteZone_Struct)); return; } GMEmoteZone_Struct* gmez = (GMEmoteZone_Struct*)app->pBuffer; @@ -5986,7 +5969,7 @@ void Client::Handle_OP_GMEmoteZone(const EQApplicationPacket *app) void Client::Handle_OP_GMEndTraining(const EQApplicationPacket *app) { if (app->size != sizeof(GMTrainEnd_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_GMEndTraining expected %i got %i", sizeof(GMTrainEnd_Struct), app->size); + LogDebug("Size mismatch in OP_GMEndTraining expected [{}] got [{}]", sizeof(GMTrainEnd_Struct), app->size); DumpPacket(app); return; } @@ -6002,7 +5985,7 @@ void Client::Handle_OP_GMFind(const EQApplicationPacket *app) return; } if (app->size != sizeof(GMSummon_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_GMFind, size=%i, expected %i", app->size, sizeof(GMSummon_Struct)); + LogError("Wrong size: OP_GMFind, size=[{}], expected [{}]", app->size, sizeof(GMSummon_Struct)); return; } //Break down incoming @@ -6067,7 +6050,7 @@ void Client::Handle_OP_GMHideMe(const EQApplicationPacket *app) return; } if (app->size != sizeof(SpawnAppearance_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_GMHideMe, size=%i, expected %i", app->size, sizeof(SpawnAppearance_Struct)); + LogError("Wrong size: OP_GMHideMe, size=[{}], expected [{}]", app->size, sizeof(SpawnAppearance_Struct)); return; } SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; @@ -6117,7 +6100,7 @@ void Client::Handle_OP_GMKill(const EQApplicationPacket *app) return; } if (app->size != sizeof(GMKill_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_GMKill, size=%i, expected %i", app->size, sizeof(GMKill_Struct)); + LogError("Wrong size: OP_GMKill, size=[{}], expected [{}]", app->size, sizeof(GMKill_Struct)); return; } GMKill_Struct* gmk = (GMKill_Struct *)app->pBuffer; @@ -6184,7 +6167,7 @@ void Client::Handle_OP_GMLastName(const EQApplicationPacket *app) void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app) { if (app->size != sizeof(GMName_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_GMNameChange, size=%i, expected %i", app->size, sizeof(GMName_Struct)); + LogError("Wrong size: OP_GMNameChange, size=[{}], expected [{}]", app->size, sizeof(GMName_Struct)); return; } const GMName_Struct* gmn = (const GMName_Struct *)app->pBuffer; @@ -6194,7 +6177,7 @@ void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app) return; } Client* client = entity_list.GetClientByName(gmn->oldname); - Log(Logs::General, Logs::Status, "GM(%s) changeing players name. Old:%s New:%s", GetName(), gmn->oldname, gmn->newname); + LogInfo("GM([{}]) changeing players name. Old:[{}] New:[{}]", GetName(), gmn->oldname, gmn->newname); bool usedname = database.CheckUsedName((const char*)gmn->newname); if (client == 0) { Message(Chat::Red, "%s not found for name change. Operation failed!", gmn->oldname); @@ -6236,8 +6219,7 @@ void Client::Handle_OP_GMSearchCorpse(const EQApplicationPacket *app) if (app->size < sizeof(GMSearchCorpse_Struct)) { - Log(Logs::General, Logs::None, "OP_GMSearchCorpse size lower than expected: got %u expected at least %u", - app->size, sizeof(GMSearchCorpse_Struct)); + LogDebug("OP_GMSearchCorpse size lower than expected: got [{}] expected at least [{}]", app->size, sizeof(GMSearchCorpse_Struct)); DumpPacket(app); return; } @@ -6358,7 +6340,7 @@ void Client::Handle_OP_GMToggle(const EQApplicationPacket *app) void Client::Handle_OP_GMTraining(const EQApplicationPacket *app) { if (app->size != sizeof(GMTrainee_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_GMTraining expected %i got %i", sizeof(GMTrainee_Struct), app->size); + LogDebug("Size mismatch in OP_GMTraining expected [{}] got [{}]", sizeof(GMTrainee_Struct), app->size); DumpPacket(app); return; } @@ -6369,7 +6351,7 @@ void Client::Handle_OP_GMTraining(const EQApplicationPacket *app) void Client::Handle_OP_GMTrainSkill(const EQApplicationPacket *app) { if (app->size != sizeof(GMSkillChange_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_GMTrainSkill expected %i got %i", sizeof(GMSkillChange_Struct), app->size); + LogDebug("Size mismatch in OP_GMTrainSkill expected [{}] got [{}]", sizeof(GMSkillChange_Struct), app->size); DumpPacket(app); return; } @@ -6437,7 +6419,7 @@ void Client::Handle_OP_GMZoneRequest2(const EQApplicationPacket *app) return; } if (app->size < sizeof(uint32)) { - Log(Logs::General, Logs::Error, "OP size error: OP_GMZoneRequest2 expected:%i got:%i", sizeof(uint32), app->size); + LogError("OP size error: OP_GMZoneRequest2 expected:[{}] got:[{}]", sizeof(uint32), app->size); return; } @@ -6454,7 +6436,7 @@ void Client::Handle_OP_GroupAcknowledge(const EQApplicationPacket *app) void Client::Handle_OP_GroupCancelInvite(const EQApplicationPacket *app) { if (app->size != sizeof(GroupCancel_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size for OP_GroupCancelInvite: Expected: %i, Got: %i", + LogError("Invalid size for OP_GroupCancelInvite: Expected: [{}], Got: [{}]", sizeof(GroupCancel_Struct), app->size); return; } @@ -6498,12 +6480,12 @@ void Client::Handle_OP_GroupDelete(const EQApplicationPacket *app) void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) { if (app->size != sizeof(GroupGeneric_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size for GroupGeneric_Struct: Expected: %i, Got: %i", + LogError("Invalid size for GroupGeneric_Struct: Expected: [{}], Got: [{}]", sizeof(GroupGeneric_Struct), app->size); return; } - Log(Logs::General, Logs::None, "Member Disband Request from %s\n", GetName()); + LogDebug("Member Disband Request from [{}]\n", GetName()); GroupGeneric_Struct* gd = (GroupGeneric_Struct*)app->pBuffer; @@ -6659,7 +6641,7 @@ void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) } else { - Log(Logs::General, Logs::Error, "Failed to remove player from group. Unable to find player named %s in player group", gd->name2); + LogError("Failed to remove player from group. Unable to find player named [{}] in player group", gd->name2); } } if (LFP) @@ -6679,7 +6661,7 @@ void Client::Handle_OP_GroupFollow(const EQApplicationPacket *app) void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) { if (app->size != sizeof(GroupGeneric_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size for OP_GroupFollow: Expected: %i, Got: %i", + LogError("Invalid size for OP_GroupFollow: Expected: [{}], Got: [{}]", sizeof(GroupGeneric_Struct), app->size); return; } @@ -6728,7 +6710,7 @@ void Client::Handle_OP_GroupInvite(const EQApplicationPacket *app) void Client::Handle_OP_GroupInvite2(const EQApplicationPacket *app) { if (app->size != sizeof(GroupInvite_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size for OP_GroupInvite: Expected: %i, Got: %i", + LogError("Invalid size for OP_GroupInvite: Expected: [{}], Got: [{}]", sizeof(GroupInvite_Struct), app->size); return; } @@ -6798,7 +6780,7 @@ void Client::Handle_OP_GroupMakeLeader(const EQApplicationPacket *app) if (g->IsLeader(this)) g->ChangeLeader(NewLeader); else { - Log(Logs::General, Logs::None, "Group /makeleader request originated from non-leader member: %s", GetName()); + LogDebug("Group /makeleader request originated from non-leader member: [{}]", GetName()); DumpPacket(app); } } @@ -6807,7 +6789,7 @@ void Client::Handle_OP_GroupMakeLeader(const EQApplicationPacket *app) void Client::Handle_OP_GroupMentor(const EQApplicationPacket *app) { if (app->size != sizeof(GroupMentor_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_GroupMentor, size=%i, expected %i", app->size, sizeof(GroupMentor_Struct)); + LogError("Wrong size: OP_GroupMentor, size=[{}], expected [{}]", app->size, sizeof(GroupMentor_Struct)); DumpPacket(app); return; } @@ -6843,7 +6825,7 @@ void Client::Handle_OP_GroupMentor(const EQApplicationPacket *app) void Client::Handle_OP_GroupRoles(const EQApplicationPacket *app) { if (app->size != sizeof(GroupRole_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_GroupRoles, size=%i, expected %i", app->size, sizeof(GroupRole_Struct)); + LogError("Wrong size: OP_GroupRoles, size=[{}], expected [{}]", app->size, sizeof(GroupRole_Struct)); DumpPacket(app); return; } @@ -6889,8 +6871,7 @@ void Client::Handle_OP_GroupUpdate(const EQApplicationPacket *app) { if (app->size != sizeof(GroupUpdate_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch on OP_GroupUpdate: got %u expected %u", - app->size, sizeof(GroupUpdate_Struct)); + LogDebug("Size mismatch on OP_GroupUpdate: got [{}] expected [{}]", app->size, sizeof(GroupUpdate_Struct)); DumpPacket(app); return; } @@ -6908,7 +6889,7 @@ void Client::Handle_OP_GroupUpdate(const EQApplicationPacket *app) if (group->IsLeader(this)) group->ChangeLeader(newleader); else { - Log(Logs::General, Logs::None, "Group /makeleader request originated from non-leader member: %s", GetName()); + LogDebug("Group /makeleader request originated from non-leader member: [{}]", GetName()); DumpPacket(app); } } @@ -6917,7 +6898,7 @@ void Client::Handle_OP_GroupUpdate(const EQApplicationPacket *app) default: { - Log(Logs::General, Logs::None, "Received unhandled OP_GroupUpdate requesting action %u", gu->action); + LogDebug("Received unhandled OP_GroupUpdate requesting action [{}]", gu->action); DumpPacket(app); return; } @@ -6937,7 +6918,7 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) } if (app->size < sizeof(uint32)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_GuildBank, size=%i, expected %i", app->size, sizeof(uint32)); + LogError("Wrong size: OP_GuildBank, size=[{}], expected [{}]", app->size, sizeof(uint32)); DumpPacket(app); return; } @@ -6963,7 +6944,7 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) { if ((Action != GuildBankDeposit) && (Action != GuildBankViewItem) && (Action != GuildBankWithdraw)) { - Log(Logs::General, Logs::Error, "Suspected hacking attempt on guild bank from %s", GetName()); + LogError("Suspected hacking attempt on guild bank from [{}]", GetName()); GuildBankAck(); @@ -7124,7 +7105,7 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) if (!IsGuildBanker() && !GuildBanks->AllowedToWithdraw(GuildID(), gbwis->Area, gbwis->SlotID, GetName())) { - Log(Logs::General, Logs::Error, "Suspected attempted hack on the guild bank from %s", GetName()); + LogError("Suspected attempted hack on the guild bank from [{}]", GetName()); GuildBankAck(); @@ -7195,7 +7176,7 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) { Message(Chat::Red, "Unexpected GuildBank action."); - Log(Logs::General, Logs::Error, "Received unexpected guild bank action code %i from %s", Action, GetName()); + LogError("Received unexpected guild bank action code [{}] from [{}]", Action, GetName()); } } } @@ -7266,7 +7247,7 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) uint32 NewGuildID = guild_mgr.CreateGuild(GuildName, CharacterID()); - Log(Logs::Detail, Logs::Guilds, "%s: Creating guild %s with leader %d via UF+ GUI. It was given id %lu.", GetName(), + LogGuilds("[{}]: Creating guild [{}] with leader [{}] via UF+ GUI. It was given id [{}]", GetName(), GuildName, CharacterID(), (unsigned long)NewGuildID); if (NewGuildID == GUILD_NONE) @@ -7288,12 +7269,12 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) void Client::Handle_OP_GuildDelete(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Guilds, "Received OP_GuildDelete"); + LogGuilds("Received OP_GuildDelete"); if (!IsInAGuild() || !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) Message(0, "You are not a guild leader or not in a guild."); else { - Log(Logs::Detail, Logs::Guilds, "Deleting guild %s (%d)", guild_mgr.GetGuildName(GuildID()), GuildID()); + LogGuilds("Deleting guild [{}] ([{}])", guild_mgr.GetGuildName(GuildID()), GuildID()); if (!guild_mgr.DeleteGuild(GuildID())) Message(0, "Guild delete failed."); else { @@ -7304,10 +7285,10 @@ void Client::Handle_OP_GuildDelete(const EQApplicationPacket *app) void Client::Handle_OP_GuildDemote(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Guilds, "Received OP_GuildDemote"); + LogGuilds("Received OP_GuildDemote"); if (app->size != sizeof(GuildDemoteStruct)) { - Log(Logs::Detail, Logs::Guilds, "Error: app size of %i != size of GuildDemoteStruct of %i\n", app->size, sizeof(GuildDemoteStruct)); + LogGuilds("Error: app size of [{}] != size of GuildDemoteStruct of [{}]\n", app->size, sizeof(GuildDemoteStruct)); return; } @@ -7337,7 +7318,7 @@ void Client::Handle_OP_GuildDemote(const EQApplicationPacket *app) uint8 rank = gci.rank - 1; - Log(Logs::Detail, Logs::Guilds, "Demoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", + LogGuilds("Demoting [{}] ([{}]) from rank [{}] ([{}]) to [{}] ([{}]) in [{}] ([{}])", demote->target, gci.char_id, guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, guild_mgr.GetRankName(GuildID(), rank), rank, @@ -7355,7 +7336,7 @@ void Client::Handle_OP_GuildDemote(const EQApplicationPacket *app) void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Guilds, "Received OP_GuildInvite"); + LogGuilds("Received OP_GuildInvite"); if (app->size != sizeof(GuildCommand_Struct)) { std::cout << "Wrong size: OP_GuildInvite, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; @@ -7396,7 +7377,7 @@ void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) //we could send this to the member and prompt them to see if they want to //be demoted (I guess), but I dont see a point in that. - Log(Logs::Detail, Logs::Guilds, "%s (%d) is demoting %s (%d) to rank %d in guild %s (%d)", + LogGuilds("[{}] ([{}]) is demoting [{}] ([{}]) to rank [{}] in guild [{}] ([{}])", GetName(), CharacterID(), client->GetName(), client->CharacterID(), gc->officer, @@ -7415,7 +7396,7 @@ void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) return; } - Log(Logs::Detail, Logs::Guilds, "%s (%d) is asking to promote %s (%d) to rank %d in guild %s (%d)", + LogGuilds("[{}] ([{}]) is asking to promote [{}] ([{}]) to rank [{}] in guild [{}] ([{}])", GetName(), CharacterID(), client->GetName(), client->CharacterID(), gc->officer, @@ -7427,7 +7408,7 @@ void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) if (gc->guildeqid == 0) gc->guildeqid = GuildID(); - Log(Logs::Detail, Logs::Guilds, "Sending OP_GuildInvite for promotion to %s, length %d", client->GetName(), app->size); + LogGuilds("Sending OP_GuildInvite for promotion to [{}], length [{}]", client->GetName(), app->size); client->QueuePacket(app); } @@ -7450,7 +7431,7 @@ void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) return; } - Log(Logs::Detail, Logs::Guilds, "Inviting %s (%d) into guild %s (%d)", + LogGuilds("Inviting [{}] ([{}]) into guild [{}] ([{}])", client->GetName(), client->CharacterID(), guild_mgr.GetGuildName(GuildID()), GuildID()); @@ -7470,7 +7451,7 @@ void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) gc->officer = 8; } - Log(Logs::Detail, Logs::Guilds, "Sending OP_GuildInvite for invite to %s, length %d", client->GetName(), app->size); + LogGuilds("Sending OP_GuildInvite for invite to [{}], length [{}]", client->GetName(), app->size); client->SetPendingGuildInvitation(true); client->QueuePacket(app); @@ -7493,7 +7474,7 @@ void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Guilds, "Received OP_GuildInviteAccept"); + LogGuilds("Received OP_GuildInviteAccept"); SetPendingGuildInvitation(false); @@ -7531,7 +7512,7 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) else if (!worldserver.Connected()) Message(0, "Error: World server disconnected"); else { - Log(Logs::Detail, Logs::Guilds, "Guild Invite Accept: guild %d, response %d, inviter %s, person %s", + LogGuilds("Guild Invite Accept: guild [{}], response [{}], inviter [{}], person [{}]", gj->guildeqid, gj->response, gj->inviter, gj->newmember); //ok, the invite is also used for changing rank as well. @@ -7561,7 +7542,7 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) if (gj->guildeqid == GuildID()) { //only need to change rank. - Log(Logs::Detail, Logs::Guilds, "Changing guild rank of %s (%d) to rank %d in guild %s (%d)", + LogGuilds("Changing guild rank of [{}] ([{}]) to rank [{}] in guild [{}] ([{}])", GetName(), CharacterID(), gj->response, guild_mgr.GetGuildName(GuildID()), GuildID()); @@ -7573,7 +7554,7 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) } else { - Log(Logs::Detail, Logs::Guilds, "Adding %s (%d) to guild %s (%d) at rank %d", + LogGuilds("Adding [{}] ([{}]) to guild [{}] ([{}]) at rank [{}]", GetName(), CharacterID(), guild_mgr.GetGuildName(gj->guildeqid), gj->guildeqid, gj->response); @@ -7602,10 +7583,10 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) void Client::Handle_OP_GuildLeader(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Guilds, "Received OP_GuildLeader"); + LogGuilds("Received OP_GuildLeader"); if (app->size < 2) { - Log(Logs::Detail, Logs::Guilds, "Invalid length %d on OP_GuildLeader", app->size); + LogGuilds("Invalid length [{}] on OP_GuildLeader", app->size); return; } @@ -7624,7 +7605,7 @@ void Client::Handle_OP_GuildLeader(const EQApplicationPacket *app) Client* newleader = entity_list.GetClientByName(gml->target); if (newleader) { - Log(Logs::Detail, Logs::Guilds, "Transfering leadership of %s (%d) to %s (%d)", + LogGuilds("Transfering leadership of [{}] ([{}]) to [{}] ([{}])", guild_mgr.GetGuildName(GuildID()), GuildID(), newleader->GetName(), newleader->CharacterID()); @@ -7645,9 +7626,9 @@ void Client::Handle_OP_GuildLeader(const EQApplicationPacket *app) void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Guilds, "Got OP_GuildManageBanker of len %d", app->size); + LogGuilds("Got OP_GuildManageBanker of len [{}]", app->size); if (app->size != sizeof(GuildManageBanker_Struct)) { - Log(Logs::Detail, Logs::Guilds, "Error: app size of %i != size of OP_GuildManageBanker of %i\n", app->size, sizeof(GuildManageBanker_Struct)); + LogGuilds("Error: app size of [{}] != size of OP_GuildManageBanker of [{}]\n", app->size, sizeof(GuildManageBanker_Struct)); return; } GuildManageBanker_Struct* gmb = (GuildManageBanker_Struct*)app->pBuffer; @@ -7722,16 +7703,16 @@ void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) void Client::Handle_OP_GuildPeace(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Guilds, "Got OP_GuildPeace of len %d", app->size); + LogGuilds("Got OP_GuildPeace of len [{}]", app->size); return; } void Client::Handle_OP_GuildPromote(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Guilds, "Received OP_GuildPromote"); + LogGuilds("Received OP_GuildPromote"); if (app->size != sizeof(GuildPromoteStruct)) { - Log(Logs::Detail, Logs::Guilds, "Error: app size of %i != size of GuildDemoteStruct of %i\n", app->size, sizeof(GuildPromoteStruct)); + LogGuilds("Error: app size of [{}] != size of GuildDemoteStruct of [{}]\n", app->size, sizeof(GuildPromoteStruct)); return; } @@ -7763,7 +7744,7 @@ void Client::Handle_OP_GuildPromote(const EQApplicationPacket *app) } - Log(Logs::Detail, Logs::Guilds, "Promoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", + LogGuilds("Promoting [{}] ([{}]) from rank [{}] ([{}]) to [{}] ([{}]) in [{}] ([{}])", promote->target, gci.char_id, guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, guild_mgr.GetRankName(GuildID(), rank), rank, @@ -7780,7 +7761,7 @@ void Client::Handle_OP_GuildPromote(const EQApplicationPacket *app) void Client::Handle_OP_GuildPublicNote(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Guilds, "Received OP_GuildPublicNote"); + LogGuilds("Received OP_GuildPublicNote"); if (app->size < sizeof(GuildUpdate_PublicNote)) { // client calls for a motd on login even if they arent in a guild @@ -7799,7 +7780,7 @@ void Client::Handle_OP_GuildPublicNote(const EQApplicationPacket *app) return; } - Log(Logs::Detail, Logs::Guilds, "Setting public note on %s (%d) in guild %s (%d) to: %s", + LogGuilds("Setting public note on [{}] ([{}]) in guild [{}] ([{}]) to: [{}]", gpn->target, gci.char_id, guild_mgr.GetGuildName(GuildID()), GuildID(), gpn->note); @@ -7816,7 +7797,7 @@ void Client::Handle_OP_GuildPublicNote(const EQApplicationPacket *app) void Client::Handle_OP_GuildRemove(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Guilds, "Received OP_GuildRemove"); + LogGuilds("Received OP_GuildRemove"); if (app->size != sizeof(GuildCommand_Struct)) { std::cout << "Wrong size: OP_GuildRemove, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; @@ -7846,7 +7827,7 @@ void Client::Handle_OP_GuildRemove(const EQApplicationPacket *app) } char_id = client->CharacterID(); - Log(Logs::Detail, Logs::Guilds, "Removing %s (%d) from guild %s (%d)", + LogGuilds("Removing [{}] ([{}]) from guild [{}] ([{}])", client->GetName(), client->CharacterID(), guild_mgr.GetGuildName(GuildID()), GuildID()); } @@ -7862,7 +7843,7 @@ void Client::Handle_OP_GuildRemove(const EQApplicationPacket *app) } char_id = gci.char_id; - Log(Logs::Detail, Logs::Guilds, "Removing remote/offline %s (%d) into guild %s (%d)", + LogGuilds("Removing remote/offline [{}] ([{}]) into guild [{}] ([{}])", gci.char_name.c_str(), gci.char_id, guild_mgr.GetGuildName(GuildID()), GuildID()); } @@ -7887,8 +7868,7 @@ void Client::Handle_OP_GuildStatus(const EQApplicationPacket *app) { if (app->size != sizeof(GuildStatus_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_GuildStatus expected %i got %i", - sizeof(GuildStatus_Struct), app->size); + LogDebug("Size mismatch in OP_GuildStatus expected [{}] got [{}]", sizeof(GuildStatus_Struct), app->size); DumpPacket(app); @@ -7944,8 +7924,7 @@ void Client::Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app) { if (app->size != sizeof(GuildUpdateURLAndChannel_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_GuildUpdateURLAndChannel expected %i got %i", - sizeof(GuildUpdateURLAndChannel_Struct), app->size); + LogDebug("Size mismatch in OP_GuildUpdateURLAndChannel expected [{}] got [{}]", sizeof(GuildUpdateURLAndChannel_Struct), app->size); DumpPacket(app); @@ -7972,7 +7951,7 @@ void Client::Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app) void Client::Handle_OP_GuildWar(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Guilds, "Got OP_GuildWar of len %d", app->size); + LogGuilds("Got OP_GuildWar of len [{}]", app->size); return; } @@ -7987,7 +7966,7 @@ void Client::Handle_OP_Hide(const EQApplicationPacket *app) if (app->size == 4) { auto data = app->ReadUInt32(0); if (data) - Log(Logs::Detail, Logs::None, "Got OP_Hide with unexpected data %d", data); + LogDebug("Got OP_Hide with unexpected data [{}]", data); return; } @@ -8061,8 +8040,7 @@ void Client::Handle_OP_HideCorpse(const EQApplicationPacket *app) // if (app->size != sizeof(HideCorpse_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_HideCorpse expected %i got %i", - sizeof(HideCorpse_Struct), app->size); + LogDebug("Size mismatch in OP_HideCorpse expected [{}] got [{}]", sizeof(HideCorpse_Struct), app->size); DumpPacket(app); @@ -8090,8 +8068,7 @@ void Client::Handle_OP_Ignore(const EQApplicationPacket *app) void Client::Handle_OP_Illusion(const EQApplicationPacket *app) { if (app->size != sizeof(Illusion_Struct)) { - Log(Logs::General, Logs::Error, "Received invalid sized OP_Illusion: got %d, expected %d", app->size, - sizeof(Illusion_Struct)); + LogError("Received invalid sized OP_Illusion: got [{}], expected [{}]", app->size, sizeof(Illusion_Struct)); DumpPacket(app); return; } @@ -8120,7 +8097,7 @@ void Client::Handle_OP_InspectAnswer(const EQApplicationPacket *app) { if (app->size != sizeof(InspectResponse_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_InspectAnswer, size=%i, expected %i", app->size, sizeof(InspectResponse_Struct)); + LogError("Wrong size: OP_InspectAnswer, size=[{}], expected [{}]", app->size, sizeof(InspectResponse_Struct)); return; } @@ -8165,7 +8142,7 @@ void Client::Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app) { if (app->size != sizeof(InspectMessage_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_InspectMessageUpdate, size=%i, expected %i", app->size, sizeof(InspectMessage_Struct)); + LogError("Wrong size: OP_InspectMessageUpdate, size=[{}], expected [{}]", app->size, sizeof(InspectMessage_Struct)); return; } @@ -8179,7 +8156,7 @@ void Client::Handle_OP_InspectRequest(const EQApplicationPacket *app) { if (app->size != sizeof(Inspect_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_InspectRequest, size=%i, expected %i", app->size, sizeof(Inspect_Struct)); + LogError("Wrong size: OP_InspectRequest, size=[{}], expected [{}]", app->size, sizeof(Inspect_Struct)); return; } @@ -8216,7 +8193,7 @@ void Client::Handle_OP_InstillDoubt(const EQApplicationPacket *app) void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) { if (app->size != sizeof(ItemViewRequest_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size on OP_ItemLinkClick. Got: %i, Expected: %i", app->size, + LogError("Wrong size on OP_ItemLinkClick. Got: [{}], Expected: [{}]", app->size, sizeof(ItemViewRequest_Struct)); DumpPacket(app); return; @@ -8302,7 +8279,7 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) void Client::Handle_OP_ItemLinkResponse(const EQApplicationPacket *app) { if (app->size != sizeof(LDONItemViewRequest_Struct)) { - Log(Logs::General, Logs::Error, "OP size error: OP_ItemLinkResponse expected:%i got:%i", sizeof(LDONItemViewRequest_Struct), app->size); + LogError("OP size error: OP_ItemLinkResponse expected:[{}] got:[{}]", sizeof(LDONItemViewRequest_Struct), app->size); return; } LDONItemViewRequest_Struct* item = (LDONItemViewRequest_Struct*)app->pBuffer; @@ -8317,7 +8294,7 @@ void Client::Handle_OP_ItemLinkResponse(const EQApplicationPacket *app) void Client::Handle_OP_ItemName(const EQApplicationPacket *app) { if (app->size != sizeof(ItemNamePacket_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size for ItemNamePacket_Struct: Expected: %i, Got: %i", + LogError("Invalid size for ItemNamePacket_Struct: Expected: [{}], Got: [{}]", sizeof(ItemNamePacket_Struct), app->size); return; } @@ -8517,7 +8494,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) using EQEmu::spells::CastingSlot; if (app->size != sizeof(ItemVerifyRequest_Struct)) { - Log(Logs::General, Logs::Error, "OP size error: OP_ItemVerifyRequest expected:%i got:%i", sizeof(ItemVerifyRequest_Struct), app->size); + LogError("OP size error: OP_ItemVerifyRequest expected:[{}] got:[{}]", sizeof(ItemVerifyRequest_Struct), app->size); return; } @@ -8545,7 +8522,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) } if (slot_id < 0) { - Log(Logs::General, Logs::None, "Unknown slot being used by %s, slot being used is: %i", GetName(), request->slot); + LogDebug("Unknown slot being used by [{}], slot being used is: [{}]", GetName(), request->slot); return; } @@ -8593,7 +8570,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) if (spell_id > 0 && (spells[spell_id].targettype == ST_Pet || spells[spell_id].targettype == ST_SummonedPet)) target_id = GetPetID(); - Log(Logs::General, Logs::None, "OP ItemVerifyRequest: spell=%i, target=%i, inv=%i", spell_id, target_id, slot_id); + LogDebug("OP ItemVerifyRequest: spell=[{}], target=[{}], inv=[{}]", spell_id, target_id, slot_id); if (m_inv.SupportsClickCasting(slot_id) || ((item->ItemType == EQEmu::item::ItemTypePotion || item->PotionBelt) && m_inv.SupportsPotionBeltCasting(slot_id))) // sanity check { @@ -8631,7 +8608,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) if ((spell_id <= 0) && (item->ItemType != EQEmu::item::ItemTypeFood && item->ItemType != EQEmu::item::ItemTypeDrink && item->ItemType != EQEmu::item::ItemTypeAlcohol && item->ItemType != EQEmu::item::ItemTypeSpell)) { - Log(Logs::General, Logs::None, "Item with no effect right clicked by %s", GetName()); + LogDebug("Item with no effect right clicked by [{}]", GetName()); } else if (inst->IsClassCommon()) { @@ -8708,7 +8685,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) { if (item->ItemType != EQEmu::item::ItemTypeFood && item->ItemType != EQEmu::item::ItemTypeDrink && item->ItemType != EQEmu::item::ItemTypeAlcohol) { - Log(Logs::General, Logs::None, "Error: unknown item->Click.Type (%i)", item->Click.Type); + LogDebug("Error: unknown item->Click.Type ([{}])", item->Click.Type); } else { @@ -8726,7 +8703,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) else if (item->ItemType == EQEmu::item::ItemTypeAlcohol) { #if EQDEBUG >= 1 - Log(Logs::General, Logs::None, "Drinking Alcohol from slot:%i", slot_id); + LogDebug("Drinking Alcohol from slot:[{}]", slot_id); #endif // This Seems to be handled in OP_DeleteItem handling //DeleteItemInInventory(slot_id, 1, false); @@ -8755,7 +8732,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) } else { - Log(Logs::General, Logs::None, "Error: unknown item->Click.Type (%i)", item->Click.Type); + LogDebug("Error: unknown item->Click.Type ([{}])", item->Click.Type); } } } @@ -8897,7 +8874,7 @@ void Client::Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app) void Client::Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app) { if (app->size != 1) { - Log(Logs::General, Logs::None, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); + LogDebug("Size mismatch in OP_LeadershipExpToggle expected [{}] got [{}]", 1, app->size); DumpPacket(app); return; } @@ -8984,7 +8961,7 @@ void Client::Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app) { if (app->size != sizeof(LFGGetMatchesRequest_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_LFGGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFGGetMatchesRequest_Struct)); + LogError("Wrong size: OP_LFGGetMatchesRequest, size=[{}], expected [{}]", app->size, sizeof(LFGGetMatchesRequest_Struct)); DumpPacket(app); return; } @@ -9146,7 +9123,7 @@ void Client::Handle_OP_LFPCommand(const EQApplicationPacket *app) { if (app->size != sizeof(LFP_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_LFPCommand, size=%i, expected %i", app->size, sizeof(LFP_Struct)); + LogError("Wrong size: OP_LFPCommand, size=[{}], expected [{}]", app->size, sizeof(LFP_Struct)); DumpPacket(app); return; } @@ -9183,7 +9160,7 @@ void Client::Handle_OP_LFPCommand(const EQApplicationPacket *app) // This should not happen. The client checks if you are in a group and will not let you put LFP on if // you are not the leader. if (!g->IsLeader(this)) { - Log(Logs::General, Logs::Error, "Client sent LFP on for character %s who is grouped but not leader.", GetName()); + LogError("Client sent LFP on for character [{}] who is grouped but not leader", GetName()); return; } // Fill the LFPMembers array with the rest of the group members, excluding ourself @@ -9208,7 +9185,7 @@ void Client::Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app) { if (app->size != sizeof(LFPGetMatchesRequest_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_LFPGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFPGetMatchesRequest_Struct)); + LogError("Wrong size: OP_LFPGetMatchesRequest, size=[{}], expected [{}]", app->size, sizeof(LFPGetMatchesRequest_Struct)); DumpPacket(app); return; } @@ -9248,7 +9225,7 @@ void Client::Handle_OP_LoadSpellSet(const EQApplicationPacket *app) void Client::Handle_OP_Logout(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::None, "%s sent a logout packet.", GetName()); + LogDebug("[{}] sent a logout packet", GetName()); SendLogoutPackets(); @@ -9262,7 +9239,7 @@ void Client::Handle_OP_Logout(const EQApplicationPacket *app) void Client::Handle_OP_LootItem(const EQApplicationPacket *app) { if (app->size != sizeof(LootingItem_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_LootItem, size=%i, expected %i", app->size, sizeof(LootingItem_Struct)); + LogError("Wrong size: OP_LootItem, size=[{}], expected [{}]", app->size, sizeof(LootingItem_Struct)); return; } @@ -9407,7 +9384,7 @@ void Client::Handle_OP_MercenaryCommand(const EQApplicationPacket *app) if (app->size != sizeof(MercenaryCommand_Struct)) { Message(Chat::Red, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); - Log(Logs::General, Logs::None, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); + LogDebug("Size mismatch in OP_MercenaryCommand expected [{}] got [{}]", sizeof(MercenaryCommand_Struct), app->size); DumpPacket(app); return; } @@ -9462,7 +9439,7 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) // The payload is 4 bytes. The EntityID of the Mercenary Liason which are of class 71. if (app->size != sizeof(MercenaryMerchantShopRequest_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_MercenaryDataRequest expected 4 got %i", app->size); + LogDebug("Size mismatch in OP_MercenaryDataRequest expected 4 got [{}]", app->size); DumpPacket(app); @@ -9594,7 +9571,7 @@ void Client::Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app if (app->size != 0) { Message(Chat::Red, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); - Log(Logs::General, Logs::None, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); + LogDebug("Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got [{}]", app->size); DumpPacket(app); return; } @@ -9613,7 +9590,7 @@ void Client::Handle_OP_MercenaryDismiss(const EQApplicationPacket *app) if (app->size > 1) { Message(Chat::Red, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); - Log(Logs::General, Logs::None, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); + LogDebug("Size mismatch in OP_MercenaryDismiss expected 0 got [{}]", app->size); DumpPacket(app); return; } @@ -9637,7 +9614,7 @@ void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) // The payload is 16 bytes. First four bytes are the Merc ID (Template ID) if (app->size != sizeof(MercenaryMerchantRequest_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_MercenaryHire expected %i got %i", sizeof(MercenaryMerchantRequest_Struct), app->size); + LogDebug("Size mismatch in OP_MercenaryHire expected [{}] got [{}]", sizeof(MercenaryMerchantRequest_Struct), app->size); DumpPacket(app); @@ -9708,7 +9685,7 @@ void Client::Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app) if (app->size != sizeof(SuspendMercenary_Struct)) { Message(Chat::Red, "Size mismatch in OP_MercenarySuspendRequest expected %i got %i", sizeof(SuspendMercenary_Struct), app->size); - Log(Logs::General, Logs::None, "Size mismatch in OP_MercenarySuspendRequest expected %i got %i", sizeof(SuspendMercenary_Struct), app->size); + LogDebug("Size mismatch in OP_MercenarySuspendRequest expected [{}] got [{}]", sizeof(SuspendMercenary_Struct), app->size); DumpPacket(app); return; } @@ -9731,7 +9708,7 @@ void Client::Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app) if (app->size > 1) { Message(Chat::Red, "Size mismatch in OP_MercenaryTimerRequest expected 0 got %i", app->size); - Log(Logs::General, Logs::None, "Size mismatch in OP_MercenaryTimerRequest expected 0 got %i", app->size); + LogDebug("Size mismatch in OP_MercenaryTimerRequest expected 0 got [{}]", app->size); DumpPacket(app); return; } @@ -9768,7 +9745,7 @@ void Client::Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app) void Client::Handle_OP_MoveCoin(const EQApplicationPacket *app) { if (app->size != sizeof(MoveCoin_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size on OP_MoveCoin. Got: %i, Expected: %i", app->size, sizeof(MoveCoin_Struct)); + LogError("Wrong size on OP_MoveCoin. Got: [{}], Expected: [{}]", app->size, sizeof(MoveCoin_Struct)); DumpPacket(app); return; } @@ -9784,7 +9761,7 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) } if (app->size != sizeof(MoveItem_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_MoveItem, size=%i, expected %i", app->size, sizeof(MoveItem_Struct)); + LogError("Wrong size: OP_MoveItem, size=[{}], expected [{}]", app->size, sizeof(MoveItem_Struct)); return; } @@ -9872,7 +9849,7 @@ void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app) void Client::Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Tribute, "Received OP_OpenGuildTributeMaster of length %d", app->size); + LogTribute("Received OP_OpenGuildTributeMaster of length [{}]", app->size); if (app->size != sizeof(StartTribute_Struct)) printf("Error in OP_OpenGuildTributeMaster. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); @@ -9903,7 +9880,7 @@ void Client::Handle_OP_OpenInventory(const EQApplicationPacket *app) void Client::Handle_OP_OpenTributeMaster(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Tribute, "Received OP_OpenTributeMaster of length %d", app->size); + LogTribute("Received OP_OpenTributeMaster of length [{}]", app->size); if (app->size != sizeof(StartTribute_Struct)) printf("Error in OP_OpenTributeMaster. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); @@ -9929,7 +9906,7 @@ void Client::Handle_OP_OpenTributeMaster(const EQApplicationPacket *app) void Client::Handle_OP_PDeletePetition(const EQApplicationPacket *app) { if (app->size < 2) { - Log(Logs::General, Logs::Error, "Wrong size: OP_PDeletePetition, size=%i, expected %i", app->size, 2); + LogError("Wrong size: OP_PDeletePetition, size=[{}], expected [{}]", app->size, 2); return; } if (petition_list.DeletePetitionByCharName((char*)app->pBuffer)) @@ -9942,7 +9919,7 @@ void Client::Handle_OP_PDeletePetition(const EQApplicationPacket *app) void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) { if (app->size != sizeof(PetCommand_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_PetCommands, size=%i, expected %i", app->size, sizeof(PetCommand_Struct)); + LogError("Wrong size: OP_PetCommands, size=[{}], expected [{}]", app->size, sizeof(PetCommand_Struct)); return; } char val1[20] = { 0 }; @@ -10559,7 +10536,7 @@ void Client::Handle_OP_PetitionBug(const EQApplicationPacket *app) void Client::Handle_OP_PetitionCheckIn(const EQApplicationPacket *app) { if (app->size != sizeof(Petition_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_PetitionCheckIn, size=%i, expected %i", app->size, sizeof(Petition_Struct)); + LogError("Wrong size: OP_PetitionCheckIn, size=[{}], expected [{}]", app->size, sizeof(Petition_Struct)); return; } Petition_Struct* inpet = (Petition_Struct*)app->pBuffer; @@ -10603,7 +10580,7 @@ void Client::Handle_OP_PetitionCheckout(const EQApplicationPacket *app) void Client::Handle_OP_PetitionDelete(const EQApplicationPacket *app) { if (app->size != sizeof(PetitionUpdate_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_PetitionDelete, size=%i, expected %i", app->size, sizeof(PetitionUpdate_Struct)); + LogError("Wrong size: OP_PetitionDelete, size=[{}], expected [{}]", app->size, sizeof(PetitionUpdate_Struct)); return; } auto outapp = new EQApplicationPacket(OP_PetitionUpdate, sizeof(PetitionUpdate_Struct)); @@ -10698,7 +10675,7 @@ void Client::Handle_OP_PickPocket(const EQApplicationPacket *app) { if (app->size != sizeof(PickPocket_Struct)) { - Log(Logs::General, Logs::Error, "Size mismatch for Pick Pocket packet"); + LogError("Size mismatch for Pick Pocket packet"); DumpPacket(app); } @@ -10768,11 +10745,7 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) { if (app->size != sizeof(PopupResponse_Struct)) { - Log(Logs::General, - Logs::None, - "Size mismatch in OP_PopupResponse expected %i got %i", - sizeof(PopupResponse_Struct), - app->size); + LogDebug("Size mismatch in OP_PopupResponse expected [{}] got [{}]", sizeof(PopupResponse_Struct), app->size); DumpPacket(app); return; @@ -10816,15 +10789,14 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app) { if (app->size != sizeof(MovePotionToBelt_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_PotionBelt expected %i got %i", - sizeof(MovePotionToBelt_Struct), app->size); + LogDebug("Size mismatch in OP_PotionBelt expected [{}] got [{}]", sizeof(MovePotionToBelt_Struct), app->size); DumpPacket(app); return; } MovePotionToBelt_Struct *mptbs = (MovePotionToBelt_Struct*)app->pBuffer; if (!EQEmu::ValueWithin(mptbs->SlotNumber, 0U, 3U)) { - Log(Logs::General, Logs::None, "Client::Handle_OP_PotionBelt mptbs->SlotNumber out of range."); + LogDebug("Client::Handle_OP_PotionBelt mptbs->SlotNumber out of range"); return; } @@ -10847,7 +10819,7 @@ void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app) void Client::Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app) { if (app->size != sizeof(uint32)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); + LogDebug("Size mismatch in OP_LeadershipExpToggle expected [{}] got [{}]", 1, app->size); DumpPacket(app); return; } @@ -10940,8 +10912,7 @@ void Client::Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *a // if (app->size != sizeof(PVPLeaderBoardDetailsRequest_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_PVPLeaderBoardDetailsRequest expected %i got %i", - sizeof(PVPLeaderBoardDetailsRequest_Struct), app->size); + LogDebug("Size mismatch in OP_PVPLeaderBoardDetailsRequest expected [{}] got [{}]", sizeof(PVPLeaderBoardDetailsRequest_Struct), app->size); DumpPacket(app); @@ -10967,8 +10938,7 @@ void Client::Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app) // if (app->size != sizeof(PVPLeaderBoardRequest_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_PVPLeaderBoardRequest expected %i got %i", - sizeof(PVPLeaderBoardRequest_Struct), app->size); + LogDebug("Size mismatch in OP_PVPLeaderBoardRequest expected [{}] got [{}]", sizeof(PVPLeaderBoardRequest_Struct), app->size); DumpPacket(app); @@ -11066,7 +11036,7 @@ void Client::Handle_OP_QueryUCSServerStatus(const EQApplicationPacket *app) void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) { if (app->size < sizeof(RaidGeneral_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_RaidCommand, size=%i, expected at least %i", app->size, sizeof(RaidGeneral_Struct)); + LogError("Wrong size: OP_RaidCommand, size=[{}], expected at least [{}]", app->size, sizeof(RaidGeneral_Struct)); DumpPacket(app); return; } @@ -11585,7 +11555,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) client_moved->GetRaid()->SendHPManaEndPacketsTo(client_moved); client_moved->GetRaid()->SendHPManaEndPacketsFrom(client_moved); - Log(Logs::General, Logs::HP_Update, + Log(Logs::General, Logs::HPUpdate, "Client::Handle_OP_RaidCommand :: %s sending and recieving HP/Mana/End updates", client_moved->GetCleanName() ); @@ -11685,7 +11655,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) void Client::Handle_OP_RandomReq(const EQApplicationPacket *app) { if (app->size != sizeof(RandomReq_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_RandomReq, size=%i, expected %i", app->size, sizeof(RandomReq_Struct)); + LogError("Wrong size: OP_RandomReq, size=[{}], expected [{}]", app->size, sizeof(RandomReq_Struct)); return; } const RandomReq_Struct* rndq = (const RandomReq_Struct*)app->pBuffer; @@ -11714,7 +11684,7 @@ void Client::Handle_OP_RandomReq(const EQApplicationPacket *app) void Client::Handle_OP_ReadBook(const EQApplicationPacket *app) { if (app->size != sizeof(BookRequest_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_ReadBook, size=%i, expected %i", app->size, sizeof(BookRequest_Struct)); + LogError("Wrong size: OP_ReadBook, size=[{}], expected [{}]", app->size, sizeof(BookRequest_Struct)); return; } BookRequest_Struct* book = (BookRequest_Struct*)app->pBuffer; @@ -11730,7 +11700,7 @@ void Client::Handle_OP_ReadBook(const EQApplicationPacket *app) void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app) { if (app->size != sizeof(RecipeAutoCombine_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size for RecipeAutoCombine_Struct: Expected: %i, Got: %i", + LogError("Invalid size for RecipeAutoCombine_Struct: Expected: [{}], Got: [{}]", sizeof(RecipeAutoCombine_Struct), app->size); return; } @@ -11744,7 +11714,7 @@ void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app) void Client::Handle_OP_RecipeDetails(const EQApplicationPacket *app) { if (app->size < sizeof(uint32)) { - Log(Logs::General, Logs::Error, "Invalid size for RecipeDetails Request: Expected: %i, Got: %i", + LogError("Invalid size for RecipeDetails Request: Expected: [{}], Got: [{}]", sizeof(uint32), app->size); return; } @@ -11758,14 +11728,14 @@ void Client::Handle_OP_RecipeDetails(const EQApplicationPacket *app) void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) { if (app->size != sizeof(TradeskillFavorites_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size for TradeskillFavorites_Struct: Expected: %i, Got: %i", + LogError("Invalid size for TradeskillFavorites_Struct: Expected: [{}], Got: [{}]", sizeof(TradeskillFavorites_Struct), app->size); return; } TradeskillFavorites_Struct* tsf = (TradeskillFavorites_Struct*)app->pBuffer; - Log(Logs::General, Logs::None, "Requested Favorites for: %d - %d\n", tsf->object_type, tsf->some_id); + LogDebug("Requested Favorites for: [{}] - [{}]\n", tsf->object_type, tsf->some_id); // results show that object_type is combiner type // some_id = 0 if world combiner, item number otherwise @@ -11782,7 +11752,7 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) auto item = database.GetItem(tsf->some_id); if (!item) { - Log(Logs::General, Logs::Error, "Invalid container ID: %d. GetItem returned null. Defaulting to BagSlots = 10.\n", tsf->some_id); + LogError("Invalid container ID: [{}]. GetItem returned null. Defaulting to BagSlots = 10.\n", tsf->some_id); combineObjectSlots = 10; } else @@ -11831,7 +11801,7 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) { if (app->size != sizeof(RecipesSearch_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size for RecipesSearch_Struct: Expected: %i, Got: %i", + LogError("Invalid size for RecipesSearch_Struct: Expected: [{}], Got: [{}]", sizeof(RecipesSearch_Struct), app->size); return; } @@ -11840,7 +11810,7 @@ void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) rss->query[55] = '\0'; //just to be sure. - Log(Logs::General, Logs::None, "Requested search recipes for: %d - %d\n", rss->object_type, rss->some_id); + LogDebug("Requested search recipes for: [{}] - [{}]\n", rss->object_type, rss->some_id); // make where clause segment for container(s) char containers[30]; @@ -11856,7 +11826,7 @@ void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) auto item = database.GetItem(rss->some_id); if (!item) { - Log(Logs::General, Logs::Error, "Invalid container ID: %d. GetItem returned null. Defaulting to BagSlots = 10.\n", rss->some_id); + LogError("Invalid container ID: [{}]. GetItem returned null. Defaulting to BagSlots = 10.\n", rss->some_id); combineObjectSlots = 10; } else @@ -11912,8 +11882,7 @@ void Client::Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app) if (app->size != sizeof(BlockedBuffs_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_RemoveBlockedBuffs expected %i got %i", - sizeof(BlockedBuffs_Struct), app->size); + LogDebug("Size mismatch in OP_RemoveBlockedBuffs expected [{}] got [{}]", sizeof(BlockedBuffs_Struct), app->size); DumpPacket(app); @@ -11970,7 +11939,7 @@ void Client::Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app) void Client::Handle_OP_RemoveTrap(const EQApplicationPacket *app) { if (app->size != 4) {// just an int - Log(Logs::General, Logs::None, "Size mismatch in OP_RemoveTrap expected 4 got %i", app->size); + LogDebug("Size mismatch in OP_RemoveTrap expected 4 got [{}]", app->size); DumpPacket(app); return; } @@ -12097,8 +12066,7 @@ void Client::Handle_OP_RespawnWindow(const EQApplicationPacket *app) // if (app->size != 4) { - Log(Logs::General, Logs::None, "Size mismatch in OP_RespawnWindow expected %i got %i", - 4, app->size); + LogDebug("Size mismatch in OP_RespawnWindow expected [{}] got [{}]", 4, app->size); DumpPacket(app); return; } @@ -12125,7 +12093,7 @@ void Client::Handle_OP_RezzAnswer(const EQApplicationPacket *app) const Resurrect_Struct* ra = (const Resurrect_Struct*)app->pBuffer; - Log(Logs::Detail, Logs::Spells, "Received OP_RezzAnswer from client. Pendingrezzexp is %i, action is %s", + LogSpells("Received OP_RezzAnswer from client. Pendingrezzexp is [{}], action is [{}]", PendingRezzXP, ra->action ? "ACCEPT" : "DECLINE"); @@ -12147,14 +12115,14 @@ void Client::Handle_OP_Sacrifice(const EQApplicationPacket *app) { if (app->size != sizeof(Sacrifice_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_Sacrifice expected %i got %i", sizeof(Sacrifice_Struct), app->size); + LogDebug("Size mismatch in OP_Sacrifice expected [{}] got [{}]", sizeof(Sacrifice_Struct), app->size); DumpPacket(app); return; } Sacrifice_Struct *ss = (Sacrifice_Struct*)app->pBuffer; if (!PendingSacrifice) { - Log(Logs::General, Logs::Error, "Unexpected OP_Sacrifice reply"); + LogError("Unexpected OP_Sacrifice reply"); DumpPacket(app); return; } @@ -12192,12 +12160,12 @@ void Client::Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app) void Client::Handle_OP_SelectTribute(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Tribute, "Received OP_SelectTribute of length %d", app->size); + LogTribute("Received OP_SelectTribute of length [{}]", app->size); //we should enforce being near a real tribute master to change this //but im not sure how I wanna do that right now. if (app->size != sizeof(SelectTributeReq_Struct)) - Log(Logs::General, Logs::Error, "Invalid size on OP_SelectTribute packet"); + LogError("Invalid size on OP_SelectTribute packet"); else { SelectTributeReq_Struct *t = (SelectTributeReq_Struct *)app->pBuffer; SendTributeDetails(t->client_id, t->tribute_id); @@ -12281,7 +12249,7 @@ void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app) void Client::Handle_OP_SetGuildMOTD(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Guilds, "Received OP_SetGuildMOTD"); + LogGuilds("Received OP_SetGuildMOTD"); if (app->size != sizeof(GuildMOTD_Struct)) { // client calls for a motd on login even if they arent in a guild @@ -12299,7 +12267,7 @@ void Client::Handle_OP_SetGuildMOTD(const EQApplicationPacket *app) GuildMOTD_Struct* gmotd = (GuildMOTD_Struct*)app->pBuffer; - Log(Logs::Detail, Logs::Guilds, "Setting MOTD for %s (%d) to: %s - %s", + LogGuilds("Setting MOTD for [{}] ([{}]) to: [{}] - [{}]", guild_mgr.GetGuildName(GuildID()), GuildID(), GetName(), gmotd->motd); if (!guild_mgr.SetGuildMOTD(GuildID(), gmotd->motd, GetName())) { @@ -12312,9 +12280,7 @@ void Client::Handle_OP_SetGuildMOTD(const EQApplicationPacket *app) void Client::Handle_OP_SetRunMode(const EQApplicationPacket *app) { if (app->size < sizeof(SetRunMode_Struct)) { - Log(Logs::General, Logs::Error, "Received invalid sized " - "OP_SetRunMode: got %d, expected %d", app->size, - sizeof(SetRunMode_Struct)); + LogError("Received invalid sized OP_SetRunMode: got [{}], expected [{}]", app->size, sizeof(SetRunMode_Struct)); DumpPacket(app); return; } @@ -12329,9 +12295,7 @@ void Client::Handle_OP_SetRunMode(const EQApplicationPacket *app) void Client::Handle_OP_SetServerFilter(const EQApplicationPacket *app) { if (app->size != sizeof(SetServerFilter_Struct)) { - Log(Logs::General, Logs::Error, "Received invalid sized " - "OP_SetServerFilter: got %d, expected %d", app->size, - sizeof(SetServerFilter_Struct)); + LogError("Received invalid sized OP_SetServerFilter: got [{}], expected [{}]", app->size, sizeof(SetServerFilter_Struct)); DumpPacket(app); return; } @@ -12349,7 +12313,7 @@ void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) } if (app->size < 1) { - Log(Logs::General, Logs::Error, "Wrong size: OP_SetStartCity, size=%i, expected %i", app->size, 1); + LogError("Wrong size: OP_SetStartCity, size=[{}], expected [{}]", app->size, 1); DumpPacket(app); return; } @@ -12363,7 +12327,7 @@ void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) m_pp.class_, m_pp.deity, m_pp.race); auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "No valid start zones found for /setstartcity"); + LogError("No valid start zones found for /setstartcity"); return; } @@ -12414,7 +12378,7 @@ void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) void Client::Handle_OP_SetTitle(const EQApplicationPacket *app) { if (app->size != sizeof(SetTitle_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_SetTitle expected %i got %i", sizeof(SetTitle_Struct), app->size); + LogDebug("Size mismatch in OP_SetTitle expected [{}] got [{}]", sizeof(SetTitle_Struct), app->size); DumpPacket(app); return; } @@ -12438,7 +12402,7 @@ void Client::Handle_OP_SetTitle(const EQApplicationPacket *app) void Client::Handle_OP_Shielding(const EQApplicationPacket *app) { if (app->size != sizeof(Shielding_Struct)) { - Log(Logs::General, Logs::Error, "OP size error: OP_Shielding expected:%i got:%i", sizeof(Shielding_Struct), app->size); + LogError("OP size error: OP_Shielding expected:[{}] got:[{}]", sizeof(Shielding_Struct), app->size); return; } if (GetClass() != WARRIOR) @@ -12531,7 +12495,7 @@ void Client::Handle_OP_ShopEnd(const EQApplicationPacket *app) void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) { if (app->size != sizeof(Merchant_Sell_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size on OP_ShopPlayerBuy: Expected %i, Got %i", + LogError("Invalid size on OP_ShopPlayerBuy: Expected [{}], Got [{}]", sizeof(Merchant_Sell_Struct), app->size); return; } @@ -12539,7 +12503,7 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) t1.start(); Merchant_Sell_Struct* mp = (Merchant_Sell_Struct*)app->pBuffer; #if EQDEBUG >= 5 - Log(Logs::General, Logs::None, "%s, purchase item..", GetName()); + LogDebug("[{}], purchase item", GetName()); DumpPacket(app); #endif @@ -12699,7 +12663,7 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) SendItemPacket(freeslotid, inst, ItemPacketTrade); } else if (!stacked) { - Log(Logs::General, Logs::Error, "OP_ShopPlayerBuy: item->ItemClass Unknown! Type: %i", item->ItemClass); + LogError("OP_ShopPlayerBuy: item->ItemClass Unknown! Type: [{}]", item->ItemClass); } QueuePacket(outapp); if (inst && tmpmer_used) { @@ -12771,7 +12735,7 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) qsaudit->items[0].aug_5 = 0; if (freeslotid != INVALID_INDEX) { - Log(Logs::General, Logs::Error, "Handle_OP_ShopPlayerBuy: QS Audit could not locate merchant (%u) purchased item in player (%u) inventory slot (%i)", + LogError("Handle_OP_ShopPlayerBuy: QS Audit could not locate merchant ([{}]) purchased item in player ([{}]) inventory slot ([{}])", qsaudit->merchant_id, qsaudit->char_id, freeslotid); } } @@ -12799,7 +12763,7 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) { if (app->size != sizeof(Merchant_Purchase_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size on OP_ShopPlayerSell: Expected %i, Got %i", + LogError("Invalid size on OP_ShopPlayerSell: Expected [{}], Got [{}]", sizeof(Merchant_Purchase_Struct), app->size); return; } @@ -12992,7 +12956,7 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app) { if (app->size != sizeof(Merchant_Click_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_ShopRequest, size=%i, expected %i", app->size, sizeof(Merchant_Click_Struct)); + LogError("Wrong size: OP_ShopRequest, size=[{}], expected [{}]", app->size, sizeof(Merchant_Click_Struct)); return; } @@ -13199,7 +13163,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) } else { - Log(Logs::Detail, Logs::Error, "Client %s :: unknown appearance %i", name, (int)sa->parameter); + LogError("Client [{}] :: unknown appearance [{}]", name, (int)sa->parameter); return; } @@ -13221,7 +13185,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) m_pp.anon = 0; } else { - Log(Logs::Detail, Logs::Error, "Client %s :: unknown Anon/Roleplay Switch %i", name, (int)sa->parameter); + LogError("Client [{}] :: unknown Anon/Roleplay Switch [{}]", name, (int)sa->parameter); return; } entity_list.QueueClients(this, app, true); @@ -13290,7 +13254,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) void Client::Handle_OP_Split(const EQApplicationPacket *app) { if (app->size != sizeof(Split_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_Split, size=%i, expected %i", app->size, sizeof(Split_Struct)); + LogError("Wrong size: OP_Split, size=[{}], expected [{}]", app->size, sizeof(Split_Struct)); return; } // The client removes the money on its own, but we have to @@ -13327,7 +13291,7 @@ void Client::Handle_OP_Surname(const EQApplicationPacket *app) { if (app->size != sizeof(Surname_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in Surname expected %i got %i", sizeof(Surname_Struct), app->size); + LogDebug("Size mismatch in Surname expected [{}] got [{}]", sizeof(Surname_Struct), app->size); return; } @@ -13420,7 +13384,7 @@ void Client::Handle_OP_SwapSpell(const EQApplicationPacket *app) void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app) { if (app->size != sizeof(ClientTarget_Struct)) { - Log(Logs::General, Logs::Error, "OP size error: OP_TargetMouse expected:%i got:%i", sizeof(ClientTarget_Struct), app->size); + LogError("OP size error: OP_TargetMouse expected:[{}] got:[{}]", sizeof(ClientTarget_Struct), app->size); return; } @@ -13643,8 +13607,7 @@ void Client::Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app) { if (app->size != sizeof(TaskHistoryRequest_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_TaskHistoryRequest expected %i got %i", - sizeof(TaskHistoryRequest_Struct), app->size); + LogDebug("Size mismatch in OP_TaskHistoryRequest expected [{}] got [{}]", sizeof(TaskHistoryRequest_Struct), app->size); DumpPacket(app); return; } @@ -13701,7 +13664,7 @@ void Client::Handle_OP_Track(const EQApplicationPacket *app) CheckIncreaseSkill(EQEmu::skills::SkillTracking, nullptr, 15); if (!entity_list.MakeTrackPacket(this)) - Log(Logs::General, Logs::Error, "Unable to generate OP_Track packet requested by client."); + LogError("Unable to generate OP_Track packet requested by client"); return; } @@ -13715,7 +13678,7 @@ void Client::Handle_OP_TrackTarget(const EQApplicationPacket *app) if (app->size != sizeof(TrackTarget_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size for OP_TrackTarget: Expected: %i, Got: %i", + LogError("Invalid size for OP_TrackTarget: Expected: [{}], Got: [{}]", sizeof(TrackTarget_Struct), app->size); return; } @@ -13890,7 +13853,7 @@ void Client::Handle_OP_TradeAcceptClick(const EQApplicationPacket *app) void Client::Handle_OP_TradeBusy(const EQApplicationPacket *app) { if (app->size != sizeof(TradeBusy_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_TradeBusy, size=%i, expected %i", app->size, sizeof(TradeBusy_Struct)); + LogError("Wrong size: OP_TradeBusy, size=[{}], expected [{}]", app->size, sizeof(TradeBusy_Struct)); return; } // Trade request recipient is cancelling the trade due to being busy @@ -13929,7 +13892,7 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app) { case BazaarTrader_EndTraderMode: { Trader_EndTrader(); - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: End Trader Session"); + LogTrading("Client::Handle_OP_Trader: End Trader Session"); break; } case BazaarTrader_EndTransaction: { @@ -13938,20 +13901,20 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app) if (c) { c->WithCustomer(0); - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: End Transaction"); + LogTrading("Client::Handle_OP_Trader: End Transaction"); } else - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Null Client Pointer"); + LogTrading("Client::Handle_OP_Trader: Null Client Pointer"); break; } case BazaarTrader_ShowItems: { Trader_ShowItems(); - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Show Trader Items"); + LogTrading("Client::Handle_OP_Trader: Show Trader Items"); break; } default: { - Log(Logs::Detail, Logs::Trading, "Unhandled action code in OP_Trader ShowItems_Struct"); + LogTrading("Unhandled action code in OP_Trader ShowItems_Struct"); break; } } @@ -13970,7 +13933,7 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app) { GetItems_Struct* gis = GetTraderItems(); - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Start Trader Mode"); + LogTrading("Client::Handle_OP_Trader: Start Trader Mode"); // Verify there are no NODROP or items with a zero price bool TradeItemsValid = true; @@ -14033,32 +13996,32 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app) } } else { - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Unknown TraderStruct code of: %i\n", + LogTrading("Client::Handle_OP_Trader: Unknown TraderStruct code of: [{}]\n", ints->Code); - Log(Logs::General, Logs::Error, "Unknown TraderStruct code of: %i\n", ints->Code); + LogError("Unknown TraderStruct code of: [{}]\n", ints->Code); } } else if (app->size == sizeof(TraderStatus_Struct)) { TraderStatus_Struct* tss = (TraderStatus_Struct*)app->pBuffer; - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Trader Status Code: %d", tss->Code); + LogTrading("Client::Handle_OP_Trader: Trader Status Code: [{}]", tss->Code); switch (tss->Code) { case BazaarTrader_EndTraderMode: { Trader_EndTrader(); - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: End Trader Session"); + LogTrading("Client::Handle_OP_Trader: End Trader Session"); break; } case BazaarTrader_ShowItems: { Trader_ShowItems(); - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Show Trader Items"); + LogTrading("Client::Handle_OP_Trader: Show Trader Items"); break; } default: { - Log(Logs::Detail, Logs::Trading, "Unhandled action code in OP_Trader ShowItems_Struct"); + LogTrading("Unhandled action code in OP_Trader ShowItems_Struct"); break; } } @@ -14067,12 +14030,12 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app) } else if (app->size == sizeof(TraderPriceUpdate_Struct)) { - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Trader Price Update"); + LogTrading("Client::Handle_OP_Trader: Trader Price Update"); HandleTraderPriceUpdate(app); } else { - Log(Logs::Detail, Logs::Trading, "Unknown size for OP_Trader: %i\n", app->size); - Log(Logs::General, Logs::Error, "Unknown size for OP_Trader: %i\n", app->size); + LogTrading("Unknown size for OP_Trader: [{}]\n", app->size); + LogError("Unknown size for OP_Trader: [{}]\n", app->size); DumpPacket(app); return; } @@ -14087,7 +14050,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) // Client has elected to buy an item from a Trader // if (app->size != sizeof(TraderBuy_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_TraderBuy, size=%i, expected %i", app->size, sizeof(TraderBuy_Struct)); + LogError("Wrong size: OP_TraderBuy, size=[{}], expected [{}]", app->size, sizeof(TraderBuy_Struct)); return; } @@ -14095,10 +14058,10 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) if (Client* Trader = entity_list.GetClientByID(tbs->TraderID)) { BuyTraderItem(tbs, Trader, app); - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Buy Trader Item "); + LogTrading("Client::Handle_OP_TraderBuy: Buy Trader Item "); } else { - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Null Client Pointer"); + LogTrading("Client::Handle_OP_TraderBuy: Null Client Pointer"); } @@ -14108,7 +14071,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app) { if (app->size != sizeof(TradeRequest_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_TradeRequest, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); + LogError("Wrong size: OP_TradeRequest, size=[{}], expected [{}]", app->size, sizeof(TradeRequest_Struct)); return; } // Client requesting a trade session from an npc/client @@ -14144,7 +14107,7 @@ void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app) void Client::Handle_OP_TradeRequestAck(const EQApplicationPacket *app) { if (app->size != sizeof(TradeRequest_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size: OP_TradeRequestAck, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); + LogError("Wrong size: OP_TradeRequestAck, size=[{}], expected [{}]", app->size, sizeof(TradeRequest_Struct)); return; } // Trade request recipient is acknowledging they are able to trade @@ -14169,12 +14132,12 @@ void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) TraderClick_Struct* tcs = (TraderClick_Struct*)app->pBuffer; - Log(Logs::Detail, Logs::Trading, "Handle_OP_TraderShop: TraderClick_Struct TraderID %d, Code %d, Unknown008 %d, Approval %d", + LogTrading("Handle_OP_TraderShop: TraderClick_Struct TraderID [{}], Code [{}], Unknown008 [{}], Approval [{}]", tcs->TraderID, tcs->Code, tcs->Unknown008, tcs->Approval); if (tcs->Code == BazaarWelcome) { - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Sent Bazaar Welcome Info"); + LogTrading("Client::Handle_OP_TraderShop: Sent Bazaar Welcome Info"); SendBazaarWelcome(); } else @@ -14190,10 +14153,10 @@ void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) if (Trader) { outtcs->Approval = Trader->WithCustomer(GetID()); - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Shop Request (%s) to (%s) with Approval: %d", GetCleanName(), Trader->GetCleanName(), outtcs->Approval); + LogTrading("Client::Handle_OP_TraderShop: Shop Request ([{}]) to ([{}]) with Approval: [{}]", GetCleanName(), Trader->GetCleanName(), outtcs->Approval); } else { - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)" + LogTrading("Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)" " returned a nullptr pointer"); safe_delete(outapp); return; @@ -14210,12 +14173,12 @@ void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) this->BulkSendTraderInventory(Trader->CharacterID()); Trader->Trader_CustomerBrowsing(this); TraderID = tcs->TraderID; - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Trader Inventory Sent"); + LogTrading("Client::Handle_OP_TraderShop: Trader Inventory Sent"); } else { MessageString(Chat::Yellow, TRADER_BUSY); - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Trader Busy"); + LogTrading("Client::Handle_OP_TraderShop: Trader Busy"); } safe_delete(outapp); @@ -14228,7 +14191,7 @@ void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) // RoF+ // Client requested Bazaar Welcome Info (Trader and Item Total Counts) SendBazaarWelcome(); - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Sent Bazaar Welcome Info"); + LogTrading("Client::Handle_OP_TraderShop: Sent Bazaar Welcome Info"); } else if (app->size == sizeof(TraderBuy_Struct)) { @@ -14240,12 +14203,12 @@ void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) if (Client* Trader = entity_list.GetClientByID(tbs->TraderID)) { BuyTraderItem(tbs, Trader, app); - Log(Logs::Detail, Logs::Trading, "Handle_OP_TraderShop: Buy Action %d, Price %d, Trader %d, ItemID %d, Quantity %d, ItemName, %s", + LogTrading("Handle_OP_TraderShop: Buy Action [{}], Price [{}], Trader [{}], ItemID [{}], Quantity [{}], ItemName, [{}]", tbs->Action, tbs->Price, tbs->TraderID, tbs->ItemID, tbs->Quantity, tbs->ItemName); } else { - Log(Logs::Detail, Logs::Trading, "OP_TraderShop: Null Client Pointer"); + LogTrading("OP_TraderShop: Null Client Pointer"); } } else if (app->size == 4) @@ -14261,24 +14224,24 @@ void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) if (c) { c->WithCustomer(0); - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: End Transaction - Code %d", Command); + LogTrading("Client::Handle_OP_Trader: End Transaction - Code [{}]", Command); } else { - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Null Client Pointer for Trader - Code %d", Command); + LogTrading("Client::Handle_OP_Trader: Null Client Pointer for Trader - Code [{}]", Command); } EQApplicationPacket empty(OP_ShopEndConfirm); QueuePacket(&empty); } else { - Log(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Unhandled Code %d", Command); + LogTrading("Client::Handle_OP_Trader: Unhandled Code [{}]", Command); } } else { - Log(Logs::Detail, Logs::Trading, "Unknown size for OP_TraderShop: %i\n", app->size); - Log(Logs::General, Logs::Error, "Unknown size for OP_TraderShop: %i\n", app->size); + LogTrading("Unknown size for OP_TraderShop: [{}]\n", app->size); + LogError("Unknown size for OP_TraderShop: [{}]\n", app->size); DumpPacket(app); return; } @@ -14287,7 +14250,7 @@ void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) void Client::Handle_OP_TradeSkillCombine(const EQApplicationPacket *app) { if (app->size != sizeof(NewCombine_Struct)) { - Log(Logs::General, Logs::Error, "Invalid size for NewCombine_Struct: Expected: %i, Got: %i", + LogError("Invalid size for NewCombine_Struct: Expected: [{}], Got: [{}]", sizeof(NewCombine_Struct), app->size); return; } @@ -14308,7 +14271,7 @@ void Client::Handle_OP_Translocate(const EQApplicationPacket *app) { if (app->size != sizeof(Translocate_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_Translocate expected %i got %i", sizeof(Translocate_Struct), app->size); + LogDebug("Size mismatch in OP_Translocate expected [{}] got [{}]", sizeof(Translocate_Struct), app->size); DumpPacket(app); return; } @@ -14356,7 +14319,7 @@ void Client::Handle_OP_Translocate(const EQApplicationPacket *app) void Client::Handle_OP_TributeItem(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Tribute, "Received OP_TributeItem of length %d", app->size); + LogTribute("Received OP_TributeItem of length [{}]", app->size); //player donates an item... if (app->size != sizeof(TributeItem_Struct)) @@ -14374,7 +14337,7 @@ void Client::Handle_OP_TributeItem(const EQApplicationPacket *app) t->tribute_points = TributeItem(t->slot, t->quantity); - Log(Logs::Detail, Logs::Tribute, "Sending tribute item reply with %d points", t->tribute_points); + LogTribute("Sending tribute item reply with [{}] points", t->tribute_points); QueuePacket(app); } @@ -14383,7 +14346,7 @@ void Client::Handle_OP_TributeItem(const EQApplicationPacket *app) void Client::Handle_OP_TributeMoney(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Tribute, "Received OP_TributeMoney of length %d", app->size); + LogTribute("Received OP_TributeMoney of length [{}]", app->size); //player donates money if (app->size != sizeof(TributeMoney_Struct)) @@ -14401,7 +14364,7 @@ void Client::Handle_OP_TributeMoney(const EQApplicationPacket *app) t->tribute_points = TributeMoney(t->platinum); - Log(Logs::Detail, Logs::Tribute, "Sending tribute money reply with %d points", t->tribute_points); + LogTribute("Sending tribute money reply with [{}] points", t->tribute_points); QueuePacket(app); } @@ -14410,17 +14373,17 @@ void Client::Handle_OP_TributeMoney(const EQApplicationPacket *app) void Client::Handle_OP_TributeNPC(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Tribute, "Received OP_TributeNPC of length %d", app->size); + LogTribute("Received OP_TributeNPC of length [{}]", app->size); return; } void Client::Handle_OP_TributeToggle(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Tribute, "Received OP_TributeToggle of length %d", app->size); + LogTribute("Received OP_TributeToggle of length [{}]", app->size); if (app->size != sizeof(uint32)) - Log(Logs::General, Logs::Error, "Invalid size on OP_TributeToggle packet"); + LogError("Invalid size on OP_TributeToggle packet"); else { uint32 *val = (uint32 *)app->pBuffer; ToggleTribute(*val ? true : false); @@ -14430,11 +14393,11 @@ void Client::Handle_OP_TributeToggle(const EQApplicationPacket *app) void Client::Handle_OP_TributeUpdate(const EQApplicationPacket *app) { - Log(Logs::Detail, Logs::Tribute, "Received OP_TributeUpdate of length %d", app->size); + LogTribute("Received OP_TributeUpdate of length [{}]", app->size); //sent when the client changes their tribute settings... if (app->size != sizeof(TributeInfo_Struct)) - Log(Logs::General, Logs::Error, "Invalid size on OP_TributeUpdate packet"); + LogError("Invalid size on OP_TributeUpdate packet"); else { TributeInfo_Struct *t = (TributeInfo_Struct *)app->pBuffer; ChangeTributeSettings(t); @@ -14445,9 +14408,7 @@ void Client::Handle_OP_TributeUpdate(const EQApplicationPacket *app) void Client::Handle_OP_VetClaimRequest(const EQApplicationPacket *app) { if (app->size < sizeof(VeteranClaim)) { - Log(Logs::General, Logs::None, - "OP_VetClaimRequest size lower than expected: got %u expected at least %u", app->size, - sizeof(VeteranClaim)); + LogDebug("OP_VetClaimRequest size lower than expected: got [{}] expected at least [{}]", app->size, sizeof(VeteranClaim)); DumpPacket(app); return; } @@ -14477,8 +14438,7 @@ void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app) if (app->size != sizeof(VoiceMacroIn_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_VoiceMacroIn expected %i got %i", - sizeof(VoiceMacroIn_Struct), app->size); + LogDebug("Size mismatch in OP_VoiceMacroIn expected [{}] got [{}]", sizeof(VoiceMacroIn_Struct), app->size); DumpPacket(app); @@ -14496,8 +14456,7 @@ void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app) void Client::Handle_OP_UpdateAura(const EQApplicationPacket *app) { if (app->size != sizeof(AuraDestory_Struct)) { - Log(Logs::General, Logs::None, "Size mismatch in OP_UpdateAura expected %i got %i", - sizeof(AuraDestory_Struct), app->size); + LogDebug("Size mismatch in OP_UpdateAura expected [{}] got [{}]", sizeof(AuraDestory_Struct), app->size); return; } @@ -14549,7 +14508,7 @@ void Client::Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app) { if (app->size != 1) { - Log(Logs::General, Logs::None, "Size mismatch in OP_XTargetAutoAddHaters, expected 1, got %i", app->size); + LogDebug("Size mismatch in OP_XTargetAutoAddHaters, expected 1, got [{}]", app->size); DumpPacket(app); return; } @@ -14561,7 +14520,7 @@ void Client::Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app) void Client::Handle_OP_XTargetOpen(const EQApplicationPacket *app) { if (app->size != 4) { - Log(Logs::General, Logs::None, "Size mismatch in OP_XTargetOpen, expected 1, got %i", app->size); + LogDebug("Size mismatch in OP_XTargetOpen, expected 1, got [{}]", app->size); DumpPacket(app); return; } @@ -14574,7 +14533,7 @@ void Client::Handle_OP_XTargetRequest(const EQApplicationPacket *app) { if (app->size < 12) { - Log(Logs::General, Logs::None, "Size mismatch in OP_XTargetRequest, expected at least 12, got %i", app->size); + LogDebug("Size mismatch in OP_XTargetRequest, expected at least 12, got [{}]", app->size); DumpPacket(app); return; } @@ -14797,7 +14756,7 @@ void Client::Handle_OP_XTargetRequest(const EQApplicationPacket *app) } default: - Log(Logs::General, Logs::None, "Unhandled XTarget Type %i", Type); + LogDebug("Unhandled XTarget Type [{}]", Type); break; } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 7465ac925..a857c3c21 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -241,12 +241,12 @@ bool Client::Process() { if (RuleB(Character, ActiveInvSnapshots) && time(nullptr) >= GetNextInvSnapshotTime()) { if (database.SaveCharacterInvSnapshot(CharacterID())) { SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM)); - Log(Logs::Moderate, Logs::Inventory, "Successful inventory snapshot taken of %s - setting next interval for %i minute%s.", + LogInventory("Successful inventory snapshot taken of [{}] - setting next interval for [{}] minute[{}]", GetName(), RuleI(Character, InvSnapshotMinIntervalM), (RuleI(Character, InvSnapshotMinIntervalM) == 1 ? "" : "s")); } else { SetNextInvSnapshot(RuleI(Character, InvSnapshotMinRetryM)); - Log(Logs::Moderate, Logs::Inventory, "Failed to take inventory snapshot of %s - retrying in %i minute%s.", + LogInventory("Failed to take inventory snapshot of [{}] - retrying in [{}] minute[{}]", GetName(), RuleI(Character, InvSnapshotMinRetryM), (RuleI(Character, InvSnapshotMinRetryM) == 1 ? "" : "s")); } } @@ -559,11 +559,10 @@ bool Client::Process() { if (client_state != CLIENT_LINKDEAD && !eqs->CheckState(ESTABLISHED)) { OnDisconnect(true); - Log(Logs::General, Logs::Zone_Server, "Client linkdead: %s", name); + LogInfo("Client linkdead: {}", name); - if (GetGM()) { - if (GetMerc()) - { + if (Admin() > 100) { + if (GetMerc()) { GetMerc()->Save(); GetMerc()->Depop(); } @@ -608,7 +607,7 @@ bool Client::Process() { npc_scan_count++; } - Log(Logs::General, Logs::Aggro, "Checking Reverse Aggro (client->npc) scanned_npcs (%i)", npc_scan_count); + LogAggro("Checking Reverse Aggro (client->npc) scanned_npcs ([{}])", npc_scan_count); } if (client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED))) @@ -710,7 +709,7 @@ void Client::OnDisconnect(bool hard_disconnect) { Mob *Other = trade->With(); if(Other) { - Log(Logs::Detail, Logs::Trading, "Client disconnected during a trade. Returning their items."); + LogTrading("Client disconnected during a trade. Returning their items"); FinishTrade(this); if(Other->IsClient()) @@ -742,7 +741,7 @@ void Client::BulkSendInventoryItems() if(inst) { bool is_arrow = (inst->GetItem()->ItemType == EQEmu::item::ItemTypeArrow) ? true : false; int16 free_slot_id = m_inv.FindFreeSlot(inst->IsClassBag(), true, inst->GetItem()->Size, is_arrow); - Log(Logs::Detail, Logs::Inventory, "Incomplete Trade Transaction: Moving %s from slot %i to %i", inst->GetItem()->Name, slot_id, free_slot_id); + LogInventory("Incomplete Trade Transaction: Moving [{}] from slot [{}] to [{}]", inst->GetItem()->Name, slot_id, free_slot_id); PutItemInInventory(free_slot_id, *inst, false); database.SaveInventory(character_id, nullptr, slot_id); safe_delete(inst); @@ -771,7 +770,7 @@ void Client::BulkSendInventoryItems() inst->Serialize(ob, slot_id); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Inventory, "Serialization failed on item slot %d during BulkSendInventoryItems. Item skipped.", slot_id); + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); last_pos = ob.tellp(); } @@ -785,7 +784,7 @@ void Client::BulkSendInventoryItems() inst->Serialize(ob, slot_id); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Inventory, "Serialization failed on item slot %d during BulkSendInventoryItems. Item skipped.", slot_id); + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); last_pos = ob.tellp(); } @@ -799,7 +798,7 @@ void Client::BulkSendInventoryItems() inst->Serialize(ob, slot_id); if (ob.tellp() == last_pos) - Log(Logs::General, Logs::Inventory, "Serialization failed on item slot %d during BulkSendInventoryItems. Item skipped.", slot_id); + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); last_pos = ob.tellp(); } @@ -887,7 +886,7 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { // Account for merchant lists with gaps. if (ml.slot >= i) { if (ml.slot > i) - Log(Logs::General, Logs::None, "(WARNING) Merchantlist contains gap at slot %d. Merchant: %d, NPC: %d", i, merchant_id, npcid); + LogDebug("(WARNING) Merchantlist contains gap at slot [{}]. Merchant: [{}], NPC: [{}]", i, merchant_id, npcid); i = ml.slot + 1; } } @@ -977,7 +976,7 @@ uint8 Client::WithCustomer(uint16 NewCustomer){ Client* c = entity_list.GetClientByID(CustomerID); if(!c) { - Log(Logs::Detail, Logs::Trading, "Previous customer has gone away."); + LogTrading("Previous customer has gone away"); CustomerID = NewCustomer; return 1; } @@ -989,7 +988,7 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I { if(PendingRezzXP < 0) { // pendingrezexp is set to -1 if we are not expecting an OP_RezzAnswer - Log(Logs::Detail, Logs::Spells, "Unexpected OP_RezzAnswer. Ignoring it."); + LogSpells("Unexpected OP_RezzAnswer. Ignoring it"); Message(Chat::Red, "You have already been resurrected.\n"); return; } @@ -999,7 +998,7 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I // Mark the corpse as rezzed in the database, just in case the corpse has buried, or the zone the // corpse is in has shutdown since the rez spell was cast. database.MarkCorpseAsRezzed(PendingRezzDBID); - Log(Logs::Detail, Logs::Spells, "Player %s got a %i Rezz, spellid %i in zone%i, instance id %i", + LogSpells("Player [{}] got a [{}] Rezz, spellid [{}] in zone[{}], instance id [{}]", this->name, (uint16)spells[SpellID].base[0], SpellID, ZoneID, InstanceID); @@ -1053,7 +1052,7 @@ void Client::OPMemorizeSpell(const EQApplicationPacket* app) { if(app->size != sizeof(MemorizeSpell_Struct)) { - Log(Logs::General, Logs::Error, "Wrong size on OP_MemorizeSpell. Got: %i, Expected: %i", app->size, sizeof(MemorizeSpell_Struct)); + LogError("Wrong size on OP_MemorizeSpell. Got: [{}], Expected: [{}]", app->size, sizeof(MemorizeSpell_Struct)); DumpPacket(app); return; } @@ -1612,12 +1611,12 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app) EQEmu::skills::SkillType skill = (EQEmu::skills::SkillType)gmskill->skill_id; if(!CanHaveSkill(skill)) { - Log(Logs::Detail, Logs::Skills, "Tried to train skill %d, which is not allowed.", skill); + LogSkills("Tried to train skill [{}], which is not allowed", skill); return; } if(MaxSkill(skill) == 0) { - Log(Logs::Detail, Logs::Skills, "Tried to train skill %d, but training is not allowed at this level.", skill); + LogSkills("Tried to train skill [{}], but training is not allowed at this level", skill); return; } @@ -1802,8 +1801,7 @@ void Client::DoStaminaHungerUpdate() auto outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); Stamina_Struct *sta = (Stamina_Struct *)outapp->pBuffer; - Log(Logs::General, Logs::Food, "Client::DoStaminaHungerUpdate() hunger_level: %i thirst_level: %i before loss", - m_pp.hunger_level, m_pp.thirst_level); + LogFood("Client::DoStaminaHungerUpdate() hunger_level: [{}] thirst_level: [{}] before loss", m_pp.hunger_level, m_pp.thirst_level); if (zone->GetZoneID() != 151 && !GetGM()) { int loss = RuleI(Character, FoodLossPerUpdate); @@ -1824,9 +1822,7 @@ void Client::DoStaminaHungerUpdate() sta->water = 6000; } - Log(Logs::General, Logs::Food, - "Client::DoStaminaHungerUpdate() Current hunger_level: %i = (%i minutes left) thirst_level: %i = (%i " - "minutes left) - after loss", + LogFood("Client::DoStaminaHungerUpdate() Current hunger_level: [{}] = ([{}] minutes left) thirst_level: [{}] = ([{}] minutes left) - after loss", m_pp.hunger_level, m_pp.hunger_level, m_pp.thirst_level, m_pp.thirst_level); FastQueuePacket(&outapp); @@ -2005,7 +2001,7 @@ void Client::HandleRespawnFromHover(uint32 Option) { if (PendingRezzXP < 0 || PendingRezzSpellID == 0) { - Log(Logs::Detail, Logs::Spells, "Unexpected Rezz from hover request."); + LogSpells("Unexpected Rezz from hover request"); return; } SetHP(GetMaxHP() / 5); @@ -2039,10 +2035,10 @@ void Client::HandleRespawnFromHover(uint32 Option) if (corpse && corpse->IsCorpse()) { - Log(Logs::Detail, Logs::Spells, "Hover Rez in zone %s for corpse %s", + LogSpells("Hover Rez in zone [{}] for corpse [{}]", zone->GetShortName(), PendingRezzCorpseName.c_str()); - Log(Logs::Detail, Logs::Spells, "Found corpse. Marking corpse as rezzed."); + LogSpells("Found corpse. Marking corpse as rezzed"); corpse->IsRezzed(true); corpse->CompleteResurrection(); diff --git a/zone/command.cpp b/zone/command.cpp index b979716c8..1ddd99aa0 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -182,6 +182,7 @@ int command_init(void) command_add("crashtest", "- Crash the zoneserver", 255, command_crashtest) || command_add("cvs", "- Summary of client versions currently online.", 200, command_cvs) || command_add("damage", "[amount] - Damage your target", 100, command_damage) || + command_add("databuckets", "View|Delete [key] [limit]- View data buckets, limit 50 default or Delete databucket by key", 80, command_databuckets) || command_add("date", "[yyyy] [mm] [dd] [HH] [MM] - Set EQ time", 90, command_date) || command_add("dbspawn2", "[spawngroup] [respawn] [variance] - Spawn an NPC from a predefined row in the spawn2 table", 100, command_dbspawn2) || command_add("delacct", "[accountname] - Delete an account", 150, command_delacct) || @@ -453,33 +454,90 @@ int command_init(void) std::map>> command_settings; database.GetCommandSettings(command_settings); - std::map working_cl = commandlist; - for (auto iter_cl = working_cl.begin(); iter_cl != working_cl.end(); ++iter_cl) { - auto iter_cs = command_settings.find(iter_cl->first); - if (iter_cs == command_settings.end()) { - if (iter_cl->second->access == 0) - Log(Logs::General, Logs::Commands, "command_init(): Warning: Command '%s' defaulting to access level 0!", iter_cl->first.c_str()); + std::vector> injected_command_settings; + std::vector orphaned_command_settings; + + for (auto cs_iter : command_settings) { + + auto cl_iter = commandlist.find(cs_iter.first); + if (cl_iter == commandlist.end()) { + + orphaned_command_settings.push_back(cs_iter.first); + LogInfo( + "Command [{}] no longer exists... Deleting orphaned entry from `command_settings` table...", + cs_iter.first.c_str() + ); + } + } + + if (orphaned_command_settings.size()) { + if (!database.UpdateOrphanedCommandSettings(orphaned_command_settings)) { + LogInfo("Failed to process 'Orphaned Commands' update operation."); + } + } + + auto working_cl = commandlist; + for (auto working_cl_iter : working_cl) { + + auto cs_iter = command_settings.find(working_cl_iter.first); + if (cs_iter == command_settings.end()) { + + injected_command_settings.push_back(std::pair(working_cl_iter.first, working_cl_iter.second->access)); + LogInfo( + "New Command [{}] found... Adding to `command_settings` table with access [{}]...", + working_cl_iter.first.c_str(), + working_cl_iter.second->access + ); + + if (working_cl_iter.second->access == 0) { + LogCommands( + "command_init(): Warning: Command [{}] defaulting to access level 0!", + working_cl_iter.first.c_str() + ); + } + continue; } - iter_cl->second->access = iter_cs->second.first; - Log(Logs::General, Logs::Commands, "command_init(): - Command '%s' set to access level %d.", iter_cl->first.c_str(), iter_cs->second.first); - if (iter_cs->second.second.empty()) + working_cl_iter.second->access = cs_iter->second.first; + LogCommands( + "command_init(): - Command [{}] set to access level [{}]", + working_cl_iter.first.c_str(), + cs_iter->second.first + ); + + if (cs_iter->second.second.empty()) { continue; + } - for (auto iter_aka = iter_cs->second.second.begin(); iter_aka != iter_cs->second.second.end(); - ++iter_aka) { - if (iter_aka->empty()) - continue; - if (commandlist.find(*iter_aka) != commandlist.end()) { - Log(Logs::General, Logs::Commands, "command_init(): Warning: Alias '%s' already exists as a command - skipping!", iter_aka->c_str()); + for (auto alias_iter : cs_iter->second.second) { + if (alias_iter.empty()) { continue; } - commandlist[*iter_aka] = iter_cl->second; - commandaliases[*iter_aka] = iter_cl->first; + if (commandlist.find(alias_iter) != commandlist.end()) { + LogCommands( + "command_init(): Warning: Alias [{}] already exists as a command - skipping!", + alias_iter.c_str() + ); + + continue; + } - Log(Logs::General, Logs::Commands, "command_init(): - Alias '%s' added to command '%s'.", iter_aka->c_str(), commandaliases[*iter_aka].c_str()); + commandlist[alias_iter] = working_cl_iter.second; + commandaliases[alias_iter] = working_cl_iter.first; + + LogCommands( + "command_init(): - Alias [{}] added to command [{}]", + alias_iter.c_str(), + commandaliases[alias_iter].c_str() + ); + } + } + + if (injected_command_settings.size()) { + if (!database.UpdateInjectedCommandSettings(injected_command_settings)) { + LogInfo("Failed to process 'Injected Commands' update operation."); } } @@ -519,21 +577,21 @@ void command_deinit(void) int command_add(std::string command_name, const char *desc, int access, CmdFuncPtr function) { if (command_name.empty()) { - Log(Logs::General, Logs::Error, "command_add() - Command added with empty name string - check command.cpp."); + LogError("command_add() - Command added with empty name string - check command.cpp"); return -1; } if (function == nullptr) { - Log(Logs::General, Logs::Error, "command_add() - Command '%s' added without a valid function pointer - check command.cpp.", command_name.c_str()); + LogError("command_add() - Command [{}] added without a valid function pointer - check command.cpp", command_name.c_str()); return -1; } if (commandlist.count(command_name) != 0) { - Log(Logs::General, Logs::Error, "command_add() - Command '%s' is a duplicate command name - check command.cpp.", command_name.c_str()); + LogError("command_add() - Command [{}] is a duplicate command name - check command.cpp", command_name.c_str()); return -1; } for (auto iter = commandlist.begin(); iter != commandlist.end(); ++iter) { if (iter->second->function != function) continue; - Log(Logs::General, Logs::Error, "command_add() - Command '%s' equates to an alias of '%s' - check command.cpp.", command_name.c_str(), iter->first.c_str()); + LogError("command_add() - Command [{}] equates to an alias of [{}] - check command.cpp", command_name.c_str(), iter->first.c_str()); return -1; } @@ -587,11 +645,11 @@ int command_realdispatch(Client *c, const char *message) } if(cur->access >= COMMANDS_LOGGING_MIN_STATUS) { - Log(Logs::General, Logs::Commands, "%s (%s) used command: %s (target=%s)", c->GetName(), c->AccountName(), message, c->GetTarget()?c->GetTarget()->GetName():"NONE"); + LogCommands("[{}] ([{}]) used command: [{}] (target=[{}])", c->GetName(), c->AccountName(), message, c->GetTarget()?c->GetTarget()->GetName():"NONE"); } if(cur->function == nullptr) { - Log(Logs::General, Logs::Error, "Command '%s' has a null function\n", cstr.c_str()); + LogError("Command [{}] has a null function\n", cstr.c_str()); return(-1); } else { //dispatch C++ command @@ -1376,7 +1434,7 @@ void command_viewpetition(Client *c, const Seperator *sep) if (!results.Success()) return; - Log(Logs::General, Logs::Normal, "View petition request from %s, petition number: %i", c->GetName(), atoi(sep->argplus[1]) ); + LogInfo("View petition request from [{}], petition number: [{}]", c->GetName(), atoi(sep->argplus[1]) ); if (results.RowCount() == 0) { c->Message(Chat::Red,"There was an error in your request: ID not found! Please check the Id and try again."); @@ -1401,7 +1459,7 @@ void command_petitioninfo(Client *c, const Seperator *sep) if (!results.Success()) return; - Log(Logs::General, Logs::Normal, "Petition information request from %s, petition number:", c->GetName(), atoi(sep->argplus[1]) ); + LogInfo("Petition information request from [{}], petition number:", c->GetName(), atoi(sep->argplus[1]) ); if (results.RowCount() == 0) { c->Message(Chat::Red,"There was an error in your request: ID not found! Please check the Id and try again."); @@ -1427,7 +1485,7 @@ void command_delpetition(Client *c, const Seperator *sep) if (!results.Success()) return; - Log(Logs::General, Logs::Normal, "Delete petition request from %s, petition number:", c->GetName(), atoi(sep->argplus[1]) ); + LogInfo("Delete petition request from [{}], petition number:", c->GetName(), atoi(sep->argplus[1]) ); } @@ -1913,7 +1971,7 @@ void command_permaclass(Client *c, const Seperator *sep) c->Message(Chat::White,"Target is not a client."); else { c->Message(Chat::White, "Setting %s's class...Sending to char select.", t->GetName()); - Log(Logs::General, Logs::Normal, "Class change request from %s for %s, requested class:%i", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); + LogInfo("Class change request from [{}] for [{}], requested class:[{}]", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); t->SetBaseClass(atoi(sep->arg[1])); t->Save(); t->Kick("Class was changed."); @@ -1935,7 +1993,7 @@ void command_permarace(Client *c, const Seperator *sep) c->Message(Chat::White,"Target is not a client."); else { c->Message(Chat::White, "Setting %s's race - zone to take effect", t->GetName()); - Log(Logs::General, Logs::Normal, "Permanant race change request from %s for %s, requested race:%i", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); + LogInfo("Permanant race change request from [{}] for [{}], requested race:[{}]", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); uint32 tmp = Mob::GetDefaultGender(atoi(sep->arg[1]), t->GetBaseGender()); t->SetBaseRace(atoi(sep->arg[1])); t->SetBaseGender(tmp); @@ -1959,7 +2017,7 @@ void command_permagender(Client *c, const Seperator *sep) c->Message(Chat::White,"Target is not a client."); else { c->Message(Chat::White, "Setting %s's gender - zone to take effect", t->GetName()); - Log(Logs::General, Logs::Normal, "Permanant gender change request from %s for %s, requested gender:%i", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); + LogInfo("Permanant gender change request from [{}] for [{}], requested gender:[{}]", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); t->SetBaseGender(atoi(sep->arg[1])); t->Save(); t->SendIllusionPacket(atoi(sep->arg[1])); @@ -2298,7 +2356,7 @@ void command_dbspawn2(Client *c, const Seperator *sep) { if (sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3)) { - Log(Logs::General, Logs::Normal, "Spawning database spawn"); + LogInfo("Spawning database spawn"); uint16 cond = 0; int16 cond_min = 0; if(sep->IsNumber(4)) { @@ -2320,13 +2378,18 @@ void command_shutdown(Client *c, const Seperator *sep) void command_delacct(Client *c, const Seperator *sep) { - if(sep->arg[1][0] == 0) + if (sep->arg[1][0] == 0) c->Message(Chat::White, "Format: #delacct accountname"); - else - if (database.DeleteAccount(sep->arg[1])) + else { + std::string user; + std::string loginserver; + ParseAccountString(sep->arg[1], user, loginserver); + + if (database.DeleteAccount(user.c_str(), loginserver.c_str())) c->Message(Chat::White, "The account was deleted."); else c->Message(Chat::White, "Unable to delete account."); + } } void command_setpass(Client *c, const Seperator *sep) @@ -2334,8 +2397,12 @@ void command_setpass(Client *c, const Seperator *sep) if(sep->argnum != 2) c->Message(Chat::White, "Format: #setpass accountname password"); else { + std::string user; + std::string loginserver; + ParseAccountString(sep->arg[1], user, loginserver); + int16 tmpstatus = 0; - uint32 tmpid = database.GetAccountIDByName(sep->arg[1], &tmpstatus); + uint32 tmpid = database.GetAccountIDByName(user.c_str(), loginserver.c_str(), &tmpstatus); if (!tmpid) c->Message(Chat::White, "Error: Account not found"); else if (tmpstatus > c->Admin()) @@ -2356,7 +2423,7 @@ void command_setlsinfo(Client *c, const Seperator *sep) ServerLSAccountUpdate_Struct* s = (ServerLSAccountUpdate_Struct *) pack->pBuffer; s->useraccountid = c->LSAccountID(); strn0cpy(s->useraccount, c->AccountName(), 30); - strn0cpy(s->useremail, sep->arg[1], 100); + strn0cpy(s->user_email, sep->arg[1], 100); strn0cpy(s->userpassword, sep->arg[2], 50); worldserver.SendPacket(pack); c->Message(Chat::White, "Login Server update packet sent."); @@ -2698,7 +2765,7 @@ void command_setlanguage(Client *c, const Seperator *sep) } else { - Log(Logs::General, Logs::Normal, "Set language request from %s, target:%s lang_id:%i value:%i", c->GetName(), c->GetTarget()->GetName(), atoi(sep->arg[1]), atoi(sep->arg[2]) ); + LogInfo("Set language request from [{}], target:[{}] lang_id:[{}] value:[{}]", c->GetName(), c->GetTarget()->GetName(), atoi(sep->arg[1]), atoi(sep->arg[2]) ); uint8 langid = (uint8)atoi(sep->arg[1]); uint8 value = (uint8)atoi(sep->arg[2]); c->GetTarget()->CastToClient()->SetLanguageSkill( langid, value ); @@ -2723,7 +2790,7 @@ void command_setskill(Client *c, const Seperator *sep) c->Message(Chat::White, " x = 0 to %d", HIGHEST_CAN_SET_SKILL); } else { - Log(Logs::General, Logs::Normal, "Set skill request from %s, target:%s skill_id:%i value:%i", c->GetName(), c->GetTarget()->GetName(), atoi(sep->arg[1]), atoi(sep->arg[2]) ); + LogInfo("Set skill request from [{}], target:[{}] skill_id:[{}] value:[{}]", c->GetName(), c->GetTarget()->GetName(), atoi(sep->arg[1]), atoi(sep->arg[2]) ); int skill_num = atoi(sep->arg[1]); uint16 skill_value = atoi(sep->arg[2]); if (skill_num <= EQEmu::skills::HIGHEST_SKILL) @@ -2743,7 +2810,7 @@ void command_setskillall(Client *c, const Seperator *sep) } else { if (c->Admin() >= commandSetSkillsOther || c->GetTarget()==c || c->GetTarget()==0) { - Log(Logs::General, Logs::Normal, "Set ALL skill request from %s, target:%s", c->GetName(), c->GetTarget()->GetName()); + LogInfo("Set ALL skill request from [{}], target:[{}]", c->GetName(), c->GetTarget()->GetName()); uint16 level = atoi(sep->arg[1]); for (EQEmu::skills::SkillType skill_num = EQEmu::skills::Skill1HBlunt; skill_num <= EQEmu::skills::HIGHEST_SKILL; skill_num = (EQEmu::skills::SkillType)(skill_num + 1)) { c->GetTarget()->CastToClient()->SetSkill(skill_num, level); @@ -4141,7 +4208,7 @@ void command_listpetition(Client *c, const Seperator *sep) if (!results.Success()) return; - Log(Logs::General, Logs::Normal, "Petition list requested by %s", c->GetName()); + LogInfo("Petition list requested by [{}]", c->GetName()); if (results.RowCount() == 0) return; @@ -4875,7 +4942,7 @@ void command_lastname(Client *c, const Seperator *sep) if(c->GetTarget() && c->GetTarget()->IsClient()) t=c->GetTarget()->CastToClient(); - Log(Logs::General, Logs::Normal, "#lastname request from %s for %s", c->GetName(), t->GetName()); + LogInfo("#lastname request from [{}] for [{}]", c->GetName(), t->GetName()); if(strlen(sep->arg[1]) <= 70) t->ChangeLastName(sep->arg[1]); @@ -5701,7 +5768,7 @@ void command_iteminfo(Client *c, const Seperator *sep) } auto item = inst->GetItem(); if (!item) { - Log(Logs::General, Logs::Inventory, "(%s) Command #iteminfo processed an item with no data pointer"); + LogInventory("([{}]) Command #iteminfo processed an item with no data pointer"); c->Message(Chat::Red, "Error: This item has no data reference"); return; } @@ -5773,8 +5840,8 @@ void command_flag(Client *c, const Seperator *sep) c->Message(Chat::White, "Usage: #flag [status] [acctname]"); else if (c->Admin() < commandChangeFlags) { -//this check makes banning players by less than this level -//impossible, but i'll leave it in anyways + //this check makes banning players by less than this level + //impossible, but i'll leave it in anyways c->Message(Chat::White, "You may only refresh your own flag, doing so now."); c->UpdateAdmin(); } @@ -5787,11 +5854,15 @@ void command_flag(Client *c, const Seperator *sep) c->Message(Chat::White, "Unable to set GM Flag."); else { c->Message(Chat::White, "Set GM Flag on account."); - auto pack = new ServerPacket(ServerOP_FlagUpdate, 6); - *((uint32*) pack->pBuffer) = database.GetAccountIDByName(sep->argplus[2]); - *((int16*) &pack->pBuffer[4]) = atoi(sep->arg[1]); - worldserver.SendPacket(pack); - delete pack; + + std::string user; + std::string loginserver; + ParseAccountString(sep->argplus[2], user, loginserver); + + ServerPacket pack(ServerOP_FlagUpdate, 6); + *((uint32*) pack.pBuffer) = database.GetAccountIDByName(user.c_str(), loginserver.c_str()); + *((int16*) &pack.pBuffer[4]) = atoi(sep->arg[1]); + worldserver.SendPacket(&pack); } } } @@ -5806,7 +5877,7 @@ void command_time(Client *c, const Seperator *sep) } c->Message(Chat::Red, "Setting world time to %s:%i (Timezone: 0)...", sep->arg[1], minutes); zone->SetTime(atoi(sep->arg[1])+1, minutes); - Log(Logs::General, Logs::Zone_Server, "%s :: Setting world time to %s:%i (Timezone: 0)...", c->GetCleanName(), sep->arg[1], minutes); + LogInfo("{} :: Setting world time to {}:{} (Timezone: 0)...", c->GetCleanName(), sep->arg[1], minutes); } else { c->Message(Chat::Red, "To set the Time: #time HH [MM]"); @@ -5821,7 +5892,7 @@ void command_time(Client *c, const Seperator *sep) zone->zone_time.getEQTimeZoneMin() ); c->Message(Chat::Red, "It is now %s.", timeMessage); - Log(Logs::General, Logs::Zone_Server, "Current Time is: %s", timeMessage); + LogInfo("Current Time is: {}", timeMessage); } } @@ -5955,12 +6026,9 @@ void command_guild(Client *c, const Seperator *sep) } if(guild_id == GUILD_NONE) { - Log(Logs::Detail, Logs::Guilds, "%s: Removing %s (%d) from guild with GM command.", c->GetName(), - sep->arg[2], charid); + LogGuilds("[{}]: Removing [{}] ([{}]) from guild with GM command", c->GetName(), sep->arg[2], charid); } else { - Log(Logs::Detail, Logs::Guilds, "%s: Putting %s (%d) into guild %s (%d) with GM command.", c->GetName(), - sep->arg[2], charid, - guild_mgr.GetGuildName(guild_id), guild_id); + LogGuilds("[{}]: Putting [{}] ([{}]) into guild [{}] ([{}]) with GM command", c->GetName(), sep->arg[2], charid, guild_mgr.GetGuildName(guild_id), guild_id); } if(!guild_mgr.SetGuild(charid, guild_id, GUILD_MEMBER)) { @@ -6007,8 +6075,7 @@ void command_guild(Client *c, const Seperator *sep) return; } - Log(Logs::Detail, Logs::Guilds, "%s: Setting %s (%d)'s guild rank to %d with GM command.", c->GetName(), - sep->arg[2], charid, rank); + LogGuilds("[{}]: Setting [{}] ([{}])'s guild rank to [{}] with GM command", c->GetName(), sep->arg[2], charid, rank); if(!guild_mgr.SetGuildRank(charid, rank)) c->Message(Chat::Red, "Error while setting rank %d on '%s'.", rank, sep->arg[2]); @@ -6049,7 +6116,7 @@ void command_guild(Client *c, const Seperator *sep) uint32 id = guild_mgr.CreateGuild(sep->argplus[3], leader); - Log(Logs::Detail, Logs::Guilds, "%s: Creating guild %s with leader %d with GM command. It was given id %lu.", c->GetName(), + LogGuilds("[{}]: Creating guild [{}] with leader [{}] with GM command. It was given id [{}]", c->GetName(), sep->argplus[3], leader, (unsigned long)id); if (id == GUILD_NONE) @@ -6088,7 +6155,7 @@ void command_guild(Client *c, const Seperator *sep) } } - Log(Logs::Detail, Logs::Guilds, "%s: Deleting guild %s (%d) with GM command.", c->GetName(), + LogGuilds("[{}]: Deleting guild [{}] ([{}]) with GM command", c->GetName(), guild_mgr.GetGuildName(id), id); if (!guild_mgr.DeleteGuild(id)) @@ -6122,7 +6189,7 @@ void command_guild(Client *c, const Seperator *sep) } } - Log(Logs::Detail, Logs::Guilds, "%s: Renaming guild %s (%d) to '%s' with GM command.", c->GetName(), + LogGuilds("[{}]: Renaming guild [{}] ([{}]) to [{}] with GM command", c->GetName(), guild_mgr.GetGuildName(id), id, sep->argplus[3]); if (!guild_mgr.RenameGuild(id, sep->argplus[3])) @@ -6173,7 +6240,7 @@ void command_guild(Client *c, const Seperator *sep) } } - Log(Logs::Detail, Logs::Guilds, "%s: Setting leader of guild %s (%d) to %d with GM command.", c->GetName(), + LogGuilds("[{}]: Setting leader of guild [{}] ([{}]) to [{}] with GM command", c->GetName(), guild_mgr.GetGuildName(id), id, leader); if(!guild_mgr.SetGuildLeader(id, leader)) @@ -6628,7 +6695,7 @@ void command_scribespells(Client *c, const Seperator *sep) t->Message(Chat::White, "Scribing spells to spellbook."); if(t != c) c->Message(Chat::White, "Scribing spells for %s.", t->GetName()); - Log(Logs::General, Logs::Normal, "Scribe spells request for %s from %s, levels: %u -> %u", t->GetName(), c->GetName(), min_level, max_level); + LogInfo("Scribe spells request for [{}] from [{}], levels: [{}] -> [{}]", t->GetName(), c->GetName(), min_level, max_level); int book_slot = t->GetNextAvailableSpellBookSlot(); int spell_id = 0; @@ -6722,7 +6789,7 @@ void command_scribespell(Client *c, const Seperator *sep) { if(t != c) c->Message(Chat::White, "Scribing spell: %s (%i) for %s.", spells[spell_id].name, spell_id, t->GetName()); - Log(Logs::General, Logs::Normal, "Scribe spell: %s (%i) request for %s from %s.", spells[spell_id].name, spell_id, t->GetName(), c->GetName()); + LogInfo("Scribe spell: [{}] ([{}]) request for [{}] from [{}]", spells[spell_id].name, spell_id, t->GetName(), c->GetName()); if (spells[spell_id].classes[WARRIOR] != 0 && spells[spell_id].skill != 52 && spells[spell_id].classes[t->GetPP().class_ - 1] > 0 && !IsDiscipline(spell_id)) { book_slot = t->GetNextAvailableSpellBookSlot(); @@ -6769,7 +6836,7 @@ void command_unscribespell(Client *c, const Seperator *sep) { if(t != c) c->Message(Chat::White, "Unscribing spell: %s (%i) for %s.", spells[spell_id].name, spell_id, t->GetName()); - Log(Logs::General, Logs::Normal, "Unscribe spell: %s (%i) request for %s from %s.", spells[spell_id].name, spell_id, t->GetName(), c->GetName()); + LogInfo("Unscribe spell: [{}] ([{}]) request for [{}] from [{}]", spells[spell_id].name, spell_id, t->GetName(), c->GetName()); } else { t->Message(Chat::Red, "Unable to unscribe spell: %s (%i) from your spellbook. This spell is not scribed.", spells[spell_id].name, spell_id); @@ -8510,7 +8577,7 @@ void command_ucs(Client *c, const Seperator *sep) if (!c) return; - Log(Logs::Detail, Logs::UCS_Server, "Character %s attempting ucs reconnect while ucs server is %savailable", + LogInfo("Character [{}] attempting ucs reconnect while ucs server is [{}] available", c->GetName(), (zone->IsUCSServerAvailable() ? "" : "un")); if (zone->IsUCSServerAvailable()) { @@ -9255,7 +9322,7 @@ void command_traindisc(Client *c, const Seperator *sep) t->Message(Chat::White, "Training disciplines"); if(t != c) c->Message(Chat::White, "Training disciplines for %s.", t->GetName()); - Log(Logs::General, Logs::Normal, "Train disciplines request for %s from %s, levels: %u -> %u", t->GetName(), c->GetName(), min_level, max_level); + LogInfo("Train disciplines request for [{}] from [{}], levels: [{}] -> [{}]", t->GetName(), c->GetName(), min_level, max_level); int spell_id = 0; int count = 0; @@ -12201,7 +12268,7 @@ void command_logtest(Client *c, const Seperator *sep){ uint32 i = 0; t = std::clock(); for (i = 0; i < atoi(sep->arg[1]); i++){ - Log(Logs::General, Logs::Debug, "[%u] Test #2... Took %f seconds", i, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); + LogDebug("[[{}]] Test #2 Took [{}] seconds", i, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); } } @@ -12299,7 +12366,7 @@ void command_mysqltest(Client *c, const Seperator *sep) auto results = database.QueryDatabase(query); } } - Log(Logs::General, Logs::Debug, "MySQL Test... Took %f seconds", ((float)(std::clock() - t)) / CLOCKS_PER_SEC); + LogDebug("MySQL Test Took [{}] seconds", ((float)(std::clock() - t)) / CLOCKS_PER_SEC); } void command_resetaa_timer(Client *c, const Seperator *sep) { @@ -12574,6 +12641,85 @@ void command_scale(Client *c, const Seperator *sep) } } +void command_databuckets(Client *c, const Seperator *sep) + { + if (sep->arg[1][0] == 0) { + c->Message(Chat::Yellow, "Usage: #databuckets view (partial key)|(limit) OR #databuckets delete (key)"); + return; + } + if (strcasecmp(sep->arg[1], "view") == 0) { + + std::string key_filter; + uint8 limit = 50; + for (int i = 2; i < 4; i++) { + if (sep->arg[i][0] == '\0') + break; + if (strcasecmp(sep->arg[i], "limit") == 0) { + limit = (uint8)atoi(sep->arg[i + 1]); + continue; + } + } + if (sep->arg[2]) { + key_filter = str_tolower(sep->arg[2]); + } + std::string query = "SELECT `id`, `key`, `value`, `expires` FROM data_buckets"; + if (!key_filter.empty()) query += StringFormat(" WHERE `key` LIKE '%%%s%%'", key_filter.c_str()); + query += StringFormat(" LIMIT %u", limit); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + if (results.RowCount() == 0) { + c->Message(Chat::Yellow, "No data_buckets found"); + return; + } + int _ctr = 0; + // put in window for easier readability in case want command line for something else + std::string window_title = "Data Buckets"; + std::string window_text = + "" + "" + "" + "" + "" + "" + ""; + for (auto row = results.begin(); row != results.end(); ++row) { + auto id = static_cast(atoi(row[0])); + std::string key = row[1]; + std::string value = row[2]; + std::string expires = row[3]; + window_text.append(StringFormat( + "" + "" + "" + "" + "" + "", + id, + expires.c_str(), + key.c_str(), + value.c_str() + )); + _ctr++; + std::string del_saylink = StringFormat("#databuckets delete %s", key.c_str()); + c->Message(Chat::White, "%s : %s", + EQEmu::SayLinkEngine::GenerateQuestSaylink(del_saylink, false, "Delete").c_str(), key.c_str(), " Value: ", value.c_str()); + } + window_text.append("
IDExpiresKeyValue
%u%s%s%s
"); + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + std::string response = _ctr > 0 ? StringFormat("Found %i matching data buckets", _ctr).c_str() : "No Databuckets found."; + c->Message(Chat::Yellow, response.c_str()); + } + else if (strcasecmp(sep->arg[1], "delete") == 0) + { + if (DataBucket::DeleteData(sep->argplus[2])) + c->Message(Chat::Yellow, "data bucket %s deleted.", sep->argplus[2]); + else + c->Message(Chat::Red, "An error occurred deleting data bucket %s", sep->argplus[2]); + return; + } +} + void command_who(Client *c, const Seperator *sep) { std::string query = diff --git a/zone/command.h b/zone/command.h index af92ec3f8..b1b488d49 100644 --- a/zone/command.h +++ b/zone/command.h @@ -77,6 +77,7 @@ void command_crashtest(Client *c, const Seperator *sep); void command_cvs(Client *c, const Seperator *sep); void command_d1(Client *c, const Seperator *sep); void command_damage(Client *c, const Seperator *sep); +void command_databuckets(Client *c, const Seperator *sep); void command_date(Client *c, const Seperator *sep); void command_dbspawn2(Client *c, const Seperator *sep); void command_delacct(Client *c, const Seperator *sep); diff --git a/zone/common.h b/zone/common.h index 0ffa67468..e06fa134a 100644 --- a/zone/common.h +++ b/zone/common.h @@ -388,6 +388,7 @@ struct StatBonuses { int32 skillmod[EQEmu::skills::HIGHEST_SKILL + 1]; int32 skillmodmax[EQEmu::skills::HIGHEST_SKILL + 1]; int effective_casting_level; + int adjusted_casting_skill; // SPA 112 for fizzles int reflect_chance; // chance to reflect incoming spell uint32 singingMod; uint32 Amplification; // stacks with singingMod diff --git a/zone/corpse.cpp b/zone/corpse.cpp index e3a5eb80e..09661826d 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -803,7 +803,7 @@ bool Corpse::Process() { spc->zone_id = zone->graveyard_zoneid(); worldserver.SendPacket(pack); safe_delete(pack); - Log(Logs::General, Logs::None, "Moved %s player corpse to the designated graveyard in zone %s.", this->GetName(), database.GetZoneName(zone->graveyard_zoneid())); + LogDebug("Moved [{}] player corpse to the designated graveyard in zone [{}]", this->GetName(), database.GetZoneName(zone->graveyard_zoneid())); corpse_db_id = 0; } @@ -833,10 +833,10 @@ bool Corpse::Process() { Save(); player_corpse_depop = true; corpse_db_id = 0; - Log(Logs::General, Logs::None, "Tagged %s player corpse has buried.", this->GetName()); + LogDebug("Tagged [{}] player corpse has buried", this->GetName()); } else { - Log(Logs::General, Logs::Error, "Unable to bury %s player corpse.", this->GetName()); + LogError("Unable to bury [{}] player corpse", this->GetName()); return true; } } @@ -943,7 +943,7 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a } - Log(Logs::Moderate, Logs::Inventory, "MakeLootRequestPackets() LootRequestType %u for %s", loot_request_type, client->GetName()); + LogInventory("MakeLootRequestPackets() LootRequestType [{}] for [{}]", (int) loot_request_type, client->GetName()); if (loot_request_type == LootRequestType::Forbidden) { SendLootReqErrorPacket(client, LootResponse::NotAtThisTime); @@ -1027,13 +1027,13 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a if (pkitem->RecastDelay) pkinst->SetRecastTimestamp(timestamps.count(pkitem->RecastType) ? timestamps.at(pkitem->RecastType) : 0); - Log(Logs::Detail, Logs::Inventory, "MakeLootRequestPackets() Slot %u, Item '%s'", EQEmu::invslot::CORPSE_BEGIN, pkitem->Name); + LogInventory("MakeLootRequestPackets() Slot [{}], Item [{}]", EQEmu::invslot::CORPSE_BEGIN, pkitem->Name); client->SendItemPacket(EQEmu::invslot::CORPSE_BEGIN, pkinst, ItemPacketLoot); safe_delete(pkinst); } else { - Log(Logs::General, Logs::Inventory, "MakeLootRequestPackets() PlayerKillItem %i not found", pkitemid); + LogInventory("MakeLootRequestPackets() PlayerKillItem [{}] not found", pkitemid); client->Message(Chat::Red, "PlayerKillItem (id: %i) could not be found!", pkitemid); } @@ -1083,7 +1083,7 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a if (item->RecastDelay) inst->SetRecastTimestamp(timestamps.count(item->RecastType) ? timestamps.at(item->RecastType) : 0); - Log(Logs::Moderate, Logs::Inventory, "MakeLootRequestPackets() Slot %i, Item '%s'", loot_slot, item->Name); + LogInventory("MakeLootRequestPackets() Slot [{}], Item [{}]", loot_slot, item->Name); client->SendItemPacket(loot_slot, inst, ItemPacketLoot); safe_delete(inst); @@ -1107,7 +1107,7 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) auto lootitem = (LootingItem_Struct *)app->pBuffer; - Log(Logs::Moderate, Logs::Inventory, "LootItem() LootRequestType %u, Slot %u for %s", loot_request_type, lootitem->slot_id, client->GetName()); + LogInventory("LootItem() LootRequestType [{}], Slot [{}] for [{}]", (int) loot_request_type, lootitem->slot_id, client->GetName()); if (loot_request_type < LootRequestType::GMAllowed) { // LootRequestType::Forbidden and LootRequestType::GMPeek client->QueuePacket(app); diff --git a/zone/doors.cpp b/zone/doors.cpp index d81eb72f8..92e83f06d 100644 --- a/zone/doors.cpp +++ b/zone/doors.cpp @@ -325,7 +325,7 @@ void Doors::HandleClick(Client* sender, uint8 trigger) { float player_pick_lock_skill = sender->GetSkill(EQEmu::skills::SkillPickLock); sender->CheckIncreaseSkill(EQEmu::skills::SkillPickLock, nullptr, 1); - Log(Logs::General, Logs::Skills, "Client has lockpicks: skill=%f", player_pick_lock_skill); + LogSkills("Client has lockpicks: skill=[{}]", player_pick_lock_skill); if (GetLockpick() <= player_pick_lock_skill) { if (!IsDoorOpen()) { @@ -679,7 +679,7 @@ int32 ZoneDatabase::GetDoorsDBCountPlusOne(const char *zone_name, int16 version) } bool ZoneDatabase::LoadDoors(int32 door_count, Door *into, const char *zone_name, int16 version) { - Log(Logs::General, Logs::Status, "Loading Doors from database..."); + LogInfo("Loading Doors from database"); std::string query = StringFormat( " SELECT " diff --git a/zone/effects.cpp b/zone/effects.cpp index 25923b35f..ba82cf3f0 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -435,7 +435,7 @@ bool Client::TrainDiscipline(uint32 itemid) { const EQEmu::ItemData *item = database.GetItem(itemid); if(item == nullptr) { Message(Chat::Red, "Unable to find the tome you turned in!"); - Log(Logs::General, Logs::Error, "Unable to find turned in tome id %lu\n", (unsigned long)itemid); + LogError("Unable to find turned in tome id [{}]\n", (unsigned long)itemid); return(false); } diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 759fe5931..4ccb0747e 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -145,7 +145,7 @@ void PerlembParser::ReloadQuests() { perl = nullptr; } - Log(Logs::General, Logs::Status, "Error re-initializing perlembed: %s", e.what()); + LogInfo("Error re-initializing perlembed: [{}]", e.what()); throw e.what(); } diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 5cb17ca50..d8e0c6a62 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -226,8 +226,7 @@ XS(XS__spawn); XS(XS__spawn) { dXSARGS; if (items != 6) - Perl_croak(aTHX_ - "Usage: quest::spawn(int npc_type_id, int grid_id, int int_unused, float x, float y, float z)"); + Perl_croak(aTHX_ "Usage: quest::spawn(int npc_type_id, int grid_id, int int_unused, float x, float y, float z)"); uint16 RETVAL; dXSTARG; @@ -249,8 +248,7 @@ XS(XS__spawn2); XS(XS__spawn2) { dXSARGS; if (items != 7) - Perl_croak(aTHX_ - "Usage: quest::spawn2(int npc_type_id, int grid_id, int int_unused, float x, float y, float z, float heading)"); + Perl_croak(aTHX_ "Usage: quest::spawn2(int npc_type_id, int grid_id, int int_unused, float x, float y, float z, float heading)"); uint16 RETVAL; dXSTARG; @@ -272,8 +270,7 @@ XS(XS__unique_spawn); XS(XS__unique_spawn) { dXSARGS; if (items != 6 && items != 7) - Perl_croak(aTHX_ - "Usage: quest::unique_spawn(int npc_type_id, int grid_id, int int_unused, float x, float y, float z, [float heading])"); + Perl_croak(aTHX_ "Usage: quest::unique_spawn(int npc_type_id, int grid_id, int int_unused, float x, float y, float z, [float heading])"); uint16 RETVAL; dXSTARG; @@ -1315,8 +1312,7 @@ XS(XS__targlobal); XS(XS__targlobal) { dXSARGS; if (items != 6) - Perl_croak(aTHX_ - "Usage: quest::targlobal(stirng key, string value, string duration, int npc_id, int chararacter_id, int zone_id)"); + Perl_croak(aTHX_ "Usage: quest::targlobal(stirng key, string value, string duration, int npc_id, int chararacter_id, int zone_id)"); char *key = (char *) SvPV_nolen(ST(0)); char *str_value = (char *) SvPV_nolen(ST(1)); @@ -1411,8 +1407,7 @@ XS(XS__moveto); XS(XS__moveto) { dXSARGS; if (items != 3 && items != 4 && items != 5) - Perl_croak(aTHX_ - "Usage: quest::moveto(float x, float y, float z, [float heading], [bool save_guard_location])"); + Perl_croak(aTHX_ "Usage: quest::moveto(float x, float y, float z, [float heading], [bool save_guard_location])"); float x = (float) SvNV(ST(0)); float y = (float) SvNV(ST(1)); @@ -1561,8 +1556,7 @@ XS(XS__set_proximity); XS(XS__set_proximity) { dXSARGS; if (items != 4 && items != 6 && items != 7) - Perl_croak(aTHX_ - "Usage: quest::set_proximity(float min_x, float max_x, float min_y, float max_y, [float min_z], [float max_z], [say])"); + Perl_croak(aTHX_ "Usage: quest::set_proximity(float min_x, float max_x, float min_y, float max_y, [float min_z], [float max_z], [say])"); float min_x = (float) SvNV(ST(0)); float max_x = (float) SvNV(ST(1)); @@ -1643,8 +1637,7 @@ XS(XS__spawn_condition); XS(XS__spawn_condition) { dXSARGS; if (items < 3 || items > 4) - Perl_croak(aTHX_ - "Usage: quest::spawn_condition(string zone_short, [int instance_id], uint16 condition_id, int16 value)"); + Perl_croak(aTHX_ "Usage: quest::spawn_condition(string zone_short, [int instance_id], uint16 condition_id, int16 value)"); if (items == 3) { char *zone_short = (char *) SvPV_nolen(ST(0)); @@ -1701,8 +1694,7 @@ XS(XS__toggle_spawn_event); XS(XS__toggle_spawn_event) { dXSARGS; if (items != 4) - Perl_croak(aTHX_ - "Usage: quest::toggle_spawn_event(uint32 event_id, [bool is_enabled = false], [bool is_strict = false], [bool reset_base = false])"); + Perl_croak(aTHX_ "Usage: quest::toggle_spawn_event(uint32 event_id, [bool is_enabled = false], [bool is_strict = false], [bool reset_base = false])"); uint32 event_id = (int) SvIV(ST(0)); bool is_enabled = ((int) SvIV(ST(1))) == 0 ? false : true; @@ -1763,8 +1755,7 @@ XS(XS__summonburiedplayercorpse); XS(XS__summonburiedplayercorpse) { dXSARGS; if (items != 5) - Perl_croak(aTHX_ - "Usage: quest::summonburiedplayercorpse(uint32 char_id, float dest_x, float dest_y, float dest_z, float dest_heading)"); + Perl_croak(aTHX_ "Usage: quest::summonburiedplayercorpse(uint32 char_id, float dest_x, float dest_y, float dest_z, float dest_heading)"); bool RETVAL; uint32 char_id = (int) SvIV(ST(0)); @@ -1781,8 +1772,7 @@ XS(XS__summonallplayercorpses); XS(XS__summonallplayercorpses) { dXSARGS; if (items != 5) - Perl_croak(aTHX_ - "Usage: quest::summonallplayercorpses(int char_id, float dest_x, float dest_y, float dest_z, float dest_heading)"); + Perl_croak(aTHX_ "Usage: quest::summonallplayercorpses(int char_id, float dest_x, float dest_y, float dest_z, float dest_heading)"); bool RETVAL; uint32 char_id = (int) SvIV(ST(0)); @@ -2038,8 +2028,7 @@ XS(XS__playerfeature); XS(XS__playerfeature) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ - "Usage: quest::playerfeature(string feature [race|gender|texture|helm|haircolor|beardcolor|eyecolor1|eyecolor2|hair|face|beard|heritage|tatoo|details|size], int setting)"); + Perl_croak(aTHX_ "Usage: quest::playerfeature(string feature [race|gender|texture|helm|haircolor|beardcolor|eyecolor1|eyecolor2|hair|face|beard|heritage|tatoo|details|size], int setting)"); char *str_value = (char *) SvPV_nolen(ST(0)); int int_value = (int) SvIV(ST(1)); @@ -2053,8 +2042,7 @@ XS(XS__npcfeature); XS(XS__npcfeature) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ - "Usage: quest::npcfeature(string feature [race|gender|texture|helm|haircolor|beardcolor|eyecolor1|eyecolor2|hair|face|beard|heritage|tatoo|details|size], int value)"); + Perl_croak(aTHX_ "Usage: quest::npcfeature(string feature [race|gender|texture|helm|haircolor|beardcolor|eyecolor1|eyecolor2|hair|face|beard|heritage|tatoo|details|size], int value)"); char *str_value = (char *) SvPV_nolen(ST(0)); int int_value = (int) SvIV(ST(1)); @@ -2281,8 +2269,7 @@ XS(XS__updatetaskactivity) { } quest_manager.updatetaskactivity(task_id, activity_id, count, ignore_quest_update); } else { - Perl_croak(aTHX_ - "Usage: quest::updatetaskactivity(int task_id, int activity_id, [int count], [bool ignore_quest_update = false])"); + Perl_croak(aTHX_ "Usage: quest::updatetaskactivity(int task_id, int activity_id, [int count], [bool ignore_quest_update = false])"); } XSRETURN_EMPTY; @@ -2570,8 +2557,7 @@ XS(XS__popup) { int duration = 0; if ((items < 2) || (items > 5)) - Perl_croak(aTHX_ - "Usage: quest::popup(string window_title, string message, int popup_id, int buttons, int duration)"); + Perl_croak(aTHX_ "Usage: quest::popup(string window_title, string message, int popup_id, int buttons, int duration)"); if (items >= 3) popup_id = (int) SvIV(ST(2)); @@ -2652,8 +2638,7 @@ XS(XS__CreateGroundObject); XS(XS__CreateGroundObject) { dXSARGS; if (items != 5 && items != 6) - Perl_croak(aTHX_ - "Usage: quest::creategroundobject(int item_id, float x, float y, float z, float heading, [uint32 decay_time-ms = 300000])"); + Perl_croak(aTHX_ "Usage: quest::creategroundobject(int item_id, float x, float y, float z, float heading, [uint32 decay_time-ms = 300000])"); int item_id = (int) SvIV(ST(0)); float x = (float) SvNV(ST(1)); @@ -2676,8 +2661,7 @@ XS(XS__CreateGroundObjectFromModel); XS(XS__CreateGroundObjectFromModel) { dXSARGS; if (items < 5 || items > 7) - Perl_croak(aTHX_ - "Usage: quest::creategroundobjectfrommodel(string model_name, float x, float y, float z, float heading, [int object_type], [uint32 decay_time-ms = 300000])"); + Perl_croak(aTHX_ "Usage: quest::creategroundobjectfrommodel(string model_name, float x, float y, float z, float heading, [int object_type], [uint32 decay_time-ms = 300000])"); char *modelname = (char *) SvPV_nolen(ST(0)); float x = (float) SvNV(ST(1)); @@ -2702,8 +2686,7 @@ XS(XS__CreateDoor); XS(XS__CreateDoor) { dXSARGS; if (items < 5 || items > 7) - Perl_croak(aTHX_ - "Usage: quest::createdoor(string model_name, float x, float y, float z, float heading, [int object_type = 58], [int size = 100])"); + Perl_croak(aTHX_ "Usage: quest::createdoor(string model_name, float x, float y, float z, float heading, [int object_type = 58], [int size = 100])"); char *modelname = (char *) SvPV_nolen(ST(0)); float x = (float) SvNV(ST(1)); @@ -3032,8 +3015,7 @@ XS(XS__MovePCInstance); XS(XS__MovePCInstance) { dXSARGS; if (items != 5 && items != 6) - Perl_croak(aTHX_ - "Usage: quest::MovePCInstance(int zone_id, int instance_id, float x, float y, float z, [float heading])"); + Perl_croak(aTHX_ "Usage: quest::MovePCInstance(int zone_id, int instance_id, float x, float y, float z, [float heading])"); int zone_id = (int) SvIV(ST(0)); int instanceid = (int) SvIV(ST(1)); @@ -3286,8 +3268,7 @@ XS(XS__wearchange); XS(XS__wearchange) { dXSARGS; if (items < 2) - Perl_croak(aTHX_ - "Usage: quest::wearchange(uint8 slot, uint16 texture_id, [uint32 hero_forge_model_id = 0], [uint32 elite_material_id = 0])"); + Perl_croak(aTHX_ "Usage: quest::wearchange(uint8 slot, uint16 texture_id, [uint32 hero_forge_model_id = 0], [uint32 elite_material_id = 0])"); uint8 slot = (int) SvUV(ST(0)); uint16 texture_id = (int) SvUV(ST(1)); @@ -3535,8 +3516,7 @@ XS(XS__crosszonesetentityvariablebynpctypeid) { dXSARGS; if (items != 3) - Perl_croak(aTHX_ - "Usage: quest::crosszonesetentityvariablebynpctypeid(int npc_type_id, string key, string value)"); + Perl_croak(aTHX_ "Usage: quest::crosszonesetentityvariablebynpctypeid(int npc_type_id, string key, string value)"); if (items == 3) { uint32 npc_type_id = (uint32) SvIV(ST(0)); @@ -3553,8 +3533,7 @@ XS(XS__crosszonesetentityvariablebyclientname) { dXSARGS; if (items != 3) - Perl_croak(aTHX_ - "Usage: quest::crosszonesetentityvariablebyclientname(string client_name, string key, string value)"); + Perl_croak(aTHX_ "Usage: quest::crosszonesetentityvariablebyclientname(string client_name, string key, string value)"); if (items == 3) { const char *client_name = (const char *) SvPV_nolen(ST(0)); @@ -3586,8 +3565,7 @@ XS(XS__worldwidemarquee); XS(XS__worldwidemarquee) { dXSARGS; if (items != 6) - Perl_croak(aTHX_ - "Usage: quest::worldwidemarquee(uint32 color_id, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, string message)"); + Perl_croak(aTHX_ "Usage: quest::worldwidemarquee(uint32 color_id, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, string message)"); if (items == 6) { uint32 color_id = (uint32) SvIV(ST(0)); @@ -3618,11 +3596,11 @@ XS(XS__debug) { return; if (debug_level == Logs::General) { - Log(Logs::General, Logs::QuestDebug, log_message); + Log(Logs::General, Logs::QuestDebug, log_message.c_str()); } else if (debug_level == Logs::Moderate) { - Log(Logs::Moderate, Logs::QuestDebug, log_message); + Log(Logs::Moderate, Logs::QuestDebug, log_message.c_str()); } else if (debug_level == Logs::Detail) { - Log(Logs::Detail, Logs::QuestDebug, log_message); + Log(Logs::Detail, Logs::QuestDebug, log_message.c_str()); } } XSRETURN_EMPTY; @@ -3747,7 +3725,7 @@ EXTERN_C XS(boot_quest) { file[255] = '\0'; if (items != 1) - Log(Logs::General, Logs::Error, "boot_quest does not take any arguments."); + LogError("boot_quest does not take any arguments"); char buf[128]; //shouldent have any function names longer than this. diff --git a/zone/embperl.cpp b/zone/embperl.cpp index bc20bd4ef..22c248fd7 100644 --- a/zone/embperl.cpp +++ b/zone/embperl.cpp @@ -140,12 +140,12 @@ void Embperl::DoInit() { catch(const char *err) { //remember... lasterr() is no good if we crap out here, in construction - Log(Logs::General, Logs::Quests, "perl error: %s", err); + LogQuests("perl error: [{}]", err); throw "failed to install eval_file hook"; } #ifdef EMBPERL_IO_CAPTURE - Log(Logs::General, Logs::Quests, "Tying perl output to eqemu logs"); + LogQuests("Tying perl output to eqemu logs"); //make a tieable class to capture IO and pass it into EQEMuLog eval_pv( "package EQEmuIO; " @@ -170,7 +170,7 @@ void Embperl::DoInit() { ,FALSE ); - Log(Logs::General, Logs::Quests, "Loading perlemb plugins."); + LogQuests("Loading perlemb plugins"); try { std::string perl_command; @@ -179,7 +179,7 @@ void Embperl::DoInit() { } catch(const char *err) { - Log(Logs::General, Logs::Quests, "Warning - %s: %s", Config->PluginPlFile.c_str(), err); + LogQuests("Warning - [{}]: [{}]", Config->PluginPlFile.c_str(), err); } try { @@ -197,7 +197,7 @@ void Embperl::DoInit() { } catch(const char *err) { - Log(Logs::General, Logs::Quests, "Perl warning: %s", err); + LogQuests("Perl warning: [{}]", err); } #endif //EMBPERL_PLUGIN in_use = false; diff --git a/zone/embxs.cpp b/zone/embxs.cpp index 3816850db..b9013335b 100644 --- a/zone/embxs.cpp +++ b/zone/embxs.cpp @@ -68,7 +68,7 @@ EXTERN_C XS(boot_qc) file[255] = '\0'; if(items != 1) - Log(Logs::General, Logs::Error, "boot_qc does not take any arguments."); + LogError("boot_qc does not take any arguments"); char buf[128]; //shouldent have any function names longer than this. diff --git a/zone/entity.cpp b/zone/entity.cpp index 2a1c76101..084e0fc93 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -76,12 +76,12 @@ Entity::~Entity() Client *Entity::CastToClient() { if (this == 0x00) { - Log(Logs::General, Logs::Error, "CastToClient error (nullptr)"); + LogError("CastToClient error (nullptr)"); return 0; } #ifdef _EQDEBUG if (!IsClient()) { - Log(Logs::General, Logs::Error, "CastToClient error (not client)"); + LogError("CastToClient error (not client)"); return 0; } #endif @@ -93,7 +93,7 @@ NPC *Entity::CastToNPC() { #ifdef _EQDEBUG if (!IsNPC()) { - Log(Logs::General, Logs::Error, "CastToNPC error (Not NPC)"); + LogError("CastToNPC error (Not NPC)"); return 0; } #endif @@ -377,7 +377,7 @@ void EntityList::CheckGroupList (const char *fname, const int fline) { if (*it == nullptr) { - Log(Logs::General, Logs::Error, "nullptr group, %s:%i", fname, fline); + LogError("nullptr group, [{}]:[{}]", fname, fline); } } } @@ -535,17 +535,17 @@ void EntityList::MobProcess() #ifdef _WINDOWS struct in_addr in; in.s_addr = mob->CastToClient()->GetIP(); - Log(Logs::General, Logs::Zone_Server, "Dropping client: Process=false, ip=%s port=%u", inet_ntoa(in), mob->CastToClient()->GetPort()); + LogInfo("Dropping client: Process=false, ip=[{}] port=[{}]", inet_ntoa(in), mob->CastToClient()->GetPort()); #endif zone->StartShutdownTimer(); Group *g = GetGroupByMob(mob); if(g) { - Log(Logs::General, Logs::Error, "About to delete a client still in a group."); + LogError("About to delete a client still in a group"); g->DelMember(mob); } Raid *r = entity_list.GetRaidByClient(mob->CastToClient()); if(r) { - Log(Logs::General, Logs::Error, "About to delete a client still in a raid."); + LogError("About to delete a client still in a raid"); r->MemberZoned(mob->CastToClient()); } entity_list.RemoveClient(id); @@ -592,8 +592,7 @@ void EntityList::AddGroup(Group *group) uint32 gid = worldserver.NextGroupID(); if (gid == 0) { - Log(Logs::General, Logs::Error, - "Unable to get new group ID from world server. group is going to be broken."); + LogError("Unable to get new group ID from world server. group is going to be broken"); return; } @@ -621,8 +620,7 @@ void EntityList::AddRaid(Raid *raid) uint32 gid = worldserver.NextGroupID(); if (gid == 0) { - Log(Logs::General, Logs::Error, - "Unable to get new group ID from world server. group is going to be broken."); + LogError("Unable to get new group ID from world server. group is going to be broken"); return; } @@ -817,7 +815,7 @@ void EntityList::CheckSpawnQueue() auto it = npc_list.find(ns->spawn.spawnId); if (it == npc_list.end()) { // We must of despawned, hope that's the reason! - Log(Logs::General, Logs::Error, "Error in EntityList::CheckSpawnQueue: Unable to find NPC for spawnId '%u'", ns->spawn.spawnId); + LogError("Error in EntityList::CheckSpawnQueue: Unable to find NPC for spawnId [{}]", ns->spawn.spawnId); } else { NPC *pnpc = it->second; @@ -2877,7 +2875,7 @@ char *EntityList::MakeNameUnique(char *name) return name; } } - Log(Logs::General, Logs::Error, "Fatal error in EntityList::MakeNameUnique: Unable to find unique name for '%s'", name); + LogError("Fatal error in EntityList::MakeNameUnique: Unable to find unique name for [{}]", name); char tmp[64] = "!"; strn0cpy(&tmp[1], name, sizeof(tmp) - 1); strcpy(name, tmp); diff --git a/zone/exp.cpp b/zone/exp.cpp index 23593f644..e70e8e936 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -46,7 +46,7 @@ static uint32 ScaleAAXPBasedOnCurrentAATotal(int earnedAA, uint32 add_aaxp) // Are we within the scaling window? if (earnedAA >= aaLimit || earnedAA < aaMinimum) { - Log(Logs::Detail, Logs::None, "Not within AA scaling window."); + LogDebug("Not within AA scaling window"); // At or past the limit. We're done. return add_aaxp; @@ -524,7 +524,7 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { } void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { - Log(Logs::Detail, Logs::None, "Attempting to Set Exp for %s (XP: %u, AAXP: %u, Rez: %s)", this->GetCleanName(), set_exp, set_aaxp, isrezzexp ? "true" : "false"); + LogDebug("Attempting to Set Exp for [{}] (XP: [{}], AAXP: [{}], Rez: [{}])", this->GetCleanName(), set_exp, set_aaxp, isrezzexp ? "true" : "false"); auto max_AAXP = GetRequiredAAExperience(); if (max_AAXP == 0 || GetEXPForLevel(GetLevel()) == 0xFFFFFFFF) { @@ -643,7 +643,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { //figure out how many AA points we get from the exp were setting m_pp.aapoints = set_aaxp / max_AAXP; - Log(Logs::Detail, Logs::None, "Calculating additional AA Points from AAXP for %s: %u / %u = %.1f points", this->GetCleanName(), set_aaxp, max_AAXP, (float)set_aaxp / (float)max_AAXP); + LogDebug("Calculating additional AA Points from AAXP for [{}]: [{}] / [{}] = [{}] points", this->GetCleanName(), set_aaxp, max_AAXP, (float)set_aaxp / (float)max_AAXP); //get remainder exp points, set in PP below set_aaxp = set_aaxp - (max_AAXP * m_pp.aapoints); @@ -765,7 +765,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { void Client::SetLevel(uint8 set_level, bool command) { if (GetEXPForLevel(set_level) == 0xFFFFFFFF) { - Log(Logs::General, Logs::Error, "Client::SetLevel() GetEXPForLevel(%i) = 0xFFFFFFFF", set_level); + LogError("Client::SetLevel() GetEXPForLevel([{}]) = 0xFFFFFFFF", set_level); return; } @@ -823,7 +823,7 @@ void Client::SetLevel(uint8 set_level, bool command) safe_delete(outapp); this->SendAppearancePacket(AT_WhoLevel, set_level); // who level change - Log(Logs::General, Logs::Normal, "Setting Level for %s to %i", GetName(), set_level); + LogInfo("Setting Level for [{}] to [{}]", GetName(), set_level); CalcBonuses(); @@ -1131,15 +1131,16 @@ uint32 Client::GetCharMaxLevelFromQGlobal() { return 0; } -uint32 Client::GetCharMaxLevelFromBucket() { - uint32 char_id = this->CharacterID(); - std::string query = StringFormat("SELECT value FROM data_buckets WHERE `key` = '%i-CharMaxLevel'", char_id); - auto results = database.QueryDatabase(query); +uint32 Client::GetCharMaxLevelFromBucket() +{ + uint32 char_id = this->CharacterID(); + std::string query = StringFormat("SELECT value FROM data_buckets WHERE `key` = '%i-CharMaxLevel'", char_id); + auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Data bucket for CharMaxLevel for char ID %i failed.", char_id); - return 0; - } - + LogError("Data bucket for CharMaxLevel for char ID [{}] failed", char_id); + return 0; + } + if (results.RowCount() > 0) { auto row = results.begin(); return atoi(row[0]); diff --git a/zone/forage.cpp b/zone/forage.cpp index 12d2f61a1..3c69dce19 100644 --- a/zone/forage.cpp +++ b/zone/forage.cpp @@ -62,17 +62,17 @@ uint32 ZoneDatabase::GetZoneForage(uint32 ZoneID, uint8 skill) { return 0; } - uint8 index = 0; - for (auto row = results.begin(); row != results.end(); ++row, ++index) { - if (index >= FORAGE_ITEM_LIMIT) - break; - - item[index] = atoi(row[0]); - chance[index] = atoi(row[1]) + chancepool; - Log(Logs::General, Logs::Error, "Possible Forage: %d with a %d chance", item[index], chance[index]); - chancepool = chance[index]; - } + uint8 index = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++index) { + if (index >= FORAGE_ITEM_LIMIT) { + break; + } + item[index] = atoi(row[0]); + chance[index] = atoi(row[1]) + chancepool; + LogError("Possible Forage: [{}] with a [{}] chance", item[index], chance[index]); + chancepool = chance[index]; + } if(chancepool == 0 || index < 1) return 0; @@ -413,7 +413,7 @@ void Client::ForageItem(bool guarantee) { const EQEmu::ItemData* food_item = database.GetItem(foragedfood); if(!food_item) { - Log(Logs::General, Logs::Error, "nullptr returned from database.GetItem in ClientForageItem"); + LogError("nullptr returned from database.GetItem in ClientForageItem"); return; } diff --git a/zone/groups.cpp b/zone/groups.cpp index cef37fbda..e29deca43 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -835,7 +835,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { caster->SpellOnTarget(spell_id, members[z]->GetPet()); #endif } else - Log(Logs::Detail, Logs::Spells, "Group spell: %s is out of range %f at distance %f from %s", members[z]->GetName(), range, distance, caster->GetName()); + LogSpells("Group spell: [{}] is out of range [{}] at distance [{}] from [{}]", members[z]->GetName(), range, distance, caster->GetName()); } } @@ -874,7 +874,7 @@ void Group::GroupBardPulse(Mob* caster, uint16 spell_id) { members[z]->GetPet()->BardPulse(spell_id, caster); #endif } else - Log(Logs::Detail, Logs::Spells, "Group bard pulse: %s is out of range %f at distance %f from %s", members[z]->GetName(), range, distance, caster->GetName()); + LogSpells("Group bard pulse: [{}] is out of range [{}] at distance [{}] from [{}]", members[z]->GetName(), range, distance, caster->GetName()); } } } @@ -1177,10 +1177,15 @@ bool Group::LearnMembers() { if (!results.Success()) return false; - if (results.RowCount() == 0) { - Log(Logs::General, Logs::Error, "Error getting group members for group %lu: %s", (unsigned long)GetID(), results.ErrorMessage().c_str()); - return false; - } + if (results.RowCount() == 0) { + LogError( + "Error getting group members for group [{}]: [{}]", + (unsigned long) GetID(), + results.ErrorMessage().c_str() + ); + + return false; + } int memberIndex = 0; for(auto row = results.begin(); row != results.end(); ++row) { @@ -1207,7 +1212,7 @@ void Group::VerifyGroup() { for (i = 0; i < MAX_GROUP_MEMBERS; i++) { if (membername[i][0] == '\0') { #if EQDEBUG >= 7 - Log(Logs::General, Logs::None, "Group %lu: Verify %d: Empty.\n", (unsigned long)GetID(), i); + LogDebug("Group [{}]: Verify [{}]: Empty.\n", (unsigned long)GetID(), i); #endif members[i] = nullptr; continue; @@ -1216,7 +1221,7 @@ void Group::VerifyGroup() { Mob *them = entity_list.GetMob(membername[i]); if(them == nullptr && members[i] != nullptr) { //they aren't in zone #if EQDEBUG >= 6 - Log(Logs::General, Logs::None, "Member of group %lu named '%s' has disappeared!!", (unsigned long)GetID(), membername[i]); + LogDebug("Member of group [{}] named [{}] has disappeared!!", (unsigned long)GetID(), membername[i]); #endif membername[i][0] = '\0'; members[i] = nullptr; @@ -1225,13 +1230,13 @@ void Group::VerifyGroup() { if(them != nullptr && members[i] != them) { //our pointer is out of date... not so good. #if EQDEBUG >= 5 - Log(Logs::General, Logs::None, "Member of group %lu named '%s' had an out of date pointer!!", (unsigned long)GetID(), membername[i]); + LogDebug("Member of group [{}] named [{}] had an out of date pointer!!", (unsigned long)GetID(), membername[i]); #endif members[i] = them; continue; } #if EQDEBUG >= 8 - Log(Logs::General, Logs::None, "Member of group %lu named '%s' is valid.", (unsigned long)GetID(), membername[i]); + LogDebug("Member of group [{}] named [{}] is valid", (unsigned long)GetID(), membername[i]); #endif } } @@ -1566,7 +1571,7 @@ void Group::DelegateMainTank(const char *NewMainTankName, uint8 toggle) MainTankName.c_str(), GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to set group main tank: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to set group main tank: [{}]\n", results.ErrorMessage().c_str()); } } @@ -1612,7 +1617,7 @@ void Group::DelegateMainAssist(const char *NewMainAssistName, uint8 toggle) MainAssistName.c_str(), GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to set group main assist: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to set group main assist: [{}]\n", results.ErrorMessage().c_str()); } } @@ -1659,7 +1664,7 @@ void Group::DelegatePuller(const char *NewPullerName, uint8 toggle) PullerName.c_str(), GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to set group main puller: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to set group main puller: [{}]\n", results.ErrorMessage().c_str()); } @@ -1810,7 +1815,7 @@ void Group::UnDelegateMainTank(const char *OldMainTankName, uint8 toggle) std::string query = StringFormat("UPDATE group_leaders SET maintank = '' WHERE gid = %i LIMIT 1", GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to clear group main tank: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to clear group main tank: [{}]\n", results.ErrorMessage().c_str()); if(!toggle) { for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { @@ -1859,7 +1864,7 @@ void Group::UnDelegateMainAssist(const char *OldMainAssistName, uint8 toggle) std::string query = StringFormat("UPDATE group_leaders SET assist = '' WHERE gid = %i LIMIT 1", GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to clear group main assist: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to clear group main assist: [{}]\n", results.ErrorMessage().c_str()); if(!toggle) { @@ -1887,7 +1892,7 @@ void Group::UnDelegatePuller(const char *OldPullerName, uint8 toggle) std::string query = StringFormat("UPDATE group_leaders SET puller = '' WHERE gid = %i LIMIT 1", GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to clear group main puller: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to clear group main puller: [{}]\n", results.ErrorMessage().c_str()); if(!toggle) { for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { @@ -1970,7 +1975,7 @@ void Group::SetGroupMentor(int percent, char *name) mentoree_name.c_str(), mentor_percent, GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to set group mentor: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to set group mentor: [{}]\n", results.ErrorMessage().c_str()); } void Group::ClearGroupMentor() @@ -1981,7 +1986,7 @@ void Group::ClearGroupMentor() std::string query = StringFormat("UPDATE group_leaders SET mentoree = '', mentor_percent = 0 WHERE gid = %i LIMIT 1", GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to clear group mentor: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to clear group mentor: [{}]\n", results.ErrorMessage().c_str()); } void Group::NotifyAssistTarget(Client *c) @@ -2051,7 +2056,7 @@ void Group::DelegateMarkNPC(const char *NewNPCMarkerName) NewNPCMarkerName, GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to set group mark npc: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to set group mark npc: [{}]\n", results.ErrorMessage().c_str()); } void Group::NotifyMarkNPC(Client *c) @@ -2132,7 +2137,7 @@ void Group::UnDelegateMarkNPC(const char *OldNPCMarkerName) std::string query = StringFormat("UPDATE group_leaders SET marknpc = '' WHERE gid = %i LIMIT 1", GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to clear group marknpc: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to clear group marknpc: [{}]\n", results.ErrorMessage().c_str()); } @@ -2149,7 +2154,7 @@ void Group::SaveGroupLeaderAA() safe_delete_array(queryBuffer); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to store LeadershipAA: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to store LeadershipAA: [{}]\n", results.ErrorMessage().c_str()); } diff --git a/zone/guild.cpp b/zone/guild.cpp index bb39c7fc0..166e6edfb 100644 --- a/zone/guild.cpp +++ b/zone/guild.cpp @@ -56,7 +56,7 @@ void Client::SendGuildMOTD(bool GetGuildMOTDReply) { } - Log(Logs::Detail, Logs::Guilds, "Sending OP_GuildMOTD of length %d", outapp->size); + LogGuilds("Sending OP_GuildMOTD of length [{}]", outapp->size); FastQueuePacket(&outapp); } @@ -147,10 +147,10 @@ void Client::SendGuildSpawnAppearance() { if (!IsInAGuild()) { // clear guildtag SendAppearancePacket(AT_GuildID, GUILD_NONE); - Log(Logs::Detail, Logs::Guilds, "Sending spawn appearance for no guild tag."); + LogGuilds("Sending spawn appearance for no guild tag"); } else { uint8 rank = guild_mgr.GetDisplayedRank(GuildID(), GuildRank(), CharacterID()); - Log(Logs::Detail, Logs::Guilds, "Sending spawn appearance for guild %d at rank %d", GuildID(), rank); + LogGuilds("Sending spawn appearance for guild [{}] at rank [{}]", GuildID(), rank); SendAppearancePacket(AT_GuildID, GuildID()); if (ClientVersion() >= EQEmu::versions::ClientVersion::RoF) { @@ -174,11 +174,11 @@ void Client::SendGuildList() { //ask the guild manager to build us a nice guild list packet outapp->pBuffer = guild_mgr.MakeGuildList(/*GetName()*/"", outapp->size); if(outapp->pBuffer == nullptr) { - Log(Logs::Detail, Logs::Guilds, "Unable to make guild list!"); + LogGuilds("Unable to make guild list!"); return; } - Log(Logs::Detail, Logs::Guilds, "Sending OP_ZoneGuildList of length %d", outapp->size); + LogGuilds("Sending OP_ZoneGuildList of length [{}]", outapp->size); FastQueuePacket(&outapp); } @@ -195,7 +195,7 @@ void Client::SendGuildMembers() { outapp->pBuffer = data; data = nullptr; - Log(Logs::Detail, Logs::Guilds, "Sending OP_GuildMemberList of length %d", outapp->size); + LogGuilds("Sending OP_GuildMemberList of length [{}]", outapp->size); FastQueuePacket(&outapp); @@ -227,7 +227,7 @@ void Client::RefreshGuildInfo() CharGuildInfo info; if(!guild_mgr.GetCharInfo(CharacterID(), info)) { - Log(Logs::Detail, Logs::Guilds, "Unable to obtain guild char info for %s (%d)", GetName(), CharacterID()); + LogGuilds("Unable to obtain guild char info for [{}] ([{}])", GetName(), CharacterID()); return; } @@ -341,7 +341,7 @@ void Client::SendGuildJoin(GuildJoin_Struct* gj){ outgj->rank = gj->rank; outgj->zoneid = gj->zoneid; - Log(Logs::Detail, Logs::Guilds, "Sending OP_GuildManageAdd for join of length %d", outapp->size); + LogGuilds("Sending OP_GuildManageAdd for join of length [{}]", outapp->size); FastQueuePacket(&outapp); diff --git a/zone/guild_mgr.cpp b/zone/guild_mgr.cpp index eacfcb474..200f67d90 100644 --- a/zone/guild_mgr.cpp +++ b/zone/guild_mgr.cpp @@ -32,7 +32,7 @@ extern WorldServer worldserver; extern volatile bool is_zone_loaded; void ZoneGuildManager::SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation) { - Log(Logs::Detail, Logs::Guilds, "Sending guild refresh for %d to world, changes: name=%d, motd=%d, rank=d, relation=%d", guild_id, name, motd, rank, relation); + LogGuilds("Sending guild refresh for [{}] to world, changes: name=[{}], motd=[{}], rank=d, relation=[{}]", guild_id, name, motd, rank, relation); auto pack = new ServerPacket(ServerOP_RefreshGuild, sizeof(ServerGuildRefresh_Struct)); ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer; s->guild_id = guild_id; @@ -46,7 +46,7 @@ void ZoneGuildManager::SendGuildRefresh(uint32 guild_id, bool name, bool motd, b void ZoneGuildManager::SendCharRefresh(uint32 old_guild_id, uint32 guild_id, uint32 charid) { if(guild_id == 0) { - Log(Logs::Detail, Logs::Guilds, "Guild lookup for char %d when sending char refresh.", charid); + LogGuilds("Guild lookup for char [{}] when sending char refresh", charid); CharGuildInfo gci; if(!GetCharInfo(charid, gci)) { @@ -56,7 +56,7 @@ void ZoneGuildManager::SendCharRefresh(uint32 old_guild_id, uint32 guild_id, uin } } - Log(Logs::Detail, Logs::Guilds, "Sending char refresh for %d from guild %d to world", charid, guild_id); + LogGuilds("Sending char refresh for [{}] from guild [{}] to world", charid, guild_id); auto pack = new ServerPacket(ServerOP_GuildCharRefresh, sizeof(ServerGuildCharRefresh_Struct)); ServerGuildCharRefresh_Struct *s = (ServerGuildCharRefresh_Struct *) pack->pBuffer; @@ -89,7 +89,7 @@ void ZoneGuildManager::SendRankUpdate(uint32 CharID) } void ZoneGuildManager::SendGuildDelete(uint32 guild_id) { - Log(Logs::Detail, Logs::Guilds, "Sending guild delete for guild %d to world", guild_id); + LogGuilds("Sending guild delete for guild [{}] to world", guild_id); auto pack = new ServerPacket(ServerOP_DeleteGuild, sizeof(ServerGuildID_Struct)); ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer; s->guild_id = guild_id; @@ -261,12 +261,12 @@ void ZoneGuildManager::ProcessWorldPacket(ServerPacket *pack) { switch(pack->opcode) { case ServerOP_RefreshGuild: { if(pack->size != sizeof(ServerGuildRefresh_Struct)) { - Log(Logs::General, Logs::Error, "Received ServerOP_RefreshGuild of incorrect size %d, expected %d", pack->size, sizeof(ServerGuildRefresh_Struct)); + LogError("Received ServerOP_RefreshGuild of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildRefresh_Struct)); return; } ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer; - Log(Logs::Detail, Logs::Guilds, "Received guild refresh from world for %d, changes: name=%d, motd=%d, rank=%d, relation=%d", s->guild_id, s->name_change, s->motd_change, s->rank_change, s->relation_change); + LogGuilds("Received guild refresh from world for [{}], changes: name=[{}], motd=[{}], rank=[{}], relation=[{}]", s->guild_id, s->name_change, s->motd_change, s->rank_change, s->relation_change); //reload all the guild details from the database. RefreshGuild(s->guild_id); @@ -295,12 +295,12 @@ void ZoneGuildManager::ProcessWorldPacket(ServerPacket *pack) { case ServerOP_GuildCharRefresh: { if(pack->size != sizeof(ServerGuildCharRefresh_Struct)) { - Log(Logs::General, Logs::Error, "Received ServerOP_RefreshGuild of incorrect size %d, expected %d", pack->size, sizeof(ServerGuildCharRefresh_Struct)); + LogError("Received ServerOP_RefreshGuild of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildCharRefresh_Struct)); return; } ServerGuildCharRefresh_Struct *s = (ServerGuildCharRefresh_Struct *) pack->pBuffer; - Log(Logs::Detail, Logs::Guilds, "Received guild member refresh from world for char %d from guild %d", s->char_id, s->guild_id); + LogGuilds("Received guild member refresh from world for char [{}] from guild [{}]", s->char_id, s->guild_id); Client *c = entity_list.GetClientByCharID(s->char_id); @@ -338,7 +338,7 @@ void ZoneGuildManager::ProcessWorldPacket(ServerPacket *pack) { { if(pack->size != sizeof(ServerGuildRankUpdate_Struct)) { - Log(Logs::General, Logs::Error, "Received ServerOP_RankUpdate of incorrect size %d, expected %d", + LogError("Received ServerOP_RankUpdate of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildRankUpdate_Struct)); return; @@ -364,12 +364,12 @@ void ZoneGuildManager::ProcessWorldPacket(ServerPacket *pack) { case ServerOP_DeleteGuild: { if(pack->size != sizeof(ServerGuildID_Struct)) { - Log(Logs::General, Logs::Error, "Received ServerOP_DeleteGuild of incorrect size %d, expected %d", pack->size, sizeof(ServerGuildID_Struct)); + LogError("Received ServerOP_DeleteGuild of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildID_Struct)); return; } ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer; - Log(Logs::Detail, Logs::Guilds, "Received guild delete from world for guild %d", s->guild_id); + LogGuilds("Received guild delete from world for guild [{}]", s->guild_id); //clear all the guild tags. entity_list.RefreshAllGuildInfo(s->guild_id); @@ -417,23 +417,22 @@ void ZoneGuildManager::ProcessWorldPacket(ServerPacket *pack) { if (!c || !c->IsInAGuild()) { - Log(Logs::Detail, Logs::Guilds,"Invalid Client or not in guild. ID=%i", FromID); + LogGuilds("Invalid Client or not in guild. ID=[{}]", FromID); break; } - Log(Logs::Detail, Logs::Guilds,"Processing ServerOP_OnlineGuildMembersResponse"); + LogGuilds("Processing ServerOP_OnlineGuildMembersResponse"); auto outapp = new EQApplicationPacket(OP_GuildMemberUpdate, sizeof(GuildMemberUpdate_Struct)); GuildMemberUpdate_Struct *gmus = (GuildMemberUpdate_Struct*)outapp->pBuffer; char Name[64]; gmus->LastSeen = time(nullptr); gmus->InstanceID = 0; gmus->GuildID = c->GuildID(); - for (int i=0;iMemberName, Name, sizeof(gmus->MemberName)); gmus->ZoneID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - Log(Logs::Detail, Logs::Guilds,"Sending OP_GuildMemberUpdate to %i. Name=%s ZoneID=%i",FromID,Name,gmus->ZoneID); + LogGuilds("Sending OP_GuildMemberUpdate to [{}]. Name=[{}] ZoneID=[{}]", FromID, Name, gmus->ZoneID); c->QueuePacket(outapp); } safe_delete(outapp); @@ -683,7 +682,7 @@ void GuildBankManager::SendGuildBank(Client *c) if(Iterator == Banks.end()) { - Log(Logs::General, Logs::Error, "Unable to find guild bank for guild ID %i", c->GuildID()); + LogError("Unable to find guild bank for guild ID [{}]", c->GuildID()); return; } @@ -856,7 +855,7 @@ bool GuildBankManager::AddItem(uint32 GuildID, uint8 Area, uint32 ItemID, int32 if(Iterator == Banks.end()) { - Log(Logs::General, Logs::Error, "Unable to find guild bank for guild ID %i", GuildID); + LogError("Unable to find guild bank for guild ID [{}]", GuildID); return false; } @@ -902,7 +901,7 @@ bool GuildBankManager::AddItem(uint32 GuildID, uint8 Area, uint32 ItemID, int32 if(Slot < 0) { - Log(Logs::General, Logs::Error, "No space to add item to the guild bank."); + LogError("No space to add item to the guild bank"); return false; } diff --git a/zone/heal_rotation.cpp b/zone/heal_rotation.cpp index 489d1ab45..3de331623 100644 --- a/zone/heal_rotation.cpp +++ b/zone/heal_rotation.cpp @@ -170,7 +170,7 @@ bool HealRotation::ClearMemberPool() m_active_heal_target = false; if (!ClearTargetPool()) - Log(Logs::General, Logs::Error, "HealRotation::ClearTargetPool() failed to clear m_target_pool (size: %u)", m_target_pool.size()); + LogError("HealRotation::ClearTargetPool() failed to clear m_target_pool (size: [{}])", m_target_pool.size()); auto clear_list = const_cast&>(m_member_pool); for (auto member_iter : clear_list) @@ -871,42 +871,42 @@ void HealRotation::bias_targets() m_casting_target_poke = true; #if (EQDEBUG >= 12) - Log(Logs::General, Logs::Error, "HealRotation::bias_targets() - *** Post-processing state ***"); - Log(Logs::General, Logs::Error, "HealRotation Settings:"); - Log(Logs::General, Logs::Error, "HealRotation::m_interval_ms = %u", m_interval_ms); - Log(Logs::General, Logs::Error, "HealRotation::m_next_cast_time_ms = %u (current_time: %u, time_diff: %i)", m_next_cast_time_ms, Timer::GetCurrentTime(), ((int32)Timer::GetCurrentTime() - (int32)m_next_cast_time_ms)); - Log(Logs::General, Logs::Error, "HealRotation::m_next_poke_time_ms = %u (current_time: %u, time_diff: %i)", m_next_poke_time_ms, Timer::GetCurrentTime(), ((int32)Timer::GetCurrentTime() - (int32)m_next_poke_time_ms)); - Log(Logs::General, Logs::Error, "HealRotation::m_fast_heals = %s", ((m_fast_heals) ? ("true") : ("false"))); - Log(Logs::General, Logs::Error, "HealRotation::m_adaptive_targeting = %s", ((m_adaptive_targeting) ? ("true") : ("false"))); - Log(Logs::General, Logs::Error, "HealRotation::m_casting_override = %s", ((m_casting_override) ? ("true") : ("false"))); - Log(Logs::General, Logs::Error, "HealRotation::m_casting_target_poke = %s", ((m_casting_target_poke) ? ("true") : ("false"))); - Log(Logs::General, Logs::Error, "HealRotation::m_active_heal_target = %s", ((m_active_heal_target) ? ("true") : ("false"))); - Log(Logs::General, Logs::Error, "HealRotation::m_is_active = %s", ((m_is_active) ? ("true") : ("false"))); - Log(Logs::General, Logs::Error, "HealRotation::m_member_list.size() = %i", m_member_pool.size()); - Log(Logs::General, Logs::Error, "HealRotation::m_cycle_list.size() = %i", m_cycle_pool.size()); - Log(Logs::General, Logs::Error, "HealRotation::m_target_list.size() = %i", m_target_pool.size()); - if (m_member_pool.size()) { Log(Logs::General, Logs::Error, "(std::shared_ptr::use_count() = %i", m_member_pool.front()->MemberOfHealRotation()->use_count()); } - else { Log(Logs::General, Logs::Error, "(std::shared_ptr::use_count() = unknown (0)"); } - Log(Logs::General, Logs::Error, "HealRotation Members:"); + LogError("HealRotation::bias_targets() - *** Post-processing state ***"); + LogError("HealRotation Settings:"); + LogError("HealRotation::m_interval_ms = [{}]", m_interval_ms); + LogError("HealRotation::m_next_cast_time_ms = [{}] (current_time: [{}], time_diff: [{}])", m_next_cast_time_ms, Timer::GetCurrentTime(), ((int32)Timer::GetCurrentTime() - (int32)m_next_cast_time_ms)); + LogError("HealRotation::m_next_poke_time_ms = [{}] (current_time: [{}], time_diff: [{}])", m_next_poke_time_ms, Timer::GetCurrentTime(), ((int32)Timer::GetCurrentTime() - (int32)m_next_poke_time_ms)); + LogError("HealRotation::m_fast_heals = [{}]", ((m_fast_heals) ? ("true") : ("false"))); + LogError("HealRotation::m_adaptive_targeting = [{}]", ((m_adaptive_targeting) ? ("true") : ("false"))); + LogError("HealRotation::m_casting_override = [{}]", ((m_casting_override) ? ("true") : ("false"))); + LogError("HealRotation::m_casting_target_poke = [{}]", ((m_casting_target_poke) ? ("true") : ("false"))); + LogError("HealRotation::m_active_heal_target = [{}]", ((m_active_heal_target) ? ("true") : ("false"))); + LogError("HealRotation::m_is_active = [{}]", ((m_is_active) ? ("true") : ("false"))); + LogError("HealRotation::m_member_list.size() = [{}]", m_member_pool.size()); + LogError("HealRotation::m_cycle_list.size() = [{}]", m_cycle_pool.size()); + LogError("HealRotation::m_target_list.size() = [{}]", m_target_pool.size()); + if (m_member_pool.size()) { LogError("(std::shared_ptr::use_count() = [{}]", m_member_pool.front()->MemberOfHealRotation()->use_count()); } + else { LogError("(std::shared_ptr::use_count() = unknown (0)"); } + LogError("HealRotation Members:"); int member_index = 0; for (auto mlist_iter : m_member_pool) { if (!mlist_iter) { continue; } - Log(Logs::General, Logs::Error, "(%i) %s (hrcast: %c)", (++member_index), mlist_iter->GetCleanName(), ((mlist_iter->AmICastingForHealRotation())?('T'):('F'))); + LogError("([{}]) [{}] (hrcast: [{}])", (++member_index), mlist_iter->GetCleanName(), ((mlist_iter->AmICastingForHealRotation())?('T'):('F'))); } - if (!member_index) { Log(Logs::General, Logs::Error, "(0) None"); } - Log(Logs::General, Logs::Error, "HealRotation Cycle:"); + if (!member_index) { LogError("(0) None"); } + LogError("HealRotation Cycle:"); int cycle_index = 0; for (auto clist_iter : m_cycle_pool) { if (!clist_iter) { continue; } - Log(Logs::General, Logs::Error, "(%i) %s", (++cycle_index), clist_iter->GetCleanName()); + LogError("([{}]) [{}]", (++cycle_index), clist_iter->GetCleanName()); } - if (!cycle_index) { Log(Logs::General, Logs::Error, "(0) None"); } - Log(Logs::General, Logs::Error, "HealRotation Targets: (sort type: %u)", sort_type); + if (!cycle_index) { LogError("(0) None"); } + LogError("HealRotation Targets: (sort type: [{}])", sort_type); int target_index = 0; for (auto tlist_iter : m_target_pool) { if (!tlist_iter) { continue; } - Log(Logs::General, Logs::Error, "(%i) %s (hp: %3.1f%%, at: %u, dontheal: %c, crit(base): %c(%c), safe(base): %c(%c), hcnt(ext): %u(%u), hfreq(ext): %f(%f))", + LogError("([{}]) [{}] (hp: [{}], at: [{}], dontheal: [{}], crit(base): [{}]([{}]), safe(base): [{}]([{}]), hcnt(ext): [{}]([{}]), hfreq(ext): [{}]([{}]))", (++target_index), tlist_iter->GetCleanName(), tlist_iter->GetHPRatio(), ClassArmorType(tlist_iter->GetClass()), @@ -920,7 +920,7 @@ void HealRotation::bias_targets() tlist_iter->HealRotationHealFrequency(), tlist_iter->HealRotationExtendedHealFrequency()); } - if (!target_index) { Log(Logs::General, Logs::Error, "(0) None (hp: 0.0\%, at: 0, dontheal: F, crit(base): F(F), safe(base): F(F), hcnt(ext): 0(0), hfreq(ext): 0.0(0.0))"); } + if (!target_index) { LogError("(0) None (hp: 0.0\%, at: 0, dontheal: F, crit(base): F(F), safe(base): F(F), hcnt(ext): 0(0), hfreq(ext): 0.0(0.0))"); } #endif } diff --git a/zone/horse.cpp b/zone/horse.cpp index 3871b355d..484b67149 100644 --- a/zone/horse.cpp +++ b/zone/horse.cpp @@ -79,8 +79,8 @@ const NPCType *Horse::BuildHorseType(uint16 spell_id) { } if (results.RowCount() != 1) { - Log(Logs::General, Logs::Error, "No Database entry for mount: %s, check the horses table", fileName); - return nullptr; + LogError("No Database entry for mount: [{}], check the horses table", fileName); + return nullptr; } auto row = results.begin(); @@ -122,7 +122,7 @@ void Client::SummonHorse(uint16 spell_id) { return; } if(!Horse::IsHorseSpell(spell_id)) { - Log(Logs::General, Logs::Error, "%s tried to summon an unknown horse, spell id %d", GetName(), spell_id); + LogError("[{}] tried to summon an unknown horse, spell id [{}]", GetName(), spell_id); return; } diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 500663491..3ee86df47 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -186,7 +186,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, // make sure the item exists if(item == nullptr) { Message(Chat::Red, "Item %u does not exist.", item_id); - Log(Logs::Detail, Logs::Inventory, "Player %s on account %s attempted to create an item with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, Aug6: %u)\n", + LogInventory("Player [{}] on account [{}] attempted to create an item with an invalid id.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", GetName(), account_name, item_id, aug1, aug2, aug3, aug4, aug5, aug6); return false; @@ -201,7 +201,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, // check to make sure we are augmenting an augmentable item else if (((!item->IsClassCommon()) || (item->AugType > 0)) && (aug1 | aug2 | aug3 | aug4 | aug5 | aug6)) { Message(Chat::Red, "You can not augment an augment or a non-common class item."); - Log(Logs::Detail, Logs::Inventory, "Player %s on account %s attempted to augment an augment or a non-common class item.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, Aug5: %u)\n", + LogInventory("Player [{}] on account [{}] attempted to augment an augment or a non-common class item.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug5: [{}])\n", GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5, aug6); return false; @@ -215,7 +215,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, /* else if(item->MinStatus && ((this->Admin() < item->MinStatus) || (this->Admin() < RuleI(GM, MinStatusToSummonItem)))) { Message(Chat::Red, "You are not a GM or do not have the status to summon this item."); - Log(Logs::Detail, Logs::Inventory, "Player %s on account %s attempted to create a GM-only item with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, Aug6: %u, MinStatus: %u)\n", + LogInventory("Player [{}] on account [{}] attempted to create a GM-only item with a status of [{}].\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}], MinStatus: [{}])\n", GetName(), account_name, this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5, aug6, item->MinStatus); return false; @@ -238,7 +238,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, if(augtest == nullptr) { if(augments[iter]) { Message(Chat::Red, "Augment %u (Aug%i) does not exist.", augments[iter], iter + 1); - Log(Logs::Detail, Logs::Inventory, "Player %s on account %s attempted to create an augment (Aug%i) with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, Aug6: %u)\n", + LogInventory("Player [{}] on account [{}] attempted to create an augment (Aug[{}]) with an invalid id.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5, aug6); return false; @@ -255,7 +255,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, // check that augment is an actual augment else if(augtest->AugType == 0) { Message(Chat::Red, "%s (%u) (Aug%i) is not an actual augment.", augtest->Name, augtest->ID, iter + 1); - Log(Logs::Detail, Logs::Inventory, "Player %s on account %s attempted to use a non-augment item (Aug%i) as an augment.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, Aug6: %u)\n", + LogInventory("Player [{}] on account [{}] attempted to use a non-augment item (Aug[{}]) as an augment.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", GetName(), account_name, item->ID, (iter + 1), aug1, aug2, aug3, aug4, aug5, aug6); return false; @@ -267,7 +267,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, /* else if(augtest->MinStatus && ((this->Admin() < augtest->MinStatus) || (this->Admin() < RuleI(GM, MinStatusToSummonItem)))) { Message(Chat::Red, "You are not a GM or do not have the status to summon this augment."); - Log(Logs::Detail, Logs::Inventory, "Player %s on account %s attempted to create a GM-only augment (Aug%i) with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, MinStatus: %u)\n", + LogInventory("Player [{}] on account [{}] attempted to create a GM-only augment (Aug[{}]) with a status of [{}].\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], MinStatus: [{}])\n", GetName(), account_name, (iter + 1), this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5, aug6, item->MinStatus); return false; @@ -278,7 +278,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, if(enforcewear) { if ((item->AugSlotType[iter] == EQEmu::item::AugTypeNone) || !(((uint32)1 << (item->AugSlotType[iter] - 1)) & augtest->AugType)) { Message(Chat::Red, "Augment %u (Aug%i) is not acceptable wear on Item %u.", augments[iter], iter + 1, item->ID); - Log(Logs::Detail, Logs::Inventory, "Player %s on account %s attempted to augment an item with an unacceptable augment type (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, Aug6: %u)\n", + LogInventory("Player [{}] on account [{}] attempted to augment an item with an unacceptable augment type (Aug[{}]).\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5, aug6); return false; @@ -286,7 +286,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, if(item->AugSlotVisible[iter] == 0) { Message(Chat::Red, "Item %u has not evolved enough to accept Augment %u (Aug%i).", item->ID, augments[iter], iter + 1); - Log(Logs::Detail, Logs::Inventory, "Player %s on account %s attempted to augment an unevolved item with augment type (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, Aug6: %u)\n", + LogInventory("Player [{}] on account [{}] attempted to augment an unevolved item with augment type (Aug[{}]).\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5, aug6); return false; @@ -463,7 +463,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, if(restrictfail) { Message(Chat::Red, "Augment %u (Aug%i) is restricted from wear on Item %u.", augments[iter], (iter + 1), item->ID); - Log(Logs::Detail, Logs::Inventory, "Player %s on account %s attempted to augment an item with a restricted augment (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, Aug6: %u)\n", + LogInventory("Player [{}] on account [{}] attempted to augment an item with a restricted augment (Aug[{}]).\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5, aug6); return false; @@ -474,7 +474,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, // check for class usability if(item->Classes && !(classes &= augtest->Classes)) { Message(Chat::Red, "Augment %u (Aug%i) will result in an item not usable by any class.", augments[iter], (iter + 1)); - Log(Logs::Detail, Logs::Inventory, "Player %s on account %s attempted to create an item unusable by any class.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, Aug6: %u)\n", + LogInventory("Player [{}] on account [{}] attempted to create an item unusable by any class.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5, aug6); return false; @@ -483,7 +483,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, // check for race usability if(item->Races && !(races &= augtest->Races)) { Message(Chat::Red, "Augment %u (Aug%i) will result in an item not usable by any race.", augments[iter], (iter + 1)); - Log(Logs::Detail, Logs::Inventory, "Player %s on account %s attempted to create an item unusable by any race.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, Aug6: %u)\n", + LogInventory("Player [{}] on account [{}] attempted to create an item unusable by any race.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5, aug6); return false; @@ -492,7 +492,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, // check for slot usability if(item->Slots && !(slots &= augtest->Slots)) { Message(Chat::Red, "Augment %u (Aug%i) will result in an item not usable in any slot.", augments[iter], (iter + 1)); - Log(Logs::Detail, Logs::Inventory, "Player %s on account %s attempted to create an item unusable in any slot.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, Aug6: %u)\n", + LogInventory("Player [{}] on account [{}] attempted to create an item unusable in any slot.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5, aug6); return false; @@ -519,7 +519,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, if(inst == nullptr) { Message(Chat::Red, "An unknown server error has occurred and your item was not created."); // this goes to logfile since this is a major error - Log(Logs::General, Logs::Error, "Player %s on account %s encountered an unknown item creation error.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, Aug6: %u)\n", + LogError("Player [{}] on account [{}] encountered an unknown item creation error.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5, aug6); return false; @@ -545,7 +545,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, if(!(slots & ((uint32)1 << slottest))) { Message(0, "This item is not equipable at slot %u - moving to cursor.", to_slot); - Log(Logs::Detail, Logs::Inventory, "Player %s on account %s attempted to equip an item unusable in slot %u - moved to cursor.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, Aug6: %u)\n", + LogInventory("Player [{}] on account [{}] attempted to equip an item unusable in slot [{}] - moved to cursor.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", GetName(), account_name, to_slot, item->ID, aug1, aug2, aug3, aug4, aug5, aug6); to_slot = EQEmu::invslot::slotCursor; @@ -582,7 +582,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, // Drop item from inventory to ground (generally only dropped from SLOT_CURSOR) void Client::DropItem(int16 slot_id, bool recurse) { - Log(Logs::General, Logs::Inventory, "'%s' (char_id: %u) Attempting to drop item from slot %i on the ground", + LogInventory("[{}] (char_id: [{}]) Attempting to drop item from slot [{}] on the ground", GetCleanName(), CharacterID(), slot_id); if(GetInv().CheckNoDrop(slot_id, recurse) && RuleI(World, FVNoDropFlag) == 0 || @@ -590,20 +590,20 @@ void Client::DropItem(int16 slot_id, bool recurse) { auto invalid_drop = m_inv.GetItem(slot_id); if (!invalid_drop) { - Log(Logs::General, Logs::Inventory, "Error in InventoryProfile::CheckNoDrop() - returned 'true' for empty slot"); + LogInventory("Error in InventoryProfile::CheckNoDrop() - returned 'true' for empty slot"); } else { if (LogSys.log_settings[Logs::Inventory].is_category_enabled) { - Log(Logs::General, Logs::Inventory, "DropItem() Hack detected - full item parse:"); - Log(Logs::General, Logs::Inventory, "depth: 0, Item: '%s' (id: %u), IsDroppable: %s", + LogInventory("DropItem() Hack detected - full item parse:"); + LogInventory("depth: 0, Item: [{}] (id: [{}]), IsDroppable: [{}]", (invalid_drop->GetItem() ? invalid_drop->GetItem()->Name : "null data"), invalid_drop->GetID(), (invalid_drop->IsDroppable(false) ? "true" : "false")); for (auto iter1 : *invalid_drop->GetContents()) { // depth 1 - Log(Logs::General, Logs::Inventory, "-depth: 1, Item: '%s' (id: %u), IsDroppable: %s", + LogInventory("-depth: 1, Item: [{}] (id: [{}]), IsDroppable: [{}]", (iter1.second->GetItem() ? iter1.second->GetItem()->Name : "null data"), iter1.second->GetID(), (iter1.second->IsDroppable(false) ? "true" : "false")); for (auto iter2 : *iter1.second->GetContents()) { // depth 2 - Log(Logs::General, Logs::Inventory, "--depth: 2, Item: '%s' (id: %u), IsDroppable: %s", + LogInventory("--depth: 2, Item: [{}] (id: [{}]), IsDroppable: [{}]", (iter2.second->GetItem() ? iter2.second->GetItem()->Name : "null data"), iter2.second->GetID(), (iter2.second->IsDroppable(false) ? "true" : "false")); } } @@ -620,38 +620,38 @@ void Client::DropItem(int16 slot_id, bool recurse) EQEmu::ItemInstance *inst = m_inv.PopItem(slot_id); if(inst) { if (LogSys.log_settings[Logs::Inventory].is_category_enabled) { - Log(Logs::General, Logs::Inventory, "DropItem() Processing - full item parse:"); - Log(Logs::General, Logs::Inventory, "depth: 0, Item: '%s' (id: %u), IsDroppable: %s", + LogInventory("DropItem() Processing - full item parse:"); + LogInventory("depth: 0, Item: [{}] (id: [{}]), IsDroppable: [{}]", (inst->GetItem() ? inst->GetItem()->Name : "null data"), inst->GetID(), (inst->IsDroppable(false) ? "true" : "false")); if (!inst->IsDroppable(false)) - Log(Logs::General, Logs::Error, "Non-droppable item being processed for drop by '%s'", GetCleanName()); + LogError("Non-droppable item being processed for drop by [{}]", GetCleanName()); for (auto iter1 : *inst->GetContents()) { // depth 1 - Log(Logs::General, Logs::Inventory, "-depth: 1, Item: '%s' (id: %u), IsDroppable: %s", + LogInventory("-depth: 1, Item: [{}] (id: [{}]), IsDroppable: [{}]", (iter1.second->GetItem() ? iter1.second->GetItem()->Name : "null data"), iter1.second->GetID(), (iter1.second->IsDroppable(false) ? "true" : "false")); if (!iter1.second->IsDroppable(false)) - Log(Logs::General, Logs::Error, "Non-droppable item being processed for drop by '%s'", GetCleanName()); + LogError("Non-droppable item being processed for drop by [{}]", GetCleanName()); for (auto iter2 : *iter1.second->GetContents()) { // depth 2 - Log(Logs::General, Logs::Inventory, "--depth: 2, Item: '%s' (id: %u), IsDroppable: %s", + LogInventory("--depth: 2, Item: [{}] (id: [{}]), IsDroppable: [{}]", (iter2.second->GetItem() ? iter2.second->GetItem()->Name : "null data"), iter2.second->GetID(), (iter2.second->IsDroppable(false) ? "true" : "false")); if (!iter2.second->IsDroppable(false)) - Log(Logs::General, Logs::Error, "Non-droppable item being processed for drop by '%s'", GetCleanName()); + LogError("Non-droppable item being processed for drop by [{}]", GetCleanName()); } } } int i = parse->EventItem(EVENT_DROP_ITEM, this, inst, nullptr, "", slot_id); if(i != 0) { - Log(Logs::General, Logs::Inventory, "Item drop handled by [EVENT_DROP_ITEM]"); + LogInventory("Item drop handled by [EVENT_DROP_ITEM]"); safe_delete(inst); } } else { // Item doesn't exist in inventory! - Log(Logs::General, Logs::Inventory, "DropItem() - No item found in slot %i", slot_id); + LogInventory("DropItem() - No item found in slot [{}]", slot_id); Message(Chat::Red, "Error: Item not found in slot %i", slot_id); return; } @@ -673,7 +673,7 @@ void Client::DropItem(int16 slot_id, bool recurse) entity_list.AddObject(object, true); object->StartDecay(); - Log(Logs::General, Logs::Inventory, "Item drop handled ut assolet"); + LogInventory("Item drop handled ut assolet"); DropItemQS(inst, false); safe_delete(inst); @@ -851,7 +851,7 @@ void Client::SendCursorBuffer() } if (!lore_pass) { - Log(Logs::General, Logs::Inventory, "(%s) Duplicate lore items are not allowed - destroying item %s(id:%u) on cursor", + LogInventory("([{}]) Duplicate lore items are not allowed - destroying item [{}](id:[{}]) on cursor", GetName(), test_item->Name, test_item->ID); MessageString(Chat::Loot, 290); parse->EventItem(EVENT_DESTROY_ITEM, this, test_inst, nullptr, "", 0); @@ -866,7 +866,7 @@ void Client::SendCursorBuffer() // Remove item from inventory void Client::DeleteItemInInventory(int16 slot_id, int8 quantity, bool client_update, bool update_db) { #if (EQDEBUG >= 5) - Log(Logs::General, Logs::None, "DeleteItemInInventory(%i, %i, %s)", slot_id, quantity, (client_update) ? "true":"false"); + LogDebug("DeleteItemInInventory([{}], [{}], [{}])", slot_id, quantity, (client_update) ? "true":"false"); #endif // Added 'IsSlotValid(slot_id)' check to both segments of client packet processing. @@ -986,7 +986,7 @@ void Client::DeleteItemInInventory(int16 slot_id, int8 quantity, bool client_upd bool Client::PushItemOnCursor(const EQEmu::ItemInstance& inst, bool client_update) { - Log(Logs::Detail, Logs::Inventory, "Putting item %s (%d) on the cursor", inst.GetItem()->Name, inst.GetItem()->ID); + LogInventory("Putting item [{}] ([{}]) on the cursor", inst.GetItem()->Name, inst.GetItem()->ID); m_inv.PushCursor(inst); if (client_update) { @@ -1002,7 +1002,7 @@ bool Client::PushItemOnCursor(const EQEmu::ItemInstance& inst, bool client_updat // (Also saves changes back to the database: this may be optimized in the future) // client_update: Sends packet to client bool Client::PutItemInInventory(int16 slot_id, const EQEmu::ItemInstance& inst, bool client_update) { - Log(Logs::Detail, Logs::Inventory, "Putting item %s (%d) into slot %d", inst.GetItem()->Name, inst.GetItem()->ID, slot_id); + LogInventory("Putting item [{}] ([{}]) into slot [{}]", inst.GetItem()->Name, inst.GetItem()->ID, slot_id); if (slot_id == EQEmu::invslot::slotCursor) { // don't trust macros before conditional statements... return PushItemOnCursor(inst, client_update); @@ -1031,7 +1031,7 @@ bool Client::PutItemInInventory(int16 slot_id, const EQEmu::ItemInstance& inst, void Client::PutLootInInventory(int16 slot_id, const EQEmu::ItemInstance &inst, ServerLootItem_Struct** bag_item_data) { - Log(Logs::Detail, Logs::Inventory, "Putting loot item %s (%d) into slot %d", inst.GetItem()->Name, inst.GetItem()->ID, slot_id); + LogInventory("Putting loot item [{}] ([{}]) into slot [{}]", inst.GetItem()->Name, inst.GetItem()->ID, slot_id); bool cursor_empty = m_inv.CursorEmpty(); @@ -1075,8 +1075,7 @@ void Client::PutLootInInventory(int16 slot_id, const EQEmu::ItemInstance &inst, // Dump bag contents to cursor in the event that owning bag is not the first cursor item // (This assumes that the data passed is correctly associated..no safety checks are implemented) if (slot_id == EQEmu::invslot::slotCursor && !cursor_empty) { - Log(Logs::Detail, Logs::Inventory, - "Putting bag loot item %s (%d) into slot %d (non-empty cursor override)", + LogInventory("Putting bag loot item [{}] ([{}]) into slot [{}] (non-empty cursor override)", inst.GetItem()->Name, inst.GetItem()->ID, EQEmu::invslot::slotCursor); PutLootInInventory(EQEmu::invslot::slotCursor, *bagitem); @@ -1084,8 +1083,7 @@ void Client::PutLootInInventory(int16 slot_id, const EQEmu::ItemInstance &inst, else { auto bag_slot = EQEmu::InventoryProfile::CalcSlotId(slot_id, index); - Log(Logs::Detail, Logs::Inventory, - "Putting bag loot item %s (%d) into slot %d (bag slot %d)", + LogInventory("Putting bag loot item [{}] ([{}]) into slot [{}] (bag slot [{}])", inst.GetItem()->Name, inst.GetItem()->ID, bag_slot, index); PutLootInInventory(bag_slot, *bagitem); @@ -1521,7 +1519,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { // SoF+ sends a Unix timestamp (should be int32) for src and dst slots every 10 minutes for some reason. if(src_slot_check < 2147483647) Message(Chat::Red, "Warning: Invalid slot move from slot %u to slot %u with %u charges!", src_slot_check, dst_slot_check, stack_count_check); - Log(Logs::Detail, Logs::Inventory, "Invalid slot move from slot %u to slot %u with %u charges!", src_slot_check, dst_slot_check, stack_count_check); + LogInventory("Invalid slot move from slot [{}] to slot [{}] with [{}] charges!", src_slot_check, dst_slot_check, stack_count_check); return false; } @@ -1529,7 +1527,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { // SoF+ sends a Unix timestamp (should be int32) for src and dst slots every 10 minutes for some reason. if(src_slot_check < 2147483647) Message(Chat::Red, "Warning: Invalid slot move from slot %u to slot %u with %u charges!", src_slot_check, dst_slot_check, stack_count_check); - Log(Logs::Detail, Logs::Inventory, "Invalid slot move from slot %u to slot %u with %u charges!", src_slot_check, dst_slot_check, stack_count_check); + LogInventory("Invalid slot move from slot [{}] to slot [{}] with [{}] charges!", src_slot_check, dst_slot_check, stack_count_check); return false; } @@ -1553,7 +1551,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { } if (!lore_pass) { - Log(Logs::General, Logs::Inventory, "(%s) Duplicate lore items are not allowed - destroying item %s(id:%u) on cursor", + LogInventory("([{}]) Duplicate lore items are not allowed - destroying item [{}](id:[{}]) on cursor", GetName(), test_item->Name, test_item->ID); MessageString(Chat::Loot, 290); parse->EventItem(EVENT_DESTROY_ITEM, this, test_inst, nullptr, "", 0); @@ -1565,7 +1563,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { if (move_in->to_slot == (uint32)INVALID_INDEX) { if (move_in->from_slot == (uint32)EQEmu::invslot::slotCursor) { - Log(Logs::Detail, Logs::Inventory, "Client destroyed item from cursor slot %d", move_in->from_slot); + LogInventory("Client destroyed item from cursor slot [{}]", move_in->from_slot); if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit EQEmu::ItemInstance *inst = m_inv.GetItem(EQEmu::invslot::slotCursor); @@ -1579,7 +1577,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { return true; // Item destroyed by client } else { - Log(Logs::Detail, Logs::Inventory, "Deleted item from slot %d as a result of an inventory container tradeskill combine.", move_in->from_slot); + LogInventory("Deleted item from slot [{}] as a result of an inventory container tradeskill combine", move_in->from_slot); if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit DeleteItemInInventory(move_in->from_slot); return true; // Item deletion @@ -1619,7 +1617,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { EQEmu::ItemInstance* src_inst = m_inv.GetItem(src_slot_id); EQEmu::ItemInstance* dst_inst = m_inv.GetItem(dst_slot_id); if (src_inst){ - Log(Logs::Detail, Logs::Inventory, "Src slot %d has item %s (%d) with %d charges in it.", src_slot_id, src_inst->GetItem()->Name, src_inst->GetItem()->ID, src_inst->GetCharges()); + LogInventory("Src slot [{}] has item [{}] ([{}]) with [{}] charges in it", src_slot_id, src_inst->GetItem()->Name, src_inst->GetItem()->ID, src_inst->GetCharges()); srcitemid = src_inst->GetItem()->ID; //SetTint(dst_slot_id,src_inst->GetColor()); if (src_inst->GetCharges() > 0 && (src_inst->GetCharges() < (int16)move_in->number_in_stack || move_in->number_in_stack > src_inst->GetItem()->StackSize)) @@ -1629,7 +1627,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { } } if (dst_inst) { - Log(Logs::Detail, Logs::Inventory, "Dest slot %d has item %s (%d) with %d charges in it.", dst_slot_id, dst_inst->GetItem()->Name, dst_inst->GetItem()->ID, dst_inst->GetCharges()); + LogInventory("Dest slot [{}] has item [{}] ([{}]) with [{}] charges in it", dst_slot_id, dst_inst->GetItem()->Name, dst_inst->GetItem()->ID, dst_inst->GetCharges()); dstitemid = dst_inst->GetItem()->ID; } if (Trader && srcitemid>0){ @@ -1664,7 +1662,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { move_in->from_slot = dst_slot_check; move_in->to_slot = src_slot_check; move_in->number_in_stack = dst_inst->GetCharges(); - if(!SwapItem(move_in)) { Log(Logs::Detail, Logs::Inventory, "Recursive SwapItem call failed due to non-existent destination item (charid: %i, fromslot: %i, toslot: %i)", CharacterID(), src_slot_id, dst_slot_id); } + if(!SwapItem(move_in)) { LogInventory("Recursive SwapItem call failed due to non-existent destination item (charid: [{}], fromslot: [{}], toslot: [{}])", CharacterID(), src_slot_id, dst_slot_id); } } return false; @@ -1672,7 +1670,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { //verify shared bank transactions in the database if (src_inst && src_slot_id >= EQEmu::invslot::SHARED_BANK_BEGIN && src_slot_id <= EQEmu::invbag::SHARED_BANK_BAGS_END) { if(!database.VerifyInventory(account_id, src_slot_id, src_inst)) { - Log(Logs::General, Logs::Error, "Player %s on account %s was found exploiting the shared bank.\n", GetName(), account_name); + LogError("Player [{}] on account [{}] was found exploiting the shared bank.\n", GetName(), account_name); DeleteItemInInventory(dst_slot_id,0,true); return(false); } @@ -1687,7 +1685,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { } if (dst_inst && dst_slot_id >= EQEmu::invslot::SHARED_BANK_BEGIN && dst_slot_id <= EQEmu::invbag::SHARED_BANK_BAGS_END) { if(!database.VerifyInventory(account_id, dst_slot_id, dst_inst)) { - Log(Logs::General, Logs::Error, "Player %s on account %s was found exploting the shared bank.\n", GetName(), account_name); + LogError("Player [{}] on account [{}] was found exploting the shared bank.\n", GetName(), account_name); DeleteItemInInventory(src_slot_id,0,true); return(false); } @@ -1724,7 +1722,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { ndh_item_data.append(StringFormat(", nodrop=%s(%u)", (ndh_item->NoDrop == 0 ? "true" : "false"), ndh_item->NoDrop)); } } - Log(Logs::General, Logs::Error, "WorldKick() of Player %s(id:%u, acct:%u) due to 'NoDrop Hack' detection >> SlotID:%i, ItemData:'%s'", + LogError("WorldKick() of Player [{}](id:[{}], acct:[{}]) due to 'NoDrop Hack' detection >> SlotID:[{}], ItemData:[{}]", GetName(), CharacterID(), AccountID(), src_slot_id, ndh_item_data.c_str()); ndh_inst = nullptr; @@ -1826,7 +1824,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { return false; } if (with) { - Log(Logs::Detail, Logs::Inventory, "Trade item move from slot %d to slot %d (trade with %s)", src_slot_id, dst_slot_id, with->GetName()); + LogInventory("Trade item move from slot [{}] to slot [{}] (trade with [{}])", src_slot_id, dst_slot_id, with->GetName()); // Fill Trade list with items from cursor if (!m_inv[EQEmu::invslot::slotCursor]) { Message(Chat::Red, "Error: Cursor item not located on server!"); @@ -1859,18 +1857,18 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { if (move_in->number_in_stack > 0) { // Determine if charged items can stack if(src_inst && !src_inst->IsStackable()) { - Log(Logs::Detail, Logs::Inventory, "Move from %d to %d with stack size %d. %s is not a stackable item. (charname: %s)", src_slot_id, dst_slot_id, move_in->number_in_stack, src_inst->GetItem()->Name, GetName()); + LogInventory("Move from [{}] to [{}] with stack size [{}]. [{}] is not a stackable item. (charname: [{}])", src_slot_id, dst_slot_id, move_in->number_in_stack, src_inst->GetItem()->Name, GetName()); return false; } if (dst_inst) { if(src_inst->GetID() != dst_inst->GetID()) { - Log(Logs::Detail, Logs::Inventory, "Move from %d to %d with stack size %d. Incompatible item types: %d != %d", src_slot_id, dst_slot_id, move_in->number_in_stack, src_inst->GetID(), dst_inst->GetID()); + LogInventory("Move from [{}] to [{}] with stack size [{}]. Incompatible item types: [{}] != [{}]", src_slot_id, dst_slot_id, move_in->number_in_stack, src_inst->GetID(), dst_inst->GetID()); return(false); } if(dst_inst->GetCharges() < dst_inst->GetItem()->StackSize) { //we have a chance of stacking. - Log(Logs::Detail, Logs::Inventory, "Move from %d to %d with stack size %d. dest has %d/%d charges", src_slot_id, dst_slot_id, move_in->number_in_stack, dst_inst->GetCharges(), dst_inst->GetItem()->StackSize); + LogInventory("Move from [{}] to [{}] with stack size [{}]. dest has [{}]/[{}] charges", src_slot_id, dst_slot_id, move_in->number_in_stack, dst_inst->GetCharges(), dst_inst->GetItem()->StackSize); // Charges can be emptied into dst uint16 usedcharges = dst_inst->GetItem()->StackSize - dst_inst->GetCharges(); if (usedcharges > move_in->number_in_stack) @@ -1882,15 +1880,15 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { // Depleted all charges? if (src_inst->GetCharges() < 1) { - Log(Logs::Detail, Logs::Inventory, "Dest (%d) now has %d charges, source (%d) was entirely consumed. (%d moved)", dst_slot_id, dst_inst->GetCharges(), src_slot_id, usedcharges); + LogInventory("Dest ([{}]) now has [{}] charges, source ([{}]) was entirely consumed. ([{}] moved)", dst_slot_id, dst_inst->GetCharges(), src_slot_id, usedcharges); database.SaveInventory(CharacterID(),nullptr,src_slot_id); m_inv.DeleteItem(src_slot_id); all_to_stack = true; } else { - Log(Logs::Detail, Logs::Inventory, "Dest (%d) now has %d charges, source (%d) has %d (%d moved)", dst_slot_id, dst_inst->GetCharges(), src_slot_id, src_inst->GetCharges(), usedcharges); + LogInventory("Dest ([{}]) now has [{}] charges, source ([{}]) has [{}] ([{}] moved)", dst_slot_id, dst_inst->GetCharges(), src_slot_id, src_inst->GetCharges(), usedcharges); } } else { - Log(Logs::Detail, Logs::Inventory, "Move from %d to %d with stack size %d. Exceeds dest maximum stack size: %d/%d", src_slot_id, dst_slot_id, move_in->number_in_stack, (src_inst->GetCharges()+dst_inst->GetCharges()), dst_inst->GetItem()->StackSize); + LogInventory("Move from [{}] to [{}] with stack size [{}]. Exceeds dest maximum stack size: [{}]/[{}]", src_slot_id, dst_slot_id, move_in->number_in_stack, (src_inst->GetCharges()+dst_inst->GetCharges()), dst_inst->GetItem()->StackSize); return false; } } @@ -1900,12 +1898,12 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { // Move entire stack EQEmu::InventoryProfile::SwapItemFailState fail_state = EQEmu::InventoryProfile::swapInvalid; if (!m_inv.SwapItem(src_slot_id, dst_slot_id, fail_state)) { return false; } - Log(Logs::Detail, Logs::Inventory, "Move entire stack from %d to %d with stack size %d. Dest empty.", src_slot_id, dst_slot_id, move_in->number_in_stack); + LogInventory("Move entire stack from [{}] to [{}] with stack size [{}]. Dest empty", src_slot_id, dst_slot_id, move_in->number_in_stack); } else { // Split into two src_inst->SetCharges(src_inst->GetCharges() - move_in->number_in_stack); - Log(Logs::Detail, Logs::Inventory, "Split stack of %s (%d) from slot %d to %d with stack size %d. Src keeps %d.", src_inst->GetItem()->Name, src_inst->GetItem()->ID, src_slot_id, dst_slot_id, move_in->number_in_stack, src_inst->GetCharges()); + LogInventory("Split stack of [{}] ([{}]) from slot [{}] to [{}] with stack size [{}]. Src keeps [{}]", src_inst->GetItem()->Name, src_inst->GetItem()->ID, src_slot_id, dst_slot_id, move_in->number_in_stack, src_inst->GetCharges()); EQEmu::ItemInstance* inst = database.CreateItem(src_inst->GetItem(), move_in->number_in_stack); m_inv.PutItem(dst_slot_id, *inst); safe_delete(inst); @@ -1944,7 +1942,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { return false; } - Log(Logs::Detail, Logs::Inventory, "Moving entire item from slot %d to slot %d", src_slot_id, dst_slot_id); + LogInventory("Moving entire item from slot [{}] to slot [{}]", src_slot_id, dst_slot_id); if (src_slot_id <= EQEmu::invslot::EQUIPMENT_END) { if(src_inst) { @@ -2007,7 +2005,7 @@ void Client::SwapItemResync(MoveItem_Struct* move_slots) { // resync the 'from' and 'to' slots on an as-needed basis // Not as effective as the full process, but less intrusive to gameplay - Log(Logs::Detail, Logs::Inventory, "Inventory desyncronization. (charname: %s, source: %i, destination: %i)", GetName(), move_slots->from_slot, move_slots->to_slot); + LogInventory("Inventory desyncronization. (charname: [{}], source: [{}], destination: [{}])", GetName(), move_slots->from_slot, move_slots->to_slot); Message(Chat::Yellow, "Inventory Desyncronization detected: Resending slot data..."); if (move_slots->from_slot >= EQEmu::invslot::EQUIPMENT_BEGIN && move_slots->from_slot <= EQEmu::invbag::CURSOR_BAG_END) { @@ -2459,7 +2457,7 @@ static bool CopyBagContents(EQEmu::ItemInstance* new_bag, const EQEmu::ItemInsta for (auto bag_slot = 0; bag_slot < old_bag->GetItem()->BagSlots; ++bag_slot) { if (!old_bag->GetItem(bag_slot)) { continue; } if (old_bag->GetItem(bag_slot)->GetItem()->Size > new_bag->GetItem()->BagSize) { - Log(Logs::General, Logs::Inventory, "Copy Bag Contents: Failure due to %s is larger than size capacity of %s (%i > %i)", + LogInventory("Copy Bag Contents: Failure due to [{}] is larger than size capacity of [{}] ([{}] > [{}])", old_bag->GetItem(bag_slot)->GetItem()->Name, new_bag->GetItem()->Name, old_bag->GetItem(bag_slot)->GetItem()->Size, new_bag->GetItem()->BagSize); return false; } @@ -2493,7 +2491,7 @@ void Client::DisenchantSummonedBags(bool client_update) if (!new_inst) { continue; } if (CopyBagContents(new_inst, inst)) { - Log(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id); + LogInventory("Disenchant Summoned Bags: Replacing [{}] with [{}] in slot [{}]", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id); PutItemInInventory(slot_id, *new_inst, client_update); } safe_delete(new_inst); @@ -2517,7 +2515,7 @@ void Client::DisenchantSummonedBags(bool client_update) if (!new_inst) { continue; } if (CopyBagContents(new_inst, inst)) { - Log(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id); + LogInventory("Disenchant Summoned Bags: Replacing [{}] with [{}] in slot [{}]", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id); PutItemInInventory(slot_id, *new_inst, client_update); } safe_delete(new_inst); @@ -2538,7 +2536,7 @@ void Client::DisenchantSummonedBags(bool client_update) if (!new_inst) { continue; } if (CopyBagContents(new_inst, inst)) { - Log(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id); + LogInventory("Disenchant Summoned Bags: Replacing [{}] with [{}] in slot [{}]", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id); PutItemInInventory(slot_id, *new_inst, client_update); } safe_delete(new_inst); @@ -2559,7 +2557,7 @@ void Client::DisenchantSummonedBags(bool client_update) if (!new_inst) { break; } if (CopyBagContents(new_inst, inst)) { - Log(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, EQEmu::invslot::slotCursor); + LogInventory("Disenchant Summoned Bags: Replacing [{}] with [{}] in slot [{}]", inst->GetItem()->Name, new_inst->GetItem()->Name, EQEmu::invslot::slotCursor); std::list local; local.push_front(new_inst); m_inv.PopItem(EQEmu::invslot::slotCursor); @@ -2598,7 +2596,7 @@ void Client::RemoveNoRent(bool client_update) auto inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { - Log(Logs::Detail, Logs::Inventory, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + LogInventory("NoRent Timer Lapse: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); DeleteItemInInventory(slot_id, 0, client_update); } } @@ -2609,7 +2607,7 @@ void Client::RemoveNoRent(bool client_update) auto inst = m_inv[slot_id]; if (inst && !inst->GetItem()->NoRent) { - Log(Logs::Detail, Logs::Inventory, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + LogInventory("NoRent Timer Lapse: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); DeleteItemInInventory(slot_id, 0, client_update); } } @@ -2621,7 +2619,7 @@ void Client::RemoveNoRent(bool client_update) auto inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { - Log(Logs::Detail, Logs::Inventory, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + LogInventory("NoRent Timer Lapse: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); DeleteItemInInventory(slot_id, 0, client_update); } } @@ -2632,7 +2630,7 @@ void Client::RemoveNoRent(bool client_update) auto inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { - Log(Logs::Detail, Logs::Inventory, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + LogInventory("NoRent Timer Lapse: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); DeleteItemInInventory(slot_id, 0, false); // Can't delete from client Bank slots } } @@ -2644,7 +2642,7 @@ void Client::RemoveNoRent(bool client_update) auto inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { - Log(Logs::Detail, Logs::Inventory, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + LogInventory("NoRent Timer Lapse: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); DeleteItemInInventory(slot_id, 0, false); // Can't delete from client Bank Container slots } } @@ -2652,7 +2650,7 @@ void Client::RemoveNoRent(bool client_update) for (auto slot_id = EQEmu::invslot::SHARED_BANK_BEGIN; slot_id <= EQEmu::invslot::SHARED_BANK_END; ++slot_id) { auto inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { - Log(Logs::Detail, Logs::Inventory, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + LogInventory("NoRent Timer Lapse: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); DeleteItemInInventory(slot_id, 0, false); // Can't delete from client Shared Bank slots } } @@ -2660,7 +2658,7 @@ void Client::RemoveNoRent(bool client_update) for (auto slot_id = EQEmu::invbag::SHARED_BANK_BAGS_BEGIN; slot_id <= EQEmu::invbag::SHARED_BANK_BAGS_END; ++slot_id) { auto inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { - Log(Logs::Detail, Logs::Inventory, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + LogInventory("NoRent Timer Lapse: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); DeleteItemInInventory(slot_id, 0, false); // Can't delete from client Shared Bank Container slots } } @@ -2678,7 +2676,7 @@ void Client::RemoveNoRent(bool client_update) auto inst = *iter; if (inst == nullptr) { continue; } if (!inst->GetItem()->NoRent) { - Log(Logs::Detail, Logs::Inventory, "NoRent Timer Lapse: Deleting %s from `Limbo`", inst->GetItem()->Name); + LogInventory("NoRent Timer Lapse: Deleting [{}] from `Limbo`", inst->GetItem()->Name); } else { m_inv.PushCursor(*inst); @@ -2702,7 +2700,7 @@ void Client::RemoveDuplicateLore(bool client_update) auto inst = m_inv.PopItem(slot_id); if (inst == nullptr) { continue; } if(CheckLoreConflict(inst->GetItem())) { - Log(Logs::Detail, Logs::Inventory, "Lore Duplication Error: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + LogInventory("Lore Duplication Error: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); database.SaveInventory(character_id, nullptr, slot_id); } else { @@ -2718,7 +2716,7 @@ void Client::RemoveDuplicateLore(bool client_update) auto inst = m_inv.PopItem(slot_id); if (inst == nullptr) { continue; } if (CheckLoreConflict(inst->GetItem())) { - Log(Logs::Detail, Logs::Inventory, "Lore Duplication Error: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + LogInventory("Lore Duplication Error: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); database.SaveInventory(character_id, nullptr, slot_id); } else { @@ -2735,7 +2733,7 @@ void Client::RemoveDuplicateLore(bool client_update) auto inst = m_inv.PopItem(slot_id); if (inst == nullptr) { continue; } if(CheckLoreConflict(inst->GetItem())) { - Log(Logs::Detail, Logs::Inventory, "Lore Duplication Error: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + LogInventory("Lore Duplication Error: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); database.SaveInventory(character_id, nullptr, slot_id); } else { @@ -2751,7 +2749,7 @@ void Client::RemoveDuplicateLore(bool client_update) auto inst = m_inv.PopItem(slot_id); if (inst == nullptr) { continue; } if(CheckLoreConflict(inst->GetItem())) { - Log(Logs::Detail, Logs::Inventory, "Lore Duplication Error: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + LogInventory("Lore Duplication Error: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); database.SaveInventory(character_id, nullptr, slot_id); } else { @@ -2768,7 +2766,7 @@ void Client::RemoveDuplicateLore(bool client_update) auto inst = m_inv.PopItem(slot_id); if (inst == nullptr) { continue; } if(CheckLoreConflict(inst->GetItem())) { - Log(Logs::Detail, Logs::Inventory, "Lore Duplication Error: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + LogInventory("Lore Duplication Error: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); database.SaveInventory(character_id, nullptr, slot_id); } else { @@ -2793,7 +2791,7 @@ void Client::RemoveDuplicateLore(bool client_update) auto inst = *iter; if (inst == nullptr) { continue; } if (CheckLoreConflict(inst->GetItem())) { - Log(Logs::Detail, Logs::Inventory, "Lore Duplication Error: Deleting %s from `Limbo`", inst->GetItem()->Name); + LogInventory("Lore Duplication Error: Deleting [{}] from `Limbo`", inst->GetItem()->Name); safe_delete(inst); } else { @@ -2812,7 +2810,7 @@ void Client::RemoveDuplicateLore(bool client_update) m_inv.PushCursor(*inst); } else { - Log(Logs::Detail, Logs::Inventory, "Lore Duplication Error: Deleting %s from `Limbo`", inst->GetItem()->Name); + LogInventory("Lore Duplication Error: Deleting [{}] from `Limbo`", inst->GetItem()->Name); } safe_delete(inst); } @@ -2830,7 +2828,7 @@ void Client::MoveSlotNotAllowed(bool client_update) auto inst = m_inv.PopItem(slot_id); bool is_arrow = (inst->GetItem()->ItemType == EQEmu::item::ItemTypeArrow) ? true : false; int16 free_slot_id = m_inv.FindFreeSlot(inst->IsClassBag(), true, inst->GetItem()->Size, is_arrow); - Log(Logs::Detail, Logs::Inventory, "Slot Assignment Error: Moving %s from slot %i to %i", inst->GetItem()->Name, slot_id, free_slot_id); + LogInventory("Slot Assignment Error: Moving [{}] from slot [{}] to [{}]", inst->GetItem()->Name, slot_id, free_slot_id); PutItemInInventory(free_slot_id, *inst, client_update); database.SaveInventory(character_id, nullptr, slot_id); safe_delete(inst); @@ -2846,7 +2844,7 @@ void Client::MoveSlotNotAllowed(bool client_update) // auto inst = m_inv.PopItem(slot_id); // bool is_arrow = (inst->GetItem()->ItemType == EQEmu::item::ItemTypeArrow) ? true : false; // int16 free_slot_id = m_inv.FindFreeSlot(inst->IsClassBag(), true, inst->GetItem()->Size, is_arrow); - // Log(Logs::Detail, Logs::Inventory, "Slot Assignment Error: Moving %s from slot %i to %i", inst->GetItem()->Name, slot_id, free_slot_id); + // LogInventory("Slot Assignment Error: Moving [{}] from slot [{}] to [{}]", inst->GetItem()->Name, slot_id, free_slot_id); // PutItemInInventory(free_slot_id, *inst, client_update); // database.SaveInventory(character_id, nullptr, slot_id); // safe_delete(inst); @@ -3001,7 +2999,7 @@ void Client::CreateBandolier(const EQApplicationPacket *app) BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer; - Log(Logs::Detail, Logs::Inventory, "Char: %s Creating Bandolier Set %i, Set Name: %s", GetName(), bs->Number, bs->Name); + LogInventory("Char: [{}] Creating Bandolier Set [{}], Set Name: [{}]", GetName(), bs->Number, bs->Name); strcpy(m_pp.bandoliers[bs->Number].Name, bs->Name); const EQEmu::ItemInstance* InvItem = nullptr; @@ -3015,13 +3013,13 @@ void Client::CreateBandolier(const EQApplicationPacket *app) InvItem = GetInv()[WeaponSlot]; if(InvItem) { BaseItem = InvItem->GetItem(); - Log(Logs::Detail, Logs::Inventory, "Char: %s adding item %s to slot %i", GetName(),BaseItem->Name, WeaponSlot); + LogInventory("Char: [{}] adding item [{}] to slot [{}]", GetName(),BaseItem->Name, WeaponSlot); m_pp.bandoliers[bs->Number].Items[BandolierSlot].ID = BaseItem->ID; m_pp.bandoliers[bs->Number].Items[BandolierSlot].Icon = BaseItem->Icon; database.SaveCharacterBandolier(this->CharacterID(), bs->Number, BandolierSlot, m_pp.bandoliers[bs->Number].Items[BandolierSlot].ID, m_pp.bandoliers[bs->Number].Items[BandolierSlot].Icon, bs->Name); } else { - Log(Logs::Detail, Logs::Inventory, "Char: %s no item in slot %i", GetName(), WeaponSlot); + LogInventory("Char: [{}] no item in slot [{}]", GetName(), WeaponSlot); m_pp.bandoliers[bs->Number].Items[BandolierSlot].ID = 0; m_pp.bandoliers[bs->Number].Items[BandolierSlot].Icon = 0; } @@ -3031,7 +3029,7 @@ void Client::CreateBandolier(const EQApplicationPacket *app) void Client::RemoveBandolier(const EQApplicationPacket *app) { BandolierDelete_Struct *bds = (BandolierDelete_Struct*)app->pBuffer; - Log(Logs::Detail, Logs::Inventory, "Char: %s removing set", GetName(), bds->Number); + LogInventory("Char: [{}] removing set", GetName(), bds->Number); memset(m_pp.bandoliers[bds->Number].Name, 0, 32); for(int i = bandolierPrimary; i <= bandolierAmmo; i++) { m_pp.bandoliers[bds->Number].Items[i].ID = 0; @@ -3046,7 +3044,7 @@ void Client::SetBandolier(const EQApplicationPacket *app) // any items currently in the weapon slots to inventory. BandolierSet_Struct *bss = (BandolierSet_Struct*)app->pBuffer; - Log(Logs::Detail, Logs::Inventory, "Char: %s activating set %i", GetName(), bss->Number); + LogInventory("Char: [{}] activating set [{}]", GetName(), bss->Number); int16 slot = 0; int16 WeaponSlot = 0; EQEmu::ItemInstance *BandolierItems[4]; // Temporary holding area for the weapons we pull out of their inventory @@ -3113,19 +3111,19 @@ void Client::SetBandolier(const EQApplicationPacket *app) else { // The player doesn't have the required weapon with them. BandolierItems[BandolierSlot] = 0; if (slot == INVALID_INDEX) { - Log(Logs::Detail, Logs::Inventory, "Character does not have required bandolier item for slot %i", WeaponSlot); + LogInventory("Character does not have required bandolier item for slot [{}]", WeaponSlot); EQEmu::ItemInstance *InvItem = m_inv.PopItem(WeaponSlot); if(InvItem) { // If there was an item in that weapon slot, put it in the inventory - Log(Logs::Detail, Logs::Inventory, "returning item %s in weapon slot %i to inventory", + LogInventory("returning item [{}] in weapon slot [{}] to inventory", InvItem->GetItem()->Name, WeaponSlot); - Log(Logs::Detail, Logs::Inventory, "returning item %s in weapon slot %i to inventory", InvItem->GetItem()->Name, WeaponSlot); + LogInventory("returning item [{}] in weapon slot [{}] to inventory", InvItem->GetItem()->Name, WeaponSlot); if (MoveItemToInventory(InvItem)) { database.SaveInventory(character_id, 0, WeaponSlot); - Log(Logs::General, Logs::Error, "returning item %s in weapon slot %i to inventory", InvItem->GetItem()->Name, WeaponSlot); + LogError("returning item [{}] in weapon slot [{}] to inventory", InvItem->GetItem()->Name, WeaponSlot); } else { - Log(Logs::General, Logs::Error, "Char: %s, ERROR returning %s to inventory", GetName(), InvItem->GetItem()->Name); + LogError("Char: [{}], ERROR returning [{}] to inventory", GetName(), InvItem->GetItem()->Name); } safe_delete(InvItem); } @@ -3159,7 +3157,7 @@ void Client::SetBandolier(const EQApplicationPacket *app) if(InvItem) { // If there was already an item in that weapon slot that we replaced, find a place to put it if (!MoveItemToInventory(InvItem)) { - Log(Logs::General, Logs::Error, "Char: %s, ERROR returning %s to inventory", GetName(), InvItem->GetItem()->Name); + LogError("Char: [{}], ERROR returning [{}] to inventory", GetName(), InvItem->GetItem()->Name); } safe_delete(InvItem); } @@ -3170,13 +3168,13 @@ void Client::SetBandolier(const EQApplicationPacket *app) // put it in the player's inventory. EQEmu::ItemInstance *InvItem = m_inv.PopItem(WeaponSlot); if(InvItem) { - Log(Logs::Detail, Logs::Inventory, "Bandolier has no item for slot %i, returning item %s to inventory", WeaponSlot, InvItem->GetItem()->Name); + LogInventory("Bandolier has no item for slot [{}], returning item [{}] to inventory", WeaponSlot, InvItem->GetItem()->Name); // If there was an item in that weapon slot, put it in the inventory if (MoveItemToInventory(InvItem)) { database.SaveInventory(character_id, 0, WeaponSlot); } else { - Log(Logs::General, Logs::Error, "Char: %s, ERROR returning %s to inventory", GetName(), InvItem->GetItem()->Name); + LogError("Char: [{}], ERROR returning [{}] to inventory", GetName(), InvItem->GetItem()->Name); } safe_delete(InvItem); } @@ -3209,7 +3207,7 @@ bool Client::MoveItemToInventory(EQEmu::ItemInstance *ItemToReturn, bool UpdateC return false; } - Log(Logs::Detail, Logs::Inventory,"Char: %s Returning %s to inventory", GetName(), ItemToReturn->GetItem()->Name); + LogInventory("Char: [{}] Returning [{}] to inventory", GetName(), ItemToReturn->GetItem()->Name); uint32 ItemID = ItemToReturn->GetItem()->ID; @@ -3293,7 +3291,7 @@ bool Client::MoveItemToInventory(EQEmu::ItemInstance *ItemToReturn, bool UpdateC database.SaveInventory(character_id, m_inv.GetItem(i), i); - Log(Logs::Detail, Logs::Inventory, "Char: %s Storing in main inventory slot %i", GetName(), i); + LogInventory("Char: [{}] Storing in main inventory slot [{}]", GetName(), i); return true; } @@ -3316,7 +3314,7 @@ bool Client::MoveItemToInventory(EQEmu::ItemInstance *ItemToReturn, bool UpdateC database.SaveInventory(character_id, m_inv.GetItem(BaseSlotID + BagSlot), BaseSlotID + BagSlot); - Log(Logs::Detail, Logs::Inventory, "Char: %s Storing in bag slot %i", GetName(), BaseSlotID + BagSlot); + LogInventory("Char: [{}] Storing in bag slot [{}]", GetName(), BaseSlotID + BagSlot); return true; } @@ -3326,7 +3324,7 @@ bool Client::MoveItemToInventory(EQEmu::ItemInstance *ItemToReturn, bool UpdateC // Store on the cursor // - Log(Logs::Detail, Logs::Inventory, "Char: %s No space, putting on the cursor", GetName()); + LogInventory("Char: [{}] No space, putting on the cursor", GetName()); PushItemOnCursor(*ItemToReturn, UpdateClient); @@ -3394,7 +3392,7 @@ bool Client::InterrogateInventory(Client* requester, bool log, bool silent, bool log = true; if (log) { - Log(Logs::General, Logs::Error, "Client::InterrogateInventory() called for %s by %s with an error state of %s", GetName(), requester->GetName(), (error ? "TRUE" : "FALSE")); + LogError("Client::InterrogateInventory() called for [{}] by [{}] with an error state of [{}]", GetName(), requester->GetName(), (error ? "TRUE" : "FALSE")); } if (!silent) { requester->Message(Chat::Default, "--- Inventory Interrogation Report for %s (requested by: %s, error state: %s) ---", GetName(), requester->GetName(), (error ? "TRUE" : "FALSE")); @@ -3415,8 +3413,8 @@ bool Client::InterrogateInventory(Client* requester, bool log, bool silent, bool } if (log) { - Log(Logs::General, Logs::Error, "Target interrogate inventory flag: %s", (GetInterrogateInvState() ? "TRUE" : "FALSE")); - Log(Logs::Detail, Logs::None, "[CLIENT] Client::InterrogateInventory() -- End"); + LogError("Target interrogate inventory flag: [{}]", (GetInterrogateInvState() ? "TRUE" : "FALSE")); + LogDebug("[CLIENT] Client::InterrogateInventory() -- End"); } if (!silent) { requester->Message(Chat::Default, "Target interrogation flag: %s", (GetInterrogateInvState() ? "TRUE" : "FALSE")); @@ -3431,7 +3429,7 @@ bool Client::InterrogateInventory(Client* requester, bool log, bool silent, bool void Client::InterrogateInventory_(bool errorcheck, Client* requester, int16 head, int16 index, const EQEmu::ItemInstance* inst, const EQEmu::ItemInstance* parent, bool log, bool silent, bool &error, int depth) { if (depth >= 10) { - Log(Logs::Detail, Logs::None, "[CLIENT] Client::InterrogateInventory_() - Recursion count has exceeded the maximum allowable (You have a REALLY BIG PROBLEM!!)"); + LogDebug("[CLIENT] Client::InterrogateInventory_() - Recursion count has exceeded the maximum allowable (You have a REALLY BIG PROBLEM!!)"); return; } @@ -3462,7 +3460,7 @@ void Client::InterrogateInventory_(bool errorcheck, Client* requester, int16 hea else { e = ""; } if (log) { - Log(Logs::General, Logs::Error, "Head: %i, Depth: %i, Instance: %s, Parent: %s%s", + LogError("Head: [{}], Depth: [{}], Instance: [{}], Parent: [{}][{}]", head, depth, i.c_str(), p.c_str(), e.c_str()); } if (!silent) { diff --git a/zone/loottables.cpp b/zone/loottables.cpp index e7c9624f1..3b5ae520f 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -240,7 +240,7 @@ void NPC::AddLootDrop(const EQEmu::ItemData *item2, ItemList* itemlist, int16 ch auto item = new ServerLootItem_Struct; #if EQDEBUG>=11 - Log(Logs::General, Logs::None, "Adding drop to npc: %s, Item: %i", GetName(), item2->ID); + LogDebug("Adding drop to npc: [{}], Item: [{}]", GetName(), item2->ID); #endif EQApplicationPacket* outapp = nullptr; @@ -352,7 +352,7 @@ void NPC::AddLootDrop(const EQEmu::ItemData *item2, ItemList* itemlist, int16 ch } else if (foundslot == EQEmu::invslot::slotSecondary && (GetOwner() != nullptr || (CanThisClassDualWield() && zone->random.Roll(NPC_DW_CHANCE)) || (item2->Damage==0)) && - (item2->IsType1HWeapon() || item2->ItemType == EQEmu::item::ItemTypeShield)) + (item2->IsType1HWeapon() || item2->ItemType == EQEmu::item::ItemTypeShield || item2->ItemType == EQEmu::item::ItemTypeLight)) { if (item2->Proc.Effect!=0) CastToMob()->AddProcToWeapon(item2->Proc.Effect, true); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 4dddd887d..253a7a4cc 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -1368,14 +1368,14 @@ double lua_clock() { } void lua_debug(std::string message) { - Log(Logs::General, Logs::QuestDebug, message); + Log(Logs::General, Logs::QuestDebug, message.c_str()); } void lua_debug(std::string message, int level) { if (level < Logs::General || level > Logs::Detail) return; - Log(static_cast(level), Logs::QuestDebug, message); + Log(static_cast(level), Logs::QuestDebug, message.c_str()); } void lua_update_zone_header(std::string type, std::string value) { @@ -2327,17 +2327,17 @@ luabind::scope lua_register_rules_const() { return luabind::class_("Rule") .enum_("constants") [ -#define RULE_INT(cat, rule, default_value) \ +#define RULE_INT(cat, rule, default_value, notes) \ luabind::value(#rule, RuleManager::Int__##rule), #include "../common/ruletypes.h" luabind::value("_IntRuleCount", RuleManager::_IntRuleCount), #undef RULE_INT -#define RULE_REAL(cat, rule, default_value) \ +#define RULE_REAL(cat, rule, default_value, notes) \ luabind::value(#rule, RuleManager::Real__##rule), #include "../common/ruletypes.h" luabind::value("_RealRuleCount", RuleManager::_RealRuleCount), #undef RULE_REAL -#define RULE_BOOL(cat, rule, default_value) \ +#define RULE_BOOL(cat, rule, default_value, notes) \ luabind::value(#rule, RuleManager::Bool__##rule), #include "../common/ruletypes.h" luabind::value("_BoolRuleCount", RuleManager::_BoolRuleCount) diff --git a/zone/lua_stat_bonuses.cpp b/zone/lua_stat_bonuses.cpp index 0be88fd07..d311cf123 100644 --- a/zone/lua_stat_bonuses.cpp +++ b/zone/lua_stat_bonuses.cpp @@ -335,6 +335,11 @@ int Lua_StatBonuses::Geteffective_casting_level() const { return self->effective_casting_level; } +int Lua_StatBonuses::Getadjusted_casting_skill() const { + Lua_Safe_Call_Int(); + return self->adjusted_casting_skill; +} + int Lua_StatBonuses::Getreflect_chance() const { Lua_Safe_Call_Int(); return self->reflect_chance; @@ -1349,6 +1354,7 @@ luabind::scope lua_register_stat_bonuses() { .def("skillmod", &Lua_StatBonuses::Getskillmod) .def("skillmodmax", &Lua_StatBonuses::Getskillmodmax) .def("effective_casting_level", &Lua_StatBonuses::Geteffective_casting_level) + .def("adjusted_casting_skill", &Lua_StatBonuses::Getadjusted_casting_skill) .def("reflect_chance", &Lua_StatBonuses::Getreflect_chance) .def("singingMod", &Lua_StatBonuses::GetsingingMod) .def("Amplification", &Lua_StatBonuses::GetAmplification) diff --git a/zone/lua_stat_bonuses.h b/zone/lua_stat_bonuses.h index 9ad04d681..c65c589a4 100644 --- a/zone/lua_stat_bonuses.h +++ b/zone/lua_stat_bonuses.h @@ -91,6 +91,7 @@ public: int32 Getskillmod(int idx) const; int32 Getskillmodmax(int idx) const; int Geteffective_casting_level() const; + int Getadjusted_casting_skill() const; int Getreflect_chance() const; uint32 GetsingingMod() const; uint32 GetAmplification() const; diff --git a/zone/map.cpp b/zone/map.cpp index a63ea6672..a0e3e6400 100644 --- a/zone/map.cpp +++ b/zone/map.cpp @@ -223,7 +223,7 @@ Map *Map::LoadMapFile(std::string file) { filename += file; filename += ".map"; - Log(Logs::General, Logs::Status, "Attempting to load Map File '%s'", filename.c_str()); + LogInfo("Attempting to load Map File [{}]", filename.c_str()); auto m = new Map(); if (m->Load(filename)) { @@ -238,7 +238,7 @@ Map *Map::LoadMapFile(std::string file) { bool Map::Load(std::string filename, bool force_mmf_overwrite) { if (LoadMMF(filename, force_mmf_overwrite)) { - Log(Logs::General, Logs::Status, "Loaded .MMF Map File in place of '%s'", filename.c_str()); + LogInfo("Loaded .MMF Map File in place of [{}]", filename.c_str()); return true; } #else @@ -255,7 +255,7 @@ bool Map::Load(std::string filename) } if(version == 0x01000000) { - Log(Logs::General, Logs::Status, "Loaded V1 Map File '%s'", filename.c_str()); + LogInfo("Loaded V1 Map File [{}]", filename.c_str()); bool v = LoadV1(f); fclose(f); @@ -266,7 +266,7 @@ bool Map::Load(std::string filename) return v; } else if(version == 0x02000000) { - Log(Logs::General, Logs::Status, "Loaded V2 Map File '%s'", filename.c_str()); + LogInfo("Loaded V2 Map File [{}]", filename.c_str()); bool v = LoadV2(f); fclose(f); @@ -943,53 +943,53 @@ bool Map::LoadMMF(const std::string& map_file_name, bool force_mmf_overwrite) std::string mmf_file_name = map_file_name; strip_map_extension(mmf_file_name); if (!add_mmf_extension(mmf_file_name)) { - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s'", mmf_file_name.c_str()); + LogInfo("Failed to load Map MMF file: [{}]", mmf_file_name.c_str()); return false; } FILE *f = fopen(mmf_file_name.c_str(), "rb"); if (!f) { - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - could not open file", mmf_file_name.c_str()); + LogInfo("Failed to load Map MMF file: [{}] - could not open file", mmf_file_name.c_str()); return false; } uint32 file_version; if (fread(&file_version, sizeof(uint32), 1, f) != 1) { fclose(f); - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@file_version", mmf_file_name.c_str()); + LogInfo("Failed to load Map MMF file: [{}] - f@file_version", mmf_file_name.c_str()); return false; } uint32 rm_buffer_size; if (fread(&rm_buffer_size, sizeof(uint32), 1, f) != 1) { fclose(f); - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@rm_buffer_size", mmf_file_name.c_str()); + LogInfo("Failed to load Map MMF file: [{}] - f@rm_buffer_size", mmf_file_name.c_str()); return false; } uint32 rm_buffer_crc32; if (fread(&rm_buffer_crc32, sizeof(uint32), 1, f) != 1) { fclose(f); - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@rm_buffer_crc32", mmf_file_name.c_str()); + LogInfo("Failed to load Map MMF file: [{}] - f@rm_buffer_crc32", mmf_file_name.c_str()); return false; } if (rm_buffer_crc32 != /*crc32_check*/ 0) { fclose(f); - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - bad rm_buffer checksum", mmf_file_name.c_str()); + LogInfo("Failed to load Map MMF file: [{}] - bad rm_buffer checksum", mmf_file_name.c_str()); return false; } uint32 mmf_buffer_size; if (fread(&mmf_buffer_size, sizeof(uint32), 1, f) != 1) { fclose(f); - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@mmf_buffer_size", mmf_file_name.c_str()); + LogInfo("Failed to load Map MMF file: [{}] - f@mmf_buffer_size", mmf_file_name.c_str()); return false; } std::vector mmf_buffer(mmf_buffer_size); if (fread(mmf_buffer.data(), mmf_buffer_size, 1, f) != 1) { fclose(f); - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - f@mmf_buffer", mmf_file_name.c_str()); + LogInfo("Failed to load Map MMF file: [{}] - f@mmf_buffer", mmf_file_name.c_str()); return false; } @@ -1016,7 +1016,7 @@ bool Map::LoadMMF(const std::string& map_file_name, bool force_mmf_overwrite) if (!imp->rm) { delete imp; imp = nullptr; - Log(Logs::General, Logs::Zone_Server, "Failed to load Map MMF file: '%s' - null RaycastMesh", mmf_file_name.c_str()); + LogInfo("Failed to load Map MMF file: [{}] - null RaycastMesh", mmf_file_name.c_str()); return false; } @@ -1026,14 +1026,14 @@ bool Map::LoadMMF(const std::string& map_file_name, bool force_mmf_overwrite) bool Map::SaveMMF(const std::string& map_file_name, bool force_mmf_overwrite) { if (!imp || !imp->rm) { - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file - No implementation (map_file_name: '%s')", map_file_name.c_str()); + LogInfo("Failed to save Map MMF file - No implementation (map_file_name: [{}])", map_file_name.c_str()); return false; } std::string mmf_file_name = map_file_name; strip_map_extension(mmf_file_name); if (!add_mmf_extension(mmf_file_name)) { - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s'", mmf_file_name.c_str()); + LogInfo("Failed to save Map MMF file: [{}]", mmf_file_name.c_str()); return false; } @@ -1047,7 +1047,7 @@ bool Map::SaveMMF(const std::string& map_file_name, bool force_mmf_overwrite) std::vector rm_buffer; // size set in MyRaycastMesh::serialize() serializeRaycastMesh(imp->rm, rm_buffer); if (rm_buffer.empty()) { - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - empty RaycastMesh buffer", mmf_file_name.c_str()); + LogInfo("Failed to save Map MMF file: [{}] - empty RaycastMesh buffer", mmf_file_name.c_str()); return false; } @@ -1058,13 +1058,13 @@ bool Map::SaveMMF(const std::string& map_file_name, bool force_mmf_overwrite) mmf_buffer_size = DeflateData(rm_buffer.data(), rm_buffer.size(), mmf_buffer.data(), mmf_buffer.size()); if (!mmf_buffer_size) { - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - null MMF buffer size", mmf_file_name.c_str()); + LogInfo("Failed to save Map MMF file: [{}] - null MMF buffer size", mmf_file_name.c_str()); return false; } f = fopen(mmf_file_name.c_str(), "wb"); if (!f) { - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - could not open file", mmf_file_name.c_str()); + LogInfo("Failed to save Map MMF file: [{}] - could not open file", mmf_file_name.c_str()); return false; } @@ -1072,14 +1072,14 @@ bool Map::SaveMMF(const std::string& map_file_name, bool force_mmf_overwrite) if (fwrite(&file_version, sizeof(uint32), 1, f) != 1) { fclose(f); std::remove(mmf_file_name.c_str()); - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@file_version", mmf_file_name.c_str()); + LogInfo("Failed to save Map MMF file: [{}] - f@file_version", mmf_file_name.c_str()); return false; } if (fwrite(&rm_buffer_size, sizeof(uint32), 1, f) != 1) { fclose(f); std::remove(mmf_file_name.c_str()); - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@rm_buffer_size", mmf_file_name.c_str()); + LogInfo("Failed to save Map MMF file: [{}] - f@rm_buffer_size", mmf_file_name.c_str()); return false; } @@ -1087,21 +1087,21 @@ bool Map::SaveMMF(const std::string& map_file_name, bool force_mmf_overwrite) if (fwrite(&rm_buffer_crc32, sizeof(uint32), 1, f) != 1) { fclose(f); std::remove(mmf_file_name.c_str()); - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@rm_buffer_crc32", mmf_file_name.c_str()); + LogInfo("Failed to save Map MMF file: [{}] - f@rm_buffer_crc32", mmf_file_name.c_str()); return false; } if (fwrite(&mmf_buffer_size, sizeof(uint32), 1, f) != 1) { fclose(f); std::remove(mmf_file_name.c_str()); - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@mmf_buffer_size", mmf_file_name.c_str()); + LogInfo("Failed to save Map MMF file: [{}] - f@mmf_buffer_size", mmf_file_name.c_str()); return false; } if (fwrite(mmf_buffer.data(), mmf_buffer_size, 1, f) != 1) { fclose(f); std::remove(mmf_file_name.c_str()); - Log(Logs::General, Logs::Zone_Server, "Failed to save Map MMF file: '%s' - f@mmf_buffer", mmf_file_name.c_str()); + LogInfo("Failed to save Map MMF file: [{}] - f@mmf_buffer", mmf_file_name.c_str()); return false; } diff --git a/zone/merc.cpp b/zone/merc.cpp index c48c3fb66..365e442ee 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -891,7 +891,7 @@ int32 Merc::CalcMaxMana() break; } default: { - Log(Logs::General, Logs::None, "Invalid Class '%c' in CalcMaxMana", GetCasterClass()); + LogDebug("Invalid Class [{}] in CalcMaxMana", GetCasterClass()); max_mana = 0; break; } @@ -912,7 +912,7 @@ int32 Merc::CalcMaxMana() } #if EQDEBUG >= 11 - Log(Logs::General, Logs::None, "Merc::CalcMaxMana() called for %s - returning %d", GetName(), max_mana); + LogDebug("Merc::CalcMaxMana() called for [{}] - returning [{}]", GetName(), max_mana); #endif return max_mana; } @@ -1688,7 +1688,7 @@ void Merc::AI_Process() { if (AI_movement_timer->Check()) { if(!IsRooted()) { - Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", GetTarget()->GetCleanName()); + LogAI("Pursuing [{}] while engaged", GetTarget()->GetCleanName()); RunTo(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); return; } @@ -1807,7 +1807,7 @@ bool Merc::AI_EngagedCastCheck() { { AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - Log(Logs::Detail, Logs::AI, "Merc Engaged autocast check triggered"); + LogAI("Merc Engaged autocast check triggered"); int8 mercClass = GetClass(); @@ -1862,7 +1862,7 @@ bool Merc::AI_IdleCastCheck() { if (AIautocastspell_timer->Check(false)) { #if MercAI_DEBUG_Spells >= 25 - Log(Logs::Detail, Logs::AI, "Merc Non-Engaged autocast check triggered: %s", this->GetCleanName()); + LogAI("Merc Non-Engaged autocast check triggered: [{}]", this->GetCleanName()); #endif AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. @@ -1914,7 +1914,7 @@ bool EntityList::Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, // according to Rogean, Live NPCs will just cast through walls/floors, no problem.. // // This check was put in to address an idle-mob CPU issue - Log(Logs::General, Logs::Error, "Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); + LogError("Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); return(false); } @@ -4494,7 +4494,7 @@ bool Merc::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, boo { if (!other) { SetTarget(nullptr); - Log(Logs::General, Logs::Error, "A null Mob object was passed to Merc::Attack() for evaluation!"); + LogError("A null Mob object was passed to Merc::Attack() for evaluation!"); return false; } @@ -6181,7 +6181,7 @@ void NPC::LoadMercTypes() { auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error in NPC::LoadMercTypes()"); + LogError("Error in NPC::LoadMercTypes()"); return; } @@ -6214,7 +6214,7 @@ void NPC::LoadMercs() { if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error in NPC::LoadMercTypes()"); + LogError("Error in NPC::LoadMercTypes()"); return; } diff --git a/zone/mob.cpp b/zone/mob.cpp index cf26e4177..4e6b6bf5c 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1322,7 +1322,7 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal * This is to prevent excessive packet sending under trains/fast combat */ if (this->CastToClient()->hp_self_update_throttle_timer.Check() || force_update_all) { - Log(Logs::General, Logs::HP_Update, + Log(Logs::General, Logs::HPUpdate, "Mob::SendHPUpdate :: Update HP of self (%s) HP: %i last: %i skip_self: %s", this->GetCleanName(), current_hp, @@ -1356,14 +1356,14 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal int8 current_hp_percent = static_cast(max_hp == 0 ? 0 : static_cast(current_hp * 100 / max_hp)); Log(Logs::General, - Logs::HP_Update, + Logs::HPUpdate, "Mob::SendHPUpdate :: SendHPUpdate %s HP is %i last %i", this->GetCleanName(), current_hp_percent, last_hp_percent); if (current_hp_percent == last_hp_percent && !force_update_all) { - Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: Same HP - skipping update"); + Log(Logs::General, Logs::HPUpdate, "Mob::SendHPUpdate :: Same HP - skipping update"); ResetHPUpdateTimer(); return; } @@ -1373,7 +1373,7 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal this->CastToClient()->SendHPUpdateMarquee(); } - Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: HP Changed - Send update"); + Log(Logs::General, Logs::HPUpdate, "Mob::SendHPUpdate :: HP Changed - Send update"); last_hp_percent = current_hp_percent; } @@ -1840,9 +1840,7 @@ void Mob::SendIllusionPacket( /* Refresh armor and tints after send illusion packet */ this->SendArmorAppearance(); - Log(Logs::Detail, - Logs::Spells, - "Illusion: Race = %i, Gender = %i, Texture = %i, HelmTexture = %i, HairColor = %i, BeardColor = %i, EyeColor1 = %i, EyeColor2 = %i, HairStyle = %i, Face = %i, DrakkinHeritage = %i, DrakkinTattoo = %i, DrakkinDetails = %i, Size = %f", + LogSpells("Illusion: Race = [{}], Gender = [{}], Texture = [{}], HelmTexture = [{}], HairColor = [{}], BeardColor = [{}], EyeColor1 = [{}], EyeColor2 = [{}], HairStyle = [{}], Face = [{}], DrakkinHeritage = [{}], DrakkinTattoo = [{}], DrakkinDetails = [{}], Size = [{}]", race, gender, new_texture, @@ -3063,7 +3061,7 @@ void Mob::ExecWeaponProc(const EQEmu::ItemInstance *inst, uint16 spell_id, Mob * if(!IsValidSpell(spell_id)) { // Check for a valid spell otherwise it will crash through the function if(IsClient()){ Message(0, "Invalid spell proc %u", spell_id); - Log(Logs::Detail, Logs::Spells, "Player %s, Weapon Procced invalid spell %u", this->GetName(), spell_id); + LogSpells("Player [{}], Weapon Procced invalid spell [{}]", this->GetName(), spell_id); } return; } @@ -4629,7 +4627,7 @@ void Mob::MeleeLifeTap(int32 damage) { if(lifetap_amt && damage > 0){ lifetap_amt = damage * lifetap_amt / 100; - Log(Logs::Detail, Logs::Combat, "Melee lifetap healing for %d damage.", damage); + LogCombat("Melee lifetap healing for [{}] damage", damage); if (lifetap_amt > 0) HealDamage(lifetap_amt); //Heal self for modified damage amount. diff --git a/zone/mob.h b/zone/mob.h index 1a19eb9fb..4f07e10e6 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1467,8 +1467,8 @@ protected: eStandingPetOrder pStandingPetOrder; uint32 minLastFightingDelayMoving; uint32 maxLastFightingDelayMoving; - float pAggroRange; - float pAssistRange; + float pAggroRange = 0; + float pAssistRange = 0; std::unique_ptr AI_think_timer; std::unique_ptr AI_movement_timer; std::unique_ptr AI_target_check_timer; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index de7cc68dd..4546b5c45 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -127,7 +127,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates ) { #if MobAI_DEBUG_Spells >= 21 - Log(Logs::Detail, Logs::AI, "Mob::AICastSpell: Casting: spellid=%u, tar=%s, dist2[%f]<=%f, mana_cost[%i]<=%i, cancast[%u]<=%u, type=%u", + LogAI("Mob::AICastSpell: Casting: spellid=[{}], tar=[{}], dist2[[{}]]<=[{}], mana_cost[[{}]]<=[{}], cancast[[{}]]<=[{}], type=[{}]", AIspells[i].spellid, tar->GetName(), dist2, (spells[AIspells[i].spellid].range * spells[AIspells[i].spellid].range), mana_cost, GetMana(), AIspells[i].time_cancast, Timer::GetCurrentTime(), AIspells[i].type); #endif @@ -360,7 +360,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates } #if MobAI_DEBUG_Spells >= 21 else { - Log(Logs::Detail, Logs::AI, "Mob::AICastSpell: NotCasting: spellid=%u, tar=%s, dist2[%f]<=%f, mana_cost[%i]<=%i, cancast[%u]<=%u, type=%u", + LogAI("Mob::AICastSpell: NotCasting: spellid=[{}], tar=[{}], dist2[[{}]]<=[{}], mana_cost[[{}]]<=[{}], cancast[[{}]]<=[{}], type=[{}]", AIspells[i].spellid, tar->GetName(), dist2, (spells[AIspells[i].spellid].range * spells[AIspells[i].spellid].range), mana_cost, GetMana(), AIspells[i].time_cancast, Timer::GetCurrentTime(), AIspells[i].type); } #endif @@ -371,7 +371,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore) { #if MobAI_DEBUG_Spells >= 1 - Log(Logs::Detail, Logs::AI, "Mob::AIDoSpellCast: spellid = %u, tar = %s, mana = %i, Name: '%s'", AIspells[i].spellid, tar->GetName(), mana_cost, spells[AIspells[i].spellid].name); + LogAI("Mob::AIDoSpellCast: spellid = [{}], tar = [{}], mana = [{}], Name: [{}]", AIspells[i].spellid, tar->GetName(), mana_cost, spells[AIspells[i].spellid].name); #endif casting_spell_AIindex = i; @@ -385,7 +385,7 @@ bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float // according to Rogean, Live NPCs will just cast through walls/floors, no problem.. // // This check was put in to address an idle-mob CPU issue - Log(Logs::General, Logs::Error, "Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); + LogError("Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); return(false); } @@ -1356,7 +1356,7 @@ void Mob::AI_Process() { } else if (AI_movement_timer->Check() && target) { if (!IsRooted()) { - Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", target->GetName()); + LogAI("Pursuing [{}] while engaged", target->GetName()); RunTo(target->GetX(), target->GetY(), target->GetZ()); } @@ -1721,9 +1721,7 @@ void NPC::AI_DoMovement() { else if (!(AI_walking_timer->Enabled())) { // currently moving bool doMove = true; if(IsPositionEqual(glm::vec2(m_CurrentWayPoint.x, m_CurrentWayPoint.y), glm::vec2(GetX(), GetY()))) { - Log(Logs::Detail, - Logs::AI, - "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", + LogAI("We have reached waypoint [{}] ({},{},{}) on grid [{}]", cur_wp, GetX(), GetY(), @@ -1775,7 +1773,7 @@ void NPC::AI_DoMovement() { else if (gridno < 0) { // this mob is under quest control if (pause_timer_complete == true) { // time to pause has ended SetGrid(0 - GetGrid()); // revert to AI control - Log(Logs::Detail, Logs::Pathing, "Quest pathing is finished. Resuming on grid %d", GetGrid()); + LogPathing("Quest pathing is finished. Resuming on grid [{}]", GetGrid()); SetAppearance(eaStanding, false); @@ -1790,12 +1788,7 @@ void NPC::AI_DoMovement() { if (at_gp) { if (moved) { - Log(Logs::Detail, - Logs::AI, - "Reached guard point (%.3f,%.3f,%.3f)", - m_GuardPoint.x, - m_GuardPoint.y, - m_GuardPoint.z); + LogAI("Reached guard point ({},{},{})", m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z); ClearFeignMemory(); moved = false; @@ -1843,10 +1836,10 @@ void NPC::AI_SetupNextWaypoint() { } else { pause_timer_complete = false; - Log(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp); + LogPathing("We are departing waypoint [{}]", cur_wp); //if we were under quest control (with no grid), we are done now.. if (cur_wp == EQEmu::WaypointStatus::QuestControlNoGrid) { - Log(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode."); + LogPathing("Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode"); roamer = false; cur_wp = 0; } @@ -1983,7 +1976,7 @@ bool NPC::AI_EngagedCastCheck() { if (AIautocastspell_timer->Check(false)) { AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - Log(Logs::Detail, Logs::AI, "Engaged autocast check triggered. Trying to cast healing spells then maybe offensive spells."); + LogAI("Engaged autocast check triggered. Trying to cast healing spells then maybe offensive spells"); // first try innate (spam) spells if(!AICastSpell(GetTarget(), 0, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root, true)) { @@ -2012,7 +2005,7 @@ bool NPC::AI_PursueCastCheck() { if (AIautocastspell_timer->Check(false)) { AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - Log(Logs::Detail, Logs::AI, "Engaged (pursuing) autocast check triggered. Trying to cast offensive spells."); + LogAI("Engaged (pursuing) autocast check triggered. Trying to cast offensive spells"); // checking innate (spam) spells first if(!AICastSpell(GetTarget(), AISpellVar.pursue_detrimental_chance, SpellType_Root | SpellType_Nuke | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff, true)) { if(!AICastSpell(GetTarget(), AISpellVar.pursue_detrimental_chance, SpellType_Root | SpellType_Nuke | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff)) { @@ -2034,7 +2027,7 @@ bool NPC::AI_IdleCastCheck() { //last duration it was set to... try to put up a more reasonable timer... AIautocastspell_timer->Start(RandomTimer(AISpellVar.idle_no_sp_recast_min, AISpellVar.idle_no_sp_recast_max), false); - Log(Logs::Moderate, Logs::Spells, "Triggering AI_IdleCastCheck :: Mob %s - Min : %u Max : %u", this->GetCleanName(), AISpellVar.idle_no_sp_recast_min, AISpellVar.idle_no_sp_recast_max); + LogSpells("Triggering AI_IdleCastCheck :: Mob [{}] - Min : [{}] Max : [{}]", this->GetCleanName(), AISpellVar.idle_no_sp_recast_min, AISpellVar.idle_no_sp_recast_max); } //else, spell casting finishing will reset the timer. } //else, spell casting finishing will reset the timer. @@ -2507,21 +2500,21 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) { else { debug_msg.append(" (not found)"); } - Log(Logs::Detail, Logs::AI, "%s", debug_msg.c_str()); + LogAI("[{}]", debug_msg.c_str()); #ifdef MobAI_DEBUG_Spells >= 25 if (parentlist) { for (const auto &iter : parentlist->entries) { - Log(Logs::Detail, Logs::AI, "(%i) %s", iter.spellid, spells[iter.spellid].name); + LogAI("([{}]) [{}]", iter.spellid, spells[iter.spellid].name); } } - Log(Logs::Detail, Logs::AI, "fin (parent list)"); + LogAI("fin (parent list)"); if (spell_list) { for (const auto &iter : spell_list->entries) { - Log(Logs::Detail, Logs::AI, "(%i) %s", iter.spellid, spells[iter.spellid].name); + LogAI("([{}]) [{}]", iter.spellid, spells[iter.spellid].name); } } - Log(Logs::Detail, Logs::AI, "fin (spell list)"); + LogAI("fin (spell list)"); #endif #endif @@ -2682,7 +2675,7 @@ bool NPC::AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID) { else { debug_msg.append(" (not found)"); } - Log(Logs::Detail, Logs::AI, "%s", debug_msg.c_str()); + LogAI("[{}]", debug_msg.c_str()); #endif if (parentlist) { diff --git a/zone/net.cpp b/zone/net.cpp index 4b4859e5e..0ce901359 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -42,7 +42,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/eqemu_exception.h" #include "../common/spdat.h" #include "../common/eqemu_logsys.h" -#include "../common/eqemu_logsys_fmt.h" #include "api_service.h" #include "zone_config.h" @@ -146,9 +145,9 @@ int main(int argc, char** argv) { QServ = new QueryServ; - Log(Logs::General, Logs::Zone_Server, "Loading server configuration.."); + LogInfo("Loading server configuration.."); if (!ZoneConfig::LoadConfig()) { - Log(Logs::General, Logs::Error, "Loading server configuration failed."); + LogError("Loading server configuration failed"); return 1; } Config = ZoneConfig::get(); @@ -226,14 +225,14 @@ int main(int argc, char** argv) { worldserver.SetLauncherName("NONE"); } - Log(Logs::General, Logs::Zone_Server, "Connecting to MySQL..."); + LogInfo("Connecting to MySQL... "); if (!database.Connect( Config->DatabaseHost.c_str(), Config->DatabaseUsername.c_str(), Config->DatabasePassword.c_str(), Config->DatabaseDB.c_str(), Config->DatabasePort)) { - Log(Logs::General, Logs::Error, "Cannot continue without a database connection."); + LogError("Cannot continue without a database connection"); return 1; } @@ -256,135 +255,135 @@ int main(int argc, char** argv) { _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif - Log(Logs::General, Logs::Zone_Server, "CURRENT_VERSION: %s", CURRENT_VERSION); + LogInfo("CURRENT_VERSION: {}", CURRENT_VERSION); /* * Setup nice signal handlers */ if (signal(SIGINT, CatchSignal) == SIG_ERR) { - Log(Logs::General, Logs::Error, "Could not set signal handler"); + LogError("Could not set signal handler"); return 1; } if (signal(SIGTERM, CatchSignal) == SIG_ERR) { - Log(Logs::General, Logs::Error, "Could not set signal handler"); + LogError("Could not set signal handler"); return 1; } #ifndef WIN32 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { - Log(Logs::General, Logs::Error, "Could not set signal handler"); + LogError("Could not set signal handler"); return 1; } #endif - Log(Logs::General, Logs::Zone_Server, "Mapping Incoming Opcodes"); + LogInfo("Mapping Incoming Opcodes"); MapOpcodes(); - Log(Logs::General, Logs::Zone_Server, "Loading Variables"); + LogInfo("Loading Variables"); database.LoadVariables(); std::string hotfix_name; if (database.GetVariable("hotfix_name", hotfix_name)) { if (!hotfix_name.empty()) { - Log(Logs::General, Logs::Zone_Server, "Current hotfix in use: '%s'", hotfix_name.c_str()); + LogInfo("Current hotfix in use: [{}]", hotfix_name.c_str()); } } - Log(Logs::General, Logs::Zone_Server, "Loading zone names"); + LogInfo("Loading zone names"); database.LoadZoneNames(); - Log(Logs::General, Logs::Zone_Server, "Loading items"); + LogInfo("Loading items"); if (!database.LoadItems(hotfix_name)) { - Log(Logs::General, Logs::Error, "Loading items FAILED!"); - Log(Logs::General, Logs::Error, "Failed. But ignoring error and going on..."); + LogError("Loading items failed!"); + LogError("Failed. But ignoring error and going on.."); } - Log(Logs::General, Logs::Zone_Server, "Loading npc faction lists"); + LogInfo("Loading npc faction lists"); if (!database.LoadNPCFactionLists(hotfix_name)) { - Log(Logs::General, Logs::Error, "Loading npcs faction lists FAILED!"); + LogError("Loading npcs faction lists failed!"); return 1; } - Log(Logs::General, Logs::Zone_Server, "Loading loot tables"); + LogInfo("Loading loot tables"); if (!database.LoadLoot(hotfix_name)) { - Log(Logs::General, Logs::Error, "Loading loot FAILED!"); + LogError("Loading loot failed!"); return 1; } - Log(Logs::General, Logs::Zone_Server, "Loading skill caps"); + LogInfo("Loading skill caps"); if (!database.LoadSkillCaps(std::string(hotfix_name))) { - Log(Logs::General, Logs::Error, "Loading skill caps FAILED!"); + LogError("Loading skill caps failed!"); return 1; } - Log(Logs::General, Logs::Zone_Server, "Loading spells"); + LogInfo("Loading spells"); if (!database.LoadSpells(hotfix_name, &SPDAT_RECORDS, &spells)) { - Log(Logs::General, Logs::Error, "Loading spells FAILED!"); + LogError("Loading spells failed!"); return 1; } - Log(Logs::General, Logs::Zone_Server, "Loading base data"); + LogInfo("Loading base data"); if (!database.LoadBaseData(hotfix_name)) { - Log(Logs::General, Logs::Error, "Loading base data FAILED!"); + LogError("Loading base data failed!"); return 1; } - Log(Logs::General, Logs::Zone_Server, "Loading guilds"); + LogInfo("Loading guilds"); guild_mgr.LoadGuilds(); - Log(Logs::General, Logs::Zone_Server, "Loading factions"); + LogInfo("Loading factions"); database.LoadFactionData(); - Log(Logs::General, Logs::Zone_Server, "Loading titles"); + LogInfo("Loading titles"); title_manager.LoadTitles(); - Log(Logs::General, Logs::Zone_Server, "Loading tributes"); + LogInfo("Loading tributes"); database.LoadTributes(); - Log(Logs::General, Logs::Zone_Server, "Loading corpse timers"); + LogInfo("Loading corpse timers"); database.GetDecayTimes(npcCorpseDecayTimes); - Log(Logs::General, Logs::Zone_Server, "Loading profanity list"); + LogInfo("Loading profanity list"); if (!EQEmu::ProfanityManager::LoadProfanityList(&database)) - Log(Logs::General, Logs::Error, "Loading profanity list FAILED!"); + LogError("Loading profanity list failed!"); - Log(Logs::General, Logs::Zone_Server, "Loading commands"); + LogInfo("Loading commands"); int retval = command_init(); if (retval<0) - Log(Logs::General, Logs::Error, "Command loading FAILED"); + LogError("Command loading failed"); else - Log(Logs::General, Logs::Zone_Server, "%d commands loaded", retval); + LogInfo("{} commands loaded", retval); //rules: { std::string tmp; if (database.GetVariable("RuleSet", tmp)) { - Log(Logs::General, Logs::Zone_Server, "Loading rule set '%s'", tmp.c_str()); + LogInfo("Loading rule set [{}]", tmp.c_str()); if (!RuleManager::Instance()->LoadRules(&database, tmp.c_str(), false)) { - Log(Logs::General, Logs::Error, "Failed to load ruleset '%s', falling back to defaults.", tmp.c_str()); + LogError("Failed to load ruleset [{}], falling back to defaults", tmp.c_str()); } } else { if (!RuleManager::Instance()->LoadRules(&database, "default", false)) { - Log(Logs::General, Logs::Zone_Server, "No rule set configured, using default rules"); + LogInfo("No rule set configured, using default rules"); } else { - Log(Logs::General, Logs::Zone_Server, "Loaded default rule set 'default'", tmp.c_str()); + LogInfo("Loaded default rule set 'default'"); } } EQEmu::InitializeDynamicLookups(); - Log(Logs::General, Logs::Zone_Server, "Initialized dynamic dictionary entries"); + LogInfo("Initialized dynamic dictionary entries"); } #ifdef BOTS - Log(Logs::General, Logs::Zone_Server, "Loading bot commands"); + LogInfo("Loading bot commands"); int botretval = bot_command_init(); if (botretval<0) - Log(Logs::General, Logs::Error, "Bot command loading FAILED"); + LogError("Bot command loading failed"); else - Log(Logs::General, Logs::Zone_Server, "%d bot commands loaded", botretval); + LogInfo("[{}] bot commands loaded", botretval); - Log(Logs::General, Logs::Zone_Server, "Loading bot spell casting chances"); + LogInfo("Loading bot spell casting chances"); if (!database.botdb.LoadBotSpellCastingChances()) - Log(Logs::General, Logs::Error, "Bot spell casting chances loading FAILED"); + LogError("Bot spell casting chances loading failed"); #endif if (RuleB(TaskSystem, EnableTaskSystem)) { @@ -408,7 +407,7 @@ int main(int argc, char** argv) { #endif //now we have our parser, load the quests - Log(Logs::General, Logs::Zone_Server, "Loading quests"); + LogInfo("Loading quests"); parse->ReloadQuests(); worldserver.Connect(); @@ -421,10 +420,10 @@ int main(int argc, char** argv) { #endif #endif if (!strlen(zone_name) || !strcmp(zone_name, ".")) { - Log(Logs::General, Logs::Zone_Server, "Entering sleep mode"); + LogInfo("Entering sleep mode"); } else if (!Zone::Bootup(database.GetZoneID(zone_name), instance_id, true)) { - Log(Logs::General, Logs::Error, "Zone Bootup failed :: Zone::Bootup"); + LogError("Zone Bootup failed :: Zone::Bootup"); zone = 0; } @@ -433,11 +432,11 @@ int main(int argc, char** argv) { RegisterAllPatches(stream_identifier); #ifndef WIN32 - Log(Logs::Detail, Logs::None, "Main thread running with thread id %d", pthread_self()); + LogDebug("Main thread running with thread id [{}]", pthread_self()); #endif - bool worldwasconnected = worldserver.Connected(); - bool eqsf_open = false; + bool worldwasconnected = worldserver.Connected(); + bool eqsf_open = false; bool websocker_server_opened = false; Timer quest_timers(100); @@ -460,16 +459,10 @@ int main(int argc, char** argv) { frame_prev = frame_now; /** - * Telnet server + * Websocket server */ if (!websocker_server_opened && Config->ZonePort != 0) { - Log( - Logs::General, - Logs::Zone_Server, - "Websocket Server listener started (%s:%u).", - Config->TelnetIP.c_str(), - Config->ZonePort - ); + LogInfo("Websocket Server listener started ([{}]:[{}])", Config->TelnetIP.c_str(), Config->ZonePort); ws_server.reset(new EQ::Net::WebsocketServer(Config->TelnetIP, Config->ZonePort)); RegisterApiService(ws_server); websocker_server_opened = true; @@ -479,7 +472,7 @@ int main(int argc, char** argv) { * EQStreamManager */ if (!eqsf_open && Config->ZonePort != 0) { - Log(Logs::General, Logs::Zone_Server, "Starting EQ Network server on port %d", Config->ZonePort); + LogInfo("Starting EQ Network server on port {}", Config->ZonePort); EQStreamManagerInterfaceOptions opts(Config->ZonePort, false, RuleB(Network, CompressZoneStream)); opts.daybreak_options.resend_delay_ms = RuleI(Network, ResendDelayBaseMS); @@ -492,7 +485,7 @@ int main(int argc, char** argv) { eqsm->OnNewConnection([&stream_identifier](std::shared_ptr stream) { stream_identifier.AddStream(stream); - LogF(Logs::Detail, Logs::World_Server, "New connection from IP {0}:{1}", stream->GetRemoteIP(), ntohs(stream->GetRemotePort())); + LogF(Logs::Detail, Logs::WorldServer, "New connection from IP {0}:{1}", stream->GetRemoteIP(), ntohs(stream->GetRemotePort())); }); } @@ -504,7 +497,7 @@ int main(int argc, char** argv) { //now that we know what patch they are running, start up their client object struct in_addr in; in.s_addr = eqsi->GetRemoteIP(); - Log(Logs::Detail, Logs::World_Server, "New client from %s:%d", inet_ntoa(in), ntohs(eqsi->GetRemotePort())); + LogInfo("New client from [{}]:[{}]", inet_ntoa(in), ntohs(eqsi->GetRemotePort())); auto client = new Client(eqsi); entity_list.AddClient(client); } @@ -550,8 +543,9 @@ int main(int argc, char** argv) { } } - if (quest_timers.Check()) + if (quest_timers.Check()) { quest_manager.Process(); + } } } @@ -608,14 +602,14 @@ int main(int argc, char** argv) { bot_command_deinit(); #endif safe_delete(parse); - Log(Logs::General, Logs::Zone_Server, "Proper zone shutdown complete."); + LogInfo("Proper zone shutdown complete."); LogSys.CloseFileLogs(); return 0; } void CatchSignal(int sig_num) { #ifdef _WINDOWS - Log(Logs::General, Logs::Zone_Server, "Recieved signal: %i", sig_num); + LogInfo("Recieved signal: [{}]", sig_num); #endif RunLoops = false; } @@ -624,7 +618,7 @@ void Shutdown() { Zone::Shutdown(true); RunLoops = false; - Log(Logs::General, Logs::Zone_Server, "Shutting down..."); + LogInfo("Shutting down..."); LogSys.CloseFileLogs(); } diff --git a/zone/npc.cpp b/zone/npc.cpp index 755664f49..8ccde1363 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -209,6 +209,7 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi default_accuracy_rating = npc_type_data->accuracy_rating; default_avoidance_rating = npc_type_data->avoidance_rating; default_atk = npc_type_data->ATK; + strn0cpy(default_special_abilities, npc_type_data->special_abilities, 512); // used for when getting charmed, if 0, doesn't swap charm_ac = npc_type_data->charm_ac; @@ -616,11 +617,11 @@ void NPC::QueryLoot(Client* to) int item_count = 0; for (auto cur = itemlist.begin(); cur != itemlist.end(); ++cur, ++item_count) { if (!(*cur)) { - Log(Logs::General, Logs::Error, "NPC::QueryLoot() - ItemList error, null item"); + LogError("NPC::QueryLoot() - ItemList error, null item"); continue; } if (!(*cur)->item_id || !database.GetItem((*cur)->item_id)) { - Log(Logs::General, Logs::Error, "NPC::QueryLoot() - Database error, invalid item"); + LogError("NPC::QueryLoot() - Database error, invalid item"); continue; } @@ -1902,7 +1903,7 @@ void Mob::NPCSpecialAttacks(const char* parse, int permtag, bool reset, bool rem { if(database.SetSpecialAttkFlag(this->GetNPCTypeID(), orig_parse)) { - Log(Logs::General, Logs::Normal, "NPCTypeID: %i flagged to '%s' for Special Attacks.\n",this->GetNPCTypeID(),orig_parse); + LogInfo("NPCTypeID: [{}] flagged to [{}] for Special Attacks.\n",this->GetNPCTypeID(),orig_parse); } } } @@ -2152,7 +2153,7 @@ void NPC::ModifyNPCStat(const char *identifier, const char *new_value) std::string variable_key = StringFormat("modify_stat_%s", id.c_str()); SetEntityVariable(variable_key.c_str(), new_value); - Log(Logs::Detail, Logs::NPCScaling, "NPC::ModifyNPCStat key: %s val: %s ", variable_key.c_str(), new_value); + LogNPCScaling("NPC::ModifyNPCStat key: [{}] val: [{}] ", variable_key.c_str(), new_value); if (id == "ac") { AC = atoi(val.c_str()); @@ -2838,39 +2839,61 @@ void NPC::DepopSwarmPets() } } -void NPC::ModifyStatsOnCharm(bool bRemoved) +void NPC::ModifyStatsOnCharm(bool is_charm_removed) { - if (bRemoved) { - if (charm_ac) + if (is_charm_removed) { + if (charm_ac) { AC = default_ac; - if (charm_attack_delay) + } + if (charm_attack_delay) { attack_delay = default_attack_delay; - if (charm_accuracy_rating) + } + if (charm_accuracy_rating) { accuracy_rating = default_accuracy_rating; - if (charm_avoidance_rating) + } + if (charm_avoidance_rating) { avoidance_rating = default_avoidance_rating; - if (charm_atk) + } + if (charm_atk) { ATK = default_atk; + } if (charm_min_dmg || charm_max_dmg) { base_damage = round((default_max_dmg - default_min_dmg) / 1.9); - min_damage = default_min_dmg - round(base_damage / 10.0); + min_damage = default_min_dmg - round(base_damage / 10.0); } - } else { - if (charm_ac) - AC = charm_ac; - if (charm_attack_delay) - attack_delay = charm_attack_delay; - if (charm_accuracy_rating) - accuracy_rating = charm_accuracy_rating; - if (charm_avoidance_rating) - avoidance_rating = charm_avoidance_rating; - if (charm_atk) - ATK = charm_atk; - if (charm_min_dmg || charm_max_dmg) { - base_damage = round((charm_max_dmg - charm_min_dmg) / 1.9); - min_damage = charm_min_dmg - round(base_damage / 10.0); + if (RuleB(Spells, CharmDisablesSpecialAbilities)) { + ProcessSpecialAbilities(default_special_abilities); } + + SetAttackTimer(); + CalcAC(); + + return; } + + if (charm_ac) { + AC = charm_ac; + } + if (charm_attack_delay) { + attack_delay = charm_attack_delay; + } + if (charm_accuracy_rating) { + accuracy_rating = charm_accuracy_rating; + } + if (charm_avoidance_rating) { + avoidance_rating = charm_avoidance_rating; + } + if (charm_atk) { + ATK = charm_atk; + } + if (charm_min_dmg || charm_max_dmg) { + base_damage = round((charm_max_dmg - charm_min_dmg) / 1.9); + min_damage = charm_min_dmg - round(base_damage / 10.0); + } + if (RuleB(Spells, CharmDisablesSpecialAbilities)) { + ClearSpecialAbilities(); + } + // the rest of the stats aren't cached, so lets just do these two instead of full CalcBonuses() SetAttackTimer(); CalcAC(); diff --git a/zone/npc.h b/zone/npc.h index 4c913f70d..5d7a7bd73 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -294,7 +294,7 @@ public: int32 GetNPCHPRegen() const { return hp_regen + itembonuses.HPRegen + spellbonuses.HPRegen; } inline const char* GetAmmoIDfile() const { return ammo_idfile; } - void ModifyStatsOnCharm(bool bRemoved); + void ModifyStatsOnCharm(bool is_charm_removed); //waypoint crap int GetMaxWp() const { return max_wp; } @@ -541,6 +541,7 @@ protected: int default_accuracy_rating; int default_avoidance_rating; int default_atk; + char default_special_abilities[512]; // when charmed, switch to these int charm_ac; diff --git a/zone/npc_scale_manager.cpp b/zone/npc_scale_manager.cpp index 5daabcb16..be9891e7f 100644 --- a/zone/npc_scale_manager.cpp +++ b/zone/npc_scale_manager.cpp @@ -24,10 +24,11 @@ /** * @param npc */ -void NpcScaleManager::ScaleNPC(NPC * npc) +void NpcScaleManager::ScaleNPC(NPC *npc) { - if (npc->IsSkipAutoScale()) + if (npc->IsSkipAutoScale()) { return; + } int8 npc_type = GetNPCScalingType(npc); int npc_level = npc->GetLevel(); @@ -36,7 +37,8 @@ void NpcScaleManager::ScaleNPC(NPC * npc) global_npc_scale scale_data = GetGlobalScaleDataForTypeLevel(npc_type, npc_level); if (!scale_data.level) { - Log(Logs::General, Logs::NPCScaling, "NPC: %s - scaling data not found for type: %i level: %i", + LogNPCScaling( + "NPC: [{}] - scaling data not found for type: [{}] level: [{}]", npc->GetCleanName(), npc_type, npc_level @@ -109,11 +111,7 @@ void NpcScaleManager::ScaleNPC(NPC * npc) int32 class_level_damage_mod = GetClassLevelDamageMod(npc->GetLevel(), npc->GetClass()); min_dmg = (min_dmg * class_level_damage_mod) / 220; - Log(Logs::Moderate, - Logs::NPCScaling, - "ClassLevelDamageMod::min_dmg base: %i calc: %i", - scale_data.min_dmg, - min_dmg); + LogNPCScaling("ClassLevelDamageMod::min_dmg base: [{}] calc: [{}]", scale_data.min_dmg, min_dmg); } npc->ModifyNPCStat("min_hit", std::to_string(min_dmg).c_str()); @@ -124,12 +122,7 @@ void NpcScaleManager::ScaleNPC(NPC * npc) int32 class_level_damage_mod = GetClassLevelDamageMod(npc->GetLevel(), npc->GetClass()); max_dmg = (scale_data.max_dmg * class_level_damage_mod) / 220; - Log(Logs::Moderate, - Logs::NPCScaling, - "ClassLevelDamageMod::max_dmg base: %i calc: %i", - scale_data.max_dmg, - max_dmg - ); + LogNPCScaling("ClassLevelDamageMod::max_dmg base: [{}] calc: [{}]", scale_data.max_dmg, max_dmg); } npc->ModifyNPCStat("max_hit", std::to_string(max_dmg).c_str()); @@ -160,14 +153,14 @@ void NpcScaleManager::ScaleNPC(NPC * npc) } } - Log(Logs::General, - Logs::NPCScaling, - "(%s) level: %i type: %i Auto: %s Setting: %s", + LogNPCScaling( + "([{}]) level: [{}] type: [{}] Auto: [{}] Setting: [{}]", npc->GetCleanName(), npc_level, npc_type, (is_auto_scaled ? "true" : "false"), - scale_log.c_str()); + scale_log.c_str() + ); } } @@ -249,7 +242,7 @@ bool NpcScaleManager::LoadScaleData() ); } - Log(Logs::General, Logs::NPCScaling, "Global Base Scaling Data Loaded..."); + LogNPCScaling("Global Base Scaling Data Loaded"); return true; } @@ -426,7 +419,7 @@ int8 NpcScaleManager::GetNPCScalingType(NPC *&npc) if (npc->IsRareSpawn() || npc_name.find('#') != std::string::npos || isupper(npc_name[0])) { return 1; } - + return 0; } @@ -489,10 +482,8 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically(NPC *&npc) global_npc_scale scale_data = GetGlobalScaleDataForTypeLevel(npc_type, npc_level); if (!scale_data.level) { - Log( - Logs::General, - Logs::NPCScaling, - "NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically NPC: %s - scaling data not found for type: %i level: %i", + LogNPCScaling( + "NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically NPC: [{}] - scaling data not found for type: [{}] level: [{}]", npc->GetCleanName(), npc_type, npc_level @@ -577,10 +568,8 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCDynamically(NPC *&npc) global_npc_scale scale_data = GetGlobalScaleDataForTypeLevel(npc_type, npc_level); if (!scale_data.level) { - Log( - Logs::General, - Logs::NPCScaling, - "NpcScaleManager::ApplyGlobalBaseScalingToNPCDynamically NPC: %s - scaling data not found for type: %i level: %i", + LogNPCScaling( + "NpcScaleManager::ApplyGlobalBaseScalingToNPCDynamically NPC: [{}] - scaling data not found for type: [{}] level: [{}]", npc->GetCleanName(), npc_type, npc_level diff --git a/zone/object.cpp b/zone/object.cpp index 241d3a3f1..4fbaef1c3 100644 --- a/zone/object.cpp +++ b/zone/object.cpp @@ -339,7 +339,7 @@ const EQEmu::ItemInstance* Object::GetItem(uint8 index) { void Object::PutItem(uint8 index, const EQEmu::ItemInstance* inst) { if (index > 9) { - Log(Logs::General, Logs::Error, "Object::PutItem: Invalid index specified (%i)", index); + LogError("Object::PutItem: Invalid index specified ([{}])", index); return; } @@ -443,6 +443,14 @@ bool Object::Process(){ if(m_ground_spawn && respawn_timer.Check()){ RandomSpawn(true); } + + if (user != nullptr && !entity_list.GetClientByCharID(user->CharacterID())) { + m_inuse = false; + last_user = user; + user->SetTradeskillObject(nullptr); + user = nullptr; + } + return true; } @@ -465,7 +473,7 @@ void Object::RandomSpawn(bool send_packet) { } } - Log(Logs::Detail, Logs::Zone_Server, "Object::RandomSpawn(%s): %d (%.2f, %.2f, %.2f)", m_data.object_name, m_inst->GetID(), m_data.x, m_data.y, m_data.z); + LogInfo("Object::RandomSpawn([{}]): [{}] ([{}], [{}], [{}])", m_data.object_name, m_inst->GetID(), m_data.x, m_data.y, m_data.z); respawn_timer.Disable(); @@ -554,7 +562,6 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) ClickObjectAction_Struct* coa = (ClickObjectAction_Struct*)outapp->pBuffer; //TODO: there is prolly a better way to do this. - m_inuse = true; coa->type = m_type; coa->unknown16 = 0x0a; @@ -576,12 +583,6 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) } } - if(sender->IsLooting()) - { - coa->open = 0x00; - user = sender; - } - sender->QueuePacket(outapp); safe_delete(outapp); @@ -590,6 +591,7 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) return(false); // Starting to use this object + m_inuse = true; sender->SetTradeskillObject(this); user = sender; @@ -645,7 +647,7 @@ uint32 ZoneDatabase::AddObject(uint32 type, uint32 icon, const Object_Struct& ob safe_delete_array(object_name); auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Unable to insert object: %s", results.ErrorMessage().c_str()); + LogError("Unable to insert object: [{}]", results.ErrorMessage().c_str()); return 0; } @@ -684,7 +686,7 @@ void ZoneDatabase::UpdateObject(uint32 id, uint32 type, uint32 icon, const Objec safe_delete_array(object_name); auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Unable to update object: %s", results.ErrorMessage().c_str()); + LogError("Unable to update object: [{}]", results.ErrorMessage().c_str()); return; } @@ -728,7 +730,7 @@ void ZoneDatabase::DeleteObject(uint32 id) std::string query = StringFormat("DELETE FROM object WHERE id = %i", id); auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Unable to delete object: %s", results.ErrorMessage().c_str()); + LogError("Unable to delete object: [{}]", results.ErrorMessage().c_str()); } } diff --git a/zone/pathfinder_nav_mesh.cpp b/zone/pathfinder_nav_mesh.cpp index 262d7a923..5be10b64f 100644 --- a/zone/pathfinder_nav_mesh.cpp +++ b/zone/pathfinder_nav_mesh.cpp @@ -475,7 +475,7 @@ void PathfinderNavmesh::Load(const std::string &path) m_impl->nav_mesh->addTile(data, data_size, DT_TILE_FREE_DATA, tile_ref, 0); } - Log(Logs::General, Logs::Status, "Loaded Navmesh V%u file %s", version, path.c_str()); + LogInfo("Loaded Navmesh V[{}] file [{}]", version, path.c_str()); } } diff --git a/zone/pathfinder_waypoint.cpp b/zone/pathfinder_waypoint.cpp index 83d924499..58dcd1582 100644 --- a/zone/pathfinder_waypoint.cpp +++ b/zone/pathfinder_waypoint.cpp @@ -251,14 +251,14 @@ void PathfinderWaypoint::Load(const std::string &filename) { if (strncmp(Magic, "EQEMUPATH", 9)) { - Log(Logs::General, Logs::Error, "Bad Magic String in .path file."); + LogError("Bad Magic String in .path file"); fclose(f); return; } fread(&Head, sizeof(Head), 1, f); - Log(Logs::General, Logs::Status, "Path File Header: Version %ld, PathNodes %ld", + LogInfo("Path File Header: Version [{}], PathNodes [{}]", (long)Head.version, (long)Head.PathNodeCount); if (Head.version == 2) @@ -271,7 +271,7 @@ void PathfinderWaypoint::Load(const std::string &filename) { return; } else { - Log(Logs::General, Logs::Error, "Unsupported path file version."); + LogError("Unsupported path file version"); fclose(f); return; } @@ -306,7 +306,7 @@ void PathfinderWaypoint::LoadV2(FILE *f, const PathFileHeader &header) auto &node = m_impl->Nodes[i]; if (PathNodes[i].Neighbours[j].id > MaxNodeID) { - Log(Logs::General, Logs::Error, "Path Node %i, Neighbour %i (%i) out of range.", i, j, PathNodes[i].Neighbours[j].id); + LogError("Path Node [{}], Neighbour [{}] ([{}]) out of range", i, j, PathNodes[i].Neighbours[j].id); m_impl->PathFileValid = false; } diff --git a/zone/pathing.cpp b/zone/pathing.cpp index e579bd0fe..ee5f0138a 100644 --- a/zone/pathing.cpp +++ b/zone/pathing.cpp @@ -30,7 +30,7 @@ void CullPoints(std::vector &points) { if (zone->zonemap->CheckLoS(glm::vec3(p.x, p.y, p.z), glm::vec3(p2.x, p2.y, p2.z))) { points.erase(points.begin() + i + 1); - Log(Logs::General, Logs::Status, "Culled find path point %u, connecting %u->%u instead.", i + 1, i, i + 2); + LogInfo("Culled find path point [{}], connecting [{}]->[{}] instead", i + 1, i, i + 2); } else { break; diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 4aac33267..60a21fd92 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -961,8 +961,7 @@ XS(XS_Client_SetEXP); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_SetEXP) { dXSARGS; if (items < 3 || items > 4) - Perl_croak(aTHX_ - "Usage: Client::SetEXP(THIS, uint32 experience_points, uint32 aa_experience_points, [bool resexp=false])"); + Perl_croak(aTHX_ "Usage: Client::SetEXP(THIS, uint32 experience_points, uint32 aa_experience_points, [bool resexp=false])"); { Client *THIS; uint32 set_exp = (uint32) SvUV(ST(1)); @@ -992,8 +991,7 @@ XS(XS_Client_SetBindPoint); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_SetBindPoint) { dXSARGS; if (items < 1 || items > 6) - Perl_croak(aTHX_ - "Usage: Client::SetBindPoint(THIS, int to_zone = -1, int to_instance = 0, float new_x = 0.0f, float new_y = 0.0f, float new_z = 0.0f)"); + Perl_croak(aTHX_ "Usage: Client::SetBindPoint(THIS, int to_zone = -1, int to_instance = 0, float new_x = 0.0f, float new_y = 0.0f, float new_z = 0.0f)"); { Client *THIS; int to_zone; @@ -1231,22 +1229,18 @@ XS(XS_Client_MovePC) { THIS->MovePC(zoneID, x, y, z, heading); } else { if (THIS->IsMerc()) { - Log(Logs::Detail, Logs::None, - "[CLIENT] Perl(XS_Client_MovePC) attempted to process a type Merc reference"); + LogDebug("[CLIENT] Perl(XS_Client_MovePC) attempted to process a type Merc reference"); } #ifdef BOTS else if (THIS->IsBot()) { - Log(Logs::Detail, Logs::None, - "[CLIENT] Perl(XS_Client_MovePC) attempted to process a type Bot reference"); + LogDebug("[CLIENT] Perl(XS_Client_MovePC) attempted to process a type Bot reference"); } #endif else if (THIS->IsNPC()) { - Log(Logs::Detail, Logs::None, - "[CLIENT] Perl(XS_Client_MovePC) attempted to process a type NPC reference"); + LogDebug("[CLIENT] Perl(XS_Client_MovePC) attempted to process a type NPC reference"); } else { - Log(Logs::Detail, Logs::None, - "[CLIENT] Perl(XS_Client_MovePC) attempted to process an Unknown type reference"); + LogDebug("[CLIENT] Perl(XS_Client_MovePC) attempted to process an Unknown type reference"); } Perl_croak(aTHX_ "THIS is not of type Client"); @@ -1260,8 +1254,7 @@ XS(XS_Client_MovePCInstance); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_MovePCInstance) { dXSARGS; if (items != 7) - Perl_croak(aTHX_ - "Usage: Client::MovePCInstance(THIS, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading)"); + Perl_croak(aTHX_ "Usage: Client::MovePCInstance(THIS, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading)"); { Client *THIS; uint32 zoneID = (uint32) SvUV(ST(1)); @@ -1283,22 +1276,18 @@ XS(XS_Client_MovePCInstance) { THIS->MovePC(zoneID, instanceID, x, y, z, heading); } else { if (THIS->IsMerc()) { - Log(Logs::Detail, Logs::None, - "[CLIENT] Perl(XS_Client_MovePCInstance) attempted to process a type Merc reference"); + LogDebug("[CLIENT] Perl(XS_Client_MovePCInstance) attempted to process a type Merc reference"); } #ifdef BOTS else if (THIS->IsBot()) { - Log(Logs::Detail, Logs::None, - "[CLIENT] Perl(XS_Client_MovePCInstance) attempted to process a type Bot reference"); + LogDebug("[CLIENT] Perl(XS_Client_MovePCInstance) attempted to process a type Bot reference"); } #endif else if (THIS->IsNPC()) { - Log(Logs::Detail, Logs::None, - "[CLIENT] Perl(XS_Client_MovePCInstance) attempted to process a type NPC reference"); + LogDebug("[CLIENT] Perl(XS_Client_MovePCInstance) attempted to process a type NPC reference"); } else { - Log(Logs::Detail, Logs::None, - "[CLIENT] Perl(XS_Client_MovePCInstance) attempted to process an Unknown type reference"); + LogDebug("[CLIENT] Perl(XS_Client_MovePCInstance) attempted to process an Unknown type reference"); } Perl_croak(aTHX_ "THIS is not of type Client"); @@ -1335,8 +1324,7 @@ XS(XS_Client_GetFactionLevel); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_GetFactionLevel) { dXSARGS; if (items != 8) - Perl_croak(aTHX_ - "Usage: Client::GetFactionLevel(THIS, uint32 character_id, uint32 npc_id, uint32 player_race_id, uint32 player_class_id, uint32 player_deity_id, uint32 player_faction_id, Mob*)"); + Perl_croak(aTHX_ "Usage: Client::GetFactionLevel(THIS, uint32 character_id, uint32 npc_id, uint32 player_race_id, uint32 player_class_id, uint32 player_deity_id, uint32 player_faction_id, Mob*)"); { Client *THIS; FACTION_VALUE RETVAL; @@ -1376,8 +1364,7 @@ XS(XS_Client_SetFactionLevel); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_SetFactionLevel) { dXSARGS; if (items != 6) - Perl_croak(aTHX_ - "Usage: Client::SetFactionLevel(THIS, uint32 character_id, uint32 npc_id, uint8 character_class, uint8 character_race, uint8 character_deity)"); + Perl_croak(aTHX_ "Usage: Client::SetFactionLevel(THIS, uint32 character_id, uint32 npc_id, uint8 character_class, uint8 character_race, uint8 character_deity)"); { Client *THIS; uint32 char_id = (uint32) SvUV(ST(1)); @@ -1403,8 +1390,7 @@ XS(XS_Client_SetFactionLevel2); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_SetFactionLevel2) { dXSARGS; if (items < 7 || items > 8) - Perl_croak(aTHX_ - "Usage: Client::SetFactionLevel2(THIS, uint32 character_id, int32 faction_id, uint8 character_class, uint8 character_race, uint8 character_deity, int32 value, uint8 temp)"); + Perl_croak(aTHX_ "Usage: Client::SetFactionLevel2(THIS, uint32 character_id, int32 faction_id, uint8 character_class, uint8 character_race, uint8 character_deity, int32 value, uint8 temp)"); { Client *THIS; uint32 char_id = (uint32) SvUV(ST(1)); @@ -1724,8 +1710,7 @@ XS(XS_Client_AddMoneyToPP); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_AddMoneyToPP) { dXSARGS; if (items != 6) - Perl_croak(aTHX_ - "Usage: Client::AddMoneyToPP(THIS, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool update_client)"); + Perl_croak(aTHX_ "Usage: Client::AddMoneyToPP(THIS, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool update_client)"); { Client *THIS; uint32 copper = (uint32) SvUV(ST(1)); @@ -3095,8 +3080,7 @@ XS(XS_Client_DeleteItemInInventory); /* prototype to pass -Wmissing-prototypes * XS(XS_Client_DeleteItemInInventory) { dXSARGS; if (items < 2 || items > 4) - Perl_croak(aTHX_ - "Usage: Client::DeleteItemInInventory(THIS, int16 slot_id, [int8 quantity = 0], [bool client_update = false])"); + Perl_croak(aTHX_ "Usage: Client::DeleteItemInInventory(THIS, int16 slot_id, [int8 quantity = 0], [bool client_update = false])"); { Client *THIS; int16 slot_id = (int16) SvIV(ST(1)); @@ -3132,8 +3116,7 @@ XS(XS_Client_SummonItem); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_SummonItem) { dXSARGS; if (items < 2 || items > 10) - Perl_croak(aTHX_ - "Usage: Client::SummonItem(THIS, uint32 item_id, [int16 charges = -1], [bool attune = false], [uint32 aug1 = 0], [uint32 aug2 = 0], [uint32 aug3 = 0], [uint32 aug4 = 0], [uint32 aug5 = 0], [uint16 slot_id = cursor])"); + Perl_croak(aTHX_ "Usage: Client::SummonItem(THIS, uint32 item_id, [int16 charges = -1], [bool attune = false], [uint32 aug1 = 0], [uint32 aug2 = 0], [uint32 aug3 = 0], [uint32 aug4 = 0], [uint32 aug5 = 0], [uint16 slot_id = cursor])"); { Client *THIS; uint32 item_id = (uint32) SvUV(ST(1)); @@ -4834,8 +4817,7 @@ XS(XS_Client_GrantAlternateAdvancementAbility); /* prototype to pass -Wmissing-p XS(XS_Client_GrantAlternateAdvancementAbility) { dXSARGS; if (items < 3 || items > 4) - Perl_croak(aTHX_ - "Usage: Client::GrantAlternateAdvancementAbility(THIS, int aa_id, int points, [bool ignore_cost = false])"); + Perl_croak(aTHX_ "Usage: Client::GrantAlternateAdvancementAbility(THIS, int aa_id, int points, [bool ignore_cost = false])"); { Client *THIS; bool RETVAL; @@ -4992,8 +4974,7 @@ XS(XS_Client_UpdateTaskActivity); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_UpdateTaskActivity) { dXSARGS; if (items < 4) - Perl_croak(aTHX_ - "Usage: Client::UpdateTaskActivity(THIS, int task_id, int activity_id, int count, [bool ignore_quest_update = false])"); + Perl_croak(aTHX_ "Usage: Client::UpdateTaskActivity(THIS, int task_id, int activity_id, int count, [bool ignore_quest_update = false])"); { bool ignore_quest_update = false; @@ -5052,8 +5033,7 @@ XS(XS_Client_AssignTask); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_AssignTask) { dXSARGS; if (items != 3 && items != 4) - Perl_croak(aTHX_ - "Usage: Client::AssignTask(THIS, int task_id, int npc_id, [bool enforce_level_requirement = false])"); + Perl_croak(aTHX_ "Usage: Client::AssignTask(THIS, int task_id, int npc_id, [bool enforce_level_requirement = false])"); { Client *THIS; int TaskID = (int) SvIV(ST(1)); @@ -5964,8 +5944,7 @@ XS(XS_Client_SendMarqueeMessage); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_SendMarqueeMessage) { dXSARGS; if (items != 7) - Perl_croak(aTHX_ - "Usage: Client::SendMarqueeMessage(THIS, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, string msg)"); + Perl_croak(aTHX_ "Usage: Client::SendMarqueeMessage(THIS, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, string msg)"); { Client *THIS; uint32 type = (uint32) SvUV(ST(1)); @@ -6144,8 +6123,7 @@ XS(XS_Client_QuestReward); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_QuestReward) { dXSARGS; if (items < 1 || items > 9) - Perl_croak(aTHX_ - "Usage: Client::QuestReward(THIS, int32 mob, int32 copper, int32 silver, int32 gold, int32 platinum, int32 item_id, int32 exp, [bool faction = false])"); + Perl_croak(aTHX_ "Usage: Client::QuestReward(THIS, int32 mob, int32 copper, int32 silver, int32 gold, int32 platinum, int32 item_id, int32 exp, [bool faction = false])"); { Client *THIS; Mob *mob = nullptr; @@ -6245,8 +6223,7 @@ XS(XS_Client_Popup2); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_Popup2) { dXSARGS; if (items < 3 || items > 10) - Perl_croak(aTHX_ - "Usage: Client::SendFullPopup(THIS, string title, string text, uint32 popup_id, uint32 negative_id, uint32 buttons, uint32 duration, string button_name_0, string button_name_1, uint32 sound_controls)"); + Perl_croak(aTHX_ "Usage: Client::SendFullPopup(THIS, string title, string text, uint32 popup_id, uint32 negative_id, uint32 buttons, uint32 duration, string button_name_0, string button_name_1, uint32 sound_controls)"); { Client *THIS; char *Title = (char *) SvPV_nolen(ST(1)); diff --git a/zone/perl_entity.cpp b/zone/perl_entity.cpp index 58e25b263..0f7fe21c7 100644 --- a/zone/perl_entity.cpp +++ b/zone/perl_entity.cpp @@ -1222,8 +1222,7 @@ XS(XS_EntityList_MessageStatus); /* prototype to pass -Wmissing-prototypes */ XS(XS_EntityList_MessageStatus) { dXSARGS; if (items < 5) - Perl_croak(aTHX_ - "Usage: EntityList::MessageStatus(THIS, uint32 guild_id, uint32 emote_color_type, string message)"); + Perl_croak(aTHX_ "Usage: EntityList::MessageStatus(THIS, uint32 guild_id, uint32 emote_color_type, string message)"); { EntityList *THIS; uint32 to_guilddbid = (uint32) SvUV(ST(1)); @@ -1248,8 +1247,7 @@ XS(XS_EntityList_MessageClose); /* prototype to pass -Wmissing-prototypes */ XS(XS_EntityList_MessageClose) { dXSARGS; if (items < 6) - Perl_croak(aTHX_ - "Usage: EntityList::MessageClose(THIS, Mob* sender, bool skip_sender, float distance, uint32 emote_color_type, string message)"); + Perl_croak(aTHX_ "Usage: EntityList::MessageClose(THIS, Mob* sender, bool skip_sender, float distance, uint32 emote_color_type, string message)"); { EntityList *THIS; Mob *sender; @@ -1682,8 +1680,7 @@ XS(XS_EntityList_MessageGroup); /* prototype to pass -Wmissing-prototypes */ XS(XS_EntityList_MessageGroup) { dXSARGS; if (items < 5) - Perl_croak(aTHX_ - "Usage: EntityList::MessageGroup(THIS, Mob* sender, bool skip_close, uint32 emote_color_type, string message)"); + Perl_croak(aTHX_ "Usage: EntityList::MessageGroup(THIS, Mob* sender, bool skip_close, uint32 emote_color_type, string message)"); { EntityList *THIS; Mob *sender; @@ -1716,8 +1713,7 @@ XS(XS_EntityList_GetRandomClient); /* prototype to pass -Wmissing-prototypes */ XS(XS_EntityList_GetRandomClient) { dXSARGS; if ((items < 5) || (items > 6)) - Perl_croak(aTHX_ - "Usage: EntityList::GetRandomClient(THIS, float x, float y, float z, float distance, [Client* exclude_client = nullptr])"); + Perl_croak(aTHX_ "Usage: EntityList::GetRandomClient(THIS, float x, float y, float z, float distance, [Client* exclude_client = nullptr])"); { EntityList *THIS; Client *RETVAL, *c = nullptr; diff --git a/zone/perl_groups.cpp b/zone/perl_groups.cpp index 33ed9689d..c23246b4a 100644 --- a/zone/perl_groups.cpp +++ b/zone/perl_groups.cpp @@ -486,8 +486,7 @@ XS(XS_Group_TeleportGroup); /* prototype to pass -Wmissing-prototypes */ XS(XS_Group_TeleportGroup) { dXSARGS; if (items != 7) - Perl_croak(aTHX_ - "Usage: Group::TeleportGroup(THIS, Mob* sender, uint32 zone_id, float x, float y, float z, float heading)"); + Perl_croak(aTHX_ "Usage: Group::TeleportGroup(THIS, Mob* sender, uint32 zone_id, float x, float y, float z, float heading)"); { Group *THIS; Mob *sender; diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 9ad3a6e8b..ca72ca803 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -794,8 +794,7 @@ XS(XS_Mob_Attack); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_Attack) { dXSARGS; if (items < 2 || items > 4) - Perl_croak(aTHX_ - "Usage: Mob::Attack(THIS, Mob* other, [int hand = 13 [prim|sec]], [bool from_riposte = false])"); + Perl_croak(aTHX_ "Usage: Mob::Attack(THIS, Mob* other, [int hand = 13 [prim|sec]], [bool from_riposte = false])"); { Mob *THIS; bool RETVAL; @@ -842,8 +841,7 @@ XS(XS_Mob_Damage); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_Damage) { dXSARGS; if (items < 5 || items > 8) - Perl_croak(aTHX_ - "Usage: Mob::Damage(THIS, Mob* from, int32 damage, uint16 spell_id, int attack_skill, [bool avoidable = true], [int8 buffslot = -1], [bool buff_tic = false])"); + Perl_croak(aTHX_ "Usage: Mob::Damage(THIS, Mob* from, int32 damage, uint16 spell_id, int attack_skill, [bool avoidable = true], [int8 buffslot = -1], [bool buff_tic = false])"); { Mob *THIS; Mob *from; @@ -1447,8 +1445,7 @@ XS(XS_Mob_MakeTempPet); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_MakeTempPet) { dXSARGS; if (items < 2 || items > 6) - Perl_croak(aTHX_ - "Usage: Mob::MakeTempPet(THIS, uint16 spell_id, [string name = nullptr], [uint32 duration = 0], [Mob* target = nullptr], [bool sticktarg = 0])"); + Perl_croak(aTHX_ "Usage: Mob::MakeTempPet(THIS, uint16 spell_id, [string name = nullptr], [uint32 duration = 0], [Mob* target = nullptr], [bool sticktarg = 0])"); { Mob *THIS; uint16 spell_id = (uint16) SvUV(ST(1)); @@ -1498,8 +1495,7 @@ XS(XS_Mob_TypesTempPet); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_TypesTempPet) { dXSARGS; if (items < 2 || items > 7) - Perl_croak(aTHX_ - "Usage: Mob::TypesTempPet(THIS, uint32 type_id, [string name = nullptr], [uint32 duration = 0], [bool follow = 0], [Mob* target = nullptr], [bool stick_targ = 0])"); + Perl_croak(aTHX_ "Usage: Mob::TypesTempPet(THIS, uint32 type_id, [string name = nullptr], [uint32 duration = 0], [bool follow = 0], [Mob* target = nullptr], [bool stick_targ = 0])"); { Mob *THIS; uint32 typesid = (uint32) SvUV(ST(1)); @@ -3680,8 +3676,7 @@ XS(XS_Mob_Message_StringID); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_Message_StringID) { dXSARGS; if (items < 3 || items > 4) - Perl_croak(aTHX_ - "Usage: Mob::Message_StringID(THIS, uint32 emote_color_type, uint32 string_id, [uint32 distance = 0])"); + Perl_croak(aTHX_ "Usage: Mob::Message_StringID(THIS, uint32 emote_color_type, uint32 string_id, [uint32 distance = 0])"); { Mob *THIS; uint32 type = (uint32) SvUV(ST(1)); @@ -3805,8 +3800,7 @@ XS(XS_Mob_CastSpell); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_CastSpell) { dXSARGS; if (items < 3 || items > 7) - Perl_croak(aTHX_ - "Usage: Mob::CastSpell(THIS, uint16 spell_id, uint16 target_id, [int slot = 22], [int32 cast_time = -1], [int32 mana_cost = -1], [int16 resist_adjust = 0])"); + Perl_croak(aTHX_ "Usage: Mob::CastSpell(THIS, uint16 spell_id, uint16 target_id, [int slot = 22], [int32 cast_time = -1], [int32 mana_cost = -1], [int16 resist_adjust = 0])"); { Mob *THIS; uint16 spell_id = (uint16) SvUV(ST(1)); @@ -3862,8 +3856,7 @@ XS(XS_Mob_SpellFinished); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_SpellFinished) { dXSARGS; if (items < 2 || items > 5) - Perl_croak(aTHX_ - "Usage: Mob::SpellFinished(uint16 spell_id, [Mob* spell_target = this], [uint16 mana_cost = 0], [uint16 resist_diff = 0])"); + Perl_croak(aTHX_ "Usage: Mob::SpellFinished(uint16 spell_id, [Mob* spell_target = this], [uint16 mana_cost = 0], [uint16 resist_diff = 0])"); { Mob *THIS; uint16 spell_id = (uint16) SvUV(ST(1)); @@ -4045,8 +4038,7 @@ XS(XS_Mob_CanBuffStack); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_CanBuffStack) { dXSARGS; if (items < 3 || items > 4) - Perl_croak(aTHX_ - "Usage: Mob::CanBuffStack(THIS, uint16 spell_id, uint8 caster_level, [bool fail_if_overwritten = false])"); + Perl_croak(aTHX_ "Usage: Mob::CanBuffStack(THIS, uint16 spell_id, uint8 caster_level, [bool fail_if_overwritten = false])"); { Mob *THIS; int RETVAL; @@ -5044,8 +5036,7 @@ XS(XS_Mob_AddToHateList); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_AddToHateList) { dXSARGS; if (items < 2 || items > 7) - Perl_croak(aTHX_ - "Usage: Mob::AddToHateList(THIS, Mob* other, [int32 hate = 0], [int32 damage = 0], [bool yell_for_help = true], [bool frenzy = false], [bool buff_tic = false])"); + Perl_croak(aTHX_ "Usage: Mob::AddToHateList(THIS, Mob* other, [int32 hate = 0], [int32 damage = 0], [bool yell_for_help = true], [bool frenzy = false], [bool buff_tic = false])"); { Mob *THIS; Mob *other; @@ -5609,8 +5600,7 @@ XS(XS_Mob_NavigateTo); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_NavigateTo) { dXSARGS; if (items < 4 || items > 5) - Perl_croak(aTHX_ - "Usage: Mob::NavigateTo(THIS, float x, float y, float z)"); + Perl_croak(aTHX_ "Usage: Mob::NavigateTo(THIS, float x, float y, float z)"); { Mob *THIS; float x = (float) SvNV(ST(1)); @@ -5735,8 +5725,7 @@ XS(XS_Mob_NPCSpecialAttacks); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_NPCSpecialAttacks) { dXSARGS; if (items < 3 || items > 5) - Perl_croak(aTHX_ - "Usage: Mob::NPCSpecialAttacks(THIS, string abilities_string, int perm_tag, [bool reset = true], [bool remove = true])"); + Perl_croak(aTHX_ "Usage: Mob::NPCSpecialAttacks(THIS, string abilities_string, int perm_tag, [bool reset = true], [bool remove = true])"); { Mob *THIS; char *parse = (char *) SvPV_nolen(ST(1)); @@ -6476,8 +6465,7 @@ XS(XS_Mob_DoSpecialAttackDamage); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_DoSpecialAttackDamage) { dXSARGS; if (items < 4 || items > 6) - Perl_croak(aTHX_ - "Usage: Mob::DoSpecialAttackDamage(THIS, Mob* target, int skill, int32 max_damage, [int32 min_damage = 1], [int32 hate_override = -11])"); + Perl_croak(aTHX_ "Usage: Mob::DoSpecialAttackDamage(THIS, Mob* target, int skill, int32 max_damage, [int32 min_damage = 1], [int32 hate_override = -11])"); { Mob *THIS; Mob *target; @@ -6620,8 +6608,7 @@ XS(XS_Mob_ProjectileAnim); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_ProjectileAnim) { dXSARGS; if (items < 3 || items > 9) - Perl_croak(aTHX_ - "Usage: Mob::ProjectileAnim(THIS, Mob* mob, int item_id, [bool is_arrow = false], [float speed = 0], [float angle = 0], [float tilt = 0], [float arc = 0])"); + Perl_croak(aTHX_ "Usage: Mob::ProjectileAnim(THIS, Mob* mob, int item_id, [bool is_arrow = false], [float speed = 0], [float angle = 0], [float tilt = 0], [float arc = 0])"); { Mob *THIS; @@ -6703,8 +6690,7 @@ XS(XS_Mob_SendAppearanceEffect); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_SendAppearanceEffect) { dXSARGS; if (items < 2 || items > 7) - Perl_croak(aTHX_ - "Usage: Mob::SendAppearanceEffect(THIS, int32 param_1, [int32 param_2 = 0], [int32 param_3 = 0], [int32 param_4 = 0], [int32 param_5 = 0], [Client* single_client_to_send_to = null])"); + Perl_croak(aTHX_ "Usage: Mob::SendAppearanceEffect(THIS, int32 param_1, [int32 param_2 = 0], [int32 param_3 = 0], [int32 param_4 = 0], [int32 param_5 = 0], [Client* single_client_to_send_to = null])"); { Mob *THIS; int32 parm1 = (int32) SvIV(ST(1)); @@ -6833,8 +6819,7 @@ XS(XS_Mob_SendIllusion); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_SendIllusion) { dXSARGS; if (items < 2 || items > 14) - Perl_croak(aTHX_ - "Usage: Mob::SendIllusion(THIS, uint16 race, [uint8 gender = 0xFF], [uint8 texture face = 0xFF], [uint8 hairstyle = 0xFF], [uint8 hair_color = 0xFF], [uint8 beard = 0xFF], [uint8 beard_color =FF], [uint32 drakkin_tattoo = 0xFFFFFFFF], [uint32 drakkin_details = 0xFFFFFFFF], [float size = -1])"); + Perl_croak(aTHX_ "Usage: Mob::SendIllusion(THIS, uint16 race, [uint8 gender = 0xFF], [uint8 texture face = 0xFF], [uint8 hairstyle = 0xFF], [uint8 hair_color = 0xFF], [uint8 beard = 0xFF], [uint8 beard_color =FF], [uint32 drakkin_tattoo = 0xFFFFFFFF], [uint32 drakkin_details = 0xFFFFFFFF], [float size = -1])"); { Mob *THIS; uint16 race = (uint16) SvIV(ST(1)); @@ -6882,8 +6867,7 @@ XS(XS_Mob_CameraEffect); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_CameraEffect) { dXSARGS; if (items < 2 || items > 5) - Perl_croak(aTHX_ - "Usage: Mob::CameraEffect(THIS, uint32 duration, [uint32 intensity = 0], [Client* single_client = nullptr], [bool is_world_wide = false])"); + Perl_croak(aTHX_ "Usage: Mob::CameraEffect(THIS, uint32 duration, [uint32 intensity = 0], [Client* single_client = nullptr], [bool is_world_wide = false])"); { Mob *THIS; uint32 duration = (uint32) SvUV(ST(1)); @@ -6925,8 +6909,7 @@ XS(XS_Mob_SpellEffect); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_SpellEffect) { dXSARGS; if (items < 2 || items > 8) - Perl_croak(aTHX_ - "Usage: Mob::SpellEffect(THIS, uint32 effect, [uint32 duration = 5000], [uint32 finish_delay = 0], [bool zone_wide = false], [uint32 unk20 = 3000], [bool perm_effect = false], [Client* single_client])"); + Perl_croak(aTHX_ "Usage: Mob::SpellEffect(THIS, uint32 effect, [uint32 duration = 5000], [uint32 finish_delay = 0], [bool zone_wide = false], [uint32 unk20 = 3000], [bool perm_effect = false], [Client* single_client])"); { Mob *THIS; uint32 effect = (uint32) SvUV(ST(1)); @@ -7054,8 +7037,7 @@ XS(XS_Mob_SetGlobal); XS(XS_Mob_SetGlobal) { dXSARGS; if (items < 5 || items > 6) - Perl_croak(aTHX_ - "Usage: SetGlobal(THIS, string var_name, string new_value, int options, string duration, [Mob* other = nullptr])"); + Perl_croak(aTHX_ "Usage: SetGlobal(THIS, string var_name, string new_value, int options, string duration, [Mob* other = nullptr])"); { Mob *THIS; char *varname = (char *) SvPV_nolen(ST(1)); @@ -7091,8 +7073,7 @@ XS(XS_Mob_TarGlobal); XS(XS_Mob_TarGlobal) { dXSARGS; if (items != 7) - Perl_croak(aTHX_ - "Usage: TarGlobal(THIS, string var_name, string value, string duration, int npc_id, int character_id, int zone_id)"); + Perl_croak(aTHX_ "Usage: TarGlobal(THIS, string var_name, string value, string duration, int npc_id, int character_id, int zone_id)"); { Mob *THIS; char *varname = (char *) SvPV_nolen(ST(1)); @@ -7141,8 +7122,7 @@ XS(XS_Mob_SetSlotTint); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_SetSlotTint) { dXSARGS; if (items != 5) - Perl_croak(aTHX_ - "Usage: Mob::SetSlotTint(THIS, uint8 material_slot, uint8 red_tint, uint8 green_tint, uint8 blue_tint)"); + Perl_croak(aTHX_ "Usage: Mob::SetSlotTint(THIS, uint8 material_slot, uint8 red_tint, uint8 green_tint, uint8 blue_tint)"); { Mob *THIS; uint8 material_slot = (uint8) SvIV(ST(1)); @@ -7167,8 +7147,7 @@ XS(XS_Mob_WearChange); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_WearChange) { dXSARGS; if (items < 3 || items > 4) - Perl_croak(aTHX_ - "Usage: Mob::WearChange(THIS, uint8 material_slot, uint16 texture, [uint32 color = 0, uint32 hero_forge_model = 0])"); + Perl_croak(aTHX_ "Usage: Mob::WearChange(THIS, uint8 material_slot, uint16 texture, [uint32 color = 0, uint32 hero_forge_model = 0])"); { Mob *THIS; uint8 material_slot = (uint8) SvIV(ST(1)); @@ -7598,8 +7577,7 @@ XS(XS_Mob_DoMeleeSkillAttackDmg); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_DoMeleeSkillAttackDmg) { dXSARGS; if (items != 7) - Perl_croak(aTHX_ - "Usage: Mob::DoMeleeSkillAttackDmg(THIS, Mob* target, uint16 weapon_damage, int skill, int16 chance_mod, int16 focus, uint8 can_riposte)"); + Perl_croak(aTHX_ "Usage: Mob::DoMeleeSkillAttackDmg(THIS, Mob* target, uint16 weapon_damage, int skill, int16 chance_mod, int16 focus, uint8 can_riposte)"); { Mob *THIS; Mob *target; @@ -7634,8 +7612,7 @@ XS(XS_Mob_DoArcheryAttackDmg); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_DoArcheryAttackDmg) { dXSARGS; if (items != 7) - Perl_croak(aTHX_ - "Usage: Mob::DoArcheryAttackDmg(THIS, Mob* target, [range_weapon_item_instance = nullptr], [ammo_item_instance = nullptr], uint16 weapon_damage, int16 chance_mod, int16 focus)"); + Perl_croak(aTHX_ "Usage: Mob::DoArcheryAttackDmg(THIS, Mob* target, [range_weapon_item_instance = nullptr], [ammo_item_instance = nullptr], uint16 weapon_damage, int16 chance_mod, int16 focus)"); { Mob *THIS; Mob *target; @@ -7670,8 +7647,7 @@ XS(XS_Mob_DoThrowingAttackDmg); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_DoThrowingAttackDmg) { dXSARGS; if (items != 7) - Perl_croak(aTHX_ - "Usage: Mob::DoThrowingAttackDmg(THIS, Mob* target, [range_weapon_item_instance = nullptr], [ammo_item_instance = nullptr], uint16 weapon_damage, int16 chance_mod, int16 focus)"); + Perl_croak(aTHX_ "Usage: Mob::DoThrowingAttackDmg(THIS, Mob* target, [range_weapon_item_instance = nullptr], [ammo_item_instance = nullptr], uint16 weapon_damage, int16 chance_mod, int16 focus)"); { Mob *THIS; Mob *target; diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 366aad2ec..97dc889e1 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -97,8 +97,7 @@ XS(XS_NPC_AddItem); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_AddItem) { dXSARGS; if (items < 2 || items > 10) - Perl_croak(aTHX_ - "Usage: NPC::AddItem(THIS, uint32 item_id, [uint16 charges = 0], [bool equip_item = true], [uint32 aug1 = 0], [uint32 aug2 = 0], [uint32 aug3 = 0], [uint32 aug4 = 0], [uint32 aug5 = 0], [uint32 aug6 = 0])"); + Perl_croak(aTHX_ "Usage: NPC::AddItem(THIS, uint32 item_id, [uint16 charges = 0], [bool equip_item = true], [uint32 aug1 = 0], [uint32 aug2 = 0], [uint32 aug3 = 0], [uint32 aug4 = 0], [uint32 aug5 = 0], [uint32 aug6 = 0])"); { NPC *THIS; uint32 itemid = (uint32) SvUV(ST(1)); @@ -1283,8 +1282,7 @@ XS(XS_NPC_MoveTo); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_MoveTo) { dXSARGS; if (items != 4 && items != 5 && items != 6) - Perl_croak(aTHX_ - "Usage: NPC::MoveTo(THIS, float x, float y, float z, [float heading], [bool save_guard_location = false])"); + Perl_croak(aTHX_ "Usage: NPC::MoveTo(THIS, float x, float y, float z, [float heading], [bool save_guard_location = false])"); { NPC *THIS; float mtx = (float) SvNV(ST(1)); @@ -1391,8 +1389,7 @@ XS(XS_NPC_AI_SetRoambox); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_AI_SetRoambox) { dXSARGS; if (items < 6 || items > 8) - Perl_croak(aTHX_ - "Usage: NPC::AI_SetRoambox(THIS, float distance, float max_x, float min_x, float max_y, float min_y, [uint32 max_delay = 2500], [uint32 min_delay = 2500])"); + Perl_croak(aTHX_ "Usage: NPC::AI_SetRoambox(THIS, float distance, float max_x, float min_x, float max_y, float min_y, [uint32 max_delay = 2500], [uint32 min_delay = 2500])"); { NPC *THIS; float iDist = (float) SvNV(ST(1)); @@ -1852,8 +1849,7 @@ XS(XS_NPC_AddSpellToNPCList); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_AddSpellToNPCList) { dXSARGS; if (items != 7) - Perl_croak(aTHX_ - "Usage: NPC::AddAISpell(THIS, int priority, int spell_id, int type, int mana_cost, int recast_delay, int resist_adjust)"); + Perl_croak(aTHX_ "Usage: NPC::AddAISpell(THIS, int priority, int spell_id, int type, int mana_cost, int recast_delay, int resist_adjust)"); { NPC *THIS; int priority = (int) SvIV(ST(1)); diff --git a/zone/perl_raids.cpp b/zone/perl_raids.cpp index f5b91448e..72672de4e 100644 --- a/zone/perl_raids.cpp +++ b/zone/perl_raids.cpp @@ -420,8 +420,7 @@ XS(XS_Raid_TeleportGroup); /* prototype to pass -Wmissing-prototypes */ XS(XS_Raid_TeleportGroup) { dXSARGS; if (items != 8) - Perl_croak(aTHX_ - "Usage: Raid::TeleportGroup(THIS, Mob* sender, uint32 zone_id, float x, float y, float z, float heading, uint32 group_id)"); + Perl_croak(aTHX_ "Usage: Raid::TeleportGroup(THIS, Mob* sender, uint32 zone_id, float x, float y, float z, float heading, uint32 group_id)"); { Raid *THIS; Mob *sender; @@ -457,8 +456,7 @@ XS(XS_Raid_TeleportRaid); /* prototype to pass -Wmissing-prototypes */ XS(XS_Raid_TeleportRaid) { dXSARGS; if (items != 7) - Perl_croak(aTHX_ - "Usage: Raid::TeleportRaid(THIS, Mob* sender, uint32 zone_id, float x, float y, float z, float heading)"); + Perl_croak(aTHX_ "Usage: Raid::TeleportRaid(THIS, Mob* sender, uint32 zone_id, float x, float y, float z, float heading)"); { Raid *THIS; Mob *sender; diff --git a/zone/petitions.cpp b/zone/petitions.cpp index 4ba73332d..112a630e4 100644 --- a/zone/petitions.cpp +++ b/zone/petitions.cpp @@ -252,7 +252,7 @@ void ZoneDatabase::InsertPetitionToDB(Petition* wpet) } #if EQDEBUG >= 5 - Log(Logs::General, Logs::None, "New petition created"); + LogDebug("New petition created"); #endif } diff --git a/zone/pets.cpp b/zone/pets.cpp index 6f8c5d44e..438509b77 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -213,7 +213,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, PetRecord record; if(!database.GetPoweredPetEntry(pettype, act_power, &record)) { Message(Chat::Red, "Unable to find data for pet %s", pettype); - Log(Logs::General, Logs::Error, "Unable to find data for pet %s, check pets table.", pettype); + LogError("Unable to find data for pet [{}], check pets table", pettype); return; } @@ -221,7 +221,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, const NPCType *base = database.LoadNPCTypesData(record.npc_type); if(base == nullptr) { Message(Chat::Red, "Unable to load NPC data for pet %s", pettype); - Log(Logs::General, Logs::Error, "Unable to load NPC data for pet %s (NPC ID %d), check pets and npc_types tables.", pettype, record.npc_type); + LogError("Unable to load NPC data for pet [{}] (NPC ID [{}]), check pets and npc_types tables", pettype, record.npc_type); return; } @@ -374,7 +374,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, npc_type->helmtexture = monster->helmtexture; npc_type->herosforgemodel = monster->herosforgemodel; } else - Log(Logs::General, Logs::Error, "Error loading NPC data for monster summoning pet (NPC ID %d)", monsterid); + LogError("Error loading NPC data for monster summoning pet (NPC ID [{}])", monsterid); } @@ -665,7 +665,7 @@ bool ZoneDatabase::GetBasePetItems(int32 equipmentset, uint32 *items) { if (results.RowCount() != 1) { // invalid set reference, it doesn't exist - Log(Logs::General, Logs::Error, "Error in GetBasePetItems equipment set '%d' does not exist", curset); + LogError("Error in GetBasePetItems equipment set [{}] does not exist", curset); return false; } diff --git a/zone/quest_parser_collection.cpp b/zone/quest_parser_collection.cpp index 5604647f7..c3d38ce4f 100644 --- a/zone/quest_parser_collection.cpp +++ b/zone/quest_parser_collection.cpp @@ -1050,7 +1050,7 @@ int QuestParserCollection::DispatchEventSpell(QuestEventID evt, NPC* npc, Client void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings* perl_event_export_settings) { - Log(Logs::General, Logs::Zone_Server, "Loading Perl Event Export Settings..."); + LogInfo("Loading Perl Event Export Settings..."); /* Write Defaults First (All Enabled) */ for (int i = 0; i < _LargestEventID; i++){ diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 64e5bd008..69e55a57f 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -157,7 +157,7 @@ void QuestManager::echo(int colour, const char *str) { void QuestManager::say(const char *str, Journal::Options &opts) { QuestManagerCurrentQuestVars(); if (!owner) { - Log(Logs::General, Logs::Quests, "QuestManager::say called with nullptr owner. Probably syntax error in quest file."); + LogQuests("QuestManager::say called with nullptr owner. Probably syntax error in quest file"); return; } else { @@ -258,7 +258,16 @@ Mob *QuestManager::spawn_from_spawn2(uint32 spawn2_id) return nullptr; } } - uint32 npcid = spawn_group->GetNPCType(); + + uint16 condition_value=1; + uint16 condition_id=found_spawn->GetSpawnCondition(); + + if (condition_id > 0) { + condition_value = zone->spawn_conditions.GetCondition(zone->GetShortName(), zone->GetInstanceID(), condition_id); + } + + uint32 npcid = spawn_group->GetNPCType(condition_value); + if (npcid == 0) { return nullptr; } @@ -558,7 +567,7 @@ void QuestManager::pausetimer(const char *timer_name) { { if (pcur->owner && pcur->owner == owner && pcur->name == timer_name) { - Log(Logs::General, Logs::Quests, "Timer %s is already paused for %s. Returning...", timer_name, owner->GetName()); + LogQuests("Timer [{}] is already paused for [{}]. Returning", timer_name, owner->GetName()); return; } ++pcur; @@ -580,7 +589,7 @@ void QuestManager::pausetimer(const char *timer_name) { pt.name = timername; pt.owner = owner; pt.time = milliseconds; - Log(Logs::General, Logs::Quests, "Pausing timer %s for %s with %d ms remaining.", timer_name, owner->GetName(), milliseconds); + LogQuests("Pausing timer [{}] for [{}] with [{}] ms remaining", timer_name, owner->GetName(), milliseconds); PTimerList.push_back(pt); } @@ -606,7 +615,7 @@ void QuestManager::resumetimer(const char *timer_name) { if (milliseconds == 0) { - Log(Logs::General, Logs::Quests, "Paused timer %s not found or has expired. Returning...", timer_name); + LogQuests("Paused timer [{}] not found or has expired. Returning", timer_name); return; } @@ -617,14 +626,14 @@ void QuestManager::resumetimer(const char *timer_name) { { cur->Timer_.Enable(); cur->Timer_.Start(milliseconds, false); - Log(Logs::General, Logs::Quests, "Resuming timer %s for %s with %d ms remaining.", timer_name, owner->GetName(), milliseconds); + LogQuests("Resuming timer [{}] for [{}] with [{}] ms remaining", timer_name, owner->GetName(), milliseconds); return; } ++cur; } QTimerList.push_back(QuestTimer(milliseconds, owner, timer_name)); - Log(Logs::General, Logs::Quests, "Creating a new timer and resuming %s for %s with %d ms remaining.", timer_name, owner->GetName(), milliseconds); + LogQuests("Creating a new timer and resuming [{}] for [{}] with [{}] ms remaining", timer_name, owner->GetName(), milliseconds); } @@ -649,7 +658,7 @@ bool QuestManager::ispausedtimer(const char *timer_name) { void QuestManager::emote(const char *str) { QuestManagerCurrentQuestVars(); if (!owner) { - Log(Logs::General, Logs::Quests, "QuestManager::emote called with nullptr owner. Probably syntax error in quest file."); + LogQuests("QuestManager::emote called with nullptr owner. Probably syntax error in quest file"); return; } else { @@ -660,7 +669,7 @@ void QuestManager::emote(const char *str) { void QuestManager::shout(const char *str) { QuestManagerCurrentQuestVars(); if (!owner) { - Log(Logs::General, Logs::Quests, "QuestManager::shout called with nullptr owner. Probably syntax error in quest file."); + LogQuests("QuestManager::shout called with nullptr owner. Probably syntax error in quest file"); return; } else { @@ -671,7 +680,7 @@ void QuestManager::shout(const char *str) { void QuestManager::shout2(const char *str) { QuestManagerCurrentQuestVars(); if (!owner) { - Log(Logs::General, Logs::Quests, "QuestManager::shout2 called with nullptr owner. Probably syntax error in quest file."); + LogQuests("QuestManager::shout2 called with nullptr owner. Probably syntax error in quest file"); return; } else { @@ -690,7 +699,7 @@ void QuestManager::gmsay(const char *str, uint32 color, bool send_to_world, uint void QuestManager::depop(int npc_type) { QuestManagerCurrentQuestVars(); if (!owner || !owner->IsNPC()) { - Log(Logs::General, Logs::Quests, "QuestManager::depop called with nullptr owner or non-NPC owner. Probably syntax error in quest file."); + LogQuests("QuestManager::depop called with nullptr owner or non-NPC owner. Probably syntax error in quest file"); return; } else { @@ -720,7 +729,7 @@ void QuestManager::depop(int npc_type) { void QuestManager::depop_withtimer(int npc_type) { QuestManagerCurrentQuestVars(); if (!owner || !owner->IsNPC()) { - Log(Logs::General, Logs::Quests, "QuestManager::depop_withtimer called with nullptr owner or non-NPC owner. Probably syntax error in quest file."); + LogQuests("QuestManager::depop_withtimer called with nullptr owner or non-NPC owner. Probably syntax error in quest file"); return; } else { @@ -747,7 +756,7 @@ void QuestManager::depopall(int npc_type) { entity_list.DepopAll(npc_type); } else { - Log(Logs::General, Logs::Quests, "QuestManager::depopall called with nullptr owner, non-NPC owner, or invalid NPC Type ID. Probably syntax error in quest file."); + LogQuests("QuestManager::depopall called with nullptr owner, non-NPC owner, or invalid NPC Type ID. Probably syntax error in quest file"); } } @@ -756,7 +765,7 @@ void QuestManager::depopzone(bool StartSpawnTimer) { zone->Depop(StartSpawnTimer); } else { - Log(Logs::General, Logs::Quests, "QuestManager::depopzone called with nullptr zone. Probably syntax error in quest file."); + LogQuests("QuestManager::depopzone called with nullptr zone. Probably syntax error in quest file"); } } @@ -765,7 +774,7 @@ void QuestManager::repopzone() { zone->Repop(); } else { - Log(Logs::General, Logs::Quests, "QuestManager::repopzone called with nullptr zone. Probably syntax error in quest file."); + LogQuests("QuestManager::repopzone called with nullptr zone. Probably syntax error in quest file"); } } @@ -1825,7 +1834,7 @@ void QuestManager::showgrid(int grid) { "ORDER BY `number`", grid, zone->GetZoneID()); auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Quests, "Error loading grid %d for showgrid(): %s", grid, results.ErrorMessage().c_str()); + LogQuests("Error loading grid [{}] for showgrid(): [{}]", grid, results.ErrorMessage().c_str()); return; } @@ -2978,7 +2987,7 @@ void QuestManager::voicetell(const char *str, int macronum, int racenum, int gen safe_delete(outapp); } else - Log(Logs::General, Logs::Quests, "QuestManager::voicetell from %s. Client %s not found.", owner->GetName(), str); + LogQuests("QuestManager::voicetell from [{}]. Client [{}] not found", owner->GetName(), str); } } diff --git a/zone/raids.cpp b/zone/raids.cpp index 9b5485f40..7a153864d 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -103,7 +103,7 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo auto results = database.QueryDatabase(query); if(!results.Success()) { - Log(Logs::General, Logs::Error, "Error inserting into raid members: %s", results.ErrorMessage().c_str()); + LogError("Error inserting into raid members: [{}]", results.ErrorMessage().c_str()); } LearnMembers(); @@ -266,12 +266,12 @@ void Raid::SetRaidLeader(const char *wasLead, const char *name) std::string query = StringFormat("UPDATE raid_members SET israidleader = 0 WHERE name = '%s'", wasLead); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Set Raid Leader error: %s\n", results.ErrorMessage().c_str()); + LogError("Set Raid Leader error: [{}]\n", results.ErrorMessage().c_str()); query = StringFormat("UPDATE raid_members SET israidleader = 1 WHERE name = '%s'", name); results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Set Raid Leader error: %s\n", results.ErrorMessage().c_str()); + LogError("Set Raid Leader error: [{}]\n", results.ErrorMessage().c_str()); strn0cpy(leadername, name, 64); @@ -304,7 +304,7 @@ void Raid::SaveGroupLeaderAA(uint32 gid) safe_delete_array(queryBuffer); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to store LeadershipAA: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to store LeadershipAA: [{}]\n", results.ErrorMessage().c_str()); } void Raid::SaveRaidLeaderAA() @@ -318,7 +318,7 @@ void Raid::SaveRaidLeaderAA() safe_delete_array(queryBuffer); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to store LeadershipAA: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to store LeadershipAA: [{}]\n", results.ErrorMessage().c_str()); } void Raid::UpdateGroupAAs(uint32 gid) @@ -547,7 +547,7 @@ void Raid::CastGroupSpell(Mob* caster, uint16 spellid, uint32 gid) #endif } else{ - Log(Logs::Detail, Logs::Spells, "Raid spell: %s is out of range %f at distance %f from %s", members[x].member->GetName(), range, distance, caster->GetName()); + LogSpells("Raid spell: [{}] is out of range [{}] at distance [{}] from [{}]", members[x].member->GetName(), range, distance, caster->GetName()); } } } @@ -848,7 +848,7 @@ void Raid::GroupBardPulse(Mob* caster, uint16 spellid, uint32 gid){ members[z].member->GetPet()->BardPulse(spellid, caster); #endif } else - Log(Logs::Detail, Logs::Spells, "Group bard pulse: %s is out of range %f at distance %f from %s", members[z].member->GetName(), range, distance, caster->GetName()); + LogSpells("Group bard pulse: [{}] is out of range [{}] at distance [{}] from [{}]", members[z].member->GetName(), range, distance, caster->GetName()); } } } @@ -1455,10 +1455,14 @@ void Raid::GetRaidDetails() if (!results.Success()) return; - if (results.RowCount() == 0) { - Log(Logs::General, Logs::Error, "Error getting raid details for raid %lu: %s", (unsigned long)GetID(), results.ErrorMessage().c_str()); - return; - } + if (results.RowCount() == 0) { + LogError( + "Error getting raid details for raid [{}]: [{}]", + (unsigned long) GetID(), + results.ErrorMessage().c_str() + ); + return; + } auto row = results.begin(); @@ -1488,7 +1492,7 @@ bool Raid::LearnMembers() return false; if(results.RowCount() == 0) { - Log(Logs::General, Logs::Error, "Error getting raid members for raid %lu: %s", (unsigned long)GetID(), results.ErrorMessage().c_str()); + LogError("Error getting raid members for raid [{}]: [{}]", (unsigned long)GetID(), results.ErrorMessage().c_str()); disbandCheck = true; return false; } @@ -1750,7 +1754,7 @@ void Raid::SetGroupMentor(uint32 group_id, int percent, char *name) name, percent, group_id, GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to set raid group mentor: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to set raid group mentor: [{}]\n", results.ErrorMessage().c_str()); } void Raid::ClearGroupMentor(uint32 group_id) @@ -1765,7 +1769,7 @@ void Raid::ClearGroupMentor(uint32 group_id) group_id, GetID()); auto results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Unable to clear raid group mentor: %s\n", results.ErrorMessage().c_str()); + LogError("Unable to clear raid group mentor: [{}]\n", results.ErrorMessage().c_str()); } // there isn't a nice place to add this in another function, unlike groups diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index 93c955d4e..e6d265813 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -154,17 +154,13 @@ bool Spawn2::Process() { if (timer.Check()) { timer.Disable(); - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Timer has triggered", spawn2_id); + LogSpawns("Spawn2 [{}]: Timer has triggered", spawn2_id); //first check our spawn condition, if this isnt active //then we reset the timer and try again next time. if (condition_id != SC_AlwaysEnabled && !zone->spawn_conditions.Check(condition_id, condition_min_value)) { - Log(Logs::Detail, - Logs::Spawns, - "Spawn2 %d: spawning prevented by spawn condition %d", - spawn2_id, - condition_id); + LogSpawns("Spawn2 [{}]: spawning prevented by spawn condition [{}]", spawn2_id, condition_id); Reset(); return (true); } @@ -175,23 +171,21 @@ bool Spawn2::Process() { } if (spawn_group == nullptr) { - Log(Logs::Detail, - Logs::Spawns, - "Spawn2 %d: Unable to locate spawn group %d. Disabling.", - spawn2_id, - spawngroup_id_); + LogSpawns("Spawn2 [{}]: Unable to locate spawn group [{}]. Disabling", spawn2_id, spawngroup_id_); return false; } + uint16 condition_value=1; + + if (condition_id > 0) { + condition_value = zone->spawn_conditions.GetCondition(zone->GetShortName(), zone->GetInstanceID(), condition_id); + } + //have the spawn group pick an NPC for us - uint32 npcid = spawn_group->GetNPCType(); + uint32 npcid = spawn_group->GetNPCType(condition_value); if (npcid == 0) { - Log(Logs::Detail, - Logs::Spawns, - "Spawn2 %d: Spawn group %d did not yeild an NPC! not spawning.", - spawn2_id, - spawngroup_id_); + LogSpawns("Spawn2 [{}]: Spawn group [{}] did not yeild an NPC! not spawning", spawn2_id, spawngroup_id_); Reset(); //try again later (why?) return (true); @@ -200,24 +194,14 @@ bool Spawn2::Process() { //try to find our NPC type. const NPCType *tmp = database.LoadNPCTypesData(npcid); if (tmp == nullptr) { - Log(Logs::Detail, - Logs::Spawns, - "Spawn2 %d: Spawn group %d yeilded an invalid NPC type %d", - spawn2_id, - spawngroup_id_, - npcid); + LogSpawns("Spawn2 [{}]: Spawn group [{}] yeilded an invalid NPC type [{}]", spawn2_id, spawngroup_id_, npcid); Reset(); //try again later return (true); } if (tmp->unique_spawn_by_name) { if (!entity_list.LimitCheckName(tmp->name)) { - Log(Logs::Detail, - Logs::Spawns, - "Spawn2 %d: Spawn group %d yeilded NPC type %d, which is unique and one already exists.", - spawn2_id, - spawngroup_id_, - npcid); + LogSpawns("Spawn2 [{}]: Spawn group [{}] yeilded NPC type [{}], which is unique and one already exists", spawn2_id, spawngroup_id_, npcid); timer.Start(5000); //try again in five seconds. return (true); } @@ -225,13 +209,7 @@ bool Spawn2::Process() { if (tmp->spawn_limit > 0) { if (!entity_list.LimitCheckType(npcid, tmp->spawn_limit)) { - Log(Logs::Detail, - Logs::Spawns, - "Spawn2 %d: Spawn group %d yeilded NPC type %d, which is over its spawn limit (%d)", - spawn2_id, - spawngroup_id_, - npcid, - tmp->spawn_limit); + LogSpawns("Spawn2 [{}]: Spawn group [{}] yeilded NPC type [{}], which is over its spawn limit ([{}])", spawn2_id, spawngroup_id_, npcid, tmp->spawn_limit); timer.Start(5000); //try again in five seconds. return (true); } @@ -287,9 +265,7 @@ bool Spawn2::Process() { } if (zone->InstantGrids()) { - Log(Logs::Detail, - Logs::Spawns, - "Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f).", + LogSpawns("Spawn2 [{}]: Group [{}] spawned [{}] ([{}]) at ([{}], [{}], [{}])", spawn2_id, spawngroup_id_, npc->GetName(), @@ -302,16 +278,15 @@ bool Spawn2::Process() { LoadGrid(); } else { - Log(Logs::Detail, - Logs::Spawns, - "Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f). Grid loading delayed.", + LogSpawns("Spawn2 [{}]: Group [{}] spawned [{}] ([{}]) at ([{}], [{}], [{}]). Grid loading delayed", spawn2_id, spawngroup_id_, tmp->name, npcid, x, y, - z); + z + ); } } @@ -337,7 +312,7 @@ void Spawn2::LoadGrid() { //dont set an NPC's grid until its loaded for them. npcthis->SetGrid(grid_); npcthis->AssignWaypoints(grid_); - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Loading grid %d for %s", spawn2_id, grid_, npcthis->GetName()); + LogSpawns("Spawn2 [{}]: Loading grid [{}] for [{}]", spawn2_id, grid_, npcthis->GetName()); } /* @@ -347,21 +322,21 @@ void Spawn2::LoadGrid() { void Spawn2::Reset() { timer.Start(resetTimer()); npcthis = nullptr; - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn reset, repop in %d ms", spawn2_id, timer.GetRemainingTime()); + LogSpawns("Spawn2 [{}]: Spawn reset, repop in [{}] ms", spawn2_id, timer.GetRemainingTime()); } void Spawn2::Depop() { timer.Disable(); - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn reset, repop disabled", spawn2_id); + LogSpawns("Spawn2 [{}]: Spawn reset, repop disabled", spawn2_id); npcthis = nullptr; } void Spawn2::Repop(uint32 delay) { if (delay == 0) { timer.Trigger(); - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn reset, repop immediately.", spawn2_id); + LogSpawns("Spawn2 [{}]: Spawn reset, repop immediately", spawn2_id); } else { - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn reset for repop, repop in %d ms", spawn2_id, delay); + LogSpawns("Spawn2 [{}]: Spawn reset for repop, repop in [{}] ms", spawn2_id, delay); timer.Start(delay); } npcthis = nullptr; @@ -406,7 +381,7 @@ void Spawn2::ForceDespawn() cur = despawnTimer(dtimer); } - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn group %d set despawn timer to %d ms.", spawn2_id, spawngroup_id_, cur); + LogSpawns("Spawn2 [{}]: Spawn group [{}] set despawn timer to [{}] ms", spawn2_id, spawngroup_id_, cur); timer.Start(cur); } @@ -427,7 +402,7 @@ void Spawn2::DeathReset(bool realdeath) if(spawn2_id) { database.UpdateRespawnTime(spawn2_id, zone->GetInstanceID(), (cur/1000)); - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn reset by death, repop in %d ms", spawn2_id, timer.GetRemainingTime()); + LogSpawns("Spawn2 [{}]: Spawn reset by death, repop in [{}] ms", spawn2_id, timer.GetRemainingTime()); //store it to database too } } @@ -712,12 +687,12 @@ void Spawn2::SpawnConditionChanged(const SpawnCondition &c, int16 old_value) { if(GetSpawnCondition() != c.condition_id) return; - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Notified that our spawn condition %d has changed from %d to %d. Our min value is %d.", spawn2_id, c.condition_id, old_value, c.value, condition_min_value); + LogSpawns("Spawn2 [{}]: Notified that our spawn condition [{}] has changed from [{}] to [{}]. Our min value is [{}]", spawn2_id, c.condition_id, old_value, c.value, condition_min_value); bool old_state = (old_value >= condition_min_value); bool new_state = (c.value >= condition_min_value); if(old_state == new_state) { - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Our threshold for this condition was not crossed. Doing nothing.", spawn2_id); + LogSpawns("Spawn2 [{}]: Our threshold for this condition was not crossed. Doing nothing", spawn2_id); return; //no change } @@ -725,50 +700,50 @@ void Spawn2::SpawnConditionChanged(const SpawnCondition &c, int16 old_value) { switch(c.on_change) { case SpawnCondition::DoNothing: //that was easy. - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Our condition is now %s. Taking no action on existing spawn.", spawn2_id, new_state?"enabled":"disabled"); + LogSpawns("Spawn2 [{}]: Our condition is now [{}]. Taking no action on existing spawn", spawn2_id, new_state?"enabled":"disabled"); break; case SpawnCondition::DoDepop: - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Our condition is now %s. Depoping our mob.", spawn2_id, new_state?"enabled":"disabled"); + LogSpawns("Spawn2 [{}]: Our condition is now [{}]. Depoping our mob", spawn2_id, new_state?"enabled":"disabled"); if(npcthis != nullptr) npcthis->Depop(false); //remove the current mob Reset(); //reset our spawn timer break; case SpawnCondition::DoRepop: - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Our condition is now %s. Forcing a repop.", spawn2_id, new_state?"enabled":"disabled"); + LogSpawns("Spawn2 [{}]: Our condition is now [{}]. Forcing a repop", spawn2_id, new_state?"enabled":"disabled"); if(npcthis != nullptr) npcthis->Depop(false); //remove the current mob Repop(); //repop break; case SpawnCondition::DoRepopIfReady: - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Our condition is now %s. Forcing a repop if repsawn timer is expired.", spawn2_id, new_state?"enabled":"disabled"); + LogSpawns("Spawn2 [{}]: Our condition is now [{}]. Forcing a repop if repsawn timer is expired", spawn2_id, new_state?"enabled":"disabled"); if(npcthis != nullptr) { - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Our npcthis is currently not null. The zone thinks it is %s. Forcing a depop.", spawn2_id, npcthis->GetName()); + LogSpawns("Spawn2 [{}]: Our npcthis is currently not null. The zone thinks it is [{}]. Forcing a depop", spawn2_id, npcthis->GetName()); npcthis->Depop(false); //remove the current mob npcthis = nullptr; } if(new_state) { // only get repawn timer remaining when the SpawnCondition is enabled. timer_remaining = database.GetSpawnTimeLeft(spawn2_id,zone->GetInstanceID()); - Log(Logs::Detail, Logs::Spawns,"Spawn2 %d: Our condition is now %s. The respawn timer_remaining is %d. Forcing a repop if it is <= 0.", spawn2_id, new_state?"enabled":"disabled", timer_remaining); + LogSpawns("Spawn2 [{}]: Our condition is now [{}]. The respawn timer_remaining is [{}]. Forcing a repop if it is <= 0", spawn2_id, new_state?"enabled":"disabled", timer_remaining); if(timer_remaining <= 0) Repop(); } else { - Log(Logs::Detail, Logs::Spawns,"Spawn2 %d: Our condition is now %s. Not checking respawn timer.", spawn2_id, new_state?"enabled":"disabled"); + LogSpawns("Spawn2 [{}]: Our condition is now [{}]. Not checking respawn timer", spawn2_id, new_state?"enabled":"disabled"); } break; default: if(c.on_change < SpawnCondition::DoSignalMin) { - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Our condition is now %s. Invalid on-change action %d.", spawn2_id, new_state?"enabled":"disabled", c.on_change); + LogSpawns("Spawn2 [{}]: Our condition is now [{}]. Invalid on-change action [{}]", spawn2_id, new_state?"enabled":"disabled", c.on_change); return; //unknown onchange action } int signal_id = c.on_change - SpawnCondition::DoSignalMin; - Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Our condition is now %s. Signaling our mob with %d.", spawn2_id, new_state?"enabled":"disabled", signal_id); + LogSpawns("Spawn2 [{}]: Our condition is now [{}]. Signaling our mob with [{}]", spawn2_id, new_state?"enabled":"disabled", signal_id); if(npcthis != nullptr) npcthis->SignalNPC(signal_id); } } void Zone::SpawnConditionChanged(const SpawnCondition &c, int16 old_value) { - Log(Logs::Detail, Logs::Spawns, "Zone notified that spawn condition %d has changed from %d to %d. Notifying all spawn points.", c.condition_id, old_value, c.value); + LogSpawns("Zone notified that spawn condition [{}] has changed from [{}] to [{}]. Notifying all spawn points", c.condition_id, old_value, c.value); LinkedListIterator iterator(spawn2_list); @@ -838,7 +813,7 @@ void SpawnConditionManager::Process() { EQTime::AddMinutes(cevent.period, &cevent.next); std::string t; EQTime::ToString(&cevent.next, t); - Log(Logs::Detail, Logs::Spawns, "Event %d: Will trigger again in %d EQ minutes at %s.", cevent.id, cevent.period, t.c_str()); + LogSpawns("Event [{}]: Will trigger again in [{}] EQ minutes at [{}]", cevent.id, cevent.period, t.c_str()); //save the next event time in the DB UpdateDBEvent(cevent); //find the next closest event timer. @@ -857,7 +832,7 @@ void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) { std::map::iterator condi; condi = spawn_conditions.find(event.condition_id); if(condi == spawn_conditions.end()) { - Log(Logs::Detail, Logs::Spawns, "Event %d: Unable to find condition %d to execute on.", event.id, event.condition_id); + LogSpawns("Event [{}]: Unable to find condition [{}] to execute on", event.id, event.condition_id); return; //unable to find the spawn condition to operate on } @@ -865,7 +840,7 @@ void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) { zone->zone_time.GetCurrentEQTimeOfDay(&tod); if(event.strict && (event.next.hour != tod.hour || event.next.day != tod.day || event.next.month != tod.month || event.next.year != tod.year)) { - Log(Logs::Detail, Logs::Spawns, "Event %d: Unable to execute. Condition is strict, and event time has already passed.", event.id); + LogSpawns("Event [{}]: Unable to execute. Condition is strict, and event time has already passed", event.id); return; } @@ -877,26 +852,26 @@ void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) { switch(event.action) { case SpawnEvent::ActionSet: new_value = event.argument; - Log(Logs::Detail, Logs::Spawns, "Event %d: Executing. Setting condition %d to %d.", event.id, event.condition_id, event.argument); + LogSpawns("Event [{}]: Executing. Setting condition [{}] to [{}]", event.id, event.condition_id, event.argument); break; case SpawnEvent::ActionAdd: new_value += event.argument; - Log(Logs::Detail, Logs::Spawns, "Event %d: Executing. Adding %d to condition %d, yeilding %d.", event.id, event.argument, event.condition_id, new_value); + LogSpawns("Event [{}]: Executing. Adding [{}] to condition [{}], yielding [{}]", event.id, event.argument, event.condition_id, new_value); break; case SpawnEvent::ActionSubtract: new_value -= event.argument; - Log(Logs::Detail, Logs::Spawns, "Event %d: Executing. Subtracting %d from condition %d, yeilding %d.", event.id, event.argument, event.condition_id, new_value); + LogSpawns("Event [{}]: Executing. Subtracting [{}] from condition [{}], yielding [{}]", event.id, event.argument, event.condition_id, new_value); break; case SpawnEvent::ActionMultiply: new_value *= event.argument; - Log(Logs::Detail, Logs::Spawns, "Event %d: Executing. Multiplying condition %d by %d, yeilding %d.", event.id, event.condition_id, event.argument, new_value); + LogSpawns("Event [{}]: Executing. Multiplying condition [{}] by [{}], yielding [{}]", event.id, event.condition_id, event.argument, new_value); break; case SpawnEvent::ActionDivide: new_value /= event.argument; - Log(Logs::Detail, Logs::Spawns, "Event %d: Executing. Dividing condition %d by %d, yeilding %d.", event.id, event.condition_id, event.argument, new_value); + LogSpawns("Event [{}]: Executing. Dividing condition [{}] by [{}], yielding [{}]", event.id, event.condition_id, event.argument, new_value); break; default: - Log(Logs::Detail, Logs::Spawns, "Event %d: Invalid event action type %d", event.id, event.action); + LogSpawns("Event [{}]: Invalid event action type [{}]", event.id, event.action); return; } @@ -966,7 +941,7 @@ bool SpawnConditionManager::LoadDBEvent(uint32 event_id, SpawnEvent &event, std: std::string timeAsString; EQTime::ToString(&event.next, timeAsString); - Log(Logs::Detail, Logs::Spawns, "(LoadDBEvent) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d. Will trigger at %s", event.enabled? "enabled": "disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict, timeAsString.c_str()); + LogSpawns("(LoadDBEvent) Loaded [{}] spawn event [{}] on condition [{}] with period [{}], action [{}], argument [{}], strict [{}]. Will trigger at [{}]", event.enabled? "enabled": "disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict, timeAsString.c_str()); return true; } @@ -993,7 +968,7 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in cond.on_change = (SpawnCondition::OnChange) atoi(row[1]); spawn_conditions[cond.condition_id] = cond; - Log(Logs::Detail, Logs::Spawns, "Loaded spawn condition %d with value %d and on_change %d", cond.condition_id, cond.value, cond.on_change); + LogSpawns("Loaded spawn condition [{}] with value [{}] and on_change [{}]", cond.condition_id, cond.value, cond.on_change); } //load values @@ -1022,33 +997,42 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in return false; } - for (auto row = results.begin(); row != results.end(); ++row) { - SpawnEvent event; + for (auto row = results.begin(); row != results.end(); ++row) { + SpawnEvent event; - event.id = atoi(row[0]); - event.condition_id = atoi(row[1]); - event.period = atoi(row[2]); + event.id = atoi(row[0]); + event.condition_id = atoi(row[1]); + event.period = atoi(row[2]); - if(event.period == 0) { - Log(Logs::General, Logs::Error, "Refusing to load spawn event #%d because it has a period of 0\n", event.id); - continue; - } + if (event.period == 0) { + LogError("Refusing to load spawn event #[{}] because it has a period of 0\n", event.id); + continue; + } - event.next.minute = atoi(row[3]); - event.next.hour = atoi(row[4]); - event.next.day = atoi(row[5]); - event.next.month = atoi(row[6]); - event.next.year = atoi(row[7]); + event.next.minute = atoi(row[3]); + event.next.hour = atoi(row[4]); + event.next.day = atoi(row[5]); + event.next.month = atoi(row[6]); + event.next.year = atoi(row[7]); - event.enabled = atoi(row[8])==0?false:true; - event.action = (SpawnEvent::Action) atoi(row[9]); - event.argument = atoi(row[10]); - event.strict = atoi(row[11])==0?false:true; + event.enabled = atoi(row[8]) == 0 ? false : true; + event.action = (SpawnEvent::Action) atoi(row[9]); + event.argument = atoi(row[10]); + event.strict = atoi(row[11]) == 0 ? false : true; - spawn_events.push_back(event); + spawn_events.push_back(event); - Log(Logs::Detail, Logs::Spawns, "(LoadSpawnConditions) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d", event.enabled? "enabled": "disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict); - } + LogSpawns( + "(LoadSpawnConditions) Loaded [{}] spawn event [{}] on condition [{}] with period [{}], action [{}], argument [{}], strict [{}]", + event.enabled ? "enabled" : "disabled", + event.id, + event.condition_id, + event.period, + event.action, + event.argument, + event.strict + ); + } //now we need to catch up on events that happened while we were away //and use them to alter just the condition variables. @@ -1080,31 +1064,33 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in if(!cevent.enabled) continue; - //watch for special case of all 0s, which means to reset next to now - if(cevent.next.year == 0 && cevent.next.month == 0 && cevent.next.day == 0 && cevent.next.hour == 0 && cevent.next.minute == 0) { - Log(Logs::Detail, Logs::Spawns, "Initial next trigger time set for spawn event %d", cevent.id); - memcpy(&cevent.next, &tod, sizeof(cevent.next)); - //add one period - EQTime::AddMinutes(cevent.period, &cevent.next); - //save it in the db. - UpdateDBEvent(cevent); - continue; //were done with this event. - } + //watch for special case of all 0s, which means to reset next to now + if (cevent.next.year == 0 && cevent.next.month == 0 && cevent.next.day == 0 && cevent.next.hour == 0 && + cevent.next.minute == 0) { + LogSpawns("Initial next trigger time set for spawn event [{}]", cevent.id); + memcpy(&cevent.next, &tod, sizeof(cevent.next)); + //add one period + EQTime::AddMinutes(cevent.period, &cevent.next); + //save it in the db. + UpdateDBEvent(cevent); + continue; //were done with this event. + } - bool ran = false; - while(EQTime::IsTimeBefore(&tod, &cevent.next)) { - Log(Logs::Detail, Logs::Spawns, "Catch up triggering on event %d", cevent.id); - //this event has been triggered. - //execute the event - if(!cevent.strict || StrictCheck) - ExecEvent(cevent, false); + bool ran = false; + while (EQTime::IsTimeBefore(&tod, &cevent.next)) { + LogSpawns("Catch up triggering on event [{}]", cevent.id); + //this event has been triggered. + //execute the event + if (!cevent.strict || StrictCheck) { + ExecEvent(cevent, false); + } - //add the period of the event to the trigger time - EQTime::AddMinutes(cevent.period, &cevent.next); - ran = true; - } + //add the period of the event to the trigger time + EQTime::AddMinutes(cevent.period, &cevent.next); + ran = true; + } - //only write it out if the event actually ran + //only write it out if the event actually ran if(ran) UpdateDBEvent(cevent); //save the event in the DB } @@ -1136,10 +1122,10 @@ void SpawnConditionManager::FindNearestEvent() { } } if (next_id == -1) { - Log(Logs::Detail, Logs::Spawns, "No spawn events enabled. Disabling next event."); + LogSpawns("No spawn events enabled. Disabling next event"); } else { - Log(Logs::Detail, Logs::Spawns, "Next event determined to be event %d", next_id); + LogSpawns("Next event determined to be event [{}]", next_id); } } @@ -1152,14 +1138,14 @@ void SpawnConditionManager::SetCondition(const char *zone_short, uint32 instance std::map::iterator condi; condi = spawn_conditions.find(condition_id); if(condi == spawn_conditions.end()) { - Log(Logs::Detail, Logs::Spawns, "Condition update received from world for %d, but we do not have that conditon.", condition_id); + LogSpawns("Condition update received from world for [{}], but we do not have that conditon", condition_id); return; //unable to find the spawn condition } SpawnCondition &cond = condi->second; if(cond.value == new_value) { - Log(Logs::Detail, Logs::Spawns, "Condition update received from world for %d with value %d, which is what we already have.", condition_id, new_value); + LogSpawns("Condition update received from world for [{}] with value [{}], which is what we already have", condition_id, new_value); return; } @@ -1168,7 +1154,7 @@ void SpawnConditionManager::SetCondition(const char *zone_short, uint32 instance //set our local value cond.value = new_value; - Log(Logs::Detail, Logs::Spawns, "Condition update received from world for %d with value %d", condition_id, new_value); + LogSpawns("Condition update received from world for [{}] with value [{}]", condition_id, new_value); //now we have to test each spawn point to see if it changed. zone->SpawnConditionChanged(cond, old_value); @@ -1179,14 +1165,14 @@ void SpawnConditionManager::SetCondition(const char *zone_short, uint32 instance std::map::iterator condi; condi = spawn_conditions.find(condition_id); if(condi == spawn_conditions.end()) { - Log(Logs::Detail, Logs::Spawns, "Local Condition update requested for %d, but we do not have that conditon.", condition_id); + LogSpawns("Local Condition update requested for [{}], but we do not have that conditon", condition_id); return; //unable to find the spawn condition } SpawnCondition &cond = condi->second; if(cond.value == new_value) { - Log(Logs::Detail, Logs::Spawns, "Local Condition update requested for %d with value %d, which is what we already have.", condition_id, new_value); + LogSpawns("Local Condition update requested for [{}] with value [{}], which is what we already have", condition_id, new_value); return; } @@ -1197,7 +1183,7 @@ void SpawnConditionManager::SetCondition(const char *zone_short, uint32 instance //save it in the DB too UpdateDBCondition(zone_short, instance_id, condition_id, new_value); - Log(Logs::Detail, Logs::Spawns, "Local Condition update requested for %d with value %d", condition_id, new_value); + LogSpawns("Local Condition update requested for [{}] with value [{}]", condition_id, new_value); //now we have to test each spawn point to see if it changed. zone->SpawnConditionChanged(cond, old_value); @@ -1207,7 +1193,7 @@ void SpawnConditionManager::SetCondition(const char *zone_short, uint32 instance //this is a remote spawn condition, update the DB and send //an update packet to the zone if its up - Log(Logs::Detail, Logs::Spawns, "Remote spawn condition %d set to %d. Updating DB and notifying world.", condition_id, new_value); + LogSpawns("Remote spawn condition [{}] set to [{}]. Updating DB and notifying world", condition_id, new_value); UpdateDBCondition(zone_short, instance_id, condition_id, new_value); @@ -1227,7 +1213,7 @@ void SpawnConditionManager::SetCondition(const char *zone_short, uint32 instance void SpawnConditionManager::ReloadEvent(uint32 event_id) { std::string zone_short_name; - Log(Logs::Detail, Logs::Spawns, "Requested to reload event %d from the database.", event_id); + LogSpawns("Requested to reload event [{}] from the database", event_id); //first look for the event in our local event list std::vector::iterator cur,end; @@ -1240,7 +1226,7 @@ void SpawnConditionManager::ReloadEvent(uint32 event_id) { //load the event into the old event slot if(!LoadDBEvent(event_id, cevent, zone_short_name)) { //unable to find the event in the database... - Log(Logs::Detail, Logs::Spawns, "Failed to reload event %d from the database.", event_id); + LogSpawns("Failed to reload event [{}] from the database", event_id); return; } //sync up our nearest event @@ -1253,7 +1239,7 @@ void SpawnConditionManager::ReloadEvent(uint32 event_id) { SpawnEvent e; if(!LoadDBEvent(event_id, e, zone_short_name)) { //unable to find the event in the database... - Log(Logs::Detail, Logs::Spawns, "Failed to reload event %d from the database.", event_id); + LogSpawns("Failed to reload event [{}] from the database", event_id); return; } @@ -1270,7 +1256,7 @@ void SpawnConditionManager::ReloadEvent(uint32 event_id) { void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool strict, bool reset_base) { - Log(Logs::Detail, Logs::Spawns, "Request to %s spawn event %d %sresetting trigger time", enabled?"enable":"disable", event_id, reset_base?"":"without "); + LogSpawns("Request to [{}] spawn event [{}] [{}]resetting trigger time", enabled?"enable":"disable", event_id, reset_base?"":"without "); //first look for the event in our local event list std::vector::iterator cur,end; @@ -1285,13 +1271,13 @@ void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool stri cevent.enabled = enabled; cevent.strict = strict; if(reset_base) { - Log(Logs::Detail, Logs::Spawns, "Spawn event %d located in this zone. State set. Trigger time reset (period %d).", event_id, cevent.period); + LogSpawns("Spawn event [{}] located in this zone. State set. Trigger time reset (period [{}])", event_id, cevent.period); //start with the time now zone->zone_time.GetCurrentEQTimeOfDay(&cevent.next); //advance the next time by our period EQTime::AddMinutes(cevent.period, &cevent.next); } else { - Log(Logs::Detail, Logs::Spawns, "Spawn event %d located in this zone. State changed.", event_id); + LogSpawns("Spawn event [{}] located in this zone. State changed", event_id); } //save the event in the DB @@ -1300,7 +1286,7 @@ void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool stri //sync up our nearest event FindNearestEvent(); } else { - Log(Logs::Detail, Logs::Spawns, "Spawn event %d located in this zone but no change was needed.", event_id); + LogSpawns("Spawn event [{}] located in this zone but no change was needed", event_id); } //even if we dont change anything, we still found it return; @@ -1319,24 +1305,24 @@ void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool stri SpawnEvent e; std::string zone_short_name; if(!LoadDBEvent(event_id, e, zone_short_name)) { - Log(Logs::Detail, Logs::Spawns, "Unable to find spawn event %d in the database.", event_id); + LogSpawns("Unable to find spawn event [{}] in the database", event_id); //unable to find the event in the database... return; } if(e.enabled == enabled && !reset_base) { - Log(Logs::Detail, Logs::Spawns, "Spawn event %d is not located in this zone but no change was needed.", event_id); + LogSpawns("Spawn event [{}] is not located in this zone but no change was needed", event_id); return; //no changes. } e.enabled = enabled; if(reset_base) { - Log(Logs::Detail, Logs::Spawns, "Spawn event %d is in zone %s. State set. Trigger time reset (period %d). Notifying world.", event_id, zone_short_name.c_str(), e.period); + LogSpawns("Spawn event [{}] is in zone [{}]. State set. Trigger time reset (period [{}]). Notifying world", event_id, zone_short_name.c_str(), e.period); //start with the time now zone->zone_time.GetCurrentEQTimeOfDay(&e.next); //advance the next time by our period EQTime::AddMinutes(e.period, &e.next); } else { - Log(Logs::Detail, Logs::Spawns, "Spawn event %d is in zone %s. State changed. Notifying world.", event_id, zone_short_name.c_str(), e.period); + LogSpawns("Spawn event [{}] is in zone [{}]. State changed. Notifying world", event_id, zone_short_name.c_str(), e.period); } //save the event in the DB UpdateDBEvent(e); @@ -1361,7 +1347,7 @@ int16 SpawnConditionManager::GetCondition(const char *zone_short, uint32 instanc condi = spawn_conditions.find(condition_id); if(condi == spawn_conditions.end()) { - Log(Logs::Detail, Logs::Spawns, "Unable to find local condition %d in Get request.", condition_id); + LogSpawns("Unable to find local condition [{}] in Get request", condition_id); return(0); //unable to find the spawn condition } @@ -1370,20 +1356,22 @@ int16 SpawnConditionManager::GetCondition(const char *zone_short, uint32 instanc } //this is a remote spawn condition, grab it from the DB - //load spawn conditions - std::string query = StringFormat("SELECT value FROM spawn_condition_values " - "WHERE zone = '%s' AND instance_id = %u AND id = %d", - zone_short, instance_id, condition_id); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - Log(Logs::Detail, Logs::Spawns, "Unable to query remote condition %d from zone %s in Get request.", condition_id, zone_short); - return 0; //dunno a better thing to do... - } + //load spawn conditions + std::string query = StringFormat( + "SELECT value FROM spawn_condition_values " + "WHERE zone = '%s' AND instance_id = %u AND id = %d", + zone_short, instance_id, condition_id + ); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogSpawns("Unable to query remote condition [{}] from zone [{}] in Get request", condition_id, zone_short); + return 0; //dunno a better thing to do... + } - if (results.RowCount() == 0) { - Log(Logs::Detail, Logs::Spawns, "Unable to load remote condition %d from zone %s in Get request.", condition_id, zone_short); - return 0; //dunno a better thing to do... - } + if (results.RowCount() == 0) { + LogSpawns("Unable to load remote condition [{}] from zone [{}] in Get request", condition_id, zone_short); + return 0; //dunno a better thing to do... + } auto row = results.begin(); diff --git a/zone/spawngroup.cpp b/zone/spawngroup.cpp index 41cd14060..cc3735386 100644 --- a/zone/spawngroup.cpp +++ b/zone/spawngroup.cpp @@ -28,11 +28,12 @@ extern EntityList entity_list; extern Zone *zone; -SpawnEntry::SpawnEntry(uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_limit) +SpawnEntry::SpawnEntry(uint32 in_NPCType, int in_chance, uint16 in_filter, uint8 in_npc_spawn_limit) { - NPCType = in_NPCType; - chance = in_chance; - npc_spawn_limit = in_npc_spawn_limit; + NPCType = in_NPCType; + chance = in_chance; + condition_value_filter = in_filter; + npc_spawn_limit = in_npc_spawn_limit; } SpawnGroup::SpawnGroup( @@ -64,11 +65,8 @@ SpawnGroup::SpawnGroup( despawn_timer = despawn_timer_in; } -uint32 SpawnGroup::GetNPCType() +uint32 SpawnGroup::GetNPCType(uint16 in_filter) { -#if EQDEBUG >= 10 - Log(Logs::General, Logs::None, "SpawnGroup[%08x]::GetNPCType()", (uint32) this); -#endif int npcType = 0; int totalchance = 0; @@ -87,6 +85,9 @@ uint32 SpawnGroup::GetNPCType() continue; } + if (se->condition_value_filter != in_filter) + continue; + totalchance += se->chance; possible.push_back(se); } @@ -94,7 +95,6 @@ uint32 SpawnGroup::GetNPCType() return 0; } - int32 roll = 0; roll = zone->random.Int(0, totalchance - 1); @@ -242,6 +242,7 @@ bool ZoneDatabase::LoadSpawnGroups(const char *zone_name, uint16 version, SpawnG spawnentry.spawngroupID, npcid, chance, + condition_value_filter, npc_types.spawn_limit AS sl FROM @@ -266,7 +267,8 @@ bool ZoneDatabase::LoadSpawnGroups(const char *zone_name, uint16 version, SpawnG auto new_spawn_entry = new SpawnEntry( atoi(row[1]), atoi(row[2]), - (row[3] ? atoi(row[3]) : 0) + atoi(row[3]), + (row[4] ? atoi(row[4]) : 0) ); SpawnGroup *spawn_group = spawn_group_list->GetSpawnGroup(atoi(row[0])); @@ -342,6 +344,7 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawn_group_id, SpawnGroupList *spawn (spawnentry.spawngroupID), spawnentry.npcid, spawnentry.chance, + spawnentry.condition_value_filter, spawngroup.spawn_limit FROM spawnentry, @@ -362,7 +365,8 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawn_group_id, SpawnGroupList *spawn auto new_spawn_entry = new SpawnEntry( atoi(row[1]), atoi(row[2]), - (row[3] ? atoi(row[3]) : 0) + atoi(row[3]), + (row[4] ? atoi(row[4]) : 0) ); SpawnGroup *spawn_group = spawn_group_list->GetSpawnGroup(atoi(row[0])); diff --git a/zone/spawngroup.h b/zone/spawngroup.h index 4b68a35eb..2a4c1af6c 100644 --- a/zone/spawngroup.h +++ b/zone/spawngroup.h @@ -25,10 +25,11 @@ class SpawnEntry { public: - SpawnEntry(uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_limit); + SpawnEntry(uint32 in_NPCType, int in_chance, uint16 in_filter, uint8 in_npc_spawn_limit); ~SpawnEntry() {} uint32 NPCType; int chance; + uint16 condition_value_filter; //this is a cached value from npc_types, for speed uint8 npc_spawn_limit; //max # of this entry which can be spawned in this zone @@ -52,7 +53,7 @@ public: ); ~SpawnGroup(); - uint32 GetNPCType(); + uint32 GetNPCType(uint16 condition_value_filter=1); void AddSpawnEntry(SpawnEntry *newEntry); uint32 id; float roamdist; diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index d9666efc6..b7cdebf02 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -631,7 +631,7 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { //make sure the attack and ranged timers are up //if the ranged timer is disabled, then they have no ranged weapon and shouldent be attacking anyhow if(!CanDoubleAttack && ((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check()))) { - Log(Logs::Detail, Logs::Combat, "Throwing attack canceled. Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); + LogCombat("Throwing attack canceled. Timer not up. Attack [{}], ranged [{}]", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); // The server and client timers are not exact matches currently, so this would spam too often if enabled //Message(0, "Error: Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); return; @@ -643,12 +643,12 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { const EQEmu::ItemInstance* Ammo = m_inv[EQEmu::invslot::slotAmmo]; if (!RangeWeapon || !RangeWeapon->IsClassCommon()) { - Log(Logs::Detail, Logs::Combat, "Ranged attack canceled. Missing or invalid ranged weapon (%d) in slot %d", GetItemIDAt(EQEmu::invslot::slotRange), EQEmu::invslot::slotRange); + LogCombat("Ranged attack canceled. Missing or invalid ranged weapon ([{}]) in slot [{}]", GetItemIDAt(EQEmu::invslot::slotRange), EQEmu::invslot::slotRange); Message(0, "Error: Rangeweapon: GetItem(%i)==0, you have no bow!", GetItemIDAt(EQEmu::invslot::slotRange)); return; } if (!Ammo || !Ammo->IsClassCommon()) { - Log(Logs::Detail, Logs::Combat, "Ranged attack canceled. Missing or invalid ammo item (%d) in slot %d", GetItemIDAt(EQEmu::invslot::slotAmmo), EQEmu::invslot::slotAmmo); + LogCombat("Ranged attack canceled. Missing or invalid ammo item ([{}]) in slot [{}]", GetItemIDAt(EQEmu::invslot::slotAmmo), EQEmu::invslot::slotAmmo); Message(0, "Error: Ammo: GetItem(%i)==0, you have no ammo!", GetItemIDAt(EQEmu::invslot::slotAmmo)); return; } @@ -657,17 +657,17 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { const EQEmu::ItemData* AmmoItem = Ammo->GetItem(); if (RangeItem->ItemType != EQEmu::item::ItemTypeBow) { - Log(Logs::Detail, Logs::Combat, "Ranged attack canceled. Ranged item is not a bow. type %d.", RangeItem->ItemType); + LogCombat("Ranged attack canceled. Ranged item is not a bow. type [{}]", RangeItem->ItemType); Message(0, "Error: Rangeweapon: Item %d is not a bow.", RangeWeapon->GetID()); return; } if (AmmoItem->ItemType != EQEmu::item::ItemTypeArrow) { - Log(Logs::Detail, Logs::Combat, "Ranged attack canceled. Ammo item is not an arrow. type %d.", AmmoItem->ItemType); + LogCombat("Ranged attack canceled. Ammo item is not an arrow. type [{}]", AmmoItem->ItemType); Message(0, "Error: Ammo: type %d != %d, you have the wrong type of ammo!", AmmoItem->ItemType, EQEmu::item::ItemTypeArrow); return; } - Log(Logs::Detail, Logs::Combat, "Shooting %s with bow %s (%d) and arrow %s (%d)", other->GetName(), RangeItem->Name, RangeItem->ID, AmmoItem->Name, AmmoItem->ID); + LogCombat("Shooting [{}] with bow [{}] ([{}]) and arrow [{}] ([{}])", other->GetName(), RangeItem->Name, RangeItem->ID, AmmoItem->Name, AmmoItem->ID); //look for ammo in inventory if we only have 1 left... if(Ammo->GetCharges() == 1) { @@ -694,7 +694,7 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { Ammo = baginst; ammo_slot = m_inv.CalcSlotId(r, i); found = true; - Log(Logs::Detail, Logs::Combat, "Using ammo from quiver stack at slot %d. %d in stack.", ammo_slot, Ammo->GetCharges()); + LogCombat("Using ammo from quiver stack at slot [{}]. [{}] in stack", ammo_slot, Ammo->GetCharges()); break; } } @@ -709,17 +709,17 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { if (aslot != INVALID_INDEX) { ammo_slot = aslot; Ammo = m_inv[aslot]; - Log(Logs::Detail, Logs::Combat, "Using ammo from inventory stack at slot %d. %d in stack.", ammo_slot, Ammo->GetCharges()); + LogCombat("Using ammo from inventory stack at slot [{}]. [{}] in stack", ammo_slot, Ammo->GetCharges()); } } } float range = RangeItem->Range + AmmoItem->Range + GetRangeDistTargetSizeMod(GetTarget()); - Log(Logs::Detail, Logs::Combat, "Calculated bow range to be %.1f", range); + LogCombat("Calculated bow range to be [{}]", range); range *= range; float dist = DistanceSquared(m_Position, other->GetPosition()); if(dist > range) { - Log(Logs::Detail, Logs::Combat, "Ranged attack out of range... client should catch this. (%f > %f).\n", dist, range); + LogCombat("Ranged attack out of range client should catch this. ([{}] > [{}]).\n", dist, range); MessageString(Chat::Red,TARGET_OUT_OF_RANGE);//Client enforces range and sends the message, this is a backup just incase. return; } @@ -747,9 +747,9 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { if (RangeItem->ExpendableArrow || !ChanceAvoidConsume || (ChanceAvoidConsume < 100 && zone->random.Int(0,99) > ChanceAvoidConsume)){ DeleteItemInInventory(ammo_slot, 1, true); - Log(Logs::Detail, Logs::Combat, "Consumed one arrow from slot %d", ammo_slot); + LogCombat("Consumed one arrow from slot [{}]", ammo_slot); } else { - Log(Logs::Detail, Logs::Combat, "Endless Quiver prevented ammo consumption."); + LogCombat("Endless Quiver prevented ammo consumption"); } CheckIncreaseSkill(EQEmu::skills::SkillArchery, GetTarget(), -15); @@ -808,7 +808,7 @@ void Mob::DoArcheryAttackDmg(Mob *other, const EQEmu::ItemInstance *RangeWeapon, SendItemAnimation(other, AmmoItem, EQEmu::skills::SkillArchery); } - Log(Logs::Detail, Logs::Combat, "Ranged attack hit %s.", other->GetName()); + LogCombat("Ranged attack hit [{}]", other->GetName()); uint32 hate = 0; int TotalDmg = 0; @@ -840,12 +840,10 @@ void Mob::DoArcheryAttackDmg(Mob *other, const EQEmu::ItemInstance *RangeWeapon, hate = ((WDmg + ADmg)); if (RuleB(Combat, ProjectileDmgOnImpact)) { - Log(Logs::Detail, Logs::Combat, "Bow and Arrow DMG %d, Max Damage %d.", WDmg, - MaxDmg); + LogCombat("Bow and Arrow DMG [{}], Max Damage [{}]", WDmg, MaxDmg); } else { - Log(Logs::Detail, Logs::Combat, "Bow DMG %d, Arrow DMG %d, Max Damage %d.", WDmg, - ADmg, MaxDmg); + LogCombat("Bow DMG [{}], Arrow DMG [{}], Max Damage [{}]", WDmg, ADmg, MaxDmg); } if (MaxDmg == 0) @@ -1087,7 +1085,7 @@ void NPC::RangedAttack(Mob* other) //make sure the attack and ranged timers are up //if the ranged timer is disabled, then they have no ranged weapon and shouldent be attacking anyhow if((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check())){ - Log(Logs::Detail, Logs::Combat, "Archery canceled. Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); + LogCombat("Archery canceled. Timer not up. Attack [{}], ranged [{}]", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); return; } @@ -1223,7 +1221,7 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 //make sure the attack and ranged timers are up //if the ranged timer is disabled, then they have no ranged weapon and shouldent be attacking anyhow if((!CanDoubleAttack && (attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check()))) { - Log(Logs::Detail, Logs::Combat, "Throwing attack canceled. Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); + LogCombat("Throwing attack canceled. Timer not up. Attack [{}], ranged [{}]", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); // The server and client timers are not exact matches currently, so this would spam too often if enabled //Message(0, "Error: Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); return; @@ -1233,19 +1231,19 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 const EQEmu::ItemInstance* RangeWeapon = m_inv[EQEmu::invslot::slotRange]; if (!RangeWeapon || !RangeWeapon->IsClassCommon()) { - Log(Logs::Detail, Logs::Combat, "Ranged attack canceled. Missing or invalid ranged weapon (%d) in slot %d", GetItemIDAt(EQEmu::invslot::slotRange), EQEmu::invslot::slotRange); + LogCombat("Ranged attack canceled. Missing or invalid ranged weapon ([{}]) in slot [{}]", GetItemIDAt(EQEmu::invslot::slotRange), EQEmu::invslot::slotRange); Message(0, "Error: Rangeweapon: GetItem(%i)==0, you have nothing to throw!", GetItemIDAt(EQEmu::invslot::slotRange)); return; } const EQEmu::ItemData* item = RangeWeapon->GetItem(); if (item->ItemType != EQEmu::item::ItemTypeLargeThrowing && item->ItemType != EQEmu::item::ItemTypeSmallThrowing) { - Log(Logs::Detail, Logs::Combat, "Ranged attack canceled. Ranged item %d is not a throwing weapon. type %d.", item->ItemType); + LogCombat("Ranged attack canceled. Ranged item [{}] is not a throwing weapon. type [{}]", item->ItemType); Message(0, "Error: Rangeweapon: GetItem(%i)==0, you have nothing useful to throw!", GetItemIDAt(EQEmu::invslot::slotRange)); return; } - Log(Logs::Detail, Logs::Combat, "Throwing %s (%d) at %s", item->Name, item->ID, other->GetName()); + LogCombat("Throwing [{}] ([{}]) at [{}]", item->Name, item->ID, other->GetName()); if(RangeWeapon->GetCharges() == 1) { //first check ammo @@ -1254,7 +1252,7 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 //more in the ammo slot, use it RangeWeapon = AmmoItem; ammo_slot = EQEmu::invslot::slotAmmo; - Log(Logs::Detail, Logs::Combat, "Using ammo from ammo slot, stack at slot %d. %d in stack.", ammo_slot, RangeWeapon->GetCharges()); + LogCombat("Using ammo from ammo slot, stack at slot [{}]. [{}] in stack", ammo_slot, RangeWeapon->GetCharges()); } else { //look through our inventory for more int32 aslot = m_inv.HasItem(item->ID, 1, invWherePersonal); @@ -1262,17 +1260,17 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 //the item wont change, but the instance does, not that it matters ammo_slot = aslot; RangeWeapon = m_inv[aslot]; - Log(Logs::Detail, Logs::Combat, "Using ammo from inventory slot, stack at slot %d. %d in stack.", ammo_slot, RangeWeapon->GetCharges()); + LogCombat("Using ammo from inventory slot, stack at slot [{}]. [{}] in stack", ammo_slot, RangeWeapon->GetCharges()); } } } float range = item->Range + GetRangeDistTargetSizeMod(other); - Log(Logs::Detail, Logs::Combat, "Calculated bow range to be %.1f", range); + LogCombat("Calculated bow range to be [{}]", range); range *= range; float dist = DistanceSquared(m_Position, other->GetPosition()); if(dist > range) { - Log(Logs::Detail, Logs::Combat, "Throwing attack out of range... client should catch this. (%f > %f).\n", dist, range); + LogCombat("Throwing attack out of range client should catch this. ([{}] > [{}]).\n", dist, range); MessageString(Chat::Red,TARGET_OUT_OF_RANGE);//Client enforces range and sends the message, this is a backup just incase. return; } @@ -1339,7 +1337,7 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQEmu::ItemInstance *RangeWeapon SendItemAnimation(other, AmmoItem, EQEmu::skills::SkillThrowing); } - Log(Logs::Detail, Logs::Combat, "Throwing attack hit %s.", other->GetName()); + LogCombat("Throwing attack hit [{}]", other->GetName()); int WDmg = 0; @@ -1377,7 +1375,7 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQEmu::ItemInstance *RangeWeapon DoAttack(other, my_hit); TotalDmg = my_hit.damage_done; - Log(Logs::Detail, Logs::Combat, "Item DMG %d. Hit for damage %d", WDmg, TotalDmg); + LogCombat("Item DMG [{}]. Hit for damage [{}]", WDmg, TotalDmg); } else { TotalDmg = DMG_INVULNERABLE; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index cc9ead157..501cf752a 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -489,7 +489,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if(!target_zone) { #ifdef SPELL_EFFECT_SPAM - Log(Logs::General, Logs::None, "Succor/Evacuation Spell In Same Zone."); + LogDebug("Succor/Evacuation Spell In Same Zone"); #endif if(IsClient()) CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), x, y, z, heading, 0, EvacToSafeCoords); @@ -498,7 +498,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } else { #ifdef SPELL_EFFECT_SPAM - Log(Logs::General, Logs::None, "Succor/Evacuation Spell To Another Zone."); + LogDebug("Succor/Evacuation Spell To Another Zone"); #endif if(IsClient()) CastToClient()->MovePC(target_zone, x, y, z, heading); @@ -710,7 +710,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove stun_resist += aabonuses.StunResist; if (stun_resist <= 0 || zone->random.Int(0,99) >= stun_resist) { - Log(Logs::Detail, Logs::Combat, "Stunned. We had %d percent resist chance.", stun_resist); + LogCombat("Stunned. We had [{}] percent resist chance", stun_resist); if (caster && caster->IsClient()) effect_value += effect_value*caster->GetFocusEffect(focusFcStunTimeMod, spell_id)/100; @@ -720,7 +720,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (IsClient()) MessageString(Chat::Stun, SHAKE_OFF_STUN); - Log(Logs::Detail, Logs::Combat, "Stun Resisted. We had %d percent resist chance.", stun_resist); + LogCombat("Stun Resisted. We had [{}] percent resist chance", stun_resist); } } break; @@ -1678,7 +1678,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (IsCorpse() && CastToCorpse()->IsPlayerCorpse()) { if(caster) - Log(Logs::Detail, Logs::Spells, " corpse being rezzed using spell %i by %s", + LogSpells(" corpse being rezzed using spell [{}] by [{}]", spell_id, caster->GetName()); CastToCorpse()->CastRezz(spell_id, caster); @@ -1810,7 +1810,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } else { MessageString(Chat::LightBlue, TARGET_NOT_FOUND); - Log(Logs::General, Logs::Error, "%s attempted to cast spell id %u with spell effect SE_SummonCorpse, but could not cast target into a Client object.", GetCleanName(), spell_id); + LogError("[{}] attempted to cast spell id [{}] with spell effect SE_SummonCorpse, but could not cast target into a Client object", GetCleanName(), spell_id); } } @@ -3060,7 +3060,7 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, int oval = effect_value; int mod = ApplySpellEffectiveness(spell_id, instrument_mod, true, caster_id); effect_value = effect_value * mod / 10; - Log(Logs::Detail, Logs::Spells, "Effect value %d altered with bard modifier of %d to yeild %d", + LogSpells("Effect value [{}] altered with bard modifier of [{}] to yeild [{}]", oval, mod, effect_value); } @@ -3123,7 +3123,7 @@ snare has both of them negative, yet their range should work the same: updownsign = 1; } - Log(Logs::Detail, Logs::Spells, "CSEV: spell %d, formula %d, base %d, max %d, lvl %d. Up/Down %d", + LogSpells("CSEV: spell [{}], formula [{}], base [{}], max [{}], lvl [{}]. Up/Down [{}]", spell_id, formula, base, max, caster_level, updownsign); switch(formula) @@ -3345,7 +3345,7 @@ snare has both of them negative, yet their range should work the same: result = ubase * (caster_level * (formula - 2000) + 1); } else - Log(Logs::General, Logs::None, "Unknown spell effect value forumula %d", formula); + LogDebug("Unknown spell effect value forumula [{}]", formula); } } @@ -3370,7 +3370,7 @@ snare has both of them negative, yet their range should work the same: if (base < 0 && result > 0) result *= -1; - Log(Logs::Detail, Logs::Spells, "Result: %d (orig %d), cap %d %s", result, oresult, max, (base < 0 && result > 0)?"Inverted due to negative base":""); + LogSpells("Result: [{}] (orig [{}]), cap [{}] [{}]", result, oresult, max, (base < 0 && result > 0)?"Inverted due to negative base":""); return result; } @@ -3397,12 +3397,12 @@ void Mob::BuffProcess() --buffs[buffs_i].ticsremaining; if (buffs[buffs_i].ticsremaining < 0) { - Log(Logs::Detail, Logs::Spells, "Buff %d in slot %d has expired. Fading.", buffs[buffs_i].spellid, buffs_i); + LogSpells("Buff [{}] in slot [{}] has expired. Fading", buffs[buffs_i].spellid, buffs_i); BuffFadeBySlot(buffs_i); } else { - Log(Logs::Detail, Logs::Spells, "Buff %d in slot %d has %d tics remaining.", buffs[buffs_i].spellid, buffs_i, buffs[buffs_i].ticsremaining); + LogSpells("Buff [{}] in slot [{}] has [{}] tics remaining", buffs[buffs_i].spellid, buffs_i, buffs[buffs_i].ticsremaining); } } else if (IsClient() && !(CastToClient()->ClientVersionBit() & EQEmu::versions::maskSoFAndLater)) @@ -3733,7 +3733,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) if (IsClient() && !CastToClient()->IsDead()) CastToClient()->MakeBuffFadePacket(buffs[slot].spellid, slot); - Log(Logs::Detail, Logs::Spells, "Fading buff %d from slot %d", buffs[slot].spellid, slot); + LogSpells("Fading buff [{}] from slot [{}]", buffs[slot].spellid, slot); if(spells[buffs[slot].spellid].viral_targets > 0) { bool last_virus = true; @@ -4712,7 +4712,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo return 0; break; default: - Log(Logs::General, Logs::Normal, "CalcFocusEffect: unknown limit spelltype %d", + LogInfo("CalcFocusEffect: unknown limit spelltype [{}]", focus_spell.base[i]); } break; @@ -5090,7 +5090,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo // this spits up a lot of garbage when calculating spell focuses // since they have all kinds of extra effects on them. default: - Log(Logs::General, Logs::Normal, "CalcFocusEffect: unknown effectid %d", + LogInfo("CalcFocusEffect: unknown effectid [{}]", focus_spell.effectid[i]); #endif } diff --git a/zone/spells.cpp b/zone/spells.cpp index 9392ab7af..6d2e0d140 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -158,7 +158,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, uint32 timer, uint32 timer_duration, int16 *resist_adjust, uint32 aa_id) { - Log(Logs::Detail, Logs::Spells, "CastSpell called for spell %s (%d) on entity %d, slot %d, time %d, mana %d, from item slot %d", + LogSpells("CastSpell called for spell [{}] ([{}]) on entity [{}], slot [{}], time [{}], mana [{}], from item slot [{}]", (IsValidSpell(spell_id))?spells[spell_id].name:"UNKNOWN SPELL", spell_id, target_id, static_cast(slot), cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot); if(casting_spell_id == spell_id) @@ -177,7 +177,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, (IsAmnesiad() && IsDiscipline(spell_id)) ) { - Log(Logs::Detail, Logs::Spells, "Spell casting canceled: not able to cast now. Valid? %d, casting %d, waiting? %d, spellend? %d, stunned? %d, feared? %d, mezed? %d, silenced? %d, amnesiad? %d", + LogSpells("Spell casting canceled: not able to cast now. Valid? [{}], casting [{}], waiting? [{}], spellend? [{}], stunned? [{}], feared? [{}], mezed? [{}], silenced? [{}], amnesiad? [{}]", IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled(), IsStunned(), IsFeared(), IsMezzed(), IsSilenced(), IsAmnesiad() ); if(IsSilenced() && !IsDiscipline(spell_id)) MessageString(Chat::Red, SILENCED_STRING); @@ -215,7 +215,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, //cannot cast under divine aura if(DivineAura()) { - Log(Logs::Detail, Logs::Spells, "Spell casting canceled: cannot cast while Divine Aura is in effect."); + LogSpells("Spell casting canceled: cannot cast while Divine Aura is in effect"); InterruptSpell(173, 0x121, false); return(false); } @@ -237,7 +237,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, } if (HasActiveSong() && IsBardSong(spell_id)) { - Log(Logs::Detail, Logs::Spells, "Casting a new song while singing a song. Killing old song %d.", bardsong); + LogSpells("Casting a new song while singing a song. Killing old song [{}]", bardsong); //Note: this does NOT tell the client _StopSong(); } @@ -252,7 +252,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, if ((itm->GetItem()->Click.Type == EQEmu::item::ItemEffectEquipClick) && !(itm->GetItem()->Classes & bitmask)) { if (CastToClient()->ClientVersion() < EQEmu::versions::ClientVersion::SoF) { // They are casting a spell from an item that requires equipping but shouldn't let them equip it - Log(Logs::General, Logs::Error, "HACKER: %s (account: %s) attempted to click an equip-only effect on item %s (id: %d) which they shouldn't be able to equip!", + LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) which they shouldn't be able to equip!", CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID); database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item with an invalid class"); } @@ -264,7 +264,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, if ((itm->GetItem()->Click.Type == EQEmu::item::ItemEffectClick2) && !(itm->GetItem()->Classes & bitmask)) { if (CastToClient()->ClientVersion() < EQEmu::versions::ClientVersion::SoF) { // They are casting a spell from an item that they don't meet the race/class requirements to cast - Log(Logs::General, Logs::Error, "HACKER: %s (account: %s) attempted to click a race/class restricted effect on item %s (id: %d) which they shouldn't be able to click!", + LogError("HACKER: [{}] (account: [{}]) attempted to click a race/class restricted effect on item [{}] (id: [{}]) which they shouldn't be able to click!", CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID); database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking race/class restricted item with an invalid class"); } @@ -285,7 +285,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, if (itm && (itm->GetItem()->Click.Type == EQEmu::item::ItemEffectEquipClick) && item_slot > EQEmu::invslot::EQUIPMENT_END){ if (CastToClient()->ClientVersion() < EQEmu::versions::ClientVersion::SoF) { // They are attempting to cast a must equip clicky without having it equipped - Log(Logs::General, Logs::Error, "HACKER: %s (account: %s) attempted to click an equip-only effect on item %s (id: %d) without equiping it!", CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID); + LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) without equiping it!", CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID); database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item without equiping it"); } else { @@ -344,7 +344,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, const SPDat_Spell_Struct &spell = spells[spell_id]; - Log(Logs::Detail, Logs::Spells, "DoCastSpell called for spell %s (%d) on entity %d, slot %d, time %d, mana %d, from item %d", + LogSpells("DoCastSpell called for spell [{}] ([{}]) on entity [{}], slot [{}], time [{}], mana [{}], from item [{}]", spell.name, spell_id, target_id, static_cast(slot), cast_time, mana_cost, item_slot==0xFFFFFFFF?999:item_slot); casting_spell_id = spell_id; @@ -364,7 +364,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE; uint32 use_mana = ((spells[spell_id].mana) / 4); - Log(Logs::Detail, Logs::Spells, "Spell casting canceled: fizzled. %d mana has been consumed", use_mana); + LogSpells("Spell casting canceled: fizzled. [{}] mana has been consumed", use_mana); // fizzle 1/4 the mana away Mob::SetMana(GetMana() - use_mana); // We send StopCasting which will update mana @@ -395,7 +395,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, } SaveSpellLoc(); - Log(Logs::Detail, Logs::Spells, "Casting %d Started at (%.3f,%.3f,%.3f)", spell_id, m_SpellLocation.x, m_SpellLocation.y, m_SpellLocation.z); + LogSpells("Casting [{}] Started at ({},{},{})", spell_id, m_SpellLocation.x, m_SpellLocation.y, m_SpellLocation.z); // if this spell doesn't require a target, or if it's an optional target // and a target wasn't provided, then it's us; unless TGB is on and this @@ -408,7 +408,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, spell.targettype == ST_Beam || spell.targettype == ST_TargetOptional) && target_id == 0) { - Log(Logs::Detail, Logs::Spells, "Spell %d auto-targeted the caster. Group? %d, target type %d", spell_id, IsGroupSpell(spell_id), spell.targettype); + LogSpells("Spell [{}] auto-targeted the caster. Group? [{}], target type [{}]", spell_id, IsGroupSpell(spell_id), spell.targettype); target_id = GetID(); } @@ -425,7 +425,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, // we checked for spells not requiring targets above if(target_id == 0) { - Log(Logs::Detail, Logs::Spells, "Spell Error: no target. spell=%d", spell_id); + LogSpells("Spell Error: no target. spell=[{}]", spell_id); if(IsClient()) { //clients produce messages... npcs should not for this case MessageString(Chat::Red, SPELL_NEED_TAR); @@ -458,7 +458,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, { mana_cost = 0; } else { - Log(Logs::Detail, Logs::Spells, "Spell Error not enough mana spell=%d mymana=%d cost=%d\n", spell_id, my_curmana, mana_cost); + LogSpells("Spell Error not enough mana spell=[{}] mymana=[{}] cost=[{}]\n", spell_id, my_curmana, mana_cost); if(IsClient()) { //clients produce messages... npcs should not for this case MessageString(Chat::Red, INSUFFICIENT_MANA); @@ -479,7 +479,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, casting_spell_resist_adjust = resist_adjust; - Log(Logs::Detail, Logs::Spells, "Spell %d: Casting time %d (orig %d), mana cost %d", + LogSpells("Spell [{}]: Casting time [{}] (orig [{}]), mana cost [{}]", spell_id, cast_time, orgcasttime, mana_cost); // now tell the people in the area -- we ALWAYS want to send this, even instant cast spells. @@ -573,7 +573,7 @@ bool Mob::DoCastingChecks() if (RuleB(Spells, BuffLevelRestrictions)) { // casting_spell_targetid is guaranteed to be what we went, check for ST_Self for now should work though if (spell_target && spells[spell_id].targettype != ST_Self && !spell_target->CheckSpellLevelRestriction(spell_id)) { - Log(Logs::Detail, Logs::Spells, "Spell %d failed: recipient did not meet the level restrictions", spell_id); + LogSpells("Spell [{}] failed: recipient did not meet the level restrictions", spell_id); if (!IsBardSong(spell_id)) MessageString(Chat::SpellFailure, SPELL_TOO_POWERFUL); return false; @@ -757,6 +757,8 @@ bool Client::CheckFizzle(uint16 spell_id) act_skill = GetSkill(spells[spell_id].skill); act_skill += GetLevel(); // maximum of whatever the client can cheat + act_skill += itembonuses.adjusted_casting_skill + spellbonuses.adjusted_casting_skill + aabonuses.adjusted_casting_skill; + LogSpells("Adjusted casting skill: [{}]+[{}]+[{}]+[{}]+[{}]=[{}]", GetSkill(spells[spell_id].skill), GetLevel(), itembonuses.adjusted_casting_skill, spellbonuses.adjusted_casting_skill, aabonuses.adjusted_casting_skill, act_skill); //spell specialization float specialize = GetSpecializeSkillValue(spell_id); @@ -808,7 +810,7 @@ bool Client::CheckFizzle(uint16 spell_id) float fizzle_roll = zone->random.Real(0, 100); - Log(Logs::Detail, Logs::Spells, "Check Fizzle %s spell %d fizzlechance: %0.2f%% diff: %0.2f roll: %0.2f", GetName(), spell_id, fizzlechance, diff, fizzle_roll); + LogSpells("Check Fizzle [{}] spell [{}] fizzlechance: [{}] diff: [{}] roll: [{}]", GetName(), spell_id, fizzlechance, diff, fizzle_roll); if(fizzle_roll > fizzlechance) return(true); @@ -867,7 +869,7 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid) ZeroCastingVars(); // resets all the state keeping stuff - Log(Logs::Detail, Logs::Spells, "Spell %d has been interrupted.", spellid); + LogSpells("Spell [{}] has been interrupted", spellid); if(!spellid) return; @@ -970,7 +972,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if(!CastToClient()->GetPTimers().Expired(&database, pTimerSpellStart + spell_id, false)) { //should we issue a message or send them a spell gem packet? MessageString(Chat::Red, SPELL_RECAST); - Log(Logs::Detail, Logs::Spells, "Casting of %d canceled: spell reuse timer not expired", spell_id); + LogSpells("Casting of [{}] canceled: spell reuse timer not expired", spell_id); StopCasting(); return; } @@ -984,7 +986,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo { if(!CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + itm->GetItem()->RecastType), false)) { MessageString(Chat::Red, SPELL_RECAST); - Log(Logs::Detail, Logs::Spells, "Casting of %d canceled: item spell reuse timer not expired", spell_id); + LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id); StopCasting(); return; } @@ -993,7 +995,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if(!IsValidSpell(spell_id)) { - Log(Logs::Detail, Logs::Spells, "Casting of %d canceled: invalid spell id", spell_id); + LogSpells("Casting of [{}] canceled: invalid spell id", spell_id); InterruptSpell(); return; } @@ -1004,7 +1006,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo { if(delaytimer) { - Log(Logs::Detail, Logs::Spells, "Casting of %d canceled: recast too quickly", spell_id); + LogSpells("Casting of [{}] canceled: recast too quickly", spell_id); Message(Chat::Red, "You are unable to focus."); InterruptSpell(); return; @@ -1014,7 +1016,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo // make sure they aren't somehow casting 2 timed spells at once if (casting_spell_id != spell_id) { - Log(Logs::Detail, Logs::Spells, "Casting of %d canceled: already casting", spell_id); + LogSpells("Casting of [{}] canceled: already casting", spell_id); MessageString(Chat::Red,ALREADY_CASTING); InterruptSpell(); return; @@ -1029,7 +1031,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo { if (IsBardSong(spell_id)) { if(spells[spell_id].buffduration == 0xFFFF) { - Log(Logs::Detail, Logs::Spells, "Bard song %d not applying bard logic because duration. dur=%d, recast=%d", spells[spell_id].buffduration); + LogSpells("Bard song [{}] not applying bard logic because duration. dur=[{}], recast=[{}]", spells[spell_id].buffduration); } else { // So long recast bard songs need special bard logic, although the effects don't repulse like other songs // This is basically a hack to get that effect @@ -1045,7 +1047,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo bardsong_target_id = spell_target->GetID(); bardsong_timer.Start(6000); } - Log(Logs::Detail, Logs::Spells, "Bard song %d started: slot %d, target id %d", bardsong, bardsong_slot, bardsong_target_id); + LogSpells("Bard song [{}] started: slot [{}], target id [{}]", bardsong, (int) bardsong_slot, bardsong_target_id); bard_song_mode = true; } } @@ -1124,10 +1126,10 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo } } - Log(Logs::Detail, Logs::Spells, "Checking Interruption: spell x: %f spell y: %f cur x: %f cur y: %f channelchance %f channeling skill %d\n", GetSpellX(), GetSpellY(), GetX(), GetY(), channelchance, GetSkill(EQEmu::skills::SkillChanneling)); + LogSpells("Checking Interruption: spell x: [{}] spell y: [{}] cur x: [{}] cur y: [{}] channelchance [{}] channeling skill [{}]\n", GetSpellX(), GetSpellY(), GetX(), GetY(), channelchance, GetSkill(EQEmu::skills::SkillChanneling)); if(!spells[spell_id].uninterruptable && zone->random.Real(0, 100) > channelchance) { - Log(Logs::Detail, Logs::Spells, "Casting of %d canceled: interrupted.", spell_id); + LogSpells("Casting of [{}] canceled: interrupted", spell_id); InterruptSpell(); return; } @@ -1149,10 +1151,10 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if(IsClient()) { int reg_focus = CastToClient()->GetFocusEffect(focusReagentCost,spell_id);//Client only if(zone->random.Roll(reg_focus)) { - Log(Logs::Detail, Logs::Spells, "Spell %d: Reagent focus item prevented reagent consumption (%d chance)", spell_id, reg_focus); + LogSpells("Spell [{}]: Reagent focus item prevented reagent consumption ([{}] chance)", spell_id, reg_focus); } else { if(reg_focus > 0) - Log(Logs::Detail, Logs::Spells, "Spell %d: Reagent focus item failed to prevent reagent consumption (%d chance)", spell_id, reg_focus); + LogSpells("Spell [{}]: Reagent focus item failed to prevent reagent consumption ([{}] chance)", spell_id, reg_focus); Client *c = this->CastToClient(); int component, component_count, inv_slot_id; bool missingreags = false; @@ -1205,11 +1207,11 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo break; default: // some non-instrument component. Let it go, but record it in the log - Log(Logs::Detail, Logs::Spells, "Something odd happened: Song %d required component %d", spell_id, component); + LogSpells("Something odd happened: Song [{}] required component [{}]", spell_id, component); } if(!HasInstrument) { // if the instrument is missing, log it and interrupt the song - Log(Logs::Detail, Logs::Spells, "Song %d: Canceled. Missing required instrument %d", spell_id, component); + LogSpells("Song [{}]: Canceled. Missing required instrument [{}]", spell_id, component); if(c->GetGM()) c->Message(Chat::White, "Your GM status allows you to finish casting even though you're missing a required instrument."); else { @@ -1235,12 +1237,12 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo const EQEmu::ItemData *item = database.GetItem(component); if(item) { c->MessageString(Chat::Red, MISSING_SPELL_COMP_ITEM, item->Name); - Log(Logs::Detail, Logs::Spells, "Spell %d: Canceled. Missing required reagent %s (%d)", spell_id, item->Name, component); + LogSpells("Spell [{}]: Canceled. Missing required reagent [{}] ([{}])", spell_id, item->Name, component); } else { char TempItemName[64]; strcpy((char*)&TempItemName, "UNKNOWN"); - Log(Logs::Detail, Logs::Spells, "Spell %d: Canceled. Missing required reagent %s (%d)", spell_id, TempItemName, component); + LogSpells("Spell [{}]: Canceled. Missing required reagent [{}] ([{}])", spell_id, TempItemName, component); } } } // end bard/not bard ifs @@ -1266,7 +1268,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if (component == -1 || noexpend == component) continue; component_count = spells[spell_id].component_counts[t_count]; - Log(Logs::Detail, Logs::Spells, "Spell %d: Consuming %d of spell component item id %d", spell_id, component_count, component); + LogSpells("Spell [{}]: Consuming [{}] of spell component item id [{}]", spell_id, component_count, component); // Components found, Deleting // now we go looking for and deleting the items one by one for(int s = 0; s < component_count; s++) @@ -1331,7 +1333,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo { if(!CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + recasttype), false)) { MessageString(Chat::Red, SPELL_RECAST); - Log(Logs::Detail, Logs::Spells, "Casting of %d canceled: item spell reuse timer not expired", spell_id); + LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id); StopCasting(); return; } @@ -1352,15 +1354,15 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if(fromaug) { charges = -1; } //Don't destroy the parent item if(charges > -1) { // charged item, expend a charge - Log(Logs::Detail, Logs::Spells, "Spell %d: Consuming a charge from item %s (%d) which had %d/%d charges.", spell_id, inst->GetItem()->Name, inst->GetItem()->ID, inst->GetCharges(), inst->GetItem()->MaxCharges); + LogSpells("Spell [{}]: Consuming a charge from item [{}] ([{}]) which had [{}]/[{}] charges", spell_id, inst->GetItem()->Name, inst->GetItem()->ID, inst->GetCharges(), inst->GetItem()->MaxCharges); DeleteChargeFromSlot = inventory_slot; } else { - Log(Logs::Detail, Logs::Spells, "Spell %d: Cast from unlimited charge item %s (%d) (%d charges)", spell_id, inst->GetItem()->Name, inst->GetItem()->ID, inst->GetItem()->MaxCharges); + LogSpells("Spell [{}]: Cast from unlimited charge item [{}] ([{}]) ([{}] charges)", spell_id, inst->GetItem()->Name, inst->GetItem()->ID, inst->GetItem()->MaxCharges); } } else { - Log(Logs::Detail, Logs::Spells, "Item used to cast spell %d was missing from inventory slot %d after casting!", spell_id, inventory_slot); + LogSpells("Item used to cast spell [{}] was missing from inventory slot [{}] after casting!", spell_id, inventory_slot); Message(Chat::Red, "Casting Error: Active casting item not found in inventory slot %i", inventory_slot); InterruptSpell(); return; @@ -1370,7 +1372,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo // we're done casting, now try to apply the spell if( !SpellFinished(spell_id, spell_target, slot, mana_used, inventory_slot, resist_adjust) ) { - Log(Logs::Detail, Logs::Spells, "Casting of %d canceled: SpellFinished returned false.", spell_id); + LogSpells("Casting of [{}] canceled: SpellFinished returned false", spell_id); // most of the cases we return false have a message already or are logic errors that shouldn't happen // if there are issues I guess we can do something else, but this should work StopCasting(); @@ -1413,7 +1415,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo c->SetLinkedSpellReuseTimer(spells[spell_id].EndurTimerIndex, spells[spell_id].recast_time / 1000); c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar); } - Log(Logs::Detail, Logs::Spells, "Bard song %d should be started", spell_id); + LogSpells("Bard song [{}] should be started", spell_id); } else { @@ -1451,7 +1453,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo delaytimer = true; spellend_timer.Start(10, true); - Log(Logs::Detail, Logs::Spells, "Spell casting of %d is finished.", spell_id); + LogSpells("Spell casting of [{}] is finished", spell_id); } @@ -1498,7 +1500,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce && (IsGrouped() // still self only if not grouped || IsRaidGrouped()) && (HasProjectIllusion())){ - Log(Logs::Detail, Logs::AA, "Project Illusion overwrote target caster: %s spell id: %d was ON", GetName(), spell_id); + LogAA("Project Illusion overwrote target caster: [{}] spell id: [{}] was ON", GetName(), spell_id); targetType = ST_GroupClientAndPet; } @@ -1586,7 +1588,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce ) { //invalid target - Log(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target of body type %d (undead)", spell_id, mob_body); + LogSpells("Spell [{}] canceled: invalid target of body type [{}] (undead)", spell_id, mob_body); if(!spell_target) MessageString(Chat::Red,SPELL_NEED_TAR); else @@ -1601,7 +1603,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce if(!spell_target || (mob_body != BT_Summoned && mob_body != BT_Summoned2 && mob_body != BT_Summoned3)) { //invalid target - Log(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target of body type %d (summoned)", spell_id, mob_body); + LogSpells("Spell [{}] canceled: invalid target of body type [{}] (summoned)", spell_id, mob_body); MessageString(Chat::Red,SPELL_NEED_TAR); return false; } @@ -1614,7 +1616,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce if(!spell_target || (spell_target != GetPet()) || (mob_body != BT_Summoned && mob_body != BT_Summoned2 && mob_body != BT_Summoned3 && mob_body != BT_Animal)) { - Log(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target of body type %d (summoned pet)", + LogSpells("Spell [{}] canceled: invalid target of body type [{}] (summoned pet)", spell_id, mob_body); MessageString(Chat::Red, SPELL_NEED_TAR); @@ -1639,7 +1641,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce if(!spell_target || mob_body != target_bt) { //invalid target - Log(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target of body type %d (want body Type %d)", spell_id, mob_body, target_bt); + LogSpells("Spell [{}] canceled: invalid target of body type [{}] (want body Type [{}])", spell_id, mob_body, target_bt); if(!spell_target) MessageString(Chat::Red,SPELL_NEED_TAR); else @@ -1657,7 +1659,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce { if(!spell_target) { - Log(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target (ldon object)", spell_id); + LogSpells("Spell [{}] canceled: invalid target (ldon object)", spell_id); MessageString(Chat::Red,SPELL_NEED_TAR); return false; } @@ -1665,14 +1667,14 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce { if(!spell_target->IsNPC()) { - Log(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target (normal)", spell_id); + LogSpells("Spell [{}] canceled: invalid target (normal)", spell_id); MessageString(Chat::Red,SPELL_NEED_TAR); return false; } if(spell_target->GetClass() != LDON_TREASURE) { - Log(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target (normal)", spell_id); + LogSpells("Spell [{}] canceled: invalid target (normal)", spell_id); MessageString(Chat::Red,SPELL_NEED_TAR); return false; } @@ -1681,7 +1683,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce if(!spell_target) { - Log(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target (normal)", spell_id); + LogSpells("Spell [{}] canceled: invalid target (normal)", spell_id); MessageString(Chat::Red,SPELL_NEED_TAR); return false; // can't cast these unless we have a target } @@ -1693,7 +1695,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce { if(!spell_target || !spell_target->IsPlayerCorpse()) { - Log(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target (corpse)", spell_id); + LogSpells("Spell [{}] canceled: invalid target (corpse)", spell_id); uint32 message = ONLY_ON_CORPSES; if(!spell_target) message = SPELL_NEED_TAR; else if(!spell_target->IsCorpse()) message = ONLY_ON_CORPSES; @@ -1709,7 +1711,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce spell_target = GetPet(); if(!spell_target) { - Log(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target (no pet)", spell_id); + LogSpells("Spell [{}] canceled: invalid target (no pet)", spell_id); MessageString(Chat::Red,NO_PET); return false; // can't cast these unless we have a target } @@ -1780,7 +1782,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce { if(!spell_target) { - Log(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target (AOE)", spell_id); + LogSpells("Spell [{}] canceled: invalid target (AOE)", spell_id); MessageString(Chat::Red,SPELL_NEED_TAR); return false; } @@ -1817,7 +1819,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce { if(!spell_target) { - Log(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target (Group Required: Single Target)", spell_id); + LogSpells("Spell [{}] canceled: invalid target (Group Required: Single Target)", spell_id); MessageString(Chat::Red,SPELL_NEED_TAR); return false; } @@ -1934,14 +1936,14 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce if(group_id_caster == 0 || group_id_target == 0) { - Log(Logs::Detail, Logs::Spells, "Spell %d canceled: Attempted to cast a Single Target Group spell on a ungrouped member.", spell_id); + LogSpells("Spell [{}] canceled: Attempted to cast a Single Target Group spell on a ungrouped member", spell_id); MessageString(Chat::Red, TARGET_GROUP_MEMBER); return false; } if(group_id_caster != group_id_target) { - Log(Logs::Detail, Logs::Spells, "Spell %d canceled: Attempted to cast a Single Target Group spell on a ungrouped member.", spell_id); + LogSpells("Spell [{}] canceled: Attempted to cast a Single Target Group spell on a ungrouped member", spell_id); MessageString(Chat::Red, TARGET_GROUP_MEMBER); return false; } @@ -2012,7 +2014,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce default: { - Log(Logs::Detail, Logs::Spells, "I dont know Target Type: %d Spell: (%d) %s", spells[spell_id].targettype, spell_id, spells[spell_id].name); + LogSpells("I dont know Target Type: [{}] Spell: ([{}]) [{}]", spells[spell_id].targettype, spell_id, spells[spell_id].name); Message(0, "I dont know Target Type: %d Spell: (%d) %s", spells[spell_id].targettype, spell_id, spells[spell_id].name); CastAction = CastActUnknown; break; @@ -2069,7 +2071,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui if (IsClient() && CastToClient()->GetGM()){ if (zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))){ - Log(Logs::Detail, Logs::Spells, "GM Cast Blocked Spell: %s (ID %i)", GetSpellName(spell_id), spell_id); + LogSpells("GM Cast Blocked Spell: [{}] (ID [{}])", GetSpellName(spell_id), spell_id); } } @@ -2097,7 +2099,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui if(!DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction, slot, isproc)) return(false); - Log(Logs::Detail, Logs::Spells, "Spell %d: target type %d, target %s, AE center %s", spell_id, CastAction, spell_target?spell_target->GetName():"NONE", ae_center?ae_center->GetName():"NONE"); + LogSpells("Spell [{}]: target type [{}], target [{}], AE center [{}]", spell_id, CastAction, spell_target?spell_target->GetName():"NONE", ae_center?ae_center->GetName():"NONE"); // if a spell has the AEDuration flag, it becomes an AE on target // spell that's recast every 2500 msec for AEDuration msec. There are @@ -2108,7 +2110,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui Mob *beacon_loc = spell_target ? spell_target : this; auto beacon = new Beacon(beacon_loc, spells[spell_id].AEDuration); entity_list.AddBeacon(beacon); - Log(Logs::Detail, Logs::Spells, "Spell %d: AE duration beacon created, entity id %d", spell_id, beacon->GetName()); + LogSpells("Spell [{}]: AE duration beacon created, entity id [{}]", spell_id, beacon->GetName()); spell_target = nullptr; ae_center = beacon; CastAction = AECaster; @@ -2117,7 +2119,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui // check line of sight to target if it's a detrimental spell if(!spells[spell_id].npc_no_los && spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id) && spells[spell_id].targettype != ST_TargetOptional) { - Log(Logs::Detail, Logs::Spells, "Spell %d: cannot see target %s", spell_id, spell_target->GetName()); + LogSpells("Spell [{}]: cannot see target [{}]", spell_id, spell_target->GetName()); MessageString(Chat::Red,CANT_SEE_TARGET); return false; } @@ -2148,13 +2150,13 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; if(dist2 > range2) { //target is out of range. - Log(Logs::Detail, Logs::Spells, "Spell %d: Spell target is out of range (squared: %f > %f)", spell_id, dist2, range2); + LogSpells("Spell [{}]: Spell target is out of range (squared: [{}] > [{}])", spell_id, dist2, range2); MessageString(Chat::Red, TARGET_OUT_OF_RANGE); return(false); } else if (dist2 < min_range2){ //target is too close range. - Log(Logs::Detail, Logs::Spells, "Spell %d: Spell target is too close (squared: %f < %f)", spell_id, dist2, min_range2); + LogSpells("Spell [{}]: Spell target is too close (squared: [{}] < [{}])", spell_id, dist2, min_range2); MessageString(Chat::Red, TARGET_TOO_CLOSE); return(false); } @@ -2169,13 +2171,13 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; if(dist2 > range2) { //target is out of range. - Log(Logs::Detail, Logs::Spells, "Spell %d: Spell target is out of range (squared: %f > %f)", spell_id, dist2, range2); + LogSpells("Spell [{}]: Spell target is out of range (squared: [{}] > [{}])", spell_id, dist2, range2); MessageString(Chat::Red, TARGET_OUT_OF_RANGE); return(false); } else if (dist2 < min_range2){ //target is too close range. - Log(Logs::Detail, Logs::Spells, "Spell %d: Spell target is too close (squared: %f < %f)", spell_id, dist2, min_range2); + LogSpells("Spell [{}]: Spell target is too close (squared: [{}] < [{}])", spell_id, dist2, min_range2); MessageString(Chat::Red, TARGET_TOO_CLOSE); return(false); } @@ -2205,7 +2207,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui #endif //BOTS if(spell_target == nullptr) { - Log(Logs::Detail, Logs::Spells, "Spell %d: Targeted spell, but we have no target", spell_id); + LogSpells("Spell [{}]: Targeted spell, but we have no target", spell_id); return(false); } if (isproc) { @@ -2229,11 +2231,11 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui if(IsPlayerIllusionSpell(spell_id) && IsClient() && (HasProjectIllusion())){ - Log(Logs::Detail, Logs::AA, "Effect Project Illusion for %s on spell id: %d was ON", GetName(), spell_id); + LogAA("Effect Project Illusion for [{}] on spell id: [{}] was ON", GetName(), spell_id); SetProjectIllusion(false); } else{ - Log(Logs::Detail, Logs::AA, "Effect Project Illusion for %s on spell id: %d was OFF", GetName(), spell_id); + LogAA("Effect Project Illusion for [{}] on spell id: [{}] was OFF", GetName(), spell_id); } break; } @@ -2401,7 +2403,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui // clamp if we some how got focused above our current mana if (GetMana() < mana_used) mana_used = GetMana(); - Log(Logs::Detail, Logs::Spells, "Spell %d: consuming %d mana", spell_id, mana_used); + LogSpells("Spell [{}]: consuming [{}] mana", spell_id, mana_used); if (!DoHPToManaCovert(mana_used)) { SetMana(GetMana() - mana_used); TryTriggerOnValueAmount(false, true); @@ -2434,7 +2436,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui { //aa new todo: aa expendable charges here CastToClient()->GetPTimers().Start(casting_spell_timer, casting_spell_timer_duration); - Log(Logs::Detail, Logs::Spells, "Spell %d: Setting custom reuse timer %d to %d", spell_id, casting_spell_timer, casting_spell_timer_duration); + LogSpells("Spell [{}]: Setting custom reuse timer [{}] to [{}]", spell_id, casting_spell_timer, casting_spell_timer_duration); } else if(spells[spell_id].recast_time > 1000 && !spells[spell_id].IsDisciplineBuff) { int recast = spells[spell_id].recast_time/1000; @@ -2450,7 +2452,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui if(reduction) recast -= reduction; - Log(Logs::Detail, Logs::Spells, "Spell %d: Setting long reuse timer to %d s (orig %d)", spell_id, recast, spells[spell_id].recast_time); + LogSpells("Spell [{}]: Setting long reuse timer to [{}] s (orig [{}])", spell_id, recast, spells[spell_id].recast_time); CastToClient()->GetPTimers().Start(pTimerSpellStart + spell_id, recast); } } @@ -2492,7 +2494,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, CastingSlot slot) { if(slot == CastingSlot::Item) { //bard songs should never come from items... - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse %d: Supposidly cast from an item. Killing song.", spell_id); + LogSpells("Bard Song Pulse [{}]: Supposidly cast from an item. Killing song", spell_id); return(false); } @@ -2500,12 +2502,12 @@ bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, CastingSlot slo Mob *ae_center = nullptr; CastAction_type CastAction; if(!DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction, slot)) { - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse %d: was unable to determine target. Stopping.", spell_id); + LogSpells("Bard Song Pulse [{}]: was unable to determine target. Stopping", spell_id); return(false); } if(ae_center != nullptr && ae_center->IsBeacon()) { - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse %d: Unsupported Beacon NPC AE spell", spell_id); + LogSpells("Bard Song Pulse [{}]: Unsupported Beacon NPC AE spell", spell_id); return(false); } @@ -2514,18 +2516,18 @@ bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, CastingSlot slo if(mana_used > 0) { if(mana_used > GetMana()) { //ran out of mana... this calls StopSong() for us - Log(Logs::Detail, Logs::Spells, "Ran out of mana while singing song %d", spell_id); + LogSpells("Ran out of mana while singing song [{}]", spell_id); return(false); } - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse %d: consuming %d mana (have %d)", spell_id, mana_used, GetMana()); + LogSpells("Bard Song Pulse [{}]: consuming [{}] mana (have [{}])", spell_id, mana_used, GetMana()); SetMana(GetMana() - mana_used); } // check line of sight to target if it's a detrimental spell if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target)) { - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse %d: cannot see target %s", spell_target->GetName()); + LogSpells("Bard Song Pulse [{}]: cannot see target [{}]", spell_target->GetName()); MessageString(Chat::Red, CANT_SEE_TARGET); return(false); } @@ -2540,7 +2542,7 @@ bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, CastingSlot slo float range2 = range * range; if(dist2 > range2) { //target is out of range. - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse %d: Spell target is out of range (squared: %f > %f)", spell_id, dist2, range2); + LogSpells("Bard Song Pulse [{}]: Spell target is out of range (squared: [{}] > [{}])", spell_id, dist2, range2); MessageString(Chat::Red, TARGET_OUT_OF_RANGE); return(false); } @@ -2556,10 +2558,10 @@ bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, CastingSlot slo case SingleTarget: { if(spell_target == nullptr) { - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse %d: Targeted spell, but we have no target", spell_id); + LogSpells("Bard Song Pulse [{}]: Targeted spell, but we have no target", spell_id); return(false); } - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse: Targeted. spell %d, target %s", spell_id, spell_target->GetName()); + LogSpells("Bard Song Pulse: Targeted. spell [{}], target [{}]", spell_id, spell_target->GetName()); spell_target->BardPulse(spell_id, this); break; } @@ -2577,7 +2579,7 @@ bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, CastingSlot slo { // we can't cast an AE spell without something to center it on if(ae_center == nullptr) { - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse %d: AE Targeted spell, but we have no target", spell_id); + LogSpells("Bard Song Pulse [{}]: AE Targeted spell, but we have no target", spell_id); return(false); } @@ -2585,9 +2587,9 @@ bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, CastingSlot slo if(spell_target) { // this must be an AETarget spell // affect the target too spell_target->BardPulse(spell_id, this); - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse: spell %d, AE target %s", spell_id, spell_target->GetName()); + LogSpells("Bard Song Pulse: spell [{}], AE target [{}]", spell_id, spell_target->GetName()); } else { - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse: spell %d, AE with no target", spell_id); + LogSpells("Bard Song Pulse: spell [{}], AE with no target", spell_id); } bool affect_caster = !IsNPC(); //NPC AE spells do not affect the NPC caster entity_list.AEBardPulse(this, ae_center, spell_id, affect_caster); @@ -2597,13 +2599,13 @@ bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, CastingSlot slo case GroupSpell: { if(spell_target->IsGrouped()) { - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse: spell %d, Group targeting group of %s", spell_id, spell_target->GetName()); + LogSpells("Bard Song Pulse: spell [{}], Group targeting group of [{}]", spell_id, spell_target->GetName()); Group *target_group = entity_list.GetGroupByMob(spell_target); if(target_group) target_group->GroupBardPulse(this, spell_id); } else if(spell_target->IsRaidGrouped() && spell_target->IsClient()) { - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse: spell %d, Raid group targeting raid group of %s", spell_id, spell_target->GetName()); + LogSpells("Bard Song Pulse: spell [{}], Raid group targeting raid group of [{}]", spell_id, spell_target->GetName()); Raid *r = entity_list.GetRaidByClient(spell_target->CastToClient()); if(r){ uint32 gid = r->GetGroup(spell_target->GetName()); @@ -2620,7 +2622,7 @@ bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, CastingSlot slo } } else { - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse: spell %d, Group target without group. Affecting caster.", spell_id); + LogSpells("Bard Song Pulse: spell [{}], Group target without group. Affecting caster", spell_id); BardPulse(spell_id, this); #ifdef GROUP_BUFF_PETS if (GetPet() && HasPetAffinity() && !GetPet()->IsCharmed()) @@ -2646,13 +2648,13 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { if(buffs[buffs_i].spellid != spell_id) continue; if(buffs[buffs_i].casterid != caster->GetID()) { - Log(Logs::Detail, Logs::Spells, "Bard Pulse for %d: found buff from caster %d and we are pulsing for %d... are there two bards playing the same song???", spell_id, buffs[buffs_i].casterid, caster->GetID()); + LogSpells("Bard Pulse for [{}]: found buff from caster [{}] and we are pulsing for [{}] are there two bards playing the same song???", spell_id, buffs[buffs_i].casterid, caster->GetID()); return; } //extend the spell if it will expire before the next pulse if(buffs[buffs_i].ticsremaining <= 3) { buffs[buffs_i].ticsremaining += 3; - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse %d: extending duration in slot %d to %d tics", spell_id, buffs_i, buffs[buffs_i].ticsremaining); + LogSpells("Bard Song Pulse [{}]: extending duration in slot [{}] to [{}] tics", spell_id, buffs_i, buffs[buffs_i].ticsremaining); } //should we send this buff update to the client... seems like it would @@ -2702,7 +2704,7 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { //we are done... return; } - Log(Logs::Detail, Logs::Spells, "Bard Song Pulse %d: Buff not found, reapplying spell.", spell_id); + LogSpells("Bard Song Pulse [{}]: Buff not found, reapplying spell", spell_id); //this spell is not affecting this mob, apply it. caster->SpellOnTarget(spell_id, this); } @@ -2748,7 +2750,7 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste res = mod_buff_duration(res, caster, target, spell_id); - Log(Logs::Detail, Logs::Spells, "Spell %d: Casting level %d, formula %d, base_duration %d: result %d", + LogSpells("Spell [{}]: Casting level [{}], formula [{}], base_duration [{}]: result [{}]", spell_id, castlevel, formula, duration, res); return res; @@ -2839,24 +2841,24 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, int blocked_effect, blocked_below_value, blocked_slot; int overwrite_effect, overwrite_below_value, overwrite_slot; - Log(Logs::Detail, Logs::Spells, "Check Stacking on old %s (%d) @ lvl %d (by %s) vs. new %s (%d) @ lvl %d (by %s)", sp1.name, spellid1, caster_level1, (caster1==nullptr)?"Nobody":caster1->GetName(), sp2.name, spellid2, caster_level2, (caster2==nullptr)?"Nobody":caster2->GetName()); + LogSpells("Check Stacking on old [{}] ([{}]) @ lvl [{}] (by [{}]) vs. new [{}] ([{}]) @ lvl [{}] (by [{}])", sp1.name, spellid1, caster_level1, (caster1==nullptr)?"Nobody":caster1->GetName(), sp2.name, spellid2, caster_level2, (caster2==nullptr)?"Nobody":caster2->GetName()); if (spellid1 == spellid2 ) { if (!IsStackableDot(spellid1) && !IsEffectInSpell(spellid1, SE_ManaBurn)) { // mana burn spells we need to use the stacking command blocks live actually checks those first, we should probably rework to that too if (caster_level1 > caster_level2) { // cur buff higher level than new if (IsEffectInSpell(spellid1, SE_ImprovedTaunt)) { - Log(Logs::Detail, Logs::Spells, "SE_ImprovedTaunt level exception, overwriting."); + LogSpells("SE_ImprovedTaunt level exception, overwriting"); return 1; } else { - Log(Logs::Detail, Logs::Spells, "Spells the same but existing is higher level, stopping."); + LogSpells("Spells the same but existing is higher level, stopping"); return -1; } } else { - Log(Logs::Detail, Logs::Spells, "Spells the same but newer is higher or equal level, overwriting."); + LogSpells("Spells the same but newer is higher or equal level, overwriting"); return 1; } } else if (spellid1 == 2751) { - Log(Logs::Detail, Logs::Spells, "Blocking spell because manaburn does not stack with itself."); + LogSpells("Blocking spell because manaburn does not stack with itself"); return -1; } } @@ -2871,7 +2873,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, { if(!IsDetrimentalSpell(spellid1) && !IsDetrimentalSpell(spellid2)) { - Log(Logs::Detail, Logs::Spells, "%s and %s are beneficial, and one is a bard song, no action needs to be taken", sp1.name, sp2.name); + LogSpells("[{}] and [{}] are beneficial, and one is a bard song, no action needs to be taken", sp1.name, sp2.name); return (0); } } @@ -2886,7 +2888,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, } } } else if (IsEffectInSpell(spellid1, SE_ManaBurn)) { - Log(Logs::Detail, Logs::Spells, "We have a Mana Burn spell that is the same, they won't stack"); + LogSpells("We have a Mana Burn spell that is the same, they won't stack"); return -1; } @@ -2944,16 +2946,16 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, { sp1_value = CalcSpellEffectValue(spellid1, overwrite_slot, caster_level1); - Log(Logs::Detail, Logs::Spells, "%s (%d) overwrites existing spell if effect %d on slot %d is below %d. Old spell has value %d on that slot/effect. %s.", + LogSpells("[{}] ([{}]) overwrites existing spell if effect [{}] on slot [{}] is below [{}]. Old spell has value [{}] on that slot/effect. [{}]", sp2.name, spellid2, overwrite_effect, overwrite_slot, overwrite_below_value, sp1_value, (sp1_value < overwrite_below_value)?"Overwriting":"Not overwriting"); if(sp1_value < overwrite_below_value) { - Log(Logs::Detail, Logs::Spells, "Overwrite spell because sp1_value < overwrite_below_value"); + LogSpells("Overwrite spell because sp1_value < overwrite_below_value"); return 1; // overwrite spell if its value is less } } else { - Log(Logs::Detail, Logs::Spells, "%s (%d) overwrites existing spell if effect %d on slot %d is below %d, but we do not have that effect on that slot. Ignored.", + LogSpells("[{}] ([{}]) overwrites existing spell if effect [{}] on slot [{}] is below [{}], but we do not have that effect on that slot. Ignored", sp2.name, spellid2, overwrite_effect, overwrite_slot, overwrite_below_value); } @@ -2967,22 +2969,22 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, { sp2_value = CalcSpellEffectValue(spellid2, blocked_slot, caster_level2); - Log(Logs::Detail, Logs::Spells, "%s (%d) blocks effect %d on slot %d below %d. New spell has value %d on that slot/effect. %s.", + LogSpells("[{}] ([{}]) blocks effect [{}] on slot [{}] below [{}]. New spell has value [{}] on that slot/effect. [{}]", sp1.name, spellid1, blocked_effect, blocked_slot, blocked_below_value, sp2_value, (sp2_value < blocked_below_value)?"Blocked":"Not blocked"); if (sp2_value < blocked_below_value) { - Log(Logs::Detail, Logs::Spells, "Blocking spell because sp2_Value < blocked_below_value"); + LogSpells("Blocking spell because sp2_Value < blocked_below_value"); return -1; //blocked } } else { - Log(Logs::Detail, Logs::Spells, "%s (%d) blocks effect %d on slot %d below %d, but we do not have that effect on that slot. Ignored.", + LogSpells("[{}] ([{}]) blocks effect [{}] on slot [{}] below [{}], but we do not have that effect on that slot. Ignored", sp1.name, spellid1, blocked_effect, blocked_slot, blocked_below_value); } } } } else { - Log(Logs::Detail, Logs::Spells, "%s (%d) and %s (%d) appear to be in the same line, skipping Stacking Overwrite/Blocking checks", + LogSpells("[{}] ([{}]) and [{}] ([{}]) appear to be in the same line, skipping Stacking Overwrite/Blocking checks", sp1.name, spellid1, sp2.name, spellid2); } @@ -3035,13 +3037,13 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, */ if(IsNPC() && caster1 && caster2 && caster1 != caster2) { if(effect1 == SE_CurrentHP && sp1_detrimental && sp2_detrimental) { - Log(Logs::Detail, Logs::Spells, "Both casters exist and are not the same, the effect is a detrimental dot, moving on"); + LogSpells("Both casters exist and are not the same, the effect is a detrimental dot, moving on"); continue; } } if(effect1 == SE_CompleteHeal){ //SE_CompleteHeal never stacks or overwrites ever, always block. - Log(Logs::Detail, Logs::Spells, "Blocking spell because complete heal never stacks or overwries"); + LogSpells("Blocking spell because complete heal never stacks or overwries"); return (-1); } @@ -3050,7 +3052,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, and the effect is a dot we can go ahead and stack it */ if(effect1 == SE_CurrentHP && spellid1 != spellid2 && sp1_detrimental && sp2_detrimental) { - Log(Logs::Detail, Logs::Spells, "The spells are not the same and it is a detrimental dot, passing"); + LogSpells("The spells are not the same and it is a detrimental dot, passing"); continue; } @@ -3075,7 +3077,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, sp2_value = 0 - sp2_value; if(sp2_value < sp1_value) { - Log(Logs::Detail, Logs::Spells, "Spell %s (value %d) is not as good as %s (value %d). Rejecting %s.", + LogSpells("Spell [{}] (value [{}]) is not as good as [{}] (value [{}]). Rejecting [{}]", sp2.name, sp2_value, sp1.name, sp1_value, sp2.name); return -1; // can't stack } @@ -3084,7 +3086,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, //we dont return here... a better value on this one effect dosent mean they are //all better... - Log(Logs::Detail, Logs::Spells, "Spell %s (value %d) is not as good as %s (value %d). We will overwrite %s if there are no other conflicts.", + LogSpells("Spell [{}] (value [{}]) is not as good as [{}] (value [{}]). We will overwrite [{}] if there are no other conflicts", sp1.name, sp1_value, sp2.name, sp2_value, sp1.name); will_overwrite = true; } @@ -3093,15 +3095,15 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, //so now we see if this new spell is any better, or if its not related at all if(will_overwrite) { if (values_equal && effect_match && !IsGroupSpell(spellid2) && IsGroupSpell(spellid1)) { - Log(Logs::Detail, Logs::Spells, "%s (%d) appears to be the single target version of %s (%d), rejecting", + LogSpells("[{}] ([{}]) appears to be the single target version of [{}] ([{}]), rejecting", sp2.name, spellid2, sp1.name, spellid1); return -1; } - Log(Logs::Detail, Logs::Spells, "Stacking code decided that %s should overwrite %s.", sp2.name, sp1.name); + LogSpells("Stacking code decided that [{}] should overwrite [{}]", sp2.name, sp1.name); return(1); } - Log(Logs::Detail, Logs::Spells, "Stacking code decided that %s is not affected by %s.", sp2.name, sp1.name); + LogSpells("Stacking code decided that [{}] is not affected by [{}]", sp2.name, sp1.name); return 0; } @@ -3194,11 +3196,11 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid } if (duration == 0) { - Log(Logs::Detail, Logs::Spells, "Buff %d failed to add because its duration came back as 0.", spell_id); + LogSpells("Buff [{}] failed to add because its duration came back as 0", spell_id); return -2; // no duration? this isn't a buff } - Log(Logs::Detail, Logs::Spells, "Trying to add buff %d cast by %s (cast level %d) with duration %d", + LogSpells("Trying to add buff [{}] cast by [{}] (cast level [{}]) with duration [{}]", spell_id, caster?caster->GetName():"UNKNOWN", caster_level, duration); // first we loop through everything checking that the spell @@ -3218,7 +3220,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid ret = CheckStackConflict(curbuf.spellid, curbuf.casterlevel, spell_id, caster_level, entity_list.GetMobID(curbuf.casterid), caster, buffslot); if (ret == -1) { // stop the spell - Log(Logs::Detail, Logs::Spells, "Adding buff %d failed: stacking prevented by spell %d in slot %d with caster level %d", + LogSpells("Adding buff [{}] failed: stacking prevented by spell [{}] in slot [{}] with caster level [{}]", spell_id, curbuf.spellid, buffslot, curbuf.casterlevel); if (caster && caster->IsClient() && RuleB(Client, UseLiveBlockedMessage)) { caster->Message(Chat::Red, "Your %s did not take hold on %s. (Blocked by %s.)", spells[spell_id].name, this->GetName(), spells[curbuf.spellid].name); @@ -3226,7 +3228,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid return -1; } if (ret == 1) { // set a flag to indicate that there will be overwriting - Log(Logs::Detail, Logs::Spells, "Adding buff %d will overwrite spell %d in slot %d with caster level %d", + LogSpells("Adding buff [{}] will overwrite spell [{}] in slot [{}] with caster level [{}]", spell_id, curbuf.spellid, buffslot, curbuf.casterlevel); // If this is the first buff it would override, use its slot if (!will_overwrite && !IsDisciplineBuff(spell_id)) @@ -3250,7 +3252,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid for (buffslot = 0; buffslot < buff_count; buffslot++) { const Buffs_Struct &curbuf = buffs[buffslot]; if (IsBeneficialSpell(curbuf.spellid)) { - Log(Logs::Detail, Logs::Spells, "No slot for detrimental buff %d, so we are overwriting a beneficial buff %d in slot %d", + LogSpells("No slot for detrimental buff [{}], so we are overwriting a beneficial buff [{}] in slot [{}]", spell_id, curbuf.spellid, buffslot); BuffFadeBySlot(buffslot, false); emptyslot = buffslot; @@ -3258,11 +3260,11 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid } } if(emptyslot == -1) { - Log(Logs::Detail, Logs::Spells, "Unable to find a buff slot for detrimental buff %d", spell_id); + LogSpells("Unable to find a buff slot for detrimental buff [{}]", spell_id); return -1; } } else { - Log(Logs::Detail, Logs::Spells, "Unable to find a buff slot for beneficial buff %d", spell_id); + LogSpells("Unable to find a buff slot for beneficial buff [{}]", spell_id); return -1; } } @@ -3314,7 +3316,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid buffs[emptyslot].UpdateClient = true; } - Log(Logs::Detail, Logs::Spells, "Buff %d added to slot %d with caster level %d", spell_id, emptyslot, caster_level); + LogSpells("Buff [{}] added to slot [{}] with caster level [{}]", spell_id, emptyslot, caster_level); if (IsPet() && GetOwner() && GetOwner()->IsClient()) SendPetBuffsToClient(); @@ -3356,7 +3358,7 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) { int i, ret, firstfree = -2; - Log(Logs::Detail, Logs::AI, "Checking if buff %d cast at level %d can stack on me.%s", spellid, caster_level, iFailIfOverwrite?" failing if we would overwrite something":""); + LogAI("Checking if buff [{}] cast at level [{}] can stack on me.[{}]", spellid, caster_level, iFailIfOverwrite?" failing if we would overwrite something":""); int buff_count = GetMaxTotalSlots(); for (i=0; i < buff_count; i++) @@ -3380,7 +3382,7 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) if(ret == 1) { // should overwrite current slot if(iFailIfOverwrite) { - Log(Logs::Detail, Logs::AI, "Buff %d would overwrite %d in slot %d, reporting stack failure", spellid, curbuf.spellid, i); + LogAI("Buff [{}] would overwrite [{}] in slot [{}], reporting stack failure", spellid, curbuf.spellid, i); return(-1); } if(firstfree == -2) @@ -3388,12 +3390,12 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) } if(ret == -1) { - Log(Logs::Detail, Logs::AI, "Buff %d would conflict with %d in slot %d, reporting stack failure", spellid, curbuf.spellid, i); + LogAI("Buff [{}] would conflict with [{}] in slot [{}], reporting stack failure", spellid, curbuf.spellid, i); return -1; // stop the spell, can't stack it } } - Log(Logs::Detail, Logs::AI, "Reporting that buff %d could successfully be placed into slot %d", spellid, firstfree); + LogAI("Reporting that buff [{}] could successfully be placed into slot [{}]", spellid, firstfree); return firstfree; } @@ -3421,7 +3423,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r // well we can't cast a spell on target without a target if(!spelltar) { - Log(Logs::Detail, Logs::Spells, "Unable to apply spell %d without a target", spell_id); + LogSpells("Unable to apply spell [{}] without a target", spell_id); Message(Chat::Red, "SOT: You must have a target for this spell."); return false; } @@ -3455,7 +3457,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r uint16 caster_level = level_override > 0 ? level_override : GetCasterLevel(spell_id); - Log(Logs::Detail, Logs::Spells, "Casting spell %d on %s with effective caster level %d", spell_id, spelltar->GetName(), caster_level); + LogSpells("Casting spell [{}] on [{}] with effective caster level [{}]", spell_id, spelltar->GetName(), caster_level); // Actual cast action - this causes the caster animation and the particles // around the target @@ -3546,7 +3548,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r if (RuleB(Spells, EnableBlockedBuffs)) { // We return true here since the caster's client should act like normal if (spelltar->IsBlockedBuff(spell_id)) { - Log(Logs::Detail, Logs::Spells, "Spell %i not applied to %s as it is a Blocked Buff.", + LogSpells("Spell [{}] not applied to [{}] as it is a Blocked Buff", spell_id, spelltar->GetName()); safe_delete(action_packet); return true; @@ -3554,7 +3556,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r if (spelltar->IsPet() && spelltar->GetOwner() && spelltar->GetOwner()->IsBlockedPetBuff(spell_id)) { - Log(Logs::Detail, Logs::Spells, "Spell %i not applied to %s (%s's pet) as it is a Pet Blocked Buff.", + LogSpells("Spell [{}] not applied to [{}] ([{}]'s pet) as it is a Pet Blocked Buff", spell_id, spelltar->GetName(), spelltar->GetOwner()->GetName()); safe_delete(action_packet); return true; @@ -3563,7 +3565,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r // invuln mobs can't be affected by any spells, good or bad if(spelltar->GetInvul() || spelltar->DivineAura()) { - Log(Logs::Detail, Logs::Spells, "Casting spell %d on %s aborted: they are invulnerable.", spell_id, spelltar->GetName()); + LogSpells("Casting spell [{}] on [{}] aborted: they are invulnerable", spell_id, spelltar->GetName()); safe_delete(action_packet); return false; } @@ -3574,17 +3576,17 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r if (RuleB(Pets, UnTargetableSwarmPet)) { if (spelltar->IsNPC()) { if (!spelltar->CastToNPC()->GetSwarmOwner()) { - Log(Logs::Detail, Logs::Spells, "Casting spell %d on %s aborted: they are untargetable", spell_id, spelltar->GetName()); + LogSpells("Casting spell [{}] on [{}] aborted: they are untargetable", spell_id, spelltar->GetName()); safe_delete(action_packet); return(false); } } else { - Log(Logs::Detail, Logs::Spells, "Casting spell %d on %s aborted: they are untargetable", spell_id, spelltar->GetName()); + LogSpells("Casting spell [{}] on [{}] aborted: they are untargetable", spell_id, spelltar->GetName()); safe_delete(action_packet); return(false); } } else { - Log(Logs::Detail, Logs::Spells, "Casting spell %d on %s aborted: they are untargetable", spell_id, spelltar->GetName()); + LogSpells("Casting spell [{}] on [{}] aborted: they are untargetable", spell_id, spelltar->GetName()); safe_delete(action_packet); return(false); } @@ -3693,9 +3695,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r { if(spells[spell_id].targettype == ST_AEBard) { //if it was a beneficial AE bard song don't spam the window that it would not hold - Log(Logs::Detail, Logs::Spells, "Beneficial ae bard song %d can't take hold %s -> %s, IBA? %d", spell_id, GetName(), spelltar->GetName(), IsBeneficialAllowed(spelltar)); + LogSpells("Beneficial ae bard song [{}] can't take hold [{}] -> [{}], IBA? [{}]", spell_id, GetName(), spelltar->GetName(), IsBeneficialAllowed(spelltar)); } else { - Log(Logs::Detail, Logs::Spells, "Beneficial spell %d can't take hold %s -> %s, IBA? %d", spell_id, GetName(), spelltar->GetName(), IsBeneficialAllowed(spelltar)); + LogSpells("Beneficial spell [{}] can't take hold [{}] -> [{}], IBA? [{}]", spell_id, GetName(), spelltar->GetName(), IsBeneficialAllowed(spelltar)); MessageString(Chat::SpellFailure, SPELL_NO_HOLD); } safe_delete(action_packet); @@ -3705,7 +3707,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r } else if ( !IsAttackAllowed(spelltar, true) && !IsResurrectionEffects(spell_id)) // Detrimental spells - PVP check { - Log(Logs::Detail, Logs::Spells, "Detrimental spell %d can't take hold %s -> %s", spell_id, GetName(), spelltar->GetName()); + LogSpells("Detrimental spell [{}] can't take hold [{}] -> [{}]", spell_id, GetName(), spelltar->GetName()); spelltar->MessageString(Chat::SpellFailure, YOU_ARE_PROTECTED, GetCleanName()); safe_delete(action_packet); return false; @@ -3719,7 +3721,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r if(spelltar->IsImmuneToSpell(spell_id, this)) { //the above call does the message to the client if needed - Log(Logs::Detail, Logs::Spells, "Spell %d can't take hold due to immunity %s -> %s", spell_id, GetName(), spelltar->GetName()); + LogSpells("Spell [{}] can't take hold due to immunity [{}] -> [{}]", spell_id, GetName(), spelltar->GetName()); safe_delete(action_packet); return false; } @@ -3832,7 +3834,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r { if(spell_effectiveness == 0 || !IsPartialCapableSpell(spell_id) ) { - Log(Logs::Detail, Logs::Spells, "Spell %d was completely resisted by %s", spell_id, spelltar->GetName()); + LogSpells("Spell [{}] was completely resisted by [{}]", spell_id, spelltar->GetName()); if (spells[spell_id].resisttype == RESIST_PHYSICAL){ MessageString(Chat::SpellFailure, PHYSICAL_RESIST_FAIL,spells[spell_id].name); @@ -3884,7 +3886,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r if (spelltar->IsAIControlled() && IsDetrimentalSpell(spell_id) && !IsHarmonySpell(spell_id)) { int32 aggro_amount = CheckAggroAmount(spell_id, spelltar, isproc); - Log(Logs::Detail, Logs::Spells, "Spell %d cast on %s generated %d hate", spell_id, + LogSpells("Spell [{}] cast on [{}] generated [{}] hate", spell_id, spelltar->GetName(), aggro_amount); if (aggro_amount > 0) { spelltar->AddToHateList(this, aggro_amount); @@ -3903,7 +3905,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r // make sure spelltar is high enough level for the buff if(RuleB(Spells, BuffLevelRestrictions) && !spelltar->CheckSpellLevelRestriction(spell_id)) { - Log(Logs::Detail, Logs::Spells, "Spell %d failed: recipient did not meet the level restrictions", spell_id); + LogSpells("Spell [{}] failed: recipient did not meet the level restrictions", spell_id); if(!IsBardSong(spell_id)) MessageString(Chat::SpellFailure, SPELL_TOO_POWERFUL); safe_delete(action_packet); @@ -3915,7 +3917,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r { // if SpellEffect returned false there's a problem applying the // spell. It's most likely a buff that can't stack. - Log(Logs::Detail, Logs::Spells, "Spell %d could not apply its effects %s -> %s\n", spell_id, GetName(), spelltar->GetName()); + LogSpells("Spell [{}] could not apply its effects [{}] -> [{}]\n", spell_id, GetName(), spelltar->GetName()); if(casting_spell_aa_id) MessageString(Chat::SpellFailure, SPELL_NO_HOLD); safe_delete(action_packet); @@ -3986,14 +3988,14 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r safe_delete(action_packet); safe_delete(message_packet); - Log(Logs::Detail, Logs::Spells, "Cast of %d by %s on %s complete successfully.", spell_id, GetName(), spelltar->GetName()); + LogSpells("Cast of [{}] by [{}] on [{}] complete successfully", spell_id, GetName(), spelltar->GetName()); return true; } void Corpse::CastRezz(uint16 spellid, Mob* Caster) { - Log(Logs::Detail, Logs::Spells, "Corpse::CastRezz spellid %i, Rezzed() is %i, rezzexp is %i", spellid,IsRezzed(),rez_experience); + LogSpells("Corpse::CastRezz spellid [{}], Rezzed() is [{}], rezzexp is [{}]", spellid,IsRezzed(),rez_experience); if(IsRezzed()){ if(Caster && Caster->IsClient()) @@ -4220,7 +4222,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) //this spell like 10 times, this could easily be consolidated //into one loop through with a switch statement. - Log(Logs::Detail, Logs::Spells, "Checking to see if we are immune to spell %d cast by %s", spell_id, caster->GetName()); + LogSpells("Checking to see if we are immune to spell [{}] cast by [{}]", spell_id, caster->GetName()); if(!IsValidSpell(spell_id)) return true; @@ -4231,7 +4233,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) if(IsMezSpell(spell_id)) { if(GetSpecialAbility(UNMEZABLE)) { - Log(Logs::Detail, Logs::Spells, "We are immune to Mez spells."); + LogSpells("We are immune to Mez spells"); caster->MessageString(Chat::SpellFailure, CANNOT_MEZ); int32 aggro = caster->CheckAggroAmount(spell_id, this); if(aggro > 0) { @@ -4249,7 +4251,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) if((GetLevel() > spells[spell_id].max[effect_index]) && (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity)))) { - Log(Logs::Detail, Logs::Spells, "Our level (%d) is higher than the limit of this Mez spell (%d)", GetLevel(), spells[spell_id].max[effect_index]); + LogSpells("Our level ([{}]) is higher than the limit of this Mez spell ([{}])", GetLevel(), spells[spell_id].max[effect_index]); caster->MessageString(Chat::SpellFailure, CANNOT_MEZ_WITH_SPELL); AddToHateList(caster, 1,0,true,false,false,spell_id); return true; @@ -4259,7 +4261,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) // slow and haste spells if(GetSpecialAbility(UNSLOWABLE) && IsEffectInSpell(spell_id, SE_AttackSpeed)) { - Log(Logs::Detail, Logs::Spells, "We are immune to Slow spells."); + LogSpells("We are immune to Slow spells"); caster->MessageString(Chat::Red, IMMUNE_ATKSPEED); int32 aggro = caster->CheckAggroAmount(spell_id, this); if(aggro > 0) { @@ -4275,7 +4277,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) { effect_index = GetSpellEffectIndex(spell_id, SE_Fear); if(GetSpecialAbility(UNFEARABLE)) { - Log(Logs::Detail, Logs::Spells, "We are immune to Fear spells."); + LogSpells("We are immune to Fear spells"); caster->MessageString(Chat::Red, IMMUNE_FEAR); // need to verify message type, not in MQ2Cast for easy look up int32 aggro = caster->CheckAggroAmount(spell_id, this); if(aggro > 0) { @@ -4286,13 +4288,13 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) return true; } else if(IsClient() && caster->IsClient() && (caster->CastToClient()->GetGM() == false)) { - Log(Logs::Detail, Logs::Spells, "Clients cannot fear eachother!"); + LogSpells("Clients cannot fear eachother!"); caster->MessageString(Chat::Red, IMMUNE_FEAR); // need to verify message type, not in MQ2Cast for easy look up return true; } else if(GetLevel() > spells[spell_id].max[effect_index] && spells[spell_id].max[effect_index] != 0) { - Log(Logs::Detail, Logs::Spells, "Level is %d, cannot be feared by this spell.", GetLevel()); + LogSpells("Level is [{}], cannot be feared by this spell", GetLevel()); caster->MessageString(Chat::Shout, FEAR_TOO_HIGH); int32 aggro = caster->CheckAggroAmount(spell_id, this); if (aggro > 0) { @@ -4305,7 +4307,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) else if (CheckAATimer(aaTimerWarcry)) { Message(Chat::Red, "Your are immune to fear."); - Log(Logs::Detail, Logs::Spells, "Clients has WarCry effect, immune to fear!"); + LogSpells("Clients has WarCry effect, immune to fear!"); caster->MessageString(Chat::Red, IMMUNE_FEAR); // need to verify message type, not in MQ2Cast for easy look up return true; } @@ -4315,7 +4317,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) { if(GetSpecialAbility(UNCHARMABLE)) { - Log(Logs::Detail, Logs::Spells, "We are immune to Charm spells."); + LogSpells("We are immune to Charm spells"); caster->MessageString(Chat::Red, CANNOT_CHARM); // need to verify message type, not in MQ2Cast for easy look up int32 aggro = caster->CheckAggroAmount(spell_id, this); if(aggro > 0) { @@ -4328,7 +4330,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) if(this == caster) { - Log(Logs::Detail, Logs::Spells, "You are immune to your own charms."); + LogSpells("You are immune to your own charms"); caster->Message(Chat::Red, "You cannot charm yourself."); // need to look up message? return true; } @@ -4341,7 +4343,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) assert(effect_index >= 0); if(GetLevel() > spells[spell_id].max[effect_index] && spells[spell_id].max[effect_index] != 0) { - Log(Logs::Detail, Logs::Spells, "Our level (%d) is higher than the limit of this Charm spell (%d)", GetLevel(), spells[spell_id].max[effect_index]); + LogSpells("Our level ([{}]) is higher than the limit of this Charm spell ([{}])", GetLevel(), spells[spell_id].max[effect_index]); caster->MessageString(Chat::Red, CANNOT_CHARM_YET); // need to verify message type, not in MQ2Cast for easy look up AddToHateList(caster, 1,0,true,false,false,spell_id); return true; @@ -4356,7 +4358,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) ) { if(GetSpecialAbility(UNSNAREABLE)) { - Log(Logs::Detail, Logs::Spells, "We are immune to Snare spells."); + LogSpells("We are immune to Snare spells"); caster->MessageString(Chat::Red, IMMUNE_MOVEMENT); int32 aggro = caster->CheckAggroAmount(spell_id, this); if(aggro > 0) { @@ -4372,7 +4374,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) { if(this == caster) { - Log(Logs::Detail, Logs::Spells, "You cannot lifetap yourself."); + LogSpells("You cannot lifetap yourself"); caster->MessageString(Chat::SpellFailure, CANT_DRAIN_SELF); return true; } @@ -4382,13 +4384,13 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) { if(this == caster) { - Log(Logs::Detail, Logs::Spells, "You cannot sacrifice yourself."); + LogSpells("You cannot sacrifice yourself"); caster->MessageString(Chat::SpellFailure, CANNOT_SAC_SELF); return true; } } - Log(Logs::Detail, Logs::Spells, "No immunities to spell %d found.", spell_id); + LogSpells("No immunities to spell [{}] found", spell_id); return false; } @@ -4455,7 +4457,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use if(GetSpecialAbility(IMMUNE_MAGIC)) { - Log(Logs::Detail, Logs::Spells, "We are immune to magic, so we fully resist the spell %d", spell_id); + LogSpells("We are immune to magic, so we fully resist the spell [{}]", spell_id); return(0); } @@ -4476,7 +4478,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use int fear_resist_bonuses = CalcFearResistChance(); if(zone->random.Roll(fear_resist_bonuses)) { - Log(Logs::Detail, Logs::Spells, "Resisted spell in fear resistance, had %d chance to resist", fear_resist_bonuses); + LogSpells("Resisted spell in fear resistance, had [{}] chance to resist", fear_resist_bonuses); return 0; } } @@ -4494,7 +4496,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use int resist_bonuses = CalcResistChanceBonus(); if(resist_bonuses && zone->random.Roll(resist_bonuses)) { - Log(Logs::Detail, Logs::Spells, "Resisted spell in sanctification, had %d chance to resist", resist_bonuses); + LogSpells("Resisted spell in sanctification, had [{}] chance to resist", resist_bonuses); return 0; } } @@ -4502,7 +4504,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use //Get the resist chance for the target if(resist_type == RESIST_NONE || spells[spell_id].no_resist) { - Log(Logs::Detail, Logs::Spells, "Spell was unresistable"); + LogSpells("Spell was unresistable"); return 100; } @@ -5020,7 +5022,7 @@ void Client::MemSpell(uint16 spell_id, int slot, bool update_client) } m_pp.mem_spells[slot] = spell_id; - Log(Logs::Detail, Logs::Spells, "Spell %d memorized into slot %d", spell_id, slot); + LogSpells("Spell [{}] memorized into slot [{}]", spell_id, slot); database.SaveCharacterMemorizedSpell(this->CharacterID(), m_pp.mem_spells[slot], slot); @@ -5035,7 +5037,7 @@ void Client::UnmemSpell(int slot, bool update_client) if(slot > EQEmu::spells::SPELL_GEM_COUNT || slot < 0) return; - Log(Logs::Detail, Logs::Spells, "Spell %d forgotten from slot %d", m_pp.mem_spells[slot], slot); + LogSpells("Spell [{}] forgotten from slot [{}]", m_pp.mem_spells[slot], slot); m_pp.mem_spells[slot] = 0xFFFFFFFF; database.DeleteCharacterMemorizedSpell(this->CharacterID(), m_pp.mem_spells[slot], slot); @@ -5095,7 +5097,7 @@ void Client::ScribeSpell(uint16 spell_id, int slot, bool update_client) m_pp.spell_book[slot] = spell_id; database.SaveCharacterSpell(this->CharacterID(), spell_id, slot); - Log(Logs::Detail, Logs::Spells, "Spell %d scribed into spell book slot %d", spell_id, slot); + LogSpells("Spell [{}] scribed into spell book slot [{}]", spell_id, slot); if(update_client) { @@ -5108,7 +5110,7 @@ void Client::UnscribeSpell(int slot, bool update_client) if(slot >= EQEmu::spells::SPELLBOOK_SIZE || slot < 0) return; - Log(Logs::Detail, Logs::Spells, "Spell %d erased from spell book slot %d", m_pp.spell_book[slot], slot); + LogSpells("Spell [{}] erased from spell book slot [{}]", m_pp.spell_book[slot], slot); m_pp.spell_book[slot] = 0xFFFFFFFF; database.DeleteCharacterSpell(this->CharacterID(), m_pp.spell_book[slot], slot); @@ -5137,7 +5139,7 @@ void Client::UntrainDisc(int slot, bool update_client) if(slot >= MAX_PP_DISCIPLINES || slot < 0) return; - Log(Logs::Detail, Logs::Spells, "Discipline %d untrained from slot %d", m_pp.disciplines.values[slot], slot); + LogSpells("Discipline [{}] untrained from slot [{}]", m_pp.disciplines.values[slot], slot); m_pp.disciplines.values[slot] = 0; database.DeleteCharacterDisc(this->CharacterID(), slot); @@ -5198,28 +5200,50 @@ bool Client::SpellGlobalCheck(uint16 spell_id, uint32 char_id) { query = StringFormat("SELECT value FROM quest_globals " "WHERE charid = %i AND name = '%s'", - char_id, spell_global_name.c_str()); - results = database.QueryDatabase(query); - if (!results.Success()) { - Log(Logs::General, Logs::Error, "Spell ID %i query of spell_globals with Name: '%s' Value: '%i' failed", spell_id, spell_global_name.c_str(), spell_global_value); - return false; - } + char_id, spell_global_name.c_str()); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogError( + "Spell ID [{}] query of spell_globals with Name: [{}] Value: [{}] failed", + spell_id, + spell_global_name.c_str(), + spell_global_value + ); - if (results.RowCount() != 1) { - Log(Logs::General, Logs::Error, "Char ID: %i does not have the Qglobal Name: '%s' for Spell ID %i", char_id, spell_global_name.c_str(), spell_id); - return false; - } + return false; + } - row = results.begin(); - global_value = atoi(row[0]); - if (global_value == spell_global_value) - return true; // If the values match from both tables, allow the spell to be scribed - else if (global_value > spell_global_value) - return true; // Check if the qglobal value is greater than the require spellglobal value + if (results.RowCount() != 1) { + LogError( + "Char ID: [{}] does not have the Qglobal Name: [{}] for Spell ID [{}]", + char_id, + spell_global_name.c_str(), + spell_id + ); - // If no matching result found in qglobals, don't scribe this spell - Log(Logs::General, Logs::Error, "Char ID: %i Spell_globals Name: '%s' Value: '%i' did not match QGlobal Value: '%i' for Spell ID %i", char_id, spell_global_name.c_str(), spell_global_value, global_value, spell_id); - return false; + return false; + } + + row = results.begin(); + global_value = atoi(row[0]); + if (global_value == spell_global_value) { + return true; // If the values match from both tables, allow the spell to be scribed + } + else if (global_value > spell_global_value) { + return true; + } // Check if the qglobal value is greater than the require spellglobal value + + // If no matching result found in qglobals, don't scribe this spell + LogError( + "Char ID: [{}] SpellGlobals Name: [{}] Value: [{}] did not match QGlobal Value: [{}] for Spell ID [{}]", + char_id, + spell_global_name.c_str(), + spell_global_value, + global_value, + spell_id + ); + + return false; } bool Client::SpellBucketCheck(uint16 spell_id, uint32 char_id) { @@ -5239,18 +5263,30 @@ bool Client::SpellBucketCheck(uint16 spell_id, uint32 char_id) { spell_bucket_value = atoi(row[1]); if (spell_bucket_name.empty()) return true; - - query = StringFormat("SELECT value FROM data_buckets WHERE `key` = '%i-%s'", char_id, spell_bucket_name.c_str()); + + query = StringFormat("SELECT value FROM data_buckets WHERE `key` = '%i-%s'", char_id, spell_bucket_name.c_str()); results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Spell bucket %s for spell ID %i for char ID %i failed.", spell_bucket_name.c_str(), spell_id, char_id); - return false; - } + LogError( + "Spell bucket [{}] for spell ID [{}] for char ID [{}] failed", + spell_bucket_name.c_str(), + spell_id, + char_id + ); - if (results.RowCount() != 1) { - Log(Logs::General, Logs::Error, "Spell bucket %s does not exist for spell ID %i for char ID %i.", spell_bucket_name.c_str(), spell_id, char_id); - return false; - } + return false; + } + + if (results.RowCount() != 1) { + LogError( + "Spell bucket [{}] does not exist for spell ID [{}] for char ID [{}]", + spell_bucket_name.c_str(), + spell_id, + char_id + ); + + return false; + } row = results.begin(); @@ -5262,7 +5298,7 @@ bool Client::SpellBucketCheck(uint16 spell_id, uint32 char_id) { return true; // Check if the data bucket value is greater than the required spell bucket value // If no matching result found in spell buckets, don't scribe this spell - Log(Logs::General, Logs::Error, "Spell bucket %s for spell ID %i for char ID %i did not match value %i.", spell_bucket_name.c_str(), spell_id, char_id, spell_bucket_value); + LogError("Spell bucket [{}] for spell ID [{}] for char ID [{}] did not match value [{}]", spell_bucket_name.c_str(), spell_id, char_id, spell_bucket_value); return false; } @@ -5351,12 +5387,12 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 b PermaProcs[i].chance = iChance; PermaProcs[i].base_spellID = base_spell_id; PermaProcs[i].level_override = level_override; - Log(Logs::Detail, Logs::Spells, "Added permanent proc spell %d with chance %d to slot %d", spell_id, iChance, i); + LogSpells("Added permanent proc spell [{}] with chance [{}] to slot [{}]", spell_id, iChance, i); return true; } } - Log(Logs::Detail, Logs::Spells, "Too many perma procs for %s", GetName()); + LogSpells("Too many perma procs for [{}]", GetName()); } else { for (i = 0; i < MAX_PROCS; i++) { if (SpellProcs[i].spellID == SPELL_UNKNOWN) { @@ -5364,11 +5400,11 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 b SpellProcs[i].chance = iChance; SpellProcs[i].base_spellID = base_spell_id;; SpellProcs[i].level_override = level_override; - Log(Logs::Detail, Logs::Spells, "Added spell-granted proc spell %d with chance %d to slot %d", spell_id, iChance, i); + LogSpells("Added spell-granted proc spell [{}] with chance [{}] to slot [{}]", spell_id, iChance, i); return true; } } - Log(Logs::Detail, Logs::Spells, "Too many procs for %s", GetName()); + LogSpells("Too many procs for [{}]", GetName()); } return false; } @@ -5380,7 +5416,7 @@ bool Mob::RemoveProcFromWeapon(uint16 spell_id, bool bAll) { SpellProcs[i].chance = 0; SpellProcs[i].base_spellID = SPELL_UNKNOWN; SpellProcs[i].level_override = -1; - Log(Logs::Detail, Logs::Spells, "Removed proc %d from slot %d", spell_id, i); + LogSpells("Removed proc [{}] from slot [{}]", spell_id, i); } } return true; @@ -5397,7 +5433,7 @@ bool Mob::AddDefensiveProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id DefensiveProcs[i].spellID = spell_id; DefensiveProcs[i].chance = iChance; DefensiveProcs[i].base_spellID = base_spell_id; - Log(Logs::Detail, Logs::Spells, "Added spell-granted defensive proc spell %d with chance %d to slot %d", spell_id, iChance, i); + LogSpells("Added spell-granted defensive proc spell [{}] with chance [{}] to slot [{}]", spell_id, iChance, i); return true; } } @@ -5412,7 +5448,7 @@ bool Mob::RemoveDefensiveProc(uint16 spell_id, bool bAll) DefensiveProcs[i].spellID = SPELL_UNKNOWN; DefensiveProcs[i].chance = 0; DefensiveProcs[i].base_spellID = SPELL_UNKNOWN; - Log(Logs::Detail, Logs::Spells, "Removed defensive proc %d from slot %d", spell_id, i); + LogSpells("Removed defensive proc [{}] from slot [{}]", spell_id, i); } } return true; @@ -5429,7 +5465,7 @@ bool Mob::AddRangedProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id) RangedProcs[i].spellID = spell_id; RangedProcs[i].chance = iChance; RangedProcs[i].base_spellID = base_spell_id; - Log(Logs::Detail, Logs::Spells, "Added spell-granted ranged proc spell %d with chance %d to slot %d", spell_id, iChance, i); + LogSpells("Added spell-granted ranged proc spell [{}] with chance [{}] to slot [{}]", spell_id, iChance, i); return true; } } @@ -5444,7 +5480,7 @@ bool Mob::RemoveRangedProc(uint16 spell_id, bool bAll) RangedProcs[i].spellID = SPELL_UNKNOWN; RangedProcs[i].chance = 0; RangedProcs[i].base_spellID = SPELL_UNKNOWN;; - Log(Logs::Detail, Logs::Spells, "Removed ranged proc %d from slot %d", spell_id, i); + LogSpells("Removed ranged proc [{}] from slot [{}]", spell_id, i); } } return true; @@ -5475,7 +5511,7 @@ bool Mob::UseBardSpellLogic(uint16 spell_id, int slot) int Mob::GetCasterLevel(uint16 spell_id) { int level = GetLevel(); level += itembonuses.effective_casting_level + spellbonuses.effective_casting_level + aabonuses.effective_casting_level; - Log(Logs::Detail, Logs::Spells, "Determined effective casting level %d+%d+%d=%d", GetLevel(), spellbonuses.effective_casting_level, itembonuses.effective_casting_level, level); + LogSpells("Determined effective casting level [{}]+[{}]+[{}]=[{}]", GetLevel(), spellbonuses.effective_casting_level, itembonuses.effective_casting_level, level); return std::max(1, level); } @@ -5911,7 +5947,7 @@ void Client::SetLinkedSpellReuseTimer(uint32 timer_id, uint32 duration) { if (timer_id > 19) return; - Log(Logs::Detail, Logs::Spells, "Setting Linked Spell Reuse %d for %d", timer_id, duration); + LogSpells("Setting Linked Spell Reuse [{}] for [{}]", timer_id, duration); GetPTimers().Start(pTimerLinkedSpellReuseStart + timer_id, duration); auto outapp = new EQApplicationPacket(OP_LinkedReuse, sizeof(LinkedSpellReuseTimer_Struct)); auto lr = (LinkedSpellReuseTimer_Struct *)outapp->pBuffer; diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 9137898c3..778a556e3 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -54,31 +54,34 @@ TaskManager::~TaskManager() { } } -bool TaskManager::LoadTaskSets() { +bool TaskManager::LoadTaskSets() +{ // Clear all task sets in memory. Done so we can reload them on the fly if required by just calling // this method again. - for(int i=0; i 0 AND `id` < %i " - "AND `taskid` >= 0 AND `taskid` < %i " - "ORDER BY `id`, `taskid` ASC", - MAXTASKSETS, MAXTASKS); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - Log(Logs::General, Logs::Error, "[TASKS]Error in TaskManager::LoadTaskSets: %s", results.ErrorMessage().c_str()); + std::string query = StringFormat( + "SELECT `id`, `taskid` from `tasksets` " + "WHERE `id` > 0 AND `id` < %i " + "AND `taskid` >= 0 AND `taskid` < %i " + "ORDER BY `id`, `taskid` ASC", + MAXTASKSETS, MAXTASKS + ); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogError("Error in TaskManager::LoadTaskSets: [{}]", results.ErrorMessage().c_str()); return false; - } + } for (auto row = results.begin(); row != results.end(); ++row) { - int taskSet = atoi(row[0]); - int taskID = atoi(row[1]); + int taskSet = atoi(row[0]); + int taskID = atoi(row[1]); - TaskSets[taskSet].push_back(taskID); - Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] Adding TaskID %4i to TaskSet %4i", taskID, taskSet); - } + TaskSets[taskSet].push_back(taskID); + Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] Adding TaskID %4i to TaskSet %4i", taskID, taskSet); + } return true; } @@ -130,7 +133,7 @@ bool TaskManager::LoadTasks(int singleTask) auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + LogError(ERR_MYSQLERROR, results.ErrorMessage().c_str()); return false; } @@ -139,8 +142,7 @@ bool TaskManager::LoadTasks(int singleTask) if ((taskID <= 0) || (taskID >= MAXTASKS)) { // This shouldn't happen, as the SELECT is bounded by MAXTASKS - Log(Logs::General, Logs::Error, - "[TASKS]Task ID %i out of range while loading tasks from database", taskID); + LogError("[TASKS]Task ID [{}] out of range while loading tasks from database", taskID); continue; } @@ -188,7 +190,7 @@ bool TaskManager::LoadTasks(int singleTask) singleTask, MAXACTIVITIESPERTASK); results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + LogError(ERR_MYSQLERROR, results.ErrorMessage().c_str()); return false; } @@ -200,16 +202,12 @@ bool TaskManager::LoadTasks(int singleTask) if ((taskID <= 0) || (taskID >= MAXTASKS) || (activityID < 0) || (activityID >= MAXACTIVITIESPERTASK)) { // This shouldn't happen, as the SELECT is bounded by MAXTASKS - Log(Logs::General, Logs::Error, - "[TASKS]Task or Activity ID (%i, %i) out of range while loading " - "activities from database", - taskID, activityID); + LogError("[TASKS]Task or Activity ID ([{}], [{}]) out of range while loading activities from database", taskID, activityID); continue; } if (Tasks[taskID] == nullptr) { - Log(Logs::General, Logs::Error, - "[TASKS]Activity for non-existent task (%i, %i) while loading activities from database", + LogError("[TASKS]Activity for non-existent task ([{}], [{}]) while loading activities from database", taskID, activityID); continue; } @@ -227,8 +225,7 @@ bool TaskManager::LoadTasks(int singleTask) // ERR_NOTASK errors. // Change to (activityID != (Tasks[taskID]->ActivityCount + 1)) to index from 1 if (activityID != Tasks[taskID]->ActivityCount) { - Log(Logs::General, Logs::Error, - "[TASKS]Activities for Task %i are not sequential starting at 0. Not loading task.", taskID, + LogError("[TASKS]Activities for Task [{}] are not sequential starting at 0. Not loading task", taskID, activityID); Tasks[taskID] = nullptr; continue; @@ -318,7 +315,7 @@ bool TaskManager::SaveClientState(Client *c, ClientTaskState *state) state->ActiveTasks[task].AcceptedTime); auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + LogError(ERR_MYSQLERROR, results.ErrorMessage().c_str()); } else { state->ActiveTasks[task].Updated = false; } @@ -362,7 +359,7 @@ bool TaskManager::SaveClientState(Client *c, ClientTaskState *state) auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + LogError(ERR_MYSQLERROR, results.ErrorMessage().c_str()); continue; } @@ -398,7 +395,7 @@ bool TaskManager::SaveClientState(Client *c, ClientTaskState *state) StringFormat(completedTaskQuery, characterID, state->CompletedTasks[i].CompletedTime, taskID, -1); auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + LogError(ERR_MYSQLERROR, results.ErrorMessage().c_str()); continue; } @@ -416,7 +413,7 @@ bool TaskManager::SaveClientState(Client *c, ClientTaskState *state) taskID, j); results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + LogError(ERR_MYSQLERROR, results.ErrorMessage().c_str()); } } @@ -467,7 +464,7 @@ bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) characterID); auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "[TASKS]Error in TaskManager::LoadClientState load Tasks: %s", + LogError("[TASKS]Error in TaskManager::LoadClientState load Tasks: [{}]", results.ErrorMessage().c_str()); return false; } @@ -478,21 +475,19 @@ bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) TaskType type = static_cast(atoi(row[2])); if ((taskID < 0) || (taskID >= MAXTASKS)) { - Log(Logs::General, Logs::Error, - "[TASKS]Task ID %i out of range while loading character tasks from database", taskID); + LogError("[TASKS]Task ID [{}] out of range while loading character tasks from database", taskID); continue; } auto task_info = state->GetClientTaskInfo(type, slot); if (task_info == nullptr) { - Log(Logs::General, Logs::Error, - "[TASKS] Slot %i out of range while loading character tasks from database", slot); + LogError("[TASKS] Slot [{}] out of range while loading character tasks from database", slot); continue; } if (task_info->TaskID != TASKSLOTEMPTY) { - Log(Logs::General, Logs::Error, "[TASKS] Slot %i for Task %is is already occupied.", slot, + LogError("[TASKS] Slot [{}] for Task [{}]s is already occupied", slot, taskID); continue; } @@ -526,7 +521,7 @@ bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) characterID); results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "[TASKS]Error in TaskManager::LoadClientState load Activities: %s", + LogError("[TASKS]Error in TaskManager::LoadClientState load Activities: [{}]", results.ErrorMessage().c_str()); return false; } @@ -534,15 +529,13 @@ bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) for (auto row = results.begin(); row != results.end(); ++row) { int taskID = atoi(row[0]); if ((taskID < 0) || (taskID >= MAXTASKS)) { - Log(Logs::General, Logs::Error, - "[TASKS]Task ID %i out of range while loading character activities from database", taskID); + LogError("[TASKS]Task ID [{}] out of range while loading character activities from database", taskID); continue; } int activityID = atoi(row[1]); if ((activityID < 0) || (activityID >= MAXACTIVITIESPERTASK)) { - Log(Logs::General, Logs::Error, - "[TASKS]Activity ID %i out of range while loading character activities from database", + LogError("[TASKS]Activity ID [{}] out of range while loading character activities from database", activityID); continue; } @@ -559,8 +552,7 @@ bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) task_info = &state->ActiveQuests[i]; if (task_info == nullptr) { - Log(Logs::General, Logs::Error, - "[TASKS]Activity %i found for task %i which client does not have.", activityID, taskID); + LogError("[TASKS]Activity [{}] found for task [{}] which client does not have", activityID, taskID); continue; } @@ -588,8 +580,7 @@ bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) characterID); results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, - "[TASKS]Error in TaskManager::LoadClientState load completed tasks: %s", + LogError("[TASKS]Error in TaskManager::LoadClientState load completed tasks: [{}]", results.ErrorMessage().c_str()); return false; } @@ -606,8 +597,7 @@ bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) int taskID = atoi(row[0]); if ((taskID <= 0) || (taskID >= MAXTASKS)) { - Log(Logs::General, Logs::Error, - "[TASKS]Task ID %i out of range while loading completed tasks from database", + LogError("[TASKS]Task ID [{}] out of range while loading completed tasks from database", taskID); continue; } @@ -618,8 +608,7 @@ bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) // completed. int activityID = atoi(row[1]); if ((activityID < -1) || (activityID >= MAXACTIVITIESPERTASK)) { - Log(Logs::General, Logs::Error, - "[TASKS]Activity ID %i out of range while loading completed tasks from database", + LogError("[TASKS]Activity ID [{}] out of range while loading completed tasks from database", activityID); continue; } @@ -660,7 +649,7 @@ bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) characterID, MAXTASKS); results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "[TASKS]Error in TaskManager::LoadClientState load enabled tasks: %s", + LogError("[TASKS]Error in TaskManager::LoadClientState load enabled tasks: [{}]", results.ErrorMessage().c_str()); } else { for (auto row = results.begin(); row != results.end(); ++row) { @@ -683,7 +672,7 @@ bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) "Removing from memory. Contact a GM to resolve this.", i, taskID); - Log(Logs::General, Logs::Error, "[TASKS]Character %i has task %i which does not exist.", + LogError("[TASKS]Character [{}] has task [{}] which does not exist", characterID, taskID); state->ActiveTasks[i].TaskID = TASKSLOTEMPTY; continue; @@ -696,9 +685,7 @@ bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) "Removing from memory. Contact a GM to resolve this.", taskID, Tasks[taskID]->Title.c_str()); - Log(Logs::General, Logs::Error, - "[TASKS]Fatal error in character %i task state. Activity %i for " - "Task %i either missing from client state or from task.", + LogError("[TASKS]Fatal error in character [{}] task state. Activity [{}] for Task [{}] either missing from client state or from task", characterID, j, taskID); state->ActiveTasks[i].TaskID = TASKSLOTEMPTY; break; @@ -3171,7 +3158,7 @@ void ClientTaskState::RemoveTask(Client *c, int sequenceNumber, TaskType type) characterID, task_id); auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "[TASKS] Error in CientTaskState::CancelTask %s", + LogError("[TASKS] Error in CientTaskState::CancelTask [{}]", results.ErrorMessage().c_str()); return; } @@ -3181,7 +3168,7 @@ void ClientTaskState::RemoveTask(Client *c, int sequenceNumber, TaskType type) task_id, static_cast(type)); results = database.QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "[TASKS] Error in CientTaskState::CancelTask %s", + LogError("[TASKS] Error in CientTaskState::CancelTask [{}]", results.ErrorMessage().c_str()); Log(Logs::General, Logs::Tasks, "[UPDATE] CancelTask: %s", query.c_str()); diff --git a/zone/titles.cpp b/zone/titles.cpp index 033edd144..7b4537b88 100644 --- a/zone/titles.cpp +++ b/zone/titles.cpp @@ -349,7 +349,7 @@ void Client::EnableTitle(int titleSet) { CharacterID(), titleSet); auto results = database.QueryDatabase(query); if(!results.Success()) - Log(Logs::General, Logs::Error, "Error in EnableTitle query for titleset %i and charid %i", titleSet, CharacterID()); + LogError("Error in EnableTitle query for titleset [{}] and charid [{}]", titleSet, CharacterID()); } diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 9230871c8..d94a2bdd8 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -42,7 +42,7 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme { if (!user || !in_augment) { - Log(Logs::General, Logs::Error, "Client or AugmentItem_Struct not set in Object::HandleAugmentation"); + LogError("Client or AugmentItem_Struct not set in Object::HandleAugmentation"); return; } @@ -89,7 +89,7 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme if(!container) { - Log(Logs::General, Logs::Error, "Player tried to augment an item without a container set."); + LogError("Player tried to augment an item without a container set"); user->Message(Chat::Red, "Error: This item is not a container!"); return; } @@ -169,7 +169,7 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme bool isSolvent = auged_with->GetItem()->ItemType == EQEmu::item::ItemTypeAugmentationSolvent; if (!isSolvent && auged_with->GetItem()->ItemType != EQEmu::item::ItemTypeAugmentationDistiller) { - Log(Logs::General, Logs::Error, "Player tried to remove an augment without a solvent or distiller."); + LogError("Player tried to remove an augment without a solvent or distiller"); user->Message(Chat::Red, "Error: Missing an augmentation solvent or distiller for removing this augment."); return; @@ -179,7 +179,7 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme if (aug) { if (!isSolvent && auged_with->GetItem()->ID != aug->GetItem()->AugDistiller) { - Log(Logs::General, Logs::Error, "Player tried to safely remove an augment with the wrong distiller (item %u vs expected %u).", auged_with->GetItem()->ID, aug->GetItem()->AugDistiller); + LogError("Player tried to safely remove an augment with the wrong distiller (item [{}] vs expected [{}])", auged_with->GetItem()->ID, aug->GetItem()->AugDistiller); user->Message(Chat::Red, "Error: Wrong augmentation distiller for safely removing this augment."); return; } @@ -252,7 +252,7 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Object *worldo) { if (!user || !in_combine) { - Log(Logs::General, Logs::Error, "Client or NewCombine_Struct not set in Object::HandleCombine"); + LogError("Client or NewCombine_Struct not set in Object::HandleCombine"); return; } @@ -433,7 +433,7 @@ void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Ob if(success && spec.replace_container) { if(worldcontainer){ //should report this error, but we dont have the recipe ID, so its not very useful - Log(Logs::General, Logs::Error, "Replace container combine executed in a world container."); + LogError("Replace container combine executed in a world container"); } else user->DeleteItemInInventory(in_combine->container_slot, 0, true); @@ -459,7 +459,7 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac //ask the database for the recipe to make sure it exists... DBTradeskillRecipe_Struct spec; if (!database.GetTradeRecipe(rac->recipe_id, rac->object_type, rac->some_id, user->CharacterID(), &spec)) { - Log(Logs::General, Logs::Error, "Unknown recipe for HandleAutoCombine: %u\n", rac->recipe_id); + LogError("Unknown recipe for HandleAutoCombine: [{}]\n", rac->recipe_id); user->QueuePacket(outapp); safe_delete(outapp); return; @@ -488,14 +488,14 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac } if(results.RowCount() < 1) { - Log(Logs::General, Logs::Error, "Error in HandleAutoCombine: no components returned"); + LogError("Error in HandleAutoCombine: no components returned"); user->QueuePacket(outapp); safe_delete(outapp); return; } if(results.RowCount() > 10) { - Log(Logs::General, Logs::Error, "Error in HandleAutoCombine: too many components returned (%u)", results.RowCount()); + LogError("Error in HandleAutoCombine: too many components returned ([{}])", results.RowCount()); user->QueuePacket(outapp); safe_delete(outapp); return; @@ -706,7 +706,7 @@ void Client::TradeskillSearchResults(const std::string &query, unsigned long obj return; //search gave no results... not an error if(results.ColumnCount() != 6) { - Log(Logs::General, Logs::Error, "Error in TradeskillSearchResults query '%s': Invalid column count in result", query.c_str()); + LogError("Error in TradeskillSearchResults query [{}]: Invalid column count in result", query.c_str()); return; } @@ -756,12 +756,12 @@ void Client::SendTradeskillDetails(uint32 recipe_id) { } if(results.RowCount() < 1) { - Log(Logs::General, Logs::Error, "Error in SendTradeskillDetails: no components returned"); + LogError("Error in SendTradeskillDetails: no components returned"); return; } if(results.RowCount() > 10) { - Log(Logs::General, Logs::Error, "Error in SendTradeskillDetails: too many components returned (%u)", results.RowCount()); + LogError("Error in SendTradeskillDetails: too many components returned ([{}])", results.RowCount()); return; } @@ -940,7 +940,7 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { //handle caps if(spec->nofail) { chance = 100; //cannot fail. - Log(Logs::Detail, Logs::Tradeskills, "...This combine cannot fail."); + LogTradeskills("This combine cannot fail"); } else if(over_trivial >= 0) { // At reaching trivial the chance goes to 95% going up an additional // percent for every 40 skillpoints above the trivial. @@ -960,8 +960,8 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { chance = 95; } - Log(Logs::Detail, Logs::Tradeskills, "...Current skill: %d , Trivial: %d , Success chance: %f percent", user_skill , spec->trivial , chance); - Log(Logs::Detail, Logs::Tradeskills, "...Bonusstat: %d , INT: %d , WIS: %d , DEX: %d , STR: %d", bonusstat , GetINT() , GetWIS() , GetDEX() , GetSTR()); + LogTradeskills("Current skill: [{}] , Trivial: [{}] , Success chance: [{}] percent", user_skill , spec->trivial , chance); + LogTradeskills("Bonusstat: [{}] , INT: [{}] , WIS: [{}] , DEX: [{}] , STR: [{}]", bonusstat , GetINT() , GetWIS() , GetDEX() , GetSTR()); float res = zone->random.Real(0, 99); int aa_chance = 0; @@ -980,7 +980,7 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { MessageString(Chat::LightBlue, TRADESKILL_SUCCEED, spec->name.c_str()); - Log(Logs::Detail, Logs::Tradeskills, "Tradeskill success"); + LogTradeskills("Tradeskill success"); itr = spec->onsuccess.begin(); while(itr != spec->onsuccess.end() && !spec->quest) { @@ -1012,7 +1012,7 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { MessageString(Chat::Emote,TRADESKILL_FAILED); - Log(Logs::Detail, Logs::Tradeskills, "Tradeskill failed"); + LogTradeskills("Tradeskill failed"); if (this->GetGroup()) { entity_list.MessageGroup(this, true, Chat::Skills,"%s was unsuccessful in %s tradeskill attempt.",GetName(),this->GetGender() == 0 ? "his" : this->GetGender() == 1 ? "her" : "its"); @@ -1091,9 +1091,9 @@ void Client::CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float NotifyNewTitlesAvailable(); } - Log(Logs::Detail, Logs::Tradeskills, "...skillup_modifier: %f , success_modifier: %d , stat modifier: %d", skillup_modifier , success_modifier , stat_modifier); - Log(Logs::Detail, Logs::Tradeskills, "...Stage1 chance was: %f percent", chance_stage1); - Log(Logs::Detail, Logs::Tradeskills, "...Stage2 chance was: %f percent. 0 percent means stage1 failed", chance_stage2); + LogTradeskills("skillup_modifier: [{}] , success_modifier: [{}] , stat modifier: [{}]", skillup_modifier , success_modifier , stat_modifier); + LogTradeskills("Stage1 chance was: [{}] percent", chance_stage1); + LogTradeskills("Stage2 chance was: [{}] percent. 0 percent means stage1 failed", chance_stage2); } bool ZoneDatabase::GetTradeRecipe(const EQEmu::ItemInstance* container, uint8 c_type, uint32 some_id, @@ -1146,8 +1146,8 @@ bool ZoneDatabase::GetTradeRecipe(const EQEmu::ItemInstance* container, uint8 c_ buf2.c_str(), containers.c_str(), count, sum); auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error in GetTradeRecipe search, query: %s", query.c_str()); - Log(Logs::General, Logs::Error, "Error in GetTradeRecipe search, error: %s", results.ErrorMessage().c_str()); + LogError("Error in GetTradeRecipe search, query: [{}]", query.c_str()); + LogError("Error in GetTradeRecipe search, error: [{}]", results.ErrorMessage().c_str()); return false; } @@ -1168,7 +1168,7 @@ bool ZoneDatabase::GetTradeRecipe(const EQEmu::ItemInstance* container, uint8 c_ //length limit on buf2 if(index == 214) { //Maximum number of recipe matches (19 * 215 = 4096) - Log(Logs::General, Logs::Error, "GetTradeRecipe warning: Too many matches. Unable to search all recipe entries. Searched %u of %u possible entries.", index + 1, results.RowCount()); + LogError("GetTradeRecipe warning: Too many matches. Unable to search all recipe entries. Searched [{}] of [{}] possible entries", index + 1, results.RowCount()); break; } } @@ -1180,8 +1180,8 @@ bool ZoneDatabase::GetTradeRecipe(const EQEmu::ItemInstance* container, uint8 c_ "AND sum(tre.item_id * tre.componentcount) = %u", buf2.c_str(), count, sum); results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error in GetTradeRecipe, re-query: %s", query.c_str()); - Log(Logs::General, Logs::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str()); + LogError("Error in GetTradeRecipe, re-query: [{}]", query.c_str()); + LogError("Error in GetTradeRecipe, error: [{}]", results.ErrorMessage().c_str()); return false; } } @@ -1189,36 +1189,45 @@ bool ZoneDatabase::GetTradeRecipe(const EQEmu::ItemInstance* container, uint8 c_ if (results.RowCount() < 1) return false; - if(results.RowCount() > 1) { + if (results.RowCount() > 1) { //The recipe is not unique, so we need to compare the container were using. uint32 containerId = 0; - if(some_id) //Standard container + if (some_id) { //Standard container containerId = some_id; - else if(c_type)//World container + } + else if (c_type) {//World container containerId = c_type; - else //Invalid container + } + else { //Invalid container return false; + } - query = StringFormat("SELECT tre.recipe_id " - "FROM tradeskill_recipe_entries AS tre " - "WHERE tre.recipe_id IN (%s) " - "AND tre.item_id = %u;", buf2.c_str(), containerId); - results = QueryDatabase(query); + query = StringFormat( + "SELECT tre.recipe_id " + "FROM tradeskill_recipe_entries AS tre " + "WHERE tre.recipe_id IN (%s) " + "AND tre.item_id = %u;", buf2.c_str(), containerId + ); + results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error in GetTradeRecipe, re-query: %s", query.c_str()); - Log(Logs::General, Logs::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str()); + LogError("Error in GetTradeRecipe, re-query: [{}]", query.c_str()); + LogError("Error in GetTradeRecipe, error: [{}]", results.ErrorMessage().c_str()); return false; } - if(results.RowCount() == 0) { //Recipe contents matched more than 1 recipe, but not in this container - Log(Logs::General, Logs::Error, "Combine error: Incorrect container is being used!"); + if (results.RowCount() == 0) { //Recipe contents matched more than 1 recipe, but not in this container + LogError("Combine error: Incorrect container is being used!"); return false; } - if (results.RowCount() > 1) //Recipe contents matched more than 1 recipe in this container - Log(Logs::General, Logs::Error, "Combine error: Recipe is not unique! %u matches found for container %u. Continuing with first recipe match.", results.RowCount(), containerId); - + if (results.RowCount() > 1) { //Recipe contents matched more than 1 recipe in this container + LogError( + "Combine error: Recipe is not unique! [{}] matches found for container [{}]. Continuing with first recipe match", + results.RowCount(), + containerId + ); + } } auto row = results.begin(); @@ -1288,8 +1297,8 @@ bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id char_id, (unsigned long)recipe_id, containers.c_str()); auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error in GetTradeRecipe, query: %s", query.c_str()); - Log(Logs::General, Logs::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str()); + LogError("Error in GetTradeRecipe, query: [{}]", query.c_str()); + LogError("Error in GetTradeRecipe, error: [{}]", results.ErrorMessage().c_str()); return false; } @@ -1324,7 +1333,7 @@ bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id } if(results.RowCount() < 1 && !spec->quest) { - Log(Logs::General, Logs::Error, "Error in GetTradeRecept success: no success items returned"); + LogError("Error in GetTradeRecept success: no success items returned"); return false; } @@ -1391,7 +1400,7 @@ void Client::LearnRecipe(uint32 recipeID) } if (results.RowCount() != 1) { - Log(Logs::General, Logs::Normal, "Client::LearnRecipe - RecipeID: %d had %d occurences.", recipeID, results.RowCount()); + LogInfo("Client::LearnRecipe - RecipeID: [{}] had [{}] occurences", recipeID, results.RowCount()); return; } diff --git a/zone/trading.cpp b/zone/trading.cpp index 2d8f03e4f..1618e7770 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -87,7 +87,7 @@ void Trade::AddEntity(uint16 trade_slot_id, uint32 stack_size) { if (!owner || !owner->IsClient()) { // This should never happen - Log(Logs::General, Logs::None, "Programming error: NPC's should not call Trade::AddEntity()"); + LogDebug("Programming error: NPC's should not call Trade::AddEntity()"); return; } @@ -127,7 +127,7 @@ void Trade::AddEntity(uint16 trade_slot_id, uint32 stack_size) { inst2->SetCharges(stack_size + inst2->GetCharges()); } - Log(Logs::Detail, Logs::Trading, "%s added partial item '%s' stack (qty: %i) to trade slot %i", owner->GetName(), inst->GetItem()->Name, stack_size, trade_slot_id); + LogTrading("[{}] added partial item [{}] stack (qty: [{}]) to trade slot [{}]", owner->GetName(), inst->GetItem()->Name, stack_size, trade_slot_id); if (_stack_size > 0) inst->SetCharges(_stack_size); @@ -144,7 +144,7 @@ void Trade::AddEntity(uint16 trade_slot_id, uint32 stack_size) { SendItemData(inst, trade_slot_id); - Log(Logs::Detail, Logs::Trading, "%s added item '%s' to trade slot %i", owner->GetName(), inst->GetItem()->Name, trade_slot_id); + LogTrading("[{}] added item [{}] to trade slot [{}]", owner->GetName(), inst->GetItem()->Name, trade_slot_id); client->PutItemInInventory(trade_slot_id, *inst); client->DeleteItemInInventory(EQEmu::invslot::slotCursor); @@ -297,7 +297,7 @@ void Trade::LogTrade() void Trade::DumpTrade() { Mob* with = With(); - Log(Logs::Detail, Logs::Trading, "Dumping trade data: '%s' in TradeState %i with '%s'", + LogTrading("Dumping trade data: [{}] in TradeState [{}] with [{}]", this->owner->GetName(), state, ((with==nullptr)?"(null)":with->GetName())); if (!owner->IsClient()) @@ -308,7 +308,7 @@ void Trade::DumpTrade() const EQEmu::ItemInstance* inst = trader->GetInv().GetItem(i); if (inst) { - Log(Logs::Detail, Logs::Trading, "Item %i (Charges=%i, Slot=%i, IsBag=%s)", + LogTrading("Item [{}] (Charges=[{}], Slot=[{}], IsBag=[{}])", inst->GetItem()->ID, inst->GetCharges(), i, ((inst->IsClassBag()) ? "True" : "False")); @@ -316,7 +316,7 @@ void Trade::DumpTrade() for (uint8 j = EQEmu::invbag::SLOT_BEGIN; j <= EQEmu::invbag::SLOT_END; j++) { inst = trader->GetInv().GetItem(i, j); if (inst) { - Log(Logs::Detail, Logs::Trading, "\tBagItem %i (Charges=%i, Slot=%i)", + LogTrading("\tBagItem [{}] (Charges=[{}], Slot=[{}])", inst->GetItem()->ID, inst->GetCharges(), EQEmu::InventoryProfile::CalcSlotId(i, j)); } @@ -325,7 +325,7 @@ void Trade::DumpTrade() } } - Log(Logs::Detail, Logs::Trading, "\tpp:%i, gp:%i, sp:%i, cp:%i", pp, gp, sp, cp); + LogTrading("\tpp:[{}], gp:[{}], sp:[{}], cp:[{}]", pp, gp, sp, cp); } @@ -369,7 +369,7 @@ void Client::ResetTrade() { break; if (partial_inst->GetID() != inst->GetID()) { - Log(Logs::Detail, Logs::None, "[CLIENT] Client::ResetTrade() - an incompatible location reference was returned by Inventory::FindFreeSlotForTradeItem()"); + LogDebug("[CLIENT] Client::ResetTrade() - an incompatible location reference was returned by Inventory::FindFreeSlotForTradeItem()"); break; } @@ -459,7 +459,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st bool qs_log = false; if(other) { - Log(Logs::Detail, Logs::Trading, "Finishing trade with client %s", other->GetName()); + LogTrading("Finishing trade with client [{}]", other->GetName()); this->AddMoneyToPP(other->trade->cp, other->trade->sp, other->trade->gp, other->trade->pp, true); @@ -492,7 +492,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st const EQEmu::ItemInstance* inst = m_inv[trade_slot]; if (inst && inst->IsClassBag()) { - Log(Logs::Detail, Logs::Trading, "Giving container %s (%d) in slot %d to %s", inst->GetItem()->Name, inst->GetItem()->ID, trade_slot, other->GetName()); + LogTrading("Giving container [{}] ([{}]) in slot [{}] to [{}]", inst->GetItem()->Name, inst->GetItem()->ID, trade_slot, other->GetName()); // TODO: need to check bag items/augments for no drop..everything for attuned... if (inst->GetItem()->NoDrop != 0 || Admin() >= RuleI(Character, MinStatusForNoDropExemptions) || RuleI(World, FVNoDropFlag) == 1 || other == this) { @@ -500,7 +500,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st if (free_slot != INVALID_INDEX) { if (other->PutItemInInventory(free_slot, *inst, true)) { - Log(Logs::Detail, Logs::Trading, "Container %s (%d) successfully transferred, deleting from trade slot.", inst->GetItem()->Name, inst->GetItem()->ID); + LogTrading("Container [{}] ([{}]) successfully transferred, deleting from trade slot", inst->GetItem()->Name, inst->GetItem()->ID); if (qs_log) { auto detail = new QSTradeItems_Struct; @@ -552,17 +552,17 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st } } else { - Log(Logs::Detail, Logs::Trading, "Transfer of container %s (%d) to %s failed, returning to giver.", inst->GetItem()->Name, inst->GetItem()->ID, other->GetName()); + LogTrading("Transfer of container [{}] ([{}]) to [{}] failed, returning to giver", inst->GetItem()->Name, inst->GetItem()->ID, other->GetName()); PushItemOnCursor(*inst, true); } } else { - Log(Logs::Detail, Logs::Trading, "%s's inventory is full, returning container %s (%d) to giver.", other->GetName(), inst->GetItem()->Name, inst->GetItem()->ID); + LogTrading("[{}]'s inventory is full, returning container [{}] ([{}]) to giver", other->GetName(), inst->GetItem()->Name, inst->GetItem()->ID); PushItemOnCursor(*inst, true); } } else { - Log(Logs::Detail, Logs::Trading, "Container %s (%d) is NoDrop, returning to giver.", inst->GetItem()->Name, inst->GetItem()->ID); + LogTrading("Container [{}] ([{}]) is NoDrop, returning to giver", inst->GetItem()->Name, inst->GetItem()->ID); PushItemOnCursor(*inst, true); } @@ -588,7 +588,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st break; if (partial_inst->GetID() != inst->GetID()) { - Log(Logs::Detail, Logs::Trading, "[CLIENT] Client::ResetTrade() - an incompatible location reference was returned by Inventory::FindFreeSlotForTradeItem()"); + LogTrading("[CLIENT] Client::ResetTrade() - an incompatible location reference was returned by Inventory::FindFreeSlotForTradeItem()"); break; } @@ -606,10 +606,10 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st inst->SetCharges(0); } - Log(Logs::Detail, Logs::Trading, "Transferring partial stack %s (%d) in slot %d to %s", inst->GetItem()->Name, inst->GetItem()->ID, trade_slot, other->GetName()); + LogTrading("Transferring partial stack [{}] ([{}]) in slot [{}] to [{}]", inst->GetItem()->Name, inst->GetItem()->ID, trade_slot, other->GetName()); if (other->PutItemInInventory(partial_slot, *partial_inst, true)) { - Log(Logs::Detail, Logs::Trading, "Partial stack %s (%d) successfully transferred, deleting %i charges from trade slot.", + LogTrading("Partial stack [{}] ([{}]) successfully transferred, deleting [{}] charges from trade slot", inst->GetItem()->Name, inst->GetItem()->ID, (old_charges - inst->GetCharges())); if (qs_log) { auto detail = new QSTradeItems_Struct; @@ -635,7 +635,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st } } else { - Log(Logs::Detail, Logs::Trading, "Transfer of partial stack %s (%d) to %s failed, returning %i charges to trade slot.", + LogTrading("Transfer of partial stack [{}] ([{}]) to [{}] failed, returning [{}] charges to trade slot", inst->GetItem()->Name, inst->GetItem()->ID, other->GetName(), (old_charges - inst->GetCharges())); inst->SetCharges(old_charges); @@ -710,7 +710,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st const EQEmu::ItemInstance* inst = m_inv[trade_slot]; if (inst) { - Log(Logs::Detail, Logs::Trading, "Giving item %s (%d) in slot %d to %s", inst->GetItem()->Name, inst->GetItem()->ID, trade_slot, other->GetName()); + LogTrading("Giving item [{}] ([{}]) in slot [{}] to [{}]", inst->GetItem()->Name, inst->GetItem()->ID, trade_slot, other->GetName()); // TODO: need to check bag items/augments for no drop..everything for attuned... if (inst->GetItem()->NoDrop != 0 || Admin() >= RuleI(Character, MinStatusForNoDropExemptions) || RuleI(World, FVNoDropFlag) == 1 || other == this) { @@ -718,7 +718,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st if (free_slot != INVALID_INDEX) { if (other->PutItemInInventory(free_slot, *inst, true)) { - Log(Logs::Detail, Logs::Trading, "Item %s (%d) successfully transferred, deleting from trade slot.", inst->GetItem()->Name, inst->GetItem()->ID); + LogTrading("Item [{}] ([{}]) successfully transferred, deleting from trade slot", inst->GetItem()->Name, inst->GetItem()->ID); if (qs_log) { auto detail = new QSTradeItems_Struct; @@ -771,17 +771,17 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st } } else { - Log(Logs::Detail, Logs::Trading, "Transfer of Item %s (%d) to %s failed, returning to giver.", inst->GetItem()->Name, inst->GetItem()->ID, other->GetName()); + LogTrading("Transfer of Item [{}] ([{}]) to [{}] failed, returning to giver", inst->GetItem()->Name, inst->GetItem()->ID, other->GetName()); PushItemOnCursor(*inst, true); } } else { - Log(Logs::Detail, Logs::Trading, "%s's inventory is full, returning item %s (%d) to giver.", other->GetName(), inst->GetItem()->Name, inst->GetItem()->ID); + LogTrading("[{}]'s inventory is full, returning item [{}] ([{}]) to giver", other->GetName(), inst->GetItem()->Name, inst->GetItem()->ID); PushItemOnCursor(*inst, true); } } else { - Log(Logs::Detail, Logs::Trading, "Item %s (%d) is NoDrop, returning to giver.", inst->GetItem()->Name, inst->GetItem()->ID); + LogTrading("Item [{}] ([{}]) is NoDrop, returning to giver", inst->GetItem()->Name, inst->GetItem()->ID); PushItemOnCursor(*inst, true); } @@ -1178,7 +1178,7 @@ void Client::SendTraderItem(uint32 ItemID, uint16 Quantity) { const EQEmu::ItemData* item = database.GetItem(ItemID); if(!item){ - Log(Logs::Detail, Logs::Trading, "Bogus item deleted in Client::SendTraderItem!\n"); + LogTrading("Bogus item deleted in Client::SendTraderItem!\n"); return; } @@ -1237,10 +1237,8 @@ void Client::BulkSendTraderInventory(uint32 char_id) { safe_delete(inst); } else - Log(Logs::Detail, Logs::Trading, "Client::BulkSendTraderInventory nullptr inst pointer"); + LogTrading("Client::BulkSendTraderInventory nullptr inst pointer"); } - else - Log(Logs::Detail, Logs::Trading, "Client::BulkSendTraderInventory nullptr item pointer or item is NODROP %8X",item); } safe_delete(TraderItems); } @@ -1263,7 +1261,7 @@ uint32 Client::FindTraderItemSerialNumber(int32 ItemID) { } } } - Log(Logs::Detail, Logs::Trading, "Client::FindTraderItemSerialNumber Couldn't find item! Item ID %i", ItemID); + LogTrading("Client::FindTraderItemSerialNumber Couldn't find item! Item ID [{}]", ItemID); return 0; } @@ -1286,7 +1284,7 @@ EQEmu::ItemInstance* Client::FindTraderItemBySerialNumber(int32 SerialNumber){ } } } - Log(Logs::Detail, Logs::Trading, "Client::FindTraderItemBySerialNumber Couldn't find item! Serial No. was %i", SerialNumber); + LogTrading("Client::FindTraderItemBySerialNumber Couldn't find item! Serial No. was [{}]", SerialNumber); return nullptr; } @@ -1348,7 +1346,7 @@ uint16 Client::FindTraderItem(int32 SerialNumber, uint16 Quantity){ } } } - Log(Logs::Detail, Logs::Trading, "Could NOT find a match for Item: %i with a quantity of: %i on Trader: %s\n", + LogTrading("Could NOT find a match for Item: [{}] with a quantity of: [{}] on Trader: [{}]\n", SerialNumber , Quantity, this->GetName()); return 0; @@ -1359,7 +1357,7 @@ void Client::NukeTraderItem(uint16 Slot,int16 Charges,uint16 Quantity,Client* Cu if(!Customer) return; - Log(Logs::Detail, Logs::Trading, "NukeTraderItem(Slot %i, Charges %i, Quantity %i", Slot, Charges, Quantity); + LogTrading("NukeTraderItem(Slot [{}], Charges [{}], Quantity [{}]", Slot, Charges, Quantity); if(Quantity < Charges) { @@ -1446,7 +1444,7 @@ void Client::FindAndNukeTraderItem(int32 SerialNumber, uint16 Quantity, Client* if (!item) { - Log(Logs::Detail, Logs::Trading, "Could not find Item: %i on Trader: %s", SerialNumber, Quantity, this->GetName()); + LogTrading("Could not find Item: [{}] on Trader: [{}]", SerialNumber, Quantity, this->GetName()); return; } @@ -1457,7 +1455,7 @@ void Client::FindAndNukeTraderItem(int32 SerialNumber, uint16 Quantity, Client* if (!Stackable) Quantity = (Charges > 0) ? Charges : 1; - Log(Logs::Detail, Logs::Trading, "FindAndNuke %s, Charges %i, Quantity %i", item->GetItem()->Name, Charges, Quantity); + LogTrading("FindAndNuke [{}], Charges [{}], Quantity [{}]", item->GetItem()->Name, Charges, Quantity); if (Charges <= Quantity || (Charges <= 0 && Quantity==1) || !Stackable) { @@ -1499,7 +1497,7 @@ void Client::FindAndNukeTraderItem(int32 SerialNumber, uint16 Quantity, Client* } } - Log(Logs::Detail, Logs::Trading, "Could NOT find a match for Item: %i with a quantity of: %i on Trader: %s\n",SerialNumber, + LogTrading("Could NOT find a match for Item: [{}] with a quantity of: [{}] on Trader: [{}]\n",SerialNumber, Quantity,this->GetName()); } @@ -1599,7 +1597,7 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs, Client* Trader, const EQApplic BuyItem = Trader->FindTraderItemBySerialNumber(tbs->ItemID); if(!BuyItem) { - Log(Logs::Detail, Logs::Trading, "Unable to find item on trader."); + LogTrading("Unable to find item on trader"); TradeRequestFailed(app); safe_delete(outapp); return; @@ -1607,7 +1605,7 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs, Client* Trader, const EQApplic tbs->Price = BuyItem->GetPrice(); - Log(Logs::Detail, Logs::Trading, "Buyitem: Name: %s, IsStackable: %i, Requested Quantity: %i, Charges on Item %i", + LogTrading("Buyitem: Name: [{}], IsStackable: [{}], Requested Quantity: [{}], Charges on Item [{}]", BuyItem->GetItem()->Name, BuyItem->IsStackable(), tbs->Quantity, BuyItem->GetCharges()); // If the item is not stackable, then we can only be buying one of them. if(!BuyItem->IsStackable()) @@ -1625,13 +1623,12 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs, Client* Trader, const EQApplic outtbs->Quantity = tbs->Quantity; } - Log(Logs::Detail, Logs::Trading, "Actual quantity that will be traded is %i", outtbs->Quantity); + LogTrading("Actual quantity that will be traded is [{}]", outtbs->Quantity); if((tbs->Price * outtbs->Quantity) <= 0) { Message(Chat::Red, "Internal error. Aborting trade. Please report this to the ServerOP. Error code is 1"); Trader->Message(Chat::Red, "Internal error. Aborting trade. Please report this to the ServerOP. Error code is 1"); - Log(Logs::General, Logs::Error, "Bazaar: Zero price transaction between %s and %s aborted." - "Item: %s, Charges: %i, TBS: Qty %i, Price: %i", + LogError("Bazaar: Zero price transaction between [{}] and [{}] aborted. Item: [{}], Charges: [{}], TBS: Qty [{}], Price: [{}]", GetName(), Trader->GetName(), BuyItem->GetItem()->Name, BuyItem->GetCharges(), tbs->Quantity, tbs->Price); TradeRequestFailed(app); @@ -1668,7 +1665,7 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs, Client* Trader, const EQApplic return; } - Log(Logs::Detail, Logs::Trading, "Customer Paid: %d in Copper", TotalCost); + LogTrading("Customer Paid: [{}] in Copper", TotalCost); uint32 platinum = TotalCost / 1000; TotalCost -= (platinum * 1000); @@ -1680,7 +1677,7 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs, Client* Trader, const EQApplic Trader->AddMoneyToPP(copper, silver, gold, platinum, true); - Log(Logs::Detail, Logs::Trading, "Trader Received: %d Platinum, %d Gold, %d Silver, %d Copper", platinum, gold, silver, copper); + LogTrading("Trader Received: [{}] Platinum, [{}] Gold, [{}] Silver, [{}] Copper", platinum, gold, silver, copper); ReturnTraderReq(app, outtbs->Quantity, ItemID); @@ -1939,7 +1936,7 @@ void Client::SendBazaarResults(uint32 TraderID, uint32 Class_, uint32 Race, uint return; } - Log(Logs::Detail, Logs::Trading, "SRCH: %s", query.c_str()); + LogTrading("SRCH: [{}]", query.c_str()); int Size = 0; uint32 ID = 0; @@ -1984,7 +1981,7 @@ void Client::SendBazaarResults(uint32 TraderID, uint32 Class_, uint32 Race, uint ID = Trader2->GetID(); VARSTRUCT_ENCODE_TYPE(uint32, bufptr, ID); } else { - Log(Logs::Detail, Logs::Trading, "Unable to find trader: %i\n", atoi(row[1])); + LogTrading("Unable to find trader: [{}]\n", atoi(row[1])); VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 0); } Cost = atoi(row[5]); @@ -2074,7 +2071,7 @@ static void UpdateTraderCustomerItemsAdded(uint32 CustomerID, TraderCharges_Stru if(inst->IsStackable()) inst->SetMerchantCount(gis->Charges[i]); - Log(Logs::Detail, Logs::Trading, "Sending price update for %s, Serial No. %i with %i charges", + LogTrading("Sending price update for [{}], Serial No. [{}] with [{}] charges", item->Name, gis->SerialNumber[i], gis->Charges[i]); Customer->SendItemPacket(EQEmu::invslot::slotCursor, inst, ItemPacketMerchant); // MainCursor? @@ -2120,7 +2117,7 @@ static void UpdateTraderCustomerPriceChanged(uint32 CustomerID, TraderCharges_St tdis->ItemID = gis->SerialNumber[i]; } tdis->ItemID = gis->SerialNumber[i]; - Log(Logs::Detail, Logs::Trading, "Telling customer to remove item %i with %i charges and S/N %i", + LogTrading("Telling customer to remove item [{}] with [{}] charges and S/N [{}]", ItemID, Charges, gis->SerialNumber[i]); @@ -2132,7 +2129,7 @@ static void UpdateTraderCustomerPriceChanged(uint32 CustomerID, TraderCharges_St return; } - Log(Logs::Detail, Logs::Trading, "Sending price updates to customer %s", Customer->GetName()); + LogTrading("Sending price updates to customer [{}]", Customer->GetName()); EQEmu::ItemInstance* inst = database.CreateItem(item); @@ -2158,7 +2155,7 @@ static void UpdateTraderCustomerPriceChanged(uint32 CustomerID, TraderCharges_St inst->SetMerchantSlot(gis->SerialNumber[i]); - Log(Logs::Detail, Logs::Trading, "Sending price update for %s, Serial No. %i with %i charges", + LogTrading("Sending price update for [{}], Serial No. [{}] with [{}] charges", item->Name, gis->SerialNumber[i], gis->Charges[i]); Customer->SendItemPacket(EQEmu::invslot::slotCursor, inst, ItemPacketMerchant); // MainCursor?? @@ -2174,7 +2171,7 @@ void Client::HandleTraderPriceUpdate(const EQApplicationPacket *app) { // TraderPriceUpdate_Struct* tpus = (TraderPriceUpdate_Struct*)app->pBuffer; - Log(Logs::Detail, Logs::Trading, "Received Price Update for %s, Item Serial No. %i, New Price %i", + LogTrading("Received Price Update for [{}], Item Serial No. [{}], New Price [{}]", GetName(), tpus->SerialNumber, tpus->NewPrice); // Pull the items this Trader currently has for sale from the trader table. @@ -2182,7 +2179,7 @@ void Client::HandleTraderPriceUpdate(const EQApplicationPacket *app) { TraderCharges_Struct* gis = database.LoadTraderItemWithCharges(CharacterID()); if(!gis) { - Log(Logs::Detail, Logs::None, "[CLIENT] Error retrieving Trader items details to update price."); + LogDebug("[CLIENT] Error retrieving Trader items details to update price"); return; } @@ -2202,7 +2199,7 @@ void Client::HandleTraderPriceUpdate(const EQApplicationPacket *app) { if((gis->ItemID[i] > 0) && (gis->SerialNumber[i] == tpus->SerialNumber)) { // We found the item that the Trader wants to change the price of (or add back up for sale). // - Log(Logs::Detail, Logs::Trading, "ItemID is %i, Charges is %i", gis->ItemID[i], gis->Charges[i]); + LogTrading("ItemID is [{}], Charges is [{}]", gis->ItemID[i], gis->Charges[i]); IDOfItemToUpdate = gis->ItemID[i]; @@ -2228,7 +2225,7 @@ void Client::HandleTraderPriceUpdate(const EQApplicationPacket *app) { return ; } - Log(Logs::Detail, Logs::Trading, "Unable to find item to update price for. Rechecking trader satchels"); + LogTrading("Unable to find item to update price for. Rechecking trader satchels"); // Find what is in their Trader Satchels GetItems_Struct* newgis=GetTraderItems(); @@ -2241,7 +2238,7 @@ void Client::HandleTraderPriceUpdate(const EQApplicationPacket *app) { if((newgis->Items[i] > 0) && (newgis->SerialNumber[i] == tpus->SerialNumber)) { - Log(Logs::Detail, Logs::Trading, "Found new Item to Add, ItemID is %i, Charges is %i", newgis->Items[i], + LogTrading("Found new Item to Add, ItemID is [{}], Charges is [{}]", newgis->Items[i], newgis->Charges[i]); IDOfItemToAdd = newgis->Items[i]; @@ -2259,7 +2256,7 @@ void Client::HandleTraderPriceUpdate(const EQApplicationPacket *app) { if(!IDOfItemToAdd || !item) { - Log(Logs::Detail, Logs::Trading, "Item not found in Trader Satchels either."); + LogTrading("Item not found in Trader Satchels either"); tpus->SubAction = BazaarPriceChange_Fail; QueuePacket(app); Trader_EndTrader(); @@ -2304,7 +2301,7 @@ void Client::HandleTraderPriceUpdate(const EQApplicationPacket *app) { gis->SerialNumber[i] = newgis->SerialNumber[i]; gis->ItemCost[i] = tpus->NewPrice; - Log(Logs::Detail, Logs::Trading, "Adding new item for %s. ItemID %i, SerialNumber %i, Charges %i, Price: %i, Slot %i", + LogTrading("Adding new item for [{}]. ItemID [{}], SerialNumber [{}], Charges [{}], Price: [{}], Slot [{}]", GetName(), newgis->Items[i], newgis->SerialNumber[i], newgis->Charges[i], tpus->NewPrice, i); } @@ -2350,7 +2347,7 @@ void Client::HandleTraderPriceUpdate(const EQApplicationPacket *app) { QueuePacket(app); if(OldPrice == tpus->NewPrice) { - Log(Logs::Detail, Logs::Trading, "The new price is the same as the old one."); + LogTrading("The new price is the same as the old one"); safe_delete(gis); return; } @@ -2371,7 +2368,7 @@ void Client::SendBuyerResults(char* searchString, uint32 searchID) { // This method is called when a potential seller in the /barter window searches for matching buyers // - Log(Logs::Detail, Logs::None, "[CLIENT] Client::SendBuyerResults %s\n", searchString); + LogDebug("[CLIENT] Client::SendBuyerResults [{}]\n", searchString); auto escSearchString = new char[strlen(searchString) * 2 + 1]; database.DoEscapeString(escSearchString, searchString, strlen(searchString)); @@ -2619,7 +2616,7 @@ void Client::SellToBuyer(const EQApplicationPacket *app) { Quantity = i; break; } - Log(Logs::General, Logs::Error, "Unexpected error while moving item from seller to buyer."); + LogError("Unexpected error while moving item from seller to buyer"); Message(Chat::Red, "Internal error while processing transaction."); return; } @@ -2627,7 +2624,7 @@ void Client::SellToBuyer(const EQApplicationPacket *app) { EQEmu::ItemInstance* ItemToTransfer = m_inv.PopItem(SellerSlot); if(!ItemToTransfer || !Buyer->MoveItemToInventory(ItemToTransfer, true)) { - Log(Logs::General, Logs::Error, "Unexpected error while moving item from seller to buyer."); + LogError("Unexpected error while moving item from seller to buyer"); Message(Chat::Red, "Internal error while processing transaction."); if(ItemToTransfer) @@ -2665,7 +2662,7 @@ void Client::SellToBuyer(const EQApplicationPacket *app) { int16 SellerSlot = m_inv.HasItem(ItemID, 1, invWhereWorn|invWherePersonal|invWhereCursor); if (SellerSlot == INVALID_INDEX) { - Log(Logs::General, Logs::Error, "Unexpected error while moving item from seller to buyer."); + LogError("Unexpected error while moving item from seller to buyer"); Message(Chat::Red, "Internal error while processing transaction."); return; } @@ -2673,7 +2670,7 @@ void Client::SellToBuyer(const EQApplicationPacket *app) { EQEmu::ItemInstance* ItemToTransfer = m_inv.PopItem(SellerSlot); if(!ItemToTransfer) { - Log(Logs::General, Logs::Error, "Unexpected error while moving item from seller to buyer."); + LogError("Unexpected error while moving item from seller to buyer"); Message(Chat::Red, "Internal error while processing transaction."); return; } @@ -2685,7 +2682,7 @@ void Client::SellToBuyer(const EQApplicationPacket *app) { QuantityMoved += ItemToTransfer->GetCharges(); if(!Buyer->MoveItemToInventory(ItemToTransfer, true)) { - Log(Logs::General, Logs::Error, "Unexpected error while moving item from seller to buyer."); + LogError("Unexpected error while moving item from seller to buyer"); Message(Chat::Red, "Internal error while processing transaction."); safe_delete(ItemToTransfer); return; @@ -2720,7 +2717,7 @@ void Client::SellToBuyer(const EQApplicationPacket *app) { ItemToTransfer->SetCharges(QuantityToRemoveFromStack); if(!Buyer->MoveItemToInventory(ItemToTransfer, true)) { - Log(Logs::General, Logs::Error, "Unexpected error while moving item from seller to buyer."); + LogError("Unexpected error while moving item from seller to buyer"); Message(Chat::Red, "Internal error while processing transaction."); safe_delete(ItemToTransfer); return; @@ -2955,11 +2952,11 @@ void Client::UpdateBuyLine(const EQApplicationPacket *app) { bool LoreConflict = CheckLoreConflict(item); - Log(Logs::Detail, Logs::Trading, "UpdateBuyLine: Char: %s BuySlot: %i ItemID %i %s Quantity %i Toggle: %i Price %i ItemCount %i LoreConflict %i", + LogTrading("UpdateBuyLine: Char: [{}] BuySlot: [{}] ItemID [{}] [{}] Quantity [{}] Toggle: [{}] Price [{}] ItemCount [{}] LoreConflict [{}]", GetName(), BuySlot, ItemID, item->Name, Quantity, ToggleOnOff, Price, ItemCount, LoreConflict); if((item->NoDrop != 0) && !LoreConflict && (Quantity > 0) && HasMoney(Quantity * Price) && ToggleOnOff && (ItemCount == 0)) { - Log(Logs::Detail, Logs::Trading, "Adding to database"); + LogTrading("Adding to database"); database.AddBuyLine(CharacterID(), BuySlot, ItemID, ItemName, Quantity, Price); QueuePacket(app); } diff --git a/zone/tribute.cpp b/zone/tribute.cpp index a52d5d1d6..3e80f5446 100644 --- a/zone/tribute.cpp +++ b/zone/tribute.cpp @@ -220,7 +220,7 @@ void Client::ChangeTributeSettings(TributeInfo_Struct *t) { void Client::SendTributeDetails(uint32 client_id, uint32 tribute_id) { if(tribute_list.count(tribute_id) != 1) { - Log(Logs::General, Logs::Error, "Details request for invalid tribute %lu", (unsigned long)tribute_id); + LogError("Details request for invalid tribute [{}]", (unsigned long)tribute_id); return; } TributeData &td = tribute_list[tribute_id]; @@ -409,28 +409,28 @@ bool ZoneDatabase::LoadTributes() { return false; } - for (auto row = results.begin(); row != results.end(); ++row) { - uint32 id = atoul(row[0]); + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 id = atoul(row[0]); - if(tribute_list.count(id) != 1) { - Log(Logs::General, Logs::Error, "Error in LoadTributes: unknown tribute %lu in tribute_levels", (unsigned long)id); - continue; - } + if (tribute_list.count(id) != 1) { + LogError("Error in LoadTributes: unknown tribute [{}] in tribute_levels", (unsigned long) id); + continue; + } - TributeData &cur = tribute_list[id]; + TributeData &cur = tribute_list[id]; - if(cur.tier_count >= MAX_TRIBUTE_TIERS) { - Log(Logs::General, Logs::Error, "Error in LoadTributes: on tribute %lu: more tiers defined than permitted", (unsigned long)id); - continue; - } + if (cur.tier_count >= MAX_TRIBUTE_TIERS) { + LogError("Error in LoadTributes: on tribute [{}]: more tiers defined than permitted", (unsigned long) id); + continue; + } - TributeLevel_Struct &s = cur.tiers[cur.tier_count]; + TributeLevel_Struct &s = cur.tiers[cur.tier_count]; - s.level = atoul(row[1]); - s.cost = atoul(row[2]); - s.tribute_item_id = atoul(row[3]); - cur.tier_count++; - } + s.level = atoul(row[1]); + s.cost = atoul(row[2]); + s.tribute_item_id = atoul(row[3]); + cur.tier_count++; + } return true; } diff --git a/zone/water_map.cpp b/zone/water_map.cpp index 9c51e3056..d2e6daad1 100644 --- a/zone/water_map.cpp +++ b/zone/water_map.cpp @@ -14,30 +14,30 @@ WaterMap* WaterMap::LoadWaterMapfile(std::string zone_name) { std::transform(zone_name.begin(), zone_name.end(), zone_name.begin(), ::tolower); std::string file_path = Config->MapDir + "water/" + zone_name + std::string(".wtr"); - Log(Logs::General, Logs::Debug, "Attempting to load water map with path %s", file_path.c_str()); + LogDebug("Attempting to load water map with path [{}]", file_path.c_str()); FILE *f = fopen(file_path.c_str(), "rb"); if(f) { char magic[10]; uint32 version; if(fread(magic, 10, 1, f) != 1) { - Log(Logs::General, Logs::Debug, "Failed to load water map, error reading magic string in header."); + LogDebug("Failed to load water map, error reading magic string in header"); fclose(f); return nullptr; } if(strncmp(magic, "EQEMUWATER", 10)) { - Log(Logs::General, Logs::Debug, "Failed to load water map, bad magic string in header."); + LogDebug("Failed to load water map, bad magic string in header"); fclose(f); return nullptr; } if(fread(&version, sizeof(version), 1, f) != 1) { - Log(Logs::General, Logs::Debug, "Failed to load water map, error reading version."); + LogDebug("Failed to load water map, error reading version"); fclose(f); return nullptr; } - Log(Logs::General, Logs::Debug, "Attempting to V%u load water map %s.", version, file_path.c_str()); + LogDebug("Attempting to V[{}] load water map [{}]", version, file_path.c_str()); if(version == 1) { auto wm = new WaterMapV1(); if(!wm->Load(f)) { @@ -45,7 +45,7 @@ WaterMap* WaterMap::LoadWaterMapfile(std::string zone_name) { wm = nullptr; } - Log(Logs::General, Logs::Status, "Loaded Water Map V%u file %s", version, file_path.c_str()); + LogInfo("Loaded Water Map V[{}] file [{}]", version, file_path.c_str()); fclose(f); return wm; @@ -56,17 +56,17 @@ WaterMap* WaterMap::LoadWaterMapfile(std::string zone_name) { wm = nullptr; } - Log(Logs::General, Logs::Status, "Loaded Water Map V%u file %s", version, file_path.c_str()); + LogInfo("Loaded Water Map V[{}] file [{}]", version, file_path.c_str()); fclose(f); return wm; } else { - Log(Logs::General, Logs::Debug, "Failed to load water map, unsupported version V%u.", version); + LogDebug("Failed to load water map, unsupported version V[{}]", version); fclose(f); return nullptr; } } - Log(Logs::General, Logs::Debug, "Failed to load water map, could not open file for reading %s.", file_path.c_str()); + LogDebug("Failed to load water map, could not open file for reading [{}]", file_path.c_str()); return nullptr; } diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index ebbf5fda5..10e4a11f3 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/string_util.h" #include "../common/misc_functions.h" #include "../common/eqemu_logsys.h" -#include "../common/eqemu_logsys_fmt.h" #include "map.h" #include "npc.h" @@ -105,7 +104,7 @@ void NPC::StopWandering() roamer = false; CastToNPC()->SetGrid(0); StopNavigation(); - Log(Logs::Detail, Logs::Pathing, "Stop Wandering requested."); + LogPathing("Stop Wandering requested"); return; } @@ -124,16 +123,16 @@ void NPC::ResumeWandering() cur_wp = save_wp; UpdateWaypoint(cur_wp); // have him head to last destination from here } - Log(Logs::Detail, Logs::Pathing, "Resume Wandering requested. Grid %d, wp %d", GetGrid(), cur_wp); + LogPathing("Resume Wandering requested. Grid [{}], wp [{}]", GetGrid(), cur_wp); } else if (AI_walking_timer->Enabled()) { // we are at a waypoint paused normally - Log(Logs::Detail, Logs::Pathing, "Resume Wandering on timed pause. Grid %d, wp %d", GetGrid(), cur_wp); + LogPathing("Resume Wandering on timed pause. Grid [{}], wp [{}]", GetGrid(), cur_wp); AI_walking_timer->Trigger(); // disable timer to end pause now } else { - Log(Logs::General, Logs::Error, "NPC not paused - can't resume wandering: %lu", (unsigned long)GetNPCTypeID()); + LogError("NPC not paused - can't resume wandering: [{}]", (unsigned long)GetNPCTypeID()); return; } @@ -148,7 +147,7 @@ void NPC::ResumeWandering() } else { - Log(Logs::General, Logs::Error, "NPC not on grid - can't resume wandering: %lu", (unsigned long)GetNPCTypeID()); + LogError("NPC not on grid - can't resume wandering: [{}]", (unsigned long)GetNPCTypeID()); } return; } @@ -160,7 +159,7 @@ void NPC::PauseWandering(int pausetime) if (GetGrid() != 0) { moving = false; DistractedFromGrid = true; - Log(Logs::Detail, Logs::Pathing, "Paused Wandering requested. Grid %d. Resuming in %d ms (0=not until told)", GetGrid(), pausetime); + LogPathing("Paused Wandering requested. Grid [{}]. Resuming in [{}] ms (0=not until told)", GetGrid(), pausetime); StopNavigation(); if (pausetime < 1) { // negative grid number stops him dead in his tracks until ResumeWandering() SetGrid(0 - GetGrid()); @@ -170,7 +169,7 @@ void NPC::PauseWandering(int pausetime) } } else { - Log(Logs::General, Logs::Error, "NPC not on grid - can't pause wandering: %lu", (unsigned long)GetNPCTypeID()); + LogError("NPC not on grid - can't pause wandering: [{}]", (unsigned long)GetNPCTypeID()); } return; } @@ -180,19 +179,14 @@ void NPC::MoveTo(const glm::vec4 &position, bool saveguardspot) if (IsNPC() && GetGrid() != 0) { // he is on a grid if (GetGrid() < 0) { // currently stopped by a quest command SetGrid(0 - GetGrid()); // get him moving again - Log(Logs::Detail, - Logs::AI, - "MoveTo during quest wandering. Canceling quest wandering and going back to grid %d when MoveTo is done.", - GetGrid()); + LogAI("MoveTo during quest wandering. Canceling quest wandering and going back to grid [{}] when MoveTo is done", GetGrid()); } AI_walking_timer->Disable(); // disable timer in case he is paused at a wp if (cur_wp >= 0) { // we've not already done a MoveTo() save_wp = cur_wp; // save the current waypoint cur_wp = EQEmu::WaypointStatus::QuestControlGrid; } - Log(Logs::Detail, - Logs::AI, - "MoveTo %s, pausing regular grid wandering. Grid %d, save_wp %d", + LogAI("MoveTo [{}], pausing regular grid wandering. Grid [{}], save_wp [{}]", to_string(static_cast(position)).c_str(), -GetGrid(), save_wp); @@ -201,7 +195,7 @@ void NPC::MoveTo(const glm::vec4 &position, bool saveguardspot) roamer = true; save_wp = 0; cur_wp = EQEmu::WaypointStatus::QuestControlNoGrid; - Log(Logs::Detail, Logs::AI, "MoveTo %s without a grid.", to_string(static_cast(position)).c_str()); + LogAI("MoveTo [{}] without a grid", to_string(static_cast(position)).c_str()); } glm::vec3 dest(position); @@ -219,10 +213,7 @@ void NPC::MoveTo(const glm::vec4 &position, bool saveguardspot) if (m_GuardPoint.w == -1) m_GuardPoint.w = this->CalculateHeadingToTarget(position.x, position.y); - Log(Logs::Detail, - Logs::AI, - "Setting guard position to %s", - to_string(static_cast(m_GuardPoint)).c_str()); + LogAI("Setting guard position to [{}]", to_string(static_cast(m_GuardPoint)).c_str()); } cur_wp_pause = 0; @@ -235,7 +226,7 @@ void NPC::MoveTo(const glm::vec4 &position, bool saveguardspot) void NPC::UpdateWaypoint(int wp_index) { if (wp_index >= static_cast(Waypoints.size())) { - Log(Logs::Detail, Logs::AI, "Update to waypoint %d failed. Not found.", wp_index); + LogAI("Update to waypoint [{}] failed. Not found", wp_index); return; } std::vector::iterator cur; @@ -244,7 +235,7 @@ void NPC::UpdateWaypoint(int wp_index) m_CurrentWayPoint = glm::vec4(cur->x, cur->y, cur->z, cur->heading); cur_wp_pause = cur->pause; - Log(Logs::Detail, Logs::AI, "Next waypoint %d: (%.3f, %.3f, %.3f, %.3f)", wp_index, m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, m_CurrentWayPoint.w); + LogAI("Next waypoint [{}]: ({}, {}, {}, {})", wp_index, m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, m_CurrentWayPoint.w); } @@ -432,7 +423,7 @@ void NPC::SaveGuardSpot(const glm::vec4 &pos) if (m_GuardPoint.w == 0) m_GuardPoint.w = 0.0001; //hack to make IsGuarding simpler - LogF(Logs::Detail, Logs::AI, "Setting guard position to {0}", to_string(static_cast(m_GuardPoint))); + LogAI("Setting guard position to {0}", to_string(static_cast(m_GuardPoint))); } void NPC::NextGuardPosition() { @@ -566,7 +557,7 @@ void Mob::SendTo(float new_x, float new_y, float new_z) { m_Position.x = new_x; m_Position.y = new_y; m_Position.z = new_z; - Log(Logs::Detail, Logs::AI, "Sent To (%.3f, %.3f, %.3f)", new_x, new_y, new_z); + LogAI("Sent To ({}, {}, {})", new_x, new_y, new_z); if (flymode == GravityBehavior::Flying) return; @@ -582,7 +573,7 @@ void Mob::SendTo(float new_x, float new_y, float new_z) { float newz = zone->zonemap->FindBestZ(dest, nullptr); - Log(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz, m_Position.x, m_Position.y, m_Position.z); + LogAI("BestZ returned {} at {}, {}, {}", newz, m_Position.x, m_Position.y, m_Position.z); if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaSendTo)) // Sanity check. m_Position.z = newz + 1; @@ -610,7 +601,7 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) { float newz = zone->zonemap->FindBestZ(dest, nullptr); - Log(Logs::Moderate, Logs::Pathing, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz, m_Position.x, m_Position.y, m_Position.z); + LogPathing("BestZ returned [{}] at [{}], [{}], [{}]", newz, m_Position.x, m_Position.y, m_Position.z); if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaSendTo)) // Sanity check. m_Position.z = newz + 1; @@ -646,9 +637,7 @@ float Mob::GetFixedZ(const glm::vec3 &destination, int32 z_find_offset) { auto duration = timer.elapsed(); - Log(Logs::Moderate, - Logs::FixZ, - "Mob::GetFixedZ() (%s) returned %4.3f at %4.3f, %4.3f, %4.3f - Took %lf", + LogFixZ("Mob::GetFixedZ() ([{}]) returned [{}] at [{}], [{}], [{}] - Took [{}]", this->GetCleanName(), new_z, destination.x, @@ -691,11 +680,7 @@ void Mob::FixZ(int32 z_find_offset /*= 5*/, bool fix_client_z /*= false*/) { this->SendAppearanceEffect(103, 0, 0, 0, 0); } - Log(Logs::General, - Logs::FixZ, - "%s is failing to find Z %f", - this->GetCleanName(), - std::abs(m_Position.z - new_z)); + LogFixZ("[{}] is failing to find Z [{}]", this->GetCleanName(), std::abs(m_Position.z - new_z)); } } diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 2fc29e3b6..1ed7d4998 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -85,6 +85,8 @@ void WorldServer::Connect() }); m_connection->OnMessage(std::bind(&WorldServer::HandleMessage, this, std::placeholders::_1, std::placeholders::_2)); + + m_keepalive.reset(new EQ::Timer(2500, true, std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1))); } bool WorldServer::SendPacket(ServerPacket *pack) @@ -191,7 +193,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) if (pack->size != sizeof(ServerConnectInfo)) break; ServerConnectInfo* sci = (ServerConnectInfo*)pack->pBuffer; - Log(Logs::Detail, Logs::Zone_Server, "World assigned Port: %d for this zone.", sci->port); + LogInfo("World assigned Port: [{}] for this zone", sci->port); ZoneConfig::SetZonePort(sci->port); break; } @@ -423,12 +425,12 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } else { - Log(Logs::Detail, Logs::None, "[CLIENT] id=%i, playerineqstring=%i, playersinzonestring=%i. Dumping WhoAllReturnStruct:", + LogDebug("[CLIENT] id=[{}], playerineqstring=[{}], playersinzonestring=[{}]. Dumping WhoAllReturnStruct:", wars->id, wars->playerineqstring, wars->playersinzonestring); } } else - Log(Logs::General, Logs::Error, "WhoAllReturnStruct: Could not get return struct!"); + LogError("WhoAllReturnStruct: Could not get return struct!"); break; } case ServerOP_EmoteMessage: { @@ -563,7 +565,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) ServerZoneDropClient_Struct* drop = (ServerZoneDropClient_Struct*)pack->pBuffer; if (zone) { zone->RemoveAuth(drop->lsid); - + auto client = entity_list.GetClientByLSID(drop->lsid); if (client) { client->Kick("Dropped by world CLE subsystem"); @@ -747,7 +749,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) //pendingrezexp is the amount of XP on the corpse. Setting it to a value >= 0 //also serves to inform Client::OPRezzAnswer to expect a packet. client->SetPendingRezzData(srs->exp, srs->dbid, srs->rez.spellid, srs->rez.corpse_name); - Log(Logs::Detail, Logs::Spells, "OP_RezzRequest in zone %s for %s, spellid:%i", + LogSpells("OP_RezzRequest in zone [{}] for [{}], spellid:[{}]", zone->GetShortName(), client->GetName(), srs->rez.spellid); auto outapp = new EQApplicationPacket(OP_RezzRequest, sizeof(Resurrect_Struct)); @@ -762,10 +764,10 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) // to the zone that the corpse is in. Corpse* corpse = entity_list.GetCorpseByName(srs->rez.corpse_name); if (corpse && corpse->IsCorpse()) { - Log(Logs::Detail, Logs::Spells, "OP_RezzComplete received in zone %s for corpse %s", + LogSpells("OP_RezzComplete received in zone [{}] for corpse [{}]", zone->GetShortName(), srs->rez.corpse_name); - Log(Logs::Detail, Logs::Spells, "Found corpse. Marking corpse as rezzed if needed."); + LogSpells("Found corpse. Marking corpse as rezzed if needed"); // I don't know why Rezzed is not set to true in CompleteRezz(). if (!IsEffectInSpell(srs->rez.spellid, SE_SummonToCorpse)) { corpse->IsRezzed(true); @@ -794,7 +796,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } case ServerOP_SyncWorldTime: { if (zone != 0 && !zone->is_zone_time_localized) { - Log(Logs::Moderate, Logs::Zone_Server, "%s Received Message SyncWorldTime", __FUNCTION__); + LogInfo("[{}] Received Message SyncWorldTime", __FUNCTION__); eqTimeOfDay* newtime = (eqTimeOfDay*)pack->pBuffer; zone->zone_time.SetCurrentEQTimeOfDay(newtime->start_eqtime, newtime->start_realtime); @@ -816,18 +818,18 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) (eq_time.hour >= 13) ? "pm" : "am" ); - Log(Logs::General, Logs::Zone_Server, "Time Broadcast Packet: %s", time_message); + LogInfo("Time Broadcast Packet: {}", time_message); zone->SetZoneHasCurrentTime(true); } if (zone && zone->is_zone_time_localized) { - Log(Logs::General, Logs::Zone_Server, "Received request to sync time from world, but our time is localized currently"); + LogInfo("Received request to sync time from world, but our time is localized currently"); } break; } case ServerOP_RefreshCensorship: { if (!EQEmu::ProfanityManager::LoadProfanityList(&database)) - Log(Logs::General, Logs::Error, "Received request to refresh the profanity list..but, the action failed"); + LogError("Received request to refresh the profanity list..but, the action failed"); break; } case ServerOP_ChangeWID: { @@ -1436,7 +1438,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) if (NewCorpse) NewCorpse->Spawn(); else - Log(Logs::General, Logs::Error, "Unable to load player corpse id %u for zone %s.", s->player_corpse_id, zone->GetShortName()); + LogError("Unable to load player corpse id [{}] for zone [{}]", s->player_corpse_id, zone->GetShortName()); break; } @@ -1942,34 +1944,34 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) case ServerOP_ChangeSharedMem: { std::string hotfix_name = std::string((char*)pack->pBuffer); - Log(Logs::General, Logs::Zone_Server, "Loading items"); + LogInfo("Loading items"); if (!database.LoadItems(hotfix_name)) { - Log(Logs::General, Logs::Error, "Loading items FAILED!"); + LogError("Loading items failed!"); } - Log(Logs::General, Logs::Zone_Server, "Loading npc faction lists"); + LogInfo("Loading npc faction lists"); if (!database.LoadNPCFactionLists(hotfix_name)) { - Log(Logs::General, Logs::Error, "Loading npcs faction lists FAILED!"); + LogError("Loading npcs faction lists failed!"); } - Log(Logs::General, Logs::Zone_Server, "Loading loot tables"); + LogInfo("Loading loot tables"); if (!database.LoadLoot(hotfix_name)) { - Log(Logs::General, Logs::Error, "Loading loot FAILED!"); + LogError("Loading loot failed!"); } - Log(Logs::General, Logs::Zone_Server, "Loading skill caps"); + LogInfo("Loading skill caps"); if (!database.LoadSkillCaps(std::string(hotfix_name))) { - Log(Logs::General, Logs::Error, "Loading skill caps FAILED!"); + LogError("Loading skill caps failed!"); } - Log(Logs::General, Logs::Zone_Server, "Loading spells"); + LogInfo("Loading spells"); if (!database.LoadSpells(hotfix_name, &SPDAT_RECORDS, &spells)) { - Log(Logs::General, Logs::Error, "Loading spells FAILED!"); + LogError("Loading spells failed!"); } - Log(Logs::General, Logs::Zone_Server, "Loading base data"); + LogInfo("Loading base data"); if (!database.LoadBaseData(hotfix_name)) { - Log(Logs::General, Logs::Error, "Loading base data FAILED!"); + LogError("Loading base data failed!"); } break; } @@ -2103,7 +2105,7 @@ bool WorldServer::SendVoiceMacro(Client* From, uint32 Type, char* Target, uint32 bool WorldServer::RezzPlayer(EQApplicationPacket* rpack, uint32 rezzexp, uint32 dbid, uint16 opcode) { - Log(Logs::Detail, Logs::Spells, "WorldServer::RezzPlayer rezzexp is %i (0 is normal for RezzComplete", rezzexp); + LogSpells("WorldServer::RezzPlayer rezzexp is [{}] (0 is normal for RezzComplete", rezzexp); auto pack = new ServerPacket(ServerOP_RezzPlayer, sizeof(RezzPlayer_Struct)); RezzPlayer_Struct* sem = (RezzPlayer_Struct*)pack->pBuffer; sem->rezzopcode = opcode; @@ -2112,9 +2114,9 @@ bool WorldServer::RezzPlayer(EQApplicationPacket* rpack, uint32 rezzexp, uint32 sem->dbid = dbid; bool ret = SendPacket(pack); if (ret) - Log(Logs::Detail, Logs::Spells, "Sending player rezz packet to world spellid:%i", sem->rez.spellid); + LogSpells("Sending player rezz packet to world spellid:[{}]", sem->rez.spellid); else - Log(Logs::Detail, Logs::Spells, "NOT Sending player rezz packet to world"); + LogSpells("NOT Sending player rezz packet to world"); safe_delete(pack); return ret; @@ -2190,7 +2192,7 @@ uint32 WorldServer::NextGroupID() { if (cur_groupid >= last_groupid) { //this is an error... This means that 50 groups were created before //1 packet could make the zone->world->zone trip... so let it error. - Log(Logs::General, Logs::Error, "Ran out of group IDs before the server sent us more."); + LogError("Ran out of group IDs before the server sent us more"); return(0); } if (cur_groupid > (last_groupid - /*50*/995)) { @@ -2352,3 +2354,9 @@ void WorldServer::RequestTellQueue(const char *who) safe_delete(pack); return; } + +void WorldServer::OnKeepAlive(EQ::Timer *t) +{ + ServerPacket pack(ServerOP_KeepAlive, 0); + SendPacket(&pack); +} diff --git a/zone/worldserver.h b/zone/worldserver.h index 4c1b9ab12..1eee0b948 100644 --- a/zone/worldserver.h +++ b/zone/worldserver.h @@ -72,7 +72,10 @@ private: uint32 cur_groupid; uint32 last_groupid; + void OnKeepAlive(EQ::Timer *t); + std::unique_ptr m_connection; + std::unique_ptr m_keepalive; }; #endif diff --git a/zone/zone.cpp b/zone/zone.cpp index 8ca78735f..1bb8b8c36 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -92,7 +92,7 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { return false; } - Log(Logs::General, Logs::Status, "Booting %s (%d:%d)", zonename, iZoneID, iInstanceID); + LogInfo("Booting [{}] ([{}]:[{}])", zonename, iZoneID, iInstanceID); numclients = 0; zone = new Zone(iZoneID, iInstanceID, zonename); @@ -117,13 +117,13 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { log_levels[i]=0; //set to zero on a bogue char } zone->loglevelvar = log_levels[0]; - Log(Logs::General, Logs::Status, "General logging level: %i", zone->loglevelvar); + LogInfo("General logging level: [{}]", zone->loglevelvar); zone->merchantvar = log_levels[1]; - Log(Logs::General, Logs::Status, "Merchant logging level: %i", zone->merchantvar); + LogInfo("Merchant logging level: [{}]", zone->merchantvar); zone->tradevar = log_levels[2]; - Log(Logs::General, Logs::Status, "Trade logging level: %i", zone->tradevar); + LogInfo("Trade logging level: [{}]", zone->tradevar); zone->lootvar = log_levels[3]; - Log(Logs::General, Logs::Status, "Loot logging level: %i", zone->lootvar); + LogInfo("Loot logging level: [{}]", zone->lootvar); } else { zone->loglevelvar = uint8(tmp_i); //continue supporting only command logging (for now) @@ -144,8 +144,8 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { delete pack; } - Log(Logs::General, Logs::Normal, "---- Zone server %s, listening on port:%i ----", zonename, ZoneConfig::get()->ZonePort); - Log(Logs::General, Logs::Status, "Zone Bootup: %s (%i: %i)", zonename, iZoneID, iInstanceID); + LogInfo("---- Zone server [{}], listening on port:[{}] ----", zonename, ZoneConfig::get()->ZonePort); + LogInfo("Zone Bootup: [{}] ([{}]: [{}])", zonename, iZoneID, iInstanceID); parse->Init(); UpdateWindowTitle(); zone->GetTimeSync(); @@ -176,12 +176,12 @@ bool Zone::LoadZoneObjects() zoneid, instanceversion); auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error Loading Objects from DB: %s", + LogError("Error Loading Objects from DB: [{}]", results.ErrorMessage().c_str()); return false; } - Log(Logs::General, Logs::Status, "Loading Objects from DB..."); + LogInfo("Loading Objects from DB"); for (auto row = results.begin(); row != results.end(); ++row) { if (atoi(row[9]) == 0) { // Type == 0 - Static Object @@ -300,7 +300,7 @@ bool Zone::LoadGroundSpawns() { memset(&groundspawn, 0, sizeof(groundspawn)); int gsindex=0; - Log(Logs::General, Logs::Status, "Loading Ground Spawns from DB..."); + LogInfo("Loading Ground Spawns from DB"); database.LoadGroundSpawns(zoneid, GetInstanceVersion(), &groundspawn); uint32 ix=0; char* name = nullptr; @@ -419,7 +419,7 @@ uint32 Zone::GetTempMerchantQuantity(uint32 NPCID, uint32 Slot) { } void Zone::LoadTempMerchantData() { - Log(Logs::General, Logs::Status, "Loading Temporary Merchant Lists..."); + LogInfo("Loading Temporary Merchant Lists"); std::string query = StringFormat( "SELECT " "DISTINCT ml.npcid, " @@ -489,7 +489,7 @@ void Zone::LoadNewMerchantData(uint32 merchantid) { } void Zone::GetMerchantDataForZoneLoad() { - Log(Logs::General, Logs::Status, "Loading Merchant Lists..."); + LogInfo("Loading Merchant Lists"); std::string query = StringFormat( "SELECT " "DISTINCT ml.merchantid, " @@ -512,7 +512,7 @@ void Zone::GetMerchantDataForZoneLoad() { std::map >::iterator cur; uint32 npcid = 0; if (results.RowCount() == 0) { - Log(Logs::General, Logs::None, "No Merchant Data found for %s.", GetShortName()); + LogDebug("No Merchant Data found for [{}]", GetShortName()); return; } for (auto row = results.begin(); row != results.end(); ++row) { @@ -560,18 +560,18 @@ void Zone::LoadMercTemplates(){ merc_templates.clear(); std::string query = "SELECT `class_id`, `proficiency_id`, `stance_id`, `isdefault` FROM " "`merc_stance_entries` ORDER BY `class_id`, `proficiency_id`, `stance_id`"; - auto results = database.QueryDatabase(query); + auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error in ZoneDatabase::LoadMercTemplates()"); + LogError("Error in ZoneDatabase::LoadMercTemplates()"); } else { for (auto row = results.begin(); row != results.end(); ++row) { MercStanceInfo tempMercStanceInfo; - tempMercStanceInfo.ClassID = atoi(row[0]); + tempMercStanceInfo.ClassID = atoi(row[0]); tempMercStanceInfo.ProficiencyID = atoi(row[1]); - tempMercStanceInfo.StanceID = atoi(row[2]); - tempMercStanceInfo.IsDefault = atoi(row[3]); + tempMercStanceInfo.StanceID = atoi(row[2]); + tempMercStanceInfo.IsDefault = atoi(row[3]); merc_stances.push_back(tempMercStanceInfo); } @@ -583,11 +583,11 @@ void Zone::LoadMercTemplates(){ "AS CostFormula, MTem.clientversion, MTem.merc_npc_type_id " "FROM merc_types MTyp, merc_templates MTem, merc_subtypes MS " "WHERE MTem.merc_type_id = MTyp.merc_type_id AND MTem.merc_subtype_id = MS.merc_subtype_id " - "ORDER BY MTyp.race_id, MS.class_id, MTyp.proficiency_id;"; - results = database.QueryDatabase(query); - if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error in ZoneDatabase::LoadMercTemplates()"); - return; + "ORDER BY MTyp.race_id, MS.class_id, MTyp.proficiency_id;"; + results = database.QueryDatabase(query); + if (!results.Success()) { + LogError("Error in ZoneDatabase::LoadMercTemplates()"); + return; } for (auto row = results.begin(); row != results.end(); ++row) { @@ -630,7 +630,7 @@ void Zone::LoadLevelEXPMods(){ const std::string query = "SELECT level, exp_mod, aa_exp_mod FROM level_exp_mods"; auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error in ZoneDatabase::LoadEXPLevelMods()"); + LogError("Error in ZoneDatabase::LoadEXPLevelMods()"); return; } @@ -652,11 +652,11 @@ void Zone::LoadMercSpells(){ "FROM merc_spell_lists msl, merc_spell_list_entries msle " "WHERE msle.merc_spell_list_id = msl.merc_spell_list_id " "ORDER BY msl.class_id, msl.proficiency_id, msle.spell_type, msle.minlevel, msle.slot;"; - auto results = database.QueryDatabase(query); - if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error in Zone::LoadMercSpells()"); - return; - } + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogError("Error in Zone::LoadMercSpells()"); + return; + } for (auto row = results.begin(); row != results.end(); ++row) { uint32 classid; @@ -683,42 +683,44 @@ bool Zone::IsLoaded() { return is_zone_loaded; } -void Zone::Shutdown(bool quite) +void Zone::Shutdown(bool quiet) { - if (!is_zone_loaded) + if (!is_zone_loaded) { return; + } entity_list.StopMobAI(); - std::map::iterator itr; - while(!zone->npctable.empty()) { - itr=zone->npctable.begin(); + std::map::iterator itr; + while (!zone->npctable.empty()) { + itr = zone->npctable.begin(); delete itr->second; zone->npctable.erase(itr); } - while(!zone->merctable.empty()) { - itr=zone->merctable.begin(); + while (!zone->merctable.empty()) { + itr = zone->merctable.begin(); delete itr->second; zone->merctable.erase(itr); } zone->adventure_entry_list_flavor.clear(); - std::map::iterator itr4; - while(!zone->ldon_trap_list.empty()) - { + std::map::iterator itr4; + while (!zone->ldon_trap_list.empty()) { itr4 = zone->ldon_trap_list.begin(); delete itr4->second; zone->ldon_trap_list.erase(itr4); } zone->ldon_trap_entry_list.clear(); - Log(Logs::General, Logs::Status, "Zone Shutdown: %s (%i)", zone->GetShortName(), zone->GetZoneID()); + LogInfo("Zone Shutdown: [{}] ([{}])", zone->GetShortName(), zone->GetZoneID()); petition_list.ClearPetitions(); zone->SetZoneHasCurrentTime(false); - if (!quite) - Log(Logs::General, Logs::Normal, "Zone shutdown: going to sleep"); + if (!quiet) { + LogInfo("Zone Shutdown: Going to sleep"); + } + is_zone_loaded = false; zone->ResetAuth(); @@ -728,23 +730,28 @@ void Zone::Shutdown(bool quite) UpdateWindowTitle(); LogSys.CloseFileLogs(); + + if (RuleB(Zone, KillProcessOnDynamicShutdown)) { + LogInfo("[KillProcessOnDynamicShutdown] Shutting down"); + std::exit(EXIT_SUCCESS); + } } void Zone::LoadZoneDoors(const char* zone, int16 version) { - Log(Logs::General, Logs::Status, "Loading doors for %s ...", zone); + LogInfo("Loading doors for [{}] ", zone); uint32 maxid; int32 count = database.GetDoorsCount(&maxid, zone, version); if(count < 1) { - Log(Logs::General, Logs::Status, "... No doors loaded."); + LogInfo("No doors loaded"); return; } auto dlist = new Door[count]; if(!database.LoadDoors(count, dlist, zone, version)) { - Log(Logs::General, Logs::Error, "... Failed to load doors."); + LogError("Failed to load doors"); delete[] dlist; return; } @@ -805,14 +812,14 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) database.GetZoneLongName(short_name, &long_name, file_name, &m_SafePoint.x, &m_SafePoint.y, &m_SafePoint.z, &pgraveyard_id, &pMaxClients); if(graveyard_id() > 0) { - Log(Logs::General, Logs::None, "Graveyard ID is %i.", graveyard_id()); + LogDebug("Graveyard ID is [{}]", graveyard_id()); bool GraveYardLoaded = database.GetZoneGraveyard(graveyard_id(), &pgraveyard_zoneid, &m_Graveyard.x, &m_Graveyard.y, &m_Graveyard.z, &m_Graveyard.w); if (GraveYardLoaded) { - Log(Logs::General, Logs::None, "Loaded a graveyard for zone %s: graveyard zoneid is %u at %s.", short_name, graveyard_zoneid(), to_string(m_Graveyard).c_str()); + LogDebug("Loaded a graveyard for zone [{}]: graveyard zoneid is [{}] at [{}]", short_name, graveyard_zoneid(), to_string(m_Graveyard).c_str()); } else { - Log(Logs::General, Logs::Error, "Unable to load the graveyard id %i for zone %s.", graveyard_id(), short_name); + LogError("Unable to load the graveyard id [{}] for zone [{}]", graveyard_id(), short_name); } } if (long_name == 0) { @@ -821,12 +828,12 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) autoshutdown_timer.Start(AUTHENTICATION_TIMEOUT * 1000, false); Weather_Timer = new Timer(60000); Weather_Timer->Start(); - Log(Logs::General, Logs::None, "The next weather check for zone: %s will be in %i seconds.", short_name, Weather_Timer->GetRemainingTime()/1000); - zone_weather = 0; - weather_intensity = 0; - blocked_spells = nullptr; - totalBS = 0; - zone_has_current_time = false; + LogDebug("The next weather check for zone: [{}] will be in [{}] seconds", short_name, Weather_Timer->GetRemainingTime()/1000); + zone_weather = 0; + weather_intensity = 0; + blocked_spells = nullptr; + zone_total_blocked_spells = 0; + zone_has_current_time = false; Instance_Shutdown_Timer = nullptr; bool is_perma = false; @@ -915,64 +922,64 @@ bool Zone::Init(bool iStaticZone) { zone->watermap = WaterMap::LoadWaterMapfile(zone->map_name); zone->pathing = IPathfinder::Load(zone->map_name); - Log(Logs::General, Logs::Status, "Loading spawn conditions..."); + LogInfo("Loading spawn conditions"); if(!spawn_conditions.LoadSpawnConditions(short_name, instanceid)) { - Log(Logs::General, Logs::Error, "Loading spawn conditions failed, continuing without them."); + LogError("Loading spawn conditions failed, continuing without them"); } - Log(Logs::General, Logs::Status, "Loading static zone points..."); + LogInfo("Loading static zone points"); if (!database.LoadStaticZonePoints(&zone_point_list, short_name, GetInstanceVersion())) { - Log(Logs::General, Logs::Error, "Loading static zone points failed."); + LogError("Loading static zone points failed"); return false; } - Log(Logs::General, Logs::Status, "Loading spawn groups..."); + LogInfo("Loading spawn groups"); if (!database.LoadSpawnGroups(short_name, GetInstanceVersion(), &spawn_group_list)) { - Log(Logs::General, Logs::Error, "Loading spawn groups failed."); + LogError("Loading spawn groups failed"); return false; } - Log(Logs::General, Logs::Status, "Loading spawn2 points..."); + LogInfo("Loading spawn2 points"); if (!database.PopulateZoneSpawnList(zoneid, spawn2_list, GetInstanceVersion())) { - Log(Logs::General, Logs::Error, "Loading spawn2 points failed."); + LogError("Loading spawn2 points failed"); return false; } - Log(Logs::General, Logs::Status, "Loading player corpses..."); + LogInfo("Loading player corpses"); if (!database.LoadCharacterCorpses(zoneid, instanceid)) { - Log(Logs::General, Logs::Error, "Loading player corpses failed."); + LogError("Loading player corpses failed"); return false; } - Log(Logs::General, Logs::Status, "Loading traps..."); + LogInfo("Loading traps"); if (!database.LoadTraps(short_name, GetInstanceVersion())) { - Log(Logs::General, Logs::Error, "Loading traps failed."); + LogError("Loading traps failed"); return false; } - Log(Logs::General, Logs::Status, "Loading adventure flavor text..."); + LogInfo("Loading adventure flavor text"); LoadAdventureFlavor(); - Log(Logs::General, Logs::Status, "Loading ground spawns..."); + LogInfo("Loading ground spawns"); if (!LoadGroundSpawns()) { - Log(Logs::General, Logs::Error, "Loading ground spawns failed. continuing."); + LogError("Loading ground spawns failed. continuing"); } - Log(Logs::General, Logs::Status, "Loading World Objects from DB..."); + LogInfo("Loading World Objects from DB"); if (!LoadZoneObjects()) { - Log(Logs::General, Logs::Error, "Loading World Objects failed. continuing."); + LogError("Loading World Objects failed. continuing"); } - Log(Logs::General, Logs::Status, "Flushing old respawn timers..."); + LogInfo("Flushing old respawn timers"); database.QueryDatabase("DELETE FROM `respawn_times` WHERE (`start` + `duration`) < UNIX_TIMESTAMP(NOW())"); //load up the zone's doors (prints inside) zone->LoadZoneDoors(zone->GetShortName(), zone->GetInstanceVersion()); - zone->LoadBlockedSpells(zone->GetZoneID()); + zone->LoadZoneBlockedSpells(zone->GetZoneID()); //clear trader items if we are loading the bazaar if(strncasecmp(short_name,"bazaar",6)==0) { @@ -1008,10 +1015,10 @@ bool Zone::Init(bool iStaticZone) { petition_list.ClearPetitions(); petition_list.ReadDatabase(); - Log(Logs::General, Logs::Status, "Loading timezone data..."); + LogInfo("Loading timezone data"); zone->zone_time.setEQTimeZone(database.GetZoneTZ(zoneid, GetInstanceVersion())); - Log(Logs::General, Logs::Status, "Init Finished: ZoneID = %d, Time Offset = %d", zoneid, zone->zone_time.getEQTimeZone()); + LogInfo("Init Finished: ZoneID = [{}], Time Offset = [{}]", zoneid, zone->zone_time.getEQTimeZone()); LoadTickItems(); @@ -1022,32 +1029,32 @@ bool Zone::Init(bool iStaticZone) { } void Zone::ReloadStaticData() { - Log(Logs::General, Logs::Status, "Reloading Zone Static Data..."); + LogInfo("Reloading Zone Static Data"); - Log(Logs::General, Logs::Status, "Reloading static zone points..."); + LogInfo("Reloading static zone points"); zone_point_list.Clear(); if (!database.LoadStaticZonePoints(&zone_point_list, GetShortName(), GetInstanceVersion())) { - Log(Logs::General, Logs::Error, "Loading static zone points failed."); + LogError("Loading static zone points failed"); } - Log(Logs::General, Logs::Status, "Reloading traps..."); + LogInfo("Reloading traps"); entity_list.RemoveAllTraps(); if (!database.LoadTraps(GetShortName(), GetInstanceVersion())) { - Log(Logs::General, Logs::Error, "Reloading traps failed."); + LogError("Reloading traps failed"); } - Log(Logs::General, Logs::Status, "Reloading ground spawns..."); + LogInfo("Reloading ground spawns"); if (!LoadGroundSpawns()) { - Log(Logs::General, Logs::Error, "Reloading ground spawns failed. continuing."); + LogError("Reloading ground spawns failed. continuing"); } entity_list.RemoveAllObjects(); - Log(Logs::General, Logs::Status, "Reloading World Objects from DB..."); + LogInfo("Reloading World Objects from DB"); if (!LoadZoneObjects()) { - Log(Logs::General, Logs::Error, "Reloading World Objects failed. continuing."); + LogError("Reloading World Objects failed. continuing"); } entity_list.RemoveAllDoors(); @@ -1063,7 +1070,7 @@ void Zone::ReloadStaticData() { if (!LoadZoneCFG(zone->GetShortName(), zone->GetInstanceVersion())) // try loading the zone name... LoadZoneCFG(zone->GetFileName(), zone->GetInstanceVersion()); // if that fails, try the file name, then load defaults - Log(Logs::General, Logs::Status, "Zone Static Data Reloaded."); + LogInfo("Zone Static Data Reloaded"); } bool Zone::LoadZoneCFG(const char* filename, uint16 instance_id) @@ -1082,7 +1089,7 @@ bool Zone::LoadZoneCFG(const char* filename, uint16 instance_id) if(!database.GetZoneCFG(database.GetZoneID(filename), 0, &newzone_data, can_bind, can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, allow_mercs, max_movement_update_range, zone_type, default_ruleset, &map_name)) { - Log(Logs::General, Logs::Error, "Error loading the Zone Config."); + LogError("Error loading the Zone Config"); return false; } } @@ -1093,7 +1100,7 @@ bool Zone::LoadZoneCFG(const char* filename, uint16 instance_id) strcpy(newzone_data.zone_long_name, GetLongName()); strcpy(newzone_data.zone_short_name2, GetShortName()); - Log(Logs::General, Logs::Status, "Successfully loaded Zone Config."); + LogInfo("Successfully loaded Zone Config"); return true; } @@ -1203,7 +1210,7 @@ bool Zone::Process() { EQEmu::InventoryProfile::CleanDirty(); - Log(Logs::Detail, Logs::Spawns, "Running Zone::Process -> Spawn2::Process"); + LogSpawns("Running Zone::Process -> Spawn2::Process"); iterator.Reset(); while (iterator.MoreElements()) { @@ -1423,11 +1430,11 @@ void Zone::ChangeWeather() weathertimer = weatherTimerRule*1000; Weather_Timer->Start(weathertimer); } - Log(Logs::General, Logs::None, "The next weather check for zone: %s will be in %i seconds.", zone->GetShortName(), Weather_Timer->GetRemainingTime()/1000); + LogDebug("The next weather check for zone: [{}] will be in [{}] seconds", zone->GetShortName(), Weather_Timer->GetRemainingTime()/1000); } else { - Log(Logs::General, Logs::None, "The weather for zone: %s has changed. Old weather was = %i. New weather is = %i The next check will be in %i seconds. Rain chance: %i, Rain duration: %i, Snow chance %i, Snow duration: %i", zone->GetShortName(), tmpOldWeather, zone_weather,Weather_Timer->GetRemainingTime()/1000,rainchance,rainduration,snowchance,snowduration); + LogDebug("The weather for zone: [{}] has changed. Old weather was = [{}]. New weather is = [{}] The next check will be in [{}] seconds. Rain chance: [{}], Rain duration: [{}], Snow chance [{}], Snow duration: [{}]", zone->GetShortName(), tmpOldWeather, zone_weather,Weather_Timer->GetRemainingTime()/1000,rainchance,rainduration,snowchance,snowduration); this->weatherSend(); if (zone->weather_intensity == 0) { @@ -1458,16 +1465,12 @@ void Zone::StartShutdownTimer(uint32 set_time) { if (set_time == (RuleI(Zone, AutoShutdownDelay))) { set_time = static_cast(database.getZoneShutDownDelay(GetZoneID(), GetInstanceVersion())); } + autoshutdown_timer.SetTimer(set_time); - Log(Logs::General, Logs::Zone_Server, "Zone::StartShutdownTimer set to %u", set_time); + LogDebug("Zone::StartShutdownTimer set to {}", set_time); } - Log(Logs::Detail, Logs::Zone_Server, - "Zone::StartShutdownTimer trigger - set_time: %u remaining_time: %u diff: %i", - set_time, - autoshutdown_timer.GetRemainingTime(), - (set_time - autoshutdown_timer.GetRemainingTime()) - ); + LogDebug("Zone::StartShutdownTimer trigger - set_time: [{}] remaining_time: [{}] diff: [{}]", set_time, autoshutdown_timer.GetRemainingTime(), (set_time - autoshutdown_timer.GetRemainingTime())); } bool Zone::Depop(bool StartSpawnTimer) { @@ -1528,7 +1531,7 @@ void Zone::RepopClose(const glm::vec4& client_position, uint32 repop_distance) quest_manager.ClearAllTimers(); if (!database.PopulateZoneSpawnListClose(zoneid, spawn2_list, GetInstanceVersion(), client_position, repop_distance)) - Log(Logs::General, Logs::None, "Error in Zone::Repop: database.PopulateZoneSpawnList failed"); + LogDebug("Error in Zone::Repop: database.PopulateZoneSpawnList failed"); initgrids_timer.Start(); @@ -1556,7 +1559,7 @@ void Zone::Repop(uint32 delay) quest_manager.ClearAllTimers(); if (!database.PopulateZoneSpawnList(zoneid, spawn2_list, GetInstanceVersion(), delay)) - Log(Logs::General, Logs::None, "Error in Zone::Repop: database.PopulateZoneSpawnList failed"); + LogDebug("Error in Zone::Repop: database.PopulateZoneSpawnList failed"); initgrids_timer.Start(); @@ -1606,7 +1609,7 @@ void Zone::SetTime(uint8 hour, uint8 minute, bool update_world /*= true*/) /* By Default we update worlds time, but we can optionally no update world which updates the rest of the zone servers */ if (update_world){ - Log(Logs::General, Logs::Zone_Server, "Setting master time on world server to: %d:%d (%d)\n", hour, minute, (int)eq_time_of_day->start_realtime); + LogInfo("Setting master time on world server to: {}:{} ({})\n", hour, minute, (int)eq_time_of_day->start_realtime); worldserver.SendPacket(pack); /* Set Time Localization Flag */ @@ -1615,7 +1618,7 @@ void Zone::SetTime(uint8 hour, uint8 minute, bool update_world /*= true*/) /* When we don't update world, we are localizing ourselves, we become disjointed from normal syncs and set time locally */ else{ - Log(Logs::General, Logs::Zone_Server, "Setting zone localized time..."); + LogInfo("Setting zone localized time..."); zone->zone_time.SetCurrentEQTimeOfDay(eq_time_of_day->start_eqtime, eq_time_of_day->start_realtime); auto outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); @@ -1668,8 +1671,8 @@ ZonePoint* Zone::GetClosestZonePoint(const glm::vec3& location, uint32 to, Clien if ((zone->HasWaterMap() && !zone->watermap->InZoneLine(glm::vec3(client->GetPosition()))) || (!zone->HasWaterMap() && closest_dist > 400.0f && closest_dist < max_distance2)) { //TODO cheat detection - Log(Logs::General, Logs::Status, "WARNING: Closest zone point for zone id %d is %f, you might need to update your zone_points table if you dont arrive at the right spot.", to, closest_dist); - Log(Logs::General, Logs::Status, ". %s", to_string(location).c_str()); + LogInfo("WARNING: Closest zone point for zone id [{}] is [{}], you might need to update your zone_points table if you dont arrive at the right spot", to, closest_dist); + LogInfo(". [{}]", to_string(location).c_str()); } if(closest_dist > max_distance2) @@ -1880,17 +1883,21 @@ bool ZoneDatabase::GetDecayTimes(npcDecayTimes_Struct *npcCorpseDecayTimes) return true; } -void Zone::weatherSend(Client* client) +void Zone::weatherSend(Client *client) { auto outapp = new EQApplicationPacket(OP_Weather, 8); - if(zone_weather>0) - outapp->pBuffer[0] = zone_weather-1; - if(zone_weather>0) + if (zone_weather > 0) { + outapp->pBuffer[0] = zone_weather - 1; + } + if (zone_weather > 0) { outapp->pBuffer[4] = zone->weather_intensity; - if (client) + } + if (client) { client->QueuePacket(outapp); - else + } + else { entity_list.QueueClients(0, outapp); + } safe_delete(outapp); } @@ -1908,16 +1915,14 @@ void Zone::SetGraveyard(uint32 zoneid, const glm::vec4& graveyardPosition) { m_Graveyard = graveyardPosition; } -void Zone::LoadBlockedSpells(uint32 zoneid) +void Zone::LoadZoneBlockedSpells(uint32 zone_id) { - if(!blocked_spells) - { - totalBS = database.GetBlockedSpellsCount(zoneid); - if(totalBS > 0){ - blocked_spells = new ZoneSpellsBlocked[totalBS]; - if(!database.LoadBlockedSpells(totalBS, blocked_spells, zoneid)) - { - Log(Logs::General, Logs::Error, "... Failed to load blocked spells."); + if (!blocked_spells) { + zone_total_blocked_spells = database.GetBlockedSpellsCount(zone_id); + if (zone_total_blocked_spells > 0) { + blocked_spells = new ZoneSpellsBlocked[zone_total_blocked_spells]; + if (!database.LoadBlockedSpells(zone_total_blocked_spells, blocked_spells, zone_id)) { + LogError(" Failed to load blocked spells"); ClearBlockedSpells(); } } @@ -1926,102 +1931,89 @@ void Zone::LoadBlockedSpells(uint32 zoneid) void Zone::ClearBlockedSpells() { - if(blocked_spells){ + if (blocked_spells) { safe_delete_array(blocked_spells); - totalBS = 0; + zone_total_blocked_spells = 0; } } -bool Zone::IsSpellBlocked(uint32 spell_id, const glm::vec3& location) +bool Zone::IsSpellBlocked(uint32 spell_id, const glm::vec3 &location) { - if (blocked_spells) - { + if (blocked_spells) { bool exception = false; bool block_all = false; - for (int x = 0; x < totalBS; x++) - { - if (blocked_spells[x].spellid == spell_id) - { + + for (int x = 0; x < GetZoneTotalBlockedSpells(); x++) { + if (blocked_spells[x].spellid == spell_id) { exception = true; } - if (blocked_spells[x].spellid == 0) - { + if (blocked_spells[x].spellid == 0) { block_all = true; } } - for (int x = 0; x < totalBS; x++) - { - // If spellid is 0, block all spells in the zone - if (block_all) - { - // If the same zone has entries other than spellid 0, they act as exceptions and are allowed - if (exception) - { - return false; - } - else - { - return true; - } - } - else - { - if (spell_id != blocked_spells[x].spellid) - { - continue; - } + // If all spells are blocked and this is an exception, it is not blocked + if (block_all && exception) { + return false; + } - switch (blocked_spells[x].type) - { - case 1: - { + for (int x = 0; x < GetZoneTotalBlockedSpells(); x++) { + // Spellid of 0 matches all spells + if (0 != blocked_spells[x].spellid && spell_id != blocked_spells[x].spellid) { + continue; + } + + switch (blocked_spells[x].type) { + case ZoneBlockedSpellTypes::ZoneWide: { + return true; + break; + } + case ZoneBlockedSpellTypes::Region: { + if (IsWithinAxisAlignedBox( + location, + blocked_spells[x].m_Location - blocked_spells[x].m_Difference, + blocked_spells[x].m_Location + blocked_spells[x].m_Difference + )) { return true; - break; - } - case 2: - { - if (IsWithinAxisAlignedBox(location, blocked_spells[x].m_Location - blocked_spells[x].m_Difference, blocked_spells[x].m_Location + blocked_spells[x].m_Difference)) - return true; - break; - } - default: - { - continue; - break; } + break; + } + default: { + continue; + break; } } } } + return false; } -const char* Zone::GetSpellBlockedMessage(uint32 spell_id, const glm::vec3& location) +const char *Zone::GetSpellBlockedMessage(uint32 spell_id, const glm::vec3 &location) { - if(blocked_spells) - { - for(int x = 0; x < totalBS; x++) - { - if(spell_id != blocked_spells[x].spellid && blocked_spells[x].spellid != 0) + if (blocked_spells) { + for (int x = 0; x < GetZoneTotalBlockedSpells(); x++) { + if (spell_id != blocked_spells[x].spellid && blocked_spells[x].spellid != 0) { continue; + } - switch(blocked_spells[x].type) - { - case 1: - { + switch (blocked_spells[x].type) { + case ZoneBlockedSpellTypes::ZoneWide: { return blocked_spells[x].message; break; } - case 2: - { - if(IsWithinAxisAlignedBox(location, blocked_spells[x].m_Location - blocked_spells[x].m_Difference, blocked_spells[x].m_Location + blocked_spells[x].m_Difference)) + case ZoneBlockedSpellTypes::Region: { + if (IsWithinAxisAlignedBox( + location, + blocked_spells[x].m_Location - blocked_spells[x].m_Difference, + blocked_spells[x].m_Location + blocked_spells[x].m_Difference + )) { return blocked_spells[x].message; + } break; } - default: - { + default: { continue; break; } diff --git a/zone/zone.h b/zone/zone.h index 176fe0cb3..4ed017627 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -84,7 +84,7 @@ class MobMovementManager; class Zone { public: static bool Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone = false); - static void Shutdown(bool quite = false); + static void Shutdown(bool quiet = false); Zone(uint32 in_zoneid, uint32 in_instanceid, const char *in_short_name); ~Zone(); @@ -162,7 +162,7 @@ public: inline void SetZoneHasCurrentTime(bool time) { zone_has_current_time = time; } inline void ShowNPCGlobalLoot(Client *to, NPC *who) { m_global_loot.ShowNPCGlobalLoot(to, who); } inline void ShowZoneGlobalLoot(Client *to) { m_global_loot.ShowZoneGlobalLoot(to); } - int GetTotalBlockedSpells() { return totalBS; } + int GetZoneTotalBlockedSpells() { return zone_total_blocked_spells; } int SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charges, bool sold = false); int32 MobsAggroCount() { return aggroedmobs; } @@ -234,7 +234,7 @@ public: void LoadAdventureFlavor(); void LoadAlternateAdvancement(); void LoadAlternateCurrencies(); - void LoadBlockedSpells(uint32 zoneid); + void LoadZoneBlockedSpells(uint32 zone_id); void LoadLDoNTrapEntries(); void LoadLDoNTraps(); void LoadLevelEXPMods(); @@ -348,7 +348,7 @@ private: glm::vec3 m_SafePoint; glm::vec4 m_Graveyard; int default_ruleset; - int totalBS; + int zone_total_blocked_spells; int npc_position_update_distance; int32 aggroedmobs; uint8 zone_type; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 91f456768..dc2e29cad 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -15,6 +15,7 @@ #include #include +#include extern Zone* zone; @@ -204,7 +205,7 @@ bool ZoneDatabase::GetZoneCFG( //not in the DB yet: zone_data->gravity = atof(row[56]); - Log(Logs::General, Logs::Debug, "Zone Gravity is %f", zone_data->gravity); + LogDebug("Zone Gravity is [{}]", zone_data->gravity); allow_mercs = true; zone_data->FastRegenHP = atoi(row[57]); @@ -681,17 +682,20 @@ void ZoneDatabase::GetEventLogs(const char* name,char* target,uint32 account_id, void ZoneDatabase::LoadWorldContainer(uint32 parentid, EQEmu::ItemInstance* container) { if (!container) { - Log(Logs::General, Logs::Error, "Programming error: LoadWorldContainer passed nullptr pointer"); + LogError("Programming error: LoadWorldContainer passed nullptr pointer"); return; } - std::string query = StringFormat("SELECT bagidx, itemid, charges, augslot1, augslot2, augslot3, augslot4, augslot5, augslot6 " - "FROM object_contents WHERE parentid = %i", parentid); - auto results = QueryDatabase(query); - if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error in DB::LoadWorldContainer: %s", results.ErrorMessage().c_str()); - return; - } + std::string query = StringFormat( + "SELECT bagidx, itemid, charges, augslot1, augslot2, augslot3, augslot4, augslot5, augslot6 " + "FROM object_contents WHERE parentid = %i", parentid + ); + + auto results = QueryDatabase(query); + if (!results.Success()) { + LogError("Error in DB::LoadWorldContainer: [{}]", results.ErrorMessage().c_str()); + return; + } for (auto row = results.begin(); row != results.end(); ++row) { uint8 index = (uint8)atoi(row[0]); @@ -754,7 +758,7 @@ void ZoneDatabase::SaveWorldContainer(uint32 zone_id, uint32 parent_id, const EQ augslot[0], augslot[1], augslot[2], augslot[3], augslot[4], augslot[5]); auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Error in ZoneDatabase::SaveWorldContainer: %s", results.ErrorMessage().c_str()); + LogError("Error in ZoneDatabase::SaveWorldContainer: [{}]", results.ErrorMessage().c_str()); } @@ -766,7 +770,7 @@ void ZoneDatabase::DeleteWorldContainer(uint32 parent_id, uint32 zone_id) std::string query = StringFormat("DELETE FROM object_contents WHERE parentid = %i AND zoneid = %i", parent_id, zone_id); auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::General, Logs::Error, "Error in ZoneDatabase::DeleteWorldContainer: %s", results.ErrorMessage().c_str()); + LogError("Error in ZoneDatabase::DeleteWorldContainer: [{}]", results.ErrorMessage().c_str()); } @@ -778,14 +782,14 @@ Trader_Struct* ZoneDatabase::LoadTraderItem(uint32 char_id) std::string query = StringFormat("SELECT * FROM trader WHERE char_id = %i ORDER BY slot_id LIMIT 80", char_id); auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::Detail, Logs::Trading, "Failed to load trader information!\n"); + LogTrading("Failed to load trader information!\n"); return loadti; } loadti->Code = BazaarTrader_ShowItems; for (auto row = results.begin(); row != results.end(); ++row) { if (atoi(row[5]) >= 80 || atoi(row[4]) < 0) { - Log(Logs::Detail, Logs::Trading, "Bad Slot number when trying to load trader information!\n"); + LogTrading("Bad Slot number when trying to load trader information!\n"); continue; } @@ -803,13 +807,13 @@ TraderCharges_Struct* ZoneDatabase::LoadTraderItemWithCharges(uint32 char_id) std::string query = StringFormat("SELECT * FROM trader WHERE char_id=%i ORDER BY slot_id LIMIT 80", char_id); auto results = QueryDatabase(query); if (!results.Success()) { - Log(Logs::Detail, Logs::Trading, "Failed to load trader information!\n"); + LogTrading("Failed to load trader information!\n"); return loadti; } for (auto row = results.begin(); row != results.end(); ++row) { if (atoi(row[5]) >= 80 || atoi(row[5]) < 0) { - Log(Logs::Detail, Logs::Trading, "Bad Slot number when trying to load trader information!\n"); + LogTrading("Bad Slot number when trying to load trader information!\n"); continue; } @@ -829,7 +833,7 @@ EQEmu::ItemInstance* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int Seria return nullptr; if (results.RowCount() == 0) { - Log(Logs::Detail, Logs::Trading, "Bad result from query\n"); fflush(stdout); + LogTrading("Bad result from query\n"); fflush(stdout); return nullptr; } @@ -842,7 +846,7 @@ EQEmu::ItemInstance* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int Seria const EQEmu::ItemData *item = database.GetItem(ItemID); if(!item) { - Log(Logs::Detail, Logs::Trading, "Unable to create item\n"); + LogTrading("Unable to create item\n"); fflush(stdout); return nullptr; } @@ -852,7 +856,7 @@ EQEmu::ItemInstance* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int Seria EQEmu::ItemInstance* inst = database.CreateItem(item); if(!inst) { - Log(Logs::Detail, Logs::Trading, "Unable to create item instance\n"); + LogTrading("Unable to create item instance\n"); fflush(stdout); return nullptr; } @@ -874,25 +878,24 @@ void ZoneDatabase::SaveTraderItem(uint32 CharID, uint32 ItemID, uint32 SerialNum CharID, ItemID, SerialNumber, Charges, ItemCost, Slot); auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::Detail, Logs::None, "[CLIENT] Failed to save trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); + LogDebug("[CLIENT] Failed to save trader item: [{}] for char_id: [{}], the error was: [{}]\n", ItemID, CharID, results.ErrorMessage().c_str()); } void ZoneDatabase::UpdateTraderItemCharges(int CharID, uint32 SerialNumber, int32 Charges) { - Log(Logs::Detail, Logs::Trading, "ZoneDatabase::UpdateTraderItemCharges(%i, %i, %i)", CharID, SerialNumber, Charges); + LogTrading("ZoneDatabase::UpdateTraderItemCharges([{}], [{}], [{}])", CharID, SerialNumber, Charges); std::string query = StringFormat("UPDATE trader SET charges = %i WHERE char_id = %i AND serialnumber = %i", Charges, CharID, SerialNumber); auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::Detail, Logs::None, "[CLIENT] Failed to update charges for trader item: %i for char_id: %i, the error was: %s\n", - SerialNumber, CharID, results.ErrorMessage().c_str()); + LogDebug("[CLIENT] Failed to update charges for trader item: [{}] for char_id: [{}], the error was: [{}]\n", SerialNumber, CharID, results.ErrorMessage().c_str()); } void ZoneDatabase::UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charges, uint32 NewPrice) { - Log(Logs::Detail, Logs::Trading, "ZoneDatabase::UpdateTraderPrice(%i, %i, %i, %i)", CharID, ItemID, Charges, NewPrice); + LogTrading("ZoneDatabase::UpdateTraderPrice([{}], [{}], [{}], [{}])", CharID, ItemID, Charges, NewPrice); const EQEmu::ItemData *item = database.GetItem(ItemID); @@ -900,12 +903,12 @@ void ZoneDatabase::UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charg return; if(NewPrice == 0) { - Log(Logs::Detail, Logs::Trading, "Removing Trader items from the DB for CharID %i, ItemID %i", CharID, ItemID); + LogTrading("Removing Trader items from the DB for CharID [{}], ItemID [{}]", CharID, ItemID); std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i AND item_id = %i",CharID, ItemID); auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::Detail, Logs::None, "[CLIENT] Failed to remove trader item(s): %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); + LogDebug("[CLIENT] Failed to remove trader item(s): [{}] for char_id: [{}], the error was: [{}]\n", ItemID, CharID, results.ErrorMessage().c_str()); return; } @@ -916,7 +919,7 @@ void ZoneDatabase::UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charg NewPrice, CharID, ItemID, Charges); auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::Detail, Logs::None, "[CLIENT] Failed to update price for trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); + LogDebug("[CLIENT] Failed to update price for trader item: [{}] for char_id: [{}], the error was: [{}]\n", ItemID, CharID, results.ErrorMessage().c_str()); return; } @@ -926,7 +929,7 @@ void ZoneDatabase::UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charg NewPrice, CharID, ItemID); auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::Detail, Logs::None, "[CLIENT] Failed to update price for trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); + LogDebug("[CLIENT] Failed to update price for trader item: [{}] for char_id: [{}], the error was: [{}]\n", ItemID, CharID, results.ErrorMessage().c_str()); } void ZoneDatabase::DeleteTraderItem(uint32 char_id){ @@ -935,7 +938,7 @@ void ZoneDatabase::DeleteTraderItem(uint32 char_id){ const std::string query = "DELETE FROM trader"; auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::Detail, Logs::None, "[CLIENT] Failed to delete all trader items data, the error was: %s\n", results.ErrorMessage().c_str()); + LogDebug("[CLIENT] Failed to delete all trader items data, the error was: [{}]\n", results.ErrorMessage().c_str()); return; } @@ -943,7 +946,7 @@ void ZoneDatabase::DeleteTraderItem(uint32 char_id){ std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i", char_id); auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::Detail, Logs::None, "[CLIENT] Failed to delete trader item data for char_id: %i, the error was: %s\n", char_id, results.ErrorMessage().c_str()); + LogDebug("[CLIENT] Failed to delete trader item data for char_id: [{}], the error was: [{}]\n", char_id, results.ErrorMessage().c_str()); } void ZoneDatabase::DeleteTraderItem(uint32 CharID,uint16 SlotID) { @@ -951,7 +954,7 @@ void ZoneDatabase::DeleteTraderItem(uint32 CharID,uint16 SlotID) { std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i And slot_id = %i", CharID, SlotID); auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::Detail, Logs::None, "[CLIENT] Failed to delete trader item data for char_id: %i, the error was: %s\n",CharID, results.ErrorMessage().c_str()); + LogDebug("[CLIENT] Failed to delete trader item data for char_id: [{}], the error was: [{}]\n",CharID, results.ErrorMessage().c_str()); } void ZoneDatabase::DeleteBuyLines(uint32 CharID) { @@ -960,7 +963,7 @@ void ZoneDatabase::DeleteBuyLines(uint32 CharID) { const std::string query = "DELETE FROM buyer"; auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::Detail, Logs::None, "[CLIENT] Failed to delete all buyer items data, the error was: %s\n",results.ErrorMessage().c_str()); + LogDebug("[CLIENT] Failed to delete all buyer items data, the error was: [{}]\n",results.ErrorMessage().c_str()); return; } @@ -968,7 +971,7 @@ void ZoneDatabase::DeleteBuyLines(uint32 CharID) { std::string query = StringFormat("DELETE FROM buyer WHERE charid = %i", CharID); auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::Detail, Logs::None, "[CLIENT] Failed to delete buyer item data for charid: %i, the error was: %s\n",CharID,results.ErrorMessage().c_str()); + LogDebug("[CLIENT] Failed to delete buyer item data for charid: [{}], the error was: [{}]\n",CharID,results.ErrorMessage().c_str()); } @@ -977,7 +980,7 @@ void ZoneDatabase::AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, cons CharID, BuySlot, ItemID, ItemName, Quantity, Price); auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::Detail, Logs::None, "[CLIENT] Failed to save buline item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); + LogDebug("[CLIENT] Failed to save buline item: [{}] for char_id: [{}], the error was: [{}]\n", ItemID, CharID, results.ErrorMessage().c_str()); } @@ -985,7 +988,7 @@ void ZoneDatabase::RemoveBuyLine(uint32 CharID, uint32 BuySlot) { std::string query = StringFormat("DELETE FROM buyer WHERE charid = %i AND buyslot = %i", CharID, BuySlot); auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::Detail, Logs::None, "[CLIENT] Failed to delete buyslot %i for charid: %i, the error was: %s\n", BuySlot, CharID, results.ErrorMessage().c_str()); + LogDebug("[CLIENT] Failed to delete buyslot [{}] for charid: [{}], the error was: [{}]\n", BuySlot, CharID, results.ErrorMessage().c_str()); } @@ -998,7 +1001,7 @@ void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) std::string query = StringFormat("UPDATE buyer SET quantity = %i WHERE charid = %i AND buyslot = %i", Quantity, CharID, BuySlot); auto results = QueryDatabase(query); if (!results.Success()) - Log(Logs::Detail, Logs::None, "[CLIENT] Failed to update quantity in buyslot %i for charid: %i, the error was: %s\n", BuySlot, CharID, results.ErrorMessage().c_str()); + LogDebug("[CLIENT] Failed to update quantity in buyslot [{}] for charid: [{}], the error was: [{}]\n", BuySlot, CharID, results.ErrorMessage().c_str()); } @@ -1509,7 +1512,7 @@ bool ZoneDatabase::LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Str bool ZoneDatabase::SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value){ std::string query = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, lang_id, value); QueryDatabase(query); - Log(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterLanguage for character ID: %i, lang_id:%u value:%u done", character_id, lang_id, value); + LogDebug("ZoneDatabase::SaveCharacterLanguage for character ID: [{}], lang_id:[{}] value:[{}] done", character_id, lang_id, value); return true; } @@ -1521,13 +1524,12 @@ bool ZoneDatabase::SaveCharacterBindPoint(uint32 character_id, const BindStruct "%u, %u, %f, %f, %f, %f, %i)", character_id, bind.zoneId, bind.instance_id, bind.x, bind.y, bind.z, bind.heading, bind_num); - Log(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterBindPoint for character ID: %i zone_id: %u " - "instance_id: %u position: %f %f %f %f bind_num: %u", + LogDebug("ZoneDatabase::SaveCharacterBindPoint for character ID: [{}] zone_id: [{}] instance_id: [{}] position: [{}] [{}] [{}] [{}] bind_num: [{}]", character_id, bind.zoneId, bind.instance_id, bind.x, bind.y, bind.z, bind.heading, bind_num); auto results = QueryDatabase(query); if (!results.RowsAffected()) - Log(Logs::General, Logs::None, "ERROR Bind Home Save: %s. %s", results.ErrorMessage().c_str(), + LogDebug("ERROR Bind Home Save: [{}]. [{}]", results.ErrorMessage().c_str(), query.c_str()); return true; @@ -1539,20 +1541,20 @@ bool ZoneDatabase::SaveCharacterMaterialColor(uint32 character_id, uint32 slot_i uint8 blue = (color & 0x000000FF); std::string query = StringFormat("REPLACE INTO `character_material` (id, slot, red, green, blue, color, use_tint) VALUES (%u, %u, %u, %u, %u, %u, 255)", character_id, slot_id, red, green, blue, color); auto results = QueryDatabase(query); - Log(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterMaterialColor for character ID: %i, slot_id: %u color: %u done", character_id, slot_id, color); + LogDebug("ZoneDatabase::SaveCharacterMaterialColor for character ID: [{}], slot_id: [{}] color: [{}] done", character_id, slot_id, color); return true; } bool ZoneDatabase::SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value){ std::string query = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, skill_id, value); auto results = QueryDatabase(query); - Log(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterSkill for character ID: %i, skill_id:%u value:%u done", character_id, skill_id, value); + LogDebug("ZoneDatabase::SaveCharacterSkill for character ID: [{}], skill_id:[{}] value:[{}] done", character_id, skill_id, value); return true; } bool ZoneDatabase::SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id){ std::string query = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, slot_id, disc_id); auto results = QueryDatabase(query); - Log(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterDisc for character ID: %i, slot:%u disc_id:%u done", character_id, slot_id, disc_id); + LogDebug("ZoneDatabase::SaveCharacterDisc for character ID: [{}], slot:[{}] disc_id:[{}] done", character_id, slot_id, disc_id); return true; } @@ -1564,7 +1566,7 @@ bool ZoneDatabase::SaveCharacterTribute(uint32 character_id, PlayerProfile_Struc if (pp->tributes[i].tribute >= 0 && pp->tributes[i].tribute != TRIBUTE_NONE){ std::string query = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); QueryDatabase(query); - Log(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterTribute for character ID: %i, tier:%u tribute:%u done", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); + LogDebug("ZoneDatabase::SaveCharacterTribute for character ID: [{}], tier:[{}] tribute:[{}] done", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); } } return true; @@ -1576,7 +1578,7 @@ bool ZoneDatabase::SaveCharacterBandolier(uint32 character_id, uint8 bandolier_i DoEscapeString(bandolier_name_esc, bandolier_name, strlen(bandolier_name)); std::string query = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%u, %u, %u, %u, %u,'%s')", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name_esc); auto results = QueryDatabase(query); - Log(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterBandolier for character ID: %i, bandolier_id: %u, bandolier_slot: %u item_id: %u, icon:%u band_name:%s done", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name); + LogDebug("ZoneDatabase::SaveCharacterBandolier for character ID: [{}], bandolier_id: [{}], bandolier_slot: [{}] item_id: [{}], icon:[{}] band_name:[{}] done", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name); return true; } @@ -1903,7 +1905,7 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla mail_key.c_str() ); auto results = database.QueryDatabase(query); - Log(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); + LogDebug("ZoneDatabase::SaveCharacterData [{}], done Took [{}] seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); return true; } @@ -1944,7 +1946,7 @@ bool ZoneDatabase::SaveCharacterCurrency(uint32 character_id, PlayerProfile_Stru pp->currentEbonCrystals, pp->careerEbonCrystals); auto results = database.QueryDatabase(query); - Log(Logs::General, Logs::None, "Saving Currency for character ID: %i, done", character_id); + LogDebug("Saving Currency for character ID: [{}], done", character_id); return true; } @@ -1953,7 +1955,7 @@ bool ZoneDatabase::SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 cur " VALUES (%u, %u, %u, %u)", character_id, aa_id, current_level, charges); auto results = QueryDatabase(rquery); - Log(Logs::General, Logs::None, "Saving AA for character ID: %u, aa_id: %u current_level: %u", character_id, aa_id, current_level); + LogDebug("Saving AA for character ID: [{}], aa_id: [{}] current_level: [{}]", character_id, aa_id, current_level); return true; } @@ -2078,7 +2080,7 @@ bool ZoneDatabase::SaveCharacterInvSnapshot(uint32 character_id) { character_id ); auto results = database.QueryDatabase(query); - Log(Logs::Moderate, Logs::Inventory, "ZoneDatabase::SaveCharacterInventorySnapshot %i (%s)", character_id, (results.Success() ? "pass" : "fail")); + LogInventory("ZoneDatabase::SaveCharacterInventorySnapshot [{}] ([{}])", character_id, (results.Success() ? "pass" : "fail")); return results.Success(); } @@ -2295,7 +2297,7 @@ bool ZoneDatabase::RestoreCharacterInvSnapshot(uint32 character_id, uint32 times // we should know what we're doing by the time we call this function..but, // this is to prevent inventory deletions where no timestamp entries exists if (!ValidateCharacterInvSnapshotTimestamp(character_id, timestamp)) { - Log(Logs::General, Logs::Error, "ZoneDatabase::RestoreCharacterInvSnapshot() called for id: %u without valid snapshot entries @ %u", character_id, timestamp); + LogError("ZoneDatabase::RestoreCharacterInvSnapshot() called for id: [{}] without valid snapshot entries @ [{}]", character_id, timestamp); return false; } @@ -2360,7 +2362,7 @@ bool ZoneDatabase::RestoreCharacterInvSnapshot(uint32 character_id, uint32 times ); results = database.QueryDatabase(query); - Log(Logs::General, Logs::Inventory, "ZoneDatabase::RestoreCharacterInvSnapshot() %s snapshot for %u @ %u", + LogInventory("ZoneDatabase::RestoreCharacterInvSnapshot() [{}] snapshot for [{}] @ [{}]", (results.Success() ? "restored" : "failed to restore"), character_id, timestamp); return results.Success(); @@ -2379,7 +2381,7 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load std::string where_condition = ""; if (bulk_load) { - Log(Logs::General, Logs::Debug, "Performing bulk NPC Types load"); + LogDebug("Performing bulk NPC Types load"); where_condition = StringFormat( "INNER JOIN spawnentry ON npc_types.id = spawnentry.npcID " "INNER JOIN spawn2 ON spawnentry.spawngroupID = spawn2.spawngroupID " @@ -3113,12 +3115,12 @@ void ZoneDatabase::SaveMercBuffs(Merc *merc) { Buffs_Struct *buffs = merc->GetBuffs(); // Remove any existing buff saves - std::string query = StringFormat("DELETE FROM merc_buffs WHERE MercId = %u", merc->GetMercID()); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - Log(Logs::General, Logs::Error, "Error While Deleting Merc Buffs before save: %s", results.ErrorMessage().c_str()); - return; - } + std::string query = StringFormat("DELETE FROM merc_buffs WHERE MercId = %u", merc->GetMercID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogError("Error While Deleting Merc Buffs before save: [{}]", results.ErrorMessage().c_str()); + return; + } for (int buffCount = 0; buffCount <= BUFF_COUNT; buffCount++) { if(buffs[buffCount].spellid == 0 || buffs[buffCount].spellid == SPELL_UNKNOWN) @@ -3142,7 +3144,7 @@ void ZoneDatabase::SaveMercBuffs(Merc *merc) { buffs[buffCount].caston_z, buffs[buffCount].ExtraDIChance); results = database.QueryDatabase(query); if(!results.Success()) { - Log(Logs::General, Logs::Error, "Error Saving Merc Buffs: %s", results.ErrorMessage().c_str()); + LogError("Error Saving Merc Buffs: [{}]", results.ErrorMessage().c_str()); break; } } @@ -3161,7 +3163,7 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) { merc->GetMercID()); auto results = database.QueryDatabase(query); if(!results.Success()) { - Log(Logs::General, Logs::Error, "Error Loading Merc Buffs: %s", results.ErrorMessage().c_str()); + LogError("Error Loading Merc Buffs: [{}]", results.ErrorMessage().c_str()); return; } @@ -3206,7 +3208,7 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) { query = StringFormat("DELETE FROM merc_buffs WHERE MercId = %u", merc->GetMercID()); results = database.QueryDatabase(query); if(!results.Success()) - Log(Logs::General, Logs::Error, "Error Loading Merc Buffs: %s", results.ErrorMessage().c_str()); + LogError("Error Loading Merc Buffs: [{}]", results.ErrorMessage().c_str()); } @@ -3222,14 +3224,14 @@ bool ZoneDatabase::DeleteMerc(uint32 merc_id) { auto results = database.QueryDatabase(query); if(!results.Success()) { - Log(Logs::General, Logs::Error, "Error Deleting Merc Buffs: %s", results.ErrorMessage().c_str()); + LogError("Error Deleting Merc Buffs: [{}]", results.ErrorMessage().c_str()); } query = StringFormat("DELETE FROM mercs WHERE MercID = '%u'", merc_id); results = database.QueryDatabase(query); if(!results.Success()) { - Log(Logs::General, Logs::Error, "Error Deleting Merc: %s", results.ErrorMessage().c_str()); + LogError("Error Deleting Merc: [{}]", results.ErrorMessage().c_str()); return false; } @@ -3247,7 +3249,7 @@ void ZoneDatabase::LoadMercEquipment(Merc *merc) { merc->GetLevel(), merc->GetLevel()); auto results = database.QueryDatabase(query); if(!results.Success()) { - Log(Logs::General, Logs::Error, "Error Loading Merc Inventory: %s", results.ErrorMessage().c_str()); + LogError("Error Loading Merc Inventory: [{}]", results.ErrorMessage().c_str()); return; } @@ -3458,7 +3460,7 @@ int32 ZoneDatabase::GetBlockedSpellsCount(uint32 zoneid) bool ZoneDatabase::LoadBlockedSpells(int32 blockedSpellsCount, ZoneSpellsBlocked* into, uint32 zoneid) { - Log(Logs::General, Logs::Status, "Loading Blocked Spells from database..."); + LogInfo("Loading Blocked Spells from database"); std::string query = StringFormat("SELECT id, spellid, type, x, y, z, x_diff, y_diff, z_diff, message " "FROM blocked_spells WHERE zoneid = %d ORDER BY id ASC", zoneid); @@ -4072,60 +4074,113 @@ bool ZoneDatabase::SetCharacterFactionLevel(uint32 char_id, int32 faction_id, in bool ZoneDatabase::LoadFactionData() { - std::string query = "SELECT MAX(id) FROM faction_list"; - auto results = QueryDatabase(query); - if (!results.Success()) { + std::string query("SELECT MAX(`id`) FROM `faction_list`"); + + auto faction_max_results = QueryDatabase(query); + if (!faction_max_results.Success() || faction_max_results.RowCount() == 0) { return false; } - if (results.RowCount() == 0) - return false; + auto fmr_row = faction_max_results.begin(); - auto row = results.begin(); + max_faction = atoul(fmr_row[0]); + faction_array = new Faction *[max_faction + 1]; - max_faction = row[0] ? atoi(row[0]) : 0; - faction_array = new Faction*[max_faction+1]; - for(unsigned int index=0; index faction_ids; + + // load factions + query = "SELECT `id`, `name`, `base` FROM `faction_list`"; - query = "SELECT id, name, base FROM faction_list"; - results = QueryDatabase(query); - if (!results.Success()) { + auto faction_results = QueryDatabase(query); + if (!faction_results.Success()) { return false; } - for (row = results.begin(); row != results.end(); ++row) { - uint32 index = atoi(row[0]); + for (auto fr_row : faction_results) { + + uint32 index = atoul(fr_row[0]); + if (index > max_faction) { + Log(Logs::General, Logs::Error, "Faction '%u' is out-of-bounds for faction array size!", index); + continue; + } + + // this should never hit since `id` is keyed..but, it alleviates any risk of lost pointers + if (faction_array[index] != nullptr) { + Log(Logs::General, Logs::Error, "Faction '%u' has already been assigned! (Duplicate Entry)", index); + continue; + } + faction_array[index] = new Faction; - strn0cpy(faction_array[index]->name, row[1], 50); - faction_array[index]->base = atoi(row[2]); + strn0cpy(faction_array[index]->name, fr_row[1], 50); + faction_array[index]->base = atoi(fr_row[2]); faction_array[index]->min = MIN_PERSONAL_FACTION; faction_array[index]->max = MAX_PERSONAL_FACTION; + + faction_ids.push_back(index); + } - // Load in the mimimum and maximum faction that can be earned for this faction - query = StringFormat("SELECT `min` , `max` FROM `faction_base_data` WHERE client_faction_id = %u", index); - auto baseResults = QueryDatabase(query); - if (!baseResults.Success() || baseResults.RowCount() == 0) { - Log(Logs::General, Logs::General, "Faction %d has no base data", (int)index); - } - else { - for (auto modRow = baseResults.begin(); modRow != baseResults.end(); ++modRow) { - faction_array[index]->min = atoi(modRow[0]); - faction_array[index]->max = atoi(modRow[1]); - Log(Logs::General, Logs::None, "Min(%d), Max(%d) for faction (%u)",faction_array[index]->min, faction_array[index]->max, index); + LogInfo("[{}] Faction(s) loaded...", faction_ids.size()); + + const std::string faction_id_criteria(implode(",", std::pair('\'', '\''), faction_ids)); + + // load faction mins/maxes + query = fmt::format("SELECT `client_faction_id`, `min`, `max` FROM `faction_base_data` WHERE `client_faction_id` IN ({})", faction_id_criteria); + + auto base_results = QueryDatabase(query); + if (base_results.Success()) { + + for (auto br_row : base_results) { + + uint32 index = atoul(br_row[0]); + if (index > max_faction) { + LogError("Faction [{}] is out-of-bounds for faction array size in Base adjustment!", index); + continue; } + + if (faction_array[index] == nullptr) { + LogError("Faction [{}] does not exist for Base adjustment!", index); + continue; + } + + faction_array[index]->min = atoi(br_row[1]); + faction_array[index]->max = atoi(br_row[2]); } - // Load in modifiers to the faction based on characters race, class and diety. - query = StringFormat("SELECT `mod`, `mod_name` FROM `faction_list_mod` WHERE faction_id = %u", index); - auto modResults = QueryDatabase(query); - if (!modResults.Success()) - continue; + LogInfo("[{}] Faction Base(s) loaded...", base_results.RowCount()); + } + else { + LogInfo("Unable to load Faction Base data..."); + } + + // load race, class and diety modifiers + query = fmt::format("SELECT `faction_id`, `mod`, `mod_name` FROM `faction_list_mod` WHERE `faction_id` IN ({})", faction_id_criteria); - for (auto modRow = modResults.begin(); modRow != modResults.end(); ++modRow) { - faction_array[index]->mods[modRow[1]] = atoi(modRow[0]); + auto modifier_results = QueryDatabase(query); + if (modifier_results.Success()) { + + for (auto mr_row : modifier_results) { + + uint32 index = atoul(mr_row[0]); + if (index > max_faction) { + Log(Logs::General, Logs::Error, "Faction '%u' is out-of-bounds for faction array size in Modifier adjustment!", index); + continue; + } + + if (faction_array[index] == nullptr) { + Log(Logs::General, Logs::Error, "Faction '%u' does not exist for Modifier adjustment!", index); + continue; + } + + faction_array[index]->mods[mr_row[2]] = atoi(mr_row[1]); } - } + + LogInfo("[{}] Faction Modifier(s) loaded", modifier_results.RowCount()); + } + else { + LogError("Unable to load Faction Modifier data"); + } return true; } @@ -4602,11 +4657,11 @@ Corpse* ZoneDatabase::SummonBuriedCharacterCorpses(uint32 char_id, uint32 dest_z if (!corpse) continue; - entity_list.AddCorpse(corpse); - corpse->SetDecayTimer(RuleI(Character, CorpseDecayTimeMS)); - corpse->Spawn(); - if (!UnburyCharacterCorpse(corpse->GetCorpseDBID(), dest_zone_id, dest_instance_id, position)) - Log(Logs::General, Logs::Error, "Unable to unbury a summoned player corpse for character id %u.", char_id); + entity_list.AddCorpse(corpse); + corpse->SetDecayTimer(RuleI(Character, CorpseDecayTimeMS)); + corpse->Spawn(); + if (!UnburyCharacterCorpse(corpse->GetCorpseDBID(), dest_zone_id, dest_instance_id, position)) + LogError("Unable to unbury a summoned player corpse for character id [{}]", char_id); } return corpse; @@ -4645,7 +4700,7 @@ bool ZoneDatabase::SummonAllCharacterCorpses(uint32 char_id, uint32 dest_zone_id ++CorpseCount; } else{ - Log(Logs::General, Logs::Error, "Unable to construct a player corpse for character id %u.", char_id); + LogError("Unable to construct a player corpse for character id [{}]", char_id); } } diff --git a/zone/zoning.cpp b/zone/zoning.cpp index 800ee8cd2..8eaa05c5a 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -44,12 +44,12 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { bZoning = true; if (app->size != sizeof(ZoneChange_Struct)) { - Log(Logs::General, Logs::None, "Wrong size: OP_ZoneChange, size=%d, expected %d", app->size, sizeof(ZoneChange_Struct)); + LogDebug("Wrong size: OP_ZoneChange, size=[{}], expected [{}]", app->size, sizeof(ZoneChange_Struct)); return; } #if EQDEBUG >= 5 - Log(Logs::General, Logs::None, "Zone request from %s", GetName()); + LogDebug("Zone request from [{}]", GetName()); DumpPacket(app); #endif ZoneChange_Struct* zc=(ZoneChange_Struct*)app->pBuffer; @@ -97,7 +97,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { //Todo cheat detection Message(Chat::Red, "Invalid unsolicited zone request."); - Log(Logs::General, Logs::Error, "Zoning %s: Invalid unsolicited zone request to zone id '%d'.", GetName(), target_zone_id); + LogError("Zoning [{}]: Invalid unsolicited zone request to zone id [{}]", GetName(), target_zone_id); SendZoneCancel(zc); return; } @@ -129,7 +129,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { //if we didnt get a zone point, or its to a different zone, //then we assume this is invalid. if(!zone_point || zone_point->target_zone_id != target_zone_id) { - Log(Logs::General, Logs::Error, "Zoning %s: Invalid unsolicited zone request to zone id '%d'.", GetName(), target_zone_id); + LogError("Zoning [{}]: Invalid unsolicited zone request to zone id [{}]", GetName(), target_zone_id); //todo cheat detection SendZoneCancel(zc); return; @@ -160,7 +160,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { if(target_zone_name == nullptr) { //invalid zone... Message(Chat::Red, "Invalid target zone ID."); - Log(Logs::General, Logs::Error, "Zoning %s: Unable to get zone name for zone id '%d'.", GetName(), target_zone_id); + LogError("Zoning [{}]: Unable to get zone name for zone id [{}]", GetName(), target_zone_id); SendZoneCancel(zc); return; } @@ -173,7 +173,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { if(!database.GetSafePoints(target_zone_name, database.GetInstanceVersion(target_instance_id), &safe_x, &safe_y, &safe_z, &minstatus, &minlevel, flag_needed)) { //invalid zone... Message(Chat::Red, "Invalid target zone while getting safe points."); - Log(Logs::General, Logs::Error, "Zoning %s: Unable to get safe coordinates for zone '%s'.", GetName(), target_zone_name); + LogError("Zoning [{}]: Unable to get safe coordinates for zone [{}]", GetName(), target_zone_name); SendZoneCancel(zc); return; } @@ -193,7 +193,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { switch(zone_mode) { case EvacToSafeCoords: case ZoneToSafeCoords: - Log(Logs::General, Logs::None, "Zoning %s to safe coords (%f,%f,%f) in %s (%d)", GetName(), safe_x, safe_y, safe_z, target_zone_name, target_zone_id); + LogDebug("Zoning [{}] to safe coords ([{}],[{}],[{}]) in [{}] ([{}])", GetName(), safe_x, safe_y, safe_z, target_zone_name, target_zone_id); dest_x = safe_x; dest_y = safe_y; dest_z = safe_z; @@ -253,7 +253,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { //could not find a valid reason for them to be zoning, stop it. //todo cheat detection - Log(Logs::General, Logs::Error, "Zoning %s: Invalid unsolicited zone request to zone id '%s'. Not near a zone point.", GetName(), target_zone_name); + LogError("Zoning [{}]: Invalid unsolicited zone request to zone id [{}]. Not near a zone point", GetName(), target_zone_name); SendZoneCancel(zc); return; default: @@ -288,7 +288,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { //we have successfully zoned DoZoneSuccess(zc, target_zone_id, target_instance_id, dest_x, dest_y, dest_z, dest_h, ignorerestrictions); } else { - Log(Logs::General, Logs::Error, "Zoning %s: Rules prevent this char from zoning into '%s'", GetName(), target_zone_name); + LogError("Zoning [{}]: Rules prevent this char from zoning into [{}]", GetName(), target_zone_name); SendZoneError(zc, myerror); } } @@ -313,7 +313,7 @@ void Client::SendZoneCancel(ZoneChange_Struct *zc) { void Client::SendZoneError(ZoneChange_Struct *zc, int8 err) { - Log(Logs::General, Logs::Error, "Zone %i is not available because target wasn't found or character insufficent level", zc->zoneID); + LogError("Zone [{}] is not available because target wasn't found or character insufficent level", zc->zoneID); EQApplicationPacket *outapp = nullptr; outapp = new EQApplicationPacket(OP_ZoneChange, sizeof(ZoneChange_Struct)); @@ -348,7 +348,7 @@ void Client::DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instanc if(this->GetPet()) entity_list.RemoveFromHateLists(this->GetPet()); - Log(Logs::General, Logs::Status, "Zoning '%s' to: %s (%i) - (%i) x=%f, y=%f, z=%f", m_pp.name, database.GetZoneName(zone_id), zone_id, instance_id, dest_x, dest_y, dest_z); + LogInfo("Zoning [{}] to: [{}] ([{}]) - ([{}]) x [{}] y [{}] z [{}]", m_pp.name, database.GetZoneName(zone_id), zone_id, instance_id, dest_x, dest_y, dest_z); //set the player's coordinates in the new zone so they have them //when they zone into it @@ -471,7 +471,7 @@ void Client::ProcessMovePC(uint32 zoneID, uint32 instance_id, float x, float y, ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); break; default: - Log(Logs::General, Logs::Error, "Client::ProcessMovePC received a reguest to perform an unsupported client zone operation."); + LogError("Client::ProcessMovePC received a reguest to perform an unsupported client zone operation"); break; } } @@ -530,7 +530,7 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z heading = m_pp.binds[0].heading; zonesummon_ignorerestrictions = 1; - Log(Logs::General, Logs::None, "Player %s has died and will be zoned to bind point in zone: %s at LOC x=%f, y=%f, z=%f, heading=%f", + LogDebug("Player [{}] has died and will be zoned to bind point in zone: [{}] at LOC x=[{}], y=[{}], z=[{}], heading=[{}]", GetName(), pZoneName, m_pp.binds[0].x, m_pp.binds[0].y, m_pp.binds[0].z, m_pp.binds[0].heading); break; case SummonPC: @@ -539,7 +539,7 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z SetHeading(heading); break; case Rewind: - Log(Logs::General, Logs::None, "%s has requested a /rewind from %f, %f, %f, to %f, %f, %f in %s", GetName(), + LogDebug("[{}] has requested a /rewind from [{}], [{}], [{}], to [{}], [{}], [{}] in [{}]", GetName(), m_Position.x, m_Position.y, m_Position.z, m_RewindLocation.x, m_RewindLocation.y, m_RewindLocation.z, zone->GetShortName()); m_ZoneSummonLocation = glm::vec3(x, y, z); @@ -547,7 +547,7 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z SetHeading(heading); break; default: - Log(Logs::General, Logs::Error, "Client::ZonePC() received a reguest to perform an unsupported client zone operation."); + LogError("Client::ZonePC() received a reguest to perform an unsupported client zone operation"); ReadyToZone = false; break; } @@ -683,7 +683,7 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z safe_delete(outapp); } - Log(Logs::Detail, Logs::None, "Player %s has requested a zoning to LOC x=%f, y=%f, z=%f, heading=%f in zoneid=%i", GetName(), x, y, z, heading, zoneID); + LogDebug("Player [{}] has requested a zoning to LOC x=[{}], y=[{}], z=[{}], heading=[{}] in zoneid=[{}]", GetName(), x, y, z, heading, zoneID); //Clear zonesummon variables if we're zoning to our own zone //Client wont generate a zone change packet to the server in this case so //They aren't needed and it keeps behavior on next zone attempt from being undefined. @@ -777,7 +777,7 @@ void Client::SetZoneFlag(uint32 zone_id) { std::string query = StringFormat("INSERT INTO zone_flags (charID,zoneID) VALUES(%d,%d)", CharacterID(), zone_id); auto results = database.QueryDatabase(query); if(!results.Success()) - Log(Logs::General, Logs::Error, "MySQL Error while trying to set zone flag for %s: %s", GetName(), results.ErrorMessage().c_str()); + LogError("MySQL Error while trying to set zone flag for [{}]: [{}]", GetName(), results.ErrorMessage().c_str()); } void Client::ClearZoneFlag(uint32 zone_id) { @@ -790,7 +790,7 @@ void Client::ClearZoneFlag(uint32 zone_id) { std::string query = StringFormat("DELETE FROM zone_flags WHERE charID=%d AND zoneID=%d", CharacterID(), zone_id); auto results = database.QueryDatabase(query); if(!results.Success()) - Log(Logs::General, Logs::Error, "MySQL Error while trying to clear zone flag for %s: %s", GetName(), results.ErrorMessage().c_str()); + LogError("MySQL Error while trying to clear zone flag for [{}]: [{}]", GetName(), results.ErrorMessage().c_str()); } @@ -800,7 +800,7 @@ void Client::LoadZoneFlags() { std::string query = StringFormat("SELECT zoneID from zone_flags WHERE charID=%d", CharacterID()); auto results = database.QueryDatabase(query); if (!results.Success()) { - Log(Logs::General, Logs::Error, "MySQL Error while trying to load zone flags for %s: %s", GetName(), results.ErrorMessage().c_str()); + LogError("MySQL Error while trying to load zone flags for [{}]: [{}]", GetName(), results.ErrorMessage().c_str()); return; } @@ -863,23 +863,23 @@ bool Client::CanBeInZone() { char flag_needed[128]; if(!database.GetSafePoints(zone->GetShortName(), zone->GetInstanceVersion(), &safe_x, &safe_y, &safe_z, &minstatus, &minlevel, flag_needed)) { //this should not happen... - Log(Logs::Detail, Logs::None, "[CLIENT] Unable to query zone info for ourself '%s'", zone->GetShortName()); + LogDebug("[CLIENT] Unable to query zone info for ourself [{}]", zone->GetShortName()); return(false); } if(GetLevel() < minlevel) { - Log(Logs::Detail, Logs::None, "[CLIENT] Character does not meet min level requirement (%d < %d)!", GetLevel(), minlevel); + LogDebug("[CLIENT] Character does not meet min level requirement ([{}] < [{}])!", GetLevel(), minlevel); return(false); } if(Admin() < minstatus) { - Log(Logs::Detail, Logs::None, "[CLIENT] Character does not meet min status requirement (%d < %d)!", Admin(), minstatus); + LogDebug("[CLIENT] Character does not meet min status requirement ([{}] < [{}])!", Admin(), minstatus); return(false); } if(flag_needed[0] != '\0') { //the flag needed string is not empty, meaning a flag is required. if(Admin() < minStatusToIgnoreZoneFlags && !HasZoneFlag(zone->GetZoneID())) { - Log(Logs::Detail, Logs::None, "[CLIENT] Character does not have the flag to be in this zone (%s)!", flag_needed); + LogDebug("[CLIENT] Character does not have the flag to be in this zone ([{}])!", flag_needed); return(false); } }