Compare commits

...

28 Commits

Author SHA1 Message Date
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
156 changed files with 6682 additions and 8557 deletions
+135
View File
@@ -1,3 +1,138 @@
## [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
+5 -6
View File
@@ -22,6 +22,7 @@ SET(common_sources
dbcore.cpp
deity.cpp
dynamic_zone_base.cpp
dynamic_zone_lockout.cpp
emu_constants.cpp
emu_limits.cpp
emu_opcodes.cpp
@@ -40,7 +41,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
@@ -213,10 +213,9 @@ 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
@@ -410,10 +409,9 @@ 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
@@ -561,6 +559,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
@@ -587,7 +586,6 @@ SET(common_headers
events/player_events.h
event_sub.h
evolving_items.h
expedition_lockout_timer.h
extprofile.h
faction.h
file.h
@@ -647,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
+101 -1
View File
@@ -6770,7 +6770,107 @@ 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_2024_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
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
},
// -- template; copy/paste this when you need to create a new entry
// ManifestEntry{
// .version = 9228,
+2 -1
View File
@@ -30,7 +30,7 @@ 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 "database.h"
@@ -479,6 +479,7 @@ 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));
}
void Database::FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 group_id)
+1 -2
View File
@@ -311,10 +311,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",
+293 -64
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)
{
@@ -93,13 +95,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 +123,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 +203,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 +236,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 +257,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 +354,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 +531,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 +643,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 +699,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;
};
+2
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;
-19
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,
+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)
{ }
};
+53 -44
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;
@@ -5529,56 +5529,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
+3 -4
View File
@@ -81,14 +81,13 @@ void PlayerEventLogs::Init()
if (!settings_to_insert.empty()) {
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();
}
}
@@ -402,7 +401,7 @@ void PlayerEventLogs::ProcessBatchQueue()
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);
}
}
}
+49 -5
View File
@@ -65,6 +65,9 @@ namespace PlayerEvent {
BARTER_TRANSACTION,
SPEECH,
EVOLVE_ITEM,
GUILD_BANK_DEPOSIT,
GUILD_BANK_WITHDRAWAL,
GUILD_BANK_MOVE_TO_BANK_AREA,
MAX // dont remove
};
@@ -129,7 +132,10 @@ namespace PlayerEvent {
"Parcel Prune Routine",
"Barter Transaction",
"Player Speech",
"Evolve Item Update"
"Evolve Item Update",
"Guild Bank Item Deposit",
"Guild Bank Item Withdrawal",
"Guild Bank Move From Deposit Area to Bank Area"
};
// Generic struct used by all events
@@ -875,8 +881,9 @@ namespace PlayerEvent {
uint32 trader_id;
std::string trader_name;
uint32 price;
uint32 charges;
uint32 total_cost;
uint32 quantity;
int32 charges;
uint64 total_cost;
uint64 player_money_balance;
@@ -896,6 +903,7 @@ namespace PlayerEvent {
CEREAL_NVP(trader_id),
CEREAL_NVP(trader_name),
CEREAL_NVP(price),
CEREAL_NVP(quantity),
CEREAL_NVP(charges),
CEREAL_NVP(total_cost),
CEREAL_NVP(player_money_balance)
@@ -915,8 +923,9 @@ namespace PlayerEvent {
uint32 buyer_id;
std::string buyer_name;
uint32 price;
uint32 charges;
uint32 total_cost;
uint32 quantity;
int32 charges;
uint64 total_cost;
uint64 player_money_balance;
@@ -936,6 +945,7 @@ namespace PlayerEvent {
CEREAL_NVP(buyer_id),
CEREAL_NVP(buyer_name),
CEREAL_NVP(price),
CEREAL_NVP(quantity),
CEREAL_NVP(charges),
CEREAL_NVP(total_cost),
CEREAL_NVP(player_money_balance)
@@ -1145,6 +1155,7 @@ namespace PlayerEvent {
uint32 augment_5_id;
uint32 augment_6_id;
uint32 quantity;
int32 charges;
std::string from_player_name;
std::string to_player_name;
uint32 sent_date;
@@ -1162,6 +1173,7 @@ namespace PlayerEvent {
CEREAL_NVP(augment_5_id),
CEREAL_NVP(augment_6_id),
CEREAL_NVP(quantity),
CEREAL_NVP(charges),
CEREAL_NVP(from_player_name),
CEREAL_NVP(to_player_name),
CEREAL_NVP(sent_date)
@@ -1274,6 +1286,38 @@ namespace PlayerEvent {
);
}
};
struct GuildBankTransaction {
uint32 char_id;
uint32 guild_id;
uint32 item_id;
uint32 aug_slot_one;
uint32 aug_slot_two;
uint32 aug_slot_three;
uint32 aug_slot_four;
uint32 aug_slot_five;
uint32 aug_slot_six;
uint32 quantity;
uint32 permission;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(char_id),
CEREAL_NVP(guild_id),
CEREAL_NVP(item_id),
CEREAL_NVP(aug_slot_one),
CEREAL_NVP(aug_slot_two),
CEREAL_NVP(aug_slot_three),
CEREAL_NVP(aug_slot_four),
CEREAL_NVP(aug_slot_five),
CEREAL_NVP(aug_slot_six),
CEREAL_NVP(quantity)
);
}
};
}
#endif //EQEMU_PLAYER_EVENTS_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
+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
+16 -16
View File
@@ -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:
+13 -16
View File
@@ -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:
+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
@@ -23,10 +23,12 @@ public:
std::string key_;
std::string value;
uint32_t expires;
int64_t account_id;
int64_t character_id;
int64_t npc_id;
int64_t bot_id;
uint64_t account_id;
uint64_t character_id;
uint32_t npc_id;
uint32_t bot_id;
uint16_t zone_id;
uint16_t instance_id;
// cereal
template<class Archive>
@@ -40,7 +42,9 @@ public:
CEREAL_NVP(account_id),
CEREAL_NVP(character_id),
CEREAL_NVP(npc_id),
CEREAL_NVP(bot_id)
CEREAL_NVP(bot_id),
CEREAL_NVP(zone_id),
CEREAL_NVP(instance_id)
);
}
};
@@ -61,6 +65,8 @@ public:
"character_id",
"npc_id",
"bot_id",
"zone_id",
"instance_id",
};
}
@@ -75,6 +81,8 @@ public:
"character_id",
"npc_id",
"bot_id",
"zone_id",
"instance_id",
};
}
@@ -123,6 +131,8 @@ public:
e.character_id = 0;
e.npc_id = 0;
e.bot_id = 0;
e.zone_id = 0;
e.instance_id = 0;
return e;
}
@@ -163,10 +173,12 @@ public:
e.key_ = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : "";
e.expires = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.account_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
e.character_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.npc_id = row[6] ? strtoll(row[6], nullptr, 10) : 0;
e.bot_id = row[7] ? strtoll(row[7], nullptr, 10) : 0;
e.account_id = row[4] ? strtoull(row[4], nullptr, 10) : 0;
e.character_id = row[5] ? strtoull(row[5], nullptr, 10) : 0;
e.npc_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.bot_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.zone_id = row[8] ? static_cast<uint16_t>(strtoul(row[8], nullptr, 10)) : 0;
e.instance_id = row[9] ? static_cast<uint16_t>(strtoul(row[9], nullptr, 10)) : 0;
return e;
}
@@ -207,6 +219,8 @@ public:
v.push_back(columns[5] + " = " + std::to_string(e.character_id));
v.push_back(columns[6] + " = " + std::to_string(e.npc_id));
v.push_back(columns[7] + " = " + std::to_string(e.bot_id));
v.push_back(columns[8] + " = " + std::to_string(e.zone_id));
v.push_back(columns[9] + " = " + std::to_string(e.instance_id));
auto results = db.QueryDatabase(
fmt::format(
@@ -236,6 +250,8 @@ public:
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.npc_id));
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.zone_id));
v.push_back(std::to_string(e.instance_id));
auto results = db.QueryDatabase(
fmt::format(
@@ -273,6 +289,8 @@ public:
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.npc_id));
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.zone_id));
v.push_back(std::to_string(e.instance_id));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -310,10 +328,12 @@ public:
e.key_ = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : "";
e.expires = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.account_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
e.character_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.npc_id = row[6] ? strtoll(row[6], nullptr, 10) : 0;
e.bot_id = row[7] ? strtoll(row[7], nullptr, 10) : 0;
e.account_id = row[4] ? strtoull(row[4], nullptr, 10) : 0;
e.character_id = row[5] ? strtoull(row[5], nullptr, 10) : 0;
e.npc_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.bot_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.zone_id = row[8] ? static_cast<uint16_t>(strtoul(row[8], nullptr, 10)) : 0;
e.instance_id = row[9] ? static_cast<uint16_t>(strtoul(row[9], nullptr, 10)) : 0;
all_entries.push_back(e);
}
@@ -342,10 +362,12 @@ public:
e.key_ = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : "";
e.expires = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.account_id = row[4] ? strtoll(row[4], nullptr, 10) : 0;
e.character_id = row[5] ? strtoll(row[5], nullptr, 10) : 0;
e.npc_id = row[6] ? strtoll(row[6], nullptr, 10) : 0;
e.bot_id = row[7] ? strtoll(row[7], nullptr, 10) : 0;
e.account_id = row[4] ? strtoull(row[4], nullptr, 10) : 0;
e.character_id = row[5] ? strtoull(row[5], nullptr, 10) : 0;
e.npc_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.bot_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.zone_id = row[8] ? static_cast<uint16_t>(strtoul(row[8], nullptr, 10)) : 0;
e.instance_id = row[9] ? static_cast<uint16_t>(strtoul(row[9], nullptr, 10)) : 0;
all_entries.push_back(e);
}
@@ -428,6 +450,8 @@ public:
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.npc_id));
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.zone_id));
v.push_back(std::to_string(e.instance_id));
auto results = db.QueryDatabase(
fmt::format(
@@ -458,6 +482,8 @@ public:
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.npc_id));
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.zone_id));
v.push_back(std::to_string(e.instance_id));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -9,18 +9,18 @@
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_EXPEDITION_LOCKOUTS_REPOSITORY_H
#define EQEMU_BASE_EXPEDITION_LOCKOUTS_REPOSITORY_H
#ifndef EQEMU_BASE_DYNAMIC_ZONE_LOCKOUTS_REPOSITORY_H
#define EQEMU_BASE_DYNAMIC_ZONE_LOCKOUTS_REPOSITORY_H
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
class BaseExpeditionLockoutsRepository {
class BaseDynamicZoneLockoutsRepository {
public:
struct ExpeditionLockouts {
struct DynamicZoneLockouts {
uint32_t id;
uint32_t expedition_id;
uint32_t dynamic_zone_id;
std::string event_name;
time_t expire_time;
uint32_t duration;
@@ -36,7 +36,7 @@ public:
{
return {
"id",
"expedition_id",
"dynamic_zone_id",
"event_name",
"expire_time",
"duration",
@@ -48,7 +48,7 @@ public:
{
return {
"id",
"expedition_id",
"dynamic_zone_id",
"event_name",
"UNIX_TIMESTAMP(expire_time)",
"duration",
@@ -68,7 +68,7 @@ public:
static std::string TableName()
{
return std::string("expedition_lockouts");
return std::string("dynamic_zone_lockouts");
}
static std::string BaseSelect()
@@ -89,12 +89,12 @@ public:
);
}
static ExpeditionLockouts NewEntity()
static DynamicZoneLockouts NewEntity()
{
ExpeditionLockouts e{};
DynamicZoneLockouts e{};
e.id = 0;
e.expedition_id = 0;
e.dynamic_zone_id = 0;
e.event_name = "";
e.expire_time = std::time(nullptr);
e.duration = 0;
@@ -103,23 +103,23 @@ public:
return e;
}
static ExpeditionLockouts GetExpeditionLockouts(
const std::vector<ExpeditionLockouts> &expedition_lockoutss,
int expedition_lockouts_id
static DynamicZoneLockouts GetDynamicZoneLockouts(
const std::vector<DynamicZoneLockouts> &dynamic_zone_lockoutss,
int dynamic_zone_lockouts_id
)
{
for (auto &expedition_lockouts : expedition_lockoutss) {
if (expedition_lockouts.id == expedition_lockouts_id) {
return expedition_lockouts;
for (auto &dynamic_zone_lockouts : dynamic_zone_lockoutss) {
if (dynamic_zone_lockouts.id == dynamic_zone_lockouts_id) {
return dynamic_zone_lockouts;
}
}
return NewEntity();
}
static ExpeditionLockouts FindOne(
static DynamicZoneLockouts FindOne(
Database& db,
int expedition_lockouts_id
int dynamic_zone_lockouts_id
)
{
auto results = db.QueryDatabase(
@@ -127,16 +127,16 @@ public:
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
expedition_lockouts_id
dynamic_zone_lockouts_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
ExpeditionLockouts e{};
DynamicZoneLockouts e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.expedition_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.dynamic_zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.event_name = row[2] ? row[2] : "";
e.expire_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10);
e.duration = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
@@ -150,7 +150,7 @@ public:
static int DeleteOne(
Database& db,
int expedition_lockouts_id
int dynamic_zone_lockouts_id
)
{
auto results = db.QueryDatabase(
@@ -158,7 +158,7 @@ public:
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
expedition_lockouts_id
dynamic_zone_lockouts_id
)
);
@@ -167,14 +167,14 @@ public:
static int UpdateOne(
Database& db,
const ExpeditionLockouts &e
const DynamicZoneLockouts &e
)
{
std::vector<std::string> v;
auto columns = Columns();
v.push_back(columns[1] + " = " + std::to_string(e.expedition_id));
v.push_back(columns[1] + " = " + std::to_string(e.dynamic_zone_id));
v.push_back(columns[2] + " = '" + Strings::Escape(e.event_name) + "'");
v.push_back(columns[3] + " = FROM_UNIXTIME(" + (e.expire_time > 0 ? std::to_string(e.expire_time) : "null") + ")");
v.push_back(columns[4] + " = " + std::to_string(e.duration));
@@ -193,15 +193,15 @@ public:
return (results.Success() ? results.RowsAffected() : 0);
}
static ExpeditionLockouts InsertOne(
static DynamicZoneLockouts InsertOne(
Database& db,
ExpeditionLockouts e
DynamicZoneLockouts e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.expedition_id));
v.push_back(std::to_string(e.dynamic_zone_id));
v.push_back("'" + Strings::Escape(e.event_name) + "'");
v.push_back("FROM_UNIXTIME(" + (e.expire_time > 0 ? std::to_string(e.expire_time) : "null") + ")");
v.push_back(std::to_string(e.duration));
@@ -227,7 +227,7 @@ public:
static int InsertMany(
Database& db,
const std::vector<ExpeditionLockouts> &entries
const std::vector<DynamicZoneLockouts> &entries
)
{
std::vector<std::string> insert_chunks;
@@ -236,7 +236,7 @@ public:
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.expedition_id));
v.push_back(std::to_string(e.dynamic_zone_id));
v.push_back("'" + Strings::Escape(e.event_name) + "'");
v.push_back("FROM_UNIXTIME(" + (e.expire_time > 0 ? std::to_string(e.expire_time) : "null") + ")");
v.push_back(std::to_string(e.duration));
@@ -258,9 +258,9 @@ public:
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<ExpeditionLockouts> All(Database& db)
static std::vector<DynamicZoneLockouts> All(Database& db)
{
std::vector<ExpeditionLockouts> all_entries;
std::vector<DynamicZoneLockouts> all_entries;
auto results = db.QueryDatabase(
fmt::format(
@@ -272,10 +272,10 @@ public:
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
ExpeditionLockouts e{};
DynamicZoneLockouts e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.expedition_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.dynamic_zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.event_name = row[2] ? row[2] : "";
e.expire_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10);
e.duration = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
@@ -287,9 +287,9 @@ public:
return all_entries;
}
static std::vector<ExpeditionLockouts> GetWhere(Database& db, const std::string &where_filter)
static std::vector<DynamicZoneLockouts> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<ExpeditionLockouts> all_entries;
std::vector<DynamicZoneLockouts> all_entries;
auto results = db.QueryDatabase(
fmt::format(
@@ -302,10 +302,10 @@ public:
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
ExpeditionLockouts e{};
DynamicZoneLockouts e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.expedition_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.dynamic_zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.event_name = row[2] ? row[2] : "";
e.expire_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10);
e.duration = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
@@ -379,13 +379,13 @@ public:
static int ReplaceOne(
Database& db,
const ExpeditionLockouts &e
const DynamicZoneLockouts &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.expedition_id));
v.push_back(std::to_string(e.dynamic_zone_id));
v.push_back("'" + Strings::Escape(e.event_name) + "'");
v.push_back("FROM_UNIXTIME(" + (e.expire_time > 0 ? std::to_string(e.expire_time) : "null") + ")");
v.push_back(std::to_string(e.duration));
@@ -404,7 +404,7 @@ public:
static int ReplaceMany(
Database& db,
const std::vector<ExpeditionLockouts> &entries
const std::vector<DynamicZoneLockouts> &entries
)
{
std::vector<std::string> insert_chunks;
@@ -413,7 +413,7 @@ public:
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.expedition_id));
v.push_back(std::to_string(e.dynamic_zone_id));
v.push_back("'" + Strings::Escape(e.event_name) + "'");
v.push_back("FROM_UNIXTIME(" + (e.expire_time > 0 ? std::to_string(e.expire_time) : "null") + ")");
v.push_back(std::to_string(e.duration));
@@ -436,4 +436,4 @@ public:
}
};
#endif //EQEMU_BASE_EXPEDITION_LOCKOUTS_REPOSITORY_H
#endif //EQEMU_BASE_DYNAMIC_ZONE_LOCKOUTS_REPOSITORY_H
@@ -42,6 +42,8 @@ public:
float zone_in_z;
float zone_in_heading;
uint8_t has_zone_in;
int8_t is_locked;
int8_t add_replay;
};
static std::string PrimaryKey()
@@ -75,6 +77,8 @@ public:
"zone_in_z",
"zone_in_heading",
"has_zone_in",
"is_locked",
"add_replay",
};
}
@@ -104,6 +108,8 @@ public:
"zone_in_z",
"zone_in_heading",
"has_zone_in",
"is_locked",
"add_replay",
};
}
@@ -167,6 +173,8 @@ public:
e.zone_in_z = 0;
e.zone_in_heading = 0;
e.has_zone_in = 0;
e.is_locked = 0;
e.add_replay = 1;
return e;
}
@@ -226,6 +234,8 @@ public:
e.zone_in_z = row[20] ? strtof(row[20], nullptr) : 0;
e.zone_in_heading = row[21] ? strtof(row[21], nullptr) : 0;
e.has_zone_in = row[22] ? static_cast<uint8_t>(strtoul(row[22], nullptr, 10)) : 0;
e.is_locked = row[23] ? static_cast<int8_t>(atoi(row[23])) : 0;
e.add_replay = row[24] ? static_cast<int8_t>(atoi(row[24])) : 1;
return e;
}
@@ -281,6 +291,8 @@ public:
v.push_back(columns[20] + " = " + std::to_string(e.zone_in_z));
v.push_back(columns[21] + " = " + std::to_string(e.zone_in_heading));
v.push_back(columns[22] + " = " + std::to_string(e.has_zone_in));
v.push_back(columns[23] + " = " + std::to_string(e.is_locked));
v.push_back(columns[24] + " = " + std::to_string(e.add_replay));
auto results = db.QueryDatabase(
fmt::format(
@@ -325,6 +337,8 @@ public:
v.push_back(std::to_string(e.zone_in_z));
v.push_back(std::to_string(e.zone_in_heading));
v.push_back(std::to_string(e.has_zone_in));
v.push_back(std::to_string(e.is_locked));
v.push_back(std::to_string(e.add_replay));
auto results = db.QueryDatabase(
fmt::format(
@@ -377,6 +391,8 @@ public:
v.push_back(std::to_string(e.zone_in_z));
v.push_back(std::to_string(e.zone_in_heading));
v.push_back(std::to_string(e.has_zone_in));
v.push_back(std::to_string(e.is_locked));
v.push_back(std::to_string(e.add_replay));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -433,6 +449,8 @@ public:
e.zone_in_z = row[20] ? strtof(row[20], nullptr) : 0;
e.zone_in_heading = row[21] ? strtof(row[21], nullptr) : 0;
e.has_zone_in = row[22] ? static_cast<uint8_t>(strtoul(row[22], nullptr, 10)) : 0;
e.is_locked = row[23] ? static_cast<int8_t>(atoi(row[23])) : 0;
e.add_replay = row[24] ? static_cast<int8_t>(atoi(row[24])) : 1;
all_entries.push_back(e);
}
@@ -480,6 +498,8 @@ public:
e.zone_in_z = row[20] ? strtof(row[20], nullptr) : 0;
e.zone_in_heading = row[21] ? strtof(row[21], nullptr) : 0;
e.has_zone_in = row[22] ? static_cast<uint8_t>(strtoul(row[22], nullptr, 10)) : 0;
e.is_locked = row[23] ? static_cast<int8_t>(atoi(row[23])) : 0;
e.add_replay = row[24] ? static_cast<int8_t>(atoi(row[24])) : 1;
all_entries.push_back(e);
}
@@ -577,6 +597,8 @@ public:
v.push_back(std::to_string(e.zone_in_z));
v.push_back(std::to_string(e.zone_in_heading));
v.push_back(std::to_string(e.has_zone_in));
v.push_back(std::to_string(e.is_locked));
v.push_back(std::to_string(e.add_replay));
auto results = db.QueryDatabase(
fmt::format(
@@ -622,6 +644,8 @@ public:
v.push_back(std::to_string(e.zone_in_z));
v.push_back(std::to_string(e.zone_in_heading));
v.push_back(std::to_string(e.has_zone_in));
v.push_back(std::to_string(e.is_locked));
v.push_back(std::to_string(e.add_replay));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -1,415 +0,0 @@
/**
* DO NOT MODIFY THIS FILE
*
* This repository was automatically generated and is NOT to be modified directly.
* Any repository modifications are meant to be made to the repository extending the base.
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_EXPEDITIONS_REPOSITORY_H
#define EQEMU_BASE_EXPEDITIONS_REPOSITORY_H
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
class BaseExpeditionsRepository {
public:
struct Expeditions {
uint32_t id;
uint32_t dynamic_zone_id;
uint8_t add_replay_on_join;
uint8_t is_locked;
};
static std::string PrimaryKey()
{
return std::string("id");
}
static std::vector<std::string> Columns()
{
return {
"id",
"dynamic_zone_id",
"add_replay_on_join",
"is_locked",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"id",
"dynamic_zone_id",
"add_replay_on_join",
"is_locked",
};
}
static std::string ColumnsRaw()
{
return std::string(Strings::Implode(", ", Columns()));
}
static std::string SelectColumnsRaw()
{
return std::string(Strings::Implode(", ", SelectColumns()));
}
static std::string TableName()
{
return std::string("expeditions");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static Expeditions NewEntity()
{
Expeditions e{};
e.id = 0;
e.dynamic_zone_id = 0;
e.add_replay_on_join = 1;
e.is_locked = 0;
return e;
}
static Expeditions GetExpeditions(
const std::vector<Expeditions> &expeditionss,
int expeditions_id
)
{
for (auto &expeditions : expeditionss) {
if (expeditions.id == expeditions_id) {
return expeditions;
}
}
return NewEntity();
}
static Expeditions FindOne(
Database& db,
int expeditions_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
expeditions_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
Expeditions e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.dynamic_zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.add_replay_on_join = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 1;
e.is_locked = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
return e;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int expeditions_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
expeditions_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
const Expeditions &e
)
{
std::vector<std::string> v;
auto columns = Columns();
v.push_back(columns[1] + " = " + std::to_string(e.dynamic_zone_id));
v.push_back(columns[2] + " = " + std::to_string(e.add_replay_on_join));
v.push_back(columns[3] + " = " + std::to_string(e.is_locked));
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static Expeditions InsertOne(
Database& db,
Expeditions e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.dynamic_zone_id));
v.push_back(std::to_string(e.add_replay_on_join));
v.push_back(std::to_string(e.is_locked));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
Strings::Implode(",", v)
)
);
if (results.Success()) {
e.id = results.LastInsertedID();
return e;
}
e = NewEntity();
return e;
}
static int InsertMany(
Database& db,
const std::vector<Expeditions> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.dynamic_zone_id));
v.push_back(std::to_string(e.add_replay_on_join));
v.push_back(std::to_string(e.is_locked));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<Expeditions> All(Database& db)
{
std::vector<Expeditions> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
Expeditions e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.dynamic_zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.add_replay_on_join = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 1;
e.is_locked = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
all_entries.push_back(e);
}
return all_entries;
}
static std::vector<Expeditions> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<Expeditions> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {}",
BaseSelect(),
where_filter
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
Expeditions e{};
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.dynamic_zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.add_replay_on_join = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 1;
e.is_locked = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
all_entries.push_back(e);
}
return all_entries;
}
static int DeleteWhere(Database& db, const std::string &where_filter)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {}",
TableName(),
where_filter
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int Truncate(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"TRUNCATE TABLE {}",
TableName()
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int64 GetMaxId(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COALESCE(MAX({}), 0) FROM {}",
PrimaryKey(),
TableName()
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static int64 Count(Database& db, const std::string &where_filter = "")
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COUNT(*) FROM {} {}",
TableName(),
(where_filter.empty() ? "" : "WHERE " + where_filter)
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const Expeditions &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.dynamic_zone_id));
v.push_back(std::to_string(e.add_replay_on_join));
v.push_back(std::to_string(e.is_locked));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<Expeditions> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.dynamic_zone_id));
v.push_back(std::to_string(e.add_replay_on_join));
v.push_back(std::to_string(e.is_locked));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_EXPEDITIONS_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_GUILD_BANK_REPOSITORY_H
@@ -16,19 +16,24 @@
#include "../../strings.h"
#include <ctime>
class BaseGuildBankRepository {
public:
struct GuildBank {
uint32_t id;
uint32_t guildid;
uint32_t guild_id;
uint8_t area;
uint32_t slot;
uint32_t itemid;
uint32_t qty;
uint32_t item_id;
uint32_t augment_one_id;
uint32_t augment_two_id;
uint32_t augment_three_id;
uint32_t augment_four_id;
uint32_t augment_five_id;
uint32_t augment_six_id;
int32_t quantity;
std::string donator;
uint8_t permissions;
std::string whofor;
std::string who_for;
};
static std::string PrimaryKey()
@@ -40,14 +45,20 @@ public:
{
return {
"id",
"guildid",
"guild_id",
"area",
"slot",
"itemid",
"qty",
"item_id",
"augment_one_id",
"augment_two_id",
"augment_three_id",
"augment_four_id",
"augment_five_id",
"augment_six_id",
"quantity",
"donator",
"permissions",
"whofor",
"who_for",
};
}
@@ -55,14 +66,20 @@ public:
{
return {
"id",
"guildid",
"guild_id",
"area",
"slot",
"itemid",
"qty",
"item_id",
"augment_one_id",
"augment_two_id",
"augment_three_id",
"augment_four_id",
"augment_five_id",
"augment_six_id",
"quantity",
"donator",
"permissions",
"whofor",
"who_for",
};
}
@@ -103,15 +120,21 @@ public:
{
GuildBank e{};
e.id = 0;
e.guildid = 0;
e.area = 0;
e.slot = 0;
e.itemid = 0;
e.qty = 0;
e.donator = "";
e.permissions = 0;
e.whofor = "";
e.id = 0;
e.guild_id = 0;
e.area = 0;
e.slot = 0;
e.item_id = 0;
e.augment_one_id = 0;
e.augment_two_id = 0;
e.augment_three_id = 0;
e.augment_four_id = 0;
e.augment_five_id = 0;
e.augment_six_id = 0;
e.quantity = 0;
e.donator = "";
e.permissions = 0;
e.who_for = "";
return e;
}
@@ -148,15 +171,21 @@ public:
if (results.RowCount() == 1) {
GuildBank e{};
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.guildid = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.area = static_cast<uint8_t>(strtoul(row[2], nullptr, 10));
e.slot = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
e.itemid = static_cast<uint32_t>(strtoul(row[4], nullptr, 10));
e.qty = static_cast<uint32_t>(strtoul(row[5], nullptr, 10));
e.donator = row[6] ? row[6] : "";
e.permissions = static_cast<uint8_t>(strtoul(row[7], nullptr, 10));
e.whofor = row[8] ? row[8] : "";
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.guild_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.area = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.slot = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.item_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.augment_one_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.augment_two_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.augment_three_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.augment_four_id = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.augment_five_id = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.augment_six_id = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.quantity = row[11] ? static_cast<int32_t>(atoi(row[11])) : 0;
e.donator = row[12] ? row[12] : "";
e.permissions = row[13] ? static_cast<uint8_t>(strtoul(row[13], nullptr, 10)) : 0;
e.who_for = row[14] ? row[14] : "";
return e;
}
@@ -190,14 +219,20 @@ public:
auto columns = Columns();
v.push_back(columns[1] + " = " + std::to_string(e.guildid));
v.push_back(columns[1] + " = " + std::to_string(e.guild_id));
v.push_back(columns[2] + " = " + std::to_string(e.area));
v.push_back(columns[3] + " = " + std::to_string(e.slot));
v.push_back(columns[4] + " = " + std::to_string(e.itemid));
v.push_back(columns[5] + " = " + std::to_string(e.qty));
v.push_back(columns[6] + " = '" + Strings::Escape(e.donator) + "'");
v.push_back(columns[7] + " = " + std::to_string(e.permissions));
v.push_back(columns[8] + " = '" + Strings::Escape(e.whofor) + "'");
v.push_back(columns[4] + " = " + std::to_string(e.item_id));
v.push_back(columns[5] + " = " + std::to_string(e.augment_one_id));
v.push_back(columns[6] + " = " + std::to_string(e.augment_two_id));
v.push_back(columns[7] + " = " + std::to_string(e.augment_three_id));
v.push_back(columns[8] + " = " + std::to_string(e.augment_four_id));
v.push_back(columns[9] + " = " + std::to_string(e.augment_five_id));
v.push_back(columns[10] + " = " + std::to_string(e.augment_six_id));
v.push_back(columns[11] + " = " + std::to_string(e.quantity));
v.push_back(columns[12] + " = '" + Strings::Escape(e.donator) + "'");
v.push_back(columns[13] + " = " + std::to_string(e.permissions));
v.push_back(columns[14] + " = '" + Strings::Escape(e.who_for) + "'");
auto results = db.QueryDatabase(
fmt::format(
@@ -220,14 +255,20 @@ public:
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.guildid));
v.push_back(std::to_string(e.guild_id));
v.push_back(std::to_string(e.area));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.itemid));
v.push_back(std::to_string(e.qty));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.augment_one_id));
v.push_back(std::to_string(e.augment_two_id));
v.push_back(std::to_string(e.augment_three_id));
v.push_back(std::to_string(e.augment_four_id));
v.push_back(std::to_string(e.augment_five_id));
v.push_back(std::to_string(e.augment_six_id));
v.push_back(std::to_string(e.quantity));
v.push_back("'" + Strings::Escape(e.donator) + "'");
v.push_back(std::to_string(e.permissions));
v.push_back("'" + Strings::Escape(e.whofor) + "'");
v.push_back("'" + Strings::Escape(e.who_for) + "'");
auto results = db.QueryDatabase(
fmt::format(
@@ -258,14 +299,20 @@ public:
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.guildid));
v.push_back(std::to_string(e.guild_id));
v.push_back(std::to_string(e.area));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.itemid));
v.push_back(std::to_string(e.qty));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.augment_one_id));
v.push_back(std::to_string(e.augment_two_id));
v.push_back(std::to_string(e.augment_three_id));
v.push_back(std::to_string(e.augment_four_id));
v.push_back(std::to_string(e.augment_five_id));
v.push_back(std::to_string(e.augment_six_id));
v.push_back(std::to_string(e.quantity));
v.push_back("'" + Strings::Escape(e.donator) + "'");
v.push_back(std::to_string(e.permissions));
v.push_back("'" + Strings::Escape(e.whofor) + "'");
v.push_back("'" + Strings::Escape(e.who_for) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -299,15 +346,21 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
GuildBank e{};
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.guildid = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.area = static_cast<uint8_t>(strtoul(row[2], nullptr, 10));
e.slot = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
e.itemid = static_cast<uint32_t>(strtoul(row[4], nullptr, 10));
e.qty = static_cast<uint32_t>(strtoul(row[5], nullptr, 10));
e.donator = row[6] ? row[6] : "";
e.permissions = static_cast<uint8_t>(strtoul(row[7], nullptr, 10));
e.whofor = row[8] ? row[8] : "";
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.guild_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.area = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.slot = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.item_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.augment_one_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.augment_two_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.augment_three_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.augment_four_id = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.augment_five_id = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.augment_six_id = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.quantity = row[11] ? static_cast<int32_t>(atoi(row[11])) : 0;
e.donator = row[12] ? row[12] : "";
e.permissions = row[13] ? static_cast<uint8_t>(strtoul(row[13], nullptr, 10)) : 0;
e.who_for = row[14] ? row[14] : "";
all_entries.push_back(e);
}
@@ -332,15 +385,21 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
GuildBank e{};
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.guildid = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.area = static_cast<uint8_t>(strtoul(row[2], nullptr, 10));
e.slot = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
e.itemid = static_cast<uint32_t>(strtoul(row[4], nullptr, 10));
e.qty = static_cast<uint32_t>(strtoul(row[5], nullptr, 10));
e.donator = row[6] ? row[6] : "";
e.permissions = static_cast<uint8_t>(strtoul(row[7], nullptr, 10));
e.whofor = row[8] ? row[8] : "";
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.guild_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.area = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.slot = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.item_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.augment_one_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
e.augment_two_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
e.augment_three_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
e.augment_four_id = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
e.augment_five_id = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
e.augment_six_id = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.quantity = row[11] ? static_cast<int32_t>(atoi(row[11])) : 0;
e.donator = row[12] ? row[12] : "";
e.permissions = row[13] ? static_cast<uint8_t>(strtoul(row[13], nullptr, 10)) : 0;
e.who_for = row[14] ? row[14] : "";
all_entries.push_back(e);
}
@@ -399,6 +458,90 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const GuildBank &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.guild_id));
v.push_back(std::to_string(e.area));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.augment_one_id));
v.push_back(std::to_string(e.augment_two_id));
v.push_back(std::to_string(e.augment_three_id));
v.push_back(std::to_string(e.augment_four_id));
v.push_back(std::to_string(e.augment_five_id));
v.push_back(std::to_string(e.augment_six_id));
v.push_back(std::to_string(e.quantity));
v.push_back("'" + Strings::Escape(e.donator) + "'");
v.push_back(std::to_string(e.permissions));
v.push_back("'" + Strings::Escape(e.who_for) + "'");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<GuildBank> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.guild_id));
v.push_back(std::to_string(e.area));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.augment_one_id));
v.push_back(std::to_string(e.augment_two_id));
v.push_back(std::to_string(e.augment_three_id));
v.push_back(std::to_string(e.augment_four_id));
v.push_back(std::to_string(e.augment_five_id));
v.push_back(std::to_string(e.augment_six_id));
v.push_back(std::to_string(e.quantity));
v.push_back("'" + Strings::Escape(e.donator) + "'");
v.push_back(std::to_string(e.permissions));
v.push_back("'" + Strings::Escape(e.who_for) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_GUILD_BANK_REPOSITORY_H
@@ -2,7 +2,7 @@
#define EQEMU_CHARACTER_EXPEDITION_LOCKOUTS_REPOSITORY_H
#include "../database.h"
#include "../expedition_lockout_timer.h"
#include "../dynamic_zone_lockout.h"
#include "../strings.h"
#include "base/base_character_expedition_lockouts_repository.h"
#include <unordered_map>
@@ -47,33 +47,8 @@ public:
// Custom extended repository methods here
struct CharacterExpeditionLockoutsTimeStamp {
int id;
int character_id;
std::string expedition_name;
std::string event_name;
time_t expire_time;
int duration;
std::string from_expedition_uuid;
};
static ExpeditionLockoutTimer GetExpeditionLockoutTimerFromEntry(
CharacterExpeditionLockoutsTimeStamp&& entry)
{
ExpeditionLockoutTimer lockout_timer{
std::move(entry.from_expedition_uuid),
std::move(entry.expedition_name),
std::move(entry.event_name),
static_cast<uint64_t>(entry.expire_time),
static_cast<uint32_t>(entry.duration)
};
return lockout_timer;
}
static std::unordered_map<uint32_t, std::vector<ExpeditionLockoutTimer>> GetManyCharacterLockoutTimers(
Database& db, const std::vector<uint32_t>& character_ids,
const std::string& expedition_name, const std::string& ordered_event_name)
static std::unordered_map<uint32_t, std::vector<DzLockout>> GetLockouts(
Database& db, const std::vector<uint32_t>& char_ids, const std::string& expedition)
{
auto results = db.QueryDatabase(fmt::format(SQL(
SELECT
@@ -84,39 +59,171 @@ public:
from_expedition_uuid
FROM character_expedition_lockouts
WHERE
character_id IN ({})
character_id IN ({0})
AND expire_time > NOW()
AND expedition_name = '{}'
AND expedition_name = '{1}'
ORDER BY
FIELD(character_id, {}),
FIELD(event_name, '{}') DESC
FIELD(character_id, {0}),
FIELD(event_name, '{2}') DESC
),
fmt::join(character_ids, ","),
Strings::Escape(expedition_name),
fmt::join(character_ids, ","),
Strings::Escape(ordered_event_name)
fmt::join(char_ids, ","),
Strings::Escape(expedition),
Strings::Escape(DzLockout::ReplayTimer)
));
std::unordered_map<uint32_t, std::vector<ExpeditionLockoutTimer>> lockouts;
std::unordered_map<uint32_t, std::vector<DzLockout>> lockouts;
for (auto row = results.begin(); row != results.end(); ++row)
{
CharacterExpeditionLockoutsTimeStamp entry{};
int col = 0;
entry.character_id = std::strtoul(row[col++], nullptr, 10);
entry.expire_time = std::strtoull(row[col++], nullptr, 10);
entry.duration = std::strtoul(row[col++], nullptr, 10);
entry.event_name = row[col++];
entry.expedition_name = expedition_name;
entry.from_expedition_uuid = row[col++];
uint32_t char_id = std::strtoul(row[col++], nullptr, 10);
time_t expire_time = std::strtoull(row[col++], nullptr, 10);
uint32_t duration = std::strtoul(row[col++], nullptr, 10);
std::string event = row[col++];
std::string uuid = row[col++];
auto lockout = GetExpeditionLockoutTimerFromEntry(std::move(entry));
lockouts[entry.character_id].emplace_back(std::move(lockout));
lockouts[char_id].emplace_back(std::move(uuid), expedition, std::move(event), expire_time, duration);
}
return lockouts;
}
static std::vector<DzLockout> GetLockouts(Database& db, uint32_t char_id)
{
std::vector<DzLockout> lockouts;
auto rows = GetWhere(db, fmt::format("character_id = {} AND expire_time > NOW()", char_id));
lockouts.reserve(rows.size());
for (auto& row : rows)
{
lockouts.emplace_back(
std::move(row.from_expedition_uuid),
std::move(row.expedition_name),
std::move(row.event_name),
row.expire_time,
row.duration
);
}
return lockouts;
}
static std::vector<CharacterExpeditionLockouts> GetLockouts(Database& db, const std::vector<std::string>& names, const std::string& expedition, const std::string& event)
{
if (names.empty())
{
return {};
}
return GetWhere(db, fmt::format(
"character_id IN (select id from character_data where name IN ('{}')) AND expire_time > NOW() AND expedition_name = '{}' AND event_name = '{}' LIMIT 1",
fmt::join(names, "','"), Strings::Escape(expedition), Strings::Escape(event)));
}
static void InsertLockouts(Database& db, uint32_t char_id, const std::vector<DzLockout>& lockouts)
{
std::string insert_values;
for (const auto& lockout : lockouts)
{
fmt::format_to(std::back_inserter(insert_values),
"({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),",
char_id,
lockout.GetExpireTime(),
lockout.GetDuration(),
Strings::Escape(lockout.UUID()),
Strings::Escape(lockout.DzName()),
Strings::Escape(lockout.Event())
);
}
if (!insert_values.empty())
{
insert_values.pop_back(); // trailing comma
auto query = fmt::format(SQL(
INSERT INTO character_expedition_lockouts
(character_id, expire_time, duration, from_expedition_uuid, expedition_name, event_name)
VALUES {}
ON DUPLICATE KEY UPDATE
from_expedition_uuid = VALUES(from_expedition_uuid),
expire_time = VALUES(expire_time),
duration = VALUES(duration);
), insert_values);
db.QueryDatabase(query);
}
}
static void InsertLockout(Database& db, const std::vector<uint32_t>& char_ids, const DzLockout& lockout)
{
std::string insert_values;
for (const auto& char_id : char_ids)
{
fmt::format_to(std::back_inserter(insert_values),
"({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),",
char_id,
lockout.GetExpireTime(),
lockout.GetDuration(),
Strings::Escape(lockout.UUID()),
Strings::Escape(lockout.DzName()),
Strings::Escape(lockout.Event())
);
}
if (!insert_values.empty())
{
insert_values.pop_back(); // trailing comma
auto query = fmt::format(SQL(
INSERT INTO character_expedition_lockouts
(character_id, expire_time, duration, from_expedition_uuid, expedition_name, event_name)
VALUES {}
ON DUPLICATE KEY UPDATE
from_expedition_uuid = VALUES(from_expedition_uuid),
expire_time = VALUES(expire_time),
duration = VALUES(duration);
), insert_values);
db.QueryDatabase(query);
}
}
// inserts a new lockout or updates existing lockout with seconds added to current time
static void AddLockoutDuration(Database& db, const std::vector<uint32_t>& char_ids, const DzLockout& lockout, int seconds)
{
std::string insert_values;
for (const auto& char_id : char_ids)
{
fmt::format_to(std::back_inserter(insert_values),
"({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),",
char_id,
lockout.GetExpireTime(),
lockout.GetDuration(),
Strings::Escape(lockout.UUID()),
Strings::Escape(lockout.DzName()),
Strings::Escape(lockout.Event())
);
}
if (!insert_values.empty())
{
insert_values.pop_back(); // trailing comma
auto query = fmt::format(SQL(
INSERT INTO character_expedition_lockouts
(character_id, expire_time, duration, from_expedition_uuid, expedition_name, event_name)
VALUES {0}
ON DUPLICATE KEY UPDATE
from_expedition_uuid = VALUES(from_expedition_uuid),
expire_time = DATE_ADD(expire_time, INTERVAL {1} SECOND),
duration = GREATEST(0, CAST(duration AS SIGNED) + {1});
), insert_values, seconds);
db.QueryDatabase(query);
}
}
};
#endif //EQEMU_CHARACTER_EXPEDITION_LOCKOUTS_REPOSITORY_H
@@ -0,0 +1,84 @@
#ifndef EQEMU_DYNAMIC_ZONE_LOCKOUTS_REPOSITORY_H
#define EQEMU_DYNAMIC_ZONE_LOCKOUTS_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "../dynamic_zone_lockout.h"
#include "base/base_dynamic_zone_lockouts_repository.h"
class DynamicZoneLockoutsRepository: public BaseDynamicZoneLockoutsRepository {
public:
/**
* This file was auto generated and can be modified and extended upon
*
* Base repository methods are automatically
* generated in the "base" version of this repository. The base repository
* is immutable and to be left untouched, while methods in this class
* are used as extension methods for more specific persistence-layer
* accessors or mutators.
*
* Base Methods (Subject to be expanded upon in time)
*
* Note: Not all tables are designed appropriately to fit functionality with all base methods
*
* InsertOne
* UpdateOne
* DeleteOne
* FindOne
* GetWhere(std::string where_filter)
* DeleteWhere(std::string where_filter)
* InsertMany
* All
*
* Example custom methods in a repository
*
* DynamicZoneLockoutsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* DynamicZoneLockoutsRepository::GetWhereNeverExpires()
* DynamicZoneLockoutsRepository::GetWhereXAndY()
* DynamicZoneLockoutsRepository::DeleteWhereXAndY()
*
* Most of the above could be covered by base methods, but if you as a developer
* find yourself re-using logic for other parts of the code, its best to just make a
* method that can be re-used easily elsewhere especially if it can use a base repository
* method and encapsulate filters there
*/
// Custom extended repository methods here
static void InsertLockouts(Database& db, uint32_t dz_id, const std::vector<DzLockout>& lockouts)
{
std::string insert_values;
for (const auto& lockout : lockouts)
{
fmt::format_to(std::back_inserter(insert_values),
"({}, '{}', '{}', FROM_UNIXTIME({}), {}),",
dz_id,
Strings::Escape(lockout.UUID()),
Strings::Escape(lockout.Event()),
lockout.GetExpireTime(),
lockout.GetDuration()
);
}
if (!insert_values.empty())
{
insert_values.pop_back(); // trailing comma
auto query = fmt::format(SQL(
INSERT INTO dynamic_zone_lockouts
(dynamic_zone_id, from_expedition_uuid, event_name, expire_time, duration)
VALUES {}
ON DUPLICATE KEY UPDATE
from_expedition_uuid = VALUES(from_expedition_uuid),
expire_time = VALUES(expire_time),
duration = VALUES(duration);
), insert_values);
db.QueryDatabase(query);
}
}
};
#endif //EQEMU_DYNAMIC_ZONE_LOCKOUTS_REPOSITORY_H
@@ -65,7 +65,7 @@ public:
));
}
static std::vector<MemberWithName> GetAllWithNames(Database& db)
static std::vector<MemberWithName> AllWithNames(Database& db)
{
std::vector<MemberWithName> all_entries;
@@ -146,65 +146,34 @@ public:
static void RemoveMember(Database& db, uint32_t dynamic_zone_id, uint32_t character_id)
{
db.QueryDatabase(fmt::format(SQL(
DELETE FROM {}
WHERE dynamic_zone_id = {} AND character_id = {};
),
TableName(), dynamic_zone_id, character_id
));
DeleteWhere(db, fmt::format("dynamic_zone_id = {} AND character_id = {}", dynamic_zone_id, character_id));
}
static void RemoveAllMembers(Database& db, uint32_t dynamic_zone_id)
{
db.QueryDatabase(fmt::format(SQL(
DELETE FROM {}
WHERE dynamic_zone_id = {};
),
TableName(), dynamic_zone_id
));
DeleteWhere(db, fmt::format("dynamic_zone_id = {}", dynamic_zone_id));
}
static void RemoveAllMembers(Database& db, std::vector<uint32_t> dynamic_zone_ids)
static uint32_t InsertOrUpdateMany(Database& db, const std::vector<DynamicZoneMembers>& entries)
{
if (!dynamic_zone_ids.empty())
if (entries.empty())
{
db.QueryDatabase(fmt::format(SQL(
DELETE FROM {}
WHERE dynamic_zone_id IN ({});
),
TableName(), Strings::Join(dynamic_zone_ids, ",")
));
}
}
static int InsertOrUpdateMany(Database& db,
const std::vector<DynamicZoneMembers>& dynamic_zone_members_entries)
{
std::vector<std::string> insert_chunks;
for (auto &dynamic_zone_members_entry: dynamic_zone_members_entries)
{
std::vector<std::string> insert_values;
insert_values.push_back(std::to_string(dynamic_zone_members_entry.id));
insert_values.push_back(std::to_string(dynamic_zone_members_entry.dynamic_zone_id));
insert_values.push_back(std::to_string(dynamic_zone_members_entry.character_id));
insert_chunks.push_back("(" + Strings::Implode(",", insert_values) + ")");
return 0;
}
std::vector<std::string> insert_values;
std::vector<std::string> values;
values.reserve(entries.size());
auto results = db.QueryDatabase(
fmt::format(
"INSERT INTO {} ({}) VALUES {} ON DUPLICATE KEY UPDATE id = id;",
TableName(),
ColumnsRaw(),
Strings::Implode(",", insert_chunks)
)
);
for (const auto& entry : entries)
{
values.push_back(fmt::format("({},{},{})", entry.id, entry.dynamic_zone_id, entry.character_id));
}
return (results.Success() ? results.RowsAffected() : 0);
auto results = db.QueryDatabase(fmt::format(
"INSERT INTO {} ({}) VALUES {} ON DUPLICATE KEY UPDATE id = id;",
TableName(), ColumnsRaw(), fmt::join(values, ",")));
return results.Success() ? results.RowsAffected() : 0;
}
};
@@ -70,6 +70,8 @@ public:
float zone_in_z;
float zone_in_heading;
int has_zone_in;
int8_t is_locked;
int8_t add_replay;
int zone;
int version;
int is_global;
@@ -105,6 +107,8 @@ public:
dynamic_zones.zone_in_z,
dynamic_zones.zone_in_heading,
dynamic_zones.has_zone_in,
dynamic_zones.is_locked,
dynamic_zones.add_replay,
instance_list.zone,
instance_list.version,
instance_list.is_global,
@@ -144,6 +148,8 @@ public:
entry.zone_in_z = strtof(row[col++], nullptr);
entry.zone_in_heading = strtof(row[col++], nullptr);
entry.has_zone_in = strtol(row[col++], nullptr, 10) != 0;
entry.is_locked = static_cast<int8_t>(strtol(row[col++], nullptr, 10));
entry.add_replay = static_cast<int8_t>(strtol(row[col++], nullptr, 10));
// from instance_list
entry.zone = strtol(row[col++], nullptr, 10);
entry.version = strtol(row[col++], nullptr, 10);
@@ -244,6 +250,22 @@ public:
}
}
static void UpdateLocked(Database& db, uint32_t dz_id, bool lock)
{
if (dz_id != 0)
{
db.QueryDatabase(fmt::format("UPDATE dynamic_zones SET is_locked = {} WHERE id = {}", lock, dz_id));
}
}
static void UpdateReplayOnJoin(Database& db, uint32_t dz_id, bool enabled)
{
if (dz_id != 0)
{
db.QueryDatabase(fmt::format("UPDATE dynamic_zones SET add_replay = {} WHERE id = {}", enabled, dz_id));
}
}
static void UpdateSwitchID(Database& db, uint32_t dz_id, int dz_switch_id)
{
if (dz_id != 0)
@@ -351,6 +373,59 @@ public:
return all_entries;
}
struct CharacterDz
{
uint32_t id;
std::string name;
uint32_t dz_id;
};
// get character ids with possible active dz id by type
static std::vector<CharacterDz> GetCharactersWithDz(Database& db, const std::vector<std::string>& names, int type)
{
if (names.empty())
{
return {};
}
std::vector<CharacterDz> entries;
entries.reserve(names.size());
auto results = db.QueryDatabase(fmt::format(SQL(
SELECT
character_data.id,
character_data.name,
MAX(dynamic_zones.id)
FROM character_data
LEFT JOIN dynamic_zone_members
ON character_data.id = dynamic_zone_members.character_id
LEFT JOIN dynamic_zones
ON dynamic_zone_members.dynamic_zone_id = dynamic_zones.id
AND dynamic_zones.`type` = {0}
WHERE character_data.name IN ('{1}')
GROUP BY character_data.id
ORDER BY FIELD(character_data.name, '{1}')
),
type,
fmt::join(names, "','")
));
if (results.Success())
{
for (auto row = results.begin(); row != results.end(); ++row)
{
CharacterDz entry{};
entry.id = std::strtoul(row[0], nullptr, 10);
entry.name = row[1];
entry.dz_id = row[2] ? std::strtoul(row[2], nullptr, 10) : 0;
entries.push_back(std::move(entry));
}
}
return entries;
}
};
#endif //EQEMU_DYNAMIC_ZONES_REPOSITORY_H
@@ -1,102 +0,0 @@
#ifndef EQEMU_EXPEDITION_LOCKOUTS_REPOSITORY_H
#define EQEMU_EXPEDITION_LOCKOUTS_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_expedition_lockouts_repository.h"
class ExpeditionLockoutsRepository: public BaseExpeditionLockoutsRepository {
public:
/**
* This file was auto generated and can be modified and extended upon
*
* Base repository methods are automatically
* generated in the "base" version of this repository. The base repository
* is immutable and to be left untouched, while methods in this class
* are used as extension methods for more specific persistence-layer
* accessors or mutators.
*
* Base Methods (Subject to be expanded upon in time)
*
* Note: Not all tables are designed appropriately to fit functionality with all base methods
*
* InsertOne
* UpdateOne
* DeleteOne
* FindOne
* GetWhere(std::string where_filter)
* DeleteWhere(std::string where_filter)
* InsertMany
* All
*
* Example custom methods in a repository
*
* ExpeditionLockoutsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* ExpeditionLockoutsRepository::GetWhereNeverExpires()
* ExpeditionLockoutsRepository::GetWhereXAndY()
* ExpeditionLockoutsRepository::DeleteWhereXAndY()
*
* Most of the above could be covered by base methods, but if you as a developer
* find yourself re-using logic for other parts of the code, its best to just make a
* method that can be re-used easily elsewhere especially if it can use a base repository
* method and encapsulate filters there
*/
// Custom extended repository methods here
struct ExpeditionLockoutsWithTimestamp {
uint32_t id;
uint32_t expedition_id;
std::string event_name;
time_t expire_time;
int duration;
std::string from_expedition_uuid;
};
static std::vector<ExpeditionLockoutsWithTimestamp> GetWithTimestamp(
Database& db, const std::vector<uint32_t>& expedition_ids)
{
if (expedition_ids.empty())
{
return {};
}
std::vector<ExpeditionLockoutsWithTimestamp> all_entries;
auto results = db.QueryDatabase(fmt::format(SQL(
SELECT
id,
expedition_id,
event_name,
UNIX_TIMESTAMP(expire_time),
duration,
from_expedition_uuid
FROM expedition_lockouts
WHERE expedition_id IN ({})
),
Strings::Join(expedition_ids, ",")
));
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row)
{
ExpeditionLockoutsWithTimestamp entry{};
int col = 0;
entry.id = strtoul(row[col++], nullptr, 10);
entry.expedition_id = strtoul(row[col++], nullptr, 10);
entry.event_name = row[col++];
entry.expire_time = strtoull(row[col++], nullptr, 10);
entry.duration = strtol(row[col++], nullptr, 10);
entry.from_expedition_uuid = row[col++];
all_entries.emplace_back(std::move(entry));
}
return all_entries;
}
};
#endif //EQEMU_EXPEDITION_LOCKOUTS_REPOSITORY_H
@@ -1,134 +0,0 @@
#ifndef EQEMU_EXPEDITIONS_REPOSITORY_H
#define EQEMU_EXPEDITIONS_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_expeditions_repository.h"
class ExpeditionsRepository: public BaseExpeditionsRepository {
public:
/**
* This file was auto generated and can be modified and extended upon
*
* Base repository methods are automatically
* generated in the "base" version of this repository. The base repository
* is immutable and to be left untouched, while methods in this class
* are used as extension methods for more specific persistence-layer
* accessors or mutators.
*
* Base Methods (Subject to be expanded upon in time)
*
* Note: Not all tables are designed appropriately to fit functionality with all base methods
*
* InsertOne
* UpdateOne
* DeleteOne
* FindOne
* GetWhere(std::string where_filter)
* DeleteWhere(std::string where_filter)
* InsertMany
* All
*
* Example custom methods in a repository
*
* ExpeditionsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* ExpeditionsRepository::GetWhereNeverExpires()
* ExpeditionsRepository::GetWhereXAndY()
* ExpeditionsRepository::DeleteWhereXAndY()
*
* Most of the above could be covered by base methods, but if you as a developer
* find yourself re-using logic for other parts of the code, its best to just make a
* method that can be re-used easily elsewhere especially if it can use a base repository
* method and encapsulate filters there
*/
// Custom extended repository methods here
struct CharacterExpedition
{
uint32_t id;
std::string name;
uint32_t expedition_id;
};
static std::vector<CharacterExpedition> GetCharactersWithExpedition(
Database& db, const std::vector<std::string>& character_names)
{
if (character_names.empty())
{
return {};
}
std::vector<CharacterExpedition> entries;
auto joined_character_names = fmt::format("'{}'", Strings::Join(character_names, "','"));
auto results = db.QueryDatabase(fmt::format(SQL(
SELECT
character_data.id,
character_data.name,
MAX(expeditions.id)
FROM character_data
LEFT JOIN dynamic_zone_members
ON character_data.id = dynamic_zone_members.character_id
LEFT JOIN expeditions
ON dynamic_zone_members.dynamic_zone_id = expeditions.dynamic_zone_id
WHERE character_data.name IN ({})
GROUP BY character_data.id
ORDER BY FIELD(character_data.name, {})
),
joined_character_names,
joined_character_names
));
if (results.Success())
{
entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row)
{
CharacterExpedition entry{};
entry.id = std::strtoul(row[0], nullptr, 10);
entry.name = row[1];
entry.expedition_id = row[2] ? std::strtoul(row[2], nullptr, 10) : 0;
entries.emplace_back(std::move(entry));
}
}
return entries;
}
static uint32_t GetIDByMemberID(Database& db, uint32_t character_id)
{
if (character_id == 0)
{
return 0;
}
uint32_t expedition_id = 0;
auto results = db.QueryDatabase(fmt::format(SQL(
SELECT
expeditions.id
FROM expeditions
INNER JOIN dynamic_zone_members
ON expeditions.dynamic_zone_id = dynamic_zone_members.dynamic_zone_id
WHERE
dynamic_zone_members.character_id = {}
),
character_id
));
if (results.Success() && results.RowCount() > 0)
{
auto row = results.begin();
expedition_id = std::strtoul(row[0], nullptr, 10);
}
return expedition_id;
}
};
#endif //EQEMU_EXPEDITIONS_REPOSITORY_H
@@ -45,33 +45,26 @@ public:
// Custom extended repository methods here
static int InsertOrUpdateMany(Database& db,
const std::vector<InstanceListPlayer>& instance_list_player_entries)
static uint32_t InsertOrUpdateMany(Database& db, const std::vector<InstanceListPlayer>& entries)
{
std::vector<std::string> insert_chunks;
for (auto &instance_list_player_entry: instance_list_player_entries)
if (entries.empty())
{
std::vector<std::string> insert_values;
insert_values.push_back(std::to_string(instance_list_player_entry.id));
insert_values.push_back(std::to_string(instance_list_player_entry.charid));
insert_chunks.push_back("(" + Strings::Implode(",", insert_values) + ")");
return 0;
}
std::vector<std::string> insert_values;
std::vector<std::string> values;
values.reserve(entries.size());
auto results = db.QueryDatabase(
fmt::format(
"INSERT INTO {} ({}) VALUES {} ON DUPLICATE KEY UPDATE id = VALUES(id)",
TableName(),
ColumnsRaw(),
Strings::Implode(",", insert_chunks)
)
);
for (const auto& entry : entries)
{
values.push_back(fmt::format("({},{})", entry.id, entry.charid));
}
return (results.Success() ? results.RowsAffected() : 0);
auto results = db.QueryDatabase(fmt::format(
"INSERT INTO {} ({}) VALUES {} ON DUPLICATE KEY UPDATE id = VALUES(id)",
TableName(), ColumnsRaw(), fmt::join(values, ",")));
return results.Success() ? results.RowsAffected() : 0;
}
static bool ReplaceOne(Database& db, InstanceListPlayer e)
+5 -3
View File
@@ -230,6 +230,7 @@ RULE_BOOL(Character, PlayerTradingLoreFeedback, true, "If enabled, during a play
RULE_INT(Character, MendAlwaysSucceedValue, 199, "Value at which mend will always succeed its skill check. Default: 199")
RULE_BOOL(Character, SneakAlwaysSucceedOver100, false, "When sneak skill is over 100, always succeed sneak/hide. Default: false")
RULE_INT(Character, BandolierSwapDelay, 0, "Bandolier swap delay in milliseconds, default is 0")
RULE_BOOL(Character, EnableHackedFastCampForGM, false, "Enables hacked fast camp for GM clients, if the GM doesn't have a hacked client they'll camp like normal")
RULE_CATEGORY_END()
RULE_CATEGORY(Mercs)
@@ -713,6 +714,7 @@ RULE_INT(Aggro, BardAggroCap, 40, "per song bard aggro cap.")
RULE_INT(Aggro, InitialAggroBonus, 100, "Initial Aggro Bonus, Default: 100")
RULE_INT(Aggro, InitialPetAggroBonus, 100, "Initial Pet Aggro Bonus, Default 100")
RULE_STRING(Aggro, ExcludedFleeAllyFactionIDs, "0|5013|5014|5023|5032", "Common Faction IDs that are excluded from faction checks in EntityList::FleeAllyCount")
RULE_BOOL(Aggro, AggroBotPets, false, "If enabled, NPCs will aggro bot pets")
RULE_CATEGORY_END()
RULE_CATEGORY(TaskSystem)
@@ -817,11 +819,11 @@ RULE_INT(Bots, MinDelayBetweenInCombatCastAttempts, 500, "The minimum delay in m
RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2500ms.")
RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 1000, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 1000ms.")
RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 2500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 2500ms.")
RULE_INT(Bots, MezChance, 35, "35 Default. Chance for a bot to attempt to Mez a target after validating it is eligible.")
RULE_INT(Bots, MezChance, 60, "60 Default. Chance for a bot to attempt to Mez a target after validating it is eligible.")
RULE_INT(Bots, AEMezChance, 35, "35 Default. Chance for a bot to attempt to AE Mez targets after validating they are eligible.")
RULE_INT(Bots, MezSuccessDelay, 3500, "3500 (3.5 sec) Default. Delay between successful Mez attempts.")
RULE_INT(Bots, MezSuccessDelay, 2500, "2500 (2.5 sec) Default. Delay between successful Mez attempts.")
RULE_INT(Bots, AEMezSuccessDelay, 5000, "5000 (5 sec) Default. Delay between successful AEMez attempts.")
RULE_INT(Bots, MezFailDelay, 2000, "2000 (2 sec) Default. Delay between failed Mez attempts.")
RULE_INT(Bots, MezFailDelay, 1250, "1250 (1.25 sec) Default. Delay between failed Mez attempts.")
RULE_INT(Bots, MezAEFailDelay, 4000, "4000 (4 sec) Default. Delay between failed AEMez attempts.")
RULE_INT(Bots, MinGroupHealTargets, 3, "Minimum number of targets in valid range that are required for a group heal to cast. Default 3.")
RULE_INT(Bots, MinGroupCureTargets, 3, "Minimum number of targets in valid range that are required for a cure heal to cast. Default 3.")
+143
View File
@@ -0,0 +1,143 @@
#ifndef EQEMU_SERVER_RELOAD_TYPES_H
#define EQEMU_SERVER_RELOAD_TYPES_H
#include <string>
#include <vector>
#include <cstdint>
namespace ServerReload {
enum Type {
ReloadTypeNone = 0,
AAData,
AlternateCurrencies,
BaseData,
BlockedSpells,
Commands,
ContentFlags,
DataBucketsCache,
Doors,
DzTemplates,
Factions,
GroundSpawns,
LevelEXPMods,
Logs,
Loot,
Merchants,
NPCEmotes,
NPCSpells,
Objects,
Opcodes,
PerlExportSettings,
Quests,
QuestsTimerReset,
Rules,
SkillCaps,
StaticZoneData,
Tasks,
Titles,
Traps,
Variables,
VeteranRewards,
WorldRepop,
WorldWithRespawn,
ZoneData,
ZonePoints,
Max
};
static const char *Name[ServerReload::Max] = {
"None",
"AA Data",
"Alternate Currencies",
"Base Data",
"Blocked Spells",
"Commands",
"Content Flags",
"Data Buckets Cache",
"Doors",
"DZ Templates",
"Factions",
"Ground Spawns",
"Level EXP Mods",
"Logs",
"Loot",
"Merchants",
"NPC Emotes",
"NPC Spells",
"Objects",
"Opcodes",
"Perl Event Export Settings",
"Quest",
"Quests With Timer (Resets timer events)",
"Rules",
"Skill Caps",
"Static Zone Data",
"Tasks",
"Titles",
"Traps",
"Variables",
"Veteran Rewards",
"World Repop",
"World Repop Timers (Clear Respawn Timers)",
"Zone Data",
"Zone Points"
};
inline std::string GetName(int reload_type)
{
if (reload_type < 0 || reload_type >= ServerReload::Type::Max) {
return "Unknown";
}
return ServerReload::Name[reload_type];
}
// Get a clean name without spaces or special characters
inline std::string GetNameClean(int reload_type)
{
if (reload_type < 0 || reload_type >= ServerReload::Type::Max) {
return "Unknown";
}
// get the name before parentheses
std::string name = ServerReload::Name[reload_type];
size_t pos = name.find('(');
if (pos != std::string::npos) {
name = name.substr(0, pos);
}
// Trim leading spaces
size_t start = name.find_first_not_of(' ');
if (start == std::string::npos) {
return ""; // If all spaces, return empty string
}
// Trim trailing spaces
size_t end = name.find_last_not_of(' ');
// Extract trimmed substring
return name.substr(start, end - start + 1);
return name;
}
inline std::vector<ServerReload::Type> GetTypes()
{
std::vector<ServerReload::Type> types;
types.reserve(ServerReload::Type::Max);
for (int i = 1; i < ServerReload::Type::Max; i++) {
types.push_back(static_cast<ServerReload::Type>(i));
}
return types;
}
struct Request {
int type = 0;
bool requires_zone_booted = false;
int64 reload_at_unix = 0;
int32 opt_param = 0;
uint32_t zone_server_id = 0;
};
}
#endif //EQEMU_SERVER_RELOAD_TYPES_H
+30 -67
View File
@@ -170,18 +170,6 @@
#define ServerOP_LFPMatches 0x0214
#define ServerOP_ClientVersionSummary 0x0215
// expedition
#define ServerOP_ExpeditionCreate 0x0400
#define ServerOP_ExpeditionLockout 0x0403
#define ServerOP_ExpeditionDzAddPlayer 0x0408
#define ServerOP_ExpeditionDzMakeLeader 0x0409
#define ServerOP_ExpeditionCharacterLockout 0x040d
#define ServerOP_ExpeditionSaveInvite 0x040e
#define ServerOP_ExpeditionRequestInvite 0x040f
#define ServerOP_ExpeditionReplayOnJoin 0x0410
#define ServerOP_ExpeditionLockState 0x0411
#define ServerOP_ExpeditionLockoutDuration 0x0414
// dz
#define ServerOP_DzAddRemoveMember 0x0450
#define ServerOP_DzRemoveAllMembers 0x0451
@@ -199,6 +187,15 @@
#define ServerOP_DzDeleted 0x045d
#define ServerOP_DzSetSwitchID 0x045e
#define ServerOP_DzMovePC 0x045f
#define ServerOP_DzLock 0x0460
#define ServerOP_DzReplayOnJoin 0x0461
#define ServerOP_DzLockout 0x0462
#define ServerOP_DzLockoutDuration 0x0463
#define ServerOP_DzCharacterLockout 0x0464
#define ServerOP_DzAddPlayer 0x0465
#define ServerOP_DzSaveInvite 0x0466
#define ServerOP_DzRequestInvite 0x0467
#define ServerOP_DzMakeLeader 0x0468
#define ServerOP_LSInfo 0x1000
#define ServerOP_LSStatus 0x1001
@@ -251,37 +248,7 @@
#define ServerOP_UpdateSchedulerEvents 0x4007
#define ServerOP_DiscordWebhookMessage 0x4008
#define ServerOP_ReloadAAData 0x4100
#define ServerOP_ReloadAlternateCurrencies 0x4101
#define ServerOP_ReloadBlockedSpells 0x4102
#define ServerOP_ReloadCommands 0x4103
#define ServerOP_ReloadContentFlags 0x4104
#define ServerOP_ReloadDoors 0x4105
#define ServerOP_ReloadGroundSpawns 0x4106
#define ServerOP_ReloadLevelEXPMods 0x4107
#define ServerOP_ReloadLogs 0x4108
#define ServerOP_ReloadMerchants 0x4109
#define ServerOP_ReloadNPCEmotes 0x4110
#define ServerOP_ReloadObjects 0x4111
#define ServerOP_ReloadOpcodes 0x4112
#define ServerOP_ReloadPerlExportSettings 0x4113
#define ServerOP_ReloadRules 0x4114
#define ServerOP_ReloadStaticZoneData 0x4115
#define ServerOP_ReloadTasks 0x4116
#define ServerOP_ReloadTitles 0x4117
#define ServerOP_ReloadTraps 0x4118
#define ServerOP_ReloadVariables 0x4119
#define ServerOP_ReloadVeteranRewards 0x4120
#define ServerOP_ReloadWorld 0x4121
#define ServerOP_ReloadZonePoints 0x4122
#define ServerOP_ReloadDzTemplates 0x4123
#define ServerOP_ReloadZoneData 0x4124
#define ServerOP_ReloadDataBucketsCache 0x4125
#define ServerOP_ReloadFactions 0x4126
#define ServerOP_ReloadLoot 0x4127
#define ServerOP_ReloadBaseData 0x4128
#define ServerOP_ReloadSkillCaps 0x4129
#define ServerOP_ReloadNPCSpells 0x4130
#define ServerOP_ServerReloadRequest 0x4100
#define ServerOP_CZDialogueWindow 0x4500
#define ServerOP_CZLDoNUpdate 0x4501
@@ -1543,10 +1510,8 @@ struct UCSServerStatus_Struct {
};
};
struct ServerExpeditionID_Struct {
uint32 expedition_id;
uint32 sender_zone_id;
uint32 sender_instance_id;
struct ServerCharacterID_Struct {
uint32_t char_id;
};
struct ServerDzLeaderID_Struct {
@@ -1580,45 +1545,42 @@ struct ServerDzMovePC_Struct {
uint32 character_id;
};
struct ServerExpeditionLockout_Struct {
uint32 expedition_id;
struct ServerDzLockout_Struct {
uint32 dz_id;
uint64 expire_time;
uint32 duration;
uint32 sender_zone_id;
uint16 sender_instance_id;
uint8 remove;
uint8 members_only;
int seconds_adjust;
int seconds;
char event_name[256];
};
struct ServerExpeditionLockState_Struct {
uint32 expedition_id;
struct ServerDzLock_Struct {
uint32 dz_id;
uint32 sender_zone_id;
uint16 sender_instance_id;
uint8 enabled;
bool lock;
uint8 lock_msg; // 0: none, 1: closing 2: trial begin
uint32 color;
};
struct ServerExpeditionSetting_Struct {
uint32 expedition_id;
struct ServerDzBool_Struct {
uint32 dz_id;
uint32 sender_zone_id;
uint16 sender_instance_id;
uint8 enabled;
bool enabled;
};
struct ServerExpeditionCharacterLockout_Struct {
struct ServerDzCharacterLockout_Struct {
uint8 remove;
uint32 character_id;
uint32 char_id;
uint64 expire_time;
uint32 duration;
char uuid[37];
char expedition_name[128];
char event_name[256];
};
struct ServerExpeditionCharacterID_Struct {
uint32_t character_id;
char expedition[128];
char event[256];
};
struct ServerDzExpireWarning_Struct {
@@ -1627,7 +1589,7 @@ struct ServerDzExpireWarning_Struct {
};
struct ServerDzCommand_Struct {
uint32 expedition_id;
uint32 dz_id;
uint8 is_char_online; // 0: target name is offline, 1: online
char requester_name[64];
char target_name[64];
@@ -1696,11 +1658,12 @@ struct ServerDzSetDuration_Struct {
uint32 seconds;
};
struct ServerDzCreateSerialized_Struct {
struct ServerDzCreate_Struct {
uint16_t origin_zone_id;
uint16_t origin_instance_id;
uint32_t dz_id;
uint32_t cereal_size;
char cereal_data[0];
char cereal_data[1];
};
struct ServerSendPlayerEvent_Struct {
+23 -14
View File
@@ -93,7 +93,14 @@ bool IsTargetableAESpell(uint16 spell_id)
return false;
}
return spells[spell_id].target_type == ST_AETarget;
return (
spells[spell_id].target_type == ST_AETarget ||
spells[spell_id].target_type == ST_TargetAETap ||
spells[spell_id].target_type == ST_AETargetHateList ||
spells[spell_id].target_type == ST_TargetAENoPlayersPets ||
spells[spell_id].target_type == ST_UndeadAE ||
spells[spell_id].target_type == ST_SummonedAE
);
}
bool IsSacrificeSpell(uint16 spell_id)
@@ -675,17 +682,19 @@ bool IsAnyNukeOrStunSpell(uint16 spell_id) {
}
bool IsAnyAESpell(uint16 spell_id) {
return (
IsValidSpell(spell_id) &&
(
IsAEDurationSpell(spell_id) ||
IsAESpell(spell_id) ||
IsAERainNukeSpell(spell_id) ||
IsAERainSpell(spell_id) ||
IsPBAESpell(spell_id) ||
IsPBAENukeSpell(spell_id)
)
);
if (!IsValidSpell(spell_id)) {
return false;
}
return (
IsTargetableAESpell(spell_id) ||
IsAESpell(spell_id) ||
IsPBAESpell(spell_id) ||
IsAEDurationSpell(spell_id) ||
IsAERainNukeSpell(spell_id) ||
IsAERainSpell(spell_id) ||
IsPBAENukeSpell(spell_id)
);
}
bool IsAESpell(uint16 spell_id)
@@ -740,8 +749,8 @@ bool IsPBAESpell(uint16 spell_id)
if (
spell.aoe_range > 0 &&
spell.target_type == ST_AECaster
) {
!IsTargetRequiredForSpell(spell_id)
) {
return true;
}
+3 -3
View File
@@ -743,7 +743,7 @@ namespace BotSpellTypes
constexpr uint16 DISCIPLINE_END = BotSpellTypes::DiscUtility; // Do not remove this, increment as needed
}
static std::map<uint16, std::string> spellType_names = {
static std::map<uint16, std::string> spell_type_names = {
{ BotSpellTypes::Nuke, "Nuke" },
{ BotSpellTypes::RegularHeal, "Regular Heal" },
{ BotSpellTypes::Root, "Root" },
@@ -818,7 +818,7 @@ static std::map<uint16, std::string> spellType_names = {
{ BotSpellTypes::AELull, "AE Lull" }
};
static std::map<uint16, std::string> spellType_shortNames = {
static std::map<uint16, std::string> spell_type_short_names = {
{ BotSpellTypes::Nuke, "nukes" },
{ BotSpellTypes::RegularHeal, "regularheals" },
{ BotSpellTypes::Root, "roots" },
@@ -900,7 +900,7 @@ const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellTyp
// Bot related functions
bool IsBotSpellTypeDetrimental (uint16 spell_type);
bool IsBotSpellTypeBeneficial (uint16 spell_type);
bool IsBotSpellTypeOtherBeneficial(uint16 spell_type);
bool BotSpellTypeUsesTargetSettings(uint16 spell_type);
bool IsBotSpellTypeInnate (uint16 spell_type);
bool IsAEBotSpellType(uint16 spell_type);
bool IsGroupBotSpellType(uint16 spell_type);
+1 -1
View File
@@ -90,7 +90,7 @@ bool IsBotSpellTypeBeneficial(uint16 spell_type) {
return false;
}
bool IsBotSpellTypeOtherBeneficial(uint16 spell_type) {
bool BotSpellTypeUsesTargetSettings(uint16 spell_type) {
switch (spell_type) {
case BotSpellTypes::RegularHeal:
case BotSpellTypes::CompleteHeal:
+24
View File
@@ -912,3 +912,27 @@ std::string Strings::ZoneTime(const uint8 hours, const uint8 minutes)
hours >= 13 ? "PM" : "AM"
);
}
std::string Strings::Slugify(const std::string& input, const std::string& separator) {
std::string slug;
bool last_was_hyphen = false;
for (char c : input) {
if (std::isalnum(c)) {
slug += std::tolower(c);
last_was_hyphen = false;
} else if (c == ' ' || c == '_' || c == '-') {
if (!last_was_hyphen && !slug.empty()) {
slug += separator;
last_was_hyphen = true;
}
}
}
// Remove trailing hyphen if present
if (!slug.empty() && slug.back() == '-') {
slug.pop_back();
}
return slug;
}
+2
View File
@@ -186,6 +186,8 @@ public:
value = strtod(tmp_str.data(), nullptr);
return res;
}
static std::string Slugify(const std::string &input, const std::string &separator = "-");
};
const std::string StringFormat(const char *format, ...);
+2 -2
View File
@@ -25,7 +25,7 @@
// Build variables
// these get injected during the build pipeline
#define CURRENT_VERSION "22.62.2-dev" // always append -dev to the current version for custom-builds
#define CURRENT_VERSION "23.0.1-dev" // always append -dev to the current version for custom-builds
#define LOGIN_VERSION "0.8.0"
#define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__
@@ -42,7 +42,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9302
#define CURRENT_BINARY_DATABASE_VERSION 9306
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054
#endif
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "eqemu-server",
"version": "22.62.2",
"version": "23.0.1",
"repository": {
"type": "git",
"url": "https://github.com/EQEmu/Server.git"
+8 -3
View File
@@ -12,6 +12,7 @@
#include "worldserver.h"
#include "../common/events/player_events.h"
#include "../common/events/player_event_logs.h"
#include "../common/server_reload_types.h"
#include <iomanip>
#include <iostream>
#include <stdarg.h>
@@ -72,9 +73,13 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
case 0: {
break;
}
case ServerOP_ReloadLogs: {
LogSys.LoadLogDatabaseSettings();
player_event_logs.ReloadSettings();
case ServerOP_ServerReloadRequest: {
auto o = (ServerReload::Request*) p.Data();
if (o->type == ServerReload::Type::Logs) {
LogSys.LoadLogDatabaseSettings();
player_event_logs.ReloadSettings();
}
break;
}
case ServerOP_QueryServGeneric: {
+8 -3
View File
@@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "database.h"
#include "../common/discord/discord_manager.h"
#include "../common/events/player_event_logs.h"
#include "../common/server_reload_types.h"
#include <iostream>
#include <string.h>
@@ -75,9 +76,13 @@ void WorldServer::ProcessMessage(uint16 opcode, EQ::Net::Packet &p)
{
break;
}
case ServerOP_ReloadLogs: {
LogSys.LoadLogDatabaseSettings();
player_event_logs.ReloadSettings();
case ServerOP_ServerReloadRequest: {
auto o = (ServerReload::Request*) pack->pBuffer;
if (o->type == ServerReload::Type::Logs) {
LogSys.LoadLogDatabaseSettings();
player_event_logs.ReloadSettings();
}
break;
}
case ServerOP_PlayerEvent: {
-4
View File
@@ -11,8 +11,6 @@ SET(world_sources
dynamic_zone_manager.cpp
eql_config.cpp
eqemu_api_world_data_service.cpp
expedition_database.cpp
expedition_message.cpp
launcher_link.cpp
launcher_list.cpp
lfplist.cpp
@@ -48,8 +46,6 @@ SET(world_headers
dynamic_zone_manager.h
eql_config.h
eqemu_api_world_data_service.h
expedition_database.h
expedition_message.h
launcher_link.h
launcher_list.h
lfplist.h
+3 -3
View File
@@ -127,8 +127,8 @@ public:
inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { tell_queue.push_back(scm); }
void ProcessTellQueue();
void SetPendingExpeditionInvite(ServerPacket* pack) { p_pending_expedition_invite.reset(pack->Copy()); };
std::unique_ptr<ServerPacket> GetPendingExpeditionInvite() { return std::move(p_pending_expedition_invite); }
void SetPendingDzInvite(ServerPacket* pack) { m_dz_invite.reset(pack->Copy()); };
std::unique_ptr<ServerPacket> GetPendingDzInvite() { return std::move(m_dz_invite); }
private:
void ClearVars(bool iAll = false);
@@ -176,7 +176,7 @@ private:
// Tell Queue -- really a vector :D
std::vector<ServerChannelMessage_Struct *> tell_queue;
std::unique_ptr<ServerPacket> p_pending_expedition_invite = nullptr;
std::unique_ptr<ServerPacket> m_dz_invite;
};
#endif /*CLIENTENTRY_H_*/
+7 -4
View File
@@ -1717,13 +1717,13 @@ void ClientList::SendCharacterMessageID(ClientListEntry* character,
return;
}
SerializeBuffer serialized_args;
SerializeBuffer argbuf;
for (const auto& arg : args)
{
serialized_args.WriteString(arg);
argbuf.WriteString(arg);
}
uint32_t args_size = static_cast<uint32_t>(serialized_args.size());
uint32_t args_size = static_cast<uint32_t>(argbuf.size());
uint32_t pack_size = sizeof(CZClientMessageString_Struct) + args_size;
auto pack = std::make_unique<ServerPacket>(ServerOP_CZClientMessageString, pack_size);
auto buf = reinterpret_cast<CZClientMessageString_Struct*>(pack->pBuffer);
@@ -1731,7 +1731,10 @@ void ClientList::SendCharacterMessageID(ClientListEntry* character,
buf->chat_type = chat_type;
strn0cpy(buf->client_name, character->name(), sizeof(buf->client_name));
buf->args_size = args_size;
memcpy(buf->args, serialized_args.buffer(), serialized_args.size());
if (argbuf.size() > 0)
{
memcpy(buf->args, argbuf.buffer(), argbuf.size());
}
character->Server()->SendPacket(pack.get());
}
+1 -5
View File
@@ -911,11 +911,7 @@ void ConsoleReloadWorld(
)
{
connection->SendLine("Reloading World...");
auto pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct));
ReloadWorld_Struct *RW = (ReloadWorld_Struct *) pack->pBuffer;
RW->global_repop = ReloadWorld::Repop;
zoneserver_list.SendPacket(pack);
safe_delete(pack);
zoneserver_list.SendServerReload(ServerReload::Type::WorldRepop, nullptr);
}
auto debounce_reload = std::chrono::system_clock::now();
+2 -128
View File
@@ -164,132 +164,6 @@ void DynamicZone::SendZonesLeaderChanged()
zoneserver_list.SendPacket(pack.get());
}
void DynamicZone::HandleZoneMessage(ServerPacket* pack)
{
switch (pack->opcode)
{
case ServerOP_DzCreated:
{
dynamic_zone_manager.CacheNewDynamicZone(pack);
break;
}
case ServerOP_DzSetCompass:
case ServerOP_DzSetSafeReturn:
case ServerOP_DzSetZoneIn:
{
auto buf = reinterpret_cast<ServerDzLocation_Struct*>(pack->pBuffer);
auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id);
if (dz)
{
if (pack->opcode == ServerOP_DzSetCompass)
{
dz->SetCompass(buf->zone_id, buf->x, buf->y, buf->z, false);
}
else if (pack->opcode == ServerOP_DzSetSafeReturn)
{
dz->SetSafeReturn(buf->zone_id, buf->x, buf->y, buf->z, buf->heading, false);
}
else if (pack->opcode == ServerOP_DzSetZoneIn)
{
dz->SetZoneInLocation(buf->x, buf->y, buf->z, buf->heading, false);
}
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzSetSwitchID:
{
auto buf = reinterpret_cast<ServerDzSwitchID_Struct*>(pack->pBuffer);
auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id);
if (dz)
{
dz->ProcessSetSwitchID(buf->dz_switch_id);
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzAddRemoveMember:
{
auto buf = reinterpret_cast<ServerDzMember_Struct*>(pack->pBuffer);
auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id);
if (dz)
{
auto status = static_cast<DynamicZoneMemberStatus>(buf->character_status);
dz->ProcessMemberAddRemove({ buf->character_id, buf->character_name, status }, buf->removed);
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzSwapMembers:
{
auto buf = reinterpret_cast<ServerDzMemberSwap_Struct*>(pack->pBuffer);
auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id);
if (dz)
{
// we add first in world so new member can be chosen if leader is removed
auto status = static_cast<DynamicZoneMemberStatus>(buf->add_character_status);
dz->ProcessMemberAddRemove({ buf->add_character_id, buf->add_character_name, status }, false);
dz->ProcessMemberAddRemove({ buf->remove_character_id, buf->remove_character_name }, true);
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzRemoveAllMembers:
{
auto buf = reinterpret_cast<ServerDzID_Struct*>(pack->pBuffer);
auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id);
if (dz)
{
dz->ProcessRemoveAllMembers();
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzSetSecondsRemaining:
{
auto buf = reinterpret_cast<ServerDzSetDuration_Struct*>(pack->pBuffer);
auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id);
if (dz)
{
dz->SetSecondsRemaining(buf->seconds);
}
break;
}
case ServerOP_DzGetMemberStatuses:
{
auto buf = reinterpret_cast<ServerDzID_Struct*>(pack->pBuffer);
auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id);
if (dz)
{
dz->SendZoneMemberStatuses(buf->sender_zone_id, buf->sender_instance_id);
}
break;
}
case ServerOP_DzUpdateMemberStatus:
{
auto buf = reinterpret_cast<ServerDzMemberStatus_Struct*>(pack->pBuffer);
auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id);
if (dz)
{
auto status = static_cast<DynamicZoneMemberStatus>(buf->status);
dz->ProcessMemberStatusChange(buf->character_id, status);
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzMovePC:
{
auto buf = reinterpret_cast<ServerDzMovePC_Struct*>(pack->pBuffer);
auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id);
if (dz && dz->HasMember(buf->character_id))
{
zoneserver_list.SendPacket(buf->sender_zone_id, buf->sender_instance_id, pack);
}
break;
}
};
}
void DynamicZone::ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed)
{
DynamicZoneBase::ProcessMemberAddRemove(member, removed);
@@ -345,13 +219,13 @@ void DynamicZone::SendZoneMemberStatuses(uint16_t zone_id, uint16_t instance_id)
void DynamicZone::CacheMemberStatuses()
{
if (m_has_member_statuses)
if (m_has_member_statuses || m_members.empty())
{
return;
}
// called when a new dz is cached to fill member statuses
std::string zone_name{};
std::string zone_name;
std::vector<ClientListEntry*> all_clients;
all_clients.reserve(client_list.GetClientCount());
client_list.GetClients(zone_name.c_str(), all_clients);
+1 -2
View File
@@ -22,7 +22,6 @@ public:
using DynamicZoneBase::DynamicZoneBase; // inherit base constructors
static DynamicZone* FindDynamicZoneByID(uint32_t dz_id);
static void HandleZoneMessage(ServerPacket* pack);
void SetSecondsRemaining(uint32_t seconds_remaining) override;
@@ -33,7 +32,7 @@ public:
protected:
Database& GetDatabase() override;
void ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed) override;
bool ProcessMemberStatusChange(uint32_t member_id, DynamicZoneMemberStatus status) override;
bool ProcessMemberStatusChange(uint32_t character_id, DynamicZoneMemberStatus status) override;
bool SendServerPacket(ServerPacket* packet) override;
private:
+280 -39
View File
@@ -1,12 +1,14 @@
#include "dynamic_zone_manager.h"
#include "dynamic_zone.h"
#include "cliententry.h"
#include "clientlist.h"
#include "worlddb.h"
#include "zonelist.h"
#include "zoneserver.h"
#include "../common/rulesys.h"
#include "../common/repositories/expeditions_repository.h"
#include "../common/repositories/expedition_lockouts_repository.h"
#include "../common/repositories/dynamic_zone_lockouts_repository.h"
extern ClientList client_list;
extern ZSList zoneserver_list;
DynamicZoneManager dynamic_zone_manager;
@@ -27,6 +29,8 @@ void DynamicZoneManager::PurgeExpiredDynamicZones()
{
LogDynamicZones("Purging [{}] dynamic zone(s)", dz_ids.size());
DynamicZoneLockoutsRepository::DeleteWhere(database,
fmt::format("dynamic_zone_id IN ({})", fmt::join(dz_ids, ",")));
DynamicZoneMembersRepository::DeleteWhere(database,
fmt::format("dynamic_zone_id IN ({})", Strings::Join(dz_ids, ",")));
DynamicZonesRepository::DeleteWhere(database,
@@ -34,8 +38,7 @@ void DynamicZoneManager::PurgeExpiredDynamicZones()
}
}
DynamicZone* DynamicZoneManager::CreateNew(
DynamicZone& dz_request, const std::vector<DynamicZoneMember>& members)
DynamicZone* DynamicZoneManager::TryCreate(DynamicZone& dz_request, const std::vector<DynamicZoneMember>& members)
{
// this creates a new dz instance and saves it to both db and cache
uint32_t dz_id = dz_request.Create();
@@ -46,15 +49,12 @@ DynamicZone* DynamicZoneManager::CreateNew(
}
auto dz = std::make_unique<DynamicZone>(dz_request);
if (!members.empty())
{
dz->SaveMembers(members);
dz->CacheMemberStatuses();
}
dz->SaveMembers(members);
dz->CacheMemberStatuses();
LogDynamicZones("Created new dz [{}] for zone [{}]", dz_id, dz_request.GetZoneID());
auto pack = dz->CreateServerDzCreatePacket(0, 0);
auto pack = dz->CreateServerPacket(0, 0);
zoneserver_list.SendPacket(pack.get());
auto inserted = dynamic_zone_cache.emplace(dz_id, std::move(dz));
@@ -63,18 +63,17 @@ DynamicZone* DynamicZoneManager::CreateNew(
void DynamicZoneManager::CacheNewDynamicZone(ServerPacket* pack)
{
auto buf = reinterpret_cast<ServerDzCreateSerialized_Struct*>(pack->pBuffer);
auto buf = reinterpret_cast<ServerDzCreate_Struct*>(pack->pBuffer);
auto new_dz = std::make_unique<DynamicZone>();
new_dz->LoadSerializedDzPacket(buf->cereal_data, buf->cereal_size);
new_dz->Unserialize({ buf->cereal_data, buf->cereal_size });
new_dz->CacheMemberStatuses();
// reserialize with member statuses cached before forwarding (restore origin zone)
auto repack = new_dz->CreateServerDzCreatePacket(buf->origin_zone_id, buf->origin_instance_id);
auto repack = new_dz->CreateServerPacket(buf->origin_zone_id, buf->origin_instance_id);
uint32_t dz_id = new_dz->GetID();
dynamic_zone_cache.emplace(dz_id, std::move(new_dz));
LogDynamicZones("Cached new dynamic zone [{}]", dz_id);
dynamic_zone_cache.emplace(buf->dz_id, std::move(new_dz));
LogDynamicZones("Cached new dynamic zone [{}]", buf->dz_id);
zoneserver_list.SendPacket(repack.get());
}
@@ -83,18 +82,19 @@ void DynamicZoneManager::CacheAllFromDatabase()
{
BenchTimer bench;
auto dynamic_zones = DynamicZonesRepository::AllWithInstanceNotExpired(database);
auto dynamic_zone_members = DynamicZoneMembersRepository::GetAllWithNames(database);
auto dzs = DynamicZonesRepository::AllWithInstanceNotExpired(database);
auto members = DynamicZoneMembersRepository::AllWithNames(database);
auto lockouts = DynamicZoneLockoutsRepository::All(database);
dynamic_zone_cache.clear();
dynamic_zone_cache.reserve(dynamic_zones.size());
dynamic_zone_cache.reserve(dzs.size());
for (auto& entry : dynamic_zones)
for (auto& entry : dzs)
{
uint32_t dz_id = entry.id;
auto dz = std::make_unique<DynamicZone>(std::move(entry));
for (auto& member : dynamic_zone_members)
for (auto& member : members)
{
if (member.dynamic_zone_id == dz_id)
{
@@ -102,6 +102,14 @@ void DynamicZoneManager::CacheAllFromDatabase()
}
}
for (auto& lockout : lockouts)
{
if (lockout.dynamic_zone_id == dz->GetID())
{
dz->m_lockouts.emplace_back(dz->GetName(), std::move(lockout));
}
}
// note leader status won't be updated here until leader is set by owning system (expeditions)
dz->CacheMemberStatuses();
@@ -142,24 +150,10 @@ void DynamicZoneManager::Process()
dynamic_zone_cache.erase(dz_id);
}
// need to look up expedition ids until lockouts are moved to dynamic zones
std::vector<uint32_t> expedition_ids;
auto expeditions = ExpeditionsRepository::GetWhere(database,
fmt::format("dynamic_zone_id IN ({})", Strings::Join(dynamic_zone_ids, ",")));
if (!expeditions.empty())
{
for (const auto& expedition : expeditions)
{
expedition_ids.emplace_back(expedition.id);
}
ExpeditionLockoutsRepository::DeleteWhere(database,
fmt::format("expedition_id IN ({})", Strings::Join(expedition_ids, ",")));
}
ExpeditionsRepository::DeleteWhere(database,
fmt::format("dynamic_zone_id IN ({})", Strings::Join(dynamic_zone_ids, ",")));
DynamicZoneMembersRepository::RemoveAllMembers(database, dynamic_zone_ids);
DynamicZoneLockoutsRepository::DeleteWhere(database,
fmt::format("dynamic_zone_id IN ({})", fmt::join(dynamic_zone_ids, ",")));
DynamicZoneMembersRepository::DeleteWhere(database,
fmt::format("dynamic_zone_id IN ({})", fmt::join(dynamic_zone_ids, ",")));
DynamicZonesRepository::DeleteWhere(database,
fmt::format("id IN ({})", Strings::Join(dynamic_zone_ids, ",")));
}
@@ -174,3 +168,250 @@ void DynamicZoneManager::LoadTemplates()
m_dz_templates[dz_template.id] = dz_template;
}
}
void DynamicZoneManager::HandleZoneMessage(ServerPacket* pack)
{
switch (pack->opcode)
{
case ServerOP_DzCreated:
{
CacheNewDynamicZone(pack);
break;
}
case ServerOP_DzAddPlayer:
{
auto buf = reinterpret_cast<ServerDzCommand_Struct*>(pack->pBuffer);
ClientListEntry* cle = client_list.FindCharacter(buf->target_name);
if (cle && cle->Server())
{
// continue in the add target's zone
buf->is_char_online = true;
cle->Server()->SendPacket(pack);
}
else
{
// add target not online, return to inviter
ClientListEntry* inviter_cle = client_list.FindCharacter(buf->requester_name);
if (inviter_cle && inviter_cle->Server())
{
inviter_cle->Server()->SendPacket(pack);
}
}
break;
}
case ServerOP_DzSaveInvite:
{
auto buf = reinterpret_cast<ServerDzCommand_Struct*>(pack->pBuffer);
if (ClientListEntry* cle = client_list.FindCharacter(buf->target_name))
{
// store packet on cle and re-send it when client requests it
buf->is_char_online = true;
pack->opcode = ServerOP_DzAddPlayer;
cle->SetPendingDzInvite(pack);
}
break;
}
case ServerOP_DzRequestInvite:
{
auto buf = reinterpret_cast<ServerCharacterID_Struct*>(pack->pBuffer);
if (ClientListEntry* cle = client_list.FindCLEByCharacterID(buf->char_id))
{
auto invite_pack = cle->GetPendingDzInvite();
if (invite_pack && cle->Server())
{
cle->Server()->SendPacket(invite_pack.get());
}
}
break;
}
case ServerOP_DzMakeLeader:
{
auto buf = reinterpret_cast<ServerDzCommandMakeLeader_Struct*>(pack->pBuffer);
// notify requester (old leader) and new leader of the result
ZoneServer* new_leader_zs = nullptr;
ClientListEntry* leader_cle = client_list.FindCharacter(buf->new_leader_name);
if (leader_cle && leader_cle->Server())
{
auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id);
if (dz && dz->GetLeaderID() == buf->requester_id)
{
buf->is_success = dz->SetNewLeader(leader_cle->CharID());
}
buf->is_online = true;
new_leader_zs = leader_cle->Server();
new_leader_zs->SendPacket(pack);
}
// if old and new leader are in the same zone only send one message
ClientListEntry* requester_cle = client_list.FindCLEByCharacterID(buf->requester_id);
if (requester_cle && requester_cle->Server() && requester_cle->Server() != new_leader_zs)
{
requester_cle->Server()->SendPacket(pack);
}
break;
}
case ServerOP_DzSetCompass:
case ServerOP_DzSetSafeReturn:
case ServerOP_DzSetZoneIn:
{
auto buf = reinterpret_cast<ServerDzLocation_Struct*>(pack->pBuffer);
if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id))
{
if (pack->opcode == ServerOP_DzSetCompass)
{
dz->SetCompass(buf->zone_id, buf->x, buf->y, buf->z, false);
}
else if (pack->opcode == ServerOP_DzSetSafeReturn)
{
dz->SetSafeReturn(buf->zone_id, buf->x, buf->y, buf->z, buf->heading, false);
}
else if (pack->opcode == ServerOP_DzSetZoneIn)
{
dz->SetZoneInLocation(buf->x, buf->y, buf->z, buf->heading, false);
}
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzSetSwitchID:
{
auto buf = reinterpret_cast<ServerDzSwitchID_Struct*>(pack->pBuffer);
if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id))
{
dz->ProcessSetSwitchID(buf->dz_switch_id);
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzAddRemoveMember:
{
auto buf = reinterpret_cast<ServerDzMember_Struct*>(pack->pBuffer);
if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id))
{
auto status = static_cast<DynamicZoneMemberStatus>(buf->character_status);
dz->ProcessMemberAddRemove({ buf->character_id, buf->character_name, status }, buf->removed);
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzSwapMembers:
{
auto buf = reinterpret_cast<ServerDzMemberSwap_Struct*>(pack->pBuffer);
if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id))
{
// we add first in world so new member can be chosen if leader is removed
auto status = static_cast<DynamicZoneMemberStatus>(buf->add_character_status);
dz->ProcessMemberAddRemove({ buf->add_character_id, buf->add_character_name, status }, false);
dz->ProcessMemberAddRemove({ buf->remove_character_id, buf->remove_character_name }, true);
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzRemoveAllMembers:
{
auto buf = reinterpret_cast<ServerDzID_Struct*>(pack->pBuffer);
if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id))
{
dz->ProcessRemoveAllMembers();
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzSetSecondsRemaining:
{
auto buf = reinterpret_cast<ServerDzSetDuration_Struct*>(pack->pBuffer);
if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id))
{
dz->SetSecondsRemaining(buf->seconds);
}
break;
}
case ServerOP_DzGetMemberStatuses:
{
auto buf = reinterpret_cast<ServerDzID_Struct*>(pack->pBuffer);
if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id))
{
dz->SendZoneMemberStatuses(buf->sender_zone_id, buf->sender_instance_id);
}
break;
}
case ServerOP_DzUpdateMemberStatus:
{
auto buf = reinterpret_cast<ServerDzMemberStatus_Struct*>(pack->pBuffer);
if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id))
{
auto status = static_cast<DynamicZoneMemberStatus>(buf->status);
dz->ProcessMemberStatusChange(buf->character_id, status);
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzMovePC:
{
auto buf = reinterpret_cast<ServerDzMovePC_Struct*>(pack->pBuffer);
auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id);
if (dz && dz->HasMember(buf->character_id))
{
zoneserver_list.SendPacket(buf->sender_zone_id, buf->sender_instance_id, pack);
}
break;
}
case ServerOP_DzLock:
{
auto buf = reinterpret_cast<ServerDzLock_Struct*>(pack->pBuffer);
if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id))
{
dz->SetLocked(buf->lock);
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzReplayOnJoin:
{
auto buf = reinterpret_cast<ServerDzBool_Struct*>(pack->pBuffer);
if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id))
{
dz->SetReplayOnJoin(buf->enabled);
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzLockout:
{
auto buf = reinterpret_cast<ServerDzLockout_Struct*>(pack->pBuffer);
if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id))
{
DzLockout lockout{ dz->GetUUID(), dz->GetName(), buf->event_name, buf->expire_time, buf->duration };
dz->HandleLockoutUpdate(lockout, buf->remove, buf->members_only);
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzLockoutDuration:
{
auto buf = reinterpret_cast<ServerDzLockout_Struct*>(pack->pBuffer);
if (auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id))
{
DzLockout lockout{ dz->GetUUID(), dz->GetName(), buf->event_name, buf->expire_time, buf->duration };
dz->HandleLockoutDuration(lockout, buf->seconds, buf->members_only, false);
}
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_DzCharacterLockout:
{
auto buf = reinterpret_cast<ServerDzCharacterLockout_Struct*>(pack->pBuffer);
auto cle = client_list.FindCLEByCharacterID(buf->char_id);
if (cle && cle->Server())
{
cle->Server()->SendPacket(pack);
}
break;
}
default:
break;
};
}
+2 -1
View File
@@ -20,7 +20,8 @@ public:
void CacheAllFromDatabase();
void CacheNewDynamicZone(ServerPacket* pack);
DynamicZone* CreateNew(DynamicZone& dz_request, const std::vector<DynamicZoneMember>& members);
DynamicZone* TryCreate(DynamicZone& dz_request, const std::vector<DynamicZoneMember>& members);
void HandleZoneMessage(ServerPacket* pack);
void LoadTemplates();
void Process();
void PurgeExpiredDynamicZones();
+14 -102
View File
@@ -5,6 +5,7 @@
#include "zoneserver.h"
#include "zonelist.h"
#include "../common/database_schema.h"
#include "../common/server_reload_types.h"
#include "../common/zone_store.h"
#include "worlddb.h"
#include "wguild_mgr.h"
@@ -113,136 +114,47 @@ void callGetClientList(Json::Value &response)
client_list.GetClientList(response);
}
struct Reload {
std::string command{};
uint16 opcode;
std::string desc{};
};
std::vector<Reload> reload_types = {
Reload{.command = "aa", .opcode = ServerOP_ReloadAAData, .desc = "Alternate Advancement"},
Reload{.command = "alternate_currencies", .opcode = ServerOP_ReloadAlternateCurrencies, .desc = "Alternate Currencies"},
Reload{.command = "base_data", .opcode = ServerOP_ReloadBaseData, .desc = "Base Data"},
Reload{.command = "blocked_spells", .opcode = ServerOP_ReloadBlockedSpells, .desc = "Blocked Spells"},
Reload{.command = "commands", .opcode = ServerOP_ReloadCommands, .desc = "Commands"},
Reload{.command = "content_flags", .opcode = ServerOP_ReloadContentFlags, .desc = "Content Flags"},
Reload{.command = "data_buckets_cache", .opcode = ServerOP_ReloadDataBucketsCache, .desc = "Data Buckets Cache"},
Reload{.command = "doors", .opcode = ServerOP_ReloadDoors, .desc = "Doors"},
Reload{.command = "dztemplates", .opcode = ServerOP_ReloadDzTemplates, .desc = "Dynamic Zone Templates"},
Reload{.command = "ground_spawns", .opcode = ServerOP_ReloadGroundSpawns, .desc = "Ground Spawns"},
Reload{.command = "level_mods", .opcode = ServerOP_ReloadLevelEXPMods, .desc = "Level Mods"},
Reload{.command = "logs", .opcode = ServerOP_ReloadLogs, .desc = "Log Settings"},
Reload{.command = "loot", .opcode = ServerOP_ReloadLoot, .desc = "Loot"},
Reload{.command = "merchants", .opcode = ServerOP_ReloadMerchants, .desc = "Merchants"},
Reload{.command = "npc_emotes", .opcode = ServerOP_ReloadNPCEmotes, .desc = "NPC Emotes"},
Reload{.command = "npc_spells", .opcode = ServerOP_ReloadNPCSpells, .desc = "NPC Spells"},
Reload{.command = "objects", .opcode = ServerOP_ReloadObjects, .desc = "Objects"},
Reload{.command = "opcodes", .opcode = ServerOP_ReloadOpcodes, .desc = "Opcodes"},
Reload{.command = "perl_export", .opcode = ServerOP_ReloadPerlExportSettings, .desc = "Perl Event Export Settings"},
Reload{.command = "rules", .opcode = ServerOP_ReloadRules, .desc = "Rules"},
Reload{.command = "skill_caps", .opcode = ServerOP_ReloadSkillCaps, .desc = "Skill Caps"},
Reload{.command = "static", .opcode = ServerOP_ReloadStaticZoneData, .desc = "Static Zone Data"},
Reload{.command = "tasks", .opcode = ServerOP_ReloadTasks, .desc = "Tasks"},
Reload{.command = "titles", .opcode = ServerOP_ReloadTitles, .desc = "Titles"},
Reload{.command = "traps", .opcode = ServerOP_ReloadTraps, .desc = "Traps"},
Reload{.command = "variables", .opcode = ServerOP_ReloadVariables, .desc = "Variables"},
Reload{.command = "veteran_rewards", .opcode = ServerOP_ReloadVeteranRewards, .desc = "Veteran Rewards"},
Reload{.command = "world", .opcode = ServerOP_ReloadWorld, .desc = "World"},
Reload{.command = "zone_points", .opcode = ServerOP_ReloadZonePoints, .desc = "Zone Points"},
};
void getReloadTypes(Json::Value &response)
{
for (auto &c: reload_types) {
for (auto &t: ServerReload::GetTypes()) {
Json::Value v;
v["command"] = c.command;
v["opcode"] = c.opcode;
v["description"] = c.desc;
v["command"] = std::to_string(t);
v["description"] = ServerReload::GetName(t);
response.append(v);
}
}
void EQEmuApiWorldDataService::reload(Json::Value &r, const std::vector<std::string> &args)
{
std::vector<std::string> commands{};
commands.reserve(reload_types.size());
for (auto &c: reload_types) {
commands.emplace_back(c.command);
commands.reserve(ServerReload::GetTypes().size());
for (auto &c: ServerReload::GetTypes()) {
commands.emplace_back(std::to_string(c));
}
std::string command = !args[1].empty() ? args[1] : "";
if (command.empty()) {
message(r, fmt::format("Need to provide a type to reload. Example(s) [{}]", Strings::Implode("|", commands)));
message(r, fmt::format("Need to provide a type ID to reload. Example(s) [{}]", Strings::Implode("|", commands)));
return;
}
ServerPacket *pack = nullptr;
bool found_command = false;
for (auto &c: reload_types) {
if (command == c.command) {
if (c.command == "world") {
uint8 global_repop = ReloadWorld::NoRepop;
bool found_command = false;
if (Strings::IsNumber(args[2])) {
global_repop = static_cast<uint8>(Strings::ToUnsignedInt(args[2]));
if (global_repop > ReloadWorld::ForceRepop) {
global_repop = ReloadWorld::ForceRepop;
}
}
message(
r,
fmt::format(
"Attempting to reload Quests {}worldwide.",
(
global_repop ?
(
global_repop == ReloadWorld::Repop ?
"and repop NPCs " :
"and forcefully repop NPCs "
) :
""
)
)
);
pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct));
auto RW = (ReloadWorld_Struct *) pack->pBuffer;
RW->global_repop = global_repop;
}
else {
pack = new ServerPacket(c.opcode, 0);
message(r, fmt::format("Reloading [{}] globally", c.desc));
if (c.opcode == ServerOP_ReloadLogs) {
LogSys.LoadLogDatabaseSettings();
QSLink.SendPacket(pack);
UCSLink.SendPacket(pack);
}
else if (c.opcode == ServerOP_ReloadRules) {
RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true);
}
}
found_command = true;
for (auto &t: ServerReload::GetTypes()) {
if (std::to_string(t) == command || Strings::ToLower(ServerReload::GetName(t)) == command) {
message(r, fmt::format("Reloading [{}] globally", ServerReload::GetName(t)));
zoneserver_list.SendServerReload(t, nullptr);
}
found_command = true;
}
if (!found_command) {
message(r, fmt::format("Need to provide a type to reload. Example(s) [{}]", Strings::Implode("|", commands)));
return;
}
if (pack) {
zoneserver_list.SendPacket(pack);
}
safe_delete(pack);
}
void EQEmuApiWorldDataService::message(Json::Value &r, const std::string &message)
-78
View File
@@ -1,78 +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_database.h"
#include "worlddb.h"
#include "../common/repositories/expeditions_repository.h"
#include "../common/repositories/expedition_lockouts_repository.h"
#include "../common/repositories/dynamic_zone_members_repository.h"
void ExpeditionDatabase::PurgeExpiredExpeditions()
{
std::string query = SQL(
SELECT
expeditions.id,
expeditions.dynamic_zone_id
FROM expeditions
LEFT JOIN dynamic_zones ON expeditions.dynamic_zone_id = dynamic_zones.id
LEFT JOIN instance_list ON dynamic_zones.instance_id = instance_list.id
LEFT JOIN
(
SELECT dynamic_zone_id, COUNT(*) member_count
FROM dynamic_zone_members
GROUP BY dynamic_zone_id
) dynamic_zone_members
ON dynamic_zone_members.dynamic_zone_id = expeditions.dynamic_zone_id
WHERE
instance_list.id IS NULL
OR dynamic_zone_members.member_count IS NULL
OR dynamic_zone_members.member_count = 0
OR (instance_list.start_time + instance_list.duration) <= UNIX_TIMESTAMP();
);
auto results = database.QueryDatabase(query);
if (results.Success())
{
std::vector<std::string> expedition_ids;
std::vector<uint32_t> dynamic_zone_ids;
for (auto row = results.begin(); row != results.end(); ++row)
{
expedition_ids.emplace_back(row[0]);
dynamic_zone_ids.emplace_back(static_cast<uint32_t>(strtoul(row[1], nullptr, 10)));
}
if (!expedition_ids.empty())
{
ExpeditionsRepository::DeleteWhere(database, fmt::format("id IN ({})", fmt::join(expedition_ids, ",")));
ExpeditionLockoutsRepository::DeleteWhere(database, fmt::format("expedition_id IN ({})", fmt::join(expedition_ids, ",")));
DynamicZoneMembersRepository::RemoveAllMembers(database, dynamic_zone_ids);
}
}
}
void ExpeditionDatabase::PurgeExpiredCharacterLockouts()
{
std::string query = SQL(
DELETE FROM character_expedition_lockouts
WHERE expire_time <= NOW();
);
database.QueryDatabase(query);
}
-35
View File
@@ -1,35 +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 WORLD_EXPEDITION_DATABASE_H
#define WORLD_EXPEDITION_DATABASE_H
#include <cstdint>
#include <vector>
class Expedition;
namespace ExpeditionDatabase
{
void PurgeExpiredExpeditions();
void PurgeExpiredCharacterLockouts();
};
#endif
-151
View File
@@ -1,151 +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 "dynamic_zone.h"
#include "expedition_message.h"
#include "cliententry.h"
#include "clientlist.h"
#include "zonelist.h"
#include "zoneserver.h"
#include "../common/servertalk.h"
#include <algorithm>
extern ClientList client_list;
extern ZSList zoneserver_list;
void ExpeditionMessage::HandleZoneMessage(ServerPacket* pack)
{
switch (pack->opcode)
{
case ServerOP_ExpeditionCreate:
{
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ExpeditionDzAddPlayer:
{
ExpeditionMessage::AddPlayer(pack);
break;
}
case ServerOP_ExpeditionDzMakeLeader:
{
ExpeditionMessage::MakeLeader(pack);
break;
}
case ServerOP_ExpeditionCharacterLockout:
{
auto buf = reinterpret_cast<ServerExpeditionCharacterLockout_Struct*>(pack->pBuffer);
auto cle = client_list.FindCLEByCharacterID(buf->character_id);
if (cle && cle->Server())
{
cle->Server()->SendPacket(pack);
}
break;
}
case ServerOP_ExpeditionSaveInvite:
{
ExpeditionMessage::SaveInvite(pack);
break;
}
case ServerOP_ExpeditionRequestInvite:
{
ExpeditionMessage::RequestInvite(pack);
break;
}
}
}
void ExpeditionMessage::AddPlayer(ServerPacket* pack)
{
auto buf = reinterpret_cast<ServerDzCommand_Struct*>(pack->pBuffer);
ClientListEntry* invited_cle = client_list.FindCharacter(buf->target_name);
if (invited_cle && invited_cle->Server())
{
// continue in the add target's zone
buf->is_char_online = true;
invited_cle->Server()->SendPacket(pack);
}
else
{
// add target not online, return to inviter
ClientListEntry* inviter_cle = client_list.FindCharacter(buf->requester_name);
if (inviter_cle && inviter_cle->Server())
{
inviter_cle->Server()->SendPacket(pack);
}
}
}
void ExpeditionMessage::MakeLeader(ServerPacket* pack)
{
auto buf = reinterpret_cast<ServerDzCommandMakeLeader_Struct*>(pack->pBuffer);
// notify requester (old leader) and new leader of the result
ZoneServer* new_leader_zs = nullptr;
ClientListEntry* new_leader_cle = client_list.FindCharacter(buf->new_leader_name);
if (new_leader_cle && new_leader_cle->Server())
{
auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id);
if (dz && dz->GetLeaderID() == buf->requester_id)
{
buf->is_success = dz->SetNewLeader(new_leader_cle->CharID());
}
buf->is_online = true;
new_leader_zs = new_leader_cle->Server();
new_leader_zs->SendPacket(pack);
}
// if old and new leader are in the same zone only send one message
ClientListEntry* requester_cle = client_list.FindCLEByCharacterID(buf->requester_id);
if (requester_cle && requester_cle->Server() && requester_cle->Server() != new_leader_zs)
{
requester_cle->Server()->SendPacket(pack);
}
}
void ExpeditionMessage::SaveInvite(ServerPacket* pack)
{
auto buf = reinterpret_cast<ServerDzCommand_Struct*>(pack->pBuffer);
ClientListEntry* invited_cle = client_list.FindCharacter(buf->target_name);
if (invited_cle)
{
// store packet on cle and re-send it when client requests it
buf->is_char_online = true;
pack->opcode = ServerOP_ExpeditionDzAddPlayer;
invited_cle->SetPendingExpeditionInvite(pack);
}
}
void ExpeditionMessage::RequestInvite(ServerPacket* pack)
{
auto buf = reinterpret_cast<ServerExpeditionCharacterID_Struct*>(pack->pBuffer);
ClientListEntry* cle = client_list.FindCLEByCharacterID(buf->character_id);
if (cle)
{
auto invite_pack = cle->GetPendingExpeditionInvite();
if (invite_pack && cle->Server())
{
cle->Server()->SendPacket(invite_pack.get());
}
}
}
-35
View File
@@ -1,35 +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 WORLD_EXPEDITION_MESSAGE_H
#define WORLD_EXPEDITION_MESSAGE_H
class ServerPacket;
namespace ExpeditionMessage
{
void AddPlayer(ServerPacket* pack);
void HandleZoneMessage(ServerPacket* pack);
void MakeLeader(ServerPacket* pack);
void RequestInvite(ServerPacket* pack);
void SaveInvite(ServerPacket* pack);
};
#endif
+2 -2
View File
@@ -76,10 +76,10 @@
#include "web_interface.h"
#include "console.h"
#include "dynamic_zone_manager.h"
#include "expedition_database.h"
#include "world_server_cli.h"
#include "../common/content/world_content_service.h"
#include "../common/repositories/character_expedition_lockouts_repository.h"
#include "../common/repositories/character_task_timers_repository.h"
#include "../common/zone_store.h"
#include "world_event_scheduler.h"
@@ -449,7 +449,7 @@ int main(int argc, char **argv)
if (PurgeInstanceTimer.Check()) {
database.PurgeExpiredInstances();
database.PurgeAllDeletedDataBuckets();
ExpeditionDatabase::PurgeExpiredCharacterLockouts();
CharacterExpeditionLockoutsRepository::DeleteWhere(database, "expire_time <= NOW()");
CharacterTaskTimersRepository::DeleteWhere(database, "expire_time <= NOW()");
}
+1 -1
View File
@@ -1172,7 +1172,7 @@ void SharedTaskManager::CreateDynamicZone(SharedTask *shared_task, DynamicZone &
}
}
auto new_dz = dynamic_zone_manager.CreateNew(dz_request, dz_members);
auto new_dz = dynamic_zone_manager.TryCreate(dz_request, dz_members);
if (new_dz) {
auto shared_task_dz = SharedTaskDynamicZonesRepository::NewEntity();
shared_task_dz.shared_task_id = shared_task->GetDbSharedTask().id;
+1 -1
View File
@@ -283,7 +283,7 @@ void SharedTaskWorldMessaging::HandleZoneMessage(ServerPacket *pack)
auto t = shared_task_manager.FindSharedTaskByTaskIdAndCharacterId(buf->task_id, buf->source_character_id);
if (t) {
DynamicZone dz;
dz.LoadSerializedDzPacket(buf->cereal_data, buf->cereal_size);
dz.Unserialize({ buf->cereal_data, buf->cereal_size });
shared_task_manager.CreateDynamicZone(t, dz);
}
+3 -4
View File
@@ -5,12 +5,12 @@
#include "../common/http/uri.h"
#include "../common/net/console_server.h"
#include "../common/net/servertalk_server.h"
#include "../common/repositories/character_expedition_lockouts_repository.h"
#include "../common/repositories/character_task_timers_repository.h"
#include "../common/rulesys.h"
#include "../common/strings.h"
#include "adventure_manager.h"
#include "dynamic_zone_manager.h"
#include "expedition_database.h"
#include "login_server_list.h"
#include "shared_task_manager.h"
#include "ucs.h"
@@ -367,9 +367,8 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
LogInfo("Purging expired dynamic zones and members");
dynamic_zone_manager.PurgeExpiredDynamicZones();
LogInfo("Purging expired expeditions");
ExpeditionDatabase::PurgeExpiredExpeditions();
ExpeditionDatabase::PurgeExpiredCharacterLockouts();
LogInfo("Purging expired character expedition lockouts");
CharacterExpeditionLockoutsRepository::DeleteWhere(database, "expire_time <= NOW()");
LogInfo("Purging expired character task timers");
CharacterTaskTimersRepository::DeleteWhere(database, "expire_time <= NOW()");
+1
View File
@@ -4,6 +4,7 @@
#include <string>
#include "../common/types.h"
#include "../common/discord/discord.h"
#include "ucs.h"
extern UCSConnection UCSLink;
+2 -5
View File
@@ -2,6 +2,7 @@
#include "../common/servertalk.h"
#include <ctime>
#include "../common/rulesys.h"
#include "../common/server_reload_types.h"
void WorldEventScheduler::Process(ZSList *zs_list)
{
@@ -55,11 +56,7 @@ void WorldEventScheduler::Process(ZSList *zs_list)
if (e.event_type == ServerEvents::EVENT_TYPE_RELOAD_WORLD) {
LogScheduler("Sending reload world event [{}]", e.event_data.c_str());
auto pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct));
auto *reload_world = (ReloadWorld_Struct *) pack->pBuffer;
reload_world->global_repop = ReloadWorld::Repop;
zs_list->SendPacket(pack);
safe_delete(pack);
zs_list->SendServerReload(ServerReload::Type::WorldRepop, nullptr);
}
}
}
+82
View File
@@ -28,10 +28,19 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/event_sub.h"
#include "web_interface.h"
#include "../common/zone_store.h"
#include "../common/events/player_event_logs.h"
#include "../common/patches/patches.h"
#include "../common/skill_caps.h"
#include "../common/content/world_content_service.h"
#include "world_boot.h"
#include "shared_task_manager.h"
#include "dynamic_zone_manager.h"
#include "ucs.h"
extern uint32 numzones;
extern EQ::Random emu_random;
extern WebInterfaceList web_interface;
extern SharedTaskManager shared_task_manager;
volatile bool UCSServerAvailable_ = false;
void CatchSignal(int sig_num);
@@ -861,3 +870,76 @@ bool ZSList::SendPacketToBootedZones(ServerPacket* pack)
return true;
}
void ZSList::SendServerReload(ServerReload::Type type, uchar *packet)
{
static auto pack = ServerPacket(ServerOP_ServerReloadRequest, sizeof(ServerReload::Request));
auto r = (ServerReload::Request *) pack.pBuffer;
// Copy the packet data if it exists
if (packet) {
memcpy(pack.pBuffer, packet, sizeof(ServerReload::Request));
}
r->type = type;
r->requires_zone_booted = true;
LogInfo("Sending reload to all zones for type [{}]", ServerReload::GetName(type));
static const std::unordered_set<ServerReload::Type> no_zone_boot_required = {
ServerReload::Type::Opcodes,
ServerReload::Type::Rules,
ServerReload::Type::ContentFlags,
ServerReload::Type::Logs,
ServerReload::Type::Commands,
ServerReload::Type::PerlExportSettings,
ServerReload::Type::DataBucketsCache,
ServerReload::Type::WorldRepop
};
// Set requires_zone_booted flag before executing reload logic
if (no_zone_boot_required.contains(type)) {
r->requires_zone_booted = false;
}
// reload at the world level
if (type == ServerReload::Type::Opcodes) {
ReloadAllPatches();
} else if (type == ServerReload::Type::Rules) {
RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true);
} else if (type == ServerReload::Type::SkillCaps) {
skill_caps.ReloadSkillCaps();
} else if (type == ServerReload::Type::ContentFlags) {
content_service.SetExpansionContext()->ReloadContentFlags();
} else if (type == ServerReload::Type::Logs) {
LogSys.LoadLogDatabaseSettings();
player_event_logs.ReloadSettings();
UCSLink.SendPacket(&pack);
} else if (type == ServerReload::Type::Tasks) {
shared_task_manager.LoadTaskData();
} else if (type == ServerReload::Type::DzTemplates) {
dynamic_zone_manager.LoadTemplates();
}
// Send the packet to all zones with staggered delays
// to prevent all zones from reloading at the same time
// and causing a massive spike in CPU usage
// This is especially important for large servers
// with many zones
// we reload 10 zones every second
int counter = 0;
for (auto &z: zone_server_list) {
bool is_local = r->zone_server_id != 0;
// if the zone reload is local to a specific zone
if (r->zone_server_id != 0 && r->zone_server_id != z->GetID()) {
continue;
}
// if the reload is local, we don't need to stagger the reloads
r->reload_at_unix = is_local ? 0 : (std::time(nullptr) + 1) + (counter / 10);
z->SendPacket(&pack);
++counter;
}
}
+2
View File
@@ -5,6 +5,7 @@
#include "../common/eqtime.h"
#include "../common/timer.h"
#include "../common/event/timer.h"
#include "../common/server_reload_types.h"
#include <vector>
#include <memory>
#include <deque>
@@ -69,6 +70,7 @@ public:
ZoneServer* FindByZoneID(uint32 ZoneID);
const std::list<std::unique_ptr<ZoneServer>> &getZoneServerList() const;
void SendServerReload(ServerReload::Type type, uchar *packet = nullptr);
private:
void OnTick(EQ::Timer *t);
+14 -75
View File
@@ -39,7 +39,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/zone_store.h"
#include "dynamic_zone.h"
#include "dynamic_zone_manager.h"
#include "expedition_message.h"
#include "shared_task_world_messaging.h"
#include "../common/shared_tasks.h"
#include "shared_task_manager.h"
@@ -50,6 +49,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../zone/data_bucket.h"
#include "../common/repositories/guild_tributes_repository.h"
#include "../common/skill_caps.h"
#include "../common/server_reload_types.h"
extern ClientList client_list;
extern GroupLFPList LFPGroupList;
@@ -1357,11 +1357,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
QSLink.SendPacket(pack);
break;
}
case ServerOP_ReloadOpcodes: {
ReloadAllPatches();
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_CZDialogueWindow:
case ServerOP_CZLDoNUpdate:
case ServerOP_CZMarquee:
@@ -1375,10 +1370,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
case ServerOP_Consent:
case ServerOP_DepopAllPlayersCorpses:
case ServerOP_DepopPlayerCorpse:
case ServerOP_ExpeditionLockState:
case ServerOP_ExpeditionLockout:
case ServerOP_ExpeditionLockoutDuration:
case ServerOP_ExpeditionReplayOnJoin:
case ServerOP_GuildRankUpdate:
case ServerOP_ItemStatus:
case ServerOP_KickPlayer:
@@ -1389,30 +1380,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
case ServerOP_RaidGroupSay:
case ServerOP_RaidSay:
case ServerOP_RefreshCensorship:
case ServerOP_ReloadAAData:
case ServerOP_ReloadAlternateCurrencies:
case ServerOP_ReloadBaseData:
case ServerOP_ReloadBlockedSpells:
case ServerOP_ReloadCommands:
case ServerOP_ReloadDoors:
case ServerOP_ReloadDataBucketsCache:
case ServerOP_ReloadFactions:
case ServerOP_ReloadGroundSpawns:
case ServerOP_ReloadLevelEXPMods:
case ServerOP_ReloadMerchants:
case ServerOP_ReloadNPCEmotes:
case ServerOP_ReloadNPCSpells:
case ServerOP_ReloadObjects:
case ServerOP_ReloadPerlExportSettings:
case ServerOP_ReloadStaticZoneData:
case ServerOP_ReloadTitles:
case ServerOP_ReloadTraps:
case ServerOP_ReloadVariables:
case ServerOP_ReloadVeteranRewards:
case ServerOP_ReloadWorld:
case ServerOP_ReloadZonePoints:
case ServerOP_ReloadZoneData:
case ServerOP_ReloadLoot:
case ServerOP_RezzPlayerAccept:
case ServerOP_SpawnStatusChange:
case ServerOP_TraderMessaging:
@@ -1430,14 +1397,9 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ReloadSkillCaps: {
zoneserver_list.SendPacket(pack);
skill_caps.ReloadSkillCaps();
break;
}
case ServerOP_ReloadRules: {
zoneserver_list.SendPacket(pack);
RuleManager::Instance()->LoadRules(&database, "default", true);
case ServerOP_ServerReloadRequest: {
auto o = (ServerReload::Request*) pack->pBuffer;
zoneserver_list.SendServerReload((ServerReload::Type) o->type, pack->pBuffer);
break;
}
case ServerOP_IsOwnerOnline: {
@@ -1465,29 +1427,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
}
break;
}
case ServerOP_ReloadContentFlags: {
zoneserver_list.SendPacket(pack);
content_service.SetExpansionContext()->ReloadContentFlags();
break;
}
case ServerOP_ReloadLogs: {
zoneserver_list.SendPacket(pack);
QSLink.SendPacket(pack);
UCSLink.SendPacket(pack);
LogSys.LoadLogDatabaseSettings();
player_event_logs.ReloadSettings();
break;
}
case ServerOP_ReloadTasks: {
shared_task_manager.LoadTaskData();
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ReloadDzTemplates: {
dynamic_zone_manager.LoadTemplates();
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ChangeSharedMem: {
auto hotfix_name = std::string((char*) pack->pBuffer);
@@ -1532,16 +1471,11 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
SharedTaskWorldMessaging::HandleZoneMessage(pack);
break;
}
case ServerOP_ExpeditionCreate:
case ServerOP_ExpeditionDzAddPlayer:
case ServerOP_ExpeditionDzMakeLeader:
case ServerOP_ExpeditionCharacterLockout:
case ServerOP_ExpeditionSaveInvite:
case ServerOP_ExpeditionRequestInvite: {
ExpeditionMessage::HandleZoneMessage(pack);
break;
}
case ServerOP_DzCreated:
case ServerOP_DzAddPlayer:
case ServerOP_DzSaveInvite:
case ServerOP_DzRequestInvite:
case ServerOP_DzMakeLeader:
case ServerOP_DzAddRemoveMember:
case ServerOP_DzSwapMembers:
case ServerOP_DzRemoveAllMembers:
@@ -1552,8 +1486,13 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
case ServerOP_DzSetZoneIn:
case ServerOP_DzSetSwitchID:
case ServerOP_DzMovePC:
case ServerOP_DzLock:
case ServerOP_DzReplayOnJoin:
case ServerOP_DzLockout:
case ServerOP_DzLockoutDuration:
case ServerOP_DzCharacterLockout:
case ServerOP_DzUpdateMemberStatus: {
DynamicZone::HandleZoneMessage(pack);
dynamic_zone_manager.HandleZoneMessage(pack);
break;
}
case ServerOP_GuildTributeUpdate: {
-4
View File
@@ -36,8 +36,6 @@ SET(zone_sources
encounter.cpp
entity.cpp
exp.cpp
expedition.cpp
expedition_database.cpp
expedition_request.cpp
fastmath.cpp
fearpath.cpp
@@ -205,8 +203,6 @@ SET(zone_headers
encounter.h
entity.h
event_codes.h
expedition.h
expedition_database.h
expedition_request.h
fastmath.h
forage.h
+34 -11
View File
@@ -405,17 +405,12 @@ bool Mob::CheckWillAggro(Mob *mob) {
return false;
}
Mob *pet_owner = mob->GetOwner();
if (
pet_owner &&
pet_owner->IsClient() &&
(
!RuleB(Aggro, AggroPlayerPets) ||
pet_owner->CastToClient()->GetGM() ||
mob->GetSpecialAbility(SpecialAbility::AggroImmunity)
)
) {
return false;
Mob* pet_owner = mob->GetOwner();
if (pet_owner && pet_owner->IsOfClientBot()) {
if (mob->IsPetAggroExempt(pet_owner)) {
return false;
}
}
if (IsNPC() && mob->IsNPC() && mob->GetSpecialAbility(SpecialAbility::NPCAggroImmunity)) {
@@ -582,6 +577,34 @@ bool Mob::CheckWillAggro(Mob *mob) {
return false;
}
bool Mob::IsPetAggroExempt(Mob* pet_owner) {
if (!pet_owner) {
return false;
}
bool exempt_client_pet = pet_owner->IsClient() && !RuleB(Aggro, AggroPlayerPets);
bool exempt_bot_pet = pet_owner->IsBot() && !RuleB(Aggro, AggroBotPets);
if (exempt_client_pet || exempt_bot_pet) {
return true;
}
Mob* ultimate_owner = GetUltimateOwner();
Client* client_owner = (ultimate_owner && ultimate_owner->IsClient())
? ultimate_owner->CastToClient()
: nullptr;
if (client_owner && client_owner->GetGM()) {
return true;
}
if (GetSpecialAbility(SpecialAbility::AggroImmunity)) {
return true;
}
return false;
}
int EntityList::FleeAllyCount(Mob* attacker, Mob* skipped)
{
// Return a list of how many NPCs of the same faction or race are within aggro range of the given exclude Mob.
+142 -268
View File
@@ -3265,7 +3265,11 @@ Mob* Bot::GetBotTarget(Client* bot_owner)
}
bool Bot::TargetValidation(Mob* other) {
if (!other || GetAppearance() == eaDead) {
if (GetAppearance() == eaDead || GetHP() < 0) {
return false;
}
if (!other || other->GetAppearance() == eaDead || other->GetHP() < 0) {
return false;
}
@@ -7684,15 +7688,21 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) {
}
uint8 Bot::GetNumberNeedingHealedInGroup(Mob* tar, uint16 spell_type, uint16 spell_id, float range) {
if (!tar) {
if (!TargetValidation(tar)) {
return 0;
}
uint8 count = 0;
auto target_list = tar->IsClient() ? GatherSpellTargets(false, tar) : tar->CastToBot()->GetSpellTargetList();
std::vector<Mob*> target_list = tar->IsClient() ? GatherSpellTargets(false, tar) : tar->CastToBot()->GetSpellTargetList();
for (Mob* m : target_list) {
if (m && tar->CalculateDistance(m) < range && CastChecks(spell_id, m, spell_type, true, IsGroupBotSpellType(spell_type))) {
for (auto* m : target_list) {
if (
m &&
entity_list.IsMobInZone(m) &&
TargetValidation(m) &&
tar->CalculateDistance(m) < range &&
CastChecks(spell_id, m, spell_type, true, IsGroupBotSpellType(spell_type))
) {
++count;
}
}
@@ -8353,12 +8363,13 @@ void Bot::ListBotSpells(uint8 min_level)
bot_owner->Message(
Chat::White,
fmt::format(
"Spell {} | Spell: {} | Add Spell: {}",
"Spell {} | Spell: {} (ID: {}) | Add Spell: {}",
spell_number,
Saylink::Silent(
fmt::format("^spellinfo {}", s.spellid),
spells[s.spellid].name
),
s.spellid,
Saylink::Silent(
fmt::format("^spellsettingsadd {} {} {} {}", s.spellid, s.priority, s.min_hp, s.max_hp), "Add")
).c_str()
@@ -9403,7 +9414,7 @@ void Bot::DoItemClick(const EQ::ItemData *item, uint16 slot_id)
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND] = { 0 };
bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) {
if (!tar) {
if (!TargetValidation(tar)) {
LogBotSpellChecksDetail("{} says, 'Cancelling cast due to PrecastChecks !tar.'", GetCleanName());
return false;
}
@@ -9425,24 +9436,18 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) {
return true;
}
if (
GetManaRatio() < GetSpellTypeMinManaLimit(spell_type) ||
GetManaRatio() > GetSpellTypeMaxManaLimit(spell_type)
) {
if (!EQ::ValueWithin(GetManaRatio(), GetSpellTypeMinManaLimit(spell_type), GetSpellTypeMaxManaLimit(spell_type))) {
LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName());
return false;
}
if (
GetHPRatio() < GetSpellTypeMinHPLimit(spell_type) ||
GetHPRatio() > GetSpellTypeMaxHPLimit(spell_type)
) {
if (!EQ::ValueWithin(GetHPRatio(), GetSpellTypeMinHPLimit(spell_type), GetSpellTypeMaxHPLimit(spell_type))) {
LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinHPLimit or GetSpellTypeMaxHPLimit.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName());
return false;
}
if (!GetUltimateSpellTypeDelayCheck(spell_type, tar)) {
LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellTypeDelayCheck.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName());
if (!GetUltimateSpellTypeRecastCheck(spell_type, tar)) {
LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellTypeRecastCheck.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName());
return false;
}
@@ -9451,10 +9456,7 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) {
case BotSpellTypes::AEMez:
return true;
default:
if (
GetHPRatioForSpellType(spell_type, tar) < GetUltimateSpellTypeMinThreshold(spell_type, tar) ||
GetHPRatioForSpellType(spell_type, tar) > GetUltimateSpellTypeMaxThreshold(spell_type, tar)
) {
if (!EQ::ValueWithin(GetHPRatioForSpellType(spell_type, tar), GetUltimateSpellTypeMinThreshold(spell_type, tar), GetUltimateSpellTypeMaxThreshold(spell_type, tar))) {
LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellTypeMinThreshold or GetUltimateSpellTypeMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName());
return false;
}
@@ -9465,7 +9467,7 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) {
bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool prechecks, bool ae_check) {
if (prechecks) {
if (!tar) {
if (!tar || tar->GetAppearance() == eaDead || tar->GetHP() < 0) {
LogBotSpellChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName());
return false;
}
@@ -9616,7 +9618,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck
if (
BotSpellTypeRequiresTarget(spell_type) &&
!tar
!TargetValidation(tar)
) {
LogBotSpellChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName());
return false;
@@ -9706,13 +9708,10 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck
}
//LogBotSpellChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName());
if (!CanCastSpellType(spell_type, spell_id, tar)) {
LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to CanCastSpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName());
return false;
}
if (IsCommandedSpell()) { //stop checks here for commanded spells
return true;
}
if (!IsValidTargetType(spell_id, GetSpellTargetType(spell_id), tar->GetBodyType())) {
LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName());
return false;
@@ -9749,7 +9748,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck
return false;
}
if (spell_type == UINT16_MAX) { //AA/Forced cast checks, return here
if (IsCommandedSpell() || spell_type == UINT16_MAX) { //AA/Forced cast checks, return here
return true;
}
@@ -10520,7 +10519,7 @@ void Bot::SetBotSpellRecastTimer(uint16 spell_type, Mob* tar, bool precast) {
return;
}
if (!precast && IsBotSpellTypeOtherBeneficial(spell_type)) {
if (!precast && BotSpellTypeUsesTargetSettings(spell_type)) {
return;
}
@@ -10545,7 +10544,7 @@ void Bot::SetBotSpellRecastTimer(uint16 spell_type, Mob* tar, bool precast) {
owner->CastToBot()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellTypeDelay(spell_type, tar) + added_delay));
}
}
else if (IsBotSpellTypeOtherBeneficial(spell_type)) {
else if (BotSpellTypeUsesTargetSettings(spell_type)) {
if (tar->IsClient()) {
tar->CastToClient()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellTypeDelay(spell_type, tar) + added_delay));
}
@@ -10559,6 +10558,12 @@ void Bot::SetBotSpellRecastTimer(uint16 spell_type, Mob* tar, bool precast) {
}
BotSpell Bot::GetSpellByHealType(uint16 spell_type, Mob* tar) {
if (!TargetValidation(tar)) {
BotSpell result;
return result;
}
switch (spell_type) {
case BotSpellTypes::VeryFastHeals:
case BotSpellTypes::PetVeryFastHeals:
@@ -10758,6 +10763,7 @@ bool Bot::GetDefaultSpellTypeHold(uint16 spell_type, uint8 stance) {
switch (stance) {
case Stance::AEBurn:
case Stance::Burn:
case Stance::Assist:
return true;
default:
return false;
@@ -11037,31 +11043,36 @@ bool Bot::GetDefaultSpellTypeAggroCheck(uint16 spell_type, uint8 stance) {
switch (stance) {
case Stance::AEBurn:
case Stance::Burn:
case Stance::Aggressive:
return false;
default:
break;
}
switch (spell_type) {
case BotSpellTypes::Nuke:
case BotSpellTypes::Root:
case BotSpellTypes::Snare:
case BotSpellTypes::DOT:
case BotSpellTypes::Slow:
case BotSpellTypes::Debuff:
case BotSpellTypes::Fear:
case BotSpellTypes::Stun:
case BotSpellTypes::AEDebuff:
case BotSpellTypes::AEDispel:
case BotSpellTypes::AEDoT:
case BotSpellTypes::AEFear:
case BotSpellTypes::AEHateLine:
case BotSpellTypes::AELifetap:
case BotSpellTypes::AENukes:
case BotSpellTypes::AERains:
case BotSpellTypes::AEStun:
case BotSpellTypes::AEDebuff:
case BotSpellTypes::AERoot:
case BotSpellTypes::AESlow:
case BotSpellTypes::AESnare:
case BotSpellTypes::AEFear:
case BotSpellTypes::AEDispel:
case BotSpellTypes::AERoot:
case BotSpellTypes::AEDoT:
case BotSpellTypes::AEStun:
case BotSpellTypes::DOT:
case BotSpellTypes::Debuff:
case BotSpellTypes::Dispel:
case BotSpellTypes::Fear:
case BotSpellTypes::HateLine:
case BotSpellTypes::Nuke:
case BotSpellTypes::PBAENuke:
case BotSpellTypes::Root:
case BotSpellTypes::Slow:
case BotSpellTypes::Snare:
case BotSpellTypes::Stun:
return true;
default:
return false;
@@ -11142,7 +11153,7 @@ uint16 Bot::GetDefaultSpellTypeAnnounceCast(uint16 spell_type, uint8 stance) {
}
bool Bot::GetUltimateSpellTypeHold(uint16 spell_type, Mob* tar) {
if (!tar) {
if (!TargetValidation(tar)) {
return GetSpellTypeHold(spell_type);
}
@@ -11519,11 +11530,12 @@ uint16 Bot::GetParentSpellType(uint16 spell_type) {
case BotSpellTypes::AELull:
case BotSpellTypes::Lull:
return BotSpellTypes::Lull;
case BotSpellTypes::HateLine:
case BotSpellTypes::AEHateLine:
return BotSpellTypes::HateLine;
case BotSpellTypes::Charm:
case BotSpellTypes::Escape:
case BotSpellTypes::HateRedux:
case BotSpellTypes::HateLine:
case BotSpellTypes::AEHateLine:
case BotSpellTypes::InCombatBuff:
case BotSpellTypes::InCombatBuffSong:
case BotSpellTypes::OutOfCombatBuffSong:
@@ -11983,7 +11995,7 @@ bool Bot::HasRequiredLoSForPositioning(Mob* tar) {
bool Bot::HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob* tar) {
int spell_range = caster->GetActSpellRange(spell_id, spells[spell_id].range);
int spell_ae_range = caster->GetActSpellRange(spell_id, spells[spell_id].aoe_range);
int spell_ae_range = caster->GetAOERange(spell_id);
int target_count = 0;
for (auto& close_mob : caster->m_close_mobs) {
@@ -11994,6 +12006,18 @@ bool Bot::HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob*
}
switch (spell_type) {
case BotSpellTypes::AELull:
if (m->GetSpecialAbility(SpecialAbility::PacifyImmunity)) {
continue;
}
break;
case BotSpellTypes::AEMez:
if (m->GetSpecialAbility(SpecialAbility::MesmerizeImmunity)) {
continue;
}
break;
case BotSpellTypes::AEDispel:
if (m->GetSpecialAbility(SpecialAbility::DispellImmunity)) {
continue;
@@ -12077,182 +12101,6 @@ bool Bot::HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob*
return true;
}
void Bot::CopySettings(Bot* to, uint8 setting_type, uint16 spell_type) {
switch (setting_type) {
case BotSettingCategories::BaseSetting:
for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) {
to->SetBotBaseSetting(i, GetBotBaseSetting(i));
}
break;
case BotSettingCategories::SpellHold:
if (spell_type != UINT16_MAX) {
to->SetSpellTypeHold(spell_type, GetSpellTypeHold(spell_type));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypeHold(i, GetSpellTypeHold(i));
}
}
break;
case BotSettingCategories::SpellDelay:
if (spell_type != UINT16_MAX) {
to->SetSpellTypeDelay(spell_type, GetSpellTypeDelay(spell_type));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypeDelay(i, GetSpellTypeDelay(i));
}
}
break;
case BotSettingCategories::SpellMinThreshold:
if (spell_type != UINT16_MAX) {
to->SetSpellTypeMinThreshold(spell_type, GetSpellTypeMinThreshold(spell_type));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypeMinThreshold(i, GetSpellTypeMinThreshold(i));
}
}
break;
case BotSettingCategories::SpellMaxThreshold:
if (spell_type != UINT16_MAX) {
to->SetSpellTypeMaxThreshold(spell_type, GetSpellTypeMaxThreshold(spell_type));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypeMaxThreshold(i, GetSpellTypeMaxThreshold(i));
}
}
break;
case BotSettingCategories::SpellTypeAggroCheck:
if (spell_type != UINT16_MAX) {
to->SetSpellTypeAggroCheck(spell_type, GetSpellTypeAggroCheck(spell_type));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypeAggroCheck(i, GetSpellTypeAggroCheck(i));
}
}
break;
case BotSettingCategories::SpellTypeResistLimit:
if (spell_type != UINT16_MAX) {
to->SetSpellTypeResistLimit(spell_type, GetSpellTypeResistLimit(spell_type));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypeResistLimit(i, GetSpellTypeResistLimit(i));
}
}
break;
case BotSettingCategories::SpellTypeMinManaPct:
if (spell_type != UINT16_MAX) {
to->SetSpellTypeMinManaLimit(spell_type, GetSpellTypeMinManaLimit(spell_type));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypeMinManaLimit(i, GetSpellTypeMinManaLimit(i));
}
}
break;
case BotSettingCategories::SpellTypeMaxManaPct:
if (spell_type != UINT16_MAX) {
to->SetSpellTypeMaxManaLimit(spell_type, GetSpellTypeMaxManaLimit(spell_type));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypeMaxManaLimit(i, GetSpellTypeMaxManaLimit(i));
}
}
break;
case BotSettingCategories::SpellTypeMinHPPct:
if (spell_type != UINT16_MAX) {
to->SetSpellTypeMinHPLimit(spell_type, GetSpellTypeMinHPLimit(spell_type));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypeMinHPLimit(i, GetSpellTypeMinHPLimit(i));
}
}
break;
case BotSettingCategories::SpellTypeMaxHPPct:
if (spell_type != UINT16_MAX) {
to->SetSpellTypeMaxHPLimit(spell_type, GetSpellTypeMaxHPLimit(spell_type));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypeMaxHPLimit(i, GetSpellTypeMaxHPLimit(i));
}
}
break;
case BotSettingCategories::SpellTypeIdlePriority:
if (spell_type != UINT16_MAX) {
to->SetSpellTypePriority(spell_type, BotPriorityCategories::Idle, GetSpellTypePriority(spell_type, BotPriorityCategories::Idle));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypePriority(i, BotPriorityCategories::Idle, GetSpellTypePriority(i, BotPriorityCategories::Idle));
}
}
break;
case BotSettingCategories::SpellTypeEngagedPriority:
if (spell_type != UINT16_MAX) {
to->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, GetSpellTypePriority(spell_type, BotPriorityCategories::Engaged));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypePriority(i, BotPriorityCategories::Engaged, GetSpellTypePriority(i, BotPriorityCategories::Engaged));
}
}
break;
case BotSettingCategories::SpellTypePursuePriority:
if (spell_type != UINT16_MAX) {
to->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, GetSpellTypePriority(spell_type, BotPriorityCategories::Pursue));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypePriority(i, BotPriorityCategories::Pursue, GetSpellTypePriority(i, BotPriorityCategories::Pursue));
}
}
break;
case BotSettingCategories::SpellTypeAEOrGroupTargetCount:
if (spell_type != UINT16_MAX) {
to->SetSpellTypeAEOrGroupTargetCount(spell_type, GetSpellTypeAEOrGroupTargetCount(spell_type));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypeAEOrGroupTargetCount(i, GetSpellTypeAEOrGroupTargetCount(i));
}
}
break;
case BotSettingCategories::SpellTypeAnnounceCast:
if (spell_type != UINT16_MAX) {
to->SetSpellTypeAnnounceCast(spell_type, GetSpellTypeAnnounceCast(spell_type));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetSpellTypeAnnounceCast(i, GetSpellTypeAnnounceCast(i));
}
}
break;
}
}
void Bot::CopyBotSpellSettings(Bot* to)
{
if (!to) {
@@ -12801,12 +12649,16 @@ bool Bot::IsValidBotSpellCategory(uint8 setting_type) {
}
std::string Bot::GetBotSpellCategoryName(uint8 setting_type) {
return Bot::IsValidBotSpellCategory(setting_type) ? botSpellCategory_names[setting_type] : "UNKNOWN CATEGORY";
return Bot::IsValidBotSpellCategory(setting_type) ? bot_setting_category_names[setting_type] : "UNKNOWN CATEGORY";
}
std::string Bot::GetBotSpellCategoryShortName(uint8 setting_type) {
return Bot::IsValidBotSpellCategory(setting_type) ? bot_setting_category_short_names[setting_type] : "UNKNOWN CATEGORY";
}
uint16 Bot::GetBotSpellCategoryIDByShortName(std::string setting_string) {
for (int i = BotSettingCategories::START; i <= BotSettingCategories::END; ++i) {
if (!Strings::ToLower(setting_string).compare(Strings::ToLower(GetBotSpellCategoryName(i)))) {
if (!Strings::ToLower(setting_string).compare(Strings::ToLower(GetBotSpellCategoryShortName(i)))) {
return i;
}
}
@@ -12833,11 +12685,11 @@ uint16 Bot::GetBaseSettingIDByShortName(std::string setting_string) {
}
std::string Bot::GetSpellTypeShortNameByID(uint16 spell_type) {
return IsValidBotSpellType(spell_type) ? spellType_shortNames[spell_type] : "UNKNOWN SPELLTYPE";
return IsValidBotSpellType(spell_type) ? spell_type_short_names[spell_type] : "UNKNOWN SPELLTYPE";
}
std::string Bot::GetSpellTypeNameByID(uint16 spell_type) {
return IsValidBotSpellType(spell_type) ? spellType_names[spell_type] : "UNKNOWN SPELLTYPE";
return IsValidBotSpellType(spell_type) ? spell_type_names[spell_type] : "UNKNOWN SPELLTYPE";
}
bool Bot::IsValidSubType(uint16 sub_type) {
@@ -13001,7 +12853,7 @@ uint8 Bot::GetDefaultSpellTypeMaxThreshold(uint16 spell_type, uint8 stance) {
}
case BotSpellTypes::GroupHeals:
case BotSpellTypes::RegularHeal:
if (bot_class == Class::Necromancer || (bot_class == Class::Shaman && !GetSpellTypeHold(BotSpellTypes::InCombatBuff))) {
if (bot_class == Class::Necromancer || bot_class == Class::Shaman) {
return 60;
}
@@ -13130,81 +12982,88 @@ uint8 Bot::GetDefaultSpellTypeMaxThreshold(uint16 spell_type, uint8 stance) {
}
uint16 Bot::GetUltimateSpellTypeDelay(uint16 spell_type, Mob* tar) {
if (!tar) {
if (!TargetValidation(tar)) {
return GetSpellTypeDelay(spell_type);
}
if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) {
Mob* owner = tar->GetOwner();
Mob* owner = tar->IsPet() ? tar->GetOwner() : nullptr;
return owner->IsClient() ? owner->CastToClient()->GetSpellTypeDelay(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellTypeDelay(
GetPetBotSpellType(spell_type));
if (owner && owner->IsOfClientBot()) {
return owner->IsClient()
? owner->CastToClient()->GetSpellTypeDelay(GetPetBotSpellType(spell_type))
: owner->CastToBot()->GetSpellTypeDelay(GetPetBotSpellType(spell_type));
}
if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) {
return tar->IsClient() ? tar->CastToClient()->GetSpellTypeDelay(spell_type) : tar->CastToBot()->GetSpellTypeDelay(
spell_type
);
if (BotSpellTypeUsesTargetSettings(spell_type) && tar->IsOfClientBot()) {
return tar->IsClient()
? tar->CastToClient()->GetSpellTypeDelay(spell_type)
: tar->CastToBot()->GetSpellTypeDelay(spell_type);
}
return GetSpellTypeDelay(spell_type);
}
bool Bot::GetUltimateSpellTypeDelayCheck(uint16 spell_type, Mob* tar) {
if (!tar) {
bool Bot::GetUltimateSpellTypeRecastCheck(uint16 spell_type, Mob* tar) {
if (!TargetValidation(tar)) {
return SpellTypeRecastCheck(spell_type);
}
if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) {
Mob* owner = tar->GetOwner();
Mob* owner = tar->IsPet() ? tar->GetOwner() : nullptr;
return owner->IsClient() ? owner->CastToClient()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type)) : owner->CastToBot()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type));
if (owner && owner->IsOfClientBot()) {
return owner->IsClient()
? owner->CastToClient()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type))
: owner->CastToBot()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type));
}
if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) {
return tar->IsClient() ? tar->CastToClient()->SpellTypeRecastCheck(spell_type) : tar->CastToBot()->SpellTypeRecastCheck(spell_type);
if (BotSpellTypeUsesTargetSettings(spell_type) && tar->IsOfClientBot()) {
return tar->IsClient()
? tar->CastToClient()->SpellTypeRecastCheck(spell_type)
: tar->CastToBot()->SpellTypeRecastCheck(spell_type);
}
return SpellTypeRecastCheck(spell_type);
}
uint8 Bot::GetUltimateSpellTypeMinThreshold(uint16 spell_type, Mob* tar) {
if (!tar) {
if (!TargetValidation(tar)) {
return GetSpellTypeMinThreshold(spell_type);
}
if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) {
Mob* owner = tar->GetOwner();
Mob* owner = tar->IsPet() ? tar->GetOwner() : nullptr;
return owner->IsClient() ? owner->CastToClient()->GetSpellTypeMinThreshold(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellTypeMinThreshold(
GetPetBotSpellType(spell_type));
if (owner && owner->IsOfClientBot()) {
return owner->IsClient()
? owner->CastToClient()->GetSpellTypeMinThreshold(GetPetBotSpellType(spell_type))
: owner->CastToBot()->GetSpellTypeMinThreshold(GetPetBotSpellType(spell_type));
}
if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) {
return tar->IsClient() ? tar->CastToClient()->GetSpellTypeMinThreshold(spell_type) : tar->CastToBot()->GetSpellTypeMinThreshold(
spell_type
);
if (BotSpellTypeUsesTargetSettings(spell_type) && tar->IsOfClientBot()) {
return tar->IsClient()
? tar->CastToClient()->GetSpellTypeMinThreshold(spell_type)
: tar->CastToBot()->GetSpellTypeMinThreshold(spell_type);
}
return GetSpellTypeMinThreshold(spell_type);
}
uint8 Bot::GetUltimateSpellTypeMaxThreshold(uint16 spell_type, Mob* tar) {
if (!tar) {
if (!TargetValidation(tar)) {
return GetSpellTypeMaxThreshold(spell_type);
}
if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) {
Mob* owner = tar->GetOwner();
Mob* owner = tar->IsPet() ? tar->GetOwner() : nullptr;
return owner->IsClient() ? owner->CastToClient()->GetSpellTypeMaxThreshold(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellTypeMaxThreshold(
GetPetBotSpellType(spell_type));
if (owner && owner->IsOfClientBot()) {
return owner->IsClient()
? owner->CastToClient()->GetSpellTypeMaxThreshold(GetPetBotSpellType(spell_type))
: owner->CastToBot()->GetSpellTypeMaxThreshold(GetPetBotSpellType(spell_type));
}
if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) {
return tar->IsClient() ? tar->CastToClient()->GetSpellTypeMaxThreshold(spell_type) : tar->CastToBot()->GetSpellTypeMaxThreshold(
spell_type
);
if (BotSpellTypeUsesTargetSettings(spell_type) && tar->IsOfClientBot()) {
return tar->IsClient()
? tar->CastToClient()->GetSpellTypeMaxThreshold(spell_type)
: tar->CastToBot()->GetSpellTypeMaxThreshold(spell_type);
}
return GetSpellTypeMaxThreshold(spell_type);
@@ -13288,11 +13147,9 @@ bool Bot::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) {
(
IsEffectInSpell(spell_id, SE_Root) ||
IsEffectInSpell(spell_id, SE_MovementSpeed)
)
) {
if (GetSpecialAbility(SpecialAbility::SnareImmunity)) {
return true;
}
)
) {
return true;
}
if (IsLifetapSpell(spell_id)) {
@@ -13320,3 +13177,20 @@ std::vector<Mob*> Bot::GetSpellTargetList(bool entire_raid) {
return entire_raid ? _spell_target_list : _group_spell_target_list;
}
bool Bot::IsValidBotStance(uint8 stance) {
switch (stance) {
case Stance::Passive:
case Stance::Balanced:
case Stance::Efficient:
case Stance::Aggressive:
case Stance::Assist:
case Stance::Burn:
case Stance::AEBurn:
return true;
default:
return false;
}
return false;
}
+26 -6
View File
@@ -93,7 +93,7 @@ enum BotCastingChanceConditional : uint8
cntHSND = 16
};
namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed
namespace BotSettingCategories {
constexpr uint8 BaseSetting = 0;
constexpr uint8 SpellHold = 1;
constexpr uint8 SpellDelay = 2;
@@ -118,13 +118,32 @@ namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed
constexpr uint16 END = BotSettingCategories::SpellTypeAnnounceCast;
};
static std::map<uint8, std::string> botSpellCategory_names = {
static std::map<uint8, std::string> bot_setting_category_names = {
{ BotSettingCategories::BaseSetting, "Base Setting" },
{ BotSettingCategories::SpellHold, "Spell Holds" },
{ BotSettingCategories::SpellDelay, "Spell Delays" },
{ BotSettingCategories::SpellMinThreshold, "Spell Minimum Thresholds" },
{ BotSettingCategories::SpellMaxThreshold, "Spell Maximum Thresholds" },
{ BotSettingCategories::SpellTypeResistLimit, "Spell Resist Limits" },
{ BotSettingCategories::SpellTypeAggroCheck, "Spell Aggro Checks" },
{ BotSettingCategories::SpellTypeMinManaPct, "Spell Min Mana Percent" },
{ BotSettingCategories::SpellTypeMaxManaPct, "Spell Max Mana Percent" },
{ BotSettingCategories::SpellTypeMinHPPct, "Spell Min HP Percent" },
{ BotSettingCategories::SpellTypeMaxHPPct, "Spell Max HP Percent" },
{ BotSettingCategories::SpellTypeIdlePriority, "Spell Idle Priority" },
{ BotSettingCategories::SpellTypeEngagedPriority, "Spell Engaged Priority" },
{ BotSettingCategories::SpellTypePursuePriority, "Spell Pursue Priority" },
{ BotSettingCategories::SpellTypeAEOrGroupTargetCount, "Spell Target Counts" },
{ BotSettingCategories::SpellTypeAnnounceCast, "Spell Announce Casts" }
};
static std::map<uint8, std::string> bot_setting_category_short_names = {
{ BotSettingCategories::BaseSetting, "BaseSetting" },
{ BotSettingCategories::SpellHold, "SpellHolds" },
{ BotSettingCategories::SpellDelay, "SpellDelays" },
{ BotSettingCategories::SpellMinThreshold, "SpellMinThresholds" },
{ BotSettingCategories::SpellMaxThreshold, "SpellMaxThresholds" },
{ BotSettingCategories::SpellTypeResistLimit, "SpellResistLimit" },
{ BotSettingCategories::SpellTypeResistLimit, "SpellResistLimits" },
{ BotSettingCategories::SpellTypeAggroCheck, "SpellAggroChecks" },
{ BotSettingCategories::SpellTypeMinManaPct, "SpellMinManaPct" },
{ BotSettingCategories::SpellTypeMaxManaPct, "SpellMaxManaPct" },
@@ -137,7 +156,7 @@ static std::map<uint8, std::string> botSpellCategory_names = {
{ BotSettingCategories::SpellTypeAnnounceCast, "SpellAnnounceCasts" }
};
namespace BotPriorityCategories { // Update GetBotSpellCategoryName as needed
namespace BotPriorityCategories {
constexpr uint8 Idle = 0;
constexpr uint8 Engaged = 1;
constexpr uint8 Pursue = 2;
@@ -591,7 +610,6 @@ public:
void SetBotBaseSetting(uint16 bot_setting, int setting_value);
int GetSetting(uint16 setting_category, uint16 setting_type);
void SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_value);
void CopySettings(Bot* to, uint8 setting_type, uint16 spell_type = UINT16_MAX);
void CopyBotSpellSettings(Bot* to);
void ResetBotSpellSettings();
@@ -643,7 +661,7 @@ public:
uint8 GetDefaultSpellTypeMinThreshold(uint16 spell_type, uint8 stance = Stance::Balanced);
uint8 GetDefaultSpellTypeMaxThreshold(uint16 spell_type, uint8 stance = Stance::Balanced);
uint16 GetUltimateSpellTypeDelay(uint16 spell_type, Mob* tar);
bool GetUltimateSpellTypeDelayCheck(uint16 spell_type, Mob* tar);
bool GetUltimateSpellTypeRecastCheck(uint16 spell_type, Mob* tar);
uint8 GetUltimateSpellTypeMinThreshold(uint16 spell_type, Mob* tar);
uint8 GetUltimateSpellTypeMaxThreshold(uint16 spell_type, Mob* tar);
void SetIllusionBlock(bool value) { _illusionBlock = value; }
@@ -677,6 +695,7 @@ public:
bool IsValidSpellTypeSubType(uint16 spell_type, uint16 sub_type, uint16 spell_id);
static bool IsValidBotSpellCategory(uint8 setting_type);
static std::string GetBotSpellCategoryName(uint8 setting_type);
static std::string GetBotSpellCategoryShortName(uint8 setting_type);
static uint16 GetBotSpellCategoryIDByShortName(std::string setting_string);
void AssignBotSpellsToTypes(std::vector<BotSpells>& AIBot_spells, std::unordered_map<uint16, std::vector<BotSpells_wIndex>>& AIBot_spells_by_type);
uint16 GetSpellByAA(int id, AA::Rank*& rank);
@@ -797,6 +816,7 @@ public:
bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; }
uint8 GetBotStance() { return _botStance; }
static bool IsValidBotStance(uint8 stance);
uint8 GetChanceToCastBySpellType(uint16 spell_type);
bool IsGroupHealer() const { return m_CastingRoles.GroupHealer; }
bool IsGroupSlower() const { return m_CastingRoles.GroupSlower; }
+1
View File
@@ -53,6 +53,7 @@ void bot_command_attack(Client *c, const Seperator *sep)
size_t attacker_count = 0;
Bot *first_attacker = nullptr;
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
for (auto bot_iter : sbl) {
if (!bot_iter->ValidStateCheck(c)) {
continue;
+2
View File
@@ -12,6 +12,7 @@ void bot_command_behind_mob(Client* c, const Seperator* sep)
BotCommandHelpParams p;
p.description = { "Toggles whether or not bots will stay behind the mob during combat." };
p.notes = { "- Bots that are taunting will remain in front of their target"};
p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) };
p.examples_one =
{
@@ -84,6 +85,7 @@ void bot_command_behind_mob(Client* c, const Seperator* sep)
const int ab_mask = ActionableBots::ABM_Type1;
std::string class_race_arg = sep->arg[ab_arg];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
+4 -4
View File
@@ -9,7 +9,7 @@ void bot_command_blocked_buffs(Client* c, const Seperator* sep)
}
if (helper_command_alias_fail(c, "bot_command_blocked_buffs", sep->arg[0], "blockedbuffs")) {
c->Message(Chat::White, "note: Allows you to set, view and wipe blocked buffs for the selected bots.");
c->Message(Chat::White, "note: Allows you to set, view and wipe beneficial blocked buffs for the selected bots.");
return;
}
@@ -17,7 +17,7 @@ void bot_command_blocked_buffs(Client* c, const Seperator* sep)
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Allows you to set, view and wipe blocked buffs for the selected bots." };
p.description = { "Allows you to set, view and wipe beneficial blocked buffs for the selected bots." };
p.notes = { "- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list." };
p.example_format = { fmt::format("{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]", sep->arg[0]) };
p.examples_one =
@@ -231,7 +231,7 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep)
}
if (helper_command_alias_fail(c, "bot_command_blocked_pet_buffs", sep->arg[0], "blockedpetbuffs")) {
c->Message(Chat::White, "note: Allows you to set, view and wipe blocked pet buffs for the selected bots.");
c->Message(Chat::White, "note: Allows you to set, view and wipe beneficial blocked pet buffs for the selected bots.");
return;
}
@@ -239,7 +239,7 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep)
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Allows you to set, view and wipe blocked pet buffs for the selected bots." };
p.description = { "Allows you to set, view and wipe beneficial blocked pet buffs for the selected bots." };
p.notes =
{
"- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list.",
+8 -13
View File
@@ -480,7 +480,7 @@ void bot_command_follow_distance(Client *c, const Seperator *sep)
p.description = { "Sets or resets the follow distance of the selected bots." };
p.notes =
{
fmt::format("[Default]: {}", RuleI(Bots, MaxFollowDistance)),
fmt::format("[Default]: {}", RuleI(Bots, DefaultFollowDistance)),
fmt::format("- You must use a value between 1 and {}.", RuleI(Bots, MaxFollowDistance))
};
p.example_format = { fmt::format("{} [reset]/[set [value]] [actionable]", sep->arg[0]) };
@@ -1154,7 +1154,7 @@ void bot_command_stance(Client *c, const Seperator *sep)
p.description = { "Change a bot's stance to control the way it behaves." };
p.notes =
{
"- <b>Changing a stance will reset all settings to match that stance type.</b>",
"- Changing a stance will reset all settings to match that stance type.",
"- Any changes made will only save to that stance for future use.",
fmt::format(
"- {} (#{}) will tell Non-Warrior classes to taunt automatically.",
@@ -1181,16 +1181,16 @@ void bot_command_stance(Client *c, const Seperator *sep)
Stance::AEBurn
),
"<br>",
fmt::format(
"- {} (#{}) [Default] - Overall balance and casts most spell types by default.",
Stance::GetName(Stance::Balanced),
Stance::Balanced
),
fmt::format(
"- {} (#{}) - Idle. Does not cast or engage in combat.",
Stance::GetName(Stance::Passive),
Stance::Passive
),
fmt::format(
"- {} (#{}) [Default] - Overall balance and casts most spell types by default.",
Stance::GetName(Stance::Balanced),
Stance::Balanced
),
fmt::format(
"- {} (#{}) - More mana and aggro efficient (SKs will still cast hate line). Longer delays between detrimental spells, thresholds adjusted to cast less often.",
Stance::GetName(Stance::Efficient),
@@ -1263,12 +1263,7 @@ void bot_command_stance(Client *c, const Seperator *sep)
if (sep->IsNumber(1)) {
++ab_arg;
value = atoi(sep->arg[1]);
if (
value < Stance::Passive ||
value > Stance::AEBurn ||
value == Stance::Reactive ||
value == Stance::Assist
) {
if (!Bot::IsValidBotStance(value)) {
c->Message(
Chat::Yellow,
fmt::format(
+163 -172
View File
@@ -52,7 +52,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep)
),
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrochecks, spelltargetcounts, spellresistlimits, spellannouncecasts, blockedbuffs, blockedpetbuffs" };
p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellresistlimits, spellaggrochecks, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spelltargetcounts, spellannouncecasts, blockedbuffs, blockedpetbuffs" };
p.options_one =
{
"[spellsettings] will copy ^spellsettings options",
@@ -85,12 +85,6 @@ void bot_command_copy_settings(Client* c, const Seperator* sep)
return;
}
int spell_type_arg_int = 4;
std::string spell_type_arg = sep->arg[spell_type_arg_int];
int ab_arg = 2;
bool valid_option = false;
uint16 spell_type = UINT16_MAX;
uint16 setting_type = UINT16_MAX;
std::vector<std::string> options =
{
"all",
@@ -100,63 +94,79 @@ void bot_command_copy_settings(Client* c, const Seperator* sep)
"spellholds",
"spelldelays",
"spellminthresholds",
"spellmaxthresholds",
"spellmaxthresholds",
"spellresistlimits",
"spellaggrochecks",
"spellminmanapct",
"spellmaxmanapct",
"spellminhppct",
"spellmaxhppct",
"spellidlepriority",
"spellengagedpriority",
"spellpursuepriority",
"spellaggrochecks",
"spellpursuepriority",
"spelltargetcounts",
"spellresistlimits",
"spellannouncecasts",
"blockedbuffs",
"blockedpetbuffs"
};
if (sep->IsNumber(spell_type_arg_int)) {
spell_type = atoi(sep->arg[spell_type_arg_int]);
if (!Bot::IsValidBotSpellType(spell_type)) {
c->Message(
Chat::Yellow,
fmt::format(
"You must choose a valid spell type. Use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
else if (!spell_type_arg.empty()) {
if (Bot::GetSpellTypeIDByShortName(spell_type_arg) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(spell_type_arg);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"You must choose a valid spell type. Use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
std::string option_arg = Strings::ToLower(sep->arg[3]);
bool valid_option = false;
bool copy_all = false;
bool copy_misc = false;
bool copy_spell_settings = false;
bool copy_spell_type_settings = false;
bool copy_blocked_buffs = false;
bool copy_blocked_pet_buffs = false;
uint16 setting_type = UINT16_MAX;
for (int i = 0; i < options.size(); i++) {
if (sep->arg[3] == options[i]) {
setting_type = Bot::GetBotSpellCategoryIDByShortName(sep->arg[3]);
valid_option = true;
break;
if (option_arg == options[i]) {
if (option_arg == "all") {
copy_all = true;
valid_option = true;
break;
}
else if (option_arg == "misc") {
copy_misc = true;
valid_option = true;
break;
}
else if (option_arg == "spellsettings") {
copy_spell_settings = true;
valid_option = true;
break;
}
else if (option_arg == "spelltypesettings") {
copy_spell_type_settings = true;
valid_option = true;
break;
}
else if (option_arg == "blockedbuffs") {
copy_blocked_buffs = true;
valid_option = true;
break;
}
else if (option_arg == "blockedpetbuffs") {
copy_blocked_pet_buffs = true;
valid_option = true;
break;
}
else {
setting_type = Bot::GetBotSpellCategoryIDByShortName(option_arg);
if (setting_type != UINT16_MAX) {
valid_option = true;
break;
}
}
}
}
@@ -226,125 +236,18 @@ void bot_command_copy_settings(Client* c, const Seperator* sep)
return;
}
std::string output = "";
int ab_arg = 4;
std::string spell_type_arg = sep->arg[ab_arg];
uint16 spell_type = UINT16_MAX;
if (setting_type != UINT16_MAX) {
if (spell_type != UINT16_MAX) {
from->CopySettings(to, setting_type, spell_type);
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
from->CopySettings(to, setting_type, i);
}
}
if (sep->IsNumber(ab_arg)) {
spell_type = atoi(sep->arg[ab_arg]);
output = Bot::GetBotSpellCategoryName(setting_type);
}
else {
if (!strcasecmp(sep->arg[3], "misc")) {
from->CopySettings(to, BotSettingCategories::BaseSetting);
output = "Miscellaneous";
}
else if (!strcasecmp(sep->arg[3], "spellsettings")) {
from->CopyBotSpellSettings(to);
output = "^spellsettings";
}
else if (!strcasecmp(sep->arg[3], "spelltypesettings")) {
if (spell_type != UINT16_MAX) {
from->CopySettings(to, BotSettingCategories::SpellHold, spell_type);
from->CopySettings(to, BotSettingCategories::SpellDelay, spell_type);
from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spell_type);
from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, spell_type);
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
from->CopySettings(to, BotSettingCategories::SpellHold, i);
from->CopySettings(to, BotSettingCategories::SpellDelay, i);
from->CopySettings(to, BotSettingCategories::SpellMinThreshold, i);
from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, i);
from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, i);
from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, i);
from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, i);
from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, i);
from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, i);
from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, i);
}
}
output = "spell type";
}
else if (!strcasecmp(sep->arg[3], "blockedbuffs")) {
from->CopyBotBlockedBuffs(to);
}
else if (!strcasecmp(sep->arg[3], "blockedpetbuffs")) {
from->CopyBotBlockedPetBuffs(to);
}
else if (!strcasecmp(sep->arg[3], "all")) {
from->CopySettings(to, BotSettingCategories::BaseSetting);
if (spell_type != UINT16_MAX) {
from->CopySettings(to, BotSettingCategories::SpellHold, spell_type);
from->CopySettings(to, BotSettingCategories::SpellDelay, spell_type);
from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spell_type);
from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, spell_type);
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
from->CopySettings(to, BotSettingCategories::SpellHold, i);
from->CopySettings(to, BotSettingCategories::SpellDelay, i);
from->CopySettings(to, BotSettingCategories::SpellMinThreshold, i);
from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, i);
from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, i);
from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, i);
from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, i);
from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, i);
from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, i);
from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, i);
}
}
from->CopyBotSpellSettings(to);
from->CopyBotBlockedBuffs(to);
from->CopyBotBlockedPetBuffs(to);
output = "spell type";
}
else {
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
"You must choose a valid spell type. Use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
@@ -353,6 +256,101 @@ void bot_command_copy_settings(Client* c, const Seperator* sep)
return;
}
++ab_arg;
}
else if (!spell_type_arg.empty()) {
if (Bot::GetSpellTypeIDByShortName(spell_type_arg) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(spell_type_arg);
++ab_arg;
}
else {
if (
!copy_all &&
!copy_misc &&
!copy_spell_settings &&
!copy_blocked_buffs &&
!copy_blocked_pet_buffs
) {
c->Message(Chat::Yellow, "If you are trying to specify a spell type, you must enter a valid spell type. Otherwise you can ignore this message.");
}
}
}
std::string output = "";
if (copy_all) {
for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) {
to->SetBotBaseSetting(i, from->GetBotBaseSetting(i));
}
for (uint16 i = BotSettingCategories::START_NO_BASE; i <= BotSettingCategories::END; ++i) {
for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) {
to->SetBotSetting(i, x, from->GetSetting(i, x));
}
}
from->CopyBotSpellSettings(to);
from->CopyBotBlockedBuffs(to);
from->CopyBotBlockedPetBuffs(to);
output = "settings were";
}
else if (copy_misc) {
for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) {
to->SetBotBaseSetting(i, from->GetBotBaseSetting(i));
output = "miscellaneous settings were";
}
}
else if (copy_spell_settings) {
from->ResetBotSpellSettings();
output = "^spellsettings were";
}
else if (copy_spell_type_settings) {
if (spell_type != UINT16_MAX) {
for (uint16 i = BotSettingCategories::START_NO_BASE; i <= BotSettingCategories::END; ++i) {
to->SetBotSetting(i, spell_type, from->GetSetting(i, spell_type));
}
output = fmt::format(
"[{}] settings were",
Bot::GetSpellTypeNameByID(spell_type)
);
}
else {
for (uint16 i = BotSettingCategories::START_NO_BASE; i <= BotSettingCategories::END; ++i) {
for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) {
to->SetBotSetting(i, x, from->GetSetting(i, x));
}
}
output = "spell type settings were";
}
}
else if (copy_blocked_buffs) {
from->CopyBotBlockedBuffs(to);
}
else if (copy_blocked_pet_buffs) {
from->CopyBotBlockedPetBuffs(to);
}
else if (setting_type != UINT16_MAX) {
if (spell_type != UINT16_MAX) {
to->SetBotSetting(setting_type, spell_type, from->GetSetting(setting_type, spell_type));
output = fmt::format(
"[{}] {} were",
Bot::GetSpellTypeNameByID(spell_type),
Bot::GetBotSpellCategoryName(Bot::GetBotSpellCategoryIDByShortName(option_arg))
);
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
to->SetBotSetting(setting_type, i, from->GetSetting(setting_type, i));
}
output = fmt::format(
"{} settings for all spell types",
Bot::GetBotSpellCategoryName(Bot::GetBotSpellCategoryIDByShortName(option_arg))
);
}
}
to->Save();
@@ -360,15 +358,8 @@ void bot_command_copy_settings(Client* c, const Seperator* sep)
c->Message(
Chat::Green,
fmt::format(
"{}'s{}{} settings were copied to {}.",
"{}'s {} copied to {}.",
from->GetCleanName(),
(
spell_type != UINT16_MAX ?
fmt::format(" [{}] ",
Bot::GetSpellTypeNameByID(spell_type)
)
: " "
),
output,
to->GetCleanName()
).c_str()
+133 -334
View File
@@ -11,7 +11,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep)
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Restores a bot's setting(s) to defaults" };
p.description = { "Restores a bot's setting(s) to defaults." };
p.notes = { "- You can put a spell type ID or shortname after any option except [all], [misc] and [spellsettings] to restore that specifc spell type only"};
p.example_format = { fmt::format("{} [option] [optional: spelltype id/short name] [actionable]", sep->arg[0]) };
p.examples_one =
@@ -38,7 +38,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep)
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrocheck, spelltargetcounts, spellresistlimits, spellannouncecasts" };
p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellresistlimits, spellaggrocheck, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spelltargetcounts, spellannouncecasts" };
p.options_one =
{
"[spellsettings] will restore ^spellsettings options",
@@ -75,12 +75,6 @@ void bot_command_default_settings(Client* c, const Seperator* sep)
return;
}
int spell_type_arg_int = 2;
std::string spell_type_arg = sep->arg[spell_type_arg_int];
int ab_arg = 2;
bool valid_option = false;
uint16 spell_type = UINT16_MAX;
uint16 setting_type = UINT16_MAX;
std::vector<std::string> options =
{
"all",
@@ -91,6 +85,8 @@ void bot_command_default_settings(Client* c, const Seperator* sep)
"spelldelays",
"spellminthresholds",
"spellmaxthresholds",
"spellresistlimits",
"spellaggrochecks",
"spellminmanapct",
"spellmaxmanapct",
"spellminhppct",
@@ -98,14 +94,76 @@ void bot_command_default_settings(Client* c, const Seperator* sep)
"spellidlepriority",
"spellengagedpriority",
"spellpursuepriority",
"spellaggrochecks",
"spelltargetcounts",
"spellresistlimits",
"spellannouncecasts"
};
if (sep->IsNumber(spell_type_arg_int)) {
spell_type = atoi(sep->arg[spell_type_arg_int]);
std::string option_arg = Strings::ToLower(sep->arg[1]);
bool valid_option = false;
bool default_all = false;
bool default_misc = false;
bool default_spell_settings = false;
bool default_spell_type_settings = false;
uint16 setting_type = UINT16_MAX;
for (int i = 0; i < options.size(); i++) {
if (option_arg == options[i]) {
if (option_arg == "all") {
default_all = true;
valid_option = true;
break;
}
else if (option_arg == "misc") {
default_misc = true;
valid_option = true;
break;
}
else if (option_arg == "spellsettings") {
default_spell_settings = true;
valid_option = true;
break;
}
else if (option_arg == "spelltypesettings") {
default_spell_type_settings = true;
valid_option = true;
break;
}
else {
setting_type = Bot::GetBotSpellCategoryIDByShortName(option_arg);
if (setting_type != UINT16_MAX) {
valid_option = true;
break;
}
}
}
}
if (!valid_option) {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
int ab_arg = 2;
std::string spell_type_arg = sep->arg[ab_arg];
uint16 spell_type = UINT16_MAX;
if (sep->IsNumber(ab_arg)) {
spell_type = atoi(sep->arg[ab_arg]);
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(
@@ -126,44 +184,13 @@ void bot_command_default_settings(Client* c, const Seperator* sep)
else if (!spell_type_arg.empty()) {
if (Bot::GetSpellTypeIDByShortName(spell_type_arg) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(spell_type_arg);
++ab_arg;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"You must choose a valid spell type. Use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
if (!default_all && !default_misc && !default_spell_settings) {
c->Message(Chat::Yellow, "If you are trying to specify a spell type, you must enter a valid spell type. Otherwise you can ignore this message.");
}
}
++ab_arg;
}
for (int i = 0; i < options.size(); i++) {
if (sep->arg[1] == options[i]) {
setting_type = Bot::GetBotSpellCategoryIDByShortName(sep->arg[1]);
valid_option = true;
break;
}
}
if (!valid_option) {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
@@ -193,286 +220,72 @@ void bot_command_default_settings(Client* c, const Seperator* sep)
}
bot_stance = my_bot->GetBotStance();
if (default_all) {
for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) {
my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i, bot_stance));
}
for (uint16 i = BotSettingCategories::START_NO_BASE; i <= BotSettingCategories::END; ++i) {
for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) {
my_bot->SetBotSetting(i, x, my_bot->GetDefaultSetting(i, x, bot_stance));
}
}
if (setting_type != UINT16_MAX) {
my_bot->ResetBotSpellSettings();
my_bot->ClearBotBlockedBuffs();
output = "settings were reset";
}
else if (default_misc) {
for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) {
my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i, bot_stance));
output = "miscellaneous settings were reset";
}
}
else if (default_spell_settings) {
my_bot->ResetBotSpellSettings();
output = "^spellsettings were reset";
}
else if (default_spell_type_settings) {
if (spell_type != UINT16_MAX) {
for (uint16 i = BotSettingCategories::START_NO_BASE; i <= BotSettingCategories::END; ++i) {
my_bot->SetBotSetting(i, spell_type, my_bot->GetDefaultSetting(i, spell_type, bot_stance));
}
output = fmt::format(
"[{}] settings were reset",
Bot::GetSpellTypeNameByID(spell_type)
);
}
else {
for (uint16 i = BotSettingCategories::START_NO_BASE; i <= BotSettingCategories::END; ++i) {
for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) {
my_bot->SetBotSetting(i, x, my_bot->GetDefaultSetting(i, x, bot_stance));
}
}
output = "spell type settings were reset";
}
}
else if (setting_type != UINT16_MAX) {
if (spell_type != UINT16_MAX) {
my_bot->SetBotSetting(setting_type, spell_type, my_bot->GetDefaultSetting(setting_type, spell_type, bot_stance));
output = fmt::format(
"[{}] {} were reset",
Bot::GetSpellTypeNameByID(spell_type),
Bot::GetBotSpellCategoryName(Bot::GetBotSpellCategoryIDByShortName(option_arg))
);
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetBotSetting(setting_type, i, my_bot->GetDefaultSetting(setting_type, i, bot_stance));
}
output = fmt::format(
"{} settings were reset for all spell types",
Bot::GetBotSpellCategoryName(Bot::GetBotSpellCategoryIDByShortName(option_arg))
);
}
output = (spell_type != UINT16_MAX ? Bot::GetSpellTypeNameByID(spell_type) : "");
output += sep->arg[3];
}
else if (!strcasecmp(sep->arg[1], "misc")) {
for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) {
my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i, bot_stance));
output = "miscellanous settings";
}
}
else if (!strcasecmp(sep->arg[1], "spellsettings")) {
my_bot->ResetBotSpellSettings();
output = "^spellsettings";
}
else if (!strcasecmp(sep->arg[1], "spelltypesettings")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeHold(spell_type, my_bot->GetDefaultSpellTypeHold(spell_type, bot_stance));
my_bot->SetSpellTypeDelay(spell_type, my_bot->GetDefaultSpellTypeDelay(spell_type, bot_stance));
my_bot->SetSpellTypeMinThreshold(
spell_type,
my_bot->GetDefaultSpellTypeMinThreshold(spell_type, bot_stance));
my_bot->SetSpellTypeMaxThreshold(
spell_type,
my_bot->GetDefaultSpellTypeMaxThreshold(spell_type, bot_stance));
my_bot->SetSpellTypeAggroCheck(spell_type, my_bot->GetDefaultSpellTypeAggroCheck(spell_type, bot_stance));
my_bot->SetSpellTypeResistLimit(spell_type, my_bot->GetDefaultSpellTypeResistLimit(spell_type, bot_stance));
my_bot->SetSpellTypeMinManaLimit(spell_type, my_bot->GetDefaultSpellTypeMinManaLimit(spell_type, bot_stance));
my_bot->SetSpellTypeMaxManaLimit(spell_type, my_bot->GetDefaultSpellTypeMaxManaLimit(spell_type, bot_stance));
my_bot->SetSpellTypeMinHPLimit(spell_type, my_bot->GetDefaultSpellTypeMinHPLimit(spell_type, bot_stance));
my_bot->SetSpellTypeMaxHPLimit(spell_type, my_bot->GetDefaultSpellTypeMaxHPLimit(spell_type, bot_stance));
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance));
my_bot->SetSpellTypeAnnounceCast(spell_type, my_bot->GetDefaultSpellTypeAnnounceCast(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeHold(i, my_bot->GetDefaultSpellTypeHold(i, bot_stance));
my_bot->SetSpellTypeDelay(i, my_bot->GetDefaultSpellTypeDelay(i, bot_stance));
my_bot->SetSpellTypeMinThreshold(i, my_bot->GetDefaultSpellTypeMinThreshold(i, bot_stance));
my_bot->SetSpellTypeMaxThreshold(i, my_bot->GetDefaultSpellTypeMaxThreshold(i, bot_stance));
my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance));
my_bot->SetSpellTypeResistLimit(i, my_bot->GetDefaultSpellTypeResistLimit(i, bot_stance));
my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance));
my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance));
my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance));
my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i, bot_stance));
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance));
my_bot->SetSpellTypeAnnounceCast(i, my_bot->GetDefaultSpellTypeAnnounceCast(i, bot_stance));
}
}
output = "spell type settings";
}
else if (!strcasecmp(sep->arg[1], "all")) {
for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) {
my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i, bot_stance));
}
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeHold(i, my_bot->GetDefaultSpellTypeHold(i, bot_stance));
my_bot->SetSpellTypeDelay(i, my_bot->GetDefaultSpellTypeDelay(i, bot_stance));
my_bot->SetSpellTypeMinThreshold(i, my_bot->GetDefaultSpellTypeMinThreshold(i, bot_stance));
my_bot->SetSpellTypeMaxThreshold(i, my_bot->GetDefaultSpellTypeMaxThreshold(i, bot_stance));
my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance));
my_bot->SetSpellTypeResistLimit(i, my_bot->GetDefaultSpellTypeResistLimit(i, bot_stance));
my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance));
my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance));
my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance));
my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i, bot_stance));
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance));
my_bot->SetSpellTypeAnnounceCast(i, my_bot->GetDefaultSpellTypeAnnounceCast(i, bot_stance));
};
my_bot->ResetBotSpellSettings();
my_bot->ClearBotBlockedBuffs();
output = "settings";
}
else if (!strcasecmp(sep->arg[1], "holds")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeHold(spell_type, my_bot->GetDefaultSpellTypeHold(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeHold(i, my_bot->GetDefaultSpellTypeHold(i, bot_stance));
}
}
output = "hold settings";
}
else if (!strcasecmp(sep->arg[1], "delays")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeDelay(spell_type, my_bot->GetDefaultSpellTypeDelay(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeDelay(i, my_bot->GetDefaultSpellTypeDelay(i, bot_stance));
}
}
output = "delay settings";
}
else if (!strcasecmp(sep->arg[1], "minthresholds")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeMinThreshold(
spell_type,
my_bot->GetDefaultSpellTypeMinThreshold(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeMinThreshold(i, my_bot->GetDefaultSpellTypeMinThreshold(i, bot_stance));
}
}
output = "minimum threshold settings";
}
else if (!strcasecmp(sep->arg[1], "maxthresholds")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeMaxThreshold(
spell_type,
my_bot->GetDefaultSpellTypeMaxThreshold(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeMaxThreshold(i, my_bot->GetDefaultSpellTypeMaxThreshold(i, bot_stance));
}
}
output = "maximum threshold settings";
}
else if (!strcasecmp(sep->arg[1], "aggrochecks")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeAggroCheck(spell_type, my_bot->GetDefaultSpellTypeAggroCheck(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance));
}
}
output = "aggro check settings";
}
else if (!strcasecmp(sep->arg[1], "resist limit")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance));
}
}
output = "resist limit settings";
}
else if (!strcasecmp(sep->arg[1], "minmanapct")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeMinManaLimit(spell_type, my_bot->GetDefaultSpellTypeMinManaLimit(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance));
}
}
output = "min mana settings";
}
else if (!strcasecmp(sep->arg[1], "maxmanapct")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeMaxManaLimit(spell_type, my_bot->GetDefaultSpellTypeMaxManaLimit(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance));
}
}
output = "max mana settings";
}
else if (!strcasecmp(sep->arg[1], "minhppct")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeMinHPLimit(spell_type, my_bot->GetDefaultSpellTypeMinHPLimit(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance));
}
}
output = "min hp settings";
}
else if (!strcasecmp(sep->arg[1], "maxhppct")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeMaxHPLimit(spell_type, my_bot->GetDefaultSpellTypeMaxHPLimit(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i, bot_stance));
}
}
output = "max hp settings";
}
else if (!strcasecmp(sep->arg[1], "idlepriority")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance));
}
}
output = "idle priority settings";
}
else if (!strcasecmp(sep->arg[1], "engagedpriority")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance));
}
}
output = "engaged priority settings";
}
else if (!strcasecmp(sep->arg[1], "pursuepriority")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance));
}
}
output = "pursue priority settings";
}
else if (!strcasecmp(sep->arg[1], "targetcounts")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance));
}
}
output = "target count settings";
}
else if (!strcasecmp(sep->arg[1], "announcecast")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance));
}
}
output = "announce cast settings";
}
my_bot->Save();
@@ -483,15 +296,8 @@ void bot_command_default_settings(Client* c, const Seperator* sep)
c->Message(
Chat::Green,
fmt::format(
"{} says, '{}{} were restored.'",
"{} says, 'My {}.'",
first_found->GetCleanName(),
(
spell_type != UINT16_MAX ?
fmt::format("My [{}] ",
Bot::GetSpellTypeNameByID(spell_type)
)
: "My "
),
output
).c_str()
);
@@ -500,15 +306,8 @@ void bot_command_default_settings(Client* c, const Seperator* sep)
c->Message(
Chat::Green,
fmt::format(
"{} of your bot's{}{} were restored.",
"{} of your bots' {}.",
success_count,
(
spell_type != UINT16_MAX ?
fmt::format(" [{}] ",
Bot::GetSpellTypeNameByID(spell_type)
)
: " "
),
output
).c_str()
);
+1 -1
View File
@@ -220,7 +220,7 @@ void bot_command_depart(Client* c, const Seperator* sep)
}
if (bot_iter->CastSpell(itr->SpellId, tar->GetID(), EQ::spells::CastingSlot::Gem2, -1, -1)) {
if (IsBotSpellTypeOtherBeneficial(BotSpellTypes::Teleport)) {
if (BotSpellTypeUsesTargetSettings(BotSpellTypes::Teleport)) {
bot_iter->SetCastedSpellType(UINT16_MAX);
}
else {
+1 -5
View File
@@ -209,10 +209,6 @@ void bot_command_discipline(Client* c, const Seperator* sep)
continue;
}
if (spell.buff_duration_formula != 0 && spell.target_type == ST_Self && bot_iter->HasDiscBuff()) {
continue;
}
if (!tar || (spell.target_type == ST_Self && tar != bot_iter)) {
tar = bot_iter;
}
@@ -231,7 +227,7 @@ void bot_command_discipline(Client* c, const Seperator* sep)
}
if (!is_success) {
c->Message(Chat::Yellow, "No bots were selected.");
c->Message(Chat::Yellow, "No bots were capable of doing that. This could be because they don't have the ability or things like Line of Sight, range, endurance, etc.");
}
else {
if (aggressive || defensive) {
+1 -1
View File
@@ -16,7 +16,7 @@ void bot_command_distance_ranged(Client* c, const Seperator* sep)
{
"- Bots will stay between half the value of the setting and the current value. IE, if set to 60, bots will stay between 30 and 60.",
"- Casters will never go closer than their maximum melee range.",
"- Throwing bots will never get closer than the minimum value for ranged to work, or beyond the range of their items."
"- Archery & Throwing bots will never get closer than the minimum value for ranged to work, or beyond the range of their items."
};
p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) };
p.examples_one = {
+6
View File
@@ -12,6 +12,12 @@ void bot_command_max_melee_range(Client* c, const Seperator* sep)
BotCommandHelpParams p;
p.description = { "Toggles whether or not bots will stay at max melee range during combat."};
p.notes = {
fmt::format(
"- Bots {} use special abilities when set to max melee range (taunt, kick, bash, etc).",
RuleB(Bots, DisableSpecialAbilitiesAtMaxMelee) ? "will not" : "will"
)
};
p.example_format ={ fmt::format("{} [value] [actionable]", sep->arg[0]) };
p.examples_one =
{
+6
View File
@@ -106,6 +106,12 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep)
BotCommandHelpParams p;
p.description = { "Allows you to change the type of pet Magician bots will cast." };
p.notes = {
fmt::format(
"- Epic pets are currently {} on this server.",
RuleB(Bots, AllowMagicianEpicPet) ? "allowed" : "not allowed"
)
};
p.example_format = { fmt::format("{} [current | water | fire | air | earth | monster | epic] [actionable, default: target]", sep->arg[0]) };
p.examples_one =
{
+1
View File
@@ -12,6 +12,7 @@ void bot_command_taunt(Client* c, const Seperator* sep)
BotCommandHelpParams p;
p.description = { "Allows you to turn on/off the taunting state of your bots and/or their pets." };
p.notes = { "- If a taunting class is set to Aggressive (5) stance, they will automatically taunt and don't need to have it enabled manually." };
p.example_format = { fmt::format("{} [on / off / pet] [optional: pet] [actionable, default: target]", sep->arg[0]) };
p.examples_one =
{
+8 -8
View File
@@ -2249,7 +2249,7 @@ bool BotDatabase::LoadBotSettings(Mob* m)
else {
LogBotSettings("[{}] says, 'Loading {} [{}], {} [{}] - setting to [{}].",
m->GetCleanName(),
Bot::GetBotSpellCategoryName(e.setting_type),
Bot::GetBotSpellCategoryShortName(e.setting_type),
e.setting_type,
Bot::GetSpellTypeNameByID(e.setting_id),
e.setting_id,
@@ -2312,7 +2312,7 @@ bool BotDatabase::SaveBotSettings(Mob* m)
.setting_id = static_cast<uint16_t>(i),
.setting_type = static_cast<uint8_t>(BotSettingCategories::BaseSetting),
.value = static_cast<int32_t>(m->CastToBot()->GetBotBaseSetting(i)),
.category_name = Bot::GetBotSpellCategoryName(BotSettingCategories::BaseSetting),
.category_name = Bot::GetBotSpellCategoryShortName(BotSettingCategories::BaseSetting),
.setting_name = Bot::GetBotSettingCategoryName(i)
};
@@ -2332,13 +2332,13 @@ bool BotDatabase::SaveBotSettings(Mob* m)
.setting_id = static_cast<uint16_t>(x),
.setting_type = static_cast<uint8_t>(i),
.value = m->CastToBot()->GetSetting(i, x),
.category_name = Bot::GetBotSpellCategoryName(i),
.category_name = Bot::GetBotSpellCategoryShortName(i),
.setting_name = Bot::GetSpellTypeNameByID(x)
};
v.emplace_back(e);
LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSpellCategoryName(i), Bot::GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x, bot_stance));
LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSpellCategoryShortName(i), Bot::GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x, bot_stance));
}
}
}
@@ -2354,7 +2354,7 @@ bool BotDatabase::SaveBotSettings(Mob* m)
.setting_id = static_cast<uint16_t>(BotBaseSettings::IllusionBlock),
.setting_type = static_cast<uint8_t>(BotSettingCategories::BaseSetting),
.value = m->CastToClient()->GetIllusionBlock(),
.category_name = Bot::GetBotSpellCategoryName(BotSettingCategories::BaseSetting),
.category_name = Bot::GetBotSpellCategoryShortName(BotSettingCategories::BaseSetting),
.setting_name = Bot::GetBotSettingCategoryName(BotBaseSettings::IllusionBlock)
};
@@ -2366,7 +2366,7 @@ bool BotDatabase::SaveBotSettings(Mob* m)
for (uint16 i = BotSettingCategories::START_CLIENT; i <= BotSettingCategories::END_CLIENT; ++i) {
for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) {
LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSpellCategoryName(i), Bot::GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x));
LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSpellCategoryShortName(i), Bot::GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x));
if (m->CastToClient()->GetBotSetting(i, x) != m->CastToClient()->GetDefaultBotSettings(i, x)) {
auto e = BotSettingsRepository::BotSettings{
.character_id = character_id,
@@ -2375,13 +2375,13 @@ bool BotDatabase::SaveBotSettings(Mob* m)
.setting_id = static_cast<uint16_t>(x),
.setting_type = static_cast<uint8_t>(i),
.value = m->CastToClient()->GetBotSetting(i, x),
.category_name = Bot::GetBotSpellCategoryName(i),
.category_name = Bot::GetBotSpellCategoryShortName(i),
.setting_name = Bot::GetSpellTypeNameByID(x)
};
v.emplace_back(e);
LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSpellCategoryName(i), Bot::GetSpellTypeNameByID(x), x, e.value, m->CastToClient()->GetDefaultBotSettings(i, x));
LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSpellCategoryShortName(i), Bot::GetSpellTypeNameByID(x), x, e.value, m->CastToClient()->GetDefaultBotSettings(i, x));
}
}
}
+78 -65
View File
@@ -242,7 +242,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ
}
if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) {
if (IsBotSpellTypeOtherBeneficial(spell_type)) {
if (BotSpellTypeUsesTargetSettings(spell_type)) {
SetCastedSpellType(UINT16_MAX);
if (!IsCommandedSpell()) {
@@ -288,7 +288,7 @@ bool Bot::BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel
}
if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) {
if (IsBotSpellTypeOtherBeneficial(spell_type)) {
if (BotSpellTypeUsesTargetSettings(spell_type)) {
SetCastedSpellType(UINT16_MAX);
if (!IsCommandedSpell()) {
@@ -493,6 +493,10 @@ bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe
}
bool Bot::BotCastHeal(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type) {
if (!TargetValidation(tar)) {
return false;
}
bot_spell = GetSpellByHealType(spell_type, tar);
if (!IsValidSpell(bot_spell.SpellId)) {
@@ -1064,10 +1068,8 @@ std::vector<BotSpell_wPriority> Bot::GetPrioritizedBotSpellsBySpellType(Bot* cas
if (
caster->IsCommandedSpell() ||
!AE ||
(
BotSpellTypeRequiresAEChecks(spell_type) &&
caster->HasValidAETarget(caster, bot_spell_list[i].spellid, spell_type, tar)
)
!BotSpellTypeRequiresAEChecks(spell_type) ||
caster->HasValidAETarget(caster, bot_spell_list[i].spellid, spell_type, tar)
) {
BotSpell_wPriority bot_spell;
bot_spell.SpellId = bot_spell_list[i].spellid;
@@ -1287,30 +1289,32 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* caster, Mob* tar, uint16 spell_ty
result.SpellIndex = 0;
result.ManaCost = 0;
if (caster) {
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
int target_count = 0;
if (!caster->TargetValidation(tar)) {
return result;
}
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
// Assuming all the spells have been loaded into this list by level and in descending order
if (IsRegularGroupHealSpell(bot_spell_list_itr->SpellId)) {
uint16 spell_id = bot_spell_list_itr->SpellId;
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
int target_count = 0;
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
if (!caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) {
target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id));
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
// Assuming all the spells have been loaded into this list by level and in descending order
if (IsRegularGroupHealSpell(bot_spell_list_itr->SpellId)) {
uint16 spell_id = bot_spell_list_itr->SpellId;
if (target_count < caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) {
continue;
}
if (caster->TargetValidation(tar) && !caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) {
target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id));
if (target_count < required_count) {
continue;
}
result.SpellId = bot_spell_list_itr->SpellId;
result.SpellIndex = bot_spell_list_itr->SpellIndex;
result.ManaCost = bot_spell_list_itr->ManaCost;
break;
}
result.SpellId = bot_spell_list_itr->SpellId;
result.SpellIndex = bot_spell_list_itr->SpellIndex;
result.ManaCost = bot_spell_list_itr->ManaCost;
break;
}
}
@@ -1324,30 +1328,32 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* caster, Mob* tar, uint16
result.SpellIndex = 0;
result.ManaCost = 0;
if (caster) {
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_HealOverTime);
if (!caster->TargetValidation(tar)) {
return result;
}
int target_count = 0;
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_HealOverTime);
int target_count = 0;
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
// Assuming all the spells have been loaded into this list by level and in descending order
if (IsGroupHealOverTimeSpell(bot_spell_list_itr->SpellId)) {
uint16 spell_id = bot_spell_list_itr->SpellId;
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
// Assuming all the spells have been loaded into this list by level and in descending order
if (IsGroupHealOverTimeSpell(bot_spell_list_itr->SpellId)) {
uint16 spell_id = bot_spell_list_itr->SpellId;
if (!caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) {
target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id));
if (caster->TargetValidation(tar) && !caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) {
target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id));
if (target_count < caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) {
continue;
}
if (target_count < required_count) {
continue;
}
result.SpellId = bot_spell_list_itr->SpellId;
result.SpellIndex = bot_spell_list_itr->SpellIndex;
result.ManaCost = bot_spell_list_itr->ManaCost;
break;
}
result.SpellId = bot_spell_list_itr->SpellId;
result.SpellIndex = bot_spell_list_itr->SpellIndex;
result.ManaCost = bot_spell_list_itr->ManaCost;
break;
}
}
@@ -1361,30 +1367,32 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* caster, Mob* tar, uint16
result.SpellIndex = 0;
result.ManaCost = 0;
if (caster) {
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CompleteHeal);
int target_count = 0;
if (!caster->TargetValidation(tar)) {
return result;
}
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
// Assuming all the spells have been loaded into this list by level and in descending order
if (IsGroupCompleteHealSpell(bot_spell_list_itr->SpellId)) {
uint16 spell_id = bot_spell_list_itr->SpellId;
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CompleteHeal);
int target_count = 0;
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
if (!caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) {
target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id));
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
// Assuming all the spells have been loaded into this list by level and in descending order
if (IsGroupCompleteHealSpell(bot_spell_list_itr->SpellId)) {
uint16 spell_id = bot_spell_list_itr->SpellId;
if (target_count < caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) {
continue;
}
if (caster->TargetValidation(tar) && !caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) {
target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id));
if (target_count < required_count) {
continue;
}
result.SpellId = bot_spell_list_itr->SpellId;
result.SpellIndex = bot_spell_list_itr->SpellIndex;
result.ManaCost = bot_spell_list_itr->ManaCost;
break;
}
result.SpellId = bot_spell_list_itr->SpellId;
result.SpellIndex = bot_spell_list_itr->SpellIndex;
result.ManaCost = bot_spell_list_itr->ManaCost;
break;
}
}
@@ -1423,7 +1431,8 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_typ
Mob* result = nullptr;
if (caster && caster->GetOwner()) {
int spell_range = (!AE ? caster->GetActSpellRange(spell_id, spells[spell_id].range) : caster->GetActSpellRange(spell_id, spells[spell_id].aoe_range));
int spell_range = caster->GetActSpellRange(spell_id, spells[spell_id].range);
int spell_ae_range = caster->GetAOERange(spell_id);
int buff_count = 0;
NPC* npc = nullptr;
@@ -1454,7 +1463,7 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_typ
}
if (IsPBAESpell(spell_id)) {
if (spell_range < Distance(caster->GetPosition(), m->GetPosition())) {
if (spell_ae_range < Distance(caster->GetPosition(), m->GetPosition())) {
continue;
}
}
@@ -2722,7 +2731,11 @@ bool Bot::IsValidSpellRange(uint16 spell_id, Mob* tar) {
float range = spells[spell_id].range + GetRangeDistTargetSizeMod(tar);
if (IsAnyAESpell(spell_id)) {
if (
spells[spell_id].target_type != ST_AETargetHateList &&
!IsTargetableAESpell(spell_id) &&
IsAnyAESpell(spell_id)
) {
range = GetAOERange(spell_id);
}
+277
View File
@@ -0,0 +1,277 @@
#include <chrono>
#include <iostream>
#include <random>
#include "../../common/http/httplib.h"
#include "../../common/eqemu_logsys.h"
#include "../sidecar_api/sidecar_api.h"
#include "../../common/platform.h"
#include "../data_bucket.h"
#include "../zonedb.h"
#include "../../common/repositories/data_buckets_repository.h"
void RunBenchmarkCycle(uint64_t target_rows)
{
const size_t OPERATIONS_PER_TEST = 5000;
const std::string test_key_prefix = "test_key_";
std::cout << Strings::Repeat("-", 70) << "\n";
std::cout << "📊 Running Benchmark at " << Strings::Commify(target_rows) << " Rows...\n";
std::cout << Strings::Repeat("-", 70) << "\n";
// 🧹 **Purge `test_key_*` Keys Before Each Run**
std::cout << "🧹 Purging test keys (`test_key_*`)...\n";
auto purge_start = std::chrono::high_resolution_clock::now();
DataBucketsRepository::DeleteWhere(database, "`key` LIKE '" + test_key_prefix + "%'");
auto purge_end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> purge_time = purge_end - purge_start;
std::cout << "✅ Purged test keys in " << purge_time.count() << " seconds.\n";
// 📊 **Ensure the Table Contains At Least `target_rows`**
auto populate_start = std::chrono::high_resolution_clock::now();
uint64_t current_count = DataBucketsRepository::Count(database);
if (current_count < target_rows) {
std::cout << "📌 Populating table to " << Strings::Commify(target_rows) << " rows...\n";
std::mt19937 rng(std::random_device{}());
std::uniform_int_distribution<int> entity_type(1, 5);
std::uniform_int_distribution<int> id_dist(1, 1000000);
std::uniform_int_distribution<int> expiry_dist(0, 86400 * 30); // Expiry up to 30 days
while (current_count < target_rows) {
std::vector<DataBucketsRepository::DataBuckets> batch;
for (size_t i = 0; i < 100000; ++i) {
if (i > target_rows - current_count) {
break;
}
int entity_choice = entity_type(rng);
int entity_id = id_dist(rng);
std::string key = "test_key_" + std::to_string(current_count + i);
std::string value = "value_" + std::to_string(current_count + i);
int expires = static_cast<int>(std::time(nullptr)) + expiry_dist(rng);
DataBucketsRepository::DataBuckets e{};
e.key_ = key;
e.value = value;
e.expires = expires;
e.account_id = (entity_choice == 1) ? entity_id : 0;
e.character_id = (entity_choice == 2) ? entity_id : 0;
e.npc_id = (entity_choice == 3) ? entity_id : 0;
e.bot_id = (entity_choice == 4) ? entity_id : 0;
e.zone_id = (entity_choice == 5) ? entity_id : 0;
e.instance_id = (entity_choice == 5) ? entity_id : 0;
batch.emplace_back(e);
}
DataBucketsRepository::InsertMany(database, batch);
current_count += batch.size();
}
}
else {
std::cout << "✅ Table already has " << current_count << " rows, proceeding with benchmark.\n";
}
auto populate_end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> populate_time = populate_end - populate_start;
std::cout << "✅ Populated table in " << populate_time.count() << " seconds.\n";
std::mt19937 rng(std::random_device{}());
std::uniform_int_distribution<int> id_dist(1, 1000);
// 🚀 **Measure Insert Performance**
std::vector<DataBucketKey> inserted_keys = {};
auto insert_start = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < OPERATIONS_PER_TEST; ++i) {
std::string key = test_key_prefix + std::to_string(current_count + i);
std::string value = "value_" + std::to_string(current_count + i);
int expires = static_cast<int>(std::time(nullptr)) + 3600;
DataBucketKey e{
.key = key,
.value = value,
.expires = std::to_string(expires),
.account_id = 0,
.character_id = 0,
.npc_id = 0,
.bot_id = 0
};
// randomly set account_id, character_id, npc_id, or bot_id
switch (i % 4) {
case 0:
e.account_id = id_dist(rng);
break;
case 1:
e.character_id = id_dist(rng);
break;
case 2:
e.npc_id = id_dist(rng);
break;
case 3:
e.bot_id = id_dist(rng);
break;
case 4:
int entity_choice = id_dist(rng);
e.zone_id = entity_choice;
e.instance_id = entity_choice;
break;
}
DataBucket::SetData(e);
inserted_keys.emplace_back(e);
}
auto insert_end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> insert_time = insert_end - insert_start;
std::cout << "✅ Completed " << Strings::Commify(OPERATIONS_PER_TEST) << " inserts in " << insert_time.count()
<< " seconds. (Individual Insert Performance)\n";
// ✏️ **Measure Update Performance Using DataBucket**
auto update_start = std::chrono::high_resolution_clock::now();
for (auto &key: inserted_keys) {
// 🔍 Retrieve existing bucket using scoped `GetData`
auto e = DataBucket::GetData(key);
if (e.id > 0) {
// create a new key object with the updated values
DataBucketKey bucket_entry_key{
.key = e.key_,
.value = "some_new_value",
.expires = std::to_string(e.expires),
.account_id = e.account_id,
.character_id = e.character_id,
.npc_id = e.npc_id,
.bot_id = e.bot_id,
.zone_id = e.zone_id,
.instance_id = e.instance_id
};
// 🔄 Update using DataBucket class
DataBucket::SetData(bucket_entry_key);
}
}
auto update_end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> update_time = update_end - update_start;
std::cout << "✅ Completed " << Strings::Commify(OPERATIONS_PER_TEST) << " updates in " << update_time.count()
<< " seconds. (Scoped Update Performance)\n";
// 🔍 **Measure Cached Read Performance**
auto read_cached_start = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < OPERATIONS_PER_TEST; ++i) {
std::string key = test_key_prefix + std::to_string(current_count + i);
DataBucketKey k{
.key = key,
.account_id = 0,
.character_id = 0,
.npc_id = 0,
.bot_id = 0,
.zone_id = 0,
.instance_id = 0
};
// randomly set account_id, character_id, npc_id, or bot_id
switch (i % 4) {
case 0:
k.account_id = id_dist(rng);
break;
case 1:
k.character_id = id_dist(rng);
break;
case 2:
k.npc_id = id_dist(rng);
break;
case 3:
k.bot_id = id_dist(rng);
break;
case 4:
int entity_choice = id_dist(rng);
k.zone_id = entity_choice;
k.instance_id = entity_choice;
}
DataBucket::GetData(key);
}
auto read_cached_end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> read_cached_time = read_cached_end - read_cached_start;
std::cout << "✅ Completed " << Strings::Commify(OPERATIONS_PER_TEST) << " cached reads in "
<< read_cached_time.count() << " seconds. (DataBucket::GetData)\n";
// 🔍 **Measure Non-Cached Read Performance (Direct Query)**
auto read_uncached_start = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < OPERATIONS_PER_TEST; ++i) {
std::string key = test_key_prefix + std::to_string(current_count + i);
DataBucketsRepository::GetWhere(database, "`key` = '" + key + "'");
}
auto read_uncached_end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> read_uncached_time = read_uncached_end - read_uncached_start;
std::cout << "✅ Completed " << Strings::Commify(OPERATIONS_PER_TEST) << " non-cached reads in "
<< read_uncached_time.count() << " seconds. (DataBucketsRepository::GetWhere)\n";
// 🗑️ **Measure Delete Performance**
auto delete_start = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < OPERATIONS_PER_TEST; ++i) {
std::string key = test_key_prefix + std::to_string(current_count + i);
DataBucketKey k{
.key = key,
.account_id = 0,
.character_id = 0,
.npc_id = 0,
.bot_id = 0,
.zone_id = 0,
.instance_id = 0
};
// randomly set account_id, character_id, npc_id, or bot_id
switch (i % 4) {
case 0:
k.account_id = id_dist(rng);
break;
case 1:
k.character_id = id_dist(rng);
break;
case 2:
k.npc_id = id_dist(rng);
break;
case 3:
k.bot_id = id_dist(rng);
break;
case 4:
int entity_choice = id_dist(rng);
k.zone_id = entity_choice;
k.instance_id = entity_choice;
}
DataBucket::DeleteData(k);
}
auto delete_end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> delete_time = delete_end - delete_start;
std::cout << "✅ Completed " << Strings::Commify(OPERATIONS_PER_TEST) << " deletes in " << delete_time.count()
<< " seconds.\n";
}
void ZoneCLI::BenchmarkDatabuckets(int argc, char **argv, argh::parser &cmd, std::string &description)
{
description = "Benchmark individual reads/writes/deletes in data_buckets at different table sizes.";
if (cmd[{"-h", "--help"}]) {
std::cout << "Usage: BenchmarkDatabuckets\n";
return;
}
if (std::getenv("DEBUG")) {
LogSys.SetDatabase(&database)->LoadLogDatabaseSettings();
}
auto start_time = std::chrono::high_resolution_clock::now();
std::vector<uint64_t> benchmark_sizes = {10000, 100000, 1000000};
for (auto size: benchmark_sizes) {
RunBenchmarkCycle(size);
}
// 🚀 **Total Benchmark Time**
auto end_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> total_elapsed = end_time - start_time;
std::cout << "\n🚀 Total Benchmark Time: " << total_elapsed.count() << " seconds\n";
}
+123 -666
View File
File diff suppressed because it is too large Load Diff
+29 -33
View File
@@ -21,8 +21,7 @@
class Client;
class EQApplicationPacket;
class DynamicZone;
class Expedition;
class ExpeditionLockoutTimer;
class DzLockout;
class ExpeditionRequest;
class Group;
class NPC;
@@ -32,6 +31,7 @@ class Seperator;
class ServerPacket;
struct DynamicZoneLocation;
enum WaterRegionType : int;
enum class DynamicZoneMemberStatus;
namespace EQ
{
@@ -248,6 +248,13 @@ struct ClientReward
uint32 amount;
};
struct ExpeditionInvite
{
uint32_t dz_id;
std::string inviter_name;
std::string swap_name;
};
class Client : public Mob
{
public:
@@ -879,11 +886,9 @@ public:
void QuestReadBook(const char* text, uint8 type);
void SendMoneyUpdate();
bool TakeMoneyFromPP(uint64 copper, bool update_client = false);
bool TakeMoneyFromPPWithOverFlow(uint64 copper, bool update_client);
bool TakePlatinum(uint32 platinum, bool update_client = false);
void AddMoneyToPP(uint64 copper, bool update_client = false);
void AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool update_client = false);
void AddMoneyToPPWithOverflow(uint64 copper, bool update_client);
void AddPlatinum(uint32 platinu, bool update_client = false);
bool HasMoney(uint64 copper);
uint64 GetCarriedMoney();
@@ -1567,32 +1572,24 @@ public:
Client* client, const std::string& client_name, uint16_t chat_type,
uint32_t string_id, const std::initializer_list<std::string>& arguments = {});
void AddExpeditionLockout(const ExpeditionLockoutTimer& lockout, bool update_db = false);
void AddExpeditionLockoutDuration(const std::string& expedition_name,
const std::string& event_Name, int seconds, const std::string& uuid = {}, bool update_db = false);
void AddNewExpeditionLockout(const std::string& expedition_name,
const std::string& event_name, uint32_t duration, std::string uuid = {});
Expedition* CreateExpedition(DynamicZone& dz, bool disable_messages = false);
Expedition* CreateExpedition(const std::string& zone_name,
uint32 version, uint32 duration, const std::string& expedition_name,
uint32 min_players, uint32 max_players, bool disable_messages = false);
Expedition* CreateExpeditionFromTemplate(uint32_t dz_template_id);
Expedition* GetExpedition() const;
uint32 GetExpeditionID() const { return m_expedition_id; }
const ExpeditionLockoutTimer* GetExpeditionLockout(
const std::string& expedition_name, const std::string& event_name, bool include_expired = false) const;
const std::vector<ExpeditionLockoutTimer>& GetExpeditionLockouts() const { return m_expedition_lockouts; };
std::vector<ExpeditionLockoutTimer> GetExpeditionLockouts(const std::string& expedition_name, bool include_expired = false);
uint32 GetPendingExpeditionInviteID() const { return m_pending_expedition_invite.expedition_id; }
bool HasExpeditionLockout(const std::string& expedition_name, const std::string& event_name, bool include_expired = false);
bool IsInExpedition() const { return m_expedition_id != 0; }
void RemoveAllExpeditionLockouts(const std::string& expedition_name, bool update_db = false);
void RemoveExpeditionLockout(const std::string& expedition_name,
const std::string& event_name, bool update_db = false);
void RequestPendingExpeditionInvite();
void SendExpeditionLockoutTimers();
void SetExpeditionID(uint32 expedition_id) { m_expedition_id = expedition_id; };
void SetPendingExpeditionInvite(ExpeditionInvite&& invite) { m_pending_expedition_invite = invite; }
void AddDzLockout(const DzLockout& lockout, bool update_db = false);
void AddDzLockout(const std::string& expedition, const std::string& event, uint32_t duration, std::string uuid = {});
void AddDzLockoutDuration(const DzLockout& lockout, int seconds, const std::string& uuid = {}, bool update_db = false);
DynamicZone* CreateExpedition(DynamicZone& dz, bool silent = false);
DynamicZone* CreateExpedition(uint32 zone_id, uint32 version, uint32 duration, const std::string& name, uint32 min_players, uint32 max_players, bool silent = false);
DynamicZone* CreateExpeditionFromTemplate(uint32_t dz_template_id);
DynamicZone* GetExpedition() const;
uint32 GetExpeditionID() const;
const DzLockout* GetDzLockout(const std::string& expedition, const std::string& event) const;
const std::vector<DzLockout>& GetDzLockouts() const { return m_dz_lockouts; };
std::vector<DzLockout> GetDzLockouts(const std::string& expedition);
uint32 GetPendingDzInviteID() const { return m_dz_invite.dz_id; }
void SetPendingDzInvite(const ExpeditionInvite& invite) { m_dz_invite = invite; }
void RequestPendingDzInvite() const;
bool HasDzLockout(const std::string& expedition, const std::string& event) const;
void RemoveDzLockouts(const std::string& expedition, bool update_db = false);
void RemoveDzLockout(const std::string& expedition, const std::string& event, bool update_db = false);
void SendDzLockoutTimers();
void DzListTimers();
void SetDzRemovalTimer(bool enable_timer);
void SendDzCompassUpdate();
@@ -2287,9 +2284,8 @@ private:
uint8 client_max_level;
uint32 m_expedition_id = 0;
ExpeditionInvite m_pending_expedition_invite { 0 };
std::vector<ExpeditionLockoutTimer> m_expedition_lockouts;
ExpeditionInvite m_dz_invite = {};
std::vector<DzLockout> m_dz_lockouts;
glm::vec3 m_quest_compass;
bool m_has_quest_compass = false;
std::vector<uint32_t> m_dynamic_zone_ids;
+254 -243
View File
@@ -43,8 +43,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/data_verification.h"
#include "../common/rdtsc.h"
#include "data_bucket.h"
#include "dynamic_zone.h"
#include "event_codes.h"
#include "expedition.h"
#include "guild_mgr.h"
#include "merc.h"
#include "petitions.h"
@@ -1781,8 +1781,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
m_dynamic_zone_ids.emplace_back(entry.dynamic_zone_id);
}
m_expedition_id = ExpeditionsRepository::GetIDByMemberID(database, CharacterID());
auto dz = zone->GetDynamicZone();
if (dz && dz->GetSafeReturnLocation().zone_id != 0)
{
@@ -4251,7 +4249,14 @@ void Client::Handle_OP_Camp(const EQApplicationPacket *app)
if (GetGM())
{
OnDisconnect(true);
if (RuleB(Character, EnableHackedFastCampForGM))
{
camp_timer.Start(100, true);
}
else {
OnDisconnect(true);
}
return;
}
@@ -6055,8 +6060,8 @@ void Client::Handle_OP_DzAddPlayer(const EQApplicationPacket *app)
}
else
{
// the only /dz command that sends an error message if no active expedition
Message(Chat::System, DZ_YOU_NOT_ASSIGNED);
// message string 8271 (not in emu clients) is the only /dz command that sends an error if no active expedition
Message(Chat::System, "You could not use this command because you are not currently assigned to a dynamic zone.");
}
}
@@ -6089,14 +6094,14 @@ void Client::Handle_OP_DzChooseZoneReply(const EQApplicationPacket *app)
void Client::Handle_OP_DzExpeditionInviteResponse(const EQApplicationPacket *app)
{
auto expedition = Expedition::FindCachedExpeditionByID(m_pending_expedition_invite.expedition_id);
std::string swap_remove_name = m_pending_expedition_invite.swap_remove_name;
m_pending_expedition_invite = { 0 }; // clear before re-validating
auto expedition = DynamicZone::FindDynamicZoneByID(m_dz_invite.dz_id);
std::string swap_name = m_dz_invite.swap_name;
m_dz_invite = {}; // clear before re-validating
if (expedition)
{
auto dzmsg = reinterpret_cast<ExpeditionInviteResponse_Struct*>(app->pBuffer);
expedition->DzInviteResponse(this, dzmsg->accepted, swap_remove_name);
expedition->DzInviteResponse(this, dzmsg->accepted, swap_name);
}
}
@@ -7585,292 +7590,298 @@ void Client::Handle_OP_GroupUpdate(const EQApplicationPacket *app)
void Client::Handle_OP_GuildBank(const EQApplicationPacket *app)
{
if (!GuildBanks)
if (!GuildBanks) {
GuildBankAck();
return;
}
if (zone->GetZoneID() != Zones::GUILDHALL)
{
if (zone->GetZoneID() != Zones::GUILDHALL) {
Message(Chat::Red, "The Guild Bank is not available in this zone.");
GuildBankAck();
return;
}
if (app->size < sizeof(uint32)) {
LogError("Wrong size: OP_GuildBank, size=[{}], expected [{}]", app->size, sizeof(uint32));
DumpPacket(app);
GuildBankAck();
return;
}
char *Buffer = (char *)app->pBuffer;
uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buffer);
char *Buffer = (char *) app->pBuffer;
uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buffer);
uint32 sentAction = Action;
if (!IsInAGuild())
{
if (!IsInAGuild()) {
Message(Chat::Red, "You must be in a Guild to use the Guild Bank.");
if (Action == GuildBankDeposit)
if (Action == GuildBankDeposit) {
GuildBankDepositAck(true, sentAction);
else
}
else {
GuildBankAck();
}
return;
}
if (!IsGuildBanker())
{
if ((Action != GuildBankDeposit) && (Action != GuildBankViewItem) && (Action != GuildBankWithdraw))
if (Action != GuildBankDeposit && Action != GuildBankViewItem && Action != GuildBankWithdraw)
{
LogError("Suspected hacking attempt on guild bank from [{}]", GetName());
GuildBankAck();
return;
}
}
switch (Action)
{
case GuildBankPromote:
{
if (GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea))
{
MessageString(Chat::Red, GUILD_BANK_FULL);
GuildBankDepositAck(true, sentAction);
return;
}
GuildBankPromote_Struct *gbps = (GuildBankPromote_Struct*)app->pBuffer;
int Slot = GuildBanks->Promote(GuildID(), gbps->Slot);
if (Slot >= 0)
{
EQ::ItemInstance* inst = GuildBanks->GetItem(GuildID(), GuildBankMainArea, Slot, 1);
if (inst)
{
MessageString(Chat::LightGray, GUILD_BANK_TRANSFERRED, inst->GetItem()->Name);
safe_delete(inst);
}
}
else
Message(Chat::Red, "Unexpected error while moving item into Guild Bank.");
GuildBankAck();
break;
}
case GuildBankViewItem:
{
GuildBankViewItem_Struct *gbvis = (GuildBankViewItem_Struct*)app->pBuffer;
EQ::ItemInstance* inst = GuildBanks->GetItem(GuildID(), gbvis->Area, gbvis->SlotID, 1);
if (!inst)
break;
SendItemPacket(0, inst, ItemPacketViewLink);
safe_delete(inst);
break;
}
case GuildBankDeposit: // Deposit Item
{
EQ::ItemInstance *CursorItemInst = GetInv().GetItem(EQ::invslot::slotCursor);
bool Allowed = true;
if (!CursorItemInst)
{
Message(Chat::Red, "No Item on the cursor.");
GuildBankDepositAck(true, sentAction);
return;
}
const EQ::ItemData* CursorItem = CursorItemInst->GetItem();
if (GuildBanks->IsAreaFull(GuildID(), GuildBankDepositArea))
{
MessageString(Chat::Red, GUILD_BANK_FULL);
GuildBankDepositAck(true, sentAction);
if (ClientVersion() >= EQ::versions::ClientVersion::RoF) {
GetInv().PopItem(EQ::invslot::slotCursor);
PushItemOnCursor(CursorItem, true);
switch (Action) {
case GuildBankPromote: {
if (GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) {
MessageString(Chat::Red, GUILD_BANK_FULL);
GuildBankAck();
return;
}
return;
}
auto gbps = (GuildBankPromote_Struct *) app->pBuffer;
int slot_id = GuildBanks->Promote(GuildID(), gbps->Slot, this);
if (!CursorItem->NoDrop || CursorItemInst->IsAttuned())
{
Allowed = false;
}
else if (CursorItemInst->IsNoneEmptyContainer())
{
Allowed = false;
}
else if (CursorItemInst->IsAugmented())
{
Allowed = false;
}
else if (CursorItem->NoRent == 0)
{
Allowed = false;
}
else if (CursorItem->LoreFlag && GuildBanks->HasItem(GuildID(), CursorItem->ID))
{
Allowed = false;
}
if (slot_id >= 0) {
auto inst = GuildBanks->GetItem(GuildID(), GuildBankMainArea, slot_id, 1);
if (inst) {
if (player_event_logs.IsEventEnabled(PlayerEvent::GUILD_BANK_MOVE_TO_BANK_AREA)) {
PlayerEvent::GuildBankTransaction log{};
log.char_id = CharacterID();
log.guild_id = GuildID();
log.item_id = inst->GetID();
log.quantity = inst->GetCharges();
if (inst->IsAugmented()) {
auto augs = inst->GetAugmentIDs();
log.aug_slot_one = augs.at(0);
log.aug_slot_two = augs.at(1);
log.aug_slot_three = augs.at(2);
log.aug_slot_four = augs.at(3);
log.aug_slot_five = augs.at(4);
log.aug_slot_six = augs.at(5);
}
if (!Allowed)
{
MessageString(Chat::Red, GUILD_BANK_CANNOT_DEPOSIT);
GuildBankDepositAck(true, sentAction);
RecordPlayerEventLog(PlayerEvent::GUILD_BANK_MOVE_TO_BANK_AREA, log);
}
if (ClientVersion() >= EQ::versions::ClientVersion::RoF) {
GetInv().PopItem(EQ::invslot::slotCursor);
PushItemOnCursor(CursorItem, true);
MessageString(Chat::LightGray, GUILD_BANK_TRANSFERRED, inst->GetItem()->Name);
}
}
else {
Message(Chat::Red, "Unexpected error while moving item into Guild Bank.");
}
return;
}
if (GuildBanks->AddItem(GuildID(), GuildBankDepositArea, CursorItem->ID, CursorItemInst->GetCharges(), GetName(), GuildBankBankerOnly, ""))
{
GuildBankDepositAck(false, sentAction);
DeleteItemInInventory(EQ::invslot::slotCursor, 0, false);
}
break;
}
case GuildBankPermissions:
{
GuildBankPermissions_Struct *gbps = (GuildBankPermissions_Struct*)app->pBuffer;
if (gbps->Permissions == 1)
GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, gbps->MemberName);
else
GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, "");
GuildBankAck();
break;
}
case GuildBankWithdraw:
{
if (GetInv()[EQ::invslot::slotCursor])
{
MessageString(Chat::Red, GUILD_BANK_EMPTY_HANDS);
GuildBankAck();
break;
}
case GuildBankViewItem: {
auto gbvis = (GuildBankViewItem_Struct *) app->pBuffer;
auto inst = GuildBanks->GetItem(GuildID(), gbvis->Area, gbvis->SlotID, 1);
if (!inst) {
break;
}
SendItemPacket(0, inst.get(), ItemPacketViewLink);
break;
}
case GuildBankDeposit: // Deposit Item
{
const auto cursor_item_inst = GetInv().GetItem(EQ::invslot::slotCursor);
bool allowed = true;
if (!cursor_item_inst) {
Message(Chat::Red, "No Item on the cursor.");
GuildBankDepositAck(true, sentAction);
return;
}
const auto cursor_item = cursor_item_inst->GetItem();
if (GuildBanks->IsAreaFull(GuildID(), GuildBankDepositArea)) {
MessageString(Chat::Red, GUILD_BANK_FULL);
GuildBankDepositAck(true, sentAction);
if (ClientVersion() >= EQ::versions::ClientVersion::RoF) {
GetInv().PopItem(EQ::invslot::slotCursor);
PushItemOnCursor(*cursor_item_inst, true);
}
return;
}
if (!cursor_item->NoDrop ||
cursor_item_inst->IsAttuned() ||
cursor_item_inst->IsNoneEmptyContainer() ||
cursor_item->NoRent == 0 ||
(cursor_item->LoreFlag && GuildBanks->HasItem(GuildID(), cursor_item->ID))
) {
allowed = false;
}
if (!allowed) {
MessageString(Chat::Red, GUILD_BANK_CANNOT_DEPOSIT);
GuildBankDepositAck(true, sentAction);
if (ClientVersion() >= EQ::versions::ClientVersion::RoF) {
GetInv().PopItem(EQ::invslot::slotCursor);
PushItemOnCursor(*cursor_item_inst, true);
}
return;
}
auto item = GuildBankRepository::NewEntity();
item.guild_id = GuildID();
item.area = GuildBankDepositArea;
item.item_id = cursor_item->ID;
item.quantity = cursor_item_inst->GetCharges();
item.donator = GetCleanName();
item.permissions = GuildBankBankerOnly;
if (cursor_item_inst->IsAugmented()) {
auto const augs = cursor_item_inst->GetAugmentIDs();
item.augment_one_id = augs.at(0);
item.augment_two_id = augs.at(1);
item.augment_three_id = augs.at(2);
item.augment_four_id = augs.at(3);
item.augment_five_id = augs.at(4);
item.augment_six_id = augs.at(5);
}
if (GuildBanks->AddItem(item, this)) {
GuildBankDepositAck(false, sentAction);
DeleteItemInInventory(EQ::invslot::slotCursor, 0, false);
if (player_event_logs.IsEventEnabled(PlayerEvent::GUILD_BANK_DEPOSIT)) {
PlayerEvent::GuildBankTransaction log{};
log.char_id = CharacterID();
log.guild_id = GuildID();
log.item_id = item.item_id;
log.quantity = item.quantity;
log.aug_slot_one = item.augment_one_id;
log.aug_slot_two = item.augment_two_id;
log.aug_slot_three = item.augment_three_id;
log.aug_slot_four = item.augment_four_id;
log.aug_slot_five = item.augment_five_id;
log.aug_slot_six = item.augment_six_id;
RecordPlayerEventLog(PlayerEvent::GUILD_BANK_DEPOSIT, log);
}
}
break;
}
GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer;
case GuildBankPermissions: {
auto gbps = (GuildBankPermissions_Struct *) app->pBuffer;
EQ::ItemInstance* inst = GuildBanks->GetItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity);
if (gbps->Permissions == 1) {
GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, gbps->MemberName, this);
}
else {
GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, "", this);
}
if (!inst)
{
GuildBankAck();
break;
}
if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_BANK_WITHDRAW_ITEMS))
{
Message(Chat::Red, "You do not have permission to withdraw.");
case GuildBankWithdraw: {
if (GetInv()[EQ::invslot::slotCursor]) {
MessageString(Chat::Red, GUILD_BANK_EMPTY_HANDS);
GuildBankAck();
break;
}
auto gbwis = (GuildBankWithdrawItem_Struct *) app->pBuffer;
auto inst = GuildBanks->GetItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity);
if (!inst) {
GuildBankAck();
break;
}
if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_BANK_WITHDRAW_ITEMS)) {
Message(Chat::Red, "You do not have permission to withdraw.");
GuildBankAck();
break;
}
if (!IsGuildBanker()) {
LogError("Suspected attempted hack on the guild bank from [{}]", GetName());
GuildBankAck();
break;
}
if (CheckLoreConflict(inst->GetItem())) {
MessageString(Chat::Red, DUP_LORE);
GuildBankAck();
break;
}
if (inst->GetCharges() > 0) {
gbwis->Quantity = inst->GetCharges();
}
if (inst->GetCharges() < 0) {
gbwis->Quantity = 1;
}
PushItemOnCursor(*inst.get());
SendItemPacket(EQ::invslot::slotCursor, inst.get(), ItemPacketLimbo);
GuildBanks->DeleteItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity, this);
if (player_event_logs.IsEventEnabled(PlayerEvent::GUILD_BANK_WITHDRAWAL)) {
PlayerEvent::GuildBankTransaction log{};
log.char_id = CharacterID();
log.guild_id = GuildID();
log.item_id = inst->GetID();
log.quantity = gbwis->Quantity;
if (inst->IsAugmented()) {
auto augs = inst->GetAugmentIDs();
log.aug_slot_one = augs.at(0);
log.aug_slot_two = augs.at(1);
log.aug_slot_three = augs.at(2);
log.aug_slot_four = augs.at(3);
log.aug_slot_five = augs.at(4);
log.aug_slot_six = augs.at(5);
}
RecordPlayerEventLog(PlayerEvent::GUILD_BANK_WITHDRAWAL, log);
}
else {
Message(Chat::Red, "Unable to withdraw 0 quantity of %s", inst->GetItem()->Name);
}
GuildBankAck();
break;
}
case GuildBankSplitStacks: {
if (GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) {
MessageString(Chat::Red, GUILD_BANK_FULL);
}
else {
auto gbwis = (GuildBankWithdrawItem_Struct *) app->pBuffer;
GuildBanks->SplitStack(GuildID(), gbwis->SlotID, gbwis->Quantity, this);
}
GuildBankAck();
break;
}
case GuildBankMergeStacks: {
auto gbwis = (GuildBankWithdrawItem_Struct *) app->pBuffer;
GuildBanks->MergeStacks(GuildID(), gbwis->SlotID, this);
GuildBankAck();
safe_delete(inst);
break;
}
if (!IsGuildBanker() && !GuildBanks->AllowedToWithdraw(GuildID(), gbwis->Area, gbwis->SlotID, GetName()))
{
LogError("Suspected attempted hack on the guild bank from [{}]", GetName());
GuildBankAck();
safe_delete(inst);
break;
default: {
Message(Chat::Red, "Unexpected GuildBank action.");
LogError("Received unexpected guild bank action code [{}] from [{}]", Action, GetName());
}
if (CheckLoreConflict(inst->GetItem()))
{
MessageString(Chat::Red, DUP_LORE);
GuildBankAck();
safe_delete(inst);
break;
}
if (gbwis->Quantity > 0)
{
PushItemOnCursor(*inst);
SendItemPacket(EQ::invslot::slotCursor, inst, ItemPacketLimbo);
GuildBanks->DeleteItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity);
}
else
{
Message(Chat::Red, "Unable to withdraw 0 quantity of %s", inst->GetItem()->Name);
}
safe_delete(inst);
GuildBankAck();
break;
}
case GuildBankSplitStacks:
{
if (GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea))
MessageString(Chat::Red, GUILD_BANK_FULL);
else
{
GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer;
GuildBanks->SplitStack(GuildID(), gbwis->SlotID, gbwis->Quantity);
}
GuildBankAck();
break;
}
case GuildBankMergeStacks:
{
GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer;
GuildBanks->MergeStacks(GuildID(), gbwis->SlotID);
GuildBankAck();
break;
}
default:
{
Message(Chat::Red, "Unexpected GuildBank action.");
LogError("Received unexpected guild bank action code [{}] from [{}]", Action, GetName());
}
}
}
+1 -1
View File
@@ -39,8 +39,8 @@
#include "../common/skills.h"
#include "../common/spdat.h"
#include "../common/strings.h"
#include "dynamic_zone.h"
#include "event_codes.h"
#include "expedition.h"
#include "guild_mgr.h"
#include "map.h"
#include "petitions.h"
-1
View File
@@ -22,7 +22,6 @@
#include "data_bucket.h"
#include "command.h"
#include "dynamic_zone.h"
#include "expedition.h"
#include "queryserv.h"
#include "quest_parser_collection.h"
#include "titles.h"
-7
View File
@@ -862,13 +862,6 @@ struct DamageHitInfo {
EQ::skills::SkillType skill;
};
struct ExpeditionInvite
{
uint32_t expedition_id;
std::string inviter_name;
std::string swap_remove_name;
};
struct DataBucketCache
{
uint64_t bucket_id;
+1 -1
View File
@@ -16,8 +16,8 @@
#include "../common/say_link.h"
#include "corpse.h"
#include "dynamic_zone.h"
#include "entity.h"
#include "expedition.h"
#include "groups.h"
#include "mob.h"
#include "raids.h"
+124 -29
View File
@@ -19,10 +19,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);
@@ -54,6 +50,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;
@@ -189,12 +188,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 +212,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 +245,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 +292,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);
}
@@ -347,12 +352,15 @@ bool DataBucket::DeleteData(const DataBucketKey &k)
);
LogDataBuckets(
"Deleting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}] cache size before [{}] after [{}]",
"Deleting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}] bot_id [{}] zone_id [{}] instance_id [{}] cache size before [{}] after [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_id,
k.bot_id,
k.zone_id,
k.instance_id,
size_before,
g_data_bucket_cache.size()
);
@@ -390,12 +398,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 +419,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 +469,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 +604,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 +616,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 +659,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,13 +713,42 @@ 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;
}
+11 -5
View File
@@ -12,10 +12,12 @@ 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 +25,7 @@ namespace DataBucketLoadType {
Bot,
Account,
Client,
Zone,
MaxType
};
@@ -30,6 +33,7 @@ namespace DataBucketLoadType {
"Bot",
"Account",
"Client",
"Zone"
};
}
@@ -56,12 +60,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);

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