mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-31 13:16:39 +00:00
Compare commits
152 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a2b2a6a5cf | |||
| 43a5bff84a | |||
| e983d07228 | |||
| 90db12483a | |||
| ff71cfbd5b | |||
| 08bb9de437 | |||
| 16e341906d | |||
| 216b3a039f | |||
| b813cf71bb | |||
| 48ecd1222f | |||
| e758b407e9 | |||
| 758dd1875e | |||
| 8e2961dda5 | |||
| 5522eda6e4 | |||
| ac1469bac2 | |||
| c2989e019a | |||
| e16b481ba2 | |||
| 4fc0ffd173 | |||
| b883888a19 | |||
| 50ad97aa0b | |||
| dca892e258 | |||
| f9fe4ea2ec | |||
| cc30c72538 | |||
| d1fd40cd85 | |||
| f3af458cb3 | |||
| a093d04594 | |||
| 115df81400 | |||
| 5babc864b9 | |||
| 60a2dd8616 | |||
| 9be2485330 | |||
| a2bf10624a | |||
| ed58d16f1f | |||
| e9b45fb360 | |||
| 803972873a | |||
| d9e57eca79 | |||
| 6429dc80d3 | |||
| c4262b3fa6 | |||
| 92128b98fd | |||
| b9cfdea76c | |||
| c8a7066d0e | |||
| 950cc4a325 | |||
| 82b48fe6e8 | |||
| ca9c1fdd24 | |||
| fe08961d25 | |||
| 5b9f7ff4c9 | |||
| 23743a4050 | |||
| 444d688ad2 | |||
| d554eb3423 | |||
| deb298dda7 | |||
| 19e785b842 | |||
| 7b9691d486 | |||
| 30fddcc5a0 | |||
| 235e59a2d8 | |||
| bc1ffe0716 | |||
| 44497414db | |||
| 96e34fe8f7 | |||
| ceca28d2a3 | |||
| b040571427 | |||
| 49664cc1a0 | |||
| 5e4fd43920 | |||
| 937b947597 | |||
| bb70850421 | |||
| 46511365a7 | |||
| 6d69ac7a98 | |||
| 938937c271 | |||
| cd808416c8 | |||
| a05d0752f6 | |||
| 799609fb21 | |||
| 213fe6a9e9 | |||
| be6a5d5f50 | |||
| 1af29bd7b1 | |||
| ef945e6e99 | |||
| 9528c1e7fc | |||
| fd6e5f465d | |||
| d00125abe1 | |||
| 5d69235a4c | |||
| e93785f885 | |||
| 3c2545cfaf | |||
| 8d1a9efac9 | |||
| f6b18fb003 | |||
| 00e77f190c | |||
| 9cb72a6ba7 | |||
| 8203c034bf | |||
| 33ae51f56f | |||
| 30c39194a3 | |||
| 051ce3736f | |||
| 84708edccf | |||
| da4e9ab95b | |||
| a8fea95eab | |||
| 53610c2f0f | |||
| 9f10c12874 | |||
| a0634adb3c | |||
| a2ed6be1f5 | |||
| c33ac40567 | |||
| 9ee095b354 | |||
| 7ab32af4dc | |||
| 0c301419c2 | |||
| d6a21be25e | |||
| 1d4ba082ad | |||
| 94553501ba | |||
| da824d5178 | |||
| 5a1df38900 | |||
| 8cd7148b29 | |||
| 09e079a45e | |||
| 4bc881da4b | |||
| 0615864d51 | |||
| 3638d157b2 | |||
| d41725e325 | |||
| 88580b69b6 | |||
| f7a6fe595a | |||
| 07d14c2681 | |||
| eac7a73fb6 | |||
| c5715f1f14 | |||
| 3902230fa1 | |||
| 212969f5cd | |||
| de4226fdc9 | |||
| 8b13434197 | |||
| 27274397ec | |||
| 2b79a36014 | |||
| 8cf52294e9 | |||
| 0ef79903f8 | |||
| ab14458f9e | |||
| 5b94e736b3 | |||
| c3b8cc9744 | |||
| 89e3b2c72e | |||
| 23c4aa241b | |||
| acb7584e26 | |||
| a885bd9322 | |||
| 20fe1926e0 | |||
| eb7118754b | |||
| 3611b49f68 | |||
| 511d8a8bb3 | |||
| 1598d2e17b | |||
| 805757ba87 | |||
| eb6ac25540 | |||
| cb634cf57d | |||
| 2f7ca2cdc8 | |||
| 425d24c1f4 | |||
| 875df8e64a | |||
| 7a2d2a0c51 | |||
| 5296202e56 | |||
| e2db8ffea8 | |||
| fa2ab11676 | |||
| 80e8634a48 | |||
| 98c2fa5127 | |||
| 8b8b41dab3 | |||
| 63b1e6b4b4 | |||
| 21b7b6e7ab | |||
| 878a5377ae | |||
| 486c7c44be | |||
| aa4869c6e9 | |||
| 94e1b4edfa |
+343
@@ -1,3 +1,346 @@
|
|||||||
|
## [23.5.0] 4/10/2025
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
* World API Optimizations ([#4850](https://github.com/EQEmu/Server/pull/4850)) @Akkadius 2025-04-10
|
||||||
|
|
||||||
|
### Bots
|
||||||
|
|
||||||
|
* Add valid state checks to ^clickitem ([#4830](https://github.com/EQEmu/Server/pull/4830)) @nytmyr 2025-04-10
|
||||||
|
* Flag all buffs with SE_DamageShield as Damage Shield ([#4833](https://github.com/EQEmu/Server/pull/4833)) @nytmyr 2025-04-10
|
||||||
|
* Positioning rewrite ([#4856](https://github.com/EQEmu/Server/pull/4856)) @nytmyr 2025-04-10
|
||||||
|
* Restore old buff overwrite blocking ([#4832](https://github.com/EQEmu/Server/pull/4832)) @nytmyr 2025-04-10
|
||||||
|
|
||||||
|
### Bugfix
|
||||||
|
|
||||||
|
* Load zone variables before encounter_load. ([#4846](https://github.com/EQEmu/Server/pull/4846)) @zimp-wow 2025-04-10
|
||||||
|
* Prevent depops from blocking new spawns. ([#4841](https://github.com/EQEmu/Server/pull/4841)) @zimp-wow 2025-04-10
|
||||||
|
* Prevent final shutdown from persisting incomplete state. ([#4849](https://github.com/EQEmu/Server/pull/4849)) @zimp-wow 2025-04-10
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
* Remove queryserv dump flag ([#4842](https://github.com/EQEmu/Server/pull/4842)) @joligario 2025-04-10
|
||||||
|
* Update link for legacy EQEmu loginserver account setup ([#4826](https://github.com/EQEmu/Server/pull/4826)) @joligario 2025-04-10
|
||||||
|
|
||||||
|
### Crash
|
||||||
|
|
||||||
|
* Fix rarer exception crash issue in PlayerEventLogs::ProcessBatchQueue ([#4835](https://github.com/EQEmu/Server/pull/4835)) @Akkadius 2025-04-03
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
|
* Fix manifest for `helmtexture` in `horses` table ([#4852](https://github.com/EQEmu/Server/pull/4852)) @joligario 2025-04-10
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* Add rule to consume command text from any channel ([#4839](https://github.com/EQEmu/Server/pull/4839)) @catapultam-habeo 2025-04-10
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Add the bazaar search limit to query ([#4829](https://github.com/EQEmu/Server/pull/4829)) @neckkola 2025-04-10
|
||||||
|
* Backfill expire_at (not sure why this didn't make it in there to begin with) @Akkadius 2025-03-31
|
||||||
|
* Bazaar Search window not working in a DZ ([#4828](https://github.com/EQEmu/Server/pull/4828)) @neckkola 2025-04-10
|
||||||
|
* Databuckets Account Cache Loading ([#4855](https://github.com/EQEmu/Server/pull/4855)) @Akkadius 2025-04-10
|
||||||
|
* Fix missing timer_name check on Mob::StopTimer ([#4840](https://github.com/EQEmu/Server/pull/4840)) @zimp-wow 2025-04-04
|
||||||
|
* FixHeading Infinite Loop Fix ([#4854](https://github.com/EQEmu/Server/pull/4854)) @KimLS 2025-04-10
|
||||||
|
* Make sure we don't expire default value instances @Akkadius 2025-03-31
|
||||||
|
* Regression in World SendEmoteMessageRaw ([#4837](https://github.com/EQEmu/Server/pull/4837)) @Akkadius 2025-04-03
|
||||||
|
* Remove QS Tables From Export @Akkadius 2025-04-10
|
||||||
|
* Zone State Spawn2 Location Restore ([#4844](https://github.com/EQEmu/Server/pull/4844)) @Akkadius 2025-04-10
|
||||||
|
|
||||||
|
### Netcode
|
||||||
|
|
||||||
|
* Fix Stale Client Edge Case ([#4853](https://github.com/EQEmu/Server/pull/4853)) @Akkadius 2025-04-10
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
* Character Save Optimizations ([#4851](https://github.com/EQEmu/Server/pull/4851)) @Akkadius 2025-04-10
|
||||||
|
* Network Ring Buffers ([#4857](https://github.com/EQEmu/Server/pull/4857)) @Akkadius 2025-04-10
|
||||||
|
* Pre-Compute CLE Server Lists ([#4838](https://github.com/EQEmu/Server/pull/4838)) @Akkadius 2025-04-10
|
||||||
|
|
||||||
|
### Spells
|
||||||
|
|
||||||
|
* Fear resistance effects edge case fixes and support for SPA 102 as an AA ([#4848](https://github.com/EQEmu/Server/pull/4848)) @KayenEQ 2025-04-10
|
||||||
|
* Update to SPA 180 SE_ResistSpellChance to not block unresistable spells. ([#4847](https://github.com/EQEmu/Server/pull/4847)) @KayenEQ 2025-04-10
|
||||||
|
* Update to SPA 378 SE_SpellEffectResistChance ([#4845](https://github.com/EQEmu/Server/pull/4845)) @KayenEQ 2025-04-10
|
||||||
|
|
||||||
|
## [23.4.0] 3/30/2025
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
* Expose Zoneserver Compile Metadata ([#4815](https://github.com/EQEmu/Server/pull/4815)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Bots
|
||||||
|
|
||||||
|
* Charmed Pets were breaking Mob respawns ([#4780](https://github.com/EQEmu/Server/pull/4780)) @nytmyr 2025-03-16
|
||||||
|
* Enraged positioning ([#4789](https://github.com/EQEmu/Server/pull/4789)) @nytmyr 2025-03-29
|
||||||
|
* Fix IsValidSpellTypeBySpellID to account for all types ([#4764](https://github.com/EQEmu/Server/pull/4764)) @nytmyr 2025-03-19
|
||||||
|
* Fix Rule ZonesWithSpawnLimits/ZonesWithForcedSpawnLimits errors ([#4791](https://github.com/EQEmu/Server/pull/4791)) @nytmyr 2025-03-29
|
||||||
|
* Fix rule Bots:FinishBuffing ([#4788](https://github.com/EQEmu/Server/pull/4788)) @nytmyr 2025-03-29
|
||||||
|
* Line of Sight and Mez optimizations and cleanup ([#4746](https://github.com/EQEmu/Server/pull/4746)) @nytmyr 2025-03-29
|
||||||
|
* Prevent bot pets from despawning on #repop ([#4790](https://github.com/EQEmu/Server/pull/4790)) @nytmyr 2025-03-29
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
* Control flow defaults missed in recent bot updates ([#4817](https://github.com/EQEmu/Server/pull/4817)) @joligario 2025-03-30
|
||||||
|
* Remove Extraneous Time Type in ShowZoneData ([#4806](https://github.com/EQEmu/Server/pull/4806)) @Kinglykrab 2025-03-29
|
||||||
|
* Remove Unused Command Methods ([#4805](https://github.com/EQEmu/Server/pull/4805)) @Kinglykrab 2025-03-29
|
||||||
|
* UCS Member Count ([#4819](https://github.com/EQEmu/Server/pull/4819)) @joligario 2025-03-30
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
* Add #show zone_variables ([#4812](https://github.com/EQEmu/Server/pull/4812)) @Akkadius 2025-03-29
|
||||||
|
* Add Instance Support to #zoneshutdown ([#4807](https://github.com/EQEmu/Server/pull/4807)) @Kinglykrab 2025-03-29
|
||||||
|
|
||||||
|
### Crash
|
||||||
|
|
||||||
|
* Fix Rarer World Crash with Player Event Thread Processor ([#4800](https://github.com/EQEmu/Server/pull/4800)) @Akkadius 2025-03-29
|
||||||
|
* Fix Repop Race Condition Crash ([#4814](https://github.com/EQEmu/Server/pull/4814)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
|
* Fix Respawn Times Table ([#4802](https://github.com/EQEmu/Server/pull/4802)) @Akkadius 2025-03-29
|
||||||
|
* Wrap PurgeExpiredInstances in a Transaction ([#4824](https://github.com/EQEmu/Server/pull/4824)) @Akkadius 2025-03-30
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* Implement /changename & related script bindings. Clean up #set name ([#4770](https://github.com/EQEmu/Server/pull/4770)) @catapultam-habeo 2025-03-20
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* AllowFVNoDrop Flag trades ([#4809](https://github.com/EQEmu/Server/pull/4809)) @neckkola 2025-03-27
|
||||||
|
* Fix Instance Creation Race Condition ([#4803](https://github.com/EQEmu/Server/pull/4803)) @Akkadius 2025-03-29
|
||||||
|
* Fix zone crash when attempting to add a disappearing client to hate list. ([#4782](https://github.com/EQEmu/Server/pull/4782)) @zimp-wow 2025-03-19
|
||||||
|
* Globally Reloading Quests when not loaded ([#4813](https://github.com/EQEmu/Server/pull/4813)) @Akkadius 2025-03-29
|
||||||
|
* Instance DZ Creation ([#4823](https://github.com/EQEmu/Server/pull/4823)) @Akkadius 2025-03-30
|
||||||
|
* Zone State Entity Variable Load Pre-Spawn ([#4785](https://github.com/EQEmu/Server/pull/4785)) @Akkadius 2025-03-19
|
||||||
|
* Zone State Position Fix ([#4784](https://github.com/EQEmu/Server/pull/4784)) @Akkadius 2025-03-19
|
||||||
|
* Zone State Variables Load First ([#4798](https://github.com/EQEmu/Server/pull/4798)) @Akkadius 2025-03-29
|
||||||
|
* Zone state edge case with 0 hp ([#4787](https://github.com/EQEmu/Server/pull/4787)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Instance
|
||||||
|
|
||||||
|
* Clear Respawn Timers on Creation ([#4801](https://github.com/EQEmu/Server/pull/4801)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Instances
|
||||||
|
|
||||||
|
* Add `expire_at` Column ([#4820](https://github.com/EQEmu/Server/pull/4820)) @Akkadius 2025-03-30
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
* Add several database indexes ([#4811](https://github.com/EQEmu/Server/pull/4811)) @Akkadius 2025-03-29
|
||||||
|
* Have World Send Smarter Guild Updates ([#4796](https://github.com/EQEmu/Server/pull/4796)) @Akkadius 2025-03-29
|
||||||
|
* Improve Character Select DB Performance ([#4799](https://github.com/EQEmu/Server/pull/4799)) @Akkadius 2025-03-29
|
||||||
|
* Reduce Adventure S2S chatter ([#4793](https://github.com/EQEmu/Server/pull/4793)) @Akkadius 2025-03-29
|
||||||
|
* Reduce CorpseOwnerOnline S2S Chatter to World ([#4795](https://github.com/EQEmu/Server/pull/4795)) @Akkadius 2025-03-29
|
||||||
|
* Reduce LFGuild Chatter ([#4794](https://github.com/EQEmu/Server/pull/4794)) @Akkadius 2025-03-29
|
||||||
|
* Reduce UpdateWho S2S Chatter to World ([#4792](https://github.com/EQEmu/Server/pull/4792)) @Akkadius 2025-03-29
|
||||||
|
* Send Smarter Emote Packets ([#4818](https://github.com/EQEmu/Server/pull/4818)) @Akkadius 2025-03-30
|
||||||
|
|
||||||
|
### Quest API
|
||||||
|
|
||||||
|
* Add Support for NPC ID and NPC Name Specificity ([#4781](https://github.com/EQEmu/Server/pull/4781)) @Kinglykrab 2025-03-19
|
||||||
|
|
||||||
|
### Reload
|
||||||
|
|
||||||
|
* Add Reload for Maps / Navs ([#4816](https://github.com/EQEmu/Server/pull/4816)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Zone
|
||||||
|
|
||||||
|
* Zone State Automated Testing and Improvements ([#4808](https://github.com/EQEmu/Server/pull/4808)) @Akkadius 2025-03-30
|
||||||
|
* Zone State Improvements Part 3 ([#4773](https://github.com/EQEmu/Server/pull/4773)) @Akkadius 2025-03-13
|
||||||
|
|
||||||
|
## [23.3.4] 3/14/2025
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Add check for simultaneous direct vendor and parcel Trader/Buyer Purchase ([#4778](https://github.com/EQEmu/Server/pull/4778)) @neckkola 2025-03-14
|
||||||
|
* Fix for rare circumstance where NPC's would have 0 health on restore @Akkadius
|
||||||
|
|
||||||
|
## [23.3.3] 3/13/2025
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
|
* Add indexes for data_buckets and zone_state_spawns ([#4771](https://github.com/EQEmu/Server/pull/4771)) @Akkadius 2025-03-11
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Update GuildBank to correctly handle items with charges equal to zero ([#4774](https://github.com/EQEmu/Server/pull/4774)) @neckkola 2025-03-13
|
||||||
|
|
||||||
|
### Networking
|
||||||
|
|
||||||
|
* Fix "port in use" error ([#4772](https://github.com/EQEmu/Server/pull/4772)) @Akkadius 2025-03-12
|
||||||
|
|
||||||
|
### Zone
|
||||||
|
|
||||||
|
* Zone State Improvements Part 3 ([#4773](https://github.com/EQEmu/Server/pull/4773)) @Akkadius 2025-03-13
|
||||||
|
|
||||||
|
## [23.3.2] 3/11/2025
|
||||||
|
|
||||||
|
### DynamicZones
|
||||||
|
|
||||||
|
* Bulk request dz member statuses on zone boot ([#4769](https://github.com/EQEmu/Server/pull/4769)) @hgtw 2025-03-11
|
||||||
|
|
||||||
|
### Zone
|
||||||
|
|
||||||
|
* Zone State Improvements (Continued) ([#4768](https://github.com/EQEmu/Server/pull/4768)) @Akkadius 2025-03-11
|
||||||
|
|
||||||
|
## [23.3.0] 3/8/2025
|
||||||
|
|
||||||
|
### Bots
|
||||||
|
|
||||||
|
* Fix buffs not overwriting lesser buffs ([#4756](https://github.com/EQEmu/Server/pull/4756)) @nytmyr 2025-03-06
|
||||||
|
* Fix taunting bots positioning ([#4754](https://github.com/EQEmu/Server/pull/4754)) @nytmyr 2025-03-06
|
||||||
|
* Move commanded spell map to zone ([#4755](https://github.com/EQEmu/Server/pull/4755)) @nytmyr 2025-03-06
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
* Fix typo in GM tradeskill combine message ([#4762](https://github.com/EQEmu/Server/pull/4762)) @nytmyr 2025-03-08
|
||||||
|
|
||||||
|
### Crash
|
||||||
|
|
||||||
|
* Bot aura crash fix ([#4752](https://github.com/EQEmu/Server/pull/4752)) @nytmyr 2025-03-06
|
||||||
|
|
||||||
|
### Databuckets
|
||||||
|
|
||||||
|
* Nested Databuckets Protections and Improvements ([#4748](https://github.com/EQEmu/Server/pull/4748)) @Akkadius 2025-03-04
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* Add Rule for dealing with augments when an item evolves ([#4758](https://github.com/EQEmu/Server/pull/4758)) @neckkola 2025-03-08
|
||||||
|
* Allow assigning Helm Texture independently of Body Texture for Horses ([#4759](https://github.com/EQEmu/Server/pull/4759)) @catapultam-habeo 2025-03-08
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Add crash checks for certain PlayerEventLogs ([#4761](https://github.com/EQEmu/Server/pull/4761)) @neckkola 2025-03-07
|
||||||
|
* Correct incorrectly calculated stat caps with Heroic Stats ([#4760](https://github.com/EQEmu/Server/pull/4760)) @catapultam-habeo 2025-03-08
|
||||||
|
* Fix sigabort crash from invalid JSON @Akkadius 2025-03-03
|
||||||
|
* Forgot to push up some changes for test output @Akkadius 2025-03-04
|
||||||
|
* Parcel Delivery Updates for two edge cases ([#4753](https://github.com/EQEmu/Server/pull/4753)) @neckkola 2025-03-06
|
||||||
|
* Remove one port check in world @Akkadius 2025-03-03
|
||||||
|
* Zero out currentnpcid whenever spawn is reset. ([#4763](https://github.com/EQEmu/Server/pull/4763)) @zimp-wow 2025-03-08
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
* Convert JSON Error to Data Buckets Logging Category ([#4747](https://github.com/EQEmu/Server/pull/4747)) @Kinglykrab 2025-03-04
|
||||||
|
|
||||||
|
### Pets
|
||||||
|
|
||||||
|
* Fix renamed pets loading as blank names ([#4751](https://github.com/EQEmu/Server/pull/4751)) @nytmyr 2025-03-05
|
||||||
|
|
||||||
|
### Rules
|
||||||
|
|
||||||
|
* Fix EvolvingItems:PercentOfRaidExperience Description ([#4757](https://github.com/EQEmu/Server/pull/4757)) @Kinglykrab 2025-03-07
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
* Cleanup Hand-in Tests ([#4749](https://github.com/EQEmu/Server/pull/4749)) @Akkadius 2025-03-04
|
||||||
|
|
||||||
|
### Zone
|
||||||
|
|
||||||
|
* Make zone controller less likely to be visible, immune to all forms of combat ([#4750](https://github.com/EQEmu/Server/pull/4750)) @Akkadius 2025-03-06
|
||||||
|
* State Save Improvements ([#4765](https://github.com/EQEmu/Server/pull/4765)) @Akkadius 2025-03-08
|
||||||
|
|
||||||
|
## [23.2.0] 3/3/2025
|
||||||
|
|
||||||
|
### Crash
|
||||||
|
|
||||||
|
* Database SetMutex crash fix ([#4741](https://github.com/EQEmu/Server/pull/4741)) @Akkadius 2025-03-03
|
||||||
|
* Fix Aura process crash with bots ([#4743](https://github.com/EQEmu/Server/pull/4743)) @Akkadius 2025-03-03
|
||||||
|
* Fix crash in add loot code path ([#4745](https://github.com/EQEmu/Server/pull/4745)) @Akkadius 2025-03-03
|
||||||
|
* Fix world repop crash ([#4742](https://github.com/EQEmu/Server/pull/4742)) @Akkadius 2025-03-03
|
||||||
|
* Potential crash fix in scan close mobs ([#4744](https://github.com/EQEmu/Server/pull/4744)) @Akkadius 2025-03-03
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Cleanup zone buckets on instance purge. ([#4739](https://github.com/EQEmu/Server/pull/4739)) @zimp-wow 2025-03-02
|
||||||
|
* Fix an error causing Endurance Regen to not be applied by items. ([#4738](https://github.com/EQEmu/Server/pull/4738)) @catapultam-habeo 2025-03-02
|
||||||
|
|
||||||
|
### World
|
||||||
|
|
||||||
|
* Check if port in use to avoid double booting mistakes ([#4740](https://github.com/EQEmu/Server/pull/4740)) @Akkadius 2025-03-03
|
||||||
|
|
||||||
|
## [23.1.0] 3/1/2025
|
||||||
|
|
||||||
|
### Bots
|
||||||
|
|
||||||
|
* Fix unresponsive bots in groups upon group wipe ([#4712](https://github.com/EQEmu/Server/pull/4712)) @nytmyr 2025-02-28
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
* More login <-> world code cleanup ([#4724](https://github.com/EQEmu/Server/pull/4724)) @Akkadius 2025-02-28
|
||||||
|
|
||||||
|
### Crash
|
||||||
|
|
||||||
|
* Check for directory existence before traversing in CheckForCompatibleQuestPlugins ([#4730](https://github.com/EQEmu/Server/pull/4730)) @Akkadius 2025-03-02
|
||||||
|
* Fix filesystem crash / exception in DatabaseDumpService::RemoveCredentialsFile() ([#4731](https://github.com/EQEmu/Server/pull/4731)) @Akkadius 2025-03-01
|
||||||
|
* Fix large file size crash in File::GetContents for windows ([#4735](https://github.com/EQEmu/Server/pull/4735)) @Akkadius 2025-03-02
|
||||||
|
* Fix reload concurrency crash when ran from Spire ([#4733](https://github.com/EQEmu/Server/pull/4733)) @Akkadius 2025-03-02
|
||||||
|
* Validate item in SE_SummonItemIntoBag ([#4734](https://github.com/EQEmu/Server/pull/4734)) @Akkadius 2025-03-02
|
||||||
|
* World CLI validation ([#4728](https://github.com/EQEmu/Server/pull/4728)) @Akkadius 2025-03-01
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
|
* Remove force_interactive from big bag updates ([#4727](https://github.com/EQEmu/Server/pull/4727)) @Akkadius 2025-03-01
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* Add a rule for spells to bypass stacking rules ([#4716](https://github.com/EQEmu/Server/pull/4716)) @catapultam-habeo 2025-02-28
|
||||||
|
* Evolving items Additions ([#4725](https://github.com/EQEmu/Server/pull/4725)) @neckkola 2025-03-01
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Add character_pet_name to player tables schema @Akkadius 2025-03-02
|
||||||
|
* Add client packets to questmanager:setguild ([#4732](https://github.com/EQEmu/Server/pull/4732)) @neckkola 2025-03-01
|
||||||
|
* Clear m_completed_shared_tasks before reloading @Akkadius 2025-02-24
|
||||||
|
* Fix AA Reset Error Message ([#4720](https://github.com/EQEmu/Server/pull/4720)) @Kinglykrab 2025-02-28
|
||||||
|
* Fix Issue with Suffixes/Prefixes ([#4723](https://github.com/EQEmu/Server/pull/4723)) @Kinglykrab 2025-02-28
|
||||||
|
* Fix Trading Items to Bot Pets ([#4721](https://github.com/EQEmu/Server/pull/4721)) @MortimerGreenwald 2025-02-28
|
||||||
|
* Refactor ApplyItemBonuses to fix double-counting of ATK and recommended levels not correctly applying ([#4713](https://github.com/EQEmu/Server/pull/4713)) @catapultam-habeo 2025-03-01
|
||||||
|
|
||||||
|
### Loginserver
|
||||||
|
|
||||||
|
* Minor cleanup ([#4729](https://github.com/EQEmu/Server/pull/4729)) @Akkadius 2025-03-01
|
||||||
|
|
||||||
|
### Quest API
|
||||||
|
|
||||||
|
* Add Key Ring Methods to Perl and Lua ([#4719](https://github.com/EQEmu/Server/pull/4719)) @Kinglykrab 2025-02-28
|
||||||
|
* Implement eq.handin() and quest::handin() ([#4718](https://github.com/EQEmu/Server/pull/4718)) @Akkadius 2025-02-28
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
|
||||||
|
* Extend IsTaskCompleted to also be aware of shared task completion ([#4714](https://github.com/EQEmu/Server/pull/4714)) @Akkadius 2025-02-24
|
||||||
|
|
||||||
|
### Zone
|
||||||
|
|
||||||
|
* Implement Zone State Saving on Shutdown ([#4715](https://github.com/EQEmu/Server/pull/4715)) @Akkadius 2025-02-28
|
||||||
|
|
||||||
|
### Zone State
|
||||||
|
|
||||||
|
* Wrap all serialization/deserialization in try/catch ([#4726](https://github.com/EQEmu/Server/pull/4726)) @Akkadius 2025-03-01
|
||||||
|
|
||||||
|
## [23.0.2] 2/21/2025
|
||||||
|
|
||||||
|
### Bots
|
||||||
|
|
||||||
|
* Add checks to ensure bots and pets do not engage on ^pull ([#4708](https://github.com/EQEmu/Server/pull/4708)) @nytmyr 2025-02-22
|
||||||
|
* Improve positioning ([#4709](https://github.com/EQEmu/Server/pull/4709)) @nytmyr 2025-02-22
|
||||||
|
* Prevent medding in combat if any mob has bot targeted ([#4707](https://github.com/EQEmu/Server/pull/4707)) @nytmyr 2025-02-22
|
||||||
|
|
||||||
|
### Client Mod
|
||||||
|
|
||||||
|
* Adds a hacked fast camp rule for GMs ([#4697](https://github.com/EQEmu/Server/pull/4697)) @KimLS 2025-02-20
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Fix Lua Zone ID Exports ([#4700](https://github.com/EQEmu/Server/pull/4700)) @Kinglykrab 2025-02-22
|
||||||
|
* Fix bad Mob reference in QuestManager::resumetimer() ([#4710](https://github.com/EQEmu/Server/pull/4710)) @zimp-wow 2025-02-22
|
||||||
|
* Fix cursor load on zone ([#4704](https://github.com/EQEmu/Server/pull/4704)) @nytmyr 2025-02-22
|
||||||
|
* Fix infinite loop in QuestManager::stoptimer() ([#4703](https://github.com/EQEmu/Server/pull/4703)) @zimp-wow 2025-02-21
|
||||||
|
|
||||||
|
### Quest API
|
||||||
|
|
||||||
|
* Add GetSpawn() to Perl and Lua ([#4702](https://github.com/EQEmu/Server/pull/4702)) @Kinglykrab 2025-02-22
|
||||||
|
|
||||||
## [23.0.1] 2/20/2025
|
## [23.0.1] 2/20/2025
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|||||||
@@ -671,6 +671,7 @@ SET(common_headers
|
|||||||
net/console_server_connection.h
|
net/console_server_connection.h
|
||||||
net/crc32.h
|
net/crc32.h
|
||||||
net/daybreak_connection.h
|
net/daybreak_connection.h
|
||||||
|
net/daybreak_pooling.h
|
||||||
net/daybreak_structs.h
|
net/daybreak_structs.h
|
||||||
net/dns.h
|
net/dns.h
|
||||||
net/endian.h
|
net/endian.h
|
||||||
@@ -682,6 +683,7 @@ SET(common_headers
|
|||||||
net/servertalk_server.h
|
net/servertalk_server.h
|
||||||
net/servertalk_server_connection.h
|
net/servertalk_server_connection.h
|
||||||
net/tcp_connection.h
|
net/tcp_connection.h
|
||||||
|
net/tcp_connection_pooling.h
|
||||||
net/tcp_server.h
|
net/tcp_server.h
|
||||||
net/websocket_server.h
|
net/websocket_server.h
|
||||||
net/websocket_server_connection.h
|
net/websocket_server_connection.h
|
||||||
@@ -742,6 +744,7 @@ SOURCE_GROUP(Net FILES
|
|||||||
net/crc32.h
|
net/crc32.h
|
||||||
net/daybreak_connection.cpp
|
net/daybreak_connection.cpp
|
||||||
net/daybreak_connection.h
|
net/daybreak_connection.h
|
||||||
|
net/daybreak_pooling.h
|
||||||
net/daybreak_structs.h
|
net/daybreak_structs.h
|
||||||
net/dns.h
|
net/dns.h
|
||||||
net/endian.h
|
net/endian.h
|
||||||
@@ -762,6 +765,7 @@ SOURCE_GROUP(Net FILES
|
|||||||
net/servertalk_server_connection.h
|
net/servertalk_server_connection.h
|
||||||
net/tcp_connection.cpp
|
net/tcp_connection.cpp
|
||||||
net/tcp_connection.h
|
net/tcp_connection.h
|
||||||
|
net/tcp_connection_pooling.h
|
||||||
net/tcp_server.cpp
|
net/tcp_server.cpp
|
||||||
net/tcp_server.h
|
net/tcp_server.h
|
||||||
net/websocket_server.cpp
|
net/websocket_server.cpp
|
||||||
|
|||||||
+2
-1
@@ -279,7 +279,8 @@ Bazaar::GetSearchResults(
|
|||||||
trader_items_ids,
|
trader_items_ids,
|
||||||
std::string(search.item_name),
|
std::string(search.item_name),
|
||||||
field_criteria_items,
|
field_criteria_items,
|
||||||
where_criteria_items
|
where_criteria_items,
|
||||||
|
search.max_results
|
||||||
);
|
);
|
||||||
|
|
||||||
if (item_results.empty()) {
|
if (item_results.empty()) {
|
||||||
|
|||||||
+38
-1
@@ -245,7 +245,7 @@ uint32 Database::CreateAccount(
|
|||||||
e.password = password;
|
e.password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogInfo("Account Attempting to be created: [{}:{}] status: {}", loginserver, name, status);
|
LogInfo("Account attempting to be created loginserver [{}] name [{}] status [{}]", loginserver, name, status);
|
||||||
|
|
||||||
e = AccountRepository::InsertOne(*this, e);
|
e = AccountRepository::InsertOne(*this, e);
|
||||||
|
|
||||||
@@ -955,6 +955,29 @@ bool Database::UpdateName(const std::string& old_name, const std::string& new_na
|
|||||||
return CharacterDataRepository::UpdateOne(*this, e);
|
return CharacterDataRepository::UpdateOne(*this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Database::UpdateNameByID(const int character_id, const std::string& new_name)
|
||||||
|
{
|
||||||
|
LogInfo("Renaming [{}] to [{}]", character_id, new_name);
|
||||||
|
|
||||||
|
auto l = CharacterDataRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`id` = {}",
|
||||||
|
character_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (l.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& e = l.front();
|
||||||
|
|
||||||
|
e.name = new_name;
|
||||||
|
|
||||||
|
return CharacterDataRepository::UpdateOne(*this, e);
|
||||||
|
}
|
||||||
|
|
||||||
bool Database::IsNameUsed(const std::string& name)
|
bool Database::IsNameUsed(const std::string& name)
|
||||||
{
|
{
|
||||||
if (RuleB(Bots, Enabled)) {
|
if (RuleB(Bots, Enabled)) {
|
||||||
@@ -982,6 +1005,20 @@ bool Database::IsNameUsed(const std::string& name)
|
|||||||
return !character_data.empty();
|
return !character_data.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Players cannot have the same name as a pet vanity name, or memory corruption occurs.
|
||||||
|
bool Database::IsPetNameUsed(const std::string& name)
|
||||||
|
{
|
||||||
|
const auto& pet_name_data = CharacterPetNameRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`name` = '{}'",
|
||||||
|
Strings::Escape(name)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return !pet_name_data.empty();
|
||||||
|
}
|
||||||
|
|
||||||
uint32 Database::GetServerType()
|
uint32 Database::GetServerType()
|
||||||
{
|
{
|
||||||
const auto& l = VariablesRepository::GetWhere(*this, "`varname` = 'ServerType' LIMIT 1");
|
const auto& l = VariablesRepository::GetWhere(*this, "`varname` = 'ServerType' LIMIT 1");
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ public:
|
|||||||
bool ReserveName(uint32 account_id, const std::string& name);
|
bool ReserveName(uint32 account_id, const std::string& name);
|
||||||
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp);
|
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp);
|
||||||
bool UpdateName(const std::string& old_name, const std::string& new_name);
|
bool UpdateName(const std::string& old_name, const std::string& new_name);
|
||||||
|
bool UpdateNameByID(const int character_id, const std::string& new_name);
|
||||||
bool CopyCharacter(
|
bool CopyCharacter(
|
||||||
const std::string& source_character_name,
|
const std::string& source_character_name,
|
||||||
const std::string& destination_character_name,
|
const std::string& destination_character_name,
|
||||||
@@ -116,6 +117,7 @@ public:
|
|||||||
bool CheckGMIPs(const std::string& login_ip, uint32 account_id);
|
bool CheckGMIPs(const std::string& login_ip, uint32 account_id);
|
||||||
bool CheckNameFilter(const std::string& name, bool surname = false);
|
bool CheckNameFilter(const std::string& name, bool surname = false);
|
||||||
bool IsNameUsed(const std::string& name);
|
bool IsNameUsed(const std::string& name);
|
||||||
|
bool IsPetNameUsed(const std::string& name);
|
||||||
|
|
||||||
uint32 GetAccountIDByChar(const std::string& name, uint32* character_id = 0);
|
uint32 GetAccountIDByChar(const std::string& name, uint32* character_id = 0);
|
||||||
uint32 GetAccountIDByChar(uint32 character_id);
|
uint32 GetAccountIDByChar(uint32 character_id);
|
||||||
@@ -139,6 +141,7 @@ public:
|
|||||||
bool CheckInstanceExpired(uint16 instance_id);
|
bool CheckInstanceExpired(uint16 instance_id);
|
||||||
bool CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration);
|
bool CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration);
|
||||||
bool GetUnusedInstanceID(uint16& instance_id);
|
bool GetUnusedInstanceID(uint16& instance_id);
|
||||||
|
bool TryGetUnusedInstanceID(uint16& instance_id);
|
||||||
bool IsGlobalInstance(uint16 instance_id);
|
bool IsGlobalInstance(uint16 instance_id);
|
||||||
bool RemoveClientFromInstance(uint16 instance_id, uint32 char_id);
|
bool RemoveClientFromInstance(uint16 instance_id, uint32 char_id);
|
||||||
bool RemoveClientsFromInstance(uint16 instance_id);
|
bool RemoveClientsFromInstance(uint16 instance_id);
|
||||||
|
|||||||
@@ -596,7 +596,12 @@ void DatabaseDumpService::BuildCredentialsFile()
|
|||||||
void DatabaseDumpService::RemoveCredentialsFile()
|
void DatabaseDumpService::RemoveCredentialsFile()
|
||||||
{
|
{
|
||||||
if (File::Exists(CREDENTIALS_FILE)) {
|
if (File::Exists(CREDENTIALS_FILE)) {
|
||||||
std::filesystem::remove(CREDENTIALS_FILE);
|
try {
|
||||||
|
std::filesystem::remove(CREDENTIALS_FILE);
|
||||||
|
}
|
||||||
|
catch (std::exception &e) {
|
||||||
|
LogError("std::filesystem::remove err [{}]", e.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ private:
|
|||||||
bool dump_system_tables = false;
|
bool dump_system_tables = false;
|
||||||
bool dump_content_tables = false;
|
bool dump_content_tables = false;
|
||||||
bool dump_player_tables = false;
|
bool dump_player_tables = false;
|
||||||
bool dump_query_server_tables = false;
|
|
||||||
bool dump_login_server_tables = false;
|
bool dump_login_server_tables = false;
|
||||||
bool dump_with_no_data = false;
|
bool dump_with_no_data = false;
|
||||||
bool dump_table_lock = false;
|
bool dump_table_lock = false;
|
||||||
|
|||||||
@@ -6417,7 +6417,7 @@ ADD COLUMN `guid` bigint(20) UNSIGNED NOT NULL DEFAULT 0 AFTER `ornament_hero_mo
|
|||||||
ADD PRIMARY KEY (`account_id`, `slot_id`);
|
ADD PRIMARY KEY (`account_id`, `slot_id`);
|
||||||
)",
|
)",
|
||||||
.content_schema_update = false,
|
.content_schema_update = false,
|
||||||
.force_interactive = true
|
.force_interactive = false
|
||||||
},
|
},
|
||||||
ManifestEntry{
|
ManifestEntry{
|
||||||
.version = 9298,
|
.version = 9298,
|
||||||
@@ -6481,7 +6481,7 @@ UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id`
|
|||||||
UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2541) + 11210) WHERE `slot_id` BETWEEN 2541 AND 2550; -- Shared Bank Bag 2
|
UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2541) + 11210) WHERE `slot_id` BETWEEN 2541 AND 2550; -- Shared Bank Bag 2
|
||||||
)",
|
)",
|
||||||
.content_schema_update = false,
|
.content_schema_update = false,
|
||||||
.force_interactive = true
|
.force_interactive = false
|
||||||
},
|
},
|
||||||
ManifestEntry{
|
ManifestEntry{
|
||||||
.version = 9299,
|
.version = 9299,
|
||||||
@@ -6792,7 +6792,7 @@ UPDATE `character_corpse_items` SET `equip_slot` = ((`equip_slot` - 341) + 5810)
|
|||||||
},
|
},
|
||||||
ManifestEntry{
|
ManifestEntry{
|
||||||
.version = 9304,
|
.version = 9304,
|
||||||
.description = "2024_12_01_2024_update_guild_bank",
|
.description = "2024_12_01_update_guild_bank",
|
||||||
.check = "SHOW COLUMNS FROM `guild_bank` LIKE 'augment_one_id'",
|
.check = "SHOW COLUMNS FROM `guild_bank` LIKE 'augment_one_id'",
|
||||||
.condition = "empty",
|
.condition = "empty",
|
||||||
.match = "",
|
.match = "",
|
||||||
@@ -6845,7 +6845,7 @@ RENAME TABLE `expedition_lockouts` TO `dynamic_zone_lockouts`;
|
|||||||
.condition = "empty",
|
.condition = "empty",
|
||||||
.match = "",
|
.match = "",
|
||||||
.sql = R"(
|
.sql = R"(
|
||||||
-- ✅ Drop old indexes
|
-- Drop old indexes if exists
|
||||||
DROP INDEX IF EXISTS `keys` ON `data_buckets`;
|
DROP INDEX IF EXISTS `keys` ON `data_buckets`;
|
||||||
DROP INDEX IF EXISTS `idx_npc_expires` ON `data_buckets`;
|
DROP INDEX IF EXISTS `idx_npc_expires` ON `data_buckets`;
|
||||||
DROP INDEX IF EXISTS `idx_bot_expires` ON `data_buckets`;
|
DROP INDEX IF EXISTS `idx_bot_expires` ON `data_buckets`;
|
||||||
@@ -6863,11 +6863,227 @@ ALTER TABLE `data_buckets`
|
|||||||
MODIFY COLUMN `npc_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `character_id`,
|
MODIFY COLUMN `npc_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `character_id`,
|
||||||
MODIFY COLUMN `bot_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `npc_id`;
|
MODIFY COLUMN `bot_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `npc_id`;
|
||||||
|
|
||||||
-- ✅ Create optimized unique index with `key` first
|
-- Create optimized unique index with `key` first
|
||||||
CREATE UNIQUE INDEX `keys` ON data_buckets (`key`, character_id, npc_id, bot_id, account_id, zone_id, instance_id);
|
CREATE UNIQUE INDEX `keys` ON data_buckets (`key`, character_id, npc_id, bot_id, account_id, zone_id, instance_id);
|
||||||
|
|
||||||
-- ✅ Create indexes for just instance_id (instance deletion)
|
-- Create indexes for just instance_id (instance deletion)
|
||||||
CREATE INDEX idx_instance_id ON data_buckets (instance_id);
|
CREATE INDEX idx_instance_id ON data_buckets (instance_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9307,
|
||||||
|
.description = "2025_02_17_zone_state_spawns.sql",
|
||||||
|
.check = "SHOW TABLES LIKE 'zone_state_spawns'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
CREATE TABLE `zone_state_spawns` (
|
||||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
`zone_id` int(11) unsigned DEFAULT NULL,
|
||||||
|
`instance_id` int(11) unsigned DEFAULT NULL,
|
||||||
|
`is_corpse` tinyint(11) DEFAULT 0,
|
||||||
|
`decay_in_seconds` int(11) DEFAULT 0,
|
||||||
|
`npc_id` int(10) unsigned DEFAULT NULL,
|
||||||
|
`spawn2_id` int(10) unsigned NOT NULL,
|
||||||
|
`spawngroup_id` int(10) unsigned NOT NULL,
|
||||||
|
`x` float NOT NULL,
|
||||||
|
`y` float NOT NULL,
|
||||||
|
`z` float NOT NULL,
|
||||||
|
`heading` float NOT NULL,
|
||||||
|
`respawn_time` int(10) unsigned NOT NULL,
|
||||||
|
`variance` int(10) unsigned NOT NULL,
|
||||||
|
`grid` int(10) unsigned DEFAULT 0,
|
||||||
|
`current_waypoint` int(11) DEFAULT 0,
|
||||||
|
`path_when_zone_idle` smallint(6) DEFAULT 0,
|
||||||
|
`condition_id` smallint(5) unsigned DEFAULT 0,
|
||||||
|
`condition_min_value` smallint(6) DEFAULT 0,
|
||||||
|
`enabled` smallint(6) DEFAULT 1,
|
||||||
|
`anim` smallint(5) unsigned DEFAULT 0,
|
||||||
|
`loot_data` text DEFAULT NULL,
|
||||||
|
`entity_variables` text DEFAULT NULL,
|
||||||
|
`buffs` text DEFAULT NULL,
|
||||||
|
`hp` bigint(20) DEFAULT 0,
|
||||||
|
`mana` bigint(20) DEFAULT 0,
|
||||||
|
`endurance` bigint(20) DEFAULT 0,
|
||||||
|
`created_at` datetime DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9308,
|
||||||
|
.description = "2025_03_29_add_multivalue_support_to_evolving_subtype.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `items_evolving_details` LIKE 'sub_type'",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "varchar(200)",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `items_evolving_details`
|
||||||
|
CHANGE COLUMN `sub_type` `sub_type` VARCHAR(200) NULL DEFAULT '0' AFTER `type`;
|
||||||
|
)",
|
||||||
|
.content_schema_update = true
|
||||||
|
},
|
||||||
|
// this one got missed being added to PEQ dumps so adding it again so it gets added when folks take a new release
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9309,
|
||||||
|
.description = "2025_03_1_create_pet_names_table_if_not_exist.sql",
|
||||||
|
.check = "SHOW TABLES LIKE 'character_pet_name'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
CREATE TABLE `character_pet_name` (
|
||||||
|
`character_id` INT(11) NOT NULL PRIMARY KEY,
|
||||||
|
`name` VARCHAR(64) NOT NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
)",
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9310,
|
||||||
|
.description = "2025_03_7_expand_horse_def.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `horses` LIKE 'helmtexture'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `horses`
|
||||||
|
ADD COLUMN `helmtexture` TINYINT(2) NOT NULL DEFAULT -1 AFTER `texture`;
|
||||||
|
)",
|
||||||
|
.content_schema_update = true
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9311,
|
||||||
|
.description = "2025_03_09_add_zone_state_is_zone_field.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `zone_state_spawns` LIKE 'is_zone'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `zone_state_spawns`
|
||||||
|
ADD COLUMN `is_zone` tinyint(11) NULL DEFAULT 0 AFTER `is_corpse`;
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9312,
|
||||||
|
.description = "2025_03_11_data_bucket_indexes.sql",
|
||||||
|
.check = "SHOW INDEX FROM data_buckets",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_zone_instance_expires",
|
||||||
|
.sql = R"(
|
||||||
|
DROP INDEX IF EXISTS `idx_zone_instance_expires` ON `data_buckets`;
|
||||||
|
DROP INDEX IF EXISTS `idx_character_expires` ON `data_buckets`;
|
||||||
|
DROP INDEX IF EXISTS `idx_bot_expires` ON `data_buckets`;
|
||||||
|
ALTER TABLE data_buckets ADD INDEX idx_zone_instance_expires (zone_id, instance_id, expires);
|
||||||
|
ALTER TABLE data_buckets ADD INDEX idx_character_expires (character_id, expires);
|
||||||
|
ALTER TABLE data_buckets ADD INDEX idx_bot_expires (bot_id, expires);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9313,
|
||||||
|
.description = "2025_03_11_zone_state_spawns.sql",
|
||||||
|
.check = "SHOW INDEX FROM zone_state_spawns",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_zone_instance",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE zone_state_spawns ADD INDEX idx_zone_instance (zone_id, instance_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9314,
|
||||||
|
.description = "2025_03_12_zone_state_spawns_one_time_truncate.sql",
|
||||||
|
.check = "SELECT * FROM db_version WHERE version >= 9314",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
TRUNCATE TABLE zone_state_spawns;
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9315,
|
||||||
|
.description = "2025_03_29_character_tribute_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM character_tribute",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_character_id",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE character_tribute ADD INDEX idx_character_id (character_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9316,
|
||||||
|
.description = "2025_03_29_player_titlesets_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM player_titlesets",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_char_id",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE player_titlesets ADD INDEX idx_char_id (char_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9317,
|
||||||
|
.description = "2025_03_29_respawn_times_instance_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM respawn_times",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_instance_id",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE respawn_times ADD INDEX idx_instance_id (instance_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9318,
|
||||||
|
.description = "2025_03_29_zone_state_spawns_instance_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM zone_state_spawns",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_instance_id",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE zone_state_spawns ADD INDEX idx_instance_id (instance_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9319,
|
||||||
|
.description = "2025_03_29_data_buckets_expires_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM data_buckets",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_expires",
|
||||||
|
.sql = R"(
|
||||||
|
CREATE INDEX idx_expires ON data_buckets (expires);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9320,
|
||||||
|
.description = "2025_03_23_add_respawn_times_expire_at.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `respawn_times` LIKE 'expire_at'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `respawn_times`
|
||||||
|
ADD COLUMN `expire_at` int(11) UNSIGNED NULL DEFAULT 0 AFTER `duration`;
|
||||||
|
|
||||||
|
UPDATE respawn_times set expire_at = `start` + `duration`; -- backfill existing data
|
||||||
|
|
||||||
|
CREATE INDEX `idx_expire_at` ON `respawn_times` (`expire_at`);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9321,
|
||||||
|
.description = "2025_03_30_instance_list_add_expire_at.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `instance_list` LIKE 'expire_at'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `instance_list`
|
||||||
|
ADD COLUMN `expire_at` bigint(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `duration`;
|
||||||
|
|
||||||
|
UPDATE instance_list set expire_at = `start_time` + `duration`; -- backfill existing data
|
||||||
|
|
||||||
|
CREATE INDEX `idx_expire_at` ON `instance_list` (`expire_at`);
|
||||||
)",
|
)",
|
||||||
.content_schema_update = false
|
.content_schema_update = false
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "../common/repositories/spawn_condition_values_repository.h"
|
#include "../common/repositories/spawn_condition_values_repository.h"
|
||||||
#include "repositories/spawn2_disabled_repository.h"
|
#include "repositories/spawn2_disabled_repository.h"
|
||||||
#include "repositories/data_buckets_repository.h"
|
#include "repositories/data_buckets_repository.h"
|
||||||
|
#include "repositories/zone_state_spawns_repository.h"
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
@@ -128,11 +128,35 @@ bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version
|
|||||||
e.version = version;
|
e.version = version;
|
||||||
e.start_time = std::time(nullptr);
|
e.start_time = std::time(nullptr);
|
||||||
e.duration = duration;
|
e.duration = duration;
|
||||||
|
e.expire_at = e.start_time + duration;
|
||||||
|
|
||||||
return InstanceListRepository::InsertOne(*this, e).id;
|
RespawnTimesRepository::ClearInstanceTimers(*this, e.id);
|
||||||
|
InstanceListRepository::ReplaceOne(*this, e);
|
||||||
|
return instance_id > 0 && e.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Database::GetUnusedInstanceID(uint16 &instance_id)
|
bool Database::GetUnusedInstanceID(uint16 &instance_id)
|
||||||
|
{
|
||||||
|
// attempt to get an unused instance id
|
||||||
|
for (int a = 0; a < 10; a++) {
|
||||||
|
uint16 attempted_id = 0;
|
||||||
|
if (TryGetUnusedInstanceID(attempted_id)) {
|
||||||
|
auto i = InstanceListRepository::NewEntity();
|
||||||
|
i.id = attempted_id;
|
||||||
|
i.notes = "Prefetching";
|
||||||
|
auto n = InstanceListRepository::InsertOne(*this, i);
|
||||||
|
if (n.id > 0) {
|
||||||
|
instance_id = n.id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instance_id = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::TryGetUnusedInstanceID(uint16 &instance_id)
|
||||||
{
|
{
|
||||||
uint32 max_reserved_instance_id = RuleI(Instances, ReservedInstances);
|
uint32 max_reserved_instance_id = RuleI(Instances, ReservedInstances);
|
||||||
uint32 max_instance_id = 32000;
|
uint32 max_instance_id = 32000;
|
||||||
@@ -480,6 +504,9 @@ void Database::DeleteInstance(uint16 instance_id)
|
|||||||
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
|
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
|
||||||
CharacterCorpsesRepository::BuryInstance(*this, instance_id);
|
CharacterCorpsesRepository::BuryInstance(*this, instance_id);
|
||||||
DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
|
DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
|
||||||
|
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||||
|
ZoneStateSpawnsRepository::DeleteWhere(*this, fmt::format("`instance_id` = {}", instance_id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 group_id)
|
void Database::FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 group_id)
|
||||||
@@ -534,14 +561,12 @@ void Database::GetCharactersInInstance(uint16 instance_id, std::list<uint32> &ch
|
|||||||
|
|
||||||
void Database::PurgeExpiredInstances()
|
void Database::PurgeExpiredInstances()
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Delay purging by a day so that we can continue using adjacent free instance id's
|
|
||||||
* from the table without risking the chance we immediately re-allocate a zone that freshly expired but
|
|
||||||
* has not been fully de-allocated
|
|
||||||
*/
|
|
||||||
auto l = InstanceListRepository::GetWhere(
|
auto l = InstanceListRepository::GetWhere(
|
||||||
*this,
|
*this,
|
||||||
"(start_time + duration) <= (UNIX_TIMESTAMP() - 86400) AND never_expires = 0"
|
fmt::format(
|
||||||
|
"expire_at <= (UNIX_TIMESTAMP() - {}) and expire_at != 0 AND never_expires = 0",
|
||||||
|
RuleI(Instances, ExpireOffsetTimeSeconds)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
if (l.empty()) {
|
if (l.empty()) {
|
||||||
return;
|
return;
|
||||||
@@ -552,16 +577,24 @@ void Database::PurgeExpiredInstances()
|
|||||||
instance_ids.emplace_back(std::to_string(e.id));
|
instance_ids.emplace_back(std::to_string(e.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto imploded_instance_ids = Strings::Implode(",", instance_ids);
|
const auto ids = Strings::Implode(",", instance_ids);
|
||||||
|
|
||||||
InstanceListRepository::DeleteWhere(*this, fmt::format("id IN ({})", imploded_instance_ids));
|
TransactionBegin();
|
||||||
InstanceListPlayerRepository::DeleteWhere(*this, fmt::format("id IN ({})", imploded_instance_ids));
|
InstanceListRepository::DeleteWhere(*this, fmt::format("id IN ({})", ids));
|
||||||
RespawnTimesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
InstanceListPlayerRepository::DeleteWhere(*this, fmt::format("id IN ({})", ids));
|
||||||
SpawnConditionValuesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
RespawnTimesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
|
||||||
CharacterCorpsesRepository::BuryInstances(*this, imploded_instance_ids);
|
SpawnConditionValuesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
|
||||||
DynamicZoneMembersRepository::DeleteByManyInstances(*this, imploded_instance_ids);
|
CharacterCorpsesRepository::BuryInstances(*this, ids);
|
||||||
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
DynamicZoneMembersRepository::DeleteByManyInstances(*this, ids);
|
||||||
Spawn2DisabledRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
|
||||||
|
Spawn2DisabledRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
|
||||||
|
DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id != 0 and instance_id IN ({})", ids));
|
||||||
|
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||||
|
ZoneStateSpawnsRepository::DeleteWhere(*this, fmt::format("`instance_id` IN ({})", ids));
|
||||||
|
}
|
||||||
|
TransactionCommit();
|
||||||
|
|
||||||
|
LogInfo("Purged [{}] expired instances", l.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
|
void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
|
||||||
@@ -573,6 +606,7 @@ void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
|
|||||||
|
|
||||||
i.start_time = std::time(nullptr);
|
i.start_time = std::time(nullptr);
|
||||||
i.duration = new_duration;
|
i.duration = new_duration;
|
||||||
|
i.expire_at = i.start_time + i.duration;
|
||||||
|
|
||||||
InstanceListRepository::UpdateOne(*this, i);
|
InstanceListRepository::UpdateOne(*this, i);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ namespace DatabaseSchema {
|
|||||||
"character_pet_buffs",
|
"character_pet_buffs",
|
||||||
"character_pet_info",
|
"character_pet_info",
|
||||||
"character_pet_inventory",
|
"character_pet_inventory",
|
||||||
|
"character_pet_name",
|
||||||
"character_peqzone_flags",
|
"character_peqzone_flags",
|
||||||
"character_potionbelt",
|
"character_potionbelt",
|
||||||
"character_skills",
|
"character_skills",
|
||||||
@@ -350,6 +351,7 @@ namespace DatabaseSchema {
|
|||||||
"shared_task_dynamic_zones",
|
"shared_task_dynamic_zones",
|
||||||
"shared_task_members",
|
"shared_task_members",
|
||||||
"shared_tasks",
|
"shared_tasks",
|
||||||
|
"zone_state_spawns",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -302,7 +302,9 @@ std::string DBcore::Escape(const std::string& s)
|
|||||||
|
|
||||||
void DBcore::SetMutex(Mutex *mutex)
|
void DBcore::SetMutex(Mutex *mutex)
|
||||||
{
|
{
|
||||||
safe_delete(m_mutex);
|
if (m_mutex && m_mutex != mutex) {
|
||||||
|
safe_delete(m_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
DBcore::m_mutex = mutex;
|
DBcore::m_mutex = mutex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,15 +58,16 @@ uint32_t DynamicZoneBase::CreateInstance()
|
|||||||
insert_instance.start_time = static_cast<int>(std::chrono::system_clock::to_time_t(m_start_time));
|
insert_instance.start_time = static_cast<int>(std::chrono::system_clock::to_time_t(m_start_time));
|
||||||
insert_instance.duration = static_cast<int>(m_duration.count());
|
insert_instance.duration = static_cast<int>(m_duration.count());
|
||||||
insert_instance.never_expires = m_never_expires;
|
insert_instance.never_expires = m_never_expires;
|
||||||
|
insert_instance.expire_at = insert_instance.start_time + insert_instance.duration;
|
||||||
|
|
||||||
auto instance = InstanceListRepository::InsertOne(GetDatabase(), insert_instance);
|
auto instance = InstanceListRepository::ReplaceOne(GetDatabase(), insert_instance);
|
||||||
if (instance.id == 0)
|
if (!instance)
|
||||||
{
|
{
|
||||||
LogDynamicZones("Failed to create instance [{}] for zone [{}]", unused_instance_id, m_zone_id);
|
LogDynamicZones("Failed to create instance [{}] for zone [{}]", unused_instance_id, m_zone_id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_instance_id = instance.id;
|
m_instance_id = unused_instance_id;
|
||||||
|
|
||||||
return m_instance_id;
|
return m_instance_id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -287,6 +287,8 @@ N(OP_InstillDoubt),
|
|||||||
N(OP_InterruptCast),
|
N(OP_InterruptCast),
|
||||||
N(OP_InvokeChangePetName),
|
N(OP_InvokeChangePetName),
|
||||||
N(OP_InvokeChangePetNameImmediate),
|
N(OP_InvokeChangePetNameImmediate),
|
||||||
|
N(OP_InvokeNameChangeImmediate),
|
||||||
|
N(OP_InvokeNameChangeLazy),
|
||||||
N(OP_ItemLinkClick),
|
N(OP_ItemLinkClick),
|
||||||
N(OP_ItemLinkResponse),
|
N(OP_ItemLinkResponse),
|
||||||
N(OP_ItemLinkText),
|
N(OP_ItemLinkText),
|
||||||
|
|||||||
+17
-10
@@ -47,7 +47,7 @@ static const uint32 ADVANCED_LORE_LENGTH = 8192;
|
|||||||
*/
|
*/
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
|
|
||||||
struct LoginInfo_Struct {
|
struct LoginInfo {
|
||||||
/*000*/ char login_info[64];
|
/*000*/ char login_info[64];
|
||||||
/*064*/ uint8 unknown064[124];
|
/*064*/ uint8 unknown064[124];
|
||||||
/*188*/ uint8 zoning; // 01 if zoning, 00 if not
|
/*188*/ uint8 zoning; // 01 if zoning, 00 if not
|
||||||
@@ -5832,21 +5832,28 @@ struct ChangeSize_Struct
|
|||||||
/*16*/
|
/*16*/
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ChangeNameResponse : int {
|
||||||
|
Denied = 0, // 5167: "You have requested an invalid name or a Customer Service Representative has denied your name request. Please try another name."
|
||||||
|
Accepted = 1, // 5976: "Your request for a name change was successful."
|
||||||
|
Timeout = -1, // 5977: "Your request for a name change has timed out. Please try again later."
|
||||||
|
ServerError = -2, // 5978: "The server had an error while processing your name request. Please try again later."
|
||||||
|
RateLimited = -3, // 5979: "You must wait longer before submitting another name request. Please try again in a few minutes."
|
||||||
|
Ineligible = -4, // 5980: "Your character is not eligible for a name change."
|
||||||
|
Pending = -5 // 5193: "You already have a name change pending. Please wait until it is fully processed before attempting another name change."
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AltChangeName_Struct {
|
||||||
|
/*00*/ char new_name[64];
|
||||||
|
/*40*/ char old_name[64];
|
||||||
|
/*80*/ int response_code;
|
||||||
|
};
|
||||||
|
|
||||||
struct ChangePetName_Struct {
|
struct ChangePetName_Struct {
|
||||||
/*00*/ char new_pet_name[64];
|
/*00*/ char new_pet_name[64];
|
||||||
/*40*/ char pet_owner_name[64];
|
/*40*/ char pet_owner_name[64];
|
||||||
/*80*/ int response_code;
|
/*80*/ int response_code;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ChangePetNameResponse : int {
|
|
||||||
Denied = 0, // 5167 You have requested an invalid name or a Customer Service Representative has denied your name request. Please try another name.
|
|
||||||
Accepted = 1, // 5976 Your request for a name change was successful.
|
|
||||||
Timeout = -3, // 5979 You must wait longer before submitting another name request. Please try again in a few minutes.
|
|
||||||
NotEligible = -4, // 5980 Your character is not eligible for a name change.
|
|
||||||
Pending = -5, // 5193 You already have a name change pending. Please wait until it is fully processed before attempting another name change.
|
|
||||||
Unhandled = -1
|
|
||||||
};
|
|
||||||
|
|
||||||
// New OpCode/Struct for SoD+
|
// New OpCode/Struct for SoD+
|
||||||
struct GroupMakeLeader_Struct
|
struct GroupMakeLeader_Struct
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -614,7 +614,7 @@ void EQEmuLogSys::EnableConsoleLogging()
|
|||||||
std::copy(std::begin(pre_silence_settings), std::end(pre_silence_settings), std::begin(log_settings));
|
std::copy(std::begin(pre_silence_settings), std::end(pre_silence_settings), std::begin(log_settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings(bool silent_load)
|
||||||
{
|
{
|
||||||
InjectTablesIfNotExist();
|
InjectTablesIfNotExist();
|
||||||
|
|
||||||
@@ -699,6 +699,10 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (silent_load) {
|
||||||
|
SilenceConsoleLogging();
|
||||||
|
}
|
||||||
|
|
||||||
LogInfo("Loaded [{}] log categories", categories.size());
|
LogInfo("Loaded [{}] log categories", categories.size());
|
||||||
|
|
||||||
auto webhooks = DiscordWebhooksRepository::GetWhere(*m_database, fmt::format("id < {}", MAX_DISCORD_WEBHOOK_ID));
|
auto webhooks = DiscordWebhooksRepository::GetWhere(*m_database, fmt::format("id < {}", MAX_DISCORD_WEBHOOK_ID));
|
||||||
@@ -716,6 +720,10 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
|||||||
log_settings[Logs::Info].log_to_file = static_cast<uint8>(Logs::General);
|
log_settings[Logs::Info].log_to_file = static_cast<uint8>(Logs::General);
|
||||||
log_settings[Logs::Info].log_to_console = static_cast<uint8>(Logs::General);
|
log_settings[Logs::Info].log_to_console = static_cast<uint8>(Logs::General);
|
||||||
|
|
||||||
|
if (silent_load) {
|
||||||
|
SilenceConsoleLogging();
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-4
@@ -74,7 +74,7 @@ namespace Logs {
|
|||||||
Spawns,
|
Spawns,
|
||||||
Spells,
|
Spells,
|
||||||
Status, // deprecated
|
Status, // deprecated
|
||||||
TCPConnection,
|
TCPConnection, // deprecated
|
||||||
Tasks,
|
Tasks,
|
||||||
Tradeskills,
|
Tradeskills,
|
||||||
Trading,
|
Trading,
|
||||||
@@ -149,6 +149,9 @@ namespace Logs {
|
|||||||
BotSpellChecks,
|
BotSpellChecks,
|
||||||
BotSpellTypeChecks,
|
BotSpellTypeChecks,
|
||||||
NpcHandin,
|
NpcHandin,
|
||||||
|
ZoneState,
|
||||||
|
NetClient,
|
||||||
|
NetTCP,
|
||||||
MaxCategoryID /* Don't Remove this */
|
MaxCategoryID /* Don't Remove this */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -182,7 +185,7 @@ namespace Logs {
|
|||||||
"Spawns",
|
"Spawns",
|
||||||
"Spells",
|
"Spells",
|
||||||
"Status (Deprecated)",
|
"Status (Deprecated)",
|
||||||
"TCP Connection",
|
"TCP Connection (Deprecated)",
|
||||||
"Tasks",
|
"Tasks",
|
||||||
"Tradeskills",
|
"Tradeskills",
|
||||||
"Trading",
|
"Trading",
|
||||||
@@ -256,7 +259,10 @@ namespace Logs {
|
|||||||
"Bot Settings",
|
"Bot Settings",
|
||||||
"Bot Spell Checks",
|
"Bot Spell Checks",
|
||||||
"Bot Spell Type Checks",
|
"Bot Spell Type Checks",
|
||||||
"NpcHandin"
|
"NpcHandin",
|
||||||
|
"ZoneState",
|
||||||
|
"Net Server <-> Client",
|
||||||
|
"Net TCP"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,7 +283,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
void CloseFileLogs();
|
void CloseFileLogs();
|
||||||
EQEmuLogSys *LoadLogSettingsDefaults();
|
EQEmuLogSys *LoadLogSettingsDefaults();
|
||||||
EQEmuLogSys *LoadLogDatabaseSettings();
|
EQEmuLogSys *LoadLogDatabaseSettings(bool silent_load = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param directory_name
|
* @param directory_name
|
||||||
|
|||||||
@@ -261,26 +261,6 @@
|
|||||||
OutF(LogSys, Logs::Detail, Logs::Spells, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
OutF(LogSys, Logs::Detail, Logs::Spells, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define LogStatus(message, ...) do {\
|
|
||||||
if (LogSys.IsLogEnabled(Logs::General, Logs::Status))\
|
|
||||||
OutF(LogSys, Logs::General, Logs::Status, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LogStatusDetail(message, ...) do {\
|
|
||||||
if (LogSys.IsLogEnabled(Logs::Detail, Logs::Status))\
|
|
||||||
OutF(LogSys, Logs::Detail, Logs::Status, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LogTCPConnection(message, ...) do {\
|
|
||||||
if (LogSys.IsLogEnabled(Logs::General, Logs::TCPConnection))\
|
|
||||||
OutF(LogSys, Logs::General, Logs::TCPConnection, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LogTCPConnectionDetail(message, ...) do {\
|
|
||||||
if (LogSys.IsLogEnabled(Logs::Detail, Logs::TCPConnection))\
|
|
||||||
OutF(LogSys, Logs::Detail, Logs::TCPConnection, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define LogTasks(message, ...) do {\
|
#define LogTasks(message, ...) do {\
|
||||||
if (LogSys.IsLogEnabled(Logs::General, Logs::Tasks))\
|
if (LogSys.IsLogEnabled(Logs::General, Logs::Tasks))\
|
||||||
OutF(LogSys, Logs::General, Logs::Tasks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
OutF(LogSys, Logs::General, Logs::Tasks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
@@ -914,6 +894,36 @@
|
|||||||
OutF(LogSys, Logs::Detail, Logs::NpcHandin, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
OutF(LogSys, Logs::Detail, Logs::NpcHandin, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define LogZoneState(message, ...) do {\
|
||||||
|
if (LogSys.IsLogEnabled(Logs::General, Logs::ZoneState))\
|
||||||
|
OutF(LogSys, Logs::General, Logs::ZoneState, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LogZoneStateDetail(message, ...) do {\
|
||||||
|
if (LogSys.IsLogEnabled(Logs::Detail, Logs::ZoneState))\
|
||||||
|
OutF(LogSys, Logs::Detail, Logs::ZoneState, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LogNetClient(message, ...) do {\
|
||||||
|
if (LogSys.IsLogEnabled(Logs::General, Logs::NetClient))\
|
||||||
|
OutF(LogSys, Logs::General, Logs::NetClient, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LogNetClientDetail(message, ...) do {\
|
||||||
|
if (LogSys.IsLogEnabled(Logs::Detail, Logs::NetClient))\
|
||||||
|
OutF(LogSys, Logs::Detail, Logs::NetClient, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LogNetTCP(message, ...) do {\
|
||||||
|
if (LogSys.IsLogEnabled(Logs::General, Logs::NetTCP))\
|
||||||
|
OutF(LogSys, Logs::General, Logs::NetTCP, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LogNetTCPDetail(message, ...) do {\
|
||||||
|
if (LogSys.IsLogEnabled(Logs::Detail, Logs::NetTCP))\
|
||||||
|
OutF(LogSys, Logs::Detail, Logs::NetTCP, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define Log(debug_level, log_category, message, ...) do {\
|
#define Log(debug_level, log_category, message, ...) do {\
|
||||||
if (LogSys.IsLogEnabled(debug_level, log_category))\
|
if (LogSys.IsLogEnabled(debug_level, log_category))\
|
||||||
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
|||||||
@@ -181,9 +181,17 @@ void PlayerEventLogs::ProcessBatchQueue()
|
|||||||
|
|
||||||
// Helper to deserialize event data
|
// Helper to deserialize event data
|
||||||
auto Deserialize = [](const std::string &data, auto &out) {
|
auto Deserialize = [](const std::string &data, auto &out) {
|
||||||
std::stringstream ss(data);
|
if (!Strings::IsValidJson(data)) {
|
||||||
cereal::JSONInputArchive ar(ss);
|
return;
|
||||||
out.serialize(ar);
|
}
|
||||||
|
|
||||||
|
// cpp exceptions are terrible, don't ever use them
|
||||||
|
try {
|
||||||
|
std::stringstream ss(data);
|
||||||
|
cereal::JSONInputArchive ar(ss);
|
||||||
|
out.serialize(ar);
|
||||||
|
}
|
||||||
|
catch (const std::exception &e) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper to assign ETL table ID
|
// Helper to assign ETL table ID
|
||||||
|
|||||||
+12
-13
@@ -39,6 +39,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
@@ -90,23 +91,21 @@ std::string File::GetCwd()
|
|||||||
|
|
||||||
FileContentsResult File::GetContents(const std::string &file_name)
|
FileContentsResult File::GetContents(const std::string &file_name)
|
||||||
{
|
{
|
||||||
std::string error;
|
std::ifstream f(file_name, std::ios::in | std::ios::binary);
|
||||||
std::ifstream f;
|
if (!f) {
|
||||||
f.open(file_name);
|
return { .error = fmt::format("Couldn't open file [{}]", file_name) };
|
||||||
std::string line;
|
|
||||||
std::string lines;
|
|
||||||
if (f.is_open()) {
|
|
||||||
while (f) {
|
|
||||||
std::getline(f, line);
|
|
||||||
lines += line + "\n";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
error = fmt::format("Couldn't open file [{}]", file_name);
|
constexpr size_t CHUNK_SIZE = 4096; // Read 4KB chunks
|
||||||
|
std::string lines;
|
||||||
|
std::vector<char> buffer(CHUNK_SIZE);
|
||||||
|
|
||||||
|
while (f.read(buffer.data(), CHUNK_SIZE) || f.gcount() > 0) {
|
||||||
|
lines.append(buffer.data(), f.gcount());
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileContentsResult{
|
return FileContentsResult{
|
||||||
.contents = lines,
|
.contents = lines,
|
||||||
.error = error,
|
.error = {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,3 +259,84 @@ bool IpUtil::IsIPAddress(const std::string &ip_address)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#pragma comment(lib, "ws2_32.lib") // Link against Winsock library
|
||||||
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h> // For inet_pton
|
||||||
|
#pragma comment(lib, "ws2_32.lib") // Link against Winsock library
|
||||||
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h> // For inet_pton
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool IpUtil::IsPortInUse(const std::string& ip, int port) {
|
||||||
|
bool in_use = false;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSADATA wsaData;
|
||||||
|
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||||
|
std::cerr << "WSAStartup failed\n";
|
||||||
|
return true; // Assume in use on failure
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sock < 0) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSACleanup();
|
||||||
|
#endif
|
||||||
|
return true; // Assume in use on failure
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
int opt = 1;
|
||||||
|
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&opt, sizeof(opt)); // Windows-specific
|
||||||
|
#else
|
||||||
|
int opt = 1;
|
||||||
|
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); // Linux/macOS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sockaddr_in addr{};
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(port);
|
||||||
|
|
||||||
|
// Convert IP address from string to binary format
|
||||||
|
if (inet_pton(AF_INET, ip.c_str(), &addr.sin_addr) <= 0) {
|
||||||
|
std::cerr << "Invalid IP address format: " << ip << std::endl;
|
||||||
|
#ifdef _WIN32
|
||||||
|
closesocket(sock);
|
||||||
|
WSACleanup();
|
||||||
|
#else
|
||||||
|
close(sock);
|
||||||
|
#endif
|
||||||
|
return true; // Assume in use on failure
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||||
|
in_use = true; // Bind failed, port is in use
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
closesocket(sock);
|
||||||
|
WSACleanup();
|
||||||
|
#else
|
||||||
|
close(sock);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return in_use;
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ public:
|
|||||||
int port
|
int port
|
||||||
);
|
);
|
||||||
static bool IsIPAddress(const std::string &ip_address);
|
static bool IsIPAddress(const std::string &ip_address);
|
||||||
|
static bool IsPortInUse(const std::string& ip, int port);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -906,24 +906,32 @@ bool EQ::ItemInstance::IsSlotAllowed(int16 slot_id) const {
|
|||||||
|
|
||||||
bool EQ::ItemInstance::IsDroppable(bool recurse) const
|
bool EQ::ItemInstance::IsDroppable(bool recurse) const
|
||||||
{
|
{
|
||||||
if (!m_item)
|
if (!m_item) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
/*if (m_ornamentidfile) // not implemented
|
/*if (m_ornamentidfile) // not implemented
|
||||||
return false;*/
|
return false;*/
|
||||||
if (m_attuned)
|
if (m_attuned) {
|
||||||
return false;
|
return false;
|
||||||
/*if (m_item->FVNoDrop != 0) // not implemented
|
}
|
||||||
return false;*/
|
|
||||||
if (m_item->NoDrop == 0)
|
if (RuleI(World, FVNoDropFlag) == FVNoDropFlagRule::Enabled && m_item->FVNoDrop == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_item->NoDrop == 0) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (recurse) {
|
if (recurse) {
|
||||||
for (auto iter : m_contents) {
|
for (auto iter: m_contents) {
|
||||||
if (!iter.second)
|
if (!iter.second) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!iter.second->IsDroppable(recurse))
|
if (!iter.second->IsDroppable(recurse)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ struct LootItem {
|
|||||||
uint16 trivial_max_level;
|
uint16 trivial_max_level;
|
||||||
uint16 npc_min_level;
|
uint16 npc_min_level;
|
||||||
uint16 npc_max_level;
|
uint16 npc_max_level;
|
||||||
|
uint32 lootdrop_id; // required for zone state referencing
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::list<LootItem*> LootItems;
|
typedef std::list<LootItem*> LootItems;
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
#include "daybreak_connection.h"
|
#include "daybreak_connection.h"
|
||||||
#include "../event/event_loop.h"
|
#include "../event/event_loop.h"
|
||||||
#include "../event/task.h"
|
|
||||||
#include "../data_verification.h"
|
#include "../data_verification.h"
|
||||||
#include "crc32.h"
|
#include "crc32.h"
|
||||||
#include "../eqemu_logsys.h"
|
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <sstream>
|
|
||||||
|
// observed client receive window is 300 packets, 140KB
|
||||||
|
constexpr size_t MAX_CLIENT_RECV_PACKETS_PER_WINDOW = 300;
|
||||||
|
constexpr size_t MAX_CLIENT_RECV_BYTES_PER_WINDOW = 140 * 1024;
|
||||||
|
|
||||||
|
// buffer pools
|
||||||
|
SendBufferPool send_buffer_pool;
|
||||||
|
|
||||||
EQ::Net::DaybreakConnectionManager::DaybreakConnectionManager()
|
EQ::Net::DaybreakConnectionManager::DaybreakConnectionManager()
|
||||||
{
|
{
|
||||||
@@ -53,16 +57,22 @@ void EQ::Net::DaybreakConnectionManager::Attach(uv_loop_t *loop)
|
|||||||
uv_ip4_addr("0.0.0.0", m_options.port, &recv_addr);
|
uv_ip4_addr("0.0.0.0", m_options.port, &recv_addr);
|
||||||
int rc = uv_udp_bind(&m_socket, (const struct sockaddr *)&recv_addr, UV_UDP_REUSEADDR);
|
int rc = uv_udp_bind(&m_socket, (const struct sockaddr *)&recv_addr, UV_UDP_REUSEADDR);
|
||||||
|
|
||||||
rc = uv_udp_recv_start(&m_socket,
|
rc = uv_udp_recv_start(
|
||||||
[](uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
|
&m_socket,
|
||||||
buf->base = new char[suggested_size];
|
[](uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
|
||||||
memset(buf->base, 0, suggested_size);
|
if (suggested_size > 65536) {
|
||||||
buf->len = suggested_size;
|
buf->base = new char[suggested_size];
|
||||||
},
|
buf->len = suggested_size;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static thread_local char temp_buf[65536];
|
||||||
|
buf->base = temp_buf;
|
||||||
|
buf->len = 65536;
|
||||||
|
},
|
||||||
[](uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) {
|
[](uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) {
|
||||||
DaybreakConnectionManager *c = (DaybreakConnectionManager*)handle->data;
|
DaybreakConnectionManager *c = (DaybreakConnectionManager*)handle->data;
|
||||||
if (nread < 0 || addr == nullptr) {
|
if (nread < 0 || addr == nullptr) {
|
||||||
delete[] buf->base;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +80,10 @@ void EQ::Net::DaybreakConnectionManager::Attach(uv_loop_t *loop)
|
|||||||
uv_ip4_name((const sockaddr_in*)addr, endpoint, 16);
|
uv_ip4_name((const sockaddr_in*)addr, endpoint, 16);
|
||||||
auto port = ntohs(((const sockaddr_in*)addr)->sin_port);
|
auto port = ntohs(((const sockaddr_in*)addr)->sin_port);
|
||||||
c->ProcessPacket(endpoint, port, buf->base, nread);
|
c->ProcessPacket(endpoint, port, buf->base, nread);
|
||||||
delete[] buf->base;
|
|
||||||
|
if (buf->len > 65536) {
|
||||||
|
delete[] buf->base;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
m_attached = loop;
|
m_attached = loop;
|
||||||
@@ -310,7 +323,7 @@ EQ::Net::DaybreakConnection::DaybreakConnection(DaybreakConnectionManager *owner
|
|||||||
m_last_session_stats = Clock::now();
|
m_last_session_stats = Clock::now();
|
||||||
m_outgoing_budget = owner->m_options.outgoing_data_rate;
|
m_outgoing_budget = owner->m_options.outgoing_data_rate;
|
||||||
|
|
||||||
LogNetcode("New session [{}] with encode key [{}]", m_connect_code, HostToNetwork(m_encode_key));
|
LogNetClient("New session [{}] with encode key [{}]", m_connect_code, HostToNetwork(m_encode_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
//new connection made as client
|
//new connection made as client
|
||||||
@@ -342,16 +355,16 @@ EQ::Net::DaybreakConnection::~DaybreakConnection()
|
|||||||
|
|
||||||
void EQ::Net::DaybreakConnection::Close()
|
void EQ::Net::DaybreakConnection::Close()
|
||||||
{
|
{
|
||||||
if (m_status == StatusConnected) {
|
if (m_status != StatusDisconnected && m_status != StatusDisconnecting) {
|
||||||
FlushBuffer();
|
FlushBuffer();
|
||||||
SendDisconnect();
|
SendDisconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_status != StatusDisconnecting) {
|
||||||
m_close_time = Clock::now();
|
m_close_time = Clock::now();
|
||||||
ChangeStatus(StatusDisconnecting);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ChangeStatus(StatusDisconnecting);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChangeStatus(StatusDisconnecting);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EQ::Net::DaybreakConnection::QueuePacket(Packet &p)
|
void EQ::Net::DaybreakConnection::QueuePacket(Packet &p)
|
||||||
@@ -634,7 +647,7 @@ void EQ::Net::DaybreakConnection::ProcessDecodedPacket(const Packet &p)
|
|||||||
p.PutSerialize(0, reply);
|
p.PutSerialize(0, reply);
|
||||||
InternalSend(p);
|
InternalSend(p);
|
||||||
|
|
||||||
LogNetcode("[OP_SessionRequest] Session [{}] started with encode key [{}]", m_connect_code, HostToNetwork(m_encode_key));
|
LogNetClient("[OP_SessionRequest] Session [{}] started with encode key [{}]", m_connect_code, HostToNetwork(m_encode_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -653,7 +666,7 @@ void EQ::Net::DaybreakConnection::ProcessDecodedPacket(const Packet &p)
|
|||||||
m_max_packet_size = reply.max_packet_size;
|
m_max_packet_size = reply.max_packet_size;
|
||||||
ChangeStatus(StatusConnected);
|
ChangeStatus(StatusConnected);
|
||||||
|
|
||||||
LogNetcode(
|
LogNetClient(
|
||||||
"[OP_SessionResponse] Session [{}] refresh with encode key [{}]",
|
"[OP_SessionResponse] Session [{}] refresh with encode key [{}]",
|
||||||
m_connect_code,
|
m_connect_code,
|
||||||
HostToNetwork(m_encode_key)
|
HostToNetwork(m_encode_key)
|
||||||
@@ -782,7 +795,7 @@ void EQ::Net::DaybreakConnection::ProcessDecodedPacket(const Packet &p)
|
|||||||
SendDisconnect();
|
SendDisconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
LogNetcode(
|
LogNetClient(
|
||||||
"[OP_SessionDisconnect] Session [{}] disconnect with encode key [{}]",
|
"[OP_SessionDisconnect] Session [{}] disconnect with encode key [{}]",
|
||||||
m_connect_code,
|
m_connect_code,
|
||||||
HostToNetwork(m_encode_key)
|
HostToNetwork(m_encode_key)
|
||||||
@@ -852,7 +865,7 @@ bool EQ::Net::DaybreakConnection::ValidateCRC(Packet &p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (p.Length() < (size_t)m_crc_bytes) {
|
if (p.Length() < (size_t)m_crc_bytes) {
|
||||||
LogNetcode("Session [{}] ignored packet (crc bytes invalid on session)", m_connect_code);
|
LogNetClient("Session [{}] ignored packet (crc bytes invalid on session)", m_connect_code);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1043,7 +1056,7 @@ void EQ::Net::DaybreakConnection::Decompress(Packet &p, size_t offset, size_t le
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t new_buffer[4096];
|
static thread_local uint8_t new_buffer[4096];
|
||||||
uint8_t *buffer = (uint8_t*)p.Data() + offset;
|
uint8_t *buffer = (uint8_t*)p.Data() + offset;
|
||||||
uint32_t new_length = 0;
|
uint32_t new_length = 0;
|
||||||
|
|
||||||
@@ -1064,7 +1077,7 @@ void EQ::Net::DaybreakConnection::Decompress(Packet &p, size_t offset, size_t le
|
|||||||
|
|
||||||
void EQ::Net::DaybreakConnection::Compress(Packet &p, size_t offset, size_t length)
|
void EQ::Net::DaybreakConnection::Compress(Packet &p, size_t offset, size_t length)
|
||||||
{
|
{
|
||||||
uint8_t new_buffer[2048] = { 0 };
|
static thread_local uint8_t new_buffer[2048] = { 0 };
|
||||||
uint8_t *buffer = (uint8_t*)p.Data() + offset;
|
uint8_t *buffer = (uint8_t*)p.Data() + offset;
|
||||||
uint32_t new_length = 0;
|
uint32_t new_length = 0;
|
||||||
bool send_uncompressed = true;
|
bool send_uncompressed = true;
|
||||||
@@ -1091,10 +1104,6 @@ void EQ::Net::DaybreakConnection::ProcessResend()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// observed client receive window is 300 packets, 140KB
|
|
||||||
constexpr size_t MAX_CLIENT_RECV_PACKETS_PER_WINDOW = 300;
|
|
||||||
constexpr size_t MAX_CLIENT_RECV_BYTES_PER_WINDOW = 140 * 1024;
|
|
||||||
|
|
||||||
void EQ::Net::DaybreakConnection::ProcessResend(int stream)
|
void EQ::Net::DaybreakConnection::ProcessResend(int stream)
|
||||||
{
|
{
|
||||||
if (m_status == DbProtocolStatus::StatusDisconnected) {
|
if (m_status == DbProtocolStatus::StatusDisconnected) {
|
||||||
@@ -1117,23 +1126,24 @@ void EQ::Net::DaybreakConnection::ProcessResend(int stream)
|
|||||||
auto &first_packet = s->sent_packets.begin()->second;
|
auto &first_packet = s->sent_packets.begin()->second;
|
||||||
auto time_since_first_sent = std::chrono::duration_cast<std::chrono::milliseconds>(now - first_packet.first_sent).count();
|
auto time_since_first_sent = std::chrono::duration_cast<std::chrono::milliseconds>(now - first_packet.first_sent).count();
|
||||||
|
|
||||||
|
if (time_since_first_sent >= m_owner->m_options.resend_timeout) {
|
||||||
|
Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// make sure that the first_packet in the list first_sent time is within the resend_delay and now
|
// make sure that the first_packet in the list first_sent time is within the resend_delay and now
|
||||||
// if it is not, then we need to resend all packets in the list
|
// if it is not, then we need to resend all packets in the list
|
||||||
if (time_since_first_sent <= first_packet.resend_delay && !m_acked_since_last_resend) {
|
if (time_since_first_sent <= first_packet.resend_delay && !m_acked_since_last_resend) {
|
||||||
LogNetcodeDetail(
|
LogNetClient(
|
||||||
"Not resending packets for stream [{}] time since first sent [{}] resend delay [{}] m_acked_since_last_resend [{}]",
|
"Not resending packets for stream [{}] packets [{}] time_first_sent [{}] resend_delay [{}] m_acked_since_last_resend [{}]",
|
||||||
stream,
|
stream,
|
||||||
|
s->sent_packets.size(),
|
||||||
time_since_first_sent,
|
time_since_first_sent,
|
||||||
first_packet.resend_delay,
|
first_packet.resend_delay,
|
||||||
m_acked_since_last_resend
|
m_acked_since_last_resend
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (time_since_first_sent >= m_owner->m_options.resend_timeout) {
|
|
||||||
Close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LogSys.IsLogEnabled(Logs::Detail, Logs::Netcode)) {
|
if (LogSys.IsLogEnabled(Logs::Detail, Logs::Netcode)) {
|
||||||
@@ -1142,7 +1152,7 @@ void EQ::Net::DaybreakConnection::ProcessResend(int stream)
|
|||||||
total_size += e.second.packet.Length();
|
total_size += e.second.packet.Length();
|
||||||
}
|
}
|
||||||
|
|
||||||
LogNetcodeDetail(
|
LogNetClient(
|
||||||
"Resending packets for stream [{}] packet count [{}] total packet size [{}] m_acked_since_last_resend [{}]",
|
"Resending packets for stream [{}] packet count [{}] total packet size [{}] m_acked_since_last_resend [{}]",
|
||||||
stream,
|
stream,
|
||||||
s->sent_packets.size(),
|
s->sent_packets.size(),
|
||||||
@@ -1154,7 +1164,7 @@ void EQ::Net::DaybreakConnection::ProcessResend(int stream)
|
|||||||
for (auto &e: s->sent_packets) {
|
for (auto &e: s->sent_packets) {
|
||||||
if (m_resend_packets_sent >= MAX_CLIENT_RECV_PACKETS_PER_WINDOW ||
|
if (m_resend_packets_sent >= MAX_CLIENT_RECV_PACKETS_PER_WINDOW ||
|
||||||
m_resend_bytes_sent >= MAX_CLIENT_RECV_BYTES_PER_WINDOW) {
|
m_resend_bytes_sent >= MAX_CLIENT_RECV_BYTES_PER_WINDOW) {
|
||||||
LogNetcodeDetail(
|
LogNetClient(
|
||||||
"Stopping resend because we hit thresholds m_resend_packets_sent [{}] max [{}] m_resend_bytes_sent [{}] max [{}]",
|
"Stopping resend because we hit thresholds m_resend_packets_sent [{}] max [{}] m_resend_bytes_sent [{}] max [{}]",
|
||||||
m_resend_packets_sent,
|
m_resend_packets_sent,
|
||||||
MAX_CLIENT_RECV_PACKETS_PER_WINDOW,
|
MAX_CLIENT_RECV_PACKETS_PER_WINDOW,
|
||||||
@@ -1333,99 +1343,97 @@ void EQ::Net::DaybreakConnection::SendKeepAlive()
|
|||||||
InternalSend(p);
|
InternalSend(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EQ::Net::DaybreakConnection::InternalSend(Packet &p)
|
void EQ::Net::DaybreakConnection::InternalSend(Packet &p) {
|
||||||
{
|
|
||||||
if (m_owner->m_options.outgoing_data_rate > 0.0) {
|
if (m_owner->m_options.outgoing_data_rate > 0.0) {
|
||||||
auto new_budget = m_outgoing_budget - (p.Length() / 1024.0);
|
auto new_budget = m_outgoing_budget - (p.Length() / 1024.0);
|
||||||
if (new_budget <= 0.0) {
|
if (new_budget <= 0.0) {
|
||||||
m_stats.dropped_datarate_packets++;
|
m_stats.dropped_datarate_packets++;
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
m_outgoing_budget = new_budget;
|
m_outgoing_budget = new_budget;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_last_send = Clock::now();
|
m_last_send = Clock::now();
|
||||||
|
|
||||||
auto send_func = [](uv_udp_send_t* req, int status) {
|
auto pooled_opt = send_buffer_pool.acquire();
|
||||||
delete[](char*)req->data;
|
if (!pooled_opt) {
|
||||||
delete req;
|
m_stats.dropped_datarate_packets++;
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto [send_req, data, ctx] = *pooled_opt;
|
||||||
|
ctx->pool = &send_buffer_pool; // set pool pointer
|
||||||
|
|
||||||
|
sockaddr_in send_addr{};
|
||||||
|
uv_ip4_addr(m_endpoint.c_str(), m_port, &send_addr);
|
||||||
|
uv_buf_t send_buffers[1];
|
||||||
|
|
||||||
if (PacketCanBeEncoded(p)) {
|
if (PacketCanBeEncoded(p)) {
|
||||||
|
|
||||||
m_stats.bytes_before_encode += p.Length();
|
m_stats.bytes_before_encode += p.Length();
|
||||||
|
|
||||||
DynamicPacket out;
|
DynamicPacket out;
|
||||||
out.PutPacket(0, p);
|
out.PutPacket(0, p);
|
||||||
|
|
||||||
for (int i = 0; i < 2; ++i) {
|
for (auto &m_encode_passe: m_encode_passes) {
|
||||||
switch (m_encode_passes[i]) {
|
switch (m_encode_passe) {
|
||||||
case EncodeCompression:
|
case EncodeCompression:
|
||||||
if (out.GetInt8(0) == 0)
|
if (out.GetInt8(0) == 0) {
|
||||||
Compress(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
|
Compress(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
|
||||||
else
|
} else {
|
||||||
Compress(out, 1, out.Length() - 1);
|
Compress(out, 1, out.Length() - 1);
|
||||||
break;
|
}
|
||||||
case EncodeXOR:
|
break;
|
||||||
if (out.GetInt8(0) == 0)
|
case EncodeXOR:
|
||||||
Encode(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
|
if (out.GetInt8(0) == 0) {
|
||||||
else
|
Encode(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
|
||||||
Encode(out, 1, out.Length() - 1);
|
} else {
|
||||||
break;
|
Encode(out, 1, out.Length() - 1);
|
||||||
default:
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendCRC(out);
|
AppendCRC(out);
|
||||||
|
|
||||||
uv_udp_send_t *send_req = new uv_udp_send_t;
|
|
||||||
memset(send_req, 0, sizeof(*send_req));
|
|
||||||
sockaddr_in send_addr;
|
|
||||||
uv_ip4_addr(m_endpoint.c_str(), m_port, &send_addr);
|
|
||||||
uv_buf_t send_buffers[1];
|
|
||||||
|
|
||||||
char *data = new char[out.Length()];
|
|
||||||
memcpy(data, out.Data(), out.Length());
|
memcpy(data, out.Data(), out.Length());
|
||||||
send_buffers[0] = uv_buf_init(data, out.Length());
|
send_buffers[0] = uv_buf_init(data, out.Length());
|
||||||
send_req->data = send_buffers[0].base;
|
} else {
|
||||||
|
memcpy(data, p.Data(), p.Length());
|
||||||
m_stats.sent_bytes += out.Length();
|
send_buffers[0] = uv_buf_init(data, p.Length());
|
||||||
m_stats.sent_packets++;
|
|
||||||
if (m_owner->m_options.simulated_out_packet_loss && m_owner->m_options.simulated_out_packet_loss >= m_owner->m_rand.Int(0, 100)) {
|
|
||||||
delete[](char*)send_req->data;
|
|
||||||
delete send_req;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uv_udp_send(send_req, &m_owner->m_socket, send_buffers, 1, (sockaddr*)&send_addr, send_func);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_stats.bytes_before_encode += p.Length();
|
|
||||||
|
|
||||||
uv_udp_send_t *send_req = new uv_udp_send_t;
|
|
||||||
sockaddr_in send_addr;
|
|
||||||
uv_ip4_addr(m_endpoint.c_str(), m_port, &send_addr);
|
|
||||||
uv_buf_t send_buffers[1];
|
|
||||||
|
|
||||||
char *data = new char[p.Length()];
|
|
||||||
memcpy(data, p.Data(), p.Length());
|
|
||||||
send_buffers[0] = uv_buf_init(data, p.Length());
|
|
||||||
send_req->data = send_buffers[0].base;
|
|
||||||
|
|
||||||
m_stats.sent_bytes += p.Length();
|
m_stats.sent_bytes += p.Length();
|
||||||
m_stats.sent_packets++;
|
m_stats.sent_packets++;
|
||||||
|
|
||||||
if (m_owner->m_options.simulated_out_packet_loss && m_owner->m_options.simulated_out_packet_loss >= m_owner->m_rand.Int(0, 100)) {
|
if (m_owner->m_options.simulated_out_packet_loss &&
|
||||||
delete[](char*)send_req->data;
|
m_owner->m_options.simulated_out_packet_loss >= m_owner->m_rand.Int(0, 100)) {
|
||||||
delete send_req;
|
send_buffer_pool.release(ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uv_udp_send(send_req, &m_owner->m_socket, send_buffers, 1, (sockaddr*)&send_addr, send_func);
|
int send_result = uv_udp_send(
|
||||||
|
send_req, &m_owner->m_socket, send_buffers, 1, (sockaddr *)&send_addr,
|
||||||
|
[](uv_udp_send_t *req, int status) {
|
||||||
|
auto *ctx = reinterpret_cast<EmbeddedContext *>(req->data);
|
||||||
|
if (!ctx) {
|
||||||
|
std::cerr << "Error: send_req->data is null in callback!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status < 0) {
|
||||||
|
std::cerr << "uv_udp_send failed: " << uv_strerror(status) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->pool->release(ctx);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (send_result < 0) {
|
||||||
|
std::cerr << "uv_udp_send() failed: " << uv_strerror(send_result) << std::endl;
|
||||||
|
send_buffer_pool.release(ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EQ::Net::DaybreakConnection::InternalQueuePacket(Packet &p, int stream_id, bool reliable)
|
void EQ::Net::DaybreakConnection::InternalQueuePacket(Packet &p, int stream_id, bool reliable)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "../random.h"
|
#include "../random.h"
|
||||||
#include "packet.h"
|
#include "packet.h"
|
||||||
#include "daybreak_structs.h"
|
#include "daybreak_structs.h"
|
||||||
|
#include "daybreak_pooling.h"
|
||||||
#include <uv.h>
|
#include <uv.h>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
#include <iostream>
|
||||||
|
#include "../eqemu_logsys.h"
|
||||||
|
#include <uv.h>
|
||||||
|
|
||||||
|
constexpr size_t UDP_BUFFER_SIZE = 512;
|
||||||
|
|
||||||
|
struct EmbeddedContext {
|
||||||
|
size_t pool_index;
|
||||||
|
class SendBufferPool* pool;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SendBufferPool {
|
||||||
|
public:
|
||||||
|
explicit SendBufferPool(size_t initial_capacity = 64)
|
||||||
|
: m_capacity(initial_capacity), m_head(0)
|
||||||
|
{
|
||||||
|
LogNetClient("[SendBufferPool] Initializing with capacity [{}]", (int)m_capacity);
|
||||||
|
|
||||||
|
m_pool.reserve(m_capacity);
|
||||||
|
m_locks = std::make_unique<std::atomic_bool[]>(m_capacity);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_capacity; ++i) {
|
||||||
|
auto* req = new PooledUdpSend();
|
||||||
|
req->context.pool_index = i;
|
||||||
|
req->context.pool = this;
|
||||||
|
req->uv_req.data = &req->context;
|
||||||
|
|
||||||
|
m_pool.emplace_back(std::unique_ptr<PooledUdpSend>(req));
|
||||||
|
m_locks[i].store(false, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::tuple<uv_udp_send_t*, char*, EmbeddedContext*>> acquire() {
|
||||||
|
size_t cap = m_capacity.load(std::memory_order_acquire);
|
||||||
|
for (size_t i = 0; i < cap; ++i) {
|
||||||
|
size_t index = m_head.fetch_add(1, std::memory_order_relaxed) % cap;
|
||||||
|
bool expected = false;
|
||||||
|
if (m_locks[index].compare_exchange_strong(expected, true)) {
|
||||||
|
auto* req = m_pool[index].get();
|
||||||
|
LogNetClientDetail("[SendBufferPool] Acquired [{}]", index);
|
||||||
|
return std::make_tuple(&req->uv_req, req->buffer.data(), &req->context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogNetClient("[SendBufferPool] Growing from [{}] to [{}]", cap, cap * 2);
|
||||||
|
grow();
|
||||||
|
return acquireAfterGrowth();
|
||||||
|
}
|
||||||
|
|
||||||
|
void release(EmbeddedContext* ctx) {
|
||||||
|
if (!ctx || ctx->pool != this || ctx->pool_index >= m_capacity.load(std::memory_order_acquire)) {
|
||||||
|
LogNetClient("[SendBufferPool] Invalid context release [{}]", ctx ? ctx->pool_index : -1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_locks[ctx->pool_index].store(false, std::memory_order_release);
|
||||||
|
LogNetClientDetail("[SendBufferPool] Released [{}]", ctx->pool_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct PooledUdpSend {
|
||||||
|
uv_udp_send_t uv_req;
|
||||||
|
std::array<char, UDP_BUFFER_SIZE> buffer;
|
||||||
|
EmbeddedContext context;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<PooledUdpSend>> m_pool;
|
||||||
|
std::unique_ptr<std::atomic_bool[]> m_locks;
|
||||||
|
std::atomic<size_t> m_capacity;
|
||||||
|
std::atomic<size_t> m_head;
|
||||||
|
std::mutex m_grow_mutex;
|
||||||
|
|
||||||
|
void grow() {
|
||||||
|
std::lock_guard<std::mutex> lock(m_grow_mutex);
|
||||||
|
|
||||||
|
size_t old_cap = m_capacity.load(std::memory_order_acquire);
|
||||||
|
size_t new_cap = old_cap * 2;
|
||||||
|
|
||||||
|
m_pool.reserve(new_cap);
|
||||||
|
for (size_t i = old_cap; i < new_cap; ++i) {
|
||||||
|
auto* req = new PooledUdpSend();
|
||||||
|
req->context.pool_index = i;
|
||||||
|
req->context.pool = this;
|
||||||
|
req->uv_req.data = &req->context;
|
||||||
|
|
||||||
|
m_pool.emplace_back(std::unique_ptr<PooledUdpSend>(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto new_locks = std::make_unique<std::atomic_bool[]>(new_cap);
|
||||||
|
for (size_t i = 0; i < old_cap; ++i) {
|
||||||
|
new_locks[i].store(m_locks[i].load(std::memory_order_acquire));
|
||||||
|
}
|
||||||
|
for (size_t i = old_cap; i < new_cap; ++i) {
|
||||||
|
new_locks[i].store(false, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_locks = std::move(new_locks);
|
||||||
|
m_capacity.store(new_cap, std::memory_order_release);
|
||||||
|
|
||||||
|
LogNetClient("[SendBufferPool] Grew to [{}] from [{}]", new_cap, old_cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::tuple<uv_udp_send_t*, char*, EmbeddedContext*>> acquireAfterGrowth() {
|
||||||
|
size_t cap = m_capacity.load(std::memory_order_acquire);
|
||||||
|
for (size_t i = 0; i < cap; ++i) {
|
||||||
|
size_t index = m_head.fetch_add(1, std::memory_order_relaxed) % cap;
|
||||||
|
bool expected = false;
|
||||||
|
if (m_locks[index].compare_exchange_strong(expected, true)) {
|
||||||
|
auto* req = m_pool[index].get();
|
||||||
|
LogNetClient("[SendBufferPool] Acquired after grow [{}]", index);
|
||||||
|
return std::make_tuple(&req->uv_req, req->buffer.data(), &req->context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -171,3 +171,4 @@ namespace EQ
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,15 +62,15 @@ void EQ::Net::ServertalkClient::Connect()
|
|||||||
m_connecting = true;
|
m_connecting = true;
|
||||||
EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
|
EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
|
||||||
if (connection == nullptr) {
|
if (connection == nullptr) {
|
||||||
LogF(Logs::General, Logs::TCPConnection, "Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port);
|
LogNetTCP("Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port);
|
||||||
m_connecting = false;
|
m_connecting = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogF(Logs::General, Logs::TCPConnection, "Connected to {0}:{1}", m_addr, m_port);
|
LogNetTCP("Connected to {0}:{1}", m_addr, m_port);
|
||||||
m_connection = connection;
|
m_connection = connection;
|
||||||
m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) {
|
m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) {
|
||||||
LogF(Logs::General, Logs::TCPConnection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port);
|
LogNetTCP("Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port);
|
||||||
m_connection.reset();
|
m_connection.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -58,15 +58,15 @@ void EQ::Net::ServertalkLegacyClient::Connect()
|
|||||||
m_connecting = true;
|
m_connecting = true;
|
||||||
EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
|
EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
|
||||||
if (connection == nullptr) {
|
if (connection == nullptr) {
|
||||||
LogF(Logs::General, Logs::TCPConnection, "Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port);
|
LogNetTCP("Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port);
|
||||||
m_connecting = false;
|
m_connecting = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogF(Logs::General, Logs::TCPConnection, "Connected to {0}:{1}", m_addr, m_port);
|
LogNetTCP("Connected to {0}:{1}", m_addr, m_port);
|
||||||
m_connection = connection;
|
m_connection = connection;
|
||||||
m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) {
|
m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) {
|
||||||
LogF(Logs::General, Logs::TCPConnection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port);
|
LogNetTCP("Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port);
|
||||||
m_connection.reset();
|
m_connection.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (opcode == ServerOP_UsertoWorldReq) {
|
if (opcode == ServerOP_UsertoWorldReq) {
|
||||||
auto req_in = (UsertoWorldRequest_Struct*)p.Data();
|
auto req_in = (UsertoWorldRequest*)p.Data();
|
||||||
|
|
||||||
EQ::Net::DynamicPacket req;
|
EQ::Net::DynamicPacket req;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
@@ -45,7 +45,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (opcode == ServerOP_LSClientAuth) {
|
if (opcode == ServerOP_LSClientAuth) {
|
||||||
auto req_in = (ClientAuth_Struct*)p.Data();
|
auto req_in = (ClientAuth*)p.Data();
|
||||||
|
|
||||||
EQ::Net::DynamicPacket req;
|
EQ::Net::DynamicPacket req;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
@@ -54,7 +54,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
|
|||||||
req.PutData(i, req_in->key, 30); i += 30;
|
req.PutData(i, req_in->key, 30); i += 30;
|
||||||
req.PutUInt8(i, req_in->lsadmin); i += 1;
|
req.PutUInt8(i, req_in->lsadmin); i += 1;
|
||||||
req.PutUInt16(i, req_in->is_world_admin); i += 2;
|
req.PutUInt16(i, req_in->is_world_admin); i += 2;
|
||||||
req.PutUInt32(i, req_in->ip); i += 4;
|
req.PutUInt32(i, req_in->ip_address); i += 4;
|
||||||
req.PutUInt8(i, req_in->is_client_from_local_network); i += 1;
|
req.PutUInt8(i, req_in->is_client_from_local_network); i += 1;
|
||||||
|
|
||||||
EQ::Net::DynamicPacket out;
|
EQ::Net::DynamicPacket out;
|
||||||
|
|||||||
+108
-55
@@ -1,5 +1,8 @@
|
|||||||
#include "tcp_connection.h"
|
#include "tcp_connection.h"
|
||||||
#include "../event/event_loop.h"
|
#include "../event/event_loop.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
WriteReqPool tcp_write_pool;
|
||||||
|
|
||||||
void on_close_handle(uv_handle_t* handle) {
|
void on_close_handle(uv_handle_t* handle) {
|
||||||
delete (uv_tcp_t *)handle;
|
delete (uv_tcp_t *)handle;
|
||||||
@@ -64,36 +67,37 @@ void EQ::Net::TCPConnection::Connect(const std::string &addr, int port, bool ipv
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EQ::Net::TCPConnection::Start() {
|
void EQ::Net::TCPConnection::Start()
|
||||||
uv_read_start((uv_stream_t*)m_socket, [](uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
|
{
|
||||||
buf->base = new char[suggested_size];
|
uv_read_start(
|
||||||
buf->len = suggested_size;
|
(uv_stream_t *) m_socket, [](uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
|
||||||
}, [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
|
if (suggested_size > 65536) {
|
||||||
|
buf->base = new char[suggested_size];
|
||||||
|
buf->len = suggested_size;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TCPConnection *connection = (TCPConnection*)stream->data;
|
static thread_local char temp_buf[65536];
|
||||||
|
buf->base = temp_buf;
|
||||||
|
buf->len = 65536;
|
||||||
|
}, [](uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
|
||||||
|
auto *connection = (TCPConnection *) stream->data;
|
||||||
|
|
||||||
if (nread > 0) {
|
if (nread > 0) {
|
||||||
connection->Read(buf->base, nread);
|
connection->Read(buf->base, nread);
|
||||||
|
}
|
||||||
|
else if (nread == UV_EOF) {
|
||||||
|
connection->Disconnect();
|
||||||
|
}
|
||||||
|
else if (nread < 0) {
|
||||||
|
connection->Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
if (buf->base) {
|
if (buf->len > 65536) {
|
||||||
delete[] buf->base;
|
delete [] buf->base;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (nread == UV_EOF) {
|
);
|
||||||
connection->Disconnect();
|
|
||||||
|
|
||||||
if (buf->base) {
|
|
||||||
delete[] buf->base;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (nread < 0) {
|
|
||||||
connection->Disconnect();
|
|
||||||
|
|
||||||
if (buf->base) {
|
|
||||||
delete[] buf->base;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EQ::Net::TCPConnection::OnRead(std::function<void(TCPConnection*, const unsigned char*, size_t)> cb)
|
void EQ::Net::TCPConnection::OnRead(std::function<void(TCPConnection*, const unsigned char*, size_t)> cb)
|
||||||
@@ -130,43 +134,92 @@ void EQ::Net::TCPConnection::Read(const char *data, size_t count)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EQ::Net::TCPConnection::Write(const char *data, size_t count)
|
void EQ::Net::TCPConnection::Write(const char* data, size_t count) {
|
||||||
{
|
if (!m_socket || !data || count == 0) {
|
||||||
if (!m_socket) {
|
std::cerr << "TCPConnection::Write - Invalid socket or data\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WriteBaton
|
if (count <= TCP_BUFFER_SIZE) {
|
||||||
{
|
// Fast path: use pooled request with embedded buffer
|
||||||
TCPConnection *connection;
|
auto req_opt = tcp_write_pool.acquire();
|
||||||
char *buffer;
|
if (!req_opt) {
|
||||||
};
|
std::cerr << "TCPConnection::Write - Out of write requests\n";
|
||||||
|
return;
|
||||||
WriteBaton *baton = new WriteBaton;
|
|
||||||
baton->connection = this;
|
|
||||||
baton->buffer = new char[count];
|
|
||||||
|
|
||||||
uv_write_t *write_req = new uv_write_t;
|
|
||||||
memset(write_req, 0, sizeof(uv_write_t));
|
|
||||||
write_req->data = baton;
|
|
||||||
uv_buf_t send_buffers[1];
|
|
||||||
|
|
||||||
memcpy(baton->buffer, data, count);
|
|
||||||
send_buffers[0] = uv_buf_init(baton->buffer, count);
|
|
||||||
|
|
||||||
uv_write(write_req, (uv_stream_t*)m_socket, send_buffers, 1, [](uv_write_t* req, int status) {
|
|
||||||
WriteBaton *baton = (WriteBaton*)req->data;
|
|
||||||
delete[] baton->buffer;
|
|
||||||
delete req;
|
|
||||||
|
|
||||||
if (status < 0) {
|
|
||||||
baton->connection->Disconnect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete baton;
|
TCPWriteReq* write_req = *req_opt;
|
||||||
});
|
|
||||||
|
// Fill buffer and set context
|
||||||
|
memcpy(write_req->buffer.data(), data, count);
|
||||||
|
write_req->connection = this;
|
||||||
|
write_req->magic = 0xC0FFEE;
|
||||||
|
|
||||||
|
uv_buf_t buf = uv_buf_init(write_req->buffer.data(), static_cast<unsigned int>(count));
|
||||||
|
|
||||||
|
int result = uv_write(
|
||||||
|
&write_req->req,
|
||||||
|
reinterpret_cast<uv_stream_t*>(m_socket),
|
||||||
|
&buf,
|
||||||
|
1,
|
||||||
|
[](uv_write_t* req, int status) {
|
||||||
|
auto* full_req = reinterpret_cast<TCPWriteReq*>(req);
|
||||||
|
if (full_req->magic != 0xC0FFEE) {
|
||||||
|
std::cerr << "uv_write callback - invalid magic, skipping release\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp_write_pool.release(full_req);
|
||||||
|
|
||||||
|
if (status < 0 && full_req->connection) {
|
||||||
|
std::cerr << "uv_write failed: " << uv_strerror(status) << std::endl;
|
||||||
|
full_req->connection->Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result < 0) {
|
||||||
|
std::cerr << "uv_write() failed immediately: " << uv_strerror(result) << std::endl;
|
||||||
|
tcp_write_pool.release(write_req);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Slow path: allocate heap buffer for large write
|
||||||
|
LogNetTCP("[TCPConnection] Large write of [{}] bytes, using heap buffer", count);
|
||||||
|
|
||||||
|
char* heap_buffer = new char[count];
|
||||||
|
memcpy(heap_buffer, data, count);
|
||||||
|
|
||||||
|
uv_write_t* write_req = new uv_write_t;
|
||||||
|
write_req->data = heap_buffer;
|
||||||
|
|
||||||
|
uv_buf_t buf = uv_buf_init(heap_buffer, static_cast<unsigned int>(count));
|
||||||
|
|
||||||
|
int result = uv_write(
|
||||||
|
write_req,
|
||||||
|
reinterpret_cast<uv_stream_t*>(m_socket),
|
||||||
|
&buf,
|
||||||
|
1,
|
||||||
|
[](uv_write_t* req, int status) {
|
||||||
|
char* data = static_cast<char*>(req->data);
|
||||||
|
delete[] data;
|
||||||
|
delete req;
|
||||||
|
|
||||||
|
if (status < 0) {
|
||||||
|
std::cerr << "uv_write (large) failed: " << uv_strerror(status) << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result < 0) {
|
||||||
|
std::cerr << "uv_write() (large) failed immediately: " << uv_strerror(result) << std::endl;
|
||||||
|
delete[] heap_buffer;
|
||||||
|
delete write_req;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string EQ::Net::TCPConnection::LocalIP() const
|
std::string EQ::Net::TCPConnection::LocalIP() const
|
||||||
{
|
{
|
||||||
sockaddr_storage addr;
|
sockaddr_storage addr;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "tcp_connection_pooling.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../eqemu_logsys.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <mutex>
|
||||||
|
#include <uv.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace EQ { namespace Net { class TCPConnection; } }
|
||||||
|
|
||||||
|
constexpr size_t TCP_BUFFER_SIZE = 8192;
|
||||||
|
|
||||||
|
struct TCPWriteReq {
|
||||||
|
uv_write_t req{};
|
||||||
|
std::array<char, TCP_BUFFER_SIZE> buffer{};
|
||||||
|
size_t buffer_index{};
|
||||||
|
EQ::Net::TCPConnection* connection{};
|
||||||
|
uint32_t magic = 0xC0FFEE;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WriteReqPool {
|
||||||
|
public:
|
||||||
|
explicit WriteReqPool(size_t initial_capacity = 512)
|
||||||
|
: m_capacity(initial_capacity), m_head(0) {
|
||||||
|
initialize_pool(m_capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TCPWriteReq*> acquire() {
|
||||||
|
size_t cap = m_capacity.load(std::memory_order_acquire);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < cap; ++i) {
|
||||||
|
size_t index = m_head.fetch_add(1, std::memory_order_relaxed) % cap;
|
||||||
|
|
||||||
|
bool expected = false;
|
||||||
|
if (m_locks[index].compare_exchange_strong(expected, true, std::memory_order_acquire)) {
|
||||||
|
LogNetTCPDetail("[WriteReqPool] Acquired buffer index [{}]", index);
|
||||||
|
return m_reqs[index].get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogNetTCP("[WriteReqPool] Growing from [{}] to [{}]", cap, cap * 2);
|
||||||
|
grow();
|
||||||
|
return acquireAfterGrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void release(TCPWriteReq* req) {
|
||||||
|
if (!req) return;
|
||||||
|
|
||||||
|
const size_t index = req->buffer_index;
|
||||||
|
const size_t cap = m_capacity.load(std::memory_order_acquire);
|
||||||
|
|
||||||
|
if (index >= cap || m_reqs[index].get() != req) {
|
||||||
|
std::cerr << "WriteReqPool::release - Invalid or stale pointer (index=" << index << ")\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_locks[index].store(false, std::memory_order_release);
|
||||||
|
LogNetTCPDetail("[WriteReqPool] Released buffer index [{}]", index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::unique_ptr<TCPWriteReq>> m_reqs;
|
||||||
|
std::unique_ptr<std::atomic_bool[]> m_locks;
|
||||||
|
std::atomic<size_t> m_capacity;
|
||||||
|
std::atomic<size_t> m_head;
|
||||||
|
std::mutex m_grow_mutex;
|
||||||
|
|
||||||
|
void initialize_pool(size_t count) {
|
||||||
|
m_reqs.reserve(count);
|
||||||
|
m_locks = std::make_unique<std::atomic_bool[]>(count);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
auto req = std::make_unique<TCPWriteReq>();
|
||||||
|
req->buffer_index = i;
|
||||||
|
req->req.data = req.get(); // optional: for use in libuv callbacks
|
||||||
|
m_locks[i].store(false, std::memory_order_relaxed);
|
||||||
|
m_reqs.emplace_back(std::move(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_capacity.store(count, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
void grow() {
|
||||||
|
std::lock_guard<std::mutex> lock(m_grow_mutex);
|
||||||
|
|
||||||
|
const size_t old_cap = m_capacity.load(std::memory_order_acquire);
|
||||||
|
const size_t new_cap = old_cap * 2;
|
||||||
|
|
||||||
|
m_reqs.reserve(new_cap);
|
||||||
|
for (size_t i = old_cap; i < new_cap; ++i) {
|
||||||
|
auto req = std::make_unique<TCPWriteReq>();
|
||||||
|
req->buffer_index = i;
|
||||||
|
req->req.data = req.get(); // optional
|
||||||
|
m_reqs.emplace_back(std::move(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto new_locks = std::make_unique<std::atomic_bool[]>(new_cap);
|
||||||
|
for (size_t i = 0; i < old_cap; ++i) {
|
||||||
|
new_locks[i].store(m_locks[i].load(std::memory_order_acquire));
|
||||||
|
}
|
||||||
|
for (size_t i = old_cap; i < new_cap; ++i) {
|
||||||
|
new_locks[i].store(false, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_locks = std::move(new_locks);
|
||||||
|
m_capacity.store(new_cap, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TCPWriteReq*> acquireAfterGrow() {
|
||||||
|
const size_t cap = m_capacity.load(std::memory_order_acquire);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < cap; ++i) {
|
||||||
|
bool expected = false;
|
||||||
|
if (m_locks[i].compare_exchange_strong(expected, true, std::memory_order_acquire)) {
|
||||||
|
LogNetTCP("[WriteReqPool] Acquired buffer index [{}] after grow", i);
|
||||||
|
return m_reqs[i].get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -76,7 +76,7 @@ void PathManager::LoadPaths()
|
|||||||
constexpr int path_width = 0;
|
constexpr int path_width = 0;
|
||||||
constexpr int break_length = 70;
|
constexpr int break_length = 70;
|
||||||
|
|
||||||
std::cout << std::endl;
|
LogInfo("Loading server paths");
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
LogInfo("{}", Strings::Repeat("-", break_length));
|
||||||
for (const auto& [name, in_path] : paths) {
|
for (const auto& [name, in_path] : paths) {
|
||||||
if (!in_path.empty()) {
|
if (!in_path.empty()) {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public:
|
|||||||
int16_t race;
|
int16_t race;
|
||||||
int8_t gender;
|
int8_t gender;
|
||||||
int8_t texture;
|
int8_t texture;
|
||||||
|
int8_t helmtexture;
|
||||||
float mountspeed;
|
float mountspeed;
|
||||||
std::string notes;
|
std::string notes;
|
||||||
};
|
};
|
||||||
@@ -41,6 +42,7 @@ public:
|
|||||||
"race",
|
"race",
|
||||||
"gender",
|
"gender",
|
||||||
"texture",
|
"texture",
|
||||||
|
"helmtexture",
|
||||||
"mountspeed",
|
"mountspeed",
|
||||||
"notes",
|
"notes",
|
||||||
};
|
};
|
||||||
@@ -54,6 +56,7 @@ public:
|
|||||||
"race",
|
"race",
|
||||||
"gender",
|
"gender",
|
||||||
"texture",
|
"texture",
|
||||||
|
"helmtexture",
|
||||||
"mountspeed",
|
"mountspeed",
|
||||||
"notes",
|
"notes",
|
||||||
};
|
};
|
||||||
@@ -96,13 +99,14 @@ public:
|
|||||||
{
|
{
|
||||||
Horses e{};
|
Horses e{};
|
||||||
|
|
||||||
e.id = 0;
|
e.id = 0;
|
||||||
e.filename = "";
|
e.filename = "";
|
||||||
e.race = 216;
|
e.race = 216;
|
||||||
e.gender = 0;
|
e.gender = 0;
|
||||||
e.texture = 0;
|
e.texture = 0;
|
||||||
e.mountspeed = 0.75;
|
e.helmtexture = -1;
|
||||||
e.notes = "Notes";
|
e.mountspeed = 0.75;
|
||||||
|
e.notes = "Notes";
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -139,13 +143,14 @@ public:
|
|||||||
if (results.RowCount() == 1) {
|
if (results.RowCount() == 1) {
|
||||||
Horses e{};
|
Horses e{};
|
||||||
|
|
||||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||||
e.filename = row[1] ? row[1] : "";
|
e.filename = row[1] ? row[1] : "";
|
||||||
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
||||||
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
e.mountspeed = row[5] ? strtof(row[5], nullptr) : 0.75;
|
e.helmtexture = row[5] ? static_cast<int8_t>(atoi(row[5])) : -1;
|
||||||
e.notes = row[6] ? row[6] : "Notes";
|
e.mountspeed = row[6] ? strtof(row[6], nullptr) : 0.75;
|
||||||
|
e.notes = row[7] ? row[7] : "Notes";
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -183,8 +188,9 @@ public:
|
|||||||
v.push_back(columns[2] + " = " + std::to_string(e.race));
|
v.push_back(columns[2] + " = " + std::to_string(e.race));
|
||||||
v.push_back(columns[3] + " = " + std::to_string(e.gender));
|
v.push_back(columns[3] + " = " + std::to_string(e.gender));
|
||||||
v.push_back(columns[4] + " = " + std::to_string(e.texture));
|
v.push_back(columns[4] + " = " + std::to_string(e.texture));
|
||||||
v.push_back(columns[5] + " = " + std::to_string(e.mountspeed));
|
v.push_back(columns[5] + " = " + std::to_string(e.helmtexture));
|
||||||
v.push_back(columns[6] + " = '" + Strings::Escape(e.notes) + "'");
|
v.push_back(columns[6] + " = " + std::to_string(e.mountspeed));
|
||||||
|
v.push_back(columns[7] + " = '" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -211,6 +217,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.race));
|
v.push_back(std::to_string(e.race));
|
||||||
v.push_back(std::to_string(e.gender));
|
v.push_back(std::to_string(e.gender));
|
||||||
v.push_back(std::to_string(e.texture));
|
v.push_back(std::to_string(e.texture));
|
||||||
|
v.push_back(std::to_string(e.helmtexture));
|
||||||
v.push_back(std::to_string(e.mountspeed));
|
v.push_back(std::to_string(e.mountspeed));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -247,6 +254,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.race));
|
v.push_back(std::to_string(e.race));
|
||||||
v.push_back(std::to_string(e.gender));
|
v.push_back(std::to_string(e.gender));
|
||||||
v.push_back(std::to_string(e.texture));
|
v.push_back(std::to_string(e.texture));
|
||||||
|
v.push_back(std::to_string(e.helmtexture));
|
||||||
v.push_back(std::to_string(e.mountspeed));
|
v.push_back(std::to_string(e.mountspeed));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -282,13 +290,14 @@ public:
|
|||||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
Horses e{};
|
Horses e{};
|
||||||
|
|
||||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||||
e.filename = row[1] ? row[1] : "";
|
e.filename = row[1] ? row[1] : "";
|
||||||
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
||||||
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
e.mountspeed = row[5] ? strtof(row[5], nullptr) : 0.75;
|
e.helmtexture = row[5] ? static_cast<int8_t>(atoi(row[5])) : -1;
|
||||||
e.notes = row[6] ? row[6] : "Notes";
|
e.mountspeed = row[6] ? strtof(row[6], nullptr) : 0.75;
|
||||||
|
e.notes = row[7] ? row[7] : "Notes";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -313,13 +322,14 @@ public:
|
|||||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
Horses e{};
|
Horses e{};
|
||||||
|
|
||||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||||
e.filename = row[1] ? row[1] : "";
|
e.filename = row[1] ? row[1] : "";
|
||||||
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
||||||
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
e.mountspeed = row[5] ? strtof(row[5], nullptr) : 0.75;
|
e.helmtexture = row[5] ? static_cast<int8_t>(atoi(row[5])) : -1;
|
||||||
e.notes = row[6] ? row[6] : "Notes";
|
e.mountspeed = row[6] ? strtof(row[6], nullptr) : 0.75;
|
||||||
|
e.notes = row[7] ? row[7] : "Notes";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -399,6 +409,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.race));
|
v.push_back(std::to_string(e.race));
|
||||||
v.push_back(std::to_string(e.gender));
|
v.push_back(std::to_string(e.gender));
|
||||||
v.push_back(std::to_string(e.texture));
|
v.push_back(std::to_string(e.texture));
|
||||||
|
v.push_back(std::to_string(e.helmtexture));
|
||||||
v.push_back(std::to_string(e.mountspeed));
|
v.push_back(std::to_string(e.mountspeed));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -428,6 +439,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.race));
|
v.push_back(std::to_string(e.race));
|
||||||
v.push_back(std::to_string(e.gender));
|
v.push_back(std::to_string(e.gender));
|
||||||
v.push_back(std::to_string(e.texture));
|
v.push_back(std::to_string(e.texture));
|
||||||
|
v.push_back(std::to_string(e.helmtexture));
|
||||||
v.push_back(std::to_string(e.mountspeed));
|
v.push_back(std::to_string(e.mountspeed));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public:
|
|||||||
uint8_t is_global;
|
uint8_t is_global;
|
||||||
uint32_t start_time;
|
uint32_t start_time;
|
||||||
uint32_t duration;
|
uint32_t duration;
|
||||||
|
uint64_t expire_at;
|
||||||
uint8_t never_expires;
|
uint8_t never_expires;
|
||||||
std::string notes;
|
std::string notes;
|
||||||
};
|
};
|
||||||
@@ -43,6 +44,7 @@ public:
|
|||||||
"is_global",
|
"is_global",
|
||||||
"start_time",
|
"start_time",
|
||||||
"duration",
|
"duration",
|
||||||
|
"expire_at",
|
||||||
"never_expires",
|
"never_expires",
|
||||||
"notes",
|
"notes",
|
||||||
};
|
};
|
||||||
@@ -57,6 +59,7 @@ public:
|
|||||||
"is_global",
|
"is_global",
|
||||||
"start_time",
|
"start_time",
|
||||||
"duration",
|
"duration",
|
||||||
|
"expire_at",
|
||||||
"never_expires",
|
"never_expires",
|
||||||
"notes",
|
"notes",
|
||||||
};
|
};
|
||||||
@@ -105,6 +108,7 @@ public:
|
|||||||
e.is_global = 0;
|
e.is_global = 0;
|
||||||
e.start_time = 0;
|
e.start_time = 0;
|
||||||
e.duration = 0;
|
e.duration = 0;
|
||||||
|
e.expire_at = 0;
|
||||||
e.never_expires = 0;
|
e.never_expires = 0;
|
||||||
e.notes = "";
|
e.notes = "";
|
||||||
|
|
||||||
@@ -149,8 +153,9 @@ public:
|
|||||||
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||||
e.never_expires = row[6] ? static_cast<uint8_t>(strtoul(row[6], nullptr, 10)) : 0;
|
e.expire_at = row[6] ? strtoull(row[6], nullptr, 10) : 0;
|
||||||
e.notes = row[7] ? row[7] : "";
|
e.never_expires = row[7] ? static_cast<uint8_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.notes = row[8] ? row[8] : "";
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -189,8 +194,9 @@ public:
|
|||||||
v.push_back(columns[3] + " = " + std::to_string(e.is_global));
|
v.push_back(columns[3] + " = " + std::to_string(e.is_global));
|
||||||
v.push_back(columns[4] + " = " + std::to_string(e.start_time));
|
v.push_back(columns[4] + " = " + std::to_string(e.start_time));
|
||||||
v.push_back(columns[5] + " = " + std::to_string(e.duration));
|
v.push_back(columns[5] + " = " + std::to_string(e.duration));
|
||||||
v.push_back(columns[6] + " = " + std::to_string(e.never_expires));
|
v.push_back(columns[6] + " = " + std::to_string(e.expire_at));
|
||||||
v.push_back(columns[7] + " = '" + Strings::Escape(e.notes) + "'");
|
v.push_back(columns[7] + " = " + std::to_string(e.never_expires));
|
||||||
|
v.push_back(columns[8] + " = '" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -218,6 +224,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.is_global));
|
v.push_back(std::to_string(e.is_global));
|
||||||
v.push_back(std::to_string(e.start_time));
|
v.push_back(std::to_string(e.start_time));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.never_expires));
|
v.push_back(std::to_string(e.never_expires));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -255,6 +262,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.is_global));
|
v.push_back(std::to_string(e.is_global));
|
||||||
v.push_back(std::to_string(e.start_time));
|
v.push_back(std::to_string(e.start_time));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.never_expires));
|
v.push_back(std::to_string(e.never_expires));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -296,8 +304,9 @@ public:
|
|||||||
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||||
e.never_expires = row[6] ? static_cast<uint8_t>(strtoul(row[6], nullptr, 10)) : 0;
|
e.expire_at = row[6] ? strtoull(row[6], nullptr, 10) : 0;
|
||||||
e.notes = row[7] ? row[7] : "";
|
e.never_expires = row[7] ? static_cast<uint8_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.notes = row[8] ? row[8] : "";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -328,8 +337,9 @@ public:
|
|||||||
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||||
e.never_expires = row[6] ? static_cast<uint8_t>(strtoul(row[6], nullptr, 10)) : 0;
|
e.expire_at = row[6] ? strtoull(row[6], nullptr, 10) : 0;
|
||||||
e.notes = row[7] ? row[7] : "";
|
e.never_expires = row[7] ? static_cast<uint8_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.notes = row[8] ? row[8] : "";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -410,6 +420,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.is_global));
|
v.push_back(std::to_string(e.is_global));
|
||||||
v.push_back(std::to_string(e.start_time));
|
v.push_back(std::to_string(e.start_time));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.never_expires));
|
v.push_back(std::to_string(e.never_expires));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -440,6 +451,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.is_global));
|
v.push_back(std::to_string(e.is_global));
|
||||||
v.push_back(std::to_string(e.start_time));
|
v.push_back(std::to_string(e.start_time));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.never_expires));
|
v.push_back(std::to_string(e.never_expires));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
|
|||||||
@@ -19,13 +19,13 @@
|
|||||||
class BaseItemsEvolvingDetailsRepository {
|
class BaseItemsEvolvingDetailsRepository {
|
||||||
public:
|
public:
|
||||||
struct ItemsEvolvingDetails {
|
struct ItemsEvolvingDetails {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint32_t item_evo_id;
|
uint32_t item_evo_id;
|
||||||
uint32_t item_evolve_level;
|
uint32_t item_evolve_level;
|
||||||
uint32_t item_id;
|
uint32_t item_id;
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint32_t sub_type;
|
std::string sub_type;
|
||||||
int64_t required_amount;
|
int64_t required_amount;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::string PrimaryKey()
|
static std::string PrimaryKey()
|
||||||
@@ -101,7 +101,7 @@ public:
|
|||||||
e.item_evolve_level = 0;
|
e.item_evolve_level = 0;
|
||||||
e.item_id = 0;
|
e.item_id = 0;
|
||||||
e.type = 0;
|
e.type = 0;
|
||||||
e.sub_type = 0;
|
e.sub_type = "0";
|
||||||
e.required_amount = 0;
|
e.required_amount = 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
@@ -144,7 +144,7 @@ public:
|
|||||||
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.sub_type = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.sub_type = row[5] ? row[5] : "0";
|
||||||
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
@@ -183,7 +183,7 @@ public:
|
|||||||
v.push_back(columns[2] + " = " + std::to_string(e.item_evolve_level));
|
v.push_back(columns[2] + " = " + std::to_string(e.item_evolve_level));
|
||||||
v.push_back(columns[3] + " = " + std::to_string(e.item_id));
|
v.push_back(columns[3] + " = " + std::to_string(e.item_id));
|
||||||
v.push_back(columns[4] + " = " + std::to_string(e.type));
|
v.push_back(columns[4] + " = " + std::to_string(e.type));
|
||||||
v.push_back(columns[5] + " = " + std::to_string(e.sub_type));
|
v.push_back(columns[5] + " = '" + Strings::Escape(e.sub_type) + "'");
|
||||||
v.push_back(columns[6] + " = " + std::to_string(e.required_amount));
|
v.push_back(columns[6] + " = " + std::to_string(e.required_amount));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
@@ -211,7 +211,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.item_evolve_level));
|
v.push_back(std::to_string(e.item_evolve_level));
|
||||||
v.push_back(std::to_string(e.item_id));
|
v.push_back(std::to_string(e.item_id));
|
||||||
v.push_back(std::to_string(e.type));
|
v.push_back(std::to_string(e.type));
|
||||||
v.push_back(std::to_string(e.sub_type));
|
v.push_back("'" + Strings::Escape(e.sub_type) + "'");
|
||||||
v.push_back(std::to_string(e.required_amount));
|
v.push_back(std::to_string(e.required_amount));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
@@ -247,7 +247,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.item_evolve_level));
|
v.push_back(std::to_string(e.item_evolve_level));
|
||||||
v.push_back(std::to_string(e.item_id));
|
v.push_back(std::to_string(e.item_id));
|
||||||
v.push_back(std::to_string(e.type));
|
v.push_back(std::to_string(e.type));
|
||||||
v.push_back(std::to_string(e.sub_type));
|
v.push_back("'" + Strings::Escape(e.sub_type) + "'");
|
||||||
v.push_back(std::to_string(e.required_amount));
|
v.push_back(std::to_string(e.required_amount));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
@@ -287,7 +287,7 @@ public:
|
|||||||
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.sub_type = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.sub_type = row[5] ? row[5] : "0";
|
||||||
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
@@ -318,7 +318,7 @@ public:
|
|||||||
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.sub_type = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.sub_type = row[5] ? row[5] : "0";
|
||||||
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
@@ -399,7 +399,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.item_evolve_level));
|
v.push_back(std::to_string(e.item_evolve_level));
|
||||||
v.push_back(std::to_string(e.item_id));
|
v.push_back(std::to_string(e.item_id));
|
||||||
v.push_back(std::to_string(e.type));
|
v.push_back(std::to_string(e.type));
|
||||||
v.push_back(std::to_string(e.sub_type));
|
v.push_back("'" + Strings::Escape(e.sub_type) + "'");
|
||||||
v.push_back(std::to_string(e.required_amount));
|
v.push_back(std::to_string(e.required_amount));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
@@ -428,7 +428,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.item_evolve_level));
|
v.push_back(std::to_string(e.item_evolve_level));
|
||||||
v.push_back(std::to_string(e.item_id));
|
v.push_back(std::to_string(e.item_id));
|
||||||
v.push_back(std::to_string(e.type));
|
v.push_back(std::to_string(e.type));
|
||||||
v.push_back(std::to_string(e.sub_type));
|
v.push_back("'" + Strings::Escape(e.sub_type) + "'");
|
||||||
v.push_back(std::to_string(e.required_amount));
|
v.push_back(std::to_string(e.required_amount));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
|||||||
@@ -19,10 +19,11 @@
|
|||||||
class BaseRespawnTimesRepository {
|
class BaseRespawnTimesRepository {
|
||||||
public:
|
public:
|
||||||
struct RespawnTimes {
|
struct RespawnTimes {
|
||||||
int32_t id;
|
int32_t id;
|
||||||
int32_t start;
|
int32_t start;
|
||||||
int32_t duration;
|
int32_t duration;
|
||||||
int16_t instance_id;
|
uint32_t expire_at;
|
||||||
|
int16_t instance_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::string PrimaryKey()
|
static std::string PrimaryKey()
|
||||||
@@ -36,6 +37,7 @@ public:
|
|||||||
"id",
|
"id",
|
||||||
"start",
|
"start",
|
||||||
"duration",
|
"duration",
|
||||||
|
"expire_at",
|
||||||
"instance_id",
|
"instance_id",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -46,6 +48,7 @@ public:
|
|||||||
"id",
|
"id",
|
||||||
"start",
|
"start",
|
||||||
"duration",
|
"duration",
|
||||||
|
"expire_at",
|
||||||
"instance_id",
|
"instance_id",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -90,6 +93,7 @@ public:
|
|||||||
e.id = 0;
|
e.id = 0;
|
||||||
e.start = 0;
|
e.start = 0;
|
||||||
e.duration = 0;
|
e.duration = 0;
|
||||||
|
e.expire_at = 0;
|
||||||
e.instance_id = 0;
|
e.instance_id = 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
@@ -130,7 +134,8 @@ public:
|
|||||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||||
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||||
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||||
e.instance_id = row[3] ? static_cast<int16_t>(atoi(row[3])) : 0;
|
e.expire_at = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[4] ? static_cast<int16_t>(atoi(row[4])) : 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -167,7 +172,8 @@ public:
|
|||||||
v.push_back(columns[0] + " = " + std::to_string(e.id));
|
v.push_back(columns[0] + " = " + std::to_string(e.id));
|
||||||
v.push_back(columns[1] + " = " + std::to_string(e.start));
|
v.push_back(columns[1] + " = " + std::to_string(e.start));
|
||||||
v.push_back(columns[2] + " = " + std::to_string(e.duration));
|
v.push_back(columns[2] + " = " + std::to_string(e.duration));
|
||||||
v.push_back(columns[3] + " = " + std::to_string(e.instance_id));
|
v.push_back(columns[3] + " = " + std::to_string(e.expire_at));
|
||||||
|
v.push_back(columns[4] + " = " + std::to_string(e.instance_id));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -192,6 +198,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.start));
|
v.push_back(std::to_string(e.start));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.instance_id));
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
@@ -225,6 +232,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.start));
|
v.push_back(std::to_string(e.start));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.instance_id));
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
@@ -262,7 +270,8 @@ public:
|
|||||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||||
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||||
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||||
e.instance_id = row[3] ? static_cast<int16_t>(atoi(row[3])) : 0;
|
e.expire_at = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[4] ? static_cast<int16_t>(atoi(row[4])) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -290,7 +299,8 @@ public:
|
|||||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||||
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||||
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||||
e.instance_id = row[3] ? static_cast<int16_t>(atoi(row[3])) : 0;
|
e.expire_at = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[4] ? static_cast<int16_t>(atoi(row[4])) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -368,6 +378,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.start));
|
v.push_back(std::to_string(e.start));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.instance_id));
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
@@ -394,6 +405,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.start));
|
v.push_back(std::to_string(e.start));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.instance_id));
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
|||||||
@@ -0,0 +1,715 @@
|
|||||||
|
/**
|
||||||
|
* DO NOT MODIFY THIS FILE
|
||||||
|
*
|
||||||
|
* This repository was automatically generated and is NOT to be modified directly.
|
||||||
|
* Any repository modifications are meant to be made to the repository extending the base.
|
||||||
|
* Any modifications to base repositories are to be made by the generator only
|
||||||
|
*
|
||||||
|
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||||
|
* @docs https://docs.eqemu.io/developer/repositories
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EQEMU_BASE_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||||
|
#define EQEMU_BASE_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||||
|
|
||||||
|
#include "../../database.h"
|
||||||
|
#include "../../strings.h"
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
class BaseZoneStateSpawnsRepository {
|
||||||
|
public:
|
||||||
|
struct ZoneStateSpawns {
|
||||||
|
int64_t id;
|
||||||
|
uint32_t zone_id;
|
||||||
|
uint32_t instance_id;
|
||||||
|
int8_t is_corpse;
|
||||||
|
int8_t is_zone;
|
||||||
|
int32_t decay_in_seconds;
|
||||||
|
uint32_t npc_id;
|
||||||
|
uint32_t spawn2_id;
|
||||||
|
uint32_t spawngroup_id;
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
float heading;
|
||||||
|
uint32_t respawn_time;
|
||||||
|
uint32_t variance;
|
||||||
|
uint32_t grid;
|
||||||
|
int32_t current_waypoint;
|
||||||
|
int16_t path_when_zone_idle;
|
||||||
|
uint16_t condition_id;
|
||||||
|
int16_t condition_min_value;
|
||||||
|
int16_t enabled;
|
||||||
|
uint16_t anim;
|
||||||
|
std::string loot_data;
|
||||||
|
std::string entity_variables;
|
||||||
|
std::string buffs;
|
||||||
|
int64_t hp;
|
||||||
|
int64_t mana;
|
||||||
|
int64_t endurance;
|
||||||
|
time_t created_at;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::string PrimaryKey()
|
||||||
|
{
|
||||||
|
return std::string("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> Columns()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"id",
|
||||||
|
"zone_id",
|
||||||
|
"instance_id",
|
||||||
|
"is_corpse",
|
||||||
|
"is_zone",
|
||||||
|
"decay_in_seconds",
|
||||||
|
"npc_id",
|
||||||
|
"spawn2_id",
|
||||||
|
"spawngroup_id",
|
||||||
|
"x",
|
||||||
|
"y",
|
||||||
|
"z",
|
||||||
|
"heading",
|
||||||
|
"respawn_time",
|
||||||
|
"variance",
|
||||||
|
"grid",
|
||||||
|
"current_waypoint",
|
||||||
|
"path_when_zone_idle",
|
||||||
|
"condition_id",
|
||||||
|
"condition_min_value",
|
||||||
|
"enabled",
|
||||||
|
"anim",
|
||||||
|
"loot_data",
|
||||||
|
"entity_variables",
|
||||||
|
"buffs",
|
||||||
|
"hp",
|
||||||
|
"mana",
|
||||||
|
"endurance",
|
||||||
|
"created_at",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> SelectColumns()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"id",
|
||||||
|
"zone_id",
|
||||||
|
"instance_id",
|
||||||
|
"is_corpse",
|
||||||
|
"is_zone",
|
||||||
|
"decay_in_seconds",
|
||||||
|
"npc_id",
|
||||||
|
"spawn2_id",
|
||||||
|
"spawngroup_id",
|
||||||
|
"x",
|
||||||
|
"y",
|
||||||
|
"z",
|
||||||
|
"heading",
|
||||||
|
"respawn_time",
|
||||||
|
"variance",
|
||||||
|
"grid",
|
||||||
|
"current_waypoint",
|
||||||
|
"path_when_zone_idle",
|
||||||
|
"condition_id",
|
||||||
|
"condition_min_value",
|
||||||
|
"enabled",
|
||||||
|
"anim",
|
||||||
|
"loot_data",
|
||||||
|
"entity_variables",
|
||||||
|
"buffs",
|
||||||
|
"hp",
|
||||||
|
"mana",
|
||||||
|
"endurance",
|
||||||
|
"UNIX_TIMESTAMP(created_at)",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string ColumnsRaw()
|
||||||
|
{
|
||||||
|
return std::string(Strings::Implode(", ", Columns()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string SelectColumnsRaw()
|
||||||
|
{
|
||||||
|
return std::string(Strings::Implode(", ", SelectColumns()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string TableName()
|
||||||
|
{
|
||||||
|
return std::string("zone_state_spawns");
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseSelect()
|
||||||
|
{
|
||||||
|
return fmt::format(
|
||||||
|
"SELECT {} FROM {}",
|
||||||
|
SelectColumnsRaw(),
|
||||||
|
TableName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseInsert()
|
||||||
|
{
|
||||||
|
return fmt::format(
|
||||||
|
"INSERT INTO {} ({}) ",
|
||||||
|
TableName(),
|
||||||
|
ColumnsRaw()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ZoneStateSpawns NewEntity()
|
||||||
|
{
|
||||||
|
ZoneStateSpawns e{};
|
||||||
|
|
||||||
|
e.id = 0;
|
||||||
|
e.zone_id = 0;
|
||||||
|
e.instance_id = 0;
|
||||||
|
e.is_corpse = 0;
|
||||||
|
e.is_zone = 0;
|
||||||
|
e.decay_in_seconds = 0;
|
||||||
|
e.npc_id = 0;
|
||||||
|
e.spawn2_id = 0;
|
||||||
|
e.spawngroup_id = 0;
|
||||||
|
e.x = 0;
|
||||||
|
e.y = 0;
|
||||||
|
e.z = 0;
|
||||||
|
e.heading = 0;
|
||||||
|
e.respawn_time = 0;
|
||||||
|
e.variance = 0;
|
||||||
|
e.grid = 0;
|
||||||
|
e.current_waypoint = 0;
|
||||||
|
e.path_when_zone_idle = 0;
|
||||||
|
e.condition_id = 0;
|
||||||
|
e.condition_min_value = 0;
|
||||||
|
e.enabled = 1;
|
||||||
|
e.anim = 0;
|
||||||
|
e.loot_data = "";
|
||||||
|
e.entity_variables = "";
|
||||||
|
e.buffs = "";
|
||||||
|
e.hp = 0;
|
||||||
|
e.mana = 0;
|
||||||
|
e.endurance = 0;
|
||||||
|
e.created_at = 0;
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ZoneStateSpawns GetZoneStateSpawns(
|
||||||
|
const std::vector<ZoneStateSpawns> &zone_state_spawnss,
|
||||||
|
int zone_state_spawns_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
for (auto &zone_state_spawns : zone_state_spawnss) {
|
||||||
|
if (zone_state_spawns.id == zone_state_spawns_id) {
|
||||||
|
return zone_state_spawns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
static ZoneStateSpawns FindOne(
|
||||||
|
Database& db,
|
||||||
|
int zone_state_spawns_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} WHERE {} = {} LIMIT 1",
|
||||||
|
BaseSelect(),
|
||||||
|
PrimaryKey(),
|
||||||
|
zone_state_spawns_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto row = results.begin();
|
||||||
|
if (results.RowCount() == 1) {
|
||||||
|
ZoneStateSpawns e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoll(row[0], nullptr, 10) : 0;
|
||||||
|
e.zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
|
e.is_corpse = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
|
e.is_zone = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
|
e.decay_in_seconds = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||||
|
e.npc_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||||
|
e.spawn2_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.spawngroup_id = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||||
|
e.x = row[9] ? strtof(row[9], nullptr) : 0;
|
||||||
|
e.y = row[10] ? strtof(row[10], nullptr) : 0;
|
||||||
|
e.z = row[11] ? strtof(row[11], nullptr) : 0;
|
||||||
|
e.heading = row[12] ? strtof(row[12], nullptr) : 0;
|
||||||
|
e.respawn_time = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||||
|
e.variance = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
|
e.grid = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||||
|
e.current_waypoint = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||||
|
e.path_when_zone_idle = row[17] ? static_cast<int16_t>(atoi(row[17])) : 0;
|
||||||
|
e.condition_id = row[18] ? static_cast<uint16_t>(strtoul(row[18], nullptr, 10)) : 0;
|
||||||
|
e.condition_min_value = row[19] ? static_cast<int16_t>(atoi(row[19])) : 0;
|
||||||
|
e.enabled = row[20] ? static_cast<int16_t>(atoi(row[20])) : 1;
|
||||||
|
e.anim = row[21] ? static_cast<uint16_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||||
|
e.loot_data = row[22] ? row[22] : "";
|
||||||
|
e.entity_variables = row[23] ? row[23] : "";
|
||||||
|
e.buffs = row[24] ? row[24] : "";
|
||||||
|
e.hp = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||||
|
e.mana = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||||
|
e.endurance = row[27] ? strtoll(row[27], nullptr, 10) : 0;
|
||||||
|
e.created_at = strtoll(row[28] ? row[28] : "-1", nullptr, 10);
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DeleteOne(
|
||||||
|
Database& db,
|
||||||
|
int zone_state_spawns_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"DELETE FROM {} WHERE {} = {}",
|
||||||
|
TableName(),
|
||||||
|
PrimaryKey(),
|
||||||
|
zone_state_spawns_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int UpdateOne(
|
||||||
|
Database& db,
|
||||||
|
const ZoneStateSpawns &e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
auto columns = Columns();
|
||||||
|
|
||||||
|
v.push_back(columns[1] + " = " + std::to_string(e.zone_id));
|
||||||
|
v.push_back(columns[2] + " = " + std::to_string(e.instance_id));
|
||||||
|
v.push_back(columns[3] + " = " + std::to_string(e.is_corpse));
|
||||||
|
v.push_back(columns[4] + " = " + std::to_string(e.is_zone));
|
||||||
|
v.push_back(columns[5] + " = " + std::to_string(e.decay_in_seconds));
|
||||||
|
v.push_back(columns[6] + " = " + std::to_string(e.npc_id));
|
||||||
|
v.push_back(columns[7] + " = " + std::to_string(e.spawn2_id));
|
||||||
|
v.push_back(columns[8] + " = " + std::to_string(e.spawngroup_id));
|
||||||
|
v.push_back(columns[9] + " = " + std::to_string(e.x));
|
||||||
|
v.push_back(columns[10] + " = " + std::to_string(e.y));
|
||||||
|
v.push_back(columns[11] + " = " + std::to_string(e.z));
|
||||||
|
v.push_back(columns[12] + " = " + std::to_string(e.heading));
|
||||||
|
v.push_back(columns[13] + " = " + std::to_string(e.respawn_time));
|
||||||
|
v.push_back(columns[14] + " = " + std::to_string(e.variance));
|
||||||
|
v.push_back(columns[15] + " = " + std::to_string(e.grid));
|
||||||
|
v.push_back(columns[16] + " = " + std::to_string(e.current_waypoint));
|
||||||
|
v.push_back(columns[17] + " = " + std::to_string(e.path_when_zone_idle));
|
||||||
|
v.push_back(columns[18] + " = " + std::to_string(e.condition_id));
|
||||||
|
v.push_back(columns[19] + " = " + std::to_string(e.condition_min_value));
|
||||||
|
v.push_back(columns[20] + " = " + std::to_string(e.enabled));
|
||||||
|
v.push_back(columns[21] + " = " + std::to_string(e.anim));
|
||||||
|
v.push_back(columns[22] + " = '" + Strings::Escape(e.loot_data) + "'");
|
||||||
|
v.push_back(columns[23] + " = '" + Strings::Escape(e.entity_variables) + "'");
|
||||||
|
v.push_back(columns[24] + " = '" + Strings::Escape(e.buffs) + "'");
|
||||||
|
v.push_back(columns[25] + " = " + std::to_string(e.hp));
|
||||||
|
v.push_back(columns[26] + " = " + std::to_string(e.mana));
|
||||||
|
v.push_back(columns[27] + " = " + std::to_string(e.endurance));
|
||||||
|
v.push_back(columns[28] + " = FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"UPDATE {} SET {} WHERE {} = {}",
|
||||||
|
TableName(),
|
||||||
|
Strings::Implode(", ", v),
|
||||||
|
PrimaryKey(),
|
||||||
|
e.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ZoneStateSpawns InsertOne(
|
||||||
|
Database& db,
|
||||||
|
ZoneStateSpawns e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.zone_id));
|
||||||
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
v.push_back(std::to_string(e.is_corpse));
|
||||||
|
v.push_back(std::to_string(e.is_zone));
|
||||||
|
v.push_back(std::to_string(e.decay_in_seconds));
|
||||||
|
v.push_back(std::to_string(e.npc_id));
|
||||||
|
v.push_back(std::to_string(e.spawn2_id));
|
||||||
|
v.push_back(std::to_string(e.spawngroup_id));
|
||||||
|
v.push_back(std::to_string(e.x));
|
||||||
|
v.push_back(std::to_string(e.y));
|
||||||
|
v.push_back(std::to_string(e.z));
|
||||||
|
v.push_back(std::to_string(e.heading));
|
||||||
|
v.push_back(std::to_string(e.respawn_time));
|
||||||
|
v.push_back(std::to_string(e.variance));
|
||||||
|
v.push_back(std::to_string(e.grid));
|
||||||
|
v.push_back(std::to_string(e.current_waypoint));
|
||||||
|
v.push_back(std::to_string(e.path_when_zone_idle));
|
||||||
|
v.push_back(std::to_string(e.condition_id));
|
||||||
|
v.push_back(std::to_string(e.condition_min_value));
|
||||||
|
v.push_back(std::to_string(e.enabled));
|
||||||
|
v.push_back(std::to_string(e.anim));
|
||||||
|
v.push_back("'" + Strings::Escape(e.loot_data) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.buffs) + "'");
|
||||||
|
v.push_back(std::to_string(e.hp));
|
||||||
|
v.push_back(std::to_string(e.mana));
|
||||||
|
v.push_back(std::to_string(e.endurance));
|
||||||
|
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES ({})",
|
||||||
|
BaseInsert(),
|
||||||
|
Strings::Implode(",", v)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (results.Success()) {
|
||||||
|
e.id = results.LastInsertedID();
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
e = NewEntity();
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int InsertMany(
|
||||||
|
Database& db,
|
||||||
|
const std::vector<ZoneStateSpawns> &entries
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> insert_chunks;
|
||||||
|
|
||||||
|
for (auto &e: entries) {
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.zone_id));
|
||||||
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
v.push_back(std::to_string(e.is_corpse));
|
||||||
|
v.push_back(std::to_string(e.is_zone));
|
||||||
|
v.push_back(std::to_string(e.decay_in_seconds));
|
||||||
|
v.push_back(std::to_string(e.npc_id));
|
||||||
|
v.push_back(std::to_string(e.spawn2_id));
|
||||||
|
v.push_back(std::to_string(e.spawngroup_id));
|
||||||
|
v.push_back(std::to_string(e.x));
|
||||||
|
v.push_back(std::to_string(e.y));
|
||||||
|
v.push_back(std::to_string(e.z));
|
||||||
|
v.push_back(std::to_string(e.heading));
|
||||||
|
v.push_back(std::to_string(e.respawn_time));
|
||||||
|
v.push_back(std::to_string(e.variance));
|
||||||
|
v.push_back(std::to_string(e.grid));
|
||||||
|
v.push_back(std::to_string(e.current_waypoint));
|
||||||
|
v.push_back(std::to_string(e.path_when_zone_idle));
|
||||||
|
v.push_back(std::to_string(e.condition_id));
|
||||||
|
v.push_back(std::to_string(e.condition_min_value));
|
||||||
|
v.push_back(std::to_string(e.enabled));
|
||||||
|
v.push_back(std::to_string(e.anim));
|
||||||
|
v.push_back("'" + Strings::Escape(e.loot_data) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.buffs) + "'");
|
||||||
|
v.push_back(std::to_string(e.hp));
|
||||||
|
v.push_back(std::to_string(e.mana));
|
||||||
|
v.push_back(std::to_string(e.endurance));
|
||||||
|
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||||
|
|
||||||
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES {}",
|
||||||
|
BaseInsert(),
|
||||||
|
Strings::Implode(",", insert_chunks)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<ZoneStateSpawns> All(Database& db)
|
||||||
|
{
|
||||||
|
std::vector<ZoneStateSpawns> all_entries;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{}",
|
||||||
|
BaseSelect()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
all_entries.reserve(results.RowCount());
|
||||||
|
|
||||||
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
ZoneStateSpawns e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoll(row[0], nullptr, 10) : 0;
|
||||||
|
e.zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
|
e.is_corpse = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
|
e.is_zone = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
|
e.decay_in_seconds = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||||
|
e.npc_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||||
|
e.spawn2_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.spawngroup_id = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||||
|
e.x = row[9] ? strtof(row[9], nullptr) : 0;
|
||||||
|
e.y = row[10] ? strtof(row[10], nullptr) : 0;
|
||||||
|
e.z = row[11] ? strtof(row[11], nullptr) : 0;
|
||||||
|
e.heading = row[12] ? strtof(row[12], nullptr) : 0;
|
||||||
|
e.respawn_time = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||||
|
e.variance = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
|
e.grid = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||||
|
e.current_waypoint = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||||
|
e.path_when_zone_idle = row[17] ? static_cast<int16_t>(atoi(row[17])) : 0;
|
||||||
|
e.condition_id = row[18] ? static_cast<uint16_t>(strtoul(row[18], nullptr, 10)) : 0;
|
||||||
|
e.condition_min_value = row[19] ? static_cast<int16_t>(atoi(row[19])) : 0;
|
||||||
|
e.enabled = row[20] ? static_cast<int16_t>(atoi(row[20])) : 1;
|
||||||
|
e.anim = row[21] ? static_cast<uint16_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||||
|
e.loot_data = row[22] ? row[22] : "";
|
||||||
|
e.entity_variables = row[23] ? row[23] : "";
|
||||||
|
e.buffs = row[24] ? row[24] : "";
|
||||||
|
e.hp = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||||
|
e.mana = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||||
|
e.endurance = row[27] ? strtoll(row[27], nullptr, 10) : 0;
|
||||||
|
e.created_at = strtoll(row[28] ? row[28] : "-1", nullptr, 10);
|
||||||
|
|
||||||
|
all_entries.push_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<ZoneStateSpawns> GetWhere(Database& db, const std::string &where_filter)
|
||||||
|
{
|
||||||
|
std::vector<ZoneStateSpawns> all_entries;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} WHERE {}",
|
||||||
|
BaseSelect(),
|
||||||
|
where_filter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
all_entries.reserve(results.RowCount());
|
||||||
|
|
||||||
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
ZoneStateSpawns e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoll(row[0], nullptr, 10) : 0;
|
||||||
|
e.zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
|
e.is_corpse = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
|
e.is_zone = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
|
e.decay_in_seconds = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||||
|
e.npc_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||||
|
e.spawn2_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.spawngroup_id = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||||
|
e.x = row[9] ? strtof(row[9], nullptr) : 0;
|
||||||
|
e.y = row[10] ? strtof(row[10], nullptr) : 0;
|
||||||
|
e.z = row[11] ? strtof(row[11], nullptr) : 0;
|
||||||
|
e.heading = row[12] ? strtof(row[12], nullptr) : 0;
|
||||||
|
e.respawn_time = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||||
|
e.variance = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
|
e.grid = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||||
|
e.current_waypoint = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||||
|
e.path_when_zone_idle = row[17] ? static_cast<int16_t>(atoi(row[17])) : 0;
|
||||||
|
e.condition_id = row[18] ? static_cast<uint16_t>(strtoul(row[18], nullptr, 10)) : 0;
|
||||||
|
e.condition_min_value = row[19] ? static_cast<int16_t>(atoi(row[19])) : 0;
|
||||||
|
e.enabled = row[20] ? static_cast<int16_t>(atoi(row[20])) : 1;
|
||||||
|
e.anim = row[21] ? static_cast<uint16_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||||
|
e.loot_data = row[22] ? row[22] : "";
|
||||||
|
e.entity_variables = row[23] ? row[23] : "";
|
||||||
|
e.buffs = row[24] ? row[24] : "";
|
||||||
|
e.hp = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||||
|
e.mana = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||||
|
e.endurance = row[27] ? strtoll(row[27], nullptr, 10) : 0;
|
||||||
|
e.created_at = strtoll(row[28] ? row[28] : "-1", nullptr, 10);
|
||||||
|
|
||||||
|
all_entries.push_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DeleteWhere(Database& db, const std::string &where_filter)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"DELETE FROM {} WHERE {}",
|
||||||
|
TableName(),
|
||||||
|
where_filter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Truncate(Database& db)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"TRUNCATE TABLE {}",
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64 GetMaxId(Database& db)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT COALESCE(MAX({}), 0) FROM {}",
|
||||||
|
PrimaryKey(),
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64 Count(Database& db, const std::string &where_filter = "")
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT COUNT(*) FROM {} {}",
|
||||||
|
TableName(),
|
||||||
|
(where_filter.empty() ? "" : "WHERE " + where_filter)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseReplace()
|
||||||
|
{
|
||||||
|
return fmt::format(
|
||||||
|
"REPLACE INTO {} ({}) ",
|
||||||
|
TableName(),
|
||||||
|
ColumnsRaw()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ReplaceOne(
|
||||||
|
Database& db,
|
||||||
|
const ZoneStateSpawns &e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.zone_id));
|
||||||
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
v.push_back(std::to_string(e.is_corpse));
|
||||||
|
v.push_back(std::to_string(e.is_zone));
|
||||||
|
v.push_back(std::to_string(e.decay_in_seconds));
|
||||||
|
v.push_back(std::to_string(e.npc_id));
|
||||||
|
v.push_back(std::to_string(e.spawn2_id));
|
||||||
|
v.push_back(std::to_string(e.spawngroup_id));
|
||||||
|
v.push_back(std::to_string(e.x));
|
||||||
|
v.push_back(std::to_string(e.y));
|
||||||
|
v.push_back(std::to_string(e.z));
|
||||||
|
v.push_back(std::to_string(e.heading));
|
||||||
|
v.push_back(std::to_string(e.respawn_time));
|
||||||
|
v.push_back(std::to_string(e.variance));
|
||||||
|
v.push_back(std::to_string(e.grid));
|
||||||
|
v.push_back(std::to_string(e.current_waypoint));
|
||||||
|
v.push_back(std::to_string(e.path_when_zone_idle));
|
||||||
|
v.push_back(std::to_string(e.condition_id));
|
||||||
|
v.push_back(std::to_string(e.condition_min_value));
|
||||||
|
v.push_back(std::to_string(e.enabled));
|
||||||
|
v.push_back(std::to_string(e.anim));
|
||||||
|
v.push_back("'" + Strings::Escape(e.loot_data) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.buffs) + "'");
|
||||||
|
v.push_back(std::to_string(e.hp));
|
||||||
|
v.push_back(std::to_string(e.mana));
|
||||||
|
v.push_back(std::to_string(e.endurance));
|
||||||
|
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES ({})",
|
||||||
|
BaseReplace(),
|
||||||
|
Strings::Implode(",", v)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ReplaceMany(
|
||||||
|
Database& db,
|
||||||
|
const std::vector<ZoneStateSpawns> &entries
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> insert_chunks;
|
||||||
|
|
||||||
|
for (auto &e: entries) {
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.zone_id));
|
||||||
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
v.push_back(std::to_string(e.is_corpse));
|
||||||
|
v.push_back(std::to_string(e.is_zone));
|
||||||
|
v.push_back(std::to_string(e.decay_in_seconds));
|
||||||
|
v.push_back(std::to_string(e.npc_id));
|
||||||
|
v.push_back(std::to_string(e.spawn2_id));
|
||||||
|
v.push_back(std::to_string(e.spawngroup_id));
|
||||||
|
v.push_back(std::to_string(e.x));
|
||||||
|
v.push_back(std::to_string(e.y));
|
||||||
|
v.push_back(std::to_string(e.z));
|
||||||
|
v.push_back(std::to_string(e.heading));
|
||||||
|
v.push_back(std::to_string(e.respawn_time));
|
||||||
|
v.push_back(std::to_string(e.variance));
|
||||||
|
v.push_back(std::to_string(e.grid));
|
||||||
|
v.push_back(std::to_string(e.current_waypoint));
|
||||||
|
v.push_back(std::to_string(e.path_when_zone_idle));
|
||||||
|
v.push_back(std::to_string(e.condition_id));
|
||||||
|
v.push_back(std::to_string(e.condition_min_value));
|
||||||
|
v.push_back(std::to_string(e.enabled));
|
||||||
|
v.push_back(std::to_string(e.anim));
|
||||||
|
v.push_back("'" + Strings::Escape(e.loot_data) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.buffs) + "'");
|
||||||
|
v.push_back(std::to_string(e.hp));
|
||||||
|
v.push_back(std::to_string(e.mana));
|
||||||
|
v.push_back(std::to_string(e.endurance));
|
||||||
|
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||||
|
|
||||||
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES {}",
|
||||||
|
BaseReplace(),
|
||||||
|
Strings::Implode(",", insert_chunks)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //EQEMU_BASE_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||||
@@ -7,49 +7,11 @@
|
|||||||
|
|
||||||
class InstanceListRepository: public BaseInstanceListRepository {
|
class InstanceListRepository: public BaseInstanceListRepository {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
|
||||||
* This file was auto generated and can be modified and extended upon
|
|
||||||
*
|
|
||||||
* Base repository methods are automatically
|
|
||||||
* generated in the "base" version of this repository. The base repository
|
|
||||||
* is immutable and to be left untouched, while methods in this class
|
|
||||||
* are used as extension methods for more specific persistence-layer
|
|
||||||
* accessors or mutators.
|
|
||||||
*
|
|
||||||
* Base Methods (Subject to be expanded upon in time)
|
|
||||||
*
|
|
||||||
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
|
||||||
*
|
|
||||||
* InsertOne
|
|
||||||
* UpdateOne
|
|
||||||
* DeleteOne
|
|
||||||
* FindOne
|
|
||||||
* GetWhere(std::string where_filter)
|
|
||||||
* DeleteWhere(std::string where_filter)
|
|
||||||
* InsertMany
|
|
||||||
* All
|
|
||||||
*
|
|
||||||
* Example custom methods in a repository
|
|
||||||
*
|
|
||||||
* InstanceListRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
|
||||||
* InstanceListRepository::GetWhereNeverExpires()
|
|
||||||
* InstanceListRepository::GetWhereXAndY()
|
|
||||||
* InstanceListRepository::DeleteWhereXAndY()
|
|
||||||
*
|
|
||||||
* Most of the above could be covered by base methods, but if you as a developer
|
|
||||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
|
||||||
* method that can be re-used easily elsewhere especially if it can use a base repository
|
|
||||||
* method and encapsulate filters there
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Custom extended repository methods here
|
|
||||||
|
|
||||||
static int UpdateDuration(Database& db, uint16 instance_id, uint32_t new_duration)
|
static int UpdateDuration(Database& db, uint16 instance_id, uint32_t new_duration)
|
||||||
{
|
{
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"UPDATE `{}` SET `duration` = {} WHERE `{}` = {}",
|
"UPDATE `{}` SET `duration` = {}, `expire_at` = (`duration` + `start_time`) WHERE `{}` = {}",
|
||||||
TableName(),
|
TableName(),
|
||||||
new_duration,
|
new_duration,
|
||||||
PrimaryKey(),
|
PrimaryKey(),
|
||||||
@@ -65,7 +27,7 @@ public:
|
|||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
SQL(
|
SQL(
|
||||||
SELECT ((start_time + duration) - UNIX_TIMESTAMP()) AS `remaining` FROM `{}`
|
SELECT (`expire_at` - UNIX_TIMESTAMP()) AS `remaining` FROM `{}`
|
||||||
WHERE `id` = {}
|
WHERE `id` = {}
|
||||||
),
|
),
|
||||||
TableName(),
|
TableName(),
|
||||||
|
|||||||
@@ -8,47 +8,11 @@
|
|||||||
class RespawnTimesRepository: public BaseRespawnTimesRepository {
|
class RespawnTimesRepository: public BaseRespawnTimesRepository {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
|
||||||
* This file was auto generated and can be modified and extended upon
|
|
||||||
*
|
|
||||||
* Base repository methods are automatically
|
|
||||||
* generated in the "base" version of this repository. The base repository
|
|
||||||
* is immutable and to be left untouched, while methods in this class
|
|
||||||
* are used as extension methods for more specific persistence-layer
|
|
||||||
* accessors or mutators.
|
|
||||||
*
|
|
||||||
* Base Methods (Subject to be expanded upon in time)
|
|
||||||
*
|
|
||||||
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
|
||||||
*
|
|
||||||
* InsertOne
|
|
||||||
* UpdateOne
|
|
||||||
* DeleteOne
|
|
||||||
* FindOne
|
|
||||||
* GetWhere(std::string where_filter)
|
|
||||||
* DeleteWhere(std::string where_filter)
|
|
||||||
* InsertMany
|
|
||||||
* All
|
|
||||||
*
|
|
||||||
* Example custom methods in a repository
|
|
||||||
*
|
|
||||||
* RespawnTimesRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
|
||||||
* RespawnTimesRepository::GetWhereNeverExpires()
|
|
||||||
* RespawnTimesRepository::GetWhereXAndY()
|
|
||||||
* RespawnTimesRepository::DeleteWhereXAndY()
|
|
||||||
*
|
|
||||||
* Most of the above could be covered by base methods, but if you as a developer
|
|
||||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
|
||||||
* method that can be re-used easily elsewhere especially if it can use a base repository
|
|
||||||
* method and encapsulate filters there
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Custom extended repository methods here
|
|
||||||
static void ClearExpiredRespawnTimers(Database& db)
|
static void ClearExpiredRespawnTimers(Database& db)
|
||||||
{
|
{
|
||||||
db.QueryDatabase(
|
db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"DELETE FROM `{}` WHERE (`start` + `duration`) < UNIX_TIMESTAMP(NOW())",
|
"DELETE FROM `{}` WHERE `expire_at` < UNIX_TIMESTAMP(NOW())",
|
||||||
TableName()
|
TableName()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -77,6 +41,11 @@ public:
|
|||||||
|
|
||||||
return ((r.start + r.duration) - time_seconds);
|
return ((r.start + r.duration) - time_seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ClearInstanceTimers(Database &db, int32_t id)
|
||||||
|
{
|
||||||
|
RespawnTimesRepository::DeleteWhere(db, fmt::format("`instance_id` = {}", id));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //EQEMU_RESPAWN_TIMES_REPOSITORY_H
|
#endif //EQEMU_RESPAWN_TIMES_REPOSITORY_H
|
||||||
|
|||||||
@@ -54,17 +54,31 @@ public:
|
|||||||
{
|
{
|
||||||
BulkTraders_Struct all_entries{};
|
BulkTraders_Struct all_entries{};
|
||||||
std::vector<DistinctTraders_Struct> distinct_traders;
|
std::vector<DistinctTraders_Struct> distinct_traders;
|
||||||
|
MySQLRequestResult results;
|
||||||
|
|
||||||
auto results = db.QueryDatabase(fmt::format(
|
if (RuleB(Bazaar, UseAlternateBazaarSearch)) {
|
||||||
"SELECT DISTINCT(t.char_id), t.char_zone_id, t.char_zone_instance_id, t.char_entity_id, c.name "
|
results = db.QueryDatabase(fmt::format(
|
||||||
"FROM trader AS t "
|
"SELECT DISTINCT(t.char_id), t.char_zone_id, t.char_zone_instance_id, t.char_entity_id, c.name "
|
||||||
"JOIN character_data AS c ON t.char_id = c.id "
|
"FROM trader AS t "
|
||||||
"WHERE t.char_zone_instance_id = {} "
|
"JOIN character_data AS c ON t.char_id = c.id "
|
||||||
"ORDER BY t.char_zone_instance_id ASC "
|
"WHERE t.char_zone_instance_id = {} "
|
||||||
"LIMIT {}",
|
"ORDER BY t.char_zone_instance_id ASC "
|
||||||
char_zone_instance_id,
|
"LIMIT {}",
|
||||||
max_results)
|
char_zone_instance_id,
|
||||||
);
|
max_results)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
results = db.QueryDatabase(fmt::format(
|
||||||
|
"SELECT DISTINCT(t.char_id), t.char_zone_id, t.char_zone_instance_id, t.char_entity_id, c.name "
|
||||||
|
"FROM trader AS t "
|
||||||
|
"JOIN character_data AS c ON t.char_id = c.id "
|
||||||
|
"ORDER BY t.char_zone_instance_id ASC "
|
||||||
|
"LIMIT {}",
|
||||||
|
char_zone_instance_id,
|
||||||
|
max_results)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
distinct_traders.reserve(results.RowCount());
|
distinct_traders.reserve(results.RowCount());
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
#ifndef EQEMU_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||||
|
#define EQEMU_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||||
|
|
||||||
|
#include "../database.h"
|
||||||
|
#include "../strings.h"
|
||||||
|
#include "base/base_zone_state_spawns_repository.h"
|
||||||
|
|
||||||
|
class ZoneStateSpawnsRepository : public BaseZoneStateSpawnsRepository {
|
||||||
|
public:
|
||||||
|
static void PurgeInvalidZoneStates(Database &database)
|
||||||
|
{
|
||||||
|
std::string query = R"(
|
||||||
|
SELECT zone_id, instance_id
|
||||||
|
FROM zone_state_spawns
|
||||||
|
GROUP BY zone_id, instance_id
|
||||||
|
HAVING COUNT(*) = SUM(
|
||||||
|
CASE
|
||||||
|
WHEN hp = 0
|
||||||
|
AND mana = 0
|
||||||
|
AND endurance = 0
|
||||||
|
AND (loot_data IS NULL OR loot_data = '')
|
||||||
|
AND (entity_variables IS NULL OR entity_variables = '')
|
||||||
|
AND (buffs IS NULL OR buffs = '')
|
||||||
|
THEN 1 ELSE 0
|
||||||
|
END
|
||||||
|
);
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto results = database.QueryDatabase(query);
|
||||||
|
if (!results.Success()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto row: results) {
|
||||||
|
uint32 zone_id = std::stoul(row[0]);
|
||||||
|
uint32 instance_id = std::stoul(row[1]);
|
||||||
|
|
||||||
|
int rows = ZoneStateSpawnsRepository::DeleteWhere(
|
||||||
|
database,
|
||||||
|
fmt::format(
|
||||||
|
"`zone_id` = {} AND `instance_id` = {}",
|
||||||
|
zone_id,
|
||||||
|
instance_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
LogInfo(
|
||||||
|
"Purged invalid zone state data for zone [{}] instance [{}] rows [{}]",
|
||||||
|
zone_id,
|
||||||
|
instance_id,
|
||||||
|
Strings::Commify(rows)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PurgeOldZoneStates(Database &database)
|
||||||
|
{
|
||||||
|
int days = RuleI(Zone, StateSaveClearDays);
|
||||||
|
|
||||||
|
std::string query = fmt::format(
|
||||||
|
"DELETE FROM zone_state_spawns WHERE created_at < NOW() - INTERVAL {} DAY",
|
||||||
|
days
|
||||||
|
);
|
||||||
|
|
||||||
|
auto results = database.QueryDatabase(query);
|
||||||
|
if (!results.Success()) {
|
||||||
|
LogError("Failed to purge old zone state data older than {} days.", days);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.RowsAffected() > 0) {
|
||||||
|
LogInfo(
|
||||||
|
"Purged old zone state data older than days [{}] rows [{}]",
|
||||||
|
days,
|
||||||
|
Strings::Commify(results.RowsAffected())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //EQEMU_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||||
+25
-20
@@ -54,7 +54,7 @@ RULE_INT(Character, CorpseDecayTime, 604800000, "Time after which the corpse dec
|
|||||||
RULE_INT(Character, EmptyCorpseDecayTime, 10800000, "Time after which an empty corpse decays (milliseconds) DEFAULT: 10800000 (3 Hours)")
|
RULE_INT(Character, EmptyCorpseDecayTime, 10800000, "Time after which an empty corpse decays (milliseconds) DEFAULT: 10800000 (3 Hours)")
|
||||||
RULE_INT(Character, CorpseResTime, 10800000, "Time after which the corpse can no longer be resurrected (milliseconds) DEFAULT: 10800000 (3 Hours)")
|
RULE_INT(Character, CorpseResTime, 10800000, "Time after which the corpse can no longer be resurrected (milliseconds) DEFAULT: 10800000 (3 Hours)")
|
||||||
RULE_INT(Character, DuelCorpseResTime, 600000, "Time before cant res corpse after a duel (milliseconds) DEFAULT: 600000 (10 Minutes)")
|
RULE_INT(Character, DuelCorpseResTime, 600000, "Time before cant res corpse after a duel (milliseconds) DEFAULT: 600000 (10 Minutes)")
|
||||||
RULE_INT(Character, CorpseOwnerOnlineTime, 30000, "How often corpse will check if its owner is online DEFAULT: 30000 (30 Seconds)")
|
RULE_INT(Character, CorpseOwnerOnlineCheckTime, 300, "How often corpse will check if its owner is online DEFAULT: 300 (5 minutes)")
|
||||||
RULE_BOOL(Character, LeaveCorpses, true, "Setting whether you leave a corpse behind")
|
RULE_BOOL(Character, LeaveCorpses, true, "Setting whether you leave a corpse behind")
|
||||||
RULE_BOOL(Character, LeaveNakedCorpses, false, "Setting whether you leave a corpse without items")
|
RULE_BOOL(Character, LeaveNakedCorpses, false, "Setting whether you leave a corpse without items")
|
||||||
RULE_INT(Character, MaxDraggedCorpses, 2, "Maximum number of corpses you can drag at once")
|
RULE_INT(Character, MaxDraggedCorpses, 2, "Maximum number of corpses you can drag at once")
|
||||||
@@ -231,6 +231,7 @@ RULE_INT(Character, MendAlwaysSucceedValue, 199, "Value at which mend will alway
|
|||||||
RULE_BOOL(Character, SneakAlwaysSucceedOver100, false, "When sneak skill is over 100, always succeed sneak/hide. Default: false")
|
RULE_BOOL(Character, SneakAlwaysSucceedOver100, false, "When sneak skill is over 100, always succeed sneak/hide. Default: false")
|
||||||
RULE_INT(Character, BandolierSwapDelay, 0, "Bandolier swap delay in milliseconds, default is 0")
|
RULE_INT(Character, BandolierSwapDelay, 0, "Bandolier swap delay in milliseconds, default is 0")
|
||||||
RULE_BOOL(Character, EnableHackedFastCampForGM, false, "Enables hacked fast camp for GM clients, if the GM doesn't have a hacked client they'll camp like normal")
|
RULE_BOOL(Character, EnableHackedFastCampForGM, false, "Enables hacked fast camp for GM clients, if the GM doesn't have a hacked client they'll camp like normal")
|
||||||
|
RULE_BOOL(Character, AlwaysAllowNameChange, false, "Enable this option to allow /changename to work without enabling a name change via scripts.")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Mercs)
|
RULE_CATEGORY(Mercs)
|
||||||
@@ -260,6 +261,7 @@ RULE_INT(Guild, TributeTime, 600000, "Time in ms for guild tributes. Default is
|
|||||||
RULE_INT(Guild, TributeTimeRefreshInterval, 180000, "Time in ms to send all guild members a Tribute Time refresh. Default is 3 mins.")
|
RULE_INT(Guild, TributeTimeRefreshInterval, 180000, "Time in ms to send all guild members a Tribute Time refresh. Default is 3 mins.")
|
||||||
RULE_INT(Guild, TributePlatConversionRate, 10, "The conversion rate of platinum donations. Default is 10 guild favor to 1 platinum.")
|
RULE_INT(Guild, TributePlatConversionRate, 10, "The conversion rate of platinum donations. Default is 10 guild favor to 1 platinum.")
|
||||||
RULE_BOOL(Guild, UseCharacterMaxLevelForGuildTributes, true, "Guild Tributes will adhere to Character:MaxLevel. Default is true.")
|
RULE_BOOL(Guild, UseCharacterMaxLevelForGuildTributes, true, "Guild Tributes will adhere to Character:MaxLevel. Default is true.")
|
||||||
|
RULE_BOOL(Guild, EnableLFGuild, false, "Enable the LFGuild system (Requires queryserv)")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Skills)
|
RULE_CATEGORY(Skills)
|
||||||
@@ -289,6 +291,7 @@ RULE_BOOL(Pets, ClientPetsUseOwnerNameInLastName, true, "Disable this to keep cl
|
|||||||
RULE_BOOL(Pets, CanTakeNoDrop, false, "Setting whether anyone can give no-drop items to pets")
|
RULE_BOOL(Pets, CanTakeNoDrop, false, "Setting whether anyone can give no-drop items to pets")
|
||||||
RULE_INT(Pets, PetTauntRange, 150, "Range at which a pet will taunt targets.")
|
RULE_INT(Pets, PetTauntRange, 150, "Range at which a pet will taunt targets.")
|
||||||
RULE_BOOL(Pets, AlwaysAllowPetRename, false, "Enable this option to allow /changepetname to work without enabling a pet name change via scripts.")
|
RULE_BOOL(Pets, AlwaysAllowPetRename, false, "Enable this option to allow /changepetname to work without enabling a pet name change via scripts.")
|
||||||
|
RULE_BOOL(Pets, PetsRequireLoS, false, "Whether or not pets require line of sight to be told to attack their target")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(GM)
|
RULE_CATEGORY(GM)
|
||||||
@@ -344,6 +347,7 @@ RULE_STRING(World, SupportedClients, "RoF2", "Comma-delimited list of clients to
|
|||||||
RULE_STRING(World, CustomFilesKey, "", "Enable if the server requires custom files and sends a key to validate. Empty string to disable. Example: eqcustom_v1")
|
RULE_STRING(World, CustomFilesKey, "", "Enable if the server requires custom files and sends a key to validate. Empty string to disable. Example: eqcustom_v1")
|
||||||
RULE_STRING(World, CustomFilesUrl, "github.com/knervous/eqnexus/releases", "URL to display at character select if client is missing custom files")
|
RULE_STRING(World, CustomFilesUrl, "github.com/knervous/eqnexus/releases", "URL to display at character select if client is missing custom files")
|
||||||
RULE_INT(World, CustomFilesAdminLevel, 20, "Admin level at which custom file key is not required when CustomFilesKey is specified")
|
RULE_INT(World, CustomFilesAdminLevel, 20, "Admin level at which custom file key is not required when CustomFilesKey is specified")
|
||||||
|
RULE_BOOL(World, RealTimeCalculateGuilds, false, "(Temp feature flag) If true, guilds will be calculated in real time instead of at zone boot. This is a performance hit but allows for more dynamic guilds.")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Zone)
|
RULE_CATEGORY(Zone)
|
||||||
@@ -374,6 +378,11 @@ RULE_BOOL(Zone, AllowCrossZoneSpellsOnBots, false, "Set to true to allow cross z
|
|||||||
RULE_BOOL(Zone, AllowCrossZoneSpellsOnMercs, false, "Set to true to allow cross zone spells (cast/remove) to affect mercenaries")
|
RULE_BOOL(Zone, AllowCrossZoneSpellsOnMercs, false, "Set to true to allow cross zone spells (cast/remove) to affect mercenaries")
|
||||||
RULE_BOOL(Zone, AllowCrossZoneSpellsOnPets, false, "Set to true to allow cross zone spells (cast/remove) to affect pets")
|
RULE_BOOL(Zone, AllowCrossZoneSpellsOnPets, false, "Set to true to allow cross zone spells (cast/remove) to affect pets")
|
||||||
RULE_BOOL(Zone, ZoneShardQuestMenuOnly, false, "Set to true if you only want quests to show the zone shard menu")
|
RULE_BOOL(Zone, ZoneShardQuestMenuOnly, false, "Set to true if you only want quests to show the zone shard menu")
|
||||||
|
RULE_BOOL(Zone, StateSaveEntityVariables, true, "Set to true if you want buffs to be saved on shutdown")
|
||||||
|
RULE_BOOL(Zone, StateSaveBuffs, true, "Set to true if you want buffs to be saved on shutdown")
|
||||||
|
RULE_INT(Zone, StateSaveClearDays, 7, "Clears state save data older than this many days")
|
||||||
|
RULE_BOOL(Zone, StateSavingOnShutdown, true, "Set to true if you want zones to save state on shutdown (npcs, corpses, loot, entity variables, buffs etc.)")
|
||||||
|
RULE_INT(Zone, UpdateWhoTimer, 120, "Seconds between updates to /who list, CLE stale timer")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Map)
|
RULE_CATEGORY(Map)
|
||||||
@@ -383,9 +392,10 @@ RULE_BOOL(Map, MobZVisualDebug, false, "Displays spell effects determining wheth
|
|||||||
RULE_BOOL(Map, MobPathingVisualDebug, false, "Displays nodes in pathing points in realtime to help with visual debugging")
|
RULE_BOOL(Map, MobPathingVisualDebug, false, "Displays nodes in pathing points in realtime to help with visual debugging")
|
||||||
RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "At runtime in SendTo: maximum change in Z to allow the BestZ code to apply")
|
RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "At runtime in SendTo: maximum change in Z to allow the BestZ code to apply")
|
||||||
RULE_INT(Map, FindBestZHeightAdjust, 1, "Adds this to the current Z before seeking the best Z position")
|
RULE_INT(Map, FindBestZHeightAdjust, 1, "Adds this to the current Z before seeking the best Z position")
|
||||||
RULE_BOOL(Map, CheckForLoSCheat, false, "Runs predefined zone checks to check for LoS cheating through doors and such.")
|
RULE_BOOL(Map, CheckForDoorLoSCheat, true, "Runs LoS checks to prevent cheating through doors.")
|
||||||
RULE_BOOL(Map, EnableLoSCheatExemptions, false, "Enables exemptions for the LoS Cheat check.")
|
RULE_BOOL(Map, EnableLoSCheatExemptions, false, "Enables exemptions for the LoS Cheat check. Must modify source to create these.")
|
||||||
RULE_REAL(Map, RangeCheckForLoSCheat, 20.0, "Default 20.0. Range to check if one is within range of a door.")
|
RULE_REAL(Map, RangeCheckForDoorLoSCheat, 250.0, "Default 250.0. Range to check if a door is blocking LoS from the target.")
|
||||||
|
RULE_STRING(Map, ZonesToCheckDoorCheat, "89,103", "Zones that will check for the door LoS cheat. You can leave it blank to disable, 'all' to check all zones or use a comma-delimited list of zones. Default Sebilis & Chardok")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Pathing)
|
RULE_CATEGORY(Pathing)
|
||||||
@@ -528,6 +538,7 @@ RULE_INT(Spells, TargetedAOEMaxTargets, 4, "Max number of targets a Targeted AOE
|
|||||||
RULE_INT(Spells, PointBlankAOEMaxTargets, 0, "Max number of targets a Point-Blank AOE spell can cast on. Set to 0 for no limit.")
|
RULE_INT(Spells, PointBlankAOEMaxTargets, 0, "Max number of targets a Point-Blank AOE spell can cast on. Set to 0 for no limit.")
|
||||||
RULE_INT(Spells, DefaultAOEMaxTargets, 0, "Max number of targets that an AOE spell which does not meet other descriptions can cast on. Set to 0 for no limit.")
|
RULE_INT(Spells, DefaultAOEMaxTargets, 0, "Max number of targets that an AOE spell which does not meet other descriptions can cast on. Set to 0 for no limit.")
|
||||||
RULE_BOOL(Spells, AllowFocusOnSkillDamageSpells, false, "Allow focus effects 185, 459, and 482 to enhance SkillAttack spell effect 193")
|
RULE_BOOL(Spells, AllowFocusOnSkillDamageSpells, false, "Allow focus effects 185, 459, and 482 to enhance SkillAttack spell effect 193")
|
||||||
|
RULE_STRING(Spells, AlwaysStackSpells, "", "Comma-Seperated list of spell IDs to always stack with every other spell, except themselves.")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Combat)
|
RULE_CATEGORY(Combat)
|
||||||
@@ -808,6 +819,7 @@ RULE_INT(Bots, PercentChanceToCastDispel, 75, "The chance for a bot to attempt t
|
|||||||
RULE_INT(Bots, PercentChanceToCastInCombatBuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastInCombatBuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastHateLine, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastHateLine, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastMez, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastMez, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
|
RULE_INT(Bots, PercentChanceToCastAEMez, 40, "The chance for a bot to attempt to cast the given spell type in combat. Default 40%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastSlow, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastSlow, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastDebuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastDebuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastCure, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastCure, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
@@ -819,8 +831,6 @@ RULE_INT(Bots, MinDelayBetweenInCombatCastAttempts, 500, "The minimum delay in m
|
|||||||
RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2500ms.")
|
RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2500ms.")
|
||||||
RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 1000, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 1000ms.")
|
RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 1000, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 1000ms.")
|
||||||
RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 2500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 2500ms.")
|
RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 2500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 2500ms.")
|
||||||
RULE_INT(Bots, MezChance, 60, "60 Default. Chance for a bot to attempt to Mez a target after validating it is eligible.")
|
|
||||||
RULE_INT(Bots, AEMezChance, 35, "35 Default. Chance for a bot to attempt to AE Mez targets after validating they are eligible.")
|
|
||||||
RULE_INT(Bots, MezSuccessDelay, 2500, "2500 (2.5 sec) Default. Delay between successful Mez attempts.")
|
RULE_INT(Bots, MezSuccessDelay, 2500, "2500 (2.5 sec) Default. Delay between successful Mez attempts.")
|
||||||
RULE_INT(Bots, AEMezSuccessDelay, 5000, "5000 (5 sec) Default. Delay between successful AEMez attempts.")
|
RULE_INT(Bots, AEMezSuccessDelay, 5000, "5000 (5 sec) Default. Delay between successful AEMez attempts.")
|
||||||
RULE_INT(Bots, MezFailDelay, 1250, "1250 (1.25 sec) Default. Delay between failed Mez attempts.")
|
RULE_INT(Bots, MezFailDelay, 1250, "1250 (1.25 sec) Default. Delay between failed Mez attempts.")
|
||||||
@@ -830,14 +840,14 @@ RULE_INT(Bots, MinGroupCureTargets, 3, "Minimum number of targets in valid range
|
|||||||
RULE_INT(Bots, MinTargetsForAESpell, 3, "Minimum number of targets in valid range that are required for an AE spell to cast. Default 3.")
|
RULE_INT(Bots, MinTargetsForAESpell, 3, "Minimum number of targets in valid range that are required for an AE spell to cast. Default 3.")
|
||||||
RULE_INT(Bots, MinTargetsForGroupSpell, 3, "Minimum number of targets in valid range that are required for an group spell to cast. Default 3.")
|
RULE_INT(Bots, MinTargetsForGroupSpell, 3, "Minimum number of targets in valid range that are required for an group spell to cast. Default 3.")
|
||||||
RULE_BOOL(Bots, AllowBuffingHealingFamiliars, false, "Determines if bots are allowed to buff and heal familiars. Default false.")
|
RULE_BOOL(Bots, AllowBuffingHealingFamiliars, false, "Determines if bots are allowed to buff and heal familiars. Default false.")
|
||||||
RULE_BOOL(Bots, RunSpellTypeChecksOnSpawn, false, "This will run a serious of checks on spell types and output errors to LogBotSpellTypeChecks")
|
RULE_BOOL(Bots, RunSpellTypeChecksOnBoot, false, "This will run a series of checks to find potential errors in your bot_spells_entries table on boot and output to LogBotSpellTypeChecks")
|
||||||
RULE_BOOL(Bots, UseParentSpellTypeForChecks, true, "This will check only the parent instead of AE/Group/Pet types (ex: AENukes/AERains/PBAENukes fall under Nukes or PetBuffs fall under buffs) when RunSpellTypeChecksOnSpawn fires")
|
RULE_BOOL(Bots, UseParentSpellTypeForChecks, true, "This will check only the parent instead of AE/Group/Pet types (ex: AENukes/AERains/PBAENukes fall under Nukes or PetBuffs fall under buffs) when RunSpellTypeChecksOnSpawn fires")
|
||||||
RULE_BOOL(Bots, AllowForcedCastsBySpellID, true, "If enabled, players can use ^cast spellid # to cast a specific spell by ID that is in their spell list")
|
RULE_BOOL(Bots, AllowForcedCastsBySpellID, true, "If enabled, players can use ^cast spellid # to cast a specific spell by ID that is in their spell list")
|
||||||
RULE_BOOL(Bots, AllowCastAAs, true, "If enabled, players can use ^cast aa to cast a clickable AA")
|
RULE_BOOL(Bots, AllowCastAAs, true, "If enabled, players can use ^cast aa to cast a clickable AA")
|
||||||
RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summon their epic pets following the rules AllowMagicianEpicPetLevel")
|
RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summon their epic pets following the rules AllowMagicianEpicPetLevel")
|
||||||
RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level")
|
RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level")
|
||||||
RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement")
|
RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement")
|
||||||
RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their spell list to cast.")
|
RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their usable spell list to cast. Empty uses Manifest Elements - 'SumMageMultiElement'")
|
||||||
RULE_INT(Bots, ReclaimEnergySpellID, 331, "Spell ID for reclaim energy when using ^petsettype. Default 331")
|
RULE_INT(Bots, ReclaimEnergySpellID, 331, "Spell ID for reclaim energy when using ^petsettype. Default 331")
|
||||||
RULE_BOOL(Bots, UseSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.")
|
RULE_BOOL(Bots, UseSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.")
|
||||||
RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will be cast to pull by bots")
|
RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will be cast to pull by bots")
|
||||||
@@ -848,17 +858,7 @@ RULE_BOOL(Bots, BotArcheryConsumesAmmo, true, "Set to false to disable Archery A
|
|||||||
RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption")
|
RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption")
|
||||||
RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).")
|
RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).")
|
||||||
RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.")
|
RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.")
|
||||||
RULE_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.")
|
RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, true, "If true, when bots are at max melee distance, special abilities including taunt will be disabled. Default True.")
|
||||||
RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "If UseFlatNormalMeleeRange is enabled, multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.")
|
|
||||||
RULE_REAL(Bots, PercentMinMeleeDistance, 0.75, "Multiplier of the their melee range - Minimum distance from target a bot will stand while in melee combat before trying to adjust. 0.60 Recommended.")
|
|
||||||
RULE_REAL(Bots, MaxDistanceForMelee, 20, "Maximum distance bots will stand for melee. Default 20 to allow all special attacks to land.")
|
|
||||||
RULE_REAL(Bots, TauntNormalMeleeRangeDistance, 0.50, "Multiplier of the max melee range at which a taunting bot will stand in melee combat. 0.50 Recommended, closer than others .")
|
|
||||||
RULE_REAL(Bots, PercentTauntMinMeleeDistance, 0.40, "Multiplier of their melee range - Minimum distance from target a taunting bot will stand while in melee combat before trying to adjust. 0.25 Recommended.")
|
|
||||||
RULE_REAL(Bots, PercentMaxMeleeRangeDistance, 0.95, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.95 Recommended, max melee while disabling special attacks/taunt.")
|
|
||||||
RULE_REAL(Bots, PercentMinMaxMeleeRangeDistance, 0.75, "Multiplier of the closest max melee range at which a bot will stand in melee combat before trying to adjust. 0.75 Recommended, max melee while disabling special attacks/taunt.")
|
|
||||||
RULE_BOOL(Bots, TauntingBotsFollowTopHate, true, "True Default. If true, bots that are taunting will attempt to stick with whoever currently is top hate.")
|
|
||||||
RULE_INT(Bots, DistanceTauntingBotsStickMainHate, 10, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.")
|
|
||||||
RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, true, "True Default. If true, when bots are at max melee distance, special abilities including taunt will be disabled.")
|
|
||||||
RULE_INT(Bots, MinJitterTimer, 500, "Minimum ms between bot movement jitter checks.")
|
RULE_INT(Bots, MinJitterTimer, 500, "Minimum ms between bot movement jitter checks.")
|
||||||
RULE_INT(Bots, MaxJitterTimer, 2500, "Maximum ms between bot movement jitter checks. Set to 0 to disable timer checks.")
|
RULE_INT(Bots, MaxJitterTimer, 2500, "Maximum ms between bot movement jitter checks. Set to 0 to disable timer checks.")
|
||||||
RULE_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.")
|
RULE_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.")
|
||||||
@@ -899,6 +899,7 @@ RULE_STRING(Bots, ZonesWithForcedSpawnLimits, "", "Comma-delimited list of zones
|
|||||||
RULE_STRING(Bots, ZoneForcedSpawnLimits, "", "Comma-delimited list of forced spawn limits for zones.")
|
RULE_STRING(Bots, ZoneForcedSpawnLimits, "", "Comma-delimited list of forced spawn limits for zones.")
|
||||||
RULE_INT(Bots, AICastSpellTypeDelay, 100, "Delay in milliseconds between AI cast attempts for each spell type. Default 100ms")
|
RULE_INT(Bots, AICastSpellTypeDelay, 100, "Delay in milliseconds between AI cast attempts for each spell type. Default 100ms")
|
||||||
RULE_INT(Bots, AICastSpellTypeHeldDelay, 2500, "Delay in milliseconds between AI cast attempts for each spell type that is held or disabled. Default 2500ms (2.5s)")
|
RULE_INT(Bots, AICastSpellTypeHeldDelay, 2500, "Delay in milliseconds between AI cast attempts for each spell type that is held or disabled. Default 2500ms (2.5s)")
|
||||||
|
RULE_BOOL(Bots, BotsRequireLoS, true, "Whether or not bots require line of sight to be told to attack their target")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Chat)
|
RULE_CATEGORY(Chat)
|
||||||
@@ -922,6 +923,7 @@ RULE_BOOL(Chat, AutoInjectSaylinksToSay, true, "Automatically injects saylinks i
|
|||||||
RULE_BOOL(Chat, AutoInjectSaylinksToClientMessage, true, "Automatically injects saylinks into dialogue that has [brackets in them]")
|
RULE_BOOL(Chat, AutoInjectSaylinksToClientMessage, true, "Automatically injects saylinks into dialogue that has [brackets in them]")
|
||||||
RULE_BOOL(Chat, QuestDialogueUsesDialogueWindow, false, "Pipes all quest dialogue to dialogue window")
|
RULE_BOOL(Chat, QuestDialogueUsesDialogueWindow, false, "Pipes all quest dialogue to dialogue window")
|
||||||
RULE_BOOL(Chat, DialogueWindowAnimatesNPCsIfNoneSet, true, "If there is no animation specified in the dialogue window markdown then it will choose a random greet animation such as wave or salute")
|
RULE_BOOL(Chat, DialogueWindowAnimatesNPCsIfNoneSet, true, "If there is no animation specified in the dialogue window markdown then it will choose a random greet animation such as wave or salute")
|
||||||
|
RULE_BOOL(Chat, AlwaysCaptureCommandText, false, "Consume command text (# and ^ by default), regardless of which channel it is sent to")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Merchant)
|
RULE_CATEGORY(Merchant)
|
||||||
@@ -1090,6 +1092,7 @@ RULE_CATEGORY(Instances)
|
|||||||
RULE_INT(Instances, ReservedInstances, 100, "Number of instance IDs which are reserved for globals. This value should not be changed while a server is running")
|
RULE_INT(Instances, ReservedInstances, 100, "Number of instance IDs which are reserved for globals. This value should not be changed while a server is running")
|
||||||
RULE_BOOL(Instances, RecycleInstanceIds, true, "Setting whether free instance IDs should be recycled to prevent them from gradually running out at 32k")
|
RULE_BOOL(Instances, RecycleInstanceIds, true, "Setting whether free instance IDs should be recycled to prevent them from gradually running out at 32k")
|
||||||
RULE_INT(Instances, GuildHallExpirationDays, 90, "Amount of days before a Guild Hall instance expires")
|
RULE_INT(Instances, GuildHallExpirationDays, 90, "Amount of days before a Guild Hall instance expires")
|
||||||
|
RULE_INT(Instances, ExpireOffsetTimeSeconds, 3600, "Amount of seconds to beyond instance expiration time we wait to purge the entry from the database. (Default: 1 Hour)")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Expedition)
|
RULE_CATEGORY(Expedition)
|
||||||
@@ -1145,6 +1148,7 @@ RULE_BOOL(Items, DisableSpellFocusEffects, false, "Enable this to disable Spell
|
|||||||
RULE_BOOL(Items, SummonItemAllowInvisibleAugments, false, "Enable this to allow augments to be put in invisible augment slots of items in Client::SummonItem")
|
RULE_BOOL(Items, SummonItemAllowInvisibleAugments, false, "Enable this to allow augments to be put in invisible augment slots of items in Client::SummonItem")
|
||||||
RULE_BOOL(Items, AugmentItemAllowInvisibleAugments, false, "Enable this to allow augments to be put in invisible augment slots by players")
|
RULE_BOOL(Items, AugmentItemAllowInvisibleAugments, false, "Enable this to allow augments to be put in invisible augment slots by players")
|
||||||
RULE_BOOL(Items, AlwaysReturnHandins, true, "Enable this to always return handins to the player")
|
RULE_BOOL(Items, AlwaysReturnHandins, true, "Enable this to always return handins to the player")
|
||||||
|
RULE_BOOL(Items, NPCUseRecommendedLevels, false, "Enable to have NPCs scale item stats by recommended levels")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Parcel)
|
RULE_CATEGORY(Parcel)
|
||||||
@@ -1160,8 +1164,9 @@ RULE_CATEGORY_END()
|
|||||||
RULE_CATEGORY(EvolvingItems)
|
RULE_CATEGORY(EvolvingItems)
|
||||||
RULE_REAL(EvolvingItems, PercentOfSoloExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.")
|
RULE_REAL(EvolvingItems, PercentOfSoloExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.")
|
||||||
RULE_REAL(EvolvingItems, PercentOfGroupExperience, 0.1, "Percentage of group experience allocated to evolving items that require experience.")
|
RULE_REAL(EvolvingItems, PercentOfGroupExperience, 0.1, "Percentage of group experience allocated to evolving items that require experience.")
|
||||||
RULE_REAL(EvolvingItems, PercentOfRaidExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.")
|
RULE_REAL(EvolvingItems, PercentOfRaidExperience, 0.1, "Percentage of raid experience allocated to evolving items that require experience.")
|
||||||
RULE_INT(EvolvingItems, DelayUponEquipping, 30000, "Delay in ms before an evolving item will earn rewards after equipping. Default is 30000ms or 30s.")
|
RULE_INT(EvolvingItems, DelayUponEquipping, 30000, "Delay in ms before an evolving item will earn rewards after equipping. Default is 30000ms or 30s.")
|
||||||
|
RULE_BOOL(EvolvingItems, DestroyAugmentsOnEvolve, false, "If this is enabled, any augments in an item will be destroyed when the item evolves. Otherwise, send augments to the player via the parcel system (requires that the Parcel System be enabled).")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
#undef RULE_CATEGORY
|
#undef RULE_CATEGORY
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace ServerReload {
|
|||||||
LevelEXPMods,
|
LevelEXPMods,
|
||||||
Logs,
|
Logs,
|
||||||
Loot,
|
Loot,
|
||||||
|
Maps,
|
||||||
Merchants,
|
Merchants,
|
||||||
NPCEmotes,
|
NPCEmotes,
|
||||||
NPCSpells,
|
NPCSpells,
|
||||||
@@ -61,6 +62,7 @@ namespace ServerReload {
|
|||||||
"Level EXP Mods",
|
"Level EXP Mods",
|
||||||
"Logs",
|
"Logs",
|
||||||
"Loot",
|
"Loot",
|
||||||
|
"Maps",
|
||||||
"Merchants",
|
"Merchants",
|
||||||
"NPC Emotes",
|
"NPC Emotes",
|
||||||
"NPC Spells",
|
"NPC Spells",
|
||||||
|
|||||||
+68
-43
@@ -196,6 +196,7 @@
|
|||||||
#define ServerOP_DzSaveInvite 0x0466
|
#define ServerOP_DzSaveInvite 0x0466
|
||||||
#define ServerOP_DzRequestInvite 0x0467
|
#define ServerOP_DzRequestInvite 0x0467
|
||||||
#define ServerOP_DzMakeLeader 0x0468
|
#define ServerOP_DzMakeLeader 0x0468
|
||||||
|
#define ServerOP_DzGetBulkMemberStatuses 0x0469
|
||||||
|
|
||||||
#define ServerOP_LSInfo 0x1000
|
#define ServerOP_LSInfo 0x1000
|
||||||
#define ServerOP_LSStatus 0x1001
|
#define ServerOP_LSStatus 0x1001
|
||||||
@@ -677,36 +678,53 @@ struct ServerLSPlayerZoneChange_Struct {
|
|||||||
uint32 to; // 0 = world
|
uint32 to; // 0 = world
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClientAuth_Struct {
|
struct ClientAuth {
|
||||||
uint32 loginserver_account_id; // ID# in login server's db
|
uint32 loginserver_account_id; // ID# in login server's db
|
||||||
char loginserver_name[64];
|
char loginserver_name[64];
|
||||||
char account_name[30]; // username in login server's db
|
char account_name[30]; // username in login server's db
|
||||||
char key[30]; // the Key the client will present
|
char key[30]; // the key the client will present
|
||||||
uint8 lsadmin; // login server admin level
|
uint8 lsadmin; // 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
|
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;
|
uint32 ip_address;
|
||||||
uint8 is_client_from_local_network; // 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 <class Archive>
|
template<class Archive>
|
||||||
void serialize(Archive &ar)
|
void serialize(Archive &ar)
|
||||||
{
|
{
|
||||||
ar(loginserver_account_id, loginserver_name, account_name, key, lsadmin, is_world_admin, ip, is_client_from_local_network);
|
ar(
|
||||||
|
loginserver_account_id,
|
||||||
|
loginserver_name,
|
||||||
|
account_name,
|
||||||
|
key,
|
||||||
|
lsadmin,
|
||||||
|
is_world_admin,
|
||||||
|
ip_address,
|
||||||
|
is_client_from_local_network
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClientAuthLegacy_Struct {
|
struct ClientAuthLegacy {
|
||||||
uint32 loginserver_account_id; // ID# in login server's db
|
uint32 loginserver_account_id; // ID# in login server's db
|
||||||
char loginserver_account_name[30]; // username 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
|
char key[30]; // the key the client will present
|
||||||
uint8 loginserver_admin_level; // login server admin level
|
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
|
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;
|
uint32 ip_address;
|
||||||
uint8 is_client_from_local_network; // 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 <class Archive>
|
template<class Archive>
|
||||||
void serialize(Archive &ar)
|
void serialize(Archive &ar)
|
||||||
{
|
{
|
||||||
ar(loginserver_account_id, loginserver_account_name, key, loginserver_admin_level, is_world_admin, ip, is_client_from_local_network);
|
ar(
|
||||||
|
loginserver_account_id,
|
||||||
|
loginserver_account_name,
|
||||||
|
key,
|
||||||
|
loginserver_admin_level,
|
||||||
|
is_world_admin,
|
||||||
|
ip_address,
|
||||||
|
is_client_from_local_network
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -834,38 +852,38 @@ struct ServerSyncWorldList_Struct {
|
|||||||
bool placeholder;
|
bool placeholder;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UsertoWorldRequestLegacy_Struct {
|
struct UsertoWorldRequestLegacy {
|
||||||
uint32 lsaccountid;
|
|
||||||
uint32 worldid;
|
|
||||||
uint32 FromID;
|
|
||||||
uint32 ToID;
|
|
||||||
char IPAddr[64];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UsertoWorldRequest_Struct {
|
|
||||||
uint32 lsaccountid;
|
|
||||||
uint32 worldid;
|
|
||||||
uint32 FromID;
|
|
||||||
uint32 ToID;
|
|
||||||
char IPAddr[64];
|
|
||||||
char login[64];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UsertoWorldResponseLegacy_Struct {
|
|
||||||
uint32 lsaccountid;
|
uint32 lsaccountid;
|
||||||
uint32 worldid;
|
uint32 worldid;
|
||||||
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
uint32 FromID;
|
||||||
|
uint32 ToID;
|
||||||
|
char IPAddr[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UsertoWorldRequest {
|
||||||
|
uint32 lsaccountid;
|
||||||
|
uint32 worldid;
|
||||||
|
uint32 FromID; // appears to be unused today
|
||||||
|
uint32 ToID; // appears to be unused today
|
||||||
|
char IPAddr[64];
|
||||||
|
char login[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UsertoWorldResponseLegacy {
|
||||||
|
uint32 lsaccountid;
|
||||||
|
uint32 worldid;
|
||||||
|
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
||||||
uint32 FromID;
|
uint32 FromID;
|
||||||
uint32 ToID;
|
uint32 ToID;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UsertoWorldResponse_Struct {
|
struct UsertoWorldResponse {
|
||||||
uint32 lsaccountid;
|
uint32 lsaccountid;
|
||||||
uint32 worldid;
|
uint32 worldid;
|
||||||
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
||||||
uint32 FromID;
|
uint32 FromID; // appears to be unused today
|
||||||
uint32 ToID;
|
uint32 ToID; // appears to be unused today
|
||||||
char login[64];
|
char login[64];
|
||||||
};
|
};
|
||||||
|
|
||||||
// generic struct to be used for alot of simple zone->world questions
|
// generic struct to be used for alot of simple zone->world questions
|
||||||
@@ -1538,6 +1556,13 @@ struct ServerDzMemberStatuses_Struct {
|
|||||||
ServerDzMemberStatusEntry_Struct entries[0];
|
ServerDzMemberStatusEntry_Struct entries[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ServerDzCerealData_Struct {
|
||||||
|
uint16_t zone_id;
|
||||||
|
uint16_t inst_id;
|
||||||
|
uint32_t cereal_size;
|
||||||
|
char cereal_data[1];
|
||||||
|
};
|
||||||
|
|
||||||
struct ServerDzMovePC_Struct {
|
struct ServerDzMovePC_Struct {
|
||||||
uint32 dz_id;
|
uint32 dz_id;
|
||||||
uint16 sender_zone_id;
|
uint16 sender_zone_id;
|
||||||
|
|||||||
+49
-49
@@ -1456,41 +1456,42 @@ bool IsCompleteHealSpell(uint16 spell_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IsFastHealSpell(uint16 spell_id) {
|
bool IsFastHealSpell(uint16 spell_id) {
|
||||||
spell_id = (
|
spell_id = (
|
||||||
IsEffectInSpell(spell_id, SE_CurrentHP) ?
|
IsEffectInSpell(spell_id, SE_CurrentHP) ?
|
||||||
spell_id :
|
spell_id :
|
||||||
GetSpellTriggerSpellID(spell_id, SE_CurrentHP)
|
GetSpellTriggerSpellID(spell_id, SE_CurrentHP)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!spell_id) {
|
if (!spell_id) {
|
||||||
spell_id = (
|
spell_id = (
|
||||||
IsEffectInSpell(spell_id, SE_CurrentHPOnce) ?
|
IsEffectInSpell(spell_id, SE_CurrentHPOnce) ?
|
||||||
spell_id :
|
spell_id :
|
||||||
GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce)
|
GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id && IsValidSpell(spell_id)) {
|
if (IsValidSpell(spell_id)) {
|
||||||
if (
|
if (
|
||||||
spells[spell_id].cast_time <= MAX_FAST_HEAL_CASTING_TIME &&
|
spell_id != SPELL_MINOR_HEALING &&
|
||||||
spells[spell_id].good_effect &&
|
(spells[spell_id].cast_time > MAX_VERY_FAST_HEAL_CASTING_TIME && spells[spell_id].cast_time <= MAX_FAST_HEAL_CASTING_TIME) &&
|
||||||
!IsGroupSpell(spell_id)
|
spells[spell_id].good_effect &&
|
||||||
) {
|
!IsGroupSpell(spell_id)
|
||||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
) {
|
||||||
if (
|
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||||
spells[spell_id].base_value[i] > 0 &&
|
if (
|
||||||
(
|
spells[spell_id].base_value[i] > 0 &&
|
||||||
spells[spell_id].effect_id[i] == SE_CurrentHP ||
|
(
|
||||||
spells[spell_id].effect_id[i] == SE_CurrentHPOnce
|
spells[spell_id].effect_id[i] == SE_CurrentHP ||
|
||||||
)
|
spells[spell_id].effect_id[i] == SE_CurrentHPOnce
|
||||||
) {
|
)
|
||||||
return true;
|
) {
|
||||||
}
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsVeryFastHealSpell(uint16 spell_id)
|
bool IsVeryFastHealSpell(uint16 spell_id)
|
||||||
@@ -1509,8 +1510,9 @@ bool IsVeryFastHealSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id) {
|
if (IsValidSpell(spell_id)) {
|
||||||
if (
|
if (
|
||||||
|
spell_id != SPELL_MINOR_HEALING &&
|
||||||
spells[spell_id].cast_time <= MAX_VERY_FAST_HEAL_CASTING_TIME &&
|
spells[spell_id].cast_time <= MAX_VERY_FAST_HEAL_CASTING_TIME &&
|
||||||
spells[spell_id].good_effect &&
|
spells[spell_id].good_effect &&
|
||||||
!IsGroupSpell(spell_id)
|
!IsGroupSpell(spell_id)
|
||||||
@@ -1548,8 +1550,13 @@ bool IsRegularSingleTargetHealSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id) {
|
if (IsValidSpell(spell_id)) {
|
||||||
|
if (spell_id == SPELL_MINOR_HEALING) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
spells[spell_id].cast_time > MAX_FAST_HEAL_CASTING_TIME &&
|
||||||
spells[spell_id].target_type == ST_Target &&
|
spells[spell_id].target_type == ST_Target &&
|
||||||
!IsCompleteHealSpell(spell_id) &&
|
!IsCompleteHealSpell(spell_id) &&
|
||||||
!IsHealOverTimeSpell(spell_id) &&
|
!IsHealOverTimeSpell(spell_id) &&
|
||||||
@@ -1589,9 +1596,14 @@ bool IsRegularPetHealSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id && IsValidSpell(spell_id)) {
|
if (IsValidSpell(spell_id)) {
|
||||||
|
if (spell_id == SPELL_MINOR_HEALING) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_Undead) &&
|
spells[spell_id].cast_time > MAX_FAST_HEAL_CASTING_TIME &&
|
||||||
|
(spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_SummonedPet) &&
|
||||||
!IsCompleteHealSpell(spell_id) &&
|
!IsCompleteHealSpell(spell_id) &&
|
||||||
!IsHealOverTimeSpell(spell_id) &&
|
!IsHealOverTimeSpell(spell_id) &&
|
||||||
!IsGroupSpell(spell_id)
|
!IsGroupSpell(spell_id)
|
||||||
@@ -1630,7 +1642,7 @@ bool IsRegularGroupHealSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id) {
|
if (IsValidSpell(spell_id)) {
|
||||||
if (
|
if (
|
||||||
IsGroupSpell(spell_id) &&
|
IsGroupSpell(spell_id) &&
|
||||||
!IsCompleteHealSpell(spell_id) &&
|
!IsCompleteHealSpell(spell_id) &&
|
||||||
@@ -2796,18 +2808,6 @@ bool IsLichSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidSpellAndLoS(uint32 spell_id, bool has_los) {
|
|
||||||
if (!IsValidSpell(spell_id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_los && IsTargetRequiredForSpell(spell_id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsInstantHealSpell(uint32 spell_id) {
|
bool IsInstantHealSpell(uint32 spell_id) {
|
||||||
if (!IsValidSpell(spell_id)) {
|
if (!IsValidSpell(spell_id)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
+11
-8
@@ -215,6 +215,7 @@
|
|||||||
#define SPELL_AMPLIFICATION 2603
|
#define SPELL_AMPLIFICATION 2603
|
||||||
#define SPELL_DIVINE_REZ 2738
|
#define SPELL_DIVINE_REZ 2738
|
||||||
#define SPELL_NATURES_RECOVERY 2520
|
#define SPELL_NATURES_RECOVERY 2520
|
||||||
|
#define SPELL_MINOR_HEALING 200
|
||||||
#define SPELL_ADRENALINE_SWELL 14445
|
#define SPELL_ADRENALINE_SWELL 14445
|
||||||
#define SPELL_ADRENALINE_SWELL_RK2 14446
|
#define SPELL_ADRENALINE_SWELL_RK2 14446
|
||||||
#define SPELL_ADRENALINE_SWELL_RK3 14447
|
#define SPELL_ADRENALINE_SWELL_RK3 14447
|
||||||
@@ -736,11 +737,12 @@ namespace BotSpellTypes
|
|||||||
constexpr uint16 DiscUtility = 203;
|
constexpr uint16 DiscUtility = 203;
|
||||||
|
|
||||||
constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this
|
constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this
|
||||||
constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed
|
constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed
|
||||||
constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this
|
constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this
|
||||||
constexpr uint16 COMMANDED_END = BotSpellTypes::AELull; // Do not remove this, increment as needed
|
constexpr uint16 COMMANDED_END = BotSpellTypes::AELull; // Do not remove this, increment as needed
|
||||||
constexpr uint16 DISCIPLINE_START = BotSpellTypes::Discipline; // Do not remove or change this
|
constexpr uint16 DISCIPLINE_START = BotSpellTypes::Discipline; // Do not remove or change this
|
||||||
constexpr uint16 DISCIPLINE_END = BotSpellTypes::DiscUtility; // Do not remove this, increment as needed
|
constexpr uint16 DISCIPLINE_END = BotSpellTypes::DiscUtility; // Do not remove this, increment as needed
|
||||||
|
constexpr uint16 PARENT_TYPE_END = BotSpellTypes::PreCombatBuffSong; // This is the last ID of the original bot spell types, the rest are considered sub types.
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<uint16, std::string> spell_type_names = {
|
static std::map<uint16, std::string> spell_type_names = {
|
||||||
@@ -898,8 +900,8 @@ const uint32 SPELL_TYPES_BENEFICIAL = (SpellType_Heal | SpellType_Buff | SpellTy
|
|||||||
const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root);
|
const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root);
|
||||||
|
|
||||||
// Bot related functions
|
// Bot related functions
|
||||||
bool IsBotSpellTypeDetrimental (uint16 spell_type);
|
bool IsBotSpellTypeDetrimental(uint16 spell_type);
|
||||||
bool IsBotSpellTypeBeneficial (uint16 spell_type);
|
bool IsBotSpellTypeBeneficial(uint16 spell_type);
|
||||||
bool BotSpellTypeUsesTargetSettings(uint16 spell_type);
|
bool BotSpellTypeUsesTargetSettings(uint16 spell_type);
|
||||||
bool IsBotSpellTypeInnate (uint16 spell_type);
|
bool IsBotSpellTypeInnate (uint16 spell_type);
|
||||||
bool IsAEBotSpellType(uint16 spell_type);
|
bool IsAEBotSpellType(uint16 spell_type);
|
||||||
@@ -915,6 +917,8 @@ bool IsCommandedBotSpellType(uint16 spell_type);
|
|||||||
bool IsPullingBotSpellType(uint16 spell_type);
|
bool IsPullingBotSpellType(uint16 spell_type);
|
||||||
uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id);
|
uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id);
|
||||||
uint16 GetPetBotSpellType(uint16 spell_type);
|
uint16 GetPetBotSpellType(uint16 spell_type);
|
||||||
|
bool IsBotBuffSpellType(uint16 spell_type);
|
||||||
|
bool BotRequiresLoSToCast(uint16 spell_type, uint16 spell_id);
|
||||||
|
|
||||||
// These should not be used to determine spell category..
|
// These should not be used to determine spell category..
|
||||||
// They are a graphical affects (effects?) index only
|
// They are a graphical affects (effects?) index only
|
||||||
@@ -1812,7 +1816,6 @@ bool IsEffectInSpell(uint16 spell_id, int effect_id);
|
|||||||
uint16 GetSpellTriggerSpellID(uint16 spell_id, int effect_id);
|
uint16 GetSpellTriggerSpellID(uint16 spell_id, int effect_id);
|
||||||
bool IsBlankSpellEffect(uint16 spell_id, int effect_index);
|
bool IsBlankSpellEffect(uint16 spell_id, int effect_index);
|
||||||
bool IsValidSpell(uint32 spell_id);
|
bool IsValidSpell(uint32 spell_id);
|
||||||
bool IsValidSpellAndLoS(uint32 spell_id, bool has_los = true);
|
|
||||||
bool IsSummonSpell(uint16 spell_id);
|
bool IsSummonSpell(uint16 spell_id);
|
||||||
bool IsDamageSpell(uint16 spell_id);
|
bool IsDamageSpell(uint16 spell_id);
|
||||||
bool IsAnyDamageSpell(uint16 spell_id);
|
bool IsAnyDamageSpell(uint16 spell_id);
|
||||||
|
|||||||
+40
-252
@@ -1,4 +1,5 @@
|
|||||||
#include "spdat.h"
|
#include "spdat.h"
|
||||||
|
#include "../zone/bot.h"
|
||||||
|
|
||||||
bool IsBotSpellTypeDetrimental(uint16 spell_type) {
|
bool IsBotSpellTypeDetrimental(uint16 spell_type) {
|
||||||
switch (spell_type) {
|
switch (spell_type) {
|
||||||
@@ -417,264 +418,23 @@ uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id) {
|
|||||||
return UINT16_MAX;
|
return UINT16_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 correct_type = UINT16_MAX;
|
uint16 correct_type = spell_type;
|
||||||
SPDat_Spell_Struct spell = spells[spell_id];
|
|
||||||
std::string teleport_zone = spell.teleport_zone;
|
|
||||||
|
|
||||||
if (IsCharmSpell(spell_id)) {
|
if (!Bot::IsValidSpellTypeBySpellID(spell_type, spell_id)) {
|
||||||
correct_type = BotSpellTypes::Charm;
|
correct_type = UINT16_MAX;
|
||||||
}
|
|
||||||
else if (IsFearSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Fear;
|
|
||||||
}
|
|
||||||
else if (IsEffectInSpell(spell_id, SE_Revive)) {
|
|
||||||
correct_type = BotSpellTypes::Resurrect;
|
|
||||||
}
|
|
||||||
else if (IsHarmonySpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Lull;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
teleport_zone.compare("") &&
|
|
||||||
!IsEffectInSpell(spell_id, SE_GateToHomeCity) &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Teleport;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_Succor)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Succor;
|
|
||||||
}
|
|
||||||
else if (IsEffectInSpell(spell_id, SE_BindAffinity)) {
|
|
||||||
correct_type = BotSpellTypes::BindAffinity;
|
|
||||||
}
|
|
||||||
else if (IsEffectInSpell(spell_id, SE_Identify)) {
|
|
||||||
correct_type = BotSpellTypes::Identify;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::Levitate &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_Levitate)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Levitate;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::Rune &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Rune;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::WaterBreathing &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_WaterBreathing)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::WaterBreathing;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::Size &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Size;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::Invisibility &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Invisibility;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::MovementSpeed &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_MovementSpeed)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::MovementSpeed;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
!teleport_zone.compare("") &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_Translocate) || IsEffectInSpell(spell_id, SE_GateToHomeCity))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::SendHome;
|
|
||||||
}
|
|
||||||
else if (IsEffectInSpell(spell_id, SE_SummonCorpse)) {
|
|
||||||
correct_type = BotSpellTypes::SummonCorpse;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (correct_type == UINT16_MAX) {
|
auto start = std::min({ BotSpellTypes::START, BotSpellTypes::COMMANDED_START, BotSpellTypes::DISCIPLINE_START });
|
||||||
if (
|
auto end = std::max({ BotSpellTypes::END, BotSpellTypes::COMMANDED_END, BotSpellTypes::DISCIPLINE_END });
|
||||||
IsSummonPetSpell(spell_id) ||
|
|
||||||
IsEffectInSpell(spell_id, SE_TemporaryPets)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Pet;
|
|
||||||
}
|
|
||||||
else if (IsMesmerizeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Mez;
|
|
||||||
}
|
|
||||||
else if (IsEscapeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Escape;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_Root)
|
|
||||||
) {
|
|
||||||
if (IsAnyAESpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::AERoot;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
correct_type = BotSpellTypes::Root;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsLifetapSpell(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Lifetap;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_MovementSpeed)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Snare;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
(IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::DOT;
|
|
||||||
}
|
|
||||||
else if (IsDispelSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Dispel;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsSlowSpell(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Slow;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDebuffSpell(spell_id) &&
|
|
||||||
!IsHateReduxSpell(spell_id) &&
|
|
||||||
!IsHateSpell(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Debuff;
|
|
||||||
}
|
|
||||||
else if (IsHateReduxSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::HateRedux;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsHateSpell(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::HateLine;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsBuffSpell(spell_id) &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsBardSong(spell_id)
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
spell_type == BotSpellTypes::InCombatBuffSong ||
|
|
||||||
spell_type == BotSpellTypes::OutOfCombatBuffSong ||
|
|
||||||
spell_type == BotSpellTypes::PreCombatBuffSong
|
|
||||||
) {
|
|
||||||
correct_type = spell_type;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
correct_type = BotSpellTypes::OutOfCombatBuffSong;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
!IsBardSong(spell_id) &&
|
|
||||||
(
|
|
||||||
(IsSelfConversionSpell(spell_id) && spell.buff_duration < 1) ||
|
|
||||||
(spell_type == BotSpellTypes::InCombatBuff && IsAnyBuffSpell(spell_id))
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::InCombatBuff;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::PreCombatBuff &&
|
|
||||||
IsAnyBuffSpell(spell_id) &&
|
|
||||||
!IsBardSong(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::PreCombatBuff;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
(IsCureSpell(spell_id) && spell_type == BotSpellTypes::Cure) ||
|
|
||||||
(IsCureSpell(spell_id) && !IsAnyHealSpell(spell_id))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Cure;
|
|
||||||
}
|
|
||||||
else if (IsAnyNukeOrStunSpell(spell_id)) {
|
|
||||||
if (IsAnyAESpell(spell_id)) {
|
|
||||||
if (IsAERainSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::AERains;
|
|
||||||
}
|
|
||||||
else if (IsPBAENukeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::PBAENuke;
|
|
||||||
}
|
|
||||||
else if (IsStunSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::AEStun;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
correct_type = BotSpellTypes::AENukes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IsStunSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Stun;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
correct_type = BotSpellTypes::Nuke;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IsAnyHealSpell(spell_id)) {
|
|
||||||
if (IsGroupSpell(spell_id)) {
|
|
||||||
if (IsGroupCompleteHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::GroupCompleteHeals;
|
|
||||||
}
|
|
||||||
else if (IsGroupHealOverTimeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::GroupHoTHeals;
|
|
||||||
}
|
|
||||||
else if (IsRegularGroupHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::GroupHeals;
|
|
||||||
}
|
|
||||||
|
|
||||||
return correct_type;
|
for (int i = end; i >= start; --i) {
|
||||||
|
if (!Bot::IsValidBotSpellType(i) || i == BotSpellTypes::InCombatBuff) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsVeryFastHealSpell(spell_id)) {
|
if (Bot::IsValidSpellTypeBySpellID(i, spell_id)) {
|
||||||
correct_type = BotSpellTypes::VeryFastHeals;
|
correct_type = i;
|
||||||
}
|
|
||||||
else if (IsFastHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::FastHeals;
|
|
||||||
}
|
|
||||||
else if (IsCompleteHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::CompleteHeal;
|
|
||||||
}
|
|
||||||
else if (IsHealOverTimeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::HoTHeals;
|
|
||||||
}
|
|
||||||
else if (IsRegularSingleTargetHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::RegularHeal;
|
|
||||||
}
|
|
||||||
else if (IsRegularPetHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::RegularHeal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IsAnyBuffSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Buff;
|
|
||||||
|
|
||||||
if (IsResistanceOnlySpell(spell_id)) {
|
break;
|
||||||
correct_type = BotSpellTypes::ResistBuffs;
|
|
||||||
}
|
|
||||||
else if (IsDamageShieldOnlySpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::DamageShields;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -708,3 +468,31 @@ uint16 GetPetBotSpellType(uint16 spell_type) {
|
|||||||
|
|
||||||
return spell_type;
|
return spell_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsBotBuffSpellType(uint16 spell_type) {
|
||||||
|
switch (spell_type) {
|
||||||
|
case BotSpellTypes::Buff:
|
||||||
|
case BotSpellTypes::PetBuffs:
|
||||||
|
case BotSpellTypes::ResistBuffs:
|
||||||
|
case BotSpellTypes::PetResistBuffs:
|
||||||
|
case BotSpellTypes::DamageShields:
|
||||||
|
case BotSpellTypes::PetDamageShields:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BotRequiresLoSToCast(uint16 spell_type, uint16 spell_id) {
|
||||||
|
if (!BotSpellTypeRequiresTarget(spell_type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsTargetRequiredForSpell(spell_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -936,3 +936,11 @@ std::string Strings::Slugify(const std::string& input, const std::string& separa
|
|||||||
|
|
||||||
return slug;
|
return slug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Strings::IsValidJson(const std::string &json)
|
||||||
|
{
|
||||||
|
rapidjson::Document doc;
|
||||||
|
rapidjson::ParseResult result = doc.Parse(json.c_str());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
#include <cereal/external/rapidjson/document.h>
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
// this doesn't appear to affect linux-based systems..need feedback for _WIN64
|
// this doesn't appear to affect linux-based systems..need feedback for _WIN64
|
||||||
@@ -188,6 +189,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::string Slugify(const std::string &input, const std::string &separator = "-");
|
static std::string Slugify(const std::string &input, const std::string &separator = "-");
|
||||||
|
static bool IsValidJson(const std::string& json);
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::string StringFormat(const char *format, ...);
|
const std::string StringFormat(const char *format, ...);
|
||||||
|
|||||||
@@ -196,3 +196,25 @@ const uint32 Timer::SetCurrentTime()
|
|||||||
return current_time;
|
return current_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint32 Timer::RollForward(uint32 seconds)
|
||||||
|
{
|
||||||
|
struct timeval read_time{};
|
||||||
|
uint32 this_time;
|
||||||
|
|
||||||
|
gettimeofday(&read_time, nullptr);
|
||||||
|
this_time = read_time.tv_sec * 1000 + read_time.tv_usec / 1000;
|
||||||
|
|
||||||
|
if (last_time == 0) {
|
||||||
|
current_time = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
current_time += this_time - last_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_time = this_time;
|
||||||
|
|
||||||
|
// Roll forward the specified number of seconds (converted to milliseconds)
|
||||||
|
current_time += seconds * 1000;
|
||||||
|
|
||||||
|
return current_time;
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ public:
|
|||||||
inline uint32 GetDuration() { return(timer_time); }
|
inline uint32 GetDuration() { return(timer_time); }
|
||||||
|
|
||||||
static const uint32 SetCurrentTime();
|
static const uint32 SetCurrentTime();
|
||||||
|
static const uint32 RollForward(uint32 seconds);
|
||||||
static const uint32 GetCurrentTime();
|
static const uint32 GetCurrentTime();
|
||||||
static const uint32 GetTimeSeconds();
|
static const uint32 GetTimeSeconds();
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
// Build variables
|
// Build variables
|
||||||
// these get injected during the build pipeline
|
// these get injected during the build pipeline
|
||||||
#define CURRENT_VERSION "23.0.1-dev" // always append -dev to the current version for custom-builds
|
#define CURRENT_VERSION "23.5.0-dev" // always append -dev to the current version for custom-builds
|
||||||
#define LOGIN_VERSION "0.8.0"
|
#define LOGIN_VERSION "0.8.0"
|
||||||
#define COMPILE_DATE __DATE__
|
#define COMPILE_DATE __DATE__
|
||||||
#define COMPILE_TIME __TIME__
|
#define COMPILE_TIME __TIME__
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define CURRENT_BINARY_DATABASE_VERSION 9306
|
#define CURRENT_BINARY_DATABASE_VERSION 9321
|
||||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054
|
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
|
|||||||
packet.ToString()
|
packet.ToString()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (packet.Length() < sizeof(UsertoWorldResponseLegacy_Struct)) {
|
if (packet.Length() < sizeof(UsertoWorldResponseLegacy)) {
|
||||||
LogError(
|
LogError(
|
||||||
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
||||||
"but was too small. Discarded to avoid buffer overrun"
|
"but was too small. Discarded to avoid buffer overrun"
|
||||||
@@ -152,7 +152,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *res = (UsertoWorldResponseLegacy_Struct *) packet.Data();
|
auto *res = (UsertoWorldResponseLegacy *) packet.Data();
|
||||||
|
|
||||||
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
||||||
Client *c = server.client_manager->GetClient(res->lsaccountid, "eqemu");
|
Client *c = server.client_manager->GetClient(res->lsaccountid, "eqemu");
|
||||||
@@ -229,7 +229,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
|
|||||||
packet.ToString()
|
packet.ToString()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (packet.Length() < sizeof(UsertoWorldResponse_Struct)) {
|
if (packet.Length() < sizeof(UsertoWorldResponse)) {
|
||||||
LogError(
|
LogError(
|
||||||
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
||||||
"but was too small. Discarded to avoid buffer overrun"
|
"but was too small. Discarded to avoid buffer overrun"
|
||||||
@@ -238,7 +238,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = (UsertoWorldResponse_Struct *) packet.Data();
|
auto res = (UsertoWorldResponse *) packet.Data();
|
||||||
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
||||||
|
|
||||||
Client *c = server.client_manager->GetClient(
|
Client *c = server.client_manager->GetClient(
|
||||||
@@ -358,14 +358,12 @@ void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet
|
|||||||
void WorldServer::HandleNewWorldserver(LoginserverNewWorldRequest *req)
|
void WorldServer::HandleNewWorldserver(LoginserverNewWorldRequest *req)
|
||||||
{
|
{
|
||||||
if (m_is_server_logged_in) {
|
if (m_is_server_logged_in) {
|
||||||
LogError(
|
LogInfo("Login server was already marked as logged in, returning");
|
||||||
"Login server was already marked as logged in, aborting"
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!HandleNewWorldserverValidation(req)) {
|
if (!HandleNewWorldserverValidation(req)) {
|
||||||
LogError("WorldServer::HandleNewWorldserver failed validation rules");
|
LogError("failed validation rules");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,7 +492,7 @@ void WorldServer::HandleWorldserverStatusUpdate(LoginserverWorldStatusUpdate *u)
|
|||||||
void WorldServer::SendClientAuthToWorld(Client *c)
|
void WorldServer::SendClientAuthToWorld(Client *c)
|
||||||
{
|
{
|
||||||
EQ::Net::DynamicPacket outapp;
|
EQ::Net::DynamicPacket outapp;
|
||||||
ClientAuth_Struct a{};
|
ClientAuth a{};
|
||||||
|
|
||||||
a.loginserver_account_id = c->GetAccountID();
|
a.loginserver_account_id = c->GetAccountID();
|
||||||
|
|
||||||
@@ -503,7 +501,7 @@ void WorldServer::SendClientAuthToWorld(Client *c)
|
|||||||
|
|
||||||
a.lsadmin = 0;
|
a.lsadmin = 0;
|
||||||
a.is_world_admin = 0;
|
a.is_world_admin = 0;
|
||||||
a.ip = inet_addr(c->GetConnection()->GetRemoteAddr().c_str());
|
a.ip_address = inet_addr(c->GetConnection()->GetRemoteAddr().c_str());
|
||||||
strncpy(a.loginserver_name, &c->GetLoginServerName()[0], 64);
|
strncpy(a.loginserver_name, &c->GetLoginServerName()[0], 64);
|
||||||
|
|
||||||
const std::string &client_address(c->GetConnection()->GetRemoteAddr());
|
const std::string &client_address(c->GetConnection()->GetRemoteAddr());
|
||||||
@@ -521,7 +519,7 @@ void WorldServer::SendClientAuthToWorld(Client *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct in_addr ip_addr{};
|
struct in_addr ip_addr{};
|
||||||
ip_addr.s_addr = a.ip;
|
ip_addr.s_addr = a.ip_address;
|
||||||
|
|
||||||
LogInfo(
|
LogInfo(
|
||||||
"Client authentication response: world_address [{}] client_address [{}]",
|
"Client authentication response: world_address [{}] client_address [{}]",
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ private:
|
|||||||
std::string m_server_version;
|
std::string m_server_version;
|
||||||
bool m_is_server_authorized_to_list;
|
bool m_is_server_authorized_to_list;
|
||||||
bool m_is_server_logged_in;
|
bool m_is_server_logged_in;
|
||||||
bool m_is_server_trusted;
|
bool m_is_server_trusted; // this is primarily for worldserver being able to push updates to the loginserver
|
||||||
|
|
||||||
static void FormatWorldServerName(char *name, int8 server_list_type);
|
static void FormatWorldServerName(char *name, int8 server_list_type);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -154,9 +154,9 @@ void WorldServerManager::SendUserLoginToWorldRequest(
|
|||||||
|
|
||||||
if (iter != m_world_servers.end()) {
|
if (iter != m_world_servers.end()) {
|
||||||
EQ::Net::DynamicPacket outapp;
|
EQ::Net::DynamicPacket outapp;
|
||||||
outapp.Resize(sizeof(UsertoWorldRequest_Struct));
|
outapp.Resize(sizeof(UsertoWorldRequest));
|
||||||
|
|
||||||
auto *r = reinterpret_cast<UsertoWorldRequest_Struct *>(outapp.Data());
|
auto *r = reinterpret_cast<UsertoWorldRequest *>(outapp.Data());
|
||||||
r->worldid = server_id;
|
r->worldid = server_id;
|
||||||
r->lsaccountid = client_account_id;
|
r->lsaccountid = client_account_id;
|
||||||
strncpy(r->login, client_loginserver.c_str(), 64);
|
strncpy(r->login, client_loginserver.c_str(), 64);
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "eqemu-server",
|
"name": "eqemu-server",
|
||||||
"version": "23.0.1",
|
"version": "23.5.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/EQEmu/Server.git"
|
"url": "https://github.com/EQEmu/Server.git"
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ int main()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (player_event_process_timer.Check()) {
|
if (player_event_process_timer.Check()) {
|
||||||
std::jthread player_event_thread(&PlayerEventLogs::Process, &player_event_logs);
|
player_event_logs.Process();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -177,7 +177,7 @@ void ChatChannelList::SendAllChannels(Client *c) {
|
|||||||
|
|
||||||
std::string Message;
|
std::string Message;
|
||||||
|
|
||||||
char CountString[10];
|
char CountString[13];
|
||||||
|
|
||||||
while(iterator.MoreElements()) {
|
while(iterator.MoreElements()) {
|
||||||
|
|
||||||
@@ -408,7 +408,7 @@ void ChatChannel::SendChannelMembers(Client *c) {
|
|||||||
|
|
||||||
if(!c) return;
|
if(!c) return;
|
||||||
|
|
||||||
char CountString[10];
|
char CountString[13];
|
||||||
|
|
||||||
sprintf(CountString, "(%i)", MemberCount(c->GetAccountStatus()));
|
sprintf(CountString, "(%i)", MemberCount(c->GetAccountStatus()));
|
||||||
|
|
||||||
|
|||||||
@@ -746,3 +746,6 @@ OP_TradeSkillRecipeInspect=0x4f7e
|
|||||||
OP_InvokeChangePetNameImmediate=0x046d
|
OP_InvokeChangePetNameImmediate=0x046d
|
||||||
OP_InvokeChangePetName=0x4506
|
OP_InvokeChangePetName=0x4506
|
||||||
OP_ChangePetName=0x5dab
|
OP_ChangePetName=0x5dab
|
||||||
|
|
||||||
|
OP_InvokeNameChangeImmediate=0x4fe2
|
||||||
|
OP_InvokeNameChangeLazy=0x2f2e
|
||||||
|
|||||||
@@ -56,8 +56,10 @@ echo "# Running shared_memory"
|
|||||||
echo "# Running NPC hand-in tests"
|
echo "# Running NPC hand-in tests"
|
||||||
./bin/zone tests:npc-handins 2>&1 | tee test_output.log
|
./bin/zone tests:npc-handins 2>&1 | tee test_output.log
|
||||||
./bin/zone tests:npc-handins-multiquest 2>&1 | tee -a test_output.log
|
./bin/zone tests:npc-handins-multiquest 2>&1 | tee -a test_output.log
|
||||||
|
./bin/zone tests:databuckets 2>&1 | tee -a test_output.log
|
||||||
|
./bin/zone tests:zone-state 2>&1 | tee -a test_output.log
|
||||||
|
|
||||||
if grep -E -q "QueryErr|Error" test_output.log; then
|
if grep -E -q "QueryErr|Error|FAILED" test_output.log; then
|
||||||
echo "Error found in test output! Failing build."
|
echo "Error found in test output! Failing build."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/golang/protobuf v1.3.2 // indirect
|
github.com/golang/protobuf v1.3.2 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
golang.org/x/crypto v0.31.0 // indirect
|
golang.org/x/crypto v0.35.0 // indirect
|
||||||
golang.org/x/net v0.33.0 // indirect
|
golang.org/x/net v0.36.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD
|
|||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ bash -c "${world_bin} database:dump --login-tables --drop-table-syntax-only --du
|
|||||||
bash -c "${world_bin} database:dump --player-tables --drop-table-syntax-only --dump-output-to-console > ${dump_path}drop_tables_player.sql"
|
bash -c "${world_bin} database:dump --player-tables --drop-table-syntax-only --dump-output-to-console > ${dump_path}drop_tables_player.sql"
|
||||||
bash -c "${world_bin} database:dump --system-tables --drop-table-syntax-only --dump-output-to-console > ${dump_path}drop_tables_system.sql"
|
bash -c "${world_bin} database:dump --system-tables --drop-table-syntax-only --dump-output-to-console > ${dump_path}drop_tables_system.sql"
|
||||||
bash -c "${world_bin} database:dump --state-tables --drop-table-syntax-only --dump-output-to-console > ${dump_path}drop_tables_state.sql"
|
bash -c "${world_bin} database:dump --state-tables --drop-table-syntax-only --dump-output-to-console > ${dump_path}drop_tables_state.sql"
|
||||||
bash -c "${world_bin} database:dump --query-serv-tables --drop-table-syntax-only --dump-output-to-console > ${dump_path}drop_tables_queryserv.sql"
|
|
||||||
|
|
||||||
#############################################
|
#############################################
|
||||||
# generate "create_" table files
|
# generate "create_" table files
|
||||||
@@ -45,7 +44,6 @@ bash -c "${world_bin} database:dump --login-tables --table-structure-only --dump
|
|||||||
bash -c "${world_bin} database:dump --player-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_player.sql"
|
bash -c "${world_bin} database:dump --player-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_player.sql"
|
||||||
bash -c "${world_bin} database:dump --state-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_state.sql"
|
bash -c "${world_bin} database:dump --state-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_state.sql"
|
||||||
bash -c "${world_bin} database:dump --static-instance-data --dump-output-to-console >> ${dump_path}create_tables_state.sql"
|
bash -c "${world_bin} database:dump --static-instance-data --dump-output-to-console >> ${dump_path}create_tables_state.sql"
|
||||||
bash -c "${world_bin} database:dump --query-serv-tables --table-structure-only --dump-output-to-console | sed 's/ AUTO_INCREMENT=[0-9]*\b//g' > ${dump_path}create_tables_queryserv.sql"
|
|
||||||
|
|
||||||
# with content
|
# with content
|
||||||
bash -c "${world_bin} database:dump --content-tables --dump-output-to-console > ${dump_path}create_tables_content.sql"
|
bash -c "${world_bin} database:dump --content-tables --dump-output-to-console > ${dump_path}create_tables_content.sql"
|
||||||
|
|||||||
+14
-14
@@ -447,30 +447,30 @@ void Client::SendPostEnterWorld() {
|
|||||||
|
|
||||||
bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
if (app->size != sizeof(LoginInfo_Struct)) {
|
if (app->size != sizeof(LoginInfo)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *login_info = (LoginInfo_Struct *) app->pBuffer;
|
auto *r = (LoginInfo *) app->pBuffer;
|
||||||
|
|
||||||
// Quagmire - max len for name is 18, pass 15
|
// Quagmire - max len for name is 18, pass 15
|
||||||
char name[19] = {0};
|
char name[19] = {0};
|
||||||
char password[16] = {0};
|
char password[16] = {0};
|
||||||
strn0cpy(name, (char *) login_info->login_info, 18);
|
strn0cpy(name, (char *) r->login_info, 18);
|
||||||
strn0cpy(password, (char *) &(login_info->login_info[strlen(name) + 1]), 15);
|
strn0cpy(password, (char *) &(r->login_info[strlen(name) + 1]), 15);
|
||||||
|
|
||||||
LogDebug("Receiving Login Info Packet from Client | name [{0}] password [{1}]", name, password);
|
LogDebug("Receiving login info packet from client | name [{}] password [{}]", name, password);
|
||||||
|
|
||||||
if (strlen(password) <= 1) {
|
if (strlen(password) <= 1) {
|
||||||
LogInfo("Login without a password");
|
LogInfo("Login without a password");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_player_zoning = (login_info->zoning == 1);
|
is_player_zoning = (r->zoning == 1);
|
||||||
|
|
||||||
uint32 id = Strings::ToInt(name);
|
uint32 id = Strings::ToInt(name);
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
LogWarning("Receiving Login Info Packet from Client | account_id is 0 - disconnecting");
|
LogWarning("Receiving login info packet from client | account_id is 0 - disconnecting");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,15 +480,15 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
|||||||
LogClientLogin("Checking authentication id [{}] passed", id);
|
LogClientLogin("Checking authentication id [{}] passed", id);
|
||||||
if (!is_player_zoning) {
|
if (!is_player_zoning) {
|
||||||
// Track who is in and who is out of the game
|
// Track who is in and who is out of the game
|
||||||
char *inout= (char *) "";
|
std::string in_out;
|
||||||
|
|
||||||
if (cle->GetOnline() == CLE_Status::Never){
|
if (cle->GetOnline() == CLE_Status::Never) {
|
||||||
// Desktop -> Char Select
|
// Desktop -> Char Select
|
||||||
inout = (char *) "In";
|
in_out = "in";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Game -> Char Select
|
// Game -> Char Select
|
||||||
inout=(char *) "Out";
|
in_out = "out";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always at Char select at this point.
|
// Always at Char select at this point.
|
||||||
@@ -497,7 +497,7 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
|||||||
// Could use a Logging Out Completely message somewhere.
|
// Could use a Logging Out Completely message somewhere.
|
||||||
cle->SetOnline(CLE_Status::CharSelect);
|
cle->SetOnline(CLE_Status::CharSelect);
|
||||||
|
|
||||||
LogInfo("Account ({}) Logging({}) to character select :: LSID [{}] ", cle->AccountName(), inout, cle->LSID());
|
LogInfo("Account ({}) Logging ({}) to character select :: LSID [{}] ", cle->AccountName(), in_out, cle->LSID());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cle->SetOnline();
|
cle->SetOnline();
|
||||||
@@ -545,7 +545,7 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
|||||||
if (!skip_char_info && !custom_files_key.empty() && cle->Admin() < RuleI(World, CustomFilesAdminLevel)) {
|
if (!skip_char_info && !custom_files_key.empty() && cle->Admin() < RuleI(World, CustomFilesAdminLevel)) {
|
||||||
// Modified clients can utilize this unused block in login_info to send custom payloads on login
|
// Modified clients can utilize this unused block in login_info to send custom payloads on login
|
||||||
// which indicates they are using custom client files with the correct version, based on key payload.
|
// which indicates they are using custom client files with the correct version, based on key payload.
|
||||||
const auto client_key = std::string(reinterpret_cast<char*>(login_info->unknown064));
|
const auto client_key = std::string(reinterpret_cast<char*>(r->unknown064));
|
||||||
if (custom_files_key != client_key) {
|
if (custom_files_key != client_key) {
|
||||||
std::string message = fmt::format("Missing Files [{}]", RuleS(World, CustomFilesUrl) );
|
std::string message = fmt::format("Missing Files [{}]", RuleS(World, CustomFilesUrl) );
|
||||||
SendUnsupportedClientPacket(message);
|
SendUnsupportedClientPacket(message);
|
||||||
@@ -1434,7 +1434,7 @@ void Client::EnterWorld(bool TryBootup) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (TryBootup) {
|
if (TryBootup) {
|
||||||
LogInfo("Attempting autobootup of [{}] ([{}]:[{}])", zone_name, zone_id, instance_id);
|
LogInfo("Attempting autobootup of [{}] [{}] [{}]", zone_name, zone_id, instance_id);
|
||||||
autobootup_timeout.Start();
|
autobootup_timeout.Start();
|
||||||
zone_waiting_for_bootup = zoneserver_list.TriggerBootup(zone_id, instance_id);
|
zone_waiting_for_bootup = zoneserver_list.TriggerBootup(zone_id, instance_id);
|
||||||
if (zone_waiting_for_bootup == 0) {
|
if (zone_waiting_for_bootup == 0) {
|
||||||
|
|||||||
+159
-182
@@ -1,20 +1,3 @@
|
|||||||
/* EQEMu: Everquest Server Emulator
|
|
||||||
Copyright (C) 2001-2005 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/global_define.h"
|
||||||
#include "cliententry.h"
|
#include "cliententry.h"
|
||||||
#include "clientlist.h"
|
#include "clientlist.h"
|
||||||
@@ -24,93 +7,86 @@
|
|||||||
#include "worlddb.h"
|
#include "worlddb.h"
|
||||||
#include "zoneserver.h"
|
#include "zoneserver.h"
|
||||||
#include "world_config.h"
|
#include "world_config.h"
|
||||||
#include "../common/guilds.h"
|
|
||||||
#include "../common/strings.h"
|
|
||||||
|
|
||||||
extern uint32 numplayers;
|
extern uint32 numplayers;
|
||||||
extern LoginServerList loginserverlist;
|
extern LoginServerList loginserverlist;
|
||||||
extern ClientList client_list;
|
extern ClientList client_list;
|
||||||
extern volatile bool RunLoops;
|
extern volatile bool RunLoops;
|
||||||
extern SharedTaskManager shared_task_manager;
|
extern SharedTaskManager shared_task_manager;
|
||||||
|
|
||||||
/**
|
|
||||||
* @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(
|
ClientListEntry::ClientListEntry(
|
||||||
uint32 in_id,
|
uint32 id,
|
||||||
uint32 in_loginserver_id,
|
uint32 login_server_id,
|
||||||
const char *in_loginserver_name,
|
const char *login_server_name,
|
||||||
const char *in_login_name,
|
const char *account_name,
|
||||||
const char *in_login_key,
|
const char *login_key,
|
||||||
int16 in_is_world_admin,
|
int16 is_world_admin,
|
||||||
uint32 ip,
|
uint32 ip_address,
|
||||||
uint8 local
|
uint8 local
|
||||||
)
|
)
|
||||||
: id(in_id)
|
: m_id(id)
|
||||||
{
|
{
|
||||||
ClearVars(true);
|
ClearVars(true);
|
||||||
|
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"in_id [{0}] in_loginserver_id [{1}] in_loginserver_name [{2}] in_login_name [{3}] in_login_key [{4}] "
|
"id [{}] loginserver_id [{}] loginserver_name [{}] login_name [{}] login_key [{}] is_world_admin [{}] ip [{}] local [{}]",
|
||||||
" in_is_world_admin [{5}] ip [{6}] local [{7}]",
|
id,
|
||||||
in_id,
|
login_server_id,
|
||||||
in_loginserver_id,
|
login_server_name,
|
||||||
in_loginserver_name,
|
account_name,
|
||||||
in_login_name,
|
login_key,
|
||||||
in_login_key,
|
is_world_admin,
|
||||||
in_is_world_admin,
|
ip_address,
|
||||||
ip,
|
|
||||||
local
|
local
|
||||||
);
|
);
|
||||||
|
|
||||||
pIP = ip;
|
m_ip_address = ip_address;
|
||||||
pLSID = in_loginserver_id;
|
m_login_server_id = login_server_id;
|
||||||
if (in_loginserver_id > 0) {
|
if (login_server_id > 0) {
|
||||||
paccountid = database.GetAccountIDFromLSID(in_loginserver_name, in_loginserver_id, paccountname, &padmin);
|
m_account_id = database.GetAccountIDFromLSID(
|
||||||
|
login_server_name,
|
||||||
|
login_server_id,
|
||||||
|
m_account_name,
|
||||||
|
&m_admin
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
strn0cpy(loginserver_account_name, in_login_name, sizeof(loginserver_account_name));
|
strn0cpy(m_login_account_name, account_name, sizeof(m_login_account_name));
|
||||||
strn0cpy(plskey, in_login_key, sizeof(plskey));
|
strn0cpy(m_key, login_key, sizeof(m_key));
|
||||||
strn0cpy(source_loginserver, in_loginserver_name, sizeof(source_loginserver));
|
strn0cpy(m_source_loginserver, login_server_name, sizeof(m_source_loginserver));
|
||||||
pworldadmin = in_is_world_admin;
|
|
||||||
plocal = (local == 1);
|
|
||||||
|
|
||||||
memset(pLFGComments, 0, 64);
|
m_world_admin = is_world_admin;
|
||||||
|
m_is_local = (local == 1);
|
||||||
|
|
||||||
|
memset(m_lfg_comments, 0, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer *iZS, ServerClientList_Struct *scl, CLE_Status iOnline)
|
ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer *z, ServerClientList_Struct *scl, CLE_Status online)
|
||||||
: id(in_id)
|
: m_id(in_id)
|
||||||
{
|
{
|
||||||
ClearVars(true);
|
ClearVars(true);
|
||||||
|
|
||||||
pIP = 0;
|
m_ip_address = 0;
|
||||||
pLSID = scl->LSAccountID;
|
m_login_server_id = scl->LSAccountID;
|
||||||
strn0cpy(loginserver_account_name, scl->name, sizeof(loginserver_account_name));
|
strn0cpy(m_login_account_name, scl->name, sizeof(m_login_account_name));
|
||||||
strn0cpy(plskey, scl->lskey, sizeof(plskey));
|
strn0cpy(m_key, scl->lskey, sizeof(m_key));
|
||||||
pworldadmin = 0;
|
m_world_admin = 0;
|
||||||
|
|
||||||
paccountid = scl->AccountID;
|
m_account_id = scl->AccountID;
|
||||||
strn0cpy(paccountname, scl->AccountName, sizeof(paccountname));
|
strn0cpy(m_account_name, scl->AccountName, sizeof(m_account_name));
|
||||||
padmin = scl->Admin;
|
m_admin = scl->Admin;
|
||||||
|
|
||||||
pinstance = 0;
|
m_instance = 0;
|
||||||
pLFGFromLevel = 0;
|
m_lfg_from_level = 0;
|
||||||
pLFGToLevel = 0;
|
m_lfg_to_level = 0;
|
||||||
pLFGMatchFilter = false;
|
m_lfg_match_filter = false;
|
||||||
memset(pLFGComments, 0, 64);
|
memset(m_lfg_comments, 0, 64);
|
||||||
|
|
||||||
if (iOnline >= CLE_Status::Zoning) {
|
if (online >= CLE_Status::Zoning) {
|
||||||
Update(iZS, scl, iOnline);
|
Update(z, scl, online);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SetOnline(iOnline);
|
SetOnline(online);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,16 +96,16 @@ ClientListEntry::~ClientListEntry()
|
|||||||
Camp(); // updates zoneserver's numplayers
|
Camp(); // updates zoneserver's numplayers
|
||||||
client_list.RemoveCLEReferances(this);
|
client_list.RemoveCLEReferances(this);
|
||||||
}
|
}
|
||||||
for (auto& elem : tell_queue) {
|
for (auto &elem: m_tell_queue) {
|
||||||
safe_delete_array(elem);
|
safe_delete_array(elem);
|
||||||
}
|
}
|
||||||
tell_queue.clear();
|
m_tell_queue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientListEntry::SetChar(uint32 iCharID, const char *iCharName)
|
void ClientListEntry::SetChar(uint32 iCharID, const char *iCharName)
|
||||||
{
|
{
|
||||||
pcharid = iCharID;
|
m_char_id = iCharID;
|
||||||
strn0cpy(pname, iCharName, sizeof(pname));
|
strn0cpy(m_char_name, iCharName, sizeof(m_char_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientListEntry::SetOnline(CLE_Status iOnline)
|
void ClientListEntry::SetOnline(CLE_Status iOnline)
|
||||||
@@ -142,20 +118,20 @@ void ClientListEntry::SetOnline(CLE_Status iOnline)
|
|||||||
static_cast<int>(iOnline)
|
static_cast<int>(iOnline)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (iOnline >= CLE_Status::Online && pOnline < CLE_Status::Online) {
|
if (iOnline >= CLE_Status::Online && m_online < CLE_Status::Online) {
|
||||||
numplayers++;
|
numplayers++;
|
||||||
}
|
}
|
||||||
else if (iOnline < CLE_Status::Online && pOnline >= CLE_Status::Online) {
|
else if (iOnline < CLE_Status::Online && m_online >= CLE_Status::Online) {
|
||||||
numplayers--;
|
numplayers--;
|
||||||
}
|
}
|
||||||
if (iOnline != CLE_Status::Online || pOnline < CLE_Status::Online) {
|
if (iOnline != CLE_Status::Online || m_online < CLE_Status::Online) {
|
||||||
pOnline = iOnline;
|
m_online = iOnline;
|
||||||
}
|
}
|
||||||
if (iOnline < CLE_Status::Zoning) {
|
if (iOnline < CLE_Status::Zoning) {
|
||||||
Camp();
|
Camp();
|
||||||
}
|
}
|
||||||
if (pOnline >= CLE_Status::Online) {
|
if (m_online >= CLE_Status::Online) {
|
||||||
stale = 0;
|
m_stale = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,49 +169,49 @@ void ClientListEntry::LSZoneChange(ZoneToZone_Struct *ztz)
|
|||||||
|
|
||||||
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 (m_zone_server != iZS) {
|
||||||
if (pzoneserver) {
|
if (m_zone_server) {
|
||||||
pzoneserver->RemovePlayer();
|
m_zone_server->RemovePlayer();
|
||||||
LSUpdate(pzoneserver);
|
LSUpdate(m_zone_server);
|
||||||
}
|
}
|
||||||
if (iZS) {
|
if (iZS) {
|
||||||
iZS->AddPlayer();
|
iZS->AddPlayer();
|
||||||
LSUpdate(iZS);
|
LSUpdate(iZS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pzoneserver = iZS;
|
m_zone_server = iZS;
|
||||||
pzone = scl->zone;
|
m_zone = scl->zone;
|
||||||
pinstance = scl->instance_id;
|
m_instance = scl->instance_id;
|
||||||
pcharid = scl->charid;
|
m_char_id = scl->charid;
|
||||||
|
|
||||||
strcpy(pname, scl->name);
|
strcpy(m_char_name, scl->name);
|
||||||
if (paccountid == 0) {
|
if (m_account_id == 0) {
|
||||||
paccountid = scl->AccountID;
|
m_account_id = scl->AccountID;
|
||||||
strcpy(paccountname, scl->AccountName);
|
strcpy(m_account_name, scl->AccountName);
|
||||||
strcpy(loginserver_account_name, scl->AccountName);
|
strcpy(m_login_account_name, scl->AccountName);
|
||||||
pIP = scl->IP;
|
m_ip_address = scl->IP;
|
||||||
pLSID = scl->LSAccountID;
|
m_login_server_id = scl->LSAccountID;
|
||||||
strn0cpy(plskey, scl->lskey, sizeof(plskey));
|
strn0cpy(m_key, scl->lskey, sizeof(m_key));
|
||||||
}
|
}
|
||||||
padmin = scl->Admin;
|
m_admin = scl->Admin;
|
||||||
plevel = scl->level;
|
m_level = scl->level;
|
||||||
pclass_ = scl->class_;
|
m_class_ = scl->class_;
|
||||||
prace = scl->race;
|
m_race = scl->race;
|
||||||
panon = scl->anon;
|
m_anon = scl->anon;
|
||||||
ptellsoff = scl->tellsoff;
|
m_tells_off = scl->tellsoff;
|
||||||
pguild_id = scl->guild_id;
|
m_guild_id = scl->guild_id;
|
||||||
pguild_rank = scl->guild_rank;
|
m_guild_rank = scl->guild_rank;
|
||||||
pguild_tribute_opt_in = scl->guild_tribute_opt_in;
|
m_guild_tribute_opt_in = scl->guild_tribute_opt_in;
|
||||||
pLFG = scl->LFG;
|
m_lfg = scl->LFG;
|
||||||
gm = scl->gm;
|
m_gm = scl->gm;
|
||||||
pClientVersion = scl->ClientVersion;
|
m_client_version = scl->ClientVersion;
|
||||||
|
|
||||||
// Fields from the LFG Window
|
// Fields from the LFG Window
|
||||||
if ((scl->LFGFromLevel != 0) && (scl->LFGToLevel != 0)) {
|
if ((scl->LFGFromLevel != 0) && (scl->LFGToLevel != 0)) {
|
||||||
pLFGFromLevel = scl->LFGFromLevel;
|
m_lfg_from_level = scl->LFGFromLevel;
|
||||||
pLFGToLevel = scl->LFGToLevel;
|
m_lfg_to_level = scl->LFGToLevel;
|
||||||
pLFGMatchFilter = scl->LFGMatchFilter;
|
m_lfg_match_filter = scl->LFGMatchFilter;
|
||||||
memcpy(pLFGComments, scl->LFGComments, sizeof(pLFGComments));
|
memcpy(m_lfg_comments, scl->LFGComments, sizeof(m_lfg_comments));
|
||||||
}
|
}
|
||||||
|
|
||||||
SetOnline(iOnline);
|
SetOnline(iOnline);
|
||||||
@@ -243,76 +219,76 @@ void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, CLE_
|
|||||||
|
|
||||||
void ClientListEntry::LeavingZone(ZoneServer *iZS, CLE_Status iOnline)
|
void ClientListEntry::LeavingZone(ZoneServer *iZS, CLE_Status iOnline)
|
||||||
{
|
{
|
||||||
if (iZS != 0 && iZS != pzoneserver) {
|
if (iZS != 0 && iZS != m_zone_server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SetOnline(iOnline);
|
SetOnline(iOnline);
|
||||||
|
|
||||||
shared_task_manager.RemoveActiveInvitationByCharacterID(CharID());
|
shared_task_manager.RemoveActiveInvitationByCharacterID(CharID());
|
||||||
|
|
||||||
if (pzoneserver) {
|
if (m_zone_server) {
|
||||||
pzoneserver->RemovePlayer();
|
m_zone_server->RemovePlayer();
|
||||||
LSUpdate(pzoneserver);
|
LSUpdate(m_zone_server);
|
||||||
}
|
}
|
||||||
pzoneserver = 0;
|
m_zone_server = 0;
|
||||||
pzone = 0;
|
m_zone = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientListEntry::ClearVars(bool iAll)
|
void ClientListEntry::ClearVars(bool iAll)
|
||||||
{
|
{
|
||||||
if (iAll) {
|
if (iAll) {
|
||||||
pOnline = CLE_Status::Never;
|
m_online = CLE_Status::Never;
|
||||||
stale = 0;
|
m_stale = 0;
|
||||||
|
|
||||||
pLSID = 0;
|
m_login_server_id = 0;
|
||||||
memset(loginserver_account_name, 0, sizeof(loginserver_account_name));
|
memset(m_login_account_name, 0, sizeof(m_login_account_name));
|
||||||
memset(plskey, 0, sizeof(plskey));
|
memset(m_key, 0, sizeof(m_key));
|
||||||
pworldadmin = 0;
|
m_world_admin = 0;
|
||||||
|
|
||||||
paccountid = 0;
|
m_account_id = 0;
|
||||||
memset(paccountname, 0, sizeof(paccountname));
|
memset(m_account_name, 0, sizeof(m_account_name));
|
||||||
padmin = AccountStatus::Player;
|
m_admin = AccountStatus::Player;
|
||||||
}
|
}
|
||||||
pzoneserver = 0;
|
m_zone_server = 0;
|
||||||
pzone = 0;
|
m_zone = 0;
|
||||||
pcharid = 0;
|
m_char_id = 0;
|
||||||
memset(pname, 0, sizeof(pname));
|
memset(m_char_name, 0, sizeof(m_char_name));
|
||||||
plevel = 0;
|
m_level = 0;
|
||||||
pclass_ = 0;
|
m_class_ = 0;
|
||||||
prace = 0;
|
m_race = 0;
|
||||||
panon = 0;
|
m_anon = 0;
|
||||||
ptellsoff = 0;
|
m_tells_off = 0;
|
||||||
pguild_id = GUILD_NONE;
|
m_guild_id = GUILD_NONE;
|
||||||
pguild_rank = 0;
|
m_guild_rank = 0;
|
||||||
pLFG = 0;
|
m_lfg = 0;
|
||||||
gm = 0;
|
m_gm = 0;
|
||||||
pClientVersion = 0;
|
m_client_version = 0;
|
||||||
for (auto& elem : tell_queue) {
|
for (auto &elem: m_tell_queue) {
|
||||||
safe_delete_array(elem);
|
safe_delete_array(elem);
|
||||||
}
|
}
|
||||||
tell_queue.clear();
|
m_tell_queue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientListEntry::Camp(ZoneServer *iZS)
|
void ClientListEntry::Camp(ZoneServer *iZS)
|
||||||
{
|
{
|
||||||
if (iZS != 0 && iZS != pzoneserver) {
|
if (iZS != 0 && iZS != m_zone_server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (pzoneserver) {
|
if (m_zone_server) {
|
||||||
pzoneserver->RemovePlayer();
|
m_zone_server->RemovePlayer();
|
||||||
LSUpdate(pzoneserver);
|
LSUpdate(m_zone_server);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearVars();
|
ClearVars();
|
||||||
|
|
||||||
stale = 0;
|
m_stale = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClientListEntry::CheckStale()
|
bool ClientListEntry::CheckStale()
|
||||||
{
|
{
|
||||||
stale++;
|
m_stale++;
|
||||||
if (stale > 20) {
|
if (m_stale > 20) {
|
||||||
if (pOnline > CLE_Status::Offline) {
|
if (m_online > CLE_Status::Offline) {
|
||||||
SetOnline(CLE_Status::Offline);
|
SetOnline(CLE_Status::Offline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,48 +300,50 @@ bool ClientListEntry::CheckStale()
|
|||||||
bool ClientListEntry::CheckAuth(uint32 loginserver_account_id, const char *key_password)
|
bool ClientListEntry::CheckAuth(uint32 loginserver_account_id, const char *key_password)
|
||||||
{
|
{
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"ls_account_id [{0}] key_password [{1}] plskey [{2}]",
|
"ls_account_id [{}] key_password [{}] key [{}]",
|
||||||
loginserver_account_id,
|
loginserver_account_id,
|
||||||
key_password,
|
key_password,
|
||||||
plskey
|
m_key
|
||||||
);
|
);
|
||||||
if (pLSID == loginserver_account_id && strncmp(plskey, key_password, 10) == 0) {
|
|
||||||
|
|
||||||
|
if (m_login_server_id == loginserver_account_id && strncmp(m_key, key_password, 10) == 0) {
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"ls_account_id [{0}] key_password [{1}] plskey [{2}] lsid [{3}] paccountid [{4}]",
|
"ls_account_id [{}] key_password [{}] m_key [{}] lsid [{}] m_account_id [{}]",
|
||||||
loginserver_account_id,
|
loginserver_account_id,
|
||||||
key_password,
|
key_password,
|
||||||
plskey,
|
m_key,
|
||||||
LSID(),
|
LSID(),
|
||||||
paccountid
|
m_account_id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (paccountid == 0 && LSID() > 0) {
|
// create account if it doesn't exist
|
||||||
|
if (m_account_id == 0 && LSID() > 0) {
|
||||||
int16 default_account_status = WorldConfig::get()->DefaultStatus;
|
int16 default_account_status = WorldConfig::get()->DefaultStatus;
|
||||||
|
|
||||||
paccountid = database.CreateAccount(
|
m_account_id = database.CreateAccount(
|
||||||
loginserver_account_name,
|
m_login_account_name,
|
||||||
std::string(),
|
std::string(),
|
||||||
default_account_status,
|
default_account_status,
|
||||||
source_loginserver,
|
m_source_loginserver,
|
||||||
LSID()
|
LSID()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!paccountid) {
|
if (!m_account_id) {
|
||||||
LogInfo(
|
LogError(
|
||||||
"Error adding local account for LS login: [{0}:{1}], duplicate name",
|
"Error adding local account for LS login [{}] [{}], duplicate name",
|
||||||
source_loginserver,
|
m_source_loginserver,
|
||||||
loginserver_account_name
|
m_login_account_name
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
strn0cpy(paccountname, loginserver_account_name, sizeof(paccountname));
|
strn0cpy(m_account_name, m_login_account_name, sizeof(m_account_name));
|
||||||
padmin = default_account_status;
|
m_admin = default_account_status;
|
||||||
}
|
}
|
||||||
std::string lsworldadmin;
|
std::string lsworldadmin;
|
||||||
if (database.GetVariable("honorlsworldadmin", lsworldadmin)) {
|
if (database.GetVariable("honorlsworldadmin", lsworldadmin)) {
|
||||||
if (Strings::ToInt(lsworldadmin) == 1 && pworldadmin != 0 && (padmin < pworldadmin || padmin == AccountStatus::Player)) {
|
if (Strings::ToInt(lsworldadmin) == 1 && m_world_admin != 0 &&
|
||||||
padmin = pworldadmin;
|
(m_admin < m_world_admin || m_admin == AccountStatus::Player)) {
|
||||||
|
m_admin = m_world_admin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -380,8 +358,8 @@ void ClientListEntry::ProcessTellQueue()
|
|||||||
}
|
}
|
||||||
|
|
||||||
ServerPacket *pack;
|
ServerPacket *pack;
|
||||||
auto it = tell_queue.begin();
|
auto it = m_tell_queue.begin();
|
||||||
while (it != tell_queue.end()) {
|
while (it != m_tell_queue.end()) {
|
||||||
pack = new ServerPacket(
|
pack = new ServerPacket(
|
||||||
ServerOP_ChannelMessage,
|
ServerOP_ChannelMessage,
|
||||||
sizeof(ServerChannelMessage_Struct) + strlen((*it)->message) + 1
|
sizeof(ServerChannelMessage_Struct) + strlen((*it)->message) + 1
|
||||||
@@ -390,8 +368,7 @@ void ClientListEntry::ProcessTellQueue()
|
|||||||
Server()->SendPacket(pack);
|
Server()->SendPacket(pack);
|
||||||
safe_delete(pack);
|
safe_delete(pack);
|
||||||
safe_delete_array(*it);
|
safe_delete_array(*it);
|
||||||
it = tell_queue.erase(it);
|
it = m_tell_queue.erase(it);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+100
-118
@@ -8,8 +8,7 @@
|
|||||||
#include "../common/rulesys.h"
|
#include "../common/rulesys.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
typedef enum
|
typedef enum {
|
||||||
{
|
|
||||||
Never,
|
Never,
|
||||||
Offline,
|
Offline,
|
||||||
Online,
|
Online,
|
||||||
@@ -18,7 +17,7 @@ typedef enum
|
|||||||
InZone
|
InZone
|
||||||
} CLE_Status;
|
} CLE_Status;
|
||||||
|
|
||||||
static const char * CLEStatusString[] = {
|
static const char *CLEStatusString[] = {
|
||||||
"Never",
|
"Never",
|
||||||
"Offline",
|
"Offline",
|
||||||
"Online",
|
"Online",
|
||||||
@@ -33,148 +32,131 @@ struct ServerClientList_Struct;
|
|||||||
class ClientListEntry {
|
class ClientListEntry {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
|
||||||
* @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(
|
ClientListEntry(
|
||||||
uint32 id,
|
uint32 id,
|
||||||
uint32 in_loginserver_id,
|
uint32 login_server_id,
|
||||||
const char *in_loginserver_name,
|
const char *login_server_name,
|
||||||
const char *in_login_name,
|
const char *account_name,
|
||||||
const char *in_login_key,
|
const char *login_key,
|
||||||
int16 in_is_world_admin = 0,
|
int16 is_world_admin = 0,
|
||||||
uint32 ip = 0,
|
uint32 ip_address = 0,
|
||||||
uint8 local = 0
|
uint8 local = 0
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
ClientListEntry(uint32 id, ZoneServer *z, ServerClientList_Struct *scl, CLE_Status online);
|
||||||
* @param id
|
|
||||||
* @param iZS
|
|
||||||
* @param scl
|
|
||||||
* @param iOnline
|
|
||||||
*/
|
|
||||||
ClientListEntry(uint32 id, uint32 iAccID, const char* iAccName, MD5& iMD5Pass, int16 iAdmin = AccountStatus::Player);
|
|
||||||
ClientListEntry(uint32 id, ZoneServer* iZS, ServerClientList_Struct* scl, CLE_Status iOnline);
|
|
||||||
~ClientListEntry();
|
~ClientListEntry();
|
||||||
bool CheckStale();
|
bool CheckStale();
|
||||||
void Update(ZoneServer* zoneserver, ServerClientList_Struct* scl, CLE_Status iOnline = CLE_Status::InZone);
|
void Update(ZoneServer *zoneserver, ServerClientList_Struct *scl, CLE_Status iOnline = CLE_Status::InZone);
|
||||||
void LSUpdate(ZoneServer* zoneserver);
|
void LSUpdate(ZoneServer *zoneserver);
|
||||||
void LSZoneChange(ZoneToZone_Struct* ztz);
|
void LSZoneChange(ZoneToZone_Struct *ztz);
|
||||||
bool CheckAuth(uint32 loginserver_account_id, const char* key_password);
|
bool CheckAuth(uint32 loginserver_account_id, const char *key_password);
|
||||||
void SetOnline(CLE_Status iOnline = CLE_Status::Online);
|
void SetOnline(CLE_Status iOnline = CLE_Status::Online);
|
||||||
void SetChar(uint32 iCharID, const char* iCharName);
|
void SetChar(uint32 iCharID, const char *iCharName);
|
||||||
inline CLE_Status Online() { return pOnline; }
|
inline CLE_Status Online() { return m_online; }
|
||||||
inline const uint32 GetID() const { return id; }
|
inline const uint32 GetID() const { return m_id; }
|
||||||
inline const uint32 GetIP() const { return pIP; }
|
inline const uint32 GetIP() const { return m_ip_address; }
|
||||||
inline void SetIP(const uint32& iIP) { pIP = iIP; }
|
inline void SetIP(const uint32 &iIP) { m_ip_address = iIP; }
|
||||||
inline void KeepAlive() { stale = 0; }
|
inline void KeepAlive() { m_stale = 0; }
|
||||||
inline uint8 GetStaleCounter() const { return stale; }
|
inline uint8 GetStaleCounter() const { return m_stale; }
|
||||||
void LeavingZone(ZoneServer* iZS = 0, CLE_Status iOnline = CLE_Status::Offline);
|
void LeavingZone(ZoneServer *iZS = 0, CLE_Status iOnline = CLE_Status::Offline);
|
||||||
void Camp(ZoneServer* iZS = 0);
|
void Camp(ZoneServer *iZS = 0);
|
||||||
|
|
||||||
// Login Server stuff
|
// Login Server stuff
|
||||||
inline const char* LoginServer() const { return source_loginserver; }
|
inline const char *LoginServer() const { return m_source_loginserver; }
|
||||||
inline uint32 LSID() const { return pLSID; }
|
inline uint32 LSID() const { return m_login_server_id; }
|
||||||
inline uint32 LSAccountID() const { return pLSID; }
|
inline uint32 LSAccountID() const { return m_login_server_id; }
|
||||||
inline const char* LSName() const { return loginserver_account_name; }
|
inline const char *LSName() const { return m_login_account_name; }
|
||||||
inline int16 WorldAdmin() const { return pworldadmin; }
|
inline int16 WorldAdmin() const { return m_world_admin; }
|
||||||
inline const char* GetLSKey() const { return plskey; }
|
inline const char *GetLSKey() const { return m_key; }
|
||||||
inline const CLE_Status GetOnline() const { return pOnline; }
|
inline const CLE_Status GetOnline() const { return m_online; }
|
||||||
|
|
||||||
// Account stuff
|
// Account stuff
|
||||||
inline uint32 AccountID() const { return paccountid; }
|
inline uint32 AccountID() const { return m_account_id; }
|
||||||
inline const char* AccountName() const { return paccountname; }
|
inline const char *AccountName() const { return m_account_name; }
|
||||||
inline int16 Admin() const { return padmin; }
|
inline int16 Admin() const { return m_admin; }
|
||||||
inline void SetAdmin(uint16 iAdmin) { padmin = iAdmin; }
|
inline void SetAdmin(uint16 iAdmin) { m_admin = iAdmin; }
|
||||||
|
|
||||||
// Character info
|
// Character info
|
||||||
inline ZoneServer *Server() const { return pzoneserver; }
|
inline ZoneServer *Server() const { return m_zone_server; }
|
||||||
inline void ClearServer() { pzoneserver = 0; }
|
inline void ClearServer() { m_zone_server = 0; }
|
||||||
inline uint32 CharID() const { return pcharid; }
|
inline uint32 CharID() const { return m_char_id; }
|
||||||
inline const char *name() const { return pname; }
|
inline const char *name() const { return m_char_name; }
|
||||||
inline uint32 zone() const { return pzone; }
|
inline uint32 zone() const { return m_zone; }
|
||||||
inline uint16 instance() const { return pinstance; }
|
inline uint16 instance() const { return m_instance; }
|
||||||
inline uint8 level() const { return plevel; }
|
inline uint8 level() const { return m_level; }
|
||||||
inline uint8 class_() const { return pclass_; }
|
inline uint8 class_() const { return m_class_; }
|
||||||
inline uint16 race() const { return prace; }
|
inline uint16 race() const { return m_race; }
|
||||||
inline uint8 Anon() { return panon; }
|
inline uint8 Anon() { return m_anon; }
|
||||||
inline uint8 TellsOff() const { return ptellsoff; }
|
inline uint8 TellsOff() const { return m_tells_off; }
|
||||||
inline uint32 GuildID() const { return pguild_id; }
|
inline uint32 GuildID() const { return m_guild_id; }
|
||||||
inline uint32 GuildRank() const { return pguild_rank; }
|
inline uint32 GuildRank() const { return m_guild_rank; }
|
||||||
inline bool GuildTributeOptIn() const { return pguild_tribute_opt_in; }
|
inline bool GuildTributeOptIn() const { return m_guild_tribute_opt_in; }
|
||||||
inline void SetGuild(uint32 guild_id) { pguild_id = guild_id; }
|
inline void SetGuild(uint32 guild_id) { m_guild_id = guild_id; }
|
||||||
inline void SetGuildTributeOptIn(bool opt) { pguild_tribute_opt_in = opt; }
|
inline void SetGuildTributeOptIn(bool opt) { m_guild_tribute_opt_in = opt; }
|
||||||
inline bool LFG() const { return pLFG; }
|
inline bool LFG() const { return m_lfg; }
|
||||||
inline uint8 GetGM() const { return gm; }
|
inline uint8 GetGM() const { return m_gm; }
|
||||||
inline void SetGM(uint8 igm) { gm = igm; }
|
inline void SetGM(uint8 igm) { m_gm = igm; }
|
||||||
inline void SetZone(uint32 zone) { pzone = zone; }
|
inline void SetZone(uint32 zone) { m_zone = zone; }
|
||||||
inline bool IsLocalClient() const { return plocal; }
|
inline bool IsLocalClient() const { return m_is_local; }
|
||||||
inline uint8 GetLFGFromLevel() const { return pLFGFromLevel; }
|
inline uint8 GetLFGFromLevel() const { return m_lfg_from_level; }
|
||||||
inline uint8 GetLFGToLevel() const { return pLFGToLevel; }
|
inline uint8 GetLFGToLevel() const { return m_lfg_to_level; }
|
||||||
inline bool GetLFGMatchFilter() const { return pLFGMatchFilter; }
|
inline bool GetLFGMatchFilter() const { return m_lfg_match_filter; }
|
||||||
inline const char *GetLFGComments() const { return pLFGComments; }
|
inline const char *GetLFGComments() const { return m_lfg_comments; }
|
||||||
inline uint8 GetClientVersion() { return pClientVersion; }
|
inline uint8 GetClientVersion() { return m_client_version; }
|
||||||
|
|
||||||
inline bool TellQueueFull() const { return tell_queue.size() >= RuleI(World, TellQueueSize); }
|
inline bool TellQueueFull() const { return m_tell_queue.size() >= RuleI(World, TellQueueSize); }
|
||||||
inline bool TellQueueEmpty() const { return tell_queue.empty(); }
|
inline bool TellQueueEmpty() const { return m_tell_queue.empty(); }
|
||||||
inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { tell_queue.push_back(scm); }
|
inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { m_tell_queue.push_back(scm); }
|
||||||
void ProcessTellQueue();
|
void ProcessTellQueue();
|
||||||
|
|
||||||
void SetPendingDzInvite(ServerPacket* pack) { m_dz_invite.reset(pack->Copy()); };
|
void SetPendingDzInvite(ServerPacket *pack) { m_dz_invite.reset(pack->Copy()); };
|
||||||
std::unique_ptr<ServerPacket> GetPendingDzInvite() { return std::move(m_dz_invite); }
|
std::unique_ptr<ServerPacket> GetPendingDzInvite() { return std::move(m_dz_invite); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ClearVars(bool iAll = false);
|
void ClearVars(bool iAll = false);
|
||||||
|
|
||||||
const uint32 id;
|
const uint32 m_id;
|
||||||
uint32 pIP;
|
uint32 m_ip_address;
|
||||||
CLE_Status pOnline;
|
CLE_Status m_online;
|
||||||
uint8 stale;
|
uint8 m_stale;
|
||||||
|
|
||||||
// Login Server stuff
|
// Login Server stuff
|
||||||
char source_loginserver[64]{}; //Loginserver we came from.
|
char m_source_loginserver[64]{}; //Loginserver we came from.
|
||||||
uint32 pLSID;
|
uint32 m_login_server_id;
|
||||||
char loginserver_account_name[32]{};
|
char m_login_account_name[32]{};
|
||||||
char plskey[16]{};
|
char m_key[16]{};
|
||||||
int16 pworldadmin; // Login server's suggested admin status setting
|
int16 m_world_admin; // Login server's suggested admin status setting
|
||||||
bool plocal;
|
bool m_is_local;
|
||||||
|
|
||||||
// Account stuff
|
// Account stuff
|
||||||
uint32 paccountid;
|
uint32 m_account_id;
|
||||||
char paccountname[32]{};
|
char m_account_name[32]{};
|
||||||
int16 padmin{};
|
int16 m_admin{};
|
||||||
|
|
||||||
// Character info
|
// Character info
|
||||||
ZoneServer* pzoneserver{};
|
ZoneServer *m_zone_server{};
|
||||||
uint32 pzone{};
|
uint32 m_zone{};
|
||||||
uint16 pinstance{};
|
uint16 m_instance{};
|
||||||
uint32 pcharid{};
|
uint32 m_char_id{};
|
||||||
char pname[64]{};
|
char m_char_name[64]{};
|
||||||
uint8 plevel{};
|
uint8 m_level{};
|
||||||
uint8 pclass_{};
|
uint8 m_class_{};
|
||||||
uint16 prace{};
|
uint16 m_race{};
|
||||||
uint8 panon{};
|
uint8 m_anon{};
|
||||||
uint8 ptellsoff{};
|
uint8 m_tells_off{};
|
||||||
uint32 pguild_id{};
|
uint32 m_guild_id{};
|
||||||
uint32 pguild_rank;
|
uint32 m_guild_rank;
|
||||||
bool pguild_tribute_opt_in{};
|
bool m_guild_tribute_opt_in{};
|
||||||
bool pLFG{};
|
bool m_lfg{};
|
||||||
uint8 gm{};
|
uint8 m_gm{};
|
||||||
uint8 pClientVersion{};
|
uint8 m_client_version{};
|
||||||
uint8 pLFGFromLevel{};
|
uint8 m_lfg_from_level{};
|
||||||
uint8 pLFGToLevel{};
|
uint8 m_lfg_to_level{};
|
||||||
bool pLFGMatchFilter{};
|
bool m_lfg_match_filter{};
|
||||||
char pLFGComments[64]{};
|
char m_lfg_comments[64]{};
|
||||||
|
|
||||||
// Tell Queue -- really a vector :D
|
// Tell Queue -- really a vector :D
|
||||||
std::vector<ServerChannelMessage_Struct *> tell_queue;
|
std::vector<ServerChannelMessage_Struct *> m_tell_queue;
|
||||||
|
|
||||||
std::unique_ptr<ServerPacket> m_dz_invite;
|
std::unique_ptr<ServerPacket> m_dz_invite;
|
||||||
};
|
};
|
||||||
|
|||||||
+186
-56
@@ -42,11 +42,16 @@ extern ZSList zoneserver_list;
|
|||||||
uint32 numplayers = 0; //this really wants to be a member variable of ClientList...
|
uint32 numplayers = 0; //this really wants to be a member variable of ClientList...
|
||||||
|
|
||||||
ClientList::ClientList()
|
ClientList::ClientList()
|
||||||
: CLStale_timer(10000)
|
: CLStale_timer(10000),
|
||||||
|
m_poll_cache_timer(6000)
|
||||||
{
|
{
|
||||||
NextCLEID = 1;
|
NextCLEID = 1;
|
||||||
|
|
||||||
m_tick = std::make_unique<EQ::Timer>(5000, true, std::bind(&ClientList::OnTick, this, std::placeholders::_1));
|
m_tick = std::make_unique<EQ::Timer>(5000, true, std::bind(&ClientList::OnTick, this, std::placeholders::_1));
|
||||||
|
|
||||||
|
// pre-allocate / pin memory for the zone server caches
|
||||||
|
m_gm_zone_server_ids.reserve(512);
|
||||||
|
m_guild_zone_server_ids.reserve(1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientList::~ClientList() {
|
ClientList::~ClientList() {
|
||||||
@@ -57,6 +62,10 @@ void ClientList::Process() {
|
|||||||
if (CLStale_timer.Check())
|
if (CLStale_timer.Check())
|
||||||
CLCheckStale();
|
CLCheckStale();
|
||||||
|
|
||||||
|
if (m_poll_cache_timer.Check()) {
|
||||||
|
RebuildZoneServerCaches();
|
||||||
|
}
|
||||||
|
|
||||||
LinkedListIterator<Client*> iterator(list);
|
LinkedListIterator<Client*> iterator(list);
|
||||||
|
|
||||||
iterator.Reset();
|
iterator.Reset();
|
||||||
@@ -331,8 +340,26 @@ void ClientList::SendCLEList(const int16& admin, const char* to, WorldTCPConnect
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ClientList::CLEAdd(uint32 iLSID, const char *iLoginServerName, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin, uint32 ip, uint8 local) {
|
void ClientList::CLEAdd(
|
||||||
auto tmp = new ClientListEntry(GetNextCLEID(), iLSID, iLoginServerName, iLoginName, iLoginKey, iWorldAdmin, ip, local);
|
uint32 login_server_id,
|
||||||
|
const char *login_server_name,
|
||||||
|
const char *login_name,
|
||||||
|
const char *login_key,
|
||||||
|
int16 world_admin,
|
||||||
|
uint32 ip_address,
|
||||||
|
uint8 is_local
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto tmp = new ClientListEntry(
|
||||||
|
GetNextCLEID(),
|
||||||
|
login_server_id,
|
||||||
|
login_server_name,
|
||||||
|
login_name,
|
||||||
|
login_key,
|
||||||
|
world_admin,
|
||||||
|
ip_address,
|
||||||
|
is_local
|
||||||
|
);
|
||||||
|
|
||||||
clientlist.Append(tmp);
|
clientlist.Append(tmp);
|
||||||
}
|
}
|
||||||
@@ -366,6 +393,7 @@ void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *s
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cle->Update(zoneserver, scl);
|
cle->Update(zoneserver, scl);
|
||||||
|
AddToZoneServerCaches(cle);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -440,6 +468,7 @@ void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *s
|
|||||||
);
|
);
|
||||||
|
|
||||||
clientlist.Insert(cle);
|
clientlist.Insert(cle);
|
||||||
|
AddToZoneServerCaches(cle);
|
||||||
zoneserver->ChangeWID(scl->charid, cle->GetID());
|
zoneserver->ChangeWID(scl->charid, cle->GetID());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,19 +486,19 @@ void ClientList::CLEKeepAlive(uint32 numupdates, uint32* wid) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientListEntry *ClientList::CheckAuth(uint32 iLSID, const char *iKey)
|
ClientListEntry *ClientList::CheckAuth(uint32 loginserver_account_id, const char *key)
|
||||||
{
|
{
|
||||||
LinkedListIterator<ClientListEntry *> iterator(clientlist);
|
LinkedListIterator<ClientListEntry *> iterator(clientlist);
|
||||||
|
|
||||||
iterator.Reset();
|
iterator.Reset();
|
||||||
while (iterator.MoreElements()) {
|
while (iterator.MoreElements()) {
|
||||||
if (iterator.GetData()->CheckAuth(iLSID, iKey)) {
|
if (iterator.GetData()->CheckAuth(loginserver_account_id, key)) {
|
||||||
return iterator.GetData();
|
return iterator.GetData();
|
||||||
}
|
}
|
||||||
iterator.Advance();
|
iterator.Advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientList::SendOnlineGuildMembers(uint32 FromID, uint32 GuildID)
|
void ClientList::SendOnlineGuildMembers(uint32 FromID, uint32 GuildID)
|
||||||
@@ -1590,7 +1619,7 @@ void ClientList::OnTick(EQ::Timer *t)
|
|||||||
/**
|
/**
|
||||||
* @param response
|
* @param response
|
||||||
*/
|
*/
|
||||||
void ClientList::GetClientList(Json::Value &response)
|
void ClientList::GetClientList(Json::Value &response, bool full_list)
|
||||||
{
|
{
|
||||||
LinkedListIterator<ClientListEntry *> Iterator(clientlist);
|
LinkedListIterator<ClientListEntry *> Iterator(clientlist);
|
||||||
|
|
||||||
@@ -1601,62 +1630,68 @@ void ClientList::GetClientList(Json::Value &response)
|
|||||||
|
|
||||||
Json::Value row;
|
Json::Value row;
|
||||||
|
|
||||||
row["account_id"] = cle->AccountID();
|
row["id"] = cle->GetID();
|
||||||
row["account_name"] = cle->AccountName();
|
row["name"] = cle->name();
|
||||||
row["admin"] = cle->Admin();
|
row["level"] = cle->level();
|
||||||
row["id"] = cle->GetID();
|
row["ip"] = cle->GetIP();
|
||||||
row["ip"] = cle->GetIP();
|
row["gm"] = cle->GetGM();
|
||||||
row["loginserver_account_id"] = cle->LSAccountID();
|
row["race"] = cle->race();
|
||||||
row["loginserver_id"] = cle->LSID();
|
row["class"] = cle->class_();
|
||||||
row["loginserver_name"] = cle->LSName();
|
row["client_version"] = cle->GetClientVersion();
|
||||||
row["online"] = cle->Online();
|
row["admin"] = cle->Admin();
|
||||||
row["world_admin"] = cle->WorldAdmin();
|
row["account_id"] = cle->AccountID();
|
||||||
|
row["account_name"] = cle->AccountName();
|
||||||
|
row["character_id"] = cle->CharID();
|
||||||
|
row["anon"] = cle->Anon();
|
||||||
|
row["guild_id"] = cle->GuildID();
|
||||||
|
|
||||||
|
if (full_list) {
|
||||||
|
row["loginserver_account_id"] = cle->LSAccountID();
|
||||||
|
row["loginserver_id"] = cle->LSID();
|
||||||
|
row["loginserver_name"] = cle->LSName();
|
||||||
|
row["online"] = cle->Online();
|
||||||
|
row["world_admin"] = cle->WorldAdmin();
|
||||||
|
row["guild_rank"] = cle->GuildRank();
|
||||||
|
row["guild_tribute_opt_in"] = cle->GuildTributeOptIn();
|
||||||
|
row["instance"] = cle->instance();
|
||||||
|
row["is_local_client"] = cle->IsLocalClient();
|
||||||
|
row["lfg"] = cle->LFG();
|
||||||
|
row["lfg_comments"] = cle->GetLFGComments();
|
||||||
|
row["lfg_from_level"] = cle->GetLFGFromLevel();
|
||||||
|
row["lfg_match_filter"] = cle->GetLFGMatchFilter();
|
||||||
|
row["lfg_to_level"] = cle->GetLFGToLevel();
|
||||||
|
row["tells_off"] = cle->TellsOff();
|
||||||
|
row["zone"] = cle->zone();
|
||||||
|
}
|
||||||
|
|
||||||
auto server = cle->Server();
|
auto server = cle->Server();
|
||||||
if (server) {
|
if (server) {
|
||||||
row["server"]["client_address"] = server->GetCAddress();
|
row["server"]["zone_id"] = server->GetZoneID();
|
||||||
row["server"]["client_local_address"] = server->GetCLocalAddress();
|
row["server"]["zone_long_name"] = server->GetZoneLongName();
|
||||||
row["server"]["client_port"] = server->GetCPort();
|
row["server"]["zone_name"] = server->GetZoneName();
|
||||||
row["server"]["compile_time"] = server->GetCompileTime();
|
row["server"]["zone_os_pid"] = server->GetZoneOSProcessID();
|
||||||
row["server"]["id"] = server->GetID();
|
row["server"]["id"] = server->GetID();
|
||||||
row["server"]["instance_id"] = server->GetInstanceID();
|
|
||||||
row["server"]["ip"] = server->GetIP();
|
if (full_list) {
|
||||||
row["server"]["is_booting"] = server->IsBootingUp();
|
row["server"]["client_address"] = server->GetCAddress();
|
||||||
row["server"]["launch_name"] = server->GetLaunchName();
|
row["server"]["client_local_address"] = server->GetCLocalAddress();
|
||||||
row["server"]["launched_name"] = server->GetLaunchedName();
|
row["server"]["client_port"] = server->GetCPort();
|
||||||
row["server"]["number_players"] = server->NumPlayers();
|
row["server"]["compile_time"] = server->GetCompileTime();
|
||||||
row["server"]["port"] = server->GetPort();
|
row["server"]["instance_id"] = server->GetInstanceID();
|
||||||
row["server"]["previous_zone_id"] = server->GetPrevZoneID();
|
row["server"]["ip"] = server->GetIP();
|
||||||
row["server"]["static_zone"] = server->IsStaticZone();
|
row["server"]["is_booting"] = server->IsBootingUp();
|
||||||
row["server"]["uui"] = server->GetUUID();
|
row["server"]["launch_name"] = server->GetLaunchName();
|
||||||
row["server"]["zone_id"] = server->GetZoneID();
|
row["server"]["launched_name"] = server->GetLaunchedName();
|
||||||
row["server"]["zone_long_name"] = server->GetZoneLongName();
|
row["server"]["number_players"] = server->NumPlayers();
|
||||||
row["server"]["zone_name"] = server->GetZoneName();
|
row["server"]["port"] = server->GetPort();
|
||||||
row["server"]["zone_os_pid"] = server->GetZoneOSProcessID();
|
row["server"]["previous_zone_id"] = server->GetPrevZoneID();
|
||||||
|
row["server"]["static_zone"] = server->IsStaticZone();
|
||||||
|
row["server"]["uui"] = server->GetUUID();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
row["server"] = Json::Value();
|
row["server"] = Json::Value();
|
||||||
}
|
}
|
||||||
row["anon"] = cle->Anon();
|
|
||||||
row["character_id"] = cle->CharID();
|
|
||||||
row["class"] = cle->class_();
|
|
||||||
row["client_version"] = cle->GetClientVersion();
|
|
||||||
row["gm"] = cle->GetGM();
|
|
||||||
row["guild_id"] = cle->GuildID();
|
|
||||||
row["guild_rank"] = cle->GuildRank();
|
|
||||||
row["guild_tribute_opt_in"] = cle->GuildTributeOptIn();
|
|
||||||
row["instance"] = cle->instance();
|
|
||||||
row["is_local_client"] = cle->IsLocalClient();
|
|
||||||
row["level"] = cle->level();
|
|
||||||
row["lfg"] = cle->LFG();
|
|
||||||
row["lfg_comments"] = cle->GetLFGComments();
|
|
||||||
row["lfg_from_level"] = cle->GetLFGFromLevel();
|
|
||||||
row["lfg_match_filter"] = cle->GetLFGMatchFilter();
|
|
||||||
row["lfg_to_level"] = cle->GetLFGToLevel();
|
|
||||||
row["name"] = cle->name();
|
|
||||||
row["race"] = cle->race();
|
|
||||||
row["tells_off"] = cle->TellsOff();
|
|
||||||
row["zone"] = cle->zone();
|
|
||||||
|
|
||||||
response.append(row);
|
response.append(row);
|
||||||
|
|
||||||
@@ -1832,3 +1867,98 @@ std::map<uint32, ClientListEntry *> ClientList::GetGuildClientsWithTributeOptIn(
|
|||||||
}
|
}
|
||||||
return guild_members;
|
return guild_members;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientList::RebuildZoneServerCaches()
|
||||||
|
{
|
||||||
|
// Clear without freeing memory (buckets stay allocated)
|
||||||
|
m_gm_zone_server_ids.clear();
|
||||||
|
m_guild_zone_server_ids.clear();
|
||||||
|
|
||||||
|
LinkedListIterator<ClientListEntry*> iterator(clientlist);
|
||||||
|
iterator.Reset();
|
||||||
|
|
||||||
|
while (iterator.MoreElements()) {
|
||||||
|
ClientListEntry* cle = iterator.GetData();
|
||||||
|
|
||||||
|
if (cle->Online() != CLE_Status::InZone || !cle->Server()) {
|
||||||
|
iterator.Advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t server_id = cle->Server()->GetID();
|
||||||
|
|
||||||
|
// Track GM zone server
|
||||||
|
if (cle->GetGM()) {
|
||||||
|
m_gm_zone_server_ids.insert(server_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track guild zone servers
|
||||||
|
if (cle->GuildID() > 0) {
|
||||||
|
auto& guild_set = m_guild_zone_server_ids[cle->GuildID()];
|
||||||
|
guild_set.insert(server_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator.Advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> ClientList::GetGuildZoneServers(uint32 guild_id)
|
||||||
|
{
|
||||||
|
if (RuleB(World, RealTimeCalculateGuilds)) {
|
||||||
|
std::vector<uint32_t> zone_server_ids;
|
||||||
|
std::unordered_set<uint32_t> seen_ids;
|
||||||
|
|
||||||
|
LinkedListIterator<ClientListEntry *> iterator(clientlist);
|
||||||
|
|
||||||
|
iterator.Reset();
|
||||||
|
while (iterator.MoreElements()) {
|
||||||
|
ClientListEntry *cle = iterator.GetData();
|
||||||
|
|
||||||
|
if (cle->Online() != CLE_Status::InZone) {
|
||||||
|
iterator.Advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cle->Server()) {
|
||||||
|
iterator.Advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cle->GuildID() == guild_id) {
|
||||||
|
uint32_t id = cle->Server()->GetID();
|
||||||
|
if (seen_ids.insert(id).second) {
|
||||||
|
zone_server_ids.emplace_back(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator.Advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
return zone_server_ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = m_guild_zone_server_ids.find(guild_id);
|
||||||
|
if (it == m_guild_zone_server_ids.end()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {it->second.begin(), it->second.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientList::AddToZoneServerCaches(ClientListEntry* cle)
|
||||||
|
{
|
||||||
|
if (!cle || cle->Online() != CLE_Status::InZone || !cle->Server()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t server_id = cle->Server()->GetID();
|
||||||
|
|
||||||
|
// Add GM zone server if applicable
|
||||||
|
if (cle->GetGM()) {
|
||||||
|
m_gm_zone_server_ids.insert(server_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add guild zone server if applicable
|
||||||
|
if (cle->GuildID() > 0) {
|
||||||
|
m_guild_zone_server_ids[cle->GuildID()].insert(server_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+17
-3
@@ -51,7 +51,7 @@ public:
|
|||||||
|
|
||||||
void ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl);
|
void ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl);
|
||||||
void CLERemoveZSRef(ZoneServer* iZS);
|
void CLERemoveZSRef(ZoneServer* iZS);
|
||||||
ClientListEntry* CheckAuth(uint32 iLSID, const char* iKey);
|
ClientListEntry* CheckAuth(uint32 loginserver_account_id, const char* key);
|
||||||
ClientListEntry* FindCharacter(const char* name);
|
ClientListEntry* FindCharacter(const char* name);
|
||||||
ClientListEntry* FindCLEByAccountID(uint32 iAccID);
|
ClientListEntry* FindCLEByAccountID(uint32 iAccID);
|
||||||
ClientListEntry* FindCLEByCharacterID(uint32 iCharID);
|
ClientListEntry* FindCLEByCharacterID(uint32 iCharID);
|
||||||
@@ -59,14 +59,14 @@ public:
|
|||||||
void DisconnectByIP(uint32 in_ip);
|
void DisconnectByIP(uint32 in_ip);
|
||||||
void CLCheckStale();
|
void CLCheckStale();
|
||||||
void CLEKeepAlive(uint32 numupdates, uint32* wid);
|
void CLEKeepAlive(uint32 numupdates, uint32* wid);
|
||||||
void CLEAdd(uint32 iLSID, const char* iLoginServerName, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = AccountStatus::Player, uint32 ip = 0, uint8 local=0);
|
void CLEAdd(uint32 login_server_id, const char* login_server_name, const char* login_name, const char* login_key, int16 world_admin = AccountStatus::Player, uint32 ip_address = 0, uint8 is_local=0);
|
||||||
void UpdateClientGuild(uint32 char_id, uint32 guild_id);
|
void UpdateClientGuild(uint32 char_id, uint32 guild_id);
|
||||||
bool IsAccountInGame(uint32 iLSID);
|
bool IsAccountInGame(uint32 iLSID);
|
||||||
|
|
||||||
int GetClientCount();
|
int GetClientCount();
|
||||||
void GetClients(const char *zone_name, std::vector<ClientListEntry *> &into);
|
void GetClients(const char *zone_name, std::vector<ClientListEntry *> &into);
|
||||||
|
|
||||||
void GetClientList(Json::Value &response);
|
void GetClientList(Json::Value &response, bool full_list = false);
|
||||||
void GetGuildClientList(Json::Value& response, uint32 guild_id);
|
void GetGuildClientList(Json::Value& response, uint32 guild_id);
|
||||||
|
|
||||||
void SendCharacterMessage(uint32_t character_id, int chat_type, const std::string& message);
|
void SendCharacterMessage(uint32_t character_id, int chat_type, const std::string& message);
|
||||||
@@ -76,6 +76,15 @@ public:
|
|||||||
void SendCharacterMessageID(const std::string& character_name, int chat_type, int eqstr_id, std::initializer_list<std::string> args = {});
|
void SendCharacterMessageID(const std::string& character_name, int chat_type, int eqstr_id, std::initializer_list<std::string> args = {});
|
||||||
void SendCharacterMessageID(ClientListEntry* character, int chat_type, int eqstr_id, std::initializer_list<std::string> args = {});
|
void SendCharacterMessageID(ClientListEntry* character, int chat_type, int eqstr_id, std::initializer_list<std::string> args = {});
|
||||||
|
|
||||||
|
void AddToZoneServerCaches(ClientListEntry* cle);
|
||||||
|
void RebuildZoneServerCaches();
|
||||||
|
|
||||||
|
std::vector<uint32_t> GetGuildZoneServers(uint32 guild_id);
|
||||||
|
inline std::vector<uint32_t> GetZoneServersWithGMs()
|
||||||
|
{
|
||||||
|
return {m_gm_zone_server_ids.begin(), m_gm_zone_server_ids.end()};
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OnTick(EQ::Timer *t);
|
void OnTick(EQ::Timer *t);
|
||||||
inline uint32 GetNextCLEID() { return NextCLEID++; }
|
inline uint32 GetNextCLEID() { return NextCLEID++; }
|
||||||
@@ -90,6 +99,11 @@ private:
|
|||||||
|
|
||||||
|
|
||||||
std::unique_ptr<EQ::Timer> m_tick;
|
std::unique_ptr<EQ::Timer> m_tick;
|
||||||
|
|
||||||
|
// Zone server routing caches
|
||||||
|
Timer m_poll_cache_timer;
|
||||||
|
std::unordered_set<uint32_t> m_gm_zone_server_ids;
|
||||||
|
std::unordered_map<uint32_t, std::unordered_set<uint32_t>> m_guild_zone_server_ids;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /*CLIENTLIST_H_*/
|
#endif /*CLIENTLIST_H_*/
|
||||||
|
|||||||
@@ -137,8 +137,11 @@ void DynamicZone::SetSecondsRemaining(uint32_t seconds_remaining)
|
|||||||
m_expire_time = now + new_remaining;
|
m_expire_time = now + new_remaining;
|
||||||
m_duration = std::chrono::duration_cast<std::chrono::seconds>(m_expire_time - m_start_time);
|
m_duration = std::chrono::duration_cast<std::chrono::seconds>(m_expire_time - m_start_time);
|
||||||
|
|
||||||
InstanceListRepository::UpdateDuration(database,
|
InstanceListRepository::UpdateDuration(
|
||||||
GetInstanceID(), static_cast<uint32_t>(m_duration.count()));
|
database,
|
||||||
|
GetInstanceID(),
|
||||||
|
static_cast<uint32_t>(m_duration.count())
|
||||||
|
);
|
||||||
|
|
||||||
SendZonesDurationUpdate(); // update zone caches and actual instance's timer
|
SendZonesDurationUpdate(); // update zone caches and actual instance's timer
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "zoneserver.h"
|
#include "zoneserver.h"
|
||||||
#include "../common/rulesys.h"
|
#include "../common/rulesys.h"
|
||||||
#include "../common/repositories/dynamic_zone_lockouts_repository.h"
|
#include "../common/repositories/dynamic_zone_lockouts_repository.h"
|
||||||
|
#include <cereal/types/utility.hpp>
|
||||||
|
|
||||||
extern ClientList client_list;
|
extern ClientList client_list;
|
||||||
extern ZSList zoneserver_list;
|
extern ZSList zoneserver_list;
|
||||||
@@ -169,6 +170,33 @@ void DynamicZoneManager::LoadTemplates()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DynamicZoneManager::SendBulkMemberStatuses(uint32_t zone_id, uint16_t inst_id)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<uint32_t, std::vector<DynamicZoneMember>>> dzs;
|
||||||
|
dzs.reserve(dynamic_zone_cache.size());
|
||||||
|
|
||||||
|
for (const auto& [dz_id, dz] : dynamic_zone_cache)
|
||||||
|
{
|
||||||
|
dzs.emplace_back(dz_id, dz->GetMembers());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream ss;
|
||||||
|
{
|
||||||
|
cereal::BinaryOutputArchive archive(ss);
|
||||||
|
archive(dzs);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view sv = ss.view();
|
||||||
|
|
||||||
|
size_t size = sizeof(ServerDzCerealData_Struct) + sv.size();
|
||||||
|
ServerPacket pack(ServerOP_DzGetBulkMemberStatuses, static_cast<uint32_t>(size));
|
||||||
|
auto buf = reinterpret_cast<ServerDzCerealData_Struct*>(pack.pBuffer);
|
||||||
|
buf->cereal_size = static_cast<uint32_t>(sv.size());
|
||||||
|
memcpy(buf->cereal_data, sv.data(), sv.size());
|
||||||
|
|
||||||
|
zoneserver_list.SendPacket(zone_id, inst_id, &pack);
|
||||||
|
}
|
||||||
|
|
||||||
void DynamicZoneManager::HandleZoneMessage(ServerPacket* pack)
|
void DynamicZoneManager::HandleZoneMessage(ServerPacket* pack)
|
||||||
{
|
{
|
||||||
switch (pack->opcode)
|
switch (pack->opcode)
|
||||||
@@ -338,6 +366,15 @@ void DynamicZoneManager::HandleZoneMessage(ServerPacket* pack)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ServerOP_DzGetBulkMemberStatuses:
|
||||||
|
{
|
||||||
|
auto buf = reinterpret_cast<ServerDzCerealData_Struct*>(pack->pBuffer);
|
||||||
|
if (buf->zone_id != 0 && !dynamic_zone_cache.empty())
|
||||||
|
{
|
||||||
|
SendBulkMemberStatuses(buf->zone_id, buf->inst_id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ServerOP_DzUpdateMemberStatus:
|
case ServerOP_DzUpdateMemberStatus:
|
||||||
{
|
{
|
||||||
auto buf = reinterpret_cast<ServerDzMemberStatus_Struct*>(pack->pBuffer);
|
auto buf = reinterpret_cast<ServerDzMemberStatus_Struct*>(pack->pBuffer);
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ public:
|
|||||||
std::unordered_map<uint32_t, std::unique_ptr<DynamicZone>> dynamic_zone_cache;
|
std::unordered_map<uint32_t, std::unique_ptr<DynamicZone>> dynamic_zone_cache;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SendBulkMemberStatuses(uint32_t zone_id, uint16_t inst_id);
|
||||||
|
|
||||||
Timer m_process_throttle_timer{};
|
Timer m_process_throttle_timer{};
|
||||||
std::unordered_map<uint32_t, DynamicZoneTemplatesRepository::DynamicZoneTemplates> m_dz_templates;
|
std::unordered_map<uint32_t, DynamicZoneTemplatesRepository::DynamicZoneTemplates> m_dz_templates;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ void callGetZoneList(Json::Value &response)
|
|||||||
row["client_address"] = zone->GetCAddress();
|
row["client_address"] = zone->GetCAddress();
|
||||||
row["client_local_address"] = zone->GetCLocalAddress();
|
row["client_local_address"] = zone->GetCLocalAddress();
|
||||||
row["client_port"] = zone->GetCPort();
|
row["client_port"] = zone->GetCPort();
|
||||||
|
row["compile_version"] = zone->GetCurrentVersion();
|
||||||
|
row["compile_date"] = zone->GetCompileDate();
|
||||||
row["compile_time"] = zone->GetCompileTime();
|
row["compile_time"] = zone->GetCompileTime();
|
||||||
row["id"] = zone->GetID();
|
row["id"] = zone->GetID();
|
||||||
row["instance_id"] = zone->GetInstanceID();
|
row["instance_id"] = zone->GetInstanceID();
|
||||||
@@ -109,9 +111,17 @@ void callGetDatabaseSchema(Json::Value &response)
|
|||||||
response.append(schema);
|
response.append(schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
void callGetClientList(Json::Value &response)
|
void callGetClientList(Json::Value &response, const std::vector<std::string> &args)
|
||||||
{
|
{
|
||||||
client_list.GetClientList(response);
|
// if args has "full"
|
||||||
|
bool full_list = false;
|
||||||
|
if (args.size() > 1) {
|
||||||
|
if (args[1] == "full") {
|
||||||
|
full_list = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client_list.GetClientList(response, full_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getReloadTypes(Json::Value &response)
|
void getReloadTypes(Json::Value &response)
|
||||||
@@ -125,6 +135,12 @@ void getReloadTypes(Json::Value &response)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void getServerCounts(Json::Value &response, const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
response["zone_count"] = zoneserver_list.GetServerListCount();
|
||||||
|
response["client_count"] = client_list.GetClientCount();
|
||||||
|
}
|
||||||
|
|
||||||
void EQEmuApiWorldDataService::reload(Json::Value &r, const std::vector<std::string> &args)
|
void EQEmuApiWorldDataService::reload(Json::Value &r, const std::vector<std::string> &args)
|
||||||
{
|
{
|
||||||
std::vector<std::string> commands{};
|
std::vector<std::string> commands{};
|
||||||
@@ -172,7 +188,7 @@ void EQEmuApiWorldDataService::get(Json::Value &r, const std::vector<std::string
|
|||||||
callGetDatabaseSchema(r);
|
callGetDatabaseSchema(r);
|
||||||
}
|
}
|
||||||
if (m == "get_client_list") {
|
if (m == "get_client_list") {
|
||||||
callGetClientList(r);
|
callGetClientList(r, args);
|
||||||
}
|
}
|
||||||
if (m == "get_reload_types") {
|
if (m == "get_reload_types") {
|
||||||
getReloadTypes(r);
|
getReloadTypes(r);
|
||||||
@@ -183,6 +199,9 @@ void EQEmuApiWorldDataService::get(Json::Value &r, const std::vector<std::string
|
|||||||
if (m == "get_guild_details") {
|
if (m == "get_guild_details") {
|
||||||
callGetGuildDetails(r, args);
|
callGetGuildDetails(r, args);
|
||||||
}
|
}
|
||||||
|
if (m == "get_server_counts") {
|
||||||
|
getServerCounts(r, args);
|
||||||
|
}
|
||||||
if (m == "lock_status") {
|
if (m == "lock_status") {
|
||||||
r["locked"] = WorldConfig::get()->Locked;
|
r["locked"] = WorldConfig::get()->Locked;
|
||||||
}
|
}
|
||||||
@@ -190,7 +209,6 @@ void EQEmuApiWorldDataService::get(Json::Value &r, const std::vector<std::string
|
|||||||
|
|
||||||
void EQEmuApiWorldDataService::callGetGuildDetails(Json::Value &response, const std::vector<std::string> &args)
|
void EQEmuApiWorldDataService::callGetGuildDetails(Json::Value &response, const std::vector<std::string> &args)
|
||||||
{
|
{
|
||||||
|
|
||||||
std::string command = !args[1].empty() ? args[1] : "";
|
std::string command = !args[1].empty() ? args[1] : "";
|
||||||
if (command.empty()) {
|
if (command.empty()) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
+41
-41
@@ -46,8 +46,8 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
const WorldConfig *Config = WorldConfig::get();
|
const WorldConfig *Config = WorldConfig::get();
|
||||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||||
|
|
||||||
UsertoWorldRequestLegacy_Struct *utwr = (UsertoWorldRequestLegacy_Struct *) p.Data();
|
UsertoWorldRequestLegacy *utwr = (UsertoWorldRequestLegacy *) p.Data();
|
||||||
uint32 id = database.GetAccountIDFromLSID("eqemu", utwr->lsaccountid);
|
uint32 id = database.GetAccountIDFromLSID("eqemu", utwr->lsaccountid);
|
||||||
int16 status = database.GetAccountStatus(id);
|
int16 status = database.GetAccountStatus(id);
|
||||||
|
|
||||||
LogDebug(
|
LogDebug(
|
||||||
@@ -63,11 +63,11 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
|
|
||||||
ServerPacket outpack;
|
ServerPacket outpack;
|
||||||
outpack.opcode = ServerOP_UsertoWorldRespLeg;
|
outpack.opcode = ServerOP_UsertoWorldRespLeg;
|
||||||
outpack.size = sizeof(UsertoWorldResponseLegacy_Struct);
|
outpack.size = sizeof(UsertoWorldResponseLegacy);
|
||||||
outpack.pBuffer = new uchar[outpack.size];
|
outpack.pBuffer = new uchar[outpack.size];
|
||||||
memset(outpack.pBuffer, 0, outpack.size);
|
memset(outpack.pBuffer, 0, outpack.size);
|
||||||
|
|
||||||
UsertoWorldResponseLegacy_Struct *utwrs = (UsertoWorldResponseLegacy_Struct *) outpack.pBuffer;
|
UsertoWorldResponseLegacy *utwrs = (UsertoWorldResponseLegacy *) outpack.pBuffer;
|
||||||
utwrs->lsaccountid = utwr->lsaccountid;
|
utwrs->lsaccountid = utwr->lsaccountid;
|
||||||
utwrs->ToID = utwr->FromID;
|
utwrs->ToID = utwr->FromID;
|
||||||
utwrs->worldid = utwr->worldid;
|
utwrs->worldid = utwr->worldid;
|
||||||
@@ -126,8 +126,8 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
const WorldConfig *Config = WorldConfig::get();
|
const WorldConfig *Config = WorldConfig::get();
|
||||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||||
|
|
||||||
UsertoWorldRequest_Struct *utwr = (UsertoWorldRequest_Struct *) p.Data();
|
UsertoWorldRequest *utwr = (UsertoWorldRequest *) p.Data();
|
||||||
uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid);
|
uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid);
|
||||||
int16 status = database.GetAccountStatus(id);
|
int16 status = database.GetAccountStatus(id);
|
||||||
|
|
||||||
LogDebug(
|
LogDebug(
|
||||||
@@ -143,11 +143,11 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
|
|
||||||
ServerPacket outpack;
|
ServerPacket outpack;
|
||||||
outpack.opcode = ServerOP_UsertoWorldResp;
|
outpack.opcode = ServerOP_UsertoWorldResp;
|
||||||
outpack.size = sizeof(UsertoWorldResponse_Struct);
|
outpack.size = sizeof(UsertoWorldResponse);
|
||||||
outpack.pBuffer = new uchar[outpack.size];
|
outpack.pBuffer = new uchar[outpack.size];
|
||||||
memset(outpack.pBuffer, 0, outpack.size);
|
memset(outpack.pBuffer, 0, outpack.size);
|
||||||
|
|
||||||
UsertoWorldResponse_Struct *utwrs = (UsertoWorldResponse_Struct *) outpack.pBuffer;
|
UsertoWorldResponse *utwrs = (UsertoWorldResponse *) outpack.pBuffer;
|
||||||
utwrs->lsaccountid = utwr->lsaccountid;
|
utwrs->lsaccountid = utwr->lsaccountid;
|
||||||
utwrs->ToID = utwr->FromID;
|
utwrs->ToID = utwr->FromID;
|
||||||
strn0cpy(utwrs->login, utwr->login, 64);
|
strn0cpy(utwrs->login, utwr->login, 64);
|
||||||
@@ -208,27 +208,27 @@ void LoginServer::ProcessLSClientAuthLegacy(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto client_authentication_request = p.GetSerialize<ClientAuthLegacy_Struct>(0);
|
auto r = p.GetSerialize<ClientAuthLegacy>(0);
|
||||||
|
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"Processing Loginserver Auth Legacy | account_id [{0}] account_name [{1}] key [{2}] admin [{3}] ip [{4}] "
|
"Processing Loginserver Auth Legacy | account_id [{}] account_name [{}] key [{}] admin [{}] ip [{}] "
|
||||||
"local_network [{5}]",
|
"local_network [{}]",
|
||||||
client_authentication_request.loginserver_account_id,
|
r.loginserver_account_id,
|
||||||
client_authentication_request.loginserver_account_name,
|
r.loginserver_account_name,
|
||||||
client_authentication_request.key,
|
r.key,
|
||||||
client_authentication_request.is_world_admin,
|
r.is_world_admin,
|
||||||
client_authentication_request.ip,
|
r.ip_address,
|
||||||
client_authentication_request.is_client_from_local_network
|
r.is_client_from_local_network
|
||||||
);
|
);
|
||||||
|
|
||||||
client_list.CLEAdd(
|
client_list.CLEAdd(
|
||||||
client_authentication_request.loginserver_account_id,
|
r.loginserver_account_id,
|
||||||
"eqemu",
|
"eqemu",
|
||||||
client_authentication_request.loginserver_account_name,
|
r.loginserver_account_name,
|
||||||
client_authentication_request.key,
|
r.key,
|
||||||
client_authentication_request.is_world_admin,
|
r.is_world_admin,
|
||||||
client_authentication_request.ip,
|
r.ip_address,
|
||||||
client_authentication_request.is_client_from_local_network
|
r.is_client_from_local_network
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (std::exception &ex) {
|
catch (std::exception &ex) {
|
||||||
@@ -242,28 +242,28 @@ void LoginServer::ProcessLSClientAuth(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto client_authentication_request = p.GetSerialize<ClientAuth_Struct>(0);
|
auto r = p.GetSerialize<ClientAuth>(0);
|
||||||
|
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"Processing Loginserver Auth | account_id [{0}] account_name [{1}] loginserver_name [{2}] key [{3}] "
|
"Processing Loginserver Auth | account_id [{}] account_name [{}] loginserver_name [{}] key [{}] "
|
||||||
"admin [{4}] ip [{5}] local_network [{6}]",
|
"admin [{}] ip [{}] local_network [{}]",
|
||||||
client_authentication_request.loginserver_account_id,
|
r.loginserver_account_id,
|
||||||
client_authentication_request.account_name,
|
r.account_name,
|
||||||
client_authentication_request.loginserver_name,
|
r.loginserver_name,
|
||||||
client_authentication_request.key,
|
r.key,
|
||||||
client_authentication_request.is_world_admin,
|
r.is_world_admin,
|
||||||
client_authentication_request.ip,
|
r.ip_address,
|
||||||
client_authentication_request.is_client_from_local_network
|
r.is_client_from_local_network
|
||||||
);
|
);
|
||||||
|
|
||||||
client_list.CLEAdd(
|
client_list.CLEAdd(
|
||||||
client_authentication_request.loginserver_account_id,
|
r.loginserver_account_id,
|
||||||
client_authentication_request.loginserver_name,
|
r.loginserver_name,
|
||||||
client_authentication_request.account_name,
|
r.account_name,
|
||||||
client_authentication_request.key,
|
r.key,
|
||||||
client_authentication_request.is_world_admin,
|
r.is_world_admin,
|
||||||
client_authentication_request.ip,
|
r.ip_address,
|
||||||
client_authentication_request.is_client_from_local_network
|
r.is_client_from_local_network
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (std::exception &ex) {
|
catch (std::exception &ex) {
|
||||||
@@ -286,7 +286,7 @@ void LoginServer::ProcessLSFatalError(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
if (error.find("Worldserver Account / Password INVALID") != std::string::npos) {
|
if (error.find("Worldserver Account / Password INVALID") != std::string::npos) {
|
||||||
reason = "Usually this indicates you do not have a valid [account] and [password] (worldserver) account associated with your loginserver configuration. ";
|
reason = "Usually this indicates you do not have a valid [account] and [password] (worldserver) account associated with your loginserver configuration. ";
|
||||||
if (fmt::format("{}", m_loginserver_address).find("login.eqemulator.net") != std::string::npos) {
|
if (fmt::format("{}", m_loginserver_address).find("login.eqemulator.net") != std::string::npos) {
|
||||||
reason += "For Legacy EQEmulator connections, you need to register your server @ http://www.eqemulator.org/account/?LS";
|
reason += "For Legacy EQEmulator connections, you need to register your server @ https://www.eqemulator.org/index.php?pageid=ws_mgmt";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-5
@@ -89,6 +89,7 @@
|
|||||||
#include "../common/events/player_event_logs.h"
|
#include "../common/events/player_event_logs.h"
|
||||||
#include "../common/skill_caps.h"
|
#include "../common/skill_caps.h"
|
||||||
#include "../common/repositories/character_parcels_repository.h"
|
#include "../common/repositories/character_parcels_repository.h"
|
||||||
|
#include "../common/ip_util.h"
|
||||||
|
|
||||||
SkillCaps skill_caps;
|
SkillCaps skill_caps;
|
||||||
ZoneStore zone_store;
|
ZoneStore zone_store;
|
||||||
@@ -188,6 +189,11 @@ int main(int argc, char **argv)
|
|||||||
launcher_list.LoadList();
|
launcher_list.LoadList();
|
||||||
zoneserver_list.Init();
|
zoneserver_list.Init();
|
||||||
|
|
||||||
|
if (IpUtil::IsPortInUse(Config->WorldIP, Config->WorldTCPPort)) {
|
||||||
|
LogError("World port [{}] already in use", Config->WorldTCPPort);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<EQ::Net::ConsoleServer> console;
|
std::unique_ptr<EQ::Net::ConsoleServer> console;
|
||||||
if (Config->TelnetEnabled) {
|
if (Config->TelnetEnabled) {
|
||||||
LogInfo("Console (TCP) listener started on [{}:{}]", Config->TelnetIP, Config->TelnetTCPPort);
|
LogInfo("Console (TCP) listener started on [{}:{}]", Config->TelnetIP, Config->TelnetTCPPort);
|
||||||
@@ -375,11 +381,19 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
Timer player_event_process_timer(1000);
|
|
||||||
if (player_event_logs.LoadDatabaseConnection()) {
|
if (player_event_logs.LoadDatabaseConnection()) {
|
||||||
player_event_logs.Init();
|
player_event_logs.Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto event_log_processor = std::jthread([](const std::stop_token& stoken) {
|
||||||
|
while (!stoken.stop_requested()) {
|
||||||
|
if (!RuleB(Logging, PlayerEventsQSProcess)) {
|
||||||
|
player_event_logs.Process();
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
auto loop_fn = [&](EQ::Timer* t) {
|
auto loop_fn = [&](EQ::Timer* t) {
|
||||||
Timer::SetCurrentTime();
|
Timer::SetCurrentTime();
|
||||||
|
|
||||||
@@ -442,10 +456,6 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player_event_process_timer.Check()) {
|
|
||||||
std::jthread event_thread(&PlayerEventLogs::Process, &player_event_logs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PurgeInstanceTimer.Check()) {
|
if (PurgeInstanceTimer.Check()) {
|
||||||
database.PurgeExpiredInstances();
|
database.PurgeExpiredInstances();
|
||||||
database.PurgeAllDeletedDataBuckets();
|
database.PurgeAllDeletedDataBuckets();
|
||||||
@@ -496,6 +506,8 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
EQ::EventLoop::Get().Run();
|
EQ::EventLoop::Get().Run();
|
||||||
|
|
||||||
|
event_log_processor.request_stop();
|
||||||
|
|
||||||
LogInfo("World main loop completed");
|
LogInfo("World main loop completed");
|
||||||
LogInfo("Shutting down zone connections (if any)");
|
LogInfo("Shutting down zone connections (if any)");
|
||||||
zoneserver_list.KillAll();
|
zoneserver_list.KillAll();
|
||||||
|
|||||||
+21
-16
@@ -50,7 +50,7 @@ void WorldGuildManager::SendGuildRefresh(uint32 guild_id, bool name, bool motd,
|
|||||||
s->motd_change = motd;
|
s->motd_change = motd;
|
||||||
s->rank_change = rank;
|
s->rank_change = rank;
|
||||||
s->relation_change = relation;
|
s->relation_change = relation;
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(guild_id, pack);
|
||||||
safe_delete(pack);
|
safe_delete(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ void WorldGuildManager::SendCharRefresh(uint32 old_guild_id, uint32 guild_id, ui
|
|||||||
s->guild_id = guild_id;
|
s->guild_id = guild_id;
|
||||||
s->old_guild_id = old_guild_id;
|
s->old_guild_id = old_guild_id;
|
||||||
s->char_id = charid;
|
s->char_id = charid;
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(guild_id, pack);
|
||||||
safe_delete(pack);
|
safe_delete(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ void WorldGuildManager::SendGuildDelete(uint32 guild_id) {
|
|||||||
auto pack = new ServerPacket(ServerOP_DeleteGuild, sizeof(ServerGuildID_Struct));
|
auto pack = new ServerPacket(ServerOP_DeleteGuild, sizeof(ServerGuildID_Struct));
|
||||||
ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer;
|
ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer;
|
||||||
s->guild_id = guild_id;
|
s->guild_id = guild_id;
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(guild_id, pack);
|
||||||
safe_delete(pack);
|
safe_delete(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,15 +85,14 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer;
|
ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer;
|
||||||
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);
|
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.
|
//preform a local refresh.
|
||||||
if(!RefreshGuild(s->guild_id)) {
|
if(!RefreshGuild(s->guild_id)) {
|
||||||
LogGuilds("Unable to preform local refresh on guild [{}]", s->guild_id);
|
BaseGuildManager::RefreshGuild(s->guild_id);
|
||||||
//can we do anything?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//broadcast this packet to all zones.
|
||||||
|
zoneserver_list.SendPacketToZonesWithGuild(s->guild_id, pack);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +107,7 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
|
|
||||||
//broadcast this update to any zone with a member in this guild.
|
//broadcast this update to any zone with a member in this guild.
|
||||||
//because im sick of this not working, sending it to all zones, just spends a bit more bandwidth.
|
//because im sick of this not working, sending it to all zones, just spends a bit more bandwidth.
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(s->guild_id, pack);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -147,7 +146,7 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
auto s = (ServerGuildID_Struct *)pack->pBuffer;
|
auto s = (ServerGuildID_Struct *)pack->pBuffer;
|
||||||
RefreshGuild(s->guild_id);
|
RefreshGuild(s->guild_id);
|
||||||
|
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(s->guild_id, pack);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ServerOP_GuildPermissionUpdate:
|
case ServerOP_GuildPermissionUpdate:
|
||||||
@@ -179,7 +178,7 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
sg->function_value
|
sg->function_value
|
||||||
);
|
);
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(sg->guild_id, pack);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LogError("World Received ServerOP_GuildPermissionUpdate for guild [{}] function id {} with value of {} but guild could not be found.",
|
LogError("World Received ServerOP_GuildPermissionUpdate for guild [{}] function id {} with value of {} but guild could not be found.",
|
||||||
@@ -213,7 +212,7 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
rnc->rank,
|
rnc->rank,
|
||||||
rnc->rank_name
|
rnc->rank_name
|
||||||
);
|
);
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(rnc->guild_id, pack);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LogError("World Received ServerOP_GuildRankNameChange from zone for guild [{}] rank id {} with new name of {} but could not find guild.",
|
LogError("World Received ServerOP_GuildRankNameChange from zone for guild [{}] rank id {} with new name of {} but could not find guild.",
|
||||||
@@ -230,10 +229,10 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
case ServerOP_GuildChannel:
|
case ServerOP_GuildChannel:
|
||||||
case ServerOP_GuildURL:
|
case ServerOP_GuildURL:
|
||||||
case ServerOP_GuildMemberRemove:
|
case ServerOP_GuildMemberRemove:
|
||||||
case ServerOP_GuildSendGuildList:
|
|
||||||
case ServerOP_GuildMembersList:
|
case ServerOP_GuildMembersList:
|
||||||
{
|
{
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
auto in = (ServerOP_GuildMessage_Struct *) pack->pBuffer;
|
||||||
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, pack);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ServerOP_GuildMemberAdd:
|
case ServerOP_GuildMemberAdd:
|
||||||
@@ -244,9 +243,15 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
BaseGuildManager::RefreshGuild(in->guild_id);
|
BaseGuildManager::RefreshGuild(in->guild_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, pack);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ServerOP_GuildSendGuildList: {
|
||||||
|
auto in = (ServerOP_GuildMessage_Struct *) pack->pBuffer;
|
||||||
|
zoneserver_list.SendPacketToBootedZones(pack);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LogGuilds("Unknown packet {:#04x} received from zone??", pack->opcode);
|
LogGuilds("Unknown packet {:#04x} received from zone??", pack->opcode);
|
||||||
break;
|
break;
|
||||||
@@ -451,6 +456,6 @@ void WorldGuildManager::SendGuildTributeFavorAndTimer(uint32 guild_id, uint32 fa
|
|||||||
data->tribute_timer = time;
|
data->tribute_timer = time;
|
||||||
data->trophy_timer = 0;
|
data->trophy_timer = 0;
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(sp);
|
zoneserver_list.SendPacketToZonesWithGuild(guild_id, sp);
|
||||||
safe_delete(sp)
|
safe_delete(sp)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "../common/zone_store.h"
|
#include "../common/zone_store.h"
|
||||||
#include "../common/path_manager.h"
|
#include "../common/path_manager.h"
|
||||||
#include "../common/database/database_update.h"
|
#include "../common/database/database_update.h"
|
||||||
|
#include "../common/repositories/zone_state_spawns_repository.h"
|
||||||
|
|
||||||
extern ZSList zoneserver_list;
|
extern ZSList zoneserver_list;
|
||||||
extern WorldConfig Config;
|
extern WorldConfig Config;
|
||||||
@@ -101,6 +102,13 @@ bool WorldBoot::HandleCommandInput(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if we ran a valid command, this whole CLI handler needs to be improved at a later time
|
||||||
|
std::string arg1 = argc >= 2 ? argv[1] : "";
|
||||||
|
if (argc >= 2 && !Strings::Contains(arg1, ":")) {
|
||||||
|
std::cout << "Invalid command, use --help to see available commands" << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,6 +413,11 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
|
|||||||
LogInfo("Cleaning up instance corpses");
|
LogInfo("Cleaning up instance corpses");
|
||||||
database.CleanupInstanceCorpses();
|
database.CleanupInstanceCorpses();
|
||||||
|
|
||||||
|
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||||
|
ZoneStateSpawnsRepository::PurgeInvalidZoneStates(database);
|
||||||
|
ZoneStateSpawnsRepository::PurgeOldZoneStates(database);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+246
-288
@@ -29,18 +29,16 @@
|
|||||||
#include "../common/repositories/inventory_repository.h"
|
#include "../common/repositories/inventory_repository.h"
|
||||||
#include "../common/repositories/criteria/content_filter_criteria.h"
|
#include "../common/repositories/criteria/content_filter_criteria.h"
|
||||||
#include "../common/zone_store.h"
|
#include "../common/zone_store.h"
|
||||||
|
#include "../common/repositories/character_data_repository.h"
|
||||||
|
#include "../common/repositories/character_bind_repository.h"
|
||||||
|
#include "../common/repositories/character_material_repository.h"
|
||||||
|
#include "../common/repositories/start_zones_repository.h"
|
||||||
|
|
||||||
WorldDatabase database;
|
WorldDatabase database;
|
||||||
WorldDatabase content_db;
|
WorldDatabase content_db;
|
||||||
extern std::vector<RaceClassAllocation> character_create_allocations;
|
extern std::vector<RaceClassAllocation> character_create_allocations;
|
||||||
extern std::vector<RaceClassCombos> character_create_race_class_combos;
|
extern std::vector<RaceClassCombos> character_create_race_class_combos;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param account_id
|
|
||||||
* @param out_app
|
|
||||||
* @param client_version_bit
|
|
||||||
*/
|
|
||||||
void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit)
|
void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit)
|
||||||
{
|
{
|
||||||
EQ::versions::ClientVersion
|
EQ::versions::ClientVersion
|
||||||
@@ -56,187 +54,177 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o
|
|||||||
character_limit = 8;
|
character_limit = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string character_list_query = fmt::format(
|
auto characters = CharacterDataRepository::GetWhere(
|
||||||
SQL(
|
database,
|
||||||
SELECT
|
fmt::format(
|
||||||
`id`,
|
"`account_id` = {} AND `deleted_at` IS NULL ORDER BY `name` LIMIT {}",
|
||||||
`name`,
|
account_id,
|
||||||
`gender`,
|
character_limit
|
||||||
`race`,
|
)
|
||||||
`class`,
|
|
||||||
`level`,
|
|
||||||
`deity`,
|
|
||||||
`last_login`,
|
|
||||||
`time_played`,
|
|
||||||
`hair_color`,
|
|
||||||
`beard_color`,
|
|
||||||
`eye_color_1`,
|
|
||||||
`eye_color_2`,
|
|
||||||
`hair_style`,
|
|
||||||
`beard`,
|
|
||||||
`face`,
|
|
||||||
`drakkin_heritage`,
|
|
||||||
`drakkin_tattoo`,
|
|
||||||
`drakkin_details`,
|
|
||||||
`zone_id`
|
|
||||||
FROM
|
|
||||||
`character_data`
|
|
||||||
WHERE
|
|
||||||
`account_id` = {}
|
|
||||||
AND
|
|
||||||
`deleted_at` IS NULL
|
|
||||||
ORDER BY `name`
|
|
||||||
LIMIT {}
|
|
||||||
),
|
|
||||||
account_id,
|
|
||||||
character_limit
|
|
||||||
);
|
);
|
||||||
|
|
||||||
auto results = database.QueryDatabase(character_list_query);
|
size_t character_count = characters.size();
|
||||||
size_t character_count = results.RowCount();
|
if (characters.empty()) {
|
||||||
if (character_count == 0) {
|
|
||||||
*out_app = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
|
*out_app = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
|
||||||
CharacterSelect_Struct *cs = (CharacterSelect_Struct *) (*out_app)->pBuffer;
|
auto *cs = (CharacterSelect_Struct *) (*out_app)->pBuffer;
|
||||||
cs->CharCount = 0;
|
cs->CharCount = 0;
|
||||||
cs->TotalChars = character_limit;
|
cs->TotalChars = character_limit;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> character_ids;
|
||||||
|
for (auto &e: characters) {
|
||||||
|
character_ids.push_back(e.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& inventories = InventoryRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`character_id` IN ({}) AND `slot_id` BETWEEN {} AND {}",
|
||||||
|
Strings::Join(character_ids, ","),
|
||||||
|
EQ::invslot::slotHead,
|
||||||
|
EQ::invslot::slotFeet
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto& character_binds = CharacterBindRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`id` IN ({}) ORDER BY id, slot",
|
||||||
|
Strings::Join(character_ids, ",")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto& character_materials = CharacterMaterialRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`id` IN ({}) ORDER BY id, slot",
|
||||||
|
Strings::Join(character_ids, ",")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count);
|
size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count);
|
||||||
*out_app = new EQApplicationPacket(OP_SendCharInfo, packet_size);
|
*out_app = new EQApplicationPacket(OP_SendCharInfo, packet_size);
|
||||||
|
|
||||||
unsigned char *buff_ptr = (*out_app)->pBuffer;
|
unsigned char *buff_ptr = (*out_app)->pBuffer;
|
||||||
CharacterSelect_Struct *cs = (CharacterSelect_Struct *) buff_ptr;
|
auto *cs = (CharacterSelect_Struct *) buff_ptr;
|
||||||
|
|
||||||
cs->CharCount = character_count;
|
cs->CharCount = character_count;
|
||||||
cs->TotalChars = character_limit;
|
cs->TotalChars = character_limit;
|
||||||
|
|
||||||
buff_ptr += sizeof(CharacterSelect_Struct);
|
buff_ptr += sizeof(CharacterSelect_Struct);
|
||||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
for (auto &e: characters) {
|
||||||
CharacterSelectEntry_Struct *p_character_select_entry_struct = (CharacterSelectEntry_Struct *) buff_ptr;
|
auto *cse = (CharacterSelectEntry_Struct *) buff_ptr;
|
||||||
PlayerProfile_Struct pp;
|
PlayerProfile_Struct pp;
|
||||||
EQ::InventoryProfile inventory_profile;
|
EQ::InventoryProfile inv;
|
||||||
|
|
||||||
pp.SetPlayerProfileVersion(EQ::versions::ConvertClientVersionToMobVersion(client_version));
|
pp.SetPlayerProfileVersion(EQ::versions::ConvertClientVersionToMobVersion(client_version));
|
||||||
inventory_profile.SetInventoryVersion(client_version);
|
inv.SetInventoryVersion(client_version);
|
||||||
inventory_profile.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support
|
inv.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support
|
||||||
|
|
||||||
uint32 character_id = Strings::ToUnsignedInt(row[0]);
|
uint32 character_id = e.id;
|
||||||
uint8 has_home = 0;
|
uint8 has_home = 0;
|
||||||
uint8 has_bind = 0;
|
uint8 has_bind = 0;
|
||||||
|
|
||||||
memset(&pp, 0, sizeof(PlayerProfile_Struct));
|
memset(&pp, 0, sizeof(PlayerProfile_Struct));
|
||||||
memset(p_character_select_entry_struct->Name, 0, sizeof(p_character_select_entry_struct->Name));
|
memset(cse->Name, 0, sizeof(cse->Name));
|
||||||
strcpy(p_character_select_entry_struct->Name, row[1]);
|
strcpy(cse->Name, e.name.c_str());
|
||||||
p_character_select_entry_struct->Class = (uint8) Strings::ToUnsignedInt(row[4]);
|
cse->Class = e.class_;
|
||||||
p_character_select_entry_struct->Race = (uint32) Strings::ToUnsignedInt(row[3]);
|
cse->Race = e.race;
|
||||||
p_character_select_entry_struct->Level = (uint8) Strings::ToUnsignedInt(row[5]);
|
cse->Level = e.level;
|
||||||
p_character_select_entry_struct->ShroudClass = p_character_select_entry_struct->Class;
|
cse->ShroudClass = cse->Class;
|
||||||
p_character_select_entry_struct->ShroudRace = p_character_select_entry_struct->Race;
|
cse->ShroudRace = cse->Race;
|
||||||
p_character_select_entry_struct->Zone = (uint16) Strings::ToUnsignedInt(row[19]);
|
cse->Zone = e.zone_id;
|
||||||
p_character_select_entry_struct->Instance = 0;
|
cse->Instance = 0;
|
||||||
p_character_select_entry_struct->Gender = (uint8) Strings::ToUnsignedInt(row[2]);
|
cse->Gender = e.gender;
|
||||||
p_character_select_entry_struct->Face = (uint8) Strings::ToUnsignedInt(row[15]);
|
cse->Face = e.face;
|
||||||
|
|
||||||
for (uint32 material_slot = 0; material_slot < EQ::textures::materialCount; material_slot++) {
|
for (auto &s: cse->Equip) {
|
||||||
p_character_select_entry_struct->Equip[material_slot].Material = 0;
|
s.Material = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].Unknown1 = 0;
|
s.Unknown1 = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].EliteModel = 0;
|
s.EliteModel = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].HerosForgeModel = 0;
|
s.HerosForgeModel = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].Unknown2 = 0;
|
s.Unknown2 = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].Color = 0;
|
s.Color = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
p_character_select_entry_struct->Unknown15 = 0xFF;
|
cse->Unknown15 = 0xFF;
|
||||||
p_character_select_entry_struct->Unknown19 = 0xFF;
|
cse->Unknown19 = 0xFF;
|
||||||
p_character_select_entry_struct->DrakkinTattoo = (uint32) Strings::ToInt(row[17]);
|
cse->DrakkinTattoo = e.drakkin_tattoo;
|
||||||
p_character_select_entry_struct->DrakkinDetails = (uint32) Strings::ToInt(row[18]);
|
cse->DrakkinDetails = e.drakkin_details;
|
||||||
p_character_select_entry_struct->Deity = (uint32) Strings::ToInt(row[6]);
|
cse->Deity = e.deity;
|
||||||
p_character_select_entry_struct->PrimaryIDFile = 0; // Processed Below
|
cse->PrimaryIDFile = 0; // Processed Below
|
||||||
p_character_select_entry_struct->SecondaryIDFile = 0; // Processed Below
|
cse->SecondaryIDFile = 0; // Processed Below
|
||||||
p_character_select_entry_struct->HairColor = (uint8) Strings::ToInt(row[9]);
|
cse->HairColor = e.hair_color;
|
||||||
p_character_select_entry_struct->BeardColor = (uint8) Strings::ToInt(row[10]);
|
cse->BeardColor = e.beard_color;
|
||||||
p_character_select_entry_struct->EyeColor1 = (uint8) Strings::ToInt(row[11]);
|
cse->EyeColor1 = e.eye_color_1;
|
||||||
p_character_select_entry_struct->EyeColor2 = (uint8) Strings::ToInt(row[12]);
|
cse->EyeColor2 = e.eye_color_2;
|
||||||
p_character_select_entry_struct->HairStyle = (uint8) Strings::ToInt(row[13]);
|
cse->HairStyle = e.hair_style;
|
||||||
p_character_select_entry_struct->Beard = (uint8) Strings::ToInt(row[14]);
|
cse->Beard = e.beard;
|
||||||
p_character_select_entry_struct->GoHome = 0; // Processed Below
|
cse->GoHome = 0; // Processed Below
|
||||||
p_character_select_entry_struct->Tutorial = 0; // Processed Below
|
cse->Tutorial = 0; // Processed Below
|
||||||
p_character_select_entry_struct->DrakkinHeritage = (uint32) Strings::ToInt(row[16]);
|
cse->DrakkinHeritage = e.drakkin_heritage;
|
||||||
p_character_select_entry_struct->Unknown1 = 0;
|
cse->Unknown1 = 0;
|
||||||
p_character_select_entry_struct->Enabled = 1;
|
cse->Enabled = 1;
|
||||||
p_character_select_entry_struct->LastLogin = (uint32) Strings::ToInt(row[7]); // RoF2 value: 1212696584
|
cse->LastLogin = e.last_login; // RoF2 value: 1212696584
|
||||||
p_character_select_entry_struct->Unknown2 = 0;
|
cse->Unknown2 = 0;
|
||||||
|
|
||||||
if (RuleB(World, EnableReturnHomeButton)) {
|
if (RuleB(World, EnableReturnHomeButton)) {
|
||||||
int now = time(nullptr);
|
int now = time(nullptr);
|
||||||
if ((now - Strings::ToInt(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome))
|
if (now - e.last_login >= RuleI(World, MinOfflineTimeToReturnHome)) {
|
||||||
p_character_select_entry_struct->GoHome = 1;
|
cse->GoHome = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RuleB(World, EnableTutorialButton) && (p_character_select_entry_struct->Level <= RuleI(World, MaxLevelForTutorial)))
|
if (RuleB(World, EnableTutorialButton) && (cse->Level <= RuleI(World, MaxLevelForTutorial))) {
|
||||||
p_character_select_entry_struct->Tutorial = 1;
|
cse->Tutorial = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// binds
|
||||||
* Bind
|
int bind_count = 0;
|
||||||
*/
|
for (auto &bind : character_binds) {
|
||||||
character_list_query = fmt::format(
|
if (bind.id != e.id) {
|
||||||
SQL(
|
continue;
|
||||||
SELECT
|
|
||||||
`zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`
|
|
||||||
FROM
|
|
||||||
`character_bind`
|
|
||||||
WHERE
|
|
||||||
`id` = {}
|
|
||||||
LIMIT 5
|
|
||||||
),
|
|
||||||
character_id
|
|
||||||
);
|
|
||||||
auto results_bind = database.QueryDatabase(character_list_query);
|
|
||||||
auto bind_count = results_bind.RowCount();
|
|
||||||
for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) {
|
|
||||||
if (row_b[6] && Strings::ToInt(row_b[6]) == 4) {
|
|
||||||
has_home = 1;
|
|
||||||
// If our bind count is less than 5, we need to actually make use of this data so lets parse it
|
|
||||||
if (bind_count < 5) {
|
|
||||||
pp.binds[4].zone_id = Strings::ToInt(row_b[0]);
|
|
||||||
pp.binds[4].instance_id = Strings::ToInt(row_b[1]);
|
|
||||||
pp.binds[4].x = Strings::ToFloat(row_b[2]);
|
|
||||||
pp.binds[4].y = Strings::ToFloat(row_b[3]);
|
|
||||||
pp.binds[4].z = Strings::ToFloat(row_b[4]);
|
|
||||||
pp.binds[4].heading = Strings::ToFloat(row_b[5]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row_b[6] && Strings::ToInt(row_b[6]) == 0)
|
if (bind.slot == 4) {
|
||||||
|
has_home = 1;
|
||||||
|
pp.binds[4].zone_id = bind.zone_id;
|
||||||
|
pp.binds[4].instance_id = bind.instance_id;
|
||||||
|
pp.binds[4].x = bind.x;
|
||||||
|
pp.binds[4].y = bind.y;
|
||||||
|
pp.binds[4].z = bind.z;
|
||||||
|
pp.binds[4].heading = bind.heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind.slot == 0) {
|
||||||
has_bind = 1;
|
has_bind = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_home == 0 || has_bind == 0) {
|
if (has_home == 0 || has_bind == 0) {
|
||||||
std::string character_list_query = fmt::format(
|
const auto &start_zones = StartZonesRepository::GetWhere(
|
||||||
SQL(
|
content_db,
|
||||||
SELECT
|
fmt::format(
|
||||||
`zone_id`, `bind_id`, `x`, `y`, `z`, `heading`
|
"`player_class` = {} AND `player_deity` = {} AND `player_race` = {} {}",
|
||||||
FROM
|
cse->Class,
|
||||||
`start_zones`
|
cse->Deity,
|
||||||
WHERE
|
cse->Race,
|
||||||
`player_class` = {}
|
ContentFilterCriteria::apply().c_str()
|
||||||
AND
|
)
|
||||||
`player_deity` = {}
|
|
||||||
AND
|
|
||||||
`player_race` = {} {}
|
|
||||||
),
|
|
||||||
p_character_select_entry_struct->Class,
|
|
||||||
p_character_select_entry_struct->Deity,
|
|
||||||
p_character_select_entry_struct->Race,
|
|
||||||
ContentFilterCriteria::apply().c_str()
|
|
||||||
);
|
);
|
||||||
auto results_bind = content_db.QueryDatabase(character_list_query);
|
|
||||||
for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) {
|
|
||||||
/* If a bind_id is specified, make them start there */
|
|
||||||
if (Strings::ToInt(row_d[1]) != 0) {
|
|
||||||
pp.binds[4].zone_id = (uint32) Strings::ToInt(row_d[1]);
|
|
||||||
|
|
||||||
|
if (!start_zones.empty()) {
|
||||||
|
pp.binds[4].zone_id = start_zones[0].zone_id;
|
||||||
|
pp.binds[4].x = start_zones[0].x;
|
||||||
|
pp.binds[4].y = start_zones[0].y;
|
||||||
|
pp.binds[4].z = start_zones[0].z;
|
||||||
|
pp.binds[4].heading = start_zones[0].heading;
|
||||||
|
|
||||||
|
if (start_zones[0].bind_id != 0) {
|
||||||
|
pp.binds[4].zone_id = start_zones[0].bind_id;
|
||||||
auto z = GetZone(pp.binds[4].zone_id);
|
auto z = GetZone(pp.binds[4].zone_id);
|
||||||
if (z) {
|
if (z) {
|
||||||
pp.binds[4].x = z->safe_x;
|
pp.binds[4].x = z->safe_x;
|
||||||
@@ -244,168 +232,151 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o
|
|||||||
pp.binds[4].z = z->safe_z;
|
pp.binds[4].z = z->safe_z;
|
||||||
pp.binds[4].heading = z->safe_heading;
|
pp.binds[4].heading = z->safe_heading;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
/* Otherwise, use the zone and coordinates given */
|
pp.binds[4].zone_id = start_zones[0].zone_id;
|
||||||
else {
|
pp.binds[4].x = start_zones[0].x;
|
||||||
pp.binds[4].zone_id = (uint32) Strings::ToInt(row_d[0]);
|
pp.binds[4].y = start_zones[0].y;
|
||||||
float x = Strings::ToFloat(row_d[2]);
|
pp.binds[4].z = start_zones[0].z;
|
||||||
float y = Strings::ToFloat(row_d[3]);
|
pp.binds[4].heading = start_zones[0].heading;
|
||||||
float z = Strings::ToFloat(row_d[4]);
|
|
||||||
float heading = Strings::ToFloat(row_d[5]);
|
if (pp.binds[4].x == 0 && pp.binds[4].y == 0 && pp.binds[4].z == 0 && pp.binds[4].heading == 0) {
|
||||||
if (x == 0 && y == 0 && z == 0 && heading == 0) {
|
|
||||||
auto zone = GetZone(pp.binds[4].zone_id);
|
auto zone = GetZone(pp.binds[4].zone_id);
|
||||||
if (zone) {
|
if (zone) {
|
||||||
x = zone->safe_x;
|
pp.binds[4].x = zone->safe_x;
|
||||||
y = zone->safe_y;
|
pp.binds[4].y = zone->safe_y;
|
||||||
z = zone->safe_z;
|
pp.binds[4].z = zone->safe_z;
|
||||||
heading = zone->safe_heading;
|
pp.binds[4].heading = zone->safe_heading;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pp.binds[4].x = x;
|
|
||||||
pp.binds[4].y = y;
|
|
||||||
pp.binds[4].z = z;
|
|
||||||
pp.binds[4].heading = heading;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pp.binds[0] = pp.binds[4];
|
else {
|
||||||
/* If no home bind set, set it */
|
LogError("No start zone found for class [{}] deity [{}] race [{}]", cse->Class, cse->Deity, cse->Race);
|
||||||
if (has_home == 0) {
|
|
||||||
std::string query = fmt::format(
|
|
||||||
SQL(
|
|
||||||
REPLACE INTO
|
|
||||||
`character_bind`
|
|
||||||
(`id`, `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`)
|
|
||||||
VALUES ({}, {}, {}, {}, {}, {}, {}, {})
|
|
||||||
),
|
|
||||||
character_id,
|
|
||||||
pp.binds[4].zone_id,
|
|
||||||
0,
|
|
||||||
pp.binds[4].x,
|
|
||||||
pp.binds[4].y,
|
|
||||||
pp.binds[4].z,
|
|
||||||
pp.binds[4].heading,
|
|
||||||
4
|
|
||||||
);
|
|
||||||
auto results_bset = QueryDatabase(query);
|
|
||||||
}
|
}
|
||||||
/* If no regular bind set, set it */
|
|
||||||
|
|
||||||
|
pp.binds[0] = pp.binds[4];
|
||||||
|
|
||||||
|
// If we don't have home set, set it
|
||||||
|
if (has_home == 0) {
|
||||||
|
auto bind = CharacterBindRepository::NewEntity();
|
||||||
|
bind.id = character_id;
|
||||||
|
bind.zone_id = pp.binds[4].zone_id;
|
||||||
|
bind.instance_id = 0;
|
||||||
|
bind.x = pp.binds[4].x;
|
||||||
|
bind.y = pp.binds[4].y;
|
||||||
|
bind.z = pp.binds[4].z;
|
||||||
|
bind.heading = pp.binds[4].heading;
|
||||||
|
bind.slot = 4;
|
||||||
|
CharacterBindRepository::ReplaceOne(*this, bind);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have regular bind set, set it
|
||||||
if (has_bind == 0) {
|
if (has_bind == 0) {
|
||||||
std::string query = fmt::format(
|
auto bind = CharacterBindRepository::NewEntity();
|
||||||
SQL(
|
bind.id = character_id;
|
||||||
REPLACE INTO
|
bind.zone_id = pp.binds[0].zone_id;
|
||||||
`character_bind`
|
bind.instance_id = 0;
|
||||||
(`id`, `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`)
|
bind.x = pp.binds[0].x;
|
||||||
VALUES ({}, {}, {}, {}, {}, {}, {}, {})
|
bind.y = pp.binds[0].y;
|
||||||
),
|
bind.z = pp.binds[0].z;
|
||||||
character_id,
|
bind.heading = pp.binds[0].heading;
|
||||||
pp.binds[0].zone_id,
|
bind.slot = 0;
|
||||||
0,
|
CharacterBindRepository::ReplaceOne(*this, bind);
|
||||||
pp.binds[0].x,
|
|
||||||
pp.binds[0].y,
|
|
||||||
pp.binds[0].z,
|
|
||||||
pp.binds[0].heading,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
auto results_bset = QueryDatabase(query);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* If our bind count is less than 5, then we have null data that needs to be filled in. */
|
|
||||||
|
// If our bind count is less than 5, then we have null data that needs to be filled in
|
||||||
if (bind_count < 5) {
|
if (bind_count < 5) {
|
||||||
// we know that home and main bind must be valid here, so we don't check those
|
// we know that home and main bind must be valid here, so we don't check those
|
||||||
// we also use home to fill in the null data like live does.
|
// we also use home to fill in the null data like live does.
|
||||||
|
|
||||||
|
std::vector<CharacterBindRepository::CharacterBind> binds;
|
||||||
|
|
||||||
for (int i = 1; i < 4; i++) {
|
for (int i = 1; i < 4; i++) {
|
||||||
if (pp.binds[i].zone_id != 0) // we assume 0 is the only invalid one ...
|
if (pp.binds[i].zone_id != 0) { // we assume 0 is the only invalid one ...
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
std::string query = fmt::format(
|
auto bind = CharacterBindRepository::NewEntity();
|
||||||
SQL(
|
|
||||||
REPLACE INTO
|
bind.slot = i;
|
||||||
`character_bind`
|
bind.id = character_id;
|
||||||
(`id`, `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`)
|
bind.zone_id = pp.binds[4].zone_id;
|
||||||
VALUES ({}, {}, {}, {}, {}, {}, {}, {})
|
bind.instance_id = 0;
|
||||||
),
|
bind.x = pp.binds[4].x;
|
||||||
character_id,
|
bind.y = pp.binds[4].y;
|
||||||
pp.binds[4].zone_id,
|
bind.z = pp.binds[4].z;
|
||||||
0,
|
bind.heading = pp.binds[4].heading;
|
||||||
pp.binds[4].x,
|
binds.emplace_back(bind);
|
||||||
pp.binds[4].y,
|
|
||||||
pp.binds[4].z,
|
|
||||||
pp.binds[4].heading,
|
|
||||||
i
|
|
||||||
);
|
|
||||||
auto results_bset = QueryDatabase(query);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CharacterBindRepository::ReplaceMany(*this, binds);
|
||||||
}
|
}
|
||||||
|
|
||||||
character_list_query = fmt::format(
|
for (auto &cm : character_materials) {
|
||||||
SQL(
|
pp.item_tint.Slot[cm.slot].Red = cm.red;
|
||||||
SELECT
|
pp.item_tint.Slot[cm.slot].Green = cm.green;
|
||||||
`slot`, `red`, `green`, `blue`, `use_tint`, `color`
|
pp.item_tint.Slot[cm.slot].Blue = cm.blue;
|
||||||
FROM
|
pp.item_tint.Slot[cm.slot].UseTint = cm.use_tint;
|
||||||
`character_material`
|
pp.item_tint.Slot[cm.slot].Color = cm.color;
|
||||||
WHERE
|
|
||||||
`id` = {}
|
|
||||||
),
|
|
||||||
character_id
|
|
||||||
);
|
|
||||||
auto results_b = database.QueryDatabase(character_list_query);
|
|
||||||
uint8 slot = 0;
|
|
||||||
for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) {
|
|
||||||
slot = Strings::ToInt(row_b[0]);
|
|
||||||
pp.item_tint.Slot[slot].Red = Strings::ToInt(row_b[1]);
|
|
||||||
pp.item_tint.Slot[slot].Green = Strings::ToInt(row_b[2]);
|
|
||||||
pp.item_tint.Slot[slot].Blue = Strings::ToInt(row_b[3]);
|
|
||||||
pp.item_tint.Slot[slot].UseTint = Strings::ToInt(row_b[4]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetCharSelInventory(account_id, p_character_select_entry_struct->Name, &inventory_profile)) {
|
if (GetCharSelInventory(inventories, e, &inv)) {
|
||||||
const EQ::ItemData *item = nullptr;
|
const EQ::ItemData *item = nullptr;
|
||||||
const EQ::ItemInstance *inst = nullptr;
|
const EQ::ItemInstance *inst = nullptr;
|
||||||
int16 inventory_slot = 0;
|
int16 inventory_slot = 0;
|
||||||
|
|
||||||
for (uint32 matslot = EQ::textures::textureBegin; matslot < EQ::textures::materialCount; matslot++) {
|
for (uint32 matslot = EQ::textures::textureBegin; matslot < EQ::textures::materialCount; matslot++) {
|
||||||
inventory_slot = EQ::InventoryProfile::CalcSlotFromMaterial(matslot);
|
inventory_slot = EQ::InventoryProfile::CalcSlotFromMaterial(matslot);
|
||||||
if (inventory_slot == INVALID_INDEX) { continue; }
|
if (inventory_slot == INVALID_INDEX) { continue; }
|
||||||
inst = inventory_profile.GetItem(inventory_slot);
|
inst = inv.GetItem(inventory_slot);
|
||||||
if (inst == nullptr)
|
if (inst == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
item = inst->GetItem();
|
item = inst->GetItem();
|
||||||
if (item == nullptr)
|
if (item == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (matslot > 6) {
|
if (matslot > 6) {
|
||||||
uint32 item_id_file = 0;
|
uint32 item_id_file = 0;
|
||||||
// Weapon Models
|
// Weapon Models
|
||||||
if (inst->GetOrnamentationIDFile() != 0) {
|
if (inst->GetOrnamentationIDFile() != 0) {
|
||||||
item_id_file = inst->GetOrnamentationIDFile();
|
item_id_file = inst->GetOrnamentationIDFile();
|
||||||
p_character_select_entry_struct->Equip[matslot].Material = item_id_file;
|
cse->Equip[matslot].Material = item_id_file;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
if (strlen(item->IDFile) > 2) {
|
if (strlen(item->IDFile) > 2) {
|
||||||
item_id_file = Strings::ToInt(&item->IDFile[2]);
|
item_id_file = Strings::ToInt(&item->IDFile[2]);
|
||||||
p_character_select_entry_struct->Equip[matslot].Material = item_id_file;
|
cse->Equip[matslot].Material = item_id_file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matslot == EQ::textures::weaponPrimary) {
|
if (matslot == EQ::textures::weaponPrimary) {
|
||||||
p_character_select_entry_struct->PrimaryIDFile = item_id_file;
|
cse->PrimaryIDFile = item_id_file;
|
||||||
} else {
|
|
||||||
p_character_select_entry_struct->SecondaryIDFile = item_id_file;
|
|
||||||
}
|
}
|
||||||
} else {
|
else {
|
||||||
|
cse->SecondaryIDFile = item_id_file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
// Armor Materials/Models
|
// Armor Materials/Models
|
||||||
uint32 color = (
|
uint32 color = (
|
||||||
pp.item_tint.Slot[matslot].UseTint ?
|
pp.item_tint.Slot[matslot].UseTint ?
|
||||||
pp.item_tint.Slot[matslot].Color :
|
pp.item_tint.Slot[matslot].Color :
|
||||||
inst->GetColor()
|
inst->GetColor()
|
||||||
);
|
);
|
||||||
p_character_select_entry_struct->Equip[matslot].Material = item->Material;
|
cse->Equip[matslot].Material = item->Material;
|
||||||
p_character_select_entry_struct->Equip[matslot].EliteModel = item->EliteMaterial;
|
cse->Equip[matslot].EliteModel = item->EliteMaterial;
|
||||||
p_character_select_entry_struct->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot);
|
cse->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot);
|
||||||
p_character_select_entry_struct->Equip[matslot].Color = color;
|
cse->Equip[matslot].Color = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
printf("Error loading inventory for %s\n", p_character_select_entry_struct->Name);
|
else {
|
||||||
|
printf("Error loading inventory for %s\n", cse->Name);
|
||||||
}
|
}
|
||||||
buff_ptr += sizeof(CharacterSelectEntry_Struct);
|
buff_ptr += sizeof(CharacterSelectEntry_Struct);
|
||||||
}
|
}
|
||||||
@@ -855,34 +826,21 @@ bool WorldDatabase::LoadCharacterCreateCombos()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is a slightly modified version of SharedDatabase::GetInventory(...) for character select use-only
|
bool WorldDatabase::GetCharSelInventory(
|
||||||
bool WorldDatabase::GetCharSelInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv)
|
const std::vector<InventoryRepository::Inventory> &inventories,
|
||||||
|
const CharacterDataRepository::CharacterData &character,
|
||||||
|
EQ::InventoryProfile *inv
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (!account_id || !name || !inv) {
|
if (inventories.empty() || !character.id || !inv) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32 character_id = GetCharacterID(name);
|
for (const auto& e : inventories) {
|
||||||
|
if (e.character_id != character.id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!character_id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& l = InventoryRepository::GetWhere(
|
|
||||||
*this,
|
|
||||||
fmt::format(
|
|
||||||
"`character_id` = {} AND `slot_id` BETWEEN {} AND {}",
|
|
||||||
character_id,
|
|
||||||
EQ::invslot::slotHead,
|
|
||||||
EQ::invslot::slotFeet
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (l.empty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& e : l) {
|
|
||||||
switch (e.slot_id) {
|
switch (e.slot_id) {
|
||||||
case EQ::invslot::slotFace:
|
case EQ::invslot::slotFace:
|
||||||
case EQ::invslot::slotEar2:
|
case EQ::invslot::slotEar2:
|
||||||
|
|||||||
+7
-1
@@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include "../common/shareddb.h"
|
#include "../common/shareddb.h"
|
||||||
#include "../common/eq_packet.h"
|
#include "../common/eq_packet.h"
|
||||||
|
#include "../common/repositories/inventory_repository.h"
|
||||||
|
#include "../common/repositories/character_data_repository.h"
|
||||||
|
|
||||||
struct PlayerProfile_Struct;
|
struct PlayerProfile_Struct;
|
||||||
struct CharCreate_Struct;
|
struct CharCreate_Struct;
|
||||||
@@ -43,7 +45,11 @@ private:
|
|||||||
void SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc);
|
void SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc);
|
||||||
void SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc);
|
void SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc);
|
||||||
|
|
||||||
bool GetCharSelInventory(uint32 account_id, char* name, EQ::InventoryProfile* inv);
|
bool GetCharSelInventory(
|
||||||
|
const std::vector<InventoryRepository::Inventory> &inventories,
|
||||||
|
const CharacterDataRepository::CharacterData &character,
|
||||||
|
EQ::InventoryProfile *inv
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern WorldDatabase database;
|
extern WorldDatabase database;
|
||||||
|
|||||||
+71
-15
@@ -36,11 +36,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "shared_task_manager.h"
|
#include "shared_task_manager.h"
|
||||||
#include "dynamic_zone_manager.h"
|
#include "dynamic_zone_manager.h"
|
||||||
#include "ucs.h"
|
#include "ucs.h"
|
||||||
|
#include "clientlist.h"
|
||||||
|
|
||||||
extern uint32 numzones;
|
extern uint32 numzones;
|
||||||
extern EQ::Random emu_random;
|
extern EQ::Random emu_random;
|
||||||
extern WebInterfaceList web_interface;
|
extern WebInterfaceList web_interface;
|
||||||
extern SharedTaskManager shared_task_manager;
|
extern SharedTaskManager shared_task_manager;
|
||||||
|
extern ClientList client_list;
|
||||||
volatile bool UCSServerAvailable_ = false;
|
volatile bool UCSServerAvailable_ = false;
|
||||||
void CatchSignal(int sig_num);
|
void CatchSignal(int sig_num);
|
||||||
|
|
||||||
@@ -515,19 +517,27 @@ void ZSList::SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_mins
|
|||||||
SendEmoteMessageRaw(to, to_guilddbid, to_minstatus, type, buffer);
|
SendEmoteMessageRaw(to, to_guilddbid, to_minstatus, type, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZSList::SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message) {
|
void ZSList::SendEmoteMessageRaw(
|
||||||
if (!message)
|
const char *to,
|
||||||
|
uint32 to_guilddbid,
|
||||||
|
int16 to_minstatus,
|
||||||
|
uint32 type,
|
||||||
|
const char *message
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!message) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
auto pack = new ServerPacket;
|
auto pack = new ServerPacket;
|
||||||
|
|
||||||
pack->opcode = ServerOP_EmoteMessage;
|
pack->opcode = ServerOP_EmoteMessage;
|
||||||
pack->size = sizeof(ServerEmoteMessage_Struct) + strlen(message) + 1;
|
pack->size = sizeof(ServerEmoteMessage_Struct) + strlen(message) + 1;
|
||||||
pack->pBuffer = new uchar[pack->size];
|
pack->pBuffer = new uchar[pack->size];
|
||||||
memset(pack->pBuffer, 0, pack->size);
|
memset(pack->pBuffer, 0, pack->size);
|
||||||
ServerEmoteMessage_Struct* sem = (ServerEmoteMessage_Struct*)pack->pBuffer;
|
ServerEmoteMessage_Struct *sem = (ServerEmoteMessage_Struct *) pack->pBuffer;
|
||||||
|
|
||||||
if (to) {
|
if (to) {
|
||||||
strcpy((char *)sem->to, to);
|
strcpy((char *) sem->to, to);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sem->to[0] = 0;
|
sem->to[0] = 0;
|
||||||
@@ -535,22 +545,37 @@ void ZSList::SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_m
|
|||||||
|
|
||||||
sem->guilddbid = to_guilddbid;
|
sem->guilddbid = to_guilddbid;
|
||||||
sem->minstatus = to_minstatus;
|
sem->minstatus = to_minstatus;
|
||||||
sem->type = type;
|
sem->type = type;
|
||||||
strcpy(&sem->message[0], message);
|
strcpy(&sem->message[0], message);
|
||||||
char tempto[64] = { 0 };
|
char tempto[64] = {0};
|
||||||
if (to)
|
if (to) {
|
||||||
strn0cpy(tempto, to, 64);
|
strn0cpy(tempto, to, 64);
|
||||||
|
}
|
||||||
|
|
||||||
if (tempto[0] == 0) {
|
if (tempto[0] == 0) {
|
||||||
SendPacket(pack);
|
if (to_guilddbid > 0) {
|
||||||
|
SendPacketToZonesWithGuild(to_guilddbid, pack);
|
||||||
|
}
|
||||||
|
else if (to_minstatus > 0) {
|
||||||
|
SendPacketToZonesWithGMs(pack);
|
||||||
|
} else {
|
||||||
|
SendPacket(pack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ZoneServer* zs = FindByName(to);
|
ZoneServer *zs = FindByName(to);
|
||||||
|
if (zs) {
|
||||||
if (zs != 0)
|
|
||||||
zs->SendPacket(pack);
|
zs->SendPacket(pack);
|
||||||
else
|
}
|
||||||
|
else if (to_guilddbid > 0) {
|
||||||
|
SendPacketToZonesWithGuild(to_guilddbid, pack);
|
||||||
|
}
|
||||||
|
else if (to_minstatus > 0) {
|
||||||
|
SendPacketToZonesWithGMs(pack);
|
||||||
|
}
|
||||||
|
else {
|
||||||
SendPacket(pack);
|
SendPacket(pack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
delete pack;
|
delete pack;
|
||||||
}
|
}
|
||||||
@@ -871,6 +896,34 @@ bool ZSList::SendPacketToBootedZones(ServerPacket* pack)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ZSList::SendPacketToZonesWithGuild(uint32 guild_id, ServerPacket* pack)
|
||||||
|
{
|
||||||
|
auto servers = client_list.GetGuildZoneServers(guild_id);
|
||||||
|
for (auto const& z : zone_server_list) {
|
||||||
|
for (auto const& server_id : servers) {
|
||||||
|
if (z->GetID() == server_id && z->GetZoneID() > 0) {
|
||||||
|
z->SendPacket(pack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZSList::SendPacketToZonesWithGMs(ServerPacket* pack)
|
||||||
|
{
|
||||||
|
auto servers = client_list.GetZoneServersWithGMs();
|
||||||
|
for (auto const &z: zone_server_list) {
|
||||||
|
for (auto const &server_id: servers) {
|
||||||
|
if (z->GetID() == server_id && z->GetZoneID() > 0) {
|
||||||
|
z->SendPacket(pack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void ZSList::SendServerReload(ServerReload::Type type, uchar *packet)
|
void ZSList::SendServerReload(ServerReload::Type type, uchar *packet)
|
||||||
{
|
{
|
||||||
static auto pack = ServerPacket(ServerOP_ServerReloadRequest, sizeof(ServerReload::Request));
|
static auto pack = ServerPacket(ServerOP_ServerReloadRequest, sizeof(ServerReload::Request));
|
||||||
@@ -894,7 +947,10 @@ void ZSList::SendServerReload(ServerReload::Type type, uchar *packet)
|
|||||||
ServerReload::Type::Commands,
|
ServerReload::Type::Commands,
|
||||||
ServerReload::Type::PerlExportSettings,
|
ServerReload::Type::PerlExportSettings,
|
||||||
ServerReload::Type::DataBucketsCache,
|
ServerReload::Type::DataBucketsCache,
|
||||||
ServerReload::Type::WorldRepop
|
ServerReload::Type::Quests,
|
||||||
|
ServerReload::Type::QuestsTimerReset,
|
||||||
|
ServerReload::Type::WorldRepop,
|
||||||
|
ServerReload::Type::WorldWithRespawn
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set requires_zone_booted flag before executing reload logic
|
// Set requires_zone_booted flag before executing reload logic
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ public:
|
|||||||
bool SendPacket(ServerPacket *pack);
|
bool SendPacket(ServerPacket *pack);
|
||||||
bool SendPacket(uint32 zoneid, ServerPacket *pack);
|
bool SendPacket(uint32 zoneid, ServerPacket *pack);
|
||||||
bool SendPacket(uint32 zoneid, uint16 instanceid, ServerPacket *pack);
|
bool SendPacket(uint32 zoneid, uint16 instanceid, ServerPacket *pack);
|
||||||
|
bool SendPacketToZonesWithGuild(uint32 guild_id, ServerPacket *pack);
|
||||||
|
bool SendPacketToZonesWithGMs(ServerPacket *pack);
|
||||||
bool SendPacketToBootedZones(ServerPacket* pack);
|
bool SendPacketToBootedZones(ServerPacket* pack);
|
||||||
bool SetLockedZone(uint16 iZoneID, bool iLock);
|
bool SetLockedZone(uint16 iZoneID, bool iLock);
|
||||||
|
|
||||||
@@ -70,6 +72,7 @@ public:
|
|||||||
ZoneServer* FindByZoneID(uint32 ZoneID);
|
ZoneServer* FindByZoneID(uint32 ZoneID);
|
||||||
|
|
||||||
const std::list<std::unique_ptr<ZoneServer>> &getZoneServerList() const;
|
const std::list<std::unique_ptr<ZoneServer>> &getZoneServerList() const;
|
||||||
|
inline uint32_t GetServerListCount() { return zone_server_list.size(); }
|
||||||
void SendServerReload(ServerReload::Type type, uchar *packet = nullptr);
|
void SendServerReload(ServerReload::Type type, uchar *packet = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
+17
-8
@@ -571,7 +571,13 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
zoneserver_list.SendPacket(pack);
|
if (scm->guilddbid > 0) {
|
||||||
|
zoneserver_list.SendPacketToZonesWithGuild(scm->guilddbid, pack);
|
||||||
|
} else if (scm->chan_num == ChatChannel_GMSAY) {
|
||||||
|
zoneserver_list.SendPacketToZonesWithGMs(pack);
|
||||||
|
} else {
|
||||||
|
zoneserver_list.SendPacket(pack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -729,13 +735,15 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
zs = zoneserver_list.FindByID(s->zone_server_id);
|
zs = zoneserver_list.FindByID(s->zone_server_id);
|
||||||
} else if (s->zone_id) {
|
} else if (s->zone_id) {
|
||||||
zs = zoneserver_list.FindByName(ZoneName(s->zone_id));
|
zs = zoneserver_list.FindByName(ZoneName(s->zone_id));
|
||||||
|
} else if (s->instance_id) {
|
||||||
|
zs = zoneserver_list.FindByInstanceID(s->instance_id);
|
||||||
} else {
|
} else {
|
||||||
zoneserver_list.SendEmoteMessage(
|
zoneserver_list.SendEmoteMessage(
|
||||||
s->admin_name,
|
s->admin_name,
|
||||||
0,
|
0,
|
||||||
AccountStatus::Player,
|
AccountStatus::Player,
|
||||||
Chat::White,
|
Chat::White,
|
||||||
"Error: SOP_ZoneShutdown: neither ID nor name specified"
|
"Error: SOP_ZoneShutdown: Zone ID, Instance ID, nor Zone Short Name specified"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1480,6 +1488,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
case ServerOP_DzSwapMembers:
|
case ServerOP_DzSwapMembers:
|
||||||
case ServerOP_DzRemoveAllMembers:
|
case ServerOP_DzRemoveAllMembers:
|
||||||
case ServerOP_DzGetMemberStatuses:
|
case ServerOP_DzGetMemberStatuses:
|
||||||
|
case ServerOP_DzGetBulkMemberStatuses:
|
||||||
case ServerOP_DzSetSecondsRemaining:
|
case ServerOP_DzSetSecondsRemaining:
|
||||||
case ServerOP_DzSetCompass:
|
case ServerOP_DzSetCompass:
|
||||||
case ServerOP_DzSetSafeReturn:
|
case ServerOP_DzSetSafeReturn:
|
||||||
@@ -1509,7 +1518,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
|
|
||||||
guild->tribute.timer.Disable();
|
guild->tribute.timer.Disable();
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(data->guild_id, pack);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1552,7 +1561,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
guild_mgr.UpdateDbGuildFavor(data->guild_id, data->favor);
|
guild_mgr.UpdateDbGuildFavor(data->guild_id, data->favor);
|
||||||
guild_mgr.UpdateDbTributeTimeRemaining(data->guild_id, data->time_remaining);
|
guild_mgr.UpdateDbTributeTimeRemaining(data->guild_id, data->time_remaining);
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(data->guild_id, pack);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1586,7 +1595,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
data->time_remaining = in->time_remaining;
|
data->time_remaining = in->time_remaining;
|
||||||
strn0cpy(data->player_name, in->player_name, sizeof(data->player_name));
|
strn0cpy(data->player_name, in->player_name, sizeof(data->player_name));
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(out);
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, out);
|
||||||
safe_delete(out);
|
safe_delete(out);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1609,7 +1618,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
out->tribute_id_2_tier = guild->tribute.id_2_tier;
|
out->tribute_id_2_tier = guild->tribute.id_2_tier;
|
||||||
out->time_remaining = guild_mgr.GetGuildTributeTimeRemaining(in->guild_id);
|
out->time_remaining = guild_mgr.GetGuildTributeTimeRemaining(in->guild_id);
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(sp);
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, sp);
|
||||||
safe_delete(sp);
|
safe_delete(sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1629,7 +1638,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
out->tribute_timer = guild_mgr.GetGuildTributeTimeRemaining(in->guild_id);
|
out->tribute_timer = guild_mgr.GetGuildTributeTimeRemaining(in->guild_id);
|
||||||
out->trophy_timer = 0;
|
out->trophy_timer = 0;
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(sp);
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, sp);
|
||||||
safe_delete(sp);
|
safe_delete(sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1653,7 +1662,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
out->member_time = in->member_time;
|
out->member_time = in->member_time;
|
||||||
strn0cpy(out->player_name, in->player_name, sizeof(out->player_name));
|
strn0cpy(out->player_name, in->player_name, sizeof(out->player_name));
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(sp);
|
zoneserver_list.SendPacketToZonesWithGuild(out->guild_id, sp);
|
||||||
safe_delete(sp)
|
safe_delete(sp)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ public:
|
|||||||
|
|
||||||
inline const char* GetZoneName() const { return zone_name; }
|
inline const char* GetZoneName() const { return zone_name; }
|
||||||
inline const char* GetZoneLongName() const { return long_name; }
|
inline const char* GetZoneLongName() const { return long_name; }
|
||||||
|
inline std::string GetCurrentVersion() const { return CURRENT_VERSION; }
|
||||||
|
inline std::string GetCompileDate() const { return COMPILE_DATE; }
|
||||||
const char* GetCompileTime() const{ return compiled; }
|
const char* GetCompileTime() const{ return compiled; }
|
||||||
void SetCompile(char* in_compile){ strcpy(compiled,in_compile); }
|
void SetCompile(char* in_compile){ strcpy(compiled,in_compile); }
|
||||||
inline uint32 GetZoneID() const { return zone_server_zone_id; }
|
inline uint32 GetZoneID() const { return zone_server_zone_id; }
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ SET(zone_sources
|
|||||||
zone_event_scheduler.cpp
|
zone_event_scheduler.cpp
|
||||||
zone_npc_factions.cpp
|
zone_npc_factions.cpp
|
||||||
zone_reload.cpp
|
zone_reload.cpp
|
||||||
|
zone_save_state.cpp
|
||||||
zoning.cpp
|
zoning.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -292,6 +293,7 @@ SET(zone_headers
|
|||||||
zonedump.h
|
zonedump.h
|
||||||
zone_cli.h
|
zone_cli.h
|
||||||
zone_reload.h
|
zone_reload.h
|
||||||
|
zone_save_state.h
|
||||||
zone_cli.cpp)
|
zone_cli.cpp)
|
||||||
|
|
||||||
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})
|
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})
|
||||||
|
|||||||
+33
-18
@@ -2507,6 +2507,12 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_resumed_from_zone_suspend && !IsQueuedForCorpse()) {
|
||||||
|
LogInfo("NPC [{}] is resumed from zone suspend, cannot kill until zone resume is complete.", GetCleanName());
|
||||||
|
SetHP(0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (IsMultiQuestEnabled()) {
|
if (IsMultiQuestEnabled()) {
|
||||||
for (auto &i: m_hand_in.items) {
|
for (auto &i: m_hand_in.items) {
|
||||||
if (i.is_multiquest_item && i.item->GetItem()->NoDrop != 0) {
|
if (i.is_multiquest_item && i.item->GetItem()->NoDrop != 0) {
|
||||||
@@ -2789,32 +2795,35 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
|||||||
const uint16 entity_id = GetID();
|
const uint16 entity_id = GetID();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!HasOwner() &&
|
|
||||||
!IsMerc() &&
|
|
||||||
!GetSwarmInfo() &&
|
|
||||||
(!is_merchant || allow_merchant_corpse) &&
|
|
||||||
(
|
(
|
||||||
|
!HasOwner() &&
|
||||||
|
!IsMerc() &&
|
||||||
|
!GetSwarmInfo() &&
|
||||||
|
(!is_merchant || allow_merchant_corpse) &&
|
||||||
(
|
(
|
||||||
killer &&
|
|
||||||
(
|
(
|
||||||
killer->IsClient() ||
|
killer &&
|
||||||
(
|
(
|
||||||
killer->HasOwner() &&
|
killer->IsClient() ||
|
||||||
killer->GetUltimateOwner()->IsClient()
|
(
|
||||||
) ||
|
killer->HasOwner() &&
|
||||||
(
|
killer->GetUltimateOwner()->IsClient()
|
||||||
killer->IsNPC() &&
|
) ||
|
||||||
killer->CastToNPC()->GetSwarmInfo() &&
|
(
|
||||||
killer->CastToNPC()->GetSwarmInfo()->GetOwner() &&
|
killer->IsNPC() &&
|
||||||
killer->CastToNPC()->GetSwarmInfo()->GetOwner()->IsClient()
|
killer->CastToNPC()->GetSwarmInfo() &&
|
||||||
|
killer->CastToNPC()->GetSwarmInfo()->GetOwner() &&
|
||||||
|
killer->CastToNPC()->GetSwarmInfo()->GetOwner()->IsClient()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
) ||
|
||||||
|
(
|
||||||
|
killer_mob && is_ldon_treasure
|
||||||
)
|
)
|
||||||
) ||
|
|
||||||
(
|
|
||||||
killer_mob && is_ldon_treasure
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
) {
|
|| IsQueuedForCorpse()
|
||||||
|
) {
|
||||||
if (killer) {
|
if (killer) {
|
||||||
if (killer->GetOwner() != 0 && killer->GetOwner()->IsClient()) {
|
if (killer->GetOwner() != 0 && killer->GetOwner()->IsClient()) {
|
||||||
killer = killer->GetOwner();
|
killer = killer->GetOwner();
|
||||||
@@ -3038,9 +3047,15 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
|||||||
if (!other)
|
if (!other)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (other->IsDestroying())
|
||||||
|
return;
|
||||||
|
|
||||||
if (other == this)
|
if (other == this)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (other->IsClient() && (other->CastToClient()->IsZoning() || other->CastToClient()->Connected() == false))
|
||||||
|
return;
|
||||||
|
|
||||||
if (other->IsTrap())
|
if (other->IsTrap())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -690,12 +690,16 @@ void Aura::ProcessSpawns()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!e.second->IsOfClientBot()) {
|
if (!e.second->IsClient()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto c = e.second->CastToClient();
|
auto c = e.second->CastToClient();
|
||||||
|
|
||||||
|
if (!c) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
bool spawned = spawned_for.find(c->GetID()) != spawned_for.end();
|
bool spawned = spawned_for.find(c->GetID()) != spawned_for.end();
|
||||||
if (ShouldISpawnFor(c)) {
|
if (ShouldISpawnFor(c)) {
|
||||||
if (!spawned) {
|
if (!spawned) {
|
||||||
|
|||||||
+199
-412
@@ -281,443 +281,233 @@ void Mob::AddItemBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_a
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_ammo_item) {
|
if (is_ammo_item) {
|
||||||
const auto recommended_level = is_augment ? recommended_level_override : inst->GetItemRecommendedLevel(true);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (IsNPC() || current_level >= recommended_level) {
|
const auto recommended_level = is_augment ? recommended_level_override : inst->GetItemRecommendedLevel(true);
|
||||||
b->HP += item->HP;
|
const bool meets_recommended = (IsNPC() && !RuleB(Items, NPCUseRecommendedLevels)) || current_level >= recommended_level;
|
||||||
b->Mana += item->Mana;
|
|
||||||
b->Endurance += item->Endur;
|
|
||||||
|
|
||||||
b->AC += item->AC;
|
auto CalcItemBonus = [&](int statValue) -> int {
|
||||||
b->ATK += item->Attack;
|
return meets_recommended ? statValue : CalcRecommendedLevelBonus(current_level, recommended_level, statValue);
|
||||||
|
};
|
||||||
|
|
||||||
b->STR += (item->AStr + item->HeroicStr);
|
auto CalcCappedItemBonus = [&](int currentStat, int bonus, int cap) -> int {
|
||||||
b->STA += (item->ASta + item->HeroicSta);
|
int calc_stat = currentStat + CalcItemBonus(bonus);
|
||||||
b->DEX += (item->ADex + item->HeroicDex);
|
return IsOfClientBotMerc() ? std::min(cap, calc_stat) : calc_stat;
|
||||||
b->AGI += (item->AAgi + item->HeroicAgi);
|
};
|
||||||
b->INT += (item->AInt + item->HeroicInt);
|
|
||||||
b->WIS += (item->AWis + item->HeroicWis);
|
|
||||||
b->CHA += (item->ACha + item->HeroicCha);
|
|
||||||
|
|
||||||
b->MR += (item->MR + item->HeroicMR);
|
b->HP += CalcItemBonus(item->HP);
|
||||||
b->FR += (item->FR + item->HeroicFR);
|
b->Mana += CalcItemBonus(item->Mana);
|
||||||
b->CR += (item->CR + item->HeroicCR);
|
b->Endurance += CalcItemBonus(item->Endur);
|
||||||
b->PR += (item->PR + item->HeroicPR);
|
b->AC += CalcItemBonus(item->AC);
|
||||||
b->DR += (item->DR + item->HeroicDR);
|
|
||||||
b->Corrup += (item->SVCorruption + item->HeroicSVCorrup);
|
|
||||||
|
|
||||||
b->STRCapMod += item->HeroicStr;
|
b->STR += CalcItemBonus(item->AStr + item->HeroicStr);
|
||||||
b->STACapMod += item->HeroicSta;
|
b->STA += CalcItemBonus(item->ASta + item->HeroicSta);
|
||||||
b->DEXCapMod += item->HeroicDex;
|
b->DEX += CalcItemBonus(item->ADex + item->HeroicDex);
|
||||||
b->AGICapMod += item->HeroicAgi;
|
b->AGI += CalcItemBonus(item->AAgi + item->HeroicAgi);
|
||||||
b->INTCapMod += item->HeroicInt;
|
b->INT += CalcItemBonus(item->AInt + item->HeroicInt);
|
||||||
b->WISCapMod += item->HeroicWis;
|
b->WIS += CalcItemBonus(item->AWis + item->HeroicWis);
|
||||||
b->CHACapMod += item->HeroicCha;
|
b->CHA += CalcItemBonus(item->ACha + item->HeroicCha);
|
||||||
|
|
||||||
b->MRCapMod += item->HeroicMR;
|
b->HeroicSTR += CalcItemBonus(item->HeroicStr);
|
||||||
b->CRCapMod += item->HeroicFR;
|
b->HeroicSTA += CalcItemBonus(item->HeroicSta);
|
||||||
b->FRCapMod += item->HeroicCR;
|
b->HeroicDEX += CalcItemBonus(item->HeroicDex);
|
||||||
b->PRCapMod += item->HeroicPR;
|
b->HeroicAGI += CalcItemBonus(item->HeroicAgi);
|
||||||
b->DRCapMod += item->HeroicDR;
|
b->HeroicINT += CalcItemBonus(item->HeroicInt);
|
||||||
b->CorrupCapMod += item->HeroicSVCorrup;
|
b->HeroicWIS += CalcItemBonus(item->HeroicWis);
|
||||||
|
b->HeroicCHA += CalcItemBonus(item->HeroicCha);
|
||||||
|
|
||||||
b->HeroicSTR += item->HeroicStr;
|
b->STRCapMod += item->HeroicStr;
|
||||||
b->HeroicSTA += item->HeroicSta;
|
b->STACapMod += item->HeroicSta;
|
||||||
b->HeroicDEX += item->HeroicDex;
|
b->DEXCapMod += item->HeroicDex;
|
||||||
b->HeroicAGI += item->HeroicAgi;
|
b->AGICapMod += item->HeroicAgi;
|
||||||
b->HeroicINT += item->HeroicInt;
|
b->INTCapMod += item->HeroicInt;
|
||||||
b->HeroicWIS += item->HeroicWis;
|
b->WISCapMod += item->HeroicWis;
|
||||||
b->HeroicCHA += item->HeroicCha;
|
b->CHACapMod += item->HeroicCha;
|
||||||
|
|
||||||
b->HeroicMR += item->HeroicMR;
|
b->MR += CalcItemBonus(item->MR + item->HeroicMR);
|
||||||
b->HeroicFR += item->HeroicFR;
|
b->FR += CalcItemBonus(item->FR + item->HeroicFR);
|
||||||
b->HeroicCR += item->HeroicCR;
|
b->CR += CalcItemBonus(item->CR + item->HeroicCR);
|
||||||
b->HeroicPR += item->HeroicPR;
|
b->PR += CalcItemBonus(item->PR + item->HeroicPR);
|
||||||
b->HeroicDR += item->HeroicDR;
|
b->DR += CalcItemBonus(item->DR + item->HeroicDR);
|
||||||
b->HeroicCorrup += item->HeroicSVCorrup;
|
b->Corrup += CalcItemBonus(item->SVCorruption + item->HeroicSVCorrup);
|
||||||
|
|
||||||
|
b->HeroicMR += CalcItemBonus(item->HeroicMR);
|
||||||
|
b->HeroicFR += CalcItemBonus(item->HeroicFR);
|
||||||
|
b->HeroicCR += CalcItemBonus(item->HeroicCR);
|
||||||
|
b->HeroicPR += CalcItemBonus(item->HeroicPR);
|
||||||
|
b->HeroicDR += CalcItemBonus(item->HeroicDR);
|
||||||
|
b->HeroicCorrup += CalcItemBonus(item->HeroicSVCorrup);
|
||||||
|
|
||||||
|
b->MRCapMod += item->HeroicMR;
|
||||||
|
b->FRCapMod += item->HeroicFR;
|
||||||
|
b->CRCapMod += item->HeroicCR;
|
||||||
|
b->PRCapMod += item->HeroicPR;
|
||||||
|
b->DRCapMod += item->HeroicDR;
|
||||||
|
b->CorrupCapMod += item->HeroicSVCorrup;
|
||||||
|
|
||||||
|
b->HPRegen += CalcItemBonus(item->Regen);
|
||||||
|
b->ManaRegen += CalcItemBonus(item->ManaRegen);
|
||||||
|
b->EnduranceRegen += CalcItemBonus(item->EnduranceRegen);
|
||||||
|
|
||||||
|
// These have rule-configured caps.
|
||||||
|
b->ATK = CalcCappedItemBonus(b->ATK, item->Attack, RuleI(Character, ItemATKCap) + itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap);
|
||||||
|
b->DamageShield = CalcCappedItemBonus(b->DamageShield, item->DamageShield, RuleI(Character, ItemDamageShieldCap));
|
||||||
|
b->SpellShield = CalcCappedItemBonus(b->SpellShield, item->SpellShield, RuleI(Character, ItemSpellShieldingCap));
|
||||||
|
b->MeleeMitigation = CalcCappedItemBonus(b->MeleeMitigation, item->Shielding, RuleI(Character, ItemShieldingCap));
|
||||||
|
b->StunResist = CalcCappedItemBonus(b->StunResist, item->StunResist, RuleI(Character, ItemStunResistCap));
|
||||||
|
b->StrikeThrough = CalcCappedItemBonus(b->StrikeThrough, item->StrikeThrough, RuleI(Character, ItemStrikethroughCap));
|
||||||
|
b->AvoidMeleeChance = CalcCappedItemBonus(b->AvoidMeleeChance, item->Avoidance, RuleI(Character, ItemAvoidanceCap));
|
||||||
|
b->HitChance = CalcCappedItemBonus(b->HitChance, item->Accuracy, RuleI(Character, ItemAccuracyCap));
|
||||||
|
b->ProcChance = CalcCappedItemBonus(b->ProcChance, item->CombatEffects, RuleI(Character, ItemCombatEffectsCap));
|
||||||
|
b->DoTShielding = CalcCappedItemBonus(b->DoTShielding, item->DotShielding, RuleI(Character, ItemDoTShieldingCap));
|
||||||
|
b->HealAmt = CalcCappedItemBonus(b->HealAmt, item->HealAmt, RuleI(Character, ItemHealAmtCap));
|
||||||
|
b->SpellDmg = CalcCappedItemBonus(b->SpellDmg, item->SpellDmg, RuleI(Character, ItemSpellDmgCap));
|
||||||
|
b->Clairvoyance = CalcCappedItemBonus(b->Clairvoyance, item->Clairvoyance, RuleI(Character, ItemClairvoyanceCap));
|
||||||
|
b->DSMitigation = CalcCappedItemBonus(b->DSMitigation, item->DSMitigation, RuleI(Character, ItemDSMitigationCap));
|
||||||
|
|
||||||
|
if (b->haste < item->Haste) {
|
||||||
|
b->haste = item->Haste;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item->ExtraDmgAmt != 0 && item->ExtraDmgSkill <= EQ::skills::HIGHEST_SKILL) {
|
||||||
|
if (item->ExtraDmgSkill == ALL_SKILLS) {
|
||||||
|
for (const auto &skill_id: EQ::skills::GetExtraDamageSkills()) {
|
||||||
|
b->SkillDamageAmount[skill_id] = CalcCappedItemBonus(b->SkillDamageAmount[skill_id], item->ExtraDmgAmt, RuleI(Character, ItemExtraDmgCap));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
b->HP += CalcRecommendedLevelBonus(current_level, recommended_level, item->HP);
|
b->SkillDamageAmount[item->ExtraDmgSkill] = CalcCappedItemBonus(b->SkillDamageAmount[item->ExtraDmgSkill], item->ExtraDmgAmt, RuleI(Character, ItemExtraDmgCap));
|
||||||
b->Mana += CalcRecommendedLevelBonus(current_level, recommended_level, item->Mana);
|
|
||||||
b->Endurance += CalcRecommendedLevelBonus(current_level, recommended_level, item->Endur);
|
|
||||||
|
|
||||||
b->AC += CalcRecommendedLevelBonus(current_level, recommended_level, item->AC);
|
|
||||||
b->ATK += CalcRecommendedLevelBonus(current_level, recommended_level, item->Attack);
|
|
||||||
|
|
||||||
b->STR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->AStr + item->HeroicStr));
|
|
||||||
b->STA += CalcRecommendedLevelBonus(current_level, recommended_level, (item->ASta + item->HeroicSta));
|
|
||||||
b->DEX += CalcRecommendedLevelBonus(current_level, recommended_level, (item->ADex + item->HeroicDex));
|
|
||||||
b->AGI += CalcRecommendedLevelBonus(current_level, recommended_level, (item->AAgi + item->HeroicAgi));
|
|
||||||
b->INT += CalcRecommendedLevelBonus(current_level, recommended_level, (item->AInt + item->HeroicInt));
|
|
||||||
b->WIS += CalcRecommendedLevelBonus(current_level, recommended_level, (item->AWis + item->HeroicWis));
|
|
||||||
b->CHA += CalcRecommendedLevelBonus(current_level, recommended_level, (item->ACha + item->HeroicCha));
|
|
||||||
|
|
||||||
b->MR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->MR + item->HeroicMR));
|
|
||||||
b->FR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->FR + item->HeroicFR));
|
|
||||||
b->CR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->CR + item->HeroicCR));
|
|
||||||
b->PR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->PR + item->HeroicPR));
|
|
||||||
b->DR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->DR + item->HeroicDR));
|
|
||||||
b->Corrup += CalcRecommendedLevelBonus(current_level, recommended_level, (item->SVCorruption + item->HeroicSVCorrup));
|
|
||||||
|
|
||||||
b->STRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicStr);
|
|
||||||
b->STACapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicSta);
|
|
||||||
b->DEXCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicDex);
|
|
||||||
b->AGICapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicAgi);
|
|
||||||
b->INTCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicInt);
|
|
||||||
b->WISCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicWis);
|
|
||||||
b->CHACapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicCha);
|
|
||||||
|
|
||||||
b->MRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicMR);
|
|
||||||
b->CRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicFR);
|
|
||||||
b->FRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicCR);
|
|
||||||
b->PRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicPR);
|
|
||||||
b->DRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicDR);
|
|
||||||
b->CorrupCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicSVCorrup);
|
|
||||||
|
|
||||||
b->HeroicSTR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicStr);
|
|
||||||
b->HeroicSTA += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicSta);
|
|
||||||
b->HeroicDEX += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicDex);
|
|
||||||
b->HeroicAGI += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicAgi);
|
|
||||||
b->HeroicINT += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicInt);
|
|
||||||
b->HeroicWIS += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicWis);
|
|
||||||
b->HeroicCHA += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicCha);
|
|
||||||
|
|
||||||
b->HeroicMR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicMR);
|
|
||||||
b->HeroicFR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicFR);
|
|
||||||
b->HeroicCR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicCR);
|
|
||||||
b->HeroicPR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicPR);
|
|
||||||
b->HeroicDR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicDR);
|
|
||||||
b->HeroicCorrup += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicSVCorrup);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (b->haste < item->Haste) {
|
if (item->Worn.Effect > 0 && item->Worn.Type == EQ::item::ItemEffectWorn) {
|
||||||
b->haste = item->Haste;
|
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, b, 0, item->Worn.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item->Focus.Effect > 0 && item->Focus.Type == EQ::item::ItemEffectFocus) {
|
||||||
|
if (
|
||||||
|
IsOfClientBotMerc() ||
|
||||||
|
(IsNPC() && RuleB(Spells, NPC_UseFocusFromItems))
|
||||||
|
) {
|
||||||
|
ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, b, 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (item->Regen != 0) {
|
switch (item->BardType) {
|
||||||
b->HPRegen += item->Regen;
|
case EQ::item::ItemTypeAllInstrumentTypes: { // (e.g. Singing Short Sword)
|
||||||
|
if (item->BardValue > b->singingMod) {
|
||||||
|
b->singingMod = item->BardValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item->BardValue > b->brassMod) {
|
||||||
|
b->brassMod = item->BardValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item->BardValue > b->stringedMod) {
|
||||||
|
b->stringedMod = item->BardValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item->BardValue > b->percussionMod) {
|
||||||
|
b->percussionMod = item->BardValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item->BardValue > b->windMod) {
|
||||||
|
b->windMod = item->BardValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
case EQ::item::ItemTypeSinging: {
|
||||||
|
if (item->BardValue > b->singingMod) {
|
||||||
|
b->singingMod = item->BardValue;
|
||||||
|
}
|
||||||
|
|
||||||
if (item->ManaRegen != 0) {
|
break;
|
||||||
b->ManaRegen += item->ManaRegen;
|
|
||||||
}
|
}
|
||||||
|
case EQ::item::ItemTypeWindInstrument: {
|
||||||
|
if (item->BardValue > b->windMod) {
|
||||||
|
b->windMod = item->BardValue;
|
||||||
|
}
|
||||||
|
|
||||||
if (item->EnduranceRegen != 0) {
|
break;
|
||||||
b->EnduranceRegen += item->EnduranceRegen;
|
|
||||||
}
|
}
|
||||||
|
case EQ::item::ItemTypeStringedInstrument: {
|
||||||
|
if (item->BardValue > b->stringedMod) {
|
||||||
|
b->stringedMod = item->BardValue;
|
||||||
|
}
|
||||||
|
|
||||||
if (item->Attack != 0) {
|
break;
|
||||||
unsigned int cap = RuleI(Character, ItemATKCap);
|
}
|
||||||
cap += itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap;
|
case EQ::item::ItemTypeBrassInstrument: {
|
||||||
|
if (item->BardValue > b->brassMod) {
|
||||||
|
b->brassMod = item->BardValue;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
break;
|
||||||
IsOfClientBotMerc() &&
|
}
|
||||||
(b->ATK + item->Attack) > cap
|
case EQ::item::ItemTypePercussionInstrument: {
|
||||||
|
if (item->BardValue > b->percussionMod) {
|
||||||
|
b->percussionMod = item->BardValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item->SkillModValue != 0 && item->SkillModType <= EQ::skills::HIGHEST_SKILL) {
|
||||||
|
if (
|
||||||
|
(item->SkillModValue > 0 && b->skillmod[item->SkillModType] < item->SkillModValue) ||
|
||||||
|
(item->SkillModValue < 0 && b->skillmod[item->SkillModType] > item->SkillModValue)
|
||||||
) {
|
) {
|
||||||
b->ATK = RuleI(Character, ItemATKCap);
|
b->skillmod[item->SkillModType] = item->SkillModValue;
|
||||||
} else {
|
|
||||||
b->ATK += item->Attack;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (item->DamageShield != 0) {
|
if (item->FactionMod1) {
|
||||||
if (
|
if (item->FactionAmt1 > 0 && item->FactionAmt1 > GetItemFactionBonus(item->FactionMod1)) {
|
||||||
IsOfClientBotMerc() &&
|
AddItemFactionBonus(item->FactionMod1, item->FactionAmt1);
|
||||||
(b->DamageShield + item->DamageShield) > RuleI(Character, ItemDamageShieldCap)
|
} else if (item->FactionAmt1 < 0 && item->FactionAmt1 < GetItemFactionBonus(item->FactionMod1)) {
|
||||||
) {
|
AddItemFactionBonus(item->FactionMod1, item->FactionAmt1);
|
||||||
b->DamageShield = RuleI(Character, ItemDamageShieldCap);
|
|
||||||
} else {
|
|
||||||
b->DamageShield += item->DamageShield;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (item->SpellShield != 0) {
|
if (item->FactionMod2) {
|
||||||
if (
|
if (item->FactionAmt2 > 0 && item->FactionAmt2 > GetItemFactionBonus(item->FactionMod2)) {
|
||||||
IsOfClientBotMerc() &&
|
AddItemFactionBonus(item->FactionMod2, item->FactionAmt2);
|
||||||
(b->SpellShield + item->SpellShield) > RuleI(Character, ItemSpellShieldingCap)
|
} else if (item->FactionAmt2 < 0 && item->FactionAmt2 < GetItemFactionBonus(item->FactionMod2)) {
|
||||||
) {
|
AddItemFactionBonus(item->FactionMod2, item->FactionAmt2);
|
||||||
b->SpellShield = RuleI(Character, ItemSpellShieldingCap);
|
|
||||||
} else {
|
|
||||||
b->SpellShield += item->SpellShield;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (item->Shielding != 0) {
|
if (item->FactionMod3) {
|
||||||
if (
|
if (item->FactionAmt3 > 0 && item->FactionAmt3 > GetItemFactionBonus(item->FactionMod3)) {
|
||||||
IsOfClientBotMerc() &&
|
AddItemFactionBonus(item->FactionMod3, item->FactionAmt3);
|
||||||
(b->MeleeMitigation + item->Shielding) > RuleI(Character, ItemShieldingCap)
|
} else if (item->FactionAmt3 < 0 && item->FactionAmt3 < GetItemFactionBonus(item->FactionMod3)) {
|
||||||
) {
|
AddItemFactionBonus(item->FactionMod3, item->FactionAmt3);
|
||||||
b->MeleeMitigation = RuleI(Character, ItemShieldingCap);
|
|
||||||
} else {
|
|
||||||
b->MeleeMitigation += item->Shielding;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (item->StunResist != 0) {
|
if (item->FactionMod4) {
|
||||||
if (
|
if (item->FactionAmt4 > 0 && item->FactionAmt4 > GetItemFactionBonus(item->FactionMod4)) {
|
||||||
IsOfClientBotMerc() &&
|
AddItemFactionBonus(item->FactionMod4, item->FactionAmt4);
|
||||||
(b->StunResist + item->StunResist) > RuleI(Character, ItemStunResistCap)
|
} else if (item->FactionAmt4 < 0 && item->FactionAmt4 < GetItemFactionBonus(item->FactionMod4)) {
|
||||||
) {
|
AddItemFactionBonus(item->FactionMod4, item->FactionAmt4);
|
||||||
b->StunResist = RuleI(Character, ItemStunResistCap);
|
|
||||||
} else {
|
|
||||||
b->StunResist += item->StunResist;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (item->StrikeThrough != 0) {
|
if (!is_augment) {
|
||||||
if (
|
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
||||||
IsOfClientBotMerc() &&
|
const auto* augment = inst->GetAugment(i);
|
||||||
(b->StrikeThrough + item->StrikeThrough) > RuleI(Character, ItemStrikethroughCap)
|
if (!augment) {
|
||||||
) {
|
continue;
|
||||||
b->StrikeThrough = RuleI(Character, ItemStrikethroughCap);
|
|
||||||
} else {
|
|
||||||
b->StrikeThrough += item->StrikeThrough;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (item->Avoidance != 0) {
|
AddItemBonuses(augment, b, true, false, recommended_level);
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->AvoidMeleeChance + item->Avoidance) > RuleI(Character, ItemAvoidanceCap)
|
|
||||||
) {
|
|
||||||
b->AvoidMeleeChance = RuleI(Character, ItemAvoidanceCap);
|
|
||||||
} else {
|
|
||||||
b->AvoidMeleeChance += item->Avoidance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->Accuracy != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->HitChance + item->Accuracy) > RuleI(Character, ItemAccuracyCap)
|
|
||||||
) {
|
|
||||||
b->HitChance = RuleI(Character, ItemAccuracyCap);
|
|
||||||
} else {
|
|
||||||
b->HitChance += item->Accuracy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->CombatEffects != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->ProcChance + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap)
|
|
||||||
) {
|
|
||||||
b->ProcChance = RuleI(Character, ItemCombatEffectsCap);
|
|
||||||
} else {
|
|
||||||
b->ProcChance += item->CombatEffects;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->DotShielding != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->DoTShielding + item->DotShielding) > RuleI(Character, ItemDoTShieldingCap)
|
|
||||||
) {
|
|
||||||
b->DoTShielding = RuleI(Character, ItemDoTShieldingCap);
|
|
||||||
} else {
|
|
||||||
b->DoTShielding += item->DotShielding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->HealAmt != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->HealAmt + item->HealAmt) > RuleI(Character, ItemHealAmtCap)
|
|
||||||
) {
|
|
||||||
b->HealAmt = RuleI(Character, ItemHealAmtCap);
|
|
||||||
} else {
|
|
||||||
b->HealAmt += item->HealAmt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->SpellDmg != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->SpellDmg + item->SpellDmg) > RuleI(Character, ItemSpellDmgCap)
|
|
||||||
) {
|
|
||||||
b->SpellDmg = RuleI(Character, ItemSpellDmgCap);
|
|
||||||
} else {
|
|
||||||
b->SpellDmg += item->SpellDmg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->Clairvoyance != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->Clairvoyance + item->Clairvoyance) > RuleI(Character, ItemClairvoyanceCap)
|
|
||||||
) {
|
|
||||||
b->Clairvoyance = RuleI(Character, ItemClairvoyanceCap);
|
|
||||||
} else {
|
|
||||||
b->Clairvoyance += item->Clairvoyance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->DSMitigation != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->DSMitigation + item->DSMitigation) > RuleI(Character, ItemDSMitigationCap)
|
|
||||||
) {
|
|
||||||
b->DSMitigation = RuleI(Character, ItemDSMitigationCap);
|
|
||||||
} else {
|
|
||||||
b->DSMitigation += item->DSMitigation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->Worn.Effect > 0 && item->Worn.Type == EQ::item::ItemEffectWorn) {
|
|
||||||
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, b, 0, item->Worn.Type);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->Focus.Effect > 0 && item->Focus.Type == EQ::item::ItemEffectFocus) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() ||
|
|
||||||
(IsNPC() && RuleB(Spells, NPC_UseFocusFromItems))
|
|
||||||
) {
|
|
||||||
ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, b, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (item->BardType) {
|
|
||||||
case EQ::item::ItemTypeAllInstrumentTypes: { // (e.g. Singing Short Sword)
|
|
||||||
if (item->BardValue > b->singingMod) {
|
|
||||||
b->singingMod = item->BardValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->BardValue > b->brassMod) {
|
|
||||||
b->brassMod = item->BardValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->BardValue > b->stringedMod) {
|
|
||||||
b->stringedMod = item->BardValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->BardValue > b->percussionMod) {
|
|
||||||
b->percussionMod = item->BardValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->BardValue > b->windMod) {
|
|
||||||
b->windMod = item->BardValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EQ::item::ItemTypeSinging: {
|
|
||||||
if (item->BardValue > b->singingMod) {
|
|
||||||
b->singingMod = item->BardValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EQ::item::ItemTypeWindInstrument: {
|
|
||||||
if (item->BardValue > b->windMod) {
|
|
||||||
b->windMod = item->BardValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EQ::item::ItemTypeStringedInstrument: {
|
|
||||||
if (item->BardValue > b->stringedMod) {
|
|
||||||
b->stringedMod = item->BardValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EQ::item::ItemTypeBrassInstrument: {
|
|
||||||
if (item->BardValue > b->brassMod) {
|
|
||||||
b->brassMod = item->BardValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EQ::item::ItemTypePercussionInstrument: {
|
|
||||||
if (item->BardValue > b->percussionMod) {
|
|
||||||
b->percussionMod = item->BardValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->SkillModValue != 0 && item->SkillModType <= EQ::skills::HIGHEST_SKILL) {
|
|
||||||
if (
|
|
||||||
(item->SkillModValue > 0 && b->skillmod[item->SkillModType] < item->SkillModValue) ||
|
|
||||||
(item->SkillModValue < 0 && b->skillmod[item->SkillModType] > item->SkillModValue)
|
|
||||||
) {
|
|
||||||
b->skillmod[item->SkillModType] = item->SkillModValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->FactionMod1) {
|
|
||||||
if (item->FactionAmt1 > 0 && item->FactionAmt1 > GetItemFactionBonus(item->FactionMod1)) {
|
|
||||||
AddItemFactionBonus(item->FactionMod1, item->FactionAmt1);
|
|
||||||
} else if (item->FactionAmt1 < 0 && item->FactionAmt1 < GetItemFactionBonus(item->FactionMod1)) {
|
|
||||||
AddItemFactionBonus(item->FactionMod1, item->FactionAmt1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->FactionMod2) {
|
|
||||||
if (item->FactionAmt2 > 0 && item->FactionAmt2 > GetItemFactionBonus(item->FactionMod2)) {
|
|
||||||
AddItemFactionBonus(item->FactionMod2, item->FactionAmt2);
|
|
||||||
} else if (item->FactionAmt2 < 0 && item->FactionAmt2 < GetItemFactionBonus(item->FactionMod2)) {
|
|
||||||
AddItemFactionBonus(item->FactionMod2, item->FactionAmt2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->FactionMod3) {
|
|
||||||
if (item->FactionAmt3 > 0 && item->FactionAmt3 > GetItemFactionBonus(item->FactionMod3)) {
|
|
||||||
AddItemFactionBonus(item->FactionMod3, item->FactionAmt3);
|
|
||||||
} else if (item->FactionAmt3 < 0 && item->FactionAmt3 < GetItemFactionBonus(item->FactionMod3)) {
|
|
||||||
AddItemFactionBonus(item->FactionMod3, item->FactionAmt3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->FactionMod4) {
|
|
||||||
if (item->FactionAmt4 > 0 && item->FactionAmt4 > GetItemFactionBonus(item->FactionMod4)) {
|
|
||||||
AddItemFactionBonus(item->FactionMod4, item->FactionAmt4);
|
|
||||||
} else if (item->FactionAmt4 < 0 && item->FactionAmt4 < GetItemFactionBonus(item->FactionMod4)) {
|
|
||||||
AddItemFactionBonus(item->FactionMod4, item->FactionAmt4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->ExtraDmgAmt != 0 && item->ExtraDmgSkill <= EQ::skills::HIGHEST_SKILL) {
|
|
||||||
if (item->ExtraDmgSkill == ALL_SKILLS) {
|
|
||||||
for (const auto& skill_id : EQ::skills::GetExtraDamageSkills()) {
|
|
||||||
if (
|
|
||||||
!IsNPC() &&
|
|
||||||
RuleI(Character, ItemExtraDmgCap) >= 0 &&
|
|
||||||
(b->SkillDamageAmount[skill_id] + item->ExtraDmgAmt) > RuleI(Character, ItemExtraDmgCap)
|
|
||||||
) {
|
|
||||||
b->SkillDamageAmount[skill_id] = RuleI(Character, ItemExtraDmgCap);
|
|
||||||
} else {
|
|
||||||
b->SkillDamageAmount[skill_id] += item->ExtraDmgAmt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (
|
|
||||||
!IsNPC() &&
|
|
||||||
RuleI(Character, ItemExtraDmgCap) >= 0 &&
|
|
||||||
(b->SkillDamageAmount[item->ExtraDmgSkill] + item->ExtraDmgAmt) > RuleI(Character, ItemExtraDmgCap)
|
|
||||||
) {
|
|
||||||
b->SkillDamageAmount[item->ExtraDmgSkill] = RuleI(Character, ItemExtraDmgCap);
|
|
||||||
} else {
|
|
||||||
b->SkillDamageAmount[item->ExtraDmgSkill] += item->ExtraDmgAmt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_augment) {
|
|
||||||
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
|
||||||
const auto* augment = inst->GetAugment(i);
|
|
||||||
if (!augment) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddItemBonuses(augment, b, true, false, recommended_level);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Mob::AdditiveWornBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_augment) {
|
void Mob::AdditiveWornBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_augment) {
|
||||||
/*
|
/*
|
||||||
Powerful Non-live like option allows developers to add worn effects on items that
|
Powerful Non-live like option allows developers to add worn effects on items that
|
||||||
@@ -1375,10 +1165,6 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case SE_ResistFearChance: {
|
case SE_ResistFearChance: {
|
||||||
if (base_value == 100) // If we reach 100% in a single spell/item then we should be immune to
|
|
||||||
// negative fear resist effects until our immunity is over
|
|
||||||
newbon->Fearless = true;
|
|
||||||
|
|
||||||
newbon->ResistFearChance += base_value; // these should stack
|
newbon->ResistFearChance += base_value; // these should stack
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2684,9 +2470,6 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
|||||||
|
|
||||||
case SE_ResistFearChance:
|
case SE_ResistFearChance:
|
||||||
{
|
{
|
||||||
if(effect_value == 100) // If we reach 100% in a single spell/item then we should be immune to negative fear resist effects until our immunity is over
|
|
||||||
new_bonus->Fearless = true;
|
|
||||||
|
|
||||||
new_bonus->ResistFearChance += effect_value; // these should stack
|
new_bonus->ResistFearChance += effect_value; // these should stack
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -4899,11 +4682,7 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SE_ResistFearChance:
|
case SE_ResistFearChance:
|
||||||
if (negate_spellbonus) {
|
if (negate_spellbonus) {spellbonuses.ResistFearChance = effect_value; }
|
||||||
spellbonuses.Fearless = false;
|
|
||||||
spellbonuses.ResistFearChance = effect_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (negate_aabonus) { aabonuses.ResistFearChance = effect_value; }
|
if (negate_aabonus) { aabonuses.ResistFearChance = effect_value; }
|
||||||
if (negate_itembonus) { itembonuses.ResistFearChance = effect_value; }
|
if (negate_itembonus) { itembonuses.ResistFearChance = effect_value; }
|
||||||
break;
|
break;
|
||||||
@@ -5541,6 +5320,14 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id)
|
|||||||
spellbonuses.SEResist[e] = effect_value;
|
spellbonuses.SEResist[e] = effect_value;
|
||||||
spellbonuses.SEResist[e + 1] = effect_value;
|
spellbonuses.SEResist[e + 1] = effect_value;
|
||||||
}
|
}
|
||||||
|
if (negate_itembonus) {
|
||||||
|
itembonuses.SEResist[e] = effect_value;
|
||||||
|
itembonuses.SEResist[e + 1] = effect_value;
|
||||||
|
}
|
||||||
|
if (negate_aabonus) {
|
||||||
|
aabonuses.SEResist[e] = effect_value;
|
||||||
|
aabonuses.SEResist[e + 1] = effect_value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+594
-430
File diff suppressed because it is too large
Load Diff
+6
-38
@@ -39,9 +39,6 @@
|
|||||||
|
|
||||||
constexpr uint32 BOT_KEEP_ALIVE_INTERVAL = 5000; // 5 seconds
|
constexpr uint32 BOT_KEEP_ALIVE_INTERVAL = 5000; // 5 seconds
|
||||||
|
|
||||||
constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MIN = 1500; // 1.5 seconds
|
|
||||||
constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MAX = 3000; // 3 seconds
|
|
||||||
|
|
||||||
constexpr uint32 MAG_EPIC_1_0 = 28034;
|
constexpr uint32 MAG_EPIC_1_0 = 28034;
|
||||||
|
|
||||||
extern WorldServer worldserver;
|
extern WorldServer worldserver;
|
||||||
@@ -232,22 +229,6 @@ static std::map<uint16, std::string> botSubType_names = {
|
|||||||
{ CommandedSubTypes::Selo, "Selo" }
|
{ CommandedSubTypes::Selo, "Selo" }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CombatRangeInput {
|
|
||||||
Mob* target;
|
|
||||||
float target_distance;
|
|
||||||
bool behind_mob;
|
|
||||||
uint8 stop_melee_level;
|
|
||||||
const EQ::ItemInstance* p_item;
|
|
||||||
const EQ::ItemInstance* s_item;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CombatRangeOutput {
|
|
||||||
bool at_combat_range = false;
|
|
||||||
float melee_distance_min = 0.0f;
|
|
||||||
float melee_distance = 0.0f;
|
|
||||||
float melee_distance_max = 0.0f;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Bot : public NPC {
|
class Bot : public NPC {
|
||||||
friend class Mob;
|
friend class Mob;
|
||||||
public:
|
public:
|
||||||
@@ -578,7 +559,7 @@ public:
|
|||||||
uint16 GetPetBotSpellType(uint16 spell_type);
|
uint16 GetPetBotSpellType(uint16 spell_type);
|
||||||
|
|
||||||
// Movement checks
|
// Movement checks
|
||||||
bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only = false, bool front_only = false, bool bypass_los = false);
|
bool PlotBotPositionAroundTarget(const FindPositionInput& input);
|
||||||
std::vector<Mob*> GetSpellTargetList(bool entire_raid = false);
|
std::vector<Mob*> GetSpellTargetList(bool entire_raid = false);
|
||||||
void SetSpellTargetList(std::vector<Mob*> spell_target_list) { _spell_target_list = spell_target_list; }
|
void SetSpellTargetList(std::vector<Mob*> spell_target_list) { _spell_target_list = spell_target_list; }
|
||||||
std::vector<Mob*> GetGroupSpellTargetList() { return _group_spell_target_list; }
|
std::vector<Mob*> GetGroupSpellTargetList() { return _group_spell_target_list; }
|
||||||
@@ -684,12 +665,9 @@ public:
|
|||||||
void SetSitManaPct(uint8 value) { _SitManaPct = value; }
|
void SetSitManaPct(uint8 value) { _SitManaPct = value; }
|
||||||
|
|
||||||
// Spell lists
|
// Spell lists
|
||||||
void CheckBotSpells();
|
|
||||||
void MapSpellTypeLevels();
|
|
||||||
const std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>>& GetCommandedSpellTypesMinLevels() { return commanded_spells_min_level; }
|
|
||||||
std::list<BotSpellTypeOrder> GetSpellTypesPrioritized(uint8 priority_type);
|
std::list<BotSpellTypeOrder> GetSpellTypesPrioritized(uint8 priority_type);
|
||||||
static uint16 GetParentSpellType(uint16 spell_type);
|
static uint16 GetParentSpellType(uint16 spell_type);
|
||||||
bool IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id);
|
static bool IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id);
|
||||||
inline uint16 GetCastedSpellType() const { return _castedSpellType; }
|
inline uint16 GetCastedSpellType() const { return _castedSpellType; }
|
||||||
void SetCastedSpellType(uint16 spell_type);
|
void SetCastedSpellType(uint16 spell_type);
|
||||||
bool IsValidSpellTypeSubType(uint16 spell_type, uint16 sub_type, uint16 spell_id);
|
bool IsValidSpellTypeSubType(uint16 spell_type, uint16 sub_type, uint16 spell_id);
|
||||||
@@ -1106,15 +1084,8 @@ public:
|
|||||||
bool CheckIfCasting(float fm_distance);
|
bool CheckIfCasting(float fm_distance);
|
||||||
void HealRotationChecks();
|
void HealRotationChecks();
|
||||||
|
|
||||||
bool GetCombatJitterFlag() { return m_combat_jitter_flag; }
|
|
||||||
void SetCombatJitterFlag(bool flag = true) { m_combat_jitter_flag = flag; }
|
|
||||||
bool GetCombatOutOfRangeJitterFlag() { return m_combat_out_of_range_jitter_flag; }
|
|
||||||
void SetCombatOutOfRangeJitterFlag(bool flag = true) { m_combat_out_of_range_jitter_flag = flag; }
|
|
||||||
void SetCombatJitter();
|
void SetCombatJitter();
|
||||||
void SetCombatOutOfRangeJitter();
|
bool DoCombatPositioning(const CombatPositioningInput& input);
|
||||||
void DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stop_melee_level, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behind_mob, bool front_mob);
|
|
||||||
void DoFaceCheckWithJitter(Mob* tar);
|
|
||||||
void DoFaceCheckNoJitter(Mob* tar);
|
|
||||||
void RunToGoalWithJitter(glm::vec3 Goal);
|
void RunToGoalWithJitter(glm::vec3 Goal);
|
||||||
bool RequiresLoSForPositioning();
|
bool RequiresLoSForPositioning();
|
||||||
bool HasRequiredLoSForPositioning(Mob* tar);
|
bool HasRequiredLoSForPositioning(Mob* tar);
|
||||||
@@ -1122,11 +1093,12 @@ public:
|
|||||||
// Try Combat Methods
|
// Try Combat Methods
|
||||||
bool TryEvade(Mob* tar);
|
bool TryEvade(Mob* tar);
|
||||||
bool TryFacingTarget(Mob* tar);
|
bool TryFacingTarget(Mob* tar);
|
||||||
bool TryPursueTarget(float leash_distance, glm::vec3& Goal);
|
bool TryPursueTarget(float leash_distance);
|
||||||
bool TryMeditate();
|
bool TryMeditate();
|
||||||
bool TryAutoDefend(Client* bot_owner, float leash_distance);
|
bool TryAutoDefend(Client* bot_owner, float leash_distance);
|
||||||
bool TryIdleChecks(float fm_distance);
|
bool TryIdleChecks(float fm_distance);
|
||||||
bool TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal);
|
bool TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob);
|
||||||
|
void DoOutOfCombatChecks(Client* bot_owner, Mob* follow_mob, float leash_distance, float fm_distance);
|
||||||
bool TryBardMovementCasts();
|
bool TryBardMovementCasts();
|
||||||
bool BotRangedAttack(Mob* other, bool can_double_attack = false);
|
bool BotRangedAttack(Mob* other, bool can_double_attack = false);
|
||||||
bool CheckDoubleRangedAttack();
|
bool CheckDoubleRangedAttack();
|
||||||
@@ -1152,8 +1124,6 @@ protected:
|
|||||||
std::vector<BotSpells> AIBot_spells_enforced;
|
std::vector<BotSpells> AIBot_spells_enforced;
|
||||||
std::unordered_map<uint16, std::vector<BotSpells_wIndex>> AIBot_spells_by_type;
|
std::unordered_map<uint16, std::vector<BotSpells_wIndex>> AIBot_spells_by_type;
|
||||||
|
|
||||||
std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>> commanded_spells_min_level;
|
|
||||||
|
|
||||||
std::vector<BotTimer> bot_timers;
|
std::vector<BotTimer> bot_timers;
|
||||||
std::vector<BotBlockedBuffs> bot_blocked_buffs;
|
std::vector<BotBlockedBuffs> bot_blocked_buffs;
|
||||||
|
|
||||||
@@ -1194,8 +1164,6 @@ private:
|
|||||||
Timer m_auto_save_timer;
|
Timer m_auto_save_timer;
|
||||||
|
|
||||||
Timer m_combat_jitter_timer;
|
Timer m_combat_jitter_timer;
|
||||||
bool m_combat_jitter_flag;
|
|
||||||
bool m_combat_out_of_range_jitter_flag;
|
|
||||||
|
|
||||||
bool m_dirtyautohaters;
|
bool m_dirtyautohaters;
|
||||||
bool m_guard_flag;
|
bool m_guard_flag;
|
||||||
|
|||||||
+10
-2
@@ -57,6 +57,7 @@
|
|||||||
#include "water_map.h"
|
#include "water_map.h"
|
||||||
#include "worldserver.h"
|
#include "worldserver.h"
|
||||||
#include "mob.h"
|
#include "mob.h"
|
||||||
|
#include "bot_database.h"
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
@@ -219,6 +220,13 @@ int bot_command_init(void)
|
|||||||
std::vector<std::pair<std::string, uint8>> injected_bot_command_settings;
|
std::vector<std::pair<std::string, uint8>> injected_bot_command_settings;
|
||||||
std::vector<std::string> orphaned_bot_command_settings;
|
std::vector<std::string> orphaned_bot_command_settings;
|
||||||
|
|
||||||
|
if (RuleB(Bots, RunSpellTypeChecksOnBoot)) {
|
||||||
|
LogBotSpellTypeChecks("Running SpellType checks. There may be some spells that are mislabeled as incorrect. Use this as a loose guideline.");
|
||||||
|
database.botdb.CheckBotSpells();
|
||||||
|
}
|
||||||
|
|
||||||
|
database.botdb.MapCommandedSpellTypeMinLevels();
|
||||||
|
|
||||||
for (auto bcs_iter : bot_command_settings) {
|
for (auto bcs_iter : bot_command_settings) {
|
||||||
|
|
||||||
auto bcl_iter = bot_command_list.find(bcs_iter.first);
|
auto bcl_iter = bot_command_list.find(bcs_iter.first);
|
||||||
@@ -796,10 +804,10 @@ void helper_send_usage_required_bots(Client *bot_owner, uint16 spell_type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& spell_map = bot->GetCommandedSpellTypesMinLevels();
|
auto spell_map = database.botdb.GetCommandedSpellTypesMinLevels();
|
||||||
|
|
||||||
if (spell_map.empty()) {
|
if (spell_map.empty()) {
|
||||||
bot_owner->Message(Chat::Yellow, "No bots are capable of casting this spell type");
|
bot_owner->Message(Chat::Yellow, "No bots are capable of casting this spell type.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void bot_command_attack(Client *c, const Seperator *sep)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c->DoLosChecks(target_mob)) {
|
if (RuleB(Bots, BotsRequireLoS) && !c->DoLosChecks(target_mob)) {
|
||||||
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -525,6 +525,9 @@ void bot_command_cast(Client* c, const Seperator* sep)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool requires_los = !(IsAnyHealSpell(spell_id) && !IsPBAESpell(spell_id));
|
||||||
|
bot_iter->SetHasLoS(requires_los ? bot_iter->DoLosChecks(new_tar) : true);
|
||||||
|
|
||||||
if (!bot_iter->AttemptAACastSpell(tar, spell_id, rank)) {
|
if (!bot_iter->AttemptAACastSpell(tar, spell_id, rank)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -543,6 +546,9 @@ void bot_command_cast(Client* c, const Seperator* sep)
|
|||||||
tar = bot_iter;
|
tar = bot_iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool los_required = bot_iter != tar && !IsAnyHealSpell(chosen_spell_id) && !IsPBAESpell(chosen_spell_id);
|
||||||
|
bot_iter->SetHasLoS(los_required ? bot_iter->DoLosChecks(new_tar) : true);
|
||||||
|
|
||||||
if (bot_iter->AttemptForcedCastSpell(tar, chosen_spell_id)) {
|
if (bot_iter->AttemptForcedCastSpell(tar, chosen_spell_id)) {
|
||||||
if (!first_found) {
|
if (!first_found) {
|
||||||
first_found = bot_iter;
|
first_found = bot_iter;
|
||||||
@@ -556,6 +562,7 @@ void bot_command_cast(Client* c, const Seperator* sep)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bot_iter->SetCommandedSpell(true);
|
bot_iter->SetCommandedSpell(true);
|
||||||
|
bot_iter->SetHasLoS(BotSpellTypeRequiresLoS(spell_type) ? bot_iter->DoLosChecks(new_tar) : true);
|
||||||
|
|
||||||
if (bot_iter->AICastSpell(new_tar, 100, spell_type, sub_target_type, sub_type)) {
|
if (bot_iter->AICastSpell(new_tar, 100, spell_type, sub_target_type, sub_type)) {
|
||||||
if (!first_found) {
|
if (!first_found) {
|
||||||
|
|||||||
@@ -48,9 +48,10 @@ void bot_command_click_item(Client* c, const Seperator* sep)
|
|||||||
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
|
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
|
||||||
|
|
||||||
Mob* tar = c->GetTarget();
|
Mob* tar = c->GetTarget();
|
||||||
|
bool is_success = false;
|
||||||
|
|
||||||
for (auto my_bot : sbl) {
|
for (auto my_bot : sbl) {
|
||||||
if (my_bot->BotPassiveCheck()) {
|
if (!my_bot->ValidStateCheck(c)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +69,11 @@ void bot_command_click_item(Client* c, const Seperator* sep)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_success = true;
|
||||||
my_bot->TryItemClick(slot_id);
|
my_bot->TryItemClick(slot_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!is_success) {
|
||||||
|
c->Message(Chat::Yellow, "None of your bots are capable of doing that currently.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,7 +197,11 @@ void bot_command_depart(Client* c, const Seperator* sep)
|
|||||||
|
|
||||||
bot_iter->SetCommandedSpell(true);
|
bot_iter->SetCommandedSpell(true);
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(itr->SpellId, bot_iter->HasLoS())) {
|
if (!IsValidSpell(itr->SpellId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(BotSpellTypes::Teleport, itr->SpellId) && !bot_iter->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ void bot_command_precombat(Client* c, const Seperator* sep)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c->DoLosChecks(c->GetTarget())) {
|
if (RuleB(Bots, BotsRequireLoS) && !c->DoLosChecks(c->GetTarget())) {
|
||||||
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ void bot_command_pull(Client *c, const Seperator *sep)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c->DoLosChecks(target_mob)) {
|
if (RuleB(Bots, BotsRequireLoS) && !c->DoLosChecks(target_mob)) {
|
||||||
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include "../common/repositories/bot_pet_buffs_repository.h"
|
#include "../common/repositories/bot_pet_buffs_repository.h"
|
||||||
#include "../common/repositories/bot_pet_inventories_repository.h"
|
#include "../common/repositories/bot_pet_inventories_repository.h"
|
||||||
#include "../common/repositories/bot_spell_casting_chances_repository.h"
|
#include "../common/repositories/bot_spell_casting_chances_repository.h"
|
||||||
|
#include "../common/repositories/bot_spells_entries_repository.h"
|
||||||
#include "../common/repositories/bot_settings_repository.h"
|
#include "../common/repositories/bot_settings_repository.h"
|
||||||
#include "../common/repositories/bot_stances_repository.h"
|
#include "../common/repositories/bot_stances_repository.h"
|
||||||
#include "../common/repositories/bot_timers_repository.h"
|
#include "../common/repositories/bot_timers_repository.h"
|
||||||
@@ -2521,3 +2522,208 @@ bool BotDatabase::DeleteBotBlockedBuffs(const uint32 bot_id)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BotDatabase::CheckBotSpells() {
|
||||||
|
auto spell_list = BotSpellsEntriesRepository::All(content_db);
|
||||||
|
uint16 spell_id;
|
||||||
|
SPDat_Spell_Struct spell;
|
||||||
|
|
||||||
|
for (const auto& s : spell_list) {
|
||||||
|
if (!IsValidSpell(s.spell_id)) {
|
||||||
|
LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
spell = spells[s.spell_id];
|
||||||
|
spell_id = spell.id;
|
||||||
|
|
||||||
|
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] >= 255) {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.minlevel) {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the min level is currently set to {}.",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id,
|
||||||
|
s.minlevel
|
||||||
|
);
|
||||||
|
|
||||||
|
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]",
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
spell_id,
|
||||||
|
s.npc_spells_id,
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
s.minlevel,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] < s.minlevel) {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] could be used starting at level {} for a {} [#{}] instead of the current min level of {}.",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id,
|
||||||
|
s.minlevel
|
||||||
|
);
|
||||||
|
|
||||||
|
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]",
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
spell_id,
|
||||||
|
s.npc_spells_id,
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
s.minlevel,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.maxlevel) {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the max level is currently set to {}.",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id,
|
||||||
|
s.maxlevel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 correct_type = GetCorrectBotSpellType(s.type, spell_id);
|
||||||
|
|
||||||
|
if (RuleB(Bots, UseParentSpellTypeForChecks)) {
|
||||||
|
uint16 parent_type = Bot::GetParentSpellType(correct_type);
|
||||||
|
|
||||||
|
if (s.type == parent_type || s.type == correct_type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (correct_type != parent_type) {
|
||||||
|
correct_type = parent_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (IsPetBotSpellType(s.type)) {
|
||||||
|
correct_type = GetPetBotSpellType(correct_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsPetBotSpellType(correct_type) && (spell.target_type != ST_Pet && spell.target_type != ST_SummonedPet)) {
|
||||||
|
correct_type = Bot::GetParentSpellType(correct_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (correct_type == s.type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (correct_type == UINT16_MAX) {
|
||||||
|
LogBotSpellTypeChecks(
|
||||||
|
"{} [#{}] is incorrect. It is currently set as {} [#{}] but the correct type is unknown.",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
Bot::GetSpellTypeNameByID(s.type),
|
||||||
|
s.type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
Bot::GetSpellTypeNameByID(s.type),
|
||||||
|
s.type,
|
||||||
|
Bot::GetSpellTypeNameByID(correct_type),
|
||||||
|
correct_type
|
||||||
|
);
|
||||||
|
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spell_id` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]",
|
||||||
|
correct_type,
|
||||||
|
spell_id,
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
Bot::GetSpellTypeNameByID(s.type),
|
||||||
|
s.type,
|
||||||
|
Bot::GetSpellTypeNameByID(correct_type),
|
||||||
|
correct_type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BotDatabase::MapCommandedSpellTypeMinLevels() {
|
||||||
|
commanded_spell_type_min_levels.clear();
|
||||||
|
|
||||||
|
auto start = std::min({ BotSpellTypes::START, BotSpellTypes::COMMANDED_START, BotSpellTypes::DISCIPLINE_START });
|
||||||
|
auto end = std::max({ BotSpellTypes::END, BotSpellTypes::COMMANDED_END, BotSpellTypes::DISCIPLINE_END });
|
||||||
|
|
||||||
|
for (int i = start; i <= end; ++i) {
|
||||||
|
if (!Bot::IsValidBotSpellType(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int x = Class::Warrior; x <= Class::Berserker; ++x) {
|
||||||
|
commanded_spell_type_min_levels[i][x] = {UINT8_MAX, "" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto spell_list = BotSpellsEntriesRepository::All(content_db);
|
||||||
|
|
||||||
|
for (const auto& s : spell_list) {
|
||||||
|
if (!IsValidSpell(s.spell_id)) {
|
||||||
|
LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto spell = spells[s.spell_id];
|
||||||
|
|
||||||
|
if (spell.target_type == ST_Self) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t bot_class = s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!EQ::ValueWithin(bot_class, Class::Warrior, Class::Berserker) ||
|
||||||
|
!Bot::IsValidBotSpellType(s.type)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = start; i <= end; ++i) {
|
||||||
|
if (s.minlevel > commanded_spell_type_min_levels[i][bot_class].min_level) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
i > BotSpellTypes::PARENT_TYPE_END &&
|
||||||
|
i != s.type &&
|
||||||
|
Bot::GetParentSpellType(i) != s.type
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Bot::IsValidSpellTypeBySpellID(i, s.spell_id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.minlevel < commanded_spell_type_min_levels[i][bot_class].min_level) {
|
||||||
|
commanded_spell_type_min_levels[i][bot_class].min_level = s.minlevel;
|
||||||
|
commanded_spell_type_min_levels[i][bot_class].description = StringFormat(
|
||||||
|
"%s [#%u] - Level %u",
|
||||||
|
GetClassIDName(bot_class),
|
||||||
|
bot_class,
|
||||||
|
s.minlevel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+9
-1
@@ -24,7 +24,7 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "bot_structs.h"
|
||||||
|
|
||||||
class Bot;
|
class Bot;
|
||||||
class Client;
|
class Client;
|
||||||
@@ -130,6 +130,10 @@ public:
|
|||||||
bool SaveBotSettings(Mob* m);
|
bool SaveBotSettings(Mob* m);
|
||||||
bool DeleteBotSettings(const uint32 bot_id);
|
bool DeleteBotSettings(const uint32 bot_id);
|
||||||
|
|
||||||
|
void CheckBotSpells();
|
||||||
|
void MapCommandedSpellTypeMinLevels();
|
||||||
|
std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>> GetCommandedSpellTypesMinLevels() { return commanded_spell_type_min_levels; }
|
||||||
|
|
||||||
/* Bot group functions */
|
/* Bot group functions */
|
||||||
bool LoadGroupedBotsByGroupID(const uint32 owner_id, const uint32 group_id, std::list<uint32>& group_list);
|
bool LoadGroupedBotsByGroupID(const uint32 owner_id, const uint32 group_id, std::list<uint32>& group_list);
|
||||||
|
|
||||||
@@ -211,6 +215,10 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::string query;
|
std::string query;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>> commanded_spell_type_min_levels;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/types.h"
|
||||||
#include "../common/timer.h"
|
#include "../common/timer.h"
|
||||||
|
#include "mob.h"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
@@ -151,4 +152,39 @@ struct BotSpellTypesByClass {
|
|||||||
std::string description;
|
std::string description;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CombatRangeInput {
|
||||||
|
Mob* target;
|
||||||
|
float target_distance;
|
||||||
|
bool stop_melee_level;
|
||||||
|
const EQ::ItemInstance* p_item;
|
||||||
|
const EQ::ItemInstance* s_item;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CombatRangeOutput {
|
||||||
|
bool at_combat_range = false;
|
||||||
|
float melee_distance_min = 0.0f;
|
||||||
|
float melee_distance = 0.0f;
|
||||||
|
float melee_distance_max = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CombatPositioningInput {
|
||||||
|
Mob* tar;
|
||||||
|
bool stop_melee_level;
|
||||||
|
float tar_distance;
|
||||||
|
float melee_distance_min;
|
||||||
|
float melee_distance;
|
||||||
|
float melee_distance_max;
|
||||||
|
bool behind_mob;
|
||||||
|
bool front_mob;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FindPositionInput {
|
||||||
|
Mob* tar;
|
||||||
|
float distance_min;
|
||||||
|
float distance_max;
|
||||||
|
bool behind_only;
|
||||||
|
bool front_only;
|
||||||
|
bool bypass_los;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // BOT_STRUCTS
|
#endif // BOT_STRUCTS
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user