Compare commits

..

329 Commits

Author SHA1 Message Date
Chris Miles e8f4ffd628 [Release] 23.10.2 2025-09-17 00:52:31 -05:00
Chris Miles 0447618f7e hotfix: merge fix 2025-09-17 00:50:03 -05:00
Chris Miles e71ce001ff Hotfix: revert #4996 2025-09-17 00:47:42 -05:00
Alex King 1575a2af40 [Hotfix] Fixed Mail Key Bug (#5015)
* [Hotfix] Fixed Mail Key Bug

* Release
2025-09-16 22:52:57 -04:00
JJ 4b69df646c [Release] 23.10.0 (#5014)
* Update CHANGELOG.md

* Update version.h

* Update package.json
2025-09-16 11:35:03 -04:00
Alex King 044b9c1420 [Repositories] Convert Mail Key to Repositories (#5007)
* [Repositories] Convert Mail Key to Repositories

* Update shareddb.cpp

---------

Co-authored-by: JJ <3617814+joligario@users.noreply.github.com>
2025-09-15 14:23:48 -04:00
Alex King 0bbb5b90e7 [Repositories] Convert Total Time Played to Repositories (#5008) 2025-09-15 09:14:59 -04:00
Alex King 6506ad5b51 [Quest API] Add EVENT_CHARM_START and EVENT_CHARM_END (#5013) 2025-09-15 09:14:46 -04:00
Alex King dcaa0ecdaa [Quest API] Add Identifiers to Get/Modify NPC Stat Methods (#5012) 2025-09-15 09:14:29 -04:00
Mitch Freeman 9b143132be [Bug Fix] Fix Several Evolving Item Bugs (#4992)
* Add sending evolving items via parcels
Send, retrieve and inspect tested ok

* Sell evolving items to a merchant and purchase tested ok.

* Fix a problem with potions and quantity
2025-09-07 22:51:39 -04:00
Mitch Freeman 231bf8b4ec Update database_update_manifest.cpp (#5011) 2025-09-07 19:13:00 -04:00
Alex King dee58f9a91 [Repositories] Convert Shared Bank Platinum to Repositories (#5006) 2025-09-01 21:39:24 -04:00
Alex King 1eb89edbbd [Repositories] Convert Item Loading to Repositories (#4998) 2025-08-30 16:39:23 -04:00
Alex King e015d0d67e [Repositories] Convert Damage Shield Types to Repositories (#4995)
* [Repositories] Convert Damage Shield Types to Repositories

* Remove max spell ID
2025-08-30 16:23:33 -04:00
Alex King ba78394ce9 [Repositories] Convert Spell Loading to Repositories (#4996) 2025-08-30 15:42:04 -04:00
Alex King 060f6e377d [Constants] Convert SE Defines to SpellEffect Namespace (#4999) 2025-08-30 15:33:19 -04:00
Alex King 92c8b0e585 [Constants] Change Race Changes to Race Namespace (#5000) 2025-08-30 15:33:11 -04:00
Alex King 33df8ea665 [Repositories] Convert Character Inspect Messages to Repositories (#4997) 2025-08-30 14:42:40 -04:00
Alex King 9668074d94 [Bug Fix] Fix Recipe Inspect Bug (#4994) 2025-08-30 13:56:14 -04:00
Alex King 822a5dcac4 [Cleanup] Cleanup #show ip_lookup Message (#5005)
* [Cleanup] Cleanup #show ip_lookup Message

* Test

* Update clientlist.cpp
2025-08-30 13:05:18 -04:00
Alex King 0c65a4febe [Cleanup] Fix #set race 0 Message (#5004) 2025-08-29 20:08:03 -04:00
Alex King 02f66de679 [Cleanup] Remove Unused errorname Variable (#5001) 2025-08-29 12:02:11 -04:00
Alex King 145111f11e [Bug Fix] Fix Task Reloading (#5002) 2025-08-29 06:52:51 -04:00
Alex King a4e47d9180 [Pets] Add Pet Constants and Methods (#4987)
* [Pets] Add Pet Constants and Methods

* Remove GetID

* GetPetTypeName()

* Cleanup
2025-08-17 01:00:31 -04:00
Alex King 207ee2daa0 [Database] Add heal_amount to character_stats_record (#4986)
* [Database] Add heal_amount to character_stats_record

* Update version.h
2025-08-16 21:35:55 -04:00
Alex King 1fe5d9fa4f [Commands] Add #task complete Saylink to #task show (#4985) 2025-08-16 21:35:38 -04:00
Alex King ed3f6c2a40 [Cleanup] Fix Issues with Strings::Commify and Mob::SendStatsWindow (#4984) 2025-08-16 21:35:13 -04:00
Alex King adb3196ca5 [Cleanup] Add #npcedit npc_tint_id Help Message (#4982)
* [Cleanup] Add #npcedit npc_tint_id Help Message

* Update npcedit.cpp

* Update npcedit.cpp
2025-08-16 21:34:55 -04:00
Alex King e13b133ac8 [Quest API] Add GetNPCTintIndex() to Perl/Lua (#4983) 2025-08-16 21:34:39 -04:00
Alex King d475428157 [Quest API] Add GetKeyRing() to Perl/Lua (#4980) 2025-08-16 21:34:28 -04:00
Alex King 00b66ce432 [Commands] Add #find account Subcommand (#4981)
Credit goes to https://github.com/The-Heroes-Journey-EQEMU/Server/pull/281
2025-08-16 21:34:16 -04:00
Alex 4d12dd5c43 [Code] Remove Attributions (#4988) 2025-08-15 23:36:35 -05:00
regneq c4f408bffc [Bug Fix] Fix Race 474 for Titanium (#4979) 2025-08-11 16:49:59 -04:00
Alex King d876c6df2a [Bug Fix] Fix #show recipe uint16 Cap (#4978) 2025-08-11 14:06:13 -04:00
Alex King d142e1ca81 [Hotfix] Fix Quest Ownership Edge Case (#4977)
* [Hotfix] Fix Quest Ownership Edge Case

* Push
2025-08-02 22:09:30 -05:00
Chris Miles d9f4d49ef4 [Release] 23.9.0 (#4976) 2025-08-02 20:11:29 -05:00
Alex King 0a1df5bbb6 [Quest API] Add GetTimers() and GetPausedTimers() to Perl/Lua (#4965)
* [Quest API] Add GetTimers() and GetPausedTimers() to Perl/Lua

* Push
2025-08-02 19:17:56 -05:00
dependabot[bot] 41f3d7ff31 Bump golang.org/x/oauth2 in /utils/scripts/build/should-release (#4967)
Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.0.0-20180821212333-d2e6202438be to 0.27.0.
- [Commits](https://github.com/golang/oauth2/commits/v0.27.0)

---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-version: 0.27.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-02 19:16:57 -05:00
solar 0afef19d26 [Loginserver] Fix Legacy World When Using Local DB (#4970)
Co-authored-by: solar <solar@heliacal.net>
2025-08-02 19:16:35 -05:00
Alex King e9be2d76c3 [Commands] Add #show keyring Subcommand (#4973) 2025-08-02 19:15:44 -05:00
solar ffa813b92c [Build] Fix Linking with GCC (#4969)
Co-authored-by: solar <solar@heliacal.net>
2025-08-02 19:15:11 -05:00
solar 909de47acd fix player movement deltas (jumping, turning) (#4975)
Co-authored-by: solar <solar@heliacal.net>
2025-08-02 10:32:30 -04:00
regneq 73a5f11e17 [Bug Fix] [Bug Fix] Show player count on the server list status. (#4971) 2025-07-30 19:39:55 -04:00
Alex King 9544e100c3 [Bug Fix] Fix Hero's Forge Ingame and Character Select (#4966) 2025-07-30 19:39:29 -04:00
Chris Miles f3232cdc3a [Database] Add Indexes to NPC's Spawns Loot (#4972) 2025-07-30 09:37:23 -04:00
Bemvaras b8884d6572 [Quest API] Add GetMemberRole() to Perl/Lua (#4963)
* Add group member role retrieval

* Expose group member role to Lua

* Cleanup

* Cleanup

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2025-07-10 15:09:04 -04:00
Alex King 323a0c0b27 [Feature] Zone Scripting (#4908)
* [Feature] Add Zone Scripting Capabilities

* Push

* Update zone.cpp

* Fix crashes

* Lua

* Add other events, finish Lua

* Add EVENT_ENTER_ZONE

* Final

* Push

* Push

* [Feature] Add Zone Scripting Capabilities

* Push

* Update zone.cpp

* Fix crashes

* Add EVENT_ENTER_ZONE

* Remove duplicates

* Update embparser.cpp
2025-07-10 15:08:08 -04:00
regneq ab45d4358d [Bug Fix] Add a missing Froglok starting area for Titanium Startzone. (#4962)
* Add a missing Froglok starting area for Titanium Startzone.

* fix formatting.

* add a missing break.
2025-07-04 12:53:45 -04:00
nytmyr f7775c7a75 [Bots] Fix FinishBuffing rule (#4961) 2025-06-30 19:54:04 -05:00
Chris Miles 659a960401 [Performance] Clear Wearchange Deduplication Cache (#4960)
* [Performance] Clear Wearchange Deduplication Cache

* Move timers to process loops
2025-06-30 13:57:43 -05:00
Chris Miles 07d484597d [Build] More Build Speed Improvements (#4959) 2025-06-30 13:09:23 -05:00
nytmyr fc470d5f83 [Bots] Fix ^cast resurrects (#4958)
- Was failing stack checks and couldn't be casted. Added bypass.
2025-06-29 10:19:00 -04:00
Chris Miles 585ed3bd25 [Release] 23.8.1 2025-06-28 17:29:25 -05:00
Chris Miles 2eb291a461 [Databuckets] Revert Caching Changes of #4917 (#4957)
* [Databuckets] Revert Caching Changes of #4917

* Comment caching tests until addressed later
2025-06-28 17:28:19 -05:00
Alex King a1421af214 [Bug Fix] Fix Hero Forge on Character Select (#4954) 2025-06-26 19:35:28 -04:00
Alex King 13aad6229f [Crash Fix] Fix Possible Crashes with Raid Methods (#4955) 2025-06-26 19:35:20 -04:00
Alex King 76d46ceaf0 [Bug Fix] Fix FindCharacter Using content_db (#4956) 2025-06-26 19:35:11 -04:00
Chris Miles 389047c4e2 [Release] 23.8.0 (#4953)
* [Release] 23.8.0

* Changelog

* Update ruletypes.h
2025-06-25 21:12:05 -05:00
Chris Miles 2c6d405b2c [Hotfix] Fix ClientList Singleton Shortcomings of #4942 2025-06-25 15:50:34 -05:00
Fryguy 50ae0f8351 [Corpses] Add corpse entity variables to DB (#4911)
* [Corpses] Add corpse entity variables to DB

- When corpses have entity variables add/remove/modified it will update the database accordingly.
- Corpse loading will pull the database values if they exist.

* Updates per review

* Update version.h

---------

Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-06-25 15:21:52 -05:00
Alex King dc261bb203 [Code] ZSList Global to Singleton Cleanup (#4940)
* [Code] ZSList Global to Singleton Cleanup

* Final

* Post merge fixes

---------

Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-06-25 15:16:44 -05:00
Alex King 643ee56433 [Code] PlayerEventLogs Global to Singleton Cleanup (#4928)
* [Code] PlayerEventLogs Global to Singleton Cleanup

* Fix

---------

Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-06-25 15:05:33 -05:00
Alex King c0bb32ed12 [Code] WorldContentServer Global to Singleton Cleanup (#4939) 2025-06-25 14:55:39 -05:00
Alex King c99bda3f47 [Code] WebInterfaceList Global to Singleton Cleanup (#4935) 2025-06-25 14:49:32 -05:00
Alex King 967a13e692 [Code] WorldEventScheduler and ZoneEventScheduler Global to Singleton Cleanup (#4932) 2025-06-25 14:47:37 -05:00
Alex King f304f9cc61 [Code] QueryServConnection Global to Singleton Cleanup (#4938)
* [Code] QueryServConnection Global to Singleton Cleanup

* Fix missed file in PR

---------

Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-06-25 14:43:01 -05:00
Alex King 004e2ca63f [Code] AdventureManager Global to Singleton Cleanup (#4931)
* [Code] AdventureManager Global to Singleton Cleanup

* Post merge fix

---------

Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-06-25 14:38:24 -05:00
Alex King 128732e05d [Code] EvolvingItemsManager Global to Singleton Cleanup (#4929)
Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-06-25 14:26:29 -05:00
Alex King 1e6a4dac78 [Code] UCSConnection Global to Singleton Cleanup (#4937)
Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-06-25 14:25:37 -05:00
Alex King 654764685a [Code] LFGuildManager Global to Singleton Cleanup (#4927)
* [Code] LFGuildManager Global to Singleton Cleanup

* Update lfguild.h

* Update worldserver.cpp

---------

Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-06-25 14:19:52 -05:00
Alex King c5ab35e4af [Code] SharedTaskManager Global to Singleton Cleanup (#4930)
Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-06-25 14:11:40 -05:00
Chris Miles 947795f1d1 [Code] EQEmuLogSys Global to Singleton Cleanup (#4925)
* [Code] EQEmuLogSys Global to Singleton Cleanup

* Post merge fix
2025-06-25 14:05:49 -05:00
Alex King 1153c9ab96 [Code] LoginServerList Global to Singleton Cleanup (#4941) 2025-06-25 13:49:55 -05:00
Alex King 2128b45313 [Code] ClientList Global to Singleton Cleanup (#4942) 2025-06-25 13:49:42 -05:00
Alex King 4f7ff2d6f2 [Code] DatabaseUpdate Global to Singleton Cleanup (#4943)
Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-06-25 13:48:18 -05:00
Alex King b5f1e99d3b [Code] DiscordManager Global to Singleton Cleanup (#4926)
* [Code] DiscordManager Global to Singleton Cleanup

* Update discord_manager.h

---------

Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-06-25 13:47:47 -05:00
Chris Miles 83918ce020 [Performance] Wearchange Packet Send Deduplication (#4916)
* [Performance] Wearchange Packet Send Deduplication

* Update mob_appearance.cpp

* Reduce one allocation

* Update mob_appearance.cpp

* Change

* Update mob_appearance.cpp

* Update mob_appearance.cpp

* Update mob_appearance.cpp

* Update mob_appearance.cpp

* Update mob_appearance.cpp

* Wut

* ffs
2025-06-25 13:19:29 -05:00
Alex King f3aaeff0a9 [Code] PetitionList Global to Singleton Cleanup (#4944)
* [Code] PetitionList Global to Singleton Cleanup

* Update worldserver.cpp

* Update worldserver.cpp
2025-06-25 13:15:21 -05:00
Chris Miles 98eff43346 chore: Update language for clarity and attribution 2025-06-25 13:09:43 -05:00
Bemvaras 46b43a990f [Quest API] Add CompleteTask and UncompleteTask methods to Perl/Lua (#4921)
* Add quest::uncompletetask to quest API

* Remove CMakeSettings.json (local build config) from PR

* Push

* Final

* Update embparser_api.cpp

* Update questmgr.cpp

* Update lua_client.cpp

* Fix parcel slot duplication for evolving item augments

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-06-25 13:03:23 -05:00
Chris Miles ea96cbf885 [Databuckets] Nested Caching (#4917)
* [Databuckets] Nested Caching

* One more

* Update benchmark_databuckets.cpp

* Add caching tests

* Fix tests and scoping

* Update databuckets.cpp

* Fix tests

* Rebase fixes

* [Databuckets] Implement Cache in World (#4920)
2025-06-25 12:47:43 -05:00
regneq de07870c99 [Fix] Fix breaking changes to Titanium, SoF, SoD patches causes by big bags update (#4922)
* [Fix] Fix breaking change to Titanium patches caused by Big Bags update.

* Fixes for Titanium, SoD, SoF causes by big bag bug.
2025-06-25 12:41:13 -05:00
Chris Miles b3b228c26c [Code] PCH Cleanup (#4952)
* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update app-pch.h

* Update CMakeLists.txt

* Split out pch to zone

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update windows-build.ps1

* Update windows-build.ps1

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt

* Update CMakeLists.txt
2025-06-25 12:26:38 -05:00
Fryguy 27b5c80c3c [Quest API] Lua Client Scoped EnableTitleSets (#4914)
Example usage:
```lua
client:EnableTitleSet(reward.title_set)
```
2025-06-25 12:25:35 -05:00
Chris Miles fb4d003e19 [Player Events] Zone Fetch Settings from QS if Enabled (#4915)
* [Player Events] Zone Fetch Settings from QS if Enabled

* Update zonelist.cpp

* Update zoneserver.cpp
2025-06-25 12:21:35 -05:00
Chris Miles f025e5741b [Player Event Logs] Don't Clear Event Data on ETL Events (#4913)
* [Player Event Logs] Don't Clear Event Data on ETL Events

* Migration
2025-06-24 20:15:22 -04:00
Chris Miles c3f2708f1b [Player Events] Don't Write Empty Data (#4912)
* [Player Events] Don't Write Empty Data

* Revert "[Player Events] Don't Write Empty Data"

This reverts commit f64a824b33.

* Update player_events.h

* Augs
2025-06-24 20:14:51 -04:00
Alex King 2e760d6397 [Code] ZoneStore Global to Singleton Cleanup (#4934) 2025-06-23 06:45:06 -04:00
Chris Miles bac892b582 [API] Add wwmarquee <type> <message> (#4919)
* [API] Implement Console "wwmarquee"

* Fin
2025-06-23 05:25:50 -04:00
Chris Miles 4005b68383 [Code] Optimize PCH Config (Faster Builds) (#4951) 2025-06-22 13:52:23 -05:00
Chris Miles 5ac9dd04e4 [Code] Cleanup Strings Header (#4950)
* [Code] Cleanup Strings Header

* Include optimize
2025-06-22 13:52:13 -05:00
Alex King f0c041e8b3 [Code] EQ::Random Global to Singleton Cleanup (#4936) 2025-06-22 14:13:58 -04:00
Alex King 2f4a5b56dd [Code] SkillCaps Global to Singleton Cleanup (#4933) 2025-06-22 14:13:16 -04:00
Alex King 940f97c9ae [Code] TaskManager Global to Singleton Cleanup (#4945)
* [Code] TaskManager Global to Singleton Cleanup

* Remove checks for existence

* Final
2025-06-22 14:10:48 -04:00
Chris Miles f8ee664b27 [Code] Remove Lua Rule Constants (#4949) 2025-06-22 13:55:55 -04:00
Chris Miles df86ad371b [Code] PathManager Global to Singleton Cleanup (#4924) 2025-06-22 13:50:52 -04:00
Chris Miles e846bb86b6 [Code] Remove Regex Compile Bloat (#4947) 2025-06-22 02:08:15 -05:00
Chris Miles 3e6a3e2168 [Build] Significantly Improve Build Times Using Unity Builds (#4948) 2025-06-22 02:08:03 -05:00
Alex King 2aebf1a78a [Code] Remove Unused MZoneShutdown Mutex (#4946) 2025-06-22 01:49:39 -05:00
Chris Miles 1be7e56b86 [Databuckets] Move Databuckets to Common (#4918)
* [Databuckets] Move Databuckets to Common

* Fix linking issue
2025-06-16 16:48:29 -05:00
Chris Miles a0ff9d67a1 [Fix] Bulk Send Corpses after Idle State (#4910) 2025-06-09 14:31:28 -05:00
Chris Miles befee1c729 [Quests] Support Multiple Quest, Plugin, and Lua Module Paths (#4906)
* [Quests] Add Support for Multiple Load Paths

* Adjust load paths

* plugin != m_lua_module_directories
2025-06-09 12:49:46 -05:00
nytmyr 3d70063a68 [Doors] Fix door saving for versions (#4905)
* [Doors] Fix door saving for versions

- Door saving wasn't saving to the proper version on `#door save`

* Update doors.cpp

---------

Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-06-09 12:47:56 -05:00
JJ 4e28bcf85e Bump libuv to 1.50.0 (#4898) 2025-06-09 12:40:37 -05:00
JJ 687d10960a [Logs] Fix output for tasks in logs (#4907) 2025-06-09 12:32:45 -05:00
Chris Miles 567d46c3d6 [Performance] Auto Idle / AFK (#4903)
* [Performance] AFK Client Packet Filtering

* Player feedback

* Update client_packet.cpp

* Fixes

* Streamline updates to SetAFK

* Decouple idling and AFK and manual AFK

* Reset clock timer when we take AFK or idle off

* Exclude bard songs in non combat zones from resetting timer

* GM exclusion adjustments
2025-05-22 13:08:32 -05:00
Chris Miles 53cc2de459 [World API] Input Validation (#4904)
* [World API] Input Validation

* Update eqemu_api_world_data_service.cpp

* Add db ping to player events processor, move back into main thread
2025-05-22 13:08:17 -05:00
Chris Miles cb866cba31 [Release] 23.7.0 (#4902) 2025-05-19 12:31:37 -05:00
JJ e2162c08da [CLI] Add custom database version output (#4901)
* Add custom database version output to CLI from #4892

* Spacing alignment
2025-05-18 11:28:10 -05:00
Chris Miles e657953b8f [Netcode] Resend Logic Adjustments (#4900)
* Timeout adjustment

* Update daybreak_connection.cpp

* Update daybreak_connection.cpp

* Update daybreak_connection.cpp

* Update daybreak_connection.cpp

* Update daybreak_connection.cpp

* Update daybreak_connection.cpp

* Stuff

* Update daybreak_connection.h

* Update daybreak_connection.cpp

* Update daybreak_connection.cpp

* Update daybreak_connection.cpp

* Update daybreak_connection.cpp
2025-05-18 11:19:37 -05:00
JJ eb366e67b7 [CLI] Fix MySQL check in database dumper (#4897) 2025-05-16 18:56:38 -05:00
JJ 5b728a42f7 Update zonelist.h (#4896) 2025-05-16 18:17:38 -04:00
Chris Miles a1d414d64c [Netcode] Resend Logic Adjustments (#4895)
* [Netcode] Resend Logic Adjustments

* Update daybreak_connection.h
2025-05-16 16:46:07 -05:00
Chris Miles 907029ed76 [Database] Remove Transaction Wrapped Character Save (#4894) 2025-05-16 16:07:45 -05:00
Chris Miles 894f22fba0 [Fix] Deadlock on failed #copycharacter commands (#4887) 2025-05-16 13:39:34 -05:00
Chris Miles 7ec09d7e0f [Player Events] Add rule to ignore configured GM commands (#4888) 2025-05-16 13:39:24 -05:00
Chris Miles c82266790a [Zone State] Load New Spawn2 Data When Present (#4889) 2025-05-16 13:39:16 -05:00
Chris Miles ec31fddbae [Logging] Auto Update Log Category Names (#4890) 2025-05-16 13:39:08 -05:00
Chris Miles fb49ce2404 [Rules] Auto Update Rule Notes from Source (#4891)
* [Rules] Auto Update Rule Notes from Source

* Update rulesys.cpp
2025-05-16 13:38:59 -05:00
Chris Miles 276b7e238a [Database] Add Custom Database Migrations for Operators (#4892)
* [Database] Add Custom Database Migrations for Operators

* Changes

* Update database_update_manifest_custom.cpp
2025-05-16 13:38:51 -05:00
Chris Miles c7a463420b [World] Fix Rarer Reload Deadlock (#4893) 2025-05-16 13:38:42 -05:00
Chris Miles a56bb52808 Update README.md 2025-05-15 15:29:05 -05:00
Chris Miles 0bbdb58679 Update README.md 2025-05-15 15:26:07 -05:00
Alex King f29478c105 [Commands] Add #zonevariable Command (#4882)
* [Commands] Add #zonevariable Command

* Update zonevariable.cpp

* Argument safety

---------

Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-05-14 20:53:43 -05:00
Chris Miles 888a88f966 [Release] 23.6.0 (#4885) 2025-05-14 20:50:49 -05:00
Alex King 3b617a6652 [Quest API] Add Last Login and First Login Flags to EVENT_CONNECT (#4866)
* [Quest API] Add Last Login and First Login Flags to EVENT_CONNECT

* Push

* Update base_character_data_repository.h

* Update version field

---------

Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-05-14 20:46:25 -05:00
Alex King c36c336bc7 [Performance] Store Player Title Sets in Client Memory (#4836)
* [Performance] Store Player Title Sets in Zone Memory

* Move to client memory

* Update client_packet.cpp

* Update zonedb.cpp

* Save only when necessary

* Single Insert

* Add optional insert flag

* Update client.h

* Consolidation
2025-05-14 20:46:11 -05:00
zrix-eq 4a9779635d [Feature] Add Character:TradeskillUpMinChance rule (#4867) 2025-05-14 20:27:09 -05:00
Mitch Freeman 20da490bda [Feature] Enable spawn attribute for NPCTintID (#4871)
* Add scripting for NPCTintIndex

* AddNPCTintID

Add NPCTintID to spawn logic

* Update base_npc_types_repository.h

* Correct version.h
2025-05-14 20:26:35 -05:00
Mitch Freeman 1221e88d92 [Fix] Add trader/buyer cleanup actions (#4843)
* Add trader/buyer cleanup actions

Add trader/buyer db cleanup for
- on zone idle
- on client first login
- when world drops a zone connection
- in Client::ProcessMovePC

Cleanup several compiler warnings

* Formatting Updates
2025-05-14 20:24:59 -05:00
nytmyr a2b28b2e16 [Bots] Correct ^pull logic and add checks for Enchanter pets (#4827)
* [Bots] Fix bots getting stuck on ^pull

- Removed initial LoS check from bots and rely only on owner when told to ^pull. This prevents bots from getting stuck in a loop if they don't have LoS when trying to initiate the pull.

* [Bots] Add valid state checks to ^clickitem

- Bots previously weren't checking if they were in a valid state or not before trying to click an item when told (in a death state, held, feared, silenced, in group/raid, etc.)

* [Bots] Add valid state checks to ^clickitem

- Bots previously weren't checking if they were in a valid state or not before trying to click an item when told (in a death state, held, feared, silenced, in group/raid, etc.)

Miscommit

* undo

* Cleanup and Fixes

- Cleanup
- Fixed Animation Empathy checks and consolidates
- Enchanter pets will honor Animation Empathy rules all around
- Pets will properly guard if allowed when pulling
2025-05-14 20:21:25 -05:00
zimp-wow 3d607d352c [Fix] Fix Object Name Init, User Refs, and Client Sync on Close (#4861)
* [Bugfix] Fix uninitialized char* in object.cpp.

* [Bugfix] Clear object user and user tradeskill object on reset.

* [Bugfix] Send clear object packet on Object::Close()
2025-05-14 20:20:57 -05:00
JJ 83cd8119c8 [CLI] ETL Settings Output (#4873)
* Update etl_get_settings.cpp

* Update etl_get_settings.cpp

---------

Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-05-14 20:19:30 -05:00
nytmyr 4de8fbbd56 [Bots] Fix creation limit, spawn limit, level requirement checks (#4868)
* [Bots] Fix creation limit, spawn limit, level requirement checks

- Previously if buckets were being used to control any of these values and the appropriate rule was set to 0, unset class specific buckets would override the main limit buckets.
- For example, if `Bots:SpawnLimit` is set to `0` and a player has their `bot_spawn_limit` set to `5` but they don't have a class bucket set for the class they're attempting to spawn a Cleric, the unset `bot_spawn_limit_Cleric` would return 0 and prevent Clerics from being spawned.
- This affected spawn limits, creation limits and level requirements to use bots if controlled by buckets.
- `#gm on` is required to be on for those beyond the ruled min status requirements to bypass the limits.

Rewrote checks and tested every scenario of set unset rules/buckets.

* Cleanup, fix bot count

- Fixes QueryBotCount to not account for soft deleted bots (`-deleted-`)
2025-05-14 20:13:50 -05:00
nytmyr 780120036d [Bots] Move all spell_id instances to uint16 (#4876)
* [Bots] Move all spell_id instances to uint16

* Alignment
2025-05-14 19:58:18 -05:00
catapultam-habeo f3697e633c [Fix] Prevent Ranged Attack from being triggered at arbitrary rate (#4879) 2025-05-14 19:55:14 -05:00
hbingram 24f8d88333 [Fix] Fix breaking change to UF patches caused by Big Bags update (#4883)
* Fix breaking change to UF patches caused by Big Bags update

* Update comment

* Removed original line that was commented.
2025-05-14 19:51:48 -05:00
carolus21rex 21bd906a4d [Crash] Fix crash bug with pbae and quest scripts spawning mobs (#4884)
If a PBAE is in progress while a mob pops it can cause a crash. Forcing a copy before the ae has a chance to pop a mob should fix the weirdness
2025-05-14 19:50:12 -05:00
JJ 138612bc88 Only resume NPC if npc_id is ready (#4875) 2025-05-02 16:25:18 -04:00
dependabot[bot] 5919bb4dea Bump golang.org/x/net in /utils/scripts/build/should-release (#4864)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.36.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.36.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-02 16:24:44 -04:00
Alex King 99d249fefd [Bug Fix] Fix Crash with #task (#4874) 2025-04-30 19:15:53 -04:00
Mitch Freeman c08f286817 AddUntargetableToSpawn (#4872)
Adds the db entry for untargetable to the spawn struct
2025-04-28 17:50:57 -04:00
nytmyr cd003ff0b7 [Cleanup] Fix typo in QueryNameAvailablity (#4869) 2025-04-28 17:49:03 -04:00
nytmyr 1a539f6656 [Hotfix] Fix #copycharacter command (#4860)
This was failing due to a column change in `inventory` from `charid` to `character_id` and would result in no inventory for the new character.
2025-04-28 17:48:30 -04:00
nytmyr 7e7fb7b758 [Bots] Prevent non-taunters from potentially fleeing mob on TargetReflection (#4859)
- Casters could endlessly flee a mob if they had target reflection and were too close.
- Prevents all bots that aren't taunting from adjusting if they're too close and have target reflection. This is safer than limiting it to just casters and ranged bots to prevent situations where melee bots might not be able to outrun the mob to get to their melee range and continually flee.
- WE'LL GET THIS RIGHT EVENTUALLY /sigh
2025-04-28 17:46:12 -04:00
Mitch Freeman 5ae87b40e2 Add some evolvingitem crash checks seen over the past few months. (#4870) 2025-04-28 17:45:15 -04:00
Mitch Freeman 0ec07daebb Fix an edge case for sending a no drop item within a parcel (#4865) 2025-04-28 17:43:31 -04:00
Mitch Freeman 9869da2a0a Resolve guild messaging for incorrect guild tags when creating/deleting guilds. (#4863) 2025-04-28 17:43:04 -04:00
Mitch Freeman 617eb4432b Resolve empty trader lists in the bazaar window. This resolves a typo in the GetDistinctTraders routine. (#4862) 2025-04-28 17:41:45 -04:00
Chris Miles a2b2a6a5cf [Release] 23.5.0 (#4858) 2025-04-10 02:11:20 -05:00
Chris Miles 43a5bff84a [Performance] Network Ring Buffers (#4857)
* [Performance] Network Ring Buffers

* Cursor versus linear scan (wtf GPT)
2025-04-10 02:02:25 -05:00
KayenEQ e983d07228 [Spells] Fear resistance effects edge case fixes and support for SPA 102 as an AA (#4848)
* Clean up for fear related bonus variables

SPA 181 FearResistChance  (% chance to outright resist fear) and SPA 102 Fearless (on live used only on pets for  full fear immunity if presents)

The way were calculating the bonuses was mixing the variables we used for each together in a way that could cause conflicts when combined with negate spell effect.

While doing some live testing was able to confirm SPA102 when cast on pets will terminate the fear on the next tick which is consistent with how we have the mechanic coded.

* Update spells.cpp

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2025-04-09 23:49:55 -05:00
catapultam-habeo 90db12483a [Feature] Add rule to consume command text from any channel (#4839)
* vanilla implementation

* fix typo
2025-04-09 22:45:09 -05:00
nytmyr ff71cfbd5b [Bots] Positioning rewrite (#4856)
* [Bots] Positioning rewrite

- Fixes positioning issues where bots could run back and forth or stay at the target's feet.
- Adds checks to ensure bots don't spam positioning updates and complete their initial positioning.
- Cleans up logic and makes it easier to read.

* Cleanup
2025-04-09 22:39:59 -05:00
Mitch Freeman 08bb9de437 [Fix] Add the bazaar search limit to query (#4829) 2025-04-09 21:44:19 -05:00
Chris Miles 16e341906d [Fix] Zone State Spawn2 Location Restore (#4844)
* [Fix] Zone State Spawn2 Location Restore

* Update zone_save_state.cpp

* Update zone_save_state.cpp

* Update zone_save_state.cpp

* Update zone_save_state.cpp
2025-04-09 21:43:18 -05:00
Mitch Freeman 216b3a039f [Fix] Bazaar Search window not working in a DZ (#4828) 2025-04-09 21:33:48 -05:00
nytmyr b813cf71bb [Bots] Add valid state checks to ^clickitem (#4830)
* [Bots] Add valid state checks to ^clickitem

- Bots previously weren't checking if they were in a valid state or not before trying to click an item when told (in a death state, held, feared, silenced, in group/raid, etc.)

* Add error output on no bots selected.=
2025-04-09 21:33:11 -05:00
nytmyr 48ecd1222f [Bots] Restore old buff overwrite blocking (#4832)
- [#4756](https://github.com/EQEmu/Server/pull/4756) changed the way overwriting worked for buffs. It allowed stronger spells to overwrite lesser spells if they were already on the target.
- However, this is a positive change but has its drawbacks with spells of similar value. It could cause an endless buff loop where they constantly replaced one another.

Overall, the con of endless buffing outweighs the benefit of automatic overwriting lesser spells and the control should be left to only spell lists via `^spells` for bot owners as it was before to determine which buffs bots will cast.
2025-04-09 21:32:57 -05:00
nytmyr e758b407e9 [Bots] Flag all buffs with SE_DamageShield as Damage Shield (#4833)
- Certain resist spells have a DS bonus on them and can be casted as a resist rather than a damage shield. This forces anything with a damage shield as a damage shield buff.
2025-04-09 21:32:38 -05:00
KayenEQ 758dd1875e [Spells] Update to SPA 180 SE_ResistSpellChance to not block unresistable spells. (#4847)
* SPA180 bypassed by field no_resist (field209)

SPA 180 SE_ResistSpellChance provides chance to resist spells outright. This should not be checked to resist a spell if spell has (field209 / no_resist) set to 1.

* Update spells.cpp

confirmed on live unresistable spells are not blocked by sanctification disc or related AA's
2025-04-09 21:31:27 -05:00
KayenEQ 8e2961dda5 [Spells] Update to SPA 378 SE_SpellEffectResistChance (#4845)
* SPA 378 update

Update to SPA 378

SE_SpellEffectResistChance provides chance to resist specific spell effects.

This updates allows support for multiple resistance checks against different effects in same spell.

Example. If a spell has a silence and a blind effect and the target has an AA that gives 10% chance to resist silence and 5% chance to resist blind. It will roll against effect respectively each giving a chance to resist the spell.

* Update spells.cpp

Unresistable spells (resisttype = 0) do not ever resist a spell even with SPA378

Example: Pheonix Charm in Plane of Fire, or Storm Comet in Bastion of Thunder. Parsed both and never got a resist despite having the AA that has effect specific resists which both those spells match.
2025-04-09 21:23:39 -05:00
JJ 5522eda6e4 [Cleanup] Update link for legacy EQEmu loginserver account setup (#4826) 2025-04-09 21:15:22 -05:00
Chris Miles ac1469bac2 [Performance] Pre-Compute CLE Server Lists (#4838)
* [Performance] Pre-Compute CLE Server Lists

* Remove debug
2025-04-09 21:11:52 -05:00
zimp-wow c2989e019a [Bugfix] Prevent depops from blocking new spawns. (#4841) 2025-04-09 21:11:05 -05:00
JJ e16b481ba2 [Cleanup] Remove queryserv dump flag (#4842)
Since queryserv tables are deprecated, there is no longer a need to use this flag
2025-04-09 21:10:46 -05:00
zimp-wow 4fc0ffd173 [Bugfix] Load zone variables before encounter_load. (#4846) 2025-04-09 20:59:19 -05:00
Chris Miles b883888a19 [Performance] Character Save Optimizations (#4851) 2025-04-09 20:56:24 -05:00
Chris Miles 50ad97aa0b [Fix] Databuckets Account Cache Loading (#4855) 2025-04-09 20:56:11 -05:00
zimp-wow dca892e258 [Bugfix] Prevent final shutdown from persisting incomplete state. (#4849)
* [Bugfix] Prevent final shutdown from persisting incomplete state.

* [Bugfix] Change shutdown parameters to member variable.
2025-04-09 20:55:56 -05:00
Alex f9fe4ea2ec [Fix] FixHeading Infinite Loop Fix (#4854)
Co-authored-by: KimLS <KimLS@peqtgc.com>
2025-04-09 20:55:35 -05:00
Chris Miles cc30c72538 [API] World API Optimizations (#4850) 2025-04-09 20:55:13 -05:00
JJ d1fd40cd85 [Database] Fix manifest for helmtexture in horses table (#4852)
* Update database_update_manifest.cpp

* Update database_update_manifest.cpp
2025-04-09 20:53:41 -05:00
Chris Miles f3af458cb3 [Netcode] Fix Stale Client Edge Case (#4853)
* [Netcode] Fix Stale Client Edge Case

* [Netcode] Make sure we always set m_close_time
2025-04-09 20:52:46 -05:00
Akkadius a093d04594 [Hotfix] Remove QS Tables From Export 2025-04-09 20:17:28 -05:00
zimp-wow 115df81400 [Bug Fix] Fix missing timer_name check on Mob::StopTimer (#4840) 2025-04-03 20:40:13 -04:00
Chris Miles 5babc864b9 [Fix] Regression in World SendEmoteMessageRaw (#4837) 2025-04-03 11:17:09 -05:00
Chris Miles 60a2dd8616 [Crash] Fix rarer exception crash issue in PlayerEventLogs::ProcessBatchQueue (#4835) 2025-04-02 19:22:40 -05:00
Akkadius 9be2485330 [Hotfix] Backfill expire_at (not sure why this didn't make it in there to begin with) 2025-03-31 02:34:35 -05:00
Akkadius a2bf10624a Update database_update_manifest.cpp 2025-03-31 02:29:25 -05:00
Akkadius ed58d16f1f [Hotfix] Make sure we don't expire default value instances 2025-03-31 02:25:07 -05:00
Akkadius e9b45fb360 Update CHANGELOG.md 2025-03-30 17:05:27 -05:00
Chris Miles 803972873a [Database] Wrap PurgeExpiredInstances in a Transaction (#4824) 2025-03-30 17:03:57 -05:00
Chris Miles d9e57eca79 [Fix] Instance DZ Creation (#4823)
* [Fix] Instance DZ Creation

* Update dynamic_zone_base.cpp
2025-03-30 16:59:29 -05:00
JJ 6429dc80d3 [Release] 23.4.0 (#4822)
* Update CHANGELOG.md

* Update package.json

* Update version.h
2025-03-30 16:12:42 -05:00
JJ c4262b3fa6 [Cleanup] UCS Member Count (#4819)
* [Cleanup] Fix SendChannelMembers size in UCS ChatChannelList

* Part 2

* Forgot null terminator
2025-03-30 14:46:46 -05:00
Chris Miles 92128b98fd [Instances] Add expire_at Column (#4820)
* [Instances] Add `expire_at` Column

* expire_at update

* Update servertalk.h

* Add rule Instances:ExpireOffsetTimeSeconds
2025-03-30 14:46:02 -05:00
Chris Miles b9cfdea76c [Zone] Zone State Automated Testing and Improvements (#4808)
* [Zone] Zone State Automated Testing and Improvements

* Spawn condition

* Update zone.cpp

* Remove redundant logic

* Update zone_state.cpp

* TestZLocationDrift

* Protect NPC resumed NPC's from being able to die
2025-03-30 01:45:28 -05:00
Chris Miles c8a7066d0e [Performance] Send Smarter Emote Packets (#4818) 2025-03-29 19:50:44 -05:00
JJ 950cc4a325 [Cleanup] Control flow defaults missed in recent bot updates (#4817) 2025-03-29 19:07:31 -05:00
Chris Miles 82b48fe6e8 [Reload] Add Reload for Maps / Navs (#4816) 2025-03-29 18:24:17 -05:00
Chris Miles ca9c1fdd24 [API] Expose Zoneserver Compile Metadata (#4815) 2025-03-29 18:14:02 -05:00
Chris Miles fe08961d25 [Crash] Fix Repop Race Condition Crash (#4814)
* [Crash] Fix Repop Race Condition Crash

* True fix
2025-03-29 17:39:40 -05:00
Chris Miles 5b9f7ff4c9 [Fix] Globally Reloading Quests when not loaded (#4813) 2025-03-29 17:10:40 -05:00
Chris Miles 23743a4050 [Fix] Fix Instance Creation Race Condition (#4803)
* [Fix] Fix Instance Creation Race Condition

* Rejigger

* Update database_instances.cpp

* Update database_instances.cpp

* Update database_instances.cpp

* Update database_instances.cpp
2025-03-29 17:10:20 -05:00
nytmyr 444d688ad2 [Bots] Line of Sight and Mez optimizations and cleanup (#4746)
* [Bots] Line of Sight and Mez optimizations and cleanup

- Renames `Map:CheckForLoSCheat` to `Map:CheckForDoorLoSCheat` to better reflect what it does.
- Renames `Map:RangeCheckForLoSCheat` to `Map:RangeCheckForDoorLoSCheat` to better reflect what it does.
- Adds the rule `Pets:PetsRequireLoS` to determine whether or not commanded pet attacks require an addition layer of LoS checks for edge-cases.
- Adds the rule `Bots:BotsRequireLoS` to determine whether or not bots require LoS to `^attack`, `^pull` and `^precombat`.
- Adds the rule `Map:ZonesToCheckDoorCheat` to control what if any zones will be checked..
- Corrects, removes and adds LoS checks where necessary.
- Improves door checking logic for locked or triggered doors that could be blocking LoS.
- Cleans up false positives for door cheat checks.
- Adds `drawbox` option to `#door` command. This will spawn points at the center and each corner of the door's "box". It will also spawn points at your and your target's location.
- Improves Mez and AE Mez logic
- Adds more details to the rule `Bots:EpicPetSpellName`

* Remove leftover debugging

* Change return to continue for GetFirstIncomingMobToMez checks

* Move mez chance fail to beginning of cast process
2025-03-29 16:01:31 -05:00
nytmyr d554eb3423 [Bots] Fix rule Bots:FinishBuffing (#4788) 2025-03-29 15:17:33 -05:00
nytmyr deb298dda7 [Bots] Enraged positioning (#4789)
- Non-taunting melee bots will now properly go behind their target when it is enraged.
- Reduces how often taunting bots adjust their positioning by removing unnecessary rules.
- Cleans up CombatPositioning a bit
2025-03-29 15:00:08 -05:00
nytmyr 19e785b842 [Bots] Fix Rule ZonesWithSpawnLimits/ZonesWithForcedSpawnLimits errors (#4791)
* [Bots] Fix error copy/paste

Oops

* Eliminate false errors on empty rules and add more sanity checks

- `Bots:ZonesWithSpawnLimits` - This is to be used when zones will only allow up to x amount of bots. Example: A player can normally spawn 5 bots but a zone in this rule has a lower limit of up to 3, this rule would override their normal limit if their normal limit is above the listed zone's max in `Bots:ZoneSpawnLimits`.
- `Bots:ZonesWithForcedSpawnLimits` - Zones in this rule will override any spawn limits high or low and force it. If one player can normally spawn 2 and another player can spawn 10 but a zone listed forces a limit of 5, all players will be able to spawn 5. Follows the limits set in `Bots:ZoneForcedSpawnLimits`
2025-03-29 14:59:09 -05:00
Chris Miles 7b9691d486 [Performance] Reduce LFGuild Chatter (#4794) 2025-03-29 14:58:01 -05:00
Chris Miles 30fddcc5a0 [Performance] Reduce UpdateWho S2S Chatter to World (#4792)
* [Performance] Reduce UpdateWho S2S chatter

* Add rule to change this dynamically
2025-03-29 14:56:32 -05:00
Chris Miles 235e59a2d8 [Database] Fix Respawn Times Table (#4802) 2025-03-29 14:54:26 -05:00
Chris Miles bc1ffe0716 [Performance] Reduce Adventure S2S chatter (#4793) 2025-03-29 14:48:41 -05:00
Chris Miles 44497414db [Instance] Clear Respawn Timers on Creation (#4801)
* [DZ] Clear Respawn Timers on Creation

* Revert "[DZ] Clear Respawn Timers on Creation"

This reverts commit ae18b77e83.

* Clear respawn times on instance creation
2025-03-29 14:47:25 -05:00
Chris Miles 96e34fe8f7 [Performance] Improve Character Select DB Performance (#4799) 2025-03-29 14:46:57 -05:00
Alex King ceca28d2a3 [Cleanup] Remove Extraneous Time Type in ShowZoneData (#4806) 2025-03-29 14:44:26 -05:00
Alex King b040571427 [Commands] Add Instance Support to #zoneshutdown (#4807)
* [Commands] Add Instance Support to #zoneshutdown

* Update zoneserver.cpp

* Update zoneshutdown.cpp
2025-03-29 14:44:08 -05:00
Chris Miles 49664cc1a0 [Performance] Reduce CorpseOwnerOnline S2S Chatter to World (#4795) 2025-03-29 14:30:00 -05:00
nytmyr 5e4fd43920 [Bots] Prevent bot pets from despawning on #repop (#4790) 2025-03-29 14:29:29 -05:00
Chris Miles 937b947597 [Performance] Have World Send Smarter Guild Updates (#4796)
* [Performance] Have World Send Smarter Guild Updates

* Updates to correct incorrect guild window details (permissions, etc) not being sent on guild creation.

---------

Co-authored-by: Mitch Freeman <65987027+neckkola@users.noreply.github.com>
2025-03-29 14:27:49 -05:00
Chris Miles bb70850421 [Fix] Zone State Variables Load First (#4798) 2025-03-29 14:26:12 -05:00
Chris Miles 46511365a7 [Crash] Fix Rarer World Crash with Player Event Thread Processor (#4800)
* [Crash] Fix Rarer World Crash with Player Event thread processor

* Update main.cpp
2025-03-29 14:26:00 -05:00
Chris Miles 6d69ac7a98 [Performance] Add several database indexes (#4811)
* [Performance] Add several database indexes

* Update database_update_manifest.cpp

* Update database_update_manifest.cpp

* Push
2025-03-29 14:23:28 -05:00
Alex King 938937c271 [Cleanup] Remove Unused Command Methods (#4805)
* [Cleanup] Remove Unused Command Methods

* Update command.h
2025-03-29 14:23:10 -05:00
Chris Miles cd808416c8 [Command] Add #show zone_variables (#4812) 2025-03-29 14:22:14 -05:00
Chris Miles a05d0752f6 [Fix] Zone state edge case with 0 hp (#4787) 2025-03-29 14:20:23 -05:00
Mitch Freeman 799609fb21 [Fix] AllowFVNoDrop Flag trades (#4809) 2025-03-26 21:38:13 -04:00
catapultam-habeo 213fe6a9e9 [Feature] Implement /changename & related script bindings. Clean up #set name (#4770)
* initial work, need to clean up gm commands still

* cleaned up command, works without kicking char select now

* remove thj-specific methods

* add script hooks

* actually clear flag

* rework questmgr::rename

* remove unnecessary logging

* revert

* added missing binding to perl api and updated some text

* don't return a value

* Fix some bad argument types.

* adjust case

* alpha order

* refactor some old string stuff

* don't quote integers, bob

---------

Co-authored-by: Zimp <zimp@zenryo.xyz>
Co-authored-by: Chris Miles <akkadius1@gmail.com>
2025-03-19 21:00:45 -05:00
Alex King be6a5d5f50 [Quest API] Add Support for NPC ID and NPC Name Specificity (#4781)
* [Quest API] Add Support for NPC ID and NPC Name Specificity

* Update quest_parser_collection.cpp

* Update quest_parser_collection.cpp

* Update quest_parser_collection.cpp

* Update quest_parser_collection.cpp

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2025-03-19 17:55:00 -05:00
nytmyr 1af29bd7b1 [Bots] Fix IsValidSpellTypeBySpellID to account for all types (#4764)
* [Bots] Fix IsValidSpellTypeBySpellID to account for all types

* Formatting
2025-03-19 17:43:15 -05:00
zimp-wow ef945e6e99 [Fix] Fix zone crash when attempting to add a disappearing client to hate list. (#4782) 2025-03-19 16:26:54 -05:00
Chris Miles 9528c1e7fc [Fix] Zone State Entity Variable Load Pre-Spawn (#4785)
* [Fix] Zone state ent variable pre-spawn

* Update zone_save_state.cpp

* Update zone_save_state.cpp

* Update spawn2.cpp

* Update zone_save_state.cpp

* Update zone_save_state.cpp
2025-03-19 16:21:36 -05:00
Chris Miles fd6e5f465d [Fix] Zone State Position Fix (#4784) 2025-03-19 16:17:25 -05:00
nytmyr d00125abe1 [Bots] Charmed Pets were breaking Mob respawns (#4780)
- When bots would charm a pet, once they zoned or camped the pet would poof and not trigger a respawn so the NPC which was charmed would never respawn until the zone was shut down or server restarted.
2025-03-16 19:12:48 -04:00
Akkadius 5d69235a4c [Release] 23.3.4 2025-03-14 13:09:53 -05:00
Mitch Freeman e93785f885 [Fix] Add check for simultaneous direct vendor and parcel Trader/Buyer Purchase (#4778) 2025-03-13 22:30:00 -05:00
Chris Miles 3c2545cfaf [Release] 23.3.3 (#4777) 2025-03-13 17:06:52 -05:00
Chris Miles 8d1a9efac9 [Zone] Zone State Improvements Part 3 (#4773)
* [Zone State] Additional improvements

* Return early

* Update zone_save_state.cpp

* Push

* Push

* Update zone.cpp

* Update zone_save_state.cpp

* Equip items that were dynamically added on restore

* IsZoneStateValid helper

* ZoneStateSpawnsRepository::PurgeInvalidZoneStates

* Add Zone:StateSaveClearDays and PurgeOldZoneStates

* spawn2 / unique_spawn block when restored from zone state

* One time purge

* Update zone_state_spawns_repository.h

* Update npc.cpp

* Update npc.cpp

* test

* ORDER BY spawn2_id

* Stuff

* Restored corpses shouldn't trigger events

* Fix weird edge case
2025-03-13 17:00:30 -05:00
Mitch Freeman f6b18fb003 [Fix] Update GuildBank to correctly handle items with charges equal to zero (#4774) 2025-03-12 21:57:29 -04:00
dependabot[bot] 00e77f190c Bump golang.org/x/net in /utils/scripts/build/should-release (#4775)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.33.0 to 0.36.0.
- [Commits](https://github.com/golang/net/compare/v0.33.0...v0.36.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-12 20:57:06 -05:00
Chris Miles 9cb72a6ba7 [Networking] Fix "port in use" error (#4772) 2025-03-12 00:52:17 -05:00
Chris Miles 8203c034bf [Database] Add indexes for data_buckets and zone_state_spawns (#4771)
* [Database] Add indexes for data_buckets and zone_state_spawns

* Update database_update_manifest.cpp

* Update database_update_manifest.cpp

* Update package.json
2025-03-11 03:29:26 -05:00
Akkadius 33ae51f56f [Release] 23.3.1 2025-03-11 01:38:59 -05:00
Chris Miles 30c39194a3 [Zone] Zone State Improvements (Continued) (#4768)
* [Zone] Zone State Improvements (Continued)

* Ignore a few events when we resume from suspend

* Add is_zone field

* Update database_update_manifest.cpp

* Update database_update_manifest.cpp

* Update database_update_manifest.cpp

* Update zone_save_state.cpp

* Update zone_save_state.cpp

* Add Zone Variables

* Update methods

* Update zone_save_state.cpp

* Update zone_save_state.cpp

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2025-03-11 01:14:09 -05:00
hg 051ce3736f [DynamicZones] Bulk request dz member statuses on zone boot (#4769)
When dynamic zones are cached on zone boot each dz requests member
statuses from world separately. This causes a lot of network traffic
between world and booted zones when there are a lot of active dzs.

This changes it to make a single request to world on zone boot and a
single bulk reply back.
2025-03-11 01:13:29 -05:00
Mitch Freeman 84708edccf Update client_evolving_items.cpp (#4767) 2025-03-09 12:20:37 -04:00
Chris Miles da4e9ab95b [Release] 23.3.0 (#4766) 2025-03-08 03:18:40 -06:00
Chris Miles a8fea95eab [Zone] State Save Improvements (#4765)
* [Zone] State saving improvements

* Make sure we load spawn enabled off of the state row

* Update npc.h

* Update spawn2.cpp

* Update database_instances.cpp

* Update database_instances.cpp
2025-03-08 03:15:42 -06:00
Mitch Freeman 53610c2f0f [Feature] Add Rule for dealing with augments when an item evolves (#4758) 2025-03-07 23:00:11 -06:00
catapultam-habeo 9f10c12874 [Bug Fix] Correct incorrectly calculated stat caps with Heroic Stats (#4760)
* fix incorrectly caclulcated stat caps

* fix typos and formatting
2025-03-07 22:59:47 -06:00
catapultam-habeo a0634adb3c [Feature] Allow assigning Helm Texture independently of Body Texture for Horses (#4759) 2025-03-07 22:59:12 -06:00
zimp-wow a2ed6be1f5 [Bug Fix] Zero out currentnpcid whenever spawn is reset. (#4763) 2025-03-07 22:38:23 -06:00
nytmyr c33ac40567 [Cleanup] Fix typo in GM tradeskill combine message (#4762) 2025-03-07 22:33:43 -06:00
Mitch Freeman 9ee095b354 [Fix] Add crash checks for certain PlayerEventLogs (#4761) 2025-03-07 16:17:36 -06:00
Alex King 7ab32af4dc [Rules] Fix EvolvingItems:PercentOfRaidExperience Description (#4757)
- Fixes an issue where the description was inaccurate, described [here](https://discord.com/channels/212663220849213441/557677602706423982/1347293634453835899).
2025-03-06 23:49:36 -05:00
Chris Miles 0c301419c2 [Zone] Make zone controller less likely to be visible, immune to all forms of combat (#4750)
* [Zone] Make zone controller less likely to be visible, immune to all forms of combat

* Exclude zone controller from scanning
2025-03-06 17:08:08 -05:00
nytmyr d6a21be25e [Bots] Fix taunting bots positioning (#4754)
* [Bots] Fix taunting bots positioning

- Fixes taunting bots liking to hug their target on certain models or chosen positions.
- Makes bots have a more realistic combat range in comparison to players.
- Removed unnecessary rules and checks for melee distance.

* Update ruletypes.h
2025-03-06 17:07:38 -05:00
nytmyr 1d4ba082ad [Bots] Move commanded spell map to zone (#4755)
- Moves the mapping of commanded spell min levels to zone rather than to each individual bot.
- Adds support for sub spell types.
2025-03-06 17:05:36 -05:00
nytmyr 94553501ba [Bots] Fix buffs not overwriting lesser buffs (#4756) 2025-03-06 17:03:50 -05:00
nytmyr da824d5178 [Crash] Bot aura crash fix (#4752)
- Something between the latest release caused this crash to appear, unsure of this exact cause.
- Prevents bots from being sent a spawn packet for Auras.
- Removes the bot's auras on Depop
2025-03-06 01:12:24 -05:00
Mitch Freeman 5a1df38900 [Fix] Parcel Delivery Updates for two edge cases (#4753)
- Properly send an item via parcel that has 0 charges
2025-03-06 01:12:02 -05:00
nytmyr 8cd7148b29 [Pets] Fix renamed pets loading as blank names (#4751) 2025-03-05 14:31:16 -05:00
Akkadius 09e079a45e [Hotfix] Forgot to push up some changes for test output 2025-03-04 17:18:02 -06:00
Chris Miles 4bc881da4b [Tests] Cleanup Hand-in Tests (#4749) 2025-03-04 17:15:27 -06:00
Chris Miles 0615864d51 [Databuckets] Nested Databuckets Protections and Improvements (#4748)
* Check for valid JSON before using it

* Do not allow nested keys to set be set an expiration

* Prevent overwriting of existing object or array

* Nested deletion support

* Update data_bucket.cpp

* Test cases

* More test cases, fix

* Update databuckets.cpp

* Update databuckets.cpp

* Basic databucket tests

* Update databuckets.cpp

* Update databuckets.cpp
2025-03-04 13:16:21 -06:00
Alex King 3638d157b2 [Logging] Convert JSON Error to Data Buckets Logging Category (#4747) 2025-03-03 23:16:47 -06:00
Akkadius d41725e325 [Hotfix] Fix sigabort crash from invalid JSON 2025-03-03 01:47:34 -06:00
Akkadius 88580b69b6 [Hotfix] Remove one port check in world 2025-03-03 00:49:02 -06:00
Akkadius f7a6fe595a [Release] 23.2.0 2025-03-03 00:17:54 -06:00
Chris Miles 07d14c2681 [Crash] Fix crash in add loot code path (#4745) 2025-03-03 00:16:51 -06:00
Chris Miles eac7a73fb6 [Crash] Potential crash fix in scan close mobs (#4744) 2025-03-03 00:16:42 -06:00
Chris Miles c5715f1f14 [Crash] Fix Aura process crash with bots (#4743) 2025-03-03 00:13:00 -06:00
Chris Miles 3902230fa1 [Crash] Fix world repop crash (#4742) 2025-03-02 22:04:39 -08:00
Chris Miles 212969f5cd [Crash] Database SetMutex crash fix (#4741) 2025-03-02 22:04:24 -08:00
Chris Miles de4226fdc9 [World] Check if port in use to avoid double booting mistakes (#4740)
* Stuff

* Potentially fix aura crash

* Reload crash fix

* Revert "Reload crash fix"

This reverts commit 96e1e76306.

* Fix

* Update entity.cpp

* Update dbcore.cpp

* [World] Check if port in use to avoid double booting mistakes

* Revert "Stuff"

This reverts commit 2162c00edd.

* Revert "Potentially fix aura crash"

This reverts commit 7c242723f4.

* Revert "Fix"

This reverts commit 8419e284d4.

* Revert "Update entity.cpp"

This reverts commit 8a1f4545a4.

* Revert "Update dbcore.cpp"

This reverts commit f0278d9591.
2025-03-02 22:04:03 -08:00
zimp-wow 8b13434197 [Bug Fix] Cleanup zone buckets on instance purge. (#4739) 2025-03-02 17:01:25 -06:00
catapultam-habeo 27274397ec [Bug Fix] Fix an error causing Endurance Regen to not be applied by items. (#4738)
* fix typo causing endurance regen to not be applied by items

* further correction
2025-03-02 03:39:22 -05:00
Chris Miles 2b79a36014 [Release] 23.1.0 (#4736)
* [Release] 23.1.0

* Version

* Pet table last minute add in
2025-03-01 19:23:50 -06:00
Akkadius 8cf52294e9 [Hotfix] Add character_pet_name to player tables schema 2025-03-01 18:56:38 -06:00
Chris Miles 0ef79903f8 [Crash] Validate item in SE_SummonItemIntoBag (#4734) 2025-03-01 18:32:43 -06:00
Chris Miles ab14458f9e [Crash] Fix large file size crash in File::GetContents for windows (#4735) 2025-03-01 18:32:35 -06:00
Chris Miles 5b94e736b3 [Crash] Fix reload concurrency crash when ran from Spire (#4733) 2025-03-01 18:05:28 -06:00
Chris Miles c3b8cc9744 [Crash] Check for directory existence before traversing in CheckForCompatibleQuestPlugins (#4730) 2025-03-01 20:01:37 -04:00
Mitch Freeman 89e3b2c72e [Fix] Add client packets to questmanager:setguild (#4732) 2025-03-01 17:55:44 -06:00
Chris Miles 23c4aa241b [Crash] World CLI validation (#4728)
* [Crash] World CLI validation

* Clean
2025-03-01 19:52:51 -04:00
Chris Miles acb7584e26 [Loginserver] Minor cleanup (#4729) 2025-03-01 19:52:21 -04:00
Chris Miles a885bd9322 [Crash] Fix filesystem crash / exception in DatabaseDumpService::RemoveCredentialsFile() (#4731) 2025-03-01 19:51:41 -04:00
Chris Miles 20fe1926e0 [Database] Remove force_interactive from big bag updates (#4727) 2025-03-01 16:55:57 -06:00
Chris Miles eb7118754b [Zone State] Wrap all serialization/deserialization in try/catch (#4726) 2025-03-01 16:46:08 -06:00
Mitch Freeman 3611b49f68 [Feature] Evolving items Additions (#4725)
* Implement multi-value for evolving sub_types

- Added ability for evolving sub_types to contain multiple values
- Implemented EvolvingItems::Types::NUMBER_OF_KILLS with level for sub_type

* Repair a timer issue preventing proper evolution of items

* Simplify

* Remove extra level of nesting

* Update client_evolving_items.cpp

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2025-03-01 16:38:59 -06:00
catapultam-habeo 511d8a8bb3 [Bug Fix] Refactor ApplyItemBonuses to fix double-counting of ATK and recommended levels not correctly applying (#4713)
* refactor AddItemBonuses

* typo

* simplify some syntax

* fix indents

* Revert "fix indents"

This reverts commit 8e660707a9.

* fix indents without blowing up entire file

* Revert "fix indents without blowing up entire file"

This reverts commit 6b21d1bcc9.

* ok for real though

* Undo spaces in formatting, source uses tabs

* Spaces to tabs take 2

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2025-02-28 21:04:42 -06:00
catapultam-habeo 1598d2e17b [Feature] Add a rule for spells to bypass stacking rules (#4716)
* define rule that allows for spells which always stack

* Update spells.cpp

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2025-02-28 16:49:20 -06:00
nytmyr 805757ba87 [Bots] Fix unresponsive bots in groups upon group wipe (#4712)
- Bots were failing to recall their group after dying due to the way it was grabbing the group from entity list.
2025-02-28 16:10:46 -06:00
Chris Miles eb6ac25540 [Code Cleanup] More login <-> world code cleanup (#4724)
* More cleanup

* More cleanup
2025-02-28 16:08:06 -06:00
MortimerGreenwald cb634cf57d [Bug Fix] Fix Trading Items to Bot Pets (#4721)
Small change using IsPetOwnerOfClientBot() instead of just IsPetOwnerClient

Can bot pet take item?

not before, but hopefully now?
2025-02-28 15:54:38 -06:00
Chris Miles 2f7ca2cdc8 [Zone] Implement Zone State Saving on Shutdown (#4715)
* Save spawns

* Update base_zone_state_spawns_repository.h

* Zone state save work

* Code cleanup

* More cleanup

* Database migration

* Update database_update_manifest.cpp

* Revert decay at storage model

* Code cleanup

* More cleanup

* More cleanup

* More cleanup

* Entity variables

* Add entity variables to the schema

* Post rebase

* Checkpoint

* Serialize / deserialize buffs

* Current hp / mana / end save / load

* Save / load current_waypoint

* Add zone spawn protection

* Finishing touches

* Cleanup

* Update zone_save_state.cpp

* Cleanup

* Update zone_save_state.cpp

* Update npc.cpp

* Update npc.cpp

* More

* Update perl_npc.cpp

* Update zone_loot.cpp
2025-02-28 15:31:06 -06:00
Chris Miles 425d24c1f4 [Quest API] Implement eq.handin() and quest::handin() (#4718)
* [Quest API] Implement eq.handin() and quest::handin()

* Fix MQ using new API style
2025-02-28 15:22:39 -06:00
Alex King 875df8e64a [Quest API] Add Key Ring Methods to Perl and Lua (#4719) 2025-02-28 15:08:57 -06:00
Alex King 7a2d2a0c51 [Bug Fix] Fix AA Reset Error Message (#4720)
* [Bug Fix] Fix AA Reset Error Message

* Update client.cpp

* Update client.cpp
2025-02-28 15:06:49 -06:00
Alex King 5296202e56 [Bug Fix] Fix Issue with Suffixes/Prefixes (#4723) 2025-02-28 15:05:57 -06:00
Akkadius e2db8ffea8 [Hotfix] Clear m_completed_shared_tasks before reloading 2025-02-24 17:24:18 -06:00
Chris Miles fa2ab11676 [Tasks] Extend IsTaskCompleted to also be aware of shared task completion (#4714)
* [Tasks] Extend IsTaskCompleted to also be aware of shared task completion

* Fix my stupidity

* Update client.h
2025-02-24 16:31:35 -06:00
Chris Miles 80e8634a48 [Release] 23.0.2 (#4711) 2025-02-21 23:18:29 -06:00
zimp-wow 98c2fa5127 [Bug Fix] Fix bad Mob reference in QuestManager::resumetimer() (#4710) 2025-02-21 23:14:50 -06:00
nytmyr 8b8b41dab3 [Bots] Add checks to ensure bots and pets do not engage on ^pull (#4708) 2025-02-21 23:13:54 -06:00
nytmyr 63b1e6b4b4 [Bots] Improve positioning (#4709)
- Prevent bots not set to ^behindmob from finding positions behind their target.
- Backs up a bot if they're too close and have target reflection, not just taunting bots.
- Backs up bots if the target is rooting and they are not taunting to the appropriate position.
- This will particularly help with casters running around.
2025-02-21 23:12:50 -06:00
nytmyr 21b7b6e7ab [Bots] Prevent medding in combat if any mob has bot targeted (#4707)
- Previously this only checked the bots target to med. It will now check the hatelist of the bot to ensure no mobs have the bot targeted before medding.
2025-02-21 23:12:29 -06:00
Alex King 878a5377ae [Bug Fix] Fix Lua Zone ID Exports (#4700) 2025-02-21 23:12:04 -06:00
Alex King 486c7c44be [Quest API] Add GetSpawn() to Perl and Lua (#4702)
* [Quest API] Add GetSpawn() to Perl and Lua

* Push

* Update npc.h
2025-02-21 22:57:41 -06:00
nytmyr aa4869c6e9 [Hotfix] Fix cursor load on zone (#4704)
* [Hotfix] Fix cursor load on zone

- Corrects an issue where the item on your cursor would become invisible upon trying to place it anywhere in your inventory.

* Remove general to prevent duplicate loads
2025-02-21 22:54:35 -06:00
zimp-wow 94e1b4edfa [Bug Fix] Fix infinite loop in QuestManager::stoptimer() (#4703) 2025-02-20 19:52:08 -05:00
Akkadius 4d73b7d641 [Release] 23.0.1 2025-02-20 02:48:01 -06:00
Chris Miles ed4f9b0d30 [Fix] Player event ordering merge fix (#4699) 2025-02-20 02:46:24 -06:00
Alex King f8837d0926 [Quest API] Add DisableRespawnTimers to Perl and Lua (#4691)
* [Quest API] Add DisableRespawnTimers to Perl and Lua

* Update lua_zone.h

* Update lua_zone.cpp

* Change up how we're doing this

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2025-02-20 00:26:46 -06:00
Chris Miles 753d83c499 [Release] 23.0.0 (#4698)
* [Release] 23.0.0

* Update CHANGELOG.md

* Fix migration
2025-02-20 00:17:40 -06:00
Alex f0108826d3 [Client Mod] Adds a hacked fast camp rule for GMs (#4697)
Co-authored-by: KimLS <KimLS@peqtgc.com>
2025-02-19 23:20:22 -06:00
Chris Miles 6ac846c003 [Fix] Fix issue with getting an unset nested databucket (#4693) 2025-02-19 13:28:02 -06:00
Chris Miles 44963f3f21 [Fix] #rq and #reload quest alias (#4694) 2025-02-18 13:28:07 -06:00
Chris Miles 9fd935ef10 [Fix] Prevent zone from loading ETL ID's on bootup (#4696) 2025-02-18 13:27:56 -06:00
Chris Miles 6f390c81f9 [Fix] Fix non-error in player_event_logs (#4695) 2025-02-18 13:27:43 -06:00
Chris Miles 1bd281c8f2 [Performance] Server Reload Overhaul (#4689)
* [Performance] Server Reload Overhaul

* Client::SendReloadCommandMessages

* Remove global buffs
2025-02-18 00:54:37 -06:00
Chris Miles 49cf97ae9c [Databuckets] Add Zone Scoped Databuckets (#4690)
* [Databuckets] Add Zone Scoped Databuckets

* Add database indexes

* Update database_update_manifest.cpp

* Shutdown fix

* Testing

* Perf boost

* Revert "Perf boost"

This reverts commit 55d3e507d3.

* Update data_bucket.cpp
2025-02-18 00:14:49 -06:00
MortimerGreenwald 8315240b17 [Bug Fix] Find Zone - Expansion Settings (#4692)
* Bug Fix Find Zone - Expansion Settings

#fz expansion 30 Would crash your zone since the expansions don't go up that high. This Adds a check for range out of bounds and provides an exception to expansion 99 since those are test/dev zones.

* Update zone.cpp

* Update zone.cpp

* Update zone.cpp

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2025-02-17 23:37:35 -05:00
hg 55155ff800 [Expeditions] Move expedition code into DynamicZone (#4672)
This removes the separate Expedition class and moves lockout code and
/dz command handlers into DynamicZone classes. It also refactors some
code to reduce bloat and some database usage.

This completes the effort of moving everything to DynamicZone that
started when implementing shared tasks. It also makes sense to do this
since expeditions are just dynamic zones internally despite dzs being
used for other types. Expedition specific things are just handled with
dz type checks.

Functionally nothing should change. This is mainly internal refactoring
and moving code around along with some bug fixes and reduced database
usage.

Main changes:

 - The `expeditions` database table has been removed

 - Expeditions no longer use a separate id, the expedition id is just the dz id

 - Expedition lock state and replay timer option were moved to the
   `dynamic_zones` table

 - Expeditions no longer have a separate cache from dynamic zones

 - Expedition creation no longer has every zone query the database to cache it

 - Expedition internal lockouts are now stored on DynamicZone

 - The `expedition_lockouts` table has been renamed to `dynamic_zone_lockouts`

 - Fixed a small bug with the UpdateLockoutDuration api where the
   internal lockout would get the time added twice in memory in the
   initiating zone (this api is likely rarely used)

 - Fixed an issue where use of the group/raid DoesAnyMemberHaveExpeditionLockout
   api would query once for every out of zone character.

   - This api now checks all members in the current zone first and only
     performs a single bulk query for out of zone members if that check
     is exhausted

 - Deprecated the max_check_count param of DoesAnyMemberHaveExpeditionLockout,
   the quest api still exists to avoid api break but a passed arg has no effect
2025-02-15 18:40:35 -06:00
Mitch Freeman ab4e1191ef [Fix] Parcel Delivery Updates (#4688)
* Fix two parcel bugs

Fix two Parcel Bugs

- If a player was at their parcel limit and perform a bazaar purchase via parcel delivery, their money would be lost
- If a container with items was delivered via parcel, the parcel under certain inventory conditions could be delivered into an incorrect slot resulting in the container being lost.

* Incorrect field used for BagSize vs ItemSize.  Silly mistake.

* Remove duplicate check and reorder stacking check

* Fix edge case when Parcel Window remains open and Bazaar purchases are made.

* Repair
- bazaar purchase of items with charges reverting to 1 charge in error
- bazaar visual error with selling price. Was caused by the parcel fee not being properly reflected in the client
- corrected a type mismatch with parcel fee uint32 vs uin64
- corrected a few TraderPurchase and TraderSell event data points by splitting quantity and charges

* Formatting

* Use pre-existing AddMoney and TakeMoney and remove unnecessary routines

* Updates after rebase
2025-02-15 18:27:09 -06:00
Mitch Freeman c09fad5a75 [Feature] GuildBank Updates (#4674)
* First pass of a re-write of Guild Bank to enable RoF2 features

* Testing - Corrected a few bugs with merging, splitting and withdrawing

* Testing - Corrected a few bugs with depositing of bags

* Added player event logging for deposit, withdrawal and movement between deposit to main area.

* Fix the guilddelete routine

Fix the guilddelete routine as a result of the new guild_bank table structure

* Fix an issue with items not being withdrawn correctly.

* Final Testing Phase 1 - A few failures to be resolved yet.

* Final Testing Phase 2 - Looks good

* Final Testing Phase 3 - Repair a visual bug with withdrawal of items with charges

* Cleanup

* Formatting feedback updates

* Rebase and fix version.h

* Fix manifest issue after changes
2025-02-15 17:48:50 -06:00
Chris Miles 8201175c2c [Fix] Item Handins to Pets (#4687)
* [Fix] Item handins to pets

* Update npc.cpp
2025-02-15 17:07:40 -06:00
Chris Miles e88ea24966 [Fix] GMMove Update Edge Case With Clients (#4686)
* [Fix] GMMove with clients

* Exclude clients entirely for position update caching
2025-02-15 16:20:37 -06:00
nytmyr 02e2f6771c [Bots] Fix AE range calculation (#4683)
- Certain AE types weren't properly calculating their AE range and would base off the actual AE range for targeted types rather than the spells range itself
2025-02-15 15:41:08 -06:00
nytmyr 3b399dfac5 [Bots] Add missing stance options (#4681)
- Moved stance check to function
- Added check for Efficient and Assist stances.
- Sets aggressive to ignore aggrochecks by default
- Adds more types to default aggrocheck list
- Sets Assist to hold CCs
2025-02-15 15:03:55 -06:00
nytmyr 9aa0f7c695 [Spells] Add all types to checks for max_targets_allowed rule for AEs (#4682)
- Added all TargetedAE types to IsTargetableAESpell
- Added IsTargetableAESpell to IsAnyAESpell and changed order for earlier escapes
- IsPBAESpell now checks all PBAE types
- The rule Spells, TargetedAOEMaxTargets will now check all Targeted AE types
- The rule Spells, PointBlankAOEMaxTargets will now check all remaining types that can be a PBAE and not only Nuke PBAEs
2025-02-15 15:03:23 -06:00
nytmyr 3ba113a91d [Bots] Fix pets causing aggro (#4677)
- Adds the rule `Aggro, AggroBotPets`
2025-02-15 15:02:38 -06:00
nytmyr b0c951bd6e [Bots] Prevents casting on ineligible targets due to target type, stacking, etc. (#4680)
- Prevents casting on ineligible targets due to target type, stacking, etc.
- Adjust the rules Bots, MezChance, Bots, MezSuccessDelay and Bots, MezFailDelay
2025-02-15 15:01:39 -06:00
nytmyr 8a5f885558 [Bots] Add AEHateLine to HateLine ParentType (#4678) 2025-02-15 15:00:54 -06:00
nytmyr fd3f5cfd29 [Bots] Command Cleanup (#4676)
- Fix ^discipline saying no bots were selected
- Adds more information to certain commands to explain how they function.
- Rewrote ^copysettings and ^defaultsettings to not be so bloated and accept arguments properly.
- Added long names for setting categories
- Add Spell ID output to ^spells
2025-02-15 15:00:14 -06:00
nytmyr 74b8cf8bd3 [Hotfix] Update pre big bag corpse slot_id's to support big bags (#4679) 2025-02-15 14:58:39 -06:00
nytmyr 18685748f6 [Bots] Crash fixes related to GetNumberNeedingHealedInGroup (#4684)
- Add pre-death checks and early returns
- Cleanup pointers for GetUltimateSpellType checks
- Rename IsBotSpellTypeOtherBeneficial to BotSpellTypeUsesTargetSettings
- Rename GetUltimateSpellTypeDelayCheck to GetUltimateSpellTypeRecastCheck
-Add entity validation to GetNumberNeedingHealedInGroup
2025-02-15 14:55:52 -06:00
nytmyr da24bf467a [Commands] Fix #goto not accepting proper heading (#4685) 2025-02-15 11:12:23 -05:00
Alex King e948a6815c [Bug Fix] Fix Illusion Fade Texture Bug (#4673)
* [Bug Fix] Fix Illusion Fade Texture Bug

* Update spell_effects.cpp
2025-02-13 19:57:31 -06:00
455 changed files with 32390 additions and 24457 deletions
+795
View File
@@ -1,3 +1,798 @@
## [23.10.2] 9/16/2025
### Hotfix
* Revert #4996 as it was causing critical issues with spells that needs to be further investigated. @Akkadius 2025-09-17
## [23.10.1] 9/16/2025
### Hotfix
* Fixed Mail Key Bug ([#5015](https://github.com/EQEmu/Server/pull/5015)) @Kinglykrab 2025-09-16
## [23.10.0] 9/15/2025
### Build
* Fix Linking with GCC ([#4969](https://github.com/EQEmu/Server/pull/4969)) @solar984 2025-08-03
### Code
* Add #npcedit npc_tint_id Help Message ([#4982](https://github.com/EQEmu/Server/pull/4982)) @Kinglykrab 2025-08-17
* Cleanup #show ip_lookup Message ([#5005](https://github.com/EQEmu/Server/pull/5005)) @Kinglykrab 2025-08-30
* Fix #set race 0 Message ([#5004](https://github.com/EQEmu/Server/pull/5004)) @Kinglykrab 2025-08-30
* Fix Issues with Strings::Commify and Mob::SendStatsWindow ([#4984](https://github.com/EQEmu/Server/pull/4984)) @Kinglykrab 2025-08-17
* Remove Attributions ([#4988](https://github.com/EQEmu/Server/pull/4988)) @KimLS 2025-08-16
* Remove Unused errorname Variable ([#5001](https://github.com/EQEmu/Server/pull/5001)) @Kinglykrab 2025-08-29
### Commands
* Add #find account Subcommand ([#4981](https://github.com/EQEmu/Server/pull/4981)) @Kinglykrab 2025-08-17
* Add #show keyring Subcommand ([#4973](https://github.com/EQEmu/Server/pull/4973)) @Kinglykrab 2025-08-03
* Add #task complete Saylink to #task show ([#4985](https://github.com/EQEmu/Server/pull/4985)) @Kinglykrab 2025-08-17
### Constants
* Change Race Changes to Race Namespace ([#5000](https://github.com/EQEmu/Server/pull/5000)) @Kinglykrab 2025-08-30
* Convert SE Defines to SpellEffect Namespace ([#4999](https://github.com/EQEmu/Server/pull/4999)) @Kinglykrab 2025-08-30
### Database
* Add `heal_amount` to `character_stats_record` ([#4986](https://github.com/EQEmu/Server/pull/4986)) @Kinglykrab 2025-08-17
### Fixes
* Fix #show recipe uint16 Cap ([#4978](https://github.com/EQEmu/Server/pull/4978)) @Kinglykrab 2025-08-11
* Fix Race 474 for Titanium ([#4979](https://github.com/EQEmu/Server/pull/4979)) @regneq 2025-08-11
* Fix Recipe Inspect Bug ([#4994](https://github.com/EQEmu/Server/pull/4994)) @Kinglykrab 2025-08-30
* Fix Several Evolving Item Bugs ([#4992](https://github.com/EQEmu/Server/pull/4992)) @neckkola 2025-09-08
* Fix Task Reloading ([#5002](https://github.com/EQEmu/Server/pull/5002)) @Kinglykrab 2025-08-29
### Loginserver
* Fix Legacy World When Using Local DB ([#4970](https://github.com/EQEmu/Server/pull/4970)) @solar984 2025-08-03
### Pets
* Add Pet Constants and Methods ([#4987](https://github.com/EQEmu/Server/pull/4987)) @Kinglykrab 2025-08-17
### Quest API
* Add EVENT_CHARM_START and EVENT_CHARM_END ([#5013](https://github.com/EQEmu/Server/pull/5013)) @Kinglykrab 2025-09-15
* Add GetKeyRing() to Perl/Lua ([#4980](https://github.com/EQEmu/Server/pull/4980)) @Kinglykrab 2025-08-17
* Add GetNPCTintIndex() to Perl/Lua ([#4983](https://github.com/EQEmu/Server/pull/4983)) @Kinglykrab 2025-08-17
* Add GetTimers() and GetPausedTimers() to Perl/Lua ([#4965](https://github.com/EQEmu/Server/pull/4965)) @Kinglykrab 2025-08-03
* Add Identifiers to Get/Modify NPC Stat Methods ([#5012](https://github.com/EQEmu/Server/pull/5012)) @Kinglykrab 2025-09-15
### Repositories
* Convert Character Inspect Messages to Repositories ([#4997](https://github.com/EQEmu/Server/pull/4997)) @Kinglykrab 2025-08-30
* Convert Damage Shield Types to Repositories ([#4995](https://github.com/EQEmu/Server/pull/4995)) @Kinglykrab 2025-08-30
* Convert Item Loading to Repositories ([#4998](https://github.com/EQEmu/Server/pull/4998)) @Kinglykrab 2025-08-30
* Convert Mail Key to Repositories ([#5007](https://github.com/EQEmu/Server/pull/5007)) @Kinglykrab 2025-09-15
* Convert Shared Bank Platinum to Repositories ([#5006](https://github.com/EQEmu/Server/pull/5006)) @Kinglykrab 2025-09-02
* Convert Spell Loading to Repositories ([#4996](https://github.com/EQEmu/Server/pull/4996)) @Kinglykrab 2025-08-30
* Convert Total Time Played to Repositories ([#5008](https://github.com/EQEmu/Server/pull/5008)) @Kinglykrab 2025-09-15
## [23.9.1] 8/2/2025
### Hotfix
* Fix Quest Ownership Edge Case ([#4977](https://github.com/EQEmu/Server/pull/4977)) @Kinglykrab 2025-08-02
## [23.9.0] 8/2/2025
### Bots
* Fix FinishBuffing rule ([#4961](https://github.com/EQEmu/Server/pull/4961)) @nytmyr 2025-07-01
* Fix ^cast resurrects ([#4958](https://github.com/EQEmu/Server/pull/4958)) @nytmyr 2025-06-29
### Build
* Fix Linking with GCC ([#4969](https://github.com/EQEmu/Server/pull/4969)) @solar984 2025-08-03
* More Build Speed Improvements ([#4959](https://github.com/EQEmu/Server/pull/4959)) @Akkadius 2025-06-30
### Commands
* Add #show keyring Subcommand ([#4973](https://github.com/EQEmu/Server/pull/4973)) @Kinglykrab 2025-08-03
### Database
* Add Indexes to NPC's Spawns Loot ([#4972](https://github.com/EQEmu/Server/pull/4972)) @Akkadius 2025-07-30
### Feature
* Zone Scripting ([#4908](https://github.com/EQEmu/Server/pull/4908)) @Kinglykrab 2025-07-10
### Fixes
* Add a missing Froglok starting area for Titanium Startzone. ([#4962](https://github.com/EQEmu/Server/pull/4962)) @regneq 2025-07-04
* Fix Hero's Forge Ingame and Character Select ([#4966](https://github.com/EQEmu/Server/pull/4966)) @Kinglykrab 2025-07-30
* Show player count on the server list status. ([#4971](https://github.com/EQEmu/Server/pull/4971)) @regneq 2025-07-30
### Loginserver
* Fix Legacy World When Using Local DB ([#4970](https://github.com/EQEmu/Server/pull/4970)) @solar984 2025-08-03
### Performance
* Clear Wearchange Deduplication Cache ([#4960](https://github.com/EQEmu/Server/pull/4960)) @Akkadius 2025-06-30
### Quest API
* Add GetMemberRole() to Perl/Lua ([#4963](https://github.com/EQEmu/Server/pull/4963)) @Barathos 2025-07-10
* Add GetTimers() and GetPausedTimers() to Perl/Lua ([#4965](https://github.com/EQEmu/Server/pull/4965)) @Kinglykrab 2025-08-03
## [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
* Database SetMutex crash fix ([#4741](https://github.com/EQEmu/Server/pull/4741)) @Akkadius 2025-03-03
* Fix Aura process crash with bots ([#4743](https://github.com/EQEmu/Server/pull/4743)) @Akkadius 2025-03-03
* Fix crash in add loot code path ([#4745](https://github.com/EQEmu/Server/pull/4745)) @Akkadius 2025-03-03
* Fix world repop crash ([#4742](https://github.com/EQEmu/Server/pull/4742)) @Akkadius 2025-03-03
* Potential crash fix in scan close mobs ([#4744](https://github.com/EQEmu/Server/pull/4744)) @Akkadius 2025-03-03
### Fixes
* Cleanup zone buckets on instance purge. ([#4739](https://github.com/EQEmu/Server/pull/4739)) @zimp-wow 2025-03-02
* Fix an error causing Endurance Regen to not be applied by items. ([#4738](https://github.com/EQEmu/Server/pull/4738)) @catapultam-habeo 2025-03-02
### World
* Check if port in use to avoid double booting mistakes ([#4740](https://github.com/EQEmu/Server/pull/4740)) @Akkadius 2025-03-03
## [23.1.0] 3/1/2025
### Bots
* Fix unresponsive bots in groups upon group wipe ([#4712](https://github.com/EQEmu/Server/pull/4712)) @nytmyr 2025-02-28
### Code
* More login <-> world code cleanup ([#4724](https://github.com/EQEmu/Server/pull/4724)) @Akkadius 2025-02-28
### Crash
* Check for directory existence before traversing in CheckForCompatibleQuestPlugins ([#4730](https://github.com/EQEmu/Server/pull/4730)) @Akkadius 2025-03-02
* Fix filesystem crash / exception in DatabaseDumpService::RemoveCredentialsFile() ([#4731](https://github.com/EQEmu/Server/pull/4731)) @Akkadius 2025-03-01
* Fix large file size crash in File::GetContents for windows ([#4735](https://github.com/EQEmu/Server/pull/4735)) @Akkadius 2025-03-02
* Fix reload concurrency crash when ran from Spire ([#4733](https://github.com/EQEmu/Server/pull/4733)) @Akkadius 2025-03-02
* Validate item in SE_SummonItemIntoBag ([#4734](https://github.com/EQEmu/Server/pull/4734)) @Akkadius 2025-03-02
* World CLI validation ([#4728](https://github.com/EQEmu/Server/pull/4728)) @Akkadius 2025-03-01
### Database
* Remove force_interactive from big bag updates ([#4727](https://github.com/EQEmu/Server/pull/4727)) @Akkadius 2025-03-01
### Feature
* Add a rule for spells to bypass stacking rules ([#4716](https://github.com/EQEmu/Server/pull/4716)) @catapultam-habeo 2025-02-28
* Evolving items Additions ([#4725](https://github.com/EQEmu/Server/pull/4725)) @neckkola 2025-03-01
### Fixes
* Add character_pet_name to player tables schema @Akkadius 2025-03-02
* Add client packets to questmanager:setguild ([#4732](https://github.com/EQEmu/Server/pull/4732)) @neckkola 2025-03-01
* Clear m_completed_shared_tasks before reloading @Akkadius 2025-02-24
* Fix AA Reset Error Message ([#4720](https://github.com/EQEmu/Server/pull/4720)) @Kinglykrab 2025-02-28
* Fix Issue with Suffixes/Prefixes ([#4723](https://github.com/EQEmu/Server/pull/4723)) @Kinglykrab 2025-02-28
* Fix Trading Items to Bot Pets ([#4721](https://github.com/EQEmu/Server/pull/4721)) @MortimerGreenwald 2025-02-28
* Refactor ApplyItemBonuses to fix double-counting of ATK and recommended levels not correctly applying ([#4713](https://github.com/EQEmu/Server/pull/4713)) @catapultam-habeo 2025-03-01
### Loginserver
* Minor cleanup ([#4729](https://github.com/EQEmu/Server/pull/4729)) @Akkadius 2025-03-01
### Quest API
* Add Key Ring Methods to Perl and Lua ([#4719](https://github.com/EQEmu/Server/pull/4719)) @Kinglykrab 2025-02-28
* Implement eq.handin() and quest::handin() ([#4718](https://github.com/EQEmu/Server/pull/4718)) @Akkadius 2025-02-28
### Tasks
* Extend IsTaskCompleted to also be aware of shared task completion ([#4714](https://github.com/EQEmu/Server/pull/4714)) @Akkadius 2025-02-24
### Zone
* Implement Zone State Saving on Shutdown ([#4715](https://github.com/EQEmu/Server/pull/4715)) @Akkadius 2025-02-28
### Zone State
* Wrap all serialization/deserialization in try/catch ([#4726](https://github.com/EQEmu/Server/pull/4726)) @Akkadius 2025-03-01
## [23.0.2] 2/21/2025
### Bots
* Add checks to ensure bots and pets do not engage on ^pull ([#4708](https://github.com/EQEmu/Server/pull/4708)) @nytmyr 2025-02-22
* Improve positioning ([#4709](https://github.com/EQEmu/Server/pull/4709)) @nytmyr 2025-02-22
* Prevent medding in combat if any mob has bot targeted ([#4707](https://github.com/EQEmu/Server/pull/4707)) @nytmyr 2025-02-22
### Client Mod
* Adds a hacked fast camp rule for GMs ([#4697](https://github.com/EQEmu/Server/pull/4697)) @KimLS 2025-02-20
### Fixes
* Fix Lua Zone ID Exports ([#4700](https://github.com/EQEmu/Server/pull/4700)) @Kinglykrab 2025-02-22
* Fix bad Mob reference in QuestManager::resumetimer() ([#4710](https://github.com/EQEmu/Server/pull/4710)) @zimp-wow 2025-02-22
* Fix cursor load on zone ([#4704](https://github.com/EQEmu/Server/pull/4704)) @nytmyr 2025-02-22
* Fix infinite loop in QuestManager::stoptimer() ([#4703](https://github.com/EQEmu/Server/pull/4703)) @zimp-wow 2025-02-21
### Quest API
* Add GetSpawn() to Perl and Lua ([#4702](https://github.com/EQEmu/Server/pull/4702)) @Kinglykrab 2025-02-22
## [23.0.1] 2/20/2025
### Fixes
* Player event ordering merge fix ([#4699](https://github.com/EQEmu/Server/pull/4699)) @Akkadius 2025-02-20
### Quest API
* Add DisableRespawnTimers to Perl and Lua ([#4691](https://github.com/EQEmu/Server/pull/4691)) @Kinglykrab 2025-02-20
## [23.0.0] 2/19/2025
### Bots
* Add AEHateLine to HateLine ParentType ([#4678](https://github.com/EQEmu/Server/pull/4678)) @nytmyr 2025-02-15
* Add IsInRaidOrGroup checks to ^attack and ^pull ([#4654](https://github.com/EQEmu/Server/pull/4654)) @nytmyr 2025-02-07
* Add missing stance options ([#4681](https://github.com/EQEmu/Server/pull/4681)) @nytmyr 2025-02-15
* Bot Overhaul ([#4580](https://github.com/EQEmu/Server/pull/4580)) @nytmyr 2025-02-03
* Command Cleanup ([#4676](https://github.com/EQEmu/Server/pull/4676)) @nytmyr 2025-02-15
* Correct camp count on ^camp ([#4650](https://github.com/EQEmu/Server/pull/4650)) @nytmyr 2025-02-06
* Correct helper message for forced casts ([#4656](https://github.com/EQEmu/Server/pull/4656)) @nytmyr 2025-02-07
* Crash fixes related to GetNumberNeedingHealedInGroup ([#4684](https://github.com/EQEmu/Server/pull/4684)) @nytmyr 2025-02-15
* Fix AE range calculation ([#4683](https://github.com/EQEmu/Server/pull/4683)) @nytmyr 2025-02-15
* Fix Bards not casting ([#4638](https://github.com/EQEmu/Server/pull/4638)) @nytmyr 2025-02-03
* Fix a couple potential crashes with GetNumberNeedingHealedInGroup ([#4652](https://github.com/EQEmu/Server/pull/4652)) @nytmyr 2025-02-07
* Fix crash related to GetTempSpellType() ([#4649](https://github.com/EQEmu/Server/pull/4649)) @nytmyr 2025-02-06
* Fix pets causing aggro ([#4677](https://github.com/EQEmu/Server/pull/4677)) @nytmyr 2025-02-15
* Fix spell priority commands ([#4660](https://github.com/EQEmu/Server/pull/4660)) @nytmyr 2025-02-08
* Fix typo in positioning ([#4659](https://github.com/EQEmu/Server/pull/4659)) @nytmyr 2025-02-08
* Move BotGetSpellsByType to cache ([#4655](https://github.com/EQEmu/Server/pull/4655)) @nytmyr 2025-02-07
* Prevents casting on ineligible targets due to target type, stacking, etc. ([#4680](https://github.com/EQEmu/Server/pull/4680)) @nytmyr 2025-02-15
* Sanity checks for spell type updates ([#4641](https://github.com/EQEmu/Server/pull/4641)) @nytmyr 2025-02-05
### Bug
* Item Purchase Offset when multiple buyers are buying at the same time. ([#4628](https://github.com/EQEmu/Server/pull/4628)) @fryguy503 2025-02-06
### CI
* Fix database race condition ([#4646](https://github.com/EQEmu/Server/pull/4646)) @Akkadius 2025-02-06
### Client Mod
* Adds a hacked fast camp rule for GMs ([#4697](https://github.com/EQEmu/Server/pull/4697)) @KimLS 2025-02-20
### Code
* Bot RaidGroupSay ([#4653](https://github.com/EQEmu/Server/pull/4653)) @nytmyr 2025-02-07
* Cleanup logic in cursor bag check ([#4642](https://github.com/EQEmu/Server/pull/4642)) @nytmyr 2025-02-04
* Use Repositories for Titles ([#4608](https://github.com/EQEmu/Server/pull/4608)) @Kinglykrab 2025-02-07
### Commands
* Fix #goto not accepting proper heading ([#4685](https://github.com/EQEmu/Server/pull/4685)) @nytmyr 2025-02-15
* Fix Illusion Block ([#4666](https://github.com/EQEmu/Server/pull/4666)) @nytmyr 2025-02-12
### Crash
* Fix raid/group crash regression ([#4671](https://github.com/EQEmu/Server/pull/4671)) @Akkadius 2025-02-12
* Fix zone crash caused by NPC::MoveTo ([#4639](https://github.com/EQEmu/Server/pull/4639)) @catapultam-habeo 2025-02-03
### Databuckets
* Add Zone Scoped Databuckets ([#4690](https://github.com/EQEmu/Server/pull/4690)) @Akkadius 2025-02-18
### Expeditions
* Move expedition code into DynamicZone ([#4672](https://github.com/EQEmu/Server/pull/4672)) @hgtw 2025-02-16
### Feature
* Add Support for Tradeskill Recipe Inspect ([#4648](https://github.com/EQEmu/Server/pull/4648)) @Kinglykrab 2025-02-06
* Add rule to allow /changepetname to function without being enabled by scripts. @catapultam-habeo 2025-02-05
* GuildBank Updates ([#4674](https://github.com/EQEmu/Server/pull/4674)) @neckkola 2025-02-15
* Implement Big Bags ([#4606](https://github.com/EQEmu/Server/pull/4606)) @Kinglykrab 2025-02-03
### Fixes
* #rq and #reload quest alias ([#4694](https://github.com/EQEmu/Server/pull/4694)) @Akkadius 2025-02-18
* Always spawn zone controller first ([#4669](https://github.com/EQEmu/Server/pull/4669)) @Akkadius 2025-02-12
* Big Bag Cleanup ([#4643](https://github.com/EQEmu/Server/pull/4643)) @fryguy503 2025-02-05
* Big Bag additional fixes ([#4644](https://github.com/EQEmu/Server/pull/4644)) @fryguy503 2025-02-05
* Change logging level for no items found in a bazaar search to reduce spam logs. ([#4675](https://github.com/EQEmu/Server/pull/4675)) @neckkola 2025-02-13
* Find Zone - Expansion Settings ([#4692](https://github.com/EQEmu/Server/pull/4692)) @MortimerGreenwald 2025-02-18
* Fix Beastlord Warder Size Modifier ([#4665](https://github.com/EQEmu/Server/pull/4665)) @Kinglykrab 2025-02-12
* Fix CI since hand-ins are merged @Akkadius 2025-02-03
* Fix Illusion Fade Texture Bug ([#4673](https://github.com/EQEmu/Server/pull/4673)) @Kinglykrab 2025-02-14
* Fix Item Discovery ([#4663](https://github.com/EQEmu/Server/pull/4663)) @Kinglykrab 2025-02-10
* Fix ST_GroupNoPets and ST_GroupClientAndPet ([#4667](https://github.com/EQEmu/Server/pull/4667)) @nytmyr 2025-02-10
* Fix SendStatsWindow Mod2 Value Display ([#4658](https://github.com/EQEmu/Server/pull/4658)) @Kinglykrab 2025-02-07
* Fix Tradeskill Queries ([#4661](https://github.com/EQEmu/Server/pull/4661)) @Kinglykrab 2025-02-09
* Fix error in update manifest ([#4637](https://github.com/EQEmu/Server/pull/4637)) @nytmyr 2025-02-03
* Fix issue with getting an unset nested databucket ([#4693](https://github.com/EQEmu/Server/pull/4693)) @Akkadius 2025-02-19
* Fix non-error in player_event_logs ([#4695](https://github.com/EQEmu/Server/pull/4695)) @Akkadius 2025-02-18
* GMMove Update Edge Case With Clients ([#4686](https://github.com/EQEmu/Server/pull/4686)) @Akkadius 2025-02-15
* Item Handins to Pets ([#4687](https://github.com/EQEmu/Server/pull/4687)) @Akkadius 2025-02-15
* Parcel Delivery Updates ([#4688](https://github.com/EQEmu/Server/pull/4688)) @neckkola 2025-02-16
* Prevent zone from loading ETL ID's on bootup ([#4696](https://github.com/EQEmu/Server/pull/4696)) @Akkadius 2025-02-18
* Update pre big bag corpse slot_id's to support big bags ([#4679](https://github.com/EQEmu/Server/pull/4679)) @nytmyr 2025-02-15
### Inventory
* Fix cursor bag saving to invalid slot_ids ([#4640](https://github.com/EQEmu/Server/pull/4640)) @nytmyr 2025-02-04
### Items
* Overhaul Item Hand-in System ([#4593](https://github.com/EQEmu/Server/pull/4593)) @Akkadius 2025-02-03
### Loginserver
* Fix iterator crash ([#4670](https://github.com/EQEmu/Server/pull/4670)) @Akkadius 2025-02-12
* Modernize codebase ([#4647](https://github.com/EQEmu/Server/pull/4647)) @Akkadius 2025-02-06
### NPC Handins
* Fix MultiQuest Handins ([#4651](https://github.com/EQEmu/Server/pull/4651)) @Akkadius 2025-02-07
### Performance
* Server Reload Overhaul ([#4689](https://github.com/EQEmu/Server/pull/4689)) @Akkadius 2025-02-18
### Player Event Logs
* Migrate and Deprecate QS Legacy Logging ([#4542](https://github.com/EQEmu/Server/pull/4542)) @neckkola 2025-02-05
### Quest API
* Add Bandolier Methods ([#4635](https://github.com/EQEmu/Server/pull/4635)) @Kinglykrab 2025-02-03
* Add Potion Belt Methods ([#4634](https://github.com/EQEmu/Server/pull/4634)) @Kinglykrab 2025-02-04
* Add Zone Support to Perl and Lua ([#4662](https://github.com/EQEmu/Server/pull/4662)) @Kinglykrab 2025-02-09
### Spells
* Add all types to checks for max_targets_allowed rule for AEs ([#4682](https://github.com/EQEmu/Server/pull/4682)) @nytmyr 2025-02-15
## [22.62.2] 2/1/2025
### Fixes
+3
View File
@@ -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}")
+123 -56
View File
@@ -1,83 +1,150 @@
# EQEmulator Core Server
| Drone (Linux x64) | Drone (Windows x64) |
|:---:|:---:|
|[![Build Status](http://drone.akkadius.com/api/badges/EQEmu/Server/status.svg)](http://drone.akkadius.com/EQEmu/Server) |[![Build Status](http://drone.akkadius.com/api/badges/EQEmu/Server/status.svg)](http://drone.akkadius.com/EQEmu/Server) |
<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&amp;logo=discord&amp;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 Install Count](http://analytics.akkadius.com/?install_count&windows_count)|![Linux Install Count](http://analytics.akkadius.com/?install_count&linux_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 ProjectEQs 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** | [![Docs](https://img.shields.io/badge/docs-MkDocs%20Powered-blueviolet)](https://docs.eqemu.io) | [docs.eqemu.io](https://docs.eqemu.io/) |
| **Discord Community**| [![Discord](https://img.shields.io/discord/212663220849213441?label=Discord&logo=discord&color=7289DA)](https://discord.gg/QHsm7CD) | [Join Discord](https://discord.gg/QHsm7CD) |
| **Latest Release** | [![Latest Release](https://img.shields.io/github/v/release/eqemu/server)](https://github.com/eqemu/server/releases) <br> [![Release Date](https://img.shields.io/github/release-date/EQEmu/Server)](https://github.com/EQEmu/Server/releases) <br> [![All Releases](https://img.shields.io/github/downloads/eqemu/server/total.svg)](https://github.com/eqemu/server/releases) | [View Releases](https://github.com/eqemu/server/releases) |
| **License** | [![License](https://img.shields.io/github/license/EQEmu/Server)](./LICENSE) | [View License](./LICENSE) |
| **Build Status** | [![Build Status](http://drone.akkadius.com/api/badges/EQEmu/Server/status.svg)](http://drone.akkadius.com/EQEmu/Server) | [View Build Status](http://drone.akkadius.com/EQEmu/Server) |
| **Docker Pulls** | [![Docker Pulls](https://img.shields.io/docker/pulls/akkadius/eqemu-server)](https://hub.docker.com/r/akkadius/eqemu-server) | [Docker Hub](https://hub.docker.com/r/akkadius/eqemu-server) |
| **Contributions** | [![GitHub PRs](https://img.shields.io/github/issues-pr-closed/eqemu/server)](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>
+9 -16
View File
@@ -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;
+9 -16
View File
@@ -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);
+18 -17
View File
@@ -17,11 +17,13 @@ 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
deity.cpp
dynamic_zone_base.cpp
dynamic_zone_lockout.cpp
emu_constants.cpp
emu_limits.cpp
emu_opcodes.cpp
@@ -40,7 +42,6 @@ SET(common_sources
events/player_event_logs.cpp
events/player_event_discord_formatter.cpp
evolving_items.cpp
expedition_lockout_timer.cpp
extprofile.cpp
discord/discord_manager.cpp
faction.cpp
@@ -103,9 +104,9 @@ SET(common_sources
net/console_server.cpp
net/console_server_connection.cpp
net/crc32.cpp
net/daybreak_connection.cpp
net/eqstream.cpp
net/packet.cpp
net/reliable_stream_connection.cpp
net/servertalk_client_connection.cpp
net/servertalk_legacy_client_connection.cpp
net/servertalk_server.cpp
@@ -213,16 +214,14 @@ SET(repositories
repositories/base/base_discovered_items_repository.h
repositories/base/base_doors_repository.h
repositories/base/base_dynamic_zones_repository.h
repositories/base/base_dynamic_zone_lockouts_repository.h
repositories/base/base_dynamic_zone_members_repository.h
repositories/base/base_dynamic_zone_templates_repository.h
repositories/base/base_expeditions_repository.h
repositories/base/base_expedition_lockouts_repository.h
repositories/base/base_faction_association_repository.h
repositories/base/base_faction_base_data_repository.h
repositories/base/base_faction_list_repository.h
repositories/base/base_faction_list_mod_repository.h
repositories/base/base_faction_values_repository.h
repositories/base/base_find_location_repository.h
repositories/base/base_fishing_repository.h
repositories/base/base_forage_repository.h
repositories/base/base_friends_repository.h
@@ -411,16 +410,14 @@ SET(repositories
repositories/discovered_items_repository.h
repositories/doors_repository.h
repositories/dynamic_zones_repository.h
repositories/dynamic_zone_lockouts_repository.h
repositories/dynamic_zone_members_repository.h
repositories/dynamic_zone_templates_repository.h
repositories/expeditions_repository.h
repositories/expedition_lockouts_repository.h
repositories/faction_association_repository.h
repositories/faction_base_data_repository.h
repositories/faction_list_repository.h
repositories/faction_list_mod_repository.h
repositories/faction_values_repository.h
repositories/find_location_repository.h
repositories/fishing_repository.h
repositories/forage_repository.h
repositories/friends_repository.h
@@ -563,6 +560,7 @@ SET(common_headers
discord/discord.h
discord/discord_manager.h
dynamic_zone_base.h
dynamic_zone_lockout.h
emu_constants.h
emu_limits.h
emu_opcodes.h
@@ -589,7 +587,6 @@ SET(common_headers
events/player_events.h
event_sub.h
evolving_items.h
expedition_lockout_timer.h
extprofile.h
faction.h
file.h
@@ -606,7 +603,6 @@ SET(common_headers
ipc_mutex.h
ip_util.h
item_data.h
item_fieldlist.h
item_instance.h
json_config.h
light_source.h
@@ -649,6 +645,7 @@ SET(common_headers
server_event_scheduler.h
serverinfo.h
servertalk.h
server_reload_types.h
shared_tasks.h
shareddb.h
skills.h
@@ -673,18 +670,20 @@ SET(common_headers
net/console_server.h
net/console_server_connection.h
net/crc32.h
net/daybreak_connection.h
net/daybreak_structs.h
net/dns.h
net/endian.h
net/eqstream.h
net/packet.h
net/reliable_stream_connection.h
net/reliable_stream_pooling.h
net/reliable_stream_structs.h
net/servertalk_client_connection.h
net/servertalk_legacy_client_connection.h
net/servertalk_common.h
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
@@ -743,9 +742,6 @@ SOURCE_GROUP(Net FILES
net/console_server_connection.h
net/crc32.cpp
net/crc32.h
net/daybreak_connection.cpp
net/daybreak_connection.h
net/daybreak_structs.h
net/dns.h
net/endian.h
net/eqmq.cpp
@@ -754,6 +750,10 @@ SOURCE_GROUP(Net FILES
net/eqstream.h
net/packet.cpp
net/packet.h
net/reliable_stream_connection.cpp
net/reliable_stream_connection.h
net/reliable_stream_pooling.h
net/reliable_stream_structs.h
net/servertalk_client_connection.cpp
net/servertalk_client_connection.h
net/servertalk_legacy_client_connection.cpp
@@ -765,6 +765,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
@@ -839,8 +840,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
View File
@@ -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()) {
+5 -5
View File
@@ -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 &&
+6 -2
View File
@@ -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
View File
@@ -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);
}
+277 -92
View File
@@ -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)
{
@@ -19,10 +25,6 @@ void DataBucket::SetData(const std::string &bucket_key, const std::string &bucke
.key = bucket_key,
.value = bucket_value,
.expires = expires_time,
.account_id = 0,
.character_id = 0,
.npc_id = 0,
.bot_id = 0
};
DataBucket::SetData(k);
@@ -31,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();
}
@@ -54,6 +57,9 @@ void DataBucket::SetData(const DataBucketKey &k_)
}
else if (k.bot_id > 0) {
b.bot_id = k.bot_id;
} else if (k.zone_id > 0) {
b.zone_id = k.zone_id;
b.instance_id = k.instance_id;
}
const uint64 bucket_id = b.id;
@@ -64,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;
@@ -76,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();
@@ -106,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) {
@@ -143,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
}
@@ -189,12 +226,14 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k_,
}
LogDataBuckets(
"Getting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}]",
"Getting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}] zone_id [{}] instance_id [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_id
k.npc_id,
k.zone_id,
k.instance_id
);
bool can_cache = CanCache(k);
@@ -211,7 +250,7 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k_,
LogDataBuckets("Returning key [{}] value [{}] from cache", e.key_, e.value);
if (is_nested_key) {
if (is_nested_key && !k_.key.empty()) {
return ExtractNestedValue(e, k_.key);
}
@@ -244,17 +283,21 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k_,
.account_id = k.account_id,
.character_id = k.character_id,
.npc_id = k.npc_id,
.bot_id = k.bot_id
.bot_id = k.bot_id,
.zone_id = k.zone_id,
.instance_id = k.instance_id
}
);
LogDataBuckets(
"Key [{}] not found in database, adding to cache as a miss account_id [{}] character_id [{}] npc_id [{}] bot_id [{}] cache size before [{}] after [{}]",
"Key [{}] not found in database, adding to cache as a miss account_id [{}] character_id [{}] npc_id [{}] bot_id [{}] zone_id [{}] instance_id [{}] cache size before [{}] after [{}]",
k.key,
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()
);
@@ -287,7 +330,7 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k_,
}
// Handle nested key extraction
if (is_nested_key) {
if (is_nested_key && !k_.key.empty()) {
return ExtractNestedValue(bucket, k_.key);
}
@@ -309,65 +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 [{}] cache size before [{}] after [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_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(
@@ -390,12 +488,15 @@ std::string DataBucket::GetDataExpires(const DataBucketKey &k)
std::string DataBucket::GetDataRemaining(const DataBucketKey &k)
{
LogDataBuckets(
"Getting bucket remaining key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}]",
"Getting bucket remaining key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}] bot_id [{}] zone_id [{}] instance_id [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_id
k.npc_id,
k.bot_id,
k.zone_id,
k.instance_id
);
auto r = GetData(k);
@@ -408,39 +509,46 @@ std::string DataBucket::GetDataRemaining(const DataBucketKey &k)
std::string DataBucket::GetScopedDbFilters(const DataBucketKey &k)
{
std::vector<std::string> query = {};
std::vector<std::string> q = {};
if (k.character_id > 0) {
query.emplace_back(fmt::format("character_id = {}", k.character_id));
q.emplace_back(fmt::format("character_id = {}", k.character_id));
}
else {
query.emplace_back("character_id = 0");
q.emplace_back("character_id = 0");
}
if (k.account_id > 0) {
query.emplace_back(fmt::format("account_id = {}", k.account_id));
q.emplace_back(fmt::format("account_id = {}", k.account_id));
}
else {
query.emplace_back("account_id = 0");
q.emplace_back("account_id = 0");
}
if (k.npc_id > 0) {
query.emplace_back(fmt::format("npc_id = {}", k.npc_id));
q.emplace_back(fmt::format("npc_id = {}", k.npc_id));
}
else {
query.emplace_back("npc_id = 0");
q.emplace_back("npc_id = 0");
}
if (k.bot_id > 0) {
query.emplace_back(fmt::format("bot_id = {}", k.bot_id));
q.emplace_back(fmt::format("bot_id = {}", k.bot_id));
}
else {
query.emplace_back("bot_id = 0");
q.emplace_back("bot_id = 0");
}
if (k.zone_id > 0) {
q.emplace_back(fmt::format("zone_id = {} AND instance_id = {}", k.zone_id, k.instance_id));
}
else {
q.emplace_back("zone_id = 0 AND instance_id = 0");
}
return fmt::format(
"{} {}",
Strings::Join(query, " AND "),
!query.empty() ? "AND" : ""
Strings::Join(q, " AND "),
!q.empty() ? "AND" : ""
);
}
@@ -451,7 +559,52 @@ bool DataBucket::CheckBucketMatch(const DataBucketsRepository::DataBuckets &dbe,
dbe.bot_id == k.bot_id &&
dbe.account_id == k.account_id &&
dbe.character_id == k.character_id &&
dbe.npc_id == k.npc_id
dbe.npc_id == k.npc_id &&
dbe.zone_id == k.zone_id &&
dbe.instance_id == k.instance_id
);
}
void DataBucket::LoadZoneCache(uint16 zone_id, uint16 instance_id)
{
const auto &l = DataBucketsRepository::GetWhere(
database,
fmt::format(
"zone_id = {} AND instance_id = {} AND (`expires` > {} OR `expires` = 0)",
zone_id,
instance_id,
(long long) std::time(nullptr)
)
);
if (l.empty()) {
return;
}
LogDataBucketsDetail("cache size before [{}] l size [{}]", g_data_bucket_cache.size(), l.size());
uint32 added_count = 0;
for (const auto &e: l) {
if (!ExistsInCache(e)) {
added_count++;
}
}
for (const auto &e: l) {
if (!ExistsInCache(e)) {
LogDataBucketsDetail("bucket id [{}] bucket key [{}] bucket value [{}]", e.id, e.key_, e.value);
g_data_bucket_cache.emplace_back(e);
}
}
LogDataBucketsDetail("cache size after [{}]", g_data_bucket_cache.size());
LogDataBuckets(
"Loaded [{}] zone keys new cache size is [{}]",
l.size(),
g_data_bucket_cache.size()
);
}
@@ -541,7 +694,7 @@ void DataBucket::BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector
);
}
void DataBucket::DeleteCachedBuckets(DataBucketLoadType::Type type, uint32 id)
void DataBucket::DeleteCachedBuckets(DataBucketLoadType::Type type, uint32 id, uint32 secondary_id)
{
size_t size_before = g_data_bucket_cache.size();
@@ -553,7 +706,8 @@ void DataBucket::DeleteCachedBuckets(DataBucketLoadType::Type type, uint32 id)
return (
(type == DataBucketLoadType::Bot && e.bot_id == id) ||
(type == DataBucketLoadType::Account && e.account_id == id) ||
(type == DataBucketLoadType::Client && e.character_id == id)
(type == DataBucketLoadType::Client && e.character_id == id) ||
(type == DataBucketLoadType::Zone && e.zone_id == id && e.instance_id == secondary_id)
);
}
),
@@ -595,7 +749,9 @@ void DataBucket::DeleteFromMissesCache(DataBucketsRepository::DataBuckets e)
ce.account_id == e.account_id &&
ce.character_id == e.character_id &&
ce.npc_id == e.npc_id &&
ce.bot_id == e.bot_id;
ce.bot_id == e.bot_id &&
ce.zone_id == e.zone_id &&
ce.instance_id == e.instance_id;
}
),
g_data_bucket_cache.end()
@@ -647,15 +803,44 @@ void DataBucket::DeleteFromCache(uint64 id, DataBucketLoadType::Type type)
);
}
void DataBucket::DeleteZoneFromCache(uint16 zone_id, uint16 instance_id, DataBucketLoadType::Type type)
{
size_t size_before = g_data_bucket_cache.size();
g_data_bucket_cache.erase(
std::remove_if(
g_data_bucket_cache.begin(),
g_data_bucket_cache.end(),
[&](DataBucketsRepository::DataBuckets &e) {
switch (type) {
case DataBucketLoadType::Zone:
return e.zone_id == zone_id && e.instance_id == instance_id;
default:
return false;
}
}
),
g_data_bucket_cache.end()
);
LogDataBuckets(
"Deleted zone [{}] instance [{}] from cache size before [{}] after [{}]",
zone_id,
instance_id,
size_before,
g_data_bucket_cache.size()
);
}
// CanCache returns whether a bucket can be cached or not
// characters are only in one zone at a time so we can cache locally to the zone
// bots (not implemented) are only in one zone at a time so we can cache locally to the zone
// npcs (ids) can be in multiple zones so we can't cache locally to the zone
bool DataBucket::CanCache(const DataBucketKey &key)
{
if (key.character_id > 0 || key.account_id > 0 || key.bot_id > 0) {
if (key.character_id > 0 || key.account_id > 0 || key.bot_id > 0 || key.zone_id > 0) {
return true;
}
return false;
}
}
+14 -12
View File
@@ -2,20 +2,20 @@
#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;
std::string value;
std::string expires;
int64_t account_id = 0;
int64_t character_id = 0;
int64_t npc_id = 0;
int64_t bot_id = 0;
uint64_t account_id = 0;
uint64_t character_id = 0;
uint32_t npc_id = 0;
uint32_t bot_id = 0;
uint16_t zone_id = 0;
uint16_t instance_id = 0;
};
namespace DataBucketLoadType {
@@ -23,6 +23,7 @@ namespace DataBucketLoadType {
Bot,
Account,
Client,
Zone,
MaxType
};
@@ -30,6 +31,7 @@ namespace DataBucketLoadType {
"Bot",
"Account",
"Client",
"Zone"
};
}
@@ -42,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);
@@ -56,12 +56,14 @@ public:
static bool CheckBucketMatch(const DataBucketsRepository::DataBuckets &dbe, const DataBucketKey &k);
static bool ExistsInCache(const DataBucketsRepository::DataBuckets &entry);
static void LoadZoneCache(uint16 zone_id, uint16 instance_id);
static void BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector<uint32> ids);
static void DeleteCachedBuckets(DataBucketLoadType::Type type, uint32 id);
static void DeleteCachedBuckets(DataBucketLoadType::Type type, uint32 id, uint32 secondary_id = 0);
static void DeleteFromMissesCache(DataBucketsRepository::DataBuckets e);
static void ClearCache();
static void DeleteFromCache(uint64 id, DataBucketLoadType::Type type);
static void DeleteZoneFromCache(uint16 zone_id, uint16 instance_id, DataBucketLoadType::Type type);
static bool CanCache(const DataBucketKey &key);
static DataBucketsRepository::DataBuckets
ExtractNestedValue(const DataBucketsRepository::DataBuckets &bucket, const std::string &full_key);
+78 -9
View File
@@ -245,7 +245,7 @@ uint32 Database::CreateAccount(
e.password = password;
}
LogInfo("Account Attempting to be created: [{}:{}] status: {}", loginserver, name, status);
LogInfo("Account attempting to be created loginserver [{}] name [{}] status [{}]", loginserver, name, status);
e = AccountRepository::InsertOne(*this, e);
@@ -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
View File
@@ -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);
+9 -4
View File
@@ -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()) {
@@ -596,7 +596,12 @@ void DatabaseDumpService::BuildCredentialsFile()
void DatabaseDumpService::RemoveCredentialsFile()
{
if (File::Exists(CREDENTIALS_FILE)) {
std::filesystem::remove(CREDENTIALS_FILE);
try {
std::filesystem::remove(CREDENTIALS_FILE);
}
catch (std::exception &e) {
LogError("std::filesystem::remove err [{}]", e.what());
}
}
}
-1
View File
@@ -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;
+39 -4
View File
@@ -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");
}
}
+9
View File
@@ -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();
};
+418 -3
View File
@@ -6417,7 +6417,7 @@ ADD COLUMN `guid` bigint(20) UNSIGNED NOT NULL DEFAULT 0 AFTER `ornament_hero_mo
ADD PRIMARY KEY (`account_id`, `slot_id`);
)",
.content_schema_update = false,
.force_interactive = true
.force_interactive = false
},
ManifestEntry{
.version = 9298,
@@ -6481,7 +6481,7 @@ UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id`
UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2541) + 11210) WHERE `slot_id` BETWEEN 2541 AND 2550; -- Shared Bank Bag 2
)",
.content_schema_update = false,
.force_interactive = true
.force_interactive = false
},
ManifestEntry{
.version = 9299,
@@ -6770,7 +6770,422 @@ SET `aliases` =
WHERE `command` = 'illusionblock'
AND `aliases` NOT LIKE '%ib%';
)",
}
},
ManifestEntry{
.version = 9303,
.description = "2025_02_13_corpse_slot_fix.sql",
.check = "SELECT * FROM `character_corpse_items` WHERE `equip_slot` BETWEEN 251 AND 350",
.condition = "not_empty",
.match = "",
.sql = R"(
UPDATE `character_corpse_items` SET `equip_slot` = ((`equip_slot` - 251) + 4010) WHERE `equip_slot` BETWEEN 251 AND 260; -- Bag 1
UPDATE `character_corpse_items` SET `equip_slot` = ((`equip_slot` - 261) + 4210) WHERE `equip_slot` BETWEEN 261 AND 270; -- Bag 2
UPDATE `character_corpse_items` SET `equip_slot` = ((`equip_slot` - 271) + 4410) WHERE `equip_slot` BETWEEN 271 AND 280; -- Bag 3
UPDATE `character_corpse_items` SET `equip_slot` = ((`equip_slot` - 281) + 4610) WHERE `equip_slot` BETWEEN 281 AND 290; -- Bag 4
UPDATE `character_corpse_items` SET `equip_slot` = ((`equip_slot` - 291) + 4810) WHERE `equip_slot` BETWEEN 291 AND 300; -- Bag 5
UPDATE `character_corpse_items` SET `equip_slot` = ((`equip_slot` - 301) + 5010) WHERE `equip_slot` BETWEEN 301 AND 310; -- Bag 6
UPDATE `character_corpse_items` SET `equip_slot` = ((`equip_slot` - 311) + 5210) WHERE `equip_slot` BETWEEN 311 AND 320; -- Bag 7
UPDATE `character_corpse_items` SET `equip_slot` = ((`equip_slot` - 321) + 5410) WHERE `equip_slot` BETWEEN 321 AND 330; -- Bag 8
UPDATE `character_corpse_items` SET `equip_slot` = ((`equip_slot` - 331) + 5610) WHERE `equip_slot` BETWEEN 331 AND 340; -- Bag 9
UPDATE `character_corpse_items` SET `equip_slot` = ((`equip_slot` - 341) + 5810) WHERE `equip_slot` BETWEEN 341 AND 350; -- Bag 10
)",
},
ManifestEntry{
.version = 9304,
.description = "2024_12_01_update_guild_bank",
.check = "SHOW COLUMNS FROM `guild_bank` LIKE 'augment_one_id'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `guild_bank`
DROP INDEX `guildid`,
CHANGE COLUMN `guildid` `guild_id` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `id`,
CHANGE COLUMN `itemid` `item_id` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `slot`,
CHANGE COLUMN `whofor` `who_for` VARCHAR(64) NULL DEFAULT NULL COLLATE 'utf8_general_ci' AFTER `permissions`,
ADD COLUMN `augment_one_id` INT UNSIGNED NULL DEFAULT '0' AFTER `item_id`,
ADD COLUMN `augment_two_id` INT UNSIGNED NULL DEFAULT '0' AFTER `augment_one_id`,
ADD COLUMN `augment_three_id` INT UNSIGNED NULL DEFAULT '0' AFTER `augment_two_id`,
ADD COLUMN `augment_four_id` INT UNSIGNED NULL DEFAULT '0' AFTER `augment_three_id`,
ADD COLUMN `augment_five_id` INT UNSIGNED NULL DEFAULT '0' AFTER `augment_four_id`,
ADD COLUMN `augment_six_id` INT UNSIGNED NULL DEFAULT '0' AFTER `augment_five_id`,
CHANGE COLUMN `qty` `quantity` INT(10) NOT NULL DEFAULT '0' AFTER `augment_six_id`;
ALTER TABLE `guild_bank`
ADD INDEX `guild_id` (`guild_id`);
)"
},
ManifestEntry{
.version = 9305,
.description = "2024_12_01_expedition_dz_merge.sql",
.check = "SHOW COLUMNS FROM `dynamic_zones` LIKE 'is_locked'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `dynamic_zones`
ADD COLUMN `is_locked` TINYINT NOT NULL DEFAULT '0' AFTER `has_zone_in`,
ADD COLUMN `add_replay` TINYINT NOT NULL DEFAULT '1' AFTER `is_locked`;
ALTER TABLE `expedition_lockouts`
CHANGE COLUMN `expedition_id` `dynamic_zone_id` INT(10) UNSIGNED NOT NULL AFTER `id`,
DROP INDEX `expedition_id_event_name`,
ADD UNIQUE INDEX `dz_id_event_name` (`dynamic_zone_id`, `event_name`) USING BTREE;
UPDATE expedition_lockouts lockouts
INNER JOIN expeditions ON lockouts.dynamic_zone_id = expeditions.id
SET lockouts.dynamic_zone_id = expeditions.dynamic_zone_id;
DROP TABLE `expeditions`;
RENAME TABLE `expedition_lockouts` TO `dynamic_zone_lockouts`;
)"
},
ManifestEntry{
.version = 9306,
.description = "2025_02_16_data_buckets_zone_id_instance_id.sql",
.check = "SHOW COLUMNS FROM `data_buckets` LIKE 'zone_id'",
.condition = "empty",
.match = "",
.sql = R"(
-- 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`;
-- Add zone_id, instance_id
ALTER TABLE `data_buckets`
MODIFY COLUMN `npc_id` int(11) NOT NULL DEFAULT 0 AFTER `character_id`,
MODIFY COLUMN `bot_id` int(11) NOT NULL DEFAULT 0 AFTER `npc_id`,
ADD COLUMN `zone_id` smallint(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `bot_id`,
ADD COLUMN `instance_id` smallint(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `zone_id`;
ALTER TABLE `data_buckets`
MODIFY COLUMN `account_id` bigint(11) UNSIGNED NULL DEFAULT 0 AFTER `expires`,
MODIFY COLUMN `character_id` bigint(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `account_id`,
MODIFY COLUMN `npc_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `character_id`,
MODIFY COLUMN `bot_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `npc_id`;
-- 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 INDEX idx_instance_id ON data_buckets (instance_id);
)",
.content_schema_update = false
},
ManifestEntry{
.version = 9307,
.description = "2025_02_17_zone_state_spawns.sql",
.check = "SHOW TABLES LIKE 'zone_state_spawns'",
.condition = "empty",
.match = "",
.sql = R"(
CREATE TABLE `zone_state_spawns` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`zone_id` int(11) unsigned DEFAULT NULL,
`instance_id` int(11) unsigned DEFAULT NULL,
`is_corpse` tinyint(11) DEFAULT 0,
`decay_in_seconds` int(11) DEFAULT 0,
`npc_id` int(10) unsigned DEFAULT NULL,
`spawn2_id` int(10) unsigned NOT NULL,
`spawngroup_id` int(10) unsigned NOT NULL,
`x` float NOT NULL,
`y` float NOT NULL,
`z` float NOT NULL,
`heading` float NOT NULL,
`respawn_time` int(10) unsigned NOT NULL,
`variance` int(10) unsigned NOT NULL,
`grid` int(10) unsigned DEFAULT 0,
`current_waypoint` int(11) DEFAULT 0,
`path_when_zone_idle` smallint(6) DEFAULT 0,
`condition_id` smallint(5) unsigned DEFAULT 0,
`condition_min_value` smallint(6) DEFAULT 0,
`enabled` smallint(6) DEFAULT 1,
`anim` smallint(5) unsigned DEFAULT 0,
`loot_data` text DEFAULT NULL,
`entity_variables` text DEFAULT NULL,
`buffs` text DEFAULT NULL,
`hp` bigint(20) DEFAULT 0,
`mana` bigint(20) DEFAULT 0,
`endurance` bigint(20) DEFAULT 0,
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4
)",
.content_schema_update = false
},
ManifestEntry{
.version = 9308,
.description = "2025_03_29_add_multivalue_support_to_evolving_subtype.sql",
.check = "SHOW COLUMNS FROM `items_evolving_details` LIKE 'sub_type'",
.condition = "missing",
.match = "varchar(200)",
.sql = R"(
ALTER TABLE `items_evolving_details`
CHANGE COLUMN `sub_type` `sub_type` VARCHAR(200) NULL DEFAULT '0' AFTER `type`;
)",
.content_schema_update = true
},
// this one got missed being added to PEQ dumps so adding it again so it gets added when folks take a new release
ManifestEntry{
.version = 9309,
.description = "2025_03_1_create_pet_names_table_if_not_exist.sql",
.check = "SHOW TABLES LIKE 'character_pet_name'",
.condition = "empty",
.match = "",
.sql = R"(
CREATE TABLE `character_pet_name` (
`character_id` INT(11) NOT NULL PRIMARY KEY,
`name` VARCHAR(64) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
)",
},
ManifestEntry{
.version = 9310,
.description = "2025_03_7_expand_horse_def.sql",
.check = "SHOW COLUMNS FROM `horses` LIKE 'helmtexture'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `horses`
ADD COLUMN `helmtexture` TINYINT(2) NOT NULL DEFAULT -1 AFTER `texture`;
)",
.content_schema_update = true
},
ManifestEntry{
.version = 9311,
.description = "2025_03_09_add_zone_state_is_zone_field.sql",
.check = "SHOW COLUMNS FROM `zone_state_spawns` LIKE 'is_zone'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `zone_state_spawns`
ADD COLUMN `is_zone` tinyint(11) NULL DEFAULT 0 AFTER `is_corpse`;
)",
.content_schema_update = false
},
ManifestEntry{
.version = 9312,
.description = "2025_03_11_data_bucket_indexes.sql",
.check = "SHOW INDEX FROM data_buckets",
.condition = "missing",
.match = "idx_zone_instance_expires",
.sql = R"(
DROP INDEX IF EXISTS `idx_zone_instance_expires` ON `data_buckets`;
DROP INDEX IF EXISTS `idx_character_expires` ON `data_buckets`;
DROP INDEX IF EXISTS `idx_bot_expires` ON `data_buckets`;
ALTER TABLE data_buckets ADD INDEX idx_zone_instance_expires (zone_id, instance_id, expires);
ALTER TABLE data_buckets ADD INDEX idx_character_expires (character_id, expires);
ALTER TABLE data_buckets ADD INDEX idx_bot_expires (bot_id, expires);
)",
.content_schema_update = false
},
ManifestEntry{
.version = 9313,
.description = "2025_03_11_zone_state_spawns.sql",
.check = "SHOW INDEX FROM zone_state_spawns",
.condition = "missing",
.match = "idx_zone_instance",
.sql = R"(
ALTER TABLE zone_state_spawns ADD INDEX idx_zone_instance (zone_id, instance_id);
)",
.content_schema_update = false
},
ManifestEntry{
.version = 9314,
.description = "2025_03_12_zone_state_spawns_one_time_truncate.sql",
.check = "SELECT * FROM db_version WHERE version >= 9314",
.condition = "empty",
.match = "",
.sql = R"(
TRUNCATE TABLE zone_state_spawns;
)",
.content_schema_update = false
},
ManifestEntry{
.version = 9315,
.description = "2025_03_29_character_tribute_index.sql",
.check = "SHOW INDEX FROM character_tribute",
.condition = "missing",
.match = "idx_character_id",
.sql = R"(
ALTER TABLE character_tribute ADD INDEX idx_character_id (character_id);
)",
.content_schema_update = false
},
ManifestEntry{
.version = 9316,
.description = "2025_03_29_player_titlesets_index.sql",
.check = "SHOW INDEX FROM player_titlesets",
.condition = "missing",
.match = "idx_char_id",
.sql = R"(
ALTER TABLE player_titlesets ADD INDEX idx_char_id (char_id);
)",
.content_schema_update = false
},
ManifestEntry{
.version = 9317,
.description = "2025_03_29_respawn_times_instance_index.sql",
.check = "SHOW INDEX FROM respawn_times",
.condition = "missing",
.match = "idx_instance_id",
.sql = R"(
ALTER TABLE respawn_times ADD INDEX idx_instance_id (instance_id);
)",
.content_schema_update = false
},
ManifestEntry{
.version = 9318,
.description = "2025_03_29_zone_state_spawns_instance_index.sql",
.check = "SHOW INDEX FROM zone_state_spawns",
.condition = "missing",
.match = "idx_instance_id",
.sql = R"(
ALTER TABLE zone_state_spawns ADD INDEX idx_instance_id (instance_id);
)",
.content_schema_update = false
},
ManifestEntry{
.version = 9319,
.description = "2025_03_29_data_buckets_expires_index.sql",
.check = "SHOW INDEX FROM data_buckets",
.condition = "missing",
.match = "idx_expires",
.sql = R"(
CREATE INDEX idx_expires ON data_buckets (expires);
)",
.content_schema_update = false
},
ManifestEntry{
.version = 9320,
.description = "2025_03_23_add_respawn_times_expire_at.sql",
.check = "SHOW COLUMNS FROM `respawn_times` LIKE 'expire_at'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `respawn_times`
ADD COLUMN `expire_at` int(11) UNSIGNED NULL DEFAULT 0 AFTER `duration`;
UPDATE respawn_times set expire_at = `start` + `duration`; -- backfill existing data
CREATE INDEX `idx_expire_at` ON `respawn_times` (`expire_at`);
)",
.content_schema_update = false
},
ManifestEntry{
.version = 9321,
.description = "2025_03_30_instance_list_add_expire_at.sql",
.check = "SHOW COLUMNS FROM `instance_list` LIKE 'expire_at'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `instance_list`
ADD COLUMN `expire_at` bigint(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `duration`;
UPDATE instance_list set expire_at = `start_time` + `duration`; -- backfill existing data
CREATE INDEX `idx_expire_at` ON `instance_list` (`expire_at`);
)",
.content_schema_update = false
},
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
},
ManifestEntry{
.version = 9326,
.description = "2025_07_27_add_indexes_npc_spawns_loot.sql",
.check = "SHOW INDEX FROM npc_types",
.condition = "missing",
.match = "idx_npc_types_loottable_id",
.sql = R"(
ALTER TABLE npc_types
ADD INDEX idx_npc_types_loottable_id (loottable_id);
ALTER TABLE spawnentry
ADD INDEX idx_spawnentry_spawngroup_id (spawngroupID),
ADD INDEX idx_spawnentry_npc_id (npcID);
ALTER TABLE lootdrop_entries
ADD INDEX idx_lootdrop_entries_lootdrop_id (lootdrop_id),
ADD INDEX idx_lootdrop_entries_item_id (item_id);
ALTER TABLE loottable_entries
ADD INDEX idx_loottable_entries_lootdrop_id (lootdrop_id),
ADD INDEX idx_loottable_entries_loottable_id (loottable_id);
)",
.content_schema_update = true
},
ManifestEntry{
.version = 9327,
.description = "2025_08_13_character_stats_record_heal_amount.sql",
.check = "SHOW COLUMNS FROM `character_stats_record` LIKE 'heal_amount'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `character_stats_record`
ADD COLUMN `heal_amount` int(11) NULL DEFAULT 0 AFTER `spell_damage`;
)"
},
ManifestEntry{
.version = 9328,
.description = "2025_08_22_character_parcel_updates.sql",
.check = "SHOW COLUMNS FROM `character_parcels` LIKE 'evolve_amount'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `character_parcels`
ADD COLUMN `evolve_amount` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `quantity`;
ALTER TABLE `character_parcels_containers`
ADD COLUMN `evolve_amount` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `quantity`;
)",
.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
// };
+53 -18
View File
@@ -30,8 +30,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/repositories/respawn_times_repository.h"
#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;
@@ -479,6 +503,10 @@ void Database::DeleteInstance(uint16 instance_id)
DynamicZoneMembersRepository::DeleteByInstance(*this, 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)
@@ -533,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;
@@ -551,16 +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));
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)
@@ -572,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);
}
+4 -3
View File
@@ -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"},
@@ -139,6 +139,7 @@ namespace DatabaseSchema {
"character_pet_buffs",
"character_pet_info",
"character_pet_inventory",
"character_pet_name",
"character_peqzone_flags",
"character_potionbelt",
"character_skills",
@@ -311,10 +312,9 @@ namespace DatabaseSchema {
"completed_shared_task_members",
"completed_shared_tasks",
"discord_webhooks",
"dynamic_zone_lockouts",
"dynamic_zone_members",
"dynamic_zones",
"expedition_lockouts",
"expeditions",
"gm_ips",
"group_id",
"group_leaders",
@@ -351,6 +351,7 @@ namespace DatabaseSchema {
"shared_task_dynamic_zones",
"shared_task_members",
"shared_tasks",
"zone_state_spawns",
};
}
+6 -4
View File
@@ -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()
@@ -302,7 +302,9 @@ std::string DBcore::Escape(const std::string& s)
void DBcore::SetMutex(Mutex *mutex)
{
safe_delete(m_mutex);
if (m_mutex && m_mutex != mutex) {
safe_delete(m_mutex);
}
DBcore::m_mutex = mutex;
}
+1 -1
View File
@@ -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);
-1
View File
@@ -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"
+2 -2
View File
@@ -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);
}
+6
View File
@@ -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{};
+297 -67
View File
@@ -1,11 +1,13 @@
#include "dynamic_zone_base.h"
#include "database.h"
#include "eqemu_logsys.h"
#include "repositories/instance_list_repository.h"
#include "repositories/instance_list_player_repository.h"
#include "rulesys.h"
#include "servertalk.h"
#include "util/uuid.h"
#include "repositories/character_expedition_lockouts_repository.h"
#include "repositories/dynamic_zone_lockouts_repository.h"
#include "repositories/instance_list_repository.h"
#include "repositories/instance_list_player_repository.h"
DynamicZoneBase::DynamicZoneBase(DynamicZonesRepository::DynamicZoneInstance&& entry)
{
@@ -56,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;
}
@@ -93,13 +96,15 @@ void DynamicZoneBase::LoadRepositoryResult(DynamicZonesRepository::DynamicZoneIn
m_zonein.y = dz_entry.zone_in_y;
m_zonein.z = dz_entry.zone_in_z;
m_zonein.heading = dz_entry.zone_in_heading;
m_has_zonein = (dz_entry.has_zone_in != 0);
m_has_zonein = dz_entry.has_zone_in != 0;
m_is_locked = dz_entry.is_locked;
m_add_replay = dz_entry.add_replay;
// instance_list portion
m_zone_id = dz_entry.zone;
m_zone_version = dz_entry.version;
m_start_time = std::chrono::system_clock::from_time_t(dz_entry.start_time);
m_duration = std::chrono::seconds(dz_entry.duration);
m_never_expires = (dz_entry.never_expires != 0);
m_never_expires = dz_entry.never_expires != 0;
m_expire_time = m_start_time + m_duration;
}
@@ -119,37 +124,40 @@ void DynamicZoneBase::AddMemberFromRepositoryResult(
uint32_t DynamicZoneBase::SaveToDatabase()
{
LogDynamicZonesDetail("Saving dz instance [{}] to database", m_instance_id);
if (m_instance_id != 0)
if (m_instance_id == 0)
{
auto insert_dz = DynamicZonesRepository::NewEntity();
insert_dz.uuid = m_uuid;
insert_dz.name = m_name;
insert_dz.leader_id = m_leader.id;
insert_dz.min_players = m_min_players;
insert_dz.max_players = m_max_players;
insert_dz.instance_id = m_instance_id,
insert_dz.type = static_cast<int>(m_type);
insert_dz.dz_switch_id = m_dz_switch_id;
insert_dz.compass_zone_id = m_compass.zone_id;
insert_dz.compass_x = m_compass.x;
insert_dz.compass_y = m_compass.y;
insert_dz.compass_z = m_compass.z;
insert_dz.safe_return_zone_id = m_safereturn.zone_id;
insert_dz.safe_return_x = m_safereturn.x;
insert_dz.safe_return_y = m_safereturn.y;
insert_dz.safe_return_z = m_safereturn.z;
insert_dz.safe_return_heading = m_safereturn.heading;
insert_dz.zone_in_x = m_zonein.x;
insert_dz.zone_in_y = m_zonein.y;
insert_dz.zone_in_z = m_zonein.z;
insert_dz.zone_in_heading = m_zonein.heading;
insert_dz.has_zone_in = m_has_zonein;
auto inserted_dz = DynamicZonesRepository::InsertOne(GetDatabase(), insert_dz);
return inserted_dz.id;
return 0;
}
return 0;
auto dz = DynamicZonesRepository::NewEntity();
dz.uuid = m_uuid;
dz.name = m_name;
dz.leader_id = m_leader.id;
dz.min_players = m_min_players;
dz.max_players = m_max_players;
dz.instance_id = static_cast<int32_t>(m_instance_id),
dz.type = static_cast<uint8_t>(m_type);
dz.dz_switch_id = m_dz_switch_id;
dz.compass_zone_id = m_compass.zone_id;
dz.compass_x = m_compass.x;
dz.compass_y = m_compass.y;
dz.compass_z = m_compass.z;
dz.safe_return_zone_id = m_safereturn.zone_id;
dz.safe_return_x = m_safereturn.x;
dz.safe_return_y = m_safereturn.y;
dz.safe_return_z = m_safereturn.z;
dz.safe_return_heading = m_safereturn.heading;
dz.zone_in_x = m_zonein.x;
dz.zone_in_y = m_zonein.y;
dz.zone_in_z = m_zonein.z;
dz.zone_in_heading = m_zonein.heading;
dz.has_zone_in = static_cast<uint8_t>(m_has_zonein);
dz.is_locked = static_cast<int8_t>(m_is_locked);
dz.add_replay = static_cast<int8_t>(m_add_replay);
dz = DynamicZonesRepository::InsertOne(GetDatabase(), std::move(dz));
return dz.id;
}
bool DynamicZoneBase::AddMember(const DynamicZoneMember& add_member)
@@ -196,10 +204,9 @@ bool DynamicZoneBase::RemoveMember(const DynamicZoneMember& remove_member)
return true;
}
bool DynamicZoneBase::SwapMember(
const DynamicZoneMember& add_member, const std::string& remove_char_name)
bool DynamicZoneBase::SwapMember(const DynamicZoneMember& add_member, const std::string& remove_name)
{
auto remove_member = GetMemberData(remove_char_name);
auto remove_member = GetMemberData(remove_name);
if (!add_member.IsValid() || !remove_member.IsValid())
{
return false;
@@ -230,9 +237,18 @@ void DynamicZoneBase::RemoveAllMembers()
void DynamicZoneBase::SaveMembers(const std::vector<DynamicZoneMember>& members)
{
if (members.empty())
{
return;
}
LogDynamicZonesDetail("Saving [{}] member(s) for dz [{}]", members.size(), m_id);
m_members = members;
if (m_members.size() > m_max_players)
{
m_members.resize(m_max_players);
}
// the lower level instance_list_players needs to be kept updated as well
std::vector<DynamicZoneMembersRepository::DynamicZoneMembers> insert_members;
@@ -242,12 +258,12 @@ void DynamicZoneBase::SaveMembers(const std::vector<DynamicZoneMember>& members)
DynamicZoneMembersRepository::DynamicZoneMembers member_entry{};
member_entry.dynamic_zone_id = m_id;
member_entry.character_id = member.id;
insert_members.emplace_back(member_entry);
insert_members.push_back(member_entry);
InstanceListPlayerRepository::InstanceListPlayer player_entry;
player_entry.id = static_cast<int>(m_instance_id);
player_entry.charid = static_cast<int>(member.id);
insert_players.emplace_back(player_entry);
InstanceListPlayerRepository::InstanceListPlayer player_entry{};
player_entry.id = m_instance_id;
player_entry.charid = member.id;
insert_players.push_back(player_entry);
}
DynamicZoneMembersRepository::InsertOrUpdateMany(GetDatabase(), insert_members);
@@ -339,6 +355,44 @@ void DynamicZoneBase::SetLeader(const DynamicZoneMember& new_leader, bool update
}
}
void DynamicZoneBase::SetLocked(bool lock, bool update_db, DzLockMsg lock_msg, uint32_t color)
{
m_is_locked = lock;
if (update_db)
{
DynamicZonesRepository::UpdateLocked(GetDatabase(), m_id, lock);
ServerPacket pack(ServerOP_DzLock, sizeof(ServerDzLock_Struct));
auto buf = reinterpret_cast<ServerDzLock_Struct*>(pack.pBuffer);
buf->dz_id = GetID();
buf->sender_zone_id = GetCurrentZoneID();
buf->sender_instance_id = GetCurrentInstanceID();
buf->lock = m_is_locked;
buf->lock_msg = static_cast<uint8_t>(lock_msg);
buf->color = color;
SendServerPacket(&pack);
}
}
void DynamicZoneBase::SetReplayOnJoin(bool enabled, bool update_db)
{
m_add_replay = enabled;
if (update_db)
{
DynamicZonesRepository::UpdateReplayOnJoin(GetDatabase(), m_id, enabled);
ServerPacket pack(ServerOP_DzReplayOnJoin, sizeof(ServerDzBool_Struct));
auto buf = reinterpret_cast<ServerDzBool_Struct*>(pack.pBuffer);
buf->dz_id = GetID();
buf->sender_zone_id = GetCurrentZoneID();
buf->sender_instance_id = GetCurrentInstanceID();
buf->enabled = enabled;
SendServerPacket(&pack);
}
}
uint32_t DynamicZoneBase::GetSecondsRemaining() const
{
auto remaining = std::chrono::duration_cast<std::chrono::seconds>(GetDurationRemaining()).count();
@@ -478,13 +532,13 @@ void DynamicZoneBase::RemoveInternalMember(uint32_t character_id)
), m_members.end());
}
bool DynamicZoneBase::HasMember(uint32_t character_id)
bool DynamicZoneBase::HasMember(uint32_t character_id) const
{
return std::any_of(m_members.begin(), m_members.end(),
[&](const DynamicZoneMember& member) { return member.id == character_id; });
}
bool DynamicZoneBase::HasMember(const std::string& character_name)
bool DynamicZoneBase::HasMember(const std::string& character_name) const
{
return std::any_of(m_members.begin(), m_members.end(),
[&](const DynamicZoneMember& member) {
@@ -590,35 +644,34 @@ std::string DynamicZoneBase::GetDynamicZoneTypeName(DynamicZoneType dz_type)
}
}
EQ::Net::DynamicPacket DynamicZoneBase::GetSerializedDzPacket()
std::unique_ptr<ServerPacket> DynamicZoneBase::CreateServerPacket(uint16_t zone_id, uint16_t instance_id)
{
EQ::Net::DynamicPacket dyn_pack;
dyn_pack.PutSerialize(0, *this);
std::ostringstream ss = GetSerialized();
std::string_view sv = ss.view();
LogDynamicZonesDetail("Serialized server dz size [{}]", dyn_pack.Length());
return dyn_pack;
}
std::unique_ptr<ServerPacket> DynamicZoneBase::CreateServerDzCreatePacket(
uint16_t origin_zone_id, uint16_t origin_instance_id)
{
EQ::Net::DynamicPacket dyn_pack = GetSerializedDzPacket();
auto pack_size = sizeof(ServerDzCreateSerialized_Struct) + dyn_pack.Length();
auto pack_size = sizeof(ServerDzCreate_Struct) + sv.size();
auto pack = std::make_unique<ServerPacket>(ServerOP_DzCreated, static_cast<uint32_t>(pack_size));
auto buf = reinterpret_cast<ServerDzCreateSerialized_Struct*>(pack->pBuffer);
buf->origin_zone_id = origin_zone_id;
buf->origin_instance_id = origin_instance_id;
buf->cereal_size = static_cast<uint32_t>(dyn_pack.Length());
memcpy(buf->cereal_data, dyn_pack.Data(), dyn_pack.Length());
auto buf = reinterpret_cast<ServerDzCreate_Struct*>(pack->pBuffer);
buf->origin_zone_id = zone_id;
buf->origin_instance_id = instance_id;
buf->dz_id = GetID();
buf->cereal_size = static_cast<uint32_t>(sv.size());
memcpy(buf->cereal_data, sv.data(), sv.size());
return pack;
}
void DynamicZoneBase::LoadSerializedDzPacket(char* cereal_data, uint32_t cereal_size)
std::ostringstream DynamicZoneBase::GetSerialized()
{
LogDynamicZonesDetail("Deserializing server dz size [{}]", cereal_size);
EQ::Util::MemoryStreamReader ss(cereal_data, cereal_size);
std::ostringstream ss;
cereal::BinaryOutputArchive archive(ss);
archive(*this);
return ss;
}
void DynamicZoneBase::Unserialize(std::span<char> buf)
{
EQ::Util::MemoryStreamReader ss(buf.data(), buf.size());
cereal::BinaryInputArchive archive(ss);
archive(*this);
}
@@ -647,3 +700,180 @@ void DynamicZoneBase::LoadTemplate(const DynamicZoneTemplatesRepository::Dynamic
m_zonein.z = dz_template.zone_in_z;
m_zonein.heading = dz_template.zone_in_h;
}
std::vector<uint32_t> DynamicZoneBase::GetMemberIds()
{
std::vector<uint32_t> ids;
ids.reserve(m_members.size());
for (const auto& member : m_members)
{
ids.push_back(member.id);
}
return ids;
}
bool DynamicZoneBase::HasLockout(const std::string& event)
{
return std::ranges::any_of(m_lockouts, [&](const auto& l) { return l.IsEvent(event); });
}
bool DynamicZoneBase::HasReplayLockout()
{
return HasLockout(DzLockout::ReplayTimer);
}
void DynamicZoneBase::AddLockout(const std::string& event, uint32_t seconds)
{
auto lockout = DzLockout::Create(m_name, event, seconds, m_uuid);
AddLockout(lockout);
}
void DynamicZoneBase::AddLockout(const DzLockout& lockout, bool members_only)
{
if (!members_only)
{
DynamicZoneLockoutsRepository::InsertLockouts(GetDatabase(), GetID(), { lockout });
}
CharacterExpeditionLockoutsRepository::InsertLockout(GetDatabase(), GetMemberIds(), lockout);
HandleLockoutUpdate(lockout, false, members_only);
SendServerPacket(CreateLockoutPacket(lockout, false, members_only).get());
}
void DynamicZoneBase::AddLockoutDuration(const std::string& event, int seconds, bool members_only)
{
auto lockout = DzLockout::Create(m_name, event, std::max(0, seconds), m_uuid);
// lockout has unsigned duration, pass original seconds to support reducing existing timers
int secs = static_cast<int>(seconds * RuleR(Expedition, LockoutDurationMultiplier));
CharacterExpeditionLockoutsRepository::AddLockoutDuration(GetDatabase(), GetMemberIds(), lockout, secs);
HandleLockoutDuration(lockout, seconds, members_only, true);
SendServerPacket(CreateLockoutDurationPacket(lockout, seconds, members_only).get());
}
void DynamicZoneBase::UpdateLockoutDuration(const std::string& event, uint32_t seconds, bool members_only)
{
// some live expeditions update existing lockout timers during progression
auto it = std::ranges::find_if(m_lockouts, [&](const auto& l) { return l.IsEvent(event); });
if (it != m_lockouts.end())
{
seconds = static_cast<uint32_t>(seconds * RuleR(Expedition, LockoutDurationMultiplier));
DzLockout lockout(m_uuid, m_name, event, it->GetStartTime() + seconds, seconds);
AddLockout(lockout, members_only);
}
}
void DynamicZoneBase::RemoveLockout(const std::string& event)
{
DynamicZoneLockoutsRepository::DeleteWhere(GetDatabase(), fmt::format(
"dynamic_zone_id = {} AND event_name = '{}'", GetID(), Strings::Escape(event)));
CharacterExpeditionLockoutsRepository::DeleteWhere(GetDatabase(), fmt::format(
"character_id IN ({}) AND expedition_name = '{}' AND event_name = '{}'",
fmt::join(GetMemberIds(), ","), Strings::Escape(m_name), Strings::Escape(event)));
DzLockout lockout{m_uuid, m_name, event, 0, 0};
HandleLockoutUpdate(lockout, true, false);
SendServerPacket(CreateLockoutPacket(lockout, true).get());
}
void DynamicZoneBase::HandleLockoutUpdate(const DzLockout& lockout, bool remove, bool members_only)
{
if (!members_only)
{
std::erase_if(m_lockouts, [&](const auto& l) { return l.IsEvent(lockout.Event()); });
if (!remove)
{
m_lockouts.push_back(lockout);
}
}
}
void DynamicZoneBase::HandleLockoutDuration(const DzLockout& lockout, int seconds, bool members_only, bool insert_db)
{
if (!members_only)
{
auto it = std::ranges::find_if(m_lockouts, [&](const auto& l) { return l.IsEvent(lockout.Event()); });
if (it != m_lockouts.end())
{
it->AddLockoutTime(seconds);
}
else
{
it = m_lockouts.insert(m_lockouts.end(), lockout);
}
if (insert_db)
{
DynamicZoneLockoutsRepository::InsertLockouts(GetDatabase(), GetID(), { *it });
}
}
}
std::unique_ptr<ServerPacket> DynamicZoneBase::CreateLockoutPacket(const DzLockout& lockout, bool remove, bool members_only) const
{
uint32_t pack_size = sizeof(ServerDzLockout_Struct);
auto pack = std::make_unique<ServerPacket>(ServerOP_DzLockout, pack_size);
auto buf = reinterpret_cast<ServerDzLockout_Struct*>(pack->pBuffer);
buf->dz_id = GetID();
buf->expire_time = lockout.GetExpireTime();
buf->duration = lockout.GetDuration();
buf->sender_zone_id = GetCurrentZoneID();
buf->sender_instance_id = GetCurrentInstanceID();
buf->remove = remove;
buf->members_only = members_only;
strn0cpy(buf->event_name, lockout.Event().c_str(), sizeof(buf->event_name));
return pack;
}
std::unique_ptr<ServerPacket> DynamicZoneBase::CreateLockoutDurationPacket(const DzLockout& lockout, int seconds, bool members_only) const
{
uint32_t pack_size = sizeof(ServerDzLockout_Struct);
auto pack = std::make_unique<ServerPacket>(ServerOP_DzLockoutDuration, pack_size);
auto buf = reinterpret_cast<ServerDzLockout_Struct*>(pack->pBuffer);
buf->dz_id = GetID();
buf->expire_time = lockout.GetExpireTime();
buf->duration = lockout.GetDuration();
buf->sender_zone_id = GetCurrentZoneID();
buf->sender_instance_id = GetCurrentInstanceID();
buf->members_only = members_only;
buf->seconds = seconds;
strn0cpy(buf->event_name, lockout.Event().c_str(), sizeof(buf->event_name));
return pack;
}
void DynamicZoneBase::SyncCharacterLockouts(uint32_t char_id, std::vector<DzLockout>& lockouts)
{
// adds missing event lockouts to client for this expedition and updates
// client timers that are both shorter and from another expedition
bool modified = false;
for (const auto& lockout : m_lockouts)
{
if (lockout.IsReplay() || lockout.IsExpired() || lockout.UUID() != m_uuid)
{
continue;
}
auto it = std::find_if(lockouts.begin(), lockouts.end(), [&](const DzLockout& l) { return l.IsSame(lockout); });
if (it == lockouts.end())
{
modified = true;
lockouts.push_back(lockout); // insert missing
}
else if (it->GetSecondsRemaining() < lockout.GetSecondsRemaining() && it->UUID() != m_uuid)
{
// only update lockout timer not uuid so loot event apis still work
modified = true;
it->SetDuration(lockout.GetDuration());
it->SetExpireTime(lockout.GetExpireTime());
}
}
if (modified)
{
CharacterExpeditionLockoutsRepository::InsertLockouts(GetDatabase(), char_id, lockouts);
}
}
+68 -14
View File
@@ -1,8 +1,8 @@
#ifndef COMMON_DYNAMIC_ZONE_BASE_H
#define COMMON_DYNAMIC_ZONE_BASE_H
#include "dynamic_zone_lockout.h"
#include "eq_constants.h"
#include "net/packet.h"
#include "repositories/dynamic_zones_repository.h"
#include "repositories/dynamic_zone_members_repository.h"
#include "repositories/dynamic_zone_templates_repository.h"
@@ -10,12 +10,40 @@
#include <chrono>
#include <cstdint>
#include <memory>
#include <span>
#include <string>
#include <vector>
class Database;
class ServerPacket;
// message string 8312 added in September 08 2020 Test patch (used by both dz and shared tasks)
inline constexpr char DzNotAllAdded[] = "Not all players in your {0} were added to the {1}. The {1} can take a maximum of {2} players, and your {0} has {3}.";
enum class DzLockMsg : uint8_t
{
None = 0, Close, Begin
};
enum class DynamicZoneType
{
None = 0,
Expedition,
Tutorial,
Task,
Mission, // Shared Task
Quest
};
enum class DynamicZoneMemberStatus
{
Unknown = 0,
Online,
Offline,
InDynamicZone,
LinkDead
};
struct DynamicZoneMember
{
uint32_t id = 0;
@@ -93,6 +121,7 @@ public:
const std::string& GetName() const { return m_name; }
const std::string& GetUUID() const { return m_uuid; }
const DynamicZoneMember& GetLeader() const { return m_leader; }
const std::vector<DzLockout>& GetLockouts() const { return m_lockouts; }
const std::vector<DynamicZoneMember>& GetMembers() const { return m_members; }
const DynamicZoneLocation& GetCompassLocation() const { return m_compass; }
const DynamicZoneLocation& GetSafeReturnLocation() const { return m_safereturn; }
@@ -104,31 +133,34 @@ public:
uint32_t GetDatabaseMemberCount();
DynamicZoneMember GetMemberData(uint32_t character_id);
DynamicZoneMember GetMemberData(const std::string& character_name);
EQ::Net::DynamicPacket GetSerializedDzPacket();
std::vector<uint32_t> GetMemberIds();
std::ostringstream GetSerialized();
bool HasDatabaseMember(uint32_t character_id);
bool HasMember(uint32_t character_id);
bool HasMember(const std::string& character_name);
bool HasMember(uint32_t character_id) const;
bool HasMember(const std::string& character_name) const;
bool HasMembers() const { return !m_members.empty(); }
bool HasZoneInLocation() const { return m_has_zonein; }
bool IsExpedition() const { return m_type == DynamicZoneType::Expedition; }
bool IsExpired() const { return m_expire_time < std::chrono::system_clock::now(); }
bool IsInstanceID(uint32_t instance_id) const { return (m_instance_id != 0 && m_instance_id == instance_id); }
bool IsLocked() const { return m_is_locked; }
bool IsValid() const { return m_instance_id != 0; }
bool IsSameDz(uint32_t zone_id, uint32_t instance_id) const { return zone_id == m_zone_id && instance_id == m_instance_id; }
void LoadSerializedDzPacket(char* cereal_data, uint32_t cereal_size);
void LoadTemplate(const DynamicZoneTemplatesRepository::DynamicZoneTemplates& dz_template);
void RemoveAllMembers();
bool RemoveMember(uint32_t character_id);
bool RemoveMember(const std::string& character_name);
bool RemoveMember(const DynamicZoneMember& remove_member);
void SaveMembers(const std::vector<DynamicZoneMember>& members);
void SetCompass(const DynamicZoneLocation& location, bool update_db = false);
void SetCompass(uint32_t zone_id, float x, float y, float z, bool update_db = false);
void SetDuration(uint32_t seconds) { m_duration = std::chrono::seconds(seconds); }
void SetLeader(const DynamicZoneMember& leader, bool update_db = false);
void SetLocked(bool lock, bool update_db = false, DzLockMsg lock_msg = DzLockMsg::None, uint32_t color = Chat::Yellow);
void SetMaxPlayers(uint32_t max_players) { m_max_players = max_players; }
void SetMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status);
void SetMinPlayers(uint32_t min_players) { m_min_players = min_players; }
void SetName(const std::string& name) { m_name = name; }
void SetReplayOnJoin(bool enabled, bool update_db = false);
void SetSafeReturn(const DynamicZoneLocation& location, bool update_db = false);
void SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db = false);
void SetSwitchID(int dz_switch_id, bool update_db = false);
@@ -136,34 +168,48 @@ public:
void SetUUID(std::string uuid) { m_uuid = std::move(uuid); }
void SetZoneInLocation(const DynamicZoneLocation& location, bool update_db = false);
void SetZoneInLocation(float x, float y, float z, float heading, bool update_db = false);
bool SwapMember(const DynamicZoneMember& add_member, const std::string& remove_char_name);
bool SwapMember(const DynamicZoneMember& add_member, const std::string& remove_name);
void AddLockout(const std::string& event, uint32_t seconds);
void AddLockoutDuration(const std::string& event, int seconds, bool members_only = true);
bool HasLockout(const std::string& event);
bool HasReplayLockout();
void RemoveLockout(const std::string& event);
void SyncCharacterLockouts(uint32_t char_id, std::vector<DzLockout>& lockouts);
void UpdateLockoutDuration(const std::string& event, uint32_t seconds, bool members_only = true);
protected:
virtual uint16_t GetCurrentInstanceID() { return 0; }
virtual uint16_t GetCurrentZoneID() { return 0; }
virtual uint16_t GetCurrentInstanceID() const { return 0; }
virtual uint16_t GetCurrentZoneID() const { return 0; }
virtual Database& GetDatabase() = 0;
virtual void HandleLockoutDuration(const DzLockout& lockout, int seconds, bool members_only, bool insert_db);
virtual void HandleLockoutUpdate(const DzLockout& lockout, bool remove, bool members_only);
virtual void ProcessCompassChange(const DynamicZoneLocation& location) { m_compass = location; }
virtual void ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed);
virtual bool ProcessMemberStatusChange(uint32_t member_id, DynamicZoneMemberStatus status);
virtual void ProcessRemoveAllMembers(bool silent = false) { m_members.clear(); }
virtual bool ProcessMemberStatusChange(uint32_t character_id, DynamicZoneMemberStatus status);
virtual void ProcessRemoveAllMembers() { m_members.clear(); }
virtual void ProcessSetSwitchID(int dz_switch_id) { m_dz_switch_id = dz_switch_id; }
virtual bool SendServerPacket(ServerPacket* packet) = 0;
void AddLockout(const DzLockout& lockout, bool members_only = false);
void AddInternalMember(const DynamicZoneMember& member);
uint32_t Create();
uint32_t CreateInstance();
void LoadRepositoryResult(DynamicZonesRepository::DynamicZoneInstance&& dz_entry);
void RemoveInternalMember(uint32_t character_id);
void SaveMembers(const std::vector<DynamicZoneMember>& members);
uint32_t SaveToDatabase();
bool SetInternalMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status);
std::unique_ptr<ServerPacket> CreateServerDzCreatePacket(uint16_t origin_zone_id, uint16_t origin_instance_id);
std::unique_ptr<ServerPacket> CreateServerPacket(uint16_t zone_id, uint16_t instance_id);
std::unique_ptr<ServerPacket> CreateServerDzLocationPacket(uint16_t server_opcode, const DynamicZoneLocation& location);
std::unique_ptr<ServerPacket> CreateServerDzSwitchIDPacket();
std::unique_ptr<ServerPacket> CreateServerMemberAddRemovePacket(const DynamicZoneMember& member, bool removed);
std::unique_ptr<ServerPacket> CreateServerMemberStatusPacket(uint32_t character_id, DynamicZoneMemberStatus status);
std::unique_ptr<ServerPacket> CreateServerMemberSwapPacket(const DynamicZoneMember& remove_member, const DynamicZoneMember& add_member);
std::unique_ptr<ServerPacket> CreateServerRemoveAllMembersPacket();
std::unique_ptr<ServerPacket> CreateLockoutPacket(const DzLockout& lockout, bool remove, bool members_only = false) const;
std::unique_ptr<ServerPacket> CreateLockoutDurationPacket(const DzLockout& lockout, int seconds, bool members_only = false) const;
uint32_t m_id = 0;
uint32_t m_zone_id = 0;
@@ -175,6 +221,8 @@ protected:
bool m_never_expires = false;
bool m_has_zonein = false;
bool m_has_member_statuses = false;
bool m_is_locked = false;
bool m_add_replay = true;
std::string m_name;
std::string m_uuid;
DynamicZoneMember m_leader;
@@ -182,12 +230,15 @@ protected:
DynamicZoneLocation m_compass;
DynamicZoneLocation m_safereturn;
DynamicZoneLocation m_zonein;
std::chrono::seconds m_duration;
std::chrono::seconds m_duration = {};
std::chrono::time_point<std::chrono::system_clock> m_start_time;
std::chrono::time_point<std::chrono::system_clock> m_expire_time;
std::vector<DynamicZoneMember> m_members;
std::vector<DzLockout> m_lockouts;
public:
void Unserialize(std::span<char> buf);
template<class Archive>
void serialize(Archive& archive)
{
@@ -202,6 +253,8 @@ public:
m_never_expires,
m_has_zonein,
m_has_member_statuses,
m_is_locked,
m_add_replay,
m_name,
m_uuid,
m_leader,
@@ -212,7 +265,8 @@ public:
m_duration,
m_start_time,
m_expire_time,
m_members
m_members,
m_lockouts
);
}
};
+92
View File
@@ -0,0 +1,92 @@
#include "dynamic_zone_lockout.h"
#include "strings.h"
#include "rulesys.h"
#include "util/uuid.h"
#include <fmt/format.h>
#include <cereal/types/chrono.hpp>
DzLockout::DzLockout(std::string uuid, std::string expedition, std::string event, uint64_t expire_time, uint32_t duration)
: m_uuid(std::move(uuid))
, m_name(std::move(expedition))
, m_event(std::move(event))
, m_expire_time(std::chrono::system_clock::from_time_t(expire_time))
, m_duration(duration)
{
m_is_replay = m_event == ReplayTimer;
}
DzLockout::DzLockout(std::string_view name, BaseDynamicZoneLockoutsRepository::DynamicZoneLockouts&& lockout)
: m_uuid(std::move(lockout.from_expedition_uuid))
, m_name(name)
, m_event(std::move(lockout.event_name))
, m_expire_time(std::chrono::system_clock::from_time_t(lockout.expire_time))
, m_duration(lockout.duration)
{
m_is_replay = m_event == ReplayTimer;
}
DzLockout DzLockout::Create(const std::string& expedition, const std::string& event, uint32_t seconds, std::string uuid)
{
seconds = static_cast<uint32_t>(seconds * RuleR(Expedition, LockoutDurationMultiplier));
if (uuid.empty())
{
uuid = EQ::Util::UUID::Generate().ToString();
}
DzLockout lockout{uuid, expedition, event, 0, seconds};
lockout.Reset(); // sets expire time
return lockout;
}
uint32_t DzLockout::GetSecondsRemaining() const
{
auto now = std::chrono::system_clock::now();
if (m_expire_time > now)
{
auto remaining = m_expire_time - now;
return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::seconds>(remaining).count());
}
return 0;
}
DzLockout::TimeStrings DzLockout::GetTimeRemainingStrs() const
{
auto seconds = GetSecondsRemaining();
return DzLockout::TimeStrings{
fmt::format_int(seconds / 86400).str(), // days
fmt::format_int(seconds / 3600 % 24).str(), // hours
fmt::format_int(seconds / 60 % 60).str(), // minutes
fmt::format_int(seconds % 60).str() // seconds
};
}
bool DzLockout::IsSame(const DzLockout& other) const
{
return other.IsSame(m_name, m_event);
}
bool DzLockout::IsSame(const std::string& expedition, const std::string& event) const
{
return m_name == expedition && m_event == event;
}
void DzLockout::AddLockoutTime(int seconds)
{
seconds = static_cast<int>(seconds * RuleR(Expedition, LockoutDurationMultiplier));
auto new_duration = std::max(0, static_cast<int>(m_duration.count()) + seconds);
auto start_time = m_expire_time - m_duration;
m_duration = std::chrono::seconds(new_duration);
m_expire_time = start_time + m_duration;
}
template <typename T>
void DzLockout::serialize(T& archive)
{
archive(m_is_replay, m_uuid, m_name, m_event, m_duration, m_expire_time);
}
template void DzLockout::serialize(cereal::BinaryOutputArchive&);
template void DzLockout::serialize(cereal::BinaryInputArchive&);
+56
View File
@@ -0,0 +1,56 @@
#pragma once
#include <chrono>
#include <string>
#include "repositories/base/base_dynamic_zone_lockouts_repository.h"
class DzLockout
{
public:
DzLockout() = default;
DzLockout(std::string uuid, std::string expedition, std::string event, uint64_t expire_time, uint32_t duration);
DzLockout(std::string_view name, BaseDynamicZoneLockoutsRepository::DynamicZoneLockouts&& lockout);
static constexpr char ReplayTimer[] = "Replay Timer";
static DzLockout Create(const std::string& expedition, const std::string& event, uint32_t seconds, std::string uuid = {});
struct TimeStrings
{
std::string days;
std::string hours;
std::string mins;
std::string secs;
};
void AddLockoutTime(int seconds);
uint32_t GetDuration() const { return static_cast<uint32_t>(m_duration.count()); }
uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); }
uint64_t GetStartTime() const { return std::chrono::system_clock::to_time_t(m_expire_time - m_duration); }
uint32_t GetSecondsRemaining() const;
TimeStrings GetTimeRemainingStrs() const;
const std::string& DzName() const { return m_name; }
const std::string& Event() const { return m_event; }
const std::string& UUID() const { return m_uuid; }
bool IsEvent(std::string_view event) const { return m_event == event; }
bool IsExpired() const { return GetSecondsRemaining() == 0; }
bool IsReplay() const { return m_is_replay; }
bool IsSame(const DzLockout& other) const;
bool IsSame(const std::string& expedition, const std::string& event) const;
bool IsUUID(const std::string& uuid) const { return uuid == m_uuid; }
void Reset() { m_expire_time = std::chrono::system_clock::now() + m_duration; }
void SetDuration(uint32_t seconds) { m_duration = std::chrono::seconds(seconds); }
void SetExpireTime(uint64_t expire_time) { m_expire_time = std::chrono::system_clock::from_time_t(expire_time); }
void SetUUID(const std::string& uuid) { m_uuid = uuid; }
template <typename T>
void serialize(T& archive);
private:
bool m_is_replay = false;
std::string m_uuid; // dz received in
std::string m_name;
std::string m_event;
std::chrono::seconds m_duration = {};
std::chrono::time_point<std::chrono::system_clock> m_expire_time;
};
+20
View File
@@ -451,3 +451,23 @@ bool LDoNTheme::IsValid(uint32 theme_id)
{
return ldon_theme_names.find(theme_id) != ldon_theme_names.end();
}
std::string PetCommand::GetName(uint8 pet_command)
{
return IsValid(pet_command) ? pet_commands[pet_command] : "UNKNOWN PET COMMAND";
}
bool PetCommand::IsValid(uint8 pet_command)
{
return pet_commands.find(pet_command) != pet_commands.end();
}
std::string PetType::GetName(uint8 pet_type)
{
return IsValid(pet_type) ? pet_types[pet_type] : "UNKNOWN PET TYPE";
}
bool PetType::IsValid(uint8 pet_type)
{
return pet_types.find(pet_type) != pet_types.end();
}
+129
View File
@@ -130,6 +130,8 @@ namespace EQ
using RoF2::invtype::MAIL_SIZE;
using RoF2::invtype::GUILD_TROPHY_TRIBUTE_SIZE;
using RoF2::invtype::KRONO_SIZE;
using RoF2::invtype::GUILD_BANK_MAIN_SIZE;
using RoF2::invtype::GUILD_BANK_DEPOSIT_SIZE;
using RoF2::invtype::OTHER_SIZE;
using RoF2::invtype::TRADE_NPC_SIZE;
@@ -790,4 +792,131 @@ namespace BookType {
constexpr uint8 ItemInfo = 2;
}
namespace PetButton {
constexpr uint8 Sit = 0;
constexpr uint8 Stop = 1;
constexpr uint8 Regroup = 2;
constexpr uint8 Follow = 3;
constexpr uint8 Guard = 4;
constexpr uint8 Taunt = 5;
constexpr uint8 Hold = 6;
constexpr uint8 GreaterHold = 7;
constexpr uint8 Focus = 8;
constexpr uint8 SpellHold = 9;
}
namespace PetButtonState {
constexpr uint8 Off = 0;
constexpr uint8 On = 1;
}
namespace PetCommand {
constexpr uint8 HealthReport = 0; // /pet health or Pet Window
constexpr uint8 Leader = 1; // /pet leader or Pet Window
constexpr uint8 Attack = 2; // /pet attack or Pet Window
constexpr uint8 QAttack = 3; // /pet qattack or Pet Window
constexpr uint8 FollowMe = 4; // /pet follow or Pet Window
constexpr uint8 GuardHere = 5; // /pet guard or Pet Window
constexpr uint8 Sit = 6; // /pet sit or Pet Window
constexpr uint8 SitDown = 7; // /pet sit on
constexpr uint8 StandUp = 8; // /pet sit off
constexpr uint8 Stop = 9; // /pet stop or Pet Window - Not implemented
constexpr uint8 StopOn = 10; // /pet stop on - Not implemented
constexpr uint8 StopOff = 11; // /pet stop off - Not implemented
constexpr uint8 Taunt = 12; // /pet taunt or Pet Window
constexpr uint8 TauntOn = 13; // /pet taunt on
constexpr uint8 TauntOff = 14; // /pet taunt off
constexpr uint8 Hold = 15; // /pet hold or Pet Window, won't add to hate list unless attacking
constexpr uint8 HoldOn = 16; // /pet hold on
constexpr uint8 HoldOff = 17; // /pet hold off
constexpr uint8 GreaterHold = 18; // /pet ghold, will never add to hate list unless told to
constexpr uint8 GreaterHoldOn = 19; // /pet ghold on
constexpr uint8 GreaterHoldOff = 20; // /pet ghold off
constexpr uint8 SpellHold = 21; // /pet no cast or /pet spellhold or Pet Window
constexpr uint8 SpellHoldOn = 22; // /pet spellhold on
constexpr uint8 SpellHoldOff = 23; // /pet spellhold off
constexpr uint8 Focus = 24; // /pet focus or Pet Window
constexpr uint8 FocusOn = 25; // /pet focus on
constexpr uint8 FocusOff = 26; // /pet focus off
constexpr uint8 Feign = 27; // /pet feign
constexpr uint8 BackOff = 28; // /pet back off
constexpr uint8 GetLost = 29; // /pet get lost
constexpr uint8 GuardMe = 30; // Same as /pet follow, but different message in older clients
constexpr uint8 Regroup = 31; // /pet regroup, acts like classic hold
constexpr uint8 RegroupOn = 32; // /pet regroup on
constexpr uint8 RegroupOff = 33; // /pet regroup off
constexpr uint8 Max = 34;
static std::map<uint8, std::string> pet_commands = {
{ PetCommand::HealthReport, "Health Report" },
{ PetCommand::Leader, "Leader" },
{ PetCommand::Attack, "Attack" },
{ PetCommand::QAttack, "QAttack" },
{ PetCommand::FollowMe, "Follow Me" },
{ PetCommand::GuardHere, "Guard Here" },
{ PetCommand::Sit, "Sit" },
{ PetCommand::SitDown, "Sit Down" },
{ PetCommand::StandUp, "Stand Up" },
{ PetCommand::Stop, "Stop" },
{ PetCommand::StopOn, "Stop On" },
{ PetCommand::StopOff, "Stop Off" },
{ PetCommand::Taunt, "Taunt" },
{ PetCommand::TauntOn, "Taunt On" },
{ PetCommand::TauntOff, "Taunt Off" },
{ PetCommand::Hold, "Hold" },
{ PetCommand::HoldOn, "Hold On" },
{ PetCommand::HoldOff, "Hold Off" },
{ PetCommand::GreaterHold, "Greater Hold" },
{ PetCommand::GreaterHoldOn, "Greater Hold On" },
{ PetCommand::GreaterHoldOff, "Greater Hold Off" },
{ PetCommand::SpellHold, "Spell Hold" },
{ PetCommand::SpellHoldOn, "Spell Hold On" },
{ PetCommand::SpellHoldOff, "Spell Hold Off" },
{ PetCommand::Focus, "Focus" },
{ PetCommand::FocusOn, "Focus On" },
{ PetCommand::FocusOff, "Focus Off" },
{ PetCommand::Feign, "Feign" },
{ PetCommand::BackOff, "Back Off" },
{ PetCommand::GetLost, "Get Lost" },
{ PetCommand::GuardMe, "Guard Me" },
{ PetCommand::Regroup, "Regroup" },
{ PetCommand::RegroupOn, "Regroup On" },
{ PetCommand::RegroupOff, "Regroup Off" },
{ PetCommand::Max, "Max" }
};
std::string GetName(uint8 pet_command);
bool IsValid(uint8 pet_command);
}
namespace PetOrder {
constexpr uint8 Follow = 0;
constexpr uint8 Sit = 1;
constexpr uint8 Guard = 2;
constexpr uint8 Feign = 3;
}
namespace PetType {
constexpr uint8 Familiar = 0;
constexpr uint8 Animation = 1;
constexpr uint8 Normal = 2;
constexpr uint8 Charmed = 3;
constexpr uint8 Follow = 4;
constexpr uint8 TargetLock = 5;
constexpr uint8 None = 255;
static std::map<uint8, std::string> pet_types = {
{ PetType::Familiar, "Familiar" },
{ PetType::Animation, "Animation" },
{ PetType::Normal, "Normal" },
{ PetType::Charmed, "Charmed" },
{ PetType::Follow, "Follow" },
{ PetType::TargetLock, "Target Lock" },
{ PetType::None, "None" }
};
std::string GetName(uint8 pet_type);
bool IsValid(uint8 pet_type);
}
#endif /*COMMON_EMU_CONSTANTS_H*/
+2 -1
View File
@@ -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),
@@ -473,7 +475,6 @@ N(OP_SendAATable),
N(OP_SendCharInfo),
N(OP_SendExpZonein),
N(OP_SendFindableNPCs),
N(OP_SendFindableLocations),
N(OP_SendGuildTributes),
N(OP_SendLoginInfo),
N(OP_SendMaxCharacters),
+2 -20
View File
@@ -974,25 +974,6 @@ namespace ZoneBlockedSpellTypes {
const uint8 Region = 2;
};
enum class DynamicZoneType
{
None = 0,
Expedition,
Tutorial,
Task,
Mission, // Shared Task
Quest
};
enum class DynamicZoneMemberStatus : uint8_t
{
Unknown = 0,
Online,
Offline,
InDynamicZone,
LinkDead
};
enum StartZoneIndex {
Odus = 0,
Qeynos,
@@ -1007,7 +988,8 @@ enum StartZoneIndex {
Felwithe,
Akanon,
Cabilis,
SharVahl
SharVahl,
RatheMtn
};
enum FVNoDropFlagRule
+22 -22
View File
@@ -173,7 +173,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
ClientUnknown::INULL, ClientUnknown::INULL, ClientUnknown::INULL,
ClientUnknown::INULL, ClientUnknown::INULL, ClientUnknown::INULL,
ClientUnknown::INULL, ClientUnknown::INULL, ClientUnknown::INULL,
ClientUnknown::INULL
ClientUnknown::INULL, ClientUnknown::INULL, ClientUnknown::INULL
),
ClientUnknown::INULL,
@@ -200,7 +200,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
Client62::INULL, Client62::INULL, Client62::INULL,
Client62::INULL, Client62::INULL, Client62::INULL,
Client62::INULL, Client62::INULL, Client62::INULL,
Client62::INULL
Client62::INULL, Client62::INULL, Client62::INULL
),
Client62::INULL,
@@ -227,7 +227,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
Titanium::invtype::VIEW_MOD_PC_SIZE, Titanium::invtype::VIEW_MOD_BANK_SIZE, Titanium::invtype::VIEW_MOD_SHARED_BANK_SIZE,
Titanium::invtype::VIEW_MOD_LIMBO_SIZE, Titanium::invtype::ALT_STORAGE_SIZE, Titanium::invtype::ARCHIVED_SIZE,
Titanium::INULL, Titanium::INULL, Titanium::INULL,
Titanium::invtype::OTHER_SIZE
Titanium::INULL, Titanium::INULL, Titanium::invtype::OTHER_SIZE
),
Titanium::invslot::EQUIPMENT_BITMASK,
@@ -254,7 +254,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
SoF::invtype::VIEW_MOD_PC_SIZE, SoF::invtype::VIEW_MOD_BANK_SIZE, SoF::invtype::VIEW_MOD_SHARED_BANK_SIZE,
SoF::invtype::VIEW_MOD_LIMBO_SIZE, SoF::invtype::ALT_STORAGE_SIZE, SoF::invtype::ARCHIVED_SIZE,
SoF::INULL, SoF::INULL, SoF::INULL,
SoF::invtype::OTHER_SIZE
SoF::INULL, SoF::INULL, SoF::invtype::OTHER_SIZE
),
SoF::invslot::EQUIPMENT_BITMASK,
@@ -281,7 +281,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
SoD::invtype::VIEW_MOD_PC_SIZE, SoD::invtype::VIEW_MOD_BANK_SIZE, SoD::invtype::VIEW_MOD_SHARED_BANK_SIZE,
SoD::invtype::VIEW_MOD_LIMBO_SIZE, SoD::invtype::ALT_STORAGE_SIZE, SoD::invtype::ARCHIVED_SIZE,
SoD::INULL, SoD::INULL, SoD::INULL,
SoD::invtype::OTHER_SIZE
SoD::INULL, SoD::INULL, SoD::invtype::OTHER_SIZE
),
SoD::invslot::EQUIPMENT_BITMASK,
@@ -308,7 +308,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
UF::invtype::VIEW_MOD_PC_SIZE, UF::invtype::VIEW_MOD_BANK_SIZE, UF::invtype::VIEW_MOD_SHARED_BANK_SIZE,
UF::invtype::VIEW_MOD_LIMBO_SIZE, UF::invtype::ALT_STORAGE_SIZE, UF::invtype::ARCHIVED_SIZE,
UF::INULL, UF::INULL, UF::INULL,
UF::invtype::OTHER_SIZE
UF::INULL, UF::INULL, UF::invtype::OTHER_SIZE
),
UF::invslot::EQUIPMENT_BITMASK,
@@ -335,7 +335,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
RoF::invtype::VIEW_MOD_PC_SIZE, RoF::invtype::VIEW_MOD_BANK_SIZE, RoF::invtype::VIEW_MOD_SHARED_BANK_SIZE,
RoF::invtype::VIEW_MOD_LIMBO_SIZE, RoF::invtype::ALT_STORAGE_SIZE, RoF::invtype::ARCHIVED_SIZE,
RoF::invtype::MAIL_SIZE, RoF::invtype::GUILD_TROPHY_TRIBUTE_SIZE, RoF::INULL,
RoF::invtype::OTHER_SIZE
RoF::INULL,RoF::INULL,RoF::invtype::OTHER_SIZE
),
RoF::invslot::EQUIPMENT_BITMASK,
@@ -362,7 +362,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
RoF2::invtype::VIEW_MOD_PC_SIZE, RoF2::invtype::VIEW_MOD_BANK_SIZE, RoF2::invtype::VIEW_MOD_SHARED_BANK_SIZE,
RoF2::invtype::VIEW_MOD_LIMBO_SIZE, RoF2::invtype::ALT_STORAGE_SIZE, RoF2::invtype::ARCHIVED_SIZE,
RoF2::invtype::MAIL_SIZE, RoF2::invtype::GUILD_TROPHY_TRIBUTE_SIZE, RoF2::invtype::KRONO_SIZE,
RoF2::invtype::OTHER_SIZE
RoF2::invtype::GUILD_BANK_MAIN_SIZE,RoF2::invtype::GUILD_BANK_DEPOSIT_SIZE, RoF2::invtype::OTHER_SIZE
),
RoF2::invslot::EQUIPMENT_BITMASK,
@@ -389,7 +389,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::NPC::INULL, EntityLimits::NPC::INULL, EntityLimits::NPC::INULL,
EntityLimits::NPC::INULL, EntityLimits::NPC::INULL, EntityLimits::NPC::INULL,
EntityLimits::NPC::INULL, EntityLimits::NPC::INULL, EntityLimits::NPC::INULL,
EntityLimits::NPC::INULL
EntityLimits::NPC::INULL, EntityLimits::NPC::INULL,EntityLimits::NPC::INULL
),
EntityLimits::NPC::INULL,
@@ -416,7 +416,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL,
EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL,
EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL,
EntityLimits::NPCMerchant::INULL
EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL
),
EntityLimits::NPCMerchant::INULL,
@@ -443,7 +443,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::Merc::INULL, EntityLimits::Merc::INULL, EntityLimits::Merc::INULL,
EntityLimits::Merc::INULL, EntityLimits::Merc::INULL, EntityLimits::Merc::INULL,
EntityLimits::Merc::INULL, EntityLimits::Merc::INULL, EntityLimits::Merc::INULL,
EntityLimits::Merc::INULL
EntityLimits::Merc::INULL, EntityLimits::Merc::INULL, EntityLimits::Merc::INULL
),
EntityLimits::Merc::INULL,
@@ -470,7 +470,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::Bot::INULL, EntityLimits::Bot::INULL, EntityLimits::Bot::INULL,
EntityLimits::Bot::INULL, EntityLimits::Bot::INULL, EntityLimits::Bot::INULL,
EntityLimits::Bot::INULL, EntityLimits::Bot::INULL, EntityLimits::Bot::INULL,
EntityLimits::Bot::INULL
EntityLimits::Bot::INULL, EntityLimits::Bot::INULL, EntityLimits::Bot::INULL
),
EntityLimits::Bot::invslot::EQUIPMENT_BITMASK,
@@ -497,7 +497,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL,
EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL,
EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL,
EntityLimits::ClientPet::INULL
EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL
),
EntityLimits::ClientPet::INULL,
@@ -524,7 +524,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL,
EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL,
EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL,
EntityLimits::NPCPet::INULL
EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL
),
EntityLimits::NPCPet::INULL,
@@ -551,7 +551,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL,
EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL,
EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL,
EntityLimits::MercPet::INULL
EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL
),
EntityLimits::MercPet::INULL,
@@ -578,7 +578,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL,
EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL,
EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL,
EntityLimits::BotPet::INULL
EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL
),
EntityLimits::BotPet::INULL,
@@ -605,7 +605,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
Titanium::invtype::VIEW_MOD_PC_SIZE, Titanium::invtype::VIEW_MOD_BANK_SIZE, Titanium::invtype::VIEW_MOD_SHARED_BANK_SIZE,
Titanium::invtype::VIEW_MOD_LIMBO_SIZE, Titanium::INULL, Titanium::INULL,
Titanium::INULL, Titanium::INULL, Titanium::INULL,
Titanium::INULL
Titanium::INULL, Titanium::INULL, Titanium::INULL
),
Titanium::INULL,
@@ -632,7 +632,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
SoF::invtype::VIEW_MOD_PC_SIZE, SoF::invtype::VIEW_MOD_BANK_SIZE, SoF::invtype::VIEW_MOD_SHARED_BANK_SIZE,
SoF::invtype::VIEW_MOD_LIMBO_SIZE, SoF::INULL, SoF::INULL,
SoF::INULL, SoF::INULL, SoF::INULL,
SoF::INULL
SoF::INULL, SoF::INULL, SoF::INULL
),
SoF::INULL,
@@ -659,7 +659,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
SoD::invtype::VIEW_MOD_PC_SIZE, SoD::invtype::VIEW_MOD_BANK_SIZE, SoD::invtype::VIEW_MOD_SHARED_BANK_SIZE,
SoD::invtype::VIEW_MOD_LIMBO_SIZE, SoD::INULL, SoD::INULL,
SoD::INULL, SoD::INULL, SoD::INULL,
SoD::INULL
SoD::INULL, SoD::INULL, SoD::INULL
),
SoD::INULL,
@@ -686,7 +686,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
UF::invtype::VIEW_MOD_PC_SIZE, UF::invtype::VIEW_MOD_BANK_SIZE, UF::invtype::VIEW_MOD_SHARED_BANK_SIZE,
UF::invtype::VIEW_MOD_LIMBO_SIZE, UF::INULL, UF::INULL,
UF::INULL, UF::INULL, UF::INULL,
UF::INULL
UF::INULL, UF::INULL, UF::INULL
),
UF::INULL,
@@ -713,7 +713,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
RoF::invtype::VIEW_MOD_PC_SIZE, RoF::invtype::VIEW_MOD_BANK_SIZE, RoF::invtype::VIEW_MOD_SHARED_BANK_SIZE,
RoF::invtype::VIEW_MOD_LIMBO_SIZE, RoF::INULL, RoF::INULL,
RoF::INULL, RoF::INULL, RoF::INULL,
RoF::INULL
RoF::INULL, RoF::INULL, RoF::INULL
),
RoF::INULL,
@@ -740,7 +740,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
RoF2::invtype::VIEW_MOD_PC_SIZE, RoF2::invtype::VIEW_MOD_BANK_SIZE, RoF2::invtype::VIEW_MOD_SHARED_BANK_SIZE,
RoF2::invtype::VIEW_MOD_LIMBO_SIZE, RoF2::INULL, RoF2::INULL,
RoF2::INULL, RoF2::INULL, RoF2::INULL,
RoF2::INULL
RoF2::INULL, RoF2::INULL, RoF2::INULL
),
RoF2::INULL,
+3 -3
View File
@@ -87,7 +87,7 @@ namespace EQ
int16 ViewMODPC, ViewMODBank, ViewMODSharedBank;
int16 ViewMODLimbo, AltStorage, Archived;
int16 Mail, GuildTrophyTribute, Krono;
int16 Other;
int16 GuildBankMain,GuildBankDeposit, Other;
InventoryTypeSize_Struct(
int16 Possessions, int16 Bank, int16 SharedBank,
@@ -98,7 +98,7 @@ namespace EQ
int16 ViewMODPC, int16 ViewMODBank, int16 ViewMODSharedBank,
int16 ViewMODLimbo, int16 AltStorage, int16 Archived,
int16 Mail, int16 GuildTrophyTribute, int16 Krono,
int16 Other
int16 GuildBankMain,int16 GuildBankDeposit, int16 Other
) :
Possessions(Possessions), Bank(Bank), SharedBank(SharedBank),
Trade(Trade), World(World), Limbo(Limbo),
@@ -108,7 +108,7 @@ namespace EQ
ViewMODPC(ViewMODPC), ViewMODBank(ViewMODBank), ViewMODSharedBank(ViewMODSharedBank),
ViewMODLimbo(ViewMODLimbo), AltStorage(AltStorage), Archived(Archived),
Mail(Mail), GuildTrophyTribute(GuildTrophyTribute), Krono(Krono),
Other(Other)
GuildBankMain(GuildBankMain), GuildBankDeposit(GuildBankDeposit), Other(Other)
{ }
};
+72 -54
View File
@@ -19,17 +19,17 @@
#ifndef EQ_PACKET_STRUCTS_H
#define EQ_PACKET_STRUCTS_H
#include "types.h"
#include <list>
#include <string.h>
#include <string>
#include <list>
#include <time.h>
#include "../common/version.h"
#include "emu_constants.h"
#include "textures.h"
#include "../cereal/include/cereal/archives/binary.hpp"
#include "../cereal/include/cereal/types/string.hpp"
#include "../cereal/include/cereal/types/vector.hpp"
#include "../common/version.h"
#include "emu_constants.h"
#include "textures.h"
#include "types.h"
static const uint32 BUFF_COUNT = 42;
static const uint32 PET_BUFF_COUNT = 30;
@@ -47,7 +47,7 @@ static const uint32 ADVANCED_LORE_LENGTH = 8192;
*/
#pragma pack(1)
struct LoginInfo_Struct {
struct LoginInfo {
/*000*/ char login_info[64];
/*064*/ uint8 unknown064[124];
/*188*/ uint8 zoning; // 01 if zoning, 00 if not
@@ -324,6 +324,8 @@ union
bool guild_show;
bool trader;
bool buyer;
bool untargetable;
uint32 npc_tint_id;
};
struct PlayerState_Struct {
@@ -5529,56 +5531,65 @@ struct GuildBankWithdrawItem_Struct
struct GuildBankItemUpdate_Struct
{
void Init(uint32 inAction, uint32 inUnknown004, uint16 inSlotID, uint16 inArea, uint16 inUnknown012, uint32 inItemID, uint32 inIcon, uint32 inQuantity,
uint32 inPermissions, uint32 inAllowMerge, bool inUseable)
void Init(
uint32 inAction,
uint32 inUnknown004,
uint16 inSlotID,
uint16 inArea,
uint16 inUnknown012,
uint32 inItemID,
uint32 inIcon,
uint32 inQuantity,
uint32 inPermissions,
uint32 inAllowMerge,
bool inUseable)
{
Action = inAction;
Unknown004 = inUnknown004;
SlotID = inSlotID;
Area = inArea;
Unknown012 = inUnknown012;
ItemID = inItemID;
Icon = inIcon;
Quantity = inQuantity;
Permissions = inPermissions;
AllowMerge = inAllowMerge;
Useable = inUseable;
ItemName[0] = '\0';
Donator[0] = '\0';
WhoFor[0] = '\0';
action = inAction;
unknown004 = inUnknown004;
slot_id = inSlotID;
area = inArea;
display = inUnknown012;
item_id = inItemID;
icon_id = inIcon;
quantity = inQuantity;
permissions = inPermissions;
allow_merge = inAllowMerge;
is_useable = inUseable;
item_name[0] = '\0';
donator[0] = '\0';
who_for[0] = '\0';
};
/*000*/ uint32 Action;
/*004*/ uint32 Unknown004;
/*008*/ uint16 SlotID;
/*010*/ uint16 Area;
/*012*/ uint32 Unknown012;
/*016*/ uint32 ItemID;
/*020*/ uint32 Icon;
/*024*/ uint32 Quantity;
/*028*/ uint32 Permissions;
/*032*/ uint8 AllowMerge;
/*033*/ uint8 Useable; // Used in conjunction with the Public-if-useable permission.
/*034*/ char ItemName[64];
/*098*/ char Donator[64];
/*162*/ char WhoFor[64];
/*226*/ uint16 Unknown226;
/*000*/ uint32 action;
/*004*/ uint32 unknown004;
/*008*/ uint16 slot_id;
/*010*/ uint16 area;
/*012*/ uint32 display;
/*016*/ uint32 item_id;
/*020*/ uint32 icon_id;
/*024*/ uint32 quantity;
/*028*/ uint32 permissions;
/*032*/ uint8 allow_merge;
/*033*/ uint8 is_useable; // Used in conjunction with the Public-if-useable permission.
/*034*/ char item_name[64];
/*098*/ char donator[64];
/*162*/ char who_for[64];
/*226*/ uint16 unknown226;
};
// newer clients (RoF+) send a list that contains 240 entries
// The packets don't actually use all 64 chars in the strings, but we'll just overallocate for these
struct GuildBankItemListEntry_Struct
{
uint8 vaild;
struct GuildBankItemListEntry_Struct {
uint8 vaild;
uint32 permissions;
char whofor[64];
char donator[64];
char whofor[64];
char donator[64];
uint32 item_id;
uint32 item_icon;
uint32 quantity;
uint8 allow_merge; // 1 here for non-full stacks
uint8 usable;
char item_name[64];
uint8 allow_merge; // 1 here for non-full stacks
uint8 usable;
char item_name[64];
};
struct GuildBankClear_Struct
@@ -5823,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
{
+2 -2
View File
@@ -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());
}
+6 -6
View File
@@ -6,7 +6,7 @@
#include <string>
#include "emu_versions.h"
#include "eq_packet.h"
#include "net/daybreak_connection.h"
#include "net/reliable_stream_connection.h"
typedef enum {
ESTABLISHED,
@@ -33,18 +33,18 @@ struct EQStreamManagerInterfaceOptions
//Login I had trouble getting to recognize compression at all
//but that might be because it was still a bit buggy when i was testing that.
if (compressed) {
daybreak_options.encode_passes[0] = EQ::Net::EncodeCompression;
reliable_stream_options.encode_passes[0] = EQ::Net::EncodeCompression;
}
else if (encoded) {
daybreak_options.encode_passes[0] = EQ::Net::EncodeXOR;
reliable_stream_options.encode_passes[0] = EQ::Net::EncodeXOR;
}
daybreak_options.port = port;
reliable_stream_options.port = port;
}
int opcode_size;
bool track_opcode_stats;
EQ::Net::DaybreakConnectionManagerOptions daybreak_options;
EQ::Net::ReliableStreamConnectionManagerOptions reliable_stream_options;
};
class EQStreamManagerInterface
@@ -80,7 +80,7 @@ public:
struct Stats
{
EQ::Net::DaybreakConnectionStats DaybreakStats;
EQ::Net::ReliableStreamConnectionStats ReliableStreamStats;
int RecvCount[_maxEmuOpcode];
int SentCount[_maxEmuOpcode];
};
+17 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+25 -15
View File
@@ -74,7 +74,7 @@ namespace Logs {
Spawns,
Spells,
Status, // deprecated
TCPConnection,
TCPConnection, // deprecated
Tasks,
Tradeskills,
Trading,
@@ -149,6 +149,9 @@ namespace Logs {
BotSpellChecks,
BotSpellTypeChecks,
NpcHandin,
ZoneState,
NetClient,
NetTCP,
MaxCategoryID /* Don't Remove this */
};
@@ -182,7 +185,7 @@ namespace Logs {
"Spawns",
"Spells",
"Status (Deprecated)",
"TCP Connection",
"TCP Connection (Deprecated)",
"Tasks",
"Tradeskills",
"Trading",
@@ -191,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)",
@@ -208,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",
@@ -234,7 +237,7 @@ namespace Logs {
"DialogueWindow",
"HTTP",
"Saylink",
"ChecksumVer",
"Checksum Verification",
"CombatRecord",
"Hate",
"Discord",
@@ -256,12 +259,13 @@ namespace Logs {
"Bot Settings",
"Bot Spell Checks",
"Bot Spell Type Checks",
"NpcHandin"
"NpcHandin",
"ZoneState",
"Net Server <-> Client",
"Net TCP"
};
}
#include "eqemu_logsys_log_aliases.h"
class Database;
constexpr uint16 MAX_DISCORD_WEBHOOK_ID = 300;
@@ -277,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
@@ -344,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]{};
@@ -428,7 +438,7 @@ private:
void InjectTablesIfNotExist();
};
extern EQEmuLogSys LogSys;
#include "eqemu_logsys_log_aliases.h"
/**
template<typename... Args>
@@ -450,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
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",
+41 -15
View File
@@ -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();
@@ -82,13 +82,12 @@ void PlayerEventLogs::Init()
PlayerEventLogSettingsRepository::ReplaceMany(*m_database, settings_to_insert);
}
LoadEtlIds();
bool processing_in_world = !RuleB(Logging, PlayerEventsQSProcess) && IsWorld();
bool processing_in_qs = RuleB(Logging, PlayerEventsQSProcess) && IsQueryServ();
// on initial boot process truncation
if (processing_in_world || processing_in_qs) {
LoadEtlIds();
ProcessRetentionTruncation();
}
}
@@ -182,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++;
}
@@ -399,10 +408,9 @@ 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 {
LogError("Non-Implemented ETL routing [{}]", r.event_type_id);
LogPlayerEventsDetail("Non-Implemented ETL routing [{}]", r.event_type_id);
}
}
}
@@ -501,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;
}
@@ -521,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
@@ -909,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();
+12 -5
View File
@@ -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
File diff suppressed because it is too large Load Diff
+45 -18
View File
@@ -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,11 @@ 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);
if (inst.GetEvolveCurrentAmount() > 0) {
e.current_amount = inst.GetEvolveCurrentAmount();
inst.CalculateEvolveProgression();
}
auto r = CharacterEvolvingItemsRepository::InsertOne(*m_db, e);
e.id = r.id;
@@ -87,21 +95,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 +128,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 +207,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 +255,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 +276,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 +318,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();
+7 -4
View File
@@ -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
-101
View File
@@ -1,101 +0,0 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "expedition_lockout_timer.h"
#include "../common/strings.h"
#include "../common/rulesys.h"
#include "../common/util/uuid.h"
#include <fmt/format.h>
const char* const DZ_REPLAY_TIMER_NAME = "Replay Timer"; // see December 14, 2016 patch notes
ExpeditionLockoutTimer::ExpeditionLockoutTimer(
std::string expedition_uuid, std::string expedition_name,
std::string event_name, uint64_t expire_time, uint32_t duration
) :
m_expedition_uuid{std::move(expedition_uuid)},
m_expedition_name{std::move(expedition_name)},
m_event_name{std::move(event_name)},
m_expire_time(std::chrono::system_clock::from_time_t(expire_time)),
m_duration(duration)
{
if (m_event_name == DZ_REPLAY_TIMER_NAME)
{
m_is_replay_timer = true;
}
}
ExpeditionLockoutTimer ExpeditionLockoutTimer::CreateLockout(
const std::string& expedition_name, const std::string& event_name, uint32_t seconds, std::string uuid)
{
seconds = static_cast<uint32_t>(seconds * RuleR(Expedition, LockoutDurationMultiplier));
if (uuid.empty())
{
uuid = EQ::Util::UUID::Generate().ToString();
}
ExpeditionLockoutTimer lockout{uuid, expedition_name, event_name, 0, seconds};
lockout.Reset(); // sets expire time
return lockout;
}
uint32_t ExpeditionLockoutTimer::GetSecondsRemaining() const
{
auto now = std::chrono::system_clock::now();
if (m_expire_time > now)
{
auto remaining = m_expire_time - now;
return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::seconds>(remaining).count());
}
return 0;
}
ExpeditionLockoutTimer::DaysHoursMinutes ExpeditionLockoutTimer::GetDaysHoursMinutesRemaining() const
{
auto seconds = GetSecondsRemaining();
return ExpeditionLockoutTimer::DaysHoursMinutes{
fmt::format_int(seconds / 86400).str(), // days
fmt::format_int((seconds / 3600) % 24).str(), // hours
fmt::format_int((seconds / 60) % 60).str() // minutes
};
}
bool ExpeditionLockoutTimer::IsSameLockout(const ExpeditionLockoutTimer& compare_lockout) const
{
return compare_lockout.IsSameLockout(GetExpeditionName(), GetEventName());
}
bool ExpeditionLockoutTimer::IsSameLockout(
const std::string& expedition_name, const std::string& event_name) const
{
return GetExpeditionName() == expedition_name && GetEventName() == event_name;
}
void ExpeditionLockoutTimer::AddLockoutTime(int seconds)
{
seconds = static_cast<uint32_t>(seconds * RuleR(Expedition, LockoutDurationMultiplier));
auto new_duration = std::max(0, static_cast<int>(m_duration.count()) + seconds);
auto start_time = m_expire_time - m_duration;
m_duration = std::chrono::seconds(new_duration);
m_expire_time = start_time + m_duration;
}
-76
View File
@@ -1,76 +0,0 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EXPEDITION_LOCKOUT_TIMER_H
#define EXPEDITION_LOCKOUT_TIMER_H
#include <chrono>
#include <string>
extern const char* const DZ_REPLAY_TIMER_NAME;
class ExpeditionLockoutTimer
{
public:
ExpeditionLockoutTimer() = default;
ExpeditionLockoutTimer(
std::string expedition_uuid, std::string expedition_name,
std::string event_name, uint64_t expire_time, uint32_t duration);
static ExpeditionLockoutTimer CreateLockout(
const std::string& expedition_name, const std::string& event_name,
uint32_t seconds, std::string uuid = {});
struct DaysHoursMinutes
{
std::string days;
std::string hours;
std::string mins;
};
void AddLockoutTime(int seconds);
uint32_t GetDuration() const { return static_cast<uint32_t>(m_duration.count()); }
uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); }
uint64_t GetStartTime() const { return std::chrono::system_clock::to_time_t(m_expire_time - m_duration); }
uint32_t GetSecondsRemaining() const;
DaysHoursMinutes GetDaysHoursMinutesRemaining() const;
const std::string& GetExpeditionName() const { return m_expedition_name; }
const std::string& GetExpeditionUUID() const { return m_expedition_uuid; }
const std::string& GetEventName() const { return m_event_name; }
bool IsExpired() const { return GetSecondsRemaining() == 0; }
bool IsFromExpedition(const std::string& uuid) const { return uuid == m_expedition_uuid; }
bool IsReplayTimer() const { return m_is_replay_timer; }
bool IsSameLockout(const ExpeditionLockoutTimer& compare_lockout) const;
bool IsSameLockout(const std::string& expedition_name, const std::string& event_name) const;
void Reset() { m_expire_time = std::chrono::system_clock::now() + m_duration; }
void SetDuration(uint32_t seconds) { m_duration = std::chrono::seconds(seconds); }
void SetExpireTime(uint64_t expire_time) { m_expire_time = std::chrono::system_clock::from_time_t(expire_time); }
void SetUUID(const std::string& uuid) { m_expedition_uuid = uuid; }
private:
bool m_is_replay_timer = false;
std::string m_expedition_uuid; // expedition received in
std::string m_expedition_name;
std::string m_event_name;
std::chrono::seconds m_duration;
std::chrono::time_point<std::chrono::system_clock> m_expire_time;
};
#endif
+29 -29
View File
@@ -95,12 +95,12 @@ bool IsOfEqualRace(int r1, int r2)
}
// TODO: add more values
switch (r1) {
case DARK_ELF:
case Race::DarkElf:
if (r2 == Race::NeriakCitizen) {
return true;
}
break;
case BARBARIAN:
case Race::Barbarian:
if (r2 == Race::HalasCitizen) {
return true;
}
@@ -116,49 +116,49 @@ bool IsOfIndiffRace(int r1, int r2)
}
// TODO: add more values
switch (r1) {
case DARK_ELF:
case OGRE:
case TROLL:
if (r2 == OGRE || r2 == TROLL || r2 == DARK_ELF) {
case Race::DarkElf:
case Race::Ogre:
case Race::Troll:
if (r2 == Race::Ogre || r2 == Race::Troll || r2 == Race::DarkElf) {
return true;
}
break;
case HUMAN:
case BARBARIAN:
case HALF_ELF:
case GNOME:
case HALFLING:
case WOOD_ELF:
if (r2 == HUMAN ||
r2 == BARBARIAN ||
r2 == ERUDITE ||
r2 == HALF_ELF ||
r2 == GNOME ||
r2 == HALFLING ||
r2 == DWARF ||
r2 == HIGH_ELF ||
r2 == WOOD_ELF) {
case Race::Human:
case Race::Barbarian:
case Race::HalfElf:
case Race::Gnome:
case Race::Halfling:
case Race::WoodElf:
if (r2 == Race::Human ||
r2 == Race::Barbarian ||
r2 == Race::Erudite ||
r2 == Race::HalfElf ||
r2 == Race::Gnome ||
r2 == Race::Halfling ||
r2 == Race::Dwarf ||
r2 == Race::HighElf ||
r2 == Race::WoodElf) {
return true;
}
break;
case ERUDITE:
if (r2 == HUMAN || r2 == HALF_ELF) {
case Race::Erudite:
if (r2 == Race::Human || r2 == Race::HalfElf) {
return true;
}
break;
case DWARF:
if (r2 == HALFLING || r2 == GNOME) {
case Race::Dwarf:
if (r2 == Race::Halfling || r2 == Race::Gnome) {
return true;
}
break;
case HIGH_ELF:
if (r2 == WOOD_ELF) {
case Race::HighElf:
if (r2 == Race::WoodElf) {
return true;
}
break;
case VAHSHIR:
case Race::VahShir:
return true;
case IKSAR:
case Race::Iksar:
return false;
}
return false;
+12 -13
View File
@@ -39,6 +39,7 @@
#include <filesystem>
#include <iostream>
#include <sys/stat.h>
#include <vector>
namespace fs = std::filesystem;
@@ -90,23 +91,21 @@ std::string File::GetCwd()
FileContentsResult File::GetContents(const std::string &file_name)
{
std::string error;
std::ifstream f;
f.open(file_name);
std::string line;
std::string lines;
if (f.is_open()) {
while (f) {
std::getline(f, line);
lines += line + "\n";
}
std::ifstream f(file_name, std::ios::in | std::ios::binary);
if (!f) {
return { .error = fmt::format("Couldn't open file [{}]", file_name) };
}
else {
error = fmt::format("Couldn't open file [{}]", file_name);
constexpr size_t CHUNK_SIZE = 4096; // Read 4KB chunks
std::string lines;
std::vector<char> buffer(CHUNK_SIZE);
while (f.read(buffer.data(), CHUNK_SIZE) || f.gcount() > 0) {
lines.append(buffer.data(), f.gcount());
}
return FileContentsResult{
.contents = lines,
.error = error,
.error = {}
};
}
+28 -26
View File
@@ -547,60 +547,62 @@ uint32 BaseGuildManager::UpdateDbCreateGuild(std::string name, uint32 leader)
bool BaseGuildManager::UpdateDbDeleteGuild(uint32 guild_id, bool local_delete, bool db_delete)
{
auto const where_filter = fmt::format("guild_id = {}", guild_id);
auto const bank_items = GuildBankRepository::GetWhere(*m_db, where_filter);
if (local_delete) {
auto where_filter = fmt::format("guildid = {}", guild_id);
auto bank_items = GuildBankRepository::GetWhere(*m_db, where_filter);
if (!bank_items.empty()) {
LogError(
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try again.",
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try "
"again.",
guild_id,
bank_items.size()
);
LogGuilds(
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try again.",
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try "
"again.",
guild_id,
bank_items.size()
);
return false;
}
else {
std::map<uint32, GuildInfo *>::iterator res;
res = m_guilds.find(guild_id);
if (res != m_guilds.end()) {
delete res->second;
m_guilds.erase(res);
LogGuilds("Deleted guild [{}] from memory", guild_id);
//Does this need to be sent to world?
}
auto res = m_guilds.find(guild_id);
if (res != m_guilds.end()) {
safe_delete(res->second);
m_guilds.erase(res);
LogGuilds("Deleted guild [{}] from memory", guild_id);
// Does this need to be sent to world?
}
}
if (db_delete) {
auto where_filter = fmt::format("guildid = {}", guild_id);
auto bank_items = GuildBankRepository::GetWhere(*m_db, where_filter);
if (!bank_items.empty()) {
LogError(
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try again.",
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try "
"again.",
guild_id,
bank_items.size()
);
LogGuilds(
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try again.",
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try "
"again.",
guild_id,
bank_items.size()
);
return false;
}
else {
auto where_filter = fmt::format("guild_id = {}", guild_id);
GuildTributesRepository::DeleteOne(*m_db, guild_id);
GuildsRepository::DeleteOne(*m_db, guild_id);
GuildRanksRepository::DeleteWhere(*m_db, where_filter);
GuildPermissionsRepository::DeleteWhere(*m_db, where_filter);
GuildMembersRepository::DeleteWhere(*m_db, where_filter);
LogGuilds("Deleted guild [{}] from the database", guild_id);
}
GuildTributesRepository::DeleteOne(*m_db, guild_id);
GuildsRepository::DeleteOne(*m_db, guild_id);
GuildRanksRepository::DeleteWhere(*m_db, where_filter);
GuildPermissionsRepository::DeleteWhere(*m_db, where_filter);
GuildMembersRepository::DeleteWhere(*m_db, where_filter);
LogGuilds("Deleted guild [{}] from the database", guild_id);
}
return true;
}
+54
View File
@@ -2028,3 +2028,57 @@ int16 EQ::InventoryProfile::_HasEvolvingItem(ItemInstQueue &iqueue, uint64 evolv
return INVALID_INDEX;
}
int16 EQ::InventoryProfile::FindFirstFreeSlotThatFitsItemWithStacking(ItemInstance *item_inst) const
{
auto item_data = item_inst->GetItem();
if (!item_data) {
return INVALID_INDEX;
}
for (int16 i = invslot::GENERAL_BEGIN; i <= invslot::GENERAL_END; i++) {
auto const inv_item = GetItem(i);
if (!inv_item) {
// Found available slot in personal inventory
// Anything will fit here
return i;
}
if (item_data->IsClassBag() && item_inst->IsNoneEmptyContainer()) {
// If the inbound item is a bag with items, it cannot be stored within a bag
// Move to next potential slot
continue;
}
if (inv_item->GetID() == item_data->ID && item_data->Stackable) {
if (item_inst->GetCharges() + inv_item->GetCharges() <= item_data->StackSize) {
// Found a personal inventory slot that has room for a stackable addition
return i;
}
}
if (inv_item->IsClassBag() && CanItemFitInContainer(item_data, inv_item->GetItem())) {
int16 const base_slot_id = CalcSlotId(i, invbag::SLOT_BEGIN);
uint8 const bag_size = inv_item->GetItem()->BagSlots;
uint8 const item_size = item_data->Size;
for (uint8 bag_slot = invbag::SLOT_BEGIN; bag_slot < bag_size; bag_slot++) {
auto bag_item = GetItem(base_slot_id + bag_slot);
if (!bag_item) {
// Found available slot within bag that will hold inbound item
return base_slot_id + bag_slot;
}
if (bag_item && item_data->Stackable && bag_item->GetID() == item_data->ID) {
if (item_inst->GetCharges() + bag_item->GetCharges() <= item_data->StackSize) {
// Found a bag slot has room for a stackable addition
return base_slot_id + bag_slot;
}
}
}
}
}
return INVALID_INDEX;
}
+1
View File
@@ -179,6 +179,7 @@ namespace EQ
int16 FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size = 0, bool is_arrow = false);
int16 FindFreeSlotForTradeItem(const ItemInstance* inst, int16 general_start = invslot::GENERAL_BEGIN, uint8 bag_start = invbag::SLOT_BEGIN);
int16 FindFirstFreeSlotThatFitsItem(const EQ::ItemData *inst);
int16 FindFirstFreeSlotThatFitsItemWithStacking(ItemInstance *inst) const;
// Calculate slot_id for an item within a bag
static int16 CalcSlotId(int16 slot_id); // Calc parent bag's slot_id
+81
View File
@@ -259,3 +259,84 @@ bool IpUtil::IsIPAddress(const std::string &ip_address)
}
#include <iostream>
#ifdef _WIN32
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib") // Link against Winsock library
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#endif
#include <iostream>
#include <string>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h> // For inet_pton
#pragma comment(lib, "ws2_32.lib") // Link against Winsock library
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> // For inet_pton
#include <unistd.h>
#endif
bool IpUtil::IsPortInUse(const std::string& ip, int port) {
bool in_use = false;
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WSAStartup failed\n";
return true; // Assume in use on failure
}
#endif
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
#ifdef _WIN32
WSACleanup();
#endif
return true; // Assume in use on failure
}
#ifdef _WIN32
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&opt, sizeof(opt)); // Windows-specific
#else
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); // Linux/macOS
#endif
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
// Convert IP address from string to binary format
if (inet_pton(AF_INET, ip.c_str(), &addr.sin_addr) <= 0) {
std::cerr << "Invalid IP address format: " << ip << std::endl;
#ifdef _WIN32
closesocket(sock);
WSACleanup();
#else
close(sock);
#endif
return true; // Assume in use on failure
}
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
in_use = true; // Bind failed, port is in use
}
#ifdef _WIN32
closesocket(sock);
WSACleanup();
#else
close(sock);
#endif
return in_use;
}
+1
View File
@@ -37,6 +37,7 @@ public:
int port
);
static bool IsIPAddress(const std::string &ip_address);
static bool IsPortInUse(const std::string& ip, int port);
};
+1 -1
View File
@@ -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
+1 -1
View File
@@ -438,7 +438,7 @@ namespace EQ
//uint32 Unk054 {};
int16 MaxCharges {}; // Maximum charges items can hold: -1 if not a chargeable item
uint8 ItemType {}; // Item Type/Skill (itemClass* from above)
int32 SubType {}; // Some items have sub types that can be used for other things (unbreakable fishing poles, SE_FFItemClass)
int32 SubType {}; // Some items have sub types that can be used for other things (unbreakable fishing poles, SpellEffect::FFItemClass)
uint8 Material {}; // Item material type
uint32 HerosForgeModel {};// Hero's Forge Armor Model Type (2-13?)
float SellRate {}; // Sell rate
-206
View File
@@ -1,206 +0,0 @@
/*
These fields must be in the order of how they are serialized!
*/
F(itemclass)
F(name)
F(lore)
F(idfile)
F(id)
F(weight)
F(norent)
F(nodrop)
F(size)
F(slots)
F(price)
F(icon)
F(UNK012)
F(UNK013)
F(benefitflag)
F(tradeskills)
F(cr)
F(dr)
F(pr)
F(mr)
F(fr)
F(astr)
F(asta)
F(aagi)
F(adex)
F(acha)
F(aint)
F(awis)
F(hp)
F(mana)
F(ac)
F(deity)
F(skillmodvalue)
F(UNK033)
F(skillmodmax)
F(skillmodtype)
F(banedmgrace)
F(banedmgamt)
F(banedmgbody)
F(magic)
F(casttime_)
F(reqlevel)
F(bardtype)
F(bardvalue)
F(light)
F(delay)
F(reclevel)
F(recskill)
F(elemdmgtype)
F(elemdmgamt)
F(range)
F(damage)
F(color)
F(classes)
F(races)
F(UNK054)
F(maxcharges)
F(itemtype)
F(material)
F(herosforgemodel)
F(sellrate)
F(UNK059)
F(casttime)
F(elitematerial)
F(procrate)
F(combateffects)
F(shielding)
F(stunresist)
F(strikethrough)
F(extradmgskill)
F(extradmgamt)
F(spellshield)
F(avoidance)
F(accuracy)
F(charmfileid)
F(factionmod1)
F(factionmod2)
F(factionmod3)
F(factionmod4)
F(factionamt1)
F(factionamt2)
F(factionamt3)
F(factionamt4)
F(charmfile)
F(augtype)
F(augslot1type)
F(augslot1visible)
F(augslot2type)
F(augslot2visible)
F(augslot3type)
F(augslot3visible)
F(augslot4type)
F(augslot4visible)
F(augslot5type)
F(augslot5visible)
F(augslot6type)
F(augslot6visible)
F(ldontheme)
F(ldonprice)
F(ldonsold)
F(bagtype)
F(bagslots)
F(bagsize)
F(bagwr)
F(book)
F(booktype)
F(filename)
F(banedmgraceamt)
F(augrestrict)
F(loregroup)
F(pendingloreflag)
F(artifactflag)
F(summonedflag)
F(favor)
F(fvnodrop)
F(endur)
F(dotshielding)
F(attack)
F(regen)
F(manaregen)
F(enduranceregen)
F(haste)
F(damageshield)
F(recastdelay)
F(recasttype)
F(guildfavor)
F(augdistiller)
F(UNK123)
F(UNK124)
F(attuneable)
F(nopet)
F(UNK127)
F(pointtype)
F(potionbelt)
F(potionbeltslots)
F(stacksize)
F(notransfer)
F(stackable)
F(UNK134)
F(clickeffect)
F(clicktype)
F(clicklevel)
F(clicklevel2)
F(proceffect)
F(proctype)
F(proclevel)
F(proclevel2)
F(worneffect)
F(worntype)
F(wornlevel)
F(wornlevel2)
F(focuseffect)
F(focustype)
F(focuslevel)
F(focuslevel2)
F(scrolleffect)
F(scrolltype)
F(scrolllevel)
F(scrolllevel2)
F(bardeffect)
F(bardeffecttype)
F(bardlevel2)
F(bardlevel)
F(questitemflag)
F(svcorruption)
F(purity)
F(evoitem)
F(evoid)
F(evolvinglevel)
F(evomax)
F(backstabdmg)
F(dsmitigation)
F(heroic_str)
F(heroic_int)
F(heroic_wis)
F(heroic_agi)
F(heroic_dex)
F(heroic_sta)
F(heroic_cha)
F(heroic_mr)
F(heroic_fr)
F(heroic_cr)
F(heroic_dr)
F(heroic_pr)
F(heroic_svcorrup)
F(healamt)
F(spelldmg)
F(ldonsellbackrate)
F(scriptfileid)
F(expendablearrow)
F(clairvoyance)
F(clickname)
F(procname)
F(wornname)
F(focusname)
F(scrollname)
F(subtype)
+18 -10
View File
@@ -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;
+1 -1
View File
@@ -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:
//////////////////////////
+1
View File
@@ -25,6 +25,7 @@ struct LootItem {
uint16 trivial_max_level;
uint16 npc_min_level;
uint16 npc_max_level;
uint32 lootdrop_id; // required for zone state referencing
};
typedef std::list<LootItem*> LootItems;
+12 -12
View File
@@ -1,11 +1,11 @@
#include "eqstream.h"
#include "../eqemu_logsys.h"
EQ::Net::EQStreamManager::EQStreamManager(const EQStreamManagerInterfaceOptions &options) : EQStreamManagerInterface(options), m_daybreak(options.daybreak_options)
EQ::Net::EQStreamManager::EQStreamManager(const EQStreamManagerInterfaceOptions &options) : EQStreamManagerInterface(options), m_reliable_stream(options.reliable_stream_options)
{
m_daybreak.OnNewConnection(std::bind(&EQStreamManager::DaybreakNewConnection, this, std::placeholders::_1));
m_daybreak.OnConnectionStateChange(std::bind(&EQStreamManager::DaybreakConnectionStateChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_daybreak.OnPacketRecv(std::bind(&EQStreamManager::DaybreakPacketRecv, this, std::placeholders::_1, std::placeholders::_2));
m_reliable_stream.OnNewConnection(std::bind(&EQStreamManager::ReliableStreamNewConnection, this, std::placeholders::_1));
m_reliable_stream.OnConnectionStateChange(std::bind(&EQStreamManager::ReliableStreamConnectionStateChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_reliable_stream.OnPacketRecv(std::bind(&EQStreamManager::ReliableStreamPacketRecv, this, std::placeholders::_1, std::placeholders::_2));
}
EQ::Net::EQStreamManager::~EQStreamManager()
@@ -15,11 +15,11 @@ EQ::Net::EQStreamManager::~EQStreamManager()
void EQ::Net::EQStreamManager::SetOptions(const EQStreamManagerInterfaceOptions &options)
{
m_options = options;
auto &opts = m_daybreak.GetOptions();
opts = options.daybreak_options;
auto &opts = m_reliable_stream.GetOptions();
opts = options.reliable_stream_options;
}
void EQ::Net::EQStreamManager::DaybreakNewConnection(std::shared_ptr<DaybreakConnection> connection)
void EQ::Net::EQStreamManager::ReliableStreamNewConnection(std::shared_ptr<ReliableStreamConnection> connection)
{
std::shared_ptr<EQStream> stream(new EQStream(this, connection));
m_streams.emplace(std::make_pair(connection, stream));
@@ -28,7 +28,7 @@ void EQ::Net::EQStreamManager::DaybreakNewConnection(std::shared_ptr<DaybreakCon
}
}
void EQ::Net::EQStreamManager::DaybreakConnectionStateChange(std::shared_ptr<DaybreakConnection> connection, DbProtocolStatus from, DbProtocolStatus to)
void EQ::Net::EQStreamManager::ReliableStreamConnectionStateChange(std::shared_ptr<ReliableStreamConnection> connection, DbProtocolStatus from, DbProtocolStatus to)
{
auto iter = m_streams.find(connection);
if (iter != m_streams.end()) {
@@ -42,7 +42,7 @@ void EQ::Net::EQStreamManager::DaybreakConnectionStateChange(std::shared_ptr<Day
}
}
void EQ::Net::EQStreamManager::DaybreakPacketRecv(std::shared_ptr<DaybreakConnection> connection, const Packet &p)
void EQ::Net::EQStreamManager::ReliableStreamPacketRecv(std::shared_ptr<ReliableStreamConnection> connection, const Packet &p)
{
auto iter = m_streams.find(connection);
if (iter != m_streams.end()) {
@@ -53,7 +53,7 @@ void EQ::Net::EQStreamManager::DaybreakPacketRecv(std::shared_ptr<DaybreakConnec
}
}
EQ::Net::EQStream::EQStream(EQStreamManagerInterface *owner, std::shared_ptr<DaybreakConnection> connection)
EQ::Net::EQStream::EQStream(EQStreamManagerInterface *owner, std::shared_ptr<ReliableStreamConnection> connection)
{
m_owner = owner;
m_connection = connection;
@@ -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) {
@@ -235,7 +235,7 @@ EQStreamState EQ::Net::EQStream::GetState() {
EQ::Net::EQStream::Stats EQ::Net::EQStream::GetStats() const
{
Stats ret;
ret.DaybreakStats = m_connection->GetStats();
ret.ReliableStreamStats = m_connection->GetStats();
for (int i = 0; i < _maxEmuOpcode; ++i) {
ret.RecvCount[i] = 0;
+8 -8
View File
@@ -3,7 +3,7 @@
#include "../eq_packet.h"
#include "../eq_stream_intf.h"
#include "../opcodemgr.h"
#include "daybreak_connection.h"
#include "reliable_stream_connection.h"
#include <vector>
#include <deque>
#include <unordered_map>
@@ -23,21 +23,21 @@ namespace EQ
void OnNewConnection(std::function<void(std::shared_ptr<EQStream>)> func) { m_on_new_connection = func; }
void OnConnectionStateChange(std::function<void(std::shared_ptr<EQStream>, DbProtocolStatus, DbProtocolStatus)> func) { m_on_connection_state_change = func; }
private:
DaybreakConnectionManager m_daybreak;
ReliableStreamConnectionManager m_reliable_stream;
std::function<void(std::shared_ptr<EQStream>)> m_on_new_connection;
std::function<void(std::shared_ptr<EQStream>, DbProtocolStatus, DbProtocolStatus)> m_on_connection_state_change;
std::map<std::shared_ptr<DaybreakConnection>, std::shared_ptr<EQStream>> m_streams;
std::map<std::shared_ptr<ReliableStreamConnection>, std::shared_ptr<EQStream>> m_streams;
void DaybreakNewConnection(std::shared_ptr<DaybreakConnection> connection);
void DaybreakConnectionStateChange(std::shared_ptr<DaybreakConnection> connection, DbProtocolStatus from, DbProtocolStatus to);
void DaybreakPacketRecv(std::shared_ptr<DaybreakConnection> connection, const Packet &p);
void ReliableStreamNewConnection(std::shared_ptr<ReliableStreamConnection> connection);
void ReliableStreamConnectionStateChange(std::shared_ptr<ReliableStreamConnection> connection, DbProtocolStatus from, DbProtocolStatus to);
void ReliableStreamPacketRecv(std::shared_ptr<ReliableStreamConnection> connection, const Packet &p);
friend class EQStream;
};
class EQStream : public EQStreamInterface
{
public:
EQStream(EQStreamManagerInterface *parent, std::shared_ptr<DaybreakConnection> connection);
EQStream(EQStreamManagerInterface *parent, std::shared_ptr<ReliableStreamConnection> connection);
~EQStream();
virtual void QueuePacket(const EQApplicationPacket *p, bool ack_req = true);
@@ -67,7 +67,7 @@ namespace EQ
virtual EQStreamManagerInterface* GetManager() const;
private:
EQStreamManagerInterface *m_owner;
std::shared_ptr<DaybreakConnection> m_connection;
std::shared_ptr<ReliableStreamConnection> m_connection;
OpcodeManager **m_opcode_manager;
std::deque<std::unique_ptr<EQ::Net::Packet>> m_packet_queue;
std::unordered_map<int, int> m_packet_recv_count;
File diff suppressed because it is too large Load Diff
@@ -2,7 +2,8 @@
#include "../random.h"
#include "packet.h"
#include "daybreak_structs.h"
#include "reliable_stream_structs.h"
#include "reliable_stream_pooling.h"
#include <uv.h>
#include <chrono>
#include <functional>
@@ -15,7 +16,7 @@ namespace EQ
{
namespace Net
{
enum DaybreakProtocolOpcode
enum ReliableStreamProtocolOpcode
{
OP_Padding = 0x00,
OP_SessionRequest = 0x01,
@@ -54,7 +55,7 @@ namespace EQ
StatusDisconnected
};
enum DaybreakEncodeType
enum ReliableStreamEncodeType
{
EncodeNone = 0,
EncodeCompression = 1,
@@ -71,9 +72,9 @@ namespace EQ
typedef std::chrono::steady_clock::time_point Timestamp;
typedef std::chrono::steady_clock Clock;
struct DaybreakConnectionStats
struct ReliableStreamConnectionStats
{
DaybreakConnectionStats() {
ReliableStreamConnectionStats() {
recv_bytes = 0;
sent_bytes = 0;
recv_packets = 0;
@@ -133,14 +134,14 @@ namespace EQ
uint64_t bytes_before_encode;
};
class DaybreakConnectionManager;
class DaybreakConnection;
class DaybreakConnection
class ReliableStreamConnectionManager;
class ReliableStreamConnection;
class ReliableStreamConnection
{
public:
DaybreakConnection(DaybreakConnectionManager *owner, const DaybreakConnect &connect, const std::string &endpoint, int port);
DaybreakConnection(DaybreakConnectionManager *owner, const std::string &endpoint, int port);
~DaybreakConnection();
ReliableStreamConnection(ReliableStreamConnectionManager *owner, const ReliableStreamConnect &connect, const std::string &endpoint, int port);
ReliableStreamConnection(ReliableStreamConnectionManager *owner, const std::string &endpoint, int port);
~ReliableStreamConnection();
const std::string& RemoteEndpoint() const { return m_endpoint; }
int RemotePort() const { return m_port; }
@@ -150,23 +151,23 @@ namespace EQ
void QueuePacket(Packet &p, int stream);
void QueuePacket(Packet &p, int stream, bool reliable);
DaybreakConnectionStats GetStats();
ReliableStreamConnectionStats GetStats();
void ResetStats();
size_t GetRollingPing() const { return m_rolling_ping; }
DbProtocolStatus GetStatus() const { return m_status; }
const DaybreakEncodeType* GetEncodePasses() const { return m_encode_passes; }
const DaybreakConnectionManager* GetManager() const { return m_owner; }
DaybreakConnectionManager* GetManager() { return m_owner; }
const ReliableStreamEncodeType* GetEncodePasses() const { return m_encode_passes; }
const ReliableStreamConnectionManager* GetManager() const { return m_owner; }
ReliableStreamConnectionManager* GetManager() { return m_owner; }
private:
DaybreakConnectionManager *m_owner;
ReliableStreamConnectionManager *m_owner;
std::string m_endpoint;
int m_port;
uint32_t m_connect_code;
uint32_t m_encode_key;
uint32_t m_max_packet_size;
uint32_t m_crc_bytes;
DaybreakEncodeType m_encode_passes[2];
ReliableStreamEncodeType m_encode_passes[2];
Timestamp m_last_send;
Timestamp m_last_recv;
@@ -175,7 +176,7 @@ namespace EQ
std::list<DynamicPacket> m_buffered_packets;
size_t m_buffered_packets_length;
std::unique_ptr<char[]> m_combined;
DaybreakConnectionStats m_stats;
ReliableStreamConnectionStats m_stats;
Timestamp m_last_session_stats;
size_t m_rolling_ping;
Timestamp m_close_time;
@@ -185,8 +186,9 @@ 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
struct ReliableStreamSentPacket
{
DynamicPacket packet;
Timestamp last_sent;
@@ -195,9 +197,9 @@ namespace EQ
size_t resend_delay;
};
struct DaybreakStream
struct ReliableStream
{
DaybreakStream() {
ReliableStream() {
sequence_in = 0;
sequence_out = 0;
fragment_current_bytes = 0;
@@ -212,11 +214,11 @@ namespace EQ
uint32_t fragment_current_bytes;
uint32_t fragment_total_bytes;
std::map<uint16_t, DaybreakSentPacket> sent_packets;
std::map<uint16_t, ReliableStreamSentPacket> sent_packets;
};
DaybreakStream m_streams[4];
std::weak_ptr<DaybreakConnection> m_self;
ReliableStream m_streams[4];
std::weak_ptr<ReliableStreamConnection> m_self;
void Process();
void ProcessPacket(Packet &p);
@@ -249,12 +251,12 @@ namespace EQ
void FlushBuffer();
SequenceOrder CompareSequence(uint16_t expected, uint16_t actual) const;
friend class DaybreakConnectionManager;
friend class ReliableStreamConnectionManager;
};
struct DaybreakConnectionManagerOptions
struct ReliableStreamConnectionManagerOptions
{
DaybreakConnectionManagerOptions() {
ReliableStreamConnectionManagerOptions() {
max_connection_count = 0;
keepalive_delay_ms = 9000;
resend_delay_ms = 30;
@@ -266,8 +268,8 @@ namespace EQ
connect_stale_ms = 5000;
crc_length = 2;
max_packet_size = 512;
encode_passes[0] = DaybreakEncodeType::EncodeNone;
encode_passes[1] = DaybreakEncodeType::EncodeNone;
encode_passes[0] = ReliableStreamEncodeType::EncodeNone;
encode_passes[1] = ReliableStreamEncodeType::EncodeNone;
port = 0;
hold_size = 512;
hold_length_ms = 50;
@@ -297,28 +299,28 @@ namespace EQ
double tic_rate_hertz;
size_t resend_timeout;
size_t connection_close_time;
DaybreakEncodeType encode_passes[2];
ReliableStreamEncodeType encode_passes[2];
int port;
double outgoing_data_rate;
};
class DaybreakConnectionManager
class ReliableStreamConnectionManager
{
public:
DaybreakConnectionManager();
DaybreakConnectionManager(const DaybreakConnectionManagerOptions &opts);
~DaybreakConnectionManager();
ReliableStreamConnectionManager();
ReliableStreamConnectionManager(const ReliableStreamConnectionManagerOptions &opts);
~ReliableStreamConnectionManager();
void Connect(const std::string &addr, int port);
void Process();
void UpdateDataBudget();
void ProcessResend();
void OnNewConnection(std::function<void(std::shared_ptr<DaybreakConnection>)> func) { m_on_new_connection = func; }
void OnConnectionStateChange(std::function<void(std::shared_ptr<DaybreakConnection>, DbProtocolStatus, DbProtocolStatus)> func) { m_on_connection_state_change = func; }
void OnPacketRecv(std::function<void(std::shared_ptr<DaybreakConnection>, const Packet &)> func) { m_on_packet_recv = func; }
void OnNewConnection(std::function<void(std::shared_ptr<ReliableStreamConnection>)> func) { m_on_new_connection = func; }
void OnConnectionStateChange(std::function<void(std::shared_ptr<ReliableStreamConnection>, DbProtocolStatus, DbProtocolStatus)> func) { m_on_connection_state_change = func; }
void OnPacketRecv(std::function<void(std::shared_ptr<ReliableStreamConnection>, const Packet &)> func) { m_on_packet_recv = func; }
void OnErrorMessage(std::function<void(const std::string&)> func) { m_on_error_message = func; }
DaybreakConnectionManagerOptions& GetOptions() { return m_options; }
ReliableStreamConnectionManagerOptions& GetOptions() { return m_options; }
private:
void Attach(uv_loop_t *loop);
void Detach();
@@ -327,18 +329,18 @@ namespace EQ
uv_timer_t m_timer;
uv_udp_t m_socket;
uv_loop_t *m_attached;
DaybreakConnectionManagerOptions m_options;
std::function<void(std::shared_ptr<DaybreakConnection>)> m_on_new_connection;
std::function<void(std::shared_ptr<DaybreakConnection>, DbProtocolStatus, DbProtocolStatus)> m_on_connection_state_change;
std::function<void(std::shared_ptr<DaybreakConnection>, const Packet&)> m_on_packet_recv;
ReliableStreamConnectionManagerOptions m_options;
std::function<void(std::shared_ptr<ReliableStreamConnection>)> m_on_new_connection;
std::function<void(std::shared_ptr<ReliableStreamConnection>, DbProtocolStatus, DbProtocolStatus)> m_on_connection_state_change;
std::function<void(std::shared_ptr<ReliableStreamConnection>, const Packet&)> m_on_packet_recv;
std::function<void(const std::string&)> m_on_error_message;
std::map<std::pair<std::string, int>, std::shared_ptr<DaybreakConnection>> m_connections;
std::map<std::pair<std::string, int>, std::shared_ptr<ReliableStreamConnection>> m_connections;
void ProcessPacket(const std::string &endpoint, int port, const char *data, size_t size);
std::shared_ptr<DaybreakConnection> FindConnectionByEndpoint(std::string addr, int port);
std::shared_ptr<ReliableStreamConnection> FindConnectionByEndpoint(std::string addr, int port);
void SendDisconnect(const std::string &addr, int port);
friend class DaybreakConnection;
friend class ReliableStreamConnection;
};
}
}
+123
View File
@@ -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;
}
};
@@ -8,7 +8,7 @@ namespace EQ
{
namespace Net
{
struct DaybreakHeader
struct ReliableStreamHeader
{
static size_t size() { return 2; }
uint8_t zero;
@@ -22,7 +22,7 @@ namespace EQ
}
};
struct DaybreakConnect
struct ReliableStreamConnect
{
static size_t size() { return 14; }
uint8_t zero;
@@ -42,7 +42,7 @@ namespace EQ
}
};
struct DaybreakConnectReply
struct ReliableStreamConnectReply
{
static size_t size() { return 17; }
uint8_t zero;
@@ -68,7 +68,7 @@ namespace EQ
}
};
struct DaybreakDisconnect
struct ReliableStreamDisconnect
{
static size_t size() { return 8; }
uint8_t zero;
@@ -84,7 +84,7 @@ namespace EQ
}
};
struct DaybreakReliableHeader
struct ReliableStreamReliableHeader
{
static size_t size() { return 4; }
uint8_t zero;
@@ -100,10 +100,10 @@ namespace EQ
}
};
struct DaybreakReliableFragmentHeader
struct ReliableStreamReliableFragmentHeader
{
static size_t size() { return 4 + DaybreakReliableHeader::size(); }
DaybreakReliableHeader reliable;
static size_t size() { return 4 + ReliableStreamReliableHeader::size(); }
ReliableStreamReliableHeader reliable;
uint32_t total_size;
template <class Archive>
@@ -114,7 +114,7 @@ namespace EQ
}
};
struct DaybreakSessionStatRequest
struct ReliableStreamSessionStatRequest
{
static size_t size() { return 40; }
uint8_t zero;
@@ -144,7 +144,7 @@ namespace EQ
}
};
struct DaybreakSessionStatResponse
struct ReliableStreamSessionStatResponse
{
static size_t size() { return 40; }
uint8_t zero;
@@ -171,3 +171,4 @@ namespace EQ
};
}
}
+3 -3
View File
@@ -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);
+4 -4
View File
@@ -25,7 +25,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
return;
if (opcode == ServerOP_UsertoWorldReq) {
auto req_in = (UsertoWorldRequest_Struct*)p.Data();
auto req_in = (UsertoWorldRequest*)p.Data();
EQ::Net::DynamicPacket req;
size_t i = 0;
@@ -45,7 +45,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
}
if (opcode == ServerOP_LSClientAuth) {
auto req_in = (ClientAuth_Struct*)p.Data();
auto req_in = (ClientAuth*)p.Data();
EQ::Net::DynamicPacket req;
size_t i = 0;
@@ -54,7 +54,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
req.PutData(i, req_in->key, 30); i += 30;
req.PutUInt8(i, req_in->lsadmin); i += 1;
req.PutUInt16(i, req_in->is_world_admin); i += 2;
req.PutUInt32(i, req_in->ip); i += 4;
req.PutUInt32(i, req_in->ip_address); i += 4;
req.PutUInt8(i, req_in->is_client_from_local_network); i += 1;
EQ::Net::DynamicPacket out;
@@ -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
View File
@@ -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;
+2 -1
View File
@@ -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);
+125
View File
@@ -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;
}
};
+18 -18
View File
@@ -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;
@@ -1213,22 +1213,22 @@ namespace RoF
case 1: { // GuildBankItemUpdate
auto emu = (GuildBankItemUpdate_Struct *)in->pBuffer;
auto eq = (structs::GuildBankItemUpdate_Struct *)outapp->pBuffer;
eq->Action = 0;
OUT(Unknown004);
eq->Unknown08 = 0;
OUT(SlotID);
OUT(Area);
OUT(Unknown012);
OUT(ItemID);
OUT(Icon);
OUT(Quantity);
OUT(Permissions);
OUT(AllowMerge);
OUT(Useable);
OUT_str(ItemName);
OUT_str(Donator);
OUT_str(WhoFor);
OUT(Unknown226);
eq->action = 0;
OUT(unknown004);
eq->unknown008 = 0;
OUT(slot_id);
OUT(area);
OUT(display);
OUT(item_id);
OUT(icon_id);
OUT(quantity);
OUT(permissions);
OUT(allow_merge);
OUT(is_useable);
OUT_str(item_name);
OUT_str(donator);
OUT_str(who_for);
OUT(unknown226);
break;
}
default:
+24 -26
View File
@@ -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;
@@ -1743,22 +1743,19 @@ namespace RoF2
case 1: { // GuildBankItemUpdate
auto emu = (GuildBankItemUpdate_Struct *)in->pBuffer;
auto eq = (structs::GuildBankItemUpdate_Struct *)outapp->pBuffer;
eq->Action = 0;
OUT(Unknown004);
eq->Unknown08 = 0;
OUT(SlotID);
OUT(Area);
OUT(Unknown012);
OUT(ItemID);
OUT(Icon);
OUT(Quantity);
OUT(Permissions);
OUT(AllowMerge);
OUT(Useable);
OUT_str(ItemName);
OUT_str(Donator);
OUT_str(WhoFor);
OUT(Unknown226);
eq->action = 0;
OUT(display);
OUT(slot_id);
OUT(area);
OUT(item_id);
OUT(icon_id);
OUT(quantity);
OUT(permissions);
OUT(allow_merge);
OUT(is_useable);
OUT_str(item_name);
OUT_str(donator);
OUT_str(who_for);
break;
}
default:
@@ -4691,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;
@@ -4842,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)
@@ -6484,7 +6481,7 @@ namespace RoF2
hdr.scaled_value = (inst->IsScaling() ? (inst->GetExp() / 100) : 0);
hdr.instance_id = (inst->GetMerchantSlot() ? inst->GetMerchantSlot() : inst->GetSerialNumber());
hdr.parcel_item_id = packet_type == ItemPacketParcel ? inst->GetID() : 0;
if (item->EvolvingItem) {
if (item->EvolvingItem && packet_type != ItemPacketParcel && packet_type != ItemPacketMerchant) {
hdr.instance_id = inst->GetEvolveUniqueID() & 0xFFFFFFFF; //lower dword
hdr.parcel_item_id = inst->GetEvolveUniqueID() >> 32; //upper dword
}
@@ -6503,6 +6500,7 @@ namespace RoF2
if (item->EvolvingItem > 0) {
RoF2::structs::EvolvingItem_Struct evotop;
inst->CalculateEvolveProgression();
evotop.final_item_id = inst->GetEvolveFinalItemID();
evotop.evolve_level = item->EvolvingLevel;
+2
View File
@@ -101,6 +101,8 @@ namespace RoF2
const int16 MAIL_SIZE = 0;//unknown
const int16 GUILD_TROPHY_TRIBUTE_SIZE = 0;//unknown
const int16 KRONO_SIZE = 0;//unknown
const int16 GUILD_BANK_MAIN_SIZE = 200;
const int16 GUILD_BANK_DEPOSIT_SIZE = 40;
const int16 OTHER_SIZE = 0;//unknown
const int16 TRADE_NPC_SIZE = 4; // defined by implication
+29 -31
View File
@@ -1965,41 +1965,39 @@ struct GuildBankWithdrawItem_Struct
struct GuildBankItemUpdate_Struct
{
void Init(uint32 inAction, uint32 inUnknown004, uint16 inSlotID, uint16 inArea, uint16 inUnknown012, uint32 inItemID, uint32 inIcon, uint32 inQuantity,
void Init(uint32 inAction, uint32 inUnknown004, uint16 inSlotID, uint16 inArea, uint16 inUnknown016, uint32 inItemID, uint32 inIcon, uint32 inQuantity,
uint32 inPermissions, uint32 inAllowMerge, bool inUseable)
{
Action = inAction;
Unknown004 = inUnknown004;
SlotID = inSlotID;
Area = inArea;
Unknown012 = inUnknown012;
ItemID = inItemID;
Icon = inIcon;
Quantity = inQuantity;
Permissions = inPermissions;
AllowMerge = inAllowMerge;
Useable = inUseable;
ItemName[0] = '\0';
Donator[0] = '\0';
WhoFor[0] = '\0';
action = inAction;
slot_id = inSlotID;
area = inArea;
display = inUnknown016;
item_id = inItemID;
icon_id = inIcon;
quantity = inQuantity;
permissions = inPermissions;
allow_merge = inAllowMerge;
is_useable = inUseable;
item_name[0] = '\0';
donator[0] = '\0';
who_for[0] = '\0';
};
/*000*/ uint32 Action;
/*004*/ uint32 Unknown004;
/*008*/ uint32 Unknown08;
/*012*/ uint16 SlotID;
/*014*/ uint16 Area;
/*016*/ uint32 Unknown012;
/*020*/ uint32 ItemID;
/*024*/ uint32 Icon;
/*028*/ uint32 Quantity;
/*032*/ uint32 Permissions;
/*036*/ uint8 AllowMerge;
/*037*/ uint8 Useable; // Used in conjunction with the Public-if-useable permission.
/*038*/ char ItemName[64];
/*102*/ char Donator[64];
/*166*/ char WhoFor[64];
/*230*/ uint16 Unknown226;
/*000*/ uint32 action;
/*004*/ uint32 not_used004; //disassemble of client did not use this
/*008*/ uint32 not_used008; //disassemble of client did not use this
/*012*/ uint16 slot_id;
/*014*/ uint16 area;
/*016*/ uint32 display;
/*020*/ uint32 item_id;
/*024*/ uint32 icon_id;
/*028*/ uint32 quantity;
/*032*/ uint32 permissions;
/*036*/ uint8 allow_merge;
/*037*/ uint8 is_useable; // Used in conjunction with the Public-if-useable permission.
/*038*/ char item_name[64];
/*102*/ char donator[64];
/*166*/ char who_for[64];
};
struct GuildBankClear_Struct
+30 -30
View File
@@ -1946,38 +1946,38 @@ struct GuildBankItemUpdate_Struct
void Init(uint32 inAction, uint32 inUnknown004, uint16 inSlotID, uint16 inArea, uint16 inUnknown012, uint32 inItemID, uint32 inIcon, uint32 inQuantity,
uint32 inPermissions, uint32 inAllowMerge, bool inUseable)
{
Action = inAction;
Unknown004 = inUnknown004;
SlotID = inSlotID;
Area = inArea;
Unknown012 = inUnknown012;
ItemID = inItemID;
Icon = inIcon;
Quantity = inQuantity;
Permissions = inPermissions;
AllowMerge = inAllowMerge;
Useable = inUseable;
ItemName[0] = '\0';
Donator[0] = '\0';
WhoFor[0] = '\0';
action = inAction;
unknown004 = inUnknown004;
slot_id = inSlotID;
area = inArea;
display = inUnknown012;
item_id = inItemID;
icon_id = inIcon;
quantity = inQuantity;
permissions = inPermissions;
allow_merge = inAllowMerge;
is_useable = inUseable;
item_name[0] = '\0';
donator[0] = '\0';
who_for[0] = '\0';
};
/*000*/ uint32 Action;
/*004*/ uint32 Unknown004;
/*008*/ uint32 Unknown08;
/*012*/ uint16 SlotID;
/*014*/ uint16 Area;
/*016*/ uint32 Unknown012;
/*020*/ uint32 ItemID;
/*024*/ uint32 Icon;
/*028*/ uint32 Quantity;
/*032*/ uint32 Permissions;
/*036*/ uint8 AllowMerge;
/*037*/ uint8 Useable; // Used in conjunction with the Public-if-useable permission.
/*038*/ char ItemName[64];
/*102*/ char Donator[64];
/*166*/ char WhoFor[64];
/*230*/ uint16 Unknown226;
/*000*/ uint32 action;
/*004*/ uint32 unknown004;
/*008*/ uint32 unknown008;
/*012*/ uint16 slot_id;
/*014*/ uint16 area;
/*016*/ uint32 display;
/*020*/ uint32 item_id;
/*024*/ uint32 icon_id;
/*028*/ uint32 quantity;
/*032*/ uint32 permissions;
/*036*/ uint8 allow_merge;
/*037*/ uint8 is_useable; // Used in conjunction with the Public-if-useable permission.
/*038*/ char item_name[64];
/*102*/ char donator[64];
/*166*/ char who_for[64];
/*230*/ uint16 unknown226;
};
struct GuildBankClear_Struct
+13 -13
View File
@@ -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
View File
@@ -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) {
+17 -17
View File
@@ -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;
@@ -1059,7 +1059,7 @@ namespace Titanium
OUT(spawnid);
OUT_str(charname);
if (emu->race > 473)
if (emu->race > 474)
eq->race = 1;
else
OUT(race);
@@ -1840,7 +1840,7 @@ namespace Titanium
emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
eq->Race[char_index] = emu_cse->Race;
if (eq->Race[char_index] > 473)
if (eq->Race[char_index] > 474)
eq->Race[char_index] = 1;
for (int index = 0; index < EQ::textures::materialCount; ++index) {
@@ -2421,7 +2421,7 @@ namespace Titanium
strcpy(eq->title, emu->title);
// eq->unknown0274 = emu->unknown0274;
eq->helm = emu->helm;
if (emu->race > 473)
if (emu->race > 474)
eq->race = 1;
else
eq->race = emu->race;
@@ -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
View File
@@ -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) {
+45 -24
View File
@@ -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}
};
@@ -76,13 +86,24 @@ void PathManager::LoadPaths()
constexpr int path_width = 0;
constexpr int break_length = 70;
std::cout << std::endl;
LogInfo("Loading server paths");
LogInfo("{}", Strings::Repeat("-", break_length));
for (const auto& [name, in_path] : paths) {
if (!in_path.empty()) {
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
View File
@@ -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
+9
View File
@@ -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"
-34
View File
@@ -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>
+14
View File
@@ -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
View File
@@ -1,6 +1,7 @@
#include <string>
#include <memory>
#include "process.h"
#include <fmt/format.h>
std::string Process::execute(const std::string &cmd)
{
+1767 -2012
View File
File diff suppressed because it is too large Load Diff
+89 -113
View File
@@ -21,103 +21,7 @@
#include "../common/types.h"
#include <string>
namespace Gender {
constexpr uint8 Male = 0;
constexpr uint8 Female = 1;
constexpr uint8 Neuter = 2;
}
//theres a big list straight from the client below.
#define HUMAN 1
#define BARBARIAN 2
#define ERUDITE 3
#define WOOD_ELF 4
#define HIGH_ELF 5
#define DARK_ELF 6
#define HALF_ELF 7
#define DWARF 8
#define TROLL 9
#define OGRE 10
#define HALFLING 11
#define GNOME 12
#define WEREWOLF 14
#define WOLF 42
#define BEAR 43
#define SKELETON 60
#define TIGER 63
#define ELEMENTAL 75
#define ALLIGATOR 91
#define OGGOK_CITIZEN 93
#define EYE_OF_ZOMM 108
#define WOLF_ELEMENTAL 120
#define INVISIBLE_MAN 127
#define IKSAR 128
#define VAHSHIR 130
#define CONTROLLED_BOAT 141
#define MINOR_ILL_OBJ 142
#define TREE 143
#define IKSAR_SKELETON 161
#define FROGLOK 330
// TODO: check all clients for (BYTE) usage of '/who all' class and remove FROGLOK2, if possible (330 - 74 = 256 .. WORD->BYTE conversion loss...)
#define FROGLOK2 74 // Not sure why /who all reports race as 74 for frogloks
#define FAIRY 473
#define DRAKKIN 522 // 32768
#define EMU_RACE_NPC 131069 // was 65533
#define EMU_RACE_PET 131070 // was 65534
#define EMU_RACE_UNKNOWN 131071 // was 65535
// player race values
#define PLAYER_RACE_UNKNOWN 0
#define PLAYER_RACE_HUMAN 1
#define PLAYER_RACE_BARBARIAN 2
#define PLAYER_RACE_ERUDITE 3
#define PLAYER_RACE_WOOD_ELF 4
#define PLAYER_RACE_HIGH_ELF 5
#define PLAYER_RACE_DARK_ELF 6
#define PLAYER_RACE_HALF_ELF 7
#define PLAYER_RACE_DWARF 8
#define PLAYER_RACE_TROLL 9
#define PLAYER_RACE_OGRE 10
#define PLAYER_RACE_HALFLING 11
#define PLAYER_RACE_GNOME 12
#define PLAYER_RACE_IKSAR 13
#define PLAYER_RACE_VAHSHIR 14
#define PLAYER_RACE_FROGLOK 15
#define PLAYER_RACE_DRAKKIN 16
#define PLAYER_RACE_COUNT 16
#define PLAYER_RACE_EMU_NPC 17
#define PLAYER_RACE_EMU_PET 18
#define PLAYER_RACE_EMU_COUNT 19
// player race bits
#define PLAYER_RACE_UNKNOWN_BIT 0
#define PLAYER_RACE_HUMAN_BIT 1
#define PLAYER_RACE_BARBARIAN_BIT 2
#define PLAYER_RACE_ERUDITE_BIT 4
#define PLAYER_RACE_WOOD_ELF_BIT 8
#define PLAYER_RACE_HIGH_ELF_BIT 16
#define PLAYER_RACE_DARK_ELF_BIT 32
#define PLAYER_RACE_HALF_ELF_BIT 64
#define PLAYER_RACE_DWARF_BIT 128
#define PLAYER_RACE_TROLL_BIT 256
#define PLAYER_RACE_OGRE_BIT 512
#define PLAYER_RACE_HALFLING_BIT 1024
#define PLAYER_RACE_GNOME_BIT 2048
#define PLAYER_RACE_IKSAR_BIT 4096
#define PLAYER_RACE_VAHSHIR_BIT 8192
#define PLAYER_RACE_FROGLOK_BIT 16384
#define PLAYER_RACE_DRAKKIN_BIT 32768
#define PLAYER_RACE_ALL_MASK 65535
const char* GetRaceIDName(uint16 race_id);
const char* GetPlayerRaceName(uint32 player_race_value);
const char* GetGenderName(uint32 gender_id);
bool IsPlayerRace(uint16 race_id);
@@ -127,25 +31,13 @@ uint32 GetPlayerRaceValue(uint16 race_id);
uint16 GetPlayerRaceBit(uint16 race_id);
uint16 GetRaceIDFromPlayerRaceValue(uint32 player_race_value);
uint16 GetRaceIDFromPlayerRaceBit(uint32 player_race_bit);
float GetRaceGenderDefaultHeight(int race, int gender);
// player race-/gender-based model feature validators
namespace PlayerAppearance
{
bool IsValidBeard(uint16 race_id, uint8 gender_id, uint8 beard_value, bool use_luclin = true);
bool IsValidBeardColor(uint16 race_id, uint8 gender_id, uint8 beard_color_value, bool use_luclin = true);
bool IsValidDetail(uint16 race_id, uint8 gender_id, uint32 detail_value, bool use_luclin = true);
bool IsValidEyeColor(uint16 race_id, uint8 gender_id, uint8 eye_color_value, bool use_luclin = true);
bool IsValidFace(uint16 race_id, uint8 gender_id, uint8 face_value, bool use_luclin = true);
bool IsValidHair(uint16 race_id, uint8 gender_id, uint8 hair_value, bool use_luclin = true);
bool IsValidHairColor(uint16 race_id, uint8 gender_id, uint8 hair_color_value, bool use_luclin = true);
bool IsValidHead(uint16 race_id, uint8 gender_id, uint8 head_value, bool use_luclin = true);
bool IsValidHeritage(uint16 race_id, uint8 gender_id, uint32 heritage_value, bool use_luclin = true);
bool IsValidTattoo(uint16 race_id, uint8 gender_id, uint32 tattoo_value, bool use_luclin = true);
bool IsValidTexture(uint16 race_id, uint8 gender_id, uint8 texture_value, bool use_luclin = true);
bool IsValidWoad(uint16 race_id, uint8 gender_id, uint8 woad_value, bool use_luclin = true);
namespace Gender {
constexpr uint8 Male = 0;
constexpr uint8 Female = 1;
constexpr uint8 Neuter = 2;
}
namespace Race {
@@ -884,8 +776,92 @@ namespace Race {
constexpr uint16 Pegasus3 = 732;
constexpr uint16 InteractiveObject = 2250;
constexpr uint16 Node = 2254;
}
constexpr uint16 ALL_RACES_BITMASK = 65535;
namespace RaceBitmask {
constexpr uint16 Unknown = 0;
constexpr uint16 Human = 1;
constexpr uint16 Barbarian = 2;
constexpr uint16 Erudite = 4;
constexpr uint16 WoodElf = 8;
constexpr uint16 HighElf = 16;
constexpr uint16 DarkElf = 32;
constexpr uint16 HalfElf = 64;
constexpr uint16 Dwarf = 128;
constexpr uint16 Troll = 256;
constexpr uint16 Ogre = 512;
constexpr uint16 Halfling = 1024;
constexpr uint16 Gnome = 2048;
constexpr uint16 Iksar = 4096;
constexpr uint16 VahShir = 8192;
constexpr uint16 Froglok = 16384;
constexpr uint16 Drakkin = 32768;
constexpr uint16 All = 65535;
}
namespace RaceIndex {
constexpr uint16 Human = 1;
constexpr uint16 Barbarian = 2;
constexpr uint16 Erudite = 3;
constexpr uint16 WoodElf = 4;
constexpr uint16 HighElf = 5;
constexpr uint16 DarkElf = 6;
constexpr uint16 HalfElf = 7;
constexpr uint16 Dwarf = 8;
constexpr uint16 Troll = 9;
constexpr uint16 Ogre = 10;
constexpr uint16 Halfling = 11;
constexpr uint16 Gnome = 12;
constexpr uint16 Iksar = 13;
constexpr uint16 VahShir = 14;
constexpr uint16 Froglok = 15;
constexpr uint16 Drakkin = 16;
}
namespace RaceAppearance {
bool IsValidBeard(uint16 race_id, uint8 gender_id, uint8 beard_value, bool use_luclin = true);
bool IsValidBeardColor(uint16 race_id, uint8 gender_id, uint8 beard_color_value, bool use_luclin = true);
bool IsValidDetail(uint16 race_id, uint8 gender_id, uint32 detail_value, bool use_luclin = true);
bool IsValidEyeColor(uint16 race_id, uint8 gender_id, uint8 eye_color_value, bool use_luclin = true);
bool IsValidFace(uint16 race_id, uint8 gender_id, uint8 face_value, bool use_luclin = true);
bool IsValidHair(uint16 race_id, uint8 gender_id, uint8 hair_value, bool use_luclin = true);
bool IsValidHairColor(uint16 race_id, uint8 gender_id, uint8 hair_color_value, bool use_luclin = true);
bool IsValidHeritage(uint16 race_id, uint8 gender_id, uint32 heritage_value, bool use_luclin = true);
bool IsValidTattoo(uint16 race_id, uint8 gender_id, uint32 tattoo_value, bool use_luclin = true);
bool IsValidWoad(uint16 race_id, uint8 gender_id, uint8 woad_value, bool use_luclin = true);
constexpr int HumanMale = (Race::Human << 8) | Gender::Male;
constexpr int HumanFemale = (Race::Human << 8) | Gender::Female;
constexpr int BarbarianMale = (Race::Barbarian << 8) | Gender::Male;
constexpr int BarbarianFemale = (Race::Barbarian << 8) | Gender::Female;
constexpr int EruditeMale = (Race::Erudite << 8) | Gender::Male;
constexpr int EruditeFemale = (Race::Erudite << 8) | Gender::Female;
constexpr int WoodElfMale = (Race::WoodElf << 8) | Gender::Male;
constexpr int WoodElfFemale = (Race::WoodElf << 8) | Gender::Female;
constexpr int HighElfMale = (Race::HighElf << 8) | Gender::Male;
constexpr int HighElfFemale = (Race::HighElf << 8) | Gender::Female;
constexpr int DarkElfMale = (Race::DarkElf << 8) | Gender::Male;
constexpr int DarkElfFemale = (Race::DarkElf << 8) | Gender::Female;
constexpr int HalfElfMale = (Race::HalfElf << 8) | Gender::Male;
constexpr int HalfElfFemale = (Race::HalfElf << 8) | Gender::Female;
constexpr int DwarfMale = (Race::Dwarf << 8) | Gender::Male;
constexpr int DwarfFemale = (Race::Dwarf << 8) | Gender::Female;
constexpr int TrollMale = (Race::Troll << 8) | Gender::Male;
constexpr int TrollFemale = (Race::Troll << 8) | Gender::Female;
constexpr int OgreMale = (Race::Ogre << 8) | Gender::Male;
constexpr int OgreFemale = (Race::Ogre << 8) | Gender::Female;
constexpr int HalflingMale = (Race::Halfling << 8) | Gender::Male;
constexpr int HalflingFemale = (Race::Halfling << 8) | Gender::Female;
constexpr int GnomeMale = (Race::Gnome << 8) | Gender::Male;
constexpr int GnomeFemale = (Race::Gnome << 8) | Gender::Female;
constexpr int IksarMale = (Race::Iksar << 8) | Gender::Male;
constexpr int IksarFemale = (Race::Iksar << 8) | Gender::Female;
constexpr int VahShirMale = (Race::VahShir << 8) | Gender::Male;
constexpr int VahShirFemale = (Race::VahShir << 8) | Gender::Female;
constexpr int FroglokMale = (Race::Froglok2 << 8) | Gender::Male;
constexpr int FroglokFemale = (Race::Froglok2 << 8) | Gender::Female;
constexpr int DrakkinMale = (Race::Drakkin << 8) | Gender::Male;
constexpr int DrakkinFemale = (Race::Drakkin << 8) | Gender::Female;
}
#endif
+6
View File
@@ -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));
@@ -30,6 +30,7 @@ public:
uint32_t aug_slot_5;
uint32_t aug_slot_6;
uint32_t quantity;
uint32_t evolve_amount;
};
static std::string PrimaryKey()
@@ -51,6 +52,7 @@ public:
"aug_slot_5",
"aug_slot_6",
"quantity",
"evolve_amount",
};
}
@@ -68,6 +70,7 @@ public:
"aug_slot_5",
"aug_slot_6",
"quantity",
"evolve_amount",
};
}
@@ -108,17 +111,18 @@ public:
{
CharacterParcelsContainers e{};
e.id = 0;
e.parcels_id = 0;
e.slot_id = 0;
e.item_id = 0;
e.aug_slot_1 = 0;
e.aug_slot_2 = 0;
e.aug_slot_3 = 0;
e.aug_slot_4 = 0;
e.aug_slot_5 = 0;
e.aug_slot_6 = 0;
e.quantity = 0;
e.id = 0;
e.parcels_id = 0;
e.slot_id = 0;
e.item_id = 0;
e.aug_slot_1 = 0;
e.aug_slot_2 = 0;
e.aug_slot_3 = 0;
e.aug_slot_4 = 0;
e.aug_slot_5 = 0;
e.aug_slot_6 = 0;
e.quantity = 0;
e.evolve_amount = 0;
return e;
}
@@ -155,17 +159,18 @@ public:
if (results.RowCount() == 1) {
CharacterParcelsContainers e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.parcels_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.slot_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.aug_slot_1 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.aug_slot_2 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.aug_slot_3 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.aug_slot_4 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.aug_slot_5 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.aug_slot_6 = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.parcels_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.slot_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.aug_slot_1 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.aug_slot_2 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.aug_slot_3 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.aug_slot_4 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.aug_slot_5 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.aug_slot_6 = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.evolve_amount = row[11] ? static_cast<uint32_t>(strtoul(row[11], nullptr, 10)) : 0;
return e;
}
@@ -209,6 +214,7 @@ public:
v.push_back(columns[8] + " = " + std::to_string(e.aug_slot_5));
v.push_back(columns[9] + " = " + std::to_string(e.aug_slot_6));
v.push_back(columns[10] + " = " + std::to_string(e.quantity));
v.push_back(columns[11] + " = " + std::to_string(e.evolve_amount));
auto results = db.QueryDatabase(
fmt::format(
@@ -241,6 +247,7 @@ public:
v.push_back(std::to_string(e.aug_slot_5));
v.push_back(std::to_string(e.aug_slot_6));
v.push_back(std::to_string(e.quantity));
v.push_back(std::to_string(e.evolve_amount));
auto results = db.QueryDatabase(
fmt::format(
@@ -281,6 +288,7 @@ public:
v.push_back(std::to_string(e.aug_slot_5));
v.push_back(std::to_string(e.aug_slot_6));
v.push_back(std::to_string(e.quantity));
v.push_back(std::to_string(e.evolve_amount));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -314,17 +322,18 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
CharacterParcelsContainers e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.parcels_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.slot_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.aug_slot_1 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.aug_slot_2 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.aug_slot_3 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.aug_slot_4 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.aug_slot_5 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.aug_slot_6 = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.parcels_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.slot_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.aug_slot_1 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.aug_slot_2 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.aug_slot_3 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.aug_slot_4 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.aug_slot_5 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.aug_slot_6 = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.evolve_amount = row[11] ? static_cast<uint32_t>(strtoul(row[11], nullptr, 10)) : 0;
all_entries.push_back(e);
}
@@ -349,17 +358,18 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
CharacterParcelsContainers e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.parcels_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.slot_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.aug_slot_1 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.aug_slot_2 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.aug_slot_3 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.aug_slot_4 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.aug_slot_5 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.aug_slot_6 = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.parcels_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.slot_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.aug_slot_1 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.aug_slot_2 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.aug_slot_3 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.aug_slot_4 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.aug_slot_5 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.aug_slot_6 = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.evolve_amount = row[11] ? static_cast<uint32_t>(strtoul(row[11], nullptr, 10)) : 0;
all_entries.push_back(e);
}
@@ -445,6 +455,7 @@ public:
v.push_back(std::to_string(e.aug_slot_5));
v.push_back(std::to_string(e.aug_slot_6));
v.push_back(std::to_string(e.quantity));
v.push_back(std::to_string(e.evolve_amount));
auto results = db.QueryDatabase(
fmt::format(
@@ -478,6 +489,7 @@ public:
v.push_back(std::to_string(e.aug_slot_5));
v.push_back(std::to_string(e.aug_slot_6));
v.push_back(std::to_string(e.quantity));
v.push_back(std::to_string(e.evolve_amount));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -30,6 +30,7 @@ public:
uint32_t aug_slot_6;
uint32_t slot_id;
uint32_t quantity;
uint32_t evolve_amount;
std::string from_name;
std::string note;
time_t sent_date;
@@ -54,6 +55,7 @@ public:
"aug_slot_6",
"slot_id",
"quantity",
"evolve_amount",
"from_name",
"note",
"sent_date",
@@ -74,6 +76,7 @@ public:
"aug_slot_6",
"slot_id",
"quantity",
"evolve_amount",
"from_name",
"note",
"UNIX_TIMESTAMP(sent_date)",
@@ -117,20 +120,21 @@ public:
{
CharacterParcels e{};
e.id = 0;
e.char_id = 0;
e.item_id = 0;
e.aug_slot_1 = 0;
e.aug_slot_2 = 0;
e.aug_slot_3 = 0;
e.aug_slot_4 = 0;
e.aug_slot_5 = 0;
e.aug_slot_6 = 0;
e.slot_id = 0;
e.quantity = 0;
e.from_name = "";
e.note = "";
e.sent_date = 0;
e.id = 0;
e.char_id = 0;
e.item_id = 0;
e.aug_slot_1 = 0;
e.aug_slot_2 = 0;
e.aug_slot_3 = 0;
e.aug_slot_4 = 0;
e.aug_slot_5 = 0;
e.aug_slot_6 = 0;
e.slot_id = 0;
e.quantity = 0;
e.evolve_amount = 0;
e.from_name = "";
e.note = "";
e.sent_date = 0;
return e;
}
@@ -167,20 +171,21 @@ public:
if (results.RowCount() == 1) {
CharacterParcels e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.aug_slot_1 = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.aug_slot_2 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.aug_slot_3 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.aug_slot_4 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.aug_slot_5 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.aug_slot_6 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.slot_id = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.from_name = row[11] ? row[11] : "";
e.note = row[12] ? row[12] : "";
e.sent_date = strtoll(row[13] ? row[13] : "-1", nullptr, 10);
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.aug_slot_1 = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.aug_slot_2 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.aug_slot_3 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.aug_slot_4 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.aug_slot_5 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.aug_slot_6 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.slot_id = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.evolve_amount = row[11] ? static_cast<uint32_t>(strtoul(row[11], nullptr, 10)) : 0;
e.from_name = row[12] ? row[12] : "";
e.note = row[13] ? row[13] : "";
e.sent_date = strtoll(row[14] ? row[14] : "-1", nullptr, 10);
return e;
}
@@ -224,9 +229,10 @@ public:
v.push_back(columns[8] + " = " + std::to_string(e.aug_slot_6));
v.push_back(columns[9] + " = " + std::to_string(e.slot_id));
v.push_back(columns[10] + " = " + std::to_string(e.quantity));
v.push_back(columns[11] + " = '" + Strings::Escape(e.from_name) + "'");
v.push_back(columns[12] + " = '" + Strings::Escape(e.note) + "'");
v.push_back(columns[13] + " = FROM_UNIXTIME(" + (e.sent_date > 0 ? std::to_string(e.sent_date) : "null") + ")");
v.push_back(columns[11] + " = " + std::to_string(e.evolve_amount));
v.push_back(columns[12] + " = '" + Strings::Escape(e.from_name) + "'");
v.push_back(columns[13] + " = '" + Strings::Escape(e.note) + "'");
v.push_back(columns[14] + " = FROM_UNIXTIME(" + (e.sent_date > 0 ? std::to_string(e.sent_date) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
@@ -259,6 +265,7 @@ public:
v.push_back(std::to_string(e.aug_slot_6));
v.push_back(std::to_string(e.slot_id));
v.push_back(std::to_string(e.quantity));
v.push_back(std::to_string(e.evolve_amount));
v.push_back("'" + Strings::Escape(e.from_name) + "'");
v.push_back("'" + Strings::Escape(e.note) + "'");
v.push_back("FROM_UNIXTIME(" + (e.sent_date > 0 ? std::to_string(e.sent_date) : "null") + ")");
@@ -302,6 +309,7 @@ public:
v.push_back(std::to_string(e.aug_slot_6));
v.push_back(std::to_string(e.slot_id));
v.push_back(std::to_string(e.quantity));
v.push_back(std::to_string(e.evolve_amount));
v.push_back("'" + Strings::Escape(e.from_name) + "'");
v.push_back("'" + Strings::Escape(e.note) + "'");
v.push_back("FROM_UNIXTIME(" + (e.sent_date > 0 ? std::to_string(e.sent_date) : "null") + ")");
@@ -338,20 +346,21 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
CharacterParcels e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.aug_slot_1 = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.aug_slot_2 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.aug_slot_3 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.aug_slot_4 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.aug_slot_5 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.aug_slot_6 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.slot_id = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.from_name = row[11] ? row[11] : "";
e.note = row[12] ? row[12] : "";
e.sent_date = strtoll(row[13] ? row[13] : "-1", nullptr, 10);
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.aug_slot_1 = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.aug_slot_2 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.aug_slot_3 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.aug_slot_4 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.aug_slot_5 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.aug_slot_6 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.slot_id = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.evolve_amount = row[11] ? static_cast<uint32_t>(strtoul(row[11], nullptr, 10)) : 0;
e.from_name = row[12] ? row[12] : "";
e.note = row[13] ? row[13] : "";
e.sent_date = strtoll(row[14] ? row[14] : "-1", nullptr, 10);
all_entries.push_back(e);
}
@@ -376,20 +385,21 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
CharacterParcels e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.aug_slot_1 = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.aug_slot_2 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.aug_slot_3 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.aug_slot_4 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.aug_slot_5 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.aug_slot_6 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.slot_id = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.from_name = row[11] ? row[11] : "";
e.note = row[12] ? row[12] : "";
e.sent_date = strtoll(row[13] ? row[13] : "-1", nullptr, 10);
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.aug_slot_1 = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.aug_slot_2 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.aug_slot_3 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.aug_slot_4 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.aug_slot_5 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.aug_slot_6 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.slot_id = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.evolve_amount = row[11] ? static_cast<uint32_t>(strtoul(row[11], nullptr, 10)) : 0;
e.from_name = row[12] ? row[12] : "";
e.note = row[13] ? row[13] : "";
e.sent_date = strtoll(row[14] ? row[14] : "-1", nullptr, 10);
all_entries.push_back(e);
}
@@ -475,6 +485,7 @@ public:
v.push_back(std::to_string(e.aug_slot_6));
v.push_back(std::to_string(e.slot_id));
v.push_back(std::to_string(e.quantity));
v.push_back(std::to_string(e.evolve_amount));
v.push_back("'" + Strings::Escape(e.from_name) + "'");
v.push_back("'" + Strings::Escape(e.note) + "'");
v.push_back("FROM_UNIXTIME(" + (e.sent_date > 0 ? std::to_string(e.sent_date) : "null") + ")");
@@ -511,6 +522,7 @@ public:
v.push_back(std::to_string(e.aug_slot_6));
v.push_back(std::to_string(e.slot_id));
v.push_back(std::to_string(e.quantity));
v.push_back(std::to_string(e.evolve_amount));
v.push_back("'" + Strings::Escape(e.from_name) + "'");
v.push_back("'" + Strings::Escape(e.note) + "'");
v.push_back("FROM_UNIXTIME(" + (e.sent_date > 0 ? std::to_string(e.sent_date) : "null") + ")");

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