mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-23 13:12:28 +00:00
Compare commits
209 Commits
v23.2.0
...
build-analyze
| Author | SHA1 | Date | |
|---|---|---|---|
| 588de15382 | |||
| aeb13e8fd3 | |||
| 98f824b0f5 | |||
| 605e23e8d0 | |||
| fc470d5f83 | |||
| 585ed3bd25 | |||
| 2eb291a461 | |||
| a1421af214 | |||
| 13aad6229f | |||
| 76d46ceaf0 | |||
| 389047c4e2 | |||
| 2c6d405b2c | |||
| 50ae0f8351 | |||
| dc261bb203 | |||
| 643ee56433 | |||
| c0bb32ed12 | |||
| c99bda3f47 | |||
| 967a13e692 | |||
| f304f9cc61 | |||
| 004e2ca63f | |||
| 128732e05d | |||
| 1e6a4dac78 | |||
| 654764685a | |||
| c5ab35e4af | |||
| 947795f1d1 | |||
| 1153c9ab96 | |||
| 2128b45313 | |||
| 4f7ff2d6f2 | |||
| b5f1e99d3b | |||
| 83918ce020 | |||
| f3aaeff0a9 | |||
| 98eff43346 | |||
| 46b43a990f | |||
| ea96cbf885 | |||
| de07870c99 | |||
| b3b228c26c | |||
| 27b5c80c3c | |||
| fb4d003e19 | |||
| f025e5741b | |||
| c3f2708f1b | |||
| 2e760d6397 | |||
| bac892b582 | |||
| 4005b68383 | |||
| 5ac9dd04e4 | |||
| f0c041e8b3 | |||
| 2f4a5b56dd | |||
| 940f97c9ae | |||
| f8ee664b27 | |||
| df86ad371b | |||
| e846bb86b6 | |||
| 3e6a3e2168 | |||
| 2aebf1a78a | |||
| 1be7e56b86 | |||
| a0ff9d67a1 | |||
| befee1c729 | |||
| 3d70063a68 | |||
| 4e28bcf85e | |||
| 687d10960a | |||
| 567d46c3d6 | |||
| 53cc2de459 | |||
| cb866cba31 | |||
| e2162c08da | |||
| e657953b8f | |||
| eb366e67b7 | |||
| 5b728a42f7 | |||
| a1d414d64c | |||
| 907029ed76 | |||
| 894f22fba0 | |||
| 7ec09d7e0f | |||
| c82266790a | |||
| ec31fddbae | |||
| fb49ce2404 | |||
| 276b7e238a | |||
| c7a463420b | |||
| a56bb52808 | |||
| 0bbdb58679 | |||
| f29478c105 | |||
| 888a88f966 | |||
| 3b617a6652 | |||
| c36c336bc7 | |||
| 4a9779635d | |||
| 20da490bda | |||
| 1221e88d92 | |||
| a2b28b2e16 | |||
| 3d607d352c | |||
| 83cd8119c8 | |||
| 4de8fbbd56 | |||
| 780120036d | |||
| f3697e633c | |||
| 24f8d88333 | |||
| 21bd906a4d | |||
| 138612bc88 | |||
| 5919bb4dea | |||
| 99d249fefd | |||
| c08f286817 | |||
| cd003ff0b7 | |||
| 1a539f6656 | |||
| 7e7fb7b758 | |||
| 5ae87b40e2 | |||
| 0ec07daebb | |||
| 9869da2a0a | |||
| 617eb4432b | |||
| 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 |
+33
-1
@@ -30,6 +30,11 @@ steps:
|
||||
- name: cache
|
||||
path: /home/eqemu/.ccache/
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
exclude:
|
||||
- build-analyze
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
@@ -57,6 +62,11 @@ steps:
|
||||
commands:
|
||||
- .\utils\scripts\build\windows-build.ps1
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
exclude:
|
||||
- build-analyze
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
@@ -87,7 +97,10 @@ steps:
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- master
|
||||
include:
|
||||
- master
|
||||
exclude:
|
||||
- build-analyze
|
||||
event:
|
||||
- push
|
||||
|
||||
@@ -95,4 +108,23 @@ depends_on:
|
||||
- Build Windows
|
||||
- Build Linux
|
||||
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: Build Analyze
|
||||
|
||||
steps:
|
||||
- name: Build Linux X64
|
||||
image: akkadius/eqemu-server:v16
|
||||
environment:
|
||||
GITHUB_TOKEN:
|
||||
from_secret: GH_RELEASE_GITHUB_API_TOKEN
|
||||
commands:
|
||||
- ./utils/scripts/build/linux-build-analyze.sh
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- build-analyze
|
||||
event:
|
||||
- push
|
||||
+435
@@ -1,3 +1,438 @@
|
||||
## [23.8.1] 6/28/2025
|
||||
|
||||
### Crash Fix
|
||||
|
||||
* Fix Possible Crashes with Raid Methods ([#4955](https://github.com/EQEmu/Server/pull/4955)) @Kinglykrab 2025-06-26
|
||||
|
||||
### Databuckets
|
||||
|
||||
* Revert Caching Changes of #4917 ([#4957](https://github.com/EQEmu/Server/pull/4957)) @Akkadius 2025-06-28
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix FindCharacter Using content_db ([#4956](https://github.com/EQEmu/Server/pull/4956)) @Kinglykrab 2025-06-26
|
||||
* Fix Hero Forge on Character Select ([#4954](https://github.com/EQEmu/Server/pull/4954)) @Kinglykrab 2025-06-26
|
||||
|
||||
## [23.8.0] 6/25/2025
|
||||
|
||||
### API
|
||||
|
||||
* Add wwmarquee <type> <message> ([#4919](https://github.com/EQEmu/Server/pull/4919)) @Akkadius 2025-06-23
|
||||
|
||||
### Build
|
||||
|
||||
* Significantly Improve Build Times Using Unity Builds ([#4948](https://github.com/EQEmu/Server/pull/4948)) @Akkadius 2025-06-22
|
||||
|
||||
### Code
|
||||
|
||||
* AdventureManager Global to Singleton Cleanup ([#4931](https://github.com/EQEmu/Server/pull/4931)) @Kinglykrab 2025-06-25
|
||||
* Cleanup Strings Header ([#4950](https://github.com/EQEmu/Server/pull/4950)) @Akkadius 2025-06-22
|
||||
* ClientList Global to Singleton Cleanup ([#4942](https://github.com/EQEmu/Server/pull/4942)) @Kinglykrab 2025-06-25
|
||||
* DatabaseUpdate Global to Singleton Cleanup ([#4943](https://github.com/EQEmu/Server/pull/4943)) @Kinglykrab 2025-06-25
|
||||
* DiscordManager Global to Singleton Cleanup ([#4926](https://github.com/EQEmu/Server/pull/4926)) @Kinglykrab 2025-06-25
|
||||
* EQ::Random Global to Singleton Cleanup ([#4936](https://github.com/EQEmu/Server/pull/4936)) @Kinglykrab 2025-06-22
|
||||
* EQEmuLogSys Global to Singleton Cleanup ([#4925](https://github.com/EQEmu/Server/pull/4925)) @Akkadius 2025-06-25
|
||||
* EvolvingItemsManager Global to Singleton Cleanup ([#4929](https://github.com/EQEmu/Server/pull/4929)) @Kinglykrab 2025-06-25
|
||||
* LFGuildManager Global to Singleton Cleanup ([#4927](https://github.com/EQEmu/Server/pull/4927)) @Kinglykrab 2025-06-25
|
||||
* LoginServerList Global to Singleton Cleanup ([#4941](https://github.com/EQEmu/Server/pull/4941)) @Kinglykrab 2025-06-25
|
||||
* Optimize PCH Config (Faster Builds) ([#4951](https://github.com/EQEmu/Server/pull/4951)) @Akkadius 2025-06-22
|
||||
* PCH Cleanup ([#4952](https://github.com/EQEmu/Server/pull/4952)) @Akkadius 2025-06-25
|
||||
* PathManager Global to Singleton Cleanup ([#4924](https://github.com/EQEmu/Server/pull/4924)) @Akkadius 2025-06-22
|
||||
* PetitionList Global to Singleton Cleanup ([#4944](https://github.com/EQEmu/Server/pull/4944)) @Kinglykrab 2025-06-25
|
||||
* PlayerEventLogs Global to Singleton Cleanup ([#4928](https://github.com/EQEmu/Server/pull/4928)) @Kinglykrab 2025-06-25
|
||||
* QueryServConnection Global to Singleton Cleanup ([#4938](https://github.com/EQEmu/Server/pull/4938)) @Kinglykrab 2025-06-25
|
||||
* Remove Lua Rule Constants ([#4949](https://github.com/EQEmu/Server/pull/4949)) @Akkadius 2025-06-22
|
||||
* Remove Regex Compile Bloat ([#4947](https://github.com/EQEmu/Server/pull/4947)) @Akkadius 2025-06-22
|
||||
* Remove Unused MZoneShutdown Mutex ([#4946](https://github.com/EQEmu/Server/pull/4946)) @Kinglykrab 2025-06-22
|
||||
* SharedTaskManager Global to Singleton Cleanup ([#4930](https://github.com/EQEmu/Server/pull/4930)) @Kinglykrab 2025-06-25
|
||||
* SkillCaps Global to Singleton Cleanup ([#4933](https://github.com/EQEmu/Server/pull/4933)) @Kinglykrab 2025-06-22
|
||||
* TaskManager Global to Singleton Cleanup ([#4945](https://github.com/EQEmu/Server/pull/4945)) @Kinglykrab 2025-06-22
|
||||
* UCSConnection Global to Singleton Cleanup ([#4937](https://github.com/EQEmu/Server/pull/4937)) @Kinglykrab 2025-06-25
|
||||
* WebInterfaceList Global to Singleton Cleanup ([#4935](https://github.com/EQEmu/Server/pull/4935)) @Kinglykrab 2025-06-25
|
||||
* WorldContentServer Global to Singleton Cleanup ([#4939](https://github.com/EQEmu/Server/pull/4939)) @Kinglykrab 2025-06-25
|
||||
* WorldEventScheduler and ZoneEventScheduler Global to Singleton Cleanup ([#4932](https://github.com/EQEmu/Server/pull/4932)) @Kinglykrab 2025-06-25
|
||||
* ZSList Global to Singleton Cleanup ([#4940](https://github.com/EQEmu/Server/pull/4940)) @Kinglykrab 2025-06-25
|
||||
* ZoneStore Global to Singleton Cleanup ([#4934](https://github.com/EQEmu/Server/pull/4934)) @Kinglykrab 2025-06-23
|
||||
|
||||
### Corpses
|
||||
|
||||
* Add corpse entity variables to DB ([#4911](https://github.com/EQEmu/Server/pull/4911)) @fryguy503 2025-06-25
|
||||
|
||||
### Databuckets
|
||||
|
||||
* Move Databuckets to Common ([#4918](https://github.com/EQEmu/Server/pull/4918)) @Akkadius 2025-06-16
|
||||
* Nested Caching ([#4917](https://github.com/EQEmu/Server/pull/4917)) @Akkadius 2025-06-25
|
||||
|
||||
### Doors
|
||||
|
||||
* Fix door saving for versions ([#4905](https://github.com/EQEmu/Server/pull/4905)) @nytmyr 2025-06-09
|
||||
|
||||
### Fixes
|
||||
|
||||
* Bulk Send Corpses after Idle State ([#4910](https://github.com/EQEmu/Server/pull/4910)) @Akkadius 2025-06-09
|
||||
* Fix ClientList Singleton Shortcomings of #4942 @Akkadius 2025-06-25
|
||||
* Fix breaking changes to Titanium, SoF, SoD patches causes by big bags update ([#4922](https://github.com/EQEmu/Server/pull/4922)) @regneq 2025-06-25
|
||||
|
||||
### Logs
|
||||
|
||||
* Fix output for tasks in logs ([#4907](https://github.com/EQEmu/Server/pull/4907)) @joligario 2025-06-09
|
||||
|
||||
### Performance
|
||||
|
||||
* Auto Idle / AFK ([#4903](https://github.com/EQEmu/Server/pull/4903)) @Akkadius 2025-05-22
|
||||
* Wearchange Packet Send Deduplication ([#4916](https://github.com/EQEmu/Server/pull/4916)) @Akkadius 2025-06-25
|
||||
|
||||
### Player Event Logs
|
||||
|
||||
* Don't Clear Event Data on ETL Events ([#4913](https://github.com/EQEmu/Server/pull/4913)) @Akkadius 2025-06-25
|
||||
|
||||
### Player Events
|
||||
|
||||
* Don't Write Empty Data ([#4912](https://github.com/EQEmu/Server/pull/4912)) @Akkadius 2025-06-25
|
||||
* Zone Fetch Settings from QS if Enabled ([#4915](https://github.com/EQEmu/Server/pull/4915)) @Akkadius 2025-06-25
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add CompleteTask and UncompleteTask methods to Perl/Lua ([#4921](https://github.com/EQEmu/Server/pull/4921)) @Barathos 2025-06-25
|
||||
* Lua Client Scoped EnableTitleSets ([#4914](https://github.com/EQEmu/Server/pull/4914)) @fryguy503 2025-06-25
|
||||
|
||||
### Quests
|
||||
|
||||
* Support Multiple Quest, Plugin, and Lua Module Paths ([#4906](https://github.com/EQEmu/Server/pull/4906)) @Akkadius 2025-06-09
|
||||
|
||||
### World API
|
||||
|
||||
* Input Validation ([#4904](https://github.com/EQEmu/Server/pull/4904)) @Akkadius 2025-05-22
|
||||
|
||||
## [23.7.0] 5/19/2025
|
||||
|
||||
### CLI
|
||||
|
||||
* Add custom database version output ([#4901](https://github.com/EQEmu/Server/pull/4901)) @joligario 2025-05-18
|
||||
* Fix MySQL check in database dumper ([#4897](https://github.com/EQEmu/Server/pull/4897)) @joligario 2025-05-16
|
||||
|
||||
### Commands
|
||||
|
||||
* Add #zonevariable Command ([#4882](https://github.com/EQEmu/Server/pull/4882)) @Kinglykrab 2025-05-15
|
||||
|
||||
### Database
|
||||
|
||||
* Add Custom Database Migrations for Operators ([#4892](https://github.com/EQEmu/Server/pull/4892)) @Akkadius 2025-05-16
|
||||
* Remove Transaction Wrapped Character Save ([#4894](https://github.com/EQEmu/Server/pull/4894)) @Akkadius 2025-05-16
|
||||
|
||||
### Fixes
|
||||
|
||||
* Deadlock on failed #copycharacter commands ([#4887](https://github.com/EQEmu/Server/pull/4887)) @Akkadius 2025-05-16
|
||||
|
||||
### Logging
|
||||
|
||||
* Auto Update Log Category Names ([#4890](https://github.com/EQEmu/Server/pull/4890)) @Akkadius 2025-05-16
|
||||
|
||||
### Netcode
|
||||
|
||||
* Resend Logic Adjustments ([#4900](https://github.com/EQEmu/Server/pull/4900)) @Akkadius 2025-05-18
|
||||
|
||||
### Player Events
|
||||
|
||||
* Add rule to ignore configured GM commands ([#4888](https://github.com/EQEmu/Server/pull/4888)) @Akkadius 2025-05-16
|
||||
|
||||
### Rules
|
||||
|
||||
* Auto Update Rule Notes from Source ([#4891](https://github.com/EQEmu/Server/pull/4891)) @Akkadius 2025-05-16
|
||||
|
||||
### World
|
||||
|
||||
* Fix Rarer Reload Deadlock ([#4893](https://github.com/EQEmu/Server/pull/4893)) @Akkadius 2025-05-16
|
||||
|
||||
### Zone State
|
||||
|
||||
* Load New Spawn2 Data When Present ([#4889](https://github.com/EQEmu/Server/pull/4889)) @Akkadius 2025-05-16
|
||||
|
||||
## [23.6.0] 5/14/2025
|
||||
|
||||
### Bots
|
||||
|
||||
* Correct ^pull logic and add checks for Enchanter pets ([#4827](https://github.com/EQEmu/Server/pull/4827)) @nytmyr 2025-05-15
|
||||
* Fix creation limit, spawn limit, level requirement checks ([#4868](https://github.com/EQEmu/Server/pull/4868)) @nytmyr 2025-05-15
|
||||
* Move all spell_id instances to uint16 ([#4876](https://github.com/EQEmu/Server/pull/4876)) @nytmyr 2025-05-15
|
||||
* Prevent non-taunters from potentially fleeing mob on TargetReflection ([#4859](https://github.com/EQEmu/Server/pull/4859)) @nytmyr 2025-04-28
|
||||
|
||||
### CLI
|
||||
|
||||
* ETL Settings Output ([#4873](https://github.com/EQEmu/Server/pull/4873)) @joligario 2025-05-15
|
||||
|
||||
### Code
|
||||
|
||||
* Fix typo in QueryNameAvailablity ([#4869](https://github.com/EQEmu/Server/pull/4869)) @nytmyr 2025-04-28
|
||||
|
||||
### Crash
|
||||
|
||||
* Fix crash bug with pbae and quest scripts spawning mobs ([#4884](https://github.com/EQEmu/Server/pull/4884)) @carolus21rex 2025-05-15
|
||||
|
||||
### Feature
|
||||
|
||||
* Add Character:TradeskillUpMinChance rule ([#4867](https://github.com/EQEmu/Server/pull/4867)) @zrix-eq 2025-05-15
|
||||
* Enable spawn attribute for NPCTintID ([#4871](https://github.com/EQEmu/Server/pull/4871)) @neckkola 2025-05-15
|
||||
|
||||
### Fixes
|
||||
|
||||
* Add trader/buyer cleanup actions ([#4843](https://github.com/EQEmu/Server/pull/4843)) @neckkola 2025-05-15
|
||||
* Fix #copycharacter command ([#4860](https://github.com/EQEmu/Server/pull/4860)) @nytmyr 2025-04-28
|
||||
* Fix Crash with #task ([#4874](https://github.com/EQEmu/Server/pull/4874)) @Kinglykrab 2025-04-30
|
||||
* Fix Object Name Init, User Refs, and Client Sync on Close ([#4861](https://github.com/EQEmu/Server/pull/4861)) @zimp-wow 2025-05-15
|
||||
* Fix breaking change to UF patches caused by Big Bags update ([#4883](https://github.com/EQEmu/Server/pull/4883)) @hbingram 2025-05-15
|
||||
* Prevent Ranged Attack from being triggered at arbitrary rate ([#4879](https://github.com/EQEmu/Server/pull/4879)) @catapultam-habeo 2025-05-15
|
||||
|
||||
### Performance
|
||||
|
||||
* Store Player Title Sets in Client Memory ([#4836](https://github.com/EQEmu/Server/pull/4836)) @Kinglykrab 2025-05-15
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add Last Login and First Login Flags to EVENT_CONNECT ([#4866](https://github.com/EQEmu/Server/pull/4866)) @Kinglykrab 2025-05-15
|
||||
|
||||
## [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
|
||||
|
||||
@@ -42,6 +42,7 @@ IF(USE_MAP_MMFS)
|
||||
ENDIF (USE_MAP_MMFS)
|
||||
|
||||
IF(MSVC)
|
||||
add_compile_options(/bigobj)
|
||||
ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS)
|
||||
ADD_DEFINITIONS(-DNOMINMAX)
|
||||
ADD_DEFINITIONS(-DCRASH_LOGGING)
|
||||
@@ -362,6 +363,8 @@ MESSAGE(STATUS "**************************************************")
|
||||
#setup server libs and headers
|
||||
SET(SERVER_LIBS common ${DATABASE_LIBRARY_LIBS} ${ZLIB_LIBRARY_LIBS} ${Boost_LIBRARIES} uv_a fmt RecastNavigation::Detour)
|
||||
|
||||
set(FMT_HEADER_ONLY OFF)
|
||||
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${DATABASE_LIBRARY_INCLUDE}")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${ZLIB_LIBRARY_INCLUDE}")
|
||||
INCLUDE_DIRECTORIES(SYSTEM "${Boost_INCLUDE_DIRS}")
|
||||
|
||||
@@ -1,83 +1,150 @@
|
||||
# EQEmulator Core Server
|
||||
| Drone (Linux x64) | Drone (Windows x64) |
|
||||
|:---:|:---:|
|
||||
|[](http://drone.akkadius.com/EQEmu/Server) |[](http://drone.akkadius.com/EQEmu/Server) |
|
||||
<h1 align="center">EQEmulator Server Platform</h1>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://github.com/user-attachments/assets/11942e15-b512-402d-a619-0543c7f1151e" style="border-radius: 10px">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<b>EQEmulator - A Fan-Made Project Honoring a Legendary MMORPG</b>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/eqemu/server/graphs/contributors"><img src="https://img.shields.io/github/contributors/eqemu/server" alt="Contributors"></a>
|
||||
<a href="https://discord.gg/QHsm7CD"><img src="https://img.shields.io/discord/212663220849213441?label=Discord&logo=discord&color=7289DA" alt="Discord"></a>
|
||||
<a href="https://docs.eqemu.io"><img src="https://img.shields.io/badge/docs-MkDocs%20Powered-blueviolet" alt="Docs"></a>
|
||||
<a href="./LICENSE"><img src="https://img.shields.io/github/license/EQEmu/Server" alt="License"></a>
|
||||
<a href="https://github.com/eqemu/server/releases"><img src="https://img.shields.io/github/v/release/eqemu/server" alt="Latest Release"></a>
|
||||
<a href="https://github.com/EQEmu/Server/releases"><img src="https://img.shields.io/github/release-date/EQEmu/Server" alt="Release Date"></a>
|
||||
<img src="https://img.shields.io/github/downloads/eqemu/server/total.svg" alt="Github All Releases"></a>
|
||||
<a href="http://drone.akkadius.com/EQEmu/Server"><img src="http://drone.akkadius.com/api/badges/EQEmu/Server/status.svg" alt="Build Status"></a>
|
||||
<img src="https://img.shields.io/github/issues-pr-closed/eqemu/server" alt="GitHub Issues or Pull Requests">
|
||||
<img src="https://img.shields.io/docker/pulls/akkadius/eqemu-server" alt="Docker Pulls">
|
||||
<a href="http://drone.akkadius.com/EQEmu/Server"><img src="http://drone.akkadius.com/api/badges/EQEmu/Server/status.svg" alt="Build Status"></a> <img src="https://jb.gg/badges/official-plastic.svg" alt="Official">
|
||||
|
||||
</p>
|
||||
|
||||
***
|
||||
|
||||
**EQEmulator is a custom completely from-scratch open source server implementation for EverQuest built mostly on C++**
|
||||
* MySQL/MariaDB is used as the database engine (over 200+ tables)
|
||||
* Perl and LUA are both supported scripting languages for NPC/Player/Quest oriented events
|
||||
* Open source database (Project EQ) has content up to expansion OoW (included in server installs)
|
||||
* Game server environments and databases can be heavily customized to create all new experiences
|
||||
* Hundreds of Quests/events created and maintained by Project EQ
|
||||
<p align="center">
|
||||
EQEmulator is a <b>passion-driven</b>, <b>open source server emulator</b> project dedicated to preserving and celebrating the legacy of a groundbreaking classic MMORPG.
|
||||
</p>
|
||||
|
||||
## Server Installs
|
||||
| |Windows|Linux|
|
||||
|:---:|:---:|:---:|
|
||||
|**Install Count**|||
|
||||
### > Windows
|
||||
<p align="center">
|
||||
For over two decades and continuing, EQEmulator has served as a <strong>fan tribute</strong>, providing tools and technology that allow players to explore, customize, and experience the legendary game's iconic gameplay in new ways. This project exists solely out of <strong>deep admiration</strong> for the original developers, artists, designers, and visionaries who created one of the most influential online worlds of all time.
|
||||
</p>
|
||||
|
||||
* [Install Guide](https://docs.eqemu.io/server/installation/server-installation-windows/)
|
||||
<p align="center">
|
||||
We do not claim ownership of the original game or its assets. <strong>All credit and respect belong to the original creators and Daybreak Game Company</strong>, whose work continues to inspire generations of players and developers alike.
|
||||
</p>
|
||||
|
||||
### > Debian/Ubuntu/CentOS/Fedora
|
||||
<p align="center">
|
||||
EQEmulator has for over 20 years and always will be a <strong>fan-based, non-commercial open-source effort</strong> made by players, for players—preserving the legacy of the game while empowering community-driven creativity, learning and joy that the game and its creators has so strongly inspired in us all.
|
||||
</p>
|
||||
|
||||
* [Install Guide](https://docs.eqemu.io/server/installation/server-installation-linux/)
|
||||
***
|
||||
|
||||
* You can use curl or wget to kick off the installer (whichever your OS has)
|
||||
> curl -O https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/linux_installer/install.sh install.sh && chmod 755 install.sh && ./install.sh
|
||||
<h3 align="center">
|
||||
Technical Overview & Reverse Engineering Effort
|
||||
</h1>
|
||||
|
||||
> wget --no-check-certificate https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/linux_installer/install.sh -O install.sh && chmod 755 install.sh && ./install.sh
|
||||
<p align="center">EQEmulator represents <strong>over two decades of collaborative reverse engineering</strong>, building the server from the ground up without access to the original source code. This effort was achieved entirely through <strong>community-driven analysis, network protocol decoding, and in-game behavioral research</strong>.</p>
|
||||
|
||||
## Supported Clients
|
||||
<h1 align="center">
|
||||
💡 How We Did It
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://github.com/user-attachments/assets/b6b48cf7-f64a-4497-9750-71f442a3d132" height="300px">
|
||||
</p>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<strong>Reverse Engineering</strong>
|
||||
Every system, packet, opcode, and game mechanic has been reconstructed through countless hours of live packet sniffing, client disassembly, and in-game experimentation by dedicated contributors over the years.
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
No proprietary code or server sources were ever used.
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
All implementations are the result of clean-room engineering.
|
||||
</p>
|
||||
|
||||
|
||||
<h1 align="center">
|
||||
🛠️ Technology Stack
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://github.com/user-attachments/assets/df5ea809-86c5-439d-a8fa-651fb04ba477" style="border-radius: 10px">
|
||||
</p>
|
||||
|
||||
**C++ Core Engine**
|
||||
|
||||
* High-performance networking and gameplay logic built in C++
|
||||
* Cross-platform support for Linux and Windows
|
||||
|
||||
**MySQL / MariaDB Backend**
|
||||
|
||||
* Fully structured schema with over 200+ tables
|
||||
* Supports content customization, expansions, and custom worlds
|
||||
|
||||
**Scripting Engine**
|
||||
|
||||
* Native support for **Perl** and **Lua** scripting
|
||||
* Powerfully extendable for quests, NPC behaviors, and custom events
|
||||
|
||||
**Open Source Content Database**
|
||||
|
||||
* Includes ProjectEQ’s world data up through *Dragons of Norrath*
|
||||
* 100% customizable to create entirely new game worlds
|
||||
|
||||
<h1 align="center">
|
||||
🚀 Why It Matters
|
||||
</h1>
|
||||
|
||||
<p align="center">🧬 EQEmulator stands as a <strong>technical preservation project</strong>, ensuring that the magic of classic and custom servers lives on for future generations of players, tinkerers, and game designers.
|
||||
</p>
|
||||
|
||||
> We humbly acknowledge and thank the original developers for creating one of the most influential online experiences in gaming history.
|
||||
|
||||
<h1 align="center">
|
||||
🧑💻🖥️ Supported Clients
|
||||
</h1>
|
||||
|
||||
|Titanium Edition|Secrets of Faydwer|Seeds of Destruction|Underfoot|Rain of Fear|
|
||||
|:---:|:---:|:---:|:---:|:---:|
|
||||
|<img src="http://i.imgur.com/hrwDxoM.jpg" height="150">|<img src="http://i.imgur.com/cRDW5tn.png" height="150">|<img src="http://i.imgur.com/V48kuVn.jpg" height="150">|<img src="http://i.imgur.com/IJQ0XMa.jpg" height="150">|<img src="http://i.imgur.com/OMpHkKa.png" height="100">|
|
||||
|
||||
## Bug Reports <img src="http://i.imgur.com/daf1Vjw.png" height="20">
|
||||
* Please use the [issue tracker](https://github.com/EQEmu/Server/issues) provided by GitHub to send us bug
|
||||
reports or feature requests.
|
||||
* The [EQEmu Forums](http://www.eqemulator.org/forums/) are also a place to submit and get help with bugs.
|
||||
## 📚 Resources
|
||||
|
||||
## Contributions <img src="http://image.flaticon.com/icons/png/512/25/25231.png" width="20">
|
||||
| Resource | Badges | Link |
|
||||
|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
|
||||
| **EQEmulator Docs** | [](https://docs.eqemu.io) | [docs.eqemu.io](https://docs.eqemu.io/) |
|
||||
| **Discord Community**| [](https://discord.gg/QHsm7CD) | [Join Discord](https://discord.gg/QHsm7CD) |
|
||||
| **Latest Release** | [](https://github.com/eqemu/server/releases) <br> [](https://github.com/EQEmu/Server/releases) <br> [](https://github.com/eqemu/server/releases) | [View Releases](https://github.com/eqemu/server/releases) |
|
||||
| **License** | [](./LICENSE) | [View License](./LICENSE) |
|
||||
| **Build Status** | [](http://drone.akkadius.com/EQEmu/Server) | [View Build Status](http://drone.akkadius.com/EQEmu/Server) |
|
||||
| **Docker Pulls** | [](https://hub.docker.com/r/akkadius/eqemu-server) | [Docker Hub](https://hub.docker.com/r/akkadius/eqemu-server) |
|
||||
| **Contributions** | [](https://github.com/eqemu/server/pulls?q=is%3Apr+is%3Aclosed) | [Closed PRs & Issues](https://github.com/eqemu/server/pulls?q=is%3Apr+is%3Aclosed) |
|
||||
|
||||
* The preferred way to contribute is to fork the repo and submit a pull request on
|
||||
GitHub. If you need help with your changes, you can always post on the forums or
|
||||
try Discord. You can also post unified diffs (`git diff` should do the trick) on the
|
||||
[Server Code Submissions](http://www.eqemulator.org/forums/forumdisplay.php?f=669)
|
||||
forum, although pull requests will be much quicker and easier on all parties.
|
||||
## 🛠️ Getting Started
|
||||
|
||||
## Contact <img src="http://gamerescape.com/wp-content/uploads/2015/06/discord.png" height="20">
|
||||
If you want to set up your own EQEmulator server, please refer to the current [server installation guides](https://docs.eqemu.io/#server-installation). We've had 100,000s of players and developers use our guides to set up their own servers, and we hope you will too!
|
||||
|
||||
- Discord Channel: https://discord.gg/QHsm7CD
|
||||
- **User Discord Channel**: `#general`
|
||||
- **Developer Discord Channel**: `#eqemucoders`
|
||||
## 🗂️ Related Repositories
|
||||
|
||||
## Resources
|
||||
- [EQEmulator Forums](http://www.eqemulator.org/forums)
|
||||
- [EQEmulator Wiki](https://docs.eqemu.io/)
|
||||
| Repository | Description |
|
||||
|--------------------|----------------------------------------------------------------------------------|
|
||||
| [ProjectEQ Quests](https://github.com/ProjectEQ/projecteqquests) | Official quests and event scripts for ProjectEQ |
|
||||
| [Maps](https://github.com/Akkadius/EQEmuMaps) | EQEmu-compatible zone maps |
|
||||
| [Installer Resources](https://github.com/Akkadius/EQEmuInstall) | Scripts and assets for setting up EQEmu servers |
|
||||
| [Zone Utilities](https://github.com/EQEmu/zone-utilities) | Utilities for parsing, rendering, and manipulating EQ zone files |
|
||||
|
||||
## Related Repositories
|
||||
* [ProjectEQ Quests](https://github.com/ProjectEQ/projecteqquests)
|
||||
* [Maps](https://github.com/Akkadius/EQEmuMaps)
|
||||
* [Installer Resources](https://github.com/Akkadius/EQEmuInstall)
|
||||
* [Zone Utilities](https://github.com/EQEmu/zone-utilities) - Various utilities and libraries for parsing, rendering and manipulating EQ Zone files.
|
||||
|
||||
## Other License Info
|
||||
|
||||
* The server code and utilities are released under **GPLv3**
|
||||
* We also include some small libraries for convienence that may be under different licensing
|
||||
* SocketLib - GPL LibXML
|
||||
* zlib - zlib license
|
||||
* MariaDB/MySQL - GPL
|
||||
* GPL Perl - GPL / ActiveState (under the assumption that this is a free project)
|
||||
* CPPUnit - GLP StringUtilities - Apache
|
||||
* LUA - MIT
|
||||
|
||||
## Contributors
|
||||
|
||||
<a href="https://github.com/EQEmu/server/graphs/contributors">
|
||||
<img src="https://contributors-img.firebaseapp.com/image?repo=EQEmu/server" />
|
||||
</a>
|
||||
|
||||
|
||||
@@ -38,13 +38,6 @@
|
||||
#include "../../common/skill_caps.h"
|
||||
#include "../../common/evolving_items.h"
|
||||
|
||||
EQEmuLogSys LogSys;
|
||||
WorldContentService content_service;
|
||||
ZoneStore zone_store;
|
||||
PathManager path;
|
||||
PlayerEventLogs player_event_logs;
|
||||
EvolvingItemsManager evolving_items_manager;
|
||||
|
||||
void ExportSpells(SharedDatabase *db);
|
||||
void ExportSkillCaps(SharedDatabase *db);
|
||||
void ExportBaseData(SharedDatabase *db);
|
||||
@@ -53,10 +46,10 @@ void ExportDBStrings(SharedDatabase *db);
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
RegisterExecutablePlatform(ExePlatformClientExport);
|
||||
LogSys.LoadLogSettingsDefaults();
|
||||
EQEmuLogSys::Instance()->LoadLogSettingsDefaults();
|
||||
set_exception_handler();
|
||||
|
||||
path.LoadPaths();
|
||||
PathManager::Instance()->Init();
|
||||
|
||||
LogInfo("Client Files Export Utility");
|
||||
if (!EQEmuConfig::LoadConfig()) {
|
||||
@@ -99,8 +92,8 @@ int main(int argc, char **argv)
|
||||
content_db.SetMySQL(database);
|
||||
}
|
||||
|
||||
LogSys.SetDatabase(&database)
|
||||
->SetLogPath(path.GetLogPath())
|
||||
EQEmuLogSys::Instance()->SetDatabase(&database)
|
||||
->SetLogPath(PathManager::Instance()->GetLogPath())
|
||||
->LoadLogDatabaseSettings()
|
||||
->StartFileLogs();
|
||||
|
||||
@@ -129,14 +122,14 @@ int main(int argc, char **argv)
|
||||
ExportBaseData(&content_db);
|
||||
ExportDBStrings(&database);
|
||||
|
||||
LogSys.CloseFileLogs();
|
||||
EQEmuLogSys::Instance()->CloseFileLogs();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ExportSpells(SharedDatabase* db)
|
||||
{
|
||||
std::ofstream file(fmt::format("{}/export/spells_us.txt", path.GetServerPath()));
|
||||
std::ofstream file(fmt::format("{}/export/spells_us.txt", PathManager::Instance()->GetServerPath()));
|
||||
if (!file || !file.is_open()) {
|
||||
LogError("Unable to open export/spells_us.txt to write, skipping.");
|
||||
return;
|
||||
@@ -155,7 +148,7 @@ void ExportSpells(SharedDatabase* db)
|
||||
|
||||
void ExportSkillCaps(SharedDatabase* db)
|
||||
{
|
||||
std::ofstream file(fmt::format("{}/export/SkillCaps.txt", path.GetServerPath()));
|
||||
std::ofstream file(fmt::format("{}/export/SkillCaps.txt", PathManager::Instance()->GetServerPath()));
|
||||
if (!file || !file.is_open()) {
|
||||
LogError("Unable to open export/SkillCaps.txt to write, skipping.");
|
||||
return;
|
||||
@@ -174,7 +167,7 @@ void ExportSkillCaps(SharedDatabase* db)
|
||||
|
||||
void ExportBaseData(SharedDatabase *db)
|
||||
{
|
||||
std::ofstream file(fmt::format("{}/export/BaseData.txt", path.GetServerPath()));
|
||||
std::ofstream file(fmt::format("{}/export/BaseData.txt", PathManager::Instance()->GetServerPath()));
|
||||
if (!file || !file.is_open()) {
|
||||
LogError("Unable to open export/BaseData.txt to write, skipping.");
|
||||
return;
|
||||
@@ -193,7 +186,7 @@ void ExportBaseData(SharedDatabase *db)
|
||||
|
||||
void ExportDBStrings(SharedDatabase *db)
|
||||
{
|
||||
std::ofstream file(fmt::format("{}/export/dbstr_us.txt", path.GetServerPath()));
|
||||
std::ofstream file(fmt::format("{}/export/dbstr_us.txt", PathManager::Instance()->GetServerPath()));
|
||||
if (!file || !file.is_open()) {
|
||||
LogError("Unable to open export/dbstr_us.txt to write, skipping.");
|
||||
return;
|
||||
|
||||
@@ -32,13 +32,6 @@
|
||||
#include "../../common/events/player_event_logs.h"
|
||||
#include "../../common/evolving_items.h"
|
||||
|
||||
EQEmuLogSys LogSys;
|
||||
WorldContentService content_service;
|
||||
ZoneStore zone_store;
|
||||
PathManager path;
|
||||
PlayerEventLogs player_event_logs;
|
||||
EvolvingItemsManager evolving_items_manager;
|
||||
|
||||
void ImportSpells(SharedDatabase *db);
|
||||
void ImportSkillCaps(SharedDatabase *db);
|
||||
void ImportBaseData(SharedDatabase *db);
|
||||
@@ -46,10 +39,10 @@ void ImportDBStrings(SharedDatabase *db);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
RegisterExecutablePlatform(ExePlatformClientImport);
|
||||
LogSys.LoadLogSettingsDefaults();
|
||||
EQEmuLogSys::Instance()->LoadLogSettingsDefaults();
|
||||
set_exception_handler();
|
||||
|
||||
path.LoadPaths();
|
||||
PathManager::Instance()->Init();
|
||||
|
||||
LogInfo("Client Files Import Utility");
|
||||
if(!EQEmuConfig::LoadConfig()) {
|
||||
@@ -92,8 +85,8 @@ int main(int argc, char **argv) {
|
||||
content_db.SetMySQL(database);
|
||||
}
|
||||
|
||||
LogSys.SetDatabase(&database)
|
||||
->SetLogPath(path.GetLogPath())
|
||||
EQEmuLogSys::Instance()->SetDatabase(&database)
|
||||
->SetLogPath(PathManager::Instance()->GetLogPath())
|
||||
->LoadLogDatabaseSettings()
|
||||
->StartFileLogs();
|
||||
|
||||
@@ -102,7 +95,7 @@ int main(int argc, char **argv) {
|
||||
ImportBaseData(&content_db);
|
||||
ImportDBStrings(&database);
|
||||
|
||||
LogSys.CloseFileLogs();
|
||||
EQEmuLogSys::Instance()->CloseFileLogs();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -138,7 +131,7 @@ bool IsStringField(int i) {
|
||||
|
||||
void ImportSpells(SharedDatabase *db) {
|
||||
LogInfo("Importing Spells");
|
||||
std::string file = fmt::format("{}/import/spells_us.txt", path.GetServerPath());
|
||||
std::string file = fmt::format("{}/import/spells_us.txt", PathManager::Instance()->GetServerPath());
|
||||
FILE *f = fopen(file.c_str(), "r");
|
||||
if(!f) {
|
||||
LogError("Unable to open {} to read, skipping.", file);
|
||||
@@ -228,7 +221,7 @@ void ImportSpells(SharedDatabase *db) {
|
||||
void ImportSkillCaps(SharedDatabase *db) {
|
||||
LogInfo("Importing Skill Caps");
|
||||
|
||||
std::string file = fmt::format("{}/import/SkillCaps.txt", path.GetServerPath());
|
||||
std::string file = fmt::format("{}/import/SkillCaps.txt", PathManager::Instance()->GetServerPath());
|
||||
FILE *f = fopen(file.c_str(), "r");
|
||||
if(!f) {
|
||||
LogError("Unable to open {} to read, skipping.", file);
|
||||
@@ -265,7 +258,7 @@ void ImportBaseData(SharedDatabase *db)
|
||||
{
|
||||
LogInfo("Importing Base Data");
|
||||
|
||||
const std::string& file_name = fmt::format("{}/import/BaseData.txt", path.GetServerPath());
|
||||
const std::string& file_name = fmt::format("{}/import/BaseData.txt", PathManager::Instance()->GetServerPath());
|
||||
|
||||
const auto& file_contents = File::GetContents(file_name);
|
||||
if (!file_contents.error.empty()) {
|
||||
@@ -305,7 +298,7 @@ void ImportBaseData(SharedDatabase *db)
|
||||
void ImportDBStrings(SharedDatabase *db) {
|
||||
LogInfo("Importing DB Strings");
|
||||
|
||||
std::string file = fmt::format("{}/import/dbstr_us.txt", path.GetServerPath());
|
||||
std::string file = fmt::format("{}/import/dbstr_us.txt", PathManager::Instance()->GetServerPath());
|
||||
FILE *f = fopen(file.c_str(), "r");
|
||||
if(!f) {
|
||||
LogError("Unable to open {} to read, skipping.", file);
|
||||
|
||||
@@ -17,6 +17,7 @@ SET(common_sources
|
||||
database.cpp
|
||||
database_instances.cpp
|
||||
database/database_update_manifest.cpp
|
||||
database/database_update_manifest_custom.cpp
|
||||
database/database_update_manifest_bots.cpp
|
||||
database/database_update.cpp
|
||||
dbcore.cpp
|
||||
@@ -671,6 +672,7 @@ SET(common_headers
|
||||
net/console_server_connection.h
|
||||
net/crc32.h
|
||||
net/daybreak_connection.h
|
||||
net/daybreak_pooling.h
|
||||
net/daybreak_structs.h
|
||||
net/dns.h
|
||||
net/endian.h
|
||||
@@ -682,6 +684,7 @@ SET(common_headers
|
||||
net/servertalk_server.h
|
||||
net/servertalk_server_connection.h
|
||||
net/tcp_connection.h
|
||||
net/tcp_connection_pooling.h
|
||||
net/tcp_server.h
|
||||
net/websocket_server.h
|
||||
net/websocket_server_connection.h
|
||||
@@ -742,6 +745,7 @@ SOURCE_GROUP(Net FILES
|
||||
net/crc32.h
|
||||
net/daybreak_connection.cpp
|
||||
net/daybreak_connection.h
|
||||
net/daybreak_pooling.h
|
||||
net/daybreak_structs.h
|
||||
net/dns.h
|
||||
net/endian.h
|
||||
@@ -762,6 +766,7 @@ SOURCE_GROUP(Net FILES
|
||||
net/servertalk_server_connection.h
|
||||
net/tcp_connection.cpp
|
||||
net/tcp_connection.h
|
||||
net/tcp_connection_pooling.h
|
||||
net/tcp_server.cpp
|
||||
net/tcp_server.h
|
||||
net/websocket_server.cpp
|
||||
@@ -836,8 +841,8 @@ IF (UNIX)
|
||||
SET_SOURCE_FILES_PROPERTIES("patches/sod.cpp" "patches/sof.cpp" "patches/rof.cpp" "patches/rof2.cpp" "patches/uf.cpp" PROPERTIES COMPILE_FLAGS -O0)
|
||||
ENDIF (UNIX)
|
||||
|
||||
IF (WIN32 AND EQEMU_BUILD_PCH)
|
||||
TARGET_PRECOMPILE_HEADERS(common PRIVATE pch/pch.h)
|
||||
IF (EQEMU_BUILD_PCH)
|
||||
TARGET_PRECOMPILE_HEADERS(common PRIVATE pch/std-pch.h)
|
||||
ENDIF ()
|
||||
|
||||
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
|
||||
+2
-1
@@ -279,7 +279,8 @@ Bazaar::GetSearchResults(
|
||||
trader_items_ids,
|
||||
std::string(search.item_name),
|
||||
field_criteria_items,
|
||||
where_criteria_items
|
||||
where_criteria_items,
|
||||
search.max_results
|
||||
);
|
||||
|
||||
if (item_results.empty()) {
|
||||
|
||||
@@ -27,7 +27,7 @@ WorldContentService *WorldContentService::SetExpansionContext()
|
||||
// pull expansion from rules
|
||||
int expansion = RuleI(Expansion, CurrentExpansion);
|
||||
if (expansion >= Expansion::Classic && expansion <= Expansion::MaxId) {
|
||||
content_service.SetCurrentExpansion(expansion);
|
||||
WorldContentService::Instance()->SetCurrentExpansion(expansion);
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
@@ -41,12 +41,12 @@ WorldContentService *WorldContentService::SetExpansionContext()
|
||||
|
||||
std::string WorldContentService::GetCurrentExpansionName()
|
||||
{
|
||||
if (content_service.GetCurrentExpansion() == Expansion::EXPANSION_ALL) {
|
||||
if (WorldContentService::Instance()->GetCurrentExpansion() == Expansion::EXPANSION_ALL) {
|
||||
return "All Expansions";
|
||||
}
|
||||
|
||||
if (current_expansion >= Expansion::Classic && current_expansion <= Expansion::MaxId) {
|
||||
return Expansion::ExpansionName[content_service.GetCurrentExpansion()];
|
||||
return Expansion::ExpansionName[WorldContentService::Instance()->GetCurrentExpansion()];
|
||||
}
|
||||
|
||||
return "Unknown Expansion";
|
||||
@@ -185,7 +185,7 @@ void WorldContentService::ReloadContentFlags()
|
||||
|
||||
SetContentFlags(set_content_flags);
|
||||
LoadStaticGlobalZoneInstances();
|
||||
zone_store.LoadZones(*m_content_database);
|
||||
ZoneStore::Instance()->LoadZones(*m_content_database);
|
||||
}
|
||||
|
||||
Database *WorldContentService::GetDatabase() const
|
||||
@@ -291,7 +291,7 @@ WorldContentService *WorldContentService::LoadStaticGlobalZoneInstances()
|
||||
// instance_list table entry for lavastorm has version = 1, is_global = 1, never_expires = 1
|
||||
WorldContentService::FindZoneResult WorldContentService::FindZone(uint32 zone_id, uint32 instance_id)
|
||||
{
|
||||
for (const auto &z: zone_store.GetZones()) {
|
||||
for (const auto &z: ZoneStore::Instance()->GetZones()) {
|
||||
for (auto &i: m_zone_static_instances) {
|
||||
if (
|
||||
z.zoneidnumber == zone_id &&
|
||||
|
||||
@@ -181,6 +181,12 @@ public:
|
||||
FindZoneResult FindZone(uint32 zone_id, uint32 instance_id);
|
||||
bool IsInPublicStaticInstance(uint32 instance_id);
|
||||
|
||||
static WorldContentService* Instance()
|
||||
{
|
||||
static WorldContentService instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
private:
|
||||
int current_expansion{};
|
||||
std::vector<ContentFlagsRepository::ContentFlags> content_flags;
|
||||
@@ -194,6 +200,4 @@ private:
|
||||
std::vector<InstanceListRepository::InstanceList> m_zone_static_instances;
|
||||
};
|
||||
|
||||
extern WorldContentService content_service;
|
||||
|
||||
#endif //EQEMU_WORLD_CONTENT_SERVICE_H
|
||||
|
||||
+7
-5
@@ -27,6 +27,8 @@ void SendCrashReport(const std::string &crash_report)
|
||||
// "http://localhost:3010/api/v1/analytics/server-crash-report", // development
|
||||
};
|
||||
|
||||
EQEmuLogSys* log = EQEmuLogSys::Instance();
|
||||
|
||||
auto config = EQEmuConfig::get();
|
||||
for (auto &e: endpoints) {
|
||||
uri u(e);
|
||||
@@ -68,12 +70,12 @@ void SendCrashReport(const std::string &crash_report)
|
||||
p["cpus"] = cpus.size();
|
||||
p["origination_info"] = "";
|
||||
|
||||
if (!LogSys.origination_info.zone_short_name.empty()) {
|
||||
if (!log->origination_info.zone_short_name.empty()) {
|
||||
p["origination_info"] = fmt::format(
|
||||
"{} ({}) instance_id [{}]",
|
||||
LogSys.origination_info.zone_short_name,
|
||||
LogSys.origination_info.zone_long_name,
|
||||
LogSys.origination_info.instance_id
|
||||
log->origination_info.zone_short_name,
|
||||
log->origination_info.zone_long_name,
|
||||
log->origination_info.instance_id
|
||||
);
|
||||
}
|
||||
|
||||
@@ -294,7 +296,7 @@ void print_trace()
|
||||
SendCrashReport(crash_report);
|
||||
}
|
||||
|
||||
LogSys.CloseFileLogs();
|
||||
EQEmuLogSys::Instance()->CloseFileLogs();
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
#include "data_bucket.h"
|
||||
#include "zonedb.h"
|
||||
#include "mob.h"
|
||||
#include "worldserver.h"
|
||||
#include "../common/data_bucket.h"
|
||||
#include "database.h"
|
||||
#include <ctime>
|
||||
#include <cctype>
|
||||
#include "../common/json/json.hpp"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
extern WorldServer worldserver;
|
||||
const std::string NESTED_KEY_DELIMITER = ".";
|
||||
const std::string NESTED_KEY_DELIMITER = ".";
|
||||
std::vector<DataBucketsRepository::DataBuckets> g_data_bucket_cache = {};
|
||||
|
||||
std::vector<DataBucketsRepository::DataBuckets> g_data_bucket_cache = {};
|
||||
#if defined(ZONE)
|
||||
#include "../zone/zonedb.h"
|
||||
extern ZoneDatabase database;
|
||||
#elif defined(WORLD)
|
||||
#include "../world/worlddb.h"
|
||||
extern WorldDatabase database;
|
||||
#else
|
||||
#error "You must define either ZONE or WORLD"
|
||||
#endif
|
||||
|
||||
void DataBucket::SetData(const std::string &bucket_key, const std::string &bucket_value, std::string expires_time)
|
||||
{
|
||||
@@ -27,7 +33,8 @@ void DataBucket::SetData(const std::string &bucket_key, const std::string &bucke
|
||||
void DataBucket::SetData(const DataBucketKey &k_)
|
||||
{
|
||||
DataBucketKey k = k_; // copy the key so we can modify it
|
||||
if (k.key.find(NESTED_KEY_DELIMITER) != std::string::npos) {
|
||||
bool is_nested = k.key.find(NESTED_KEY_DELIMITER) != std::string::npos;
|
||||
if (is_nested) {
|
||||
k.key = Strings::Split(k.key, NESTED_KEY_DELIMITER).front();
|
||||
}
|
||||
|
||||
@@ -63,6 +70,10 @@ void DataBucket::SetData(const DataBucketKey &k_)
|
||||
if (isalpha(k.expires[0]) || isalpha(k.expires[k.expires.length() - 1])) {
|
||||
expires_time_unix = static_cast<int64>(std::time(nullptr)) + Strings::TimeToSeconds(k.expires);
|
||||
}
|
||||
if (is_nested) {
|
||||
LogDataBuckets("Nested keys can't expire; set expiration on the parent key");
|
||||
expires_time_unix = 0;
|
||||
}
|
||||
}
|
||||
|
||||
b.expires = expires_time_unix;
|
||||
@@ -75,26 +86,45 @@ void DataBucket::SetData(const DataBucketKey &k_)
|
||||
std::string existing_value = r.id > 0 ? r.value : "{}";
|
||||
json json_value = json::object();
|
||||
|
||||
try {
|
||||
json_value = json::parse(existing_value);
|
||||
} catch (json::parse_error &e) {
|
||||
LogError("Failed to parse JSON for key [{}]: {}", k_.key, e.what());
|
||||
json_value = json::object(); // Reset to an empty object on error
|
||||
// Check if the JSON is valid
|
||||
if (Strings::IsValidJson(existing_value)) {
|
||||
try {
|
||||
json_value = json::parse(existing_value);
|
||||
} catch (json::parse_error &e) {
|
||||
LogDataBuckets("Failed to parse JSON for key [{}] [{}]", k_.key, e.what());
|
||||
json_value = json::object(); // Reset to an empty object on error
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively merge new key-value pair into the JSON object
|
||||
auto nested_keys = Strings::Split(k_.key, NESTED_KEY_DELIMITER);
|
||||
auto top_key = nested_keys.front();
|
||||
// remove the top-level key
|
||||
nested_keys.erase(nested_keys.begin());
|
||||
|
||||
json *current = &json_value;
|
||||
|
||||
for (size_t i = 0; i < nested_keys.size(); ++i) {
|
||||
const std::string &key_part = nested_keys[i];
|
||||
|
||||
if (i == nested_keys.size() - 1) {
|
||||
|
||||
LogDataBucketsDetail("Setting key [{}] key_part [{}]", k.key, key_part);
|
||||
|
||||
// If the key already exists and is an object or array, prevent overwriting to avoid data loss
|
||||
if (current->contains(key_part) &&
|
||||
((*current)[key_part].is_object() || (*current)[key_part].is_array())) {
|
||||
LogDataBuckets("Attempted to overwrite an existing object or array at key [{}] - skipping", k_.key);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the value at the final key
|
||||
(*current)[key_part] = k_.value;
|
||||
} else {
|
||||
// Traverse or create nested objects
|
||||
if (!current->contains(key_part)) {
|
||||
(*current)[key_part] = json::object();
|
||||
LogDataBucketsDetail("Creating nested root key [{}] key_part [{}]", k.key, key_part);
|
||||
} else if (!(*current)[key_part].is_object()) {
|
||||
// If key exists but is not an object, reset to object to avoid conflicts
|
||||
(*current)[key_part] = json::object();
|
||||
@@ -105,7 +135,7 @@ void DataBucket::SetData(const DataBucketKey &k_)
|
||||
|
||||
// Serialize JSON back to string
|
||||
b.value = json_value.dump();
|
||||
b.key_ = nested_keys.front(); // Use the top-level key
|
||||
b.key_ = top_key; // Use the top-level key
|
||||
}
|
||||
|
||||
if (bucket_id) {
|
||||
@@ -142,12 +172,20 @@ DataBucketsRepository::DataBuckets DataBucket::ExtractNestedValue(
|
||||
const std::string &full_key)
|
||||
{
|
||||
auto nested_keys = Strings::Split(full_key, NESTED_KEY_DELIMITER);
|
||||
auto top_key = nested_keys.front();
|
||||
nested_keys.erase(nested_keys.begin());
|
||||
json json_value;
|
||||
|
||||
// Check if the JSON is valid
|
||||
if (!Strings::IsValidJson(bucket.value)) {
|
||||
LogDataBuckets("Invalid JSON for key [{}]", bucket.key_);
|
||||
return DataBucketsRepository::NewEntity();
|
||||
}
|
||||
|
||||
try {
|
||||
json_value = json::parse(bucket.value); // Parse the JSON
|
||||
} catch (json::parse_error &ex) {
|
||||
LogError("Failed to parse JSON for key [{}]: {}", bucket.key_, ex.what());
|
||||
LogDataBuckets("Failed to parse JSON for key [{}] [{}]", bucket.key_, ex.what());
|
||||
return DataBucketsRepository::NewEntity(); // Return empty entity on parse error
|
||||
}
|
||||
|
||||
@@ -314,68 +352,120 @@ bool DataBucket::DeleteData(const std::string &bucket_key)
|
||||
return DeleteData(DataBucketKey{.key = bucket_key});
|
||||
}
|
||||
|
||||
// GetDataBuckets bulk loads all data buckets for a mob
|
||||
bool DataBucket::GetDataBuckets(Mob *mob)
|
||||
bool DataBucket::DeleteData(const DataBucketKey &k)
|
||||
{
|
||||
const uint32 id = mob->GetMobTypeIdentifier();
|
||||
bool is_nested_key = k.key.find(NESTED_KEY_DELIMITER) != std::string::npos;
|
||||
|
||||
if (!id) {
|
||||
if (!is_nested_key) {
|
||||
// Update cache
|
||||
if (CanCache(k)) {
|
||||
// delete from cache where contents match
|
||||
g_data_bucket_cache.erase(
|
||||
std::remove_if(
|
||||
g_data_bucket_cache.begin(),
|
||||
g_data_bucket_cache.end(),
|
||||
[&](DataBucketsRepository::DataBuckets &e) {
|
||||
return CheckBucketMatch(e, k);
|
||||
}
|
||||
),
|
||||
g_data_bucket_cache.end()
|
||||
);
|
||||
}
|
||||
|
||||
// Regular key deletion, no nesting involved
|
||||
return DataBucketsRepository::DeleteWhere(
|
||||
database,
|
||||
fmt::format("{} `key` = '{}'", DataBucket::GetScopedDbFilters(k), k.key)
|
||||
);
|
||||
}
|
||||
|
||||
// If it's a nested key, retrieve the top-level JSON object
|
||||
auto top_level_key = Strings::Split(k.key, NESTED_KEY_DELIMITER).front();
|
||||
DataBucketKey top_level_k = k;
|
||||
top_level_k.key = top_level_key;
|
||||
|
||||
auto r = GetData(top_level_k);
|
||||
if (r.id == 0 || r.value.empty() || !Strings::IsValidJson(r.value)) {
|
||||
LogDataBuckets("Attempted to delete nested key [{}] but parent key [{}] does not exist or is invalid JSON", k.key, top_level_key);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mob->IsBot()) {
|
||||
BulkLoadEntitiesToCache(DataBucketLoadType::Bot, {id});
|
||||
json json_value;
|
||||
try {
|
||||
json_value = json::parse(r.value);
|
||||
} catch (json::parse_error &ex) {
|
||||
LogDataBuckets("Failed to parse JSON for key [{}] [{}]", top_level_key, ex.what());
|
||||
return false;
|
||||
}
|
||||
else if (mob->IsClient()) {
|
||||
BulkLoadEntitiesToCache(DataBucketLoadType::Account, {id});
|
||||
BulkLoadEntitiesToCache(DataBucketLoadType::Client, {id});
|
||||
|
||||
// Recursively remove the nested key
|
||||
auto nested_keys = Strings::Split(k.key, NESTED_KEY_DELIMITER);
|
||||
auto top_key = nested_keys.front();
|
||||
nested_keys.erase(nested_keys.begin());
|
||||
json *current = &json_value;
|
||||
|
||||
for (size_t i = 0; i < nested_keys.size(); ++i) {
|
||||
const std::string &key_part = nested_keys[i];
|
||||
|
||||
if (i == nested_keys.size() - 1) {
|
||||
// Last key in the hierarchy - delete it
|
||||
if (current->contains(key_part)) {
|
||||
current->erase(key_part);
|
||||
LogDataBuckets("Deleted nested key [{}] from [{}]", key_part, k.key);
|
||||
} else {
|
||||
LogDataBuckets("Key [{}] not found in JSON - nothing to delete", k.key);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!current->contains(key_part) || !(*current)[key_part].is_object()) {
|
||||
LogDataBuckets("Parent key [{}] does not exist or is not an object", key_part);
|
||||
return false;
|
||||
}
|
||||
current = &(*current)[key_part];
|
||||
}
|
||||
}
|
||||
|
||||
// If the JSON object is now empty, delete the top-level key
|
||||
if (json_value.empty()) {
|
||||
LogDataBuckets("Top-level key [{}] is now empty, deleting entire entry", top_level_key);
|
||||
|
||||
// delete cache
|
||||
if (CanCache(k)) {
|
||||
g_data_bucket_cache.erase(
|
||||
std::remove_if(
|
||||
g_data_bucket_cache.begin(),
|
||||
g_data_bucket_cache.end(),
|
||||
[&](DataBucketsRepository::DataBuckets &e) {
|
||||
return CheckBucketMatch(e, top_level_k);
|
||||
}
|
||||
),
|
||||
g_data_bucket_cache.end()
|
||||
);
|
||||
}
|
||||
|
||||
return DataBucketsRepository::DeleteWhere(
|
||||
database,
|
||||
fmt::format("{} `key` = '{}'", DataBucket::GetScopedDbFilters(k), top_level_key)
|
||||
);
|
||||
}
|
||||
|
||||
// Otherwise, update the existing JSON without the deleted key
|
||||
r.value = json_value.dump();
|
||||
DataBucketsRepository::UpdateOne(database, r);
|
||||
|
||||
// Update cache
|
||||
if (CanCache(k)) {
|
||||
for (auto &e : g_data_bucket_cache) {
|
||||
if (CheckBucketMatch(e, top_level_k)) {
|
||||
e.value = r.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DataBucket::DeleteData(const DataBucketKey &k)
|
||||
{
|
||||
if (CanCache(k)) {
|
||||
size_t size_before = g_data_bucket_cache.size();
|
||||
|
||||
// delete from cache where contents match
|
||||
g_data_bucket_cache.erase(
|
||||
std::remove_if(
|
||||
g_data_bucket_cache.begin(),
|
||||
g_data_bucket_cache.end(),
|
||||
[&](DataBucketsRepository::DataBuckets &e) {
|
||||
return CheckBucketMatch(e, k);
|
||||
}
|
||||
),
|
||||
g_data_bucket_cache.end()
|
||||
);
|
||||
|
||||
LogDataBuckets(
|
||||
"Deleting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}] bot_id [{}] zone_id [{}] instance_id [{}] cache size before [{}] after [{}]",
|
||||
k.key,
|
||||
k.bot_id,
|
||||
k.account_id,
|
||||
k.character_id,
|
||||
k.npc_id,
|
||||
k.bot_id,
|
||||
k.zone_id,
|
||||
k.instance_id,
|
||||
size_before,
|
||||
g_data_bucket_cache.size()
|
||||
);
|
||||
}
|
||||
|
||||
return DataBucketsRepository::DeleteWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"{} `key` = '{}'",
|
||||
DataBucket::GetScopedDbFilters(k),
|
||||
k.key
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
std::string DataBucket::GetDataExpires(const DataBucketKey &k)
|
||||
{
|
||||
LogDataBuckets(
|
||||
@@ -753,4 +843,4 @@ bool DataBucket::CanCache(const DataBucketKey &key)
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,9 @@
|
||||
#define EQEMU_DATABUCKET_H
|
||||
|
||||
#include <string>
|
||||
#include "../common/types.h"
|
||||
#include "../common/repositories/data_buckets_repository.h"
|
||||
#include "mob.h"
|
||||
#include "../common/json/json_archive_single_line.h"
|
||||
#include "../common/servertalk.h"
|
||||
#include "types.h"
|
||||
#include "repositories/data_buckets_repository.h"
|
||||
#include "json/json_archive_single_line.h"
|
||||
|
||||
struct DataBucketKey {
|
||||
std::string key;
|
||||
@@ -46,8 +44,6 @@ public:
|
||||
static std::string GetDataExpires(const std::string &bucket_key);
|
||||
static std::string GetDataRemaining(const std::string &bucket_key);
|
||||
|
||||
static bool GetDataBuckets(Mob *mob);
|
||||
|
||||
// scoped bucket methods
|
||||
static void SetData(const DataBucketKey &k_);
|
||||
static bool DeleteData(const DataBucketKey &k);
|
||||
+77
-8
@@ -708,6 +708,20 @@ const std::string Database::GetNPCNameByID(uint32 npc_id)
|
||||
return e.id ? e.name : std::string();
|
||||
}
|
||||
|
||||
template<typename InputIterator, typename OutputIterator>
|
||||
inline auto CleanMobName(InputIterator first, InputIterator last, OutputIterator result)
|
||||
{
|
||||
for (; first != last; ++first) {
|
||||
if (*first == '_') {
|
||||
*result = ' ';
|
||||
}
|
||||
else if (isalpha(*first) || *first == '`') {
|
||||
*result = *first;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::string Database::GetCleanNPCNameByID(uint32 npc_id)
|
||||
{
|
||||
const auto& e = NpcTypesRepository::FindOne(*this, npc_id);
|
||||
@@ -955,6 +969,29 @@ bool Database::UpdateName(const std::string& old_name, const std::string& new_na
|
||||
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)
|
||||
{
|
||||
if (RuleB(Bots, Enabled)) {
|
||||
@@ -982,6 +1019,20 @@ bool Database::IsNameUsed(const std::string& name)
|
||||
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()
|
||||
{
|
||||
const auto& l = VariablesRepository::GetWhere(*this, "`varname` = 'ServerType' LIMIT 1");
|
||||
@@ -1058,13 +1109,13 @@ void Database::SetLFP(uint32 character_id, bool is_lfp)
|
||||
CharacterDataRepository::UpdateOne(*this, e);
|
||||
}
|
||||
|
||||
void Database::SetLoginFlags(uint32 character_id, bool is_lfp, bool is_lfg, uint8 first_logon)
|
||||
void Database::SetLoginFlags(uint32 character_id, bool is_lfp, bool is_lfg, uint8 ingame)
|
||||
{
|
||||
auto e = CharacterDataRepository::FindOne(*this, character_id);
|
||||
|
||||
e.firstlogon = first_logon;
|
||||
e.lfg = is_lfg ? 1 : 0;
|
||||
e.lfp = is_lfp ? 1 : 0;
|
||||
e.ingame = ingame;
|
||||
e.lfg = is_lfg ? 1 : 0;
|
||||
e.lfp = is_lfp ? 1 : 0;
|
||||
|
||||
CharacterDataRepository::UpdateOne(*this, e);
|
||||
}
|
||||
@@ -1078,11 +1129,11 @@ void Database::SetLFG(uint32 character_id, bool is_lfg)
|
||||
CharacterDataRepository::UpdateOne(*this, e);
|
||||
}
|
||||
|
||||
void Database::SetFirstLogon(uint32 character_id, uint8 first_logon)
|
||||
void Database::SetIngame(uint32 character_id, uint8 ingame)
|
||||
{
|
||||
auto e = CharacterDataRepository::FindOne(*this, character_id);
|
||||
|
||||
e.firstlogon = first_logon;
|
||||
e.ingame = ingame;
|
||||
|
||||
CharacterDataRepository::UpdateOne(*this, e);
|
||||
}
|
||||
@@ -1883,6 +1934,7 @@ bool Database::CopyCharacter(
|
||||
std::vector<std::string> tables_to_zero_id = {
|
||||
"keyring",
|
||||
"data_buckets",
|
||||
"character_evolving_items",
|
||||
"character_instance_safereturns",
|
||||
"character_expedition_lockouts",
|
||||
"character_instance_lockouts",
|
||||
@@ -1914,6 +1966,12 @@ bool Database::CopyCharacter(
|
||||
)
|
||||
);
|
||||
|
||||
if (!results.Success()) {
|
||||
LogError("Transaction failed [{}] rolling back", results.ErrorMessage());
|
||||
TransactionRollback();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> columns = {};
|
||||
int column_count = 0;
|
||||
|
||||
@@ -1932,6 +1990,12 @@ bool Database::CopyCharacter(
|
||||
)
|
||||
);
|
||||
|
||||
if (!results.Success()) {
|
||||
LogError("Transaction failed [{}] rolling back", results.ErrorMessage());
|
||||
TransactionRollback();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::vector<std::string>> new_rows;
|
||||
|
||||
for (auto row : results) {
|
||||
@@ -1999,13 +2063,18 @@ bool Database::CopyCharacter(
|
||||
LogInfo("Copying table [{}] rows [{}]", table_name, Strings::Commify(rows_copied));
|
||||
|
||||
if (!insert.ErrorMessage().empty()) {
|
||||
LogError("Error copying table [{}] [{}]", table_name, insert.ErrorMessage());
|
||||
TransactionRollback();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TransactionCommit();
|
||||
auto r = TransactionCommit();
|
||||
if (!r.Success()) {
|
||||
LogError("Transaction failed [{}] rolling back", r.ErrorMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
"Character [{}] copied to [{}] total rows [{}]",
|
||||
@@ -2175,7 +2244,7 @@ void Database::PurgeCharacterParcels()
|
||||
pel.event_data = ss.str();
|
||||
pel.created_at = std::time(nullptr);
|
||||
|
||||
player_event_logs.AddToQueue(pel);
|
||||
PlayerEventLogs::Instance()->AddToQueue(pel);
|
||||
|
||||
ss.str("");
|
||||
ss.clear();
|
||||
|
||||
+4
-1
@@ -103,6 +103,7 @@ public:
|
||||
bool ReserveName(uint32 account_id, const std::string& name);
|
||||
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp);
|
||||
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(
|
||||
const std::string& source_character_name,
|
||||
const std::string& destination_character_name,
|
||||
@@ -116,6 +117,7 @@ public:
|
||||
bool CheckGMIPs(const std::string& login_ip, uint32 account_id);
|
||||
bool CheckNameFilter(const std::string& name, bool surname = false);
|
||||
bool IsNameUsed(const std::string& name);
|
||||
bool IsPetNameUsed(const std::string& name);
|
||||
|
||||
uint32 GetAccountIDByChar(const std::string& name, uint32* character_id = 0);
|
||||
uint32 GetAccountIDByChar(uint32 character_id);
|
||||
@@ -139,6 +141,7 @@ public:
|
||||
bool CheckInstanceExpired(uint16 instance_id);
|
||||
bool CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration);
|
||||
bool GetUnusedInstanceID(uint16& instance_id);
|
||||
bool TryGetUnusedInstanceID(uint16& instance_id);
|
||||
bool IsGlobalInstance(uint16 instance_id);
|
||||
bool RemoveClientFromInstance(uint16 instance_id, uint32 char_id);
|
||||
bool RemoveClientsFromInstance(uint16 instance_id);
|
||||
@@ -260,7 +263,7 @@ public:
|
||||
bool SaveTime(int8 minute, int8 hour, int8 day, int8 month, int16 year);
|
||||
void ClearMerchantTemp();
|
||||
void ClearPTimers(uint32 character_id);
|
||||
void SetFirstLogon(uint32 character_id, uint8 first_logon);
|
||||
void SetIngame(uint32 character_id, uint8 ingame);
|
||||
void SetLFG(uint32 character_id, bool is_lfg);
|
||||
void SetLFP(uint32 character_id, bool is_lfp);
|
||||
void SetLoginFlags(uint32 character_id, bool is_lfp, bool is_lfg, uint8 first_logon);
|
||||
|
||||
@@ -50,7 +50,7 @@ bool DatabaseDumpService::IsMySQLInstalled()
|
||||
{
|
||||
std::string version_output = GetMySQLVersion();
|
||||
|
||||
return version_output.find("mysql") != std::string::npos && version_output.find("Ver") != std::string::npos;
|
||||
return version_output.find("mysql") != std::string::npos && (version_output.find("Ver") != std::string::npos || version_output.find("from") != std::string::npos);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,7 +204,7 @@ void DatabaseDumpService::DatabaseDump()
|
||||
}
|
||||
|
||||
if (IsDumpOutputToConsole()) {
|
||||
LogSys.SilenceConsoleLogging();
|
||||
EQEmuLogSys::Instance()->SilenceConsoleLogging();
|
||||
}
|
||||
|
||||
LogInfo("MySQL installed [{}]", GetMySQLVersion());
|
||||
@@ -324,7 +324,7 @@ void DatabaseDumpService::DatabaseDump()
|
||||
}
|
||||
|
||||
if (!IsDumpOutputToConsole()) {
|
||||
LogSys.LoadLogSettingsDefaults();
|
||||
EQEmuLogSys::Instance()->LoadLogSettingsDefaults();
|
||||
}
|
||||
|
||||
if (!pipe_file.empty()) {
|
||||
|
||||
@@ -65,7 +65,6 @@ private:
|
||||
bool dump_system_tables = false;
|
||||
bool dump_content_tables = false;
|
||||
bool dump_player_tables = false;
|
||||
bool dump_query_server_tables = false;
|
||||
bool dump_login_server_tables = false;
|
||||
bool dump_with_no_data = false;
|
||||
bool dump_table_lock = false;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "../http/httplib.h"
|
||||
|
||||
#include "database_update_manifest.cpp"
|
||||
#include "database_update_manifest_custom.cpp"
|
||||
#include "database_update_manifest_bots.cpp"
|
||||
#include "database_dump_service.h"
|
||||
|
||||
@@ -14,7 +15,7 @@ constexpr int BREAK_LENGTH = 70;
|
||||
|
||||
DatabaseVersion DatabaseUpdate::GetDatabaseVersions()
|
||||
{
|
||||
auto results = m_database->QueryDatabase("SELECT `version`, `bots_version` FROM `db_version` LIMIT 1");
|
||||
auto results = m_database->QueryDatabase("SELECT `version`, `bots_version`, `custom_version` FROM `db_version` LIMIT 1");
|
||||
if (!results.Success() || !results.RowCount()) {
|
||||
LogError("Failed to read from [db_version] table!");
|
||||
return DatabaseVersion{};
|
||||
@@ -25,6 +26,7 @@ DatabaseVersion DatabaseUpdate::GetDatabaseVersions()
|
||||
return DatabaseVersion{
|
||||
.server_database_version = Strings::ToInt(r[0]),
|
||||
.bots_database_version = Strings::ToInt(r[1]),
|
||||
.custom_database_version = Strings::ToInt(r[2]),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -33,6 +35,7 @@ DatabaseVersion DatabaseUpdate::GetBinaryDatabaseVersions()
|
||||
return DatabaseVersion{
|
||||
.server_database_version = CURRENT_BINARY_DATABASE_VERSION,
|
||||
.bots_database_version = (RuleB(Bots, Enabled) ? CURRENT_BINARY_BOTS_DATABASE_VERSION : 0),
|
||||
.custom_database_version = CUSTOM_BINARY_DATABASE_VERSION,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,6 +46,7 @@ constexpr int LOOK_BACK_AMOUNT = 10;
|
||||
// this check will take action
|
||||
void DatabaseUpdate::CheckDbUpdates()
|
||||
{
|
||||
InjectCustomVersionColumn();
|
||||
InjectBotsVersionColumn();
|
||||
auto v = GetDatabaseVersions();
|
||||
auto b = GetBinaryDatabaseVersions();
|
||||
@@ -59,6 +63,15 @@ void DatabaseUpdate::CheckDbUpdates()
|
||||
m_database->QueryDatabase(fmt::format("UPDATE `db_version` SET `version` = {}", b.server_database_version));
|
||||
}
|
||||
|
||||
if (UpdateManifest(manifest_entries_custom, v.custom_database_version, b.custom_database_version)) {
|
||||
LogInfo(
|
||||
"Updates ran successfully, setting database version to [{}] from [{}]",
|
||||
b.custom_database_version,
|
||||
v.custom_database_version
|
||||
);
|
||||
m_database->QueryDatabase(fmt::format("UPDATE `db_version` SET `custom_version` = {}", b.custom_database_version));
|
||||
}
|
||||
|
||||
if (b.bots_database_version > 0) {
|
||||
if (UpdateManifest(bot_manifest_entries, v.bots_database_version, b.bots_database_version)) {
|
||||
LogInfo(
|
||||
@@ -141,7 +154,7 @@ bool DatabaseUpdate::UpdateManifest(
|
||||
std::vector<int> missing_migrations = {};
|
||||
if (version_low != version_high) {
|
||||
|
||||
LogSys.DisableMySQLErrorLogs();
|
||||
EQEmuLogSys::Instance()->DisableMySQLErrorLogs();
|
||||
bool force_interactive = false;
|
||||
for (int version = version_low + 1; version <= version_high; ++version) {
|
||||
for (auto &e: entries) {
|
||||
@@ -171,7 +184,7 @@ bool DatabaseUpdate::UpdateManifest(
|
||||
}
|
||||
}
|
||||
}
|
||||
LogSys.EnableMySQLErrorLogs();
|
||||
EQEmuLogSys::Instance()->EnableMySQLErrorLogs();
|
||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||
|
||||
if (!missing_migrations.empty() && m_skip_backup) {
|
||||
@@ -344,6 +357,16 @@ bool DatabaseUpdate::CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b)
|
||||
);
|
||||
}
|
||||
|
||||
if (b.custom_database_version > 0) {
|
||||
LogInfo(
|
||||
"{:>8} | database [{}] binary [{}] {}",
|
||||
"Custom",
|
||||
v.custom_database_version,
|
||||
b.custom_database_version,
|
||||
(v.custom_database_version == b.custom_database_version) ? "up to date" : "checking updates"
|
||||
);
|
||||
}
|
||||
|
||||
LogInfo("{:>8} | [server.auto_database_updates] [<green>true]", "Config");
|
||||
|
||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||
@@ -353,7 +376,10 @@ bool DatabaseUpdate::CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b)
|
||||
// bots database version is optional, if not enabled then it is always up-to-date
|
||||
bool bots_up_to_date = RuleB(Bots, Enabled) ? v.bots_database_version >= b.bots_database_version : true;
|
||||
|
||||
return server_up_to_date && bots_up_to_date;
|
||||
// custom database version is optional, if not enabled then it is always up-to-date
|
||||
bool custom_up_to_date = v.custom_database_version >= b.custom_database_version;
|
||||
|
||||
return server_up_to_date && bots_up_to_date && custom_up_to_date;
|
||||
}
|
||||
|
||||
// checks to see if there are pending updates
|
||||
@@ -373,3 +399,12 @@ void DatabaseUpdate::InjectBotsVersionColumn()
|
||||
m_database->QueryDatabase("ALTER TABLE db_version ADD bots_version int(11) DEFAULT '0' AFTER version");
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseUpdate::InjectCustomVersionColumn()
|
||||
{
|
||||
auto results = m_database->QueryDatabase("SHOW COLUMNS FROM `db_version` LIKE 'custom_version'");
|
||||
if (!results.Success() || results.RowCount() == 0) {
|
||||
LogInfo("Adding custom_version column to db_version table");
|
||||
m_database->QueryDatabase("ALTER TABLE `db_version` ADD COLUMN `custom_version` INT(11) UNSIGNED NOT NULL DEFAULT 0");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ struct ManifestEntry {
|
||||
struct DatabaseVersion {
|
||||
int server_database_version;
|
||||
int bots_database_version;
|
||||
int custom_database_version;
|
||||
};
|
||||
|
||||
class DatabaseUpdate {
|
||||
@@ -32,12 +33,20 @@ public:
|
||||
DatabaseUpdate *SetContentDatabase(Database *db);
|
||||
DatabaseUpdate *SetSkipBackup(bool skip);
|
||||
bool HasPendingUpdates();
|
||||
|
||||
static DatabaseUpdate* Instance()
|
||||
{
|
||||
static DatabaseUpdate instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_skip_backup = false;
|
||||
Database *m_database;
|
||||
Database *m_content_database;
|
||||
static bool CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b);
|
||||
void InjectBotsVersionColumn();
|
||||
void InjectCustomVersionColumn();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -6792,7 +6792,7 @@ UPDATE `character_corpse_items` SET `equip_slot` = ((`equip_slot` - 341) + 5810)
|
||||
},
|
||||
ManifestEntry{
|
||||
.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'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
@@ -6845,7 +6845,7 @@ RENAME TABLE `expedition_lockouts` TO `dynamic_zone_lockouts`;
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
-- ✅ Drop old indexes
|
||||
-- Drop old indexes if exists
|
||||
DROP INDEX IF EXISTS `keys` ON `data_buckets`;
|
||||
DROP INDEX IF EXISTS `idx_npc_expires` ON `data_buckets`;
|
||||
DROP INDEX IF EXISTS `idx_bot_expires` ON `data_buckets`;
|
||||
@@ -6863,10 +6863,10 @@ ALTER TABLE `data_buckets`
|
||||
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`;
|
||||
|
||||
-- ✅ 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 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);
|
||||
)",
|
||||
.content_schema_update = false
|
||||
@@ -6914,7 +6914,7 @@ CREATE TABLE `zone_state_spawns` (
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9308,
|
||||
.description = "2025_add_multivalue_support_to_evolving_subtype.sql",
|
||||
.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)",
|
||||
@@ -6938,6 +6938,204 @@ CREATE TABLE `character_pet_name` (
|
||||
) 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
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9322,
|
||||
.description = "2025_04_24_add_npc_tint_id.sql",
|
||||
.check = "SHOW COLUMNS FROM `npc_types` LIKE 'npc_tint_id'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `npc_types`
|
||||
ADD COLUMN `npc_tint_id` SMALLINT UNSIGNED NULL DEFAULT '0' AFTER `multiquest_enabled`;
|
||||
)",
|
||||
.content_schema_update = true
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9323,
|
||||
.description = "2025_04_16_character_data_first_login.sql",
|
||||
.check = "SHOW COLUMNS FROM `character_data` LIKE 'first_login'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `character_data`
|
||||
CHANGE COLUMN `firstlogon` `ingame` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 AFTER `xtargets`,
|
||||
ADD COLUMN `first_login` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `xtargets`;
|
||||
)",
|
||||
.content_schema_update = false
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9324,
|
||||
.description = "2025_06_11_player_event_logs_table.sql",
|
||||
.check = "SHOW CREATE TABLE `player_event_logs`",
|
||||
.condition = "missing",
|
||||
.match = "COMPRESS",
|
||||
.sql = R"(
|
||||
ALTER TABLE player_event_logs ROW_FORMAT=COMPRESSED;
|
||||
CREATE INDEX idx_event_type_char_id ON player_event_logs (event_type_id, character_id);
|
||||
)",
|
||||
.content_schema_update = false
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9325,
|
||||
.description = "2025_06_10_character_corpses_entity_variables.sql",
|
||||
.check = "SHOW COLUMNS FROM `character_corpses` LIKE 'entity_variables'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `character_corpses`
|
||||
ADD COLUMN `entity_variables` TEXT DEFAULT NULL AFTER `rezzable`;
|
||||
)",
|
||||
.content_schema_update = false
|
||||
},
|
||||
// -- template; copy/paste this when you need to create a new entry
|
||||
// ManifestEntry{
|
||||
// .version = 9228,
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "database_update.h"
|
||||
|
||||
std::vector<ManifestEntry> manifest_entries_custom = {
|
||||
ManifestEntry{
|
||||
.version = 1,
|
||||
.description = "2025_05_16_new_database_check_test",
|
||||
.check = "SHOW TABLES LIKE 'new_table'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
CREATE TABLE `new_table` (
|
||||
`id` int NOT NULL AUTO_INCREMENT,
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
)",
|
||||
.content_schema_update = false,
|
||||
},
|
||||
// Used for testing
|
||||
// ManifestEntry{
|
||||
// .version = 9229,
|
||||
// .description = "new_database_check_test",
|
||||
// .check = "SHOW TABLES LIKE 'new_table'",
|
||||
// .condition = "empty",
|
||||
// .match = "",
|
||||
// .sql = R"(
|
||||
//CREATE TABLE `new_table` (
|
||||
// `id` int NOT NULL AUTO_INCREMENT,
|
||||
// PRIMARY KEY (`id`)
|
||||
//);
|
||||
//CREATE TABLE `new_table1` (
|
||||
// `id` int NOT NULL AUTO_INCREMENT,
|
||||
// PRIMARY KEY (`id`)
|
||||
//);
|
||||
//CREATE TABLE `new_table2` (
|
||||
// `id` int NOT NULL AUTO_INCREMENT,
|
||||
// PRIMARY KEY (`id`)
|
||||
//);
|
||||
//CREATE TABLE `new_table3` (
|
||||
// `id` int NOT NULL AUTO_INCREMENT,
|
||||
// PRIMARY KEY (`id`)
|
||||
//);
|
||||
//)",
|
||||
// }
|
||||
|
||||
};
|
||||
|
||||
// see struct definitions for what each field does
|
||||
// struct ManifestEntry {
|
||||
// int version{}; // database version of the migration
|
||||
// std::string description{}; // description of the migration ex: "add_new_table" or "add_index_to_table"
|
||||
// std::string check{}; // query that checks against the condition
|
||||
// std::string condition{}; // condition or "match_type" - Possible values [contains|match|missing|empty|not_empty]
|
||||
// std::string match{}; // match field that is not always used, but works in conjunction with "condition" values [missing|match|contains]
|
||||
// std::string sql{}; // the SQL DDL that gets ran when the condition is true
|
||||
// };
|
||||
@@ -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 "repositories/spawn2_disabled_repository.h"
|
||||
#include "repositories/data_buckets_repository.h"
|
||||
|
||||
#include "repositories/zone_state_spawns_repository.h"
|
||||
#include "database.h"
|
||||
|
||||
#include <iomanip>
|
||||
@@ -128,11 +128,35 @@ bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version
|
||||
e.version = version;
|
||||
e.start_time = std::time(nullptr);
|
||||
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)
|
||||
{
|
||||
// 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_instance_id = 32000;
|
||||
@@ -480,6 +504,9 @@ void Database::DeleteInstance(uint16 instance_id)
|
||||
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
|
||||
CharacterCorpsesRepository::BuryInstance(*this, 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)
|
||||
@@ -534,14 +561,12 @@ void Database::GetCharactersInInstance(uint16 instance_id, std::list<uint32> &ch
|
||||
|
||||
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(
|
||||
*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()) {
|
||||
return;
|
||||
@@ -552,17 +577,24 @@ void Database::PurgeExpiredInstances()
|
||||
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));
|
||||
InstanceListPlayerRepository::DeleteWhere(*this, fmt::format("id IN ({})", imploded_instance_ids));
|
||||
RespawnTimesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
||||
SpawnConditionValuesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
||||
CharacterCorpsesRepository::BuryInstances(*this, imploded_instance_ids);
|
||||
DynamicZoneMembersRepository::DeleteByManyInstances(*this, imploded_instance_ids);
|
||||
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
||||
Spawn2DisabledRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
||||
DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id != 0 and instance_id IN ({})", imploded_instance_ids));
|
||||
TransactionBegin();
|
||||
InstanceListRepository::DeleteWhere(*this, fmt::format("id IN ({})", ids));
|
||||
InstanceListPlayerRepository::DeleteWhere(*this, fmt::format("id IN ({})", ids));
|
||||
RespawnTimesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
|
||||
SpawnConditionValuesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
|
||||
CharacterCorpsesRepository::BuryInstances(*this, ids);
|
||||
DynamicZoneMembersRepository::DeleteByManyInstances(*this, 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)
|
||||
@@ -574,6 +606,7 @@ void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
|
||||
|
||||
i.start_time = std::time(nullptr);
|
||||
i.duration = new_duration;
|
||||
i.expire_at = i.start_time + i.duration;
|
||||
|
||||
InstanceListRepository::UpdateOne(*this, i);
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace DatabaseSchema {
|
||||
{"guild_members", "char_id"},
|
||||
{"guilds", "id"},
|
||||
{"instance_list_player", "id"},
|
||||
{"inventory", "charid"},
|
||||
{"inventory", "character_id"},
|
||||
{"inventory_snapshots", "charid"},
|
||||
{"keyring", "char_id"},
|
||||
{"mail", "charid"},
|
||||
|
||||
+3
-3
@@ -160,7 +160,7 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
|
||||
(uint32) mysql_insert_id(mysql)
|
||||
);
|
||||
|
||||
if (LogSys.log_settings[Logs::MySQLQuery].is_category_enabled == 1) {
|
||||
if (EQEmuLogSys::Instance()->log_settings[Logs::MySQLQuery].is_category_enabled == 1) {
|
||||
if ((strncasecmp(query, "select", 6) == 0)) {
|
||||
LogMySQLQuery(
|
||||
"{0} -- ({1} row{2} returned) ({3}s)",
|
||||
@@ -189,9 +189,9 @@ void DBcore::TransactionBegin()
|
||||
QueryDatabase("START TRANSACTION");
|
||||
}
|
||||
|
||||
void DBcore::TransactionCommit()
|
||||
MySQLRequestResult DBcore::TransactionCommit()
|
||||
{
|
||||
QueryDatabase("COMMIT");
|
||||
return QueryDatabase("COMMIT");
|
||||
}
|
||||
|
||||
void DBcore::TransactionRollback()
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ public:
|
||||
MySQLRequestResult QueryDatabase(const std::string& query, bool retryOnFailureOnce = true);
|
||||
MySQLRequestResult QueryDatabaseMulti(const std::string &query);
|
||||
void TransactionBegin();
|
||||
void TransactionCommit();
|
||||
MySQLRequestResult TransactionCommit();
|
||||
void TransactionRollback();
|
||||
std::string Escape(const std::string& s);
|
||||
uint32 DoEscapeString(char *tobuf, const char *frombuf, uint32 fromlen);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <string>
|
||||
#include "../types.h"
|
||||
#include "../http/httplib.h"
|
||||
#include "../repositories/player_event_logs_repository.h"
|
||||
#include "../events/player_events.h"
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ void DiscordManager::ProcessMessageQueue()
|
||||
continue;
|
||||
}
|
||||
|
||||
auto webhook = LogSys.GetDiscordWebhooks()[q.first];
|
||||
auto webhook = EQEmuLogSys::Instance()->GetDiscordWebhooks()[q.first];
|
||||
std::string message;
|
||||
|
||||
for (auto &m: q.second) {
|
||||
@@ -68,7 +68,7 @@ void DiscordManager::ProcessMessageQueue()
|
||||
|
||||
void DiscordManager::QueuePlayerEventMessage(const PlayerEvent::PlayerEventContainer& e)
|
||||
{
|
||||
auto w = player_event_logs.GetDiscordWebhookUrlFromEventType(e.player_event_log.event_type_id);
|
||||
auto w = PlayerEventLogs::Instance()->GetDiscordWebhookUrlFromEventType(e.player_event_log.event_type_id);
|
||||
if (!w.empty()) {
|
||||
Discord::SendPlayerEventMessage(e, w);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,12 @@ public:
|
||||
void QueueWebhookMessage(uint32 webhook_id, const std::string& message);
|
||||
void ProcessMessageQueue();
|
||||
void QueuePlayerEventMessage(const PlayerEvent::PlayerEventContainer& e);
|
||||
|
||||
static DiscordManager* Instance()
|
||||
{
|
||||
static DiscordManager instance;
|
||||
return &instance;
|
||||
}
|
||||
private:
|
||||
std::mutex webhook_queue_lock{};
|
||||
std::map<uint32, std::vector<std::string>> webhook_message_queue{};
|
||||
|
||||
@@ -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.duration = static_cast<int>(m_duration.count());
|
||||
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);
|
||||
if (instance.id == 0)
|
||||
auto instance = InstanceListRepository::ReplaceOne(GetDatabase(), insert_instance);
|
||||
if (!instance)
|
||||
{
|
||||
LogDynamicZones("Failed to create instance [{}] for zone [{}]", unused_instance_id, m_zone_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_instance_id = instance.id;
|
||||
m_instance_id = unused_instance_id;
|
||||
|
||||
return m_instance_id;
|
||||
}
|
||||
|
||||
@@ -287,6 +287,8 @@ N(OP_InstillDoubt),
|
||||
N(OP_InterruptCast),
|
||||
N(OP_InvokeChangePetName),
|
||||
N(OP_InvokeChangePetNameImmediate),
|
||||
N(OP_InvokeNameChangeImmediate),
|
||||
N(OP_InvokeNameChangeLazy),
|
||||
N(OP_ItemLinkClick),
|
||||
N(OP_ItemLinkResponse),
|
||||
N(OP_ItemLinkText),
|
||||
|
||||
@@ -324,6 +324,8 @@ union
|
||||
bool guild_show;
|
||||
bool trader;
|
||||
bool buyer;
|
||||
bool untargetable;
|
||||
uint32 npc_tint_id;
|
||||
};
|
||||
|
||||
struct PlayerState_Struct {
|
||||
@@ -5832,21 +5834,28 @@ struct ChangeSize_Struct
|
||||
/*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 {
|
||||
/*00*/ char new_pet_name[64];
|
||||
/*40*/ char pet_owner_name[64];
|
||||
/*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+
|
||||
struct GroupMakeLeader_Struct
|
||||
{
|
||||
|
||||
@@ -545,13 +545,13 @@ void EQStream::SendPacket(uint16 opcode, EQApplicationPacket *p)
|
||||
uint32 chunksize, used;
|
||||
uint32 length;
|
||||
|
||||
if (LogSys.log_settings[Logs::Server_Client_Packet].is_category_enabled == 1){
|
||||
if (EQEmuLogSys::Instance()->log_settings[Logs::Server_Client_Packet].is_category_enabled == 1){
|
||||
if (p->GetOpcode() != OP_SpecialMesg){
|
||||
Log(Logs::General, Logs::Server_Client_Packet, "[%s - 0x%04x] [Size: %u]", OpcodeManager::EmuToName(p->GetOpcode()), p->GetOpcode(), p->Size());
|
||||
}
|
||||
}
|
||||
|
||||
if (LogSys.log_settings[Logs::Server_Client_Packet_With_Dump].is_category_enabled == 1){
|
||||
if (EQEmuLogSys::Instance()->log_settings[Logs::Server_Client_Packet_With_Dump].is_category_enabled == 1){
|
||||
if (p->GetOpcode() != OP_SpecialMesg){
|
||||
Log(Logs::General, Logs::Server_Client_Packet_With_Dump, "[%s - 0x%04x] [Size: %u] %s", OpcodeManager::EmuToName(p->GetOpcode()), p->GetOpcode(), p->Size(), DumpPacketToString(p).c_str());
|
||||
}
|
||||
|
||||
+17
-2
@@ -177,6 +177,21 @@ void EQEmuConfig::parse_config()
|
||||
SharedMemDir = _root["server"]["directories"].get("shared_memory", "shared/").asString();
|
||||
LogDir = _root["server"]["directories"].get("logs", "logs/").asString();
|
||||
|
||||
auto load_paths = [&](const std::string& key, std::vector<std::string>& target) {
|
||||
const auto& paths = _root["server"]["directories"][key];
|
||||
if (paths.isArray()) {
|
||||
for (const auto& dir : paths) {
|
||||
if (dir.isString()) {
|
||||
target.push_back(dir.asString());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
load_paths("quest_paths", m_quest_directories);
|
||||
load_paths("plugin_paths", m_plugin_directories);
|
||||
load_paths("lua_module_paths", m_lua_module_directories);
|
||||
|
||||
/**
|
||||
* Logs
|
||||
*/
|
||||
@@ -421,11 +436,11 @@ void EQEmuConfig::CheckUcsConfigConversion()
|
||||
LogInfo("Migrating old [eqemu_config] UCS configuration to new configuration");
|
||||
|
||||
std::string config_file_path = std::filesystem::path{
|
||||
path.GetServerPath() + "/eqemu_config.json"
|
||||
PathManager::Instance()->GetServerPath() + "/eqemu_config.json"
|
||||
}.string();
|
||||
|
||||
std::string config_file_bak_path = std::filesystem::path{
|
||||
path.GetServerPath() + "/eqemu_config.ucs-migrate-json.bak"
|
||||
PathManager::Instance()->GetServerPath() + "/eqemu_config.ucs-migrate-json.bak"
|
||||
}.string();
|
||||
|
||||
// copy eqemu_config.json to eqemu_config.json.bak
|
||||
|
||||
+22
-1
@@ -120,6 +120,22 @@ class EQEmuConfig
|
||||
const std::string &GetUCSHost() const;
|
||||
uint16 GetUCSPort() const;
|
||||
|
||||
std::vector<std::string> GetQuestDirectories() const
|
||||
{
|
||||
return m_quest_directories;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetPluginsDirectories() const
|
||||
{
|
||||
return m_plugin_directories;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetLuaModuleDirectories() const
|
||||
{
|
||||
return m_lua_module_directories;
|
||||
}
|
||||
|
||||
|
||||
// uint16 DynamicCount;
|
||||
|
||||
// map<string,uint16> StaticZones;
|
||||
@@ -133,6 +149,11 @@ class EQEmuConfig
|
||||
Json::Value _root;
|
||||
static std::string ConfigFile;
|
||||
|
||||
std::vector<std::string> m_quest_directories = {};
|
||||
std::vector<std::string> m_plugin_directories = {};
|
||||
std::vector<std::string> m_lua_module_directories = {};
|
||||
|
||||
protected:
|
||||
void parse_config();
|
||||
|
||||
EQEmuConfig()
|
||||
@@ -170,7 +191,7 @@ class EQEmuConfig
|
||||
|
||||
std::string file = fmt::format(
|
||||
"{}/{}",
|
||||
(file_path.empty() ? path.GetServerPath() : file_path),
|
||||
(file_path.empty() ? PathManager::Instance()->GetServerPath() : file_path),
|
||||
EQEmuConfig::ConfigFile
|
||||
);
|
||||
|
||||
|
||||
+40
-13
@@ -537,9 +537,9 @@ void EQEmuLogSys::StartFileLogs(const std::string &log_name)
|
||||
{
|
||||
EQEmuLogSys::CloseFileLogs();
|
||||
|
||||
if (!File::Exists(path.GetLogPath())) {
|
||||
LogInfo("Logs directory not found, creating [{}]", path.GetLogPath());
|
||||
File::Makedir(path.GetLogPath());
|
||||
if (!File::Exists(PathManager::Instance()->GetLogPath())) {
|
||||
LogInfo("Logs directory not found, creating [{}]", PathManager::Instance()->GetLogPath());
|
||||
File::Makedir(PathManager::Instance()->GetLogPath());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -614,7 +614,7 @@ void EQEmuLogSys::EnableConsoleLogging()
|
||||
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();
|
||||
|
||||
@@ -656,7 +656,7 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
||||
// If we go through this whole loop and nothing is set to any debug level, there
|
||||
// is no point to create a file or keep anything open
|
||||
if (log_settings[c.log_category_id].log_to_file > 0) {
|
||||
LogSys.m_file_logs_enabled = true;
|
||||
m_file_logs_enabled = true;
|
||||
}
|
||||
|
||||
db_categories.emplace_back(c.log_category_id);
|
||||
@@ -682,14 +682,33 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
||||
if (is_missing_in_database && !is_deprecated_category) {
|
||||
LogInfo("Automatically adding new log category [{}] ({})", Logs::LogCategoryName[i], i);
|
||||
|
||||
auto new_category = LogsysCategoriesRepository::NewEntity();
|
||||
new_category.log_category_id = i;
|
||||
new_category.log_category_description = Strings::Escape(Logs::LogCategoryName[i]);
|
||||
new_category.log_to_console = log_settings[i].log_to_console;
|
||||
new_category.log_to_gmsay = log_settings[i].log_to_gmsay;
|
||||
new_category.log_to_file = log_settings[i].log_to_file;
|
||||
new_category.log_to_discord = log_settings[i].log_to_discord;
|
||||
db_categories_to_add.emplace_back(new_category);
|
||||
auto e = LogsysCategoriesRepository::NewEntity();
|
||||
e.log_category_id = i;
|
||||
e.log_category_description = Strings::Escape(Logs::LogCategoryName[i]);
|
||||
e.log_to_console = log_settings[i].log_to_console;
|
||||
e.log_to_gmsay = log_settings[i].log_to_gmsay;
|
||||
e.log_to_file = log_settings[i].log_to_file;
|
||||
e.log_to_discord = log_settings[i].log_to_discord;
|
||||
db_categories_to_add.emplace_back(e);
|
||||
}
|
||||
|
||||
// look to see if the category name is different in the database
|
||||
auto it = std::find_if(
|
||||
categories.begin(),
|
||||
categories.end(),
|
||||
[i](const auto &c) { return c.log_category_id == i; }
|
||||
);
|
||||
if (it != categories.end()) {
|
||||
if (it->log_category_description != Logs::LogCategoryName[i]) {
|
||||
LogInfo(
|
||||
"Updating log category [{}] ({}) to new name [{}]",
|
||||
it->log_category_description,
|
||||
i,
|
||||
Logs::LogCategoryName[i]
|
||||
);
|
||||
it->log_category_description = Logs::LogCategoryName[i];
|
||||
LogsysCategoriesRepository::ReplaceOne(*m_database, *it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,6 +718,10 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
||||
return this;
|
||||
}
|
||||
|
||||
if (silent_load) {
|
||||
SilenceConsoleLogging();
|
||||
}
|
||||
|
||||
LogInfo("Loaded [{}] log categories", categories.size());
|
||||
|
||||
auto webhooks = DiscordWebhooksRepository::GetWhere(*m_database, fmt::format("id < {}", MAX_DISCORD_WEBHOOK_ID));
|
||||
@@ -716,6 +739,10 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
||||
log_settings[Logs::Info].log_to_file = static_cast<uint8>(Logs::General);
|
||||
log_settings[Logs::Info].log_to_console = static_cast<uint8>(Logs::General);
|
||||
|
||||
if (silent_load) {
|
||||
SilenceConsoleLogging();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
+23
-15
@@ -74,7 +74,7 @@ namespace Logs {
|
||||
Spawns,
|
||||
Spells,
|
||||
Status, // deprecated
|
||||
TCPConnection,
|
||||
TCPConnection, // deprecated
|
||||
Tasks,
|
||||
Tradeskills,
|
||||
Trading,
|
||||
@@ -150,6 +150,8 @@ namespace Logs {
|
||||
BotSpellTypeChecks,
|
||||
NpcHandin,
|
||||
ZoneState,
|
||||
NetClient,
|
||||
NetTCP,
|
||||
MaxCategoryID /* Don't Remove this */
|
||||
};
|
||||
|
||||
@@ -183,7 +185,7 @@ namespace Logs {
|
||||
"Spawns",
|
||||
"Spells",
|
||||
"Status (Deprecated)",
|
||||
"TCP Connection",
|
||||
"TCP Connection (Deprecated)",
|
||||
"Tasks",
|
||||
"Tradeskills",
|
||||
"Trading",
|
||||
@@ -192,8 +194,8 @@ namespace Logs {
|
||||
"Web Interface (Deprecated)",
|
||||
"World Server (Deprecated)",
|
||||
"Zone Server (Deprecated)",
|
||||
"QueryErr",
|
||||
"Query",
|
||||
"MySQL Error",
|
||||
"MySQL Query",
|
||||
"Mercenaries",
|
||||
"Quest Debug",
|
||||
"Legacy Packet Logging (Deprecated)",
|
||||
@@ -209,15 +211,15 @@ namespace Logs {
|
||||
"Traps",
|
||||
"NPC Roam Box",
|
||||
"NPC Scaling",
|
||||
"MobAppearance",
|
||||
"Mob Appearance",
|
||||
"Info",
|
||||
"Warning",
|
||||
"Critical (Deprecated)",
|
||||
"Emergency (Deprecated)",
|
||||
"Alert (Deprecated)",
|
||||
"Notice (Deprecated)",
|
||||
"AI Scan",
|
||||
"AI Yell",
|
||||
"AI Scan Close",
|
||||
"AI Yell For Help",
|
||||
"AI CastBeneficial",
|
||||
"AOE Cast",
|
||||
"Entity Management",
|
||||
@@ -235,7 +237,7 @@ namespace Logs {
|
||||
"DialogueWindow",
|
||||
"HTTP",
|
||||
"Saylink",
|
||||
"ChecksumVer",
|
||||
"Checksum Verification",
|
||||
"CombatRecord",
|
||||
"Hate",
|
||||
"Discord",
|
||||
@@ -258,12 +260,12 @@ namespace Logs {
|
||||
"Bot Spell Checks",
|
||||
"Bot Spell Type Checks",
|
||||
"NpcHandin",
|
||||
"ZoneState"
|
||||
"ZoneState",
|
||||
"Net Server <-> Client",
|
||||
"Net TCP"
|
||||
};
|
||||
}
|
||||
|
||||
#include "eqemu_logsys_log_aliases.h"
|
||||
|
||||
class Database;
|
||||
|
||||
constexpr uint16 MAX_DISCORD_WEBHOOK_ID = 300;
|
||||
@@ -279,7 +281,13 @@ public:
|
||||
*/
|
||||
void CloseFileLogs();
|
||||
EQEmuLogSys *LoadLogSettingsDefaults();
|
||||
EQEmuLogSys *LoadLogDatabaseSettings();
|
||||
EQEmuLogSys *LoadLogDatabaseSettings(bool silent_load = false);
|
||||
|
||||
static EQEmuLogSys *Instance()
|
||||
{
|
||||
static EQEmuLogSys instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param directory_name
|
||||
@@ -346,7 +354,7 @@ public:
|
||||
/**
|
||||
* Internally used memory reference for all log settings per category
|
||||
* These are loaded via DB and have defaults loaded in LoadLogSettingsDefaults
|
||||
* Database loaded via LogSys.SetDatabase(&database)->LoadLogDatabaseSettings();
|
||||
* Database loaded via EQEmuLogSys::Instance()->SetDatabase(&database)->LoadLogDatabaseSettings();
|
||||
*/
|
||||
LogSettings log_settings[Logs::LogCategory::MaxCategoryID]{};
|
||||
|
||||
@@ -430,7 +438,7 @@ private:
|
||||
void InjectTablesIfNotExist();
|
||||
};
|
||||
|
||||
extern EQEmuLogSys LogSys;
|
||||
#include "eqemu_logsys_log_aliases.h"
|
||||
|
||||
/**
|
||||
template<typename... Args>
|
||||
@@ -452,7 +460,7 @@ void OutF(
|
||||
|
||||
#define OutF(ls, debug_level, log_category, file, func, line, formatStr, ...) \
|
||||
do { \
|
||||
ls.Out(debug_level, log_category, file, func, line, fmt::format(formatStr, ##__VA_ARGS__).c_str()); \
|
||||
ls->Out(debug_level, log_category, file, func, line, fmt::format(formatStr, ##__VA_ARGS__).c_str()); \
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
|
||||
+378
-376
File diff suppressed because it is too large
Load Diff
@@ -716,7 +716,7 @@ std::string PlayerEventDiscordFormatter::FormatNPCHandinEvent(
|
||||
);
|
||||
|
||||
for (int i = 0; i < h.augment_ids.size(); i++) {
|
||||
if (!Strings::EqualFold(h.augment_names[i], "None")) {
|
||||
if (!h.augment_names[i].empty()) {
|
||||
const uint8 slot_id = (i + 1);
|
||||
handin_items_info += fmt::format(
|
||||
"Augment {}: {} ({})\n",
|
||||
@@ -741,7 +741,7 @@ std::string PlayerEventDiscordFormatter::FormatNPCHandinEvent(
|
||||
);
|
||||
|
||||
for (int i = 0; i < r.augment_ids.size(); i++) {
|
||||
if (!Strings::EqualFold(r.augment_names[i], "None")) {
|
||||
if (!r.augment_names[i].empty()) {
|
||||
const uint8 slot_id = (i + 1);
|
||||
return_items_info += fmt::format(
|
||||
"Augment {}: {} ({})\n",
|
||||
|
||||
@@ -15,9 +15,9 @@ const uint32 PROCESS_RETENTION_TRUNCATION_TIMER_INTERVAL = 60 * 60 * 1000; // 1
|
||||
// general initialization routine
|
||||
void PlayerEventLogs::Init()
|
||||
{
|
||||
|
||||
m_process_batch_events_timer.SetTimer(RuleI(Logging, BatchPlayerEventProcessIntervalSeconds) * 1000);
|
||||
m_process_retention_truncation_timer.SetTimer(PROCESS_RETENTION_TRUNCATION_TIMER_INTERVAL);
|
||||
m_database_ping_timer.SetTimer(10 * 1000); // 10 seconds
|
||||
|
||||
ValidateDatabaseConnection();
|
||||
|
||||
@@ -81,7 +81,7 @@ void PlayerEventLogs::Init()
|
||||
if (!settings_to_insert.empty()) {
|
||||
PlayerEventLogSettingsRepository::ReplaceMany(*m_database, settings_to_insert);
|
||||
}
|
||||
|
||||
|
||||
bool processing_in_world = !RuleB(Logging, PlayerEventsQSProcess) && IsWorld();
|
||||
bool processing_in_qs = RuleB(Logging, PlayerEventsQSProcess) && IsQueryServ();
|
||||
|
||||
@@ -181,16 +181,26 @@ void PlayerEventLogs::ProcessBatchQueue()
|
||||
|
||||
// Helper to deserialize event data
|
||||
auto Deserialize = [](const std::string &data, auto &out) {
|
||||
std::stringstream ss(data);
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
out.serialize(ar);
|
||||
if (!Strings::IsValidJson(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
auto AssignEtlId = [&](
|
||||
PlayerEventLogsRepository::PlayerEventLogs &r,
|
||||
PlayerEvent::EventType type
|
||||
) {
|
||||
|
||||
auto AssignEtlId = [&](
|
||||
PlayerEventLogsRepository::PlayerEventLogs& r,
|
||||
PlayerEvent::EventType type
|
||||
)
|
||||
{
|
||||
if (m_etl_settings.contains(type)) {
|
||||
r.etl_table_id = m_etl_settings.at(type).next_id++;
|
||||
}
|
||||
@@ -398,7 +408,6 @@ void PlayerEventLogs::ProcessBatchQueue()
|
||||
auto it = event_processors.find(static_cast<PlayerEvent::EventType>(r.event_type_id));
|
||||
if (it != event_processors.end()) {
|
||||
it->second(r); // Call the appropriate lambda
|
||||
r.event_data = "{}"; // Clear event data
|
||||
}
|
||||
else {
|
||||
LogPlayerEventsDetail("Non-Implemented ETL routing [{}]", r.event_type_id);
|
||||
@@ -500,7 +509,7 @@ bool PlayerEventLogs::IsEventDiscordEnabled(int32_t event_type_id)
|
||||
}
|
||||
|
||||
// ensure there is a matching webhook to begin with
|
||||
if (!LogSys.GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url.empty()) {
|
||||
if (!EQEmuLogSys::Instance()->GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -520,13 +529,27 @@ std::string PlayerEventLogs::GetDiscordWebhookUrlFromEventType(int32_t event_typ
|
||||
}
|
||||
|
||||
// ensure there is a matching webhook to begin with
|
||||
if (!LogSys.GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url.empty()) {
|
||||
return LogSys.GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url;
|
||||
if (!EQEmuLogSys::Instance()->GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url.empty()) {
|
||||
return EQEmuLogSys::Instance()->GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void PlayerEventLogs::LoadPlayerEventSettingsFromQS(
|
||||
const std::vector<PlayerEventLogSettingsRepository::PlayerEventLogSettings> &settings
|
||||
)
|
||||
{
|
||||
for (const auto &e : settings) {
|
||||
if (e.id >= PlayerEvent::MAX || e.id < 0) {
|
||||
continue;
|
||||
}
|
||||
m_settings[e.id] = e;
|
||||
}
|
||||
|
||||
LogInfo("Applied [{}] player event log settings from QS", settings.size());
|
||||
}
|
||||
|
||||
// GM_COMMAND | [x] Implemented Formatter
|
||||
// ZONING | [x] Implemented Formatter
|
||||
// AA_GAIN | [x] Implemented Formatter
|
||||
@@ -908,6 +931,10 @@ std::string PlayerEventLogs::GetDiscordPayloadFromEvent(const PlayerEvent::Playe
|
||||
// general process function, used in world or QS depending on rule Logging:PlayerEventsQSProcess
|
||||
void PlayerEventLogs::Process()
|
||||
{
|
||||
if (m_database_ping_timer.Check()) {
|
||||
m_database->ping();
|
||||
}
|
||||
|
||||
if (m_process_batch_events_timer.Check() ||
|
||||
m_record_batch_queue.size() >= RuleI(Logging, BatchPlayerEventProcessChunkSize)) {
|
||||
ProcessBatchQueue();
|
||||
|
||||
@@ -73,9 +73,11 @@ public:
|
||||
return BuildPlayerEventPacket(c);
|
||||
}
|
||||
|
||||
[[nodiscard]] const PlayerEventLogSettingsRepository::PlayerEventLogSettings *GetSettings() const;
|
||||
bool IsEventDiscordEnabled(int32_t event_type_id);
|
||||
std::string GetDiscordWebhookUrlFromEventType(int32_t event_type_id);
|
||||
[[nodiscard]] const PlayerEventLogSettingsRepository::PlayerEventLogSettings * GetSettings() const;
|
||||
bool IsEventDiscordEnabled(int32_t event_type_id);
|
||||
std::string GetDiscordWebhookUrlFromEventType(int32_t event_type_id);
|
||||
|
||||
void LoadPlayerEventSettingsFromQS(const std::vector<PlayerEventLogSettingsRepository::PlayerEventLogSettings>& settings);
|
||||
|
||||
static std::string GetDiscordPayloadFromEvent(const PlayerEvent::PlayerEventContainer &e);
|
||||
|
||||
@@ -94,6 +96,12 @@ public:
|
||||
std::vector<PlayerEventAaPurchaseRepository::PlayerEventAaPurchase> aa_purchase;
|
||||
};
|
||||
|
||||
static PlayerEventLogs* Instance()
|
||||
{
|
||||
static PlayerEventLogs instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
private:
|
||||
struct EtlSettings {
|
||||
bool enabled;
|
||||
@@ -113,6 +121,7 @@ private:
|
||||
std::map<PlayerEvent::EventType, EtlSettings> m_etl_settings{};
|
||||
|
||||
// timers
|
||||
Timer m_database_ping_timer; // database ping timer
|
||||
Timer m_process_batch_events_timer; // events processing timer
|
||||
Timer m_process_retention_truncation_timer; // timer for truncating events based on retention settings
|
||||
|
||||
@@ -126,6 +135,4 @@ public:
|
||||
std::map<PlayerEvent::EventType, EtlSettings> &GetEtlSettings() { return m_etl_settings;}
|
||||
};
|
||||
|
||||
extern PlayerEventLogs player_event_logs;
|
||||
|
||||
#endif //EQEMU_PLAYER_EVENT_LOGS_H
|
||||
|
||||
+553
-120
File diff suppressed because it is too large
Load Diff
+41
-18
@@ -21,8 +21,8 @@ void EvolvingItemsManager::LoadEvolvingItems() const
|
||||
results.begin(),
|
||||
results.end(),
|
||||
std::inserter(
|
||||
evolving_items_manager.GetEvolvingItemsCache(),
|
||||
evolving_items_manager.GetEvolvingItemsCache().end()
|
||||
EvolvingItemsManager::Instance()->GetEvolvingItemsCache(),
|
||||
EvolvingItemsManager::Instance()->GetEvolvingItemsCache().end()
|
||||
),
|
||||
[](const ItemsEvolvingDetailsRepository::ItemsEvolvingDetails &x) {
|
||||
return std::make_pair(x.item_id, x);
|
||||
@@ -42,18 +42,22 @@ void EvolvingItemsManager::SetContentDatabase(Database *db)
|
||||
|
||||
double EvolvingItemsManager::CalculateProgression(const uint64 current_amount, const uint32 item_id)
|
||||
{
|
||||
if (!evolving_items_manager.GetEvolvingItemsCache().contains(item_id)) {
|
||||
if (!EvolvingItemsManager::Instance()->GetEvolvingItemsCache().contains(item_id)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return evolving_items_manager.GetEvolvingItemsCache().at(item_id).required_amount > 0
|
||||
return EvolvingItemsManager::Instance()->GetEvolvingItemsCache().at(item_id).required_amount > 0
|
||||
? static_cast<double>(current_amount)
|
||||
/ static_cast<double>(evolving_items_manager.GetEvolvingItemsCache().at(item_id).required_amount) * 100
|
||||
/ static_cast<double>(EvolvingItemsManager::Instance()->GetEvolvingItemsCache().at(item_id).required_amount) * 100
|
||||
: 0;
|
||||
}
|
||||
|
||||
void EvolvingItemsManager::DoLootChecks(const uint32 char_id, const uint16 slot_id, const EQ::ItemInstance &inst) const
|
||||
{
|
||||
if (!inst) {
|
||||
return;
|
||||
}
|
||||
|
||||
inst.SetEvolveEquipped(false);
|
||||
if (inst.IsEvolving() && slot_id <= EQ::invslot::EQUIPMENT_END && slot_id >= EQ::invslot::EQUIPMENT_BEGIN) {
|
||||
inst.SetEvolveEquipped(true);
|
||||
@@ -69,7 +73,7 @@ void EvolvingItemsManager::DoLootChecks(const uint32 char_id, const uint16 slot_
|
||||
e.character_id = char_id;
|
||||
e.item_id = inst.GetID();
|
||||
e.equipped = inst.GetEvolveEquipped();
|
||||
e.final_item_id = evolving_items_manager.GetFinalItemID(inst);
|
||||
e.final_item_id = EvolvingItemsManager::Instance()->GetFinalItemID(inst);
|
||||
|
||||
auto r = CharacterEvolvingItemsRepository::InsertOne(*m_db, e);
|
||||
e.id = r.id;
|
||||
@@ -87,21 +91,25 @@ void EvolvingItemsManager::DoLootChecks(const uint32 char_id, const uint16 slot_
|
||||
|
||||
uint32 EvolvingItemsManager::GetFinalItemID(const EQ::ItemInstance &inst) const
|
||||
{
|
||||
if (!inst) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto start_iterator = std::ranges::find_if(
|
||||
evolving_items_manager.GetEvolvingItemsCache().cbegin(),
|
||||
evolving_items_manager.GetEvolvingItemsCache().cend(),
|
||||
EvolvingItemsManager::Instance()->GetEvolvingItemsCache().cbegin(),
|
||||
EvolvingItemsManager::Instance()->GetEvolvingItemsCache().cend(),
|
||||
[&](const std::pair<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &a) {
|
||||
return a.second.item_evo_id == inst.GetEvolveLoreID();
|
||||
}
|
||||
);
|
||||
|
||||
if (start_iterator == std::end(evolving_items_manager.GetEvolvingItemsCache())) {
|
||||
if (start_iterator == std::end(EvolvingItemsManager::Instance()->GetEvolvingItemsCache())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto final_id = std::ranges::max_element(
|
||||
start_iterator,
|
||||
evolving_items_manager.GetEvolvingItemsCache().cend(),
|
||||
EvolvingItemsManager::Instance()->GetEvolvingItemsCache().cend(),
|
||||
[&](
|
||||
const std::pair<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &a,
|
||||
const std::pair<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &b
|
||||
@@ -116,18 +124,22 @@ uint32 EvolvingItemsManager::GetFinalItemID(const EQ::ItemInstance &inst) const
|
||||
|
||||
uint32 EvolvingItemsManager::GetNextEvolveItemID(const EQ::ItemInstance &inst) const
|
||||
{
|
||||
if (!inst) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int8 const current_level = inst.GetEvolveLvl();
|
||||
|
||||
const auto iterator = std::ranges::find_if(
|
||||
evolving_items_manager.GetEvolvingItemsCache().cbegin(),
|
||||
evolving_items_manager.GetEvolvingItemsCache().cend(),
|
||||
EvolvingItemsManager::Instance()->GetEvolvingItemsCache().cbegin(),
|
||||
EvolvingItemsManager::Instance()->GetEvolvingItemsCache().cend(),
|
||||
[&](const std::pair<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &a) {
|
||||
return a.second.item_evo_id == inst.GetEvolveLoreID() &&
|
||||
a.second.item_evolve_level == current_level + 1;
|
||||
}
|
||||
);
|
||||
|
||||
if (iterator == std::end(evolving_items_manager.GetEvolvingItemsCache())) {
|
||||
if (iterator == std::end(EvolvingItemsManager::Instance()->GetEvolvingItemsCache())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -191,6 +203,10 @@ uint64 EvolvingItemsManager::GetTotalEarnedXP(const EQ::ItemInstance &inst)
|
||||
EvolveGetNextItem EvolvingItemsManager::GetNextItemByXP(const EQ::ItemInstance &inst_in, const int64 in_xp)
|
||||
{
|
||||
EvolveGetNextItem ets{};
|
||||
if (!inst_in) {
|
||||
return ets;
|
||||
}
|
||||
|
||||
const auto evolve_items = GetEvolveIDItems(inst_in.GetEvolveLoreID());
|
||||
uint32 max_transfer_level = 0;
|
||||
int64 xp = in_xp;
|
||||
@@ -235,9 +251,12 @@ EvolveTransfer EvolvingItemsManager::DetermineTransferResults(
|
||||
)
|
||||
{
|
||||
EvolveTransfer ets{};
|
||||
if (!inst_from || !inst_to) {
|
||||
return ets;
|
||||
}
|
||||
|
||||
auto evolving_details_inst_from = evolving_items_manager.GetEvolveItemDetails(inst_from.GetID());
|
||||
auto evolving_details_inst_to = evolving_items_manager.GetEvolveItemDetails(inst_to.GetID());
|
||||
auto evolving_details_inst_from = EvolvingItemsManager::Instance()->GetEvolveItemDetails(inst_from.GetID());
|
||||
auto evolving_details_inst_to = EvolvingItemsManager::Instance()->GetEvolveItemDetails(inst_to.GetID());
|
||||
|
||||
if (!evolving_details_inst_from.id || !evolving_details_inst_to.id) {
|
||||
return ets;
|
||||
@@ -253,10 +272,10 @@ EvolveTransfer EvolvingItemsManager::DetermineTransferResults(
|
||||
compatibility = 30;
|
||||
}
|
||||
|
||||
xp = evolving_items_manager.GetTotalEarnedXP(inst_from) * compatibility / 100;
|
||||
auto results = evolving_items_manager.GetNextItemByXP(inst_to, xp);
|
||||
xp = EvolvingItemsManager::Instance()->GetTotalEarnedXP(inst_from) * compatibility / 100;
|
||||
auto results = EvolvingItemsManager::Instance()->GetNextItemByXP(inst_to, xp);
|
||||
|
||||
ets.item_from_id = evolving_items_manager.GetFirstItemInLoreGroup(inst_from.GetEvolveLoreID());
|
||||
ets.item_from_id = EvolvingItemsManager::Instance()->GetFirstItemInLoreGroup(inst_from.GetEvolveLoreID());
|
||||
ets.item_from_current_amount = results.from_current_amount;
|
||||
ets.item_to_id = results.new_item_id;
|
||||
ets.item_to_current_amount = results.new_current_amount;
|
||||
@@ -295,6 +314,10 @@ uint32 EvolvingItemsManager::GetFirstItemInLoreGroupByItemID(const uint32 item_i
|
||||
|
||||
void EvolvingItemsManager::LoadPlayerEvent(const EQ::ItemInstance &inst, PlayerEvent::EvolveItem &e)
|
||||
{
|
||||
if (!inst) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.item_id = inst.GetID();
|
||||
e.item_name = inst.GetItem() ? inst.GetItem()->Name : std::string();
|
||||
e.level = inst.GetEvolveLvl();
|
||||
|
||||
@@ -53,15 +53,18 @@ public:
|
||||
ItemsEvolvingDetailsRepository::ItemsEvolvingDetails GetEvolveItemDetails(uint64 id);
|
||||
EvolveTransfer DetermineTransferResults(const EQ::ItemInstance& inst_from, const EQ::ItemInstance& inst_to);
|
||||
EvolveGetNextItem GetNextItemByXP(const EQ::ItemInstance &inst_in, int64 in_xp);
|
||||
std::map<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails>& GetEvolvingItemsCache() { return evolving_items_cache; }
|
||||
std::map<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails>& GetEvolvingItemsCache() { return m_evolving_items_cache; }
|
||||
std::vector<ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> GetEvolveIDItems(uint32 evolve_id);
|
||||
|
||||
static EvolvingItemsManager* Instance()
|
||||
{
|
||||
static EvolvingItemsManager instance;
|
||||
return &instance;
|
||||
}
|
||||
private:
|
||||
std::map<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> evolving_items_cache;
|
||||
std::map<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> m_evolving_items_cache;
|
||||
Database * m_db;
|
||||
Database * m_content_db;
|
||||
};
|
||||
|
||||
extern EvolvingItemsManager evolving_items_manager;
|
||||
|
||||
#endif //EVOLVING_H
|
||||
|
||||
@@ -303,6 +303,14 @@ bool IpUtil::IsPortInUse(const std::string& ip, int port) {
|
||||
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);
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace EQ {
|
||||
EQ_EXCEPT("IPC Mutex", "Could not create mutex.");
|
||||
}
|
||||
#else
|
||||
std::string final_name = fmt::format("{}/{}.lock", path.GetSharedMemoryPath(), name);
|
||||
std::string final_name = fmt::format("{}/{}.lock", PathManager::Instance()->GetSharedMemoryPath(), name);
|
||||
|
||||
#ifdef __DARWIN
|
||||
#if __DARWIN_C_LEVEL < 200809L
|
||||
|
||||
+18
-10
@@ -574,7 +574,7 @@ EQ::ItemInstance* EQ::ItemInstance::GetOrnamentationAugment() const
|
||||
uint32 EQ::ItemInstance::GetOrnamentHeroModel(int32 material_slot) const
|
||||
{
|
||||
// Not a Hero Forge item.
|
||||
if (m_ornament_hero_model == 0 || material_slot < 0) {
|
||||
if (m_ornament_hero_model == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -906,24 +906,32 @@ bool EQ::ItemInstance::IsSlotAllowed(int16 slot_id) const {
|
||||
|
||||
bool EQ::ItemInstance::IsDroppable(bool recurse) const
|
||||
{
|
||||
if (!m_item)
|
||||
if (!m_item) {
|
||||
return false;
|
||||
}
|
||||
/*if (m_ornamentidfile) // not implemented
|
||||
return false;*/
|
||||
if (m_attuned)
|
||||
if (m_attuned) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (recurse) {
|
||||
for (auto iter : m_contents) {
|
||||
if (!iter.second)
|
||||
for (auto iter: m_contents) {
|
||||
if (!iter.second) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!iter.second->IsDroppable(recurse))
|
||||
if (!iter.second->IsDroppable(recurse)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1791,7 +1799,7 @@ std::vector<std::string> EQ::ItemInstance::GetAugmentNames() const
|
||||
|
||||
for (uint8 slot_id = invaug::SOCKET_BEGIN; slot_id <= invaug::SOCKET_END; slot_id++) {
|
||||
const auto augment = GetAugment(slot_id);
|
||||
augment_names.push_back(augment ? augment->GetItem()->Name : "None");
|
||||
augment_names.push_back(augment ? augment->GetItem()->Name : "");
|
||||
}
|
||||
|
||||
return augment_names;
|
||||
|
||||
@@ -335,7 +335,7 @@ namespace EQ
|
||||
void SetEvolveAddToCurrentAmount(const uint64 in) const { m_evolving_details.current_amount += in; }
|
||||
void SetEvolveFinalItemID(const uint32 in) const { m_evolving_details.final_item_id = in; }
|
||||
bool TransferOwnership(Database& db, const uint32 to_char_id) const;
|
||||
void CalculateEvolveProgression() const { m_evolving_details.progression = evolving_items_manager.CalculateProgression(GetEvolveCurrentAmount(), GetID()); }
|
||||
void CalculateEvolveProgression() const { m_evolving_details.progression = EvolvingItemsManager::Instance()->CalculateProgression(GetEvolveCurrentAmount(), GetID()); }
|
||||
|
||||
protected:
|
||||
//////////////////////////
|
||||
|
||||
+142
-104
@@ -1,12 +1,16 @@
|
||||
#include "daybreak_connection.h"
|
||||
#include "../event/event_loop.h"
|
||||
#include "../event/task.h"
|
||||
#include "../data_verification.h"
|
||||
#include "crc32.h"
|
||||
#include "../eqemu_logsys.h"
|
||||
#include <zlib.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()
|
||||
{
|
||||
@@ -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);
|
||||
int rc = uv_udp_bind(&m_socket, (const struct sockaddr *)&recv_addr, UV_UDP_REUSEADDR);
|
||||
|
||||
rc = uv_udp_recv_start(&m_socket,
|
||||
[](uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
|
||||
buf->base = new char[suggested_size];
|
||||
memset(buf->base, 0, suggested_size);
|
||||
buf->len = suggested_size;
|
||||
},
|
||||
rc = uv_udp_recv_start(
|
||||
&m_socket,
|
||||
[](uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
|
||||
if (suggested_size > 65536) {
|
||||
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) {
|
||||
DaybreakConnectionManager *c = (DaybreakConnectionManager*)handle->data;
|
||||
if (nread < 0 || addr == nullptr) {
|
||||
delete[] buf->base;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -70,7 +80,10 @@ void EQ::Net::DaybreakConnectionManager::Attach(uv_loop_t *loop)
|
||||
uv_ip4_name((const sockaddr_in*)addr, endpoint, 16);
|
||||
auto port = ntohs(((const sockaddr_in*)addr)->sin_port);
|
||||
c->ProcessPacket(endpoint, port, buf->base, nread);
|
||||
delete[] buf->base;
|
||||
|
||||
if (buf->len > 65536) {
|
||||
delete[] buf->base;
|
||||
}
|
||||
});
|
||||
|
||||
m_attached = loop;
|
||||
@@ -310,7 +323,7 @@ EQ::Net::DaybreakConnection::DaybreakConnection(DaybreakConnectionManager *owner
|
||||
m_last_session_stats = Clock::now();
|
||||
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
|
||||
@@ -342,16 +355,16 @@ EQ::Net::DaybreakConnection::~DaybreakConnection()
|
||||
|
||||
void EQ::Net::DaybreakConnection::Close()
|
||||
{
|
||||
if (m_status == StatusConnected) {
|
||||
if (m_status != StatusDisconnected && m_status != StatusDisconnecting) {
|
||||
FlushBuffer();
|
||||
SendDisconnect();
|
||||
}
|
||||
|
||||
if (m_status != StatusDisconnecting) {
|
||||
m_close_time = Clock::now();
|
||||
ChangeStatus(StatusDisconnecting);
|
||||
}
|
||||
else {
|
||||
ChangeStatus(StatusDisconnecting);
|
||||
}
|
||||
|
||||
ChangeStatus(StatusDisconnecting);
|
||||
}
|
||||
|
||||
void EQ::Net::DaybreakConnection::QueuePacket(Packet &p)
|
||||
@@ -634,7 +647,7 @@ void EQ::Net::DaybreakConnection::ProcessDecodedPacket(const Packet &p)
|
||||
p.PutSerialize(0, reply);
|
||||
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;
|
||||
@@ -653,7 +666,7 @@ void EQ::Net::DaybreakConnection::ProcessDecodedPacket(const Packet &p)
|
||||
m_max_packet_size = reply.max_packet_size;
|
||||
ChangeStatus(StatusConnected);
|
||||
|
||||
LogNetcode(
|
||||
LogNetClient(
|
||||
"[OP_SessionResponse] Session [{}] refresh with encode key [{}]",
|
||||
m_connect_code,
|
||||
HostToNetwork(m_encode_key)
|
||||
@@ -782,7 +795,7 @@ void EQ::Net::DaybreakConnection::ProcessDecodedPacket(const Packet &p)
|
||||
SendDisconnect();
|
||||
}
|
||||
|
||||
LogNetcode(
|
||||
LogNetClient(
|
||||
"[OP_SessionDisconnect] Session [{}] disconnect with encode key [{}]",
|
||||
m_connect_code,
|
||||
HostToNetwork(m_encode_key)
|
||||
@@ -852,7 +865,7 @@ bool EQ::Net::DaybreakConnection::ValidateCRC(Packet &p)
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1043,7 +1056,7 @@ void EQ::Net::DaybreakConnection::Decompress(Packet &p, size_t offset, size_t le
|
||||
return;
|
||||
}
|
||||
|
||||
static uint8_t new_buffer[4096];
|
||||
static thread_local uint8_t new_buffer[4096];
|
||||
uint8_t *buffer = (uint8_t*)p.Data() + offset;
|
||||
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)
|
||||
{
|
||||
uint8_t new_buffer[2048] = { 0 };
|
||||
static thread_local uint8_t new_buffer[2048] = { 0 };
|
||||
uint8_t *buffer = (uint8_t*)p.Data() + offset;
|
||||
uint32_t new_length = 0;
|
||||
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)
|
||||
{
|
||||
if (m_status == DbProtocolStatus::StatusDisconnected) {
|
||||
@@ -1117,34 +1126,57 @@ void EQ::Net::DaybreakConnection::ProcessResend(int stream)
|
||||
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();
|
||||
|
||||
if (time_since_first_sent >= m_owner->m_options.resend_timeout) {
|
||||
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
|
||||
auto first_sent_ms = std::chrono::duration_cast<std::chrono::milliseconds>(first_packet.first_sent.time_since_epoch()).count();
|
||||
LogNetClient(
|
||||
"Closing connection for m_endpoint [{}] m_port [{}] time_since_first_sent [{}] >= m_owner->m_options.resend_timeout [{}] now [{}] first_packet.first_sent [{}]",
|
||||
m_endpoint,
|
||||
m_port,
|
||||
time_since_first_sent,
|
||||
m_owner->m_options.resend_timeout,
|
||||
now_ms,
|
||||
first_sent_ms
|
||||
);
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_last_ack - now > std::chrono::milliseconds(1000)) {
|
||||
LogNetClient(
|
||||
"Resetting m_acked_since_last_resend flag for m_endpoint [{}] m_port [{}]",
|
||||
m_endpoint,
|
||||
m_port
|
||||
);
|
||||
m_acked_since_last_resend = true;
|
||||
}
|
||||
|
||||
// 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 (time_since_first_sent <= first_packet.resend_delay && !m_acked_since_last_resend) {
|
||||
LogNetcodeDetail(
|
||||
"Not resending packets for stream [{}] time since first sent [{}] resend delay [{}] m_acked_since_last_resend [{}]",
|
||||
stream,
|
||||
LogNetClientDetail(
|
||||
"Not resending packets for m_endpoint [{}] m_port [{}] packets [{}] time_first_sent [{}] resend_delay [{}] m_acked_since_last_resend [{}]",
|
||||
m_endpoint,
|
||||
m_port,
|
||||
s->sent_packets.size(),
|
||||
time_since_first_sent,
|
||||
first_packet.resend_delay,
|
||||
m_acked_since_last_resend
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (time_since_first_sent >= m_owner->m_options.resend_timeout) {
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (LogSys.IsLogEnabled(Logs::Detail, Logs::Netcode)) {
|
||||
if (EQEmuLogSys::Instance()->IsLogEnabled(Logs::General, Logs::NetClient)) {
|
||||
size_t total_size = 0;
|
||||
for (auto &e: s->sent_packets) {
|
||||
total_size += e.second.packet.Length();
|
||||
}
|
||||
|
||||
LogNetcodeDetail(
|
||||
"Resending packets for stream [{}] packet count [{}] total packet size [{}] m_acked_since_last_resend [{}]",
|
||||
stream,
|
||||
LogNetClientDetail(
|
||||
"Resending packets for m_endpoint [{}] m_port [{}] packet count [{}] total packet size [{}] m_acked_since_last_resend [{}]",
|
||||
m_endpoint,
|
||||
m_port,
|
||||
s->sent_packets.size(),
|
||||
total_size,
|
||||
m_acked_since_last_resend
|
||||
@@ -1154,10 +1186,13 @@ void EQ::Net::DaybreakConnection::ProcessResend(int stream)
|
||||
for (auto &e: s->sent_packets) {
|
||||
if (m_resend_packets_sent >= MAX_CLIENT_RECV_PACKETS_PER_WINDOW ||
|
||||
m_resend_bytes_sent >= MAX_CLIENT_RECV_BYTES_PER_WINDOW) {
|
||||
LogNetcodeDetail(
|
||||
"Stopping resend because we hit thresholds m_resend_packets_sent [{}] max [{}] m_resend_bytes_sent [{}] max [{}]",
|
||||
LogNetClient(
|
||||
"Stopping resend because we hit thresholds for m_endpoint [{}] m_port [{}] m_resend_packets_sent [{}] max [{}] in_queue [{}] m_resend_bytes_sent [{}] max [{}]",
|
||||
m_endpoint,
|
||||
m_port,
|
||||
m_resend_packets_sent,
|
||||
MAX_CLIENT_RECV_PACKETS_PER_WINDOW,
|
||||
s->sent_packets.size(),
|
||||
m_resend_bytes_sent,
|
||||
MAX_CLIENT_RECV_BYTES_PER_WINDOW
|
||||
);
|
||||
@@ -1194,11 +1229,11 @@ void EQ::Net::DaybreakConnection::ProcessResend(int stream)
|
||||
}
|
||||
|
||||
m_acked_since_last_resend = false;
|
||||
m_last_ack = now;
|
||||
}
|
||||
|
||||
void EQ::Net::DaybreakConnection::Ack(int stream, uint16_t seq)
|
||||
{
|
||||
|
||||
auto now = Clock::now();
|
||||
auto s = &m_streams[stream];
|
||||
auto iter = s->sent_packets.begin();
|
||||
@@ -1214,12 +1249,14 @@ void EQ::Net::DaybreakConnection::Ack(int stream, uint16_t seq)
|
||||
m_rolling_ping = (m_rolling_ping * 2 + round_time) / 3;
|
||||
|
||||
iter = s->sent_packets.erase(iter);
|
||||
m_acked_since_last_resend = true;
|
||||
}
|
||||
else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
m_acked_since_last_resend = true;
|
||||
m_last_ack = now;
|
||||
}
|
||||
|
||||
void EQ::Net::DaybreakConnection::OutOfOrderAck(int stream, uint16_t seq)
|
||||
@@ -1237,6 +1274,9 @@ void EQ::Net::DaybreakConnection::OutOfOrderAck(int stream, uint16_t seq)
|
||||
|
||||
s->sent_packets.erase(iter);
|
||||
}
|
||||
|
||||
m_acked_since_last_resend = true;
|
||||
m_last_ack = now;
|
||||
}
|
||||
|
||||
void EQ::Net::DaybreakConnection::UpdateDataBudget(double budget_add)
|
||||
@@ -1333,99 +1373,97 @@ void EQ::Net::DaybreakConnection::SendKeepAlive()
|
||||
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) {
|
||||
auto new_budget = m_outgoing_budget - (p.Length() / 1024.0);
|
||||
if (new_budget <= 0.0) {
|
||||
m_stats.dropped_datarate_packets++;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
m_outgoing_budget = new_budget;
|
||||
}
|
||||
}
|
||||
|
||||
m_last_send = Clock::now();
|
||||
|
||||
auto send_func = [](uv_udp_send_t* req, int status) {
|
||||
delete[](char*)req->data;
|
||||
delete req;
|
||||
};
|
||||
auto pooled_opt = send_buffer_pool.acquire();
|
||||
if (!pooled_opt) {
|
||||
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)) {
|
||||
|
||||
m_stats.bytes_before_encode += p.Length();
|
||||
|
||||
DynamicPacket out;
|
||||
out.PutPacket(0, p);
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
switch (m_encode_passes[i]) {
|
||||
case EncodeCompression:
|
||||
if (out.GetInt8(0) == 0)
|
||||
Compress(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
|
||||
else
|
||||
Compress(out, 1, out.Length() - 1);
|
||||
break;
|
||||
case EncodeXOR:
|
||||
if (out.GetInt8(0) == 0)
|
||||
Encode(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
|
||||
else
|
||||
Encode(out, 1, out.Length() - 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
for (auto &m_encode_passe: m_encode_passes) {
|
||||
switch (m_encode_passe) {
|
||||
case EncodeCompression:
|
||||
if (out.GetInt8(0) == 0) {
|
||||
Compress(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
|
||||
} else {
|
||||
Compress(out, 1, out.Length() - 1);
|
||||
}
|
||||
break;
|
||||
case EncodeXOR:
|
||||
if (out.GetInt8(0) == 0) {
|
||||
Encode(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
|
||||
} else {
|
||||
Encode(out, 1, out.Length() - 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
send_buffers[0] = uv_buf_init(data, out.Length());
|
||||
send_req->data = send_buffers[0].base;
|
||||
|
||||
m_stats.sent_bytes += out.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;
|
||||
} else {
|
||||
memcpy(data, p.Data(), p.Length());
|
||||
send_buffers[0] = uv_buf_init(data, p.Length());
|
||||
}
|
||||
|
||||
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_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;
|
||||
if (m_owner->m_options.simulated_out_packet_loss &&
|
||||
m_owner->m_options.simulated_out_packet_loss >= m_owner->m_rand.Int(0, 100)) {
|
||||
send_buffer_pool.release(ctx);
|
||||
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)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "../random.h"
|
||||
#include "packet.h"
|
||||
#include "daybreak_structs.h"
|
||||
#include "daybreak_pooling.h"
|
||||
#include <uv.h>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
@@ -185,6 +186,7 @@ namespace EQ
|
||||
size_t m_resend_packets_sent = 0;
|
||||
size_t m_resend_bytes_sent = 0;
|
||||
bool m_acked_since_last_resend = false;
|
||||
Timestamp m_last_ack;
|
||||
|
||||
struct DaybreakSentPacket
|
||||
{
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ void EQ::Net::EQStream::QueuePacket(const EQApplicationPacket *p, bool ack_req)
|
||||
OpcodeManager::EmuToName(p->GetOpcode()),
|
||||
(*m_opcode_manager)->EmuToEQ(p->GetOpcode()),
|
||||
p->Size(),
|
||||
(LogSys.IsLogEnabled(Logs::Detail, Logs::PacketServerClient) ? DumpPacketToString(p) : "")
|
||||
(EQEmuLogSys::Instance()->IsLogEnabled(Logs::Detail, Logs::PacketServerClient) ? DumpPacketToString(p) : "")
|
||||
);
|
||||
|
||||
if (m_opcode_manager && *m_opcode_manager) {
|
||||
|
||||
@@ -62,15 +62,15 @@ void EQ::Net::ServertalkClient::Connect()
|
||||
m_connecting = true;
|
||||
EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
|
||||
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;
|
||||
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->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();
|
||||
});
|
||||
|
||||
|
||||
@@ -58,15 +58,15 @@ void EQ::Net::ServertalkLegacyClient::Connect()
|
||||
m_connecting = true;
|
||||
EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
|
||||
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;
|
||||
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->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();
|
||||
});
|
||||
|
||||
@@ -131,7 +131,7 @@ void EQ::Net::ServertalkLegacyClient::ProcessReadBuffer()
|
||||
}
|
||||
else {
|
||||
EQ::Net::StaticPacket p(&m_buffer[current + 4], length);
|
||||
|
||||
|
||||
auto cb = m_message_callbacks.find(opcode);
|
||||
if (cb != m_message_callbacks.end()) {
|
||||
cb->second(opcode, p);
|
||||
|
||||
@@ -319,7 +319,7 @@ void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p)
|
||||
size_t message_len = length;
|
||||
EQ::Net::StaticPacket packet(&data[0], message_len);
|
||||
|
||||
const auto is_detail_enabled = LogSys.IsLogEnabled(Logs::Detail, Logs::PacketServerToServer);
|
||||
const auto is_detail_enabled = EQEmuLogSys::Instance()->IsLogEnabled(Logs::Detail, Logs::PacketServerToServer);
|
||||
if (opcode != ServerOP_KeepAlive || is_detail_enabled) {
|
||||
LogPacketServerToServer(
|
||||
"[{:#06x}] Size [{}] {}",
|
||||
|
||||
+108
-55
@@ -1,5 +1,8 @@
|
||||
#include "tcp_connection.h"
|
||||
#include "../event/event_loop.h"
|
||||
#include <iostream>
|
||||
|
||||
WriteReqPool tcp_write_pool;
|
||||
|
||||
void on_close_handle(uv_handle_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() {
|
||||
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];
|
||||
buf->len = suggested_size;
|
||||
}, [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
|
||||
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) {
|
||||
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) {
|
||||
connection->Read(buf->base, nread);
|
||||
if (nread > 0) {
|
||||
connection->Read(buf->base, nread);
|
||||
}
|
||||
else if (nread == UV_EOF) {
|
||||
connection->Disconnect();
|
||||
}
|
||||
else if (nread < 0) {
|
||||
connection->Disconnect();
|
||||
}
|
||||
|
||||
if (buf->base) {
|
||||
delete[] buf->base;
|
||||
if (buf->len > 65536) {
|
||||
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)
|
||||
@@ -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)
|
||||
{
|
||||
if (!m_socket) {
|
||||
void EQ::Net::TCPConnection::Write(const char* data, size_t count) {
|
||||
if (!m_socket || !data || count == 0) {
|
||||
std::cerr << "TCPConnection::Write - Invalid socket or data\n";
|
||||
return;
|
||||
}
|
||||
|
||||
struct WriteBaton
|
||||
{
|
||||
TCPConnection *connection;
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
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();
|
||||
if (count <= TCP_BUFFER_SIZE) {
|
||||
// Fast path: use pooled request with embedded buffer
|
||||
auto req_opt = tcp_write_pool.acquire();
|
||||
if (!req_opt) {
|
||||
std::cerr << "TCPConnection::Write - Out of write requests\n";
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
sockaddr_storage addr;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "tcp_connection_pooling.h"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
@@ -16,7 +17,7 @@ namespace EQ
|
||||
~TCPConnection();
|
||||
|
||||
static void Connect(const std::string &addr, int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb);
|
||||
|
||||
|
||||
void Start();
|
||||
void OnRead(std::function<void(TCPConnection*, const unsigned char *, size_t)> cb);
|
||||
void OnDisconnect(std::function<void(TCPConnection*)> cb);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -78,7 +78,7 @@ namespace RoF
|
||||
{
|
||||
//create our opcode manager if we havent already
|
||||
if (opcodes == nullptr) {
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name);
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", PathManager::Instance()->GetPatchPath(), name);
|
||||
|
||||
//load up the opcode manager.
|
||||
//TODO: figure out how to support shared memory with multiple patches...
|
||||
@@ -117,7 +117,7 @@ namespace RoF
|
||||
//we need to go to every stream and replace it's manager.
|
||||
|
||||
if (opcodes != nullptr) {
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name);
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", PathManager::Instance()->GetPatchPath(), name);
|
||||
if (!opcodes->ReloadOpcodes(opfile.c_str())) {
|
||||
LogNetcode("[OPCODES] Error reloading opcodes file [{}] for patch [{}]", opfile.c_str(), name);
|
||||
return;
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace RoF2
|
||||
//create our opcode manager if we havent already
|
||||
if (opcodes == nullptr) {
|
||||
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name);
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", PathManager::Instance()->GetPatchPath(), name);
|
||||
|
||||
//load up the opcode manager.
|
||||
//TODO: figure out how to support shared memory with multiple patches...
|
||||
@@ -123,7 +123,7 @@ namespace RoF2
|
||||
//we need to go to every stream and replace it's manager.
|
||||
|
||||
if (opcodes != nullptr) {
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name);
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", PathManager::Instance()->GetPatchPath(), name);
|
||||
if (!opcodes->ReloadOpcodes(opfile.c_str())) {
|
||||
LogNetcode("[OPCODES] Error reloading opcodes file [{}] for patch [{}]", opfile.c_str(), name);
|
||||
return;
|
||||
@@ -4688,7 +4688,7 @@ namespace RoF2
|
||||
Bitfields->linkdead = 0;
|
||||
Bitfields->showhelm = emu->showhelm;
|
||||
Bitfields->trader = emu->trader ? 1 : 0;
|
||||
Bitfields->targetable = 1;
|
||||
Bitfields->targetable = emu->NPC ? emu->untargetable : 1;
|
||||
Bitfields->targetable_with_hotkey = emu->targetable_with_hotkey ? 1 : 0;
|
||||
Bitfields->showname = ShowName;
|
||||
|
||||
@@ -4839,13 +4839,13 @@ namespace RoF2
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // FindBits MQ2 name
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // FindBits MQ2 name
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->PlayerState);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // NpcTintIndex
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // PrimaryTintIndex
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // SecondaryTintIndex
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // These do something with OP_WeaponEquip1
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // ^
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->npc_tint_id); // NpcTintIndex
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // PrimaryTintIndex
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // SecondaryTintIndex
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // These do something with OP_WeaponEquip1
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // ^
|
||||
|
||||
if ((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) ||
|
||||
(emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin)
|
||||
|
||||
+13
-13
@@ -72,7 +72,7 @@ namespace SoD
|
||||
{
|
||||
//create our opcode manager if we havent already
|
||||
if (opcodes == nullptr) {
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name);
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", PathManager::Instance()->GetPatchPath(), name);
|
||||
//load up the opcode manager.
|
||||
//TODO: figure out how to support shared memory with multiple patches...
|
||||
opcodes = new RegularOpcodeManager();
|
||||
@@ -113,7 +113,7 @@ namespace SoD
|
||||
//we need to go to every stream and replace it's manager.
|
||||
|
||||
if (opcodes != nullptr) {
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name);
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", PathManager::Instance()->GetPatchPath(), name);
|
||||
if (!opcodes->ReloadOpcodes(opfile.c_str())) {
|
||||
LogNetcode("[OPCODES] Error reloading opcodes file [{}] for patch [{}]", opfile.c_str(), name);
|
||||
return;
|
||||
@@ -3966,12 +3966,12 @@ namespace SoD
|
||||
SoDSlot = serverSlot - 2;
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invbag::GENERAL_BAGS_8_END && serverSlot >= EQ::invbag::GENERAL_BAGS_BEGIN) {
|
||||
SoDSlot = serverSlot + 11;
|
||||
else if (serverSlot <= EQ::invbag::GENERAL_BAGS_END && serverSlot >= EQ::invbag::GENERAL_BAGS_BEGIN) {
|
||||
SoDSlot = serverSlot - (EQ::invbag::GENERAL_BAGS_BEGIN - invbag::GENERAL_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((serverSlot - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT));;
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invbag::CURSOR_BAG_END && serverSlot >= EQ::invbag::CURSOR_BAG_BEGIN) {
|
||||
SoDSlot = serverSlot - 9;
|
||||
SoDSlot = serverSlot - (EQ::invbag::CURSOR_BAG_BEGIN - invbag::CURSOR_BAG_BEGIN);
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invslot::TRIBUTE_END && serverSlot >= EQ::invslot::TRIBUTE_BEGIN) {
|
||||
@@ -3991,7 +3991,7 @@ namespace SoD
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invbag::BANK_BAGS_END && serverSlot >= EQ::invbag::BANK_BAGS_BEGIN) {
|
||||
SoDSlot = serverSlot + 1;
|
||||
SoDSlot = serverSlot - (EQ::invbag::BANK_BAGS_BEGIN - invbag::BANK_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((serverSlot - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT));;
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invslot::SHARED_BANK_END && serverSlot >= EQ::invslot::SHARED_BANK_BEGIN) {
|
||||
@@ -3999,7 +3999,7 @@ namespace SoD
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invbag::SHARED_BANK_BAGS_END && serverSlot >= EQ::invbag::SHARED_BANK_BAGS_BEGIN) {
|
||||
SoDSlot = serverSlot + 1;
|
||||
SoDSlot = serverSlot - (EQ::invbag::SHARED_BANK_BAGS_BEGIN - invbag::SHARED_BANK_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((serverSlot - EQ::invbag::SHARED_BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT));
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invslot::TRADE_END && serverSlot >= EQ::invslot::TRADE_BEGIN) {
|
||||
@@ -4007,7 +4007,7 @@ namespace SoD
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invbag::TRADE_BAGS_END && serverSlot >= EQ::invbag::TRADE_BAGS_BEGIN) {
|
||||
SoDSlot = serverSlot;
|
||||
SoDSlot = serverSlot - (EQ::invbag::TRADE_BAGS_BEGIN - invbag::TRADE_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((serverSlot - EQ::invbag::TRADE_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT));
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invslot::WORLD_END && serverSlot >= EQ::invslot::WORLD_BEGIN) {
|
||||
@@ -4049,11 +4049,11 @@ namespace SoD
|
||||
}
|
||||
|
||||
else if (sod_slot <= invbag::GENERAL_BAGS_END && sod_slot >= invbag::GENERAL_BAGS_BEGIN) {
|
||||
server_slot = sod_slot - 11;
|
||||
server_slot = sod_slot + (EQ::invbag::GENERAL_BAGS_BEGIN - invbag::GENERAL_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((sod_slot - invbag::GENERAL_BAGS_BEGIN) / invbag::SLOT_COUNT));
|
||||
}
|
||||
|
||||
else if (sod_slot <= invbag::CURSOR_BAG_END && sod_slot >= invbag::CURSOR_BAG_BEGIN) {
|
||||
server_slot = sod_slot + 9;
|
||||
server_slot = sod_slot + (EQ::invbag::CURSOR_BAG_BEGIN - invbag::CURSOR_BAG_BEGIN);
|
||||
}
|
||||
|
||||
else if (sod_slot <= invslot::TRIBUTE_END && sod_slot >= invslot::TRIBUTE_BEGIN) {
|
||||
@@ -4073,7 +4073,7 @@ namespace SoD
|
||||
}
|
||||
|
||||
else if (sod_slot <= invbag::BANK_BAGS_END && sod_slot >= invbag::BANK_BAGS_BEGIN) {
|
||||
server_slot = sod_slot - 1;
|
||||
server_slot = sod_slot + (EQ::invbag::BANK_BAGS_BEGIN - invbag::BANK_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((sod_slot - invbag::BANK_BAGS_BEGIN) / invbag::SLOT_COUNT));
|
||||
}
|
||||
|
||||
else if (sod_slot <= invslot::SHARED_BANK_END && sod_slot >= invslot::SHARED_BANK_BEGIN) {
|
||||
@@ -4081,7 +4081,7 @@ namespace SoD
|
||||
}
|
||||
|
||||
else if (sod_slot <= invbag::SHARED_BANK_BAGS_END && sod_slot >= invbag::SHARED_BANK_BAGS_BEGIN) {
|
||||
server_slot = sod_slot - 1;
|
||||
server_slot = sod_slot + (EQ::invbag::SHARED_BANK_BAGS_BEGIN - invbag::SHARED_BANK_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((sod_slot - invbag::SHARED_BANK_BAGS_BEGIN) / invbag::SLOT_COUNT));
|
||||
}
|
||||
|
||||
else if (sod_slot <= invslot::TRADE_END && sod_slot >= invslot::TRADE_BEGIN) {
|
||||
@@ -4089,7 +4089,7 @@ namespace SoD
|
||||
}
|
||||
|
||||
else if (sod_slot <= invbag::TRADE_BAGS_END && sod_slot >= invbag::TRADE_BAGS_BEGIN) {
|
||||
server_slot = sod_slot;
|
||||
server_slot = sod_slot + (EQ::invbag::TRADE_BAGS_BEGIN - invbag::TRADE_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((sod_slot - invbag::TRADE_BAGS_BEGIN) / invbag::SLOT_COUNT));
|
||||
}
|
||||
|
||||
else if (sod_slot <= invslot::WORLD_END && sod_slot >= invslot::WORLD_BEGIN) {
|
||||
|
||||
+13
-13
@@ -71,7 +71,7 @@ namespace SoF
|
||||
{
|
||||
//create our opcode manager if we havent already
|
||||
if (opcodes == nullptr) {
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name);
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", PathManager::Instance()->GetPatchPath(), name);
|
||||
//load up the opcode manager.
|
||||
//TODO: figure out how to support shared memory with multiple patches...
|
||||
opcodes = new RegularOpcodeManager();
|
||||
@@ -110,7 +110,7 @@ namespace SoF
|
||||
//we need to go to every stream and replace it's manager.
|
||||
|
||||
if (opcodes != nullptr) {
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name);
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", PathManager::Instance()->GetPatchPath(), name);
|
||||
if (!opcodes->ReloadOpcodes(opfile.c_str())) {
|
||||
LogNetcode("[OPCODES] Error reloading opcodes file [{}] for patch [{}]", opfile.c_str(), name);
|
||||
return;
|
||||
@@ -3355,12 +3355,12 @@ namespace SoF
|
||||
sof_slot = server_slot - 2;
|
||||
}
|
||||
|
||||
else if (server_slot <= EQ::invbag::GENERAL_BAGS_8_END && server_slot >= EQ::invbag::GENERAL_BAGS_BEGIN) {
|
||||
sof_slot = server_slot + 11;
|
||||
else if (server_slot <= EQ::invbag::GENERAL_BAGS_END && server_slot >= EQ::invbag::GENERAL_BAGS_BEGIN) {
|
||||
sof_slot = server_slot - (EQ::invbag::GENERAL_BAGS_BEGIN - invbag::GENERAL_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((server_slot - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT));;
|
||||
}
|
||||
|
||||
else if (server_slot <= EQ::invbag::CURSOR_BAG_END && server_slot >= EQ::invbag::CURSOR_BAG_BEGIN) {
|
||||
sof_slot = server_slot - 9;
|
||||
sof_slot = server_slot - (EQ::invbag::CURSOR_BAG_BEGIN - invbag::CURSOR_BAG_BEGIN);
|
||||
}
|
||||
|
||||
else if (server_slot <= EQ::invslot::TRIBUTE_END && server_slot >= EQ::invslot::TRIBUTE_BEGIN) {
|
||||
@@ -3380,7 +3380,7 @@ namespace SoF
|
||||
}
|
||||
|
||||
else if (server_slot <= EQ::invbag::BANK_BAGS_END && server_slot >= EQ::invbag::BANK_BAGS_BEGIN) {
|
||||
sof_slot = server_slot + 1;
|
||||
sof_slot = server_slot - (EQ::invbag::BANK_BAGS_BEGIN - invbag::BANK_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((server_slot - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT));
|
||||
}
|
||||
|
||||
else if (server_slot <= EQ::invslot::SHARED_BANK_END && server_slot >= EQ::invslot::SHARED_BANK_BEGIN) {
|
||||
@@ -3388,7 +3388,7 @@ namespace SoF
|
||||
}
|
||||
|
||||
else if (server_slot <= EQ::invbag::SHARED_BANK_BAGS_END && server_slot >= EQ::invbag::SHARED_BANK_BAGS_BEGIN) {
|
||||
sof_slot = server_slot + 1;
|
||||
sof_slot = server_slot - (EQ::invbag::SHARED_BANK_BAGS_BEGIN - invbag::SHARED_BANK_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((server_slot - EQ::invbag::SHARED_BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT));
|
||||
}
|
||||
|
||||
else if (server_slot <= EQ::invslot::TRADE_END && server_slot >= EQ::invslot::TRADE_BEGIN) {
|
||||
@@ -3396,7 +3396,7 @@ namespace SoF
|
||||
}
|
||||
|
||||
else if (server_slot <= EQ::invbag::TRADE_BAGS_END && server_slot >= EQ::invbag::TRADE_BAGS_BEGIN) {
|
||||
sof_slot = server_slot;
|
||||
sof_slot = server_slot - (EQ::invbag::TRADE_BAGS_BEGIN - invbag::TRADE_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((server_slot - EQ::invbag::TRADE_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT));
|
||||
}
|
||||
|
||||
else if (server_slot <= EQ::invslot::WORLD_END && server_slot >= EQ::invslot::WORLD_BEGIN) {
|
||||
@@ -3442,11 +3442,11 @@ namespace SoF
|
||||
}
|
||||
|
||||
else if (sof_slot <= invbag::GENERAL_BAGS_END && sof_slot >= invbag::GENERAL_BAGS_BEGIN) {
|
||||
server_slot = sof_slot - 11;
|
||||
server_slot = sof_slot + (EQ::invbag::GENERAL_BAGS_BEGIN - invbag::GENERAL_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((sof_slot - invbag::GENERAL_BAGS_BEGIN) / invbag::SLOT_COUNT));;
|
||||
}
|
||||
|
||||
else if (sof_slot <= invbag::CURSOR_BAG_END && sof_slot >= invbag::CURSOR_BAG_BEGIN) {
|
||||
server_slot = sof_slot + 9;
|
||||
server_slot = sof_slot + (EQ::invbag::CURSOR_BAG_BEGIN - invbag::CURSOR_BAG_BEGIN);
|
||||
}
|
||||
|
||||
else if (sof_slot <= invslot::TRIBUTE_END && sof_slot >= invslot::TRIBUTE_BEGIN) {
|
||||
@@ -3466,7 +3466,7 @@ namespace SoF
|
||||
}
|
||||
|
||||
else if (sof_slot <= invbag::BANK_BAGS_END && sof_slot >= invbag::BANK_BAGS_BEGIN) {
|
||||
server_slot = sof_slot - 1;
|
||||
server_slot = sof_slot + (EQ::invbag::BANK_BAGS_BEGIN - invbag::BANK_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((sof_slot - invbag::BANK_BAGS_BEGIN) / invbag::SLOT_COUNT));;
|
||||
}
|
||||
|
||||
else if (sof_slot <= invslot::SHARED_BANK_END && sof_slot >= invslot::SHARED_BANK_BEGIN) {
|
||||
@@ -3474,7 +3474,7 @@ namespace SoF
|
||||
}
|
||||
|
||||
else if (sof_slot <= invbag::SHARED_BANK_BAGS_END && sof_slot >= invbag::SHARED_BANK_BAGS_BEGIN) {
|
||||
server_slot = sof_slot - 1;
|
||||
server_slot = sof_slot + (EQ::invbag::SHARED_BANK_BAGS_BEGIN - invbag::SHARED_BANK_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((sof_slot - invbag::SHARED_BANK_BAGS_BEGIN) / invbag::SLOT_COUNT));;
|
||||
}
|
||||
|
||||
else if (sof_slot <= invslot::TRADE_END && sof_slot >= invslot::TRADE_BEGIN) {
|
||||
@@ -3482,7 +3482,7 @@ namespace SoF
|
||||
}
|
||||
|
||||
else if (sof_slot <= invbag::TRADE_BAGS_END && sof_slot >= invbag::TRADE_BAGS_BEGIN) {
|
||||
server_slot = sof_slot;
|
||||
server_slot = sof_slot + (EQ::invbag::TRADE_BAGS_BEGIN - invbag::TRADE_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((sof_slot - invbag::TRADE_BAGS_BEGIN) / invbag::SLOT_COUNT));;
|
||||
}
|
||||
|
||||
else if (sof_slot <= invslot::WORLD_END && sof_slot >= invslot::WORLD_BEGIN) {
|
||||
|
||||
+14
-14
@@ -73,7 +73,7 @@ namespace Titanium
|
||||
auto Config = EQEmuConfig::get();
|
||||
//create our opcode manager if we havent already
|
||||
if (opcodes == nullptr) {
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name);
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", PathManager::Instance()->GetPatchPath(), name);
|
||||
//load up the opcode manager.
|
||||
//TODO: figure out how to support shared memory with multiple patches...
|
||||
opcodes = new RegularOpcodeManager();
|
||||
@@ -114,7 +114,7 @@ namespace Titanium
|
||||
//we need to go to every stream and replace it's manager.
|
||||
|
||||
if (opcodes != nullptr) {
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name);
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", PathManager::Instance()->GetPatchPath(), name);
|
||||
if (!opcodes->ReloadOpcodes(opfile.c_str())) {
|
||||
LogNetcode("[OPCODES] Error reloading opcodes file [{}] for patch [{}]", opfile.c_str(), name);
|
||||
return;
|
||||
@@ -3596,12 +3596,12 @@ namespace Titanium
|
||||
else if (server_slot == (EQ::invslot::POSSESSIONS_COUNT + EQ::invslot::slotAmmo)) {
|
||||
titanium_slot = server_slot - 4;
|
||||
}
|
||||
else if (server_slot <= EQ::invbag::GENERAL_BAGS_8_END &&
|
||||
else if (server_slot <= EQ::invbag::GENERAL_BAGS_END &&
|
||||
server_slot >= EQ::invbag::GENERAL_BAGS_BEGIN) {
|
||||
titanium_slot = server_slot;
|
||||
titanium_slot = server_slot - (EQ::invbag::GENERAL_BAGS_BEGIN - invbag::GENERAL_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((server_slot - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT));
|
||||
}
|
||||
else if (server_slot <= EQ::invbag::CURSOR_BAG_END && server_slot >= EQ::invbag::CURSOR_BAG_BEGIN) {
|
||||
titanium_slot = server_slot - 20;
|
||||
titanium_slot = server_slot - (EQ::invbag::CURSOR_BAG_BEGIN - invbag::CURSOR_BAG_BEGIN);
|
||||
}
|
||||
else if (server_slot <= EQ::invslot::TRIBUTE_END && server_slot >= EQ::invslot::TRIBUTE_BEGIN) {
|
||||
titanium_slot = server_slot;
|
||||
@@ -3616,21 +3616,21 @@ namespace Titanium
|
||||
else if (server_slot <= EQ::invslot::BANK_END && server_slot >= EQ::invslot::BANK_BEGIN) {
|
||||
titanium_slot = server_slot;
|
||||
}
|
||||
else if (server_slot <= EQ::invbag::BANK_BAGS_16_END && server_slot >= EQ::invbag::BANK_BAGS_BEGIN) {
|
||||
titanium_slot = server_slot;
|
||||
else if (server_slot <= EQ::invbag::BANK_BAGS_END && server_slot >= EQ::invbag::BANK_BAGS_BEGIN) {
|
||||
titanium_slot = server_slot - (EQ::invbag::BANK_BAGS_BEGIN - invbag::BANK_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((server_slot - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT));
|
||||
}
|
||||
else if (server_slot <= EQ::invslot::SHARED_BANK_END && server_slot >= EQ::invslot::SHARED_BANK_BEGIN) {
|
||||
titanium_slot = server_slot;
|
||||
}
|
||||
else if (server_slot <= EQ::invbag::SHARED_BANK_BAGS_END &&
|
||||
server_slot >= EQ::invbag::SHARED_BANK_BAGS_BEGIN) {
|
||||
titanium_slot = server_slot;
|
||||
titanium_slot = server_slot - (EQ::invbag::SHARED_BANK_BAGS_BEGIN - invbag::SHARED_BANK_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((server_slot - EQ::invbag::SHARED_BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT));
|
||||
}
|
||||
else if (server_slot <= EQ::invslot::TRADE_END && server_slot >= EQ::invslot::TRADE_BEGIN) {
|
||||
titanium_slot = server_slot;
|
||||
}
|
||||
else if (server_slot <= EQ::invbag::TRADE_BAGS_END && server_slot >= EQ::invbag::TRADE_BAGS_BEGIN) {
|
||||
titanium_slot = server_slot;
|
||||
titanium_slot = server_slot - (EQ::invbag::TRADE_BAGS_BEGIN - invbag::TRADE_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((server_slot - EQ::invbag::TRADE_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT));
|
||||
}
|
||||
else if (server_slot <= EQ::invslot::WORLD_END && server_slot >= EQ::invslot::WORLD_BEGIN) {
|
||||
titanium_slot = server_slot;
|
||||
@@ -3687,10 +3687,10 @@ namespace Titanium
|
||||
server_slot = titanium_slot + 4;
|
||||
}
|
||||
else if (titanium_slot <= invbag::GENERAL_BAGS_END && titanium_slot >= invbag::GENERAL_BAGS_BEGIN) {
|
||||
server_slot = titanium_slot;
|
||||
server_slot = titanium_slot + (EQ::invbag::GENERAL_BAGS_BEGIN - invbag::GENERAL_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((titanium_slot - invbag::GENERAL_BAGS_BEGIN) / invbag::SLOT_COUNT));
|
||||
}
|
||||
else if (titanium_slot <= invbag::CURSOR_BAG_END && titanium_slot >= invbag::CURSOR_BAG_BEGIN) {
|
||||
server_slot = titanium_slot + 20;
|
||||
server_slot = titanium_slot + (EQ::invbag::CURSOR_BAG_BEGIN - invbag::CURSOR_BAG_BEGIN);
|
||||
}
|
||||
else if (titanium_slot <= invslot::TRIBUTE_END && titanium_slot >= invslot::TRIBUTE_BEGIN) {
|
||||
server_slot = titanium_slot;
|
||||
@@ -3705,19 +3705,19 @@ namespace Titanium
|
||||
server_slot = titanium_slot;
|
||||
}
|
||||
else if (titanium_slot <= invbag::BANK_BAGS_END && titanium_slot >= invbag::BANK_BAGS_BEGIN) {
|
||||
server_slot = titanium_slot;
|
||||
server_slot = titanium_slot + (EQ::invbag::BANK_BAGS_BEGIN - invbag::BANK_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((titanium_slot - invbag::BANK_BAGS_BEGIN) / invbag::SLOT_COUNT));
|
||||
}
|
||||
else if (titanium_slot <= invslot::SHARED_BANK_END && titanium_slot >= invslot::SHARED_BANK_BEGIN) {
|
||||
server_slot = titanium_slot;
|
||||
}
|
||||
else if (titanium_slot <= invbag::SHARED_BANK_BAGS_END && titanium_slot >= invbag::SHARED_BANK_BAGS_BEGIN) {
|
||||
server_slot = titanium_slot;
|
||||
server_slot = titanium_slot + (EQ::invbag::SHARED_BANK_BAGS_BEGIN - invbag::SHARED_BANK_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((titanium_slot - invbag::SHARED_BANK_BAGS_BEGIN) / invbag::SLOT_COUNT));
|
||||
}
|
||||
else if (titanium_slot <= invslot::TRADE_END && titanium_slot >= invslot::TRADE_BEGIN) {
|
||||
server_slot = titanium_slot;
|
||||
}
|
||||
else if (titanium_slot <= invbag::TRADE_BAGS_END && titanium_slot >= invbag::TRADE_BAGS_BEGIN) {
|
||||
server_slot = titanium_slot;
|
||||
server_slot = titanium_slot + (EQ::invbag::TRADE_BAGS_BEGIN - invbag::TRADE_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((titanium_slot - invbag::TRADE_BAGS_BEGIN) / invbag::SLOT_COUNT));
|
||||
}
|
||||
else if (titanium_slot <= invslot::WORLD_END && titanium_slot >= invslot::WORLD_BEGIN) {
|
||||
server_slot = titanium_slot;
|
||||
|
||||
+13
-13
@@ -76,7 +76,7 @@ namespace UF
|
||||
{
|
||||
//create our opcode manager if we havent already
|
||||
if (opcodes == nullptr) {
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name);
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", PathManager::Instance()->GetPatchPath(), name);
|
||||
//load up the opcode manager.
|
||||
//TODO: figure out how to support shared memory with multiple patches...
|
||||
opcodes = new RegularOpcodeManager();
|
||||
@@ -117,7 +117,7 @@ namespace UF
|
||||
//we need to go to every stream and replace it's manager.
|
||||
|
||||
if (opcodes != nullptr) {
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name);
|
||||
std::string opfile = fmt::format("{}/patch_{}.conf", PathManager::Instance()->GetPatchPath(), name);
|
||||
if (!opcodes->ReloadOpcodes(opfile.c_str())) {
|
||||
LogNetcode("[OPCODES] Error reloading opcodes file [{}] for patch [{}]", opfile.c_str(), name);
|
||||
return;
|
||||
@@ -4908,12 +4908,12 @@ namespace UF
|
||||
UFSlot = serverSlot - 2;
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invbag::GENERAL_BAGS_8_END && serverSlot >= EQ::invbag::GENERAL_BAGS_BEGIN) {
|
||||
UFSlot = serverSlot + 11;
|
||||
else if (serverSlot <= EQ::invbag::GENERAL_BAGS_END && serverSlot >= EQ::invbag::GENERAL_BAGS_BEGIN) {
|
||||
UFSlot = serverSlot - (EQ::invbag::GENERAL_BAGS_BEGIN - invbag::GENERAL_BAGS_BEGIN)/*3748*/ - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((serverSlot - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT)); // + 11;
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invbag::CURSOR_BAG_END && serverSlot >= EQ::invbag::CURSOR_BAG_BEGIN) {
|
||||
UFSlot = serverSlot - 9;
|
||||
UFSlot = serverSlot - (EQ::invbag::CURSOR_BAG_BEGIN - invbag::CURSOR_BAG_BEGIN)/*5668*/; // - 9;
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invslot::TRIBUTE_END && serverSlot >= EQ::invslot::TRIBUTE_BEGIN) {
|
||||
@@ -4933,7 +4933,7 @@ namespace UF
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invbag::BANK_BAGS_END && serverSlot >= EQ::invbag::BANK_BAGS_BEGIN) {
|
||||
UFSlot = serverSlot + 1;
|
||||
UFSlot = serverSlot - (EQ::invbag::BANK_BAGS_BEGIN - invbag::BANK_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((serverSlot - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT)); // + 1;
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invslot::SHARED_BANK_END && serverSlot >= EQ::invslot::SHARED_BANK_BEGIN) {
|
||||
@@ -4941,7 +4941,7 @@ namespace UF
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invbag::SHARED_BANK_BAGS_END && serverSlot >= EQ::invbag::SHARED_BANK_BAGS_BEGIN) {
|
||||
UFSlot = serverSlot + 1;
|
||||
UFSlot = serverSlot - (EQ::invbag::SHARED_BANK_BAGS_BEGIN - invbag::SHARED_BANK_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((serverSlot - EQ::invbag::SHARED_BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT)); // + 1;
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invslot::TRADE_END && serverSlot >= EQ::invslot::TRADE_BEGIN) {
|
||||
@@ -4949,7 +4949,7 @@ namespace UF
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invbag::TRADE_BAGS_END && serverSlot >= EQ::invbag::TRADE_BAGS_BEGIN) {
|
||||
UFSlot = serverSlot;
|
||||
UFSlot = serverSlot - (EQ::invbag::TRADE_BAGS_BEGIN - invbag::TRADE_BAGS_BEGIN) - ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((serverSlot - EQ::invbag::TRADE_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT)); // + 0;
|
||||
}
|
||||
|
||||
else if (serverSlot <= EQ::invslot::WORLD_END && serverSlot >= EQ::invslot::WORLD_BEGIN) {
|
||||
@@ -4991,11 +4991,11 @@ namespace UF
|
||||
}
|
||||
|
||||
else if (ufSlot <= invbag::GENERAL_BAGS_END && ufSlot >= invbag::GENERAL_BAGS_BEGIN) {
|
||||
ServerSlot = ufSlot - 11;
|
||||
ServerSlot = ufSlot + (EQ::invbag::GENERAL_BAGS_BEGIN - invbag::GENERAL_BAGS_BEGIN)/*3748*/ + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((ufSlot - invbag::GENERAL_BAGS_BEGIN) / invbag::SLOT_COUNT)); // - 11;
|
||||
}
|
||||
|
||||
else if (ufSlot <= invbag::CURSOR_BAG_END && ufSlot >= invbag::CURSOR_BAG_BEGIN) {
|
||||
ServerSlot = ufSlot + 9;
|
||||
ServerSlot = ufSlot + (EQ::invbag::CURSOR_BAG_BEGIN - invbag::CURSOR_BAG_BEGIN)/*5668*/; // + 9;
|
||||
}
|
||||
|
||||
else if (ufSlot <= invslot::TRIBUTE_END && ufSlot >= invslot::TRIBUTE_BEGIN) {
|
||||
@@ -5015,7 +5015,7 @@ namespace UF
|
||||
}
|
||||
|
||||
else if (ufSlot <= invbag::BANK_BAGS_END && ufSlot >= invbag::BANK_BAGS_BEGIN) {
|
||||
ServerSlot = ufSlot - 1;
|
||||
ServerSlot = ufSlot + (EQ::invbag::BANK_BAGS_BEGIN - invbag::BANK_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((ufSlot - invbag::BANK_BAGS_BEGIN) / invbag::SLOT_COUNT)); // - 1;
|
||||
}
|
||||
|
||||
else if (ufSlot <= invslot::SHARED_BANK_END && ufSlot >= invslot::SHARED_BANK_BEGIN) {
|
||||
@@ -5023,7 +5023,7 @@ namespace UF
|
||||
}
|
||||
|
||||
else if (ufSlot <= invbag::SHARED_BANK_BAGS_END && ufSlot >= invbag::SHARED_BANK_BAGS_BEGIN) {
|
||||
ServerSlot = ufSlot - 1;
|
||||
ServerSlot = ufSlot + (EQ::invbag::SHARED_BANK_BAGS_BEGIN - invbag::SHARED_BANK_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((ufSlot - invbag::SHARED_BANK_BAGS_BEGIN) / invbag::SLOT_COUNT)); // - 1;
|
||||
}
|
||||
|
||||
else if (ufSlot <= invslot::TRADE_END && ufSlot >= invslot::TRADE_BEGIN) {
|
||||
@@ -5031,7 +5031,7 @@ namespace UF
|
||||
}
|
||||
|
||||
else if (ufSlot <= invbag::TRADE_BAGS_END && ufSlot >= invbag::TRADE_BAGS_BEGIN) {
|
||||
ServerSlot = ufSlot;
|
||||
ServerSlot = ufSlot + (EQ::invbag::TRADE_BAGS_BEGIN - invbag::TRADE_BAGS_BEGIN) + ((EQ::invbag::SLOT_COUNT - invbag::SLOT_COUNT) * ((ufSlot - invbag::TRADE_BAGS_BEGIN) / invbag::SLOT_COUNT)); // - 0;
|
||||
}
|
||||
|
||||
else if (ufSlot <= invslot::WORLD_END && ufSlot >= invslot::WORLD_BEGIN) {
|
||||
|
||||
+44
-23
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
void PathManager::LoadPaths()
|
||||
void PathManager::Init()
|
||||
{
|
||||
m_server_path = File::FindEqemuConfigPath();
|
||||
|
||||
@@ -48,10 +48,23 @@ void PathManager::LoadPaths()
|
||||
return dir;
|
||||
};
|
||||
|
||||
auto load_many_paths_fallback = [&](const std::vector<std::string>& dirs, const std::string& fallback, std::vector<std::string>& target) {
|
||||
target.clear();
|
||||
if (!dirs.empty()) {
|
||||
for (const auto& path : dirs) {
|
||||
target.push_back(resolve_path(path));
|
||||
}
|
||||
} else {
|
||||
target.push_back(resolve_path(fallback));
|
||||
}
|
||||
};
|
||||
|
||||
load_many_paths_fallback(c->GetQuestDirectories(), c->QuestDir, m_quests_paths);
|
||||
load_many_paths_fallback(c->GetPluginsDirectories(), c->PluginDir, m_plugin_paths);
|
||||
load_many_paths_fallback(c->GetLuaModuleDirectories(), c->LuaModuleDir, m_lua_module_paths);
|
||||
|
||||
// resolve all paths
|
||||
m_maps_path = resolve_path(c->MapDir, {"maps", "Maps"});
|
||||
m_quests_path = resolve_path(c->QuestDir);
|
||||
m_plugins_path = resolve_path(c->PluginDir);
|
||||
m_lua_modules_path = resolve_path(c->LuaModuleDir);
|
||||
m_lua_mods_path = resolve_path("mods");
|
||||
m_patch_path = resolve_path(c->PatchDir);
|
||||
m_opcode_path = resolve_path(c->OpcodeDir);
|
||||
@@ -62,13 +75,10 @@ void PathManager::LoadPaths()
|
||||
std::vector<std::pair<std::string, std::string>> paths = {
|
||||
{"server", m_server_path},
|
||||
{"logs", m_log_path},
|
||||
{"lua mods", m_lua_mods_path},
|
||||
{"lua_modules", m_lua_modules_path},
|
||||
{"maps", m_maps_path},
|
||||
{"lua mods", m_lua_mods_path},
|
||||
{"patches", m_patch_path},
|
||||
{"opcode", m_opcode_path},
|
||||
{"plugins", m_plugins_path},
|
||||
{"quests", m_quests_path},
|
||||
{"shared_memory", m_shared_memory_path}
|
||||
};
|
||||
|
||||
@@ -83,6 +93,17 @@ void PathManager::LoadPaths()
|
||||
LogInfo("{:>{}} > [{:<{}}]", name, name_width, in_path, path_width);
|
||||
}
|
||||
}
|
||||
|
||||
auto log_paths = [&](const std::string& label, const std::vector<std::string>& paths) {
|
||||
if (!paths.empty()) {
|
||||
LogInfo("{:>{}} > [{:<{}}]", label, name_width - 1, Strings::Join(paths, ";"), path_width);
|
||||
}
|
||||
};
|
||||
|
||||
log_paths("quests", m_quests_paths);
|
||||
log_paths("plugins", m_plugin_paths);
|
||||
log_paths("lua_modules", m_lua_module_paths);
|
||||
|
||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
||||
}
|
||||
|
||||
@@ -96,21 +117,26 @@ const std::string &PathManager::GetMapsPath() const
|
||||
return m_maps_path;
|
||||
}
|
||||
|
||||
const std::string &PathManager::GetQuestsPath() const
|
||||
{
|
||||
return m_quests_path;
|
||||
}
|
||||
|
||||
const std::string &PathManager::GetPluginsPath() const
|
||||
{
|
||||
return m_plugins_path;
|
||||
}
|
||||
|
||||
const std::string &PathManager::GetSharedMemoryPath() const
|
||||
{
|
||||
return m_shared_memory_path;
|
||||
}
|
||||
|
||||
std::vector<std::string> PathManager::GetQuestPaths() const
|
||||
{
|
||||
return m_quests_paths;
|
||||
}
|
||||
|
||||
std::vector<std::string> PathManager::GetPluginPaths() const
|
||||
{
|
||||
return m_plugin_paths;
|
||||
}
|
||||
|
||||
std::vector<std::string> PathManager::GetLuaModulePaths() const
|
||||
{
|
||||
return m_lua_module_paths;
|
||||
}
|
||||
|
||||
const std::string &PathManager::GetLogPath() const
|
||||
{
|
||||
return m_log_path;
|
||||
@@ -126,11 +152,6 @@ const std::string &PathManager::GetOpcodePath() const
|
||||
return m_opcode_path;
|
||||
}
|
||||
|
||||
const std::string &PathManager::GetLuaModulesPath() const
|
||||
{
|
||||
return m_lua_modules_path;
|
||||
}
|
||||
|
||||
const std::string &PathManager::GetLuaModsPath() const
|
||||
{
|
||||
return m_lua_mods_path;
|
||||
|
||||
+25
-15
@@ -3,10 +3,17 @@
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class PathManager {
|
||||
public:
|
||||
void LoadPaths();
|
||||
void Init();
|
||||
|
||||
static PathManager *Instance()
|
||||
{
|
||||
static PathManager instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &GetLogPath() const;
|
||||
[[nodiscard]] const std::string &GetLuaModsPath() const;
|
||||
@@ -14,24 +21,27 @@ public:
|
||||
[[nodiscard]] const std::string &GetMapsPath() const;
|
||||
[[nodiscard]] const std::string &GetPatchPath() const;
|
||||
[[nodiscard]] const std::string &GetOpcodePath() const;
|
||||
[[nodiscard]] const std::string &GetPluginsPath() const;
|
||||
[[nodiscard]] const std::string &GetQuestsPath() const;
|
||||
[[nodiscard]] const std::string &GetServerPath() const;
|
||||
[[nodiscard]] const std::string &GetSharedMemoryPath() const;
|
||||
[[nodiscard]] std::vector<std::string> GetQuestPaths() const;
|
||||
[[nodiscard]] std::vector<std::string> GetPluginPaths() const;
|
||||
[[nodiscard]] std::vector<std::string> GetLuaModulePaths() const;
|
||||
|
||||
private:
|
||||
std::string m_log_path;
|
||||
std::string m_lua_mods_path;
|
||||
std::string m_lua_modules_path;
|
||||
std::string m_maps_path;
|
||||
std::string m_patch_path;
|
||||
std::string m_opcode_path;
|
||||
std::string m_plugins_path;
|
||||
std::string m_quests_path;
|
||||
std::string m_server_path;
|
||||
std::string m_shared_memory_path;
|
||||
std::string m_log_path;
|
||||
std::string m_lua_mods_path;
|
||||
std::string m_maps_path;
|
||||
std::string m_patch_path;
|
||||
std::string m_opcode_path;
|
||||
std::string m_quests_path;
|
||||
std::vector<std::string> m_quests_paths;
|
||||
std::vector<std::string> m_plugin_paths;
|
||||
std::vector<std::string> m_lua_module_paths;
|
||||
|
||||
|
||||
private:
|
||||
std::string m_server_path;
|
||||
std::string m_shared_memory_path;
|
||||
};
|
||||
|
||||
extern PathManager path;
|
||||
|
||||
#endif //EQEMU_PATH_MANAGER_H
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "../types.h"
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
#include "../eqemu_logsys.h"
|
||||
#include "../eqemu_logsys_log_aliases.h"
|
||||
#include "../features.h"
|
||||
#include "../global_define.h"
|
||||
@@ -1,34 +0,0 @@
|
||||
// types
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
|
||||
// containers
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
// utilities
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
#include <fstream>
|
||||
#include <cstdio>
|
||||
|
||||
// fmt
|
||||
#include <fmt/format.h>
|
||||
|
||||
// lua
|
||||
#include "lua.hpp"
|
||||
#include <luabind/luabind.hpp>
|
||||
#include <luabind/object.hpp>
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
// Lightweight, widely used
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
// fmt
|
||||
#include <fmt/format.h>
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "process.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
std::string Process::execute(const std::string &cmd)
|
||||
{
|
||||
|
||||
@@ -116,6 +116,12 @@ namespace EQ {
|
||||
Reseed();
|
||||
}
|
||||
|
||||
static Random* Instance()
|
||||
{
|
||||
static Random instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
private:
|
||||
#ifndef BIASED_INT_DIST
|
||||
typedef std::uniform_int_distribution<int>::param_type int_param_t;
|
||||
|
||||
@@ -70,6 +70,7 @@ public:
|
||||
uint32_t gm_exp;
|
||||
uint32_t killed_by;
|
||||
uint8_t rezzable;
|
||||
std::string entity_variables;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@@ -131,6 +132,7 @@ public:
|
||||
"gm_exp",
|
||||
"killed_by",
|
||||
"rezzable",
|
||||
"entity_variables",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -188,6 +190,7 @@ public:
|
||||
"gm_exp",
|
||||
"killed_by",
|
||||
"rezzable",
|
||||
"entity_variables",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -279,6 +282,7 @@ public:
|
||||
e.gm_exp = 0;
|
||||
e.killed_by = 0;
|
||||
e.rezzable = 0;
|
||||
e.entity_variables = "";
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -366,6 +370,7 @@ public:
|
||||
e.gm_exp = row[48] ? static_cast<uint32_t>(strtoul(row[48], nullptr, 10)) : 0;
|
||||
e.killed_by = row[49] ? static_cast<uint32_t>(strtoul(row[49], nullptr, 10)) : 0;
|
||||
e.rezzable = row[50] ? static_cast<uint8_t>(strtoul(row[50], nullptr, 10)) : 0;
|
||||
e.entity_variables = row[51] ? row[51] : "";
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -449,6 +454,7 @@ public:
|
||||
v.push_back(columns[48] + " = " + std::to_string(e.gm_exp));
|
||||
v.push_back(columns[49] + " = " + std::to_string(e.killed_by));
|
||||
v.push_back(columns[50] + " = " + std::to_string(e.rezzable));
|
||||
v.push_back(columns[51] + " = '" + Strings::Escape(e.entity_variables) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -521,6 +527,7 @@ public:
|
||||
v.push_back(std::to_string(e.gm_exp));
|
||||
v.push_back(std::to_string(e.killed_by));
|
||||
v.push_back(std::to_string(e.rezzable));
|
||||
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -601,6 +608,7 @@ public:
|
||||
v.push_back(std::to_string(e.gm_exp));
|
||||
v.push_back(std::to_string(e.killed_by));
|
||||
v.push_back(std::to_string(e.rezzable));
|
||||
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -685,6 +693,7 @@ public:
|
||||
e.gm_exp = row[48] ? static_cast<uint32_t>(strtoul(row[48], nullptr, 10)) : 0;
|
||||
e.killed_by = row[49] ? static_cast<uint32_t>(strtoul(row[49], nullptr, 10)) : 0;
|
||||
e.rezzable = row[50] ? static_cast<uint8_t>(strtoul(row[50], nullptr, 10)) : 0;
|
||||
e.entity_variables = row[51] ? row[51] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -760,6 +769,7 @@ public:
|
||||
e.gm_exp = row[48] ? static_cast<uint32_t>(strtoul(row[48], nullptr, 10)) : 0;
|
||||
e.killed_by = row[49] ? static_cast<uint32_t>(strtoul(row[49], nullptr, 10)) : 0;
|
||||
e.rezzable = row[50] ? static_cast<uint8_t>(strtoul(row[50], nullptr, 10)) : 0;
|
||||
e.entity_variables = row[51] ? row[51] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -885,6 +895,7 @@ public:
|
||||
v.push_back(std::to_string(e.gm_exp));
|
||||
v.push_back(std::to_string(e.killed_by));
|
||||
v.push_back(std::to_string(e.rezzable));
|
||||
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -958,6 +969,7 @@ public:
|
||||
v.push_back(std::to_string(e.gm_exp));
|
||||
v.push_back(std::to_string(e.killed_by));
|
||||
v.push_back(std::to_string(e.rezzable));
|
||||
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
@@ -115,7 +115,8 @@ public:
|
||||
uint8_t lfg;
|
||||
std::string mailkey;
|
||||
uint8_t xtargets;
|
||||
int8_t firstlogon;
|
||||
uint8_t ingame;
|
||||
uint32_t first_login;
|
||||
uint32_t e_aa_effects;
|
||||
uint32_t e_percent_to_aa;
|
||||
uint32_t e_expended_aa_spent;
|
||||
@@ -230,7 +231,8 @@ public:
|
||||
"lfg",
|
||||
"mailkey",
|
||||
"xtargets",
|
||||
"firstlogon",
|
||||
"ingame",
|
||||
"first_login",
|
||||
"e_aa_effects",
|
||||
"e_percent_to_aa",
|
||||
"e_expended_aa_spent",
|
||||
@@ -341,7 +343,8 @@ public:
|
||||
"lfg",
|
||||
"mailkey",
|
||||
"xtargets",
|
||||
"firstlogon",
|
||||
"ingame",
|
||||
"first_login",
|
||||
"e_aa_effects",
|
||||
"e_percent_to_aa",
|
||||
"e_expended_aa_spent",
|
||||
@@ -486,7 +489,8 @@ public:
|
||||
e.lfg = 0;
|
||||
e.mailkey = "";
|
||||
e.xtargets = 5;
|
||||
e.firstlogon = 0;
|
||||
e.ingame = 0;
|
||||
e.first_login = 0;
|
||||
e.e_aa_effects = 0;
|
||||
e.e_percent_to_aa = 0;
|
||||
e.e_expended_aa_spent = 0;
|
||||
@@ -627,15 +631,16 @@ public:
|
||||
e.lfg = row[93] ? static_cast<uint8_t>(strtoul(row[93], nullptr, 10)) : 0;
|
||||
e.mailkey = row[94] ? row[94] : "";
|
||||
e.xtargets = row[95] ? static_cast<uint8_t>(strtoul(row[95], nullptr, 10)) : 5;
|
||||
e.firstlogon = row[96] ? static_cast<int8_t>(atoi(row[96])) : 0;
|
||||
e.e_aa_effects = row[97] ? static_cast<uint32_t>(strtoul(row[97], nullptr, 10)) : 0;
|
||||
e.e_percent_to_aa = row[98] ? static_cast<uint32_t>(strtoul(row[98], nullptr, 10)) : 0;
|
||||
e.e_expended_aa_spent = row[99] ? static_cast<uint32_t>(strtoul(row[99], nullptr, 10)) : 0;
|
||||
e.aa_points_spent_old = row[100] ? static_cast<uint32_t>(strtoul(row[100], nullptr, 10)) : 0;
|
||||
e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0;
|
||||
e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0;
|
||||
e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10);
|
||||
e.illusion_block = row[104] ? static_cast<uint8_t>(strtoul(row[104], nullptr, 10)) : 0;
|
||||
e.ingame = row[96] ? static_cast<uint8_t>(strtoul(row[96], nullptr, 10)) : 0;
|
||||
e.first_login = row[97] ? static_cast<uint32_t>(strtoul(row[97], nullptr, 10)) : 0;
|
||||
e.e_aa_effects = row[98] ? static_cast<uint32_t>(strtoul(row[98], nullptr, 10)) : 0;
|
||||
e.e_percent_to_aa = row[99] ? static_cast<uint32_t>(strtoul(row[99], nullptr, 10)) : 0;
|
||||
e.e_expended_aa_spent = row[100] ? static_cast<uint32_t>(strtoul(row[100], nullptr, 10)) : 0;
|
||||
e.aa_points_spent_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0;
|
||||
e.aa_points_old = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0;
|
||||
e.e_last_invsnapshot = row[103] ? static_cast<uint32_t>(strtoul(row[103], nullptr, 10)) : 0;
|
||||
e.deleted_at = strtoll(row[104] ? row[104] : "-1", nullptr, 10);
|
||||
e.illusion_block = row[105] ? static_cast<uint8_t>(strtoul(row[105], nullptr, 10)) : 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -764,15 +769,16 @@ public:
|
||||
v.push_back(columns[93] + " = " + std::to_string(e.lfg));
|
||||
v.push_back(columns[94] + " = '" + Strings::Escape(e.mailkey) + "'");
|
||||
v.push_back(columns[95] + " = " + std::to_string(e.xtargets));
|
||||
v.push_back(columns[96] + " = " + std::to_string(e.firstlogon));
|
||||
v.push_back(columns[97] + " = " + std::to_string(e.e_aa_effects));
|
||||
v.push_back(columns[98] + " = " + std::to_string(e.e_percent_to_aa));
|
||||
v.push_back(columns[99] + " = " + std::to_string(e.e_expended_aa_spent));
|
||||
v.push_back(columns[100] + " = " + std::to_string(e.aa_points_spent_old));
|
||||
v.push_back(columns[101] + " = " + std::to_string(e.aa_points_old));
|
||||
v.push_back(columns[102] + " = " + std::to_string(e.e_last_invsnapshot));
|
||||
v.push_back(columns[103] + " = FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
|
||||
v.push_back(columns[104] + " = " + std::to_string(e.illusion_block));
|
||||
v.push_back(columns[96] + " = " + std::to_string(e.ingame));
|
||||
v.push_back(columns[97] + " = " + std::to_string(e.first_login));
|
||||
v.push_back(columns[98] + " = " + std::to_string(e.e_aa_effects));
|
||||
v.push_back(columns[99] + " = " + std::to_string(e.e_percent_to_aa));
|
||||
v.push_back(columns[100] + " = " + std::to_string(e.e_expended_aa_spent));
|
||||
v.push_back(columns[101] + " = " + std::to_string(e.aa_points_spent_old));
|
||||
v.push_back(columns[102] + " = " + std::to_string(e.aa_points_old));
|
||||
v.push_back(columns[103] + " = " + std::to_string(e.e_last_invsnapshot));
|
||||
v.push_back(columns[104] + " = FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
|
||||
v.push_back(columns[105] + " = " + std::to_string(e.illusion_block));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -890,7 +896,8 @@ public:
|
||||
v.push_back(std::to_string(e.lfg));
|
||||
v.push_back("'" + Strings::Escape(e.mailkey) + "'");
|
||||
v.push_back(std::to_string(e.xtargets));
|
||||
v.push_back(std::to_string(e.firstlogon));
|
||||
v.push_back(std::to_string(e.ingame));
|
||||
v.push_back(std::to_string(e.first_login));
|
||||
v.push_back(std::to_string(e.e_aa_effects));
|
||||
v.push_back(std::to_string(e.e_percent_to_aa));
|
||||
v.push_back(std::to_string(e.e_expended_aa_spent));
|
||||
@@ -1024,7 +1031,8 @@ public:
|
||||
v.push_back(std::to_string(e.lfg));
|
||||
v.push_back("'" + Strings::Escape(e.mailkey) + "'");
|
||||
v.push_back(std::to_string(e.xtargets));
|
||||
v.push_back(std::to_string(e.firstlogon));
|
||||
v.push_back(std::to_string(e.ingame));
|
||||
v.push_back(std::to_string(e.first_login));
|
||||
v.push_back(std::to_string(e.e_aa_effects));
|
||||
v.push_back(std::to_string(e.e_percent_to_aa));
|
||||
v.push_back(std::to_string(e.e_expended_aa_spent));
|
||||
@@ -1162,15 +1170,16 @@ public:
|
||||
e.lfg = row[93] ? static_cast<uint8_t>(strtoul(row[93], nullptr, 10)) : 0;
|
||||
e.mailkey = row[94] ? row[94] : "";
|
||||
e.xtargets = row[95] ? static_cast<uint8_t>(strtoul(row[95], nullptr, 10)) : 5;
|
||||
e.firstlogon = row[96] ? static_cast<int8_t>(atoi(row[96])) : 0;
|
||||
e.e_aa_effects = row[97] ? static_cast<uint32_t>(strtoul(row[97], nullptr, 10)) : 0;
|
||||
e.e_percent_to_aa = row[98] ? static_cast<uint32_t>(strtoul(row[98], nullptr, 10)) : 0;
|
||||
e.e_expended_aa_spent = row[99] ? static_cast<uint32_t>(strtoul(row[99], nullptr, 10)) : 0;
|
||||
e.aa_points_spent_old = row[100] ? static_cast<uint32_t>(strtoul(row[100], nullptr, 10)) : 0;
|
||||
e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0;
|
||||
e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0;
|
||||
e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10);
|
||||
e.illusion_block = row[104] ? static_cast<uint8_t>(strtoul(row[104], nullptr, 10)) : 0;
|
||||
e.ingame = row[96] ? static_cast<uint8_t>(strtoul(row[96], nullptr, 10)) : 0;
|
||||
e.first_login = row[97] ? static_cast<uint32_t>(strtoul(row[97], nullptr, 10)) : 0;
|
||||
e.e_aa_effects = row[98] ? static_cast<uint32_t>(strtoul(row[98], nullptr, 10)) : 0;
|
||||
e.e_percent_to_aa = row[99] ? static_cast<uint32_t>(strtoul(row[99], nullptr, 10)) : 0;
|
||||
e.e_expended_aa_spent = row[100] ? static_cast<uint32_t>(strtoul(row[100], nullptr, 10)) : 0;
|
||||
e.aa_points_spent_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0;
|
||||
e.aa_points_old = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0;
|
||||
e.e_last_invsnapshot = row[103] ? static_cast<uint32_t>(strtoul(row[103], nullptr, 10)) : 0;
|
||||
e.deleted_at = strtoll(row[104] ? row[104] : "-1", nullptr, 10);
|
||||
e.illusion_block = row[105] ? static_cast<uint8_t>(strtoul(row[105], nullptr, 10)) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -1291,15 +1300,16 @@ public:
|
||||
e.lfg = row[93] ? static_cast<uint8_t>(strtoul(row[93], nullptr, 10)) : 0;
|
||||
e.mailkey = row[94] ? row[94] : "";
|
||||
e.xtargets = row[95] ? static_cast<uint8_t>(strtoul(row[95], nullptr, 10)) : 5;
|
||||
e.firstlogon = row[96] ? static_cast<int8_t>(atoi(row[96])) : 0;
|
||||
e.e_aa_effects = row[97] ? static_cast<uint32_t>(strtoul(row[97], nullptr, 10)) : 0;
|
||||
e.e_percent_to_aa = row[98] ? static_cast<uint32_t>(strtoul(row[98], nullptr, 10)) : 0;
|
||||
e.e_expended_aa_spent = row[99] ? static_cast<uint32_t>(strtoul(row[99], nullptr, 10)) : 0;
|
||||
e.aa_points_spent_old = row[100] ? static_cast<uint32_t>(strtoul(row[100], nullptr, 10)) : 0;
|
||||
e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0;
|
||||
e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0;
|
||||
e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10);
|
||||
e.illusion_block = row[104] ? static_cast<uint8_t>(strtoul(row[104], nullptr, 10)) : 0;
|
||||
e.ingame = row[96] ? static_cast<uint8_t>(strtoul(row[96], nullptr, 10)) : 0;
|
||||
e.first_login = row[97] ? static_cast<uint32_t>(strtoul(row[97], nullptr, 10)) : 0;
|
||||
e.e_aa_effects = row[98] ? static_cast<uint32_t>(strtoul(row[98], nullptr, 10)) : 0;
|
||||
e.e_percent_to_aa = row[99] ? static_cast<uint32_t>(strtoul(row[99], nullptr, 10)) : 0;
|
||||
e.e_expended_aa_spent = row[100] ? static_cast<uint32_t>(strtoul(row[100], nullptr, 10)) : 0;
|
||||
e.aa_points_spent_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0;
|
||||
e.aa_points_old = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0;
|
||||
e.e_last_invsnapshot = row[103] ? static_cast<uint32_t>(strtoul(row[103], nullptr, 10)) : 0;
|
||||
e.deleted_at = strtoll(row[104] ? row[104] : "-1", nullptr, 10);
|
||||
e.illusion_block = row[105] ? static_cast<uint8_t>(strtoul(row[105], nullptr, 10)) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -1470,7 +1480,8 @@ public:
|
||||
v.push_back(std::to_string(e.lfg));
|
||||
v.push_back("'" + Strings::Escape(e.mailkey) + "'");
|
||||
v.push_back(std::to_string(e.xtargets));
|
||||
v.push_back(std::to_string(e.firstlogon));
|
||||
v.push_back(std::to_string(e.ingame));
|
||||
v.push_back(std::to_string(e.first_login));
|
||||
v.push_back(std::to_string(e.e_aa_effects));
|
||||
v.push_back(std::to_string(e.e_percent_to_aa));
|
||||
v.push_back(std::to_string(e.e_expended_aa_spent));
|
||||
@@ -1597,7 +1608,8 @@ public:
|
||||
v.push_back(std::to_string(e.lfg));
|
||||
v.push_back("'" + Strings::Escape(e.mailkey) + "'");
|
||||
v.push_back(std::to_string(e.xtargets));
|
||||
v.push_back(std::to_string(e.firstlogon));
|
||||
v.push_back(std::to_string(e.ingame));
|
||||
v.push_back(std::to_string(e.first_login));
|
||||
v.push_back(std::to_string(e.e_aa_effects));
|
||||
v.push_back(std::to_string(e.e_percent_to_aa));
|
||||
v.push_back(std::to_string(e.e_expended_aa_spent));
|
||||
|
||||
@@ -24,6 +24,7 @@ public:
|
||||
int16_t race;
|
||||
int8_t gender;
|
||||
int8_t texture;
|
||||
int8_t helmtexture;
|
||||
float mountspeed;
|
||||
std::string notes;
|
||||
};
|
||||
@@ -41,6 +42,7 @@ public:
|
||||
"race",
|
||||
"gender",
|
||||
"texture",
|
||||
"helmtexture",
|
||||
"mountspeed",
|
||||
"notes",
|
||||
};
|
||||
@@ -54,6 +56,7 @@ public:
|
||||
"race",
|
||||
"gender",
|
||||
"texture",
|
||||
"helmtexture",
|
||||
"mountspeed",
|
||||
"notes",
|
||||
};
|
||||
@@ -96,13 +99,14 @@ public:
|
||||
{
|
||||
Horses e{};
|
||||
|
||||
e.id = 0;
|
||||
e.filename = "";
|
||||
e.race = 216;
|
||||
e.gender = 0;
|
||||
e.texture = 0;
|
||||
e.mountspeed = 0.75;
|
||||
e.notes = "Notes";
|
||||
e.id = 0;
|
||||
e.filename = "";
|
||||
e.race = 216;
|
||||
e.gender = 0;
|
||||
e.texture = 0;
|
||||
e.helmtexture = -1;
|
||||
e.mountspeed = 0.75;
|
||||
e.notes = "Notes";
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -139,13 +143,14 @@ public:
|
||||
if (results.RowCount() == 1) {
|
||||
Horses e{};
|
||||
|
||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.filename = row[1] ? row[1] : "";
|
||||
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.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||
e.mountspeed = row[5] ? strtof(row[5], nullptr) : 0.75;
|
||||
e.notes = row[6] ? row[6] : "Notes";
|
||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.filename = row[1] ? row[1] : "";
|
||||
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.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||
e.helmtexture = row[5] ? static_cast<int8_t>(atoi(row[5])) : -1;
|
||||
e.mountspeed = row[6] ? strtof(row[6], nullptr) : 0.75;
|
||||
e.notes = row[7] ? row[7] : "Notes";
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -183,8 +188,9 @@ public:
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.race));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.gender));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.texture));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.mountspeed));
|
||||
v.push_back(columns[6] + " = '" + Strings::Escape(e.notes) + "'");
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.helmtexture));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.mountspeed));
|
||||
v.push_back(columns[7] + " = '" + Strings::Escape(e.notes) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -211,6 +217,7 @@ public:
|
||||
v.push_back(std::to_string(e.race));
|
||||
v.push_back(std::to_string(e.gender));
|
||||
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("'" + Strings::Escape(e.notes) + "'");
|
||||
|
||||
@@ -247,6 +254,7 @@ public:
|
||||
v.push_back(std::to_string(e.race));
|
||||
v.push_back(std::to_string(e.gender));
|
||||
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("'" + Strings::Escape(e.notes) + "'");
|
||||
|
||||
@@ -282,13 +290,14 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Horses e{};
|
||||
|
||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.filename = row[1] ? row[1] : "";
|
||||
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.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||
e.mountspeed = row[5] ? strtof(row[5], nullptr) : 0.75;
|
||||
e.notes = row[6] ? row[6] : "Notes";
|
||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.filename = row[1] ? row[1] : "";
|
||||
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.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||
e.helmtexture = row[5] ? static_cast<int8_t>(atoi(row[5])) : -1;
|
||||
e.mountspeed = row[6] ? strtof(row[6], nullptr) : 0.75;
|
||||
e.notes = row[7] ? row[7] : "Notes";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -313,13 +322,14 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Horses e{};
|
||||
|
||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.filename = row[1] ? row[1] : "";
|
||||
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.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||
e.mountspeed = row[5] ? strtof(row[5], nullptr) : 0.75;
|
||||
e.notes = row[6] ? row[6] : "Notes";
|
||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.filename = row[1] ? row[1] : "";
|
||||
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.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||
e.helmtexture = row[5] ? static_cast<int8_t>(atoi(row[5])) : -1;
|
||||
e.mountspeed = row[6] ? strtof(row[6], nullptr) : 0.75;
|
||||
e.notes = row[7] ? row[7] : "Notes";
|
||||
|
||||
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.gender));
|
||||
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("'" + Strings::Escape(e.notes) + "'");
|
||||
|
||||
@@ -428,6 +439,7 @@ public:
|
||||
v.push_back(std::to_string(e.race));
|
||||
v.push_back(std::to_string(e.gender));
|
||||
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("'" + Strings::Escape(e.notes) + "'");
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
uint8_t is_global;
|
||||
uint32_t start_time;
|
||||
uint32_t duration;
|
||||
uint64_t expire_at;
|
||||
uint8_t never_expires;
|
||||
std::string notes;
|
||||
};
|
||||
@@ -43,6 +44,7 @@ public:
|
||||
"is_global",
|
||||
"start_time",
|
||||
"duration",
|
||||
"expire_at",
|
||||
"never_expires",
|
||||
"notes",
|
||||
};
|
||||
@@ -57,6 +59,7 @@ public:
|
||||
"is_global",
|
||||
"start_time",
|
||||
"duration",
|
||||
"expire_at",
|
||||
"never_expires",
|
||||
"notes",
|
||||
};
|
||||
@@ -105,6 +108,7 @@ public:
|
||||
e.is_global = 0;
|
||||
e.start_time = 0;
|
||||
e.duration = 0;
|
||||
e.expire_at = 0;
|
||||
e.never_expires = 0;
|
||||
e.notes = "";
|
||||
|
||||
@@ -149,8 +153,9 @@ public:
|
||||
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.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.notes = row[7] ? row[7] : "";
|
||||
e.expire_at = row[6] ? strtoull(row[6], nullptr, 10) : 0;
|
||||
e.never_expires = row[7] ? static_cast<uint8_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.notes = row[8] ? row[8] : "";
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -189,8 +194,9 @@ public:
|
||||
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[5] + " = " + std::to_string(e.duration));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.never_expires));
|
||||
v.push_back(columns[7] + " = '" + Strings::Escape(e.notes) + "'");
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.expire_at));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.never_expires));
|
||||
v.push_back(columns[8] + " = '" + Strings::Escape(e.notes) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -218,6 +224,7 @@ public:
|
||||
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.duration));
|
||||
v.push_back(std::to_string(e.expire_at));
|
||||
v.push_back(std::to_string(e.never_expires));
|
||||
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.start_time));
|
||||
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("'" + 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.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.never_expires = row[6] ? static_cast<uint8_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.notes = row[7] ? row[7] : "";
|
||||
e.expire_at = row[6] ? strtoull(row[6], nullptr, 10) : 0;
|
||||
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);
|
||||
}
|
||||
@@ -328,8 +337,9 @@ public:
|
||||
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.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.notes = row[7] ? row[7] : "";
|
||||
e.expire_at = row[6] ? strtoull(row[6], nullptr, 10) : 0;
|
||||
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);
|
||||
}
|
||||
@@ -410,6 +420,7 @@ public:
|
||||
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.duration));
|
||||
v.push_back(std::to_string(e.expire_at));
|
||||
v.push_back(std::to_string(e.never_expires));
|
||||
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.start_time));
|
||||
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("'" + Strings::Escape(e.notes) + "'");
|
||||
|
||||
|
||||
@@ -149,6 +149,7 @@ public:
|
||||
uint8_t keeps_sold_items;
|
||||
uint8_t is_parcel_merchant;
|
||||
uint8_t multiquest_enabled;
|
||||
uint16_t npc_tint_id;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@@ -289,6 +290,7 @@ public:
|
||||
"keeps_sold_items",
|
||||
"is_parcel_merchant",
|
||||
"multiquest_enabled",
|
||||
"npc_tint_id",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -425,6 +427,7 @@ public:
|
||||
"keeps_sold_items",
|
||||
"is_parcel_merchant",
|
||||
"multiquest_enabled",
|
||||
"npc_tint_id",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -595,6 +598,7 @@ public:
|
||||
e.keeps_sold_items = 1;
|
||||
e.is_parcel_merchant = 0;
|
||||
e.multiquest_enabled = 0;
|
||||
e.npc_tint_id = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -761,6 +765,7 @@ public:
|
||||
e.keeps_sold_items = row[127] ? static_cast<uint8_t>(strtoul(row[127], nullptr, 10)) : 1;
|
||||
e.is_parcel_merchant = row[128] ? static_cast<uint8_t>(strtoul(row[128], nullptr, 10)) : 0;
|
||||
e.multiquest_enabled = row[129] ? static_cast<uint8_t>(strtoul(row[129], nullptr, 10)) : 0;
|
||||
e.npc_tint_id = row[130] ? static_cast<uint16_t>(strtoul(row[130], nullptr, 10)) : 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -923,6 +928,7 @@ public:
|
||||
v.push_back(columns[127] + " = " + std::to_string(e.keeps_sold_items));
|
||||
v.push_back(columns[128] + " = " + std::to_string(e.is_parcel_merchant));
|
||||
v.push_back(columns[129] + " = " + std::to_string(e.multiquest_enabled));
|
||||
v.push_back(columns[130] + " = " + std::to_string(e.npc_tint_id));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -1074,6 +1080,7 @@ public:
|
||||
v.push_back(std::to_string(e.keeps_sold_items));
|
||||
v.push_back(std::to_string(e.is_parcel_merchant));
|
||||
v.push_back(std::to_string(e.multiquest_enabled));
|
||||
v.push_back(std::to_string(e.npc_tint_id));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -1233,6 +1240,7 @@ public:
|
||||
v.push_back(std::to_string(e.keeps_sold_items));
|
||||
v.push_back(std::to_string(e.is_parcel_merchant));
|
||||
v.push_back(std::to_string(e.multiquest_enabled));
|
||||
v.push_back(std::to_string(e.npc_tint_id));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -1396,6 +1404,7 @@ public:
|
||||
e.keeps_sold_items = row[127] ? static_cast<uint8_t>(strtoul(row[127], nullptr, 10)) : 1;
|
||||
e.is_parcel_merchant = row[128] ? static_cast<uint8_t>(strtoul(row[128], nullptr, 10)) : 0;
|
||||
e.multiquest_enabled = row[129] ? static_cast<uint8_t>(strtoul(row[129], nullptr, 10)) : 0;
|
||||
e.npc_tint_id = row[130] ? static_cast<uint16_t>(strtoul(row[130], nullptr, 10)) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -1550,6 +1559,7 @@ public:
|
||||
e.keeps_sold_items = row[127] ? static_cast<uint8_t>(strtoul(row[127], nullptr, 10)) : 1;
|
||||
e.is_parcel_merchant = row[128] ? static_cast<uint8_t>(strtoul(row[128], nullptr, 10)) : 0;
|
||||
e.multiquest_enabled = row[129] ? static_cast<uint8_t>(strtoul(row[129], nullptr, 10)) : 0;
|
||||
e.npc_tint_id = row[130] ? static_cast<uint16_t>(strtoul(row[130], nullptr, 10)) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -1754,6 +1764,7 @@ public:
|
||||
v.push_back(std::to_string(e.keeps_sold_items));
|
||||
v.push_back(std::to_string(e.is_parcel_merchant));
|
||||
v.push_back(std::to_string(e.multiquest_enabled));
|
||||
v.push_back(std::to_string(e.npc_tint_id));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -1906,6 +1917,7 @@ public:
|
||||
v.push_back(std::to_string(e.keeps_sold_items));
|
||||
v.push_back(std::to_string(e.is_parcel_merchant));
|
||||
v.push_back(std::to_string(e.multiquest_enabled));
|
||||
v.push_back(std::to_string(e.npc_tint_id));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "../../database.h"
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
#include <cereal/cereal.hpp>
|
||||
class BasePlayerEventLogSettingsRepository {
|
||||
public:
|
||||
struct PlayerEventLogSettings {
|
||||
@@ -25,6 +25,20 @@ public:
|
||||
int32_t retention_days;
|
||||
int32_t discord_webhook_id;
|
||||
uint8_t etl_enabled;
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(id),
|
||||
CEREAL_NVP(event_name),
|
||||
CEREAL_NVP(event_enabled),
|
||||
CEREAL_NVP(retention_days),
|
||||
CEREAL_NVP(discord_webhook_id),
|
||||
CEREAL_NVP(etl_enabled)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
|
||||
@@ -19,10 +19,11 @@
|
||||
class BaseRespawnTimesRepository {
|
||||
public:
|
||||
struct RespawnTimes {
|
||||
int32_t id;
|
||||
int32_t start;
|
||||
int32_t duration;
|
||||
int16_t instance_id;
|
||||
int32_t id;
|
||||
int32_t start;
|
||||
int32_t duration;
|
||||
uint32_t expire_at;
|
||||
int16_t instance_id;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@@ -36,6 +37,7 @@ public:
|
||||
"id",
|
||||
"start",
|
||||
"duration",
|
||||
"expire_at",
|
||||
"instance_id",
|
||||
};
|
||||
}
|
||||
@@ -46,6 +48,7 @@ public:
|
||||
"id",
|
||||
"start",
|
||||
"duration",
|
||||
"expire_at",
|
||||
"instance_id",
|
||||
};
|
||||
}
|
||||
@@ -90,6 +93,7 @@ public:
|
||||
e.id = 0;
|
||||
e.start = 0;
|
||||
e.duration = 0;
|
||||
e.expire_at = 0;
|
||||
e.instance_id = 0;
|
||||
|
||||
return e;
|
||||
@@ -130,7 +134,8 @@ public:
|
||||
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.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;
|
||||
}
|
||||
@@ -167,7 +172,8 @@ public:
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.id));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.start));
|
||||
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(
|
||||
fmt::format(
|
||||
@@ -192,6 +198,7 @@ public:
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.start));
|
||||
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));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
@@ -225,6 +232,7 @@ public:
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.start));
|
||||
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));
|
||||
|
||||
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.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 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);
|
||||
}
|
||||
@@ -290,7 +299,8 @@ public:
|
||||
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.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);
|
||||
}
|
||||
@@ -368,6 +378,7 @@ public:
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.start));
|
||||
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));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
@@ -394,6 +405,7 @@ public:
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.start));
|
||||
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));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
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;
|
||||
@@ -61,6 +62,7 @@ public:
|
||||
"zone_id",
|
||||
"instance_id",
|
||||
"is_corpse",
|
||||
"is_zone",
|
||||
"decay_in_seconds",
|
||||
"npc_id",
|
||||
"spawn2_id",
|
||||
@@ -95,6 +97,7 @@ public:
|
||||
"zone_id",
|
||||
"instance_id",
|
||||
"is_corpse",
|
||||
"is_zone",
|
||||
"decay_in_seconds",
|
||||
"npc_id",
|
||||
"spawn2_id",
|
||||
@@ -163,6 +166,7 @@ public:
|
||||
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;
|
||||
@@ -227,30 +231,31 @@ public:
|
||||
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.decay_in_seconds = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.npc_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.spawn2_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.spawngroup_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.x = row[8] ? strtof(row[8], nullptr) : 0;
|
||||
e.y = row[9] ? strtof(row[9], nullptr) : 0;
|
||||
e.z = row[10] ? strtof(row[10], nullptr) : 0;
|
||||
e.heading = row[11] ? strtof(row[11], nullptr) : 0;
|
||||
e.respawn_time = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
|
||||
e.variance = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.grid = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.current_waypoint = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
||||
e.path_when_zone_idle = row[16] ? static_cast<int16_t>(atoi(row[16])) : 0;
|
||||
e.condition_id = row[17] ? static_cast<uint16_t>(strtoul(row[17], nullptr, 10)) : 0;
|
||||
e.condition_min_value = row[18] ? static_cast<int16_t>(atoi(row[18])) : 0;
|
||||
e.enabled = row[19] ? static_cast<int16_t>(atoi(row[19])) : 1;
|
||||
e.anim = row[20] ? static_cast<uint16_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||
e.loot_data = row[21] ? row[21] : "";
|
||||
e.entity_variables = row[22] ? row[22] : "";
|
||||
e.buffs = row[23] ? row[23] : "";
|
||||
e.hp = row[24] ? strtoll(row[24], nullptr, 10) : 0;
|
||||
e.mana = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||
e.endurance = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||
e.created_at = strtoll(row[27] ? row[27] : "-1", nullptr, 10);
|
||||
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;
|
||||
}
|
||||
@@ -287,30 +292,31 @@ public:
|
||||
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.decay_in_seconds));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.npc_id));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.spawn2_id));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.spawngroup_id));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.x));
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.y));
|
||||
v.push_back(columns[10] + " = " + std::to_string(e.z));
|
||||
v.push_back(columns[11] + " = " + std::to_string(e.heading));
|
||||
v.push_back(columns[12] + " = " + std::to_string(e.respawn_time));
|
||||
v.push_back(columns[13] + " = " + std::to_string(e.variance));
|
||||
v.push_back(columns[14] + " = " + std::to_string(e.grid));
|
||||
v.push_back(columns[15] + " = " + std::to_string(e.current_waypoint));
|
||||
v.push_back(columns[16] + " = " + std::to_string(e.path_when_zone_idle));
|
||||
v.push_back(columns[17] + " = " + std::to_string(e.condition_id));
|
||||
v.push_back(columns[18] + " = " + std::to_string(e.condition_min_value));
|
||||
v.push_back(columns[19] + " = " + std::to_string(e.enabled));
|
||||
v.push_back(columns[20] + " = " + std::to_string(e.anim));
|
||||
v.push_back(columns[21] + " = '" + Strings::Escape(e.loot_data) + "'");
|
||||
v.push_back(columns[22] + " = '" + Strings::Escape(e.entity_variables) + "'");
|
||||
v.push_back(columns[23] + " = '" + Strings::Escape(e.buffs) + "'");
|
||||
v.push_back(columns[24] + " = " + std::to_string(e.hp));
|
||||
v.push_back(columns[25] + " = " + std::to_string(e.mana));
|
||||
v.push_back(columns[26] + " = " + std::to_string(e.endurance));
|
||||
v.push_back(columns[27] + " = FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||
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(
|
||||
@@ -336,6 +342,7 @@ public:
|
||||
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));
|
||||
@@ -393,6 +400,7 @@ public:
|
||||
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));
|
||||
@@ -454,30 +462,31 @@ public:
|
||||
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.decay_in_seconds = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.npc_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.spawn2_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.spawngroup_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.x = row[8] ? strtof(row[8], nullptr) : 0;
|
||||
e.y = row[9] ? strtof(row[9], nullptr) : 0;
|
||||
e.z = row[10] ? strtof(row[10], nullptr) : 0;
|
||||
e.heading = row[11] ? strtof(row[11], nullptr) : 0;
|
||||
e.respawn_time = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
|
||||
e.variance = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.grid = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.current_waypoint = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
||||
e.path_when_zone_idle = row[16] ? static_cast<int16_t>(atoi(row[16])) : 0;
|
||||
e.condition_id = row[17] ? static_cast<uint16_t>(strtoul(row[17], nullptr, 10)) : 0;
|
||||
e.condition_min_value = row[18] ? static_cast<int16_t>(atoi(row[18])) : 0;
|
||||
e.enabled = row[19] ? static_cast<int16_t>(atoi(row[19])) : 1;
|
||||
e.anim = row[20] ? static_cast<uint16_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||
e.loot_data = row[21] ? row[21] : "";
|
||||
e.entity_variables = row[22] ? row[22] : "";
|
||||
e.buffs = row[23] ? row[23] : "";
|
||||
e.hp = row[24] ? strtoll(row[24], nullptr, 10) : 0;
|
||||
e.mana = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||
e.endurance = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||
e.created_at = strtoll(row[27] ? row[27] : "-1", nullptr, 10);
|
||||
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);
|
||||
}
|
||||
@@ -506,30 +515,31 @@ public:
|
||||
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.decay_in_seconds = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.npc_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.spawn2_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.spawngroup_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.x = row[8] ? strtof(row[8], nullptr) : 0;
|
||||
e.y = row[9] ? strtof(row[9], nullptr) : 0;
|
||||
e.z = row[10] ? strtof(row[10], nullptr) : 0;
|
||||
e.heading = row[11] ? strtof(row[11], nullptr) : 0;
|
||||
e.respawn_time = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
|
||||
e.variance = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.grid = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.current_waypoint = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
||||
e.path_when_zone_idle = row[16] ? static_cast<int16_t>(atoi(row[16])) : 0;
|
||||
e.condition_id = row[17] ? static_cast<uint16_t>(strtoul(row[17], nullptr, 10)) : 0;
|
||||
e.condition_min_value = row[18] ? static_cast<int16_t>(atoi(row[18])) : 0;
|
||||
e.enabled = row[19] ? static_cast<int16_t>(atoi(row[19])) : 1;
|
||||
e.anim = row[20] ? static_cast<uint16_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||
e.loot_data = row[21] ? row[21] : "";
|
||||
e.entity_variables = row[22] ? row[22] : "";
|
||||
e.buffs = row[23] ? row[23] : "";
|
||||
e.hp = row[24] ? strtoll(row[24], nullptr, 10) : 0;
|
||||
e.mana = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||
e.endurance = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||
e.created_at = strtoll(row[27] ? row[27] : "-1", nullptr, 10);
|
||||
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);
|
||||
}
|
||||
@@ -608,6 +618,7 @@ public:
|
||||
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));
|
||||
@@ -658,6 +669,7 @@ public:
|
||||
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));
|
||||
|
||||
@@ -106,13 +106,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
auto buy_lines = BaseBuyerBuyLinesRepository::GetWhere(
|
||||
db,
|
||||
fmt::format("`buyer_id` = '{}'", buyer.front().id)
|
||||
);
|
||||
if (buy_lines.empty()) {
|
||||
return false;
|
||||
}
|
||||
auto buy_lines =
|
||||
BaseBuyerBuyLinesRepository::GetWhere(db, fmt::format("`buyer_id` = {}", buyer.front().id));
|
||||
|
||||
std::vector<std::string> buy_line_ids{};
|
||||
for (auto const &bl: buy_lines) {
|
||||
@@ -121,23 +116,65 @@ public:
|
||||
|
||||
DeleteWhere(db, fmt::format("`char_id` = '{}';", char_id));
|
||||
if (buy_line_ids.empty()) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
BaseBuyerBuyLinesRepository::DeleteWhere(
|
||||
db,
|
||||
fmt::format("`id` IN({})", Strings::Implode(", ", buy_line_ids))
|
||||
db, fmt::format("`id` IN({})", Strings::Implode(", ", buy_line_ids))
|
||||
);
|
||||
BaseBuyerTradeItemsRepository::DeleteWhere(
|
||||
db,
|
||||
fmt::format(
|
||||
"`buyer_buy_lines_id` IN({})",
|
||||
Strings::Implode(", ", buy_line_ids))
|
||||
db, fmt::format("`buyer_buy_lines_id` IN({})", Strings::Implode(", ", buy_line_ids))
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool DeleteBuyers(Database &db, uint32 char_zone_id, uint32 char_zone_instance_id)
|
||||
{
|
||||
auto buyers = GetWhere(
|
||||
db,
|
||||
fmt::format(
|
||||
"`char_zone_id` = {} AND `char_zone_instance_id` = {}", char_zone_id, char_zone_instance_id
|
||||
)
|
||||
);
|
||||
if (buyers.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> buyer_ids{};
|
||||
std::vector<std::string> buy_line_ids{};
|
||||
|
||||
for (auto const &b: buyers) {
|
||||
buyer_ids.push_back(std::to_string(b.id));
|
||||
}
|
||||
|
||||
auto buy_lines = BaseBuyerBuyLinesRepository::GetWhere(
|
||||
db, fmt::format("`buyer_id` IN({})", Strings::Implode(", ", buyer_ids))
|
||||
);
|
||||
|
||||
if (!buy_lines.empty()) {
|
||||
for (auto const &bl: buy_lines) {
|
||||
buy_line_ids.push_back(std::to_string(bl.id));
|
||||
}
|
||||
}
|
||||
|
||||
DeleteWhere(db, fmt::format("`id` IN({});", Strings::Implode(", ", buyer_ids)));
|
||||
if (buy_line_ids.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BaseBuyerBuyLinesRepository::DeleteWhere(
|
||||
db,
|
||||
fmt::format("`id` IN({})", Strings::Implode(", ", buy_line_ids))
|
||||
);
|
||||
BaseBuyerTradeItemsRepository::DeleteWhere(
|
||||
db,
|
||||
fmt::format("`buyer_buy_lines_id` IN({})", Strings::Implode(", ", buy_line_ids))
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BUYER_REPOSITORY_H
|
||||
|
||||
@@ -231,6 +231,21 @@ public:
|
||||
|
||||
return UpdateOne(db, corpse);
|
||||
}
|
||||
|
||||
static int UpdateEntityVariables(Database& db, uint32 corpse_id, const std::string& json_string)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE `{}` SET `entity_variables` = '{}' WHERE `{}` = {}",
|
||||
TableName(),
|
||||
Strings::Escape(json_string),
|
||||
PrimaryKey(),
|
||||
corpse_id
|
||||
)
|
||||
);
|
||||
|
||||
return results.Success() ? results.RowsAffected() : 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_CHARACTER_CORPSES_REPOSITORY_H
|
||||
|
||||
@@ -167,6 +167,30 @@ public:
|
||||
|
||||
return zone_player_counts;
|
||||
}
|
||||
|
||||
static std::vector<uint32_t> GetCharacterIDsByAccountID(
|
||||
Database& db,
|
||||
uint32_t account_id
|
||||
)
|
||||
{
|
||||
std::vector<uint32_t> character_ids;
|
||||
|
||||
auto query = fmt::format(
|
||||
"SELECT id FROM character_data WHERE account_id = {} AND deleted_at IS NULL",
|
||||
account_id
|
||||
);
|
||||
|
||||
auto results = db.QueryDatabase(query);
|
||||
if (results.Success()) {
|
||||
for (auto row : results) {
|
||||
if (row[0]) {
|
||||
character_ids.push_back(static_cast<uint32_t>(std::stoul(row[0])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return character_ids;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_CHARACTER_DATA_REPOSITORY_H
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace ContentFilterCriteria {
|
||||
table_prefix = table_prefix + ".";
|
||||
}
|
||||
|
||||
int current_expansion_filter_criteria = content_service.GetCurrentExpansion();
|
||||
int current_expansion_filter_criteria = WorldContentService::Instance()->GetCurrentExpansion();
|
||||
if (current_expansion_filter_criteria == Expansion::EXPANSION_ALL) {
|
||||
current_expansion_filter_criteria = Expansion::EXPANSION_FILTER_MAX;
|
||||
}
|
||||
@@ -33,8 +33,8 @@ namespace ContentFilterCriteria {
|
||||
table_prefix
|
||||
);
|
||||
|
||||
std::vector<std::string> flags_disabled = content_service.GetContentFlagsDisabled();
|
||||
std::vector<std::string> flags_enabled = content_service.GetContentFlagsEnabled();
|
||||
std::vector<std::string> flags_disabled = WorldContentService::Instance()->GetContentFlagsDisabled();
|
||||
std::vector<std::string> flags_enabled = WorldContentService::Instance()->GetContentFlagsEnabled();
|
||||
std::string flags_in_filter_enabled;
|
||||
std::string flags_in_filter_disabled;
|
||||
if (!flags_enabled.empty()) {
|
||||
|
||||
@@ -7,49 +7,11 @@
|
||||
|
||||
class InstanceListRepository: public BaseInstanceListRepository {
|
||||
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)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE `{}` SET `duration` = {} WHERE `{}` = {}",
|
||||
"UPDATE `{}` SET `duration` = {}, `expire_at` = (`duration` + `start_time`) WHERE `{}` = {}",
|
||||
TableName(),
|
||||
new_duration,
|
||||
PrimaryKey(),
|
||||
@@ -65,7 +27,7 @@ public:
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
SQL(
|
||||
SELECT ((start_time + duration) - UNIX_TIMESTAMP()) AS `remaining` FROM `{}`
|
||||
SELECT (`expire_at` - UNIX_TIMESTAMP()) AS `remaining` FROM `{}`
|
||||
WHERE `id` = {}
|
||||
),
|
||||
TableName(),
|
||||
|
||||
@@ -8,47 +8,11 @@
|
||||
class RespawnTimesRepository: public BaseRespawnTimesRepository {
|
||||
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)
|
||||
{
|
||||
db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM `{}` WHERE (`start` + `duration`) < UNIX_TIMESTAMP(NOW())",
|
||||
"DELETE FROM `{}` WHERE `expire_at` < UNIX_TIMESTAMP(NOW())",
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
@@ -77,6 +41,11 @@ public:
|
||||
|
||||
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
|
||||
|
||||
@@ -43,6 +43,47 @@ public:
|
||||
* method and encapsulate filters there
|
||||
*/
|
||||
|
||||
template<typename T1, typename T2, typename T3, typename T4>
|
||||
static std::vector<std::string> join_tuple(
|
||||
const std::string &glue,
|
||||
const std::pair<char, char> &encapsulation,
|
||||
const std::vector<std::tuple<T1, T2, T3, T4>> &src
|
||||
)
|
||||
{
|
||||
if (src.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> output;
|
||||
|
||||
for (const std::tuple<T1, T2, T3, T4> &src_iter: src) {
|
||||
|
||||
output.emplace_back(
|
||||
|
||||
fmt::format(
|
||||
"{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}",
|
||||
encapsulation.first,
|
||||
std::get<0>(src_iter),
|
||||
encapsulation.second,
|
||||
glue,
|
||||
encapsulation.first,
|
||||
std::get<1>(src_iter),
|
||||
encapsulation.second,
|
||||
glue,
|
||||
encapsulation.first,
|
||||
std::get<2>(src_iter),
|
||||
encapsulation.second,
|
||||
glue,
|
||||
encapsulation.first,
|
||||
std::get<3>(src_iter),
|
||||
encapsulation.second
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// Custom extended repository methods here
|
||||
static std::vector<std::string> GetRuleNames(Database &db, int rule_set_id)
|
||||
{
|
||||
@@ -87,12 +128,28 @@ public:
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static std::string
|
||||
ImplodePair(const std::string &glue, const std::pair<char, char> &encapsulation, const std::vector<T> &src)
|
||||
{
|
||||
if (src.empty()) {
|
||||
return {};
|
||||
}
|
||||
std::ostringstream oss;
|
||||
for (const T &src_iter: src) {
|
||||
oss << encapsulation.first << src_iter << encapsulation.second << glue;
|
||||
}
|
||||
std::string output(oss.str());
|
||||
output.resize(output.size() - glue.size());
|
||||
return output;
|
||||
}
|
||||
|
||||
static bool DeleteOrphanedRules(Database& db, std::vector<std::string>& v)
|
||||
{
|
||||
const auto query = fmt::format(
|
||||
"DELETE FROM {} WHERE rule_name IN ({})",
|
||||
TableName(),
|
||||
Strings::ImplodePair(",", std::pair<char, char>('\'', '\''), v)
|
||||
ImplodePair(",", std::pair<char, char>('\'', '\''), v)
|
||||
);
|
||||
|
||||
return db.QueryDatabase(query).Success();
|
||||
@@ -103,7 +160,7 @@ public:
|
||||
const auto query = fmt::format(
|
||||
"REPLACE INTO {} (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES {}",
|
||||
TableName(),
|
||||
Strings::ImplodePair(
|
||||
ImplodePair(
|
||||
",",
|
||||
std::pair<char, char>('(', ')'),
|
||||
join_tuple(",", std::pair<char, char>('\'', '\''), v)
|
||||
|
||||
@@ -54,17 +54,30 @@ public:
|
||||
{
|
||||
BulkTraders_Struct all_entries{};
|
||||
std::vector<DistinctTraders_Struct> distinct_traders;
|
||||
MySQLRequestResult results;
|
||||
|
||||
auto 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 "
|
||||
"WHERE t.char_zone_instance_id = {} "
|
||||
"ORDER BY t.char_zone_instance_id ASC "
|
||||
"LIMIT {}",
|
||||
char_zone_instance_id,
|
||||
max_results)
|
||||
);
|
||||
if (RuleB(Bazaar, UseAlternateBazaarSearch)) {
|
||||
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 "
|
||||
"WHERE t.char_zone_instance_id = {} "
|
||||
"ORDER BY t.char_zone_instance_id ASC "
|
||||
"LIMIT {}",
|
||||
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 {}",
|
||||
max_results)
|
||||
);
|
||||
}
|
||||
|
||||
distinct_traders.reserve(results.RowCount());
|
||||
|
||||
|
||||
@@ -5,9 +5,77 @@
|
||||
#include "../strings.h"
|
||||
#include "base/base_zone_state_spawns_repository.h"
|
||||
|
||||
class ZoneStateSpawnsRepository: public BaseZoneStateSpawnsRepository {
|
||||
class ZoneStateSpawnsRepository : public BaseZoneStateSpawnsRepository {
|
||||
public:
|
||||
// Custom extended repository methods here
|
||||
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())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -502,6 +502,19 @@ bool RuleManager::UpdateInjectedRules(Database *db, const std::string &rule_set_
|
||||
}
|
||||
}
|
||||
|
||||
// update rules in the database where the description is different
|
||||
for (auto &e : RuleValuesRepository::All(*db)) {
|
||||
auto i = rule_data.find(e.rule_name);
|
||||
if (i != rule_data.end()) {
|
||||
// if notes are different, update them
|
||||
if (i->second.second != nullptr && *i->second.second != e.notes) {
|
||||
LogInfo("Updating rule [{}] notes to [{}]", i->first, *i->second.second);
|
||||
e.notes = *i->second.second;
|
||||
RuleValuesRepository::ReplaceOne(*db, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (injected_rule_entries.size()) {
|
||||
if (!RuleValuesRepository::InjectRules(*db, injected_rule_entries)) {
|
||||
return false;
|
||||
|
||||
+31
-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, 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, 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, LeaveNakedCorpses, false, "Setting whether you leave a corpse without items")
|
||||
RULE_INT(Character, MaxDraggedCorpses, 2, "Maximum number of corpses you can drag at once")
|
||||
@@ -156,6 +156,7 @@ RULE_REAL(Character, TradeskillUpPottery, 4.0, "Pottery skillup rate adjustment.
|
||||
RULE_REAL(Character, TradeskillUpResearch, 1.0, "Research skillup rate adjustment. Lower is faster")
|
||||
RULE_REAL(Character, TradeskillUpTinkering, 2.0, "Tinkering skillup rate adjustment. Lower is faster")
|
||||
RULE_REAL(Character, TradeskillUpTailoring, 2.0, "Tailoring skillup rate adjustment. Lower is faster")
|
||||
RULE_REAL(Character, TradeskillUpMinChance, 2.5, "Determines the minimum percentage chance to gain a skill increase from a tradeskill. Cannot go below 2.5")
|
||||
RULE_BOOL(Character, MarqueeHPUpdates, false, "Will show health percentage in center of screen if health lesser than 100%")
|
||||
RULE_INT(Character, IksarCommonTongue, 95, "Starting value for Common Tongue for Iksars")
|
||||
RULE_INT(Character, OgreCommonTongue, 95, "Starting value for Common Tongue for Ogres")
|
||||
@@ -231,6 +232,13 @@ 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_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, AlwaysAllowNameChange, false, "Enable this option to allow /changename to work without enabling a name change via scripts.")
|
||||
RULE_BOOL(Character, EnableAutoAFK, false, "Enable or disable the auto AFK feature, cuts down on packet spam")
|
||||
RULE_BOOL(Character, AutoIdleFilterPackets, true, "Enable or disable filtering packets when auto AFK is enabled, heavily cuts down on packet spam in zones with lots of players")
|
||||
RULE_INT(Character, SecondsBeforeIdleCombatZone, 600, "Seconds before a player is considered idle in combat zones (600 = 10 minutes)")
|
||||
RULE_INT(Character, SecondsBeforeIdleNonCombatZone, 60, "Seconds before a player is considered idle in non-combat zones (60 = 1 minute)")
|
||||
RULE_INT(Character, SecondsBeforeAFKCombatZone, 1800, "Seconds before a player is considered AFK in combat zones (1800 = 30 minutes)")
|
||||
RULE_INT(Character, SecondsBeforeAFKNonCombatZone, 600, "Seconds before a player is considered AFK in non-combat zones (600 = 10 minutes)")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Mercs)
|
||||
@@ -260,6 +268,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, 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, EnableLFGuild, false, "Enable the LFGuild system (Requires queryserv)")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Skills)
|
||||
@@ -289,6 +298,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_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, PetsRequireLoS, false, "Whether or not pets require line of sight to be told to attack their target")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(GM)
|
||||
@@ -344,6 +354,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, 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_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(Zone)
|
||||
@@ -374,7 +385,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, 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, 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(Map)
|
||||
@@ -384,9 +399,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_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_BOOL(Map, CheckForLoSCheat, false, "Runs predefined zone checks to check for LoS cheating through doors and such.")
|
||||
RULE_BOOL(Map, EnableLoSCheatExemptions, false, "Enables exemptions for the LoS Cheat check.")
|
||||
RULE_REAL(Map, RangeCheckForLoSCheat, 20.0, "Default 20.0. Range to check if one is within range of a door.")
|
||||
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. Must modify source to create these.")
|
||||
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(Pathing)
|
||||
@@ -810,6 +826,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, 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, 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, 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%.")
|
||||
@@ -821,8 +838,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, 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, 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, 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.")
|
||||
@@ -832,14 +847,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, 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, 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, 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, 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, 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_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")
|
||||
@@ -850,17 +865,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_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_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.")
|
||||
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_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, true, "If true, when bots are at max melee distance, special abilities including taunt will be disabled. Default True.")
|
||||
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_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.")
|
||||
@@ -880,6 +885,7 @@ RULE_INT(Bots, MinStatusToBypassSpawnLimit, 100, "Minimum status to bypass spawn
|
||||
RULE_INT(Bots, MinStatusBypassSpawnLimit, 120, "Spawn limit with status bypass. Default 120.")
|
||||
RULE_INT(Bots, MinStatusToBypassCreateLimit, 100, "Minimum status to bypass create limit. Default 100.")
|
||||
RULE_INT(Bots, MinStatusBypassCreateLimit, 120, "Create limit with status bypass. Default 120.")
|
||||
RULE_INT(Bots, MinStatusToBypassBotLevelRequirement, 100, "Minimum status to bypass level requirement for bots. Default 100.")
|
||||
RULE_BOOL(Bots, EnableBotTGB, true, "If enabled bots will cast group buffs as TGB.")
|
||||
RULE_BOOL(Bots, DoResponseAnimations, true, "If enabled bots will do animations to certain responses or commands.")
|
||||
RULE_INT(Bots, DefaultFollowDistance, 20, "Default 20. Distance a bot will follow behind.")
|
||||
@@ -901,6 +907,7 @@ RULE_STRING(Bots, ZonesWithForcedSpawnLimits, "", "Comma-delimited list of 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, 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(Chat)
|
||||
@@ -924,6 +931,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, 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, AlwaysCaptureCommandText, false, "Consume command text (# and ^ by default), regardless of which channel it is sent to")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Merchant)
|
||||
@@ -1071,6 +1079,7 @@ RULE_CATEGORY(Logging)
|
||||
RULE_BOOL(Logging, PrintFileFunctionAndLine, false, "Ex: [World Server] [net.cpp::main:309] Loading variables...")
|
||||
RULE_BOOL(Logging, WorldGMSayLogging, true, "Relay worldserver logging to zone processes via GM say output")
|
||||
RULE_BOOL(Logging, PlayerEventsQSProcess, false, "Have query server process player events instead of world. Useful when wanting to use a dedicated server and database for processing player events on separate disk")
|
||||
RULE_STRING(Logging, PlayerEventsIgnoreGMCommands, "help,show", "This is a comma delimited list of commands to ignore when recording GM command player events.")
|
||||
RULE_INT(Logging, BatchPlayerEventProcessIntervalSeconds, 5, "This is the interval in which player events are processed in world or qs")
|
||||
RULE_INT(Logging, BatchPlayerEventProcessChunkSize, 10000, "This is the cap of events that can be inserted into the queue before a force flush. This is to keep from hitting MySQL max_allowed_packet and killing the connection")
|
||||
RULE_CATEGORY_END()
|
||||
@@ -1092,6 +1101,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_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, 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(Expedition)
|
||||
@@ -1163,8 +1173,9 @@ RULE_CATEGORY_END()
|
||||
RULE_CATEGORY(EvolvingItems)
|
||||
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, 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_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()
|
||||
|
||||
#undef RULE_CATEGORY
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace ServerReload {
|
||||
LevelEXPMods,
|
||||
Logs,
|
||||
Loot,
|
||||
Maps,
|
||||
Merchants,
|
||||
NPCEmotes,
|
||||
NPCSpells,
|
||||
@@ -61,6 +62,7 @@ namespace ServerReload {
|
||||
"Level EXP Mods",
|
||||
"Logs",
|
||||
"Loot",
|
||||
"Maps",
|
||||
"Merchants",
|
||||
"NPC Emotes",
|
||||
"NPC Spells",
|
||||
|
||||
+11
-1
@@ -196,6 +196,7 @@
|
||||
#define ServerOP_DzSaveInvite 0x0466
|
||||
#define ServerOP_DzRequestInvite 0x0467
|
||||
#define ServerOP_DzMakeLeader 0x0468
|
||||
#define ServerOP_DzGetBulkMemberStatuses 0x0469
|
||||
|
||||
#define ServerOP_LSInfo 0x1000
|
||||
#define ServerOP_LSStatus 0x1001
|
||||
@@ -272,8 +273,9 @@
|
||||
#define ServerOP_WWTaskUpdate 0x4758
|
||||
|
||||
// player events
|
||||
#define ServerOP_QSSendQuery 0x5000
|
||||
#define ServerOP_QSSendQuery 0x5000
|
||||
#define ServerOP_PlayerEvent 0x5100
|
||||
#define ServerOP_SendPlayerEventSettings 0x5101
|
||||
|
||||
enum {
|
||||
CZUpdateType_Character,
|
||||
@@ -1555,6 +1557,13 @@ struct ServerDzMemberStatuses_Struct {
|
||||
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 {
|
||||
uint32 dz_id;
|
||||
uint16 sender_zone_id;
|
||||
@@ -1770,6 +1779,7 @@ struct BazaarPurchaseMessaging_Struct {
|
||||
uint32 id;
|
||||
};
|
||||
|
||||
|
||||
#pragma pack()
|
||||
|
||||
#endif
|
||||
|
||||
@@ -48,7 +48,7 @@ enum class SharedTaskRequestGroupType {
|
||||
struct ServerSharedTaskRequest_Struct {
|
||||
uint32 requested_character_id;
|
||||
uint32 requested_task_id;
|
||||
uint32 requested_npc_type_id; // original task logic passthrough
|
||||
uint32 requested_npc_entity_id; // original task logic passthrough
|
||||
uint32 accept_time;
|
||||
};
|
||||
|
||||
|
||||
+54
-6
@@ -824,7 +824,7 @@ bool SharedDatabase::GetInventory(Client *c)
|
||||
e.character_id = char_id;
|
||||
e.item_id = item_id;
|
||||
e.equipped = inst->GetEvolveEquipped();
|
||||
e.final_item_id = evolving_items_manager.GetFinalItemID(*inst);
|
||||
e.final_item_id = EvolvingItemsManager::Instance()->GetFinalItemID(*inst);
|
||||
|
||||
auto r = CharacterEvolvingItemsRepository::InsertOne(*this, e);
|
||||
e.id = r.id;
|
||||
@@ -974,7 +974,7 @@ bool SharedDatabase::LoadItems(const std::string &prefix) {
|
||||
const auto Config = EQEmuConfig::get();
|
||||
EQ::IPCMutex mutex("items");
|
||||
mutex.Lock();
|
||||
std::string file_name = fmt::format("{}/{}{}", path.GetSharedMemoryPath(), prefix, std::string("items"));
|
||||
std::string file_name = fmt::format("{}/{}{}", PathManager::Instance()->GetSharedMemoryPath(), prefix, std::string("items"));
|
||||
items_mmf = std::make_unique<EQ::MemoryMappedFile>(file_name);
|
||||
items_hash = std::make_unique<EQ::FixedMemoryHashSet<EQ::ItemData>>(static_cast<uint8*>(items_mmf->Get()), items_mmf->Size());
|
||||
mutex.Unlock();
|
||||
@@ -1544,12 +1544,60 @@ bool SharedDatabase::GetCommandSettings(std::map<std::string, std::pair<uint8, s
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
inline std::vector<std::string> join_pair(
|
||||
const std::string &glue,
|
||||
const std::pair<char, char> &encapsulation,
|
||||
const std::vector<std::pair<T1, T2>> &src
|
||||
)
|
||||
{
|
||||
if (src.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> output;
|
||||
|
||||
for (const std::pair<T1, T2> &src_iter: src) {
|
||||
output.emplace_back(
|
||||
|
||||
fmt::format(
|
||||
"{}{}{}{}{}{}{}",
|
||||
encapsulation.first,
|
||||
src_iter.first,
|
||||
encapsulation.second,
|
||||
glue,
|
||||
encapsulation.first,
|
||||
src_iter.second,
|
||||
encapsulation.second
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::string
|
||||
ImplodePair(const std::string &glue, const std::pair<char, char> &encapsulation, const std::vector<T> &src)
|
||||
{
|
||||
if (src.empty()) {
|
||||
return {};
|
||||
}
|
||||
std::ostringstream oss;
|
||||
for (const T &src_iter: src) {
|
||||
oss << encapsulation.first << src_iter << encapsulation.second << glue;
|
||||
}
|
||||
std::string output(oss.str());
|
||||
output.resize(output.size() - glue.size());
|
||||
return output;
|
||||
}
|
||||
|
||||
bool SharedDatabase::UpdateInjectedCommandSettings(const std::vector<std::pair<std::string, uint8>> &injected)
|
||||
{
|
||||
if (injected.size()) {
|
||||
const std::string query = fmt::format(
|
||||
"REPLACE INTO `command_settings`(`command`, `access`) VALUES {}",
|
||||
Strings::ImplodePair(
|
||||
ImplodePair(
|
||||
",",
|
||||
std::pair<char, char>('(', ')'),
|
||||
join_pair(",", std::pair<char, char>('\'', '\''), injected)
|
||||
@@ -1576,7 +1624,7 @@ bool SharedDatabase::UpdateOrphanedCommandSettings(const std::vector<std::string
|
||||
if (orphaned.size()) {
|
||||
std::string query = fmt::format(
|
||||
"DELETE FROM `command_settings` WHERE `command` IN ({})",
|
||||
Strings::ImplodePair(",", std::pair<char, char>('\'', '\''), orphaned)
|
||||
ImplodePair(",", std::pair<char, char>('\'', '\''), orphaned)
|
||||
);
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
@@ -1586,7 +1634,7 @@ bool SharedDatabase::UpdateOrphanedCommandSettings(const std::vector<std::string
|
||||
|
||||
query = fmt::format(
|
||||
"DELETE FROM `command_subsettings` WHERE `parent_command` IN ({})",
|
||||
Strings::ImplodePair(",", std::pair<char, char>('\'', '\''), orphaned)
|
||||
ImplodePair(",", std::pair<char, char>('\'', '\''), orphaned)
|
||||
);
|
||||
|
||||
auto results_two = QueryDatabase(query);
|
||||
@@ -1665,7 +1713,7 @@ bool SharedDatabase::LoadSpells(const std::string &prefix, int32 *records, const
|
||||
EQ::IPCMutex mutex("spells");
|
||||
mutex.Lock();
|
||||
|
||||
std::string file_name = fmt::format("{}/{}{}", path.GetSharedMemoryPath(), prefix, std::string("spells"));
|
||||
std::string file_name = fmt::format("{}/{}{}", PathManager::Instance()->GetSharedMemoryPath(), prefix, std::string("spells"));
|
||||
spells_mmf = std::make_unique<EQ::MemoryMappedFile>(file_name);
|
||||
LogInfo("Loading [{}]", file_name);
|
||||
*records = *static_cast<uint32*>(spells_mmf->Get());
|
||||
|
||||
+6
-2
@@ -16,12 +16,16 @@ public:
|
||||
static int32_t GetSkillCapMaxLevel(uint8 class_id, EQ::skills::SkillType skill_id);
|
||||
|
||||
SkillCaps *SetContentDatabase(Database *db);
|
||||
|
||||
static SkillCaps* Instance()
|
||||
{
|
||||
static SkillCaps instance;
|
||||
return &instance;
|
||||
}
|
||||
private:
|
||||
Database *m_content_database{};
|
||||
std::map<uint64, SkillCapsRepository::SkillCaps> m_skill_caps = {};
|
||||
};
|
||||
|
||||
extern SkillCaps skill_caps;
|
||||
|
||||
|
||||
#endif //CODE_SKILL_CAPS_H
|
||||
|
||||
+49
-49
@@ -1456,41 +1456,42 @@ bool IsCompleteHealSpell(uint16 spell_id)
|
||||
}
|
||||
|
||||
bool IsFastHealSpell(uint16 spell_id) {
|
||||
spell_id = (
|
||||
IsEffectInSpell(spell_id, SE_CurrentHP) ?
|
||||
spell_id :
|
||||
GetSpellTriggerSpellID(spell_id, SE_CurrentHP)
|
||||
);
|
||||
spell_id = (
|
||||
IsEffectInSpell(spell_id, SE_CurrentHP) ?
|
||||
spell_id :
|
||||
GetSpellTriggerSpellID(spell_id, SE_CurrentHP)
|
||||
);
|
||||
|
||||
if (!spell_id) {
|
||||
spell_id = (
|
||||
IsEffectInSpell(spell_id, SE_CurrentHPOnce) ?
|
||||
spell_id :
|
||||
GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce)
|
||||
);
|
||||
}
|
||||
if (!spell_id) {
|
||||
spell_id = (
|
||||
IsEffectInSpell(spell_id, SE_CurrentHPOnce) ?
|
||||
spell_id :
|
||||
GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce)
|
||||
);
|
||||
}
|
||||
|
||||
if (spell_id && IsValidSpell(spell_id)) {
|
||||
if (
|
||||
spells[spell_id].cast_time <= MAX_FAST_HEAL_CASTING_TIME &&
|
||||
spells[spell_id].good_effect &&
|
||||
!IsGroupSpell(spell_id)
|
||||
) {
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
if (
|
||||
spells[spell_id].base_value[i] > 0 &&
|
||||
(
|
||||
spells[spell_id].effect_id[i] == SE_CurrentHP ||
|
||||
spells[spell_id].effect_id[i] == SE_CurrentHPOnce
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (IsValidSpell(spell_id)) {
|
||||
if (
|
||||
spell_id != SPELL_MINOR_HEALING &&
|
||||
(spells[spell_id].cast_time > MAX_VERY_FAST_HEAL_CASTING_TIME && spells[spell_id].cast_time <= MAX_FAST_HEAL_CASTING_TIME) &&
|
||||
spells[spell_id].good_effect &&
|
||||
!IsGroupSpell(spell_id)
|
||||
) {
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
if (
|
||||
spells[spell_id].base_value[i] > 0 &&
|
||||
(
|
||||
spells[spell_id].effect_id[i] == SE_CurrentHP ||
|
||||
spells[spell_id].effect_id[i] == SE_CurrentHPOnce
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsVeryFastHealSpell(uint16 spell_id)
|
||||
@@ -1509,8 +1510,9 @@ bool IsVeryFastHealSpell(uint16 spell_id)
|
||||
);
|
||||
}
|
||||
|
||||
if (spell_id) {
|
||||
if (IsValidSpell(spell_id)) {
|
||||
if (
|
||||
spell_id != SPELL_MINOR_HEALING &&
|
||||
spells[spell_id].cast_time <= MAX_VERY_FAST_HEAL_CASTING_TIME &&
|
||||
spells[spell_id].good_effect &&
|
||||
!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 (
|
||||
spells[spell_id].cast_time > MAX_FAST_HEAL_CASTING_TIME &&
|
||||
spells[spell_id].target_type == ST_Target &&
|
||||
!IsCompleteHealSpell(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 (
|
||||
(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) &&
|
||||
!IsHealOverTimeSpell(spell_id) &&
|
||||
!IsGroupSpell(spell_id)
|
||||
@@ -1630,7 +1642,7 @@ bool IsRegularGroupHealSpell(uint16 spell_id)
|
||||
);
|
||||
}
|
||||
|
||||
if (spell_id) {
|
||||
if (IsValidSpell(spell_id)) {
|
||||
if (
|
||||
IsGroupSpell(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) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
|
||||
+11
-8
@@ -215,6 +215,7 @@
|
||||
#define SPELL_AMPLIFICATION 2603
|
||||
#define SPELL_DIVINE_REZ 2738
|
||||
#define SPELL_NATURES_RECOVERY 2520
|
||||
#define SPELL_MINOR_HEALING 200
|
||||
#define SPELL_ADRENALINE_SWELL 14445
|
||||
#define SPELL_ADRENALINE_SWELL_RK2 14446
|
||||
#define SPELL_ADRENALINE_SWELL_RK3 14447
|
||||
@@ -736,11 +737,12 @@ namespace BotSpellTypes
|
||||
constexpr uint16 DiscUtility = 203;
|
||||
|
||||
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 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 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 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_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_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 = {
|
||||
@@ -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);
|
||||
|
||||
// Bot related functions
|
||||
bool IsBotSpellTypeDetrimental (uint16 spell_type);
|
||||
bool IsBotSpellTypeBeneficial (uint16 spell_type);
|
||||
bool IsBotSpellTypeDetrimental(uint16 spell_type);
|
||||
bool IsBotSpellTypeBeneficial(uint16 spell_type);
|
||||
bool BotSpellTypeUsesTargetSettings(uint16 spell_type);
|
||||
bool IsBotSpellTypeInnate (uint16 spell_type);
|
||||
bool IsAEBotSpellType(uint16 spell_type);
|
||||
@@ -915,6 +917,8 @@ bool IsCommandedBotSpellType(uint16 spell_type);
|
||||
bool IsPullingBotSpellType(uint16 spell_type);
|
||||
uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id);
|
||||
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..
|
||||
// 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);
|
||||
bool IsBlankSpellEffect(uint16 spell_id, int effect_index);
|
||||
bool IsValidSpell(uint32 spell_id);
|
||||
bool IsValidSpellAndLoS(uint32 spell_id, bool has_los = true);
|
||||
bool IsSummonSpell(uint16 spell_id);
|
||||
bool IsDamageSpell(uint16 spell_id);
|
||||
bool IsAnyDamageSpell(uint16 spell_id);
|
||||
|
||||
+41
-252
@@ -1,4 +1,5 @@
|
||||
#include "spdat.h"
|
||||
#include "../zone/bot.h"
|
||||
|
||||
bool IsBotSpellTypeDetrimental(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
@@ -357,6 +358,7 @@ bool RequiresStackCheck(uint16 spell_type) {
|
||||
case BotSpellTypes::CompleteHeal:
|
||||
case BotSpellTypes::PetCompleteHeals:
|
||||
case BotSpellTypes::GroupCompleteHeals:
|
||||
case BotSpellTypes::Resurrect:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
@@ -417,264 +419,23 @@ uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id) {
|
||||
return UINT16_MAX;
|
||||
}
|
||||
|
||||
uint16 correct_type = UINT16_MAX;
|
||||
SPDat_Spell_Struct spell = spells[spell_id];
|
||||
std::string teleport_zone = spell.teleport_zone;
|
||||
uint16 correct_type = spell_type;
|
||||
|
||||
if (IsCharmSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::Charm;
|
||||
}
|
||||
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 (!Bot::IsValidSpellTypeBySpellID(spell_type, spell_id)) {
|
||||
correct_type = UINT16_MAX;
|
||||
|
||||
if (correct_type == UINT16_MAX) {
|
||||
if (
|
||||
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;
|
||||
}
|
||||
auto start = std::min({ BotSpellTypes::START, BotSpellTypes::COMMANDED_START, BotSpellTypes::DISCIPLINE_START });
|
||||
auto end = std::max({ BotSpellTypes::END, BotSpellTypes::COMMANDED_END, BotSpellTypes::DISCIPLINE_END });
|
||||
|
||||
return correct_type;
|
||||
for (int i = end; i >= start; --i) {
|
||||
if (!Bot::IsValidBotSpellType(i) || i == BotSpellTypes::InCombatBuff) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsVeryFastHealSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::VeryFastHeals;
|
||||
}
|
||||
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 (Bot::IsValidSpellTypeBySpellID(i, spell_id)) {
|
||||
correct_type = i;
|
||||
|
||||
if (IsResistanceOnlySpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::ResistBuffs;
|
||||
}
|
||||
else if (IsDamageShieldOnlySpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::DamageShields;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -708,3 +469,31 @@ uint16 GetPetBotSpellType(uint16 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;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
*/
|
||||
|
||||
#include "strings.h"
|
||||
#include <cereal/external/rapidjson/document.h>
|
||||
#include <fmt/format.h>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
@@ -41,6 +42,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <random>
|
||||
#include <string>
|
||||
@@ -49,6 +51,12 @@
|
||||
#include "strings_legacy.cpp" // legacy c functions
|
||||
#include "strings_misc.cpp" // anything non "Strings" scoped
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#include <ctype.h>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#endif
|
||||
|
||||
std::string Strings::Random(size_t length)
|
||||
{
|
||||
static auto &chrs = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
@@ -701,6 +709,18 @@ std::string &Strings::Trim(std::string &str, const std::string &chars)
|
||||
return LTrim(RTrim(str, chars), chars);
|
||||
}
|
||||
|
||||
const std::string NUM_TO_ENGLISH_X[] = {
|
||||
"", "One ", "Two ", "Three ", "Four ",
|
||||
"Five ", "Six ", "Seven ", "Eight ", "Nine ", "Ten ", "Eleven ",
|
||||
"Twelve ", "Thirteen ", "Fourteen ", "Fifteen ",
|
||||
"Sixteen ", "Seventeen ", "Eighteen ", "Nineteen "
|
||||
};
|
||||
|
||||
const std::string NUM_TO_ENGLISH_Y[] = {
|
||||
"", "", "Twenty ", "Thirty ", "Forty ",
|
||||
"Fifty ", "Sixty ", "Seventy ", "Eighty ", "Ninety "
|
||||
};
|
||||
|
||||
// Function to convert single digit or two digit number into words
|
||||
std::string Strings::ConvertToDigit(int n, const std::string& suffix)
|
||||
{
|
||||
@@ -936,3 +956,11 @@ std::string Strings::Slugify(const std::string& input, const std::string& separa
|
||||
|
||||
return slug;
|
||||
}
|
||||
|
||||
bool Strings::IsValidJson(const std::string &json)
|
||||
{
|
||||
rapidjson::Document doc;
|
||||
rapidjson::ParseResult result = doc.Parse(json.c_str());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+4
-192
@@ -36,52 +36,19 @@
|
||||
#define _STRINGUTIL_H_
|
||||
|
||||
#include <charconv>
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdarg>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
// this doesn't appear to affect linux-based systems..need feedback for _WIN64
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#ifdef _WIN32
|
||||
#include <ctype.h>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace detail {
|
||||
// template magic to check if std::from_chars floating point functions exist
|
||||
template <typename T, typename = void>
|
||||
struct has_from_chars_float : std::false_type { };
|
||||
|
||||
// basically it "uses" this template if they do exist because reasons
|
||||
template <typename T>
|
||||
struct has_from_chars_float < T,
|
||||
std::void_t<decltype(std::from_chars(std::declval<const char *>(), std::declval<const char *>(),
|
||||
std::declval<T &>()))>> : std::true_type { };
|
||||
}; // namespace detail
|
||||
|
||||
namespace EQ {
|
||||
// lame -- older GCC's didn't define this, clang's libc++ however does, even though they lack FP support
|
||||
#if defined(__GNUC__) && (__GNUC__ < 11) && !defined(__clang__)
|
||||
enum class chars_format {
|
||||
scientific = 1, fixed = 2, hex = 4, general = fixed | scientific
|
||||
};
|
||||
#else
|
||||
using chars_format = std::chars_format;
|
||||
#endif
|
||||
}; // namespace EQ
|
||||
|
||||
class Strings {
|
||||
public:
|
||||
static bool Contains(std::vector<std::string> container, const std::string& element);
|
||||
@@ -132,62 +99,8 @@ public:
|
||||
static bool BeginsWith(const std::string& subject, const std::string& search);
|
||||
static bool EndsWith(const std::string& subject, const std::string& search);
|
||||
static std::string ZoneTime(const uint8 hours, const uint8 minutes);
|
||||
|
||||
template<typename T>
|
||||
static std::string
|
||||
ImplodePair(const std::string &glue, const std::pair<char, char> &encapsulation, const std::vector<T> &src)
|
||||
{
|
||||
if (src.empty()) {
|
||||
return {};
|
||||
}
|
||||
std::ostringstream oss;
|
||||
for (const T &src_iter: src) {
|
||||
oss << encapsulation.first << src_iter << encapsulation.second << glue;
|
||||
}
|
||||
std::string output(oss.str());
|
||||
output.resize(output.size() - glue.size());
|
||||
return output;
|
||||
}
|
||||
|
||||
// basic string_view overloads that just use std stuff since they work!
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_floating_point_v<T> && detail::has_from_chars_float<T>::value, std::from_chars_result>
|
||||
static from_chars(std::string_view str, T& value, EQ::chars_format fmt = EQ::chars_format::general)
|
||||
{
|
||||
return std::from_chars(str.data(), str.data() + str.size(), value, fmt);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_integral_v<T>, std::from_chars_result>
|
||||
static from_chars(std::string_view str, T& value, int base = 10)
|
||||
{
|
||||
return std::from_chars(str.data(), str.data() + str.size(), value, base);
|
||||
}
|
||||
|
||||
// fallback versions of floating point in case they're not implemented
|
||||
// TODO: add error handling ...
|
||||
// This does have to allocate since from_chars doesn't need a null terminated string and neither does string_view
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_floating_point_v<T> && !detail::has_from_chars_float<T>::value && std::is_same_v<T, float>, std::from_chars_result>
|
||||
static from_chars(std::string_view str, T& value, EQ::chars_format fmt = EQ::chars_format::general)
|
||||
{
|
||||
std::from_chars_result res{};
|
||||
std::string tmp_str(str.data(), str.size());
|
||||
value = strtof(tmp_str.data(), nullptr);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_floating_point_v<T> && !detail::has_from_chars_float<T>::value && std::is_same_v<T, double>, std::from_chars_result>
|
||||
static from_chars(std::string_view str, T& value, EQ::chars_format fmt = EQ::chars_format::general)
|
||||
{
|
||||
std::from_chars_result res{};
|
||||
std::string tmp_str(str.data(), str.size());
|
||||
value = strtod(tmp_str.data(), nullptr);
|
||||
return res;
|
||||
}
|
||||
|
||||
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, ...);
|
||||
@@ -197,93 +110,6 @@ const std::string vStringFormat(const char *format, va_list args);
|
||||
// Used for grid nodes, as NPC names remove numerals.
|
||||
// But general purpose
|
||||
|
||||
const std::string NUM_TO_ENGLISH_X[] = {
|
||||
"", "One ", "Two ", "Three ", "Four ",
|
||||
"Five ", "Six ", "Seven ", "Eight ", "Nine ", "Ten ", "Eleven ",
|
||||
"Twelve ", "Thirteen ", "Fourteen ", "Fifteen ",
|
||||
"Sixteen ", "Seventeen ", "Eighteen ", "Nineteen "
|
||||
};
|
||||
|
||||
const std::string NUM_TO_ENGLISH_Y[] = {
|
||||
"", "", "Twenty ", "Thirty ", "Forty ",
|
||||
"Fifty ", "Sixty ", "Seventy ", "Eighty ", "Ninety "
|
||||
};
|
||||
|
||||
// _WIN32 builds require that #include<fmt/format.h> be included in whatever code file the invocation is made from (no header files)
|
||||
template<typename T1, typename T2>
|
||||
std::vector<std::string> join_pair(
|
||||
const std::string &glue,
|
||||
const std::pair<char, char> &encapsulation,
|
||||
const std::vector<std::pair<T1, T2>> &src
|
||||
)
|
||||
{
|
||||
if (src.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> output;
|
||||
|
||||
for (const std::pair<T1, T2> &src_iter: src) {
|
||||
output.emplace_back(
|
||||
|
||||
fmt::format(
|
||||
"{}{}{}{}{}{}{}",
|
||||
encapsulation.first,
|
||||
src_iter.first,
|
||||
encapsulation.second,
|
||||
glue,
|
||||
encapsulation.first,
|
||||
src_iter.second,
|
||||
encapsulation.second
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// _WIN32 builds require that #include<fmt/format.h> be included in whatever code file the invocation is made from (no header files)
|
||||
template<typename T1, typename T2, typename T3, typename T4>
|
||||
std::vector<std::string> join_tuple(
|
||||
const std::string &glue,
|
||||
const std::pair<char, char> &encapsulation,
|
||||
const std::vector<std::tuple<T1, T2, T3, T4>> &src
|
||||
)
|
||||
{
|
||||
if (src.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> output;
|
||||
|
||||
for (const std::tuple<T1, T2, T3, T4> &src_iter: src) {
|
||||
|
||||
output.emplace_back(
|
||||
|
||||
fmt::format(
|
||||
"{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}",
|
||||
encapsulation.first,
|
||||
std::get<0>(src_iter),
|
||||
encapsulation.second,
|
||||
glue,
|
||||
encapsulation.first,
|
||||
std::get<1>(src_iter),
|
||||
encapsulation.second,
|
||||
glue,
|
||||
encapsulation.first,
|
||||
std::get<2>(src_iter),
|
||||
encapsulation.second,
|
||||
glue,
|
||||
encapsulation.first,
|
||||
std::get<3>(src_iter),
|
||||
encapsulation.second
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// misc functions
|
||||
std::string SanitizeWorldServerName(std::string server_long_name);
|
||||
std::vector<std::string> GetBadWords();
|
||||
@@ -308,18 +134,4 @@ std::string FormatName(const std::string &char_name);
|
||||
bool IsAllowedWorldServerCharacterList(char c);
|
||||
void SanitizeWorldServerName(char *name);
|
||||
|
||||
template<typename InputIterator, typename OutputIterator>
|
||||
auto CleanMobName(InputIterator first, InputIterator last, OutputIterator result)
|
||||
{
|
||||
for (; first != last; ++first) {
|
||||
if (*first == '_') {
|
||||
*result = ' ';
|
||||
}
|
||||
else if (isalpha(*first) || *first == '`') {
|
||||
*result = *first;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user